Repository navigation
feat: add torsion to the Hermite finite-EI cable - #2
Merged
Merged
Conversation
New module CableDyn_HermiteTorsion for condensed isotropic torsion: for one line it returns the parallel-transport holonomy Theta between the two end frames, its gradient and its Hessian in the existing general-band layout (half-bandwidth 11), plus the derivatives with respect to incremental end frame rotations. Element holonomies and smallest-rotation chain terms are differentiated in closed form; the chain derivatives use per-link gauges frozen at the current configuration. Also provides the 2 pi unwrap rule with a pi/2 step limit and a fold guard (1 + t1.t >= 0.5) that fail closed with named status codes. The module is not called by any solver yet, so existing results are unchanged.
tests/test_hermite_torsion_kernel.f90 checks Theta, its gradient and every second derivative against reference values from validation/scripts/hermite_torsion_oracle.py (JAX, four random large-deformation lines with clamped and articulated ends), and against finite differences. It also checks the band edge, rigid-rotation invariance, planar curves, the helix closed form, unwrapping through more than three turns, the step limit, the fold guard and malformed input.
CD_HermiteCable_Static_Solve takes an optional torsion description (CD_HermiteTorsionType: GJ per element, end frames, end compliances, imposed twist Phi, the accepted Theta). Without it nothing changes. With it, the imposed twist is the last load stage: Phi is ramped from the twist of the untwisted equilibrium (zero torque) in stages of at most pi/4, halved on a failed stage. Each Newton step adds E_t = (Phi - Theta)^2/(2C) and solves the bordered system by Sherman-Morrison on the band factor, with pivot, denominator and backward-error guards and a shifted, refined fallback. Theta is unwrapped against the last accepted iterate and may change by at most pi/2 per accepted step. Every converged stage is tested for stability by the inertia of B + g g^T/C (banded L D L^T and the determinant lemma); an unstable state above a buckling onset is left by descent along the lowest mode and energy minimisation, and one exact zero mode can be allowed for coaxial clamped ends. The energy step handles the rank-one term through the same update. tests/test_hermite_torsion_static.f90 gates pure torsion (exact torque), the clamped Greenhill onsets of van der Heijden et al. (2003) eq. (33) at N = 32 within 1e-5 with fourth-order convergence, the semi-tangential pinned onsets, post-buckling descent, multi-turn twist across the +-pi branch of the holonomy and fail-closed inputs.
CD_HermiteCable_Dyn_Set_Torsion (and CD_HFMF_Set_Torsion) installs the torsion of a converged static solve: end frames, GJ, the imposed twist and the accepted Theta, which now passes through snapshot, restore and end. The residual, the support reaction, the end force, the energy and the connection moment include it. The moment adds M_t dTheta/dw of the end frame, so a torsionally restrained end returns its torque to the support, also at a bending-pinned end; at a rigid end the slaved tangent adds its share through the constraint reaction. CD_HermiteCable_Dyn_Torsion_State reports Theta and the torque without advancing the stored state. The time step refuses a model with torsion (statics only in this build).
END CONNECTIONS rows take optional columns TorsStiffness NxX NxY NxZ [Pretwist]: Free (default), Rigid or a torsional stiffness, the zero-twist reference normal in the frame of Ez, and a roll of the end frame about Ez in degrees. Six-column rows parse as before. Torsion is solved on a line only when both ends are restrained; it needs a finite-EI line with a Fixed End B and an explicit GJ on every section, and is refused on rod ends, non-Rigid6 bodies and with ATTACHMENTS. Malformed rows fail with named errors. On the standalone cubic-Hermite route (TMax 0) a torsional line is solved in 3D, then the imposed twist Phi = Pretwist(B) - Pretwist(A) is ramped on the final mesh with stability checks and buckling descent; the torque is returned to a Rigid6 body at End A, in the output loads and in the body static equilibrium. New channels Torq<L>N<J> (N m), Twist<L>N<J> (cable twist from End A, deg) and Twist<L> (Phi - Theta, deg) are accepted only on torsional lines. Dynamic decks (TMax > 0), modal analysis, the coupled OpenFAST and FAST.Farm entries and the mixed aggregate route refuse torsion by name. tests/test_torsion_deck.f90 gates the torque law and twist channels, byte-identical output with Free or one-end torsion columns, a three-turn pretwist sweep, the body torque return, the moored-body statics and every named refusal.
CD_HermiteTorsion_Line takes an optional workspace (CD_HermiteTorsion_Work_Init) for its chain vertices, gauges and Hessian band, so a dynamic step can evaluate the twist without heap allocation. With keep_hessian the unscaled Hessian stays in the workspace and CD_HermiteTorsion_Work_Add_Hessian adds it with a factor that depends on Theta (the torque). The kernel test checks that the workspace path is bit-identical to the allocating one and that a workspace of the wrong size is refused.
The generalised-alpha step now carries condensed torsion instead of refusing it. The residual adds -M_t dTheta/dq and the tangent -M_t d2Theta/dq2; the rank-one (1 - alpha_f) g g^T / C joins the solve by Sherman-Morrison on the band factor, with g, K^-1 g and the denominator kept with the factor so that a reused factor stays consistent. Theta is unwrapped against the committed value; a step that would move it by more than pi/2 fails its evaluation, so the line search or the recovering step cuts it. Torsion is quasi-static (no torsional inertia) and needs the force-blended generalised-alpha. CD_HermiteCable_Dyn_Set_Torsion_Drive sets the end frames and Phi at the end of the next step (the recovering step interpolates the frames on SO(3) and Phi linearly over its substeps); the commit advances them with Theta. CD_HermiteCable_Dyn_Set_Torsion_Frames moves the committed frames between steps. Snapshot and restore carry Theta, Phi and the frames. The kernel evaluates in a model-owned, explicit-shape workspace, so a step allocates nothing. CD_HFMF_Set_Torsion can attach the coupled end's torsion frame to its parent: UpdateStates and SetCoupledKinematics turn it with the parent orientation (also at a bending-pinned end), and UpdateStates takes the imposed twist u_twist. The checkpoint mirror gains Theta, Phi and the frames when torsion is active (unchanged otherwise). tests/test_hermite_torsion_dynamic.f90 gates the step response against the quasi-static torque, a parent turning 1.5 turns, energy conservation at rho_inf = 1, bit-identical snapshot and mirror restarts past one turn, and the bordered step; test_step_allocations gains a torsion mode.
A deck with torsional END CONNECTIONS at both ends of a line now runs dynamically on the standalone cubic-Hermite route and the multibody march (it needs the force-blended generalised-alpha; modal analysis and the coupled OpenFAST and FAST.Farm entries still refuse torsion by name). End A's torsion frame (-Ez, Nx) is attached to its parent, so a turning body or vessel turns it, also at a bending-pinned end. motionFile point rows take an optional 12th column on a deck with a torsional line: the roll of the line end frames at that point about Ez in degrees, added to the End A pretwist (Phi = Pretwist(B) - Pretwist(A) - roll), given on every row of a point or on none, starting at 0 and only at the End A point of a torsional line (named errors otherwise). Torsion has no inertia here, so the torque follows the roll at once. Range graphs of a torsional line add Torque and Twist (from End A) envelopes; other lines' range files are unchanged. tests/test_torsion_deck.f90 gains a roll-column step and a vessel rolling about the line axis (Twist = -roll, quasi-static torque at every step), the range envelopes, a body swinging as a torsional pendulum through two turns at sqrt(GJ / (L I)) with the connection moment along the axis, and the refusals of the configuration blend and of roll-column misuse.
The static body solve returned a line's torque as a fixed load each pass, which converged only when the body's own restoring stiffness was far above GJ/L. Each pass now also gives the body solve the torque's moment stiffness (1/C) a a^T about the previous pass's pose (a = dTheta/dw of the cable with its shape held: the End A frame turns, a clamped end's tangent with it). The own-load Jacobian carries it into the Newton step and the stability test, so the passes converge whatever the ratio of the body's restoring stiffness to GJ/L, to 1e-10 of the force scale. Gate 9 of tests/test_torsion_deck.f90: a buoyant body held by a vertical twisted cable with no yaw restoring at all yaws until the cable is untwisted, and with two legs whose yaw stiffness is of the order of GJ/L the two share the twist; Twist = 90 deg + yaw, and the static net moment on the body is below 1e-11 of the torque, in three passes.
DeckFile validates the optional END CONNECTIONS torsion columns (TorsStiffness NxX NxY NxZ [Pretwist]) with the native rules: 6, 10 or 11 columns, Free/Rigid or a non-negative stiffness, a non-zero reference normal not parallel to Ez, and the line rules of a torsional line (finite EI, Fixed End B, explicit GJ, no attachments, no rod end, a Rigid6 body), the force blend for a dynamic run, no modal analysis and no coupled route. The Torq<L>N<J>, Twist<L>N<J> and Twist<L> channels are recognised (one identity per quantity) and accepted only on a line restrained at both ends. DeckModel's EndConnection carries the optional torsion columns and writes them back, and add_end_connection takes them as keyword arguments. The parity tests pin each rule.
The Unreleased torsion entry now covers dynamics (quasi-static torsion with turning end frames and a motionFile roll column), the body torque in statics and dynamics, the range envelopes and the Python deck tools.
…h and the bordered step A new validation test checks zero-twist parity on a three-dimensional sagging line, a clamped four-turn Kirchhoff helix against its helical wrench, the clamped onset at T L^2/EI = 400 and 1600 against eq. (33), a bent and twisted rod against the Cosserat rod path, and the rank-one term of the bordered Newton step on post-buckled states (linear convergence at the predicted rate without it).
test_torsion_orcaflex checks pure torsion through the deck, the clamped and semi-tangential buckling onsets, and the Gulf of Mexico 80 m lazy-wave cable twisted up to five turns at the hang-off against OrcaFlex 11.6d summary values. validation/scripts/orcaflex_torsion_reference.py regenerates them (summary values and settings only).
… scope refusals The rolling-body gate also runs with a bending-pinned End A, and a rod end, a Point3 body and ATTACHMENTS on a torsional line stop by name.
read_output attaches N-m and deg to the Torq and Twist channels, read_range_graphs returns the torque and twist envelopes, and the torsion option check takes option records.
torsion_lazy_wave_hangoff_twist.dat twists the Gulf of Mexico 80 m cable two turns at the hang-off over 60 s and holds it; it runs as a CTest example and is catalogued.
Deck columns, explicit GJ, the motionFile roll column, the Torq and Twist channels and range columns, sign conventions, the condensed-torsion theory with its references, the semi-tangential pinned end, named errors and the scope limits.
…ibody route A body holding a line restrained in torsion had its moment rows scaled by its weight, so a heavy body with a small rotational inertia accepted moment residuals far above the line torque, and a trial turn above 180 deg aliased the line's twist by a whole turn. Its moment rows now scale with its rotational step stiffness and each trial turn stays inside the torsion step limit; the torsional pendulum follows the trapezoidal closed form at Omega dt = 2 and 5.
A static re-solve from a stored Theta stops by name when the untwisted state is more than 90 deg of twist away, and a failed solve leaves the stored Theta unchanged. The bordered step falls back to a dense solve on small lines and names an ill-conditioned failure; the inertia count rejects a non-square band and names an unreliable count; the stability test trusts a reliable count and scales the zero-mode band by EI/(EA Le^2). Newton and descent steps bound their linearised twist change before the trial. SetCoupledKinematics checks the turned torsion frame before writing anything, a restored mirror checks its frames and recovers the parent orientation, and the committed-state torque queries no longer allocate (now gated with CalcOutput). Test maxima are NaN-aware.
TorsStiffness takes Inf, Infinity, Rigid (also quoted), Free and Zero in the native reader as documented, and both readers parse the END CONNECTIONS stiffness columns as plain reals (no q exponent, finite, not subnormal). A Free end's reference normal is not checked, and one restrained end is ignored before the End B rule, so it no longer refuses a line with two moving ends. Python refuses torsion on a standalone mixed EI = 0 + finite-EI deck as the aggregate does, after the line rules; the refusal and the motionFile roll message name the deck's own ends, and a failed torsion stage is reported without a doubled prefix. The static retry starts from a fresh torsion state, the body pass loop passes the torque stiffness only with torsion, and the simple deck writer refuses torsion arguments.
A refused SetCoupledKinematics (and its preflight) leaves the cable unchanged, a mirror with non-orthonormal torsion frames is refused before any write, and a reload recovers a parent-attached frame's orientation. The deck gates add the Rigid/Infinity/Inf/quoted keywords, Free ends with an unused normal, a one-end row on a line with two moving ends (the six-column verdict) and a failed torsion stage named once.
The roll column is described in terms of the line's moving end, the Free-end normal and the keyword set as the reader takes them, the multibody 90-degree step limit and the refusal of torsion on mixed decks without a body. The validation record presents its cases without the planning labels and records the coarse-step pendulum; the changelog entry is shortened. The oracle script needs --out, the OrcaFlex script imports OrcFxAPI only to run OrcaFlex, and internal wording is removed from test data and docstrings.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds torsion to the cubic-Hermite finite-EI cable. The twist of a line is the parallel-transport twist of its centreline, condensed into one torque unknown per line, so the element keeps its 12 degrees of freedom and the banded solver its bandwidth. Torsion is active only on lines whose two ends both declare a torsional restraint; every other deck runs exactly as before.
What is included
END CONNECTIONScolumnsTorsStiffness NxX NxY NxZ [Pretwist](Rigid,Inf/Infinityor a finite stiffness). A torsional line needs an explicitGJin its line type.motionFile, torque returned to Rigid6 bodies in statics and dynamics, restart and snapshot state, no added allocation per step.Torq<L>N<J>,Twist<L>N<J>,Twist<L>and range-graph torque/twist columns; Python deck tools accept the new columns and channels.examples/torsion_lazy_wave_hangoff_twist.dat),VALIDATION.mdtorsion section and a changelog entry.Limits in this version (refused by name or documented)
No torsional inertia, no seabed friction resisting twist, no torque–tension coupling and no self-contact. Coupled OpenFAST/FAST.Farm runs, rod ends, Point3 bodies, lines with
ATTACHMENTS, modal analysis and the configuration force blend refuse torsional decks with a named error. A bending-pinned end with twist restrained behaves as a semi-tangential torque end (onset 4.9112877 EI/L, not the hinged 2π EI/L); the documentation says so.Validation
Checks
-fcheck=all, warning-free.dev.-W -nclean; pre-commit clean.