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

Improve Group Item QuantityType calculations #4563

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

Conversation

andrewfg
Copy link
Contributor

Fixes #4562

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]>
Unit<? extends Quantity<?>> baseUnit = baseNI.getUnit();
Unit<? extends Quantity<?>> memberUnit = memberNI.getUnit();
if (baseUnit != null && memberUnit != null) {
return baseUnit.isCompatible(memberUnit) || baseUnit.isCompatible(memberUnit.inverse());
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return baseUnit.isCompatible(memberUnit) || baseUnit.isCompatible(memberUnit.inverse());
return baseUnit.isCompatible(memberUnit) || baseUnit.isCompatible(memberUnit.getBaseUnit().inverse());

We discussed this previously. I think we should always use the base unit for inversion, which works in all cases we have identified so far.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess you meant getSystemUnit() here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

PS Insofar as all the JUnit tests succeeded I am wondering if the change is necessary? Or should we add another test for that case?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess you meant getSystemUnit() here?

Yes, that's what I meant.

PS Insofar as all the JUnit tests succeeded I am wondering if the change is necessary? Or should we add another test for that case?

Maybe try checking if mK (milliKelvin) toInvertibleUnit and then toUnit(Mired) works. I suspect that may give strange results as the inverse of milliKelvin is not in the same scale as Mired.

Copy link
Contributor Author

@andrewfg andrewfg Jan 19, 2025

Choose a reason for hiding this comment

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

milliKelvin is wrong for two reasons .. a) the factor would be micro rather than milli, and b) the conversions are mirek = 1e6 / kelvin resp. kelvin = 1e6 / mired -- i.e. not mirek = microKelvin ..

EDIT i.e. it is mirek = micro(inverse(kelvin)) .. that is why it is called mirek micro reverse kelvin

Copy link
Contributor Author

@andrewfg andrewfg Jan 19, 2025

Choose a reason for hiding this comment

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

Maybe also keep in mind that isCompatible() works both ways even without falling back to systemUnit. The issue was that toInvertibleUnit itself had a bug that requires an intermediate conversion via systemUnit.

So there is nothing to fix at this point in the code.

And #4561 provides the intermediate conversion via systemUnit.

Copy link
Contributor

Choose a reason for hiding this comment

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

My intention was to not start from the system unit (K), but from a derived unit (like mK or whatever multiplier symbol). But you are right with the fix in toInvertibleUnit, it should be fine.

Copy link
Contributor

Choose a reason for hiding this comment

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

My prior JUnit tests were testing toInvertibleUnit with target Kelvin and they all pass. However I added a test that tests toInvertibleUnit with the inverse target Mirek. This test currently fails. However it should work fine once #4561 is merged.

I don’t get why that fix would make a difference though. Did you try running the test with the patch applied as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok I will patch and test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just tried the tests with the #4561 code patch applied, and I can confirm that it works fine.

@andrewfg andrewfg changed the title [wip] QuantityType improve group calculations Improve Group Item QuantityType calculations Jan 19, 2025
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Temperature.class, QuantityType.valueOf(2000, Units.KELVIN)));
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Temperature.class, QuantityType.valueOf(1726.85, SIUnits.CELSIUS)));
Copy link
Contributor

Choose a reason for hiding this comment

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

Is color temperature really ever expressed in °C or °F? I know it now works, but I would just limit it to Kelvin and Mired.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would rather keep the test. I know it should not be commonly used. But it addresses the common case where a user creates a plain vanilla Number:Temperature Item without specifying a unit. By default Number:Temperature items have unit=Fahrenheit in USA and unit=Celsius everywhere else. So we need to test that this won't fall over..

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I understand. I am still worried that fixing that issue actually makes things worse. Right now the conversion from °C to mirek fails in the conversion as it correctly says it is not the same scale. With the changes, it will succeed. But looking at all of the discussion in the community post, that same user also assumes the state description min and max are in Kelvin when he doesn’t set the unit. Without the error, he would not have known.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think you are referring to #4432

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