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 
41 
42 namespace aspect
43 {
44  template <int dim> class SimulatorAccess;
45 
46  namespace Plugins
47  {
48  using namespace dealii;
49 
60  template <typename TestType, typename PluginType>
61  inline
62  bool
63  plugin_type_matches (const PluginType &object)
64  {
65  return (dynamic_cast<const TestType *> (&object) != nullptr);
66  }
67 
77  template <typename TestType, typename PluginType>
78  inline
79  TestType &
80  get_plugin_as_type (PluginType &object)
81  {
82  AssertThrow(plugin_type_matches<TestType>(object),
83  ExcMessage("You have requested to convert a plugin of type <"
84  + boost::core::demangle(typeid(PluginType).name())
85  + "> into type <"
86  + boost::core::demangle(typeid(TestType).name()) +
87  ">, but this cast cannot be performed."));
88 
89  // We can safely dereference the pointer, because we checked above that
90  // the object is actually of type TestType, and so the result
91  // is not a nullptr.
92  return *dynamic_cast<TestType *> (&object);
93  }
94  }
95 
96  namespace internal
97  {
102  namespace Plugins
103  {
104  using namespace dealii;
105 
116  template <typename InterfaceClass,
117  typename ModelClass>
119  {
125  RegisterHelper (void (*register_function) (const std::string &,
126  const std::string &,
127  void ( *)(ParameterHandler &),
128  std::unique_ptr<InterfaceClass> ( *)()),
129  const char *name,
130  const char *description)
131  {
132  register_function (name,
133  description,
135  &factory);
136  }
137 
142  static
143  std::unique_ptr<InterfaceClass> factory ()
144  {
145  return std::make_unique<ModelClass>();
146  }
147  };
148 
149 
154  template <typename InterfaceClass>
155  struct PluginList
156  {
168  using PluginInfo
169  = std::tuple<std::string,
170  std::string,
171  void ( *) (ParameterHandler &),
172  std::unique_ptr<InterfaceClass>( *) ()>;
173 
186  static std::list<PluginInfo> *plugins;
187 
191  ~PluginList ();
192 
198  static
199  void register_plugin (const std::string &name,
200  const std::string &description,
201  void (*declare_parameters_function) (ParameterHandler &),
202  std::unique_ptr<InterfaceClass> (*factory_function) ());
203 
211  static
212  std::string get_pattern_of_names ();
213 
221  static
222  std::string get_description_string ();
223 
227  static
228  void declare_parameters (ParameterHandler &prm);
229 
239  static
240  std::unique_ptr<InterfaceClass>
241  create_plugin (const std::string &name,
242  const std::string &documentation);
243 
254  static
255  std::unique_ptr<InterfaceClass>
256  create_plugin (const std::string &name,
257  const std::string &documentation,
258  ParameterHandler &prm);
259 
281  static
282  void
283  write_plugin_graph (const std::string &plugin_system_name,
284  std::ostream &output_stream,
285  const std::string &attachment_point = "Simulator");
286 
290  DeclException1 (ExcUnknownPlugin,
291  std::string,
292  << "Can't create a plugin of name <" << arg1
293  << "> because such a plugin hasn't been declared.");
294  };
295 
296 
297  /* ------------------------ template and inline functions --------------------- */
298 
299  template <typename InterfaceClass>
302  {
303  // if any plugins have been registered, then delete
304  // the list
305  if (plugins != nullptr)
306  delete plugins;
307  plugins = nullptr;
308  }
309 
310 
311 
312  template <typename InterfaceClass>
313  void
315  register_plugin (const std::string &name,
316  const std::string &description,
317  void (*declare_parameters_function) (ParameterHandler &),
318  std::unique_ptr<InterfaceClass> (*factory_function) ())
319  {
320  // see if this is the first time we get into this
321  // function and if so initialize the static member variable
322  if (plugins == nullptr)
323  plugins = new std::list<PluginInfo>();
324 
325  // verify that the same name has not previously been
326  // used to register a plugin, since we would then no
327  // longer be able to identify the plugin
328  for (const auto &p : *plugins)
329  Assert (std::get<0>(p) != name,
330  ExcMessage ("A plugin with name <" + name + "> has "
331  "already been registered!"));
332 
333  // now add one record to the list
334  plugins->emplace_back (name,
335  description,
336  declare_parameters_function,
337  factory_function);
338  }
339 
340 
341 
342  template <typename InterfaceClass>
343  std::string
346  {
347  Assert (plugins != nullptr,
348  ExcMessage ("No plugins registered!?"));
349 
350  // get all names and put them into a data structure that keeps
351  // them sorted
352  std::set<std::string> names;
353  for (typename std::list<PluginInfo>::const_iterator
354  p = plugins->begin();
355  p != plugins->end(); ++p)
356  names.insert (std::get<0>(*p));
357 
358  // now create a pattern from all of these sorted names
359  std::string pattern_of_names;
360  for (const auto &name : names)
361  {
362  if (pattern_of_names.size() > 0)
363  pattern_of_names += "|";
364  pattern_of_names += name;
365  }
366 
367  return pattern_of_names;
368  }
369 
370 
371 
372  template <typename InterfaceClass>
373  std::string
376  {
377  std::string description;
378 
379  // get all names_and_descriptions and put them into a data structure that keeps
380  // them sorted
381  std::map<std::string,std::string> names_and_descriptions;
382  for (typename std::list<PluginInfo>::const_iterator
383  p = plugins->begin();
384  p != plugins->end(); ++p)
385  names_and_descriptions[std::get<0>(*p)] = std::get<1>(*p);
386 
387  // then output it all
388  std::map<std::string,std::string>::const_iterator
389  p = names_and_descriptions.begin();
390  while (true)
391  {
392  // write the name and
393  // description of the
394  // parameter
395  description += "`";
396  description += p->first;
397  description += "': ";
398  description += p->second;
399 
400  // increment the pointer
401  // by one. if we are not
402  // at the end yet then
403  // add an empty line
404  ++p;
405  if (p != names_and_descriptions.end())
406  description += "\n\n";
407  else
408  break;
409  }
410 
411  return description;
412  }
413 
414 
415 
416 
417  template <typename InterfaceClass>
418  void
420  declare_parameters (ParameterHandler &prm)
421  {
422  Assert (plugins != nullptr,
423  ExcMessage ("No postprocessors registered!?"));
424 
425  for (typename std::list<PluginInfo>::const_iterator
426  p = plugins->begin();
427  p != plugins->end(); ++p)
428  (std::get<2>(*p))(prm);
429  }
430 
431 
432 
433  template <typename InterfaceClass>
434  std::unique_ptr<InterfaceClass>
436  create_plugin (const std::string &name,
437  const std::string &documentation)
438  {
439  (void)documentation;
440  Assert (plugins != nullptr,
441  ExcMessage ("No postprocessors registered!?"));
442  AssertThrow (name != "unspecified",
443  ExcMessage(std::string("A plugin must have a name!\n\n"
444  "This function was asked to create a plugin but no name for the "
445  "plugin was provided. This may be due to the fact that you did not "
446  "explicitly specify a name for this plugin in your input file and "
447  "ASPECT does not provide a default for this kind of plugin, for "
448  "example because no generally useful plugin exists. An example "
449  "is that there is no default geometry: You need to explicitly "
450  "provide one in the input file, and it seems like you have not "
451  "done so.\n\n"
452  "To find out which kind of plugin this function tries to create, "
453  "take a look at the backtrace of this error message.\n\n"
454  "The place that called this function also provided as "
455  "additional information this:\n\n"
456  " <")
457  + documentation + ">"));
458 
459  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
460  p != plugins->end(); ++p)
461  if (std::get<0>(*p) == name)
462  {
463  std::unique_ptr<InterfaceClass> i = std::get<3>(*p)();
464  return i;
465  }
466 
467  AssertThrow (false, ExcUnknownPlugin(name));
468  return nullptr;
469  }
470 
471 
472 
473  template <typename InterfaceClass>
474  std::unique_ptr<InterfaceClass>
476  create_plugin (const std::string &name,
477  const std::string &documentation,
478  ParameterHandler &prm)
479  {
480  std::unique_ptr<InterfaceClass> i = create_plugin(name, documentation);
481  i->parse_parameters (prm);
482  return i;
483  }
484 
485 
486 
487  template <typename InterfaceClass>
488  void
490  write_plugin_graph (const std::string &plugin_system_name,
491  std::ostream &output_stream,
492  const std::string &attachment_point)
493  {
494  // first output a graph node for the interface class as the central
495  // hub of this plugin system, plotted as a square.
496  //
497  // we use the typeid name of the interface class to label
498  // nodes within this plugin system, as they are unique among
499  // all other plugin systems
500  output_stream << std::string(typeid(InterfaceClass).name())
501  << " [label=\""
502  << plugin_system_name
503  << "\", height=.8,width=.8,shape=\"rect\",fillcolor=\"green\"]"
504  << std::endl;
505 
506  // then output the graph nodes for each plugin, with links to the
507  // interface class and, as appropriate, from the SimulatorAccess class
508  //
509  // we would like to establish a predictable order of output here, but
510  // plugins self-register via static global variables, and their
511  // initialization order is not deterministic. consequently, let us
512  // loop over all plugins first and put pointers to them into a
513  // map with deterministic keys. as key, we use the declared name
514  // of the plugin by which it is referred in the .prm file
515  std::map<std::string, typename std::list<PluginInfo>::const_iterator>
516  plugin_map;
517  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
518  p != plugins->end(); ++p)
519  plugin_map[std::get<0>(*p)] = p;
520 
521  // now output the information sorted by the plugin names
522  for (typename std::map<std::string, typename std::list<PluginInfo>::const_iterator>::const_iterator
523  p = plugin_map.begin();
524  p != plugin_map.end(); ++p)
525  {
526  // take the name of the plugin and split it into strings of
527  // 15 characters at most; then combine them
528  // again using \n to make dot/neato show these parts of
529  // the name on separate lines
530  const std::vector<std::string> plugin_label_parts
531  = ::Utilities::break_text_into_lines(p->first, 15);
532  Assert (plugin_label_parts.size()>0, ExcInternalError());
533  std::string plugin_name = plugin_label_parts[0];
534  for (unsigned int i=1; i<plugin_label_parts.size(); ++i)
535  plugin_name += "\\n" + plugin_label_parts[i];
536 
537  // next create a (symbolic) node name for this plugin. because
538  // each plugin corresponds to a particular class, use the mangled
539  // name of the class
540  std::unique_ptr<InterfaceClass> instance (create_plugin (p->first, ""));
541  const std::string node_name = typeid(*instance).name();
542 
543  // then output the whole shebang describing this node
544  output_stream << node_name
545  << " [label=\""
546  << plugin_name
547  << "\", height=.8,width=.8,shape=\"circle\",fillcolor=\"lightblue\"];"
548  << std::endl;
549 
550  // next build connections from this plugin to the
551  // interface class
552  output_stream << node_name
553  << " -> "
554  << std::string(typeid(InterfaceClass).name())
555  << " [len=3, weight=50]"
556  << ';'
557  << std::endl;
558 
559  // finally see if this plugin is derived from
560  // SimulatorAccess; if so, draw an arrow from SimulatorAccess
561  // also to the plugin's name
562  if (dynamic_cast<const SimulatorAccess<2>*>(instance.get()) != nullptr
563  ||
564  dynamic_cast<const SimulatorAccess<3>*>(instance.get()) != nullptr)
565  output_stream << "SimulatorAccess"
566  << " -> "
567  << node_name
568  << " [style=\"dotted\", arrowhead=\"empty\", constraint=false, color=\"gray\", len=20, weight=0.1];"
569  << std::endl;
570  }
571 
572  // as a last step, also draw a connection from the interface class
573  // to the Simulator class, or whatever the calling function indicates
574  // as the attachment point
575  output_stream << std::string(typeid(InterfaceClass).name())
576  << " -> "
577  << attachment_point
578  << " [len=15, weight=50]"
579  << ';'
580  << std::endl;
581 
582  // end it with an empty line to make things easier to
583  // read when looking over stuff visually
584  output_stream << std::endl;
585  }
586  }
587  }
588 }
589 
590 
591 #endif
bool plugin_type_matches(const PluginType &object)
Definition: plugins.h:63
void write_plugin_graph(std::ostream &output_stream)
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:125
static std::list< PluginInfo > * plugins
Definition: plugins.h:186
TestType & get_plugin_as_type(PluginType &object)
Definition: plugins.h:80
std::tuple< std::string, std::string, void(*)(ParameterHandler &), std::unique_ptr< InterfaceClass >(*)()> PluginInfo
Definition: plugins.h:172
void declare_parameters(ParameterHandler &prm)
static std::unique_ptr< InterfaceClass > factory()
Definition: plugins.h:143
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.")