-
Notifications
You must be signed in to change notification settings - Fork 60
feat: add ACI 318-19 concrete and reinforcement material properties #343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| """ACI 318-19.""" | ||
|
|
||
| import typing as t | ||
|
|
||
| from ._concrete_material_properties import ( | ||
| Ec, | ||
| alpha1, | ||
| beta1, | ||
| eps_cu, | ||
| fct, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fct not defined in ACI 318-19 |
||
| fr, | ||
| lambda_factor, | ||
| ) | ||
| from ._reinforcement_material_properties import ( | ||
| Es, | ||
| epsyd, | ||
| fy_design, | ||
| reinforcement_grade_props, | ||
| ) | ||
|
|
||
| __all__ = [ | ||
| 'Ec', | ||
| 'Es', | ||
| 'alpha1', | ||
| 'beta1', | ||
| 'eps_cu', | ||
| 'epsyd', | ||
| 'fct', | ||
| 'fr', | ||
| 'fy_design', | ||
| 'lambda_factor', | ||
| 'reinforcement_grade_props', | ||
| ] | ||
|
|
||
| __title__: str = 'ACI 318-19' | ||
| __year__: str = '2019' | ||
| __materials__: t.Tuple[str] = ('concrete', 'reinforcement') | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| """Concrete material properties according to ACI 318-19.""" | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest we order the methods as they appear in the code. Let's not jump from Chaper 19 to 22 and then back to 19. |
||
| from __future__ import annotations | ||
|
|
||
| import math | ||
| import typing as t | ||
|
|
||
| LAMBDA_FACTORS = { | ||
| 'normalweight': 1.0, | ||
| 'sand-lightweight': 0.85, | ||
| 'all-lightweight': 0.75, | ||
| } | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The lambda factor in ACI-318-19 is a function of the weight and lies between 1.00 and 0.75. You provide 'sand-lightweight as an intermediate value. Consider programing the equation as a function of the weight (as done for the modulus of elasticity) or adding the values from Table 19.2.4.1 'lightweight, fine blend' and 'sand-lightweight, corase blend' |
||
|
|
||
| def Ec(fc: float, wc: float = 2320.0) -> float: | ||
| """The modulus of elasticity of concrete. | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Default value of concrete. The typical specific weight of concrete used in North American practice is 150 lbs/cf, which I roughly equivalent to a density of 150/(2.2*0.3048^3)≈2400 kg/m3. I understand this my have to do with going from specific weight to density using g=9.81 m/s2, but it is not clear for me how you derive it, as the units are not consistent. On the other hand, Eq. 19.2.2.1b provides a factor of 4700 which would lead to a value of w=2286 kg/m3. |
||
| ACI 318-19, Table 19.2.2.1. | ||
|
|
||
| Args: | ||
| fc (float): The specified compressive strength of concrete in | ||
| MPa. | ||
|
|
||
| Keyword Args: | ||
| wc (float): The unit weight of concrete in kg/m3. | ||
| Default is 2320 kg/m3 (normalweight concrete). | ||
|
|
||
| Returns: | ||
| float: The modulus of elasticity in MPa. | ||
|
|
||
| Raises: | ||
| ValueError: If fc is not positive. | ||
| ValueError: If wc is outside the range 1440-2560 kg/m3. | ||
| """ | ||
| if fc <= 0: | ||
| raise ValueError(f'fc={fc} must be positive') | ||
| if wc < 1440 or wc > 2560: | ||
| raise ValueError(f'wc={wc} must be between 1440 and 2560 kg/m3') | ||
| return wc**1.5 * 0.043 * math.sqrt(fc) | ||
|
|
||
|
|
||
| def fr(fc: float, lambda_s: float = 1.0) -> float: | ||
| """The modulus of rupture of concrete. | ||
|
|
||
| ACI 318-19, Eq. 19.2.3.1. | ||
|
|
||
| Args: | ||
| fc (float): The specified compressive strength of concrete in | ||
| MPa. | ||
|
|
||
| Keyword Args: | ||
| lambda_s (float): The modification factor for lightweight | ||
| concrete. Default is 1.0 (normalweight). | ||
|
|
||
| Returns: | ||
| float: The modulus of rupture in MPa. | ||
|
|
||
| Raises: | ||
| ValueError: If fc is not positive. | ||
| ValueError: If lambda_s is not in (0, 1]. | ||
| """ | ||
| if fc <= 0: | ||
| raise ValueError(f'fc={fc} must be positive') | ||
| if lambda_s <= 0 or lambda_s > 1.0: | ||
| raise ValueError(f'lambda_s={lambda_s} must be in the range (0, 1]') | ||
| return 0.62 * lambda_s * math.sqrt(fc) | ||
|
|
||
|
|
||
| def beta1(fc: float) -> float: | ||
| """The Whitney stress block depth factor. | ||
|
|
||
| ACI 318-19, Table 22.2.2.4.3. | ||
|
|
||
| Args: | ||
| fc (float): The specified compressive strength of concrete in | ||
| MPa. | ||
|
|
||
| Returns: | ||
| float: The stress block depth factor (dimensionless). | ||
|
|
||
| Raises: | ||
| ValueError: If fc is not positive. | ||
| """ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to Table 22.2.2.4.3. f'c must be greater that 17. We should set this as a lower limit. Not 0. |
||
| if fc <= 0: | ||
| raise ValueError(f'fc={fc} must be positive') | ||
| if fc <= 28: | ||
| return 0.85 | ||
| if fc >= 55: | ||
| return 0.65 | ||
| return 0.85 - 0.05 * (fc - 28) / 7 | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace expression: 0.85 - 0.05 * (fc - 28) / 7 by 0.85-0.20/27*(fc-28) - why simplify and lose accuracy? I could drive some people crazy... |
||
|
|
||
| def eps_cu() -> float: | ||
| """The maximum usable strain at the extreme concrete compression fiber. | ||
|
|
||
| ACI 318-19, Section 22.2.2.1. | ||
|
|
||
| Returns: | ||
| float: The ultimate concrete strain (dimensionless). | ||
| """ | ||
| return 0.003 | ||
|
|
||
|
|
||
| def lambda_factor( | ||
| concrete_type: t.Literal[ | ||
| 'normalweight', 'sand-lightweight', 'all-lightweight' | ||
| ], | ||
| ) -> float: | ||
| """The modification factor for lightweight concrete. | ||
|
|
||
| ACI 318-19, Table 19.2.4.2. | ||
|
|
||
| Args: | ||
| concrete_type (str): The concrete type. One of | ||
| 'normalweight', 'sand-lightweight', or 'all-lightweight'. | ||
|
|
||
| Returns: | ||
| float: The lightweight modification factor (dimensionless). | ||
|
|
||
| Raises: | ||
| ValueError: If concrete_type is not recognized. | ||
| """ | ||
| result = LAMBDA_FACTORS.get(concrete_type.lower()) | ||
| if result is None: | ||
| raise ValueError( | ||
| f'Unknown concrete type: {concrete_type}. ' | ||
| f'Valid types: {list(LAMBDA_FACTORS.keys())}' | ||
| ) | ||
| return result | ||
|
|
||
|
|
||
| def fct(fc: float, lambda_s: float = 1.0) -> float: | ||
| """The approximate splitting tensile strength of concrete. | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The formula used in this method does not correspond to ACI 318-19, Section 19.2.4.3, as stated. We should possibly delete this method as fct is not defined in ACI 318-19. |
||
| ACI 318-19, Section 19.2.4.3. | ||
|
|
||
| Args: | ||
| fc (float): The specified compressive strength of concrete in | ||
| MPa. | ||
|
|
||
| Keyword Args: | ||
| lambda_s (float): The modification factor for lightweight | ||
| concrete. Default is 1.0 (normalweight). | ||
|
|
||
| Returns: | ||
| float: The splitting tensile strength in MPa. | ||
|
|
||
| Raises: | ||
| ValueError: If fc is not positive. | ||
| """ | ||
| if fc <= 0: | ||
| raise ValueError(f'fc={fc} must be positive') | ||
| return 0.56 * lambda_s * math.sqrt(fc) | ||
|
|
||
|
|
||
| def alpha1() -> float: | ||
| """The ratio of equivalent rectangular stress block intensity. | ||
|
|
||
| ACI 318-19, Section 22.2.2.4.1. | ||
|
|
||
| Returns: | ||
| float: The stress block intensity factor (dimensionless). | ||
| """ | ||
| return 0.85 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| """Reinforcement material properties according to ACI 318-19.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import typing as t | ||
|
|
||
| REINFORCEMENT_GRADES = { | ||
| '40': {'fy': 280.0, 'fu': 420.0}, | ||
| '60': {'fy': 420.0, 'fu': 550.0}, | ||
| '80': {'fy': 550.0, 'fu': 690.0}, | ||
| '100': {'fy': 690.0, 'fu': 860.0}, | ||
| } | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Values for Reinforcement grades based on table 20.2.1.3(a). Please add this reference to comments. |
||
|
|
||
| def Es() -> float: | ||
| """The modulus of elasticity of reinforcement. | ||
|
|
||
| ACI 318-19, Section 20.2.2.2. | ||
|
|
||
| Returns: | ||
| float: The modulus of elasticity in MPa. | ||
| """ | ||
| return 200000.0 | ||
|
|
||
|
|
||
| def fy_design(fy: float, phi: float = 1.0) -> float: | ||
| """The design yield strength of reinforcement. | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As you say, ACI does not consider a partial factor on steel. Why do we need to provide one? The user should not be able to introduce a value different that 1.00. If we need this method for compatibility reasons we shoudl delete phi as a KWARG. |
||
| ACI 318-19 applies strength reduction factors (phi) at the | ||
| member capacity level, not the material level. The default | ||
| phi=1.0 returns the unreduced yield strength, which is the | ||
| standard ACI convention for material properties. | ||
|
|
||
| Args: | ||
| fy (float): The specified yield strength in MPa. | ||
|
|
||
| Keyword Args: | ||
| phi (float): Optional strength reduction factor. | ||
| Default is 1.0 (no reduction). | ||
|
|
||
| Returns: | ||
| float: The design yield strength in MPa. | ||
|
|
||
| Raises: | ||
| ValueError: If fy is not positive. | ||
| ValueError: If phi is not in (0, 1]. | ||
| """ | ||
| if fy <= 0: | ||
| raise ValueError(f'fy={fy} must be positive') | ||
| if phi <= 0 or phi > 1.0: | ||
| raise ValueError(f'phi={phi} must be in the range (0, 1]') | ||
| return phi * fy | ||
|
|
||
|
|
||
| def epsyd(fy: float, _Es: float = 200000.0) -> float: | ||
| """The yield strain of reinforcement. | ||
|
|
||
| Args: | ||
| fy (float): The specified yield strength in MPa. | ||
|
|
||
| Keyword Args: | ||
| _Es (float): The modulus of elasticity in MPa. | ||
| Default is 200000 MPa. | ||
|
|
||
| Returns: | ||
| float: The yield strain (dimensionless). | ||
|
|
||
| Raises: | ||
| ValueError: If fy is not positive. | ||
| """ | ||
| if fy <= 0: | ||
| raise ValueError(f'fy={fy} must be positive') | ||
| return fy / _Es | ||
|
|
||
|
|
||
| def reinforcement_grade_props( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does ACI 318-19 also provide values for the strain at ultimate strength? If so, please consider including it in the return dictionary. |
||
| grade: t.Literal['40', '60', '80', '100'], | ||
| ) -> t.Dict[str, float]: | ||
| """Return the minimum specified properties for a reinforcement grade. | ||
|
|
||
| ACI 318-19, Table 20.2.2.4a (SI equivalents). | ||
|
|
||
| Args: | ||
| grade (str): The ASTM reinforcement grade designation. | ||
| One of '40', '60', '80', or '100'. | ||
|
|
||
| Returns: | ||
| Dict[str, float]: A dict with keys 'fy' (yield strength in | ||
| MPa) and 'fu' (ultimate strength in MPa). | ||
|
|
||
| Raises: | ||
| ValueError: If the grade is not recognized. | ||
| """ | ||
| props = REINFORCEMENT_GRADES.get(str(grade)) | ||
| if props is None: | ||
| raise ValueError( | ||
| f'Unknown reinforcement grade: {grade}. ' | ||
| f'Valid grades: {list(REINFORCEMENT_GRADES.keys())}' | ||
| ) | ||
| return dict(props) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor comment about the naming.
For Eurocode 2 we decided to implement different generations in different modules, e.g.
ec2_2004for the first generation that was released in 2004, andec2_2023for the second generation that was released in 2023. The second generation is expected to become accepted as national governing design code around Europe some time in the period 2027-2028.Please correct me if I am wrong here, but my impression is that the 19 in ACI318-19 indicates that it was released in 2019. Similarly, a slightly newer version was released in 2025 and named ACI318-25. If this is right, please consider indicating this in the name of the module, e.g.
aci318_19instead of justaci318.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Morten, I agree with your comment. We need to keep different versions separate. When there is no change we can use the method of the previous version, but we may need to update the references and son on. So, yes it should be aci318-19