Peano
Loading...
Searching...
No Matches
CCZ4Solver.py
Go to the documentation of this file.
1""" This file is part of the ExaGRyPE project. For conditions of distribution and
2 use, please see the copyright notice at www.peano-framework.org
3"""
4
5import enum
6from typing import Dict, List, Union
7
8import exahype2.api
9import peano4.api
12
13from . import initialvalues
14
15
17 """!
18
19 Solver for the Einstein equations in the first-order CCZ4 formulation
20
21 The solver is a plain exahype2.api.Solver with the 59 CCZ4 unknowns and
22 the PDE terms of CCZ4 pre-registered. All terms delegate to the
23 ExaGRyPE library (configure Peano with --enable-exagrype):
24
25 - the non-conservative product to exagrype::ccz4::ncp(),
26 - the volumetric source to exagrype::ccz4::source(),
27 - the maximum eigenvalue to exagrype::ccz4::maxEigenvalue(),
28 - the initial condition to one of the routines from
29 exagrype::initialvalues, @see exagrype.api.initialvalues.
30
31 CCZ4 has no conservative flux. You still have to assign a numerical
32 scheme, a grid construction and (optionally) a plotter, exactly as for
33 any other exahype2.api.Solver, and to add the solver to an
34 exahype2.api.Project. The project has to be three-dimensional: the
35 generated solver class refuses to compile otherwise.
36
37 ## Shortcomings
38
39 - The fourth-order Finite Differences of the production runs are
40 available through exahype2.api.rkfd4, i.e. pass a
41 CentralDifferencesWithRungeKutta as numerical scheme. They have no
42 artificial dissipation. exagrype.api.FD4WithKODissipation is the same
43 scheme with a Kreiss-Oliger dissipation on top. Unlike the legacy
44 ExaGRyPE, neither supports adaptive mesh refinement yet.
45 - The solution is pushed back onto the CCZ4 constraints
46 (exagrype::ccz4::enforceCCZ4constraints()) only if you switch it on
47 via enforce_ccz4_constraint(). Without it, long runs drift away from
48 the constraint surface.
49 - exagrype::ccz4::ncp() omits the advection terms
50 @f$ \\beta^k \\partial _k Q @f$ of all unknowns besides the metric,
51 the lapse, the shift and phi (whose advection is part of the
52 algebraic source) and K0. It expects the numerical scheme to add them
53 with lopsided stencils. The fourth-order Finite Differences do so if
54 you select the kernel CentralDifferencesWithLopsidedAdvection with
55 ADVECTION_VELOCITY and lopsided_advection_quantities(). The Finite
56 Volumes of exahype2.api.fv do not add them, i.e. they evolve CCZ4
57 without these advection terms.
58 - The second-order formulation, black holes (TwoPunctures), tracers and
59 the ADM/Psi4 postprocessing are not available yet.
60
61 """
62
63 class EnforceCCZ4Constraint(enum.Enum):
64 """!
65
66 When to push the solution back onto the algebraic CCZ4 constraints
67
68 - No: never. This is the default.
69 - OnlyFinalSolution: after each task which writes a new solution
70 (exahype2.api.Solver.SolutionUpdateTaskTag), i.e. once per time
71 step.
72 - IntermediateSteps: in addition after each task which writes an
73 intermediate solution (exahype2.api.Solver.IntermediateSolutionUpdateTag),
74 i.e. after each Runge-Kutta stage. For single-step schemes such as
75 the Finite Volumes, this is the same as OnlyFinalSolution.
76
77 @ref exagrype.api.CCZ4Solver.CCZ4Solver.enforce_ccz4_constraint()
78
79 """
80
81 No = 0
82 OnlyFinalSolution = 1
83 IntermediateSteps = 2
84
85 Unknowns = {
86 "G": 6,
87 "K": 6,
88 "theta": 1,
89 "Z": 3,
90 "lapse": 1,
91 "shift": 3,
92 "b": 3,
93 "dLapse": 3,
94 "dxShift": 3,
95 "dyShift": 3,
96 "dzShift": 3,
97 "dxG": 6,
98 "dyG": 6,
99 "dzG": 6,
100 "traceK": 1,
101 "phi": 1,
102 "P": 3,
103 "K0": 1,
104 }
105 """!
106
107 The unknowns of the first-order CCZ4 formulation in the order the
108 kernels in exagrype::ccz4 expect them. The names match the legacy
109 ExaGRyPE solvers.
110
111 """
112
113 DefaultParameters = {
114 "CCZ4LapseType": 0,
115 "CCZ4SO": 0,
116 "CCZ4ds": 1.0,
117 "CCZ4c": 1.0,
118 "CCZ4e": 1.0,
119 "CCZ4f": 0.75,
120 "CCZ4bs": 0.0,
121 "CCZ4sk": 0.0,
122 "CCZ4xi": 1.0,
123 "CCZ4itau": 1.0,
124 "CCZ4eta": 1.0,
125 "CCZ4k1": 0.1,
126 "CCZ4k2": 0.0,
127 "CCZ4k3": 0.5,
128 "CCZ4GLMc": 1.2,
129 "CCZ4GLMd": 2.0,
130 "CCZ4mu": 0.2,
131 }
132 """!
133
134 Default values of the CCZ4 parameters, taken from the legacy ExaGRyPE
135 solvers. Integer values become int constants of the generated solver
136 class, all others double constants.
137
138 """
139
140 ADVECTION_VELOCITY = "shift"
141 """!
142
143 The variable which advects the unknowns, i.e. the velocity to pass to
144 lopsided advection kernels (@see lopsided_advection_quantities()).
145
146 """
147
149 self,
150 name: str,
151 parameters: Dict[str, Union[int, float]] = None,
152 initial_condition: exahype2.api.InitPointWisely = None,
153 ):
154 """!
155
156 Create a CCZ4 solver
157
158 @param name: str
159 Name of the solver, @see exahype2.api.Solver.
160
161 @param parameters: Dict[str, Union[int, float]]
162 CCZ4 parameters that differ from DefaultParameters, e.g.
163 {"CCZ4k1": 0.0}. Keys which are not in DefaultParameters are
164 rejected, as they most likely are typos.
165
166 @param initial_condition: exahype2.api.InitPointWisely
167 Initial condition. Defaults to exagrype.api.initialvalues.gauge_wave().
168 Pass any other routine from exagrype.api.initialvalues, or your
169 own exahype2.api.InitPointWisely.
170
171 """
172 super().__init__(name, unknowns=self.Unknowns, auxiliary_variables=0)
173
174 self._enforce_ccz4_constraint = CCZ4Solver.EnforceCCZ4Constraint.No
175
177 for key, value in (parameters or {}).items():
178 if key not in self.DefaultParameters:
179 raise KeyError(
180 f"Solver {name}: unknown CCZ4 parameter {key!r}. "
181 f"Available parameters are {list(self.DefaultParameters)}."
182 )
183 self._parameters[key] = value
184
186 'static_assert(Dimensions == 3, "CCZ4 (ExaGRyPE) is three-dimensional only");'
187 )
188 for key, value in self._parameters.items():
189 cpp_type = (
190 "int" if isinstance(self.DefaultParameters[key], int) else "double"
191 )
193 f"static constexpr {cpp_type} {key} = {value};"
194 )
195
197 """
198#include "exagrype/ccz4/Kernels.h"
199#include "exagrype/initialvalues/InitialValues.h"
200#include "exagrype/initialvalues/ApplyTwoPunctures.h"
201"""
202 )
203
204 self.add_pde_term(
206 implementation=self._ncp_implementation()
207 )
208 )
209 self.add_pde_term(
211 )
212 self.add_pde_term(
214 implementation=self._max_eigenvalue_implementation()
215 )
216 )
217 self.add_pde_term(
218 initial_condition
219 if initial_condition is not None
220 else initialvalues.gauge_wave()
221 )
222
223 def lopsided_advection_quantities(self) -> List[str]:
224 """!
225
226 The unknowns whose advection terms exagrype::ccz4::ncp() omits
227
228 exagrype::ccz4::ncp() is invoked with its advection switched off.
229 The unknowns returned are those whose advection terms
230 @f$ \\beta^k \\partial _k Q @f$ hence have to be added by the
231 numerical scheme, e.g. via
232
233 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
234 exahype2.api.rkfd4.CentralDifferencesWithRungeKutta(
235 solver, patch_size, rk_order=4,
236 right_hand_side_kernel = exahype2.api.rkfd4.CentralDifferencesWithRungeKutta.RightHandSideKernel.CentralDifferencesWithLopsidedAdvection,
237 advection_velocity = exagrype.api.CCZ4Solver.ADVECTION_VELOCITY,
238 advected_quantities = solver.lopsided_advection_quantities(),
239 )
240 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
241
242 These are all unknowns besides the conformal metric G, the lapse,
243 the shift, phi and K0: The advection terms of the metric, the lapse,
244 the shift and phi are part of the algebraic source
245 (exagrype::ccz4::source()), which expresses them through the
246 auxiliary unknowns, i.e. the first derivatives. K0 does not evolve.
247
248 The advection of b and of the derivatives of the shift (dxShift,
249 dyShift, dzShift) is scaled with CCZ4bs and CCZ4sk. If either of
250 them is zero, the routine leaves these unknowns out.
251
252 The equation of b couples the advection of b and of Ghat (the
253 unknown Z): Its
254 advection terms are CCZ4sk * (CCZ4xi * beta^k d_k Ghat + CCZ4bs *
255 (beta^k d_k b - beta^k d_k Ghat)). A kernel which advects each
256 quantity on its own, i.e. adds beta^k d_k b to b, reproduces this
257 only for CCZ4xi=1, CCZ4bs=1 and CCZ4sk=1. This is the case for the
258 default CCZ4xi and the moving puncture setups with CCZ4bs=CCZ4sk=1.
259
260 @return List[str]
261 Names of unknowns, @see Unknowns.
262
263 """
264 excluded = {"G", "lapse", "shift", "phi", "K0"}
265 if self._parameters["CCZ4bs"] == 0 or self._parameters["CCZ4sk"] == 0:
266 excluded |= {"b", "dxShift", "dyShift", "dzShift"}
267 return [name for name in self.Unknowns if name not in excluded]
268
269 def enforce_ccz4_constraint(self, value: "CCZ4Solver.EnforceCCZ4Constraint"):
270 """!
271
272 Set when the solver pushes the solution back onto the CCZ4 constraints
273
274 By default, the solver does not enforce the constraints. If you
275 switch the enforcement on, generate_solver_realisation() inserts a
276 task which applies exagrype::ccz4::enforceCCZ4constraints() to each
277 volume or grid point after each tagged solution update of the
278 numerical scheme.
279
280 @param value: CCZ4Solver.EnforceCCZ4Constraint
281 No, OnlyFinalSolution or IntermediateSteps.
282
283 """
284 if not isinstance(value, CCZ4Solver.EnforceCCZ4Constraint):
285 raise TypeError(
286 f"Solver {self.name}: enforce_ccz4_constraint() expects a "
287 f"CCZ4Solver.EnforceCCZ4Constraint, but got {value!r}"
288 )
289 self._enforce_ccz4_constraint = value
290
292 self,
293 exahype2_project,
294 peano_project,
295 setup_simulation,
296 time_step,
297 plot,
298 mesh_adaptation,
299 ):
300 """!
301
302 Create the solver's task graphs and insert the constraint enforcement
303
304 The routine first calls the superclass, which lets the grid
305 construction, the numerical scheme and the plotter add their tasks.
306 If the constraint enforcement is switched on
307 (enforce_ccz4_constraint()), it afterwards inserts the enforcement
308 after each tagged solution update of the numerical scheme. Which
309 patch a tagged task writes and which guard it uses depends on the
310 scheme, so the routine distinguishes the schemes explicitly
311 (@ref page_exahype2_solver_manipulate_solver_steps). It raises an
312 exception for any other scheme.
313
314 All insertions work on the patch which the tagged tasks write and
315 use the cell guard:
316
317 - exahype2.api.fv.RusanovWithExplicitEuler: one task after Update
318 cell.
319 - exahype2.api.fv.RusanovWithOptimisticExplicitEuler: one task after
320 both Update cell and Insert patch interior. Each cell runs exactly
321 one of the two, so the task constrains every cell once, after its
322 update.
323 - exahype2.api.rkfd4.CentralDifferencesWithRungeKutta and its
324 subclass exagrype.api.FD4WithKODissipation: one task after
325 Complete time step and, for IntermediateSteps, one task after each
326 Prepare stage k on the stage input.
327
328 The scheme's log of the time stepping graph does not show the
329 enforcement. With TEXT or VISUALISATION logging, the routine hence
330 logs the graph once more.
331
332 """
334 exahype2_project,
335 peano_project,
336 setup_simulation,
337 time_step,
338 plot,
339 mesh_adaptation,
340 )
342 if self._enforce_ccz4_constraint == Enforce.No:
343 return
344
345
348 match scheme:
349 case (
352 ):
353 stage_input = None
355 stage_input = self._stage_input
356 case _:
357 raise TypeError(
358 f"Solver {self.name}: cannot enforce the CCZ4 constraints for "
359 f"the numerical scheme {type(scheme).__name__}. Supported are "
360 "exahype2.api.fv.RusanovWithExplicitEuler, "
361 "exahype2.api.fv.RusanovWithOptimisticExplicitEuler and "
362 "exahype2.api.rkfd4.CentralDifferencesWithRungeKutta."
363 )
364
365
368 time_step,
370 time_step, exahype2.api.Solver.SolutionUpdateTaskTag
371 ),
372 self._patch,
373 )
374
375 if (
376 self._enforce_ccz4_constraint == Enforce.IntermediateSteps
377 and stage_input is not None
378 ):
379 for anchor in self._tasks_of_this_solver(
380 time_step, exahype2.api.Solver.IntermediateSolutionUpdateTag
381 ):
382 self._insert_constraint_enforcement(time_step, [anchor], stage_input)
383
384 title = (
385 f"Time stepping task graph for {self.name} with CCZ4 constraint enforcement"
386 )
387 if peano4.api.Logger().is_visualisation:
388 time_step.visualise(title=title)
389 if peano4.api.Logger().is_text:
390 time_step.log_as_text(title=title)
391
392 def _tasks_of_this_solver(self, time_step, tag):
393 """!
394 Tasks of time_step which carry tag and belong to this solver, as the
395 graph holds the tasks of all solvers
396 """
397 return [
398 task
399 for task in time_step.find_tasks(tag)
400 if task.name.startswith(self.namename + "_")
401 ]
402
403 def _insert_constraint_enforcement(self, time_step, anchors, patch):
404 """!
405
406 Insert the enforcement of the CCZ4 constraints after anchors
407
408 The routine creates a task which applies
409 exagrype::ccz4::enforceCCZ4constraints() to each volume or grid point
410 of patch, and inserts it directly after all anchors via
411 peano4.api.taskgraph.Graph.insert_after(). The anchors' successors,
412 e.g. the projection onto the faces, hence see the constrained data
413 only. The task uses the solver's cell guard: If several anchors
414 write patch, each cell is updated by one of them, and the task runs
415 once per cell after all of them.
416
417 @param time_step: peano4.api.taskgraph.Graph
418 The time stepping task graph which holds the anchors.
419
420 @param anchors: List[peano4.api.taskgraph.Task]
421 The tasks which write patch. If the list is empty, the routine
422 does nothing.
423
424 @param patch: peano4.api.datamodel.Patch
425 The cell data which the anchors write, i.e. the solution or a
426 stage input.
427
428 """
429 if not anchors:
430 return
431 anchor_names = " and ".join(
432 anchor.name[len(self.namename) + 1 :] for anchor in anchors
433 )
435 name=f"{self.name}_Enforce CCZ4 constraints after {anchor_names}",
436 entity=peano4.api.taskgraph.Task.GridEntity.CELL,
437 )
438
439 task.use_cell(patch, peano4.api.taskgraph.Task.DataAccess.READ_AND_WRITE)
440 task.add_action_set(
441 toolbox.blockstructured.api.ApplyPointWiselyToPatch(
442 patch=patch,
443 implementation="::exagrype::ccz4::enforceCCZ4constraints(Q);",
444 guard=self.grid_selector.createCellGuard(),
445 additional_includes=self._get_default_includes()
447 )
448 )
449 time_step.insert_after(anchors, task)
450
451 @property
452 def parameters(self) -> Dict[str, Union[int, float]]:
453 """!
454 The CCZ4 parameters the solver is generated with (read-only copy).
455 """
456 return dict(self._parameters)
457
458 @staticmethod
460 """!
461
462 The Finite Volume kernels hand over the jump deltaQ along one normal,
463 the fourth-order Finite Differences of exahype2.api.rkfd4 the
464 derivative along one normal times the mesh width,
465 whereas exagrype::ccz4::ncp() expects the gradient along all three
466 directions. We hence embed the jump into an otherwise empty gradient.
467
468 """
469 return """
470 double deltaQSerialised[NumberOfUnknowns * 3];
471 for (int i = 0; i < NumberOfUnknowns; i++) {
472 deltaQSerialised[i + 0 * NumberOfUnknowns] = 0.0;
473 deltaQSerialised[i + 1 * NumberOfUnknowns] = 0.0;
474 deltaQSerialised[i + 2 * NumberOfUnknowns] = 0.0;
475
476 deltaQSerialised[i + normal * NumberOfUnknowns] = deltaQ[i];
477 }
478 ::exagrype::ccz4::ncp(BTimesDeltaQ, Q, deltaQSerialised, normal, CCZ4LapseType, CCZ4ds, CCZ4c, CCZ4e, CCZ4f, CCZ4bs, CCZ4sk, CCZ4xi, CCZ4mu, CCZ4SO);
479"""
480
481 @staticmethod
483 return """
484 tarch::memset(S, 0, NumberOfUnknowns * sizeof(double));
485 ::exagrype::ccz4::source(S, Q, CCZ4LapseType, CCZ4ds, CCZ4c, CCZ4e, CCZ4f, CCZ4bs, CCZ4sk, CCZ4xi, CCZ4itau, CCZ4eta, CCZ4k1, CCZ4k2, CCZ4k3, CCZ4SO);
486"""
487
488 @staticmethod
490 return """
491 ::exagrype::ccz4::maxEigenvalue(Q, normal, CCZ4e, CCZ4ds, CCZ4GLMc, CCZ4GLMd, lambdaMax);
492"""
When to push the solution back onto the algebraic CCZ4 constraints.
Definition CCZ4Solver.py:63
Solver for the Einstein equations in the first-order CCZ4 formulation.
Definition CCZ4Solver.py:16
Dict[str, Union[int, float]] parameters(self)
The CCZ4 parameters the solver is generated with (read-only copy).
generate_solver_realisation(self, exahype2_project, peano_project, setup_simulation, time_step, plot, mesh_adaptation)
Create the solver's task graphs and insert the constraint enforcement.
enforce_ccz4_constraint(self, "CCZ4Solver.EnforceCCZ4Constraint" value)
Set when the solver pushes the solution back onto the CCZ4 constraints.
_enforce_ccz4_constraint
One task after all writers of the new solution: The optimistic Finite Volumes write it in one of two ...
str _ncp_implementation()
The Finite Volume kernels hand over the jump deltaQ along one normal, the fourth-order Finite Differe...
List[str] lopsided_advection_quantities(self)
The unknowns whose advection terms exagrype::ccz4::ncp() omits.
_patch
One task after all writers of the new solution: The optimistic Finite Volumes write it in one of two ...
_tasks_of_this_solver(self, time_step, tag)
Tasks of time_step which carry tag and belong to this solver, as the graph holds the tasks of all sol...
_insert_constraint_enforcement(self, time_step, anchors, patch)
Insert the enforcement of the CCZ4 constraints after anchors.
__init__(self, str name, Dict[str, Union[int, float]] parameters=None, exahype2.api.InitPointWisely initial_condition=None)
Create a CCZ4 solver.
Point-wise initial condition: given the physical position of a single degree of freedom,...
The maximum absolute eigenvalue of the flux Jacobian in a given direction, i.e.
The non-conservative product B(Q) applied to a jump deltaQ in a given direction, i....
Abstract base class for all ExaHyPE 2 solvers.
Definition Solver.py:17
"Solver.NumericalScheme" numerical_scheme(self)
Definition Solver.py:570
add_abstract_solver_attribute(self, str declaration)
Add a raw C++ member attribute declaration to the generated Abstract<SolverName> class (.
Definition Solver.py:798
str _get_default_includes(self)
Includes every generated action set can rely on being available, regardless of which action sets a co...
Definition Solver.py:682
add_pde_term(self, PDETerm term)
Register a PDE term with this solver, or replace the one already registered under the same name and s...
Definition Solver.py:767
add_user_solver_includes(self, str value)
Add further includes to this property, if your solver requires some additional routines from other he...
Definition Solver.py:675
str user_solver_includes(self)
Add further includes to this property, if your solver requires some additional routines from other he...
Definition Solver.py:668
numerical_scheme(self, "Solver.NumericalScheme" value)
Definition Solver.py:574
str name(self)
Unique solver name.
Definition Solver.py:563
The algebraic, volume-distributed source term S(Q) of a hyperbolic PDE - as opposed to PointSource,...
Finite Volumes with a Rusanov Riemann solver and explicit Euler time stepping, realised over a block-...
Finite Volumes with a Rusanov Riemann solver and optimistic explicit Euler time stepping,...
Fourth-order central Finite Differences with explicit Runge-Kutta time stepping, realised over a bloc...
Singleton logger for the Peano 4 Python API.
Definition Logger.py:37
One task within a peano4.api.taskgraph.Graph.
Definition Task.py:8
The task graph abstraction allows users to formalise their solver steps over small task graphs which ...
Definition __init__.py:1