Skip to main content

Physics export (rod_spec)

The rod_spec bridge exports a device's reduced constitutive properties — bending, torsion and axial stiffness, plus outer diameter and linear density, per section — as a small, versioned JSON file that any physics model can import. It is the supported way to drive an external simulator (a robotics gym, a navigation solver, a custom FEA or XPBD rod) with real device mechanics computed from construction, instead of stiffness values hand-tuned to a physical sample.

Introduced in VirtuCath™ 1.2.0.

Why it exists

Most rod and beam solvers model a catheter from a single material — one Young's modulus for the whole device, with torsion tied to bending through a fixed Poisson's ratio. That cannot represent a real catheter, which is a graded construction (a stiff braided shaft tapering to a soft atraumatic tip) and, in the case of a braid, one whose torsion is deliberately decoupled from bending.

VirtuCath already computes the true per-section stiffness profile from the actual construction — braid angle, wire, liner, durometers, extruded cores. The rod_spec bridge hands those numbers to your solver directly, section by section:

  • EI — bending stiffness (per axis; see anisotropy)
  • GJ — torsional stiffness (independent of EI)
  • EA — axial stiffness
  • r_out, lin_density — geometry and mass for contact and inertia

The contract

Files declare a schema version (virtucath.rod_spec/1.0) and are SI throughout (N, m, kg, rad). The device is described as piecewise-constant sections, which is VirtuCath's native model, with an optional resampled convenience array for fixed-segment rods.

{
"schema": "virtucath.rod_spec/1.0",
"provenance": {
"virtucath_version": "1.2.0",
"config_hash": "sha256:…", // hash of the source configuration
"units": "SI"
},
"device": {
"name": "DefaultCatheter",
"total_length_m": 0.319888,
"proximal_is_s0": true, // s is measured from the proximal hub toward the tip
"transitions": "step" // sections are STEP changes, not tapers
},
"sections": [ // source of truth — piecewise constant
{
"s_start_m": 0.0,
"s_end_m": 0.20,
"label": "Proximal",
"EI_x_Nm2": 1.70e-3, // bending about local x
"EI_y_Nm2": 1.70e-3, // bending about local y (equal when isotropic)
"GJ_Nm2": 1.74e-3, // torsion (independent of EI)
"GJ_Nm2_weak": 1.74e-3, // free-warping torsion
"GJ_Nm2_strong": 1.74e-3, // warping-constrained torsion
"EA_N": 2831.1, // axial
"r_out_m": 0.001694, // OD / 2, for contact + rendering radii
"r_in_m": 0.0005,
"lin_density_kg_m": 8.996e-3
}
// … one entry per VirtuCath section
],
"resampled": { // OPTIONAL — present when requested
"ds_m": 0.001,
"s_m": [ … ],
"EI_x_Nm2": [ … ], "EI_y_Nm2": [ … ], "GJ_Nm2": [ … ],
"EA_N": [ … ], "r_out_m": [ … ], "lin_density_kg_m": [ … ]
}
}

Rules a consumer must respect

  • SI units. Newtons, metres, kilograms, radians. No mm, no grams.
  • s = 0 is the proximal hub, increasing toward the distal tip (proximal_is_s0: true).
  • Transitions are step changes. Do not smooth or interpolate stiffness across a section boundary unless you are deliberately modelling a physical taper — the steps are real (durometer and reinforcement changes).
  • Use lin_density_kg_m for mass per unit length; do not derive mass from r_out and an assumed density (the wall is a composite).
  • EA and EI are independent of the geometry. Because the wall is a composite, EA/EI does not equal the section's area-over-second-moment, and GJ/EI varies section to section. Feed the stiffnesses directly; do not back them out into a single modulus.

Anisotropic sections

For isotropic sections EI_x_Nm2 == EI_y_Nm2. They differ only when a section is genuinely anisotropic — most commonly a multi-lumen extruded core or an oval profile (VirtuCath 1.2.0). A solver that supports only a single bending stiffness can use EI_y_Nm2 (the primary axis); a Cosserat solver should take both. Off-axis material rotation, when present, is reported as principal_axis_angle_deg.

Steering (pullwires)

A steerable device carries an optional actuation block. It is geometry only — it describes where each pullwire sits and how a pull maps to a bending moment, so any engine can reproduce the steering from the exported EI profile. No construction is exposed.

"actuation": {
"type": "pullwire",
"convention": "moment_Nm = tension_N * radial_offset_m, applied over [proximal_s_m, distal_attachment_s_m], bending toward angle_deg",
"pullwires": [
{ "id": 0, "radial_offset_m": 0.00114, "angle_deg": 180.0,
"proximal_s_m": 0.0, "distal_attachment_s_m": 0.30 }
]
}

A wire at tension T applies a moment M = T · radial_offset_m over its span [proximal_s_m, distal_attachment_s_m], bending the device toward angle_deg in the material frame. Because the local EI is in the same file, an engine gets the resulting curvature directly (κ = M / EI). Adapters map this to each engine's native mechanism:

  • MuJoCo — a spatial tendon through sites at the wire offset (VirtuCath's direct MJCF export already builds these tendons and their actuators; use it when you want a ready-to-run steerable MuJoCo model).
  • NVIDIA Cosserat — an imposed tip rest-curvature (set_tip_bend / RodTipConfig), since that solver steers by target curvature rather than a physical tendon.

The block is omitted entirely for a non-steerable device.

Producing an export

From the application

File → Export → Physics Simulation Spec (.json) writes the file for the currently loaded device.

From the CLI API

Load a device, then call export_rod_spec. It returns the rod_spec object and, if you pass path, also writes it to disk. Pass resample_ds_m to include the fixed-step array.

{"command": "load", "config": "Catheter Files/default_catheter.json"}
{"command": "export_rod_spec", "path": "device.rod_spec.json", "resample_ds_m": 0.001}
{
"status": "success",
"rod_spec": { "schema": "virtucath.rod_spec/1.0", … },
"warnings": [], // empty = passed the round-trip validator
"written_to": "device.rod_spec.json"
}

Reference adapters

Worked examples for loading a rod_spec into common solvers ship with the bridge:

  • Generic Python loader — a dependency-free load_rod_spec() that returns the local EI/GJ/EA/r_out/lin_density at any arc-length, plus an evenly-sampled helper for fixed-segment rods. The starting point for any custom engine.
  • NVIDIA Isaac for Healthcare (Cosserat rod) — maps each section onto the per-edge bend/twist stiffness of the open XPBD Cosserat-rod solver in the Catheter Navigation workflow.
  • MuJoCo (MJCF) — VirtuCath also exports a MuJoCo model directly; the adapter documents the same section-to-body mapping for an existing MJCF chain.

Solver support and current limitations

What a target engine can accept varies, and one common case needs a workaround today.

Per-segment properties (NVIDIA Cosserat solver)

The open NVIDIA Isaac for Healthcare Cosserat-rod solver takes a single scalar material for the whole rod — one Young's modulus, with the shear modulus tied to it through a fixed Poisson's ratio (ν = 0.3), and one bend-stiffness multiplier. There is no public API to set stiffness per segment, to make torsion independent of bending, or to set axial stiffness directly. As shipped, it cannot represent a graded catheter or a braided shaft.

Workaround used by the NVIDIA adapter

The solver already stores stiffness per rod edge internally — the scalar config is only broadcast into those arrays at setup. The bundled adapter writes VirtuCath's real per-section EI/GJ straight into that per-edge array instead, neutralising the scalar modulus. Two details make it exact:

  1. Run the solver in mm-kg-s. Its bend/twist compliance is regularised with a fixed epsilon; catheter-scale rigidities in SI fall below it and get silently clamped. Working in millimetres scales EI up by 1e9 and clears the floor.
  2. Map to the discrete constraint. The solver's bend/twist constraint is C ≈ κ·l/2, so the per-edge stiffness that reproduces a target EI is 4·EI/l² (l = segment length). Same factor for GJ.

This is validated against Euler-Bernoulli (δ = P·L³/3EI) to within a few percent. A feature request has been filed with NVIDIA to accept per-segment material natively; if accepted, the adapter simplifies to a direct assignment.

Axial stiffness on the NVIDIA solver

That solver's XPBD path treats stretch and shear as near-rigid regardless of configuration, so EA is not exercised there — the rod is effectively inextensible. This is usually fine for catheters (they are ~inextensible), but if your application depends on axial compliance, the NVIDIA Cosserat path is not the right target today. EA is present in the rod_spec and is honoured by MuJoCo and by custom solvers that model stretch.

MuJoCo and most custom beam/FEA solvers accept per-segment stiffness natively, so they consume the rod_spec directly with no workaround.

Pullwire steering

The actuation block is a v1 abstraction: it exports pullwire geometry (offset, angle, attachment span) and the moment convention, not a calibrated force→deflection curve. How faithfully it reproduces steering depends on the engine:

  • MuJoCo models the pullwire directly as a spatial tendon, so steering is physical and multi-wire control is exact. For a steerable device, prefer VirtuCath's dedicated MJCF export, which builds the tendons and their actuators for you; the rod_spec actuation block is the engine-neutral fallback.
  • NVIDIA Cosserat has no tendon. It steers by imposing a rest curvature on the tip, so the adapter approximates a pullwire as a target tip bend. That approximation has limits: it applies a curvature at the distal tip rather than a moment distributed along the wire's full attachment span, and independent simultaneous control of multiple wires is not represented. It reproduces single-wire tip deflection well; complex multi-wire or proximal-shaft steering is out of scope on that solver.
note

The actuation block does not yet carry a calibrated actuation gain (pull force → tip deflection). Engines derive curvature from the exported EI and the wire offset (κ = T·offset / EI). A calibrated reference point may be added in a later schema revision.

What is not exported

The bridge emits only the reduced constitutive outputs. It never exports the composite internals that produced them — braid engagement model, flowdown stackup, ply moduli, layer-by-layer construction. That keeps the format engine-neutral and is the intellectual-property boundary of the export (EULA §2.8). Bulk automated exports are rate-limited.