diff --git a/README.md b/README.md index 4dae719a..1a784cf6 100644 --- a/README.md +++ b/README.md @@ -46,23 +46,28 @@ through submitting bug reports, feature requests or pull requests. Please read t - [x] Concrete material - [x] Service stress-strain profiles - - [x] Linear profile - - [x] Linear profile (no tension) - - [x] Eurocode non-linear - - [x] Modified Mander non-linear profile (confined & unconfined concrete) + - [x] Generic piecewise linear stress-strain profile + - [x] Linear stress-strain profile + - [x] Linear (no tension) stress-strain profile + - [x] Eurocode non-linear stress-strain profile + - [x] Modified Mander non-linear (confined & unconfined concrete) stress-strain profile - [x] Ultimate stress-strain profiles - - [x] Rectangular stress block - - [x] Bilinear stress-strain profile - - [x] Eurocode parabolic - - [x] Flexural tensile strength + - [x] Generic piecewise linear stress-strain profile + - [x] Equivalent Rectangular stress-strain profile + - [x] Generic Bilinear stress-strain profile + - [x] Generic Parabolic stress-strain profile + - [x] Eurocode Bilinear stress-strain profile + - [x] Eurocode Parabolic stress-strain profile - [x] Steel material - [x] Stress-strain profiles - - [x] Elastic-plastic - - [x] Elastic-plastic (with hardening) + - [x] Generic piecewise linear stress-strain profile + - [x] Elastic-plastic stress-strain profile + - [x] Elastic-plastic (with hardening) stress-strain profile - [x] Strand material - [x] Stress-strain profiles - - [x] Elastic-plastic (with hardening) - - [x] PCI journal (1992) non-linear + - [x] Generic piecewise linear stress-strain profile + - [x] Elastic-plastic (with hardening) stress-strain profile + - [x] PCI journal (1992) non-linear stress-strain profile ### Gross Section Properties @@ -78,7 +83,7 @@ through submitting bug reports, feature requests or pull requests. Please read t - [x] Principal second moments of area - [x] Centroidal section moduli - [x] Principal section moduli -- [x] Prestressed Aations +- [x] Prestressed Actions ### Service Analysis @@ -89,8 +94,6 @@ through submitting bug reports, feature requests or pull requests. Please read t ### Ultimate Analysis - [x] Ultimate bending capacity -- [x] Squash load -- [x] Tensile load - [x] Moment interaction diagrams - [x] M-N curves - [x] Biaxial bending curve diff --git a/concreteproperties/stress_strain_profile.py b/concreteproperties/stress_strain_profile.py index b653dd45..0232d3a8 100644 --- a/concreteproperties/stress_strain_profile.py +++ b/concreteproperties/stress_strain_profile.py @@ -2,7 +2,7 @@ import warnings from dataclasses import dataclass, field -from typing import TYPE_CHECKING, List, Optional, Union +from typing import TYPE_CHECKING, List, Optional, Tuple, Union import matplotlib.pyplot as plt import numpy as np @@ -19,13 +19,24 @@ @dataclass class StressStrainProfile: - """Abstract base class for a material stress-strain profile. + r"""Abstract base class for a material stress-strain profile. Implements a piecewise linear stress-strain profile. Positive stresses & strains are - compression. + compression. The generic stress-strain relationship is defined as a series of + corresponding material strain (:math:`\varepsilon_{m(i)}`) and material stress + (:math:`\sigma_{m(i)}`) values. - :param strains: List of strains (must be increasing or equal) - :param stresses: List of stresses + Corresponding stress-strain points are sorted based on increasing values of strain. + + .. plot:: ./_static/doc_plots/generic_stress_strain_plot.py + generic_stress_strain_plot + :include-source: False + :caption: StressStrainProfile Parameters + + :param strains: List of strains, i.e. + :math:`...\varepsilon_{m(i-1)},\varepsilon_{m(i)},\varepsilon_{m(i+1)}...` + :param stresses: List of corresponding stresses, i.e + :math:`...\sigma_{m(i-1)},\sigma_{m(i)},\sigma_{m(i+1)}...` """ strains: List[float] @@ -42,16 +53,10 @@ def __post_init__( if len(self.strains) < 2: raise ValueError("Length of strains and stresses must be greater than 1") - # validate input - increasing values - prev_strain = self.strains[0] - - for idx in range(len(self.strains)): - if idx != 0: - if self.strains[idx] < prev_strain: - msg = "strains must contain increasing values." - raise ValueError(msg) - - prev_strain = self.strains[idx] + # sort stresses and strains based on increasing strains + self.strains, self.stresses = ( + list(i) for i in zip(*sorted(zip(self.strains, self.stresses))) + ) def get_stress( self, @@ -235,11 +240,26 @@ def plot_stress_strain( @dataclass class ConcreteServiceProfile(StressStrainProfile): - """Abstract class for a concrete service stress-strain profile. + r"""Abstract class for a concrete service stress-strain profile. - :param strains: List of strains (must be increasing or equal) - :param stresses: List of stresses - :param ultimate_strain: Concrete strain at failure + Implements a piecewise linear stress-strain profile. Positive stresses & strains are + compression. The generic stress-strain relationship is defined as a series of + corresponding concrete strain (:math:`\varepsilon_{c(i)}`) and concrete stress + (:math:`\sigma_{c(i)}`) values. + + Corresponding stress-strain points are sorted based on increasing values of strain. + + .. plot:: ./_static/doc_plots/generic_conc_service_plot.py + generic_conc_service_plot + :include-source: False + :caption: ConcreteServiceProfile Parameters + + :param strains: List of strains, i.e. + :math:`...\varepsilon_{c(i-1)},\varepsilon_{c(i)},\varepsilon_{c(i+1)}...` + :param stresses: List of corresponding stresses, i.e + :math:`...\sigma_{c(i-1)},\sigma_{c(i)},\sigma_{c(i+1)}...` + :param ultimate_strain: Maximum concrete compressive strain at failure + (:math:`\varepsilon_{u1}`) """ strains: List[float] @@ -317,10 +337,22 @@ def get_ultimate_compressive_strain( @dataclass class ConcreteLinear(ConcreteServiceProfile): - """Class for a symmetric linear stress-strain profile. + r"""Class for a symmetric linear stress-strain profile. + + The linear stress strain relationship is defined based on the slope of the + stress-strain curve being equal to the elastic modulus, i.e. + + :math:`\quad \displaystyle{E_c=\frac{\sigma_c}{\varepsilon_c}=\frac{\sigma_{c(i+1)} + -\sigma_{c(i)}}{\varepsilon_{c(i+1)}-\varepsilon_{c(i)}}}` - :param elastic_modulus: Elastic modulus of the stress-strain profile - :param ultimate_strain: Concrete strain at failure + .. plot:: ./_static/doc_plots/generic_linear_service_plot.py + generic_linear_service_plot + :include-source: False + :caption: ConcreteLinear Parameters + + :param elastic_modulus: Elastic modulus of the stress-strain profile (:math:`E_c`) + :param ultimate_strain: Maximum concrete compressive strain at failure + (:math:`\varepsilon_{u1}`) """ strains: List[float] = field(init=False) @@ -337,10 +369,27 @@ def __post_init__( @dataclass class ConcreteLinearNoTension(ConcreteServiceProfile): - """Class for a linear stress-strain profile with no tensile strength. + r"""Class for a linear stress-strain profile with no tensile strength. + + The linear stress strain relationship is defined based on the slope of the + stress-strain curve being equal to the elastic modulus, i.e. + + :math:`\quad \displaystyle{E_c=\frac{\sigma_c}{\varepsilon_c}=\frac{ + \sigma_{c(i+1)}-\sigma_{c(i)}}{\varepsilon_{c(i+1)}-\varepsilon_{c(i)}}}` + + If the concrete compressive strength and ultimate compressive strain are specified + then a constant stress return is added from the yield strain + (:math:`\varepsilon_{y1}`) up to the specified ultimate + compressive strain (:math:`\varepsilon_{u1}`). - :param elastic_modulus: Elastic modulus of the stress-strain profile - :param ultimate_strain: Concrete strain at failure + .. plot:: ./_static/doc_plots/generic_linear_no_tension_service_plot.py + generic_linear_no_tension_service_plot + :include-source: False + :caption: ConcreteLinearNoTension Parameters + + :param elastic_modulus: Elastic modulus of the stress-strain profile (:math:`E_c`) + :param ultimate_strain: Maximum concrete compressive strain at failure + (:math:`\varepsilon_{u1}`) :param compressive_strength: Compressive strength of the concrete """ @@ -372,11 +421,12 @@ class EurocodeNonLinear(ConcreteServiceProfile): ``tension_softening_stiffness``. :param elastic_modulus: Concrete elastic modulus (:math:`E_{cm}`) - :param ultimate_strain: Concrete strain at failure (:math:`\epsilon_{cu1}`) + :param ultimate_strain: Maximum concrete compressive strain at failure + (:math:`\varepsilon_{cu1}`) :param compressive_strength: Concrete compressive strength (:math:`f_{cm}`) - :param compressive_strain: Strain at which the concrete stress equals the - compressive strength (:math:`\epsilon_{c1}`) - :param tensile_strength: Concrete tensile strength + :param compressive_strain: Strain at which the maximum concrete strength is reached + (:math:`\varepsilon_{c1}`) + :param tensile_strength: Concrete tensile strength :param tension_softening_stiffness: Slope of the linear tension softening branch :param n_points_1: Number of points to discretise the curve prior to the peak stress @@ -822,11 +872,25 @@ def __post_init__( @dataclass class ConcreteUltimateProfile(StressStrainProfile): - """Abstract class for a concrete ultimate stress-strain profile. + r"""Abstract class for a concrete ultimate stress-strain profile. - :param strains: List of strains (must be increasing or equal) - :param stresses: List of stresses - :param compressive_strength: Concrete compressive strength + Implements a piecewise linear stress-strain profile. Positive stresses & strains are + compression. The generic stress-strain relationship is defined as a series of + corresponding concrete strain (:math:`\varepsilon_{c(i)}`) and concrete stress + (:math:`\sigma_{c(i)}`) values. + + Corresponding stress-strain points are sorted based on increasing values of strain. + + .. plot:: ./_static/doc_plots/generic_conc_ultimate_plot.py + generic_conc_ultimate_plot + :include-source: False + :caption: ConcreteUltimateProfile Parameters + + :param strains: List of strains, i.e. + :math:`...\varepsilon_{c(i-1)},\varepsilon_{c(i)},\varepsilon_{c(i+1)}...` + :param stresses: List of corresponding stresses, i.e + :math:`...\sigma_{c(i-1)},\sigma_{c(i)},\sigma_{c(i+1)}...` + :param compressive_strength: Concrete compressive strength (:math:`f'_c`) """ strains: List[float] @@ -883,12 +947,35 @@ def print_properties( @dataclass class RectangularStressBlock(ConcreteUltimateProfile): - """Class for a rectangular stress block. + r"""Class for an equivalent rectangular stress block. + + The equivalent rectangular stress block stress-strain relationship can be defined + as:- + + - A uniformly distributed concrete stress of :math:`\alpha f'_c` acting over a + depth of :math:`\gamma d_n` from the extreme compressive fibre, where + :math:`d_n` is the neutral axis depth measured from the extreme compressive + fibre to the neutral axis. + + - Alternatively this can be expressed as a uniformaly distributed concrete stress of + :math:`\alpha f'_c` acting where the concrete strain :math:`\varepsilon_c` + satisfies the relationship + :math:`\varepsilon_{1}\leq\varepsilon_c\leq\varepsilon_{u1}`. + Where the transition strain :math:`\varepsilon_{1}` is defined as being + :math:`\varepsilon_{1}=\varepsilon_{u1}(1-\gamma)` and :math:`\varepsilon_{u1}` + is defined as the ultimate compressive strain. + + .. plot:: ./_static/doc_plots/generic_rect_ultimate_plot.py + generic_rect_ultimate_plot + :include-source: False + :caption: RectangularStressBlock Parameters - :param compressive_strength: Concrete compressive strength + :param compressive_strength: Concrete compressive strength (:math:`f'_c`) :param alpha: Factor that modifies the concrete compressive strength - :param gamma: Factor that modifies the depth of the stress block - :param ultimate_strain: Concrete strain at failure + (:math:`\alpha`) + :param gamma: Factor that modifies the depth of the stress block (:math:`\gamma`) + :param ultimate_strain: Maximum concrete compressive strain at failure + (:math:`\varepsilon_{u1}`) """ strains: List[float] = field(init=False) @@ -936,12 +1023,18 @@ def get_stress( @dataclass class BilinearStressStrain(ConcreteUltimateProfile): - """Class for a bilinear stress-strain relationship. + r"""Class for an ultimate bilinear stress-strain relationship. + + .. plot:: ./_static/doc_plots/generic_bilinear_ultimate_plot.py + generic_bilinear_ultimate_plot + :include-source: False + :caption: BilinearStressStrain Parameters - :param compressive_strength: Concrete compressive strength - :param compressive_strain: Strain at which the concrete stress equals the - compressive strength - :param ultimate_strain: Concrete strain at failure + :param compressive_strength: Concrete compressive strength (:math:`f'_c`) + :param compressive_strain: Strain at which the maximum concrete strength is reached + (:math:`\varepsilon_{1}`) + :param ultimate_strain: Maximum concrete compressive strain at failure + (:math:`\varepsilon_{u1}`) """ strains: List[float] = field(init=False) @@ -968,15 +1061,26 @@ def __post_init__( @dataclass -class EurocodeParabolicUltimate(ConcreteUltimateProfile): - """Class for an ultimate parabolic stress-strain relationship to EC2. +class ParabolicStressStrain(ConcreteUltimateProfile): + r"""Class for an ultimate parabolic stress-strain relationship. + + Note parabolic portion of the stress-strain relationship is based on the EC2 + derivation, refer to + :class:`~concreteproperties.stress_strain_profile.EurocodeParabolicUltimate` class + for further details. - :param compressive_strength: Concrete compressive strength - :param compressive_strain: Strain at which the concrete stress equals the - compressive strength - :param ultimate_strain: Concrete strain at failure - :param n: Parabolic curve exponent + :param compressive_strength: Concrete compressive strength (:math:`f'_c`) + :param compressive_strain: Strain at which the maximum concrete strength is reached + (:math:`\varepsilon_{1}`) + :param ultimate_strain: Maximum concrete compressive strain at failure + (:math:`\varepsilon_{u1}`) + :param n_exp: Parabolic curve exponent :param n_points: Number of points to discretise the parabolic segment of the curve + + .. plot:: ./_static/doc_plots/generic_parabolic_ultimate_plot.py + generic_parabolic_ultimate_plot + :include-source: False + :caption: ParabolicStressStrain Parameters """ strains: List[float] = field(init=False) @@ -984,7 +1088,7 @@ class EurocodeParabolicUltimate(ConcreteUltimateProfile): compressive_strength: float compressive_strain: float ultimate_strain: float - n: float + n_exp: float n_points: int = field(default=10) def __post_init__( @@ -1003,7 +1107,7 @@ def __post_init__( for idx in range(self.n_points): conc_strain = self.compressive_strain / self.n_points * (idx + 1) conc_stress = self.compressive_strength * ( - 1 - np.power(1 - (conc_strain / self.compressive_strain), self.n) + 1 - np.power(1 - (conc_strain / self.compressive_strain), self.n_exp) ) self.strains.append(conc_strain) @@ -1014,6 +1118,416 @@ def __post_init__( self.stresses.append(self.compressive_strength) +@dataclass +class EurocodeBilinearUltimate(ConcreteUltimateProfile): + r"""Class for an ultimate bilinear stress-strain relationship to EC2. + + The stress-strain relationship is defined as follows:- + + With the design concrete compressive strength (:math:`f_{cd}`) being defined in EC2 + CL 3.1.6(1)P as:- + + :math:`\quad f_{cd}=\displaystyle{\frac{\alpha_{cc}f_{ck}}{\gamma_C}}` + + And the stress & strain relationship being defined in EC2 CL 3.1.7(2) as:- + + :math:`\quad\sigma_c=f_{cd}\displaystyle{\bigg[\frac{\varepsilon_c} + {\varepsilon_{c3}}\bigg]}` for :math:`0\leq\varepsilon_c\leq\varepsilon_{c3}` + + :math:`\quad\sigma_c=f_{cd}` for :math:`\varepsilon_{c3}\leq\varepsilon_c\leq + \varepsilon_{cu3}` + + + .. note:: + The default recommended values for the stress-strain relationship parameters + :math:`\alpha_{cc}` & :math:`\gamma_C` are taken as per EC2. Note that by default + the design situation for the default value of :math:`\gamma_C` assumes a + 'Persistent & Transient' load case verses an 'Accidental' load case. + + However, note that a Countries National Annex may modify the value of these + parameters. If this is the case then provide the values for the variable(s) to + override the default EC2 value(s) as required. + + .. tip:: + Note, if utilising the + :class:`~concreteproperties.stress_strain_profile.EurocodeBilinearUltimate` + stress-strain profile with design codes (other than EC2) which might utilise a + strength reduction factor (:math:`\phi`) based approach verses a partial factor of + safety (:math:`\gamma`) based approach, then a + :class:`~concreteproperties.stress_strain_profile.EurocodeBilinearUltimate` + stress-strain profile can be adopted provided that consideration is given to the + following:- + + - The :math:`\gamma_C` factor should generally be taken as being 1.0, and the + strength reduction factor should be applied to the design ultimate strength or + material properties in the normal manner associated with the design code. + + - The concrete compressive strength should have an appropriate reduction factor + applied to it which is consistent with the design code via the + :math:`\alpha_{cc}` factor. + + .. plot:: ./_static/doc_plots/ec2_bilinear_ultimate_plot.py + ec2_bilinear_ultimate_plot + :include-source: False + :caption: EurocodeBilinearParabolicUltimate Parameters + + :param compressive_strength: Characteristic concrete compressive cylinder strength + (:math:`f_{ck}`) + :param limiting_strain: Upper limit on the strain considered in the generated + stress-strain profile. This variable should be utilised when using the EC2 + bilinear stress-strain relationship with other design codes that have a + lower limit on the ultimate compressive strain than the maximum compressive + strain of 0.0035 considered in EC2. In this case the EC2 curve is truncated at + the limiting strain specified if applicable + :param alpha_cc: Coefficient to take account of long term effects on the compressive + strength and of unfavourable effects resulting for the way the load is applied + (:math:`\alpha_{cc}`) + + - The recommended value in EC2 CL 3.1.6(1)P is 1.0. However, the value of + :math:`\alpha_{cc}` for use in a Country should lie between 0.8 and 1.0 and + may be found in that Countries National Annex + + :param gamma_c: Partial factor of safety for concrete (:math:`\gamma_C`) in + accordance with EC2 CL 2.4.2.4 or a Countries National Annex + + - The recommended values from EC2 CL 2.4.2.4 are as outlined below. However, the + value of :math:`\gamma_C` may be found in a Countries National Annex + + +------------------------+---------------------------------------+ + | Design Situations | Partial Factor Of Safety for Concrete | + +========================+=======================================+ + | Persistent & Transient | 1.5 | + +------------------------+---------------------------------------+ + | Accidental | 1.2 | + +------------------------+---------------------------------------+ + + - Note that the 'Persistent & Transient' value is utilised by default unless a + user defined value is provided + """ + + strains: List[float] = field(init=False) + stresses: List[float] = field(init=False) + compressive_strength: float + ultimate_strain: float = field(init=False, default=0) + limiting_strain: float = 0.0035 + alpha_cc: float = 1.0 + gamma_c: float = 1.5 + + def __post_init__( + self, + ): + self.strains = [] + self.stresses = [] + + # determine transition and ultimate compressive strains + epsilon_c3, epsilon_cu3 = self.epsilon_bilinear( + self.compressive_strength, self.limiting_strain + ) + + # determine design concrete compressive strength + f_cd = self.alpha_cc * self.compressive_strength / self.gamma_c + + # tensile portion of curve + self.strains.append(-epsilon_c3) + self.stresses.append(0) + self.strains.append(0) + self.stresses.append(0) + + # linear sloping portion of curve + self.strains.append(epsilon_c3) + self.stresses.append(f_cd) + + # compressive plateau + self.strains.append(epsilon_cu3) + self.stresses.append(f_cd) + + # initiate ultimate compressive strain as maximum strain + self.ultimate_strain = max(self.strains) + + # add small horizontal compressive strain to improve interpolation + self.strains.append(self.strains[-1] + 1e-12) + self.stresses.append(self.stresses[-1]) + + def epsilon_bilinear( + self, f_ck: float, limiting_strain: float = 0.0035 + ) -> Tuple[float, float]: + r"""Function to calculate the strain at which the maximum concrete strength is + reached (:math:`\epsilon_{c3}`) and the ultimate compressive strain + (:math:`\epsilon_{cu3}`) for the EC2 bilinear stress-strain relationship. Refer + to EC2 Table 3.1. + + :param f_ck: Characteristic concrete compressive cylinder strength + (:math:`f_{ck}`) + :param limiting_strain: Upper limit on the strain considered in the generated + stress-strain profile. This variable should be utilised when using the EC2 + parabolic stress-strain relationship with other design codes that have a + lower limit on the ultimate compressive strain than the maximum compressive + strain of 0.0035 considered in EC2. In this case the returned strains are + limited to the limiting strain specified if applicable + + :raises ValueError: If concrete strength provided is outside the bounds of 12 + MPa and 90 MPa + :raises ValueError: If the specified limiting strain is greater than that + considered by the EC2 design code + :return: Strain at which maximum concrete strength is reached + (:math:`\epsilon_{c3}`) & the ultimate compressive strain + (:math:`\epsilon_{cu3}`) + """ + + # Check if user defined limiting strain is greater than maximum ultimate + # compressive strain from EC2 + if limiting_strain > 0.0035: + raise ValueError( + f"The limiting strain of {limiting_strain} provided is greater " + f" than the 0.0035 limiting ultimate strain from EC2" + ) + + if 12 <= f_ck <= 90: + # EC2 table 3.1, epsilon_c2 & epsilon_cu2 + if f_ck < 50: + epsilon_c = 1.75 / 1000 + epsilon_cu = 3.5 / 1000 + else: + epsilon_c = (1.75 + 0.55 * ((f_ck - 50) / 40)) / 1000 + epsilon_cu = (2.6 + 35 * ((90 - f_ck) / 100) ** 4) / 1000 + else: + raise ValueError( + f"Concrete compressive strength should be between 12 MPa & 90 MPa for " + f"EC2, a compressive strength of {f_ck:.2f} MPa was provided." + ) + + # limit strain to an upper bound based on the defined limiting strain. + if epsilon_c > limiting_strain: + epsilon_c = limiting_strain + if epsilon_cu > limiting_strain: + epsilon_cu = limiting_strain + + return epsilon_c, epsilon_cu + + +@dataclass +class EurocodeParabolicUltimate(ConcreteUltimateProfile): + r"""Class for an ultimate parabolic stress-strain relationship to EC2. + + The stress-strain relationship is defined as follows:- + + With the design concrete compressive strength (:math:`f_{cd}`) being defined in EC2 + CL 3.1.6(1)P as:- + + :math:`\quad f_{cd}=\displaystyle{\frac{\alpha_{cc}f_{ck}}{\gamma_C}}` + + And the stress & strain relationship being defined in EC2 CL 3.1.7(1) as:- + + :math:`\quad\sigma_c=f_{cd}\displaystyle{\bigg[1-\Big(1-\frac{\varepsilon_c} + {\varepsilon_{c2}}\Big)^n\bigg]}` for :math:`0\leq\varepsilon_c\leq\varepsilon_{c2}` + + :math:`\quad\sigma_c=f_{cd}` for :math:`\varepsilon_{c2}\leq\varepsilon_c\leq + \varepsilon_{cu2}` + + .. note:: + The default recommended values for the stress-strain relationship parameters + :math:`\alpha_{cc}` & :math:`\gamma_C` are taken as per EC2. Note that by default + the design situation for the default value of :math:`\gamma_C` assumes a + 'Persistent & Transient' load case verses an 'Accidental' load case. + + However, note that a Countries National Annex may modify the value of these + parameters. If this is the case then provide the values for the variable(s) to + override the default EC2 value(s) as required. + + .. tip:: + Note, if utilising the + :class:`~concreteproperties.stress_strain_profile.EurocodeParabolicUltimate` + stress-strain profile with design codes (other than EC2) which might utilise a + strength reduction factor (:math:`\phi`) based approach verses a partial factor of + safety (:math:`\gamma`) based approach, then a + :class:`~concreteproperties.stress_strain_profile.EurocodeParabolicUltimate` + stress-strain profile can be adopted provided that consideration is given to the + following:- + + - The :math:`\gamma_C` factor should generally be taken as being 1.0, and the + strength reduction factor should be applied to the design ultimate strength or + material properties in the normal manner associated with the design code. + + - The concrete compressive strength should have an appropriate reduction factor + applied to it which is consistent with the design code via the + :math:`\alpha_{cc}` factor. + + .. plot:: ./_static/doc_plots/ec2_parabolic_ultimate_plot.py + ec2_parabolic_ultimate_plot + :include-source: False + :caption: EurocodeParabolicUltimate Parameters + + :param compressive_strength: Characteristic concrete compressive cylinder strength + (:math:`f_{ck}`) + :param limiting_strain: Upper limit on the strain considered in the generated + stress-strain profile. This variable should be utilised when using the EC2 + parabolic stress-strain relationship with other design codes that have a + lower limit on the ultimate compressive strain than the maximum compressive + strain of 0.0035 considered in EC2. In this case the EC2 curve is truncated at + the limiting strain specified if applicable + :param alpha_cc: Coefficient to take account of long term effects on the compressive + strength and of unfavourable effects resulting for the way the load is applied + (:math:`\alpha_{cc}`) + + - The recommended value in EC2 CL 3.1.6(1)P is 1.0. However, the value of + :math:`\alpha_{cc}` for use in a Country should lie between 0.8 and 1.0 and + may be found in that Countries National Annex + + :param gamma_c: Partial factor of safety for concrete (:math:`\gamma_C`) in + accordance with EC2 CL 2.4.2.4 or a Countries National Annex + + - The recommended values from EC2 CL 2.4.2.4 are as outlined below. However, the + value of :math:`\gamma_C` may be found in a Countries National Annex + + +------------------------+---------------------------------------+ + | Design Situations | Partial Factor Of Safety for Concrete | + +========================+=======================================+ + | Persistent & Transient | 1.5 | + +------------------------+---------------------------------------+ + | Accidental | 1.2 | + +------------------------+---------------------------------------+ + + - Note that the 'Persistent & Transient' value is utilised by default unless a + user defined value is provided + + :param n_points: Number of points to discretise the parabolic segment of the curve + """ + + strains: List[float] = field(init=False) + stresses: List[float] = field(init=False) + compressive_strength: float + ultimate_strain: float = field(init=False, default=0) + limiting_strain: float = 0.0035 + alpha_cc: float = 1.0 + gamma_c: float = 1.5 + n_points: int = field(default=10) + + def __post_init__( + self, + ): + self.strains = [] + self.stresses = [] + + # determine n exponent + n_exp = self.n_exp(self.compressive_strength) + + # determine transition and ultimate compressive strains + epsilon_c2, epsilon_cu2 = self.epsilon_parabolic( + self.compressive_strength, self.limiting_strain + ) + + # determine design concrete compressive strength + f_cd = self.alpha_cc * self.compressive_strength / self.gamma_c + + # tensile portion of curve + self.strains.append(-epsilon_c2) + self.stresses.append(0) + self.strains.append(0) + self.stresses.append(0) + + # parabolic portion of curve + for idx in range(self.n_points): + conc_strain = epsilon_c2 / self.n_points * (idx + 1) + conc_stress = f_cd * (1 - np.power(1 - (conc_strain / epsilon_c2), n_exp)) + + self.strains.append(conc_strain) + self.stresses.append(conc_stress) + + # compressive plateau + self.strains.append(epsilon_cu2) + self.stresses.append(f_cd) + + # initiate ultimate compressive strain as maximum strain + self.ultimate_strain = max(self.strains) + + # add small horizontal compressive strain to improve interpolation + self.strains.append(self.strains[-1] + 1e-12) + self.stresses.append(self.stresses[-1]) + + def n_exp(self, f_ck: float) -> float: + """Function to calculate the 'n' exponent in EC2 stress block equation, refer to + EC2 Table 3.1. + + :param f_ck: Characteristic concrete compressive cylinder strength + (:math:`f_{ck}`) + :raises ValueError: If concrete strength provided is outside the EC2 bounds of + 12 MPa and 90 MPa + :return: :math:`n` exponential + """ + + if 12 <= f_ck <= 90: + n_exp = 2 if f_ck < 50 else 1.4 + 23.4 * ((90 - f_ck) / 100) ** 4 + else: + raise ValueError( + f"Concrete compressive strength should be between 12 MPa & 90 MPa for " + f"EC2, a compressive strength of {f_ck:.2f} MPa was provided." + ) + + return n_exp + + def epsilon_parabolic( + self, f_ck: float, limiting_strain: float = 0.0035 + ) -> Tuple[float, float]: + r"""Function to calculate the strain at which the maximum concrete strength is + reached (:math:`\epsilon_{c2}`) and the ultimate compressive strain + (:math:`\epsilon_{cu2}`) for the EC2 parabolic stress-strain relationship. Refer + to EC2 Table 3.1. + + :param f_ck: Characteristic concrete compressive cylinder strength + (:math:`f_{ck}`) + :param limiting_strain: Upper limit on the strain considered in the generated + stress-strain profile. This variable should be utilised when using the EC2 + parabolic stress-strain relationship with other design codes that have a + lower limit on the ultimate compressive strain than the maximum compressive + strain of 0.0035 considered in EC2. In this case the returned strains are + limited to the limiting strain specified if applicable + + :raises ValueError: If concrete strength provided is outside the bounds of 12 + MPa and 90 MPa + :raises ValueError: If the specified limiting strain is greater than that + considered by the EC2 design code + :return: Strain at which maximum concrete strength is reached + (:math:`\epsilon_{c2}`) & the ultimate compressive strain + (:math:`\epsilon_{cu2}`) + """ + + # Check if user defined limiting strain is greater than maximum ultimate + # compressive strain from EC2 + if limiting_strain > 0.0035: + raise ValueError( + f"The limiting strain of {limiting_strain} provided is greater " + f" than the 0.0035 limiting ultimate strain from EC2" + ) + + if 12 <= f_ck <= 90: + # EC2 table 3.1, epsilon_c2 & epsilon_cu2 + if f_ck < 50: + epsilon_c = 2 / 1000 + epsilon_cu = 3.5 / 1000 + else: + epsilon_c = (2 + 0.085 * (f_ck - 50) ** 0.53) / 1000 + epsilon_cu = (2.6 + 35 * ((90 - f_ck) / 100) ** 4) / 1000 + else: + raise ValueError( + f"Concrete compressive strength should be between 12 MPa & 90 MPa for " + f"EC2, a compressive strength of {f_ck:.2f} MPa was provided." + ) + + # around a concrete strength of 90MPa, epsilon_c2 creeps over epsilon_cu2 by a + # very small margin, effectively there is no constant stress plateau at 90MPa + # concrete strength + if epsilon_c > epsilon_cu: + epsilon_c = epsilon_cu + + # limit strain to an upper bound based on the defined limiting strain. + if epsilon_c > limiting_strain: + epsilon_c = limiting_strain + if epsilon_cu > limiting_strain: + epsilon_cu = limiting_strain + + return epsilon_c, epsilon_cu + + @dataclass class SteelProfile(StressStrainProfile): """Abstract class for a steel stress-strain profile. @@ -1162,7 +1676,9 @@ class StrandProfile(StressStrainProfile): Implements a piecewise linear stress-strain profile. Positive stresses & strains are compression. - :param strains: List of strains (must be increasing or equal) + Corresponding stress-strain points are sorted based on increasing values of strain. + + :param strains: List of strains :param stresses: List of stresses :param yield_strength: Strand yield strength """ diff --git a/concreteproperties/tests/test_stress_strain_profile.py b/concreteproperties/tests/test_stress_strain_profile.py index 770a8dd2..47f54c32 100644 --- a/concreteproperties/tests/test_stress_strain_profile.py +++ b/concreteproperties/tests/test_stress_strain_profile.py @@ -24,8 +24,8 @@ def test_piecewise_linear(): with pytest.raises(ValueError): profile = StressStrainProfile([-1, 0, 1], [0, 2]) - with pytest.raises(ValueError): - profile = StressStrainProfile([0, 1, 0.5], [0, 3, 5]) + # with pytest.raises(ValueError): + # profile = StressStrainProfile([0, 1, 0.5], [0, 3, 5]) profile = StressStrainProfile([-0.05, 0, 0.0025, 0.05], [0, 0, 500, 600]) diff --git a/docs/source/_static/doc_plots/ec2_bilinear_ultimate_plot.py b/docs/source/_static/doc_plots/ec2_bilinear_ultimate_plot.py new file mode 100644 index 00000000..922c550c --- /dev/null +++ b/docs/source/_static/doc_plots/ec2_bilinear_ultimate_plot.py @@ -0,0 +1,185 @@ +import matplotlib.pyplot as plt +import concreteproperties.stress_strain_profile as ssp + + +def ec2_bilinear_ultimate_plot(render=False): + """Creates a plot for use in the docstring of EurocodeBilinearUltimate class, + generates a plot with stress-strain parameters shown to aid in + interpreting class variables. + + :param render: Set to True to plot for testing purposes, note will plot + automatically in a docstring plot directive when set to default of False + """ + # create EurocodeBilinearUltimate stress-strain profile + compressive_strength = 40 + alpha_cc = 1.0 + gamma_c = 1.5 + f_ck = compressive_strength + f_cd = alpha_cc * f_ck / gamma_c + + stress_strain_profile = ssp.EurocodeBilinearUltimate( + compressive_strength=f_ck, + alpha_cc=alpha_cc, + gamma_c=gamma_c, + ) + + stress_strain_profile_nominal = ssp.EurocodeBilinearUltimate( + compressive_strength=f_ck, + alpha_cc=1, + gamma_c=1, + ) + # overide default tension branch strain + stress_strain_profile.strains[0] = 0 + + # add return of stress-strain diagram to zero stress + stress_strain_profile.stresses.append(0) + stress_strain_profile.strains.append(stress_strain_profile.strains[-1]) + + # overide default tension branch strain + stress_strain_profile_nominal.strains[0] = 0 + + # add return of stress-strain diagram to f_cd + stress_strain_profile_nominal.stresses.append(stress_strain_profile.stresses[-2]) + stress_strain_profile_nominal.strains.append(stress_strain_profile.strains[-1]) + + # plot design stress-strain relationship + ax = stress_strain_profile.plot_stress_strain( + fmt="-k", render=False, linewidth=1, figsize=(8, 6) + ) + + # plot nominal stress-strian relationship + ax.plot( + stress_strain_profile_nominal.strains, + stress_strain_profile_nominal.stresses, + color="k", + lw=1.25, + ls="--", + dashes=[12, 6], + ) + + # add fake origin axes lines + plt.axvline(linewidth=1.5, color="grey") + plt.axhline(linewidth=1.5, color="grey") + + # add arrows at end of origin axes + ax.plot( + (1), + (0), + ls="", + marker=">", + ms=10, + color="k", + transform=ax.get_yaxis_transform(), + clip_on=False, + ) + ax.plot( + (0), + (1), + ls="", + marker="^", + ms=10, + color="k", + transform=ax.get_xaxis_transform(), + clip_on=False, + ) + + # add title and axes labels + plt.title(label="Eurocode 2 Bilinear Stress-Strain Profile").set_fontsize(16) + plt.xlabel("Concrete Strain $\\varepsilon_c$", labelpad=10).set_fontsize(16) + plt.ylabel("Concrete Stress $\sigma_c$", labelpad=10).set_fontsize(16) + + # define data for annotations + f_cd = max(stress_strain_profile.stresses) + eps_c3, eps_cu3 = stress_strain_profile.epsilon_bilinear(f_ck=f_ck) + x = [ + 0, + eps_c3, + eps_cu3, + eps_c3, + eps_cu3, + ] + y = [ + 0, + f_cd, + f_cd, + f_ck, + f_ck, + ] + x_annotation = [ + "$0$", + "", + "", + "$\\varepsilon_{c3}$", + "$\\varepsilon_{cu3}$", + ] + y_label = [ + 0, + f_cd, + f_ck, + ] + y_annotation = [ + "$0$", + "$f_{cd}$", + "$f_{ck}$", + ] + + # add markers + plt.plot(x, y, "ok", ms=6) + + # add tick labels for control points + plt.xticks(x, labels=x_annotation, fontsize=16) + plt.yticks(y_label, labels=y_annotation, fontsize=16) + + # set min axes extent + xmin, xmax, ymin, ymax = plt.axis() + ax.axes.set_xlim(xmin) + ax.axes.set_ylim(ymin) + + # add line to maximum strength f_cd at eps_c3 + plt.plot( + [xmin, eps_c3, eps_c3], + [f_cd, f_cd, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add line to maximum strength f_ck at eps_c3 + plt.plot( + [xmin, eps_c3, eps_c3], + [f_ck, f_ck, f_cd], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add line to maximum strength f_cd at eps_cu3 + plt.plot( + [xmin, eps_cu3, eps_cu3], + [f_cd, f_cd, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add fill + plt.fill_between( + stress_strain_profile.strains, + stress_strain_profile.stresses, + 0, + alpha=0.15, + color="grey", + ) + + # Turn off grid + plt.grid(False) + + # turn off plot border + plt.box(False) + + # turn on tight layout + plt.tight_layout() + + # plot if required for testing purposes + if render: + plt.show() diff --git a/docs/source/_static/doc_plots/ec2_parabolic_ultimate_plot.py b/docs/source/_static/doc_plots/ec2_parabolic_ultimate_plot.py new file mode 100644 index 00000000..32d7b2b0 --- /dev/null +++ b/docs/source/_static/doc_plots/ec2_parabolic_ultimate_plot.py @@ -0,0 +1,188 @@ +import matplotlib.pyplot as plt +import concreteproperties.stress_strain_profile as ssp + + +def ec2_parabolic_ultimate_plot(render=False): + """Creates a plot for use in the docstring of EurocodeParabolicUltimate class, + generates a plot with stress-strain parameters shown to aid in + interpreting class variables. + + :param render: Set to True to plot for testing purposes, note will plot + automatically in a docstring plot directive when set to default of False + """ + # create EurocodeParabolicUltimate stress-strain profile + compressive_strength = 40 + alpha_cc = 1.0 + gamma_c = 1.5 + f_ck = compressive_strength + f_cd = alpha_cc * f_ck / gamma_c + n_points = 50 + + stress_strain_profile = ssp.EurocodeParabolicUltimate( + compressive_strength=f_ck, + alpha_cc=alpha_cc, + gamma_c=gamma_c, + n_points=n_points, + ) + + stress_strain_profile_nominal = ssp.EurocodeParabolicUltimate( + compressive_strength=f_ck, + alpha_cc=1, + gamma_c=1, + n_points=n_points, + ) + # overide default tension branch strain + stress_strain_profile.strains[0] = 0 + + # add return of stress-strain diagram to zero stress + stress_strain_profile.stresses.append(0) + stress_strain_profile.strains.append(stress_strain_profile.strains[-1]) + + # overide default tension branch strain + stress_strain_profile_nominal.strains[0] = 0 + + # add return of stress-strain diagram to f_cd + stress_strain_profile_nominal.stresses.append(stress_strain_profile.stresses[-2]) + stress_strain_profile_nominal.strains.append(stress_strain_profile.strains[-1]) + + # plot design stress-strain relationship + ax = stress_strain_profile.plot_stress_strain( + fmt="-k", render=False, linewidth=1, figsize=(8, 6) + ) + + # plot nominal stress-strian relationship + ax.plot( + stress_strain_profile_nominal.strains, + stress_strain_profile_nominal.stresses, + color="k", + lw=1.25, + ls="--", + dashes=[12, 6], + ) + + # add fake origin axes lines + plt.axvline(linewidth=1.5, color="grey") + plt.axhline(linewidth=1.5, color="grey") + + # add arrows at end of origin axes + ax.plot( + (1), + (0), + ls="", + marker=">", + ms=10, + color="k", + transform=ax.get_yaxis_transform(), + clip_on=False, + ) + ax.plot( + (0), + (1), + ls="", + marker="^", + ms=10, + color="k", + transform=ax.get_xaxis_transform(), + clip_on=False, + ) + + # add title and axes labels + plt.title(label="Eurocode 2 Parabolic Stress-Strain Profile").set_fontsize(16) + plt.xlabel("Concrete Strain $\\varepsilon_c$", labelpad=10).set_fontsize(16) + plt.ylabel("Concrete Stress $\sigma_c$", labelpad=10).set_fontsize(16) + + # define data for annotations + f_cd = max(stress_strain_profile.stresses) + eps_c2, eps_cu2 = stress_strain_profile.epsilon_parabolic(f_ck=f_ck) + x = [ + 0, + eps_c2, + eps_cu2, + eps_c2, + eps_cu2, + ] + y = [ + 0, + f_cd, + f_cd, + f_ck, + f_ck, + ] + x_annotation = [ + "$0$", + "", + "", + "$\\varepsilon_{c2}$", + "$\\varepsilon_{cu2}$", + ] + y_label = [ + 0, + f_cd, + f_ck, + ] + y_annotation = [ + "$0$", + "$f_{cd}$", + "$f_{ck}$", + ] + + # add markers + plt.plot(x, y, "ok", ms=6) + + # add tick labels for control points + plt.xticks(x, labels=x_annotation, fontsize=16) + plt.yticks(y_label, labels=y_annotation, fontsize=16) + + # set min axes extent + xmin, xmax, ymin, ymax = plt.axis() + ax.axes.set_xlim(xmin) + ax.axes.set_ylim(ymin) + + # add line to maximum strength f_cd at eps_c2 + plt.plot( + [xmin, eps_c2, eps_c2], + [f_cd, f_cd, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add line to maximum strength f_ck at eps_c2 + plt.plot( + [xmin, eps_c2, eps_c2], + [f_ck, f_ck, f_cd], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add line to maximum strength f_cd at esp_cu2 + plt.plot( + [xmin, eps_cu2, eps_cu2], + [f_cd, f_cd, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add fill + plt.fill_between( + stress_strain_profile.strains, + stress_strain_profile.stresses, + 0, + alpha=0.15, + color="grey", + ) + + # Turn off grid + plt.grid(False) + + # turn off plot border + plt.box(False) + + # turn on tight layout + plt.tight_layout() + + # plot if required for testing purposes + if render: + plt.show() diff --git a/docs/source/_static/doc_plots/generic_bilinear_ultimate_plot.py b/docs/source/_static/doc_plots/generic_bilinear_ultimate_plot.py new file mode 100644 index 00000000..ab0f7c7b --- /dev/null +++ b/docs/source/_static/doc_plots/generic_bilinear_ultimate_plot.py @@ -0,0 +1,142 @@ +import matplotlib.pyplot as plt +import concreteproperties.stress_strain_profile as ssp + + +def generic_bilinear_ultimate_plot(render=False): + """Creates a plot for use in the docstring of BilinearStressStrain class, + generates a plot with stress-strain parameters shown to aid in + interpreting class variables. + + :param render: Set to True to plot for testing purposes, note will plot + automatically in a docstring plot directive when set to default of False + """ + # create BilinearStressStrain stress-strain profile + compressive_strength = 40 + compressive_strain = 0.002 + ultimate_strain = 0.0035 + + stress_strain_profile = ssp.BilinearStressStrain( + compressive_strength=compressive_strength, + compressive_strain=compressive_strain, + ultimate_strain=ultimate_strain, + ) + + # overide default tension branch strain + stress_strain_profile.strains[0] = 0 + + # add return of stress-strain diagram to zero stress + stress_strain_profile.stresses.append(0) + stress_strain_profile.strains.append(stress_strain_profile.strains[-1]) + + # plot design stress-strain relationship + ax = stress_strain_profile.plot_stress_strain( + fmt="-k", render=False, linewidth=1, figsize=(8, 6) + ) + + # add fake origin axes lines + plt.axvline(linewidth=1.5, color="grey") + plt.axhline(linewidth=1.5, color="grey") + + # add arrows at end of origin axes + ax.plot( + (1), + (0), + ls="", + marker=">", + ms=10, + color="k", + transform=ax.get_yaxis_transform(), + clip_on=False, + ) + ax.plot( + (0), + (1), + ls="", + marker="^", + ms=10, + color="k", + transform=ax.get_xaxis_transform(), + clip_on=False, + ) + + # add title and axes labels + plt.title(label="Generic Bilinear Stress-Strain Profile").set_fontsize(16) + plt.xlabel("Concrete Strain $\\varepsilon_c$", labelpad=10).set_fontsize(16) + plt.ylabel("Concrete Stress $\sigma_c$", labelpad=10).set_fontsize(16) + + # define data for annotations + x = [ + 0, + compressive_strain, + ultimate_strain, + ] + y = [ + 0, + compressive_strength, + compressive_strength, + ] + x_annotation = [ + "$0$", + "$\\varepsilon_{1}$", + "$\\varepsilon_{u1}$", + ] + y_label = [ + 0, + compressive_strength, + ] + y_annotation = [ + "$0$", + "$\\alpha f'_c$", + ] + + # add markers + plt.plot(x, y, "ok", ms=6) + + # add tick labels for control points + plt.xticks(x, labels=x_annotation, fontsize=16) + plt.yticks(y_label, labels=y_annotation, fontsize=16) + + # set min axes extent + xmin, xmax, ymin, ymax = plt.axis() + ax.axes.set_xlim(xmin) + ax.axes.set_ylim(ymin) + + # add line to maximum strength at eps_1 + plt.plot( + [xmin, compressive_strain, compressive_strain], + [compressive_strength, compressive_strength, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add line to maximum strength f_cd at eps_u1 + plt.plot( + [xmin, ultimate_strain, ultimate_strain], + [compressive_strength, compressive_strength, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add fill + plt.fill_between( + stress_strain_profile.strains, + stress_strain_profile.stresses, + 0, + alpha=0.15, + color="grey", + ) + + # Turn off grid + plt.grid(False) + + # turn off plot border + plt.box(False) + + # turn on tight layout + plt.tight_layout() + + # plot if required for testing purposes + if render: + plt.show() diff --git a/docs/source/_static/doc_plots/generic_conc_service_plot.py b/docs/source/_static/doc_plots/generic_conc_service_plot.py new file mode 100644 index 00000000..ce38a5c7 --- /dev/null +++ b/docs/source/_static/doc_plots/generic_conc_service_plot.py @@ -0,0 +1,152 @@ +import matplotlib.pyplot as plt +import concreteproperties.stress_strain_profile as ssp + + +def generic_conc_service_plot(render=False): + """Creates a plot for use in the docstring of ConcreteServiceProfile class, + generates a plot with stress-strain parameters shown to aid in interpreting class + variables. + + :param render: Set to True to plot for testing purposes, note will plot + automatically in a docstring plot directive when set to default of False + """ + # create ConcreteServiceProfile stress-strain profile + compressive_strength = 40 + ultimate_strain = 0.0035 + + strains = [ + -0.00015, + -0.00010, + -0.000075, + 0, + 0.00045, + 0.001, + 0.0015, + 0.002, + ultimate_strain, + ] + stresses = [ + 0, + 0, + -0.1 * compressive_strength, + 0, + 0.6 * compressive_strength, + 0.8 * compressive_strength, + 0.925 * compressive_strength, + compressive_strength, + compressive_strength, + ] + + stress_strain_profile = ssp.ConcreteServiceProfile( + # compressive_strength=compressive_strength, + strains=strains, + stresses=stresses, + ultimate_strain=ultimate_strain, + ) + + # add return of stress-strain diagram to zero stress + stress_strain_profile.stresses.append(0) + stress_strain_profile.strains.append(stress_strain_profile.strains[-1]) + + # plot design stress-strain relationship + ax = stress_strain_profile.plot_stress_strain( + fmt="-k", render=False, linewidth=1, figsize=(8, 6) + ) + + # add fake origin axes lines + plt.axvline(linewidth=1.5, color="grey") + plt.axhline(linewidth=1.5, color="grey") + + # add arrows at end of origin axes + ax.plot( + (1), + (0), + ls="", + marker=">", + ms=10, + color="k", + transform=ax.get_yaxis_transform(), + clip_on=False, + ) + ax.plot( + (0), + (1), + ls="", + marker="^", + ms=10, + color="k", + transform=ax.get_xaxis_transform(), + clip_on=False, + ) + + # add title and axes labels + plt.title(label="Generic Service Stress-Strain Profile").set_fontsize(16) + plt.xlabel("Concrete Strain $\\varepsilon_c$", labelpad=10).set_fontsize(16) + plt.ylabel("Concrete Stress $\sigma_c$", labelpad=10).set_fontsize(16) + + # define data for annotations + x = strains[4:7] + y = stresses[4:7] + x.insert(0, 0) + x.append(ultimate_strain) + y.insert(0, 0) + y.append(compressive_strength) + x_annotation = [ + "$0$", + "$\\varepsilon_{c(i-1)}$", + "$\\varepsilon_{c(i)}$", + "$\\varepsilon_{c(i+1)}$", + "$\\varepsilon_{u1}$", + ] + + y_annotation = [ + "$0$", + "$\sigma_{c(i-1)}$", + "$\sigma_{c(i)}$", + "$\sigma_{c(i+1)}$", + "$f'_c$", + ] + + # add markers + plt.plot(strains, stresses, "ok", ms=6) + + # add tick labels for control points + plt.xticks(x, labels=x_annotation, fontsize=16) + plt.yticks(y, labels=y_annotation, fontsize=16) + + # set min axes extent + xmin, xmax, ymin, ymax = plt.axis() + ax.axes.set_xlim(xmin) + ax.axes.set_ylim(ymin) + + # add line to each stress and strain point + for x, y in zip(x, y): + plt.plot( + [xmin, x, x], + [y, y, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add fill + plt.fill_between( + stress_strain_profile.strains, + stress_strain_profile.stresses, + 0, + alpha=0.15, + color="grey", + ) + + # Turn off grid + plt.grid(False) + + # turn off plot border + plt.box(False) + + # turn on tight layout + plt.tight_layout() + + # plot if required for testing purposes + if render: + plt.show() diff --git a/docs/source/_static/doc_plots/generic_conc_ultimate_plot.py b/docs/source/_static/doc_plots/generic_conc_ultimate_plot.py new file mode 100644 index 00000000..fa38b5f2 --- /dev/null +++ b/docs/source/_static/doc_plots/generic_conc_ultimate_plot.py @@ -0,0 +1,139 @@ +import matplotlib.pyplot as plt +import concreteproperties.stress_strain_profile as ssp + + +def generic_conc_ultimate_plot(render=False): + """Creates a plot for use in the docstring of ConcreteUltimateProfile class, + generates a plot with stress-strain parameters shown to aid in interpreting class + variables. + + :param render: Set to True to plot for testing purposes, note will plot + automatically in a docstring plot directive when set to default of False + """ + # create ConcreteUltimateProfile stress-strain profile + compressive_strength = 40 + ultimate_strain = 0.0035 + + strains = [0, 0.0005, 0.001, 0.002, 0.00275, ultimate_strain] + stresses = [ + 0, + 0, + 0.5 * compressive_strength, + 0.8 * compressive_strength, + 0.95 * compressive_strength, + compressive_strength, + ] + + stress_strain_profile = ssp.ConcreteUltimateProfile( + compressive_strength=compressive_strength, + strains=strains, + stresses=stresses, + ) + + # add return of stress-strain diagram to zero stress + stress_strain_profile.stresses.append(0) + stress_strain_profile.strains.append(stress_strain_profile.strains[-1]) + + # plot design stress-strain relationship + ax = stress_strain_profile.plot_stress_strain( + fmt="-k", render=False, linewidth=1, figsize=(8, 6) + ) + + # add fake origin axes lines + plt.axvline(linewidth=1.5, color="grey") + plt.axhline(linewidth=1.5, color="grey") + + # add arrows at end of origin axes + ax.plot( + (1), + (0), + ls="", + marker=">", + ms=10, + color="k", + transform=ax.get_yaxis_transform(), + clip_on=False, + ) + ax.plot( + (0), + (1), + ls="", + marker="^", + ms=10, + color="k", + transform=ax.get_xaxis_transform(), + clip_on=False, + ) + + # add title and axes labels + plt.title(label="Generic Ultimate Stress-Strain Profile").set_fontsize(16) + plt.xlabel("Concrete Strain $\\varepsilon_c$", labelpad=10).set_fontsize(16) + plt.ylabel("Concrete Stress $\sigma_c$", labelpad=10).set_fontsize(16) + + # define data for annotations + x = strains[2:5] + y = stresses[2:5] + x.insert(0, 0) + x.append(ultimate_strain) + y.insert(0, 0) + y.append(compressive_strength) + + x_annotation = [ + "$0$", + "$\\varepsilon_{c(i-1)}$", + "$\\varepsilon_{c(i)}$", + "$\\varepsilon_{c(i+1)}$", + "$\\varepsilon_{u1}$", + ] + + y_annotation = [ + "$0$", + "$\sigma_{c(i-1)}$", + "$\sigma_{c(i)}$", + "$\sigma_{c(i+1)}$", + "$f'_c$", + ] + + # add markers + plt.plot(strains, stresses, "ok", ms=6) + + # add tick labels for control points + plt.xticks(x, labels=x_annotation, fontsize=16) + plt.yticks(y, labels=y_annotation, fontsize=16) + + # set min axes extent + xmin, xmax, ymin, ymax = plt.axis() + ax.axes.set_xlim(xmin) + ax.axes.set_ylim(ymin) + + # add line to each stress and strain point + for x, y in zip(x, y): + plt.plot( + [xmin, x, x], + [y, y, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add fill + plt.fill_between( + stress_strain_profile.strains, + stress_strain_profile.stresses, + 0, + alpha=0.15, + color="grey", + ) + + # Turn off grid + plt.grid(False) + + # turn off plot border + plt.box(False) + + # turn on tight layout + plt.tight_layout() + + # plot if required for testing purposes + if render: + plt.show() diff --git a/docs/source/_static/doc_plots/generic_linear_no_tension_service_plot.py b/docs/source/_static/doc_plots/generic_linear_no_tension_service_plot.py new file mode 100644 index 00000000..e9a951fd --- /dev/null +++ b/docs/source/_static/doc_plots/generic_linear_no_tension_service_plot.py @@ -0,0 +1,154 @@ +import matplotlib.pyplot as plt +import concreteproperties.stress_strain_profile as ssp + + +def generic_linear_no_tension_service_plot(render=False): + """Creates a plot for use in the docstring of ConcreteLinearNoTension class, + generates a plot with stress-strain parameters shown to aid in + interpreting class variables. + + :param render: Set to True to plot for testing purposes, note will plot + automatically in a docstring plot directive when set to default of False + """ + # create ConcreteLinearNoTension stress-strain profile + elastic_modulus = 35000 + compressive_strength = 40 + ultimate_strain = 0.0035 + yield_strain = compressive_strength / elastic_modulus + + stress_strain_profile = ssp.ConcreteLinearNoTension( + elastic_modulus=elastic_modulus, + compressive_strength=compressive_strength, + ultimate_strain=ultimate_strain, + ) + + # overide default tension branch strain + stress_strain_profile.strains[0] = 0 + + # add return of stress-strain diagram to zero stress + stress_strain_profile.stresses.append(0) + stress_strain_profile.strains.append(stress_strain_profile.strains[-1]) + + # plot design stress-strain relationship + ax = stress_strain_profile.plot_stress_strain( + fmt="-k", render=False, linewidth=1, figsize=(8, 6) + ) + + # add fake origin axes lines + plt.axvline(linewidth=1.5, color="grey") + plt.axhline(linewidth=1.5, color="grey") + + # add arrows at end of origin axes + ax.plot( + (1), + (0), + ls="", + marker=">", + ms=10, + color="k", + transform=ax.get_yaxis_transform(), + clip_on=False, + ) + ax.plot( + (0), + (1), + ls="", + marker="^", + ms=10, + color="k", + transform=ax.get_xaxis_transform(), + clip_on=False, + ) + + # add title and axes labels + plt.title(label="Generic Linear No Tension Stress-Strain Profile").set_fontsize(16) + plt.xlabel("Concrete Strain $\\varepsilon_c$", labelpad=10).set_fontsize(16) + plt.ylabel("Concrete Stress $\sigma_c$", labelpad=10).set_fontsize(16) + + # define data for annotations + x = [ + 0, + 0.33 * compressive_strength / elastic_modulus, + 0.67 * compressive_strength / elastic_modulus, + yield_strain, + ultimate_strain, + ] + y = [ + 0, + 0.33 * compressive_strength, + 0.67 * compressive_strength, + compressive_strength, + compressive_strength, + ] + x_annotation = [ + "$0$", + "$\\varepsilon_{c(i)}$", + "$\\varepsilon_{c(i+1)}$", + "$\\varepsilon_{y1}$", + "$\\varepsilon_{u1}$", + ] + y_label = [ + 0, + 0.33 * compressive_strength, + 0.67 * compressive_strength, + compressive_strength, + ] + y_annotation = [ + "$0$", + "$\sigma_{c(i)}$", + "$\sigma_{c(i+1)}$", + "$f'_c$", + ] + + # add markers + plt.plot(x, y, "ok", ms=6) + + # add tick labels for control points + plt.xticks(x, labels=x_annotation, fontsize=16) + plt.yticks(y_label, labels=y_annotation, fontsize=16) + + # set min axes extent + xmin, xmax, ymin, ymax = plt.axis() + ax.axes.set_xlim(xmin) + ax.axes.set_ylim(ymin) + + # add line to each stress and strain point + for x, y in zip(x, y_label): + plt.plot( + [xmin, x, x], + [y, y, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add line to maximum strength f_c at eps_u1 + plt.plot( + [ultimate_strain, ultimate_strain], + [compressive_strength, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add fill + plt.fill_between( + stress_strain_profile.strains, + stress_strain_profile.stresses, + 0, + alpha=0.15, + color="grey", + ) + + # Turn off grid + plt.grid(False) + + # turn off plot border + plt.box(False) + + # turn on tight layout + plt.tight_layout() + + # plot if required for testing purposes + if render: + plt.show() diff --git a/docs/source/_static/doc_plots/generic_linear_service_plot.py b/docs/source/_static/doc_plots/generic_linear_service_plot.py new file mode 100644 index 00000000..ad5cae55 --- /dev/null +++ b/docs/source/_static/doc_plots/generic_linear_service_plot.py @@ -0,0 +1,130 @@ +import matplotlib.pyplot as plt +import concreteproperties.stress_strain_profile as ssp + + +def generic_linear_service_plot(render=False): + """Creates a plot for use in the docstring of ConcreteLinear class, + generates a plot with stress-strain parameters shown to aid in + interpreting class variables. + + :param render: Set to True to plot for testing purposes, note will plot + automatically in a docstring plot directive when set to default of False + """ + # create ConcreteLinear stress-strain profile + elastic_modulus = 1 + + stress_strain_profile = ssp.ConcreteLinear( + elastic_modulus=elastic_modulus, + ) + + # add return of stress-strain diagram to zero stress at max tension/compression + stress_strain_profile.stresses.append(0) + stress_strain_profile.strains.append(stress_strain_profile.strains[-1]) + stress_strain_profile.stresses.insert(0, 0) + stress_strain_profile.strains.insert(0, stress_strain_profile.strains[0]) + + # plot design stress-strain relationship + ax = stress_strain_profile.plot_stress_strain( + fmt="-k", render=False, linewidth=1, figsize=(8, 6) + ) + + # add fake origin axes lines + plt.axvline(linewidth=1.5, color="grey") + plt.axhline(linewidth=1.5, color="grey") + + # add arrows at end of origin axes + ax.plot( + (1), + (0), + ls="", + marker=">", + ms=10, + color="k", + transform=ax.get_yaxis_transform(), + clip_on=False, + ) + ax.plot( + (0), + (1), + ls="", + marker="^", + ms=10, + color="k", + transform=ax.get_xaxis_transform(), + clip_on=False, + ) + + # add title and axes labels + plt.title( + label="Generic Linear Stress-Strain Profile", + pad=20, + ).set_fontsize(16) + plt.xlabel("Concrete Strain $\\varepsilon_c$", labelpad=10).set_fontsize(16) + plt.ylabel("Concrete Stress $\sigma_c$", labelpad=10).set_fontsize(16) + + # define data for annotations + x = [-0.001, 0.00033, 0.00067, 0.001] + y = [-0.001, 0.00033, 0.00067, 0.001] + + # add markers + plt.plot(x, y, "ok", ms=6) + + # labels + x_label = [ + 0.00033, + 0.00067, + ] + y_label = [ + 0.00033, + 0.00067, + ] + + x_annotation = [ + "$\\varepsilon_{c(i)}$", + "$\\varepsilon_{c(i+1)}$", + ] + y_annotation = [ + "$\sigma_{c(i)}$", + "$\sigma_{c(i+1)}$", + ] + + # add tick labels for control points + plt.xticks(x_label, labels=x_annotation, fontsize=16) + plt.yticks(y_label, labels=y_annotation, fontsize=16) + + # set min axes extent + xmin, xmax, ymin, ymax = plt.axis() + ax.axes.set_xlim(xmin) + ax.axes.set_ylim(ymin) + + # add line to each stress and strain point + for x, y in zip(x_label, y_label): + plt.plot( + [xmin, x, x], + [y, y, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add fill + plt.fill_between( + stress_strain_profile.strains, + stress_strain_profile.stresses, + 0, + alpha=0.15, + color="grey", + ) + + # Turn off grid + plt.grid(False) + + # turn off plot border + plt.box(False) + + # turn on tight layout + plt.tight_layout() + + # plot if required for testing purposes + if render: + plt.show() diff --git a/docs/source/_static/doc_plots/generic_parabolic_ultimate_plot.py b/docs/source/_static/doc_plots/generic_parabolic_ultimate_plot.py new file mode 100644 index 00000000..aa6d6cc6 --- /dev/null +++ b/docs/source/_static/doc_plots/generic_parabolic_ultimate_plot.py @@ -0,0 +1,145 @@ +import matplotlib.pyplot as plt +import concreteproperties.stress_strain_profile as ssp + + +def generic_parabolic_ultimate_plot(render=False): + """Creates a plot for use in the docstring of ParabolicStressStrain class, + generates a plot with stress-strain parameters shown to aid in interpreting class + variables. + + :param render: Set to True to plot for testing purposes, note will plot + automatically in a docstring plot directive when set to default of False + """ + # create ParabolicStressStrain stress-strain profile + compressive_strength = 40 + compressive_strain = 0.0025 + ultimate_strain = 0.0035 + n_points = 50 + + stress_strain_profile = ssp.ParabolicStressStrain( + compressive_strength=compressive_strength, + compressive_strain=compressive_strain, + ultimate_strain=ultimate_strain, + n_exp=2, + n_points=n_points, + ) + + # overide default tension branch strain + stress_strain_profile.strains[0] = 0 + + # add return of stress-strain diagram to zero stress + stress_strain_profile.stresses.append(0) + stress_strain_profile.strains.append(stress_strain_profile.strains[-1]) + + # plot design stress-strain relationship + ax = stress_strain_profile.plot_stress_strain( + fmt="-k", render=False, linewidth=1, figsize=(8, 6) + ) + + # add fake origin axes lines + plt.axvline(linewidth=1.5, color="grey") + plt.axhline(linewidth=1.5, color="grey") + + # add arrows at end of origin axes + ax.plot( + (1), + (0), + ls="", + marker=">", + ms=10, + color="k", + transform=ax.get_yaxis_transform(), + clip_on=False, + ) + ax.plot( + (0), + (1), + ls="", + marker="^", + ms=10, + color="k", + transform=ax.get_xaxis_transform(), + clip_on=False, + ) + + # add title and axes labels + plt.title(label="Generic Parabolic Stress-Strain Profile").set_fontsize(16) + plt.xlabel("Concrete Strain $\\varepsilon_c$", labelpad=10).set_fontsize(16) + plt.ylabel("Concrete Stress $\sigma_c$", labelpad=10).set_fontsize(16) + + # define data for annotations + x = [ + 0, + compressive_strain, + ultimate_strain, + ] + y = [ + 0, + compressive_strength, + compressive_strength, + ] + x_annotation = [ + "$0$", + "$\\varepsilon_{1}$", + "$\\varepsilon_{u1}$", + ] + y_label = [ + 0, + compressive_strength, + ] + y_annotation = [ + "$0$", + "$\\alpha f'_c$", + ] + + # add markers + plt.plot(x, y, "ok", ms=6) + + # add tick labels for control points + plt.xticks(x, labels=x_annotation, fontsize=16) + plt.yticks(y_label, labels=y_annotation, fontsize=16) + + # set min axes extent + xmin, xmax, ymin, ymax = plt.axis() + ax.axes.set_xlim(xmin) + ax.axes.set_ylim(ymin) + + # add line to maximum strength at eps_1 + plt.plot( + [xmin, compressive_strain, compressive_strain], + [compressive_strength, compressive_strength, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add line to maximum strength f_cd at eps_u1 + plt.plot( + [xmin, ultimate_strain, ultimate_strain], + [compressive_strength, compressive_strength, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add fill + plt.fill_between( + stress_strain_profile.strains, + stress_strain_profile.stresses, + 0, + alpha=0.15, + color="grey", + ) + + # Turn off grid + plt.grid(False) + + # turn off plot border + plt.box(False) + + # turn on tight layout + plt.tight_layout() + + # plot if required for testing purposes + if render: + plt.show() diff --git a/docs/source/_static/doc_plots/generic_rect_ultimate_plot.py b/docs/source/_static/doc_plots/generic_rect_ultimate_plot.py new file mode 100644 index 00000000..82aa7bbe --- /dev/null +++ b/docs/source/_static/doc_plots/generic_rect_ultimate_plot.py @@ -0,0 +1,191 @@ +import matplotlib.pyplot as plt +import concreteproperties.stress_strain_profile as ssp + + +def generic_rect_ultimate_plot(render=False): + """Creates a plot for use in the docstring of RectangularStressBlock class, + generates a plot with stress-strain parameters shown to aid in + interpreting class variables. + + :param render: Set to True to plot for testing purposes, note will plot + automatically in a docstring plot directive when set to default of False + """ + # create RectangularStressBlock stress-strain profile + compressive_strength = 50 + alpha = 0.85 + gamma = 0.69 + ultimate_strain = 0.003 + f_c = compressive_strength + alpha_f_c = alpha * f_c + + stress_strain_profile = ssp.RectangularStressBlock( + compressive_strength=compressive_strength, + alpha=alpha, + gamma=gamma, + ultimate_strain=ultimate_strain, + ) + + # create nominal curve stress & strain values + nom_strains = [ + stress_strain_profile.strains[2], + stress_strain_profile.strains[2], + stress_strain_profile.strains[3], + stress_strain_profile.strains[3], + ] + nom_stresses = [ + stress_strain_profile.stresses[2], + stress_strain_profile.stresses[2] / alpha, + stress_strain_profile.stresses[3] / alpha, + stress_strain_profile.stresses[3], + ] + + # overide default tension branch strain + stress_strain_profile.strains[0] = 0 + + # add return of stress-strain diagram to zero stress + stress_strain_profile.stresses.append(0) + stress_strain_profile.strains.append(stress_strain_profile.strains[-1]) + + # plot design stress-strain relationship + ax = stress_strain_profile.plot_stress_strain( + fmt="-k", render=False, linewidth=1, figsize=(8, 6) + ) + + # plot nominal stress-strain relationship + ax.plot( + nom_strains, + nom_stresses, + color="k", + lw=1.25, + ls="--", + dashes=[12, 6], + ) + + # add fake origin axes lines + plt.axvline(linewidth=1.5, color="grey") + plt.axhline(linewidth=1.5, color="grey") + + # add arrows at end of origin axes + ax.plot( + (1), + (0), + ls="", + marker=">", + ms=10, + color="k", + transform=ax.get_yaxis_transform(), + clip_on=False, + ) + ax.plot( + (0), + (1), + ls="", + marker="^", + ms=10, + color="k", + transform=ax.get_xaxis_transform(), + clip_on=False, + ) + + # add title and axes labels + plt.title(label="Rectangular Stress-Strain Profile").set_fontsize(16) + plt.xlabel("Concrete Strain $\\varepsilon_c$", labelpad=10).set_fontsize(16) + plt.ylabel("Concrete Stress $\sigma_c$", labelpad=10).set_fontsize(16) + + # define data for annotations + eps_1, eps_u1 = ultimate_strain * (1 - gamma), ultimate_strain + x = [ + 0, + eps_1, + eps_1, + eps_u1, + eps_1, + eps_u1, + ] + y = [ + 0, + 0, + f_c, + f_c, + alpha_f_c, + alpha_f_c, + ] + x_annotation = [ + "$0$", + "", + "", + "", + "$\\varepsilon_{1}$", + "$\\varepsilon_{u1}$", + ] + y_label = [ + 0, + alpha_f_c, + f_c, + ] + y_annotation = [ + "$0$", + "$\\alpha f'_{c}$", + "$f'_{c}$", + ] + + # add markers + plt.plot(x, y, "ok", ms=6) + + # add tick labels for control points + plt.xticks(x, labels=x_annotation, fontsize=16) + plt.yticks(y_label, labels=y_annotation, fontsize=16) + + # set min axes extent + xmin, xmax, ymin, ymax = plt.axis() + ax.axes.set_xlim(xmin) + ax.axes.set_ylim(ymin) + + # add line to maximum strength alpha*f'_c at eps_1 + plt.plot( + [xmin, eps_1, eps_1], + [alpha_f_c, alpha_f_c, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add line to maximum strength f'_c at eps_1 + plt.plot( + [xmin, eps_1], + [f_c, f_c], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add line to maximum strength alpha*f'_c at eps_u1 + plt.plot( + [xmin, eps_u1, eps_u1], + [alpha_f_c, alpha_f_c, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # add fill + plt.fill_between( + stress_strain_profile.strains, + stress_strain_profile.stresses, + 0, + alpha=0.15, + color="grey", + ) + + # Turn off grid + plt.grid(False) + + # turn off plot border + plt.box(False) + + # turn on tight layout + plt.tight_layout() + + # plot if required for testing purposes + if render: + plt.show() diff --git a/docs/source/_static/doc_plots/generic_stress_strain_plot.py b/docs/source/_static/doc_plots/generic_stress_strain_plot.py new file mode 100644 index 00000000..71d75248 --- /dev/null +++ b/docs/source/_static/doc_plots/generic_stress_strain_plot.py @@ -0,0 +1,110 @@ +import matplotlib.pyplot as plt +import concreteproperties.stress_strain_profile as ssp + + +def generic_stress_strain_plot(render=False): + """Creates a plot for use in the docstring of class StressStrainProfile class, + generates a plot with stress-strain parameters shown to aid in interpreting class + variables. + + :param render: Set to True to plot for testing purposes, note will plot + automatically in a docstring plot directive when set to default of False + """ + # create class StressStrainProfile stress-strain profile + strains = [0, 0.75, 1.5, 2.5, 3.8, 5] + stresses = [0, 0.6, 0.775, 0.875, 0.97, 1] + + stress_strain_profile = ssp.StressStrainProfile( + strains=strains, + stresses=stresses, + ) + + # plot design stress-strain relationship + ax = stress_strain_profile.plot_stress_strain( + fmt="-r", render=False, linewidth=1, figsize=(8, 6) + ) + + # add fake origin axes lines + plt.axvline(linewidth=1.5, color="grey") + plt.axhline(linewidth=1.5, color="grey") + + # add arrows at end of origin axes + ax.plot( + (1), + (0), + ls="", + marker=">", + ms=10, + color="k", + transform=ax.get_yaxis_transform(), + clip_on=False, + ) + ax.plot( + (0), + (1), + ls="", + marker="^", + ms=10, + color="k", + transform=ax.get_xaxis_transform(), + clip_on=False, + ) + + # add title and axes labels + plt.title(label="Generic Material Stress-Strain Profile").set_fontsize(16) + plt.xlabel("Material Strain $\\varepsilon_m$", labelpad=10).set_fontsize(16) + plt.ylabel("Material Stress $\sigma_m$", labelpad=10).set_fontsize(16) + + # define data for annotations + x = strains[2:5] + y = stresses[2:5] + x.insert(0, 0) + y.insert(0, 0) + x_annotation = [ + "$0$", + "$\\varepsilon_{m(i-1)}$", + "$\\varepsilon_{m(i)}$", + "$\\varepsilon_{m(i+1)}$", + ] + + y_annotation = [ + "$0$", + "$\sigma_{m(i-1)}$", + "$\sigma_{m(i)}$", + "$\sigma_{m(i+1)}$", + ] + + # add markers + plt.plot(strains, stresses, "ok", ms=6) + + # add tick labels for control points + plt.xticks(x, labels=x_annotation, fontsize=16) + plt.yticks(y, labels=y_annotation, fontsize=16) + + # set min axes extent + xmin, xmax, ymin, ymax = plt.axis() + ax.axes.set_xlim(xmin) + ax.axes.set_ylim(ymin) + + # add line to each stress and strain point + for x, y in zip(x, y): + plt.plot( + [xmin, x, x], + [y, y, ymin], + "k", + linewidth=0.75, + dashes=[6, 6], + ) + + # Turn off grid + plt.grid(False) + + # turn off plot border + plt.box(False) + + # turn on tight layout + plt.tight_layout() + + # plot if required for testing purposes + if render: + plt.show() diff --git a/docs/source/_static/doc_plots/mander_confined_plot.py b/docs/source/_static/doc_plots/mander_confined_plot.py index 392cfe69..a17c3472 100644 --- a/docs/source/_static/doc_plots/mander_confined_plot.py +++ b/docs/source/_static/doc_plots/mander_confined_plot.py @@ -4,13 +4,15 @@ import concreteproperties.stress_strain_profile as ssp -def mander_confined_plot(render=False): +def mander_confined_plot(render=False, plot_unconfined=False): """Creates a plot for use in the docstring of ModifiedMander class for unconfined concrete to generate a plot with stress-strain parameters shown to aid in interpreting class variables. :param render: Set to True to plot for testing purposes, note will plot automatically in a docstring plot directive when set to default of False + :param plot_unconfined: Set to True to overlay unconfined stress-strain relationship + over the confined stress strain relationship """ # create confined ModifiedMander stress-strain profile design_code = NZS3101() @@ -20,6 +22,7 @@ def mander_confined_plot(render=False): compressive_strength ) tensile_failure_strain = concrete_tensile_strength / elastic_modulus + stress_strain_profile = ssp.ModifiedMander( elastic_modulus=elastic_modulus, compressive_strength=compressive_strength, @@ -40,6 +43,16 @@ def mander_confined_plot(render=False): eps_su=0.15, ) + if plot_unconfined: + stress_strain_profile_uc = ssp.ModifiedMander( + elastic_modulus=elastic_modulus, + compressive_strength=compressive_strength, + tensile_strength=concrete_tensile_strength, + sect_type="rect", + conc_tension=False, + conc_spalling=True, + ) + # add return of stress-strain diagram to zero stress stress_strain_profile.stresses = np.append(stress_strain_profile.stresses, 0) stress_strain_profile.strains = np.append( @@ -47,11 +60,22 @@ def mander_confined_plot(render=False): stress_strain_profile.strains[-1], ) - # plot unconfined stress-strain relationship + # plot confined stress-strain relationship ax = stress_strain_profile.plot_stress_strain( fmt="-k", render=False, linewidth=1, figsize=(8, 6) ) + if plot_unconfined: + # plot unconfined stress-strain relationship for comparison + ax.plot( + stress_strain_profile_uc.strains, + stress_strain_profile_uc.stresses, + color="k", + lw=1.25, + ls="--", + dashes=[12, 6], + ) + # add fake origin axes lines plt.axvline(linewidth=1.5, color="grey") plt.axhline(linewidth=1.5, color="grey") @@ -80,8 +104,8 @@ def mander_confined_plot(render=False): # add title and axes labels plt.title(label="Modified Mander Confined Stress-Strain Profile").set_fontsize(16) - plt.xlabel("Compressive Strain $\\varepsilon_c$", labelpad=10).set_fontsize(16) - plt.ylabel("Compressive Strength $f_c$", labelpad=0).set_fontsize(16) + plt.xlabel("Concrete Strain $\\varepsilon_c$", labelpad=10).set_fontsize(16) + plt.ylabel("Concrete Stress $\sigma_c$", labelpad=10).set_fontsize(16) # define data for annotations f_cc = max(stress_strain_profile.stresses) diff --git a/docs/source/_static/doc_plots/mander_unconfined_plot.py b/docs/source/_static/doc_plots/mander_unconfined_plot.py index 701846d7..c609ecb1 100644 --- a/docs/source/_static/doc_plots/mander_unconfined_plot.py +++ b/docs/source/_static/doc_plots/mander_unconfined_plot.py @@ -20,6 +20,7 @@ def mander_unconfined_plot(render=False): compressive_strength ) tensile_failure_strain = concrete_tensile_strength / elastic_modulus + stress_strain_profile = ssp.ModifiedMander( elastic_modulus=elastic_modulus, compressive_strength=compressive_strength, @@ -62,8 +63,8 @@ def mander_unconfined_plot(render=False): # add title and axes labels plt.title(label="Modified Mander Unconfined Stress-Strain Profile").set_fontsize(16) - plt.xlabel("Compressive Strain $\\varepsilon_c$", labelpad=10).set_fontsize(16) - plt.ylabel("Compressive Strength $f_c$", labelpad=0).set_fontsize(16) + plt.xlabel("Concrete Strain $\\varepsilon_c$", labelpad=10).set_fontsize(16) + plt.ylabel("Concrete Stress $\sigma_c$", labelpad=10).set_fontsize(16) # define data for annotations x = [ diff --git a/docs/source/_static/doc_plots/plot_all_for_testing.py b/docs/source/_static/doc_plots/plot_all_for_testing.py new file mode 100644 index 00000000..32e56df0 --- /dev/null +++ b/docs/source/_static/doc_plots/plot_all_for_testing.py @@ -0,0 +1,37 @@ +import ec2_bilinear_ultimate_plot as ec2_bi +import ec2_parabolic_ultimate_plot as ec2_par +import generic_bilinear_ultimate_plot as gen_bi +import generic_conc_service_plot as gen_ser +import generic_conc_ultimate_plot as gen_ult +import generic_linear_service_plot as gen_lin +import generic_linear_no_tension_service_plot as gen_lin_nt +import generic_parabolic_ultimate_plot as gen_par +import generic_rect_ultimate_plot as gen_rec +import generic_stress_strain_plot as gen +import mander_confined_plot as man_c +import mander_unconfined_plot as man_uc + + +def main(): + """Function to show all docs plots. + + Add other plot methods as they are developed/added. + + """ + # display all concrete plots + ec2_bi.ec2_bilinear_ultimate_plot() + ec2_par.ec2_parabolic_ultimate_plot() + gen_bi.generic_bilinear_ultimate_plot() + gen_ser.generic_conc_service_plot() + gen_ult.generic_conc_ultimate_plot() + gen_lin.generic_linear_service_plot() + gen_lin_nt.generic_linear_no_tension_service_plot() + gen_par.generic_parabolic_ultimate_plot() + gen_rec.generic_rect_ultimate_plot() + gen.generic_stress_strain_plot() + man_c.mander_confined_plot() + man_uc.mander_unconfined_plot(True) + + +if __name__ == "__main__": + main() diff --git a/docs/source/notebooks/as3600.ipynb b/docs/source/notebooks/as3600.ipynb index 623a6d41..92ddcfd8 100644 --- a/docs/source/notebooks/as3600.ipynb +++ b/docs/source/notebooks/as3600.ipynb @@ -334,10 +334,10 @@ "\n", "# parabolic\n", "concrete.ultimate_stress_strain_profile = ssp.EurocodeParabolicUltimate(\n", - " compressive_strength=0.9 * 40,\n", - " compressive_strain=0.0015,\n", - " ultimate_strain=0.003,\n", - " n=2,\n", + " compressive_strength=40,\n", + " limiting_strain=0.003,\n", + " alpha_cc=0.9,\n", + " gamma_c=1,\n", ")\n", "f_mi_res_par, _, _ = design_code.moment_interaction_diagram(progress_bar=False)" ] @@ -422,7 +422,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -436,11 +436,11 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.12" + "version": "3.10.9 (tags/v3.10.9:1dd9be6, Dec 6 2022, 20:01:21) [MSC v.1934 64 bit (AMD64)]" }, "vscode": { "interpreter": { - "hash": "893b3ef6d13023afab4be8c5000be38ce11a760491bcfa4047435852657817d1" + "hash": "0803bda3a9df4dd3228c2723d1aeb0efd0f1b14533af55b8e50ccbc570bcf420" } } }, diff --git a/docs/source/user_guide/materials.rst b/docs/source/user_guide/materials.rst index 7d9442fb..1c3be33d 100644 --- a/docs/source/user_guide/materials.rst +++ b/docs/source/user_guide/materials.rst @@ -308,8 +308,8 @@ Rectangular Stress Block ).plot_stress_strain() -Bilinear Ultimate Profile -""""""""""""""""""""""""" +Generic Bilinear Ultimate Profile +""""""""""""""""""""""""""""""""" .. autoclass:: concreteproperties.stress_strain_profile.BilinearStressStrain :noindex: @@ -328,6 +328,45 @@ Bilinear Ultimate Profile ).plot_stress_strain() +Generic Parabolic Ultimate Profile +"""""""""""""""""""""""""""""""""" + +.. autoclass:: concreteproperties.stress_strain_profile.ParabolicStressStrain + :noindex: + :show-inheritance: + +.. plot:: + :include-source: True + :caption: ParabolicStressStrain Stress-Strain Profile + + from concreteproperties.stress_strain_profile import ParabolicStressStrain + + ParabolicStressStrain( + compressive_strength=40, + compressive_strain=0.002, + ultimate_strain=0.0035, + n_exp=2, + ).plot_stress_strain() + + +Eurocode Bilinear Ultimate Profile +"""""""""""""""""""""""""""""""""" + +.. autoclass:: concreteproperties.stress_strain_profile.EurocodeBilinearUltimate + :noindex: + :show-inheritance: + +.. plot:: + :include-source: True + :caption: EurocodeBilinearUltimate Stress-Strain Profile + + from concreteproperties.stress_strain_profile import EurocodeBilinearUltimate + + EurocodeBilinearUltimate( + compressive_strength=40, + ).plot_stress_strain() + + Eurocode Parabolic Ultimate Profile """"""""""""""""""""""""""""""""""" @@ -343,13 +382,9 @@ Eurocode Parabolic Ultimate Profile EurocodeParabolicUltimate( compressive_strength=40, - compressive_strain=0.00175, - ultimate_strain=0.0035, - n=2, ).plot_stress_strain() - Steel Stress-Strain Profiles ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -480,4 +515,4 @@ PCI Journal (1992) Strand Profile elastic_modulus=195e3, fracture_strain=0.035, breaking_strength=1830, - ).plot_stress_strain() \ No newline at end of file + ).plot_stress_strain()