@@ -25,13 +25,16 @@ internal static class QuantityRelationsParser
25
25
///
26
26
/// The format of a relation definition is "Quantity.Unit operator Quantity.Unit = Quantity.Unit" (See examples below).
27
27
/// "double" can be used as a unitless operand.
28
- /// "1" can be used as the left operand to define inverse relations.
28
+ /// "1" can be used as the result operand to define inverse relations.
29
+ ///
30
+ /// Division relations are inferred from multiplication relations,
31
+ /// but this can be skipped if the string ends with "NoInferredDivision".
29
32
/// </summary>
30
33
/// <example>
31
34
/// [
32
- /// "Power.Watt = ElectricPotential.Volt * ElectricCurrent.Ampere",
33
- /// "Speed.MeterPerSecond = Length.Meter / Duration.Second ",
34
- /// "ReciprocalLength.InverseMeter = 1 / Length.Meter"
35
+ /// "1 = Length.Meter * ReciprocalLength.InverseMeter"
36
+ /// "Power.Watt = ElectricPotential.Volt * ElectricCurrent.Ampere ",
37
+ /// "Mass.Kilogram = MassConcentration.KilogramPerCubicMeter * Volume.CubicMeter -- NoInferredDivision",
35
38
/// ]
36
39
/// </example>
37
40
/// <param name="rootDir">Repository root directory.</param>
@@ -61,7 +64,7 @@ public static void ParseAndApplyRelations(string rootDir, Quantity[] quantities)
61
64
62
65
// We can infer division relations from multiplication relations.
63
66
relations . AddRange ( relations
64
- . Where ( r => r . Operator is "*" )
67
+ . Where ( r => r is { Operator : "*" , NoInferredDivision : false } )
65
68
. Select ( r => r with
66
69
{
67
70
Operator = "/" ,
@@ -74,9 +77,6 @@ public static void ParseAndApplyRelations(string rootDir, Quantity[] quantities)
74
77
. Where ( r => r . LeftQuantity != r . RightQuantity )
75
78
. ToList ( ) ) ;
76
79
77
- // Remove inferred relation "MassConcentration = Mass / Volume" because it duplicates "Density = Mass / Volume"
78
- relations . RemoveAll ( r => r is { Operator : "/" , ResultQuantity . Name : "MassConcentration" , LeftQuantity . Name : "Mass" , RightQuantity . Name : "Volume" } ) ;
79
-
80
80
// We can infer TimeSpan relations from Duration relations.
81
81
var timeSpanQuantity = pseudoQuantity with { Name = "TimeSpan" } ;
82
82
relations . AddRange ( relations
@@ -103,6 +103,18 @@ public static void ParseAndApplyRelations(string rootDir, Quantity[] quantities)
103
103
throw new UnitsNetCodeGenException ( $ "Duplicate inferred relations:\n { list } ") ;
104
104
}
105
105
106
+ var ambiguous = relations
107
+ . GroupBy ( r => $ "{ r . LeftQuantity . Name } { r . Operator } { r . RightQuantity . Name } ")
108
+ . Where ( g => g . Count ( ) > 1 )
109
+ . Select ( g => g . Key )
110
+ . ToList ( ) ;
111
+
112
+ if ( ambiguous . Any ( ) )
113
+ {
114
+ var list = string . Join ( "\n " , ambiguous ) ;
115
+ throw new UnitsNetCodeGenException ( $ "Ambiguous inferred relations:\n { list } \n \n Hint: you could use NoInferredDivision in the definition file.") ;
116
+ }
117
+
106
118
foreach ( var quantity in quantities )
107
119
{
108
120
var quantityRelations = new List < QuantityRelation > ( ) ;
@@ -151,7 +163,7 @@ private static QuantityRelation ParseRelation(string relationString, IReadOnlyDi
151
163
{
152
164
var segments = relationString . Split ( ' ' ) ;
153
165
154
- if ( segments is not [ _, "=" , _, "*" , _] )
166
+ if ( segments is not [ _, "=" , _, "*" , _, .. ] )
155
167
{
156
168
throw new Exception ( $ "Invalid relation string: { relationString } ") ;
157
169
}
@@ -176,6 +188,7 @@ private static QuantityRelation ParseRelation(string relationString, IReadOnlyDi
176
188
177
189
return new QuantityRelation
178
190
{
191
+ NoInferredDivision = segments . Contains ( "NoInferredDivision" ) ,
179
192
Operator = @operator ,
180
193
LeftQuantity = leftQuantity ,
181
194
LeftUnit = leftUnit ,
0 commit comments