PRISMS-PF Manual v3.0-pre
All Classes Functions Variables Enumerations Pages
pde_problem.h
1// SPDX-FileCopyrightText: © 2025 PRISMS Center at the University of Michigan
2// SPDX-License-Identifier: GNU Lesser General Public Version 2.1
3
4#ifndef pde_problem_h
5#define pde_problem_h
6
7#include <deal.II/base/quadrature_lib.h>
8#include <deal.II/fe/fe_q.h>
9#include <deal.II/fe/fe_system.h>
10#include <deal.II/fe/mapping_q1.h>
11
12#include <prismspf/config.h>
13#include <prismspf/core/conditional_ostreams.h>
14#include <prismspf/core/constraint_handler.h>
15#include <prismspf/core/dof_handler.h>
16#include <prismspf/core/invm_handler.h>
17#include <prismspf/core/matrix_free_handler.h>
18#include <prismspf/core/solution_handler.h>
19#include <prismspf/core/solution_output.h>
20#include <prismspf/core/timer.h>
21#include <prismspf/core/triangulation_handler.h>
22#include <prismspf/core/type_enums.h>
23#include <prismspf/core/variable_attributes.h>
24#include <prismspf/solvers/explicit_postprocess_solver.h>
25#include <prismspf/solvers/explicit_solver.h>
26#include <prismspf/solvers/nonexplicit_auxiliary_solver.h>
27#include <prismspf/solvers/nonexplicit_linear_solver.h>
28#include <prismspf/solvers/nonexplicit_self_nonlinear_solver.h>
29#include <prismspf/user_inputs/user_input_parameters.h>
30#include <prismspf/utilities/element_volume.h>
31
32#include <map>
33
34#ifdef PRISMS_PF_WITH_CALIPER
35# include <caliper/cali.h>
36#endif
37
38PRISMS_PF_BEGIN_NAMESPACE
39
44template <int dim, int degree>
46{
47public:
51 explicit PDEProblem(const userInputParameters<dim> &_user_inputs);
52
56 void
57 run();
58
59private:
64 void
65 solve();
66
70 void
71 solve_increment();
72
76 void
77 init_system();
78
82 void
83 reinit_system();
84
88 const userInputParameters<dim> &user_inputs;
89
93 triangulationHandler<dim> triangulation_handler;
94
98 constraintHandler<dim> constraint_handler;
99
103 matrixfreeHandler<dim> matrix_free_handler;
104
108 dealii::MGLevelObject<matrixfreeHandler<dim, float>> multigrid_matrix_free_handler;
109
113 invmHandler<dim, degree> invm_handler;
114
118 solutionHandler<dim> solution_handler;
119
123 dofHandler<dim> dof_handler;
124
130 std::map<fieldType, dealii::FESystem<dim>> fe_system;
131
135 const dealii::MappingQ1<dim> mapping;
136
141
145 explicitConstantSolver<dim, degree> explicit_constant_solver;
146
150 explicitSolver<dim, degree> explicit_solver;
151
155 explicitPostprocessSolver<dim, degree> postprocess_explicit_solver;
156
160 nonexplicitAuxiliarySolver<dim, degree> nonexplicit_auxiliary_solver;
161
165 nonexplicitLinearSolver<dim, degree> nonexplicit_linear_solver;
166
170 nonexplicitSelfNonlinearSolver<dim, degree> nonexplicit_self_nonlinear_solver;
171};
172
173template <int dim, int degree>
175 : user_inputs(_user_inputs)
176 , triangulation_handler(_user_inputs)
177 , constraint_handler(_user_inputs)
178 , matrix_free_handler(_user_inputs)
179 , multigrid_matrix_free_handler(0, 0, _user_inputs)
180 , invm_handler(_user_inputs.var_attributes)
181 , solution_handler(_user_inputs.var_attributes)
182 , dof_handler(_user_inputs)
183 , explicit_constant_solver(user_inputs,
184 matrix_free_handler,
185 invm_handler,
186 constraint_handler,
187 dof_handler,
188 mapping,
189 solution_handler)
190 , explicit_solver(user_inputs,
191 matrix_free_handler,
192 invm_handler,
193 constraint_handler,
194 dof_handler,
195 mapping,
196 solution_handler)
197 , postprocess_explicit_solver(user_inputs,
198 matrix_free_handler,
199 invm_handler,
200 constraint_handler,
201 dof_handler,
202 mapping,
203 solution_handler)
204 , nonexplicit_auxiliary_solver(user_inputs,
205 matrix_free_handler,
206 triangulation_handler,
207 invm_handler,
208 constraint_handler,
209 dof_handler,
210 mapping,
211 multigrid_matrix_free_handler,
212 solution_handler)
213 , nonexplicit_linear_solver(user_inputs,
214 matrix_free_handler,
215 triangulation_handler,
216 invm_handler,
217 constraint_handler,
218 dof_handler,
219 mapping,
220 multigrid_matrix_free_handler,
221 solution_handler)
222 , nonexplicit_self_nonlinear_solver(user_inputs,
223 matrix_free_handler,
224 triangulation_handler,
225 invm_handler,
226 constraint_handler,
227 dof_handler,
228 mapping,
229 multigrid_matrix_free_handler,
230 solution_handler)
231{}
232
233template <int dim, int degree>
234void
236{
237 timer::serial_timer().enter_subsection("Initialization");
238
239 const unsigned int n_proc = dealii::Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD);
240 conditionalOStreams::pout_base() << "number of processes: " << n_proc << "\n"
241 << std::flush;
242
243 const unsigned int n_vect_doubles = dealii::VectorizedArray<double>::size();
244 const unsigned int n_vect_bits = 8 * sizeof(double) * n_vect_doubles;
245
247 << "vectorization over " << n_vect_doubles << " doubles = " << n_vect_bits
248 << " bits (" << dealii::Utilities::System::get_current_vectorization_level() << ')'
249 << "\n"
250 << std::flush;
251
252 // Create the SCALAR/VECTOR FESystem's, if applicable
253 conditionalOStreams::pout_base() << "creating FESystem...\n" << std::flush;
254 for (const auto &[index, variable] : user_inputs.var_attributes)
255 {
256 if (variable.field_type == fieldType::SCALAR &&
257 fe_system.find(fieldType::SCALAR) == fe_system.end())
258 {
259 fe_system.emplace(fieldType::SCALAR,
260 dealii::FESystem<dim>(dealii::FE_Q<dim>(
261 dealii::QGaussLobatto<1>(degree + 1)),
262 1));
263 conditionalOStreams::pout_summary() << " made FESystem for scalar fields\n"
264 << std::flush;
265 }
266 else if (variable.field_type == fieldType::VECTOR &&
267 fe_system.find(fieldType::VECTOR) == fe_system.end())
268 {
269 fe_system.emplace(fieldType::VECTOR,
270 dealii::FESystem<dim>(dealii::FE_Q<dim>(
271 dealii::QGaussLobatto<1>(degree + 1)),
272 dim));
273 conditionalOStreams::pout_summary() << " made FESystem for vector fields\n"
274 << std::flush;
275 }
276 }
277
278 // Create the mesh
279 conditionalOStreams::pout_base() << "creating triangulation...\n" << std::flush;
280 triangulation_handler.generate_mesh();
281
282 // Create the dof handlers.
283 conditionalOStreams::pout_base() << "creating DoFHandlers...\n" << std::flush;
284 dof_handler.init(triangulation_handler, fe_system);
285
286 // Create the constraints
287 conditionalOStreams::pout_base() << "creating constraints...\n" << std::flush;
288 constraint_handler.make_constraints(mapping, dof_handler.get_dof_handlers());
289 constraint_handler.make_mg_constraints(mapping, dof_handler.get_mg_dof_handlers());
290
291 // Reinit the matrix-free objects
292 conditionalOStreams::pout_base() << "initializing matrix-free objects...\n"
293 << std::flush;
294 matrix_free_handler.reinit(mapping,
295 dof_handler.get_dof_handlers(),
296 constraint_handler.get_constraints(),
297 dealii::QGaussLobatto<1>(degree + 1));
298
299 // Initialize the solution set
300 conditionalOStreams::pout_base() << "initializing solution set...\n" << std::flush;
301 solution_handler.init(matrix_free_handler);
302
303 // Initialize the invm and compute it
304 // TODO (landinjm): Output the invm for debug mode. This will create a lot of bloat in
305 // the output directory so we should create a separate flag and/or directory for this.
306 conditionalOStreams::pout_base() << "initializing invm...\n" << std::flush;
307 invm_handler.initialize(matrix_free_handler.get_matrix_free());
308 invm_handler.compute_invm();
309
310 // Initialize the element volumes and compute them
311 // TODO (landinjm): Output the element volumes for debug mode. This will create a lot of
312 // bloat in the output directory so we should create a separate flag and/or directory
313 // for this.
314 conditionalOStreams::pout_base() << "initializing element volumes...\n" << std::flush;
315 element_volume.initialize(matrix_free_handler.get_matrix_free());
316 element_volume.compute_element_volume(fe_system.begin()->second);
317
318 // Initialize the solver types
319 conditionalOStreams::pout_base() << "initializing solvers...\n" << std::flush;
320
321 explicit_constant_solver.init();
322 explicit_solver.init();
323 postprocess_explicit_solver.init();
324 nonexplicit_auxiliary_solver.init();
325 nonexplicit_linear_solver.init();
326 nonexplicit_self_nonlinear_solver.init();
327
328 // Update ghosts
329 solution_handler.update_ghosts();
330
331 // Solve the auxiliary fields at the 0th step
332 conditionalOStreams::pout_base() << "solving auxiliary variables in 0th timestep...\n"
333 << std::flush;
334 nonexplicit_auxiliary_solver.solve();
335
336 // Solve the linear time-independent fields at the 0th step
338 << "solving linear time-independent variables in 0th timestep...\n"
339 << std::flush;
340 nonexplicit_linear_solver.solve();
341
342 // Solve the self-nonlinear time-independent fields at the 0th step
344 << "solving self-nonlinear time-independent variables in 0th timestep...\n"
345 << std::flush;
346 nonexplicit_self_nonlinear_solver.solve();
347
348 // Solve the postprocessed fields at the 0th step
350 << "solving postprocessed variables in 0th timestep...\n"
351 << std::flush;
352 postprocess_explicit_solver.solve();
353
354 // Output initial condition
355 conditionalOStreams::pout_base() << "outputting initial condition...\n" << std::flush;
356 solutionOutput<dim> output_solution(solution_handler.solution_set,
357 dof_handler.get_dof_handlers(),
358 degree,
359 "solution",
360 user_inputs);
361
362 timer::serial_timer().leave_subsection();
363}
364
365template <int dim, int degree>
366void
368{
369 timer::serial_timer().enter_subsection("Solve Increment");
370
371 // Update ghosts
372 solution_handler.update_ghosts();
373 explicit_solver.solve();
374 nonexplicit_auxiliary_solver.solve();
375 nonexplicit_linear_solver.solve();
376 nonexplicit_self_nonlinear_solver.solve();
377
378 timer::serial_timer().leave_subsection();
379}
380
381template <int dim, int degree>
382void
384{
386 << "================================================\n"
387 " Initialization\n"
388 << "================================================\n"
389 << std::flush;
390
391 CALI_MARK_BEGIN("Initialization");
392 init_system();
393 CALI_MARK_END("Initialization");
394
396
398 << "================================================\n"
399 " Solve\n"
400 << "================================================\n"
401 << std::flush;
402 while (user_inputs.temporal_discretization.increment <
403 user_inputs.temporal_discretization.total_increments)
404 {
405 user_inputs.temporal_discretization.increment++;
406 user_inputs.temporal_discretization.time += user_inputs.temporal_discretization.dt;
407
408 CALI_MARK_BEGIN("Solve Increment");
409 solve_increment();
410 CALI_MARK_END("Solve Increment");
411
412 if (user_inputs.output_parameters.should_output(
413 user_inputs.temporal_discretization.increment))
414 {
415 postprocess_explicit_solver.solve();
416
417 solutionOutput<dim> output_solution(solution_handler.solution_set,
418 dof_handler.get_dof_handlers(),
419 degree,
420 "solution",
421 user_inputs);
422
423 // Print the l2-norms of each solution
425 << "Iteration: " << user_inputs.temporal_discretization.increment << "\n";
426 for (const auto &[pair, vector] : solution_handler.solution_set)
427 {
429 << " Solution index " << pair.first << " type " << to_string(pair.second)
430 << " l2-norm: " << vector->l2_norm() << "\n";
431 }
432 conditionalOStreams::pout_base() << "\n" << std::flush;
433 }
434 }
435}
436
437template <int dim, int degree>
438void
440{
441 solve();
442
443#ifndef PRISMS_PF_WITH_CALIPER
445#endif
446}
447
448PRISMS_PF_END_NAMESPACE
449
450#endif
This is the main class that handles the construction and solving of user-specified PDEs.
Definition pde_problem.h:46
PDEProblem(const userInputParameters< dim > &_user_inputs)
Constructor.
Definition pde_problem.h:174
void run()
Run initialization and solving steps of the given problem.
Definition pde_problem.h:439
static dealii::ConditionalOStream & pout_base()
Generic parallel output stream. Used for essential information in release and debug mode.
Definition conditional_ostreams.cc:31
static dealii::ConditionalOStream & pout_summary()
Log output stream for writing a summary.log file.
Definition conditional_ostreams.cc:22
The class handles the generation and application of boundary conditions based on the user-inputs.
Definition constraint_handler.h:25
Class that manages the deal.II DoFHandlers.
Definition dof_handler.h:25
Compute the element volume.
Definition element_volume.h:24
This class handles the explicit solves of all constant fields.
Definition explicit_constant_solver.h:28
This class handles the explicit solves of all postprocessed fields.
Definition explicit_postprocess_solver.h:29
This class handles the explicit solves of all explicit fields.
Definition explicit_solver.h:34
This class handles the computation and access of the inverted mass matrix for explicit solves.
Definition invm_handler.h:25
This class handlers the management and access of the matrix-free objects.
Definition matrix_free_handler.h:25
This class handles all auxiliary solves.
Definition nonexplicit_auxiliary_solver.h:35
This class handles all linear solves.
Definition nonexplicit_linear_solver.h:40
This class handles the self-nonlinear solves of a single nonexplicit field.
Definition nonexplicit_self_nonlinear_solver.h:26
Class that manages solution initialization and swapping with old solutions.
Definition solution_handler.h:22
Class that outputs a passed solution to vtu, vtk, or pvtu.
Definition solution_output.h:25
static dealii::TimerOutput & serial_timer()
deal.II timer for the 0th MPI process
Definition timer.cc:17
static void print_summary()
Print a sorted summary of the timed sections.
Definition timer.cc:38
This class handlers the generation and manipulation of triangulations.
Definition triangulation_handler.h:24
Definition user_input_parameters.h:22