CoSAppLogo

CoSApp tutorials on multimode systems

Multimode ODE

In this example, we show a system defining a simple Ordinary Differential Equation, with an event-driven discontinuity.

[1]:
from cosapp.base import System


class ScalarOde(System):
    """System representing ODE df/dt = F(t)"""
    def setup(self):
        self.add_inward('df')
        self.add_transient('f', der='df')


class MultimodeScalarOde(System):
    def setup(self):
        self.add_child(ScalarOde('ode'), pulling=['f', 'df'])
        self.add_outward_modevar('snapped', False)
        self.add_event('snap')  # event defining mode var `snapped`

    def transition(self):
        if self.snap.present:
            self.snapped = True

[2]:
from cosapp.drivers import RungeKutta
from cosapp.recorders import DataFrameRecorder

s = MultimodeScalarOde('s')

s.snap.trigger = "f > 0.547"

driver = s.add_driver(
    RungeKutta(order=2, time_interval=(0, 1), dt=0.1)
)
driver.add_recorder(DataFrameRecorder(includes=['f', 'df', 'snapped']), period=0.1)

# Set scenario such that df/dt changes with mode
driver.set_scenario(
    init = {'f': 0},
    values = {'df': '2 * t if not snapped else 0'},
)
s.run_drivers()

# Retrieve recorded data
data = driver.recorder.export_data()
data = data.drop(['Section', 'Status', 'Error code'], axis=1)
[3]:
# Plot results
import plotly.graph_objs as go

def get_trace(col):
    return go.Scatter(
        x = data['time'],
        y = data[col],
        name = col,
        mode = "lines+markers",
    )

go.Figure(
    data = [
        get_trace('f'),
        get_trace('df'),
    ],
    layout = go.Layout(
        xaxis = dict(title="Time"),
        height = 450,
        hovermode = "x",
    ),
)

Show recorded data

Note the double entry at snap time. When snapped is False, the solution is \(f(t) = t^2\), since the derivative is \(2t\), with initial condition \(f(0) = 0\). Event time is therefore \(t_{\rm snap} = \sqrt{0.547}\).

[4]:
from math import sqrt
print(f"{sqrt(0.547) = }")
sqrt(0.547) = 0.7395944834840239
[5]:
data
[5]:
Reference df f snapped time
0 t=0.0 0.000000 0.000 False 0.000000
1 t=0.1 0.200000 0.010 False 0.100000
2 t=0.2 0.400000 0.040 False 0.200000
3 t=0.3 0.600000 0.090 False 0.300000
4 t=0.4 0.800000 0.160 False 0.400000
5 t=0.5 1.000000 0.250 False 0.500000
6 t=0.6 1.200000 0.360 False 0.600000
7 t=0.7 1.400000 0.490 False 0.700000
8 t=0.73959448348402 1.479189 0.547 False 0.739594
9 s.snap 0.000000 0.547 True 0.739594
10 t=0.8 0.000000 0.547 True 0.800000
11 t=0.9 0.000000 0.547 True 0.900000
12 t=1.0 0.000000 0.547 True 1.000000