Skip to content

Improve unit conversion error reporting#959

Open
PhilMiller wants to merge 44 commits into
NOAA-OWP:masterfrom
PhilMiller:PhilMiller/unit-conversion-error-reporting
Open

Improve unit conversion error reporting#959
PhilMiller wants to merge 44 commits into
NOAA-OWP:masterfrom
PhilMiller:PhilMiller/unit-conversion-error-reporting

Conversation

@PhilMiller

@PhilMiller PhilMiller commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Improve how errors in unit conversion are tracked and reported. Each particular mis-match between distinct models and their variables will be logged exactly once. The reports have comprehensive detail allowing for straightforward correction of the underlying modules or configuration.

Additions

  • Define and use a unit_conversion_exception type ("UCE") when UDUNITS fails to apply a conversion, storing the provider and variable details
  • Catch UCEs at the requester call site, and log the corresponding requester details along with those of the provider
  • Keep a set of the already-reported errors so that they don't get repeated

Changes

  • Report UCEs from BMI modules, CSV forcings, and NetCDF forcings
  • Tests and test models have units corrected to match up
  • Separate model update() from the existing get_response(), which is now specifically used as getting the effective depth of surface runoff
  • Normalization of how units of dimensionless quantities are spelled
  • Explicitly test CSV forcing reader's handling of when it can't parse units from the field headers
  • Lots of preprocessor include cleanups resulting from refactoring

Testing

  1. All integrated tests pass, include some that have been revised and expanded

Notes

  • This has been cherry-picked from the NGWPC codebase with subsequent conflict fixups, and then substantial revisions to address reviewer comments

Checklist

  • PR has an informative and human-readable title
  • Changes are limited to a single goal (no scope creep)
  • Code can be automatically merged (no conflicts)
  • Code follows project standards (link if applicable)
  • Passes all existing automated tests
  • Any change in functionality is tested
  • New functions are documented (with a description, list of inputs, and expected output)
  • Placeholder code is flagged / future todos are captured in comments
  • Project documentation has been updated (including the "Unreleased" section of the CHANGELOG)
  • Reviewers requested with the Reviewers tool ➡️

PhilMiller and others added 25 commits April 27, 2026 23:32
…ce data_access to support CSV and NetCDF reporting
…ic units in tests, and disable now-throwing non-conversion cases
@PhilMiller PhilMiller force-pushed the PhilMiller/unit-conversion-error-reporting branch from 2bd6aee to 1ffa31f Compare April 28, 2026 00:07
@PhilMiller PhilMiller marked this pull request as ready for review April 28, 2026 00:07

@hellkite500 hellkite500 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

While I support the general idea behind this, I have some design and implementation concerns that may require a bit of dialogue and/or refactoring to get through.

Comment thread include/forcing/DataProvider.hpp Outdated
Comment thread include/realizations/catchment/Bmi_Multi_Formulation.hpp Outdated
Comment thread src/realizations/catchment/Bmi_Module_Formulation.cpp Outdated
Comment thread src/realizations/catchment/Bmi_Module_Formulation.cpp Outdated
Comment thread src/core/mediator/UnitsHelper.cpp Outdated
Comment thread test/forcing/CsvPerFeatureForcingProvider_Test.cpp Outdated
@PhilMiller

Copy link
Copy Markdown
Contributor Author

All useful feedback. I'll try to address it shortly.

@robertbartel robertbartel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

FWIW, I'm not fully done with review - still trying to make sure I have my head wrapped around how this executes, especially any differences with scalar versus vector values. But I did want to go ahead and make a few initial comments.

The only clear thing at this point that I can say needs changing is that the BMIconventions.md document needs to be updated to reflect these behavior changes, especially related to unitless and none-ish conventions.

A couple other early impressions that aren’t fully formed yet, but might stimulate some useful discussion …

  • This feels a little brittle. In fairness, that’s probably got a lot to due with the larger design of things around it, although this doesn’t necessarily disqualify the observation from relevance.
  • Throwing an(other) exception on every variable read with units issues seems like it could get a little computationally expensive. Cheaper than the IO overhead of cerr-ing every time a warning condition is encountered, but that could be turned off. It doesn’t look like this exception-based approach can be.

@PhilMiller

Copy link
Copy Markdown
Contributor Author

My experience with these changes on the NGWPC side is that they ultimately pushed the overall system toward an overall more robust situation, but there was a lot of reconciliation of these warning messages along the way. There's a follow-on PR coming that also optionally specifies the desired units of output variables named in the realization config, which would avoid modeling misinterpretations and a current inconsistency between mosaiced formulations for different catchments that output the same variables but in different units. Maybe I'll put that up as a PR against this branch, so we can see and evaluate it in context.

@PhilMiller

Copy link
Copy Markdown
Contributor Author

Regarding the exceptions concern, the actual exception throw is generally not expected to be much more expensive than a return. We are generating a bunch of strings in that path, though, which I can see being more of a concern. I think that can mostly be squeezed out, which I'll try to do.

Hydrologic simulation in NGen is driven by Layer calling
catchment_formulation->get_response() on each catchment's formulation
instance in turn.

The get_response() method served two related but distinct roles:
1. Advancing the simulation by one step within a formulation or one element thereof
2. Calculating and returning the hydrologic response from that step

Within a Bmi_Multi_Formulation, the latter responsibility was only
applicable to modules actually providing the runoff variable, and not
others that generated inputs to that module or ancillary output
variables. However, get_response() was the means to advance them in
time, and their primary output variable had to be queried
accordingly. Advancing in time is now split out to an update() member
function().

By making that split, get_response() can be updated to consistently
return results in the units of "m" (depth) expected by the caller in
Layer::update_models(). Without the associated changes, every other
module would incur spurious unit conversion errors at every time step,
when asked for some arbitrary 'main output variable' via
get_response() that may be in other units.
@PhilMiller

Copy link
Copy Markdown
Contributor Author

I haven't directly addressed the review comments yet, but I did push an additional commit that better applies and motivates these changes. That change could potentially be made on its own, but it kinda comes with the rest of what's here, so that's how I'm presenting it at the moment. If asked, I could propose that in more isolation instead or in advance.

@PhilMiller

Copy link
Copy Markdown
Contributor Author

On the string passing overheads in the error path, I think I'm going to push back a little bit on trying to change that right away. I agree it could become a burden. However, a 'healthy' simulation run that's not incurring unit mismatches will never incur that burden. For pre-production (benchmarking, calibration, regionalization, retrospective, etc) and operational use cases, where we're most concerned about performance, we should have hammered out those issues before running. For a scientifically meaningful run, I really don't think there's an excuse for accepting unit mismatches. If we need some targeted adapters for (e.g.) precip_rate mixing up mass vs volume, we should build those and push forward.

More broadly, we need to profile and see if/where passing around so many strings actually does cost ngen performance, and start grinding those away.

@PhilMiller PhilMiller force-pushed the PhilMiller/unit-conversion-error-reporting branch from 687c078 to a97c44a Compare May 4, 2026 17:04
@PhilMiller PhilMiller force-pushed the PhilMiller/unit-conversion-error-reporting branch from a97c44a to c55c433 Compare May 4, 2026 17:08
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