angularsen / UnitsNet

Makes life working with units of measurement just a little bit better.
https://www.nuget.org/packages/UnitsNet/
MIT No Attribution
2.19k stars 380 forks source link

Nuclear "Reactivity" Unit (conversion involves ratio of base unit) #559

Closed trb5016 closed 5 years ago

trb5016 commented 5 years ago

I'm trying to add a Reactivity Unit used by nuclear engineers to determine criticality of a reaction.

The base unit is "k-effective" and another unit is "Rho"

I'm trying to define "Rho" as:

    {
      "SingularName": "Keffective",
      "PluralName": "Keffective",
      "FromUnitToBaseFunc": "x",
      "FromBaseToUnitFunc": "x",
      "Localization": [
        {
          "Culture": "en-US",
          "Abbreviations": [ "keff" ]
        }
      ]
    },
    {
      "SingularName": "Rho",
      "PluralName": "Rho",
      "FromUnitToBaseFunc": "1/(1-x)",
      "FromBaseToUnitFunc": "(x-1)/x",
      "Localization": [
        {
          "Culture": "en-US",
          "Abbreviations": [ "ρ" ]
        }
      ]
    }

With the UnitTest being: protected override double RhoInOneKeffective => 0;

But the unit test fails because: UnitsNet.Tests.CustomCode.ReactivityTests.FromValueAndUnit [FAIL] Values are not equal within tolerance: 1E-05 Expected: 1 Actual: NaN

I think I've set the definition correctly, and it's failing because, looking at the test cases, I think it's trying to test for "Rho=1" which is NaN, and then when it's trying to loop back it gets messed up and fails the test.

Any insight would be appreciated: 1) Is this just not possible to do, or 2) have I defined the unit incorrectly.

angularsen commented 5 years ago

I've been a bit under the weather the past few days, so only chiming in briefly here, but from the looks of it the FromUnitToBaseFunc is not valid for x=1 since it results in division by zero. I don't know what the correct function is, but it seems to me maybe it is a non-continuous function.

A quick google didn't give me much material, but I found these:

https://en.wikipedia.org/wiki/Per_cent_mille http://nuclearpowertraining.tpub.com/h1019v2/css/Units-Of-Reactivity-43.htm

In nuclear reactor engineering, a per cent mille is equal to one-thousandth of a percent of the reactivity, denoted by Greek lowercase letter rho. Reactivity is a dimensionless unit representing a departure from criticality, calculated by:[2]

image

where keff denotes the effective multiplication factor for the reaction. Therefore, one pcm is equal to:

image

And finally feeding this into Wolfram tells me indeed this function is not continuous, or specifically it is undefined for x=1 (I mixed up the Wolfram arguments, but you get the point).

https://www.wolframalpha.com/input/?i=solve+p+-+(x+-+1)%2Fx+%3D+0&lk=2

image

I don't immediately know how to solve this, it's the first time we have this kind of function. I need to think some more on it, or maybe someone else sees a way to solve this?

trb5016 commented 5 years ago

Yep, I agree the non-continuity is the problem. I actually think the unit is working properly and as expected, it's just all the test code gets unhappy because it's using '1' as the test case. I thought about just tweaking my projects build scripts to not test this specific unit, but that feels unclean haha.

For now, I've changed the "Reactivity" base unit to 'rho', and defined a few others I need (including pcm) and just created separate functions for converting between keff and rho.

(From the technical perspective, while you can equate rho and keff to each other, it's practically rare that it needs done, because while they're both "reactivity" they're typically used for referring to totally separate calculations)

Thanks for looking at it. If you want to keep this open to see if anyone has ideas, that would be cool, but I'm fine closing it because I recognize the obscurity of the issue.

angularsen commented 5 years ago

Well, my first ideas are along the lines of:

  1. Add option to bypass tests for this particular unit.
  2. Add option in codegen scripts to choose a different test value and field name in the unit definition, something like this:
    {
      "SingularName": "Keffective",
      "PluralName": "Keffective",
      "FromUnitToBaseFunc": "x",
      "FromBaseToUnitFunc": "x",
      "TestFieldValue": 2,
      "TestFieldName": "RhoInTwoKeffective"
    }

Then the generated field would use the given name, and you set a new correct value:

protected override double RhoInTwoKeffective => -1;

Then generated tests would be updated to use new value 2, like this one:

        [Fact]
        public void MeterPerSecondSquaredToAccelerationUnits()
        {
            Acceleration meterpersecondsquared = Acceleration.FromMetersPerSecondSquared(2);
        }

Could that work? Might not require too much work and is hopefully generic enough to cover future, similar challenges.

tmilnthorp commented 5 years ago

Why not just make an overridable property for the ToUnit tests that is the value to convert from? Default is one. No extra json necessary.

angularsen commented 5 years ago

I like it

angularsen commented 5 years ago

The only drawback is naming of field says PerOneMeter etc. I can live with that.

trb5016 commented 5 years ago

I like tmilnthorp's idea too. There's been instances in other situations where I wanted to change the test value from '1' because it makes it a "better" check, so that would be handy for that too.

I don't think the naming would be much of an issue since 99% of the time it will still be PerOne[unit].

Thank you both for your ideas. I'll close this out.