Skip to content

single_step

A collection of single-step solvers.

SingleStepMethod

Bases: TimeIntegrator

A base class for single step solvers. All single step solvers inherit these methods as well as the methods from time_integrator.TimeIntegrator.

Source code in src/odeiter/single_step.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class SingleStepMethod(TimeIntegrator):
    """A base class for single step solvers. All single step solvers
    inherit these methods as well as the methods from
    [`time_integrator.TimeIntegrator`](time_integrator.md).
    """

    @abstractmethod
    def update(
        self,
        t: float,
        u: np.ndarray[float],
        f: np.ndarray[float],
        delta_t: float,
    ) -> np.ndarray[float]:
        """
        Compute the next time step. You probably want `solution_generator` instead.

        Parameters:
            t: The current time.
            u: The solution at the current time-step.
            f: the right-hand-side at at the current time-step.
            delta_t: the temporal step-size.

        Returns:
            The solution at the next time step.
        """
        ...

    def solution_generator(
        self,
        u0: np.ndarray[float],
        rhs: Callable[[float, np.ndarray[float]], np.ndarray[float]],
        time: TimeDomain,
    ) -> Generator[np.ndarray[float], None, None]:
        """Create a generator that yields the solution for each time in `time`.

        Parameters:
            u0: The initial condition of the system.
                Must be the same size as the system.
            rhs: The right-hand-side as a function with signature `rhs(t, u) -> u'`.
            time: The discretized time domain from.

        Returns:
            A generator that yields the solution at each time in `time.array`.
        """
        u = u0
        yield u
        for t in time.array[:-1]:
            u = self.update(t, u, rhs, time.spacing)
            yield u

update(t, u, f, delta_t) abstractmethod

Compute the next time step. You probably want solution_generator instead.

Parameters:

Name Type Description Default
t float

The current time.

required
u ndarray[float]

The solution at the current time-step.

required
f ndarray[float]

the right-hand-side at at the current time-step.

required
delta_t float

the temporal step-size.

required

Returns:

Type Description
ndarray[float]

The solution at the next time step.

Source code in src/odeiter/single_step.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@abstractmethod
def update(
    self,
    t: float,
    u: np.ndarray[float],
    f: np.ndarray[float],
    delta_t: float,
) -> np.ndarray[float]:
    """
    Compute the next time step. You probably want `solution_generator` instead.

    Parameters:
        t: The current time.
        u: The solution at the current time-step.
        f: the right-hand-side at at the current time-step.
        delta_t: the temporal step-size.

    Returns:
        The solution at the next time step.
    """
    ...

solution_generator(u0, rhs, time)

Create a generator that yields the solution for each time in time.

Parameters:

Name Type Description Default
u0 ndarray[float]

The initial condition of the system. Must be the same size as the system.

required
rhs Callable[[float, ndarray[float]], ndarray[float]]

The right-hand-side as a function with signature rhs(t, u) -> u'.

required
time TimeDomain

The discretized time domain from.

required

Returns:

Type Description
None

A generator that yields the solution at each time in time.array.

Source code in src/odeiter/single_step.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def solution_generator(
    self,
    u0: np.ndarray[float],
    rhs: Callable[[float, np.ndarray[float]], np.ndarray[float]],
    time: TimeDomain,
) -> Generator[np.ndarray[float], None, None]:
    """Create a generator that yields the solution for each time in `time`.

    Parameters:
        u0: The initial condition of the system.
            Must be the same size as the system.
        rhs: The right-hand-side as a function with signature `rhs(t, u) -> u'`.
        time: The discretized time domain from.

    Returns:
        A generator that yields the solution at each time in `time.array`.
    """
    u = u0
    yield u
    for t in time.array[:-1]:
        u = self.update(t, u, rhs, time.spacing)
        yield u

Euler

Bases: SingleStepMethod

Forward Euler, a first order explicit method.

Source code in src/odeiter/single_step.py
62
63
64
65
66
67
68
69
70
71
72
73
74
class Euler(SingleStepMethod):
    "Forward Euler, a first order explicit method."

    @property
    def order(self):
        return 1

    @property
    def name(self):
        return "Euler"

    def update(self, t, u, f, delta_t):
        return u + delta_t * f(t, u)

EulerDelta

Bases: SingleStepMethod

Forward Euler, but analytically integrates a delta forcing term at a single point in time.

Source code in src/odeiter/single_step.py
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
class EulerDelta(SingleStepMethod):
    """Forward Euler, but analytically integrates a delta
    forcing term at a single point in time.
    """

    def __init__(self, delta_time: float, delta_profile: np.ndarray[float]):
        """
        Parameters:
            delta_time: the time at which the delta function is non-zero.
            delta_profile: the magnitude of the delta for each
                dimesion of the system.
        """
        self.delta_time = delta_time
        self.delta_profile = delta_profile

    @property
    def order(self):
        return 1

    @property
    def name(self):
        return "Euler-Delta"

    def update(self, t, u, f, delta_t):
        u_new = u + delta_t * f(t, u)
        if abs(t - self.delta_time) < delta_t / 2:
            u_new += self.delta_profile
        return u_new

__init__(delta_time, delta_profile)

Parameters:

Name Type Description Default
delta_time float

the time at which the delta function is non-zero.

required
delta_profile ndarray[float]

the magnitude of the delta for each dimesion of the system.

required
Source code in src/odeiter/single_step.py
82
83
84
85
86
87
88
89
90
def __init__(self, delta_time: float, delta_profile: np.ndarray[float]):
    """
    Parameters:
        delta_time: the time at which the delta function is non-zero.
        delta_profile: the magnitude of the delta for each
            dimesion of the system.
    """
    self.delta_time = delta_time
    self.delta_profile = delta_profile

RK4

Bases: SingleStepMethod

Runge-Kutta order 4.

A single-step method that performs 4 function evaluations per time step. Often used as a default because of it's robust domain of stability and it's high order of accuracy.

Source code in src/odeiter/single_step.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
class RK4(SingleStepMethod):
    """
    Runge-Kutta order 4.

    A single-step method that performs 4 function evaluations per time
    step. Often used as a default because of it's robust domain of stability
    and it's high order of accuracy.
    """

    @property
    def name(self):
        return "RK4"

    @property
    def order(self):
        return 4

    def update(self, t, u, f, delta_t):
        k1 = f(t, u)
        k2 = f(t + delta_t / 2, u + delta_t / 2 * k1)
        k3 = f(t + delta_t / 2, u + delta_t / 2 * k2)
        k4 = f(t + delta_t, u + delta_t * k3)
        return u + delta_t / 6 * (k1 + 2 * k2 + 2 * k3 + k4)

ImplicitEuler

Bases: SingleStepMethod

An implicit method of order 1.

Source code in src/odeiter/single_step.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
class ImplicitEuler(SingleStepMethod):
    """An implicit method of order 1."""

    def __init__(self, root_finder: RootFinder = DefaultRootFinder):
        """
        Parameters:
            root_finder: A RootFinder object to solve the system
                at each time step.
        """
        self.root_finder = root_finder.solve

    @property
    def order(self):
        return 1

    @property
    def name(self):
        return "Implicit Euler"

    def update(self, t, u, f, delta_t):
        def func(x):
            return -x + u + delta_t * f(t + delta_t, x)

        return self.root_finder(func, u)

__init__(root_finder=DefaultRootFinder)

Parameters:

Name Type Description Default
root_finder RootFinder

A RootFinder object to solve the system at each time step.

DefaultRootFinder
Source code in src/odeiter/single_step.py
138
139
140
141
142
143
144
def __init__(self, root_finder: RootFinder = DefaultRootFinder):
    """
    Parameters:
        root_finder: A RootFinder object to solve the system
            at each time step.
    """
    self.root_finder = root_finder.solve

Trapezoidal

Bases: SingleStepMethod

An implicit method of order 2.

Source code in src/odeiter/single_step.py
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
class Trapezoidal(SingleStepMethod):
    """An implicit method of order 2."""

    def __init__(self, root_finder: RootFinder = DefaultRootFinder):
        """
        Parameters:
            root_finder: A RootFinder object to solve the system
                at each time step.
        """
        self.root_finder = root_finder.solve

    @property
    def order(self):
        return 2

    @property
    def name(self):
        return "Trapezoidal"

    def update(self, t, u, f, delta_t):
        fn = f(t, u)

        def func(x):
            return -x + u + delta_t / 2 * (fn + f(t + delta_t, x))

        return self.root_finder(func, u)

__init__(root_finder=DefaultRootFinder)

Parameters:

Name Type Description Default
root_finder RootFinder

A RootFinder object to solve the system at each time step.

DefaultRootFinder
Source code in src/odeiter/single_step.py
164
165
166
167
168
169
170
def __init__(self, root_finder: RootFinder = DefaultRootFinder):
    """
    Parameters:
        root_finder: A RootFinder object to solve the system
            at each time step.
    """
    self.root_finder = root_finder.solve