2-Dimensional Frame Analysis - Version 04

This program performs an elastic analysis of 2-dimensional structural frames. It has the following features:

  1. Input is provided by a set of CSV files (and cell-magics exist so you can specifiy the CSV data in a notebook cell). See the example below for an, er, example.
  2. Handles concentrated forces on nodes, and concentrated forces, concentrated moments, and linearly varying distributed loads applied transversely anywhere along the member (i.e., there is as yet no way to handle longitudinal load components).
  3. It handles fixed, pinned, roller supports and member end moment releases (internal pins). The former are handled by assigning free or fixed global degrees of freedom, and the latter are handled by adjusting the member stiffness matrix.
  4. It has the ability to handle named sets of loads with factored combinations of these.
  5. The DOF #'s are assigned by the program, with the fixed DOF #'s assigned after the non-fixed. The equilibrium equation is then partitioned for solution. Among other advantages, this means that support settlement could be easily added (there is no UI for that, yet).
  6. A non-linear analysis can be performed using the P-Delta method (fake shears are computed at column ends due to the vertical load acting through horizontal displacement differences, and these shears are applied as extra loads to the nodes).
  7. A full non-linear (2nd order) elastic analysis will soon be available by forming the equilibrium equations on the deformed structure. This is very easy to add, but it hasn't been done yet. Shouldn't be too long.
  8. There is very little no documentation below, but that will improve, slowly.

In [1]:
from __future__ import print_function

In [2]:
from salib import extend, import_notebooks
import_notebooks()
from Tables import Table
from Nodes import Node
from Members import Member
from LoadSets import LoadSet, LoadCombination
from NodeLoads import makeNodeLoad
from MemberLoads import makeMemberLoad
from collections import OrderedDict, defaultdict
import numpy as np

In [3]:
class Object(object):
    pass

class Frame2D(object):
    
    def __init__(self,dsname=None):
        self.dsname = dsname
        if dsname is not None:
            Table.set_source(dsname)
        self.reset()
        
    def reset(self):
        self.rawdata = Object()
        self.nodes = OrderedDict()
        self.members = OrderedDict()
        self.nodeloads = LoadSet()
        self.memberloads = LoadSet()
        self.loadcombinations = LoadCombination()
        #self.dofdesc = []
        #self.nodeloads = defaultdict(list)
        #self.membloads = defaultdict(list)
        self.ndof = 0
        self.nfree = 0
        self.ncons = 0
        self.R = None
        self.D = None
        self.PDF = None    # P-Delta forces
        
    COLUMNS_xxx = [] # list of column names for table 'xxx'
        
    def get_table(self,tablename,extrasok=False,optional=False):
        columns = getattr(self,'COLUMNS_'+tablename)
        t = Table(tablename,ds_name=self.dsname,columns=columns,optional=optional)
        t.read(optional=optional)
        reqdl= columns
        reqd = set(reqdl)
        prov = set(t.columns)
        if reqd-prov:
            raise Exception('Columns missing {} for table "{}". Required columns are: {}'\
                            .format(list(reqd-prov),tablename,reqdl))
        if not extrasok:
            if prov-reqd:
                raise Exception('Extra columns {} for table "{}". Required columns are: {}'\
                               .format(list(prov-reqd),tablename,reqdl))
        return t

Test Frame

Nodes


In [4]:
%%Table nodes
NODEID,X,Y,Z
A,0.,0.,5000.
B,0,4000,5000
C,8000,4000,5000
D,8000,0,5000

In [5]:
@extend
class Frame2D:
    
    COLUMNS_nodes = ('NODEID','X','Y')
        
    def install_nodes(self):
        node_table = self.get_table('nodes')
        for ix,r in node_table.data.iterrows():
            if r.NODEID in self.nodes:
                raise Exception('Multiply defined node: {}'.format(r.NODEID))
            n = Node(r.NODEID,r.X,r.Y)
            self.nodes[n.id] = n
        self.rawdata.nodes = node_table
            
    def get_node(self,id):
        try:
            return self.nodes[id]
        except KeyError:
            raise Exception('Node not defined: {}'.format(id))

In [6]:
##test:
f = Frame2D()

In [7]:
##test:
f.install_nodes()

In [8]:
##test:
f.nodes


Out[8]:
OrderedDict([('A', Node("A",0.0,0.0)),
             ('B', Node("B",0.0,4000.0)),
             ('C', Node("C",8000.0,4000.0)),
             ('D', Node("D",8000.0,0.0))])

In [9]:
##test:
f.get_node('C')


Out[9]:
Node("C",8000.0,4000.0)

Supports


In [10]:
%%Table supports
NODEID,C0,C1,C2
A,FX,FY,MZ
D,FX,FY

In [11]:
def isnan(x):
    if x is None:
        return True
    try:
        return np.isnan(x)
    except TypeError:
        return False

In [12]:
@extend
class Frame2D:
    
    COLUMNS_supports = ('NODEID','C0','C1','C2')
    
    def install_supports(self):
        table = self.get_table('supports')
        for ix,row in table.data.iterrows():
            node = self.get_node(row.NODEID)
            for c in [row.C0,row.C1,row.C2]:
                if not isnan(c):
                    node.add_constraint(c)
        self.rawdata.supports = table

In [13]:
##test:
f.install_supports()

In [14]:
##test:
vars(f.get_node('D'))


Out[14]:
{'constraints': {'FX', 'FY'},
 'dofnums': [None, None, None],
 'id': 'D',
 'x': 8000.0,
 'y': 0.0}

Members


In [15]:
%%Table members
MEMBERID,NODEJ,NODEK
AB,A,B
BC,B,C
CD,C,D

In [16]:
@extend
class Frame2D:
    
    COLUMNS_members = ('MEMBERID','NODEJ','NODEK')
    
    def install_members(self):
        table = self.get_table('members')
        for ix,m in table.data.iterrows():
            if m.MEMBERID in self.members:
                raise Exception('Multiply defined member: {}'.format(m.MEMBERID))
            memb = Member(m.MEMBERID,self.get_node(m.NODEJ),self.get_node(m.NODEK))
            self.members[memb.id] = memb
        self.rawdata.members = table
            
    def get_member(self,id):
        try:
            return self.members[id]
        except KeyError:
            raise Exception('Member not defined: {}'.format(id))

In [17]:
##test:
f.install_members()
f.members


Out[17]:
OrderedDict([('AB', Member("AB","Node("A",0.0,0.0)","Node("B",0.0,4000.0)")),
             ('BC',
              Member("BC","Node("B",0.0,4000.0)","Node("C",8000.0,4000.0)")),
             ('CD',
              Member("CD","Node("C",8000.0,4000.0)","Node("D",8000.0,0.0)"))])

In [18]:
##test:
m = f.get_member('BC')
m.id, m.L, m.dcx, m.dcy


Out[18]:
('BC', 8000.0, 1.0, 0.0)

Releases


In [19]:
%%Table releases
MEMBERID,RELEASE
AB,MZK
CD,MZJ

In [20]:
@extend
class Frame2D:
    
    COLUMNS_releases = ('MEMBERID','RELEASE')
    
    def install_releases(self):
        table = self.get_table('releases',optional=True)
        for ix,r in table.data.iterrows():
            memb = self.get_member(r.MEMBERID)
            memb.add_release(r.RELEASE)
        self.rawdata.releases = table

In [21]:
##test:
f.install_releases()

In [22]:
##test:
vars(f.get_member('AB'))


Out[22]:
{'A': None,
 'Ix': None,
 'KG': None,
 'KL': None,
 'L': 4000.0,
 'Tm': None,
 'dcx': 0.0,
 'dcy': 1.0,
 'fefsl': None,
 'id': 'AB',
 'mefs': None,
 'nodej': Node("A",0.0,0.0),
 'nodek': Node("B",0.0,4000.0),
 'releases': {'MZK'}}

Properties

If the SST module is loadable, member properties may be specified by giving steel shape designations (such as 'W310x97') in the member properties data. If the module is not available, you may still give $A$ and $I_x$ directly (it only tries to lookup the properties if these two are not provided).


In [23]:
try:
    from sst import SST
    __SST = SST()
    get_section = __SST.section
except ImportError:
    def get_section(dsg,fields):
        raise ValueError('Cannot lookup property SIZE because SST is not available.  SIZE = {}'.format(dsg))
        ##return [1.] * len(fields.split(',')) # in case you want to do it that way

In [24]:
%%Table properties
MEMBERID,SIZE,IX,A
BC,W460x106,,
AB,W310x97,,
CD,,

In [25]:
@extend
class Frame2D:
    
    COLUMNS_properties = ('MEMBERID','SIZE','IX','A')
    
    def install_properties(self):
        table = self.get_table('properties')
        table = self.fill_properties(table)
        for ix,row in table.data.iterrows():
            memb = self.get_member(row.MEMBERID)
            memb.size = row.SIZE
            memb.Ix = row.IX
            memb.A = row.A
        self.rawdata.properties = table
        
    def fill_properties(self,table):
        data = table.data
        prev = None
        for ix,row in data.iterrows():
            nf = 0
            if type(row.SIZE) in [type(''),type(u'')]:
                if isnan(row.IX) or isnan(row.A):
                    Ix,A = get_section(row.SIZE,'Ix,A')
                    if isnan(row.IX):
                        nf += 1
                        data.loc[ix,'IX'] = Ix
                    if isnan(row.A):
                        nf += 1
                        data.loc[ix,'A'] = A
            elif isnan(row.SIZE):
                data.loc[ix,'SIZE'] = '' if nf == 0 else prev
            prev = data.loc[ix,'SIZE']
        table.data = data.fillna(method='ffill')
        return table

In [26]:
##test:
f.install_properties()

In [27]:
##test:
vars(f.get_member('CD'))


Out[27]:
{'A': 12300.0,
 'Ix': 222000000.0,
 'KG': None,
 'KL': None,
 'L': 4000.0,
 'Tm': None,
 'dcx': 0.0,
 'dcy': -1.0,
 'fefsl': None,
 'id': 'CD',
 'mefs': None,
 'nodej': Node("C",8000.0,4000.0),
 'nodek': Node("D",8000.0,0.0),
 'releases': {'MZJ'},
 'size': ''}

Node Loads


In [28]:
%%Table node_loads
LOAD,NODEID,DIRN,F
Wind,B,FX,-200000.

In [29]:
@extend
class Frame2D:
    
    COLUMNS_node_loads = ('LOAD','NODEID','DIRN','F')
    
    def install_node_loads(self):
        table = self.get_table('node_loads')
        dirns = ['FX','FY','FZ']
        for ix,row in table.data.iterrows():
            n = self.get_node(row.NODEID)
            if row.DIRN not in dirns:
                raise ValueError("Invalid node load direction: {} for load {}, node {}; must be one of '{}'"
                                .format(row.DIRN, row.LOAD, row.NODEID, ', '.join(dirns)))
            l = makeNodeLoad({row.DIRN:row.F})
            self.nodeloads.append(row.LOAD,n,l)
        self.rawdata.node_loads = table

In [30]:
##test:
f.install_node_loads()

In [31]:
##test:
for o,l,fact in f.nodeloads.iterloads('Wind'):
    print(o,l,fact,l*fact)


Node("B",0.0,4000.0) NodeLoad(-200000.0,0.0,0.0) 1.0 NodeLoad(-200000.0,0.0,0.0)

Member Loads


In [32]:
%%Table member_loads
LOAD,MEMBERID,TYPE,W1,W2,A,B,C
Live,BC,UDL,-50,,,,
Live,BC,PL,-200000,,5000

In [33]:
@extend
class Frame2D:
    
    COLUMNS_member_loads = ('LOAD','MEMBERID','TYPE','W1','W2','A','B','C')
    
    def install_member_loads(self):
        table = self.get_table('member_loads')
        for ix,row in table.data.iterrows():
            m = self.get_member(row.MEMBERID)
            l = makeMemberLoad(m.L,row)
            self.memberloads.append(row.LOAD,m,l)
        self.rawdata.member_loads = table

In [34]:
##test:
f.install_member_loads()

In [35]:
##test:
for o,l,fact in f.memberloads.iterloads('Live'):
    print(o.id,l,fact,l.fefs()*fact)


BC UDL(L=8000.0,w=-50) 1.0 EF(0.0,200000.0,266666666.667,0.0,200000.0,-266666666.667)
BC PL(L=8000.0,P=-200000,a=5000.0) 1.0 EF(0.0,63281.25,140625000.0,0.0,136718.75,-234375000.0)

Load Combinations


In [36]:
%%Table load_combinations
CASE,LOAD,FACTOR
One,Live,1.5
One,Wind,1.75

In [37]:
@extend
class Frame2D:
    
    COLUMNS_load_combinations = ('CASE','LOAD','FACTOR')
    
    def install_load_combinations(self):
        table = self.get_table('load_combinations',optional=True)
        if len(table) > 0:
            for ix,row in table.data.iterrows():
                self.loadcombinations.append(row.CASE,row.LOAD,row.FACTOR)
        else:
            all = self.nodeloads.names.union(self.memberloads.names)
            for l in all:
                self.loadcombinations.append('all',l,1.0)
        self.rawdata.load_combinations = table

In [38]:
##test:
f.install_load_combinations()

In [39]:
##test:
for o,l,fact in f.loadcombinations.iterloads('One',f.nodeloads):
    print(o.id,l,fact)
for o,l,fact in f.loadcombinations.iterloads('One',f.memberloads):
    print(o.id,l,fact,l.fefs()*fact)


B NodeLoad(-200000.0,0.0,0.0) 1.75
BC UDL(L=8000.0,w=-50) 1.5 EF(0.0,300000.0,400000000.0,0.0,300000.0,-400000000.0)
BC PL(L=8000.0,P=-200000,a=5000.0) 1.5 EF(0.0,94921.875,210937500.0,0.0,205078.125,-351562500.0)

Load Iterators


In [40]:
@extend
class Frame2D:

    def iter_nodeloads(self,casename):
        for o,l,f in self.loadcombinations.iterloads(casename,self.nodeloads):
            yield o,l,f
    
    def iter_memberloads(self,casename):
        for o,l,f in self.loadcombinations.iterloads(casename,self.memberloads):
            yield o,l,f

In [41]:
##test:
for o,l,fact in f.iter_nodeloads('One'):
    print(o.id,l,fact)
for o,l,fact in f.iter_memberloads('One'):
    print(o.id,l,fact)


B NodeLoad(-200000.0,0.0,0.0) 1.75
BC UDL(L=8000.0,w=-50) 1.5
BC PL(L=8000.0,P=-200000,a=5000.0) 1.5

Accumulated Cell Data


In [42]:
##test:
Table.CELLDATA


Out[42]:
{u'load_combinations': u'CASE,LOAD,FACTOR\nOne,Live,1.5\nOne,Wind,1.75',
 u'member_loads': u'LOAD,MEMBERID,TYPE,W1,W2,A,B,C\nLive,BC,UDL,-50,,,,\nLive,BC,PL,-200000,,5000',
 u'members': u'MEMBERID,NODEJ,NODEK\nAB,A,B\nBC,B,C\nCD,C,D',
 u'node_loads': u'LOAD,NODEID,DIRN,F\nWind,B,FX,-200000.',
 u'nodes': u'NODEID,X,Y,Z\nA,0.,0.,5000.\nB,0,4000,5000\nC,8000,4000,5000\nD,8000,0,5000',
 u'properties': u'MEMBERID,SIZE,IX,A\nBC,W460x106,,\nAB,W310x97,,\nCD,,',
 u'releases': u'MEMBERID,RELEASE\nAB,MZK\nCD,MZJ',
 u'supports': u'NODEID,C0,C1,C2\nA,FX,FY,MZ\nD,FX,FY'}

Input Everything


In [43]:
@extend
class Frame2D:
    
    def install_all(self):
        self.install_nodes()
        self.install_supports()
        self.install_members()
        self.install_releases()
        self.install_properties()
        self.install_node_loads()
        self.install_member_loads()
        self.install_load_combinations()

In [44]:
##test:
f.reset()
f.install_all()

Number the DOFs


In [45]:
@extend
class Frame2D:
    
    def number_dofs(self):
        self.ndof = (3*len(self.nodes))
        self.ncons = sum([len(node.constraints) for node in self.nodes.values()])
        self.nfree = self.ndof - self.ncons
        ifree = 0
        icons = self.nfree
        self.dofdesc = [None] * self.ndof
        for node in self.nodes.values():
            for dirn,ix in node.DIRECTIONS.items():
                if dirn in node.constraints:
                    n = icons
                    icons += 1
                else:
                    n = ifree
                    ifree += 1
                node.dofnums[ix] = n
                self.dofdesc[n] = (node,dirn)

In [46]:
##test:
f.number_dofs()

In [47]:
##test:
f.ndof, f.ncons, f.nfree


Out[47]:
(12, 5, 7)

Display Nodes


In [48]:
def prhead(txt,ul='='):
    """Print a heading and underline it."""
    print()
    print(txt)
    if ul:
        print(ul*(len(txt)//len(ul)))
    print()

In [49]:
@extend
class Frame2D:

    def print_nodes(self,precision=0,printdof=False):
        prhead('Nodes:')
        print('Node          X         Y  Constraints  DOF #s')
        print('----      -----     -----  -----------  ------')
        for nid,node in self.nodes.items():
            ct = ','.join(sorted(node.constraints,key=lambda t: Node.DIRECTIONS[t]))
            dt = ','.join([str(x) for x in node.dofnums])
            print('{:<5s}{:>10.{precision}f}{:>10.{precision}f}  {:<11s}  {}'\
                  .format(nid,node.x,node.y,ct,dt,precision=precision))
        if not printdof:
            return
        print()
        print('DOF#   Node  Dirn')
        print('----   ----  ----')
        for i in range(len(self.dofdesc)):
            node,dirn = self.dofdesc[i]
            print('{:>4d}   {:<4s}  {}'.format(i,node.id,dirn))

In [50]:
##test:
f.print_nodes(printdof=True)


Nodes:
======

Node          X         Y  Constraints  DOF #s
----      -----     -----  -----------  ------
A             0         0  FX,FY,MZ     7,8,9
B             0      4000               0,1,2
C          8000      4000               3,4,5
D          8000         0  FX,FY        10,11,6

DOF#   Node  Dirn
----   ----  ----
   0   B     FX
   1   B     FY
   2   B     MZ
   3   C     FX
   4   C     FY
   5   C     MZ
   6   D     MZ
   7   A     FX
   8   A     FY
   9   A     MZ
  10   D     FX
  11   D     FY

Display Members


In [51]:
@extend
class Frame2D:
    
    def print_members(self,precision=1):
        prhead('Members:')
        print('Member   Node-J  Node-K    Length       dcx       dcy  Size                Ix           A  Releases')
        print('------   ------  ------    ------   -------   -------  --------      --------       -----  --------')
        for mid,memb in self.members.items():
            nj = memb.nodej
            nk = memb.nodek
            rt = ','.join(sorted(memb.releases,key=lambda t: Member.RELEASES[t]))
            print('{:<7s}  {:<6s}  {:<6s}  {:>8.{precision}f}  {:>8.5f}  {:>8.5f}  {:<10s}  {:>10g}  {:>10g}  {}'\
                  .format(memb.id,nj.id,nk.id,memb.L,memb.dcx,memb.dcy,str(memb.size),memb.Ix,memb.A,rt,precision=precision))

In [52]:
##test:
f.print_members()


Members:
========

Member   Node-J  Node-K    Length       dcx       dcy  Size                Ix           A  Releases
------   ------  ------    ------   -------   -------  --------      --------       -----  --------
AB       A       B         4000.0   0.00000   1.00000  W310x97       2.22e+08       12300  MZK
BC       B       C         8000.0   1.00000   0.00000  W460x106      4.88e+08       13500  
CD       C       D         4000.0   0.00000  -1.00000                2.22e+08       12300  MZJ

Display loads


In [53]:
@extend
class Frame2D:
    
    def print_loads(self,precision=0):
        
        prhead('Node Loads:')
        if self.nodeloads:
            print('Type  Node      FX          FY          MZ')
            print('----  ----  ----------  ----------  ----------')
            for lname,node,load in self.nodeloads:
                print('{:<4s}  {:<4s}  {:>10.{precision}f}  {:>10.{precision}f}  {:>10.{precision}f}'
                      .format(lname,node.id,load.fx,load.fy,load.mz,precision=precision))
        else:
            print(" - - - none - - -")

        prhead('Member Loads:')
        if self.memberloads:
            print('Type  Member  Load')
            print('----  ------  ----------------')
            for lname,memb,load in self.memberloads:
                print("{:<4s}  {:<6s}  {}".format(lname,memb.id,load))
        else:
            print(" - - - none - - -")

        prhead("Load Combinations:")
        if self.loadcombinations:
            print('Case   Type  Factor')
            print('-----  ----  ------')
            prev = None
            for cname,lname,f in self.loadcombinations:
                cn = ' '*(len(prev)//2)+'"' if cname == prev else cname
                print("{:<5s}  {:<4s}  {:>6.2f}".format(cn,lname,f))
                prev = cname
        else:
            print(" - - - none - - -")

In [54]:
##test:
f.print_loads()


Node Loads:
===========

Type  Node      FX          FY          MZ
----  ----  ----------  ----------  ----------
wind  B        -200000           0           0

Member Loads:
=============

Type  Member  Load
----  ------  ----------------
live  BC      UDL(L=8000.0,w=-50)
live  BC      PL(L=8000.0,P=-200000,a=5000.0)

Load Combinations:
==================

Case   Type  Factor
-----  ----  ------
one    live    1.50
 "     wind    1.75

In [55]:
@extend
class Frame2D:
    
    def print_input(self):
        
        prhead('Frame '+str(self.dsname)+':')
        print()
        print('              # of nodal degrees of freedom:',self.ndof)
        print('  # of constrained nodal degrees of freedom:',self.ncons)
        print('# of unconstrained nodal degrees of freedom:',self.nfree,' (= degree of kinematic indeterminacy)')
        m = len(self.members)
        r = self.ncons
        j = len(self.nodes)
        c = len(self.rawdata.releases)
        print()
        print('                               # of members:',m)
        print('                             # of reactions:',r)
        print('                                 # of nodes:',j)
        print('                            # of conditions:',c)
        print('             degree of static indeterminacy:',(3*m+r)-(3*j+c))
        print('\n')

        self.print_nodes()
        print('\n')
        self.print_members()
        print('\n')
        self.print_loads()

In [56]:
##test:
f.print_input()


Frame None:
===========


              # of nodal degrees of freedom: 12
  # of constrained nodal degrees of freedom: 5
# of unconstrained nodal degrees of freedom: 7  (= degree of kinematic indeterminacy)

                               # of members: 3
                             # of reactions: 5
                                 # of nodes: 4
                            # of conditions: 2
             degree of static indeterminacy: 0



Nodes:
======

Node          X         Y  Constraints  DOF #s
----      -----     -----  -----------  ------
A             0         0  FX,FY,MZ     7,8,9
B             0      4000               0,1,2
C          8000      4000               3,4,5
D          8000         0  FX,FY        10,11,6



Members:
========

Member   Node-J  Node-K    Length       dcx       dcy  Size                Ix           A  Releases
------   ------  ------    ------   -------   -------  --------      --------       -----  --------
AB       A       B         4000.0   0.00000   1.00000  W310x97       2.22e+08       12300  MZK
BC       B       C         8000.0   1.00000   0.00000  W460x106      4.88e+08       13500  
CD       C       D         4000.0   0.00000  -1.00000                2.22e+08       12300  MZJ



Node Loads:
===========

Type  Node      FX          FY          MZ
----  ----  ----------  ----------  ----------
wind  B        -200000           0           0

Member Loads:
=============

Type  Member  Load
----  ------  ----------------
live  BC      UDL(L=8000.0,w=-50)
live  BC      PL(L=8000.0,P=-200000,a=5000.0)

Load Combinations:
==================

Case   Type  Factor
-----  ----  ------
one    live    1.50
 "     wind    1.75

Notes

  • default load case for solving is 'all' as that is the default load combination
  • 2nd order anal: member stiffness matrix does not change between iterations, but the transformation from local to global coords does (because of large node displacements), and thus does the global stiff matrix. But loads do not change, either.

Analysis


In [57]:
@extend
class Frame2D:
    
    def buildK(self):
        K = np.mat(np.zeros((self.ndof,self.ndof)))
        for memb in self.members.values():
            Kl = memb.localK()
            Tm = memb.transform()
            Kg = Tm.T * Kl * Tm
            dofnums = memb.nodej.dofnums + memb.nodek.dofnums
            K[np.ix_(dofnums,dofnums)] += Kg
        return K

In [58]:
##test:
K = f.buildK()
K


Out[58]:
matrix([[  3.39581250e+05,   0.00000000e+00,   0.00000000e+00,
          -3.37500000e+05,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,  -2.08125000e+03,   0.00000000e+00,
           8.32500000e+06,   0.00000000e+00,   0.00000000e+00],
        [  0.00000000e+00,   6.17287500e+05,   9.15000000e+06,
           0.00000000e+00,  -2.28750000e+03,   9.15000000e+06,
           0.00000000e+00,   0.00000000e+00,  -6.15000000e+05,
           0.00000000e+00,   0.00000000e+00,   0.00000000e+00],
        [  0.00000000e+00,   9.15000000e+06,   4.88000000e+10,
           0.00000000e+00,  -9.15000000e+06,   2.44000000e+10,
           0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,   0.00000000e+00,   0.00000000e+00],
        [ -3.37500000e+05,   0.00000000e+00,   0.00000000e+00,
           3.39581250e+05,   0.00000000e+00,   0.00000000e+00,
           8.32500000e+06,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,  -2.08125000e+03,   0.00000000e+00],
        [  0.00000000e+00,  -2.28750000e+03,  -9.15000000e+06,
           0.00000000e+00,   6.17287500e+05,  -9.15000000e+06,
           0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,   0.00000000e+00,  -6.15000000e+05],
        [  0.00000000e+00,   9.15000000e+06,   2.44000000e+10,
           0.00000000e+00,  -9.15000000e+06,   4.88000000e+10,
           0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,   0.00000000e+00,   0.00000000e+00],
        [  0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
           8.32500000e+06,   0.00000000e+00,   0.00000000e+00,
           3.33000000e+10,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,  -8.32500000e+06,   0.00000000e+00],
        [ -2.08125000e+03,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,   2.08125000e+03,   0.00000000e+00,
          -8.32500000e+06,   0.00000000e+00,   0.00000000e+00],
        [  0.00000000e+00,  -6.15000000e+05,   0.00000000e+00,
           0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,   0.00000000e+00,   6.15000000e+05,
           0.00000000e+00,   0.00000000e+00,   0.00000000e+00],
        [  8.32500000e+06,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,  -8.32500000e+06,   0.00000000e+00,
           3.33000000e+10,   0.00000000e+00,   0.00000000e+00],
        [  0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
          -2.08125000e+03,   0.00000000e+00,   0.00000000e+00,
          -8.32500000e+06,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,   2.08125000e+03,   0.00000000e+00],
        [  0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,  -6.15000000e+05,   0.00000000e+00,
           0.00000000e+00,   0.00000000e+00,   0.00000000e+00,
           0.00000000e+00,   0.00000000e+00,   6.15000000e+05]])

In [59]:
@extend
class Frame2D:
    
    def buildP(self,loadcase='all'):
        P = np.mat(np.zeros((self.ndof,1)))
        for node,load,factor in self.iter_nodeloads(loadcase):
            P[node.dofnums] += load.forces * factor
        return P

In [60]:
##test:
f.buildP('one')


Out[60]:
matrix([[-350000.],
        [      0.],
        [      0.],
        [      0.],
        [      0.],
        [      0.],
        [      0.],
        [      0.],
        [      0.],
        [      0.],
        [      0.],
        [      0.]])

In [61]:
@extend
class Frame2D:
    
    def buildP(self,loadcase='all'):
        P = np.mat(np.zeros((self.ndof,1)))
        for node,load,factor in self.iter_nodeloads(loadcase):
            P[node.dofnums] += load.forces * factor
        for memb,load,factor in self.iter_memberloads(loadcase):
            fefs = memb.fefs([load])
            gfefs = memb.Tm.T * (fefs.fefs * factor)
            dofnums = memb.nodej.dofnums + memb.nodek.dofnums
            P[dofnums] -= gfefs
        return P

In [62]:
##test:
P = f.buildP('one')
P


Out[62]:
matrix([[ -3.50000000e+05],
        [ -3.94921875e+05],
        [ -6.10937500e+08],
        [  0.00000000e+00],
        [ -5.05078125e+05],
        [  7.51562500e+08],
        [  0.00000000e+00],
        [  0.00000000e+00],
        [  0.00000000e+00],
        [  0.00000000e+00],
        [  0.00000000e+00],
        [  0.00000000e+00]])

In [63]:
@extend
class Frame2D:
    
    def solve(self,loadcase='all'):
        self.number_dofs()
        K = self.buildK()
        P = self.buildP(loadcase)
        D = np.mat(np.zeros((self.ndof,1)))
        
        N = self.nfree
        Kff = K[:N,:N]  # partition the matrices ....
        Kfc = K[:N,N:]
        Kcf = K[N:,:N]
        Kcc = K[N:,N:]
        
        D[:N] = np.linalg.solve(Kff,P[:N] - Kfc*D[N:])  # displacements
        R = Kcf*D[:N] + Kcc*D[N:] - P[N:]    # reactions at the constrained DOFs
        return D,R

In [72]:
##test:
f.reset()
f.install_all()
D,R = f.solve('one')
f.print_input()


Frame None:
===========


              # of nodal degrees of freedom: 12
  # of constrained nodal degrees of freedom: 5
# of unconstrained nodal degrees of freedom: 7  (= degree of kinematic indeterminacy)

                               # of members: 3
                             # of reactions: 5
                                 # of nodes: 4
                            # of conditions: 2
             degree of static indeterminacy: 0



Nodes:
======

Node          X         Y  Constraints  DOF #s
----      -----     -----  -----------  ------
A             0         0  FX,FY,MZ     7,8,9
B             0      4000               0,1,2
C          8000      4000               3,4,5
D          8000         0  FX,FY        10,11,6



Members:
========

Member   Node-J  Node-K    Length       dcx       dcy  Size                Ix           A  Releases
------   ------  ------    ------   -------   -------  --------      --------       -----  --------
AB       A       B         4000.0   0.00000   1.00000  W310x97       2.22e+08       12300  MZK
BC       B       C         8000.0   1.00000   0.00000  W460x106      4.88e+08       13500  
CD       C       D         4000.0   0.00000  -1.00000                2.22e+08       12300  MZJ



Node Loads:
===========

Type  Node      FX          FY          MZ
----  ----  ----------  ----------  ----------
wind  B        -200000           0           0

Member Loads:
=============

Type  Member  Load
----  ------  ----------------
live  BC      UDL(L=8000.0,w=-50)
live  BC      PL(L=8000.0,P=-200000,a=5000.0)

Load Combinations:
==================

Case   Type  Factor
-----  ----  ------
one    live    1.50
 "     wind    1.75

In [68]:
##test:
D


Out[68]:
matrix([[ -1.68168168e+02],
        [ -6.70731707e-01],
        [ -2.69747726e-02],
        [ -1.68168168e+02],
        [ -7.92682927e-01],
        [  2.88653913e-02],
        [  4.20420420e-02],
        [  0.00000000e+00],
        [  0.00000000e+00],
        [  0.00000000e+00],
        [  0.00000000e+00],
        [  0.00000000e+00]])

In [69]:
##test:
R


Out[69]:
matrix([[  3.50000000e+05],
        [  4.12500000e+05],
        [ -1.40000000e+09],
        [ -5.82076609e-11],
        [  4.87500000e+05]])

Compare

Compare computed reactions with correct reactions.


In [74]:
##test:
# here are the correct reactions:
CR = np.matrix([350E3,412.5E3,-1400E6,0,487.5E3]).T
R-CR


Out[74]:
matrix([[  1.86264515e-09],
        [  2.42725946e-08],
        [ -7.62939453e-06],
        [ -5.82076609e-11],
        [  0.00000000e+00]])

Comparison is good.


In [ ]: