Skip to content

RodStraightSection::getRestTransformOnX places rest-shape node at wrong position for non-first sections #223

@lkarstensen

Description

@lkarstensen

Summary

RodStraightSection::getRestTransformOnX computes the wrong rest position for any section that is not the first entry in WireRestShape.wireMaterials. Using two consecutive RodStraightSection instances causes massive elastic forces at every timestep, leading to guidewire explosion on vessel-wall contact.

Affected component

  • File: src/BeamAdapter/component/model/RodStraightSection.inl
  • Method: RodStraightSection<DataTypes>::getRestTransformOnX

The bug

// CURRENT (buggy)
void RodStraightSection<DataTypes>::getRestTransformOnX(
    Transform& global_H_local, const Real x_used, const Real x_start)
{
    global_H_local.set(type::Vec3(x_start + x_used, 0.0, 0.0), Quat());
}

x_used is the global curvilinear abscissa along the entire wire (e.g., 430 mm for a point 10 mm into a section that starts at 420 mm). x_start is the section's start key-point (keyPts[i-1] in WireRestShape::getRestTransformOnX).

The formula x_start + x_used adds the section start twice, placing the rest-shape node at 2 × x_start + local_offset instead of the correct x_used.

Numerical example — stiff section 420 mm, flex section 30 mm:

x_start x_used Computed position Correct position
Section 0 (stiff) 0 210 0 + 210 = 210 ✓ 210
Section 1 (flex) 420 430 420 + 430 = 850 430

The flex-tip rest shape is placed ~420 mm past where it should be. Mechanical DOFs sit at ~430 mm; rest position at ~850 mm → AdaptiveBeamForceFieldAndMass generates a huge elastic restoring force every step → explosion on any disturbance.

Correct pattern

Both RodSpireSection and RodMeshSection follow the correct pattern — compute a local offset, then add x_start once:

// RodSpireSection
Real lengthCurve = x_used - x_start;       // local offset
// ... helix geometry from lengthCurve ...
type::Vec3 SpirePos = PosEndCurve + type::Vec3(x_start, 0, 0);  // back to global

// RodMeshSection
Real abs_curr = x_used - x_start;           // local offset
// ... mesh interpolation → PosEndCurve ...
type::Vec3 ExtremityPos = PosEndCurve + type::Vec3(x_start, 0, 0);  // back to global

For a straight section the local offset is trivially x_used - x_start, so the global position reduces to simply x_used.

Proposed fix

// FIXED — src/BeamAdapter/component/model/RodStraightSection.inl
void RodStraightSection<DataTypes>::getRestTransformOnX(
    Transform& global_H_local, const Real x_used, const Real x_start)
{
    global_H_local.set(type::Vec3(x_used, 0.0, 0.0), Quat());
}

One-line change. Zero effect on single-section wires (x_start = 0 → old and new formulas identical).

Workaround

Use RodMeshSection with a two-point straight mesh for the non-first sections. This exercises the correct code path until the fix lands.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions