864 lines
27 KiB
Python
864 lines
27 KiB
Python
# ***************************************************************************
|
|
# * Copyright (c) 2017 Bernd Hahnebach <bernd@bimstatik.org> *
|
|
# * *
|
|
# * This program is free software; you can redistribute it and/or modify *
|
|
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
|
# * as published by the Free Software Foundation; either version 2 of *
|
|
# * the License, or (at your option) any later version. *
|
|
# * for detail see the LICENCE text file. *
|
|
# * *
|
|
# * This program is distributed in the hope that it will be useful, *
|
|
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
# * GNU Library General Public License for more details. *
|
|
# * *
|
|
# * You should have received a copy of the GNU Library General Public *
|
|
# * License along with this program; if not, write to the Free Software *
|
|
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
# * USA *
|
|
# * *
|
|
# ***************************************************************************
|
|
|
|
__title__ = "Fem Tools for results"
|
|
__author__ = "Bernd Hahnebach"
|
|
__url__ = "http://www.freecadweb.org"
|
|
|
|
## \addtogroup FEM
|
|
# @{
|
|
|
|
import FreeCAD
|
|
import femtools.femutils as femutils
|
|
import numpy as np
|
|
from math import isnan
|
|
|
|
|
|
def purge_results(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 hasattr(m.Mesh, "Proxy") \
|
|
and m.Mesh.Proxy.Type == "Fem::FemMeshResult":
|
|
analysis.Document.removeObject(m.Mesh.Name)
|
|
analysis.Document.removeObject(m.Name)
|
|
FreeCAD.ActiveDocument.recompute()
|
|
# if analysis typ check is used result mesh
|
|
# without result obj is created in the analysis
|
|
# we could run into trouble in one loop because
|
|
# we will delete objects and try to access them later
|
|
for m in analysis.Group:
|
|
if femutils.is_of_type(m, "Fem::FemMeshResult"):
|
|
analysis.Document.removeObject(m.Name)
|
|
FreeCAD.ActiveDocument.recompute()
|
|
|
|
|
|
def reset_mesh_deformation(resultobj):
|
|
"""Resets result mesh deformation
|
|
|
|
Parameters
|
|
----------
|
|
resultobj : Fem::FemResultMechanical
|
|
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::FemResultMechanical
|
|
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::FemResultMechanical
|
|
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
|
|
# TODO: the result object does have more result types to show, implement them
|
|
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::FemResultMechanical
|
|
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, average and maximum value for provided result type
|
|
|
|
Parameters
|
|
----------
|
|
resultobj : Fem::FemResultMechanical
|
|
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, 0.0)
|
|
|
|
"""
|
|
|
|
match_table = get_all_stats(res_obj)
|
|
match_table["None"] = (0.0, 0.0, 0.0)
|
|
stats = ()
|
|
if result_type in match_table:
|
|
stats = match_table[result_type]
|
|
return stats
|
|
|
|
|
|
# - U1, U2, U3 - deformation
|
|
# - Uabs - absolute deformation
|
|
# - Sabs - Von Mises stress
|
|
# - Prin1 - Principal stress 1
|
|
# - Prin2 - Principal stress 2
|
|
# - Prin3 - Principal stress 3
|
|
# - MaxSear - maximum shear stress
|
|
# - Peeq - peeq strain
|
|
# - Temp - Temperature
|
|
# - MFlow - MassFlowRate
|
|
# - NPress - NetworkPressure
|
|
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.freecadweb.org/viewtopic.php?f=18&t=33106&start=30#p277434
|
|
|
|
Parameters
|
|
----------
|
|
resultobj : Fem::FemResultMechanical
|
|
FreeCAD FEM mechanical result object
|
|
|
|
|
|
"""
|
|
|
|
m = res_obj.Stats
|
|
stats_dict = {
|
|
"U1": (m[0], m[1], m[2]),
|
|
"U2": (m[3], m[4], m[5]),
|
|
"U3": (m[6], m[7], m[8]),
|
|
"Uabs": (m[9], m[10], m[11]),
|
|
"Sabs": (m[12], m[13], m[14]),
|
|
"MaxPrin": (m[15], m[16], m[17]),
|
|
"MidPrin": (m[18], m[19], m[20]),
|
|
"MinPrin": (m[21], m[22], m[23]),
|
|
"MaxShear": (m[24], m[25], m[26]),
|
|
"Peeq": (m[27], m[28], m[29]),
|
|
"Temp": (m[30], m[31], m[32]),
|
|
"MFlow": (m[33], m[34], m[35]),
|
|
"NPress": (m[36], m[37], m[38])
|
|
}
|
|
return stats_dict
|
|
|
|
|
|
def fill_femresult_stats(res_obj):
|
|
"""Fills a FreeCAD FEM mechanical result object with stats data
|
|
|
|
Parameters
|
|
----------
|
|
resultobj : Fem::FemResultMechanical
|
|
FreeCAD FEM mechanical result object
|
|
"""
|
|
|
|
FreeCAD.Console.PrintLog(
|
|
"Calculate stats list for result obj: " + res_obj.Name + "\n"
|
|
)
|
|
no_of_values = 1 # to avoid division by zero
|
|
# set stats values to 0, they may not exist in res_obj
|
|
x_min = y_min = z_min = x_max = y_max = z_max = x_avg = y_avg = z_avg = 0
|
|
a_max = a_min = a_avg = s_max = s_min = s_avg = 0
|
|
p1_min = p1_avg = p1_max = p2_min = p2_avg = p2_max = p3_min = p3_avg = p3_max = 0
|
|
ms_min = ms_avg = ms_max = peeq_min = peeq_avg = peeq_max = 0
|
|
temp_min = temp_avg = temp_max = 0
|
|
mflow_min = mflow_avg = mflow_max = npress_min = npress_avg = npress_max = 0
|
|
|
|
if res_obj.DisplacementVectors:
|
|
no_of_values = len(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))
|
|
sum_list = map(sum, zip(*res_obj.DisplacementVectors))
|
|
x_avg, y_avg, z_avg = [i / no_of_values for i in sum_list]
|
|
a_min = min(res_obj.DisplacementLengths)
|
|
a_avg = sum(res_obj.DisplacementLengths) / no_of_values
|
|
a_max = max(res_obj.DisplacementLengths)
|
|
if res_obj.vonMises:
|
|
s_min = min(res_obj.vonMises)
|
|
s_avg = sum(res_obj.vonMises) / no_of_values
|
|
s_max = max(res_obj.vonMises)
|
|
if res_obj.PrincipalMax:
|
|
p1_min = min(res_obj.PrincipalMax)
|
|
p1_avg = sum(res_obj.PrincipalMax) / no_of_values
|
|
p1_max = max(res_obj.PrincipalMax)
|
|
if res_obj.PrincipalMed:
|
|
p2_min = min(res_obj.PrincipalMed)
|
|
p2_avg = sum(res_obj.PrincipalMed) / no_of_values
|
|
p2_max = max(res_obj.PrincipalMed)
|
|
if res_obj.PrincipalMin:
|
|
p3_min = min(res_obj.PrincipalMin)
|
|
p3_avg = sum(res_obj.PrincipalMin) / no_of_values
|
|
p3_max = max(res_obj.PrincipalMin)
|
|
if res_obj.MaxShear:
|
|
ms_min = min(res_obj.MaxShear)
|
|
ms_avg = sum(res_obj.MaxShear) / no_of_values
|
|
ms_max = max(res_obj.MaxShear)
|
|
if res_obj.Peeq:
|
|
peeq_min = min(res_obj.Peeq)
|
|
peeq_avg = sum(res_obj.Peeq) / no_of_values
|
|
peeq_max = max(res_obj.Peeq)
|
|
if res_obj.Temperature:
|
|
temp_min = min(res_obj.Temperature)
|
|
temp_avg = sum(res_obj.Temperature) / no_of_values
|
|
temp_max = max(res_obj.Temperature)
|
|
if res_obj.MassFlowRate:
|
|
# DisplacementVectors is empty, no_of_values needs to be set
|
|
no_of_values = len(res_obj.MassFlowRate)
|
|
mflow_min = min(res_obj.MassFlowRate)
|
|
mflow_avg = sum(res_obj.MassFlowRate) / no_of_values
|
|
mflow_max = max(res_obj.MassFlowRate)
|
|
if res_obj.NetworkPressure:
|
|
npress_min = min(res_obj.NetworkPressure)
|
|
npress_avg = sum(res_obj.NetworkPressure) / no_of_values
|
|
npress_max = max(res_obj.NetworkPressure)
|
|
|
|
res_obj.Stats = [x_min, x_avg, x_max,
|
|
y_min, y_avg, y_max,
|
|
z_min, z_avg, z_max,
|
|
a_min, a_avg, a_max,
|
|
s_min, s_avg, s_max,
|
|
p1_min, p1_avg, p1_max,
|
|
p2_min, p2_avg, p2_max,
|
|
p3_min, p3_avg, p3_max,
|
|
ms_min, ms_avg, ms_max,
|
|
peeq_min, peeq_avg, peeq_max,
|
|
temp_min, temp_avg, temp_max,
|
|
mflow_min, mflow_avg, mflow_max,
|
|
npress_min, npress_avg, npress_max]
|
|
"""
|
|
stat_types = [
|
|
"U1",
|
|
"U2",
|
|
"U3",
|
|
"Uabs",
|
|
"Sabs",
|
|
"MaxPrin",
|
|
"MidPrin",
|
|
"MinPrin",
|
|
"MaxShear",
|
|
"Peeq",
|
|
"Temp",
|
|
"MFlow",
|
|
"NPress"
|
|
]
|
|
"""
|
|
# len(stat_types) == 13*3 == 39
|
|
# do not forget to adapt initialization of all Stats items in modules:
|
|
# - module femobjects/_FemResultMechanical.py
|
|
# do not forget to adapt the def get_stats in:
|
|
# - get_stats in module femresult/resulttools.py
|
|
# - module femtest/testccxtools.py
|
|
# TODO: all stats stuff should be reimplemented
|
|
# maybe a dictionary would be far more robust than a list
|
|
|
|
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 principal stress and max shear values.\n")
|
|
return res_obj
|
|
|
|
|
|
def get_concrete_nodes(res_obj):
|
|
|
|
#
|
|
# HarryvL: determine concrete / non-concrete nodes
|
|
#
|
|
|
|
from femmesh.meshtools import get_femnodes_by_refshape
|
|
femmesh = res_obj.Mesh.FemMesh
|
|
nsr = femmesh.NodeCount # nsr number of stress results
|
|
|
|
# ic[iic]:
|
|
# ic = flag for material type; iic = node number
|
|
# ic = 0: NOT ASSIGNED
|
|
# ic = 1: ReinforcedMaterial
|
|
# ic = 2: NOT ReinforcedMaterial
|
|
ic = np.zeros(nsr)
|
|
|
|
for obj in res_obj.getParentGroup().Group:
|
|
if obj.isDerivedFrom("App::MaterialObjectPython") \
|
|
and femutils.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 femutils.is_of_type(obj, "Fem::Material"):
|
|
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):
|
|
|
|
#
|
|
# HarryvL: determine concrete / non-concrete nodes
|
|
#
|
|
ic = get_concrete_nodes(res_obj)
|
|
|
|
#
|
|
# calculate principal and max Shear and fill them in res_obj
|
|
#
|
|
prinstress1 = []
|
|
prinstress2 = []
|
|
prinstress3 = []
|
|
shearstress = []
|
|
ps1v = []
|
|
ps2v = []
|
|
ps3v = []
|
|
#
|
|
# HarryvL: additional arrays to hold reinforcement ratios
|
|
# and mohr coulomb stress
|
|
#
|
|
rhx = []
|
|
rhy = []
|
|
rhz = []
|
|
moc = []
|
|
|
|
# material parameter
|
|
for obj in res_obj.getParentGroup().Group:
|
|
if femutils.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")
|
|
)
|
|
# print(matrix_af)
|
|
# print(matrix_cs)
|
|
# print(reinforce_yield)
|
|
|
|
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.
|
|
rhoy = 0.
|
|
rhoz = 0.
|
|
mc = 0.
|
|
|
|
if ic[isv] == 1:
|
|
#
|
|
# HarryvL: for concrete scxx etc. are affected by
|
|
# reinforcement (see calculate_rho(stress_tensor)). for all other
|
|
# materials scxx etc. are the original stresses
|
|
#
|
|
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])
|
|
|
|
#
|
|
# reinforcement ratios and mohr coulomb criterion
|
|
#
|
|
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
|
|
#
|
|
# HarryvL: additional concrete and principal stress plot
|
|
# results for use in _ViewProviderFemResultMechanical
|
|
#
|
|
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.PrintMessage(
|
|
"Added principal stress and max shear values as well as"
|
|
"reinforcment rations, Mohr Coloumb values.\n"
|
|
)
|
|
return res_obj
|
|
|
|
|
|
def compact_result(res_obj):
|
|
"""
|
|
compacts result.Mesh and appropriate result.NodeNumbers
|
|
"""
|
|
# as workaround for https://www.freecadweb.org/tracker/view.php?id=2873
|
|
|
|
# get compact mesh data
|
|
from femmesh.meshtools import compact_mesh as cm
|
|
compact_femmesh_data = cm(res_obj.Mesh.FemMesh)
|
|
compact_femmesh = compact_femmesh_data[0]
|
|
node_map = compact_femmesh_data[1]
|
|
# FreeCAD result obj does not support elem results ATM
|
|
# elem_map = compact_femmesh_data[2]
|
|
|
|
# set result mesh
|
|
res_obj.Mesh.FemMesh = compact_femmesh
|
|
|
|
# set result node numbers
|
|
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):
|
|
# Von mises stress: http://en.wikipedia.org/wiki/Von_Mises_yield_criterion
|
|
# simplification: https://forum.freecadweb.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)
|
|
return np.sqrt(1.5 * np.linalg.norm(normal - pressure)**2 + 3.0 * np.linalg.norm(shear)**2)
|
|
|
|
|
|
def calculate_principal_stress_std(
|
|
stress_tensor
|
|
):
|
|
|
|
# if NaN is inside the array, which can happen on Calculix frd result files return NaN
|
|
# https://forum.freecadweb.org/viewtopic.php?f=22&t=33911&start=10#p284229
|
|
# https://forum.freecadweb.org/viewtopic.php?f=18&t=32649#p274291
|
|
for s in stress_tensor:
|
|
if isnan(s) is True:
|
|
return (float("NaN"), float("NaN"), float("NaN"), float("NaN"))
|
|
|
|
s11 = stress_tensor[0] # Sxx
|
|
s22 = stress_tensor[1] # Syy
|
|
s33 = stress_tensor[2] # Szz
|
|
s12 = stress_tensor[3] # Sxy
|
|
s31 = stress_tensor[4] # Sxz
|
|
s23 = stress_tensor[5] # Syz
|
|
sigma = np.array([
|
|
[s11, s12, s31],
|
|
[s12, s22, s23],
|
|
[s31, s23, s33]
|
|
]) # https://forum.freecadweb.org/viewtopic.php?f=18&t=24637&start=10#p240408
|
|
|
|
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):
|
|
#
|
|
# HarryvL - 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 to the original method:
|
|
# https://forum.freecadweb.org/viewtopic.php?f=18&t=33106&start=90#p296539
|
|
#
|
|
|
|
s11 = stress_tensor[0] # Sxx
|
|
s22 = stress_tensor[1] # Syy
|
|
s33 = stress_tensor[2] # Szz
|
|
s12 = stress_tensor[3] # Sxy
|
|
s31 = stress_tensor[4] # Sxz
|
|
s23 = stress_tensor[5] # Syz
|
|
sigma = np.array([
|
|
[s11, s12, s31],
|
|
[s12, s22, s23],
|
|
[s31, s23, s33]
|
|
]) # https://forum.freecadweb.org/viewtopic.php?f=18&t=24637&start=10#p240408
|
|
|
|
eigenvalues, eigenvectors = np.linalg.eig(sigma)
|
|
|
|
#
|
|
# HarryvL: suppress complex eigenvalue and vectors that may occur for
|
|
# near-zero (numerical noise) stress fields
|
|
#
|
|
|
|
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):
|
|
|
|
#
|
|
# HarryvL - Calculation of Reinforcement Ratios and
|
|
# Concrete Stresses according to http://heronjournal.nl/53-4/3.pdf
|
|
# - See post:
|
|
# https://forum.freecadweb.org/viewtopic.php?f=18&t=28821
|
|
# fy: factored yield strength of reinforcement bars
|
|
#
|
|
|
|
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)
|
|
|
|
# i1=sxx+syy+szz NOT USED
|
|
# i2=sxx*syy+syy*szz+szz*sxx-sxy**2-sxz**2-syz**2 NOT USED
|
|
i3 = (sxx * syy * szz + 2 * sxy * sxz * syz - sxx * syz**2
|
|
- syy * sxz**2 - szz * sxy**2)
|
|
|
|
# Solution (5)
|
|
d = (sxx * syy - sxy**2)
|
|
if d != 0.:
|
|
rhoz[0] = i3 / d / fy
|
|
|
|
# Solution (6)
|
|
d = (sxx * szz - sxz**2)
|
|
if d != 0.:
|
|
rhoy[1] = i3 / d / fy
|
|
|
|
# Solution (7)
|
|
d = (syy * szz - syz**2)
|
|
if d != 0.:
|
|
rhox[2] = i3 / d / fy
|
|
|
|
# Solution (9)
|
|
if sxx != 0.:
|
|
fc = sxz * sxy / sxx - syz
|
|
fxy = sxy**2 / sxx
|
|
fxz = sxz**2 / sxx
|
|
|
|
# Solution (9+)
|
|
rhoy[3] = syy - fxy + fc
|
|
rhoy[3] /= fy
|
|
rhoz[3] = szz - fxz + fc
|
|
rhoz[3] /= fy
|
|
|
|
# Solution (9-)
|
|
rhoy[4] = syy - fxy - fc
|
|
rhoy[4] /= fy
|
|
rhoz[4] = szz - fxz - fc
|
|
rhoz[4] /= fy
|
|
|
|
# Solution (10)
|
|
if syy != 0.:
|
|
fc = syz * sxy / syy - sxz
|
|
fxy = sxy**2 / syy
|
|
fyz = syz**2 / syy
|
|
|
|
# Solution (10+)
|
|
rhox[5] = sxx - fxy + fc
|
|
rhox[5] /= fy
|
|
rhoz[5] = szz - fyz + fc
|
|
rhoz[5] /= fy
|
|
|
|
# Solution (10-)vm
|
|
rhox[6] = sxx - fxy - fc
|
|
|
|
rhox[6] /= fy
|
|
rhoz[6] = szz - fyz - fc
|
|
rhoz[6] /= fy
|
|
|
|
# Solution (11)
|
|
if szz != 0.:
|
|
fc = sxz * syz / szz - sxy
|
|
fxz = sxz**2 / szz
|
|
fyz = syz**2 / szz
|
|
|
|
# Solution (11+)
|
|
rhox[7] = sxx - fxz + fc
|
|
rhox[7] /= fy
|
|
rhoy[7] = syy - fyz + fc
|
|
rhoy[7] /= fy
|
|
|
|
# Solution (11-)
|
|
rhox[8] = sxx - fxz - fc
|
|
rhox[8] /= fy
|
|
rhoy[8] = syy - fyz - fc
|
|
rhoy[8] /= fy
|
|
|
|
# Solution (13)
|
|
rhox[9] = (sxx + sxy + sxz) / fy
|
|
rhoy[9] = (syy + sxy + syz) / fy
|
|
rhoz[9] = (szz + sxz + syz) / fy
|
|
|
|
# Solution (14)
|
|
rhox[10] = (sxx + sxy - sxz) / fy
|
|
rhoy[10] = (syy + sxy - syz) / fy
|
|
rhoz[10] = (szz - sxz - syz) / fy
|
|
|
|
# Solution (15)
|
|
rhox[11] = (sxx - sxy - sxz) / fy
|
|
rhoy[11] = (syy - sxy + syz) / fy
|
|
rhoz[11] = (szz - sxz + syz) / fy
|
|
|
|
# Solution (16)
|
|
rhox[12] = (sxx - sxy + sxz) / fy
|
|
rhoy[12] = (syy - sxy - syz) / fy
|
|
rhoz[12] = (szz + sxz - syz) / fy
|
|
|
|
# Solution (17)
|
|
if syz != 0.:
|
|
rhox[13] = (sxx - sxy * sxz / syz) / fy
|
|
if sxz != 0.:
|
|
rhoy[13] = (syy - sxy * syz / sxz) / fy
|
|
if sxy != 0.:
|
|
rhoz[13] = (szz - sxz * syz / sxy) / fy
|
|
|
|
for ir in range(0, rhox.size):
|
|
|
|
if rhox[ir] >= -1.e-10 and rhoy[ir] >= -1.e-10 and rhoz[ir] > -1.e-10:
|
|
|
|
# Concrete Stresses
|
|
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.e-6 and ic2 >= -1.e-6 and ic3 <= 1.0e-6:
|
|
|
|
rsum = rhox[ir] + rhoy[ir] + rhoz[ir]
|
|
|
|
if rsum < rmin and rsum > 0.:
|
|
rmin = rsum
|
|
eqmin = ir
|
|
|
|
return rhox[eqmin], rhoy[eqmin], rhoz[eqmin]
|
|
|
|
|
|
def calculate_mohr_coulomb(prin1, prin3, phi, fck):
|
|
#
|
|
# HarryvL - Calculation of Mohr Coulomb yield criterion to judge
|
|
# concrete curshing and shear failure
|
|
# 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. * coh * np.cos(phi))
|
|
|
|
if mc_stress < 0.:
|
|
mc_stress = 0.
|
|
|
|
return mc_stress
|
|
|
|
|
|
def calculate_disp_abs(displacements):
|
|
# see https://forum.freecadweb.org/viewtopic.php?f=18&t=33106&start=100#p296657
|
|
return [np.linalg.norm(nd) for nd in displacements]
|
|
|
|
## @}
|