Coverage for pySDC/implementations/sweeper_classes/generic_implicit.py: 98%

51 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-20 14:51 +0000

1from pySDC.core.sweeper import Sweeper 

2 

3 

4class generic_implicit(Sweeper): 

5 """ 

6 Generic implicit sweeper, expecting lower triangular matrix type as input 

7 

8 Attributes: 

9 QI: lower triangular matrix 

10 """ 

11 

12 def __init__(self, params): 

13 """ 

14 Initialization routine for the custom sweeper 

15 

16 Args: 

17 params: parameters for the sweeper 

18 """ 

19 

20 if 'QI' not in params: 

21 params['QI'] = 'IE' 

22 

23 # call parent's initialization routine 

24 super().__init__(params) 

25 

26 # get QI matrix 

27 self.QI = self.get_Qdelta_implicit(qd_type=self.params.QI) 

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 L = self.level 

38 P = L.prob 

39 

40 me = [] 

41 

42 # integrate RHS over all collocation nodes 

43 for m in range(1, self.coll.num_nodes + 1): 

44 # new instance of dtype_u, initialize values with 0 

45 me.append(P.dtype_u(P.init, val=0.0)) 

46 for j in range(1, self.coll.num_nodes + 1): 

47 me[-1] += L.dt * self.coll.Qmat[m, j] * L.f[j] 

48 

49 return me 

50 

51 def update_nodes(self): 

52 """ 

53 Update the u- and f-values at the collocation nodes -> corresponds to a single sweep over all nodes 

54 

55 Returns: 

56 None 

57 """ 

58 

59 L = self.level 

60 P = L.prob 

61 

62 # only if the level has been touched before 

63 assert L.status.unlocked 

64 

65 # get number of collocation nodes for easier access 

66 M = self.coll.num_nodes 

67 

68 # update the MIN-SR-FLEX preconditioner 

69 self.updateVariableCoeffs(L.status.sweep) 

70 

71 # gather all terms which are known already (e.g. from the previous iteration) 

72 # this corresponds to u0 + QF(u^k) - QdF(u^k) + tau 

73 

74 # get QF(u^k) 

75 integral = self.integrate() 

76 for m in range(M): 

77 # get -QdF(u^k)_m 

78 for j in range(1, M + 1): 

79 integral[m] -= L.dt * self.QI[m + 1, j] * L.f[j] 

80 

81 # add initial value 

82 integral[m] += L.u[0] 

83 # add tau if associated 

84 if L.tau[m] is not None: 

85 integral[m] += L.tau[m] 

86 

87 # do the sweep 

88 for m in range(0, M): 

89 # build rhs, consisting of the known values from above and new values from previous nodes (at k+1) 

90 rhs = P.dtype_u(integral[m]) 

91 for j in range(1, m + 1): 

92 rhs += L.dt * self.QI[m + 1, j] * L.f[j] 

93 

94 # implicit solve with prefactor stemming from the diagonal of Qd 

95 alpha = L.dt * self.QI[m + 1, m + 1] 

96 if alpha == 0: 

97 L.u[m + 1] = rhs 

98 else: 

99 L.u[m + 1] = P.solve_system(rhs, alpha, L.u[m + 1], L.time + L.dt * self.coll.nodes[m]) 

100 # update function values 

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

102 

103 # indicate presence of new values at this level 

104 L.status.updated = True 

105 

106 return None 

107 

108 def compute_end_point(self): 

109 """ 

110 Compute u at the right point of the interval 

111 

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

113 

114 Returns: 

115 None 

116 """ 

117 

118 L = self.level 

119 P = L.prob 

120 

121 # check if Mth node is equal to right point and do_coll_update is false, perform a simple copy 

122 if self.coll.right_is_node and not self.params.do_coll_update: 

123 # a copy is sufficient 

124 L.uend = P.dtype_u(L.u[-1]) 

125 else: 

126 # start with u0 and add integral over the full interval (using coll.weights) 

127 L.uend = P.dtype_u(L.u[0]) 

128 for m in range(self.coll.num_nodes): 

129 L.uend += L.dt * self.coll.weights[m] * L.f[m + 1] 

130 # add up tau correction of the full interval (last entry) 

131 if L.tau[-1] is not None: 

132 L.uend += L.tau[-1] 

133 

134 return None