Skip to content

Temperature times a double #518

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

Closed
Mobz87 opened this issue Oct 14, 2018 · 30 comments
Closed

Temperature times a double #518

Mobz87 opened this issue Oct 14, 2018 · 30 comments
Assignees

Comments

@Mobz87
Copy link

Mobz87 commented Oct 14, 2018

Is there a specific reason that I cant do:

Temperature * double

even though I can do it with every other unit?

@angularsen
Copy link
Owner

angularsen commented Oct 14, 2018

Yes, Temperature is tricky, because 0 Celsius != 0 Kelvin.
We added TemperatureDelta for this very case, so you should instead do
Temperature t = Temperature.FromDegreesCelsius(20) * TemperatureDelta.FromDegreesCelsius(2) // 40 degrees C

Edit: Hang on, that's not right either.

@angularsen
Copy link
Owner

Ah, found it, you can do

Temperature t = Temperature.FromDegreesCelsius(20);
Temperature t2 = t.Multiply(2, TemperatureUnit.DegreeCelsius);
Console.WriteLine(t2); // 40 C

@angularsen
Copy link
Owner

angularsen commented Oct 14, 2018

In either case, multiplication with temperatures is a bit tricky if you don't know the unit you are working with. Thinking in terms of "energy", one would think doubling the Kelvins would be right, but if you are thinking in Celsius (20) and doubling, you expect 40. The reference temperature (zero) is the missing information.

@Mobz87
Copy link
Author

Mobz87 commented Oct 15, 2018

It make sense why you have done it that way however I don't think it is that tricky.
As a physicist you always do all you calculations in SI-unit and as long as you keep to that rule you don't have to think about it.

Keeping it in Kelvin you don't have to think about if it is a absolute- or a relative temperature (what you call KelvinDelta [which I can see that you have removed] ).

The way I see it:
You shouldn't use 'Delta' in any of you units.
If you are doing any kind of calculation ( - + * / ) you always go back to the SI-unit and do the calculation. No need for special rules or specifying beforehand rather it is a absolute- or a relative.

So if I have do:

Temperature * double
20°C * 2

It should return:
313°C

and not 40°C!

An ex.

You want to calculate power:
Power = Massflow * specific_heat_capacity * (temperature2 - temperature1)

Lets say:
temperature2 = 30°C
temperature1 = 20°C

If you say that:
Power = Massflow * specific_heat_capacity * (30°C - 20°C)
is equal to:
Power = Massflow * specific_heat_capacity * (10°C)
then you have failed you exam!

If you say that:
Power = Massflow * specific_heat_capacity * (303K - 293K)
is equal to:
Power = Massflow * specific_heat_capacity * (10K)
then you still have a change to pass the exam :-D

Does it make sense?

@angularsen
Copy link
Owner

angularsen commented Oct 15, 2018

It absolutely makes sense and this was in fact the original implementation until this issue came up and the resulting PR:
#218
#219

I have myself stumbled on this and I know others have too. It's an easy mistake to make to try to double a temperature in degrees Celsius or Fahrenheit and expect the resulting value to also double, especially if you are not regularly working with temperatures.

I think this quote puts it well

What is often confusing about the Celsius measurement is that it follows an interval system but not a ratio system; that it follows a relative scale not an absolute scale. This is put simply by illustrating that while 10 °C and 20 °C have the same interval difference as 20 °C and 30 °C the temperature 20 °C is not twice the air heat energy as 10 °C. As this example shows, degrees Celsius is a useful interval measurement but does not possess the characteristics of ratio measures like weight or distance.

So. I see two ways foward:

  1. Add back support * and / using Kelvins, add some extra xmldoc to the operator overloads and help consumers learn from their mistakes when they report issues on github.
  2. Only provide Multiply(value, unit) and Divide(value, unit) methods, as we already have, and point people to use those instead. The downside is that this is less natural to use in complex arithmetic expressions and maybe less discoverable.

What do you think?

@Mobz87
Copy link
Author

Mobz87 commented Oct 15, 2018

Thanks for asking for my opinion.

I think you should add support for + - * / where the calculation is done in kelvin and then inform the programmers that 1°C + 1°C != 2°C and inform about the difference between absolute- or a relative temperature. And that a relative temperature (Delta temperature) doesn't really make sense to have as anything else than Kelvin.
In the world of physicists and engineers you get hanged for expressing a Delta temperature in anything else than Kelvin.

This could also clean up a lot of the confusion about temperatures

An example of the confusion:

Entropy test = Entropy.FromjoulesPerDegreeCelsius(10);
Entropy test1 = Entropy.FromjoulesPerKelvin(10);

This is where you get hanged ;-)
It is not "not-correct" but really really confusing..

If you really want the DegreeCelsius you should say:
Entropy test = Entropy.FromjoulesPerDELTADegreeCelsius(10);

but then we are back to
Entropy test = Entropy.FromjoulesPerKelvin(10);

Another example of the confusion:

Temperature test = Temperature.FromDegreesCelsius(10);
Temperature test1 = Temperature.FromDegreesCelsius(20);

(test1- test).Kelvins.ToString() ;//return 10, which is correct
(test1- test).DegreesCelsius.ToString(); //return 10, but should return -263°C

To sum up:

If you handle temperature like any other unit by doing all the calculation in the SI-unit (Allowing + - * /), everything would be correct and your 'only' problem would be to teach your users the correct way to handle temperature

@angularsen
Copy link
Owner

Thanks, I value your input. You clearly have a solid grasp on this domain and I agree with almost all you said.

This one I find confusing though:

(test1- test).DegreesCelsius.ToString(); //return 10, but should return -263°C

In the current implementation, Temperature - Temperature returns TemperatureDelta. In my mind at least, that is intuitive as, similar to DateTime - DateTime you get TimeSpan. The DateTime analog works, for me at least, since it too has no zero reference date and you only have relative time scales for different calendars.

So, I would expect 10 in your example above, a difference of 10 degrees Celsius the same way you also have a difference of 10 degrees Kelvin.

Hope you can enlighten me on this part, thanks!

@tmilnthorp
Copy link
Collaborator

If you look at the Boost units documentation, they do something similar. Here's the output:

    quantity<absolute<fahrenheit::temperature> >    T1p(
        32.0*absolute<fahrenheit::temperature>());
    quantity<fahrenheit::temperature>               T1v(
        32.0*fahrenheit::degrees);
    
    quantity<absolute<si::temperature> >            T2p(T1p);
    quantity<si::temperature>                       T2v(T1v);
    //]

    typedef conversion_helper<
        quantity<absolute<fahrenheit::temperature> >,
        quantity<absolute<si::temperature> > >          absolute_conv_type;
    typedef conversion_helper<
        quantity<fahrenheit::temperature>,
        quantity<si::temperature> >                     relative_conv_type;
    
    std::cout << T1p << std::endl
              << absolute_conv_type::convert(T1p) << std::endl
              << T2p << std::endl
              << T1v << std::endl
              << relative_conv_type::convert(T1v) << std::endl
              << T2v << std::endl
              << std::endl;

    return 0;

The output is:

{ 32 } F
{ 273.15 } K
{ 273.15 } K
[ 32 ] F
[ 17.7778 ] K
[ 17.7778 ] K

@Mobz87
Copy link
Author

Mobz87 commented Oct 15, 2018

Looking at the Unit system you don't see any Delta Units - Try to find me a SI Delta Unit :-)
Since you are trying to replicate the unit system I believe introducing Delta-Units are wrong and are creating unnecessary confusion both for you and for your users and it requires path-work that deviates from the original system.

In the physicists/engineers world this is how it is done:
30°C - 20°C = 10K same as 303K - 293K = 10K

and if you for some reason want to express 10k in °C you get -263°C
303K - 293K = -263°C
Everything above it perfectly fine, yes the -263°C is confusing but nonetheless holds true.
This is why you normally don't express a "delta temperature" in °C

You can not say 30°C - 20°C = 10Delta°C because Delta°C is not defined in the unit system!

If you take MassFlow - MassFlow you get... MassFlow not DeltaMassFlow
If you take Density - Density you get.. Density not DeltaDensity
If you tage Temperature - Temperature you get Temperature not DeltaTemperature
If you tage MassFlow * MassFlow you get MassFlow^2
If you tage MassFlow/MassFlow you get [ - ]

This holds true for every single Unit in the unitsystem!
Thou not saying you can do all the summation 1 + 1 = 2 (!)
And this is what (I believe) is confusing you - most temperatures dont have concentric zero points.

Most units have concentric zero points [ 0Meter = 0cm =0Feet.... ]
If you would express the difference between Meter and feet in y = ax + b you get y = 3.3x + 0
Where y is meter and x is feet and in this case b = 0. Here everything is easy.

With kelvin and DegreesCelsius:
y = 1x + 273.15 where y is kelvin and x is DegreesCelsius

'with kelvin and Fahrenheit
y = 0.55x + 459,67 [Is something like this, cant really remember it correctly but you get the point]

Even your DateTime - DateTime is DateTime!
because DateTime = seconds since 1900

(Seconds between 2010 and 1900) - (Seconds between 2000 and 1900) = (Seconds between 1910 and 1900)
but lets keep Time out of the equation for now :-)

@Mobz87
Copy link
Author

Mobz87 commented Oct 15, 2018

A quick summarizing of my points:

  • Remove anything with the name of Delta
  • Every calculation done in SI-unit

Results in

  • No special cases for any unit! Every single unit is treated the same.
  • A true copy of the real unitsystem.

@angularsen
Copy link
Owner

angularsen commented Oct 15, 2018

@tmilnthorp

If you look at the Boost units documentation, they do something similar. Here's the output:

Oh gosh I forgot how badly I dislike C++ syntax, thanks for reminding me 😆
So they distinguish multiplication of "SI temperature" in Kelvin scale and multiplication with a "Fahrenheit temperature" in Fahrenheit scale? Seems similar to our Multiply(TemperatureUnit unit) methods.

@Mobz87 From a programmer's perspective in science or engineering, this sounds reasonable. From a programmer's perspective working on a weather app and want to find the difference between the warmest point of day and the coldest and express that in Celsius or Fahrenheit - I'm just imagining up a scenario here - this probably gets confusing real quick.

Even your DateTime - DateTime is DateTime!

No, it's TimeSpan in .NET. It is a period of time between two dates and the analog breaks down when you try to construct a DateTime as the result of the seconds between two dates, that date does not make much sense in how we work with dates and time, the same way subtracting two temperatures to produce a new temperature might seem weird depending on what unit/scale you expect.

I think this is an important point when Kelvin is perhaps a foreign concept and you are simply thinking in Fahrenheit and Celsius, because that's what the imagined app does.
Please consider this:

Scenario: Calculate the degrees of Fahrenheit between warmest and coldest for a day

I speculate most developers would reach for this:

var delta = Temperature.FromDegreesFahrenheit(100) - Temperature.FromDegreesFahrenheit(70);
var deltaF = delta.DegreesFahrenheit;
// Alt1: 100F - 70F = 30F = 30F
// Alt2: 311K - 294K = 17K = -429F
// Alt3: 100F - 70F = TemperatureDelta = 30F

Now, what is reasonable to expect delta to be here?
The scientist would say 17 Kelvins. Obviously! With flames on it.
The majority of programmers, I speculate, would say 30 Fahrenheit.
The layman, non-programmer, non-scientist, my wife, I'm pretty darn sure would say 30 Fahrenheit.

This is an indication of what is intuitive. It's important.

If calculations follow alternative 2 as you propose, this is how I would obtain 30 Fahrenheit:

var delta = Temperature.FromDegreesFahrenheit(100) - Temperature.FromDegreesFahrenheit(70);
var deltaF = (Temperature.FromDegreesFahrenheit(0) + delta).DegreesFahrenheit; // 30

Please correct me if there is an easier way to calculate this. This approach seems very counter-intuitive to me at least. I mean, I understand it, but it is not how I would expect to have to code it.

TL;DR
I claim arithmetic with temperatures is confusing for the average programmer Joe, including myself, and I believe TemperatureDelta adds value to help with this. In particular, calculating the delta temperature of a warm day and express that in Celsius or Fahrenheit seems particularly counter-intuitive to me without the help of TemperatureDelta. Do you see a way to make this easier while keeping the benefits of standardized arithmetic?

I'm not arguing to be difficult and am trying to keep an open mind here, I truly just want to make sure we keep the library intuitive for all our users - not only scientists and engineers. I look forward to your reply :-)

@Mobz87
Copy link
Author

Mobz87 commented Oct 15, 2018

Well averaging on temperatures still work:

Temp1 = 10°C
Temp1 = 20°C
(10°C + 20°C) / 2 = 15°C

because

(293k + 303k)/2 = 298k
298k = 15°C

The only things that doesn't work is:

2°C + 2°C != 4°C (This many programmer will run into)
2°C - 2°C != 0°C (This many programmer will run into)
2°C / 2°C != 0°C (Programmer will never do this)
2°C * 2°C != 4°C (Programmer will never do this)

I see that you are caught between doing what is correct (in my world) or whats correct in the minds of most programmers that doesn't study engineering.

I still think doing whats correct is the right think to do here. The only thing you need to teach your programmer is:

If you want to do this
2°C + 2°C = 4°C do instead this 2°C + 2K = 4°C
With this simple information you can create a correct unit system that can be used by both engineers and programmers.

@Mobz87
Copy link
Author

Mobz87 commented Oct 16, 2018

I would compare doing
2°C + 2°C and expecting 4°C
as saying
Monday + Tuesday = Wednesday

but saying:
Monday + 48hours = Wednesday makes much more sense
just like
2°C + 2K = 4°C

I hope you understand this analogy.

If you for your v4.0.0 are up for creating a unit system that are more physicist/engineer friendly I would be happy to help both with guidance and programming. I really like your project(!) but as you might be able to tell, it is important to me that the unit calculation is done correctly :-)

@angularsen
Copy link
Owner

angularsen commented Oct 16, 2018

2°C / 2°C != 1°C (Programmer will never do this)

Might do that to find ratio. Today was 50% warmer than yesterday.
I can't think of a scenario for multiplying temperatures though.

Monday + 48hours = Wednesday makes much more sense

I agree, but I also read hours as Kelvins and TimeSpan as TemperatureDelta in this analog - so I do think that type has some merit.

2°C + 2K = 4°C

Is this the correct form or should delta symbol be used here? 2°C + 2Δ°K = 4°C

Not to beat a dead horse, but conversions of temperature difference/delta is a thing: wolframalpha.com, nettam.com, schneider-electric.com

How about this:

  1. * / for Temperature and double, using Kelvins
  2. + - * / (??) for Temperature and Temperature, using Kelvins
  3. + - * / of Temperature and TemperatureDelta
  4. Temperature.Add/Multiply/Divide() methods with double and TemperatureUnit (similar to TemperatureDelta, but maybe helps with discoverability?)

Something like this:

var fahr100 = Temperature.FromDegreesFahrenheit(100);
var fahr50 = Temperature.FromDegreesFahrenheit(50);
var deltaFahr2 = TemperatureDelta.FromDegreesFahrenheit(2); // 2 Δ°F or 1.1 Δ°K

// Temperature and double
Temperature fahr660 = fahr100 * 2.0; // Add
Temperature fahrMinus180 = fahr100 / 2.0; // Add

// Temperature and Temperature
Temperature fahr173k = fahr100 * fahr100; // Weird when not using Kelvins, but is correct
Temperature kelvin1 = fahr100 / fahr100; // Weird when not using Kelvins, but is correct
Temperature fahr660 = fahr100 + fahr100; // Weird when not using Kelvins, but is correct
Temperature fahr0 = fahr100 - fahr100; // Weird when not using Kelvins, but is correct

// Temperature and TemperatureDelta
Temperature fahr200 = fahr100 * deltaFahr2; // Add
Temperature fahr50 = fahr100 / deltaFahr2; // Add
Temperature fahr102 = fahr100 + deltaFahr2; // Keep
Temperature fahr98 = fahr100 - deltaFahr2; // Keep

// Not sure to add/keep or remove these? Might help with discoverability.
Temperature fahr98 = fahr100.Add(-2, TemperatureUnit.Fahrenheit); // Add/remove
Temperature fahr200 = fahr100.Multiply(2, TemperatureUnit.Fahrenheit); // Keep/remove
Temperature fahr50 = fahr100.Divide(2, TemperatureUnit.Fahrenheit); // Keep/remove

// Remove these, weird order. It's like TimeSpan+DateTime.
Temperature fahr120 = deltaFahr20 + fahr100; 
Temperature fahr80 = deltaFahr20 - fahr100;

// Other usages
double ratio = fahr100.DegreesFahrenheit / fahr50.DegreesFahrenheit; // 2

@Mobz87
Copy link
Author

Mobz87 commented Oct 17, 2018

You are right doing a Today was 50% warmer than yesterday. will not give you the expected result when doing the calculation in kelvin.
But Today was 50% warmer than yesterday. is still a funny thing to say because

For people using celsius:
Today was 50% warmer than yesterday.

For people using Fahrenheit:
Today was 30% warmer than yesterday.

For people using Kelvin:
Today was 10% warmer than yesterday.

And if you still want to do this calculation, you can just turn your celsius into a doubles and do the math.

Is this the correct form or should delta symbol be used here? 2°C + 2Δ°K = 4°C

Not to beat a dead horse, but conversions of temperature difference/delta is a thing: wolframalpha.com, nettam.com, schneider-electric.com

delta Δ is absolutely a thing! but it is not a unit and when you implement something that is not a unit into a unit, that's where things get confusing (and wrong in my world)

When you look at a unit (°F, °K , °C) you can't tell if it a absolute or relative (delta) by looking at the unit.

@Mobz87
Copy link
Author

Mobz87 commented Oct 17, 2018

Something like this:

var fahr100 = Temperature.FromDegreesFahrenheit(100);
var fahr50 = Temperature.FromDegreesFahrenheit(50);
var deltaFahr2 = TemperatureDelta.FromDegreesFahrenheit(2); // 2 Δ°F or 1.1 Δ°K

// Temperature and double
Temperature fahr660 = fahr100 * 2.0; // Add
Temperature fahrMinus180 = fahr100 / 2.0; // Add
Great! Mobz Comment

// Temperature and Temperature
Temperature fahr173k = fahr100 * fahr100; // Weird when not using Kelvins, but is correct
Correct! Only thing is that you get a new unit [°F] * [°F] = [°F^2] So it is not really a temperature anymore`

Temperature kelvin1 = fahr100 / fahr100; // Weird when not using Kelvins, but is correct
Should return a Ratio not a temperature [°F] / [°F] = [-]

Temperature fahr660 = fahr100 + fahr100; // Weird when not using Kelvins, but is correct
Temperature fahr0 = fahr100 - fahr100; // Weird when not using Kelvins, but is correct
Correct! Mobz

// Temperature and TemperatureDelta
Temperature fahr200 = fahr100 * deltaFahr2; // Add
Temperature fahr50 = fahr100 / deltaFahr2; // Add
Temperature fahr102 = fahr100 + deltaFahr2; // Keep
Temperature fahr98 = fahr100 - deltaFahr2; // Keep

I think it is perfectly fine to have a Delta-function so this is possible
Temperature fahr98 = fahr100 - deltaFahr2; // Keep 

// Not sure to add/keep or remove these? Might help with discoverability.
Temperature fahr98 = fahr100.Add(-2, TemperatureUnit.Fahrenheit); // Add/remove
Temperature fahr200 = fahr100.Multiply(2, TemperatureUnit.Fahrenheit); // Keep/remove
Temperature fahr50 = fahr100.Divide(2, TemperatureUnit.Fahrenheit); // Keep/remove
I would say Remove. When you can use all normal operators (+-*/) if have these?

// Remove these, weird order. It's like TimeSpan+DateTime.
Temperature fahr120 = deltaFahr20 + fahr100;
Temperature fahr80 = deltaFahr20 - fahr100;
I think this is fine except that it should be like this:
Temperature fahr80 (-80F) = deltaFahr20 (20 Delta F) - fahr100 (-100F);

// Other usages
double ratio = fahr100.DegreesFahrenheit / fahr50.DegreesFahrenheit; // 2
Perfect

@Mobz87
Copy link
Author

Mobz87 commented Oct 17, 2018

I think having the possibility to this is great and helps many people:

Temperature = Temperature + DeltaFahrenheit(10);
ex:
Temperature fahr = fahr - deltaFahr(2)

I don't hate Delta Δ :-D

What I do not like is when I try to do this:

Temperature test1;
Temperature test2;
Temperature test3;

test3 = test2 - test1;

I get: Cannot implicitly convert type 'UnitsNet.TemperatureDelta' to 'UnitsNet.Temperature'

How it is now:
[Δ°F] = [°F] - [°F]

how it should be;
[°F] = [°F] - [°F]

Now you have to define if your temperature is Delta or not - Delta has nothing to do with the Unit. Delta is something you use to help people understand your equation.

ex. If you want to calculate the heating capacity from a radiator:
Power = Cp * Massflow * (Temperature_in - Temperature_Out)

1247 [W] = 4157 [J/(kg*K)] * 0.01 [Kg/s] * (50 [°C] - 20 [°C] )

Here i could shorten my equation to

1247[W] = 4157 [J/(kg*K)] * 0.01 [Kg/s] * (30[°K] ) and I could (If i felt like it) inform that the temperature is a Delta temperature

If I did it this way (wrong way):
[W] = 4157 [J/(kg*K)] * 0.01 [Kg/s] * (30 [°C] )
[W] = 4157 [J/(kg*K)] * 0.01 [Kg/s] * (303 [°K] )
And my result would be 12595W instead of 1247W

@angularsen
Copy link
Owner

angularsen commented Oct 17, 2018

// Temperature and Temperature
Temperature fahr173k = fahr100 * fahr100; // Weird when not using Kelvins, but is correct
Correct! Only thing is that you get a new unit[°F] * [°F] = [°F^2]So it is not really a temperature anymore`

Ah, right. Good point about ensuring correct dimensions in arithmetic, I forgot. So.. if it's not Temperature, I don't think we should allow it since we don't have a quantity type to represent it - and we don't yet have a way to do arithmetic with dynamic quantity types. They can always multiply with t1.Kelvins * .t2.Kelvins. Remove?

Temperature kelvin1 = fahr100 / fahr100; // Weird when not using Kelvins, but is correct
Should return a Ratio not a temperature [°F] / [°F] = [-]

Agreed, return Ratio quantity here (not double or Temperature).

I think it is perfectly fine to have a Delta-function so this is possible
Temperature fahr98 = fahr100 - deltaFahr2; // Keep

Great, agreed.

// Not sure to add/keep or remove these? Might help with discoverability.
Temperature fahr98 = fahr100.Add(-2, TemperatureUnit.Fahrenheit); // Add/remove
Temperature fahr200 = fahr100.Multiply(2, TemperatureUnit.Fahrenheit); // Keep/remove
Temperature fahr50 = fahr100.Divide(2, TemperatureUnit.Fahrenheit); // Keep/remove
I would say Remove. When you can use all normal operators (+-*/) if have these?

I kind of agree, but DateTime.Now.Add() exists even if it supports DateTime + TimeSpan. That is my only argument for keeping, it's something .NET devs will recognize when browsing intellisense on Temperature instances and hopefully they discover that hey there is this concept called delta here and then they maybe discover TemperatureDelta and they ask why does that exist, why can't I just subtract two temperatures and then maaaybe they stumble onto the concept behind this whole discussion.
On the other hand, intellisense today shows operators and their argument types pretty well so that point might be moot. I can be open to remove it and rather add back if it makes sense later. We have a breaking change window now.

// Remove these, weird order. It's like TimeSpan+DateTime.
Temperature fahr120 = deltaFahr20 + fahr100;
Temperature fahr80 = deltaFahr20 - fahr100;
I think this is fine except that it should be like this:
Temperature fahr80 (-80F) = deltaFahr20 (20 Delta F) - fahr100 (-100F);

Ok, on second thought, I agree. It's convenient to suppert both left and right hand, although DateTime does not allow TimeSpan + DateTime. I don't see a problem keeping it and it might help resolve complex arithmetic expressions.

test3 = test2 - test1;
I get: Cannot implicitly convert type 'UnitsNet.TemperatureDelta' to 'UnitsNet.Temperature'

I can see how that is confusing in that context. This will be confusing for either "Kelvin-people" or "Celsius/Fahrenheit-people", and I buy into the point about consistent arithmetic across quantities at the expense of confusing the latter group of people. We just have to provide the tools to easily solve typical Celsius/Fahrenheit scenarios too, andTemperatureDelta arithmetic does that.

So.

TL;DR Agree on all points, but I suggest removing Temperature * Temperature since we can't represent that quantity in a meaningful way (yet).

@tmilnthorp ? @gojanpaolo

Update: Sorry, did not mean to ping you guys. But if you still drop by any feedback on this topic is helpful.

@gojanpaolo
Copy link
Contributor

My initial vote is to keep TemperatureDelta. This is because the power equation is written as Q = mc∆T where ∆T = T2 - T1. ∆T is TemperatureDelta and should be expressed to the user.

P.S. I might be missing a few points to consider. I'll revisit this issue later after work. :)

@gojanpaolo
Copy link
Contributor

test3 = test2 - test1;
I get: Cannot implicitly convert type 'UnitsNet.TemperatureDelta' to 'UnitsNet.Temperature'

I think keeping it this way is fine. We can add a function for convenience like so..

//usage
var test1 = Temperature.FromDegreesCelsius(20);
var test2 = Temperature.FromDegreesCelsius(50);

Temperature test3 = (test2 - test1).ToTemperature();

Console.WriteLine(test3.Kelvins); //30

//implementation
public static class TemperatureDeltaExtensions
{
    public static Temperature ToTemperature(this TemperatureDelta temperatureDelta)
    {
        return new Temperature(temperatureDelta.Kelvins, TemperatureUnit.Kelvin);
    }
}

@angularsen
Copy link
Owner

I'm convinced by @Mobz87 on this, that we shouldn't introduce a special case for arithmetic with temperatures. It should be calculated with base units, as all other quantities also are - it just happens that delta vs absolute measurment doesn't matter for most other quantities since they share zero point for all their units.

However, we should provide convenience for working with temperature differences, since it does add some head scratching.

I'm actually thinking of reversing your example @gojanpaolo and do this instead:

var t1 = Temperature.FromDegreesCelsius(20);
var t2 = Temperature.FromDegreesCelsius(50);

Temperature t3 = t2 - t1;  // 323 K - 293 K = difference of 30 degrees Kelvin

// How do I express this difference in Fahrenheit you say? This way =>
TemperatureDelta delta = t3.ToTemperatureDelta();
double deltaC = delta.DegreesCelsius;  // 30
double deltaF = delta.DegreesFahrenheit; //  54
double deltaK = delta.DegreesKelvin; // 30

// ...or simply like this, whatever rocks your boat.
double deltaF = t2.DegreesFahrenheit - t1.DegreesFahenheit;

In addition to supporting Temperature + TemperatureDelta and other arithmetic using deltas, I think we should make common usecases for working with temperatures in a given unit easier or more intuitive. Hopefully!

Do you see what I mean?

@gojanpaolo
Copy link
Contributor

I see.. that works too :)

@Mobz87
Copy link
Author

Mobz87 commented Oct 17, 2018

ensuring correct dimensions in arithmetic
we shouldn't introduce a special case for arithmetic with temperatures

That sounds perfect!

I would love if we had a dynamic way of calculating dimensions!

private Temperature temp1;
private Temperature temp2;

temp2 = (temp1*temp1)/temp1; //Gives an error and it shouldn't

[°F] = ([°F] * [°F]) / [°F]  --> 
[°F] = [°F^2]/[°F] -->
[°F] = [°F]

Maybe [°F^2] physically doesn't make sense but as a temporary unit inside an equation, you see it every often.
It sets a limit on how advanced the equation can be.

@angularsen
Copy link
Owner

It's doable to some extent I think, when we move from struct to class we can introduce a generic quantity base type and that type can hold operator overloads between any quantity (I hope) and manipulate the BaseDimensions property to get intermediate quantity types in complex expressions. However, I don't see how the compiler can figure out that the end result becomes Temperature without casting it, which is a big bummer.

@Mobz87
Copy link
Author

Mobz87 commented Oct 18, 2018

I don't have a working solution in mind either .
I know how others have created unitsystems i C#:

They simply have a class Quantity which holds (Double,String) [Double represent value, String represent Unit]

Quantity Temp1 = new Quantity(30, "[°C]")
Quantity Temp2 = new Quantity(10, "[°C]")

return Temp1 * Temp2 //Gives Quantity(85564, "[°C^2]")
  • All calculation is done in IS-unit and returned as the asked_for unit
  • Every units is treated the same
    This way you can define any unit and do any kind of math on them

But I really really like your system for the way you're giving each unit a unique Class name like:

MassFlow
Temperature
Pressure

My hope is that with your system we could in the future get both!

@angularsen
Copy link
Owner

angularsen commented Oct 18, 2018

We can do something similar, but it would return a generic quantity just like you wrote it.

The only thing I can think of right now for resolving strongly typed arithmetic expressions is creating quantities for all base dimension combinations for 0-3 dimensions. With 7 base units, that's 4^7 => 16384 quantities. Then we would need arithmetic operator overloads between all combinations of quantities. Not even going to bother calculating that one. Suffice to say, this approach doesn't scale :-P

@Mobz87
Copy link
Author

Mobz87 commented Oct 20, 2018

I would nice to have something that does scale :-D

Btw if you still struggle with understanding/believing the 30[°C] - 20[°C] = -263[°C]
I can recommend downloading MathCad and try to do that equation in it.

MathCad is a Unit calculator - highly trusted and tested, used by engineers and physicists allover the world for many years. For our next unit discussion we could go by the rule: when in doubt ask MathCad

@angularsen
Copy link
Owner

Good suggestion.
I understand it, but I also think the expected result will vary depending on context.

I thought it would be interesting to type in 30[°C] - 20[°C] into Wolfram Alpha, which I also consider fairly representative for engineering and science users' expectations.

Interestingly, it outputs 10 degrees Celsius as the result :-)
https://www.wolframalpha.com/input/?i=30%5B%C2%B0C%5D+-+20%5B%C2%B0C%5D

I honestly think there is no one right answer here. It depends on context and will be confusing to some no matter what we choose as our default. I still agree that we should use Kelvins as it is a consistent rule we can apply to all our quantities, whereas using deltas for some will maybe add more confusion.

But we need to conclude this thread to not go in circle. I propose for v4 release to change temperature arithmetic as explained in #518 (comment) and other comments above.

@angularsen angularsen mentioned this issue Oct 21, 2018
25 tasks
@gojanpaolo
Copy link
Contributor

@angularsen angularsen self-assigned this Nov 3, 2018
@angularsen angularsen mentioned this issue Nov 22, 2018
angularsen added a commit that referenced this issue Dec 16, 2018
# 4.0.0 Release
This PR will serve as the list of items to complete and it will be updated to show the progress before finally merged into `master` when completed. We all have busy schedules, so **if you want to help move this work forward then that is much appreciated!** 

## The main theme is to reduce binary size
In two years it has grown from 280 kB to 1.4 MB and a lot of it is due to unnecessary syntactic sugar with many method overloads for various number types and nullable types - for every of our 800+ units! It simply adds up to a big total.

These items are chosen from #180, trying to include as many of the low hanging fruits as possible, while still keeping the list short and realistic to complete in a reasonably short time - we are all busy in our daily lives. We can always have more major version bumps later than trying to perfect it all now.

## Feature complete before November, merged before mid-December
I would like to aim for a "beta" pre-release nuget sometime in October with all the items completed, so we can have some time to test it before releasing the final, stable 4.0.0 version before Christmas holidays.
We should have the work items list more or less final before Monday, October 8th.

## Changes
#### Added
- [x] UnitSystem with a different meaning, now defines the base units for a unit system (#524)

#### Removed
- [x] Replace netstandard1.0 target with netstandard2.0, to avoid the extra dependencies (#477)
- [x] Remove code marked as `[Obsolete]`, such as `VolumeUnit.Teaspoon`  (#490)
- [x] Remove nullable `From` factory methods (#483)
- [x] Remove extension methods on _nullable_ number types (#483)
- [x] Remove all number extension methods (#497)
- [x] Remove `Length2d`, replaced by `Area` (#501)
- [x] Remove static methods on `UnitSystem`, should use `UnitSystem.Default` instead (#496)
- [x] Remove unit parameter from `ToString()` methods (#546)

#### Changed
- [x] Throw exception on NaN values in static constructor methods, like `FromMeters()` (#502, see #176 (comment))
- [x] Throw if unit was not specified when constructing quantities, (#499 - #389 (comment))
- [x] Stricter parsing (#343 and #180 (comment))
- [x] Remove unit parameter from `ToString()` methods (#546)
- [x] Change Temperature arithmetic back to use base unit Kelvin (#550, see #518)

#### Renamed
- [x] Correct SingularName for some Flow unit definitions (#494, see #360)
- [x] Split/rename UnitSystem into UnitParser, GlobalConfiguration, UnitAbbreviationsCache (#511)

#### Fixed
- [x] Search for any TODO comments in the code to address, remove comment and either fix or create issue (5d24432)
- [x] Update README with v4 changes #498
- [x] Do not try/catch in UnitConverter.Try-methods (#506)
- [x] Do not try/catch in `Length.TryParse()` and for other quantities (#507)
- [x] Add/update changelog in wiki for v4 with some summary copied from this issue, some v3 to v4 migration instructions and briefly explain why some stuff were removed

## Milestones
- [x] Finalize work items list (Monday, October 8)
- [x] Release 4.0.0-beta1, feature complete (Wednesday, October 31)
- [ ] ~Show release notes and upgrade guide when upgrading to 4.x nuget, if possible~
- [x] Release 4.0.0 (Monday, December 17)
@angularsen
Copy link
Owner

I am closing this issue since v4 is now released and we decided to stay with the old behavior until someone can present convincing evidence that it will be an improvement for the majority of our userbase to change this behavior. It will be non-intuitive to some part of the userbase regardless, and at this point we don't know if it is the majority that is confused or the minority.

Please reopen if you want to champion this and gather the information we need to decide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants