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