(recipes_page)=
# Recipes





(recipe_frames_sec)=
## Frames layer


Multibody systems typically have a large family of {{ framescls }}
defined by bodies, nodes on bodies, hinge attachment points, and other
locations of interest such as end-effectors, actuator/sensor mount
locations etc. Non-multibody {{ framescls }} can include environmental
locations, as well as celestial bodies in motion. The {{ framescls }}
layer provides a convenient foundational layer for organizing these {{
framescls }} in a tree structure, and for making arbitrary relative
pose, spatial velocity and acceleration queries across them.  The
`Frame` class forms the base class for bodies, nodes and celestial body
{{ framescls }}.

(recipe_new_frame_sec)=
### Creating  a new frame

Frames are created within a {{ framectrcls }} instance. You can use `fc
 = FrameContainer("root")` to create a new frame container with just a
 single root frame.  A new {{ framecls }} can be created in a {{
 framectrcls }} `fc` using `new_f = Frame.create("new_name", fc)`. At
 this point the new {{ framecls }} is unattached to the {{ framecls }}
 tree, and not especially usable. the {{ framecls }} can be added to the
 {{ framecls }} tree by attaching it to a {{ framecls }} already in the
 tree. The new {{ framecls }} `new_f` can be attached to such a {{
 framecls }} `f` by creating a new {{ f2fcls }} edge via `new_edge =
 PrescribedFrameToFrame.create(f, new_frame)`.


### Finding a frame instance

- Given a {{ framectrcls }} `fc`, a {{ framecls }} instance can be looked up using
its name via `f = fc.getFrame(frame_name)`. 

- the root {{ framecls }} for the {{ framescls }} tree can be looked up using `f =
  fc.root()`.

- Each {{ physbodycls }} is derived from a {{ framecls }}, and this {{ framecls }} represents the body frame.

- Each {{ nodecls }} is derived from a Frame, and this {{ framecls }} represents the
  local {{ nodecls }} frame.

- Each `f2f` {{ f2fcls }} instance describes the relative motion of a
  pair of {{ framescls }}, and the `from` {{ framecls }} can be obtained by a call to
  `f2f.oframe()`, and the `to` {{ framecls }} by a call to `f2f.pframe()`.

- The {{ subhingecls }} class is derived form the {{ f2fcls }} class,
  so the above methods can be used to get access  to its `from`
  and `to` {{ framescls }}. The same applies to {{ frmpairhingecls }} and
  {{ hingecls }} classes.

- The celestial {{ framescls }} are SpiceFrame instances, and thus also frame
  instances themselves.

- Environmental geometry objects are typically instances of
  {{ pscenenodecls }} and {{ pscenepartcls }} instances. The objects have a
  {py:meth}`Karana.Scene.ProxySceneNode.ancestorFrame()` method to access the {{ framecls }} instance
  representing their local {{ framescls }}.

The building blocks of a multibody system are intimately tied to
the {{ framescls }} layer, and there is rick interplay between them.


### Frame to frame data 

Given a pair of {{ framescls }} `A` and `B`, the {{ f2fcls }} instance relating
the two can be obtained via `f2f = A.frameToFrame(B)`.


- The relative pose of {{ framecls }} `B` with respective to {{ framecls }} `A` can be
  obtained vi `A_T_B = f2f.relTransform()`.

- The relative spatial velocity of {{ framecls }} `B` with respective to frame
  `A` as observed from `A` and represented in the `A` {{ framecls }} can be
  obtained via `V_AB = f2f.relSpVel()`.


- The relative spatial velocity of {{ framecls }} `B` with respective to frame
  `A` as observed from `B` and represented in the `B` {{ framecls }} can be
  obtained via `V_AB = f2f.pframeObservedRelSpVel()`.


- The relative spatial acceleration of {{ framecls }} `B` with respective to frame
  `A` as observed from `A` and represented in the `A` {{ framecls }} can be
  obtained via `A_AB = f2f.relSpAccel()`.

- The relative spatial acceleration of {{ framecls }} `B` with respective to frame
  `A` as observed from `B` and represented in the `B` {{ framecls }} can be
  obtained via `V_AB = f2f.pframeObservedRelSpAccel()`.


Body hinges and subhinges are {{ frmpairhingecls }} and {{ subhingecls }}
classes which are derived from the {{ f2fcls }} classes, and thus the
above methods can be used with them directly to obtain relative
information across these {{ f2fcls }} instances.


### Changing observer (derivative) frame

A 3-vector `x_A` that is a derivative observed and
represented in {{ framecls }} `A`, can be converted into a `B` {{ framecls }} observed
and represented {{ framecls }} via `x_B =
B.frameToFrame(A).toOframeObserved(x_A)`.




### Changing the relative frame

- Given a hypothetical `oframe` observed and represented relative spatial
  velocity `V_oframe`, the equivalent `pframe` relative (and `pframe` observed and represented)
   spatial velocity can be obtained via `V_pframe =
  f2f.toPframeRelativeSpVel(V_oframe)`. Note that `V_oframe` does not have
  to be the actual current relative spatial velocity across the `f2f`.

- Given a hypothetical `oframe` observed and represented relative spatial
  acceleration `A_oframe`, the equivalent `pframe` relative (and `pframe` observed and represented
   spatial acceleration can be obtained via `A_pframe =
  f2f.toPframeRelativeSpAccel(A_oframe)`. Note that `A_oframe` does not have
  to be the actual current relative spatial acceleration across the `f2f`.


### Reattaching a frame


A frame, and its descendant {{ framecls }} tree, can be reattached to a
different {{ framecls }} by creating a new edge {{ f2fcls }} between the new
parent {{ framecls }} and the {{ framecls }} as described in the
{ref}`recipe_new_frame_sec` section. The old parent edge is
automatically deleted.

### Deleting a frame

Like most objects, a {{ framecls }} `f` can be deleted by calling the
{py:meth}`Karana.KCore.discard` method as `discard(f)`. Note that this method will raise an error
if `f` has children {{ framescls }}. The children {{ framescls }} must detached,
reattached elsewhere or deleted before `f` can be deleted.




(recipe_rigid_body_sec)=
## Rigid multibody models


(rigid_serial_chain_manual_recipe)=
### Create rigid body serial-chain multibody system (manual)

To create a serial chain multibody system one body at a time, you would
follow a manual approach, which provides granular control over each body
and its connections, as opposed to procedural methods like
{py:meth}`Karana.Dynamics.PhysicalBody.addSerialChain` that create multiple uniform bodies in bulk.


To create a serial chain multibody system one body at a time, you would
follow a manual approach, which provides granular control over each body
and its connections, as opposed to procedural methods like
{py:meth}`Karana.Dynamics.PhysicalBody.addSerialChain` that create multiple uniform bodies in bulk.

Here's a recipe outline for manually creating a serial chain multibody
system:

- - -


1.  **Initialize the Multibody System:**
    *   Begin by creating a {{ framectrcls }} (e.g., `fc =
        FrameContainer("root")`). This serves as the root for your
        system.
    *   Instantiate a {{ mbodycls }} object, associating it with the
        created {{ framectrcls }} (e.g., `mb = Multibody("mb", fc)`).

2.  **Define General Body and Joint Parameters (as needed):**
    *   **Spatial Inertia** (`spI`): Define the mass and inertia
        properties for your bodies. For example, `spI =
        SpatialInertia(2.0, np.zeros(3), np.diag([3, 2, 1]))`.
    *   **Joint Transforms** (`body2joint_T`, `inb2joint_T`): Specify
        the transformations from the body's {{ framecls }} to its joint and from
        the inboard body's {{ framecls }} to the joint. For instance,
        `body2joint_T=HomTran(np.array([0, 0, 0.5]))` and
        `inb2joint_T=HomTran(np.array([0, 0, -0.5]))`.
    *   **Visual Geometries** ({{ scenepartspeccls }}): Define specifications
        for how the body and its pivot points should appear in a
        graphics scene (e.g., `sp_body`, `sp_pivot` with geometry,
        material, and transform). These can be grouped into
        `scene_part_specs` within {{ physbodyparamscls }}.
    *   (Optional) **Visual Geometries** ({{ scenepartspeccls }}): If visual
        representations are needed, define {{ scenepartspeccls }} objects
         with geometry, material, and
        transform) and add them to `scene_part_specs`.

3.  **Create and Configure the First Body:**
    *   **Create the body**: Call the
        {py:meth}`Karana.Dynamics.PhysicalBody.create` method, providing a
        unique name for the body and the {{ mbodycls }} instance it belongs
        to (e.g., `bd1 = PhysicalBody("bd1", mb)`).
    *   **Set Spatial Inertia:** Assign the defined {{ spinertiacls }} to
        the body (e.g., `bd1.setSpatialInertia(spI)`).
    *   **Define Joint Connection:** Create a {{ hingecls }} to connect this first body to the {{ mbodycls }}'s `virtualRoot()`. Specify the hinge type (e.g., `HingeType.REVOLUTE` or `HingeType.BALL`) and the parent and child bodies (e.g., `bd1_hge = PhysicalHinge(cast(PhysicalBody, mb.virtualRoot()), bd1, HingeType.REVOLUTE)`).   
    *   **Set Body-to-Joint Transform:** Define the transform from the
        body's {{ framecls }} to its joint (e.g.,
        `bd1.setBodyToJointTransform(b2j)`).

4.  **Create and Configure Subsequent Bodies in the Serial Chain:**
    *   For each additional link in your serial chain, repeat the
        process from step 3:
        *   **Create a new body** Call the
        {py:meth}`Karana.Dynamics.PhysicalBody.create` method and give the
        body a )preferably unique) name and associate it with the
        {{ mbodycls }} system.
        *   **Set Spatial Inertia:** Assign its inertia properties.
        *   **Define Joint Connection:** Create a {{ hingecls }} to
            connect this new body to the *immediately preceding body* in
            the chain. For example, if `bd1` was the first body, the
            second body (`bd2`) would connect to `bd1` as its parent.
        *   **Set Body-to-Joint Transform:** Define its body-to-joint
            transform.

5.  **Finalize the Multibody System:**
    *   Once all individual bodies have been created and their hinges
        established, you must call the
        {py:meth}`Karana.Dynamics.Multibody.ensureHealthy` method on your
        {{ mbodycls }} instance. This step finalizes the setup and prepares
        the system for dynamics computations.
    *   (Optional) You may also use
        {py:meth}`Karana.Dynamics.Multibody.resetData` and
        {py:meth}`Karana.Dynamics.Multibody.displayModel` to inspect the
        created system.

- - -

This manual approach provides complete control over creating the right body types, hinge types and setting up the parent/child body relationships.


(rigid_serial_chain_procedural_recipe)=
### Procedurally create rigid body serial chain

To procedurally create a serial chain multibody system with a
user-specified number of bodies, you leverage specialized helper methods
like {py:meth}`Karana.Dynamics.PhysicalBody.addSerialChain` which are designed for generating multiple
bodies in bulk, particularly useful for large test systems or
performance benchmarking. 

Here's a recipe outline for procedurally creating a serial chain multibody system:

- - -


1. Carry out the steps `1` and `2` in the {ref}`rigid_serial_chain_manual_recipe` 
   recipe to create a {{ framectrcls }},
   an empty multibody system and define the properties for each
   body. These properties will be uniformly applied to all the bodies in
   the system. You can always modify any of body's properties later once
   the bodies have been created. Additional body properties that will be
   needed include:

    *   Define **Hinge Axes** (`axes`):
        *   Example: `axes=[np.array([0.0, 1.0, 0.0])]`
    *   Set **Body-to-Joint Transform** (`body2joint_T`): This defines
        the transformation from the body's {{ framecls }} to its joint.
        *   Example: `body2joint_T=HomTran(np.array([0, 0, 0.5]))`
    *   Set **Inboard Body-to-Joint Transform** (`inb2joint_T`): This
        defines the transformation from the inboard body's {{ framecls }} to the
        joint.
        *   Example: `inb2joint_T=HomTran(np.array([0, 0, -0.5]))`


2.  **Create the serial chain:**
    *   Call the `static`
        {py:meth}`Karana.Dynamics.PhysicalBody.addSerialChain` method ,
        available on either the {{ physbodycls }} or {{ modalbodycls }}
        class, to add the chain of bodies to your multibody system.
    *   **Parameters:**
        *   `name`: A prefix string for the new body names (e.g.,
            `"link"` or `"body"`).
        *   `nbodies`: The number of bodies to create in the serial
            chain (e.g., `n_links`).
        *   `root`: The parent body to which the chain will be
            attached. Often, this is the `virtualRoot()` of your
            {{ mbodycls }} system (e.g., `cast(PhysicalBody,
            mb.virtualRoot())`).
        *   `htype`: The type of hinge to use for connecting the bodies
            (e.g., {py:attr}`Karana.Dynamics.HingeType.REVOLUTE` or
            {py:attr}`Karana.Dynamics.HingeType.BALL`). `REVOLUTE` is often the default.
        *   `params`: The {{ physbodyparamscls }} object containing the
            mass properties, joint connection properties, and visual
            geometries defined in step 1.
    *   **Example Python Call (for rigid bodies):**
        ```python
        PhysicalBody.addSerialChain(
            "link", n_links, cast(PhysicalBody, mb.virtualRoot()),
            htype=HingeType.REVOLUTE, params=params
        )
        ```

        
3.  **Finalize the Multibody System:** 
    * Do `Step 5` in the {ref}`rigid_serial_chain_manual_recipe` recipe to
      finalize the multibody system.

- - -

This procedural approach, exemplified by the "n-link pendulum
(procedural)" example, automates the creation of large multibody systems
by defining parameters once and applying them to many bodies in a chain.



(rigid_regular_tree_procedural_recipe)=
### Procedurally create a regular rigid body tree

The steps for procedurally creating a tree multibody system with a
user-specified branching structure are very similar to the steps in the
{ref}`rigid_serial_chain_procedural_recipe` recipe, except for using the
{py:meth}`Karana.Dynamics.PhysicalBody.addTree` method instead of the
{py:meth}`Karana.Dynamics.PhysicalBody.addSerialChain` method.

The {py:meth}`Karana.Dynamics.PhysicalBody.addTree` static method directly
adds a sub-tree of rigid {{ physbodycls }} instances.

*   **Parameters:**    
    *   `name`: A prefix string to use for naming the new bodies
                (e.g., `"branch_link"`).
    *   `branch_length`: The number of bodies in each branch of the tree.
    *   `nbranches`: The number of child branches originating
       from each node.
    *   `depth`: The number of branching levels to create.
    *   `root`: The existing {{ physbodycls }} instance to which
       the new sub-tree will be attached. This is often the
       {{ mbodycls }}'s `virtualRoot()` (e.g., `cast(PhysicalBody,
       mb.virtualRoot())`).
    *   `htype`: The type of hinge to use for connections (e.g.,
       {py:attr}`Karana.Dynamics.HingeType.REVOLUTE`).
    *   `params`: The {{ physbodyparamscls }} object containing the uniform properties.
            
*   **Example Python Call:**

```python
            PhysicalBody.addTree(
                "branch_link", branch_length=3, nbranches=2, depth=2,
                root=cast(PhysicalBody, mb.virtualRoot()),
                htype=HingeType.REVOLUTE, params=params
            )
```


(rigid_tree_general_recipe)=
### Create general branched rigid body tree

The creation of arbitrary tree structure multibody systems can be done

1. manually body by body using the {ref}`rigid_serial_chain_manual_recipe`
   recipe and attaching bodies appropriately to build up the desired
  multibody topology.
  
2. alternatively, the procedural techniques in the {ref}`rigid_serial_chain_procedural_recipe`
   recipe and the {ref}`rigid_regular_tree_procedural_recipe` recipe can be used repeatedly to create
  and attach fragments to assemble the desired multibody topology.


(multibody_importexport_recipe)=
### Importing and exporting multibody model


`kdflex` supports importing and exporting multibody models in the following formats :


*   **Import/Export Formats:**
    *   {py:class}`Karana.Dynamics.SOADyn_types.SubGraphDS` DataStruct compatible formats: YAML, JSON, HDF5, and Python format model files.
    *   URDF format model files.
*   **Import Formats:**
    *   JPL DARTS dynamics simulation tool Python model format files.

**I. Exporting a Multibody:**

We assume that a multibody instance, `mb` has already been created.

1.  **Convert to {py:class}`Karana.Dynamics.SOADyn_types.SubGraphDS` DataStruct:**
    *   The {py:class}`Karana.Dynamics.Multibody` needs to be converted into a {py:class}`Karana.Dynamics.SOADyn_types.SubGraphDS` DataStruct, which is the intermediate format used for export.
    *   Use the {py:meth}`toDS() <Karana.Dynamics.Multibody.toDS>` method for this conversion.
    *   Example: `mb_DS_to_export = mb.toDS()`


2.  **Export to Various File Formats:**
    *   Use the `toFile()` method on the `mb_DS_to_export` object, specifying the desired filename with the appropriate extension.
      *   **To JSON:** Example
      `mb_DS_to_export.toFile("2_link_multibody.json")`

      *   **To YAML:** Example
      `mb_DS_to_export.toFile("2_link_multibody.yaml")`

      *     **To HDF5:**  Example
      `mb_DS_to_export.toFile("2_link_multibody.hdf5")`

      *   **To URDF:**  Example `mb.toUrdf("output.urdf")`

**II. Importing a Multibody:**

1.  **Import the {py:class}`Karana.Dynamics.SOADyn_types.SubGraphDS` DataStruct from File:**
    *   Use the `fromFile()` method of `SubGraphDS` to load the saved model file.
    *   For `JPL DARTS` Python model files, you would use {py:func}`Karana.KUtils.darts_model_file_converter.convertDARTSModelFile` first, then convert to `SubGraphDS`).
    *   Example (importing from JSON):
        ```python
        imported_ds = SubGraphDS.fromFile("2_link_multibody.json")
        # You could also import from YAML or HDF5:
        # imported_ds = SubGraphDS.fromFile("2_link_multibody.yaml")
        # imported_ds = SubGraphDS.fromFile("2_link_multibody.hdf5")
        ```
        
2.  **Convert  to {{ mbodycls }}:**
    *   Convert the `imported_ds` (which is a {py:class}`Karana.Dynamics.SOADyn_types.SubGraphDS` DataStruct) back into a functional {{ mbodycls }} object. This requires a {{ framectrcls }}.
    *   Example:
        ```python
        fc = FrameContainer("root") # Re-create or use an existing {{ framectrcls }}
        mb_new = imported_ds.toMultibody(fc)
        ```

After these steps, the `mb_new` multibody should be  ready for further
operations. The importing/exporting process is demonstrated in the
[Model import/export](generated/notebooks/example_import_export/notebook.ipynb) 
and [URDF import](generated/notebooks/example_urdf/notebook.ipynb) notebooks.


### Computing mass properties of a multibody system


A {{ mbodycls }} instance is a also a {{ subtreecls }}, and as such all of the
methods described in the {ref}`subtree_mass_props_recipe` section for
computing the mass properties for a {{ subtreecls }} can also be used by
passing the {{ mbodycls }} as an argument to the methods described in the
section.



### How to detach a  body from it parent body?

Here's a recipe outline for detaching a body in `kdFlex`:

- - -

**Recipe Outline: Detaching a Body**


A {{ physbodycls }} can be detached by calling its 
{py:meth}`detach() <Karana.Dynamics.PhysicalBody.detach>` method. For example:

        ```python
        body_to_detach.detach()
        ```


It's important to understand what happens internally when {py:meth}`detach() <Karana.Dynamics.PhysicalBody.detach>` is called:

*   **Parent Disconnection:** The body is removed from its direct parent
    in the topological tree.
*   **Hinge Deletion:** The hinge that previously connected the detached
    body to its former parent is automatically deleted.
*   **New Hinge Creation:** A new
    {py:attr}`Karana.Dynamics.HingeType.FULL6DOF` type hinge is
    automatically created.
*   **Reattachment to Virtual Root:** This newly created
    {py:attr}`FULL6DOF <Karana.Dynamics.HingeType.FULL6DOF>` hinge is
    used to re-attach the detached body directly to the multibody
    system's {py:meth}`virtualRoot()
    <Karana.Dynamics.SubTree.virtualRoot>` body. This effectively gives
    the detached body 6 degrees of freedom relative to the virtual root
    (ground).
*   **State Preservation:** Crucially, the coordinates of the new
     {py:attr}`FULL6DOF <Karana.Dynamics.HingeType.FULL6DOF>` hinge are initialized such that the detached body's
    inertial pose (position and orientation), spatial velocity, and
    spatial acceleration are preserved. This means the body's physical
    state remains continuous and consistent immediately after the
    detachment operation.

After any operation that alters the system's topology, it's good
practice to ensure the {{ mbodycls }} system's internal data structures
are updated for subsequent dynamics calculations.  You can use {{
mbodycls }}'s {py:func}`dumpTree <Karana.Dynamics.SubTree.dumpTree>`
to print the new hierarchical structure of your multibody system. This
allows you to visually confirm that the body is now connected directly
to the `_MBVROOT_` (the virtual root).
*   **Example:**
    ```python
    mb.dumpTree()
    ```
        
By following these steps, you can successfully detach a {{ physbodycls }}
from its parent and re-anchor it to the virtual root, thus modifying the
multibody system's topology while ensuring the body's dynamic state is
preserved.



### How to reattach a physical body to new parent body?

To reattach a body to a different parent body, you essentially perform a
two-step process: first, you detach the body from its current parent (if
it's not already detached), and then you attach it to the new desired
parent body. 
The {{ physbodycls }} {py:meth}`reattach() <Karana.Dynamics.PhysicalBody.reattach>`
method is designed for this purpose, allowing you to
connect a body to a new parent with a specified hinge type and relative
transforms.

It's important to understand what happens internally when
{py:meth}`reattach() <Karana.Dynamics.PhysicalBody.reattach>` is called:

*   **Hinge Deletion:** The hinge that previously connected the detached
    body to its former parent is automatically deleted.
*   **New Hinge Creation:** A new  {{ hingecls }} of the specified type is created.
*   **Reattachment to new parent:** This newly {{ hingecls }} is used to
    re-attach the detached body directly to the new {{ physbodycls }} parent
    body.
*   **State Preservation:** If the new hinge type is {py:attr}`FULL6DOF
    <Karana.Dynamics.HingeType.FULL6DOF>`, then the coordinates of the
    new {{ hingecls }} are initialized such that the reattached body's
    inertial pose (position and orientation), spatial velocity, and
    spatial acceleration are preserved. This means the body's physical
    state remains continuous and consistent immediately after the
    detachment operation. For other hinge types, the user needs to
    manually set the hinge parameters (e.g., axes), the {{ onodecls }} and
    {{ pnodecls }} transforms, and the hinge coordinates.

By following these steps, you can effectively reattach a body to a
different parent within the {{ mbodycls }} system.

- - -

(recipe_biconstraints_sec)=
## Using Bilateral Closure Constraints

Closure constraints impose motion constraints on the bodies. See the 
{ref}`constraints_sec` section for more discussion on the types of closure
constraints supported in {{ kdflex }}.

(cutjoint_constraint_recipe)=
### Adding cut-joint constraints

Bilateral loop constraints can be added to the multibody system using
the {{ cutjtconstraintcls }} class.  Such cut-joint
constraints internally use a {{ frmpairhingecls }} hinge
instance to define the motion permitted across the constraint. The end
point of a loop constraint on a body must be a
{py:class}`Karana.Dynamics.ConstraintNode` instance. When not connected to a body,
the other end can be a {py:class}`Karana.Frame.Frame` instance. The recipe for creating a cut-joint constraint is:

1. Look up or create a constraint node on the body being connected by the loop constraint.
  * Example: `cnode1 = Karana.Dynamics.ConstraintNode.lookupOrCreate("cnode1", bd)`
  * Use the {py:meth}`Karana.Dynamics.Node.setBodyToNodeTransform` method to
    position and orient the node on the body.
  
2. Repeat `Step 1` if the other end of the loop constraint is meant to
   be connected to another body. Else lookup of create a frame. Create a
   {{ f2fcls }} instance defining the two ends of the loop
   constraint.
  * Example: `f2f = cnode1.frameToFrame(cnode2)`

3. Create the cut-joint constraint using
   {py:meth}`Karana.Dynamics.LoopConstraintCutJoint.create` and specify the hinge type to
   use.
  * Example: `lc = LoopConstraintCutJoint.create("my_lc", mb, f2f, HingeType.REVOLUTE)`
  
4. Now that the loop constraint has been created, it much be activated
   by calling the {py:meth}`Karana.Dynamics.SubGraph.enableConstraint()
` method for it to have any effect. 
  * Example: `mb.enableConstraint(lc)`
  
A loop constraint can be deactivated at any time by using the
{py:meth}`Karana.Dynamics.SubGraph.disableConstraint` method. Enabled 
constraints can be looked by name using the
{py:meth}`Karana.Dynamics.SubGraph.getEnabledConstraint` method.

(convel_loop_constraint_recipe)=
### Adding convel  (non-cut-joint)  loop constraints


The process for creating a {{ convelconstraintcls }} is the same for
{{ cutjtconstraintscls }} described in the {ref}`cutjoint_constraint_recipe` recipe,
except that the Step 3 construction process is changed to:

3. Create the {{ convelconstraintcls }} using
   {py:meth}`Karana.Dynamics.LoopConstraintConVel.create`. 
  * Example: `lc = LoopConstraintConVel.create("my_lc", mb, f2f)`
  
These constraints do not use hinges. However, they require a unit axis
parameter which can set using the
{py:meth}`Karana.Dynamics.LoopConstraintConVel.setUnitAxis` method.

(coord_constraint_recipe)=
### Adding coordinate constraints

Coordinate constraints impose a fixed scale factor constraint between
the coordinates of a pair of {{ subhingescls }} (of the same
dimension). Nodes are not used by {{ coordconstraintcls }} instances. One can
be created using {py:meth}`Karana.Dynamics.CoordinateConstraint.create` and
specifying the pair of {{ subhingescls }} being constrained.
  * Example: `cc = CoordinateConstraint.create("my_cc", mb, oshg, pshg)`
  
The scale factor for a {{ coordconstraintcls }} can be set using the
{py:meth}`Karana.Dynamics.CoordinateConstraint.setScaleRatio` method.
  
A {{ coordconstraintcls }} can be deactivated at any time by using the
{py:meth}`Karana.Dynamics.SubGraph.disableConstraint` method. Enabled 
constraints can be looked by name using the
{py:meth}`Karana.Dynamics.SubGraph.getEnabledConstraint` method.



- - -

(recipe_subtree_sec)=
## Working with sub-trees

A {py:class}`Karana.Dynamics.SubTree` is a class that represents a sub-tree of
connected bodies in a {{ mbodycls }} system. It is designed to allow
algorithms and computations to be limited to a specific portion of a
{{ mbodycls }} system. See {ref}`subtrees_sec` section for for details on the
{{ subtreecls }} and {{ subgraphcls }} classes.  A {{ subtreecls }} is always a part of a
{{ mbodycls }} system. 



(create_subtree_recipe)=
### Creating a SubTree Instance

A new {{ subtreecls }} instance can be created from an existing {{ subtreecls }}
instance, such as the {{ mbodycls }} instance itself.  The bodies to be added
to the new {{ subtreecls }} instance can be tailored by specifying a new root
body, branches to include, and bodies to exclude (see 
{ref}`here <subtree_args_anchor>` for details on how to set these arguments). A new
{{ subtreecls }} can be called at the `C++` level using

```cpp
        auto new_subtree = static kc::ks_ptr<SubTree> create(
            std::string_view name,
            const kc::ks_ptr<SubTree> &parent_subtree,
            const kc::ks_ptr<BodyBase> &new_root,
            std::vector<kc::ks_ptr<BodyBase>> use_branches={},
            std::vector<kc::ks_ptr<BodyBase>> stop_at={}
        )
```

At the `Python` level the {{ subtreecls }} can be created as follows:

```python
        # Assuming 'mb' is your Karana.Dynamics.Multibody instance
        # and 'some_body' is a `BodyBase` within 'mb'
        # 'some_other_body' could be a leaf in this new subtree
        subtree_name = "my_specific_subtree"
        subtree_root_body = mb.getBody("body_A") # Get a BodyBase pointer from your multibody
        # Optional: Specify use_branches and stop_at arguments to tailor
        #   the bodies added. Using the same value will result in a 
        #   spanning tree of the bodies
        # subtree_stop_at = [mb.getBody("body_B"), mb.getBody("body_C")]

        my_subtree = Karana.Dynamics.SubTree.create(
            subtree_name,
            parent_subtree,
            subtree_root_body,
            # use_branches=subtree_stop_at # uncomment to limit to branches with these bodies 
            # stop_at=subtree_stop_at # uncomment to skip descendants of these bodies 
        )
```

You can inspect the contents of the new {{ subtreecls }} by first making it
`healthy` by calling its {py:meth}`ensureHealthy() <Karana.Core.LockingBase.ensureHealthy>` method, and then calling its
{py:meth}`displayModel() <Karana.Dynamics.SubTree.displayModel>`and its
{py:meth}`dumpTree() <Karana.Dynamics.SubTree.dumpTree>` methods.


###  SubTree Introspection

Once a {{ subtreecls }} is created, you can access its properties and structure.

1.  **Get the Virtual Root Body:**
    *   The {py:meth}`virtualRoot()
        <Karana.Dynamics.SubTree.virtualRoot>` method returns the {{
        bodybasecls }} root body of the subtree.
    *   **Recipe Step:**
        ```python
        root_of_subtree = my_subtree.virtualRoot()
        print(f"Root of '{my_subtree.name()}' is: {root_of_subtree.name()}")
        ```

2.  **Get a Body's Parent within the SubTree:**
    *   The {py:meth}`parentBody(const BodyBase &body)
        <Karana.Dynamics.SubTree.parentBody>` method returns the
        immediate parent of a specified body *within the context of this
        specific subtree*.
    *   **Recipe Step:**
        ```python
        # Assuming 'child_body_in_subtree' is a BodyBase within 'my_subtree'
        child_body_in_subtree = mb.getBody("body_Y") # Must be part of the subtree
        parent_in_subtree = my_subtree.parentBody(child_body_in_subtree)
        if parent_in_subtree:
            print(f"Parent of '{child_body_in_subtree.name()}' in '{my_subtree.name()}' is: {parent_in_subtree.name()}")
        else:
            print(f"'{child_body_in_subtree.name()}' is the root or not in '{my_subtree.name()}'")
        ```

3.  **Get the List of Base Bodies:**
    *   The {py:meth}`baseBodies
        <Karana.Dynamics.SubTree.baseBodies>` method returns a vector (or list) of {{ bodybasecls }} representing the base bodies of the subtree.
    *   **Recipe Step:**
        ```python
        base_bodies_list = my_subtree.baseBodies()
        print(f"Base bodies of '{my_subtree.name()}': {[b.name() for b in base_bodies_list]}")
        ```

4.  **Display the SubTree Structure:**
    *   The {py:meth}`dumpTree
        <Karana.Dynamics.SubTree.dumpTree()>` method can be used to display the body tree, with options to control content and verbosity using `SubTreeDumpTreeOptions`.
    *   **Recipe Step:**
        ```python
        # DumpTreeOptions can be configured for more detailed output
        # e.g., options = Karana.Dynamics.SubTreeDumpTreeOptions(show_hinges=True, show_nodes=True)
        my_subtree.dumpTree(prefix="  ", options=Karana.Dynamics.SubTreeDumpTreeOptions())
        ```

5.  **Get the Type String:**
    *   The {py:meth}`typeString
        <Karana.Core.Base.typeString>` method returns the string "Karana::Dynamics::SubTree".
    *   **Recipe Step:**
        ```python
        print(f"Type of object: {my_subtree.typeString()}") # Output: Karana::Dynamics::SubTree
        ```

### Setting the state of a sub-tree

The state of a sub-tree consists of the `Q` and `U` generalized
configuration and velocity coordinates for the subhinges and deformable
bodies in the system. The kinematics and dynamics of multibody systems
are fundamentally nonlinear and hence computations depend on the state
values. Thus it is critical that the state values are set correctly
before carrying out computations. There are multiple ways and
considerations for getting and setting the state values as described
below.

- The state of an individual subhinge `sh` instance can be set using the
  `sh.setQ()` and `sh.setU()` methods. The `sh.getQ()` and `sh.getU()`
  methods can be used to query the current subhinge state values. This
  is usually the most intuitive and robust way of setting articulation
  state values, since the step involves getting access to the relevant
  subhinge and working with it directly (and no non-robust indexing and
  slicing operations are involved).
  
- Deformable bodies have deformation state coordinates. The state of an
  individual deformable body `bd` instance can be set using the
  `bd.setQ()` and `bd.setU()` methods. The `bf.getQ()` and `bd.getU()`
  methods can be used to query the current body state values. This
  direct approach is again more robust since no indexing and slicing
  operations are involved.



### Indirect Subhinge State initialization

While a subhinge's `setQ()` and `setU()` method can be used to directly
set the state of a subhinge, sometimes the coordinate values to be set
are not known explicitly, and are instead required to meet a specific
transform relationship between a pair of {{ framescls }}.

One situation is where the subhinges coordinates are required to meet a
relative transform `T` across the parent hinge. The subhinge coordinates
can be set by calling {py:meth}`hinge.fitQ(T)
<Karana.Dynamics.FramePairHinge.fitQ>`. This method will do a best fit
to set the hinge's subhinge coordinates that achieve the desired `T`
transform. The method will return a residual error transform - which
should be identity if the desired `T` transform was achieved for the
hinge.


There are times where the desired relative transform `T` across the
hinge is not known directly either, and instead the requirement is for
the subhinge coordinates to be such as to meet a desired relative pose
across another pair of frames.  For example, the coordinates of a
vehicle's hinge are to be initialized such that a {{ framecls }}
attached to the vehicle has a specific relative pose with respect to
another {{ framecls }} in the system.  To make this concrete, let us
assume that we have a desired `T_AB` pose that needs to be achieved
between a pair of `A` and `B` {{ framescls }} by setting the coordinates
of a `hg` hinge {{ f2fcls }} on the path between `A` and `B`. To achieve
this, call {py:meth}`hg.fitQ(T_AB, f2f_AB)
<Karana.Dynamics.FramePairHinge.fitQ>`. The second `f2f_AB` argument
tells the method to interpret the first `T_AB` argument as a desired
transform for the specified frame to frame instance, instead of the
hinge itself.  Like before, the method will do a best fit to set the
hinge's subhinge coordinates that achieve the desired `T_AB` transform.
The method will return a residual error transform that should be
identity if the requirement was fully met.
 
The similar {py:meth}`fitU() <Karana.Dynamics.FramePairHinge.fitU>` and
{py:meth}`fitUdot() <Karana.Dynamics.FramePairHinge.fitUdot>` methods can be used
to initialize the velocity and acceleration coordinates for the subhinges
as well. Note that it is essential that the the `Q` coordinates be
initialized correctly before using this procedure to initialize the `U`
velocity coordinates, and the `U` coordinates in turn be initialized
correctly before attempting to initialize the `Udot` acceleration
coordinates.

### Working with state coordinates in bulk 

The above methods directly work with the objects involved and are thus
more intuitive and robust way of setting articulation and deformation
state values. However they do require getting access to the relevant
subhinge and body first before getting or setting its state. Their use
is ideal for {{ kmodelcls }} instances (e.g., motors, spring/dampers) that are
associated with individual subhinges.
  
There are times however, where we may need to get and set the state in
bulk across all the subhinges and deformable bodies. We can use the
{{ coorddatacls }} members used to manage parts of the state. 

- Get the desired {{ coorddatacls }} `cd` instance from the sub-tree. The one
  for subhinges can be obtained from `st` sub-tree via
  `st.subhingeCoordData()`, for deformable bodies via
  `st.bodyCoordData()`. Additionally, if the sub-tree is actually a
  sub-graph, the {{ coorddatacls }} for the cut-joint constraint hinges can be
  accessed via `st.cutjointCoordData()`.

- The `cd.getQ()` and `cd.getU()` methods can be used to get overall `Q`
  and `U` coordinate vectors with contributions from all the components
  within `cd`. The `cd.setQ(Q)` and `cd.setU(U)` methods conversely can
  be used to set values across all the components. Assembling and
  inspecting the contents of the `Q` and `U` vectors does require
  understanding of the packing order of the component
  contributions. Due to this, manipulating the contents of these vectors
  directly can be source of error, and the earlier component level
  methods are recommended when such manipulation is necessary. The
  following methods are however are available when the packing
  information is needed:
  
  - the `cd.coordOffsets(elt)` method can be used to return the `Q` and
    `U` offsets for the state contribution of the `elt` component.
    
  - conversely, the `cd.coordAt(index)` method can be used to query for
    the component whose state is located at the `index` value.
    
- The short-cut `cd.setQ(scalar_Q)` and `cd.setU(scalar_U)` methods can
  be used to fill all the values of the component states with specified
  scalar values. These methods are convenient for use during
  initialization.

Instead of working with the component {{ coorddatacls }} values, there is also
the option to get and state values across all the sub-tree {{ coorddatacls }}
and their components.

- The `st.getQ()` and `st.getU()` methods will return `Q` and `U`
  vectors with state data from all the subhinge, deformable bodies and
  cut-joint constraints stacked together.

- The corresponding  `st.setQ(Q)` and `st.setU(U)` methods can be used to set 
  the state data for all the subhinge, deformable bodies and
  cut-joint constraints in one-shot. 

- Similar to the {{ coorddatacls }} versions of the methods. the
  `st.coordOffsets()` and `st.coordAt()` methods can be used to query
  the packing offset of a component object as well as the object at an
  index respectively.
  
For sub-graphics with constraints, the coordinates are not all
independent. Additional steps such as described in {ref}`recipe_pose_constraint_sec` section may be needed to ensure (or solve
for) valid coordinate values that satisfy the constraints.

### Using SubTree for Targeted Operations

The primary benefit of {{ subtreecls }}s is to apply computations to a limited scope.
Many Karana dynamics algorithms are designed to accept a {{ subtreecls }} (or {{ subgraphcls }}) as an argument.
Thus assuming a hypothetical 'compute_dynamics_on_subtree()' function,
this function would take a `SubTree` argument and perform computations only on the sub-tree's bodies.

Since {py:class}`Karana.Dynamics.Multibody` is derived from
{py:class}`Karana.Dynamics.SubTree`, you can pass a full {{ mbodycls }} instance to
these algorithms if you want to perform computations on the entire
system.


<!-- TODO - Inverse dynamics, gravity compensation -->



(subtree_mass_props_recipe)=
### Computing mass properties of a SubTree

To compute the mass properties of a {{ subtreecls }}, you primarily use
static methods available within the {py:class}`Karana.Dynamics.Algorithms`
namespace. These methods allow you to calculate various dynamic and
kinematic properties, including overall mass matrices, momentum, and
spatial inertia for the specified {{ subtreecls }} argument. 

One benefit of this capability is that the user can narrow the focus to
just a part of the overall {{ mbodycls }} system by creating a {{ subtreecls }}
for that part and passing it as an argument to these methods. Thus
{{ subtreescls }} instances for individual vehicles, robot arms etc. can be
created and used to limit the mass properties computation to just the
bodies in the {{ subtreecls }}.

Of course, the {{ mbodycls }} instance is a also a {{ subtreecls }}, and
system-level mass-properties can by computed by passing the {{ mbodycls }} as
an argument to the methods. he following assumes that a `my_subtree`
{{ subtreecls }} instance already exists. If not use the steps in the 
{ref}`create_subtree_recipe` section to create one first.


**I. Accessing the Center of Mass (CM) Frame**

A {py:class}`Karana.Dynamics.SubTree` instance has a dedicated {{ framecls }} that
 automatically tracks the center of mass (CM) location of its
 bodies. This {{ framecls }} provides a convenient way to track the overall
 center of mass of the {{ subtreecls }}.

*   **Purpose:** Get the {py:class}`Karana.Frame.Frame` instance that tracks the center of mass location of the bodies in the {{ subtreecls }}.
*   **Method:** {py:meth}`Karana.Dynamics.SubTree.cmFrame`
*   **Recipe Step:**
    ```python
    cm_frame = my_subtree.cmFrame()
    # You can then query properties of this frame, e.g., its translation relative to a parent frame
    # print(f"Center of Mass Frame: {cm_frame.translation()}")
    ```
    *   **Insight:** Since this is a {py:class}`Karana.Frame.Frame` instance, it is integrated into the system's {{ framescls }} tree, allowing its location to be tracked dynamically.


**II. Computing Specific Mass Properties**

The following static methods from {py:class}`Karana.Dynamics.Algorithms` are used to compute various mass-related properties:

1.  **Overall Spatial Inertia:**
    *   **Purpose:** Calculates the total spatial inertia of all bodies within the {{ subtreecls }}.
    *   **Method:** {py:meth}`Karana.Dynamics.Algorithms.evalSpatialInertia`
    *   **Recipe Step:**
        ```python
        # Assuming 'my_subtree' is your Karana.Dynamics.SubTree instance
        overall_spatial_inertia = Karana.Dynamics.Algorithms.evalSpatialInertia(my_subtree)
        print(f"Overall Spatial Inertia:\n{overall_spatial_inertia}")
        ```
    *   **Insight:** Passing a full {{ mbodycls }} instance to this method will compute the system-level spatial inertia, while passing a strict {{ subtreecls }} will compute it only for the bodies within that sub-tree.

2.  **Overall Spatial Momentum:**
    *   **Purpose:** Calculates the overall spatial momentum for the {{ subtreecls }}.
    *   **Method:** {py:meth}`Karana.Dynamics.Algorithms.evalSpatialMomentum()`
    *   **Recipe Step:**       
        ```python
        overall_spatial_momentum = Karana.Dynamics.Algorithms.evalSpatialMomentum(my_subtree)
        print(f"Overall Spatial Momentum:\n{overall_spatial_momentum}")
        ```

3.  **Centroidal Spatial Momentum:**
    *   **Purpose:** Calculates the centroidal spatial momentum for the {{ subtreecls }}. This is the spatial momentum expressed at the {{ subtreecls }}'s center of mass.
    *   **Method:** {py:meth}`Karana.Dynamics.Algorithms.evalCentroidalMomentum`
    *   **Recipe Step:**
        ```python
        centroidal_spatial_momentum = Karana.Dynamics.Algorithms.evalCentroidalMomentum(my_subtree)
        print(f"Centroidal Spatial Momentum:\n{centroidal_spatial_momentum}")
        ```

4.  **Centroidal Momentum Matrix:**
    *   **Purpose:** Calculates the centroidal momentum matrix for the {{ subtreecls }}. This matrix relates the generalized velocities of the {{ subtreecls }} to its centroidal momentum.
    *   **Method:** {py:meth}`Karana.Dynamics.Algorithms.evalCentroidalMomentumMatrix`
    *   **Recipe Step:**
        ```python
        centroidal_momentum_matrix = Karana.Dynamics.Algorithms.evalCentroidalMomentumMatrix(my_subtree)
        print(f"Centroidal Momentum Matrix:\n{centroidal_momentum_matrix}")
        ```

5.  **Tree System Mass Matrix (using Composite Rigid Body - CRB - approach):**
    *   **Purpose:** Calculates the tree system mass matrix using the Composite Rigid Body (CRB) recursive method. This mass matrix can have a customizable row/column order.
    *   **Method:** {py:meth}`Karana.Dynamics.Algorithms.evalTreeMassMatrixCRB`
    *   **Recipe Step:**
        ```python
        mass_matrix_crb = Karana.Dynamics.Algorithms.evalTreeMassMatrixCRB(my_subtree)
        print(f"Tree Mass Matrix (CRB):\n{mass_matrix_crb}")
        ```
    *   **Note:** This method currently does not support deformable bodies.

6.  **Tree System Mass Matrix (using Newton-Euler inverse dynamics):**
    *   **Purpose:** Calculates the tree system mass matrix using a Newton-Euler inverse dynamics approach.
    *   **Method:** {py:meth}`Karana.Dynamics.Algorithms.evalTreeMassMatrixInvDyn`
    *   **Recipe Step:**
        ```python
        # 'cd' (CoordData) is optional; if provided, it specifies the order of rows/columns.
        mass_matrix_inv_dyn = Karana.Dynamics.Algorithms.evalTreeMassMatrixInvDyn(my_subtree)
        print(f"Tree Mass Matrix (Inverse Dynamics):\n{mass_matrix_inv_dyn}")
        ```

7.  **Tree System Mass Matrix Inverse (using forward dynamics):**
    *   **Purpose:** Calculates the inverse of the tree system mass matrix using a forward dynamics approach.
    *   **Method:** {py:meth}`Karana.Dynamics.Algorithms.evalTreeMassMatrixInvFwdDyn`
    *   **Recipe Step:**
        ```python
        # 'coord_data' is required and specifies the order of rows/columns for the inverse matrix.
        # Assuming 'my_coord_data' is a `CoordData` instance
        # mass_matrix_inv_fwd_dyn = Karana.Dynamics.Algorithms.evalTreeMassMatrixInvFwdDyn(my_subtree, my_coord_data)
        # print(f"Tree Mass Matrix Inverse (Forward Dynamics):\n{mass_matrix_inv_fwd_dyn}")
        ```

By following these steps, you can comprehensively compute and access the
various mass-related properties of any {{ subtreecls }} within your {{
mbodycls }} system.


- - -


(recipe_subgraph_sec)=
## Working with sub-graphs

{py:class}`Karana.Dynamics.SubGraph` is another
   related class that inherits from {{ subtreecls }}, designed for portions
   of multibody systems that might include loop constraints.

The steps for creating a sub-graph instance are similar to those for a
sub-tree described in the {ref}`recipe_subtree_sec` section, except that
the the `SubGraph::create()` should be used. Once the sub-graph has
been created, existing or new constraints can be added to it using the
`enableLoopConstraint()` and `enableCoordinateConstraint()` methods.


(cks_recipe)=
### Constraint kinematics solver

The {py:class}`Karana.Dynamics.ConstraintKinematicsSolver` class is designed to
find generalized coordinates (`Q`), velocities (`U`), and accelerations
(`Udot`) coordinates that are consistent with the closure constraints
present in a {{ subgraphcls }}. 

The solver works by interactively adjusting the system's state until the
constraint equations are satisfied, leveraging Jacobian matrices and a
nonlinear solver. The method takes into into account the closure
constraints that have been enabled for the {{ subgraphcls }}.


**I. Understand the Problem Domain**

*   **Purpose:** To resolve kinematic inconsistencies introduced by closure constraints in multibody systems.*   **Input:** An initial guess for the system's generalized coordinates, velocities, and accelerations.
*   **Output:** Updated generalized coordinates (`Q`), velocities (`U`), and accelerations (`Udot`) that satisfy all defined loop constraints.

A {{ subgraphcls }} instance with one or more closure loop constraints is used in
the following procedure.

1.  **Create the solver:** Create an Instance of {{ ckscls }} by calling  {py:meth}`Karana.Dynamics.Algorithms.constraintKinematicsSolver` method with your {{ subgraphcls }} as argument.

2.   **Frozen Coordinates:** At this time, you can freeze coordinates
     using the
     {py:meth}`Karana.Dynamics.ConstraintKinematicsSolver.freezeCoord()`
     method. Coordinates for the frozen coordinates are left unchanged
     by the solvers invoked in the later steps.  This is useful for
     managing redundant coordinates or solving around a preferred
     configuration. Coordinates can be unfrozen using the
     {py:meth}`Karana.Dynamics.ConstraintKinematicsSolver.unfreezeCoord()` 
     method.

3.  **Initial Guess for State:** Since the solver uses iterative methods, it requires an initial "guess" for the generalized coordinates (`Q`), velocities (`U`), and accelerations (`Udot`) of the system. This guess is set on the {{ mbodycls }} instance itself.
  Provide an Initial Guess for the System State by setting the initial
    generalized coordinates (`Q`), velocities (`U`), and accelerations
    (`Udot`) on the {{ subgraphcls }}.


4.  **Solve for Consistent Coordinates:** (`Q`)
    *   The {py:meth}`Karana.Dynamics.ConstraintKinematicsSolver.solveQ()`
        method solves for `Q` values for the {{ subgraphcls }} that satisfy
        the position-level constraints. The solution can be retrieved
        using the {{ subgraphcls }}'s {py:meth}`getQ <Karana.Dynamics.SubGraph.getQ>` method. The method returns the residual error - which
        should be essentially zero when a solution is found.

5.  **Solve for Consistent Velocity Coordinates:** (`U`)
    *   The {py:meth}`Karana.Dynamics.ConstraintKinematicsSolver.solveU()`
        method solves for `U` velocity coordinates for the {{ subgraphcls }}
        that satisfy the velocity-level constraints. Note that this
        method should only be called after `Q` values satisfying the
        constraints have been set in the {{ subgraphcls }}. The solution can
        be retrieved using the {{ subgraphcls }}'s {py:meth}`getU <Karana.Dynamics.SubGraph.getU>` method. The method
        returns the residual error - which should be essentially zero
        when a solution is found.

5.  **Solve for Consistent Acceleration Coordinates:** (`Udot`)
    *   The {py:meth}`Karana.Dynamics.ConstraintKinematicsSolver.solveUdot()`
        method solves for `Udot` acceleration coordinates for the
        {{ subgraphcls }} that satisfy the acceleration-level
        constraints. Note that this method should only be called after
        `Q` and `U` values satisfying the constraints have been set in
        the {{ subgraphcls }}. The solution can be retrieved using the
        {{ subgraphcls }}'s {py:meth}`getUdot() <Karana.Dynamics.SubGraph.getUdot>`
        method. The method returns the residual error - which should be
        essentially zero when a solution is found.



### Forward dynamics/Solving Equations of Motion 


In `kdFlex`, computing forward dynamics involves determining the
accelerations of a multibody system given its current state (positions
and velocities) and applied forces/torques. This is a core operation for
simulating how a system moves over time. {{ kdflex }} provides multiple
algorithms for forward dynamics, catering to different system
complexities (tree vs. graph topologies, presence of loop constraints).

The forward dynamics algorithms in {{ kdflex }} typically take as input:
*   The multibody system's current configuration (joint positions `Q`
    and velocities `U`).
*   Applied generalized forces/torques (`T`).  
*   And they output: Generalized accelerations (`Udot`).

The framework offers both explicit algorithm calls and an integrated
state propagator for time-domain simulations that internally use forward
dynamics.


The {py:class}`Karana.Dynamics.Algorithms` namespace provides static methods for directly evaluating forward dynamics.

1.  **For SubTree Systems (no closure constraints):**
    *   **Algorithm:** Articulated Body Inertia (ATBI) based forward dynamics.
    *   **Method:** {py:meth}`Karana.Dynamics.Algorithms.evalForwardDynamics`
    *   **Input:** A {py:class}`Karana.Dynamics.SubTree` instance (a {{ mbodycls }} instance can also be passed, as it derives from {{ subtreecls }}).
    *   **Recipe Step:**
        ```python
        # Assuming 'mb' is your Karana.Dynamics.Multibody instance
        # and its state (q, u) and applied forces are set.
        # If you want to compute for a sub-tree, create one first:
        # my_subtree = Karana.Dynamics.SubTree.create("my_sub", mb, mb.getBody("some_body_name"))

        Karana.Dynamics.Algorithms.evalForwardDynamics(mb) # or evalForwardDynamics(my_subtree)
        # After this call, the accelerations (udot) for the system/subtree are computed
        # and stored internally or can be queried from the system's state.
        ```

    *   **Note:** This method evaluates the ATBI-based forward dynamics.

2.  **For SubGraph Systems (with closure constraints):**
    *   When {{ subgraphcls }} has closure constraints, you need
        specialized algorithms. It is important that the `Q` and `U`
        state vectors be first initialized to values that are consistent
        with the closure constraints on the system. These methods
        operate on a {py:class}`Karana.Dynamics.SubGraph` instance.
    *   **Tree-Augmented (TA) based exact forward dynamics:** This
        method enforces the bilateral constraints exactly.
        *   **Method:** {py:meth}`Karana.Dynamics.Algorithms.evalForwardDynamics`
        *   **Input:** A {py:class}`Karana.Dynamics.SubGraph` instance (which contains loop constraints).
        *   **Recipe Step:**
        ```python
            # Assuming 'sg' is your Karana.Dynamics.SubGraph instance with loop constraints
            # and its state (q, u) and applied forces are set.

            Karana.Dynamics.Algorithms.evalForwardDynamics(sg)
            # Accelerations (udot) are computed for the subgraph.
        ```


The forward dynamics algorithms in {{ kdflex }} are in fact hybrid
algorithms, that support _prescribed motion_ at the subhinges. This
means that the O(N) recursive hybrid dynamics algorithm can reduce to
standard forward dynamics (or inverse dynamics) depending on the inputs
(mix of generalized forces and prescribed accelerations).


### Computing inter-body forces



In {{ kdflex }}, computing inter-body forces refers to finding the forces
and torques acting across the hinges (joints) that connect bodies in a
multibody system. These forces are internal to the system and are
crucial for understanding the loads on joints and for design analysis.


**I. Compute Forward Dynamics:** Inter-body forces are typically a
result of (or can be computed after) a forward dynamics calculation, as
they depend on the accelerations and inertia of the connected
bodies. Thus the  methods for retrieving inter-body
forces should only be called *after* forward dynamics has been
called. 

**II. Enabling and Accessing Inter-Body Forces**

{{ kdflex }} includes mechanisms to enable and store inter-body forces.



{{ kdflex }} provides an inexpensive expression for computing inter-body
forces using the Spatial Operator Algebra (SOA) algorithm even though
the standard ABI forward dynamics doesn't require it. This means the
computation is efficient but might be an optional step during dynamics
if not explicitly needed for the primary forward dynamics
calculation. It also offers specific methods to retrieve these forces.

*   **For SubTree Regular Hinges:**
        *   The
            {py:meth}`Karana.Dynamics.HingeOnode.getInterBodyForceTreeFwdDyn`
            method returns the inter-body spatial force at the {{ onodecls }}
            (the "outboard" node of the hinge). This is meant for
            regular hinges, not cut-joint hinges.

*   **Method Signature:** {py:meth}`Karana.Dynamics.HingeOnode.getInterBodyForceTreeFwdDyn`

    *   **Recipe Step:**
```python
           # Assuming 'mb' is your Karana.Dynamics.Multibody instance
           # and 'my_hinge' is a {{ hingecls }} object in your system.
           # First, ensure forward dynamics has been computed:
           Karana.Dynamics.Algorithms.evalForwardDynamics(mb)

           # Access the hinge's onode and then its inter-body force
           # Note: Accessing HingeOnode directly might require delving into the internal structure
           # A more direct way would be to get the hinge object itself.
           # Let's assume 'a_physical_hinge' is an instance of {{ hingecls }}
           # or you can retrieve it from a body:
           # body_B = mb.getBody("body_B")
           # a_physical_hinge = body_B.parentHinge() # Get the hinge connecting body_B to its parent

           # If 'a_physical_hinge' is available:
           # inter_body_force_vector = a_physical_hinge.getInterBodyForceTreeFwdDyn() 
           # This might be the direct call in Python binding
           # print(f"Inter-body force for hinge: {inter_body_force_vector}")
```

*   **Note:** The `getInterBodyForceTreeFwdDyn()` method returns a {py:class}`Karana.Math.SpatialVector`, which represents both linear force and angular torque.

*   **For SubGraph systems with constraints:**
    *   When dealing with loop constraints (which often involve
            "cut-joint hinges" conceptually), you use methods
            specifically designed for Tree-Augmented (TA) dynamics. For
            a regular {{ hingecls }}, the
            {py:meth}`Karana.Dynamics.HingeOnode.getInterBodyForceTAFwdDyn`
            method should be called on its {{ onodecls }} to get the
            inter-body force at the onode. The following describes the
            process for recovering the spatial constraint force across a
            {{ cutjtconstraintcls }}.
    *   **Method (from {{ cutjtconstraintcls }}):** {py:meth}`Karana.Dynamics.LoopConstraintCutJoint.getInterBodyForceTAFwdDyn`
        *   **Purpose:** Returns the inter-body spatial force at the source node for TA dynamics.
        *   **Recipe Step:**
        ```python
                # Assuming 'my_loop_constraint_hinge' is an instance of LoopConstraintCutJoint
                # and TA forward dynamics has been computed on the SubGraph containing it.
                # Example:
                my_subgraph = Karana.Dynamics.SubGraph(...)
                Karana.Dynamics.Algorithms.evalForwardDynamics(my_subgraph)

                ta_inter_body_force = my_loop_constraint_hinge..getInterBodyForceTAFwdDyn()
                print(f"TA Inter-body force for loop constraint: {ta_inter_body_force}")
        ```

        *   **Insight:** This force is at the source node and has a sign as if acting on the target node. It includes contributions from generalized forces at the cut-joint. This method should only be called *after* TA forward dynamics has been executed.









- - -



(recipe_flex_body_sec)=
## Flexible body dynamics


(flex_serial_chain_manual_recipe)=
### kdFlex flexible body definition (manual)

Creating a a serial-chain system with flexible bodies follows the same
process as in the {ref}`rigid_serial_chain_manual_recipe` recipe, except
that in we call the `static`
{py:meth}`Karana.Dynamics.PhysicalModalBody.create` method, with params of
type {py:class}`Karana.Dynamics.PhysicalModalBodyParams` with additional
deformation related parameters for the flexible bodies.


<!--
### kdFlex flexible body definition (via FEMBridge)
-->


(flex_serial_chain_procedural_recipe)=
### Procedurally create flexible body serial chain

Creating a a serial-chain system with flexible bodies follows the same
process as in the {ref}`rigid_serial_chain_procedural_recipe` recipe,
except that in `Step 2` we call the `static`
{py:meth}`Karana.Dynamics.PhysicalModalBody.addSerialChain` method, with
parameters of type {py:class}`Karana.Dynamics.PhysicalModalBodyParams` with additional
deformation related fields for flexible bodies.


### Procedurally create flexible body tree

Procedurally creating a tree of {{ modalbodiescls }} follows the process in
{ref}`rigid_regular_tree_procedural_recipe` section except the that the {py:meth}`Karana.Dynamics.PhysicalBody.addTree` call is replaced with the {py:meth}`Karana.Dynamics.PhysicalModalBody.addTree`, with 
parameters of type {py:class}`Karana.Dynamics.PhysicalModalBodyParams` with additional
deformation related fields for flexible bodies.


(recipe_state_space_sec)=
### Linearized state space model

Multibody dynamics models are nonlinear. Linearized, input.output
state-space models are however useful for control system design and
analysis. The steps for creating such linearized models are:

- define a `u_fn(x, u)` input callback function that will use a `x`
  state vector and an input `u` vector to compute and set appropriate
  forces, gravity etc values in the multibody model to effect the
  multibody state-derivative computation.

- define a `y_fn(x, u)` output callback function that will compute the
  output as a function of the `x` state vector and the input `u` vector.

- Call the `Karana.Dynamics.Algorithms::stateSpaceGenerator(mm, n_inputs, n_outputs,
  u_fn, y_fn)` method to compute and return a `StateSpace generator`
  instance `ss_gen` for creating linearized, `(A,B,C,D)` state space
  representation of the system about the current state with inputs
  and outputs defined by the vector-values `u_fn` and `y_fn` functions.
  
- Set the system state `Q` and `U` values, and call `ss_rep =
  ss_gen.generate()` to generate an `ss` instance with linearized
  `(A,B,C,D)` state space matrix values about this configuration. The
  method can be called multiple times for different system state values
  to compute state-space representations about different configurations
  for the system.
  
The generality of this approach allows the users flexibility in making
simple to complex choices for inputs and outputs. The above process
applies to systems with rigid and deformable bodies.


(recipe_kinematics_sec)=
## Kinematics


### Relative pose, velocities, accelerations


To query the relative pose, velocity or acceleration of a pair of {{ framescls }} A and B

- create a {{ f2fcls }} instance using `A2B_f2f = A.frameToFrame(B)`
- the relative pose of A to B can then be obtained as a homogeneous
  transform using `T = A2B_f2f.relTransform()`

- the relative spatial velocity of B with respect to A (as observed
  and represented in the A frame) can then be obtained via
   `V = A2B_f2f.relSpVel()`

- the relative spatial acceleration of B with respect to A (as observed
  and represented in the A frame) can then be obtained via
   `A = A2B_f2f.relSpAccel()`

Since {{ physbodycls }} and {{ nodecls }} instances are {{ framecls }} instances,
they can be used as A and B above in the above expressions for arbitrary
queries between bodies, nodes and {{ framescls }}. Moreover, subhinges
and hinges are {{ f2fcls }} instances, so their instance can be
directly queried for such relative information.


### End-effector Jacobian


The Jacobian defines the (coordinate-dependent) matrix that maps the
generalize velocity coordinates to a relative spatial velocity. The
first step thus is to identify a {{ f2fcls }} instance, say `f2f`
whose `relSpVel()` we are interested in obtaining a Jacobian
for. Towards this

- first create a jacobian generator using `jac_gen =
  Algorithms.jacobianGenerator([f2f], cd)` for this `f2f` with the  
  {{ coorddatacls }} whose components to generate the Jacobian for.

- then call `my_jac = jacgen.jacobian()` to obtain the Jacobian
   matrix. The same generator can be used repeatedly as the
   coordinates change to obtain the updates Jacobian matrix
   values. The two step approach is used to avoid repeating the setup
   costs for each invocation of the `jacobian()` method.

Note that this Jacobian capability is quite general. To obtain an
end-effector Jacobian, we simply need to create a {{ f2fcls }}
instance from the root {{ framecls }} to the end-effector frame, and use the
above recipe. This procedure can be used for Jacobians for the
relative spatial velocity between moving {{ framescls }}, such as between
end-effectors of multi-limbed robotic systems. All that is required is
the {{ f2fcls }} specifying the {{ framecls }} pair of interest.


### Multi-arm Jacobian 

There are times when we want to track multiple Jacobians at once, such
as during multi-fingered grasps. One option would be create jacobian
generator instances - one each for each desired Jacobian. A more
efficient way is to create a multi-jacobian generator as follows:

- create a list of all the {{ f2fcls }} instances that identify the
  list of `f2f` instances for the desired Jacobians

- Create multi-jacobian generator using the `mjac_gen =
  Algorithms.jacobianGenerator(f2fs, cd)` call with the list of `f2f` instances

- call the `mjac_gen.jacobian()` method to get the overall Jacobian
  with block Jacobian entries for each of the `f2f` instances.

Once again, the multi-Jacobian generator can be used over and over as
the coordinates change to get the updated Jacobian values.


(recipe_pose_constraint_sec)=
### Pose-level constraint kinematics 


Systems with bilateral constraints have fewer degrees of freedom than
defined by the hinges in the underlying multibody tree. This brings up
the need to answer questions such as the actual degrees of freedom,
the best sub-set to use as independent generalized velocity
coordinates, the mapping between independent and dependent etc. Note
that the answers to these questions is coordinate dependent. The
following steps show how these questions can be answered at the
pose-level for a subgraph `sg` with internal bilateral constraints.

- **Pose constraint errors:** To check whether the current tree
  hinge+body `Q` generalized coordinates satisfy the bilateral
  constraints, call `pose_err = sg.cks().poseResiduals()`. The
  returned value should be zero if the constraints are satisfied at
  the pose level. This method does not however tell us anything about
  the constraint methods.

  To check whether the current tree hinge+body and the constraint
  hinge `Q` generalized coordinates satisfy the bilateral constraints,
  call `pose_err = sg.poseError()`. The returned value should be
  zero if the constraints are satisfied at the pose level.

- **Solving for Q:** if the pose error is non-zero, we can use the
  current coordinates to solve for a set of subhinge+body+constraint
  coordinates that satisfy the pose-level constraints by calling `err
  = sg.cks().solveQ()`. A nonlinear solver is used to interactively find
  `Q` coordinates that satisfy the constraints.  Success is indicated
  by a zero `err` returned value. The coordinates can be obtained by
  calling `sg.getQ()`.

- **Freezing coordinates:** When the system is under-constrained, that
  is, there are more degrees of freedom than necessary to satisfy the
  constraints, there is the opportunity to only use a subset of the
  coordinates to satisfy the constraints. The remaining coordinates
  can then be used to meet other objectives. Such a need can be met by
  `freezing` individual coordinates for the purposes of the solution
  obtained by the `solveQ()` method. Each individual coordinate is
  associated with a subhinge coordinate. The
  `sg.cks().freezeCoordinate(shg, index)` call can be used multiple
  times to freeze a set of coordinates. Subsequent `solveQ()` calls
  will leave the values of these coordinates unchanged. Coordinates
  can be unfrozen using the `unfreezeCoord()` method. One caution
  to keep in mind is that `solveQ()` may fail to find a solution if
  too many coordinates have been frozen.


(recipe_velocity_constraint_sec)=
### Velocity-level constraint kinematics 

Bilateral constraints also limit the admissible velocity
motion. Before meaningfully tackling velocity level constraints, it is
essential that the system is in in a configuration where the
pose-level constraints have been satisfied as described in the {ref}`recipe_pose_constraint_sec` section.

- **Velocity constraint errors:** To check whether the current set of
  `U` generalized velocity coordinates satisfy the constraints, the
  `sg.velError()` method can be
  used to check whether the returned error vector is zero or not.

- **Velocity constraint matrix Gc:** A vector `U` of generalized
  velocities satisfies the bilateral constraints if `Gc * U = 0` where
  `Gc` denotes the velocity constraint matrix. This configuration
  dependent matrix can be obtained using `Gc =
  Algorithms.evalVelocityConstraintMatrix(SubGraph(sg,
  with_constraints)`. The `with_constraints` boolean argument should be
  set to `True` to include rows for the constraint hinge coordinates in
  the output.

- **Solving for `U`:** If the current velocity coordinates do not
  satisfy the constraints, the `err = sg.cks().solveU()` method can
  be used to solve for a `U` that satisfies the constraints. The
  returned error vector will be zero if a solution was obtained. The
  solution will not change the values of the frozen coordinates. We
  again emphasize that the resulting solution is valid, only if the
  system configuration is such that the pose-level constraints have
  already been satisfied.


- **Independent constraints:** For constrained systems, and important
  question is as to how many of the constraints are independent. The
  number of independent constraint is the `row-rank` of the `Gc`
  velocity constraint matrix.  The
  `Algorithms.evalIndepVelConstraintCoordIndices(sg)` method can be used to
  select the best subset of independent constraints at the current
  configuration. Note that the best set is configuration dependent.
  


  Many dynamics computations assume require that the velocity
  constraint matrix be full row-rank.  To meet this need, and once the
  independent set of constraints has been identified, a modified, full
  row-rank velocity constraint matrix may be used by only using the
  corresponding rows of the original `Gc` matrix to define the new
  velocity constraint matrix.

  An additional serious challenges with constrained dynamics is that
  the number of independent constrain may vary with the system
  configuration as well. The methods described above can be used to
  monitor the constraints during the course of simulations to make
  appropriate adjustments as needed.


(recipe_coordinate_partitioning_sec)=
### Coordinate-partitioning

- **Independent coordinates:** In light of the dependency among the
  velocity coordinates, the coordinate-partitioning approach seeks to
  partition the `U` velocity coordinates into independent and dependent
  coordinates. the {py:meth}`Algorithms.evalVelCoordinatePartitioning <Karana.Dynamics.Algorithms.evalVelCoordinatePartitioning>` method
  returns the best choice independent coordinates at a specific
  configuration. Note that the choice may vary with the system
  configuration. Due to this variability, users may choose to work with
  a different choice of independent velocity coordinates (same number
  however) that work across a broader range of configurations.

 
  
- **Dependency map:** Once a independent set of coordinates has been
  chosen, it is possible to obtain the dependent coordinates such that
  the overall `U` coordinates satisfy the constraints. The dependent
  coordinates mapping matrix `D` such that `U_dependent = D *
  U_independent` can be obtained using `D =
  Algorithms.evalDependentVelCoordinatesMatrix(indep_indices, full)` where
  `indep_indices` is the list of coordinates to be treated as
  independent (length equal to the row-rank of `Gc`). The `full`
  argument should be set to `True` to include rows for the independent
  coordinates in the output matrix.  The `D` matrix is coordinate
  dependent.

- **Locked coordinates:** Due to the coupling between constraints, it
  can happen that some of the velocity coordinates are locked. These
  coordinates can be identified by looking for all-zero rows of the
  `D` dependency matrix.


(recipe_acceleration_constraint_sec)=
### Acceleration-level constraint kinematics 

Bilateral constraints also limit the admissible acceleration
motion. Before meaningfully tackling acceleration level constraints, it
is essential that the system is in in a configuration where both the
pose and velocity-level constraints have been satisfied as described
in the {ref}`recipe_pose_constraint_sec` and a {ref}`recipe_velocity_constraint_sec` section.

- **Acceleration constraint errors:** To check whether the current set
  of `Udot` generalized acceleration coordinates satisfy the
  constraints, the `sg.cks().accelError()` or
  `sg.cks().accelResidual()` methods can be used to check whether the
  returned error vector is zero or not.


- **Solving for `Udot`:** If the current acceleration coordinates do
  not satisfy the  constraints, the `err = sg.cks().solveUdot()`
  method can be used to solve for a `Udot` that satisfies the
  constraints. The returned error vector will be zero if a solution
  was obtained. The solution will not change the values of the frozen
  coordinates. We again emphasize that the resulting solution is
  valid, only if the system configuration is such that the pose and
  velocity-level constraints have already been satisfied.

 


- - -



(recipe_inverse_kin_sec)=
## Inverse kinematics


The inverse kinematics (IK) problem requires solving for the `Q`
configuration coordinates that set the pose for one or more frames on
the multibody system to desired values. In the simplest form it may be
used for positioning a robot arm end-effector at a specific location,
while more generally it may simultaneously position multiple such
end-effectors for a multi-limbed system.

The IK problem is solved in kdFlex as a special case of the general
support for pose constrained in multibody systems described in {ref}`recipe_pose_constraint_sec` section. The general idea is to first

- create a dedicated subgraph `sg` for IK purpose

- create and add hinge constraint (and associated {{ framescls }}) for the
  system {{ framescls }} whose IK pose solution is desired. Hinge constraints
  of the locked type should be used wherever the full pose of a system
  {{ framecls }} needs to set.

- freeze the coordinates for the hinge constraints so that they will
  remain untouched by the solution process

Then to obtain the IK solution

- Relocate the constraint {{ framescls }} to the desired location for each of
  the corresponding {{ framescls }}.

- call `sg.solveQ()` to solve for the `Q` coordinates. On success, the
  `Q` coordinates will satisfy the IK constraints, and thus position
  the system {{ framescls }} at the constraint {{ framescls }}.

- these two steps can be repeated over and over as the IK positioning
  needs evolve for the system.

This approach allows IK solutions involving multiple {{ framescls }} to
be positioned. Furthermore, by using appropriate constraints, the
relative pose requirements can be across non-stationary {{ framecls }}
pairs that are both attached to the multibody system. Furthermore, by
using appropriate loop hinge constraints, the IK solution can allow for
finding solutions where full to partial pose of the system {{ framescls
}} to be set. For instance, if the requirement is to only position the
system {{ framecls }} at a location with no restrictions on orientation,
a hinge constraint of {py:attr}`Karana.Dynamics.HingeType.BALL` type
should be used. For the case where orientation only needs to be set, a
{py:attr}`Karana.Dynamics.HingeType.TRANSLATIONAL` hinge type should be
used. for full pose control, a
{py:attr}`Karana.Dynamics.HingeType.LOCKED` type hinge should be used.

(recipe_statepropagator_sec)=
## State Propagation

The `Karana::Dynamics::StatePropagator` class:
*   Manages the simulation time.
*   Holds the current state vector (generalized positions and velocities).
*   Uses an underlying numerical integrator (e.g., RK4, CVODE) to solve the ordinary differential equations (ODEs) that describe your multibody system's dynamics.
*   Allows for registering events that trigger at specific times or periodically.

### Creating a state propagator


Creating a `Karana::Dynamics::StatePropagator` is a crucial step for
performing time-domain simulations of your multibody system. The
`StatePropagator` is responsible for integrating the system's equations
of motion over time, advancing its state (generalized positions `Q` and
velocities `U`).

You must have a {{ subtreecls }} or {{ subgraphcls }} instance that represents your mechanical system. The `StatePropagator` operates on this system.

*   This  should be fully defined with bodies, hinges, and any other relevant components.
*   It's good practice to call `ensureHealthy()` on it before passing it to the `StatePropagator`.




The following method can be used to create a state propagator. You provide your {{ subtreecls }} object and specify the `integrator_type`.
*   **Method Signature:** `StatePropagator::create(kc::ks_ptr<SubTree> st, km::IntegratorType integrator_type, ...)`
*   **Parameters:**
    *   `st`: The {{ subtreecls }} instance.        
    *   `integrator_type`: an `enum` specifying the numerical integrator to use. Common choices are:
        *   `CVODE_STIFF`: For stiff systems (often recommended for robustness).
        *   `RK4`: A simpler, fixed-step Runge-Kutta fourth order integrator (good for non-stiff systems or when fixed steps are required).

    * `solver_type`: An `enum` specifying the type of multibody dynamics solver to use. The option for  {{ subtreecls }} systems is `TREE_DYNAMICS`. For a {{ subgraphcls }}, the best option is `TREE_AUGMENTED_DYNAMICS`.
        
*   **Recipe Step:**
```python
        # Create the StatePropagator
        # Using "cvode_stiff" for general robustness, or "rk4" for simplicity/fixed steps
        integrator_type = CVODE_STIFF
        solver_type = TREE_DYNAMICS
        sp = kd.StatePropagator(mb, integrator_type, None, None, solver_type)
        print(f"StatePropagator created using '{integrator_type}' integrator.")
```



(recipe_kmodels_register_sec)=
### Registering and unregistering a KModel
By convention, {{ kmodelscls }} are registered with a {{ mmcls }} at construction. Therefore, they typically only need to be registered if they were previously manually unregistered. A {{ kmodelcls }} can be registered manually by using {py:meth}`ModelManager.registerModel <Karana.Dynamics.ModelManager.registerModel>` or {py:meth}`StatePropagator.registerModel <Karana.Dynamics.StatePropagator.registerModel>` methods and unregistered manually by using the {py:meth}`ModelManager.unregisterModel <Karana.Dynamics.ModelManager.unregisterModel>` {py:meth}`StatePropagator.unregisterModel <Karana.Dynamics.StatePropagator.unregisterModel>` methods.

### KModel param initialization
By convention, {{ kmodelcls }}'s parameters start off not ready and must be initialized by the user. How this is done is model/parameter dependent. The parameters are always located on the `params` member of the `KModel`, and are typically set either via member variables on `params` or via `setter` methods. See the {ref}`kmodel_params_sec` section for more details.


(recipe_spstate_sec)=
### Setting state propagator state

The {{ mmcls }} initial time and continuous states for the numerical integrator must be initialized
before running a simulation. This can be done by calling the
{py:meth}`StatePropagator.setTime(t)
<Karana.Dynamics.StatePropagator.setTime>` method to set the `t` initial
time, and the {py:meth}`StatePropagator.setState(x)
<Karana.Dynamics.StatePropagator.setState>` method to set the `x`
initial continuous state values. The `x` state vector combines the
multibody system's `Q` and `U` coordinates together with the continuous
state coordinates of all of the registered models. Assembling the `x`
vector manually is not recommended since there can be errors in the
packing order. The recommended steps instead are to:

1. initialize the `Q` and `U` coordinates of all the {{ subhingecls }}
  objects in the multibody system by looking them up and calling their
  `setQ()` and `setU()` methods.
2. initialize the continuous states for any registered {{ kmodelcls }}
  that has them.
3. call the {py:meth}`x = StatePropagator.assembleState()
<Karana.Dynamics.StatePropagator.assembleState>` method to correctly
combine the component coordinates set in steps (1) and (2) into an `x` state
vector.
4. call {py:meth}`StatePropagator.setState(x)
<Karana.Dynamics.StatePropagator.setState>` method to set the initial
state in the state propagator.

The propagator's `setState()` method can be called at any time to reset
the integrator state - except when there have been configuration changes
that may have changed the size or structure of the state. In this case a hard reset is required  When there are
constraints present, this method will verify that the new state
satisfies the constrains and throw an error if that is not the case.

(recipe_datalogging_sec)=
## Data logging
Data logging is done with the {py:class}`Karana.KUtils.H5Writer`. This class takes uses {py:class}`Karana.KUtils.PacketTableConfig` to define tables and functions that populate the column of said tables. These functions are used to calculate a new row each time {py:meth}`H5Writer.log <Karana.KUtils.H5Writer.log>` is called. They can be defined in C++ or Python. See the {ref}`data_logger_setup_sec` section for more details.

### Defining tables
Tables are defined using the {py:class}`Karana.KUtils.PacketTableConfig` class. A summary of the steps follows. These steps are shown in detail in the {ref}`packet_table_config_setup_sec` section.
1. Use the `create` method to create a new {py:class}`PacketTableConfig <Karana.KUtils.PacketTableConfig>`.
1. Add columns to the table using the `addData` methods.
1. Add the table from {py:class}`PacketTableConfig <Karana.KUtils.PacketTableConfig>` to the {py:class}`H5Writer <Karana.KUtils.H5Writer>` using the {py:meth}`createTable <Karana.KUtils.H5Writer.createTable>` method.

(recipe_events_sec)=
## Using events

### Different types of events
Broadly speaking, events can be broken down into three categories:
1. Events that happen at a certain time(s)
1. Events that happen at a fixed point when advancing the dynamics forward in time (like at every derivative call or at every hop)
1. Events that are triggered based on the value of some variable (a zero-crossing event).

Events that happen at certain time(s) are created and added to the simulation via timed events. See the {ref}`timed_events_sec` section for details.

Events that happen at a fixed point when advancing the dynamics forward in time are located on one of the following `CallbackRegistry`s. These are all located on the {py:attr}`StatePropagator.fns <Karana.Dynamics.StatePropagator.fns>`. 
* {py:attr}`pre_deriv_fns <Karana.Dynamics.MMFunctions.pre_deriv_fns>` - Events on this `CallbackRegistry` will run before each derivative call.
* {py:attr}`post_deriv_fns <Karana.Dynamics.MMFunctions.post_deriv_fns>` - Events on this `CallbackRegistry` will run after each derivative call.
* {py:attr}`pre_hop_fns <Karana.Dynamics.MMFunctions.pre_hop_fns>` - Events on this `CallbackRegistry` will run before each simulation hop.
* {py:attr}`post_hop_fns <Karana.Dynamics.MMFunctions.post_hop_fns>` - Events on this `CallbackRegistry` will run after each simulation hop.
Events are registered with `CallbackRegistry`s using map- or dictionary-like access. For example, in C++
```cpp
sp->fns.pre_deriv_fns["my_event"] = [](const km::Ktime& t){std::cout << t << std::endl;}
```
and in Python
```python
sp.fns.pre_deriv_fns["my_event"] = lambda t: print(t)
```

Events that are triggered based on the value of some variable are called zero-crossing events. These are discussed in detail in the {ref}`zero_crossings_sec` section.To set one of these events, use the {py:attr}`zero_crossing_fns <Karana.Dynamics.MMFunctions.zero_crossing_fns>` `CallbackRegistry` from {py:attr}`StatePropagator.fns <Karana.Dynamics.StatePropagator.fns>`. This event takes as input the current state vector and a boolean. The input boolean indicates whether the event has triggered or not, and it outputs a boolean that indicates when the zero has been crossed. Here is an example in C++
```cpp
sp->fns.zero_crossing_fns["my_zero"] = [](const km::Vec &x, bool crossed) {
    if (crossed) {
        std::out << "Executing the zero crossing function!." << std::endl;
        // Do other work here
        return true;
    } else {
        return x[0] < 0.0;
    }
};
```
This will trigger once the first element of the state vector falls below zero. Here is the same function, but implemented in Python
```python
def _zc(x: NDArray[np.float64], crossed: bool):
    if crossed:
        print("Executing the zero crossing function!.")
        # Do other work here
        return True
    else:
        return x[0] < 0.0
sp.fns.zero_crossing_fns["my_zero"] = _zc
```

(recipe_plotting_sec)=
## Plotting
Dynamic data plotting (plots that are updated as the simulation is running) can be done using the {py:class}`Karana.KUtils.DataPlotter.DashApp`. This class is used to quickly create a [Dash app](https://dash.plotly.com/) whose data is updated dynamically as the simulation progresses. The {py:class}`Karana.KUtils.SinglePlotData` is used to define the data to be plotted. Here is a brief example:
```python
def time():
    return ktimeToSeconds(sp.getTime())

my_var = VarVec3("v", myFrameToFrame.relSpVel().getv)

def scalarData():
    return np.linalg.norm(myFrameToFrame().relTransform().getTranslation())

pds = SinglePlotData("test1", ("t", time), [("s": scalar), vec])

d = DashApp([pds], title="Test Dash App")
```

In this example, a {py:class}`SinglePlotData<Karana.KUtils.SinglePlotData>` is used to define the data to be plotted. The dependent variables can be scalar or vector values. Then, the {py:class}`DashApp <Karana.KUtils.DataPlotter.DashApp>` is used to create a live plotting instance. This takes a list of {py:class}`SinglePlotData <Karana.KUtils.SinglePlotData>` instances, so it can be used to show multiple plots on the same web frontend. Once the app is started, it will print a URL on the terminal that one can follow to view the plot(s).

To update the plots manually, simply call the {py:meth}`update <Karana.KUtils.PlotData.update>` method on the {py:class}`DashApp <Karana.KUtils.DataPlotter.DashApp>`. This will invoke all the functions in the {py:class}`SinglePlotData <Karana.KUtils.SinglePlotData>` instances and use their result to update the plot(s) data.

Oftentimes, in a simulation, one wants the {py:meth}`update <Karana.KUtils.PlotData.update>` method to be called automatically as the simulation is stepping. This can be done via the {py:class}`Karana.Models.DataPlotter` {{ kmodelcls }}.

It can be used like this:
```python
dp = DataPlotter("plot_data", sp, d)
dp.setPeriod(0.1)
```

In this example, `sp` is a {{ mmcls }}, `d` is a {py:class}`DashApp <Karana.KUtils.DataPlotter.DashApp>`, and its {py:meth}`update <Karana.KUtils.PlotData.update>` method will be called by the KModel every 0.1 seconds as the simulation progresses.

<!-- TODO 

## Feedforward dynamics

### Gravity compensation

### Single arm computed torque

### Computed torque with constraints

### Load balancing

### Computed torque for under-actuated systems


## Centroidal dynamics

### CoM momentum

### CoM mass properties

### ZMP

### Tree forward dynamics and inter-body forces

### Constrained Tree-Augmented (TA) forward dynamics and inter-body forces

### Parallel computation of dynamics


## State Propagation

### Picking an integrator

### Multi-rate models and hops


### Zero-crossing detection


(recipe_collision_sec)=
## Collision and contact dynamics
-->

(recipe_graphics_sec)=
## Graphics visualization

The `setupGraphics` helper method in kdFlex is designed to streamline the process of setting up the graphics environment, including the scene and a web server for browser visualization. It's typically called on a multibody (`mb`) object.

Here's a recipe for setting up graphics using `setupGraphics`:

**Call `setupGraphics`**

Call the `setupGraphics` helper method on your multibody object. This method takes care of initiating the graphics environment for you.

```python
cleanup_graphics, web_scene = multibody.setupGraphics(port=0, axes=0.5)
```

**Explanation of arguments:**

*   **`port`** (int, default `29523`): Specifies the port to bind the WebUI server to. Using `port=0` requests an arbitrary unused port, which is a common practice to avoid port conflicts.
*   **`axes`** (float, default `1.0`): Determines the length of the axes visualization on the root frame of your scene. Examples show usage with `0.5` or `0.0`.
*   **`client_type`** (Literal, default `'auto'`): Can be `'auto'`, `'electron'`, `'notebook'`, or `'webbrowser'`, controlling how the client for the WebUI is launched.
*   **`origin_frame`**: An optional `Frame` object to center the client's origin.
*   **`wait_for_clients`** (int, default `0`): Specifies if the system should wait for a certain number of clients to connect before proceeding. Examples show `wait_for_clients=1`.
*   **`wait_for_clients_timeout`** (float, default `0.0`): The timeout duration for waiting for clients.

**Return Values:**

The `setupGraphics` method returns two key arguments:
1.  **`cleanup_graphics`**: A callable function that you will use later to clean up the scene layer and web server when your simulation concludes.
2.  **`web_scene`**: A {{ webscenecls }} object. This object provides an interface to programmatically interact with the browser visualization, such as setting the camera position and orientation. The {py:class}`WebScene <Karana.Scene.WebScene>` class is a derived type of {py:class}`GraphicalScene <Karana.Scene.GraphicalScene>`, which is a {{ scenecls }} with an added graphics-specific interface.


(recipe_stick_sec)=
### Stick figure graphics

Adding "stick parts" in kdFlex is a straightforward way to visualize the links and structure of your multibody system. These stick parts are simple graphical representations that help you quickly understand the kinematic configuration of your model.

Here's a recipe specifically for adding stick parts:

**Ensure Graphics Setup is Complete**

Before adding stick parts, you need to have initialized your graphics environment using `setupGraphics`. This provides the scene where the stick parts will be rendered.

```python
# Assuming 'mb' is your initialized multibody object
cleanup_graphics, web_scene = mb.setupGraphics(port=0, axes=0.5)
```

**Call `createStickParts()` on Your Multibody Object**

The `createStickParts()` method is called directly on your multibody (`mb`) object. It automatically generates and attaches simple stick-like geometries to represent the links (rigid bodies) of your multibody system within the scene.

```python
mb.createStickParts()
```

**Optionally Customize with {py:class}`StickPartsConfig <Karana.Dynamics.StickPartsConfig>`**

You can customize the geometry and appearance of the stick parts. For example:

```python
from Karana.Dynamics import StickPartsConfig
stick_config = StickPartsConfig()
stick_config.body_stick_radius = 0.005
stick_config.pin_hinge_radius = 0.005
stick_config.sphere_hinge_radius = 0.005
stick_config.node_radius = 0.005
mb.createStickParts(stick_config)
```

<!--
### Using CAD geometries

### Applying materials

### Using trails

### Syncing up with time-domain simulation

### Rendering frames for creating movies


### Shadows




## Advanced topics


### Usage tracking

-->
