Source code for pyNastran.op2.op2_geom

"""
Defines:
 - read_op2_geom(op2_filename=None, combine=True, subcases=None,
                 exclude_results=None, include_results=None,
                 validate=True, xref=True,
                 build_dataframe=False, skip_undefined_matrices=True,
                 mode='msc', log=None, debug=True, debug_file=None, encoding=None)
 - OP2Geom(make_geom=True, debug=False, log=None, debug_file=None, mode='msc')
   - OP2

"""
from __future__ import annotations
from pickle import dump
from pathlib import PurePath
from typing import List, Optional, Union, Any, TYPE_CHECKING
import numpy as np

from pyNastran.op2.tables.geom.geom_common import GeomCommon
from pyNastran.op2.tables.geom.geom1 import GEOM1
from pyNastran.op2.tables.geom.geom2 import GEOM2
from pyNastran.op2.tables.geom.geom3 import GEOM3
from pyNastran.op2.tables.geom.geom4 import GEOM4

from pyNastran.op2.tables.geom.ept import EPT
from pyNastran.op2.tables.geom.mpt import MPT
from pyNastran.op2.tables.geom.edt import EDT
from pyNastran.op2.tables.geom.edom import EDOM
from pyNastran.op2.tables.geom.contact import CONTACT

from pyNastran.op2.tables.geom.dit import DIT
from pyNastran.op2.tables.geom.dynamics import DYNAMICS
from pyNastran.op2.tables.geom.axic import AXIC

from pyNastran.bdf.bdf import BDF
from pyNastran.bdf.errors import DuplicateIDsError
from pyNastran.op2.op2 import OP2, FatalError, SortCodeError, DeviceCodeError, FortranMarkerError
if TYPE_CHECKING:  # pragma: no cover
    from cpylog import SimpleLogger


[docs]def read_op2_geom(op2_filename: Optional[Union[str, PurePath]]=None, combine: bool=True, subcases: Optional[List[int]]=None, exclude_results: Optional[List[str]]=None, include_results: Optional[List[str]]=None, validate: bool=True, xref: bool=True, build_dataframe: bool=False, skip_undefined_matrices: bool=True, mode: str='msc', log: SimpleLogger=None, debug: bool=True, debug_file: Optional[str]=None, encoding: Optional[str]=None): """ Creates the OP2 object without calling the OP2 class. Parameters ---------- op2_filename : str (default=None -> popup) the op2_filename combine : bool; default=True True : objects are isubcase based False : objects are (isubcase, subtitle) based; will be used for superelements regardless of the option subcases : List[int, ...] / int; default=None->all subcases list of [subcase1_ID,subcase2_ID] exclude_results / include_results : List[str] / str; default=None a list of result types to exclude/include one of these must be None validate : bool runs various checks on the BDF (default=True) xref : bool should the bdf be cross referenced (default=True) build_dataframe : bool; default=False builds a pandas DataFrame for op2 objects skip_undefined_matrices : bool; default=False True : prevents matrix reading crashes log : Log() a logging object to write debug messages to (.. seealso:: import logging) debug : bool; default=False enables the debug log and sets the debug in the logger debug_file : str; default=None (No debug) sets the filename that will be written to encoding : str the unicode encoding (default=None; system default) Returns ------- model : OP2() an OP2 object .. todo:: creates the OP2 object without all the read methods .. note :: this method will change in order to return an object that does not have so many methods """ model = OP2Geom(log=log, debug=debug, debug_file=debug_file, mode=mode) model.set_subcases(subcases) model.include_exclude_results(exclude_results=exclude_results, include_results=include_results) model.read_op2(op2_filename=op2_filename, build_dataframe=build_dataframe, skip_undefined_matrices=skip_undefined_matrices, combine=combine, encoding=encoding) if validate: model.validate() if xref: model.cross_reference() return model
[docs]class OP2GeomCommon(OP2, GeomCommon): """interface for the OP2Geom class for to loading subclasses""" def __init__(self, make_geom: bool=True, debug: bool=False, log: Any=None, debug_file: Optional[str]=None, mode: Optional[str]=None): """ Initializes the OP2 object Parameters ---------- make_geom : bool; default=False reads the BDF tables debug : bool; default=False enables the debug log and sets the debug in the logger log: log() a logging object to write debug messages to (.. seealso:: import logging) debug_file : default=None -> no debug sets the filename that will be written to mode : str; default=None -> 'msc' {msc, nx} """ #self.big_properties = {} self.big_materials = {} self.reader_geom2 = GEOM2(self) self.reader_geom1 = GEOM1(self) self.reader_geom3 = GEOM3(self) self.reader_geom4 = GEOM4(self) self.reader_ept = EPT(self) self.reader_mpt = MPT(self) self.reader_edt = EDT(self) self.reader_edom = EDOM(self) self.reader_contact = CONTACT(self) self.reader_dit = DIT(self) self.reader_dynamic = DYNAMICS(self) self.reader_axic = AXIC(self) OP2.__init__(self, debug=debug, log=log, debug_file=debug_file, mode=mode) self.make_geom = True # F:\work\pyNastran\examples\Dropbox\move_tpl\beamp10.op2 # F:\work\pyNastran\examples\Dropbox\move_tpl\ifsr22r.op2 # F:\work\pyNastran\examples\Dropbox\move_tpl\ifssh22.op2 # F:\work\pyNastran\examples\Dropbox\move_tpl\ifsr22r.op2 # F:\work\pyNastran\examples\Dropbox\move_tpl\ifsv02pp.op2 self._viewtb_map = { (10300, 103, 16) : ['QUADP', self._read_fake], (10400, 104, 15) : ['TRIAP', self._read_fake], (10500, 105, 14) : ['BEAMP', self._read_fake], (14100, 141, 18) : ['HEXAP', self._read_view_hexa], (14200, 142, 16) : ['PENTAP', self._read_fake], (14300, 143, 14) : ['TETRAP', self._read_fake], #(10500, 105, 14) : ['???', self._read_fake], #(10500, 105, 14) : ['???', self._read_fake], } def _read_view_hexa(self, data, n): """ Word Name Type Description 1 EID I Element identification number 2 CID I Coordinate system identification number -- from CID field 3 NX I View mesh subdivision -- from VIEW field 4 NY I View mesh subdivision -- from VIEW field 5 NZ I View mesh subdivision -- from VIEW field 6 MTH CHAR4 Method -- 'DIRE' means direct 7 MINEID I Minimum VUHEXA identification number for this element 8 MAXEID I Maximum VUHEXA identification number for this element 9 MINGID I Minimum grid identification number for this element 10 MAXGID I Maximum grid identification number for this element 11 G(8) I Corner grid identification numbers """ # C:\NASA\m4\formats\git\examples\move_tpl\ifsv34b.op2 ints = np.frombuffer(data[n:], self.idtype) # .tolist() nelements = len(ints) // 18 assert len(ints) % 18 == 0 #print('nelements =', nelements) ints2 = ints.reshape(nelements, 18) for intsi in ints2: eid, cid, nx, ny, nz, junk_imth, mineid, maxeid, mingid, maxgid, *nids = intsi mth = data[n+20:n+24].decode('latin1') #print(eid, cid, [nx, ny, nz], mth, [mineid, maxeid, mingid, maxgid], nids) assert mth in ['DIRE', 'EXTR'], mth n += 72 return n
[docs] def save(self, obj_filename: str='model.obj', unxref: bool=True) -> None: """Saves a pickleable object""" #del self.log #del self._card_parser, self._card_parser_prepare #print(object_attributes(self, mode="all", keys_to_skip=[])) with open(obj_filename, 'wb') as obj_file: dump(self, obj_file)
def _get_table_mapper(self): table_mapper = OP2._get_table_mapper(self) table_mapper[b'CONTACT'] = [self.reader_contact.read_contact_4, self.reader_contact.read_contact_4] table_mapper[b'CONTACTS'] = [self.reader_contact.read_contact_4, self.reader_contact.read_contact_4] table_mapper[b'VIEWTB'] = [self._read_viewtb_4, self._read_viewtb_4] table_mapper[b'EDT'] = [self.reader_edt.read_edt_4, self.reader_edt.read_edt_4] table_mapper[b'EDTS'] = [self.reader_edt.read_edt_4, self.reader_edt.read_edt_4] # geometry table_mapper[b'GEOM1'] = [self.reader_geom1.read_geom1_4, self.reader_geom1.read_geom1_4] table_mapper[b'GEOM2'] = [self.reader_geom2.read_geom2_4, self.reader_geom2.read_geom2_4] table_mapper[b'GEOM3'] = [self.reader_geom3.read_geom3_4, self.reader_geom3.read_geom3_4] table_mapper[b'GEOM4'] = [self.reader_geom4.read_geom4_4, self.reader_geom4.read_geom4_4] # superelements table_mapper[b'GEOM1S'] = [self.reader_geom1.read_geom1_4, self.reader_geom1.read_geom1_4] table_mapper[b'GEOM2S'] = [self.reader_geom2.read_geom2_4, self.reader_geom2.read_geom2_4] table_mapper[b'GEOM3S'] = [self.reader_geom3.read_geom3_4, self.reader_geom3.read_geom3_4] table_mapper[b'GEOM4S'] = [self.reader_geom4.read_geom4_4, self.reader_geom4.read_geom4_4] table_mapper[b'GEOM1N'] = [self.reader_geom1.read_geom1_4, self.reader_geom1.read_geom1_4] table_mapper[b'GEOM2N'] = [self.reader_geom2.read_geom2_4, self.reader_geom2.read_geom2_4] table_mapper[b'GEOM3N'] = [self.reader_geom3.read_geom3_4, self.reader_geom3.read_geom3_4] table_mapper[b'GEOM4N'] = [self.reader_geom4.read_geom4_4, self.reader_geom4.read_geom4_4] table_mapper[b'GEOM1OLD'] = [self.reader_geom1.read_geom1_4, self.reader_geom1.read_geom1_4] table_mapper[b'GEOM2OLD'] = [self.reader_geom2.read_geom2_4, self.reader_geom2.read_geom2_4] table_mapper[b'GEOM3OLD'] = [self.reader_geom3.read_geom3_4, self.reader_geom3.read_geom3_4] table_mapper[b'GEOM4OLD'] = [self.reader_geom4.read_geom4_4, self.reader_geom4.read_geom4_4] table_mapper[b'GEOM1ATV'] = [self.reader_geom1.read_geom1_4, self.reader_geom1.read_geom1_4] table_mapper[b'GEOM2ATV'] = [self.reader_geom2.read_geom2_4, self.reader_geom2.read_geom2_4] table_mapper[b'EDOM'] = [self.reader_edom.read_edom4_4, self.reader_edom.read_edom4_4] # optimization table_mapper[b'EPT'] = [self.reader_ept.read_ept_4, self.reader_ept.read_ept_4] table_mapper[b'EPTS'] = [self.reader_ept.read_ept_4, self.reader_ept.read_ept_4] table_mapper[b'EPTOLD'] = [self.reader_ept.read_ept_4, self.reader_ept.read_ept_4] table_mapper[b'EPTATV'] = [self.reader_ept.read_ept_4, self.reader_ept.read_ept_4] table_mapper[b'MPT'] = [self.reader_mpt.read_mpt_4, self.reader_mpt.read_mpt_4] table_mapper[b'MPTS'] = [self.reader_mpt.read_mpt_4, self.reader_mpt.read_mpt_4] table_mapper[b'DYNAMIC'] = [self.reader_dynamic.read_dynamics_4, self.reader_dynamic.read_dynamics_4] table_mapper[b'DYNAMICS'] = [self.reader_dynamic.read_dynamics_4, self.reader_dynamic.read_dynamics_4] table_mapper[b'AXIC'] = [self.reader_axic.read_axic_4, self.reader_axic.read_axic_4] # table objects (e.g. TABLED1) table_mapper[b'DIT'] = [self.reader_dit.read_dit_4, self.reader_dit.read_dit_4] table_mapper[b'DITS'] = [self.reader_dit.read_dit_4, self.reader_dit.read_dit_4] return table_mapper def _read_viewtb_4(self, data: bytes, ndata: int): """ View information table Contains the relationship between each p-element and its view-elements and view-grids. """ return self._read_geom_4(self._viewtb_map, data, ndata)
[docs]class OP2Geom(BDF, OP2GeomCommon): """creates an interface for the OP2 and BDF classes""" _properties = [ 'is_bdf_vectorized', 'nid_map', 'wtmass', 'is_real', 'is_complex', 'is_random', '_sort_method', 'is_sort1', 'is_sort2', 'matrix_tables', 'table_name_str', 'is_geometry', #'dmigs', 'dmijs', 'dmiks', 'dmijis', 'dtis', 'dmis', ] def __init__(self, make_geom: bool=True, debug: bool=False, log: Any=None, debug_file: Optional[str]=None, mode: str='msc'): """ Initializes the OP2 object Parameters ---------- make_geom : bool; default=False reads the BDF tables debug : bool; default=False enables the debug log and sets the debug in the logger log: log() a logging object to write debug messages to (.. seealso:: import logging) debug_file : default=None -> no debug sets the filename that will be written to mode : str; default='msc' {msc, nx} """ BDF.__init__(self, debug=debug, log=log) OP2GeomCommon.__init__(self, make_geom=make_geom, debug=debug, log=log, debug_file=debug_file, mode=mode) @property def is_geometry(self) -> bool: return True
[docs] def read_op2(self, op2_filename: Optional[Union[str, PurePath]]=None, combine: bool=True, build_dataframe: Optional[bool]=False, skip_undefined_matrices: bool=False, encoding: Optional[str]=None): """see ``OP2.read_op2``""" OP2.read_op2(self, op2_filename=op2_filename, combine=combine, build_dataframe=build_dataframe, skip_undefined_matrices=skip_undefined_matrices, encoding=encoding) if len(self.nodes) == 0: self.gpdt_to_nodes()
[docs] def gpdt_to_nodes(self): """converts the GPDT & EQEXIN tables to node ids""" eqexin = self.op2_results.eqexin gpdt = self.op2_results.gpdt msg = '' if eqexin is None: msg += 'eqexin is None; ' if gpdt is None: msg += 'gpdt is None' return if msg: self.log.error('Cannot convert EQEXIN/GPDT to nodes because %s' % msg.rstrip('; ')) return nid_cp_cd_ps = gpdt.nid_cp_cd_ps xyz = gpdt.xyz nids = eqexin.nid for nid, nid_cp_cd_psi, xyzi in zip(nids, nid_cp_cd_ps, xyz): _nid, cp, cd, ps = nid_cp_cd_psi self.add_grid(nid, xyzi, cp=cp, cd=cd, ps=ps, seid=0, comment='')
def __getstate__(self): """clears out a few variables in order to pickle the object""" raise NotImplementedError() # Copy the object's state from self.__dict__ which contains # all our instance attributes. Always use the dict.copy() # method to avoid modifying the original state. #adfasd #state = BDF.__getstate__(self) #print(state) #state = self.__dict__.copy() # Remove the unpicklable entries. #i = 0 #for key, value in sorted(state.items()): #if isinstance(value, dict) and len(value) == 0: #continue ##if not isinstance(value, (str, int, float)): #if i > 5: # 72 #del state[key] #else: #print(key, type(value), value) #break #i += 1 #i = 0 #for key, value in sorted(state.items()): #if isinstance(value, dict) and len(value) == 0: #continue #if not isinstance(value, (str, int, float)): #if i > 200: # 72 #del state[key] #else: #print(key, type(value), value) #break #i += 1 #return state
[docs] def export_hdf5_file(self, hdf5_file, exporter=None): """ Converts the OP2 objects into hdf5 object Parameters ---------- hdf5_file : H5File() an h5py object exporter : HDF5Exporter; default=None unused TODO: doesn't support: - BucklingEigenvalues """ #from pyNastran.op2.op2_interface.hdf5_interface import export_op2_to_hdf5_file #op2_model = self OP2GeomCommon.export_hdf5_file(self, hdf5_file) BDF.export_hdf5_file(self, hdf5_file)
[docs]def bdf_to_op2_geom(model: BDF, validate: bool=True) -> OP2Geom: """converts a BDF() -> OP2Geom()""" if isinstance(model, OP2Geom): return model assert model is not None #assert op2_model is not None #assert model.bdf_filename is not None debug = model.debug if debug is None: debug = True op2_geom_model = OP2Geom(make_geom=True, debug=debug, log=model.log, debug_file=None, mode='msc') # apply data from our 2 models to the new model _properties = model._properties keys_to_skip = _properties + ['_properties', 'npoints', 'is_geometry'] for key in model.object_attributes(mode='both', keys_to_skip=keys_to_skip): value = getattr(model, key) #if isinstance(value, (dict, list)) and len(value) == 0: #continue #print(key, value) try: setattr(op2_geom_model, key, value) except AttributeError: op2_geom_model.log.error('cant set %r to %r' % (key, value)) raise #op2_geom_model.nodes = bdf_model.nodes #op2_geom_model.elements = bdf_model.elements return op2_geom_model
[docs]def attach_op2_results_to_bdf(bdf_model: BDF, op2_model: Optional[OP2]=None, validate: bool=True) -> OP2Geom: """We're up-converting a BDF and an OP2 result into an OP2Geom object.""" op2_geom_model = bdf_to_op2_geom(bdf_model, validate=validate) if op2_model is None: return op2_geom_model variables = [ 'op2_filename', 'matrices', 'eigenvalues', 'eigenvalues_fluid', 'displacements', 'load_vectors', 'eigenvectors', ] for key in variables: if hasattr(op2_model, key): value = getattr(op2_model, key) setattr(op2_geom_model, key, value) #if hasattr(op2_model, 'displacements'): #op2_geom_model.displacements = op2_model.displacements if validate: assert len(op2_geom_model.nodes) > 0, op2_geom_model.get_bdf_stats() return op2_geom_model