from __future__ import annotations
from typing import Callable, Optional, Tuple
import numpy as np
from pararealml.constrained_problem import ConstrainedProblem
from pararealml.initial_condition import InitialCondition
TemporalDomainInterval = Tuple[float, float]
[docs]class InitialValueProblem:
"""
A representation of an initial value problem around a boundary value
problem.
"""
def __init__(
self,
cp: ConstrainedProblem,
t_interval: TemporalDomainInterval,
initial_condition: InitialCondition,
exact_y: Optional[
Callable[
[InitialValueProblem, float, Optional[np.ndarray]],
np.ndarray
]
] = None):
"""
:param cp: the constrained problem to base the initial value problem on
:param t_interval: the bounds of the time domain of the initial value
problem
:param initial_condition: the initial condition of the problem
:param exact_y: the function returning the exact solution to the
initial value problem at time step t and points x. If it is None,
the problem is assumed to have no analytical solution.
"""
if t_interval[0] > t_interval[1]:
raise ValueError(
f'lower bound of time interval ({t_interval[0]}) cannot be '
f'greater than its upper bound ({t_interval[1]})')
self._cp = cp
self._t_interval = t_interval
self._initial_condition = initial_condition
self._exact_y = exact_y
@property
def constrained_problem(self) -> ConstrainedProblem:
"""
The constrained problem the IVP is based on.
"""
return self._cp
@property
def t_interval(self) -> TemporalDomainInterval:
"""
The bounds of the temporal domain of the differential equation.
"""
return self._t_interval
@property
def initial_condition(self) -> InitialCondition:
"""
The initial condition of the IVP.
"""
return self._initial_condition
@property
def has_exact_solution(self) -> bool:
"""
Whether the differential equation has an analytic solution
"""
return self._exact_y is not None
[docs] def exact_y(
self,
t: float,
x: Optional[np.ndarray] = None) -> np.ndarray:
"""
Returns the exact value of y(t, x).
:param t: the point in the temporal domain
:param x: the points in the non-temporal domain. If the differential
equation is an ODE, it is None.
:return: the value of y(t, x) or y(t) if it is an ODE.
"""
if not self.has_exact_solution:
raise RuntimeError(
'exact solution of initial value problem undefined')
return self._exact_y(self, t, x)