Coverage for pySDC/core/BaseTransfer.py: 100%
103 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 scipy.sparse as sp
5from pySDC.core.Errors import UnlockError
6from pySDC.helpers.pysdc_helper import FrozenClass
7from pySDC.core.Lagrange import LagrangeApproximation
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(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 self.Pcoll = self.get_transfer_matrix_Q(fine_grid, coarse_grid)
60 self.Rcoll = self.get_transfer_matrix_Q(coarse_grid, fine_grid)
62 # set up spatial transfer
63 self.space_transfer = space_transfer_class(
64 fine_prob=self.fine.prob, coarse_prob=self.coarse.prob, params=space_transfer_params
65 )
67 @staticmethod
68 def get_transfer_matrix_Q(f_nodes, c_nodes):
69 """
70 Helper routine to quickly define transfer matrices from a coarse set
71 to a fine set of nodes (fully Lagrangian)
72 Args:
73 f_nodes: fine nodes (size nF)
74 c_nodes: coarse nodes (size nC)
76 Returns:
77 matrix containing the interpolation weights (shape (nF, nC))
78 """
79 approx = LagrangeApproximation(c_nodes)
80 return approx.getInterpolationMatrix(f_nodes)
82 def restrict(self):
83 """
84 Space-time restriction routine
86 The routine applies the spatial restriction operator to the fine values on the fine nodes, then reevaluates f
87 on the coarse level. This is used for the first part of the FAS correction tau via integration. The second part
88 is the integral over the fine values, restricted to the coarse level. Finally, possible tau corrections on the
89 fine level are restricted as well.
90 """
92 # get data for easier access
93 F = self.fine
94 G = self.coarse
96 PG = G.prob
98 SF = F.sweep
99 SG = G.sweep
101 # only if the level is unlocked at least by prediction
102 if not F.status.unlocked:
103 raise UnlockError('fine level is still locked, cannot use data from there')
105 # restrict fine values in space
106 tmp_u = []
107 for m in range(1, SF.coll.num_nodes + 1):
108 tmp_u.append(self.space_transfer.restrict(F.u[m]))
110 # restrict collocation values
111 G.u[0] = self.space_transfer.restrict(F.u[0])
112 for n in range(1, SG.coll.num_nodes + 1):
113 G.u[n] = self.Rcoll[n - 1, 0] * tmp_u[0]
114 for m in range(1, SF.coll.num_nodes):
115 G.u[n] += self.Rcoll[n - 1, m] * tmp_u[m]
117 # re-evaluate f on coarse level
118 G.f[0] = PG.eval_f(G.u[0], G.time)
119 for m in range(1, SG.coll.num_nodes + 1):
120 G.f[m] = PG.eval_f(G.u[m], G.time + G.dt * SG.coll.nodes[m - 1])
122 # build coarse level tau correction part
123 tauG = G.sweep.integrate()
125 # build fine level tau correction part
126 tauF = F.sweep.integrate()
128 # restrict fine level tau correction part in space
129 tmp_tau = []
130 for m in range(SF.coll.num_nodes):
131 tmp_tau.append(self.space_transfer.restrict(tauF[m]))
133 # restrict fine level tau correction part in collocation
134 tauFG = []
135 for n in range(1, SG.coll.num_nodes + 1):
136 tauFG.append(self.Rcoll[n - 1, 0] * tmp_tau[0])
137 for m in range(1, SF.coll.num_nodes):
138 tauFG[-1] += self.Rcoll[n - 1, m] * tmp_tau[m]
140 # build tau correction
141 for m in range(SG.coll.num_nodes):
142 G.tau[m] = tauFG[m] - tauG[m]
144 if F.tau[0] is not None:
145 # restrict possible tau correction from fine in space
146 tmp_tau = []
147 for m in range(SF.coll.num_nodes):
148 tmp_tau.append(self.space_transfer.restrict(F.tau[m]))
150 # restrict possible tau correction from fine in collocation
151 for n in range(SG.coll.num_nodes):
152 for m in range(SF.coll.num_nodes):
153 G.tau[n] += self.Rcoll[n, m] * tmp_tau[m]
154 else:
155 pass
157 # save u and rhs evaluations for interpolation
158 for m in range(1, SG.coll.num_nodes + 1):
159 G.uold[m] = PG.dtype_u(G.u[m])
160 G.fold[m] = PG.dtype_f(G.f[m])
162 # works as a predictor
163 G.status.unlocked = True
165 return None
167 def prolong(self):
168 """
169 Space-time prolongation routine
171 This routine applies the spatial prolongation routine to the difference between the computed and the restricted
172 values on the coarse level and then adds this difference to the fine values as coarse correction.
173 """
175 # get data for easier access
176 F = self.fine
177 G = self.coarse
179 PF = F.prob
181 SF = F.sweep
182 SG = G.sweep
184 # only of the level is unlocked at least by prediction or restriction
185 if not G.status.unlocked:
186 raise UnlockError('coarse level is still locked, cannot use data from there')
188 # build coarse correction
190 # interpolate values in space first
191 tmp_u = []
192 for m in range(1, SG.coll.num_nodes + 1):
193 tmp_u.append(self.space_transfer.prolong(G.u[m] - G.uold[m]))
195 # interpolate values in collocation
196 for n in range(1, SF.coll.num_nodes + 1):
197 for m in range(SG.coll.num_nodes):
198 F.u[n] += self.Pcoll[n - 1, m] * tmp_u[m]
200 # re-evaluate f on fine level
201 for m in range(1, SF.coll.num_nodes + 1):
202 F.f[m] = PF.eval_f(F.u[m], F.time + F.dt * SF.coll.nodes[m - 1])
204 return None
206 def prolong_f(self):
207 """
208 Space-time prolongation routine w.r.t. the rhs f
210 This routine applies the spatial prolongation routine to the difference between the computed and the restricted
211 values on the coarse level and then adds this difference to the fine values as coarse correction.
212 """
214 # get data for easier access
215 F = self.fine
216 G = self.coarse
218 SF = F.sweep
219 SG = G.sweep
221 # only of the level is unlocked at least by prediction or restriction
222 if not G.status.unlocked:
223 raise UnlockError('coarse level is still locked, cannot use data from there')
225 # build coarse correction
227 # interpolate values in space first
228 tmp_u = []
229 tmp_f = []
230 for m in range(1, SG.coll.num_nodes + 1):
231 tmp_u.append(self.space_transfer.prolong(G.u[m] - G.uold[m]))
232 tmp_f.append(self.space_transfer.prolong(G.f[m] - G.fold[m]))
234 # interpolate values in collocation
235 for n in range(1, SF.coll.num_nodes + 1):
236 for m in range(SG.coll.num_nodes):
237 F.u[n] += self.Pcoll[n - 1, m] * tmp_u[m]
238 F.f[n] += self.Pcoll[n - 1, m] * tmp_f[m]
240 return None