Conversation
The system definition of the plan's unit `package`: `SystemDefinition` over
`NamedCollection`s of `Point`, `TwistSurface`, `Segment`, `Pulley`, `Tether`,
`Winch`, `Body`, `Joint` and `Transform`, and `load_definition` reading one
from an authoring YAML file.
Copied from SymbolicAWEModels' `src/yaml_loader.jl`, `system_structure/types.jl`,
`named_collection.jl` and the definition half of its `SystemStructure`
constructor. SAM keeps its own until unit `sam` deletes it.
Changed on the way across: the joint merge (one `Joint{M}` over `ElasticJoint`
and `TimoshenkoBeam`), the segment and winch model type parameters that take
`unit_stiffness::Any` out, the frame suffixes, and the state cull — a
definition holds nothing that is written per simulation step.
Also the repository shape: BestieTemplate off KiteUtils' `.copier-answers.yml`,
the three `bin/` scripts, a tracked default manifest per CI Julia, REUSE, the
docs and the changelog.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XRzDnVRsMUvGW2psaAgtFC
`Pkg.test()` runs in `test/Project.toml`'s own environment, where the `using LinearAlgebra` that `test/test-components.jl` needs for `Diagonal` and `det` was not declared. It loaded under `--project=.` and failed under `Pkg.test()`, which is what CI runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XRzDnVRsMUvGW2psaAgtFC
Collaborator
Author
|
Local full suite: PASS (0 min, Julia 1.12.7, one cell of the matrix) |
1-Bort-1
commented
Sep 14, 2026
1-Bort-1
left a comment
Collaborator
Author
There was a problem hiding this comment.
Independent review (advisory)
Verdict: REQUEST CHANGES · 0 inline, 0 off the diff
Good
- Every exported symbol carries a
"""docstring and the docs pages are@autodocs(docs/src/types.md, functions.md, internals.md), so a new export cannot silently fall out of the built docs — checked make.jl leavescheckdocsat its erroring default. - The state cull is consistent: read every struct in src/types.jl and src/rigid_body.jl — no
vel,len,force, quaternion orsum_lenfield survives anywhere, so the package really does hold only topology, CAD geometry and material. - Type parameterisation is real, not decorative:
narrowre-types a homogeneous collection and test/test-joints.jl:30 assertseltype(beams.joints) == Joint{TimoshenkoBeam{SimFloat}}while a mixed set falls back toJoint. - Tests name the behaviour they protect and cover both fixture wings, the generated-tether chain, the explicit-segment tether, variable expansion, the joint blocks and the inertia diagonalisation, plus three error paths (
Unknown point name,requires body_a and body_b,No points or bodies). - test/runtests.jl copies the fixture into
mktempdir()beforeset_data_path, so no test can write into the trackeddata/2plate_kite. - Repo hygiene holds:
docs/build/is untracked,Manifest-v1.*.tomlignored while only.defaultis tracked, REUSE headers on every source file, and no line in src/ or test/ exceeds 92 characters (grepped).
Not good
- src/yaml_loader.jl:11 lists a
jointstop-level block; the loader actually readselastic_jointsandtimoshenko_joints(docs/src/yaml.md gets this right). - src/system_definition.jl:13 claims "Every collection is a type parameter"; four of the eleven are.
TwistSurface'sx_airf/y_airfkeywords are absent fromload_yaml_twist_surfaces' kwargs list, sochordandy_airfare unreachable from a YAML file.- The two tracked manifests differ in shape: the 1.12 default is workspace-wide (contains KiteGeometry and Documenter), the 1.11 default is root-only —
bin/installtherefore gives a different environment on the two pinned Julias. Wingbuilds aBody, so it lands on the "Exported functions" page; a reader looking for aWingtype will not find one.Point.anchor_bis mutable while the siblingpos_undeformed_bisconst, although both are filled by the downstream simulator.
claude, rubric CLEAN_CODE.md. A different lab from the implementer
on purpose: a reviewer sharing its blind spots would not flag its mistakes.
`load_definition` named a `joints` block the loader does not read; it reads `elastic_joints` and `timoshenko_joints`. `SystemDefinition` said every collection is a type parameter; four are. `Body` and `Wing` now point at each other, since `Wing` builds a `Body` and there is no `Wing` type to find. `Wing`'s nested `ref_pair` closure becomes the module-level `weighted_ref_pair`. No behaviour changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The README, the docs index and two docstrings said every collection mirrors a table of the awesIO structure document. Checked column by column against `structure_schema.yml` it does not: every block but the two joint blocks names at least one column differently. The field sets are SymbolicAWEModels' authoring columns, written by hand; the schema is the format they are to follow, and generating them from it is the next unit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment Thanks for integrating Codecov - We've got you covered ☂️ |
A body's own frame is KA and the world ENU, so the body-frame offsets become anchor_KA, com_offset_KA, pos_undeformed_KA and the joint's anchor_a_KA / anchor_b_KA, a body's rotation into the design frame R_KA_to_CAD (keyword Q_KA_to_CAD), and the transform's base position base_pos_ENU. YAML column names are unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
pos_cad is pos_CAD on Point and Body, field and keyword, matching R_KA_to_CAD. The YAML column stays pos_cad. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
TL;DR
SystemDefinitionand the ten component types it holds, plusload_definition, the authoring YAML loader — copied out of SymbolicAWEModels, with the segment/winch model type parameters, the simulation state and the frame suffixes (_KA,_ENU,_CAD) changed on the way. The repository around it is the fleet's: BestieTemplate off KiteUtils'.copier-answers.yml, the threebin/scripts, a tracked default manifest per CI Julia, REUSE, docs and a changelog.This is one unit of duplication, not a second codepath
src/yaml_loader.jl,src/system_structure/types.jl,named_collection.jland the resolution half of SAM'sSystemStructureconstructor now exist twice: there and here. That is deliberate and it is unitpackageofplans/topology_state_split_plan.md— unitsamdeletes SAM's copies and hasSystemStructurehold aSystemDefinitioninternally. Nothing in this repository reads SAM and nothing in SAM reads this yet, so until that unit lands the two do not have to be held in step; the definition wins wherever they have drifted by then.The field sets are a second, shorter-lived piece of hand-writing: unit
schema, next, generates them from awesIO'sstructure_schema.yml. What that unit will find is below, under "The field sets against the schema".What a definition holds, and what it does not
The split is one question asked of every field: is this written every simulation step? Everything that is went, and with it the loader lines that restored it — a point's
vel_w, a pulley'slen/vel/sum_len, a tether'slen, a winch'svel/set_value, a twist surface'stwist/twist_vel, and the whole ofload_body_state!.Pointcomes across with 24 fields rather than SAM's 34, andBodysheds its whole 6-DOF state block. The plan saysPointshould arrive with about ten; those 24 are exactly the list unitmirrornames — name, references,pos_CAD,pos_undeformed_KA,anchor_KA, mass, area, drag coefficient, damping, type, flags — counted one field at a time, with "references" being ten_ref/_idxfields and "flags" three.What is left is topology, CAD geometry, material and placement instructions, and the loader completes it: Route-2 tethers expand into points and segments, every name reference resolves to an index, rest lengths left unset come from the CAD geometry, densities left unset from the settings, and a body- or beam-anchored point gets its offset derived from
pos_CAD.The joint merge and the model parameters
elastic_jointsandtimoshenko_jointsare onejointscollection ofJoint{M<:AbstractJointModel}, withElasticJoint{S}carrying the four stiffnesses andTimoshenkoBeam{S}carryingEA,GA,GJ,EIy,EIzplusshear_coeffandrest_length. The two blocks still load from their own YAML names. The plan no longer asks for this merge — it says joints stay as #22 has them until unitschemareplaces them with tubes — so it stays rather than being split back into two types that unit would delete.unit_stiffness::Anyis gone from bothSegmentandTether, replaced bymodel::M<:AbstractSegmentModel—LinearSpringorNonlinearSpring{F}.Winchgains the same treatment withAbstractWinchModel. Loading the 2-plate kite givesSystemDefinition{Segment{LinearSpring}, Tether{LinearSpring}, Winch{TorqueWinch}, Joint}, concrete through every collection whose members share a model: a segment's stiffness is read every step and an abstract field costs a dynamic dispatch each time.The field sets against the schema
The columns here are SAM's authoring columns, because the test is that SAM's own files load. Checked one by one against
structure_schema.ymlon 1-Bart-1/awesIO#4's branch (1-Bart-1/awesIO@0784141), every block except the two joint blocks names at least one column differently, and a file written to the schema would not load today. None of that is guessed. It is SAM's spelling where the schema has since chosen another, and it is exactly what unitschemaexists to settle. The lines that decide something there:point_i/point_jforpoint_a/point_b,segment_i/segment_jforsegment_a/segment_b,segment_idxsandtether_idxsforsegmentsandtethers,twist_surfacesforstations, andposandaero_modelon a body forpos_cadandaero.diameter_mm[mm] where the schema'sdiameteris [m]. Transform angles are degrees in the file and radians in the struct; the schema has no transforms.apparent_masson bodies;model,gear_ratioanddrum_radiuson winches, which come fromSettingsinstead; themetadatablock.bodiestable, and a wing is a body whoseaerois not null. Here a wing is a row of a separatewingsblock, andSystemDefinitionholdsbodiesandwings.fix_*flags, pulleyfriction_epsilon, winch friction and inertia), the tether initial conditions, the wing's aero columns (drag_frac,group_points_moment, pointarea/drag_coeff), the frame-fittingz_ref_points/y_ref_points/origin, a body's orientation, andTransformas a whole. The schema carries no body orientation at all, so a body'sinertia_principalhas no frame to be read in.rigidityisnumber | model_name; here it is a number or a callable. A string in a stiffness cell is not rejected: a joint builds anElasticJoint{Union{Float64, String}}(checked in the session), and a segment hands any non-number toNonlinearSpringthe same way. That is a validation gap, and closing it is the same decision as Decide how a nonlinear stiffness law is represented 1-Bart-1/awesIO#1.Block by block
pointsPointbody/wingread asbody_idx/wing_idx; a schemaBODY_STATICpoint errors for want ofbody_idxsegmentsSegment{M}point_a/b↔point_i/j;diameter[m] ↔diameter_mmstationsTwistSurfacemoment_fracrequired there, defaults to 0 herepulleysPulleysegment_a/b↔segment_i/jtethersTether{M}segments↔segment_idxs; the schema forbids the start/end/n_segmentsexpansionwinchesWinch{M}tethers↔tether_idxs;model,gear_ratio,drum_radiusunreadbodiesBody, split overbodies+wingspos↔pos_cad;aero↔aero_model;apparent_massunread; no orientation column thereelastic_jointsJoint{ElasticJoint}timoshenko_jointsJoint{TimoshenkoBeam}TransformWhat I could not bring across cleanly
Wing frames, mass distribution and ENU placement stayed in SAM. SAM's
SystemStructureconstructor does two jobs in one pass — resolve the definition, then set the model up. Only the first half is here.setup_wing_frame!,setup_aero!,distribute_mass_over_points!,finalize_particle_wing_mass!,reinit!andbuild_panel_twist_surface_map!are not, because each of them either needs VortexStepMethod or writes world-frame state. The visible consequence: a body whose frame is fitted fromz_ref_points/y_ref_points/originarrives withmass = 0,R_KA_to_principal = I, and its riding points keepanchor_KA = 0—has_fitted_frameis where the loader stops, because there is no frame yet to express an anchor in. Unitsamkeeps doing all of it; unitviewerswill want the CAD→ENU placement and does not get it here.Aero models are a
Symbol, not a type parameter. The plan asks for a registry of name → constructor, as the loader already has for aero modes. But every one of SAM's six aero modes has exactly one field, aVSMEngine, which is simulation state — so by the plan's own test, do the field sets differ, there is nothing to parameterise, and the constructors would drag VortexStepMethod into a geometry package.Body.aero_model::Symbolrecords the name the file wrote (defaulting to:linearizedfor a rigid wing and:directfor a particle one, as SAM does),is_wingisaero_model !== :none, and SAM'sparse_aero_modemaps it back at unitsam. The schema'saerocolumn is a model name too, so this lines up with it.The frame suffixes are the ones set on 1-Bart-1/Agents#294:
_KAfor a body's own frame,_ENUfor the world,_CADfor design geometry. Most of SAM's_w/_bfields —pos_w,vel_w,Q_b_to_w,aero_force_b,va_b— are written every step, so the state cull removed them rather than renaming them. What survived is renamed, fields and constructor keywords alike:pos_CADon points and bodies,anchor_KA,com_offset_KAandpos_undeformed_KAon points and bodies,Transform.base_pos_ENU, and a body's rotationR_KA_to_CADwith the constructor keywordQ_KA_to_CAD. That last one is the CAD rotation, not the world one: SAM's YAML columnQ_b_to_wis stored into what SAM callsR_b_to_c, and the new test "a body's Q_b_to_w column is its rotation into the CAD frame" pins it — a body turned 90° about z in the file gives that rotation, and a point riding it gets its anchor through it. By the same rule, which the issue did not list by name:Joint.anchor_a_KA/anchor_b_KA(each in its body's frame) andBody.R_KA_to_principal. Left alone:Point.beam_offset_b, which is in the beam's element frame rather than a body's, and so has no suffix in that vocabulary yet — unitschemareplaces joints with tubes and decides it there. YAML column names are untouched throughout, includingpos_cad,anchor_b,com_offset_bandQ_b_to_w, because the test is that SAM's own files still load.pos_undeformed_KA's docstring still says principal frame, as SAM's does; a wing'sR_KA_to_principalis the identity, so for the one body it is read on they are the same frame.TabulatedSpringis not here. The plan names it as a segment model; SAM has exactly two cases, a number and a callableF(ε), which areLinearSpringandNonlinearSpring. A spring backed by a curve file would need an interpolation dependency and a column nothing writes yet, and how the schema names one is 1-Bart-1/awesIO#1.CascadedLengthWinchstayed in SAM. The YAML has nomodelcolumn, so the loader only ever builds aTorqueWinch; a second model here would be a type nobody constructs. It subtypesAbstractWinchModelat unitsamlike any custom model would.SimFloatandKVec3are defined here. KiteUtils has neither and SAM defines its own pair the same way, so there is no third place to depend on. Two spellings ofFloat64is thin duplication, and it closes when SAM reexports ours.A tether of explicit segments carries a
NaNmaterial. Its segments carry their own, butTether{M}needs anM, so it holdsLinearSpring(NaN, NaN). Said on the field; nothing reads it.The deprecation paths did not come across — the removed-block errors for
materials/elements/segment_properties, theSegmentTypeenum, theQUATERNION/REFINEaliases, thewing_typekeyword. A package with no earlier version has nothing to countermand. Also dropped: theprnkeyword and the@infoline the loader printed per transform, andignore_l0, which recomputes rest lengths from world positions and so needs the placement this package does not do.The review on this pull request
Fixed in 9b40f03: the loader docstring named a
jointsblock that does not exist,SystemDefinitionclaimed every collection is a type parameter,BodyandWingnow link each other, andWing's nestedref_pairclosure is the module-levelweighted_ref_pair. Fixed in ac6d33d, turned up by the schema check above: the README, the docs index and two docstrings said the types mirror the awesIO document one to one, which they do not yet.Left as they are, with the reason:
pos_undeformed_KAstaysconst.KVec3is anMVector, and SAM writes the field in place with.=, which works (checked in the session);anchor_KAis reassigned by the loader, so it is notconst.TwistSurface'sx_airf/y_airfare not YAML columns in SAM either, so leaving them out of the loader is a faithful copy. Adding them is a column the schema does not have.[workspace]arrived in Julia 1.12; 1.11 ignores it.Dependencies
KiteUtils(registered 0.12.2, forSettings,load_settingsandget_data_path),YAML,StaticArrays(KVec3),LinearAlgebra(eigenanddetfor the inertia diagonalisation,norm/dot/crossfor the beam element frame), andDocStringExtensionsfor$(TYPEDFIELDS), which is how the fleet writes field docs. Nothing else, and no[sources]: nothing here readsSysState, so KiteUtils #130's branch buys this unit nothing and would cost it a draft.Repository shape
Generated with BestieTemplate from KiteUtils'
.copier-answers.ymlwith the name, UUID, authors and copyright holder changed, then the fleet's additions that are not in the template: the REUSE files and thereuse-lintworkflow, CompatHelper, dependabot, andbin/install,bin/update_default_manifests,bin/run_julia,bin/reuse_lint. Three deliberate differences from KiteUtils:.defaultfor each. KiteUtils' matrix also names 1.10 but tracks no manifest for it, and a matrix cell with no tracked manifest is a cell that cannot install from one.fail-fast: true, not the template'sfalse(AGENTS.md§4.10).@autodocspages rather than hand-listed@docsblocks, so an added docstring cannot silently fall out of the built docs.The fixture is SAM's
data/2plate_kiteminusaero_geometry.yaml,vsm_settings.yamlandpolars/: those describe the aerodynamic solve, which this package does not do, and nothing here reads them.Verification
Unknown point name: nothing, from a tether of explicit segments naming no start point, caught by the test written for that path (test/test-yaml-loader.jl, "a tether of explicit segments takes their endpoints").test/test-joints.jl"a body's Q_b_to_w column is its rotation into the CAD frame" red onac6d33d(FieldError: type Body has no field R_KA_to_CAD), green on2d9d167(juliaserver). Thepos_CADrename likewise:test/test-components.jlred on5977e25(UndefKeywordError: keyword argument pos_cad not assigned), green on2d9d167.test/runtests.jlon2d9d167: 218 pass, 0 fail, 0 error on Julia 1.12.7 (juliaserver); clean on 1.11 in an earlier round, not re-run.agent ci-local,Pkg.test()on Julia 1.12.7, fail-fast) on2d9d167: PASS in under a minute, 218 tests, exit 0.2d9d167: Test on PRs (1.12, ubuntu), Documentation and reuse-lint all PASS. The 1.11 and macOS/Windows cells run onmainonly.2d9d167(docs/make.jl, nomissing_docs, no broken cross-references) · up to date withorigin/main(6f7a0a7).reuseis not installed on this box. Thereuse-lintworkflow passed on2d9d167.jetls check— not run;jetlsis not installed on this box.samhas to keep fitting it. If that line is in the wrong place, it is cheaper to move now than after the five units that build on this.Scope
+6409 / −10 across 61 files on an empty repository, of which about 2780 lines are
src/, 1572 the two tracked manifests, 585 tests, 344 the fixture, docs and the two licence texts most of the rest. Thesrc/total replaces roughly 3500 lines of SAM: the loader is 873 against 1093,types.jl724 against 1406, andNamedCollection85 against 180 — most of that from lettingAbstractVectorsupply the iteration and indexing SAM hand-wrote. The last two commits,5977e25and2d9d167, are +113 / −94: the frame-suffix renames (one-for-one lines) plus the 18-line test and the docs' frames section. Unitpackageofplans/topology_state_split_plan.md, tracked in 1-Bart-1/Agents#294;schema,document,sam,kitemodelsandviewersbuild on this.Closes #21 · task
KiteGeometry.jl-21