123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- // This code simulates the static deflection and small-signal vibration of a collapsed 3D CMUT while supposing axisymmetry.
- // A CMUT is a microscale ultrasonic transducer with an electrostatic actuation. In collapse mode the CMUT membrane is
- // pulled-in, i.e. touches the bottom of the cavity.
- //
- // The CMUT geometry and mesh in this example are created using sparselizard.
- // The CMUT is made up of (from bottom layer to top):
- //
- // - a substrate (not in this example)
- // - a ground electrode
- // - an insulator layer (SiO2)
- // - a vacuum cavity
- // - a membrane layer (Silicon)
- // - an electrode
- #include "sparselizardbase.h"
- using namespace mathop;
- // Arguments are:
- //
- // CMUT radius, thickness of the membrane, depth of the cavity, insulator thickness and x-length of the pillar supporting the membrane.
- //
- mesh createmesh(double r, double thmem, double thcav, double thins, double lpillar);
- void sparselizard(void)
- {
- // Define the CMUT geometric dimensions [m]:
- double r = 20e-6, thmem = 0.5e-6, thcav = 0.5e-6, thins = 0.3e-6, lpillar = 10e-6;
-
- // Axisymmetric assumption:
- setaxisymmetry();
-
- // The domain regions as defined in 'createmesh':
- int membrane = 1, pillar = 2, cavity = 3, ground = 4, electrode = 5, insulator = 6;
-
- // Create the geometry and the mesh:
- mesh mymesh = createmesh(r, thmem, thcav, thins, lpillar);
-
- // Write the mesh for display:
- mymesh.write("cmutaxisym.msh");
-
- // Define additional regions:
- int contact = regionintersection({membrane, cavity});
- int solid = regionunion({membrane, pillar, insulator});
- int wholedomain = regionunion({solid,cavity});
-
- // Nodal shape functions 'h1' for v (the electric potential) and
- // u (membrane displacement). Three components are used for u.
- // Field umesh is used to smoothly deform the mesh in the cavity.
- //
- field u("h1xyz"), v("h1"), umesh("h1xyz"), nodalforcebalance("h1xyz");
-
- // Clamp the insulator:
- u.setconstraint(insulator);
-
- v.setconstraint(electrode, 120);
- v.setconstraint(ground, 0);
-
- // Set a conditional constraint on compy(u), the y component of the mechanical deflection.
- // The constraint is active for all nodal degrees of freedom at which 'contactcondition' is positive or zero.
- //
- // There is mechanical contact if either the deflection is larger than a given threshold
- // (chosen as 99% of the gap size times 1+1e-6, not 100% to avoid the need of remeshing the cavity)
- // or if at the same time:
- //
- // - compy(u) is greater than a given deflection (99% of the gap size)
- // - the y direction force balance on the node is negative, i.e. the node is pulled downwards
- //
- expression contactcondition = orpositive({-compy(u)-(thcav+1e-6*thcav)*0.99, andpositive({-compy(u)-thcav*0.99, -compy(nodalforcebalance)})});
-
- // Set the conditional constraint on the y component of the deflection field u:
- u.compy().setconditionalconstraint(contact, contactcondition, -0.99*(thcav+1e-8*thcav));
-
- // Young's modulus [Pa], Poisson's ratio [], the density [kg/m^3] and the electric permittivity [F/m]:
- parameter E, nu, rho, epsilon;
-
- E|solid = 150e9; E|insulator = 66e9;
- nu|solid = 0.25; nu|insulator = 0.17;
- rho|solid = 2320; rho|insulator = 2200;
-
- epsilon|cavity = 8.854e-12;
- epsilon|solid = 11.7*8.854e-12;
- epsilon|insulator = 3.9*8.854e-12;
- // An electrostatic formulation is used for the electric problem.
- // An elasticity formulation is used for the mechanical problem.
- // Geometrical nonlinearity is taken into account.
- // A forcebalance formulation is used for the conditional constraint.
- formulation electrostatics, elasticity, forcebalance;
-
- // Weak electrostatic formulation, computed on the mesh deformed by field umesh:
- electrostatics += integral(wholedomain, umesh, epsilon*grad(dof(v))*grad(tf(v)));
-
- // Weak elasticity formulation with geometrical nonlinearity
- // (refer to the documentation to add prestress with the last argument).
- elasticity += integral(solid, predefinedelasticity(dof(u), tf(u), u, E, nu, 0.0));
- // Electrostatic forces, computed on the elements of the whole electric domain
- // but with mechanical deflection test functions tf(u) only on solid.
- //
- // The electrostatic forces often appear in MEMS simulations and are thus predefined.
- // The inputs are the gradient of the test function of u defined on the mechanical domain,
- // the gradient of the previously computed electric potential field and the electric permittivity.
- //
- // The electrostatic forces are computed on the mesh deformed by field umesh.
- elasticity += integral(wholedomain, umesh, predefinedelectrostaticforce(tf(u,solid), grad(v), epsilon));
-
- // Formulation (based on the elasticity) to get the force balance on the contact region:
- forcebalance += integral(solid, -predefinedelasticity(u, tf(nodalforcebalance,contact), u, E, nu, 0.0));
- forcebalance += integral(wholedomain, umesh, -predefinedelectrostaticforce(tf(nodalforcebalance,contact), grad(v), epsilon));
-
- // Solve the Laplace equation in the cavity to smoothly deform the mesh.
- // umesh is forced to field u on region solid:
- umesh.setconstraint(solid, u);
- formulation laplacian;
- laplacian += integral(wholedomain, grad(dof(compx(umesh)))*grad(tf(compx(umesh))) + grad(dof(compy(umesh)))*grad(tf(compy(umesh))) + grad(dof(compz(umesh)))*grad(tf(compz(umesh))));
-
-
- // NONLINEAR ITERATION TO GET THE STATIC DEFLECTION:
-
- // Start with an all-zero solution vector for the elasticity formulation:
- vec solu(elasticity);
-
- double relresnorm = 1; int iter = 0;
- while (relresnorm > 1e-8)
- {
- electrostatics.generate();
- vec solv = solve(electrostatics.A(), electrostatics.b());
- // Transfer the data from the solution vector to the v field:
- v.setdata(wholedomain, solv);
- // Write the electric field computed and saved on the geometry deformed by umesh.
- (-grad(v)).write(wholedomain, umesh, "E.pos");
- v.write(wholedomain, umesh, "v.pos");
-
- // Calculate the force balance at the contact using the now known electric potential:
- forcebalance.generate();
- // The force balance is in the right handside vector:
- nodalforcebalance.setdata(contact, forcebalance.b());
-
- // Compute the membrane deflection:
- elasticity.generate();
- // Get the vector b and matrix A of problem Ax = b. Here they include the conditional constraints:
- vec b = elasticity.b();
- mat A = elasticity.A();
- // Compute the norm of the relative residual to check the convergence:
- relresnorm = (b-A*solu).norm()/b.norm();
-
- solu = solve(A,b);
- u.setdata(solid, solu);
- // Write the deflection u:
- u.write(solid, "u.pos");
-
- // Smooth the mesh in the cavity:
- laplacian.generate();
- vec solumesh = solve(laplacian.A(), laplacian.b());
- // Save the smoothed mesh in the cavity:
- umesh.setdata(wholedomain, solumesh);
-
- // Print the iteration number and relative residual norm:
- std::cout << "Rel. res. norm @it " << iter << " is " << relresnorm << ", max deflection is " << abs(compy(u)).max(contact, 5)[0]*1e9 << " nm" << std::endl;
- iter++;
- }
-
-
- // HARMONIC PERTURBATION AROUND THE STATIC DEFLECTION:
-
- // AC electric actuation frequency:
- setfundamentalfrequency(1e6);
-
- // Fields uh and vh have a constant deflection plus a harmonic deflection (harmonics 1 and 2):
- // uh = uh1 + uh2 * sin(2*pi*f0*t), vh = vh1 + vh2 * sin(2*pi*f0*t)
- field uh("h1xyz", {1,2}), vh("h1", {1,2});
-
- // Set the static deflection to the above solution:
- uh.harmonic(1).setdata(solid, solu|u);
-
- // Clamp the insulator:
- uh.setconstraint(insulator);
-
- // Set the same conditional constraint as above:
- uh.harmonic(1).compy().setconditionalconstraint(contact, contactcondition, -0.99*(thcav+1e-8*thcav));
- // The vibration around the static deflection is 0 at the contact:
- uh.harmonic(2).setconditionalconstraint(contact, contactcondition, array3x1(0,0,0));
-
- // Set the DC voltage bias on the electrode:
- vh.harmonic(1).setconstraint(electrode, 120);
- // Set a tiny AC voltage on vh2:
- vh.harmonic(2).setconstraint(electrode, 1);
- // Ground:
- vh.setconstraint(ground);
-
-
- // Redefine the electrostatic and elasticity formulations as above:
- formulation helectrostatics, helasticity;
-
- helectrostatics += integral(wholedomain, umesh, epsilon*grad(dof(vh))*grad(tf(vh)));
- helasticity += integral(solid, predefinedelasticity(dof(uh), tf(uh), u, E, nu, 0.0));
- // Here the electrostatic force must be computed using an FFT (on 5 timesteps)
- // because the force calculation involves nonlinear operations on multiharmonic fields:
- helasticity += integral(wholedomain, 5, umesh, predefinedelectrostaticforce(tf(uh,solid), grad(vh), epsilon));
- // The inertia term is added for the harmonic analysis:
- helasticity += integral(solid, -rho*dtdt(dof(uh))*tf(uh));
-
- // Generate and solve the electrostatic problem:
- helectrostatics.generate();
- vec solvh = solve(helectrostatics.A(), helectrostatics.b());
- vh.setdata(wholedomain, solvh);
-
- // Generate and solve the elasticity problem:
- helasticity.generate();
- vec soluh = solve(helasticity.A(), helasticity.b());
- uh.setdata(solid, soluh);
- uh.write(solid, umesh, "usmallsignal.pos");
- // Write the vibration at 50 timesteps of a period for a time visualization:
- (uh-uh.harmonic(1)).write(solid, umesh, "usmallsignal.pos", 1, 50);
-
- // Print the max AC deflection:
- double uacmax = abs(compy(uh.harmonic(2))).max(solid, 5)[0];
- std::cout << "Peak AC deflection: " << uacmax*1e9 << " nm" << std::endl;
-
- // Code validation line. Can be removed.
- std::cout << (uacmax < 1.1970e-09 && uacmax > 1.1967e-09);
- }
- // 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 CMUT DIMENSION WITHOUT NEEDING CALLS TO EXTERNAL MESHING SOFTWARE.
- mesh createmesh(double r, double thmem, double thcav, double thins, double lpillar)
- {
- // Give names to the physical region numbers:
- int membrane = 1, pillar = 2, cavity = 3, ground = 4, electrode = 5, insulator = 6;
-
- // Number of mesh layers:
- int nx = 40, nzcavity = 5, nzmembrane = 6, nzinsulator = 3;
- int nxpillar = nx*lpillar/r;
-
- // Insulator:
- double h = -thins;
- shape q01("quadrangle", insulator, {0,h,0, r,h,0, r,h+thins,0, 0,h+thins,0}, {nx, nzinsulator, nx, nzinsulator});
- shape q02("quadrangle", insulator, {r,h,0, r+lpillar,h,0, r+lpillar,h+thins,0, r,h+thins,0}, {nxpillar, nzinsulator, nxpillar, nzinsulator});
-
- // Cavity layer:
- h = 0.0;
- shape q11("quadrangle", cavity, {0,h,0, r,h,0, r,h+thcav,0, 0,h+thcav,0}, {nx, nzcavity, nx, nzcavity});
- shape q12("quadrangle", pillar, {r,h,0, r+lpillar,h,0, r+lpillar,h+thcav,0, r,h+thcav,0}, {nxpillar, nzcavity, nxpillar, nzcavity});
-
- // Ground line:
- shape groundline("union", ground, {q01.getsons()[0], q02.getsons()[0]});
-
- // Membrane layer:
- h = h+thcav;
- shape q21("quadrangle", membrane, {0,h,0, r,h,0, r,h+thmem,0, 0,h+thmem,0}, {nx, nzmembrane, nx, nzmembrane});
- shape q22("quadrangle", membrane, {r,h,0, r+lpillar,h,0, r+lpillar,h+thmem,0, r,h+thmem,0}, {nxpillar, nzmembrane, nxpillar, nzmembrane});
-
- // Electrode line:
- shape electrodeline("union", electrode, {q21.getsons()[2], q22.getsons()[2]});
-
- // Provide to the mesh all shapes of interest:
- mesh mymesh({q01,q02,q11,q12,q21,q22,groundline,electrodeline});
-
- return mymesh;
- }
- int main(void)
- {
- SlepcInitialize(0,{},0,0);
- sparselizard();
- SlepcFinalize();
- return 0;
- }
|