unitsofmeasurement / uom-systems

Units of Measurement Systems
http://www.uom.systems
Other
36 stars 17 forks source link

UCUM parsing "Hz/60" != Units.HERTZ.divide(60) #151

Open msqr opened 5 years ago

msqr commented 5 years ago

Hello, I was writing some tests to verify parsing of UCUM units, and ran into a problem where parsing Hz/60 did not result in a Unit that was equal to Units.HERTZ.divide(60) as I was expecting. When parsing the string, the result ends up as Hz/one*60. I wondered if these two Units should actually be considered equal? Maybe this is related to #140?

Here's some example test code of what I am doing:

public void ucum_rpm() throws UnconvertibleException, IncommensurableException {
    UCUMServiceProvider sp = new UCUMServiceProvider();

    // the following returns the DefaultFormatService class
    UnitFormat uf = sp.getFormatService().getUnitFormat();
    Unit<?> unit = uf.parse("Hz/60");
    assertThat("Unit is RPM", unit, equalTo(Units.HERTZ.divide(60)));

    // the following returns the UCUMFormatService class
    UnitFormat uf2 = sp.getUnitFormatService().getUnitFormat();
    Unit<?> unit2 = uf2.parse("Hz/60");

    // as an alternative check, look for identify transform
    UnitConverter conv = unit2.getConverterToAny(Units.HERTZ.divide(60));
    assertThat("Unit conversion to RPM is identity", conv.isIdentity(), equalTo(true));

    // The following assert does not work because `unit` is parsed into Hz/one*60
    assertThat("Unit is RPM", unit2, equalTo(Units.HERTZ.divide(60)));
}
keilw commented 5 years ago

With a quantity it is often the case, that a rather strict equals() method by the JDK fails because it looks at the exact class which is not representative in many cases, that's why we introduced the notion of equivalence. It may happen less often with units, but a TransformedUnit and AlternateUnit are also not equal in the sense of Java, so it is likely because of that. Did you try isEquivalentTo() here as well? Aside from that, while HERTZ has no special character in the UCUM catalog, you may stick to one of the classic UnitFormat implementations for units in the RI, the UCUMFormat you got from the UCUMServiceProvider is optimized to work with the UCUM system.

msqr commented 5 years ago

If I cast the Unit to a ComparableUnit then isEquivalentTo() does return true -- I did this:

final Unit<Frequency> rpm = Units.HERTZ.divide(60);
if ( unit2.isCompatible(rpm) && unit2 instanceof ComparableUnit<?> ) {
    ComparableUnit<Frequency> cu2 = (ComparableUnit<Frequency>) unit2.asType(Frequency.class);
    assertThat("Unit is RPM", cu2.isEquivalentTo(rpm), equalTo(true));
}

Unfortunately ComparableUnit and thus isEquivalentTo() is not in the javax.measure API, that is why I was using equals() for comparison. The UCUMServiceProvider is used here as I extracted the test case out of an application that is relying on UCUM. Is there a recommended way to compare Unit instances for equality while staying within the javax.measure API? For example using UnitConverter.isIdentity() worked, but I wondered if there was a more straightforward way?