123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- // This example shows the time-simulation of a 3D high-temperature superconductor (HTS):
- // a superconducting tube is used to shield its inside from an externally applied magnetic field
- // that increases linearly over time. For the simulation the so called a-v magnetodynamic formulation is used.
- //
- // The tube height is 210 mm, its thickness is 1 mm and its inner radius is 10 mm.
- //
- // This example is based on and validated with the experimental and simulation work done by K. Hogan in his thesis
- // "Passive magnetic shielding with bulk high temperature superconductors under a non-uniform magnetic field".
- //
- // Details on the equations for HTS can also be found in "Numerical simulation of the magnetization of
- // high-temperature superconductors: a 3D finite element method using a single time-step iteration" (G. Lousberg)
- #include "sparselizardbase.h"
- using namespace mathop;
- // Arguments are:
- //
- // Tube thickness, height and inner radius, size of the air domain around as well as integers defining the mesh size.
- // The last argument gives the mesh progression in the air around the tube (increase for a finer mesh at the tube).
- //
- mesh createmesh(double thtube, double htube, double rintube, double linf, int nhtube, int ncircumtube, int nthtube, int nair, int prog);
- void sparselizard(void)
- {
- wallclock clk;
-
- // The domain regions as defined in 'createmesh':
- int wholedomain = 1, tube = 2, insidetube = 3, tubeskin = 4, domainskin = 5, ground = 6, boxcut = 7;
-
- // Tube thickness, height, inner radius and domain size [m]:
- double thtube = 1e-3, htube = 210e-3, rintube = 10e-3, linf = 100e-3;
-
- // Create the geometry and the mesh:
- mesh mymesh = createmesh(thtube, htube, rintube, linf, 7, 4, 10, 7, 20);
-
- // Define a region to visualize the solution:
- boxcut = regionexclusion(wholedomain, boxcut);
-
- // Write the mesh for display:
- mymesh.write("tubeinair.msh");
-
-
- // Magnetic permeability of the air:
- double mu = 4*getpi()*1e-7;
-
- // Define a spanning tree to gauge the magnetic vector potential (otherwise the matrix to invert is singular).
- // Start growing the tree from the regions with constrained potential vector (here the domain boundary):
- spanningtree spantree({domainskin});
-
- // Use nodal shape functions 'h1' for the electric scalar potential 'v'.
- // Use edge shape functions 'hcurl' for the magnetic vector potential 'a'.
- // A spanning tree has to be provided to field 'a' for gauging.
- field a("hcurl", spantree), v("h1"), x("x"), y("y"), z("z");
-
- // Use interpolation order 1:
- a.setorder(wholedomain, 1);
- v.setorder(wholedomain, 1);
-
- // Gauge the vector potential field on the whole volume:
- a.setgauge(wholedomain);
-
- // Put a magnetic wall (i.e. set field a to 0) all around the domain (no magnetic flux can cross it):
- a.setconstraint(domainskin);
- // Ground v on a node to have a well-defined problem:
- v.setconstraint(ground);
-
-
- // The magnetic vector potential a is rewritten as the sum of a known
- // source term and an unknown part to be determined: atotal = a + asource.
- // We want to apply an external induction field b of 1 mT in
- // the z direction that linearly increases over time:
- expression bsource = array3x1(0,0,1e-3)*t();
- // Since b = curl(a) a valid choice for a is:
- expression asource = 1e-3*0.5*array3x1(-y,x,0)*t();
- // And dt(a)/dt is:
- expression dtasource = 1e-3*0.5*array3x1(-y,x,0);
-
-
- // The strong form of the magnetodynamic a-v formulation is
- //
- // curl( 1/mu * curl(a) ) + sigma * (dt(a) + grad(v)) = js, with b = curl(a) and e = -dt(a) - grad(v)
- //
- expression e = -dt(a)-dtasource - grad(v);
-
- // The conductivity of the high temperature superconductor is modeled using
- // a power law relation between the electric field and the current density:
- //
- // J = Jc/Ec^(1/n) * norm(E)^((1-n)/n) * E
- // |_______________________________|
- // sigma(E)
- //
- // where the critical current density Jc [A/m^2] and critical electric field Ec [V/m] are supposed
- // constant here and the J(E) relation is isotropic. An extra 1e-11 is added to never divide by 0.
- //
- double n = 30.0, Ec = 1e-4, Jc = 1e8;
-
- expression sigma = Jc/( pow(Ec,1.0/n) ) * pow( norm(e) + 1e-11, (1.0-n)/n );
-
-
- // Define the weak magnetodynamic formulation:
- formulation magdyn;
-
- // Magnetic equation (add an extra odd integration degree for convergence):
- magdyn += integral(wholedomain, 1/mu*( curl(dof(a)) + bsource ) * curl(tf(a)) );
- magdyn += integral(tube, sigma*( dt(dof(a)) + dtasource )*tf(a), +1 );
- magdyn += integral(tube, sigma*grad(dof(v))*tf(a) );
- // Electric equation:
- magdyn += integral(tube, sigma*grad(dof(v))*grad(tf(v)) );
- magdyn += integral(tube, sigma*( dt(dof(a)) + dtasource )*grad(tf(v)), +1 );
-
-
- // Start the implicit Euler time resolution with an all zero solution and time derivative:
- vec initsol(magdyn), initdtsol(magdyn);
-
- impliciteuler eul(magdyn, initsol, initdtsol);
- // Tolerance on the nonlinear iteration:
- eul.settolerance(1e-3);
- // Relaxation factor for the nonlinear iteration (decrease to improve convergence):
- eul.setrelaxationfactor(0.5);
-
- // Use a 5 seconds timestep:
- double timestep = 5.0;
- // Run from 0 to 150 sec with a fixed-point nonlinear iteration at every timestep.
- // Set the maximum number of nonlinear iterations to 25.
- std::vector<std::vector<vec>> sols = eul.runnonlinear(0.0, timestep, 150.0, 25);
-
- // Field a is available at every timestep in sols[0] and its time derivative in sols[1]:
- std::vector<vec> asol = sols[0];
- std::vector<vec> dtasol = sols[1];
-
- // Loop over all time solutions:
- std::vector<double> bcenter(asol.size());
- for (int i = 0; i < asol.size(); i++)
- {
- // Set variable t() to the time of the current timestep:
- settime(timestep*i);
-
- // Make the data of vector asol[i] available in field a:
- a.setdata(wholedomain, asol[i]);
-
- // Write b = curl(a) to disk:
- norm(curl(a) + bsource).write(boxcut, "normbtotal" + std::to_string(i+1000) + ".pos");
-
- // Output the b induction field [T] at the tube center to assess the shielding effectiveness.
- bcenter[i] = norm(curl(a) + bsource).interpolate(wholedomain, {1e-10,0,0})[0];
- std::cout << "b source " << timestep*i << " mT --> b tube center " << bcenter[i]*1e3 << " mT" << std::endl;
- }
- // Write to file the field at the tube center for all timesteps:
- writevector("bcenter.csv", bcenter);
-
- clk.print("Total computation time:");
-
- // Code validation line. Can be removed.
- std::cout << 1;
- }
- // THE MESH BELOW IS FULLY STRUCTURED AND IS CREATED USING THE (BASIC) SPARSELIZARD GEOMETRY CREATION TOOL.
- // THE ADVANTAGE OF IT IS THAT THE CODE ABOVE CAN BE CALLED FOR ANY TUBE DIMENSION WITHOUT NEEDING CALLS TO EXTERNAL MESHING SOFTWARE.
- // GMSH COULD HAVE BEEN USED AS AN ALTERNATIVE TO DEFINE THE GEOMETRY AND THE MESH.
- mesh createmesh(double thtube, double htube, double rintube, double linf, int nhtube, int ncircumtube, int nthtube, int nair, int prog)
- {
- int wholedomain = 1, tube = 2, insidetube = 3, tubeskin = 4, domainskin = 5, ground = 6, boxcut = 7;
-
- // To be sure to have nodes at z = 0:
- if (nhtube%2 == 0)
- nhtube++;
-
- field x("x"), y("y");
-
- // Create the footprint face for the air inside the tube:
- shape disk("disk", -1, {0,0,0}, rintube, ncircumtube*4);
- // Create the footprint face for the tube:
- shape arcin = disk.getsons()[0];
- shape arcout("arc", -1, {rintube+thtube,0,0, 0,rintube+thtube,0, 0,0,0}, ncircumtube+1);
- shape line1("line", -1, {rintube,0,0, rintube+thtube,0,0}, nthtube);
- shape line2 = line1.duplicate(); line2.rotate(0,0,90);
-
- std::vector<shape> quads(4);
- for (int i = 0; i < 4; i++)
- {
- shape curquad("quadrangle", -1, {arcin,line1,arcout,line2});
- curquad = curquad.duplicate();
- curquad.rotate(0,0,90.0*i);
- quads[i] = curquad;
- }
- shape tubefootprint("union", -1, quads);
- // Create the footprint for the remaining domain with a given progression 'prog':
- double rout = std::pow(linf*std::pow(rintube+thtube,prog-1), 1.0/prog);
- shape arcinf("arc", -1, {rout,0,0, 0,rout,0, 0,0,0}, ncircumtube+1);
- shape lineinf1("line", -1, {rintube+thtube,0,0, rout,0,0}, nair);
- shape lineinf2 = lineinf1.duplicate(); lineinf2.rotate(0,0,90);
-
- std::vector<shape> airquads(4);
- for (int i = 0; i < 4; i++)
- {
- shape curquad("quadrangle", -1, {arcout,lineinf1,arcinf,lineinf2});
- curquad = curquad.duplicate();
- curquad.rotate(0,0,90.0*i);
- airquads[i] = curquad;
- }
- shape airfootprint("union", -1, airquads);
- // Deform to have a mesh progression in the air:
- expression radius = sqrt(x*x+y*y);
- airfootprint.deform(x*pow(radius/(rintube+thtube),prog-1)-x,y*pow(radius/(rintube+thtube),prog-1)-y,0);
-
-
- // Create the cylinder region inside the tube:
- shape voltubeinside = disk.duplicate().extrude(insidetube, htube, nhtube);
- voltubeinside.shift(0,0,-htube/2);
-
- // Create the tube region:
- shape voltube = tubefootprint.duplicate().extrude(tube, htube, nhtube);
- voltube.shift(0,0,-htube/2);
-
- // Create the air cylinder around the tube:
- shape volairaroundtube = airfootprint.duplicate().extrude(-1, htube, nhtube);
- volairaroundtube.shift(0,0,-htube/2);
-
- // Create the air above and below the tube:
- shape quadall("union", -1, {disk,tubefootprint,airfootprint});
- shape volabove = quadall.duplicate().extrude(-1, linf, nair);
- volabove.shift(0,0,htube/2);
- shape volbelow = volabove.duplicate();
- volbelow.scale(1,1,-1);
-
- // Define the whole volume:
- shape wholevol("union", wholedomain, {voltube.duplicate(), voltubeinside.duplicate(), volairaroundtube.duplicate(), volabove.duplicate(), volbelow.duplicate()});
-
- mesh mymesh;
-
- mymesh.regionskin(tubeskin, tube);
- mymesh.regionskin(domainskin, wholedomain);
- mymesh.boxselection(ground, wholedomain, 0, {rintube+thtube-1e-10,rintube+thtube+1e-10, -1e-10,1e-10, -1e-10,1e-10});
- mymesh.boxselection(boxcut, wholedomain, 3, {-1e-10,linf+1e-10, -1e-10,linf+1e-10, -htube-linf,htube+linf});
-
- mymesh.load({voltube, voltubeinside, wholevol});
-
- return mymesh;
- }
- int main(void)
- {
- SlepcInitialize(0,{},0,0);
- sparselizard();
- SlepcFinalize();
- return 0;
- }
|