Coverage for pySDC/core/level.py: 100%
71 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-17 13:41 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-17 13:41 +0000
1from typing import Any, Dict, List, Optional, Type
2from pySDC.core.sweeper import Sweeper
3from pySDC.core.problem import Problem
5from pySDC.helpers.pysdc_helper import FrozenClass
8# short helper class to add params as attributes
9class _Pars(FrozenClass):
10 def __init__(self, params: Dict[str, Any]) -> None:
11 self.dt: Optional[float] = None
12 self.dt_initial: Optional[float] = None
13 self.restol: float = -1.0
14 self.nsweeps: int = 1
15 self.residual_type: str = 'full_abs'
16 for k, v in params.items():
17 setattr(self, k, v)
18 # freeze class, no further attributes allowed from this point
19 self._freeze()
21 self.dt_initial = self.dt * 1.0 if self.dt is not None else None
24# short helper class to bundle all status variables
25class _Status(FrozenClass):
26 """
27 This class carries the status of the level. All variables that the core SDC / PFASST functionality depend on are
28 initialized here.
29 """
31 def __init__(self) -> None:
32 self.residual: Optional[float] = None
33 self.unlocked: bool = False
34 self.updated: bool = False
35 self.time: Optional[float] = None
36 self.dt_new: Optional[float] = None
37 self.sweep: Optional[int] = None
38 # freeze class, no further attributes allowed from this point
39 self._freeze()
42class Level(FrozenClass):
43 """
44 Level class containing all management functionality for a single level
46 A level contains all data structures, types and objects to perform sweeps on this particular level. It does not
47 know about other levels.
49 Attributes:
50 params (__Pars): parameter object containing the custom parameters passed by the user
51 status (__Status): status object
52 level_index (int): custom string naming this level
53 uend: dof values at the right end point of the interval
54 u (list of dtype_u): dof values at the nodes
55 uold (list of dtype_u): copy of dof values for saving data during restriction)
56 f (list of dtype_f): RHS values at the nodes
57 fold (list of dtype_f): copy of RHS values for saving data during restriction
58 tau (list of dtype_u): FAS correction, allocated via step class if necessary
59 u0_reference: copy of ``u[0]`` taken when a residual was handed down to this level
60 """
62 def __init__(
63 self,
64 problem_class: Type[Problem],
65 problem_params: Dict[str, Any],
66 sweeper_class: Type[Sweeper],
67 sweeper_params: Dict[str, Any],
68 level_params: Dict[str, Any],
69 level_index: int,
70 ) -> None:
71 """
72 Initialization routine
74 Args:
75 problem_class: problem class
76 problem_params (dict): parameters for the problem to be initialized
77 sweeper_class: sweeper class
78 sweeper_params (dict): parameters for the sweeper (contains collocation)
79 level_params (dict): parameters given by the user, will be added as attributes
80 level_index (int): custom name for this level
81 """
83 # set level parameters and status
84 self.params: _Pars = _Pars(level_params)
85 self.status: _Status = _Status()
87 # instantiate sweeper, problem and hooks
88 self.__sweep: Sweeper = sweeper_class(sweeper_params, self)
89 self.__prob: Problem = problem_class(**problem_params)
91 # set name
92 self.level_index: int = level_index
94 # empty data at the nodes, the right end point and tau
95 self.uend: Optional[Any] = None
96 self.u: List[Optional[Any]] = [None] * (self.sweep.coll.num_nodes + 1)
97 self.uold: List[Optional[Any]] = [None] * (self.sweep.coll.num_nodes + 1)
98 self.u_avg: List[Optional[Any]] = [None] * self.sweep.coll.num_nodes
99 self.residual: List[Optional[Any]] = [None] * self.sweep.coll.num_nodes
100 self.increment: List[Optional[Any]] = [None] * self.sweep.coll.num_nodes
101 self.f: List[Optional[Any]] = [None] * (self.sweep.coll.num_nodes + 1)
102 self.fold: List[Optional[Any]] = [None] * (self.sweep.coll.num_nodes + 1)
104 self.tau: List[Optional[Any]] = [None] * self.sweep.coll.num_nodes
106 # Set by a transfer that hands this level a residual instead of letting it rebuild one, and
107 # read by the sweeper that carries that residual. PFASST overwrites `u[0]` with the
108 # predecessor's end value *after* the restriction, and the residual depends on `u[0]`
109 # additively, so a level carrying one has to be told what `u[0]` was when it arrived. It
110 # lives here, next to `tau`, because it is a copy of this level's own data and has to be
111 # dropped when the level is reset -- a stale one belongs to the previous step.
112 #
113 # Temporary. Putting the step-to-step exchange itself in delta form, so the predecessor
114 # sends an increment rather than a state, removes the need for it entirely.
115 self.u0_reference: Optional[Any] = None
117 self.__tag: Optional[Any] = None
119 # freeze class, no further attributes allowed from this point
120 self._freeze()
122 def reset_level(self, reset_status: bool = True) -> None:
123 """
124 Routine to clean-up the level for the next time step
126 Args:
127 reset_status (bool): Reset the status or only the solution
129 Returns:
130 None
131 """
133 # reset status
134 if reset_status:
135 self.status = _Status()
137 # all data back to None
138 self.uend = None
139 self.u = [None] * (self.sweep.coll.num_nodes + 1)
140 self.uold = [None] * (self.sweep.coll.num_nodes + 1)
141 self.f = [None] * (self.sweep.coll.num_nodes + 1)
142 self.fold = [None] * (self.sweep.coll.num_nodes + 1)
143 self.tau = [None] * self.sweep.coll.num_nodes
144 self.u0_reference = None
146 @property
147 def sweep(self) -> Sweeper:
148 """
149 Getter for the sweeper
151 Returns:
152 pySDC.Sweeper.sweeper: the sweeper associated to this level
153 """
154 return self.__sweep
156 @property
157 def prob(self) -> Problem:
158 """
159 Getter for the problem
161 Returns:
162 pySDC.Problem.ptype: the problem associated to this level
163 """
164 return self.__prob
166 @property
167 def time(self) -> Optional[float]:
168 """
169 Meta-getter for the current time
171 Returns:
172 float: referencing status time for convenience
173 """
174 return self.status.time
176 @property
177 def dt(self) -> Optional[float]:
178 """
179 Meta-getter for the time-step size
181 Returns:
182 float: referencing dt from parameters for convenience
183 """
184 return self.params.dt
186 @property
187 def tag(self) -> Optional[Any]:
188 """
189 Getter for tag
191 Returns:
192 tag for sending/receiving
193 """
194 return self.__tag
196 @tag.setter
197 def tag(self, t: Any) -> None:
198 """
199 Setter for tag
201 Args:
202 t: new tag for sending/receiving
203 """
204 self.__tag = t