// SPDX-License-Identifier: LGPL-2.1-or-later /**************************************************************************** * * * This file is part of FreeCAD. * * * * FreeCAD is free software: you can redistribute it and/or modify it * * under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation, either version 2.1 of the * * License, or (at your option) any later version. * * * * FreeCAD is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with FreeCAD. If not, see * * . * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include "Unit.h" using namespace Base; struct UnitSpec { std::string_view name; UnitExponents exps; }; constexpr auto unitSpecs = std::to_array({ // clang-format off // Length // . Mass // . . Time // . . . ElectricCurrent // . . . . ThermodynamicTemperature // . . . . . AmountOfSubstance // . . . . . . LuminousIntensity // . . . . . . . Angle { "1" , { 0, 0, 0, 0, 0, 0, 0, 0 } }, { "Length" , { 1 } }, { "Mass" , { 0, 1 } }, { "TimeSpan" , { 0, 0, 1 } }, { "ElectricCurrent" , { 0, 0, 0, 1 } }, { "Temperature" , { 0, 0, 0, 0, 1 } }, { "AmountOfSubstance" , { 0, 0, 0, 0, 0, 1 } }, { "LuminousIntensity" , { 0, 0, 0, 0, 0, 0, 1 } }, { "Angle" , { 0, 0, 0, 0, 0, 0, 0, 1 } }, { "Acceleration" , { 1, 0, -2 } }, { "AngleOfFriction" , { 0, 0, 0, 0, 0, 0, 0, 1 } }, { "Area" , { 2 } }, { "CurrentDensity" , { -2, 0, 0, 1 } }, { "Density" , { -3, 1 } }, { "DissipationRate" , { 2, 0, -3 } }, { "DynamicViscosity" , { -1, 1, -1 } }, { "ElectricalCapacitance" , { -2, -1, 4, 2 } }, { "ElectricalConductance" , { -2, -1, 3, 2 } }, { "ElectricalConductivity" , { -3, -1, 3, 2 } }, { "ElectricalInductance" , { 2, 1, -2, -2 } }, { "ElectricalResistance" , { 2, 1, -3, -2 } }, { "ElectricCharge" , { 0, 0, 1, 1 } }, { "ElectricPotential" , { 2, 1, -3, -1 } }, { "ElectromagneticPotential" , { 1, 1, -2, -1 } }, { "Force" , { 1, 1, -2 } }, { "Frequency" , { 0, 0, -1 } }, { "HeatFlux" , { 0, 1, -3 } }, { "InverseArea" , { -2 } }, { "InverseLength" , { -1 } }, { "InverseVolume" , { -3 } }, { "KinematicViscosity" , { 2, 0, -1 } }, { "MagneticFieldStrength" , { -1, 0, 0, 1 } }, { "MagneticFlux" , { 2, 1, -2, -1 } }, { "MagneticFluxDensity" , { 0, 1, -2, -1 } }, { "Magnetization" , { -1, 0, 0, 1 } }, { "Moment" , { 2, 1, -2 } }, { "Pressure" , { -1, 1, -2 } }, { "Power" , { 2, 1, -3 } }, { "ShearModulus" , { -1, 1, -2 } }, { "SpecificEnergy" , { 2, 0, -2 } }, { "SpecificHeat" , { 2, 0, -2, 0, -1 } }, { "Stiffness" , { 0, 1, -2 } }, { "StiffnessDensity" , { -2, 1, -2 } }, { "Stress" , { -1, 1, -2 } }, { "SurfaceChargeDensity" , { -2, 0, 1, 1 } }, { "ThermalConductivity" , { 1, 1, -3, 0, -1 } }, { "ThermalExpansionCoefficient" , { 0, 0, 0, 0, -1 } }, { "ThermalTransferCoefficient" , { 0, 1, -3, 0, -1 } }, { "UltimateTensileStrength" , { -1, 1, -2 } }, { "VacuumPermittivity" , { -3, -1, 4, 2 } }, { "Velocity" , { 1, 0, -1 } }, { "Volume" , { 3 } }, { "VolumeChargeDensity" , { -3, 0, 1, 1 } }, { "VolumeFlowRate" , { 3, 0, -1 } }, { "VolumetricThermalExpansionCoefficient" , { 0, 0, 0, 0, -1 } }, { "Work" , { 2, 1, -2 } }, { "YieldStrength" , { -1, 1, -2 } }, { "YoungsModulus" , { -1, 1, -2 } }, }); // clang-format on Unit::Unit( const int length, // NOLINT const int mass, const int time, const int electricCurrent, const int thermodynamicTemperature, const int amountOfSubstance, const int luminousIntensity, const int angle ) : _name {""} { auto cast = [](auto val) { return static_cast(std::clamp( val, std::numeric_limits::min(), std::numeric_limits::max() )); }; _exps[0] = cast(length); _exps[1] = cast(mass); _exps[2] = cast(time); _exps[3] = cast(electricCurrent); _exps[4] = cast(thermodynamicTemperature); _exps[5] = cast(amountOfSubstance); _exps[6] = cast(luminousIntensity); _exps[7] = cast(angle); checkRange(); } bool Unit::operator==(const Unit& that) const { return _exps == that._exps; } bool Unit::operator!=(const Unit& that) const { return _exps != that._exps; } Unit& Unit::operator*=(const Unit& that) { *this = *this * that; return *this; } Unit& Unit::operator/=(const Unit& that) { *this = *this / that; return *this; } Unit Unit::operator*(const Unit& right) const { auto mult = [&](auto leftExponent, auto rightExponent) { return leftExponent + rightExponent; }; UnitExponents res {}; std::transform(_exps.begin(), _exps.end(), right._exps.begin(), res.begin(), mult); return Unit {res}; } Unit Unit::operator/(const Unit& right) const { auto div = [&](auto leftExponent, auto rightExponent) { return leftExponent - rightExponent; }; UnitExponents res {}; std::transform(_exps.begin(), _exps.end(), right._exps.begin(), res.begin(), div); return Unit {res}; } Unit Unit::root(const uint8_t num) const { auto apply = [&](auto val) { if (val % num != 0) { throw UnitsMismatchError("unit values must be divisible by root"); } return static_cast(val / num); }; if (num < 1) { throw UnitsMismatchError("root must be > 0"); } UnitExponents res {}; std::transform(_exps.begin(), _exps.end(), res.begin(), apply); return Unit {res}; } Unit Unit::pow(const double exp) const { auto apply = [&](const auto val) { const auto num {val * exp}; if (std::fabs(std::round(num) - num) >= std::numeric_limits::epsilon()) { throw UnitsMismatchError("pow() of unit not possible"); } return static_cast(val * exp); }; UnitExponents res {}; std::transform(_exps.begin(), _exps.end(), res.begin(), apply); return Unit {res}; } UnitExponents Unit::exponents() const { return _exps; } int Unit::length() const { return _exps[0]; } std::string Unit::getString() const { auto buildSubStr = [&](auto index) { const std::string unitStrString {unitSymbols.at(index)}; const auto absol {abs(_exps.at(index))}; return absol <= 1 ? unitStrString : fmt::format("{}^{}", unitStrString, std::to_string(absol)); }; auto buildStr = [&](auto indexes) { std::vector subStrings {}; std::transform(indexes.begin(), indexes.end(), std::back_inserter(subStrings), buildSubStr); return fmt::format("{}", fmt::join(subStrings, "*")); }; //------------------------------------------------------------------------------ auto [posValIndexes, negValIndexes] = nonZeroValsIndexes(); auto numeratorStr = buildStr(posValIndexes); if (negValIndexes.empty()) { return numeratorStr; } auto denominatorStr = buildStr(negValIndexes); return fmt::format( "{}/{}", numeratorStr.empty() ? "1" : numeratorStr, negValIndexes.size() > 1 ? fmt::format("({})", denominatorStr) : denominatorStr ); } std::string Unit::representation() const { auto name = getTypeString(); auto inParen = fmt::format("Unit: {} ({})", getString(), fmt::join(_exps, ",")); return name.empty() ? inParen : fmt::format("{} [{}]", inParen, name); } std::string Unit::getTypeString() const { if (_name.empty()) { const auto spec = std::ranges::find(unitSpecs, _exps, &UnitSpec::exps); return std::string(spec == unitSpecs.end() ? "" : spec->name); } return std::string {_name.data(), _name.size()}; } std::pair, std::vector> Unit::nonZeroValsIndexes() const { std::vector pos {}; std::vector neg {}; auto posNeg = [&, index {0}](auto val) mutable { if (val != 0) { (val > 0 ? pos : neg).push_back(index); } ++index; }; std::ranges::for_each(_exps, posNeg); return {pos, neg}; } constexpr Unit make(const std::string_view name) { if (const auto spec = std::ranges::find(unitSpecs, name, &UnitSpec::name); spec != unitSpecs.end()) { return Unit {spec->exps, spec->name}; } throw NameError("Invalid unit name"); } // clang-format off constexpr Unit Unit::One = make("1" ); constexpr Unit Unit::Length = make("Length" ); constexpr Unit Unit::Mass = make("Mass" ); constexpr Unit Unit::TimeSpan = make("TimeSpan" ); constexpr Unit Unit::ElectricCurrent = make("ElectricCurrent" ); constexpr Unit Unit::Temperature = make("Temperature" ); constexpr Unit Unit::AmountOfSubstance = make("AmountOfSubstance" ); constexpr Unit Unit::LuminousIntensity = make("LuminousIntensity" ); constexpr Unit Unit::Angle = make("Angle" ); constexpr Unit Unit::Acceleration = make("Acceleration" ); constexpr Unit Unit::AngleOfFriction = make("Angle" ); constexpr Unit Unit::Area = make("Area" ); constexpr Unit Unit::CompressiveStrength = make("Pressure" ); constexpr Unit Unit::CurrentDensity = make("CurrentDensity" ); constexpr Unit Unit::Density = make("Density" ); constexpr Unit Unit::DissipationRate = make("DissipationRate" ); constexpr Unit Unit::DynamicViscosity = make("DynamicViscosity" ); constexpr Unit Unit::ElectricalCapacitance = make("ElectricalCapacitance" ); constexpr Unit Unit::ElectricalConductance = make("ElectricalConductance" ); constexpr Unit Unit::ElectricalConductivity = make("ElectricalConductivity" ); constexpr Unit Unit::ElectricalInductance = make("ElectricalInductance" ); constexpr Unit Unit::ElectricalResistance = make("ElectricalResistance" ); constexpr Unit Unit::ElectricCharge = make("ElectricCharge" ); constexpr Unit Unit::ElectricPotential = make("ElectricPotential" ); constexpr Unit Unit::ElectromagneticPotential = make("ElectromagneticPotential" ); constexpr Unit Unit::Force = make("Force" ); constexpr Unit Unit::Frequency = make("Frequency" ); constexpr Unit Unit::HeatFlux = make("HeatFlux" ); constexpr Unit Unit::InverseArea = make("InverseArea" ); constexpr Unit Unit::InverseLength = make("InverseLength" ); constexpr Unit Unit::InverseVolume = make("InverseVolume" ); constexpr Unit Unit::KinematicViscosity = make("KinematicViscosity" ); constexpr Unit Unit::MagneticFieldStrength = make("MagneticFieldStrength" ); constexpr Unit Unit::MagneticFlux = make("MagneticFlux" ); constexpr Unit Unit::MagneticFluxDensity = make("MagneticFluxDensity" ); constexpr Unit Unit::Magnetization = make("Magnetization" ); constexpr Unit Unit::Moment = make("Moment" ); constexpr Unit Unit::Pressure = make("Pressure" ); constexpr Unit Unit::Power = make("Power" ); constexpr Unit Unit::ShearModulus = make("Pressure" ); constexpr Unit Unit::SpecificEnergy = make("SpecificEnergy" ); constexpr Unit Unit::SpecificHeat = make("SpecificHeat" ); constexpr Unit Unit::Stiffness = make("Stiffness" ); constexpr Unit Unit::StiffnessDensity = make("StiffnessDensity" ); constexpr Unit Unit::Stress = make("Pressure" ); constexpr Unit Unit::SurfaceChargeDensity = make("SurfaceChargeDensity" ); constexpr Unit Unit::ThermalConductivity = make("ThermalConductivity" ); constexpr Unit Unit::ThermalExpansionCoefficient = make("ThermalExpansionCoefficient" ); constexpr Unit Unit::ThermalTransferCoefficient = make("ThermalTransferCoefficient" ); constexpr Unit Unit::UltimateTensileStrength = make("Pressure" ); constexpr Unit Unit::VacuumPermittivity = make("VacuumPermittivity" ); constexpr Unit Unit::Velocity = make("Velocity" ); constexpr Unit Unit::Volume = make("Volume" ); constexpr Unit Unit::VolumeChargeDensity = make("VolumeChargeDensity" ); constexpr Unit Unit::VolumeFlowRate = make("VolumeFlowRate" ); constexpr Unit Unit::VolumetricThermalExpansionCoefficient = make("ThermalExpansionCoefficient" ); constexpr Unit Unit::Work = make("Work" ); constexpr Unit Unit::YieldStrength = make("Pressure" ); constexpr Unit Unit::YoungsModulus = make("Pressure" );