Cryo-EM Ensemble Optimization¶
You can run the ensemble optimization through our API, as shown in our Tutorial, or through the command line:
Input Format¶
Cryo-EM Ensemble Optimization uses a custom YAML input format. A sample config file for running the ensemble optimization on a GPU with three walkers is shown below:
# Parameters marked with (*) are optional
data_params:
path_to_relion_project: /path/to/relion/project/
path_to_starfile: /path/to/starfile.star
loads_envelope: True # (*) Default: false, loads envelope function parameters
data_sign: "dark-on-light" # Relion convention, dark particles on a light background
path_to_volumetric_mask: /path/to/volumetric_mask.mrc # (*) e.g., the mask from a refinement procedure
alignment_params:
path_to_prealigned_atomic_model: /path/to/prealigned_atomic_model.pdb # an atomic model aligned to the consensus volume
path_to_reference_volume: path_to_reference_volume.mrc # (*) Reference volume (for local alignment)
downsample_box_size: 32 # (*) Downsample box size for reference volume
projector_params:
bias_constant_in_kjpermol: 3.0e5 # Bias constant for the Steered MD projection (might require tuning)
n_steps: 1000
path_to_initial_states: # (*) Initial states for each replica, useful for continuing optimizations. Optional.
- ./initial_state_0.xml # MUST BE DIFFERENT FOR EACH REPLICA if provided
- ./initial_state_1.xml
- ./initial_state_2.xml
platform: CUDA
platform_properties:
DeviceIndex: '0'
# platform: CPU # CPU version for defining the platform
# platform_properties:
# Threads: "32" # Can be unreliable, OpenMM does not always use all available threads
md_params: # (*) Override default OpenMM simulation parameters. All fields are optional.
forcefield: amber14-all.xml # (*) Default: amber14-all.xml
water_model: amber14/tip3p.xml # (*) Default: amber14/tip3p.xml
nonbonded_method: CutoffNonPeriodic # (*) Options: PME, CutoffNonPeriodic, NoCutoff, CutoffPeriodic, Ewald, LJPME
nonbonded_cutoff_nm: 1.0 # (*) Default: 1.0 nm
constraints: HBonds # (*) Options: HBonds, AllBonds, HAngles, null (no constraints)
temperature_K: 300.0 # (*) Default: 300 K
friction_per_ps: 1.0 # (*) Default: 1.0 ps^-1
timestep_ps: 0.002 # (*) Default: 0.002 ps (2 fs)
likelihood_optimizer_params:
batch_size: 50 # Batch size used for computing the log-likelihood in parallel
initial_weights: # (*) Initial weights, one for each replica. Default is 1/M, M = number of walkers
- 0.33
- 0.33
- 0.33
n_steps: 10
step_size: 2.0
n_batches_per_step: 5 # allows for memory-friendly computation of gradients
estimates_poses: true # (*) Default: false. Estimate poses during the optimization instead of using the ones in the starfile
pose_search_params: # (*) Settings for that search. Ignored when estimates_poses is false. Every field is optional
n_rounds: 5 # (*) Number of refinement rounds. 0 searches the base grid only
initial_resolution: 1 # (*) Resolution of the base (coarsest) SO(3) grid
n_candidates: 40 # (*) Number of orientations kept at the end of each round
n_angles_in_parallel: 10 # (*) Orientations evaluated in parallel (vmapped)
shift_search_range_in_angstroms: 20.0 # (*) Half-width of the searched shift range. Omit to search all shifts
volume_integrator_backend: # (*) How walker volumes are projected into images. Every field is optional
spread_mode: local # (*) Default: local. Options: local, exact
spread_width_in_stds: 6.0 # (*) Default: 6.0. Ignored when spread_mode is 'exact'
sampling_mode: average # (*) Default: average. Options: average, point
enable_pallas: false # (*) Default: false. Pallas/Triton GPU kernels. Requires a CUDA GPU and spread_mode: local
atom_selection: ... # path to a txt/npy file, or a mdtraj-compatible selection string, e.g., "not element H"
loads_b_factors: true # Load Debye-Waller b-factors from provided PDBs
n_steps: 100 # Number of ensemble optimization steps
path_to_atomic_models: # Can be the same for each replica
- path/to/atomic_models/initial_model_0.pdb
- path/to/atomic_models/initial_model_1.pdb
- path/to/atomic_models/initial_model_2.pdb
path_to_output: ./optimization_output/round3/
rng_seed: 0 # seed for all RNG operations
Comments on the input parameters¶
Alignment is crucial. If the structure is not aligned to the frame of reference of the cryo-EM particles, the likelihood will not be computed correctly and the optimization will diverge as the structure is driven toward noise. When possible, include a reference volume for alignment — this is particularly important for heterogeneous systems. Alignment typically adds about 1 second per iteration for volumes with a 32-pixel box size (controllable via downsample_box_size in alignment_params). A dilated volumetric mask can also help by reducing background noise in the images.
The path_to_initial_states argument is useful for restarting simulations or warm-starting from a previously equilibrated MD state. We recommend providing a unique state file for each walker to avoid numerical issues that arise when walkers are indistinguishable.
The optional md_params block inside projector_params lets you override any of the underlying OpenMM simulation parameters without modifying the code. All fields are optional and fall back to built-in defaults when omitted. The nonbonded_method field accepts string aliases: CutoffNonPeriodic (default, for implicit/vacuum simulations), PME or Ewald (for explicit solvent with a periodic box), CutoffPeriodic (periodic box with a simple cutoff), LJPME (PME for both electrostatics and Lennard-Jones), and NoCutoff (no cutoff; very small systems only). Note that periodic methods require a simulation box defined in the PDB. The constraints field accepts HBonds (default, constrains bonds to hydrogen — compatible with a 2 fs timestep), AllBonds, HAngles (allows ~4 fs timesteps), or null to disable constraints entirely. Numeric fields use explicit unit suffixes: _nm for nanometers, _K for Kelvin, _per_ps for ps⁻¹, and _ps for picoseconds. The platform and platform_properties fields are set separately at the top level of projector_params.
Setting estimates_poses: true inside likelihood_optimizer_params turns on pose estimation during the optimization. When it is false (the default), the poses stored in the starfile are used as they are. When it is true, the pose of each particle is re-estimated for every walker at each step with a hierarchical SO(3) grid search, which is considerably more expensive. The search itself is configured by the optional pose_search_params block, whose fields all have defaults: the cost grows with n_rounds and n_candidates, while n_angles_in_parallel trades memory for speed. Restricting the translational search with shift_search_range_in_angstroms is recommended when the particles are already roughly centered. This feature is still experimental.
Volume integrator backend¶
Every likelihood evaluation projects each walker — a mixture of gaussians, one per atom — onto the image plane. The optional volume_integrator_backend block inside likelihood_optimizer_params controls how that projection is computed. It affects only speed, memory, and numerical accuracy, not the model being optimized, and all of its fields fall back to built-in defaults when omitted. It does not affect the alignment step or the MD projector.
spread_mode(defaultlocal):localspreads each gaussian onto only the grid points near its center, with the truncation width set byspread_width_in_stds.exactinstead evaluates dense gaussian integrals over the whole grid. Since gaussians are short-ranged compared to typical box sizes,localis much faster for the same result, and is the recommended choice; useexactwhen you want a reference calculation with no truncation. Inlocalmode the number of grid points per gaussian is derived automatically from the atom variances and the pixel size, and the projection is computed on a plane twice the box size in each dimension before being cropped back.spread_width_in_stds(default6.0): how far each gaussian is spread, in standard deviations, whenspread_mode: local. Lowering it makes the projection cheaper in both time and memory, at the cost of truncating the tails more aggressively. It is ignored — with a warning — whenspread_mode: exact.sampling_mode(defaultaverage):averageuses error functions to compute the average of the gaussian over each pixel, which is the more accurate choice, especially for coarse pixel sizes.pointsimply evaluates the gaussian at the pixel center, which is cheaper but noisier.enable_pallas(defaultfalse): whether to use Pallas/Triton GPU kernels instead of the pure-JAX implementation. This requires a CUDA GPU — it raises an error if requested without one — and only applies whenspread_mode: local; it is ignored forexact.
The main reason to set enable_pallas: true is memory rather than speed. The pure-JAX forward pass is usually the faster of the two, but its memory grows with the number of particles projected at once, whereas the Pallas backward pass is a pure gather with a flat memory profile. That makes it worth enabling when a large batch_size exhausts GPU memory, at the cost of giving up some forward-pass speed. If you are not memory-limited, leave it false. Note that this field is always passed through explicitly, so it takes precedence over the CRYOJAX_ENABLE_PALLAS environment variable.
Outputs¶
final_ensemble.npz: final walker positions and weights.final_walker_*.pdb: a PDB file for each final walker.traj_walker_*.xtc: the trajectory followed by each walker during the optimization.states_proj_*/: a directory containing the OpenMM states at each iteration. Useful for restarting.- A
logfile.
Known Issues¶
Crashing OpenMM Simulations¶
This issue occurs rarely and is difficult to reproduce. Occasionally, the steered MD simulation will explode during the optimization. In some cases this is caused by water molecules in the input PDB — make sure to exclude them using the atom_selection parameter. In other cases, changing the nonbonded interaction method can resolve the issue; try different values for nonbonded_method in the md_params block of the config file.
Corrupted CryoSPARC Datasets¶
If your dataset contains particles picked in CryoSPARC, we have found that MRC files are sometimes padded with zeros, producing zero-value particles that cause numerical issues (division by zero when computing the likelihood). This can be fixed by preprocessing the dataset; we are working on providing a simple command-line script to do this.