LCOV - code coverage report
Current view: top level - variable - to_unit.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 34 34 100.0 %
Date: 2024-12-01 01:56:34 Functions: 2 2 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: BSD-3-Clause
       2             : // Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
       3             : /// @file
       4             : /// @author Simon Heybrock
       5             : #include <cmath>
       6             : #include <units/units.hpp>
       7             : 
       8             : #include "scipp/core/element/to_unit.h"
       9             : #include "scipp/core/time_point.h"
      10             : #include "scipp/variable/arithmetic.h"
      11             : #include "scipp/variable/astype.h"
      12             : #include "scipp/variable/to_unit.h"
      13             : #include "scipp/variable/transform.h"
      14             : 
      15             : namespace scipp::variable {
      16             : 
      17             : namespace {
      18         291 : bool greater_than_days(const units::Unit &unit) {
      19             :   static constexpr auto days_multiplier =
      20             :       llnl::units::precise::day.multiplier();
      21         291 :   if (!unit.has_same_base(units::s)) {
      22           1 :     throw except::UnitError("Cannot convert unit of datetime with non-time "
      23           1 :                             "unit, got `" +
      24           3 :                             to_string(unit) + "`.");
      25             :   }
      26         290 :   return unit.underlying().multiplier() >= days_multiplier;
      27             : }
      28             : } // namespace
      29             : 
      30       60163 : Variable to_unit(const Variable &var, const units::Unit &unit,
      31             :                  const CopyPolicy copy) {
      32       60163 :   const auto var_unit = variableFactory().elem_unit(var);
      33       60163 :   if (unit == var_unit)
      34       50906 :     return copy == CopyPolicy::Always ? variable::copy(var) : var;
      35        9257 :   if ((var_unit == units::none) || (unit == units::none))
      36           2 :     throw except::UnitError("Unit conversion to / from None is not permitted.");
      37             :   const auto scale =
      38        9255 :       llnl::units::quick_convert(var_unit.underlying(), unit.underlying());
      39        9255 :   if (std::isnan(scale))
      40          57 :     throw except::UnitError("Conversion from `" + to_string(var_unit) +
      41          76 :                             "` to `" + to_string(unit) + "` is not valid.");
      42        9391 :   if (var.dtype() == dtype<core::time_point> &&
      43        9390 :       (greater_than_days(variableFactory().elem_unit(var)) ||
      44         136 :        greater_than_days(unit))) {
      45          30 :     throw except::UnitError(
      46             :         "Unit conversions for datetimes with a unit of days or greater "
      47          30 :         "are not supported. Attempted conversion from `" +
      48          90 :         to_string(var_unit) + "` to `" + to_string(unit) + "`. " +
      49             :         "This limitation exists because such conversions would require "
      50          60 :         "information about calendars and time zones.");
      51             :   }
      52        9205 :   Variable scalevar;
      53             :   // Need to make sure that errors due to machine precision actually affect
      54             :   // decimal places, otherwise the approach based on std::round will do nothing.
      55        9205 :   const auto base_scale = scale > 1e6 ? scale * 1e-6 : scale;
      56       18410 :   if (const auto iscale = std::round(base_scale);
      57        9205 :       (std::abs(base_scale - iscale) < 1e-12 * std::abs(base_scale))) {
      58        3028 :     if (var.dtype() == dtype<int64_t> || var.dtype() == dtype<core::time_point>)
      59          96 :       scalevar = int64_t{scale > 1e6 ? 1000000 : 1} *
      60          96 :                  static_cast<int64_t>(iscale) * unit;
      61             :     else
      62        2932 :       scalevar = (scale > 1e6 ? 1000000 : 1) * iscale * unit;
      63             :   } else {
      64        6177 :     scalevar = scale * unit;
      65             :   }
      66        9205 :   return variable::transform(var, scalevar, core::element::to_unit, "to_unit");
      67        9205 : }
      68             : 
      69             : } // namespace scipp::variable

Generated by: LCOV version 1.14