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.pre.UnitSystem.
[1]:
import numpy as np
import openseespy.opensees as ops
import opstool as opst
length_unit = "m" # base unit
force_unit = "kN" # base unit
UNIT = opst.pre.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
These other units will be automatically converted to the base units you have set!
Truss example¶
Let’s look at a truss example. You can set the practical values of structural parameters in the model, and opstool.pre.UnitSystem will help you automatically convert to the base unit system you specify.
[2]:
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.0 * 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:
[3]:
length_unit1 = "m"
force_unit1 = "kN"
UNIT1 = opst.pre.UnitSystem(length=length_unit1, force=force_unit1)
u1, forces1, f1 = model(UNIT=UNIT1)
length_unit2 = "cm"
force_unit2 = "N"
UNIT2 = opst.pre.UnitSystem(length=length_unit2, force=force_unit2)
u2, forces2, f2 = model(UNIT=UNIT2)
length_unit3 = "ft"
force_unit3 = "lbf"
UNIT3 = opst.pre.UnitSystem(length=length_unit3, force=force_unit3)
u3, forces3, f3 = model(UNIT=UNIT3)
WARNING - the 'fullGenLapack' eigen solver is VERY SLOW. Consider using the default eigen solver.
Let’s verify it!
Structure Frequency¶
[4]:
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¶
[5]:
print("Dispalcement at node4:")
# 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)
print(f"{length_unit2}/{length_unit1}")
print(u2 / u1)
print(f"{length_unit1}/{length_unit3}")
print(u1 / u3)
Dispalcement at node4:
cm/m
[[100. 100.]
[100. 100.]
[100. 100.]
[100. 100.]
[100. 100.]
[100. 100.]
[100. 100.]
[100. 100.]
[100. 100.]
[100. 100.]]
m/ft
[[0.3048 0.3048]
[0.3048 0.3048]
[0.3048 0.3048]
[0.3048 0.3048]
[0.3048 0.3048]
[0.3048 0.3048]
[0.3048 0.3048]
[0.3048 0.3048]
[0.3048 0.3048]
[0.3048 0.3048]]
1 m = 100 cm
1 ft = 0.3048 m
Node 2 Reactions¶
[6]:
print("Reaction at node2:")
# 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)
print(f"{force_unit2}/{force_unit1}")
print(forces2 / forces1)
print(f"{force_unit3}/{force_unit1}")
print(forces3 / forces1)
Reaction at node2:
N/kN
[[1000. 1000.]
[1000. 1000.]
[1000. 1000.]
[1000. 1000.]
[1000. 1000.]
[1000. 1000.]
[1000. 1000.]
[1000. 1000.]
[1000. 1000.]
[1000. 1000.]]
lbf/kN
[[224.80894244 224.80894244]
[224.80894244 224.80894244]
[224.80894244 224.80894244]
[224.80894244 224.80894244]
[224.80894244 224.80894244]
[224.80894244 224.80894244]
[224.80894244 224.80894244]
[224.80894244 224.80894244]
[224.80894244 224.80894244]
[224.80894244 224.80894244]]
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.