Source code for pyNastran.op2.fortran_format

"""
Defines:
 - FortranFormat

"""
from typing import Optional
from pyNastran.utils import object_attributes
from pyNastran.utils.numpy_utils import integer_types
#from pyNastran.op2.errors import FortranMarkerError, SortCodeError


[docs]class FortranFormat: """defines basic methods for reading Fortran formatted data files""" def __init__(self): self.n = 0 self.f = None self.obj = None self.table_name = None self.isubcase = None self.binary_debug = None self.read_mode = 1 self._endian = None self._table_mapper = {} self._nastran_format = None #: stores if the user entered [] for isubcases self.is_all_subcases = True self.valid_subcases = [] #self.op2_reader = OP2Reader() self.IS_TESTING = True
[docs] def show(self, n: int, types: str='ifs', endian=None, force: bool=False): # pragma: no cover """Shows binary data""" return self.op2_reader.show(n, types=types, endian=endian, force=force)
[docs] def show_data(self, data, types: str='ifs', endian=None, force: bool=False): # pragma: no cover """Shows binary data""" return self.op2_reader.show_data(data, types=types, endian=endian, force=force)
[docs] def show_ndata(self, n: int, types: str='ifs', force: bool=False): # pragma: no cover self.op2_reader.show_ndata(n, types=types, force=force)
#def passer(self, data): #""" #dummy function used for unsupported tables #""" #pass def _get_table_mapper(self): raise NotImplementedError('this should be overwritten') def _finish(self): raise NotImplementedError('overwrite this') def _read_subtable_results(self, table4_parser, record_len: int) -> Optional[int]: """ # if reading the data # 1 - 1st pass to size the array (vectorized) # 2 - 2nd pass to read the data (vectorized) Parameters ---------- table4_parser : function the parser function for table 4 record_len : int the length of the record block Returns ------- n : None / int None : an error occurred or we're in read_mode=1/array sizing (???) int : the number of bytes that have been read """ op2_reader = self.op2_reader # type: OP2Reader #datai = b'' n = 0 if self.read_mode == 2: self.ntotal = 0 data, ndata = op2_reader._read_record_ndata() n = table4_parser(data, ndata) assert isinstance(n, integer_types), self.table_name self._reset_vector_counter() elif self.read_mode == 1: # if we're checking the array size #n = op2_reader._skip_record() #n = table4_parser(datai, 300000) if self.table_name in {b'R1TABRG', b'ONRGY1', b'PVT', b'PVT0', b'PVTS'}: # these tables are always fully parsed # PVT/PVTS - we want to know what the PARAM cards are, # so we can determine the NXVER data, ndata = op2_reader._read_record_ndata() else: data, ndata = op2_reader._skip_record_ndata() n = table4_parser(data, ndata) if not isinstance(n, integer_types): msg = 'n is not an integer; table_name=%s n=%s table4_parser=%s' % ( self.table_name, n, table4_parser) raise TypeError(msg) #op2_reader._goto(n) #n = op2_reader._skip_record() self._init_vector_counter(record_len) else: raise RuntimeError(self.read_mode) self._cleanup_data_members() return n def _reset_vector_counter(self) -> None: """ if reading the data 0 - non-vectorized 1 - 1st pass to size the array (vectorized) 2 - 2nd pass to read the data (vectorized) vectorized objects are stored as self.obj they have obj.itime which is their table3 counter """ if not(hasattr(self, 'obj') and hasattr(self.obj, 'itime')): #print(f'self.obj.name={self.obj.__class__.__name__!r} doesnt have itime') return #ntotal = record_len // (self.num_wide * 4) * self._data_factor # we reset the itime counter when we fill up the # total number of nodes/elements/layers in the # result, where ntotal is the critical length of # interest. This let's us start back at the correct # spot the next time we read table3 # # For displacements, ntotal=nnodes # # For a CBAR, it's ntotal=nelements*2, where 2 is # the number of nodes; points A/B # # For a CTRIA3 / linear CQUAD4, it's # ntotal=nelements*2, where 2 is the number of # layers (top/btm) and we only get a centroidal # result. # # For a CQUAD4 bilinear, it's # ntotal=nelements*(nnodes+1)*2, where 2 is the # number of layers and nnodes is 4 (we get an extra # result at the centroid). # # For a PCOMP, it's ntotal=sum(nelements*nlayers), # where each element can have a different number # of layers if self.obj.ntotal == self.obj.data.shape[1] or 1: #if self.table_name_str in ['OESRMS2', 'OESNO2', 'OSTRRMS2', 'OSTRNO2', 'OESATO2']: #print('resetting %r indicies; itime=%s; shape=%s' % ( #self.obj.class_name, self.obj.itime, self.obj.data.shape)) self.obj._reset_indices() self.obj.words = self.words self.obj.itime += 1 else: # This happens when self._data_factor hasn't been reset # or is set wrong. # can it happen any other time? # # yup, when you have sort2... msga = f'self.obj.name={self.obj.__class__.__name__!r} has itime' self.log.debug(msga) msgb = 'ntotal=%s shape=%s shape[1]=%s _data_factor=%s\n' % ( self.obj.ntotal, str(self.obj.data.shape), self.obj.data.shape[1], self._data_factor) msgb += f'obj._ntotals={self.obj._ntotals}' self.log.error(msgb) raise RuntimeError(msga + '\n' + msgb) def _init_vector_counter(self, record_len: int) -> None: """ Sets the table size Parameters ---------- record_len : int the length of the record block """ if not(hasattr(self, 'obj') and self.obj is not None): return if hasattr(self.obj, 'ntimes'): if not hasattr(self.obj, '_reset_indices'): #methods = '\ndir(obj)=%s' % ', '.join(sorted(dir(self.obj))) #msg = 'is %s vectorized because its missing _reset_indices...%s' % ( #self.obj.__class__.__name__, methods) return None #raise RuntimeError(msg) self.obj._reset_indices() self.obj.ntimes += 1 ntotal = record_len // (self.num_wide * self.size) * self._data_factor # this has a problem with XYPLOT data if there is a result # request in the same format (e.g. OESNLXD/OES1X1 tables # if they both have the same element ID) # #class_name = self.obj.__class__.__name__ #if class_name == 'RealBush1DStressArray': #print('%s.ntotal = %s' % (class_name, ntotal)) #print('num_wide=%s factor=%s len=%s ntotal=%s' % ( #self.num_wide, self._data_factor, record_len, ntotal)) self.obj.ntotal = ntotal self.obj._ntotals.append(ntotal) assert isinstance(self.obj.ntotal, integer_types), type(self.obj.ntotal) else: self.log.warning('obj=%s doesnt have ntimes' % self.obj.__class__.__name__) return def _cleanup_data_members(self) -> None: """deletes variables from previous tables""" del_words = [ 'words', #'Title', #'ID', 'analysis_code', #'result_names', #'labels', #'data_names', ] msg = '' if hasattr(self, 'words'): if len(self.words) not in [0, 28]: msg = 'table_name=%r len(self.words)=%s words=%s' % ( self.table_name, len(self.words), self.words) raise RuntimeError(msg) for word in self.words: if word in ['???', 'Title']: continue if not hasattr(self, word): continue delattr(self, word) self.words = [] if hasattr(self, 'analysis_code'): del self.analysis_code #if hasattr(self, 'data_names') and self.data_names is not None: #print(object_attributes(self)) if hasattr(self, 'data_code'): del self.data_code if hasattr(self, 'mode'): del self.mode for word in del_words: if hasattr(self, word): val = getattr(self, word) if isinstance(val, list) and len(val) == 0: continue msg += ' %s=%s\n' % (word, val) if msg: print(object_attributes(self)) print(msg)