This example introduces the uniaxial material library in OpenSees.
A uniaxial material describes the relationship between strain (or deformation) and stress (or force) in one dimension.They are often used to simulate the behavior of truss elements where forces and deformations act along a single axis.These materials also model fibers in fiber sections by capturing the axial stress-strain relationship of each fiber.In addition, uniaxial materials can represent moment-rotation behavior for concentrated plasticity hinges at discrete sections in structural elements, providing a straightforward yet powerful way to capture nonlinear effects.Despite their versatility, uniaxial materials have limitations in accurately representing multi-axial or coupled responses. They do not inherently model effects like buckling or shear interaction and may require supplemental formulations or more advanced modeling approaches to capture these complexities.We can interact with a uniaxial material using the getStress
method. This method takes the following arguments:
<blockquote class="blockquote">
<div class="mb-3 syntax-highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"></code></pre></div></div>
</blockquote>
getStress(strain: float = None, commit: bool =False) -> float
<blockquote class="blockquote">
<div class="mb-3 syntax-highlight"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"></code></pre></div></div>
</blockquote>
from xara.invoke import invoke_uniaxial
import matplotlib.pyplot as plt try: # Try loading a plotting style that may not be installed plt.style.use("veux-web") except: pass
opensees.units
submodules that will help us keep track of units.
from opensees.units.english import ksi, psi
Now we’ll define some properties that are representative of structural steel. Fy
is the yield stress, and Es
the initial Young’s modulus.
Fy = 66.8*ksi # steel yield stress Es = 29000.*ksi # modulus of steel
Next we’ll create and plot a simple strain history. Here we use
<svg class="svg-inline--fa fas fa-up-right-from-square fa-2xs" fill="currentColor" aria-hidden="true" role="img" viewBox="0 0 512 512"><use href="#fas-up-right-from-square"></use></svg></a> for basic array processing and [<code>matplotlib</code>] for plotting.
from numpy import sin, linspace, pi, sqrt strain = 0.005*sin(linspace(0, 2.0*pi, 100)) fix, ax = plt.subplots() ax.plot(strain) ax.set_ylabel(r"Strain, $\varepsilon$") ax.set_xlabel(r"Time, $t$");
Now we are ready to bring in the opensees
library. We initialize an empty list stress
, then create the material, and loop over the strains we just created to collect the stresses.
from opensees import uniaxial stress = [] elastic_pp_material = uniaxial.ElasticPP(Es, Fy/Es) with elastic_pp_material.handle() as mat: for e in strain: stress.append(mat.getStress(e, commit=True)/Fy)
Now we’ll plot the stress response we’ve just collected as a function of strain. For this we’ll first create a figure with axes using the
<svg class="svg-inline--fa fas fa-up-right-from-square fa-2xs" fill="currentColor" aria-hidden="true" role="img" viewBox="0 0 512 512"><use href="#fas-up-right-from-square"></use></svg></a> function from <code>matplotlib</code>, then we’ll use the <code>.plot()</code> method of the axes to add our data.
# create a plotting figure fig, ax = plt.subplots() ax.axhline(0, color='lightgrey', linestyle='-', linewidth=1) ax.axvline(0, color='lightgrey', linestyle='-', linewidth=1) ax.plot(strain/(Fy/Es), stress) # Draw a horizontal line at the yield stress Fy ax.axhline(y=1, color='gray', linestyle='--') ax.text(-1, 1, r"$F_y$", color='r', va='center', ha='right', bbox=dict(facecolor='white', edgecolor='none', pad=2.0)) ax.set_xlabel(r"$\varepsilon/\varepsilon_y$") ax.set_ylabel(r"$\sigma/F_y$");
Bs = 0.1 # strain-hardening ratio strain = 3*sin(linspace(0, 2.0*pi, 300))*linspace(0.8, 1.1, 300)**2 fig, ax = plt.subplots() n = 10 with invoke_uniaxial("BWBF", Es, Fy, Bs, n, beta=0.5) as m: stress = [m.getStress(e*Fy/Es, commit=True)/Fy for e in strain] ax.plot(strain, stress, "-", label=r"$\beta = 0.5$") with invoke_uniaxial("BWBF", Es, Fy, Bs, n, beta=1.0) as m: stress = [m.getStress(e*Fy/Es, commit=True)/Fy for e in strain] ax.plot(strain, stress, "-.", label=r"$\beta = 1.0$") with invoke_uniaxial("BWBF", Es, Fy, Bs, n, beta=0.2) as m: stress = [m.getStress(e*Fy/Es, commit=True)/Fy for e in strain] ax.plot(strain, stress, ":", label=r"$\beta = 0.1$") # m._interpreter.eval("print -json") ax.set_xlabel(r"$\varepsilon/\varepsilon_y$") ax.set_ylabel(r"$\sigma/\sigma_y$") # fig.savefig("img/steel.png") fig.legend();
fig, ax = plt.subplots() with invoke_uniaxial("BWBF", Es, Fy, Bs, n, beta=0.5) as m: tangent = [] for e in strain: m.getStress(e*Fy/Es, commit=True) E = m.getTangent() tangent.append(E/Es) ax.plot(tangent, "-", label=r"$\beta = 0.1$") with invoke_uniaxial("BWBF", Es, Fy, Bs, n, beta=0.2) as m: tangent = [] for e in strain: m.getStress(e*Fy/Es, commit=True) E = m.getTangent() tangent.append(E/Es) ax.plot(tangent, "-", label=r"$\beta = 0.2$") with invoke_uniaxial("BWBF", Es, Fy, Bs, n, beta=0.8) as m: tangent = [] for e in strain: m.getStress(e*Fy/Es, commit=True) E = m.getTangent() tangent.append(E/Es) ax.plot(tangent, "-", label=r"$\beta = 0.8$") fig.legend()
<svg class="svg-inline--fa fas fa-up-right-from-square fa-2xs" fill="currentColor" aria-hidden="true" role="img" viewBox="0 0 512 512"><use href="#fas-up-right-from-square"></use></svg></a>.
Bs = 0.005 # strain-hardening ratio R0 = 18 # control the transition from elastic to plastic branches cR1 = 0.925 # " cR2 = 0.15 # "
strain = 0.02*sin(linspace(0, 5.5*pi, 300))*linspace(0.5, 1, 300)**2 Bs = 0.01 fig, ax = plt.subplots() ax.axhline(0, color='lightgrey', linestyle='-', linewidth=1) ax.axvline(0, color='lightgrey', linestyle='-', linewidth=1) with uniaxial.Steel02(Fy, Es, Bs, R0).handle() as steel: ax.plot(strain/(Fy/Es), [steel.getStress(e, commit=True)/Fy for e in strain], label="Steel02") # with uniaxial.RambergOsgoodSteel(Fy, Es, 0.002, R0).handle() as steel: # ax.plot(strain, [steel.getStress(e, commit=True) for e in strain], label="RambergOsgoodSteel") esh, esu = 10*Fy/Es, 20*Fy/Es eshi = (esu + 5*esh)/5 with uniaxial.DoddRestrepo(Fy, 1.2*Fy, esh, esu, Es, eshi, 1.1*Fy).handle() as steel: ax.plot(strain/(Fy/Es), [steel.getStress(e, commit=True)/Fy for e in strain], ":", label="DoddRestrepo") ax.set_xlabel(r"$\varepsilon/\varepsilon_y$") ax.set_ylabel(r"$\sigma/\sigma_y$") fig.legend(); fig.savefig("img/steel.png")
# nominal concrete compressive strength fc = -8.5*ksi # Concrete compressive strength ksi (+Tension -Compression) Ec = 57*ksi*sqrt(-fc/psi) # Concrete Elastic Modulus # unconfined concrete fc1U = fc # unconfined concrete maximum stress (Todeschini parabola) eps1U = -0.003 # strain at maximum strength of unconfined concrete fc2U = 0.2*fc1U # ultimate stress eps2U = -0.01 # strain at ultimate stress _lambda = 0.1 # ratio between unloading slope at eps2 and initial slope Ec # tensile-strength properties ftU = -0.14*fc1U # tensile strength +tension Ets = ftU/0.002 # tension softening stiffness
strain = -2*eps1U*sin(linspace(0, 2*pi, 500)) fig, ax = plt.subplots() ax.plot(strain/eps1U) ax.set_xlabel(r"$\tau$") ax.set_ylabel(r"$\varepsilon/\varepsilon_c$") ax.set_title("Strain history"); fig, ax = plt.subplots() with uniaxial.Concrete02(fc1U, eps1U, fc2U, eps2U, _lambda, ftU, Ets).handle() as c: ax.plot(-strain/eps1U, [-c.getStress(e, commit=True)/fc for e in strain], label="Concrete02") with uniaxial.Concrete02IS(Ec, fc1U, eps1U, fc2U, eps2U, [_lambda, ftU, Ets]).handle() as conc: ax.plot(-strain/eps1U, [-conc.getStress(e, commit=True)/fc for e in strain], label="Concrete02IS") with uniaxial.Concrete04(fc1U, eps1U, eps2U, 4e3*ksi, [ftU, ftU/Ets]).handle() as conc: ax.plot(-strain/eps1U, [-conc.getStress(e, commit=True)/fc for e in strain], label="Concrete04") with uniaxial.ConcreteCM(fc1U, eps1U, 4500*ksi, 7, 1.035, 0.30, 0.00008, 1.2, 10000).handle() as conc: ax.plot(-strain/eps1U, [-conc.getStress(e, commit=True)/fc for e in strain], label="ConcreteCM") ax.legend() ax.set_xlabel(r"$\varepsilon/\varepsilon_c$") ax.set_ylabel(r"$\sigma/f'_c$");
strain = 0.02*sin(linspace(0, 5.5*pi, 300))*linspace(0.5, 1, 300)**2
fig, ax = plt.subplots() ax.plot(strain) ax.set_xlabel(r"$\tau$") ax.set_ylabel(r"$\varepsilon$") ax.set_title("Strain history");
<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mtext>parallel</mtext></msub></mrow><annotation encoding="application/x-tex"> k_{\text{parallel}} </annotation></semantics></math></span>
is given by:[k_{\text{parallel}} = k_1 + k_2]
Structural elements that might exhibit a flag-like response include certain types of bracing systems, energy dissipating devices, and connections in steel structures. These elements typically show a combination of elastic and plastic behavior with a distinct yield point, followed by a plateau and then strain hardening. This type of response is often seen in systems designed to absorb and dissipate energy during events like earthquakes, providing both stiffness and ductility.This flag-like response can be simulated using two bilinear springs in parallel. By combining two springs with different stiffness and yield strengths, we can create a composite model that captures the initial stiffness, yielding, and post-yield behavior of the structural element. This approach allows for a more accurate representation of the complex behavior of materials and connections under cyclic loading conditions.
mat_a = uniaxial.Hardening(15e3, fy=15., H_iso=0., H_kin=5e1, name=1) mat_b = uniaxial.ElasticBilin(45e3, 1e2, 15/15e3, name=2)
fig, ax = plt.subplots(1,2, sharey=True, constrained_layout=True) with mat_a.handle() as m: ax[0].plot(strain, [m.getStress(e, commit=True) for e in strain]) with mat_b.handle() as m: ax[0].plot(strain, [m.getStress(e, commit=True) for e in strain]) # with mat_a as m: # ax[0].plot(strain, [m.getStress(e, commit=True) for e in strain]) with uniaxial.Parallel([mat_a, mat_b], name=3).handle() as m: ax[1].plot(strain, [m.getStress(e, commit=True) for e in strain]) ax[0].set_title("Component Models") ax[1].set_title("Composite Model") for a in ax: a.set_xlabel(r"$\varepsilon$"); a.set_ylabel(r"$\sigma$");
<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mtext>series</mtext></msub></mrow><annotation encoding="application/x-tex"> k_{\text{series}} </annotation></semantics></math></span>
mat_a = uniaxial.Hardening(40e3, fy=40., H_iso=0., H_kin=5e3, name=1) mat_b = uniaxial.Hardening(30e3, fy=60., H_iso=0., H_kin=5e2, name=2)
fig, ax = plt.subplots(1,2, sharey=True, constrained_layout=True) with mat_a.handle() as m: ax[0].plot(strain, [m.getStress(e, commit=True) for e in strain]) with mat_b.handle() as m: ax[0].plot(strain, [m.getStress(e, commit=True) for e in strain]) with uniaxial.Series([mat_a, mat_b], name=3).handle() as m: ax[1].plot(strain, [m.getStress(e, commit=True) for e in strain]) ax[0].set_title("Component Models") ax[1].set_title("Composite Model") for a in ax: a.set_xlabel(r"$\varepsilon$"); a.set_ylabel(r"$\sigma$");
mat_a = uniaxial.Hardening(30e3, fy=60., H_iso=0., H_kin=1e2, name=1) mat_b = uniaxial.ElasticBilin(10.0, 30e3, 0.01, name=2)
fig, ax = plt.subplots(1, 2, sharey=True, constrained_layout=True) with mat_a.handle() as m: ax[0].plot(strain, [m.getStress(e, commit=True) for e in strain]) with mat_b.handle() as m: ax[0].plot(strain, [m.getStress(e, commit=True) for e in strain]) with uniaxial.Series([mat_a, mat_b], name=3).handle() as m: ax[1].plot(strain, [m.getStress(e, commit=True) for e in strain]) ax[0].set_title("Component Models") ax[1].set_title("Composite Model") for a in ax: a.set_xlabel(r"$\varepsilon$"); a.set_ylabel(r"$\sigma$");