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

Incorrect standard gravity/unit conversion constant #1306

Closed
MH-ZShearer opened this issue Aug 14, 2023 · 6 comments · Fixed by #1308
Closed

Incorrect standard gravity/unit conversion constant #1306

MH-ZShearer opened this issue Aug 14, 2023 · 6 comments · Fixed by #1308

Comments

@MH-ZShearer
Copy link
Contributor

MH-ZShearer commented Aug 14, 2023

Working in the force calibration industry, and needing to occasionally perform mass conversion, I don't agree on the values that are used for force, and mass, unit conversions. While not technically the standard acceleration due to standard gravity (standard gravity), these unit conversion rates are derived from that value and are accepted as the standard unit conversion rate.

I'm not sure where the constant originally came from, but the furthest back I could find it referenced in this repository was at the below linked file. This value has carried through significant overhauls to the library for the last decade without any documentation about where it came from. I propose that the value 9.80665002864 be changed to 9.80665 as per the standard definition of acceleration due to standard gravity.

https://en.wikipedia.org/wiki/Gravitational_acceleration

A conventional standard value is defined exactly as 9.80665 m/s2 (32.1740 ft/s2)

https://en.wikipedia.org/wiki/Standard_gravity

It is defined by standard as 9.80665 m/s2 (about 32.17405 ft/s2). This value was established by the 3rd General Conference on Weights and Measures (1901, CR 70) and used to define the standard weight of an object as the product of its mass and this nominal acceleration.

Although the actual acceleration of free fall on Earth varies according to location, the above standard figure is always used for metrological purposes.

Original

public const double Gravity = 9.80665002864;

Current

"FromUnitToBaseFunc": "{x} * 9.80665002864",

It may be worth pointing out that the conversion rate for newtons to kilograms-force is also derived from standard gravity and would require the value here to be changed as well. Other conversion rates, and quantities, are impacted by this "constant" as well.

For reference, we use 4.4482216152605 N/lbf as the conversion rate for pounds-force to newtons and 0.45359237 kgf/lbf as the conversion rate for pounds-force to kilograms-force. And to calculate the value of standard gravity, we take the conversion rate of lbf -> N divided by the conversion rate of lbf -> kgf. In the context of Units.NET, it should be the conversion rate of N -> N divided by the conversion rate of N -> kgf. This can be used as a unit test to verify the values aren't changed in the future.

https://en.wikipedia.org/wiki/Kilogram-force

Therefore, one kilogram-force is by definition equal to 9.80665 N.


Edit: I originally included the mass equations because I thought I remembered them being wrong as well. After double checking today, it appears the mass conversions are correct, but the force conversions are not. The values look like they are the result of some rounding errors but I can't quite put my finger on what it might be.

@angularsen
Copy link
Owner

angularsen commented Aug 15, 2023 via email

@MH-ZShearer
Copy link
Contributor Author

Thanks, I'm working on my changes now. But I'm running into an issue with the generated code and the file encodings. Even though I re-save the files with the original encodings, they are apparently not saving, regardless of using VS or VSCode. I thought I saw a recent issue in this repository regarding the encodings, but I can't seem to find it again.

Is there any reason I shouldn't prefer using decimals? I expect to commit this change separately. I also want to add the grams and ponds base units, but I think it's valuable to keep the kilo- units separate as they are a primary unit of force and most documentation refers to the kilo- prefixed units.

I was looking to write some tests but wasn't sure if they just go in the UnitsNet.Tests\CustomCode directory or not. I also reviewed the information in the wiki about Adding a New Unit and wanted to see if it still applied to writing my tests as well.

  • If the conversion is a simple * or / operation, then prefer * in FromBaseToUnit and / in FromUnitToBase. As an example, Length.Centimeter is defined as "FromBaseToUnit": "{x} * 100" and "FromUnitToBase": "{x} / 100", instead of {x} / 0.001 and {x} * 0.001.
  • Prefer 1e3 and 1e-5 notation instead of 1000 and 0.00001

This is what I am familiar with regarding writing tests and wanted some feedback before I submitted a PR. I think it would look nicer to group the units based on base unit (US units grouped together, newtons grouped together, etc.). Completely open to feedback on this as well.

public class ForceTests : ForceTestsBase
{
    private const decimal StandardGravity = 9.80665m;
    private const decimal PoundsConversionRate = 4.4482216152605m;

    protected override bool SupportsSIUnitSystem => true;

    protected override double MicronewtonsInOneNewton => 1e6;
    protected override double MillinewtonsInOneNewton => 1e3;
    protected override double NewtonsInOneNewton => 1;
    protected override double DecanewtonsInOneNewton => 1E-1;
    protected override double KilonewtonsInOneNewton => 1E-3;
    protected override double MeganewtonsInOneNewton => 1E-6;

    protected override double DyneInOneNewton => 1e5;

    protected override double KilogramsForceInOneNewton => (double)(1 / StandardGravity);
    protected override double TonnesForceInOneNewton => (double)(1 / (StandardGravity * 1_000m));

    protected override double KiloPondsInOneNewton => (double)(1 / StandardGravity);

    protected override double PoundalsInOneNewton => 7.23301;

    protected override double OunceForceInOneNewton => (double)(1 / (PoundsConversionRate / 16m));
    protected override double PoundsForceInOneNewton => (double)(1 / PoundsConversionRate);
    protected override double KilopoundsForceInOneNewton => (double)(1 / (PoundsConversionRate * 1_000m));
    protected override double ShortTonsForceInOneNewton => (double)(1 / (PoundsConversionRate * 2_000m));

    // ...

    public void KilogramForceDividedByNewtonEqualsStandardGravity()
    {
        double duration = Force.FromKilogramsForce(1) / Force.FromNewtons(1);
        Assert.Equal((double)StandardGravity, duration);
    }
}

@MH-ZShearer
Copy link
Contributor Author

After investigating a bit more, it looks like you do have rules regarding testing. I would still like some feedback about this prior to committing potentially unsatisfactory changes (since I already have an issue regarding the encoding).

4. Fix generated test stubs to resolve compile errors tests

  • Override the missing abstract properties in the unit test class (ex: LengthTests.cs)
  • Specify value as a constant, not a calculation, with preferably at least 7 significant figures where possible.
  • Triple-check the number you write here, this is the most important piece as it verifies your conversion function in the .JSON file
  • If possible, add a comment next to the value with an URL to where the value can be verified.

I do have some qualms about these arbitrary rules, mainly that it encourages potentially less accurate values due to the 7 significant figure recommendation. But I believe this should be a dedicated discussion.

I also happened to stumble upon the reason practically no units are using the decimal data type. I believe this should also be a separate discussion. It could improve the precision for quantities that have had precision values set for it.

@angularsen
Copy link
Owner

Is there any reason I shouldn't prefer using decimals?

Use double, it is the standard value type in UnitsNet, decimal is only used by 3 quantities and not for very good reasons.

I also want to add the grams and ponds base units

If you plan on making several changes, please create multiple pull requests.
For example, fix constants in one PR. Add units in another PR.

I was looking to write some tests but wasn't sure if they just go in the UnitsNet.Tests\CustomCode directory or not.

Yes, custom (manually written) tests go in CustomCode dir.
Generated tests (we have those too), go in GeneratedCode dir and have .g.cs extension.

Just see where how other tests are placed and try to follow that.

it encourages potentially less accurate values due to the 7 significant figure recommendation

It says at least 7 significant figures, no upper limit. 👍

practically no units are using the decimal data type

Only 3 quantities use decimal; Power, Information, BitRate.
Power is about to change to double in #1195.

Information and BitRate may or may not stay on decimal. Not decided yet. There is a lot of complexity in the code base just to support decimal for 2 quantities, while 100+ quantities use double.

If you search, you'll find tons of previous discussions on double vs decimal.
Generics may be an option to support any numeric type, but it has its own downsides described here: https://github.com/angularsen/UnitsNet/wiki/Experimental:-Generic-Math-and-INumber

Some discussion on NaN/InF and double vs decimal:
#1268 (comment)

As it is now, I would personally favor moving everything over to double until we have a better way to support generics or some other approach to let the user choose double vs decimal.

Hope this helps, just let me know if you have any further questions!

@MH-ZShearer
Copy link
Contributor Author

MH-ZShearer commented Aug 21, 2023

Thank you for your feedback. I have already made one pull request regarding these changes. I have at least one more that I want to do based on your feedback.

If you search, you'll find tons of previous discussions on double vs decimal.

I have and I was in the decimal camp due to our lab's emphasis on precision. However, after speaking with someone much smarter than myself in programming, it was stated that double should be more than sufficient for us and that it doesn't make sense to change the library. However, their recommendation was the same conclusion most others have come to here; using generics is the best option. Unfortunately, double specific values NaN and Inf definitely make that more challenging. And I'm not confident enough in my abilities to really assist in that matter.

The only thing that I still have concerns with is regarding the unit testing precision. It's stated that the tests pass as long as the value is within an epsilon of 0.00001. I believe this is the wrong approach and the testing system should prefer testing significant digits. This is a bit harder to implement, and may break, but should improve the confidence in the accuracy of this library for those that care more about accuracy than performance, for example.

I did briefly attempt to search for modifications to the testing process, but I was unable to find anything worthwhile regarding this. Was the epsilon chosen due to simplicity? I see there are a lot of tests (close to 30,000) and improved precision checking may increase the time it takes to perform all of the tests if most of them check significant digits instead of just an epsilon.

@angularsen
Copy link
Owner

Let's have the epsilon error as a separate discussion and potential pull request, so this PR can focus on fixing the conversion function constants for the units.

Epsilon constant was arbitrarily chosen and primarily used to check that conversion from small/large units and the chosen base unit and back again are within the tolerance. Example: LengthTestsBase.ConversionRoundTrip

The whole approach to error tolerance could definitely be improved, I am open to ideas.

angularsen pushed a commit that referenced this issue Sep 16, 2023
Fixes #1306

Corrects the conversion rates of the force quantity to be the precisely
defined values, relative to standard gravity.

Adds a unit test that verifies that the conversion rate from kilograms
divided by the conversion rate from newtons equals standard gravity
(`9.80665` m/s<sup>2</sup>).
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 a pull request may close this issue.

2 participants