Solver lifecycle

A Python simulation follows four phases. Keeping this boundary explicit avoids rebuilding setup data during time stepping and prevents access to state that does not exist yet.

1. Construct the solver

Choose worker device placement when constructing DEMSolver. Device placement cannot be changed later. See CUDA device selection.

2. Describe the system

Before initialization:

  • load materials and clump or mesh templates;

  • set the domain and boundary conditions;

  • add clumps, meshes, or external objects;

  • configure gravity, the force model, timestep, output, and family behavior;

  • create trackers for batches or objects whose state will be queried.

Setup methods return native-backed handles. Retain the solver for at least as long as any of these handles.

3. Initialize

Call Initialize() once after setup:

solver.SetInitTimeStep(1.0e-5)
solver.Initialize()

Initialization resolves cached topology and prepares runtime/JIT CUDA data. Methods documented as post-initialization operations must not be used earlier.

Python processes use the same persistent Jitify header cache as C++ programs. Set DEME_PERSISTENT_JITIFY_CACHE in the shell that launches Python, or set os.environ["DEME_PERSISTENT_JITIFY_CACHE"] before importing deme and calling Initialize(). Use an explicit, user-owned filename; unset the variable to retain normal non-persistent Jitify behavior.

To compare cold and warm initialization in separate Python processes, run python/demos/jitify_cache_timing.py from the repository root twice with the same cache filename. Remove the cache before the first run so that it measures header discovery rather than reuse.

4. Advance, synchronize, and retrieve

DoDynamics(duration) advances for a duration and may return before all worker activity needed by a subsequent host query is synchronized. DoDynamicsThenSync(duration) is the convenient choice when the next action reads or modifies simulation state. DoStepDynamics() advances one timestep.

Trackers provide scalar and bulk host access:

solver.DoDynamicsThenSync(0.01)
first_position = tracker.Pos()
all_positions = tracker.Positions()

They can also write directly to caller-owned CUDA allocations; see Retrieving simulation data.

Output methods may queue work. Call WaitForPendingOutput() before relying on all requested files being complete. The solver destructor also waits for pending output, but explicit synchronization makes program behavior clearer.