Loads and springs
Loads
All loads take an id and a case tag (default :LC1). Given a node n2 and a frame element beam:
NodeForce(n2, [0.0, 0.0, -50.0]; case = :live)
NodeMoment(n2, [0.0, 1e3, 0.0])
LineLoad(beam, [0.0, 0.0, -2.0]) # uniform, full span
TrapezoidLoad(beam, 0.2, 0.8, 1.0, 3.0, [0.0, 0.0, -1.0]) # partial-span, varying
DistributedLoad(beam, [0.0, 0.3, 0.5], [0.0, 4.0, 0.0], # arbitrary piecewise-linear
[0.0, 0.0, -1.0])
PointLoad(beam, 0.4, [0.0, 0.0, -10.0]) # at 40% of the span
PointMoment(beam, 0.5, [0.0, 0.0, 800.0]) # concentrated moment
SelfWeight(beam; g = [0.0, 0.0, -9.81]) # from ρA(section)SelfWeight{Float64}(FrameElement(:beam, L=6.0), [0.0, 0.0, -9.81], 1.0, :selfweight, :LC1)Every distributed shape lowers to one canonical piecewise-linear type with one exact integration (3-point Gauss against the element shape functions — exact, not approximate, for these loads). Adding a custom element load type means implementing a single method, fixed_end_forces.
Spring supports
Elastic supports are applicative: a NodalSpring references its node (the way a load does) and lives on the model — nodes don't know about their springs, and several springs on one node add up.
sb = Node([0.0, 0.0, 0.0], :fixed)
tip = Node([0.0, 0.0, 3.0], :free)
soil = NodalSpring(tip, [0.0, 0.0, 5e4, 0.0, 0.0, 0.0], :soil) # vertical only
pad = NodalSpring(tip, 1e5) # uniform translational
smodel = Model([sb, tip], AbstractElement{Float64}[FrameElement(sb, tip, wshape)],
AbstractLoad{Float64}[NodeForce(tip, [1.0, 0.0, -10.0])]; springs = [soil])
solve!(smodel)
displacement(smodel.results, tip)6-element StaticArraysCore.SVector{6, Float64} with indices SOneTo(6):
0.0005624999999999996
5.740531871003215e-20
-1.3953488372093024e-5
-2.8702659355016075e-20
0.0002812499999999998
0.0Spring reactions are recovered as −k·u in post-processing.