OpenSees Cloud
OpenSees AMI
Set and Get Concrete23 Response
Original Post - 13 Oct 2024 - Michael H. Scott
Visit Structural Analysis Is Simple on Substack.
When I wrote Concrete23, I copied Concrete01 then tweaked the unloading and reloading rules. I also added a couple of bells and whistles and it was music to my ears. But I wanted to record those sounds during an analysis.
Recorders in OpenSees use the setResponse
and getResponse
methods to
identify and obtain element, section, and material response. Admittedly,
working with setResponse
and getResponse
is a little convoluted, but it
beats polluting interfaces with dozens of methods like getRing()
,
getDing()
, and getDong()
for all the bells in all the element, section,
and material models.
This post describes the preferred approach to record the sounds of Concrete23 without having to pollute an interface or unnecessarily duplicate code. The focus is UniaxialMaterial, but the same ideas apply to recorders for Element and SectionForceDeformation objects.
First, the base UniaxialMaterial class implements setResponse
and
getResponse
for common response quantities like stress and tangent as
well as plastic strain. Below is an abridged implementation of the base
setResponse
method, which doesn’t really set anything, but is more of an
identify and set up operation.
Response *
UniaxialMaterial::setResponse(const char **argv, int argc,
OPS_Stream &theOutput)
{
if (argc < 1)
return 0;
if (strcmp(argv[0],"stress") == 0)
return new MaterialResponse(this, 1, 0.0);
if (strcmp(argv[0],"tangent") == 0)
return new MaterialResponse(this, 2, 0.0);
if (strcmp(argv[0],"plasticStrain") == 0)
return new MaterialResponse(this, 3, 0.0);
return 0;
}
The MaterialResponse
object takes a pointer to the material object
(this
), a unique integer identifier, and a data type–here just passing
a double because stress, tangent, and plastic strain are scalars. The
value of the double passed to the MaterialResponse
constructor doesn’t
matter–the constructor just needs to know the size and type of data to
be recorded.
And don’t worry, we’re not going to leak memory with the
new MaterialResponse
statements–the calling object will invoke delete
.
The corresponding abridged implementation of getResponse
is shown below.
int
UniaxialMaterial::getResponse(int responseID, Information &matInfo)
{
if (responseID == 1) {
matInfo.setDouble(this->getStress());
return 0;
}
if (responseID == 2) {
matInfo.setDouble(this->getTangent());
return 0;
}
if (responseID == 3) {
double strain = this->getStrain();
double stress = this->getStress();
double kinit = this->getInitialTangent();
// elastic unloading
matInfo.setDouble(strain-stress/kinit);
return 0;
}
return 0;
}
Note that the plastic strain is approximated by elastic unloading, subtracting the elastic component of strain from the total strain. Most implementations of UniaxialMaterial do a better job of tracking plastic strain, but this base class recorder option is adequate.
Moving on, the subclass can leverage the base class setResponse
and
getResponse
methods. First check for ring, ding, and dong (the special
sounds of Concrete23) then invoke setResponse
on the base class if
nothing hits.
Response *
Concrete23::setResponse(const char **argv, int argc,
OPS_Stream &theOutput)
{
if (argc < 1)
return 0;
if (strcmp(argv[0],"ring") == 0)
return new MaterialResponse(this, 100, 0.0);
if (strcmp(argv[0],"ding") == 0)
return new MaterialResponse(this, 101, 0.0);
if (strcmp(argv[0],"dong") == 0)
return new MaterialResponse(this, 102, 0.0);
return UniaxialMaterial::setResponse(argv, argc, theOutput);
}
The base UniaxialMaterial implementation of setResponse
uses identifiers
1, 2, 3…, thus the 101, 102, 103 in Concrete23. I should change the
base class to use negative identifiers to mitigate conflicts. The
identifiers can be the same between Concrete23 and Concrete24, but have
to be different from the base class. This approach is not quite as dumb
as the
combination to Lord Helmet’s luggage,
but look at the identifiers
in setResponse
of UniaxialMaterial before you implement recorders for
your materials.
For the subclass getResponse
, check the identifiers then pass ring,
ding, or dong (private data of Concrete23) to the Information
object. If
none of the identifiers match, call the base class getResponse
.
int
Concrete23::getResponse(int responseID, Information &matInfo)
{
if (responseID == 100) {
matInfo.setDouble(ring);
return 0;
}
if (responseID == 101) {
matInfo.setDouble(ding);
return 0;
}
if (responseID == 102) {
matInfo.setDouble(dong);
return 0;
}
return UniaxialMaterial::getResponse(responseID, matInfo);
}
Depending on how you implemented your material, you can also override
the default setResponse
and getResponse
methods for stress, tangent, or
plastic strain. These responses may be cached as private variables and
the default behavior of calling getStress
and getTangent
may launch
state determination that unnecessarily slows down the simulation, e.g.,
if your return mapping algorithm is implemented in getStress
.
Or perhaps you have the true plastic strain and don’t want to record the
elastic unloading approximation implemented in the base class. In this
case, add another if-statement in the subclass setResponse
to catch
“plasticStrain” before the base class setResponse
is called, give an
integer identifier that makes sense for your subclass, then record the
plastic strain in your getResponse
before the base class getResponse
is
called.