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<std::is_base_of<PluginType,TestType>::value>::type>
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<std::is_base_of<PluginType,TestType>::value>::type>
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 internal
116  {
121  namespace Plugins
122  {
123  using namespace dealii;
124 
135  template <typename InterfaceClass,
136  typename ModelClass>
138  {
144  RegisterHelper (void (*register_function) (const std::string &,
145  const std::string &,
146  void ( *)(ParameterHandler &),
147  std::unique_ptr<InterfaceClass> ( *)()),
148  const char *name,
149  const char *description)
150  {
151  register_function (name,
152  description,
154  &factory);
155  }
156 
161  static
162  std::unique_ptr<InterfaceClass> factory ()
163  {
164  return std::make_unique<ModelClass>();
165  }
166  };
167 
168 
173  template <typename InterfaceClass>
174  struct PluginList
175  {
187  using PluginInfo
188  = std::tuple<std::string,
189  std::string,
190  void ( *) (ParameterHandler &),
191  std::unique_ptr<InterfaceClass>( *) ()>;
192 
205  static std::list<PluginInfo> *plugins;
206 
210  ~PluginList ();
211 
217  static
218  void register_plugin (const std::string &name,
219  const std::string &description,
220  void (*declare_parameters_function) (ParameterHandler &),
221  std::unique_ptr<InterfaceClass> (*factory_function) ());
222 
230  static
231  std::string get_pattern_of_names ();
232 
240  static
241  std::string get_description_string ();
242 
246  static
247  void declare_parameters (ParameterHandler &prm);
248 
258  static
259  std::unique_ptr<InterfaceClass>
260  create_plugin (const std::string &name,
261  const std::string &documentation);
262 
273  static
274  std::unique_ptr<InterfaceClass>
275  create_plugin (const std::string &name,
276  const std::string &documentation,
277  ParameterHandler &prm);
278 
300  static
301  void
302  write_plugin_graph (const std::string &plugin_system_name,
303  std::ostream &output_stream,
304  const std::string &attachment_point = "Simulator");
305 
309  DeclException1 (ExcUnknownPlugin,
310  std::string,
311  << "Can't create a plugin of name <" << arg1
312  << "> because such a plugin hasn't been declared.");
313  };
314 
315 
316  /* ------------------------ template and inline functions --------------------- */
317 
318  template <typename InterfaceClass>
321  {
322  // if any plugins have been registered, then delete
323  // the list
324  if (plugins != nullptr)
325  delete plugins;
326  plugins = nullptr;
327  }
328 
329 
330 
331  template <typename InterfaceClass>
332  void
334  register_plugin (const std::string &name,
335  const std::string &description,
336  void (*declare_parameters_function) (ParameterHandler &),
337  std::unique_ptr<InterfaceClass> (*factory_function) ())
338  {
339  // see if this is the first time we get into this
340  // function and if so initialize the static member variable
341  if (plugins == nullptr)
342  plugins = new std::list<PluginInfo>();
343 
344  // verify that the same name has not previously been
345  // used to register a plugin, since we would then no
346  // longer be able to identify the plugin
347  for (const auto &p : *plugins)
348  {
349  Assert (std::get<0>(p) != name,
350  ExcMessage ("A plugin with name <" + name + "> has "
351  "already been registered!"));
352  (void)p;
353  }
354 
355 
356  // now add one record to the list
357  plugins->emplace_back (name,
358  description,
359  declare_parameters_function,
360  factory_function);
361  }
362 
363 
364 
365  template <typename InterfaceClass>
366  std::string
369  {
370  Assert (plugins != nullptr,
371  ExcMessage ("No plugins registered!?"));
372 
373  // get all names and put them into a data structure that keeps
374  // them sorted
375  std::set<std::string> names;
376  for (typename std::list<PluginInfo>::const_iterator
377  p = plugins->begin();
378  p != plugins->end(); ++p)
379  names.insert (std::get<0>(*p));
380 
381  // now create a pattern from all of these sorted names
382  std::string pattern_of_names;
383  for (const auto &name : names)
384  {
385  if (pattern_of_names.size() > 0)
386  pattern_of_names += "|";
387  pattern_of_names += name;
388  }
389 
390  return pattern_of_names;
391  }
392 
393 
394 
395  template <typename InterfaceClass>
396  std::string
399  {
400  std::string description;
401 
402  // get all names_and_descriptions and put them into a data structure that keeps
403  // them sorted
404  std::map<std::string,std::string> names_and_descriptions;
405  for (typename std::list<PluginInfo>::const_iterator
406  p = plugins->begin();
407  p != plugins->end(); ++p)
408  names_and_descriptions[std::get<0>(*p)] = std::get<1>(*p);
409 
410  // then output it all
411  std::map<std::string,std::string>::const_iterator
412  p = names_and_descriptions.begin();
413  while (true)
414  {
415  // write the name and
416  // description of the
417  // parameter
418  description += "`";
419  description += p->first;
420  description += "': ";
421  description += p->second;
422 
423  // increment the pointer
424  // by one. if we are not
425  // at the end yet then
426  // add an empty line
427  ++p;
428  if (p != names_and_descriptions.end())
429  description += "\n\n";
430  else
431  break;
432  }
433 
434  return description;
435  }
436 
437 
438 
439 
440  template <typename InterfaceClass>
441  void
443  declare_parameters (ParameterHandler &prm)
444  {
445  Assert (plugins != nullptr,
446  ExcMessage ("No postprocessors registered!?"));
447 
448  for (typename std::list<PluginInfo>::const_iterator
449  p = plugins->begin();
450  p != plugins->end(); ++p)
451  (std::get<2>(*p))(prm);
452  }
453 
454 
455 
456  template <typename InterfaceClass>
457  std::unique_ptr<InterfaceClass>
459  create_plugin (const std::string &name,
460  const std::string &documentation)
461  {
462  (void)documentation;
463  Assert (plugins != nullptr,
464  ExcMessage ("No postprocessors registered!?"));
465  AssertThrow (name != "unspecified",
466  ExcMessage(std::string("A plugin must have a name!\n\n"
467  "This function was asked to create a plugin but no name for the "
468  "plugin was provided. This may be due to the fact that you did not "
469  "explicitly specify a name for this plugin in your input file and "
470  "ASPECT does not provide a default for this kind of plugin, for "
471  "example because no generally useful plugin exists. An example "
472  "is that there is no default geometry: You need to explicitly "
473  "provide one in the input file, and it seems like you have not "
474  "done so.\n\n"
475  "To find out which kind of plugin this function tries to create, "
476  "take a look at the backtrace of this error message.\n\n"
477  "The place that called this function also provided as "
478  "additional information this:\n\n"
479  " <")
480  + documentation + ">"));
481 
482  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
483  p != plugins->end(); ++p)
484  if (std::get<0>(*p) == name)
485  {
486  std::unique_ptr<InterfaceClass> i = std::get<3>(*p)();
487  return i;
488  }
489 
490  AssertThrow (false, ExcUnknownPlugin(name));
491  return nullptr;
492  }
493 
494 
495 
496  template <typename InterfaceClass>
497  std::unique_ptr<InterfaceClass>
499  create_plugin (const std::string &name,
500  const std::string &documentation,
501  ParameterHandler &prm)
502  {
503  std::unique_ptr<InterfaceClass> i = create_plugin(name, documentation);
504  i->parse_parameters (prm);
505  return i;
506  }
507 
508 
509 
510  template <typename InterfaceClass>
511  void
513  write_plugin_graph (const std::string &plugin_system_name,
514  std::ostream &output_stream,
515  const std::string &attachment_point)
516  {
517  // first output a graph node for the interface class as the central
518  // hub of this plugin system, plotted as a square.
519  //
520  // we use the typeid name of the interface class to label
521  // nodes within this plugin system, as they are unique among
522  // all other plugin systems
523  output_stream << std::string(typeid(InterfaceClass).name())
524  << " [label=\""
525  << plugin_system_name
526  << "\", height=.8,width=.8,shape=\"rect\",fillcolor=\"green\"]"
527  << std::endl;
528 
529  // then output the graph nodes for each plugin, with links to the
530  // interface class and, as appropriate, from the SimulatorAccess class
531  //
532  // we would like to establish a predictable order of output here, but
533  // plugins self-register via static global variables, and their
534  // initialization order is not deterministic. consequently, let us
535  // loop over all plugins first and put pointers to them into a
536  // map with deterministic keys. as key, we use the declared name
537  // of the plugin by which it is referred in the .prm file
538  std::map<std::string, typename std::list<PluginInfo>::const_iterator>
539  plugin_map;
540  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
541  p != plugins->end(); ++p)
542  plugin_map[std::get<0>(*p)] = p;
543 
544  // now output the information sorted by the plugin names
545  for (typename std::map<std::string, typename std::list<PluginInfo>::const_iterator>::const_iterator
546  p = plugin_map.begin();
547  p != plugin_map.end(); ++p)
548  {
549  // take the name of the plugin and split it into strings of
550  // 15 characters at most; then combine them
551  // again using \n to make dot/neato show these parts of
552  // the name on separate lines
553  const std::vector<std::string> plugin_label_parts
554  = ::Utilities::break_text_into_lines(p->first, 15);
555  Assert (plugin_label_parts.size()>0, ExcInternalError());
556  std::string plugin_name = plugin_label_parts[0];
557  for (unsigned int i=1; i<plugin_label_parts.size(); ++i)
558  plugin_name += "\\n" + plugin_label_parts[i];
559 
560  // next create a (symbolic) node name for this plugin. because
561  // each plugin corresponds to a particular class, use the mangled
562  // name of the class
563  std::unique_ptr<InterfaceClass> instance (create_plugin (p->first, ""));
564  const std::string node_name = typeid(*instance).name();
565 
566  // then output the whole shebang describing this node
567  output_stream << node_name
568  << " [label=\""
569  << plugin_name
570  << "\", height=.8,width=.8,shape=\"circle\",fillcolor=\"lightblue\"];"
571  << std::endl;
572 
573  // next build connections from this plugin to the
574  // interface class
575  output_stream << node_name
576  << " -> "
577  << std::string(typeid(InterfaceClass).name())
578  << " [len=3, weight=50]"
579  << ';'
580  << std::endl;
581 
582  // finally see if this plugin is derived from
583  // SimulatorAccess; if so, draw an arrow from SimulatorAccess
584  // also to the plugin's name
585  if (dynamic_cast<const SimulatorAccess<2>*>(instance.get()) != nullptr
586  ||
587  dynamic_cast<const SimulatorAccess<3>*>(instance.get()) != nullptr)
588  output_stream << "SimulatorAccess"
589  << " -> "
590  << node_name
591  << " [style=\"dotted\", arrowhead=\"empty\", constraint=false, color=\"gray\", len=20, weight=0.1];"
592  << std::endl;
593  }
594 
595  // as a last step, also draw a connection from the interface class
596  // to the Simulator class, or whatever the calling function indicates
597  // as the attachment point
598  output_stream << std::string(typeid(InterfaceClass).name())
599  << " -> "
600  << attachment_point
601  << " [len=15, weight=50]"
602  << ';'
603  << std::endl;
604 
605  // end it with an empty line to make things easier to
606  // read when looking over stuff visually
607  output_stream << std::endl;
608  }
609  }
610  }
611 }
612 
613 
614 #endif
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:144
static std::list< PluginInfo > * plugins
Definition: plugins.h:205
std::tuple< std::string, std::string, void(*)(ParameterHandler &), std::unique_ptr< InterfaceClass >(*)()> PluginInfo
Definition: plugins.h:191
void declare_parameters(ParameterHandler &prm)
static std::unique_ptr< InterfaceClass > factory()
Definition: plugins.h:162
bool plugin_type_matches(const PluginType &object)
Definition: plugins.h:73
Definition: compat.h:42
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.")