@@ -202,6 +202,7 @@ def calculate_geometric_properties(self) -> None:
202202 - Centroidal section moduli
203203 - Radii of gyration
204204 - Principal axis properties
205+ - Yield moments (composite only)
205206 """
206207
207208 def calculate_geom (progress : Progress | None = None ) -> None :
@@ -259,11 +260,87 @@ def calculate_geom(progress: Progress | None = None) -> None:
259260 )
260261 self .section_props .e_eff = self .section_props .ea / self .section_props .area
261262 self .section_props .g_eff = self .section_props .ga / self .section_props .area
263+
264+ # calculate derived properties
262265 self .section_props .calculate_elastic_centroid ()
263266 self .section_props .calculate_centroidal_properties (
264267 node_list = self .mesh ["vertices" ]
265268 )
266269
270+ # calculate yield moments
271+ self .section_props .my_xx = 0.0
272+ self .section_props .my_yy = 0.0
273+ self .section_props .my_11 = 0.0
274+ self .section_props .my_22 = 0.0
275+
276+ # calculate yield moments:
277+ # 1) loop through each material group and through each element in the group
278+ # 2) for each point, calculate the bending stress from a unit bending moment
279+ # in each direction (mxx, myy, m11, m22)
280+ # 3) from this, calculate the yield index for each point
281+ # 4) get the largest yield index and scale the bending moment such that the
282+ # yield index is 1
283+
284+ # initialise the yield indexes
285+ yield_index = {
286+ "mxx" : 0.0 ,
287+ "myy" : 0.0 ,
288+ "m11" : 0.0 ,
289+ "m22" : 0.0 ,
290+ }
291+
292+ # get useful section properties
293+ cx = self .section_props .cx
294+ cy = self .section_props .cy
295+ phi = self .section_props .phi
296+ ixx = self .section_props .ixx_c
297+ iyy = self .section_props .iyy_c
298+ ixy = self .section_props .ixy_c
299+ i11 = self .section_props .i11_c
300+ i22 = self .section_props .i22_c
301+
302+ if ixx is None or iyy is None or ixy is None or i11 is None or i22 is None :
303+ msg = "Section properties failed to save."
304+ raise RuntimeError (msg )
305+
306+ # loop through each material group
307+ for group in self .material_groups :
308+ em = group .material .elastic_modulus
309+ fy = group .material .yield_strength
310+
311+ # loop through each element in the material group
312+ for el in group .elements :
313+ # loop through each node in the element
314+ for coord in el .coords .transpose ():
315+ # calculate coordinates wrt centroidal & principal axes
316+ x = coord [0 ] - cx
317+ y = coord [1 ] - cy
318+ x11 , y22 = fea .principal_coordinate (phi = phi , x = x , y = y )
319+
320+ # calculate bending stresses due to unit moments
321+ sig_mxx = em * (
322+ - ixy / (ixx * iyy - ixy ** 2 ) * x
323+ + iyy / (ixx * iyy - ixy ** 2 ) * y
324+ )
325+ sig_myy = em * (
326+ - ixx / (ixx * iyy - ixy ** 2 ) * x
327+ + ixy / (ixx * iyy - ixy ** 2 ) * y
328+ )
329+ sig_m11 = em / i11 * y22
330+ sig_m22 = - em / i22 * x11
331+
332+ # update yield indexes
333+ yield_index ["mxx" ] = max (yield_index ["mxx" ], abs (sig_mxx / fy ))
334+ yield_index ["myy" ] = max (yield_index ["myy" ], abs (sig_myy / fy ))
335+ yield_index ["m11" ] = max (yield_index ["m11" ], abs (sig_m11 / fy ))
336+ yield_index ["m22" ] = max (yield_index ["m22" ], abs (sig_m22 / fy ))
337+
338+ # calculate yield moments
339+ self .section_props .my_xx = 1.0 / yield_index ["mxx" ]
340+ self .section_props .my_yy = 1.0 / yield_index ["myy" ]
341+ self .section_props .my_11 = 1.0 / yield_index ["m11" ]
342+ self .section_props .my_22 = 1.0 / yield_index ["m22" ]
343+
267344 if progress and task is not None :
268345 msg = "[bold green]:white_check_mark: Geometric analysis complete"
269346 progress .update (task_id = task , description = msg )
@@ -1120,7 +1197,7 @@ def calculate_plastic_properties(
11201197
11211198 - Plastic centroids (centroidal and principal axes)
11221199 - Plastic section moduli (centroidal and principal axes)
1123- - Shape factors, non-composite only (centroidal and principal axe )
1200+ - Shape factors, non-composite only (centroidal and principal axes )
11241201 """
11251202 # check that a geometric analysis has been performed
11261203 if self .section_props .cx is None :
@@ -2240,6 +2317,32 @@ def get_ez(
22402317 self .section_props .zyy_minus / e_ref ,
22412318 )
22422319
2320+ def get_my (self ) -> tuple [float , float ]:
2321+ """Returns the yield moment for bending about the centroidal axis.
2322+
2323+ This is a composite only property, as such this can only be returned if material
2324+ properties have been applied to the cross-section.
2325+
2326+ Returns:
2327+ Yield moment for bending about the centroidal ``x`` and ``y`` axes
2328+ (``my_xx``, ``my_yy``)
2329+
2330+ Raises:
2331+ RuntimeError: If material properties have *not* been applied
2332+ RuntimeError: If a geometric analysis has not been performed
2333+ """
2334+ if not self .is_composite ():
2335+ msg = "Attempting to get a composite only property for a geometric analysis"
2336+ msg += " (material properties have not been applied). Consider using"
2337+ msg += " get_z()."
2338+ raise RuntimeError (msg )
2339+
2340+ if self .section_props .my_xx is None or self .section_props .my_yy is None :
2341+ msg = "Conduct a geometric analysis."
2342+ raise RuntimeError (msg )
2343+
2344+ return (self .section_props .my_xx , self .section_props .my_yy )
2345+
22432346 def get_rc (self ) -> tuple [float , float ]:
22442347 """Returns the cross-section centroidal radii of gyration.
22452348
@@ -2418,6 +2521,32 @@ def get_ezp(
24182521 self .section_props .z22_minus / e_ref ,
24192522 )
24202523
2524+ def get_my_p (self ) -> tuple [float , float ]:
2525+ """Returns the yield moment for bending about the principal axis.
2526+
2527+ This is a composite only property, as such this can only be returned if material
2528+ properties have been applied to the cross-section.
2529+
2530+ Returns:
2531+ Yield moment for bending about the principal ``11`` and ``22`` axes
2532+ (``my_11``, ``my_22``)
2533+
2534+ Raises:
2535+ RuntimeError: If material properties have *not* been applied
2536+ RuntimeError: If a geometric analysis has not been performed
2537+ """
2538+ if not self .is_composite ():
2539+ msg = "Attempting to get a composite only property for a geometric analysis"
2540+ msg += " (material properties have not been applied). Consider using"
2541+ msg += " get_zp()."
2542+ raise RuntimeError (msg )
2543+
2544+ if self .section_props .my_11 is None or self .section_props .my_22 is None :
2545+ msg = "Conduct a geometric analysis."
2546+ raise RuntimeError (msg )
2547+
2548+ return (self .section_props .my_11 , self .section_props .my_22 )
2549+
24212550 def get_rp (self ) -> tuple [float , float ]:
24222551 """Returns the cross-section principal radii of gyration.
24232552
0 commit comments