Skip to content
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

Fix group calculations to use GroupItem's system unit #4563

Open
wants to merge 23 commits into
base: main
Choose a base branch
from

Conversation

andrewfg
Copy link
Contributor

@andrewfg andrewfg commented Jan 18, 2025

Fixes #4562

There was a bug whereby the calculations over a GroupItem's set of member Items are based on the Unit of the first member in the set. There are two problems: a) the order of the Items in the set is not fixed, and b) the Unit of the first member Item is also not definitive to the Unit of the expected result. Therefore these calculations do produce variable and unexpected results.

This PR fixes the bug by normalising the calculations over the group's set of member Items so that all calculations are based on the System Unit of the GroupItem's base Item.

Furthermore this PR allows conversion between non- inverted and inverted Units so invertible conversions (e.g. Kelvin <=> Mirek) are supported.

Signed-off-by: Andrew Fiddian-Green [email protected]

Signed-off-by: Andrew Fiddian-Green <[email protected]>
@andrewfg andrewfg requested a review from a team as a code owner January 18, 2025 19:50
@andrewfg
Copy link
Contributor Author

@mherwege for info..

@andrewfg

This comment was marked as outdated.

@andrewfg
Copy link
Contributor Author

@mherwege while testing this, I discovered that the JUnit assertion test values are physically WRONG. e.g. the sum of 23.54 °C plus 89 °C plus 122.41 °C is absolutely NOT 234.95 °C .. rather it is 781.24 °C .. this assumes that the three absolute Kelvin values must be summed. => So this really provokes the question about what we are really trying to achieve here? Do we want a) just the sum of three numbers, or b) the sum of three physical temperatures?

       items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("23.54 °C")));
        items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
        items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("89 °C")));
        items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
        items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("122.41 °C")));

        GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class, null);
        State state = function.calculate(items);

        assertEquals(new QuantityType<>("234.95 °C"), state);

@mherwege
Copy link
Contributor

@mherwege while testing this, I discovered that the JUnit assertion test values are physically WRONG. e.g. the sum of 23.54 °C plus 89 °C plus 122.41 °C is absolutely NOT 234.95 °C .. rather it is 781.24 °C .. this assumes that the three absolute Kelvin values must be summed. => So this really provokes the question about what we are really trying to achieve here? Do we want a) just the sum of three numbers, or b) the sum of three physical temperatures?

Look at the PR I already linked several times. This is on purpose. The second and third arguments are interpreted ad differential values, not absolute values. And that’s what makes sense, as most would expect that adding 20 °C to 20 °C gives 40°C. And with the way it is done, it does. It also works for Fahrenheit, which is not the same scale as Kelvin or Celsius. This problem is only there if the 0 point of the respective units is not the same.

Signed-off-by: Andrew Fiddian-Green <[email protected]>
@andrewfg
Copy link
Contributor Author

The .. arguments are interpreted as differential values

Ok. Got it.

I just committed support for the following..

  • use the units of the group base item when available (in particular applies to sum and probably median functions
  • added support for inverse units (in particular applies to color temperatures)
  • added unit tests for the above.

Signed-off-by: Andrew Fiddian-Green <[email protected]>
@andrewfg andrewfg changed the title [wip] QuantityType improve group calculations Improve Group Item QuantityType calculations Jan 19, 2025
Signed-off-by: Andrew Fiddian-Green <[email protected]>
Signed-off-by: Andrew Fiddian-Green <[email protected]>
Signed-off-by: Andrew Fiddian-Green <[email protected]>
@mherwege
Copy link
Contributor

What about the following approach:

Create a new method in QuantityType, addAbsolute, similar to add (

)

It should be something like:

    public QuantityType<T> addAbsolute(QuantityType<T> state) {
        Quantity<T> quantity = Quantities.getQuantity(this.quantity.getValue(), this.quantity.getUnit(),
                Scale.ABSOLUTE);
        Quantity<T> quantitytoAdd = Quantities.getQuantity(state.quantity.getValue(), state.quantity.getUnit(),
                Scale.ABSOLUTE);
        return new QuantityType<>(quantity.add(quantityToAdd));
    }

You then call this method instead of add in the group arithmetic functions.
This way, adding in the group will always use the absolute 0 reference and the result becomes independent of the units and sequence. This will also yield the right average.

This will then be different from when you simply do the calculation in a rule, because there only the first argument is treated as ABSOLUTE. But I don't think this kind of logic applies in groups anyhow.

@andrewfg
Copy link
Contributor Author

andrewfg commented Jan 20, 2025

You then call this method instead of add in the group arithmetic functions.

@mherwege please calm down. As you say add is a relative function. It is based on the zero point offset of the initial unit. So in the case of Celsius it is a relative add. And in the case of Kelvin (and most other zero based units) the add function is at the same time both relative and absolute.

So..

// absolute
sum = QuantityType.valueOf(0, Unit.KELVIN)
sum.add(celsius value)
sum.add(fahrenheit value)
sum.add(kelvin value)

// relative
sum = QuantityType.valueOf(0, Unit.Celsius)
sum.add(celsius value)
sum.add(fahrenheit value)
sum.add(kelvin value)

// etc.

Signed-off-by: Andrew Fiddian-Green <[email protected]>
Signed-off-by: Andrew Fiddian-Green <[email protected]>
@mherwege
Copy link
Contributor

As you say add is a relative function. It is based on the zero point offset of the initial unit. So in the case of Celsius it is a relative add. And in the case of Kelvin (and most other zero based units) the add function is at the same time both relative and absolute.

Absolutely true, but my worry is the different results depending on the unit used. And that is not logical. QuantityType calculations are meant to give the same result, whatever the unit. And of course that cannot be true for invertible units, which is a special case. And that's what we try to solve that by converting to the group item base unit to make the result at least predictable. But if it breaks the rule of having the same result within the same dimension, whatever the unit, that is not acceptable. And the rule is broken because of the conversion to the base unit. Add now does not do a relative add anymore, whatever the unit.

@mherwege
Copy link
Contributor

please calm down

Honestly, I am pretty calm on this. I am just trying to achieve the best possible result. That's why I keep on questioning things. If we don't do that now, I know we hit issues at some point in time and we will have to find other workarounds again.

@andrewfg
Copy link
Contributor Author

andrewfg commented Jan 20, 2025

my worry is the different results depending on the unit used

We just need to add the following text to the readme and JavaDoc; then everything clear :)

The sum resp. add function summates the series of values relative to the zero point offset of the target unit. Most units have an offset of zero in which case the result is an absolute sum of the values. However some units (e.g. Fahrenheit, Celsius) have a non zero offset; in these cases the result is a sum relative to the zero point e.g. 20 C + 30 C => 50 C

And that is not logical.

See. Perfectly logical. :)

if (non_zero_offset) {
   System.out.println("sum is relative to the offset");
} else {
   System.out.println("sum is ALSO relative to the offset");
   System.out.println("ergo: sum is absolute");
}

@mherwege
Copy link
Contributor

mherwege commented Jan 20, 2025

The sum resp. add function summates the series of values relative to the zero point offset of the target unit. Most units have an offset of zero in which case the result is an absolute sum of the values. However some units (e.g. Fahrenheit, Celsius) have a non zero offset; in these cases the result is a sum relative to the zero point e.g. 20 C + 30 C => 50 C

This is not what the current add method in QuantityType does.
Here is an example:
20 K add 20 °C will result in 40 K
Equally 20 °C add 20 K will result in 40 °C

Within the group arithmetic functions, because you first convert all arguments to the base unit, you get (assuming base unit is K):
20 K + 20 °C = 20 K + 293.15 K = 313.15 K = 40 °C
Or if base unit is °C: -253.15 °C + 20 °C = -233.15 °C = 40 K
So the result becomes dependent on the base unit. I don’t think it should. So the only way around is to always use absolute values.

Signed-off-by: Andrew Fiddian-Green <[email protected]>
@andrewfg
Copy link
Contributor Author

Equally 20 °C add 20 K will result in 40 K

Are you sure? I would expect a result of 40 °C ???

Signed-off-by: Andrew Fiddian-Green <[email protected]>
@andrewfg
Copy link
Contributor Author

@mherwege I made a commit just now that should hopefully reflect our final agreement.

Signed-off-by: Andrew Fiddian-Green <[email protected]>
Signed-off-by: Andrew Fiddian-Green <[email protected]>
Signed-off-by: Andrew Fiddian-Green <[email protected]>
Signed-off-by: Andrew Fiddian-Green <[email protected]>
Copy link
Contributor

@mherwege mherwege left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@andrewfg
Copy link
Contributor Author

@mherwege thanks for approving the PR. Unfortunately I had to revert your proposal from .collect(Collectors.toList()) back to .map(s -> (QuantityType) s).toList() .. the reason is that whilst the former would compile locally in Eclipse IDE it failed the Maven build. The reverted version passes both Eclipse and Maven.

@andrewfg andrewfg changed the title Normalise GroupItem member calculations to use the GroupItem's Unit GroupItem member calculations use GroupItem's Unit Jan 23, 2025
@andrewfg andrewfg changed the title GroupItem member calculations use GroupItem's Unit GroupItem use GroupItem's SystemUnit Jan 24, 2025
@andrewfg andrewfg changed the title GroupItem use GroupItem's SystemUnit GroupItem uses GroupItem's SystemUnit Jan 24, 2025
andrewfg added a commit to andrewfg/openhab-docs that referenced this pull request Jan 27, 2025
@andrewfg andrewfg changed the title GroupItem uses GroupItem's SystemUnit GroupItem uses GroupItem's system unit Jan 27, 2025
@andrewfg andrewfg changed the title GroupItem uses GroupItem's system unit GroupItem now uses GroupItem's system unit Feb 3, 2025
@andrewfg andrewfg changed the title GroupItem now uses GroupItem's system unit Fix: GroupItem now uses GroupItem's system unit Feb 5, 2025
@andrewfg andrewfg changed the title Fix: GroupItem now uses GroupItem's system unit Fix: GroupItem to use GroupItem's system unit Feb 8, 2025
@andrewfg andrewfg changed the title Fix: GroupItem to use GroupItem's system unit Fix GroupItem to use GroupItem's system unit Feb 8, 2025
@andrewfg andrewfg changed the title Fix GroupItem to use GroupItem's system unit Fix group calculations to use GroupItem's system unit Feb 9, 2025
Signed-off-by: Andrew Fiddian-Green <[email protected]>
Signed-off-by: Andrew Fiddian-Green <[email protected]>
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

Successfully merging this pull request may close these issues.

Improve consistency of calculation functions over sets of QuantityType values
2 participants