computeItemTest.py
You can view and download this file on Github: computeItemTest.py
1#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2# This is an EXUDYN example
3#
4# Details: mbs.ComputeItem computes what the solver computes for one item, at the current state (#2779) - for
5# tests and for the debugging of a model or of a user's item; exudyn.advancedUtilities.NumericalJacobian
6# gives the numerical derivative to compare with. A double pendulum of two rigid bodies, the first on
7# a revolute joint to the ground, the second on a spring-damper:
8# (1) mbs.ComputeItem(item) lists what applies to the item;
9# (2) the mass matrix and the right-hand side of a body, the forces of the spring-damper on its markers;
10# (3) the algebraic equations of the joint and its constraint Jacobian, which equals the numerical
11# derivative of the equations, and its reaction forces C_q^T lambda;
12# (4) the position Jacobian of a body at a point, the derivative of its velocity by the velocities;
13# (5) the kinematics of a marker, the rotation Jacobian of a node.
14#
15# Author: Johannes Gerstmayr
16# Date: 2026-10-02
17#
18# Copyright:This file is part of Exudyn. Exudyn is free software. You can redistribute it and/or modify it under the terms of the Exudyn license. See 'LICENSE.txt' for more details.
19#
20#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
21
22import exudyn as exu
23from exudyn.utilities import * #includes itemInterface and rigidBodyUtilities
24from exudyn.advancedUtilities import NumericalJacobian, ItemODE2Coordinates
25import numpy as np
26
27testIsActive = exu.sys.get('testIsActive', False)
28IC = exu.ComputeItemType
29
30SC = exu.SystemContainer()
31mbs = SC.AddSystem()
32oGround = mbs.AddObject(ObjectGround())
33inertia = InertiaCuboid(density=1000, sideLengths=[0.4, 0.05, 0.05])
34b0 = mbs.CreateRigidBody(inertia=inertia, referencePosition=[0.2, 0, 0], nodeType=exu.NodeType.RotationRxyz,
35 initialAngularVelocity=[0, 0, 1], gravity=[0, -9.81, 0], returnDict=True)
36b1 = mbs.CreateRigidBody(inertia=inertia, referencePosition=[0.6, 0, 0], nodeType=exu.NodeType.RotationRxyz,
37 gravity=[0, -9.81, 0], returnDict=True)
38oJoint = mbs.CreateRevoluteJoint(itemNumbers=[oGround, b0['bodyNumber']], position=[0, 0, 0], axis=[0, 0, 1])
39oSpring = mbs.CreateSpringDamper(itemNumbers=[b0['bodyNumber'], b1['bodyNumber']], localPosition0=[0.2, 0, 0],
40 localPosition1=[-0.2, 0, 0], stiffness=1e3, damping=1)
41p = [0.2, 0, 0] #a point of the first body
42marker = mbs.AddMarker(MarkerBodyRigid(bodyNumber=b0['bodyNumber'], localPosition=p))
43mbs.Assemble()
44
45simulationSettings = exu.SimulationSettings()
46simulationSettings.timeIntegration.newton.useModifiedNewton = False #Just for the test; modified Newton is usually faster
47simulationSettings.timeIntegration.numberOfSteps = 100
48simulationSettings.timeIntegration.endTime = 0.2
49simulationSettings.solution.file.write = False
50mbs.SolveDynamic(simulationSettings) #some state that is not the initial one
51
52body = b0['bodyNumber']
53#(1) what applies
54exu.Print('ComputeItem applies to the body:', [str(t) for t in mbs.ComputeItem(body)])
55exu.Print('ComputeItem applies to the joint:', [str(t) for t in mbs.ComputeItem(oJoint)])
56
57#(2) the body and the spring-damper
58massMatrix = mbs.ComputeItem(body, IC.MassMatrix)
59lhsBody = mbs.ComputeItem(body, IC.ODE2LHS)
60lhsSpring = mbs.ComputeItem(oSpring, IC.ODE2LHS)
61exu.Print('mass of the body from its mass matrix:', massMatrix[0, 0], ', expected', inertia.Mass())
62
63#(3) the joint
64equations = mbs.ComputeItem(oJoint, IC.AlgebraicEquations)
65constraintJacobian = mbs.ComputeItem(oJoint, IC.ConstraintJacobian)
66coordinates = ItemODE2Coordinates(mbs, oJoint)
67numerical = NumericalJacobian(mbs, lambda: mbs.ComputeItem(oJoint, IC.AlgebraicEquations), coordinates)
68errorCq = np.abs(constraintJacobian - numerical).max()
69exu.Print('joint equations', np.round(equations, 8), ', C_q against its numerical derivative:', errorCq)
70reactionForces = mbs.ComputeItem(oJoint, IC.ReactionForces)
71
72#(4) the position Jacobian at a point of the body
73positionJacobian = mbs.ComputeItem(body, IC.PositionJacobian, localPosition=p)
74numerical = NumericalJacobian(mbs, lambda: mbs.GetObjectOutputBody(body, exu.OutputVariableType.Velocity, localPosition=p),
75 ItemODE2Coordinates(mbs, body), velocities=True)
76errorJpos = np.abs(positionJacobian - numerical).max()
77exu.Print('position Jacobian against the derivative of the velocity:', errorJpos)
78
79#(5) a marker and a node
80kinematics = mbs.ComputeItem(marker, IC.Kinematics)
81exu.Print('marker position', np.round(kinematics['position'], 6))
82rotationJacobian = mbs.ComputeItem(b0['nodeNumber'], IC.RotationJacobian)
83
84testResult = (np.abs(massMatrix).sum() + np.abs(lhsBody).sum() + np.abs(lhsSpring).sum() + np.abs(constraintJacobian).sum()
85 + np.abs(reactionForces).sum() + np.abs(kinematics['position']).sum() + np.abs(rotationJacobian).sum())
86exu.Print('errors (both shall be small):', errorCq < 1e-6, errorJpos < 1e-8)
87exu.Print('solution of computeItemTest=', testResult)
88exu.sys['testResult'] = testResult