Automatic Unit Conversion#

As we all know, the units should be unified in the finite element analysis. Common basic units include length, force, and time. The units of the base system can be combined in any combination, but other units including pressure, stress, mass, etc. should be unified with base system. In order to facilitate unit processing in the model, opstool has developed a class that can automatically perform unit conversion based on the basic units you set.

For details of the parameters see opstool.preprocessing.UnitSystem().

import numpy as np
import matplotlib.pyplot as plt
import openseespy.opensees as ops
import opstool as opst

length_unit = "m"
force_unit = "kN"
UNIT = opst.UnitSystem(length=length_unit, force=force_unit)
print("Length:", UNIT.mm, UNIT.mm2, UNIT.cm, UNIT.m, UNIT.inch, UNIT.ft)
print("Force", UNIT.N, UNIT.kN, UNIT.lbf, UNIT.kip)
print("Stress", UNIT.MPa, UNIT.kPa, UNIT.Pa, UNIT.psi, UNIT.ksi)
print("Mass", UNIT.g, UNIT.kg, UNIT.ton, UNIT.slug)
# print(UNIT)
Length: 0.001 1e-06 0.01 1 0.0254 0.3048
Force 0.001 1 0.0044482216282509 4.4482216
Stress 1000.0 1.0 0.001 6.8947572932000005 6894.7572932
Mass 1e-06 0.001 1.0 0.014593902936999999

Truss example#

Let’s look at a truss example. You can set the practical values of structural parameters in the model, and UnitSystem() will help you automatically convert to the base unit system you specify.

def model(UNIT):
    ops.wipe()
    ops.model('basic', '-ndm', 2, '-ndf', 2)
    # create nodes
    ops.node(1, 0, 0)
    ops.node(2, 144.0 * UNIT.cm,  0)
    ops.node(3, 2.0 * UNIT.m,  0)
    ops.node(4,  80. * UNIT.cm, 96.0 * UNIT.cm)
    ops.mass(4, 100 * UNIT.kg, 100 * UNIT.kg)
    # set boundary condition
    ops.fix(1, 1, 1)
    ops.fix(2, 1, 1)
    ops.fix(3, 1, 1)
    # define materials
    ops.uniaxialMaterial("Elastic", 1, 3000.0 * UNIT.N / UNIT.cm2)
    # define elements
    ops.element("Truss", 1, 1, 4, 100.0 * UNIT.cm2, 1)
    ops.element("Truss", 2, 2, 4, 50.0 * UNIT.cm2, 1)
    ops.element("Truss", 3, 3, 4, 50.0 * UNIT.cm2, 1)
    # eigen
    omega = np.sqrt(ops.eigen('-fullGenLapack', 2))
    f = omega / (2 * np.pi)
    # create TimeSeries
    ops.timeSeries("Linear", 1)
    ops.pattern("Plain", 1, 1)
    ops.load(4, 10.0 * UNIT.kN, -5.0 * UNIT.kN)

    # ------------------------------
    # Start of analysis generation
    # ------------------------------
    ops.system("BandSPD")
    ops.numberer("RCM")
    ops.constraints("Plain")
    ops.integrator("LoadControl", 1.0 / 10)
    ops.algorithm("Linear")
    ops.analysis("Static")
    u = []
    forces = []
    for _ in range(10):
        ops.analyze(1)
        u.append(ops.nodeDisp(4))
        ops.reactions()
        forces.append(ops.nodeReaction(2))
    u = np.array(u)
    forces = np.array(forces)
    return u, forces, f

Results#

Three Cases:

length_unit1 = "m"
force_unit1 = "kN"
UNIT1 = opst.UnitSystem(length=length_unit1, force=force_unit1)
u1, forces1, f1 = model(UNIT=UNIT1)

length_unit2 = "cm"
force_unit2 = "N"
UNIT2 = opst.UnitSystem(length=length_unit2, force=force_unit2)
u2, forces2, f2 = model(UNIT=UNIT2)

length_unit3 = "ft"
force_unit3 = "lbf"
UNIT3 = opst.UnitSystem(length=length_unit3, force=force_unit3)
u3, forces3, f3 = model(UNIT=UNIT3)

Let’s verify it!

Structure Frequency#

print("structure frequency 1:", f1)
print("structure frequency 2:", f2)
print("structure frequency 3:", f3)
structure frequency 1: [7.05364256 8.28928679]
structure frequency 2: [7.05364256 8.28928679]
structure frequency 3: [7.05364256 8.28928679]

The structural frequencies are consistent, it really has nothing to do with the unit system!

Node 4 Displacement#

print("Dispalcement at node4 case 1:", u1, length_unit1)
print("Dispalcement at node4 case 2:", u2, length_unit2)
print("Dispalcement at node4 case 3:", u3, length_unit3)
Dispalcement at node4 case 1: [[ 0.00516124 -0.00205329]
 [ 0.01032248 -0.00410659]
 [ 0.01548372 -0.00615988]
 [ 0.02064496 -0.00821317]
 [ 0.02580621 -0.01026647]
 [ 0.03096745 -0.01231976]
 [ 0.03612869 -0.01437306]
 [ 0.04128993 -0.01642635]
 [ 0.04645117 -0.01847964]
 [ 0.05161241 -0.02053294]] m
Dispalcement at node4 case 2: [[ 0.51612411 -0.20532937]
 [ 1.03224822 -0.41065875]
 [ 1.54837233 -0.61598812]
 [ 2.06449644 -0.8213175 ]
 [ 2.58062055 -1.02664687]
 [ 3.09674466 -1.23197624]
 [ 3.61286877 -1.43730562]
 [ 4.12899288 -1.64263499]
 [ 4.64511699 -1.84796437]
 [ 5.16124111 -2.05329374]] cm
Dispalcement at node4 case 3: [[ 0.01693321 -0.00673653]
 [ 0.03386641 -0.01347306]
 [ 0.05079962 -0.02020958]
 [ 0.06773282 -0.02694611]
 [ 0.08466603 -0.03368264]
 [ 0.10159923 -0.04041917]
 [ 0.11853244 -0.0471557 ]
 [ 0.13546565 -0.05389222]
 [ 0.15239885 -0.06062875]
 [ 0.16933206 -0.06736528]] ft

Node 2 Reactions#

print("Reaction at node2 case 1:", forces1, force_unit1)
print("Reaction at node2 case 2:", forces2, force_unit2)
print("Reaction at node2 case 3:", forces3, force_unit3)
Reaction at node2 case 1: [[-0.3296672   0.49450079]
 [-0.65933439  0.98900159]
 [-0.98900159  1.48350238]
 [-1.31866878  1.97800317]
 [-1.64833598  2.47250397]
 [-1.97800317  2.96700476]
 [-2.30767037  3.46150556]
 [-2.63733757  3.95600635]
 [-2.96700476  4.45050714]
 [-3.29667196  4.94500794]] kN
Reaction at node2 case 2: [[ -329.6671958    494.50079369]
 [ -659.33439159   989.00158739]
 [ -989.00158739  1483.50238108]
 [-1318.66878318  1978.00317478]
 [-1648.33597898  2472.50396847]
 [-1978.00317478  2967.00476217]
 [-2307.67037057  3461.50555586]
 [-2637.33756637  3956.00634955]
 [-2967.00476217  4450.50714325]
 [-3296.67195796  4945.00793694]] N
Reaction at node2 case 3: [[ -74.11213365  111.16820047]
 [-148.22426729  222.33640094]
 [-222.33640094  333.5046014 ]
 [-296.44853458  444.67280187]
 [-370.56066823  555.84100234]
 [-444.67280187  667.00920281]
 [-518.78493552  778.17740327]
 [-592.89706916  889.34560374]
 [-667.00920281 1000.51380421]
 [-741.12133645 1111.68200468]] lbf

The displacement and force values depend on the base unit system you set up, but they are proportional to each other. Well, the rest is left to you to verify.

Remember that you are free to change the base unit system without rewriting the model code.