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 [1]:
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 [2]:
import pandas as pd
import math

In [3]:
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 [4]:
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 [5]:
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[5]:
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

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


Out[6]:
deltaX deltaY thetaZ x y
id
A 0.000000 0.000000 0.000000 10000.0 0.0
B 26.392119 -2.613585 -0.007546 10000.0 6500.0
C 41.058763 -3.621538 -0.007561 10000.0 12000.0
D 0.000000 0.000000 0.000000 20500.0 0.0
E 26.744537 -2.803081 0.002720 20500.0 6500.0
F 40.706144 -3.857628 0.005554 20500.0 12000.0
G 26.888652 -1.321138 -0.003266 30500.0 6500.0
H 40.801376 -1.824187 -0.002161 30500.0 12000.0
I 0.000000 0.000000 -0.004572 30500.0 0.0
J 0.000000 0.000000 -0.004434 0.0 0.0
K 26.378272 -1.321138 -0.003307 0.0 6500.0
L 41.071354 -1.824187 -0.002354 0.0 12000.0

In [7]:
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 [8]:
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 [9]:
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 [10]:
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 [11]:
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 [12]:
table = pd.read_csv(filename('releases'))
for i,row in table.iterrows():
    memb = MEMBERS[row.MEMBERID]
    memb.releases.add(row.RELEASE.upper())

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


Out[13]:
A FXJ FXK FYJ FYK Ix L MZJ MZK cosx cosy releases
id
AB 12300.0 0.0 0.0 0.0 0.0 222000000.0 6500.0 0.0 0.0 0.0 1.0 {}
BC 12300.0 0.0 0.0 0.0 0.0 222000000.0 5500.0 0.0 0.0 0.0 1.0 {}
DE 12300.0 0.0 0.0 0.0 0.0 222000000.0 6500.0 0.0 0.0 0.0 1.0 {}
EF 12300.0 0.0 0.0 0.0 0.0 222000000.0 5500.0 0.0 0.0 0.0 1.0 {}
IG 12300.0 0.0 0.0 0.0 0.0 222000000.0 6500.0 0.0 0.0 0.0 1.0 {}
GH 12300.0 0.0 0.0 0.0 0.0 222000000.0 5500.0 0.0 0.0 0.0 1.0 {}
JK 12300.0 0.0 0.0 0.0 0.0 222000000.0 6500.0 0.0 0.0 0.0 1.0 {}
KL 12300.0 0.0 0.0 0.0 0.0 222000000.0 5500.0 0.0 0.0 0.0 1.0 {}
CF 13500.0 0.0 0.0 0.0 0.0 488000000.0 10500.0 0.0 0.0 1.0 0.0 {}
BE 13500.0 0.0 0.0 0.0 0.0 488000000.0 10500.0 0.0 0.0 1.0 0.0 {}
FH 13500.0 0.0 0.0 0.0 0.0 488000000.0 10000.0 0.0 0.0 1.0 0.0 {MZJ, MZK}
EG 13500.0 0.0 0.0 0.0 0.0 488000000.0 10000.0 0.0 0.0 1.0 0.0 {MZJ, MZK}
KB 13500.0 0.0 0.0 0.0 0.0 488000000.0 10000.0 0.0 0.0 1.0 0.0 {MZJ, MZK}
LC 13500.0 0.0 0.0 0.0 0.0 488000000.0 10000.0 0.0 0.0 1.0 0.0 {MZJ, MZK}

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


Out[14]:
FXJ FYJ MZJ FXK FYK MZK
MEMBERID
AB 9.891415e+05 3625.020453 6.332451e+07 -9.891415e+05 -3625.020453 -3.976188e+07
BC 4.508299e+05 -86071.539660 -2.365727e+08 -4.508299e+05 86071.539660 -2.368208e+08
DE 1.060859e+06 69040.008304 2.057977e+08 -1.060859e+06 -69040.008304 2.429624e+08
EF 4.716701e+05 117583.181532 3.004759e+08 -4.716701e+05 -117583.181532 3.462316e+08
IG 5.000000e+05 2744.482817 2.980232e-08 -5.000000e+05 -2744.482817 1.783914e+07
GH 2.250000e+05 -3243.479692 -1.783914e+07 -2.250000e+05 3243.479692 0.000000e+00
JK 5.000000e+05 2368.101512 1.490116e-08 -5.000000e+05 -2368.101512 1.539266e+07
KL 2.250000e+05 -2798.665423 -1.539266e+07 -2.250000e+05 2798.665423 0.000000e+00
CF 9.067336e+04 225829.917117 2.368208e+08 -9.067336e+04 246670.082883 -3.462316e+08
BE -9.062164e+04 263311.557360 2.763346e+08 9.062164e+04 314188.442640 -5.434382e+08
FH -2.571259e+04 225000.000000 0.000000e+00 2.571259e+04 225000.000000 0.000000e+00
EG -3.891111e+04 275000.000000 0.000000e+00 3.891111e+04 275000.000000 0.000000e+00
KB -3.738873e+03 275000.000000 0.000000e+00 3.738873e+03 275000.000000 0.000000e+00
LC 3.399701e+03 225000.000000 0.000000e+00 -3.399701e+03 225000.000000 0.000000e+00

In [15]:
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 [16]:
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[16]:
FXJ FXK FYJ FYK MZJ MZK
id
AB 0.0 0.0 0.0 0.0 0.0 0.0
BC 0.0 0.0 0.0 0.0 0.0 0.0
DE 0.0 0.0 0.0 0.0 0.0 0.0
EF 0.0 0.0 0.0 0.0 0.0 0.0
IG 0.0 0.0 0.0 0.0 0.0 0.0
GH 0.0 0.0 0.0 0.0 0.0 0.0
JK 0.0 0.0 0.0 0.0 0.0 0.0
KL 0.0 0.0 0.0 0.0 0.0 0.0
CF 0.0 0.0 0.0 0.0 0.0 0.0
BE 0.0 0.0 0.0 0.0 0.0 0.0
FH 0.0 0.0 0.0 0.0 0.0 0.0
EG 0.0 0.0 0.0 0.0 0.0 0.0
KB 0.0 0.0 0.0 0.0 0.0 0.0
LC 0.0 0.0 0.0 0.0 0.0 0.0

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


Out[17]:
FXJ    0.0
FXK    0.0
FYJ    0.0
FYK    0.0
MZJ    0.0
MZK    0.0
dtype: float64

Maximum relative differences


In [18]:
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 FH 1.1937316732449982e-14
FXK FH -1.1937316732449982e-14
FYJ EF 1.1641532182693482e-15
FYK EF -1.0189494315398125e-15
MZJ GH 1.7109195840949047e-15
MZK EF 9.871256031345683e-16

In [ ]: