Skip to content

Conversation

@krystophny
Copy link
Member

@krystophny krystophny commented Aug 12, 2025

User description

Summary

  • Extracted helper functions for optional parameter initialization and second derivative calculations
  • Created reusable subroutines that reduce code duplication and improve maintainability
  • Added comprehensive unit tests following behavior-driven design principles

Changes Made

  • src/field_can.f90: Added helper functions and subroutines:
    • optional_or_default(): Generic function for handling optional parameters with defaults
    • compute_d2vpar(): Dedicated subroutine for second derivative calculation of vpar
    • compute_d2H(): Dedicated subroutine for second derivative calculation of H
    • compute_d2pth(): Dedicated subroutine for second derivative calculation of pth
  • test/tests/test_field_can_refactored.f90: Comprehensive unit tests covering all new functionality
  • test/tests/CMakeLists.txt: Updated build configuration for new test executable

Refactoring Details

  • Optional Parameter Pattern: Replaced 9 lines of repetitive if-present-else code blocks in FieldCan_init with 3 clean function calls
  • Second Derivative Calculations: Extracted ~30 lines of complex inline calculations into 3 focused subroutines
  • Improved Organization: Each helper function has a single responsibility, making the code easier to understand and maintain
  • Better Testability: Extracted functions can be tested in isolation

Test Plan

✅ All existing tests continue to pass
✅ New unit tests cover all helper functions
✅ Tests include Given-When-Then behavior-driven structure
✅ Edge case testing (zero inputs, boundary conditions)
✅ Name/ID conversion consistency validation
✅ Build succeeds with no errors

Addresses Issue #101

This PR completes Part 3 of the refactoring plan for field_can.f90:

  • ✅ Part 1: Comment formatting (already completed)
  • ✅ Part 2: Type modernization (already completed)
  • ✅ Part 3: Code structure improvements (completed in this PR)

🤖 Generated with Claude Code


PR Type

Enhancement


Description

  • Extracted helper functions for optional parameter initialization

  • Created dedicated subroutines for second derivative calculations

  • Added comprehensive unit tests with behavior-driven design

  • Reduced code duplication and improved maintainability


Diagram Walkthrough

flowchart LR
  A["Original FieldCan_init"] --> B["optional_or_default helper"]
  C["Inline d2 calculations"] --> D["compute_d2vpar subroutine"]
  C --> E["compute_d2H subroutine"]
  C --> F["compute_d2pth subroutine"]
  G["New unit tests"] --> H["Test coverage for all helpers"]
Loading

File Walkthrough

Relevant files
Enhancement
field_can.f90
Extract helper functions for cleaner code structure           

src/field_can.f90

  • Replaced repetitive optional parameter handling with
    optional_or_default helper
  • Extracted complex second derivative calculations into dedicated
    subroutines
  • Added compute_d2vpar, compute_d2H, and compute_d2pth helper functions
  • Simplified FieldCan_init and get_derivatives2 subroutines
+72/-39 
Tests
test_field_can_refactored.f90
Add comprehensive unit tests for refactored functions       

test/tests/test_field_can_refactored.f90

  • Added comprehensive unit tests for all new helper functions
  • Implemented behavior-driven test structure with Given-When-Then format
  • Included edge case testing and validation of name/ID conversions
  • Tests cover optional parameter handling and second derivative
    computations
+364/-0 
Configuration changes
CMakeLists.txt
Configure build for new unit tests                                             

test/tests/CMakeLists.txt

  • Added build configuration for new test executable
  • Linked test with simple library and added to test suite
+5/-0     

@qodo-code-review
Copy link

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

101 - Partially compliant

Compliant requirements:

  • Identify and eliminate code duplication in canonical field calculations
  • Extract common functionality into separate subroutines
  • Add comprehensive tests for field computation

Non-compliant requirements:

  • Improve variable naming and organization

Requires further human verification:

  • Verify numerical correctness against known baselines/physics expectations beyond unit tests
  • Confirm no performance regressions in real workloads
  • Ensure new tests cover edge cases across full module behavior (integration with existing routines)
⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible Division by Zero

Helper subroutines use inputs like ro0 and hph in denominators without explicit guarding; if these can be zero, results will be NaN/Inf. Validate callers guarantee non-zero or add checks.

subroutine compute_d2vpar(d2vpar, d2Aph, d2hph, dhph, dvpar, vpar, ro0, hph)
  real(dp), intent(out) :: d2vpar(6)
  real(dp), intent(in) :: d2Aph(6), d2hph(6), dhph(3), dvpar(3)
  real(dp), intent(in) :: vpar, ro0, hph

  d2vpar = -d2Aph/ro0 - d2hph*vpar
  d2vpar(1) = d2vpar(1) - 2d0*dhph(1)*dvpar(1)
  d2vpar(2) = d2vpar(2) - (dhph(1)*dvpar(2) + dhph(2)*dvpar(1))
  d2vpar(3) = d2vpar(3) - (dhph(1)*dvpar(3) + dhph(3)*dvpar(1))
  d2vpar(4) = d2vpar(4) - 2d0*dhph(2)*dvpar(2)
  d2vpar(5) = d2vpar(5) - (dhph(2)*dvpar(3) + dhph(3)*dvpar(2))
  d2vpar(6) = d2vpar(6) - 2d0*dhph(3)*dvpar(3)
  d2vpar = d2vpar/hph
Optional Helper Scope

optional_or_default is a module-level function with generic usage; ensure no name conflicts and confirm it’s declared pure if intended for functional style, or document side-effect expectations.

function optional_or_default(opt_val, default_val) result(val)
  real(dp), intent(in), optional :: opt_val
  real(dp), intent(in) :: default_val
  real(dp) :: val

  if (present(opt_val)) then
    val = opt_val
  else
    val = default_val
  end if
end function optional_or_default
Fragile "Reasonable Range" Assertions

Tests assert values are within arbitrary ranges (e.g., ±1000) which may fail under different scales. Consider computing expected values explicitly or using relative tolerances.

if (any(abs(d2vpar) > 1000.0_dp)) then
  test_passed = .false.
  print *, '  Failed: d2vpar values out of reasonable range'
end if

@qodo-code-review
Copy link

qodo-code-review bot commented Aug 12, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Avoid division by zero defaults

Guard against zero ro0 and hph before downstream divisions to prevent
division-by-zero. Initialize ro0 to a small positive default and add runtime
checks before using it in divisions.

src/field_can.f90 [166-168]

 f%mu = optional_or_default(mu, 0d0)
-f%ro0 = optional_or_default(ro0, 0d0)
+f%ro0 = optional_or_default(ro0, 1d-12)
 f%vpar = optional_or_default(vpar, 0d0)
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies that ro0 is initialized with a default of 0d0 and later used as a divisor, which would cause a runtime error. Changing the default to a small non-zero value is a valid and important fix.

Medium
Check denominators before division

Add explicit checks to avoid dividing by zero or near-zero ro0 and hph. Return
zeros or raise a controlled error when denominators are unsafe to prevent NaNs.

src/field_can.f90 [259-272]

 subroutine compute_d2vpar(d2vpar, d2Aph, d2hph, dhph, dvpar, vpar, ro0, hph)
   real(dp), intent(out) :: d2vpar(6)
   real(dp), intent(in) :: d2Aph(6), d2hph(6), dhph(3), dvpar(3)
   real(dp), intent(in) :: vpar, ro0, hph
+  real(dp), parameter :: eps = 1d-12
+  
+  if (abs(ro0) < eps .or. abs(hph) < eps) then
+    d2vpar = 0d0
+    return
+  end if
   
   d2vpar = -d2Aph/ro0 - d2hph*vpar
   d2vpar(1) = d2vpar(1) - 2d0*dhph(1)*dvpar(1)
   d2vpar(2) = d2vpar(2) - (dhph(1)*dvpar(2) + dhph(2)*dvpar(1))
   d2vpar(3) = d2vpar(3) - (dhph(1)*dvpar(3) + dhph(3)*dvpar(1))
   d2vpar(4) = d2vpar(4) - 2d0*dhph(2)*dvpar(2)
   d2vpar(5) = d2vpar(5) - (dhph(2)*dvpar(3) + dhph(3)*dvpar(2))
   d2vpar(6) = d2vpar(6) - 2d0*dhph(3)*dvpar(3)
   d2vpar = d2vpar/hph
 end subroutine compute_d2vpar
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies that the new compute_d2vpar subroutine is vulnerable to division-by-zero errors from ro0 or hph. Adding a guard clause makes the subroutine more robust and prevents potential runtime crashes.

Medium
  • Update

## Summary
- Extracted helper functions for optional parameter initialization
- Created reusable subroutines for second derivative calculations
- Added comprehensive unit tests with behavior-driven design
- Reduced code duplication and improved maintainability

## Changes Made
- **src/field_can.f90**: Added helper functions and subroutines:
  - optional_or_default(): Generic optional parameter handling
  - compute_d2vpar(): Second derivative calculation for vpar
  - compute_d2H(): Second derivative calculation for H
  - compute_d2pth(): Second derivative calculation for pth
- **test/tests/test_field_can_refactored.f90**: Comprehensive unit tests
- **test/tests/CMakeLists.txt**: Updated build configuration

## Refactoring Details
- Replaced repetitive optional parameter handling with clean helper function
- Extracted complex second derivative calculations into dedicated subroutines
- Each helper function is focused on a single responsibility
- Improved code readability and reduced potential for errors

## Test Coverage
- Optional parameter helper function validation
- FieldCan initialization with various parameter combinations
- Name/ID conversion consistency checks
- Second derivative computation testing with edge cases
- All tests pass locally

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@krystophny krystophny force-pushed the phase3/field-can-refactor-101 branch from f9f8391 to b01cec9 Compare August 12, 2025 20:17
@krystophny
Copy link
Member Author

Closing PR per request. Code changes did not meet requirements.

@krystophny krystophny closed this Aug 12, 2025
@krystophny krystophny deleted the phase3/field-can-refactor-101 branch August 12, 2025 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants