Skip to content

Implement nitrate leaching calculations for nitrogen balance#330

Merged
SvenVw merged 18 commits into
developmentfrom
FDM239
Nov 26, 2025
Merged

Implement nitrate leaching calculations for nitrogen balance#330
SvenVw merged 18 commits into
developmentfrom
FDM239

Conversation

@SvenVw
Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw commented Nov 6, 2025

Summary by CodeRabbit

  • New Features

    • Nitrogen balance now shows ammonia and nitrate emissions separately in charts, cards and headers.
    • Nitrate emission is calculated per field and included in farm totals.
  • UI

    • Labels, tooltips and layout updated to distinguish Ammoniakemissie (NH₃) and Nitraatemissie; balance layout expanded for the nitrate card.
  • Tests

    • Added and updated tests for nitrate emission logic and the new emission result shape.
  • Documentation

    • Expanded docs explaining nitrate leaching, emission formulas and updated farm-level reporting (balance now references ammonia emission).

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

Closes #239

@SvenVw SvenVw self-assigned this Nov 6, 2025
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Nov 6, 2025

🦋 Changeset detected

Latest commit: 648f143

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@svenvw/fdm-docs Minor
@svenvw/fdm-calculator Minor
@svenvw/fdm-app Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 6, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Restructures nitrogen emission from a scalar to an object { total, ammonia, nitrate }; implements nitrate leaching calculation driven by land type, soil type, and groundwater class; updates calculator types, tests, aggregation, docs, and UI components to display ammonia and nitrate separately.

Changes

Cohort / File(s) Change Summary
Changesets
.changeset/funny-loops-own.md, .changeset/giant-ghosts-bet.md, .changeset/spotty-clubs-cheer.md, .changeset/sunny-breads-prove.md, .changeset/full-lamps-stand.md
Add multiple changeset entries documenting minor bumps and notes about reporting ammonia and nitrate separately; docs/version notes added.
Types
fdm-calculator/src/balance/nitrogen/types.d.ts
Replace scalar emission types with object: { total: Decimal; ammonia: Decimal; nitrate: Decimal } and numeric equivalent.
Nitrate emission calc
fdm-calculator/src/balance/nitrogen/emission/nitrate/index.ts, fdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.ts
New calculateNitrogenEmissionViaNitrate(balance, cultivations, soilAnalysis, cultivationDetails) and determineNitrateLeachingFactor(...) implementing soil/GWL/land-type leaching factors; comprehensive tests added.
Emission aggregator
fdm-calculator/src/balance/nitrogen/index.ts, fdm-calculator/src/balance/nitrogen/emission/index.ts, fdm-calculator/src/balance/nitrogen/index.test.ts
Field and farm aggregation updated to compute and propagate emission.ammonia and emission.nitrate; emission.total recomputed as sum; tests updated to new shape.
Chart & details UI
fdm-app/app/components/blocks/balance/nitrogen-chart.tsx, fdm-app/app/components/blocks/balance/nitrogen-details.tsx
Chart and details components refactored to accept emission object and render separate ammonia and nitrate bars/cards; new nitrate Accordion item.
Metrics & sidebar UI
fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx, fdm-app/app/components/blocks/sidebar/apps.tsx
Labels/tooltips adjusted (e.g., "Emissie (NH₃)"); data paths updated to nested emission.ammonia; sidebar collapsible logic simplified for missing links.
Routes / pages
fdm-app/app/routes/.../balance/nitrogen._index.tsx, fdm-app/app/routes/.../balance/nitrogen.$b_id.tsx
Layout columns increased (4 → 5); "Emissie" renamed to "Ammoniakemissie"; new "Nitraatemissie" card added; pages now pass emission object to components.
Docs
fdm-docs/docs/insights/balance/01-nitrogen-balance.md
Documented nitrate leaching formula, conditional use when surplus > 0, leaching-factor lookup by land/soil/GWL and examples; ammonia emission sections expanded with fertilizer/residue notes.

Sequence Diagram(s)

sequenceDiagram
    participant UI as UI (routes / chart / details)
    participant Calc as fdm-calculator
    participant Data as Field/Farm Balance

    rect rgb(245,250,245)
    Note over UI,Calc: Request/response for nitrogen balance with split emissions
    UI->>Calc: calculateNitrogenBalance(inputs)
    Calc->>Calc: compute ammonia emission (existing logic)
    Calc->>Calc: balance := supply.total + removal.total + emission.ammonia.total
    Calc->>Calc: determineNitrateLeachingFactor(landType, soilType, gwl)
    alt balance > 0
        Calc->>Calc: nitrate = balance × factor
    else balance ≤ 0
        Calc->>Calc: nitrate = 0
    end
    Calc->>Data: return per-field & farm emission { total, ammonia, nitrate }
    Data->>UI: render Ammoniakemissie and Nitraatemissie (charts/cards)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • determineNitrateLeachingFactor branching and mappings for soil/GWL/land types.
    • Consistent propagation of the new emission object across calculator → types → UI.
    • Tests for nitrate scenarios and edge cases (balance ≤ 0, unknown soil/GWL errors).
    • Emission.total aggregation correctness and Decimal ↔ number conversions.

Possibly related PRs

Suggested reviewers

  • BoraIneviNMI
  • gerardhros

Poem

🐰 I counted soil and groundwater in a hush,
Ammonia hops forward while nitrate rush.
Factors and fields in a carrot's clear light,
Two tidy numbers where once was one sight.
Hop, compute, report — the balance is bright.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main objective: implementing nitrate leaching calculations. It directly reflects the primary change across multiple files (calculator logic, UI updates, and documentation).
Linked Issues check ✅ Passed All coding requirements from issue #239 are met: nitrate leaching calculation added to nitrogen balance, implemented at emission step, computed as balance × leaching factor, and calculation rules defined.
Out of Scope Changes check ✅ Passed All changes align with issue #239 objectives. UI updates (nitrogen-chart.tsx, nitrogen-details.tsx, metrics.tsx, route files) support the new nitrate emission feature; documentation explains the calculation; changesets document version bumps.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b79086d and 648f143.

📒 Files selected for processing (2)
  • .changeset/full-lamps-stand.md (1 hunks)
  • fdm-docs/docs/insights/balance/01-nitrogen-balance.md (2 hunks)

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.

@codecov
Copy link
Copy Markdown

codecov Bot commented Nov 6, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.59%. Comparing base (d79ac2b) to head (170d1f8).

Additional details and impacted files
@@               Coverage Diff               @@
##           development     #330      +/-   ##
===============================================
+ Coverage        87.34%   87.59%   +0.25%     
===============================================
  Files               79       79              
  Lines             3872     3951      +79     
  Branches          1099     1141      +42     
===============================================
+ Hits              3382     3461      +79     
  Misses             490      490              
Flag Coverage Δ
fdm-calculator 87.81% <100.00%> (+0.56%) ⬆️
fdm-core 87.03% <ø> (ø)
fdm-data 92.12% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@SvenVw SvenVw changed the title FDM239 @coderabbitai Nov 25, 2025
@SvenVw SvenVw marked this pull request as ready for review November 25, 2025 12:36
@coderabbitai coderabbitai Bot changed the title @coderabbitai Implement nitrate leaching calculations for nitrogen balance Nov 25, 2025
@coderabbitai coderabbitai Bot added branch:development Issue only affecting development, not the main branch (yet) enhancement New feature or request fdm-app fdm-calculator labels Nov 25, 2025
Copy link
Copy Markdown
Contributor

@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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
fdm-calculator/src/balance/nitrogen/emission/index.ts (1)

9-9: Remove unused import from emission/index.ts.

The import calculateNitrogenEmissionViaNitrate at line 9 is unused in this file. While the function is called elsewhere in the codebase, it's imported directly from ./emission/nitrate in nitrogen/index.ts (line 23), not through the emission module. Remove the unused import.

fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx (1)

397-402: Verify emission prop type compatibility with NitrogenBalanceChart.

The chart component now expects emission as an object with { total, ammonia, nitrate }. Here you're passing result.emission.total which is a number, but the chart expects the full emission object.

Based on the nitrogen-chart.tsx changes, the component expects the full emission object. Apply this fix:

                         <NitrogenBalanceChart
                             balance={result.balance}
                             supply={result.supply.total}
                             removal={result.removal.total}
-                            emission={result.emission.total}
+                            emission={result.emission}
                         />
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d79ac2b and 67f35d7.

📒 Files selected for processing (15)
  • .changeset/funny-loops-own.md (1 hunks)
  • .changeset/giant-ghosts-bet.md (1 hunks)
  • .changeset/spotty-clubs-cheer.md (1 hunks)
  • .changeset/sunny-breads-prove.md (1 hunks)
  • fdm-app/app/components/blocks/balance/nitrogen-chart.tsx (3 hunks)
  • fdm-app/app/components/blocks/balance/nitrogen-details.tsx (2 hunks)
  • fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx (3 hunks)
  • fdm-app/app/components/blocks/sidebar/apps.tsx (3 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx (4 hunks)
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx (4 hunks)
  • fdm-calculator/src/balance/nitrogen/emission/index.ts (2 hunks)
  • fdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.ts (1 hunks)
  • fdm-calculator/src/balance/nitrogen/emission/nitrate/index.ts (1 hunks)
  • fdm-calculator/src/balance/nitrogen/index.ts (7 hunks)
  • fdm-calculator/src/balance/nitrogen/types.d.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (17)
📓 Common learnings
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 236
File: fdm-calculator/src/balance/nitrogen/index.ts:173-0
Timestamp: 2025-08-14T14:31:55.384Z
Learning: In nitrogen balance calculations for agricultural systems, the balance should only include ammonia emissions (emission.ammonia.total) and should not include nitrate leaching from the emission calculation. The nitrate component (emission.nitrate) should be excluded from the balance formula.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:236-238
Timestamp: 2025-05-26T10:32:15.538Z
Learning: In nitrogen balance calculations for agricultural systems, removal and volatilization are calculated as negative values by definition since they represent nitrogen losses from the system. The balance calculation uses addition (.add()) for all components because removal and volatilization are already negative, so adding them effectively subtracts the losses from the supply.
📚 Learning: 2025-08-13T10:33:05.313Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.

Applied to files:

  • .changeset/spotty-clubs-cheer.md
  • .changeset/funny-loops-own.md
📚 Learning: 2025-08-14T14:31:55.384Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 236
File: fdm-calculator/src/balance/nitrogen/index.ts:173-0
Timestamp: 2025-08-14T14:31:55.384Z
Learning: In nitrogen balance calculations for agricultural systems, the balance should only include ammonia emissions (emission.ammonia.total) and should not include nitrate leaching from the emission calculation. The nitrate component (emission.nitrate) should be excluded from the balance formula.

Applied to files:

  • .changeset/spotty-clubs-cheer.md
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
  • .changeset/funny-loops-own.md
  • .changeset/sunny-breads-prove.md
  • fdm-app/app/components/blocks/balance/nitrogen-chart.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx
  • fdm-calculator/src/balance/nitrogen/types.d.ts
  • fdm-calculator/src/balance/nitrogen/emission/index.ts
  • .changeset/giant-ghosts-bet.md
  • fdm-app/app/components/blocks/balance/nitrogen-details.tsx
  • fdm-calculator/src/balance/nitrogen/emission/nitrate/index.ts
  • fdm-calculator/src/balance/nitrogen/index.ts
  • fdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.ts
📚 Learning: 2025-02-14T09:56:37.606Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 75
File: fdm-app/app/routes/farm.$b_id_farm.field.$b_id.fertilizer.tsx:68-71
Timestamp: 2025-02-14T09:56:37.606Z
Learning: The `calculateDose` function in `svenvw/fdm-calculator` is a synchronous function that includes built-in validation for negative application amounts and nutrient rates.

Applied to files:

  • .changeset/spotty-clubs-cheer.md
  • .changeset/funny-loops-own.md
📚 Learning: 2025-05-26T10:32:00.674Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:162-168
Timestamp: 2025-05-26T10:32:00.674Z
Learning: In the nitrogen balance calculation system (fdm-calculator), removal and volatilization values are negative by definition. This means the balance calculation using supply.total.add(removal.total).add(volatilization.total) is correct, as it effectively computes supply - |removal| - |volatilization|.

Applied to files:

  • .changeset/spotty-clubs-cheer.md
  • .changeset/funny-loops-own.md
  • fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx
  • fdm-calculator/src/balance/nitrogen/types.d.ts
  • fdm-calculator/src/balance/nitrogen/emission/index.ts
  • .changeset/giant-ghosts-bet.md
  • fdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-05-26T10:32:15.538Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:236-238
Timestamp: 2025-05-26T10:32:15.538Z
Learning: In nitrogen balance calculations for agricultural systems, removal and volatilization are calculated as negative values by definition since they represent nitrogen losses from the system. The balance calculation uses addition (.add()) for all components because removal and volatilization are already negative, so adding them effectively subtracts the losses from the supply.

Applied to files:

  • .changeset/spotty-clubs-cheer.md
  • .changeset/funny-loops-own.md
  • fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx
  • fdm-calculator/src/balance/nitrogen/types.d.ts
  • fdm-calculator/src/balance/nitrogen/emission/index.ts
  • fdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2025-07-21T12:06:07.070Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 156
File: fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts:295-303
Timestamp: 2025-07-21T12:06:07.070Z
Learning: Functions in the fdm-calculator with "NL2025" in their names are specifically designed for Netherlands 2025 agricultural norms calculation and hardcoded 2025 dates are appropriate in this context, as different years would have separate calculation modules.

Applied to files:

  • .changeset/spotty-clubs-cheer.md
📚 Learning: 2025-11-21T10:02:25.556Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 343
File: fdm-calculator/src/balance/organic-matter/types.d.ts:12-132
Timestamp: 2025-11-21T10:02:25.556Z
Learning: In the organic matter balance calculation system (fdm-calculator), degradation values are negative by definition. This means the balance calculation using supply.total.plus(degradation.total) is correct, as it effectively computes supply - |degradation|. This follows the same pattern as the nitrogen balance system.

Applied to files:

  • .changeset/spotty-clubs-cheer.md
  • .changeset/funny-loops-own.md
📚 Learning: 2025-03-04T10:56:35.540Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 88
File: fdm-calculator/src/doses/calculate-dose.ts:18-18
Timestamp: 2025-03-04T10:56:35.540Z
Learning: In the FDM calculator, fertilizer nutrient rates (p_n_rt, p_p_rt, p_k_rt) are measured in g/kg, and are converted to fractions by dividing by 10 during dose calculations.

Applied to files:

  • .changeset/spotty-clubs-cheer.md
📚 Learning: 2025-09-23T12:27:07.391Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:151-204
Timestamp: 2025-09-23T12:27:07.391Z
Learning: In the FDM application, field overview functionality is implemented as a dedicated page accessible via `farm/{farmId}/{calendar}/field` rather than as a direct listing on the dashboard. The dashboard includes a "Perceelsoverzicht" quick action card that provides navigation to this comprehensive field management interface.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
  • .changeset/giant-ghosts-bet.md
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A shared layout component `FarmLayoutBase` has been created in `components/custom/farm-layout-base.tsx` to maintain consistency across farm-related pages. The component handles farm selection dropdown, breadcrumb navigation, and provides a common layout structure.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
📚 Learning: 2025-09-23T12:29:34.184Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 274
File: fdm-app/app/routes/farm.$b_id_farm._index.tsx:160-163
Timestamp: 2025-09-23T12:29:34.184Z
Learning: In the FDM application, the fertilizer application route intentionally uses `${calendar}/field/fertilizer` instead of the originally planned `/farm/{farmId}/add/fertilizer` structure. This design decision prioritizes starting from the field list view to provide better field selection workflow before applying fertilizer, rather than direct dashboard-to-action navigation.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The farm layout system has been reorganized into separate components (`FarmHeader`, `ContentLayout`, `PaginationLayout`) to support different navigation patterns (sidebar, pagination) while maintaining consistent styling. Each layout component is designed to be used independently or combined as needed.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
📚 Learning: 2025-08-11T11:55:26.053Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 233
File: fdm-app/app/integrations/nmi.ts:54-0
Timestamp: 2025-08-11T11:55:26.053Z
Learning: The NMI API Estimates endpoint (`https://api.nmi-agro.nl/estimates`) always returns the fields `b_gwl_ghg`, `b_gwl_glg`, and `cultivations` according to its specification. These fields should be kept as required (not optional) in the TypeScript return type and Zod validation schema in `fdm-app/app/integrations/nmi.ts`.

Applied to files:

  • fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx
  • fdm-calculator/src/balance/nitrogen/emission/index.ts
  • fdm-calculator/src/balance/nitrogen/emission/nitrate/index.ts
📚 Learning: 2025-11-24T10:43:09.278Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 343
File: fdm-app/app/components/blocks/balance/organic-matter-chart.tsx:19-25
Timestamp: 2025-11-24T10:43:09.278Z
Learning: In the organic matter balance chart (fdm-app), degradation values are multiplied by -1 before plotting to show supply and degradation bars side-by-side with positive magnitudes, enabling direct visual comparison. This is a deliberate design choice for the organic matter chart visualization, different from the nitrogen balance chart which uses separate stacks.

Applied to files:

  • .changeset/sunny-breads-prove.md
📚 Learning: 2025-05-27T19:56:48.556Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 143
File: fdm-app/app/components/custom/balance/nitrogen-chart.tsx:73-85
Timestamp: 2025-05-27T19:56:48.556Z
Learning: In nitrogen balance charts, supply should be in a separate stack from removal and emission. Supply represents nitrogen inputs while removal and emission represent different types of nitrogen outputs, so they should be visually grouped differently using different stackId values.

Applied to files:

  • .changeset/sunny-breads-prove.md
  • fdm-app/app/components/blocks/balance/nitrogen-chart.tsx
  • fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx
  • fdm-calculator/src/balance/nitrogen/types.d.ts
  • fdm-calculator/src/balance/nitrogen/index.ts
📚 Learning: 2024-11-27T12:15:36.425Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 9
File: fdm-data/src/cultivations/index.test.ts:57-59
Timestamp: 2024-11-27T12:15:36.425Z
Learning: In `fdm-data/src/cultivations/index.test.ts`, the `fdm` object created by `drizzle` does not have an `.end()` method. Cleanup code should not attempt to call `fdm.end();`.

Applied to files:

  • fdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.ts
🧬 Code graph analysis (4)
fdm-app/app/components/blocks/balance/nitrogen-details.tsx (1)
fdm-calculator/src/balance/nitrogen/types.d.ts (1)
  • NitrogenEmissionNumeric (577-581)
fdm-calculator/src/balance/nitrogen/emission/nitrate/index.ts (1)
fdm-calculator/src/balance/nitrogen/types.d.ts (4)
  • FieldInput (417-437)
  • SoilAnalysisPicked (403-412)
  • CultivationDetail (442-451)
  • NitrogenEmissionNitrate (296-301)
fdm-calculator/src/balance/nitrogen/index.ts (1)
fdm-calculator/src/balance/nitrogen/emission/nitrate/index.ts (1)
  • calculateNitrogenEmissionViaNitrate (32-123)
fdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.ts (1)
fdm-calculator/src/balance/nitrogen/emission/nitrate/index.ts (2)
  • calculateNitrogenEmissionViaNitrate (32-123)
  • determineNitrateLeachingFactor (143-204)
🪛 GitHub Actions: Tests
fdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.ts

[error] 63-63: AssertionError: expected '-16' to be '16'. total should be 16 but got -16.


[error] 94-94: AssertionError: expected '-33' to be '33'. total should be 33 but got -33.


[error] 126-126: AssertionError: expected '-44' to be '44'. total should be 44 but got -44.


[error] 195-195: AssertionError: expected '-16' to be '16'. total should be 16 but got -16.

🪛 GitHub Check: calculator (24)
fdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.ts

[failure] 195-195: src/balance/nitrogen/emission/nitrate/index.test.ts > calculateNitrogenEmissionViaNitrate > should prioritize grassland over cropland
AssertionError: expected '-16' to be '16' // Object.is equality

Expected: "16"
Received: "-16"

❯ src/balance/nitrogen/emission/nitrate/index.test.ts:195:41


[failure] 126-126: src/balance/nitrogen/emission/nitrate/index.test.ts > calculateNitrogenEmissionViaNitrate > should return zero emission for bare soil
AssertionError: expected '-44' to be '44' // Object.is equality

Expected: "44"
Received: "-44"

❯ src/balance/nitrogen/emission/nitrate/index.test.ts:126:41


[failure] 94-94: src/balance/nitrogen/emission/nitrate/index.test.ts > calculateNitrogenEmissionViaNitrate > should calculate nitrate emission for cropland on clay soil
AssertionError: expected '-33' to be '33' // Object.is equality

Expected: "33"
Received: "-33"

❯ src/balance/nitrogen/emission/nitrate/index.test.ts:94:41


[failure] 63-63: src/balance/nitrogen/emission/nitrate/index.test.ts > calculateNitrogenEmissionViaNitrate > should calculate nitrate emission for grassland on sandy soil
AssertionError: expected '-16' to be '16' // Object.is equality

Expected: "16"
Received: "-16"

❯ src/balance/nitrogen/emission/nitrate/index.test.ts:63:41

🔇 Additional comments (26)
.changeset/funny-loops-own.md (1)

1-5: LGTM!

The changeset correctly documents the minor version bump for @svenvw/fdm-calculator with an accurate description of the emission structure change.

.changeset/giant-ghosts-bet.md (1)

1-5: LGTM!

The changeset correctly documents the minor version bump for @svenvw/fdm-app with an accurate description of the UI enhancement to display nitrate emissions.

.changeset/sunny-breads-prove.md (1)

1-5: LGTM!

The changeset correctly documents the minor version bump for @svenvw/fdm-app with an accurate description of the visualization improvements for separate ammonia and nitrate emission displays.

.changeset/spotty-clubs-cheer.md (1)

1-5: LGTM!

The changeset correctly documents the minor version bump for @svenvw/fdm-calculator with an accurate description of the nitrate emission calculation feature.

fdm-app/app/components/blocks/sidebar/apps.tsx (2)

116-116: Good conditional logic improvement.

Linking defaultOpen to the existence of nitrogenBalanceLink ensures the collapsible only opens when the balance section is accessible, improving the user experience.


156-156: Cleaner conditional rendering.

Returning null instead of rendering empty components when links are absent is more idiomatic and improves performance by avoiding unnecessary DOM nodes.

Also applies to: 170-170

fdm-app/app/components/blocks/fertilizer-applications/metrics.tsx (2)

400-400: Correct labeling with proper chemical notation.

The label and tooltip now correctly specify ammonia emissions with proper subscript notation (NH₃), improving clarity and scientific accuracy.

Also applies to: 410-410


419-420: Correct data path for restructured emission object.

Accessing emission.ammonia.total aligns with the new emission structure where ammonia and nitrate are separated. This is consistent with the calculator's updated API.

fdm-calculator/src/balance/nitrogen/emission/index.ts (2)

38-38: Correct: emission total excludes nitrate.

Setting emission.total to ammonia.total aligns with the learning that the nitrogen balance should only include ammonia emissions, not nitrate leaching. This is the correct implementation.

Based on learnings, the balance formula should exclude nitrate.


40-42: Appropriate placeholder for nitrate calculation.

Initializing nitrate as a placeholder with Decimal(0) is appropriate since, as the comment indicates, nitrate emission depends on the nitrogen surplus and will be calculated later in the balance flow.

fdm-app/app/components/blocks/balance/nitrogen-details.tsx (2)

404-417: Appropriate nitrate emission rendering.

The renderNitrateEmissions function follows the established pattern and correctly displays the nitrate total. The empty AccordionContent is appropriate since nitrate is calculated as a single factor-based value without detailed breakdowns.


587-589: Correct integration of nitrate emission display.

Adding a separate Accordion for nitrate emissions alongside ammonia maintains visual separation and consistency with the updated emission structure.

fdm-calculator/src/balance/nitrogen/types.d.ts (2)

380-384: Type structure correctly models the new emission breakdown.

The change from a single Decimal to an object with total, ammonia, and nitrate fields aligns well with the PR objective to add nitrate leaching calculation. This structure also supports the learning that the balance should only include ammonia emissions (via emission.ammonia) while keeping nitrate separate.


608-612: Numeric type mirrors the Decimal type correctly.

The NitrogenBalanceNumeric emission type correctly mirrors the NitrogenBalance type structure. Minor note: there's a trailing comma on line 610 after ammonia that isn't present on line 611 after nitrate - this is inconsistent but not a blocking issue.

fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen.$b_id.tsx (1)

351-382: Emission display correctly split into ammonia and nitrate cards.

The UI now properly displays ammonia and nitrate emissions separately with appropriate labels ("Ammoniakemissie" and "Nitraatemissie") and icons. This aligns with the learning that ammonia and nitrate should be treated distinctly in the nitrogen balance system.

fdm-app/app/routes/farm.$b_id_farm.$calendar.balance.nitrogen._index.tsx (1)

287-318: Correctly implements separate ammonia and nitrate emission display for farm-level view.

The emission cards properly access emission.ammonia and emission.nitrate from the NitrogenBalanceNumeric type where these are direct number values. The chart correctly receives the full emission object.

fdm-app/app/components/blocks/balance/nitrogen-chart.tsx (2)

21-35: Chart correctly updated to display separate ammonia and nitrate emission bars.

The emission prop type change and chart data transformation correctly support the new emission structure. The Math.abs() ensures emissions display as positive values in the chart regardless of their sign in the data.

Minor note: The undefined checks on lines 32-34 (emission.ammonia === undefined) are defensive but technically unnecessary since the type signature requires these fields. Consider simplifying if you prefer cleaner code, though keeping them is harmless.


81-104: Stacking configuration correctly groups emission bars with removal.

Both emissionAmmonia and emissionNitrate bars use stackId="b", which groups them with removal. This is correct per the established pattern that supply (stackId="a") should be visually separate from removal and emissions (stackId="b"). Based on learnings, supply represents nitrogen inputs while removal and emission represent different types of nitrogen outputs.

fdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.ts (1)

199-310: determineNitrateLeachingFactor tests look comprehensive.

The tests cover:

  • All soil types (peat, clay, loess, sandy)
  • All GWL classes for sandy soil
  • Both grassland and cropland land types
  • Error cases for unknown soil type, GWL class, and land type

These tests are well-structured and provide good coverage of the leaching factor logic.

fdm-calculator/src/balance/nitrogen/index.ts (4)

23-23: LGTM!

The import of calculateNitrogenEmissionViaNitrate is correctly added to support the new nitrate emission calculation.


235-248: Balance calculation correctly excludes nitrate emissions.

The implementation correctly calculates the balance using only ammonia emissions before computing nitrate leaching, which aligns with the nitrogen balance methodology. Based on learnings, "the balance should only include ammonia emissions (emission.ammonia.total)."

Minor: Line 240 has a typo – "Emssion" should be "Emission".


356-361: Average calculation logic is correct.

The division by totalFarmArea to compute per-hectare averages is appropriate. Once the accumulator bug above is fixed, these values will be accurate.


376-380: LGTM!

The emission structure correctly exposes total, ammonia, and nitrate components, aligning with the updated type definitions.

fdm-calculator/src/balance/nitrogen/emission/nitrate/index.ts (3)

32-99: Land type determination logic looks correct.

The function correctly:

  • Prioritizes grassland over cropland when both are present
  • Excludes specific bare soil crop codes from being classified as grassland/cropland
  • Falls back to "bare soil" when no qualifying cultivations exist

One observation: when hasGrassland is true, landType is set to "grassland" regardless of whether hasCropland is also true. This prioritization seems intentional for mixed-use fields.


143-204: Well-structured leaching factor determination with appropriate error handling.

The function correctly:

  • Groups soil types for easier maintenance
  • Handles GWL-dependent factors for sandy soils
  • Throws descriptive errors for unknown soil types and GWL classes

Note: The current implementation treats "bare soil" the same as "cropland" (higher leaching factors). This appears intentional as a conservative assumption.

Please verify that using cropland factors for "bare soil" is the intended behavior, or if bare soil should have its own leaching factors defined.


111-122: LGTM!

The emission calculation correctly:

  • Only applies when there's a nitrogen surplus (balance > 0)
  • Returns a negative value (multiplied by -1), consistent with the convention that emissions/losses are negative

Based on learnings, this aligns with the pattern where "removal and volatilization are calculated as negative values by definition since they represent nitrogen losses."

Comment thread fdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.ts Outdated
Comment thread fdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.ts Outdated
Comment thread fdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.ts
Comment thread fdm-calculator/src/balance/nitrogen/emission/nitrate/index.test.ts Outdated
Comment thread fdm-calculator/src/balance/nitrogen/index.ts Outdated
@SvenVw SvenVw requested a review from gerardhros November 25, 2025 13:11
Copy link
Copy Markdown
Contributor

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
fdm-docs/docs/insights/balance/01-nitrogen-balance.md (1)

280-298: Clarify farm balance formula to align with established nitrogen balance principles.

The documentation contains an inconsistency between field and farm level balance formulas. Line 282 correctly defines field balance as N Supply - N Removal - Ammonia Emission, excluding nitrate leaching. However, line 285 defines farm balance as N Supply - N Removal - Total N Emission, which would include both ammonia and nitrate.

This contradicts:

  • The overall balance formula (line 15): N Supply - N Removal - N-NH3 Emission (ammonia only)
  • The field balance definition (line 282): ammonia only
  • Established principles from PR 236: the balance should only include ammonia emissions and exclude nitrate leaching

The farm balance should remain consistent with the field balance calculation and use only ammonia emissions, not total emissions. Revise line 285:

-    2. These total farm-level amounts (in kg N) are then divided by the total farm area (ha) to provide an average farm-level balance (`N Supply - N Removal - Total N Emission`) in kg N / ha.
+    2. These total farm-level amounts (in kg N) are then divided by the total farm area (ha) to provide an average farm-level balance (`N Supply - N Removal - Ammonia Emission`) in kg N / ha.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e14b1fe and b79086d.

📒 Files selected for processing (2)
  • .changeset/full-lamps-stand.md (1 hunks)
  • fdm-docs/docs/insights/balance/01-nitrogen-balance.md (1 hunks)
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 236
File: fdm-calculator/src/balance/nitrogen/index.ts:173-0
Timestamp: 2025-08-14T14:31:55.384Z
Learning: In nitrogen balance calculations for agricultural systems, the balance should only include ammonia emissions (emission.ammonia.total) and should not include nitrate leaching from the emission calculation. The nitrate component (emission.nitrate) should be excluded from the balance formula.
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:236-238
Timestamp: 2025-05-26T10:32:15.538Z
Learning: In nitrogen balance calculations for agricultural systems, removal and volatilization are calculated as negative values by definition since they represent nitrogen losses from the system. The balance calculation uses addition (.add()) for all components because removal and volatilization are already negative, so adding them effectively subtracts the losses from the supply.
📚 Learning: 2025-08-14T14:31:55.384Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 236
File: fdm-calculator/src/balance/nitrogen/index.ts:173-0
Timestamp: 2025-08-14T14:31:55.384Z
Learning: In nitrogen balance calculations for agricultural systems, the balance should only include ammonia emissions (emission.ammonia.total) and should not include nitrate leaching from the emission calculation. The nitrate component (emission.nitrate) should be excluded from the balance formula.

Applied to files:

  • .changeset/full-lamps-stand.md
  • fdm-docs/docs/insights/balance/01-nitrogen-balance.md
📚 Learning: 2025-03-04T10:56:35.540Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 88
File: fdm-calculator/src/doses/calculate-dose.ts:18-18
Timestamp: 2025-03-04T10:56:35.540Z
Learning: In the FDM calculator, fertilizer nutrient rates (p_n_rt, p_p_rt, p_k_rt) are measured in g/kg, and are converted to fractions by dividing by 10 during dose calculations.

Applied to files:

  • .changeset/full-lamps-stand.md
  • fdm-docs/docs/insights/balance/01-nitrogen-balance.md
📚 Learning: 2025-02-14T09:56:37.606Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 75
File: fdm-app/app/routes/farm.$b_id_farm.field.$b_id.fertilizer.tsx:68-71
Timestamp: 2025-02-14T09:56:37.606Z
Learning: The `calculateDose` function in `svenvw/fdm-calculator` is a synchronous function that includes built-in validation for negative application amounts and nutrient rates.

Applied to files:

  • .changeset/full-lamps-stand.md
📚 Learning: 2025-05-26T10:32:15.538Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:236-238
Timestamp: 2025-05-26T10:32:15.538Z
Learning: In nitrogen balance calculations for agricultural systems, removal and volatilization are calculated as negative values by definition since they represent nitrogen losses from the system. The balance calculation uses addition (.add()) for all components because removal and volatilization are already negative, so adding them effectively subtracts the losses from the supply.

Applied to files:

  • fdm-docs/docs/insights/balance/01-nitrogen-balance.md
📚 Learning: 2025-05-26T10:32:00.674Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 134
File: fdm-calculator/src/balance/nitrogen/index.ts:162-168
Timestamp: 2025-05-26T10:32:00.674Z
Learning: In the nitrogen balance calculation system (fdm-calculator), removal and volatilization values are negative by definition. This means the balance calculation using supply.total.add(removal.total).add(volatilization.total) is correct, as it effectively computes supply - |removal| - |volatilization|.

Applied to files:

  • fdm-docs/docs/insights/balance/01-nitrogen-balance.md
📚 Learning: 2025-07-21T12:06:07.070Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 156
File: fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts:295-303
Timestamp: 2025-07-21T12:06:07.070Z
Learning: Functions in the fdm-calculator with "NL2025" in their names are specifically designed for Netherlands 2025 agricultural norms calculation and hardcoded 2025 dates are appropriate in this context, as different years would have separate calculation modules.

Applied to files:

  • fdm-docs/docs/insights/balance/01-nitrogen-balance.md
📚 Learning: 2025-11-21T10:02:25.556Z
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 343
File: fdm-calculator/src/balance/organic-matter/types.d.ts:12-132
Timestamp: 2025-11-21T10:02:25.556Z
Learning: In the organic matter balance calculation system (fdm-calculator), degradation values are negative by definition. This means the balance calculation using supply.total.plus(degradation.total) is correct, as it effectively computes supply - |degradation|. This follows the same pattern as the nitrogen balance system.

Applied to files:

  • fdm-docs/docs/insights/balance/01-nitrogen-balance.md

Comment thread .changeset/full-lamps-stand.md Outdated
SvenVw and others added 2 commits November 25, 2025 14:17
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
Copy link
Copy Markdown
Collaborator

@gerardhros gerardhros left a comment

Choose a reason for hiding this comment

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

LGTM. it might be that the leaching fractions will change since RIVM is updating these numbers. i'll ask for the final documentation.

@SvenVw SvenVw merged commit 2dd1132 into development Nov 26, 2025
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

branch:development Issue only affecting development, not the main branch (yet) enhancement New feature or request fdm-app fdm-calculator fdm-docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add calculation of nitrate leaching to nitrogen balance

2 participants