Source code for pyNastran.op2.op2

#pylint: disable=C0103,W0201,W0223,R0901,R0902,R0904
"""
Main OP2 class
"""
from __future__ import (nested_scopes, generators, division, absolute_import,
                        print_function, unicode_literals)
import os
from numpy import array, unique, where
from pyNastran.op2.op2_scalar import OP2_Scalar

from pyNastran.f06.errors import FatalError
from pyNastran.op2.op2_common import SortCodeError

[docs]class OP2(OP2_Scalar): def __init__(self, debug=True, log=None, debug_file=None): """ Initializes the OP2 object :param debug: enables the debug log and sets the debug in the logger (default=False) :param log: a logging object to write debug messages to (.. seealso:: import logging) :param debug_file: sets the filename that will be written to (default=None -> no debug) """ make_geom = False assert make_geom == False, make_geom OP2_Scalar.__init__(self, debug=debug, log=log, debug_file=debug_file) self.ask = False
[docs] def set_as_vectorized(self, ask=False): """ Enables vectorization The code will degenerate to dictionary based results when a result does not support vectorization. Vectorization is always True here. :param ask: Do you want to see a GUI of result types. +--------+---------------+---------+------------+ | Case # | Vectorization | Ask | Read Modes | +========+===============+=========+============+ | 1 | True | True | 1, 2 | +--------+---------------+---------+------------+ | 2 | True | False | 1, 2 | +--------+---------------+---------+------------+ | 3 | False | True | 1, 2 | +--------+---------------+---------+------------+ | 4 | False | False | 0 | +--------+---------------+---------+------------+ Definitions =========== Vectorization - A storage structure that allows for faster read/access speeds and better memory usage, but comes with a more difficult to use data structure. It limits the node IDs to all be integers (e.g. element centroid). Composite plate elements (even for just CTRIA3s) with an inconsistent number of layers will have a more difficult data structure. Models with solid elements of mixed type will also be more complicated (or potentially split up). Scanning - a quick check used to figure out how many results to process that takes almost no time Reading - process the op2 data Build - call the __init__ on a results object (e.g. DisplacementObject) Start Over - Go to the start of the op2 file Ask - launch a GUI dialog to let the user click which results to load Read Mode Definitions ===================== 0. The default OP2 dictionary based-approach with no asking GUI 1. The first read of a result to get the shape of the data (and result types if vectorization=True or result types if vectorization=False) 2. The second read of a result to get the results Cases ====== 1. Scan the block to get the size, build the object (read_mode=1), ask the user, start over, fill the objects (read_mode=2). Degenerate to read_mode=0 when read_mode=2 cannot be used based upon the value of ask. 2. Same as case #1, but don't ask the user. Scan the block to get the size, build the object (read_mode=1), start over, fill the objects (read_mode=2). 3. Scan the block to get the object types (read_mode=1), ask the user, build the object & fill it (read_mode=2) 4. Read the block to get the size, build the object & fill it (read_mode=0) """ self.ask = ask
[docs] def read_op2(self, op2_filename=None, vectorized=True, combine=True): """ Starts the OP2 file reading :param op2_filename: the op2_filename (default=None -> popup) :param vectorized: should the vectorized objects be used (default=True) :param combine: should objects be isubcase based (True) or (isubcase, subtitle) based (False) The second will be used for superelements regardless of the option (default=True) """ self.log.info('vectorized=%s combine=%s' % (vectorized, combine)) assert self.ask in [True, False], self.ask self.is_vectorized = vectorized if self.is_vectorized: self.log.info('-------- reading op2 with read_mode=1 --------') self.read_mode = 1 self._close_op2 = False # get GUI object names, build objects, but don't read data OP2_Scalar.read_op2(self, op2_filename=op2_filename) # TODO: stuff to figure out objects # TODO: stuff to show gui of table names # TODO: clear out objects the user doesn't want self.read_mode = 2 self._close_op2 = True self.log.info('-------- reading op2 with read_mode=2 --------') OP2_Scalar.read_op2(self, op2_filename=self.op2_filename) else: self.read_mode = 0 self._close_op2 = True OP2_Scalar.read_op2(self, op2_filename=op2_filename) self.combine_results(combine=combine) self.log.info('finished reading op2')
[docs] def combine_results(self, combine=True): """ we want the data to be in the same format and grouped by subcase, so we take .. code-block:: python stress = { (1, 'SUPERELEMENT 0') : result1, (1, 'SUPERELEMENT 10') : result2, (1, 'SUPERELEMENT 20') : result3, (2, 'SUPERELEMENT 0') : result4, } and convert it to: .. code-block:: python stress = { 1 : result1 + result2 + results3, 2 : result4, } """ if not combine: return self.log.info('compress_results') result_types = self.get_table_types() for result_type in result_types: result = getattr(self, result_type) case_keys = sorted(result.keys()) isubcases = [isubcase_name[0] if isinstance(isubcase_name, tuple) else isubcase_name for isubcase_name in case_keys] #print('case_keys[%s]=%s; isubcases=%s' % (result_type, case_keys, isubcases)) unique_isubcases = unique(isubcases) for isubcase in unique_isubcases: wherei = [i for i, isubcasei in enumerate(isubcases) if isubcasei == isubcase] keys = [case_keys[i] for i in wherei] if len(wherei) == 1: # rename the case since we have only one tuple for the result result[isubcase] = result[keys[0]] del result[keys[0]] else: continue # multiple results to combine res1 = result[keys[0]] if not hasattr(res1, 'combine'): print("res1=%s has no method combine" % res1.__class__.__name__) continue #raise NotImplementedError('multiple results to combine') del result[keys[0]] results_list = [] for key in keys[1:]: results_list.append(result[key]) del result[key] #res1.data = hstack(res1.data, res2.data) # combination method depends on result, so stresses are # combined differently than displacements res1.combine(results_list) result[isubcase] = res1 setattr(self, result_type, result)
if __name__ == '__main__': # pragma: no cover import pyNastran pkg_path = pyNastran.__path__[0] # we don't want the variable name to get picked up by the class _op2_filename = os.path.join(pkg_path, '..', 'models', 'sol_101_elements', 'solid_shell_bar.op2') model = OP2() model.set_as_vectorized(ask=False) model.read_op2(_op2_filename) isubcase = 1 # ============displacement================ # same for velocity/acceleration/mpc forces/spc forces/applied load # maybe not temperature because it's only 1 result... displacement = model.displacement[isubcase] data = displacement.data grid_type = model.displacement[isubcase].grid_type # t1, t2, t3, r1, r2, r3 assert displacement.ntimes == 1, displacement.ntimes # get all the nodes for element 1 inode1 = data.getNodeIndex( [1] ) # [itransient, node, t1/t2] datai = data[0, inode1, :] grid_typei = grid_type[inode] # ============solid stress================= # same for solid strain solid_stress = model.solidStress[isubcase] data = solid_stress.data cid = model.solidStress[isubcase].cid # oxx, oyy, ozz, txy, tyz, txz assert solid_stress.ntimes == 1, solid_stress.ntimes # get the indexs for cid, element 1 ielem1 = solid_stress.getElementPropertiesIndex( [1] ) # element-specific properties datai = cid[ielem1] # get all the nodes for element 1 ielem1 = solid_stress.getElementIndex( [1] ) # [itransient, elem*node, oxx/oyy, etc.] datai = data[0, ielem1, :] # get all the nodes for element 1 ielem1 = solid_stress.getElementIndex( [1] ) # [itransient, elem*node, oxx/oyy, etc.] datai = data[0, ielem1, :] # get all the nodes for element 4 and 5 ielem45 = solid_stress.getElementIndex( [[1, 4, 5]] ) datai = data[0, ielem45, :] # get the index for element 1, centroid ielem1_centroid = solid_stress.getElementNodeIndex( [[1, 0]] ) datai = data[0, ielem1_centroid, :] # get the index for element 1, node 1 ielem1_node1 = solid_stress.getElementNodeIndex( [[1, 1]] ) datai = data[0, ielem1_node1, :] # get the index for element 1, node 1 and element 1, node 2 ielem1_node12 = solid_stress.getElementNodeIndex( [[1, 1], [1, 2],]) datai = data[0, ielem1_node12, :] # ============plate stress================= # same for plate strain plate_stress = model.plateStress[isubcase] data = plate_stress.data # oxx, oyy, ozz, txy, tyz, txz assert plate_stress.ntimes == 1, plate_stress.ntimes # get all the nodes for element 1 ielem1 = plate_stress.getElementIndex( [1] ) # [itransient, elem*node, oxx/oyy, etc.] datai = plate_stress[0, ielem1, :] # ========composite plate stress============ # same for plate strain comp_plate_stress = model.compositePlateStress[isubcase] data = comp_plate_stress.data cid = model.plateStress[isubcase].cid # get the indexs for cid, element 1 ielem1 = comp_plate_stress.getElementPropertiesIndex( [1] ) # element-specific properties datai = cid[ielem1] # oxx, oyy, ozz, txy, tyz, txz assert comp_plate_stress.ntimes == 1, comp_plate_stress.ntimes # get all the nodes/layers for element 1 ielem1 = comp_plate_stress.getElementIndex( [1] ) # [itransient, elem*node*layer, oxx/oyy, etc.] datai = data[0, ielem1, :] # get all the layers for element 1, centroid, and all the layers ielem1_centroid = solid_stress.getElementNodeIndex( [[1, 0]] ) datai = data[0, ielem1_centroid, :] # get the index for element 1, centroid, layer 0 ielem1_centroid_layer = solid_stress.getElementNodeLayerIndex( [[1, 0, 0]] ) datai = data[0, ielem1_centroid_layer, :] # get the index for element 1, layer 0, and all the nodes ielem1_layer = solid_stress.getElementLayerIndex( [[1, 0]] ) datai = data[0, ielem1_layer, :]