| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| __title__ = "Fem Tools for results" |
| __author__ = "Bernd Hahnebach" |
| __url__ = "https://www.freecad.org" |
|
|
| |
| |
|
|
| import numpy as np |
| from math import isnan |
|
|
| import FreeCAD |
|
|
| from femtools.femutils import is_of_type |
|
|
|
|
| def purge_result_objects(analysis): |
| """Removes all result objects and result meshes from an analysis group. |
| |
| Parameters |
| ---------- |
| analysis : Fem::FemAnalysis |
| analysis group as a container for all objects needed for the analysis |
| """ |
|
|
| |
| for m in analysis.Group: |
| if m.isDerivedFrom("Fem::FemResultObject"): |
| if m.Mesh and is_of_type(m.Mesh, "Fem::MeshResult"): |
| analysis.Document.removeObject(m.Mesh.Name) |
| analysis.Document.removeObject(m.Name) |
| analysis.Document.recompute() |
|
|
| |
| for m in analysis.Group: |
| if is_of_type(m, "Fem::MeshResult"): |
| analysis.Document.removeObject(m.Name) |
| analysis.Document.recompute() |
|
|
| |
| for m in analysis.Group: |
| if is_of_type(m, "App::TextDocument"): |
| analysis.Document.removeObject(m.Name) |
| analysis.Document.recompute() |
|
|
|
|
| def purge_postprocessing_objects(analysis): |
| """Removes all postprocessing objects and visualizations form the analysis |
| |
| Parameters |
| ---------- |
| analysis : Fem::FemAnalysis |
| analysis group as a container for all objects needed for the analysis |
| """ |
|
|
| |
| for m in analysis.Group: |
| if is_of_type(m, "Fem::FemPostPipeline"): |
| |
| analysis.Document.removeObject(m.Name) |
| analysis.Document.recompute() |
|
|
| |
| for m in analysis.Group: |
| if is_of_type(m, "Fem::FemPostVisualization"): |
| analysis.Document.removeObject(m.Name) |
| analysis.Document.recompute() |
|
|
|
|
| def purge_results(analysis): |
| """Removes all result and postprocessing objects and result meshes from an analysis group. |
| |
| Parameters |
| ---------- |
| analysis : Fem::FemAnalysis |
| analysis group as a container for all objects needed for the analysis |
| """ |
|
|
| |
| |
| |
| |
|
|
| purge_result_objects(analysis) |
| purge_postprocessing_objects(analysis) |
|
|
|
|
| def reset_mesh_deformation(resultobj): |
| """Resets result mesh deformation. |
| |
| Parameters |
| ---------- |
| resultobj : Fem::ResultMechanical |
| FreeCAD FEM mechanical result object |
| """ |
|
|
| if FreeCAD.GuiUp: |
| if resultobj.Mesh: |
| resultobj.Mesh.ViewObject.applyDisplacement(0.0) |
|
|
|
|
| def reset_mesh_color(resultobj): |
| """Resets result mesh color |
| |
| Parameters |
| ---------- |
| resultobj : Fem::ResultMechanical |
| FreeCAD FEM mechanical result object |
| """ |
|
|
| if FreeCAD.GuiUp: |
| if resultobj.Mesh: |
| resultobj.Mesh.ViewObject.NodeColor = {} |
| resultobj.Mesh.ViewObject.ElementColor = {} |
| node_numbers = resultobj.Mesh.FemMesh.Nodes.keys() |
| zero_values = [0] * len(node_numbers) |
| resultobj.Mesh.ViewObject.setNodeColorByScalars(node_numbers, zero_values) |
|
|
|
|
| def show_displacement(resultobj, displacement_factor=0.0): |
| if FreeCAD.GuiUp: |
| if resultobj.Mesh.ViewObject.Visibility is False: |
| resultobj.Mesh.ViewObject.Visibility = True |
| resultobj.Mesh.ViewObject.setNodeDisplacementByVectors( |
| resultobj.NodeNumbers, resultobj.DisplacementVectors |
| ) |
| resultobj.Mesh.ViewObject.applyDisplacement(displacement_factor) |
|
|
|
|
| def show_result(resultobj, result_type="Sabs", limit=None): |
| """Sets mesh color using selected type of results. |
| |
| Parameters |
| ---------- |
| resultobj : Fem::ResultMechanical |
| FreeCAD FEM mechanical result object |
| result_type : str, optional |
| default is Sabs |
| FreeCAD FEM mechanical result object |
| - U1, U2, U3 - deformation |
| - Uabs - absolute deformation |
| - Sabs - Von Mises stress |
| limit : float |
| limit cutoff value. All values over the limit are treated |
| as equal to the limit. Useful for filtering out hotspots. |
| """ |
|
|
| if result_type == "None": |
| reset_mesh_color(resultobj.Mesh) |
| return |
| if resultobj: |
| if result_type == "Sabs": |
| values = resultobj.vonMises |
| elif result_type == "Uabs": |
| values = resultobj.DisplacementLengths |
| |
| else: |
| match = {"U1": 0, "U2": 1, "U3": 2} |
| d = zip(*resultobj.DisplacementVectors) |
| values = list(d[match[result_type]]) |
| show_color_by_scalar_with_cutoff(resultobj, values, limit) |
| else: |
| FreeCAD.Console.PrintError("Error, No result object given.\n") |
|
|
|
|
| def show_color_by_scalar_with_cutoff(resultobj, values, limit=None): |
| """Sets mesh color using list of values. Internally used by show_result function. |
| |
| Parameters |
| ---------- |
| resultobj : Fem::ResultMechanical |
| FreeCAD FEM mechanical result object |
| values : list of floats |
| the values to be colored and cutoff |
| has to be the same length as resultobj.NodeNumbers |
| resultobj.NodeNumbers has to be present in the resultobj |
| limit : float |
| limit cutoff value. All values over the limit are treated |
| as equal to the limit. Useful for filtering out hotspots. |
| """ |
|
|
| if limit: |
| filtered_values = [] |
| for v in values: |
| if v > limit: |
| filtered_values.append(limit) |
| else: |
| filtered_values.append(v) |
| else: |
| filtered_values = values |
| if FreeCAD.GuiUp: |
| if resultobj.Mesh.ViewObject.Visibility is False: |
| resultobj.Mesh.ViewObject.Visibility = True |
| resultobj.Mesh.ViewObject.setNodeColorByScalars(resultobj.NodeNumbers, filtered_values) |
|
|
|
|
| def get_stats(res_obj, result_type): |
| """Returns minimum and maximum value for provided result type |
| |
| Parameters |
| ---------- |
| resultobj : Fem::ResultMechanical |
| FreeCAD FEM mechanical result object |
| result_type : str |
| type of FEM result |
| allowed are: see dict keys in def get_all_stats() |
| None - always return (0.0, 0.0) |
| |
| """ |
|
|
| match_table = get_all_stats(res_obj) |
| match_table["None"] = (0.0, 0.0) |
| stats = () |
| if result_type in match_table: |
| stats = match_table[result_type] |
| return stats |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| def get_all_stats(res_obj): |
| """Returns all stats for provided result type. |
| |
| - U1, U2, U3 - deformation |
| - Uabs - absolute deformation |
| - Sabs - Von Mises stress |
| - MaxPrin - Principal stress 1 |
| - MidPrin - Principal stress 2 |
| - MinPrin - Principal stress 3 |
| - MaxShear - maximum shear stress |
| - Peeq - peeq strain |
| - Temp - Temperature |
| - MFlow - MassFlowRate |
| - NPress - NetworkPressure |
| |
| for more information on result types and names |
| see in code file src/Mod/Fem/App/FemVTKTools.cpp |
| the methods _getFreeCADMechResultVectorProperties() |
| and _getFreeCADMechResultScalarProperties() |
| as well as forum topic |
| https://forum.freecad.org/viewtopic.php?f=18&t=33106&start=30#p277434 |
| |
| Parameters |
| ---------- |
| resultobj : Fem::ResultMechanical |
| FreeCAD FEM mechanical result object |
| """ |
|
|
| m = res_obj.Stats |
| stats_dict = { |
| "U1": (m[0], m[1]), |
| "U2": (m[2], m[3]), |
| "U3": (m[4], m[5]), |
| "Uabs": (m[6], m[7]), |
| "Sabs": (m[8], m[9]), |
| "MaxPrin": (m[10], m[11]), |
| "MidPrin": (m[12], m[13]), |
| "MinPrin": (m[14], m[15]), |
| "MaxShear": (m[16], m[17]), |
| "Peeq": (m[18], m[19]), |
| "Temp": (m[20], m[21]), |
| "MFlow": (m[22], m[23]), |
| "NPress": (m[24], m[25]), |
| } |
| return stats_dict |
|
|
|
|
| def fill_femresult_stats(res_obj): |
| """Fills a FreeCAD FEM mechanical result object with stats data. |
| |
| Parameters |
| ---------- |
| resultobj : Fem::ResultMechanical |
| FreeCAD FEM mechanical result object |
| """ |
|
|
| FreeCAD.Console.PrintLog("Calculate stats list for result obj: " + res_obj.Name + "\n") |
| |
| x_min = y_min = z_min = x_max = y_max = z_max = 0 |
| a_max = a_min = s_max = s_min = 0 |
| p1_min = p1_max = p2_min = p2_max = p3_min = p3_max = 0 |
| ms_min = ms_max = peeq_min = peeq_max = 0 |
| temp_min = temp_max = 0 |
| mflow_min = mflow_max = npress_min = npress_max = 0 |
|
|
| if res_obj.DisplacementVectors: |
| x_max, y_max, z_max = map(max, zip(*res_obj.DisplacementVectors)) |
| x_min, y_min, z_min = map(min, zip(*res_obj.DisplacementVectors)) |
| if res_obj.DisplacementLengths: |
| a_min = min(res_obj.DisplacementLengths) |
| a_max = max(res_obj.DisplacementLengths) |
| if res_obj.vonMises: |
| s_min = min(res_obj.vonMises) |
| s_max = max(res_obj.vonMises) |
| if res_obj.PrincipalMax: |
| p1_min = min(res_obj.PrincipalMax) |
| p1_max = max(res_obj.PrincipalMax) |
| if res_obj.PrincipalMed: |
| p2_min = min(res_obj.PrincipalMed) |
| p2_max = max(res_obj.PrincipalMed) |
| if res_obj.PrincipalMin: |
| p3_min = min(res_obj.PrincipalMin) |
| p3_max = max(res_obj.PrincipalMin) |
| if res_obj.MaxShear: |
| ms_min = min(res_obj.MaxShear) |
| ms_max = max(res_obj.MaxShear) |
| if res_obj.Peeq: |
| peeq_min = min(res_obj.Peeq) |
| peeq_max = max(res_obj.Peeq) |
| if res_obj.Temperature: |
| temp_min = min(res_obj.Temperature) |
| temp_max = max(res_obj.Temperature) |
| if res_obj.MassFlowRate: |
| |
| mflow_min = min(res_obj.MassFlowRate) |
| mflow_max = max(res_obj.MassFlowRate) |
| if res_obj.NetworkPressure: |
| npress_min = min(res_obj.NetworkPressure) |
| npress_max = max(res_obj.NetworkPressure) |
|
|
| res_obj.Stats = [ |
| x_min, |
| x_max, |
| y_min, |
| y_max, |
| z_min, |
| z_max, |
| a_min, |
| a_max, |
| s_min, |
| s_max, |
| p1_min, |
| p1_max, |
| p2_min, |
| p2_max, |
| p3_min, |
| p3_max, |
| ms_min, |
| ms_max, |
| peeq_min, |
| peeq_max, |
| temp_min, |
| temp_max, |
| mflow_min, |
| mflow_max, |
| npress_min, |
| npress_max, |
| ] |
| """ |
| stat_types = [ |
| "U1", |
| "U2", |
| "U3", |
| "Uabs", |
| "Sabs", |
| "MaxPrin", |
| "MidPrin", |
| "MinPrin", |
| "MaxShear", |
| "Peeq", |
| "Temp", |
| "MFlow", |
| "NPress" |
| ] |
| """ |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| FreeCAD.Console.PrintLog("Stats list for result obj: " + res_obj.Name + " calculated\n") |
| return res_obj |
|
|
|
|
| def add_disp_apps(res_obj): |
| res_obj.DisplacementLengths = calculate_disp_abs(res_obj.DisplacementVectors) |
| FreeCAD.Console.PrintLog("Added DisplacementLengths.\n") |
| return res_obj |
|
|
|
|
| def add_von_mises(res_obj): |
| mstress = [] |
| iterator = zip( |
| res_obj.NodeStressXX, |
| res_obj.NodeStressYY, |
| res_obj.NodeStressZZ, |
| res_obj.NodeStressXY, |
| res_obj.NodeStressXZ, |
| res_obj.NodeStressYZ, |
| ) |
| for Sxx, Syy, Szz, Sxy, Sxz, Syz in iterator: |
| mstress.append(calculate_von_mises((Sxx, Syy, Szz, Sxy, Sxz, Syz))) |
| res_obj.vonMises = mstress |
| FreeCAD.Console.PrintLog("Added von Mises stress.\n") |
| return res_obj |
|
|
|
|
| def add_principal_stress_std(res_obj): |
| |
| |
| |
| |
| prinstress1 = [] |
| prinstress2 = [] |
| prinstress3 = [] |
| shearstress = [] |
| iterator = zip( |
| res_obj.NodeStressXX, |
| res_obj.NodeStressYY, |
| res_obj.NodeStressZZ, |
| res_obj.NodeStressXY, |
| res_obj.NodeStressXZ, |
| res_obj.NodeStressYZ, |
| ) |
| for Sxx, Syy, Szz, Sxy, Sxz, Syz in iterator: |
| prin1, prin2, prin3, shear = calculate_principal_stress_std((Sxx, Syy, Szz, Sxy, Sxz, Syz)) |
| prinstress1.append(prin1) |
| prinstress2.append(prin2) |
| prinstress3.append(prin3) |
| shearstress.append(shear) |
| res_obj.PrincipalMax = prinstress1 |
| res_obj.PrincipalMed = prinstress2 |
| res_obj.PrincipalMin = prinstress3 |
| res_obj.MaxShear = shearstress |
| FreeCAD.Console.PrintLog("Added standard principal stresses and max shear values.\n") |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| MatMechNon = FreeCAD.ActiveDocument.getObject("MaterialMechanicalNonlinear") |
| if MatMechNon: |
| stress_strain = MatMechNon.YieldPoints |
| if stress_strain: |
| i = -1 |
| while stress_strain[i] == "": |
| i -= 1 |
| critical_uniaxial_strain = float(stress_strain[i].split(",")[1]) |
| |
| alpha = np.sqrt(np.e) * critical_uniaxial_strain |
| beta = 1.5 |
| if res_obj.Peeq: |
| res_obj.CriticalStrainRatio = calculate_csr( |
| prinstress1, prinstress2, prinstress3, alpha, beta, res_obj |
| ) |
|
|
| return res_obj |
|
|
|
|
| def calculate_csr(ps1, ps2, ps3, alpha, beta, res_obj): |
| """Calculate critical strain ratio. |
| |
| Forum Discussion: https://forum.freecad.org/viewtopic.php?f=18&t=35893#p303392 |
| Background: https://www.vtt.fi/inf/julkaisut/muut/2017/VTT-R-01177-17.pdf |
| |
| critical strain ratio = peeq / critical_strain (>1.0 indicates ductile rupture) |
| peeq = equivalent plastic strain |
| critical strain = alpha * np.exp(-beta * T) |
| alpha and beta are material parameters, |
| where alpha can be related to unixial test data (user input) and |
| beta is normally kept fixed at 1.5, |
| unless available from extensive research experiments |
| T = pressure / von Mises stress (stress triaxiality) |
| """ |
| csr = [] |
| nsr = len(ps1) |
| for i in range(nsr): |
| p = (ps1[i] + ps2[i] + ps3[i]) / 3.0 |
| svm = np.sqrt( |
| 1.5 * (ps1[i] - p) ** 2 + 1.5 * (ps2[i] - p) ** 2 + 1.5 * (ps3[i] - p) ** 2 |
| ) |
| if svm != 0.0: |
| T = p / svm |
| else: |
| T = 0.0 |
| critical_strain = alpha * np.exp(-beta * T) |
| csr.append(abs(res_obj.Peeq[i]) / critical_strain) |
| return csr |
|
|
|
|
| def get_concrete_nodes(res_obj): |
| """Determine concrete / non-concrete nodes.""" |
| from femmesh.meshtools import get_femnodes_by_refshape |
|
|
| femmesh = res_obj.Mesh.FemMesh |
| nsr = femmesh.NodeCount |
|
|
| |
| |
| |
| |
| |
| ic = np.zeros(nsr) |
|
|
| for obj in res_obj.getParentGroup().Group: |
| if obj.isDerivedFrom("App::MaterialObjectPython") and is_of_type( |
| obj, "Fem::MaterialReinforced" |
| ): |
| FreeCAD.Console.PrintMessage("ReinforcedMaterial\n") |
| if obj.References == []: |
| for iic in range(nsr): |
| if ic[iic] == 0: |
| ic[iic] = 1 |
| else: |
| for ref in obj.References: |
| concrete_nodes = get_femnodes_by_refshape(femmesh, ref) |
| for cn in concrete_nodes: |
| ic[cn - 1] = 1 |
| elif obj.isDerivedFrom("App::MaterialObjectPython") and is_of_type( |
| obj, "Fem::MaterialCommon" |
| ): |
| FreeCAD.Console.PrintMessage("No ReinforcedMaterial\n") |
| if obj.References == []: |
| for iic in range(nsr): |
| if ic[iic] == 0: |
| ic[iic] = 2 |
| else: |
| for ref in obj.References: |
| non_concrete_nodes = get_femnodes_by_refshape(femmesh, ref) |
| for ncn in non_concrete_nodes: |
| ic[ncn - 1] = 2 |
| return ic |
|
|
|
|
| def add_principal_stress_reinforced(res_obj): |
| |
| |
| |
| ic = get_concrete_nodes(res_obj) |
|
|
| |
| |
| |
| |
| |
| |
| |
| prinstress1 = [] |
| prinstress2 = [] |
| prinstress3 = [] |
| shearstress = [] |
| ps1v = [] |
| ps2v = [] |
| ps3v = [] |
| |
| |
| |
| |
| rhx = [] |
| rhy = [] |
| rhz = [] |
| moc = [] |
|
|
| |
| for obj in res_obj.getParentGroup().Group: |
| if is_of_type(obj, "Fem::MaterialReinforced"): |
| matrix_af = float( |
| FreeCAD.Units.Quantity(obj.Material["AngleOfFriction"]).getValueAs("rad") |
| ) |
| matrix_cs = float( |
| FreeCAD.Units.Quantity(obj.Material["CompressiveStrength"]).getValueAs("MPa") |
| ) |
| reinforce_yield = float( |
| FreeCAD.Units.Quantity(obj.Reinforcement["YieldStrength"]).getValueAs("MPa") |
| ) |
| |
| |
| |
|
|
| iterator = zip( |
| res_obj.NodeStressXX, |
| res_obj.NodeStressYY, |
| res_obj.NodeStressZZ, |
| res_obj.NodeStressXY, |
| res_obj.NodeStressXZ, |
| res_obj.NodeStressYZ, |
| ) |
| for isv, stress_tensor in enumerate(iterator): |
|
|
| rhox = 0.0 |
| rhoy = 0.0 |
| rhoz = 0.0 |
| mc = 0.0 |
|
|
| if ic[isv] == 1: |
| |
| |
| |
| |
| |
| rhox, rhoy, rhoz = calculate_rho(stress_tensor, reinforce_yield) |
|
|
| prin1, prin2, prin3, shear, psv = calculate_principal_stress_reinforced(stress_tensor) |
|
|
| prinstress1.append(prin1) |
| prinstress2.append(prin2) |
| prinstress3.append(prin3) |
| shearstress.append(shear) |
| ps1v.append(psv[0]) |
| ps2v.append(psv[1]) |
| ps3v.append(psv[2]) |
|
|
| |
| |
| |
| rhx.append(rhox) |
| rhy.append(rhoy) |
| rhz.append(rhoz) |
| if ic[isv] == 1: |
| mc = calculate_mohr_coulomb(prin1, prin3, matrix_af, matrix_cs) |
| moc.append(mc) |
|
|
| res_obj.PrincipalMax = prinstress1 |
| res_obj.PrincipalMed = prinstress2 |
| res_obj.PrincipalMin = prinstress3 |
| res_obj.MaxShear = shearstress |
| |
| |
| |
| |
| res_obj.ReinforcementRatio_x = rhx |
| res_obj.ReinforcementRatio_y = rhy |
| res_obj.ReinforcementRatio_z = rhz |
| res_obj.MohrCoulomb = moc |
|
|
| res_obj.PS1Vector = ps1v |
| res_obj.PS2Vector = ps2v |
| res_obj.PS3Vector = ps3v |
|
|
| FreeCAD.Console.PrintLog( |
| "Added reinforcement principal stresses and max shear values as well as " |
| "reinforcment ratios, Mohr Coloumb values.\n" |
| ) |
| return res_obj |
|
|
|
|
| def compact_result(res_obj): |
| """ |
| compacts result.Mesh and appropriate result.NodeNumbers |
| """ |
| |
|
|
| |
| from femmesh.meshtools import compact_mesh |
|
|
| compact_femmesh_data = compact_mesh(res_obj.Mesh.FemMesh) |
| compact_femmesh = compact_femmesh_data[0] |
| node_map = compact_femmesh_data[1] |
| |
| |
|
|
| |
| res_obj.Mesh.FemMesh = compact_femmesh |
|
|
| |
| new_node_numbers = [] |
| for old_node_id in res_obj.NodeNumbers: |
| new_node_numbers.append(node_map[old_node_id]) |
| res_obj.NodeNumbers = new_node_numbers |
|
|
| return res_obj |
|
|
|
|
| def calculate_von_mises(stress_tensor): |
| """Calculate Von mises stress. |
| See http://en.wikipedia.org/wiki/Von_Mises_yield_criterion |
| Simplification: https://forum.freecad.org/viewtopic.php?f=18&t=33974&p=296542#p296542 |
| |
| stress_tensor ... (Sxx, Syy, Szz, Sxy, Sxz, Syz) |
| """ |
| normal = stress_tensor[:3] |
| shear = stress_tensor[3:] |
| pressure = np.average(normal) |
| von_mises = np.sqrt( |
| 1.5 * np.linalg.norm(normal - pressure) ** 2 + 3.0 * np.linalg.norm(shear) ** 2 |
| ) |
| return von_mises |
|
|
|
|
| def calculate_principal_stress_std(stress_tensor): |
| |
| |
| |
| for s in stress_tensor: |
| if isnan(s) is True: |
| return (float("NaN"), float("NaN"), float("NaN"), float("NaN")) |
|
|
| s11 = stress_tensor[0] |
| s22 = stress_tensor[1] |
| s33 = stress_tensor[2] |
| s12 = stress_tensor[3] |
| s31 = stress_tensor[4] |
| s23 = stress_tensor[5] |
| sigma = np.array( |
| [[s11, s12, s31], [s12, s22, s23], [s31, s23, s33]] |
| ) |
|
|
| eigvals = list(np.linalg.eigvalsh(sigma)) |
| eigvals.sort() |
| eigvals.reverse() |
| maxshear = (eigvals[0] - eigvals[2]) / 2.0 |
| return (eigvals[0], eigvals[1], eigvals[2], maxshear) |
|
|
|
|
| def calculate_principal_stress_reinforced(stress_tensor): |
| """Calculate principal stress vectors and values. |
| |
| For total stresses use: |
| stress_tensor[0], stress_tensor[1], stress_tensor[2] |
| on the diagonal of the stress tensor |
| |
| Difference with the original method: |
| https://forum.freecad.org/viewtopic.php?f=18&t=33106&start=90#p296539 |
| """ |
|
|
| s11 = stress_tensor[0] |
| s22 = stress_tensor[1] |
| s33 = stress_tensor[2] |
| s12 = stress_tensor[3] |
| s31 = stress_tensor[4] |
| s23 = stress_tensor[5] |
| sigma = np.array( |
| [[s11, s12, s31], [s12, s22, s23], [s31, s23, s33]] |
| ) |
|
|
| eigenvalues, eigenvectors = np.linalg.eig(sigma) |
|
|
| |
| |
| |
| |
|
|
| eigenvalues = eigenvalues.real |
| eigenvectors = eigenvectors.real |
|
|
| eigenvectors[:, 0] = eigenvalues[0] * eigenvectors[:, 0] |
| eigenvectors[:, 1] = eigenvalues[1] * eigenvectors[:, 1] |
| eigenvectors[:, 2] = eigenvalues[2] * eigenvectors[:, 2] |
|
|
| idx = eigenvalues.argsort()[::-1] |
| eigenvalues = eigenvalues[idx] |
| eigenvectors = eigenvectors[:, idx] |
|
|
| maxshear = (eigenvalues[0] - eigenvalues[2]) / 2.0 |
|
|
| return ( |
| eigenvalues[0], |
| eigenvalues[1], |
| eigenvalues[2], |
| maxshear, |
| tuple([tuple(row) for row in eigenvectors.T]), |
| ) |
|
|
|
|
| def calculate_rho(stress_tensor, fy): |
| """Calculation of Reinforcement Ratios and Concrete Stresses |
| (in accordance with http://heronjournal.nl/53-4/3.pdf) |
| |
| Parameters |
| ---------- |
| - fy: factored yield strength of reinforcement bars |
| |
| See post: |
| https://forum.freecad.org/viewtopic.php?f=18&t=28821 |
| |
| """ |
|
|
| rmin = 1.0e9 |
| eqmin = 14 |
|
|
| sxx = stress_tensor[0] |
| syy = stress_tensor[1] |
| szz = stress_tensor[2] |
| sxy = stress_tensor[3] |
| syz = stress_tensor[5] |
| sxz = stress_tensor[4] |
|
|
| rhox = np.zeros(15) |
| rhoy = np.zeros(15) |
| rhoz = np.zeros(15) |
|
|
| |
| |
| i3 = sxx * syy * szz + 2 * sxy * sxz * syz - sxx * syz**2 - syy * sxz**2 - szz * sxy**2 |
|
|
| |
| d = sxx * syy - sxy**2 |
| if d != 0.0: |
| rhoz[0] = i3 / d / fy |
|
|
| |
| d = sxx * szz - sxz**2 |
| if d != 0.0: |
| rhoy[1] = i3 / d / fy |
|
|
| |
| d = syy * szz - syz**2 |
| if d != 0.0: |
| rhox[2] = i3 / d / fy |
|
|
| |
| if sxx != 0.0: |
| fc = sxz * sxy / sxx - syz |
| fxy = sxy**2 / sxx |
| fxz = sxz**2 / sxx |
|
|
| |
| rhoy[3] = syy - fxy + fc |
| rhoy[3] /= fy |
| rhoz[3] = szz - fxz + fc |
| rhoz[3] /= fy |
|
|
| |
| rhoy[4] = syy - fxy - fc |
| rhoy[4] /= fy |
| rhoz[4] = szz - fxz - fc |
| rhoz[4] /= fy |
|
|
| |
| if syy != 0.0: |
| fc = syz * sxy / syy - sxz |
| fxy = sxy**2 / syy |
| fyz = syz**2 / syy |
|
|
| |
| rhox[5] = sxx - fxy + fc |
| rhox[5] /= fy |
| rhoz[5] = szz - fyz + fc |
| rhoz[5] /= fy |
|
|
| |
| rhox[6] = sxx - fxy - fc |
|
|
| rhox[6] /= fy |
| rhoz[6] = szz - fyz - fc |
| rhoz[6] /= fy |
|
|
| |
| if szz != 0.0: |
| fc = sxz * syz / szz - sxy |
| fxz = sxz**2 / szz |
| fyz = syz**2 / szz |
|
|
| |
| rhox[7] = sxx - fxz + fc |
| rhox[7] /= fy |
| rhoy[7] = syy - fyz + fc |
| rhoy[7] /= fy |
|
|
| |
| rhox[8] = sxx - fxz - fc |
| rhox[8] /= fy |
| rhoy[8] = syy - fyz - fc |
| rhoy[8] /= fy |
|
|
| |
| rhox[9] = (sxx + sxy + sxz) / fy |
| rhoy[9] = (syy + sxy + syz) / fy |
| rhoz[9] = (szz + sxz + syz) / fy |
|
|
| |
| rhox[10] = (sxx + sxy - sxz) / fy |
| rhoy[10] = (syy + sxy - syz) / fy |
| rhoz[10] = (szz - sxz - syz) / fy |
|
|
| |
| rhox[11] = (sxx - sxy - sxz) / fy |
| rhoy[11] = (syy - sxy + syz) / fy |
| rhoz[11] = (szz - sxz + syz) / fy |
|
|
| |
| rhox[12] = (sxx - sxy + sxz) / fy |
| rhoy[12] = (syy - sxy - syz) / fy |
| rhoz[12] = (szz + sxz - syz) / fy |
|
|
| |
| if syz != 0.0: |
| rhox[13] = (sxx - sxy * sxz / syz) / fy |
| if sxz != 0.0: |
| rhoy[13] = (syy - sxy * syz / sxz) / fy |
| if sxy != 0.0: |
| rhoz[13] = (szz - sxz * syz / sxy) / fy |
|
|
| for ir in range(0, rhox.size): |
|
|
| if rhox[ir] >= -1.0e-10 and rhoy[ir] >= -1.0e-10 and rhoz[ir] > -1.0e-10: |
|
|
| |
| scxx = sxx - rhox[ir] * fy |
| scyy = syy - rhoy[ir] * fy |
| sczz = szz - rhoz[ir] * fy |
| ic1 = scxx + scyy + sczz |
| ic2 = scxx * scyy + scyy * sczz + sczz * scxx - sxy**2 - sxz**2 - syz**2 |
| ic3 = ( |
| scxx * scyy * sczz |
| + 2 * sxy * sxz * syz |
| - scxx * syz**2 |
| - scyy * sxz**2 |
| - sczz * sxy**2 |
| ) |
|
|
| if ic1 <= 1.0e-6 and ic2 >= -1.0e-6 and ic3 <= 1.0e-6: |
|
|
| rsum = rhox[ir] + rhoy[ir] + rhoz[ir] |
|
|
| if rsum < rmin and rsum > 0.0: |
| rmin = rsum |
| eqmin = ir |
|
|
| return rhox[eqmin], rhoy[eqmin], rhoz[eqmin] |
|
|
|
|
| def calculate_mohr_coulomb(prin1, prin3, phi, fck): |
| """Calculation of Mohr Coulomb yield criterion to judge |
| concrete crushing and shear failure. |
| |
| Parameters |
| ---------- |
| - phi: angle of internal friction |
| - fck: factored compressive strength of the matrix material (usually concrete) |
| """ |
|
|
| coh = fck * (1 - np.sin(phi)) / 2 / np.cos(phi) |
|
|
| mc_stress = (prin1 - prin3) + (prin1 + prin3) * np.sin(phi) - 2.0 * coh * np.cos(phi) |
|
|
| if mc_stress < 0.0: |
| mc_stress = 0.0 |
|
|
| return mc_stress |
|
|
|
|
| def calculate_disp_abs(displacements): |
| |
| return [np.linalg.norm(nd) for nd in displacements] |
|
|
|
|
| |
|
|