From 96fa9866c3c06fce2279a64c8d0446fa1a397975 Mon Sep 17 00:00:00 2001 From: Morten Engen Date: Thu, 21 May 2026 22:29:10 +0200 Subject: [PATCH 1/2] Factor out lines related to outer loop from inner loop --- structuralcodes/sections/_beam_section.py | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/structuralcodes/sections/_beam_section.py b/structuralcodes/sections/_beam_section.py index 07aa8936..bff879a8 100644 --- a/structuralcodes/sections/_beam_section.py +++ b/structuralcodes/sections/_beam_section.py @@ -320,25 +320,25 @@ def get_balanced_failure_strain( rotated frame y*z* it is a case of uniaxial bending). """ chi_min = 1e10 + # Check if the section is a reinforced concrete section: + # If it is, we need to obtain the "yield" strain of concrete + # (-0.002 for default parabola-rectangle concrete) + is_rc_section = self.section.geometry.reinforced_concrete for g in geom.geometries + geom.point_geometries: + # This is left on purpose: even if tempted we should not do + # this check: + # if g != other_g: + eps_p = g.material.constitutive_law.get_ultimate_strain( + yielding=yielding + )[1] + if isinstance(g, SurfaceGeometry): + y_p = g.polygon.bounds[1] + elif isinstance(g, PointGeometry): + y_p = g._point.coords[0][1] for other_g in geom.geometries + geom.point_geometries: - # This is left on purpose: even if tempted we should not do - # this check: - # if g != other_g: - eps_p = g.material.constitutive_law.get_ultimate_strain( - yielding=yielding - )[1] - if isinstance(g, SurfaceGeometry): - y_p = g.polygon.bounds[1] - elif isinstance(g, PointGeometry): - y_p = g._point.coords[0][1] - # Check if the section is a reinforced concrete section: - # If it is, we need to obtain the "yield" strain of concrete - # (-0.002 for default parabola-rectangle concrete) # If the geometry is not concrete, don't get the yield strain # If it is not a reinforced concrete section, return # the yield strain if asked. - is_rc_section = self.section.geometry.reinforced_concrete is_concrete_geom = ( isinstance(other_g, SurfaceGeometry) and other_g.concrete ) From 032491293d044bceef6857ea7b99c5d0c36b4532 Mon Sep 17 00:00:00 2001 From: Morten Engen Date: Wed, 10 Jun 2026 08:20:23 +0200 Subject: [PATCH 2/2] Factor out queries from double for loop --- structuralcodes/sections/_beam_section.py | 89 +++++++++++++++-------- 1 file changed, 59 insertions(+), 30 deletions(-) diff --git a/structuralcodes/sections/_beam_section.py b/structuralcodes/sections/_beam_section.py index bff879a8..07246196 100644 --- a/structuralcodes/sections/_beam_section.py +++ b/structuralcodes/sections/_beam_section.py @@ -324,52 +324,81 @@ def get_balanced_failure_strain( # If it is, we need to obtain the "yield" strain of concrete # (-0.002 for default parabola-rectangle concrete) is_rc_section = self.section.geometry.reinforced_concrete - for g in geom.geometries + geom.point_geometries: - # This is left on purpose: even if tempted we should not do - # this check: - # if g != other_g: - eps_p = g.material.constitutive_law.get_ultimate_strain( - yielding=yielding - )[1] + + # Pre-process all data to avoid expensive calls in the double loop + # below + geom_strain_data = {} + all_geometries = geom.geometries + geom.point_geometries + + for g in all_geometries: + # Initialize empty dict + geom_strain_data.setdefault( + g, + { + 'eps_p': None, + 'eps_n': None, + 'y_p': None, + 'y_n': None, + }, + ) + + # Find ultimate strain given the 'yielding' arg to the function + geom_strain_data[g]['eps_p'] = ( + g.material.constitutive_law.get_ultimate_strain( + yielding=yielding + )[1] + ) + + # Find bounding coordinate if isinstance(g, SurfaceGeometry): - y_p = g.polygon.bounds[1] + geom_strain_data[g]['y_p'] = g.polygon.bounds[1] elif isinstance(g, PointGeometry): - y_p = g._point.coords[0][1] - for other_g in geom.geometries + geom.point_geometries: - # If the geometry is not concrete, don't get the yield strain - # If it is not a reinforced concrete section, return - # the yield strain if asked. - is_concrete_geom = ( - isinstance(other_g, SurfaceGeometry) and other_g.concrete - ) + geom_strain_data[g]['y_p'] = g._point.coords[0][1] - use_yielding = ( - yielding - if ( - (is_rc_section and is_concrete_geom) - or (not is_rc_section) - ) - else False + if isinstance(g, SurfaceGeometry): + geom_strain_data[g]['y_n'] = g.polygon.bounds[3] + elif isinstance(g, PointGeometry): + geom_strain_data[g]['y_n'] = g._point.coords[0][1] + + # If the geometry is not concrete, don't get the yield strain + # If it is not a reinforced concrete section, return + # the yield strain if asked. + is_concrete_geom = isinstance(g, SurfaceGeometry) and g.concrete + + use_yielding = ( + yielding + if ( + (is_rc_section and is_concrete_geom) or (not is_rc_section) ) + else False + ) - eps_n = other_g.material.constitutive_law.get_ultimate_strain( + geom_strain_data[g]['eps_n'] = ( + g.material.constitutive_law.get_ultimate_strain( yielding=use_yielding )[0] - - if isinstance(other_g, SurfaceGeometry): - y_n = other_g.polygon.bounds[3] - elif isinstance(other_g, PointGeometry): - y_n = other_g._point.coords[0][1] + ) + for g, this_geom_strain_data in geom_strain_data.items(): + # This is left on purpose: even if tempted we should not do + # this check: + # if g != other_g: + eps_p = this_geom_strain_data['eps_p'] + y_p = this_geom_strain_data['y_p'] + for _, other_geom_strain_data in geom_strain_data.items(): + eps_n = other_geom_strain_data['eps_n'] + y_n = other_geom_strain_data['y_n'] if y_p >= y_n: continue chi = -(eps_p - eps_n) / (y_p - y_n) - # print(y_p,eps_p,y_n,eps_n,chi) + if chi < chi_min: chi_min = chi eps_0 = eps_n + chi_min * y_n y_n_min = y_n y_p_min = y_p + y_p, y_n = y_p_min, y_n_min + # In standard CRS negative curvature stretches bottom fiber strain = [eps_0, -chi_min, 0] return (y_n, y_p, strain)