Skip to content

Commit 8459928

Browse files
Merge pull request #252 from robbievanleeuwen/concrete_column_section
Update concrete_sections library to have a unified approach to working with bar diameters and areas. Added `concrete_column_section`.
2 parents f942d5c + 2000e3d commit 8459928

File tree

2 files changed

+281
-60
lines changed

2 files changed

+281
-60
lines changed

sectionproperties/pre/library/concrete_sections.py

Lines changed: 183 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from typing import Union, Optional
12
import numpy as np
23
from shapely.geometry import Polygon
34
import sectionproperties.pre.pre as pre
@@ -106,53 +107,40 @@ def concrete_rectangular_section(
106107
x_i_side_right = b - x_i_side_left
107108
spacing_side = (d - 2 * cover - dia_top / 2 - dia_bot / 2) / (n_side + 1)
108109

110+
if area_top is None:
111+
area_top = np.pi * dia_top**2 / 4
112+
if area_bot is None:
113+
area_bot = np.pi * dia_bot**2 / 4
114+
if area_side is None and dia_side is not None:
115+
area_side = np.pi * dia_side**2 / 4
116+
109117
# add top bars
110118
for i in range(n_top):
111-
if area_top:
112-
bar = primitive_sections.circular_section_by_area(
113-
area=area_top, n=n_circle, material=steel_mat
114-
)
115-
else:
116-
bar = primitive_sections.circular_section(
117-
d=dia_top, n=n_circle, material=steel_mat
118-
)
119-
119+
bar = primitive_sections.circular_section_by_area(
120+
area=area_top, n=n_circle, material=steel_mat
121+
)
120122
bar = bar.shift_section(
121123
x_offset=x_i_top + spacing_top * i, y_offset=d - cover - dia_top / 2
122124
)
123-
124125
geom = (geom - bar) + bar
125126

126127
# add bot bars
127128
for i in range(n_bot):
128-
if area_bot:
129-
bar = primitive_sections.circular_section_by_area(
130-
area=area_bot, n=n_circle, material=steel_mat
131-
)
132-
else:
133-
bar = primitive_sections.circular_section(
134-
d=dia_bot, n=n_circle, material=steel_mat
135-
)
136-
129+
bar = primitive_sections.circular_section_by_area(
130+
area=area_bot, n=n_circle, material=steel_mat
131+
)
137132
bar = bar.shift_section(
138133
x_offset=x_i_bot + spacing_bot * i, y_offset=cover + dia_bot / 2
139134
)
140-
141135
geom = (geom - bar) + bar
142136

143137
# add side bars if specified
144138
if n_side != 0:
145139
for i in range(n_side):
146-
if area_side:
147-
bar_left = primitive_sections.circular_section_by_area(
148-
area=area_side, n=n_circle, material=steel_mat
149-
)
150-
bar_right = bar_left
151-
else:
152-
bar_left = primitive_sections.circular_section(
153-
d=dia_side, n=n_circle, material=steel_mat
154-
)
155-
bar_right = bar_left
140+
bar_left = primitive_sections.circular_section_by_area(
141+
area=area_side, n=n_circle, material=steel_mat
142+
)
143+
bar_right = bar_left
156144

157145
bar_left = bar_left.shift_section(
158146
x_offset=x_i_side_left,
@@ -168,6 +156,129 @@ def concrete_rectangular_section(
168156
return geom
169157

170158

159+
def concrete_column_section(
160+
b: float,
161+
d: float,
162+
cover: float,
163+
n_bars_b: int,
164+
n_bars_d: int,
165+
dia_bar: float,
166+
bar_area: Optional[float] = None,
167+
conc_mat: pre.Material = pre.DEFAULT_MATERIAL,
168+
steel_mat: pre.Material = pre.DEFAULT_MATERIAL,
169+
filled: bool = False,
170+
n_circle: int = 4,
171+
) -> geometry.CompoundGeometry:
172+
"""Constructs a concrete rectangular section of width *b* and depth *d*, with
173+
steel bar reinforcing organized as an *n_bars_b* by *n_bars_d* array, discretised
174+
with *n_circle* points with equal sides and top/bottom *cover* to the steel which
175+
is taken as the clear cover (edge of bar to edge of concrete).
176+
177+
:param float b: Concrete section width, parallel to the x-axis
178+
:param float d: Concrete section depth, parallel to the y-axis
179+
:param float cover: Clear cover, calculated as distance from edge of reinforcing bar to edge of section.
180+
:param int n_bars_b: Number of bars placed across the width of the section, minimum 2.
181+
:param int n_bars_d: Number of bars placed across the depth of the section, minimum 2.
182+
:param float dia_bar: Diameter of reinforcing bars. Used for calculating bar placement and,
183+
optionally, for calculating the bar area for section capacity calculations.
184+
:param Optional[float] bar_area: Area of reinforcing bars. Used for section capacity calculations.
185+
If not provided, then dia_bar will be used to calculate the bar area.
186+
:param Optional[sectionproperties.pre.pre.Material] conc_mat: Material to
187+
associate with the concrete
188+
:param Optional[sectionproperties.pre.pre.Material] steel_mat: Material to
189+
associate with the reinforcing steel
190+
:param bool filled: When True, will populate the concrete section with an equally
191+
spaced 2D array of reinforcing bars numbering 'n_bars_b' by 'n_bars_d'.
192+
When False, only the bars around the perimeter of the array will be present.
193+
:param int n_circle: The number of points used to discretize the circle of the reinforcing
194+
bars. The bars themselves will have an exact area of 'bar_area' regardless of the
195+
number of points used in the circle. Useful for making the reinforcing bars look
196+
more circular when plotting the concrete section.
197+
198+
:raises ValueError: If the number of bars in either 'n_bars_b' or 'n_bars_d' is not greater
199+
than or equal to 2.
200+
201+
The following example creates a 600D x 300W concrete column with 25 mm diameter
202+
reinforcing bars each with 500 mm**2 area and 35 mm cover in a 3x6 array without
203+
the interior bars being filled::
204+
205+
from sectionproperties.pre.library.concrete_sections import concrete_column_section
206+
from sectionproperties.pre.pre import Material
207+
208+
concrete = Material(
209+
name='Concrete', elastic_modulus=30.1e3, poissons_ratio=0.2, yield_strength=32,
210+
density=2.4e-6, color='lightgrey'
211+
)
212+
steel = Material(
213+
name='Steel', elastic_modulus=200e3, poissons_ratio=0.3, yield_strength=500,
214+
density=7.85e-6, color='grey'
215+
)
216+
217+
geometry = concrete_column_section(
218+
b=300, d=600, dia_bar=25, bar_area=500, cover=35, n_bars_b=3, n_bars_d=6,
219+
conc_mat=concrete, steel_mat=steel, filled=False, n_circle=4
220+
)
221+
geometry.create_mesh(mesh_sizes=[500])
222+
223+
.. figure:: ../images/sections/concrete_rectangular_section_geometry.png
224+
:align: center
225+
:scale: 50 %
226+
227+
Concrete rectangular section geometry.
228+
229+
.. figure:: ../images/sections/concrete_rectangular_section_mesh.png
230+
:align: center
231+
:scale: 50 %
232+
233+
Mesh generated from the above geometry.
234+
"""
235+
concrete_geometry = primitive_sections.rectangular_section(b, d, material=conc_mat)
236+
bar_extents = concrete_geometry.offset_perimeter(
237+
-cover - dia_bar / 2
238+
).calculate_extents()
239+
bar_x_min, bar_x_max, bar_y_min, bar_y_max = bar_extents
240+
241+
b_edge_bars_x = np.linspace(bar_x_min, bar_x_max, n_bars_b)
242+
d_edge_bars_y = np.linspace(bar_y_min, bar_y_max, n_bars_d)
243+
244+
if not filled:
245+
b_edge_bars_y1 = [bar_y_min] * n_bars_b
246+
b_edge_bars_y2 = [bar_y_max] * n_bars_b
247+
248+
d_edge_bars_x1 = [bar_x_min] * n_bars_d
249+
d_edge_bars_x2 = [bar_x_max] * n_bars_d
250+
251+
b_edge_bars_top = list(zip(b_edge_bars_x, b_edge_bars_y2))
252+
b_edge_bars_bottom = list(zip(b_edge_bars_x, b_edge_bars_y1))
253+
d_edge_bars_right = list(zip(d_edge_bars_x2, d_edge_bars_y))
254+
d_edge_bars_left = list(zip(d_edge_bars_x1, d_edge_bars_y))
255+
256+
all_bar_coords = list(
257+
set(
258+
b_edge_bars_top
259+
+ b_edge_bars_bottom
260+
+ d_edge_bars_right
261+
+ d_edge_bars_left
262+
)
263+
)
264+
if filled:
265+
xy = np.meshgrid(b_edge_bars_x, d_edge_bars_y)
266+
all_bar_coords = np.append(xy[0].reshape(-1, 1), xy[1].reshape(-1, 1), axis=1)
267+
268+
if bar_area is None:
269+
bar_area = np.pi * dia_bar**2 / 4
270+
for bar_coord in all_bar_coords:
271+
concrete_geometry = add_bar(
272+
concrete_geometry,
273+
area=bar_area,
274+
material=steel_mat,
275+
x=bar_coord[0],
276+
y=bar_coord[1],
277+
n=n_circle,
278+
)
279+
return concrete_geometry
280+
281+
171282
def concrete_tee_section(
172283
b: float,
173284
d: float,
@@ -261,38 +372,29 @@ def concrete_tee_section(
261372
spacing_top = (b_f - 2 * cover - dia_top) / (n_top - 1)
262373
spacing_bot = (b - 2 * cover - dia_bot) / (n_bot - 1)
263374

375+
if area_top is None:
376+
area_top = np.pi * dia_top**2 / 4
377+
if area_bot is None:
378+
area_bot = np.pi * dia_bot**2 / 4
379+
264380
# add top bars
265381
for i in range(n_top):
266-
if area_top:
267-
bar = primitive_sections.circular_section_by_area(
268-
area=area_top, n=n_circle, material=steel_mat
269-
)
270-
else:
271-
bar = primitive_sections.circular_section(
272-
d=dia_top, n=n_circle, material=steel_mat
273-
)
274-
382+
bar = primitive_sections.circular_section_by_area(
383+
area=area_top, n=n_circle, material=steel_mat
384+
)
275385
bar = bar.shift_section(
276386
x_offset=x_i_top + spacing_top * i, y_offset=d - cover - dia_top / 2
277387
)
278-
279388
geom = (geom - bar) + bar
280389

281390
# add bot bars
282391
for i in range(n_bot):
283-
if area_bot:
284-
bar = primitive_sections.circular_section_by_area(
285-
area=area_bot, n=n_circle, material=steel_mat
286-
)
287-
else:
288-
bar = primitive_sections.circular_section(
289-
d=dia_bot, n=n_circle, material=steel_mat
290-
)
291-
392+
bar = primitive_sections.circular_section_by_area(
393+
area=area_bot, n=n_circle, material=steel_mat
394+
)
292395
bar = bar.shift_section(
293396
x_offset=x_i_bot + spacing_bot * i, y_offset=cover + dia_bot / 2
294397
)
295-
296398
geom = (geom - bar) + bar
297399

298400
return geom
@@ -380,20 +482,41 @@ def concrete_circular_section(
380482
r = d / 2 - cover - dia / 2
381483
d_theta = 2 * np.pi / n_bar
382484

485+
if area_bar is None:
486+
area_bar = np.pi * dia**2 / 4
383487
for i in range(n_bar):
384-
if area_bar:
385-
bar = primitive_sections.circular_section_by_area(
386-
area=area_bar, n=n_circle, material=steel_mat
387-
)
388-
else:
389-
bar = primitive_sections.circular_section(
390-
d=dia, n=n_circle, material=steel_mat
391-
)
392-
488+
bar = primitive_sections.circular_section_by_area(
489+
area=area_bar, n=n_circle, material=steel_mat
490+
)
393491
bar = bar.shift_section(
394492
x_offset=r * np.cos(i * d_theta), y_offset=r * np.sin(i * d_theta)
395493
)
396-
397494
geom = (geom - bar) + bar
398495

399496
return geom
497+
498+
499+
def add_bar(
500+
geometry: Union[geometry.Geometry, geometry.CompoundGeometry],
501+
area: float,
502+
material: pre.DEFAULT_MATERIAL,
503+
x: float,
504+
y: float,
505+
n: int = 4,
506+
) -> geometry.CompoundGeometry:
507+
"""Adds a reinforcing bar to a *sectionproperties* geometry.
508+
Bars are discretised by four points by default.
509+
:param geometry: Reinforced concrete geometry to which the new bar will be added
510+
:param area: Bar cross-sectional area
511+
:param material: Material object for the bar
512+
:param x: x-position of the bar
513+
:param y: y-position of the bar
514+
:param n: Number of points to discretise the bar circle
515+
:return: Reinforced concrete geometry with added bar
516+
"""
517+
518+
bar = primitive_sections.circular_section_by_area(
519+
area=area, n=n, material=material # type: ignore
520+
).shift_section(x_offset=x, y_offset=y)
521+
522+
return (geometry - bar) + bar

0 commit comments

Comments
 (0)