stability_derivatives gives roll, pitch and yaw rate derivatives about solver.reference_point - #356
Conversation
…y-about-the-origin' into agent/345-stability-derivatives-gives-roll-pitch-a # Conflicts: # CHANGELOG.md # src/body_aerodynamics.jl
The omega columns of linearize, scaled to p̂ = pb/2V, q̂ = q c_ref/2V and r̂ = rb/2V, with the body turning about solver.reference_point. trim_angle's bracketing solves turn about the same point. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1-Bort-1
left a comment
There was a problem hiding this comment.
Independent review (advisory)
Verdict: APPROVE WITH COMMENTS · 2 inline, 0 off the diff
Good
- The rate columns follow the plan in the card:
linearizeruns over[va; omega]withomega_idxs=4:6, and columns 4..6 are scaled by 2V/b, 2V/c_ref and 2V/b (src/stability.jl:18-30). - The pivot reaches both AD paths: FiniteDiff calls
set_va!onbody_aeroitself, which keeps its storedreference_point, andmake_dual_shadowcopiesreference_pointinto the dual body (checked inlinearizeat src/solver.jl:1297-1320). - The rate test checks against central differences of
solve!about the same pivot, with a guard that no column is zero, so a missing or mis-scaled column fails it. - The trim regression test solves again at the returned alpha about the reference point and checks CMy against the slope, which pins the pivot bug the card describes.
set_va!now has one inflow formula,va - omega × (control_point - reference_point), and areference_pointsetter; the oldall(iszero, omega)branch and the per-wing index loop are gone.- The card says why c_ref and not S/b is used for q̂, and names the difference from Python.
- The changed src and test files have no line over 92 characters (grep for
.{93,}).
Not good
src/stability.jl:17—stability_derivativespermanently overwritesbody_aero.reference_pointwithsolver.reference_point, and only the test says so; the docstring says 'leavesbody_aeroat this inflow'. A caller who set their own pivot will see laterset_va!/reinit!calls turn about a different point with no warning.test/solver/test_stability.jl:64—coeffs_at_rateis a nested closure with logic that nearly repeats the file's top-levelcoeffs_athelper (set_va!, solve!, stack coefficients). Extendingcoeffs_atwithomega/reference_pointkeywords gives one helper and follows §2 and §6.- The card does not say what was searched for before adding the rate scaling and the new test helper (§2).
- The
stability_derivativesdocstring sentence 'dp, dq, dr with respect to the body rates about x, y and z as p̂ = …' is hard to parse; 'with respect to p̂ = pb/2V, q̂ = …, r̂ = …' would say it directly. - V is
wind_speedin the p̂/q̂/r̂ scaling, but the coefficients are normalised by the reference speed built from the panel inflow. The card flags this as a risk, but neither the docstring nor the CHANGELOG mentions it. pitch_moment_coeffhas the same pivot-writing side effect asstability_derivatives, so everythingtrim_angletouches changesbody_aero.reference_pointtoo.- The ForwardDiff testset builds a new
BodyAerodynamics([wing])on every iteration whilebody_aeroalready exists. This is cheap, but it shareswingwithbody_aerowithout saying so. - In the ForwardDiff test,
turnsreuses the outeromegafor its first entry and then names the loop variableomega_op, so two omegas are in scope.
claude, rubric CLEAN_CODE.md. A different lab from the implementer
on purpose: a reviewer sharing its blind spots would not flag its mistakes.
| va = apparent_wind(alpha, beta, wind_speed) | ||
| jac, results, converged = linearize(solver, body_aero, va; | ||
| theta_idxs=nothing, va_idxs=1:3, aero_coeffs=true, kwargs...) | ||
| set_va!(body_aero, va, body_aero.omega; reference_point=solver.reference_point) |
There was a problem hiding this comment.
MINOR: stability_derivatives permanently overwrites body_aero.reference_point with solver.reference_point, and only the test says so; the docstring says 'leaves body_aero at this inflow'. A caller who set their own pivot will see later set_va!/reinit! calls turn about a different point with no warning.
There was a problem hiding this comment.
Kept the overwrite: the rates are only the rate derivatives when the body turns about the moment point. The docstrings of stability_derivatives, trim_angle and coeffs_at_angles now say they leave body_aero.reference_point = solver.reference_point, in 953b2d1.
| @test derivatives.converged | ||
| @test body_aero.reference_point == reference_point | ||
|
|
||
| function coeffs_at_rate(rate) |
There was a problem hiding this comment.
MINOR: coeffs_at_rate is a nested closure with logic that nearly repeats the file's top-level coeffs_at helper (set_va!, solve!, stack coefficients). Extending coeffs_at with omega/reference_point keywords gives one helper and follows §2 and §6.
There was a problem hiding this comment.
Removed coeffs_at_rate: the test now steps body_aero.omega and calls coeffs_at_angles, the helper that replaced coeffs_at, in 953b2d1.
|
Local full suite: PASS (6 min, Julia 1.13.0, one cell of the matrix) |
…agent/345-stability-derivatives-gives-roll-pitch-a Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…y-about-the-origin' into agent/345-stability-derivatives-gives-roll-pitch-a # Conflicts: # CHANGELOG.md # src/body_aerodynamics.jl
…ty-derivatives-and' into agent/345-stability-derivatives-gives-roll-pitch-a # Conflicts: # src/body_aerodynamics.jl # src/stability.jl # test/solver/test_stability.jl
…ch-a Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Resolve conflicts and address review comments |
…he rates The docstrings of stability_derivatives, trim_angle and coeffs_at_angles now say they store solver.reference_point on body_aero, and that V in p̂, q̂, r̂ is va rather than the panel inflow speed the coefficients are normalised by. The rate test steps body_aero.omega and reuses coeffs_at_angles instead of its own closure. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Conflicts with main (#346, #368 renames) resolved in 1a30832. Review 5229755456 addressed in 953b2d1: docstrings state the stored pivot and that V is va rather than the panel inflow speed, the rate test reuses coeffs_at_angles, dp/dq/dr wording rewritten. The two ForwardDiff-test points are about test_forwarddiff.jl, which came in with #353 and is not in this diff; noted in the description. |
TL;DR
stability_derivativesnow also returnsdp,dqanddr: the derivatives of CFx..CMz with respect to p̂ = pb/2V, q̂ = q c_ref/2V and r̂ = rb/2V, taken fromlinearize's omega columns with the body turning aboutsolver.reference_point. Rate derivatives are only meaningful when the body turns about the same point the moments are taken about, which #353 made possible.What changed
stability_derivativesstoressolver.reference_pointas the pivot onbody_aero(set_va!(...; reference_point)), linearises in[va; omega]atbody_aero.omega, and scales the omega columns by 2V/b, 2V/c_ref and 2V/b, with b the wing span and V the apparent wind speedva.stability_derivatives,trim_angleandcoeffs_at_anglesleavebody_aero.reference_point = solver.reference_point, and their docstrings say so: the omega columns are only rate derivatives when the body turns about the point the moments are taken about, so a separate pivot onbody_aerocannot be kept through the call.coeffs_at_angles, whichtrim_anglebrackets with, turns aboutsolver.reference_pointtoo. Before, a pitching body was trimmed about whatever pivotbody_aeroheld: withomega = [0, 0.5, 0]andreference_point = [1, 0, 0], the returned trim at α = −1.12° had CMy 3.9e-6 turning about the origin and −0.083 turning about the reference point. It is now 1.3e-5, inside the bisection tolerance.Choice of chord for q̂
The Python
compute_rigid_body_stability_derivativesnormalises q̂ with c = S/b while its moment coefficients use the max chord. Here q̂ usesbody_aero.c_ref(max panel chord), the same length the moment coefficients are divided by, so one chord runs through every pitch term. For a tapered or curved kitedqtherefore differs from Python's by the factor c_ref/(S/b).Searched before writing
rgforspan,c_ref,2V,omega_idxs,rate,pb/2andnondimensionalinsrc/andtest/: nothing scaled rates already, andlinearize'somega_idxscolumns were the only rate Jacobian. The rate test reusescoeffs_at_angles(steppingbody_aero.omega) rather than a helper of its own.Found on the way
test/solver/test_forwarddiff.jl's pivot testset (from set_va! turns the body about a stored reference_point, and set_va!(body_aero, settings) applies yaw_rate #353, now onmain, outside this diff) builds a newBodyAerodynamics([wing])per case sharingwingwithbody_aero, and hasomegaand loop variableomega_opin scope together. Test-only tidying; I would fold it into acleanup:PR rather than widen this one.Pivot matters
Same wing, α = 4°, β = 3°, reference point [0.25, 0.5, 0.1]: dCFz/dq (raw, per rad/s) is 0.1087 turning about the reference point against 0.1654 turning about the origin, and dCMx/dp is −0.510 against −0.456.
Stack
#346 and #353 have both merged, so this now targets
mainand the diff is onlysrc/stability.jl, its test and the changelog. Mergingmainafter #368 renamed the speed argumentwind_speed→vaand thelinearizekeywordva_idxs→va_vec_idxs:src/stability.jland CHANGELOG conflicted, resolved by keeping the rate derivatives and the pivot and taking #368's names; the new testsets were renamed the same way.Verification
trim_anglepivot bug was reproduced first: CMy −0.083 at the returned trimtest/solver/test_stability.jlred before, green after (juliaserver): new "rate derivatives match central differences of solve! about reference_point" errored on missingdp/dq/drand failed onbody_aero.reference_point == [0,0,0], now 6/6. Central differences are over omega = [0.1, −0.05, 0.08] ± 1e-4 at rtol 1e-4. New "pitching body: trim turning about the reference point" failed at0.0835 < 1e-5·|slope|, now inside 11/11 for the trim testset. The existing α/β testset passes 5/5mainat8754b6d(Add stability_derivatives (angle of attack, sideslip) and trim_angle, built on linearize #346, BREAKING: name the public apparent wind va, va_vec and va_dist, with no deprecation aliases #368): test_stability, test_forwarddiff, test_body_aerodynamics 4940/4940 pass on1a30832· docs build clean (docs/make.jl; only the existingfunctions.md/private_functions.mdsize warnings)953b2d1: test_stability 27/27, docs build clean · Local full suite started on1a30832: PASS (7 min, Julia 1.13.0, one matrix cell;953b2d1changes onlysrc/stability.jldocstrings andtest_stability.jl) · GitHub CI on953b2d1: PASS (Documentation, Julia 1.12 on ubuntu/macOS/windows, Julia 1.13 on ubuntu, setup test, codecov)dp/dq/drusevafor V while the coefficients are normalised by the area-weighted RMS panel inflow speed, so at nonzeroomegathe p̂ scaling and the dynamic pressure use slightly different speeds. The docstring now states it.Scope
+63 / −14 against
main, acrosssrc/stability.jl,test/solver/test_stability.jlandCHANGELOG.md. Stack: 2/2 after #346, which merged.Closes #345 · task
VortexStepMethod.jl-345