<!-- Revised by Codex for kelly on 2026-10-27. -->
(advanced_guide_page)=
# Advanced Guide

This area contains detailed technical discussion related to {{ kdflex }}
architecture and design.

```{include} advanced_guide/mbody_root_frames.md
:heading-offset: 2
:relative-images:
:relative-docs: advanced_guide/
```

## Partitioned integrators

{py:func}`Karana.Integrators.createIntegrator` takes `num_q_states` immediately
after `num_states`. The state layout is `[Q, U]`: the first `num_q_states` entries
are Q and the remaining entries are U. Ordinary integrators store this count
but do not use the partition; pass zero when it is unspecified. Every integrator
requires `num_q_states <= num_states`. The
{py:meth}`Karana.Integrators.Integrator.nQ` getter returns the
stored count. {py:meth}`Karana.Integrators.Integrator.hardReset`
takes both dimensions, while a soft reset preserves them.

{py:class}`Karana.Integrators.PartitionedIntegrator` is abstract and requires
nonempty Q and U blocks for a nonempty system. An empty system uses both counts
set to zero and advances time without evaluating derivatives.

{py:class}`Karana.Integrators.ExplicitVerletIntegrator` uses explicit
kick–drift–kick steps. Each `advanceTo` call takes one step over the requested
interval. Second-order accuracy requires separable equations `Qdot = f(U)` and
`Udot = g(t, Q)`. Q and U may have different sizes. Coordinate-dependent Q rates
and velocity-dependent forces fall outside this contract; the integrator cannot
check those dependencies from the callback.

For a unit harmonic oscillator:

```python
import Karana.Core as kc
import Karana.Integrators as ki
import numpy as np

def oscillator(time: object, state: np.ndarray, derivative: np.ndarray) -> bool:
    """Write [U, -Q] into derivative for state=[Q, U]; time is unused; return True."""
    derivative[0] = state[1]
    derivative[1] = -state[0]
    return True

integrator = ki.createIntegrator(ki.IntegratorType.EXPLICIT_VERLET, 2, 1, oscillator)
try:
    integrator.softReset(0.0, np.array([1.0, 0.0]))
    for step in range(1, 101):
        integrator.advanceTo(step / 100)
    print(integrator.getX())
finally:
    kc.discard(integrator)
```

Manual construction calls must supply the new argument, and dependent C++
libraries must be rebuilt. StatePropagator integration also requires its factory
and hard-reset calls to supply the Q count of the actual packed layout.

