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