The simulation model

To construct a Lorenz-63 simulation model for pypfilt, we need to create a Model subclass that defines the state vector structure and the state update rule. Because this model is a system of ordinary differential equations, we can derive from the OdeModel class, which provides a convenient wrapper around scipy.integrate.solve_ivp().

Here, we define the state vector to contain six floating-point values:

\[x_t = [\sigma, \rho, \beta, x, y, z]^T\]
  • The state vector structure is defined by the field_types() method, which returns a list of (name, type) pairs.

  • The right-hand side is defined by the d_dt() method, which keeps the parameters \(\sigma\), \(\rho\), and \(\beta\) fixed, and calculates the rate of change for \(x(t)\), \(y(t)\), and \(z(t)\).

class Lorenz63(OdeModel):
    def field_types(self, ctx):
        r"""
        Define the state vector :math:`[\sigma, \rho, \beta, x, y, z]^T`.
        """
        return [
            ('sigma', float),
            ('rho', float),
            ('beta', float),
            ('x', float),
            ('y', float),
            ('z', float),
        ]

    def d_dt(self, time, xt, ctx, is_forecast):
        """
        The right-hand side of the ODE system.

        :param time: The current time.
        :param xt: The particle state vectors.
        :param ctx: The simulation context.
        :param is_forecast: True if this is a forecasting simulation.
        """
        rates = np.zeros(xt.shape, xt.dtype)
        rates['x'] = xt['sigma'] * (xt['y'] - xt['x'])
        rates['y'] = xt['x'] * (xt['rho'] - xt['z']) - xt['y']
        rates['z'] = xt['x'] * xt['y'] - xt['beta'] * xt['z']
        return rates