Skip to content
Open
3 changes: 3 additions & 0 deletions structuralcodes/materials/constitutive_laws/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ._elastic import Elastic
from ._elastic_2d import Elastic2D
from ._elasticplastic import ElasticPlastic
from ._elasticplastic_2d import ElasticPlastic2D
from ._parabolarectangle import ParabolaRectangle
from ._parabolarectangle_2d import ParabolaRectangle2D
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the tests are failing because we are trying to import ParabolaRectangle2D, but this is not available, since it is being implemented in #242. Please remove this for now, and we will make sure it exists after merging #242.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in commit 3362924

from ._popovics import Popovics
Expand All @@ -25,6 +26,7 @@
'UserDefined',
'get_constitutive_laws_list',
'create_constitutive_law',
'ElasticPlastic2D',
]

CONSTITUTIVE_LAWS: t.Dict[str, ConstitutiveLaw] = {
Expand All @@ -37,6 +39,7 @@
'parabolarectangle2d': ParabolaRectangle2D,
'popovics': Popovics,
'sargin': Sargin,
'elasticplastic2d': ElasticPlastic2D,
}


Expand Down
68 changes: 68 additions & 0 deletions structuralcodes/materials/constitutive_laws/_elasticplastic_2d.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""Elastic-plastic constitutive law."""

from __future__ import annotations

import typing as t

import numpy as np
from numpy.typing import ArrayLike

from structuralcodes.materials.constitutive_laws._elasticplastic import (
ElasticPlastic,
)


class ElasticPlastic2D(ElasticPlastic):
"""Class for elastic-plastic Constitutive Law in 2D."""

__materials__: t.Tuple[str] = (
'steel',
'rebars',
)

def __init__(
self,
E: float,
fy: float,
Eh: float = 0.0,
eps_su: t.Optional[float] = None,
name: t.Optional[str] = None,
) -> None:
"""Initialize an Elastic-Plastic 2D Material.

Arguments:
E (float): The elastic modulus.
fy (float): The yield strength.

Keyword Arguments:
Eh (float): The hardening modulus.
eps_su (float): The ultimate strain.
name (str): A descriptive name for the constitutive law.
"""
name = name if name is not None else 'ElasticPlasticLaw2D'
super().__init__(E=E, fy=fy, Eh=Eh, eps_su=eps_su, name=name)

@property
def E(self) -> float:
"""Return the elastic modulus."""
return self._E

@property
def C_s(self) -> np.ndarray:
"""Return the 2D constitutive matrix."""
return self.E * np.array(
[
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 0.0],
]
)

def get_stress(self, eps: ArrayLike) -> np.ndarray:
"""Return the stress given strain."""
sig_s = super().get_stress(eps)
return sig_s @ self.C_s / self.E

def get_tangent(self) -> np.ndarray:
"""Compute the 3x3 tangent stiffness matrix C."""
return self.C_s
63 changes: 63 additions & 0 deletions tests/test_materials/test_elasticplastid_2d.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Tests for the ElasticPlastic2D class."""

import numpy as np
import pytest

from structuralcodes.materials.constitutive_laws import ElasticPlastic2D


def test_elasticplastic_2d_init():
"""Test elasticplastic 2D material."""
mat = ElasticPlastic2D(210000, 410)
assert mat.E == 210000
assert mat._fy == 410
assert mat._Eh == 0.0
assert mat._eps_su is None
assert mat.name == 'ElasticPlasticLaw2D'


@pytest.mark.parametrize(
'E, fy, strain',
[
(210000, 410, np.array([0.001, 0.0, 0.0])),
(210000, 410, np.array([-0.002, -0.1, -0.002])),
(200000, 450, np.array([0.003, 0.005, 0.010])),
],
)
def test_elasticplastic_2d_get_stress(E, fy, strain):
"""Test elasticplastic 2D material."""
mat = ElasticPlastic2D(E, fy)
actual = mat.get_stress(strain)

expected = np.array(
[
np.clip(E * strain[0], -fy, +fy),
np.clip(E * strain[1], -fy, +fy),
0.0,
]
)

assert np.allclose(actual, expected, atol=1e-8)


def test_elasticplastic_2d_input_correct():
"""Test invalid input values for ElasticPlastic2D."""
with pytest.raises(ValueError) as excinfo:
ElasticPlastic2D(-210000, 500)
assert str(excinfo.value) == 'Elastic modulus E must be greater than zero'


@pytest.mark.parametrize(
'E, fy',
[
(210000, 500),
(200000, 500),
(195000, 500),
],
)
def test_elasticplastic_get_tangent(E, fy):
"""Test the elasticPlastic2D tangent matrix."""
assert np.allclose(
ElasticPlastic2D(E, fy).get_tangent(),
np.array([[E, 0, 0], [0, E, 0], [0, 0, 0]]),
)