Add exudation of root carbohydrates and uptake of nutrients by plants#738
Conversation
… input rate conversion
…ients if nutrient concentrations are negative
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #738 +/- ##
===========================================
+ Coverage 94.68% 94.69% +0.01%
===========================================
Files 74 74
Lines 5135 5148 +13
===========================================
+ Hits 4862 4875 +13
Misses 273 273 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Hi @jacobcook1995 , wondering about the decision behind the following, "with a deficit that needs to be filled before reaching zero again"? Is there a real plant/scientific process that this deficit is representing, or else would it be more life-like to just reset any negative values to zero and then start from there if concentrations go back up? Not sure how crucial this is, but wanted to hear the rationale. Will look through the code now as well. |
|
It's basically to avoid spontaneous creation of nutrients, if the plants take more nutrients than there are in the soil and then the concentrations are reset to zero, nutrients have been added out of thin air. Whereas if the pools remain negative until they are refilled the balance is preserved (even if it's a bit nonsensical) |
|
@jacobcook1995 just to follow up on @sallymatson 's question while I take a closer look. I agree that temporarily "storing" nutrient deficits somewhere is sensible to avoid nutrient addition out-of-thin-air by zeroing it. However, in the first place, as the nutrient drops to zero, the plant is not uptaking any more from the soil pool, so there is no deficiency in the first place? Say, in a time step, the plant wants to uptake 50 mg of nutrient but there is only 25 mg in the soil. The plant will only ever take 25 mg, instead of taking 50 mg and then leaving a 25 mg deficit in the soil? |
|
|
||
| # Assume plants can take 0.1% of the available nutrient per day, uptake stops | ||
| # (rather than goes negative) if the soil nutrient concentrations go negative | ||
| self.data["plant_ammonium_uptake"] = xr.where( |
There was a problem hiding this comment.
Noob question about python: when I googled xr.where I got to the page xarray.where. Does python functions always have a shortform counterpart?
There was a problem hiding this comment.
If you look at the top of the file, you'll see import xarray as xr. This then allows us to use the short name within that code file.
Some packages are commonly used this way (numpy -> np, xarray -> xr etc) and others just aren't particularly. The abbreviations tend to be fairly universal, but there isn't actual anything to stop someone doing import xarray as I_really_like_typing_so_this_is_xarray
There was a problem hiding this comment.
There's some python packages that have standard condensed names (I can only think of np for numpy and xr for xarray off the top of my head).
These get defined at the top of the script using the following
import xarray as xr| self.data["plant_ammonium_uptake"] = xr.where( | ||
| self.data["soil_n_pool_ammonium"] > 0.0, | ||
| self.data["soil_n_pool_ammonium"] * 0.001, | ||
| 0.0, |
There was a problem hiding this comment.
Should this be self.data["soil_n_pool_ammonium"] instead of zero? In this case, the plant exhaust the last amount of nutrient rather than not taking anything. Same question for nitrate and P.
There was a problem hiding this comment.
This maybe isn't clear enough, but the where condition here is defining what happens when nutrient concentrations are negative, so this acts to stop plants from taking up a negative amount of nutrients (i.e. for some reason deciding to pump nutrients into the soil). This is basically just making sure that the nonsensical things don't happen when the "negative nutrient concentration" edge case is hit
|
|
||
| # Calculate rate at which ammonium volatilises as ammonia | ||
| ammonia_volatilisation_rate = ( | ||
| ammonia_volatilisation_rate = np.where( |
There was a problem hiding this comment.
Why does this have to be conditional and not simply self.constants.ammonia_volatilisation_rate_constant * self.pools.soil_n_pool_ammonium . Is this to guarantee that it does not become negative?
There was a problem hiding this comment.
Yep because self.pools.soil_n_pool_ammonium can be negative, this step is needed to avoid negative volatilisation (which is nonsensical).
| / self.max_depth_of_microbial_activity | ||
| ) | ||
|
|
||
| # Determine net changes to the pools |
There was a problem hiding this comment.
So this is where each component in a summuation can lead to negative delta values?
There was a problem hiding this comment.
Yep it's in this section. So e.g. self.data["plant_ammonium_uptake"] does not change as the soil model progresses (as it's calculated by the plants model), so it will still be subtracted at the same rate even "soil_n_pool_ammonium" has decreased due to soil model processes, which means that it can potentially drive the value negative
| ammonium=ammonium_leaching, | ||
| nitrate=nitrate_leaching, | ||
| labile_P=labile_phosphorus_leaching, | ||
| ammonium=np.where(ammonium_leaching >= 0.0, ammonium_leaching, 0.0), |
There was a problem hiding this comment.
Like the question above, I wonder if instead of fixing the FALSE condition to zero, it could be the last remaining amount of ammonium instead. Without fully understanding the code, my gut feeling says instead of zeroing the rates, we would be zeroing the nutrient amount instead.
There was a problem hiding this comment.
Yeah this is the same as the above, where 0.0 is used to handle the case where ammonium concentration is negative. This is then needed to stop negative leaching from happening, which would be nonsensical (and break the nutrient balance)
hrlai
left a comment
There was a problem hiding this comment.
The motivation of this PR makes sense. My overall thought is:
- should we zero out nutrient amounts instead of their rate of change?
- is places it seems that you zero out both the amounts and rates? is this redundant? If we somehow ensures that the amounts never drop to zero then can we safely ignore the rates that would drive the amount to negative?
- alternatively, we only conditionally change the rates to one that does not drive amounts to negative, but that feels like slightly more work than simply constraining the lower bound of amounts?
It is impossible to completely rule out this happening. The fundamental reason for this is that the plant model and soil model are updated separately, so while nutrient uptake can be calculated in a sensible manner so that the plants never take up more nutrients than are there, nutrient availability can decrease during the soil model update which means the same uptake rate now drives the nutrient concentrations negative. By using sensible parameters choices we make sure that negative concentrations don't normally happen, but they will always exist as potential edge case so they have to be handled in someway.
The big issue with doing this is that it breaks the nutrient balance, because plants have taken more nutrients from the soil than are there but then there's no deficit. We could record when this happens, and subtract the amount the next time plants try to uptake nutrients. The reason I'm reluctant to do this is that this would involve adding a bunch of "correction" variables to the data object, which is messy from the sense of people understanding the set of variables used in the model. |
…(for the plant model)
…rom the soil model to the plants model
…e the dissolved nutrient amounts rather than the total nutrient amounts
|
@davidorme, I've made the change you suggested in the meeting yesterday and now the amount of dissolved nutrients are available to the plants model. Let me know if the setup seems sensible |
…id possible divide by zero
…model from the plants model into the soil model
Description
This PR adds exudation of simple carbohydrates from plant roots as well as plant uptake of inorganic nutrients. All of these are basically just dummy functions on the plant side, with the point being to define the things that the soil model needs to operate.
One problem that this new system introduces is that the plants can now drive soil nutrient concentrations to negative values. This isn't something that we can completely prevent from happening unless we decided to solve the plants and microbes as a single system of equations (the plants provide constant uptake rates to the soil integration, these rates cannot change within the time span of the integration, even if all nutrients have been exhausted).
Realistically, we shouldn't ever see negative nutrient concentrations with sensible parameter choices, and seeing negative nutrient concentrations will probably be a good "something is wrong here" indicator. I've refactored the functions that depend on the three possibly negative pools (ammonium, nitrate, labile phosphorus) so that they stop (rather than switching sign) when concentrations go negative. This should mean that the simulation treats negative concentrations as effectively the same as zero concentrations (with a deficit that needs to be filled before reaching zero again), rather than spiralling out of control and magicking nutrients out of thin air.
Also let me know if this fits sensibly with the changes to the plant model, happy to change things if it doesn't
Fixes #736
Type of change
Key checklist
pre-commitchecks:$ pre-commit run -a$ poetry run pytestFurther checks