Getting started#

While there are a lot of complicated components to SkellySim, the basic workflow is easy.

  1. Generate a configuration file

    $ python gen_config.py skelly_config.toml

  2. Generate precompute data

    $ skelly_precompute skelly_config.toml

  3. Run the simulation. Recommended in an sbatch script. See example submission script

    $ mpirun skelly_sim --config-file=skelly_config.toml

  4. Visualize or post-process

Example simulation full workflow#

The following example is from our github. It represents a fairly minimal system which has a Fiber attached to a Body and a Periphery. The Body experiences a constant upward force, while the Fiber attaches and hinges to the Periphery when it gets close to it. For more details about the actual configuration generation process, you should check out generate a configuration file. If you’re using singularity, which we recommend to start with, note you’ll have to prefix all your commands as noted in the Installation page. Now, before we get into how to generate images, let’s show what this initial condition looks like

_images/example_ic.png

Download the example and put in a clean directory.

% ls
gen_config.py
% ./gen_config.py
Using default toml file for output: 'skelly_config.toml'. Provide an alternative filename argument to this script to use that instead.
% ls
gen_config.py skelly_config.toml

The config generation scripts we provide all default to making a ‘skelly_config.toml’ file, though this naming isn’t necessary. Your script can name the file whatever it likes, though using this default is nice since most scripts look for it. It’s also worth taking a look at the output just to see what’s inside, though there isn’t really much reason to peer into them unless you want to make a quick change to see what a parameter does.

The next step is to generate some precompute data which is used throughout the simulations (quadrature weights and matrix inversions). Use the skelly_precompute utility to do this. If you’re using a virtual environment or conda for the python portion, make sure to activate that environment first. Note that this can take some time, especially if your Periphery has a lot of nodes.

% skelly_precompute skelly_config.toml
{'nucleation_type': 'auto', 'n_nucleation_sites': 50, 'position': [0.0, 0.0, 0.0], 'orientation': [0.0, 0.0, 0.0, 1.0], 'shape': 'sphere', 'radius': 0.5, 'n_nodes': 400, 'precompute_file': 'body_precompute.npz', 'external_force': [0.0, 0.0, 0.5]}
Building Quadrature Weights
Finished building Quadrature Weights
Building Quadrature Weights
Finished building Quadrature Weights
Creating periphery object
Finished creating periphery object
Finished periphery init.
% ls
body_precompute.npz  gen_config.py  periphery_precompute.npz  skelly_config.toml

Now we have all our configuration and precompute data and are ready to run a simulation. Since this is a very basic simulation, there is no need to use mpirun, which will likely hinder more than help performance. When simulating thousands of fibers or giant Peripheries, mpirun becomes necessary. You may see some warnings about OpenMP from Kokkos. You can ignore the warning or do as it recommends. It may or may not help performance.

Run the simulation as follows. Then maybe go get a cup of coffee, as it should take a few minutes.

% skelly_sim
[2022-03-28 16:04:06.968] [SkellySim] [info] ****** SkellySim 0.9.3 (a5a5baae) ******
[2022-03-28 16:04:06.971] [SkellySim] [info] Preprocessing config file
[2022-03-28 16:04:06.972] [SkellySim] [info] Initializing FiberContainer
[2022-03-28 16:04:08.645] [SkellySim] [info] Reading in 1 fibers.
[2022-03-28 16:04:10.706] [SkellySim] [info] Loading raw precomputation data from file periphery_precompute.npz for periphery into rank 0
[2022-03-28 16:04:11.032] [SkellySim] [info] Done initializing base periphery
[2022-03-28 16:04:14.660] [SkellySim] [info] Reading in 1 bodies
[2022-03-28 16:04:14.737] [SkellySim] [info] Body 0: [ 0, 0, 0 ]
[2022-03-28 16:04:15.691] [SkellySim] [info] Solver converged with parameters: iters 7, time 0.3280859529040754, achieved tolerance 4.6319964036583045e-11
[2022-03-28 16:04:15.731] [SkellySim] [info] Residual: 1.824888062028897e-07
[2022-03-28 16:04:15.731] [SkellySim] [info] Accepting timestep and advancing time
[2022-03-28 16:04:15.732] [SkellySim] [info] System time, dt, fiber_error: 0.1, 0.1, 2.0501852452392555e-06
etc...
% ls
body_precompute.npz  gen_config.py  periphery_precompute.npz  skelly_config.toml  skelly_sim.out

We now have one more file ‘skelly_sim.out’. This is the trajectory information. This contains all solution information at all output times in a binary nested dictionary format msgpack (which is similar to json).

To visualize this, you should be able to open it with our provided blend file (just make sure to put the skelly_blend.py script with it in the same directory. This process isn’t standardized yet unfortunately). By default these are both in the source scripts directory. This doesn’t need the Singularity container or any install at all. Just the ‘skelly.blend’ and ‘skelly_blend.py’ files. The first time the script is run it should bootstrap itself to work with Blender’s packaged Python.

% blender  ~/projects/codes/SkellySim/scripts/skelly.blend -y

Will open Blender, find the simulation, and set up a scene and animation for it. Just make sure to run it from the simulation directory. Also, note the ‘-y’ option, which allows Blender to execute scripts, which our setup depends on. Otherwise you’ll have to click a dialog menu.

If you’d like to animate it, you can use the standard gui facilities, or you can run Blender in batch mode.

% blender  ~/projects/codes/SkellySim/scripts/skelly.blend -y -b -o movie -F AVIRAW -a

will generate a very very large AVI file in your simulation directory (likely named movie0000-0199.avi) in this specific case. If you have a working ffmpeg install (or another utility of your choice), you can compress this by a few orders of magnitude without real loss of quality.

% ffmpeg -y -r 60 -i movie0000-0199.avi -vcodec libx264 -crf 10 -r 60 movie.mp4

Now we probably want to do some analysis. The following code loads in a trajectory and plots vs time the ‘z’ coordinates of the body, the fiber plus end, and the fiber minus end in the body_fiber_periphery_constant_body_force example. It also generates a 3d quiver plot of the velocity field at the 11th time frame of the velocity field. The full code is in the examples/analysis_example.py script.

import numpy as np
import matplotlib
matplotlib.use('TKAgg')
import matplotlib.pyplot as plt

from skelly_sim.reader import TrajectoryReader, Listener, Request

traj = TrajectoryReader('skelly_config.toml')
shell_radius = traj.config_data['periphery']['radius']
body_radius = traj.config_data['bodies'][0]['radius']

body_pos = np.empty(shape=(len(traj), 3)) # COM body position in time
plus_pos = np.empty(shape=(len(traj), 3)) # fiber plus end in time
minus_pos = np.empty(shape=(len(traj), 3)) # fiber minus end in time

for i in range(len(traj)):
    traj.load_frame(i)
    body_pos[i, :] = traj['bodies'][0]['position_']
    minus_pos[i, :] = traj['fibers'][0]['x_'][0, :]
    plus_pos[i, :] = traj['fibers'][0]['x_'][-1, :]

print("system keys: " + str(list(traj.keys())))
print("fiber keys: " + str(list(traj['fibers'][0].keys())))
print("body keys: " + str(list(traj['bodies'][0].keys())))
print("shell keys: " + str(list(traj['shell'].keys())))

ax1 = plt.subplot(1, 2, 2)
ax1.plot(traj.times, body_pos[:, 2], traj.times, plus_pos[:,2], traj.times, minus_pos[:,2])

# Fire up SkellySim in "listener" mode
listener = Listener(binary='skelly_sim_release')

# All analysis requests are done via a "Request" object
req = Request()

# specify frame number to evaluate and evaluator (CPU, GPU, FMM)
req.frame_no = 11
req.evaluator = "GPU"

# Request velocity field
tmp = np.linspace(-shell_radius, shell_radius, 20)
xm, ym, zm = np.meshgrid(tmp, tmp, tmp)
xcube = np.array((xm.ravel(), ym.ravel(), zm.ravel())).T

# Filter out points outside periphery and inside body
relpoints = np.where((np.linalg.norm(xcube - body_pos[11,:], axis=1) > body_radius) &
(np.linalg.norm(xcube, axis=1) < shell_radius))
req.velocity_field.x = xcube[relpoints]

# Make our request to SkellySim! Might take a second...
res = listener.request(req)

x = req.velocity_field.x
v = res['velocity_field']

ax2 = plt.subplot(1, 2, 2, projection="3d")
ax2.quiver(x[:, 0], x[:, 1], x[:, 2], v[:, 0], v[:, 1], v[:, 2])

plt.show()
% python ../analysis_example.py
Loading trajectory index.
Loading trajectory index.
Stale trajectory index file. Rebuilding.
system keys: ['time', 'dt', 'rng_state', 'fibers', 'bodies', 'shell']
fiber keys: ['n_nodes_', 'length_', 'length_prev_', 'bending_rigidity_', 'penalty_param_', 'force_scale_', 'beta_tstep_', 'epsilon_', 'binding_site_', 'tension_', 'x_']
body keys: ['radius_', 'position_', 'orientation_', 'solution_vec_']
shell keys: ['solution_vec_']
_images/example_plots.png