Check Member End Forces

This notebook reads files describing a structure, and the files output by Frame2D after an analysis, and checks that the member end forces computed here from the displacements and member loads agree with those computed by Frame2D.

It does this in the simplest way possible, using quite different logic than Frame2D, resulting in a higher degree of confidence in the results. It would have been better had someone else programmed it, but oh well ...


In [19]:
ds = 'KG82'
lcase = 'all'
#ds = 'l22x6'
#lcase = 'Case-2b'

def filename(basename,lc=None):
    if lc is not None:
        basename = lc + '/' + basename
    return 'data/' + ds + '.d/' + basename + '.csv'

def Warn(msg):
    print('!!!!! Warning: {}'.format(msg))

In [20]:
import pandas as pd
import math

In [21]:
class Node(object):
    
    def __init__(self,id,x,y):
        self.id = id
        self.x = x
        self.y = y
        self.deltaX = 0.
        self.deltaY = 0.
        self.thetaZ = 0.

In [22]:
ntable = pd.read_csv(filename('nodes'))
NODES = {}
for i,n in ntable.iterrows():
    if n.NODEID in NODES:
        Warn("Node '{}' is multiply defined.".format(n.NODEID))
    NODES[n.NODEID] = Node(n.NODEID,float(n.X),float(n.Y))
#ntable

In [23]:
dtable = pd.read_csv(filename('node_displacements',lcase))
for i,n in dtable.iterrows():
    node = NODES[n.NODEID]
    node.deltaX = float(n.DX)
    node.deltaY = float(n.DY)
    node.thetaZ = float(n.RZ)
dtable


Out[23]:
NODEID DX DY RZ
0 A 0.000000 0.000000 0.000000
1 B 26.392119 -2.613585 -0.007546
2 C 41.058763 -3.621538 -0.007561
3 D 0.000000 0.000000 0.000000
4 E 26.744537 -2.803081 0.002720
5 F 40.706144 -3.857628 0.005554
6 G 26.888652 -1.321138 -0.003266
7 H 40.801376 -1.824187 -0.002161
8 I 0.000000 0.000000 -0.004572
9 J 0.000000 0.000000 -0.004434
10 K 26.378272 -1.321138 -0.003307
11 L 41.071354 -1.824187 -0.002354

12 rows × 4 columns


In [24]:
pd.DataFrame([vars(v) for v in NODES.values()]).set_index('id')


Out[24]:
deltaX deltaY thetaZ x y
id
D 0.000000 0.000000 0.000000 20500 0
B 26.392119 -2.613585 -0.007546 10000 6500
K 26.378272 -1.321138 -0.003307 0 6500
J 0.000000 0.000000 -0.004434 0 0
C 41.058763 -3.621538 -0.007561 10000 12000
F 40.706144 -3.857628 0.005554 20500 12000
G 26.888652 -1.321138 -0.003266 30500 6500
L 41.071354 -1.824187 -0.002354 0 12000
A 0.000000 0.000000 0.000000 10000 0
H 40.801376 -1.824187 -0.002161 30500 12000
E 26.744537 -2.803081 0.002720 20500 6500
I 0.000000 0.000000 -0.004572 30500 0

12 rows × 5 columns


In [25]:
class Member(object):
    
    E = 200000.
    
    def __init__(self,id,nodej,nodek):
        self.id = id
        self.nodej = nodej
        self.nodek = nodek
        
        dx = nodek.x - nodej.x
        dy = nodek.y - nodej.y
        self.L = L = math.sqrt(dx*dx + dy*dy)
        self.cosx = dx/L
        self.cosy = dy/L
        
        self.Ix = 0.
        self.A = 0.
        self.loads = []
        self.releases = set()
        
        for a in 'FXJ FXK FYJ FYK MZJ MZK'.split():
            setattr(self,a,0.)

In [26]:
table = pd.read_csv(filename('members'))
MEMBERS = {}
for i,m in table.iterrows():
    if m.MEMBERID in MEMBERS:
        Warn("Member '{}' is multiply defined.".format(m.MEMBERID))
    MEMBERS[m.MEMBERID] = Member(m.MEMBERID,NODES[m.NODEJ],NODES[m.NODEK])

In [27]:
import sst
SST = sst.SST()
table = pd.read_csv(filename('properties'))
defIx = defA = None
for i,row in table.iterrows():
    if not pd.isnull(row.SIZE):
        defIx,defA = SST.section(row.SIZE,'Ix,A')
    memb = MEMBERS[row.MEMBERID]
    memb.Ix = float(defIx if pd.isnull(row.IX) else row.IX)
    memb.A = float(defA if pd.isnull(row.A) else row.A)
    if not pd.isnull(row.IX):
        defIx = row.IX
    if not pd.isnull(row.A):
        defA = row.A

In [28]:
try:
    lctable = pd.read_csv(filename('load_combinations'))
    use_all = False
    COMBO = {}
    for i,row in lctable.iterrows():
        if row.CASE == lcase:
            COMBO[row.LOAD.lower()] = row.FACTOR
except OSError:
    use_all = True
    COMBO = None
COMBO

In [29]:
table = pd.read_csv(filename('member_loads'))
for i,row in table.iterrows():
    memb = MEMBERS[row.MEMBERID]
    typ = row.TYPE
    f = 1.0 if use_all else COMBO.get(row.LOAD.lower(),0.)
    if f != 0.:
        w1 = None if pd.isnull(row.W1) else (float(row.W1)*f)
        w2 = None if pd.isnull(row.W2) else (float(row.W2)*f)
        a = None if pd.isnull(row.A) else float(row.A)
        b = None if pd.isnull(row.B) else float(row.B)
        c = None if pd.isnull(row.C) else float(row.C)
        memb.loads.append((typ,w1,w2,a,b,c))
#MEMBERS['LC'].loads

In [30]:
table = pd.read_csv(filename('releases'))
for i,row in table.iterrows():
    memb = MEMBERS[row.MEMBERID]
    memb.releases.add(row.RELEASE.upper())

In [31]:
t = pd.DataFrame([vars(v) for v in MEMBERS.values()]).set_index('id')
del t['nodej']
del t['nodek']
del t['loads']
t


Out[31]:
A FXJ FXK FYJ FYK Ix L MZJ MZK cosx cosy releases
id
EF 12300 0 0 0 0 222000000 5500 0 0 0 1 set([])
CF 13500 0 0 0 0 488000000 10500 0 0 1 0 set([])
JK 12300 0 0 0 0 222000000 6500 0 0 0 1 set([])
EG 13500 0 0 0 0 488000000 10000 0 0 1 0 set([MZJ, MZK])
LC 13500 0 0 0 0 488000000 10000 0 0 1 0 set([MZJ, MZK])
DE 12300 0 0 0 0 222000000 6500 0 0 0 1 set([])
FH 13500 0 0 0 0 488000000 10000 0 0 1 0 set([MZJ, MZK])
GH 12300 0 0 0 0 222000000 5500 0 0 0 1 set([])
AB 12300 0 0 0 0 222000000 6500 0 0 0 1 set([])
BC 12300 0 0 0 0 222000000 5500 0 0 0 1 set([])
KB 13500 0 0 0 0 488000000 10000 0 0 1 0 set([MZJ, MZK])
KL 12300 0 0 0 0 222000000 5500 0 0 0 1 set([])
BE 13500 0 0 0 0 488000000 10500 0 0 1 0 set([])
IG 12300 0 0 0 0 222000000 6500 0 0 0 1 set([])

14 rows × 12 columns


In [32]:
MEFS = pd.read_csv(filename('member_end_forces',lcase)).set_index('MEMBERID')
MEFS


Out[32]:
FXJ FYJ MZJ FXK FYK MZK
MEMBERID
AB 989141.474477 3625.020453 6.332451e+07 -989141.474477 -3625.020453 -3.976188e+07
BC 450829.917117 -86071.539660 -2.365727e+08 -450829.917117 86071.539660 -2.368208e+08
DE 1060858.525523 69040.008304 2.057977e+08 -1060858.525523 -69040.008304 2.429624e+08
EF 471670.082883 117583.181532 3.004759e+08 -471670.082883 -117583.181532 3.462316e+08
IG 500000.000000 2744.482817 4.639151e-08 -500000.000000 -2744.482817 1.783914e+07
GH 225000.000000 -3243.479692 -1.783914e+07 -225000.000000 3243.479692 1.173612e-08
JK 500000.000000 2368.101512 3.059540e-09 -500000.000000 -2368.101512 1.539266e+07
KL 225000.000000 -2798.665423 -1.539266e+07 -225000.000000 2798.665423 1.389708e-08
CF 90673.363911 225829.917117 2.368208e+08 -90673.363911 246670.082883 -3.462316e+08
BE -90621.641809 263311.557360 2.763346e+08 90621.641809 314188.442640 -5.434382e+08
FH -25712.591580 225000.000000 0.000000e+00 25712.591580 225000.000000 0.000000e+00
EG -38911.114770 275000.000000 0.000000e+00 38911.114770 275000.000000 0.000000e+00
KB -3738.873400 275000.000000 0.000000e+00 3738.873400 275000.000000 0.000000e+00
LC 3399.701061 225000.000000 0.000000e+00 -3399.701061 225000.000000 0.000000e+00

14 rows × 6 columns


In [33]:
cols = 'FXJ FXK FYJ FYK MZJ MZK'.split()
for m in MEMBERS.values():
    for a in cols:
        setattr(m,a,0.)
        
    # difference in end displacements, global coords
    dX = m.nodek.deltaX - m.nodej.deltaX
    dY = m.nodek.deltaY - m.nodej.deltaY
    
    # axial deformation / force:
    ldX = dX*m.cosx + dY*m.cosy
    T = m.E*m.A*ldX/m.L
    m.FXK += T
    m.FXJ += -T
    #print(m.id,ldX,T)
    
    # shear deformation / force:
    vdY = dY*m.cosx - dX*m.cosy
    M = -6.*m.E*m.Ix*vdY/(m.L*m.L)
    V = 2.*M/m.L
    m.MZJ += M
    m.MZK += M
    m.FYJ += V
    m.FYK += -V
    #print(m.id,vdY,M,V)
    
    # end rotations / moments:
    MJ = (m.E*m.Ix/m.L)*(4.*m.nodej.thetaZ + 2.*m.nodek.thetaZ)
    MK = (m.E*m.Ix/m.L)*(2.*m.nodej.thetaZ + 4.*m.nodek.thetaZ)
    VJ = (MJ+MK)/m.L
    m.MZJ += MJ
    m.MZK += MK
    m.FYJ += VJ
    m.FYK += -VJ
    #print(m.id,m.nodej.thetaZ,m.nodek.thetaZ,MJ,MK,VJ)
    
    # applied loads: fixed-end moments and shears:
    for ltype,w1,w2,a,b,c in m.loads:
        mj = mk = 0.
        vj = vk = 0.
        if ltype == 'PL':
            b = m.L - a
            P = w1
            mj = -P*a*b*b/(m.L*m.L)
            mk = P*b*a*a/(m.L*m.L)
            vj = (-P*b + nj + mk)/m.L
            vk = -P - vj
        elif ltype == 'UDL':
            mj = -w1*m.L**2/12.
            mk = -mj
            vj = -w1*m.L/2.
            vk = vj
        else:
            Warn("Load type '{}' not implemented here ...".format(ltype))
            continue
        m.MZJ += mj
        m.MZK += mk
        m.FYJ += vj
        m.FYK += vk
        
    # member end moment releases:
    relc = m.releases.copy()
    if 'MZJ' in m.releases:
        mj = -m.MZJ
        mk = 0. if 'MZK' in m.releases else 0.5*mj
        vj = (mj + mk)/m.L
        ##print(m.id,'MZJ',m.MZJ,m.MZK,mj,mk,rel)
        m.MZJ += mj
        m.MZK += mk
        m.FYJ += vj
        m.FYK += -vj
        relc.remove('MZJ')
    if 'MZK' in m.releases:
        mk = -m.MZK
        mj = 0. if 'MZJ' in m.releases else 0.5*mk
        vj = (mj + mk)/m.L
        ##print(m.id,'MZK',m.MZJ,m.MZK,mj,mk,rel)
        m.MZJ += mj
        m.MZK += mk
        m.FYJ += vj
        m.FYK += -vj
        relc.remove('MZK')
    if relc:
        Warn("Member end-releases not processed: {}".format(relc))

In [34]:
computed = pd.DataFrame([{k:getattr(m,k) for k in ['id']+cols} 
                         for m in MEMBERS.values()]).set_index('id')
diff = (computed - MEFS[cols])
lim = 1E-12
for c in cols:
    biggest = MEFS[c].abs().max()
    diff[c][diff[c].abs() < biggest*lim] = 0
diff


Out[34]:
FXJ FXK FYJ FYK MZJ MZK
AB 0 0 0 0 0 0
BC 0 0 0 0 0 0
BE 0 0 0 0 0 0
CF 0 0 0 0 0 0
DE 0 0 0 0 0 0
EF 0 0 0 0 0 0
EG 0 0 0 0 0 0
FH 0 0 0 0 0 0
GH 0 0 0 0 0 0
IG 0 0 0 0 0 0
JK 0 0 0 0 0 0
KB 0 0 0 0 0 0
KL 0 0 0 0 0 0
LC 0 0 0 0 0 0

14 rows × 6 columns


In [35]:
diff.abs().max()


Out[35]:
FXJ    0
FXK    0
FYJ    0
FYK    0
MZJ    0
MZK    0
dtype: float64

Maximum relative differences


In [36]:
diff = (computed - MEFS[cols])
for c in cols:
    biggest = MEFS[c].abs().max()
    r = (diff[c]/biggest)
    idr = r.abs().idxmax()
    print(c,idr,r[idr])


FXJ EG 1.44441189535e-14
FXK EG -1.44441189535e-14
FYJ CF -1.90497799353e-15
FYK CF 1.66737179707e-15
MZJ BC 2.87632857616e-15
MZK BC 9.32285291849e-16

In [ ]: