Skip to content

Commit

Permalink
fluent:
Browse files Browse the repository at this point in the history
 - renaming fluent_model.element_id to fluent_model.result_element_id; element_id is now a property
 - fixing case where properties are not restricted
  • Loading branch information
Steve Doyle committed Dec 4, 2024
1 parent da4b064 commit 265afa7
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 53 deletions.
79 changes: 48 additions & 31 deletions pyNastran/converters/fluent/fluent.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,28 +37,32 @@ def __init__(self, auto_read_write_h5: bool=True,

# daten
self.titles = np.zeros((0, ), dtype='|U8')
self.element_id = np.array([], dtype='int32')
self.result_element_id = np.array([], dtype='int32')
self.results = np.zeros((0, 0), dtype='float64')

# TODO: may be removed
self.element_ids = np.array([], dtype='int32')

@property
def element_id(self) -> np.ndarray:
return self.result_element_id

@classmethod
def from_data(self,
node_id: np.ndarray, xyz: np.ndarray,
tris: np.ndarray, quads: np.ndarray,
element_id: np.ndarray, titles: np.ndarray,
result_element_id: np.ndarray, titles: np.ndarray,
quad_results: np.ndarray, tri_results: np.ndarray,
auto_read_write_h5: bool=True,
log=None, debug: bool=True):
model = Fluent(
auto_read_write_h5=auto_read_write_h5,
log=log, debug=debug)
assert len(node_id) > 0
assert len(element_id) > 0
assert len(result_element_id) > 0
model.node_id = node_id
model.xyz = xyz
model.element_id = element_id
model.result_element_id = result_element_id
model.quads = quads
model.tris = tris
model.titles = titles
Expand Down Expand Up @@ -88,7 +92,8 @@ def write_h5(self, h5_filename: PathLike='',
h5_filename = self._get_h5_filename(h5_filename)

h5file = tables.open_file(h5_filename, 'w', driver='H5FD_CORE')
names = ['node_id', 'xyz', 'element_id', 'titles', 'results',
names = ['node_id', 'xyz',
'titles', 'results', 'result_element_id', #'element_id',
'quads', 'tris', 'element_ids']
h5file.create_array(h5file.root, 'format', ['fluent'])
for name in names:
Expand All @@ -110,7 +115,8 @@ def read_h5(self, h5_filename: PathLike='',
raise
return is_loaded
names = [
'node_id', 'xyz', 'element_id', 'titles', 'results',
'node_id', 'xyz',
'titles', 'results', 'result_element_id', #'element_id',
'quads', 'tris', 'element_ids',
]
h5_filename = self._get_h5_filename(h5_filename)
Expand All @@ -137,7 +143,7 @@ def read_h5(self, h5_filename: PathLike='',
else:
setattr(self, name, datai)
h5file.close()
assert len(self.element_id) > 0, self.element_id
assert len(self.result_element_id) > 0, self.result_element_id
assert len(self.element_ids) > 0, self.element_ids
is_loaded = True
return is_loaded
Expand All @@ -151,6 +157,7 @@ def read_fluent(self, fluent_filename: PathLike) -> None:
self.fluent_filename = fluent_filename
if self.auto_read_write_h5 and os.path.exists(h5_filename):
# fails if pytables isn't installed
self.log.debug(f'reading fluent {h5_filename}')
is_loaded = self.read_h5(h5_filename, require_load=False)
if is_loaded:
return
Expand All @@ -160,12 +167,12 @@ def read_fluent(self, fluent_filename: PathLike) -> None:

(quads, tris), element_ids = read_cell(cell_filename)
node, xyz = read_vrt(vrt_filename)
element_id, titles, results = read_daten(daten_filename, scale=1.0)
result_element_id, titles, results = read_daten(daten_filename, scale=1.0)

self.node_id = node
self.xyz = xyz
self.element_id = element_id # result element ids

self.result_element_id = result_element_id
self.titles = titles
self.results = results

Expand All @@ -180,7 +187,7 @@ def read_fluent(self, fluent_filename: PathLike) -> None:
# respects interspersed quads/tris
self.element_ids = element_ids # TODO: consider removing

assert len(element_id) > 0, element_id
assert len(result_element_id) > 0, result_element_id
assert len(element_ids) > 0, element_ids
if self.auto_read_write_h5:
# fails if pytables isn't installed
Expand All @@ -191,7 +198,7 @@ def write_fluent(self, fluent_filename: str) -> None:
assert len(self.node_id) == len(self.xyz)
assert self.tris.shape[1] == 5, self.tris.shape
assert self.quads.shape[1] == 6, self.quads.shape
assert len(self.element_id) > 0, self.element_id
assert len(self.result_element_id) > 0, self.result_element_id

base, ext = os.path.splitext(fluent_filename)
vrt_filename = base + '.vrt'
Expand All @@ -200,24 +207,24 @@ def write_fluent(self, fluent_filename: str) -> None:

#(quads, tris), (element_ids, region)
# node, xyz = read_vrt(vrt_filename)
# element_id, titles, results = read_daten(daten_filename, scale=1.0)
# result_element_id, titles, results = read_daten(daten_filename, scale=1.0)
write_cell(cell_filename, self.quads, self.tris)
write_vrt(vrt_filename, self.node_id, self.xyz)

elements_list = []
results_elements_list = []
results_list = []
if len(self.tris):
itri = np.searchsorted(self.element_id, self.tris[:, 0])
elements_list.append(self.element_id[itri])
itri = np.searchsorted(self.result_element_id, self.tris[:, 0])
results_elements_list.append(self.result_element_id[itri])
results_list.append(self.results[itri, :])

if len(self.quads):
iquad = np.searchsorted(self.element_id, self.quads[:, 0])
elements_list.append(self.element_id[iquad])
iquad = np.searchsorted(self.result_element_id, self.quads[:, 0])
results_elements_list.append(self.result_element_id[iquad])
results_list.append(self.results[iquad, :])
element_id = np.hstack(elements_list)
result_element_id = np.hstack(results_elements_list)
results = np.vstack(results_list)
write_daten(daten_filename, element_id, self.titles, results)
write_daten(daten_filename, result_element_id, self.titles, results)

def get_area_centroid_normal(self, tris: np.ndarray,
quads: np.ndarray) -> tuple[np.ndarray, np.ndarray,
Expand Down Expand Up @@ -279,54 +286,64 @@ def get_filtered_data(self,
#deepcopy: bool=True,
) -> tuple[np.ndarray, np.ndarray, np.ndarray,
np.ndarray, np.ndarray]:
assert np.array_equal(self.result_element_id, np.unique(self.result_element_id))
if regions_to_remove is None:
regions_to_remove = []
if regions_to_include is None:
regions_to_include = []
region_split = bool(len(regions_to_remove) + len(regions_to_include))
assert len(self.element_id) > 0
assert len(self.result_element_id) > 0
if region_split:
self.log.info(f'regions_to_remove={regions_to_remove}; '
f'regions_to_include={regions_to_include}')
element_id, tris, quads, quad_results, tri_results = filter_by_region(
result_element_id, tris, quads, quad_results, tri_results = filter_by_region(
self, regions_to_remove, regions_to_include)
if len(element_id) == 0 and 0:
assert np.array_equal(result_element_id, np.unique(result_element_id))
if len(result_element_id) == 0 and 0:
# error state to not crash gui
region_id = self.region[0]
self.log.warning(f'no elements remaining; including region_id={region_id}')
return self.get_filtered_data(
regions_to_include=[region_id],
return_model=return_model)
assert len(element_id) > 0, 'no elements remaining'
assert len(result_element_id) > 0, 'no elements remaining'

model2 = self.from_data(
self.node_id, self.xyz,
tris, quads,
element_id, self.titles,
result_element_id, self.titles,
quad_results, tri_results,
auto_read_write_h5=True,
log=self.log)
assert isinstance(model2, Fluent)
str(model2)
assert len(model2.result_element_id) > 0
region = model2.region
results = model2.results
else:
self.log.debug('no filtering of regions')
tris = self.tris
quads = self.quads

# we reordered the tris/quads to be continuous to make them easier to add
iquad = np.searchsorted(self.element_id, quads[:, 0])
itri = np.searchsorted(self.element_id, tris[:, 0])
assert np.array_equal(quads[:, 0], np.unique(quads[:, 0]))
assert np.array_equal(tris[:, 0], np.unique(tris[:, 0]))

iquad = np.searchsorted(self.result_element_id, quads[:, 0])
itri = np.searchsorted(self.result_element_id, tris[:, 0])
quad_results = self.results[iquad, :]
tri_results = self.results[itri, :]
element_id = self.element_id
result_element_id = self.result_element_id
assert np.array_equal(result_element_id, np.unique(result_element_id))

region = self.region
results = np.vstack([quad_results, tri_results])
model2 = self
assert len(element_id) > 0, 'no elements remaining2'
self.results = results
assert len(result_element_id) > 0, 'no elements remaining2'
assert len(self.result_element_id) > 0
if return_model:
return model2
return element_id, tris, quads, region, results
return result_element_id, tris, quads, region, results

@property
def region(self) -> np.ndarray:
Expand All @@ -341,7 +358,7 @@ def __repr__(self) -> str:
'Fluent:\n'
f' - node_id = {self.node_id}\n'
f' - xyz =\n{self.xyz}\n'
f' - element_id = {self.element_id}\n'
f' - result_element_id = {self.result_element_id}\n'
f' - quads = {self.quads}\n'
f' - tris = {self.tris}\n'
f' - titles = {self.titles}\n'
Expand Down
27 changes: 18 additions & 9 deletions pyNastran/converters/fluent/fluent_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def load_fluent_geometry(self, fld_filename: str,
fld_filename, #auto_read_write_h5=False,
log=log, debug=False)
model = cast(Fluent, model)
assert len(model.result_element_id) > 0, str(model)
#self.model_type = model.model_type

node_id = model.node_id
Expand All @@ -60,20 +61,28 @@ def load_fluent_geometry(self, fld_filename: str,
regions_to_remove = other_settings.cart3d_fluent_remove
regions_to_include = other_settings.cart3d_fluent_include

assert len(model.result_element_id) > 0
model2 = model.get_filtered_data(
regions_to_remove, regions_to_include, return_model=True)
str(model2)

element_id = model2.element_id
assert len(element_id) > 0, element_id
assert len(element_id) == len(np.unique(element_id))
result_element_id = model2.result_element_id
assert len(result_element_id) > 0, result_element_id
assert len(result_element_id) == len(np.unique(result_element_id))
tris = model2.tris
quads = model2.quads
region = model2.region

# support multiple results
titles = model2.titles
results = model2.results
if 0:
results = model2.results
else:
iquad = np.searchsorted(model2.result_element_id, quads[:, 0])
itri = np.searchsorted(model2.result_element_id, tris[:, 0])
quad_results = model2.results[iquad, :]
tri_results = model2.results[itri, :]
results = np.vstack([quad_results, tri_results])
assert len(titles)-1 == results.shape[1], (len(titles), results.shape)

out = model.get_area_centroid_normal(tris, quads)
Expand All @@ -96,8 +105,8 @@ def load_fluent_geometry(self, fld_filename: str,
node_id = node_id[inode_used]
nodes = nodes[inode_used, :]

nelement = len(element_id)
assert len(element_id) == len(region), f'neids={len(element_id)} nregion={len(region)}'
nelement = len(result_element_id)
assert len(result_element_id) == len(region), f'neids={len(result_element_id)} nregion={len(region)}'

assert nodes is not None
nodes = gui.scale_length(nodes)
Expand Down Expand Up @@ -146,12 +155,12 @@ def load_fluent_geometry(self, fld_filename: str,

gui.isubcase_name_map[ID] = ('Fluent', '')
form, cases = _fill_fluent_case(
cases, ID, node_id, element_id,
cases, ID, node_id, result_element_id,
region, results, titles, normal,
nnodes_array)

gui.node_ids = node_id
gui.element_ids = element_id
gui.element_ids = result_element_id
#log.debug(f'running _finish_results_io2')
gui._finish_results_io2(model_name, form, cases)
#log.info(f'finished')
Expand Down Expand Up @@ -260,7 +269,7 @@ def _fill_fluent_case(cases: dict[int, Any],
data_format='%.3f', colormap=colormap, uname='NormalZ')

nnodes_res = GuiResult(ID, 'nnodes', 'Nnodes', 'centroid', nnodes_array,
data_format='%.0f', colormap=colormap, uname='Nnodes')
data_format='%.0f', colormap=colormap, uname='Nnodes')

assert len(element_id) == len(region), f'neids={len(element_id)} nregion={len(region)}'
cases[icase] = (nid_res, (itime, 'NodeID'))
Expand Down
6 changes: 3 additions & 3 deletions pyNastran/converters/fluent/fluent_to_tecplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def fluent_to_tecplot(fluent_filename: PathLike,
log = fluent_model.log
node_id = fluent_model.node_id
xyz = fluent_model.xyz
element_id = fluent_model.element_id
result_element_id = fluent_model.result_element_id
titles = fluent_model.titles
results = fluent_model.results
quads = fluent_model.quads # eid, pid, n1, n2, n3, n4
Expand Down Expand Up @@ -51,7 +51,7 @@ def fluent_to_tecplot(fluent_filename: PathLike,
jtri_nodes = np.searchsorted(unodes, tri_nodes)
#-------------

ires_tri = np.searchsorted(element_id, tri_eids)
ires_tri = np.searchsorted(result_element_id, tri_eids)
res_tri = results[ires_tri, :]
assert res_tri.ndim == 2, res_tri.shape
res_tri = np.column_stack((tri_regions, res_tri))
Expand Down Expand Up @@ -84,7 +84,7 @@ def fluent_to_tecplot(fluent_filename: PathLike,
jquad_nodes = np.searchsorted(unodes, quad_nodes)
#-------------

ires_quad = np.searchsorted(element_id, quad_eids)
ires_quad = np.searchsorted(result_element_id, quad_eids)
res_quad = results[ires_quad, :]
assert res_quad.ndim == 2, res_quad.shape
res_quad = np.column_stack((quad_regions, res_quad))
Expand Down
3 changes: 2 additions & 1 deletion pyNastran/converters/fluent/nastran_to_fluent.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from pyNastran.bdf.bdf import read_bdf
from cpylog import SimpleLogger

def nastran_to_fluent(nastran_filename: str, fluent_filename: str, log: SimpleLogger=None):
def nastran_to_fluent(nastran_filename: str, fluent_filename: str,
log: SimpleLogger=None):
model = read_bdf(nastran_filename, log=log)
vrt_filename = os.path.splitext(fluent_filename)[0] + '.vrt'
cel_filename = os.path.splitext(fluent_filename)[0] + '.cel'
Expand Down
10 changes: 7 additions & 3 deletions pyNastran/converters/fluent/test_fluent.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pathlib import Path

import numpy as np
#from cpylog import get_logger
from cpylog import SimpleLogger

import pyNastran
from pyNastran.converters.fluent.fluent import Fluent, read_vrt, read_cell, read_daten, read_fluent
Expand Down Expand Up @@ -82,21 +82,25 @@ def test_nastran_to_fluent(self):
cel_filename = BWB_PATH / 'bwb_saero.cel'
daten_filename = BWB_PATH / 'bwb_saero.daten'
h5_filename = BWB_PATH / 'bwb_saero.h5'
h5_filename2 = BWB_PATH / 'bwb_saero2.h5'
tecplot_filename = BWB_PATH / 'bwb_saero.plt'
if h5_filename.exists():
os.remove(h5_filename)
if h5_filename2.exists():
os.remove(h5_filename2)

#model = Fluent()
#is_loaded = model.read_h5(h5_filename)
#assert is_loaded is True, h5_filename

nastran_to_fluent(nastran_filename, vrt_filename)
log = SimpleLogger('debug')
nastran_to_fluent(nastran_filename, vrt_filename, log=log)

node_id, xyz = read_vrt(vrt_filename)
assert len(xyz) == 10135, xyz.shape
(quads, tris), element_ids = read_cell(cel_filename)
element_id, titles, results = read_daten(daten_filename, scale=2.0)
model = read_fluent(vrt_filename)
model = read_fluent(vrt_filename, log=log)
model.write_fluent(vrt_filename2)
model2 = read_fluent(vrt_filename2)
model2.get_filtered_data(
Expand Down
7 changes: 5 additions & 2 deletions pyNastran/converters/fluent/test_fluent_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ def test_fluent_gui_ugrid3d_gui_box(self):
"""simple UGRID3D box model"""
ugrid_filename = UGRID_PATH / 'box.b8.ugrid'
fluent_filename = UGRID_PATH / 'box.vrt'
h5_filename = UGRID_PATH / 'box.h5'
if h5_filename.exists():
os.remove(h5_filename)
fluent_model = ugrid_to_fluent_filename(ugrid_filename, fluent_filename)

log = get_logger(level='warning', encoding='utf-8')
Expand All @@ -76,10 +79,10 @@ def test_fluent_gui_missing_nodes(self):
model.quads = np.array([
[2, 12, 1, 2, 3, 4],
])
model.element_id = np.array([1, 2])
model.result_element_id = np.array([1, 2])
model.element_ids = np.array([1, 2])
model.titles = ['ShellID', 'Pi']
model.results = np.ones((len(model.element_id), 1)) * 3.14
model.results = np.ones((len(model.result_element_id), 1)) * 3.14

log = get_logger(level='warning', encoding='utf-8')
test = FluentGui()
Expand Down
Loading

0 comments on commit 265afa7

Please sign in to comment.