ASPECT
plugins.h
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2020 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>
29 #include <tuple>
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  InterfaceClass * ( *)()),
129  const char *name,
130  const char *description)
131  {
132  register_function (name,
133  description,
135  &factory);
136  }
137 
142  static
143  InterfaceClass *factory ()
144  {
145  return new ModelClass();
146  }
147  };
148 
149 
154  template <typename InterfaceClass>
155  struct PluginList
156  {
166  using PluginInfo
167  = std::tuple<std::string,
168  std::string,
169  void ( *) (ParameterHandler &),
170  InterfaceClass *( *) ()>;
171 
184  static std::list<PluginInfo> *plugins;
185 
189  ~PluginList ();
190 
196  static
197  void register_plugin (const std::string &name,
198  const std::string &description,
199  void (*declare_parameters_function) (ParameterHandler &),
200  InterfaceClass * (*factory_function) ());
201 
209  static
210  std::string get_pattern_of_names ();
211 
219  static
220  std::string get_description_string ();
221 
225  static
227 
237  static
238  InterfaceClass *
239  create_plugin (const std::string &name,
240  const std::string &documentation);
241 
252  static
253  InterfaceClass *
254  create_plugin (const std::string &name,
255  const std::string &documentation,
256  ParameterHandler &prm);
257 
279  static
280  void
281  write_plugin_graph (const std::string &plugin_system_name,
282  std::ostream &output_stream,
283  const std::string &attachment_point = "Simulator");
284 
288  DeclException1 (ExcUnknownPlugin,
289  std::string,
290  << "Can't create a plugin of name <" << arg1
291  << "> because such a plugin hasn't been declared.");
292  };
293 
294 
295  /* ------------------------ template and inline functions --------------------- */
296 
297  template <typename InterfaceClass>
300  {
301  // if any plugins have been registered, then delete
302  // the list
303  if (plugins != nullptr)
304  delete plugins;
305  plugins = nullptr;
306  }
307 
308 
309 
310  template <typename InterfaceClass>
311  void
313  register_plugin (const std::string &name,
314  const std::string &description,
315  void (*declare_parameters_function) (ParameterHandler &),
316  InterfaceClass * (*factory_function) ())
317  {
318  // see if this is the first time we get into this
319  // function and if so initialize the static member variable
320  if (plugins == nullptr)
321  plugins = new std::list<PluginInfo>();
322 
323  // verify that the same name has not previously been
324  // used to register a plugin, since we would then no
325  // longer be able to identify the plugin
326  for (typename std::list<PluginInfo>::const_iterator
327  p = plugins->begin();
328  p != plugins->end(); ++p)
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->push_back (PluginInfo(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 (typename std::set<std::string>::const_iterator
361  p = names.begin();
362  p != names.end(); ++p)
363  {
364  if (pattern_of_names.size() > 0)
365  pattern_of_names += "|";
366  pattern_of_names += *p;
367  }
368 
369  return pattern_of_names;
370  }
371 
372 
373 
374  template <typename InterfaceClass>
375  std::string
378  {
379  std::string description;
380 
381  // get all names_and_descriptions and put them into a data structure that keeps
382  // them sorted
383  std::map<std::string,std::string> names_and_descriptions;
384  for (typename std::list<PluginInfo>::const_iterator
385  p = plugins->begin();
386  p != plugins->end(); ++p)
387  names_and_descriptions[std::get<0>(*p)] = std::get<1>(*p);;
388 
389  // then output it all
390  std::map<std::string,std::string>::const_iterator
391  p = names_and_descriptions.begin();
392  while (true)
393  {
394  // write the name and
395  // description of the
396  // parameter
397  description += "`";
398  description += p->first;
399  description += "': ";
400  description += p->second;
401 
402  // increment the pointer
403  // by one. if we are not
404  // at the end yet then
405  // add an empty line
406  ++p;
407  if (p != names_and_descriptions.end())
408  description += "\n\n";
409  else
410  break;
411  }
412 
413  return description;
414  }
415 
416 
417 
418 
419  template <typename InterfaceClass>
420  void
423  {
424  Assert (plugins != nullptr,
425  ExcMessage ("No postprocessors registered!?"));
426 
427  for (typename std::list<PluginInfo>::const_iterator
428  p = plugins->begin();
429  p != plugins->end(); ++p)
430  (std::get<2>(*p))(prm);
431  }
432 
433 
434 
435  template <typename InterfaceClass>
436  InterfaceClass *
438  create_plugin (const std::string &name,
439  const std::string &documentation)
440  {
441  (void)documentation;
442  Assert (plugins != nullptr,
443  ExcMessage ("No postprocessors registered!?"));
444  AssertThrow (name != "unspecified",
445  ExcMessage(std::string("A plugin must have a name!\n\n"
446  "This function was asked to create a plugin but no name for the "
447  "plugin was provided. This may be due to the fact that you did not "
448  "explicitly specify a name for this plugin in your input file and "
449  "ASPECT does not provide a default for this kind of plugin, for "
450  "example because no generally useful plugin exists. An example "
451  "is that there is no default geometry: You need to explicitly "
452  "provide one in the input file, and it seems like you have not "
453  "done so.\n\n"
454  "To find out which kind of plugin this function tries to create, "
455  "take a look at the backtrace of this error message.\n\n"
456  "The place that called this function also provided as "
457  "additional information this:\n\n"
458  " <")
459  + documentation + ">"));
460 
461  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
462  p != plugins->end(); ++p)
463  if (std::get<0>(*p) == name)
464  {
465  InterfaceClass *i = std::get<3>(*p)();
466  return i;
467  }
468 
469  AssertThrow (false, ExcUnknownPlugin(name));
470  return nullptr;
471  }
472 
473 
474 
475  template <typename InterfaceClass>
476  InterfaceClass *
478  create_plugin (const std::string &name,
479  const std::string &documentation,
480  ParameterHandler &prm)
481  {
482  InterfaceClass *i = create_plugin(name, documentation);
483  i->parse_parameters (prm);
484  return i;
485  }
486 
487 
488 
489  template <typename InterfaceClass>
490  void
492  write_plugin_graph (const std::string &plugin_system_name,
493  std::ostream &output_stream,
494  const std::string &attachment_point)
495  {
496  // first output a graph node for the interface class as the central
497  // hub of this plugin system, plotted as a square.
498  //
499  // we use the typeid name of the interface class to label
500  // nodes within this plugin system, as they are unique among
501  // all other plugin systems
502  output_stream << std::string(typeid(InterfaceClass).name())
503  << " [label=\""
504  << plugin_system_name
505  << "\", height=.8,width=.8,shape=\"rect\",fillcolor=\"green\"]"
506  << std::endl;
507 
508  // then output the graph nodes for each plugin, with links to the
509  // interface class and, as appropriate, from the SimulatorAccess class
510  //
511  // we would like to establish a predictable order of output here, but
512  // plugins self-register via static global variables, and their
513  // initialization order is not deterministic. consequently, let us
514  // loop over all plugins first and put pointers to them into a
515  // map with deterministic keys. as key, we use the declared name
516  // of the plugin by which it is referred in the .prm file
517  std::map<std::string, typename std::list<PluginInfo>::const_iterator>
518  plugin_map;
519  for (typename std::list<PluginInfo>::const_iterator p = plugins->begin();
520  p != plugins->end(); ++p)
521  plugin_map[std::get<0>(*p)] = p;
522 
523  // now output the information sorted by the plugin names
524  for (typename std::map<std::string, typename std::list<PluginInfo>::const_iterator>::const_iterator
525  p = plugin_map.begin();
526  p != plugin_map.end(); ++p)
527  {
528  // take the name of the plugin and split it into strings of
529  // 15 characters at most; then combine them
530  // again using \n to make dot/neato show these parts of
531  // the name on separate lines
532  const std::vector<std::string> plugin_label_parts
533  = ::Utilities::break_text_into_lines(p->first, 15);
534  Assert (plugin_label_parts.size()>0, ExcInternalError());
535  std::string plugin_name = plugin_label_parts[0];
536  for (unsigned int i=1; i<plugin_label_parts.size(); ++i)
537  plugin_name += "\\n" + plugin_label_parts[i];
538 
539  // next create a (symbolic) node name for this plugin. because
540  // each plugin corresponds to a particular class, use the mangled
541  // name of the class
542  std::unique_ptr<InterfaceClass> instance (create_plugin (p->first, ""));
543  const std::string node_name = typeid(*instance).name();
544 
545  // then output the whole shebang describing this node
546  output_stream << node_name
547  << " [label=\""
548  << plugin_name
549  << "\", height=.8,width=.8,shape=\"circle\",fillcolor=\"lightblue\"];"
550  << std::endl;
551 
552  // next build connections from this plugin to the
553  // interface class
554  output_stream << node_name
555  << " -> "
556  << std::string(typeid(InterfaceClass).name())
557  << " [len=3, weight=50]"
558  << ';'
559  << std::endl;
560 
561  // finally see if this plugin is derived from
562  // SimulatorAccess; if so, draw an arrow from SimulatorAccess
563  // also to the plugin's name
564  if (dynamic_cast<const SimulatorAccess<2>*>(instance.get()) != nullptr
565  ||
566  dynamic_cast<const SimulatorAccess<3>*>(instance.get()) != nullptr)
567  output_stream << "SimulatorAccess"
568  << " -> "
569  << node_name
570  << " [style=\"dotted\", arrowhead=\"empty\", constraint=false, color=\"gray\", len=20, weight=0.1];"
571  << std::endl;
572  }
573 
574  // as a last step, also draw a connection from the interface class
575  // to the Simulator class, or whatever the calling function indicates
576  // as the attachment point
577  output_stream << std::string(typeid(InterfaceClass).name())
578  << " -> "
579  << attachment_point
580  << " [len=15, weight=50]"
581  << ';'
582  << std::endl;
583 
584  // end it with an empty line to make things easier to
585  // read when looking over stuff visually
586  output_stream << std::endl;
587  }
588  }
589  }
590 }
591 
592 
593 #endif
bool plugin_type_matches(const PluginType &object)
Definition: plugins.h:63
void write_plugin_graph(std::ostream &output_stream)
static std::list< PluginInfo > * plugins
Definition: plugins.h:184
std::tuple< std::string, std::string, void(*)(ParameterHandler &), InterfaceClass *(*)()> PluginInfo
Definition: plugins.h:170
#define AssertThrow(cond, exc)
TestType & get_plugin_as_type(PluginType &object)
Definition: plugins.h:80
static ::ExceptionBase & ExcMessage(std::string arg1)
void declare_parameters(ParameterHandler &prm)
#define Assert(cond, exc)
RegisterHelper(void(*register_function)(const std::string &, const std::string &, void(*)(ParameterHandler &), InterfaceClass *(*)()), const char *name, const char *description)
Definition: plugins.h:125
DeclException1(ExcARKodeError, int,<< "One of the SUNDIALS ARKode internal functions "<< " returned a negative error code: "<< arg1<< ". Please consult SUNDIALS manual.")
static InterfaceClass * factory()
Definition: plugins.h:143
static ::ExceptionBase & ExcInternalError()