# pylint: disable=R0904,R0902
"""
All constraint cards are defined in this file. This includes:
* Constraint
* SUPORT
* SUPORT1
* SPC
* SPC1
* SPCAX
* SPCD
* MPC
* ConstraintADD
* SPCADD
* MPCADD
The ConstraintObject contain multiple constraints.
"""
from __future__ import (nested_scopes, generators, division, absolute_import,
print_function, unicode_literals)
from six import iteritems
from six.moves import zip, range
from itertools import count
from pyNastran.bdf.cards.baseCard import BaseCard, expand_thru
from pyNastran.bdf.bdfInterface.assign_type import (integer, integer_or_blank,
double, double_or_blank, components, components_or_blank)
from pyNastran.bdf.field_writer_8 import print_card_8, print_float_8
from pyNastran.bdf.field_writer_16 import print_float_16
from pyNastran.bdf.field_writer_double import print_scientific_double
[docs]class ConstraintObject(object):
def __init__(self):
self.constraints = {} # SPC, SPC1, SPCD, etc...
self.add_constraints = {} # SPCADD
[docs] def Add(self, ADD_Constraint):
"""Bad name, but adds an SPCADD or MPCADD"""
key = ADD_Constraint.conid
if key in self.add_constraints:
print("already has key...key=%s\n%s" % (key, str(ADD_Constraint)))
else:
self.add_constraints[key] = ADD_Constraint
[docs] def append(self, constraint):
key = constraint.conid
if key in self.constraints:
#print("already has key...key=%s\n%s" %(key,str(constraint)))
self.constraints[key].append(constraint)
else:
self.constraints[key] = [constraint]
[docs] def _spc(self, spcID):
"""could be an spcadd/mpcadd or spc/mpc,spc1,spcd,spcax,suport1"""
if spcID in self.add_constraints:
return self.add_constraints[spcID]
return self.constraints[spcID]
[docs] def cross_reference(self, model):
#return
#add_constraints2 = {}
for key, add_constraint in sorted(iteritems(self.add_constraints)):
for i, spcID in enumerate(add_constraint.sets):
add_constraint.sets[i] = self._spc(spcID)
self.add_constraints[key] = add_constraint
#self.add_constraints = add_constraints2
constraints2 = {}
for key, constraints in sorted(iteritems(self.constraints)):
constraints2[key] = []
for constraint in constraints:
constraints2[key].append(constraint.cross_reference(model))
self.constraints = constraints2
[docs] def createConstraintsForID(self):
"""
This function returns all the constraints with an given constraint ID.
For example an MPCADD that references 2 MPCADDs which reference 4 MPCs
should return 4 MPCs (or rather the IDs of those MPCs).
.. todo:: This function *should* also find unassociated constraints.
not really done yet, idea needs to be integrated/separated from
cross-referencing. no point in doing it twice
"""
raise NotImplementedError()
constraints2 = {}
referencedConstraints = {}
# some of the ADDConstraint keys are MPCADDs/SPCADDs, some are not
for key, add_constraint in sorted(iteritems(self.add_constraints)):
constraints = []
for i, spc_id in enumerate(add_constraint.sets):
constraintIDs = add_constraint.getConstraintIDs()
constraints[spc_id] = constraintIDs
constraints += constraintIDs
#constraints.append(spc_id)
constraints2[key] = [spc_id]
constraints2[key] = constraints
# not needed b/c there are no MPCADD/SPCADD
#for key, constraints in sorted(iteritems(self.constraints)):
#for constraint in constraints:
#conID = constraint.ConID()
#constraints2[conID]
constraints3 = self.remapSPCs(constraints2)
# def remapSPCs(self, constraints):
#"""not really done yet"""
## takes the MPCADDs that reference MPCADDs and makes them
## reference MPCs
#constraints2 = {}
#key = constraints.keys()
#nkeys = len(key) - 1
#for i in range(nkeys):
#key = keys[i]
#constraints2[key]
#for j in range(nkeys):
#if i > j:
#constraints2[key].append(constraints[key])
#return constraints2
[docs] def ConstraintID(self):
if isinstance(self.conid, int):
return self.conid
return self.conid.conid
[docs] def getConstraintIDs(self):
IDs = []
for key, constraints in sorted(iteritems(self.add_constraints)):
conID = constraints.ConID()
IDs.append(conID)
for key, constraints in sorted(iteritems(self.constraints)):
for constraint in constraints:
conID = constraint.ConID()
IDs.append(conID)
IDs = list(set(IDs))
IDs.sort()
return IDs
def __repr__(self):
msg = ''
# write the SPCADD/MPCADD cards
for key, add_constraint in sorted(iteritems(self.add_constraints)):
msg += str(add_constraint)
for key, constraints in sorted(iteritems(self.constraints)):
for constraint in constraints:
msg += str(constraint)
return msg
[docs]class Constraint(BaseCard):
def __init__(self, card, data):
pass
[docs] def raw_fields(self):
fields = [self.type, self.conid]
return fields
[docs]class SUPORT1(Constraint):
"""
+---------+-----+-----+----+-----+----+-----+----+
| SUPORT1 | SID | ID1 | C1 | ID2 | C2 | ID3 | C3 |
+---------+-----+-----+----+-----+----+-----+----+
| SUPORT1 | 1 | 2 | 23 | 4 | 15 | 5 | 0 |
+---------+-----+-----+----+-----+----+-----+----+
"""
type = 'SUPORT1'
def __init__(self, card=None, data=None, comment=''):
Constraint.__init__(self, card, data)
self.IDs = []
self.Cs = []
if comment:
self._comment = comment
if card:
self.conid = integer(card, 1, 'conid') # really a support id sid
nfields = len(card)
assert len(card) > 2
nterms = int((nfields - 1.) / 2.)
n = 1
for i in range(nterms):
nstart = 2 + 2 * i
ID = integer(card, nstart, 'ID%s' % n)
C = components_or_blank(card, nstart + 1, 'component%s' % n, '0')
self.IDs.append(ID)
self.Cs.append(C)
n += 1
else:
msg = '%s has not implemented data parsing' % self.type
raise NotImplementedError(msg)
assert len(self.IDs) > 0
assert len(self.IDs) == len(self.Cs)
[docs] def raw_fields(self):
fields = ['SUPORT1', self.conid]
for ID, c in zip(self.IDs, self.Cs):
fields += [ID, c]
return fields
[docs] def write_card(self, size=8, is_double=False):
card = self.raw_fields()
return self.comment() + print_card_8(card)
[docs]class SUPORT(Constraint):
"""
::
SUPORT ID1 C1 ID2 C2 ID3 C3 ID4 C4
SUPORT1 SID ID1 C1 ID2 C2 ID3 C3
"""
type = 'SUPORT'
def __init__(self, card=None, data=None, comment=''):
Constraint.__init__(self, card, data)
if comment:
self._comment = comment
self.IDs = []
self.Cs = []
if card:
fields = card.fields(1)
nfields = len(card)
assert len(card) > 1, card
nterms = int(nfields / 2.)
n = 1
for i in range(nterms):
nstart = 1 + 2 * i
ID = integer(card, nstart, 'ID%s' % n)
C = components_or_blank(card, nstart + 1, 'component%s' % n, '0')
self.IDs.append(ID)
self.Cs.append(C)
n += 1
else:
fields = data
for i in range(0, len(fields), 2):
self.IDs.append(fields[i])
self.Cs.append(fields[i + 1])
assert len(self.IDs) > 0
assert len(self.IDs) == len(self.Cs)
[docs] def raw_fields(self):
fields = ['SUPORT']
for ID, c in zip(self.IDs, self.Cs):
fields += [ID, c]
return fields
[docs] def write_card(self, size=8, is_double=False):
card = self.raw_fields()
return self.comment() + print_card_8(card)
[docs]class MPC(Constraint):
type = 'MPC'
def __init__(self, card=None, data=None, comment=''):
Constraint.__init__(self, card, data)
if comment:
self._comment = comment
if card:
#: Set identification number. (Integer > 0)
self.conid = integer(card, 1, 'conid')
#: Identification number of grid or scalar point. (Integer > 0)
self.gids = []
#: Component number. (Any one of the Integers 1 through 6 for grid
#: points; blank or zero for scalar points.)
self.constraints = []
#: Coefficient. (Real; Default = 0.0 except A1 must be nonzero.)
self.enforced = []
fields = card.fields(0)
nfields = len(fields)
i = 1
for ifield in range(2, nfields, 8):
grid = integer(card, ifield, 'G%i' % i)
component = components_or_blank(card, ifield + 1, 'constraint%i' % i, 0) # scalar point
if i == 1:
value = double(card, ifield + 2, 'enforced%i' % i)
if value == 0.0:
raise RuntimeError('enforced1 must be nonzero; value=%r' % value)
else:
value = double_or_blank(card, ifield + 2, 'enforced%i' % i, 0.0)
self.gids.append(grid)
self.constraints.append(component)
self.enforced.append(value)
i += 1
if ifield + 4 > nfields and i != 2:
# if G2 is empty (it's ifield+4 because nfields is length based and not loop friendly)
break
grid = integer(card, ifield + 3, 'G%i' % i)
component = components_or_blank(card, ifield + 4, 'constraint%i' % i, 0) # scalar point
value = double_or_blank(card, ifield + 5, 'enforced%i' % i)
self.gids.append(grid)
self.constraints.append(component)
self.enforced.append(value)
i += 1
# reduce the size if there are duplicate Nones
#nConstraints = max(len(self.gids ),
# len(self.constraints),
# len(self.enforced ))
#self.gids = self.gids[ 0:nConstraints]
#self.constraints = self.constraints[0:nConstraints]
#self.enforced = self.enforced[ 0:nConstraints]
else:
msg = '%s has not implemented data parsing' % self.type
raise NotImplementedError(msg)
[docs] def nodeIDs(self):
return self.gids
[docs] def raw_fields(self): # MPC
fields = ['MPC', self.conid]
for i, gid, constraint, enforced in zip(count(), self.gids, self.constraints, self.enforced):
fields += [gid, constraint, enforced]
if i % 2 == 1 and i > 0:
fields.append(None)
fields.append(None)
return fields
[docs] def write_card(self, size=8, is_double=False):
grids, constraints, enforceds = self.gids, self.constraints, self.enforced
if size == 8:
msg = 'MPC %8s' % self.conid
for i, grid, component, enforced in zip(count(), grids, constraints, enforceds):
msg += '%8i%8s%8s' % (grid, component, print_float_8(enforced))
if i % 2 == 1 and i > 0:
msg += '\n%8s%8s' % ('', '')
elif size == 16:
# TODO: we're sure MPCs support double precision?
msg = 'MPC* %16s' % self.conid
if is_double:
for i, grid, component, enforced in zip(count(), grids, constraints, enforceds):
msg += '%16i%16s%16s' % (grid, component, print_scientific_double(enforced))
if i % 2 == 1 and i > 0:
msg += '\n%8s%16s' % ('*', '')
else:
for i, grid, component, enforced in zip(count(), grids, constraints, enforceds):
msg += '%16i%16s%16s' % (grid, component, print_float_16(enforced))
if i % 2 == 1 and i > 0:
msg += '\n%8s%16s' % ('*', '')
return self.comment() + msg.rstrip() + '\n'
[docs]class SPC(Constraint):
"""
Defines enforced displacement/temperature (static analysis)
velocity/acceleration (dynamic analysis).
+-----+-----+----+----+------+----+----+----+
| SPC | SID | G1 | C1 | D1 | G2 | C2 | D2 |
+-----+-----+----+----+------+----+----+----+
| SPC | 2 | 32 | 3 | -2.6 | 5 | | |
+-----+-----+----+----+------+----+----+----+
"""
type = 'SPC'
def __init__(self, card=None, data=None, comment=''):
Constraint.__init__(self, card, data)
if comment:
self._comment = comment
if card:
self.conid = integer(card, 1, 'sid')
self.gids = [integer(card, 2, 'G1'), integer_or_blank(card, 5, 'G2')]
# :0 if scalar point 1-6 if grid
self.constraints = [components_or_blank(card, 3, 'C1', 0),
components_or_blank(card, 6, 'C2', 0)]
self.enforced = [double_or_blank(card, 4, 'D1', 0.0),
double_or_blank(card, 7, 'D2', 0.0)]
# reduce the size if there are duplicate Nones
nConstraints = max(len(self.gids),
len(self.constraints),
len(self.enforced))
self.gids = self.gids[0:nConstraints]
self.constraints = self.constraints[0:nConstraints]
self.enforced = self.enforced[0:nConstraints]
else:
self.conid = data[0]
self.gids = [data[1]]
self.constraints = [data[2]]
self.enforced = [data[3]]
[docs] def getNodeDOFs(self, model):
pass
#return conid, dofs
[docs] def nodeIDs(self):
return self.gids
[docs] def cross_reference(self, i, node):
dof_count = 0
self.gids[i] = node
#for (i,constraint) in enumerate(self.constraints):
# if self.constraint is None:
# node = self.Node(self.gids[i])
# if not node.Is('GRID'): # SPOINT, EPOINT, DPOINT
# dofCount+=1
# else:
# dofCount+=6
return dof_count
[docs] def raw_fields(self):
fields = ['SPC', self.conid]
for (gid, constraint, enforced) in zip(self.gids, self.constraints,
self.enforced):
fields += [gid, constraint, enforced]
return fields
[docs] def write_card(self, size=8, is_double=False):
card = self.raw_fields()
return self.comment() + print_card_8(card)
[docs]class SPCD(SPC):
"""
Defines an enforced displacement value for static analysis and an enforced
motion value (displacement, velocity or acceleration) in dynamic analysis.
+------+-----+-----+-----+------+----+---+----+
| SPCD | SID | G1 | C1 | D1 | G2 |C2 | D2 |
+------+-----+-----+-----+------+----+---+----+
| SPCD | 100 | 32 | 436 | -2.6 | 5 | 2 | .9 |
+------+-----+-----+-----+------+----+---+----+
"""
type = 'SPCD'
def __init__(self, card=None, data=None, comment=''):
# defines everything :) at least until cross-referencing methods are
# implemented
SPC.__init__(self, card, data)
if comment:
self._comment = comment
[docs] def raw_fields(self):
fields = ['SPCD', self.conid]
for (gid, constraint, enforced) in zip(self.gids, self.constraints,
self.enforced):
fields += [gid, constraint, enforced]
return fields
[docs] def write_card(self, size=8, is_double=False):
card = self.raw_fields()
return self.comment() + print_card_8(card)
[docs]class SPCAX(Constraint):
"""
Defines a set of single-point constraints or enforced displacements
for conical shell coordinates.
+-------+-----+-----+-----+----+-----+
| SPCAX | SID | RID | HID | C | D |
+-------+-----+-----+-----+----+-----+
| SPCAX | 2 | 3 | 4 | 13 | 6.0 |
+-------+-----+-----+-----+----+-----+
"""
type = 'SPCAX'
def __init__(self, card=None, data=None, comment=''):
# defines everything :) at least until cross-referencing methods are
# implemented
SPC.__init__(self, card, data)
if comment:
self._comment = comment
if card:
#: Identification number of a single-point constraint set.
self.conid = integer(card, 1, 'conid')
#: Ring identification number. See RINGAX entry.
self.rid = integer(card, 2, 'rid')
#: Harmonic identification number. (Integer >= 0)
self.hid = integer(card, 3, 'hid')
#: Component identification number. (Any unique combination of the
#: Integers 1 through 6.)
self.c = components(card, 4, 'c')
#: Enforced displacement value
self.d = double(card, 5, 'd')
else:
msg = '%s has not implemented data parsing' % self.type
raise NotImplementedError(msg)
[docs] def cross_reference(self, i, node):
pass
[docs] def raw_fields(self):
fields = ['SPCAX', self.conid, self.rid, self.hid, self.c, self.d]
return fields
[docs] def write_card(self, size=8, is_double=False):
card = self.raw_fields()
return self.comment() + print_card_8(card)
[docs]class SPC1(Constraint):
"""
+------+-----+----+----+--------+----+----+----+----+
| SPC1 | SID | C | G1 | G2 | G3 | G4 | G5 | G6 |
+------+-----+----+----+--------+----+----+----+----+
| | G7 | G8 | G9 | -etc.- | | | | |
+------+-----+----+----+--------+----+----+----+----+
+------+---+-----+--------+--------+--------+--------+--------+---+
| SPC1 | 3 | 246 | 209075 | 209096 | 209512 | 209513 | 209516 | |
+------+---+-----+--------+--------+--------+--------+--------+---+
| SPC1 | 3 | 2 | 1 | 3 | 10 | 9 | 6 | 5 |
+------+---+-----+--------+--------+--------+--------+--------+---+
| | 2 | 8 | | | | | | |
+------+---+-----+--------+--------+--------+--------+--------+---+
+------+-----+-------+----+------+----+
| SPC1 | SID | C | G1 | THRU | G2 |
+------+-----+-------+----+------+----+
| SPC1 | 313 | 12456 | 6 | THRU | 32 |
+------+-----+-------+----+------+----+
"""
type = 'SPC1'
def __init__(self, card=None, data=None, comment=''):
Constraint.__init__(self, card, data)
if comment:
self._comment = comment
if card:
self.conid = integer(card, 1, 'conid')
self.constraints = components(card, 2, 'constraints') # 246 = y; dx, dz dir
nodes = card.fields(3)
self.nodes = expand_thru(nodes)
self.nodes.sort()
else:
self.conid = data[0]
self.constraints = data[1]
self.nodes = data[2:]
[docs] def cross_reference(self, i, node):
self.nodes[i] = node
[docs] def raw_fields(self): # SPC1
#test = [i for i in range(self.nodes[0], self.nodes[-1]+1)]
#print("self.nodes = ",self.nodes)
#print("test = ",test)
#if self.nodes == test:
# nodes = [self.nodes[0], 'THRU', self.nodes[-1]]
#else:
#print("SPC1 self.nodes = ",self.nodes)
nodes = [int(i) for i in self.nodes] # SPC1
fields = ['SPC1', self.conid, self.constraints] + nodes
return fields
[docs] def write_card(self, size=8, is_double=False):
card = self.raw_fields()
return self.comment() + print_card_8(card)
#class ADDConstraint(Constraint):
# def __init__(self,card,data):
# self.__init__(Constraint)
[docs]class ConstraintADD(Constraint):
def __init__(self, card, data):
Constraint.__init__(self, card, data)
[docs] def _reprSpcMpcAdd(self, fields):
outSPCs = ''
fieldSets = []
#print("repr---mpcadd")
#print(self.sets)
for spcsets in sorted(self.sets):
#print("*spcsets",spcsets)
if isinstance(spcsets, int): # spcset wasnt found
#print("int unfound...%s" %(spcsets))
#outSPCs += str(spcsets)
fields.append(spcsets)
elif isinstance(spcsets, list):
for spcset in sorted(spcsets):
fieldSets.append(spcset.conid)
outSPCs += str(spcset)
else:
# dict
#outSPCs += str(spcsets.conid)
for (key, spcset) in sorted(iteritems(spcsets)):
fieldSets.append(spcsets.conid)
# SPCADD
return self.print_card(fields + list(set(fieldSets))) + outSPCs
[docs]class SPCADD(ConstraintADD):
"""
Defines a single-point constraint set as a union of single-point constraint
sets defined on SPC or SPC1 entries.
+---------+---+---+------+
| SPCADD | 2 | 1 | 3 |
+---------+---+---+------+
"""
type = 'SPCADD'
def __init__(self, card=None, data=None, comment=''):
ConstraintADD.__init__(self, card, data)
if comment:
self._comment = comment
if card:
self.conid = integer(card, 1, 'conid')
sets = card.fields(2)
self.sets = expand_thru(sets)
self.sets.sort()
else:
msg = '%s has not implemented data parsing' % self.type
raise NotImplementedError(msg)
[docs] def organizeConstraints(self, model):
"""
Figures out magnitudes of the loads to be applied to the various nodes.
This includes figuring out scale factors.
"""
position_spcs = []
types_found = ['SPCADD']
(scale_factors, loads) = self.getReducedConstraints()
return (types_found, position_spcs)
[docs] def cross_reference(self, i, node):
self.sets.sort()
self.sets[i] = node
[docs] def raw_fields(self):
fields = ['SPCADD', self.conid] # +self.sets
for set_id in self.sets:
fields.append(set_id)
return fields
#return self._reprSpcMpcAdd(fields)
[docs] def write_card(self, size=8, is_double=False):
card = self.raw_fields()
return self.comment() + print_card_8(card)
[docs]class MPCADD(ConstraintADD):
r"""
Defines a multipoint constraint equation of the form
:math:`\Sigma_j A_j u_j =0` where :math:`u_j` represents
degree-of-freedom :math:`C_j` at grid or scalar point :math:`G_j`.
mPCADD 2 1 3
"""
type = 'MPCADD'
def __init__(self, card=None, data=None, comment=''):
ConstraintADD.__init__(self, card, data)
if comment:
self._comment = comment
if card:
self.conid = integer(card, 1, 'conid')
sets = card.fields(2)
self.sets = expand_thru(sets)
self.sets.sort()
else:
msg = '%s has not implemented data parsing' % self.type
raise NotImplementedError(msg)
[docs] def cross_reference(self, i, node):
self.sets.sort()
self.sets[i] = node
[docs] def raw_fields(self):
fields = ['MPCADD', self.conid] # +self.sets
for set_id in self.sets:
fields.append(set_id)
return fields
#return self._reprSpcMpcAdd(fields)
[docs] def write_card(self, size=8, is_double=False):
card = self.raw_fields()
return self.comment() + print_card_8(card)