Skip to content

Cheap and accurate integrated flux computation in conservation based ODE systems #2889

@SouthEndMusic

Description

@SouthEndMusic

Background

In Ribasim we solve a system of ODEs for the water storage in water bodies of the form

$$ \frac{\text{d}S}{\text{d}t} = BC(t) + \sum q_\text{in} - \sum q_\text{out} $$

i.e. a simple conservation equation per water body with boundary conditions and exchanges with neighboring water bodies. Our first OrdinaryDiffEq.jl implementation of this used the water storages in the water bodies as states. A downside however is that the solver then doesn't tell us the integrated fluxes between the water bodies, which is important for many of our applications. These fluxes can not be reconstructed from the storage changes as this is an underdetermined problem. We can compute the integrated fluxes from the instantaneous fluxes only when using simple algorithms (e.g. Euler forward/backward), but we settled on QNDF because it gives us the best performance (large models have ~50.000 water bodies).

Our next step was to reformulate our ODE in terms of integrated fluxes, i.e the ODEs simply become

$$ \frac{\text{d}Q}{dt} = q, $$

where the storages are formulated from the $Q$ in the right hand side. This way the solver gives us accurate integrated fluxes. This however has a large performance drawback, because there are many more flux terms than there are storage terms.

Current developments

I'm currently working on a PR where I formulate the ODE system in terms of integrated flux states, but solve internally in terms of storage states. The key insight is that the computation from integrated fluxes to storages is linear, so that our ODE system is of the form

$$ \frac{\text{d}\textbf{u}_\text{integrated flux}}{\text{d}t} = f(\textbf{u}_\text{integrated flux}, p, t) = g(\textbf{u}_\text{storage}, p, t) = g( A\textbf{u}_\text{integrated flux} + b(t), p, t), $$

using that

$$ \textbf{u}_\text{storage} = A\textbf{u}_\text{integrated flux} + b(t) $$

where $A$ is a highly sparse matrix with $1$ and $-1$ entries based on the connectivity of the water bodies and $b(t)$ consists of the initial storages and exactly integrated influxes.

There are several things we can do to take advantage of this structure of $f$, using that $\textbf{u}_\text{storage}$ lives in a much lower dimensional space ($\mathbb{R}_{\ge 0}^k$) than $\textbf{u}_\text{integrated flux}$ ($\mathbb{R}_{\ge 0}^n$):

  • For Jacobian computation we can make use of the chain rule to only use AD to compute the $(n, k)$ sized Jacobian of $g$ instead of the $(n, n)$ sized Jacobian of $f$;
  • For the linear solve we can use the Woodbury matrix identity to solve in terms of $k$ unknowns instead of $n$ unknowns, see here for the precise derivation.

Recommendations and open questions

  • It required quite a bit of overloading internals of OrdinaryDiffEq.jl to get the above to work. Since it is quite a general procedure (applicable for every conservation based ODE system where accurate integrated flux computation is important), it might merit its own subpackage
  • I would like to also like to compute EEst in the reduced space, but this isn't easy to do because of how this computation is incorporated in perform_step! based on calculate_residuals! and internalnorm in an algorithm specific way
  • Currently we have to constantly update our reltol in the integrated flux formulation because of the ever growing state values (even in a dynamic equilibrium situation), it would be nice if this could be handled internally
  • Generally speaking, it seems that the OrdinaryDiffEq.jl internals aren't really designed to incorporate the described kind of 'state space reduction', is there a nice way to get around this?
  • Maybe there is a way to achieve the above with a state object that contains both S and Q?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions