-
Notifications
You must be signed in to change notification settings - Fork 384
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inferred division from defined multiplication relations #1354
Conversation
b7456ea
to
a256eab
Compare
I rebased this on the latest changes in #1329 which makes it much cleaner. Example of an interesting situation that occurs here, we previously defined:
If we infer division from multiplication, one of those has to add or lose a kilo 😅 |
a256eab
to
df96c8e
Compare
Will review this next, hopefully in the next few days.
Haha 😆 But it will still work, right? |
Yes, it will work, except we're not sure why the original contributor chose a particular unit for an operator and if it matters, with precision for example. Also, this PR is more of an experiment than a serious proposal. There's some questions about the "magic" of it, and I took some liberties with implicit/explicit conversion between |
Although the Density and Mass Concentration share the same units, they are not interchangeable- they describe different types of ratios. Casting between them (either implicitly or explicitly) makes no sense.. |
df96c8e
to
0491bfb
Compare
Thanks for the explanation! That makes the concept of inferred division a bit harder, given that we currently have:
which leads to these inferred ambiguous operators, which is what I tried to solve with my casting shenanigans:
The latter is currently not defined, so skipping it would not be a breaking change. I've reworked this PR to do that and removed the casting stuff. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like where this is going and would like to get it merged.
Some comments so far.
@@ -630,11 +638,10 @@ public static bool TryParseUnit(string str, IFormatProvider? provider, out Recip | |||
return ReciprocalArea.FromInverseSquareMeters(left.InverseMeters * right.InverseMeters); | |||
} | |||
|
|||
/// <summary>Calculates the inverse of this quantity.</summary> | |||
/// <returns>The corresponding inverse quantity, <see cref="Length"/>.</returns> | |||
public Length Inverse() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you move Inverse()
back to its original position? It'll reduce the diff in PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this was just this one file, you can disregard this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not just this one, this happens in Area
, ReciprocalArea
, Length
and ReciprocalLength
.
But I think it's an improvement, Inverse
is now always the first operator in the #region because it's now defined as
1 = Area.SquareMeter * ReciprocalArea.InverseSquareMeter
instead of
ReciprocalArea.InverseSquareMeter = 1 / Area.SquareMeter
and sorted on that string alphanumerically.
.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" }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can solve this in a way that makes it more discoverable by moving this into the JSON file, how about this:
Option 1) Skip division if multiple competing definitions
If there more than one inferred division definition QuantityA / QuantityB
with different resulting quantity type, then skip all of them. They will have to be manually defined in the JSON file.
Option 2) Allow defining opt-out in the JSON file
We could move the array to a structure like this:
{
"skipInferredDivisionForDefinitions": [
"Mass.Kilogram = MassConcentration.KilogramPerCubicMeter * Volume.CubicMeter"
],
"definitions": [
"1 = Area.SquareMeter * ReciprocalArea.InverseSquareMeter",
"1 = ElectricResistivity.OhmMeter * ElectricConductivity.SiemensPerMeter",
"1 = Length.Meter * ReciprocalLength.InverseMeter",
"Acceleration.MeterPerSecondSquared = Jerk.MeterPerSecondCubed * Duration.Second",
"AmountOfSubstance.Kilomole = MolarFlow.KilomolePerSecond * Duration.Second",
"AmountOfSubstance.Mole = Molarity.MolePerCubicMeter * Volume.CubicMeter",
"Mass.Kilogram = MassConcentration.KilogramPerCubicMeter * Volume.CubicMeter"
]
}
Or add a structure for each definition, allowing us to configure stuff for each entry:
{
"definitions": [
{ "d": "1 = Area.SquareMeter * ReciprocalArea.InverseSquareMeter" },
{ "d": "1 = ElectricResistivity.OhmMeter * ElectricConductivity.SiemensPerMeter" },
{ "d": "1 = Length.Meter * ReciprocalLength.InverseMeter" },
{ "d": "Acceleration.MeterPerSecondSquared = Jerk.MeterPerSecondCubed * Duration.Second" },
{ "d": "AmountOfSubstance.Kilomole = MolarFlow.KilomolePerSecond * Duration.Second" },
{ "d": "AmountOfSubstance.Mole = Molarity.MolePerCubicMeter * Volume.CubicMeter" },
{ "d": "Mass.Kilogram = MassConcentration.KilogramPerCubicMeter * Volume.CubicMeter", "skipInferredDivision": true }
]
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like option 2.2, but I also like the simple json structure we have now, so I propose a string-based solution of just appending NoInferredDivision
to the relation, see last commits.
44bd139
to
1950b51
Compare
### Changes - Change `Duration` from explicit to implicit cast to/from `TimeSpan` - Remove operator overloads for `TimeSpan` now covered by implicit cast for all but left operands ### Background See #1354 (comment) One issue is that the operator overloads only work when `TimeSpan` is the right operand. I changed the code generation to take this into account, but another option would be to make a breaking change where we just don't support `TimeSpan` as the left operand at all. Then users would have to cast explicitly, or for multiplication just reverse the operands. This would affect 13 operators: ``` TimeSpan * Acceleration TimeSpan * ElectricCurrent TimeSpan * ElectricCurrentGradient TimeSpan * ForceChangeRate TimeSpan * KinematicViscosity TimeSpan * MassFlow TimeSpan * MolarFlow TimeSpan * Power TimeSpan * PressureChangeRate TimeSpan * RotationalSpeed TimeSpan * Speed TimeSpan * TemperatureChangeRate TimeSpan * VolumeFlow ``` Of which only 6 are used in tests so I assume are supported in v5: ``` TimeSpan * KinematicViscosity TimeSpan * MassFlow TimeSpan * Power TimeSpan * RotationalSpeed TimeSpan * TemperatureChangeRate TimeSpan * Speed ``` --------- Co-authored-by: Andreas Gullberg Larsen <[email protected]>
1950b51
to
6a5ad04
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, just one minor comment we can address later.
@@ -576,7 +590,7 @@ public static bool TryParseUnit(string str, IFormatProvider? provider, out Tempe | |||
/// <summary>Get <see cref="Length"/> from <see cref="TemperatureDelta"/> / <see cref="TemperatureGradient"/>.</summary> | |||
public static Length operator /(TemperatureDelta temperatureDelta, TemperatureGradient temperatureGradient) | |||
{ | |||
return Length.FromKilometers(temperatureDelta.Kelvins / temperatureGradient.DegreesCelsiusPerKilometer); | |||
return Length.FromKilometers(temperatureDelta.DegreesCelsius / temperatureGradient.DegreesCelsiusPerKilometer); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is from before, but isn't PerKilometer
an odd choice here?
I'd expect DegreesCelciusPerMeter
.
Same below.
Can fix in separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Muximize Just an extra ping to make sure you saw this:
This is from before, but isn't PerKilometer
an odd choice here?
I'd expect DegreesCelciusPerMeter
.
Same below.
Can fix in separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree it seems odd, but it was added this way in #991. I guess this is used in meteorology, where kilometers make more sense?
The Kelvin part is weird tho. Previously we had:
TemperatureDelta.DegreeCelsius = TemperatureGradient.DegreeCelsiusPerKilometer * Length.Kilometer
TemperatureGradient.KelvinPerMeter = TemperatureDelta.Kelvin / Length.Meter
Length.Kilometer = TemperatureDelta.Kelvin / TemperatureGradient.DegreeCelsiusPerKilometer
We dropped the last two because they now get inferred, but with Celsius instead of Kelvin. I guess we need a domain expert to answer both these concerns?
Edit: just checked TemperatureDelta.json, Kelvin
and DegreeCelsius
both have {x}
so they're basically aliases.
Division now inferred by f7ce00b - Inferred division from defined multiplication relations (angularsen#1354)
Nuget on the way |
In #1329 this proposal came up:
This PR is an experiment implementing this.
Breaking changes:
TimeSpan = Volume / VolumeFlow
=>Duration = Volume / VolumeFlow