Source code for api.app.calculators.concrete.conc_beam_design_doubly_reinforced

"""
Doubly Reinforced Beam Design Module per IS 456:2000 (Limit State Method)

This module implements doubly reinforced concrete beam design for sections where
the applied factored moment exceeds the limiting moment capacity of a singly
reinforced section (Mu > Mu,lim).

The module is called from the main beam design calculator when over-reinforcement
is detected and compression steel is required.

Design Philosophy:
    When Mu > Mu,lim, compression steel is added to increase moment capacity
    while maintaining ductility. The total capacity is split into:
    - Mu,lim: Resisted by singly reinforced section (Ast1)
    - Mu,excess = Mu - Mu,lim: Resisted by compression steel pair (Asc + Ast2)

References:
    IS 456:2000 Clause 38 - Flexural Strength
    IS 456:2000 Annex G - Design Aid for Reinforced Concrete
"""

import math
from dataclasses import dataclass
from typing import Any, Dict, Tuple


[docs] @dataclass class DoublyReinforcedResult: """ Data class containing doubly reinforced beam design results. Attributes: section_type (str): Section classification ('singly' or 'doubly') is_under_reinforced (bool): Whether section meets ductility requirements Mu (float): Applied factored moment in kN-m Mu_lim (float): Limiting moment capacity in kN-m Mu_excess (float): Excess moment requiring compression steel in kN-m ast1 (float): Tension steel for Mu_lim in mm² ast2 (float): Additional tension steel for Mu_excess in mm² ast_required (float): Total required tension steel in mm² ast_method2 (float): Verification steel area by alternate method in mm² asc_required (float): Required compression steel area in mm² xu_max (float): Maximum neutral axis depth in mm z (float): Lever arm in mm d_prime (float): Effective cover to compression steel in mm lever_arm_compression (float): Lever arm for compression steel in mm discriminant (float): Quadratic equation discriminant for validation min_area (float): Minimum steel area per IS 456 in mm² max_area_tension (float): Maximum tension steel area in mm² max_area_compression (float): Maximum compression steel area in mm² method (str): Design method identifier """ section_type: str is_under_reinforced: bool # Moment information Mu: float # in kN-m Mu_lim: float # in kN-m Mu_excess: float # in kN-m # Steel calculations (tension) ast1: float # For Mu_lim ast2: float # Additional for excess ast_required: float # Total tension ast_method2: float # For verification # Steel calculations (compression) asc_required: float # Design parameters xu_max: float z: float d_prime: float lever_arm_compression: float discriminant: float # Limits min_area: float max_area_tension: float max_area_compression: float # Design method method: str
[docs] class DoublyReinforcedDesigner: """ Doubly reinforced concrete beam designer per IS 456:2000. Handles design of beam sections where the applied moment exceeds the limiting moment capacity of a singly reinforced section, requiring compression steel to increase capacity while maintaining ductility. Design Theory: When Mu > Mu,lim: 1. A single layer of tension steel at xu,max cannot resist the moment 2. Compression steel is added to reduce neutral axis depth 3. Total capacity = Mu,lim (singly reinforced) + Mu,excess (steel couple) 4. Ast = Ast1 (for Mu,lim) + Ast2 (paired with Asc) Design Steps: 1. Calculate Ast1 for Mu,lim using singly reinforced closed-form solution 2. Calculate excess moment: Mu,excess = Mu - Mu,lim 3. Design compression steel Asc for Mu,excess 4. Calculate additional tension steel Ast2 = Asc (force equilibrium) 5. Total tension steel Ast = Ast1 + Ast2 6. Verify minimum and maximum steel requirements Example: >>> designer = DoublyReinforcedDesigner() >>> result = designer.design_doubly_reinforced( ... Mu=250, # kN-m ... b=300, # mm ... d=600, # mm ... fck=25, # MPa ... fy=415, # MPa ... d_prime=50 # mm ... ) >>> print(f"Tension steel: {result.ast_required:.0f} mm²") >>> print(f"Compression steel: {result.asc_required:.0f} mm²") References: IS 456:2000 Clause 38 - Flexural Strength IS 456:2000 Clause 38.1 - Assumptions for Limit State Design IS 456:2000 Annex G - Design Aids """
[docs] def __init__(self) -> None: """ Initialize doubly reinforced designer with IS 456 parameters. Sets up stress block factors and neutral axis depth ratios per IS 456:2000. """ # IS 456:2000 Clause 38.1 - Stress block parameters self.stress_block_factor = 0.36 # α for rectangular stress block self.neutral_axis_factor = 0.42 # β for stress centroid self.steel_design_stress = 0.87 # Design stress factor (0.87 × fy) # IS 456:2000 Table - Maximum neutral axis depth ratios # Fe250 removed - outdated per IS 1786:2008 self.xu_max_ratios = { "Fe415": 0.48, # HYSD bars (most common) "Fe500": 0.46, # High strength steel "Fe550": 0.44, # Very high strength steel }
def _calculate_ast_closed_form( self, mu_nmm: float, fck: float, fy: float, b: float, d: float ) -> Tuple[float, float]: """ Calculate tension steel area using closed-form limit state method. Solves the quadratic equilibrium equation for steel area based on moment equilibrium and strain compatibility per IS 456:2000 Clause 38.1. Args: mu_nmm: Applied moment in N-mm fck: Characteristic compressive strength of concrete in MPa fy: Characteristic yield strength of steel in MPa b: Beam width in mm d: Effective depth in mm Returns: Tuple containing: - Ast (float): Required steel area in mm² - discriminant (float): Quadratic discriminant for validation Raises: ValueError: If discriminant is negative (moment exceeds section capacity) Note: Formula: Ast = (0.5 × fck/fy) × [1 - sqrt(1 - 4.6×Mu/(fck×b×d²))] × b × d Discriminant must be ≥ 0 for real solution. If negative, section is inadequate even with maximum steel. References: IS 456:2000 Clause 38.1 - Flexural Strength Calculation SP 16 - Design Aids for Reinforced Concrete (Formula derivation) """ discriminant = 1 - (4.6 * mu_nmm) / (fck * b * d**2) if discriminant < 0: raise ValueError( f"Invalid discriminant {discriminant:.4f}. " f"Moment {mu_nmm / 1e6:.1f} kN-m exceeds section capacity. " f"Increase section size or concrete grade." ) ast = (0.5 * fck / fy) * (1 - math.sqrt(discriminant)) * b * d return ast, discriminant def _calculate_ast_method2(self, mu_nmm: float, fy: float, d: float, z: float) -> float: """ Calculate tension steel using lever arm method (verification). Alternative method based on internal couple (C = T) with lever arm z. Used to verify closed-form solution. Args: mu_nmm: Applied moment in N-mm fy: Characteristic yield strength in MPa d: Effective depth in mm z: Lever arm (distance between C and T) in mm Returns: Required steel area in mm² Note: Formula: Ast = Mu / (0.87 × fy × z) Lever arm z = d - (0.42 × xu,max) for limiting neutral axis depth. This method is simpler but requires known lever arm. References: IS 456:2000 Clause 38.1 - Lever Arm Approach """ ast = mu_nmm / (0.87 * fy * z) return ast def _calculate_mu_lim( self, fck: float, fy: float, b: float, d: float, steel_grade: str = "Fe415" ) -> float: """ Calculate limiting moment of resistance for singly reinforced section. Determines maximum moment that can be resisted by tension steel alone at limiting neutral axis depth (xu,max) per IS 456:2000. Args: fck: Characteristic concrete strength in MPa fy: Characteristic steel strength in MPa b: Beam width in mm d: Effective depth in mm steel_grade: Steel grade ('Fe250', 'Fe415', 'Fe500'). Default: 'Fe415' Returns: Limiting moment Mu,lim in kN-m Note: Formula: Mu,lim = 0.36 × fck × (xu,max/d) × [1 - 0.42(xu,max/d)] × b × d² xu,max/d depends on steel grade: - Fe415: 0.48 - Fe500: 0.46 - Fe550: 0.44 Limiting neutral axis ensures ductile failure (steel yields before concrete crushes). References: IS 456:2000 Clause 38.1 - Limiting Neutral Axis Depth IS 456:2000 Annex G - Moment of Resistance Charts """ xu_max_ratio = self.xu_max_ratios.get(steel_grade, 0.48) mu_lim_nmm = ( self.stress_block_factor * fck * xu_max_ratio * (1 - self.neutral_axis_factor * xu_max_ratio) * b * d**2 ) return mu_lim_nmm / 1e6 # Convert N-mm to kN-m def _calculate_compression_steel( self, mu_excess_nmm: float, fy: float, fsc: float, d: float, d_prime: float ) -> float: """ Calculate required compression steel for excess moment. Determines compression steel area needed to resist the moment exceeding Mu,lim in combination with additional tension steel. Args: mu_excess_nmm: Excess moment (Mu - Mu,lim) in N-mm fy: Characteristic yield strength of steel in MPa fsc: Stress in compression steel in MPa (≤0.87fy) d: Effective depth in mm d_prime: Effective cover to compression steel in mm Returns: Required compression steel area Asc in mm² Note: Formula: Asc = Mu,excess / [fsc × (d - d')] Lever arm for compression steel couple: (d - d') Compression steel stress fsc depends on strain: - If ε_sc > ε_y: fsc = 0.87fy (steel yielded) - If ε_sc < ε_y: fsc = Es × ε_sc (elastic range) Conservative assumption: fsc = 0.87fy (assumes yielding) References: IS 456:2000 Clause 38 - Doubly Reinforced Section Design """ asc = mu_excess_nmm / (fsc * (d - d_prime)) return asc
[docs] def design_doubly_reinforced( self, Mu: float, b: float, d: float, fck: float, fy: float, d_prime: float = 50.0, steel_grade: str = "Fe415", ) -> DoublyReinforcedResult: """ Design doubly reinforced beam section per IS 456:2000. Main entry point for doubly reinforced beam design. Calculates tension and compression steel required for moment exceeding Mu,lim. Args: Mu: Applied factored moment in kN-m b: Beam width in mm (≥150mm) d: Effective depth in mm (≥150mm) fck: Characteristic concrete strength in MPa (M20-M50) fy: Characteristic steel strength in MPa (415, 500, 550) d_prime: Effective cover to compression steel in mm. Default: 50mm steel_grade: Steel grade string ('Fe415', 'Fe500', 'Fe550'). Default: 'Fe415' Returns: DoublyReinforcedResult object containing: - ast_required: Total tension steel in mm² - asc_required: Compression steel in mm² - Mu_lim: Limiting moment in kN-m - Mu_excess: Excess moment in kN-m - Complete design parameters and checks Raises: ValueError: If inputs violate IS 456 limits or discriminant is negative Example: >>> designer = DoublyReinforcedDesigner() >>> result = designer.design_doubly_reinforced( ... Mu=250, ... b=300, ... d=600, ... fck=25, ... fy=415, ... d_prime=50 ... ) >>> print(f"Ast = {result.ast_required:.0f} mm²") >>> print(f"Asc = {result.asc_required:.0f} mm²") Ast = 2845 mm² Asc = 628 mm² Note: Design assumes: - Plane sections remain plane (strain compatibility) - Maximum concrete strain εcu = 0.0035 - Steel yields (fy reached) for ductile failure - Compression steel confined and properly detailed References: IS 456:2000 Clause 38 - Flexural Strength IS 456:2000 Clause 26.5.1 - Minimum and Maximum Reinforcement """ # Convert moment to N-mm for calculations mu_nmm = Mu * 1e6 # Step 1: Calculate limiting moment Mu,lim mu_lim = self._calculate_mu_lim(fck, fy, b, d, steel_grade) mu_lim_nmm = mu_lim * 1e6 # Step 2: Calculate Ast1 for Mu,lim (singly reinforced solution) xu_max_ratio = self.xu_max_ratios.get(steel_grade, 0.48) xu_max = xu_max_ratio * d z = d - self.neutral_axis_factor * xu_max # Lever arm ast1, discriminant = self._calculate_ast_closed_form(mu_lim_nmm, fck, fy, b, d) # Step 3: Calculate excess moment requiring compression steel mu_excess = Mu - mu_lim mu_excess_nmm = mu_excess * 1e6 # Step 4: Design compression steel for excess moment # Assume compression steel yields: fsc = 0.87 × fy fsc = self.steel_design_stress * fy asc_required = self._calculate_compression_steel(mu_excess_nmm, fy, fsc, d, d_prime) # Step 5: Calculate additional tension steel (force equilibrium) # Ast2 must balance Asc: T = C ast2 = asc_required # Force equilibrium: fsc × Asc = 0.87fy × Ast2 # Step 6: Total tension steel ast_required = ast1 + ast2 # Verification using lever arm method ast_method2 = self._calculate_ast_method2(mu_nmm, fy, d, z) # Calculate limits per IS 456:2000 Clause 26.5.1 min_area = 0.85 * b * d / fy # Minimum tension steel max_area_tension = 0.04 * b * d # Maximum 4% of gross area max_area_compression = 0.04 * b * d # Maximum 4% of gross area # Check if section is under-reinforced (ductile) is_under_reinforced = ast_required <= max_area_tension return DoublyReinforcedResult( section_type="doubly", is_under_reinforced=is_under_reinforced, Mu=Mu, Mu_lim=mu_lim, Mu_excess=mu_excess, ast1=ast1, ast2=ast2, ast_required=ast_required, ast_method2=ast_method2, asc_required=asc_required, xu_max=xu_max, z=z, d_prime=d_prime, lever_arm_compression=d - d_prime, discriminant=discriminant, min_area=min_area, max_area_tension=max_area_tension, max_area_compression=max_area_compression, method="limit_state_doubly_reinforced", )
[docs] def verify_design(self, result: DoublyReinforcedResult) -> Dict[str, Any]: """ Verify doubly reinforced design against IS 456:2000 requirements. Performs comprehensive checks on the design including: - Minimum and maximum steel requirements - Ductility (under-reinforced condition) - Compression steel stress validity - Method cross-verification Args: result: DoublyReinforcedResult from design_doubly_reinforced() Returns: Dictionary containing: - is_valid (bool): Overall design validity - checks (dict): Individual check results - warnings (list): Design warnings Example: >>> verification = designer.verify_design(result) >>> if verification['is_valid']: ... print("Design is valid per IS 456:2000") >>> for warning in verification['warnings']: ... print(f"Warning: {warning}") References: IS 456:2000 Clause 26.5.1 - Reinforcement Limits """ checks = {} warnings = [] # Check minimum steel checks["min_steel"] = result.ast_required >= result.min_area if not checks["min_steel"]: warnings.append( f"Tension steel {result.ast_required:.0f} mm² < minimum " f"{result.min_area:.0f} mm² per IS 456 Clause 26.5.1.1" ) # Check maximum tension steel checks["max_tension_steel"] = result.ast_required <= result.max_area_tension if not checks["max_tension_steel"]: warnings.append( f"Tension steel {result.ast_required:.0f} mm² > maximum " f"{result.max_area_tension:.0f} mm² (4% of bd)" ) # Check maximum compression steel checks["max_compression_steel"] = result.asc_required <= result.max_area_compression if not checks["max_compression_steel"]: warnings.append( f"Compression steel {result.asc_required:.0f} mm² > maximum " f"{result.max_area_compression:.0f} mm² (4% of bd)" ) # Check ductility checks["ductile"] = result.is_under_reinforced if not checks["ductile"]: warnings.append( "Section is over-reinforced - brittle failure mode. " "Increase section size or use higher grade concrete." ) # Cross-verify methods (should be within 5%) method_diff_pct = abs(result.ast_required - result.ast_method2) / result.ast_required * 100 checks["method_agreement"] = method_diff_pct < 5.0 if not checks["method_agreement"]: warnings.append( f"Method disagreement: {method_diff_pct:.1f}% difference " f"between closed-form and lever arm methods" ) is_valid = all(checks.values()) return {"is_valid": is_valid, "checks": checks, "warnings": warnings}
# Example usage and testing if __name__ == "__main__": """ Example usage of DoublyReinforcedDesigner. Demonstrates design of a beam section with moment exceeding Mu,lim. """ designer = DoublyReinforcedDesigner() # Design parameters Mu = 250 # kN-m (exceeds Mu,lim for this section) b = 300 # mm d = 600 # mm fck = 25 # MPa (M25 concrete) fy = 415 # MPa (Fe415 steel) d_prime = 50 # mm # Perform design result = designer.design_doubly_reinforced(Mu=Mu, b=b, d=d, fck=fck, fy=fy, d_prime=d_prime) # Print results print("=" * 60) print("DOUBLY REINFORCED BEAM DESIGN PER IS 456:2000") print("=" * 60) print("\nInput Parameters:") print(f" Applied Moment (Mu) : {Mu:.1f} kN-m") print(f" Beam Width (b) : {b} mm") print(f" Effective Depth (d) : {d} mm") print(f" Concrete Grade : M{int(fck)}") print(f" Steel Grade : Fe{int(fy)}") print(f" Cover to Comp. Steel : {d_prime} mm") print("\nMoment Analysis:") print(f" Limiting Moment (Mu,lim) : {result.Mu_lim:.2f} kN-m") print(f" Excess Moment : {result.Mu_excess:.2f} kN-m") print(f" Section Type : {result.section_type.upper()}") print("\nSteel Requirements:") print(" Tension Steel (Ast):") print(f" - For Mu,lim (Ast1) : {result.ast1:.0f} mm²") print(f" - For Excess (Ast2) : {result.ast2:.0f} mm²") print(f" - Total Required : {result.ast_required:.0f} mm²") print(f" Compression Steel (Asc) : {result.asc_required:.0f} mm²") print("\nDesign Parameters:") print(f" Max Neutral Axis (xu,max): {result.xu_max:.1f} mm") print(f" Lever Arm (z) : {result.z:.1f} mm") print(f" Discriminant : {result.discriminant:.4f}") print("\nLimits Check:") print(f" Minimum Steel : {result.min_area:.0f} mm²") print(f" Maximum Tension Steel : {result.max_area_tension:.0f} mm²") print(f" Maximum Comp. Steel : {result.max_area_compression:.0f} mm²") print(f" Under-Reinforced? : {'YES' if result.is_under_reinforced else 'NO'}") # Verify design verification = designer.verify_design(result) print("\nDesign Verification:") print(f" Overall Valid : {'PASS' if verification['is_valid'] else 'FAIL'}") if verification["warnings"]: print("\n Warnings:") for warning in verification["warnings"]: print(f" - {warning}") else: print(" No warnings - design complies with IS 456:2000") print("=" * 60)