hit this in the HARVEST event path. events.c computes:
const double woodC = envi.plantWoodC + envi.plantWoodCStorageDelta;
...
double litterAdd = fracTA * (envi.plantLeafC + woodC);
In my runs, envi.plantWoodC had been clamped to zero, but envi.plantWoodCStorageDelta had accumulated negative from the 5-day allocation lag at sipnet.c. woodC went negative, litterAdd went negative, litterC below zero on harvest.
Patched locally in the harvest case:
const double woodC =
fmax(envi.plantWoodC + envi.plantWoodCStorageDelta, 0.0);
that clears the harvest symptom. But the harvest path is just where I hit it, the asymmetry is at the clamp itself:
1 . ensureNonNegativeStocks at sipnet.c clamps plantWoodC
2. plantWoodCStorageDelta is not in the clamp list.
3. state.h says the actual wood carbon is the sum of the two
same sum shows up in other places, and flips sign when the sum goes negative:
woodResp , woodLitter and
output column written at sipnet.c, which pecan maps to AbvGrndWood at model2netcdf.SIPNET.R
getMassTotals at balance.c sums both into total C, so the balance check still closes; the issue isn't mass conservation, it's that the state representation allows nonphysical values.
separate from #282 (which was about accounting for the clamp mass, and correctly didn't touch StorageDelta since StorageDelta wasn't in the clamped set). This is the v2.1 split not having a joint-sum floor.
question: is the right fix to apply the same fmax idea generally, clamp the sum at ensureNonNegativeStocks and route the mass through the #282 post clamp machinery; or do you want a different approach ?
hit this in the HARVEST event path. events.c computes:
In my runs, envi.plantWoodC had been clamped to zero, but envi.plantWoodCStorageDelta had accumulated negative from the 5-day allocation lag at sipnet.c. woodC went negative, litterAdd went negative, litterC below zero on harvest.
Patched locally in the harvest case:
that clears the harvest symptom. But the harvest path is just where I hit it, the asymmetry is at the clamp itself:
1 . ensureNonNegativeStocks at sipnet.c clamps plantWoodC
2. plantWoodCStorageDelta is not in the clamp list.
3. state.h says the actual wood carbon is the sum of the two
same sum shows up in other places, and flips sign when the sum goes negative:
woodResp , woodLitter and
output column written at sipnet.c, which pecan maps to AbvGrndWood at model2netcdf.SIPNET.R
getMassTotals at balance.c sums both into total C, so the balance check still closes; the issue isn't mass conservation, it's that the state representation allows nonphysical values.
separate from #282 (which was about accounting for the clamp mass, and correctly didn't touch StorageDelta since StorageDelta wasn't in the clamped set). This is the v2.1 split not having a joint-sum floor.
question: is the right fix to apply the same fmax idea generally, clamp the sum at ensureNonNegativeStocks and route the mass through the #282 post clamp machinery; or do you want a different approach ?