Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VTFx export from fedempy.fmm_solver #25

Merged
merged 9 commits into from
Feb 8, 2025
2 changes: 1 addition & 1 deletion .github/workflows/run_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
run: cmake --build ./pFUnit3-build --target install

- name: Install required python packages
run: pip install numpy pandas PyYAML
run: pip install numpy pandas progress PyYAML

- name: Configure main build
run: >
Expand Down
1 change: 1 addition & 0 deletions PythonAPI/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ python-dateutil==2.8.2
pytz==2022.7.1
six==1.16.0
types-PyYAML==6.0.12.8
progress==1.6
42 changes: 26 additions & 16 deletions PythonAPI/src/fedempy/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class Exporter:
Absolute path to the shared object library vtfxExporter.so
vtfx_path : str, default=None
Absolute path to vtfx-file, use a temporary file if None
case_name : str, default="Case"
Case identifier

Methods
-------
Expand All @@ -54,13 +56,13 @@ class Exporter:
Converts temporary generated vtfx-file to CUG database and cleans up
"""

def __init__(self, fe_parts, vis_parts, lib_path, vtfx_path=None):
def __init__(self, fe_parts, vis_parts, lib_path, vtfx_path=None, case_name="Case"):
"""
Constructor.
Initializes the internal datastructure of the shared object library,
and loads the finite element and visualization parts into memory.
"""
self._initialize(lib_path, vtfx_path)
self._initialize(lib_path, vtfx_path, None, case_name)
self._fem_parts = []
self._vis_parts = []

Expand Down Expand Up @@ -135,48 +137,52 @@ def do_step(self, time, transformation_in, deformation_in, stress_in):
if rc != 0:
raise ExporterException(rc)

def clean(self, time_step, lib_dir, out_dir=None, cug_path="/usr/bin/CugComposer"):
def clean(self, time_step, lib_dir=None, out_dir=None, fmax=-1.0):
"""
Clears all data about included FE-parts and frs-files,
and removes all transformation, stress and deformation results.
Closes the vtfx-file and converts it to a CUG database.

Parameters
----------
time_step : double
time_step : float
Time step to use for animation speed
lib_dir : str
Absolute path to app location
lib_dir : str, default None
Absolute path for folder where to write CUG log-file
If None, current working directory will be used
out_dir : str, default=None
Absolute path for output folder of the CUG database
If None, CUG data is not generated and the vtfx-file is retained
cug_path : str, default="/usr/bin/CugComposer"
Absolute path to Ceetron CUG composer executable
fmax : float, default=-1
Set fringe range to [0,fmax] if fmax is larger than 0.
If less than zero, the range will be set automatically.
"""
print(" * Writing VTFX-file", self.vtfx_file_path, flush=True)
rc = self.lib_exporter.finish(c_double(time_step))
if rc == 0 and out_dir and path.isfile(cug_path):
print(" * Writing VTFX-file", self.vtfx_path, flush=True)
rc = self.lib_exporter.finish(c_double(time_step), c_double(0), c_double(fmax))
if rc == 0 and out_dir and path.isfile(self.cug_path):
logfile = lib_dir + "/Cug.log" if path.isdir(lib_dir) else "./Cug.log"
with open(logfile, "w") as outfile:
print(" * Creating CUG database in", out_dir, flush=True)
rc = run(
[cug_path, self.vtfx_file_path, out_dir], check=True, stdout=outfile
[self.cug_path, self.vtfx_path, out_dir], check=True, stdout=outfile
).returncode

if rc != 0:
print(f" *** Failed ({rc})")
elif out_dir:
remove(self.vtfx_file_path)
remove(self.vtfx_path)

def _initialize(self, lib_path, vtfx_path):
def _initialize(self, lib_path, vtfx_path, cug_path, case_name):
"""
Initialization
"""
self.lib_exporter = cdll.LoadLibrary(lib_path)
self.lib_exporter.initialize(c_bool(False))
self.vtfx_file_path = "/tmp/temp.vtfx" if vtfx_path is None else vtfx_path
self.vtfx_path = "/tmp/temp.vtfx" if vtfx_path is None else vtfx_path
self.cug_path = "/usr/bin/CugComposer" if cug_path is None else cug_path
rc = self.lib_exporter.setVtfxPath(
c_char_p(self.vtfx_file_path.encode("utf-8"))
c_char_p(self.vtfx_path.encode("utf-8")),
c_char_p(case_name.encode("utf-8")),
)
if rc != 0:
raise ExporterException(rc)
Expand Down Expand Up @@ -214,6 +220,8 @@ def _add_fe_part(
c_bool(surface_only),
c_bool(include_spiders),
c_float(1.0),
c_float(1.0),
None,
)
if rc != 0:
raise ExporterException(rc)
Expand Down Expand Up @@ -242,6 +250,8 @@ def _add_visualization_part(self, file_path, part_name, base_id):
c_char_p(part_name),
c_int(base_id),
c_float(1.0),
c_float(1.0),
None,
)
if rc != 0:
raise ExporterException(rc)
Expand Down
29 changes: 28 additions & 1 deletion PythonAPI/src/fedempy/fmm.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
Used for convenience in order to hide native type convertions.
"""

from ctypes import c_bool, c_char_p, c_double, c_int, cdll, create_string_buffer
from ctypes import byref, c_bool, c_char_p, c_double, c_int, cdll, create_string_buffer

from fedempy.enums import Enum, FmType

Expand Down Expand Up @@ -141,6 +141,7 @@ def __init__(self, lib_path, plugin1=None, plugin2=None):
self._fmlib.FmTagObjects.restype = c_int
self._fmlib.FmGetPosition.restype = c_bool
self._fmlib.FmGetNode.restype = c_int
self._fmlib.FmGetFEModel.restype = c_bool
self._fmlib.FmSync.restype = c_bool
self._fmlib.FmSync.argstype = [c_int]
self._fmlib.FmReduce.restype = c_bool
Expand Down Expand Up @@ -368,6 +369,32 @@ def fm_get_node(self, part_id, pos):
pos_[:] = pos[0:3]
return self._fmlib.FmGetNode(self._convert_id(part_id), pos_)

def fm_get_femodel(self, base_id):
"""
This method returns the FE data file for the specified Part.

Parameters
----------
base_id : int
Base Id of the FE part to get the FE model for

Returns
-------
str
Absolute path to the FE data file
int
If less than -1: No file is associated with this part.
If -1: This is a generic part with a visualization file.
If 0: This is a FE part but not subjected to stress recovery.
If 1: This FE part is subjected to stress recovery.
"""
femfile = create_string_buffer(512)
recover = c_int(0)
if self._fmlib.FmGetFEModel(_convert_int(base_id), femfile, byref(recover)):
return femfile.value.decode("utf-8"), recover.value

return "", -2

def fm_sync_part(self, base_id):
"""
This method syncronizes the currently open Fedem model with contents
Expand Down
Loading