Skip to content

Commit

Permalink
Cfd: update FenicsSolver case writing and bugfix
Browse files Browse the repository at this point in the history
  • Loading branch information
qingfengxia committed Jan 21, 2020
1 parent 476d8b4 commit c249da9
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 48 deletions.
16 changes: 10 additions & 6 deletions .travis.yml
Expand Up @@ -22,22 +22,26 @@ before_install:
# packages:
# - freecad-daily

# ubuntu 18.04 has the 4.1 in the official repository
# ubuntu 18.04 has OpenFOAM 4.1 in the official repository
install:
- sudo apt-get update -q
- if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then travis_retry sudo apt-get install -y python-matplotlib python-numpy git openfoam && pip install PyFoam; fi

- if [[ $TRAVIS_PYTHON_VERSION == '3.6' ]]; then travis_retry sudo apt-get install -y python3-matplotlib python3-numpy git openfoam && pip3 install PyFoam; fi

# this $(pwd) does not work!
- echo "current working dir is $(pwd), current user is $(id)"
- git clone https://github.com/qingfengxia/Cfd.git
# install to user home (does that exist?), simulate the addon manager
- cp -r Cfd ~/.FreeCAD/Mod/

# clone this repo has been done by Travis, and cd to <user_name>/<repo_name>
#- git clone https://github.com/qingfengxia/Cfd.git

# install to user home (/home/travis/), simulate the addon manager
- cd .. && mkdir -p ~/.FreeCAD/Mod && ls -s Cfd ~/.FreeCAD/Mod/Cfd

# testing script
script:
# test should be done in dailybuild of FreeCAD
# test should be done in daily build of FreeCAD
- echo "start test script in the current working dir of $(pwd)"
- if [ -d 'TestCfd.py' ] ; then freecadcmd-daily Cfd/TestCfd.py; fi
- if [ -d 'Cfd/TestCfd.py' ] ; then freecadcmd-daily Cfd/TestCfd.py; fi
- echo "start test script in the current working dir of $(pwd)"
- freecad-daily Cfd/test_files/test_cfd_gui.py
14 changes: 8 additions & 6 deletions cfdobjects/CaeCaseWriterFenics.py → CaeCaseWriterFenics.py
Expand Up @@ -53,6 +53,8 @@ def __init__(self, analysis_obj):
self.solver_obj = CfdTools.getSolver(analysis_obj)
self.mesh_obj = CfdTools.getMesh(analysis_obj)
self.part_obj = self.mesh_obj.Part
if not self.part_obj:
print("Error!, mesh has no Part property link to an geometry object")
self.dimension = CfdTools.getPartDimension(self.part_obj)
self.material_obj = CfdTools.getMaterial(analysis_obj)
self.bc_group = CfdTools.getConstraintGroup(analysis_obj) # not work for pure Python constraint yet
Expand All @@ -76,13 +78,13 @@ def write_solver_name(self):
if self.solver_obj.PhysicalDomain == u"Fluidic":
self.case_settings['solver_name'] = "CoupledNavierStokesSolver"
elif self.solver_obj.PhysicalDomain == u"Thermal":
self.case_settings['solver_name'] = "ScalerEquationSolver"
self.case_settings['scaler'] = "temperature"
self.case_settings['solver_name'] = "ScalarTransportSolver"
self.case_settings['scalar_name'] = "temperature"
else:
print('Error: {} solver is not not supported by Fencis and FreeCAD yet'.format(self.solver_obj.PhysicalDomain))
print('Error: {} solver is not not supported by Fenics and FreeCAD yet'.format(self.solver_obj.PhysicalDomain))

def write_case(self, updating=False):
""" Write_case() will collect case setings, and finally build a runnable case
""" Write_case() will collect case settings, and finally build a runnable case
"""

FreeCAD.Console.PrintMessage("Start to write case to folder {}\n".format(self.solver_obj.WorkingDir))
Expand Down Expand Up @@ -113,7 +115,7 @@ def write_case(self, updating=False):
with open(self.case_file_name, 'w') as fp:
json.dump(self.case_settings, fp, ensure_ascii=True, indent = 4)
#use_decimal=True, Decimal instead of float
FreeCAD.Console.PrintMessage("Sucessfully write case file {}\n".format(self.case_file_name))
FreeCAD.Console.PrintMessage("Successfully write case file {}\n".format(self.case_file_name))

os.chdir(_cwd) # restore working dir
return True
Expand All @@ -130,7 +132,7 @@ def validated(self):
def write_mesh(self):
""" This is FreeCAD specific code, convert from GMSH mesh file to 3D Fenics XML file
"""
self.console_log("Start Gmsh mesh export for Fenics solver...")
self.console_log("Start gmsh mesh export for Fenics solver...")
import importGmshMesh
error = importGmshMesh.export_fenics_mesh(self.mesh_obj, self.mesh_file_name)
'''
Expand Down
4 changes: 2 additions & 2 deletions CfdObjects.py
Expand Up @@ -25,7 +25,8 @@
__url__ = "http://www.freecadweb.org"

import FreeCAD
from cfdobjects import _CfdResult, CfdSolverFenics, CfdSolverFoam, _CaeMeshGmsh, _CaeMeshImported
from ObjectsFem import makeMaterialFluid
from cfdobjects import _CfdResult, CfdSolverFenics, CfdSolverFoam, _CaeMeshGmsh, _CaeMeshImported, _CfdFluidBoundary

def makeCfdAnalysis(name):
'''makeCfdAnalysis(name): makes a Cfd Analysis object based on Fem::FemAnalysisPython'''
Expand Down Expand Up @@ -74,7 +75,6 @@ def makeCfdMeshImported(name="ImportedCFDMesh"):

def makeCfdFluidMaterial(name="FluidMaterial"):
'''makeCfdFluidMaterial(name): makes a CFD fluid material object from FemObjects.makeFemMaterialFluid'''
from ObjectsFem import makeMaterialFluid
obj = makeMaterialFluid(FreeCAD.ActiveDocument, name)
return obj

Expand Down
25 changes: 18 additions & 7 deletions CfdRunnableFenics.py
Expand Up @@ -25,14 +25,14 @@
__url__ = "http://www.freecadweb.org"

import os.path
import sys

import FreeCAD

import CaeCaseWriterFenics
import CfdTools
from _CfdRunnable import _CfdRunnable

import FenicsSolver
from CfdConsoleProcess import CfdConsoleProcess


# Concrete Class for CfdRunnable for FenicsSolver
Expand Down Expand Up @@ -62,18 +62,29 @@ def edit_case(self):
subprocess.Popen(['explorer', path]) # check_call() will block the python code

def get_solver_cmd(self):
# get full path to solver script
#solver_script_path = os.path.dirname(__file__) + os.path.sep + "main.py"
#os.path.dirname(os.path.abspath(a_module.__file__)) to get module install path
# python3 -m FenicsSolver/main.py case_file
# see example in /usr/lib/python3.6/unittest/__init__.py

import CfdTools
solver_script_path = CfdTools.getModulePath() + os.path.sep + "FenicsSolver/main.py"
cmd = 'python2 "{}" "{}"'.format(solver_script_path, self.case_file)
solver_script_path = CfdTools.getModulePath() + os.path.sep + "FenicsSolver/FenicsSolver/main.py"
if sys.version_info.major == 2:
python_bin = "python2"
elif sys.version_info.major == 3:
python_bin = "python3"
elif sys.version_info.major == 4:
python_bin = "python4"
else:
python_bin = "python"
cmd = '{} "{}" "{}"'.format(python_bin, solver_script_path, self.case_file)
# mpirun -np {}
FreeCAD.Console.PrintMessage("Solver run command: " + cmd + "\n")
return cmd

def solve(self):
# start external process, currently not in use TODO: move code from TaskPanel to here
FenicsSolver.main.main(self.case_file)
worker = CfdConsoleProcess()
worker.start(self.get_solver_cmd())

def view_result_externally(self):
pass
Expand Down
2 changes: 1 addition & 1 deletion CfdTools.py
Expand Up @@ -204,7 +204,7 @@ def createMesh(sel):
def runGmsh(mesh_obj, anaysis_obj=None):
if not anaysis_obj:
anaysis_obj = getParentAnalysisObject(mesh_obj)
import CaeMesherGmsh
import CaeMesherGmsh # do not import at the start of this file, to be able to run without GUI
gmsh_mesh = CaeMesherGmsh.CaeMesherGmsh(mesh_obj, anaysis_obj)
error = ''
try:
Expand Down
21 changes: 12 additions & 9 deletions InitGui.py
Expand Up @@ -61,16 +61,19 @@ def Initialize(self):
#detect FreeCAD version
ver = [float(s) for s in FreeCAD.Version()[:2]]
if (ver[0]==0 and (ver[1] ==19)): # FEM module rename commands again! Jan 20, 2019
from femcommands.commands import _MeshBoundaryLayer
from femcommands.commands import _MaterialFluid
from femcommands.commands import _MeshRegion
from femcommands.commands import _MeshDisplayInfo
mesh_info_cmd_name = 'FEM_MeshDisplayInfo'
try:
from femcommands.commands import _MeshBoundaryLayer
from femcommands.commands import _MaterialFluid
from femcommands.commands import _MeshRegion
from femcommands.commands import _MeshDisplayInfo
mesh_info_cmd_name = 'FEM_MeshDisplayInfo'

FreeCADGui.addCommand("FEM_MeshBoundaryLayer", _MeshBoundaryLayer())
FreeCADGui.addCommand("FEM_MaterialFluid", _MaterialFluid())
FreeCADGui.addCommand("FEM_MeshRegion", _MeshRegion())
FreeCADGui.addCommand("mesh_info_cmd_name", _MeshDisplayInfo())
FreeCADGui.addCommand("FEM_MeshBoundaryLayer", _MeshBoundaryLayer())
FreeCADGui.addCommand("FEM_MaterialFluid", _MaterialFluid())
FreeCADGui.addCommand("FEM_MeshRegion", _MeshRegion())
FreeCADGui.addCommand("mesh_info_cmd_name", _MeshDisplayInfo())
except ImportError as e:
print("Warning: import some commands from FemWorkbench failed. \n probably import path and name change in Fem mode")

elif (ver[0]==0 and (ver[1]<=18 or ver[1]>=17)):
from femcommands.commands import _CommandFemMeshBoundaryLayer
Expand Down
44 changes: 29 additions & 15 deletions TestCfd.py
Expand Up @@ -63,6 +63,7 @@
import CfdObjects
import CfdCaseWriterFoam
import CfdRunnableFoam
import CfdRunnableFenics

home_path = FreeCAD.getHomePath()
temp_dir = tempfile.gettempdir() + '/CFD_unittests'
Expand Down Expand Up @@ -92,15 +93,16 @@ def setUp(self):
finally:
FreeCAD.setActiveDocument("CfdTest")
self.active_doc = FreeCAD.ActiveDocument
self.box = self.active_doc.addObject("Part::Box", "Box")
self.geometry_object = self.active_doc.addObject("Part::Box", "Box")
self.active_doc.recompute()

def create_new_analysis(self):
self.analysis = CfdObjects.makeCfdAnalysis('CfdAnalysis')
self.active_doc.recompute()

def create_new_solver(self):
self.solver_object = CfdObjects.makeCfdSolver('OpenFOAM')
def create_new_solver(self, solverName='OpenFOAM'):
self.solver_name = solverName
self.solver_object = CfdObjects.makeCfdSolver(solverName)
self.solver_object.WorkingDir = cfd_analysis_dir
self.solver_object.InputCaseName = case_name
self.active_doc.recompute()
Expand All @@ -116,7 +118,7 @@ def create_new_material(self):

def create_new_mesh(self):
self.mesh_object = CfdObjects.makeCfdMeshGmsh()
error = CfdTools.runGmsh(self.mesh_object)
error = CfdTools.runGmsh(self.mesh_object) # todo: rename to runMesher()
return error

def import_mesh(self, mesh_file):
Expand All @@ -127,30 +129,36 @@ def import_mesh(self, mesh_file):

def create_wall_constraint(self):
self.wall_constraint = self.active_doc.addObject("Fem::ConstraintFluidBoundary", "wall")
self.wall_constraint.References = [(self.box, "Face1")]
self.wall_constraint.References = [(self.geometry_object, "Face1")]
self.wall_constraint.BoundaryType = 'wall'
self.wall_constraint.Subtype = 'fixed'
self.wall_constraint.BoundaryValue = 0

def create_velocity_inlet_constraint(self):
self.velocity_inlet_constraint = self.active_doc.addObject("Fem::ConstraintFluidBoundary", "velocity_inlet")
self.velocity_inlet_constraint.References = [(self.box, "Face6")]
self.velocity_inlet_constraint.References = [(self.geometry_object, "Face6")]
self.velocity_inlet_constraint.BoundaryType = 'inlet'
self.velocity_inlet_constraint.Subtype = 'uniformVelocity'
self.velocity_inlet_constraint.BoundaryValue = 0.01
#self.velocity_inlet_constraint.Direction = (self.box, ["Edge5"])
#self.velocity_inlet_constraint.Direction = (self.geometry_object, ["Edge5"])
#self.velocity_inlet_constraint.Reversed = False

def create_pressure_outlet_constraint(self):
self.pressure_outlet_constraint = self.active_doc.addObject("Fem::ConstraintFluidBoundary", "pressure_outlet")
self.pressure_outlet_constraint.References = [(self.box, "Face2")]
self.pressure_outlet_constraint.References = [(self.geometry_object, "Face2")]
self.pressure_outlet_constraint.BoundaryType = 'outlet'
self.pressure_outlet_constraint.Subtype = 'totalPressure'
self.pressure_outlet_constraint.BoundaryValue = 0
#self.pressure_outlet_constraint.Reversed = True

def create_cfd_runnable(self):
self.runnable_object = CfdRunnableFoam.CfdRunnableFoam(self.solver_object)
if self.solver_name == "OpenFOAM":
self.runnable_object = CfdRunnableFoam.CfdRunnableFoam(self.solver_object)
elif self.solver_name == "Fenics":
self.runnable_object = CfdRunnableFenics.CfdRunnableFenics(self.solver_object)
else:
self.runnable_object = None
print("Error: solver {} is not suported, check spelling, OpenFOAM/Fenics". format(self.solver_name))

def run_cfd_simulation(self):
pass # it takes too long to finish, skip it in unit test
Expand All @@ -163,22 +171,24 @@ def load_cfd_result(self):
def save_file(self, fc_file_name):
self.active_doc.saveAs(fc_file_name)

def test_new_analysis(self):
def test_new_analysis(self, solverName='OpenFOAM'):
# static
fcc_print('--------------- Start of Cfd tests ---------------')
fcc_print('Checking Cfd new analysis...')
self.create_new_analysis()
self.assertTrue(self.analysis, "CfdTest of new analysis failed")

fcc_print('Checking Cfd new solver...')
self.create_new_solver()
self.create_new_solver(solverName)
self.assertTrue(self.solver_object, "CfdTest of new solver failed")
self.analysis.addObject(self.solver_object)

fcc_print('Checking Cfd new mesh...')
self.create_new_mesh()
self.create_new_mesh() # call runGmsh()
self.assertTrue(self.mesh_object, "CfdTest of new mesh failed")
self.analysis.addObject(self.mesh_object)
self.mesh_object.Part = self.geometry_object # very important!


fcc_print('Checking Cfd new material...')
self.create_new_material()
Expand Down Expand Up @@ -215,25 +225,29 @@ def test_new_analysis(self):
fcc_print('Checking Cfd case file write...')
self.create_cfd_runnable()
successful = self.runnable_object.write_case()
self.assertTrue(successful, "Writing CFD cae failed")
self.assertTrue(successful, "Writing CFD case failed")

# todo: compare to confirm case writting and solution correctness

fcc_print('Save FreeCAD file for CFD analysis to {}...'.format(cfd_save_fc_file))
self.save_file(cfd_save_fc_file)
self.assertTrue(self.save_file, "CfdTest saving of file {} failed ...".format(cfd_save_fc_file))

fcc_print('--------------- End of Cfd tests incompressible flow analysis ---------------')
fcc_print('--------------- End of Cfd tests incompressible flow analysis case writing ---------------')

def tearDown(self):
FreeCAD.closeDocument("CfdTest")

# to run in FreeCAD editor
t = CfdTest()
t.setUp()
t.test_new_analysis()
t.test_new_analysis("Fenics")
t.tearDown()

t = CfdTest()
t.setUp()
t.test_new_analysis()
t.tearDown()
# run without FreeCAD mode, not feasible for the time being
#if __name__ == '__main__':
# unittest.main()
4 changes: 2 additions & 2 deletions importGmshMesh.py
Expand Up @@ -129,8 +129,8 @@ def export_fenics_mesh(obj, meshfileString):
FreeCAD.Console.PrintError(error)
return error
meshfileStem = (meshfileString[:-4])
if isinstance(meshfileStem, (type(u"string"),)):
meshfileStem = meshfileStem.encode('ascii')
if isinstance(meshfileStem, (type(b"bytes type"),)):
meshfileStem = meshfileStem.decode('utf8')

gmsh = CaeMesherGmsh.CaeMesherGmsh(obj, CfdTools.getParentAnalysisObject(obj))
meshfile = gmsh.export_mesh(u"Gmsh MSH", meshfileStem + u".msh")
Expand Down
3 changes: 3 additions & 0 deletions test_files/test_cfd_gui.py
Expand Up @@ -38,6 +38,7 @@
CfdObjects.makeCfdAnalysis('CfdAnalysis')
FemGui.setActiveAnalysis(App.activeDocument().ActiveObject)
FemGui.getActiveAnalysis().addObject(CfdObjects.makeCfdSolver('OpenFOAM'))
FemGui.getActiveAnalysis().addObject(CfdObjects.makeCfdSolver('Fenics'))
FemGui.getActiveAnalysis().addObject(CfdObjects.makeCfdFluidMaterial('FluidMaterial'))

mesh_obj = CfdObjects.makeCfdMeshGmsh('Cylinder_Mesh')
Expand All @@ -52,6 +53,8 @@
gmsh_mesh = CaeMesherGmsh(mesh_obj, FemGui.getActiveAnalysis())
error = gmsh_mesh.create_mesh()

# Runnable

####################end of copy ###################
#Gui.getMainWindow().close() #still wait for user to confirm save not discard
#App.ActiveDocument.Name
Expand Down

0 comments on commit c249da9

Please sign in to comment.