Skip to content

Structure for Extra PFT Traits. #943

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: develop
Choose a base branch
from

Conversation

sallymatson
Copy link
Collaborator

@sallymatson sallymatson commented Jul 10, 2025

Description

I've now completed a draft of the ExtraTraitsPFT class. The initial goal of this class is to allow us to define stoichiometric ratios for plant tissues by PFT (before, they were all set to standard defaults in the plant constants.) To do this I have:

  • Added a class (ExtraTraitsPFT) which stores a dictionary (keyed by PFT name) of dictionaries (keyed by traits). This is used in parallel with the Flora model, meaning that it stores these values per PFT, but it does not store any information about cohorts, grid cells, etc so it is essentially a reference dictionary for the entirety of the simulation.
{'pft_name': {'trait_one': value, 'trait_two': value, ...}, 'pft_name_2': {'trait_one': value, 'trait_two': value, ...} ...}
  • Expanded the get_flora_from_config method to return both a Flora instance and an ExtraTraitsPFT instance, and made that correction throughout the model. This part is a bit hacky, as it takes a pre-defined list of "stochiometry traits" to separate out to set up the ExtraTraitsPFT and uses the rest for the Flora. It works for now but it's a bit hardcoded, so would love thoughts on if we should make this more flexible, or if it's ok given that we do want to obstruct some of these things from the user (the user should just give all the traits for a PFT in the schema, and let US decide which belong where, as it's a somewhat arbitrary dictinction between what is in pyrealm and what is not.)
  • Removed the stoichiometric ratios from PlantConsts and removed relevant references in the plants model and stoichiometry model.

Fixes # (issue)

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 linked an issue Jul 10, 2025 that may be closed by this pull request
@codecov-commenter
Copy link

codecov-commenter commented Jul 10, 2025

Codecov Report

Attention: Patch coverage is 96.92308% with 2 lines in your changes missing coverage. Please review.

Project coverage is 94.22%. Comparing base (0b9f7de) to head (7f13e92).
Report is 46 commits behind head on develop.

Files with missing lines Patch % Lines
...irtual_ecosystem/models/plants/functional_types.py 93.75% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##           develop     #943   +/-   ##
========================================
  Coverage    94.22%   94.22%           
========================================
  Files           79       79           
  Lines         6320     6343   +23     
========================================
+ Hits          5955     5977   +22     
- Misses         365      366    +1     

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

"foliage_c_n_ratio",
"foliage_c_p_ratio",
]

Copy link
Collaborator

Choose a reason for hiding this comment

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

What about lignin content for the different tissues, as well as carbon mass per propagule, where are these located? Are these still seen as constants?

self.data["deadwood_c_n_ratio"] = xr.full_like(
self.data["elevation"], self.model_constants.deadwood_c_n_ratio
)
self.data["deadwood_c_n_ratio"] = xr.full_like(self.data["elevation"], 56.5)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you help me understand what this does:
self.data["deadwood_c_n_ratio"] = xr.full_like(self.data["elevation"], 56.5)

Comment on lines +79 to +81
reclaim_ratio=np.array(
[
extra_pft_traits.traits[name]["leaf_turnover_c_n_ratio"]
Copy link
Collaborator

@arne-exe arne-exe Jul 17, 2025

Choose a reason for hiding this comment

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

What does reclaim_ratio stand for (i.e., how does it behave in the model)?
-Is it to calculate the amount of nutrients that are reclaimed (and thus kept within the tree) before leaves are lost due to turnover? If so, would this mean we need both a (senesced) leaf_c_n_ratio and leaf_turnover_c_n_ratio to calculate their difference?
-I vaguely remember reading about how the nutrient cost to replace turnover must be taken into account, so perhaps that's what this reclaim_ratio does? Could you please remind me again how this works?

name,a_hd,ca_ratio,h_max,lai,par_ext,resp_f,resp_r,resp_s,rho_s,sla,tau_f,tau_r,yld,zeta,f_g,m,n,tau_rt,resp_rt,gpp_topslice,p_foliage_for_reproductive_tissue,deadwood_c_n_ratio,deadwood_c_p_ratio,leaf_turnover_c_n_ratio,leaf_turnover_c_p_ratio,plant_reproductive_tissue_turnover_c_n_ratio,plant_reproductive_tissue_turnover_c_p_ratio,root_turnover_c_p_ratio,root_turnover_c_n_ratio,foliage_c_n_ratio,foliage_c_p_ratio
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should lignin content of tissues and carbon mass per propagule also be here?

@arne-exe
Copy link
Collaborator

Hi @sallymatson, it looks good to me. I left a few questions about tissue lignin content and carbon mass per propagule, and some questions about what some parts of the code do but other than that no remarks from me.

Copy link
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.

This looks good but I think there is an issue with the class method on the ABC.

ABC objects are not intended to be directly used and all functionality should be through subclasses. The from_pft_default_ratios @classmethod should be an @abstract_method of Tissue and then the individual tissue subclasses should implement their own methods. This isn't exactly like you are creating an instance of Tissue - which is definitely not intended with ABCs - but the class method relies on the definition of specific downstream subclasses. If we wanted to add a new Tissue, we'd have to edit the ABC to support it. I can see why you've done it - because then you have the code to index the ratios from PFT names in a single location - but it isn't a clean structure.

I think there's an argument for making ExtraPFTTraits more like Flora, so it can use the CohortMethods and PandasExporter mixins from pyrealm. Then we could keep the traits in line with the community PFTs using the same mechanism and wouldn't have to do that alignment. But that might be a tweak for a separate PR.

Comment on lines +49 to +56
@classmethod
def from_pft_default_ratios(
cls,
subclass,
community: Community,
extra_pft_traits: ExtraTraitsPFT,
ratio_name: str,
) -> "Tissue":
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this should become an abstract method.

@davidorme
Copy link
Collaborator

  • Expanded the get_flora_from_config method to return both a Flora instance and an ExtraTraitsPFT instance, and made that correction throughout the model. This part is a bit hacky, as it takes a pre-defined list of "stochiometry traits" to separate out to set up the ExtraTraitsPFT and uses the rest for the Flora. It works for now but it's a bit hardcoded, so would love thoughts on if we should make this more flexible, or if it's ok given that we do want to obstruct some of these things from the user (the user should just give all the traits for a PFT in the schema, and let US decide which belong where, as it's a somewhat arbitrary dictinction between what is in pyrealm and what is not.)

I think it is absolutely fine to abstract that detail from the users. If we wanted to get a bit cleaner, Flora has an array_attrs class attribute that we could use to define that subset of the traits ( but that isn't really anything but a slightly fancier list of traits 😄 ).

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.

Make ideal foliage CNP ratios PFT dependent
4 participants