diff --git a/CodeGen/Generators/QuantityRelationsParser.cs b/CodeGen/Generators/QuantityRelationsParser.cs index 401c7e7b10..355c731261 100644 --- a/CodeGen/Generators/QuantityRelationsParser.cs +++ b/CodeGen/Generators/QuantityRelationsParser.cs @@ -23,8 +23,7 @@ internal static class QuantityRelationsParser /// The relations are defined in UnitRelations.json /// Each defined relation can be applied multiple times to one or two quantities depending on the operator and the operands. /// - /// The format of a relation definition is "Quantity.Unit operator Quantity.Unit = Quantity.Unit" (See examples below). - /// "double" can be used as a unitless operand. + /// The format of a relation definition is "Quantity.Unit = Quantity.Unit * Quantity.Unit" (See examples below). /// "1" can be used as the result operand to define inverse relations. /// /// Division relations are inferred from multiplication relations, @@ -43,10 +42,8 @@ public static void ParseAndApplyRelations(string rootDir, Quantity[] quantities) { var quantityDictionary = quantities.ToDictionary(q => q.Name, q => q); - // Add double and 1 as pseudo-quantities to validate relations that use them. - var pseudoQuantity = new Quantity { Name = null!, Units = [new Unit { SingularName = null! }] }; - quantityDictionary["double"] = pseudoQuantity with { Name = "double" }; - quantityDictionary["1"] = pseudoQuantity with { Name = "1" }; + // Add 1 as a pseudo-quantity to validate inverse relations. + quantityDictionary["1"] = new Quantity { Name = "1", Units = [new Unit()] }; var relations = ParseRelations(rootDir, quantityDictionary); @@ -61,7 +58,7 @@ public static void ParseAndApplyRelations(string rootDir, Quantity[] quantities) RightUnit = r.LeftUnit, }) .ToList()); - + // We can infer division relations from multiplication relations. relations.AddRange(relations .Where(r => r is { Operator: "*", NoInferredDivision: false }) @@ -81,8 +78,8 @@ public static void ParseAndApplyRelations(string rootDir, Quantity[] quantities) relations.Sort(); var duplicates = relations - .GroupBy(r => r.SortString) - .Where(g => g.Count() > 1) + .CountBy(r => r.SortString) + .Where(g => g.Value > 1) .Select(g => g.Key) .ToList(); @@ -91,10 +88,10 @@ public static void ParseAndApplyRelations(string rootDir, Quantity[] quantities) var list = string.Join("\n ", duplicates); throw new UnitsNetCodeGenException($"Duplicate inferred relations:\n {list}"); } - + var ambiguous = relations - .GroupBy(r => $"{r.LeftQuantity.Name} {r.Operator} {r.RightQuantity.Name}") - .Where(g => g.Count() > 1) + .CountBy(r => r.DisambiguationString) + .Where(g => g.Value > 1) .Select(g => g.Key) .ToList(); @@ -106,23 +103,8 @@ public static void ParseAndApplyRelations(string rootDir, Quantity[] quantities) foreach (var quantity in quantities) { - var quantityRelations = new List(); - - foreach (var relation in relations) - { - if (relation.LeftQuantity == quantity) - { - // The left operand of a relation is responsible for generating the operator. - quantityRelations.Add(relation); - } - else if (relation.RightQuantity == quantity && relation.LeftQuantity.Name is "double") - { - // Because we cannot add operators to double we make the right operand responsible in this case. - quantityRelations.Add(relation); - } - } - - quantity.Relations = quantityRelations.ToArray(); + // The left operand of a relation is responsible for generating the operator. + quantity.Relations = relations.Where(relation => relation.LeftQuantity == quantity).ToArray(); } } diff --git a/CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs b/CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs index dfaa53af3d..6886c10832 100644 --- a/CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs +++ b/CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs @@ -854,28 +854,11 @@ private void GenerateRelationalOperators() var leftPart = $"{leftParameter}.{leftConversionProperty}"; var rightPart = $"{rightParameter}.{rightConversionProperty}"; - if (leftParameter is "double") - { - leftParameter = leftPart = "value"; - } - - if (rightParameter is "double") - { - rightParameter = rightPart = "value"; - } - - var expression = $"{leftPart} {relation.Operator} {rightPart}"; - - if (relation.ResultQuantity.Name is not "double") - { - expression = $"{relation.ResultQuantity.Name}.From{relation.ResultUnit.PluralName}({expression})"; - } - Writer.WL($@" /// Get from {relation.Operator} . public static {relation.ResultQuantity.Name} operator {relation.Operator}({relation.LeftQuantity.Name} {leftParameter}, {relation.RightQuantity.Name} {rightParameter}) {{ - return {expression}; + return {relation.ResultQuantity.Name}.From{relation.ResultUnit.PluralName}({leftPart} {relation.Operator} {rightPart}); }} "); } diff --git a/CodeGen/JsonTypes/QuantityRelation.cs b/CodeGen/JsonTypes/QuantityRelation.cs index 35e97a6bc0..2e2cc9178a 100644 --- a/CodeGen/JsonTypes/QuantityRelation.cs +++ b/CodeGen/JsonTypes/QuantityRelation.cs @@ -19,17 +19,15 @@ internal record QuantityRelation : IComparable public Quantity ResultQuantity = null!; public Unit ResultUnit = null!; - public string SortString => ResultQuantity.Name + PrependDot(ResultUnit.SingularName) - + " = " - + LeftQuantity.Name + PrependDot(LeftUnit.SingularName) - + " " + Operator + " " - + RightQuantity.Name + PrependDot(RightUnit.SingularName); + public string DisambiguationString => $"{LeftQuantity.Name} {Operator} {RightQuantity.Name}"; + + public string SortString => $"{ResultQuantity.Name}.{ResultUnit.SingularName} = " + + $"{LeftQuantity.Name}.{LeftUnit.SingularName} {Operator} " + + $"{RightQuantity.Name}.{RightUnit.SingularName}"; public int CompareTo(QuantityRelation? other) { return string.Compare(SortString, other?.SortString, StringComparison.Ordinal); } - - private static string PrependDot(string? s) => s == null ? string.Empty : "." + s; } } \ No newline at end of file diff --git a/Common/UnitRelations.json b/Common/UnitRelations.json index bded89653c..0e5ad9a273 100644 --- a/Common/UnitRelations.json +++ b/Common/UnitRelations.json @@ -11,7 +11,6 @@ "Area.SquareMeter = Length.Meter * Length.Meter", "Area.SquareMeter = Volume.CubicMeter * ReciprocalLength.InverseMeter", "AreaMomentOfInertia.MeterToTheFourth = Volume.CubicMeter * Length.Meter", - "double = SpecificEnergy.JoulePerKilogram * BrakeSpecificFuelConsumption.KilogramPerJoule", "DynamicViscosity.NewtonSecondPerMeterSquared = Density.KilogramPerCubicMeter * KinematicViscosity.SquareMeterPerSecond", "ElectricCharge.AmpereHour = ElectricCurrent.Ampere * Duration.Hour", "ElectricCurrent.Ampere = ElectricCurrentGradient.AmperePerSecond * Duration.Second", @@ -69,6 +68,7 @@ "ReciprocalArea.InverseSquareMeter = ReciprocalLength.InverseMeter * ReciprocalLength.InverseMeter", "ReciprocalLength.InverseMeter = Length.Meter * ReciprocalArea.InverseSquareMeter", "RotationalStiffness.NewtonMeterPerRadian = RotationalStiffnessPerLength.NewtonMeterPerRadianPerMeter * Length.Meter", + "Scalar.Amount = SpecificEnergy.JoulePerKilogram * BrakeSpecificFuelConsumption.KilogramPerJoule", "SpecificEnergy.JoulePerKilogram = SpecificEntropy.JoulePerKilogramKelvin * TemperatureDelta.Kelvin", "SpecificEnergy.JoulePerKilogram = Speed.MeterPerSecond * Speed.MeterPerSecond", "SpecificWeight.NewtonPerCubicMeter = Acceleration.MeterPerSecondSquared * Density.KilogramPerCubicMeter", diff --git a/UnitsNet.Tests/CustomCode/BrakeSpecificFuelConsumptionTests.cs b/UnitsNet.Tests/CustomCode/BrakeSpecificFuelConsumptionTests.cs index 95f6bbeb38..b621adf1f2 100644 --- a/UnitsNet.Tests/CustomCode/BrakeSpecificFuelConsumptionTests.cs +++ b/UnitsNet.Tests/CustomCode/BrakeSpecificFuelConsumptionTests.cs @@ -22,9 +22,9 @@ public void PowerTimesBrakeSpecificFuelConsumptionEqualsMassFlow() } [Fact] - public void DoubleDividedByBrakeSpecificFuelConsumptionEqualsSpecificEnergy() + public void ScalarDividedByBrakeSpecificFuelConsumptionEqualsSpecificEnergy() { - SpecificEnergy massFlow = 2 / BrakeSpecificFuelConsumption.FromKilogramsPerJoule(4); + SpecificEnergy massFlow = Scalar.FromAmount(2) / BrakeSpecificFuelConsumption.FromKilogramsPerJoule(4); Assert.Equal(SpecificEnergy.FromJoulesPerKilogram(0.5), massFlow); } @@ -32,7 +32,7 @@ public void DoubleDividedByBrakeSpecificFuelConsumptionEqualsSpecificEnergy() public void BrakeSpecificFuelConsumptionTimesSpecificEnergyEqualsEnergy() { var value = BrakeSpecificFuelConsumption.FromKilogramsPerJoule(20) * SpecificEnergy.FromJoulesPerKilogram(10); - Assert.Equal(200, value); + Assert.Equal(Scalar.FromAmount(200), value); } } } diff --git a/UnitsNet.Tests/CustomCode/SpecificEnergyTests.cs b/UnitsNet.Tests/CustomCode/SpecificEnergyTests.cs index f17204e372..48d9e80203 100644 --- a/UnitsNet.Tests/CustomCode/SpecificEnergyTests.cs +++ b/UnitsNet.Tests/CustomCode/SpecificEnergyTests.cs @@ -60,9 +60,9 @@ public void SpecificEnergyTimesMassEqualsEnergy() } [Fact] - public void DoubleDividedBySpecificEnergyEqualsBrakeSpecificFuelConsumption() + public void ScalarDividedBySpecificEnergyEqualsBrakeSpecificFuelConsumption() { - BrakeSpecificFuelConsumption bsfc = 2 / SpecificEnergy.FromJoulesPerKilogram(4); + BrakeSpecificFuelConsumption bsfc = Scalar.FromAmount(2) / SpecificEnergy.FromJoulesPerKilogram(4); Assert.Equal(BrakeSpecificFuelConsumption.FromKilogramsPerJoule(0.5), bsfc); } @@ -77,7 +77,7 @@ public void SpecificEnergyTimesMassFlowEqualsPower() public void SpecificEnergyTimesBrakeSpecificFuelConsumptionEqualsEnergy() { var value = SpecificEnergy.FromJoulesPerKilogram(10) * BrakeSpecificFuelConsumption.FromKilogramsPerJoule(20); - Assert.Equal(200, value); + Assert.Equal(Scalar.FromAmount(200), value); } [Fact] diff --git a/UnitsNet/GeneratedCode/Quantities/BrakeSpecificFuelConsumption.g.cs b/UnitsNet/GeneratedCode/Quantities/BrakeSpecificFuelConsumption.g.cs index fc7b253fa6..fdc8fdea83 100644 --- a/UnitsNet/GeneratedCode/Quantities/BrakeSpecificFuelConsumption.g.cs +++ b/UnitsNet/GeneratedCode/Quantities/BrakeSpecificFuelConsumption.g.cs @@ -41,7 +41,7 @@ namespace UnitsNet #if NET7_0_OR_GREATER IDivisionOperators, IMultiplyOperators, - IMultiplyOperators, + IMultiplyOperators, IComparisonOperators, IParsable, #endif @@ -524,16 +524,10 @@ public static bool TryParseUnit([NotNullWhen(true)]string? str, IFormatProvider? return MassFlow.FromKilogramsPerSecond(brakeSpecificFuelConsumption.KilogramsPerJoule * power.Watts); } - /// Get from / . - public static SpecificEnergy operator /(double value, BrakeSpecificFuelConsumption brakeSpecificFuelConsumption) + /// Get from * . + public static Scalar operator *(BrakeSpecificFuelConsumption brakeSpecificFuelConsumption, SpecificEnergy specificEnergy) { - return SpecificEnergy.FromJoulesPerKilogram(value / brakeSpecificFuelConsumption.KilogramsPerJoule); - } - - /// Get from * . - public static double operator *(BrakeSpecificFuelConsumption brakeSpecificFuelConsumption, SpecificEnergy specificEnergy) - { - return brakeSpecificFuelConsumption.KilogramsPerJoule * specificEnergy.JoulesPerKilogram; + return Scalar.FromAmount(brakeSpecificFuelConsumption.KilogramsPerJoule * specificEnergy.JoulesPerKilogram); } #endregion diff --git a/UnitsNet/GeneratedCode/Quantities/Scalar.g.cs b/UnitsNet/GeneratedCode/Quantities/Scalar.g.cs index abc9a24426..c97fd0c560 100644 --- a/UnitsNet/GeneratedCode/Quantities/Scalar.g.cs +++ b/UnitsNet/GeneratedCode/Quantities/Scalar.g.cs @@ -40,6 +40,8 @@ namespace UnitsNet IArithmeticQuantity, #if NET7_0_OR_GREATER IDivisionOperators, + IDivisionOperators, + IDivisionOperators, IComparisonOperators, IParsable, #endif @@ -468,6 +470,22 @@ public static bool TryParseUnit([NotNullWhen(true)]string? str, IFormatProvider? #endregion + #region Relational Operators + + /// Get from / . + public static BrakeSpecificFuelConsumption operator /(Scalar scalar, SpecificEnergy specificEnergy) + { + return BrakeSpecificFuelConsumption.FromKilogramsPerJoule(scalar.Amount / specificEnergy.JoulesPerKilogram); + } + + /// Get from / . + public static SpecificEnergy operator /(Scalar scalar, BrakeSpecificFuelConsumption brakeSpecificFuelConsumption) + { + return SpecificEnergy.FromJoulesPerKilogram(scalar.Amount / brakeSpecificFuelConsumption.KilogramsPerJoule); + } + + #endregion + #region Equality / IComparable /// Returns true if less or equal to. diff --git a/UnitsNet/GeneratedCode/Quantities/SpecificEnergy.g.cs b/UnitsNet/GeneratedCode/Quantities/SpecificEnergy.g.cs index 26e37a9e26..1dd4004144 100644 --- a/UnitsNet/GeneratedCode/Quantities/SpecificEnergy.g.cs +++ b/UnitsNet/GeneratedCode/Quantities/SpecificEnergy.g.cs @@ -45,10 +45,10 @@ namespace UnitsNet IDivisionOperators, IMultiplyOperators, IMultiplyOperators, + IMultiplyOperators, IDivisionOperators, IDivisionOperators, IDivisionOperators, - IMultiplyOperators, IComparisonOperators, IParsable, #endif @@ -957,12 +957,6 @@ public static bool TryParseUnit([NotNullWhen(true)]string? str, IFormatProvider? #region Relational Operators - /// Get from / . - public static BrakeSpecificFuelConsumption operator /(double value, SpecificEnergy specificEnergy) - { - return BrakeSpecificFuelConsumption.FromKilogramsPerJoule(value / specificEnergy.JoulesPerKilogram); - } - /// Get from * . public static Energy operator *(SpecificEnergy specificEnergy, Mass mass) { @@ -975,6 +969,12 @@ public static bool TryParseUnit([NotNullWhen(true)]string? str, IFormatProvider? return Power.FromWatts(specificEnergy.JoulesPerKilogram * massFlow.KilogramsPerSecond); } + /// Get from * . + public static Scalar operator *(SpecificEnergy specificEnergy, BrakeSpecificFuelConsumption brakeSpecificFuelConsumption) + { + return Scalar.FromAmount(specificEnergy.JoulesPerKilogram * brakeSpecificFuelConsumption.KilogramsPerJoule); + } + /// Get from / . public static SpecificEntropy operator /(SpecificEnergy specificEnergy, TemperatureDelta temperatureDelta) { @@ -993,12 +993,6 @@ public static bool TryParseUnit([NotNullWhen(true)]string? str, IFormatProvider? return TemperatureDelta.FromKelvins(specificEnergy.JoulesPerKilogram / specificEntropy.JoulesPerKilogramKelvin); } - /// Get from * . - public static double operator *(SpecificEnergy specificEnergy, BrakeSpecificFuelConsumption brakeSpecificFuelConsumption) - { - return specificEnergy.JoulesPerKilogram * brakeSpecificFuelConsumption.KilogramsPerJoule; - } - #endregion #region Equality / IComparable