"""
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)