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
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-29 09:02 +0000
1import logging
3import numpy as np
4import scipy.sparse as sp
6from pySDC.core.Errors import UnlockError
7from pySDC.helpers.pysdc_helper import FrozenClass
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)
17 self._freeze()
20class base_transfer_MPI(object):
21 """
22 Standard base_transfer class
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 """
31 def __init__(self, fine_level, coarse_level, base_transfer_params, space_transfer_class, space_transfer_params):
32 """
33 Initialization routine
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 """
43 self.params = _Pars(base_transfer_params)
45 # set up logger
46 self.logger = logging.getLogger('transfer')
48 # just copy by object
49 self.fine = fine_level
50 self.coarse = coarse_level
52 fine_grid = self.fine.sweep.coll.nodes
53 coarse_grid = self.coarse.sweep.coll.nodes
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')
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 )
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
74 Returns:
75 matrix containing the interpolation weights
76 """
77 nnodes_f = len(f_nodes)
78 nnodes_c = len(c_nodes)
80 tmat = np.zeros((nnodes_f, nnodes_c))
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
95 return tmat
97 def restrict(self):
98 """
99 Space-time restriction routine
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 """
107 # get data for easier access
108 F = self.fine
109 G = self.coarse
111 PG = G.prob
113 SF = F.sweep
114 SG = G.sweep
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')
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])
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])
128 # build coarse level tau correction part
129 tauG = G.sweep.integrate()
131 # build fine level tau correction part
132 tauF = F.sweep.integrate()
134 # restrict fine level tau correction part in space
135 tauFG = self.space_transfer.restrict(tauF)
137 # build tau correction
138 G.tau[SG.rank] = tauFG - tauG
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
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])
152 # works as a predictor
153 G.status.unlocked = True
155 return None
157 def prolong(self):
158 """
159 Space-time prolongation routine
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 """
165 # get data for easier access
166 F = self.fine
167 G = self.coarse
169 PF = F.prob
171 SF = F.sweep
172 SG = G.sweep
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')
178 # build coarse correction
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])
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])
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])
192 return None
194 def prolong_f(self):
195 """
196 Space-time prolongation routine w.r.t. the rhs f
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 """
202 # get data for easier access
203 F = self.fine
204 G = self.coarse
206 PG = G.prob
208 SF = F.sweep
209 SG = G.sweep
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')
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)
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]))
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]
235 return None