| """
|
| Rectangular Beam Nominal Moment Strength Calculator
|
| Based on ACI 318 Provisions (Example 4-1 and 4-1M)
|
|
|
| Variables:
|
| fc' - Concrete compressive strength
|
| fy - Steel yield strength
|
| Es - Modulus of elasticity of steel
|
| b - Beam width
|
| h - Total beam depth
|
| d - Effective depth (to centroid of tension steel)
|
| As - Total area of tension steel
|
| beta1 - Stress block factor
|
| epsilon_cu - Ultimate concrete strain (0.003)
|
|
|
| Calculated:
|
| T - Tension force in steel
|
| a - Depth of equivalent stress block
|
| c - Neutral axis depth
|
| epsilon_y - Yield strain of steel
|
| epsilon_s - Strain in steel at ultimate
|
| Mn - Nominal moment strength
|
| As_min - Minimum steel area per ACI
|
| """
|
|
|
| import math
|
|
|
|
|
| class RectangularBeam:
|
| def __init__(
|
| self,
|
| b: float,
|
| h: float,
|
| d: float,
|
| fc: float,
|
| fy: float,
|
| n_bars: int,
|
| bar_area: float,
|
| Es: float = None,
|
| beta1: float = None,
|
| epsilon_cu: float = 0.003,
|
| unit_system: str = "imperial"
|
| ):
|
| """
|
| Initialize the Rectangular Beam.
|
|
|
| Args:
|
| b: Beam width (in or mm)
|
| h: Total beam depth (in or mm)
|
| d: Effective depth (in or mm)
|
| fc: Concrete compressive strength (psi or MPa)
|
| fy: Steel yield strength (psi or MPa)
|
| n_bars: Number of reinforcement bars
|
| bar_area: Area of each bar (in2 or mm2)
|
| Es: Modulus of elasticity of steel (psi or MPa). Default based on unit system.
|
| beta1: Stress block factor. Default calculated from fc.
|
| epsilon_cu: Ultimate concrete strain. Default 0.003.
|
| unit_system: 'imperial' (psi, in) or 'si' (MPa, mm)
|
| """
|
| self.b = b
|
| self.h = h
|
| self.d = d
|
| self.fc = fc
|
| self.fy = fy
|
| self.n_bars = n_bars
|
| self.bar_area = bar_area
|
| self.As = n_bars * bar_area
|
| self.epsilon_cu = epsilon_cu
|
| self.unit_system = unit_system.lower()
|
|
|
|
|
| if Es is None:
|
| self.Es = 29000000 if self.unit_system == "imperial" else 200000
|
| else:
|
| self.Es = Es
|
|
|
|
|
| if beta1 is None:
|
| self.beta1 = self._calculate_beta1()
|
| else:
|
| self.beta1 = beta1
|
|
|
| def _calculate_beta1(self) -> float:
|
| """Calculates beta1 based on fc per ACI 318."""
|
| if self.unit_system == "imperial":
|
|
|
| if self.fc <= 4000:
|
| return 0.85
|
| elif self.fc >= 8000:
|
| return 0.65
|
| else:
|
| return 0.85 - 0.05 * (self.fc - 4000) / 1000
|
| else:
|
|
|
| if self.fc <= 28:
|
| return 0.85
|
| elif self.fc >= 55:
|
| return 0.65
|
| else:
|
| return 0.85 - 0.05 * (self.fc - 28) / 7
|
|
|
| def calculate_as_min(self) -> float:
|
| """
|
| Calculate minimum steel area per ACI 318.
|
|
|
| Imperial: As_min = max(3*sqrt(fc)/fy * b*d, 200/fy * b*d)
|
| SI: As_min = max(0.25*sqrt(fc)/fy * b*d, 1.4/fy * b*d)
|
| """
|
| if self.unit_system == "imperial":
|
| term1 = (3 * math.sqrt(self.fc) / self.fy) * self.b * self.d
|
| term2 = (200 / self.fy) * self.b * self.d
|
| else:
|
| term1 = (0.25 * math.sqrt(self.fc) / self.fy) * self.b * self.d
|
| term2 = (1.4 / self.fy) * self.b * self.d
|
| return max(term1, term2)
|
|
|
| def calculate_mn(self) -> dict:
|
| """
|
| Calculates Nominal Moment Capacity (Mn) and related values.
|
|
|
| Returns:
|
| dict with all calculated values including:
|
| - T: Tension force
|
| - a: Depth of stress block
|
| - c: Neutral axis depth
|
| - epsilon_y: Yield strain
|
| - epsilon_s: Steel strain at ultimate
|
| - yield_check: Whether steel yields
|
| - fs: Steel stress
|
| - Mn: Nominal moment
|
| - Mn_display: Moment in display units (k-ft or kN-m)
|
| - As_min: Minimum steel area
|
| - as_check: Whether As >= As_min
|
| - phi: Strength reduction factor
|
| - Mu: Design moment capacity
|
| """
|
|
|
| T = self.As * self.fy
|
|
|
|
|
| a = (self.As * self.fy) / (0.85 * self.fc * self.b)
|
|
|
|
|
| c = a / self.beta1
|
|
|
|
|
| epsilon_y = self.fy / self.Es
|
| epsilon_s = self.epsilon_cu * (self.d - c) / c
|
|
|
|
|
| yield_check = epsilon_s >= epsilon_y
|
| fs = self.fy if yield_check else epsilon_s * self.Es
|
|
|
|
|
| Mn = self.As * fs * (self.d - a / 2)
|
|
|
|
|
| if self.unit_system == "imperial":
|
| Mn_display = Mn / 12000
|
| T_display = T / 1000
|
| Mn_k = Mn / 1000
|
| else:
|
| Mn_display = Mn / 1e6
|
| T_display = T / 1000
|
| Mn_k = Mn
|
|
|
|
|
| As_min = self.calculate_as_min()
|
| as_check = self.As >= As_min
|
|
|
|
|
| epsilon_t = epsilon_s
|
| if epsilon_t >= 0.005:
|
| phi = 0.9
|
| elif epsilon_t <= 0.002:
|
| phi = 0.65
|
| else:
|
| phi = 0.65 + 0.25 * (epsilon_t - 0.002) / 0.003
|
|
|
| Mu = phi * Mn
|
| if self.unit_system == "imperial":
|
| Mu_display = Mu / 12000
|
| else:
|
| Mu_display = Mu / 1e6
|
|
|
| return {
|
| "T": T,
|
| "T_display": T_display,
|
| "a": a,
|
| "c": c,
|
| "epsilon_y": epsilon_y,
|
| "epsilon_s": epsilon_s,
|
| "yield_check": yield_check,
|
| "fs": fs,
|
| "Mn": Mn,
|
| "Mn_k": Mn_k,
|
| "Mn_display": Mn_display,
|
| "As": self.As,
|
| "As_min": As_min,
|
| "as_check": as_check,
|
| "epsilon_t": epsilon_t,
|
| "phi": phi,
|
| "Mu": Mu,
|
| "Mu_display": Mu_display,
|
|
|
| "Mn_kft": Mn_display if self.unit_system == "imperial" else None,
|
| "Mn_kin": Mn_k / 1000 if self.unit_system == "imperial" else None,
|
| "Mu_kft": Mu_display if self.unit_system == "imperial" else None,
|
| }
|
|
|
| def get_units(self) -> dict:
|
| """Get unit labels based on current unit system."""
|
| if self.unit_system == "imperial":
|
| return {
|
| "length": "in",
|
| "area": "in^2",
|
| "force": "lb",
|
| "force_k": "kips",
|
| "stress": "psi",
|
| "moment": "lb-in",
|
| "moment_k": "k-in",
|
| "moment_display": "k-ft",
|
| }
|
| else:
|
| return {
|
| "length": "mm",
|
| "area": "mm^2",
|
| "force": "N",
|
| "force_k": "kN",
|
| "stress": "MPa",
|
| "moment": "N-mm",
|
| "moment_k": "N-mm",
|
| "moment_display": "kN-m",
|
| }
|
|
|