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