High-level interface

SimState is the high-level interface of AutoPDEx. It bundles a finite-element simulation behind a single object: you declare one spatial and temporal discretization per field, import or generate a mesh, register the governing equations as compact model functions, and add boundary and initial conditions. SimState then builds the global DOF numbering, sets up the assembly and runs the transient solve. The run method is jittable and diffable via implicit differentiation. Examples for sensitivity analysis applications are under development.

Typical workflow

Most simulations follow this compact setup sequence:

sim = SimState(spatial_discretizations_per_field)
sim.import_mesh(meshio_object)
sim.add_temporal_discretization(time_integrators)
sim.add_model("__all__", "weak form", weak_form_fun)
sim.add_strong_bc(field, on_boundary_fun, value_fun)
sim.add_weak_bc(field, on_boundary_fun, value_fun)
sim.set_initial_conditions(value_fun)
sim.set_postprocessing_policy(postprocessing_policy)
sim.set_step_size_controller(time_step_controller)
sim.initialize()
sim.prepare()
sim = sim.run(dt0, time_span, num_time_steps) # jax transformable

SimState class

class autopdex.SimState(spatial_discretizations: Dict[str, Any])[source]

AutoPDEx high-level interface (HLI): orchestrator of the simulation workflow.

SimState encapsulates the complete simulation workflow (mesh import -> domain/BC/model registration -> DOF numbering -> assembly setup -> time integration) as a state machine. The object is registered as a JAX pytree and the run method is transformable via jit, jacfwd or jacrev.

Model context

Model functions (weak forms / potentials) registered with SimState.add_model(...) receive a SimState.ModelContext argument. It is a data class providing the following attributes:

models.ModelContext(mode, x_int, trial_ansatz)

Argument passed to every high-level model function registered via SimState.add_model().

Writing a model function

A model function receives a single ModelContext and returns a different object depending on ctx.mode which can be "weak form", "potential", "internal variables", or "output".:

def model(ctx: SimState.ModelContext):
    phi = ctx.trial_ansatz["phi"]               # trial field callable
    grad_phi = jax.jacfwd(phi)(ctx.x_int, ctx.t)

    if ctx.mode == "output":                    # derived quantities for postprocessing
        return {"my quantity": ...}

    test = ctx.test_ansatz["phi"]               # test field callable (not available in output mode)
    grad_test = jax.jacfwd(test)(ctx.x_int)
    return grad_phi @ grad_test

Setup and execution

SimState(spatial_discretizations)

AutoPDEx high-level interface (HLI): orchestrator of the simulation workflow.

SimState.import_mesh(mesh[, ...])

Load mesh data from a meshio mesh object.

SimState.add_structured_mesh(n_elements, ...)

Generate a structured 2D or 3D mesh over a quadrilateral or hexahedral domain, with an optional subdivision into simplex (triangular/tetrahedral) elements.

SimState.add_temporal_discretization(...)

Assign a time-integration scheme to each field.

SimState.add_model(domain_name, domain_type, ...)

Register a model (weak form or potential) on one or more domains.

SimState.add_local_subsystem(key, local_dae, ...)

Register a local (element-level) DAE subsystem for internal variables.

SimState.add_strong_bc(field, ...[, index, ...])

Register a strong boundary condition via an internal L2 projection on the selected boundary trace.

SimState.add_weak_bc(field, on_boundary_fun, ...)

Add a weak boundary condition as a convenience wrapper around a surface weak form.

SimState.set_initial_conditions([values, ...])

Set domain-wise initial values or restart from a TimeSteppingManagerState.

SimState.set_postprocessing_policy([policy, ...])

Configure dae.SavePolicy for VTU/PVD output during run(...).

SimState.set_step_size_controller([controller])

Configure the dae.StepSizeController used by the global time stepping manager.

SimState.set_dof_scaling([scales])

Configure constant per-field DOF scaling.

SimState.set_root_solver(root_solver)

Configure the root solver used by the global time stepping manager.

SimState.initialize([verbose])

Finalize the input state and set up the time stepping manager.

SimState.prepare([clean_up])

Prepare the initialized simulation for run(...).

SimState.run(dt0, time_span, num_time_steps, *)

Run the prepared simulation over a time span and return the updated SimState.

Accessing results

After run(...) the returned SimState exposes the solution and the time-integration statistics as properties. Further transformation of the results for sensitivity analysis purposes is under development.

SimState.dofs

SimState.num_steps

SimState.num_accepted

SimState.num_rejected

Spatial discretization

The following spatial discretization classes are currently available:

spaces.H1(order, dim, field_dimension)

Continuous (H1-conforming) Lagrange finite element space of degree order.

spaces.L2(order, dim, field_dimension)

Discontinuous L2 polynomial space:

spaces.InternalVariable([field_dimension])

Gauss-point history variable for the high-level SimState interface.

Temporal discretization compatible with SimState

Current limitations: Time integrators with coupled stages, such as Gauss–Legendre Runge–Kutta methods, are not supported for primary fields. Algebraic constraints don’t work with explicit stages. The time integrators of different fields must be compatible with each other, i.e. the stage positions must be the same for all fields. The multi-step methods (BDF, AdamsMoulton, AdamsBashforth) are only compatible with the ConstantStepSizeController.