Revisions
What changed between one release of Exudyn and the next, in prose, for somebody who has models written against the previous one. Every single resolved issue is in the changelog, and the issues themselves are in the issue tracker; this chapter is the short version, and a new section is added on top of it at each release.
This chapter is deliberately short. The revision behind it is recorded step by step in a plan and a log, which are working documents of the project rather than user documentation: there are two of them, revision2026 - finished, and what version 1.12 is - and revision2026b, which carries what it did not finish. Both are linked from the developer documentation, together with the standing information document that holds the measured facts and the decisions.
Version 1.13
A revision of the whole project rather than a feature release: the layout of the repository, the build, the tests, the documentation and the development tools. Most of it is invisible from a model script. The parts that are not are first.
Note
The individual issues of this revision carry 1.11.x version numbers, because the micro version counts closed issues continuously and does not restart; 1.12.0 is the point at which the revision was declared complete. 1.12 itself was not published: 1.13.0 is the first release after 1.11 and carries the revision together with what followed it.
What can break a script
ObjectANCFThinPlate computes other values. Its curvatures use the material measure - for a strip
the curvature of ObjectANCFCable2D -, its integration rules changed (useReducedOrderIntegration 0: Gauss
5 x 5; 1 and 2: Lobatto 3 x 3 for the membrane, Gauss 2 x 2 for the bending), and a variable thickness gives
the stiffness from the local thickness. Results change slightly; the outputs change in form: StrainLocal
has 6 components through the thickness instead of the 3 membrane strains, CurvatureLocal and TorqueLocal
are in the order \(xx,\, yy,\, xy\) with one sign - positive where the surface bends towards its normal -, and
StressLocal has the opposite sign of its bending part (#2857, #2864).
bodyOrNodeList and bodyList of the Create functions are not accepted: the items are given in
itemNumbers; a script that passes either gets a TypeError that says so, and exudev scripts --fix renames
them (#2863).
Visualization parameters of items that draw nothing are gone: show of Node1D, the generic nodes,
ObjectGenericODE1, the coordinate markers, LoadCoordinate and SensorUserFunction, show and color of
ObjectConnectorCoordinateVector, and drawSize of ObjectConnectorGravity. A script that gives them, e.g.
visualization=VCoordinateVectorConstraint(show=False), leaves them out; exudev scripts finds them (#2843).
exudyn.AccessFunctionType.SuperElementAlternativeRotationMode is gone: it was a mode of
MarkerSuperElementRigid (useAlternativeApproach), not an access type, and is passed as an argument
now (#2744).
Star imports export only what a module defines. from exudyn.utilities import * used to drag
in everything that module had imported itself — np, sin, cos, sqrt, graphics and more.
Every module now declares __all__, so a script that relied on those names arriving through
Exudyn raises NameError. The fix is one line at the top of the script:
import numpy as np
from math import sin, cos, sqrt
import exudyn.graphics as graphics
from exudyn.utilities import * provides less (#2756): no longer the beam generators, the
MainSystem extensions and the old graphics helpers, which each have their own place. A script that
used them without importing them gets a NameError, and the fix is one line:
name |
write instead |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
GraphicsDataRectangle, GraphicsDataOrthoCubeLines and the color4... names are deprecated; the
scripts of the repository use exudyn.graphics for them. exudev scripts <folder> names every such
line of a script.
Names that are gone. Eleven small vector helpers were removed from exudyn.basicUtilities
— numpy does all of them, faster and in one call; what they did is in the
basicUtilities.py of Exudyn 1.11.0.
They returned lists, numpy returns arrays:
removed |
use |
|---|---|
|
|
|
|
|
|
|
|
|
|
The 23 deprecated GraphicsData... aliases in exudyn.utilities are gone as well; the current names
are in exudyn.graphics - mostly the old name without the prefix, GraphicsDataSphere is
graphics.Sphere. The exceptions: GraphicsDataOrthoCubePoint is graphics.Brick, GraphicsDataCube
graphics.Cuboid, GraphicsDataOrthoCube graphics.BrickXYZ, GraphicsDataLine graphics.Lines,
GraphicsDataFromSTLfileTxt graphics.FromSTLfileASCII, GraphicsData2PointsAndTrigs
graphics.ToPointsAndTrigs, ExportGraphicsData2STL graphics.ExportSTL and
MergeGraphicsDataTriangleList graphics.MergeTriangleLists
(the aliases of 1.11.0).
Functions that moved inside the package: from exudyn.utilities into basicUtilities,
advancedUtilities and mainSystemExtensions. A script that imports from exudyn.utilities in
the usual way notices nothing; one that imported such a function by its full module path imports it
from exudyn.utilities instead.
The files a run writes by default are in solution/: the solution file is
solution/coordinatesSolution.txt, and the solver information and the restart file are there as
well, so that a run writes nothing beside the script. A script that reads the solution file back as
'coordinatesSolution.txt' reads the new name 'solution/coordinatesSolution.txt' instead; a script
that sets its own file names keeps them. exudev scripts <folder> finds both, and every file a script
names without a directory.
InitializeFromRestartFile is gone from exudyn.utilities: it raised “not fully implemented” in
every version. The restart file is still written (simulationSettings.solution.restart); how a
simulation continues from it is #2850.
Item dictionaries are more forgiving, not less: a parameter that is left out keeps its default
or its current value, where it used to raise KeyError.
A settings file changes what a script does, if you make one. ~/.exudyn/config.json is read at
import into exudyn.special.overrideSettings and can override exudyn.config, any plain
visualizationSettings — in every such structure that is created, not only in a
SystemContainer — the size and position of a dialog, and the settings of the results monitor, which
has no file of its own (User settings that persist between runs). Nothing writes it by itself, import exudyn
prints one note naming what came from it, and EXUDYN_NO_USER_SETTINGS=1 ignores it — which is what
a bug report needs. The test suites set that variable for themselves.
ObjectContactConvexRoll.rBoundingSphere is read-only. It is computed from
coefficientsHull, and setting it never had an effect: the value was recomputed whenever the
parameters changed. It is no longer an argument of the item and can no longer be set, so a script
that passed it says so instead of being quietly ignored - delete the argument. Reading it — and pContact, the current
potential contact point — works as before, with mbs.GetObjectParameter(objectNumber, 'pContact').
OpenVR is removed. The --openvr build flag, the settings under
visualizationSettings.interactive.openVR, the openVR entry of the render state and the
example openVRengine.py are gone. It could only be used with a head mounted display or an
emulator, it was never part of a released wheel - it had to be compiled in - and it stood in the
way of the coming rendering work. A script that only sets openVR settings runs once those lines are
deleted; one that needs a head mounted display stays on Exudyn 1.11.
The text export of an image is removed. exportImages.saveImageFormat = 'TXT', the four
exportImages.saveImageAsText... settings and exudyn.plot.LoadImage are gone.
SC.renderer.GetGraphicsData() gives the same drawing elements, and more of them, as numpy arrays,
without a render window; exudyn.plot.PlotImage draws what it returns, so a vector figure of a model
is PlotImage(SC.renderer.GetGraphicsData(), fileName='model.pdf').
GraphicsData comes as rows. The functions of exudyn.graphics return points, normals, colors,
triangles and edges as 2D numpy arrays - one row per point, color or triangle - and so does
mbs.GetObject(..., addGraphicsData=True); they had been flat lists. Given to Exudyn, both forms are
read. A script that indexes a returned list as flat, g['points'][3*i+1], reshapes it first:
np.array(g['points']).reshape(-1,3)[i,1] works for both. graphics.Sphere returns the new type
Spheres for a whole sphere instead of a TriangleList; the functions of exudyn.graphics that need
triangles convert it (#2709).
Round primitives are curved. graphics.Cylinder, SolidOfRevolution (and with it Arrow, Basis,
Frame, RigidLink, BallBearingRings) and Torus return 6-node triangles (triangles6) and curved
rims (edges3) instead of flat triangles. nTiles keeps its meaning, the number of flat segments
around: half as many curved elements carry them, and the renderer draws at least those segments. A
script that reads g['triangles'] of such a shape gets the flat ones with
graphics.Triangles6ToTriangles(g); graphics.ToPointsAndTrigs does this itself, so a contact mesh
made from a cylinder with an even nTiles has the same facets as before, on more triangles (#2709).
The rotation of an ANCF slope node is one frame. NodePointSlope23 and the cross section of
ObjectANCFBeam rotate with the orthonormal frame of their slopes (\(
v_z\) normalized, \(
v_y\)
orthogonalized against it), and their angular velocity and rotation Jacobian are now its
derivatives; they were a least-squares fit of both slopes, which differs where the cross section
deforms. Joints and torques on these nodes converge as they should; the angular velocity output of a
deformed cross section, and results with joints on slope nodes, change slightly (#2763).
The time integration uses the modified Newton method by default. timeIntegration.newton.useModifiedNewton is
True: the Jacobian is kept over the steps and computed again only when Newton converges badly, which is much faster
for most models. Results change within the tolerances of Newton - in most models from the eighth digit on, with
contact and friction earlier; set it to False for the full Newton method. staticSolver.newton keeps the full Newton method (#2815).
The robotics classes compute with exudyn.HT. Robot.LinkHT, JointHT, COMHT, StdDH2HT, ModDHKK2HT,
InverseKinematicsNumerical.GetCurrentRobotHT and dictJointType2HT return exu.HT instead of 4x4 numpy arrays, and
RobotBase.HT, RobotTool.HT, RobotLink.localHT and preHT are stored as exu.HT, given as exu.HT or 4x4 matrix
as before; the kinematics is about twice as fast. Scripts written for numpy arrays keep working: an exu.HT is indexed
as its 4x4 matrix (H[0:3,3], also for writing, except the last row), H1 @ H2 is the product and H @ x what numpy
computes, copy.deepcopy copies it, and np.array(H) gives the matrix. What no longer works is numpy arithmetic on it,
such as H1 - H2: use np.array(H1) - np.array(H2) (#2821).
rigidBodyUtilities.HT is gone; its name is exudyn.HT now. The shortcut HT of the function
HomogeneousTransformation(A, r), which from exudyn.utilities import * brought into a script, is
removed, so that HT means one thing - the class exu.HT. A script that called HT(A, r) for a 4x4
array writes HomogeneousTransformation(A, r) (#2781).
What is new to use
Optional packages by name. pip install "exudyn[basic]" installs scipy and matplotlib with Exudyn, which most
models need; [common] adds what frequently used features need, [tests], [all] and [rl] the larger sets, see
Optional packages via the Exudyn extras.
Renamed settings keep working. A simulation setting that is renamed answers to its old name with a
DeprecationWarning that names the new one, as the visualization settings do: parallel.multithreadedLLimitLoads,
...Residuals, ...Jacobians and ...MassMatrices are parallel.multithreadedLowerLimitLoads and so on, until 2031 (#2588, #2800).
The same holds for item parameters: a renamed one is still taken under its old name - in the item class, in a
dictionary and by mbs.GetObjectParameter/SetObjectParameter - with a warning naming the new one; the page of the
item lists it (#2589).
mbs.CreateLinearSpringDamper creates a linear spring-damper along an axis between two rigid bodies or rigid
markers, with the arguments of the joints - itemNumbers, position, axis, useGlobalFrame -, e.g. inside a
prismatic joint (#1953).
A singular Jacobian with adaptive steps reduces the step. With adaptiveStep (the default) or automatic step size,
the static and the implicit dynamic solvers take a singular system Jacobian as a failed step and reduce the step size,
instead of stopping at once; a singularity of the model still ends the run, at the minimum step size, with the
causing coordinate in the solver message and file (#1337). SC.GetSystem(i) keeps its SystemContainer alive, as
SC.AddSystem() does, so that a system returned from a function works after the container went out of scope (#2851).
ObjectContactFrictionCircleCable2D without a friction model - frictionStiffness and frictionVelocityPenalty
zero - has no tangential force, also when its data node starts in slip (#1290).
A dynamic simulation continues from its restart file. With solution.restart.write the solvers write the state
every solution.restart.writePeriod and at the end, and with solution.restart.continueIfAvailable the same script,
started again, continues from it - appending to its solution and sensor files -, for a job that is stopped and
resubmitted; see the user manual, Continuing a simulation from its restart file (#2850).
The Create functions take bodies, nodes or markers in itemNumbers. CreateSpringDamper,
CreateCartesianSpringDamper, CreateRigidBodySpringDamper and CreateDistanceConstraint take a body at its local
position, a node or a marker for each side; the joints and CreateTorsionalSpringDamper a body or a rigid marker;
CreateCoordinateConstraint a body - its coordinates counted over its nodes -, a node, or None for the ground; the
sphere contacts the sphere on a body, node or marker and the triangle or quad on a body; CreateForce and
CreateTorque one body, node or marker in itemNumber. bodyNumbers and bodyNumber are the deprecated names
until 2031, which exudev scripts --fix rewrites; the rolling discs keep bodyNumbers, as they take bodies only
(#2863, #2337).
More markers on more bodies. ObjectRotationalMass1D takes forces and connectors anywhere on
its table, not only on its axis; ObjectANCFBeam takes MarkerBodyRigid, so torques and joints with
rotations act on its cross sections (#2775).
ANCF thin plates: damping, thickness and surface maps. ObjectANCFThinPlate has Kelvin-Voigt damping of
the membrane and the bending stiffness (stiffnessProportionalDamping, bendingStiffnessProportionalDamping),
which does not damp a rigid motion, and takes 12 thickness values - a thickness and its two gradients per
node, which ShellMesh computes from a thicknessFunction. ShellMesh(surfaceMap=...) maps the mesh onto a
surface, with the maps of exudyn.shells.SurfaceMap - SurfaceMap.Cylinder, ConeFrustum,
HemisphericalShell, ToroidalPanel, … - or a map of one’s own; exudyn.shells has functions for
constraints, edge loads and hinges of plate nodes (AddNodeConstraints, AddClampToGround, ApplyEdgeLoad,
AddEdgeSpringDamper, …). visualizationSettings.bodies.shells draws the contour at a thickness coordinate
(contourZeta), interpolated from the corners of the elements (reducedInterpolation), and along the normal
(drawNormal) (#2857, #2858, #2859, #2864).
GeneralContact can keep a contact while the bodies penetrate. With
keepContactWhilePenetrating = True a sphere-sphere or sphere-triangle contact acts as long as the
bodies penetrate, and its damping may pull at the end of the contact, as ObjectContactSphereSphere and
ObjectContactSphereTriangle do; by default it acts only while its force presses (#2848).
What an item computes, from Python. mbs.ComputeItem(item, what) computes, at the current state,
what the solver computes for one object, node or marker - the position and rotation Jacobians of a
body at a local position, its mass matrix and right-hand side, the forces of a connector, the
equations, constraint Jacobian and reaction forces of a joint, the kinematics of a marker -, and
mbs.ComputeItem(item) lists what applies (exu.ComputeItemType).
exudyn.advancedUtilities.NumericalJacobian gives the numerical derivative to compare with, for
testing a model or an item of one’s own (#2779).
exudyn.HT, the homogeneous transformation of the C++ core. exu.HT(rotation=A, translation=p)
composes with * (H1*H2, and H*v for a point), inverts (Inverse()), and converts to and from the
4x4 matrix (HT44(), exu.HT(T44)) - faster than the 4x4 numpy arrays of
exudyn.rigidBodyUtilities, whose function HomogeneousTransformation stays (#2780). It converts the rotation from and to Euler
parameters, Tait-Bryan angles and the rotation vector, and the reference coordinates of the three rigid body nodes
(GetCoordinatesEP(), SetCoordinatesRxyz(q), …), gives the frame of one HT seen from another (Relative),
interpolates (InterpolateSO3, InterpolateSE3) and the angle and axis of its rotation; its Set functions return it,
so that exu.HT().SetRotationAxis(axis, angle) is one line (#2810). LogSE3() and SetExpSE3(v) give and take the
motion vector of a screw motion, LogR3xSO3() and SetExpR3xSO3(v) the translation and the rotation vector (#2819).
The example homogeneousTransformationInterpolation.py drives rigid bodies along both interpolations; exu.HT(H)
copies an HT (#2824).
The output variable HomogeneousTransformation. Every node, body point, marker and connector that
gives Position and RotationMatrix also gives HomogeneousTransformation, the 4x4 matrix [A p; 0 1]:
GetNodeOutput, GetObjectOutputBody and GetMarkerOutput return it as an exu.HT, a sensor stores
its 16 values row by row, from which exu.HT(values) makes the HT (#2780, #2789, #2792).
A frame as one parameter. ObjectGround takes its frame as referencePosition and
referenceRotation or at once as referenceHT - a 4x4 matrix, its 16 values or an exu.HT; a
parameter left None is not given, and an HT given with one of its parts must agree with it.
CreateGround and CreateRigidBody take referenceHT, and CreateRigidBody also initialHT for
initialRotationMatrix and initialDisplacement (#2793, #2794). The rigid markers take localHT, a frame
with a rotation in the body, link or node: MarkerBodyRigid and MarkerKinematicTreeRigid with localPosition as
its translation, MarkerSuperElementRigid with offset, MarkerNodeRigid a rotation only - so a joint can take its
axis from its markers instead of rotationMarker0/1 (#2795).
ObjectKinematicTree takes its joint transformations and offsets as one list jointHTs, and numpy reads an exu.HT
as its 4x4 matrix (np.array(H)), so the HT functions of exudyn.rigidBodyUtilities and the robotics classes take an
exu.HT wherever they take a 4x4 array (#2798, #2799).
The rotation of a joint is its markers’. rotationMarker0/1 of ObjectJointGeneric, ObjectJointRevoluteZ,
ObjectJointPrismaticX, ObjectConnectorRigidBodySpringDamper and ObjectConnectorTorsionalSpringDamper are deprecated and removed in 2031: the rotation is given to the
markers as localHT, e.g. MarkerBodyRigid(bodyNumber=b, localHT=HomogeneousTransformation(A, p)); setting one
other than the unit matrix gives a DeprecationWarning at the line that adds the item. The Create... functions,
AddRevoluteJoint, AddPrismaticJoint, GetJointArgs and the robotics classes put the rotation into the markers
they create (#2745). ObjectConnectorRigidBodySpringDamper applies rotationMarker0 after the frame of marker 0,
as the joints do and as its page says; a model with a rotationMarker0 other than the unit matrix moves differently
than before (#2801). ObjectContactCurveCircles has no rotationMarker0 any more: it was used only for drawing,
and Assemble() refused any value other than the unit matrix; the curve lies in the frame of marker 0 (#2803).
Deprecations: once, counted, and found. A deprecated setting, item parameter or function warns once per session
and name; exu.special.deprecations.warnOnce = False shows every use, to find all of them in a script. Every use is
counted in exu.sys['deprecationUse'], by source (simulationSettings, visualizationSettings, items, functions)
and name, so a test sees whether a model used anything deprecated (recordUse). exudev scripts also reports deprecated
and removed item parameters, as keyword of the item class and as key of an item dictionary (#2804, #2805, #2806).
The deprecated functions of the Python library warn as well. AddRigidBody, AddRevoluteJoint,
AddPrismaticJoint, graphics.BrickXYZ, GenerateStraightLineANCFCable2D and the others marked DEPRECATED in their
description, and the arguments bodyList of the Create... functions and numberOfChildren/survivingIndividuals of
GeneticOptimization, give a DeprecationWarning at the line of the script, once per session, and are counted in
exu.sys['deprecationUse']['library']; each says what to use instead. They, and the deprecated functions of the C++
module, are removed in 2029; exudev scripts reports them in a script (#2807).
The simulation settings, reordered. No substructure ends in Settings any more, everything of the solution file
is in solution.file, of the sensors in solution.sensors, of the restart file in solution.restart, and the solver of
mbs.SolveDynamic is a setting. Every old name still works until 2031, with a DeprecationWarning naming the new one,
and exudev scripts <folder> --fix rewrites a script to the new names (#2813):
old |
new |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
timeIntegration.solverType (default GeneralizedAlpha) is the solver mbs.SolveDynamic uses; a solverType given
to SolveDynamic is used for that run and the setting is set back afterwards - SolveDynamic no longer leaves the
explicit solver type written into the settings. An exudyn.MainSolverExplicit used directly takes the solver from
timeIntegration.solverType and refuses an implicit one with a message naming the setting.
The binary solution file (solution.file.binary) stores its numbers as float if solution.precision is below 8 and
as double otherwise (#2816).
Item parameters without the prefix physics, and a few more renamed. The parameters of the bodies and finite
elements lost their prefix - physicsMass is mass, physicsInertia is inertia, physicsCenterOfMass is
centerOfMass, physicsLength, physicsMassPerLength, physicsAxialStiffness, physicsBendingDamping and the others
of the beams and the plate likewise; radiusSphere is sphereRadius (ContactSphereTorus, ContactSphereTriangle),
the friction of ObjectConnectorCoordinateSpringDamperExt is dynamicFrictionForce, staticFrictionOffsetForce and
viscousFrictionFactor (N s/m), factorValue1 of ObjectConnectorCoordinate is factor1, rollingFrictionViscous
of ObjectConnectorRollingDiscPenalty is rollingViscousFriction (s/m), and intrinsicFormulation and
classicalFormulation are useIntrinsicFormulation and useClassicalFormulation. The Create... functions take the
new names. Every old name still works until 2031 with a DeprecationWarning - in the item class, in a dictionary, in
GetObjectParameter/SetObjectParameter and as argument of a Create... function - and exudev scripts --fix
rewrites it (#2814).
A system without coordinates - only ground, sensors and user functions - is solved by every solver: time advances, the user functions are called and the sensors record (#2790).
The frame of a rigid marker is drawn with visualizationSettings.markers.showBasis and
basisSize: three lines in red, green and blue, or three arrows with short heads (#2791).
Curved shapes in GraphicsData. 6-node (quadratic) triangles - the key triangles6 of a
TriangleList - quadratic lines (Lines with shape 'quadratic') and quadratic edges (edges3)
are drawn curved: the renderers split them when they draw, as fine as
visualizationSettings.openGL.advanced.curvedTriangleTilingAngle asks, and a change of it shows at
once; each edge is split by its own curvature, so a surface curved in one direction is not split along the
other. The type Spheres draws many spheres at once, and the raytracer intersects them exactly. The round
shapes of exudyn.graphics - Cylinder, Sphere (also partial and hollow), Torus, SolidOfRevolution,
Tube, LinkedCylinders and the shapes built from them - consist of 6-node triangles, as do the surfaces of
quadratic NGsolve meshes and of Tet10 meshes (FEMinterface.VolumeToSurfaceElements) in the FFRF objects.
python/Examples/graphicsCurvedShapes.py shows them all (#2709).
A command line for the installed package: python -m exudyn info prints the version, where
the package is installed, which compiled module is loaded and which optional packages are present
— the first thing to put into a bug report. monitor, plot and demo are the other three
commands; see The command line: python -m exudyn.
Errors say what kind of problem they are. Everything Exudyn raises derives from
exudyn.ExudynError and from the matching Python built-in, so except ValueError still works
and except exudyn.SolverError is now possible — see
Errors: what Exudyn raises.
Two environment variables keep a run quiet, which matters for parameter studies and for
anything that runs unattended: EXUDYN_SUPPRESS_UI_WINDOW_OPEN=1 opens no renderer, viewer, plot
or dialog window, and EXUDYN_OUTPUTDIRECTORY=<path> puts the files a model writes where you want
them.
The windows can remember where you put them. The settings dialog has a store settings and a
store positions button, the render window has a size and a position among its settings
(view0.window.renderWindowSize and renderWindowPosition), and
exudyn.plot.StorePlotWindowGeometry() keeps the plot windows of PlotSensor where they are
arranged. Everything goes into ~/.exudyn/config.json, after showing what will be written
(User settings that persist between runs).
The scene as data: SC.renderer.GetGraphicsData() returns every line, sphere, circle, text and
triangle the renderer would draw, each with the item that drew it, and needs no window - for a test
that checks what a model looks like, or for a figure drawn with matplotlib.
Stopping a simulation from a script: SC.renderer.StopSimulation() does what closing the
render window does - the running simulation ends after its step, a later one before its first, and
neither is reported as a solver failure - from a user function, another thread or a test;
mbs.SetRenderEngineStopFlag(False) lets the next one run again.
exudyn.types answers questions about items from Python: which markers an object accepts,
which item types exist.
mbs.Inspect(itemIndex, what) asks an existing item what it provides and requests - its output
variables, its type flags, the node and marker types it requests, the access functions of a body -
as lists of the exported enumerations; what is a member of exu.InspectType, or None for all
that apply. A node marker answers the node types its node must provide as requirements, each a list of
alternatives (#2817). The test model inspectTest.py shows it.
Tutorials as notebooks. python/Notebooks/ holds the tutorials as Jupyter notebooks, stored with the outputs of
their last run; the documentation shows them with these outputs, and python/Examples/notebooks/ holds the same code as
scripts. exudev notebooks runs them and stores the outputs. exudyn.interactive.ShowImage(SC) shows the scene in a notebook -
an image of the raytracer, without a window (#2831). The examples of the Python-C++ interface chapter are notebooks
of python/Notebooks/reference/, those of the user manual’s pages on graphics, solving and the renderer notebooks of
python/Notebooks/snippets/; the tests run them, so each example on these pages shows what it printed.
Kinetic and potential energy as output variables (OutputVariableType.KineticEnergy,
PotentialEnergy) of the bodies, beams, plates, superelements and spring-dampers, and
exudyn.advancedUtilities.SystemEnergy for the energy of a whole system with its loads; an item
that cannot compute its energy - with a user function defining its force - says so, and
mbs.Inspect does not list it. The test models energiesTest.py and energiesFlexibleBodiesTest.py
show them.
Springs and ropes drawn as tubes. visualizationSettings.connectors.curveTiling is the number of segments of a
full turn of a spring winding or of the arc of a rope on a sheave, and connectors.drawSimplified = False draws the windings
of the springs as a tube instead of lines and ObjectConnectorDistance as a rod of its drawSize; the rope of
ObjectConnectorReevingSystemSprings is one closed tube along its spans and arcs (#2839, #2843). A 3D beam without
section geometry is drawn as a line, and its contour colors follow it (#2837, #2843). ObjectConnectorGravity, if
shown, is a line between its markers, and SensorLoad is drawn at the marker of its load (#2843).
What is new to read
The documentation is Markdown and is built with Sphinx for every release; the hand-written
chapters are docs/manual/, and everything under docs/generated/ — the reference manual of all
items, the Python utilities, the examples and the test models — is written by the generators from
the definitions and the docstrings.
Until this release the documentation was one PDF, theDoc.pdf — a name that still appears in
older issues, in scripts and in printed notes. It is the HTML documentation now: the same content,
searchable, and one place per subject instead of a chapter and a PDF section saying it twice.
Installing and building are told once each. The installation instructions come before the first example, and building from source - which used to be described in three places that disagreed - is one page of the developer documentation, together with a page that takes a new developer from cloning the repository to a passing test run and one on the git workflow (#2646).
CHANGELOG.md is new: every resolved issue of the current release, newest first, with
a table of every release above it. The version number says where an issue landed — the micro
version is the count of issues closed in a release, so 1.10.160 is the 160th issue closed in
release 1.10. The earlier releases are on the issue tracker page, which carries more about each
issue than the changelog does, and the two no longer print the same list twice (#2599).
What changed behind the scenes
The wheels for Windows, Linux and macOS are built by continuous integration on every tagged
release, and the documentation is built the same way. Everything else a maintainer does — build,
test, documentation, issues, release — runs through one driver, exudev, which replaced sixteen
batch files. None of that changes anything for a user of a wheel; it changes how quickly a fix
reaches one.