Source code for api.app.calculators.steel.steel_column

"""
Steel Column Design Calculator - IS 800:2007 (Complete Implementation)

Comprehensive steel beam-column (compression + bending) design as per IS 800:2007.
Includes:
- Pure compression (Section 7)
- Combined axial load + biaxial bending (Section 9.3)
- Lateral-torsional buckling checks (Section 8.2.2)
- Interaction checks for beam-columns
- Section database integration ready
- Separate effective lengths for major/minor axes
"""

import math
from enum import Enum
from typing import Any, ClassVar, Dict, List, Optional, Tuple, Type

from pydantic import BaseModel, Field, ValidationInfo, field_validator

from ..base import BaseCalculator

# ============================================================================
# ENUMERATIONS
# ============================================================================


[docs] class SectionType(str, Enum): """Standard steel section types.""" ISHB = "ISHB" # I-Section Heavy Beam ISJB = "ISJB" # I-Section Junior Beam ISLB = "ISLB" # I-Section Light Beam ISMC = "ISMC" # Channel Section ISMB = "ISMB" # I-Section Medium Beam ISWB = "ISWB" # I-Section Wide Flange Beam TUBULAR = "TUBULAR" # Hollow sections
[docs] class EndCondition(str, Enum): """Column end conditions for effective length calculation.""" FIXED_FIXED = "fixed_fixed" FIXED_PINNED = "fixed_pinned" PINNED_PINNED = "pinned_pinned" FIXED_FREE = "fixed_free" CUSTOM = "custom" # User provides K-factor directly
[docs] class BucklingClass(str, Enum): """Buckling classes as per IS 800:2007 Table 10.""" A = "a" B = "b" C = "c" D = "d"
[docs] class SteelGrade(str, Enum): """Steel grades (E-series only, no Fe-series).""" E250 = "E250" E350 = "E350" E410 = "E410" E450 = "E450"
[docs] class SectionSource(str, Enum): """Section property source.""" DATABASE = "database" # From steel section database MANUAL = "manual" # User-provided properties BUILDUP = "buildup" # Built-up section (future)
# ============================================================================ # INPUT SCHEMA # ============================================================================
[docs] class SteelColumnInput(BaseModel): """Input schema for steel column design (beam-column).""" # Section Selection section_source: SectionSource = Field( default=SectionSource.MANUAL, description="Source of section properties" ) section_database_id: Optional[str] = Field( None, description="Database ID for standard sections (e.g., 'ISMB_300')" ) # Section Properties (Required if source=MANUAL) section_type: SectionType = Field(..., description="Section type") section_designation: str = Field(..., description="Section designation (e.g., 250, 300)") # Geometric Properties (from steel tables or manual entry) area: float = Field(..., gt=0, description="Cross-sectional area in mm²") depth: float = Field(..., gt=0, description="Overall depth (h) in mm") width: float = Field(..., gt=0, description="Overall width (bf) in mm") thickness_web: float = Field(..., gt=0, description="Web thickness (tw) in mm") thickness_flange: float = Field(..., gt=0, description="Flange thickness (tf) in mm") radius_gyration_major: float = Field( ..., gt=0, description="Radius of gyration about major axis (ryy) in mm" ) radius_gyration_minor: float = Field( ..., gt=0, description="Radius of gyration about minor axis (rzz) in mm" ) # Plastic section moduli for bending strength plastic_modulus_major: float = Field( ..., gt=0, description="Plastic section modulus about major axis (Zpy) in mm³" ) plastic_modulus_minor: float = Field( ..., gt=0, description="Plastic section modulus about minor axis (Zpz) in mm³" ) # Loading - Factored Design Loads axial_load: float = Field(..., gt=0, description="Factored axial compression load in kN") moment_major: float = Field( default=0.0, ge=0, description="Factored moment about major axis (Myy) in kN-m" ) moment_minor: float = Field( default=0.0, ge=0, description="Factored moment about minor axis (Mzz) in kN-m" ) # Member Lengths - Separate for each axis length_major: float = Field( ..., gt=0, le=50.0, description="Unsupported length about major axis in m" ) length_minor: float = Field( ..., gt=0, le=50.0, description="Unsupported length about minor axis in m" ) # End Conditions or Direct K-factors end_condition_major: EndCondition = Field( default=EndCondition.PINNED_PINNED, description="End condition about major axis" ) end_condition_minor: EndCondition = Field( default=EndCondition.PINNED_PINNED, description="End condition about minor axis" ) k_factor_major: Optional[float] = Field( None, gt=0, le=2.5, description="Direct K-factor for major axis (if end_condition=CUSTOM)" ) k_factor_minor: Optional[float] = Field( None, gt=0, le=2.5, description="Direct K-factor for minor axis (if end_condition=CUSTOM)" ) # Lateral-Torsional Buckling Parameters lateral_support_spacing: Optional[float] = Field( None, gt=0, description="Spacing between lateral supports in m (for LTB check)" ) # Material Properties steel_grade: SteelGrade = Field(default=SteelGrade.E250, description="Steel grade (E-series)")
[docs] @field_validator("section_designation") @classmethod def validate_designation(cls, v: str) -> str: """Validate section designation format.""" if not v.strip(): raise ValueError("Section designation cannot be empty") return v.strip()
[docs] @field_validator("k_factor_major", "k_factor_minor") @classmethod def validate_k_factors(cls, v: Optional[float], info: ValidationInfo) -> Optional[float]: """Validate K-factors when CUSTOM end condition is specified.""" if ( info.field_name and info.data.get(f"end_condition_{info.field_name.split('_')[2]}") == EndCondition.CUSTOM ): if v is None: raise ValueError("K-factor required when end condition is CUSTOM") return v
# ============================================================================ # OUTPUT SCHEMA # ============================================================================
[docs] class SlendernessCheck(BaseModel): """Slenderness ratio check results.""" lambda_major: float = Field(..., description="Slenderness ratio about major axis") lambda_minor: float = Field(..., description="Slenderness ratio about minor axis") lambda_effective: float = Field(..., description="Effective slenderness ratio") limit: float = Field(default=180.0, description="Maximum allowable slenderness ratio") status: str = Field(..., description="PASS or FAIL")
[docs] class BucklingAnalysis(BaseModel): """Buckling analysis results.""" buckling_class_major: BucklingClass = Field(..., description="Buckling class about major axis") buckling_class_minor: BucklingClass = Field(..., description="Buckling class about minor axis") imperfection_factor_major: float = Field(..., description="Imperfection factor alpha (major)") imperfection_factor_minor: float = Field(..., description="Imperfection factor alpha (minor)") non_dimensional_slenderness_major: float = Field( ..., description="Non-dimensional slenderness (major)" ) non_dimensional_slenderness_minor: float = Field( ..., description="Non-dimensional slenderness (minor)" ) stress_reduction_factor_major: float = Field( ..., description="Stress reduction factor chi (major)" ) stress_reduction_factor_minor: float = Field( ..., description="Stress reduction factor chi (minor)" ) governing_axis: str = Field(..., description="Major or Minor axis governing")
[docs] class LTBCheck(BaseModel): """Lateral-torsional buckling check results.""" is_applicable: bool = Field(..., description="Whether LTB check is applicable") effective_length_ltb: Optional[float] = Field(None, description="Effective length for LTB in m") non_dimensional_slenderness_ltb: Optional[float] = Field( None, description="Non-dimensional slenderness for LTB" ) stress_reduction_factor_ltb: Optional[float] = Field( None, description="Stress reduction factor chi_LT" ) design_bending_strength_major: Optional[float] = Field( None, description="Design bending strength considering LTB in kN-m" ) design_bending_strength_minor: Optional[float] = Field( None, description="Design bending strength about minor axis in kN-m" )
[docs] class InteractionCheck(BaseModel): """IS 800:2007 Section 9.3 interaction check.""" compression_ratio: float = Field(..., description="P/Pd ratio") moment_major_ratio: float = Field(..., description="My/Mdy ratio") moment_minor_ratio: float = Field(..., description="Mz/Mdz ratio") interaction_ratio_1: float = Field(..., description="First interaction equation result") interaction_ratio_2: float = Field(..., description="Second interaction equation result") governing_interaction: float = Field(..., description="Maximum interaction ratio") status: str = Field(..., description="SAFE or UNSAFE")
[docs] class DesignCheck(BaseModel): """Design strength check results.""" design_strength_compression: float = Field(..., description="Design compression strength in kN") design_strength_moment_major: float = Field( ..., description="Design moment strength (major axis) in kN-m" ) design_strength_moment_minor: float = Field( ..., description="Design moment strength (minor axis) in kN-m" ) applied_load: float = Field(..., description="Applied axial load in kN") applied_moment_major: float = Field(..., description="Applied moment (major) in kN-m") applied_moment_minor: float = Field(..., description="Applied moment (minor) in kN-m") utilization_compression: float = Field(..., description="Compression utilization ratio") utilization_bending_major: float = Field(..., description="Bending utilization (major)") utilization_bending_minor: float = Field(..., description="Bending utilization (minor)") overall_utilization: float = Field(..., description="Overall utilization ratio") status: str = Field(..., description="SAFE or UNSAFE")
[docs] class SteelColumnOutput(BaseModel): """Output schema for steel column design results.""" inputs: SteelColumnInput # Section Classification section_full_name: str = Field(..., description="Full section name") yield_strength: float = Field(..., description="Yield strength in MPa") # Effective Length Calculations effective_length_major: float = Field(..., description="Effective length about major axis in m") effective_length_minor: float = Field(..., description="Effective length about minor axis in m") effective_length_factor_major: float = Field(..., description="K factor for major axis") effective_length_factor_minor: float = Field(..., description="K factor for minor axis") # Slenderness Checks slenderness: SlendernessCheck # Buckling Analysis buckling: BucklingAnalysis # Lateral-Torsional Buckling ltb_check: LTBCheck # Interaction Check (Section 9.3) interaction: InteractionCheck # Design Checks design_check: DesignCheck # Overall Status status: str = Field(..., description="Overall design status") design_type: str = Field(..., description="Pure Compression or Beam-Column") remarks: List[str] = Field( default_factory=list, description="Design remarks and recommendations" )
# ============================================================================ # CALCULATOR IMPLEMENTATION # ============================================================================
[docs] class SteelColumnCalculator(BaseCalculator): """ Calculator for steel beam-column design per IS 800:2007. Implements comprehensive steel member design including: - Pure compression (Section 7) - Combined compression + biaxial bending (Section 9.3) - Effective length calculation (Clause 7.2) - Slenderness ratio checks (Table 3) - Buckling class determination (Table 10) - Design compressive strength (Clause 7.1) - Design bending strength (Clause 8.2) - Lateral-torsional buckling (Clause 8.2.2) - Interaction checks (Clause 9.3.2.2) - Perry-Robertson formula for buckling """ # Effective length factors (Table 11, IS 800:2007 Clause 7.2.2) EFFECTIVE_LENGTH_FACTORS: ClassVar[Dict[EndCondition, float]] = { EndCondition.FIXED_FIXED: 0.65, EndCondition.FIXED_PINNED: 0.80, EndCondition.PINNED_PINNED: 1.00, EndCondition.FIXED_FREE: 2.00, } # Yield strengths for steel grades (MPa) YIELD_STRENGTHS: ClassVar[Dict[SteelGrade, float]] = { SteelGrade.E250: 250.0, SteelGrade.E350: 350.0, SteelGrade.E410: 410.0, SteelGrade.E450: 450.0, } # Imperfection factors for buckling classes (Table 10, IS 800:2007) IMPERFECTION_FACTORS: ClassVar[Dict[BucklingClass, float]] = { BucklingClass.A: 0.21, BucklingClass.B: 0.34, BucklingClass.C: 0.49, BucklingClass.D: 0.76, } # Modulus of elasticity for steel (N/mm²) E_STEEL: float = 200000.0 # Partial safety factors GAMMA_M0: float = 1.10 # Resistance governed by yielding/buckling
[docs] def __init__(self) -> None: super().__init__("steel_column_design", "3.0.0")
@property def input_schema(self) -> Type[BaseModel]: """Return input validation schema.""" return SteelColumnInput @property def output_schema(self) -> Type[BaseModel]: """Return output formatting schema.""" return SteelColumnOutput
[docs] def calculate(self, inputs: Dict[str, Any]) -> Dict[str, Any]: """ Perform comprehensive steel beam-column design calculation. Calculation sequence: 1. Extract and validate inputs 2. Determine material properties 3. Calculate effective lengths 4. Check slenderness ratios 5. Determine buckling classes 6. Calculate design compressive strength (Section 7) 7. Calculate design bending strength with LTB check (Section 8) 8. Perform interaction checks (Section 9.3) 9. Generate remarks and recommendations """ # Step 1: Parse and validate inputs input_data = SteelColumnInput(**inputs) # Step 2: Material properties fy = self.YIELD_STRENGTHS[input_data.steel_grade] section_name = f"{input_data.section_type.value} {input_data.section_designation}" # Determine design type has_moments = input_data.moment_major > 0 or input_data.moment_minor > 0 design_type = "Beam-Column" if has_moments else "Pure Compression" # Step 3: Effective length calculation k_major = self._get_effective_length_factor( input_data.end_condition_major, input_data.k_factor_major ) k_minor = self._get_effective_length_factor( input_data.end_condition_minor, input_data.k_factor_minor ) length_major_mm = input_data.length_major * 1000.0 length_minor_mm = input_data.length_minor * 1000.0 l_eff_major = k_major * length_major_mm l_eff_minor = k_minor * length_minor_mm # Step 4: Slenderness ratio calculation (Clause 7.2.1) lambda_major = l_eff_major / input_data.radius_gyration_major lambda_minor = l_eff_minor / input_data.radius_gyration_minor lambda_effective = max(lambda_major, lambda_minor) slenderness_status = "PASS" if lambda_effective <= 180.0 else "FAIL" # Step 5: Buckling class determination (Table 10) buckling_class_major, buckling_class_minor = self._determine_buckling_class( input_data.section_type, input_data.thickness_flange ) # Step 6: Design compressive strength calculation (Section 7) alpha_major = self.IMPERFECTION_FACTORS[buckling_class_major] alpha_minor = self.IMPERFECTION_FACTORS[buckling_class_minor] lambda_bar_major = self._calculate_non_dimensional_slenderness( lambda_major, fy, self.E_STEEL ) lambda_bar_minor = self._calculate_non_dimensional_slenderness( lambda_minor, fy, self.E_STEEL ) chi_major = self._calculate_stress_reduction_factor(lambda_bar_major, alpha_major) chi_minor = self._calculate_stress_reduction_factor(lambda_bar_minor, alpha_minor) # Governing axis for compression if chi_minor < chi_major: chi_governing_compression = chi_minor governing_axis = "Minor" else: chi_governing_compression = chi_major governing_axis = "Major" # Design compressive stress (Clause 7.1.2) fcd = (chi_governing_compression * fy) / self.GAMMA_M0 # Design compressive strength design_strength_compression_kn = (fcd * input_data.area) / 1000.0 # Step 7: Design bending strength with LTB check (Section 8) ltb_check_result, design_moment_major, design_moment_minor = ( self._calculate_bending_strength_with_ltb(input_data, fy) ) # Step 8: Interaction checks (Section 9.3.2.2) interaction_result = self._calculate_interaction_check( input_data, design_strength_compression_kn, design_moment_major, design_moment_minor, chi_major, chi_minor, ) # Step 9: Overall design checks utilization_compression = input_data.axial_load / design_strength_compression_kn utilization_bending_major = ( input_data.moment_major / design_moment_major if design_moment_major > 0 else 0.0 ) utilization_bending_minor = ( input_data.moment_minor / design_moment_minor if design_moment_minor > 0 else 0.0 ) # Overall utilization is governed by interaction check overall_utilization = interaction_result["governing_interaction"] design_status = "SAFE" if overall_utilization <= 1.0 else "UNSAFE" overall_status = ( "PASS" if (slenderness_status == "PASS" and design_status == "SAFE") else "FAIL" ) # Step 10: Generate remarks remarks = self._generate_remarks( input_data, design_type, lambda_effective, overall_utilization, governing_axis, design_status, ltb_check_result["is_applicable"], ) # Construct output return { "inputs": input_data.model_dump(), "section_full_name": section_name, "yield_strength": round(fy, 2), "effective_length_major": round(l_eff_major / 1000.0, 3), "effective_length_minor": round(l_eff_minor / 1000.0, 3), "effective_length_factor_major": round(k_major, 2), "effective_length_factor_minor": round(k_minor, 2), "slenderness": { "lambda_major": round(lambda_major, 2), "lambda_minor": round(lambda_minor, 2), "lambda_effective": round(lambda_effective, 2), "limit": 180.0, "status": slenderness_status, }, "buckling": { "buckling_class_major": buckling_class_major, "buckling_class_minor": buckling_class_minor, "imperfection_factor_major": round(alpha_major, 3), "imperfection_factor_minor": round(alpha_minor, 3), "non_dimensional_slenderness_major": round(lambda_bar_major, 3), "non_dimensional_slenderness_minor": round(lambda_bar_minor, 3), "stress_reduction_factor_major": round(chi_major, 3), "stress_reduction_factor_minor": round(chi_minor, 3), "governing_axis": governing_axis, }, "ltb_check": ltb_check_result, "interaction": interaction_result, "design_check": { "design_strength_compression": round(design_strength_compression_kn, 2), "design_strength_moment_major": round(design_moment_major, 2), "design_strength_moment_minor": round(design_moment_minor, 2), "applied_load": round(input_data.axial_load, 2), "applied_moment_major": round(input_data.moment_major, 2), "applied_moment_minor": round(input_data.moment_minor, 2), "utilization_compression": round(utilization_compression, 3), "utilization_bending_major": round(utilization_bending_major, 3), "utilization_bending_minor": round(utilization_bending_minor, 3), "overall_utilization": round(overall_utilization, 3), "status": design_status, }, "status": overall_status, "design_type": design_type, "remarks": remarks, }
def _get_effective_length_factor( self, end_condition: EndCondition, custom_k: Optional[float] ) -> float: """Get effective length factor from end condition or custom value.""" if end_condition == EndCondition.CUSTOM: if custom_k is None: raise ValueError("Custom K-factor required when end condition is CUSTOM") return custom_k return self.EFFECTIVE_LENGTH_FACTORS[end_condition] def _determine_buckling_class( self, section_type: SectionType, flange_thickness: float ) -> Tuple[BucklingClass, BucklingClass]: """ Determine buckling class as per IS 800:2007 Table 10. Returns: (buckling_class_major, buckling_class_minor) """ if section_type in [SectionType.ISHB, SectionType.ISMB, SectionType.ISLB, SectionType.ISWB]: # Rolled I-sections if flange_thickness <= 40.0: return BucklingClass.A, BucklingClass.B else: return BucklingClass.B, BucklingClass.C elif section_type == SectionType.ISMC: # Channel sections return BucklingClass.B, BucklingClass.C elif section_type == SectionType.TUBULAR: # Hollow sections return BucklingClass.A, BucklingClass.A else: # Default conservative return BucklingClass.C, BucklingClass.C def _calculate_non_dimensional_slenderness( self, lambda_ratio: float, fy: float, E: float ) -> float: """ Calculate non-dimensional slenderness ratio. λ̄ = λ / λₑ = λ / (π√(E/fy)) """ lambda_e = math.pi * math.sqrt(E / fy) return lambda_ratio / lambda_e def _calculate_stress_reduction_factor(self, lambda_bar: float, alpha: float) -> float: """ Calculate stress reduction factor using Perry-Robertson formula. IS 800:2007 Clause 7.1.2: φ = 0.5[1 + α(λ̄ - 0.2) + λ̄²] χ = 1 / [φ + √(φ² - λ̄²)] ≤ 1.0 """ if lambda_bar <= 0.2: return 1.0 phi = 0.5 * (1.0 + alpha * (lambda_bar - 0.2) + lambda_bar**2) discriminant = phi**2 - lambda_bar**2 if discriminant < 0: chi = 1.0 / (2.0 * phi) else: chi = 1.0 / (phi + math.sqrt(discriminant)) return min(chi, 1.0) def _calculate_bending_strength_with_ltb( self, inputs: SteelColumnInput, fy: float ) -> Tuple[Dict[str, Any], float, float]: """ Calculate design bending strength considering lateral-torsional buckling. Returns: (ltb_check_dict, design_moment_major_knm, design_moment_minor_knm) """ # Design moment about minor axis (no LTB consideration) design_moment_minor = (inputs.plastic_modulus_minor * fy) / (self.GAMMA_M0 * 1e6) # kN-m # Check if LTB is applicable for major axis bending # LTB not applicable for: minor axis bending, tubular sections, or no moments ltb_applicable = inputs.moment_major > 0 and inputs.section_type != SectionType.TUBULAR if not ltb_applicable: # No LTB check needed design_moment_major = (inputs.plastic_modulus_major * fy) / (self.GAMMA_M0 * 1e6) return ( { "is_applicable": False, "effective_length_ltb": None, "non_dimensional_slenderness_ltb": None, "stress_reduction_factor_ltb": None, "design_bending_strength_major": round(design_moment_major, 2), "design_bending_strength_minor": round(design_moment_minor, 2), }, design_moment_major, design_moment_minor, ) # LTB check applicable # Use lateral support spacing if provided, otherwise use length_major l_ltb = ( inputs.lateral_support_spacing if inputs.lateral_support_spacing else inputs.length_major ) l_ltb_mm = l_ltb * 1000.0 # Simplified elastic critical moment (IS 800 Clause 8.2.2.1) # For standard I-sections: Mcr ≈ (π²EIy/L²) * √(1 + (1/20)*(h/tf)²) Iy = inputs.area * (inputs.radius_gyration_minor**2) # mm⁴ h = inputs.depth # mm tf = inputs.thickness_flange # mm term1 = (math.pi**2 * self.E_STEEL * Iy) / (l_ltb_mm**2) term2 = math.sqrt(1 + (1 / 20) * ((h / tf) ** 2)) M_cr = term1 * term2 # N-mm # Non-dimensional slenderness for LTB beta_b = 1.0 # For plastic/compact sections lambda_LT = math.sqrt((beta_b * inputs.plastic_modulus_major * fy) / M_cr) # Imperfection factor for LTB (Table 13) alpha_LT = 0.21 # For rolled sections (conservative) # Stress reduction factor for LTB phi_LT = 0.5 * (1.0 + alpha_LT * (lambda_LT - 0.2) + lambda_LT**2) discriminant_LT = phi_LT**2 - lambda_LT**2 if discriminant_LT < 0 or lambda_LT <= 0.4: chi_LT = 1.0 else: chi_LT = 1.0 / (phi_LT + math.sqrt(discriminant_LT)) chi_LT = min(chi_LT, 1.0) # Design bending strength considering LTB design_moment_major = (chi_LT * inputs.plastic_modulus_major * fy) / (self.GAMMA_M0 * 1e6) return ( { "is_applicable": True, "effective_length_ltb": round(l_ltb, 3), "non_dimensional_slenderness_ltb": round(lambda_LT, 3), "stress_reduction_factor_ltb": round(chi_LT, 3), "design_bending_strength_major": round(design_moment_major, 2), "design_bending_strength_minor": round(design_moment_minor, 2), }, design_moment_major, design_moment_minor, ) def _calculate_interaction_check( self, inputs: SteelColumnInput, pd_compression: float, mdy_major: float, mdz_minor: float, chi_y: float, chi_z: float, ) -> Dict[str, Any]: """ Calculate interaction check as per IS 800:2007 Section 9.3.2.2. Interaction equations: P/Pdy + Ky*My/Mdy + KLT*Mz/Mdz ≤ 1.0 P/Pdz + Kz*My/Mdy + KLT*Mz/Mdz ≤ 1.0 Where: - Pdy, Pdz = Design strength governed by buckling about y, z axes - Ky, Kz = Interaction factors - KLT = Lateral-torsional buckling interaction factor """ # Compression ratios for each axis # For interaction, we need design strength about each axis separately fcd_major = (chi_y * self.YIELD_STRENGTHS[inputs.steel_grade]) / self.GAMMA_M0 fcd_minor = (chi_z * self.YIELD_STRENGTHS[inputs.steel_grade]) / self.GAMMA_M0 pdy = (fcd_major * inputs.area) / 1000.0 # kN (buckling about major axis) pdz = (fcd_minor * inputs.area) / 1000.0 # kN (buckling about minor axis) # Ratios compression_ratio_y = inputs.axial_load / pdy if pdy > 0 else 0.0 compression_ratio_z = inputs.axial_load / pdz if pdz > 0 else 0.0 moment_major_ratio = inputs.moment_major / mdy_major if mdy_major > 0 else 0.0 moment_minor_ratio = inputs.moment_minor / mdz_minor if mdz_minor > 0 else 0.0 # Interaction factors (IS 800 Clause 9.3.2.2) # Simplified: Cmy = Cmz = 1.0 (conservative) # K factors depend on ny, nz (slenderness-based) ny = compression_ratio_y nz = compression_ratio_z Cmy = 1.0 # Equivalent uniform moment factor (conservative) Cmz = 1.0 CMLT = 1.0 Ky = Cmy * (1.0 + 0.2 * ny) / (1.0 - 0.8 * ny) if ny < 1.25 else Cmy * 10.0 Kz = Cmz * (1.0 + 0.2 * nz) / (1.0 - 0.8 * nz) if nz < 1.25 else Cmz * 10.0 KLT = CMLT * (1.0 - 0.1 * ny) / (1.0 - 0.25 * ny) if ny < 4.0 else 1.0 # Interaction equations interaction_1 = compression_ratio_y + Ky * moment_major_ratio + KLT * moment_minor_ratio interaction_2 = compression_ratio_z + Kz * moment_major_ratio + KLT * moment_minor_ratio governing_interaction = max(interaction_1, interaction_2) status = "SAFE" if governing_interaction <= 1.0 else "UNSAFE" return { "compression_ratio": round(max(compression_ratio_y, compression_ratio_z), 3), "moment_major_ratio": round(moment_major_ratio, 3), "moment_minor_ratio": round(moment_minor_ratio, 3), "interaction_ratio_1": round(interaction_1, 3), "interaction_ratio_2": round(interaction_2, 3), "governing_interaction": round(governing_interaction, 3), "status": status, } def _generate_remarks( self, inputs: SteelColumnInput, design_type: str, lambda_eff: float, utilization: float, governing_axis: str, design_status: str, ltb_applicable: bool, ) -> List[str]: """Generate design remarks and recommendations.""" remarks: List[str] = [] # Design type remarks.append(f"Design type: {design_type}") # Column classification if lambda_eff < 50: remarks.append("Short column - predominantly governed by material strength.") elif lambda_eff < 140: remarks.append("Intermediate column - buckling effects moderate.") else: remarks.append("Slender column - buckling is critical design consideration.") # Governing axis remarks.append(f"Design governed by buckling about {governing_axis.lower()} axis.") # LTB consideration if ltb_applicable: remarks.append( "Lateral-torsional buckling check applied - bending capacity reduced accordingly." ) # Utilization assessment if utilization < 0.6: remarks.append("Member is under-utilized. Consider reducing section size for economy.") elif 0.6 <= utilization <= 0.85: remarks.append("Member utilization is optimal for economical design.") elif 0.85 < utilization <= 1.0: remarks.append( "Member utilization is high. Design is adequate but consider larger section for safety margin." ) else: remarks.append( "CRITICAL: Member capacity exceeded. Increase section size or reduce loads." ) # Slenderness warning if lambda_eff > 180: remarks.append("WARNING: Slenderness ratio exceeds IS 800:2007 limit of 180 (Table 3).") elif lambda_eff > 160: remarks.append("Caution: Slenderness ratio is approaching the code limit.") # Design status if design_status == "SAFE": if design_type == "Beam-Column": remarks.append("Beam-column design is SAFE as per IS 800:2007 Sections 7 and 9.3.") else: remarks.append("Column design is SAFE as per IS 800:2007 Section 7.") else: remarks.append("Column design is UNSAFE. Revise section or reduce applied loads.") # Code compliance remarks.append(f"Design complies with IS 800:2007 for {inputs.steel_grade.value} steel.") return remarks