We provide a class to find reduced-order models (continuous and discrete time) by projecting linear dynamics onto modes (Petrov-Galerkin projection). The governing equation of the full system is assumed to be either continuous time:
\partial x(t)/ \partial t &= A x(t) + B u(t) \\ y(t) &= C x(t)
or discrete time:
x(k+1) &= A x(k) + B u(k) \\ y(k) &= C x(k)
where k is the time step. Here x is the state vector. A, B, and C are, in general, linear operators (often arrays). In cases where there are no inputs and outputs, B and C are zero. A acts on x and returns a vector that lives in the same vector space as x. B acts on elements of the input space, \mathbb{R}^p, where p is the number of inputs and returns elements of the vector space in which x lives. C acts on x and returns elements of the output space, \mathbb{R}^q, where q is the number of outputs.
These dynamical equations can be projected onto a set of modes. First, approximate the state vector as a linear combination of r modes, stacked as columns of array \Phi, and time-varying coefficients q(k):
x(k) \approx \Phi q(k) .
Then substitute into the governing equations and take the inner product with a set of adjoint modes, columns of array \Psi. The result is a reduced system for q, which has as many elements as \Phi has columns, r. The adjoint, (\,\,)^+, is defined with respect to inner product weight W.
q(k+1) &= A_r q(k) + B_r u(k) \\ y(k) &= C_r q(k) \\ \text{where} \\ A_r &= (\Psi^+ \Phi)^{-1} \Psi^+ A \Phi \\ B_r &= (\Psi^+ \Phi)^{-1} \Psi^+ B \\ C_r &= C \Phi
An analagous result exists for continuous time.
If the modes are not stacked into arrays, then the following equations are used, where [\,\,]_{i,j} denotes row i and column j.
[\Psi^+ \Phi]_{i,j} &= \langle\psi_i,\, \phi_j \rangle_W \\ [\Psi^+ A \Phi]_{i,j} &= \langle\psi_i,\, A \phi_j\rangle_W \\ [\Psi^+ B] &= \langle \psi_i,\, B e_j\rangle_W \\ [C \Phi]_{:,j} &= C \phi_j
e_j is the jth standard basis (intuitively B e_j is [B]_{:,j} if B is an array.).
The A, B, and C operators may or may not be available within Python. For example, you may do simulations using code written in another language. For this reason, modred requires only the action of the operators on the vectors, i.e., the products A \phi_j, Be_j, and C \phi_j, and not the operators A, B, and C themselves.
Here's an example that uses arrays.
.. literalinclude:: ../modred/examples/rom_ex1.py
The array basis_vecs
contains the vectors that define the basis onto which
the dynamics are projected.
A few variations of this are illustrative. First, if no inputs or outputs exist, then there is only A_r and no B_r or C_r. The last two lines would then be replaced with:
A_reduced = LTI_proj.reduce_A(A.dot(basis_vec_array)) LTI_proj.put_A_reduced('A_reduced.txt')
Another variant is if the basis vecs are known to be orthonormal, as is always
the case with POD modes.
Then, the \Psi^* W \Phi array and its inverse are identity, and
computing it is wasteful.
Specifying the constructor keyword argument is_basis_orthonormal=True
tells
modred this array is identity and to not compute it.
Here's an example similar to what might arise when doing large simulations in another language or program.
.. literalinclude:: ../modred/examples/rom_ex2.py
This example works in parallel with no modifications.
If you do not have the time-derivatives of the direct modes but want a continuous time model, see :py:func:`ltigalerkinproj.compute_derivs_arrays` and :py:func:`ltigalerkinproj.compute_derivs_handles`.
See the documentation and examples provided in :py:func:`era.compute_ERA_model` and :py:class:`era.ERA`.