ASPECT
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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  };
188 
189 
190 
209  template <typename InterfaceType>
210  class ManagerBase : public InterfaceBase
211  {
212  public:
216  ~ManagerBase () override;
217 
222  void
223  update () override;
224 
234  template <typename PluginType,
235  typename = typename std::enable_if_t<std::is_base_of<InterfaceType,PluginType>::value>>
236  bool
237  has_matching_active_plugin () const;
238 
253  template <typename PluginType,
254  typename = typename std::enable_if_t<std::is_base_of<InterfaceType,PluginType>::value>>
255  const PluginType &
256  get_matching_active_plugin () const;
257 
263  const std::list<std::unique_ptr<InterfaceType>> &
264  get_active_plugins () const;
265 
272  const std::vector<std::string> &
273  get_active_plugin_names () const;
274 
275  protected:
280  std::list<std::unique_ptr<InterfaceType>> plugin_objects;
281 
286  std::vector<std::string> plugin_names;
287  };
288 
289 
290 
291  template <typename InterfaceType>
293  {
294  // only check and throw if we are not unwinding the stack due
295  // to an active exception
296 #ifdef DEAL_II_HAVE_CXX17
297  if (std::uncaught_exceptions() == 0)
298 #else
299  if (std::uncaught_exception() == false)
300 #endif
301  {
302  Assert (plugin_names.size() == plugin_objects.size(), ExcInternalError());
303  }
304  }
305 
306 
307  template <typename InterfaceType>
309  {
310  // call the update() functions of all plugins:
311  for (const auto &p : plugin_objects)
312  {
313  try
314  {
315  p->update ();
316  }
317 
318  // plugins that throw exceptions usually do not result in
319  // anything good because they result in an unwinding of the stack
320  // and, if only one processor triggers an exception, the
321  // destruction of objects often causes a deadlock. thus, if
322  // an exception is generated, catch it, print an error message,
323  // and abort the program
324  catch (std::exception &exc)
325  {
326  std::cerr << std::endl << std::endl
327  << "----------------------------------------------------"
328  << std::endl;
329  std::cerr << "Exception on MPI process <"
330  << ::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD)
331  << "> while running plugin <"
332  << typeid(*p).name()
333  << ">: " << std::endl
334  << exc.what() << std::endl
335  << "Aborting!" << std::endl
336  << "----------------------------------------------------"
337  << std::endl;
338 
339  // terminate the program!
340  MPI_Abort (MPI_COMM_WORLD, 1);
341  }
342  catch (...)
343  {
344  std::cerr << std::endl << std::endl
345  << "----------------------------------------------------"
346  << std::endl;
347  std::cerr << "Exception on MPI process <"
348  << ::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD)
349  << "> while running plugin <"
350  << typeid(*p).name()
351  << ">: " << std::endl;
352  std::cerr << "Unknown exception!" << std::endl
353  << "Aborting!" << std::endl
354  << "----------------------------------------------------"
355  << std::endl;
356 
357  // terminate the program!
358  MPI_Abort (MPI_COMM_WORLD, 1);
359  }
360  }
361  }
362 
363 
364  template <typename InterfaceType>
365  template <typename PluginType, typename>
366  inline
367  bool
369  {
370  for (const auto &p : plugin_objects)
371  if (Plugins::plugin_type_matches<PluginType>(*p))
372  return true;
373  return false;
374  }
375 
376 
377  template <typename InterfaceType>
378  template <typename PluginType, typename>
379  inline
380  const PluginType &
382  {
383  AssertThrow(has_matching_active_plugin<PluginType> (),
384  ExcMessage("You asked the object managing a collection of plugins for a "
385  "plugin object of type <" + boost::core::demangle(typeid(PluginType).name()) + "> "
386  "that could not be found in the current model. You need to "
387  "activate this plugin in the input file for it to be "
388  "available."));
389 
390  for (const auto &p : plugin_objects)
391  if (Plugins::plugin_type_matches<PluginType>(*p))
392  return Plugins::get_plugin_as_type<PluginType>(*p);
393 
394  // We will never get here, because we had the Assert above. Just to avoid warnings.
395  return Plugins::get_plugin_as_type<PluginType>(**(plugin_objects.begin()));
396  }
397 
398 
399 
400  template <typename InterfaceType>
401  const std::list<std::unique_ptr<InterfaceType>> &
403  {
404  return plugin_objects;
405  }
406 
407 
408 
409  template <typename InterfaceType>
410  const std::vector<std::string> &
412  {
413  return plugin_names;
414  }
415 
416  }
417 
418  namespace internal
419  {
424  namespace Plugins
425  {
436  template <typename InterfaceClass,
437  typename ModelClass>
439  {
445  RegisterHelper (void (*register_function) (const std::string &,
446  const std::string &,
447  void ( *)(ParameterHandler &),
448  std::unique_ptr<InterfaceClass> ( *)()),
449  const char *name,
450  const char *description)
451  {
452  register_function (name,
453  description,
455  &factory);
456  }
457 
462  static
463  std::unique_ptr<InterfaceClass> factory ()
464  {
465  return std::make_unique<ModelClass>();
466  }
467  };
468 
469 
474  template <typename InterfaceClass>
475  struct PluginList
476  {
488  using PluginInfo
489  = std::tuple<std::string,
490  std::string,
491  void ( *) (ParameterHandler &),
492  std::unique_ptr<InterfaceClass>( *) ()>;
493 
506  static std::list<PluginInfo> *plugins;
507 
511  ~PluginList ();
512 
518  static
519  void register_plugin (const std::string &name,
520  const std::string &description,
521  void (*declare_parameters_function) (ParameterHandler &),
522  std::unique_ptr<InterfaceClass> (*factory_function) ());
523 
531  static
532  std::string get_pattern_of_names ();
533 
541  static
542  std::string get_description_string ();
543 
547  static
548  void declare_parameters (ParameterHandler &prm);
549 
559  static
560  std::unique_ptr<InterfaceClass>
561  create_plugin (const std::string &name,
562  const std::string &documentation);
563 
574  static
575  std::unique_ptr<InterfaceClass>
576  create_plugin (const std::string &name,
577  const std::string &documentation,
578  ParameterHandler &prm);
579 
601  static
602  void
603  write_plugin_graph (const std::string &plugin_system_name,
604  std::ostream &output_stream,
605  const std::string &attachment_point = "Simulator");
606 
610  DeclException1 (ExcUnknownPlugin,
611  std::string,
612  << "Can't create a plugin of name <" << arg1
613  << "> because such a plugin hasn't been declared.");
614  };
615 
616 
617  /* ------------------------ template and inline functions --------------------- */
618 
619  template <typename InterfaceClass>
622  {
623  // if any plugins have been registered, then delete
624  // the list
625  if (plugins != nullptr)
626  delete plugins;
627  plugins = nullptr;
628  }
629 
630 
631 
632  template <typename InterfaceClass>
633  void
635  register_plugin (const std::string &name,
636  const std::string &description,
637  void (*declare_parameters_function) (ParameterHandler &),
638  std::unique_ptr<InterfaceClass> (*factory_function) ())
639  {
640  // see if this is the first time we get into this
641  // function and if so initialize the static member variable
642  if (plugins == nullptr)
643  plugins = new std::list<PluginInfo>();
644 
645  // verify that the same name has not previously been
646  // used to register a plugin, since we would then no
647  // longer be able to identify the plugin
648  for (const auto &p : *plugins)
649  {
650  Assert (std::get<0>(p) != name,
651  ExcMessage ("A plugin with name <" + name + "> has "
652  "already been registered!"));
653  (void)p;
654  }
655 
656 
657  // now add one record to the list
658  plugins->emplace_back (name,
659  description,
660  declare_parameters_function,
661  factory_function);
662  }
663 
664 
665 
666  template <typename InterfaceClass>
667  std::string
670  {
671  Assert (plugins != nullptr,
672  ExcMessage ("No plugins registered!?"));
673 
674  // get all names and put them into a data structure that keeps
675  // them sorted
676  std::set<std::string> names;
677  for (typename std::list<PluginInfo>::const_iterator
678  p = plugins->begin();
679  p != plugins->end(); ++p)
680  names.insert (std::get<0>(*p));
681 
682  // now create a pattern from all of these sorted names
683  std::string pattern_of_names;
684  for (const auto &name : names)
685  {
686  if (pattern_of_names.size() > 0)
687  pattern_of_names += "|";
688  pattern_of_names += name;
689  }
690 
691  return pattern_of_names;
692  }
693 
694 
695 
696  template <typename InterfaceClass>
697  std::string
700  {
701  std::string description;
702 
703  // get all names_and_descriptions and put them into a data structure that keeps
704  // them sorted
705  std::map<std::string,std::string> names_and_descriptions;
706  for (typename std::list<PluginInfo>::const_iterator
707  p = plugins->begin();
708  p != plugins->end(); ++p)
709  names_and_descriptions[std::get<0>(*p)] = std::get<1>(*p);
710 
711  // then output it all
712  std::map<std::string,std::string>::const_iterator
713  p = names_and_descriptions.begin();
714  while (true)
715  {
716  // write the name and
717  // description of the
718  // parameter
719  description += "`";
720  description += p->first;
721  description += "': ";
722  description += p->second;
723 
724  // increment the pointer
725  // by one. if we are not
726  // at the end yet then
727  // add an empty line
728  ++p;
729  if (p != names_and_descriptions.end())
730  description += "\n\n";
731  else
732  break;
733  }
734 
735  return description;
736  }
737 
738 
739 
740 
741  template <typename InterfaceClass>
742  void
744  declare_parameters (ParameterHandler &prm)
745  {
746  Assert (plugins != nullptr,
747  ExcMessage ("No postprocessors registered!?"));
748 
749  for (typename std::list<PluginInfo>::const_iterator
750  p = plugins->begin();
751  p != plugins->end(); ++p)
752  (std::get<2>(*p))(prm);
753  }
754 
755 
756 
757  template <typename InterfaceClass>
758  std::unique_ptr<InterfaceClass>
760  create_plugin (const std::string &name,
761  const std::string &documentation)
762  {
763  (void)documentation;
764  Assert (plugins != nullptr,
765  ExcMessage ("No postprocessors registered!?"));
766  AssertThrow (name != "unspecified",
767  ExcMessage(std::string("A plugin must have a name!\n\n"
768  "This function was asked to create a plugin but no name for the "
769  "plugin was provided. This may be due to the fact that you did not "
770  "explicitly specify a name for this plugin in your input file and "
771  "ASPECT does not provide a default for this kind of plugin, for "
772  "example because no generally useful plugin exists. An example "
773  "is that there is no default geometry: You need to explicitly "
774  "provide one in the input file, and it seems like you have not "
775  "done so.\n\n"
776  "To find out which kind of plugin this function tries to create, "
777  "take a look at the backtrace of this error message.\n\n"
778  "The place that called this function also provided as "
779  "additional information this:\n\n"
780  " <")
781  + documentation + ">"));
782 
783  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
784  p != plugins->end(); ++p)
785  if (std::get<0>(*p) == name)
786  {
787  std::unique_ptr<InterfaceClass> i = std::get<3>(*p)();
788  return i;
789  }
790 
791  AssertThrow (false, ExcUnknownPlugin(name));
792  return nullptr;
793  }
794 
795 
796 
797  template <typename InterfaceClass>
798  std::unique_ptr<InterfaceClass>
800  create_plugin (const std::string &name,
801  const std::string &documentation,
802  ParameterHandler &prm)
803  {
804  std::unique_ptr<InterfaceClass> i = create_plugin(name, documentation);
805  i->parse_parameters (prm);
806  return i;
807  }
808 
809 
810 
811  template <typename InterfaceClass>
812  void
814  write_plugin_graph (const std::string &plugin_system_name,
815  std::ostream &output_stream,
816  const std::string &attachment_point)
817  {
818  // first output a graph node for the interface class as the central
819  // hub of this plugin system, plotted as a square.
820  //
821  // we use the typeid name of the interface class to label
822  // nodes within this plugin system, as they are unique among
823  // all other plugin systems
824  output_stream << std::string(typeid(InterfaceClass).name())
825  << " [label=\""
826  << plugin_system_name
827  << "\", height=.8,width=.8,shape=\"rect\",fillcolor=\"lightgreen\"]"
828  << std::endl;
829 
830  // then output the graph nodes for each plugin, with links to the
831  // interface class and, as appropriate, from the SimulatorAccess class
832  //
833  // we would like to establish a predictable order of output here, but
834  // plugins self-register via static global variables, and their
835  // initialization order is not deterministic. consequently, let us
836  // loop over all plugins first and put pointers to them into a
837  // map with deterministic keys. as key, we use the declared name
838  // of the plugin by which it is referred in the .prm file
839  std::map<std::string, typename std::list<PluginInfo>::const_iterator>
840  plugin_map;
841  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
842  p != plugins->end(); ++p)
843  plugin_map[std::get<0>(*p)] = p;
844 
845  // now output the information sorted by the plugin names
846  for (typename std::map<std::string, typename std::list<PluginInfo>::const_iterator>::const_iterator
847  p = plugin_map.begin();
848  p != plugin_map.end(); ++p)
849  {
850  // take the name of the plugin and split it into strings of
851  // 15 characters at most; then combine them
852  // again using \n to make dot/neato show these parts of
853  // the name on separate lines
854  const std::vector<std::string> plugin_label_parts
855  = ::Utilities::break_text_into_lines(p->first, 15);
856  Assert (plugin_label_parts.size()>0, ExcInternalError());
857  std::string plugin_name = plugin_label_parts[0];
858  for (unsigned int i=1; i<plugin_label_parts.size(); ++i)
859  plugin_name += "\\n" + plugin_label_parts[i];
860 
861  // next create a (symbolic) node name for this plugin. because
862  // each plugin corresponds to a particular class, use the mangled
863  // name of the class
864  std::unique_ptr<InterfaceClass> instance (create_plugin (p->first, ""));
865  const std::string node_name = typeid(*instance).name();
866 
867  // then output the whole shebang describing this node
868  output_stream << node_name
869  << " [label=\""
870  << plugin_name
871  << "\", height=.8,width=.8,shape=\"circle\",fillcolor=\"lightblue\"];"
872  << std::endl;
873 
874  // next build connections from this plugin to the
875  // interface class
876  output_stream << node_name
877  << " -> "
878  << std::string(typeid(InterfaceClass).name())
879  << " [len=3, weight=50]"
880  << ';'
881  << std::endl;
882 
883  // finally see if this plugin is derived from
884  // SimulatorAccess; if so, draw an arrow from SimulatorAccess
885  // also to the plugin's name
886  if (dynamic_cast<const SimulatorAccess<2>*>(instance.get()) != nullptr
887  ||
888  dynamic_cast<const SimulatorAccess<3>*>(instance.get()) != nullptr)
889  output_stream << "SimulatorAccess"
890  << " -> "
891  << node_name
892  << " [style=\"dotted\", arrowhead=\"empty\", constraint=false, color=\"gray\", len=20, weight=0.1];"
893  << std::endl;
894  }
895 
896  // as a last step, also draw a connection from the interface class
897  // to the Simulator class, or whatever the calling function indicates
898  // as the attachment point
899  output_stream << std::string(typeid(InterfaceClass).name())
900  << " -> "
901  << attachment_point
902  << " [len=15, weight=50]"
903  << ';'
904  << std::endl;
905 
906  // end it with an empty line to make things easier to
907  // read when looking over stuff visually
908  output_stream << std::endl;
909  }
910 
911 
918  }
919  }
920 }
921 
922 
923 #endif
const std::vector< std::string > & get_active_plugin_names() const
Definition: plugins.h:411
const PluginType & get_matching_active_plugin() const
Definition: plugins.h:381
std::list< std::unique_ptr< InterfaceType > > plugin_objects
Definition: plugins.h:280
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:445
static std::list< PluginInfo > * plugins
Definition: plugins.h:506
std::tuple< std::string, std::string, void(*)(ParameterHandler &), std::unique_ptr< InterfaceClass >(*)()> PluginInfo
Definition: plugins.h:492
void declare_parameters(ParameterHandler &prm)
void update() override
Definition: plugins.h:308
static std::unique_ptr< InterfaceClass > factory()
Definition: plugins.h:463
bool has_matching_active_plugin() const
Definition: plugins.h:368
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:402
std::vector< std::string > plugin_names
Definition: plugins.h:286
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.")