diff --git a/fdm-calculator/CHANGELOG.md b/fdm-calculator/CHANGELOG.md index 2925c69a6..0242e3290 100644 --- a/fdm-calculator/CHANGELOG.md +++ b/fdm-calculator/CHANGELOG.md @@ -1,5 +1,11 @@ # fdm-calculator +## 0.6.1 + +### Patch Changes + +- a9acf19: Fix divide-by-zero in nitrogen balance when b_lu_hi is undefined or 0 + ## 0.6.0 ### Minor Changes diff --git a/fdm-calculator/package.json b/fdm-calculator/package.json index a8124aa51..b24fc9b1d 100644 --- a/fdm-calculator/package.json +++ b/fdm-calculator/package.json @@ -1,7 +1,7 @@ { "name": "@svenvw/fdm-calculator", "private": false, - "version": "0.6.0", + "version": "0.6.1", "description": "Calculate various insights based on the Farm Data Model", "license": "MIT", "homepage": "https://github.com/SvenVw/fdm", diff --git a/fdm-calculator/src/balance/nitrogen/emission/ammonia/residues.test.ts b/fdm-calculator/src/balance/nitrogen/emission/ammonia/residues.test.ts index c8e356235..ccf294294 100644 --- a/fdm-calculator/src/balance/nitrogen/emission/ammonia/residues.test.ts +++ b/fdm-calculator/src/balance/nitrogen/emission/ammonia/residues.test.ts @@ -258,4 +258,58 @@ describe("calculateNitrogenEmissionViaAmmoniaByResidues", () => { { id: "cultivation1", value: expect.any(Decimal) }, ]) }) + + it("should return 0 when b_lu_hi is 0", () => { + const cultivations: FieldInput["cultivations"] = [ + { + b_lu: "cultivation1", + b_lu_catalogue: "catalogue1", + b_lu_start: new Date("2022-01-01"), + b_lu_end: new Date("2022-12-31"), + m_cropresidue: true, + }, + ] + const harvests: FieldInput["harvests"] = [ + { + b_id_harvesting: "harvest1", + b_lu: "cultivation1", + b_lu_harvest_date: new Date(), + harvestable: { + b_id_harvestable: "harvestable1", + harvestable_analyses: [ + { + b_lu_yield: 1000, + b_lu_n_harvestable: 20, + }, + ], + }, + }, + ] + const cultivationDetailsMap = new Map([ + [ + "catalogue1", + { + b_lu_catalogue: "catalogue1", + b_lu_croprotation: "cereal", + b_lu_yield: 1000, + b_lu_n_harvestable: 20, + b_lu_hi: 0, + b_lu_n_residue: 14, + b_n_fixation: 0, + }, + ], + ]) + + const result = calculateNitrogenEmissionViaAmmoniaByResidues( + cultivations, + harvests, + cultivationDetailsMap, + ) + + //Check for approximation due to floating point + expect(result.total.equals(new Decimal(0))).toBe(true) + expect(result.cultivations).toEqual([ + { id: "cultivation1", value: new Decimal(0) }, + ]) + }) }) diff --git a/fdm-calculator/src/balance/nitrogen/emission/ammonia/residues.ts b/fdm-calculator/src/balance/nitrogen/emission/ammonia/residues.ts index 243c58718..190480573 100644 --- a/fdm-calculator/src/balance/nitrogen/emission/ammonia/residues.ts +++ b/fdm-calculator/src/balance/nitrogen/emission/ammonia/residues.ts @@ -97,6 +97,14 @@ export function calculateNitrogenEmissionViaAmmoniaByResidues( const b_lu_hi = new Decimal(cultivationDetail.b_lu_hi ?? 0) const b_lu_hi_res = new Decimal(1).minus(b_lu_hi) + // If cultivation has no residues possible return 0 + if (b_lu_hi.isZero()) { + return { + id: cultivation.b_lu, + value: new Decimal(0), + } + } + // Get the Nitrogen content of the crop residues const b_lu_n_residue = new Decimal( cultivationDetail.b_lu_n_residue ?? 0, diff --git a/fdm-calculator/src/balance/nitrogen/removal/residue.test.ts b/fdm-calculator/src/balance/nitrogen/removal/residue.test.ts index 5159ba4a5..dc4ceca38 100644 --- a/fdm-calculator/src/balance/nitrogen/removal/residue.test.ts +++ b/fdm-calculator/src/balance/nitrogen/removal/residue.test.ts @@ -17,6 +17,18 @@ describe("calculateNitrogenRemovalByResidue", () => { b_n_fixation: 0, }, ], + [ + "catalogue2", + { + b_lu_catalogue: "catalogue2", + b_lu_croprotation: "quinoa", + b_lu_yield: 1000, + b_lu_n_harvestable: 20, + b_lu_hi: 0, + b_lu_n_residue: 2, + b_n_fixation: 0, + }, + ], ]) it("should return 0 if no crop residues are left", () => { @@ -216,4 +228,28 @@ describe("calculateNitrogenRemovalByResidue", () => { expect(result.total.toNumber()).toBeCloseTo(-3) //Approximation due to floating point expect(result.cultivations[0].value.toNumber()).toBeCloseTo(-3) //Approximation due to floating point }) + + it("should return 0 if b_lu_hi is 0", () => { + const cultivations: FieldInput["cultivations"] = [ + { + b_lu: "cultivation1", + b_lu_catalogue: "catalogue2", + b_lu_start: new Date("2022-01-01"), + b_lu_end: new Date("2022-12-31"), + m_cropresidue: true, + }, + ] + const harvests: FieldInput["harvests"] = [] + + const result = calculateNitrogenRemovalByResidue( + cultivations, + harvests, + cultivationDetailsMap, + ) + + expect(result).toEqual({ + total: new Decimal(0), + cultivations: [{ id: "cultivation1", value: new Decimal(0) }], + }) + }) }) diff --git a/fdm-calculator/src/balance/nitrogen/removal/residue.ts b/fdm-calculator/src/balance/nitrogen/removal/residue.ts index 1784dad53..195c690b5 100644 --- a/fdm-calculator/src/balance/nitrogen/removal/residue.ts +++ b/fdm-calculator/src/balance/nitrogen/removal/residue.ts @@ -75,7 +75,9 @@ export function calculateNitrogenRemovalByResidue( // Fallback to default yield from cultivation_catalogue if (yieldForThisHarvest === null) { - yieldForThisHarvest = cultivationDetail.b_lu_yield + yieldForThisHarvest = new Decimal( + cultivationDetail.b_lu_yield ?? 0, + ) } if (yieldForThisHarvest !== null) { @@ -101,6 +103,14 @@ export function calculateNitrogenRemovalByResidue( cultivationDetail.b_lu_n_residue ?? 0, ) + // If cultivation has no residues possible return 0 + if (b_lu_hi.isZero()) { + return { + id: cultivation.b_lu, + value: new Decimal(0), + } + } + // Calculate the amount of Nitrogen removed by crop residues of this cultivation const removal = b_lu_yield .dividedBy(b_lu_hi) @@ -115,7 +125,7 @@ export function calculateNitrogenRemovalByResidue( } }) - // Aggregate the total maount of Nitrogen removed by crop residues + // Aggregate the total amount of Nitrogen removed by crop residues const totalValue = removalsResidue.reduce((acc, residue) => { return acc.add(residue.value) }, new Decimal(0))