"""This module provides the column class implementation for the ``eu_cdh``
design class in the BDIM layer.
"""
# Imports from installed packages
from math import ceil
import numpy as np
from typing import Dict, List, Literal, Tuple
# Imports from the design class (eu_cdh) library
from .materials import Steel, Concrete
# Imports from bdim base library
from ..baselib.column import ColumnBase
# Imports from units library
from ....utils.units import MPa, m
from ....utils.rcsection import build_column_rebar_layout, get_sum_wi_sq
# Constants
ECONOMIC_MU: float = 0.25
"""Maximum mu value considered for the economic column design."""
MAX_NIU = 0.65
"""Maximum allowed value of axial load ratio.
For DCM columns, 0.65 according to Eurocode 8 - Part 1: 5.4.3.2.1(3)P
For DCH columns, 0.55 according to Eurocode 8 - Part 1: 5.5.3.2.1(3)P
"""
BETA_FC_VECTOR = [1.00, 0.93, 0.88, 0.88, 0.93]
"""Stress block coefficients for different axial load ratio (in REBAP 1983)."""
NIU_VECTOR = [0.40, 0.50, 0.60, 0.70, 0.85]
"""Axial load ratio corresponding to each stress block coefficient."""
[docs]
class Column(ColumnBase):
"""Column implementation for design class ``eu_cdh``.
This class extends ``ColumnBase`` by narrowing the attribute types
and overriding design methods per Eurocodes 2 and 8.
Attributes
----------
steel : ~simdesign.rcmrf.bdim.eu_cdh.materials.Steel
Steel material assigned to the column.
concrete : ~simdesign.rcmrf.bdim.eu_cdh.materials.Concrete
Concrete material assigned to the column.
See Also
--------
:class:`~simdesign.rcmrf.bdim.baselib.column.ColumnBase`
Base class defining the core behaviour and configuration.
References
----------
Comité Européen de Normalisation, CEN (2004). Eurocode 2: Design of
Concrete Structures — Part 1-1: General Rules and Rules for Buildings.
European Committee for Standardization, Brussels, Belgium.
Comité Européen de Normalisation, CEN (2004). Eurocode 8: Design of
Structures for Earthquake Resistance — Part 1: General Rules,
Seismic Actions and Rules for Buildings.
European Committee for Standardization, Brussels, Belgium.
d'Arga e Lima, J., Monteiro, V., Mun, M. (2005).
Betão armado: esforços normais e de flexão: REBAP-83.
Laboratório Nacional de Engenharia Civil, Lisboa.
"""
steel: Steel
concrete: Concrete
@property
def Ix_eff(self) -> float:
"""
Returns
-------
float
Effective column moment of inertia around x-axis.
"""
return 0.5 * self.Ix # EN 1998-1:2004 4.3.1(7)
@property
def Iy_eff(self) -> float:
"""
Returns
-------
float
Effective column moment of inertia around y-axis.
"""
return 0.5 * self.Iy # EN 1998-1:2004 4.3.1(7)
@property
def rhol_max(self) -> float:
"""
Returns
-------
float
Maximum longitudinal reinforcement ratio.
"""
return 0.04 # EN 1992-1-1:2004 9.5.2(3)
@property
def rhol_min(self) -> float:
"""
Returns
-------
float
Minimum longitudinal reinforcement ratio.
"""
min_tensile_force = min(
self.envelope_forces.N1_pos,
self.envelope_forces.N9_pos,
)
if min_tensile_force > 0.0: # Tensile axial force
Ned = 0.0
else: # Compressive axial force
Ned = min(
abs(self.envelope_forces.N1_neg),
abs(self.envelope_forces.N9_neg),
)
# EN 1992-1-1:2004 9.5.2(2)
rhol_min_ec2 = max(0.1 * Ned / self.fsyd, 0.002 * self.Ag) / self.Ag
# 0.01 is introduced as a rule of thumb from practical point.
# It is not a code requirement.
return max(0.01, rhol_min_ec2)
@property
def rhoh_min(self) -> float:
"""
Returns
-------
float
Minimum transverse reinforcement ratio.
Notes
-----
The equation used herein is originally defined for beams.
We keep using it for columns just to ensure safety.
"""
# EN 1992-1-1:2004, 9.2.2(5), Eqn. 9.5N
return 0.08 * ((self.fck / MPa) ** 0.5) / (self.fsyk / MPa)
[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 EC8.
"""
# Initial guess for column concrete area
min_area = self.pre_Nd / (self.fcd * MAX_NIU)
# 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.5 * self.bx
elif self.orient == 'y': # Longer dimension is by
self.by = (2 * min_area) ** 0.5
self.bx = 0.5 * 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.
Notes
-----
- In accordance with EN 1992-1-1:2004 5.4.3.2.1(2) biaxial bending
is taken into account by decreasing the uniaxial moment of resistance
by 30%.
"""
# Distance from extreme compression fiber to centroid of longitudinal
# tension reinforcement.
dx = 0.9 * self.bx
dy = 0.9 * self.by
# Following EC2-1/6.2.3: Members with vertical shear reinforcement
# Strength reduction factor for concrete cracked in shear
v = 0.6 * (1 - self.fck / (250 * MPa)) # Eqn 6.6N
if self.fsyd / self.fsyk >= 0.80:
v1 = v # Note 1: Recommended strength reduction factor
else: # Note 2: If fsyd is below 80% of fsyk
if self.fck <= 60 * MPa:
v1 = 0.6
else:
v1 = max(0.6 * (1 - self.fck / (200 * MPa)), 0.5)
# Coefficient taking account the stress state in the comp. chord
alpha_cw = 1.0 # assuming no axial force (tension case), eqn. 6.11aN
# Angle between the conc. comp. strut and the beam axis perp. to shear
theta = 21.80140948635181 # in degrees, based on eqn. 6.7N
theta = 45 # in degrees, based on eqn. 6.7N
tan_theta = np.tan(theta * np.pi / 180) # 0.4 - 1.0
cot_theta = 1 / tan_theta # 1.0 - 2.5
# lever arm, i.e., distance between comp. and tens. forces
zx = 0.9 * dx
zy = 0.9 * dy
# Assuming vertical shear reinforcement is provided: Eqn. 6.9
Vrdx = (alpha_cw * self.by * zx * v1 * self.fcd) / (
cot_theta + tan_theta
)
Vrdy = (alpha_cw * self.bx * zy * v1 * self.fcd) / (
cot_theta + tan_theta
)
# 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.fcd)
# 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
)
# Verify the adequacy of the section dimensions
if (max_mu_y / 0.70) > 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 / 0.70) > 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 longitudinal reinforcement for design forces.
Notes
-----
- In accordance with EN 1992-1-1:2004 5.4.3.2.1(2) biaxial bending
is taken into account by decreasing the uniaxial moment of resistance
by 30%.
"""
# Initial longitudinal reinforcement area
Aslx_req = 2 * np.pi * 0.25 * ((0.012 * m)**2) # Minimum reinforcement
Asly_req = 2 * np.pi * 0.25 * ((0.012 * m)**2) # Minimum reinforcement
for force in self.design_forces:
# Dimensionless design force quantities
niu_1 = (-force.N1) / (self.Ag * self.fcd)
niu_9 = (-force.N9) / (self.Ag * self.fcd)
mu_x1 = abs(force.Mx1) / ((self.bx * self.by**2) * self.fcd) / 0.7
mu_y1 = abs(force.My1) / ((self.by * self.bx**2) * self.fcd) / 0.7
mu_x9 = abs(force.Mx9) / ((self.bx * self.by**2) * self.fcd) / 0.7
mu_y9 = abs(force.My9) / ((self.by * self.bx**2) * self.fcd) / 0.7
# Determine the required longitudinal reinforcement ratio
beta1 = np.interp(niu_1, NIU_VECTOR, BETA_FC_VECTOR)
beta9 = np.interp(niu_9, NIU_VECTOR, BETA_FC_VECTOR)
lambda_y = 0.5 - self.cover / self.bx
lambda_x = 0.5 - self.cover / self.by
niuc_1 = niu_1 - 0.85
niuc_9 = niu_9 - 0.85
# Start section
if niu_1 < 0.0: # Axial force is tensile
# REBAP (1983), pp. 48, eqn. 22
omega_x1 = mu_x1 / (lambda_x * beta1) - niu_1
omega_y1 = mu_y1 / (lambda_y * beta1) - niu_1
elif niu_1 <= 0.85:
omega_x1 = (mu_x1 + 0.55 * niu_1 * niuc_1) / (lambda_x * beta1)
omega_y1 = (mu_y1 + 0.55 * niu_1 * niuc_1) / (lambda_y * beta1)
else:
omega_x1 = mu_x1 / (lambda_x * beta1) + niuc_1
omega_y1 = mu_y1 / (lambda_y * beta1) + niuc_1
# End section
if niu_9 < 0.0: # Axial force is tensile
# REBAP (1983), pp. 48, eqn. 22
omega_x9 = mu_x9 / (lambda_x * beta9) - niu_9
omega_y9 = mu_y9 / (lambda_y * beta9) - niu_9
elif niu_9 <= 0.85:
omega_x9 = (mu_x9 + 0.55 * niu_9 * niuc_9) / (lambda_x * beta9)
omega_y9 = (mu_y9 + 0.55 * niu_9 * niuc_9) / (lambda_y * beta9)
else:
omega_x9 = mu_x9 / (lambda_x * beta9) + niuc_9
omega_y9 = mu_y9 / (lambda_y * beta9) + niuc_9
# Compute required reinforcement area on two sides
omega_x = max(omega_x1, omega_x9)
omega_y = max(omega_y1, omega_y9)
Aslx = (0.5 * omega_x * self.Ag * self.fcd / self.fsyd)
Asly = (0.5 * omega_y * self.Ag * self.fcd / self.fsyd)
# Update the reinforcement area
Aslx_req = max(Aslx_req, Aslx)
Asly_req = max(Asly_req, Asly)
# Save the required longitudinal reinforcement area
# on each side, along x and y axes
self.Aslx_req = Aslx_req
self.Asly_req = Asly_req
[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 = 0.9 * self.bx
dy = 0.9 * self.by
# lever arm, i.e., distance between comp. and tens. forces
zx = 0.9 * dx
zy = 0.9 * dy
# Compression strut angle, EN 1992-1-1:2004 eqn. 6.7N
"""TODO: This could 2.0 for both beams and columns.
1.0 is not ralistic (too conservative)."""
cot_theta = 1.0 # 45 degrees
# Initial transverse reinforcement area to spacing ratio
Ashx_sh_min = self.rhoh_min * self.by
Ashy_sh_min = self.rhoh_min * self.bx
# Design forces
Ved_x = max(self.envelope_forces.Vx1, self.envelope_forces.Vx9)
Ved_y = max(self.envelope_forces.Vy1, self.envelope_forces.Vy9)
# EN 1992-1-1:2004 6.2.3(3) eqn. 6.8
Ashx_sh = Ved_x / (zx * self.fsyd * cot_theta)
Ashy_sh = Ved_y / (zy * self.fsyd * cot_theta)
# 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_sh)
self.Ashy_sbh_req = max(Ashy_sh_min, Ashy_sh)
[docs]
def check_local_ductility_requirement(
self, combo_grav_factors: List[Dict[Literal['G', 'Q'], float]],
q0: float = 3.9, T1: float | None = None, Tc: float | None = None
) -> Tuple[bool, float, float]:
"""Checks the confinement of the critical region at the base of the
column against the local ductility requirement of EN 1998-1:2004,
5.4.3.2.2(8), Eq. 5.15:
``alpha omega_wd >= 30 mu_phi nu_d eps_syd bc / b0 - 0.035``
Parameters
----------
combo_grav_factors : List[Dict[Literal['G', 'Q'], float]]
Gravity load factors of the seismic load combinations, e.g., from
the ``_get_unique_seism_combo_grav_factors`` method of the
building. The largest normalised axial force ``nu_d`` governs.
q0 : float, optional
Basic value of the behaviour factor. By default 3.9, i.e.,
``3.0 alpha_u / alpha_1`` with ``alpha_u / alpha_1 = 1.3`` for a
multi-storey, multi-bay frame of DCM (EN 1998-1:2004, 5.2.2.2).
T1 : float | None, optional
Fundamental period of the building. If None (or `Tc` is None),
``T1 >= Tc`` is assumed. By default None.
Tc : float | None, optional
Corner period of the constant spectral acceleration branch.
By default None.
Returns
-------
ok : bool
True if the requirement is satisfied.
alpha_omega_wd : float
Provided value ``alpha omega_wd``.
required : float
Required value ``30 mu_phi nu_d eps_syd bc / b0 - 0.035``.
Notes
-----
- The check applies to the critical regions at the base of primary
seismic columns; the caller selects these columns. The column
attributes are not changed.
- Curvature ductility factor (EN 1998-1:2004, 5.2.3.4(3), Eq. 5.4):
``mu_phi = 2 q0 - 1`` if ``T1 >= Tc``, otherwise
``mu_phi = 1 + 2 (q0 - 1) Tc / T1``. The periods are not estimated
by the design; without them ``T1 >= Tc`` is assumed, which gives
the smaller value for short-period buildings.
- ``omega_wd`` is the mechanical volumetric ratio of the hoops within
the confined core, ``(hoop volume / core volume) fyd / fcd``. The
core dimensions ``b0`` and ``h0`` are measured to the centreline
of the hoops.
- Confinement effectiveness factor ``alpha = alpha_n alpha_s``
(Eqs. 5.16a and 5.16b), with the distances ``bi`` between the
centres of consecutive perimeter bars from the bar layout, i.e.,
all perimeter bars are assumed to be engaged by a hoop corner or a
cross-tie.
- ``bc / b0`` is taken for the direction with the larger ratio.
References
----------
CEN (2004) Eurocode 8: Design of structures for earthquake resistance
- Part 1: General rules, seismic actions and rules for buildings.
EN 1998-1:2004. Brussels, Belgium.
"""
# Curvature ductility factor - Eq. 5.4
if T1 is None or Tc is None or T1 >= Tc:
mu_phi = 2 * q0 - 1
else:
mu_phi = 1 + 2 * (q0 - 1) * Tc / T1
# Confined core dimensions, to the centreline of the hoops
b0 = self.bx - 2 * self.cover - self.dbh # along x
h0 = self.by - 2 * self.cover - self.dbh # along y
# Confinement effectiveness factor - Eqs. 5.16a and 5.16b, with bi
# the distances between the centres of consecutive perimeter bars
layout = build_column_rebar_layout(
self.dbl_cor, self.dbl_int, int(self.nblx_int),
int(self.nbly_int), self.dbh, self.bx, self.by, self.cover)
_, sum_bi2, _ = get_sum_wi_sq(
layout['rein_x'], layout['rein_y'], layout['rein_d'], clear=False)
alpha_n = 1 - sum_bi2 / (6 * b0 * h0)
alpha_s = (1 - self.sbh / (2 * b0)) * (1 - self.sbh / (2 * h0))
alpha = alpha_n * alpha_s
# Mechanical volumetric ratio of the hoops within the core
Abh = np.pi * self.dbh**2 / 4
rho_w = Abh * (self.nbh_x * b0 + self.nbh_y * h0) / (
self.sbh * b0 * h0)
omega_wd = rho_w * self.fsyd / self.fcd
# Largest normalised axial force in the seismic design situation
nu_d = max(
-(factors['G'] * self.forces['G/seismic']
+ factors['Q'] * self.forces['Q/seismic']).N1
/ (self.Ag * self.fcd)
for factors in combo_grav_factors
)
# Design value of the yield strain of the longitudinal steel
eps_syd = self.fsyd / self.Es
# Gross to core width ratio
bc_b0 = max(self.bx / b0, self.by / h0)
# Local ductility requirement - Eq. 5.15
alpha_omega_wd = alpha * omega_wd
required = 30 * mu_phi * nu_d * eps_syd * bc_b0 - 0.035
return alpha_omega_wd >= required, alpha_omega_wd, required