diff --git a/docs/user_guide/geometry.rst b/docs/user_guide/geometry.rst index 31d5b840..930b8a62 100644 --- a/docs/user_guide/geometry.rst +++ b/docs/user_guide/geometry.rst @@ -307,6 +307,13 @@ Concrete Sections ~sectionproperties.pre.library.concrete_sections.concrete_column_section ~sectionproperties.pre.library.concrete_sections.concrete_tee_section ~sectionproperties.pre.library.concrete_sections.concrete_circular_section + ~sectionproperties.pre.library.concrete_sections.rectangular_wall + ~sectionproperties.pre.library.concrete_sections.cee_wall + ~sectionproperties.pre.library.concrete_sections.tee_wall + ~sectionproperties.pre.library.concrete_sections.single_lift_core + ~sectionproperties.pre.library.concrete_sections.double_lift_core_a + ~sectionproperties.pre.library.concrete_sections.double_lift_core_b + ~sectionproperties.pre.library.concrete_sections.stairwell .. _label-bridge-library: diff --git a/pyproject.toml b/pyproject.toml index 6ce3fbb2..fe15b78d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,7 +64,6 @@ pardiso = [ docs = [ "furo==2024.8.6", "sphinx==8.1.3", - "notebook==7.2.2", "ipykernel==6.29.5", "ipython==8.29.0", "nbsphinx==0.9.5", @@ -83,9 +82,14 @@ lint = [ "ruff==0.7.1", "pyright==1.1.387", ] +dev = [ + "ipympl==0.9.4", + "notebook==7.2.2", + "sphinx-autobuild==2024.10.03", +] [tool.uv] -default-groups = ["docs", "test", "lint"] +default-groups = ["docs", "test", "lint", "dev"] [tool.pyright] venvPath = "." diff --git a/src/sectionproperties/pre/library/__init__.py b/src/sectionproperties/pre/library/__init__.py index 13203a36..d089e06f 100644 --- a/src/sectionproperties/pre/library/__init__.py +++ b/src/sectionproperties/pre/library/__init__.py @@ -6,10 +6,17 @@ ) from sectionproperties.pre.library.concrete_sections import ( add_bar, + cee_wall, concrete_circular_section, concrete_column_section, concrete_rectangular_section, concrete_tee_section, + double_lift_core_a, + double_lift_core_b, + rectangular_wall, + single_lift_core, + stairwell, + tee_wall, ) from sectionproperties.pre.library.nastran_sections import ( nastran_bar, diff --git a/src/sectionproperties/pre/library/concrete_sections.py b/src/sectionproperties/pre/library/concrete_sections.py index a60f1082..8debb869 100644 --- a/src/sectionproperties/pre/library/concrete_sections.py +++ b/src/sectionproperties/pre/library/concrete_sections.py @@ -2,11 +2,13 @@ from __future__ import annotations +from math import ceil + import numpy as np -import sectionproperties.pre.geometry as geometry import sectionproperties.pre.library.primitive_sections as primitive_sections import sectionproperties.pre.pre as pre +from sectionproperties.pre.geometry import CompoundGeometry, Geometry def concrete_rectangular_section( @@ -27,7 +29,7 @@ def concrete_rectangular_section( n_circle: int = 4, conc_mat: pre.Material = pre.DEFAULT_MATERIAL, steel_mat: pre.Material = pre.DEFAULT_MATERIAL, -) -> geometry.CompoundGeometry: +) -> CompoundGeometry: """Constructs a reinforced concrete rectangular section. Constructs a reinforced concrete rectangular section of depth ``d`` and width ``b``. @@ -197,7 +199,7 @@ def concrete_rectangular_section( geom = (geom - bar_left - bar_right) + bar_left + bar_right - if isinstance(geom, geometry.CompoundGeometry): + if isinstance(geom, CompoundGeometry): return geom else: msg = "Concrete section generation failed." @@ -216,7 +218,7 @@ def concrete_column_section( filled: bool = False, conc_mat: pre.Material = pre.DEFAULT_MATERIAL, steel_mat: pre.Material = pre.DEFAULT_MATERIAL, -) -> geometry.CompoundGeometry: +) -> CompoundGeometry: """Constructs a reinforced concrete column section. Constructs a reinforced concrete column section of depth ``d`` and width ``b``. @@ -344,7 +346,7 @@ def concrete_column_section( n=n_circle, ) - if isinstance(concrete_geometry, geometry.CompoundGeometry): + if isinstance(concrete_geometry, CompoundGeometry): return concrete_geometry else: msg = "Concrete section generation failed." @@ -371,7 +373,7 @@ def concrete_tee_section( n_circle: int = 4, conc_mat: pre.Material = pre.DEFAULT_MATERIAL, steel_mat: pre.Material = pre.DEFAULT_MATERIAL, -) -> geometry.CompoundGeometry: +) -> CompoundGeometry: """Constructs a reinforced concrete tee section. Constructs a concrete tee section of depth ``d``, width ``b``, flange depth ``d_f`` @@ -548,7 +550,7 @@ def concrete_tee_section( geom = (geom - bar_left - bar_right) + bar_left + bar_right - if isinstance(geom, geometry.CompoundGeometry): # pyright: ignore [reportUnnecessaryIsInstance] + if isinstance(geom, CompoundGeometry): # pyright: ignore [reportUnnecessaryIsInstance] return geom else: msg = "Concrete section generation failed." @@ -566,7 +568,7 @@ def concrete_circular_section( n_circle: int = 4, conc_mat: pre.Material = pre.DEFAULT_MATERIAL, steel_mat: pre.Material = pre.DEFAULT_MATERIAL, -) -> geometry.CompoundGeometry: +) -> CompoundGeometry: """Constructs a reinforced concrete circular section. Constructs a reinforced concrete circular section of diameter ``d``. @@ -668,41 +670,1788 @@ def concrete_circular_section( ) geom = (geom - bar) + bar - if isinstance(geom, geometry.CompoundGeometry): + if isinstance(geom, CompoundGeometry): return geom else: msg = "Concrete section generation failed." raise ValueError(msg) -def add_bar( - geometry: geometry.Geometry | geometry.CompoundGeometry, - area: float, - material: pre.Material, - x: float, - y: float, - n: int, -) -> geometry.CompoundGeometry: - """Adds a reinforcing bar to a ``sectionproperties`` geometry. +def rectangular_wall( + d: float, + t: float, + dia_bar: float, + area_bar: float, + spacing: float, + cover: float, + double: bool = True, + n_circle: int = 4, + conc_mat: pre.Material = pre.DEFAULT_MATERIAL, + steel_mat: pre.Material = pre.DEFAULT_MATERIAL, +) -> CompoundGeometry: + """Constructs a reinforced concrete rectangular wall section. - First removes the geometry through a subtraction operation, then adds the geometry - on top of the newly created hole. This method avoids the doubling up of geometry. + Constructs a reinforced concrete rectangular wall section of depth ``d`` and + thickness ``t``. The wall reinforcement has a maximum spacing of ``spacing`` and is + doubly reinforced if ``double`` is set to ``True``, or singly reinforced if it is + set to ``False``. + + .. note:: + As the reinforcing bars are described by discretised circles, the area of each + bar is required to ensure that the correct reinforcing area is provided. Args: - geometry: Reinforced concrete geometry to which the new bar will be added - area: Bar cross-sectional area - material: Material object for the bar - x: x-position of the bar - y: y-position of the bar - n: Number of points to discretise the bar circle + d: Concrete wall depth + t: Concrete wall thickness + dia_bar: Diameter of the reinforcing bars, used for calculating bar placement + area_bar: Area of the reinforcing bars + spacing: Maximum spacing of the reinforcement bars, the calculated spacing is + equal to ``ceil(extent / spacing) + 1`` + cover: Clear cover to the reinforcing bars + double: If set to ``True``, provides two layers of reinforcement to the wall. If + set to ``False``, provides a single central layer of reinforcement to the + wall. Defaults to ``True``. + n_circle: Number of points used to discretise the circular reinforcing bars. + Defaults to ``4``. + conc_mat: Material object to assign to the concrete area. Defaults to + ``pre.DEFAULT_MATERIAL``. + steel_mat: Material object to assign to the steel area. Defaults to + ``pre.DEFAULT_MATERIAL``. + + Raises: + ValueError: Geometry generation failed Returns: - Geometry object with added bar + Reinforced concrete rectangular wall section geometry + + Example: + The following example creates a 1000 mm long x 180 mm thick rectangular concrete + wall, reinforced with a single layer of N12-200 with 30 mm cover. A coarse + finite element mesh is generated to show the different material regions: + + .. plot:: + :include-source: True + :caption: Reinforced concrete rectangular wall section geometry + + from sectionproperties.pre import Material + from sectionproperties.pre.library import rectangular_wall + from sectionproperties.analysis import Section + + concrete = Material( + name="Concrete", + elastic_modulus=30.1e3, + poissons_ratio=0.2, + yield_strength=32, + density=2.4e-6, + color="lightgrey", + ) + steel = Material( + name="Steel", + elastic_modulus=200e3, + poissons_ratio=0.3, + yield_strength=500, + density=7.85e-6, + color="grey", + ) + + geom = rectangular_wall( + d=1000, + t=180, + dia_bar=12, + area_bar=110, + spacing=200, + cover=30, + double=False, + n_circle=12, + conc_mat=concrete, + steel_mat=steel, + ) + + geom.create_mesh(mesh_sizes=[0]) # a size of zero creates a coarse mesh + Section(geometry=geom).plot_mesh() """ - bar = primitive_sections.circular_section_by_area( - area=area, - n=n, - material=material, - ).shift_section(x_offset=x, y_offset=y) + # create rectangular concrete geometry + geom = primitive_sections.rectangular_section(d=d, b=t, material=conc_mat) - return (geometry - bar) + bar + # positions of bars to add + xs: list[float] = [] + ys: list[float] = [] + + # calculate number of bars along length of wall + y_length = d - 2.0 * cover - dia_bar + n_y = ceil(y_length / spacing) + 1 + + # calculate y-position of bars + start = cover + 0.5 * dia_bar + stop = d - cover - 0.5 * dia_bar + y_i = np.linspace(start=start, stop=stop, num=n_y) + + # calculate x-position of bars + x_i = [cover + 0.5 * dia_bar, t - cover - 0.5 * dia_bar] if double else [0.5 * t] + + # add bars + for y in y_i: + for x in x_i: + xs.append(x) + ys.append(y) + + return add_bars( + geometry=geom, area=area_bar, material=steel_mat, x=xs, y=ys, n=n_circle + ) + + +def cee_wall( + d: float, + b: float, + t_f: float, + t_w: float, + dia_bar: float, + area_bar: float, + spacing: float, + cover: float, + double: bool = True, + n_circle: int = 4, + conc_mat: pre.Material = pre.DEFAULT_MATERIAL, + steel_mat: pre.Material = pre.DEFAULT_MATERIAL, +) -> CompoundGeometry: + """Constructs a reinforced concrete cee-shaped wall section. + + Constructs a reinforced concrete cee-shaped wall section of depth ``d``, width + ``b``, flange thickness ``t_f`` and web thickness ``t_w``. The wall reinforcement + has a maximum spacing of ``spacing`` and is doubly reinforced if ``double`` is set + to ``True``, or singly reinforced if it is set to ``False``. + + .. note:: + As the reinforcing bars are described by discretised circles, the area of each + bar is required to ensure that the correct reinforcing area is provided. + + Args: + d: Concrete wall depth + b: Concrete wall width + t_f: Concrete wall flange thickness + t_w: Concrete wall web thickness + dia_bar: Diameter of the reinforcing bars, used for calculating bar placement + area_bar: Area of the reinforcing bars + spacing: Maximum spacing of the reinforcement bars, the calculated spacing is + equal to ``ceil(extent / spacing) + 1`` + cover: Clear cover to the reinforcing bars + double: If set to ``True``, provides two layers of reinforcement to the wall. If + set to ``False``, provides a single central layer of reinforcement to the + wall. Defaults to ``True``. + n_circle: Number of points used to discretise the circular reinforcing bars. + Defaults to ``4``. + conc_mat: Material object to assign to the concrete area. Defaults to + ``pre.DEFAULT_MATERIAL``. + steel_mat: Material object to assign to the steel area. Defaults to + ``pre.DEFAULT_MATERIAL``. + + Raises: + ValueError: Geometry generation failed + + Returns: + Reinforced concrete cee-shaped wall section geometry + + Example: + The following example creates a 2000 mm deep x 1500 mm wide cee-shaped concrete + wall, with a 200 mm thick flange and 150 mm thick web. The wall is reinforced + with a double layer of N16-150 with 30 mm cover. A coarse finite element mesh is + generated to show the different material regions: + + .. plot:: + :include-source: True + :caption: Reinforced concrete cee-shaped wall section geometry + + from sectionproperties.pre import Material + from sectionproperties.pre.library import cee_wall + from sectionproperties.analysis import Section + + concrete = Material( + name="Concrete", + elastic_modulus=30.1e3, + poissons_ratio=0.2, + yield_strength=32, + density=2.4e-6, + color="lightgrey", + ) + steel = Material( + name="Steel", + elastic_modulus=200e3, + poissons_ratio=0.3, + yield_strength=500, + density=7.85e-6, + color="grey", + ) + + geom = cee_wall( + d=2000, + b=1500, + t_f=200, + t_w=150, + dia_bar=16, + area_bar=200, + spacing=150, + cover=30, + double=True, + n_circle=12, + conc_mat=concrete, + steel_mat=steel, + ) + + geom.create_mesh(mesh_sizes=[0]) # a size of zero creates a coarse mesh + Section(geometry=geom).plot_mesh() + """ + # create cee concrete geometry + geom_outer = primitive_sections.rectangular_section(d=d, b=b, material=conc_mat) + geom_inner = ( + primitive_sections.rectangular_section( + d=d - 2 * t_f, b=b - t_w, material=conc_mat + ) + .align_center(align_to=geom_outer) + .align_to(other=geom_outer, on="right", inner=True) + ) + geom = geom_outer - geom_inner + + # positions of bars to add + xs: list[float] = [] + ys: list[float] = [] + + # calculate reinforcement positions + # singly reinforced + if not double: + # calculate number of bars along length of wall + x_length = b - cover - 0.5 * dia_bar - 0.5 * t_w + n_x = ceil(x_length / spacing) + 1 + y_length = d - t_f + n_y = ceil(y_length / spacing) + 1 + + # calculate position of bars + x_i = np.linspace(start=0.5 * t_w, stop=b - cover - 0.5 * dia_bar, num=n_x) + y_i = np.linspace(start=0.5 * t_f, stop=d - 0.5 * t_f, num=n_y) + + # add bottom bars + for x in x_i: + xs.append(x) + ys.append(0.5 * t_f) + + # add top bars + for x in x_i: + xs.append(x) + ys.append(d - 0.5 * t_f) + + # add web bars + for y in y_i[1:-1]: + xs.append(0.5 * t_w) + ys.append(y) + # doubly reinforced + else: + # bottom/top outer bars + x_length = b - 2.0 * cover - dia_bar + n_x = ceil(x_length / spacing) + 1 + x_i = np.linspace( + start=cover + 0.5 * dia_bar, stop=b - cover - 0.5 * dia_bar, num=n_x + ) + + # add bottom outer bars + for x in x_i: + xs.append(x) + ys.append(cover + 0.5 * dia_bar) + + # add top outer bars + for x in x_i: + xs.append(x) + ys.append(d - cover - 0.5 * dia_bar) + + # web outer bars + y_length = d - 2 * cover - dia_bar + n_y = ceil(y_length / spacing) + 1 + y_i = np.linspace( + start=cover + 0.5 * dia_bar, stop=d - cover - 0.5 * dia_bar, num=n_y + ) + + # add web outer bars + for y in y_i[1:-1]: + xs.append(cover + 0.5 * dia_bar) + ys.append(y) + + # bottom/top inner bars + x_length = b - t_w + n_x = ceil(x_length / spacing) + 1 + x_i = np.linspace( + start=t_w - cover - 0.5 * dia_bar, stop=b - cover - 0.5 * dia_bar, num=n_x + ) + + # add bottom inner bars + for x in x_i: + xs.append(x) + ys.append(t_f - cover - 0.5 * dia_bar) + + # add top inner bars + for x in x_i: + xs.append(x) + ys.append(d - t_f + cover + 0.5 * dia_bar) + + # web inner bars + y_length = d - 2 * t_f + 2 * cover + dia_bar + n_y = ceil(y_length / spacing) + 1 + y_i = np.linspace( + start=t_f - cover - 0.5 * dia_bar, + stop=d - t_f + cover + 0.5 * dia_bar, + num=n_y, + ) + + # add web inner bars + for y in y_i[1:-1]: + xs.append(t_w - cover - 0.5 * dia_bar) + ys.append(y) + + return add_bars( + geometry=geom, area=area_bar, material=steel_mat, x=xs, y=ys, n=n_circle + ) + + +def tee_wall( + d: float, + b: float, + t_f: float, + t_w: float, + dia_bar: float, + area_bar: float, + spacing: float, + cover: float, + double: bool = True, + n_circle: int = 4, + conc_mat: pre.Material = pre.DEFAULT_MATERIAL, + steel_mat: pre.Material = pre.DEFAULT_MATERIAL, +) -> CompoundGeometry: + """Constructs a reinforced concrete tee-shaped wall section. + + Constructs a reinforced concrete tee-shaped wall section of depth ``d``, width + ``b``, flange thickness ``t_f`` and web thickness ``t_w``. The wall reinforcement + has a maximum spacing of ``spacing`` and is doubly reinforced if ``double`` is set + to ``True``, or singly reinforced if it is set to ``False``. + + .. note:: + As the reinforcing bars are described by discretised circles, the area of each + bar is required to ensure that the correct reinforcing area is provided. + + Args: + d: Concrete wall depth + b: Concrete wall width + t_f: Concrete wall flange thickness + t_w: Concrete wall web thickness + dia_bar: Diameter of the reinforcing bars, used for calculating bar placement + area_bar: Area of the reinforcing bars + spacing: Maximum spacing of the reinforcement bars, the calculated spacing is + equal to ``ceil(extent / spacing) + 1`` + cover: Clear cover to the reinforcing bars + double: If set to ``True``, provides two layers of reinforcement to the wall. If + set to ``False``, provides a single central layer of reinforcement to the + wall. Defaults to ``True``. + n_circle: Number of points used to discretise the circular reinforcing bars. + Defaults to ``4``. + conc_mat: Material object to assign to the concrete area. Defaults to + ``pre.DEFAULT_MATERIAL``. + steel_mat: Material object to assign to the steel area. Defaults to + ``pre.DEFAULT_MATERIAL``. + + Raises: + ValueError: Geometry generation failed + + Returns: + Reinforced concrete tee-shaped wall section geometry + + Example: + The following example creates a 1200 mm deep x 1200 mm wide tee-shaped concrete + wall, with a 150 mm thick flange and 120 mm thick web. The wall is reinforced + with a single layer of N12-200 with 30 mm cover. A coarse finite element mesh is + generated to show the different material regions: + + .. plot:: + :include-source: True + :caption: Reinforced concrete tee-shaped wall section geometry + + from sectionproperties.pre import Material + from sectionproperties.pre.library import tee_wall + from sectionproperties.analysis import Section + + concrete = Material( + name="Concrete", + elastic_modulus=30.1e3, + poissons_ratio=0.2, + yield_strength=32, + density=2.4e-6, + color="lightgrey", + ) + steel = Material( + name="Steel", + elastic_modulus=200e3, + poissons_ratio=0.3, + yield_strength=500, + density=7.85e-6, + color="grey", + ) + + geom = tee_wall( + d=1200, + b=1200, + t_f=150, + t_w=120, + dia_bar=12, + area_bar=110, + spacing=200, + cover=30, + double=False, + n_circle=12, + conc_mat=concrete, + steel_mat=steel, + ) + + geom.create_mesh(mesh_sizes=[0]) # a size of zero creates a coarse mesh + Section(geometry=geom).plot_mesh() + """ + # create cee concrete geometry + geom_outer = primitive_sections.rectangular_section(d=d, b=b, material=conc_mat) + geom_inner_left = primitive_sections.rectangular_section( + d=d - t_f, b=0.5 * (b - t_w) + ) + geom_inner_right = geom_inner_left.mirror_section( + axis="y", mirror_point=(0.5 * b, 0) + ) + geom = geom_outer - geom_inner_left - geom_inner_right + + # positions of bars to add + xs: list[float] = [] + ys: list[float] = [] + + # calculate reinforcement positions + # singly reinforced + if not double: + # calculate number of bars along length of wall + x_length = b - 2 * cover - dia_bar + n_x = ceil(x_length / spacing) + 1 + y_length = d - 0.5 * t_f - cover - 0.5 * dia_bar + n_y = ceil(y_length / spacing) + 1 + + # calculate position of bars + x_i = np.linspace( + start=cover + 0.5 * dia_bar, stop=b - cover - 0.5 * dia_bar, num=n_x + ) + y_i = np.linspace(start=cover + 0.5 * dia_bar, stop=d - 0.5 * t_f, num=n_y) + + # add top bars + for x in x_i: + xs.append(x) + ys.append(d - 0.5 * t_f) + + # add web bars + for y in y_i[:-1]: + xs.append(0.5 * b) + ys.append(y) + # doubly reinforced + else: + # top outer bars + x_length = b - 2 * cover - dia_bar + n_x = ceil(x_length / spacing) + 1 + x_i = np.linspace( + start=cover + 0.5 * dia_bar, stop=b - cover - 0.5 * dia_bar, num=n_x + ) + + # add top outer bars + for x in x_i: + xs.append(x) + ys.append(d - cover - 0.5 * dia_bar) + + # top inner bars + x_length = 0.5 * (b - t_w) + n_x = ceil(x_length / spacing) + 1 + x_i_l = np.linspace( + start=cover + 0.5 * dia_bar, + stop=0.5 * (b - t_w) + cover + 0.5 * dia_bar, + num=n_x, + ) + x_i_r = np.linspace( + start=0.5 * (b + t_w) - cover - 0.5 * dia_bar, + stop=b - cover - 0.5 * dia_bar, + num=n_x, + ) + x_i = x_i_l.tolist() + x_i_r.tolist() + + # add top inner bars + for x in x_i: + xs.append(x) + ys.append(d - t_f + cover + 0.5 * dia_bar) + + # web bars + y_length = d - t_f + n_y = ceil(y_length / spacing) + 1 + y_i = np.linspace( + start=cover + 0.5 * dia_bar, stop=d - t_f + cover + 0.5 * dia_bar, num=n_y + ) + x_i = [ + 0.5 * (b - t_w) + cover + 0.5 * dia_bar, + 0.5 * (b + t_w) - cover - 0.5 * dia_bar, + ] + + # add web bars + for y in y_i[:-1]: + for x in x_i: + xs.append(x) + ys.append(y) + + return add_bars( + geometry=geom, area=area_bar, material=steel_mat, x=xs, y=ys, n=n_circle + ) + + +def single_lift_core( + d: float, + b: float, + t1: float, + t2: float, + a: float, + dia_bar: float, + area_bar: float, + spacing: float, + cover: float, + double: bool = True, + n_circle: int = 4, + conc_mat: pre.Material = pre.DEFAULT_MATERIAL, + steel_mat: pre.Material = pre.DEFAULT_MATERIAL, +) -> CompoundGeometry: + """Constructs a reinforced concrete single lift core section. + + Constructs a reinforced concrete single lift core section of depth ``d``, width + ``b``, top/bottom thickness ``t1``, left/right thickness ``t2`` and door opening + width ``a``. The wall reinforcement has a maximum spacing of ``spacing`` and is + doubly reinforced if ``double`` is set to ``True``, or singly reinforced if it is + set to ``False``. + + .. note:: + As the reinforcing bars are described by discretised circles, the area of each + bar is required to ensure that the correct reinforcing area is provided. + + Args: + d: Concrete wall depth + b: Concrete wall width + t1: Top/bottom concrete wall thickness + t2: Left/right concrete wall thickness + a: Door opening width + dia_bar: Diameter of the reinforcing bars, used for calculating bar placement + area_bar: Area of the reinforcing bars + spacing: Maximum spacing of the reinforcement bars, the calculated spacing is + equal to ``ceil(extent / spacing) + 1`` + cover: Clear cover to the reinforcing bars + double: If set to ``True``, provides two layers of reinforcement to the wall. If + set to ``False``, provides a single central layer of reinforcement to the + wall. Defaults to ``True``. + n_circle: Number of points used to discretise the circular reinforcing bars. + Defaults to ``4``. + conc_mat: Material object to assign to the concrete area. Defaults to + ``pre.DEFAULT_MATERIAL``. + steel_mat: Material object to assign to the steel area. Defaults to + ``pre.DEFAULT_MATERIAL``. + + Raises: + ValueError: Geometry generation failed + + Returns: + Reinforced concrete single lift core section geometry + + Example: + The following example creates a 2400 mm deep x 1800 mm wide concrete single lift + core section, with top/bottom walls 150 mm thick, left/right walls 200 mm thick + and a door opening of 1000 mm. The wall is reinforced with a double layer of + N20-150 with 35 mm cover. A coarse finite element mesh is generated to show the + different material regions: + + .. plot:: + :include-source: True + :caption: Reinforced concrete single lift core section geometry + + from sectionproperties.pre import Material + from sectionproperties.pre.library import single_lift_core + from sectionproperties.analysis import Section + + concrete = Material( + name="Concrete", + elastic_modulus=30.1e3, + poissons_ratio=0.2, + yield_strength=32, + density=2.4e-6, + color="lightgrey", + ) + steel = Material( + name="Steel", + elastic_modulus=200e3, + poissons_ratio=0.3, + yield_strength=500, + density=7.85e-6, + color="grey", + ) + + geom = single_lift_core( + d=2400, + b=1800, + t1=150, + t2=200, + a=1000, + dia_bar=20, + area_bar=310, + spacing=150, + cover=35, + double=True, + n_circle=12, + conc_mat=concrete, + steel_mat=steel, + ) + + geom.create_mesh(mesh_sizes=[0]) # a size of zero creates a coarse mesh + Section(geometry=geom).plot_mesh() + """ + # create lift core geometry + geom_outer = primitive_sections.rectangular_section(d=d, b=b, material=conc_mat) + geom_inner = primitive_sections.rectangular_section( + d=d - 2 * t1, b=b - 2 * t2, material=conc_mat + ).align_center(align_to=geom_outer) + geom_door = ( + primitive_sections.rectangular_section(d=a, b=t2, material=conc_mat) + .align_center(align_to=geom_outer) + .align_to(other=geom_outer, on="right", inner=True) + ) + geom = geom_outer - geom_inner - geom_door + + # positions of bars to add + xs: list[float] = [] + ys: list[float] = [] + + # calculate reinforcement positions + # singly reinforced + if not double: + # calculate number of bars along length of wall + x_length = b - t2 + n_x = ceil(x_length / spacing) + 1 + y_length_left = d - t1 + y_length_right = 0.5 * (d - a) - 0.5 * t1 - cover - 0.5 * dia_bar + n_y_left = ceil(y_length_left / spacing) + 1 + n_y_right = ceil(y_length_right / spacing) + 1 + + # calculate position of bars + x_i = np.linspace(start=0.5 * t2, stop=b - 0.5 * t2, num=n_x) + y_i_l = np.linspace(start=0.5 * t1, stop=d - 0.5 * t1, num=n_y_left) + y_i_r_b = np.linspace( + start=0.5 * t1, stop=0.5 * (d - a) - cover - 0.5 * dia_bar, num=n_y_right + ) + y_i_r_t = np.linspace( + start=0.5 * (d + a) + cover + 0.5 * dia_bar, + stop=d - 0.5 * t1, + num=n_y_right, + ) + + # add bot bars + for x in x_i: + xs.append(x) + ys.append(0.5 * t1) + + # add top bars + for x in x_i: + xs.append(x) + ys.append(d - 0.5 * t1) + + # add left bars + for y in y_i_l[1:-1]: + xs.append(0.5 * t2) + ys.append(y) + + # add right bot bars + for y in y_i_r_b[1:]: + xs.append(b - 0.5 * t2) + ys.append(y) + + # add right top bars + for y in y_i_r_t[:-1]: + xs.append(b - 0.5 * t2) + ys.append(y) + # doubly reinforced + else: + # top/bot outer bars + x_length = b - 2 * cover - dia_bar + n_x = ceil(x_length / spacing) + 1 + x_i = np.linspace( + start=cover + 0.5 * dia_bar, stop=b - cover - 0.5 * dia_bar, num=n_x + ) + y_i = [cover + 0.5 * dia_bar, d - cover - 0.5 * dia_bar] + + # add top/bot outer bars + for x in x_i: + for y in y_i: + xs.append(x) + ys.append(y) + + # top/bot inner bars + x_length = b - 2 * t2 + 2 * cover + dia_bar + n_x = ceil(x_length / spacing) + 1 + x_i = np.linspace( + start=t2 - cover - 0.5 * dia_bar, + stop=b - t2 + cover + 0.5 * dia_bar, + num=n_x, + ) + y_i = [t1 - cover - 0.5 * dia_bar, d - t1 + cover + 0.5 * dia_bar] + + # add top/bot inner bars + for x in x_i: + for y in y_i: + xs.append(x) + ys.append(y) + + # left outer bars + y_length = d - 2 * cover - dia_bar + n_y = ceil(y_length / spacing) + 1 + y_i = np.linspace( + start=cover + 0.5 * dia_bar, stop=d - cover - 0.5 * dia_bar, num=n_y + ) + + # add left outer bars + for y in y_i[1:-1]: + xs.append(cover + 0.5 * dia_bar) + ys.append(y) + + # left inner bars + y_length = d - 2 * t1 + 2 * cover + dia_bar + n_y = ceil(y_length / spacing) + 1 + y_i = np.linspace( + start=t1 - cover - 0.5 * dia_bar, + stop=d - t1 + cover + 0.5 * dia_bar, + num=n_y, + ) + + # add left inner bars + for y in y_i[1:-1]: + xs.append(t2 - cover - 0.5 * dia_bar) + ys.append(y) + + # right outer bars + y_length = 0.5 * (d - a) - 2 * cover - dia_bar + n_y = ceil(y_length / spacing) + 1 + y_i_b = np.linspace( + start=cover + 0.5 * dia_bar, + stop=0.5 * (d - a) - cover - 0.5 * dia_bar, + num=n_y, + ) + y_i_t = np.linspace( + start=0.5 * (d + a) + cover + 0.5 * dia_bar, + stop=d - cover - 0.5 * dia_bar, + num=n_y, + ) + y_i = y_i_b.tolist() + y_i_t.tolist() + + # add right outer bars + for y in y_i[1:-1]: + xs.append(b - cover - 0.5 * dia_bar) + ys.append(y) + + # right inner bars + y_length = 0.5 * (d - a) - t1 + n_y = ceil(y_length / spacing) + 1 + y_i_b = np.linspace( + start=t1 - cover - 0.5 * dia_bar, + stop=0.5 * (d - a) - cover - 0.5 * dia_bar, + num=n_y, + ) + y_i_t = np.linspace( + start=0.5 * (d + a) + cover + 0.5 * dia_bar, + stop=d - t1 + cover + 0.5 * dia_bar, + num=n_y, + ) + y_i = y_i_b.tolist() + y_i_t.tolist() + + # add right inner bars + for y in y_i[1:-1]: + xs.append(b - t2 + cover + 0.5 * dia_bar) + ys.append(y) + + return add_bars( + geometry=geom, area=area_bar, material=steel_mat, x=xs, y=ys, n=n_circle + ) + + +def double_lift_core_a( + d: float, + b: float, + t1: float, + t2: float, + a1: float, + a2: float, + dia_bar: float, + area_bar: float, + spacing: float, + cover: float, + double: bool = True, + n_circle: int = 4, + conc_mat: pre.Material = pre.DEFAULT_MATERIAL, + steel_mat: pre.Material = pre.DEFAULT_MATERIAL, +) -> CompoundGeometry: + """Constructs a reinforced concrete double lift core (type A) section. + + Constructs a reinforced concrete double lift core (type A) section of depth ``d``, + width ``b``, top/bottom thickness ``t1``, left/right thickness ``t2``, door opening + width ``a1`` and door pier weidth ``a2``. The wall reinforcement has a maximum + spacing of ``spacing`` and is doubly reinforced if ``double`` is set to ``True``, or + singly reinforced if it is set to ``False``. + + Type A refers to the fact that there is no wall dividing the two lift cores. + + .. note:: + As the reinforcing bars are described by discretised circles, the area of each + bar is required to ensure that the correct reinforcing area is provided. + + Args: + d: Concrete wall depth + b: Concrete wall width + t1: Top/bottom concrete wall thickness + t2: Left/right concrete wall thickness + a1: Door opening width + a2: Door pier width + dia_bar: Diameter of the reinforcing bars, used for calculating bar placement + area_bar: Area of the reinforcing bars + spacing: Maximum spacing of the reinforcement bars, the calculated spacing is + equal to ``ceil(extent / spacing) + 1`` + cover: Clear cover to the reinforcing bars + double: If set to ``True``, provides two layers of reinforcement to the wall. If + set to ``False``, provides a single central layer of reinforcement to the + wall. Defaults to ``True``. + n_circle: Number of points used to discretise the circular reinforcing bars. + Defaults to ``4``. + conc_mat: Material object to assign to the concrete area. Defaults to + ``pre.DEFAULT_MATERIAL``. + steel_mat: Material object to assign to the steel area. Defaults to + ``pre.DEFAULT_MATERIAL``. + + Raises: + ValueError: Geometry generation failed + ValueError: If the lift door opening geometry is invalid + + Returns: + Reinforced concrete double lift core (type A) section geometry + + Example: + The following example creates a 3200 mm deep x 1800 mm wide double lift core + (type A) concrete section, with top/bottom walls 200 mm thick, left/right walls + 150 mm thick, a door opening of 900 mm and a dor pier width of 600 mm. The wall + is reinforced with a double layer of N16-200 with 30 mm cover. A coarse finite + element mesh is generated to show the different material regions: + + .. plot:: + :include-source: True + :caption: Reinforced concrete double lift core (type A) section geometry + + from sectionproperties.pre import Material + from sectionproperties.pre.library import double_lift_core_a + from sectionproperties.analysis import Section + + concrete = Material( + name="Concrete", + elastic_modulus=30.1e3, + poissons_ratio=0.2, + yield_strength=32, + density=2.4e-6, + color="lightgrey", + ) + steel = Material( + name="Steel", + elastic_modulus=200e3, + poissons_ratio=0.3, + yield_strength=500, + density=7.85e-6, + color="grey", + ) + + geom = double_lift_core_a( + d=3200, + b=1800, + t1=200, + t2=150, + a1=900, + a2=600, + dia_bar=16, + area_bar=200, + spacing=200, + cover=30, + double=True, + n_circle=12, + conc_mat=concrete, + steel_mat=steel, + ) + + geom.create_mesh(mesh_sizes=[0]) # a size of zero creates a coarse mesh + Section(geometry=geom).plot_mesh() + """ + # check geometry + if 2 * a1 + a2 > d - 2 * t1: + msg = "(2 * a1 + a2) is larger than the lift opening." + raise ValueError(msg) + + # create lift core geometry + geom_outer = primitive_sections.rectangular_section(d=d, b=b, material=conc_mat) + geom_inner = primitive_sections.rectangular_section( + d=d - 2 * t1, b=b - 2 * t2, material=conc_mat + ).align_center(align_to=geom_outer) + geom_door = primitive_sections.rectangular_section(d=a1, b=t2, material=conc_mat) + nib = 0.5 * (d - 2 * a1 - a2 - 2 * t1) + geom_door_t = geom_door.shift_section(x_offset=b - t2, y_offset=d - t1 - nib - a1) + geom_door_b = geom_door.shift_section(x_offset=b - t2, y_offset=t1 + nib) + geom = geom_outer - geom_inner - geom_door_t - geom_door_b + + # positions of bars to add + xs: list[float] = [] + ys: list[float] = [] + + # calculate reinforcement positions + # singly reinforced + if not double: + # calculate number of bars along length of wall + x_length = b - t2 + n_x = ceil(x_length / spacing) + 1 + y_length = d - t1 + y_nib = nib + 0.5 * t1 + y_pier = a2 - 2 * cover - dia_bar + n_y = ceil(y_length / spacing) + 1 + n_nib = ceil(y_nib / spacing) + 1 + n_pier = ceil(y_pier / spacing) + 1 + + # calculate position of bars + x_i = np.linspace(start=0.5 * t2, stop=b - 0.5 * t2, num=n_x) + y_i = np.linspace(start=0.5 * t1, stop=d - 0.5 * t1, num=n_y) + y_nib_b = np.linspace( + start=0.5 * t1, stop=t1 + nib - cover - 0.5 * dia_bar, num=n_nib + ) + y_nib_t = np.linspace( + start=d - t1 - nib + cover + 0.5 * dia_bar, stop=d - 0.5 * t1, num=n_nib + ) + y_pier = np.linspace( + start=0.5 * (d - a2) + cover + 0.5 * dia_bar, + stop=0.5 * (d + a2) - cover - 0.5 * dia_bar, + num=n_pier, + ) + + # add bot bars + for x in x_i: + xs.append(x) + ys.append(0.5 * t1) + + # add top bars + for x in x_i: + xs.append(x) + ys.append(d - 0.5 * t1) + + # add left bars + for y in y_i[1:-1]: + xs.append(0.5 * t2) + ys.append(y) + + # add nib bot bars + for y in y_nib_b[1:]: + xs.append(b - 0.5 * t2) + ys.append(y) + + # add nib top bars + for y in y_nib_t[:-1]: + xs.append(b - 0.5 * t2) + ys.append(y) + + # add pier bars + for y in y_pier: + xs.append(b - 0.5 * t2) + ys.append(y) + # doubly reinforced + else: + # top/bot outer bars + x_length = b - 2 * cover - dia_bar + n_x = ceil(x_length / spacing) + 1 + x_i = np.linspace( + start=cover + 0.5 * dia_bar, stop=b - cover - 0.5 * dia_bar, num=n_x + ) + y_i = [cover + 0.5 * dia_bar, d - cover - 0.5 * dia_bar] + + # add top/bot outer bars + for x in x_i: + for y in y_i: + xs.append(x) + ys.append(y) + + # top/bot inner bars + x_length = b - 2 * t2 + 2 * cover + dia_bar + n_x = ceil(x_length / spacing) + 1 + x_i = np.linspace( + start=t2 - cover - 0.5 * dia_bar, + stop=b - t2 + cover + 0.5 * dia_bar, + num=n_x, + ) + y_i = [t1 - cover - 0.5 * dia_bar, d - t1 + cover + 0.5 * dia_bar] + + # add top/bot inner bars + for x in x_i: + for y in y_i: + xs.append(x) + ys.append(y) + + # left outer bars + y_length = d - 2 * cover - dia_bar + n_y = ceil(y_length / spacing) + 1 + y_i = np.linspace( + start=cover + 0.5 * dia_bar, stop=d - cover - 0.5 * dia_bar, num=n_y + ) + + # add left outer bars + for y in y_i[1:-1]: + xs.append(cover + 0.5 * dia_bar) + ys.append(y) + + # left inner bars + y_length = d - 2 * t1 + 2 * cover + dia_bar + n_y = ceil(y_length / spacing) + 1 + y_i = np.linspace( + start=t1 - cover - 0.5 * dia_bar, + stop=d - t1 + cover + 0.5 * dia_bar, + num=n_y, + ) + + # add left inner bars + for y in y_i[1:-1]: + xs.append(t2 - cover - 0.5 * dia_bar) + ys.append(y) + + # nib outer bars + y_nib = nib + t1 - 2 * cover - dia_bar + n_nib = ceil(y_nib / spacing) + 1 + y_nib_b = np.linspace( + start=cover + 0.5 * dia_bar, + stop=t1 + nib - cover - 0.5 * dia_bar, + num=n_nib, + ) + y_nib_t = np.linspace( + start=d - t1 - nib + cover + 0.5 * dia_bar, + stop=d - cover - 0.5 * dia_bar, + num=n_nib, + ) + y_nib = y_nib_b.tolist() + y_nib_t.tolist() + + # add nib outer bars + for y in y_nib[1:-1]: + xs.append(b - cover - 0.5 * dia_bar) + ys.append(y) + + # nib inner bars + y_nib = nib + n_nib = ceil(y_nib / spacing) + 1 + y_nib_b = np.linspace( + start=t1 - cover - 0.5 * dia_bar, + stop=t1 + nib - cover - 0.5 * dia_bar, + num=n_nib, + ) + y_nib_t = np.linspace( + start=d - t1 - nib + cover + 0.5 * dia_bar, + stop=d - t1 + cover + 0.5 * dia_bar, + num=n_nib, + ) + y_nib = y_nib_b.tolist() + y_nib_t.tolist() + + # add nib outer bars + for y in y_nib[1:-1]: + xs.append(b - t2 + cover + 0.5 * dia_bar) + ys.append(y) + + # pier bars + y_pier = a2 - 2 * cover - dia_bar + n_pier = ceil(y_pier / spacing) + 1 + y_pier = np.linspace( + start=0.5 * (d - a2) + cover + 0.5 * dia_bar, + stop=0.5 * (d + a2) - cover - 0.5 * dia_bar, + num=n_pier, + ) + x_pier = [b - t2 + cover + 0.5 * dia_bar, b - cover - 0.5 * dia_bar] + + # add pier bars + for y in y_pier: + for x in x_pier: + xs.append(x) + ys.append(y) + + return add_bars( + geometry=geom, area=area_bar, material=steel_mat, x=xs, y=ys, n=n_circle + ) + + +def double_lift_core_b( + d: float, + b: float, + t1: float, + t2: float, + t3: float, + a: float, + dia_bar: float, + area_bar: float, + spacing: float, + cover: float, + double: bool = True, + n_circle: int = 4, + conc_mat: pre.Material = pre.DEFAULT_MATERIAL, + steel_mat: pre.Material = pre.DEFAULT_MATERIAL, +) -> CompoundGeometry: + """Constructs a reinforced concrete double lift core (type B) section. + + Constructs a reinforced concrete double lift core (type B) section of depth ``d``, + width ``b``, top/bottom thickness ``t1``, left/right thickness ``t2``, central wall + thickness ``t3`` and door opening width ``a``. The wall reinforcement has a maximum + spacing of ``spacing`` and is doubly reinforced if ``double`` is set to ``True``, or + singly reinforced if it is set to ``False``. + + Type B refers to the fact that there is a central wall dividing the two lift cores. + + .. note:: + As the reinforcing bars are described by discretised circles, the area of each + bar is required to ensure that the correct reinforcing area is provided. + + Args: + d: Concrete wall depth + b: Concrete wall width + t1: Top/bottom concrete wall thickness + t2: Left/right concrete wall thickness + t3: Central concrete wall thickness + a: Door opening width + dia_bar: Diameter of the reinforcing bars, used for calculating bar placement + area_bar: Area of the reinforcing bars + spacing: Maximum spacing of the reinforcement bars, the calculated spacing is + equal to ``ceil(extent / spacing) + 1`` + cover: Clear cover to the reinforcing bars + double: If set to ``True``, provides two layers of reinforcement to the wall. If + set to ``False``, provides a single central layer of reinforcement to the + wall. Defaults to ``True``. + n_circle: Number of points used to discretise the circular reinforcing bars. + Defaults to ``4``. + conc_mat: Material object to assign to the concrete area. Defaults to + ``pre.DEFAULT_MATERIAL``. + steel_mat: Material object to assign to the steel area. Defaults to + ``pre.DEFAULT_MATERIAL``. + + Raises: + ValueError: Geometry generation failed + ValueError: If the lift door opening geometry is invalid + + Returns: + Reinforced concrete double lift core (type B) section geometry + + Example: + The following example creates a 6000 mm deep x 3000 mm wide double lift core + (type B) concrete section, with top/bottom walls 250 mm thick, left/right walls + 220 mm thick, central wall 180 mm thick and a door opening of 1500 mm. The wall + is reinforced with a single layer of N24-300 with 50 mm cover. A coarse finite + element mesh is generated to show the different material regions: + + .. plot:: + :include-source: True + :caption: Reinforced concrete double lift core (type B) section geometry + + from sectionproperties.pre import Material + from sectionproperties.pre.library import double_lift_core_b + from sectionproperties.analysis import Section + + concrete = Material( + name="Concrete", + elastic_modulus=30.1e3, + poissons_ratio=0.2, + yield_strength=32, + density=2.4e-6, + color="lightgrey", + ) + steel = Material( + name="Steel", + elastic_modulus=200e3, + poissons_ratio=0.3, + yield_strength=500, + density=7.85e-6, + color="grey", + ) + + geom = double_lift_core_b( + d=6000, + b=3000, + t1=250, + t2=220, + t3=180, + a=1500, + dia_bar=24, + area_bar=450, + spacing=300, + cover=50, + double=True, + n_circle=12, + conc_mat=concrete, + steel_mat=steel, + ) + + geom.create_mesh(mesh_sizes=[0]) # a size of zero creates a coarse mesh + Section(geometry=geom).plot_mesh() + """ + # check geometry + if a > 0.5 * (d - 2 * t1 - t3): + msg = "a is larger than a single lift opening." + raise ValueError(msg) + + # calculate nib and a2 + nib = 0.25 * (d - 2 * t1 - 2 * a - t3) + a2 = 2 * nib + t3 + + # create lift core geometry + geom_outer = primitive_sections.rectangular_section(d=d, b=b, material=conc_mat) + geom_inner = primitive_sections.rectangular_section( + d=0.5 * (d - 2 * t1 - t3), b=b - 2 * t2, material=conc_mat + ) + geom_door = primitive_sections.rectangular_section(d=a, b=t2, material=conc_mat) + geom = ( + geom_outer + - geom_inner.shift_section(x_offset=t2, y_offset=t1) + - geom_inner.shift_section(x_offset=t2, y_offset=0.5 * (d + t3)) + - geom_door.shift_section(x_offset=b - t2, y_offset=d - t1 - nib - a) + - geom_door.shift_section(x_offset=b - t2, y_offset=t1 + nib) + ) + + # positions of bars to add + xs: list[float] = [] + ys: list[float] = [] + + # calculate reinforcement positions + # singly reinforced + if not double: + # calculate number of bars along length of wall + x_length = b - t2 + n_x = ceil(x_length / spacing) + 1 + y_length = d - t1 + y_nib = nib + 0.5 * t1 + y_pier = a2 - 2 * cover - dia_bar + n_y = ceil(y_length / spacing) + 1 + n_nib = ceil(y_nib / spacing) + 1 + n_pier = ceil(y_pier / spacing) + 1 + + # calculate position of bars + x_i = np.linspace(start=0.5 * t2, stop=b - 0.5 * t2, num=n_x) + y_i = np.linspace(start=0.5 * t1, stop=d - 0.5 * t1, num=n_y) + y_nib_b = np.linspace( + start=0.5 * t1, stop=t1 + nib - cover - 0.5 * dia_bar, num=n_nib + ) + y_nib_t = np.linspace( + start=d - t1 - nib + cover + 0.5 * dia_bar, stop=d - 0.5 * t1, num=n_nib + ) + y_pier = np.linspace( + start=0.5 * (d - a2) + cover + 0.5 * dia_bar, + stop=0.5 * (d + a2) - cover - 0.5 * dia_bar, + num=n_pier, + ) + + # add bot bars + for x in x_i: + xs.append(x) + ys.append(0.5 * t1) + + # add mid bars + for x in x_i[1:-1]: + xs.append(x) + ys.append(0.5 * d) + + # add top bars + for x in x_i: + xs.append(x) + ys.append(d - 0.5 * t1) + + # add left bars + for y in y_i[1:-1]: + xs.append(0.5 * t2) + ys.append(y) + + # add nib bot bars + for y in y_nib_b[1:]: + xs.append(b - 0.5 * t2) + ys.append(y) + + # add nib top bars + for y in y_nib_t[:-1]: + xs.append(b - 0.5 * t2) + ys.append(y) + + # add pier bars + for y in y_pier: + xs.append(b - 0.5 * t2) + ys.append(y) + # doubly reinforced + else: + # top/bot outer bars + x_length = b - 2 * cover - dia_bar + n_x = ceil(x_length / spacing) + 1 + x_i = np.linspace( + start=cover + 0.5 * dia_bar, stop=b - cover - 0.5 * dia_bar, num=n_x + ) + y_i = [cover + 0.5 * dia_bar, d - cover - 0.5 * dia_bar] + + # add top/bot outer bars + for x in x_i: + for y in y_i: + xs.append(x) + ys.append(y) + + # top/bot/central inner bars + x_length = b - 2 * t2 + 2 * cover + dia_bar + n_x = ceil(x_length / spacing) + 1 + x_i = np.linspace( + start=t2 - cover - 0.5 * dia_bar, + stop=b - t2 + cover + 0.5 * dia_bar, + num=n_x, + ) + y_i = [ + t1 - cover - 0.5 * dia_bar, + 0.5 * (d - t3) + cover + 0.5 * dia_bar, + 0.5 * (d + t3) - cover - 0.5 * dia_bar, + d - t1 + cover + 0.5 * dia_bar, + ] + + # add top/bot inner bars + for x in x_i: + for y in y_i: + xs.append(x) + ys.append(y) + + # left outer bars + y_length = d - 2 * cover - dia_bar + n_y = ceil(y_length / spacing) + 1 + y_i = np.linspace( + start=cover + 0.5 * dia_bar, stop=d - cover - 0.5 * dia_bar, num=n_y + ) + + # add left outer bars + for y in y_i[1:-1]: + xs.append(cover + 0.5 * dia_bar) + ys.append(y) + + # left inner bars + y_length = 0.5 * (d - 2 * t1 - t3) + 2 * cover + dia_bar + n_y = ceil(y_length / spacing) + 1 + y_i_b = np.linspace( + start=t1 - cover - 0.5 * dia_bar, + stop=0.5 * (d - t3) + cover + 0.5 * dia_bar, + num=n_y, + ) + y_i_t = np.linspace( + start=0.5 * (d + t3) - cover - 0.5 * dia_bar, + stop=d - t1 + cover + 0.5 * dia_bar, + num=n_y, + ) + y_i = y_i_b[1:-1].tolist() + y_i_t[1:-1].tolist() + + # add left inner bars + for y in y_i: + xs.append(t2 - cover - 0.5 * dia_bar) + ys.append(y) + + # nib outer bars + y_nib = nib + t1 - 2 * cover - dia_bar + n_nib = ceil(y_nib / spacing) + 1 + y_nib_b = np.linspace( + start=cover + 0.5 * dia_bar, + stop=t1 + nib - cover - 0.5 * dia_bar, + num=n_nib, + ) + y_nib_t = np.linspace( + start=d - t1 - nib + cover + 0.5 * dia_bar, + stop=d - cover - 0.5 * dia_bar, + num=n_nib, + ) + y_nib = y_nib_b.tolist() + y_nib_t.tolist() + + # add nib outer bars + for y in y_nib[1:-1]: + xs.append(b - cover - 0.5 * dia_bar) + ys.append(y) + + # nib inner bars + y_nib = nib + n_nib = ceil(y_nib / spacing) + 1 + y_nib_b = np.linspace( + start=t1 - cover - 0.5 * dia_bar, + stop=t1 + nib - cover - 0.5 * dia_bar, + num=n_nib, + ) + y_nib_t = np.linspace( + start=d - t1 - nib + cover + 0.5 * dia_bar, + stop=d - t1 + cover + 0.5 * dia_bar, + num=n_nib, + ) + y_nib = y_nib_b.tolist() + y_nib_t.tolist() + + # add nib outer bars + for y in y_nib[1:-1]: + xs.append(b - t2 + cover + 0.5 * dia_bar) + ys.append(y) + + # outer pier bars + y_pier = a2 - 2 * cover - dia_bar + n_pier = ceil(y_pier / spacing) + 1 + y_pier = np.linspace( + start=0.5 * (d - a2) + cover + 0.5 * dia_bar, + stop=0.5 * (d + a2) - cover - 0.5 * dia_bar, + num=n_pier, + ) + + # add outer pier bars + for y in y_pier: + xs.append(b - cover - 0.5 * dia_bar) + ys.append(y) + + # inner pier bars + y_pier = nib - 2 * cover - dia_bar + n_pier = ceil(y_pier / spacing) + 1 + y_pier_b = np.linspace( + start=0.5 * (d - a2) + cover + 0.5 * dia_bar, + stop=0.5 * d - cover - 0.5 * dia_bar, + num=n_pier, + ) + y_pier_t = np.linspace( + start=0.5 * d + cover + 0.5 * dia_bar, + stop=0.5 * (d + a2) - cover - 0.5 * dia_bar, + num=n_pier, + ) + y_pier = y_pier_b[:-1].tolist() + y_pier_t[1:].tolist() + + # add outer pier bars + for y in y_pier: + xs.append(b - t2 + cover + 0.5 * dia_bar) + ys.append(y) + + return add_bars( + geometry=geom, area=area_bar, material=steel_mat, x=xs, y=ys, n=n_circle + ) + + +def stairwell( + d: float, + b: float, + t1: float, + t2: float, + a: float, + dia_bar: float, + area_bar: float, + spacing: float, + cover: float, + double: bool = True, + n_circle: int = 4, + conc_mat: pre.Material = pre.DEFAULT_MATERIAL, + steel_mat: pre.Material = pre.DEFAULT_MATERIAL, +) -> CompoundGeometry: + """Constructs a reinforced concrete stairwell section. + + Constructs a reinforced concrete stairwell section of depth ``d``, width ``b``, + top/bottom thickness ``t1``, left/right thickness ``t2`` and door opening width + ``a``. The wall reinforcement has a maximum spacing of ``spacing`` and is doubly + reinforced if ``double`` is set to ``True``, or singly reinforced if it is set to + ``False``. + + .. note:: + As the reinforcing bars are described by discretised circles, the area of each + bar is required to ensure that the correct reinforcing area is provided. + + Args: + d: Concrete wall depth + b: Concrete wall width + t1: Top/bottom concrete wall thickness + t2: Left/right concrete wall thickness + a: Door opening width + dia_bar: Diameter of the reinforcing bars, used for calculating bar placement + area_bar: Area of the reinforcing bars + spacing: Maximum spacing of the reinforcement bars, the calculated spacing is + equal to ``ceil(extent / spacing) + 1`` + cover: Clear cover to the reinforcing bars + double: If set to ``True``, provides two layers of reinforcement to the wall. If + set to ``False``, provides a single central layer of reinforcement to the + wall. Defaults to ``True``. + n_circle: Number of points used to discretise the circular reinforcing bars. + Defaults to ``4``. + conc_mat: Material object to assign to the concrete area. Defaults to + ``pre.DEFAULT_MATERIAL``. + steel_mat: Material object to assign to the steel area. Defaults to + ``pre.DEFAULT_MATERIAL``. + + Raises: + ValueError: Geometry generation failed + + Returns: + Reinforced concrete stairwell section geometry + + Example: + The following example creates a 5000 mm deep x 2500 mm wide concrete stairwell + section, with top/bottom walls 180 mm thick, left/right walls 200 mm thick and a + door opening of 900 mm. The wall is reinforced with a double layer of N16-200 + with 30 mm cover. A coarse finite element mesh is generated to show the + different material regions: + + .. plot:: + :include-source: True + :caption: Reinforced concrete stairwell section geometry + + from sectionproperties.pre import Material + from sectionproperties.pre.library import stairwell + from sectionproperties.analysis import Section + + concrete = Material( + name="Concrete", + elastic_modulus=30.1e3, + poissons_ratio=0.2, + yield_strength=32, + density=2.4e-6, + color="lightgrey", + ) + steel = Material( + name="Steel", + elastic_modulus=200e3, + poissons_ratio=0.3, + yield_strength=500, + density=7.85e-6, + color="grey", + ) + + geom = stairwell( + d=5000, + b=2500, + t1=180, + t2=200, + a=900, + dia_bar=16, + area_bar=200, + spacing=200, + cover=20, + double=True, + n_circle=12, + conc_mat=concrete, + steel_mat=steel, + ) + + geom.create_mesh(mesh_sizes=[0]) # a size of zero creates a coarse mesh + Section(geometry=geom).plot_mesh() + """ + # create stairwell geometry + geom_outer = primitive_sections.rectangular_section(d=d, b=b, material=conc_mat) + geom_inner = primitive_sections.rectangular_section( + d=d - 2 * t1, b=b - 2 * t2, material=conc_mat + ).align_center(align_to=geom_outer) + geom_door = primitive_sections.rectangular_section( + d=a, b=t2, material=conc_mat + ).shift_section(x_offset=b - t2, y_offset=d - t1 - a) + geom = geom_outer - geom_inner - geom_door + + # positions of bars to add + xs: list[float] = [] + ys: list[float] = [] + + # calculate reinforcement positions + # singly reinforced + if not double: + # calculate number of bars along length of wall + x_length = b - t2 + n_x = ceil(x_length / spacing) + 1 + y_length_left = d - t1 + y_length_right = d - 1.5 * t1 - a - cover - 0.5 * dia_bar + n_y_left = ceil(y_length_left / spacing) + 1 + n_y_right = ceil(y_length_right / spacing) + 1 + + # calculate position of bars + x_i = np.linspace(start=0.5 * t2, stop=b - 0.5 * t2, num=n_x) + y_i_l = np.linspace(start=0.5 * t1, stop=d - 0.5 * t1, num=n_y_left) + y_i_r = np.linspace( + start=0.5 * t1, stop=d - t1 - a - cover - 0.5 * dia_bar, num=n_y_right + ) + + # add bot bars + for x in x_i: + xs.append(x) + ys.append(0.5 * t1) + + # add top bars + for x in x_i: + xs.append(x) + ys.append(d - 0.5 * t1) + + # add left bars + for y in y_i_l[1:-1]: + xs.append(0.5 * t2) + ys.append(y) + + # add right bars + for y in y_i_r[1:]: + xs.append(b - 0.5 * t2) + ys.append(y) + # doubly reinforced + else: + # top/bot outer bars + x_length = b - 2 * cover - dia_bar + n_x = ceil(x_length / spacing) + 1 + x_i = np.linspace( + start=cover + 0.5 * dia_bar, stop=b - cover - 0.5 * dia_bar, num=n_x + ) + y_i = [cover + 0.5 * dia_bar, d - cover - 0.5 * dia_bar] + + # add top/bot outer bars + for x in x_i: + for y in y_i: + xs.append(x) + ys.append(y) + + # top inner bars + x_length = b - t2 + n_x = ceil(x_length / spacing) + 1 + x_i = np.linspace( + start=t2 - cover - 0.5 * dia_bar, + stop=b - cover - 0.5 * dia_bar, + num=n_x, + ) + + # add top inner bars + for x in x_i: + xs.append(x) + ys.append(d - t1 + cover + 0.5 * dia_bar) + + # bot inner bars + x_length = b - 2 * t2 + 2 * cover + dia_bar + n_x = ceil(x_length / spacing) + 1 + x_i = np.linspace( + start=t2 - cover - 0.5 * dia_bar, + stop=b - t2 + cover + 0.5 * dia_bar, + num=n_x, + ) + + # add bot inner bars + for x in x_i: + xs.append(x) + ys.append(t1 - cover - 0.5 * dia_bar) + + # left outer bars + y_length = d - 2 * cover - dia_bar + n_y = ceil(y_length / spacing) + 1 + y_i = np.linspace( + start=cover + 0.5 * dia_bar, stop=d - cover - 0.5 * dia_bar, num=n_y + ) + + # add left outer bars + for y in y_i[1:-1]: + xs.append(cover + 0.5 * dia_bar) + ys.append(y) + + # left inner bars + y_length = d - 2 * t1 + 2 * cover + dia_bar + n_y = ceil(y_length / spacing) + 1 + y_i = np.linspace( + start=t1 - cover - 0.5 * dia_bar, + stop=d - t1 + cover + 0.5 * dia_bar, + num=n_y, + ) + + # add left inner bars + for y in y_i[1:-1]: + xs.append(t2 - cover - 0.5 * dia_bar) + ys.append(y) + + # right inner bars + y_length = d - 2 * t1 - a + n_y = ceil(y_length / spacing) + 1 + y_i = np.linspace( + start=t1 - cover - 0.5 * dia_bar, + stop=d - t1 - a - cover - 0.5 * dia_bar, + num=n_y, + ) + + # add right inner bars + for y in y_i[1:]: + xs.append(b - t2 + cover + 0.5 * dia_bar) + ys.append(y) + + # right outer bars + y_length = d - t1 - a - cover - 0.5 * dia_bar + n_y = ceil(y_length / spacing) + 1 + y_i = np.linspace( + start=cover + 0.5 * dia_bar, + stop=d - t1 - a - cover - 0.5 * dia_bar, + num=n_y, + ) + + # add right outer bars + for y in y_i[1:]: + xs.append(b - cover - 0.5 * dia_bar) + ys.append(y) + + return add_bars( + geometry=geom, area=area_bar, material=steel_mat, x=xs, y=ys, n=n_circle + ) + + +def add_bar( + geometry: Geometry | CompoundGeometry, + area: float, + material: pre.Material, + x: float, + y: float, + n: int, +) -> CompoundGeometry: + """Adds a reinforcing bar to a ``sectionproperties`` geometry. + + First removes the geometry through a subtraction operation, then adds the geometry + on top of the newly created hole. This method avoids the doubling up of + + Args: + geometry: Reinforced concrete geometry to which the new bar will be added + area: Bar cross-sectional area + material: Material object for the bar + x: x-position of the bar + y: y-position of the bar + n: Number of points to discretise the bar circle + + Returns: + Geometry object with added bar + """ + bar = primitive_sections.circular_section_by_area( + area=area, + n=n, + material=material, + ).shift_section(x_offset=x, y_offset=y) + + return (geometry - bar) + bar + + +def add_bars( + geometry: Geometry | CompoundGeometry, + area: float, + material: pre.Material, + x: list[float], + y: list[float], + n: int, +) -> CompoundGeometry: + """Adds a list of reinforcing bars to a ``sectionproperties`` geometry. + + First removes the geometry through a subtraction operation, then adds the geometry + on top of the newly created hole. This method avoids the doubling up of + + Args: + geometry: Reinforced concrete geometry to which the new bars will be added + area: Bar cross-sectional area + material: Material object for the bars + x: x-positions of the bars + y: y-positions of the bars + n: Number of points to discretise the bar circle + + Returns: + Geometry object with added bars + """ + bars = CompoundGeometry( + geoms=[ + primitive_sections.circular_section_by_area( + area=area, n=n, material=material + ).shift_section(x_offset=x_i, y_offset=y_i) + for x_i, y_i in zip(x, y, strict=False) + ] + ) + + return (geometry - bars) + bars diff --git a/tests/section_library/test_concrete_sections.py b/tests/section_library/test_concrete_sections.py index 84386eb6..81462eb4 100644 --- a/tests/section_library/test_concrete_sections.py +++ b/tests/section_library/test_concrete_sections.py @@ -9,6 +9,7 @@ import sectionproperties.pre.library.concrete_sections as cs import sectionproperties.pre.library.primitive_sections as ps import sectionproperties.pre.pre as pre +from sectionproperties.pre.geometry import check_geometry_overlaps r_tol = 1e-6 @@ -62,6 +63,10 @@ def test_concrete_rectangular_section(get_materials): steel_mat=steel_mat, ) + # check no overlaps + polygons = [sec_geom.geom for sec_geom in rect.geoms] + assert not check_geometry_overlaps(polygons) + # check geometry is created correctly conc_area = 0 steel_area = 0 @@ -72,11 +77,11 @@ def test_concrete_rectangular_section(get_materials): elif geom.material == steel_mat: steel_area += geom.calculate_area() - net_area = 600 * 300 + gross_area = 600 * 300 actual_steel_area = 3 * (200 + 310) # check areas - check.almost_equal(conc_area, net_area - actual_steel_area, rel=r_tol) + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) check.almost_equal(steel_area, actual_steel_area, rel=r_tol) @@ -84,7 +89,7 @@ def test_concrete_tee_section(get_materials): """Tests the concrete_tee_section() method.""" conc_mat, steel_mat = get_materials - rect = cs.concrete_tee_section( + tee = cs.concrete_tee_section( d=900, b=300, d_f=200, @@ -102,21 +107,25 @@ def test_concrete_tee_section(get_materials): steel_mat=steel_mat, ) + # check no overlaps + polygons = [sec_geom.geom for sec_geom in tee.geoms] + assert not check_geometry_overlaps(polygons) + # check geometry is created correctly conc_area = 0 steel_area = 0 - for geom in rect.geoms: + for geom in tee.geoms: if geom.material == conc_mat: conc_area += geom.calculate_area() elif geom.material == steel_mat: steel_area += geom.calculate_area() - net_area = 700 * 300 + 1200 * 200 + gross_area = 700 * 300 + 1200 * 200 actual_steel_area = 6 * 310 + 3 * 450 # check areas - check.almost_equal(conc_area, net_area - actual_steel_area, rel=r_tol) + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) check.almost_equal(steel_area, actual_steel_area, rel=r_tol) @@ -124,7 +133,7 @@ def test_concrete_circular_section(get_materials): """Tests the concrete_circular_section() method.""" conc_mat, steel_mat = get_materials - rect = cs.concrete_circular_section( + circ = cs.concrete_circular_section( d=600, area_conc=np.pi * 600 * 600 / 4, n_conc=64, @@ -137,28 +146,30 @@ def test_concrete_circular_section(get_materials): steel_mat=steel_mat, ) + # check no overlaps + polygons = [sec_geom.geom for sec_geom in circ.geoms] + assert not check_geometry_overlaps(polygons) + # check geometry is created correctly conc_area = 0 steel_area = 0 - for geom in rect.geoms: + for geom in circ.geoms: if geom.material == conc_mat: conc_area += geom.calculate_area() elif geom.material == steel_mat: steel_area += geom.calculate_area() - net_area = np.pi * 600 * 600 / 4 + gross_area = np.pi * 600 * 600 / 4 actual_steel_area = 8 * 310 # check areas - check.almost_equal(conc_area, net_area - actual_steel_area, rel=r_tol) + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) check.almost_equal(steel_area, actual_steel_area, rel=r_tol) -def test_concrete_column_section(get_materials): +def test_concrete_column_section(): """Tests the concrete_column_section() method.""" - conc_mat, steel_mat = get_materials - concrete = pre.Material( name="Concrete", elastic_modulus=30.1e3, @@ -191,6 +202,10 @@ def test_concrete_column_section(get_materials): ) # NOTE: Bar diam and Bar area do not match. This is intentional. geometry.create_mesh(mesh_sizes=[500]) + # check no overlaps + polygons = [sec_geom.geom for sec_geom in geometry.geoms] + assert not check_geometry_overlaps(polygons) + # check geometry is created correctly conc_area = 0 steel_area = 0 @@ -201,10 +216,10 @@ def test_concrete_column_section(get_materials): elif geom.material == steel: steel_area += geom.calculate_area() - net_area = 300 * 600 + gross_area = 300 * 600 actual_steel_area = 14 * 500.0 - check.almost_equal(conc_area, net_area - actual_steel_area, rel=r_tol) + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) check.almost_equal(steel_area, actual_steel_area, rel=r_tol) bar_centroids = [tuple(geom.geom.centroid.coords[0]) for geom in geometry.geoms[1:]] @@ -228,6 +243,553 @@ def test_concrete_column_section(get_materials): check.equal(y_coords.get(540), 3) +def test_concrete_rectangular_wall(get_materials): + """Tests the rectangular_wall() method.""" + conc_mat, steel_mat = get_materials + + wall1 = cs.rectangular_wall( + d=1000, + t=180, + dia_bar=12, + area_bar=110, + spacing=200, + cover=30, + double=False, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall1.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall1.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 1000 * 180 + actual_steel_area = 6 * 110 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + wall2 = cs.rectangular_wall( + d=1000, + t=180, + dia_bar=12, + area_bar=110, + spacing=200, + cover=30, + double=True, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall2.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall2.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 1000 * 180 + actual_steel_area = 2 * 6 * 110 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + +def test_concrete_cee_wall(get_materials): + """Tests the cee_wall() method.""" + conc_mat, steel_mat = get_materials + + wall1 = cs.cee_wall( + d=2000, + b=1500, + t_f=200, + t_w=150, + dia_bar=16, + area_bar=200, + spacing=150, + cover=30, + double=False, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall1.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall1.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 2000 * 1500 - 1600 * 1350 + actual_steel_area = (13 + 2 * 10) * 200 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + wall2 = cs.cee_wall( + d=2000, + b=1500, + t_f=200, + t_w=150, + dia_bar=16, + area_bar=200, + spacing=150, + cover=30, + double=True, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall2.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall2.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 2000 * 1500 - 1600 * 1350 + actual_steel_area = 65 * 200 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + +def test_concrete_tee_wall(get_materials): + """Tests the tee_wall() method.""" + conc_mat, steel_mat = get_materials + + wall1 = cs.tee_wall( + d=1200, + b=1200, + t_f=150, + t_w=120, + dia_bar=12, + area_bar=110, + spacing=200, + cover=30, + double=False, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall1.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall1.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 1200 * 150 + 1050 * 120 + actual_steel_area = (6 + 7) * 110 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + wall2 = cs.tee_wall( + d=1200, + b=1200, + t_f=150, + t_w=120, + dia_bar=12, + area_bar=110, + spacing=200, + cover=30, + double=True, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall2.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall2.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 1200 * 150 + 1050 * 120 + actual_steel_area = 27 * 110 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + +def test_single_lift_core(get_materials): + """Tests the single_lift_core() method.""" + conc_mat, steel_mat = get_materials + + wall1 = cs.single_lift_core( + d=2400, + b=1800, + t1=150, + t2=200, + a=1000, + dia_bar=20, + area_bar=310, + spacing=150, + cover=35, + double=False, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall1.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall1.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 2400 * 1800 - 2100 * 1400 - 1000 * 200 + actual_steel_area = (16 + 2 * 11 + 2 * 4) * 310 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + wall2 = cs.single_lift_core( + d=2400, + b=1800, + t1=150, + t2=200, + a=1000, + dia_bar=20, + area_bar=310, + spacing=150, + cover=35, + double=True, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall2.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall2.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 2400 * 1800 - 2100 * 1400 - 1000 * 200 + actual_steel_area = 95 * 310 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + +def test_double_lift_core_a(get_materials): + """Tests the double_lift_core_a() method.""" + conc_mat, steel_mat = get_materials + + wall1 = cs.double_lift_core_a( + d=3200, + b=1800, + t1=200, + t2=150, + a1=900, + a2=600, + dia_bar=16, + area_bar=200, + spacing=200, + cover=30, + double=False, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall1.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall1.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 3200 * 1800 - 2800 * 1500 - 2 * 150 * 900 + actual_steel_area = (16 + 2 * 9 + 2 * 2 + 4) * 200 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + wall2 = cs.double_lift_core_a( + d=3200, + b=1800, + t1=200, + t2=150, + a1=900, + a2=600, + dia_bar=16, + area_bar=200, + spacing=200, + cover=30, + double=True, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall2.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall2.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 3200 * 1800 - 2800 * 1500 - 2 * 150 * 900 + actual_steel_area = 81 * 200 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + +def test_double_lift_core_b(get_materials): + """Tests the double_lift_core_b() method.""" + conc_mat, steel_mat = get_materials + + wall1 = cs.double_lift_core_b( + d=6000, + b=3000, + t1=250, + t2=220, + t3=180, + a=1500, + dia_bar=24, + area_bar=450, + spacing=300, + cover=50, + double=False, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall1.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall1.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 6000 * 3000 - 5320 * 2560 - 2 * 220 * 1500 + actual_steel_area = (21 + 2 * 10 + 9 + 2 * 3 + 6) * 450 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + wall2 = cs.double_lift_core_b( + d=6000, + b=3000, + t1=250, + t2=220, + t3=180, + a=1500, + dia_bar=24, + area_bar=450, + spacing=300, + cover=50, + double=True, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall2.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall2.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 6000 * 3000 - 5320 * 2560 - 2 * 220 * 1500 + actual_steel_area = 119 * 450 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + +def test_stairwell(get_materials): + """Tests the stairwell() method.""" + conc_mat, steel_mat = get_materials + + wall1 = cs.stairwell( + d=5000, + b=2500, + t1=180, + t2=200, + a=900, + dia_bar=16, + area_bar=200, + spacing=200, + cover=20, + double=False, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall1.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall1.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 5000 * 2500 - 4640 * 2100 - 200 * 900 + actual_steel_area = (26 + 12 + 12 + 20) * 200 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + wall2 = cs.stairwell( + d=5000, + b=2500, + t1=180, + t2=200, + a=900, + dia_bar=16, + area_bar=200, + spacing=200, + cover=20, + double=True, + n_circle=12, + conc_mat=conc_mat, + steel_mat=steel_mat, + ) + + # check no overlaps + polygons = [sec_geom.geom for sec_geom in wall2.geoms] + assert not check_geometry_overlaps(polygons) + + # check geometry is created correctly + conc_area = 0 + steel_area = 0 + + for geom in wall2.geoms: + if geom.material == conc_mat: + conc_area += geom.calculate_area() + elif geom.material == steel_mat: + steel_area += geom.calculate_area() + + gross_area = 5000 * 2500 - 4640 * 2100 - 200 * 900 + actual_steel_area = 139 * 200 + + # check areas + check.almost_equal(conc_area, gross_area - actual_steel_area, rel=r_tol) + check.almost_equal(steel_area, actual_steel_area, rel=r_tol) + + def test_add_bar(): """Tests the add_bar() method.""" rect = ps.rectangular_section(b=400, d=600) diff --git a/uv.lock b/uv.lock index 07587798..85226842 100644 --- a/uv.lock +++ b/uv.lock @@ -280,6 +280,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" @@ -753,6 +765,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/94/5c/368ae6c01c7628438358e6d337c19b05425727fbb221d2a3c4303c372f42/ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5", size = 117173 }, ] +[[package]] +name = "ipympl" +version = "0.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ipython" }, + { name = "ipython-genutils" }, + { name = "ipywidgets" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "pillow" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/64/05a5051f1fe457331f3d4353ad28e2e2ec4c98caaee0a087f0108ab9a40c/ipympl-0.9.4.tar.gz", hash = "sha256:cfb53c5b4fcbcee6d18f095eecfc6c6c474303d5b744e72cc66e7a2804708907", size = 58552737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/50/d6f4e7eb0e7d198b431d8259bc9a6035f879d4df487448b196cdf1e84155/ipympl-0.9.4-py3-none-any.whl", hash = "sha256:5b0c08c6f4f6ea655ba58239363457c10fb921557f5038c1a46db4457d6d6b0e", size = 516304 }, +] + [[package]] name = "ipython" version = "8.29.0" @@ -775,6 +805,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c5/a5/c15ed187f1b3fac445bb42a2dedd8dec1eee1718b35129242049a13a962f/ipython-8.29.0-py3-none-any.whl", hash = "sha256:0188a1bd83267192123ccea7f4a8ed0a78910535dbaa3f37671dca76ebd429c8", size = 819911 }, ] +[[package]] +name = "ipython-genutils" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/69/fbeffffc05236398ebfcfb512b6d2511c622871dca1746361006da310399/ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8", size = 22208 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/bc/9bd3b5c2b4774d5f33b2d544f1460be9df7df2fe42f352135381c347c69a/ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", size = 26343 }, +] + [[package]] name = "ipywidgets" version = "8.1.5" @@ -2125,7 +2164,7 @@ wheels = [ [[package]] name = "sectionproperties" -version = "3.5.0" +version = "3.6.0" source = { editable = "." } dependencies = [ { name = "cytriangle" }, @@ -2153,13 +2192,17 @@ rhino = [ ] [package.dev-dependencies] +dev = [ + { name = "ipympl" }, + { name = "notebook" }, + { name = "sphinx-autobuild" }, +] docs = [ { name = "furo" }, { name = "ipykernel" }, { name = "ipython" }, { name = "nbconvert" }, { name = "nbsphinx" }, - { name = "notebook" }, { name = "sphinx" }, { name = "sphinx-copybutton" }, { name = "sphinxext-opengraph" }, @@ -2193,13 +2236,17 @@ requires-dist = [ ] [package.metadata.requires-dev] +dev = [ + { name = "ipympl", specifier = "==0.9.4" }, + { name = "notebook", specifier = "==7.2.2" }, + { name = "sphinx-autobuild", specifier = "==2024.10.3" }, +] docs = [ { name = "furo", specifier = "==2024.8.6" }, { name = "ipykernel", specifier = "==6.29.5" }, { name = "ipython", specifier = "==8.29.0" }, { name = "nbconvert", specifier = "==7.16.4" }, { name = "nbsphinx", specifier = "==0.9.5" }, - { name = "notebook", specifier = "==7.2.2" }, { name = "sphinx", specifier = "==8.1.3" }, { name = "sphinx-copybutton", specifier = "==0.5.2" }, { name = "sphinxext-opengraph", specifier = "==0.9.1" }, @@ -2327,6 +2374,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" @@ -2431,6 +2495,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 = "tbb" version = "2022.0.0" @@ -2562,6 +2638,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.0" @@ -2576,6 +2666,59 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c8/15/828ec11907aee2349a9342fa71fba4ba7f3af938162a382dd7da339dea16/virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655", size = 3110969 }, ] +[[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" @@ -2612,6 +2755,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 = "14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/1b/380b883ce05bb5f45a905b61790319a28958a9ab1e4b6b95ff5464b60ca1/websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8", size = 162840 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/91/b1b375dbd856fd5fff3f117de0e520542343ecaf4e8fc60f1ac1e9f5822c/websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29", size = 161950 }, + { url = "https://files.pythonhosted.org/packages/61/8f/4d52f272d3ebcd35e1325c646e98936099a348374d4a6b83b524bded8116/websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179", size = 159601 }, + { url = "https://files.pythonhosted.org/packages/c4/b1/29e87b53eb1937992cdee094a0988aadc94f25cf0b37e90c75eed7123d75/websockets-14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2177ee3901075167f01c5e335a6685e71b162a54a89a56001f1c3e9e3d2ad250", size = 159854 }, + { url = "https://files.pythonhosted.org/packages/3f/e6/752a2f5e8321ae2a613062676c08ff2fccfb37dc837a2ee919178a372e8a/websockets-14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f14a96a0034a27f9d47fd9788913924c89612225878f8078bb9d55f859272b0", size = 168835 }, + { url = "https://files.pythonhosted.org/packages/60/27/ca62de7877596926321b99071639275e94bb2401397130b7cf33dbf2106a/websockets-14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f874ba705deea77bcf64a9da42c1f5fc2466d8f14daf410bc7d4ceae0a9fcb0", size = 167844 }, + { url = "https://files.pythonhosted.org/packages/7e/db/f556a1d06635c680ef376be626c632e3f2bbdb1a0189d1d1bffb061c3b70/websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199", size = 168157 }, + { url = "https://files.pythonhosted.org/packages/b3/bc/99e5f511838c365ac6ecae19674eb5e94201aa4235bd1af3e6fa92c12905/websockets-14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bea45f19b7ca000380fbd4e02552be86343080120d074b87f25593ce1700ad58", size = 168561 }, + { url = "https://files.pythonhosted.org/packages/c6/e7/251491585bad61c79e525ac60927d96e4e17b18447cc9c3cfab47b2eb1b8/websockets-14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:219c8187b3ceeadbf2afcf0f25a4918d02da7b944d703b97d12fb01510869078", size = 167979 }, + { url = "https://files.pythonhosted.org/packages/ac/98/7ac2e4eeada19bdbc7a3a66a58e3ebdf33648b9e1c5b3f08c3224df168cf/websockets-14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad2ab2547761d79926effe63de21479dfaf29834c50f98c4bf5b5480b5838434", size = 167925 }, + { url = "https://files.pythonhosted.org/packages/ab/3d/09e65c47ee2396b7482968068f6e9b516221e1032b12dcf843b9412a5dfb/websockets-14.1-cp310-cp310-win32.whl", hash = "sha256:1288369a6a84e81b90da5dbed48610cd7e5d60af62df9851ed1d1d23a9069f10", size = 162831 }, + { url = "https://files.pythonhosted.org/packages/8a/67/59828a3d09740e6a485acccfbb66600632f2178b6ed1b61388ee96f17d5a/websockets-14.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0744623852f1497d825a49a99bfbec9bea4f3f946df6eb9d8a2f0c37a2fec2e", size = 163266 }, + { url = "https://files.pythonhosted.org/packages/97/ed/c0d03cb607b7fe1f7ff45e2cd4bb5cd0f9e3299ced79c2c303a6fff44524/websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512", size = 161949 }, + { url = "https://files.pythonhosted.org/packages/06/91/bf0a44e238660d37a2dda1b4896235d20c29a2d0450f3a46cd688f43b239/websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac", size = 159606 }, + { url = "https://files.pythonhosted.org/packages/ff/b8/7185212adad274c2b42b6a24e1ee6b916b7809ed611cbebc33b227e5c215/websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280", size = 159854 }, + { url = "https://files.pythonhosted.org/packages/5a/8a/0849968d83474be89c183d8ae8dcb7f7ada1a3c24f4d2a0d7333c231a2c3/websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1", size = 169402 }, + { url = "https://files.pythonhosted.org/packages/bd/4f/ef886e37245ff6b4a736a09b8468dae05d5d5c99de1357f840d54c6f297d/websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3", size = 168406 }, + { url = "https://files.pythonhosted.org/packages/11/43/e2dbd4401a63e409cebddedc1b63b9834de42f51b3c84db885469e9bdcef/websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6", size = 168776 }, + { url = "https://files.pythonhosted.org/packages/6d/d6/7063e3f5c1b612e9f70faae20ebaeb2e684ffa36cb959eb0862ee2809b32/websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0", size = 169083 }, + { url = "https://files.pythonhosted.org/packages/49/69/e6f3d953f2fa0f8a723cf18cd011d52733bd7f6e045122b24e0e7f49f9b0/websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89", size = 168529 }, + { url = "https://files.pythonhosted.org/packages/70/ff/f31fa14561fc1d7b8663b0ed719996cf1f581abee32c8fb2f295a472f268/websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23", size = 168475 }, + { url = "https://files.pythonhosted.org/packages/f1/15/b72be0e4bf32ff373aa5baef46a4c7521b8ea93ad8b49ca8c6e8e764c083/websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e", size = 162833 }, + { url = "https://files.pythonhosted.org/packages/bc/ef/2d81679acbe7057ffe2308d422f744497b52009ea8bab34b6d74a2657d1d/websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09", size = 163263 }, + { url = "https://files.pythonhosted.org/packages/55/64/55698544ce29e877c9188f1aee9093712411a8fc9732cca14985e49a8e9c/websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed", size = 161957 }, + { url = "https://files.pythonhosted.org/packages/a2/b1/b088f67c2b365f2c86c7b48edb8848ac27e508caf910a9d9d831b2f343cb/websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d", size = 159620 }, + { url = "https://files.pythonhosted.org/packages/c1/89/2a09db1bbb40ba967a1b8225b07b7df89fea44f06de9365f17f684d0f7e6/websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707", size = 159852 }, + { url = "https://files.pythonhosted.org/packages/ca/c1/f983138cd56e7d3079f1966e81f77ce6643f230cd309f73aa156bb181749/websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a", size = 169675 }, + { url = "https://files.pythonhosted.org/packages/c1/c8/84191455d8660e2a0bdb33878d4ee5dfa4a2cedbcdc88bbd097303b65bfa/websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45", size = 168619 }, + { url = "https://files.pythonhosted.org/packages/8d/a7/62e551fdcd7d44ea74a006dc193aba370505278ad76efd938664531ce9d6/websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58", size = 169042 }, + { url = "https://files.pythonhosted.org/packages/ad/ed/1532786f55922c1e9c4d329608e36a15fdab186def3ca9eb10d7465bc1cc/websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058", size = 169345 }, + { url = "https://files.pythonhosted.org/packages/ea/fb/160f66960d495df3de63d9bcff78e1b42545b2a123cc611950ffe6468016/websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4", size = 168725 }, + { url = "https://files.pythonhosted.org/packages/cf/53/1bf0c06618b5ac35f1d7906444b9958f8485682ab0ea40dee7b17a32da1e/websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05", size = 168712 }, + { url = "https://files.pythonhosted.org/packages/e5/22/5ec2f39fff75f44aa626f86fa7f20594524a447d9c3be94d8482cd5572ef/websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0", size = 162838 }, + { url = "https://files.pythonhosted.org/packages/74/27/28f07df09f2983178db7bf6c9cccc847205d2b92ced986cd79565d68af4f/websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f", size = 163277 }, + { url = "https://files.pythonhosted.org/packages/fb/cd/382a05a1ba2a93bd9fb807716a660751295df72e77204fb130a102fcdd36/websockets-14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5dc25a9dbd1a7f61eca4b7cb04e74ae4b963d658f9e4f9aad9cd00b688692c8", size = 159633 }, + { url = "https://files.pythonhosted.org/packages/b7/a0/fa7c62e2952ef028b422fbf420f9353d9dd4dfaa425de3deae36e98c0784/websockets-14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:04a97aca96ca2acedf0d1f332c861c5a4486fdcba7bcef35873820f940c4231e", size = 159867 }, + { url = "https://files.pythonhosted.org/packages/c1/94/954b4924f868db31d5f0935893c7a8446515ee4b36bb8ad75a929469e453/websockets-14.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df174ece723b228d3e8734a6f2a6febbd413ddec39b3dc592f5a4aa0aff28098", size = 161121 }, + { url = "https://files.pythonhosted.org/packages/7a/2e/f12bbb41a8f2abb76428ba4fdcd9e67b5b364a3e7fa97c88f4d6950aa2d4/websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034feb9f4286476f273b9a245fb15f02c34d9586a5bc936aff108c3ba1b21beb", size = 160731 }, + { url = "https://files.pythonhosted.org/packages/13/97/b76979401f2373af1fe3e08f960b265cecab112e7dac803446fb98351a52/websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c308dabd2b380807ab64b62985eaccf923a78ebc572bd485375b9ca2b7dc7", size = 160681 }, + { url = "https://files.pythonhosted.org/packages/39/9c/16916d9a436c109a1d7ba78817e8fee357b78968be3f6e6f517f43afa43d/websockets-14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a42d3ecbb2db5080fc578314439b1d79eef71d323dc661aa616fb492436af5d", size = 163316 }, + { url = "https://files.pythonhosted.org/packages/b0/0b/c7e5d11020242984d9d37990310520ed663b942333b83a033c2f20191113/websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e", size = 156277 }, +] + [[package]] name = "widgetsnbextension" version = "4.0.13"