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 "scipp/core/array_to_string.h" 6 : #include "scipp/core/dimensions.h" 7 : #include "scipp/core/eigen.h" 8 : #include "scipp/core/element_array_view.h" 9 : #include "scipp/core/except.h" 10 : #include "scipp/core/has_eval.h" 11 : #include "scipp/units/unit.h" 12 : #include "scipp/variable/element_array_model.h" 13 : #include "scipp/variable/except.h" 14 : #include "scipp/variable/variable.h" 15 : #include "scipp/variable/variable_factory.h" 16 : 17 : namespace scipp::variable { 18 : 19 : template <class T> struct model { 20 : using type = ElementArrayModel<T>; 21 : }; 22 : template <class T> using model_t = typename model<T>::type; 23 : 24 : namespace { 25 : 26 10271325 : template <class T, class C> auto &requireT(C &varconcept) { 27 10271325 : if (varconcept.dtype() != dtype<typename T::value_type>) 28 32 : throw except::TypeError("Expected item dtype " + 29 : to_string(T::static_dtype()) + ", got " + 30 16 : to_string(varconcept.dtype()) + '.'); 31 10271309 : return static_cast<T &>(varconcept); 32 : } 33 : 34 4785264 : template <class T> const auto &cast(const Variable &var) { 35 4785264 : return requireT<const model_t<T>>(var.data()); 36 : } 37 : 38 3094242 : template <class T> auto &cast(Variable &var) { 39 3094242 : return requireT<model_t<T>>(var.data()); 40 : } 41 : 42 70 : template <int I, class T> decltype(auto) get(T &&t) { 43 : if constexpr (std::is_same_v<std::decay_t<T>, Eigen::Affine3d>) { 44 9 : return t.matrix().operator()(I); 45 : } else if constexpr (core::has_eval_v<std::decay_t<T>> || 46 : std::is_same_v<std::decay_t<T>, 47 : scipp::core::Quaternion> || 48 : std::is_same_v<std::decay_t<T>, 49 : scipp::core::Translation>) { 50 61 : return t.operator()(I); 51 : } else { 52 : return std::get<I>(t); 53 : } 54 : } 55 : 56 : template <class T> 57 803697 : auto make_model(const units::Unit unit, const Dimensions &dimensions, 58 : element_array<T> values, 59 : std::optional<element_array<T>> variances) { 60 : if constexpr (std::is_same_v<model_t<T>, ElementArrayModel<T>>) { 61 : return std::make_unique<model_t<T>>( 62 1565145 : dimensions.volume(), unit, std::move(values), std::move(variances)); 63 : } else { 64 : // There is an extra copy caused here, but in practice this constructor 65 : // should not be used much outside unit tests. 66 : using Elem = typename model_t<T>::element_type; 67 21120 : element_array<Elem> elems; 68 21120 : if (values) { 69 21084 : auto begin = static_cast<Elem *>(&get<0>(*values.begin())); 70 21084 : auto end = begin + model_t<T>::element_count * values.size(); 71 21084 : elems = element_array<Elem>{begin, end}; 72 : } 73 21120 : return std::make_unique<model_t<T>>(dimensions.volume(), unit, 74 63360 : std::move(elems)); 75 21120 : } 76 : } 77 : 78 : /// See also default_unit_for, for similar runtime functionality. 79 : template <class T> 80 803697 : units::Unit unit_for_dtype(const std::optional<units::Unit> &unit) { 81 803697 : if (unit.has_value()) 82 644691 : return *unit; 83 159006 : return default_unit_for(dtype<T>); 84 : } 85 : 86 : } // namespace 87 : 88 : template <class T> 89 803697 : Variable::Variable(const std::optional<units::Unit> &unit, 90 : const Dimensions &dimensions, T values_, 91 : std::optional<T> variances_) 92 803697 : : m_dims(dimensions), m_strides(dimensions), 93 1607412 : m_object( 94 : make_model(unit_for_dtype<typename std::decay_t<T>::value_type>(unit), 95 2411100 : dimensions, std::move(values_), std::move(variances_))) {} 96 : 97 4417514 : template <class T> ElementArrayView<const T> Variable::values() const { 98 4417514 : return cast<T>(*this).values(array_params()); 99 : } 100 3039019 : template <class T> ElementArrayView<T> Variable::values() { 101 3039019 : return cast<T>(*this).values(array_params()); 102 : } 103 318569 : template <class T> ElementArrayView<const T> Variable::variances() const { 104 : if constexpr (!core::canHaveVariances<T>()) 105 0 : except::throw_cannot_have_variances(core::dtype<T>); 106 : else 107 318569 : return cast<T>(*this).variances(array_params()); 108 : } 109 55223 : template <class T> ElementArrayView<T> Variable::variances() { 110 : if constexpr (!core::canHaveVariances<T>()) 111 0 : except::throw_cannot_have_variances(core::dtype<T>); 112 : else 113 55223 : return cast<T>(*this).variances(array_params()); 114 : } 115 : 116 : #define INSTANTIATE_VARIABLE_BASE(name, ...) \ 117 : namespace { \ 118 : auto register_dtype_name_##name( \ 119 : (core::dtypeNameRegistry().emplace(dtype<__VA_ARGS__>, #name), 0)); \ 120 : } \ 121 : template SCIPP_EXPORT ElementArrayView<const __VA_ARGS__> Variable::values() \ 122 : const; \ 123 : template SCIPP_EXPORT ElementArrayView<__VA_ARGS__> Variable::values(); 124 : 125 1 : template <class T> std::string Formatter<T>::format(const Variable &var) const { 126 1 : if (var.ndim() == 0) 127 1 : return core::scalar_array_to_string(var.template values<T>(), var.unit()); 128 0 : return array_to_string(var.template values<T>()); 129 : } 130 : 131 : /// Insert classes into formatting registry. The objects themselves do nothing, 132 : /// but the constructor call with comma operator does the insertion. Calling 133 : /// this is required for formatting all but basic builtin types. 134 : #define REGISTER_FORMATTER(name, ...) \ 135 : namespace { \ 136 : auto register_##name( \ 137 : (variable::formatterRegistry().emplace( \ 138 : dtype<__VA_ARGS__>, \ 139 : std::make_unique<variable::Formatter<__VA_ARGS__>>()), \ 140 : 0)); \ 141 : } 142 : 143 : } // namespace scipp::variable