123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- // This code simulates the steady-state vibration of a 2D CMUT (capacitive micromachined
- // ultrasonic transducer). A CMUT is a micromembrane, actuated with an electrostatic
- // force, that vibrates in a fluid (water here). Due to the small dimensions at play
- // the electric excitation frequency is in the megahertz range.
- //
- // In this code the electric excitation is a sine wave so that the vibration is
- // periodic. Due to the electroelastic nonlinearity however the membrane vibration
- // and the pressure field includes new harmonics. These harmonics are computed
- // with the multiharmonic resolution method in which all calculations are brought
- // back to the undeformed configuration as detailed in the paper
- // "Steady-State, Nonlinear Analysis of Large Arrays of Electrically Actuated
- // Micromembranes Vibrating in a Fluid" (A Halbach, C Geuzaine).
- #include "sparselizardbase.h"
- using namespace mathop;
- void sparselizard(void)
- {
- // The domain regions as defined in 'cmut2d.geo':
- int insulator = 1, pillars = 2, vacuumgap = 3, membrane = 4, fluid = 5, ground = 6, clamp = 6, electrode = 7, fluidboundary = 8;
-
- // The mesh can be curved!
- mesh mymesh("cmut2d.msh");
-
- // Define the region for the mechanical problem:
- int solid = regionunion({insulator, pillars, membrane});
- // Define the region for the electric problem:
- int electricdomain = regionunion({insulator, pillars, membrane, vacuumgap});
- // Define the membrane top line:
- int membranetop = regionintersection({membrane, fluid});
-
- // Nodal shape functions 'h1' for the electric potential field v, acoustic pressure
- // field p and membrane deflection u (u has components x and y).
- // umesh smoothly deforms the mesh in the vacuum gap for a given membrane deflection.
- //
- // Multiharmonic fields are used. This means that the time-dependency of a field h
- // is approximated by a truncated Fourier series:
- //
- // h = h1 + h2*sin(2pi*f0*t) + h3*cos(2pi*f0*t) + h4*sin(2 * 2pi*f0*t) + h5*cos(2 * 2pi*f0*t) + ...
- //
- // where f0 is the fundamental frequency, t is the time variable and hi are the Fourier coefficients.
- // The second argument in the field declaration lists all harmonics considered in the truncation.
- // For example the displacement field u is supposed to be well approximated by
- // u = u1 + u4*sin(2 * 2pi*f0*t) + u5*cos(2 * 2pi*f0*t) while in the pressure field the constant is removed.
- //
- field u("h1xy",{1,4,5}), umesh("h1xy",{1,4,5}), v("h1",{2,3}), p("h1",{4,5});
-
- // Set the fundamental frequency f0 to 6 MHz:
- setfundamentalfrequency(6e6);
-
- // Since this is a multiharmonic resolution on a mesh deformed by a multiharmonic
- // displacement field the calculations must be brought back on the undeformed
- // configuration with a variable change [x y z]deformed = [x y z]undeformed + umesh.
- //
- // This is the Jacobian matrix for the change of variable:
- //
- expression jac = array2x2(1+dx(compx(umesh)),dx(compy(umesh)), dy(compx(umesh)),1+dy(compy(umesh)));
- expression invjac = inverse(jac);
- expression detjac = determinant(jac);
- // The expressions above might appear multiple times in a formulation
- // and reusing them while generating a formulation will give a speedup:
- invjac.reuseit(); detjac.reuseit();
- // Use interpolation order:
- //
- // - 3 for u on the membrane
- // - 2 for u on the pillars
- // - 1 elsewhere
- //
- // - 2 for the pressure field p
- //
- // - 1 for the electric potential v
- //
- // Default order is 1.
-
- u.setorder(pillars, 2);
- u.setorder(membrane, 3);
- p.setorder(fluid, 2);
-
- // Clamp and ground all harmonics (i.e. 0 valued-Dirichlet conditions for u and v):
- u.setconstraint(clamp);
- v.setconstraint(ground);
- // Force the electric potential on the electrode to 250*sin(2pi*f0*t).
- // First set all v harmonics to 0 then set harmonic 2 to 250:
- v.setconstraint(electrode);
- v.harmonic(2).setconstraint(electrode, 250);
-
- // E is Young's modulus. nu is Poisson's ratio. rho is the volumic mass.
- // epsilon is the electric permittivity.
- //
- // The membrane is polysilicon, the insulator is silicon dioxyde.
-
- parameter E, nu, rho, epsilon;
-
- E|insulator = 70e9;
- E|pillars = 150e9;
- E|membrane = 150e9;
-
- nu|insulator = 0.17;
- nu|pillars = 0.3;
- nu|membrane = 0.3;
-
- rho|insulator = 2200;
- rho|pillars = 2330;
- rho|membrane = 2330;
- rho|fluid = 1000;
-
- epsilon|vacuumgap = 8.854e-12;
- epsilon|insulator = 3.9*8.854e-12;
- epsilon|pillars = 11.7*8.854e-12;
- epsilon|membrane = 11.7*8.854e-12;
-
- // c is the speed of sound in the water fluid. scaling is a scaling factor.
- double c = 1484, scaling = 1e10;
-
- // An electrostatic formulation is used for the electric problem.
- // An elastoacoustic formulation is used for the fluid-mechanic problem.
- formulation electrostatics, elastoacoustic;
-
- // Weak electrostatic formulation brought back to the undeformed configuration.
- // Since this is a nonlinear multiharmonic formulation we add an integer argument.
- // This means an FFT will be used to get the 20 first harmonics in the nonlinear term.
- electrostatics += integral(electricdomain, 20, epsilon*transpose(invjac*grad(dof(v)))*invjac*grad(tf(v)) * detjac );
-
- // The linear elasticity formulation is classical and thus predefined:
- elastoacoustic += integral(solid, predefinedelasticity(dof(u), tf(u), E, nu, "planestrain"));
- // Add the inertia terms:
- elastoacoustic += integral(solid, -rho*dtdt(dof(u))*tf(u));
-
- // 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 must be computed on the mesh deformed by field umesh.
- // The calculations are brought back to the undeformed configuration.
- expression gradtfu = invjac*expression({{grad(compx(tf(u,solid)))}, {grad(compy(tf(u,solid)))}});
- elastoacoustic += integral(electricdomain, 20, predefinedelectrostaticforce({gradtfu.getcolumn(0),gradtfu.getcolumn(1)}, invjac*grad(v), epsilon) * detjac );
-
- // The wave equation is solved in the fluid:
- elastoacoustic += integral(fluid, -grad(dof(p))*grad(tf(p)) -1/pow(c,2)*dtdt(dof(p))*tf(p));
- // A Sommerfeld condition is used on the fluid boundary to have outgoing waves:
- elastoacoustic += integral(fluidboundary, -1/c*dt(dof(p))*tf(p));
-
- // Elastoacoustic coupling terms.
- // The first term is the forces applied by the fluid on the membrane top.
- // The second term is Newton's law: a membrane acceleration creates a
- // pressure gradient in the fluid.
- //
- // To have a good matrix conditionning the pressure source is divided by
- // the scaling factor and to compensate it multiplies the pressure force.
- // This leads to the correct membrane deflection but a pressure field divided by the scaling factor.
- elastoacoustic += integral(membranetop, -dof(p)*tf(compy(u)) * scaling);
- elastoacoustic += integral(membranetop, rho*dtdt(dof(compy(u)))*tf(p) / scaling);
-
-
- // Solve the Laplace equation in the vacuum gap to smoothly deform the mesh.
- // umesh is forced to field u on region solid:
- umesh.setconstraint(solid, u);
- formulation laplacian;
- laplacian += integral(vacuumgap, grad(dof(compx(umesh)))*grad(tf(compx(umesh))) + grad(dof(compy(umesh)))*grad(tf(compy(umesh))) );
-
-
- // NONLINEAR ITERATION TO GET THE DEFLECTION:
-
- // Start with an all-zero solution vector for the elastoacoustic formulation:
- vec solup(elastoacoustic);
-
- double relresnorm = 1; int iter = 0;
- while (relresnorm > 1e-5)
- {
- electrostatics.generate();
- vec solv = solve(electrostatics.A(), electrostatics.b());
- // Transfer the data from the solution vector to the v field:
- v.setdata(electricdomain, solv);
-
- // Use the now known electric potential v to compute the membrane deflection:
- elastoacoustic.generate();
-
- vec b = elastoacoustic.b();
- mat A = elastoacoustic.A();
- // Compute the norm of the relative residual:
- relresnorm = (b-A*solup).norm()/b.norm();
- solup = solve(A,b);
- u.setdata(solid, solup);
- p.setdata(fluid, solup);
-
- // Smooth the mesh on the vacuum gap:
- laplacian.generate();
- vec solumesh = solve(laplacian.A(), laplacian.b());
- umesh.setdata(vacuumgap, solumesh);
- // Also save the u field on region solid to umesh.
- // This is done by selecting field u with |u on the solu vector.
- umesh.setdata(solid, solup|u);
-
- // Print the iteration number and relative residual norm:
- std::cout << "Relative residual norm @iteration " << iter << " is " << relresnorm << std::endl;
- iter++;
- }
-
- // Write the electric field with an order 1 interpolation at 50 timesteps.
- (-grad(v)).write(electricdomain, "E.pos", 1, 50);
-
- // Write the deflection u with an order 3 interpolation:
- u.write(solid, "u.pos", 3);
- // Also write it at 50 timesteps for a time view.
- u.write(solid, "u.pos", 3, 50);
- // Do the same with p (remember to multiply it by the scaling factor to get the actual pressure):
- (scaling*p).write(fluid, "p.pos", 3, 50);
-
- // Code validation line. Can be removed.
- std::cout << (solup.norm() < 0.0002539 && solup.norm() > 0.0002537);
- }
- int main(void)
- {
- SlepcInitialize(0,{},0,0);
- sparselizard();
- SlepcFinalize();
- return 0;
- }
|