Source code for implementations.sweeper_classes.multi_implicit
from pySDC.core.sweeper import Sweeper
[docs]
class multi_implicit(Sweeper):
"""
Custom sweeper class, implements Sweeper.py
First-order multi-implicit sweeper for two components
Attributes:
Q1: implicit integration matrix for the first component
Q2: implicit integration matrix for the second component
"""
def __init__(self, params):
"""
Initialization routine for the custom sweeper
Args:
params: parameters for the sweeper
"""
# Default choice: implicit Euler
if 'Q1' not in params:
params['Q1'] = 'IE'
if 'Q2' not in params:
params['Q2'] = 'IE'
# call parent's initialization routine
super(multi_implicit, self).__init__(params)
# Integration matrices
self.Q1 = self.get_Qdelta_implicit(qd_type=self.params.Q1)
self.Q2 = self.get_Qdelta_implicit(qd_type=self.params.Q2)
[docs]
def integrate(self):
"""
Integrates the right-hand side (two components)
Returns:
list of dtype_u: containing the integral as values
"""
# get current level and problem description
L = self.level
P = L.prob
me = []
# integrate RHS over all collocation nodes
for m in range(1, self.coll.num_nodes + 1):
# new instance of dtype_u, initialize values with 0
me.append(P.dtype_u(P.init, val=0.0))
for j in range(1, self.coll.num_nodes + 1):
me[-1] += L.dt * self.coll.Qmat[m, j] * (L.f[j].comp1 + L.f[j].comp2)
return me
[docs]
def update_nodes(self):
"""
Update the u- and f-values at the collocation nodes -> corresponds to a single sweep over all nodes
Returns:
None
"""
# get current level and problem description
L = self.level
P = L.prob
# only if the level has been touched before
assert L.status.unlocked
# get number of collocation nodes for easier access
M = self.coll.num_nodes
# gather all terms which are known already (e.g. from the previous iteration)
# get QF(u^k)
integral = self.integrate()
for m in range(M):
# subtract Q1F1(u^k)_m
for j in range(1, M + 1):
integral[m] -= L.dt * self.Q1[m + 1, j] * L.f[j].comp1
# add initial value
integral[m] += L.u[0]
# add tau if associated
if L.tau[m] is not None:
integral[m] += L.tau[m]
# store Q2F2(u^k) for later usage
Q2int = []
for m in range(M):
Q2int.append(P.dtype_u(P.init, val=0.0))
for j in range(1, M + 1):
Q2int[-1] += L.dt * self.Q2[m + 1, j] * L.f[j].comp2
# do the sweep
for m in range(0, M):
# build rhs, consisting of the known values from above and new values from previous nodes (at k+1)
rhs = P.dtype_u(integral[m])
for j in range(1, m + 1):
rhs += L.dt * self.Q1[m + 1, j] * L.f[j].comp1
# implicit solve with prefactor stemming from Q1
L.u[m + 1] = P.solve_system_1(
rhs, L.dt * self.Q1[m + 1, m + 1], L.u[m + 1], L.time + L.dt * self.coll.nodes[m]
)
# substract Q2F2(u^k) and add Q2F(u^k+1)
rhs = L.u[m + 1] - Q2int[m]
for j in range(1, m + 1):
rhs += L.dt * self.Q2[m + 1, j] * L.f[j].comp2
L.u[m + 1] = P.solve_system_2(
rhs,
L.dt * self.Q2[m + 1, m + 1],
L.u[m + 1], # TODO: is this a good guess?
L.time + L.dt * self.coll.nodes[m],
)
# update function values
L.f[m + 1] = P.eval_f(L.u[m + 1], L.time + L.dt * self.coll.nodes[m])
# indicate presence of new values at this level
L.status.updated = True
return None
[docs]
def compute_end_point(self):
"""
Compute u at the right point of the interval
The value uend computed here is a full evaluation of the Picard formulation unless do_full_update==False
Returns:
None
"""
# get current level and problem description
L = self.level
P = L.prob
# check if Mth node is equal to right point and do_coll_update is false, perform a simple copy
if self.coll.right_is_node and not self.params.do_coll_update:
# a copy is sufficient
L.uend = P.dtype_u(L.u[-1])
else:
# start with u0 and add integral over the full interval (using coll.weights)
L.uend = P.dtype_u(L.u[0])
for m in range(self.coll.num_nodes):
L.uend += L.dt * self.coll.weights[m] * (L.f[m + 1].comp1 + L.f[m + 1].comp2)
# add up tau correction of the full interval (last entry)
if L.tau[-1] is not None:
L.uend += L.tau[-1]
return None