// This code simulates the static deflection of an electrically actuated 2D micromembrane. // The problem is nonlinear because of the electrostatic-elastic coupling. // // The electrostatic problem and the electrostatic forces are computed on a mesh // deformed by the mechanical displacement. The mesh in the vacuum gap below the membrane // is deformed smoothly by solving an additional Laplace problem. // // The interpolation order for the electric potential field and mechanical displacement // is adapted to every geometrical region. #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, ground = 5, clamp = 5, electrode = 6; // 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}); // Nodal shape functions 'h1' for the electric potential field v // and membrane deflection u (u has components x and y). // umesh smoothly deforms the mesh in the vacuum gap for a given membrane deflection. field u("h1xy"), umesh("h1xy"), v("h1"); // Use interpolation order: // // - 3 for u on the membrane // - 2 for u on the pillars // - 1 elsewhere // // - 1 for the electric potential v // // Default order is 1. u.setorder(pillars, 2); u.setorder(membrane, 3); // Clamp and ground (i.e. 0 valued-Dirichlet conditions for u and v): u.setconstraint(clamp); v.setconstraint(ground); // Force the electric potential on the electrode to a close-to-pull-in voltage: v.setconstraint(electrode, 200); // E is Young's modulus. nu is Poisson's ratio. // epsilon is the electric permittivity. // // The membrane is polysilicon, the insulator is silicon dioxyde. parameter E, nu, epsilon; E|insulator = 70e9; E|pillars = 150e9; E|membrane = 150e9; nu|insulator = 0.17; nu|pillars = 0.3; nu|membrane = 0.3; 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; // An electrostatic formulation is used for the electric problem. // An elasticity formulation is used for the mechanical problem. formulation electrostatics, elasticity; // Weak electrostatic formulation, computed on the mesh deformed by field umesh: electrostatics += integral(electricdomain, umesh, epsilon*grad(dof(v))*grad(tf(v))); // The linear elasticity formulation is classical and thus predefined: elasticity += integral(solid, predefinedelasticity(dof(u), tf(u), E, nu, "planestrain")); // 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(electricdomain, umesh, predefinedelectrostaticforce(tf(u,solid), grad(v), epsilon)); // 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 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-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); // Write the electric field with an order 1 interpolation (default). // The electric field is computed and saved on the geometry deformed by umesh. (-grad(v)).write(electricdomain, umesh, "E.pos"); // Use the now known electric potential v to compute the membrane deflection: elasticity.generate(); vec b = elasticity.b(); mat A = elasticity.A(); // Compute the norm of the relative residual: relresnorm = (b-A*solu).norm()/b.norm(); solu = solve(A,b); u.setdata(solid, solu); // Write the deflection u with an order 3 interpolation: u.write(solid, "u.pos", 3); // Smooth the mesh on the vacuum gap: laplacian.generate(); vec solumesh = solve(laplacian.A(), laplacian.b()); // Save the smoothed mesh on the vacuum region: 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, solu|u); umesh.write(electricdomain, "umesh.pos", 3); // Print the iteration number and relative residual norm: std::cout << "Relative residual norm @iteration " << iter << " is " << relresnorm << std::endl; iter++; } // Code validation line. Can be removed. std::cout << (compy(grad(v)).integrate(vacuumgap, u, 4) < 0.0022901 && compy(grad(v)).integrate(vacuumgap, u, 4) > 0.0022900); } int main(void) { SlepcInitialize(0,{},0,0); sparselizard(); SlepcFinalize(); return 0; }