ASPECT
plugins.h
Go to the documentation of this file.
1 // ------------------------------------------------------
2 //
3 // SPDX-License-Identifier: GPL-2.0-or-later
4 //
5 // SPDX-FileCopyrightText: Copyright (C) 2011-2026 by the ASPECT authors.
6 //
7 // This file is part of ASPECT.
8 //
9 // Detailed license information governing the source code and
10 // contributions can be found in the folder LICENSES and in
11 // LICENSE and CONTRIBUTING.md at the top level directory.
12 //
13 // ------------------------------------------------------
14 
15 
16 #ifndef _aspect_plugins_h
17 #define _aspect_plugins_h
18 
19 #include <aspect/global.h>
20 
21 #include <deal.II/base/utilities.h>
24 
25 #include <boost/core/demangle.hpp>
26 
27 #include <tuple>
28 #include <string>
29 #include <list>
30 #include <set>
31 #include <map>
32 #include <iostream>
33 #include <typeinfo>
34 #include <type_traits>
35 
36 #include <mpi.h>
37 
38 
39 namespace aspect
40 {
41  template <int dim> class SimulatorAccess;
42 
43  namespace Plugins
44  {
63  template <typename TestType, typename PluginType,
64  typename = typename std::enable_if_t<std::is_base_of<PluginType,TestType>::value>>
65  inline
66  bool
67  plugin_type_matches (const PluginType &object)
68  {
69  return (dynamic_cast<const TestType *> (&object) != nullptr);
70  }
71 
89  template <typename TestType, typename PluginType,
90  typename = typename std::enable_if_t<std::is_base_of<PluginType,TestType>::value>>
91  inline
92  TestType &
93  get_plugin_as_type (PluginType &object)
94  {
95  AssertThrow(plugin_type_matches<TestType>(object),
96  ExcMessage("You have requested to convert a plugin of type <"
97  + boost::core::demangle(typeid(PluginType).name())
98  + "> into type <"
99  + boost::core::demangle(typeid(TestType).name()) +
100  ">, but this cast cannot be performed."));
101 
102  // We can safely dereference the pointer, because we checked above that
103  // the object is actually of type TestType, and so the result
104  // is not a nullptr.
105  return *dynamic_cast<TestType *> (&object);
106  }
107  }
108 
109  namespace Plugins
110  {
122  {
123  public:
128  virtual ~InterfaceBase() = default;
129 
141  virtual
142  void
143  initialize ();
144 
154  virtual
155  void
156  update ();
157 
166  static
167  void
169 
178  virtual
179  void
180  parse_parameters (ParameterHandler &prm);
181 
203  virtual
204  void save (std::map<std::string, std::string> &status_strings) const;
205 
217  virtual
218  void load (const std::map<std::string, std::string> &status_strings);
219  };
220 
221 
222 
241  template <typename InterfaceType>
242  class ManagerBase : public InterfaceBase
243  {
244  public:
248  ~ManagerBase () override;
249 
254  void
255  update () override;
256 
266  template <typename PluginType,
267  typename = typename std::enable_if_t<std::is_base_of<InterfaceType,PluginType>::value>>
268  bool
269  has_matching_active_plugin () const;
270 
285  template <typename PluginType,
286  typename = typename std::enable_if_t<std::is_base_of<InterfaceType,PluginType>::value>>
287  const PluginType &
288  get_matching_active_plugin () const;
289 
295  const std::list<std::unique_ptr<InterfaceType>> &
296  get_active_plugins () const;
297 
304  const std::vector<std::string> &
305  get_active_plugin_names () const;
306 
311  template <class Archive>
312  void save (Archive &ar,
313  const unsigned int version) const;
314 
321  using InterfaceBase::save;
322 
327  template <class Archive>
328  void load (Archive &ar,
329  const unsigned int version);
330 
337  using InterfaceBase::load;
338 
339  BOOST_SERIALIZATION_SPLIT_MEMBER()
340 
341  protected:
346  std::list<std::unique_ptr<InterfaceType>> plugin_objects;
347 
352  std::vector<std::string> plugin_names;
353  };
354 
355 
356 
357  template <typename InterfaceType>
359  {
360  // only check and throw if we are not unwinding the stack due
361  // to an active exception
362 #ifdef DEAL_II_HAVE_CXX17
363  if (std::uncaught_exceptions() == 0)
364 #else
365  if (std::uncaught_exception() == false)
366 #endif
367  {
368  Assert (plugin_names.size() == plugin_objects.size(), ExcInternalError());
369  }
370  }
371 
372 
373  template <typename InterfaceType>
375  {
376  // call the update() functions of all plugins:
377  for (const auto &p : plugin_objects)
378  {
379  try
380  {
381  p->update ();
382  }
383 
384  // plugins that throw exceptions usually do not result in
385  // anything good because they result in an unwinding of the stack
386  // and, if only one processor triggers an exception, the
387  // destruction of objects often causes a deadlock. thus, if
388  // an exception is generated, catch it, print an error message,
389  // and abort the program
390  catch (std::exception &exc)
391  {
392  std::cerr << std::endl << std::endl
393  << "----------------------------------------------------"
394  << std::endl;
395  std::cerr << "Exception on MPI process <"
396  << ::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD)
397  << "> while running plugin <"
398  << typeid(*p).name()
399  << ">: " << std::endl
400  << exc.what() << std::endl
401  << "Aborting!" << std::endl
402  << "----------------------------------------------------"
403  << std::endl;
404 
405  // terminate the program!
406  MPI_Abort (MPI_COMM_WORLD, 1);
407  }
408  catch (...)
409  {
410  std::cerr << std::endl << std::endl
411  << "----------------------------------------------------"
412  << std::endl;
413  std::cerr << "Exception on MPI process <"
414  << ::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD)
415  << "> while running plugin <"
416  << typeid(*p).name()
417  << ">: " << std::endl;
418  std::cerr << "Unknown exception!" << std::endl
419  << "Aborting!" << std::endl
420  << "----------------------------------------------------"
421  << std::endl;
422 
423  // terminate the program!
424  MPI_Abort (MPI_COMM_WORLD, 1);
425  }
426  }
427  }
428 
429 
430 
431  template <typename InterfaceType>
432  template <class Archive>
434  const unsigned int) const
435  {
436  // let all the postprocessors save their data in a map and then
437  // serialize that
438  std::map<std::string,std::string> saved_text;
439  for (const auto &p : plugin_objects)
440  p->save (saved_text);
441 
442  ar &saved_text;
443  }
444 
445 
446  template <typename InterfaceType>
447  template <class Archive>
449  const unsigned int)
450  {
451  // get the map back out of the stream; then let the postprocessors
452  // that we currently have get their data from there. note that this
453  // may not be the same set of postprocessors we had when we saved
454  // their data
455  std::map<std::string,std::string> saved_text;
456  ar &saved_text;
457 
458  for (auto &p : plugin_objects)
459  p->load (saved_text);
460  }
461 
462 
463 
464  template <typename InterfaceType>
465  template <typename PluginType, typename>
466  inline
467  bool
469  {
470  for (const auto &p : plugin_objects)
471  if (Plugins::plugin_type_matches<PluginType>(*p))
472  return true;
473  return false;
474  }
475 
476 
477  template <typename InterfaceType>
478  template <typename PluginType, typename>
479  inline
480  const PluginType &
482  {
483  AssertThrow(has_matching_active_plugin<PluginType> (),
484  ExcMessage("You asked the object managing a collection of plugins for a "
485  "plugin object of type <" + boost::core::demangle(typeid(PluginType).name()) + "> "
486  "that could not be found in the current model. You need to "
487  "activate this plugin in the input file for it to be "
488  "available."));
489 
490  for (const auto &p : plugin_objects)
491  if (Plugins::plugin_type_matches<PluginType>(*p))
492  return Plugins::get_plugin_as_type<PluginType>(*p);
493 
494  // We will never get here, because we had the Assert above. Just to avoid warnings.
495  return Plugins::get_plugin_as_type<PluginType>(**(plugin_objects.begin()));
496  }
497 
498 
499 
500  template <typename InterfaceType>
501  const std::list<std::unique_ptr<InterfaceType>> &
503  {
504  return plugin_objects;
505  }
506 
507 
508 
509  template <typename InterfaceType>
510  const std::vector<std::string> &
512  {
513  return plugin_names;
514  }
515 
516  }
517 
518  namespace internal
519  {
524  namespace Plugins
525  {
536  template <typename InterfaceClass,
537  typename ModelClass>
539  {
545  RegisterHelper (void (*register_function) (const std::string &,
546  const std::string &,
547  void ( *)(ParameterHandler &),
548  std::unique_ptr<InterfaceClass> ( *)()),
549  const char *name,
550  const char *description)
551  {
552  register_function (name,
553  description,
555  &factory);
556  }
557 
562  static
563  std::unique_ptr<InterfaceClass> factory ()
564  {
565  return std::make_unique<ModelClass>();
566  }
567  };
568 
569 
574  template <typename InterfaceClass>
575  struct PluginList
576  {
588  using PluginInfo
589  = std::tuple<std::string,
590  std::string,
591  void ( *) (ParameterHandler &),
592  std::unique_ptr<InterfaceClass>( *) ()>;
593 
606  static std::list<PluginInfo> *plugins;
607 
611  ~PluginList ();
612 
618  static
619  void register_plugin (const std::string &name,
620  const std::string &description,
621  void (*declare_parameters_function) (ParameterHandler &),
622  std::unique_ptr<InterfaceClass> (*factory_function) ());
623 
631  static
632  std::string get_pattern_of_names ();
633 
641  static
642  std::string get_description_string ();
643 
647  static
649 
659  static
660  std::unique_ptr<InterfaceClass>
661  create_plugin (const std::string &name,
662  const std::string &documentation);
663 
674  static
675  std::unique_ptr<InterfaceClass>
676  create_plugin (const std::string &name,
677  const std::string &documentation,
678  ParameterHandler &prm);
679 
701  static
702  void
703  write_plugin_graph (const std::string &plugin_system_name,
704  std::ostream &output_stream,
705  const std::string &attachment_point = "Simulator");
706 
710  DeclException1 (ExcUnknownPlugin,
711  std::string,
712  << "Can't create a plugin of name <" << arg1
713  << "> because such a plugin hasn't been declared.");
714  };
715 
716 
717  /* ------------------------ template and inline functions --------------------- */
718 
719  template <typename InterfaceClass>
722  {
723  // if any plugins have been registered, then delete
724  // the list
725  if (plugins != nullptr)
726  delete plugins;
727  plugins = nullptr;
728  }
729 
730 
731 
732  template <typename InterfaceClass>
733  void
735  register_plugin (const std::string &name,
736  const std::string &description,
737  void (*declare_parameters_function) (ParameterHandler &),
738  std::unique_ptr<InterfaceClass> (*factory_function) ())
739  {
740  // see if this is the first time we get into this
741  // function and if so initialize the static member variable
742  if (plugins == nullptr)
743  plugins = new std::list<PluginInfo>();
744 
745  // verify that the same name has not previously been
746  // used to register a plugin, since we would then no
747  // longer be able to identify the plugin
748  for (const auto &p : *plugins)
749  {
750  Assert (std::get<0>(p) != name,
751  ExcMessage ("A plugin with name <" + name + "> has "
752  "already been registered!"));
753  (void)p;
754  }
755 
756 
757  // now add one record to the list
758  plugins->emplace_back (name,
759  description,
760  declare_parameters_function,
761  factory_function);
762  }
763 
764 
765 
766  template <typename InterfaceClass>
767  std::string
770  {
771  Assert (plugins != nullptr,
772  ExcMessage ("No plugins registered!?"));
773 
774  // get all names and put them into a data structure that keeps
775  // them sorted
776  std::set<std::string> names;
777  for (typename std::list<PluginInfo>::const_iterator
778  p = plugins->begin();
779  p != plugins->end(); ++p)
780  names.insert (std::get<0>(*p));
781 
782  // now create a pattern from all of these sorted names
783  std::string pattern_of_names;
784  for (const auto &name : names)
785  {
786  if (pattern_of_names.size() > 0)
787  pattern_of_names += "|";
788  pattern_of_names += name;
789  }
790 
791  return pattern_of_names;
792  }
793 
794 
795 
796  template <typename InterfaceClass>
797  std::string
800  {
801  std::string description;
802 
803  // get all names_and_descriptions and put them into a data structure that keeps
804  // them sorted
805  std::map<std::string,std::string> names_and_descriptions;
806  for (typename std::list<PluginInfo>::const_iterator
807  p = plugins->begin();
808  p != plugins->end(); ++p)
809  names_and_descriptions[std::get<0>(*p)] = std::get<1>(*p);
810 
811  // then output it all
812  std::map<std::string,std::string>::const_iterator
813  p = names_and_descriptions.begin();
814  while (true)
815  {
816  // write the name and
817  // description of the
818  // parameter
819  description += "`";
820  description += p->first;
821  description += "': ";
822  description += p->second;
823 
824  // increment the pointer
825  // by one. if we are not
826  // at the end yet then
827  // add an empty line
828  ++p;
829  if (p != names_and_descriptions.end())
830  description += "\n\n";
831  else
832  break;
833  }
834 
835  return description;
836  }
837 
838 
839 
840 
841  template <typename InterfaceClass>
842  void
845  {
846  Assert (plugins != nullptr,
847  ExcMessage ("No postprocessors registered!?"));
848 
849  for (typename std::list<PluginInfo>::const_iterator
850  p = plugins->begin();
851  p != plugins->end(); ++p)
852  (std::get<2>(*p))(prm);
853  }
854 
855 
856 
857  template <typename InterfaceClass>
858  std::unique_ptr<InterfaceClass>
860  create_plugin (const std::string &name,
861  const std::string &documentation)
862  {
863  (void)documentation;
864  Assert (plugins != nullptr,
865  ExcMessage ("No postprocessors registered!?"));
866  AssertThrow (name != "unspecified",
867  ExcMessage(std::string("A plugin must have a name!\n\n"
868  "This function was asked to create a plugin but no name for the "
869  "plugin was provided. This may be due to the fact that you did not "
870  "explicitly specify a name for this plugin in your input file and "
871  "ASPECT does not provide a default for this kind of plugin, for "
872  "example because no generally useful plugin exists. An example "
873  "is that there is no default geometry: You need to explicitly "
874  "provide one in the input file, and it seems like you have not "
875  "done so.\n\n"
876  "To find out which kind of plugin this function tries to create, "
877  "take a look at the backtrace of this error message.\n\n"
878  "The place that called this function also provided as "
879  "additional information this:\n\n"
880  " <")
881  + documentation + ">"));
882 
883  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
884  p != plugins->end(); ++p)
885  if (std::get<0>(*p) == name)
886  {
887  std::unique_ptr<InterfaceClass> i = std::get<3>(*p)();
888  return i;
889  }
890 
891  AssertThrow (false, ExcUnknownPlugin(name));
892  return nullptr;
893  }
894 
895 
896 
897  template <typename InterfaceClass>
898  std::unique_ptr<InterfaceClass>
900  create_plugin (const std::string &name,
901  const std::string &documentation,
902  ParameterHandler &prm)
903  {
904  std::unique_ptr<InterfaceClass> i = create_plugin(name, documentation);
905  i->parse_parameters (prm);
906  return i;
907  }
908 
909 
910 
911  template <typename InterfaceClass>
912  void
914  write_plugin_graph (const std::string &plugin_system_name,
915  std::ostream &output_stream,
916  const std::string &attachment_point)
917  {
918  // first output a graph node for the interface class as the central
919  // hub of this plugin system, plotted as a square.
920  //
921  // we use the typeid name of the interface class to label
922  // nodes within this plugin system, as they are unique among
923  // all other plugin systems
924  output_stream << std::string(typeid(InterfaceClass).name())
925  << " [label=\""
926  << plugin_system_name
927  << "\", height=.8,width=.8,shape=\"rect\",fillcolor=\"lightgreen\"]"
928  << std::endl;
929 
930  // then output the graph nodes for each plugin, with links to the
931  // interface class and, as appropriate, from the SimulatorAccess class
932  //
933  // we would like to establish a predictable order of output here, but
934  // plugins self-register via static global variables, and their
935  // initialization order is not deterministic. consequently, let us
936  // loop over all plugins first and put pointers to them into a
937  // map with deterministic keys. as key, we use the declared name
938  // of the plugin by which it is referred in the .prm file
939  std::map<std::string, typename std::list<PluginInfo>::const_iterator>
940  plugin_map;
941  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
942  p != plugins->end(); ++p)
943  plugin_map[std::get<0>(*p)] = p;
944 
945  // now output the information sorted by the plugin names
946  for (typename std::map<std::string, typename std::list<PluginInfo>::const_iterator>::const_iterator
947  p = plugin_map.begin();
948  p != plugin_map.end(); ++p)
949  {
950  // take the name of the plugin and split it into strings of
951  // 15 characters at most; then combine them
952  // again using \n to make dot/neato show these parts of
953  // the name on separate lines
954  const std::vector<std::string> plugin_label_parts
955  = ::Utilities::break_text_into_lines(p->first, 15);
956  Assert (plugin_label_parts.size()>0, ExcInternalError());
957  std::string plugin_name = plugin_label_parts[0];
958  for (unsigned int i=1; i<plugin_label_parts.size(); ++i)
959  plugin_name += "\\n" + plugin_label_parts[i];
960 
961  // next create a (symbolic) node name for this plugin. because
962  // each plugin corresponds to a particular class, use the mangled
963  // name of the class
964  std::unique_ptr<InterfaceClass> instance (create_plugin (p->first, ""));
965  const std::string node_name = typeid(*instance).name();
966 
967  // then output the whole shebang describing this node
968  output_stream << node_name
969  << " [label=\""
970  << plugin_name
971  << "\", height=.8,width=.8,shape=\"circle\",fillcolor=\"lightblue\"];"
972  << std::endl;
973 
974  // next build connections from this plugin to the
975  // interface class
976  output_stream << node_name
977  << " -> "
978  << std::string(typeid(InterfaceClass).name())
979  << " [len=3, weight=50]"
980  << ';'
981  << std::endl;
982 
983  // finally see if this plugin is derived from
984  // SimulatorAccess; if so, draw an arrow from SimulatorAccess
985  // also to the plugin's name
986  if (dynamic_cast<const SimulatorAccess<2>*>(instance.get()) != nullptr
987  ||
988  dynamic_cast<const SimulatorAccess<3>*>(instance.get()) != nullptr)
989  output_stream << "SimulatorAccess"
990  << " -> "
991  << node_name
992  << " [style=\"dotted\", arrowhead=\"empty\", constraint=false, color=\"gray\", len=20, weight=0.1];"
993  << std::endl;
994  }
995 
996  // as a last step, also draw a connection from the interface class
997  // to the Simulator class, or whatever the calling function indicates
998  // as the attachment point
999  output_stream << std::string(typeid(InterfaceClass).name())
1000  << " -> "
1001  << attachment_point
1002  << " [len=15, weight=50]"
1003  << ';'
1004  << std::endl;
1005 
1006  // end it with an empty line to make things easier to
1007  // read when looking over stuff visually
1008  output_stream << std::endl;
1009  }
1010 
1011 
1012 
1013  // Add definition for plugins member:
1014  template <typename InterfaceClass>
1015  inline std::list<typename PluginList<InterfaceClass>::PluginInfo> *PluginList<InterfaceClass>::plugins = nullptr;
1016 
1017 
1018 
1025  }
1026  }
1027 }
1028 
1029 
1030 #endif
const std::vector< std::string > & get_active_plugin_names() const
Definition: plugins.h:511
const PluginType & get_matching_active_plugin() const
Definition: plugins.h:481
std::list< std::unique_ptr< InterfaceType > > plugin_objects
Definition: plugins.h:346
void write_plugin_graph(std::ostream &output_stream)
TestType & get_plugin_as_type(PluginType &object)
Definition: plugins.h:93
RegisterHelper(void(*register_function)(const std::string &, const std::string &, void(*)(ParameterHandler &), std::unique_ptr< InterfaceClass >(*)()), const char *name, const char *description)
Definition: plugins.h:545
#define AssertThrow(cond, exc)
void load(Archive &ar, const unsigned int version)
Definition: plugins.h:448
static ::ExceptionBase & ExcInternalError()
std::tuple< std::string, std::string, void(*)(ParameterHandler &), std::unique_ptr< InterfaceClass >(*)()> PluginInfo
Definition: plugins.h:592
virtual void load(const std::map< std::string, std::string > &status_strings)
void declare_parameters(ParameterHandler &prm)
#define Assert(cond, exc)
void update() override
Definition: plugins.h:374
void save(Archive &ar, const unsigned int version) const
Definition: plugins.h:433
static std::unique_ptr< InterfaceClass > factory()
Definition: plugins.h:563
bool has_matching_active_plugin() const
Definition: plugins.h:468
bool plugin_type_matches(const PluginType &object)
Definition: plugins.h:67
static ::ExceptionBase & ExcMessage(std::string arg1)
const std::list< std::unique_ptr< InterfaceType > > & get_active_plugins() const
Definition: plugins.h:502
virtual void save(std::map< std::string, std::string > &status_strings) const
static std::list< PluginInfo > * plugins
Definition: plugins.h:606
*  *  const_iterator()=default
std::vector< std::string > plugin_names
Definition: plugins.h:352
DeclException1(ProbabilityFunctionNegative, Point< dim >,<< "Your probability density function in the particle generator " "returned a negative probability density for the following position: "<< arg1<< ". Please check your function expression.")