Line data Source code
1 : // SPDX-License-Identifier: BSD-3-Clause 2 : // Copyright (c) 2023 Scipp contributors (https://github.com/scipp) 3 : 4 : #include "unit.h" 5 : 6 : #include "scipp/common/overloaded.h" 7 : #include "scipp/core/bucket.h" 8 : #include "scipp/core/dtype.h" 9 : #include "scipp/core/eigen.h" 10 : #include "scipp/core/time_point.h" 11 : #include "scipp/units/string.h" 12 : #include "scipp/variable/variable.h" 13 : 14 : #include "dtype.h" 15 : 16 : using namespace scipp; 17 : namespace py = pybind11; 18 : 19 : namespace { 20 613 : bool temporal_or_dimensionless(const units::Unit unit) { 21 613 : return unit == units::one || unit.has_same_base(units::s); 22 : } 23 : } // namespace 24 : 25 : std::tuple<units::Unit, int64_t> 26 0 : get_time_unit(const std::optional<scipp::units::Unit> value_unit, 27 : const std::optional<scipp::units::Unit> dtype_unit, 28 : const units::Unit sc_unit) { 29 0 : if (!temporal_or_dimensionless(sc_unit)) { 30 0 : throw except::UnitError("Invalid unit for dtype=datetime64: " + 31 0 : to_string(sc_unit)); 32 : } 33 0 : if (dtype_unit.value_or(units::one) != units::one && 34 0 : (sc_unit != units::one && *dtype_unit != sc_unit)) { 35 0 : throw std::invalid_argument( 36 0 : "dtype (datetime64[" + to_string(*dtype_unit) + 37 0 : "]) has a different time unit from 'unit' argument (" + 38 0 : to_string(sc_unit) + ")"); 39 : } 40 0 : units::Unit actual_unit = units::one; 41 0 : if (sc_unit != units::one) 42 0 : actual_unit = sc_unit; 43 0 : else if (dtype_unit.value_or(units::one) != units::one) 44 0 : actual_unit = *dtype_unit; 45 0 : else if (value_unit.has_value()) 46 0 : actual_unit = *value_unit; 47 : 48 : // TODO implement 49 0 : if (value_unit && value_unit != actual_unit) { 50 0 : throw std::runtime_error("Conversion of time units is not implemented."); 51 : } 52 : 53 0 : return {actual_unit, 1}; 54 : } 55 : 56 0 : std::tuple<units::Unit, int64_t> get_time_unit(const py::buffer &value, 57 : const py::object &dtype, 58 : const units::Unit unit) { 59 : return get_time_unit( 60 0 : value.is_none() || value.attr("dtype").attr("kind").cast<char>() != 'M' 61 : ? std::optional<units::Unit>{} 62 0 : : parse_datetime_dtype(value), 63 0 : dtype.is_none() ? std::optional<units::Unit>{} 64 0 : : parse_datetime_dtype(dtype), 65 0 : unit); 66 : } 67 : 68 : template <> 69 : std::tuple<scipp::units::Unit, scipp::units::Unit> 70 613 : common_unit<scipp::core::time_point>(const pybind11::object &values, 71 : const scipp::units::Unit unit) { 72 613 : if (!temporal_or_dimensionless(unit)) { 73 1 : throw except::UnitError("Invalid unit for dtype=datetime64: " + 74 2 : to_string(unit)); 75 : } 76 : 77 612 : if (values.is_none() || !has_datetime_dtype(values)) { 78 49 : return std::tuple{unit, unit}; 79 : } 80 : 81 563 : const auto value_unit = parse_datetime_dtype(values); 82 563 : if (unit == units::one) { 83 347 : return std::tuple{value_unit, value_unit}; 84 : } else { 85 216 : return std::tuple{value_unit, unit}; 86 : } 87 : } 88 : 89 209 : std::string to_numpy_time_string(const scipp::units::Unit unit) { 90 209 : if (unit == units::m) { 91 : // Would be treated as minute otherwise. 92 1 : throw except::UnitError("Invalid time unit, got 'm' which means meter. " 93 2 : "If you meant minute, use unit='min' instead."); 94 : } 95 208 : return unit == units::us ? std::string("us") 96 386 : : unit == units::Unit("min") ? std::string("m") 97 594 : : to_string(unit); 98 : } 99 : 100 15 : std::string to_numpy_time_string(const ProtoUnit &unit) { 101 : return std::visit( 102 13 : overloaded{ 103 0 : [](const scipp::units::Unit &u) { return to_numpy_time_string(u); }, 104 14 : [](const std::string &u) { 105 14 : return to_numpy_time_string(scipp::units::Unit(u)); 106 : }, 107 1 : [](const auto &) { return std::string(); }}, 108 28 : unit); 109 : } 110 : 111 52774 : scipp::units::Unit unit_or_default(const ProtoUnit &unit, const DType type) { 112 52774 : return std::visit( 113 105545 : overloaded{[type](DefaultUnit) { 114 1753 : if (type == dtype<void>) 115 0 : throw except::UnitError( 116 0 : "Default unit requested but dtype unknown."); 117 1753 : return variable::default_unit_for(type); 118 : }, 119 1418 : [](const py::none &) { return units::none; }, 120 14016 : [](const std::string &u) { return units::Unit(u); }, 121 35587 : [](const units::Unit &u) { return u; }}, 122 105542 : unit); 123 : }