```{raw} latex \clearpage ``` (sec-item-objectgenericode2)= # ObjectGenericODE2 A system of $n$ second order ordinary differential equations ({ref}`ODE2 `), having a mass matrix, damping/gyroscopic matrix, stiffness matrix and generalized forces. It can combine generic nodes, or node points. User functions can be used to compute mass matrix and generalized forces depending on given coordinates. NOTE: all matrices, vectors, etc. must have the same dimensions $n$ or $(n \times n)$, or they must be empty $(0 \times 0)$, except for the mass matrix which always needs to have dimensions $(n \times n)$. ## Interface - Body markers that can be placed on it: [](#sec-item-markersuperelementposition), [](#sec-item-markersuperelementrigid) - Nodes: see the detailed description ## Parameters The parameters of the item; in a dictionary, its type is 'GenericODE2': | name | type | size | default | description | |---|---|---|---|---| | **name** | String | | '' | objects's unique name | | **nodeNumbers** | ArrayNodeIndex | | [] | (symbol: $\mathbf{n}_n = [n_0,\,\ldots,\,n_n]\tp$) node numbers which provide the coordinates for the object (consecutively as provided in this list) | | **massMatrix** | PyMatrixContainer | | [] | (symbol: $\Mm \in \Rcal^{n \times n}$) mass matrix of object as MatrixContainer (or numpy array / list of lists) | | **stiffnessMatrix** | PyMatrixContainer | | [] | (symbol: $\Km \in \Rcal^{n \times n}$) stiffness matrix of object as MatrixContainer (or numpy array / list of lists); NOTE that (dense/sparse triplets) format must agree with dampingMatrix and jacobianUserFunction | | **dampingMatrix** | PyMatrixContainer | | [] | (symbol: $\Dm \in \Rcal^{n \times n}$) damping matrix of object as MatrixContainer (or numpy array / list of lists); NOTE that (dense/sparse triplets) format must agree with stiffnessMatrix and jacobianUserFunction | | **forceVector** | NumpyVector | | [] | (symbol: $\fv \in \Rcal^{n}$) generalized force vector added to RHS | | **forceUserFunction** | ObjectGenericODE2ForceUserFunction | | 0 | (symbol: $\fv_{user} \in \Rcal^{n}$) A Python user function which computes the generalized user force vector for the {ref}`ODE2 ` equations; see description below | | **massMatrixUserFunction** | ObjectGenericODE2MassMatrixUserFunction | | 0 | (symbol: $\Mm_{user} \in \Rcal^{n\times n}$) A Python user function which computes the mass matrix instead of the constant mass matrix given in $\Mm$; return numpy array or MatrixContainer; see description below | | **jacobianUserFunction** | ObjectGenericODE2JacobianUserFunction | | 0 | (symbol: $\Jm_{user} \in \Rcal^{n\times n}$) A Python user function which computes the jacobian, i.e., the derivative of the left-hand-side object equation w.r.t. the coordinates (times $f_{ODE2}$) and w.r.t. the velocities (times $f_{ODE2_t}$). Terms on the RHS must be subtracted from the LHS equation; the respective terms for the stiffness matrix and damping matrix are automatically added; see description below | | **coordinateIndexPerNode** | ArrayIndex | | [] | this list contains the local coordinate index for every node, which is needed, e.g., for markers; the list is generated automatically every time parameters have been changed | | **tempCoordinates** | NumpyVector | | [] | (symbol: $\cv_{temp} \in \Rcal^{n}$) temporary vector containing coordinates | | **tempCoordinates_t** | NumpyVector | | [] | (symbol: $\dot \cv_{temp} \in \Rcal^{n}$) temporary vector containing velocity coordinates | | **tempCoordinates_tt** | NumpyVector | | [] | (symbol: $\ddot \cv_{temp} \in \Rcal^{n}$) temporary vector containing acceleration coordinates | | **visualization** | VObjectGenericODE2 | | | parameters for visualization of item | ## Visualization parameters The parameters of `VObjectGenericODE2`, given as `visualization`: | name | type | size | default | description | |---|---|---|---|---| | **show** | Bool | | True | set true, if item is shown in visualization and false if it is not shown | | **color** | Float4 | 4 | [-1.,-1.,-1.,-1.] | RGBA color for object; 4th value is alpha-transparency; R=-1.f means, that default color is used | | **triangleMesh** | NumpyMatrixI | | [] | a matrix of node numbers referring to the mesh nodes of the object, one triangle per row: 3 columns for flat triangles, or 6 for 6-node triangles drawn curved - the corners counterclockwise seen from outside, then the mid nodes of the edges 0-1, 1-2 and 2-0, as in GraphicsData triangles6; the mesh uses the nodes to visualize the underlying object; contour plot colors are still computed in the local frame! | | **showNodes** | Bool | | False | set true, nodes are drawn uniquely via the mesh, eventually using the floating reference frame, even in the visualization of the node is show=False; node numbers are shown with indicator 'NF' | | **graphicsDataUserFunction** | ObjectGenericODE2GraphicsDataUserFunction | | 0 | A Python function which returns a bodyGraphicsData object, which is a list of graphics data in a dictionary computed by the user function; the graphics data is draw in global coordinates; it can be used to implement user element visualization, e.g., beam elements or simple mechanical systems; note that this user function may significantly slow down visualization | ## Drawing Drawn as [all super elements](#sec-drawing-objectssuperelement); its `triangleMesh` on the nodes, deformed with `bodies.deformationScaleFactor`, and with `showNodes` its nodes; or what its `graphicsDataUserFunction` returns. Settings beyond those of all super elements: [`nodes.defaultSize`](#sec-vsettingsnodes), [`nodes.drawNodesAsPoint`](#sec-vsettingsnodes), [`nodes.show`](#sec-vsettingsnodes), [`nodes.showNumbers`](#sec-vsettingsnodes), [`nodes.tiling`](#sec-vsettingsnodes), [`openGL.advanced.initialMaxSceneSize`](#sec-vsettingsopengladvanced). ## Output variables Available as `OutputVariableType` in sensors, `Get...Output()` and other functions: ```{tabularcolumns} \Y{0.25}\Y{0.25}\Y{0.5} ``` | output variable | symbol | description | |---|---|---| | CoordinatesTotal | | all {ref}`ODE2 ` displacement plus reference coordinates of object | | Coordinates | | all {ref}`ODE2 ` (displacement) coordinates | | Coordinates_t | | all {ref}`ODE2 ` velocity coordinates | | Coordinates_tt | | all {ref}`ODE2 ` acceleration coordinates | | Force | | generalized forces for all coordinates (residual of all forces except mass*accleration; corresponds to ComputeODE2LHS) | | KineticEnergy | $T = \frac{1}{2} \dot\qv\tp \Mm\, \dot\qv$ | kinetic energy from the mass matrix of the current state and the velocities of the nodes; current configuration only; localPosition must be $[0,0,0]$ | | PotentialEnergy | $V = \frac{1}{2} \qv\tp \Km\, \qv - \fv\tp \qv$ | elastic energy of the stiffness matrix and the potential of the constant force vector; raises for a forceUserFunction; localPosition must be $[0,0,0]$ | (description-objectgenericode2)= ## Detailed description ### Additional output variables for superelement node access Functions like `GetObjectOutputSuperElement(...)`, see [](#sec-mainsystem-object), or `SensorSuperElement`, see [](#sec-mainsystem-sensor), directly access special output variables (`OutputVariableType`) of the mesh nodes of the superelement. Additionally, the contour drawing of the object can make use the `OutputVariableType` of the meshnodes. For this object, all nodes of `ObjectGenericODE2` map their `OutputVariableType` to the meshnode $\ra$ see at the according node for the list of `OutputVariableType`. ### Equations of motion An object with node numbers $[n_0,\,\ldots,\,n_n]$ and according numbers of nodal coordinates $[n_{c_0},\,\ldots,\,n_{c_n}]$, the total number of equations (=coordinates) of the object is $$ n = \sum_{i} n_{c_i}, $$ which is used throughout the description of this object. ### Equations of motion The equations of motion read, $$ \Mm \ddot \qv + \Dm \dot \qv + \Km \qv = \fv + \fv_{user}(mbs, t, i_N,\qv,\dot \qv) $$ (eq-objectgenericode2-eom) Note that the user function $\fv_{user}(mbs, t, i_N,\qv,\dot \qv)$ may be empty (=0), and `iN` represents the itemNumber (=objectNumber). In case that a user mass matrix is specified, {eq}`eq-objectgenericode2-eom` is replaced with $$ \Mm_{user}(mbs, t, i_N, \qv,\dot \qv) \ddot \qv + \Dm \dot \qv + \Km \qv = \fv + \fv_{user}(mbs, t, i_N, \qv,\dot \qv) $$ The (internal) Jacobian $\Jm$ of {eq}`eq-objectgenericode2-eom` (assuming $\fv$ to be constant!) reads $$ \Jm = f_{ODE2} \left(\Km - \frac{\partial \fv_{user}(mbs, t, i_N,\qv,\dot \qv)}{\partial \qv}\right) + f_{ODE2_t} \left(\Dm - \frac{\partial \fv_{user}(mbs, t, i_N,\qv,\dot \qv)}{\partial \dot \qv} \right) + $$ Chosing $f_{ODE2} = 1$ and $f_{ODE2_t}=0$ would immediately give the jacobian of position quantities. If no `jacobianUserFunction` is specified, the jacobian is -- as with many objects in Exudyn -- computed by means of numerical differentiation. In case that a `jacobianUserFunction` is specified, it must represent the jacobian of the {ref}`LHS ` of {eq}`eq-objectgenericode2-eom` without $\Km$ and $\Dm$ (these matrices are added internally), $$ \Jm_{user}(mbs, t, i_N, \qv, \dot \qv, f_{ODE2}, f_{ODE2_t}) = -f_{ODE2} \left(\frac{\partial \fv_{user}(mbs, t, i_N,\qv,\dot \qv)}{\partial \qv} \right) - f_{ODE2_t} \left(\frac{\partial \fv_{user}(mbs, t, i_N,\qv,\dot \qv)}{\partial \dot \qv} \right) $$ (eq-objectgenericode2-jac) For clarification also see the **example** in `TestModels/linearFEMgenericODE2.py`. CoordinateLoads are added for the respective {ref}`ODE2 ` coordinate on the RHS of the latter equation. ### Marker interfaces The mesh nodes of the object are its nodes: mesh node $i$ is node $n_i$ of `nodeNumbers`, at the position of that node. `MarkerSuperElementPosition` and `MarkerSuperElementRigid` act on them, with the Jacobians of the nodes and the weights of the marker; a load or constraint on single coordinates uses `MarkerObjectODE2Coordinates` or the coordinate markers of the nodes. The general body markers - `MarkerBodyPosition`, `MarkerBodyRigid`, `MarkerBodyMass` - do **not** work on this object, whose body access functions are not available: `Assemble()` refuses them. **Userfunction**: `forceUserFunction(mbs, t, itemNumber, q, q_t)` A user function, which computes a force vector depending on current time and states of object. Can be used to create any kind of mechanical system by using the object states. Note that itemNumber represents the index of the ObjectGenericODE2 object in mbs, which can be used to retrieve additional data from the object through `mbs.GetObjectParameter(itemNumber, ...)`, see the according description of `GetObjectParameter`. | arguments / return | type or size | description | |---|---|---| | `mbs` | MainSystem | provides MainSystem mbs to which object belongs | | `t` | Real | current time in mbs | | `itemNumber` | Index | integer number $i_N$ of the object in mbs, allowing easy access to all object data via mbs.GetObjectParameter(itemNumber, ...) | | `q` | Vector $\in \Rcal^n$ | object coordinates (e.g., nodal displacement coordinates) in current configuration, without reference values | | `q_t` | Vector $\in \Rcal^n$ | object velocity coordinates (time derivative of `q`) in current configuration | | **return value** | Vector $\in \Rcal^{n}$ | returns force vector for object | **Userfunction**: `massMatrixUserFunction(mbs, t, itemNumber, q, q_t)` A user function, which computes a mass matrix depending on current time and states of object. Can be used to create any kind of mechanical system by using the object states. | arguments / return | type or size | description | |---|---|---| | `mbs` | MainSystem | provides MainSystem mbs to which object belongs to | | `t` | Real | current time in mbs | | `itemNumber` | Index | integer number $i_N$ of the object in mbs, allowing easy access to all object data via mbs.GetObjectParameter(itemNumber, ...) | | `q` | Vector $\in \Rcal^n$ | object coordinates (e.g., nodal displacement coordinates) in current configuration, without reference values | | `q_t` | Vector $\in \Rcal^n$ | object velocity coordinates (time derivative of `q`) in current configuration | | **return value** | MatrixContainer $\in \Rcal^{n \times n}$ | returns mass matrix for object, as exu.MatrixContainer, numpy array or list of lists; use MatrixContainer sparse format for larger matrices to speed up computations. | **Userfunction**: `jacobianUserFunction(mbs, t, itemNumber, q, q_t, fODE2, fODE2_t)` A user function, which computes the jacobian of the {ref}`LHS ` of the equations of motion, depending on current time, states of object and two factors which are used to distinguish between position level and velocity level derivatives. Can be used to create any kind of mechanical system by using the object states. | arguments / return | type or size | description | |---|---|---| | `mbs` | MainSystem | provides MainSystem mbs to which object belongs to | | `t` | Real | current time in mbs | | `itemNumber` | Index | integer number $i_N$ of the object in mbs, allowing easy access to all object data via mbs.GetObjectParameter(itemNumber, ...) | | `q` | Vector $\in \Rcal^n$ | object coordinates (e.g., nodal displacement coordinates) in current configuration, without reference values | | `q_t` | Vector $\in \Rcal^n$ | object velocity coordinates (time derivative of `q`) in current configuration | | `fODE2` | Real | factor to be multiplied with the position level jacobian, see {eq}`eq-objectgenericode2-jac` | | `fODE2_t` | Real | factor to be multiplied with the velocity level jacobian, see {eq}`eq-objectgenericode2-jac` | | **return value** | MatrixContainer $\in \Rcal^{n \times n}$ | returns special jacobian for object, as exu.MatrixContainer, numpy array or list of lists; use MatrixContainer sparse format for larger matrices to speed up computations; NOTE that the format of returnValue must AGREE with (dense/sparse triplet) format of stiffnessMatrix and dampingMatrix; sparse triplets MAY NOT contain zero values! | **Userfunction**: `graphicsDataUserFunction(mbs, itemNumber)` A user function, which is called by the visualization thread in order to draw user-defined objects. The function can be used to generate any `BodyGraphicsData`, see Section [](#sec-graphicsdata). Use `exudyn.graphics` functions, see Section [](#sec-module-graphics), to create more complicated objects. Note that `graphicsDataUserFunction` needs to copy lots of data and is therefore inefficient and only designed to enable simpler tests, but not large scale problems. For an example for `graphicsDataUserFunction` see ObjectGround, [](#sec-item-objectground). | arguments / return | type or size | description | |---|---|---| | `mbs` | MainSystem | provides reference to mbs, which can be used in user function to access all data of the object | | `itemNumber` | Index | integer number of the object in mbs, allowing easy access | | **return value** | BodyGraphicsData | list of `GraphicsData` dictionaries, see Section [](#sec-graphicsdata) | *Example*: ```python #user function, using variables M, K, ... from mini example, replacing ObjectGenericODE2(...) KD = numpy.diag([200,100]) #nonlinear force example; this force is added to right-hand-side ==> negative sign! def UFforce(mbs, t, itemNumber, q, q_t): return -np.dot(KD, q_t*q) #add nonlinear term for q_t and q, q_t*q gives vector #non-constant mass matrix: def UFmass(mbs, t, itemNumber, q, q_t): return (q[0]+1)*M #uses mass matrix from mini example #non-constant mass matrix: def UFgraphics(mbs, itemNumber): t = mbs.systemData.GetTime(exu.ConfigurationType.Visualization) #get time if needed p = mbs.GetObjectOutputSuperElement(objectNumber=itemNumber, variableType = exu.OutputVariableType.Position, meshNodeNumber = 0, #get first node's position configuration = exu.ConfigurationType.Visualization) graphics1=graphics.Sphere(point=p,radius=0.1, color=graphics.color.red) graphics2 = {'type':'Line', 'data': list(p)+[0,0,0], 'color':graphics.color.blue} return [graphics1, graphics2] #now add object instead of object in mini-example: oGenericODE2 = mbs.AddObject(ObjectGenericODE2(nodeNumbers=[nMass0,nMass1], massMatrix=M, stiffnessMatrix=K, dampingMatrix=D, forceUserFunction=UFforce, massMatrixUserFunction=UFmass, visualization=VObjectGenericODE2(graphicsDataUserFunction=UFgraphics))) ``` (miniexample-objectgenericode2)= ## Mini example ```python #set up a mechanical system with two nodes; it has the structure: |~~M0~~M1 nMass0 = mbs.AddNode(NodePoint(referenceCoordinates=[0,0,0])) nMass1 = mbs.AddNode(NodePoint(referenceCoordinates=[1,0,0])) mass = 0.5 * np.eye(3) #mass of nodes stif = 5000 * np.eye(3) #stiffness of nodes damp = 50 * np.eye(3) #damping of nodes Z = 0. * np.eye(3) #matrix with zeros #build mass, stiffness and damping matrices (: M = np.block([[mass, 0.*np.eye(3)], [0.*np.eye(3), mass ] ]) K = np.block([[2*stif, -stif], [ -stif, stif] ]) D = np.block([[2*damp, -damp], [ -damp, damp] ]) oGenericODE2 = mbs.AddObject(ObjectGenericODE2(nodeNumbers=[nMass0,nMass1], massMatrix=M, stiffnessMatrix=K, dampingMatrix=D)) mNode1 = mbs.AddMarker(MarkerNodePosition(nodeNumber=nMass1)) mbs.AddLoad(Force(markerNumber = mNode1, loadVector = [10, 0, 0])) #static solution=10*(1/5000+1/5000)=0.0004 #assemble and solve system for default parameters mbs.Assemble() mbs.SolveDynamic(solverType = exu.DynamicSolverType.TrapezoidalIndex2) #check result at default integration time exu.sys['testResult'] = mbs.GetNodeOutput(nMass1, exu.OutputVariableType.Position)[0] ``` Relevant Examples (Ex) and TestModels (TM) with weblink to github: [`kinematicTreeAndMBS.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/Examples/kinematicTreeAndMBS.py) (Ex), [`nMassOscillator.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/Examples/nMassOscillator.py) (Ex), [`nMassOscillatorInteractive.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/Examples/nMassOscillatorInteractive.py) (Ex), [`simulateInteractively.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/Examples/simulateInteractively.py) (Ex), [`ACFtest.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/TestModels/ACFtest.py) (TM), [`coordinateVectorConstraintGenericODE2.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/TestModels/coordinateVectorConstraintGenericODE2.py) (TM), [`energiesTest.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/TestModels/energiesTest.py) (TM), [`genericODE2matrixFormatsTest.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/TestModels/genericODE2matrixFormatsTest.py) (TM), [`genericODE2test.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/TestModels/genericODE2test.py) (TM), [`linearFEMgenericODE2.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/TestModels/linearFEMgenericODE2.py) (TM), [`objectFFRFTest.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/TestModels/objectFFRFTest.py) (TM), [`objectGenericODE2Test.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/TestModels/objectGenericODE2Test.py) (TM), [`rigidBodyAsUserFunctionTest.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/TestModels/rigidBodyAsUserFunctionTest.py) (TM), [`solverExplicitODE1ODE2test.py`](https://github.com/jgerstmayr/EXUDYN/blob/master/python/TestModels/solverExplicitODE1ODE2test.py) (TM)