-
Notifications
You must be signed in to change notification settings - Fork 404
Adding a New Unit
So you want to add a quantity or unit that is not yet part of Units.NET? Great! Here is how you do it.
Sometimes we just have to say no. Sorry! 😅 We simply want to avoid bloating the library.
To avoid putting in a lot of work just to get rejected, please Create a feature request first to describe what you would like to add.
Here are some examples of quantities we generally don't favor in the library:
- 🥝 Exotic quantities and units. If there isn't a wiki page for it and the Google search returns ¯\(ツ)/¯, then it may not be known enough to make the cut.
- ❓ Unit-less quantities like ratios. Consider using
Ratioor creating your own type. - 1️⃣ Quantities with only a single unit and no conversions to other quantities either. One of the major reasons for adding quantities to this library is the ability to convert between units.
We have made some exceptions to these "rules" for quantities we found useful or widely used enough to include, so start a discussion with us and we will figure it out.
Ok, enough of that. Let's move on! 😃
In order to build and run tests locally you need to have some tools installed.
-
.NET Core SDK 2.1+to generate and build code
Optional:
- Visual Studio 2017 and
Windows 10 SDK 10.0.16299.0to build Windows Runtime Component (WRC) target. Don't worry, you normally don't need this unless you want to target certain types of UWP apps built with WinJS or C++. The build server will build it for you.
Units.NET uses CodeGen, a C# command line app that reads JSON files with quantity and unit definitions and generates C# code. Adding a unit is a matter of changing a JSON file, running generate-code.bat file and specifying the test case value for the new unit. Easy peasy. Below are the detailed steps.
- Place in Common/UnitDefinitions
- See Length.json as an example.
- Multiply for
FromBaseToUnitand divide forFromUnitToBase, so thatLength.Centimeteris defined as"FromBaseToUnit": "x*100"and"FromUnitToBase": "x/100"where base unit isMeter - Prefer
1e3and1e-5notation instead of1000and0.00001 - Prefer a constant if the conversion factor is finite and well known (
Inch FromUnitToBase: x*2.54e-2) - Prefer a calculation if the conversion factor is infinite (
PrinterPoint FromUnitToBase: (x/72.27)*2.54e-2). If the calculation is not available, specify the most precise constant you can.doublenumeric type can represent 15-17 significant decimal digits
The base unit dimensions of the quantity, such as "L": 1 for Length and "L": 2 for Area (Length*Length).
The 7 SI base units are:
-
L- Length -
M- Mass -
T- Time -
I- ElectricCurrent -
Θ- Temperature -
N- AmountOfSubstance -
J- LuminousIntensity
when converting from one unit to another with FromUnitToBaseFunc and FromBaseToUnitFunc conversion functions. It is typically chosen as an SI derived unit (Meter, Newtonmeter, Squaremeter etc). This choice affects the precision of conversions for much bigger/smaller units than BaseUnit.
BaseUnits (optional) - the SI base units of a unit
Don't confuse this with the quantity's BaseUnit, which is discussed to be renamed.
If specified, you can create quantities with consistent units for a given unit system:
new Length(1, UnitSystem.SI).ToString() // "1 m"
new Length(1, myBritishEngineeringUnitSystem).ToString() // "1 ft"-
LengthUnit.Inchhas{ "L": "Inch" }(L=1) -
AreaUnit.SquareCentimeterhas{ "L": "Centimeter" }, because we ignore dimensions (L=2) -
VolumeUnit.Cubicfeethas{ "L": "Foot" }, because we ignore dimensions (L=3) -
ForceUnit.Newtonhas{ "L": "Meter", "M": "Kilogram", "T": "Second" }, becauseN = 1 kg * 1 m / s² = Kilogram * Meter / Second²and we ignore the dimensions -
ForceUnit.PoundForcehas{ "L": "Foot", "M": "Pound", "T": "Second" }, becauseN = 1 lbm * 1 ft / s² = Pound * Foot / Second²and we ignore the dimensions -
MassConcentrationUnit.GramPerDeciliterhas{ "L": "Centimeter", "M": "Gram" }, becauseDeciliter = 1 cm * 1cm * 1cm = Centimeter³and we ignore the dimensions
The only consequence of not specifying BaseUnits is that you cannot construct these units by passing a UnitSystem to the quantity constructor as in the example above.
-
VolumeUnit.ImperialGallonhas noBaseUnits, becauseVolume = Length³and there is no length unit that when multiplied three times would result in imperial gallon. -
RatioUnit.DecimalFractionhas noBaseUnits, because dimensionless units are not made up by any SI base units.
2. Run generate-code.bat
to generate unit classes, unit enumerations and base class for tests.
This step might no longer be necessary, I think Visual Studio 2017 and the new .csproj format automatically loads new files automatically.
- 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
- Example:
InchesInOneMeterin LengthTests.cs- I find the conversion factor to be
39.37007874from an online unit conversion calculator, it has 10 significant figures so that is plenty - I add the code:
protected override double InchesInOneMeter => 39.37007874; - I Google to double-check:
Inches In OneMeterand it tells me1 Meter = 39.3701 Inches(Google typically has fewer significant figures) - If Google can't help me, I find a second source to confirm the conversion factor (another conversion website, wikipedia, Wolfram Alpha etc)
- I check again by intuition, is there really around 40 inches in a meter? Yes, sounds about right.
- I find the conversion factor to be
Make sure all the tests pass. Either run build.bat or run the tests from within Visual Studio with ReSharper or the built-in test runner.
Please see GitHub: Creating a pull request. If you still have any questions, please ask on our Gitter chat or create an issue.
Units.NET supports logarithmic units by adding Logarithmic and LogarithmicScalingFactor (optional) properties.
-
LogarithmicScalingFactoris used to provide a scaling factor in the logarithmic conversion. For example, a scaling factor of2is required when implementing the ratio of the squares of two field amplitude quantities such as voltage. In most casesLogarithmicScalingFactorwill be1.
To create a logarithmic unit, follow the same steps from the previous section making the following adjustments:
Step 1. Add property "Logarithmic": "True" to the JSON file, just after BaseUnit. LogarithmicScalingFactor defaults to 1 if not defined.
Step 4. Provide custom implementations for logarithmic addition and subtraction unit tests. See LevelTests.cs for an example.
Refer to Level.json as an example implementation of logarithmic units.
- If you have the ReSharper plugin installed, there are code formatting settings checked into the repository that will take effect automatically.
- If you don't use ReSharper, at least follow the same conventions as in the existing code.