Merge pull request #21147 from 3x380V/units

Units rework II
This commit is contained in:
Benjamin Nauck
2025-06-04 15:58:00 +02:00
committed by GitHub
26 changed files with 1103 additions and 1286 deletions

View File

@@ -488,7 +488,7 @@ static inline Quantity pyToQuantity(const Py::Object &pyobj,
}
Py::Object pyFromQuantity(const Quantity &quantity) {
if(!quantity.getUnit().isEmpty())
if (!quantity.isDimensionless())
return Py::asObject(new QuantityPy(new Quantity(quantity)));
double v = quantity.getValue();
long l;

View File

@@ -145,7 +145,7 @@ FeatureTest::FeatureTest()
ADD_PROPERTY(QuantityLength, (1.0));
QuantityLength.setUnit(Base::Unit::Length);
ADD_PROPERTY(QuantityOther, (5.0));
QuantityOther.setUnit(Base::Unit(-3, 1));
QuantityOther.setUnit(Base::Unit::Density);
// clang-format on
}

View File

@@ -194,7 +194,7 @@ void PropertyVector::getPaths(std::vector<ObjectIdentifier>& paths) const
const boost::any PropertyVector::getPathValue(const ObjectIdentifier& path) const
{
Base::Unit unit = getUnit();
if (!unit.isEmpty()) {
if (unit != Unit::One) {
std::string p = path.getSubPathStr();
if (p == ".x" || p == ".y" || p == ".z") {
// Convert double to quantity
@@ -207,7 +207,7 @@ const boost::any PropertyVector::getPathValue(const ObjectIdentifier& path) cons
bool PropertyVector::getPyPathValue(const ObjectIdentifier& path, Py::Object& res) const
{
Base::Unit unit = getUnit();
if (unit.isEmpty()) {
if (unit == Unit::One) {
return false;
}

View File

@@ -105,13 +105,12 @@ void PropertyQuantity::setPyObject(PyObject* value)
else {
Base::Quantity quant = createQuantityFromPy(value);
Unit unit = quant.getUnit();
if (unit.isEmpty()) {
if (quant.isDimensionless()) {
PropertyFloat::setValue(quant.getValue());
return;
}
if (unit != _Unit) {
if (_Unit != quant.getUnit()) {
throw Base::UnitsMismatchError("Not matching Unit!");
}
@@ -123,7 +122,7 @@ void PropertyQuantity::setPathValue(const ObjectIdentifier& /*path*/, const boos
{
auto q = App::anyToQuantity(value);
aboutToSetValue();
if (!q.getUnit().isEmpty()) {
if (!q.isDimensionless()) {
_Unit = q.getUnit();
}
_dValue = q.getValue();
@@ -187,7 +186,6 @@ void PropertyQuantityConstraint::setPyObject(PyObject* value)
{
Base::Quantity quant = createQuantityFromPy(value);
Unit unit = quant.getUnit();
double temp = quant.getValue();
if (_ConstStruct) {
if (temp > _ConstStruct->UpperBound) {
@@ -199,12 +197,12 @@ void PropertyQuantityConstraint::setPyObject(PyObject* value)
}
quant.setValue(temp);
if (unit.isEmpty()) {
if (quant.isDimensionless()) {
PropertyFloat::setValue(quant.getValue()); // clazy:exclude=skipped-base-method
return;
}
if (unit != _Unit) {
if (_Unit != quant.getUnit()) {
throw Base::UnitsMismatchError("Not matching Unit!");
}

View File

@@ -183,7 +183,7 @@ Quantity Quantity::operator/(double factor) const
Quantity Quantity::pow(const Quantity& other) const
{
if (!other.myUnit.isEmpty()) {
if (!other.isDimensionless()) {
throw Base::UnitsMismatchError("Quantity::pow(): exponent must not have a unit");
}
@@ -270,10 +270,10 @@ std::string Quantity::getSafeUserString() const
return Tools::escapeQuotesFromString(userStr);
}
/// true if it has a number without a unit
/// true if unit equals to 1, therefore quantity has no dimension
bool Quantity::isDimensionless() const
{
return isValid() && myUnit.isEmpty();
return myUnit == Unit::One;
}
/// true if it has a specific unit or no dimension.
@@ -282,12 +282,6 @@ bool Quantity::isDimensionlessOrUnit(const Unit& unit) const
return isDimensionless() || myUnit == unit;
}
// true if it has a number and a valid unit
bool Quantity::isQuantity() const
{
return isValid() && !myUnit.isEmpty();
}
// true if it has a number with or without a unit
bool Quantity::isValid() const
{
@@ -300,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 ===============================================

View File

@@ -191,12 +191,10 @@ public:
double getValueAs(const Quantity&) const;
/// true if it has a number without a unit
/// true if it has no unit
bool isDimensionless() const;
/// true if it has a specific unit or no dimension.
bool isDimensionlessOrUnit(const Unit& unit) const;
/// true if it has a number and a valid unit
bool isQuantity() const;
/// true if it has a number with or without a unit
bool isValid() const;
/// sets the quantity invalid

View File

@@ -43,19 +43,15 @@ using Base::Quantity;
// returns a string which represents the object e.g. when printed in python
std::string QuantityPy::representation() const
{
std::stringstream ret;
double val = getQuantityPtr()->getValue();
Unit unit = getQuantityPtr()->getUnit();
std::stringstream ss;
// Use Python's implementation to repr() a float
Py::Float flt(val);
ret << static_cast<std::string>(flt.repr());
if (!unit.isEmpty()) {
ret << " " << unit.getString();
Py::Float flt(getQuantityPtr()->getValue());
ss << static_cast<std::string>(flt.repr());
if (!getQuantityPtr()->isDimensionless()) {
ss << " " << getQuantityPtr()->getUnit().getString();
}
return ret.str();
return ss.str();
}
PyObject* QuantityPy::toStr(PyObject* args) const
@@ -66,17 +62,16 @@ PyObject* QuantityPy::toStr(PyObject* args) const
}
double val = getQuantityPtr()->getValue();
Unit unit = getQuantityPtr()->getUnit();
std::stringstream ret;
ret.precision(prec);
ret.setf(std::ios::fixed, std::ios::floatfield);
ret << val;
if (!unit.isEmpty()) {
ret << " " << unit.getString();
std::stringstream ss;
ss.precision(prec);
ss.setf(std::ios::fixed, std::ios::floatfield);
ss << val;
if (!getQuantityPtr()->isDimensionless()) {
ss << " " << getQuantityPtr()->getUnit().getString();
}
return Py_BuildValue("s", ret.str().c_str());
return Py_BuildValue("s", ss.str().c_str());
}
PyObject* QuantityPy::PyMake(PyTypeObject* /*unused*/, PyObject* /*unused*/, PyObject* /*unused*/)
@@ -96,45 +91,37 @@ int QuantityPy::PyInit(PyObject* args, PyObject* /*kwd*/)
*self = *(static_cast<QuantityPy*>(object)->getQuantityPtr());
return 0;
}
PyErr_Clear(); // set by PyArg_ParseTuple()
double f = std::numeric_limits<double>::max();
if (PyArg_ParseTuple(args, "dO!", &f, &(UnitPy::Type), &object)) {
*self = Quantity(f, *(static_cast<UnitPy*>(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<double>::max()) {
*self = Quantity(f,
Unit {static_cast<int8_t>(i1),
static_cast<int8_t>(i2),
static_cast<int8_t>(i3),
static_cast<int8_t>(i4),
static_cast<int8_t>(i5),
static_cast<int8_t>(i6),
static_cast<int8_t>(i7),
static_cast<int8_t>(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);
@@ -149,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());
@@ -215,7 +202,7 @@ PyObject* QuantityPy::getValueAs(PyObject* args) const
};
auto tryUnitPartsAndValue = [&]() -> std::optional<Quantity> {
double f = std::numeric_limits<double>::max();
double f;
int i1 {0};
int i2 {0};
int i3 {0};
@@ -224,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<double>::max()) {
return std::nullopt;
}
auto re = [](auto val) {
return static_cast<int8_t>(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<Quantity> {
@@ -279,11 +257,6 @@ PyObject* QuantityPy::getValueAs(PyObject* args) const
}
const auto qpUnit = qPtr->getUnit();
if (qpUnit.isEmpty()) {
err("QuantityPtr returned empty unit");
return false;
}
if (const auto qUnit = quant.getUnit(); qUnit != qpUnit) {
err("Unit mismatch (`" + qUnit.getString() + "` != `" + qpUnit.getString() + "`)");
return false;
@@ -301,7 +274,7 @@ PyObject* QuantityPy::getValueAs(PyObject* args) const
}
const auto quant = optQuant.value();
if (quant.isQuantity()) {
if (!quant.isDimensionless()) {
if (!checkQuant(quant)) {
return nullptr;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,181 +1,178 @@
/***************************************************************************
* Copyright (c) 2011 Jürgen Riegel <juergen.riegel@web.de> *
* *
* 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 *
* <https://www.gnu.org/licenses/>. *
* *
***************************************************************************/
#ifndef BASE_Unit_H
#define BASE_Unit_H
#include <array>
#include <cstdint>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>
#include <FCGlobal.h>
#include "Exception.h"
namespace Base
{
/**
* The Unit class.
*/
class BaseExport Unit
constexpr auto unitSymbols =
std::to_array<std::string_view>({"mm", "kg", "s", "A", "K", "mol", "cd", "deg"});
constexpr auto unitNumExponents {unitSymbols.size()};
using UnitExponents = std::array<int8_t, unitNumExponents>;
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;
/** Operators. */
//@{
inline Unit& operator*=(const Unit& that);
inline Unit& operator/=(const Unit& that);
int operator[](int index) const;
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);
bool operator==(const Unit&) const;
bool operator!=(const Unit& that) const;
Unit& operator*=(const Unit& that);
Unit& operator/=(const Unit& that);
Unit operator*(const Unit&) const;
Unit operator/(const Unit&) const;
bool operator==(const Unit&) const;
bool operator!=(const Unit& that) const
[[nodiscard]] Unit pow(const double exp) const;
[[nodiscard]] Unit root(const uint8_t num) const;
[[nodiscard]] UnitExponents exponents() const;
[[nodiscard]] int length() const;
[[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 !(*this == that);
return root(2);
}
Unit cbrt() const
{
return root(3);
}
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;
bool isEmpty() const;
std::string getString() const;
/// get the type as an string such as "Area", "Length" or "Pressure".
std::string getTypeString() const;
private:
UnitExponents _exps {};
std::string_view _name;
/** Predefined Unit types. */
//@{
/// Length unit
static const Unit Length;
/// Mass unit
static const Unit Mass;
constexpr void checkRange()
{
for (const auto exp : _exps) {
if (exp >= unitExponentLimit) {
throw OverflowError("Unit exponent overflow");
}
if (exp < -unitExponentLimit) {
throw UnderflowError("Unit exponent underflow");
}
}
}
/// Angle
/** Returns posIndexes, negIndexes*/
std::pair<std::vector<size_t>, std::vector<size_t>> 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

View File

@@ -21,6 +21,10 @@
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#include <algorithm>
#include <ranges>
#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;
}

View File

@@ -53,22 +53,20 @@ std::string UnitsSchema::translate(const Quantity& quant) const
std::string
UnitsSchema::translate(const Quantity& quant, double& factor, std::string& unitString) const
{
// Use defaults without schema-level translation.
factor = 1.0;
unitString = quant.getUnit().getString();
if (spec.translationSpecs.empty()) {
return toLocale(quant, 1.0, unitString);
return toLocale(quant, factor, unitString);
}
const auto unitName = quant.getUnit().getTypeString();
if (spec.translationSpecs.count(unitName) == 0) {
// no schema-level translation. Use defaults.
factor = 1.0;
unitString = quant.getUnit().getString();
if (!spec.translationSpecs.contains(unitName)) {
return toLocale(quant, factor, unitString);
}
const auto value = quant.getValue();
auto isSuitable = [&](const UnitTranslationSpec& row) {
return row.threshold > value || row.threshold == 0; // zero indicates default
};
@@ -81,7 +79,7 @@ UnitsSchema::translate(const Quantity& quant, double& factor, std::string& unitS
}
if (unitSpec->factor == 0) {
return UnitsSchemasData::runSpecial(unitSpec->unitString, value);
return UnitsSchemasData::runSpecial(unitSpec->unitString, value, factor, unitString);
}
factor = unitSpec->factor;
@@ -96,20 +94,17 @@ UnitsSchema::toLocale(const Quantity& quant, const double factor, const std::str
QLocale Lc;
const QuantityFormat& format = quant.getFormat();
if (format.option != QuantityFormat::None) {
int opt = format.option;
Lc.setNumberOptions(static_cast<QLocale::NumberOptions>(opt));
Lc.setNumberOptions(static_cast<QLocale::NumberOptions>(format.option));
}
std::string valueString =
Lc.toString((quant.getValue() / factor), format.toFormat(), format.precision).toStdString();
auto valueString =
Lc.toString(quant.getValue() / factor, format.toFormat(), format.precision).toStdString();
return fmt::format("{}{}{}",
valueString,
unitString.empty() || unitString == "°" || unitString == ""
|| unitString == "" || unitString == "\"" || unitString == "'"
? ""
: " ",
unitString);
auto notUnit = [](auto s) {
return s.empty() || s == "°" || s == "" || s == "" || s == "\"" || s == "'";
};
return fmt::format("{}{}{}", valueString, notUnit(unitString) ? "" : " ", unitString);
}
bool UnitsSchema::isMultiUnitLength() const

View File

@@ -733,17 +733,27 @@ inline std::string toDms(const double value)
* Special functions caller
*/
inline const std::map<std::string, std::function<std::string(double)>> specials // clang-format off
// clang-format off
inline const std::map<std::string, std::function<std::string(double, double&, std::string&)>> specials
{
{
{ "toDMS" , [](const double val) { return toDms(val); }},
{ "toFractional" , [](const double val) { return toFractional(val); }}
{ "toDMS" , [](const double val, double& factor, std::string& unitString) {
factor = 1.0;
unitString = "deg";
return toDms(val);
}},
{ "toFractional" , [](const double val, double& factor, std::string& unitString) {
factor = 25.4;
unitString = "in";
return toFractional(val);
}}
}
}; // clang-format on
inline std::string runSpecial(const std::string& name, const double value)
inline std::string
runSpecial(const std::string& name, const double value, double& factor, std::string& unitString)
{
return specials.contains(name) ? specials.at(name)(value) : "";
return specials.contains(name) ? specials.at(name)(value, factor, unitString) : "";
}

View File

@@ -298,14 +298,14 @@ void DlgExpressionInput::checkExpression(const QString& text)
}
auto msg = value.getUserString();
if (!impliedUnit.isEmpty()) {
if (!value.getUnit().isEmpty() && value.getUnit() != impliedUnit)
if (impliedUnit != Base::Unit::One) {
if (!value.isDimensionless() && value.getUnit() != impliedUnit)
throw Base::UnitsMismatchError("Unit mismatch between result and required unit");
value.setUnit(impliedUnit);
}
else if (!value.getUnit().isEmpty()) {
else if (!value.isDimensionless()) {
msg += " (Warning: unit discarded)";
QPalette p(ui->msg->palette());

View File

@@ -30,9 +30,13 @@
#include "Dialogs/DlgUnitsCalculatorImp.h"
#include "ui_DlgUnitsCalculator.h"
#include <Base/Quantity.h>
#include <Base/UnitsApi.h>
using namespace Gui::Dialog;
using Base::Quantity;
using Base::Unit;
using Base::UnitsApi;
/* TRANSLATOR Gui::Dialog::DlgUnitsCalculator */
@@ -55,7 +59,7 @@ DlgUnitsCalculator::DlgUnitsCalculator(QWidget* parent, Qt::WindowFlags fl)
auto addItem = [&, index {0}](const auto& item) mutable {
ui->comboBoxScheme->addItem(QString::fromStdString(item), index++);
};
auto descriptions = Base::UnitsApi::getDescriptions();
auto descriptions = UnitsApi::getDescriptions();
std::for_each(descriptions.begin(), descriptions.end(), addItem);
// clang-format off
@@ -65,7 +69,7 @@ DlgUnitsCalculator::DlgUnitsCalculator(QWidget* parent, Qt::WindowFlags fl)
this, &DlgUnitsCalculator::onComboBoxSchemeActivated);
connect(ui->spinBoxDecimals, qOverload<int>(&QSpinBox::valueChanged),
this, &DlgUnitsCalculator::onSpinBoxDecimalsValueChanged);
connect(ui->ValueInput, qOverload<const Base::Quantity&>(&InputField::valueChanged),
connect(ui->ValueInput, qOverload<const Quantity&>(&InputField::valueChanged),
this, &DlgUnitsCalculator::valueChanged);
connect(ui->ValueInput, &InputField::returnPressed,
this, &DlgUnitsCalculator::returnPressed);
@@ -86,31 +90,27 @@ DlgUnitsCalculator::DlgUnitsCalculator(QWidget* parent, Qt::WindowFlags fl)
ui->ValueInput->setText(QStringLiteral("1 cm"));
ui->UnitInput->setText(QStringLiteral("in"));
units << Base::Unit::Acceleration << Base::Unit::AmountOfSubstance << Base::Unit::Angle
<< Base::Unit::Area << Base::Unit::Density << Base::Unit::CurrentDensity
<< Base::Unit::DissipationRate << Base::Unit::DynamicViscosity
<< Base::Unit::ElectricalCapacitance << Base::Unit::ElectricalInductance
<< Base::Unit::ElectricalConductance << Base::Unit::ElectricalResistance
<< Base::Unit::ElectricalConductivity << Base::Unit::ElectricCharge
<< Base::Unit::ElectricCurrent << Base::Unit::ElectricPotential << Base::Unit::Force
<< Base::Unit::Frequency << Base::Unit::HeatFlux << Base::Unit::InverseArea
<< Base::Unit::InverseLength << Base::Unit::InverseVolume
<< Base::Unit::KinematicViscosity << Base::Unit::Length << Base::Unit::LuminousIntensity
<< Base::Unit::Mass << Base::Unit::MagneticFieldStrength << Base::Unit::MagneticFlux
<< Base::Unit::MagneticFluxDensity << Base::Unit::Magnetization << Base::Unit::Power
<< Base::Unit::Pressure << Base::Unit::SpecificEnergy << Base::Unit::SpecificHeat
<< Base::Unit::Stiffness << Base::Unit::Temperature << Base::Unit::ThermalConductivity
<< Base::Unit::ThermalExpansionCoefficient << Base::Unit::ThermalTransferCoefficient
<< Base::Unit::TimeSpan << Base::Unit::VacuumPermittivity << Base::Unit::Velocity
<< Base::Unit::Volume << Base::Unit::VolumeFlowRate
<< Base::Unit::VolumetricThermalExpansionCoefficient << Base::Unit::Work;
for (const Base::Unit& it : units) {
units << Unit::Acceleration << Unit::AmountOfSubstance << Unit::Angle << Unit::Area
<< Unit::Density << Unit::CurrentDensity << Unit::DissipationRate
<< Unit::DynamicViscosity << Unit::ElectricalCapacitance << Unit::ElectricalInductance
<< Unit::ElectricalConductance << Unit::ElectricalResistance
<< Unit::ElectricalConductivity << Unit::ElectricCharge << Unit::ElectricCurrent
<< Unit::ElectricPotential << Unit::Force << Unit::Frequency << Unit::HeatFlux
<< Unit::InverseArea << Unit::InverseLength << Unit::InverseVolume
<< Unit::KinematicViscosity << Unit::Length << Unit::LuminousIntensity << Unit::Mass
<< Unit::MagneticFieldStrength << Unit::MagneticFlux << Unit::MagneticFluxDensity
<< Unit::Magnetization << Unit::Power << Unit::Pressure << Unit::SpecificEnergy
<< Unit::SpecificHeat << Unit::Stiffness << Unit::Temperature << Unit::ThermalConductivity
<< Unit::ThermalExpansionCoefficient << Unit::ThermalTransferCoefficient << Unit::TimeSpan
<< Unit::VacuumPermittivity << Unit::Velocity << Unit::Volume << Unit::VolumeFlowRate
<< Unit::VolumetricThermalExpansionCoefficient << Unit::Work;
for (const Unit& it : units) {
ui->unitsBox->addItem(QString::fromStdString(it.getTypeString()));
}
ui->quantitySpinBox->setValue(1.0);
ui->quantitySpinBox->setUnit(units.front());
ui->spinBoxDecimals->setValue(Base::UnitsApi::getDecimals());
ui->spinBoxDecimals->setValue(UnitsApi::getDecimals());
}
/** Destroys the object and frees any allocated resources */
@@ -132,27 +132,32 @@ void DlgUnitsCalculator::textChanged(QString unit)
valueChanged(actValue);
}
void DlgUnitsCalculator::valueChanged(const Base::Quantity& quant)
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 Base::Unit
// 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"))
|| Base::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 (Base::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);
}
else { // the unit is valid and has the same type
double convertValue =
Base::Quantity::parse("1" + ui->UnitInput->text().toStdString()).getValue();
Quantity::parse("1" + ui->UnitInput->text().toStdString()).getValue();
// we got now e.g. for "1 in" the value '25.4' because 1 in = 25.4 mm
// the result is now just quant / convertValue because the input is always in a base
// unit (an input of "1 cm" will immediately be converted to "10 mm" by Gui::InputField
@@ -164,7 +169,7 @@ void DlgUnitsCalculator::valueChanged(const Base::Quantity& quant)
// "10 um" in "in", thus only if value > 0.005 because FC's default are 2 decimals
QString val = QLocale().toString(value, 'g');
if (!val.contains(QChar::fromLatin1('e')) && (value > 0.005)) {
val = QLocale().toString(value, 'f', Base::UnitsApi::getDecimals());
val = QLocale().toString(value, 'f', UnitsApi::getDecimals());
}
// create the output string
QString out = QStringLiteral("%1 %2").arg(val, ui->UnitInput->text());
@@ -201,13 +206,13 @@ void DlgUnitsCalculator::onUnitsBoxActivated(int index)
{
// SI units use [m], not [mm] for lengths
//
Base::Quantity q = ui->quantitySpinBox->value();
Quantity q = ui->quantitySpinBox->value();
int32_t old = q.getUnit().length();
double value = q.getValue();
Base::Unit unit = units[index];
Unit unit = units[index];
int32_t len = unit.length();
ui->quantitySpinBox->setValue(Base::Quantity(value * std::pow(10.0, 3 * (len - old)), unit));
ui->quantitySpinBox->setValue(Quantity(value * std::pow(10.0, 3 * (len - old)), unit));
}
void DlgUnitsCalculator::onComboBoxSchemeActivated(int index)

View File

@@ -278,11 +278,11 @@ void InputField::newInput(const QString & text)
return;
}
if (res.getUnit().isEmpty())
if (res.isDimensionless())
res.setUnit(this->actUnit);
// check if unit fits!
if(!actUnit.isEmpty() && !res.getUnit().isEmpty() && actUnit != res.getUnit()){
if (actUnit != Unit::One && !res.isDimensionless() && actUnit != res.getUnit()){
if (iconLabel->isHidden()) {
iconLabel->setVisible(true);
}
@@ -644,7 +644,7 @@ void InputField::focusInEvent(QFocusEvent *event)
void InputField::focusOutEvent(QFocusEvent *event)
{
try {
if (Quantity::parse(this->text().toStdString()).getUnit().isEmpty()) {
if (Quantity::parse(this->text().toStdString()).isDimensionless()) {
// if user didn't enter a unit, we virtually compensate
// the multiplication factor induced by user unit system
double factor;

View File

@@ -224,7 +224,7 @@ public:
ok = parseString(copy, res, value, path);
// If result does not have unit: add default unit
if (res.getUnit().isEmpty()){
if (res.isDimensionless()) {
res.setUnit(unit);
}

View File

@@ -1786,15 +1786,11 @@ void PropertyVectorDistanceItem::setValue(const QVariant& variant)
}
const Base::Vector3d& value = variant.value<Base::Vector3d>();
Base::Quantity x = Base::Quantity(value.x, Base::Unit::Length);
Base::Quantity y = Base::Quantity(value.y, Base::Unit::Length);
Base::Quantity z = Base::Quantity(value.z, Base::Unit::Length);
Base::QuantityFormat format(Base::QuantityFormat::Default, highPrec);
std::string val = fmt::format("({}, {}, {})",
Base::UnitsApi::toNumber(x, format),
Base::UnitsApi::toNumber(y, format),
Base::UnitsApi::toNumber(z, format));
Base::UnitsApi::toNumber(value.x, format),
Base::UnitsApi::toNumber(value.y, format),
Base::UnitsApi::toNumber(value.z, format));
setPropertyValue(val);
}

View File

@@ -611,12 +611,11 @@ Base::Unit FemPostPipeline::getFrameUnit()
vtkAbstractArray* TimeInfo = multiblock->GetFieldData()->GetAbstractArray("TimeInfo");
if (!TimeInfo->IsA("vtkStringArray") || TimeInfo->GetNumberOfTuples() < 2) {
// units cannot be undefined, so use time
return Base::Unit::TimeSpan;
}
return Base::Unit(vtkStringArray::SafeDownCast(TimeInfo)->GetValue(1));
auto qty = Base::Quantity(0, vtkStringArray::SafeDownCast(TimeInfo)->GetValue(1));
return qty.getUnit();
}
std::vector<double> FemPostPipeline::getFrameValues()

View File

@@ -201,8 +201,8 @@ int EditDatumDialog::exec(bool atCursor)
void EditDatumDialog::accepted()
{
Base::Quantity newQuant = ui_ins_datum->labelEdit->value();
if (newQuant.isQuantity() || (Constr->Type == Sketcher::SnellsLaw && newQuant.isDimensionless())
|| (Constr->Type == Sketcher::Weight && newQuant.isDimensionless())) {
if (Constr->Type == Sketcher::SnellsLaw || Constr->Type == Sketcher::Weight
|| !newQuant.isDimensionless()) {
// save the value for the history
ui_ins_datum->labelEdit->pushToHistory();

View File

@@ -607,7 +607,7 @@ void Cell::setComputedUnit(const Base::Unit& unit)
PropertySheet::AtomicPropertyChange signaller(*owner);
computedUnit = unit;
setUsed(COMPUTED_UNIT_SET, !computedUnit.isEmpty());
setUsed(COMPUTED_UNIT_SET, computedUnit != Unit::One);
setDirty();
signaller.tryInvoke();
@@ -1110,7 +1110,7 @@ std::string Cell::getFormattedQuantity()
const Base::Unit& computedUnit = floatProp->getUnit();
qFormatted = QLocale().toString(rawVal, 'f', Base::UnitsApi::getDecimals());
if (hasDisplayUnit) {
if (computedUnit.isEmpty() || computedUnit == du.unit) {
if (computedUnit == Unit::One || computedUnit == du.unit) {
QString number =
QLocale().toString(rawVal / duScale, 'f', Base::UnitsApi::getDecimals());
qFormatted = number + QString::fromStdString(" " + displayUnit.stringRep);

View File

@@ -802,7 +802,7 @@ void Sheet::updateProperty(CellAddress key)
Base::PyGILStateLocker lock;
setObjectProperty(key, constant->getPyValue());
}
else if (!number->getUnit().isEmpty()) {
else if (number->getUnit() != Unit::One) {
setQuantityProperty(key, number->getValue(), number->getUnit());
}
else if (number->isInteger(&l)) {

View File

@@ -384,7 +384,7 @@ QVariant SheetModel::data(const QModelIndex& index, int role) const
// Display locale specific decimal separator (#0003875,#0003876)
if (cell->getDisplayUnit(displayUnit)) {
if (computedUnit.isEmpty() || computedUnit == displayUnit.unit) {
if (computedUnit == Base::Unit::One || computedUnit == displayUnit.unit) {
QString number =
QLocale().toString(floatProp->getValue() / displayUnit.scaler,
'f',

View File

@@ -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<Base::Quantity>(expression_value);
return quantity_result;
const auto expression = App::ExpressionParser::parse(this_obj(), expression_text);
return App::any_cast<Base::Quantity>(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:

View File

@@ -27,13 +27,6 @@ TEST(BaseQuantity, TestParse)
EXPECT_THROW(auto rew [[maybe_unused]] = Quantity::parse("1,234,500.12 kg"), ParserError);
}
TEST(BaseQuantity, TestDim)
{
const Quantity q1 {0, Unit::Area};
EXPECT_EQ(q1.isQuantity(), true);
}
TEST(BaseQuantity, TestNoDim)
{
const Quantity q1 {};

View File

@@ -29,6 +29,7 @@
#include "Base/UnitsSchemas.h"
#include <QLocale>
#include <array>
#include <string>
using Base::Quantity;
@@ -76,6 +77,119 @@ protected:
std::unique_ptr<UnitsSchemas> schemas; // NOLINT
};
TEST_F(SchemaTest, meter_decimal_1_mm_precision_6)
{
const std::string result = setWithPrecision("MeterDecimal", 1.0, Unit::Length, 6);
const auto expect {"0.001000 m"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, meter_decimal_15_mm2_precision_6)
{
const std::string result = setWithPrecision("MeterDecimal", 15.0, Unit::Area, 6);
const auto expect {"0.000015 m^2"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, meter_decimal_123456000_mm3_precision_6)
{
const std::string result = setWithPrecision("MeterDecimal", 123456000.0, Unit::Volume, 6);
const auto expect {"0.123456 m^3"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, meter_decimal_123456000_W_precision_6)
{
const std::string result = setWithPrecision("MeterDecimal", 123456000.0, Unit::Power, 6);
const auto expect {"123.456000 W"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, meter_decimal_123456000_V_precision_6)
{
const std::string result =
setWithPrecision("MeterDecimal", 123456000.0, Unit::ElectricPotential, 6);
const auto expect {"123.456000 V"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, meter_decimal_123456000_W_m2_precision_6)
{
const std::string result = setWithPrecision("MeterDecimal", 123.456, Unit::HeatFlux, 6);
const auto expect {"123.456000 W/m^2"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, meter_decimal_123456000_m_s_precision_6)
{
const std::string result = setWithPrecision("MeterDecimal", 123.456, Unit::Velocity, 6);
const auto expect {"0.123456 m/s"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, mks_1_mm_precision_6)
{
const std::string result = setWithPrecision("MKS", 1.0, Unit::Length, 6);
const auto expect {"1.000000 mm"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, mks_15_mm2_precision_6)
{
const std::string result = setWithPrecision("MKS", 15.0, Unit::Area, 6);
const auto expect {"15.000000 mm^2"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, mks_123456000_mm3_precision_6)
{
const std::string result = setWithPrecision("MKS", 123456000.0, Unit::Volume, 6);
const auto expect {"123.456000 l"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, mks_123456000_W_precision_6)
{
const std::string result = setWithPrecision("MKS", 123456000.0, Unit::Power, 6);
const auto expect {"123.456000 W"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, mks_123456000_V_precision_6)
{
const std::string result = setWithPrecision("MKS", 123456000.0, Unit::ElectricPotential, 6);
const auto expect {"123.456000 V"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, mks_123456000_W_m2_precision_6)
{
const std::string result = setWithPrecision("MKS", 123.456, Unit::HeatFlux, 6);
const auto expect {"123.456000 W/m^2"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, mks_123456000_m_s_precision_6)
{
const std::string result = setWithPrecision("MKS", 123.456, Unit::Velocity, 6);
const auto expect {"0.123456 m/s"};
EXPECT_EQ(result, expect);
}
TEST_F(SchemaTest, imperial_decimal_1_mm_default_precision)
{
const std::string result = set("ImperialDecimal", Unit::Length, 1.0);
@@ -410,3 +524,161 @@ TEST_F(SchemaTest, unknown_schema_name_throws)
{
EXPECT_THROW(UnitsApi::setSchema("Unknown"), RuntimeError);
}
TEST_F(SchemaTest, round_trip_test)
{
const auto units = std::to_array<Unit>({
Unit::Length,
Unit::Mass,
Unit::Area,
Unit::Density,
Unit::Volume,
Unit::TimeSpan,
Unit::Frequency,
Unit::Velocity,
Unit::Acceleration,
Unit::Temperature,
Unit::CurrentDensity,
Unit::ElectricCurrent,
Unit::ElectricPotential,
Unit::ElectricCharge,
Unit::SurfaceChargeDensity,
Unit::MagneticFieldStrength,
Unit::MagneticFlux,
Unit::MagneticFluxDensity,
Unit::Magnetization,
Unit::ElectricalCapacitance,
Unit::ElectricalInductance,
Unit::ElectricalConductance,
Unit::ElectricalResistance,
Unit::ElectricalConductivity,
Unit::ElectromagneticPotential,
Unit::AmountOfSubstance,
Unit::LuminousIntensity,
Unit::CompressiveStrength,
Unit::Pressure,
Unit::ShearModulus,
Unit::Stress,
Unit::UltimateTensileStrength,
Unit::YieldStrength,
Unit::YoungsModulus,
Unit::Stiffness,
Unit::StiffnessDensity,
Unit::Force,
Unit::Work,
Unit::Power,
Unit::Moment,
Unit::SpecificEnergy,
Unit::ThermalConductivity,
Unit::ThermalExpansionCoefficient,
Unit::VolumetricThermalExpansionCoefficient,
Unit::SpecificHeat,
Unit::ThermalTransferCoefficient,
Unit::HeatFlux,
Unit::DynamicViscosity,
Unit::KinematicViscosity,
Unit::VacuumPermittivity,
Unit::VolumeFlowRate,
Unit::DissipationRate,
Unit::InverseLength,
Unit::InverseArea,
Unit::InverseVolume,
});
std::array values = {0.01, 0.1, 1.0, 10.0, 100.0};
double factor {};
std::string unitString;
UnitsApi::setDecimals(16);
UnitsApi::setSchema("Internal");
for (auto unit : units) {
for (double value : values) {
Quantity q1 {value, unit};
std::string result = UnitsApi::schemaTranslate(q1, factor, unitString);
Quantity q2 = Quantity::parse(result);
EXPECT_DOUBLE_EQ(q2.getValue(), value);
}
}
UnitsApi::setSchema("MKS");
for (auto unit : units) {
for (double value : values) {
Quantity q1 {value, unit};
std::string result = UnitsApi::schemaTranslate(q1, factor, unitString);
Quantity q2 = Quantity::parse(result);
EXPECT_DOUBLE_EQ(q2.getValue(), value);
}
}
UnitsApi::setSchema("Imperial");
for (auto unit : units) {
for (double value : values) {
Quantity q1 {value, unit};
std::string result = UnitsApi::schemaTranslate(q1, factor, unitString);
Quantity q2 = Quantity::parse(result);
EXPECT_NEAR(q2.getValue(), value, 0.001);
}
}
UnitsApi::setSchema("ImperialDecimal");
for (auto unit : units) {
for (double value : values) {
Quantity q1 {value, unit};
std::string result = UnitsApi::schemaTranslate(q1, factor, unitString);
Quantity q2 = Quantity::parse(result);
EXPECT_NEAR(q2.getValue(), value, 0.001);
}
}
UnitsApi::setSchema("Centimeter");
for (auto unit : units) {
for (double value : values) {
Quantity q1 {value, unit};
std::string result = UnitsApi::schemaTranslate(q1, factor, unitString);
Quantity q2 = Quantity::parse(result);
EXPECT_DOUBLE_EQ(q2.getValue(), value);
}
}
UnitsApi::setSchema("MmMin");
for (auto unit : units) {
for (double value : values) {
Quantity q1 {value, unit};
std::string result = UnitsApi::schemaTranslate(q1, factor, unitString);
Quantity q2 = Quantity::parse(result);
EXPECT_DOUBLE_EQ(q2.getValue(), value);
}
}
UnitsApi::setSchema("ImperialCivil");
for (auto unit : units) {
for (double value : values) {
Quantity q1 {value, unit};
std::string result = UnitsApi::schemaTranslate(q1, factor, unitString);
Quantity q2 = Quantity::parse(result);
EXPECT_NEAR(q2.getValue(), value, 0.001);
}
}
UnitsApi::setSchema("FEM");
for (auto unit : units) {
for (double value : values) {
Quantity q1 {value, unit};
std::string result = UnitsApi::schemaTranslate(q1, factor, unitString);
Quantity q2 = Quantity::parse(result);
EXPECT_DOUBLE_EQ(q2.getValue(), value);
}
}
UnitsApi::setSchema("MeterDecimal");
for (auto unit : units) {
for (double value : values) {
Quantity q1 {value, unit};
std::string result = UnitsApi::schemaTranslate(q1, factor, unitString);
Quantity q2 = Quantity::parse(result);
EXPECT_DOUBLE_EQ(q2.getValue(), value);
}
}
}

View File

@@ -3,191 +3,154 @@
#include <Base/Exception.h>
// 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.isEmpty(), 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);
}