#include <map>
#include <sstream>
#include <string>
#include "b2ppconfig.h"
#include "utils/b2allocator.H"
#include "utils/b2exception.H"
#include "utils/b2module.H"
#include "utils/b2util.H"
Go to the source code of this file.
Classes | |
class | b2000::ObjectType |
class | b2000::ObjectTypeIncomplete< OBJECT, BASE_OBJECT_TYPE > |
class | b2000::ObjectTypeComplete< OBJECT, BASE_OBJECT_TYPE > |
class | b2000::Object |
Namespaces | |
namespace | b2000 |
Contains the base classes for implementing Finite Elements. | |
Implementations of elements, material models, boundary conditions, solvers, etc. are all sub-classes of b2000::Object
. Instances of b2000::ObjectType
are factory objects which create instances of specific sub-classes ofb2000::Object
and which, in conjunction with b2000::Module
, allow for the implementation of plugins without having to recompile B2000++.
The motivation for b2000::Object
and b2000::ObjectType
is illustrated by means of an example. In traditional Finite Element codes that are written with procedural languages such as Fortran or C, element subroutines like set_nodes() or get_value(), i.e computation of first and second variation, are called within if/else constructs. Example (pseudo-code):
While straightforward, this technique requires a lot of code duplication for each set of subroutines, for example:
This duplication makes the code hard to maintain. A better approach consists of the object-oriented concept, where functions and data are grouped in polymorphic classes that derive from a common base class. It is the necessary to define if/else constructs only once, for the instantiation of the objects:
Thereafter, the polymorphic (virtual) functions can be used regardless of the type:
While this approach is more elegant, it does not allow for dynamically adding new elements without having to recompile B2000++.
However, b2000::Object
in conjunction with b2000::ObjectType
allows to register sub-classes of b2000::Object
under a names (and several aliases), such that no if/else constructs are necessary at all.
Classes whose instances should be created dynamically should be sub-classes of b2000::Object
and must override the static member named type
. This static member is derived from b2000::ObjectType
and serves for registration and as a factory object for instances of that sub-class.
Using this static type
member, one can, by means of b2000::ObjectType::get_subtype()
, get the factory object of a sub-class, and, by means of b2000::ObjectType::new_object()
, create instances of this sub-class.
The implementation of an Object sub-class is explained by means of an example. Note that it must be done for every sub-class of b2000::Objec
t (thus, also for sub-classes of b2000::Element
, b2000::ElementProperty
, b2000::Solver
, etc.).
The sub-classElementQ4Stress2DExample
implements a quadrilateral 2D stress element and is derived from b2000::TypedElement<double>
, which itself is derived from b2000::Element
. To register (under the name Q4.S.2D.EXAMPLE
):
The static member type
is initialized as follows:
To this end, the b2000::ObjectType::get_subtype()
method is used. This is demonstrated by means of the following example. The ElementQ4Stress2DExample
class may be provided by one of the modules
libb2000++.so
(for builtin element implementations)-b2000.element.Q4.S.2D.EXAMPLE
: The name is libb2000.element.Q4.S.EXAMPLE.so
(for external element implementations)The ObjectType::get_subtype()
function works for both cases, since it will attempt to load the shared library libb2000.element.Q4.S.EXAMPLE.so if Q4.S.EXAMPLE
is not found in theb2000.element
module.
The following code is a simplification of the Domain implementation: