diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index 0227fb21be..b7f416540d 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -192,6 +192,7 @@ SET(FemSolverCalculix_SRCS femsolver/calculix/con_contact.py femsolver/calculix/con_displacement.py femsolver/calculix/con_fixed.py + femsolver/calculix/con_fluidsection.py femsolver/calculix/con_force.py femsolver/calculix/con_heatflux.py femsolver/calculix/con_initialtemperature.py diff --git a/src/Mod/Fem/femsolver/calculix/con_fluidsection.py b/src/Mod/Fem/femsolver/calculix/con_fluidsection.py new file mode 100644 index 0000000000..8557950a1e --- /dev/null +++ b/src/Mod/Fem/femsolver/calculix/con_fluidsection.py @@ -0,0 +1,220 @@ +# *************************************************************************** +# * Copyright (c) 2021 Bernd Hahnebach * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * 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__ = "FreeCAD FEM calculix constraint fluidsection" +__author__ = "Bernd Hahnebach" +__url__ = "https://www.freecadweb.org" + + +import codecs +import os +import six +from os.path import join + +import FreeCAD + +from femmesh import meshtools + + +# ******************************************************************************************** +# handle elements for constraints fluidsection with Liquid Inlet or Outlet +# belongs to write_constraints_fluidsection, should be next method +# leave the constraints fluidsection code as the last constraint method in this module +# as it is none standard constraint method compared to all other constraints +def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter): + + # Fluid sections: + # fluidsection Liquid inlet outlet objs requires special element definition + # to fill ccxwriter.FluidInletoutlet_ele list the ccx_elset are needed + # thus this has to be after the creation of ccx_elsets + # different pipe cross sections will generate ccx_elsets + + ccxwriter.FluidInletoutlet_ele = [] + ccxwriter.fluid_inout_nodes_file = join( + ccxwriter.dir_name, + "{}_inout_nodes.txt".format(ccxwriter.mesh_name) + ) + + def get_fluidsection_inoutlet_obj_if_setdata(ccx_elset): + if ( + ccx_elset["ccx_elset"] + # use six to be sure to be Python 2.7 and 3.x compatible + and not isinstance(ccx_elset["ccx_elset"], six.string_types) + and "fluidsection_obj" in ccx_elset # fluid mesh + ): + fluidsec_obj = ccx_elset["fluidsection_obj"] + if ( + fluidsec_obj.SectionType == "Liquid" + and ( + fluidsec_obj.LiquidSectionType == "PIPE INLET" + or fluidsec_obj.LiquidSectionType == "PIPE OUTLET" + ) + ): + return fluidsec_obj + return None + + def is_fluidsection_inoutlet_setnames_possible(ccx_elsets): + for ccx_elset in ccx_elsets: + if ( + ccx_elset["ccx_elset"] + and "fluidsection_obj" in ccx_elset # fluid mesh + ): + fluidsec_obj = ccx_elset["fluidsection_obj"] + if ( + fluidsec_obj.SectionType == "Liquid" + and ( + fluidsec_obj.LiquidSectionType == "PIPE INLET" + or fluidsec_obj.LiquidSectionType == "PIPE OUTLET" + ) + ): + return True + return False + + # collect elementIDs for fluidsection Liquid inlet outlet objs + # if they have element data (happens if not "eall") + for ccx_elset in ccxwriter.ccx_elsets: + fluidsec_obj = get_fluidsection_inoutlet_obj_if_setdata(ccx_elset) + if fluidsec_obj is None: + continue + elsetchanged = False + counter = 0 + for elid in ccx_elset["ccx_elset"]: + counter = counter + 1 + if (elsetchanged is False) \ + and (fluidsec_obj.LiquidSectionType == "PIPE INLET"): + # 3rd index is to track which line nr the element is defined + ccxwriter.FluidInletoutlet_ele.append( + [str(elid), fluidsec_obj.LiquidSectionType, 0] + ) + elsetchanged = True + elif (fluidsec_obj.LiquidSectionType == "PIPE OUTLET") \ + and (counter == len(ccx_elset["ccx_elset"])): + # 3rd index is to track which line nr the element is defined + ccxwriter.FluidInletoutlet_ele.append( + [str(elid), fluidsec_obj.LiquidSectionType, 0] + ) + + # create the correct element definition for fluidsection Liquid inlet outlet objs + # at least one "fluidsection_obj" needs to be in ccx_elsets and has the attributes + # TODO: what if there are other objs in elsets? + if is_fluidsection_inoutlet_setnames_possible(ccxwriter.ccx_elsets) is not None: + # it is not distinguished if split input file + # for split input file the main file is just closed and reopend even if not needed + inpfile.close() + meshtools.use_correct_fluidinout_ele_def( + ccxwriter.FluidInletoutlet_ele, + ccxwriter.femmesh_file, + ccxwriter.fluid_inout_nodes_file + ) + inpfile = codecs.open(ccxwriter.file_name, "a", encoding="utf-8") + + return inpfile + + +# ******************************************************************************************** +# TODO: +# split method into separate methods and move some part into base writer +# see also method handle_fluidsection_liquid_inlet_outlet +def write_constraints_fluidsection(f, ccxwriter): + if not ccxwriter.fluidsection_objects: + return + if ccxwriter.analysis_type not in ["thermomech"]: + return + + # write constraint to file + f.write("\n***********************************************************\n") + f.write("** FluidSection constraints\n") + if os.path.exists(ccxwriter.fluid_inout_nodes_file): + inout_nodes_file = open(ccxwriter.fluid_inout_nodes_file, "r") + lines = inout_nodes_file.readlines() + inout_nodes_file.close() + else: + FreeCAD.Console.PrintError( + "1DFlow inout nodes file not found: {}\n" + .format(ccxwriter.fluid_inout_nodes_file) + ) + # get nodes + ccxwriter.get_constraints_fluidsection_nodes() + for femobj in ccxwriter.fluidsection_objects: + # femobj --> dict, FreeCAD document object is femobj["Object"] + fluidsection_obj = femobj["Object"] + f.write("** " + fluidsection_obj.Label + "\n") + 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": + # degree of freedom 2 is for defining pressure + f.write("{},{},{},{}\n".format( + b[0], + "2", + "2", + fluidsection_obj.InletPressure + )) + 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": + # degree of freedom 1 is for defining flow rate + # factor applied to convert unit from kg/s to t/s + f.write("{},{},{},{}\n".format( + b[1], + "1", + "1", + fluidsection_obj.InletFlowRate * 0.001 + )) + 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": + # degree of freedom 2 is for defining pressure + f.write("{},{},{},{}\n".format( + b[0], + "2", + "2", + fluidsection_obj.OutletPressure + )) + 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": + # degree of freedom 1 is for defining flow rate + # factor applied to convert unit from kg/s to t/s + f.write("{},{},{},{}\n".format( + b[1], + "1", + "1", + fluidsection_obj.OutletFlowRate * 0.001 + )) diff --git a/src/Mod/Fem/femsolver/calculix/writer.py b/src/Mod/Fem/femsolver/calculix/writer.py index 332c12ded1..57bb3485c8 100644 --- a/src/Mod/Fem/femsolver/calculix/writer.py +++ b/src/Mod/Fem/femsolver/calculix/writer.py @@ -42,6 +42,7 @@ from . import con_centrif from . import con_contact from . import con_displacement from . import con_fixed +from . import con_fluidsection from . import con_force from . import con_heatflux from . import con_initialtemperature as con_initialtemp @@ -192,7 +193,7 @@ class FemInputWriterCcx(writerbase.FemInputWriter): if self.fluidsection_objects: # some fluidsection objs need special treatment, ccx_elsets are needed for this - inpfile = self.handle_fluidsection_liquid_inlet_outlet(inpfile) + inpfile = con_fluidsection.handle_fluidsection_liquid_inlet_outlet(inpfile, self) # element sets constraints self.write_constraints_sets(inpfile, self.centrif_objects, con_centrif) @@ -234,7 +235,7 @@ class FemInputWriterCcx(writerbase.FemInputWriter): self.write_constraints_sets(inpfile, self.pressure_objects, con_pressure) self.write_constraints_data(inpfile, self.temperature_objects, con_temperature) self.write_constraints_sets(inpfile, self.heatflux_objects, con_heatflux) - self.write_constraints_fluidsection(inpfile) + con_fluidsection.write_constraints_fluidsection(inpfile, self) # output and step end self.write_outputs_types(inpfile) @@ -365,189 +366,6 @@ class FemInputWriterCcx(writerbase.FemInputWriter): if write_after != "": f.write(write_after) - # ******************************************************************************************** - # handle elements for constraints fluidsection with Liquid Inlet or Outlet - # belongs to write_constraints_fluidsection, should be next method - # leave the constraints fluidsection code as the last constraint method in this module - # as it is none standard constraint method compared to all other constraints - def handle_fluidsection_liquid_inlet_outlet(self, inpfile): - - # Fluid sections: - # fluidsection Liquid inlet outlet objs requires special element definition - # to fill self.FluidInletoutlet_ele list the ccx_elset are needed - # thus this has to be after the creation of ccx_elsets - # different pipe cross sections will generate ccx_elsets - - self.FluidInletoutlet_ele = [] - self.fluid_inout_nodes_file = join( - self.dir_name, - "{}_inout_nodes.txt".format(self.mesh_name) - ) - - def get_fluidsection_inoutlet_obj_if_setdata(ccx_elset): - if ( - ccx_elset["ccx_elset"] - # use six to be sure to be Python 2.7 and 3.x compatible - and not isinstance(ccx_elset["ccx_elset"], six.string_types) - and "fluidsection_obj" in ccx_elset # fluid mesh - ): - fluidsec_obj = ccx_elset["fluidsection_obj"] - if ( - fluidsec_obj.SectionType == "Liquid" - and ( - fluidsec_obj.LiquidSectionType == "PIPE INLET" - or fluidsec_obj.LiquidSectionType == "PIPE OUTLET" - ) - ): - return fluidsec_obj - return None - - def is_fluidsection_inoutlet_setnames_possible(ccx_elsets): - for ccx_elset in ccx_elsets: - if ( - ccx_elset["ccx_elset"] - and "fluidsection_obj" in ccx_elset # fluid mesh - ): - fluidsec_obj = ccx_elset["fluidsection_obj"] - if ( - fluidsec_obj.SectionType == "Liquid" - and ( - fluidsec_obj.LiquidSectionType == "PIPE INLET" - or fluidsec_obj.LiquidSectionType == "PIPE OUTLET" - ) - ): - return True - return False - - # collect elementIDs for fluidsection Liquid inlet outlet objs - # if they have element data (happens if not "eall") - for ccx_elset in self.ccx_elsets: - fluidsec_obj = get_fluidsection_inoutlet_obj_if_setdata(ccx_elset) - if fluidsec_obj is None: - continue - elsetchanged = False - counter = 0 - for elid in ccx_elset["ccx_elset"]: - counter = counter + 1 - if (elsetchanged is False) \ - and (fluidsec_obj.LiquidSectionType == "PIPE INLET"): - # 3rd index is to track which line nr the element is defined - self.FluidInletoutlet_ele.append( - [str(elid), fluidsec_obj.LiquidSectionType, 0] - ) - elsetchanged = True - elif (fluidsec_obj.LiquidSectionType == "PIPE OUTLET") \ - and (counter == len(ccx_elset["ccx_elset"])): - # 3rd index is to track which line nr the element is defined - self.FluidInletoutlet_ele.append( - [str(elid), fluidsec_obj.LiquidSectionType, 0] - ) - - # create the correct element definition for fluidsection Liquid inlet outlet objs - # at least one "fluidsection_obj" needs to be in ccx_elsets and has the attributes - # TODO: what if there are other objs in elsets? - if is_fluidsection_inoutlet_setnames_possible(self.ccx_elsets) is not None: - # it is not distinguished if split input file - # for split input file the main file is just closed and reopend even if not needed - inpfile.close() - meshtools.use_correct_fluidinout_ele_def( - self.FluidInletoutlet_ele, - self.femmesh_file, - self.fluid_inout_nodes_file - ) - inpfile = codecs.open(self.file_name, "a", encoding="utf-8") - - return inpfile - - # ******************************************************************************************** - # constraints fluidsection - # TODO: - # split method into separate methods and move some part into base writer - # see also method handle_fluidsection_liquid_inlet_outlet - def write_constraints_fluidsection(self, f): - if not self.fluidsection_objects: - return - if self.analysis_type not in ["thermomech"]: - return - - # write constraint to file - f.write("\n***********************************************************\n") - f.write("** FluidSection constraints\n") - if os.path.exists(self.fluid_inout_nodes_file): - inout_nodes_file = open(self.fluid_inout_nodes_file, "r") - lines = inout_nodes_file.readlines() - inout_nodes_file.close() - else: - FreeCAD.Console.PrintError( - "1DFlow inout nodes file not found: {}\n" - .format(self.fluid_inout_nodes_file) - ) - # 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"] - f.write("** " + fluidsection_obj.Label + "\n") - 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": - # degree of freedom 2 is for defining pressure - f.write("{},{},{},{}\n".format( - b[0], - "2", - "2", - fluidsection_obj.InletPressure - )) - 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": - # degree of freedom 1 is for defining flow rate - # factor applied to convert unit from kg/s to t/s - f.write("{},{},{},{}\n".format( - b[1], - "1", - "1", - fluidsection_obj.InletFlowRate * 0.001 - )) - 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": - # degree of freedom 2 is for defining pressure - f.write("{},{},{},{}\n".format( - b[0], - "2", - "2", - fluidsection_obj.OutletPressure - )) - 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": - # degree of freedom 1 is for defining flow rate - # factor applied to convert unit from kg/s to t/s - f.write("{},{},{},{}\n".format( - b[1], - "1", - "1", - fluidsection_obj.OutletFlowRate * 0.001 - )) - # ******************************************************************************************** # step begin and end def write_step_begin(self, f):