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