From d99900b9d20c1f35848bc7c981f493779b33297a Mon Sep 17 00:00:00 2001 From: Jonathan Bergh Date: Sat, 23 Mar 2024 20:20:02 +0100 Subject: [PATCH 01/10] Start implementing Eulerian multiphase --- CfdOF/Solve/CfdCaseWriterFoam.py | 2 + CfdOF/Solve/CfdPhysicsSelection.py | 2 +- CfdOF/Solve/TaskPanelCfdPhysicsSelection.py | 11 ++- Gui/TaskPanelPhysics.ui | 100 ++++++++++---------- 4 files changed, 62 insertions(+), 53 deletions(-) diff --git a/CfdOF/Solve/CfdCaseWriterFoam.py b/CfdOF/Solve/CfdCaseWriterFoam.py index 2a021d9c..dd6f8a84 100644 --- a/CfdOF/Solve/CfdCaseWriterFoam.py +++ b/CfdOF/Solve/CfdCaseWriterFoam.py @@ -201,6 +201,8 @@ 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': + solver = 'multiphaseEulerFoam' else: raise RuntimeError(self.physics_model.Phase + " phase model currently not supported.") diff --git a/CfdOF/Solve/CfdPhysicsSelection.py b/CfdOF/Solve/CfdPhysicsSelection.py index f7d2023e..e9e91e76 100644 --- a/CfdOF/Solve/CfdPhysicsSelection.py +++ b/CfdOF/Solve/CfdPhysicsSelection.py @@ -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' diff --git a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py index 88af5e50..41fda2bb 100644 --- a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py +++ b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py @@ -71,6 +71,8 @@ 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') @@ -139,7 +141,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) @@ -159,7 +161,7 @@ def updateUI(self): if self.form.checkBoxIsothermal.isChecked(): self.form.checkBoxHighMach.setChecked(False) - # Viscous + # Viscous if self.form.viscousCheckBox.isChecked(): self.form.turbulenceFrame.setVisible(True) # RANS @@ -203,7 +205,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(): @@ -250,4 +253,4 @@ def reject(self): def closing(self): # We call this from unsetEdit to allow cleanup - return \ No newline at end of file + return diff --git a/Gui/TaskPanelPhysics.ui b/Gui/TaskPanelPhysics.ui index 263c2bcc..5e48d08e 100644 --- a/Gui/TaskPanelPhysics.ui +++ b/Gui/TaskPanelPhysics.ui @@ -314,29 +314,29 @@ - - + + QFrame::NoFrame QFrame::Raised - + 0 - 0 + 6 0 - 0 + 6 - - + + 0 @@ -344,12 +344,12 @@ - Isothermal + Single phase - - + + 0 @@ -357,43 +357,59 @@ - High Mach number + Multiphase - Free Surface + + + + + + + Multiphase - Eulerian - - - - Rotating frame (SRF) - - - - - + + QFrame::NoFrame QFrame::Raised - + 0 - 6 + 0 0 - 6 + 0 - - + + + + true + + + Viscous + + + false + + + false + + + + + 0 @@ -401,12 +417,12 @@ - Single phase + Isothermal - - + + 0 @@ -414,29 +430,20 @@ - Multiphase - free surface + High Mach number + + + + + + + Rotating frame (SRF) - - - - true - - - Viscous - - - false - - - false - - - @@ -702,10 +709,7 @@ radioButtonSteady radioButtonTransient - radioButtonSinglePhase checkBoxIsothermal - viscousCheckBox - srfCheckBox radioButtonLaminar radioButtonRANS radioButtonDES From fbe8ddced928ae3ac3e4949afe0a2e66e7742be6 Mon Sep 17 00:00:00 2001 From: Jonathan Bergh Date: Fri, 29 Mar 2024 09:40:12 +0100 Subject: [PATCH 02/10] Add various templates required for multiphaseEulerFoam and reactingMultiphaseEulerFoam --- .../case/constant/chemistryProperties.fluid | 39 ++++++ .../case/constant/combustionProperties.fluid | 23 ++++ .../case/constant/physicalProperties.fluid | 21 ++++ Data/Templates/case/constant/reactions.gas | 13 ++ Data/Templates/case/constant/thermo.gas | 113 ++++++++++++++++++ 5 files changed, 209 insertions(+) create mode 100644 Data/Templates/case/constant/chemistryProperties.fluid create mode 100644 Data/Templates/case/constant/combustionProperties.fluid create mode 100644 Data/Templates/case/constant/physicalProperties.fluid create mode 100644 Data/Templates/case/constant/reactions.gas create mode 100644 Data/Templates/case/constant/thermo.gas diff --git a/Data/Templates/case/constant/chemistryProperties.fluid b/Data/Templates/case/constant/chemistryProperties.fluid new file mode 100644 index 00000000..78e8bd69 --- /dev/null +++ b/Data/Templates/case/constant/chemistryProperties.fluid @@ -0,0 +1,39 @@ +%{%(solver/SolverName%) +%:reactingMultiphaseEulerFoam +%{%(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..4e4be759 --- /dev/null +++ b/Data/Templates/case/constant/combustionProperties.fluid @@ -0,0 +1,23 @@ +%{%(solver/SolverName%) +%:reactingMultiphaseEulerFoam +%{%(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/physicalProperties.fluid b/Data/Templates/case/constant/physicalProperties.fluid new file mode 100644 index 00000000..7c19e360 --- /dev/null +++ b/Data/Templates/case/constant/physicalProperties.fluid @@ -0,0 +1,21 @@ +%{%(solver/SolverName%) +%:multiphaseEulerFoam reactingMultiphaseEulerFoam +%{%(initialValues/VolumeFractions%) +%[_header%] +FoamFile +{ + format ascii; + class dictionary; + location "constant"; + object physicalProperties.%(0%); +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +viscosityModel constant; + +nu [0 2 -1 0 0 0 0] 1.48e-05; +rho [1 -3 0 0 0 0 0] 1; + +// ************************************************************************* // +%} constant/physicalProperties.%(0%) +%} 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/thermo.gas b/Data/Templates/case/constant/thermo.gas new file mode 100644 index 00000000..e558ad4b --- /dev/null +++ b/Data/Templates/case/constant/thermo.gas @@ -0,0 +1,113 @@ +species +( + CO + CO2 + H2 + H2O + AIR +); + +CO +{ + specie + { + molWeight 28.0106; + } + thermodynamics + { + Tlow 200; + Thigh 3500; + Tcommon 1000; + highCpCoeffs ( 2.71519 0.00206253 -9.98826e-07 2.30053e-10 -2.03648e-14 -14151.9 7.81869 ); + lowCpCoeffs ( 3.57953 -0.000610354 1.01681e-06 9.07006e-10 -9.04424e-13 -14344.1 3.50841 ); + } + transport + { + As 1.67212e-06; + Ts 170.672; + } +} + +CO2 +{ + specie + { + molWeight 44.01; + } + thermodynamics + { + Tlow 200; + Thigh 3500; + Tcommon 1000; + highCpCoeffs ( 3.85746 0.00441437 -2.21481e-06 5.2349e-10 -4.72084e-14 -48759.2 2.27164 ); + lowCpCoeffs ( 2.35677 0.0089846 -7.12356e-06 2.45919e-09 -1.437e-13 -48372 9.90105 ); + } + transport + { + As 1.67212e-06; + Ts 170.672; + } +} + +H2 +{ + specie + { + molWeight 2.01594; + } + thermodynamics + { + Tlow 200; + Thigh 3500; + Tcommon 1000; + highCpCoeffs ( 3.33728 -4.94025e-05 4.99457e-07 -1.79566e-10 2.00255e-14 -950.159 -3.20502 ); + lowCpCoeffs ( 2.34433 0.00798052 -1.94782e-05 2.01572e-08 -7.37612e-12 -917.935 0.68301 ); + } + transport + { + As 1.67212e-06; + Ts 170.672; + } +} + +H2O +{ + specie + { + molWeight 18.0153; + } + thermodynamics + { + Tlow 200; + Thigh 3500; + Tcommon 1000; + highCpCoeffs ( 3.03399 0.00217692 -1.64073e-07 -9.7042e-11 1.68201e-14 -30004.3 4.96677 ); + lowCpCoeffs ( 4.19864 -0.00203643 6.5204e-06 -5.48797e-09 1.77198e-12 -30293.7 -0.849032 ); + } + transport + { + As 1.67212e-06; + Ts 170.672; + } +} + +AIR +{ + specie + { + molWeight 28.9596; + } + thermodynamics + { + Tlow 200; + Thigh 3500; + Tcommon 1000; + highCpCoeffs ( 3.57304 -7.24383e-04 1.67022e-06 -1.26501e-10 -4.20580e-13 -1047.41 3.12431 ); + lowCpCoeffs ( 3.09589 1.22835e-03 -4.14267e-07 6.56910e-11 -3.87021e-15 -983.191 5.34161 ); + } + transport + { + As 1.67212e-06; + Ts 170.672; + } +} From a9110db10bf8e4fb2f616650556f9c2fe9fc5f1a Mon Sep 17 00:00:00 2001 From: Jonathan Bergh Date: Fri, 29 Mar 2024 13:07:33 +0100 Subject: [PATCH 03/10] This commit does the following things: * Updates the UI to allow selection of Eulerian MF and Reaction models * Sets up selection logic for basic combinations * Updates physics object and solver selection --- CfdOF/Solve/CfdCaseWriterFoam.py | 5 +- CfdOF/Solve/CfdPhysicsSelection.py | 6 +- CfdOF/Solve/TaskPanelCfdPhysicsSelection.py | 21 ++- Gui/TaskPanelPhysics.ui | 143 ++++++++++---------- 4 files changed, 103 insertions(+), 72 deletions(-) diff --git a/CfdOF/Solve/CfdCaseWriterFoam.py b/CfdOF/Solve/CfdCaseWriterFoam.py index dd6f8a84..a8a91c46 100644 --- a/CfdOF/Solve/CfdCaseWriterFoam.py +++ b/CfdOF/Solve/CfdCaseWriterFoam.py @@ -202,7 +202,10 @@ def getSolverName(self): else: raise RuntimeError("Only transient analysis is supported for free surface flow simulation.") elif self.physics_model.Phase == 'Eulerian': - solver = 'multiphaseEulerFoam' + if self.physics_model.ReactingModelEnabled == 'Reacting': + solver = 'reactingMultiphaseEulerFoam' + else: + solver = 'multiphaseEulerFoam' else: raise RuntimeError(self.physics_model.Phase + " phase model currently not supported.") diff --git a/CfdOF/Solve/CfdPhysicsSelection.py b/CfdOF/Solve/CfdPhysicsSelection.py index e9e91e76..d2f22004 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 * @@ -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, 'ReactingModelEnabled', False, "App::PropertyBool", "Physics modelling", + "Reaction models enabled") + def onDocumentRestored(self, obj): self.initProperties(obj) diff --git a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py index 41fda2bb..d956573a 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 * @@ -78,6 +78,9 @@ def load(self): self.form.checkBoxIsothermal.setChecked(self.obj.Flow == 'Isothermal') self.form.checkBoxHighMach.setChecked(self.obj.Flow == 'HighMachCompressible') + # Reactions + self.form.checkBoxReacting.setChecked(self.obj.ReactingModelEnabled) + # Turbulence if self.obj.Turbulence == 'Inviscid': self.form.viscousCheckBox.setChecked(False) @@ -149,18 +152,29 @@ 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 # Placeholder, do nothing at the moment + # High Mach capability self.form.checkBoxHighMach.setEnabled(not self.form.checkBoxIsothermal.isChecked()) if self.form.checkBoxIsothermal.isChecked(): self.form.checkBoxHighMach.setChecked(False) + # 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) + # Viscous if self.form.viscousCheckBox.isChecked(): self.form.turbulenceFrame.setVisible(True) @@ -247,6 +261,9 @@ def accept(self): self.form.inputSRFAxisz.property("quantity").Value) storeIfChanged(self.obj, 'SRFModelAxis', model_axis) + if self.form.checkBoxReacting.isChecked(): + storeIfChanged(self.obj, 'ReactingModelEnabled', self.form.checkBoxReacting.isChecked()) + def reject(self): doc = FreeCADGui.getDocument(self.obj.Document) doc.resetEdit() diff --git a/Gui/TaskPanelPhysics.ui b/Gui/TaskPanelPhysics.ui index 5e48d08e..9aeb17ad 100644 --- a/Gui/TaskPanelPhysics.ui +++ b/Gui/TaskPanelPhysics.ui @@ -371,7 +371,7 @@ - + QFrame::NoFrame @@ -392,26 +392,17 @@ 0 - - - - true - + + - Viscous - - - false - - - false + Rotating frame (SRF) - + 0 0 @@ -422,6 +413,22 @@ + + + true + + + Viscous + + + false + + + false + + + + @@ -434,10 +441,10 @@ - - + + - Rotating frame (SRF) + Reacting @@ -478,50 +485,17 @@ 0 - - - - - 0 - 0 - - - - QFrame::NoFrame + + + + + 75 + true + - - QFrame::Raised + + Turbulence - - - - - &Laminar - - - - - - - DES - - - - - - - RANS - - - - - - - LES - - - - @@ -575,17 +549,50 @@ - - - - - 75 - true - + + + + + 0 + 0 + - - Turbulence + + QFrame::NoFrame + + QFrame::Raised + + + + + + &Laminar + + + + + + + DES + + + + + + + RANS + + + + + + + LES + + + + From 2a8480d57ea2b8b192beb2c6c872ac6769cd4dd3 Mon Sep 17 00:00:00 2001 From: Jonathan Bergh Date: Fri, 29 Mar 2024 13:16:55 +0100 Subject: [PATCH 04/10] Update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f0e4a5f5..b8791500 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ 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 From 60a8218ac1838f13c33782a8eab3a36a631303f4 Mon Sep 17 00:00:00 2001 From: Jonathan Bergh Date: Fri, 29 Mar 2024 19:01:52 +0100 Subject: [PATCH 05/10] Add processing for combustion models and templates, case writing --- CfdOF/CfdTools.py | 7 ++++++ CfdOF/Solve/CfdCaseWriterFoam.py | 24 +++++++++++++------ CfdOF/Solve/CfdPhysicsSelection.py | 2 +- CfdOF/Solve/TaskPanelCfdPhysicsSelection.py | 4 ++-- .../{reactions.gas => _reactions.gas} | 0 .../case/constant/{thermo.gas => _thermo.gas} | 0 .../case/constant/chemistryProperties.fluid | 6 ++--- .../case/constant/combustionProperties.fluid | 4 ++-- README.md | 3 ++- 9 files changed, 34 insertions(+), 16 deletions(-) rename Data/Templates/case/constant/{reactions.gas => _reactions.gas} (100%) rename Data/Templates/case/constant/{thermo.gas => _thermo.gas} (100%) 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 a8a91c46..5a8ce987 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,12 @@ def writeCase(self): cfdMessage('Dynamic mesh adaptation rule present\n') self.processDynamicMeshRefinement() + #TODO remove + print("HERE") + if True: #self.reaction_models: + cfdMessage('Reactions present\n') + self.processReactions() + self.settings['createPatchesFromSnappyBaffles'] = False self.settings['createPatchesForPeriodics'] = False cfdMessage("Matching boundary conditions ...\n") @@ -202,10 +210,7 @@ def getSolverName(self): else: raise RuntimeError("Only transient analysis is supported for free surface flow simulation.") elif self.physics_model.Phase == 'Eulerian': - if self.physics_model.ReactingModelEnabled == 'Reacting': - solver = 'reactingMultiphaseEulerFoam' - else: - solver = 'multiphaseEulerFoam' + solver = 'multiphaseEulerFoam' else: raise RuntimeError(self.physics_model.Phase + " phase model currently not supported.") @@ -589,6 +594,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 @@ -597,14 +607,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' @@ -674,7 +684,7 @@ def processPorousZoneProperties(self): def processInitialisationZoneProperties(self): settings = self.settings - + for zone_name in settings['initialisationZones']: z = settings['initialisationZones'][zone_name] diff --git a/CfdOF/Solve/CfdPhysicsSelection.py b/CfdOF/Solve/CfdPhysicsSelection.py index d2f22004..91ca2f2d 100644 --- a/CfdOF/Solve/CfdPhysicsSelection.py +++ b/CfdOF/Solve/CfdPhysicsSelection.py @@ -161,7 +161,7 @@ def initProperties(self, obj): "Axis of rotation (SRF)") # Reacting model - addObjectProperty(obj, 'ReactingModelEnabled', False, "App::PropertyBool", "Physics modelling", + addObjectProperty(obj, 'ReactionModelsEnabled', False, "App::PropertyBool", "Physics modelling", "Reaction models enabled") def onDocumentRestored(self, obj): diff --git a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py index d956573a..8286453d 100644 --- a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py +++ b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py @@ -79,7 +79,7 @@ def load(self): self.form.checkBoxHighMach.setChecked(self.obj.Flow == 'HighMachCompressible') # Reactions - self.form.checkBoxReacting.setChecked(self.obj.ReactingModelEnabled) + self.form.checkBoxReacting.setChecked(self.obj.ReactionModelsEnabled) # Turbulence if self.obj.Turbulence == 'Inviscid': @@ -262,7 +262,7 @@ def accept(self): storeIfChanged(self.obj, 'SRFModelAxis', model_axis) if self.form.checkBoxReacting.isChecked(): - storeIfChanged(self.obj, 'ReactingModelEnabled', self.form.checkBoxReacting.isChecked()) + storeIfChanged(self.obj, 'ReactionModelsEnabled', self.form.checkBoxReacting.isChecked()) def reject(self): doc = FreeCADGui.getDocument(self.obj.Document) diff --git a/Data/Templates/case/constant/reactions.gas b/Data/Templates/case/constant/_reactions.gas similarity index 100% rename from Data/Templates/case/constant/reactions.gas rename to Data/Templates/case/constant/_reactions.gas diff --git a/Data/Templates/case/constant/thermo.gas b/Data/Templates/case/constant/_thermo.gas similarity index 100% rename from Data/Templates/case/constant/thermo.gas rename to Data/Templates/case/constant/_thermo.gas diff --git a/Data/Templates/case/constant/chemistryProperties.fluid b/Data/Templates/case/constant/chemistryProperties.fluid index 78e8bd69..291e1f97 100644 --- a/Data/Templates/case/constant/chemistryProperties.fluid +++ b/Data/Templates/case/constant/chemistryProperties.fluid @@ -1,5 +1,5 @@ -%{%(solver/SolverName%) -%:reactingMultiphaseEulerFoam +%{%(reactionsEnabled%) +%:True %{%(initialValues/VolumeFractions%) %[_header%] FoamFile @@ -32,7 +32,7 @@ odeCoeffs relTol 0.01; } -#include "reactions.gas" +#include "_reactions.gas" // ************************************************************************* // %} constant/chemistryProperties.%(0%) diff --git a/Data/Templates/case/constant/combustionProperties.fluid b/Data/Templates/case/constant/combustionProperties.fluid index 4e4be759..ef192f7b 100644 --- a/Data/Templates/case/constant/combustionProperties.fluid +++ b/Data/Templates/case/constant/combustionProperties.fluid @@ -1,5 +1,5 @@ -%{%(solver/SolverName%) -%:reactingMultiphaseEulerFoam +%{%(reactionsEnabled%) +%:True %{%(initialValues/VolumeFractions%) %[_header%] FoamFile diff --git a/README.md b/README.md index b8791500..052a1fc6 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ www.openfoam.com, and owner of the OPENFOAM® and OpenCFD® trade marks * 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) @@ -295,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) From 47addc3fcb5c5c8fac217f2442b45f1dcb843a97 Mon Sep 17 00:00:00 2001 From: Jonathan Bergh Date: Sun, 31 Mar 2024 09:01:13 +0200 Subject: [PATCH 06/10] Update existing templates, add additional templates, continue other changes --- CfdOF/Solve/CfdCaseWriterFoam.py | 25 +- CfdOF/Solve/TaskPanelCfdPhysicsSelection.py | 12 +- Data/Templates/case/0/T.fluid | 176 ++++++++++++++ Data/Templates/case/0/U | 4 +- Data/Templates/case/0/U.fluid | 220 ++++++++++++++++++ Data/Templates/case/0/alpha.fluid | 4 +- Data/Templates/case/0/alphat.fluid | 57 +++++ Data/Templates/case/0/k | 6 +- Data/Templates/case/0/k.fluid | 90 +++++++ Data/Templates/case/0/nut | 6 +- Data/Templates/case/0/nut.fluid | 97 ++++++++ Data/Templates/case/0/omega | 6 +- Data/Templates/case/0/omega.fluid | 84 +++++++ Data/Templates/case/0/p | 4 +- Data/Templates/case/0/p_rgh | 2 +- Data/Templates/case/Allrun | 1 + Data/Templates/case/constant/_thermo.gas | 113 --------- Data/Templates/case/constant/g | 2 +- .../Templates/case/constant/momentumTransport | 11 - .../case/constant/momentumTransport.fluid | 58 +++++ Data/Templates/case/constant/phaseProperties | 140 +++++++++++ .../case/constant/physicalProperties.fluid | 38 ++- .../case/constant/transportProperties | 2 +- .../case/constant/turbulenceProperties | 4 + Data/Templates/case/system/fvSolution | 4 +- Gui/TaskPanelPhysics.ui | 6 +- 26 files changed, 1007 insertions(+), 165 deletions(-) create mode 100644 Data/Templates/case/0/T.fluid create mode 100644 Data/Templates/case/0/U.fluid create mode 100644 Data/Templates/case/0/alphat.fluid create mode 100644 Data/Templates/case/0/k.fluid create mode 100644 Data/Templates/case/0/nut.fluid create mode 100644 Data/Templates/case/0/omega.fluid delete mode 100644 Data/Templates/case/constant/_thermo.gas delete mode 100644 Data/Templates/case/constant/momentumTransport create mode 100644 Data/Templates/case/constant/momentumTransport.fluid create mode 100644 Data/Templates/case/constant/phaseProperties diff --git a/CfdOF/Solve/CfdCaseWriterFoam.py b/CfdOF/Solve/CfdCaseWriterFoam.py index 5a8ce987..fc342aed 100644 --- a/CfdOF/Solve/CfdCaseWriterFoam.py +++ b/CfdOF/Solve/CfdCaseWriterFoam.py @@ -140,9 +140,7 @@ def writeCase(self): cfdMessage('Dynamic mesh adaptation rule present\n') self.processDynamicMeshRefinement() - #TODO remove - print("HERE") - if True: #self.reaction_models: + if self.reaction_models: cfdMessage('Reactions present\n') self.processReactions() @@ -210,7 +208,10 @@ def getSolverName(self): else: raise RuntimeError("Only transient analysis is supported for free surface flow simulation.") elif self.physics_model.Phase == 'Eulerian': - solver = 'multiphaseEulerFoam' + 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.") @@ -341,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 @@ -349,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 @@ -427,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 @@ -435,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 @@ -580,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): @@ -699,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 @@ -708,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/TaskPanelCfdPhysicsSelection.py b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py index 8286453d..42921b82 100644 --- a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py +++ b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py @@ -169,11 +169,13 @@ def updateUI(self): self.form.checkBoxHighMach.setChecked(False) # 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) + #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(): 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/_thermo.gas b/Data/Templates/case/constant/_thermo.gas deleted file mode 100644 index e558ad4b..00000000 --- a/Data/Templates/case/constant/_thermo.gas +++ /dev/null @@ -1,113 +0,0 @@ -species -( - CO - CO2 - H2 - H2O - AIR -); - -CO -{ - specie - { - molWeight 28.0106; - } - thermodynamics - { - Tlow 200; - Thigh 3500; - Tcommon 1000; - highCpCoeffs ( 2.71519 0.00206253 -9.98826e-07 2.30053e-10 -2.03648e-14 -14151.9 7.81869 ); - lowCpCoeffs ( 3.57953 -0.000610354 1.01681e-06 9.07006e-10 -9.04424e-13 -14344.1 3.50841 ); - } - transport - { - As 1.67212e-06; - Ts 170.672; - } -} - -CO2 -{ - specie - { - molWeight 44.01; - } - thermodynamics - { - Tlow 200; - Thigh 3500; - Tcommon 1000; - highCpCoeffs ( 3.85746 0.00441437 -2.21481e-06 5.2349e-10 -4.72084e-14 -48759.2 2.27164 ); - lowCpCoeffs ( 2.35677 0.0089846 -7.12356e-06 2.45919e-09 -1.437e-13 -48372 9.90105 ); - } - transport - { - As 1.67212e-06; - Ts 170.672; - } -} - -H2 -{ - specie - { - molWeight 2.01594; - } - thermodynamics - { - Tlow 200; - Thigh 3500; - Tcommon 1000; - highCpCoeffs ( 3.33728 -4.94025e-05 4.99457e-07 -1.79566e-10 2.00255e-14 -950.159 -3.20502 ); - lowCpCoeffs ( 2.34433 0.00798052 -1.94782e-05 2.01572e-08 -7.37612e-12 -917.935 0.68301 ); - } - transport - { - As 1.67212e-06; - Ts 170.672; - } -} - -H2O -{ - specie - { - molWeight 18.0153; - } - thermodynamics - { - Tlow 200; - Thigh 3500; - Tcommon 1000; - highCpCoeffs ( 3.03399 0.00217692 -1.64073e-07 -9.7042e-11 1.68201e-14 -30004.3 4.96677 ); - lowCpCoeffs ( 4.19864 -0.00203643 6.5204e-06 -5.48797e-09 1.77198e-12 -30293.7 -0.849032 ); - } - transport - { - As 1.67212e-06; - Ts 170.672; - } -} - -AIR -{ - specie - { - molWeight 28.9596; - } - thermodynamics - { - Tlow 200; - Thigh 3500; - Tcommon 1000; - highCpCoeffs ( 3.57304 -7.24383e-04 1.67022e-06 -1.26501e-10 -4.20580e-13 -1047.41 3.12431 ); - lowCpCoeffs ( 3.09589 1.22835e-03 -4.14267e-07 6.56910e-11 -3.87021e-15 -983.191 5.34161 ); - } - transport - { - As 1.67212e-06; - Ts 170.672; - } -} 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 index 7c19e360..58aaa7d1 100644 --- a/Data/Templates/case/constant/physicalProperties.fluid +++ b/Data/Templates/case/constant/physicalProperties.fluid @@ -1,21 +1,45 @@ %{%(solver/SolverName%) -%:multiphaseEulerFoam reactingMultiphaseEulerFoam +%:multiphaseEulerFoam %{%(initialValues/VolumeFractions%) %[_header%] FoamFile { + version 2.0; format ascii; class dictionary; - location "constant"; object physicalProperties.%(0%); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -viscosityModel constant; +thermoType +{ + type heRhoThermo; + mixture pureMixture; + transport const; + thermo hConst; + equationOfState perfectGas; + specie specie; + energy sensibleInternalEnergy; +} -nu [0 2 -1 0 0 0 0] 1.48e-05; -rho [1 -3 0 0 0 0 0] 1; +mixture +{ + specie + { + molWeight 28.9; + } + thermodynamics + { + Cp 1007; + Hf 0; + } + transport + { + mu 1.84e-05; + Pr 0.7; + } +} // ************************************************************************* // -%} constant/physicalProperties.%(0%) -%} +%} 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/TaskPanelPhysics.ui b/Gui/TaskPanelPhysics.ui index 9aeb17ad..e9e99754 100644 --- a/Gui/TaskPanelPhysics.ui +++ b/Gui/TaskPanelPhysics.ui @@ -1,4 +1,4 @@ - + Form @@ -392,7 +392,7 @@ 0 - + Rotating frame (SRF) @@ -441,7 +441,7 @@ - + Reacting From 11d00b0787a4c8a030a17f3637a5481712fd5dac Mon Sep 17 00:00:00 2001 From: Jonathan Bergh Date: Sun, 31 Mar 2024 18:12:39 +0200 Subject: [PATCH 07/10] Begin adding phase physics GUI --- CfdOF/Solve/CfdPhasePhysicsSelection.py | 231 +++++++ .../TaskPanelCfdPhasePhysicsSelection.py | 261 +++++++ Gui/Icons/phasephysics.svg | 68 ++ Gui/TaskPanelPhasePhysics.ui | 651 ++++++++++++++++++ InitGui.py | 11 +- 5 files changed, 1219 insertions(+), 3 deletions(-) create mode 100644 CfdOF/Solve/CfdPhasePhysicsSelection.py create mode 100644 CfdOF/Solve/TaskPanelCfdPhasePhysicsSelection.py create mode 100644 Gui/Icons/phasephysics.svg create mode 100644 Gui/TaskPanelPhasePhysics.ui 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/TaskPanelCfdPhasePhysicsSelection.py b/CfdOF/Solve/TaskPanelCfdPhasePhysicsSelection.py new file mode 100644 index 00000000..d31169d3 --- /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', 'Interface Composition Phase Change'] +PHASE_MODELS = ['Pure Phase Model', 'Pure Isothermal Model', 'Reacting Phase Model'] + +DRAG_MODELS = ['SchillerNauman', 'IshiiZuber'] +LIFT_MODELS = ['constant', 'Tomiyama'] +SURFACE_TENSION_MODELS = ['constant'] +TURBULENT_DISPERSION_MODELS = ['Burns'] +WALL_LUBRICATION_MODELS = ['Antal'] +VIRTUAL_MASS_MODELS = ['constant'] +INTERFACE_COMPRESSION_MODELS = [''] + +HEAT_TRANSFER_MODELS = ['RanzMarshall'] +PHASE_TRANSFER_MODELS = ['constant'] + + +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/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/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/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', From 69a4fc48e6475f8f20696dc188473f2ee5be21d1 Mon Sep 17 00:00:00 2001 From: Jonathan Bergh Date: Mon, 1 Apr 2024 11:03:35 +0200 Subject: [PATCH 08/10] Update materials, update materials panel, continue implementation --- CfdOF/Solve/TaskPanelCfdFluidProperties.py | 23 +++++- .../TaskPanelCfdPhasePhysicsSelection.py | 18 ++--- CfdOF/Solve/TaskPanelCfdPhysicsSelection.py | 2 + .../AirCompressible.FCMat | 2 + .../AirIsothermal.FCMat | 2 + .../CfdFluidMaterialProperties/Gasoline.FCMat | 2 + .../OilSAE10W.FCMat | 2 + Data/CfdFluidMaterialProperties/Readme.txt | 2 +- .../CfdFluidMaterialProperties/SeaWater.FCMat | 2 + .../WaterIncompressible.FCMat | 2 + .../WaterIsothermal.FCMat | 2 + Gui/TaskPanelCfdFluidProperties.ui | 78 +++++++++++-------- 12 files changed, 90 insertions(+), 47 deletions(-) diff --git a/CfdOF/Solve/TaskPanelCfdFluidProperties.py b/CfdOF/Solve/TaskPanelCfdFluidProperties.py index 4748b6f5..d352c997 100644 --- a/CfdOF/Solve/TaskPanelCfdFluidProperties.py +++ b/CfdOF/Solve/TaskPanelCfdFluidProperties.py @@ -41,12 +41,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 +91,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 +111,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 index d31169d3..33d7d69f 100644 --- a/CfdOF/Solve/TaskPanelCfdPhasePhysicsSelection.py +++ b/CfdOF/Solve/TaskPanelCfdPhasePhysicsSelection.py @@ -26,19 +26,19 @@ from CfdOF import CfdTools from CfdOF.CfdTools import getQuantity, setQuantity, storeIfChanged -MULTIPHASE_SYSTEM = ['Basic', 'Population balance', 'Thermal Phase Change', 'Interface Composition Phase Change'] -PHASE_MODELS = ['Pure Phase Model', 'Pure Isothermal Model', 'Reacting Phase Model'] +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 = ['constant', 'Tomiyama'] -SURFACE_TENSION_MODELS = ['constant'] -TURBULENT_DISPERSION_MODELS = ['Burns'] -WALL_LUBRICATION_MODELS = ['Antal'] -VIRTUAL_MASS_MODELS = ['constant'] +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 = ['RanzMarshall'] -PHASE_TRANSFER_MODELS = ['constant'] +HEAT_TRANSFER_MODELS = ['Gunn', 'spherical', 'RanzMarshall', 'constantNu'] +PHASE_TRANSFER_MODELS = ['deposition', 'reactionDriven'] class TaskPanelCfdPhasePhysicsSelection: diff --git a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py index 42921b82..02c06403 100644 --- a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py +++ b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py @@ -126,6 +126,7 @@ 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) @@ -134,6 +135,7 @@ def updateUI(self): 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) 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/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 + + + From f5897123ba1b98030373ab6ccfc5ee49c36deb10 Mon Sep 17 00:00:00 2001 From: Jonathan Bergh Date: Mon, 1 Apr 2024 11:39:00 +0200 Subject: [PATCH 09/10] Update copyright --- CfdOF/Solve/TaskPanelCfdFluidProperties.py | 1 + 1 file changed, 1 insertion(+) diff --git a/CfdOF/Solve/TaskPanelCfdFluidProperties.py b/CfdOF/Solve/TaskPanelCfdFluidProperties.py index d352c997..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 * From 1bcb8a3fa57469ef7055f1007982120aca087a9d Mon Sep 17 00:00:00 2001 From: Jonathan Bergh Date: Mon, 1 Apr 2024 12:47:00 +0200 Subject: [PATCH 10/10] Ensure Eulerian only works with transient time discretization --- CfdOF/Solve/TaskPanelCfdPhysicsSelection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py index 02c06403..432d9505 100644 --- a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py +++ b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py @@ -131,7 +131,7 @@ def updateUI(self): 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) @@ -163,7 +163,7 @@ def updateUI(self): # Multiphase - Eulerian if self.form.radioButtonEulerian.isChecked(): - pass # Placeholder, do nothing at the moment + pass # High Mach capability self.form.checkBoxHighMach.setEnabled(not self.form.checkBoxIsothermal.isChecked())