Nodes¶
Each node has 6 degrees of freedom (3 translations and 3 rotations). Each of these degrees of freedom needs to be restrained for the model to be stable. For example, applying full end releases to all the members framing into a node will allow the node to “spin”. The node would either have to be attached to at least one of the members rotationally, or supported rotationally by a support.
Creating a New Node¶
Nodes can be added to an existing model using the FEModel3D.add_node method:
# Add a node named 'N1' at location (0, 0, 0)
my_model.add_node('N1', 0, 0, 0)
Once added to the model, the node will be stored in the nodes dictionary of the model for easy access later on.
# Get the node we just created from the model
node_1 = my_model.nodes['N1']
Adding Supports to a Node¶
You can define supports for any node you’ve created using the def_support method. You’ll need to specify the node name, and a True or False value for each support at the node. The supports are specified in the following order: X-translation, Y-translation, Z-translation, rotation about X, rotation about Y, rotation about Z.
You can also use keyword arguments if you only want to specify specific supports. Any supports you do not specify will be set to False.
If supports have already been defined for a node they will be overwritten when def_support is called again for that node.
# Add a pinned support at node `N1`
my_model.def_support('N1', True, True, True, False, False, False)
# Another way to add a pinned support at node `N1`
my_model.def_support('N1', 1, 1, 1, 0, 0, 0)
# Yet another way to add a pinned support at node 'N1'
my_model.def_support('N1', support_DX=True, support_DY=True, support_DZ=True)
# Turn node 'N1' into a rotational support about the Y-axis.
my_model.def_support('N1', support_RY=True)
Adding Nodal Spring Supports¶
Nodal spring supports can be defined using the def_support_spring method. Nodal spring supports can be tension-only, compression-only, or two-way. Compression-only springs can be very useful for modeling foundation problems. When using a tension-only or compression-only spring the solution is iterative, so be sure not to use the FEModel3D.analyze_linear() solver.
You’ll need to specify which node to apply the spring to, which degree of freedome to apply it to ('DX', 'DY', 'DZ', 'RX', 'RY', or 'RZ'), the stiffness value, and a direction ('-' = tension-only, '+' = compression-only, None = two-way).
Note that support springs are defined individually for each degree of freedom instead of for all degrees of freedom at the same time. Unlike with regular supports, defining subsequent support springs for the same node will not override prior assignments unless the assignment is to the same degree of freedom.
# Add a compression only spring to node `N1` with a stiffness of 5 in the Y-direction
my_model.def_support_spring('N1', dof='DY', stiffness=5, direction='+')
# Add a rotational two-way spring to node `N1`
my_model.def_support_spring('N1', dof='RX', stiffness=3, direction=None)
Adding Nodal Loads¶
Use the FEModel3D.add_node_load method to add nodal loads to a model.
# Add a moment about the global Z axis to node `N1` for load case 'D'
my_model.add_node_load('N1', 'MZ', 20, 'D')
# Add a force in the global X direction to node 'N1' for load case 'E'
my_model.add_node_load('N1', 'FX', 30, 'E')
Enforced Nodal Displacements (e.g., Support Settlements)¶
Use the FEModel3D.def_node_disp method to model a known nodal displacement or rotation such as a support settlement.
# Add an enforced displacement of -1.5 at node 'N4' in global Y
my_model.def_node_disp('N4', direction='DY', magnitude=-1.5)
Getting Node Results¶
After running an analysis, each node stores its results in dictionaries keyed by load
combination name. Access a node via model.nodes['Name'] and then query the result
you need by combination.
Available nodal results¶
Displacements (global) -
DX['Combo'],DY['Combo'],DZ['Combo']Rotations (global) -
RX['Combo'],RY['Combo'],RZ['Combo']Reactions (global forces) -
RxnFX['Combo'],RxnFY['Combo'],RxnFZ['Combo']Reactions (global moments) -
RxnMX['Combo'],RxnMY['Combo'],RxnMZ['Combo']
Notes¶
Results are populated only after a successful call to analyze_linear(), analyze(), or analyze_PDelta(). Each combination analyzed will appear as a key in the per-result dictionaries.
Reaction values are reported in the global axes and represent the support reactions required to enforce supports and enforced displacements/rotations at the node.
Enforced displacements/rotations are inputs (EnforcedDX/DY/DZ/RX/RY/RZ on the node); the corresponding reaction response will appear in RxnF*/RxnM* after analysis.
Spring supports are defined per-DOF and stored as spring_D* = [stiffness, direction, active] where direction is ‘+’ (compression-only), ‘-’ (tension-only), or None (two-way).
Examples¶
n = my_model.nodes['N2']
# Global displacements/rotations for combo 'D+L'
ux = n.DX['D+L']
uy = n.DY['D+L']
rz = n.RZ['D+L']
# Support reactions for combo '1.2D+1.0W'
vy = n.RxnFY['1.2D+1.0W']
mz = n.RxnMZ['1.2D+1.0W']
# Inspect loads assigned to the node (list of tuples: (Direction, value, case))
for (dirn, val, case) in n.NodeLoads:
print(dirn, val, case)
The FEModel3D class stores nodes in a Python dictionary. Nodes can be accessed using the syntax FEModel3D.nodes['node_name'].
Once you’ve retrieved a node you can access its reactions and displacements as node class attributes. Reactions and displacements are also stored in dictionary format, with the keys being the load combination names.
# Print the Y-reaction and the reaction moment about the Z-axis at nodes 'N2' and 'N3'
print(my_model.nodes['N2'].RxnFY['1.2D+1.0W'])
print(my_model.nodes['N3'].RxnMZ['1.2D+1.0W'])
Node API Quick Reference¶
Create:
FEModel3D.add_node(name, X, Y, Z) -> strSupports:
FEModel3D.def_support(node_name, support_DX=False, support_DY=False, support_DZ=False, support_RX=False, support_RY=False, support_RZ=False)Spring supports:
FEModel3D.def_support_spring(node_name, dof, stiffness, direction=None)Enforced disp/rot:
FEModel3D.def_node_disp(node_name, direction, magnitude)Loads:
FEModel3D.add_node_load(node_name, direction, P, case='Case 1')Results on a node object:
DX/DY/DZ/RX/RY/RZ['Combo'],RxnFX/RxnFY/RxnFZ/RxnMX/RxnMY/RxnMZ['Combo']
Worked Example¶
from Pynite import FEModel3D
model = FEModel3D()
n1 = model.add_node('N1', 0, 0, 0)
n2 = model.add_node('N2', 0, 10, 0)
# Pinned base and vertical settlement at the top
model.def_support('N1', support_DX=True, support_DY=True, support_DZ=True)
model.def_node_disp('N2', 'DY', -0.25)
# Add a small horizontal force
model.add_node_load('N2', 'FX', 2.0, case='S')
model.add_load_combo('S', {'S': 1.0})
model.analyze_linear()
print(model.nodes['N2'].DX['S']) # horizontal drift
print(model.nodes['N1'].RxnFY['S']) # base vertical reaction
API Cross-Links and Reference¶
Class:
Node3D