TASK-031: Velocity Field Support (Stage 5, all four subtasks) - #44
Merged
Conversation
… (Stage 5) Stage 5's second task, built in one branch per the roadmap's own instruction. Velocity is now something simulation.step() transports through the same Advection/Diffusion/TimeIntegrator path any scalar uses, rather than a fixed constant supplied from configuration. - (a) VectorField.decompose()/.assemble() (vector_field.py): one real ScalarField per component and back, named by a fixed convention (component_name). IncompatibleVelocityFieldError moved here from advection.py to avoid a circular import assemble()'s own rejection otherwise needed. - (b) CentralDifferenceDiffusion/assemble_numerics gain a coefficient_overrides parameter: a momentum component is diffused with fluid.viscosity, an ordinary scalar keeps fluid.diffusion_ coefficient -- dispatched by field.name, decided by bootstrap.py (the one place that legitimately knows a field is called "velocity"), not baked into the scheme or the assembler. - (c) DirichletBoundaryCondition/NeumannBoundaryCondition gain an overrides parameter, and BoundaryFaceConfig gains field_values/ field_gradients: two fields transported in one run can each get their own prescribed value at the same wall (u = U, v = 0 at a moving lid). Every existing call site is unaffected. - (d) bootstrap.py decomposes velocity around each step() call when simulation.velocity_solved is set, and reassembles it after -- simulation.py itself needed no change, since velocity's own components are just more entries in the fields mapping it already treats uniformly. velocity_solved is a separate SimulationConfig field, not a widened velocity_pattern -- the same switch-vs-configured-thing mistake this project already made once with show_mesh/grid_color and didn't want to repeat. Co-Authored-By: Claude Sonnet 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.
Summary
Stage 5's second task, built in one branch per the roadmap's own instruction ("meant to be done in one session"). Velocity is now something
simulation.step()transports through the sameAdvectionScheme/DiffusionScheme/TimeIntegratorpath any scalar uses, rather than a fixed constant supplied from configuration -- the field-centric claim Stage 6 (four more transported fields) depends on being real.Subtask (a) -- Velocity as component fields.
VectorField.decompose()/.assemble()(vector_field.py): one realScalarFieldper component and back, named by a fixed convention (component_name). Reassembly rejects a bad component count/mixed meshes with two new named errors.IncompatibleVelocityFieldErrormoved fromadvection.pytovector_field.py--assemble()'s own rejection needed the same class_check_velocityalready raises, and the reverse import would have been circular.Subtask (b) -- Viscosity, distinct from a scalar's diffusivity.
CentralDifferenceDiffusion/assemble_numericsgain acoefficient_overridesparameter: a momentum component is diffused withfluid.viscosity, an ordinary scalar keepsfluid.diffusion_coefficient-- dispatched byfield.nameinside the scheme, with which names get an override decided bybootstrap.py(the one place that legitimately knows a run's velocity field is called"velocity"), not baked into the scheme or the assembler.Subtask (c) -- Per-field boundary values at one wall.
DirichletBoundaryCondition/NeumannBoundaryConditiongain anoverridesparameter, andBoundaryFaceConfiggainsfield_values/field_gradients: two fields transported in one run can each get their own prescribed value at the same wall (u = U,v = 0at a moving lid). Exercised in the tests by two ordinary scalars, not a velocity pair -- the mechanism is general. Every existing call site passing only a single value is unaffected.Subtask (d) -- Velocity advanced by
step.bootstrap.pydecomposes velocity around eachstep()call whensimulation.velocity_solvedis set, and reassembles it after.simulation.pyitself needed no change -- velocity's own components are just more entries in thefieldsmapping it already treats uniformly; a scenario asserts the orchestrator's own source still carries no"velocity"string literal,isinstance(..., VectorField)check, or hardcoded component-name pair.velocity_solvedis a separateSimulationConfigfield, not a widenedvelocity_pattern-- the same switch-vs-configured-thing mistake this project already made once withRenderingConfig.show_mesh/grid_colorand didn't want to repeat.A real gap found while implementing (recorded honestly in the roadmap, not silently narrowed):
bootstrap.py's own live-loop wiring supportsvelocity_solvedonly alongside a configuredscalar_pattern-- a velocity-only live run has no rendering path yet (no per-frame vector-arrow display exists). The mechanism itself is proven directly againstsimulation.step()regardless; TASK-034's Lid-Driven Cavity is the likely first consumer of velocity-only live rendering.Test plan
make cigreen: 642 tests passing, mypy--strictclean, all doc/graph/scenario/status/config-template checks pass.tests/features/velocity_field_support.feature(13 scenarios, grouped by subtask) written and confirmed red before each subtask's implementation.test_bootstrap.pygained a real test proving the livebootstrap()loop (not onlysimulation.step()called directly) actually decomposes/steps/reassembles velocity -- found necessary by its own coverage report.make check-references/make check-manifest/make check-scenariosall clean against the new files.🤖 Generated with Claude Code