Source code for simdesign.rcmrf.bdim.eu_cdl.beam

"""This module provides the beam class implementation for the ``eu_cdl``
design class in the BDIM layer.
"""
# Imports from installed packages
from math import ceil
import numpy as np

# Imports from the design class (eu_cdl) library
from .materials import Steel, Concrete

# Imports from bdim base library
from ..baselib.beam import BeamBase

# Imports from units library
from ....utils.units import kN, MPa, m

# Constants
ECONOMIC_MU_EB: float = 0.25
"""Maximum mu value considered for the economic emergent beam design."""
ECONOMIC_MU_WB: float = 0.25
"""Maximum mu value considered for the economic wide beam design."""
TAU_C_VECT = np.array([0.4, 0.45, 0.50, 0.55, 0.60]) * MPa
"""Vector of allowable shear stresses that carried by the concrete or
vector of the design shear strength values of concrete."""
FCK_CUBE_VECT = np.array([180, 225, 300, 350, 400])
"""Vector of cubic concrete compressive strength values (kg/cm2)."""
TAU_MAX_VECT = np.array([2.4, 2.7, 3.0, 3.3, 3.6]) * MPa
"""Vector of allowable shear stresses that can be carried by
the beam section."""
MODULAR_RATIO = 15
"""Assumed steel to concrete elastic modular ratio for reinf. computation."""


[docs] class Beam(BeamBase): """Beam implementation for design class ``eu_cdl``. This class extends ``BeamBase`` by narrowing the attribute types and overriding design methods per REBA (1967). Attributes ---------- steel : ~simdesign.rcmrf.bdim.eu_cdl.materials.Steel Steel material assigned to the beam. concrete : ~simdesign.rcmrf.bdim.eu_cdl.materials.Concrete Concrete material assigned to the beam. See Also -------- :class:`~bdim.baselib.beam.BeamBase` Base class defining the core behaviour and configuration. References ---------- REBA (1967). Regulamento de Estruturas de Betão Armado. Decreto N.° 47:723, Lisbon, Portugal. """ steel: Steel concrete: Concrete @property def fcd_eq(self) -> float: """ Returns ------- float Seismic design concrete compressive strength (in base units). """ return self.concrete.fcd_eq * MPa @property def fsyd_eq(self) -> float: """ Returns ------- float Seismic design steel yield strength (in base units). """ return self.steel.fsyd_eq * MPa
[docs] def predesign_section_dimensions(self, slab_h: float) -> None: """Make an initial guess for beam section dimensions. Parameters ---------- slab_h : float Slab thickness. Notes ----- This method overrides ``BeamBase.predesign_section_dimensions`` with the following changes: - Uses a single expression for computing the height to control emergent beam deformations under gravity loads, assuming ``d' = 0.1h`` for the cover depth. """ # Unit conversions Md = self.pre_Md * kN * m # Emergent beam cases bool1 = self.typology == 2 bool2 = self.exterior bool3 = self.stairs_wg != 0.0 if bool1 or bool2 or bool3: # Set section breadth to minimum self.b = self.min_b # Compute height for economic section, assuming d = 0.1h mu_h = ((Md / (ECONOMIC_MU_EB * self.fcd * self.b))**0.5) / 0.9 # Compute height to control deformations def_h = self.L / (0.9 * 18) # Get the maximum slab computed from all self.h = max(self.min_h, slab_h, mu_h, def_h) # Iterate for aspect ratio consideration while self.h / self.b > self.MAX_ASPECT_RATIO_EB: # Increase breadth self.b += self.B_INCR_EB # Compute height for economic section, assuming d = 0.1h mu_h = ((Md / (ECONOMIC_MU_EB * self.fcd * self.b))**0.5) / 0.9 # Compute height to control deformations def_h = self.L / (0.9 * 18) # Get the maximum slab computed from all self.h = max(self.min_h, slab_h, mu_h, def_h) # Wide beam cases else: # Set section height (slab thickness or minimum) self.h = max(slab_h, self.min_h) # Section widths if sum(self.slab_wg) == 0.0: # Secondary gravity beams self.b = self.min_b # Use minimum dimension else: # Primary gravity beams # Set width based on economic mu value and minimum allowed self.b = max( self.min_b, (Md / (ECONOMIC_MU_WB * self.fcd * (0.9 * self.h) ** 2)), ) while (self.b > self.max_b or self.b / self.h > self.MAX_ASPECT_RATIO_WB): self.h += self.H_INCR_WB self.b = Md / ( ECONOMIC_MU_WB * self.fcd * (0.9 * self.h) ** 2 ) # Round self.h = ceil(20 * self.h) / 20 self.b = ceil(20 * self.b) / 20
[docs] def verify_section_adequacy(self) -> None: """Verify the beam section dimensions for design forces. """ # Design strength mapper fcd_map = {'gravity': self.fcd, 'seismic': self.fcd_eq} # Allowable shear stress that can be carried by the beam tau_max = np.interp(self.concrete.fck_cube, FCK_CUBE_VECT, TAU_MAX_VECT) # Economic mu values (dimensionless) if self.typology == 1: mu_economic = ECONOMIC_MU_WB elif self.typology == 2: mu_economic = ECONOMIC_MU_EB # Distance from extreme compression fiber to centroid of longitudinal # tension reinforcement. d = 0.9 * self.h # lever arm, i.e., distance between comp. and tens. forces z = 0.9 * d # Maximum of envelope forces max_shear = max(self.envelope_forces.V1, self.envelope_forces.V5, self.envelope_forces.V9) # Verify the adequacy of the section dimensions tau = max_shear / (self.b * z) # for max. shear force mu = 0.0 # for max. bending moment for forces in self.design_forces: # Design moments env_pos = max(forces.M1, forces.M5, forces.M9, 0.0) env_neg = min(forces.M1, forces.M5, forces.M9, 0.0) max_moment = max(env_pos, abs(env_neg)) # Design strength fcd = fcd_map.get(forces.case) mu = max(mu, max_moment / (fcd * self.b * d**2)) if mu < mu_economic and tau < tau_max: self.ok = True # Ok else: self.ok = False # Not ok
[docs] def compute_required_longitudinal_reinforcement(self) -> None: """Compute the required longitudinal reinforcement for design forces. Notes ----- - Top reinforcement is calculated as the maximum of required reinforcement in tension for maximum of negative bending moments and required reinforcement in compression for maximum of positive bending moments. - Bottom reinforcement is calculated as the maximum of required reinforcement in compression for maximum of negative bending moments and required reinforcement in tension for maximum of positive bending moments. - Required reinforcement is computed at three different sections: start, middle, end.rcement is computed at different sections: start, mid, end. References ---------- https://mathalino.com/reviewer/reinforced-concrete-design/design-steel-reinforcement-concrete-beams-wsd-method """ # Design strength mappers fcd_map = {'gravity': self.fcd, 'seismic': self.fcd_eq} fsyd_map = {'gravity': self.fsyd, 'seismic': self.fsyd_eq} # Distance from extreme compression fiber to centroid of longitudinal # tension reinforcement. d = 0.9 * self.h d_prime = 0.1 * self.h # Alternatively, this can be directly computed. n = MODULAR_RATIO # Modular ratio self.Asl_top_req = np.zeros(3) # Required steel area at top self.Asl_bot_req = np.zeros(3) # Required steel area at bottom # Loop through each case because strength depends on the load case for forces in self.design_forces: # Design strength of materials fcd = fcd_map.get(forces.case) fsyd = fsyd_map.get(forces.case) # Design forces moment_pos = np.array( [max(forces.M1, 0.0), max(forces.M5, 0.0), max(forces.M9, 0.0)] ) moment_neg = np.array( [min(forces.M1, 0.0), min(forces.M5, 0.0), min(forces.M9, 0.0)] ) moment_neg = np.abs(moment_neg) # No need for the sign # Balanced moment capacity x_bal = (fcd * d) / (fcd + fsyd / n) C_bal = 0.50 * fcd * self.b * x_bal M_bal = C_bal * (d - x_bal / 3) # Initialize long. steel area at start, mid and end sections Asl_top = np.zeros(3) # Required steel area at top Asl_bot = np.zeros(3) # Required steel area at bottom # 1) Calculate longitudinal steel area for negative envelope (-) mask1 = moment_neg <= M_bal # Identify singly reinforced beams # Excessive moment (doubly reinforced beam case) Mexcess = moment_neg[~mask1] - M_bal # Tension reinforcement (singly reinforced beam) Asl_top[mask1] = moment_neg[mask1] / ( fsyd * (d - x_bal / 3)) # As1 (Doubly reinforced beam) Asl1 = moment_neg[~mask1] / (fsyd * (d - x_bal / 3)) # As2 (doubly reinforced beam) --> Corrected Asl2 = Mexcess / (fsyd * (d - d_prime)) # Total tension reinforcement (doubly reinforced beam) Asl_top[~mask1] = Asl1 + Asl2 # Maximum stress of the compression reinforcement (doubly) fsyd_prime = min( fsyd, (2 * fsyd * (x_bal - d_prime)) / (d - x_bal) ) # Compression reinforcement (doubly reinforced beam) Asl_bot[~mask1] = (2 * n * Mexcess) / ( fsyd_prime * (2 * n - 1) * (d - d_prime) ) # 2) Calculate longitudinal steel area for positive envelope (+) mask2 = moment_pos <= M_bal # Identify singly reinforced beams # Excessive moment (doubly reinforced beam case) Mexcess = moment_pos[~mask2] - M_bal # Tension reinforcement (singly reinforced beam) Asl_bot[mask2] = np.maximum( Asl_bot[mask2], moment_pos[mask2] / (fsyd * (d - x_bal / 3)) ) # As1 (doubly reinforced beam) Asl1 = moment_pos[~mask2] / (fsyd * (d - x_bal / 3)) # As2 (doubly reinforced beam) --> Corrected Asl2 = Mexcess / (fsyd * (d - d_prime)) # Total tension reinforcement (doubly reinforced beam) Asl_bot[~mask2] = np.maximum(Asl1 + Asl2, Asl_bot[~mask2]) # Maximum stress of the compression reinforcement (doubly) fsyd_prime = min( fsyd, (2 * fsyd * (x_bal - d_prime)) / (d - x_bal) ) # Compression reinforcement (doubly reinforced beam) Asl_top[~mask2] = np.maximum( (2 * n * Mexcess) / (fsyd_prime * (2 * n - 1) * (d - d_prime)), Asl_top[~mask2], ) # Update required longitudinal reinforcement area self.Asl_top_req = np.maximum(self.Asl_top_req, Asl_top) self.Asl_bot_req = np.maximum(self.Asl_bot_req, Asl_bot)
[docs] def compute_required_transverse_reinforcement(self) -> None: """Compute the required transverse reinforcement for design forces. Notes ----- Reinforcement is computed at three sections: start, mid, and end. """ # Design strength mappers fsyd_map = {'gravity': self.fsyd, 'seismic': self.fsyd_eq} # Allowable shear stress that can be carried by the beam tau_c = np.interp(self.concrete.fck_cube, FCK_CUBE_VECT, TAU_C_VECT) # Distance from extreme compression fiber to centroid of longitudinal # tension reinforcement. d = 0.9 * self.h # Lever arm, i.e., distance between comp. and tens. forces z = 0.9 * d # The minimum shear reinforcement sbh = 0.5 # stirrup spacing dbh = 0.006 # stirrup diameter nlegs = 2 # number of legs Ash_sbh_min = nlegs * (np.pi * 0.25 * dbh**2) / sbh # The required transverse reinforcement area to spacing at each section self.Ash_sbh_req = np.zeros(3) # Loop through each case because strength depends on the load case for forces in self.design_forces: # Design strength of materials fsyd = fsyd_map.get(forces.case) # Design forces shear = np.array([abs(forces.V1), abs(forces.V5), abs(forces.V9)]) # Changed this to the available long. reinforcement. Asl_top = self.rhol_top * self.Ag Asl_bot = self.rhol_bot * self.Ag # Check this expression because it can result in negative reinf. # Added minimum reinforcement to avoid negative reinf. situation. Vcd = tau_c * self.b * d mask = ( z * fsyd * np.maximum(Asl_top, Asl_bot) < shear * d ) # Article 35 Ash_sbh = shear / (fsyd * d) Ash_sbh[~mask] = (shear[~mask] - Vcd) / (fsyd * d) Ash_sbh = np.maximum(Ash_sbh, Ash_sbh_min) # Save the required transverse reinforcement area to spacing self.Ash_sbh_req = np.maximum(self.Ash_sbh_req, Ash_sbh)