diff --git a/docs/examples/area_properties.ipynb b/docs/examples/area_properties.ipynb
index a826b6a0..d5a4df83 100644
--- a/docs/examples/area_properties.ipynb
+++ b/docs/examples/area_properties.ipynb
@@ -132,7 +132,7 @@
"outputs": [],
"source": [
"gross_props = conc_sec.get_gross_properties()\n",
- "gross_props.print_results(fmt=\".3e\")"
+ "gross_props.print_results()"
]
},
{
@@ -153,7 +153,7 @@
"outputs": [],
"source": [
"transformed_props = conc_sec.get_transformed_gross_properties(elastic_modulus=30.1e3)\n",
- "transformed_props.print_results(fmt=\".3e\")"
+ "transformed_props.print_results()"
]
}
],
@@ -173,7 +173,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.11.4"
+ "version": "3.12.7"
}
},
"nbformat": 4,
diff --git a/docs/examples/as3600.ipynb b/docs/examples/as3600.ipynb
index 158576da..589dcc1d 100644
--- a/docs/examples/as3600.ipynb
+++ b/docs/examples/as3600.ipynb
@@ -27,7 +27,8 @@
" RectangularStressBlock,\n",
")\n",
"from concreteproperties.design_codes import AS3600\n",
- "from concreteproperties.results import MomentInteractionResults"
+ "from concreteproperties.post import si_kn_m, si_n_mm\n",
+ "from concreteproperties.results import BiaxialBendingResults, MomentInteractionResults"
]
},
{
@@ -68,8 +69,12 @@
"source": [
"print(concrete.name)\n",
"print(f\"Density = {concrete.density} kg/mm^3\")\n",
- "concrete.stress_strain_profile.plot_stress_strain(title=\"Service Profile\")\n",
- "concrete.ultimate_stress_strain_profile.plot_stress_strain(title=\"Ultimate Profile\")\n",
+ "concrete.stress_strain_profile.plot_stress_strain(\n",
+ " title=\"Service Profile\", eng=True, units=si_n_mm\n",
+ ")\n",
+ "concrete.ultimate_stress_strain_profile.plot_stress_strain(\n",
+ " title=\"Ultimate Profile\", eng=True, units=si_n_mm\n",
+ ")\n",
"print(\n",
" f\"Concrete Flexural Tensile Strength: {concrete.flexural_tensile_strength:.2f} MPa\"\n",
")"
@@ -92,7 +97,7 @@
"source": [
"print(steel.name)\n",
"print(f\"Density = {steel.density} kg/mm^3\")\n",
- "steel.stress_strain_profile.plot_stress_strain()"
+ "steel.stress_strain_profile.plot_stress_strain(eng=True, units=si_n_mm)"
]
},
{
@@ -240,7 +245,10 @@
"outputs": [],
"source": [
"MomentInteractionResults.plot_multiple_diagrams(\n",
- " [f_mi_res, mi_res], [\"Factored\", \"Unfactored\"], fmt=\"-\"\n",
+ " [mi_res, f_mi_res],\n",
+ " [\"Unfactored\", \"Factored\"],\n",
+ " fmt=\"-\",\n",
+ " units=si_kn_m,\n",
")"
]
},
@@ -294,7 +302,11 @@
"n_cases = len(n_stars)\n",
"\n",
"# plot moment interaction diagram\n",
- "ax = f_mi_res.plot_diagram(fmt=\"k-\", render=False)\n",
+ "ax = f_mi_res.plot_diagram(\n",
+ " fmt=\"k-\",\n",
+ " units=si_kn_m,\n",
+ " render=False,\n",
+ ")\n",
"\n",
"# check to see if combination is within diagram and plot result\n",
"for idx in range(n_cases):\n",
@@ -356,6 +368,7 @@
" [f_mi_res, f_mi_res_bil, f_mi_res_par],\n",
" [\"Rectangular\", \"Bilinear\", \"Parabolic\"],\n",
" fmt=\"-\",\n",
+ " units=si_kn_m,\n",
")"
]
},
@@ -400,15 +413,19 @@
"outputs": [],
"source": [
"# plot case 1\n",
- "ax = f_bb_res1.plot_diagram(fmt=\"x-\", render=False)\n",
- "bb_res1.plot_diagram(fmt=\"o-\", ax=ax)\n",
- "plt.show()\n",
+ "BiaxialBendingResults.plot_multiple_diagrams_2d(\n",
+ " [bb_res1, f_bb_res1],\n",
+ " labels=[\"Unfactored\", \"Factored\"],\n",
+ " units=si_kn_m,\n",
+ ")\n",
"print(f\"Average phi = {np.mean(phis1):.3f}\")\n",
"\n",
"# plot case 2\n",
- "ax = f_bb_res2.plot_diagram(fmt=\"x-\", render=False)\n",
- "bb_res2.plot_diagram(fmt=\"o-\", ax=ax)\n",
- "plt.show()\n",
+ "BiaxialBendingResults.plot_multiple_diagrams_2d(\n",
+ " [bb_res2, f_bb_res2],\n",
+ " labels=[\"Unfactored\", \"Factored\"],\n",
+ " units=si_kn_m,\n",
+ ")\n",
"print(f\"Average phi = {np.mean(phis2):.3f}\")"
]
},
@@ -437,7 +454,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.11.4"
+ "version": "3.12.7"
},
"vscode": {
"interpreter": {
diff --git a/docs/examples/biaxial_bending.ipynb b/docs/examples/biaxial_bending.ipynb
index bbc9f407..1ac2a66e 100644
--- a/docs/examples/biaxial_bending.ipynb
+++ b/docs/examples/biaxial_bending.ipynb
@@ -35,6 +35,7 @@
" SteelElasticPlastic,\n",
" add_bar_rectangular_array,\n",
")\n",
+ "from concreteproperties.post import si_kn_m\n",
"from concreteproperties.results import BiaxialBendingResults"
]
},
@@ -149,7 +150,7 @@
"metadata": {},
"outputs": [],
"source": [
- "bb_res.plot_diagram()"
+ "bb_res.plot_diagram(eng=True, units=si_kn_m)"
]
},
{
@@ -168,7 +169,7 @@
"outputs": [],
"source": [
"bb_res = conc_sec.biaxial_bending_diagram(n=1000e3, n_points=24, progress_bar=False)\n",
- "bb_res.plot_diagram()"
+ "bb_res.plot_diagram(eng=True, units=si_kn_m)"
]
},
{
@@ -189,8 +190,8 @@
"source": [
"mi_x = conc_sec.moment_interaction_diagram(progress_bar=False)\n",
"mi_y = conc_sec.moment_interaction_diagram(theta=np.pi / 2, progress_bar=False)\n",
- "mi_x.plot_diagram()\n",
- "mi_y.plot_diagram(moment=\"m_y\")\n",
+ "mi_x.plot_diagram(eng=True)\n",
+ "mi_y.plot_diagram(moment=\"m_y\", eng=True)\n",
"print(f\"Decompression point for M_x is N = {mi_x.results[1].n / 1e3:.2f} kN\")\n",
"print(f\"Decompression point for M_y is N = {mi_y.results[1].n / 1e3:.2f} kN\")"
]
@@ -232,7 +233,11 @@
"metadata": {},
"outputs": [],
"source": [
- "BiaxialBendingResults.plot_multiple_diagrams_3d(biaxial_results)"
+ "BiaxialBendingResults.plot_multiple_diagrams_3d(\n",
+ " biaxial_results,\n",
+ " eng=True,\n",
+ " units=si_kn_m,\n",
+ ")"
]
},
{
@@ -250,7 +255,12 @@
"metadata": {},
"outputs": [],
"source": [
- "BiaxialBendingResults.plot_multiple_diagrams_2d(biaxial_results, fmt=\"o-\")"
+ "BiaxialBendingResults.plot_multiple_diagrams_2d(\n",
+ " biaxial_results,\n",
+ " fmt=\"o-\",\n",
+ " eng=True,\n",
+ " units=si_kn_m,\n",
+ ")"
]
},
{
@@ -268,16 +278,14 @@
"metadata": {},
"outputs": [],
"source": [
- "import matplotlib.pyplot as plt\n",
- "\n",
- "labels = [f\"N = {bb_res.n / 1e3:.0f} kN\" for bb_rs in biaxial_results[::2]]\n",
+ "labels = [f\"N = {bb_res.n / 1e3:.0f} kN\" for bb_res in biaxial_results[::2]]\n",
"\n",
"ax = BiaxialBendingResults.plot_multiple_diagrams_2d(\n",
- " biaxial_results[::2], fmt=\"-\", labels=labels, render=False\n",
- ")\n",
- "ax.set_xlabel(\"Bending Moment $M_x$ [kN.m]\")\n",
- "ax.set_ylabel(\"Bending Moment $M_y$ [kN.m]\")\n",
- "plt.show()"
+ " biaxial_results[::2],\n",
+ " fmt=\"-\",\n",
+ " labels=labels,\n",
+ " units=si_kn_m,\n",
+ ")"
]
}
],
diff --git a/docs/examples/composite_section.ipynb b/docs/examples/composite_section.ipynb
index e5eb7579..8711d8b2 100644
--- a/docs/examples/composite_section.ipynb
+++ b/docs/examples/composite_section.ipynb
@@ -42,7 +42,8 @@
" SteelElasticPlastic,\n",
" add_bar_circular_array,\n",
" add_bar_rectangular_array,\n",
- ")"
+ ")\n",
+ "from concreteproperties.post import si_kn_m, si_n_mm"
]
},
{
@@ -170,7 +171,7 @@
"outputs": [],
"source": [
"el_stress = conc_sec.calculate_uncracked_stress(m_x=100e6)\n",
- "el_stress.plot_stress()"
+ "el_stress.plot_stress(units=si_n_mm)"
]
},
{
@@ -191,7 +192,7 @@
"source": [
"cr_res = conc_sec.calculate_cracked_properties()\n",
"cr_stress = conc_sec.calculate_cracked_stress(cracked_results=cr_res, m=500e6)\n",
- "cr_stress.plot_stress()"
+ "cr_stress.plot_stress(units=si_n_mm)"
]
},
{
@@ -220,7 +221,7 @@
"metadata": {},
"outputs": [],
"source": [
- "mk_res.plot_results(fmt=\"kx-\")"
+ "mk_res.plot_results(fmt=\"kx-\", eng=True, units=si_kn_m)"
]
},
{
@@ -237,7 +238,7 @@
"serv_stress = conc_sec.calculate_service_stress(\n",
" moment_curvature_results=mk_res, m=None, kappa=2e-5\n",
")\n",
- "serv_stress.plot_stress()"
+ "serv_stress.plot_stress(units=si_n_mm)"
]
},
{
@@ -258,8 +259,8 @@
"source": [
"ult_res_x = conc_sec.ultimate_bending_capacity()\n",
"ult_res_y = conc_sec.ultimate_bending_capacity(theta=np.pi / 2)\n",
- "ult_res_x.print_results()\n",
- "ult_res_y.print_results()"
+ "ult_res_x.print_results(units=si_kn_m)\n",
+ "ult_res_y.print_results(units=si_kn_m)"
]
},
{
@@ -270,7 +271,7 @@
"outputs": [],
"source": [
"mi_res = conc_sec.moment_interaction_diagram(progress_bar=False)\n",
- "mi_res.plot_diagram()"
+ "mi_res.plot_diagram(units=si_kn_m)"
]
},
{
@@ -281,7 +282,7 @@
"outputs": [],
"source": [
"bb_res = conc_sec.biaxial_bending_diagram(n_points=24, progress_bar=False)\n",
- "bb_res.plot_diagram()"
+ "bb_res.plot_diagram(units=si_kn_m)"
]
},
{
@@ -293,8 +294,8 @@
"source": [
"ult_stress_x = conc_sec.calculate_ultimate_stress(ult_res_x)\n",
"ult_stress_y = conc_sec.calculate_ultimate_stress(ult_res_y)\n",
- "ult_stress_x.plot_stress()\n",
- "ult_stress_y.plot_stress()"
+ "ult_stress_x.plot_stress(units=si_n_mm)\n",
+ "ult_stress_y.plot_stress(units=si_n_mm)"
]
},
{
@@ -400,8 +401,8 @@
"source": [
"el_stress_comp = conc_sec_comp.calculate_uncracked_stress(m_x=10e6)\n",
"el_stress_conc = conc_sec_conc.calculate_uncracked_stress(m_x=10e6)\n",
- "el_stress_comp.plot_stress()\n",
- "el_stress_conc.plot_stress()"
+ "el_stress_comp.plot_stress(units=si_n_mm)\n",
+ "el_stress_conc.plot_stress(units=si_n_mm)"
]
},
{
@@ -428,8 +429,8 @@
"cr_stress_conc = conc_sec_conc.calculate_cracked_stress(\n",
" cracked_results=cr_res_conc, m=50e6\n",
")\n",
- "cr_stress_comp.plot_stress()\n",
- "cr_stress_conc.plot_stress()"
+ "cr_stress_comp.plot_stress(units=si_n_mm)\n",
+ "cr_stress_conc.plot_stress(units=si_n_mm)"
]
},
{
@@ -467,6 +468,8 @@
" moment_curvature_results=[mk_res_comp, mk_res_conc],\n",
" labels=[\"Composite\", \"Concrete\"],\n",
" fmt=\"x-\",\n",
+ " eng=True,\n",
+ " units=si_kn_m,\n",
")"
]
},
@@ -500,6 +503,7 @@
" moment_interaction_results=[mi_res_comp, mi_res_conc],\n",
" labels=[\"Composite\", \"Concrete\"],\n",
" fmt=\"x-\",\n",
+ " units=si_kn_m,\n",
")"
]
},
@@ -632,7 +636,7 @@
"metadata": {},
"outputs": [],
"source": [
- "mk_res.plot_results()"
+ "mk_res.plot_results(eng=True, units=si_kn_m)"
]
},
{
@@ -666,9 +670,9 @@
"metadata": {},
"outputs": [],
"source": [
- "el_stress.plot_stress()\n",
- "yield_stress.plot_stress()\n",
- "ult_stress.plot_stress()"
+ "el_stress.plot_stress(units=si_n_mm)\n",
+ "yield_stress.plot_stress(units=si_n_mm)\n",
+ "ult_stress.plot_stress(units=si_n_mm)"
]
},
{
@@ -687,8 +691,8 @@
"metadata": {},
"outputs": [],
"source": [
- "ult_res = conc_sec.ultimate_bending_capacity(theta=np.pi / 2)\n",
- "ult_res.print_results()"
+ "ult_res = conc_sec.ultimate_bending_capacity()\n",
+ "ult_res.print_results(units=si_kn_m)"
]
},
{
diff --git a/docs/examples/cracked_properties.ipynb b/docs/examples/cracked_properties.ipynb
index 9c32c0a8..b81dd2b2 100644
--- a/docs/examples/cracked_properties.ipynb
+++ b/docs/examples/cracked_properties.ipynb
@@ -174,11 +174,15 @@
"metadata": {},
"outputs": [],
"source": [
+ "from concreteproperties.post import si_kn_m, si_n_mm\n",
+ "\n",
+ "si_n_mm.radians = False # show degrees\n",
+ "\n",
"cracked_res_sag.calculate_transformed_properties(elastic_modulus=32.8e3)\n",
"cracked_res_hog.calculate_transformed_properties(elastic_modulus=32.8e3)\n",
"\n",
- "cracked_res_sag.print_results()\n",
- "cracked_res_hog.print_results()"
+ "cracked_res_sag.print_results(units=si_kn_m)\n",
+ "cracked_res_hog.print_results(units=si_n_mm)"
]
},
{
@@ -256,7 +260,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.11.4"
+ "version": "3.12.7"
}
},
"nbformat": 4,
diff --git a/docs/examples/moment_curvature.ipynb b/docs/examples/moment_curvature.ipynb
index 9f5549c3..77de0bde 100644
--- a/docs/examples/moment_curvature.ipynb
+++ b/docs/examples/moment_curvature.ipynb
@@ -33,6 +33,7 @@
" SteelBar,\n",
" add_bar_rectangular_array,\n",
")\n",
+ "from concreteproperties.post import si_kn_m, si_n_mm\n",
"from concreteproperties.results import MomentCurvatureResults"
]
},
@@ -140,7 +141,9 @@
"outputs": [],
"source": [
"for conc in conc_material_list:\n",
- " conc.stress_strain_profile.plot_stress_strain(title=conc.name)"
+ " conc.stress_strain_profile.plot_stress_strain(\n",
+ " title=conc.name, eng=True, units=si_n_mm\n",
+ " )"
]
},
{
@@ -150,7 +153,9 @@
"metadata": {},
"outputs": [],
"source": [
- "steel.stress_strain_profile.plot_stress_strain(title=steel.name)"
+ "steel.stress_strain_profile.plot_stress_strain(\n",
+ " title=steel.name, eng=True, units=si_n_mm\n",
+ ")"
]
},
{
@@ -256,7 +261,11 @@
"outputs": [],
"source": [
"MomentCurvatureResults.plot_multiple_results(\n",
- " moment_curvature_results=moment_curvature_results, labels=labels, fmt=\"-\"\n",
+ " moment_curvature_results=moment_curvature_results,\n",
+ " labels=labels,\n",
+ " fmt=\"-\",\n",
+ " eng=True,\n",
+ " units=si_kn_m,\n",
")"
]
},
@@ -294,7 +303,11 @@
"outputs": [],
"source": [
"MomentCurvatureResults.plot_multiple_results(\n",
- " moment_curvature_results=moment_curvature_results[1:], labels=labels[1:], fmt=\"-\"\n",
+ " moment_curvature_results=moment_curvature_results[1:],\n",
+ " labels=labels[1:],\n",
+ " fmt=\"-\",\n",
+ " eng=True,\n",
+ " units=si_kn_m,\n",
")"
]
},
@@ -440,7 +453,7 @@
"metadata": {},
"outputs": [],
"source": [
- "res1.plot_results(fmt=\"x-\")\n",
+ "res1.plot_results()\n",
"print(f\"Number of calculations = {len(res1.kappa)}\")\n",
"print(f\"Failure curvature = {res1.kappa[-1]:.4e}\")"
]
@@ -479,7 +492,7 @@
"metadata": {},
"outputs": [],
"source": [
- "res2.plot_results(fmt=\"x-\")\n",
+ "res2.plot_results()\n",
"print(f\"Number of calculations = {len(res2.kappa)}\")\n",
"print(f\"Failure curvature = {res2.kappa[-1]:.4e}\")"
]
@@ -529,7 +542,7 @@
"metadata": {},
"outputs": [],
"source": [
- "res3.plot_results(fmt=\"x-\")\n",
+ "res3.plot_results()\n",
"print(f\"Number of calculations = {len(res3.kappa)}\")\n",
"print(f\"Failure curvature = {res3.kappa[-1]:.4e}\")"
]
@@ -670,6 +683,8 @@
" moment_curvature_results=[res_n0, res_n1, res_nt],\n",
" labels=[\"$N=0$ kN\", \"$N=0.2f'cA_g$\", \"$N=-1000$ kN\"],\n",
" fmt=\"-\",\n",
+ " eng=True,\n",
+ " units=si_kn_m,\n",
")"
]
},
diff --git a/docs/examples/moment_interaction.ipynb b/docs/examples/moment_interaction.ipynb
index 92d015b7..b434d583 100644
--- a/docs/examples/moment_interaction.ipynb
+++ b/docs/examples/moment_interaction.ipynb
@@ -34,6 +34,7 @@
" SteelBar,\n",
" SteelElasticPlastic,\n",
")\n",
+ "from concreteproperties.post import si_kn_m, si_n_mm\n",
"from concreteproperties.results import MomentInteractionResults"
]
},
@@ -148,7 +149,7 @@
"metadata": {},
"outputs": [],
"source": [
- "mi_res.plot_diagram()"
+ "mi_res.plot_diagram(eng=True, units=si_n_mm)"
]
},
{
@@ -167,7 +168,7 @@
"outputs": [],
"source": [
"mi_res = conc_sec.moment_interaction_diagram(theta=np.pi / 2, progress_bar=False)\n",
- "mi_res.plot_diagram(moment=\"m_y\")"
+ "mi_res.plot_diagram(moment=\"m_y\", units=si_kn_m)"
]
},
{
@@ -222,7 +223,10 @@
"source": [
"# plot all the diagrams on one image\n",
"MomentInteractionResults.plot_multiple_diagrams(\n",
- " moment_interaction_results=mi_results, labels=labels, fmt=\"-\"\n",
+ " moment_interaction_results=mi_results,\n",
+ " labels=labels,\n",
+ " fmt=\"-\",\n",
+ " units=si_kn_m,\n",
")"
]
},
@@ -265,6 +269,8 @@
" moment_interaction_results=[mi_res_pos, mi_res_neg],\n",
" labels=[\"Positive\", \"Negative\"],\n",
" fmt=\"-\",\n",
+ " eng=True,\n",
+ " units=si_n_mm,\n",
")"
]
},
@@ -380,7 +386,13 @@
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
- "ax = mi_res.plot_diagram(fmt=\"-kx\", labels=True, label_offset=True, render=False)\n",
+ "ax = mi_res.plot_diagram(\n",
+ " fmt=\"-kx\",\n",
+ " labels=True,\n",
+ " label_offset=True,\n",
+ " units=si_kn_m,\n",
+ " render=False,\n",
+ ")\n",
"\n",
"# reset axis limits to ensure labels are within plot\n",
"ax.set_xlim(-20, 850)\n",
diff --git a/docs/examples/nzs3101.ipynb b/docs/examples/nzs3101.ipynb
index 5d579668..15f51c73 100644
--- a/docs/examples/nzs3101.ipynb
+++ b/docs/examples/nzs3101.ipynb
@@ -27,6 +27,7 @@
" RectangularStressBlock,\n",
")\n",
"from concreteproperties.design_codes import NZS3101\n",
+ "from concreteproperties.post import si_kn_m, si_n_mm\n",
"from concreteproperties.results import (\n",
" MomentCurvatureResults,\n",
" MomentInteractionResults,\n",
@@ -75,10 +76,16 @@
"ult_comp = concrete_40.ultimate_stress_strain_profile.get_ultimate_compressive_strain()\n",
"print(f\"Ultimate Compressive Strain: {ult_comp:.4f}\")\n",
"concrete_40.stress_strain_profile.plot_stress_strain(\n",
- " title=\"Concrete Serviceability Stress-Strain Profile\", fmt=\"-r\"\n",
+ " title=\"Concrete Serviceability Stress-Strain Profile\",\n",
+ " fmt=\"-r\",\n",
+ " eng=True,\n",
+ " units=si_n_mm,\n",
")\n",
"concrete_40.ultimate_stress_strain_profile.plot_stress_strain(\n",
- " title=\"Concrete Ultimate Stress-Strain Profile\", fmt=\"-r\"\n",
+ " title=\"Concrete Ultimate Stress-Strain Profile\",\n",
+ " fmt=\"-r\",\n",
+ " eng=True,\n",
+ " units=si_n_mm,\n",
")"
]
},
@@ -103,7 +110,10 @@
"print(f\"Ultimate Tensile Strain = {ult_strain}\")\n",
"print(f\"Overstrength Factor = {steel_300e.phi_os}\")\n",
"steel_300e.stress_strain_profile.plot_stress_strain(\n",
- " title=\"Steel Stress-Strain Profile\", fmt=\"-r\"\n",
+ " title=\"Steel Stress-Strain Profile\",\n",
+ " fmt=\"-r\",\n",
+ " eng=True,\n",
+ " units=si_n_mm,\n",
")"
]
},
@@ -458,6 +468,7 @@
" fmt=\"or\",\n",
" render=False,\n",
" moment=\"m_xy\",\n",
+ " units=si_kn_m,\n",
")\n",
"plt.gca().lines[0].set_linestyle(\"solid\")\n",
"plt.gca().lines[0].set_marker(\"\")\n",
@@ -490,7 +501,7 @@
"n_cases = len(n_stars)\n",
"\n",
"# plot moment interaction diagram\n",
- "ax = f_mi_res.plot_diagram(fmt=\"-r\", render=False)\n",
+ "ax = f_mi_res.plot_diagram(fmt=\"-r\", render=False, units=si_kn_m)\n",
"\n",
"# check to see if combination is within diagram and plot result\n",
"for idx in range(n_cases):\n",
@@ -541,12 +552,12 @@
"outputs": [],
"source": [
"# plot case 1\n",
- "ax = f_bb_res1.plot_diagram(fmt=\"-r\", render=False)\n",
+ "ax = f_bb_res1.plot_diagram(fmt=\"-r\", render=False, units=si_kn_m)\n",
"ax.plot(M_o_T, 0, \"sk\")\n",
"plt.show()\n",
"\n",
"# plot case 2\n",
- "ax = f_bb_res2.plot_diagram(fmt=\"-r\", render=False)\n",
+ "ax = f_bb_res2.plot_diagram(fmt=\"-r\", render=False, units=si_kn_m)\n",
"ax.plot(M_o_C, 0, \"ok\")\n",
"plt.show()"
]
@@ -703,7 +714,10 @@
")\n",
"\n",
"concrete_25_prob.stress_strain_profile.plot_stress_strain(\n",
- " title=\"Mander Unconfined Stress-Strain Profile\", fmt=\"-r\"\n",
+ " title=\"Mander Unconfined Stress-Strain Profile\",\n",
+ " fmt=\"-r\",\n",
+ " eng=True,\n",
+ " units=si_n_mm,\n",
")"
]
},
@@ -801,7 +815,12 @@
"fail_mat = moment_curvature_results.failure_geometry.material.name\n",
"print(f\"The failure material is:-\\n{fail_mat}\")\n",
"\n",
- "MomentCurvatureResults.plot_results(moment_curvature_results, fmt=\"-r\")\n",
+ "MomentCurvatureResults.plot_results(\n",
+ " moment_curvature_results,\n",
+ " fmt=\"-r\",\n",
+ " eng=True,\n",
+ " units=si_kn_m,\n",
+ ")\n",
"mcr = conc_sec.calculate_cracked_properties(theta=0).m_cr / 1e6\n",
"print(f\"Cracking moment is M_cr = {mcr:.2f} kNm\")"
]
diff --git a/docs/examples/prestressed_section.ipynb b/docs/examples/prestressed_section.ipynb
index 43936fb6..277c9928 100644
--- a/docs/examples/prestressed_section.ipynb
+++ b/docs/examples/prestressed_section.ipynb
@@ -37,6 +37,7 @@
" StrandHardening,\n",
" add_bar_rectangular_array,\n",
")\n",
+ "from concreteproperties.post import si_kn_m, si_n_mm\n",
"from concreteproperties.results import MomentCurvatureResults"
]
},
@@ -140,8 +141,10 @@
"outputs": [],
"source": [
"# combine both stress-strain profiles on the same plot\n",
- "ax = concrete_service.stress_strain_profile.plot_stress_strain(render=False)\n",
- "concrete_service.ultimate_stress_strain_profile.plot_stress_strain(ax=ax)\n",
+ "ax = concrete_service.stress_strain_profile.plot_stress_strain(\n",
+ " units=si_n_mm, render=False\n",
+ ")\n",
+ "concrete_service.ultimate_stress_strain_profile.plot_stress_strain(units=si_n_mm, ax=ax)\n",
"ax.lines[0].set_label(\"Service\")\n",
"ax.lines[1].set_label(\"Ultimate\")\n",
"ax.legend(loc=\"center left\", bbox_to_anchor=(1, 0.5))\n",
@@ -157,8 +160,10 @@
"outputs": [],
"source": [
"# combine both stress-strain profiles on the same plot\n",
- "ax = strand_service.stress_strain_profile.plot_stress_strain(render=False)\n",
- "strand_ultimate.stress_strain_profile.plot_stress_strain(ax=ax)\n",
+ "ax = strand_service.stress_strain_profile.plot_stress_strain(\n",
+ " eng=True, units=si_kn_m, render=False\n",
+ ")\n",
+ "strand_ultimate.stress_strain_profile.plot_stress_strain(eng=True, units=si_kn_m, ax=ax)\n",
"ax.lines[0].set_label(\"Service\")\n",
"ax.lines[1].set_label(\"Ultimate\")\n",
"ax.legend(loc=\"center left\", bbox_to_anchor=(1, 0.5))\n",
@@ -270,7 +275,7 @@
"source": [
"gross_props_serv = conc_sec_serv.get_gross_properties()\n",
"gross_props_ult = conc_sec_ult.get_gross_properties()\n",
- "gross_props_serv.print_results(fmt=\".3e\")"
+ "gross_props_serv.print_results(units=si_n_mm)"
]
},
{
@@ -356,7 +361,7 @@
"source": [
"# stress due to prestressing only\n",
"uncr_stress_p = conc_sec_serv.calculate_uncracked_stress()\n",
- "uncr_stress_p.plot_stress()"
+ "uncr_stress_p.plot_stress(units=si_n_mm)"
]
},
{
@@ -376,9 +381,9 @@
"source": [
"cr_p_serv = conc_sec_serv.calculate_cracked_properties(m_ext=0)\n",
"cr_p_ult = conc_sec_ult.calculate_cracked_properties(m_ext=0)\n",
- "cr_p_serv.print_results(fmt=\".3e\")\n",
+ "cr_p_serv.print_results(units=si_kn_m)\n",
"cr_stress_p_serv = conc_sec_serv.calculate_cracked_stress(cracked_results=cr_p_serv)\n",
- "cr_stress_p_serv.plot_stress()"
+ "cr_stress_p_serv.plot_stress(units=si_n_mm)"
]
},
{
@@ -422,7 +427,7 @@
"source": [
"# stress due to G_sw + P at t = 0\n",
"uncr_stress_t0 = conc_sec_serv.calculate_uncracked_stress(m=m_g_sw)\n",
- "uncr_stress_t0.plot_stress()"
+ "uncr_stress_t0.plot_stress(units=si_n_mm)"
]
},
{
@@ -443,7 +448,7 @@
"source": [
"# stress due to G + Q + P at t = inf\n",
"uncr_stress_tinf = conc_sec_ult.calculate_uncracked_stress(m=m_g_sw + m_g_si + m_q)\n",
- "uncr_stress_tinf.plot_stress()"
+ "uncr_stress_tinf.plot_stress(units=si_n_mm)"
]
},
{
@@ -471,7 +476,7 @@
"outputs": [],
"source": [
"ult_res = conc_sec_ult.ultimate_bending_capacity()\n",
- "ult_res.print_results()"
+ "ult_res.print_results(units=si_kn_m)"
]
},
{
@@ -503,7 +508,7 @@
"outputs": [],
"source": [
"ult_stress = conc_sec_ult.calculate_ultimate_stress(ultimate_results=ult_res)\n",
- "ult_stress.plot_stress()"
+ "ult_stress.plot_stress(units=si_n_mm)"
]
},
{
@@ -550,6 +555,8 @@
" moment_curvature_results=[mk_serv, mk_ult],\n",
" labels=[\"Service\", \"Ultimate\"],\n",
" fmt=\"-\",\n",
+ " eng=True,\n",
+ " units=si_kn_m,\n",
")"
]
},
@@ -575,8 +582,8 @@
"serv_stress_u_1 = conc_sec_ult.calculate_service_stress(\n",
" moment_curvature_results=mk_ult, m=None, kappa=mk_ult.kappa[0]\n",
")\n",
- "serv_stress_s_1.plot_stress()\n",
- "serv_stress_u_1.plot_stress()"
+ "serv_stress_s_1.plot_stress(units=si_n_mm)\n",
+ "serv_stress_u_1.plot_stress(units=si_n_mm)"
]
},
{
@@ -617,7 +624,7 @@
"id": "42",
"metadata": {},
"source": [
- "Another point of interest on the moment curvature diagram is around 8000 kN.m. At this magnitude of external moment, the section in the service analysis remains uncracked (just below the flexural tensile strenght). However, the section in the ultimate analysis has started to crack and the bending stiffness has begun to soften. Note also the resulting increase in concrete compressive stress to compensate."
+ "Another point of interest on the moment curvature diagram is around 8000 kN.m. At this magnitude of external moment, the section in the service analysis remains uncracked (just below the flexural tensile strength). However, the section in the ultimate analysis has started to crack and the bending stiffness has begun to soften. Note also the resulting increase in concrete compressive stress to compensate."
]
},
{
@@ -633,8 +640,8 @@
"serv_stress_u_2 = conc_sec_ult.calculate_service_stress(\n",
" moment_curvature_results=mk_ult, m=8000e6\n",
")\n",
- "serv_stress_s_2.plot_stress()\n",
- "serv_stress_u_2.plot_stress()"
+ "serv_stress_s_2.plot_stress(units=si_n_mm)\n",
+ "serv_stress_u_2.plot_stress(units=si_n_mm)"
]
}
],
@@ -654,7 +661,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.11.4"
+ "version": "3.12.7"
},
"vscode": {
"interpreter": {
diff --git a/docs/examples/stress_analysis.ipynb b/docs/examples/stress_analysis.ipynb
index 080c7714..673ceac9 100644
--- a/docs/examples/stress_analysis.ipynb
+++ b/docs/examples/stress_analysis.ipynb
@@ -34,7 +34,8 @@
" RectangularStressBlock,\n",
" SteelBar,\n",
" SteelElasticPlastic,\n",
- ")"
+ ")\n",
+ "from concreteproperties.post import si_kn_m, si_n_mm"
]
},
{
@@ -157,7 +158,7 @@
},
"outputs": [],
"source": [
- "uncr_stress_res_1.plot_stress()"
+ "uncr_stress_res_1.plot_stress(units=si_n_mm)"
]
},
{
@@ -167,7 +168,7 @@
"metadata": {},
"outputs": [],
"source": [
- "uncr_stress_res_2.plot_stress()"
+ "uncr_stress_res_2.plot_stress(eng=True, units=si_kn_m)"
]
},
{
@@ -382,7 +383,7 @@
"metadata": {},
"outputs": [],
"source": [
- "mk_res.plot_results()"
+ "mk_res.plot_results(eng=True, units=si_kn_m)"
]
},
{
@@ -436,7 +437,7 @@
"\n",
" # create plot title and plot stress\n",
" label = f\"Moment = {m_stress / 1e6:.0f} kN.m\"\n",
- " service_stress_res.plot_stress(title=label)"
+ " service_stress_res.plot_stress(title=label, units=si_n_mm)"
]
},
{
@@ -464,7 +465,7 @@
"outputs": [],
"source": [
"mi_res = conc_sec.moment_interaction_diagram(progress_bar=False)\n",
- "mi_res.plot_diagram()"
+ "mi_res.plot_diagram(units=si_kn_m)"
]
},
{
@@ -520,9 +521,9 @@
"metadata": {},
"outputs": [],
"source": [
- "ultimate_stress_pure.plot_stress(title=\"Pure Bending\")\n",
- "ultimate_stress_bal.plot_stress(title=\"Balanced\")\n",
- "ultimate_stress_decomp.plot_stress(title=\"Decompression\")"
+ "ultimate_stress_pure.plot_stress(title=\"Pure Bending\", units=si_n_mm)\n",
+ "ultimate_stress_bal.plot_stress(title=\"Balanced\", units=si_n_mm)\n",
+ "ultimate_stress_decomp.plot_stress(title=\"Decompression\", units=si_n_mm)"
]
},
{
diff --git a/docs/examples/ultimate_bending.ipynb b/docs/examples/ultimate_bending.ipynb
index ad9379ea..20f9869e 100644
--- a/docs/examples/ultimate_bending.ipynb
+++ b/docs/examples/ultimate_bending.ipynb
@@ -32,7 +32,8 @@
" ConcreteSection,\n",
" SteelBar,\n",
" add_bar_rectangular_array,\n",
- ")"
+ ")\n",
+ "from concreteproperties.post import si_kn_m, si_n_mm"
]
},
{
@@ -184,7 +185,7 @@
"metadata": {},
"outputs": [],
"source": [
- "sag_res.print_results()"
+ "sag_res.print_results(units=si_kn_m)"
]
},
{
@@ -194,7 +195,8 @@
"metadata": {},
"outputs": [],
"source": [
- "hog_res.print_results()"
+ "si_n_mm.radians = False # display angles in degrees\n",
+ "hog_res.print_results(units=si_n_mm)"
]
},
{
diff --git a/docs/user_guide/design_codes/as3600.rst b/docs/user_guide/design_codes/as3600.rst
index 9d239b3f..6c87ee34 100644
--- a/docs/user_guide/design_codes/as3600.rst
+++ b/docs/user_guide/design_codes/as3600.rst
@@ -21,11 +21,14 @@ created it must be assigned to the design code::
.. automethod:: concreteproperties.design_codes.as3600.AS3600.assign_concrete_section
:noindex:
-.. note::
+.. warning::
- To maintain unit consistency, the cross-section dimensions should be entered in
- *[mm]*.
+ To maintain unit consistency, length dimensions must be entered in *[mm]*, force
+ dimensions entered in *[N]* and density dimensions entered |dunits|.
+.. |dunits| raw:: html
+
+ [kg/mm3]
Creating Material Properties
----------------------------
diff --git a/docs/user_guide/results.rst b/docs/user_guide/results.rst
index 59f934fb..c90728c4 100644
--- a/docs/user_guide/results.rst
+++ b/docs/user_guide/results.rst
@@ -40,9 +40,8 @@ attributes. The transformed gross area properties can be printed to the terminal
calling the
:meth:`~concreteproperties.results.TransformedGrossProperties.print_results` method.
-.. autoclass:: concreteproperties.results.TransformedGrossProperties()
+.. automethod:: concreteproperties.results.TransformedGrossProperties.print_results()
:noindex:
- :members:
.. seealso::
For an application of the above, see the example
@@ -68,7 +67,7 @@ returns a :class:`~concreteproperties.results.CrackedResults` object.
bending.
Calling
-:meth:`~concreteproperties.results.TransformedGrossProperties.calculate_transformed_properties`
+:meth:`~concreteproperties.results.CrackedResults.calculate_transformed_properties`
on a :class:`~concreteproperties.results.CrackedResults` object stores the transformed
cracked properties as attributes within the current object.
@@ -167,3 +166,49 @@ stress results.
.. seealso::
For an application of the above, see the example
:ref:`/examples/stress_analysis.ipynb`.
+
+
+Units
+-----
+
+Most of the above methods take an optional ``units`` argument as a
+:class:`~concreteproperties.post.UnitDisplay()` object. This argument allows results to
+be scaled and unit labels applied to numbers/plot axes.
+
+.. autoclass:: concreteproperties.post.UnitDisplay()
+ :noindex:
+
+For example, if the model data is specified in ``[N]`` and ``[mm]``, results can be
+expressed using ``[kN]`` and ``[m]`` by creating an appropriate ``UnitDisplay`` object:
+
+.. code-block:: python
+
+ from concreteproperties.post import UnitDisplay
+
+ kn_m = UnitDisplay(si_kn_m = UnitDisplay(
+ length="m", force="kN", mass="kg", length_factor=1e3, force_factor=1e3
+ )
+
+Note that stresses will automatically be displayed as kilopascals using the above
+unit system as ``[kPa]`` is equivalent to |kpa|. Similarly, results in ``[N.mm]``,
+``[MPa]`` etc. can be automatically displayed by creating a ``UnitDisplay`` object that
+represents the base unit system:
+
+.. |kpa| raw:: html
+
+ [kN/m2]
+
+.. code-block:: python
+
+ from concreteproperties.post import UnitDisplay
+
+ n_mm = UnitDisplay(length="mm", force="N", mass="kg")
+
+Note that the above two unit systems are baked into ``concreteproperties`` in the
+``post`` module with the ``si_n_mm`` and ``si_kn_m`` objects:
+
+.. code-block:: python
+
+ from concreteproperties.post import si_kn_m, si_n_mm
+
+``concreteproperties`` welcomes the contribution of further unit systems!
diff --git a/pyproject.toml b/pyproject.toml
index 77d4d10e..e518a4ad 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -40,6 +40,7 @@ dependencies = [
"cytriangle~=1.0.3",
"rich[jupyter]~=13.9.3",
"more-itertools~=10.5.0",
+ "quantiphy~=2.20",
]
[project.urls]
@@ -58,6 +59,7 @@ docs = [
"ipython==8.29.0",
"nbsphinx==0.9.5",
"nbconvert==7.16.4",
+ "sphinx-autobuild==2024.10.03",
"sphinx-copybutton==0.5.2",
"sphinxext-opengraph==0.9.1",
]
diff --git a/src/concreteproperties/concrete_section.py b/src/concreteproperties/concrete_section.py
index ed855e9e..7f8e58f9 100644
--- a/src/concreteproperties/concrete_section.py
+++ b/src/concreteproperties/concrete_section.py
@@ -17,12 +17,14 @@
import concreteproperties.utils as utils
from concreteproperties.analysis_section import AnalysisSection
from concreteproperties.material import Concrete, SteelStrand
-from concreteproperties.post import plotting_context
+from concreteproperties.post import DEFAULT_UNITS, plotting_context
from concreteproperties.pre import CPGeom, CPGeomConcrete
if TYPE_CHECKING:
import matplotlib.axes
+ from concreteproperties.post import UnitDisplay
+
class ConcreteSection:
"""Class for a reinforced concrete section."""
@@ -32,6 +34,7 @@ def __init__(
geometry: sp_geom.CompoundGeometry,
moment_centroid: tuple[float, float] | None = None,
geometric_centroid_override: bool = False,
+ default_units: UnitDisplay | None = None,
) -> None:
"""Inits the ConcreteSection class.
@@ -45,6 +48,8 @@ def __init__(
geometric_centroid_override: If set to True, sets ``moment_centroid`` to
the geometric centroid i.e. material properties applied (useful for
composite section analysis). Defaults to ``False``.
+ default_units: Default unit system to use for formatting results. Defaults
+ to ``None``.
Raises:
ValueError: If steel strand materials are detected, use a
@@ -52,6 +57,10 @@ def __init__(
"""
self.compound_geometry = geometry
+ # assign unitless unit if no default_unit applied
+ units = DEFAULT_UNITS if default_units is None else default_units
+ self.default_units = units
+
# check overlapping regions
polygons = [sec_geom.geom for sec_geom in self.compound_geometry.geoms]
overlapped_regions = sp_geom.check_geometry_overlaps(polygons)
@@ -100,7 +109,7 @@ def __init__(
self.all_geometries.append(cp_geom)
# calculate gross properties
- self.gross_properties = res.GrossProperties()
+ self.gross_properties = res.GrossProperties(default_units=self.default_units)
self.calculate_gross_area_properties()
# set moment centroid
@@ -298,7 +307,9 @@ def get_transformed_gross_properties(
Transformed concrete properties object
"""
return res.TransformedGrossProperties(
- concrete_properties=self.gross_properties, elastic_modulus=elastic_modulus
+ default_units=self.default_units,
+ concrete_properties=self.gross_properties,
+ elastic_modulus=elastic_modulus,
)
def calculate_cracked_properties(
@@ -317,7 +328,9 @@ def calculate_cracked_properties(
Returns:
Cracked results object
"""
- cracked_results = res.CrackedResults(theta=theta)
+ cracked_results = res.CrackedResults(
+ default_units=self.default_units, theta=theta
+ )
cracked_results.m_cr = self.calculate_cracking_moment(theta=theta)
# set neutral axis depth limits
@@ -625,7 +638,9 @@ def moment_curvature_analysis(
Moment curvature results object
"""
# initialise variables
- moment_curvature = res.MomentCurvatureResults(theta=theta, n_target=n)
+ moment_curvature = res.MomentCurvatureResults(
+ default_units=self.default_units, theta=theta, n_target=n
+ )
# function that performs moment curvature analysis
def mcurve(kappa_inc=kappa_inc, progress=None):
@@ -935,7 +950,9 @@ def ultimate_bending_capacity(
b = 6 * d_t # neutral axis at sufficiently large tensile fibre
# initialise ultimate bending results
- ultimate_results = res.UltimateBendingResults(theta=theta)
+ ultimate_results = res.UltimateBendingResults(
+ default_units=self.default_units, theta=theta
+ )
# find neutral axis that gives convergence of the axial force
try:
@@ -1007,7 +1024,9 @@ def calculate_ultimate_section_actions(
Ultimate bending results object
"""
if ultimate_results is None:
- ultimate_results = res.UltimateBendingResults(theta=0)
+ ultimate_results = res.UltimateBendingResults(
+ default_units=self.default_units, theta=0
+ )
# calculate extreme fibre in global coordinates
extreme_fibre, _ = utils.calculate_extreme_fibre(
@@ -1160,7 +1179,7 @@ def moment_interaction_diagram(
Args:
theta: Angle (in radians) the neutral axis makes with the horizontal axis
- (:math:`-\pi \leq \theta \leq \pi`)
+ (:math:`-\pi \leq \theta \leq \pi`). Defaults to ``0``.
limits: List of control points that define the start and end of the
interaction diagram. List length must equal two. The default limits
range from concrete decompression strain to zero curvature tension, i.e.
@@ -1238,7 +1257,9 @@ def moment_interaction_diagram(
labels = labels * (len(control_points) + 2)
# initialise results
- mi_results = res.MomentInteractionResults()
+ mi_results = res.MomentInteractionResults(
+ default_units=self.default_units,
+ )
# generate list of neutral axis depths/axial forces to analyse
# if we are spacing by axial force
@@ -1246,11 +1267,15 @@ def moment_interaction_diagram(
# get axial force of the limits
start_res = self.calculate_ultimate_section_actions(
d_n=limits_dn[0],
- ultimate_results=res.UltimateBendingResults(theta=theta),
+ ultimate_results=res.UltimateBendingResults(
+ default_units=self.default_units, theta=theta
+ ),
)
end_res = self.calculate_ultimate_section_actions(
d_n=limits_dn[1],
- ultimate_results=res.UltimateBendingResults(theta=theta),
+ ultimate_results=res.UltimateBendingResults(
+ default_units=self.default_units, theta=theta
+ ),
)
# generate list of axial forces
@@ -1279,12 +1304,16 @@ def micurve(progress=None):
if idx == 0:
ult_res = self.calculate_ultimate_section_actions(
d_n=limits_dn[0],
- ultimate_results=res.UltimateBendingResults(theta=theta),
+ ultimate_results=res.UltimateBendingResults(
+ default_units=self.default_units, theta=theta
+ ),
)
elif idx == len(analysis_list) - 1:
ult_res = self.calculate_ultimate_section_actions(
d_n=limits_dn[1],
- ultimate_results=res.UltimateBendingResults(theta=theta),
+ ultimate_results=res.UltimateBendingResults(
+ default_units=self.default_units, theta=theta
+ ),
)
else:
ult_res = self.ultimate_bending_capacity(
@@ -1294,7 +1323,9 @@ def micurve(progress=None):
else:
ult_res = self.calculate_ultimate_section_actions(
d_n=analysis_point,
- ultimate_results=res.UltimateBendingResults(theta=theta),
+ ultimate_results=res.UltimateBendingResults(
+ default_units=self.default_units, theta=theta
+ ),
)
# add labels for limits
@@ -1315,7 +1346,9 @@ def micurve(progress=None):
for idx, d_n in enumerate(add_cp_dn):
ult_res = self.calculate_ultimate_section_actions(
d_n=d_n,
- ultimate_results=res.UltimateBendingResults(theta=theta),
+ ultimate_results=res.UltimateBendingResults(
+ default_units=self.default_units, theta=theta
+ ),
)
# add label
@@ -1395,6 +1428,7 @@ def micurve(progress=None):
mi_results.results.insert(
0, # insertion index
res.UltimateBendingResults(
+ default_units=self.default_units,
theta=theta,
d_n=inf,
k_u=0,
@@ -1429,7 +1463,7 @@ def biaxial_bending_diagram(
Biaxial bending results
"""
# initialise results
- bb_results = res.BiaxialBendingResults(n=n)
+ bb_results = res.BiaxialBendingResults(default_units=self.default_units, n=n)
# calculate d_theta
d_theta = 2 * np.pi / n_points
@@ -1591,6 +1625,7 @@ def calculate_uncracked_stress(
lumped_reinf_geoms.append(lumped_geom)
return res.StressResult(
+ default_units=self.default_units,
concrete_section=self,
concrete_analysis_sections=conc_sections,
concrete_stresses=conc_sigs,
@@ -1729,6 +1764,7 @@ def calculate_cracked_stress(
lumped_reinf_geoms.append(lumped_geom)
return res.StressResult(
+ default_units=self.default_units,
concrete_section=self,
concrete_analysis_sections=conc_sections,
concrete_stresses=conc_sigs,
@@ -1776,7 +1812,9 @@ def calculate_service_stress(
# initialise variables
mk = res.MomentCurvatureResults(
- theta=theta, n_target=moment_curvature_results.n_target
+ default_units=self.default_units,
+ theta=theta,
+ n_target=moment_curvature_results.n_target,
)
# find neutral axis that gives convergence of the axial force
@@ -1878,6 +1916,7 @@ def calculate_service_stress(
lumped_reinf_geoms.append(lumped_geom)
return res.StressResult(
+ default_units=self.default_units,
concrete_section=self,
concrete_analysis_sections=conc_sections,
concrete_stresses=conc_sigs,
@@ -2003,6 +2042,7 @@ def calculate_ultimate_stress(
lumped_reinf_geoms.append(lumped_geom)
return res.StressResult(
+ default_units=self.default_units,
concrete_section=self,
concrete_analysis_sections=conc_sections,
concrete_stresses=conc_sigs,
diff --git a/src/concreteproperties/design_codes/as3600.py b/src/concreteproperties/design_codes/as3600.py
index f447e268..8f29b47d 100644
--- a/src/concreteproperties/design_codes/as3600.py
+++ b/src/concreteproperties/design_codes/as3600.py
@@ -15,6 +15,7 @@
import concreteproperties.stress_strain_profile as ssp
from concreteproperties.design_codes.design_code import DesignCode
from concreteproperties.material import Concrete, SteelBar
+from concreteproperties.post import DEFAULT_UNITS, si_n_mm
from concreteproperties.utils import AnalysisError, create_known_progress
if TYPE_CHECKING:
@@ -56,6 +57,11 @@ def assign_concrete_section(
msg = "Meshed reinforcement is not supported in this design code."
raise ValueError(msg)
+ # assign default units if not provided
+ if self.concrete_section.default_units is DEFAULT_UNITS:
+ self.concrete_section.default_units = si_n_mm
+ self.concrete_section.gross_properties.default_units = si_n_mm
+
# determine reinforcement class
self.reinforcement_class = "N"
@@ -82,7 +88,7 @@ def create_concrete_material(
.. admonition:: Material assumptions
- - *Density*: 2400 kg/m\ :sup:`3`
+ - *Density*: 2400 kg/m\ :sup:`3` (2.4 x 10\ :sup:`-6` kg/mm\ :sup:`3`)
- *Elastic modulus*: Interpolated from Table 3.1.2
@@ -333,7 +339,10 @@ def get_n_ub(
# calculate axial force at balanced load
balanced_res = self.concrete_section.calculate_ultimate_section_actions(
- d_n=d_nb, ultimate_results=res.UltimateBendingResults(theta=theta)
+ d_n=d_nb,
+ ultimate_results=res.UltimateBendingResults(
+ default_units=self.concrete_section.default_units, theta=theta
+ ),
)
return balanced_res.n
@@ -419,6 +428,7 @@ def non_linear_phi(phi_guess):
squash = f_mi_res.results[0]
decomp = f_mi_res.results[1]
ult_res = res.UltimateBendingResults(
+ default_units=self.concrete_section.default_units,
theta=theta,
d_n=inf,
k_u=0,
@@ -437,6 +447,7 @@ def non_linear_phi(phi_guess):
factor = n_design / n_tensile
pure = f_mi_res.results[-2]
ult_res = res.UltimateBendingResults(
+ default_units=self.concrete_section.default_units,
theta=theta,
d_n=inf,
k_u=0,
@@ -537,6 +548,7 @@ def moment_interaction_diagram( # pyright: ignore [reportIncompatibleMethodOver
mi_res.results.insert(
0,
res.UltimateBendingResults(
+ default_units=self.concrete_section.default_units,
theta=theta,
d_n=inf,
k_u=0,
@@ -550,6 +562,7 @@ def moment_interaction_diagram( # pyright: ignore [reportIncompatibleMethodOver
# add tensile load
mi_res.results.append(
res.UltimateBendingResults(
+ default_units=self.concrete_section.default_units,
theta=theta,
d_n=0,
k_u=0,
@@ -606,7 +619,9 @@ def biaxial_bending_diagram( # pyright: ignore [reportIncompatibleMethodOverrid
factors (``factored_results``, ``phis``)
"""
# initialise results
- f_bb_res = res.BiaxialBendingResults(n=n_design)
+ f_bb_res = res.BiaxialBendingResults(
+ default_units=self.concrete_section.default_units, n=n_design
+ )
phis = []
# calculate d_theta
diff --git a/src/concreteproperties/design_codes/nzs3101.py b/src/concreteproperties/design_codes/nzs3101.py
index 585a36a8..1c52813f 100644
--- a/src/concreteproperties/design_codes/nzs3101.py
+++ b/src/concreteproperties/design_codes/nzs3101.py
@@ -14,6 +14,7 @@
import concreteproperties.utils as utils
from concreteproperties.design_codes.design_code import DesignCode
from concreteproperties.material import Concrete, SteelBar
+from concreteproperties.post import si_n_mm
if TYPE_CHECKING:
from concreteproperties.concrete_section import ConcreteSection
@@ -104,6 +105,11 @@ def assign_concrete_section(
# assign section type
self.section_type = section_type
+ # assign default units if not provided
+ if self.concrete_section.default_units.length == "":
+ self.concrete_section.default_units = si_n_mm
+ self.concrete_section.gross_properties.default_units = si_n_mm
+
# check to make sure there are no meshed reinforcement regions
if self.concrete_section.reinf_geometries_meshed:
msg = f"Meshed reinforcement is not supported in the {self.analysis_code} "
@@ -377,7 +383,7 @@ def prob_compressive_strength(
Calculate the probable compressive strength of concrete in accordance with
NZSEE C5 assessement guidelines C5.4.2.2.
- Taken as the nominal 28-day compressive strenght of the concrete specified for
+ Taken as the nominal 28-day compressive strength of the concrete specified for
the original construciton, multiplied by 1.5 for strengths less than or equal to
40 MPa, and 1.4 for strengths greater than 40 MPa.
@@ -729,7 +735,7 @@ def check_axial_limits(
raise ValueError(msg)
def check_f_y_limit(self) -> None:
- """Checks the reinforcement strenghts are within limits.
+ """Checks the reinforcement strengths are within limits.
Checks that the specified steel reinforcement strengths for all defined
steel geometries comply with NZS3101:2006 CL 5.3.3.
@@ -774,7 +780,7 @@ def check_f_c_limits(
self,
pphr_class: str,
) -> None:
- """Checks the concrete strenght complies with the PPHR classification.
+ """Checks the concrete strength complies with the PPHR classification.
Checks that a valid Potential Plastic Hinge Region (PPHR) classification has
been specified, and that the specified compressive strengths for all defined
@@ -1836,7 +1842,9 @@ def biaxial_bending_diagram( # pyright: ignore [reportIncompatibleMethodOverrid
self.check_f_y_limit()
# initialise results
- f_bb_res = res.BiaxialBendingResults(n=n_design)
+ f_bb_res = res.BiaxialBendingResults(
+ default_units=self.concrete_section.default_units, n=n_design
+ )
# list to store phis
phis = []
diff --git a/src/concreteproperties/post.py b/src/concreteproperties/post.py
index 81dfe523..9ff2e9c6 100644
--- a/src/concreteproperties/post.py
+++ b/src/concreteproperties/post.py
@@ -3,9 +3,12 @@
from __future__ import annotations
import contextlib
+from dataclasses import dataclass
from typing import TYPE_CHECKING
import matplotlib.pyplot as plt
+import numpy as np
+from quantiphy import Quantity
if TYPE_CHECKING:
import matplotlib.axes
@@ -100,3 +103,243 @@ def plotting_context(
else:
plt.draw()
plt.pause(0.001)
+
+
+def string_formatter(
+ value: float,
+ eng: bool,
+ prec: int,
+ scale: float = 1.0,
+) -> str:
+ """Formats a float using engineering or fixed notation.
+
+ Args:
+ value: Number to format
+ eng: If set to ``True``, formats with engineering notation. If set to ``False``,
+ formats with fixed notation.
+ prec: The desired precision (i.e. one plus this value is the desired number of
+ digits)
+ scale: Factor by which to scale the value. Defaults to ``1.0``.
+
+ Returns:
+ Formatted string
+ """
+ q = Quantity(value)
+ form = "eng" if eng else "fixed"
+ val_fmt = q.render(
+ form=form,
+ show_units=False,
+ prec=prec,
+ scale=scale,
+ strip_zeros=False,
+ )
+ spl = val_fmt.split("e")
+
+ # if there is an exponent, render as 'x 10^n'
+ if eng and len(spl) > 1:
+ num = spl[0]
+ exp = spl[1]
+ return f"{num} x 10^{exp}"
+ else:
+ return val_fmt
+
+
+def string_formatter_plots(
+ value: float,
+ prec: int,
+) -> str:
+ """Formats a float using engineering notation for plotting.
+
+ Args:
+ value: Number to format
+ prec: The desired precision (i.e. one plus this value is the desired number of
+ digits)
+
+ Returns:
+ Formatted string
+ """
+ q = Quantity(value)
+ return q.render(
+ form="eng", show_units=False, prec=prec, strip_zeros=True, strip_radix=True
+ )
+
+
+def string_formatter_stress(
+ value: float,
+ eng: bool,
+ prec: int,
+) -> str:
+ """Formats a float using engineering notation for stress plotting.
+
+ Args:
+ value: Number to format
+ eng: If set to ``True``, formats with engineering notation. If set to ``False``,
+ formats with fixed notation.
+ prec: The desired precision (i.e. one plus this value is the desired number of
+ digits)
+
+ Returns:
+ Formatted string
+ """
+ q = Quantity(value)
+ form = "eng" if eng else "fixed"
+ val_fmt = q.render(
+ form=form, show_units=False, prec=prec, strip_zeros=False, strip_radix=False
+ )
+
+ # ensure there is an e0
+ if "e" not in val_fmt and eng:
+ val_fmt += "e0"
+
+ return val_fmt
+
+
+@dataclass
+class UnitDisplay:
+ """Class for displaying units in ``concreteproperties``.
+
+ Attributes:
+ length: Length unit string
+ force: Force unit string
+ mass: Mass unit string
+ radians: If set to ``True``, displays angles in radians, otherwise displays
+ angles in degrees. Defaults to ``True``.
+ length_factor: Factor by which the ``length`` unit differs from the base units.
+ Defaults to ``1.0``.
+ force_factor: Factor by which the ``force`` unit differs from the base units.
+ Defaults to ``1.0``.
+ mass_factor: Factor by which the ``mass`` unit differs from the base units.
+ Defaults to ``1.0``.
+ """
+
+ length: str
+ force: str
+ mass: str
+ radians: bool = True
+ length_factor: float = 1.0
+ force_factor: float = 1.0
+ mass_factor: float = 1.0
+
+ @property
+ def length_unit(self) -> str:
+ """Returns the length unit string."""
+ return self.length if self.length == "" else f" {self.length}"
+
+ @property
+ def length_scale(self) -> float:
+ """Returns the length scale."""
+ return 1 / self.length_factor
+
+ @property
+ def force_unit(self) -> str:
+ """Returns the force unit string."""
+ return self.force if self.force == "" else f" {self.force}"
+
+ @property
+ def force_scale(self) -> float:
+ """Returns the force scale."""
+ return 1 / self.force_factor
+
+ @property
+ def mass_unit(self) -> str:
+ """Returns the mass unit string."""
+ return self.mass if self.mass == "" else f" {self.mass}"
+
+ @property
+ def mass_scale(self) -> float:
+ """Returns the mass scale."""
+ return 1 / self.mass_factor
+
+ @property
+ def angle_unit(self) -> str:
+ """Returns the angle unit string."""
+ return " rads" if self.radians else " degs"
+
+ @property
+ def angle_scale(self) -> float:
+ """Returns the angle scale."""
+ return 1 if self.radians else 180.0 / np.pi
+
+ @property
+ def area_unit(self) -> str:
+ """Returns the area unit string."""
+ return self.length if self.length == "" else f" {self.length}^2"
+
+ @property
+ def area_scale(self) -> float:
+ """Returns the area scale."""
+ return 1 / self.length_factor / self.length_factor
+
+ @property
+ def mass_per_length_unit(self) -> str:
+ """Returns the mass/length unit string."""
+ return self.mass if self.mass == "" else f" {self.mass}/{self.length}"
+
+ @property
+ def mass_per_length_scale(self) -> float:
+ """Returns the mass/length scale."""
+ return 1 / self.mass_factor * self.length_factor
+
+ @property
+ def moment_unit(self) -> str:
+ """Returns the moment unit string."""
+ return self.length if self.length == "" else f" {self.force}.{self.length}"
+
+ @property
+ def moment_scale(self) -> float:
+ """Returns the moment scale."""
+ return 1 / self.force_factor / self.length_factor
+
+ @property
+ def flex_rig_unit(self) -> str:
+ """Returns the flexural rigidity unit string."""
+ return self.length if self.length == "" else f" {self.force}.{self.length}^2"
+
+ @property
+ def flex_rig_scale(self) -> float:
+ """Returns the flexural rigidity scale."""
+ return 1 / self.force_factor / self.length_factor / self.length_factor
+
+ @property
+ def stress_unit(self) -> str:
+ """Returns the stress unit string."""
+ if self.length == "mm" and self.force == "N":
+ return " MPa"
+ elif self.length == "m" and self.force == "kN":
+ return " kPa"
+ elif self.length == "":
+ return ""
+ else:
+ return f" {self.force}/{self.length}^2"
+
+ @property
+ def stress_scale(self) -> float:
+ """Returns the stress scale."""
+ return 1 / self.force_factor * self.length_factor * self.length_factor
+
+ @property
+ def length_3_unit(self) -> str:
+ """Returns the length^3 unit string."""
+ return self.length if self.length == "" else f" {self.length}^3"
+
+ @property
+ def length_3_scale(self) -> float:
+ """Returns the length^3 scale."""
+ return 1 / self.length_factor**3
+
+ @property
+ def length_4_unit(self) -> str:
+ """Returns the length^4 unit string."""
+ return self.length if self.length == "" else f" {self.length}^4"
+
+ @property
+ def length_4_scale(self) -> float:
+ """Returns the length^4 scale."""
+ return 1 / self.length_factor**4
+
+
+DEFAULT_UNITS = UnitDisplay(length="", force="", mass="")
+si_n_mm = UnitDisplay(length="mm", force="N", mass="kg")
+si_kn_m = UnitDisplay(
+ length="m", force="kN", mass="kg", length_factor=1e3, force_factor=1e3
+)
diff --git a/src/concreteproperties/prestressed_section.py b/src/concreteproperties/prestressed_section.py
index 21496361..442cb7ae 100644
--- a/src/concreteproperties/prestressed_section.py
+++ b/src/concreteproperties/prestressed_section.py
@@ -18,6 +18,8 @@
if TYPE_CHECKING:
import sectionproperties.pre.geometry as sp_geom
+ from concreteproperties.post import UnitDisplay
+
class PrestressedSection(ConcreteSection):
"""Class for a prestressed concrete section.
@@ -38,6 +40,7 @@ def __init__(
geometry: sp_geom.CompoundGeometry,
moment_centroid: tuple[float, float] | None = None,
geometric_centroid_override: bool = True,
+ default_units: UnitDisplay | None = None,
) -> None:
"""Inits the ConcreteSection class.
@@ -51,6 +54,8 @@ def __init__(
geometric_centroid_override: If set to True, sets ``moment_centroid`` to
the geometric centroid i.e. material properties applied. Defaults to
``True``.
+ default_units: Default unit system to use for formatting results. Defaults
+ to ``None``.
Raises:
ValueError: If the section is not symmetric about the y-axis
@@ -60,6 +65,7 @@ def __init__(
geometry=geometry,
moment_centroid=moment_centroid,
geometric_centroid_override=geometric_centroid_override,
+ default_units=default_units,
)
# check symmetry about y-axis
@@ -129,6 +135,7 @@ def calculate_cracked_properties( # pyright: ignore [reportIncompatibleMethodOv
# initialise cracked results object
cracked_results = res.CrackedResults(
+ default_units=self.default_units,
theta=0,
n=self.gross_properties.n_prestress + n_ext,
m=m_ext,
@@ -351,7 +358,9 @@ def moment_curvature_analysis( # pyright: ignore [reportIncompatibleMethodOverr
# determine initial curvature that gives zero moment
def find_intial_curvature(kappa0):
# initialise moment curvature result
- mk_res = res.MomentCurvatureResults(theta=theta, n_target=n)
+ mk_res = res.MomentCurvatureResults(
+ default_units=self.default_units, theta=theta, n_target=n
+ )
# find neutral axis that gives convergence of axial force
brentq(
@@ -541,6 +550,7 @@ def calculate_uncracked_stress( # pyright: ignore [reportIncompatibleMethodOver
lumped_reinf_geoms.append(lumped_geom)
return res.StressResult(
+ default_units=self.default_units,
concrete_section=self,
concrete_analysis_sections=conc_sections,
concrete_stresses=conc_sigs,
@@ -671,6 +681,7 @@ def calculate_cracked_stress( # pyright: ignore [reportIncompatibleMethodOverri
lumped_reinf_geoms.append(lumped_geom)
return res.StressResult(
+ default_units=self.default_units,
concrete_section=self,
concrete_analysis_sections=conc_sections,
concrete_stresses=conc_sigs,
@@ -720,7 +731,9 @@ def calculate_service_stress(
# initialise variables
mk = res.MomentCurvatureResults(
- theta=0, n_target=moment_curvature_results.n_target
+ default_units=self.default_units,
+ theta=0,
+ n_target=moment_curvature_results.n_target,
)
# find neutral axis that gives convergence of the axial force
@@ -836,6 +849,7 @@ def calculate_service_stress(
lumped_reinf_geoms.append(lumped_geom)
return res.StressResult(
+ default_units=self.default_units,
concrete_section=self,
concrete_analysis_sections=conc_sections,
concrete_stresses=conc_sigs,
@@ -979,6 +993,7 @@ def calculate_ultimate_stress(
lumped_reinf_geoms.append(lumped_geom)
return res.StressResult(
+ default_units=self.default_units,
concrete_section=self,
concrete_analysis_sections=conc_sections,
concrete_stresses=conc_sigs,
diff --git a/src/concreteproperties/results.py b/src/concreteproperties/results.py
index e465c445..d5f277af 100644
--- a/src/concreteproperties/results.py
+++ b/src/concreteproperties/results.py
@@ -14,17 +14,25 @@
import numpy as np
from matplotlib.collections import PatchCollection
from matplotlib.colors import CenteredNorm
+from matplotlib.ticker import FuncFormatter
from rich.console import Console
from rich.table import Table
from scipy.interpolate import interp1d
from sectionproperties.pre.geometry import CompoundGeometry
from shapely import Point, Polygon
-from concreteproperties.post import plotting_context
+from concreteproperties.post import (
+ DEFAULT_UNITS,
+ plotting_context,
+ string_formatter,
+ string_formatter_plots,
+ string_formatter_stress,
+)
if TYPE_CHECKING:
from concreteproperties.analysis_section import AnalysisSection
from concreteproperties.concrete_section import ConcreteSection
+ from concreteproperties.post import UnitDisplay
from concreteproperties.pre import CPGeom
@@ -38,6 +46,9 @@ class GrossProperties:
method.
"""
+ # units
+ default_units: UnitDisplay
+
# section areas
total_area: float = 0
concrete_area: float = 0
@@ -94,69 +105,282 @@ class GrossProperties:
def print_results(
self,
- fmt: str = "8.6e",
+ eng: bool = True,
+ prec: int = 3,
+ units: UnitDisplay | None = None,
) -> None:
"""Prints the gross concrete section properties to the terminal.
Args:
- fmt: Number format. Defaults to ``"8.6e"``.
+ eng: If set to ``True``, formats with engineering notation. If set to
+ ``False``, formats with fixed notation. Defaults to ``True``.
+ prec: The desired precision (i.e. one plus this value is the desired number
+ of digits). Defaults to ``3``.
+ units: Unit system to display. Defaults to ``None``.
"""
+ # setup table
table = Table(title="Gross Concrete Section Properties")
table.add_column("Property", justify="left", style="cyan", no_wrap=True)
table.add_column("Value", justify="right", style="green")
- table.add_row("Total Area", "{:>{fmt}}".format(self.total_area, fmt=fmt))
- table.add_row("Concrete Area", "{:>{fmt}}".format(self.concrete_area, fmt=fmt))
+ # assign default unit if no units provided
+ if units is None:
+ units = self.default_units
+
+ # add table rows
+ table.add_row(
+ "Total Area",
+ string_formatter(
+ value=self.total_area, eng=eng, prec=prec, scale=units.area_scale
+ )
+ + units.area_unit,
+ )
+ table.add_row(
+ "Concrete Area",
+ string_formatter(
+ value=self.concrete_area, eng=eng, prec=prec, scale=units.area_scale
+ )
+ + units.area_unit,
+ )
if self.reinf_meshed_area:
table.add_row(
"Meshed Reinforcement Area",
- "{:>{fmt}}".format(self.reinf_meshed_area, fmt=fmt),
+ string_formatter(
+ value=self.reinf_meshed_area,
+ eng=eng,
+ prec=prec,
+ scale=units.area_scale,
+ )
+ + units.area_unit,
)
table.add_row(
"Lumped Reinforcement Area",
- "{:>{fmt}}".format(self.reinf_lumped_area, fmt=fmt),
+ string_formatter(
+ value=self.reinf_lumped_area, eng=eng, prec=prec, scale=units.area_scale
+ )
+ + units.area_unit,
)
if self.strand_area:
- table.add_row("Strand Area", "{:>{fmt}}".format(self.strand_area, fmt=fmt))
-
- table.add_row("Axial Rigidity (EA)", "{:>{fmt}}".format(self.e_a, fmt=fmt))
- table.add_row("Mass (per unit length)", "{:>{fmt}}".format(self.mass, fmt=fmt))
- table.add_row("Perimeter", "{:>{fmt}}".format(self.perimeter, fmt=fmt))
- table.add_row("E.Qx", "{:>{fmt}}".format(self.e_qx, fmt=fmt))
- table.add_row("E.Qy", "{:>{fmt}}".format(self.e_qy, fmt=fmt))
- table.add_row("x-Centroid", "{:>{fmt}}".format(self.cx, fmt=fmt))
- table.add_row("y-Centroid", "{:>{fmt}}".format(self.cy, fmt=fmt))
- table.add_row("x-Centroid (Gross)", "{:>{fmt}}".format(self.cx_gross, fmt=fmt))
- table.add_row("y-Centroid (Gross)", "{:>{fmt}}".format(self.cy_gross, fmt=fmt))
- table.add_row("E.Ixx_g", "{:>{fmt}}".format(self.e_ixx_g, fmt=fmt))
- table.add_row("E.Iyy_g", "{:>{fmt}}".format(self.e_iyy_g, fmt=fmt))
- table.add_row("E.Ixy_g", "{:>{fmt}}".format(self.e_ixy_g, fmt=fmt))
- table.add_row("E.Ixx_c", "{:>{fmt}}".format(self.e_ixx_c, fmt=fmt))
- table.add_row("E.Iyy_c", "{:>{fmt}}".format(self.e_iyy_c, fmt=fmt))
- table.add_row("E.Ixy_c", "{:>{fmt}}".format(self.e_ixy_c, fmt=fmt))
- table.add_row("E.I11", "{:>{fmt}}".format(self.e_i11, fmt=fmt))
- table.add_row("E.I22", "{:>{fmt}}".format(self.e_i22, fmt=fmt))
- table.add_row("Principal Axis Angle", "{:>{fmt}}".format(self.phi, fmt=fmt))
- table.add_row("E.Zxx+", "{:>{fmt}}".format(self.e_zxx_plus, fmt=fmt))
- table.add_row("E.Zxx-", "{:>{fmt}}".format(self.e_zxx_minus, fmt=fmt))
- table.add_row("E.Zyy+", "{:>{fmt}}".format(self.e_zyy_plus, fmt=fmt))
- table.add_row("E.Zyy-", "{:>{fmt}}".format(self.e_zyy_minus, fmt=fmt))
- table.add_row("E.Z11+", "{:>{fmt}}".format(self.e_z11_plus, fmt=fmt))
- table.add_row("E.Z11-", "{:>{fmt}}".format(self.e_z11_minus, fmt=fmt))
- table.add_row("E.Z22+", "{:>{fmt}}".format(self.e_z22_plus, fmt=fmt))
- table.add_row("E.Z22-", "{:>{fmt}}".format(self.e_z22_minus, fmt=fmt))
+ table.add_row(
+ "Strand Area",
+ string_formatter(
+ value=self.strand_area, eng=eng, prec=prec, scale=units.area_scale
+ )
+ + units.area_unit,
+ )
+
+ table.add_row(
+ "Axial Rigidity (EA)",
+ string_formatter(
+ value=self.e_a, eng=eng, prec=prec, scale=units.force_scale
+ )
+ + units.force_unit,
+ )
+ table.add_row(
+ "Mass (per unit length)",
+ string_formatter(
+ value=self.mass, eng=eng, prec=prec, scale=units.mass_per_length_scale
+ )
+ + units.mass_per_length_unit,
+ )
+ table.add_row(
+ "Perimeter",
+ string_formatter(
+ value=self.perimeter, eng=eng, prec=prec, scale=units.length_scale
+ )
+ + units.length_unit,
+ end_section=True,
+ )
+ table.add_row(
+ "E.Qx",
+ string_formatter(
+ value=self.e_qx, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "E.Qy",
+ string_formatter(
+ value=self.e_qy, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "x-Centroid",
+ string_formatter(
+ value=self.cx, eng=eng, prec=prec, scale=units.length_scale
+ )
+ + units.length_unit,
+ )
+ table.add_row(
+ "y-Centroid",
+ string_formatter(
+ value=self.cy, eng=eng, prec=prec, scale=units.length_scale
+ )
+ + units.length_unit,
+ )
+ table.add_row(
+ "x-Centroid (Gross)",
+ string_formatter(
+ value=self.cx_gross, eng=eng, prec=prec, scale=units.length_scale
+ )
+ + units.length_unit,
+ )
+ table.add_row(
+ "y-Centroid (Gross)",
+ string_formatter(
+ value=self.cy_gross, eng=eng, prec=prec, scale=units.length_scale
+ )
+ + units.length_unit,
+ end_section=True,
+ )
+ table.add_row(
+ "E.Ixx_g",
+ string_formatter(
+ value=self.e_ixx_g, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.Iyy_g",
+ string_formatter(
+ value=self.e_iyy_g, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.Ixy_g",
+ string_formatter(
+ value=self.e_ixy_g, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.Ixx_c",
+ string_formatter(
+ value=self.e_ixx_c, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.Iyy_c",
+ string_formatter(
+ value=self.e_iyy_c, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.Ixy_c",
+ string_formatter(
+ value=self.e_ixy_c, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.I11",
+ string_formatter(
+ value=self.e_i11, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.I22",
+ string_formatter(
+ value=self.e_i22, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+
+ table.add_row(
+ "Principal Axis Angle",
+ string_formatter(
+ value=self.phi, eng=eng, prec=prec, scale=units.angle_scale
+ )
+ + units.angle_unit,
+ end_section=True,
+ )
+ table.add_row(
+ "E.Zxx+",
+ string_formatter(
+ value=self.e_zxx_plus, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "E.Zxx-",
+ string_formatter(
+ value=self.e_zxx_minus, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "E.Zyy+",
+ string_formatter(
+ value=self.e_zyy_plus, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "E.Zyy-",
+ string_formatter(
+ value=self.e_zyy_minus, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "E.Z11+",
+ string_formatter(
+ value=self.e_z11_plus, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "E.Z11-",
+ string_formatter(
+ value=self.e_z11_minus, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "E.Z22+",
+ string_formatter(
+ value=self.e_z22_plus, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "E.Z22-",
+ string_formatter(
+ value=self.e_z22_minus, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ end_section=True,
+ )
table.add_row(
"Ultimate Concrete Strain",
- "{:>{fmt}}".format(self.conc_ultimate_strain, fmt=fmt),
+ string_formatter(value=self.conc_ultimate_strain, eng=eng, prec=prec),
+ end_section=True,
)
# add prestressed results if they exist
if self.n_prestress:
- table.add_row("n_prestress", "{:>{fmt}}".format(self.n_prestress, fmt=fmt))
- table.add_row("m_prestress", "{:>{fmt}}".format(self.m_prestress, fmt=fmt))
+ table.add_row(
+ "n_prestress",
+ string_formatter(
+ value=self.n_prestress, eng=eng, prec=prec, scale=units.force_scale
+ )
+ + units.force_unit,
+ )
+ table.add_row(
+ "m_prestress",
+ string_formatter(
+ value=self.m_prestress, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
console = Console()
console.print(table)
@@ -167,10 +391,14 @@ class TransformedGrossProperties:
"""Class for storing transformed gross concrete section properties.
Args:
- concrete_properties: Concrete properties object
+ default_units: Default units to use for reporting
+ concrete_properties: Gross properties object
elastic_modulus: Reference elastic modulus
"""
+ # units
+ default_units: UnitDisplay
+
concrete_properties: GrossProperties = field(repr=False)
elastic_modulus: float
@@ -225,37 +453,171 @@ def __post_init__(self) -> None:
def print_results(
self,
- fmt: str = "8.6e",
+ eng: bool = True,
+ prec: int = 3,
+ units: UnitDisplay | None = None,
) -> None:
"""Prints the transformed gross concrete section properties to the terminal.
Args:
- fmt: Number format. Defaults to ``"8.6e"``.
+ eng: If set to ``True``, formats with engineering notation. If set to
+ ``False``, formats with fixed notation. Defaults to ``True``.
+ prec: The desired precision (i.e. one plus this value is the desired number
+ of digits). Defaults to ``3``.
+ units: Unit system to display. Defaults to ``None``.
"""
+ # setup table
table = Table(title="Transformed Gross Concrete Section Properties")
table.add_column("Property", justify="left", style="cyan", no_wrap=True)
table.add_column("Value", justify="right", style="green")
- table.add_row("E_ref", "{:>{fmt}}".format(self.elastic_modulus, fmt=fmt))
- table.add_row("Area", "{:>{fmt}}".format(self.area, fmt=fmt))
- table.add_row("Qx", "{:>{fmt}}".format(self.qx, fmt=fmt))
- table.add_row("Qy", "{:>{fmt}}".format(self.qy, fmt=fmt))
- table.add_row("Ixx_g", "{:>{fmt}}".format(self.ixx_g, fmt=fmt))
- table.add_row("Iyy_g", "{:>{fmt}}".format(self.iyy_g, fmt=fmt))
- table.add_row("Ixy_g", "{:>{fmt}}".format(self.ixy_g, fmt=fmt))
- table.add_row("Ixx_c", "{:>{fmt}}".format(self.ixx_c, fmt=fmt))
- table.add_row("Iyy_c", "{:>{fmt}}".format(self.iyy_c, fmt=fmt))
- table.add_row("Ixy_c", "{:>{fmt}}".format(self.ixy_c, fmt=fmt))
- table.add_row("I11", "{:>{fmt}}".format(self.i11, fmt=fmt))
- table.add_row("I22", "{:>{fmt}}".format(self.i22, fmt=fmt))
- table.add_row("Zxx+", "{:>{fmt}}".format(self.zxx_plus, fmt=fmt))
- table.add_row("Zxx-", "{:>{fmt}}".format(self.zxx_minus, fmt=fmt))
- table.add_row("Zyy+", "{:>{fmt}}".format(self.zyy_plus, fmt=fmt))
- table.add_row("Zyy-", "{:>{fmt}}".format(self.zyy_minus, fmt=fmt))
- table.add_row("Z11+", "{:>{fmt}}".format(self.z11_plus, fmt=fmt))
- table.add_row("Z11-", "{:>{fmt}}".format(self.z11_minus, fmt=fmt))
- table.add_row("Z22+", "{:>{fmt}}".format(self.z22_plus, fmt=fmt))
- table.add_row("Z22-", "{:>{fmt}}".format(self.z22_minus, fmt=fmt))
+ # assign default unit if no units provided
+ if units is None:
+ units = self.default_units
+
+ # add table rows
+ table.add_row(
+ "E_ref",
+ string_formatter(
+ value=self.elastic_modulus, eng=eng, prec=prec, scale=units.stress_scale
+ )
+ + units.stress_unit,
+ )
+ table.add_row(
+ "Area",
+ string_formatter(
+ value=self.area, eng=eng, prec=prec, scale=units.area_scale
+ )
+ + units.area_unit,
+ end_section=True,
+ )
+ table.add_row(
+ "Qx",
+ string_formatter(
+ value=self.qx, eng=eng, prec=prec, scale=units.length_3_scale
+ )
+ + units.length_3_unit,
+ )
+ table.add_row(
+ "Qy",
+ string_formatter(
+ value=self.qy, eng=eng, prec=prec, scale=units.length_3_scale
+ )
+ + units.length_3_unit,
+ )
+ table.add_row(
+ "Ixx_g",
+ string_formatter(
+ value=self.ixx_g, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "Iyy_g",
+ string_formatter(
+ value=self.iyy_g, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "Ixy_g",
+ string_formatter(
+ value=self.ixy_g, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "Ixx_c",
+ string_formatter(
+ value=self.ixx_c, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "Iyy_c",
+ string_formatter(
+ value=self.iyy_c, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "Ixy_c",
+ string_formatter(
+ value=self.ixy_c, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "I11",
+ string_formatter(
+ value=self.i11, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "I22",
+ string_formatter(
+ value=self.i22, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ end_section=True,
+ )
+ table.add_row(
+ "Zxx+",
+ string_formatter(
+ value=self.zxx_plus, eng=eng, prec=prec, scale=units.length_3_scale
+ )
+ + units.length_3_unit,
+ )
+ table.add_row(
+ "Zxx-",
+ string_formatter(
+ value=self.zxx_minus, eng=eng, prec=prec, scale=units.length_3_scale
+ )
+ + units.length_3_unit,
+ )
+ table.add_row(
+ "Zyy+",
+ string_formatter(
+ value=self.zyy_plus, eng=eng, prec=prec, scale=units.length_3_scale
+ )
+ + units.length_3_unit,
+ )
+ table.add_row(
+ "Zyy-",
+ string_formatter(
+ value=self.zyy_minus, eng=eng, prec=prec, scale=units.length_3_scale
+ )
+ + units.length_3_unit,
+ )
+ table.add_row(
+ "Z11+",
+ string_formatter(
+ value=self.z11_plus, eng=eng, prec=prec, scale=units.length_3_scale
+ )
+ + units.length_3_unit,
+ )
+ table.add_row(
+ "Z11-",
+ string_formatter(
+ value=self.z11_minus, eng=eng, prec=prec, scale=units.length_3_scale
+ )
+ + units.length_3_unit,
+ )
+ table.add_row(
+ "Z22+",
+ string_formatter(
+ value=self.z22_plus, eng=eng, prec=prec, scale=units.length_3_scale
+ )
+ + units.length_3_unit,
+ )
+ table.add_row(
+ "Z22-",
+ string_formatter(
+ value=self.z22_minus, eng=eng, prec=prec, scale=units.length_3_scale
+ )
+ + units.length_3_unit,
+ )
console = Console()
console.print(table)
@@ -271,10 +633,14 @@ class CrackedResults:
method.
Args:
+ default_units: Default units to use for reporting
theta: Angle (in radians) the neutral axis makes with the horizontal axis
(:math:`-\pi \leq \theta \leq \pi`)
"""
+ # units
+ default_units: UnitDisplay
+
theta: float
n: float = 0
m: float = 0
@@ -374,69 +740,315 @@ def plot_cracked_geometries(
def print_results(
self,
- fmt: str = "8.6e",
+ eng: bool = True,
+ prec: int = 3,
+ units: UnitDisplay | None = None,
) -> None:
- """Prints the cracked concrete section properties to the terminal.
+ """Prints cracked concrete section properties to the terminal.
+
+ If ``calculate_transformed_properties()`` has been called, also prints the
+ transformed properties.
Args:
- fmt: Number format. Defaults to ``"8.6e"``.
+ eng: If set to ``True``, formats with engineering notation. If set to
+ ``False``, formats with fixed notation. Defaults to ``True``.
+ prec: The desired precision (i.e. one plus this value is the desired number
+ of digits). Defaults to ``3``..
+ units: Unit system to display. Defaults to ``None``.
"""
+ # setup table
table = Table(title="Cracked Concrete Section Properties")
table.add_column("Property", justify="left", style="cyan", no_wrap=True)
table.add_column("Value", justify="right", style="green")
- table.add_row("theta", "{:>{fmt}}".format(self.theta, fmt=fmt))
- table.add_row("n", "{:>{fmt}}".format(self.n, fmt=fmt))
- table.add_row("m", "{:>{fmt}}".format(self.m, fmt=fmt))
+ # assign default unit if no units provided
+ if units is None:
+ units = self.default_units
+
+ # add table rows
+ table.add_row(
+ "theta",
+ string_formatter(
+ value=self.theta, eng=eng, prec=prec, scale=units.angle_scale
+ )
+ + units.angle_unit,
+ )
+
+ # only show n & m if one is non-zero
+ if self.n != 0 or self.m != 0:
+ table.add_row(
+ "n",
+ string_formatter(
+ value=self.n, eng=eng, prec=prec, scale=units.force_scale
+ )
+ + units.force_unit,
+ )
+ table.add_row(
+ "m",
+ string_formatter(
+ value=self.m, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
- if self.elastic_modulus_ref:
+ if self.elastic_modulus_ref is not None:
table.add_row(
- "E_ref", "{:>{fmt}}".format(self.elastic_modulus_ref, fmt=fmt)
+ "E_ref",
+ string_formatter(
+ value=self.elastic_modulus_ref,
+ eng=eng,
+ prec=prec,
+ scale=units.stress_scale,
+ )
+ + units.stress_unit,
)
+ table.add_section()
+
if isinstance(self.m_cr, tuple):
- table.add_row("m_cr_pos", "{:>{fmt}}".format(self.m_cr[0], fmt=fmt))
- table.add_row("m_cr_neg", "{:>{fmt}}".format(self.m_cr[1], fmt=fmt))
+ table.add_row(
+ "m_cr_pos",
+ string_formatter(
+ value=self.m_cr[0], eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "m_cr_neg",
+ string_formatter(
+ value=self.m_cr[1], eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
else:
- table.add_row("m_cr", "{:>{fmt}}".format(self.m_cr, fmt=fmt))
-
- table.add_row("d_nc", "{:>{fmt}}".format(self.d_nc, fmt=fmt))
-
- if self.a_cr:
- table.add_row("A_cr", "{:>{fmt}}".format(self.a_cr, fmt=fmt))
-
- table.add_row("E.A_cr", "{:>{fmt}}".format(self.e_a_cr, fmt=fmt))
-
- if self.qx_cr:
- table.add_row("Qx_cr", "{:>{fmt}}".format(self.qx_cr, fmt=fmt))
- table.add_row("Qy_cr", "{:>{fmt}}".format(self.qy_cr, fmt=fmt))
-
- table.add_row("E.Qx_cr", "{:>{fmt}}".format(self.e_qx_cr, fmt=fmt))
- table.add_row("E.Qy_cr", "{:>{fmt}}".format(self.e_qy_cr, fmt=fmt))
- table.add_row("x-Centroid", "{:>{fmt}}".format(self.cx, fmt=fmt))
- table.add_row("y-Centroid", "{:>{fmt}}".format(self.cy, fmt=fmt))
-
- if self.ixx_g_cr:
- table.add_row("Ixx_g_cr", "{:>{fmt}}".format(self.ixx_g_cr, fmt=fmt))
- table.add_row("Iyy_g_cr", "{:>{fmt}}".format(self.iyy_g_cr, fmt=fmt))
- table.add_row("Ixy_g_cr", "{:>{fmt}}".format(self.ixy_g_cr, fmt=fmt))
- table.add_row("Ixx_c_cr", "{:>{fmt}}".format(self.ixx_c_cr, fmt=fmt))
- table.add_row("Iyy_c_cr", "{:>{fmt}}".format(self.iyy_c_cr, fmt=fmt))
- table.add_row("Ixy_c_cr", "{:>{fmt}}".format(self.ixy_c_cr, fmt=fmt))
- table.add_row("Iuu_cr", "{:>{fmt}}".format(self.iuu_cr, fmt=fmt))
- table.add_row("I11_cr", "{:>{fmt}}".format(self.i11_cr, fmt=fmt))
- table.add_row("I22_cr", "{:>{fmt}}".format(self.i22_cr, fmt=fmt))
-
- table.add_row("E.Ixx_g_cr", "{:>{fmt}}".format(self.e_ixx_g_cr, fmt=fmt))
- table.add_row("E.Iyy_g_cr", "{:>{fmt}}".format(self.e_iyy_g_cr, fmt=fmt))
- table.add_row("E.Ixy_g_cr", "{:>{fmt}}".format(self.e_ixy_g_cr, fmt=fmt))
- table.add_row("E.Ixx_c_cr", "{:>{fmt}}".format(self.e_ixx_c_cr, fmt=fmt))
- table.add_row("E.Iyy_c_cr", "{:>{fmt}}".format(self.e_iyy_c_cr, fmt=fmt))
- table.add_row("E.Ixy_c_cr", "{:>{fmt}}".format(self.e_ixy_c_cr, fmt=fmt))
- table.add_row("E.Iuu_cr", "{:>{fmt}}".format(self.e_iuu_cr, fmt=fmt))
- table.add_row("E.I11_cr", "{:>{fmt}}".format(self.e_i11_cr, fmt=fmt))
- table.add_row("E.I22_cr", "{:>{fmt}}".format(self.e_i22_cr, fmt=fmt))
- table.add_row("phi_cr", "{:>{fmt}}".format(self.phi_cr, fmt=fmt))
+ table.add_row(
+ "m_cr",
+ string_formatter(
+ value=self.m_cr, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+
+ table.add_row(
+ "d_nc",
+ string_formatter(
+ value=self.d_nc, eng=eng, prec=prec, scale=units.length_scale
+ )
+ + units.length_unit,
+ )
+
+ if self.a_cr is not None:
+ table.add_row(
+ "A_cr",
+ string_formatter(
+ value=self.a_cr, eng=eng, prec=prec, scale=units.area_scale
+ )
+ + units.area_unit,
+ )
+
+ table.add_row(
+ "E.A_cr",
+ string_formatter(
+ value=self.e_a_cr, eng=eng, prec=prec, scale=units.force_scale
+ )
+ + units.force_unit,
+ end_section=True,
+ )
+
+ if self.qx_cr is not None and self.qy_cr is not None:
+ table.add_row(
+ "Qx_cr",
+ string_formatter(
+ value=self.qx_cr, eng=eng, prec=prec, scale=units.length_3_scale
+ )
+ + units.length_3_unit,
+ )
+ table.add_row(
+ "Qy_cr",
+ string_formatter(
+ value=self.qy_cr, eng=eng, prec=prec, scale=units.length_3_scale
+ )
+ + units.length_3_unit,
+ )
+
+ table.add_row(
+ "E.Qx_cr",
+ string_formatter(
+ value=self.e_qx_cr, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "E.Qy_cr",
+ string_formatter(
+ value=self.e_qy_cr, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "x-Centroid",
+ string_formatter(
+ value=self.cx, eng=eng, prec=prec, scale=units.length_scale
+ )
+ + units.length_unit,
+ )
+ table.add_row(
+ "y-Centroid",
+ string_formatter(
+ value=self.cy, eng=eng, prec=prec, scale=units.length_scale
+ )
+ + units.length_unit,
+ end_section=True,
+ )
+
+ if (
+ self.ixx_g_cr is not None
+ and self.iyy_g_cr is not None
+ and self.ixy_g_cr is not None
+ and self.ixx_c_cr is not None
+ and self.iyy_c_cr is not None
+ and self.ixy_c_cr is not None
+ and self.iuu_cr is not None
+ and self.i11_cr is not None
+ and self.i22_cr is not None
+ ):
+ table.add_row(
+ "Ixx_g_cr",
+ string_formatter(
+ value=self.ixx_g_cr, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "Iyy_g_cr",
+ string_formatter(
+ value=self.iyy_g_cr, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "Ixy_g_cr",
+ string_formatter(
+ value=self.ixy_g_cr, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "Ixx_c_cr",
+ string_formatter(
+ value=self.ixx_c_cr, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "Iyy_c_cr",
+ string_formatter(
+ value=self.iyy_c_cr, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "Ixy_c_cr",
+ string_formatter(
+ value=self.ixy_c_cr, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "Iuu_cr",
+ string_formatter(
+ value=self.iuu_cr, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "I11_cr",
+ string_formatter(
+ value=self.i11_cr, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ )
+ table.add_row(
+ "I22_cr",
+ string_formatter(
+ value=self.i22_cr, eng=eng, prec=prec, scale=units.length_4_scale
+ )
+ + units.length_4_unit,
+ end_section=True,
+ )
+
+ table.add_row(
+ "E.Ixx_g_cr",
+ string_formatter(
+ value=self.e_ixx_g_cr, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.Iyy_g_cr",
+ string_formatter(
+ value=self.e_iyy_g_cr, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.Ixy_g_cr",
+ string_formatter(
+ value=self.e_ixy_g_cr, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.Ixx_c_cr",
+ string_formatter(
+ value=self.e_ixx_c_cr, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.Iyy_c_cr",
+ string_formatter(
+ value=self.e_iyy_c_cr, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.Ixy_c_cr",
+ string_formatter(
+ value=self.e_ixy_c_cr, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.Iuu_cr",
+ string_formatter(
+ value=self.e_iuu_cr, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.I11_cr",
+ string_formatter(
+ value=self.e_i11_cr, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ )
+ table.add_row(
+ "E.I22_cr",
+ string_formatter(
+ value=self.e_i22_cr, eng=eng, prec=prec, scale=units.flex_rig_scale
+ )
+ + units.flex_rig_unit,
+ end_section=True,
+ )
+
+ table.add_row(
+ "phi_cr",
+ string_formatter(
+ value=self.phi_cr, eng=eng, prec=prec, scale=units.angle_scale
+ )
+ + units.angle_unit,
+ )
console = Console()
console.print(table)
@@ -447,6 +1059,7 @@ class MomentCurvatureResults:
r"""Class for storing moment curvature results.
Args:
+ default_units: Default units to use for reporting
theta: Angle (in radians) the neutral axis makes with the horizontal
n_target: Target axial force axis (:math:`-\pi \leq \theta \leq \pi`)
kappa: List of curvatures
@@ -461,6 +1074,9 @@ class MomentCurvatureResults:
indicates failure.
"""
+ # units
+ default_units: UnitDisplay
+
# results
theta: float
n_target: float
@@ -482,32 +1098,55 @@ class MomentCurvatureResults:
def plot_results(
self,
- m_scale: float = 1e-6,
fmt: str = "o-",
+ eng: bool = False,
+ prec: int = 2,
+ units: UnitDisplay | None = None,
**kwargs,
) -> matplotlib.axes.Axes:
"""Plots the moment curvature results.
Args:
- m_scale: Scaling factor to apply to bending moment. Defaults ``1e-6``.
fmt: Plot format string. Defaults ``"o-"``.
+ eng: If set to ``True``, formats the plot ticks with engineering notation.
+ If set to ``False``, uses the default ``matplotlib`` ticker formatting.
+ Defaults to ``False``.
+ prec: If ``eng=True``, sets the desired precision of the ticker formatting
+ (i.e. one plus this value is the desired number of digits). Defaults to
+ ``2``.
+ units: Unit system to display. Defaults to ``None``.
kwargs: Passed to :func:`~concreteproperties.post.plotting_context`
Returns:
Matplotlib axes object
"""
+ # assign default unit if no units provided
+ if units is None:
+ units = self.default_units
+
+ # check moment unit
+ moment_unit = "-" if units is DEFAULT_UNITS else units.moment_unit[1:]
+
# scale moments
- moments = np.array(self.m_xy) * m_scale
+ moments = np.array(self.m_xy) * units.moment_scale
# create plot and setup the plot
- with plotting_context(title="Moment-Curvature", **kwargs) as (fig, ax):
+ with plotting_context(title="Moment-Curvature", **kwargs) as (_, ax):
if ax is None:
msg = "Plot failed."
raise RuntimeError(msg)
ax.plot(self.kappa, moments, fmt)
- plt.xlabel("Curvature")
- plt.ylabel("Moment")
+
+ if eng:
+ tick_formatter = FuncFormatter(
+ lambda x, _: string_formatter_plots(value=x, prec=prec)
+ )
+ ax.xaxis.set_major_formatter(tick_formatter)
+ ax.yaxis.set_major_formatter(tick_formatter)
+
+ plt.xlabel("Curvature [-]")
+ plt.ylabel(f"Bending Moment [{moment_unit}]")
plt.grid(True)
return ax
@@ -516,8 +1155,10 @@ def plot_results(
def plot_multiple_results(
moment_curvature_results: list[MomentCurvatureResults],
labels: list[str],
- m_scale: float = 1e-6,
fmt: str = "o-",
+ eng: bool = False,
+ prec: int = 2,
+ units: UnitDisplay | None = None,
**kwargs,
) -> matplotlib.axes.Axes:
"""Plots multiple moment curvature results.
@@ -525,15 +1166,28 @@ def plot_multiple_results(
Args:
moment_curvature_results: List of moment curvature results objects
labels: List of labels for each moment curvature diagram
- m_scale: Scaling factor to apply to bending moment. Defaults ``1e-6``.
fmt: Plot format string. Defaults ``"o-"``.
+ eng: If set to ``True``, formats the plot ticks with engineering notation.
+ If set to ``False``, uses the default ``matplotlib`` ticker formatting.
+ Defaults to ``False``.
+ prec: If ``eng=True``, sets the desired precision of the ticker formatting
+ (i.e. one plus this value is the desired number of digits). Defaults to
+ ``2``.
+ units: Unit system to display. Defaults to ``None``.
kwargs: Passed to :func:`~concreteproperties.post.plotting_context`
Returns:
Matplotlib axes object
"""
- # create plot and setup the plot
- with plotting_context(title="Moment-Curvature", **kwargs) as (fig, ax):
+ # assign default unit if no units applied
+ if units is None:
+ units = DEFAULT_UNITS
+ moment_unit = "-"
+ else:
+ moment_unit = units.moment_unit[1:]
+
+ # create plot and setup the plots
+ with plotting_context(title="Moment-Curvature", **kwargs) as (_, ax):
if ax is None:
msg = "Plot failed."
raise RuntimeError(msg)
@@ -544,12 +1198,19 @@ def plot_multiple_results(
for idx, mk_result in enumerate(moment_curvature_results):
# scale results
kappas = np.array(mk_result.kappa)
- moments = np.array(mk_result.m_xy) * m_scale
+ moments = np.array(mk_result.m_xy) * units.moment_scale
ax.plot(kappas, moments, fmt, label=labels[idx])
- plt.xlabel("Curvature")
- plt.ylabel("Moment")
+ if eng:
+ tick_formatter = FuncFormatter(
+ lambda x, _: string_formatter_plots(value=x, prec=prec)
+ )
+ ax.xaxis.set_major_formatter(tick_formatter)
+ ax.yaxis.set_major_formatter(tick_formatter)
+
+ plt.xlabel("Curvature [-]")
+ plt.ylabel(f"Bending Moment [{moment_unit}]")
plt.grid(True)
# if there is more than one curve show legend
@@ -613,6 +1274,7 @@ class UltimateBendingResults:
r"""Class for storing ultimate bending results.
Args:
+ default_units: Default units to use for reporting
theta: Angle (in radians) the neutral axis makes with the horizontal axis
(:math:`-\pi \leq \theta \leq \pi`)
d_n: Ultimate neutral axis depth
@@ -624,6 +1286,9 @@ class UltimateBendingResults:
label: Result label
"""
+ # units
+ default_units: UnitDisplay
+
# bending angle
theta: float
@@ -642,29 +1307,77 @@ class UltimateBendingResults:
def print_results(
self,
- fmt: str = "8.6e",
+ eng: bool = True,
+ prec: int = 3,
+ units: UnitDisplay | None = None,
) -> None:
"""Prints the ultimate bending results to the terminal.
Args:
- fmt: Number format. Defaults to ``"8.6e"``.
+ eng: If set to ``True``, formats with engineering notation. If set to
+ ``False``, formats with fixed notation. Defaults to ``True``.
+ prec: The desired precision (i.e. one plus this value is the desired number
+ of digits). Defaults to ``3``.
+ units: Unit system to display. Defaults to ``None``.
"""
+ # setup table
table = Table(title="Ultimate Bending Results")
table.add_column("Property", justify="left", style="cyan", no_wrap=True)
table.add_column("Value", justify="right", style="green")
+ # assign default unit if no units provided
+ if units is None:
+ units = self.default_units
+
+ # add table rows
if self.label:
- table.add_row("Label", self.label)
+ table.add_row("Label", self.label, end_section=True)
- table.add_row("Bending Angle - theta", "{:>{fmt}}".format(self.theta, fmt=fmt))
- table.add_row("Neutral Axis Depth - d_n", "{:>{fmt}}".format(self.d_n, fmt=fmt))
table.add_row(
- "Neutral Axis Parameter - k_u", "{:>{fmt}}".format(self.k_u, fmt=fmt)
+ "Bending Angle - theta",
+ string_formatter(
+ value=self.theta, eng=eng, prec=prec, scale=units.angle_scale
+ )
+ + units.angle_unit,
+ )
+ table.add_row(
+ "Neutral Axis Depth - d_n",
+ string_formatter(
+ value=self.d_n, eng=eng, prec=prec, scale=units.length_scale
+ )
+ + units.length_unit,
+ )
+ table.add_row(
+ "Neutral Axis Parameter - k_u",
+ string_formatter(value=self.k_u, eng=eng, prec=prec),
+ end_section=True,
+ )
+ table.add_row(
+ "Axial Force",
+ string_formatter(value=self.n, eng=eng, prec=prec, scale=units.force_scale)
+ + units.force_unit,
+ )
+ table.add_row(
+ "Bending Capacity - m_x",
+ string_formatter(
+ value=self.m_x, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "Bending Capacity - m_y",
+ string_formatter(
+ value=self.m_y, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
+ )
+ table.add_row(
+ "Bending Capacity - m_xy",
+ string_formatter(
+ value=self.m_xy, eng=eng, prec=prec, scale=units.moment_scale
+ )
+ + units.moment_unit,
)
- table.add_row("Axial Force", "{:>{fmt}}".format(self.n, fmt=fmt))
- table.add_row("Bending Capacity - m_x", "{:>{fmt}}".format(self.m_x, fmt=fmt))
- table.add_row("Bending Capacity - m_y", "{:>{fmt}}".format(self.m_y, fmt=fmt))
- table.add_row("Bending Capacity - m_xy", "{:>{fmt}}".format(self.m_xy, fmt=fmt))
console = Console()
console.print(table)
@@ -675,9 +1388,13 @@ class MomentInteractionResults:
"""Class for storing moment interaction results.
Args:
+ default_units: Default units to use for reporting
results: List of ultimate bending result objects
"""
+ # units
+ default_units: UnitDisplay
+
results: list[UltimateBendingResults] = field(default_factory=list)
def sort_results(self) -> None:
@@ -731,20 +1448,18 @@ def get_results_lists(
def plot_diagram(
self,
- n_scale: float = 1e-3,
- m_scale: float = 1e-6,
moment: str = "m_x",
fmt: str = "o-",
labels: bool = False,
label_offset: bool = False,
+ eng: bool = False,
+ prec: int = 2,
+ units: UnitDisplay | None = None,
**kwargs,
) -> matplotlib.axes.Axes:
"""Plots a moment interaction diagram.
Args:
- n_scale: Scaling factor to apply to axial force. Defaults to ``1e-3``.
- m_scale: Scaling factor to apply to the bending moment. Defaults to
- ``1e-6``.
moment: Which moment to plot, acceptable values are ``"m_x"``, ``"m_y"`` or
``"m_xy"``. Defaults to ``"m_x"``.
fmt: Plot format string. Defaults to ``"o-"``.
@@ -752,16 +1467,32 @@ def plot_diagram(
``False``.
label_offset: If set to True, attempts to offset the label from the diagram.
Defaults to ``False``.
+ eng: If set to ``True``, formats the plot ticks with engineering notation.
+ If set to ``False``, uses the default ``matplotlib`` ticker formatting.
+ Defaults to ``False``.
+ prec: If ``eng=True``, sets the desired precision of the ticker formatting
+ (i.e. one plus this value is the desired number of digits). Defaults to
+ ``2``.
+ units: Unit system to display. Defaults to ``None``.
kwargs: Passed to :func:`~concreteproperties.post.plotting_context`
Returns:
Matplotlib axes object
"""
- # create plot and setup the plot
- with plotting_context(title="Moment Interaction Diagram", **kwargs) as (
- fig,
- ax,
- ):
+ # assign default unit if no units provided
+ if units is None:
+ units = self.default_units
+
+ # check moment/force unit
+ if units is DEFAULT_UNITS:
+ moment_unit = "-"
+ force_unit = "-"
+ else:
+ moment_unit = units.moment_unit[1:]
+ force_unit = units.force_unit[1:]
+
+ # create plot and setup the plots
+ with plotting_context(title="Moment Interaction Diagram", **kwargs) as (_, ax):
if ax is None:
msg = "Plot failed."
raise RuntimeError(msg)
@@ -770,8 +1501,8 @@ def plot_diagram(
n_list, m_list = self.get_results_lists(moment=moment)
# scale results
- forces = np.array(n_list) * n_scale
- moments = np.array(m_list) * m_scale
+ forces = np.array(n_list) * units.force_scale
+ moments = np.array(m_list) * units.moment_scale
# plot diagram
ax.plot(moments, forces, fmt)
@@ -788,8 +1519,8 @@ def plot_diagram(
for idx, m in enumerate(m_list):
if self.results[idx].label is not None:
# get x,y position on plot
- x = m * m_scale
- y = n_list[idx] * n_scale
+ x = m * units.moment_scale
+ y = n_list[idx] * units.force_scale
if label_offset:
# calculate text offset
@@ -820,8 +1551,15 @@ def plot_diagram(
**annotate_dict,
)
- plt.xlabel("Bending Moment")
- plt.ylabel("Axial Force")
+ if eng:
+ tick_formatter = FuncFormatter(
+ lambda x, _: string_formatter_plots(value=x, prec=prec)
+ )
+ ax.xaxis.set_major_formatter(tick_formatter)
+ ax.yaxis.set_major_formatter(tick_formatter)
+
+ plt.xlabel(f"Bending Moment [{moment_unit}]")
+ plt.ylabel(f"Axial Force [{force_unit}]")
plt.grid(True)
return ax
@@ -830,10 +1568,11 @@ def plot_diagram(
def plot_multiple_diagrams(
moment_interaction_results: list[MomentInteractionResults],
labels: list[str],
- n_scale: float = 1e-3,
- m_scale: float = 1e-6,
moment: str = "m_x",
fmt: str = "o-",
+ eng: bool = False,
+ prec: int = 2,
+ units: UnitDisplay | None = None,
**kwargs,
) -> matplotlib.axes.Axes:
"""Plots multiple moment interaction diagrams.
@@ -841,21 +1580,32 @@ def plot_multiple_diagrams(
Args:
moment_interaction_results: List of moment interaction results objects
labels: List of labels for each moment interaction diagram.
- n_scale: Scaling factor to apply to axial force. Defaults to ``1e-3``.
- m_scale: Scaling factor to apply to bending moment. Defaults to ``1e-6``.
moment: Which moment to plot, acceptable values are ``"m_x"``, ``"m_y"`` or
``"m_xy"``. Defaults to ``"m_x"``.
fmt: Plot format string. Defaults to ``"o-"``.
+ eng: If set to ``True``, formats the plot ticks with engineering notation.
+ If set to ``False``, uses the default ``matplotlib`` ticker formatting.
+ Defaults to ``False``.
+ prec: If ``eng=True``, sets the desired precision of the ticker formatting
+ (i.e. one plus this value is the desired number of digits). Defaults to
+ ``2``.
+ units: Unit system to display. Defaults to ``None``.
kwargs: Passed to :func:`~concreteproperties.post.plotting_context`
Returns:
Matplotlib axes object
"""
- # create plot and setup the plot
- with plotting_context(title="Moment Interaction Diagram", **kwargs) as (
- fig,
- ax,
- ):
+ # assign default unit if no units applied
+ if units is None:
+ units = DEFAULT_UNITS
+ force_unit = "-"
+ moment_unit = "-"
+ else:
+ force_unit = units.force_unit[1:]
+ moment_unit = units.moment_unit[1:]
+
+ # create plot and setup the plots
+ with plotting_context(title="Moment Interaction Diagram", **kwargs) as (_, ax):
if ax is None:
msg = "Plot failed."
raise RuntimeError(msg)
@@ -867,13 +1617,23 @@ def plot_multiple_diagrams(
n_list, m_list = mi_result.get_results_lists(moment=moment)
# scale results
- forces = np.array(n_list) * n_scale
- moments = np.array(m_list) * m_scale
+ forces = np.array(n_list) * units.force_scale
+ moments = np.array(m_list) * units.moment_scale
ax.plot(moments, forces, fmt, label=labels[idx])
- plt.xlabel("Bending Moment")
- plt.ylabel("Axial Force")
+ if eng:
+ x_formatter = FuncFormatter(
+ lambda x, _: string_formatter_plots(value=x, prec=prec)
+ )
+ y_formatter = FuncFormatter(
+ lambda y, _: string_formatter_plots(value=y, prec=prec)
+ )
+ ax.xaxis.set_major_formatter(x_formatter)
+ ax.yaxis.set_major_formatter(y_formatter)
+
+ plt.xlabel(f"Bending Moment [{moment_unit}]")
+ plt.ylabel(f"Axial Force [{force_unit}]")
plt.grid(True)
# if there is more than one curve show legend
@@ -919,10 +1679,14 @@ class BiaxialBendingResults:
"""Class for storing biaxial bending results.
Args:
+ default_units: Default units to use for reporting
n: Net axial force
results: List of ultimate bending result objects
"""
+ # units
+ default_units: UnitDisplay
+
n: float
results: list[UltimateBendingResults] = field(default_factory=list)
@@ -946,38 +1710,72 @@ def get_results_lists(
def plot_diagram(
self,
- m_scale: float = 1e-6,
fmt: str = "o-",
+ eng: bool = False,
+ prec: int = 2,
+ units: UnitDisplay | None = None,
**kwargs,
) -> matplotlib.axes.Axes:
"""Plots a biaxial bending diagram.
Args:
- m_scale: Scaling factor to apply to bending moment. Defaults to ``1e-6``.
fmt: Plot format string. Defaults to ``"o-"``.
+ eng: If set to ``True``, formats the plot ticks with engineering notation.
+ If set to ``False``, uses the default ``matplotlib`` ticker formatting.
+ Defaults to ``False``.
+ prec: If ``eng=True``, sets the desired precision of the ticker formatting
+ (i.e. one plus this value is the desired number of digits). Defaults to
+ ``2``.
+ units: Unit system to display. Defaults to ``None``.
kwargs: Passed to :func:`~concreteproperties.post.plotting_context`
Returns:
Matplotlib axes object
"""
+ # assign default unit if no units provided
+ if units is None:
+ units = self.default_units
+
+ # check moment/force unit
+ if units is DEFAULT_UNITS:
+ moment_unit = "-"
+ force_unit = "-"
+ else:
+ moment_unit = units.moment_unit[1:]
+ force_unit = units.force_unit[1:]
+
+ # get biaxial results
m_x_list, m_y_list = self.get_results_lists()
- # create plot and setup the plot
+ # format axial force string
+ if eng:
+ n_str = string_formatter_plots(value=self.n * units.force_scale, prec=prec)
+ else:
+ n_str = f"{self.n * units.force_scale:.2e}"
+
+ # create plot and setup the plots
with plotting_context(
- title=f"Biaxial Bending Diagram, $N = {self.n:.3e}$", **kwargs
+ title=f"Biaxial Bending Diagram, N = {n_str} {force_unit}", **kwargs
) as (fig, ax):
if ax is None:
msg = "Plot failed."
raise RuntimeError(msg)
# scale results
- m_x = np.array(m_x_list) * m_scale
- m_y = np.array(m_y_list) * m_scale
+ m_x = np.array(m_x_list) * units.moment_scale
+ m_y = np.array(m_y_list) * units.moment_scale
ax.plot(m_x, m_y, fmt)
- plt.xlabel("Bending Moment $M_x$")
- plt.ylabel("Bending Moment $M_y$")
+ if eng:
+ tick_formatter = FuncFormatter(
+ lambda x, _: string_formatter_plots(value=x, prec=prec)
+ )
+ ax.xaxis.set_major_formatter(tick_formatter)
+ ax.yaxis.set_major_formatter(tick_formatter)
+
+ plt.xlabel("Bending Moment $M_x$" + f" [{moment_unit}]")
+ plt.ylabel("Bending Moment $M_y$" + f" [{moment_unit}]")
plt.grid(True)
return ax
@@ -986,8 +1784,10 @@ def plot_diagram(
def plot_multiple_diagrams_2d(
biaxial_bending_results: list[BiaxialBendingResults],
labels: list[str] | None = None,
- m_scale: float = 1e-6,
fmt: str = "o-",
+ eng: bool = False,
+ prec: int = 2,
+ units: UnitDisplay | None = None,
**kwargs,
) -> matplotlib.axes.Axes:
"""Plots multiple biaxial bending diagrams in a 2D plot.
@@ -996,15 +1796,32 @@ def plot_multiple_diagrams_2d(
biaxial_bending_results: List of biaxial bending results objects
labels: List of labels for each biaxial bending diagram, if not provided
labels are axial forces. Defaults to ``None``.
- m_scale: Scaling factor to apply to bending moment. Defaults to ``1e-6``.
fmt: Plot format string. Defaults to ``"o-"``.
+ eng: If set to ``True``, formats the plot ticks with engineering notation.
+ If set to ``False``, uses the default ``matplotlib`` ticker formatting.
+ Defaults to ``False``.
+ prec: If ``eng=True``, sets the desired precision of the ticker formatting
+ (i.e. one plus this value is the desired number of digits). Defaults to
+ ``2``.
+ units: Unit system to display. Defaults to ``None``.
kwargs: Passed to :func:`~concreteproperties.post.plotting_context`
Returns:
Matplotlib axes object
"""
- # create plot and setup the plot
- with plotting_context(title="Biaxial Bending Diagram", **kwargs) as (fig, ax):
+ # assign default unit if no units applied
+ if units is None:
+ units = DEFAULT_UNITS
+ force_unit = ""
+ moment_unit = "-"
+ else:
+ force_unit = units.force_unit[1:]
+ moment_unit = units.moment_unit[1:]
+
+ # create plot and setup the plots
+ with plotting_context(
+ title="Biaxial Bending Diagram", aspect=True, **kwargs
+ ) as (_, ax):
if ax is None:
msg = "Plot failed."
raise RuntimeError(msg)
@@ -1023,17 +1840,31 @@ def plot_multiple_diagrams_2d(
m_x_list, m_y_list = bb_result.get_results_lists()
# scale results
- m_x_list = np.array(m_x_list) * m_scale
- m_y_list = np.array(m_y_list) * m_scale
+ m_x_list = np.array(m_x_list) * units.moment_scale
+ m_y_list = np.array(m_y_list) * units.moment_scale
# generate default labels
if default_labels:
- labels.append(f"N = {bb_result.n:.3e}")
+ if eng:
+ n_str = string_formatter_plots(
+ value=bb_result.n * units.force_scale, prec=prec
+ )
+ else:
+ n_str = f"{bb_result.n * units.force_scale:.3e}"
+
+ labels.append(f"N = {n_str} {force_unit}")
ax.plot(m_x_list, m_y_list, fmt, label=labels[idx])
- plt.xlabel("Bending Moment $M_x$")
- plt.ylabel("Bending Moment $M_y$")
+ if eng:
+ tick_formatter = FuncFormatter(
+ lambda x, _: string_formatter_plots(value=x, prec=prec)
+ )
+ ax.xaxis.set_major_formatter(tick_formatter)
+ ax.yaxis.set_major_formatter(tick_formatter)
+
+ plt.xlabel("Bending Moment $M_x$" + f" [{moment_unit}]")
+ plt.ylabel("Bending Moment $M_y$" + f" [{moment_unit}]")
plt.grid(True)
# if there is more than one curve show legend
@@ -1045,22 +1876,37 @@ def plot_multiple_diagrams_2d(
@staticmethod
def plot_multiple_diagrams_3d(
biaxial_bending_results: list[BiaxialBendingResults],
- n_scale: float = 1e-3,
- m_scale: float = 1e-6,
fmt: str = "-",
+ eng: bool = False,
+ prec: int = 2,
+ units: UnitDisplay | None = None,
) -> matplotlib.axes.Axes:
"""Plots multiple biaxial bending diagrams in a 3D plot.
Args:
biaxial_bending_results: List of biaxial bending results objects
- n_scale: Scaling factor to apply to axial force. Defaults to ``1e-3``.
- m_scale: Scaling factor to apply to bending moment. Defaults to ``1e-6``.
fmt: Plot format string. Defaults to ``"-"``.
+ eng: If set to ``True``, formats the plot ticks with engineering notation.
+ If set to ``False``, uses the default ``matplotlib`` ticker formatting.
+ Defaults to ``False``.
+ prec: If ``eng=True``, sets the desired precision of the ticker formatting
+ (i.e. one plus this value is the desired number of digits). Defaults to
+ ``2``.
+ units: Unit system to display. Defaults to ``None``.
Returns:
Matplotlib axes object
"""
- # make 3d plot
+ # assign default unit if no units applied
+ if units is None:
+ units = DEFAULT_UNITS
+ force_unit = ""
+ moment_unit = "-"
+ else:
+ force_unit = units.force_unit[1:]
+ moment_unit = units.moment_unit[1:]
+
+ # make 3d plots
plt.figure()
ax = plt.axes(projection="3d")
@@ -1069,15 +1915,23 @@ def plot_multiple_diagrams_3d(
m_x_list, m_y_list = bb_result.get_results_lists()
# scale results
- n_list = bb_result.n * n_scale * np.ones(len(m_x_list))
- m_x_list = np.array(m_x_list) * m_scale
- m_y_list = np.array(m_y_list) * m_scale
+ n_list = bb_result.n * units.force_scale * np.ones(len(m_x_list))
+ m_x_list = np.array(m_x_list) * units.moment_scale
+ m_y_list = np.array(m_y_list) * units.moment_scale
ax.plot3D(m_x_list, m_y_list, n_list, fmt) # pyright: ignore
- ax.set_xlabel("Bending Moment $M_x$")
- ax.set_ylabel("Bending Moment $M_y$")
- ax.set_zlabel("Axial Force $N$") # pyright: ignore
+ if eng:
+ tick_formatter = FuncFormatter(
+ lambda x, _: string_formatter_plots(value=x, prec=prec)
+ )
+ ax.xaxis.set_major_formatter(tick_formatter)
+ ax.yaxis.set_major_formatter(tick_formatter)
+ ax.zaxis.set_major_formatter(tick_formatter) # pyright: ignore
+
+ plt.xlabel("Bending Moment $M_x$" + f" [{moment_unit}]")
+ plt.ylabel("Bending Moment $M_y$" + f" [{moment_unit}]")
+ ax.set_zlabel("Axial Force $N$" + f" [{force_unit}]") # pyright: ignore
plt.show()
return ax
@@ -1111,6 +1965,7 @@ class StressResult:
The lever arm is computed to the elastic centroid.
Args:
+ default_units: Default units to use for reporting
concrete_analysis_sections: List of concrete analysis section objects
present in the stress analysis, which can be visualised by calling the
:meth:`~concreteproperties.analysis_section.AnalysisSection.plot_mesh` or
@@ -1141,6 +1996,9 @@ class StressResult:
(``force``, ``d_x``, ``d_y``)
"""
+ # units
+ default_units: UnitDisplay
+
concrete_section: ConcreteSection
concrete_analysis_sections: list[AnalysisSection]
concrete_stresses: list[np.ndarray]
@@ -1163,6 +2021,9 @@ def plot_stress(
title: str = "Stress",
conc_cmap: str = "RdGy",
reinf_cmap: str = "bwr",
+ eng: bool = False,
+ prec: int = 2,
+ units: UnitDisplay | None = None,
**kwargs,
) -> matplotlib.axes.Axes:
"""Plots concrete and steel stresses on a concrete section.
@@ -1171,11 +2032,30 @@ def plot_stress(
title: Plot title. Defaults to ``"Stress"``.
conc_cmap: Colour map for the concrete stress. Defaults to ``"RdGy"``.
reinf_cmap: Colour map for the reinforcement stress. Defaults to ``"bwr"``.
+ eng: If set to ``True``, formats with engineering notation. If set to
+ ``False``, formats with fixed notation. Defaults to ``False``.
+ prec: The desired precision (i.e. one plus this value is the desired number
+ of digits). Defaults to ``2``.
+ units: Unit system to display. Defaults to ``None``.
kwargs: Passed to :func:`~concreteproperties.post.plotting_context`
Returns:
Matplotlib axes object
"""
+ # assign default unit if no units provided
+ if units is None:
+ units = self.default_units
+
+ # check stress unit
+ stress_unit = "-" if units is DEFAULT_UNITS else units.stress_unit[1:]
+
+ # assign default unit if no units applied
+ if units is None:
+ units = DEFAULT_UNITS
+ stress_unit = "-"
+ else:
+ stress_unit = units.stress_unit[1:]
+
with plotting_context(
title=title,
aspect=True,
@@ -1199,16 +2079,26 @@ def plot_stress(
# determine minimum and maximum stress values for the contour list
# add tolerance for plotting stress blocks
- conc_sig_min = min([min(x) for x in self.concrete_stresses]) - 1e-12
- conc_sig_max = max([max(x) for x in self.concrete_stresses]) + 1e-12
+ conc_sig_min = (
+ min([min(x) for x in self.concrete_stresses]) * units.stress_scale
+ - 1e-12
+ )
+ conc_sig_max = (
+ max([max(x) for x in self.concrete_stresses]) * units.stress_scale
+ + 1e-12
+ )
# if there is meshed reinforcement, calculate min and max
if self.meshed_reinforcement_stresses:
meshed_reinf_sig_min = (
- min([min(x) for x in self.meshed_reinforcement_stresses]) - 1e-12
+ min([min(x) for x in self.meshed_reinforcement_stresses])
+ * units.stress_scale
+ - 1e-12
)
meshed_reinf_sig_max = (
- max([max(x) for x in self.meshed_reinforcement_stresses]) + 1e-12
+ max([max(x) for x in self.meshed_reinforcement_stresses])
+ * units.stress_scale
+ + 1e-12
)
else:
meshed_reinf_sig_min = None
@@ -1216,11 +2106,13 @@ def plot_stress(
# if there is lumped reinforcement, calculate min and max
if self.lumped_reinforcement_stresses or self.strand_stresses:
- lumped_reinf_sig_min = min(
- self.lumped_reinforcement_stresses + self.strand_stresses
+ lumped_reinf_sig_min = (
+ min(self.lumped_reinforcement_stresses + self.strand_stresses)
+ * units.stress_scale
)
- lumped_reinf_sig_max = max(
- self.lumped_reinforcement_stresses + self.strand_stresses
+ lumped_reinf_sig_max = (
+ max(self.lumped_reinforcement_stresses + self.strand_stresses)
+ * units.stress_scale
)
else:
lumped_reinf_sig_min = None
@@ -1228,17 +2120,17 @@ def plot_stress(
# determine min and max reinforcement stresess
if (
- meshed_reinf_sig_min
- and meshed_reinf_sig_max
- and lumped_reinf_sig_min
- and lumped_reinf_sig_max
+ meshed_reinf_sig_min is not None
+ and meshed_reinf_sig_max is not None
+ and lumped_reinf_sig_min is not None
+ and lumped_reinf_sig_max is not None
):
reinf_sig_min = min(meshed_reinf_sig_min, lumped_reinf_sig_min)
reinf_sig_max = max(meshed_reinf_sig_max, lumped_reinf_sig_max)
- elif meshed_reinf_sig_min and meshed_reinf_sig_max:
+ elif meshed_reinf_sig_min is not None and meshed_reinf_sig_max is not None:
reinf_sig_min = meshed_reinf_sig_min
reinf_sig_max = meshed_reinf_sig_max
- elif lumped_reinf_sig_min and lumped_reinf_sig_max:
+ elif lumped_reinf_sig_min is not None and lumped_reinf_sig_max is not None:
reinf_sig_min = lumped_reinf_sig_min
reinf_sig_max = lumped_reinf_sig_max
else:
@@ -1273,6 +2165,9 @@ def plot_stress(
self.concrete_analysis_sections[idx].mesh_elements[:, 0:3], # pyright: ignore
)
+ # scale stress
+ sig = sig * units.stress_scale
+
# plot the filled contour
trictr_conc = fig.axes[0].tricontourf(
triang_conc, sig, v_conc, cmap=cmap_conc, norm=CenteredNorm()
@@ -1319,6 +2214,9 @@ def plot_stress(
self.meshed_reinforcement_sections[idx].mesh_elements[:, 0:3], # pyright: ignore
)
+ # scale stress
+ sig = sig * units.stress_scale
+
# plot the filled contour
trictr_reinf = fig.axes[0].tricontourf(
triang_reinf, sig, v_reinf, cmap=cmap_reinf, norm=CenteredNorm()
@@ -1357,6 +2255,9 @@ def plot_stress(
colours = []
for idx, sig in enumerate(self.lumped_reinforcement_stresses):
+ # scale stress
+ sig = sig * units.stress_scale
+
lumped_geom = self.lumped_reinforcement_geometries[idx].geom
lumped_reinf_patches.append(
mpatches.Polygon(xy=list(lumped_geom.exterior.coords))
@@ -1364,6 +2265,9 @@ def plot_stress(
colours.append(sig)
for idx, sig in enumerate(self.strand_stresses):
+ # scale stress
+ sig = sig * units.stress_scale
+
lumped_reinf_patches.append(
mpatches.Polygon(
xy=list(self.strand_geometries[idx].geom.exterior.coords)
@@ -1373,17 +2277,24 @@ def plot_stress(
patch = PatchCollection(lumped_reinf_patches, cmap=cmap_reinf)
patch.set_array(colours)
+
if reinf_tick_same:
patch.set_clim(vmin=0.99 * v_reinf[0], vmax=1.01 * v_reinf[-1])
else:
patch.set_clim(vmin=v_reinf[0], vmax=v_reinf[-1])
+
fig.axes[0].add_collection(patch)
+ # set the tick formatter
+ tick_formatter = FuncFormatter(
+ lambda x, _: string_formatter_stress(value=x, eng=eng, prec=prec)
+ )
+
# add the colour bars
fig.colorbar(
trictr_conc, # pyright: ignore
- label="Concrete Stress",
- format="%.2e",
+ label="Concrete Stress" + f" [{stress_unit}]",
+ format=tick_formatter,
ticks=ticks_conc,
cax=fig.axes[1],
)
@@ -1392,8 +2303,8 @@ def plot_stress(
fig.colorbar(
mappable,
- label="Reinforcement Stress",
- format="%.2e",
+ label="Reinforcement Stress" + f" [{stress_unit}]",
+ format=tick_formatter,
ticks=ticks_reinf,
cax=fig.axes[2],
)
diff --git a/src/concreteproperties/stress_strain_profile.py b/src/concreteproperties/stress_strain_profile.py
index 588b06b5..287634b6 100644
--- a/src/concreteproperties/stress_strain_profile.py
+++ b/src/concreteproperties/stress_strain_profile.py
@@ -8,16 +8,23 @@
import matplotlib.pyplot as plt
import numpy as np
+from matplotlib.ticker import FuncFormatter
from rich.console import Console
from rich.table import Table
from scipy.interpolate import interp1d
from scipy.optimize import brentq
-from concreteproperties.post import plotting_context
+from concreteproperties.post import (
+ DEFAULT_UNITS,
+ plotting_context,
+ string_formatter_plots,
+)
if TYPE_CHECKING:
import matplotlib.axes
+ from concreteproperties.post import UnitDisplay
+
@dataclass
class StressStrainProfile:
@@ -214,6 +221,9 @@ def plot_stress_strain(
self,
title: str = "Stress-Strain Profile",
fmt: str = "o-",
+ eng: bool = False,
+ prec: int = 2,
+ units: UnitDisplay | None = None,
**kwargs,
) -> matplotlib.axes.Axes:
"""Plots the stress-strain profile.
@@ -221,20 +231,45 @@ def plot_stress_strain(
Args:
title: Plot title. Defaults to ``"Stress-Strain Profile"``.
fmt: Plot format string. Defaults to ``"o-"``.
+ eng: If set to ``True``, formats the plot ticks with engineering notation.
+ If set to ``False``, uses the default ``matplotlib`` ticker formatting.
+ Defaults to ``False``.
+ prec: If ``eng=True``, sets the desired precision of the ticker formatting
+ (i.e. one plus this value is the desired number of digits). Defaults to
+ ``2``.
+ units: Unit system to display. Defaults to ``None``.
kwargs: Passed to :func:`~concreteproperties.post.plotting_context`
Returns:
Matplotlib axes object
"""
+ # assign default unit if no units applied
+ if units is None:
+ units = DEFAULT_UNITS
+ stress_unit = "-"
+ else:
+ stress_unit = units.stress_unit[1:]
+
# create plot and setup the plot
with plotting_context(title=title, **kwargs) as (_, ax):
if ax is None:
msg = "Plot failed."
raise RuntimeError(msg)
- ax.plot(self.strains, self.stresses, fmt)
- plt.xlabel("Strain")
- plt.ylabel("Stress")
+ # scale stresses
+ stresses = np.array(self.stresses) * units.stress_scale
+
+ ax.plot(self.strains, stresses, fmt)
+
+ if eng:
+ tick_formatter = FuncFormatter(
+ lambda x, _: string_formatter_plots(value=x, prec=prec)
+ )
+ ax.xaxis.set_major_formatter(tick_formatter)
+ ax.yaxis.set_major_formatter(tick_formatter)
+
+ plt.xlabel("Strain [-]")
+ plt.ylabel(f"Stress [{stress_unit}]")
plt.grid(True)
return ax
diff --git a/tests/test_moment_interaction.py b/tests/test_moment_interaction.py
index 38321122..dbf2d574 100644
--- a/tests/test_moment_interaction.py
+++ b/tests/test_moment_interaction.py
@@ -8,6 +8,7 @@
import concreteproperties.utils as utils
from concreteproperties.concrete_section import ConcreteSection
from concreteproperties.material import Concrete, SteelBar
+from concreteproperties.post import DEFAULT_UNITS
from concreteproperties.stress_strain_profile import (
ConcreteLinear,
RectangularStressBlock,
@@ -118,7 +119,9 @@ def test_limits(limits):
limit_results = [
conc_sec.calculate_ultimate_section_actions(
d_n=conc_sec.decode_d_n(theta=0, cp=lim, d_t=d_t),
- ultimate_results=res.UltimateBendingResults(theta=theta),
+ ultimate_results=res.UltimateBendingResults(
+ default_units=DEFAULT_UNITS, theta=theta
+ ),
)
for lim in limits
]
diff --git a/tests/test_post.py b/tests/test_post.py
new file mode 100644
index 00000000..72e74a9b
--- /dev/null
+++ b/tests/test_post.py
@@ -0,0 +1,189 @@
+"""Tests the post methods."""
+
+import math
+import platform
+
+import pytest
+from sectionproperties.pre.library import concrete_rectangular_section
+
+from concreteproperties import (
+ Concrete,
+ ConcreteLinear,
+ ConcreteSection,
+ RectangularStressBlock,
+ SteelBar,
+ SteelElasticPlastic,
+)
+from concreteproperties.post import DEFAULT_UNITS, si_kn_m, si_n_mm, string_formatter
+
+linux_only = pytest.mark.skipif(
+ platform.system() != "Linux",
+ reason="Only test plotting on Linux",
+)
+
+
+@pytest.fixture
+def concrete_section() -> ConcreteSection:
+ """Generates a simple ConcreteSection object.
+
+ Returns:
+ Geometry
+ """
+ concrete = Concrete(
+ name="32 MPa Concrete",
+ density=2.4e-6,
+ stress_strain_profile=ConcreteLinear(elastic_modulus=30.1e3),
+ ultimate_stress_strain_profile=RectangularStressBlock(
+ compressive_strength=32,
+ alpha=0.802,
+ gamma=0.89,
+ ultimate_strain=0.003,
+ ),
+ flexural_tensile_strength=3.4,
+ colour="lightgrey",
+ )
+ steel = SteelBar(
+ name="500 MPa Steel",
+ density=7.85e-6,
+ stress_strain_profile=SteelElasticPlastic(
+ yield_strength=500,
+ elastic_modulus=200e3,
+ fracture_strain=0.05,
+ ),
+ colour="grey",
+ )
+ geom = concrete_rectangular_section(
+ d=300,
+ b=300,
+ dia_top=20,
+ area_top=310,
+ n_top=2,
+ c_top=30,
+ dia_bot=24,
+ area_bot=450,
+ n_bot=2,
+ c_bot=30,
+ conc_mat=concrete,
+ steel_mat=steel,
+ )
+ return ConcreteSection(geom)
+
+
+def test_string_formatter():
+ """Tests the string formatter."""
+ assert string_formatter(value=2704.897111, eng=False, prec=3) == "2704.897"
+ assert string_formatter(value=2704.897111, eng=False, prec=0) == "2705"
+ assert string_formatter(value=2704.897111, eng=False, prec=10) == "2704.8971110000"
+ assert string_formatter(value=0, eng=True, prec=2) == "0.00"
+ assert string_formatter(value=2704.897111, eng=True, prec=4) == "2.7049 x 10^3"
+ assert string_formatter(value=0.0034563, eng=True, prec=2) == "3.46 x 10^-3"
+ assert string_formatter(value=0.034563, eng=True, prec=3) == "34.56 x 10^-3"
+ assert string_formatter(value=15, eng=True, prec=2) == "15.0"
+ assert string_formatter(value=14435.654, eng=True, prec=4) == "14.436 x 10^3"
+ assert (
+ string_formatter(value=14435.654, eng=True, prec=4, scale=10) == "144.36 x 10^3"
+ )
+ assert (
+ string_formatter(value=14435.654, eng=True, prec=3, scale=1000)
+ == "14.44 x 10^6"
+ )
+ assert string_formatter(value=14435.654, eng=True, prec=3, scale=1e-3) == "14.44"
+
+
+def test_unit_display():
+ """Tests the unit display class."""
+ assert si_n_mm.length_unit == " mm"
+ assert si_kn_m.length_unit == " m"
+ assert si_n_mm.length_scale == pytest.approx(1)
+ assert si_kn_m.length_scale == pytest.approx(0.001)
+ assert si_n_mm.force_unit == " N"
+ assert si_kn_m.force_unit == " kN"
+ assert si_n_mm.force_scale == pytest.approx(1)
+ assert si_kn_m.force_scale == pytest.approx(0.001)
+ assert si_n_mm.mass_unit == " kg"
+ assert si_kn_m.mass_unit == " kg"
+ assert si_n_mm.mass_scale == pytest.approx(1)
+ assert si_kn_m.mass_scale == pytest.approx(1)
+ assert si_n_mm.angle_unit == " rads"
+ assert si_kn_m.angle_unit == " rads"
+ assert si_n_mm.angle_scale == pytest.approx(1)
+ assert si_kn_m.angle_scale == pytest.approx(1)
+ si_n_mm.radians = False
+ assert si_n_mm.angle_unit == " degs"
+ assert si_n_mm.angle_scale == pytest.approx(180 / math.pi)
+ assert si_n_mm.area_unit == " mm^2"
+ assert si_kn_m.area_unit == " m^2"
+ assert si_n_mm.area_scale == pytest.approx(1)
+ assert si_kn_m.area_scale == pytest.approx(1e-6)
+ assert si_n_mm.mass_per_length_unit == " kg/mm"
+ assert si_kn_m.mass_per_length_unit == " kg/m"
+ assert si_n_mm.mass_per_length_scale == pytest.approx(1)
+ assert si_kn_m.mass_per_length_scale == pytest.approx(1e3)
+ assert si_n_mm.moment_unit == " N.mm"
+ assert si_kn_m.moment_unit == " kN.m"
+ assert si_n_mm.moment_scale == pytest.approx(1)
+ assert si_kn_m.moment_scale == pytest.approx(1e-6)
+ assert si_n_mm.flex_rig_unit == " N.mm^2"
+ assert si_kn_m.flex_rig_unit == " kN.m^2"
+ assert si_n_mm.flex_rig_scale == pytest.approx(1)
+ assert si_kn_m.flex_rig_scale == pytest.approx(1e-9)
+ assert si_n_mm.stress_unit == " MPa"
+ assert si_kn_m.stress_unit == " kPa"
+ assert si_n_mm.stress_scale == pytest.approx(1)
+ assert si_kn_m.stress_scale == pytest.approx(1e3)
+ si_n_mm.length = "um"
+ assert si_n_mm.stress_unit == " N/um^2"
+ si_n_mm.length = "mm"
+ assert si_n_mm.length_3_unit == " mm^3"
+ assert si_kn_m.length_3_unit == " m^3"
+ assert si_n_mm.length_3_scale == pytest.approx(1)
+ assert si_kn_m.length_3_scale == pytest.approx(1e-9)
+ assert si_n_mm.length_4_unit == " mm^4"
+ assert si_kn_m.length_4_unit == " m^4"
+ assert si_n_mm.length_4_scale == pytest.approx(1)
+ assert si_kn_m.length_4_scale == pytest.approx(1e-12)
+
+
+def test_print(concrete_section: ConcreteSection):
+ """Tests printing results to terminal."""
+ conc_sec = concrete_section
+ gross_props = conc_sec.get_gross_properties()
+ gross_props.print_results()
+ gross_props.print_results(units=DEFAULT_UNITS)
+ gross_props.print_results(units=si_n_mm)
+ gross_props.print_results(units=si_kn_m)
+ transformed_props = conc_sec.get_transformed_gross_properties(
+ elastic_modulus=30.1e3
+ )
+ transformed_props.print_results()
+ cracked_res = conc_sec.calculate_cracked_properties()
+ cracked_res.print_results()
+ cracked_res.calculate_transformed_properties(elastic_modulus=32.8e3)
+ cracked_res.print_results()
+ ult_res = conc_sec.ultimate_bending_capacity()
+ ult_res.print_results()
+ si_n_mm.radians = False # display angles in degrees
+ ult_res.print_results(units=si_n_mm)
+
+
+@linux_only
+def test_plot(concrete_section: ConcreteSection):
+ """Tests plotting results."""
+ conc_sec = concrete_section
+
+ # mk
+ mk = conc_sec.moment_curvature_analysis(kappa_inc=1e-6, progress_bar=False)
+ mk.plot_results()
+ mk.plot_failure_geometry()
+ mk.plot_multiple_results([mk, mk], ["1", "2"])
+
+ # mi
+ mi = conc_sec.moment_interaction_diagram()
+ mi.plot_diagram()
+ mi.plot_multiple_diagrams([mi, mi], ["1", "2"])
+
+ # bbd
+ bbd = conc_sec.biaxial_bending_diagram(n=16)
+ bbd.plot_diagram()
+ bbd.plot_multiple_diagrams_2d([bbd, bbd])
+ bbd.plot_multiple_diagrams_3d([bbd, bbd])
diff --git a/uv.lock b/uv.lock
index 9fff1e4d..c7c2b19f 100644
--- a/uv.lock
+++ b/uv.lock
@@ -265,6 +265,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", size = 49446 },
]
+[[package]]
+name = "click"
+version = "8.1.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "platform_system == 'Windows'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 },
+]
+
[[package]]
name = "colorama"
version = "0.4.6"
@@ -295,13 +307,14 @@ dependencies = [
{ name = "matplotlib" },
{ name = "more-itertools" },
{ name = "numpy" },
+ { name = "quantiphy" },
{ name = "rich", extra = ["jupyter"] },
{ name = "scipy" },
{ name = "sectionproperties" },
{ name = "shapely" },
]
-[package.dependency-groups]
+[package.dev-dependencies]
docs = [
{ name = "furo" },
{ name = "ipykernel" },
@@ -310,6 +323,7 @@ docs = [
{ name = "nbsphinx" },
{ name = "notebook" },
{ name = "sphinx" },
+ { name = "sphinx-autobuild" },
{ name = "sphinx-copybutton" },
{ name = "sphinxext-opengraph" },
]
@@ -330,13 +344,14 @@ requires-dist = [
{ name = "matplotlib", specifier = "~=3.9.2" },
{ name = "more-itertools", specifier = "~=10.5.0" },
{ name = "numpy", specifier = "~=1.26.4" },
+ { name = "quantiphy", specifier = "~=2.20" },
{ name = "rich", extras = ["jupyter"], specifier = "~=13.9.3" },
{ name = "scipy", specifier = "~=1.14.1" },
{ name = "sectionproperties", specifier = "~=3.5.0" },
{ name = "shapely", specifier = "~=2.0.6" },
]
-[package.metadata.dependency-groups]
+[package.metadata.requires-dev]
docs = [
{ name = "furo", specifier = "==2024.8.6" },
{ name = "ipykernel", specifier = "==6.29.5" },
@@ -345,6 +360,7 @@ docs = [
{ name = "nbsphinx", specifier = "==0.9.5" },
{ name = "notebook", specifier = "==7.2.2" },
{ name = "sphinx", specifier = "==8.1.3" },
+ { name = "sphinx-autobuild", specifier = "==2024.10.3" },
{ name = "sphinx-copybutton", specifier = "==0.5.2" },
{ name = "sphinxext-opengraph", specifier = "==0.9.1" },
]
@@ -1749,6 +1765,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/d2/3b2ab40f455a256cb6672186bea95cd97b459ce4594050132d71e76f0d6f/pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c", size = 550762 },
]
+[[package]]
+name = "quantiphy"
+version = "2.20"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/92/dc/dec6a98d5ef472812848c1141630f819bfaf2e469f3733083ded2f3b08aa/quantiphy-2.20.tar.gz", hash = "sha256:ba5375ac55c3b90077a793588dd5a88aaf81b2c3b0fc9c9359513ac39f6ed84d", size = 42073 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/37/91/42c59c137bf3d82a6525f3200f23ea6cad81f53828f96e187a519cdf34c3/quantiphy-2.20-py3-none-any.whl", hash = "sha256:afdf5f9d1cc87359bd7daf19dc1cb23808eb2e264d9395b36ca6527fb4d71b3a", size = 41254 },
+]
+
[[package]]
name = "referencing"
version = "0.35.1"
@@ -2065,6 +2090,23 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 },
]
+[[package]]
+name = "sphinx-autobuild"
+version = "2024.10.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama" },
+ { name = "sphinx" },
+ { name = "starlette" },
+ { name = "uvicorn" },
+ { name = "watchfiles" },
+ { name = "websockets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a5/2c/155e1de2c1ba96a72e5dba152c509a8b41e047ee5c2def9e9f0d812f8be7/sphinx_autobuild-2024.10.3.tar.gz", hash = "sha256:248150f8f333e825107b6d4b86113ab28fa51750e5f9ae63b59dc339be951fb1", size = 14023 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/18/c0/eba125db38c84d3c74717008fd3cb5000b68cd7e2cbafd1349c6a38c3d3b/sphinx_autobuild-2024.10.3-py3-none-any.whl", hash = "sha256:158e16c36f9d633e613c9aaf81c19b0fc458ca78b112533b20dafcda430d60fa", size = 11908 },
+]
+
[[package]]
name = "sphinx-basic-ng"
version = "1.0.0b2"
@@ -2169,6 +2211,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 },
]
+[[package]]
+name = "starlette"
+version = "0.41.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3e/da/1fb4bdb72ae12b834becd7e1e7e47001d32f91ec0ce8d7bc1b618d9f0bd9/starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62", size = 2573867 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/54/43/f185bfd0ca1d213beb4293bed51d92254df23d8ceaf6c0e17146d508a776/starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d", size = 73259 },
+]
+
[[package]]
name = "terminado"
version = "0.18.1"
@@ -2267,6 +2321,20 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 },
]
+[[package]]
+name = "uvicorn"
+version = "0.32.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "h11" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e0/fc/1d785078eefd6945f3e5bab5c076e4230698046231eb0f3747bc5c8fa992/uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e", size = 77564 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/eb/14/78bd0e95dd2444b6caacbca2b730671d4295ccb628ef58b81bee903629df/uvicorn-0.32.0-py3-none-any.whl", hash = "sha256:60b8f3a5ac027dcd31448f411ced12b5ef452c646f76f02f8cc3f25d8d26fd82", size = 63723 },
+]
+
[[package]]
name = "virtualenv"
version = "20.27.1"
@@ -2281,6 +2349,59 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ae/92/78324ff89391e00c8f4cf6b8526c41c6ef36b4ea2d2c132250b1a6fc2b8d/virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4", size = 3117838 },
]
+[[package]]
+name = "watchfiles"
+version = "0.24.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c8/27/2ba23c8cc85796e2d41976439b08d52f691655fdb9401362099502d1f0cf/watchfiles-0.24.0.tar.gz", hash = "sha256:afb72325b74fa7a428c009c1b8be4b4d7c2afedafb2982827ef2156646df2fe1", size = 37870 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/89/a1/631c12626378b9f1538664aa221feb5c60dfafbd7f60b451f8d0bdbcdedd/watchfiles-0.24.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:083dc77dbdeef09fa44bb0f4d1df571d2e12d8a8f985dccde71ac3ac9ac067a0", size = 375096 },
+ { url = "https://files.pythonhosted.org/packages/f7/5c/f27c979c8a10aaa2822286c1bffdce3db731cd1aa4224b9f86623e94bbfe/watchfiles-0.24.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e94e98c7cb94cfa6e071d401ea3342767f28eb5a06a58fafdc0d2a4974f4f35c", size = 367425 },
+ { url = "https://files.pythonhosted.org/packages/74/0d/1889e5649885484d29f6c792ef274454d0a26b20d6ed5fdba5409335ccb6/watchfiles-0.24.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82ae557a8c037c42a6ef26c494d0631cacca040934b101d001100ed93d43f361", size = 437705 },
+ { url = "https://files.pythonhosted.org/packages/85/8a/01d9a22e839f0d1d547af11b1fcac6ba6f889513f1b2e6f221d9d60d9585/watchfiles-0.24.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acbfa31e315a8f14fe33e3542cbcafc55703b8f5dcbb7c1eecd30f141df50db3", size = 433636 },
+ { url = "https://files.pythonhosted.org/packages/62/32/a93db78d340c7ef86cde469deb20e36c6b2a873edee81f610e94bbba4e06/watchfiles-0.24.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b74fdffce9dfcf2dc296dec8743e5b0332d15df19ae464f0e249aa871fc1c571", size = 451069 },
+ { url = "https://files.pythonhosted.org/packages/99/c2/e9e2754fae3c2721c9a7736f92dab73723f1968ed72535fff29e70776008/watchfiles-0.24.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:449f43f49c8ddca87c6b3980c9284cab6bd1f5c9d9a2b00012adaaccd5e7decd", size = 469306 },
+ { url = "https://files.pythonhosted.org/packages/4c/45/f317d9e3affb06c3c27c478de99f7110143e87f0f001f0f72e18d0e1ddce/watchfiles-0.24.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4abf4ad269856618f82dee296ac66b0cd1d71450fc3c98532d93798e73399b7a", size = 476187 },
+ { url = "https://files.pythonhosted.org/packages/ac/d3/f1f37248abe0114916921e638f71c7d21fe77e3f2f61750e8057d0b68ef2/watchfiles-0.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f895d785eb6164678ff4bb5cc60c5996b3ee6df3edb28dcdeba86a13ea0465e", size = 425743 },
+ { url = "https://files.pythonhosted.org/packages/2b/e8/c7037ea38d838fd81a59cd25761f106ee3ef2cfd3261787bee0c68908171/watchfiles-0.24.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ae3e208b31be8ce7f4c2c0034f33406dd24fbce3467f77223d10cd86778471c", size = 612327 },
+ { url = "https://files.pythonhosted.org/packages/a0/c5/0e6e228aafe01a7995fbfd2a4edb221bb11a2744803b65a5663fb85e5063/watchfiles-0.24.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2efec17819b0046dde35d13fb8ac7a3ad877af41ae4640f4109d9154ed30a188", size = 595096 },
+ { url = "https://files.pythonhosted.org/packages/63/d5/4780e8bf3de3b4b46e7428a29654f7dc041cad6b19fd86d083e4b6f64bbe/watchfiles-0.24.0-cp310-none-win32.whl", hash = "sha256:6bdcfa3cd6fdbdd1a068a52820f46a815401cbc2cb187dd006cb076675e7b735", size = 264149 },
+ { url = "https://files.pythonhosted.org/packages/fe/1b/5148898ba55fc9c111a2a4a5fb67ad3fa7eb2b3d7f0618241ed88749313d/watchfiles-0.24.0-cp310-none-win_amd64.whl", hash = "sha256:54ca90a9ae6597ae6dc00e7ed0a040ef723f84ec517d3e7ce13e63e4bc82fa04", size = 277542 },
+ { url = "https://files.pythonhosted.org/packages/85/02/366ae902cd81ca5befcd1854b5c7477b378f68861597cef854bd6dc69fbe/watchfiles-0.24.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:bdcd5538e27f188dd3c804b4a8d5f52a7fc7f87e7fd6b374b8e36a4ca03db428", size = 375579 },
+ { url = "https://files.pythonhosted.org/packages/bc/67/d8c9d256791fe312fea118a8a051411337c948101a24586e2df237507976/watchfiles-0.24.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2dadf8a8014fde6addfd3c379e6ed1a981c8f0a48292d662e27cabfe4239c83c", size = 367726 },
+ { url = "https://files.pythonhosted.org/packages/b1/dc/a8427b21ef46386adf824a9fec4be9d16a475b850616cfd98cf09a97a2ef/watchfiles-0.24.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6509ed3f467b79d95fc62a98229f79b1a60d1b93f101e1c61d10c95a46a84f43", size = 437735 },
+ { url = "https://files.pythonhosted.org/packages/3a/21/0b20bef581a9fbfef290a822c8be645432ceb05fb0741bf3c032e0d90d9a/watchfiles-0.24.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8360f7314a070c30e4c976b183d1d8d1585a4a50c5cb603f431cebcbb4f66327", size = 433644 },
+ { url = "https://files.pythonhosted.org/packages/1c/e8/d5e5f71cc443c85a72e70b24269a30e529227986096abe091040d6358ea9/watchfiles-0.24.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:316449aefacf40147a9efaf3bd7c9bdd35aaba9ac5d708bd1eb5763c9a02bef5", size = 450928 },
+ { url = "https://files.pythonhosted.org/packages/61/ee/bf17f5a370c2fcff49e1fec987a6a43fd798d8427ea754ce45b38f9e117a/watchfiles-0.24.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73bde715f940bea845a95247ea3e5eb17769ba1010efdc938ffcb967c634fa61", size = 469072 },
+ { url = "https://files.pythonhosted.org/packages/a3/34/03b66d425986de3fc6077e74a74c78da298f8cb598887f664a4485e55543/watchfiles-0.24.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3770e260b18e7f4e576edca4c0a639f704088602e0bc921c5c2e721e3acb8d15", size = 475517 },
+ { url = "https://files.pythonhosted.org/packages/70/eb/82f089c4f44b3171ad87a1b433abb4696f18eb67292909630d886e073abe/watchfiles-0.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa0fd7248cf533c259e59dc593a60973a73e881162b1a2f73360547132742823", size = 425480 },
+ { url = "https://files.pythonhosted.org/packages/53/20/20509c8f5291e14e8a13104b1808cd7cf5c44acd5feaecb427a49d387774/watchfiles-0.24.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d7a2e3b7f5703ffbd500dabdefcbc9eafeff4b9444bbdd5d83d79eedf8428fab", size = 612322 },
+ { url = "https://files.pythonhosted.org/packages/df/2b/5f65014a8cecc0a120f5587722068a975a692cadbe9fe4ea56b3d8e43f14/watchfiles-0.24.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d831ee0a50946d24a53821819b2327d5751b0c938b12c0653ea5be7dea9c82ec", size = 595094 },
+ { url = "https://files.pythonhosted.org/packages/18/98/006d8043a82c0a09d282d669c88e587b3a05cabdd7f4900e402250a249ac/watchfiles-0.24.0-cp311-none-win32.whl", hash = "sha256:49d617df841a63b4445790a254013aea2120357ccacbed00253f9c2b5dc24e2d", size = 264191 },
+ { url = "https://files.pythonhosted.org/packages/8a/8b/badd9247d6ec25f5f634a9b3d0d92e39c045824ec7e8afcedca8ee52c1e2/watchfiles-0.24.0-cp311-none-win_amd64.whl", hash = "sha256:d3dcb774e3568477275cc76554b5a565024b8ba3a0322f77c246bc7111c5bb9c", size = 277527 },
+ { url = "https://files.pythonhosted.org/packages/af/19/35c957c84ee69d904299a38bae3614f7cede45f07f174f6d5a2f4dbd6033/watchfiles-0.24.0-cp311-none-win_arm64.whl", hash = "sha256:9301c689051a4857d5b10777da23fafb8e8e921bcf3abe6448a058d27fb67633", size = 266253 },
+ { url = "https://files.pythonhosted.org/packages/35/82/92a7bb6dc82d183e304a5f84ae5437b59ee72d48cee805a9adda2488b237/watchfiles-0.24.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7211b463695d1e995ca3feb38b69227e46dbd03947172585ecb0588f19b0d87a", size = 374137 },
+ { url = "https://files.pythonhosted.org/packages/87/91/49e9a497ddaf4da5e3802d51ed67ff33024597c28f652b8ab1e7c0f5718b/watchfiles-0.24.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b8693502d1967b00f2fb82fc1e744df128ba22f530e15b763c8d82baee15370", size = 367733 },
+ { url = "https://files.pythonhosted.org/packages/0d/d8/90eb950ab4998effea2df4cf3a705dc594f6bc501c5a353073aa990be965/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdab9555053399318b953a1fe1f586e945bc8d635ce9d05e617fd9fe3a4687d6", size = 437322 },
+ { url = "https://files.pythonhosted.org/packages/6c/a2/300b22e7bc2a222dd91fce121cefa7b49aa0d26a627b2777e7bdfcf1110b/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34e19e56d68b0dad5cff62273107cf5d9fbaf9d75c46277aa5d803b3ef8a9e9b", size = 433409 },
+ { url = "https://files.pythonhosted.org/packages/99/44/27d7708a43538ed6c26708bcccdde757da8b7efb93f4871d4cc39cffa1cc/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41face41f036fee09eba33a5b53a73e9a43d5cb2c53dad8e61fa6c9f91b5a51e", size = 452142 },
+ { url = "https://files.pythonhosted.org/packages/b0/ec/c4e04f755be003129a2c5f3520d2c47026f00da5ecb9ef1e4f9449637571/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5148c2f1ea043db13ce9b0c28456e18ecc8f14f41325aa624314095b6aa2e9ea", size = 469414 },
+ { url = "https://files.pythonhosted.org/packages/c5/4e/cdd7de3e7ac6432b0abf282ec4c1a1a2ec62dfe423cf269b86861667752d/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e4bd963a935aaf40b625c2499f3f4f6bbd0c3776f6d3bc7c853d04824ff1c9f", size = 472962 },
+ { url = "https://files.pythonhosted.org/packages/27/69/e1da9d34da7fc59db358424f5d89a56aaafe09f6961b64e36457a80a7194/watchfiles-0.24.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c79d7719d027b7a42817c5d96461a99b6a49979c143839fc37aa5748c322f234", size = 425705 },
+ { url = "https://files.pythonhosted.org/packages/e8/c1/24d0f7357be89be4a43e0a656259676ea3d7a074901f47022f32e2957798/watchfiles-0.24.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:32aa53a9a63b7f01ed32e316e354e81e9da0e6267435c7243bf8ae0f10b428ef", size = 612851 },
+ { url = "https://files.pythonhosted.org/packages/c7/af/175ba9b268dec56f821639c9893b506c69fd999fe6a2e2c51de420eb2f01/watchfiles-0.24.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ce72dba6a20e39a0c628258b5c308779b8697f7676c254a845715e2a1039b968", size = 594868 },
+ { url = "https://files.pythonhosted.org/packages/44/81/1f701323a9f70805bc81c74c990137123344a80ea23ab9504a99492907f8/watchfiles-0.24.0-cp312-none-win32.whl", hash = "sha256:d9018153cf57fc302a2a34cb7564870b859ed9a732d16b41a9b5cb2ebed2d444", size = 264109 },
+ { url = "https://files.pythonhosted.org/packages/b4/0b/32cde5bc2ebd9f351be326837c61bdeb05ad652b793f25c91cac0b48a60b/watchfiles-0.24.0-cp312-none-win_amd64.whl", hash = "sha256:551ec3ee2a3ac9cbcf48a4ec76e42c2ef938a7e905a35b42a1267fa4b1645896", size = 277055 },
+ { url = "https://files.pythonhosted.org/packages/4b/81/daade76ce33d21dbec7a15afd7479de8db786e5f7b7d249263b4ea174e08/watchfiles-0.24.0-cp312-none-win_arm64.whl", hash = "sha256:b52a65e4ea43c6d149c5f8ddb0bef8d4a1e779b77591a458a893eb416624a418", size = 266169 },
+ { url = "https://files.pythonhosted.org/packages/df/94/1ad200e937ec91b2a9d6b39ae1cf9c2b1a9cc88d5ceb43aa5c6962eb3c11/watchfiles-0.24.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:632676574429bee8c26be8af52af20e0c718cc7f5f67f3fb658c71928ccd4f7f", size = 376986 },
+ { url = "https://files.pythonhosted.org/packages/ee/fd/d9e020d687ccf90fe95efc513fbb39a8049cf5a3ff51f53c59fcf4c47a5d/watchfiles-0.24.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a2a9891723a735d3e2540651184be6fd5b96880c08ffe1a98bae5017e65b544b", size = 369445 },
+ { url = "https://files.pythonhosted.org/packages/43/cb/c0279b35053555d10ef03559c5aebfcb0c703d9c70a7b4e532df74b9b0e8/watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7fa2bc0efef3e209a8199fd111b8969fe9db9c711acc46636686331eda7dd4", size = 439383 },
+ { url = "https://files.pythonhosted.org/packages/8b/c4/08b3c2cda45db5169148a981c2100c744a4a222fa7ae7644937c0c002069/watchfiles-0.24.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01550ccf1d0aed6ea375ef259706af76ad009ef5b0203a3a4cce0f6024f9b68a", size = 426804 },
+]
+
[[package]]
name = "wcwidth"
version = "0.2.13"
@@ -2317,6 +2438,54 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826 },
]
+[[package]]
+name = "websockets"
+version = "13.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e2/73/9223dbc7be3dcaf2a7bbf756c351ec8da04b1fa573edaf545b95f6b0c7fd/websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878", size = 158549 }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0a/94/d15dbfc6a5eb636dbc754303fba18208f2e88cf97e733e1d64fb9cb5c89e/websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee", size = 157815 },
+ { url = "https://files.pythonhosted.org/packages/30/02/c04af33f4663945a26f5e8cf561eb140c35452b50af47a83c3fbcfe62ae1/websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7", size = 155466 },
+ { url = "https://files.pythonhosted.org/packages/35/e8/719f08d12303ea643655e52d9e9851b2dadbb1991d4926d9ce8862efa2f5/websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6", size = 155716 },
+ { url = "https://files.pythonhosted.org/packages/91/e1/14963ae0252a8925f7434065d25dcd4701d5e281a0b4b460a3b5963d2594/websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b", size = 164806 },
+ { url = "https://files.pythonhosted.org/packages/ec/fa/ab28441bae5e682a0f7ddf3d03440c0c352f930da419301f4a717f675ef3/websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa", size = 163810 },
+ { url = "https://files.pythonhosted.org/packages/44/77/dea187bd9d16d4b91566a2832be31f99a40d0f5bfa55eeb638eb2c3bc33d/websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700", size = 164125 },
+ { url = "https://files.pythonhosted.org/packages/cf/d9/3af14544e83f1437eb684b399e6ba0fa769438e869bf5d83d74bc197fae8/websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c", size = 164532 },
+ { url = "https://files.pythonhosted.org/packages/1c/8a/6d332eabe7d59dfefe4b8ba6f46c8c5fabb15b71c8a8bc3d2b65de19a7b6/websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0", size = 163948 },
+ { url = "https://files.pythonhosted.org/packages/1a/91/a0aeadbaf3017467a1ee03f8fb67accdae233fe2d5ad4b038c0a84e357b0/websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f", size = 163898 },
+ { url = "https://files.pythonhosted.org/packages/71/31/a90fb47c63e0ae605be914b0b969d7c6e6ffe2038cd744798e4b3fbce53b/websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe", size = 158706 },
+ { url = "https://files.pythonhosted.org/packages/93/ca/9540a9ba80da04dc7f36d790c30cae4252589dbd52ccdc92e75b0be22437/websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a", size = 159141 },
+ { url = "https://files.pythonhosted.org/packages/b2/f0/cf0b8a30d86b49e267ac84addbebbc7a48a6e7bb7c19db80f62411452311/websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19", size = 157813 },
+ { url = "https://files.pythonhosted.org/packages/bf/e7/22285852502e33071a8cf0ac814f8988480ec6db4754e067b8b9d0e92498/websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5", size = 155469 },
+ { url = "https://files.pythonhosted.org/packages/68/d4/c8c7c1e5b40ee03c5cc235955b0fb1ec90e7e37685a5f69229ad4708dcde/websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd", size = 155717 },
+ { url = "https://files.pythonhosted.org/packages/c9/e4/c50999b9b848b1332b07c7fd8886179ac395cb766fda62725d1539e7bc6c/websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02", size = 165379 },
+ { url = "https://files.pythonhosted.org/packages/bc/49/4a4ad8c072f18fd79ab127650e47b160571aacfc30b110ee305ba25fffc9/websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7", size = 164376 },
+ { url = "https://files.pythonhosted.org/packages/af/9b/8c06d425a1d5a74fd764dd793edd02be18cf6fc3b1ccd1f29244ba132dc0/websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096", size = 164753 },
+ { url = "https://files.pythonhosted.org/packages/d5/5b/0acb5815095ff800b579ffc38b13ab1b915b317915023748812d24e0c1ac/websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084", size = 165051 },
+ { url = "https://files.pythonhosted.org/packages/30/93/c3891c20114eacb1af09dedfcc620c65c397f4fd80a7009cd12d9457f7f5/websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3", size = 164489 },
+ { url = "https://files.pythonhosted.org/packages/28/09/af9e19885539759efa2e2cd29b8b3f9eecef7ecefea40d46612f12138b36/websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9", size = 164438 },
+ { url = "https://files.pythonhosted.org/packages/b6/08/6f38b8e625b3d93de731f1d248cc1493327f16cb45b9645b3e791782cff0/websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f", size = 158710 },
+ { url = "https://files.pythonhosted.org/packages/fb/39/ec8832ecb9bb04a8d318149005ed8cee0ba4e0205835da99e0aa497a091f/websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557", size = 159137 },
+ { url = "https://files.pythonhosted.org/packages/df/46/c426282f543b3c0296cf964aa5a7bb17e984f58dde23460c3d39b3148fcf/websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc", size = 157821 },
+ { url = "https://files.pythonhosted.org/packages/aa/85/22529867010baac258da7c45848f9415e6cf37fef00a43856627806ffd04/websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49", size = 155480 },
+ { url = "https://files.pythonhosted.org/packages/29/2c/bdb339bfbde0119a6e84af43ebf6275278698a2241c2719afc0d8b0bdbf2/websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd", size = 155715 },
+ { url = "https://files.pythonhosted.org/packages/9f/d0/8612029ea04c5c22bf7af2fd3d63876c4eaeef9b97e86c11972a43aa0e6c/websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0", size = 165647 },
+ { url = "https://files.pythonhosted.org/packages/56/04/1681ed516fa19ca9083f26d3f3a302257e0911ba75009533ed60fbb7b8d1/websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6", size = 164592 },
+ { url = "https://files.pythonhosted.org/packages/38/6f/a96417a49c0ed132bb6087e8e39a37db851c70974f5c724a4b2a70066996/websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9", size = 165012 },
+ { url = "https://files.pythonhosted.org/packages/40/8b/fccf294919a1b37d190e86042e1a907b8f66cff2b61e9befdbce03783e25/websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68", size = 165311 },
+ { url = "https://files.pythonhosted.org/packages/c1/61/f8615cf7ce5fe538476ab6b4defff52beb7262ff8a73d5ef386322d9761d/websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14", size = 164692 },
+ { url = "https://files.pythonhosted.org/packages/5c/f1/a29dd6046d3a722d26f182b783a7997d25298873a14028c4760347974ea3/websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf", size = 164686 },
+ { url = "https://files.pythonhosted.org/packages/0f/99/ab1cdb282f7e595391226f03f9b498f52109d25a2ba03832e21614967dfa/websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c", size = 158712 },
+ { url = "https://files.pythonhosted.org/packages/46/93/e19160db48b5581feac8468330aa11b7292880a94a37d7030478596cc14e/websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3", size = 159145 },
+ { url = "https://files.pythonhosted.org/packages/2d/75/6da22cb3ad5b8c606963f9a5f9f88656256fecc29d420b4b2bf9e0c7d56f/websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238", size = 155499 },
+ { url = "https://files.pythonhosted.org/packages/c0/ba/22833d58629088fcb2ccccedfae725ac0bbcd713319629e97125b52ac681/websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5", size = 155737 },
+ { url = "https://files.pythonhosted.org/packages/95/54/61684fe22bdb831e9e1843d972adadf359cf04ab8613285282baea6a24bb/websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9", size = 157095 },
+ { url = "https://files.pythonhosted.org/packages/fc/f5/6652fb82440813822022a9301a30afde85e5ff3fb2aebb77f34aabe2b4e8/websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6", size = 156701 },
+ { url = "https://files.pythonhosted.org/packages/67/33/ae82a7b860fa8a08aba68818bdf7ff61f04598aa5ab96df4cd5a3e418ca4/websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a", size = 156654 },
+ { url = "https://files.pythonhosted.org/packages/63/0b/a1b528d36934f833e20f6da1032b995bf093d55cb416b9f2266f229fb237/websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23", size = 159192 },
+ { url = "https://files.pythonhosted.org/packages/56/27/96a5cd2626d11c8280656c6c71d8ab50fe006490ef9971ccd154e0c42cd2/websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f", size = 152134 },
+]
+
[[package]]
name = "widgetsnbextension"
version = "4.0.13"