diff --git a/CfdOF/CfdTools.py b/CfdOF/CfdTools.py index 961a7736..bc325e9e 100644 --- a/CfdOF/CfdTools.py +++ b/CfdOF/CfdTools.py @@ -288,6 +288,13 @@ def getScalarTransportFunctionsGroup(analysis_object): return group +# Reactions +def getReactionModels(analysis_object): + group = [] + # TODO complete + return group + + # Mesh def getMeshRefinementObjs(mesh_obj): from CfdOF.Mesh.CfdMeshRefinement import CfdMeshRefinement diff --git a/CfdOF/Solve/CfdCaseWriterFoam.py b/CfdOF/Solve/CfdCaseWriterFoam.py index 2a021d9c..fc342aed 100644 --- a/CfdOF/Solve/CfdCaseWriterFoam.py +++ b/CfdOF/Solve/CfdCaseWriterFoam.py @@ -45,6 +45,7 @@ def __init__(self, analysis_obj): self.bc_group = CfdTools.getCfdBoundaryGroup(analysis_obj) self.initial_conditions = CfdTools.getInitialConditions(analysis_obj) self.reporting_functions = CfdTools.getReportingFunctionsGroup(analysis_obj) + self.reaction_models = CfdTools.getReactionModels(analysis_obj) self.scalar_transport_objs = CfdTools.getScalarTransportFunctionsGroup(analysis_obj) self.porous_zone_objs = CfdTools.getPorousZoneObjects(analysis_obj) self.initialisation_zone_objs = CfdTools.getInitialisationZoneObjects(analysis_obj) @@ -90,6 +91,7 @@ def writeCase(self): 'boundaries': dict((b.Label, CfdTools.propsToDict(b)) for b in self.bc_group), 'reportingFunctions': dict((fo.Label, CfdTools.propsToDict(fo)) for fo in self.reporting_functions), 'reportingFunctionsEnabled': False, + 'reactionsEnabled': False, 'scalarTransportFunctions': dict((st.Label, CfdTools.propsToDict(st)) for st in self.scalar_transport_objs), 'scalarTransportFunctionsEnabled': False, 'dynamicMesh': {}, @@ -138,6 +140,10 @@ def writeCase(self): cfdMessage('Dynamic mesh adaptation rule present\n') self.processDynamicMeshRefinement() + if self.reaction_models: + cfdMessage('Reactions present\n') + self.processReactions() + self.settings['createPatchesFromSnappyBaffles'] = False self.settings['createPatchesForPeriodics'] = False cfdMessage("Matching boundary conditions ...\n") @@ -201,6 +207,11 @@ def getSolverName(self): raise RuntimeError("Only isothermal analysis is currently supported for free surface flow simulation.") else: raise RuntimeError("Only transient analysis is supported for free surface flow simulation.") + elif self.physics_model.Phase == 'Eulerian': + if len(self.material_objs) >= 2: + solver = 'multiphaseEulerFoam' + else: + raise RuntimeError("At least two material objects must be present for Eulerian multiphase simulation.") else: raise RuntimeError(self.physics_model.Phase + " phase model currently not supported.") @@ -331,7 +342,7 @@ def processBoundaryConditions(self): beta = (1-wireDiam/spacing)**2 bc['PressureDropCoeff'] = CD*(1-beta) - if settings['solver']['SolverName'] in ['interFoam', 'multiphaseInterFoam']: + if settings['solver']['SolverName'] in ['interFoam', 'multiphaseInterFoam', 'multiphaseEulerFoam']: # Make sure the first n-1 alpha values exist, and write the n-th one # consistently for multiphaseInterFoam sum_alpha = 0.0 @@ -339,7 +350,7 @@ def processBoundaryConditions(self): for i, m in enumerate(settings['fluidProperties']): alpha_name = m['Name'] if i == len(settings['fluidProperties']) - 1: - if settings['solver']['SolverName'] == 'multiphaseInterFoam': + if settings['solver']['SolverName'] == 'multiphaseInterFoam' or settings['solver']['SolverName'] == 'multiphaseEulerFoam': alphas_new[alpha_name] = 1.0 - sum_alpha else: alpha = Units.Quantity(bc.get('VolumeFractions', {}).get(alpha_name, '0')).Value @@ -417,7 +428,7 @@ def processInitialConditions(self): if settings['solver']['SolverName'] in ['simpleFoam', 'porousSimpleFoam', 'pimpleFoam', 'SRFSimpleFoam']: mat_prop = settings['fluidProperties'][0] initial_values['KinematicPressure'] = initial_values['Pressure'] / mat_prop['Density'] - if settings['solver']['SolverName'] in ['interFoam', 'multiphaseInterFoam']: + if settings['solver']['SolverName'] in ['interFoam', 'multiphaseInterFoam', 'multiphaseEulerFoam']: # Make sure the first n-1 alpha values exist, and write the n-th one # consistently for multiphaseInterFoam sum_alpha = 0.0 @@ -425,7 +436,7 @@ def processInitialConditions(self): for i, m in enumerate(settings['fluidProperties']): alpha_name = m['Name'] if i == len(settings['fluidProperties'])-1: - if settings['solver']['SolverName'] == 'multiphaseInterFoam': + if settings['solver']['SolverName'] == 'multiphaseInterFoam' or settings['solver']['SolverName'] == 'multiphaseEulerFoam': alphas_new[alpha_name] = 1.0-sum_alpha else: alpha = Units.Quantity(initial_values.get('VolumeFractions', {}).get(alpha_name, '0')).Value @@ -570,7 +581,7 @@ def processReportingFunctions(self): rf['Pitch'] = tuple(p for p in pitch_axis) settings['reportingFunctions'][name]['ProbePosition'] = tuple( - Units.Quantity(p, Units.Length).getValueAs('m') + Units.Quantity(p, Units.Length).getValueAs('m') for p in settings['reportingFunctions'][name]['ProbePosition']) def processScalarTransportFunctions(self): @@ -584,6 +595,11 @@ def processScalarTransportFunctions(self): stf['InjectionPoint'] = tuple( Units.Quantity(p, Units.Length).getValueAs('m') for p in stf['InjectionPoint']) + # Process reactions and reactions models + def processReactions(self): + settings = self.settings + settings['reactionsEnabled'] = True + # Mesh related def processDynamicMeshRefinement(self): settings = self.settings @@ -592,14 +608,14 @@ def processDynamicMeshRefinement(self): # Check whether cellLevel supported if self.mesh_obj.MeshUtility not in ['cfMesh', 'snappyHexMesh']: raise RuntimeError("Dynamic mesh refinement is only supported by cfMesh and snappyHexMesh") - + # Check whether 2D extrusion present mesh_refinements = CfdTools.getMeshRefinementObjs(self.mesh_obj) for mr in mesh_refinements: if mr.Extrusion: if mr.ExtrusionType == '2DPlanar' or mr.ExtrusionType == '2DWedge': raise RuntimeError("Dynamic mesh refinement will not work with 2D or wedge mesh") - + settings['dynamicMesh'] = CfdTools.propsToDict(self.dynamic_mesh_refinement_obj) if isinstance(self.dynamic_mesh_refinement_obj.Proxy, CfdDynamicMeshRefinement.CfdDynamicMeshShockRefinement): settings['dynamicMesh']['Type'] = 'shock' @@ -669,7 +685,7 @@ def processPorousZoneProperties(self): def processInitialisationZoneProperties(self): settings = self.settings - + for zone_name in settings['initialisationZones']: z = settings['initialisationZones'][zone_name] @@ -684,7 +700,7 @@ def processInitialisationZoneProperties(self): if not z['TemperatureSpecified']: del z['Temperature'] - if settings['solver']['SolverName'] in ['interFoam', 'multiphaseInterFoam']: + if settings['solver']['SolverName'] in ['interFoam', 'multiphaseInterFoam', 'multiphaseEulerFoam']: # Make sure the first n-1 alpha values exist, and write the n-th one # consistently for multiphaseInterFoam sum_alpha = 0.0 @@ -693,14 +709,14 @@ def processInitialisationZoneProperties(self): for i, m in enumerate(settings['fluidProperties']): alpha_name = m['Name'] if i == len(settings['fluidProperties'])-1: - if settings['solver']['SolverName'] == 'multiphaseInterFoam': + if settings['solver']['SolverName'] == 'multiphaseInterFoam' or settings['solver']['SolverName'] == 'multiphaseEulerFoam': alphas_new[alpha_name] = 1.0-sum_alpha else: alpha = Units.Quantity(z['VolumeFractions'].get(alpha_name, '0')).Value alphas_new[alpha_name] = alpha sum_alpha += alpha z['VolumeFractions'] = alphas_new - + if settings['solver']['SolverName'] in ['simpleFoam', 'porousSimpleFoam', 'pimpleFoam', 'SRFSimpleFoam']: if 'Pressure' in z: z['KinematicPressure'] = z['Pressure']/settings['fluidProperties'][0]['Density'] diff --git a/CfdOF/Solve/CfdPhasePhysicsSelection.py b/CfdOF/Solve/CfdPhasePhysicsSelection.py new file mode 100644 index 00000000..13993cd0 --- /dev/null +++ b/CfdOF/Solve/CfdPhasePhysicsSelection.py @@ -0,0 +1,231 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2024 Jonathan Bergh * +# * * +# * This program is free software: you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 3 of the * +# * License, or (at your option) any later version. * +# * * +# * 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 Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with this program. If not, * +# * see . * +# * * +# *************************************************************************** + + +import os +import os.path +import FreeCAD +if FreeCAD.GuiUp: + import FreeCADGui + from PySide import QtCore +from CfdOF import CfdTools +from CfdOF.CfdTools import addObjectProperty + + +def makeCfdPhasePhysicsSelection(name="PhasePhysicsModel"): + # DocumentObjectGroupPython, FeaturePython, GeometryPython + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name) + CfdPhasePhysicsModel(obj) + if FreeCAD.GuiUp: + ViewProviderCfdPhasePhysicsSelection(obj.ViewObject) + return obj + + +class CommandCfdPhasePhysicsSelection: + """ CFD physics selection command definition """ + + def GetResources(self): + icon_path = os.path.join(CfdTools.getModulePath(), "Gui", "Icons", "phasephysics.svg") + return {'Pixmap': icon_path, + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Cfd_PhasePhysicsModel", "Select Eulerian phase models"), + 'Accel': "", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Cfd_PhysicsModel", "Select the Eulerian multiphase physics models")} + + def IsActive(self): + return CfdTools.getActiveAnalysis() is not None + + def Activated(self): + FreeCAD.ActiveDocument.openTransaction("Choose appropriate phase physics model") + is_present = False + members = CfdTools.getActiveAnalysis().Group + for i in members: + if isinstance(i.Proxy, CfdPhasePhysicsModel): + FreeCADGui.activeDocument().setEdit(i.Name) + is_present = True + + # Allow to re-create if deleted + if not is_present: + FreeCADGui.doCommand("") + FreeCADGui.doCommand("from CfdOF.Solve import CfdPhasePhysicsSelection") + FreeCADGui.doCommand("from CfdOF import CfdTools") + FreeCADGui.doCommand( + "CfdTools.getActiveAnalysis().addObject(CfdPhasePhysicsSelection.makeCfdPhasePhysicsSelection())") + FreeCADGui.ActiveDocument.setEdit(FreeCAD.ActiveDocument.ActiveObject.Name) + + +class CfdPhasePhysicsModel: + """ The Eulerian multiphase specific physics models """ + def __init__(self, obj): + obj.Proxy = self + self.Type = "PhasePhysicsModel" + self.initProperties(obj) + + def initProperties(self, obj): + + if addObjectProperty(obj, "Drag", ['SchillerNeuman'], "App::PropertyEnumeration", "Phase Physics modelling", + "Drag force model"): + obj.Drag = 'SchillerNeuman' + + if addObjectProperty(obj, "Lift", [''], + "App::PropertyEnumeration", "Phase Physics modelling", "Lift force model"): + obj.Lift = '' + + if addObjectProperty(obj, "SurfaceTension", ['Constant'], "App::PropertyEnumeration", "Phase Physics modelling", + "Surface tension force model"): + obj.SurfaceTension = 'Constant' + + if addObjectProperty(obj, "VirtualMass", ['ConstantCoefficient'], + "App::PropertyEnumeration", "Phase Physics modelling", "Virtual mass force model"): + obj.VirtualMass = 'ConstantCoefficient' + + if addObjectProperty(obj, "HeatTransfer", ['RanzMarshall'], + "App::PropertyEnumeration", "Phase Physics modelling", "Interphase heat transfer model"): + obj.HeatTransfer = 'RanzMarshall' + + if addObjectProperty(obj, "PhaseTransfer", [''], + "App::PropertyEnumeration", "Phase Physics modelling", "Interphase mass transfer model"): + obj.PhaseTransfer = '' + + if addObjectProperty(obj, "WallLubrication", [''], + "App::PropertyEnumeration", "Phase Physics modelling", "Wall lubrication model"): + obj.WallLubrication = '' + + if addObjectProperty(obj, "TurbulentDispersion", [''], + "App::PropertyEnumeration", "Phase Physics modelling", "Turbulent dispersion model"): + obj.TurbulentDispersion = '' + + if addObjectProperty(obj, "InterfaceCompression", [''], + "App::PropertyEnumeration", "Phase Physics modelling", "Interface compression model"): + obj.InterfaceCompression = '' + + # SRF model + addObjectProperty(obj, 'SRFModelEnabled', False, "App::PropertyBool", "Reference frame", + "Single Rotating Frame model enabled") + + addObjectProperty(obj, 'SRFModelRPM', '0', "App::PropertyQuantity", "Reference frame", "Rotational speed") + + addObjectProperty(obj, 'SRFModelCoR', FreeCAD.Vector(0, 0, 0), "App::PropertyPosition", "Reference frame", + "Centre of rotation (SRF)") + + addObjectProperty(obj, 'SRFModelAxis', FreeCAD.Vector(0, 0, 0), "App::PropertyPosition", "Reference frame", + "Axis of rotation (SRF)") + + def onDocumentRestored(self, obj): + self.initProperties(obj) + + +class _CfdPhasePhysicsModel: + """ Backward compatibility for old class name when loading from file """ + def onDocumentRestored(self, obj): + CfdPhasePhysicsModel(obj) + + def __getstate__(self): + return None + + def __setstate__(self, state): + return None + + # dumps and loads replace __getstate__ and __setstate__ post v. 0.21.2 + def dumps(self): + return None + + def loads(self, state): + return None + + +class ViewProviderCfdPhasePhysicsSelection: + def __init__(self, vobj): + vobj.Proxy = self + self.taskd = None + + def getIcon(self): + icon_path = os.path.join(CfdTools.getModulePath(), "Gui", "Icons", "phasephysics.svg") + return icon_path + + def attach(self, vobj): + self.ViewObject = vobj + self.Object = vobj.Object + self.bubbles = None + + def updateData(self, obj, prop): + analysis_obj = CfdTools.getParentAnalysisObject(obj) + if analysis_obj and not analysis_obj.Proxy.loading: + analysis_obj.NeedsCaseRewrite = True + + def onChanged(self, vobj, prop): + return + + def setEdit(self, vobj, mode): + from CfdOF.Solve import TaskPanelCfdPhasePhysicsSelection + import importlib + importlib.reload(TaskPanelCfdPhasePhysicsSelection) + self.taskd = TaskPanelCfdPhasePhysicsSelection.TaskPanelCfdPhasePhysicsSelection(self.Object) + self.taskd.obj = vobj.Object + FreeCADGui.Control.showDialog(self.taskd) + return True + + def doubleClicked(self, vobj): + # Make sure no other task dialog still active + doc = FreeCADGui.getDocument(vobj.Object.Document) + if not doc.getInEdit(): + doc.setEdit(vobj.Object.Name) + else: + FreeCAD.Console.PrintError('Task dialog already active\n') + FreeCADGui.Control.showTaskView() + return True + + def unsetEdit(self, vobj, mode): + if self.taskd: + self.taskd.closing() + self.taskd = None + FreeCADGui.Control.closeDialog() + + def __getstate__(self): + return None + + def __setstate__(self, state): + return None + + # dumps and loads replace __getstate__ and __setstate__ post v. 0.21.2 + def dumps(self): + return None + + def loads(self, state): + return None + + +class _ViewProviderPhasePhysicsSelection: + """ Backward compatibility for old class name when loading from file """ + def attach(self, vobj): + new_proxy = ViewProviderCfdPhasePhysicsSelection(vobj) + new_proxy.attach(vobj) + + def __getstate__(self): + return None + + def __setstate__(self, state): + return None + + # dumps and loads replace __getstate__ and __setstate__ post v. 0.21.2 + def dumps(self): + return None + + def loads(self, state): + return None diff --git a/CfdOF/Solve/CfdPhysicsSelection.py b/CfdOF/Solve/CfdPhysicsSelection.py index f7d2023e..91ca2f2d 100644 --- a/CfdOF/Solve/CfdPhysicsSelection.py +++ b/CfdOF/Solve/CfdPhysicsSelection.py @@ -4,7 +4,7 @@ # * Copyright (c) 2017-2018 Johan Heyns (CSIR) * # * Copyright (c) 2017-2018 Oliver Oxtoby (CSIR) * # * Copyright (c) 2019-2022 Oliver Oxtoby * -# * Copyright (c) 2022 Jonathan Bergh * +# * Copyright (c) 2022-2024 Jonathan Bergh * # * * # * This program is free software: you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License as * @@ -124,7 +124,7 @@ def initProperties(self, obj): else: obj.Flow = prev_flow - if addObjectProperty(obj, "Phase", ['Single', 'FreeSurface'], "App::PropertyEnumeration", "Physics modelling", + if addObjectProperty(obj, "Phase", ['Single', 'FreeSurface', 'Eulerian'], "App::PropertyEnumeration", "Physics modelling", "Type of phases present"): obj.Phase = 'Single' @@ -160,6 +160,10 @@ def initProperties(self, obj): addObjectProperty(obj, 'SRFModelAxis', FreeCAD.Vector(0, 0, 0), "App::PropertyPosition", "Reference frame", "Axis of rotation (SRF)") + # Reacting model + addObjectProperty(obj, 'ReactionModelsEnabled', False, "App::PropertyBool", "Physics modelling", + "Reaction models enabled") + def onDocumentRestored(self, obj): self.initProperties(obj) diff --git a/CfdOF/Solve/TaskPanelCfdFluidProperties.py b/CfdOF/Solve/TaskPanelCfdFluidProperties.py index 4748b6f5..920649f2 100644 --- a/CfdOF/Solve/TaskPanelCfdFluidProperties.py +++ b/CfdOF/Solve/TaskPanelCfdFluidProperties.py @@ -4,6 +4,7 @@ # * Copyright (c) 2017-2018 Oliver Oxtoby (CSIR) * # * Copyright (c) 2017-2018 Alfred Bogaers (CSIR) * # * Copyright (c) 2019-2022 Oliver Oxtoby * +# * Copyright (c) 2024 Jonathan Bergh * # * * # * This program is free software: you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License as * @@ -41,12 +42,15 @@ def __init__(self, obj, physics_obj): 'Gui', "TaskPanelCfdFluidProperties.ui")) self.material = self.obj.Material + self.all_materials = CfdTools.getMaterials(CfdTools.getActiveAnalysis()) self.form.compressibleCheckBox.setVisible(self.physics_obj.Flow == "NonIsothermal") # Make sure it is checked in the default case since object was initialised with Isothermal self.form.compressibleCheckBox.setChecked(self.material.get('Type') != "Incompressible") self.form.compressibleCheckBox.stateChanged.connect(self.compressibleCheckBoxChanged) + self.form.otherPhaseLabel.setVisible(self.physics_obj.Phase == 'FreeSurface' or self.physics_obj.Phase == 'Eulerian') + self.text_boxes = {} self.fields = [] if self.physics_obj.Flow == 'Isothermal': @@ -88,13 +92,13 @@ def createUIBasedOnPhysics(self): self.form.propertiesLayout.removeRow(0) if self.material['Type'] == 'Isothermal': - self.fields = ['Density', 'DynamicViscosity'] + self.fields = ['Density', 'DynamicViscosity', 'BubbleDiameter', 'SurfaceTensionCoefficient'] elif self.material['Type'] == 'Incompressible': self.fields = ['MolarMass', 'DensityPolynomial', 'CpPolynomial', 'DynamicViscosityPolynomial', - 'ThermalConductivityPolynomial'] + 'ThermalConductivityPolynomial', 'BubbleDiameter', 'SurfaceTensionCoefficient'] else: self.fields = ['MolarMass', 'Cp', 'SutherlandTemperature', 'SutherlandRefTemperature', - 'SutherlandRefViscosity'] + 'SutherlandRefViscosity', 'BubbleDiameter', 'SurfaceTensionCoefficient'] self.text_boxes = {} for name in self.fields: @@ -108,6 +112,20 @@ def createUIBasedOnPhysics(self): self.text_boxes[name] = widget widget.setText(val) widget.textChanged.connect(self.manualEdit) + elif name.endswith("SurfaceTensionCoefficient"): + for material in self.all_materials: + if material.Label != self.material['Name']: + widget = FreeCADGui.UiLoader().createWidget("Gui::InputField") + widget.setObjectName(name) + widget.setProperty("format", "g") + val = self.material.get(name, '0') + widget.setProperty("unit", val) + widget.setProperty("minimum", 0) + widget.setProperty("singleStep", 0.1) + self.form.otherPhaseLayout.addRow(name+material.Label+":", widget) + self.text_boxes[name] = widget + setQuantity(widget, val) + widget.valueChanged.connect(self.manualEdit) else: widget = FreeCADGui.UiLoader().createWidget("Gui::InputField") widget.setObjectName(name) diff --git a/CfdOF/Solve/TaskPanelCfdPhasePhysicsSelection.py b/CfdOF/Solve/TaskPanelCfdPhasePhysicsSelection.py new file mode 100644 index 00000000..33d7d69f --- /dev/null +++ b/CfdOF/Solve/TaskPanelCfdPhasePhysicsSelection.py @@ -0,0 +1,261 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2024 Jonathan Bergh * +# * * +# * This program is free software: you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 3 of the * +# * License, or (at your option) any later version. * +# * * +# * 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 Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with this program. If not, * +# * see . * +# * * +# *************************************************************************** + +import os +import os.path +import FreeCAD +if FreeCAD.GuiUp: + import FreeCADGui +from CfdOF import CfdTools +from CfdOF.CfdTools import getQuantity, setQuantity, storeIfChanged + +MULTIPHASE_SYSTEM = ['Basic', 'Population balance', 'Thermal Phase Change', 'Thermal Phase Change Population Balance', 'Interface Composition Phase Change', 'Interface Composition Phase Change Population Balance'] +PHASE_MODELS = ['Pure', 'Pure Isothermal', 'Pure Stationary', 'Pure Stationay Isothermal', 'Reacting', 'Multicomponent', 'Multicomponent Isothermal'] + +DRAG_MODELS = ['SchillerNauman', 'IshiiZuber'] +LIFT_MODELS = ['none', 'constantCoefficient', 'wallDamped', 'Tomiyama'] +SURFACE_TENSION_MODELS = ['none', 'constant'] +TURBULENT_DISPERSION_MODELS = ['none', 'constantCoefficient', 'Gosman', 'LopezDeBertodano', 'Burns'] +WALL_LUBRICATION_MODELS = ['none', 'Frank', 'Tomiyama', 'Antal'] +VIRTUAL_MASS_MODELS = ['none', 'constantCoefficient', 'Lamb'] +INTERFACE_COMPRESSION_MODELS = [''] + +HEAT_TRANSFER_MODELS = ['Gunn', 'spherical', 'RanzMarshall', 'constantNu'] +PHASE_TRANSFER_MODELS = ['deposition', 'reactionDriven'] + + +class TaskPanelCfdPhasePhysicsSelection: + def __init__(self, obj): + FreeCADGui.Selection.clearSelection() + self.sel_server = None + self.obj = obj + self.form = FreeCADGui.PySideUic.loadUi(os.path.join(CfdTools.getModulePath(), 'Gui', "TaskPanelPhasePhysics.ui")) + + self.form.radioButtonSteady.toggled.connect(self.updateUI) + self.form.radioButtonTransient.toggled.connect(self.updateUI) + self.form.radioButtonSinglePhase.toggled.connect(self.updateUI) + self.form.radioButtonFreeSurface.toggled.connect(self.updateUI) + self.form.checkBoxIsothermal.stateChanged.connect(self.updateUI) + self.form.viscousCheckBox.stateChanged.connect(self.updateUI) + self.form.srfCheckBox.stateChanged.connect(self.updateUI) + self.form.radioButtonLaminar.toggled.connect(self.updateUI) + self.form.radioButtonRANS.toggled.connect(self.updateUI) + self.form.radioButtonDES.toggled.connect(self.updateUI) + self.form.radioButtonLES.toggled.connect(self.updateUI) + + self.load() + + def load(self): + + # Time + if self.obj.Time == 'Steady': + self.form.radioButtonSteady.toggle() + elif self.obj.Time == 'Transient': + self.form.radioButtonTransient.toggle() + + # Phase + if self.obj.Phase == 'Single': + self.form.radioButtonSinglePhase.toggle() + elif self.obj.Phase == 'FreeSurface': + self.form.radioButtonFreeSurface.toggle() + elif self.obj.Phase == 'Eulerian': + self.form.radioButtonEulerian.toggle() + + # Flow + self.form.checkBoxIsothermal.setChecked(self.obj.Flow == 'Isothermal') + self.form.checkBoxHighMach.setChecked(self.obj.Flow == 'HighMachCompressible') + + # Reactions + self.form.checkBoxReacting.setChecked(self.obj.ReactionModelsEnabled) + + # Turbulence + if self.obj.Turbulence == 'Inviscid': + self.form.viscousCheckBox.setChecked(False) + self.form.radioButtonLaminar.toggle() + if self.obj.Turbulence == 'Laminar': + self.form.viscousCheckBox.setChecked(True) + self.form.radioButtonLaminar.toggle() + elif self.obj.Turbulence == 'RANS': + self.form.viscousCheckBox.setChecked(True) + self.form.radioButtonRANS.toggle() + elif self.obj.Turbulence == 'DES': + self.form.viscousCheckBox.setChecked(True) + self.form.radioButtonDES.toggle() + elif self.obj.Turbulence == 'LES': + self.form.viscousCheckBox.setChecked(True) + self.form.radioButtonLES.toggle() + + # Gravity + setQuantity(self.form.gx, self.obj.gx) + setQuantity(self.form.gy, self.obj.gy) + setQuantity(self.form.gz, self.obj.gz) + + # SRF model + self.form.srfCheckBox.setChecked(self.obj.SRFModelEnabled) + + setQuantity(self.form.inputSRFCoRx, self.obj.SRFModelCoR.x) + setQuantity(self.form.inputSRFCoRy, self.obj.SRFModelCoR.y) + setQuantity(self.form.inputSRFCoRz, self.obj.SRFModelCoR.z) + + setQuantity(self.form.inputSRFAxisx, self.obj.SRFModelAxis.x) + setQuantity(self.form.inputSRFAxisy, self.obj.SRFModelAxis.y) + setQuantity(self.form.inputSRFAxisz, self.obj.SRFModelAxis.z) + + setQuantity(self.form.inputSRFRPM, self.obj.SRFModelRPM) + + self.updateUI() + + def updateUI(self): + self.form.TimeFrame.setVisible(True) + self.form.FlowFrame.setVisible(True) + self.form.turbulenceFrame.setVisible(True) + + # Steady / transient + if self.form.radioButtonSteady.isChecked(): + self.form.radioButtonFreeSurface.setEnabled(False) + if self.form.radioButtonDES.isChecked() or self.form.radioButtonLES.isChecked(): + self.form.radioButtonRANS.toggle() + self.form.radioButtonDES.setEnabled(False) + self.form.radioButtonLES.setEnabled(False) + if self.form.radioButtonFreeSurface.isChecked(): + self.form.radioButtonSinglePhase.toggle() + else: + self.form.radioButtonFreeSurface.setEnabled(True) + self.form.radioButtonDES.setEnabled(True) + self.form.radioButtonLES.setEnabled(True) + + # Gravity + self.form.gravityFrame.setEnabled( + self.form.radioButtonFreeSurface.isChecked() or + (not self.form.checkBoxIsothermal.isChecked() and not self.form.checkBoxHighMach.isChecked())) + + # SRF model + srf_capable = (self.form.radioButtonSteady.isChecked() and self.form.checkBoxIsothermal.isChecked()) + srf_should_be_unchecked = ((not self.form.checkBoxIsothermal.isChecked()) + or self.form.radioButtonTransient.isChecked() + or self.form.radioButtonFreeSurface.isChecked()) + self.form.srfCheckBox.setEnabled(srf_capable) + if srf_should_be_unchecked: + self.form.srfCheckBox.setChecked(False) + self.form.srfFrame.setEnabled(self.form.srfCheckBox.isChecked()) + + # Multiphase - Free surface + if self.form.radioButtonFreeSurface.isChecked(): + self.form.checkBoxIsothermal.setChecked(True) + self.form.checkBoxIsothermal.setEnabled(False) + else: + self.form.checkBoxIsothermal.setEnabled(True) + + # Viscous + if self.form.viscousCheckBox.isChecked(): + self.form.turbulenceFrame.setVisible(True) + # RANS + if self.form.radioButtonRANS.isChecked(): + self.form.turbulenceComboBox.clear() + self.form.turbulenceComboBox.addItems(RANS_MODELS) + ti = CfdTools.indexOrDefault(RANS_MODELS, self.obj.TurbulenceModel, 0) + self.form.turbulenceComboBox.setCurrentIndex(ti) + self.form.turbulenceModelFrame.setVisible(True) + #DES + elif self.form.radioButtonDES.isChecked(): + self.form.turbulenceComboBox.clear() + self.form.turbulenceComboBox.addItems(DES_MODELS) + ti = CfdTools.indexOrDefault(DES_MODELS, self.obj.TurbulenceModel, 0) + self.form.turbulenceComboBox.setCurrentIndex(ti) + self.form.turbulenceModelFrame.setVisible(True) + # LES + elif self.form.radioButtonLES.isChecked(): + self.form.turbulenceComboBox.clear() + self.form.turbulenceComboBox.addItems(LES_MODELS) + ti = CfdTools.indexOrDefault(LES_MODELS, self.obj.TurbulenceModel, 0) + self.form.turbulenceComboBox.setCurrentIndex(ti) + self.form.turbulenceModelFrame.setVisible(True) + else: + self.form.turbulenceModelFrame.setVisible(False) + self.form.turbulenceComboBox.clear() + else: + self.form.turbulenceFrame.setVisible(False) + self.form.turbulenceModelFrame.setVisible(False) + + def accept(self): + doc = FreeCADGui.getDocument(self.obj.Document) + doc.resetEdit() + + if self.form.radioButtonSteady.isChecked(): + storeIfChanged(self.obj, 'Time', 'Steady') + elif self.form.radioButtonTransient.isChecked(): + storeIfChanged(self.obj, 'Time', 'Transient') + + if self.form.radioButtonSinglePhase.isChecked(): + storeIfChanged(self.obj, 'Phase', 'Single') + elif self.form.radioButtonFreeSurface.isChecked(): + storeIfChanged(self.obj, 'Phase', 'FreeSurface') + elif self.form.radioButtonEulerian.isChecked(): + storeIfChanged(self.obj, 'Phase', 'Eulerian') + if self.form.checkBoxIsothermal.isChecked(): + storeIfChanged(self.obj, 'Flow', 'Isothermal') + elif not self.form.checkBoxIsothermal.isChecked(): + if self.form.checkBoxHighMach.isChecked(): + storeIfChanged(self.obj, 'Flow', 'HighMachCompressible') + else: + storeIfChanged(self.obj, 'Flow', 'NonIsothermal') + + if self.form.viscousCheckBox.isChecked(): + if self.form.radioButtonLaminar.isChecked(): + storeIfChanged(self.obj, 'Turbulence', 'Laminar') + else: + if self.form.radioButtonRANS.isChecked(): + storeIfChanged(self.obj, 'Turbulence', 'RANS') + elif self.form.radioButtonDES.isChecked(): + storeIfChanged(self.obj, 'Turbulence', 'DES') + elif self.form.radioButtonLES.isChecked(): + storeIfChanged(self.obj, 'Turbulence', 'LES') + storeIfChanged(self.obj, 'TurbulenceModel', self.form.turbulenceComboBox.currentText()) + else: + storeIfChanged(self.obj, 'Turbulence', 'Inviscid') + + storeIfChanged(self.obj, 'gx', getQuantity(self.form.gx)) + storeIfChanged(self.obj, 'gy', getQuantity(self.form.gy)) + storeIfChanged(self.obj, 'gz', getQuantity(self.form.gz)) + + if self.form.srfCheckBox.isChecked(): + storeIfChanged(self.obj, 'SRFModelEnabled', self.form.srfCheckBox.isChecked()) + storeIfChanged(self.obj, 'SRFModelRPM', self.form.inputSRFRPM.text()) + centre_of_rotation = FreeCAD.Vector( + self.form.inputSRFCoRx.property("quantity").Value, + self.form.inputSRFCoRy.property("quantity").Value, + self.form.inputSRFCoRz.property("quantity").Value) + storeIfChanged(self.obj, 'SRFModelCoR', centre_of_rotation) + model_axis = FreeCAD.Vector( + self.form.inputSRFAxisx.property("quantity").Value, + self.form.inputSRFAxisy.property("quantity").Value, + self.form.inputSRFAxisz.property("quantity").Value) + storeIfChanged(self.obj, 'SRFModelAxis', model_axis) + + if self.form.checkBoxReacting.isChecked(): + storeIfChanged(self.obj, 'ReactionModelsEnabled', self.form.checkBoxReacting.isChecked()) + + def reject(self): + doc = FreeCADGui.getDocument(self.obj.Document) + doc.resetEdit() + + def closing(self): + # We call this from unsetEdit to allow cleanup + return diff --git a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py index 88af5e50..432d9505 100644 --- a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py +++ b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py @@ -4,7 +4,7 @@ # * Copyright (c) 2017-2018 Oliver Oxtoby (CSIR) * # * Copyright (c) 2017-2018 Alfred Bogaers (CSIR) * # * Copyright (c) 2019-2022 Oliver Oxtoby * -# * Copyright (c) 2022 Jonathan Bergh * +# * Copyright (c) 2022-2024 Jonathan Bergh * # * * # * This program is free software: you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License as * @@ -71,11 +71,16 @@ def load(self): self.form.radioButtonSinglePhase.toggle() elif self.obj.Phase == 'FreeSurface': self.form.radioButtonFreeSurface.toggle() + elif self.obj.Phase == 'Eulerian': + self.form.radioButtonEulerian.toggle() # Flow self.form.checkBoxIsothermal.setChecked(self.obj.Flow == 'Isothermal') self.form.checkBoxHighMach.setChecked(self.obj.Flow == 'HighMachCompressible') + # Reactions + self.form.checkBoxReacting.setChecked(self.obj.ReactionModelsEnabled) + # Turbulence if self.obj.Turbulence == 'Inviscid': self.form.viscousCheckBox.setChecked(False) @@ -121,14 +126,16 @@ def updateUI(self): # Steady / transient if self.form.radioButtonSteady.isChecked(): self.form.radioButtonFreeSurface.setEnabled(False) + self.form.radioButtonEulerian.setEnabled(False) if self.form.radioButtonDES.isChecked() or self.form.radioButtonLES.isChecked(): self.form.radioButtonRANS.toggle() self.form.radioButtonDES.setEnabled(False) self.form.radioButtonLES.setEnabled(False) - if self.form.radioButtonFreeSurface.isChecked(): + if self.form.radioButtonFreeSurface.isChecked() or self.form.radioButtonEulerian.isChecked(): self.form.radioButtonSinglePhase.toggle() else: self.form.radioButtonFreeSurface.setEnabled(True) + self.form.radioButtonEulerian.setEnabled(True) self.form.radioButtonDES.setEnabled(True) self.form.radioButtonLES.setEnabled(True) @@ -139,7 +146,7 @@ def updateUI(self): # SRF model srf_capable = (self.form.radioButtonSteady.isChecked() and self.form.checkBoxIsothermal.isChecked()) - srf_should_be_unchecked = ((not self.form.checkBoxIsothermal.isChecked()) + srf_should_be_unchecked = ((not self.form.checkBoxIsothermal.isChecked()) or self.form.radioButtonTransient.isChecked() or self.form.radioButtonFreeSurface.isChecked()) self.form.srfCheckBox.setEnabled(srf_capable) @@ -147,19 +154,32 @@ def updateUI(self): self.form.srfCheckBox.setChecked(False) self.form.srfFrame.setEnabled(self.form.srfCheckBox.isChecked()) - # Free surface + # Multiphase - Free surface if self.form.radioButtonFreeSurface.isChecked(): self.form.checkBoxIsothermal.setChecked(True) self.form.checkBoxIsothermal.setEnabled(False) else: self.form.checkBoxIsothermal.setEnabled(True) + # Multiphase - Eulerian + if self.form.radioButtonEulerian.isChecked(): + pass + # High Mach capability self.form.checkBoxHighMach.setEnabled(not self.form.checkBoxIsothermal.isChecked()) if self.form.checkBoxIsothermal.isChecked(): self.form.checkBoxHighMach.setChecked(False) - # Viscous + # Reaction model + #reacting_capable = (self.form.radioButtonEulerian.isChecked()) + #reacting_should_be_unchecked = (not self.form.radioButtonEulerian.isChecked()) + #if reacting_should_be_unchecked: + # self.form.checkBoxReacting.setChecked(False) + #self.form.checkBoxReacting.setEnabled(reacting_capable) + self.form.checkBoxReacting.setChecked(False) + self.form.checkBoxReacting.setVisible(False) + + # Viscous if self.form.viscousCheckBox.isChecked(): self.form.turbulenceFrame.setVisible(True) # RANS @@ -203,7 +223,8 @@ def accept(self): storeIfChanged(self.obj, 'Phase', 'Single') elif self.form.radioButtonFreeSurface.isChecked(): storeIfChanged(self.obj, 'Phase', 'FreeSurface') - + elif self.form.radioButtonEulerian.isChecked(): + storeIfChanged(self.obj, 'Phase', 'Eulerian') if self.form.checkBoxIsothermal.isChecked(): storeIfChanged(self.obj, 'Flow', 'Isothermal') elif not self.form.checkBoxIsothermal.isChecked(): @@ -244,10 +265,13 @@ def accept(self): self.form.inputSRFAxisz.property("quantity").Value) storeIfChanged(self.obj, 'SRFModelAxis', model_axis) + if self.form.checkBoxReacting.isChecked(): + storeIfChanged(self.obj, 'ReactionModelsEnabled', self.form.checkBoxReacting.isChecked()) + def reject(self): doc = FreeCADGui.getDocument(self.obj.Document) doc.resetEdit() def closing(self): # We call this from unsetEdit to allow cleanup - return \ No newline at end of file + return diff --git a/Data/CfdFluidMaterialProperties/AirCompressible.FCMat b/Data/CfdFluidMaterialProperties/AirCompressible.FCMat index 6f8c2331..83a6adf6 100644 --- a/Data/CfdFluidMaterialProperties/AirCompressible.FCMat +++ b/Data/CfdFluidMaterialProperties/AirCompressible.FCMat @@ -11,3 +11,5 @@ Cp = 1004.703 J/kg/K SutherlandTemperature = 110.4 K SutherlandRefViscosity = 1.716e-5 kg/m/s SutherlandRefTemperature = 273.15 K +BubbleDiameter = 1e-5 m +SurfaceTensionCoefficient = 0.07 N/m diff --git a/Data/CfdFluidMaterialProperties/AirIsothermal.FCMat b/Data/CfdFluidMaterialProperties/AirIsothermal.FCMat index 4f94f48c..9476621e 100644 --- a/Data/CfdFluidMaterialProperties/AirIsothermal.FCMat +++ b/Data/CfdFluidMaterialProperties/AirIsothermal.FCMat @@ -8,3 +8,5 @@ Type = Isothermal Description = Standard air properties at 20 Degrees Celsius and 1 atm Density = 1.20 kg/m^3 DynamicViscosity = 1.80e-5 kg/m/s +BubbleDiameter = 1e-5 m +SurfaceTensionCoefficient = 0.07 N/m diff --git a/Data/CfdFluidMaterialProperties/Gasoline.FCMat b/Data/CfdFluidMaterialProperties/Gasoline.FCMat index 4e37b5c4..ecb00c8a 100644 --- a/Data/CfdFluidMaterialProperties/Gasoline.FCMat +++ b/Data/CfdFluidMaterialProperties/Gasoline.FCMat @@ -8,3 +8,5 @@ Type = Isothermal Description = Gasoline at 20 Degrees Celsius and 1 atm Density = 680 kg/m^3 DynamicViscosity = 2.92e-4 kg/m/s +BubbleDiameter = 1e-5 m +SurfaceTensionCoefficient = 0.07 N/m diff --git a/Data/CfdFluidMaterialProperties/OilSAE10W.FCMat b/Data/CfdFluidMaterialProperties/OilSAE10W.FCMat index 0aa3de26..4897024b 100644 --- a/Data/CfdFluidMaterialProperties/OilSAE10W.FCMat +++ b/Data/CfdFluidMaterialProperties/OilSAE10W.FCMat @@ -8,3 +8,5 @@ Type = Isothermal Description = SAE 10W oil at 20 Degrees Celsius and 1 atm Density = 870 kg/m^3 DynamicViscosity = 1.04e-1 kg/m/s +BubbleDiameter = 1e-5 m +SurfaceTensionCoefficient = 0.07 N/m diff --git a/Data/CfdFluidMaterialProperties/Readme.txt b/Data/CfdFluidMaterialProperties/Readme.txt index 9ce9567c..60ae5644 100644 --- a/Data/CfdFluidMaterialProperties/Readme.txt +++ b/Data/CfdFluidMaterialProperties/Readme.txt @@ -10,7 +10,7 @@ reference. Each material has a Type keyword identifying the kind of modelling it is compatible with. Only those compatible with the physics model -selected will we shown in the GUI: +selected will be shown in the GUI: - Isothermal: Denotes properties at fixed temperature and pressure, for use in incompressible, isothermal solvers - Incompressible: Properties modelled as dependent on temperature but diff --git a/Data/CfdFluidMaterialProperties/SeaWater.FCMat b/Data/CfdFluidMaterialProperties/SeaWater.FCMat index a42384d6..4bccdfb3 100644 --- a/Data/CfdFluidMaterialProperties/SeaWater.FCMat +++ b/Data/CfdFluidMaterialProperties/SeaWater.FCMat @@ -8,3 +8,5 @@ Type = Isothermal Description = Sea water (30%) at 20 Degrees Celsius and 1 atm Density = 1025 kg/m^3 DynamicViscosity = 1.07e-3 kg/m/s +BubbleDiameter = 1e-5 m +SurfaceTensionCoefficient = 0.07 N/m diff --git a/Data/CfdFluidMaterialProperties/WaterIncompressible.FCMat b/Data/CfdFluidMaterialProperties/WaterIncompressible.FCMat index 8b5e83db..9a15d45e 100644 --- a/Data/CfdFluidMaterialProperties/WaterIncompressible.FCMat +++ b/Data/CfdFluidMaterialProperties/WaterIncompressible.FCMat @@ -11,3 +11,5 @@ DensityPolynomial = 746.025 1.93017 -0.00365471 CpPolynomial = 9850.69 -48.6714 0.13736 -0.000127063 DynamicViscosityPolynomial = 0.116947 -0.00100532 2.90283e-6 -2.80572e-9 ThermalConductivityPolynomial = -0.710696 0.0071857 -9.29827e-6 +BubbleDiameter = 1e-5 m +SurfaceTensionCoefficient = 0.07 N/m diff --git a/Data/CfdFluidMaterialProperties/WaterIsothermal.FCMat b/Data/CfdFluidMaterialProperties/WaterIsothermal.FCMat index f209653b..b6d824aa 100644 --- a/Data/CfdFluidMaterialProperties/WaterIsothermal.FCMat +++ b/Data/CfdFluidMaterialProperties/WaterIsothermal.FCMat @@ -8,3 +8,5 @@ Type = Isothermal Description = Standard distilled water properties at 20 Degrees Celsius and 1 atm Density = 998 kg/m^3 DynamicViscosity = 1.003e-3 kg/m/s +BubbleDiameter = 1e-5 m +SurfaceTensionCoefficient = 0.07 N/m diff --git a/Data/Templates/case/0/T.fluid b/Data/Templates/case/0/T.fluid new file mode 100644 index 00000000..9b5582ab --- /dev/null +++ b/Data/Templates/case/0/T.fluid @@ -0,0 +1,176 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam +%{%(initialValues/VolumeFractions%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + object T.%(0%); +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +internalField uniform %(initialValues/Temperature%); + +boundaryField +{ + +%{%(boundaries%) +%{%(boundaries/%(0%)/BoundaryType%) +%:wall +%{%(boundaries/%(0%)/ThermalBoundaryType%) +%:fixedValue + %(0%) + { +%{%(solver/SolverName%) +%:hisa + type boundaryCorrectedFixedValue; +%:default + type fixedValue; +%} + value uniform %(boundaries/%(0%)/Temperature%); + } +%:zeroGradient + %(0%) + { +%{%(solver/SolverName%) +%:hisa + type characteristicWallTemperature; +%:default + type zeroGradient; +%} + value $internalField; + } +%:fixedGradient + %(0%) + { + type fixedGradient; + gradient uniform %(boundaries/%(0%)/HeatFlux%); + } +%:heatTransferCoeff + %(0%) + { + type externalWallHeatFluxTemperature; + mode coefficient; + Ta constant %(boundaries/%(0%)/Temperature%); + h uniform %(boundaries/%(0%)/HeatTransferCoeff%); + kappaMethod fluidThermo; + value $internalField; + } +%} +%:inlet +%{%(solver/SolverName%) +%:hisa + %(0%) + { +%{%(boundaries/%(0%)/BoundarySubType%) +%:uniformVelocityInlet + type characteristicVelocityInletOutletTemperature; + U ( %(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%) ); + p %(boundaries/%(0%)/Pressure%); + T %(boundaries/%(0%)/Temperature%); + value uniform %(boundaries/%(0%)/Temperature%); +%:staticPressureInlet totalPressureInlet + type characteristicPressureInletOutletTemperature; + U ( %(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%) ); + p %(boundaries/%(0%)/Pressure%); + T %(boundaries/%(0%)/Temperature%); + value uniform %(boundaries/%(0%)/Temperature%); +%:default + type inletOutlet; + inletValue uniform %(boundaries/%(0%)/Temperature%); + value uniform %(boundaries/%(0%)/Temperature%); +%} + } +%:default + %(0%) + { + type inletOutlet; + inletValue uniform %(boundaries/%(0%)/Temperature%); + value uniform %(boundaries/%(0%)/Temperature%); + } +%} +%:outlet +%{%(solver/SolverName%) +%:hisa +%{%(boundaries/%(0%)/BoundarySubType%) +%:staticPressureOutlet + %(0%) + { + type characteristicPressureInletOutletTemperature; + U ( %(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%) ); + p %(boundaries/%(0%)/Pressure%); + T %(boundaries/%(0%)/Temperature%); + value $internalField; + } +%:uniformVelocityOutlet + %(0%) + { + type characteristicVelocityInletOutletTemperature; + U ( %(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%) ); + p %(boundaries/%(0%)/Pressure%); + T %(boundaries/%(0%)/Temperature%); + value $internalField; + } +%:default + %(0%) + { + type inletOutlet; + inletValue uniform %(boundaries/%(0%)/Temperature%); + value uniform %(boundaries/%(0%)/Temperature%); + } +%} +%:default + %(0%) + { + type inletOutlet; + inletValue uniform %(boundaries/%(0%)/Temperature%); + value uniform %(boundaries/%(0%)/Temperature%); + } +%} +%:open +%{%(boundaries/%(0%)/BoundarySubType%) +%:farField + %(0%) + { + type characteristicFarfieldTemperature; + U ( %(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%) ); + p %(boundaries/%(0%)/Pressure%); + T %(boundaries/%(0%)/Temperature%); + value $internalField; + } +%:default + %(0%) + { +%{%(solver/SolverName%) +%:hisa + type characteristicPressureInletOutletTemperature; + U ( %(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%) ); + p %(boundaries/%(0%)/Pressure%); + T %(boundaries/%(0%)/Temperature%); +%:default + type inletOutlet; + inletValue uniform %(boundaries/%(0%)/Temperature%); + value $internalField; +%} + } +%} +%:constraint +%[0/_boundary_constraint%] +%:baffle +%{%(boundaries/%(0%)/BoundarySubType%) +%:porousBaffle +%[0/_boundary_cyclic_baffle%] +%} +%} + +%} +%[0/_boundary_redistributeHelper%] +} + +// ************************************************************************* // +%} 0/T.%(0%) +%} diff --git a/Data/Templates/case/0/U b/Data/Templates/case/0/U index d1da8e62..de286d7a 100644 --- a/Data/Templates/case/0/U +++ b/Data/Templates/case/0/U @@ -1,5 +1,5 @@ %{%(solver/SolverName%) -%:SRFSimpleFoam +%:SRFSimpleFoam multiphaseEulerFoam %:default %[_header%] FoamFile @@ -216,4 +216,4 @@ boundaryField } // ************************************************************************* // -%} \ No newline at end of file +%} diff --git a/Data/Templates/case/0/U.fluid b/Data/Templates/case/0/U.fluid new file mode 100644 index 00000000..cf4cf4eb --- /dev/null +++ b/Data/Templates/case/0/U.fluid @@ -0,0 +1,220 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam +%{%(initialValues/VolumeFractions%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volVectorField; + object U.%(0%); +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -1 0 0 0 0]; + +internalField uniform (%(initialValues/Ux%) %(initialValues/Uy%) %(initialValues/Uz%)); + +boundaryField +{ + +%{%(boundaries%) +%{%(boundaries/%(0%)/BoundaryType%) +%:empty +%[0/_boundary_empty%] +%:wall +%{%(boundaries/%(0%)/BoundarySubType%) +%:fixedWall roughWall + %(0%) + { +%{%(solver/SolverName%) +%:hisa + type boundaryCorrectedFixedValue; + value uniform (0 0 0); +%:default + // movingWallVelocity reduces to fixedValue if the mesh is not moving + type movingWallVelocity; + value uniform (0 0 0); +%} + } +%:slipWall + %(0%) + { + type slip; + value $internalField; + } +%:translatingWall + %(0%) + { + // Specified velocity, only component tangential to wall is used + type translatingWallVelocity; + U (%(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%)); + value uniform (0 0 0); + } +%:partialSlipWall + %(0%) + { + type partialSlip; + valueFraction %(boundaries/%(0%)/SlipRatio%); + value uniform (0 0 0); + } +%} +%:inlet +%{%(boundaries/%(0%)/BoundarySubType%) +%:uniformVelocityInlet + %(0%) + { +%{%(solver/SolverName%) +%:hisa + type characteristicVelocityInletOutletVelocity; + U ( %(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%) ); + p %(boundaries/%(0%)/Pressure%); + T %(boundaries/%(0%)/Temperature%); +%:default + // Fix all three components of velocity on inflow and only the normal component on outflow, + // in order to be well-posed if there are some faces on the patch which are actually outflows. + type fixedNormalInletOutletVelocity; + fixTangentialInflow yes; + normalVelocity + { + type fixedValue; + value uniform ( %(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%) ); + } +%} + value uniform ( %(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%) ); + } +%:volumetricFlowRateInlet + %(0%) + { + type flowRateInletVelocity; + volumetricFlowRate %(boundaries/%(0%)/VolFlowRate%); + value $internalField; + } +%:massFlowRateInlet + %(0%) + { + type flowRateInletVelocity; + massFlowRate %(boundaries/%(0%)/MassFlowRate%); + rho rho; +%{%(solver/SolverName%) +%:simpleFoam porousSimpleFoam pimpleFoam + rhoInlet %(fluidProperties/0/Density%); +%} + value $internalField; + } +%:totalPressureInlet +%{%(solver/SolverName%) +%:hisa + %(0%) + { + type characteristicPressureInletOutletVelocity; + U (%(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%)); + p %(boundaries/%(0%)/Pressure%); + T %(boundaries/%(0%)/Temperature%); + value $internalField; + } +%:default +%[0/_U_boundary_pressureInletOutletVelocity%] +%} +%:staticPressureInlet +%{%(solver/SolverName%) +%:hisa + %(0%) + { + type characteristicPressureInletOutletVelocity; + U (%(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%)); + p %(boundaries/%(0%)/Pressure%); + T %(boundaries/%(0%)/Temperature%); + value $internalField; + } +%:default +%[0/_U_boundary_pressureInletOutletVelocity%] +%} +%} +%:outlet +%{%(boundaries/%(0%)/BoundarySubType%) +%:staticPressureOutlet +%{%(solver/SolverName%) +%:hisa + %(0%) + { + type characteristicPressureInletOutletVelocity; + U ( %(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%) ); + p %(boundaries/%(0%)/Pressure%); + T %(boundaries/%(0%)/Temperature%); + value $internalField; + } +%:default +%[0/_U_boundary_pressureInletOutletVelocity%] +%} +%:uniformVelocityOutlet + %(0%) + { +%{%(solver/SolverName%) +%:hisa + type characteristicVelocityInletOutletVelocity; + U ( %(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%) ); + p %(boundaries/%(0%)/Pressure%); + T %(boundaries/%(0%)/Temperature%); +%:default + // Fix only the normal component on outflow and all three components of velocity on inflow, + // in order to be well-posed on outflow and also in case there are any faces with inflowing velocity. + type fixedNormalInletOutletVelocity; + fixTangentialInflow yes; + normalVelocity + { + type fixedValue; + value uniform (%(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%)); + } +%} + value uniform (%(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%)); + } +%:outFlowOutlet + %(0%) + { + type inletOutlet; + inletValue uniform (0 0 0); + //TODO: We need to write an out-flowing value here so that adjustPhi can have an adjustable flux to work + //TODO: with at iteration 1 + value $internalField; + } +%} +%:open +%{%(boundaries/%(0%)/BoundarySubType%) +%:farField + %(0%) + { + type characteristicFarfieldVelocity; + U ( %(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%) ); + p %(boundaries/%(0%)/Pressure%); + T %(boundaries/%(0%)/Temperature%); + value $internalField; + } +%:default +%{%(solver/SolverName%) +%:hisa + %(0%) + { + type characteristicPressureInletOutletVelocity; + U ( %(boundaries/%(0%)/Ux%) %(boundaries/%(0%)/Uy%) %(boundaries/%(0%)/Uz%) ); + p %(boundaries/%(0%)/Pressure%); + T %(boundaries/%(0%)/Temperature%); + value $internalField; + } +%:default +%[0/_U_boundary_pressureInletOutletVelocity%] +%} +%} +%:constraint +%[0/_boundary_constraint%] +%:baffle +%[0/_boundary_cyclic_baffle%] +%} + +%} +%[0/_boundary_redistributeHelper%] +} + +// ************************************************************************* // +%} 0/U.%(0%) +%} diff --git a/Data/Templates/case/0/alpha.fluid b/Data/Templates/case/0/alpha.fluid index 7f114e8b..8707ed38 100644 --- a/Data/Templates/case/0/alpha.fluid +++ b/Data/Templates/case/0/alpha.fluid @@ -1,5 +1,5 @@ %{%(solver/SolverName%) -%:interFoam multiphaseInterFoam +%:interFoam multiphaseInterFoam multiphaseEulerFoam %{%(initialValues/VolumeFractions%) %[_header%] FoamFile @@ -41,4 +41,4 @@ boundaryField // ************************************************************************* // %} 0/alpha.%(0%) -%} \ No newline at end of file +%} diff --git a/Data/Templates/case/0/alphat.fluid b/Data/Templates/case/0/alphat.fluid new file mode 100644 index 00000000..20b69c51 --- /dev/null +++ b/Data/Templates/case/0/alphat.fluid @@ -0,0 +1,57 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam +%{%(physics/Turbulence%) +%:RANS DES LES +%{%(initialValues/VolumeFractions%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + object alphat.%(0%); +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -1 0 0 0 0]; + +internalField uniform 0; + +boundaryField +{ + +%{%(boundaries%) +%{%(boundaries/%(0%)/BoundaryType%) +%:wall +%{%(boundaries/%(0%)/BoundarySubType%) +%:fixedWall translatingWall partialSlipWall + %(0%) + { + type compressible::alphatWallFunction; + value uniform 0; + } +%:slipWall +%[0/_boundary_zeroGradient%] +%:roughWall + %(0%) + { + type compressible::alphatWallFunction; + value uniform 0; + } +%} +%:constraint +%[0/_boundary_constraint%] +%:baffle +%[0/_boundary_cyclic_baffle%] +%:default +%[0/_boundary_calculated%] +%} + +%} +%[0/_boundary_redistributeHelper%] +} + +// ************************************************************************* // +%} 0/alphat.%(0%) +%} +%} diff --git a/Data/Templates/case/0/k b/Data/Templates/case/0/k index ca0b21e8..32075040 100644 --- a/Data/Templates/case/0/k +++ b/Data/Templates/case/0/k @@ -1,3 +1,6 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam +%:default %{%(physics/Turbulence%) %:RANS DES LES %{%(physics/TurbulenceModel%) @@ -82,4 +85,5 @@ boundaryField // ************************************************************************* // %} -%} \ No newline at end of file +%} +%} diff --git a/Data/Templates/case/0/k.fluid b/Data/Templates/case/0/k.fluid new file mode 100644 index 00000000..364fcd50 --- /dev/null +++ b/Data/Templates/case/0/k.fluid @@ -0,0 +1,90 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam +%{%(physics/Turbulence%) +%:RANS DES LES +%{%(physics/TurbulenceModel%) +%:kOmegaSST kOmegaSSTDES kOmegaSSTDDES kOmegaSSTIDDES kEpsilon kOmegaSSTLM kEqn +%{%(initialValues/VolumeFractions%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + object k.%(0%); +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -2 0 0 0 0]; + +internalField uniform %(initialValues/k%); + +boundaryField +{ + +%{%(boundaries%) +%{%(boundaries/%(0%)/BoundaryType%) +%:empty +%[0/_boundary_empty%] +%:wall +%{%(boundaries/%(0%)/BoundarySubType%) +%:fixedWall translatingWall partialSlipWall roughWall + %(0%) + { + type kqRWallFunction; + value $internalField; + } +%:slipWall +%[0/_boundary_zeroGradient%] +%} +%:inlet open +%{%(boundaries/%(0%)/TurbulenceInletSpecification%) +%:TKEAndSpecDissipationRate + %(0%) + { + type inletOutlet; + inletValue uniform %(boundaries/%(0%)/TurbulentKineticEnergy%); + value $internalField; + } +%:intensityAndLengthScale + %(0%) + { + type turbulentIntensityKineticEnergyInlet; + intensity %(boundaries/%(0%)/TurbulenceIntensity%); + value $internalField; + } +%:TurbulentViscosityAndK + %(0%) + { + type fixedValue; + value uniform %(boundaries/%(0%)/kEqnTurbulentKineticEnergy%); + } +%:default + %(0%) + { + type fixedValue; + value $internalField; + } +%} +%:outlet + %(0%) + { + type inletOutlet; + inletValue $internalField; + value $internalField; + } +%:constraint +%[0/_boundary_constraint%] +%:baffle +%[0/_boundary_cyclic_baffle%] +%} + +%} +%[0/_boundary_redistributeHelper%] +} + +// ************************************************************************* // +%} 0/k.%(0%) +%} +%} +%} diff --git a/Data/Templates/case/0/nut b/Data/Templates/case/0/nut index 35e7f974..f083987c 100644 --- a/Data/Templates/case/0/nut +++ b/Data/Templates/case/0/nut @@ -1,3 +1,6 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam +%:default %{%(physics/Turbulence%) %:RANS DES LES %[_header%] @@ -89,4 +92,5 @@ boundaryField } // ************************************************************************* // -%} \ No newline at end of file +%} +%} diff --git a/Data/Templates/case/0/nut.fluid b/Data/Templates/case/0/nut.fluid new file mode 100644 index 00000000..6491f646 --- /dev/null +++ b/Data/Templates/case/0/nut.fluid @@ -0,0 +1,97 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam +%{%(physics/Turbulence%) +%:RANS DES LES +%{%(initialValues/VolumeFractions%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + object nut.%(0%); +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -1 0 0 0 0]; + +%{%(physics/TurbulenceModel%) +%:kEqn Smagorinsky WALE +internalField uniform %(initialValues/nut%); +%:default +internalField uniform 0; +%} + +boundaryField +{ + +%{%(boundaries%) +%{%(boundaries/%(0%)/BoundaryType%) +%:inlet +%{%(physics/TurbulenceModel%) +%:kEqn + %(0%) + { + type fixedValue; + value uniform %(boundaries/%(0%)/kEqnTurbulentViscosity%); + } +%:Smagorinsky WALE + %(0%) + { + type fixedValue; + value uniform %(boundaries/%(0%)/TurbulentViscosity%); + } +%:default + %(0%) + { + type calculated; + value $internalField; + } +%} +%:empty +%[0/_boundary_empty%] +%:wall +%{%(boundaries/%(0%)/BoundarySubType%) +%:fixedWall translatingWall partialSlipWall + %(0%) + { +%{%(physics/TurbulenceModel%) +%:SpalartAllmaras SpalartAllmarasDES SpalartAllmarasDDES SpalartAllmarasIDDES Smagorinsky + type nutUSpaldingWallFunction; +%:default + type nutkWallFunction; +%} + value uniform 0; + } +%:slipWall +%[0/_boundary_zeroGradient%] +%:roughWall + %(0%) + { +%{%(physics/TurbulenceModel%) +%:SpalartAllmaras SpalartAllmarasDES SpalartAllmarasDDES SpalartAllmarasIDDES Smagorinsky + type nutURoughWallFunction; +%:default + type nutkRoughWallFunction; +%} + Ks uniform %(boundaries/%(0%)/RoughnessHeight%); + Cs uniform %(boundaries/%(0%)/RoughnessConstant%); + value uniform 0; + } +%} +%:constraint +%[0/_boundary_constraint%] +%:baffle +%[0/_boundary_cyclic_baffle%] +%:default +%[0/_boundary_calculated%] +%} + +%} +%[0/_boundary_redistributeHelper%] +} + +// ************************************************************************* // +%} 0/nut.%(0%) +%} +%} diff --git a/Data/Templates/case/0/omega b/Data/Templates/case/0/omega index 217895e0..24968a08 100644 --- a/Data/Templates/case/0/omega +++ b/Data/Templates/case/0/omega @@ -1,3 +1,6 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam +%:default %{%(physics/Turbulence%) %:RANS DES %{%(physics/TurbulenceModel%) @@ -76,4 +79,5 @@ boundaryField // ************************************************************************* // %} -%} \ No newline at end of file +%} +%} diff --git a/Data/Templates/case/0/omega.fluid b/Data/Templates/case/0/omega.fluid new file mode 100644 index 00000000..aaefc1f6 --- /dev/null +++ b/Data/Templates/case/0/omega.fluid @@ -0,0 +1,84 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam +%{%(physics/Turbulence%) +%:RANS DES +%{%(physics/TurbulenceModel%) +%:kOmegaSST kOmegaSSTDES kOmegaSSTDDES kOmegaSSTIDDES kOmegaSSTLM +%{%(initialValues/VolumeFractions%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + object omega.%(0%); +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 -1 0 0 0 0]; + +internalField uniform %(initialValues/omega%); + +boundaryField +{ + +%{%(boundaries%) +%{%(boundaries/%(0%)/BoundaryType%) +%:empty +%[0/_boundary_empty%] +%:wall +%{%(boundaries/%(0%)/BoundarySubType%) +%:fixedWall translatingWall partialSlipWall roughWall + %(0%) + { + type omegaWallFunction; + value $internalField; + } +%:slipWall +%[0/_boundary_zeroGradient%] +%} +%:inlet open +%{%(boundaries/%(0%)/TurbulenceInletSpecification%) +%:TKEAndSpecDissipationRate + %(0%) + { + type inletOutlet; + inletValue uniform %(boundaries/%(0%)/SpecificDissipationRate%); + value $internalField; + } +%:intensityAndLengthScale + %(0%) + { + type turbulentMixingLengthFrequencyInlet; + mixingLength %(boundaries/%(0%)/TurbulenceLengthScale%); + value $internalField; + } +%:default + %(0%) + { + type fixedValue; + value $internalField; + } +%} +%:outlet + %(0%) + { + type inletOutlet; + inletValue $internalField; + value $internalField; + } +%:constraint +%[0/_boundary_constraint%] +%:baffle +%[0/_boundary_cyclic_baffle%] +%} + +%} +%[0/_boundary_redistributeHelper%] +} + +// ************************************************************************* // +%} 0/omega.%(0%) +%} +%} +%} diff --git a/Data/Templates/case/0/p b/Data/Templates/case/0/p index 98f3da2a..c42ac43c 100644 --- a/Data/Templates/case/0/p +++ b/Data/Templates/case/0/p @@ -12,7 +12,7 @@ FoamFile // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // %{%(solver/SolverName%) -%:simpleFoam porousSimpleFoam pimpleFoam SRFSimpleFoam +%:simpleFoam porousSimpleFoam pimpleFoam SRFSimpleFoam multiphaseEulerFoam dimensions [0 2 -2 0 0 0 0]; internalField uniform %(initialValues/KinematicPressure%); @@ -190,4 +190,4 @@ boundaryField } // ************************************************************************* // -%} \ No newline at end of file +%} diff --git a/Data/Templates/case/0/p_rgh b/Data/Templates/case/0/p_rgh index 321a2b03..b167b321 100644 --- a/Data/Templates/case/0/p_rgh +++ b/Data/Templates/case/0/p_rgh @@ -1,5 +1,5 @@ %{%(solver/SolverName%) -%:buoyantSimpleFoam buoyantPimpleFoam interFoam multiphaseInterFoam +%:buoyantSimpleFoam buoyantPimpleFoam interFoam multiphaseInterFoam multiphaseEulerFoam %[_header%] FoamFile { diff --git a/Data/Templates/case/Allrun b/Data/Templates/case/Allrun index fd5e396b..a9ac6cf8 100644 --- a/Data/Templates/case/Allrun +++ b/Data/Templates/case/Allrun @@ -117,6 +117,7 @@ runCommand createBaffles -overwrite runCommand changeDictionary %} + %{%(initialValues/PotentialFlow%) %:True %{%(solver/SolverName%) diff --git a/Data/Templates/case/constant/_reactions.gas b/Data/Templates/case/constant/_reactions.gas new file mode 100644 index 00000000..6d386d90 --- /dev/null +++ b/Data/Templates/case/constant/_reactions.gas @@ -0,0 +1,13 @@ +reactions +{ + waterGasShift + { + type reversibleArrhenius; + + reaction "CO^0.93 + H2O^0.24 = CO2^0.69 + H2^1"; + + A 1e8; + beta 0.86; + Ta 1e4; + } +} diff --git a/Data/Templates/case/constant/chemistryProperties.fluid b/Data/Templates/case/constant/chemistryProperties.fluid new file mode 100644 index 00000000..291e1f97 --- /dev/null +++ b/Data/Templates/case/constant/chemistryProperties.fluid @@ -0,0 +1,39 @@ +%{%(reactionsEnabled%) +%:True +%{%(initialValues/VolumeFractions%) +%[_header%] +FoamFile +{ + format ascii; + class dictionary; + location "constant"; + object chemistryProperties.%(0%); +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +chemistryType +{ + solver EulerImplicit; +} + +chemistry on; + +initialChemicalTimeStep 1e-07; + +EulerImplicitCoeffs +{ + cTauChem 1; +} + +odeCoeffs +{ + solver Rosenbrock43; + absTol 1e-8; + relTol 0.01; +} + +#include "_reactions.gas" + +// ************************************************************************* // +%} constant/chemistryProperties.%(0%) +%} diff --git a/Data/Templates/case/constant/combustionProperties.fluid b/Data/Templates/case/constant/combustionProperties.fluid new file mode 100644 index 00000000..ef192f7b --- /dev/null +++ b/Data/Templates/case/constant/combustionProperties.fluid @@ -0,0 +1,23 @@ +%{%(reactionsEnabled%) +%:True +%{%(initialValues/VolumeFractions%) +%[_header%] +FoamFile +{ + format ascii; + class dictionary; + location "constant"; + object combustionProperties.%(0%); +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +combustionModel PaSR; + +PaSRCoeffs +{ + Cmix 1.0; +} + +// ************************************************************************* // +%} constant/combustionProperties.%(0%) +%} diff --git a/Data/Templates/case/constant/g b/Data/Templates/case/constant/g index 0ca29fe2..5d813d39 100644 --- a/Data/Templates/case/constant/g +++ b/Data/Templates/case/constant/g @@ -1,5 +1,5 @@ %{%(solver/SolverName%) -%:buoyantSimpleFoam buoyantPimpleFoam interFoam multiphaseInterFoam +%:buoyantSimpleFoam buoyantPimpleFoam interFoam multiphaseInterFoam multiphaseEulerFoam %[_header%] FoamFile { diff --git a/Data/Templates/case/constant/momentumTransport b/Data/Templates/case/constant/momentumTransport deleted file mode 100644 index a7298c5c..00000000 --- a/Data/Templates/case/constant/momentumTransport +++ /dev/null @@ -1,11 +0,0 @@ -%[_header%] -FoamFile -{ - version 2.0; - format ascii; - class dictionary; - object momentumTransport; -} -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // - -#include "turbulenceProperties" \ No newline at end of file diff --git a/Data/Templates/case/constant/momentumTransport.fluid b/Data/Templates/case/constant/momentumTransport.fluid new file mode 100644 index 00000000..8ec8c9c7 --- /dev/null +++ b/Data/Templates/case/constant/momentumTransport.fluid @@ -0,0 +1,58 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam +%{%(initialValues/VolumeFractions%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object momentumTransport.%(0%); +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +%{%(physics/Turbulence%) +%:RANS +simulationType RAS; + +RAS +{ + RASModel %(physics/TurbulenceModel%); + + turbulence on; + + printCoeffs on; +} +%:DES LES +simulationType LES; + +LES +{ + LESModel %(physics/TurbulenceModel%); + + turbulence on; + + printCoeffs on; + +%{%(physics/TurbulenceModel%) +%:kOmegaSSTDES kOmegaSSTDDES SpalartAllmarasDES SpalartAllmarasDDES Smagorinsky kEqn WALE + delta vanDriest; + +%[constant/_turbulenceProperties_LES%] +%:kOmegaSSTIDDES SpalartAllmarasIDDES + delta IDDESDelta; + + IDDESDelta + { + cw 0.15; + deltaCoeff 1; + } + +%} +} +%:default +simulationType laminar; +%} + +%} constant/momentumTransport.%(0%) +%} diff --git a/Data/Templates/case/constant/phaseProperties b/Data/Templates/case/constant/phaseProperties new file mode 100644 index 00000000..625352ef --- /dev/null +++ b/Data/Templates/case/constant/phaseProperties @@ -0,0 +1,140 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam +%[_header%] +FoamFile +{ + format ascii; + class dictionary; + location "constant"; + object phaseProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +type basicMultiphaseSystem; + +phases (air water); + +air +{ + type purePhaseModel; + diameterModel isothermal; + isothermalCoeffs + { + d0 3e-3; + p0 1e5; + } + + residualAlpha 1e-6; +} + +water +{ + type purePhaseModel; + diameterModel constant; + constantCoeffs + { + d 1e-4; + } + + residualAlpha 1e-6; +} + +blending +{ + default + { + type linear; + minFullyContinuousAlpha.air 0.7; + minPartlyContinuousAlpha.air 0.3; + minFullyContinuousAlpha.water 0.7; + minPartlyContinuousAlpha.water 0.3; + } + + drag + { + type linear; + minFullyContinuousAlpha.air 0.7; + minPartlyContinuousAlpha.air 0.5; + minFullyContinuousAlpha.water 0.7; + minPartlyContinuousAlpha.water 0.5; + } +} + +surfaceTension +{ + air_water + { + type constant; + sigma 0.07; + } +} + +drag +{ + air_dispersedIn_water + { + type SchillerNaumann; + residualRe 1e-3; + } + + water_dispersedIn_air + { + type SchillerNaumann; + residualRe 1e-3; + } + + air_segregatedWith_water + { + type segregated; + m 0.5; + n 8; + } +} + +virtualMass +{ + air_dispersedIn_water + { + type constantCoefficient; + Cvm 0.5; + } + + water_dispersedIn_air + { + type constantCoefficient; + Cvm 0.5; + } +} + +heatTransfer +{ + air_dispersedIn_water + { + type RanzMarshall; + residualAlpha 1e-4; + } + + water_dispersedIn_air + { + type RanzMarshall; + residualAlpha 1e-4; + } +} + +phaseTransfer +{} + +lift +{} + +wallLubrication +{} + +turbulentDispersion +{} + +interfaceCompression +{} + +// ************************************************************************* // +%} diff --git a/Data/Templates/case/constant/physicalProperties.fluid b/Data/Templates/case/constant/physicalProperties.fluid new file mode 100644 index 00000000..58aaa7d1 --- /dev/null +++ b/Data/Templates/case/constant/physicalProperties.fluid @@ -0,0 +1,45 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam +%{%(initialValues/VolumeFractions%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object physicalProperties.%(0%); +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +thermoType +{ + type heRhoThermo; + mixture pureMixture; + transport const; + thermo hConst; + equationOfState perfectGas; + specie specie; + energy sensibleInternalEnergy; +} + +mixture +{ + specie + { + molWeight 28.9; + } + thermodynamics + { + Cp 1007; + Hf 0; + } + transport + { + mu 1.84e-05; + Pr 0.7; + } +} + +// ************************************************************************* // +%} constant/physicalProperties.%(0%) +%} diff --git a/Data/Templates/case/constant/transportProperties b/Data/Templates/case/constant/transportProperties index a979de04..63c4169e 100644 --- a/Data/Templates/case/constant/transportProperties +++ b/Data/Templates/case/constant/transportProperties @@ -58,4 +58,4 @@ sigmas %} // ************************************************************************* // -%} \ No newline at end of file +%} diff --git a/Data/Templates/case/constant/turbulenceProperties b/Data/Templates/case/constant/turbulenceProperties index 967c15a7..e9a22c1c 100644 --- a/Data/Templates/case/constant/turbulenceProperties +++ b/Data/Templates/case/constant/turbulenceProperties @@ -1,3 +1,6 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam +%:default %[_header%] FoamFile { @@ -50,3 +53,4 @@ LES %:default simulationType laminar; %} +%} diff --git a/Data/Templates/case/system/fvSolution b/Data/Templates/case/system/fvSolution index 40477c21..9e5384e4 100644 --- a/Data/Templates/case/system/fvSolution +++ b/Data/Templates/case/system/fvSolution @@ -80,7 +80,7 @@ SIMPLE PIMPLE { %{%(solver/SolverName%) -%:interFoam multiphaseInterFoam +%:interFoam multiphaseInterFoam multiphaseEulerFoam nOuterCorrectors 1; nNonOrthogonalCorrectors 2; nCorrectors 3; @@ -172,7 +172,7 @@ solvers %} %{%(solver/SolverName%) -%:interFoam multiphaseInterFoam +%:interFoam multiphaseInterFoam multiphaseEulerFoam "alpha.*" { nAlphaCorr 1; diff --git a/Gui/Icons/phasephysics.svg b/Gui/Icons/phasephysics.svg new file mode 100644 index 00000000..f2c1d7ef --- /dev/null +++ b/Gui/Icons/phasephysics.svg @@ -0,0 +1,68 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Gui/TaskPanelCfdFluidProperties.ui b/Gui/TaskPanelCfdFluidProperties.ui index c69a59f7..c658681d 100644 --- a/Gui/TaskPanelCfdFluidProperties.ui +++ b/Gui/TaskPanelCfdFluidProperties.ui @@ -23,21 +23,8 @@ QFrame::Raised - - - - Qt::Vertical - - - - 20 - 40 - - - - - - + + 75 @@ -45,15 +32,19 @@ - Material name + Fluid properties - - + + + + Compressible + + - - + + 75 @@ -61,20 +52,39 @@ - Fluid properties + Material name - - + + + + + - Compressible + <html><head/><body><p><span style=" font-weight:600;">Other phase(s)</span></p></body></html> + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -87,16 +97,6 @@ QFrame::Raised - - - - Material Description - - - true - - - @@ -114,6 +114,16 @@ + + + + Material Description + + + true + + + diff --git a/Gui/TaskPanelPhasePhysics.ui b/Gui/TaskPanelPhasePhysics.ui new file mode 100644 index 00000000..321ea0f9 --- /dev/null +++ b/Gui/TaskPanelPhasePhysics.ui @@ -0,0 +1,651 @@ + + + Form + + + + 0 + 0 + 474 + 907 + + + + Select physics model + + + + + + + 0 + 0 + + + + QFrame::Panel + + + QFrame::Plain + + + 0 + + + + + + + 75 + true + + + + Interphasial transfer + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + Heat transfer + + + + + + + Mass transfer + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::Panel + + + QFrame::Plain + + + 0 + + + + + + + 75 + true + + + + Forces + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 6 + + + 0 + + + 6 + + + + + Lift + + + + + + + + + + Turbulent dispersion + + + + + + + + + + Surface Tension + + + + + + + Drag + + + + + + + + + + + + + Wall lubrication + + + + + + + Virtual mass + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::Panel + + + QFrame::Plain + + + 0 + + + + + + + 0 + 0 + + + + Basic multiphase + + + + + + + + 75 + true + + + + Type + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Pure phase model + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Phase types</span></p></body></html> + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + QFrame::Panel + + + QFrame::Plain + + + 0 + + + + + + + 0 + 0 + + + + 0 + + + 1000.000000000000000 + + + m/s^2 + + + 0 + + + + + + + + 0 + 0 + + + + 0 + + + 1000.000000000000000 + + + m/s^2 + + + 0 + + + + + + + + 0 + 0 + + + + 0 + + + 1000.000000000000000 + + + m/s^2 + + + 0 + + + + + + + + 75 + true + + + + Gravity + + + + + + + + + + + 16777215 + 16777215 + + + + QFrame::Panel + + + QFrame::Plain + + + 0 + + + + + + + 0 + 0 + + + + 0 + + + 1000.000000000000000 + + + + + + 0 + + + + + + + + 0 + 0 + + + + 0 + + + 1000.000000000000000 + + + + + + 0 + + + + + + + + 0 + 0 + + + + 0 + + + 1000.000000000000000 + + + + + + 0 + + + + + + + + 75 + true + + + + Moving reference frame (SRF) + + + + + + + RPM + + + + + + + Rotational axis + + + + + + + + 0 + 0 + + + + + + + 1.000000000000000 + + + rev/s + + + g + + + + + + + Centre of rotation + + + + + + + + 0 + 0 + + + + 0 + + + 1000.000000000000000 + + + mm + + + 0 + + + + + + + + 0 + 0 + + + + 0 + + + 1000.000000000000000 + + + mm + + + 0 + + + + + + + + 0 + 0 + + + + 0 + + + 1000.000000000000000 + + + mm + + + 0 + + + + + + + + + + + Gui::InputField + QLineEdit +
Gui/InputField.h
+
+
+ + radioButtonSteady + checkBoxIsothermal + gx + gy + gz + inputSRFRPM + inputSRFCoRx + inputSRFCoRy + inputSRFCoRz + inputSRFAxisx + inputSRFAxisy + inputSRFAxisz + + + +
diff --git a/Gui/TaskPanelPhysics.ui b/Gui/TaskPanelPhysics.ui index 263c2bcc..e9e99754 100644 --- a/Gui/TaskPanelPhysics.ui +++ b/Gui/TaskPanelPhysics.ui @@ -1,4 +1,4 @@ - + Form @@ -314,29 +314,29 @@
- - + + QFrame::NoFrame QFrame::Raised - + 0 - 0 + 6 0 - 0 + 6 - - + + 0 @@ -344,12 +344,12 @@ - Isothermal + Single phase - - + + 0 @@ -357,56 +357,79 @@ - High Mach number + Multiphase - Free Surface + + + + + + + Multiphase - Eulerian - - - - Rotating frame (SRF) - - - - - + + QFrame::NoFrame QFrame::Raised - + 0 - 6 + 0 0 - 6 + 0 - - + + + + Rotating frame (SRF) + + + + + - + 0 0 - Single phase + Isothermal - - + + + + true + + + Viscous + + + false + + + false + + + + + 0 @@ -414,29 +437,20 @@ - Multiphase - free surface + High Mach number + + + + + + + Reacting - - - - true - - - Viscous - - - false - - - false - - -
@@ -471,50 +485,17 @@ 0 - - - - - 0 - 0 - - - - QFrame::NoFrame + + + + + 75 + true + - - QFrame::Raised + + Turbulence - - - - - &Laminar - - - - - - - DES - - - - - - - RANS - - - - - - - LES - - - - @@ -568,17 +549,50 @@ - - - - - 75 - true - + + + + + 0 + 0 + - - Turbulence + + QFrame::NoFrame + + + QFrame::Raised + + + + + &Laminar + + + + + + + DES + + + + + + + RANS + + + + + + + LES + + + +
@@ -702,10 +716,7 @@ radioButtonSteady radioButtonTransient - radioButtonSinglePhase checkBoxIsothermal - viscousCheckBox - srfCheckBox radioButtonLaminar radioButtonRANS radioButtonDES diff --git a/InitGui.py b/InitGui.py index 85bd4d2b..cbb0ebec 100644 --- a/InitGui.py +++ b/InitGui.py @@ -3,7 +3,7 @@ # * (c) qingfeng xia @ iesensor.com 2016 * # * Copyright (c) 2017 Andrew Gill (CSIR) * # * Copyright (c) 2019-2021 Oliver Oxtoby * -# * Copyright (c) 2022 Jonathan Bergh * +# * Copyright (c) 2022-2024 Jonathan Bergh * # * * # * This program is free software: you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License as * @@ -60,6 +60,7 @@ def Initialize(self): from CfdOF.PostProcess.CfdReportingFunction import CommandCfdReportingFunction from CfdOF.Solve.CfdScalarTransportFunction import CommandCfdScalarTransportFunction from CfdOF.CfdReloadWorkbench import CommandCfdReloadWorkbench + from CfdOF.Solve.CfdPhasePhysicsSelection import CommandCfdPhasePhysicsSelection FreeCADGui.addCommand('Cfd_Analysis', CommandCfdAnalysis()) FreeCADGui.addCommand('Cfd_MeshFromShape', CommandCfdMeshFromShape()) @@ -77,12 +78,16 @@ def Initialize(self): FreeCADGui.addCommand('Cfd_ReportingFunctions', CommandCfdReportingFunction()) FreeCADGui.addCommand('Cfd_ScalarTransportFunctions', CommandCfdScalarTransportFunction()) FreeCADGui.addCommand('Cfd_ReloadWorkbench', CommandCfdReloadWorkbench()) + FreeCADGui.addCommand('Cfd_PhasePhysicsSelection', CommandCfdPhasePhysicsSelection()) cmdlst = ['Cfd_Analysis', - 'Cfd_MeshFromShape', 'Cfd_MeshRegion', + 'Cfd_MeshFromShape', 'Cfd_MeshRegion', ("Dynamic mesh refinement", ['Cfd_DynamicMeshInterfaceRefinement','Cfd_DynamicMeshShockRefinement',]), ('Cfd_GroupDynamicMeshRefinement',), - 'Cfd_PhysicsModel', 'Cfd_FluidMaterial', + 'Cfd_PhysicsModel', + ("Advanced physics model selection", ['Cfd_PhasePhysicsSelection']), + ('Cfd_PhasePhysicsSelection',), + 'Cfd_FluidMaterial', 'Cfd_FluidBoundary', 'Cfd_InitialiseInternal', 'Cfd_InitialisationZone', 'Cfd_PorousZone', 'Cfd_ReportingFunctions', 'Cfd_ScalarTransportFunctions', diff --git a/README.md b/README.md index f0e4a5f5..052a1fc6 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,14 @@ www.openfoam.com, and owner of the OPENFOAM® and OpenCFD® trade marks * Incompressible, laminar flow (simpleFoam, pimpleFoam) * Support for various RANS, LES and DES turbulent flow models * Incompressible free-surface flow (interFoam, multiphaseInterFoam) +* Compressible Eulerian-Eulerian multiphase flow (multiphaseEulerianFoam) * Compressible buoyant flow (buoyantSimpleFoam, buoyantPimpleFoam) * High-speed compressible flow ([HiSA](https://hisa.gitlab.io)) * Porous regions and porous baffles * Basic material database * Flow initialisation with a potential solver * Solution of arbitrary passive scalar transport functions +* Single phase and multiphase combustion modelling #### Meshing * Cut-cell Cartesian meshing with boundary layers (cfMesh) * Cut-cell Cartesian meshing with baffles (snappyHexMesh) @@ -294,7 +296,7 @@ and the [Council for Scientific and Industrial Research](https://www.csir.co.za) * Oliver Oxtoby (CSIR, 2016-2018; private 2019-) * Johan Heyns (CSIR, 2016-2018) * Alfred Bogaers (CSIR, 2016-2018) -* Jonathan Bergh (2022) +* Jonathan Bergh (2022-) * Qingfeng Xia (2015) * Thomas Schrader (2017-) * Michael Hindley (2016)