src/core/utils/unit.cpp
Implementation of unit system. More…
Detailed Description
Implementation of unit system.
Copyright: Copyright (c) 2017-2024 CERN and the Allpix Squared authors. This software is distributed under the terms of the MIT License, copied verbatim in the file “LICENSE.md”. In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an Intergovernmental Organization or submit itself to any jurisdiction. SPDX-License-Identifier: MIT
Source code
#include "unit.h"
#include <algorithm>
#include <cmath>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include "text.h"
using namespace allpix;
std::map<std::string, allpix::Units::UnitType> Units::unit_map_;
void Units::add(std::string str, UnitType value) {
// Do not distinguish between different case for units
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
// Find the unit
if(unit_map_.find(str) != unit_map_.end()) {
throw std::invalid_argument("unit " + str + " already defined");
}
unit_map_.emplace(str, value);
}
allpix::Units::UnitType Units::getSingle(std::string str) {
if(allpix::trim(str).empty()) {
// An empty unit equals a multiplication with one:
return 1.;
}
// Do not distinguish between different case for units
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
// Find the unit
auto iter = unit_map_.find(str);
if(iter == unit_map_.end()) {
throw std::invalid_argument("unit " + str + " not found");
}
return iter->second;
}
allpix::Units::UnitType Units::get(const std::string& str) {
UnitType ret_value = 1;
if(allpix::trim(str).empty()) {
return ret_value;
}
// Go through all units
char lst = '*';
std::string unit;
for(char ch : str) {
if(ch == '*' || ch == '/') {
if(lst == '*') {
ret_value = getSingle(ret_value, unit);
unit.clear();
} else if(lst == '/') {
ret_value = getSingleInverse(ret_value, unit);
unit.clear();
}
lst = ch;
} else {
unit += ch;
}
}
// Apply last unit
if(lst == '*') {
ret_value = getSingle(ret_value, std::move(unit));
} else if(lst == '/') {
ret_value = getSingleInverse(ret_value, std::move(unit));
}
return ret_value;
}
allpix::Units::UnitType Units::convert(UnitType input, std::string str) {
// Do not distinguish between different case for units
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
// Go through all units
char lst = '*';
std::string unit;
for(char ch : str) {
if(ch == '*' || ch == '/') {
if(lst == '*') {
input = getSingleInverse(input, unit);
unit.clear();
} else if(lst == '/') {
input = getSingle(input, unit);
unit.clear();
}
lst = ch;
} else {
unit += ch;
}
}
// Handle last unit
if(lst == '*') {
input = getSingleInverse(input, std::move(unit));
} else if(lst == '/') {
input = getSingle(input, std::move(unit));
}
return input;
}
std::string Units::display(UnitType input, std::initializer_list<std::string> units) {
if(units.size() == 0) {
throw std::invalid_argument("list of possible units cannot be empty");
}
std::ostringstream stream;
// Find best unit
int best_exponent = std::numeric_limits<int>::min();
std::string best_unit;
for(const auto& unit : units) {
Units::UnitType value = convert(input, unit);
int exponent = 0;
std::frexp(value, &exponent);
if((best_exponent <= 0 && exponent > best_exponent) || (exponent > 0 && exponent < best_exponent)) {
best_exponent = exponent;
best_unit = unit;
}
}
// Write unit
stream << convert(input, best_unit);
stream << best_unit;
return stream.str();
}
std::string Units::display(UnitType input, std::string unit) { return display(input, {std::move(unit)}); }
Updated on 2025-02-27 at 14:14:46 +0000