Coverage for pySDC/implementations/sweeper_classes/explicit.py: 96%
45 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 12:30 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 12:30 +0000
1from pySDC.core.sweeper import Sweeper
4class explicit(Sweeper):
5 """
6 Fully explicit SDC sweeper, with explicit Euler as the default base integrator.
8 Attributes:
9 QE: explicit Euler integration matrix
10 """
12 def __init__(self, params, level):
13 """
14 Initialization routine for the custom sweeper
16 Args:
17 params: parameters for the sweeper
18 level (pySDC.Level.level): the level that uses this sweeper
19 """
21 if 'QE' not in params:
22 params['QE'] = 'EE'
24 super(explicit, self).__init__(params, level)
26 # integration matrix
27 self.QE = self.get_Qdelta_explicit(qd_type=self.params.QE)
29 def integrate(self):
30 """
31 Integrates the right-hand side
33 Returns:
34 list of dtype_u: containing the integral as values
35 """
37 # get current level and problem description
38 L = self.level
39 P = L.prob
41 me = []
43 # integrate RHS over all collocation nodes
44 for m in range(1, self.coll.num_nodes + 1):
45 # new instance of dtype_u, initialize values with 0
46 me.append(P.dtype_u(P.init, val=0.0))
47 for j in range(1, self.coll.num_nodes + 1):
48 me[-1] += L.dt * self.coll.Qmat[m, j] * L.f[j]
50 return me
52 def update_nodes(self):
53 """
54 Update the u- and f-values at the collocation nodes -> corresponds to a single sweep over all nodes
56 Returns:
57 None
58 """
60 # get current level and problem description
61 L = self.level
62 P = L.prob
64 # only if the level has been touched before
65 assert L.status.unlocked
67 # get number of collocation nodes for easier access
68 M = self.coll.num_nodes
70 # the terms which are known already (from the previous iteration) are u0 + QF(u^k) - QEFE(u^k) + tau, built with
71 # Q - QE in one pass over F(u^k), kept here because the sweep replaces L.f[m]
72 F = L.f[1:]
73 diff_weights = self.coll.Qmat - self.QE
75 # do the sweep
76 for m in range(0, M):
77 # build new u, consisting of the known values and new values from previous nodes (at k+1)
78 L.u[m + 1] = P.dtype_u(L.u[0])
79 for j in range(1, M + 1):
80 L.u[m + 1] += L.dt * diff_weights[m + 1, j] * F[j - 1]
81 if L.tau[m] is not None:
82 L.u[m + 1] += L.tau[m]
83 for j in range(1, m + 1):
84 L.u[m + 1] += L.dt * self.QE[m + 1, j] * L.f[j]
86 # update function values
87 L.f[m + 1] = P.eval_f(L.u[m + 1], L.time + L.dt * self.coll.nodes[m])
89 # indicate presence of new values at this level
90 L.status.updated = True
92 return None
94 def compute_end_point(self):
95 """
96 Compute u at the right point of the interval
98 The value uend computed here is a full evaluation of the Picard formulation unless do_full_update==False
100 Returns:
101 None
102 """
104 # get current level and problem description
105 L = self.level
106 P = L.prob
108 # check if Mth node is equal to right point and do_coll_update is false, perform a simple copy
109 if self.coll.right_is_node and not self.params.do_coll_update:
110 # a copy is sufficient
111 L.uend = P.dtype_u(L.u[-1])
112 else:
113 # start with u0 and add integral over the full interval (using coll.weights)
114 L.uend = P.dtype_u(L.u[0])
115 for m in range(self.coll.num_nodes):
116 L.uend += L.dt * self.coll.weights[m] * L.f[m + 1]
117 # add up tau correction of the full interval (last entry)
118 if L.tau[-1] is not None:
119 L.uend += L.tau[-1]
121 return None