include/meltpooldg/core/simulation_case_base.hpp Source File

Developer Documentation: include/meltpooldg/core/simulation_case_base.hpp Source File
Developer Documentation
simulation_case_base.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <deal.II/base/function.h>
4#include <deal.II/base/mpi.h>
5#include <deal.II/base/parameter_handler.h>
6#include <deal.II/base/revision.h>
7#include <deal.II/base/types.h>
8
9#include <deal.II/grid/grid_tools.h>
10#include <deal.II/grid/tria.h>
11
17#include <meltpooldg/utilities/revision.hpp>
18
19#include <algorithm>
20#include <fstream>
21#include <iostream>
22#include <map>
23#include <memory>
24#include <string>
25#include <vector>
26
27#include "parameters_base.hpp"
28
29namespace MeltPoolDG
30{
42 template <int dim, typename number, int spacedim = dim>
44 {
45 public:
46 virtual ~SimulationCaseBase() = default;
47
48 // Triangulation object. Needs to be filled inside create_spatial_discretization()
49 std::shared_ptr<dealii::Triangulation<dim, spacedim>> triangulation;
50
51 // Path to the parameter file
52 const std::string parameter_file;
53
54 // MPI communicator for parallel execution
55 // TODO: make private
56 const MPI_Comm mpi_communicator;
57
58 SimulationCaseBase(const std::string &parameter_file_in, MPI_Comm mpi_communicator_in)
59 : parameter_file(parameter_file_in)
60 , mpi_communicator(mpi_communicator_in)
61 {}
62
71 void
73 {
76 AssertThrow(
77 this->triangulation,
78 dealii::ExcMessage(
79 "It seems that your SimulationCaseBase object does not contain"
80 " a valid triangulation object. A shared_ptr to your triangulation"
81 " must be specified as follows for a serialized triangulation "
82 " this->triangulation = std::make_shared<Triangulation<dim>>(); "
83 " or for a parallel triangulation "
84 " this->triangulation = std::make_shared<parallel::distributed::Triangulation<dim>>(this->mpi_communicator); "));
87 }
88
100 std::map<dealii::types::boundary_id, std::shared_ptr<dealii::Function<dim>>>
101 get_boundary_condition(const std::string &type,
102 const std::string &operation_name,
103 const bool is_optional = true) const
104 {
105 AssertThrow(is_optional || boundary_conditions_map.contains(operation_name),
106 dealii::ExcMessage(
107 "BC for " + operation_name +
108 " not found. "
109 "Did you forget to register the operation via "
110 "attach_boundary_condition({id, function} ,operation_name, function)?"));
111
112 if (!boundary_conditions_map.contains(operation_name))
113 return std::map<dealii::types::boundary_id, std::shared_ptr<dealii::Function<dim>>>();
114 else
115 return boundary_conditions_map.at(operation_name)->get_bc_of_type(type, is_optional);
116 }
117
126 std::shared_ptr<BoundaryConditionManager<dim, number>>
127 get_boundary_condition_manager(const std::string &operation_name,
128 const bool is_optional = true) const
129 {
130 AssertThrow(is_optional || boundary_conditions_map.contains(operation_name),
131 dealii::ExcMessage(
132 "BC for " + operation_name +
133 " not found. "
134 "Did you forget to register the operation via "
135 "attach_boundary_condition({id, function} ,operation_name, function)?"));
136
137 if (!boundary_conditions_map.contains(operation_name))
138 return std::make_shared<BoundaryConditionManager<dim, number>>();
139 else
140 return boundary_conditions_map.at(operation_name);
141 }
142
152 std::string
153 get_boundary_condition_type(dealii::types::boundary_id boundary_id,
154 const std::string &operation_name) const
155 {
156 AssertThrow(!boundary_conditions_map[operation_name].empty(),
157 dealii::ExcMessage(
158 "BC for " + operation_name +
159 " not found. "
160 "Did you forget to register the operation via "
161 "attach_boundary_condition({id, function} ,operation_name, function)?"));
162
163 return boundary_conditions_map.at(operation_name)->get_type(boundary_id);
164 }
165
173 void
175 {
176 for (const auto &[operation, bc] : boundary_conditions_map)
177 bc->set_time(time);
178 }
179
196 std::shared_ptr<dealii::Function<dim>>
197 get_field_function(const std::string &type,
198 const std::string &operation_name,
199 const bool is_optional = false)
200 {
201 auto field_conditions = field_functions[operation_name];
202
203 AssertThrow(is_optional || (!field_conditions.empty() && field_conditions[type]),
204 ExcFieldNotAttached(type, operation_name));
205 if (!field_conditions.empty() && field_conditions[type])
206 return field_conditions.at(type);
207 else // is_optional = true
208 return nullptr;
209 }
217 std::shared_ptr<dealii::Function<dim>>
218 get_initial_condition(const std::string &operation_name, const bool is_optional = false)
219 {
220 return get_field_function("initial_condition", operation_name, is_optional);
221 }
222
230 {
232 }
233
241 virtual void
242 do_postprocessing([[maybe_unused]] const GenericDataOut<dim, number> &generic_data_out) const
243 {
244 // do nothing default
245 }
246
247 protected:
248 // Methods to be implemented in derived classes
249
256 virtual void
258
264 virtual void
266
272 virtual void
274
284 virtual bool
285 add_case_specific_parameters(dealii::ParameterHandler &)
286 {
287 return false;
288 // default: do nothing
289 }
290
314 void
315 attach_field_function(std::shared_ptr<dealii::Function<dim>> function,
316 const std::string &type,
317 const std::string &operation_name)
318 {
319 field_functions[operation_name][type] = function;
320 }
321
326 void
327 attach_initial_condition(std::shared_ptr<dealii::Function<dim>> initial_function,
328 const std::string &operation_name)
329 {
330 attach_field_function(initial_function, "initial_condition", operation_name);
331 }
332
344 void
346 std::pair<const dealii::types::boundary_id, const std::shared_ptr<dealii::Function<dim>>>
347 id_and_function,
348 const std::string &type,
349 const std::string &operation_name)
350 {
351 // create boundary conditions object for a given operation_name if it has not yet been created
352 if (!boundary_conditions_map[operation_name])
353 boundary_conditions_map[operation_name] =
354 std::make_shared<BoundaryConditionManager<dim, number>>();
355
356 boundary_conditions_map[operation_name]->attach_boundary_condition(id_and_function, type);
357 }
358
369 void
370 attach_boundary_condition(const dealii::types::boundary_id id,
371 const std::string &type,
372 const std::string &operation_name)
373 {
374 attach_boundary_condition({id, nullptr /*dummy_function*/}, type, operation_name);
375 }
376
383 void
384 attach_periodic_boundary_condition(const dealii::types::boundary_id id_in,
385 const dealii::types::boundary_id id_out,
386 const int direction)
387 {
388 AssertThrow(this->triangulation,
389 dealii::ExcMessage("You try to pass periodic faces but the triangulation "
390 "is still empty."));
391
392 periodic_boundary_conditions.attach_boundary_condition(id_in, id_out, direction);
393
394 // distribute periodic bc to the triangulation
395 std::vector<
396 dealii::GridTools::PeriodicFacePair<typename dealii::Triangulation<dim>::cell_iterator>>
397 periodic_faces;
398
399 dealii::GridTools::collect_periodic_faces(
400 *triangulation, id_in, id_out, direction, periodic_faces);
401 triangulation->add_periodicity(periodic_faces);
402 }
403
404 private:
405 // Manager for periodic boundary conditions
407
408 // nested dictionary for storing field functions as
409 // field_functions[<operation_name>][<type>] = <function>.
410 // The <operation_name> and <type> are specified in the respective applications.
411 std::map<std::string, std::map<std::string, std::shared_ptr<dealii::Function<dim>>>>
413
414 // This map organizes boundary conditions hierarchically based on operation names.
415 std::map<std::string, std::shared_ptr<BoundaryConditionManager<dim, number>>>
417
421 void
423 {
424 bool enable_print = false;
427 [this, &enable_print](dealii::ParameterHandler &prm) {
428 enable_print = add_case_specific_parameters(prm) &&
429 (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0);
430 },
431 enable_print,
432 false /* print details */,
433 true /* skip undefined parameters */,
434 "case specific");
435 }
436 };
437
438 template <typename ParametersType,
439 template <int, typename>
440 class CaseType,
441 template <int, typename>
442 class ApplicationType>
443 void
444 run_simulation(const std::string &parameter_file, const MPI_Comm mpi_communicator)
445 {
446 unsigned int dim = 0;
447 std::string number;
448 std::string case_name;
449
450 // Read and process parameters
451 {
452 dealii::ParameterHandler prm;
453 ParametersType parameters;
454 parameters.process_parameters_file(prm, parameter_file);
455
456 // Print number of processes and GIT hashes if verbosity level >= 3
457 if (parameters.base.verbosity_level >= 3)
458 {
459 dealii::ConditionalOStream pcout(
460 std::cout, dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0);
463 "Running simulation on " +
464 std::to_string(
465 dealii::Utilities::MPI::n_mpi_processes(mpi_communicator)) +
466 " ranks.");
468 pcout << " - deal.II:" << std::endl
469 << " * branch: " << DEAL_II_GIT_BRANCH << std::endl
470 << " * revision: " << DEAL_II_GIT_REVISION << std::endl
471 << " * short: " << DEAL_II_GIT_SHORTREV << std::endl;
473 }
474
475 if (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0 &&
476 parameters.base.do_print_parameters)
477 parameters.print_parameters(prm, std::cout, false /*print_details*/);
478
479 dim = parameters.base.dimension;
480 number = parameters.base.number;
481 case_name = parameters.base.case_name;
482 }
483
484 // Try to run the simulation based on the dimension
485 try
486 {
487 if (number == "double")
488 {
489 if (dim == 1)
490 {
491 auto sim =
492 SimulationCaseFactory<CaseType<1, double>>::create_simulation(case_name,
493 parameter_file,
494 mpi_communicator);
495 sim->create();
496 auto application = std::make_unique<ApplicationType<1, double>>(std::move(sim));
497 application->run();
498 }
499 else if (dim == 2)
500 {
501 auto sim =
502 SimulationCaseFactory<CaseType<2, double>>::create_simulation(case_name,
503 parameter_file,
504 mpi_communicator);
505 sim->create();
506 auto application = std::make_unique<ApplicationType<2, double>>(std::move(sim));
507 application->run();
508 }
509 else if (dim == 3)
510 {
511 auto sim =
512 SimulationCaseFactory<CaseType<3, double>>::create_simulation(case_name,
513 parameter_file,
514 mpi_communicator);
515 sim->create();
516 auto application = std::make_unique<ApplicationType<3, double>>(std::move(sim));
517 application->run();
518 }
519 else
520 {
521 AssertThrow(false, dealii::ExcMessage("Dimension must be 1, 2, or 3."));
522 }
523 }
524 else
525 {
526 AssertThrow(false,
527 dealii::ExcMessage("Currently, explicit template instantiations are "
528 "only done for floating point number format 'double'."));
529 }
530 }
531 catch (std::exception &exc)
532 {
533 std::cerr << "\n\n----------------------------------------------------" << std::endl;
534 std::cerr << "Exception on processing: " << std::endl
535 << exc.what() << std::endl
536 << "Aborting!" << std::endl
537 << "----------------------------------------------------" << std::endl;
538 }
539 catch (...)
540 {
541 std::cerr << "\n\n----------------------------------------------------" << std::endl;
542 std::cerr << "Unknown exception!" << std::endl
543 << "Aborting!" << std::endl
544 << "----------------------------------------------------" << std::endl;
545 }
546 }
547
548 template <typename Parameters,
549 template <int, typename>
550 class Case,
551 template <int, typename>
552 class Application>
553 void
554 default_main(int argc, char *argv[], MPI_Comm mpi_comm)
555 {
556 // Ensure at least one input file is provided
557 if (argc < 2)
558 {
559 AssertThrow(false, dealii::ExcMessage("no input file specified"));
560 return;
561 }
562
563 std::string input_file = argv[argc - 1]; // Last argument as input file
564
565 // Handle help options
566 if (argc == 3 && (std::string(argv[1]) == "--help" || std::string(argv[1]) == "--help-detail"))
567 {
568 dealii::ParameterHandler prm;
569 Parameters parameters;
570 parameters.process_parameters_file(prm, input_file);
571
572 if (dealii::Utilities::MPI::this_mpi_process(mpi_comm) == 0)
573 parameters.print_parameters(prm, std::cout, std::string(argv[1]) == "--help-detail");
574
575 return;
576 }
577
578 AssertThrow(argc < 4,
579 dealii::ExcMessage(
580 "The provided number of command line parameters is not supported."));
581
582 run_simulation<Parameters, Case, Application>(input_file, mpi_comm);
583 }
584} // namespace MeltPoolDG
A generic utility for managing simulation output data in the MeltPoolDG context.
Definition generic_data_out.hpp:32
Base class for managing a simulation case in a parallel computing environment.
Definition simulation_case_base.hpp:44
std::map< std::string, std::shared_ptr< BoundaryConditionManager< dim, number > > > boundary_conditions_map
Definition simulation_case_base.hpp:416
PeriodicBoundaryConditions< dim > periodic_boundary_conditions
Definition simulation_case_base.hpp:406
std::shared_ptr< dealii::Function< dim > > get_initial_condition(const std::string &operation_name, const bool is_optional=false)
Retrieve the initial condition for a given operation.
Definition simulation_case_base.hpp:218
SimulationCaseBase(const std::string &parameter_file_in, MPI_Comm mpi_communicator_in)
Definition simulation_case_base.hpp:58
std::shared_ptr< dealii::Function< dim > > get_field_function(const std::string &type, const std::string &operation_name, const bool is_optional=false)
Definition simulation_case_base.hpp:197
void attach_boundary_condition(std::pair< const dealii::types::boundary_id, const std::shared_ptr< dealii::Function< dim > > > id_and_function, const std::string &type, const std::string &operation_name)
Attach a boundary condition for a specific operation.
Definition simulation_case_base.hpp:345
const PeriodicBoundaryConditions< dim > & get_periodic_bc() const
Get the periodic boundary condition manager.
Definition simulation_case_base.hpp:229
std::string get_boundary_condition_type(dealii::types::boundary_id boundary_id, const std::string &operation_name) const
Retrieve the type of a boundary condition for a specific boundary ID and operation.
Definition simulation_case_base.hpp:153
std::shared_ptr< dealii::Triangulation< dim, spacedim > > triangulation
Definition simulation_case_base.hpp:49
virtual ~SimulationCaseBase()=default
void parse_simulation_specific_parameters()
Parses simulation-specific parameters.
Definition simulation_case_base.hpp:422
const std::string parameter_file
Definition simulation_case_base.hpp:52
std::map< std::string, std::map< std::string, std::shared_ptr< dealii::Function< dim > > > > field_functions
Definition simulation_case_base.hpp:412
virtual void do_postprocessing(const GenericDataOut< dim, number > &generic_data_out) const
Perform specific postprocessing (can be overridden by derived classes).
Definition simulation_case_base.hpp:242
void create()
Main setup function to initialize the simulation.
Definition simulation_case_base.hpp:72
std::shared_ptr< BoundaryConditionManager< dim, number > > get_boundary_condition_manager(const std::string &operation_name, const bool is_optional=true) const
Retrieve the boundary condition object for a specific operation.
Definition simulation_case_base.hpp:127
void set_time_boundary_conditions(const number time)
Update the time for all boundary conditions.
Definition simulation_case_base.hpp:174
std::map< dealii::types::boundary_id, std::shared_ptr< dealii::Function< dim > > > get_boundary_condition(const std::string &type, const std::string &operation_name, const bool is_optional=true) const
Retrieve boundary conditions of a specific type for an operation.
Definition simulation_case_base.hpp:101
virtual void set_boundary_conditions()=0
Pure virtual function to set the boundary conditions.
virtual void create_spatial_discretization()=0
Pure virtual function to create the spatial discretization.
void attach_periodic_boundary_condition(const dealii::types::boundary_id id_in, const dealii::types::boundary_id id_out, const int direction)
Definition simulation_case_base.hpp:384
virtual void set_field_conditions()=0
Pure virtual function to set the field conditions.
void attach_initial_condition(std::shared_ptr< dealii::Function< dim > > initial_function, const std::string &operation_name)
Definition simulation_case_base.hpp:327
virtual bool add_case_specific_parameters(dealii::ParameterHandler &)
Add simulation-specific parameters (can be overridden).
Definition simulation_case_base.hpp:285
void attach_boundary_condition(const dealii::types::boundary_id id, const std::string &type, const std::string &operation_name)
Attach a boundary condition with a dummy function for a specific operation.
Definition simulation_case_base.hpp:370
void attach_field_function(std::shared_ptr< dealii::Function< dim > > function, const std::string &type, const std::string &operation_name)
Definition simulation_case_base.hpp:315
const MPI_Comm mpi_communicator
Definition simulation_case_base.hpp:56
Factory class for registering and creating simulation cases.
Definition case_factory.hpp:19
void print_decoration_line(const ConditionalOStream &pcout)
Definition journal.hpp:19
void print_line(const ConditionalOStream &pcout, const std::string &text="", const std::string &operation_name="", const unsigned int extra_size=0)
Definition journal.cpp:11
Interface for a general preconditioner.
Definition boundary_condition_functions.hpp:17
void default_main(int argc, char *argv[], MPI_Comm mpi_comm)
Definition simulation_case_base.hpp:554
void add_and_parse_parameters(const std::string &parameter_file, const std::function< void(dealii::ParameterHandler &)> &add_parameters, const bool &enable_print=false, const bool print_details=false, const bool skip_undefined=true, const std::string &subsection="")
Adds parameters from a file to a ParameterHandler object.
Definition parameters_base.cpp:7
void run_simulation(const std::string &parameter_file, const MPI_Comm mpi_communicator)
Definition simulation_case_base.hpp:444
Definition periodic_boundary_conditions.hpp:11