Coverage for pySDC/projects/parallelSDC/BaseTransfer_MPI.py: 15%

94 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-04-29 09:02 +0000

1import logging 

2 

3import numpy as np 

4import scipy.sparse as sp 

5 

6from pySDC.core.Errors import UnlockError 

7from pySDC.helpers.pysdc_helper import FrozenClass 

8 

9 

10# short helper class to add params as attributes 

11class _Pars(FrozenClass): 

12 def __init__(self, pars): 

13 self.finter = False 

14 for k, v in pars.items(): 

15 setattr(self, k, v) 

16 

17 self._freeze() 

18 

19 

20class base_transfer_MPI(object): 

21 """ 

22 Standard base_transfer class 

23 

24 Attributes: 

25 logger: custom logger for sweeper-related logging 

26 params(__Pars): parameter object containing the custom parameters passed by the user 

27 fine (pySDC.Level.level): reference to the fine level 

28 coarse (pySDC.Level.level): reference to the coarse level 

29 """ 

30 

31 def __init__(self, fine_level, coarse_level, base_transfer_params, space_transfer_class, space_transfer_params): 

32 """ 

33 Initialization routine 

34 

35 Args: 

36 fine_level (pySDC.Level.level): fine level connected with the base_transfer operations 

37 coarse_level (pySDC.Level.level): coarse level connected with the base_transfer operations 

38 base_transfer_params (dict): parameters for the base_transfer operations 

39 space_transfer_class: class to perform spatial transfer 

40 space_transfer_params (dict): parameters for the space_transfer operations 

41 """ 

42 

43 self.params = _Pars(base_transfer_params) 

44 

45 # set up logger 

46 self.logger = logging.getLogger('transfer') 

47 

48 # just copy by object 

49 self.fine = fine_level 

50 self.coarse = coarse_level 

51 

52 fine_grid = self.fine.sweep.coll.nodes 

53 coarse_grid = self.coarse.sweep.coll.nodes 

54 

55 if len(fine_grid) == len(coarse_grid): 

56 self.Pcoll = sp.eye(len(fine_grid)).toarray() 

57 self.Rcoll = sp.eye(len(fine_grid)).toarray() 

58 else: 

59 raise NotImplementedError('require no reduction of collocation nodes') 

60 

61 # set up spatial transfer 

62 self.space_transfer = space_transfer_class( 

63 fine_prob=self.fine.prob, coarse_prob=self.coarse.prob, params=space_transfer_params 

64 ) 

65 

66 @staticmethod 

67 def get_transfer_matrix_Q(f_nodes, c_nodes): 

68 """ 

69 Helper routine to quickly define transfer matrices between sets of nodes (fully Lagrangian) 

70 Args: 

71 f_nodes: fine nodes 

72 c_nodes: coarse nodes 

73 

74 Returns: 

75 matrix containing the interpolation weights 

76 """ 

77 nnodes_f = len(f_nodes) 

78 nnodes_c = len(c_nodes) 

79 

80 tmat = np.zeros((nnodes_f, nnodes_c)) 

81 

82 for i in range(nnodes_f): 

83 xi = f_nodes[i] 

84 for j in range(nnodes_c): 

85 den = 1.0 

86 num = 1.0 

87 for k in range(nnodes_c): 

88 if k == j: 

89 continue 

90 else: 

91 den *= c_nodes[j] - c_nodes[k] 

92 num *= xi - c_nodes[k] 

93 tmat[i, j] = num / den 

94 

95 return tmat 

96 

97 def restrict(self): 

98 """ 

99 Space-time restriction routine 

100 

101 The routine applies the spatial restriction operator to teh fine values on the fine nodes, then reevaluates f 

102 on the coarse level. This is used for the first part of the FAS correction tau via integration. The second part 

103 is the integral over the fine values, restricted to the coarse level. Finally, possible tau corrections on the 

104 fine level are restricted as well. 

105 """ 

106 

107 # get data for easier access 

108 F = self.fine 

109 G = self.coarse 

110 

111 PG = G.prob 

112 

113 SF = F.sweep 

114 SG = G.sweep 

115 

116 # only if the level is unlocked at least by prediction 

117 if not F.status.unlocked: 

118 raise UnlockError('fine level is still locked, cannot use data from there') 

119 

120 # restrict fine values in space 

121 G.u[0] = self.space_transfer.restrict(F.u[0]) 

122 G.u[SG.rank + 1] = self.space_transfer.restrict(F.u[SF.rank + 1]) 

123 

124 # re-evaluate f on coarse level 

125 G.f[0] = PG.eval_f(G.u[0], G.time) 

126 G.f[SG.rank + 1] = PG.eval_f(G.u[SG.rank + 1], G.time + G.dt * SG.coll.nodes[SG.rank]) 

127 

128 # build coarse level tau correction part 

129 tauG = G.sweep.integrate() 

130 

131 # build fine level tau correction part 

132 tauF = F.sweep.integrate() 

133 

134 # restrict fine level tau correction part in space 

135 tauFG = self.space_transfer.restrict(tauF) 

136 

137 # build tau correction 

138 G.tau[SG.rank] = tauFG - tauG 

139 

140 if F.tau[SF.rank] is not None: 

141 # restrict possible tau correction from fine in space 

142 G.tau[SG.rank] += self.space_transfer.restrict(F.tau[SF.rank]) 

143 else: 

144 pass 

145 

146 # save u and rhs evaluations for interpolation 

147 G.uold[SG.rank + 1] = PG.dtype_u(G.u[SG.rank + 1]) 

148 G.fold[SG.rank + 1] = PG.dtype_f(G.f[SG.rank + 1]) 

149 # G.uold[0] = PG.dtype_u(G.u[0]) 

150 # G.fold[0] = PG.dtype_f(G.f[0]) 

151 

152 # works as a predictor 

153 G.status.unlocked = True 

154 

155 return None 

156 

157 def prolong(self): 

158 """ 

159 Space-time prolongation routine 

160 

161 This routine applies the spatial prolongation routine to the difference between the computed and the restricted 

162 values on the coarse level and then adds this difference to the fine values as coarse correction. 

163 """ 

164 

165 # get data for easier access 

166 F = self.fine 

167 G = self.coarse 

168 

169 PF = F.prob 

170 

171 SF = F.sweep 

172 SG = G.sweep 

173 

174 # only of the level is unlocked at least by prediction or restriction 

175 if not G.status.unlocked: 

176 raise UnlockError('coarse level is still locked, cannot use data from there') 

177 

178 # build coarse correction 

179 

180 # we need to update u0 here for the predictor step, since here the new values for the fine sweep are not 

181 # received from the previous processor but interpolated from the coarse level. 

182 # need to restrict F.u[0] again here, since it might have changed in PFASST 

183 G.uold[0] = self.space_transfer.restrict(F.u[0]) 

184 

185 # interpolate values in space first 

186 F.u[SF.rank + 1] += self.space_transfer.prolong(G.u[SG.rank + 1] - G.uold[SG.rank + 1]) 

187 

188 # re-evaluate f on fine level 

189 F.f[0] = PF.eval_f(F.u[0], F.time) 

190 F.f[SF.rank + 1] = PF.eval_f(F.u[SF.rank + 1], F.time + F.dt * SF.coll.nodes[SF.rank]) 

191 

192 return None 

193 

194 def prolong_f(self): 

195 """ 

196 Space-time prolongation routine w.r.t. the rhs f 

197 

198 This routine applies the spatial prolongation routine to the difference between the computed and the restricted 

199 values on the coarse level and then adds this difference to the fine values as coarse correction. 

200 """ 

201 

202 # get data for easier access 

203 F = self.fine 

204 G = self.coarse 

205 

206 PG = G.prob 

207 

208 SF = F.sweep 

209 SG = G.sweep 

210 

211 # only of the level is unlocked at least by prediction or restriction 

212 if not G.status.unlocked: 

213 raise UnlockError('coarse level is still locked, cannot use data from there') 

214 

215 # build coarse correction 

216 # need to restrict F.u[0] again here, since it might have changed in PFASST 

217 G.uold[0] = self.space_transfer.restrict(F.u[0]) 

218 G.fold[0] = PG.eval_f(G.uold[0], G.time) 

219 

220 # interpolate values in space first 

221 tmp_u = [self.space_transfer.prolong(G.u[0] - G.uold[0])] 

222 tmp_f = [self.space_transfer.prolong(G.f[0] - G.fold[0])] 

223 for m in range(1, SG.coll.num_nodes + 1): 

224 tmp_u.append(self.space_transfer.prolong(G.u[m] - G.uold[m])) 

225 tmp_f.append(self.space_transfer.prolong(G.f[m] - G.fold[m])) 

226 

227 # interpolate values in collocation 

228 F.u[0] += tmp_u[0] 

229 F.f[0] += tmp_f[0] 

230 for n in range(1, SF.coll.num_nodes + 1): 

231 for m in range(1, SG.coll.num_nodes + 1): 

232 F.u[n] += self.Pcoll[n - 1, m - 1] * tmp_u[m] 

233 F.f[n] += self.Pcoll[n - 1, m - 1] * tmp_f[m] 

234 

235 return None