Many publications describe software design patterns for reusing object-oriented software. The most widely read book on design patterns is so influential that it has its own Wikipedia page. In this book, the “Gang of Four” offers two guiding principles for software design patterns.
The first principle is to program to an interface, not an implementation, i.e., seek general solutions, not specific solutions that work for only a few cases. The second principle is to favor object composition over class inheritance as a means of code reuse, i.e., treat objects you want to reuse as unchangeable units.
Although numerous software design patterns appear at all levels of the OpenSees framework, this post will highlight the Decorator pattern and its application to UniaxialMaterial and NDMaterial objects.
The Decorator pattern creates a new object that modifies the functionality of an object of the same type. Not only does the Decorator pattern allow for extensive code reuse, it can lead to rapid development of new models and quick changes to existing models without having to add new code.
Writing this post, I hope to mitigate unnecessary code duplication in OpenSees. So, before you code up a new material model, think to yourself “there might be a wrapper for that”, then re-read this post.
There are several UniaxialMaterial wrapper classes. After briefly describing the functionality of each wrapper, I will show how the wrapper affects a canonical wrapped material through one load cycle. The wrapped material is a HardeningMaterial object with elastic modulus, , yield stress,
=1, and kinematic hardening modulus,
.
In a case of addition by subtraction, the PathIndependentMaterial class does not call commitState() on its wrapped UniaxialMaterial object, making the stress-strain response path-independent.

An undocumented wrapper, the TensionOnlyMaterial class returns zero stress and tangent for any UniaxialMaterial object that returns negative stress. The wrapper also doesn’t call commitState() when there is negative stress in the wrapped material.

The MinMaxMaterial forwards strain to its wrapped UniaxialMaterial object and returns its stress and tangent, until the material strain is greater than a specified maximum strain or less than a specified minimum. After this condition is met in commitState(), the wrapper does nothing, like the wrapped material has failed. Below is a demonstration with the minimum strain set to -0.8.

Another undocumented wrapper, the SimpleFractureMaterial imposes tensile fracture on its wrapped UniaxialMaterial object. After reaching the specified tensile strain, the wrapper will provide compressive stress based on an estimate of the elastic strain. Below is a demonstration with the maximum tensile strain set to 0.8.

The InitStrainMaterial wrapper imposes an initial strain on its wrapped UniaxialMaterial object. After the constructor is called, nothing special is done by the wrapper. Below is a demonstration with the initial strain equal to -0.1.

Similarly, the InitStressMaterial imposes an initial stress on its wrapped UniaxialMaterial object. Remember that it’s a good idea to do a single analysis step with when using the initial strain and initial stress wrappers in your model. Below is a demonstration with the initial stress equal to 0.3.

The FatigueMaterial class wraps any UniaxialMaterial object and accumulates damage according to Miner’s Rule using a modified rainflow cycle counting algorithm. After the fatigue life is exhausted, the wrapper returns zero stress and tangent. Please see the wiki documentation for this wrapper to find more details.
An undocumented wrapper, the DamperMaterial class uses the stress-strain response of any UniaxialMaterial object as a stress-strain rate relationship for damping. This conversion is done by a simple transfer of strain rate to strain and tangent to damping tangent in the material state determination methods.
You might wonder why I omitted the ParallelMaterial and SeriesMaterial classes from this list. The reason is they are examples of the Composite pattern.
There are also wrappers for NDMaterial objects. The PlaneStrainMaterial sets the appropriate strain components to zero before calling its wrapped three-dimensional NDMaterial object. We should also have one of these for the axisymmetric strain condition.
The PlaneStressMaterial, PlateFiberMaterial, and BeamFiberMaterial (versions for 2D and 3D beams), classes use static condensation in order to impose the appropriate stress conditions on their wrapped three-dimensional NDMaterial objects.
The ContinuumUniaxial class performs static condensation to get from a three-dimensional NDMaterial object to a uniaxial state of stress. Because it goes between the NDMaterial and UniaxialMaterial interfaces, the ContinuumUniaxial class is technically an Adapter, not a Decorator.
The PlateFromPlaneStressMaterial wrapper adds linear-elastic shear behavior to a plane stress material in order to get the plate fiber stress condition. This wrapper does not need to perform static condensation. The PlateRebarMaterial wrapper (also technically an Adapter) uses a UniaxialMaterial object and an orientation angle to represent reinforcement in a shell layer.
The InitStressNDMaterial is the NDMaterial version of the InitStressMaterial wrapper shown above. It only works for plane strain and three-dimensional material models, but there’s no reason it can’t be adapted for initial stresses in beam and plate materials.
The InitStressNDMaterial, BeamFiberMaterial, and ContinuumUniaxial wrappers are undocumented. This is my doing.
Well, that should wrap up this post. Information on other design patterns used in the material and element models of OpenSees can be found here while information on the design patterns used in the high level model building and solution strategies can be found here.
Hello PD
Nice post again!
Quick question:
The analysis with time step 0 that should be run after initStress is called is a static or a transient analysis?
LikeLike
Hola Gustavo,
Gracias! The analysis to make sure initial stresses are in equilibrium should be static, e.g., using integrator(‘LoadControl’,0.0).
PD
LikeLike
I think a nice addition to this post is to point out that wrappers incorporating static condensation for materials are great from a software re-use point of view, but from a performance point-of-view they have a few drawbacks. 1) increased memory requirements because you are storing state variables for the full 6D tensor (e.g., vs. 3D that are actually required in plane stress), and 2) you are doing additional iterations to converge the static condensation, and 3) at least from experience, the static condensation is not as accurate as just solving the material in the constrained subspace. I think particularly the third point can be critical. For my experience, in a matlab implementation of a force-based beam element: using something similar to the “BeamFiberMaterial” wrapper, it could take around 10000 iterations to get to the desired convergence criteria, while doing the material return-mapping in the constrained subspace it would take less than 5 or so. To be fair, I’m not sure how things look in OpenSees, but the first two points will always be there.
I think it’s very necessary to have the wrappers, but it’s also nice to have the efficient option 😛
LikeLike
Hello Alex,
Good point, the UniaxialMaterial wrappers add only one indirection, but the plane stress, beam stress, etc. wrappers for NDMaterials can take a lot of time with the internal iterations at every Gauss point and fiber location (for beam stress). The wrappers are useful for trying things out prior to formulating the response directly in the constrained subspace, if you don’t have access to the original source code, or if making the proper formulation proves to be too difficult. I would be interested to do an OpenSees comparison with the MATLAB implementation you mentioned.
Thanks!
PD
LikeLike
Or compare in OpenSees is even better (maybe a future blog)! Implementing the “BeamFiber” stress state UVC model has been on my to-do list for a while… The implementation is not so difficult if you have the plane-stress case already, since the algorithm remains the exact same and the only changes are to the matrix of elastic moduli, the projection matrix and their eigendecompositions.
LikeLike
Good idea! These comparisons are actually “ready to go” using J2Plasticity with BeamFiber wrapper and the J2BeamFiber, which has the direct formulation using the projection matrix based on Simo and Hughes, Computational Inelasticity, (1998).
LikeLike