Sipnet v2 support#3813
Conversation
…e parameters for SIPNET v2
…RESP, WATER_HRESP, LEAF_WATER)
…ater, add N/CH4 NetCDF outputs
…ers from v2 template
…g litterWater handling
|
picking up from Chris's initial v2 scaffolding , the main gaps were that the v2 templates were incomplete and model2netcdf couldn't parse v2 output. Here's what this round adds:
added tests for v1 and v2 format coverage, including N2O/CH4 extraction and the missing litterWater edge case. v1 configs still work exactly as before when |
dlebauer
left a comment
There was a problem hiding this comment.
Thanks for getting this started @infotroph and for bringing this home @divine7022
I left a few questions & comments, none critical; most are thoughts to consider in future iterations.
| write.config.SIPNET <- function(defaults, trait.values, settings, run.id, inputs = NULL, IC = NULL, | ||
| restart = NULL, spinup = NULL) { | ||
|
|
||
| sipnet_version <- package_version(settings$model$revision, strict = FALSE) |
There was a problem hiding this comment.
Isn't there an output file that already provides version information? Seems like that would be a more reliable source of version information - at least for >= v2
There was a problem hiding this comment.
good idea in principle, sipnet v2 now has a version.h that has version "2.0.0" (and optionally a git hash) into the binary);
but major limitation would be write.config runs pre execution so there's no output to inspect yet, but that querying the binary's version string could be a future enhancement. Worth noting that sipnet v2 binary does support --version output, so this could be added as a validation step
There was a problem hiding this comment.
There are no outputs at this stage -- we have a binary path but haven't tried calling it yet, and conceptually Sipnet might not even exist on the machine writing the configs. Seems simpler to me to have write.configs do what the settings file says rather than try to infer it from any other source.
There was a problem hiding this comment.
haven’t pushed my local changes for this yet, I am commenting here since the context flows -- 2d7559c
There was a problem hiding this comment.
heads up:
https://github.com/PecanProject/pecan/actions/runs/22737237619/job/65941705561?pr=3813
fixed version parsing to handle non standard DB revisions like "136", "unk", "git", now only recognizes major versions 1 and 2 as valid sipnet versions; everything else defaults to v1
| lp <- as.integer(settings$model$litter_pool %||% 1) | ||
| an <- as.integer(settings$model$anaerobic %||% 1) | ||
| # NITROGEN_CYCLE requires both LITTER_POOL and ANAEROBIC | ||
| if (nc == 1 && !(lp == 1 && an == 1)) { |
There was a problem hiding this comment.
This is enforced by sipnet. In one hand, catching it early makes sense. On the other, it simplifies the pecan code if we delegate consistency checks to sipnet
There was a problem hiding this comment.
agreed on the tradeoff.; keeping it for now because it gives clearer error messages before job submission, but noting it's duplicated from sipnet's validateContext()
There was a problem hiding this comment.
👍 To delegating to Sipnet here.
There was a problem hiding this comment.
And while we're looking at this section:
- How do we feel about the current named options (
<model><nitrogen_cycle>0</nitrogen_cycle>...) vs the approach PEcAn.ed2 takes of passing a literal string of command flags (<model><binary_args>--no-nitrogen-cycle</binary_args>...)? I think I prefer the named options, but seemed worth asking. - If using named options, would it make sense to accept them as a block and pass them all without needing to enumerate? something like
<model>
<options>
<NITROGEN_CYCLE>1</NITROGEN_CYCLE>
<GDD>0</GDD>
<NEW_FLAG_SIPNET_HAS_ADDED_SINCE_LAST_REVISION_OF_WRITE.CONFIGS_CODE>1</NEW_FLAG_...>
...
</options>
...
...which could then be processed as flag_lines <- paste(names(settings$model$options), "=", settings$model$options, collapse = "\n")?
If this looks good I'll implement it.
There was a problem hiding this comment.
this is a good design decision, since new SIPNET flags don't require code changes to write.config;
and for me it seems like could be deferred to follow up PR but shouldn't be mixed into the current one (it changes the XML schema and requires documentation)
make sense ?
There was a problem hiding this comment.
deferred to follow up PR
Fine with me
changes the XML schema
No more than this PR does already -- we're already going from not expecting any options in the XML to expecting them directly inside model. But it's probably worth a review of how other models do this so that we can keep some uniformity.
| write.config.SIPNET <- function(defaults, trait.values, settings, run.id, inputs = NULL, IC = NULL, | ||
| restart = NULL, spinup = NULL) { | ||
|
|
||
| sipnet_version <- package_version(settings$model$revision, strict = FALSE) |
There was a problem hiding this comment.
There are no outputs at this stage -- we have a binary path but haven't tried calling it yet, and conceptually Sipnet might not even exist on the machine writing the configs. Seems simpler to me to have write.configs do what the settings file says rather than try to infer it from any other source.
| lp <- as.integer(settings$model$litter_pool %||% 1) | ||
| an <- as.integer(settings$model$anaerobic %||% 1) | ||
| # NITROGEN_CYCLE requires both LITTER_POOL and ANAEROBIC | ||
| if (nc == 1 && !(lp == 1 && an == 1)) { |
There was a problem hiding this comment.
👍 To delegating to Sipnet here.
| lp <- as.integer(settings$model$litter_pool %||% 1) | ||
| an <- as.integer(settings$model$anaerobic %||% 1) | ||
| # NITROGEN_CYCLE requires both LITTER_POOL and ANAEROBIC | ||
| if (nc == 1 && !(lp == 1 && an == 1)) { |
There was a problem hiding this comment.
And while we're looking at this section:
- How do we feel about the current named options (
<model><nitrogen_cycle>0</nitrogen_cycle>...) vs the approach PEcAn.ed2 takes of passing a literal string of command flags (<model><binary_args>--no-nitrogen-cycle</binary_args>...)? I think I prefer the named options, but seemed worth asking. - If using named options, would it make sense to accept them as a block and pass them all without needing to enumerate? something like
<model>
<options>
<NITROGEN_CYCLE>1</NITROGEN_CYCLE>
<GDD>0</GDD>
<NEW_FLAG_SIPNET_HAS_ADDED_SINCE_LAST_REVISION_OF_WRITE.CONFIGS_CODE>1</NEW_FLAG_...>
...
</options>
...
...which could then be processed as flag_lines <- paste(names(settings$model$options), "=", settings$model$options, collapse = "\n")?
If this looks good I'll implement it.
Co-authored-by: David LeBauer <dlebauer@arizona.edu>
| # determine which SIPNET major version we're configuring for. | ||
| # settings$model$revision may be a SIPNET version ("1", "2", "2.0") | ||
| # or a internal model identifier ("136", "unk", "git", NULL). | ||
| # Only "1" and "2" (and their dotted forms) are valid SIPNET versions; | ||
| # everything else defaults to v1 for backward compatibility | ||
| rev_str <- "v1" | ||
| if (!is.null(rev_raw) && nzchar(rev_raw)) { | ||
| if (grepl("^[0-9]+$", rev_raw)) { | ||
| rev_raw <- paste0(rev_raw, ".0") | ||
| } |
There was a problem hiding this comment.
This feels like a lot of complication to me, especially since it lets us accept a bare integer but not "v<integer>"; IMO the latter is quite a bit more widely used.
I wonder if we should instead require a numeric version for anything but an enumerated set of legacy versions (maybe just 136 and git, dropping unk?)
There was a problem hiding this comment.
I think the right approach would be to simplify to accept "v1", "v2", "1", "2" and default everything else to v1
There was a problem hiding this comment.
may be
rev_raw <- settings$model$revision %||% "1"
# accept "v1", "v2", "1", "2", "1.0", "2.0"; legacy DB identifiers (136, git) default to v1
rev_str <- if (grepl("^v?2(\\.0)?$", rev_raw, ignore.case = TRUE)) "v2" else "v1"
make sense ?
There was a problem hiding this comment.
right approach would be to simplify to accept "v1", "v2", "1", "2"
If you're saying accept those but reject x.y.z numeric versions, I disagree. settings$model$revision is declaring the version of Sipnet that will actually run when you call settings$model$binary, and needs to be interpretable by any function that needs it and not just run.write.configs.
Say hypothetically we do a couple releases with restart support and then decide we want to change its API without breaking support for the previous ones; we'd want to be able to add conditions like if (settings$model$revision >= "2.0.3") {... into write.restart without breaking the write.configs v1/v2 logic.
There was a problem hiding this comment.
enumerated known legacy identifiers ("136", "git") and map them to v1 explicitly;
sipnet_version variable is also available in function scope for any future >= comparisons (e.g. >= "2.0.3" check for api changes)
|
https://github.com/PecanProject/pecan/actions/runs/22837731305/job/66245148269?pr=3813 is this an upstream issue with how the sipnet v1.3.0 binary interacts with the current gha runner ? |
|
@divine7022 Took me a while to track this down (had to run the Github action locally), but: |
b15916b
Description
Pushing some long-stashed changes so we can iterate on them.
NOT YET TESTED.Implemented so far:
Before merging, need to at least:
And probably also:
Motivation and Context
Review Time Estimate
Types of changes
Checklist: