Problem: calculations with numbers with units: error with type conversion

Dear all!

Oh gosh, those units… only troubles…

I wanted to do a linear interpolation y = k * x + d, where [x] is a temperature and [y] is a number, so I set

val QuantityType<Temperature> x1   = -10|°C
val QuantityType<Temperature> x2   =   5|°C
val Number y1 = 6
val Number y2 = 2
var Number k
var Number d

then I did

k = (y2 - y1) / (x2 - x1)
logInfo("test.rules", "k: {}", k)

and the log file gives me nicely:

k: -0.26...67 1/°C

in units 1 over °C, as expected

calculation of the offset d (done by d = y1 - k * x1) involves the product k * x1 which proves to be problematic:

m = k * x1

gives me an error

Error during the execution of startup rule: °C is non-linear, cannot convert

despite [k] is 1 over °C and [x1] is °C, so the multiplication of 1/°C with °C should yield a dimensionless number.

what am I doing wrong here?

Thanx for support
Sulla

Have you seen -

When you come to do maths with that later, that also involve some other unit, “ordinary °C” in this case, openHAB is going to treat the °C elements as absolute temps and do conversions.

EDIT- afterthought; rules are loosely typed. You might have declared k as Number, but it holds a quantity type now, with units.

It’s the 2°C + 1°C = 3°C conundrum.
We mean 2°C as an absolute measured temperature, and 1°C as an interval or increment, but there’s no way to tell the difference.
That doesn’t matter while everything is in same units.
The nature of your calculation is that it can’t all be in same units, one part is “1/°C” units.

might be a complaint about °C being difficult to handle because of the offset or point of origin that make temperatures different from other quantity types.

Will this calculation work if all done in Kelvin, willit think that is linear? Conversions are easy.

yes, doing

k = (y2 - y1) / (x2.toUnit("K") - x1.toUnit("K"))

results in a logfile entry of

k: -0.2666666666666666666666666666666667 1/K

and mutiplication with x1 in units K works (ie without errors)

m = k * x1.toUnit("K")

but doesnt result in a dimensionless number but in the variable m being in units K

logInfo("test.rules", "m: {}", m)
m: -70.17333333333333333333333333333334 K

As a note, m=k*x1 (ie leaving x1 in °C) works as well

m = k * x1
logInfo("test.rules", "m: {}", m)
m: 2.666666666666666666666666666666667 °C

If I then further process that variable m with units K (which should be dimensionless, really) and add it to the dimensionless unit y1 (defined as number) results in d being a dimensionless number as well.

d = y1 - k * x1.toUnit("K")
logInfo("test.rules", "d: {}", d)
d: 76.17333333333333333333333333333334

and the whole interpolation works when using real input for the variable x from a temperature sensor:

y = k * (Temperaturmesser2Temperatur.state as QuantityType<Temperature>).toUnit("K") + d
logInfo("test.rules", "y: {}", y)
y: 4.10666666666666666666666666666666

which should be something between 2 and 6. 4.1 is nice (and correct as I verified with my calculator)

(I now can feed in the interpolated value into a heating control system as an on-time for the heating.)

1 Like

Have you seen -
Working with Number:Temperature Items in rules

Yes, I have, and thanks a lot for all your efforts!

Thank goodness for that!
The maths concepts were getting way beyond me, and quite how the framework deals with “invented” units like 1/K is not at all clear. I suspect there is some ‘disregarding’ of non-standard units going on.

As a rule of thumb I find it works best to make sure all the units of the calculation are the same or, if that isn’t possible, to strip the units off when doing your calculations.

x1.toUnit("K").floatValue

Once you are done with the calculation you can add the units back on if necessary, though often you end up with an unsupported unit.