Dynamics characterization for a MEMS electrostatic inchworm motor and its gap closing actuators
Publication
- A. M. Rauf, D. S. Contreras, R. M. Shih, C. B. Schindler, and K. S. J. Pister, “Nonlinear Dynamics of Lateral Electrostatic Gap Closing Actuators for Applications in Inchworm Motors,” Journal of Microelectromechanical Systems, vol. 31, no. 1, pp. 29–36, Feb. 2022, doi: 10.1109/JMEMS.2021.3130957.
This code just requires numpy
, scipy
, and matplotlib
, although some scripts also involve the
scikit-learn.metrics
module. A good script to run first after setup
is python sim_gca_transient.py
to get a sense for what's going on.
You install the requirements all in one go by calling pip install -r requirements.txt
.
Note for Running This Code in Command Line/CodeOcean
This code was written in PyCharm, which runs files in their native locations and includes all files in the
overall directory in its system path. This doesn't happen when running this code in command line, so some script
files may require you to (a) navigate to the scripts/
directory before running your code, and
(b) fiddle with the import paths. See the FAQ below for code snippets that you can
try appending to the top of your file to help out with (b).
This simulation model is meant to be modular, so you can easily swap between different layouts and/or processes without changing too much of the actual simulation code.
-
process.py
: the process parameters for a 2-layer SOI process, or any other processes that you might come up with. You can import your respective process withfrom process import SOI
-
layouts/fawn.csv
,layouts/gamma.csv
, etc.: the layout files (named after the layouts of their respective runs). These files store the drawn dimensions of your model. The code written so far is robust to anything after the second comma (so you can write comments there). See the section below with more details on the required parameters. -
gca.py
: A simulation model for a gap closing actuator. The input is a drawn dimensions filename and the initial position. The default initial positions for pull-in and release can be found via the functionsGCA.x0_pullin()
andGCA.x0_release()
.- The simulation input is an input array
u = [V, F_external]
. The state is[x_spine, v_spine]
.
- The simulation input is an input array
-
assembly.py
: Used to package multiple GCA's or other components together. Each component's state can affect each other, so this modularity can help simulate larger components.from assembly import AssemblyGCA
: A basic GCA, with no other components
-
scripts/
: All the script files. There are generally 4 types of scripts in this folder:scripts/calculate_*.py
: These are just helper files for calculating different numbers in the paper.scripts/plot_*.py
: These are plotting file, which I used to generate the figures in the paper.scripts/sim_*.py
: These are the core simulation files. Run one of these to simulate a GCA's dynamics.scripts/test_*.py
: These are another type of helper file, which I used to test different evaluation metrics and the like.
-
scripts/sim_gca_V_Fext_pullin.py
, etc.: Your simulation files. Because of the structure above, you can swap process nodes by changing an import statement or layouts by just swapping which.csv
file you're reading from. The basic structure of the code is to:- Initialize a model with its drawn dimensions file and specify whether its initial state (e.g. whether it's pulling in or releasing)
- Specify the input, e.g.
u = [V, Fext]
- Simulate the assembly with a differential equation solver, like in the code below. You can then
check whether or not your simulation terminated by checking whether
len(sol.t_events[0]) > 0
:
from scipy.integrate import solve_ivp
def sim_gca(model, u, t_span, verbose=False):
f = lambda t, x: model.dx_dt(t, x, u, verbose=verbose)
x0 = model.x0()
terminate_simulation = lambda t, x: model.terminate_simulation(t, x)
terminate_simulation.terminal = True
sol = solve_ivp(f, t_span, x0, events=[terminate_simulation], dense_output=True, max_step=0.5e-6)
return sol
Note that the max_step
parameter in solve_ivp()
is actually quite important - generally,
in Python's implementation of solve_ivp()
, for small time scales like what we're working with
the final time recorded by the terminate_simulation
condition will be some multiple of max_step
.
Thus, for production code it's generally good to have low values for max_step
(e.g. 0.1e-6), but for
quick prototyping it's sufficient to have larger values (e.g. 0.25e-6, 0.5e-6).
-
I get the error
gca.py:149: RuntimeWarning: invalid value encountered in sqrt
orgca.py:177: IntegrationWarning: The occurrence of roundoff error is detected, which prevents the requested tolerance from being achieved. The error may be underestimated.
when running my code and using Fes_calc2 (i.e. choosing calc_method=2 as an option for Fes() in gca.py).- This is likely because the time step of the integration is too large. You can change this in the max_step option in solve_ivp() (e.g. see the sim_gca() function in sim_gca_V_fingerL_pullin_release.py for reference. Note that not all integral solvers in Scipy accept the max_step argument - the default RK45 method has been found to work pretty well). The error is caused when the gap between fingers becomes negative (i.e. pull-in happens, but the integration routine jumps over the terminate_simulation boundary condition instead of converging to it).
-
I get the warning
mio.py:226: MatReadWarning: Duplicate variable name "None" in stream - replacing previous with new Consider mio5.varmats_from_mat to split file into single variable files matfile_dict = MR.get_variables(variable_names)
- Probably not an issue? This is caused by some file formatting issue in how I'm reading the data files (in the
/data
folder). I'm not sure exactly how to fix this, to be honest - fiddling with the import settings should be enough, but it reads the data correctly so who cares?
- Probably not an issue? This is caused by some file formatting issue in how I'm reading the data files (in the
-
I get
ModuleNotFoundError
errors when trying to run scripts in command line- See the note at the top about running this code in command line. Add the following code to the beginning of your script file, making sure to change the part in brackets to your specific installation
import os file_location = os.path.abspath(os.path.dirname( __file__)) dir_location = os.path.abspath(os.path.join(file_location, '..')) import sys sys.path.append(file_location) # or just the path to the script's folder, generally speaking sys.path.append(dir_location) # or just the path to the gca_dynamics folder, generally speaking
Unfortunately in hindsight, many of the parameters in the layout file somewhat differ from the paper. Below is a description, both textually and graphically, of the important parameters when defining the layout and process files.
###Layout Files
Note: All distances are written in the layout file as drawn (don't include undercut!)
name, value
gf, 4.83e-6 # front gap (called x_0 in the paper)
gb, 7.751e-6 # back gap (called x_b in the paper)
x_GCA, 3.83e-6 # distance the spine has to travel before hitting the gap stop (x0 - xf in the paper)
supportW, 3e-6 # width of the support beams (called w_spr in the paper)
supportL, 240.851e-6 # length of the support beams (called L_spr in the paper)
Nfing, 70 # number of GCA fingers (called N in the paper)
fingerW, 5.005e-6 # width of GCA fingers (called wf in the paper)
fingerL, 76.472e-6 # overlap length of the GCA fingers (called Lol in the paper)
fingerL_buffer, 10e-6 # extra length at the base of the GCA fingers but which doesn't overlap adjacent fingers (called L - Lol in the paper)
spineW, 20e-6 # width of the spine (called w_spine in the paper)
spineL, 860e-6 # length of the spine (called L_spine in the paper)
etch_hole_size, 8e-6 # size of etch hole squares in the spine (not in paper, see figure below)
etch_hole_spacing, 6e-6 # spacing between etch hole squares in the spine (not in paper, see figure below)
gapstopW, 10e-6 # width of the gap stop jutting out from either side of the spine (not in paper, see figure below)
gapstopL_half, 45e-6 # length of the gap stop jutting out from either side of the spine (not in paper, see figure below). To avoid double-counting area, this is only the length from the side of the spine to the end of the gapstop.
anchored_electrodeW, 787.934e-6 # this was used for an older version of the GCA schematic, can be deleted in more recent versions
anchored_electrodeL, 50e-6 # this was used for an older version of the GCA schematic, can be deleted in more recent versions
See the file process.py
for descriptions of all the process parameters when fabricating and testing the devices. Some
parameters, such as the undercut, unfortunately needs to be calibrated for your fabrication process (although you can
also fit the undercut via the _undercut.py
scripts in the scripts/
folder).
This library originated from work by Daniel Contreras (Ph.D.), Craig Schindler (Ph.D.), and Ryan Shih (M.S.) at UC Berkeley under Professor Kristofer Pister. Daniel's and Craig's Ph.D. dissertations can be found here and here, respectively. Many thanks to them for providing the preliminary MATLAB simulation scripts they used in their dissertations.