File size: 7,734 Bytes
7c60059
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
"""

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()
        
        # Set Es default based on unit system
        if Es is None:
            self.Es = 29000000 if self.unit_system == "imperial" else 200000
        else:
            self.Es = Es
        
        # Set beta1 - calculate if not provided
        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":
            # fc in psi
            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:
            # fc in MPa
            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

        """
        # Tension force
        T = self.As * self.fy
        
        # Stress block depth
        a = (self.As * self.fy) / (0.85 * self.fc * self.b)
        
        # Neutral axis depth
        c = a / self.beta1
        
        # Strains
        epsilon_y = self.fy / self.Es
        epsilon_s = self.epsilon_cu * (self.d - c) / c
        
        # Check if steel yields
        yield_check = epsilon_s >= epsilon_y
        fs = self.fy if yield_check else epsilon_s * self.Es
        
        # Nominal moment
        Mn = self.As * fs * (self.d - a / 2)
        
        # Convert to display units
        if self.unit_system == "imperial":
            Mn_display = Mn / 12000  # lb-in to k-ft
            T_display = T / 1000  # lb to kips
            Mn_k = Mn / 1000  # lb-in to k-in
        else:
            Mn_display = Mn / 1e6  # N-mm to kN-m
            T_display = T / 1000  # N to kN
            Mn_k = Mn  # N-mm
        
        # Minimum steel check
        As_min = self.calculate_as_min()
        as_check = self.As >= As_min
        
        # Phi factor (ACI 318)
        epsilon_t = epsilon_s  # For tension-controlled check
        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,
            # Legacy compatibility
            "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",
            }