"""This module provides the column class implementation for the ``tr_7599``
design class in the BDIM layer.
"""
# Imports from installed packages
import json
from math import ceil
import numpy as np
import numpy.typing as npt
from pathlib import Path
from typing import Tuple, Dict
from scipy.interpolate import RegularGridInterpolator
# Imports from the design class (tr_7599) library
from .materials import Steel, Concrete
# Imports from bdim base library
from ..baselib.column import ColumnBase
# Imports from utils library
from ....utils.units import MPa, N, m, mm
[docs]
class DesignTableTR:
"""
Design table class for Turkish design practice.
It is used for longitudinal reinforcement area calculation in
reinforced concrete rectangular column sections.
Attributes
----------
data : Dict[str, Dict[str, npt.NDArray[np.float64]]]
Dictionary containing reinforcement for varying steel grades
and axial load ratios.
References
----------
Aydin, M. R., Akgün, Ö. R., Topçu, A. Betonarme Kolon Tablolari,
Eskisehir, 1991.
"""
data: Dict[str, Dict[str, npt.NDArray[np.float64]]]
def __init__(self):
"""Initialize reinforcement area calculator."""
# Load json file
json_path = Path(__file__).parent / "data/design_tables.json"
with open(json_path, "r", encoding="utf-8") as json_file:
data = json.load(json_file)
# Convert "values" field into NumPy arrays
for key in data:
data[key]["mx"] = np.array(data[key]["mx"])
data[key]["my"] = np.array(data[key]["my"])
data[key]["omega"] = np.array(data[key]["omega"])
# Save as attribute
self.data = data
def _find_num_bars(self, b: float, h: float) -> Tuple[int, int, int]:
"""Find the number of bars placed on each side of the section.
Parameters
----------
b : float
Section width (m).
h : float
Section height (m).
Returns
-------
Tuple[int, int, int]
Number of internal bars along width,
Number of internal bars along height,
Total number of bars.
"""
cover_b = 0.1 * b # clear cover + stirrup diam. + 0.5*long.diam.
cover_h = 0.1 * h # clear cover + stirrup diam. + 0.5*long.diam.
# Finds short and long side lengths for between outermost rebars
shortside, longside = min(b - 2 * cover_b, h - 2 * cover_h), max(
b - 2 * cover_b, h - 2 * cover_h
)
# Finds the index of the closest spacing to 100 mm (assumption).
possible_spacings = shortside / np.arange(1, 10) # Possible spacings
closest_idx = np.argmin(np.abs(possible_spacings - 100))
if shortside == b - 2 * cover_b:
num_bars_int_b = closest_idx
num_bars_int_h = int(
np.floor(longside / possible_spacings[closest_idx]) - 1
)
else:
num_bars_int_h = closest_idx
num_bars_int_b = int(
np.floor(longside / possible_spacings[closest_idx]) - 1
)
num_bars = 2 * (num_bars_int_b + num_bars_int_h) + 4
return num_bars_int_b, num_bars_int_h, num_bars
def _interpolate(
self, table: Dict[str, npt.NDArray[np.float64]], mx: float, my: float
) -> float:
"""Perform bilinear interpolation to estimate reinforcement area.
Parameters
----------
data : Dict[str, npt.NDArray[np.float64]]
Design table data.
mx : float
Normalised moment in X.
my : float
Normalised moment in Y.
Returns
-------
float
Interpolated omega coefficient based on the design table and
sectional forces.
"""
# NOTE: The values outside the domain are extrapolated
interpolator = RegularGridInterpolator(
(table['mx'], table['my']), table['omega'],
method='linear', bounds_error=False, fill_value=None
)
w = interpolator([[my, mx]])[0]
return w
[docs]
def get_reinforcement(
self, Nd: float, Mxd: float, Myd: float, fcd: float,
fsyd: float, fsyk: float, bx: float, by: float
) -> Tuple[float, float]:
"""Retrieve reinforcement area from design tables with interpolation.
Parameters
----------
Nd : float
Axial load on the RC column (kN).
Mxd : float
Bending moment on the RC column in the X direction (kNm).
Myd : float
Bending moment on the RC column in the Y direction (kNm).
fcd : float
Design value of concrete strength (kPa).
fsyd : float
Design value of steel strength (kPa).
fsyk : float
Characteristic value of steel strength (kPa).
bx : float
Breadth (width) along global X (m).
by : float
Breadth (width) along global Y (m).
Returns
-------
Tuple[float, float]
Required longitudinal reinforcement area of bars
distributed along -x and -y on each side.
Notes
-----
The design tables are created assuming rebars are evenly spaced.
"""
# Convert units to N, mm
Nd = Nd / N
Mxd, Myd = Mxd / (N * mm), Myd / (N * mm)
fcd, fsyd = fcd / MPa, fsyd / MPa
bx, by = bx / mm, by / mm
# Compute normalized parameters
n = Nd / (fcd * bx * by)
mx = 100 * Mxd / (fcd * (bx * by**2))
my = 100 * Myd / (fcd * (by * bx**2))
# Retrieve w and calculate areas
n_values = np.arange(0.0, 1, 0.1)
if n in n_values: # Exact match
key = f"S{fsyk:.0f}_n={n:.1f}"
w = self._interpolate(self.data[key], mx, my)
Asl = w * (fcd / fsyd) * bx * by / 100
Asl = Asl * (mm**2) # Convert mm2 to m2
num_bars_int_b, num_bars_int_h, num_bars = self._find_num_bars(
bx, by)
rebar_area = Asl / num_bars
Asl_x = (2 + num_bars_int_b) * rebar_area
Asl_y = (2 + num_bars_int_h) * rebar_area
else:
n_lower_idx = np.searchsorted(n_values, n, side='right') - 1
n_upper_idx = n_lower_idx + 1
n_lower, n_upper = n_values[n_lower_idx], n_values[n_upper_idx]
# Read closest tables
key_lower = f"S{fsyk:.0f}_n={n_lower:.1f}"
key_upper = f"S{fsyk:.0f}_n={n_upper:.1f}"
# Perform bilinear interpolation
w_lower = self._interpolate(self.data[key_lower], mx, my)
w_upper = self._interpolate(self.data[key_upper], mx, my)
# Interpolate between n_lower and n_upper
w = np.interp(n, [n_lower, n_upper], [w_lower, w_upper])
Asl = w * (fcd / fsyd) * bx * by / 100
Asl = Asl * (mm**2) # Convert mm2 to m2
# Calculate areas in x and y direction of section
num_bars_int_b, num_bars_int_h, num_bars = self._find_num_bars(
bx, by)
rebar_area = Asl / num_bars
Asl_x = (2 + num_bars_int_b) * rebar_area
Asl_y = (2 + num_bars_int_h) * rebar_area
return Asl_x, Asl_y
ECONOMIC_MU: float = 0.25
"""Maximum mu value considered for the economic column design."""
MAX_NIU = 0.60
"""Maximum allowed value of axial load ratio.
Based on Section 8.2.6 in TS500-1984."""
table = DesignTableTR()
"""Design table data class used for computing required reinforcement area."""
[docs]
class Column(ColumnBase):
"""Column implementation for design class ``tr_7599``.
This class extends ``ColumnBase`` by narrowing the attribute types
and overriding design methods per TBEC-1975 and TS500-1984.
Attributes
----------
steel : ~simdesign.rcmrf.bdim.tr_7599.materials.Steel
Steel material assigned to the column.
concrete : ~simdesign.rcmrf.bdim.tr_7599.materials.Concrete
Concrete material assigned to the column.
MIN_B_SQUARE : float
The default minimum square column dimension.
MIN_B_RECTANGLE : float
The default minimum rectangular column dimension.
See Also
--------
:class:`~bdim.baselib.column.ColumnBase`
Base class defining the core behaviour and configuration.
References
----------
TBEC (1975). *Afet Bölgelerinde Yapılacak Yapılar Hakkında Yönetmelik*.
Resmi Gazete, Ankara, Türkiye.
TS500 (1984). *Requirements for Design and Construction of Reinforced
Concrete Structures*. Turkish Standards Institution (TSE), Ankara, Türkiye.
"""
steel: Steel
concrete: Concrete
MIN_B_SQUARE: float = 0.25 * m
MIN_B_RECTANGLE: float = 0.25 * m
@property
def fctk(self) -> float:
"""
Returns
-------
float
Characteristic value of tensional concrete strength
(in base units).
Notes
-----
Based on Section 3.3.2 in T5500-1984.
"""
return (0.35 * (self.concrete.fck) ** (1 / 2)) * MPa
@property
def fctd(self) -> float:
"""
Returns
-------
float
Design value of tensional concrete strength (in base units).
"""
return self.fctk / self.concrete.PARTIAL_FACTOR
@property
def rhol_max(self) -> float:
"""
Returns
-------
float
Maximum allowed longitudinal reinforcement ratio.
Notes
-----
Based on Section 12.3.2 in TS500-1984.
"""
return 0.04
@property
def rhol_min(self) -> float:
"""
Returns
-------
float
Minimum longitudinal reinforcement ratio.
Notes
-----
Based on Section 6.6 in TBEC-1975.
"""
return 0.01
@property
def rhoh_min(self) -> float:
"""
Returns
-------
float
Minimum transverse reinforcement ratio.
Notes
-----
Based on Equation 6.1 in TBEC-1975.
"""
return max(0.12 * self.fck / self.fsyk, 0.01)
[docs]
def predesign_section_dimensions(self) -> None:
"""Make an initial guess for column section dimensions.
Notes
-----
This method overrides ``ColumnBase.predesign_section_dimensions``
with the following changes:
- Minimum cross section area is calculated based on axial load ratio
limit from TS500-1984.
"""
# Initial guess for column concrete area
min_area = self.pre_Nd / (MAX_NIU * self.fck)
# Determine initial dimensions
if self.section == 1: # Square section
self.bx = min_area**0.5
self.by = min_area**0.5
elif self.section == 2: # Rectangular section
if self.orient == "x": # Longer dimension is bx
self.bx = (2 * min_area) ** 0.5
self.by = 0.50 * self.bx
elif self.orient == "y": # Longer dimension is by
self.by = (2 * min_area) ** 0.5
self.bx = 0.50 * self.by
# Check against minimum dimensions
self.bx = max(ceil(20 * self.bx) / 20, self.min_b)
self.by = max(ceil(20 * self.by) / 20, self.min_b)
[docs]
def verify_section_adequacy(self) -> None:
"""Verify the adequacy of section dimensions for design forces."""
# Maximum axial load ratio
max_niu = max(
self.envelope_forces.N1_pos,
self.envelope_forces.N9_pos,
abs(self.envelope_forces.N1_neg),
abs(self.envelope_forces.N9_neg),
) / (self.Ag * self.fck)
# Maximum moment ratio
max_mu_x = max(
self.envelope_forces.Mx1_pos,
self.envelope_forces.Mx9_pos,
abs(self.envelope_forces.Mx1_neg),
abs(self.envelope_forces.Mx9_neg),
) / ((self.bx * self.by**2) * self.fcd)
max_mu_y = max(
self.envelope_forces.My1_pos,
self.envelope_forces.My9_pos,
abs(self.envelope_forces.My1_neg),
abs(self.envelope_forces.My9_neg),
) / ((self.by * self.bx**2) * self.fcd)
# Maximum shear force
max_Vx = max(self.envelope_forces.Vx1, self.envelope_forces.Vx9)
max_Vy = max(self.envelope_forces.Vy1, self.envelope_forces.Vy9)
# Distance from extreme compression fiber to centroid of longitudinal
# reinforcement.
dx = 0.90 * self.bx
dy = 0.90 * self.by
# Maximum acceptable shear force # Eq. 8.49 in TS500-1984
Vrdx = 0.25 * self.fcd * self.by * dx
Vrdy = 0.25 * self.fcd * self.bx * dy
if max_mu_y > ECONOMIC_MU or max_Vx > Vrdx:
# Need to increase dimension parallel to global-x
self.ok_x = False
else:
self.ok_x = True
if max_mu_x > ECONOMIC_MU or max_Vy > Vrdy:
# Need to increase dimension parallel to global-y
self.ok_y = False
else:
self.ok_y = True
if max_niu > MAX_NIU and self.ok_x and self.ok_y:
# May increase both dimensions or random one?
self.ok_x = False
self.ok_y = False
[docs]
def compute_required_longitudinal_reinforcement(self) -> None:
"""Compute the required reinforcement area for design forces."""
Asl_x = 2 * np.pi * 0.25 * ((0.014 * m) ** 2)
Asl_y = 2 * np.pi * 0.25 * ((0.014 * m) ** 2)
for force in self.design_forces:
# Determine the required longitudinal reinforcement ratio
N1d = 0 if force.N1 > 0 else abs(force.N1)
N9d = 0 if force.N9 > 0 else abs(force.N9)
ex = 0.1 * self.bx # min. eccentr. in the x direct., TS500-1984
ey = 0.1 * self.by # min. eccentr. in the y direct., TS500-1984
Mx1d_ecc = N1d * ex
Mx9d_ecc = N9d * ex
My1d_ecc = N1d * ey
My9d_ecc = N9d * ey
Mx1d = max(abs(force.Mx1), Mx1d_ecc)
Mx9d = max(abs(force.Mx9), Mx9d_ecc)
My1d = max(abs(force.My1), My1d_ecc)
My9d = max(abs(force.My9), My9d_ecc)
Asl_1_x, Asl_1_y = table.get_reinforcement(
N1d, Mx1d, My1d, self.fcd, self.fsyd, self.steel.fsyk,
self.bx, self.by
)
Asl_9_x, Asl_9_y = table.get_reinforcement(
N9d, Mx9d, My9d, self.fcd, self.fsyd, self.steel.fsyk,
self.bx, self.by
)
Asl_x = max(Asl_x, max(Asl_1_x, Asl_9_x))
Asl_y = max(Asl_y, max(Asl_1_y, Asl_9_y))
# Save the required longitudinal reinforcement values
self.Aslx_req = Asl_x
self.Asly_req = Asl_y
[docs]
def compute_required_transverse_reinforcement(self) -> None:
"""Compute the required transverse reinforcement for design forces."""
# Distance of long. bars in tens. to extreme conc. fibers in compr.
dx = (self.bx - 2 * self.cover - 0.008)
dy = (self.by - 2 * self.cover - 0.008)
# Minimum transverse reinforcement area to spacing ratio
Ash_sh_min = self.rhoh_min * dy * dx
Ashx_sh_min = Ash_sh_min * dx / (dx + dy)
Ashy_sh_min = Ash_sh_min * dy / (dx + dy)
# Calculate the required transverse reinforcement area
Ashx_sbh_req = 0
Ashy_sbh_req = 0
for force in self.design_forces:
# Design shear forces
Vd_x = max(force.Vx1, force.Vx9)
Vd_y = max(force.Vy1, force.Vy9)
Nd = max(abs(force.N1), abs(force.N9))
# Shear force resisted by concrete, Section 8.3 in TS500-1984
Vcr_x = 0.65 * (self.fctd / MPa) * (self.by / mm) * (dx / mm) * \
(1 + 0.07 * (Nd / N) / (self.Ag / (mm**2))) / 1000
Vc_x = 0.8 * Vcr_x
Vcr_y = 0.65 * (self.fctd / MPa) * (self.bx / mm) * (dy / mm) * \
(1 + 0.07 * (Nd / N) / (self.Ag / (mm**2))) / 1000
Vc_y = 0.8 * Vcr_y
# Transverse reinforcement computation
if Vd_x <= Vcr_x:
Ashx_sh = Ashx_sh_min
else:
Vw = Vd_x - Vc_x
Ashx_sh = Vw / (self.fsyd * dx)
if Vd_y <= Vcr_y:
Ashy_sh = Ashy_sh_min
else:
Vw = Vd_y - Vc_y
Ashy_sh = Vw / (self.fsyd * dy)
# Save the required ratio of transverse reinforcement area along
# x and y axes to the reinforcement spacing
Ashx_sbh_req = max(Ashx_sbh_req, Ashx_sh)
Ashy_sbh_req = max(Ashy_sbh_req, Ashy_sh)
# Save the required ratio of transverse reinforcement area along
# x and y axes to the reinforcement spacing
self.Ashx_sbh_req = max(Ashx_sh_min, Ashx_sbh_req)
self.Ashy_sbh_req = max(Ashy_sh_min, Ashy_sbh_req)