Skip to content

Conversation

@ayushman1210
Copy link

@ayushman1210 ayushman1210 commented Dec 14, 2025

Description

This PR standardizes dimensional unit conversions by replacing manual arithmetic with the centralized helper function PEcAn.utils::ud_convert() where appropriate. This reduces maintenance burden, improves readability, and minimizes the risk of unit conversion errors.
The changes focus strictly on dimensional unit conversions (mass, time, energy, area) and intentionally exclude model-specific scaling, biological constants, and timestep-dependent logic, per project guidance.

Summary of Changes

  • models/sipnet/R/model2netcdf.SIPNET.R
  • Replaced manual mass unit conversions (gC → kgC) with ud_convert()
  • Retained division by timestep.s for flux variables, since SIPNET outputs per-timestep accumulations (model-specific and required)
  • Left biological constants and model-specific scalings unchanged (e.g., SLA, carbon fractions, cm→mm water conversions)
  • models/dalec/R/model2netcdf.DALEC.R
  • Replaced manual conversions (* 0.001, / 86400) with ud_convert()
  • Removed redundant time division where ud_convert() already converts from daily to per-second units
  • Ensured fluxes are consistently written as kgC m⁻² s⁻¹ and pools as kgC m⁻²
  • models/gday/R/model2netcdf.GDAY.R
  • Replaced manual Mg/ha ↔ SI conversions with ud_convert()
  • Removed extra per-second scaling where ud_convert() already performs year → second conversion
  • Kept pool variables as state quantities without time division
  • models/sipnet/R/write_restart.SIPNET.R
  • Replaced manual mass conversions with ud_convert()
  • Removed redundant conversion factors while preserving model-specific logic
  • models/stics/R/met2model.STICS.R
  • Replaced precipitation conversion (kg m⁻² s⁻¹ → mm d⁻¹) with ud_convert()
  • Left radiation and other physical constants unchanged where conversion is not purely dimensional

NOT CHANGED

  • Biological / stoichiometric constants
  • Model-specific timestep logic
  • Physical constants mixed with unit conversions
  • Plot- or model-convention scaling

Fixes - #3712

Copy link
Member

@infotroph infotroph left a comment

Choose a reason for hiding this comment

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

Thanks for working on this! Can you describe how you tested these changes? I ask because I see a few issues here that should have produced obvious error messages in a test run. The issues themselves are minor and will be easy to fix, but since unit conversions can be surprisingly tricky it's important to know at review time that someone has actually run the code and looked at its output.

@ayushman1210
Copy link
Author

hi @infotroph Thanks for the review and for pointing this out.
You’re right — I should have been clearer about testing. In this case, I did not run a full test or execute all code paths after making the changes. My checks were based on static reasoning and spot inspection, and a proper run would indeed have surfaced the issues you noticed.
I understand why this is especially important for unit conversions, and I agree that changes like this should always be validated by running the code and inspecting the output. I’ll fix the issues shortly and will make sure to:

  • run the code locally,
  • verify the relevant outputs,
  • and explicitly describe the testing performed in the follow-up.

Thanks for the reminder !!

@ayushman1210
Copy link
Author

Hi @infotroph
Just a gentle follow-up regarding the PR [#3719 ]
Whenever you get a chance, I’d really appreciate a review.
Thanks for your time!

@@ -0,0 +1,49 @@
# Test script to validate unit conversion changes
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for including tests! Unit tests like this should be placed in the tests/testthat folder of the appropriate package and not to the top-level tests folder, which is used for whole-system test material (and is due for a reorg/cleanout, but that's a separate issue). For tests of ud_convert itself, the appropriate package is PEcAn.utils so they should be added to the existing base/utils/tests/testthat/test-ud_convert.R.

I also request using standard testthat expectations rather than rolling your own comparison machinery -- this entire script is basically equivalent to

expect_equal(ud_convert(100, "g/m2", "kg/m2"), 0.1) # eg DALEC/SIPNET C pools
expect_equal(ud_convert(86400, "g/m2/d", "kg/m2/s"), 0.001) # eg DALEC/SIPNET C fluxes
expect_equal(ud_convert(10, "Mg/ha", "kg/m2"), 1) # eg GDAY pools
expect_equal(ud_convert(1000, "J/mol", "kJ/mol"), 1) # eg photosynthesis params

Comment on lines +1 to +4
# Regression tests for unit conversion refactoring
# Run with: testthat::test_dir("tests/testthat", filter = "test_model_conversions")

source("test_model_conversions.R")
Copy link
Member

Choose a reason for hiding this comment

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

delete this file -- test_model_conversions.R belongs in base/utils/tests/testthat, which already has an existing testthat.R

Comment on lines +9 to +11
# Pool conversions (mass only)
expect_equal(ud_convert(100, "g/m2", "kg/m2"), 0.1)
expect_equal(ud_convert(1000, "g/m2", "kg/m2"), 1.0)
Copy link
Member

Choose a reason for hiding this comment

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

I do not think we need to check the same unit conversion at multiple values -- Most of the actual conversion machinery is provided by the external units package and we trust them to test their own implementation, so our goal here should be to verify support for units that are important to us rather than to exhaustively test specific input values.

Copy link
Member

Choose a reason for hiding this comment

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

Or in less words: "Let's only check each unit pair once"

@@ -0,0 +1,71 @@
context("Unit Conversion Refactoring Tests")
Copy link
Member

Choose a reason for hiding this comment

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

Same comment as above that these tests belong in base/utils/tests/testthat/test-ud_convert.R

Comment on lines +65 to +66
expect_error(ud_convert(100, "gC/m2", "kgC/m2"))
expect_error(ud_convert(86400, "gC/m2/d", "kgC/m2/s"))
Copy link
Member

Choose a reason for hiding this comment

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

I like that you're taking a problem seen during development and trying to turn it into a test! But I don't think these expectations are useful to us -- they will only fail if udunits does learn how to parse C, and alas having the test case here won't keep users from making the same typo elsewhere.

It's possible that C is a common enough special case that it'd be nice for PEcAn's ud_convert to handle it explicitly (either converting it correctly or throwing a more informative error), but I suspect that would be a lot of tricky work for little practical gain.

Comment on lines +68 to +70
# But these (without 'C') should work
expect_equal(ud_convert(100, "g/m2", "kg/m2"), 0.1)
expect_equal(ud_convert(86400, "g/m2/d", "kg/m2/s"), 0.001, tolerance = 1e-10)
Copy link
Member

Choose a reason for hiding this comment

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

already checked above

ayushman1210 and others added 7 commits December 26, 2025 15:36
Co-authored-by: Chris Black <chris@ckblack.org>
Co-authored-by: Chris Black <chris@ckblack.org>
Co-authored-by: Chris Black <chris@ckblack.org>
Co-authored-by: Chris Black <chris@ckblack.org>
Co-authored-by: Chris Black <chris@ckblack.org>
Co-authored-by: Chris Black <chris@ckblack.org>
Co-authored-by: Chris Black <chris@ckblack.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants