diff --git a/CodeGen/Generators/QuantityRelationsParser.cs b/CodeGen/Generators/QuantityRelationsParser.cs
index f7ce09fa37..94398df895 100644
--- a/CodeGen/Generators/QuantityRelationsParser.cs
+++ b/CodeGen/Generators/QuantityRelationsParser.cs
@@ -25,13 +25,16 @@ internal static class QuantityRelationsParser
///
/// 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.
- /// "1" can be used as the left operand to define inverse relations.
+ /// "1" can be used as the result operand to define inverse relations.
+ ///
+ /// Division relations are inferred from multiplication relations,
+ /// but this can be skipped if the string ends with "NoInferredDivision".
///
///
/// [
- /// "Power.Watt = ElectricPotential.Volt * ElectricCurrent.Ampere",
- /// "Speed.MeterPerSecond = Length.Meter / Duration.Second",
- /// "ReciprocalLength.InverseMeter = 1 / Length.Meter"
+ /// "1 = Length.Meter * ReciprocalLength.InverseMeter"
+ /// "Power.Watt = ElectricPotential.Volt * ElectricCurrent.Ampere",
+ /// "Mass.Kilogram = MassConcentration.KilogramPerCubicMeter * Volume.CubicMeter -- NoInferredDivision",
/// ]
///
/// Repository root directory.
@@ -61,7 +64,7 @@ public static void ParseAndApplyRelations(string rootDir, Quantity[] quantities)
// We can infer division relations from multiplication relations.
relations.AddRange(relations
- .Where(r => r.Operator is "*")
+ .Where(r => r is { Operator: "*", NoInferredDivision: false })
.Select(r => r with
{
Operator = "/",
@@ -74,9 +77,6 @@ public static void ParseAndApplyRelations(string rootDir, Quantity[] quantities)
.Where(r => r.LeftQuantity != r.RightQuantity)
.ToList());
- // Remove inferred relation "MassConcentration = Mass / Volume" because it duplicates "Density = Mass / Volume"
- relations.RemoveAll(r => r is { Operator: "/", ResultQuantity.Name: "MassConcentration", LeftQuantity.Name: "Mass", RightQuantity.Name: "Volume" });
-
// We can infer TimeSpan relations from Duration relations.
var timeSpanQuantity = pseudoQuantity with { Name = "TimeSpan" };
relations.AddRange(relations
@@ -103,6 +103,18 @@ public static void ParseAndApplyRelations(string rootDir, Quantity[] quantities)
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)
+ .Select(g => g.Key)
+ .ToList();
+
+ if (ambiguous.Any())
+ {
+ var list = string.Join("\n ", ambiguous);
+ throw new UnitsNetCodeGenException($"Ambiguous inferred relations:\n {list}\n\nHint: you could use NoInferredDivision in the definition file.");
+ }
+
foreach (var quantity in quantities)
{
var quantityRelations = new List();
@@ -151,7 +163,7 @@ private static QuantityRelation ParseRelation(string relationString, IReadOnlyDi
{
var segments = relationString.Split(' ');
- if (segments is not [_, "=", _, "*", _])
+ if (segments is not [_, "=", _, "*", _, ..])
{
throw new Exception($"Invalid relation string: {relationString}");
}
@@ -176,6 +188,7 @@ private static QuantityRelation ParseRelation(string relationString, IReadOnlyDi
return new QuantityRelation
{
+ NoInferredDivision = segments.Contains("NoInferredDivision"),
Operator = @operator,
LeftQuantity = leftQuantity,
LeftUnit = leftUnit,
diff --git a/CodeGen/JsonTypes/QuantityRelation.cs b/CodeGen/JsonTypes/QuantityRelation.cs
index 9aec29c2a9..35e97a6bc0 100644
--- a/CodeGen/JsonTypes/QuantityRelation.cs
+++ b/CodeGen/JsonTypes/QuantityRelation.cs
@@ -7,6 +7,7 @@ namespace CodeGen.JsonTypes
{
internal record QuantityRelation : IComparable
{
+ public bool NoInferredDivision = false;
public string Operator = null!;
public Quantity LeftQuantity = null!;
diff --git a/Common/UnitRelations.json b/Common/UnitRelations.json
index 519ea946d4..b6e6cc2ad3 100644
--- a/Common/UnitRelations.json
+++ b/Common/UnitRelations.json
@@ -37,7 +37,7 @@
"Mass.Kilogram = AreaDensity.KilogramPerSquareMeter * Area.SquareMeter",
"Mass.Kilogram = Density.KilogramPerCubicMeter * Volume.CubicMeter",
"Mass.Kilogram = LinearDensity.KilogramPerMeter * Length.Meter",
- "Mass.Kilogram = MassConcentration.KilogramPerCubicMeter * Volume.CubicMeter",
+ "Mass.Kilogram = MassConcentration.KilogramPerCubicMeter * Volume.CubicMeter -- NoInferredDivision",
"Mass.Kilogram = MassFlow.KilogramPerSecond * Duration.Second",
"Mass.Kilogram = MassFraction.DecimalFraction * Mass.Kilogram",
"MassConcentration.KilogramPerCubicMeter = Molarity.MolePerCubicMeter * MolarMass.KilogramPerMole",