diff --git a/src/Mod/Fem/FemInputWriter.py b/src/Mod/Fem/FemInputWriter.py index f9d2e439e..f6a8d51d1 100644 --- a/src/Mod/Fem/FemInputWriter.py +++ b/src/Mod/Fem/FemInputWriter.py @@ -40,7 +40,7 @@ class FemInputWriter(): contact_obj, planerotation_obj, transform_obj, selfweight_obj, force_obj, pressure_obj, temperature_obj, heatflux_obj, initialtemperature_obj, - beamsection_obj, shellthickness_obj, + beamsection_obj, shellthickness_obj, fluidsection_obj, analysis_type, dir_name ): self.analysis = analysis_obj @@ -60,6 +60,7 @@ class FemInputWriter(): self.heatflux_objects = heatflux_obj self.initialtemperature_objects = initialtemperature_obj self.beamsection_objects = beamsection_obj + self.fluidsection_objects = fluidsection_obj self.shellthickness_objects = shellthickness_obj self.analysis_type = analysis_type self.dir_name = dir_name @@ -108,6 +109,11 @@ class FemInputWriter(): for femobj in self.temperature_objects: # femobj --> dict, FreeCAD document object is femobj['Object'] femobj['Nodes'] = FemMeshTools.get_femnodes_by_femobj_with_references(self.femmesh, femobj) + def get_constraints_fluidsection_nodes(self): + # get nodes + for femobj in self.fluidsection_objects: # femobj --> dict, FreeCAD document object is femobj['Object'] + femobj['Nodes'] = FemMeshTools.get_femnodes_by_femobj_with_references(self.femmesh, femobj) + def get_constraints_force_nodeloads(self): # check shape type of reference shape for femobj in self.force_objects: # femobj --> dict, FreeCAD document object is femobj['Object'] diff --git a/src/Mod/Fem/FemInputWriterCcx.py b/src/Mod/Fem/FemInputWriterCcx.py index 5ed0c1470..a18347b95 100644 --- a/src/Mod/Fem/FemInputWriterCcx.py +++ b/src/Mod/Fem/FemInputWriterCcx.py @@ -44,7 +44,7 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): contact_obj, planerotation_obj, transform_obj, selfweight_obj, force_obj, pressure_obj, temperature_obj, heatflux_obj, initialtemperature_obj, - beamsection_obj, shellthickness_obj, + beamsection_obj, shellthickness_obj, fluidsection_obj, analysis_type=None, dir_name=None ): @@ -56,10 +56,11 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): contact_obj, planerotation_obj, transform_obj, selfweight_obj, force_obj, pressure_obj, temperature_obj, heatflux_obj, initialtemperature_obj, - beamsection_obj, shellthickness_obj, + beamsection_obj, shellthickness_obj, fluidsection_obj, analysis_type, dir_name) self.main_file_name = self.mesh_object.Name + '.inp' self.file_name = self.dir_name + '/' + self.main_file_name + self.FluidInletoutlet_ele = [] print('FemInputWriterCcx --> self.dir_name --> ' + self.dir_name) print('FemInputWriterCcx --> self.main_file_name --> ' + self.main_file_name) print('FemInputWriterCcx --> self.file_name --> ' + self.file_name) @@ -79,6 +80,11 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): inpfile = open(self.file_name, 'a') inpfile.write('\n\n') + # Check to see if fluid sections are in analysis and use D network element type + if self.fluidsection_objects: + inpfile.close() + FemMeshTools.write_D_network_element_to_inputfile(self.file_name) + inpfile = open(self.file_name, 'a') # node and element sets self.write_element_sets_material_and_femelement_type(inpfile) if self.fixed_objects: @@ -100,6 +106,21 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): self.write_constraints_initialtemperature(inpfile) self.write_femelementsets(inpfile) + # Fluid section: Inlet and Outlet requires special element definition + if self.fluidsection_objects: + InOuttest = False + for ccx_elset in self.ccx_elsets: + if ccx_elset['ccx_elset']: + if 'fluidsection_obj'in ccx_elset: # fluid mesh + fluidsec_obj = ccx_elset['fluidsection_obj'] + if fluidsec_obj.SectionType == "Liquid": + if (fluidsec_obj.LiquidSectionType == "PIPE INLET") or (fluidsec_obj.LiquidSectionType == "PIPE OUTLET"): + InOuttest = True + if InOuttest is True: + inpfile.close() + FemMeshTools.use_correct_fluidinout_ele_def(self.FluidInletoutlet_ele, self.file_name) + inpfile = open(self.file_name, 'a') + # constraints independent from steps if self.planerotation_objects: self.write_constraints_planerotation(inpfile) @@ -138,6 +159,8 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): self.write_constraints_temperature(inpfile) if self.heatflux_objects: self.write_constraints_heatflux(inpfile) + if self.fluidsection_objects: + self.write_constraints_fluidsection(inpfile) # output and step end self.write_outputs_types(inpfile) @@ -155,7 +178,7 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): # first open file with "write" to ensure that each new iteration of writing of inputfile starts in new file # first open file with "write" to ensure that the .writeABAQUS also writes in inputfile inpfileMain = open(self.file_name, 'w') - inpfileMain.close + inpfileMain.close() inpfileMain = open(self.file_name, 'a') inpfileMain.write('\n\n') @@ -163,9 +186,15 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): name = self.file_name[:-4] include_name = self.main_file_name[:-4] - inpfileNodesElem = open(name + "_Node_Elem_sets.inp", 'w') self.femmesh.writeABAQUS(name + "_Node_Elem_sets.inp") - inpfileNodesElem.close + inpfileNodesElem = open(name + "_Node_Elem_sets.inp", 'a') + inpfileNodesElem.write('\n***********************************************************\n') + inpfileNodesElem.close() + + # Check to see if fluid sections are in analysis and use D network element type + if self.fluidsection_objects: + FemMeshTools.write_D_network_element_to_inputfile(name + "_Node_Elem_sets.inp") + inpfileMain.write('\n***********************************************************\n') inpfileMain.write('**Nodes and Elements\n') inpfileMain.write('** written by femmesh.writeABAQUS\n') @@ -238,6 +267,19 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): self.write_constraints_initialtemperature(inpfileMain) self.write_femelementsets(inpfileMain) + # Fluid section: Inlet and Outlet requires special element definition + if self.fluidsection_objects: + InOuttest = False + for ccx_elset in self.ccx_elsets: + if ccx_elset['ccx_elset']: + if 'fluidsection_obj'in ccx_elset: # fluid mesh + fluidsec_obj = ccx_elset['fluidsection_obj'] + if fluidsec_obj.SectionType == "Liquid": + if (fluidsec_obj.LiquidSectionType == "PIPE INLET") or (fluidsec_obj.LiquidSectionType == "PIPE OUTLET"): + InOuttest = True + if InOuttest is True: + FemMeshTools.use_correct_fluidinout_ele_def(self.FluidInletoutlet_ele, name + "_Node_Elem_sets.inp") + # constraints independent from steps if self.planerotation_objects: self.write_constraints_planerotation(inpfileMain) @@ -276,6 +318,8 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): self.write_constraints_temperature(inpfileMain) if self.heatflux_objects: self.write_constraints_heatflux(inpfileHeatflux) + if self.fluidsection_objects: + self.write_constraints_fluidsection(inpfileMain) # include seperately written constraints in input file inpfileMain.write('\n***********************************************************\n') @@ -308,13 +352,17 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): def write_element_sets_material_and_femelement_type(self, f): f.write('\n***********************************************************\n') - f.write('** Element sets for materials and FEM element type (solid, shell, beam)\n') + 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)) if len(self.material_objects) == 1: if self.beamsection_objects and len(self.beamsection_objects) == 1: # single mat, single beam self.get_ccx_elsets_single_mat_single_beam() elif self.beamsection_objects and len(self.beamsection_objects) > 1: # single mat, multiple beams self.get_ccx_elsets_single_mat_multiple_beam() + elif self.fluidsection_objects and len(self.fluidsection_objects) == 1: # single mat, single fluid + self.get_ccx_elsets_single_mat_single_fluid() + elif self.fluidsection_objects and len(self.fluidsection_objects) > 1: # single mat, multiple fluids + self.get_ccx_elsets_single_mat_multiple_fluid() elif self.shellthickness_objects and len(self.shellthickness_objects) == 1: # single mat, single shell self.get_ccx_elsets_single_mat_single_shell() elif self.shellthickness_objects and len(self.shellthickness_objects) > 1: # single mat, multiple shells @@ -326,6 +374,10 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): self.get_ccx_elsets_multiple_mat_single_beam() elif self.beamsection_objects and len(self.beamsection_objects) > 1: # multiple mats, multiple beams self.get_ccx_elsets_multiple_mat_multiple_beam() + if self.fluidsection_objects and len(self.fluidsection_objects) == 1: # multiple mats, single fluid + self.get_ccx_elsets_multiple_mat_single_fluid() + elif self.fluidsection_objects and len(self.fluidsection_objects) > 1: # multiple mats, multiple fluids + self.get_ccx_elsets_multiple_mat_multiple_fluid() elif self.shellthickness_objects and len(self.shellthickness_objects) == 1: # multiple mats, single shell self.get_ccx_elsets_multiple_mat_single_shell() elif self.shellthickness_objects and len(self.shellthickness_objects) > 1: # multiple mats, multiple shells @@ -334,12 +386,27 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): self.get_ccx_elsets_multiple_mat_solid() for ccx_elset in self.ccx_elsets: f.write('*ELSET,ELSET=' + ccx_elset['ccx_elset_name'] + '\n') + collect_ele = False + if ccx_elset['ccx_elset']: + if 'fluidsection_obj'in ccx_elset: + fluidsec_obj = ccx_elset['fluidsection_obj'] + if fluidsec_obj.SectionType == 'Liquid': + if (fluidsec_obj.LiquidSectionType == "PIPE INLET") or (fluidsec_obj.LiquidSectionType == "PIPE OUTLET"): + collect_ele = True if ccx_elset['ccx_elset']: if ccx_elset['ccx_elset'] == self.ccx_eall: f.write(self.ccx_eall + '\n') else: + elsetchanged = 0 + counter = 0 for elid in ccx_elset['ccx_elset']: f.write(str(elid) + ',\n') + counter = counter + 1 + if collect_ele is True and elsetchanged == 0 and fluidsec_obj.LiquidSectionType == "PIPE INLET": + self.FluidInletoutlet_ele.append([str(elid), fluidsec_obj.LiquidSectionType, 0]) # 3rd index is to track which line number the element is defined + elsetchanged = 1 + elif collect_ele is True and fluidsec_obj.LiquidSectionType == "PIPE OUTLET" and counter == len(ccx_elset['ccx_elset']): + self.FluidInletoutlet_ele.append([str(elid), fluidsec_obj.LiquidSectionType, 0]) # 3rd index is to track which line number the element is defined else: f.write('**No elements found for these objects\n') @@ -470,35 +537,47 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): mat_obj = femobj['Object'] mat_info_name = mat_obj.Material['Name'] mat_name = mat_obj.Name - # get material properties, Currently in SI units: M/kg/s/Kelvin - YM = FreeCAD.Units.Quantity(mat_obj.Material['YoungsModulus']) - YM_in_MPa = float(YM.getValueAs('MPa')) - PR = float(mat_obj.Material['PoissonRatio']) + # get material properties of solid material, Currently in SI units: M/kg/s/Kelvin + if mat_obj.Category == 'Solid': + YM = FreeCAD.Units.Quantity(mat_obj.Material['YoungsModulus']) + YM_in_MPa = float(YM.getValueAs('MPa')) + PR = float(mat_obj.Material['PoissonRatio']) if self.analysis_type == "frequency" or self.selfweight_objects or (self.analysis_type == "thermomech" and not self.solver_obj.ThermoMechSteadyState): density = FreeCAD.Units.Quantity(mat_obj.Material['Density']) density_in_tonne_per_mm3 = float(density.getValueAs('t/mm^3')) if self.analysis_type == "thermomech": TC = FreeCAD.Units.Quantity(mat_obj.Material['ThermalConductivity']) TC_in_WmK = float(TC.getValueAs('W/m/K')) # SvdW: Add factor to force units to results' base units of t/mm/s/K - W/m/K results in no factor needed - TEC = FreeCAD.Units.Quantity(mat_obj.Material['ThermalExpansionCoefficient']) - TEC_in_mmK = float(TEC.getValueAs('mm/mm/K')) SH = FreeCAD.Units.Quantity(mat_obj.Material['SpecificHeat']) SH_in_JkgK = float(SH.getValueAs('J/kg/K')) * 1e+06 # SvdW: Add factor to force units to results' base units of t/mm/s/K + if mat_obj.Category == 'Solid': + TEC = FreeCAD.Units.Quantity(mat_obj.Material['ThermalExpansionCoefficient']) + TEC_in_mmK = float(TEC.getValueAs('mm/mm/K')) + elif mat_obj.Category == 'Fluid': + DV = FreeCAD.Units.Quantity(mat_obj.Material['DynamicViscosity']) + DV_in_tmms = float(DV.getValueAs('t/mm/s')) # write material properties f.write('** FreeCAD material name: ' + mat_info_name + '\n') f.write('*MATERIAL, NAME=' + mat_name + '\n') - f.write('*ELASTIC\n') - f.write('{0:.0f}, {1:.3f}\n'.format(YM_in_MPa, PR)) + if mat_obj.Category == 'Solid': + f.write('*ELASTIC\n') + f.write('{0:.0f}, {1:.3f}\n'.format(YM_in_MPa, PR)) + if self.analysis_type == "frequency" or self.selfweight_objects or (self.analysis_type == "thermomech" and not self.solver_obj.ThermoMechSteadyState): f.write('*DENSITY\n') f.write('{0:.3e}\n'.format(density_in_tonne_per_mm3)) if self.analysis_type == "thermomech": - f.write('*CONDUCTIVITY\n') - f.write('{0:.3f}\n'.format(TC_in_WmK)) - f.write('*EXPANSION\n') - f.write('{0:.3e}\n'.format(TEC_in_mmK)) - f.write('*SPECIFIC HEAT\n') - f.write('{0:.3e}\n'.format(SH_in_JkgK)) + if mat_obj.Category == 'Solid': + f.write('*CONDUCTIVITY\n') + f.write('{0:.3f}\n'.format(TC_in_WmK)) + f.write('*EXPANSION\n') + f.write('{0:.3e}\n'.format(TEC_in_mmK)) + f.write('*SPECIFIC HEAT\n') + f.write('{0:.3e}\n'.format(SH_in_JkgK)) + elif mat_obj.Category == 'Fluid': + f.write('*FLUID CONSTANTS\n') + f.write('{0:.3e}, {1:.3e}\n'.format(SH_in_JkgK, DV_in_tmms)) + # nonlinear material properties if self.solver_obj.MaterialNonlinearity == 'nonlinear': for femobj in self.material_nonlinear_objects: # femobj --> dict, FreeCAD document object is femobj['Object'] @@ -548,6 +627,22 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): setion_def = '*BEAM GENERAL SECTION, ' + elsetdef + material + section_type + '\n' f.write(setion_def) f.write(setion_geo) + elif 'fluidsection_obj'in ccx_elset: # fluid mesh + fluidsec_obj = ccx_elset['fluidsection_obj'] + elsetdef = 'ELSET=' + ccx_elset['ccx_elset_name'] + ', ' + material = 'MATERIAL=' + ccx_elset['mat_obj_name'] + if fluidsec_obj.SectionType == 'Liquid': + section_type = fluidsec_obj.LiquidSectionType + if (section_type == "PIPE INLET") or (section_type == "PIPE OUTLET"): + section_type = "PIPE INOUT" + setion_def = '*FLUID SECTION, ' + elsetdef + 'TYPE=' + section_type + ', ' + material + '\n' + setion_geo = liquid_section_def(fluidsec_obj, section_type) + elif fluidsec_obj.SectionType == 'Gas': + section_type = fluidsec_obj.GasSectionType + elif fluidsec_obj.SectionType == 'Open Channel': + section_type = fluidsec_obj.ChannelSectionType + f.write(setion_def) + f.write(setion_geo) elif 'shellthickness_obj'in ccx_elset: # shell mesh shellth_obj = ccx_elset['shellthickness_obj'] elsetdef = 'ELSET=' + ccx_elset['ccx_elset_name'] + ', ' @@ -735,10 +830,10 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): f.write('** Self weight Constraint\n') f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) for femobj in self.selfweight_objects: # femobj --> dict, FreeCAD document object is femobj['Object'] - selwei_obj_name = femobj['Object'].Name - f.write('** ' + selwei_obj_name + '\n') + selwei_obj = femobj['Object'] + f.write('** ' + selwei_obj.Name + '\n') f.write('*DLOAD\n') - f.write('Eall,GRAV,9810,0,0,-1\n') + f.write('Eall,GRAV,9810,' + str(selwei_obj.Gravity_x) + ',' + str(selwei_obj.Gravity_y) + ',' + str(selwei_obj.Gravity_z) + '\n') f.write('\n') # grav (erdbeschleunigung) is equal for all elements # should be only one constraint @@ -831,28 +926,78 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): for i in v: f.write("{},S{},{}\n".format(i[0], i[1], heatflux_obj.DFlux * 0.001)) + def write_constraints_fluidsection(self, f): + f.write('\n***********************************************************\n') + f.write('** FluidSection constraints\n') + f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) + if os.path.exists("inout_nodes.txt"): + inout_nodes_file = open("inout_nodes.txt", "r") + lines = inout_nodes_file.readlines() + inout_nodes_file.close() + # get nodes + self.get_constraints_fluidsection_nodes() + for femobj in self.fluidsection_objects: # femobj --> dict, FreeCAD document object is femobj['Object'] + fluidsection_obj = femobj['Object'] + if fluidsection_obj.SectionType == 'Liquid': + if fluidsection_obj.LiquidSectionType == 'PIPE INLET': + f.write('**Fluid Section Inlet \n') + if fluidsection_obj.InletPressureActive is True: + f.write('*BOUNDARY \n') + for n in femobj['Nodes']: + for line in lines: + b = line.split(',') + if int(b[0]) == n and b[3] == 'PIPE INLET\n': + f.write(b[0] + ',2,2,' + str(fluidsection_obj.InletPressure) + '\n') # degree of freedom 2 is for defining pressure + if fluidsection_obj.InletFlowRateActive is True: + f.write('*BOUNDARY,MASS FLOW \n') + for n in femobj['Nodes']: + for line in lines: + b = line.split(',') + if int(b[0]) == n and b[3] == 'PIPE INLET\n': + f.write(b[1] + ',1,1,' + str(fluidsection_obj.InletFlowRate * 0.001) + '\n') # degree of freedom 1 is for defining flow rate, factor applied to convet unit from kg/s to t/s + elif fluidsection_obj.LiquidSectionType == 'PIPE OUTLET': + f.write('**Fluid Section Outlet \n') + if fluidsection_obj.OutletPressureActive is True: + f.write('*BOUNDARY \n') + for n in femobj['Nodes']: + for line in lines: + b = line.split(',') + if int(b[0]) == n and b[3] == 'PIPE OUTLET\n': + f.write(b[0] + ',2,2,' + str(fluidsection_obj.OutletPressure) + '\n') # degree of freedom 2 is for defining pressure + if fluidsection_obj.OutletFlowRateActive is True: + f.write('*BOUNDARY,MASS FLOW \n') + for n in femobj['Nodes']: + for line in lines: + b = line.split(',') + if int(b[0]) == n and b[3] == 'PIPE OUTLET\n': + f.write(b[1] + ',1,1,' + str(fluidsection_obj.OutletFlowRate * 0.001) + '\n') # degree of freedom 1 is for defining flow rate, factor applied to convet unit from kg/s to t/s + def write_outputs_types(self, f): f.write('\n***********************************************************\n') f.write('** Outputs --> frd file\n') f.write('** written by {} function\n'.format(sys._getframe().f_code.co_name)) - if self.beamsection_objects or self.shellthickness_objects: + if self.beamsection_objects or self.shellthickness_objects or self.fluidsection_objects: f.write('*NODE FILE, OUTPUT=2d\n') else: f.write('*NODE FILE\n') if self.analysis_type == "thermomech": # MPH write out nodal temperatures if thermomechanical - f.write('U, NT\n') + if not self.fluidsection_objects: + f.write('U, NT\n') + else: + f.write('MF, PS\n') else: f.write('U\n') - f.write('*EL FILE\n') - if self.solver_obj.MaterialNonlinearity == 'nonlinear': - f.write('S, E, PEEQ\n') - else: - f.write('S, E\n') - f.write('** outputs --> dat file\n') - f.write('*NODE PRINT , NSET=Nall \n') - f.write('U \n') - f.write('*EL PRINT , ELSET=Eall \n') - f.write('S \n') + if not self.fluidsection_objects: + f.write('*EL FILE\n') + if self.solver_obj.MaterialNonlinearity == 'nonlinear': + f.write('S, E, PEEQ\n') + else: + f.write('S, E\n') + f.write('** outputs --> dat file\n') + f.write('*NODE PRINT , NSET=Nall \n') + f.write('U \n') + f.write('*EL PRINT , ELSET=Eall \n') + f.write('S \n') def write_step_end(self, f): f.write('\n***********************************************************\n') @@ -897,6 +1042,17 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): ccx_elset['ccx_mat_name'] = mat_obj.Material['Name'] self.ccx_elsets.append(ccx_elset) + def get_ccx_elsets_single_mat_single_fluid(self): + mat_obj = self.material_objects[0]['Object'] + fluidsec_obj = self.fluidsection_objects[0]['Object'] + ccx_elset = {} + ccx_elset['fluidsection_obj'] = fluidsec_obj + ccx_elset['ccx_elset'] = self.ccx_eall + ccx_elset['ccx_elset_name'] = get_ccx_elset_fluid_name(mat_obj.Name, fluidsec_obj.Name) + ccx_elset['mat_obj_name'] = mat_obj.Name + ccx_elset['ccx_mat_name'] = mat_obj.Material['Name'] + self.ccx_elsets.append(ccx_elset) + def get_ccx_elsets_single_mat_single_shell(self): mat_obj = self.material_objects[0]['Object'] shellth_obj = self.shellthickness_objects[0]['Object'] @@ -932,6 +1088,21 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): ccx_elset['ccx_mat_name'] = mat_obj.Material['Name'] self.ccx_elsets.append(ccx_elset) + def get_ccx_elsets_single_mat_multiple_fluid(self): + if not self.femelement_table: + self.femelement_table = FemMeshTools.get_femelement_table(self.femmesh) + mat_obj = self.material_objects[0]['Object'] + FemMeshTools.get_femelement_sets(self.femmesh, self.femelement_table, self.fluidsection_objects) + for fluidsec_data in self.fluidsection_objects: + fluidsec_obj = fluidsec_data['Object'] + ccx_elset = {} + ccx_elset['fluidsection_obj'] = fluidsec_obj + ccx_elset['ccx_elset'] = fluidsec_data['FEMElements'] + ccx_elset['ccx_elset_name'] = get_ccx_elset_fluid_name(mat_obj.Name, fluidsec_obj.Name, None, fluidsec_data['ShortName']) + ccx_elset['mat_obj_name'] = mat_obj.Name + ccx_elset['ccx_mat_name'] = mat_obj.Material['Name'] + self.ccx_elsets.append(ccx_elset) + def get_ccx_elsets_single_mat_multiple_shell(self): if not self.femelement_table: self.femelement_table = FemMeshTools.get_femelement_table(self.femmesh) @@ -962,6 +1133,21 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): ccx_elset['ccx_mat_name'] = mat_obj.Material['Name'] self.ccx_elsets.append(ccx_elset) + def get_ccx_elsets_multiple_mat_single_fluid(self): + if not self.femelement_table: + self.femelement_table = FemMeshTools.get_femelement_table(self.femmesh) + fluidsec_obj = self.fluidsection_objects[0]['Object'] + FemMeshTools.get_femelement_sets(self.femmesh, self.femelement_table, self.material_objects) + for mat_data in self.material_objects: + mat_obj = mat_data['Object'] + ccx_elset = {} + ccx_elset['fluidsection_obj'] = fluidsec_obj + ccx_elset['ccx_elset'] = mat_data['FEMElements'] + ccx_elset['ccx_elset_name'] = get_ccx_elset_fluid_name(mat_obj.Name, fluidsec_obj.Name, mat_data['ShortName']) + ccx_elset['mat_obj_name'] = mat_obj.Name + ccx_elset['ccx_mat_name'] = mat_obj.Material['Name'] + self.ccx_elsets.append(ccx_elset) + def get_ccx_elsets_multiple_mat_single_shell(self): if not self.femelement_table: self.femelement_table = FemMeshTools.get_femelement_table(self.femmesh) @@ -1021,6 +1207,27 @@ class FemInputWriterCcx(FemInputWriter.FemInputWriter): ccx_elset['ccx_mat_name'] = mat_obj.Material['Name'] self.ccx_elsets.append(ccx_elset) + def get_ccx_elsets_multiple_mat_multiple_fluid(self): + if not self.femelement_table: + self.femelement_table = FemMeshTools.get_femelement_table(self.femmesh) + FemMeshTools.get_femelement_sets(self.femmesh, self.femelement_table, self.fluidsection_objects) + FemMeshTools.get_femelement_sets(self.femmesh, self.femelement_table, self.material_objects) + for fluidsec_data in self.fluidsection_objects: + fluidsec_obj = fluidsec_data['Object'] + for mat_data in self.material_objects: + mat_obj = mat_data['Object'] + ccx_elset = {} + ccx_elset['fluidsection_obj'] = fluidsec_obj + elemids = [] + for elemid in fluidsec_data['FEMElements']: + if elemid in mat_data['FEMElements']: + elemids.append(elemid) + ccx_elset['ccx_elset'] = elemids + ccx_elset['ccx_elset_name'] = get_ccx_elset_fluid_name(mat_obj.Name, fluidsec_obj.Name, mat_data['ShortName'], fluidsec_data['ShortName']) + ccx_elset['mat_obj_name'] = mat_obj.Name + ccx_elset['ccx_mat_name'] = mat_obj.Material['Name'] + self.ccx_elsets.append(ccx_elset) + def get_ccx_elsets_multiple_mat_multiple_shell(self): if not self.femelement_table: self.femelement_table = FemMeshTools.get_femelement_table(self.femmesh) @@ -1055,6 +1262,17 @@ def get_ccx_elset_beam_name(mat_name, beamsec_name, mat_short_name=None, beamsec return mat_name + beamsec_name +def get_ccx_elset_fluid_name(mat_name, fluidsec_name, mat_short_name=None, fluidsec_short_name=None): + if not mat_short_name: + mat_short_name = 'Mat0' + if not fluidsec_short_name: + fluidsec_short_name = 'Fluid0' + if len(mat_name + fluidsec_name) > 20: # max identifier lenght in CalculiX for beam elsets + return mat_short_name + fluidsec_short_name + else: + return mat_name + fluidsec_name + + def get_ccx_elset_shell_name(mat_name, shellth_name, mat_short_name=None, shellth_short_name=None): if not mat_short_name: mat_short_name = 'Mat0' @@ -1076,4 +1294,61 @@ def get_ccx_elset_solid_name(mat_name, solid_name=None, mat_short_name=None): else: return mat_name + solid_name + +def liquid_section_def(obj, section_type): + if section_type == 'PIPE MANNING': + manning_area = str(obj.ManningArea.getValueAs('mm^2').Value) + manning_radius = str(obj.ManningRadius.getValueAs('mm')) + manning_coefficient = str(obj.ManningCoefficient) + section_geo = manning_area + ',' + manning_radius + ',' + manning_coefficient + '\n' + return section_geo + elif section_type == 'PIPE ENLARGEMENT': + enlarge_area1 = str(obj.EnlargeArea1.getValueAs('mm^2').Value) + enlarge_area2 = str(obj.EnlargeArea2.getValueAs('mm^2').Value) + section_geo = enlarge_area1 + ',' + enlarge_area2 + '\n' + return section_geo + elif section_type == 'PIPE CONTRACTION': + contract_area1 = str(obj.ContractArea1.getValueAs('mm^2').Value) + contract_area2 = str(obj.ContractArea2.getValueAs('mm^2').Value) + section_geo = contract_area1 + ',' + contract_area2 + '\n' + return section_geo + elif section_type == 'PIPE ENTRANCE': + entrance_pipe_area = str(obj.EntrancePipeArea.getValueAs('mm^2').Value) + entrance_area = str(obj.EntranceArea.getValueAs('mm^2').Value) + section_geo = entrance_pipe_area + ',' + entrance_area + '\n' + return section_geo + elif section_type == 'PIPE DIAPHRAGM': + diaphragm_pipe_area = str(obj.DiaphragmPipeArea.getValueAs('mm^2').Value) + diaphragm_area = str(obj.DiaphragmArea.getValueAs('mm^2').Value) + section_geo = diaphragm_pipe_area + ',' + diaphragm_area + '\n' + return section_geo + elif section_type == 'PIPE BEND': + bend_pipe_area = str(obj.BendPipeArea.getValueAs('mm^2').Value) + bend_radius_diameter = str(obj.BendRadiusDiameter) + bend_angle = str(obj.BendAngle) + bend_loss_coefficient = str(obj.BendLossCoefficient) + section_geo = bend_pipe_area + ',' + bend_radius_diameter + ',' + bend_angle +',' + bend_loss_coefficient + '\n' + return section_geo + elif section_type == 'PIPE GATE VALVE': + gatevalve_pipe_area = str(obj.GateValvePipeArea.getValueAs('mm^2').Value) + gatevalve_closing_coeff = str(obj.GateValveClosingCoeff) + section_geo = gatevalve_pipe_area + ',' + gatevalve_closing_coeff + '\n' + return section_geo + elif section_type == 'PIPE WHITE-COLEBROOK': + colebrooke_area = str(obj.ColebrookeArea.getValueAs('mm^2').Value) + colebrooke_diameter = str(2 * obj.ColebrookeRadius.getValueAs('mm')) + colebrooke_grain_diameter = str(obj.ColebrookeGrainDiameter.getValueAs('mm')) + colebrooke_form_factor = str(obj.ColebrookeFormFactor) + section_geo = colebrooke_area + ',' + colebrooke_diameter + ',-1,' + colebrooke_grain_diameter + ',' + colebrooke_form_factor + '\n' + return section_geo + elif section_type == 'LIQUID PUMP': + section_geo = '' + for i in range(len(obj.PumpFlowRate)): + flow_rate = str(obj.PumpFlowRate[i]) + head = str(obj.PumpHeadLoss[i]) + section_geo = section_geo + flow_rate + ',' + head + ',' + section_geo = section_geo + '\n' + return section_geo + else: + return '' # @} diff --git a/src/Mod/Fem/FemMeshTools.py b/src/Mod/Fem/FemMeshTools.py index e35940d48..27b42f46b 100644 --- a/src/Mod/Fem/FemMeshTools.py +++ b/src/Mod/Fem/FemMeshTools.py @@ -406,6 +406,8 @@ def get_elset_short_name(obj, i): return 'Mat' + str(i) elif hasattr(obj, "Proxy") and obj.Proxy.Type == 'FemBeamSection': return 'Beam' + str(i) + elif hasattr(obj, "Proxy") and obj.Proxy.Type == 'FemFluidSection': + return 'Fluid' + str(i) elif hasattr(obj, "Proxy") and obj.Proxy.Type == 'FemShellThickness': return 'Shell' + str(i) else: @@ -1028,7 +1030,7 @@ def get_analysis_group_elements(aAnalysis, aPart): else: FreeCAD.Console.PrintError('Problem: more than one object with empty references.\n') print('We gone try to get the empty material references anyway.\n') - # ShellThickness and BeamSection could have empty references, but on solid meshes only materials should have empty references + # ShellThickness, BeamSection and FluidSection could have empty references, but on solid meshes only materials should have empty references for er in empty_references: print(er.Name) group_elements = get_anlysis_empty_references_group_elements(group_elements, aAnalysis, aPart.Shape) @@ -1075,7 +1077,7 @@ def get_anlysis_empty_references_group_elements(group_elements, aAnalysis, aShap '''get the elementIDs if the Reference shape is empty see get_analysis_group_elements() for more informatations on solid meshes only material objects could have an empty reference without beeing something wrong! - face meshes could have empty ShellThickness and edge meshes could have empty BeamSection + face meshes could have empty ShellThickness and edge meshes could have empty BeamSection/FluidSection ''' # print(group_elements) material_ref_shapes = [] @@ -1424,6 +1426,89 @@ def get_cylindrical_coords(obj): return coords +def write_D_network_element_to_inputfile(fileName): + # replace B32 elements with D elements for fluid section + f = open(fileName, 'r+') + lines = f.readlines() + f.seek(0) + for line in lines: + if line.find("B32") == -1: + f.write(line) + else: + dummy = line.replace("B32", "D") + f.write(dummy) + f.truncate() + f.close() + + +def use_correct_fluidinout_ele_def(FluidInletoutlet_ele, fileName): + f = open(fileName, 'r') + cnt = 0 + line = f.readline() + + # start reading from *ELEMENT + while line.find("Element") == -1: + line = f.readline() + cnt = cnt + 1 + line = f.readline() + cnt = cnt + 1 + + + # obtain element line numbers for inlet and outlet + while (len(line) > 1): + ind = line.find(',') + elem = line[0:ind] + for i in range(len(FluidInletoutlet_ele)): + if (elem == FluidInletoutlet_ele[i][0]): + FluidInletoutlet_ele[i][2] = cnt + line = f.readline() + cnt = cnt + 1 + f.close() + + # re-define elements for INLET and OUTLET + f = open(fileName, 'r+') + lines = f.readlines() + f.seek(0) + cnt = 0 + elem_counter = 0 + inout_nodes_file = open("inout_nodes.txt", "w") + for line in lines: + new_line = '' + for i in range(len(FluidInletoutlet_ele)): + if (cnt == FluidInletoutlet_ele[i][2]): + elem_counter = elem_counter + 1 + a = line.split(',') + for j in range(len(a)): + if elem_counter == 1: + if j == 1: + new_line = new_line + ' 0,' + node1 = int(a[j + 2]) + node2 = int(a[j + 1]) + node3 = int(a[j]) + inout_nodes_file.write(str(node1) + ',' + str(node2) + ',' + str(node3) + ',' + FluidInletoutlet_ele[i][1] + '\n') + elif j == 3: + new_line = new_line + a[j] + else: + new_line = new_line + a[j] + ',' + else: + if j == 3: + new_line = new_line + ' 0\n' + node1 = int(a[j - 2]) + node2 = int(a[j - 1]) + node3 = int(a[j]) + inout_nodes_file.write(str(node1) + ',' + str(node2) + ',' + str(node3) + ',' + FluidInletoutlet_ele[i][1] + '\n') + else: + new_line = new_line + a[j] + ',' + if new_line == '': + f.write(line) + else: + f.write(new_line) + cnt = cnt + 1 + f.truncate() + f.close() + inout_nodes_file.close() + + def make_femmesh(mesh_data): ''' makes an FreeCAD FEM Mesh object from FEM Mesh data ''' diff --git a/src/Mod/Fem/FemTools.py b/src/Mod/Fem/FemTools.py index a01ca11a7..ad1455682 100644 --- a/src/Mod/Fem/FemTools.py +++ b/src/Mod/Fem/FemTools.py @@ -157,6 +157,7 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): # [{'Object':heatflux_constraints, 'xxxxxxxx':value}, {}, ...] # [{'Object':initialtemperature_constraints, 'xxxxxxxx':value}, {}, ...] # [{'Object':beam_sections, 'xxxxxxxx':value}, {}, ...] + # [{'Object':fluid_sections, 'xxxxxxxx':value}, {}, ...] # [{'Object':shell_thicknesses, 'xxxxxxxx':value}, {}, ...] # [{'Object':contact_constraints, 'xxxxxxxx':value}, {}, ...] @@ -191,6 +192,10 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): # set of beam sections from the analysis. Updated with update_objects # Individual beam sections are Proxy.Type "FemBeamSection" self.beam_sections = [] + ## @var fluid_sections + # set of fluid sections from the analysis. Updated with update_objects + # Individual fluid sections are Proxy.Type "FemFluidSection" + self.fluid_sections = [] ## @var shell_thicknesses # set of shell thicknesses from the analysis. Updated with update_objects # Individual shell thicknesses are Proxy.Type "FemShellThickness" @@ -301,6 +306,10 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): beam_section_dict = {} beam_section_dict['Object'] = m self.beam_sections.append(beam_section_dict) + elif hasattr(m, "Proxy") and m.Proxy.Type == "FemFluidSection": + fluid_section_dict = {} + fluid_section_dict['Object'] = m + self.fluid_sections.append(fluid_section_dict) elif hasattr(m, "Proxy") and m.Proxy.Type == "FemShellThickness": shell_thickness_dict = {} shell_thickness_dict['Object'] = m @@ -338,8 +347,8 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): if self.mesh: if self.mesh.FemMesh.VolumeCount == 0 and self.mesh.FemMesh.FaceCount > 0 and not self.shell_thicknesses: message += "FEM mesh has no volume elements, either define a shell thicknesses or provide a FEM mesh with volume elements.\n" - if self.mesh.FemMesh.VolumeCount == 0 and self.mesh.FemMesh.FaceCount == 0 and self.mesh.FemMesh.EdgeCount > 0 and not self.beam_sections: - message += "FEM mesh has no volume and no shell elements, either define a beam section or provide a FEM mesh with volume elements.\n" + if self.mesh.FemMesh.VolumeCount == 0 and self.mesh.FemMesh.FaceCount == 0 and self.mesh.FemMesh.EdgeCount > 0 and not self.beam_sections and not self.fluid_sections: + message += "FEM mesh has no volume and no shell elements, either define a beam/fluid section or provide a FEM mesh with volume elements.\n" if self.mesh.FemMesh.VolumeCount == 0 and self.mesh.FemMesh.FaceCount == 0 and self.mesh.FemMesh.EdgeCount == 0: message += "FEM mesh has neither volume nor shell or edge elements. Provide a FEM mesh with elements!\n" # materials linear and nonlinear @@ -353,14 +362,16 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): has_no_references = True for m in self.materials_linear: mat_map = m['Object'].Material - if 'YoungsModulus' in mat_map: - # print Units.Quantity(mat_map['YoungsModulus']).Value - if not Units.Quantity(mat_map['YoungsModulus']).Value: - message += "Value of YoungsModulus is set to 0.0.\n" - else: - message += "No YoungsModulus defined for at least one material.\n" - if 'PoissonRatio' not in mat_map: - message += "No PoissonRatio defined for at least one material.\n" # PoissonRatio is allowed to be 0.0 (in ccx), but it should be set anyway. + mat_obj = m['Object'] + if mat_obj.Category == 'Solid': + if 'YoungsModulus' in mat_map: + # print Units.Quantity(mat_map['YoungsModulus']).Value + if not Units.Quantity(mat_map['YoungsModulus']).Value: + message += "Value of YoungsModulus is set to 0.0.\n" + else: + message += "No YoungsModulus defined for at least one material.\n" + if 'PoissonRatio' not in mat_map: + message += "No PoissonRatio defined for at least one material.\n" # PoissonRatio is allowed to be 0.0 (in ccx), but it should be set anyway. if self.analysis_type == "frequency" or self.selfweight_constraints: if 'Density' not in mat_map: message += "No Density defined for at least one material.\n" @@ -389,14 +400,18 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): # no check in the regard of loads (constraint force, pressure, self weight) is done because an analysis without loads at all is an valid analysis too if self.analysis_type == "thermomech": if not self.initialtemperature_constraints: - message += "Thermomechanical analysis: No initial temperature defined.\n" + if not self.fluid_sections: + message += "Thermomechanical analysis: No initial temperature defined.\n" if len(self.initialtemperature_constraints) > 1: message += "Thermomechanical analysis: Only one initial temperature is allowed.\n" - # beam sections and shell thicknesses + # beam sections, fluid sections and shell thicknesses if self.beam_sections: if self.shell_thicknesses: # this needs to be checked only once either here or in shell_thicknesses message += "Beam Sections and shell thicknesses in one analysis is not supported at the moment.\n" + if self.fluid_sections: + # this needs to be checked only once either here or in shell_thicknesses + message += "Beam Sections and Fluid Sections in one analysis is not supported at the moment.\n" has_no_references = False for b in self.beam_sections: if len(b['Object'].References) == 0: @@ -420,6 +435,22 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): message += "Shell thicknesses defined but FEM mesh has volume elements.\n" if self.mesh.FemMesh.FaceCount == 0: message += "Shell thicknesses defined but FEM mesh has no shell elements.\n" + if self.fluid_sections: + if not self.selfweight_constraints: + message += "A fluid network analysis requires self weight constraint to be applied" + if self.analysis_type != "thermomech": + message += "A fluid network analysis can only be done in a thermomech analysis" + has_no_references = False + for f in self.fluid_sections: + if len(f['Object'].References) == 0: + if has_no_references is True: + message += "More than one fluid section has an empty references list (Only one empty references list is allowed!).\n" + has_no_references = True + if self.mesh: + if self.mesh.FemMesh.FaceCount > 0 or self.mesh.FemMesh.VolumeCount > 0: + message += "Fluid sections defined but FEM mesh has volume or shell elements.\n" + if self.mesh.FemMesh.EdgeCount == 0: + message += "Fluid sections defined but FEM mesh has no edge elements.\n" return message ## Sets base_name diff --git a/src/Mod/Fem/FemToolsCcx.py b/src/Mod/Fem/FemToolsCcx.py index 5fa27b331..c99e86962 100644 --- a/src/Mod/Fem/FemToolsCcx.py +++ b/src/Mod/Fem/FemToolsCcx.py @@ -96,7 +96,7 @@ class FemToolsCcx(FemTools.FemTools): self.contact_constraints, self.planerotation_constraints, self.transform_constraints, self.selfweight_constraints, self.force_constraints, self.pressure_constraints, self.temperature_constraints, self.heatflux_constraints, self.initialtemperature_constraints, - self.beam_sections, self.shell_thicknesses, + self.beam_sections, self.shell_thicknesses, self.fluid_sections, self.analysis_type, self.working_dir) self.inp_file_name = inp_writer.write_calculix_input_file() except: