from xarray import Coordinates
from pymor.basic import RectDomain, ConstantFunction, ExpressionFunction, StationaryProblem, \
discretize_stationary_cg, thermal_block_problem, InstationaryProblem, discretize_instationary_cg, \
NumpyMatrixOperator, ExpressionParameterFunctional, Parameters, InstationaryModel, Mupymor.timestepping
Imports
Testing
A reference calculation using ImplicitEulerTimestepper:
import numpy as np
import matplotlib.pyplot as plt
from pymor.basic import (
NumpyMatrixOperator,
LincombOperator,
ExpressionParameterFunctional,
Parameters,
InstationaryModel,
)
from pymor.vectorarrays.numpy import NumpyVectorSpace
from pymor.algorithms.timestepping import ImplicitEulerTimeStepper# 2D state space [x, v]
space = NumpyVectorSpace(2)
A = NumpyMatrixOperator(np.array([
[0.0, -1.0],
[1.0, 0.0],
]))
B = NumpyMatrixOperator(np.array([
[0.0, 0.0],
[0.0, 1.0],
]))
operator = LincombOperator(
[A, B],
[
ExpressionParameterFunctional("w0**2", Parameters({"w0": 1})),
ExpressionParameterFunctional("c", Parameters({"c": 1})),
],
)
rhs = space.from_numpy(np.array([0.1, 0.0])) # length-1 VectorArray
initial_data = space.from_numpy(np.array([1.0, 0.0]))
T = 100.0
model = InstationaryModel(
T=T,
initial_data=initial_data,
operator=operator,
rhs=rhs,
time_stepper=ImplicitEulerTimeStepper(nt=10000), # built-in pyMOR
num_values=200,
)
mu = model.parameters.parse({"c": 0.1, "w0": "sin(t + .001)"})
reference_sol = model.solve(mu)
reference_sol_time = np.linspace(0, T, 200)
plt.plot(reference_sol_time, reference_sol.to_numpy()[0])
plt.plot(reference_sol_time, reference_sol.to_numpy()[1])API
TimeStepper.solve
def solve(
initial_time, # The time at which to begin time-stepping.
end_time, # The time until which to perform time-stepping.
initial_data, # The solution vector at initial_timeinitial_time.
operator, # The |Operator| A.
rhs:NoneType=None, # The right-hand side F (either |VectorArray| of length 1 or |Operator| with
# source.dim == 1source.dim == 1). If NoneNone, zero right-hand side is assumed.
mass:NoneType=None, # The |Operator| M. If NoneNone, the identity operator is assumed.
mu:NoneType=None, # |Parameter values| for which operatoroperator and rhsrhs are evaluated. The current
# time is added to mumu with key tt.
num_values:NoneType=None, # The number of returned vectors of the solution trajectory. If NoneNone, each
# intermediate vector that is calculated is returned.
):Apply time-stepper to the equation.
The equation is of the form ::
M(mu) * d_t u + A(u, mu, t) = F(mu, t),
u(mu, t_0) = u_0(mu).
Parameters
initial_time The time at which to begin time-stepping. end_time The time until which to perform time-stepping. initial_data The solution vector at initial_timeinitial_time. operator The |Operator| A. rhs The right-hand side F (either |VectorArray| of length 1 or |Operator| with source.dim == 1source.dim == 1). If NoneNone, zero right-hand side is assumed. mass The |Operator| M. If NoneNone, the identity operator is assumed. mu |Parameter values| for which operatoroperator and rhsrhs are evaluated. The current time is added to mumu with key tt. num_values The number of returned vectors of the solution trajectory. If NoneNone, each intermediate vector that is calculated is returned.
Returns
|VectorArray| containing the solution trajectory.
AdamsTimeStepper
def AdamsTimeStepper():Interface for time-stepping algorithms.
Algorithms implementing this interface solve time-dependent initial value problems of the form ::
M(mu) * d_t u + A(u, mu, t) = F(mu, t),
u(mu, t_0) = u_0(mu).
Time-steppers used by |InstationaryModel| have to fulfill this interface.
Comparison with the reference calculation:
dof = Coordinates({'dof': ('dof', ['x', 'v'], {'long_name': "Degrees of freedom"})})
space = XarrayVectorSpace(dof)
A = XarrayMatrixOperator(np.array([[0., -1.], [1., 0]]), source=space, range=space)
B = XarrayMatrixOperator(np.array([[0., 0.], [0., 1.]]), source=space, range=space)
operator = LincombOperator(
[A, B],
[
ExpressionParameterFunctional("w0**2", Parameters({'w0': 1})),
ExpressionParameterFunctional("c", Parameters({'c': 1}))
]
)
rhs = space.from_numpy(np.array([0.1, 0.0]))
initial_data = space.from_numpy(np.array([1.0, 0.0]))
T = 100.0
model = InstationaryModel(T, initial_data, operator, rhs, time_stepper=AdamsTimeStepper(), num_values=200)
mu = dict(c=.1, w0="sin(t)")
mu = model.parameters.parse(mu)
adams_sol = model.solve(dict(c=.1, w0="sin(t+.001)"))
adams_sol.visualize()Unable to display output for mime type(s): application/vnd.plotly.v1+json
plt.plot(adams_sol.array.Time, adams_sol.to_numpy()[0])
plt.plot(adams_sol.array.Time, adams_sol.to_numpy()[1])
plt.plot(reference_sol_time, reference_sol.to_numpy()[0])
plt.plot(reference_sol_time, reference_sol.to_numpy()[1])
The thermal block problem:
# domain = RectDomain([[0.,0.], [1.,1.]])
# diffusion = ConstantFunction(1, 2)
# rhs = ExpressionFunction('(sqrt( (x[0]-0.5)**2 + (x[1]-0.5)**2) <= 0.3) * 1.', 2)
# problem = StationaryProblem(
# domain=domain,
# diffusion=diffusion,
# rhs=rhs,
# )
# m, data = discretize_stationary_cg(problem, diameter=1)
# p = thermal_block_problem([2,2])
# m, _ = discretize_stationary_cg(p, diameter=1)
# pp = InstationaryProblem(p, initial_data=ConstantFunction(0., 2), T=1.)
# mm, _ = discretize_instationary_cg(pp, nt=10, diameter=1)
# mm = mm.with_(time_stepper=AdamsTimeStepper(), num_values=200, operator=mm.operator, mass=None, output_functional=None)
# mm.solve({'diffusion': [.5, .6, .7, .8]});
# def numpy_to_xarray(op):
# if isinstance(op, NumpyMatrixOperator): return XarrayMatrixOperator(op.matrix, source='elements', range='elements', name=op.name if op.name != 'NumpyMatrixOperator' else None)
# if isinstance(op, LincombOperator): return LincombOperator([numpy_to_xarray(o) for o in op.operators], op.coefficients, name=op.name)
# if isinstance(op, VectorOperator): return LincombOperator([numpy_to_xarray(o) for o in op.operators], op.coefficients, name=op.name)
# mm.operator
# range_space = numpy_to_xarray(mm.operator).range
# source_space = numpy_to_xarray(mm.operator).source
# mmm = mm.with_(
# T=mm.T,
# initial_data=source_space.from_numpy(mm.initial_data.array.to_numpy()),
# operator=numpy_to_xarray(mm.operator),
# rhs=range_space.from_numpy(mm.rhs.as_range_array().to_numpy()),
# mass=None,
# output_functional=None,
# time_stepper=AdamsTimeStepper()
# )
# mmm.solve({'diffusion': [.5, .6, .7, .8]})BDFTimeStepper
def BDFTimeStepper(
preconditioner:NoneType=None, spilu_drop_tol:float=0.0001, spilu_fill_factor:int=20,
spilu_drop_rule:NoneType=None, spilu_permc_spec:str='COLAMD'
):Interface for time-stepping algorithms.
Algorithms implementing this interface solve time-dependent initial value problems of the form ::
M(mu) * d_t u + A(u, mu, t) = F(mu, t),
u(mu, t_0) = u_0(mu).
Time-steppers used by |InstationaryModel| have to fulfill this interface.
Comparison with the reference calculation:
dof = Coordinates({'dof': ('dof', ['x', 'v'], {'long_name': "Degrees of freedom"})})
space = XarrayVectorSpace(dof)
A = XarrayMatrixOperator(np.array([[0., -1.], [1., 0]]), source=space, range=space)
B = XarrayMatrixOperator(np.array([[0., 0.], [0., 1.]]), source=space, range=space)
operator = LincombOperator(
[A, B],
[
ExpressionParameterFunctional("w0**2", Parameters({'w0': 1})),
ExpressionParameterFunctional("c", Parameters({'c': 1}))
]
)
rhs = space.from_numpy(np.array([[.1], [0.]]))
initial_data = space.from_numpy(np.array([1., 0.]))
T = 100
model = InstationaryModel(T, initial_data, operator, rhs, time_stepper=BDFTimeStepper(), num_values=100)
bdf_sol = model.solve(dict(c=.1, w0="sin(t)"))
bdf_sol.visualize()Unable to display output for mime type(s): application/vnd.plotly.v1+json
plt.plot(bdf_sol.array.Time, bdf_sol.to_numpy()[0])
plt.plot(bdf_sol.array.Time, bdf_sol.to_numpy()[1])
plt.plot(reference_sol_time, reference_sol.to_numpy()[0])
plt.plot(reference_sol_time, reference_sol.to_numpy()[1])
The thermal block problem:
p = thermal_block_problem([2,2])
m, _ = discretize_stationary_cg(p)
pp = InstationaryProblem(p, initial_data=ConstantFunction(0., 2), T=1.)
mm, _ = discretize_instationary_cg(pp, nt=10)solver_options={'inverse': {'type': 'scipy_lgmres_spilu', 'preconditioner_bandwidth': 2}}mm = mm.with_(time_stepper=BDFTimeStepper(), num_values=200, operator=mm.operator)mm.solve({'diffusion': [.5, .6, .7, .8]})NumpyVectorArray(
NumpyVectorSpace(20201),
[[0.00000000e+00 0.00000000e+00 0.00000000e+00 ... 0.00000000e+00
0.00000000e+00 0.00000000e+00]
[0.00000000e+00 0.00000000e+00 0.00000000e+00 ... 0.00000000e+00
0.00000000e+00 0.00000000e+00]
[0.00000000e+00 0.00000000e+00 0.00000000e+00 ... 0.00000000e+00
0.00000000e+00 0.00000000e+00]
...
[0.00000000e+00 2.75757447e-10 2.03167753e-06 ... 1.81201470e-05
2.54533008e-05 3.17670706e-05]
[0.00000000e+00 2.75757447e-10 2.03080205e-06 ... 1.79186198e-05
2.48839961e-05 3.06432543e-05]
[0.00000000e+00 2.75753797e-10 1.87420727e-06 ... 1.35152459e-05
1.73482117e-05 2.00949822e-05]],
_len=8)
StiffSwitchingTimeStepper
def StiffSwitchingTimeStepper():Interface for time-stepping algorithms.
Algorithms implementing this interface solve time-dependent initial value problems of the form ::
M(mu) * d_t u + A(u, mu, t) = F(mu, t),
u(mu, t_0) = u_0(mu).
Time-steppers used by |InstationaryModel| have to fulfill this interface.