unitsofmeasurement / indriya

JSR 385 - Reference Implementation
Other
115 stars 40 forks source link

AlternateUnit definition results in ProductUnit on multiplication #338

Closed johanneshiry closed 3 years ago

johanneshiry commented 3 years ago

Hi everyone,

since a few days we observe an issue that can be fixed by casing the unit explicitly but we are wondering if this is by intention of the indriya implementation or if we just use AlternateUnit in a wrong way.

Steps to reproduce:

  1. Define an alternative unit
    public static final Unit<Dimensionless> PU = new AlternateUnit<>(ONE, "p.u.");
  2. Define another unit with metric prefix
    public static final Unit<Power> MEGAVOLTAMPERE = MetricPrefix.MEGA(VOLTAMPERE);
  3. Multiply them
    Quantity<Power> mv = Quantities.getQuantity(10, PowerSystemUnits.MEGAVOLTAMPERE);
    Quantity<Dimensionless> p = Quantities.getQuantity(1, PowerSystemUnits.PU);

Expected behavior: Result should be 10 MVA

Actual behavior: Result is 10 MVA*PU

Thanks in advance for any guide in on this. Maybe its on our side, but we couldn't manage to resolve it yet except of casting to the resulting unit we expect.

Cheers,

Johannes

andi-huber commented 3 years ago

Do you mean you have issues with how the result is formatted?

I do see a simple formatted result 10 MVA·p.u. I think, the actual behavior is governed by the fact, that your PU has a symbol "p.u.". Is there anything preventing you from just using ...

public static final Unit<Dimensionless> PU = AbstractUnit.ONE;
johanneshiry commented 3 years ago

Thanks for the super fast reaction! :)

You're right the reason is that the equals method does not recognize, that the newly defined alternate unit PU is the same as ONE. (see AlternateUnit:151 which compares the symbol as well ...). In our case, this leads to broken tests bc the actual unit is also set to MVA*PU (see screenshot). Hence it's rather a formatting, but actual a resulting unit issue.

Is there anything preventing you from just using ...

Partly yes, bc we rely on the p.u. in other contexts to know the exact unit identity.

Screenshot: Screenshot from 2021-03-03 13-24-44

Edit: The screenshot might be missleading as this is another unit, but it reflects the resulting issue. The code from this screenshot is executed with these unit definitions:

 /** Volt ampere reactive */
  public static final Unit<Power> VAR = new AlternateUnit<>(WATT, "VAr");

  /** Megavar */
  public static final Unit<Power> MEGAVAR = MetricPrefix.MEGA(VAR);
andi-huber commented 3 years ago

I see, but I also believe we cannot do much about those facts:

  1. AbstractUnit.ONE is the identity Unit
  2. There can only be one Identity Unit (when doing arithmetic with quantities).

If we'd allow more than one Identity Unit then what is the result of PU * PU? Is it PU^2 is it PU or is it ONE. Consistent reasoning breaks down immediately.

johanneshiry commented 3 years ago

You're right I see your point ...

I think only possibility to deal with this would be to provide the ability to extend AlternateUnit and write an on class with the whole behavior of what happens on multiply, addition, ... - but this would definitely be overkill (and may create even more issues and complexity) compared to just cast the unit in this particular situation to the required unit. We were just worrying that we missed some point here and that it could be done somehow.

We'll stay with the casting then.

Thank you very much for your help & have a nice weekend! :-)