"""This module provides the beam class implementation for the ``tr_post18_dch``
design class in the BDIM layer.
"""
# Imports from installed packages
from math import ceil
import numpy as np
from typing import List, Tuple, Optional
# Imports from the design class (tr_post18) library
from .materials import Steel, Concrete
# Imports from bdim base library
from ..baselib.beam import BeamBase, BeamForces, BeamEnvelopeForces, Array3
# Imports from units library
from ....utils.units import MPa, m, mm
# 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."""
[docs]
class Beam(BeamBase):
"""Beam implementation for design class ``tr_post18_dch``.
This class extends ``BeamBase`` by narrowing the attribute types
and overriding design methods per TBEC-2018 and TS500-2000.
Attributes
----------
steel : ~simdesign.rcmrf.bdim.tr_post18_dch.materials.Steel
Steel material assigned to the beam.
concrete : ~simdesign.rcmrf.bdim.tr_post18_dch.materials.Concrete
Concrete material assigned to the beam.
design_forces_overstrength_adjusted : List[BeamForces]
List of forces obtained each load combination (design forces).
MIN_B_EB: float
The default minimum breadth (width) of emergent beams.
MIN_H_EB: float
The default minimum height (depth) of emergent beams.
Ve1 : float | None
Beam capacity design shear force at 1st gaussian point
Ve9 : float | None
Beam capacity design shear force at 9st gaussian point
See Also
--------
:class:`~bdim.baselib.beam.BeamBase`
Base class defining the core behaviour and configuration.
References
----------
TBEC (2018). *Deprem Etkisi Altında Binaların Tasarımı için Esaslar*.
Resmi Gazete, Türkiye.
TS500 (2000). *Requirements for Design and Construction of Reinforced
Concrete Structures*. Turkish Standards Institution (TSE), Ankara, Türkiye.
"""
steel: Steel
concrete: Concrete
design_forces_overstrength_adjusted: List[BeamForces]
MIN_B_EB: float = 0.25 * m
MIN_H_EB: float = 0.30 * m
Ve1: Optional[float] = None
Ve9: Optional[float] = None
@property
def max_b(self) -> float:
"""
Returns
-------
float
Computed maximum allowed section breadth (width).
Notes
-----
Based on Section 7.4.1 in TBEC-2018.
"""
if self.direction == "x": # Beam is along x
bc = max(col.by for col in self.columns if col)
elif self.direction == "y": # Beam is along y
bc = max(col.bx for col in self.columns if col)
b_max_code = bc + self.h
# Masks for finding emergent beams
bool1 = self.typology == 2
bool2 = self.exterior
bool3 = self.stairs_wg != 0.0
if bool1 or bool2 or bool3:
return min(b_max_code, self.MAX_B_EB)
else:
return min(b_max_code, self.MAX_B_WB)
@property
def max_h(self) -> float:
"""
Returns
-------
float
Computed maximum allowed section height (depth).
Notes
-----
Based on Section 7.4.1 in TBEC-2018.
"""
# Masks for finding emergent beams
bool1 = self.typology == 2 # Emergent by default
bool2 = self.exterior # Forces exterior beams to emergent
bool3 = self.stairs_wg != 0.0 # Forces stairs beams to be emergent
if bool3:
return self.MAX_H_EB
else:
if self.direction == 'x': # Beam is along x
bxmax = max(col.bx for col in self.columns if col)
bxmin = min(col.bx for col in self.columns if col)
Lnet = self.L - (bxmax + bxmin) / 2
elif self.direction == 'y': # Beam is along y
bymax = max(col.by for col in self.columns if col)
bymin = min(col.by for col in self.columns if col)
Lnet = self.L - (bymax + bymin) / 2
h_max_code = max(3.5 * self.b, Lnet / 4)
if bool1 or bool2:
return min(self.MAX_H_EB, h_max_code)
else:
return min(self.MAX_H_WB, h_max_code)
@property
def fctk(self) -> float:
"""
Returns
-------
float
Characteristic tensional strength of concrete (in base units).
Notes
-----
Based on Equation 3.1 in T5500-2000.
"""
return (0.35 * (self.concrete.fck) ** (1 / 2)) * MPa
@property
def fctd(self) -> float:
"""
Returns
-------
float
Design value for characteristic tensional strength of concrete
(in base units).
"""
return self.fctk / self.concrete.PARTIAL_FACTOR
@property
def Iy_eff(self) -> float:
"""
Returns
-------
float
Moment of inertia around y-axis of the beam.
Notes
-----
Based on Table 4.2 in TBEC-2018.
"""
return 0.35 * self.Iy
@property
def Iz_eff(self) -> float:
"""
Returns
-------
float
Moment of inertia around z-axis of the beam.
Notes
-----
Based on Table 4.2 in TBEC-2018.
"""
return 0.35 * self.Iz
@property
def rhol_min_tens(self) -> float:
"""
Returns
-------
float
Minimum longitudinal reinforcement ratio in tension zone.
Notes
-----
Based on Equation 7.8 in TBEC-2018.
"""
return 0.8 * (self.fctd / self.fsyd)
@property
def rhol_max_tens(self) -> float:
"""
Returns
-------
float
Maximum longitudinal reinforcement ratio in tens. and comp. zones.
Notes
-----
Based on Section 7.4.2 in TBEC-2018.
"""
return 0.02
@property
def rhoh_min(self) -> float:
"""
Returns
-------
float
Minimum transverse reinforcement ratio.
Notes
-----
Based on Equation 8.6 in T5500-2000.
"""
return 0.3 * (self.fctd) / (self.fsyd)
@property
def envelope_forces_overstrength_adjusted(self) -> BeamEnvelopeForces:
"""
Returns
-------
BeamEnvelopeForces
Returns the envelope forces computed from `combo_forces`.
"""
# Get a list of all attributes
attributes = ["M1", "M5", "M9", "V1", "V5", "V9"]
# Find minimum and maximum for each attribute
min_values = [
min(
getattr(force, attr)
for force in self.design_forces_overstrength_adjusted
)
for attr in attributes
]
max_values = [
max(
getattr(force, attr)
for force in self.design_forces_overstrength_adjusted
)
for attr in attributes
]
return BeamEnvelopeForces(
M1_neg=min(min_values[0], 0.0),
M5_neg=min(min_values[1], 0.0),
M9_neg=min(min_values[2], 0.0),
M1_pos=max(max_values[0], 0.0),
M5_pos=max(max_values[1], 0.0),
M9_pos=max(max_values[2], 0.0),
V1=max(max_values[3], abs(min_values[3])),
V5=max(max_values[4], abs(min_values[4])),
V9=max(max_values[5], abs(min_values[5])),
)
[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:
- It uses additional constraint on beam height based on TBEC-2018.
"""
# Unit conversions
Md = self.pre_Md
# 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
if self.stairs_wg != 0.0 or sum(self.slab_wg) != 0.0:
# The beam carries a slab (stairs or floor slab)
def_h = self.L / 12
else: # The beam is secondary gravity beam
def_h = self.L / (0.9 * 18)
# Compute height to provide support condition for slabs
bh_s = 3 * slab_h
# Get the maximum slab computed from all
self.h = max(self.min_h, bh_s, 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
if self.stairs_wg != 0.0 or sum(self.slab_wg) != 0.0:
# The beam carries a slab (stairs or floor slab)
def_h = self.L / 12
else: # The beam is secondary gravity beam
def_h = self.L / (0.9 * 18)
# Get the maximum slab computed from all
self.h = max(self.min_h, bh_s, 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."""
# mu values (dimensionless) for economic section (eng. practice)
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.90 * self.h
# Maximum of envelope forces
if self.Ve1 is not None and self.Ve9 is not None:
Vmax = max(self.Ve1, self.Ve9)
else:
Vmax = max(
self.envelope_forces.V1,
self.envelope_forces.V5,
self.envelope_forces.V9,
)
Mmax = max(
self.envelope_forces.M1_pos,
self.envelope_forces.M5_pos,
self.envelope_forces.M9_pos,
abs(self.envelope_forces.M1_neg),
abs(self.envelope_forces.M5_neg),
abs(self.envelope_forces.M9_neg),
)
# Verify the adequacy of the section dimensions
mu = Mmax / (self.fcd * self.b * d**2) # For max. bending moment
Vrd_max = (0.85 * (self.b / mm) * (d / mm)
* np.sqrt(self.fck / MPa) / 1000) # Eq. 7.10 in TBEC-2018
if mu < mu_economic and Vmax < Vrd_max:
self.ok = True # Ok
else:
self.ok = False # Not ok
def _get_long_area(self, Md: Array3
) -> Tuple[Array3[np.float64], Array3[np.float64]]:
"""Get longitudinal reinforcement area given bending moment.
Parameters
----------
Md : np.ndarray
Moment value to be used for beam design.
Returns
-------
As_required : np.ndarray
Required tension steel area.
Asprime_required : np.ndarray
Required compression steel area.
"""
# Initial material and geometrical definitions
fcd = self.fcd
fyd = self.fsyd
Es = self.Es
bw = self.b
dprime = 0.1 * self.h
d = self.h - dprime
# Definition of k1 and k3
k1 = min(1 - 0.006 * self.fck / MPa, 0.85)
k3 = 0.85
# Bending moment calculation for the choice between single
# and double reinforcement.
Mr1 = 0.235 * fcd * bw * (d**2) * (1 - 0.1175 / k3)
As_required = np.zeros(len(Md))
Asprime_required = np.zeros(len(Md))
mask = Md < Mr1
if np.any(mask): # Single reinforcement
K = Md[mask] / (bw * d**2)
rho = k3 * (fcd / fyd) * (1 - (1 - (2 * K) / (k3 * fcd)) ** 0.5)
As_required[mask] = rho * bw * d
Asprime_required[mask] = 0
else: # Double reinforcement
As1 = 0.235 * (fcd / fyd) * bw * d
Mr2 = Md[~mask] - Mr1
As2 = Mr2 / (fyd * (d - dprime))
epss_prime = 0.003 * (1 - (k1 * k3 / 0.235) * (dprime / d))
sigmas_prime = epss_prime * Es
if sigmas_prime >= fyd:
sigmas_prime = fyd
else:
sigmas_prime = sigmas_prime
As_required[~mask] = As1 + As2
Asprime_required[~mask] = As2 * fyd / sigmas_prime
return As_required, Asprime_required
[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.
"""
# Design forces
moment_pos = np.array(
[
self.envelope_forces.M1_pos,
self.envelope_forces.M5_pos,
self.envelope_forces.M9_pos,
]
)
moment_neg = np.array(
[
self.envelope_forces.M1_neg,
self.envelope_forces.M5_neg,
self.envelope_forces.M9_neg,
]
)
moment_neg = np.abs(moment_neg)
# Required area for positive moment envelope (+)
Asl_pos_bot, Asl_pos_top = self._get_long_area(moment_pos)
# Required area for negative moment envelope (-)
Asl_neg_top, Asl_neg_bot = self._get_long_area(moment_neg)
# Determine required reinforcement at top and bottom
Asl_bot = np.maximum(Asl_pos_bot, Asl_neg_bot)
Asl_top = np.maximum(Asl_neg_top, Asl_pos_top)
# Check against minimum longitudinal reinforcement area
Asl_min_tens = self.rhol_min_tens * self.b * (0.9 * self.h)
Asl_top = np.maximum(Asl_top, Asl_min_tens)
Asl_bot = np.maximum(Asl_bot, Asl_min_tens)
# Compression to tension reinf. ratio must be greater than 0.5,
# Section 7.4.2 in TBEC2018
mask = Asl_top / Asl_bot < 0.5
if any(mask):
Asl_top[mask] = 0.5 * Asl_bot[mask]
mask = Asl_bot / Asl_top < 0.5
if any(mask):
Asl_bot[mask] = 0.5 * Asl_top[mask]
# Save required longitudinal steel area at top and bottom
self.Asl_top_req = Asl_top
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.
"""
# Shear forces due to gravity and earthquake loads
Vd = np.array([self.envelope_forces.V1, self.envelope_forces.V5,
self.envelope_forces.V9])
# Design shear force
Ve = np.array([self.Ve1, self.envelope_forces.V5, self.Ve9])
# Shear force due to gravity loads
grav_forces = self.forces["G/seismic"] + self.forces["Q/seismic"]
Vd_gravity = np.array([abs(grav_forces.V1), abs(grav_forces.V5),
abs(grav_forces.V9)])
# Shear force resisted by concrete, Eq.8.1 in TS500-2000
Vcr = 0.65 * self.fctd * self.b * (0.9 * self.h)
Vc = 0.8 * Vcr
# Transverse reinforcement computation, Section 7.4.5 in TBEC-2018
Ash_sbh = np.zeros(len(Ve))
# Ve <= Vcr case
mask = Ve <= Vcr
Ash_sbh[mask] = self.rhoh_min * self.b
# Ve > Vcr case
shear_force_for_reinforcement = np.where((Vd - Vd_gravity) >= 0.5 * Vd,
Ve, Ve - Vc)
Ash_sbh[~mask] = shear_force_for_reinforcement[~mask] / \
(self.fsyd * (0.9 * self.h))
# Save required transverse reinforcement area to spacing ratio
Ash_sbh_min = self.rhoh_min * self.b # Min. transverse reinforcement
self.Ash_sbh_req = np.maximum(Ash_sbh, Ash_sbh_min)