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  {
330  Assert (std::get<0>(p) != name,
331  ExcMessage ("A plugin with name <" + name + "> has "
332  "already been registered!"));
333  (void)p;
334  }
335 
336 
337  // now add one record to the list
338  plugins->emplace_back (name,
339  description,
340  declare_parameters_function,
341  factory_function);
342  }
343 
344 
345 
346  template <typename InterfaceClass>
347  std::string
350  {
351  Assert (plugins != nullptr,
352  ExcMessage ("No plugins registered!?"));
353 
354  // get all names and put them into a data structure that keeps
355  // them sorted
356  std::set<std::string> names;
357  for (typename std::list<PluginInfo>::const_iterator
358  p = plugins->begin();
359  p != plugins->end(); ++p)
360  names.insert (std::get<0>(*p));
361 
362  // now create a pattern from all of these sorted names
363  std::string pattern_of_names;
364  for (const auto &name : names)
365  {
366  if (pattern_of_names.size() > 0)
367  pattern_of_names += "|";
368  pattern_of_names += name;
369  }
370 
371  return pattern_of_names;
372  }
373 
374 
375 
376  template <typename InterfaceClass>
377  std::string
380  {
381  std::string description;
382 
383  // get all names_and_descriptions and put them into a data structure that keeps
384  // them sorted
385  std::map<std::string,std::string> names_and_descriptions;
386  for (typename std::list<PluginInfo>::const_iterator
387  p = plugins->begin();
388  p != plugins->end(); ++p)
389  names_and_descriptions[std::get<0>(*p)] = std::get<1>(*p);
390 
391  // then output it all
392  std::map<std::string,std::string>::const_iterator
393  p = names_and_descriptions.begin();
394  while (true)
395  {
396  // write the name and
397  // description of the
398  // parameter
399  description += "`";
400  description += p->first;
401  description += "': ";
402  description += p->second;
403 
404  // increment the pointer
405  // by one. if we are not
406  // at the end yet then
407  // add an empty line
408  ++p;
409  if (p != names_and_descriptions.end())
410  description += "\n\n";
411  else
412  break;
413  }
414 
415  return description;
416  }
417 
418 
419 
420 
421  template <typename InterfaceClass>
422  void
424  declare_parameters (ParameterHandler &prm)
425  {
426  Assert (plugins != nullptr,
427  ExcMessage ("No postprocessors registered!?"));
428 
429  for (typename std::list<PluginInfo>::const_iterator
430  p = plugins->begin();
431  p != plugins->end(); ++p)
432  (std::get<2>(*p))(prm);
433  }
434 
435 
436 
437  template <typename InterfaceClass>
438  std::unique_ptr<InterfaceClass>
440  create_plugin (const std::string &name,
441  const std::string &documentation)
442  {
443  (void)documentation;
444  Assert (plugins != nullptr,
445  ExcMessage ("No postprocessors registered!?"));
446  AssertThrow (name != "unspecified",
447  ExcMessage(std::string("A plugin must have a name!\n\n"
448  "This function was asked to create a plugin but no name for the "
449  "plugin was provided. This may be due to the fact that you did not "
450  "explicitly specify a name for this plugin in your input file and "
451  "ASPECT does not provide a default for this kind of plugin, for "
452  "example because no generally useful plugin exists. An example "
453  "is that there is no default geometry: You need to explicitly "
454  "provide one in the input file, and it seems like you have not "
455  "done so.\n\n"
456  "To find out which kind of plugin this function tries to create, "
457  "take a look at the backtrace of this error message.\n\n"
458  "The place that called this function also provided as "
459  "additional information this:\n\n"
460  " <")
461  + documentation + ">"));
462 
463  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
464  p != plugins->end(); ++p)
465  if (std::get<0>(*p) == name)
466  {
467  std::unique_ptr<InterfaceClass> i = std::get<3>(*p)();
468  return i;
469  }
470 
471  AssertThrow (false, ExcUnknownPlugin(name));
472  return nullptr;
473  }
474 
475 
476 
477  template <typename InterfaceClass>
478  std::unique_ptr<InterfaceClass>
480  create_plugin (const std::string &name,
481  const std::string &documentation,
482  ParameterHandler &prm)
483  {
484  std::unique_ptr<InterfaceClass> i = create_plugin(name, documentation);
485  i->parse_parameters (prm);
486  return i;
487  }
488 
489 
490 
491  template <typename InterfaceClass>
492  void
494  write_plugin_graph (const std::string &plugin_system_name,
495  std::ostream &output_stream,
496  const std::string &attachment_point)
497  {
498  // first output a graph node for the interface class as the central
499  // hub of this plugin system, plotted as a square.
500  //
501  // we use the typeid name of the interface class to label
502  // nodes within this plugin system, as they are unique among
503  // all other plugin systems
504  output_stream << std::string(typeid(InterfaceClass).name())
505  << " [label=\""
506  << plugin_system_name
507  << "\", height=.8,width=.8,shape=\"rect\",fillcolor=\"green\"]"
508  << std::endl;
509 
510  // then output the graph nodes for each plugin, with links to the
511  // interface class and, as appropriate, from the SimulatorAccess class
512  //
513  // we would like to establish a predictable order of output here, but
514  // plugins self-register via static global variables, and their
515  // initialization order is not deterministic. consequently, let us
516  // loop over all plugins first and put pointers to them into a
517  // map with deterministic keys. as key, we use the declared name
518  // of the plugin by which it is referred in the .prm file
519  std::map<std::string, typename std::list<PluginInfo>::const_iterator>
520  plugin_map;
521  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
522  p != plugins->end(); ++p)
523  plugin_map[std::get<0>(*p)] = p;
524 
525  // now output the information sorted by the plugin names
526  for (typename std::map<std::string, typename std::list<PluginInfo>::const_iterator>::const_iterator
527  p = plugin_map.begin();
528  p != plugin_map.end(); ++p)
529  {
530  // take the name of the plugin and split it into strings of
531  // 15 characters at most; then combine them
532  // again using \n to make dot/neato show these parts of
533  // the name on separate lines
534  const std::vector<std::string> plugin_label_parts
535  = ::Utilities::break_text_into_lines(p->first, 15);
536  Assert (plugin_label_parts.size()>0, ExcInternalError());
537  std::string plugin_name = plugin_label_parts[0];
538  for (unsigned int i=1; i<plugin_label_parts.size(); ++i)
539  plugin_name += "\\n" + plugin_label_parts[i];
540 
541  // next create a (symbolic) node name for this plugin. because
542  // each plugin corresponds to a particular class, use the mangled
543  // name of the class
544  std::unique_ptr<InterfaceClass> instance (create_plugin (p->first, ""));
545  const std::string node_name = typeid(*instance).name();
546 
547  // then output the whole shebang describing this node
548  output_stream << node_name
549  << " [label=\""
550  << plugin_name
551  << "\", height=.8,width=.8,shape=\"circle\",fillcolor=\"lightblue\"];"
552  << std::endl;
553 
554  // next build connections from this plugin to the
555  // interface class
556  output_stream << node_name
557  << " -> "
558  << std::string(typeid(InterfaceClass).name())
559  << " [len=3, weight=50]"
560  << ';'
561  << std::endl;
562 
563  // finally see if this plugin is derived from
564  // SimulatorAccess; if so, draw an arrow from SimulatorAccess
565  // also to the plugin's name
566  if (dynamic_cast<const SimulatorAccess<2>*>(instance.get()) != nullptr
567  ||
568  dynamic_cast<const SimulatorAccess<3>*>(instance.get()) != nullptr)
569  output_stream << "SimulatorAccess"
570  << " -> "
571  << node_name
572  << " [style=\"dotted\", arrowhead=\"empty\", constraint=false, color=\"gray\", len=20, weight=0.1];"
573  << std::endl;
574  }
575 
576  // as a last step, also draw a connection from the interface class
577  // to the Simulator class, or whatever the calling function indicates
578  // as the attachment point
579  output_stream << std::string(typeid(InterfaceClass).name())
580  << " -> "
581  << attachment_point
582  << " [len=15, weight=50]"
583  << ';'
584  << std::endl;
585 
586  // end it with an empty line to make things easier to
587  // read when looking over stuff visually
588  output_stream << std::endl;
589  }
590  }
591  }
592 }
593 
594 
595 #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
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.")