Symbolic user functions

Notebook python/Notebooks/tutorialSymbolic.ipynb.

A nonlinear oscillator: a mass point on a Cartesian spring-damper whose force is a user function, written once in Python and turned into a symbolic user function, which runs much faster. The outputs below each cell are those of the last run.

import exudyn as exu
from exudyn.utilities import *  # includes itemInterface and rigidBodyUtilities
import exudyn.graphics as graphics

SC = exu.SystemContainer()
mbs = SC.AddSystem()
esym = exu.symbolic   #an abbreviation of the symbolic library; often it can be replaced by numpy

The parameters of the spring-damper system:

L = 0.5
mass = 1.6
k = 4000
omega0 = 50  # sqrt(k / mass)
dRel = 0.05
d = dRel * 2 * omega0
u0 = -0.08
v0 = 1
f = 80

The ground and the mass point with its initial conditions, the Cartesian spring-damper between them, a force on the mass point, and a sensor of its position:

objectGround = mbs.CreateGround(referencePosition=[0, 0, 0])
massPoint = mbs.CreateMassPoint(referencePosition=[L, 0, 0],
                                initialDisplacement=[u0, 0, 0],
                                initialVelocity=[v0, 0, 0],
                                mass=mass)
csd = mbs.CreateCartesianSpringDamper(itemNumbers=[objectGround, massPoint],
                                      stiffness=[k, k, k],
                                      damping=[d, 0, 0],
                                      offset=[L, 0, 0])
load = mbs.CreateForce(itemNumber=massPoint, loadVector=[f, 0, 0])
sMass = mbs.AddSensor(SensorBody(bodyNumber=massPoint, storeInternal=True,
                                 outputVariableType=exu.OutputVariableType.Position))

The user function of the spring-damper, with Python or symbolic expressions - esym.sign works for both:

def springForceUserFunction(mbs, t, itemNumber, u, v, k, d, offset):
    return [0.5 * u[0]**2 * k[0] + esym.sign(v[0]) * 10, k[1] * u[1], k[2] * u[2]]

Up to here, this is a regular Python user function. CreateSymbolicUserFunction records it with symbolic arguments and makes a symbolic user function, which is evaluated in C++; printing it shows the recorded expressions:

doSymbolic = True
CSDuserFunction = springForceUserFunction   #used if no symbolic user function is made
if doSymbolic:
    CSDuserFunction = CreateSymbolicUserFunction(mbs, springForceUserFunction, 'springForceUserFunction', csd)
    print('user function:\n', CSDuserFunction)
user function:
 [(((0.5 * pow(u[0], 2)) * k[0]) + (sign(v[0]) * 10)),(k[1] * u[1]),(k[2] * u[2])]

The user function goes to the object; then assemble and solve, with an explicit solver and small steps:

mbs.SetObjectParameter(csd, 'springForceUserFunction', CSDuserFunction)
mbs.Assemble()

simulationSettings = exu.SimulationSettings()
tEnd = 2
steps = 200000
simulationSettings.timeIntegration.numberOfSteps = steps
simulationSettings.timeIntegration.endTime = tEnd
simulationSettings.solution.file.write = False
simulationSettings.solution.sensors.writePeriod = 0.001

mbs.SolveDynamic(simulationSettings, solverType=exu.DynamicSolverType.ExplicitMidpoint)

n1 = mbs.GetObject(massPoint)['nodeNumber']
u = mbs.GetNodeOutput(n1, exu.OutputVariableType.Position)
print('u=', u)
u= [0.69752247 0.         0.        ]

The position of the mass point over time:

mbs.PlotSensor(sMass, components=[0], closeAll=True)
../../../_images/tutorialSymbolic_0.png