FEM: beam rotaion, implement writer, this fixes a very old restiction, the beam analysis in z-axis

This commit is contained in:
Bernd Hahnebach
2018-01-23 06:39:35 +01:00
committed by wmayer
parent 501bfef055
commit f813dce4ae
3 changed files with 155 additions and 49 deletions

View File

@@ -362,6 +362,69 @@ def get_femelement_sets(femmesh, femelement_table, fem_objects, femnodes_ele_tab
FreeCAD.Console.PrintError('Error in get_femelement_sets -- > femelements_count_ok() failed!\n')
def get_femelement_direction1D_set(femmesh, femelement_table, beamrotation_objects, theshape=None):
'''
get for each geometry edge direction, the normal and the element ids and write all into the beamrotation_objects
means no return value, we gone write into the beamrotation_objects dictionary
FEMRotations1D is a list of dictionaries for every beamdirection of all edges
beamrot_obj['FEMRotations1D'] = [ {'ids' : [theids],
'direction' : direction,
'normal' : normal},
...
]
'''
if len(beamrotation_objects) == 0:
# no beamrotation document object, all beams use standard rotation of 0 degree (angle), we need theshape (the shpae which was meshed)
# since ccx needs to split them in sets anyway we need to dake care of this too
rotations_ids = get_femelement_directions_theshape(femmesh, femelement_table, theshape)
# add normals for each direction
rotation_angle = 0
for rot in rotations_ids:
rot['normal'] = get_beam_normal(rot['direction'], rotation_angle)
beamrotation_objects.append({'FEMRotations1D': rotations_ids, 'ShortName': 'Rstd'}) # key 'Object' will be empty
elif len(beamrotation_objects) == 1:
# one beamrotaion document object with no references, all beams use rotation from this object, we need theshape (the shpae which was meshed)
# since ccx needs to split them in sets anyway we need to dake care of this too
rotations_ids = get_femelement_directions_theshape(femmesh, femelement_table, theshape)
# add normals for each direction
rotation_angle = beamrotation_objects[0]['Object'].Rotation
for rot in rotations_ids:
rot['normal'] = get_beam_normal(rot['direction'], rotation_angle)
beamrotation_objects[0]['FEMRotations1D'] = rotations_ids
beamrotation_objects[0]['ShortName'] = 'R0'
elif len(beamrotation_objects) > 1:
# multiple beam rotation document objects, rotations defined by reference shapes, TODO implement this
# do not forget all the corner cases:
# one beam rotation object, but not all edges are ref shapes
# more than one beam rotation object, but not all edges are in the ref shapes
# for the both cases above, all other edges get standard rotation.
# more than one beam roataion objects and on has no ref shapes, all edges no in an rotation object use this rotation
# one edge is in more than one beam rotation object, error
# pre check, only one beam rotation with empty ref shapes is allowed
# we need theshape for multiple rotations too, because of the corner cases mentioned above
FreeCAD.Console.PrintError('Multiple Rotations not yet supported!\n')
for rot_object in beamrotation_objects: # debug print
print(rot_object['FEMRotations1D'])
def get_femelement_directions_theshape(femmesh, femelement_table, theshape):
# see get_femelement_direction1D_set
rotations_ids = []
# add directions and all ids for each direction
for e in theshape.Shape.Edges:
the_edge = {}
the_edge['direction'] = e.Vertexes[1].Point - e.Vertexes[0].Point
edge_femnodes = femmesh.getNodesByEdge(e) # femnodes for the current edge
the_edge['ids'] = get_femelements_by_femnodes_std(femelement_table, edge_femnodes) # femelements for this edge
for rot in rotations_ids:
if rot['direction'] == the_edge['direction']: # tolerance will be managed by FreeCAD see https://forum.freecadweb.org/viewtopic.php?f=22&t=14179
rot['ids'] += the_edge['ids']
break
else:
rotations_ids.append(the_edge)
return rotations_ids
def get_beam_normal(beam_direction, defined_angle):
import math
vector_a = beam_direction
@@ -454,6 +517,8 @@ def get_elset_short_name(obj, i):
return 'M' + str(i)
elif hasattr(obj, "Proxy") and obj.Proxy.Type == 'Fem::FemElementGeometry1D':
return 'B' + str(i)
elif hasattr(obj, "Proxy") and obj.Proxy.Type == 'Fem::FemElementRotation1D':
return 'R' + str(i)
elif hasattr(obj, "Proxy") and obj.Proxy.Type == 'Fem::FemElementFluid1D':
return 'F' + str(i)
elif hasattr(obj, "Proxy") and obj.Proxy.Type == 'Fem::FemElementGeometry2D':

129
src/Mod/Fem/femsolver/calculix/writer.py Normal file → Executable file
View File

@@ -337,14 +337,18 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter):
f.write('** Element sets for materials and FEM element type (solid, shell, beam, fluid)\n')
f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name))
# in any case if we have beams, we gone need the element ids for the rotation elsets
if self.beamsection_objects:
# we will need to split the beam even for one beamobj
# because no beam in z-direction can be used in ccx without a special adjustment
# thus they need an own ccx_elset
self.get_element_rotation1D_elements()
# get the element ids for face and edge elements and write them into the objects
if len(self.shellthickness_objects) > 1:
self.get_element_geometry2D_elements()
elif len(self.beamsection_objects) > 1:
self.get_element_geometry1D_elements()
# we will need to split the beams even for one beamobj
# because no beam in z-direction can be used in ccx without a special adjustment
# thus they need an own ccx_elset --> but this is ccx specific and thus should not be in input writer!
elif len(self.fluidsection_objects) > 1:
self.get_element_fluid1D_elements()
@@ -631,25 +635,30 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter):
beamsec_obj = ccx_elset['beamsection_obj']
elsetdef = 'ELSET=' + ccx_elset['ccx_elset_name'] + ', '
material = 'MATERIAL=' + ccx_elset['mat_obj_name']
normal = ccx_elset['beam_normal']
if beamsec_obj.SectionType == 'Rectangular':
height = beamsec_obj.RectHeight.getValueAs('mm')
width = beamsec_obj.RectWidth.getValueAs('mm')
section_type = ', SECTION=RECT'
setion_geo = str(height) + ', ' + str(width) + '\n'
setion_def = '*BEAM SECTION, ' + elsetdef + material + section_type + '\n'
setion_nor = str(normal[0]) + ', ' + str(normal[1]) + ', ' + str(normal[2]) + '\n'
elif beamsec_obj.SectionType == 'Circular':
radius = 0.5 * beamsec_obj.CircDiameter.getValueAs('mm')
section_type = ', SECTION=CIRC'
setion_geo = str(radius) + '\n'
setion_def = '*BEAM SECTION, ' + elsetdef + material + section_type + '\n'
setion_nor = str(normal[0]) + ', ' + str(normal[1]) + ', ' + str(normal[2]) + '\n'
elif beamsec_obj.SectionType == 'Pipe':
radius = 0.5 * beamsec_obj.PipeDiameter.getValueAs('mm')
thickness = beamsec_obj.PipeThickness.getValueAs('mm')
section_type = ', SECTION=PIPE'
setion_geo = str(radius) + ', ' + str(thickness) + '\n'
setion_def = '*BEAM GENERAL SECTION, ' + elsetdef + material + section_type + '\n'
setion_nor = str(normal[0]) + ', ' + str(normal[1]) + ', ' + str(normal[2]) + '\n'
f.write(setion_def)
f.write(setion_geo)
f.write(setion_nor)
elif 'fluidsection_obj'in ccx_elset: # fluid mesh
fluidsec_obj = ccx_elset['fluidsection_obj']
elsetdef = 'ELSET=' + ccx_elset['ccx_elset_name'] + ', '
@@ -1071,72 +1080,94 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter):
# 'ccx_elset_name' : 'ccx_identifier_elset'
# 'mat_obj_name' : 'mat_obj.Name'
# 'ccx_mat_name' : 'mat_obj.Material['Name']' !!! not unique !!!
# 'beamsection_obj' : 'beamsection_obj' if exists
# 'fluidsection_obj' : 'fluidsection_obj' if exists
# 'shellthickness_obj' : shellthickness_obj' if exists
# 'beamsection_obj' : 'beamsection_obj' if exists
# 'fluidsection_obj' : 'fluidsection_obj' if exists
# 'shellthickness_obj' : shellthickness_obj' if exists
# 'beam_normal' : normal vector for beams only
# },
# {}, ... , {} ]
# beam
# TODO support multiple beamrotations
# we do not need any more any data from the rotation document object, thus we do not need to save the rotation document object name in the else
def get_ccx_elsets_single_mat_single_beam(self):
mat_obj = self.material_objects[0]['Object']
beamsec_obj = self.beamsection_objects[0]['Object']
elset_data = self.ccx_eedges
names = [{'short': 'M0'}, {'short': 'B0'}]
ccx_elset = {}
ccx_elset['ccx_elset'] = elset_data
ccx_elset['ccx_elset_name'] = get_ccx_elset_name_short(names)
ccx_elset['mat_obj_name'] = mat_obj.Name
ccx_elset['ccx_mat_name'] = mat_obj.Material['Name']
ccx_elset['beamsection_obj'] = beamsec_obj
self.ccx_elsets.append(ccx_elset)
beamrot_data = self.beamrotation_objects[0]
for i, beamdirection in enumerate(beamrot_data['FEMRotations1D']):
elset_data = beamdirection['ids'] # ID's for this direction
names = [{'short': 'M0'}, {'short': 'B0'}, {'short': beamrot_data['ShortName']}, {'short': 'D' + str(i)}]
ccx_elset = {}
ccx_elset['ccx_elset'] = elset_data
ccx_elset['ccx_elset_name'] = get_ccx_elset_name_short(names)
ccx_elset['mat_obj_name'] = mat_obj.Name
ccx_elset['ccx_mat_name'] = mat_obj.Material['Name']
ccx_elset['beamsection_obj'] = beamsec_obj
ccx_elset['beam_normal'] = beamdirection['normal'] # normal for this direction
self.ccx_elsets.append(ccx_elset)
def get_ccx_elsets_single_mat_multiple_beam(self):
mat_obj = self.material_objects[0]['Object']
beamrot_data = self.beamrotation_objects[0]
for beamsec_data in self.beamsection_objects:
beamsec_obj = beamsec_data['Object']
elset_data = beamsec_data['FEMElements']
names = [{'short': 'M0'}, {'short': beamsec_data['ShortName']}]
ccx_elset = {}
ccx_elset['ccx_elset'] = elset_data
ccx_elset['ccx_elset_name'] = get_ccx_elset_name_short(names)
ccx_elset['mat_obj_name'] = mat_obj.Name
ccx_elset['ccx_mat_name'] = mat_obj.Material['Name']
ccx_elset['beamsection_obj'] = beamsec_obj
self.ccx_elsets.append(ccx_elset)
def get_ccx_elsets_multiple_mat_single_beam(self):
beamsec_obj = self.beamsection_objects[0]['Object']
for mat_data in self.material_objects:
mat_obj = mat_data['Object']
elset_data = mat_data['FEMElements']
names = [{'short': mat_data['ShortName']}, {'short': 'B0'}]
ccx_elset = {}
ccx_elset['ccx_elset'] = elset_data
ccx_elset['ccx_elset_name'] = get_ccx_elset_name_short(names)
ccx_elset['mat_obj_name'] = mat_obj.Name
ccx_elset['ccx_mat_name'] = mat_obj.Material['Name']
ccx_elset['beamsection_obj'] = beamsec_obj
self.ccx_elsets.append(ccx_elset)
def get_ccx_elsets_multiple_mat_multiple_beam(self):
for beamsec_data in self.beamsection_objects:
beamsec_obj = beamsec_data['Object']
for mat_data in self.material_objects:
mat_obj = mat_data['Object']
beamsec_ids = set(beamsec_data['FEMElements'])
mat_ids = set(mat_data['FEMElements'])
elset_data = list(sorted(beamsec_ids.intersection(mat_ids))) # empty intersection sets possible
beamsec_ids = set(beamsec_data['FEMElements'])
for i, beamdirection in enumerate(beamrot_data['FEMRotations1D']):
beamdir_ids = set(beamdirection['ids'])
elset_data = list(sorted(beamsec_ids.intersection(beamdir_ids))) # empty intersection sets possible
if elset_data:
names = [{'short': mat_data['ShortName']}, {'short': beamsec_data['ShortName']}]
names = [{'short': 'M0'}, {'short': beamsec_data['ShortName']}, {'short': beamrot_data['ShortName']}, {'short': 'D' + str(i)}]
ccx_elset = {}
ccx_elset['ccx_elset'] = elset_data
ccx_elset['ccx_elset_name'] = get_ccx_elset_name_short(names)
ccx_elset['mat_obj_name'] = mat_obj.Name
ccx_elset['ccx_mat_name'] = mat_obj.Material['Name']
ccx_elset['beamsection_obj'] = beamsec_obj
ccx_elset['beam_normal'] = beamdirection['normal'] # normal for this direction
self.ccx_elsets.append(ccx_elset)
def get_ccx_elsets_multiple_mat_single_beam(self):
beamsec_obj = self.beamsection_objects[0]['Object']
beamrot_data = self.beamrotation_objects[0]
for mat_data in self.material_objects:
mat_obj = mat_data['Object']
mat_ids = set(mat_data['FEMElements'])
for i, beamdirection in enumerate(beamrot_data['FEMRotations1D']):
beamdir_ids = set(beamdirection['ids'])
elset_data = list(sorted(mat_ids.intersection(beamdir_ids)))
if elset_data:
names = [{'short': mat_data['ShortName']}, {'short': 'B0'}, {'short': beamrot_data['ShortName']}, {'short': 'D' + str(i)}]
ccx_elset = {}
ccx_elset['ccx_elset'] = elset_data
ccx_elset['ccx_elset_name'] = get_ccx_elset_name_short(names)
ccx_elset['mat_obj_name'] = mat_obj.Name
ccx_elset['ccx_mat_name'] = mat_obj.Material['Name']
ccx_elset['beamsection_obj'] = beamsec_obj
ccx_elset['beam_normal'] = beamdirection['normal'] # normal for this direction
self.ccx_elsets.append(ccx_elset)
def get_ccx_elsets_multiple_mat_multiple_beam(self):
beamrot_data = self.beamrotation_objects[0]
for beamsec_data in self.beamsection_objects:
beamsec_obj = beamsec_data['Object']
beamsec_ids = set(beamsec_data['FEMElements'])
for mat_data in self.material_objects:
mat_obj = mat_data['Object']
mat_ids = set(mat_data['FEMElements'])
for i, beamdirection in enumerate(beamrot_data['FEMRotations1D']):
beamdir_ids = set(beamdirection['ids'])
elset_data = list(sorted(beamsec_ids.intersection(mat_ids).intersection(beamdir_ids))) # empty intersection sets possible
if elset_data:
names = [{'short': mat_data['ShortName']}, {'short': beamsec_data['ShortName']}, {'short': beamrot_data['ShortName']}, {'short': 'D' + str(i)}]
ccx_elset = {}
ccx_elset['ccx_elset'] = elset_data
ccx_elset['ccx_elset_name'] = get_ccx_elset_name_short(names)
ccx_elset['mat_obj_name'] = mat_obj.Name
ccx_elset['ccx_mat_name'] = mat_obj.Material['Name']
ccx_elset['beamsection_obj'] = beamsec_obj
ccx_elset['beam_normal'] = beamdirection['normal'] # normal for this direction
self.ccx_elsets.append(ccx_elset)
# fluid
def get_ccx_elsets_single_mat_single_fluid(self):
mat_obj = self.material_objects[0]['Object']
@@ -1283,7 +1314,7 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter):
# Helpers
# ccx elset names: M .. Material, B .. Beam, F .. Fluid, S .. Shell, TODO write comment into input file to elset ids and elset attributes
# ccx elset names: M .. Material, B .. Beam, R .. BeamRotation, D ..Direction, F .. Fluid, S .. Shell, TODO write comment into input file to elset ids and elset attributes
def get_ccx_elset_name_standard(names):
# standard max length = 80
ccx_elset_name = ''

View File

@@ -78,6 +78,10 @@ class FemInputWriter():
self.ccx_efaces = 'Efaces'
self.ccx_eedges = 'Eedges'
self.ccx_elsets = []
if hasattr(self.mesh_object, "Shape"):
self.theshape = self.mesh_object.Shape
elif hasattr(self.mesh_object, "Part"):
self.theshape = self.mesh_object.Part
self.femmesh = self.mesh_object.FemMesh
self.femnodes_mesh = {}
self.femelement_table = {}
@@ -195,6 +199,12 @@ class FemInputWriter():
self.femelement_table = FemMeshTools.get_femelement_table(self.femmesh)
FemMeshTools.get_femelement_sets(self.femmesh, self.femelement_table, self.beamsection_objects)
def get_element_rotation1D_elements(self):
# get for each geometry edge direction the element ids and rotation norma
if not self.femelement_table:
self.femelement_table = FemMeshTools.get_femelement_table(self.femmesh)
FemMeshTools.get_femelement_direction1D_set(self.femmesh, self.femelement_table, self.beamrotation_objects, self.theshape)
def get_element_fluid1D_elements(self):
# get element ids and write them into the objects
print("Fluid sections")