How it works

Time integration

A Struphy model is never integrated as one system. It issplit into propagators, and a time step is a composition of them. That makes two separate choices: how the substeps are composed over the step, and how each substepsolves its own equations internally. The first is the splitting scheme, set once per simulation; the second belongs to each propagator.

Splitting schemes

Struphy composes a model's propagators in one of 2 ways, chosen withTime(split_algo=…). Pick a model to see what one step of dt actually does.

Open model page →

LieTrotter

order 1default

Apply every propagator once, in the model's order, each over the full step.

    The cheapest composition: one pass through the propagator list. It is first-order accurate in time, because the propagators do not commute -- the error left over from applying them in sequence rather than simultaneously is O(dt²) per step.

    Strang

    order 2

    Half-steps down the list, one full step of the last propagator, then the half-steps again in reverse.

      Making the composition palindromic cancels the leading error term, giving second-order accuracy for roughly twice the work per step. It needs at least two propagators, so a model with a single step can only run Lie-Trotter.

      6 models have a single propagator and can therefore only run Lie-Trotter: Poisson, ShearAlfven, TwoFluidQuasiNeutralToy, DeterministicParticleDiffusion, RandomParticleDiffusion, Maxwell.

      Inside a propagator

      Within its slice of the step, a propagator solves its own equations — most implicitly, with Crank-Nicolson (the implicit midpoint rule), which is what makes the substeps structure-preserving.7 propagators expose that choice as an algo option:

      Runge-Kutta tableaux

      A propagator stepping explicitly takes its coefficients from a ButcherTableau. All 6 are explicit — the coefficient matrixa is strictly lower-triangular, so every stage depends only on the stages before it and no stage requires a solve. Set one with CurrentCoupling5DGradB.Options(algo="explicit", butcher=ButcherTableau("heun3")).

      forward_euler1 stage · order 1
      Butcher tableau for forward_euler
      0
      1
      heun22 stages · order 2
      Butcher tableau for heun2
      0
      11
      1/21/2
      rk22 stages · order 2
      Butcher tableau for rk2
      0
      1/21/2
      01
      heun33 stages · order 3
      Butcher tableau for heun3
      0
      1/31/3
      2/302/3
      1/403/4
      3/8 rule4 stages · order 4
      Butcher tableau for 3/8 rule
      0
      1/31/3
      2/3-1/31
      11-11
      1/83/83/81/8
      rk44 stages · order 4 · default
      Butcher tableau for rk4
      0
      1/21/2
      1/201/2
      1001
      1/61/31/31/6

      Setting it up

      Both choices are made in the parameter file — the splitting once, the scheme per propagator.

      from struphy.io.options import Time
      from struphy.ode.utils import ButcherTableau
      
      # outer: how the propagators are composed (default: LieTrotter)
      time = Time(dt=0.01, Tend=1.0, split_algo="Strang")
      
      # inner: how one propagator solves its own step
      maxwell.options = MaxwellWeakAmpere.Options(
          algo="explicit",
          butcher=ButcherTableau("rk4"),
      )

      See the FEEC basics page for the spatial half of the discretization, and each propagator's page for the options it accepts.