Skip to content

Commit

Permalink
Enhancement/icepak fluent (#650)
Browse files Browse the repository at this point in the history
* added method to retrieve gas and liquids.
added method to generate fluent mesh and apply to Icepak

* Added generate_fluent_mesh method

* Added generate_fluent_mesh method

* Add png for Spiral example

* Fixing Style check

* Fixing Style check

* Fixing Style check

* Fixing Style check

* Fixed style errors

* Update pyaedt/modules/MaterialLib.py

* Update pyaedt/modules/MeshIcepak.py

* Apply suggestions from code review

Co-authored-by: Maxime Rey <87315832+MaxJPRey@users.noreply.github.com>

* First Implementation

Co-authored-by: Maxime Rey <87315832+MaxJPRey@users.noreply.github.com>
  • Loading branch information
maxcapodi78 and MaxJPRey authored Dec 15, 2021
1 parent ae9f4ed commit 41ca8db
Show file tree
Hide file tree
Showing 9 changed files with 256 additions and 19 deletions.
12 changes: 11 additions & 1 deletion _unittest/test_98_Icepak.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from pyaedt.generic.filesystem import Scratch

# Setup paths for module imports
from _unittest.conftest import local_path, scratch_path, desktop_version
from _unittest.conftest import local_path, scratch_path, desktop_version, config

try:
import pytest # noqa: F401
Expand All @@ -32,6 +32,7 @@
src_project_name = "USB_Connector_IPK"
source_project = os.path.join(local_path, "example_models", src_project_name + ".aedt")
source_project_path = os.path.join(local_path, "example_models", src_project_name)
source_fluent = os.path.join(local_path, "example_models", "ColdPlateExample.aedt")


class TestClass:
Expand All @@ -45,6 +46,7 @@ def setup_class(self):

self.test_project = self.local_scratch.copyfile(example_project)
self.test_src_project = self.local_scratch.copyfile(source_project)

self.local_scratch.copyfolder(
os.path.join(local_path, "example_models", test_project_name + ".aedb"),
os.path.join(self.local_scratch.path, test_project_name + ".aedb"),
Expand Down Expand Up @@ -380,3 +382,11 @@ def test_89_check_bounding_box(self):
exp_bounding = [0.2, 0.2, 0.2, 0.5, 0.6, 0.4]
real_bound = obj_2_bbox
assert abs(sum([i - j for i, j in zip(exp_bounding, real_bound)])) < tol

@pytest.mark.skipif(config["build_machine"], reason="Needs Workbench to run.")
def test_90_export_fluent_mesh(self):
self.fluent = self.local_scratch.copyfile(source_fluent)
app = Icepak(self.fluent)
assert app.get_liquid_objects() == ["Liquid"]
assert app.get_gas_objects() == ["Region"]
assert app.generate_fluent_mesh()
Binary file added doc/source/Resources/spiral.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 9 additions & 4 deletions pyaedt/application/Analysis3D.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,12 @@ def export_3d_model(self, fileName, filePath, fileFormat=".step", object_list=[]
allObjects = object_list[:]

self.logger.info("Exporting {} objects".format(len(allObjects)))

major = -1
minor = -1
# actual version supported by AEDT is 29.0
if fileFormat in [".step", ".stp", ".sm3", ".sat", ".sab"]:
major = 29
minor = 0
stringa = ",".join(allObjects)
arg = [
"NAME:ExportParameters",
Expand All @@ -441,11 +446,11 @@ def export_3d_model(self, fileName, filePath, fileFormat=".step", object_list=[]
"Selections:=",
stringa,
"File Name:=",
str(filePath) + "/" + str(fileName) + str(fileFormat),
os.path.join(filePath, fileName + fileFormat),
"Major Version:=",
-1,
major,
"Minor Version:=",
-1,
minor,
]

self.modeler.oeditor.Export(arg)
Expand Down
14 changes: 10 additions & 4 deletions pyaedt/application/AnalysisIcepak.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import csv
import re
import os

from pyaedt.generic.general_methods import aedt_exception_handler, generate_unique_name, is_ironpython
from pyaedt.application.Analysis import Analysis
Expand Down Expand Up @@ -253,7 +254,12 @@ def export_3d_model(self, fileName, filePath, fileFormat=".step", object_list=[]
allObjects = object_list[:]

self.logger.info("Exporting {} objects".format(len(allObjects)))

major = -1
minor = -1
# actual version supported by AEDT is 29.0
if fileFormat in [".step", ".stp", ".sm3", ".sat", ".sab"]:
major = 29
minor = 0
stringa = ",".join(allObjects)
arg = [
"NAME:ExportParameters",
Expand All @@ -264,11 +270,11 @@ def export_3d_model(self, fileName, filePath, fileFormat=".step", object_list=[]
"Selections:=",
stringa,
"File Name:=",
str(filePath) + "/" + str(fileName) + str(fileFormat),
os.path.join(filePath, fileName + fileFormat),
"Major Version:=",
-1,
major,
"Minor Version:=",
-1,
minor,
]

self.modeler.oeditor.Export(arg)
Expand Down
148 changes: 148 additions & 0 deletions pyaedt/icepak.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,17 @@
import math
import os
import re
import warnings
from collections import OrderedDict

if os.name == "posix":
try:
import subprocessdotnet as subprocess
except:
warnings.warn("Pythonnet is needed to run pyaedt within Linux")
else:
import subprocess

from pyaedt.application.AnalysisIcepak import FieldAnalysisIcepak
from pyaedt.generic.general_methods import generate_unique_name, aedt_exception_handler
from pyaedt.generic.DataHandlers import _arg2dict
Expand Down Expand Up @@ -2331,3 +2340,142 @@ def delete_pcb_component(self, comp_name):

self.modeler.oeditor.Delete(arg)
return True

@aedt_exception_handler
def get_liquid_objects(self):
"""Return the liquid materials objects.
Returns
-------
list
List of objects names
"""
mats = []
for el in self.materials.liquids:
mats.extend(self.modeler.convert_to_selections(self.modeler.get_objects_by_material(el), True))
return mats

@aedt_exception_handler
def get_gas_objects(self):
"""Return the gas materials objects.
Returns
-------
list
List of all Gas objects.
"""
mats = []
for el in self.materials.gases:
mats.extend(self.modeler.convert_to_selections(self.modeler.get_objects_by_material(el), True))
return mats

@aedt_exception_handler
def generate_fluent_mesh(self, object_lists=None):
"""Generate a Fluent Mesh for selected objects list and assign it automatically to the objects.
Parameters
----------
object_lists : list, optional
Lis of objects on which compute Fluent Mesh. If `None` mesh will be done on fluids objects.
Returns
-------
:class:`pyaedt.modules.Mesh.MeshOperation`
"""
version = self.aedt_version_id[-3:]
ansys_install_dir = os.environ.get("ANSYS{}_DIR".format(version), "")
if not ansys_install_dir:
ansys_install_dir = os.environ.get("AWP_ROOT{}".format(version), "")
assert ansys_install_dir, "Fluent {} has to be installed on to generate mesh.".format(version)
assert os.getenv("ANSYS{}_DIR".format(version))
if not object_lists:
object_lists = self.get_liquid_objects()
assert object_lists, "No Fluids objects found."
object_lists = self.modeler.convert_to_selections(object_lists, True)
file_name = self.project_name
sab_file_pointer = os.path.join(self.project_path, file_name + ".sab")
mesh_file_pointer = os.path.join(self.project_path, file_name + ".msh")
fl_uscript_file_pointer = os.path.join(self.project_path, "FLUscript.jou")
if os.path.exists(mesh_file_pointer):
os.remove(mesh_file_pointer)
if os.path.exists(sab_file_pointer):
os.remove(sab_file_pointer)
if os.path.exists(fl_uscript_file_pointer):
os.remove(fl_uscript_file_pointer)
if os.path.exists(mesh_file_pointer + ".trn"):
os.remove(mesh_file_pointer + ".trn")
assert self.export_3d_model(file_name, self.project_path, ".sab", object_lists), "Failed to export .sab"

# Building Fluent journal script file *.jou
fluent_script = open(fl_uscript_file_pointer, "w")
fluent_script.write("/file/start-transcript " + '"' + mesh_file_pointer + '.trn"\n')
fluent_script.write(
'/file/set-tui-version "{}"\n'.format(self.aedt_version_id[-3:-1] + "." + self.aedt_version_id[-1:])
)
fluent_script.write("(enable-feature 'serial-hexcore-without-poly)\n")
fluent_script.write('(cx-gui-do cx-activate-tab-index "NavigationPane*Frame1(TreeTab)" 0)\n')
fluent_script.write("(%py-exec \"workflow.InitializeWorkflow(WorkflowType=r'Watertight Geometry')\")\n")
cmd = "(%py-exec \"workflow.TaskObject['Import Geometry']."
cmd += "Arguments.setState({r'FileName': r'" + sab_file_pointer + "',})\")\n"
fluent_script.write(cmd)
fluent_script.write("(%py-exec \"workflow.TaskObject['Import Geometry'].Execute()\")\n")
fluent_script.write("(%py-exec \"workflow.TaskObject['Add Local Sizing'].AddChildToTask()\")\n")
fluent_script.write("(%py-exec \"workflow.TaskObject['Add Local Sizing'].Execute()\")\n")
fluent_script.write("(%py-exec \"workflow.TaskObject['Generate the Surface Mesh'].Execute()\")\n")
cmd = "(%py-exec \"workflow.TaskObject['Describe Geometry'].UpdateChildTasks(SetupTypeChanged=False)\")\n"
fluent_script.write(cmd)
cmd = "(%py-exec \"workflow.TaskObject['Describe Geometry']."
cmd += "Arguments.setState({r'SetupType': r'The geometry consists of only fluid regions with no voids',})\")\n"
fluent_script.write(cmd)
cmd = "(%py-exec \"workflow.TaskObject['Describe Geometry'].UpdateChildTasks(SetupTypeChanged=True)\")\n"
fluent_script.write(cmd)
cmd = "(%py-exec \"workflow.TaskObject['Describe Geometry'].Arguments.setState({r'InvokeShareTopology': r'Yes',"
cmd += "r'SetupType': r'The geometry consists of only fluid regions with no voids',r'WallToInternal': "
cmd += "r'Yes',})\")\n"
fluent_script.write(cmd)
cmd = "(%py-exec \"workflow.TaskObject['Describe Geometry'].UpdateChildTasks(SetupTypeChanged=False)\")\n"
fluent_script.write(cmd)
fluent_script.write("(%py-exec \"workflow.TaskObject['Describe Geometry'].Execute()\")\n")
fluent_script.write("(%py-exec \"workflow.TaskObject['Apply Share Topology'].Execute()\")\n")
fluent_script.write("(%py-exec \"workflow.TaskObject['Update Boundaries'].Execute()\")\n")
fluent_script.write("(%py-exec \"workflow.TaskObject['Update Regions'].Execute()\")\n")
fluent_script.write("(%py-exec \"workflow.TaskObject['Add Boundary Layers'].AddChildToTask()\")\n")
fluent_script.write("(%py-exec \"workflow.TaskObject['Add Boundary Layers'].InsertCompoundChildTask()\")\n")
cmd = "(%py-exec \"workflow.TaskObject['smooth-transition_1']."
cmd += "Arguments.setState({r'BLControlName': r'smooth-transition_1',})\")\n"
fluent_script.write(cmd)
fluent_script.write("(%py-exec \"workflow.TaskObject['Add Boundary Layers'].Arguments.setState({})\")\n")
fluent_script.write("(%py-exec \"workflow.TaskObject['smooth-transition_1'].Execute()\")\n")
# r'VolumeFill': r'hexcore' / r'tetrahedral'
cmd = "(%py-exec \"workflow.TaskObject['Generate the Volume Mesh'].Arguments.setState({r'VolumeFill': "
cmd += "r'hexcore', r'VolumeMeshPreferences': {r'MergeBodyLabels': r'yes',},})\")\n"
fluent_script.write(cmd)
fluent_script.write("(%py-exec \"workflow.TaskObject['Generate the Volume Mesh'].Execute()\")\n")
fluent_script.write("/file/hdf no\n")
fluent_script.write('/file/write-mesh "' + mesh_file_pointer + '"\n')
fluent_script.write("/file/stop-transcript\n")
fluent_script.write("/exit,\n")
fluent_script.close()

# Fluent command line parameters: -meshing -i <journal> -hidden -tm<x> (# processors for meshing) -wait
fl_ucommand = [
os.path.join(self.desktop_install_dir, "fluent", "ntbin", "win64", "fluent.exe"),
"3d",
"-meshing",
"-hidden",
"-i" + '"' + fl_uscript_file_pointer + '"',
]
self.logger.info("Fluent will be started in BG!")
subprocess.call(fl_ucommand)
if os.path.exists(mesh_file_pointer + ".trn"):
os.remove(mesh_file_pointer + ".trn")
if os.path.exists(fl_uscript_file_pointer):
os.remove(fl_uscript_file_pointer)
if os.path.exists(sab_file_pointer):
os.remove(sab_file_pointer)
if os.path.exists(mesh_file_pointer):
self.logger.info("'" + mesh_file_pointer + "' has been created.")
return self.mesh.assign_mesh_from_file(object_lists, mesh_file_pointer)
self.logger.error("Failed to create msh file")

return False
30 changes: 30 additions & 0 deletions pyaedt/modules/MaterialLib.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,36 @@ def __getitem__(self, item):
elif item in list(self.surface_material_keys.keys()):
return self.surface_material_keys[item]

@property
def liquids(self):
"""Return the liquids materials. A liquid is a fluid with density greater or equal to 100Kg/m3.
Returns
-------
list
List of fluid materials.
"""
mats = []
for el, val in self.material_keys.items():
if val.thermal_material_type == "Fluid" and val.mass_density.value and float(val.mass_density.value) >= 100:
mats.append(el)
return mats

@property
def gases(self):
"""Return the gas materials. A gas is a fluid with density lower than 100Kg/m3.
Returns
-------
list
List of all Gas materials.
"""
mats = []
for el, val in self.material_keys.items():
if val.thermal_material_type == "Fluid" and val.mass_density.value and float(val.mass_density.value) < 100:
mats.append(el)
return mats

@aedt_exception_handler
def _get_materials(self):
"""Get materials."""
Expand Down
5 changes: 3 additions & 2 deletions pyaedt/modules/Mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ def create(self):
Returns
-------
type
bool
``True`` when successful, ``False`` when failed.
"""
if self.type == "SurfApproxBased":
Expand All @@ -78,9 +79,9 @@ def create(self):
self._meshicepak.omeshmodule.AssignMeshOperation(self._get_args())
elif self.type == "CurvatureExtraction":
self._meshicepak.omeshmodule.AssignCurvatureExtractionOp(self._get_args())

else:
return False
return True

@aedt_exception_handler
def update(self):
Expand Down
46 changes: 45 additions & 1 deletion pyaedt/modules/MeshIcepak.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def assign_mesh_level(self, mesh_order, meshop_name=None):
Returns
-------
bool
list of :class:`pyaedt.modules.Mesh.MeshOperation`
``True`` when successful, ``False`` when failed.
References
Expand All @@ -306,6 +306,50 @@ def assign_mesh_level(self, mesh_order, meshop_name=None):
list_meshops.append(meshop_name)
return list_meshops

@aedt_exception_handler
def assign_mesh_from_file(self, objects, filename, meshop_name=None):
"""Assign a mesh from file to objects.
Parameters
----------
objects : list
List of objects to which apply the mesh file.
filename : str
Full path to .msh file.
meshop_name : str, optional
Name of the mesh operations. Default is ``None``.
Returns
-------
:class:`pyaedt.modules.Mesh.MeshOperation`
Mesh Operation object. ``False`` when failed.
References
----------
>>> oModule.AssignMeshOperation
"""
objs = self._app.modeler.convert_to_selections(objects, True)
if meshop_name:
meshop_name = generate_unique_name("MeshFile")
else:
meshop_name = generate_unique_name("MeshFile")
props = OrderedDict({"Enable": True, "MaxLevel": str(0), "MinLevel": str(0), "Objects": objs})
props["Local Mesh Parameters Enabled"] = False
props["Mesh Reuse Enabled"] = True
props["Mesh Reuse File"] = filename
props["Local Mesh Parameters Type"] = "3DPolygon Local Mesh Parameters"
props["Height count"] = "0"
props["Top height"] = "0mm"
props["Top ratio"] = "0"
props["Bottom height"] = "0mm"
props["Bottom ratio"] = "0"
mop = MeshOperation(self, meshop_name, props, "Icepak")
if mop.create():
self.meshoperations.append(mop)
return mop
return False

@aedt_exception_handler
def automatic_mesh_pcb(self, accuracy=2):
"""Create a custom mesh tailored on a PCB design.
Expand Down
Loading

0 comments on commit 41ca8db

Please sign in to comment.