OOF2: The Manual
Synopsis
C++ Synopsis
#include "engine/property.h"
class EqnProperty: , public PhysicalProperty {virtual void* begin_point(const FEMesh* mesh,
const Element* element,
const Equation* equation,
const MasterPosition& position,
double time) const;virtual void end_point(const FEMesh* mesh,
const Element* element,
const Equation* equation,
const MasterPosition& position,
double time,
void* data) const;virtual void time_deriv_matrices(const FEMesh* mesh,
const Element* element,
const ElementFuncNodeIterator& node,
const Equation* equation,
const MasterPosition& position,
double time,
void* data,
SmallSystem* linsys) const;virtual void force_value(const FEMesh* mesh,
const Element* element,
const Equation* equation,
const MasterPosition& position,
double time,
void* data,
SmallSystem* linsys) const;virtual void force_deriv_matrix(const FEMesh* mesh,
const Element* element,
const Equation* equation,
const ElementFuncNodeIterator& node,
const MasterPosition& position,
double time,
void* data,
SmallSystem* linsys) const;
}
Python Synopsis
PyEqnProperty is a swigged C++ class
that is derived (in C++) from
EqnProperty. It is used as a base
class for EqnProperties that are
defined in Python. When the methods listed below are called
from C++, they use the Python C API to invoke methods in the
Python derived class.
from ooflib.SWIG.engine import pypropertywrapper
class PyEqnProperty(EqnProperty):def begin_point (self, mesh, element, equation, position)def end_point(self, mesh, element, equation, position, time, data)def time_deriv_matrices(self, mesh, element, node, equation, position, time, data, linsys)def force_value(self, mesh, element, equation, position, time, data, linsys)def force_deriv_matrix(self, mesh, element, node, equation, position, time, data, linsys)
Source Files
-
SRC/engine/property.C: C++ code -
SRC/engine/property.h: C++ header -
SRC/engine/property.swg: SWIG source code -
SRC/engine/property.swg: Python code included in the SWIG output -
SRC/engine/pypropertywrapper.C: C++ code forPropertiesimplemented in Python -
SRC/engine/pypropertywrapper.h: C++ header -
SRC/engine/pypropertywrapper.swg: SWIG source code -
SRC/engine/pypropertywrapper.spy: Python code inserted into the SWIG output.
Overview
An EqnProperty is a Property that
makes a direct contribution to an Equation without being
part of a Flux. For instance Mechanical:MassDensity:ConstantMassDensity
makes a contribution to the second time derivative in the Force_Balance
Equation but is not part of the Stress Flux.
Similarly, Mechanical:Damping:Isotropic makes a
contribution to the first time derivative, and Mechanical:ForceDensity:ConstantForceDensity
contributes to the force term.
A new subclass of EqnProperty must define
at least one of following two virtual methods:
-
EqnProperty::time_deriv_matrices()computes theProperty's contribution to the coefficient of the first and/or second time derivative in the divergence equation (2.9). -
EqnProperty::force_value()computes theProperty's direct contribution to the force in Eq. (2.9).
In addition, if force_value() is nonlinear,
the new subclass must define:
-
EqnPropertry::force_deriv_matrix()computes the derivative offorce_value()with respect to theFieldcomponents.
The functions listed above all have a SmallSystem* argument, which represents a
small part of the LinearizedSystem
that is currently being built.
SmallSystem methods handle all of the
issues related to matrix indexing, element order, and so forth.
The derived EqnProperty methods (and the
developer) do not need to know anything about what equations are
being solved or how they are discretized by the finite element
machinery. Details on all of the functions are provided below.
In addition, two virtual utility functions, begin_point()
and end_point(),
can be redefined to facilitate passing data between the other
routines.
Constructor
The C++ and Python constructors for
EqnProperty are the same as the base class
constructors.
Methods
void* begin_point(...) const
void *begin_point(const FEMesh* mesh, const Element* element, const Equation* equation, const MasterPosition& position, double time) const
When building the finite element matrices and vectors, OOF2
loops over the Gauss points within each Element. At each
Gauss point, for each active Equation, it calls
EqnProperty methods for each Property that contributes the Equation,
beginning with begin_point(), followed by
time_deriv_matrices(),
force_deriv_matrix()
(for nonlinear properties), and finally force_value().
To avoid recomputing quantities that are required by more than
one of these methods, computations can be done in
begin_point() and their results stored in
a data structure. begin_point() should
return this structure (as a void* pointer in C++
or an object in Python). The data structure will be passed
through to the other FluxProperty
methods. If the data object was allocated in C++,
end_point() should delete it.
The arguments to begin_point() are:
const FEMesh* mesh-
The
FEMeshcurrently being solved. const Element* element-
The
Elementcontaining the current point. const Equation*-
The
Equationbeing solved. If theEqnPropertycontributes to more than oneEquation,begin_point()will be called once for eachEquationand this value should be checked to determine which one is current. const MasterPosition-
The position within the
Elementwhere thePropertyis being evaluated. The position is given in master coordinates, which can be converted to physical coordinates byElement::from_master()if necessary. double time-
The time at which the
Propertyis being evaluated.
The default implementation of
begin_point() simply returns
nullptr in C++ and
None in Python.
void end_point(...) const
void end_point(const FEMesh* mesh, const Element* element, const Equation* equation, const MasterPosition& position, double time, void *data) const
end_point() is just like begin_point(),
except that it's called after OOF2 is
done computing the contribution at a point. The
end_point() method can be used to clean
up any temporary objects allocated by begin_point.
The arguments for end_point() are the same
as the arguments for begin_point(),
with the addition of a void* which points to the
data returned by begin_point().
The default implementation of end_point()
does nothing.
void time_deriv_matrices(...)
const
void time_deriv_matrices(const FEMesh* mesh, const Element* element, const ElementFuncNodeIterator& node, const Equation* equation, const MasterPosition& position, double time, void *data, SmallSystem* linsys) const
EqnProperty classes that contribute to
or , the coefficients of the first and second time derivatives of the
Fields in the time-dependent divergence equation (2.9), need to define
time_deriv_matrices(). It is analogous
to FluxProperty::flux_matrix(),
but generally much simpler.
As in FluxProperty::flux_matrix(),
the derivation begins by expanding the Field components in
terms of the shape functions:[77]
where is the shape function that is 1 at node and 0 at all other nodes, and
is the component of the field at node . OOF2 stores the values of for all nodes in a vector, where should be understood as a single index.
Because is a vector, Equation (2.9) is a matrix equation, so the first derivative term is
where is an equation index. Using Eq. (201), this becomes
The derivation for is identical, except that the second time derivative is used instead of the first:
The job of time_deriv_matrices() is to
compute the terms in square brackets on the right
hand sides of (204) and (205).
The arguments to time_deriv_matrices()
are:
const FEMesh* mesh-
The finite element mesh that's being solved.
time_deriv_matrices()probably doesn't have to use themeshobject directly, but it does need to pass it through to other functions. const Element* element-
The finite element under consideration. This shouldn't be explicitly needed except in cases in which the material parameters depend on physical space coordinates, or if history-dependent fields are stored in the element.
const ElementFuncNodeIterator& node-
This is the node referred to in the discussion above. It's passed in in the form of an iterator, which can iterate over all of the nodes of the element, although it should be thought of here simply as a way of accessing the node's indices and shape functions.[78] The node's shape function and its derivatives can be obtained from
ElementFuncNodeIterator::shapefunction()andElementFuncNodeIterator::dshapefunction(). Equation* equation-
The
Equationbeing computed. Properties that contribute to more than oneEquationmay need to check this variable to know which one they're being asked to compute. const MasterPosition& position-
This is the position in (204).
positionis a point inelement'sElement's master coordinate space. It is often a Gauss integration point, but not always. Master coordinates can be converted to physical coordinates by callingElement::from_master(). double time-
The time at which the coefficients are being evaluated.
void* data-
The data object, if any, returned by
begin_point(). -
SmallSystem* linsys -
Storage for the portion of the the actual and that are currently being computed, along with other data.
SmallSystemknows how to convert localElementandNodeindices into globalFEMeshindices.
The default version of
time_deriv_matrices() does nothing.
Example of time_deriv_matrices() for
first derivatives
For example, here is the
first_time_deriv_matrix() method
for the Mechanical:Damping:Isotropic Property
in SRC/engine/properties/damping/damping.h.
The Property adds a
term to the and components of the Displacement in the force balance equation (6.120).[82]
void IsotropicDampingProp::time_deriv_matrices( const FEMesh* mesh, const Element* element, const ElementFuncNodeIterator& node,const Equation* eqn, const MasterPosition& mpos, double time, void *data, SmallSystem* linsys) const { if(*eqn != force_balance)
![]()
throw ErrProgrammingError("Unexpected equation.", __FILE__, __LINE__); double shapeFuncVal = node.shapefunction(mpos);
for(IndexP component : *eqn->components()) {
eqndata->damping_matrix_element(component, displacement, component, node)
+= coeff*shapeFuncVal;
} }
|
This is the node in Eq. (204). |
|
|
|
|
|
Checking the equation is optional here. It verifies
that the equation |
|
|
This is in Eq. (206). |
|
|
See Section 8.4 for a
discussion about looping over components of an
|
|
|
|
|
|
This is where Eq. (206) is
added to the finite element matrix. It uses
|
Example of time_deriv_matrices() for
second derivatives
Here is the time_deriv_matrices()
method for the Mechanical:MassDensity:ConstantMassDensity
Property in
SRC/engine/properties/massdensity/massdensity.C,
which adds a term
to the force
balance equation (6.120).
It is essentially the same as the damping example above, but
it calls SmallSystem::mass_matrix_element()
instead of SmallSystem::damping_matrix_element().
void MassDensityProp::time_deriv_matrices(
const FEMesh* mesh,
const Element* element,
const ElementFuncNodeIterator& node,
const Equation *eqn,
const MasterPosition& mpos,
double time,
void*,
SmallSystem *eqdata) const
{
double shapeFuncVal = node.shapefunction(mpos);
for(IndexP eqncomp : *eqn->components()) {
eqdata->mass_matrix_element(component, disp, component, node)
+= rho_ * shapeFuncVal;
}
} |
|
void force_value(...) const
void force_value(const FEMesh* mesh, const Element* element, const Equation* equation, const MasterPosition& position, double time, void* data, SmallSystem* linsys) const
A Property that makes a direct contribution to the
force term in Eq. (2.9) must redefine
force_value(), which must use SmallSystem::forceVector()
to set the value of the force at the given point in the given
Element. The arguments to
force_value() are[83]:
const FEMesh* mesh-
The
FEMeshthat's currently being solved. const Element* element-
The
Elementcontaining the current point. const Equation*-
The
Equationbeing solved. If theEqnPropertycontributes to more than oneEquation,begin_point()will be called once for eachEquationand this value should be checked to determine which one is current. const MasterPosition-
The position within the
Elementwhere thePropertyis being evaluated. The position is given in master coordinates, which can be converted to physical coordinates byElement::from_master()if necessary. double time-
The time at which the
Propertyis being evaluated. void* data-
The data object, if any, returned by
begin_point().
The default version of force_value() does
nothing.
Example of force_value()
Here is the force_value() method for
Mechanical:ForceDensity:ConstantForceDensity.
It contributes a constant force density given by parameters
gx and gy, which are
stored in the Property as a ??? g.
void ForceDensity::force_value(
const FEMesh *mesh,
const Element *element,
const Equation *eqn,
const MasterPosition &x,
double time, void *data,
SmallSystem *linsys) const
{
linsys->forceVector() += g;
} |
This line adds all components of |
void force_deriv_matrix(...) const
void force_deriv_matrix(const FEMesh* mesh, const Element* element, const ElementFuncNodeIterator& node, const Equation* equation, const MasterPosition& position, double time, void* data, SmallSystem* linsys) const
force_deriv_matrix() needs to be defined
for any EqnProperty that defines a
nonlinear force_value().[84] It computes the
derivatives of the force with respect to the Fields. That
is, it computes a ???
where is a force component
and is a Field
component. The result is stored in the given SmallSystem by calling SmallSystem::force_deriv_matrix_element(),
which handles all of the indexing.
The arguments to force_deriv_matrix() are
the same as the arguments to time_deriv_matrices().
The default version of
force_deriv_matrix() does nothing.
Example of force_deriv_matrix()
Here is a template for a
force_deriv_matrix() in a Property
that depends nonlinearly on the Displacement Field:
void NonlinearForceDensity::force_deriv_matrix(const FEMesh *mesh,
const Element *element,
const ElementFuncNodeIterator &node,
const Equation *eqn,
const MasterPosition &point,
double time, void*,
SmallSystem *linsys) const
{
DoubleVec fieldVal = displacement->values(mesh, element, point);
Coord coord = element->from_master(point);
SmallMatrix forceDeriv = nonlin_force_density_deriv(coord, time, fieldVal);
for(IndexP eqncomp : *eqn->components()) {
for (IndexP fieldcomp : *displacement->components(ALL_INDICES)) {
linsys->force_deriv_matrix_element(eqncomp, displacement, fieldcomp, node)
+= forceDeriv(eqno, fieldno);
}
}
}
|
displacement = dynamic_cast<TwoVectorField*>(Field::getField("Displacement"));
|
|
|
|
|
|
|
|
|
This adds the components of the
|
[82] The damping does not represent physical damping, because it acts on the velocity in the lab frame, not the strain rate, but it can be useful to provide numerical stability.
[83] Note
that unlike the other EqnProperty
methods, force_value() does
not take a ElementFuncNodeIterator argument. It
doesn't multiply a field or field derivative that is being
expanded in shape functions.
[84] Technically, it only needs to be defined if the nonlinear solver needs to compute the Jacobian.


}
} 
