ASPECT
plugins.h
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2024 by the authors of the ASPECT code.
3 
4  This file is part of ASPECT.
5 
6  ASPECT is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2, or (at your option)
9  any later version.
10 
11  ASPECT is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with ASPECT; see the file LICENSE. If not see
18  <http://www.gnu.org/licenses/>.
19 */
20 
21 
22 #ifndef _aspect_plugins_h
23 #define _aspect_plugins_h
24 
25 #include <aspect/global.h>
26 
27 #include <deal.II/base/utilities.h>
28 #include <deal.II/base/parameter_handler.h>
29 #include <deal.II/fe/component_mask.h>
30 
31 #include <boost/core/demangle.hpp>
32 
33 #include <tuple>
34 #include <string>
35 #include <list>
36 #include <set>
37 #include <map>
38 #include <iostream>
39 #include <typeinfo>
40 #include <type_traits>
41 
42 #include <mpi.h>
43 
44 
45 namespace aspect
46 {
47  template <int dim> class SimulatorAccess;
48 
49  namespace Plugins
50  {
69  template <typename TestType, typename PluginType,
70  typename = typename std::enable_if_t<std::is_base_of<PluginType,TestType>::value>>
71  inline
72  bool
73  plugin_type_matches (const PluginType &object)
74  {
75  return (dynamic_cast<const TestType *> (&object) != nullptr);
76  }
77 
95  template <typename TestType, typename PluginType,
96  typename = typename std::enable_if_t<std::is_base_of<PluginType,TestType>::value>>
97  inline
98  TestType &
99  get_plugin_as_type (PluginType &object)
100  {
101  AssertThrow(plugin_type_matches<TestType>(object),
102  ExcMessage("You have requested to convert a plugin of type <"
103  + boost::core::demangle(typeid(PluginType).name())
104  + "> into type <"
105  + boost::core::demangle(typeid(TestType).name()) +
106  ">, but this cast cannot be performed."));
107 
108  // We can safely dereference the pointer, because we checked above that
109  // the object is actually of type TestType, and so the result
110  // is not a nullptr.
111  return *dynamic_cast<TestType *> (&object);
112  }
113  }
114 
115  namespace Plugins
116  {
128  {
129  public:
134  virtual ~InterfaceBase() = default;
135 
147  virtual
148  void
149  initialize ();
150 
160  virtual
161  void
162  update ();
163 
172  static
173  void
174  declare_parameters (ParameterHandler &prm);
175 
184  virtual
185  void
186  parse_parameters (ParameterHandler &prm);
187 
209  virtual
210  void save (std::map<std::string, std::string> &status_strings) const;
211 
223  virtual
224  void load (const std::map<std::string, std::string> &status_strings);
225  };
226 
227 
228 
247  template <typename InterfaceType>
248  class ManagerBase : public InterfaceBase
249  {
250  public:
254  ~ManagerBase () override;
255 
260  void
261  update () override;
262 
272  template <typename PluginType,
273  typename = typename std::enable_if_t<std::is_base_of<InterfaceType,PluginType>::value>>
274  bool
275  has_matching_active_plugin () const;
276 
291  template <typename PluginType,
292  typename = typename std::enable_if_t<std::is_base_of<InterfaceType,PluginType>::value>>
293  const PluginType &
294  get_matching_active_plugin () const;
295 
301  const std::list<std::unique_ptr<InterfaceType>> &
302  get_active_plugins () const;
303 
310  const std::vector<std::string> &
311  get_active_plugin_names () const;
312 
317  template <class Archive>
318  void save (Archive &ar,
319  const unsigned int version) const;
320 
327  using InterfaceBase::save;
328 
333  template <class Archive>
334  void load (Archive &ar,
335  const unsigned int version);
336 
343  using InterfaceBase::load;
344 
345  BOOST_SERIALIZATION_SPLIT_MEMBER()
346 
347  protected:
352  std::list<std::unique_ptr<InterfaceType>> plugin_objects;
353 
358  std::vector<std::string> plugin_names;
359  };
360 
361 
362 
363  template <typename InterfaceType>
365  {
366  // only check and throw if we are not unwinding the stack due
367  // to an active exception
368 #ifdef DEAL_II_HAVE_CXX17
369  if (std::uncaught_exceptions() == 0)
370 #else
371  if (std::uncaught_exception() == false)
372 #endif
373  {
374  Assert (plugin_names.size() == plugin_objects.size(), ExcInternalError());
375  }
376  }
377 
378 
379  template <typename InterfaceType>
381  {
382  // call the update() functions of all plugins:
383  for (const auto &p : plugin_objects)
384  {
385  try
386  {
387  p->update ();
388  }
389 
390  // plugins that throw exceptions usually do not result in
391  // anything good because they result in an unwinding of the stack
392  // and, if only one processor triggers an exception, the
393  // destruction of objects often causes a deadlock. thus, if
394  // an exception is generated, catch it, print an error message,
395  // and abort the program
396  catch (std::exception &exc)
397  {
398  std::cerr << std::endl << std::endl
399  << "----------------------------------------------------"
400  << std::endl;
401  std::cerr << "Exception on MPI process <"
402  << ::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD)
403  << "> while running plugin <"
404  << typeid(*p).name()
405  << ">: " << std::endl
406  << exc.what() << std::endl
407  << "Aborting!" << std::endl
408  << "----------------------------------------------------"
409  << std::endl;
410 
411  // terminate the program!
412  MPI_Abort (MPI_COMM_WORLD, 1);
413  }
414  catch (...)
415  {
416  std::cerr << std::endl << std::endl
417  << "----------------------------------------------------"
418  << std::endl;
419  std::cerr << "Exception on MPI process <"
420  << ::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD)
421  << "> while running plugin <"
422  << typeid(*p).name()
423  << ">: " << std::endl;
424  std::cerr << "Unknown exception!" << std::endl
425  << "Aborting!" << std::endl
426  << "----------------------------------------------------"
427  << std::endl;
428 
429  // terminate the program!
430  MPI_Abort (MPI_COMM_WORLD, 1);
431  }
432  }
433  }
434 
435 
436 
437  template <typename InterfaceType>
438  template <class Archive>
440  const unsigned int) const
441  {
442  // let all the postprocessors save their data in a map and then
443  // serialize that
444  std::map<std::string,std::string> saved_text;
445  for (const auto &p : plugin_objects)
446  p->save (saved_text);
447 
448  ar &saved_text;
449  }
450 
451 
452  template <typename InterfaceType>
453  template <class Archive>
455  const unsigned int)
456  {
457  // get the map back out of the stream; then let the postprocessors
458  // that we currently have get their data from there. note that this
459  // may not be the same set of postprocessors we had when we saved
460  // their data
461  std::map<std::string,std::string> saved_text;
462  ar &saved_text;
463 
464  for (auto &p : plugin_objects)
465  p->load (saved_text);
466  }
467 
468 
469 
470  template <typename InterfaceType>
471  template <typename PluginType, typename>
472  inline
473  bool
475  {
476  for (const auto &p : plugin_objects)
477  if (Plugins::plugin_type_matches<PluginType>(*p))
478  return true;
479  return false;
480  }
481 
482 
483  template <typename InterfaceType>
484  template <typename PluginType, typename>
485  inline
486  const PluginType &
488  {
489  AssertThrow(has_matching_active_plugin<PluginType> (),
490  ExcMessage("You asked the object managing a collection of plugins for a "
491  "plugin object of type <" + boost::core::demangle(typeid(PluginType).name()) + "> "
492  "that could not be found in the current model. You need to "
493  "activate this plugin in the input file for it to be "
494  "available."));
495 
496  for (const auto &p : plugin_objects)
497  if (Plugins::plugin_type_matches<PluginType>(*p))
498  return Plugins::get_plugin_as_type<PluginType>(*p);
499 
500  // We will never get here, because we had the Assert above. Just to avoid warnings.
501  return Plugins::get_plugin_as_type<PluginType>(**(plugin_objects.begin()));
502  }
503 
504 
505 
506  template <typename InterfaceType>
507  const std::list<std::unique_ptr<InterfaceType>> &
509  {
510  return plugin_objects;
511  }
512 
513 
514 
515  template <typename InterfaceType>
516  const std::vector<std::string> &
518  {
519  return plugin_names;
520  }
521 
522  }
523 
524  namespace internal
525  {
530  namespace Plugins
531  {
542  template <typename InterfaceClass,
543  typename ModelClass>
545  {
551  RegisterHelper (void (*register_function) (const std::string &,
552  const std::string &,
553  void ( *)(ParameterHandler &),
554  std::unique_ptr<InterfaceClass> ( *)()),
555  const char *name,
556  const char *description)
557  {
558  register_function (name,
559  description,
561  &factory);
562  }
563 
568  static
569  std::unique_ptr<InterfaceClass> factory ()
570  {
571  return std::make_unique<ModelClass>();
572  }
573  };
574 
575 
580  template <typename InterfaceClass>
581  struct PluginList
582  {
594  using PluginInfo
595  = std::tuple<std::string,
596  std::string,
597  void ( *) (ParameterHandler &),
598  std::unique_ptr<InterfaceClass>( *) ()>;
599 
612  static std::list<PluginInfo> *plugins;
613 
617  ~PluginList ();
618 
624  static
625  void register_plugin (const std::string &name,
626  const std::string &description,
627  void (*declare_parameters_function) (ParameterHandler &),
628  std::unique_ptr<InterfaceClass> (*factory_function) ());
629 
637  static
638  std::string get_pattern_of_names ();
639 
647  static
648  std::string get_description_string ();
649 
653  static
654  void declare_parameters (ParameterHandler &prm);
655 
665  static
666  std::unique_ptr<InterfaceClass>
667  create_plugin (const std::string &name,
668  const std::string &documentation);
669 
680  static
681  std::unique_ptr<InterfaceClass>
682  create_plugin (const std::string &name,
683  const std::string &documentation,
684  ParameterHandler &prm);
685 
707  static
708  void
709  write_plugin_graph (const std::string &plugin_system_name,
710  std::ostream &output_stream,
711  const std::string &attachment_point = "Simulator");
712 
716  DeclException1 (ExcUnknownPlugin,
717  std::string,
718  << "Can't create a plugin of name <" << arg1
719  << "> because such a plugin hasn't been declared.");
720  };
721 
722 
723  /* ------------------------ template and inline functions --------------------- */
724 
725  template <typename InterfaceClass>
728  {
729  // if any plugins have been registered, then delete
730  // the list
731  if (plugins != nullptr)
732  delete plugins;
733  plugins = nullptr;
734  }
735 
736 
737 
738  template <typename InterfaceClass>
739  void
741  register_plugin (const std::string &name,
742  const std::string &description,
743  void (*declare_parameters_function) (ParameterHandler &),
744  std::unique_ptr<InterfaceClass> (*factory_function) ())
745  {
746  // see if this is the first time we get into this
747  // function and if so initialize the static member variable
748  if (plugins == nullptr)
749  plugins = new std::list<PluginInfo>();
750 
751  // verify that the same name has not previously been
752  // used to register a plugin, since we would then no
753  // longer be able to identify the plugin
754  for (const auto &p : *plugins)
755  {
756  Assert (std::get<0>(p) != name,
757  ExcMessage ("A plugin with name <" + name + "> has "
758  "already been registered!"));
759  (void)p;
760  }
761 
762 
763  // now add one record to the list
764  plugins->emplace_back (name,
765  description,
766  declare_parameters_function,
767  factory_function);
768  }
769 
770 
771 
772  template <typename InterfaceClass>
773  std::string
776  {
777  Assert (plugins != nullptr,
778  ExcMessage ("No plugins registered!?"));
779 
780  // get all names and put them into a data structure that keeps
781  // them sorted
782  std::set<std::string> names;
783  for (typename std::list<PluginInfo>::const_iterator
784  p = plugins->begin();
785  p != plugins->end(); ++p)
786  names.insert (std::get<0>(*p));
787 
788  // now create a pattern from all of these sorted names
789  std::string pattern_of_names;
790  for (const auto &name : names)
791  {
792  if (pattern_of_names.size() > 0)
793  pattern_of_names += "|";
794  pattern_of_names += name;
795  }
796 
797  return pattern_of_names;
798  }
799 
800 
801 
802  template <typename InterfaceClass>
803  std::string
806  {
807  std::string description;
808 
809  // get all names_and_descriptions and put them into a data structure that keeps
810  // them sorted
811  std::map<std::string,std::string> names_and_descriptions;
812  for (typename std::list<PluginInfo>::const_iterator
813  p = plugins->begin();
814  p != plugins->end(); ++p)
815  names_and_descriptions[std::get<0>(*p)] = std::get<1>(*p);
816 
817  // then output it all
818  std::map<std::string,std::string>::const_iterator
819  p = names_and_descriptions.begin();
820  while (true)
821  {
822  // write the name and
823  // description of the
824  // parameter
825  description += "`";
826  description += p->first;
827  description += "': ";
828  description += p->second;
829 
830  // increment the pointer
831  // by one. if we are not
832  // at the end yet then
833  // add an empty line
834  ++p;
835  if (p != names_and_descriptions.end())
836  description += "\n\n";
837  else
838  break;
839  }
840 
841  return description;
842  }
843 
844 
845 
846 
847  template <typename InterfaceClass>
848  void
850  declare_parameters (ParameterHandler &prm)
851  {
852  Assert (plugins != nullptr,
853  ExcMessage ("No postprocessors registered!?"));
854 
855  for (typename std::list<PluginInfo>::const_iterator
856  p = plugins->begin();
857  p != plugins->end(); ++p)
858  (std::get<2>(*p))(prm);
859  }
860 
861 
862 
863  template <typename InterfaceClass>
864  std::unique_ptr<InterfaceClass>
866  create_plugin (const std::string &name,
867  const std::string &documentation)
868  {
869  (void)documentation;
870  Assert (plugins != nullptr,
871  ExcMessage ("No postprocessors registered!?"));
872  AssertThrow (name != "unspecified",
873  ExcMessage(std::string("A plugin must have a name!\n\n"
874  "This function was asked to create a plugin but no name for the "
875  "plugin was provided. This may be due to the fact that you did not "
876  "explicitly specify a name for this plugin in your input file and "
877  "ASPECT does not provide a default for this kind of plugin, for "
878  "example because no generally useful plugin exists. An example "
879  "is that there is no default geometry: You need to explicitly "
880  "provide one in the input file, and it seems like you have not "
881  "done so.\n\n"
882  "To find out which kind of plugin this function tries to create, "
883  "take a look at the backtrace of this error message.\n\n"
884  "The place that called this function also provided as "
885  "additional information this:\n\n"
886  " <")
887  + documentation + ">"));
888 
889  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
890  p != plugins->end(); ++p)
891  if (std::get<0>(*p) == name)
892  {
893  std::unique_ptr<InterfaceClass> i = std::get<3>(*p)();
894  return i;
895  }
896 
897  AssertThrow (false, ExcUnknownPlugin(name));
898  return nullptr;
899  }
900 
901 
902 
903  template <typename InterfaceClass>
904  std::unique_ptr<InterfaceClass>
906  create_plugin (const std::string &name,
907  const std::string &documentation,
908  ParameterHandler &prm)
909  {
910  std::unique_ptr<InterfaceClass> i = create_plugin(name, documentation);
911  i->parse_parameters (prm);
912  return i;
913  }
914 
915 
916 
917  template <typename InterfaceClass>
918  void
920  write_plugin_graph (const std::string &plugin_system_name,
921  std::ostream &output_stream,
922  const std::string &attachment_point)
923  {
924  // first output a graph node for the interface class as the central
925  // hub of this plugin system, plotted as a square.
926  //
927  // we use the typeid name of the interface class to label
928  // nodes within this plugin system, as they are unique among
929  // all other plugin systems
930  output_stream << std::string(typeid(InterfaceClass).name())
931  << " [label=\""
932  << plugin_system_name
933  << "\", height=.8,width=.8,shape=\"rect\",fillcolor=\"lightgreen\"]"
934  << std::endl;
935 
936  // then output the graph nodes for each plugin, with links to the
937  // interface class and, as appropriate, from the SimulatorAccess class
938  //
939  // we would like to establish a predictable order of output here, but
940  // plugins self-register via static global variables, and their
941  // initialization order is not deterministic. consequently, let us
942  // loop over all plugins first and put pointers to them into a
943  // map with deterministic keys. as key, we use the declared name
944  // of the plugin by which it is referred in the .prm file
945  std::map<std::string, typename std::list<PluginInfo>::const_iterator>
946  plugin_map;
947  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
948  p != plugins->end(); ++p)
949  plugin_map[std::get<0>(*p)] = p;
950 
951  // now output the information sorted by the plugin names
952  for (typename std::map<std::string, typename std::list<PluginInfo>::const_iterator>::const_iterator
953  p = plugin_map.begin();
954  p != plugin_map.end(); ++p)
955  {
956  // take the name of the plugin and split it into strings of
957  // 15 characters at most; then combine them
958  // again using \n to make dot/neato show these parts of
959  // the name on separate lines
960  const std::vector<std::string> plugin_label_parts
961  = ::Utilities::break_text_into_lines(p->first, 15);
962  Assert (plugin_label_parts.size()>0, ExcInternalError());
963  std::string plugin_name = plugin_label_parts[0];
964  for (unsigned int i=1; i<plugin_label_parts.size(); ++i)
965  plugin_name += "\\n" + plugin_label_parts[i];
966 
967  // next create a (symbolic) node name for this plugin. because
968  // each plugin corresponds to a particular class, use the mangled
969  // name of the class
970  std::unique_ptr<InterfaceClass> instance (create_plugin (p->first, ""));
971  const std::string node_name = typeid(*instance).name();
972 
973  // then output the whole shebang describing this node
974  output_stream << node_name
975  << " [label=\""
976  << plugin_name
977  << "\", height=.8,width=.8,shape=\"circle\",fillcolor=\"lightblue\"];"
978  << std::endl;
979 
980  // next build connections from this plugin to the
981  // interface class
982  output_stream << node_name
983  << " -> "
984  << std::string(typeid(InterfaceClass).name())
985  << " [len=3, weight=50]"
986  << ';'
987  << std::endl;
988 
989  // finally see if this plugin is derived from
990  // SimulatorAccess; if so, draw an arrow from SimulatorAccess
991  // also to the plugin's name
992  if (dynamic_cast<const SimulatorAccess<2>*>(instance.get()) != nullptr
993  ||
994  dynamic_cast<const SimulatorAccess<3>*>(instance.get()) != nullptr)
995  output_stream << "SimulatorAccess"
996  << " -> "
997  << node_name
998  << " [style=\"dotted\", arrowhead=\"empty\", constraint=false, color=\"gray\", len=20, weight=0.1];"
999  << std::endl;
1000  }
1001 
1002  // as a last step, also draw a connection from the interface class
1003  // to the Simulator class, or whatever the calling function indicates
1004  // as the attachment point
1005  output_stream << std::string(typeid(InterfaceClass).name())
1006  << " -> "
1007  << attachment_point
1008  << " [len=15, weight=50]"
1009  << ';'
1010  << std::endl;
1011 
1012  // end it with an empty line to make things easier to
1013  // read when looking over stuff visually
1014  output_stream << std::endl;
1015  }
1016 
1017 
1018 
1019  // Add definition for plugins member:
1020  template <typename InterfaceClass>
1021  inline std::list<typename PluginList<InterfaceClass>::PluginInfo> *PluginList<InterfaceClass>::plugins = nullptr;
1022 
1023 
1024 
1031  }
1032  }
1033 }
1034 
1035 
1036 #endif
const std::vector< std::string > & get_active_plugin_names() const
Definition: plugins.h:517
const PluginType & get_matching_active_plugin() const
Definition: plugins.h:487
std::list< std::unique_ptr< InterfaceType > > plugin_objects
Definition: plugins.h:352
void write_plugin_graph(std::ostream &output_stream)
TestType & get_plugin_as_type(PluginType &object)
Definition: plugins.h:99
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:551
void load(Archive &ar, const unsigned int version)
Definition: plugins.h:454
std::tuple< std::string, std::string, void(*)(ParameterHandler &), std::unique_ptr< InterfaceClass >(*)()> PluginInfo
Definition: plugins.h:598
virtual void load(const std::map< std::string, std::string > &status_strings)
void declare_parameters(ParameterHandler &prm)
void update() override
Definition: plugins.h:380
void save(Archive &ar, const unsigned int version) const
Definition: plugins.h:439
static std::unique_ptr< InterfaceClass > factory()
Definition: plugins.h:569
bool has_matching_active_plugin() const
Definition: plugins.h:474
bool plugin_type_matches(const PluginType &object)
Definition: plugins.h:73
const std::list< std::unique_ptr< InterfaceType > > & get_active_plugins() const
Definition: plugins.h:508
virtual void save(std::map< std::string, std::string > &status_strings) const
static std::list< PluginInfo > * plugins
Definition: plugins.h:612
std::vector< std::string > plugin_names
Definition: plugins.h:358
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.")