Source code for pyNastran.bdf.cards.baseCard

# pylint: disable=R0904,R0902,C0111,C0103
from __future__ import (nested_scopes, generators, division, absolute_import,
                        print_function, unicode_literals)
from six import string_types, integer_types
from six.moves import zip, range

from pyNastran.bdf.field_writer_8 import print_card_8, is_same
from pyNastran.bdf.field_writer_16 import print_card_16
from pyNastran.bdf.field_writer_double import print_card_double
from pyNastran.bdf.bdfInterface.assign_type import interpret_value
from pyNastran.bdf.deprecated import BaseCardDeprecated, ElementDeprecated


[docs]class BaseCard(BaseCardDeprecated): def __init__(self): pass
[docs] def comment(self): if hasattr(self, '_comment'): return '%s' % self._comment return ''
[docs] def _test_update_fields(self): n = 1 while 1: try: self.update_field(n, 1.0) # dummy updating the field except IndexError: return except KeyError: return
[docs] def update_field(self, n, value): try: key_name = self._field_map[n] setattr(self, key_name, value) except KeyError: self._update_field_helper(n, value)
[docs] def get_field(self, n): try: key_name = self._field_map[n] value = getattr(self, key_name) except KeyError: value = self._get_field_helper(n) return value
[docs] def write_code_aster(self): return ('# skipping %s because write_code_aster is not implemented\n' % self.type)
[docs] def _verify(self, xref): """ Verifies all methods for this object work :param self: the object pointer :param xref: has this model been cross referenced :type xref: bool """ print('# skipping _verify (type=%s) because _verify is ' 'not implemented\n' % self.type)
[docs] def _is_same_fields(self, fields1, fields2): for (field1, field2) in zip(fields1, fields2): if not is_same(field1, field2): return False return True
[docs] def _is_same_card(self, card): if self.type != card.type: return False fields1 = self.raw_fields() fields2 = card.raw_fields() return self._is_same_fields(fields1, fields2)
[docs] def print_raw_card(self, size=8, is_double=False): ## TODO: needs to be fixed """A card's raw fields include all defaults for all fields""" list_fields = self.raw_fields() return print_card(list_fields, size=size, is_double=False)
[docs] def repr_fields(self): """gets the card's fields""" return self.raw_fields()
[docs] def print_card(self, size=8, is_double=False): list_fields = self.repr_fields() if size == 8: return self.comment() + print_card_8(list_fields) elif is_double: return self.comment() + print_card_double(list_fields) return self.comment() + print_card_16(list_fields)
[docs] def print_repr_card(self, size=8, is_double=False): list_fields = self.repr_fields() if size == 16: if is_double: return print_card_double(list_fields) return print_card_16(list_fields) return print_card_8(list_fields)
def __repr__(self): """ Prints a card in the simplest way possible (default values are left blank). """ try: return self.print_card(size=8) except: try: return self.print_card(size=16) except: print('problem printing %s card' % self.type) fields = self.repr_fields() print("fields = ", fields) raise
[docs]class Property(BaseCard): def __init__(self, card, data): assert card is None or data is None
[docs] def Pid(self): """ returns the property ID of an property :param self: the Property pointer :returns pid: the Property ID :type pid: int """ return self.pid
[docs] def Mid(self): """ returns the material ID of an element :param self: the Property pointer :returns mid: the Material ID :type mid: int """ if isinstance(self.mid, int): return self.mid else: return self.mid.mid
[docs] def cross_reference(self, model): msg = ' which is required by %s pid=%s' % (self.type, self.pid) self.mid = model.Material(self.mid, msg)
[docs]class Material(BaseCard): """Base Material Class""" def __init__(self, card, data): BaseCard.__init__(self)
[docs] def cross_reference(self, model): pass
[docs] def Mid(self): """ returns the material ID of an element :param self: the Property pointer :returns mid: the Material ID :type mid: int """ return self.mid
[docs]class Element(BaseCard, ElementDeprecated): pid = 0 # CONM2, rigid def __init__(self, card, data): BaseCard.__init__(self) assert card is None or data is None #: the list of node IDs for an element (default=None) self.nodes = None
[docs] def Pid(self): """ returns the property ID of an element :param self: the Element pointer :returns pid: the Property ID :type pid: int """ if isinstance(self.pid, int): return self.pid else: return self.pid.pid
[docs] def get_node_positions(self, nodes=None): """returns the positions of multiple node objects""" if not nodes: nodes = self.nodes positions = [] for node in nodes: if node is not None: positions.append(node.Position()) else: positions.append(None) return positions
[docs] def _nodeIDs(self, nodes=None, allowEmptyNodes=False, msg=''): """returns nodeIDs for repr functions""" try: if not nodes: nodes = self.nodes if allowEmptyNodes: nodes2 = [] for i, node in enumerate(nodes): #print("node=%r type=%r" % (node, type(node))) if node == 0 or node is None: nodes2.append(None) elif isinstance(node, int): nodes2.append(node) else: nodes2.append(node.nid) return nodes2 else: try: nodeIDs = [] for i, node in enumerate(nodes): #print("node=%r type=%r" % (node, type(node))) if isinstance(node, int): nodeIDs.append(node) else: nodeIDs.append(node.nid) #if isinstance(nodes[0], int): #nodeIDs = [node for node in nodes] #else: #nodeIDs = [node.nid for node in nodes] except: print('type=%s nodes=%s allowEmptyNodes=%s\nmsg=%s' % ( self.type, nodes, allowEmptyNodes, msg)) raise assert 0 not in nodeIDs, 'nodeIDs = %s' % nodeIDs return nodeIDs except: print('type=%s nodes=%s allowEmptyNodes=%s\nmsg=%s' % ( self.type, nodes, allowEmptyNodes, msg)) raise
[docs] def prepare_node_ids(self, nids, allow_empty_nodes=False): """Verifies all node IDs exist and that they're integers""" self.nodes = [] for nid in nids: if isinstance(nid, integer_types): self.nodes.append(nid) elif nid is None and allow_empty_nodes: self.nodes.append(None) else: # string??? #self.nodes.append(int(nid)) msg = 'this element may have missing nodes...\n' msg += 'nids=%s allow_empty_nodes=False;\ntype(nid)=%s' % (nids, type(nid)) raise RuntimeError(msg)
@property # I think this means you can just call it as an attribute...
[docs] def faces(self): """ Gets the faces of the element :returns: dictionary with face number as the keys and a list of nodes (integer pointers) as the values. .. note:: The order of the nodes are consistent with ANSYS numbering. Example ======= >>> print element.faces """ faces = {} nodes = self.node_ids if self.type.find('HEX') != -1: # CHEXA faces[1] = [nodes[0], nodes[1], nodes[2], nodes[3]] faces[2] = [nodes[0], nodes[1], nodes[5], nodes[4]] faces[3] = [nodes[1], nodes[2], nodes[6], nodes[5]] faces[4] = [nodes[2], nodes[3], nodes[7], nodes[6]] faces[5] = [nodes[3], nodes[0], nodes[4], nodes[7]] faces[6] = [nodes[4], nodes[5], nodes[6], nodes[7]] elif self.type.find('TET') != -1: # CTETRA faces[1] = [nodes[0], nodes[1], nodes[2]] faces[2] = [nodes[0], nodes[1], nodes[3]] faces[3] = [nodes[1], nodes[2], nodes[3]] faces[4] = [nodes[2], nodes[0], nodes[3]] elif self.type.find('PYR') != -1: # PYRAMID faces[1] = [nodes[0], nodes[1], nodes[2], nodes[3]] faces[2] = [nodes[0], nodes[1], nodes[4]] faces[3] = [nodes[1], nodes[2], nodes[4]] faces[4] = [nodes[2], nodes[3], nodes[4]] faces[5] = [nodes[3], nodes[0], nodes[4]] elif self.type.find('PEN') != -1: # CPENTA faces[1] = [nodes[0], nodes[1], nodes[2]] faces[2] = [nodes[3], nodes[4], nodes[5]] faces[3] = [nodes[0], nodes[1], nodes[4], nodes[3]] faces[4] = [nodes[1], nodes[2], nodes[5], nodes[4]] faces[5] = [nodes[2], nodes[0], nodes[3], nodes[5]] elif self.type.find('QUAD') != -1: # CQUADx # both sides faces[1] = [nodes[0], nodes[1], nodes[2], nodes[3]] faces[2] = [nodes[1], nodes[0], nodes[3], nodes[2]] elif self.type.find('TRI') != -1: # CTRIAx # both sides faces[1] = [nodes[0], nodes[1], nodes[2]] faces[2] = [nodes[1], nodes[0], nodes[2]] else: faces = None return faces
[docs] def nodes2face(self, nodes): """ returns the face number that matches the list of nodes input :param nodes: list of nodes :returns faces: the face number as an integer .. warning:: It's assumed you have the nodes in the proper order. """ assert isinstance(nodes, list), 'nodes=%s' % str(nodes) face = None for i in self.faces.keys(): chck = self.faces[i] if nodes == chck: face = i return face
[docs]def expand_thru(fields, set_fields=True, sort_fields=False): """ Expands a list of values of the form [1,5,THRU,9,13] to be [1,5,6,7,8,9,13] :param fields: the fields to expand :param set_fields: should the fields be converted to a set and then back to a list? This is useful for [2, 'THRU' 5, 1] (default=True) :param sort_fields: should the fields be sorted at the end? (default=False) """ # ..todo: should this be removed...is the field capitalized when read in? fields = [field.upper() if isinstance(field, string_types) else field for field in fields] if isinstance(fields, int): return [fields] if len(fields) == 1: return [int(fields[0])] out = [] nfields = len(fields) i = 0 while i < nfields: if fields[i] == 'THRU': istart = int(fields[i - 1]) iend = int(fields[i + 1]) for j in range(istart, iend + 1): # adding 1 to iend for the range offset out.append(j) i += 2 else: out.append(int(fields[i])) i += 1 if set_fields: out = list(set(out)) if sort_fields: out.sort() return out
[docs]def expand_thru_by(fields, set_fields=True, sort_fields=False): """ Expands a list of values of the form [1,5,THRU,9,BY,2,13] to be [1,5,7,9,13] :param fields: the fields to expand :param set_fields: should the fields be converted to a set and then back to a list? This is useful for [2, 'THRU' 5, 1] (default=True) :param sort_fields: should the fields be sorted at the end? (default=False) .. todo:: not tested .. note:: used for QBDY3 and what else ??? """ # ..todo: should this be removed...is the field capitalized when read in? fields = [field.upper() if isinstance(field, string_types) else field for field in fields] if len(fields) == 1: return [interpret_value(fields[0])] out = [] nfields = len(fields) i = 0 by = 1 while i < nfields: if fields[i] == 'THRU': by = 1 byCase = False if i + 2 < nfields and fields[i + 2] == 'BY': by = interpret_value(fields[i + 3]) else: by = 1 byCase = True min_value = interpret_value(fields[i - 1]) max_value = interpret_value(fields[i + 1]) maxR = int((max_value - min_value) // by + 1) # max range value for j in range(0, maxR): # +1 is to include final point value = min_value + by * j out.append(value) if byCase: # null/standard case i += 2 else: # BY case i += 3 else: out.append(interpret_value(fields[i])) i += 1 if set_fields: out = list(set(out)) if sort_fields: out.sort() return out
[docs]def expand_thru_exclude(self, fields): """ Expands a list of values of the form [1,5,THRU,11,EXCEPT,7,8,13] to be [1,5,6,9,10,11,13] .. warning:: hasnt been tested """ # ..todo: should this be removed...is the field capitalized when read in? fields = [interpret_value(field.upper()) if isinstance(field, string_types) else field for field in fields] fields_out = [] nfields = len(fields) for i in range(nfields): if fields[i] == 'THRU': sorted_list = [] for j in range(fields[i - 1], fields[i + 1]): sorted_list.append(fields[j]) elif fields[i] == 'EXCLUDE': stored_set = set(sorted_list) while fields[i] < max(sorted_list): stored_set.remove(fields[i]) sorted_list = list(stored_set) else: if sorted_list: fields_out += sorted_list fields_out.append(fields[i]) return fields_out
[docs]def collapse_thru_by(fields, get_packs=False): """ :param fields: the list of fields to collapse :param get_packs: get the list of packs so "special" formatting can be done fields packs [1, 2, 3...150] -> [1, 150, 1] [1, 3, 5...150] -> [1, 150, 2] """ assert 'THRU' not in fields, fields fields.sort() packs = condense(fields) if get_packs: return packs fields2 = build_thru(packs) #assert fields == expand_thru_by(fields2) # why doesn't this work? return fields2
[docs]def collapse_thru_by_float(fields): assert 'THRU' not in fields, fields fields.sort() packs = condense(fields) fields2 = build_thru_float(packs) #assert fields == expand_thru_by(fields2) # why doesn't this work? return fields2
[docs]def collapse_thru(fields): assert 'THRU' not in fields, fields fields.sort() packs = condense(fields) fields2 = build_thru(packs, maxDV=1) #assert fields == expand_thru_by(fields2), fields2 # why doesn't this work? return fields2
[docs]def collapse_thru_packs(fields): assert 'THRU' not in fields, fields fields.sort() packs = condense(fields) singles, doubles = build_thru_packs(packs, maxDV=1) #assert fields == expand_thru_by(fields2), fields2 # why doesn't this work? return singles, doubles
[docs]def condense(value_list): """ Builds a list of packs (list of 3 values representing the first, last, and delta values for condensing a SET card. .. seealso:: build_thru """ if len(value_list) == 0: return [] if len(value_list) == 1: return [[value_list[0], value_list[0], 1]] value_list.sort() packs = [] dv_old = None firstVal = value_list[0] lastVal = firstVal for val in value_list[1:]: try: dv = val - lastVal except TypeError: print("lastVal=%r val=%r" % (lastVal, val)) print("valueList=%r" % value_list) raise # sets up the first item of the pack if dv_old is None: dv_old = dv # fill up the pack if dv_old == dv: lastVal = val else: packs.append([firstVal, lastVal, dv_old]) lastVal = val dv_old = None firstVal = val # fills the last pack if dv_old == dv: packs.append([firstVal, val, dv]) else: packs.append([firstVal, val, dv_old]) return packs
[docs]def build_thru_packs(packs, maxDV=1): """ # invalid SET1,4000, 1, 3, THRU, 10, 20, THRU, 30 # valid SET1,4000, 1 SET1,4000, 3, THRU, 10 SET1,4000, 20, THRU, 30 returns singles = [1] doubles = [[3, 'THRU', 10], [20, 'THRU', 30]] """ singles = [] doubles = [] for (first_val, last_val, by) in packs: if first_val == last_val: singles.append(first_val) else: if by == 1: if last_val - first_val < 3: # dont make extra THRU cards for val in range(first_val, last_val + 1): singles.append(val) else: double = [first_val, 'THRU', last_val] doubles.append(double) else: for val in range(first_val, last_val + 1, by): singles.append(val) return singles, doubles
[docs]def build_thru(packs, maxDV=None): """ Takes a pack [1,7,2] and converts it into fields used by a SET card. The values correspond to the first value, last value, and delta in the list. This means that [1,1001,2] represents 500 values. [1,1001,1] represents 1001 values and will be written as [1,THRU,1001].. :param packs: list of packs (list of 3 values: [first, last, delta] ) :param maxDV: integer defining the max allowable delta between two values (default=None; no limit) """ fields = [] for (first_val, last_val, dv) in packs: if first_val == last_val: fields.append(first_val) elif dv == 1: if last_val - first_val > 2: fields.append(first_val) fields.append('THRU') fields.append(last_val) elif last_val - first_val == 2: fields.append(first_val) fields.append(first_val + 1) fields.append(last_val) else: fields.append(first_val) fields.append(last_val) else: if maxDV is None: if last_val - first_val > 4 * dv: fields.append(first_val) fields.append('THRU') fields.append(last_val) fields.append('BY') fields.append(dv) else: for v in range(first_val, last_val + dv, dv): fields.append(v) else: for v in range(first_val, last_val + dv, dv): fields.append(v) return fields
[docs]def build_thru_float(packs, maxDV=None): """ Takes a pack [1,7,2] and converts it into fields used by a SET card. The values correspond to the first value, last value, and delta in the list. This means that [1,1001,2] represents 500 values. [1,1001,1] represents 1001 values and will be written as [1,THRU,1001].. :param packs: list of packs (list of 3 values: [first, last, delta] ) :param maxDV: integer defining the max allowable delta between two values (default=None; no limit) """ fields = [] for (first_val, last_val, dv) in packs: if last_val - first_val > 4 * dv: fields.append(first_val) fields.append('THRU') fields.append(last_val) fields.append('BY') fields.append(dv) else: nv = int(round((last_val - first_val) / dv)) + 1 for i in range(nv): v = first_val + i * dv fields.append(v) return fields