Skip to content

rework solve_step function#32

Merged
joelbenway merged 3 commits intomasterfrom
dev
Dec 29, 2025
Merged

rework solve_step function#32
joelbenway merged 3 commits intomasterfrom
dev

Conversation

@joelbenway
Copy link
Copy Markdown
Owner

@joelbenway joelbenway commented Dec 29, 2025

Summary by CodeRabbit

  • Documentation

    • Clarified step-size behavior: setting step size to zero makes the solver use a variable/internal step.
  • Refactor

    • Reorganized environmental and trajectory computations for clarity and consistency.
    • Simplified solver step-size handling and unified internal step computation.
  • Breaking Change

    • Removed public overloads that allowed callers to supply an external step; callers must now rely on the solver's internal step logic.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 29, 2025

📝 Walkthrough

Walkthrough

Removed external step parameters and consolidated step-size logic inside SolveStep; updated Solve to call the single SolveStep path unconditionally; refactored environmental computations to use typed temporaries; added documentation clarifying Builder::StepSize == 0 enables variable-step mode. (36 words)

Changes

Cohort / File(s) Summary
Documentation
include/lob/lob.hpp
Added note that Builder::StepSize == 0 causes the solver to use a variable step (documentation-only).
Step-sizing API & implementation
source/solve_step.hpp, source/solve_step.cpp
Removed overloads that accepted an external step; added single SolveStep(TrajectoryStateT*, SecT*, const Input&) that computes an internal kStep from input.step_size and current velocity; updated HeunStep and trajectory advance to use kStep; removed external-step assertions and helpers include.
Solve call-site simplification
source/lob_solve.cpp
Eliminated conditional branch that used in.step_size; now always calls SolveStep(&s, &t, in) (single control path).
Builder / environmental refactor
source/lob_builder.cpp
Replaced inline environmental expressions with typed temporaries (e.g., kAirDensity, kSpeedOfSound, kVelocity, kAngle); during zero-angle search save/restore step_size and temporarily set it to 0.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Solve as Solve (caller)
    participant SolveStep as SolveStep
    participant Integrator as HeunStep / Trajectory
    note right of SolveStep `#D6EAF8`: compute internal kStep = f(input.step_size, velocity)
    Solve->>SolveStep: SolveStep(&s, &t, in)
    SolveStep->>SolveStep: compute kStep (uses input.step_size, current velocity)
    SolveStep->>Integrator: HeunStep(state, kStep)
    Integrator-->>SolveStep: updated state/time
    SolveStep-->>Solve: return updated state/time
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Add error handling to Builder #25: Changes call sites to stop passing an external step and call SolveStep without a step — strongly related to the Solve call-site updates.
  • boatright spin drift #22: Introduces/modified SolveStep overloads and signatures — directly related/conflicting with this PR's removal of those overloads.

Poem

I nibble hops through tidy scope,
Steps chosen inward, neat and slow,
No extra args to tug or tote,
Named temps hum softly as I go,
A rabbit cheers the clearer flow! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'rework solve_step function' directly and accurately describes the main change: refactoring of the SolveStep function signature and internal logic across multiple files, removing step parameter overloads and relocating step size logic.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3f3139 and 6dc9906.

📒 Files selected for processing (1)
  • source/lob_builder.cpp
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: joelbenway
Repo: joelbenway/lob PR: 22
File: source/lob_builder.cpp:520-552
Timestamp: 2025-05-22T16:06:12.031Z
Learning: The `SolveStep` function in `solve_step.hpp` has an overload with a default argument of `FeetT(1)` for the step parameter: `void SolveStep(TrajectoryStateT* ps, SecT* pt, const Input& input, FeetT step = FeetT(1))`
Learnt from: joelbenway
Repo: joelbenway/lob PR: 25
File: include/lob/lob.hpp:123-130
Timestamp: 2025-06-17T12:55:26.522Z
Learning: In the lob ballistics library, when Input::step_size is set to 0 (the default), the solver automatically sets an appropriate step size internally rather than failing with an assertion error.
📚 Learning: 2025-04-15T13:11:22.842Z
Learnt from: joelbenway
Repo: joelbenway/lob PR: 15
File: source/calc.hpp:52-52
Timestamp: 2025-04-15T13:11:22.842Z
Learning: In lob, the `CalculateProjectileReferenceArea` function returns area in square inches (SqInT), but in some calculations like those in test/source/calc_test.cpp, this value is intentionally converted to square feet (SqFtT) when multiplied by air density in pounds per cubic foot (LbsPerCuFtT) to ensure proper unit cancellation.

Applied to files:

  • source/lob_builder.cpp
📚 Learning: 2025-05-22T16:06:12.031Z
Learnt from: joelbenway
Repo: joelbenway/lob PR: 22
File: source/lob_builder.cpp:520-552
Timestamp: 2025-05-22T16:06:12.031Z
Learning: The `SolveStep` function in `solve_step.hpp` has an overload with a default argument of `FeetT(1)` for the step parameter: `void SolveStep(TrajectoryStateT* ps, SecT* pt, const Input& input, FeetT step = FeetT(1))`

Applied to files:

  • source/lob_builder.cpp
🧬 Code graph analysis (1)
source/lob_builder.cpp (2)
source/calc.hpp (10)
  • CalculateWaterVaporSaturationPressure (74-89)
  • CalculateWaterVaporSaturationPressure (74-74)
  • CalculateAirDensityRatio (92-95)
  • CalculateAirDensityRatio (92-92)
  • CalculateAirDensityRatioHumidityCorrection (98-104)
  • CalculateAirDensityRatioHumidityCorrection (98-99)
  • CalculateSpeedOfSoundHumidityCorrection (107-113)
  • CalculateSpeedOfSoundHumidityCorrection (107-108)
  • CalculateSpeedOfSoundInAir (67-70)
  • CalculateSpeedOfSoundInAir (67-67)
source/solve_step.hpp (1)
  • SolveStep (13-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: test (ubuntu-latest, static)
  • GitHub Check: test (macos-latest, shared)
  • GitHub Check: test (ubuntu-latest, shared)
  • GitHub Check: test (windows-latest, shared)
  • GitHub Check: test (macos-latest, static)
  • GitHub Check: test (windows-latest, static)
  • GitHub Check: sanitize
  • GitHub Check: coverage
🔇 Additional comments (2)
source/lob_builder.cpp (2)

422-444: LGTM! Excellent refactoring with explicit typing.

The environmental calculations have been well refactored by introducing explicit typed constants (kWaterVaporSaturationPressureInHg, kAirDensityRatio, kHumidityCorrection, kAirDensity, kSpeedOfSoundCorrection, kSpeedOfSound). This improves both type safety and readability without changing the underlying logic.


772-791: Good refactoring adapting to the new SolveStep API.

The zero-angle computation has been well refactored with precomputed typed temporaries (kZeroAngle, kAngle, kVelocity) that improve readability. The step_size manipulation correctly implements variable-step mode by setting step_size = 0U during the computation and restoring it afterward.

As noted in the PR objectives, lines 783-784 and 791 currently lack test coverage. Consider adding tests that:

  1. Trigger zero-angle computation by setting a non-zero zero_distance_ft
  2. Verify the solver uses variable step size during the computation
  3. Confirm the original step size is restored after completion
Optional: Consider RAII for step_size restoration

While the current save/restore pattern is correct, you could optionally use RAII to guarantee restoration even if the loop is modified in the future:

struct StepSizeGuard {
  uint16_t& step_size_ref;
  uint16_t saved;
  StepSizeGuard(uint16_t& ref, uint16_t new_val) 
    : step_size_ref(ref), saved(ref) { 
    step_size_ref = new_val; 
  }
  ~StepSizeGuard() { step_size_ref = saved; }
};

// Then use:
StepSizeGuard guard(pimpl->build.step_size, 0U);

However, given the codebase style (using asserts rather than exceptions), the current approach is acceptable.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 47ceb6c and 2433ea8.

📒 Files selected for processing (5)
  • include/lob/lob.hpp
  • source/lob_builder.cpp
  • source/lob_solve.cpp
  • source/solve_step.cpp
  • source/solve_step.hpp
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: joelbenway
Repo: joelbenway/lob PR: 22
File: source/lob_builder.cpp:520-552
Timestamp: 2025-05-22T16:06:12.031Z
Learning: The `SolveStep` function in `solve_step.hpp` has an overload with a default argument of `FeetT(1)` for the step parameter: `void SolveStep(TrajectoryStateT* ps, SecT* pt, const Input& input, FeetT step = FeetT(1))`
📚 Learning: 2025-06-17T12:55:26.522Z
Learnt from: joelbenway
Repo: joelbenway/lob PR: 25
File: include/lob/lob.hpp:123-130
Timestamp: 2025-06-17T12:55:26.522Z
Learning: In the lob ballistics library, when Input::step_size is set to 0 (the default), the solver automatically sets an appropriate step size internally rather than failing with an assertion error.

Applied to files:

  • include/lob/lob.hpp
  • source/solve_step.cpp
  • source/lob_solve.cpp
  • source/lob_builder.cpp
  • source/solve_step.hpp
📚 Learning: 2025-05-22T16:06:12.031Z
Learnt from: joelbenway
Repo: joelbenway/lob PR: 22
File: source/lob_builder.cpp:520-552
Timestamp: 2025-05-22T16:06:12.031Z
Learning: The `SolveStep` function in `solve_step.hpp` has an overload with a default argument of `FeetT(1)` for the step parameter: `void SolveStep(TrajectoryStateT* ps, SecT* pt, const Input& input, FeetT step = FeetT(1))`

Applied to files:

  • source/solve_step.cpp
  • source/lob_solve.cpp
  • source/lob_builder.cpp
  • source/solve_step.hpp
📚 Learning: 2025-05-22T16:11:54.845Z
Learnt from: joelbenway
Repo: joelbenway/lob PR: 22
File: source/ode.hpp:66-81
Timestamp: 2025-05-22T16:11:54.845Z
Learning: The operator+ and operator* overloads in TrajectoryStateT that take a SecT parameter apply the time scalar to both position and velocity components. While this might appear to break dimensional correctness from a physics perspective, it's an intentional design for the numerical integration methods used in the codebase and is well unit tested.

Applied to files:

  • source/solve_step.cpp
  • source/lob_solve.cpp
  • source/solve_step.hpp
📚 Learning: 2025-04-15T13:11:22.842Z
Learnt from: joelbenway
Repo: joelbenway/lob PR: 15
File: source/calc.hpp:52-52
Timestamp: 2025-04-15T13:11:22.842Z
Learning: In lob, the `CalculateProjectileReferenceArea` function returns area in square inches (SqInT), but in some calculations like those in test/source/calc_test.cpp, this value is intentionally converted to square feet (SqFtT) when multiplied by air density in pounds per cubic foot (LbsPerCuFtT) to ensure proper unit cancellation.

Applied to files:

  • source/lob_builder.cpp
📚 Learning: 2025-05-22T16:12:16.630Z
Learnt from: joelbenway
Repo: joelbenway/lob PR: 22
File: test/source/boatright_test.cpp:58-65
Timestamp: 2025-05-22T16:12:16.630Z
Learning: In the lob codebase, std::pow and potentially other standard library functions have been overloaded to work directly with strong types (like InchT, CaliberT, etc.), allowing expressions like std::pow(kD / 2, 2) to compile correctly without needing to extract the underlying value first.

Applied to files:

  • source/lob_builder.cpp
🧬 Code graph analysis (3)
source/solve_step.cpp (1)
source/ode.hpp (2)
  • HeunStep (20-25)
  • HeunStep (20-20)
source/lob_solve.cpp (2)
source/solve_step.cpp (2)
  • SolveStep (18-58)
  • SolveStep (18-18)
source/solve_step.hpp (1)
  • SolveStep (13-13)
source/lob_builder.cpp (2)
source/calc.hpp (10)
  • CalculateWaterVaporSaturationPressure (74-89)
  • CalculateWaterVaporSaturationPressure (74-74)
  • CalculateAirDensityRatio (92-95)
  • CalculateAirDensityRatio (92-92)
  • CalculateAirDensityRatioHumidityCorrection (98-104)
  • CalculateAirDensityRatioHumidityCorrection (98-99)
  • CalculateSpeedOfSoundInAir (67-70)
  • CalculateSpeedOfSoundInAir (67-67)
  • CalculateSpeedOfSoundHumidityCorrection (107-113)
  • CalculateSpeedOfSoundHumidityCorrection (107-108)
source/eng_units.hpp (14)
  • LbsPerCuFtT (462-466)
  • LbsPerCuFtT (462-462)
  • FpsT (768-770)
  • FpsT (768-768)
  • FpsT (786-788)
  • FpsT (786-786)
  • FpsT (792-794)
  • FpsT (792-792)
  • FpsT (798-800)
  • FpsT (798-798)
  • sin (192-194)
  • sin (192-192)
  • sin (937-939)
  • sin (937-937)
🪛 GitHub Actions: Continuous Integration
source/solve_step.cpp

[error] 1-1: The following files are badly formatted: source/solve_step.cpp. Run again with FIX=YES to fix these files.

🔇 Additional comments (7)
source/solve_step.hpp (1)

13-13: LGTM! API simplification aligns with the PR objective.

The removal of the explicit step parameter overloads consolidates step-sizing logic within SolveStep itself. Based on learnings, this replaces the previous overload with default FeetT(1) step parameter.

include/lob/lob.hpp (1)

433-433: Good documentation addition.

This clarifies the behavior when step_size is set to zero, which aligns with the existing behavior noted in previous learnings.

source/lob_builder.cpp (2)

421-438: LGTM! Clean refactoring with explicit typed constants.

The introduction of named constants kWaterVaporSaturationPressureInHg, kAirDensity, and kSpeedOfSound with explicit types improves readability and type safety without changing functionality.


766-785: LGTM! Well-structured zero-angle computation with proper step size handling.

The refactoring introduces:

  • Named temporaries (kAngle, kVelocity) for clarity
  • Save/restore pattern for step_size that forces variable stepping during the binary search while preserving the user's selected step size for the final Solve call

This ensures the zero-angle calibration uses optimal stepping internally without affecting the user's configuration.

source/lob_solve.cpp (1)

89-89: LGTM! Simplified call site aligns with the refactored API.

The SolveStep call now relies on internal step computation, removing the conditional branching that previously existed at this call site. This centralizes step-sizing logic within SolveStep itself.

source/solve_step.cpp (2)

56-57: LGTM! Internal step usage is correct.

The HeunStep invocation now uses the internally computed kStep, and the time accumulation is consistent. This consolidates the step-sizing logic as intended by the refactor.


22-24: Edge case is handled by calling loop's exit conditions, not SolveStep itself.

When step_size == 0 and V().X() <= 0, kStep becomes zero. However, the solver loop in lob_solve.cpp exits before this occurs: the loop terminates when velocity drops below minimum_speed (lines 109-116), and when vertical velocity exceeds horizontal by more than 3x (line 118). This means V().X() will never reach zero or negative values during normal execution.

While SolveStep doesn't explicitly guard this edge case, it's implicitly protected by the caller's loop logic. The code works as designed when step_size == 0 uses adaptive stepping based on velocity, but relies on the calling context to prevent the pathological case. No change needed unless defensive programming inside SolveStep is desired.

Comment thread source/solve_step.cpp Outdated
Comment on lines +22 to +25
const SecT kStep = input.step_size == 0 && ps->V().X() > FpsT(0)
? SecT(ps->V().X().Inverse().Value())
: SecT(UsecT(input.step_size));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix formatting issue flagged by CI.

The pipeline failure indicates a formatting issue in this file. The trailing whitespace on line 25 needs to be removed.

🔎 Proposed fix
   const SecT kStep = input.step_size == 0 && ps->V().X() > FpsT(0)
                          ? SecT(ps->V().X().Inverse().Value())
                          : SecT(UsecT(input.step_size));
-  
+
   const CartesianT<FpsT> kWind(FpsT(input.wind.x), FpsT(0.0),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const SecT kStep = input.step_size == 0 && ps->V().X() > FpsT(0)
? SecT(ps->V().X().Inverse().Value())
: SecT(UsecT(input.step_size));
const SecT kStep = input.step_size == 0 && ps->V().X() > FpsT(0)
? SecT(ps->V().X().Inverse().Value())
: SecT(UsecT(input.step_size));
🤖 Prompt for AI Agents
In source/solve_step.cpp around lines 22 to 25, there is trailing whitespace at
the end of line 25 causing the CI formatting failure; remove the trailing
space(s) from that line so the line ends immediately after the semicolon and
re-run the formatter/checks.

@codecov
Copy link
Copy Markdown

codecov Bot commented Dec 29, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@joelbenway joelbenway closed this Dec 29, 2025
@joelbenway joelbenway reopened this Dec 29, 2025
@joelbenway
Copy link
Copy Markdown
Owner Author

Codecov Report

❌ Patch coverage is 90.47619% with 2 lines in your changes missing coverage. Please review.
Files with missing lines Patch % Lines
source/lob_builder.cpp 85.71% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai what is the issue?

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 29, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the diff for source/lob_builder.cpp to see what lines were changed
git diff origin/master...HEAD -- source/lob_builder.cpp

Length of output: 3040


@joelbenway The Codecov report indicates that 2 lines in source/lob_builder.cpp lack test coverage. Based on the changes in this PR, the missing coverage is most likely in the BuildZeroAngle function where you've introduced temporary step size adjustment:

const auto kSavedStepSize = pimpl->build.step_size;
pimpl->build.step_size = 0U;  // Use variable step size  ⚠️ Missing coverage
// ... SolveStep loop ...
pimpl->build.step_size = kSavedStepSize;  // Restore selected step size  ⚠️ Missing coverage

Why these lines lack coverage:

  • These lines are executed during the zero angle binary search computation
  • If your tests don't exercise the BuildZeroAngle function, or if they use configurations that skip this computation, these lines won't be covered

Recommendation:
Add test cases that:

  1. Trigger the zero angle computation by setting a non-zero zero distance
  2. Verify that the solver correctly uses variable step size during zero angle calculation
  3. Confirm that the original step size is properly restored after the computation

The coverage issue doesn't indicate a functional problem—it simply means these specific code paths aren't being exercised by your current test suite.

@joelbenway joelbenway merged commit d1fc2d8 into master Dec 29, 2025
12 checks passed
@joelbenway joelbenway deleted the dev branch December 29, 2025 16:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant