Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2e70179
Draft initial structure for the concrete class
mortenengen Dec 13, 2022
b8f67bb
Update docstring of base material
mortenengen Dec 13, 2022
c299dcc
minimum reinforcement areas functions
DanielGMorenaFhecor Dec 15, 2022
59a04f5
raise ValueError test functions for min area
DanielGMorenaFhecor Dec 27, 2022
b7167aa
crack_min_steel_without_direct_calculation
DanielGMorenaFhecor Jan 12, 2023
7189d31
Commit
DanielGMorenaFhecor Jan 12, 2023
4a0fcfb
crack without direct calculation tests
DanielGMorenaFhecor Jan 12, 2023
84c0140
adjusted bond strength
DanielGMorenaFhecor Jan 12, 2023
e0f1baa
hc_eff_concrete_tension formulation and testing
DanielGMorenaFhecor Jan 12, 2023
f2cbb49
requiremets.txt updated
DanielGMorenaFhecor Jan 12, 2023
333dcbe
rho_p_eff
DanielGMorenaFhecor Jan 12, 2023
59f1198
kt load duration
DanielGMorenaFhecor Jan 12, 2023
34d85d2
strain diff formula
DanielGMorenaFhecor Jan 12, 2023
a8ab129
chapter completed
DanielGMorenaFhecor Jan 13, 2023
ce4e432
imports and renamed functions
DanielGMorenaFhecor Jan 13, 2023
938c0f5
removed duplicate file
DanielGMorenaFhecor Jan 13, 2023
6ba6dc9
removed testing file
DanielGMorenaFhecor Jan 13, 2023
a9c9263
test renaming and docstring corrections
DanielGMorenaFhecor Jan 16, 2023
50c65b7
pull from upstream
DanielGMorenaFhecor Feb 8, 2023
ea3552b
Merge branch 'dev' of https://github.com/fib-international/structural…
DanielGMorenaFhecor Mar 9, 2023
4fd8b7e
230309 requested changes applied
DanielGMorenaFhecor Mar 9, 2023
1cffa61
small lint fixes
DanielGMorenaFhecor Mar 9, 2023
b483d40
vscode config updated
DanielGMorenaFhecor Mar 9, 2023
e9d953d
Merge branch 'dev' of https://github.com/fib-international/structural…
DanielGMorenaFhecor May 26, 2023
182e538
Merge branch 'dev' of https://github.com/fib-international/structural…
DanielGMorenaFhecor Dec 18, 2023
e509fb9
Merge branch 'dev' of https://github.com/fib-international/structural…
DanielGMorenaFhecor Mar 4, 2024
d57f945
Merge branch 'dev' of https://github.com/fib-international/structural…
DanielGMorenaFhecor Apr 4, 2024
bc049e4
Merge branch 'dev' of https://github.com/DanielGMorenaFhecor/structur…
DanielGMorenaFhecor Jul 30, 2024
17cdf47
Merge branch 'dev' of https://github.com/fib-international/structural…
DanielGMorenaFhecor Aug 20, 2024
33ec3ee
Merge branch 'dev' of https://github.com/fib-international/structural…
Sep 25, 2024
3bdaed3
Merge remote-tracking branch 'upstream/dev' into dev
Dec 2, 2024
57d3a77
Merge branch 'dev' of https://github.com/fib-international/structural…
MestreCarlos Feb 4, 2025
a50b943
Merge branch 'dev' of https://github.com/fib-international/structural…
MestreCarlos May 22, 2025
e06c42d
Merge branch 'dev' of https://github.com/fib-international/structural…
MestreCarlos Oct 23, 2025
ed7cd74
Merge remote-tracking branch 'upstream/dev' into dev
MestreCarlos Nov 4, 2025
8738567
add get_stress_point method
MestreCarlos Nov 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions structuralcodes/core/_section_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations # To have clean hints of ArrayLike in docs

import typing as t
from dataclasses import dataclass, field, fields

import numpy as np
Expand Down Expand Up @@ -264,3 +265,25 @@ class MMInteractionDomain(InteractionDomain):

num_theta: float = 0 # number of discretizations along the angle
theta: ArrayLike = None # Array with shape (n,) containing the angle of NA


@dataclass
class PointStressResult:
"""Class for storing stress results at a specific point in a section.

Attributes:
y (float): Y-coordinate of the point.
z (float): Z-coordinate of the point.
strain (float): Strain at the point.
stress (float): Stress at the point.
geometry_name (str, optional): Name of the geometry containing the
point.
geometry_group_label (str, optional): Group label of the geometry
"""

y: float = 0.0
z: float = 0.0
strain: float = 0.0
stress: float = 0.0
geometry_name: t.Optional[str] = None
geometry_group_label: t.Optional[str] = None
80 changes: 80 additions & 0 deletions structuralcodes/sections/_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from numpy.typing import ArrayLike, NDArray
from scipy.linalg import lu_factor, lu_solve
from shapely import MultiPolygon
from shapely.geometry import Point
from shapely.ops import unary_union

import structuralcodes.core._section_results as s_res
Expand Down Expand Up @@ -1493,3 +1494,82 @@ def calculate_strain_profile(
raise StopIteration('Maximum number of iterations reached.')

return strain.tolist()

def get_stress_point(
self,
y: float,
z: float,
eps_a: float,
chi_y: float,
chi_z: float,
where: t.Literal[0, 1, 2] = 0,
) -> t.List[s_res.PointStressResult]:
"""Get the stress at a given point (y, z) inside the cross-section.

Evaluates all geometries that contain the point and returns a list
of results. The `where` parameter filters which geometry types to
evaluate.

Args:
y (float): Y-coordinate of the point.
z (float): Z-coordinate of the point.
eps_a (float): Strain at (0,0).
chi_y (float): Curvature around the Y-axis (1/mm).
chi_z (float): Curvature around the Z-axis (1/mm).
where (int): 0: all geometries; 1: surface geometries only;
2: point geometries only.

Returns:
List[PointStressResult]: List of stress results at the point
(y, z). Returns an empty list if the point is not found
within any geometry.
"""
if y is None or z is None:
return []

geom = self.section.geometry
pt = Point(y, z)
results = []

# Calculate strain at the point
# According to the codebase convention:
# eps = eps_a + chi_y * z - chi_z * y
strain = eps_a + z * chi_y - y * chi_z

# Check point geometries (reinforcement)
if where in [0, 2]:
for g in geom.point_geometries:
center = g._point
r = g._diameter / 2
poly = center.buffer(r)
if poly.contains(pt) or poly.touches(pt):
stress = g.material.constitutive_law.get_stress(strain)
results.append(
s_res.PointStressResult(
y=y,
z=z,
strain=strain,
stress=stress,
geometry_name=g.name,
geometry_group_label=g.group_label,
)
)

# Check surface geometries
if where in [0, 1]:
for g in geom.geometries:
poly = g.polygon
if poly.contains(pt) or poly.touches(pt):
stress = g.material.constitutive_law.get_stress(strain)
results.append(
s_res.PointStressResult(
y=y,
z=z,
strain=strain,
stress=stress,
geometry_name=g.name,
geometry_group_label=g.group_label,
)
)

return results
117 changes: 116 additions & 1 deletion tests/test_sections/test_generic_section.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import pytest
from shapely import Polygon

from structuralcodes import set_design_code
from structuralcodes.codes.ec2_2004 import reinforcement_duct_props
from structuralcodes.geometry import (
CircularGeometry,
Expand All @@ -20,7 +21,11 @@
ElasticPlasticMaterial,
GenericMaterial,
)
from structuralcodes.materials.concrete import ConcreteEC2_2004, ConcreteMC2010
from structuralcodes.materials.concrete import (
ConcreteEC2_2004,
ConcreteMC2010,
create_concrete,
)
from structuralcodes.materials.constitutive_laws import (
Elastic,
ElasticPlastic,
Expand All @@ -33,6 +38,7 @@
from structuralcodes.materials.reinforcement import (
ReinforcementEC2_2004,
ReinforcementMC2010,
create_reinforcement,
)
from structuralcodes.sections import (
GenericSection,
Expand Down Expand Up @@ -1658,3 +1664,112 @@ def test_issue_cracked_properties():
assert cracked_properties_before.isclose(
cracked_properties_after, rtol=1e-3, atol=1e-6
)


def test_get_stress_point():
"""Test get_stress_point method using validated results from notebook."""
# Set the active design code
set_design_code('ec2_2004')
# Create materials
concrete = create_concrete(fck=45)
reinforcement = create_reinforcement(
fyk=500, Es=200000, ftk=550, epsuk=0.07
)

# Create rectangular geometry
width = 250
height = 500
polygon = Polygon(
[
(-width / 2, -height / 2),
(width / 2, -height / 2),
(width / 2, height / 2),
(-width / 2, height / 2),
]
)
geometry = SurfaceGeometry(
poly=polygon, material=concrete, group_label='concrete'
)

# Add reinforcement
diameter_reinf = 25
cover = 50
geometry = add_reinforcement(
geometry,
(
-width / 2 + cover + diameter_reinf / 2,
-height / 2 + cover + diameter_reinf / 2,
),
diameter_reinf,
reinforcement,
group_label='bar 1',
)
geometry = add_reinforcement(
geometry,
(
width / 2 - cover - diameter_reinf / 2,
-height / 2 + cover + diameter_reinf / 2,
),
diameter_reinf,
reinforcement,
group_label='bar 2',
)

# Create section
section = GenericSection(geometry)

# Calculate strain profile
strain = section.section_calculator.calculate_strain_profile(
n=-200.0 * 1e3,
my=-200.0 * 1e6,
mz=0.0 * 1e6,
)

# Test point at reinforcement location (validated results)
y_bar = width / 2 - cover - diameter_reinf / 2 # 62.5
z_bar = -height / 2 + cover + diameter_reinf / 2 # -187.5

results = section.section_calculator.get_stress_point(
y=y_bar,
z=z_bar,
eps_a=strain[0],
chi_y=strain[1],
chi_z=strain[2],
where=0,
)

# Find bar and concrete results
bar_result = next(
(r for r in results if r.geometry_group_label == 'bar 2'), None
)
concrete_result = next(
(r for r in results if r.geometry_group_label == 'concrete'), None
)

# Validate against notebook results
assert bar_result is not None
assert math.isclose(bar_result.stress, 434.4545058908614, rel_tol=1e-3)
assert math.isclose(bar_result.strain, 0.002172272529454307, rel_tol=1e-6)

assert concrete_result is not None
assert math.isclose(concrete_result.stress, 0.0, abs_tol=1e-6)
assert math.isclose(
concrete_result.strain, 0.002172272529454307, rel_tol=1e-6
)

# Test point at top of section (validated results)
results_top = section.section_calculator.get_stress_point(
y=0,
z=height / 2, # 250.0
eps_a=strain[0],
chi_y=strain[1],
chi_z=strain[2],
where=0,
)

concrete_top = results_top[0]
assert concrete_top.geometry_group_label == 'concrete'
assert math.isclose(concrete_top.stress, -26.341523317880416, rel_tol=1e-3)
assert math.isclose(
concrete_top.strain, -0.0013015754221469022, rel_tol=1e-6
)