-
Notifications
You must be signed in to change notification settings - Fork 111
Add yield moment calculation #476
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -202,6 +202,7 @@ def calculate_geometric_properties(self) -> None: | |
| - Centroidal section moduli | ||
| - Radii of gyration | ||
| - Principal axis properties | ||
| - Yield moments (composite only) | ||
| """ | ||
|
|
||
| def calculate_geom(progress: Progress | None = None) -> None: | ||
|
|
@@ -259,11 +260,87 @@ def calculate_geom(progress: Progress | None = None) -> None: | |
| ) | ||
| self.section_props.e_eff = self.section_props.ea / self.section_props.area | ||
| self.section_props.g_eff = self.section_props.ga / self.section_props.area | ||
|
|
||
| # calculate derived properties | ||
| self.section_props.calculate_elastic_centroid() | ||
| self.section_props.calculate_centroidal_properties( | ||
| node_list=self.mesh["vertices"] | ||
| ) | ||
|
|
||
| # calculate yield moments | ||
| self.section_props.my_xx = 0.0 | ||
| self.section_props.my_yy = 0.0 | ||
| self.section_props.my_11 = 0.0 | ||
| self.section_props.my_22 = 0.0 | ||
|
|
||
| # calculate yield moments: | ||
| # 1) loop through each material group and through each element in the group | ||
| # 2) for each point, calculate the bending stress from a unit bending moment | ||
| # in each direction (mxx, myy, m11, m22) | ||
| # 3) from this, calculate the yield index for each point | ||
| # 4) get the largest yield index and scale the bending moment such that the | ||
| # yield index is 1 | ||
|
|
||
| # initialise the yield indexes | ||
| yield_index = { | ||
| "mxx": 0.0, | ||
| "myy": 0.0, | ||
| "m11": 0.0, | ||
| "m22": 0.0, | ||
| } | ||
|
|
||
| # get useful section properties | ||
| cx = self.section_props.cx | ||
| cy = self.section_props.cy | ||
| phi = self.section_props.phi | ||
| ixx = self.section_props.ixx_c | ||
| iyy = self.section_props.iyy_c | ||
| ixy = self.section_props.ixy_c | ||
| i11 = self.section_props.i11_c | ||
| i22 = self.section_props.i22_c | ||
|
|
||
| if ixx is None or iyy is None or ixy is None or i11 is None or i22 is None: | ||
| msg = "Section properties failed to save." | ||
| raise RuntimeError(msg) | ||
|
|
||
| # loop through each material group | ||
| for group in self.material_groups: | ||
| em = group.material.elastic_modulus | ||
| fy = group.material.yield_strength | ||
|
|
||
| # loop through each element in the material group | ||
| for el in group.elements: | ||
| # loop through each node in the element | ||
| for coord in el.coords.transpose(): | ||
| # calculate coordinates wrt centroidal & principal axes | ||
| x = coord[0] - cx | ||
| y = coord[1] - cy | ||
| x11, y22 = fea.principal_coordinate(phi=phi, x=x, y=y) | ||
|
|
||
| # calculate bending stresses due to unit moments | ||
| sig_mxx = em * ( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part is pure numerical, can be |
||
| -ixy / (ixx * iyy - ixy**2) * x | ||
| + iyy / (ixx * iyy - ixy**2) * y | ||
| ) | ||
| sig_myy = em * ( | ||
| -ixx / (ixx * iyy - ixy**2) * x | ||
| + ixy / (ixx * iyy - ixy**2) * y | ||
| ) | ||
| sig_m11 = em / i11 * y22 | ||
| sig_m22 = -em / i22 * x11 | ||
|
|
||
| # update yield indexes | ||
| yield_index["mxx"] = max(yield_index["mxx"], abs(sig_mxx / fy)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one is minor, why not directly store: |
||
| yield_index["myy"] = max(yield_index["myy"], abs(sig_myy / fy)) | ||
| yield_index["m11"] = max(yield_index["m11"], abs(sig_m11 / fy)) | ||
| yield_index["m22"] = max(yield_index["m22"], abs(sig_m22 / fy)) | ||
|
|
||
| # calculate yield moments | ||
| self.section_props.my_xx = 1.0 / yield_index["mxx"] | ||
| self.section_props.my_yy = 1.0 / yield_index["myy"] | ||
| self.section_props.my_11 = 1.0 / yield_index["m11"] | ||
| self.section_props.my_22 = 1.0 / yield_index["m22"] | ||
|
|
||
| if progress and task is not None: | ||
| msg = "[bold green]:white_check_mark: Geometric analysis complete" | ||
| progress.update(task_id=task, description=msg) | ||
|
|
@@ -1120,7 +1197,7 @@ def calculate_plastic_properties( | |
|
|
||
| - Plastic centroids (centroidal and principal axes) | ||
| - Plastic section moduli (centroidal and principal axes) | ||
| - Shape factors, non-composite only (centroidal and principal axe) | ||
| - Shape factors, non-composite only (centroidal and principal axes) | ||
| """ | ||
| # check that a geometric analysis has been performed | ||
| if self.section_props.cx is None: | ||
|
|
@@ -2240,6 +2317,32 @@ def get_ez( | |
| self.section_props.zyy_minus / e_ref, | ||
| ) | ||
|
|
||
| def get_my(self) -> tuple[float, float]: | ||
| """Returns the yield moment for bending about the centroidal axis. | ||
|
|
||
| This is a composite only property, as such this can only be returned if material | ||
| properties have been applied to the cross-section. | ||
|
|
||
| Returns: | ||
| Yield moment for bending about the centroidal ``x`` and ``y`` axes | ||
| (``my_xx``, ``my_yy``) | ||
|
|
||
| Raises: | ||
| RuntimeError: If material properties have *not* been applied | ||
| RuntimeError: If a geometric analysis has not been performed | ||
| """ | ||
| if not self.is_composite(): | ||
| msg = "Attempting to get a composite only property for a geometric analysis" | ||
| msg += " (material properties have not been applied). Consider using" | ||
| msg += " get_z()." | ||
| raise RuntimeError(msg) | ||
|
|
||
| if self.section_props.my_xx is None or self.section_props.my_yy is None: | ||
| msg = "Conduct a geometric analysis." | ||
| raise RuntimeError(msg) | ||
|
|
||
| return (self.section_props.my_xx, self.section_props.my_yy) | ||
|
|
||
| def get_rc(self) -> tuple[float, float]: | ||
| """Returns the cross-section centroidal radii of gyration. | ||
|
|
||
|
|
@@ -2418,6 +2521,32 @@ def get_ezp( | |
| self.section_props.z22_minus / e_ref, | ||
| ) | ||
|
|
||
| def get_my_p(self) -> tuple[float, float]: | ||
| """Returns the yield moment for bending about the principal axis. | ||
|
|
||
| This is a composite only property, as such this can only be returned if material | ||
| properties have been applied to the cross-section. | ||
|
|
||
| Returns: | ||
| Yield moment for bending about the principal ``11`` and ``22`` axes | ||
| (``my_11``, ``my_22``) | ||
|
|
||
| Raises: | ||
| RuntimeError: If material properties have *not* been applied | ||
| RuntimeError: If a geometric analysis has not been performed | ||
| """ | ||
| if not self.is_composite(): | ||
| msg = "Attempting to get a composite only property for a geometric analysis" | ||
| msg += " (material properties have not been applied). Consider using" | ||
| msg += " get_zp()." | ||
| raise RuntimeError(msg) | ||
|
|
||
| if self.section_props.my_11 is None or self.section_props.my_22 is None: | ||
| msg = "Conduct a geometric analysis." | ||
| raise RuntimeError(msg) | ||
|
|
||
| return (self.section_props.my_11, self.section_props.my_22) | ||
|
|
||
| def get_rp(self) -> tuple[float, float]: | ||
| """Returns the cross-section principal radii of gyration. | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not directly using the second moment of inertial and figure our the farthest fibre in each group, similar to elastic section modulus to figure out the yield moment for each group?
Looping over all elements seems to be very slow when the mesh is fine.
Reusing existing information may be beneficial here.