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

1from pySDC.core.sweeper import Sweeper 

2 

3 

4class explicit(Sweeper): 

5 """ 

6 Fully explicit SDC sweeper, with explicit Euler as the default base integrator. 

7 

8 Attributes: 

9 QE: explicit Euler integration matrix 

10 """ 

11 

12 def __init__(self, params, level): 

13 """ 

14 Initialization routine for the custom sweeper 

15 

16 Args: 

17 params: parameters for the sweeper 

18 level (pySDC.Level.level): the level that uses this sweeper 

19 """ 

20 

21 if 'QE' not in params: 

22 params['QE'] = 'EE' 

23 

24 super(explicit, self).__init__(params, level) 

25 

26 # integration matrix 

27 self.QE = self.get_Qdelta_explicit(qd_type=self.params.QE) 

28 

29 def integrate(self): 

30 """ 

31 Integrates the right-hand side 

32 

33 Returns: 

34 list of dtype_u: containing the integral as values 

35 """ 

36 

37 # get current level and problem description 

38 L = self.level 

39 P = L.prob 

40 

41 me = [] 

42 

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] 

49 

50 return me 

51 

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 

55 

56 Returns: 

57 None 

58 """ 

59 

60 # get current level and problem description 

61 L = self.level 

62 P = L.prob 

63 

64 # only if the level has been touched before 

65 assert L.status.unlocked 

66 

67 # get number of collocation nodes for easier access 

68 M = self.coll.num_nodes 

69 

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 

74 

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] 

85 

86 # update function values 

87 L.f[m + 1] = P.eval_f(L.u[m + 1], L.time + L.dt * self.coll.nodes[m]) 

88 

89 # indicate presence of new values at this level 

90 L.status.updated = True 

91 

92 return None 

93 

94 def compute_end_point(self): 

95 """ 

96 Compute u at the right point of the interval 

97 

98 The value uend computed here is a full evaluation of the Picard formulation unless do_full_update==False 

99 

100 Returns: 

101 None 

102 """ 

103 

104 # get current level and problem description 

105 L = self.level 

106 P = L.prob 

107 

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] 

120 

121 return None