Skip to content

Update to pyrealm 2.0.0rc2, add reproductive tissue and carbon servicing#811

Merged
davidorme merged 22 commits intodevelopfrom
750-new-branch-fruit-production
Apr 8, 2025
Merged

Update to pyrealm 2.0.0rc2, add reproductive tissue and carbon servicing#811
davidorme merged 22 commits intodevelopfrom
750-new-branch-fruit-production

Conversation

@sallymatson
Copy link
Copy Markdown
Collaborator

@sallymatson sallymatson commented Apr 1, 2025

Description

This PR incorporates the updates to the T Model in pyrealm, which enable us to produce reproductive tissue (yay fruit!!) and gpp topslicing (yay root exudates and fungi!). More specifically...

  • Update to newest version of pyrealm, which requires adding some required variables for PFT and a few other minor changes.
  • Add plant reproductive tissue info to the data object for use by @TaranRallings in the Animals module. This adds four new variables to the data object:
    • propagule_c_mass
    • non_propagule_c_mass
    • fallen_propagule_c_mass
    • fallen_non_propagule_c_mass
      • Note: The later two (fallen_propagule_c_mass and fallen_non_propagule_c_mass) have replaced plant_reproductive_tissue_turnover - @jacobcook1995 I made minor tweaks in your models to account for this, but wanted to flag.
  • Split the GPP topslice into root_exudates and plant_symbiote_carbon_supply

For clarity's sake, the following table shows the different variables that the plants model needs to populate for RT (at least in the most basic version.) The RT growing on the ground will be incorporated to the basic grass model that @davidorme is planning to work on soon. If anyone has better/clearer ideas for these names, please let me know!! Now is the best time to choose them!

Canopy Ground (growing) Ground (fallen)
Propagules propagule_c_mass ground_propagule_c_mass fallen_propagule_c_mass
Non-propagules non_propagule_c_mass ground_non_prop_c_mass fallen_non_propagule_c_mass

Type of change

  • New feature (non-breaking change which adds functionality)
  • Optimization (back-end change that speeds up the code)
  • Bug fix (non-breaking change which fixes an issue)

Key checklist

  • Make sure you've run the pre-commit checks: $ pre-commit run -a
  • All tests pass: $ poetry run pytest

Further checks

  • Code is commented, particularly in hard-to-understand areas
  • Tests added that prove fix is effective or that feature works
  • Relevant documentation reviewed and updated

@sallymatson sallymatson requested a review from davidorme April 1, 2025 12:30
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 1, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 94.18%. Comparing base (cbb5795) to head (12f1b12).
Report is 757 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #811      +/-   ##
===========================================
+ Coverage    94.15%   94.18%   +0.02%     
===========================================
  Files           74       74              
  Lines         5285     5311      +26     
===========================================
+ Hits          4976     5002      +26     
  Misses         309      309              

☔ 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.

@sallymatson sallymatson changed the title Update to use pyrealm 2.0.0rc2 Update to pyrealm 2.0.0rc2, add reproductive tissue and carbon servicing Apr 1, 2025
@jacobcook1995
Copy link
Copy Markdown
Collaborator

jacobcook1995 commented Apr 3, 2025

I guess that fallen_propagule_c_mass and fallen_non_propagule_c_mass are a bit tricky as if animals consume from them then the carbon balance gets broken. This is because their entire mass gets added to the litter pool and is tracked there. This might work if we use a strict "plants -> animals -> litter" model run order though

An alternative approach would be to just have animals eat from the relevant litter pools (i.e. above ground metabolic litter for fallen fruits). But this might not be differentiated enough for the animal model.

We could also use a similar approach as the one used in animal/decay.py where excrement and carcasses decay is "modelled" within the animal model. I say modelled in a very loose sense in that a certain fraction of the carcasses is assumed to be available to scavenging and a certain fraction is assumed to decay before it's scavenged. (this decay happens directly to the soil model to avoid simulating decay a second time in the litter model)

@sallymatson
Copy link
Copy Markdown
Collaborator Author

I guess that fallen_propagule_c_mass and fallen_non_propagule_c_mass are a bit tricky as if animals consume from them then the carbon balance gets broken. This is because their entire mass gets added to the litter pool and is tracked there. This might work if we use a strict "plants -> animals -> litter" model run order though

An alternative approach would be to just have animals eat from the relevant litter pools (i.e. above ground metabolic litter for fallen fruits). But this might not be differentiated enough for the animal model.

We could also use a similar approach as the one used in animal/decay.py where excrement and carcasses decay is "modelled" within the animal model. I say modelled in a very loose sense in that a certain fraction of the carcasses is assumed to be available to scavenging and a certain fraction is assumed to decay before it's scavenged. (this decay happens directly to the soil model to avoid simulating decay a second time in the litter model)

Thank you for this comment @jacobcook1995, this problem isn't going to go away so it's great to think through now what makes sense.

@TaranRallings what do you think about these options?

Copy link
Copy Markdown
Collaborator

@davidorme davidorme left a comment

Choose a reason for hiding this comment

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

Note

Edited to fix silly mistakes in reproductive tissue partitioning, thanks @sallymatson

This is looking good but I think we do need to add more detail in the propagules. I've thought if this is something that can be post Easter, but I think critically we need number of propagules (not just mass) for recruitment.

I think what we need here for now are two partitioning parameters for reproductive tissue:

  • the proportion allocation of reproductive mass to propagules
  • the actual carbon mass per propagule

That then gives us (using absurdly long names):

canopy_n_propagules: NDArray[np.int_] = np.floor(
    (reproductive_tissue_carbon_allocation * reproductive_tissue_mass_propagule_proportion)
    / carbon_mass_per_propagule
)
canopy_non_propagule_reproductive_tissue_mass: NDArray[np.float64] = (
    reproductive_tissue_carbon_allocation 
    - (canopy_n_propagules * carbon_mass_per_propagule)
)

When canopy_non_propagule_reproductive_tissue_mass is turned over, @TaranRallings and I agree that we can simply add it to the litter pool, so we'll need to get @jacobcook1995 to average another pile of mass with its own stochiometry into that pool.

Ideally, we'd have those two parameters as PFT specific things, but I think we live with one propagule size class for now! I think we need to take a look at creating a VE specific submodule of pyrealm to maybe isolate our changes, or perhaps to look at how to make the model more extensible, but that's for another day!

The turned over propagules then need some changes:

  • The parameter in the pyrealm PFT is an overall RT mass turnover. I think we just take the same approach as above to split the turned over mass into non-propagule mass that heads into litter and number of turned over propagules. I'm sure that is going to introduce a bias, but we don't care for now.

  • The number of turned over propagules then needs to go into a CellID x PFT array - we need to know the number of propagules per PFT for recruitment. I think ideally we want propagules to be in two different pools (surface accessible vs a more protected seedbank), but I think that is for another PR.

I don't think we need ground_propagule_c_mass and fallen_propagule_c_mass. Once we have the propagule numbers, we can add a germination rate and that introduces new cohorts into the tree part of the model. So for the moment, propagules are either on the tree, on the ground or convert into a new cohort.

I think we will need a separate mechanism to expose seedling mortality alongside the ground vegetation, but that isn't handled here.

community.stem_allometry.foliage_mass / community.stem_traits.tau_f

# Sum of turnover from all cohorts in a grid cell
# TODO: Pyrealm provides annual turnover values. Divide by 12 to get monthly
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should use the updates_per_year attribute that came in with #747 rather than hardcoding N/12.

Comment on lines +614 to +615
self.data["propagule_c_mass"] = xr.full_like(self.data["elevation"], 0)
self.data["non_propagule_c_mass"] = xr.full_like(self.data["elevation"], 0)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We could partition this across the vertical canopy layers, but @TaranRallings only cares about canopy/non-canopy and realistically I think we'd be making unwarranted assumptions about the vertical distribution of fruit within the canopy. So maybe we should note here that we are deliberately (for now and maybe permanently) not partitioning the vertical structure of this.

@sallymatson
Copy link
Copy Markdown
Collaborator Author

Okay, I've now updated the PR to distinguish between canopy_n_propagules, canopy_non_propagule_c_mass, fallen_n_propagules, and fallen_non_propagule_c_mass. The fallen_non_propagule_c_mass is what goes to the litter model (and acts exactly as reproductive_tissue_turnover used to.) The fallen propagules will not be added to this mass, and will be dealt with separately down the line.

@sallymatson sallymatson requested a review from davidorme April 3, 2025 12:31
@sallymatson sallymatson requested review from davidorme and removed request for davidorme April 8, 2025 12:54
Comment on lines +695 to +696
# TODO - This is wrong. Reproductive tissue mass can't simply move backwards
# and forwards between these two classes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

One final comment on my end, @davidorme can you clarify what this means? I guess it is just because we're recalculating the mass each update, but the wording is confusing me!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yeah - it isn't very clear.

Say we've got a propagule mass of 5g, a propagule proportion of 0.6 and the reproductive tissue mass at time 1 is $W_{rt1} = 10\textrm{g}$. That gives us 1 propagule and 5g of non-propagule. If $W_{rt2} = 16.7\textrm{g}$, then we have 2 propagules and 6.7g of non-propagule.

Damn. That example doesn't work. Well. It kind of does, because the allocation of the extra mass at time 2 is actually 5g to propagules and 1.7g to non-propagule, so the proportions are averaged through time in a weird way. I think there might be cases were non-propagule mass decreases. I think we might want a fruit lifecycle model, but that's way more complex and we end up with cohorts of fruit on cohorts of trees.

Copy link
Copy Markdown
Collaborator

@davidorme davidorme left a comment

Choose a reason for hiding this comment

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

I've looked at this code in detail in PR #822 and I'm happy that this does what we need now and that we've noted where to go next.

@davidorme davidorme merged commit f0471ad into develop Apr 8, 2025
16 checks passed
@davidorme davidorme deleted the 750-new-branch-fruit-production branch April 8, 2025 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants