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.0

Spring reactions are recovered as −k·u in post-processing.