From 2e0df80e8446d6f23c6e30637cfc2b32e975cf6d Mon Sep 17 00:00:00 2001 From: bofdahof <172177156+bofdahof@users.noreply.github.com> Date: Wed, 1 Jan 2025 18:17:00 +1000 Subject: [PATCH] Base: Units: refactor Refactor Units making it constexpr, immutable, with repetition reduced. Separate data from code. Constexpr constructed units allow constructing predefined Quantities from predefined unit types. --- src/Base/Quantity.cpp | 240 +++--- src/Base/QuantityPyImp.cpp | 53 +- src/Base/Unit.cpp | 998 +++++++--------------- src/Base/Unit.h | 268 +++--- src/Base/UnitPyImp.cpp | 56 +- src/Gui/Dialogs/DlgUnitsCalculatorImp.cpp | 14 +- tests/src/App/ExpressionParser.cpp | 10 +- tests/src/Base/Unit.cpp | 245 +++--- 8 files changed, 729 insertions(+), 1155 deletions(-) diff --git a/src/Base/Quantity.cpp b/src/Base/Quantity.cpp index a9c6fcb467..4d10a79dc0 100644 --- a/src/Base/Quantity.cpp +++ b/src/Base/Quantity.cpp @@ -294,162 +294,150 @@ void Quantity::setInvalid() } // === Predefined types ===================================================== +// clang-format off +const Quantity Quantity::NanoMetre ( 1.0e-6 , Unit::Length ); +const Quantity Quantity::MicroMetre ( 1.0e-3 , Unit::Length ); +const Quantity Quantity::MilliMetre ( 1.0 , Unit::Length ); +const Quantity Quantity::CentiMetre ( 10.0 , Unit::Length ); +const Quantity Quantity::DeciMetre ( 100.0 , Unit::Length ); +const Quantity Quantity::Metre ( 1.0e3 , Unit::Length ); +const Quantity Quantity::KiloMetre ( 1.0e6 , Unit::Length ); -const Quantity Quantity::NanoMetre(1.0e-6, Unit(1)); -const Quantity Quantity::MicroMetre(1.0e-3, Unit(1)); -const Quantity Quantity::MilliMetre(1.0, Unit(1)); -const Quantity Quantity::CentiMetre(10.0, Unit(1)); -const Quantity Quantity::DeciMetre(100.0, Unit(1)); -const Quantity Quantity::Metre(1.0e3, Unit(1)); -const Quantity Quantity::KiloMetre(1.0e6, Unit(1)); +const Quantity Quantity::MilliLiter ( 1000.0 , Unit::Volume ); +const Quantity Quantity::Liter ( 1.0e6 , Unit::Volume ); -const Quantity Quantity::MilliLiter(1000.0, Unit(3)); -const Quantity Quantity::Liter(1.0e6, Unit(3)); +const Quantity Quantity::Hertz ( 1.0 , Unit::Frequency ); +const Quantity Quantity::KiloHertz ( 1.0e3 , Unit::Frequency ); +const Quantity Quantity::MegaHertz ( 1.0e6 , Unit::Frequency ); +const Quantity Quantity::GigaHertz ( 1.0e9 , Unit::Frequency ); +const Quantity Quantity::TeraHertz ( 1.0e12 , Unit::Frequency ); -const Quantity Quantity::Hertz(1.0, Unit(0, 0, -1)); -const Quantity Quantity::KiloHertz(1.0e3, Unit(0, 0, -1)); -const Quantity Quantity::MegaHertz(1.0e6, Unit(0, 0, -1)); -const Quantity Quantity::GigaHertz(1.0e9, Unit(0, 0, -1)); -const Quantity Quantity::TeraHertz(1.0e12, Unit(0, 0, -1)); +const Quantity Quantity::MicroGram ( 1.0e-9 , Unit::Mass ); +const Quantity Quantity::MilliGram ( 1.0e-6 , Unit::Mass ); +const Quantity Quantity::Gram ( 1.0e-3 , Unit::Mass ); +const Quantity Quantity::KiloGram ( 1.0 , Unit::Mass ); +const Quantity Quantity::Ton ( 1.0e3 , Unit::Mass ); -const Quantity Quantity::MicroGram(1.0e-9, Unit(0, 1)); -const Quantity Quantity::MilliGram(1.0e-6, Unit(0, 1)); -const Quantity Quantity::Gram(1.0e-3, Unit(0, 1)); -const Quantity Quantity::KiloGram(1.0, Unit(0, 1)); -const Quantity Quantity::Ton(1.0e3, Unit(0, 1)); +const Quantity Quantity::Second ( 1.0 , Unit::TimeSpan ); +const Quantity Quantity::Minute ( 60.0 , Unit::TimeSpan ); +const Quantity Quantity::Hour ( 3600.0 , Unit::TimeSpan ); -const Quantity Quantity::Second(1.0, Unit(0, 0, 1)); -const Quantity Quantity::Minute(60.0, Unit(0, 0, 1)); -const Quantity Quantity::Hour(3600.0, Unit(0, 0, 1)); +const Quantity Quantity::Ampere ( 1.0 , Unit::ElectricCurrent ); +const Quantity Quantity::MilliAmpere ( 0.001 , Unit::ElectricCurrent ); +const Quantity Quantity::KiloAmpere ( 1000.0 , Unit::ElectricCurrent ); +const Quantity Quantity::MegaAmpere ( 1.0e6 , Unit::ElectricCurrent ); -const Quantity Quantity::Ampere(1.0, Unit(0, 0, 0, 1)); -const Quantity Quantity::MilliAmpere(0.001, Unit(0, 0, 0, 1)); -const Quantity Quantity::KiloAmpere(1000.0, Unit(0, 0, 0, 1)); -const Quantity Quantity::MegaAmpere(1.0e6, Unit(0, 0, 0, 1)); +const Quantity Quantity::Kelvin ( 1.0 , Unit::Temperature ); +const Quantity Quantity::MilliKelvin ( 0.001 , Unit::Temperature ); +const Quantity Quantity::MicroKelvin ( 0.000001 , Unit::Temperature ); -const Quantity Quantity::Kelvin(1.0, Unit(0, 0, 0, 0, 1)); -const Quantity Quantity::MilliKelvin(0.001, Unit(0, 0, 0, 0, 1)); -const Quantity Quantity::MicroKelvin(0.000001, Unit(0, 0, 0, 0, 1)); +const Quantity Quantity::MilliMole ( 0.001 , Unit::AmountOfSubstance ); +const Quantity Quantity::Mole ( 1.0 , Unit::AmountOfSubstance ); -const Quantity Quantity::MilliMole(0.001, Unit(0, 0, 0, 0, 0, 1)); -const Quantity Quantity::Mole(1.0, Unit(0, 0, 0, 0, 0, 1)); +const Quantity Quantity::Candela ( 1.0 , Unit::LuminousIntensity ); -const Quantity Quantity::Candela(1.0, Unit(0, 0, 0, 0, 0, 0, 1)); +const Quantity Quantity::Inch ( 25.4 , Unit::Length ); +const Quantity Quantity::Foot ( 304.8 , Unit::Length ); +const Quantity Quantity::Thou ( 0.0254 , Unit::Length ); +const Quantity Quantity::Yard ( 914.4 , Unit::Length ); +const Quantity Quantity::Mile ( 1609344.0 , Unit::Length ); -const Quantity Quantity::Inch(25.4, Unit(1)); -const Quantity Quantity::Foot(304.8, Unit(1)); -const Quantity Quantity::Thou(0.0254, Unit(1)); -const Quantity Quantity::Yard(914.4, Unit(1)); -const Quantity Quantity::Mile(1609344.0, Unit(1)); +const Quantity Quantity::MilePerHour ( 447.04 , Unit::Velocity ); +const Quantity Quantity::SquareFoot ( 92903.04 , Unit::Area ); +const Quantity Quantity::CubicFoot ( 28316846.592 , Unit::Volume ); -const Quantity Quantity::MilePerHour(447.04, Unit(1, 0, -1)); -const Quantity Quantity::SquareFoot(92903.04, Unit(2)); -const Quantity Quantity::CubicFoot(28316846.592, Unit(3)); +const Quantity Quantity::Pound ( 0.45359237 , Unit::Mass ); +const Quantity Quantity::Ounce ( 0.0283495231 , Unit::Mass ); +const Quantity Quantity::Stone ( 6.35029318 , Unit::Mass ); +const Quantity Quantity::Hundredweights ( 50.80234544 , Unit::Mass ); -const Quantity Quantity::Pound(0.45359237, Unit(0, 1)); -const Quantity Quantity::Ounce(0.0283495231, Unit(0, 1)); -const Quantity Quantity::Stone(6.35029318, Unit(0, 1)); -const Quantity Quantity::Hundredweights(50.80234544, Unit(0, 1)); +const Quantity Quantity::PoundForce ( 4448.22 , Unit::Force ); // lbf are ~= 4.44822 Newton -const Quantity Quantity::PoundForce(4448.22, Unit(1, 1, -2)); // lbf are ~= 4.44822 Newton +const Quantity Quantity::Newton ( 1000.0 , Unit::Force ); // Newton (kg*m/s^2) +const Quantity Quantity::MilliNewton ( 1.0 , Unit::Force ); +const Quantity Quantity::KiloNewton ( 1e+6 , Unit::Force ); +const Quantity Quantity::MegaNewton ( 1e+9 , Unit::Force ); -const Quantity Quantity::Newton(1000.0, Unit(1, 1, -2)); // Newton (kg*m/s^2) -const Quantity Quantity::MilliNewton(1.0, Unit(1, 1, -2)); -const Quantity Quantity::KiloNewton(1e+6, Unit(1, 1, -2)); -const Quantity Quantity::MegaNewton(1e+9, Unit(1, 1, -2)); +const Quantity Quantity::NewtonPerMeter ( 1.00 , Unit::Stiffness ); // Newton per meter (N/m or kg/s^2) +const Quantity Quantity::MilliNewtonPerMeter ( 1e-3 , Unit::Stiffness ); +const Quantity Quantity::KiloNewtonPerMeter ( 1e3 , Unit::Stiffness ); +const Quantity Quantity::MegaNewtonPerMeter ( 1e6 , Unit::Stiffness ); -const Quantity Quantity::NewtonPerMeter(1.00, Unit(0, 1, -2)); // Newton per meter (N/m or kg/s^2) -const Quantity Quantity::MilliNewtonPerMeter(1e-3, Unit(0, 1, -2)); -const Quantity Quantity::KiloNewtonPerMeter(1e3, Unit(0, 1, -2)); -const Quantity Quantity::MegaNewtonPerMeter(1e6, Unit(0, 1, -2)); +const Quantity Quantity::Pascal ( 0.001 , Unit::CompressiveStrength ); // Pascal (kg/m/s^2 or N/m^2) +const Quantity Quantity::KiloPascal ( 1.00 , Unit::CompressiveStrength ); +const Quantity Quantity::MegaPascal ( 1000.0 , Unit::CompressiveStrength ); +const Quantity Quantity::GigaPascal ( 1e+6 , Unit::CompressiveStrength ); -const Quantity Quantity::Pascal(0.001, Unit(-1, 1, -2)); // Pascal (kg/m/s^2 or N/m^2) -const Quantity Quantity::KiloPascal(1.00, Unit(-1, 1, -2)); -const Quantity Quantity::MegaPascal(1000.0, Unit(-1, 1, -2)); -const Quantity Quantity::GigaPascal(1e+6, Unit(-1, 1, -2)); +const Quantity Quantity::MilliBar ( 0.1 , Unit::CompressiveStrength ); +const Quantity Quantity::Bar ( 100.0 , Unit::CompressiveStrength ); // 1 bar = 100 kPa -const Quantity Quantity::MilliBar(0.1, Unit(-1, 1, -2)); -const Quantity Quantity::Bar(100.0, Unit(-1, 1, -2)); // 1 bar = 100 kPa +const Quantity Quantity::Torr ( 101.325 / 760.0 , Unit::CompressiveStrength ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) +const Quantity Quantity::mTorr ( 0.101325 / 760.0 , Unit::CompressiveStrength ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) +const Quantity Quantity::yTorr ( 0.000101325 / 760.0 , Unit::CompressiveStrength ); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) -const Quantity - Quantity::Torr(101.325 / 760.0, - Unit(-1, 1, -2)); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) -const Quantity - Quantity::mTorr(0.101325 / 760.0, - Unit(-1, 1, -2)); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) -const Quantity - Quantity::yTorr(0.000101325 / 760.0, - Unit(-1, 1, -2)); // Torr is a defined fraction of Pascal (kg/m/s^2 or N/m^2) +const Quantity Quantity::PSI ( 6.894744825494 , Unit::CompressiveStrength ); // pounds/in^2 +const Quantity Quantity::KSI ( 6894.744825494 , Unit::CompressiveStrength ); // 1000 x pounds/in^2 +const Quantity Quantity::MPSI ( 6894744.825494 , Unit::CompressiveStrength ); // 1000 ksi -const Quantity Quantity::PSI(6.894744825494, Unit(-1, 1, -2)); // pounds/in^2 -const Quantity Quantity::KSI(6894.744825494, Unit(-1, 1, -2)); // 1000 x pounds/in^2 -const Quantity Quantity::MPSI(6894744.825494, Unit(-1, 1, -2)); // 1000 ksi +const Quantity Quantity::Watt ( 1e+6 , Unit::Power ); // Watt (kg*m^2/s^3) +const Quantity Quantity::MilliWatt ( 1e+3 , Unit::Power ); +const Quantity Quantity::KiloWatt ( 1e+9 , Unit::Power ); +const Quantity Quantity::VoltAmpere ( 1e+6 , Unit::Power ); // VoltAmpere (kg*m^2/s^3) -const Quantity Quantity::Watt(1e+6, Unit(2, 1, -3)); // Watt (kg*m^2/s^3) -const Quantity Quantity::MilliWatt(1e+3, Unit(2, 1, -3)); -const Quantity Quantity::KiloWatt(1e+9, Unit(2, 1, -3)); -const Quantity Quantity::VoltAmpere(1e+6, Unit(2, 1, -3)); // VoltAmpere (kg*m^2/s^3) +const Quantity Quantity::Volt ( 1e+6 , Unit::ElectricPotential ); // Volt (kg*m^2/A/s^3) +const Quantity Quantity::MilliVolt ( 1e+3 , Unit::ElectricPotential ); +const Quantity Quantity::KiloVolt ( 1e+9 , Unit::ElectricPotential ); -const Quantity Quantity::Volt(1e+6, Unit(2, 1, -3, -1)); // Volt (kg*m^2/A/s^3) -const Quantity Quantity::MilliVolt(1e+3, Unit(2, 1, -3, -1)); -const Quantity Quantity::KiloVolt(1e+9, Unit(2, 1, -3, -1)); +const Quantity Quantity::MegaSiemens ( 1.0 , Unit::ElectricalConductance ); +const Quantity Quantity::KiloSiemens ( 1e-3 , Unit::ElectricalConductance ); +const Quantity Quantity::Siemens ( 1e-6 , Unit::ElectricalConductance ); // Siemens (A^2*s^3/kg/m^2) +const Quantity Quantity::MilliSiemens ( 1e-9 , Unit::ElectricalConductance ); +const Quantity Quantity::MicroSiemens ( 1e-12 , Unit::ElectricalConductance ); -const Quantity Quantity::MegaSiemens(1.0, Unit(-2, -1, 3, 2)); -const Quantity Quantity::KiloSiemens(1e-3, Unit(-2, -1, 3, 2)); -const Quantity Quantity::Siemens(1e-6, Unit(-2, -1, 3, 2)); // Siemens (A^2*s^3/kg/m^2) -const Quantity Quantity::MilliSiemens(1e-9, Unit(-2, -1, 3, 2)); -const Quantity Quantity::MicroSiemens(1e-12, Unit(-2, -1, 3, 2)); +const Quantity Quantity::Ohm ( 1e+6 , Unit::ElectricalResistance ); // Ohm (kg*m^2/A^2/s^3) +const Quantity Quantity::KiloOhm ( 1e+9 , Unit::ElectricalResistance ); +const Quantity Quantity::MegaOhm ( 1e+12 , Unit::ElectricalResistance ); -const Quantity Quantity::Ohm(1e+6, Unit(2, 1, -3, -2)); // Ohm (kg*m^2/A^2/s^3) -const Quantity Quantity::KiloOhm(1e+9, Unit(2, 1, -3, -2)); -const Quantity Quantity::MegaOhm(1e+12, Unit(2, 1, -3, -2)); +const Quantity Quantity::Coulomb ( 1.0 , Unit::ElectricCharge ); // Coulomb (A*s) -const Quantity Quantity::Coulomb(1.0, Unit(0, 0, 1, 1)); // Coulomb (A*s) +const Quantity Quantity::Tesla ( 1.0 , Unit::MagneticFluxDensity ); // Tesla (kg/s^2/A) +const Quantity Quantity::Gauss ( 1e-4 , Unit::MagneticFluxDensity ); // 1 G = 1e-4 T -const Quantity Quantity::Tesla(1.0, Unit(0, 1, -2, -1)); // Tesla (kg/s^2/A) -const Quantity Quantity::Gauss(1e-4, Unit(0, 1, -2, -1)); // 1 G = 1e-4 T +const Quantity Quantity::Weber ( 1e6 , Unit::MagneticFlux ); // Weber (kg*m^2/s^2/A) -const Quantity Quantity::Weber(1e6, Unit(2, 1, -2, -1)); // Weber (kg*m^2/s^2/A) +const Quantity Quantity::PicoFarad ( 1e-18 , Unit::ElectricalCapacitance ); +const Quantity Quantity::NanoFarad ( 1e-15 , Unit::ElectricalCapacitance ); +const Quantity Quantity::MicroFarad ( 1e-12 , Unit::ElectricalCapacitance ); +const Quantity Quantity::MilliFarad ( 1e-9 , Unit::ElectricalCapacitance ); +const Quantity Quantity::Farad ( 1e-6 , Unit::ElectricalCapacitance ); // Farad (s^4*A^2/m^2/kg) -// disable Oersted because people need to input e.g. a field strength of -// 1 ampere per meter -> 1 A/m and not get the recalculation to Oersted -// const Quantity Quantity::Oersted(0.07957747, Unit(-1, 0, 0, 1));// Oersted (A/m) +const Quantity Quantity::NanoHenry ( 1e-3 , Unit::ElectricalInductance ); +const Quantity Quantity::MicroHenry ( 1.0 , Unit::ElectricalInductance ); +const Quantity Quantity::MilliHenry ( 1e+3 , Unit::ElectricalInductance ); +const Quantity Quantity::Henry ( 1e+6 , Unit::ElectricalInductance ); // Henry (kg*m^2/s^2/A^2) -const Quantity Quantity::PicoFarad(1e-18, Unit(-2, -1, 4, 2)); -const Quantity Quantity::NanoFarad(1e-15, Unit(-2, -1, 4, 2)); -const Quantity Quantity::MicroFarad(1e-12, Unit(-2, -1, 4, 2)); -const Quantity Quantity::MilliFarad(1e-9, Unit(-2, -1, 4, 2)); -const Quantity Quantity::Farad(1e-6, Unit(-2, -1, 4, 2)); // Farad (s^4*A^2/m^2/kg) +const Quantity Quantity::Joule ( 1e+6 , Unit::Moment ); // Joule (kg*m^2/s^2) +const Quantity Quantity::MilliJoule ( 1e+3 , Unit::Moment ); +const Quantity Quantity::KiloJoule ( 1e+9 , Unit::Moment ); +const Quantity Quantity::NewtonMeter ( 1e+6 , Unit::Moment ); // Joule (kg*m^2/s^2) +const Quantity Quantity::VoltAmpereSecond ( 1e+6 , Unit::Moment ); // Joule (kg*m^2/s^2) +const Quantity Quantity::WattSecond ( 1e+6 , Unit::Moment ); // Joule (kg*m^2/s^2) +const Quantity Quantity::KiloWattHour ( 3.6e+12 , Unit::Moment ); // 1 kWh = 3.6e6 J +const Quantity Quantity::ElectronVolt ( 1.602176634e-13 , Unit::Moment ); // 1 eV = 1.602176634e-19 J +const Quantity Quantity::KiloElectronVolt ( 1.602176634e-10 , Unit::Moment ); +const Quantity Quantity::MegaElectronVolt ( 1.602176634e-7 , Unit::Moment ); +const Quantity Quantity::Calorie ( 4.1868e+6 , Unit::Moment ); // 1 cal = 4.1868 J +const Quantity Quantity::KiloCalorie ( 4.1868e+9 , Unit::Moment ); -const Quantity Quantity::NanoHenry(1e-3, Unit(2, 1, -2, -2)); -const Quantity Quantity::MicroHenry(1.0, Unit(2, 1, -2, -2)); -const Quantity Quantity::MilliHenry(1e+3, Unit(2, 1, -2, -2)); -const Quantity Quantity::Henry(1e+6, Unit(2, 1, -2, -2)); // Henry (kg*m^2/s^2/A^2) - -const Quantity Quantity::Joule(1e+6, Unit(2, 1, -2)); // Joule (kg*m^2/s^2) -const Quantity Quantity::MilliJoule(1e+3, Unit(2, 1, -2)); -const Quantity Quantity::KiloJoule(1e+9, Unit(2, 1, -2)); -const Quantity Quantity::NewtonMeter(1e+6, Unit(2, 1, -2)); // Joule (kg*m^2/s^2) -const Quantity Quantity::VoltAmpereSecond(1e+6, Unit(2, 1, -2)); // Joule (kg*m^2/s^2) -const Quantity Quantity::WattSecond(1e+6, Unit(2, 1, -2)); // Joule (kg*m^2/s^2) -const Quantity Quantity::KiloWattHour(3.6e+12, Unit(2, 1, -2)); // 1 kWh = 3.6e6 J -const Quantity Quantity::ElectronVolt(1.602176634e-13, Unit(2, 1, -2)); // 1 eV = 1.602176634e-19 J -const Quantity Quantity::KiloElectronVolt(1.602176634e-10, Unit(2, 1, -2)); -const Quantity Quantity::MegaElectronVolt(1.602176634e-7, Unit(2, 1, -2)); -const Quantity Quantity::Calorie(4.1868e+6, Unit(2, 1, -2)); // 1 cal = 4.1868 J -const Quantity Quantity::KiloCalorie(4.1868e+9, Unit(2, 1, -2)); - -const Quantity Quantity::KMH(277.778, Unit(1, 0, -1)); // km/h -const Quantity Quantity::MPH(447.04, Unit(1, 0, -1)); // Mile/h - -const Quantity Quantity::AngMinute(1.0 / 60.0, Unit(0, 0, 0, 0, 0, 0, 0, 1)); // angular minute -const Quantity Quantity::AngSecond(1.0 / 3600.0, Unit(0, 0, 0, 0, 0, 0, 0, 1)); // angular second -const Quantity - Quantity::Degree(1.0, - Unit(0, 0, 0, 0, 0, 0, 0, 1)); // degree (internal standard angle) -const Quantity Quantity::Radian(180 / std::numbers::pi, Unit(0, 0, 0, 0, 0, 0, 0, 1)); // radian -const Quantity Quantity::Gon(360.0 / 400.0, Unit(0, 0, 0, 0, 0, 0, 0, 1)); // gon +const Quantity Quantity::KMH ( 277.778 , Unit::Velocity ); // km/h +const Quantity Quantity::MPH ( 447.04 , Unit::Velocity ); // Mile/h +const Quantity Quantity::AngMinute ( 1.0 / 60.0 , Unit::Angle ); // angular minute +const Quantity Quantity::AngSecond ( 1.0 / 3600.0 , Unit::Angle ); // angular second +const Quantity Quantity::Degree ( 1.0 , Unit::Angle ); // degree (internal standard angle) +const Quantity Quantity::Radian ( 180 / std::numbers::pi, Unit::Angle ); // radian +const Quantity Quantity::Gon ( 360.0 / 400.0 , Unit::Angle ); // gon +// clang-format on // === Parser & Scanner stuff =============================================== diff --git a/src/Base/QuantityPyImp.cpp b/src/Base/QuantityPyImp.cpp index 1fea8807c6..365ef592f6 100644 --- a/src/Base/QuantityPyImp.cpp +++ b/src/Base/QuantityPyImp.cpp @@ -91,45 +91,37 @@ int QuantityPy::PyInit(PyObject* args, PyObject* /*kwd*/) *self = *(static_cast(object)->getQuantityPtr()); return 0; } - PyErr_Clear(); // set by PyArg_ParseTuple() + double f = std::numeric_limits::max(); if (PyArg_ParseTuple(args, "dO!", &f, &(UnitPy::Type), &object)) { *self = Quantity(f, *(static_cast(object)->getUnitPtr())); return 0; } - PyErr_Clear(); // set by PyArg_ParseTuple() + if (PyArg_ParseTuple(args, "dO!", &f, &(QuantityPy::Type), &object)) { PyErr_SetString(PyExc_TypeError, "Second argument must be a Unit not a Quantity"); return -1; } - - int i1 = 0; - int i2 = 0; - int i3 = 0; - int i4 = 0; - int i5 = 0; - int i6 = 0; - int i7 = 0; - int i8 = 0; PyErr_Clear(); // set by PyArg_ParseTuple() + + int i1 {0}; + int i2 {0}; + int i3 {0}; + int i4 {0}; + int i5 {0}; + int i6 {0}; + int i7 {0}; + int i8 {0}; if (PyArg_ParseTuple(args, "|diiiiiiii", &f, &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8)) { if (f < std::numeric_limits::max()) { - *self = Quantity(f, - Unit {static_cast(i1), - static_cast(i2), - static_cast(i3), - static_cast(i4), - static_cast(i5), - static_cast(i6), - static_cast(i7), - static_cast(i8)}); + *self = Quantity {f, Unit(i1, i2, i3, i4, i5, i6, i7, i8)}; } return 0; } - PyErr_Clear(); // set by PyArg_ParseTuple() + char* string {}; if (PyArg_ParseTuple(args, "et", "utf-8", &string)) { std::string str(string); @@ -144,13 +136,13 @@ int QuantityPy::PyInit(PyObject* args, PyObject* /*kwd*/) return 0; } - PyErr_Clear(); // set by PyArg_ParseTuple() + if (PyArg_ParseTuple(args, "det", &f, "utf-8", &string)) { - std::string unit(string); + std::string str(string); PyMem_Free(string); try { - *self = Quantity(f, unit); + *self = Quantity(f, str); } catch (const ParserError& e) { PyErr_SetString(PyExc_ValueError, e.what()); @@ -210,7 +202,7 @@ PyObject* QuantityPy::getValueAs(PyObject* args) const }; auto tryUnitPartsAndValue = [&]() -> std::optional { - double f = std::numeric_limits::max(); + double f; int i1 {0}; int i2 {0}; int i3 {0}; @@ -219,20 +211,11 @@ PyObject* QuantityPy::getValueAs(PyObject* args) const int i6 {0}; int i7 {0}; int i8 {0}; - PyErr_Clear(); if (!PyArg_ParseTuple(args, "d|iiiiiiii", &f, &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8)) { return std::nullopt; } - if (f >= std::numeric_limits::max()) { - return std::nullopt; - } - - auto re = [](auto val) { - return static_cast(val); - }; - - return Quantity {f, Unit {re(i1), re(i2), re(i3), re(i4), re(i5), re(i6), re(i7), re(i8)}}; + return Quantity {f, Unit(i1, i2, i3, i4, i5, i6, i7, i8)}; }; auto tryString = [&]() -> std::optional { diff --git a/src/Base/Unit.cpp b/src/Base/Unit.cpp index 8b8570879a..45a4af42e7 100644 --- a/src/Base/Unit.cpp +++ b/src/Base/Unit.cpp @@ -1,23 +1,22 @@ -/*************************************************************************** - * Copyright (c) 2011 Jürgen Riegel * - * * - * This file is part of the FreeCAD CAx development system. * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Library General Public * - * License as published by the Free Software Foundation; either * - * version 2 of the License, or (at your option) any later version. * - * * - * This library 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 Library General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this library; see the file COPYING.LIB. If not, * - * write to the Free Software Foundation, Inc., 59 Temple Place, * - * Suite 330, Boston, MA 02111-1307, USA * - * * +// 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 "PreCompiled.h" @@ -27,698 +26,349 @@ #include #include #include -#include +#include +#include #endif -#include "Unit.h" -#include "Exception.h" -#include "Quantity.h" +#include +#include +#include "Unit.h" using namespace Base; -// clang-format off -constexpr int UnitSignatureLengthBits = 4; -constexpr int UnitSignatureMassBits = 4; -constexpr int UnitSignatureTimeBits = 4; -constexpr int UnitSignatureElectricCurrentBits = 4; -constexpr int UnitSignatureThermodynamicTemperatureBits = 4; -constexpr int UnitSignatureAmountOfSubstanceBits = 4; -constexpr int UnitSignatureLuminousIntensityBits = 4; -constexpr int UnitSignatureAngleBits = 4; - -struct UnitSignature { - int32_t Length: UnitSignatureLengthBits; - int32_t Mass: UnitSignatureMassBits; - int32_t Time: UnitSignatureTimeBits; - int32_t ElectricCurrent: UnitSignatureElectricCurrentBits; - int32_t ThermodynamicTemperature: UnitSignatureThermodynamicTemperatureBits; - int32_t AmountOfSubstance: UnitSignatureAmountOfSubstanceBits; - int32_t LuminousIntensity: UnitSignatureLuminousIntensityBits; - int32_t Angle: UnitSignatureAngleBits; +struct UnitSpec +{ + std::string_view name; + UnitExponents exps; }; -static inline uint32_t sigVal(const std::string &op, - int length, int mass, int time, int electricCurrent, - int thermodynamicTemperature, int amountOfSubstance, int luminousIntensity, int angle) +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 {""} { - if ( ( length >= (1 << (UnitSignatureLengthBits - 1)) ) || - ( mass >= (1 << (UnitSignatureMassBits - 1)) ) || - ( time >= (1 << (UnitSignatureTimeBits - 1)) ) || - ( electricCurrent >= (1 << (UnitSignatureElectricCurrentBits - 1)) ) || - ( thermodynamicTemperature >= (1 << (UnitSignatureThermodynamicTemperatureBits - 1)) ) || - ( amountOfSubstance >= (1 << (UnitSignatureAmountOfSubstanceBits - 1)) ) || - ( luminousIntensity >= (1 << (UnitSignatureLuminousIntensityBits - 1)) ) || - ( angle >= (1 << (UnitSignatureAngleBits - 1)) ) ) { - throw Base::OverflowError(("Unit overflow in " + op).c_str()); - } - if ( ( length < -(1 << (UnitSignatureLengthBits - 1)) ) || - ( mass < -(1 << (UnitSignatureMassBits - 1)) ) || - ( time < -(1 << (UnitSignatureTimeBits - 1)) ) || - ( electricCurrent < -(1 << (UnitSignatureElectricCurrentBits - 1)) ) || - ( thermodynamicTemperature < -(1 << (UnitSignatureThermodynamicTemperatureBits - 1)) ) || - ( amountOfSubstance < -(1 << (UnitSignatureAmountOfSubstanceBits - 1)) ) || - ( luminousIntensity < -(1 << (UnitSignatureLuminousIntensityBits - 1)) ) || - ( angle < -(1 << (UnitSignatureAngleBits - 1)) ) ) { - throw Base::UnderflowError(("Unit underflow in " + op).c_str()); - } - - UnitSignature Sig; - Sig.Length = length; - Sig.Mass = mass; - Sig.Time = time; - Sig.ElectricCurrent = electricCurrent; - Sig.ThermodynamicTemperature = thermodynamicTemperature; - Sig.AmountOfSubstance = amountOfSubstance; - Sig.LuminousIntensity = luminousIntensity; - Sig.Angle = angle; - - uint32_t ret; - memcpy(&ret, &Sig, sizeof(ret)); - return ret; -} - - -Unit::Unit(int8_t Length, //NOLINT - int8_t Mass, - int8_t Time, - int8_t ElectricCurrent, - int8_t ThermodynamicTemperature, - int8_t AmountOfSubstance, - int8_t LuminousIntensity, - int8_t Angle) -{ - Val = sigVal("unit", - Length, - Mass, - Time, - ElectricCurrent, - ThermodynamicTemperature, - AmountOfSubstance, - LuminousIntensity, - Angle); -} - - -Unit::Unit() //NOLINT -{ - Val = 0; -} - -Unit::Unit(const std::string& expr) // NOLINT -{ - try { - *this = Quantity::parse(expr).getUnit(); - } - catch (const Base::ParserError&) { - Val = 0; - } -} - -Unit Unit::pow(double exp) const -{ - UnitSignature sig; - memcpy(&sig, &Val, sizeof(Val)); - auto isInt = [](double value) { - return std::fabs(std::round(value) - value) < std::numeric_limits::epsilon(); + auto cast = [](auto val) { + return static_cast(std::clamp(val, + std::numeric_limits::min(), + std::numeric_limits::max())); }; - if (!isInt(sig.Length * exp) || - !isInt(sig.Mass * exp) || - !isInt(sig.Time * exp) || - !isInt(sig.ElectricCurrent * exp) || - !isInt(sig.ThermodynamicTemperature * exp) || - !isInt(sig.AmountOfSubstance * exp) || - !isInt(sig.LuminousIntensity * exp) || - !isInt(sig.Angle * exp)) - throw Base::UnitsMismatchError("pow() of unit not possible"); - Unit result; - result.Val = sigVal("pow()", - sig.Length * exp, - sig.Mass * exp, - sig.Time * exp, - sig.ElectricCurrent * exp, - sig.ThermodynamicTemperature * exp, - sig.AmountOfSubstance * exp, - sig.LuminousIntensity * exp, - sig.Angle * exp); + _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); - return result; + checkRange(); } -Unit Unit::sqrt() const +bool Unit::operator==(const Unit& that) const { - UnitSignature sig; - memcpy(&sig, &Val, sizeof(Val)); - // All components of unit must be either zero or dividable by 2 - if (!((sig.Length % 2) == 0) && - ((sig.Mass % 2) == 0) && - ((sig.Time % 2) == 0) && - ((sig.ElectricCurrent % 2) == 0) && - ((sig.ThermodynamicTemperature % 2) == 0) && - ((sig.AmountOfSubstance % 2) == 0) && - ((sig.LuminousIntensity % 2) == 0) && - ((sig.Angle % 2) == 0)) - throw Base::UnitsMismatchError("sqrt() needs even dimensions"); - - Unit result; - result.Val = sigVal("sqrt()", - sig.Length >> 1, - sig.Mass >> 1, - sig.Time >> 1, - sig.ElectricCurrent >> 1, - sig.ThermodynamicTemperature >> 1, - sig.AmountOfSubstance >> 1, - sig.LuminousIntensity >> 1, - sig.Angle >> 1); - - return result; + return _exps == that._exps; } -Unit Unit::cbrt() const +bool Unit::operator!=(const Unit& that) const { - UnitSignature sig; - memcpy(&sig, &Val, sizeof(Val)); - // All components of unit must be either zero or dividable by 3 - if (!((sig.Length % 3) == 0) && - ((sig.Mass % 3) == 0) && - ((sig.Time % 3) == 0) && - ((sig.ElectricCurrent % 3) == 0) && - ((sig.ThermodynamicTemperature % 3) == 0) && - ((sig.AmountOfSubstance % 3) == 0) && - ((sig.LuminousIntensity % 3) == 0) && - ((sig.Angle % 3) == 0)) - throw Base::UnitsMismatchError("cbrt() needs dimensions to be multiples of 3"); + return _exps != that._exps; +} - Unit result; - result.Val = sigVal("cbrt()", - sig.Length / 3, - sig.Mass / 3, - sig.Time / 3, - sig.ElectricCurrent / 3, - sig.ThermodynamicTemperature / 3, - sig.AmountOfSubstance / 3, - sig.LuminousIntensity / 3, - sig.Angle / 3); +Unit& Unit::operator*=(const Unit& that) +{ + *this = *this * that; + return *this; +} - return result; +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 { - UnitSignature sig; - memcpy(&sig, &Val, sizeof(Val)); - return sig.Length; -} - -int Unit::mass() const -{ - UnitSignature sig; - memcpy(&sig, &Val, sizeof(Val)); - return sig.Mass; -} - - -int Unit::time() const -{ - UnitSignature sig; - memcpy(&sig, &Val, sizeof(Val)); - return sig.Time; -} - -int Unit::electricCurrent() const -{ - UnitSignature sig; - memcpy(&sig, &Val, sizeof(Val)); - return sig.ElectricCurrent; -} - -int Unit::thermodynamicTemperature() const -{ - UnitSignature sig; - memcpy(&sig, &Val, sizeof(Val)); - return sig.ThermodynamicTemperature; -} - -int Unit::amountOfSubstance() const -{ - UnitSignature sig; - memcpy(&sig, &Val, sizeof(Val)); - return sig.AmountOfSubstance; -} - -int Unit::luminousIntensity() const -{ - UnitSignature sig; - memcpy(&sig, &Val, sizeof(Val)); - return sig.LuminousIntensity; -} - -int Unit::angle() const -{ - UnitSignature sig; - memcpy(&sig, &Val, sizeof(Val)); - return sig.Angle; -} - -int Unit::operator [](int index) const -{ - UnitSignature sig; - memcpy(&sig, &Val, sizeof(Val)); - - switch (index) { - case 0: - return sig.Length; - case 1: - return sig.Mass; - case 2: - return sig.Time; - case 3: - return sig.ElectricCurrent; - case 4: - return sig.ThermodynamicTemperature; - case 5: - return sig.AmountOfSubstance; - case 6: - return sig.LuminousIntensity; - case 7: - return sig.Angle; - default: - throw Base::IndexError("Unknown Unit element"); - } -} - -bool Unit::operator ==(const Unit& that) const -{ - return Val == that.Val; -} - -bool Unit::operator !=(const Unit& that) const -{ - return Val != that.Val; -} - -Unit Unit::operator *(const Unit &right) const -{ - Unit result; - UnitSignature sig, rsig; - - memcpy(&sig, &Val, sizeof(Val)); - memcpy(&rsig, &right.Val, sizeof(right.Val)); - result.Val = sigVal("* operator", - sig.Length + rsig.Length, - sig.Mass + rsig.Mass, - sig.Time + rsig.Time, - sig.ElectricCurrent + rsig.ElectricCurrent, - sig.ThermodynamicTemperature + rsig.ThermodynamicTemperature, - sig.AmountOfSubstance + rsig.AmountOfSubstance, - sig.LuminousIntensity + rsig.LuminousIntensity, - sig.Angle + rsig.Angle); - - return result; -} - -Unit Unit::operator /(const Unit &right) const -{ - Unit result; - UnitSignature sig, rsig; - - memcpy(&sig, &Val, sizeof(Val)); - memcpy(&rsig, &right.Val, sizeof(right.Val)); - result.Val = sigVal("/ operator", - sig.Length - rsig.Length, - sig.Mass - rsig.Mass, - sig.Time - rsig.Time, - sig.ElectricCurrent - rsig.ElectricCurrent, - sig.ThermodynamicTemperature - rsig.ThermodynamicTemperature, - sig.AmountOfSubstance - rsig.AmountOfSubstance, - sig.LuminousIntensity - rsig.LuminousIntensity, - sig.Angle - rsig.Angle); - - return result; + return _exps[0]; } std::string Unit::getString() const { - if (Val == 0) { - return {}; + 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; } - std::stringstream ret; - UnitSignature sig; - memcpy(&sig, &Val, sizeof(Val)); + auto denominatorStr = buildStr(negValIndexes); - if (sig.Length > 0 || - sig.Mass > 0 || - sig.Time > 0 || - sig.ElectricCurrent > 0 || - sig.ThermodynamicTemperature > 0 || - sig.AmountOfSubstance > 0 || - sig.LuminousIntensity > 0 || - sig.Angle > 0 ) { + return fmt::format("{}/{}", + numeratorStr.empty() ? "1" : numeratorStr, + negValIndexes.size() > 1 ? fmt::format("({})", denominatorStr) + : denominatorStr); +} - bool mult = false; - if (sig.Length > 0) { - mult = true; - ret << "mm"; - if (sig.Length > 1) { - ret << "^" << sig.Length; - } - } - - if (sig.Mass > 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "kg"; - if (sig.Mass > 1) { - ret << "^" << sig.Mass; - } - } - - if (sig.Time > 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "s"; - if (sig.Time > 1) { - ret << "^" << sig.Time; - } - } - - if (sig.ElectricCurrent > 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "A"; - if (sig.ElectricCurrent > 1) { - ret << "^" << sig.ElectricCurrent; - } - } - - if (sig.ThermodynamicTemperature > 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "K"; - if (sig.ThermodynamicTemperature > 1) { - ret << "^" << sig.ThermodynamicTemperature; - } - } - - if (sig.AmountOfSubstance > 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "mol"; - if (sig.AmountOfSubstance > 1) { - ret << "^" << sig.AmountOfSubstance; - } - } - - if (sig.LuminousIntensity > 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "cd"; - if (sig.LuminousIntensity > 1) { - ret << "^" << sig.LuminousIntensity; - } - } - - if (sig.Angle > 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "deg"; - if (sig.Angle > 1) { - ret << "^" << sig.Angle; - } - } - } - else { - ret << "1"; - } - - if (sig.Length < 0 || - sig.Mass < 0 || - sig.Time < 0 || - sig.ElectricCurrent < 0 || - sig.ThermodynamicTemperature < 0 || - sig.AmountOfSubstance < 0 || - sig.LuminousIntensity < 0 || - sig.Angle < 0 ) { - ret << "/"; - - int nnom = 0; - nnom += sig.Length < 0 ? 1 : 0; - nnom += sig.Mass < 0 ? 1 : 0; - nnom += sig.Time < 0 ? 1 : 0; - nnom += sig.ElectricCurrent < 0 ? 1 : 0; - nnom += sig.ThermodynamicTemperature < 0 ? 1 : 0; - nnom += sig.AmountOfSubstance < 0 ? 1 : 0; - nnom += sig.LuminousIntensity < 0 ? 1 : 0; - nnom += sig.Angle < 0 ? 1 : 0; - - if (nnom > 1) { - ret << '('; - } - - bool mult = false; - if (sig.Length < 0) { - ret << "mm"; - mult = true; - if (sig.Length < -1) { - ret << "^" << abs(sig.Length); - } - } - - if (sig.Mass < 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "kg"; - if (sig.Mass < -1) { - ret << "^" << abs(sig.Mass); - } - } - - if (sig.Time < 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "s"; - if (sig.Time < -1) { - ret << "^" << abs(sig.Time); - } - } - - if (sig.ElectricCurrent < 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "A"; - if (sig.ElectricCurrent < -1) { - ret << "^" << abs(sig.ElectricCurrent); - } - } - - if (sig.ThermodynamicTemperature < 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "K"; - if (sig.ThermodynamicTemperature < -1) { - ret << "^" << abs(sig.ThermodynamicTemperature); - } - } - - if (sig.AmountOfSubstance < 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "mol"; - if (sig.AmountOfSubstance < -1) { - ret << "^" << abs(sig.AmountOfSubstance); - } - } - - if (sig.LuminousIntensity < 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "cd"; - if (sig.LuminousIntensity < -1) { - ret << "^" << abs(sig.LuminousIntensity); - } - } - - if (sig.Angle < 0) { - if (mult) { - ret << '*'; - } - mult = true; - ret << "deg"; - if (sig.Angle < -1) { - ret << "^" << abs(sig.Angle); - } - } - - if (nnom > 1) { - ret << ')'; - } - } - - return ret.str(); +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 { - static std::array, 57> unitSpecs {{ - { Unit::Acceleration, "Acceleration" }, - { Unit::AmountOfSubstance, "AmountOfSubstance" }, - { Unit::Angle, "Angle" }, - { Unit::AngleOfFriction, "AngleOfFriction" }, - { Unit::Area, "Area" }, - { Unit::CurrentDensity, "CurrentDensity" }, - { Unit::Density, "Density" }, - { Unit::DissipationRate, "DissipationRate" }, - { Unit::DynamicViscosity, "DynamicViscosity" }, - { Unit::ElectricalCapacitance, "ElectricalCapacitance" }, - { Unit::ElectricalConductance, "ElectricalConductance" }, - { Unit::ElectricalConductivity, "ElectricalConductivity" }, - { Unit::ElectricalInductance, "ElectricalInductance" }, - { Unit::ElectricalResistance, "ElectricalResistance" }, - { Unit::ElectricCharge, "ElectricCharge" }, - { Unit::SurfaceChargeDensity, "SurfaceChargeDensity" }, - { Unit::VolumeChargeDensity, "VolumeChargeDensity" }, - { Unit::ElectricCurrent, "ElectricCurrent" }, - { Unit::ElectricPotential, "ElectricPotential" }, - { Unit::ElectromagneticPotential, "ElectromagneticPotential" }, - { Unit::Frequency, "Frequency" }, - { Unit::Force, "Force" }, - { Unit::HeatFlux, "HeatFlux" }, - { Unit::InverseArea, "InverseArea" }, - { Unit::InverseLength, "InverseLength" }, - { Unit::InverseVolume, "InverseVolume" }, - { Unit::KinematicViscosity, "KinematicViscosity" }, - { Unit::Length, "Length" }, - { Unit::LuminousIntensity, "LuminousIntensity" }, - { Unit::MagneticFieldStrength, "MagneticFieldStrength" }, - { Unit::MagneticFlux, "MagneticFlux" }, - { Unit::MagneticFluxDensity, "MagneticFluxDensity" }, - { Unit::Magnetization, "Magnetization" }, - { Unit::Mass, "Mass" }, - { Unit::Pressure, "Pressure" }, - { Unit::Power, "Power" }, - { Unit::ShearModulus, "ShearModulus" }, - { Unit::SpecificEnergy, "SpecificEnergy" }, - { Unit::SpecificHeat, "SpecificHeat" }, - { Unit::Stiffness, "Stiffness" }, - { Unit::StiffnessDensity, "StiffnessDensity" }, - { Unit::Stress, "Stress" }, - { Unit::Temperature, "Temperature" }, - { Unit::ThermalConductivity, "ThermalConductivity" }, - { Unit::ThermalExpansionCoefficient, "ThermalExpansionCoefficient" }, - { Unit::ThermalTransferCoefficient, "ThermalTransferCoefficient" }, - { Unit::TimeSpan, "TimeSpan" }, - { Unit::UltimateTensileStrength, "UltimateTensileStrength" }, - { Unit::VacuumPermittivity, "VacuumPermittivity" }, - { Unit::Velocity, "Velocity" }, - { Unit::Volume, "Volume" }, - { Unit::VolumeFlowRate, "VolumeFlowRate" }, - { Unit::VolumetricThermalExpansionCoefficient, "VolumetricThermalExpansionCoefficient" }, - { Unit::Work, "Work" }, - { Unit::YieldStrength, "YieldStrength" }, - { Unit::YoungsModulus, "YoungsModulus" }, - { Unit::Moment, "Moment" }, - }}; + if (_name.empty()) { + const auto spec = std::ranges::find(unitSpecs, _exps, &UnitSpec::exps); + return std::string(spec == unitSpecs.end() ? "" : spec->name); + } - const auto spec = - std::find_if(unitSpecs.begin(), unitSpecs.end(), [&](const auto& pair) { - return pair.first == *this; - }); - - if (spec == std::end(unitSpecs)) - return ""; - - return spec->second; + return std::string {_name.data(), _name.size()}; } -const Unit Unit::One (0, 0, 0, 0, 0, 0, 0, 0); +std::pair, std::vector> Unit::nonZeroValsIndexes() const +{ + std::vector pos {}; + std::vector neg {}; -// SI base units -const Unit Unit::AmountOfSubstance (0, 0, 0, 0, 0, 1); -const Unit Unit::ElectricCurrent (0, 0, 0, 1); -const Unit Unit::Length (1); -const Unit Unit::LuminousIntensity (0, 0, 0, 0, 0, 0, 1); -const Unit Unit::Mass (0, 1); -const Unit Unit::Temperature (0, 0, 0, 0, 1); -const Unit Unit::TimeSpan (0, 0, 1); + auto posNeg = [&, index {0}](auto val) mutable { + if (val != 0) { + (val > 0 ? pos : neg).push_back(index); + } + ++index; + }; -// all other units -const Unit Unit::Acceleration (1, 0, -2); -const Unit Unit::Angle (0, 0, 0, 0, 0, 0, 0, 1); -const Unit Unit::AngleOfFriction (0, 0, 0, 0, 0, 0, 0, 1); -const Unit Unit::Area (2); -const Unit Unit::CompressiveStrength (-1, 1, -2); -const Unit Unit::CurrentDensity (-2, 0, 0, 1); -const Unit Unit::Density (-3, 1); -const Unit Unit::DissipationRate (2, 0, -3); // https://cfd-online.com/Wiki/Turbulence_dissipation_rate -const Unit Unit::DynamicViscosity (-1, 1, -1); -const Unit Unit::ElectricalCapacitance (-2, -1, 4, 2); -const Unit Unit::ElectricalConductance (-2, -1, 3, 2); -const Unit Unit::ElectricalConductivity (-3, -1, 3, 2); -const Unit Unit::ElectricalInductance (2, 1, -2, -2); -const Unit Unit::ElectricalResistance (2, 1, -3, -2); -const Unit Unit::ElectricCharge (0, 0, 1, 1); -const Unit Unit::SurfaceChargeDensity (-2, 0, 1, 1); -const Unit Unit::VolumeChargeDensity (-3, 0, 1, 1); -const Unit Unit::ElectricPotential (2, 1, -3, -1); -const Unit Unit::ElectromagneticPotential (1, 1, -2, -1); -const Unit Unit::Force (1, 1, -2); -const Unit Unit::Frequency (0, 0, -1); -const Unit Unit::HeatFlux (0, 1, -3, 0, 0); -const Unit Unit::InverseArea (-2, 0, 0); -const Unit Unit::InverseLength (-1, 0, 0); -const Unit Unit::InverseVolume (-3, 0, 0); -const Unit Unit::KinematicViscosity (2, 0, -1); -const Unit Unit::MagneticFieldStrength (-1,0,0,1); -const Unit Unit::MagneticFlux (2,1,-2,-1); -const Unit Unit::MagneticFluxDensity (0,1,-2,-1); -const Unit Unit::Magnetization (-1,0,0,1); -const Unit Unit::Moment (2, 1, -2); -const Unit Unit::Pressure (-1,1,-2); -const Unit Unit::Power (2, 1, -3); -const Unit Unit::ShearModulus (-1,1,-2); -const Unit Unit::SpecificEnergy (2, 0, -2); -const Unit Unit::SpecificHeat (2, 0, -2, 0, -1); -const Unit Unit::Stiffness (0, 1, -2); -const Unit Unit::StiffnessDensity (-2, 1, -2); -const Unit Unit::Stress (-1,1,-2); -const Unit Unit::ThermalConductivity (1, 1, -3, 0, -1); -const Unit Unit::ThermalExpansionCoefficient(0, 0, 0, 0, -1); -const Unit Unit::ThermalTransferCoefficient (0, 1, -3, 0, -1); -const Unit Unit::UltimateTensileStrength (-1,1,-2); -const Unit Unit::VacuumPermittivity (-3, -1, 4, 2); -const Unit Unit::Velocity (1, 0, -1); -const Unit Unit::Volume (3); -const Unit Unit::VolumeFlowRate (3, 0, -1); -const Unit Unit::VolumetricThermalExpansionCoefficient(0, 0, 0, 0, -1); -const Unit Unit::Work (2, 1, -2); -const Unit Unit::YieldStrength (-1,1,-2); -const Unit Unit::YoungsModulus (-1,1,-2); -// clang-format on + 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("Magnetization" ); +constexpr Unit Unit::MagneticFlux = make("MagneticFlux" ); +constexpr Unit Unit::MagneticFluxDensity = make("MagneticFluxDensity" ); +constexpr Unit Unit::Magnetization = make("MagneticFieldStrength" ); +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" ); diff --git a/src/Base/Unit.h b/src/Base/Unit.h index f5e6197bf2..36d93d6e45 100644 --- a/src/Base/Unit.h +++ b/src/Base/Unit.h @@ -1,178 +1,178 @@ -/*************************************************************************** - * Copyright (c) 2011 Jürgen Riegel * - * * - * This file is part of the FreeCAD CAx development system. * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Library General Public * - * License as published by the Free Software Foundation; either * - * version 2 of the License, or (at your option) any later version. * - * * - * This library 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 Library General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this library; see the file COPYING.LIB. If not, * - * write to the Free Software Foundation, Inc., 59 Temple Place, * - * Suite 330, Boston, MA 02111-1307, USA * - * * +// 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 * + * . * + * * ***************************************************************************/ - #ifndef BASE_Unit_H #define BASE_Unit_H +#include #include +#include #include +#include +#include + #include +#include "Exception.h" + namespace Base { -/** - * The Unit class. - */ -class BaseExport Unit +constexpr auto unitSymbols = + std::to_array({"mm", "kg", "s", "A", "K", "mol", "cd", "deg"}); + +constexpr auto unitNumExponents {unitSymbols.size()}; +using UnitExponents = std::array; + +constexpr auto unitExponentLimit {8}; + +class BaseExport Unit final { public: - /// default constructor - explicit Unit(int8_t Length, - int8_t Mass = 0, - int8_t Time = 0, - int8_t ElectricCurrent = 0, - int8_t ThermodynamicTemperature = 0, - int8_t AmountOfSubstance = 0, - int8_t LuminousIntensity = 0, - int8_t Angle = 0); - Unit(); - Unit(const Unit&) = default; - Unit(Unit&&) = default; - explicit Unit(const std::string& expr); - /// Destruction - ~Unit() = default; + Unit() = default; + + explicit constexpr Unit(const UnitExponents exps, const std::string_view name = "") + : _exps {exps} + , _name {name} + { + checkRange(); + } + + /// helper constructor to ease Unit construction from Python + explicit Unit(const int length, + const int mass = 0, + const int time = 0, + const int electricCurrent = 0, + const int thermodynamicTemperature = 0, + const int amountOfSubstance = 0, + const int luminousIntensity = 0, + const int angle = 0); - /** Operators. */ - //@{ - inline Unit& operator*=(const Unit& that); - inline Unit& operator/=(const Unit& that); - int operator[](int index) const; - Unit operator*(const Unit&) const; - Unit operator/(const Unit&) const; bool operator==(const Unit&) const; bool operator!=(const Unit& that) const; - Unit& operator=(const Unit&) = default; - Unit& operator=(Unit&&) = default; - Unit pow(double exp) const; - Unit sqrt() const; - Unit cbrt() const; - //@} - int length() const; - int mass() const; - int time() const; - int electricCurrent() const; - int thermodynamicTemperature() const; - int amountOfSubstance() const; - int luminousIntensity() const; - int angle() const; + Unit& operator*=(const Unit& that); + Unit& operator/=(const Unit& that); + Unit operator*(const Unit&) const; + Unit operator/(const Unit&) const; - std::string getString() const; - /// get the type as an string such as "Area", "Length" or "Pressure". - std::string getTypeString() const; + [[nodiscard]] Unit pow(const double exp) const; + [[nodiscard]] Unit root(const uint8_t num) const; - /** Predefined Unit types. */ - //@{ - static const Unit One; - /// Length unit - static const Unit Length; - /// Mass unit - static const Unit Mass; + [[nodiscard]] UnitExponents exponents() const; + [[nodiscard]] int length() const; - /// Angle + [[nodiscard]] std::string getString() const; // E.g. kg, mm^2, mm*kg/s^2 + [[nodiscard]] std::string getTypeString() const; // E.g. "Area", "Length", "Pressure" + [[nodiscard]] std::string representation() const; // E.g. "Unit: mm (1,0,0,0,0,0,0,0) [Length]" + + Unit sqrt() const + { + return root(2); + } + Unit cbrt() const + { + return root(3); + } + +private: + UnitExponents _exps {}; + std::string_view _name; + + constexpr void checkRange() + { + for (const auto exp : _exps) { + if (exp >= unitExponentLimit) { + throw OverflowError("Unit exponent overflow"); + } + if (exp < -unitExponentLimit) { + throw UnderflowError("Unit exponent underflow"); + } + } + } + + /** Returns posIndexes, negIndexes*/ + std::pair, std::vector> nonZeroValsIndexes() const; + +public: + static const Unit Acceleration; + static const Unit AmountOfSubstance; static const Unit Angle; static const Unit AngleOfFriction; - - static const Unit Density; - static const Unit Area; - static const Unit Volume; - static const Unit TimeSpan; - static const Unit Frequency; - static const Unit Velocity; - static const Unit Acceleration; - static const Unit Temperature; - + static const Unit CompressiveStrength; static const Unit CurrentDensity; + static const Unit Density; + static const Unit DissipationRate; + static const Unit DynamicViscosity; + static const Unit ElectricalCapacitance; + static const Unit ElectricalConductance; + static const Unit ElectricalConductivity; + static const Unit ElectricalInductance; + static const Unit ElectricalResistance; + static const Unit ElectricCharge; static const Unit ElectricCurrent; static const Unit ElectricPotential; - static const Unit ElectricCharge; - static const Unit SurfaceChargeDensity; - static const Unit VolumeChargeDensity; + static const Unit ElectromagneticPotential; + static const Unit Force; + static const Unit Frequency; + static const Unit HeatFlux; + static const Unit InverseArea; + static const Unit InverseLength; + static const Unit InverseVolume; + static const Unit KinematicViscosity; + static const Unit Length; + static const Unit LuminousIntensity; static const Unit MagneticFieldStrength; static const Unit MagneticFlux; static const Unit MagneticFluxDensity; static const Unit Magnetization; - static const Unit ElectricalCapacitance; - static const Unit ElectricalInductance; - static const Unit ElectricalConductance; - static const Unit ElectricalResistance; - static const Unit ElectricalConductivity; - static const Unit ElectromagneticPotential; - static const Unit AmountOfSubstance; - static const Unit LuminousIntensity; - - // Pressure - static const Unit CompressiveStrength; + static const Unit Mass; + static const Unit Moment; + static const Unit One; static const Unit Pressure; + static const Unit Power; static const Unit ShearModulus; - static const Unit Stress; - static const Unit UltimateTensileStrength; - static const Unit YieldStrength; - static const Unit YoungsModulus; - + static const Unit SpecificEnergy; + static const Unit SpecificHeat; static const Unit Stiffness; static const Unit StiffnessDensity; - - static const Unit Force; - static const Unit Work; - static const Unit Power; - static const Unit Moment; - - static const Unit SpecificEnergy; + static const Unit Stress; + static const Unit SurfaceChargeDensity; + static const Unit Temperature; + static const Unit TimeSpan; static const Unit ThermalConductivity; static const Unit ThermalExpansionCoefficient; - static const Unit VolumetricThermalExpansionCoefficient; - static const Unit SpecificHeat; static const Unit ThermalTransferCoefficient; - static const Unit HeatFlux; - static const Unit DynamicViscosity; - static const Unit KinematicViscosity; + static const Unit UltimateTensileStrength; static const Unit VacuumPermittivity; + static const Unit Velocity; + static const Unit Volume; + static const Unit VolumeChargeDensity; static const Unit VolumeFlowRate; - static const Unit DissipationRate; - - static const Unit InverseLength; - static const Unit InverseArea; - static const Unit InverseVolume; - - //@} -private: - uint32_t Val; + static const Unit VolumetricThermalExpansionCoefficient; + static const Unit Work; + static const Unit YieldStrength; + static const Unit YoungsModulus; }; -inline Unit& Unit::operator*=(const Unit& that) -{ - *this = *this * that; - return *this; -} - -inline Unit& Unit::operator/=(const Unit& that) -{ - *this = *this / that; - return *this; -} - } // namespace Base #endif // BASE_Unit_H diff --git a/src/Base/UnitPyImp.cpp b/src/Base/UnitPyImp.cpp index dd174fbf8e..35edc8450e 100644 --- a/src/Base/UnitPyImp.cpp +++ b/src/Base/UnitPyImp.cpp @@ -21,6 +21,10 @@ ***************************************************************************/ #include "PreCompiled.h" +#ifndef _PreComp_ +#include +#include +#endif #include "Unit.h" @@ -35,25 +39,7 @@ using namespace Base; // returns a string which represents the object e.g. when printed in python std::string UnitPy::representation() const { - std::stringstream ret; - Unit* self = getUnitPtr(); - - ret << "Unit: "; - ret << self->getString() << " ("; - ret << (*self).length() << ","; - ret << (*self).mass() << ","; - ret << (*self).time() << ","; - ret << (*self).electricCurrent() << ","; - ret << (*self).thermodynamicTemperature() << ","; - ret << (*self).amountOfSubstance() << ","; - ret << (*self).luminousIntensity() << ","; - ret << (*self).angle() << ")"; - - std::string type = self->getTypeString(); - if (!type.empty()) { - ret << " [" << type << "]"; - } - return ret.str(); + return getUnitPtr()->representation(); } PyObject* UnitPy::PyMake(PyTypeObject* /*unused*/, PyObject* /*unused*/, PyObject* /*unused*/) @@ -98,14 +84,14 @@ int UnitPy::PyInit(PyObject* args, PyObject* /*kwd*/) } PyErr_Clear(); // set by PyArg_ParseTuple() - int i1 = 0; - int i2 = 0; - int i3 = 0; - int i4 = 0; - int i5 = 0; - int i6 = 0; - int i7 = 0; - int i8 = 0; + int i1 {0}; + int i2 {0}; + int i3 {0}; + int i4 {0}; + int i5 {0}; + int i6 {0}; + int i7 {0}; + int i8 {0}; if (PyArg_ParseTuple(args, "|iiiiiiii", &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8)) { try { *self = Unit(i1, i2, i3, i4, i5, i6, i7, i8); @@ -115,6 +101,10 @@ int UnitPy::PyInit(PyObject* args, PyObject* /*kwd*/) PyErr_SetString(PyExc_OverflowError, e.what()); return -1; } + catch (const UnderflowError& e) { + PyErr_SetString(PyExc_OverflowError, e.what()); + return -1; + } } PyErr_SetString(PyExc_TypeError, "Either string, (float,8 ints), Unit() or Quantity()"); @@ -213,13 +203,11 @@ Py::String UnitPy::getType() const Py::Tuple UnitPy::getSignature() const { - Py::Tuple tuple(8); - Unit* self = getUnitPtr(); - - for (auto i = 0; i < tuple.size(); i++) { - tuple.setItem(i, Py::Long((*self)[i])); - } - + Py::Tuple tuple {unitNumExponents}; + auto exps = getUnitPtr()->exponents(); + std::ranges::for_each(exps, [&, pos {0}](auto exp) mutable { + tuple.setItem(pos++, Py::Long {exp}); + }); return tuple; } diff --git a/src/Gui/Dialogs/DlgUnitsCalculatorImp.cpp b/src/Gui/Dialogs/DlgUnitsCalculatorImp.cpp index bf59585041..8e9ed1edc4 100644 --- a/src/Gui/Dialogs/DlgUnitsCalculatorImp.cpp +++ b/src/Gui/Dialogs/DlgUnitsCalculatorImp.cpp @@ -30,6 +30,7 @@ #include "Dialogs/DlgUnitsCalculatorImp.h" #include "ui_DlgUnitsCalculator.h" +#include #include using namespace Gui::Dialog; @@ -133,19 +134,24 @@ void DlgUnitsCalculator::textChanged(QString unit) void DlgUnitsCalculator::valueChanged(const Quantity& quant) { + std::string unitTypeStr; + try { + unitTypeStr = + Quantity::parse(ui->UnitInput->text().toStdString()).getUnit().getTypeString(); + } + catch (const Base::ParserError&) { + } // first check the unit, if it is invalid, getTypeString() outputs an empty string // explicitly check for "ee" like in "eeV" because this would trigger an exception in Unit // since it expects then a scientific notation number like "1e3" - if ((ui->UnitInput->text().mid(0, 2) == QStringLiteral("ee")) - || Unit(ui->UnitInput->text().toStdString()).getTypeString().empty()) { + if ((ui->UnitInput->text().mid(0, 2) == QStringLiteral("ee")) || unitTypeStr.empty()) { ui->ValueOutput->setText( QStringLiteral("%1 %2").arg(tr("unknown unit:"), ui->UnitInput->text())); ui->pushButton_Copy->setEnabled(false); } else { // the unit is valid // we can only convert units of the same type, thus check - if (Unit(ui->UnitInput->text().toStdString()).getTypeString() - != quant.getUnit().getTypeString()) { + if (unitTypeStr != quant.getUnit().getTypeString()) { ui->ValueOutput->setText(tr("unit mismatch")); ui->pushButton_Copy->setEnabled(false); } diff --git a/tests/src/App/ExpressionParser.cpp b/tests/src/App/ExpressionParser.cpp index d7a88267fb..fe27e45002 100644 --- a/tests/src/App/ExpressionParser.cpp +++ b/tests/src/App/ExpressionParser.cpp @@ -37,16 +37,12 @@ protected: App::DocumentObject* this_obj() { return _this_obj; } Base::Quantity parse_expression_text_as_quantity(const char* expression_text) { - auto expression = App::ExpressionParser::parse(this_obj(), expression_text); - auto expression_value = expression->getValueAsAny(); - auto quantity_result = App::any_cast(expression_value); - return quantity_result; + const auto expression = App::ExpressionParser::parse(this_obj(), expression_text); + return App::any_cast(expression->getValueAsAny()); } Base::Quantity parse_quantity_text_as_quantity(const char* quantity_text) { - auto quantity_str = std::string(quantity_text); - auto quantity_result = Base::Quantity::parse(quantity_str); - return quantity_result; + return Base::Quantity::parse(quantity_text); } private: diff --git a/tests/src/Base/Unit.cpp b/tests/src/Base/Unit.cpp index b34386d935..c4ef021f2a 100644 --- a/tests/src/Base/Unit.cpp +++ b/tests/src/Base/Unit.cpp @@ -3,191 +3,154 @@ #include // NOLINTBEGIN -TEST(Unit, TestString) +using namespace Base; + +TEST(Unit, string_simple_numerator_no_denominator) { - auto toString = [](const Base::Unit& unit) { - return unit.getString(); - }; - EXPECT_EQ(toString(Base::Unit(0, 0, 0, 0, 0, 0, 0, 0)), ""); - EXPECT_EQ(toString(Base::Unit(1, 0, 0, 0, 0, 0, 0, 0)), "mm"); - EXPECT_EQ(toString(Base::Unit(0, 1, 0, 0, 0, 0, 0, 0)), "kg"); - EXPECT_EQ(toString(Base::Unit(0, 0, 1, 0, 0, 0, 0, 0)), "s"); - EXPECT_EQ(toString(Base::Unit(0, 0, 0, 1, 0, 0, 0, 0)), "A"); - EXPECT_EQ(toString(Base::Unit(0, 0, 0, 0, 1, 0, 0, 0)), "K"); - EXPECT_EQ(toString(Base::Unit(0, 0, 0, 0, 0, 1, 0, 0)), "mol"); - EXPECT_EQ(toString(Base::Unit(0, 0, 0, 0, 0, 0, 1, 0)), "cd"); - EXPECT_EQ(toString(Base::Unit(0, 0, 0, 0, 0, 0, 0, 1)), "deg"); - EXPECT_EQ(toString(Base::Unit(2, 0, 0, 0, 0, 0, 0, 0)), "mm^2"); - EXPECT_EQ(toString(Base::Unit(1, 1, -2, 0, 0, 0, 0, 0)), "mm*kg/s^2"); + EXPECT_EQ(Unit::Length.getString(), "mm"); } -TEST(Unit, TestTypeString) +TEST(Unit, string_complex_numerator_no_denominator) { - auto toString = [](const Base::Unit& unit) { - return unit.getTypeString(); - }; - EXPECT_EQ(toString(Base::Unit::Acceleration), "Acceleration"); - EXPECT_EQ(toString(Base::Unit::AmountOfSubstance), "AmountOfSubstance"); - EXPECT_EQ(toString(Base::Unit::Angle), "Angle"); - EXPECT_EQ(toString(Base::Unit::AngleOfFriction), "Angle"); // same unit as Angle - EXPECT_EQ(toString(Base::Unit::Area), "Area"); - EXPECT_EQ(toString(Base::Unit::CurrentDensity), "CurrentDensity"); - EXPECT_EQ(toString(Base::Unit::Density), "Density"); - EXPECT_EQ(toString(Base::Unit::DissipationRate), "DissipationRate"); - EXPECT_EQ(toString(Base::Unit::DynamicViscosity), "DynamicViscosity"); - EXPECT_EQ(toString(Base::Unit::ElectricalCapacitance), "ElectricalCapacitance"); - EXPECT_EQ(toString(Base::Unit::ElectricalConductance), "ElectricalConductance"); - EXPECT_EQ(toString(Base::Unit::ElectricalConductivity), "ElectricalConductivity"); - EXPECT_EQ(toString(Base::Unit::ElectricalInductance), "ElectricalInductance"); - EXPECT_EQ(toString(Base::Unit::ElectricalResistance), "ElectricalResistance"); - EXPECT_EQ(toString(Base::Unit::ElectricCharge), "ElectricCharge"); - EXPECT_EQ(toString(Base::Unit::ElectricCurrent), "ElectricCurrent"); - EXPECT_EQ(toString(Base::Unit::ElectricPotential), "ElectricPotential"); - EXPECT_EQ(toString(Base::Unit::Frequency), "Frequency"); - EXPECT_EQ(toString(Base::Unit::Force), "Force"); - EXPECT_EQ(toString(Base::Unit::HeatFlux), "HeatFlux"); - EXPECT_EQ(toString(Base::Unit::InverseArea), "InverseArea"); - EXPECT_EQ(toString(Base::Unit::InverseLength), "InverseLength"); - EXPECT_EQ(toString(Base::Unit::InverseVolume), "InverseVolume"); - EXPECT_EQ(toString(Base::Unit::KinematicViscosity), "KinematicViscosity"); - EXPECT_EQ(toString(Base::Unit::Length), "Length"); - EXPECT_EQ(toString(Base::Unit::LuminousIntensity), "LuminousIntensity"); - EXPECT_EQ(toString(Base::Unit::MagneticFieldStrength), "MagneticFieldStrength"); - EXPECT_EQ(toString(Base::Unit::MagneticFlux), "MagneticFlux"); - EXPECT_EQ(toString(Base::Unit::MagneticFluxDensity), "MagneticFluxDensity"); - EXPECT_EQ(toString(Base::Unit::Magnetization), - "MagneticFieldStrength"); // same as MagneticFieldStrength - EXPECT_EQ(toString(Base::Unit::Mass), "Mass"); - EXPECT_EQ(toString(Base::Unit::Pressure), "Pressure"); - EXPECT_EQ(toString(Base::Unit::Power), "Power"); - EXPECT_EQ(toString(Base::Unit::ShearModulus), "Pressure"); // same as Pressure - EXPECT_EQ(toString(Base::Unit::SpecificEnergy), "SpecificEnergy"); - EXPECT_EQ(toString(Base::Unit::SpecificHeat), "SpecificHeat"); - EXPECT_EQ(toString(Base::Unit::Stiffness), "Stiffness"); - EXPECT_EQ(toString(Base::Unit::Stress), "Pressure"); // same as Pressure - EXPECT_EQ(toString(Base::Unit::Temperature), "Temperature"); - EXPECT_EQ(toString(Base::Unit::ThermalConductivity), "ThermalConductivity"); - EXPECT_EQ(toString(Base::Unit::ThermalExpansionCoefficient), "ThermalExpansionCoefficient"); - EXPECT_EQ(toString(Base::Unit::ThermalTransferCoefficient), "ThermalTransferCoefficient"); - EXPECT_EQ(toString(Base::Unit::TimeSpan), "TimeSpan"); - EXPECT_EQ(toString(Base::Unit::UltimateTensileStrength), "Pressure"); // same as Pressure - EXPECT_EQ(toString(Base::Unit::VacuumPermittivity), "VacuumPermittivity"); - EXPECT_EQ(toString(Base::Unit::Velocity), "Velocity"); - EXPECT_EQ(toString(Base::Unit::Volume), "Volume"); - EXPECT_EQ(toString(Base::Unit::VolumeFlowRate), "VolumeFlowRate"); - EXPECT_EQ(toString(Base::Unit::VolumetricThermalExpansionCoefficient), - "ThermalExpansionCoefficient"); - EXPECT_EQ(toString(Base::Unit::Work), "Work"); - EXPECT_EQ(toString(Base::Unit::YieldStrength), "Pressure"); // same as Pressure - EXPECT_EQ(toString(Base::Unit::YoungsModulus), "Pressure"); // same unit as Pressure + EXPECT_EQ(Unit::Area.getString(), "mm^2"); } -TEST(Unit, strings) + +TEST(Unit, string_complex_single_denominator) { - EXPECT_STREQ(Base::Unit::Acceleration.getString().c_str(), "mm/s^2"); - EXPECT_STREQ(Base::Unit::AmountOfSubstance.getString().c_str(), "mol"); - EXPECT_STREQ(Base::Unit::Angle.getString().c_str(), "deg"); - EXPECT_STREQ(Base::Unit::AngleOfFriction.getString().c_str(), "deg"); - EXPECT_STREQ(Base::Unit::Area.getString().c_str(), "mm^2"); - EXPECT_STREQ(Base::Unit::CurrentDensity.getString().c_str(), "A/mm^2"); - EXPECT_STREQ(Base::Unit::Density.getString().c_str(), "kg/mm^3"); - EXPECT_STREQ(Base::Unit::DissipationRate.getString().c_str(), "mm^2/s^3"); + EXPECT_EQ(Unit::DissipationRate.getString(), "mm^2/s^3"); +} + +TEST(Unit, string_no_numerator) +{ + EXPECT_EQ(Unit::InverseArea.getString(), "1/mm^2"); +} + +TEST(Unit, string_complex_multi_denominator) +{ + EXPECT_EQ(Unit::MagneticFlux.getString(), "mm^2*kg/(s^2*A)"); +} + +TEST(Unit, type_string) +{ + EXPECT_EQ(Unit::MagneticFlux.getTypeString(), "MagneticFlux"); } TEST(Unit, TestEqual) { - Base::Unit unit {1}; - EXPECT_EQ(unit.pow(2) == Base::Unit {2}, true); + EXPECT_TRUE(Unit::Length == Unit::Length); } TEST(Unit, TestNotEqual) { - Base::Unit unit {1}; - EXPECT_EQ(unit.pow(2) != Base::Unit {1}, true); + EXPECT_TRUE(Unit::Length != Unit::Area); +} + +TEST(Unit, multiply_One_is_One) +{ + EXPECT_EQ(Unit::One * Unit::One, Unit::One); } TEST(Unit, TestMult) { - EXPECT_EQ(Base::Unit {} * Base::Unit {}, Base::Unit {}); - EXPECT_EQ(Base::Unit(0, 1) * Base::Unit(1, 0), Base::Unit(1, 1)); + constexpr UnitExponents arr {1, 1, 0, 0, 0, 0, 0, 0}; + EXPECT_EQ(Unit::Mass * Unit::Length, Unit {arr}); } -TEST(Unit, TestDiv) +TEST(Unit, div) { - EXPECT_EQ(Base::Unit {} * Base::Unit {}, Base::Unit {}); - EXPECT_EQ(Base::Unit(0, 1) / Base::Unit(1, 0), Base::Unit(-1, 1)); + EXPECT_EQ(Unit::Area / Unit::Length, Unit::Length); } -TEST(Unit, TestPowNoDim) +TEST(Unit, div_by_One_does_nothing) { - Base::Unit unit {}; - EXPECT_EQ(unit.pow(2), Base::Unit {0}); - EXPECT_EQ(unit == Base::Unit::One, true); + EXPECT_EQ(Unit::Area / Unit::One, Unit::Area); } -TEST(Unit, TestPowEQ1) +TEST(Unit, pow_0_is_One) { - Base::Unit unit {2}; - EXPECT_EQ(unit.pow(1), Base::Unit {2}); + EXPECT_EQ(Unit::Area.pow(0), Unit::One); } -TEST(Unit, TestPowEQ0) +TEST(Unit, pow_1_leaves_unit_unchanged) { - Base::Unit unit {2}; - EXPECT_EQ(unit.pow(0), Base::Unit {0}); + EXPECT_EQ(Unit::Area.pow(1), Unit::Area); } -TEST(Unit, TestPowGT1) +TEST(Unit, pow_2_is_squared) { - Base::Unit unit {2}; - EXPECT_EQ(unit.pow(2), Base::Unit {4}); + EXPECT_EQ(Unit::Length.pow(2), Unit::Area); } -TEST(Unit, TestPowLT1) +TEST(Unit, pow_3_is_cubed) { - Base::Unit unit {3}; - EXPECT_EQ(unit.pow(1.0 / 3.0), Base::Unit {1}); + EXPECT_EQ(Unit::Length.pow(3), Unit::Volume); } -TEST(Unit, TestPow3DIV2) +TEST(Unit, pow_less_than_one) { - Base::Unit unit {3}; - EXPECT_THROW(unit.pow(3.0 / 2.0), Base::UnitsMismatchError); + EXPECT_EQ(Unit::Volume.pow(1.0 / 3.0), Unit::Length); } -TEST(Unit, TestOverflow) +TEST(Unit, one_still_one_after_pow) { - // this tests _that_ the expected exception is thrown - EXPECT_THROW( - { - try { - Base::Unit unit {3}; - unit.pow(10000); - } - catch (const Base::OverflowError& e) { - // and this tests that it has the correct message - EXPECT_STREQ("Unit overflow in pow()", e.what()); - throw; - } - }, - Base::OverflowError); + EXPECT_EQ(Unit::One.pow(2), Unit::One); } -TEST(Unit, TestUnderflow) +TEST(Unit, square_root) { - // this tests _that_ the expected exception is thrown - EXPECT_THROW( - { - try { - Base::Unit unit {3}; - unit.pow(-10000); - } - catch (const Base::UnderflowError& e) { - // and this tests that it has the correct message - EXPECT_STREQ("Unit underflow in pow()", e.what()); - throw; - } - }, - Base::UnderflowError); + EXPECT_EQ(Unit::Area.root(2), Unit::Length); } -// NOLINTEND +TEST(Unit, cube_root) +{ + EXPECT_EQ(Unit::Volume.root(3), Unit::Length); +} + +TEST(Unit, zero_root) +{ + EXPECT_THROW([[maybe_unused]] auto res = Unit::Area.root(0), UnitsMismatchError); +} + +TEST(Unit, one_root) +{ + EXPECT_EQ(Unit::Area.root(1), Unit::Area); +} + +TEST(Unit, TestPow3div2) +{ + EXPECT_THROW([[maybe_unused]] auto res = Unit::Volume.pow(3.0 / 2.0), UnitsMismatchError); +} + +TEST(Unit, overflow) +{ + constexpr UnitExponents arr {99, 0, 0, 0, 0, 0, 0, 0}; + EXPECT_THROW([[maybe_unused]] auto res = Unit {arr}, OverflowError); +} + +TEST(Unit, underflow) +{ + constexpr UnitExponents arr {-99, 0, 0, 0, 0, 0, 0, 0}; + EXPECT_THROW([[maybe_unused]] auto res = Unit {arr}, UnderflowError); +} + +TEST(Unit, representation_simple) +{ + const std::string expect {"Unit: mm (1,0,0,0,0,0,0,0) [Length]"}; + const auto actual = Unit::Length.representation(); + EXPECT_EQ(actual, expect); +} + +TEST(Unit, representation_complex) +{ + const std::string expect {"Unit: mm^2*kg/(s^2*A) (2,1,-2,-1,0,0,0,0) [MagneticFlux]"}; + const auto actual = Unit::MagneticFlux.representation(); + EXPECT_EQ(actual, expect); +} + +TEST(Unit, representation_no_name) +{ + constexpr Unit unit {{1, 1}}; + const std::string expect {"Unit: mm*kg (1,1,0,0,0,0,0,0)"}; + const auto actual = unit.representation(); + EXPECT_EQ(actual, expect); +}