diff --git a/pyNastran/converters/fluent/fluent.py b/pyNastran/converters/fluent/fluent.py index f8485eeec..60a28f410 100644 --- a/pyNastran/converters/fluent/fluent.py +++ b/pyNastran/converters/fluent/fluent.py @@ -37,17 +37,21 @@ 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): @@ -55,10 +59,10 @@ def from_data(self, 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 @@ -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: @@ -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) @@ -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 @@ -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 @@ -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 @@ -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 @@ -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' @@ -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, @@ -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: @@ -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' diff --git a/pyNastran/converters/fluent/fluent_io.py b/pyNastran/converters/fluent/fluent_io.py index 6e5c7d1ed..e34b08502 100644 --- a/pyNastran/converters/fluent/fluent_io.py +++ b/pyNastran/converters/fluent/fluent_io.py @@ -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 @@ -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) @@ -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) @@ -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') @@ -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')) diff --git a/pyNastran/converters/fluent/fluent_to_tecplot.py b/pyNastran/converters/fluent/fluent_to_tecplot.py index 33c305ff5..dde04939a 100644 --- a/pyNastran/converters/fluent/fluent_to_tecplot.py +++ b/pyNastran/converters/fluent/fluent_to_tecplot.py @@ -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 @@ -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)) @@ -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)) diff --git a/pyNastran/converters/fluent/nastran_to_fluent.py b/pyNastran/converters/fluent/nastran_to_fluent.py index 5830a00d7..9f117d916 100644 --- a/pyNastran/converters/fluent/nastran_to_fluent.py +++ b/pyNastran/converters/fluent/nastran_to_fluent.py @@ -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' diff --git a/pyNastran/converters/fluent/test_fluent.py b/pyNastran/converters/fluent/test_fluent.py index be917e22c..21cc5dc20 100644 --- a/pyNastran/converters/fluent/test_fluent.py +++ b/pyNastran/converters/fluent/test_fluent.py @@ -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 @@ -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( diff --git a/pyNastran/converters/fluent/test_fluent_gui.py b/pyNastran/converters/fluent/test_fluent_gui.py index 6d17bde88..a4ac383df 100644 --- a/pyNastran/converters/fluent/test_fluent_gui.py +++ b/pyNastran/converters/fluent/test_fluent_gui.py @@ -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') @@ -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() diff --git a/pyNastran/converters/fluent/ugrid_to_fluent.py b/pyNastran/converters/fluent/ugrid_to_fluent.py index 777457125..777e04f05 100644 --- a/pyNastran/converters/fluent/ugrid_to_fluent.py +++ b/pyNastran/converters/fluent/ugrid_to_fluent.py @@ -44,7 +44,7 @@ def ugrid_to_fluent(ugrid_filename: PathLike) -> Fluent: fluent_model.node_id = np.arange(1, nnodes+1) fluent_model.xyz = ugrid_model.nodes - fluent_model.element_id = element_id + fluent_model.result_element_id = element_id fluent_model.titles = ['ShellID', 'PropertyID'] fluent_model.results = property_id.reshape((neids, 1)) return fluent_model diff --git a/pyNastran/converters/fluent/utils.py b/pyNastran/converters/fluent/utils.py index 151a82667..bdbbdd8ca 100644 --- a/pyNastran/converters/fluent/utils.py +++ b/pyNastran/converters/fluent/utils.py @@ -13,14 +13,14 @@ def filter_by_region(model: Fluent, tris = model.tris quads = model.quads results = model.results - assert len(model.element_id) > 0, model.element_id + assert len(model.result_element_id) > 0, model.result_element_id tri_regions = tris[:, 1] quad_regions = quads[:, 1] # we reordered the tris/quads to be continuous to make them easier to add - iquad = np.searchsorted(model.element_id, quads[:, 0]) - itri = np.searchsorted(model.element_id, tris[:, 0]) + iquad = np.searchsorted(model.result_element_id, quads[:, 0]) + itri = np.searchsorted(model.result_element_id, tris[:, 0]) #----------------------------- is_remove = (len(regions_to_remove) == 0) is_include = (len(regions_to_include) == 0)