1+ from typing import Union , Optional
12import numpy as np
23from shapely .geometry import Polygon
34import 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+
171282def 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