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

v6 Wishlist #1200

Open
1 of 13 tasks
angularsen opened this issue Feb 12, 2023 · 28 comments
Open
1 of 13 tasks

v6 Wishlist #1200

angularsen opened this issue Feb 12, 2023 · 28 comments
Labels
pinned Issues that should not be auto-closed due to inactivity.
Milestone

Comments

@angularsen
Copy link
Owner

angularsen commented Feb 12, 2023

This issue serves as a place to discuss what to include in this version, since major version bumps allow us to make breaking changes we think are necessary or worth it.

The wishlist keeps track of the items to include, but items may be added or removed at any time.

As changes are completed, they are added to https://github.com/angularsen/UnitsNet/wiki/Upgrading-from-5.x-to-6.x

Wishlist

@maxreb
Copy link

maxreb commented Mar 21, 2023

Add System.Text.Json support #966

@angularsen
Copy link
Owner Author

Decide on a naming and abbreviation convention for imperial/UK units and US units; like gallon, ounces, cup etc.

It is a bit of a mess right now:
image image

image

LINQPad sample
void ListUnits(string withPartialName)
{
    Quantity.Infos
        .SelectMany(i => i.UnitInfos
            .Where(n => n.Name.Contains(withPartialName))
            .Select(u => new 
            { 
                u.Name, 
                Abbrev = UnitAbbreviationsCache.Default.GetDefaultAbbreviation(u.Value.GetType(), Convert.ToInt32(u.Value)) 
            }))
        .Dump(withPartialName);
}

ListUnits("Uk");
ListUnits("Imperial");
ListUnits("Us");

I personally like "UK", since it is shorter and better matches "US".
Wolfram also uses US gallon and UK gallon.

Some conventions for imperial gallons:
https://en.wikipedia.org/wiki/Gallon#:~:text=the%20imperial%20gallon%20(imp%20gal,American%20and%20Caribbean%20countries%3B%20and

Today the abbreviation convention is gal (U.S.) and gal (imp.).
Depending on landing on UK or imperial naming, we could go with one of these pairs:

  • US gal and UK gal
  • US gal and imp gal
  • gal (US) and gal (UK)

We can support multiple of these plus the old ones, but the primary form should be standardized across the different units.

@ebfortin
Copy link
Contributor

ebfortin commented Jun 24, 2023

Do not throw ArgumentException on NaN, Inf+ or Inf- or give a way to configure the behavior globally.

Context:
#1268 (comment)

@MisinformedDNA
Copy link

MisinformedDNA commented Sep 12, 2023

I think UnitsNet should be more extensible. Perhaps there could be a base package, but separate NuGet packages for US measurements, Imperial, UK, etc. Alternatively, customizable abbreviations could be a solution. I would never expect me or my users to see "gal (US)" instead of "gal". This would also help the debate here.

UPDATE:

Custom abbreviations already exist: UnitAbbreviationsCache.Default.MapUnitToDefaultAbbreviation(HowMuchUnit.Some, "sm");

@angularsen
Copy link
Owner Author

I think UnitsNet should be more extensible.

Conceptually, I agree.

Customizable abbreviations is already supported as you found, and we support custom quantities and units with some limited functionality: https://github.com/angularsen/UnitsNet/wiki/Extending-with-Custom-Units

Splitting up in packages is not so easy though. Things like Torque = Force * Length becomes problematic when types exist in different packages and they can't have circular dependencies. Imperial is even worse, splitting some units of a quantity to different packages. The main problem is static typing, which is also one of the main features of UnitsNet.

One benefit of splitting up in packages is smaller binary size to only bring in what you need.
Some prototyping here: #1181

@Markussp256
Copy link

I was requested to give a list of units with the wrong plural form (see #1330 )

I checked the .json-file with the speed units and only MetersPerMinute has a wrong plural form, all other Speed units seem to have the correct plural form.

angularsen pushed a commit that referenced this issue Jan 2, 2024
Came across this while working on #1329. I'm afraid it's an API-breaking
change, so maybe it should wait for v6?

Related:
#1200
angularsen pushed a commit that referenced this issue Jan 6, 2024
Came across some more unit names that seem wrong. Breaking changes once
more.

Related #1200
@angularsen
Copy link
Owner Author

@Markussp256

I was requested to give a list of units with the wrong plural form (see #1330 )

I checked the .json-file with the speed units and only MetersPerMinute has a wrong plural form, all other Speed units seem to have the correct plural form.

Some more fixes were made recently in
#1351

angularsen added a commit that referenced this issue Feb 4, 2024
Related #1200 

In the PR adding generic math (#1164) @AndreasLeeb states:
> Regarding the operators in the *.extra.cs files, that could be tackled
easily by describing the dependencies (operations) between different
quantities in the quantity JSON files, and then the operator overloads
and the generic math interfaces for the quantity structs could also be
automatically generated. But that's a topic for another time 😄

I decided to give this a shot.

`UnitRelations.json` contains relations extracted from the existing
*.extra.cs files. I decided on a new file because multiplication is
commutative and I didn't want to duplicate these in the individual
quantity JSON files, or risk missing one or the other, so it's best to
define them once in one place. The generator handles this by generating
two operators for a single multiplication relation.

The relations format uses the quantities method names. This is a bit
unfortunate, but it's the best I could come up with without making the
CodeGen project depend on UnitsNet, which would create a bit of a
chicken/egg problem. This is not unheard of (self-hosted compilers) but
I wanted to keep it simple for now.

The generated code enables the removal of 44 *.extra.cs files, and the
17 remaining contain much less code.

---------

Co-authored-by: Andreas Gullberg Larsen <[email protected]>
angularsen added a commit that referenced this issue Feb 14, 2024
Related #1200 
Fixes #1356

Apparently, protobuf-net does not support 0-indexed order values.

### Changes
- Change `DataMember` explicit order from 0-indexed to 1-indexed

### Background
Some investigation indicates that the actual order value is not important, only the relative ordering: 
#1356 (comment)

- WCF should tolerate this, according to its docs. #1356 (comment)
- Binary formatters hopefully only care about relative ordering and thus still compatible?
	- Protobuf-net, it never worked and should be OK
	- [BinaryFormatter](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter?view=net-8.0) is obsolete and generally not recommended for years, so hopefully no users are affected
	- A bunch of others exist too, haven't looked into how they handle this
- XML/JSON serializers should tolerate any order, can't imagine it breaking these?
angularsen added a commit that referenced this issue Feb 14, 2024
Related #1200
Fixes #1356

Apparently, protobuf-net does not support 0-indexed order values.

- Change `DataMember` explicit order from 0-indexed to 1-indexed

Some investigation indicates that the actual order value is not important, only the relative ordering:
#1356 (comment)

- WCF should tolerate this, according to its docs. #1356 (comment)
- Binary formatters hopefully only care about relative ordering and thus still compatible?
	- Protobuf-net, it never worked and should be OK
	- [BinaryFormatter](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter?view=net-8.0) is obsolete and generally not recommended for years, so hopefully no users are affected
	- A bunch of others exist too, haven't looked into how they handle this
- XML/JSON serializers should tolerate any order, can't imagine it breaking these?
angularsen added a commit that referenced this issue Feb 14, 2024
Related #1200
Fixes #1356

Apparently, protobuf-net does not support 0-indexed order values.

- Change `DataMember` explicit order from 0-indexed to 1-indexed

Some investigation indicates that the actual order value is not
important, only the relative ordering:
#1356 (comment)

- WCF should tolerate this, according to its docs.
#1356 (comment)
- Binary formatters hopefully only care about relative ordering and thus
still compatible?
	- Protobuf-net, it never worked and should be OK
-
[BinaryFormatter](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter?view=net-8.0)
is obsolete and generally not recommended for years, so hopefully no
users are affected
	- A bunch of others exist too, haven't looked into how they handle this
- XML/JSON serializers should tolerate any order, can't imagine it
breaking these?
angularsen pushed a commit that referenced this issue Mar 1, 2024
lipchev added a commit to lipchev/UnitsNet that referenced this issue Apr 10, 2024
commit 528db5e
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Thu Apr 4 19:40:54 2024 +0200

    JsonNet: 6.0.0-pre006

commit 5f28ca3
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Thu Apr 4 19:40:49 2024 +0200

    UnitsNet: 6.0.0-pre006

commit 8f9a1c0
Merge: 3f71f26 0eb58d3
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Thu Apr 4 19:35:03 2024 +0200

    Merge remote-tracking branch 'origin/master' into release/v6

    # Conflicts:
    #	CodeGen/Generators/NanoFrameworkGen/NuspecGenerator.cs
    #	CodeGen/Generators/NanoFrameworkGen/QuantityGenerator.cs
    #	CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs
    #	UnitsNet.NanoFramework/GeneratedCode/AbsorbedDoseOfIonizingRadiation/UnitsNet.NanoFramework.AbsorbedDoseOfIonizingRadiation.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Acceleration/UnitsNet.NanoFramework.Acceleration.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/AmountOfSubstance/UnitsNet.NanoFramework.AmountOfSubstance.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/AmplitudeRatio/UnitsNet.NanoFramework.AmplitudeRatio.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Angle/UnitsNet.NanoFramework.Angle.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ApparentEnergy/UnitsNet.NanoFramework.ApparentEnergy.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ApparentPower/UnitsNet.NanoFramework.ApparentPower.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Area/UnitsNet.NanoFramework.Area.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/AreaDensity/UnitsNet.NanoFramework.AreaDensity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/AreaMomentOfInertia/UnitsNet.NanoFramework.AreaMomentOfInertia.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/BitRate/UnitsNet.NanoFramework.BitRate.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/BrakeSpecificFuelConsumption/UnitsNet.NanoFramework.BrakeSpecificFuelConsumption.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Capacitance/UnitsNet.NanoFramework.Capacitance.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/CoefficientOfThermalExpansion/UnitsNet.NanoFramework.CoefficientOfThermalExpansion.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Compressibility/UnitsNet.NanoFramework.Compressibility.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Density/UnitsNet.NanoFramework.Density.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Duration/UnitsNet.NanoFramework.Duration.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/DynamicViscosity/UnitsNet.NanoFramework.DynamicViscosity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricAdmittance/UnitsNet.NanoFramework.ElectricAdmittance.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricCharge/UnitsNet.NanoFramework.ElectricCharge.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricChargeDensity/UnitsNet.NanoFramework.ElectricChargeDensity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricConductance/UnitsNet.NanoFramework.ElectricConductance.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricConductivity/UnitsNet.NanoFramework.ElectricConductivity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricCurrent/UnitsNet.NanoFramework.ElectricCurrent.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricCurrentDensity/UnitsNet.NanoFramework.ElectricCurrentDensity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricCurrentGradient/UnitsNet.NanoFramework.ElectricCurrentGradient.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricField/UnitsNet.NanoFramework.ElectricField.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricInductance/UnitsNet.NanoFramework.ElectricInductance.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricPotential/UnitsNet.NanoFramework.ElectricPotential.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricPotentialAc/UnitsNet.NanoFramework.ElectricPotentialAc.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricPotentialChangeRate/UnitsNet.NanoFramework.ElectricPotentialChangeRate.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricPotentialDc/UnitsNet.NanoFramework.ElectricPotentialDc.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricResistance/UnitsNet.NanoFramework.ElectricResistance.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricResistivity/UnitsNet.NanoFramework.ElectricResistivity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ElectricSurfaceChargeDensity/UnitsNet.NanoFramework.ElectricSurfaceChargeDensity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Energy/UnitsNet.NanoFramework.Energy.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/EnergyDensity/UnitsNet.NanoFramework.EnergyDensity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Entropy/UnitsNet.NanoFramework.Entropy.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Force/UnitsNet.NanoFramework.Force.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ForceChangeRate/UnitsNet.NanoFramework.ForceChangeRate.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ForcePerLength/UnitsNet.NanoFramework.ForcePerLength.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Frequency/UnitsNet.NanoFramework.Frequency.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/FuelEfficiency/UnitsNet.NanoFramework.FuelEfficiency.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/HeatFlux/UnitsNet.NanoFramework.HeatFlux.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/HeatTransferCoefficient/UnitsNet.NanoFramework.HeatTransferCoefficient.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Illuminance/UnitsNet.NanoFramework.Illuminance.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Impulse/UnitsNet.NanoFramework.Impulse.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Information/UnitsNet.NanoFramework.Information.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Irradiance/UnitsNet.NanoFramework.Irradiance.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Irradiation/UnitsNet.NanoFramework.Irradiation.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Jerk/UnitsNet.NanoFramework.Jerk.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/KinematicViscosity/UnitsNet.NanoFramework.KinematicViscosity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/LapseRate/UnitsNet.NanoFramework.LapseRate.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/LeakRate/UnitsNet.NanoFramework.LeakRate.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Length/UnitsNet.NanoFramework.Length.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Level/UnitsNet.NanoFramework.Level.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/LinearDensity/UnitsNet.NanoFramework.LinearDensity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/LinearPowerDensity/UnitsNet.NanoFramework.LinearPowerDensity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Luminance/UnitsNet.NanoFramework.Luminance.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Luminosity/UnitsNet.NanoFramework.Luminosity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/LuminousFlux/UnitsNet.NanoFramework.LuminousFlux.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/LuminousIntensity/UnitsNet.NanoFramework.LuminousIntensity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/MagneticField/UnitsNet.NanoFramework.MagneticField.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/MagneticFlux/UnitsNet.NanoFramework.MagneticFlux.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Magnetization/UnitsNet.NanoFramework.Magnetization.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Mass/UnitsNet.NanoFramework.Mass.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/MassConcentration/UnitsNet.NanoFramework.MassConcentration.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/MassFlow/UnitsNet.NanoFramework.MassFlow.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/MassFlux/UnitsNet.NanoFramework.MassFlux.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/MassFraction/UnitsNet.NanoFramework.MassFraction.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/MassMomentOfInertia/UnitsNet.NanoFramework.MassMomentOfInertia.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Molality/UnitsNet.NanoFramework.Molality.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/MolarEnergy/UnitsNet.NanoFramework.MolarEnergy.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/MolarEntropy/UnitsNet.NanoFramework.MolarEntropy.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/MolarFlow/UnitsNet.NanoFramework.MolarFlow.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/MolarMass/UnitsNet.NanoFramework.MolarMass.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Molarity/UnitsNet.NanoFramework.Molarity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Permeability/UnitsNet.NanoFramework.Permeability.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Permittivity/UnitsNet.NanoFramework.Permittivity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/PorousMediumPermeability/UnitsNet.NanoFramework.PorousMediumPermeability.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Power/UnitsNet.NanoFramework.Power.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/PowerDensity/UnitsNet.NanoFramework.PowerDensity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/PowerRatio/UnitsNet.NanoFramework.PowerRatio.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Pressure/UnitsNet.NanoFramework.Pressure.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/PressureChangeRate/UnitsNet.NanoFramework.PressureChangeRate.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Properties/AssemblyInfo.cs
    #	UnitsNet.NanoFramework/GeneratedCode/RadiationExposure/UnitsNet.NanoFramework.RadiationExposure.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Radioactivity/UnitsNet.NanoFramework.Radioactivity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Ratio/UnitsNet.NanoFramework.Ratio.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/RatioChangeRate/UnitsNet.NanoFramework.RatioChangeRate.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ReactiveEnergy/UnitsNet.NanoFramework.ReactiveEnergy.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ReactivePower/UnitsNet.NanoFramework.ReactivePower.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ReciprocalArea/UnitsNet.NanoFramework.ReciprocalArea.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ReciprocalLength/UnitsNet.NanoFramework.ReciprocalLength.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/RelativeHumidity/UnitsNet.NanoFramework.RelativeHumidity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/RotationalAcceleration/UnitsNet.NanoFramework.RotationalAcceleration.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/RotationalSpeed/UnitsNet.NanoFramework.RotationalSpeed.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/RotationalStiffness/UnitsNet.NanoFramework.RotationalStiffness.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/RotationalStiffnessPerLength/UnitsNet.NanoFramework.RotationalStiffnessPerLength.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Scalar/UnitsNet.NanoFramework.Scalar.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/SolidAngle/UnitsNet.NanoFramework.SolidAngle.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/SpecificEnergy/UnitsNet.NanoFramework.SpecificEnergy.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/SpecificEntropy/UnitsNet.NanoFramework.SpecificEntropy.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/SpecificFuelConsumption/UnitsNet.NanoFramework.SpecificFuelConsumption.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/SpecificVolume/UnitsNet.NanoFramework.SpecificVolume.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/SpecificWeight/UnitsNet.NanoFramework.SpecificWeight.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Speed/UnitsNet.NanoFramework.Speed.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/StandardVolumeFlow/UnitsNet.NanoFramework.StandardVolumeFlow.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Temperature/UnitsNet.NanoFramework.Temperature.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/TemperatureChangeRate/UnitsNet.NanoFramework.TemperatureChangeRate.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/TemperatureDelta/UnitsNet.NanoFramework.TemperatureDelta.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/TemperatureGradient/UnitsNet.NanoFramework.TemperatureGradient.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ThermalConductivity/UnitsNet.NanoFramework.ThermalConductivity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/ThermalResistance/UnitsNet.NanoFramework.ThermalResistance.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Torque/UnitsNet.NanoFramework.Torque.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/TorquePerLength/UnitsNet.NanoFramework.TorquePerLength.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Turbidity/UnitsNet.NanoFramework.Turbidity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/VitaminA/UnitsNet.NanoFramework.VitaminA.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/Volume/UnitsNet.NanoFramework.Volume.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/VolumeConcentration/UnitsNet.NanoFramework.VolumeConcentration.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/VolumeFlow/UnitsNet.NanoFramework.VolumeFlow.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/VolumeFlowPerArea/UnitsNet.NanoFramework.VolumeFlowPerArea.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/VolumePerLength/UnitsNet.NanoFramework.VolumePerLength.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/VolumetricHeatCapacity/UnitsNet.NanoFramework.VolumetricHeatCapacity.nuspec
    #	UnitsNet.NanoFramework/GeneratedCode/WarpingMomentOfInertia/UnitsNet.NanoFramework.WarpingMomentOfInertia.nuspec
    #	UnitsNet.NumberExtensions/GeneratedCode/NumberToIrradiationExtensions.g.cs
    #	UnitsNet.NumberExtensions/GeneratedCode/NumberToVolumeFlowExtensions.g.cs
    #	UnitsNet.NumberExtensions/UnitsNet.NumberExtensions.csproj
    #	UnitsNet/CustomCode/Quantities/Area.extra.cs
    #	UnitsNet/CustomCode/Quantities/Length.extra.cs
    #	UnitsNet/CustomCode/Quantities/TemperatureDelta.extra.cs
    #	UnitsNet/GeneratedCode/Quantities/BitRate.g.cs
    #	UnitsNet/GeneratedCode/Quantities/Information.g.cs
    #	UnitsNet/GeneratedCode/Quantities/Power.g.cs
    #	UnitsNet/UnitsNet.csproj

commit 0eb58d3
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Thu Apr 4 18:41:05 2024 +0200

    👔Change nuget license from MIT to MIT-0 (angularsen#1381)

    Fixes angularsen#1379
    Backport from v6: angularsen#1380

    Change the license expression in the nugets to match the LICENSE file.
    Previously, nuget.org did not accept MIT-0 expression.

    ---------

    Co-authored-by: Muximize <[email protected]>

commit 3f71f26
Author: Travis Bement <[email protected]>
Date:   Thu Apr 4 12:38:00 2024 -0400

    Add operators for ReciprocalLength/-Area (v6) (angularsen#1385)

    Added additional operators that result in ReciprocalLength (Length/Area,
    Area/Volume) and ReciprocalArea (Length/Volume)

    (Mirrors functionality of v5 PR angularsen#1382)

    ---------

    Co-authored-by: Travis Bement <[email protected]>
    Co-authored-by: Andreas Gullberg Larsen <[email protected]>

commit 1b87682
Author: Travis Bement <[email protected]>
Date:   Thu Apr 4 12:27:32 2024 -0400

    Add operators for ReciprocalLength/-Area (angularsen#1382)

    Added additional operators that result in `ReciprocalLength`
    (Length/Area, Area/Volume) and `ReciprocalArea` (Length/Volume)

    Co-authored-by: Travis Bement <[email protected]>

commit b9b4365
Author: Muximize <[email protected]>
Date:   Thu Apr 4 18:24:11 2024 +0200

    Update Nuget dependencies (angularsen#1384)

commit 3b79a65
Author: Muximize <[email protected]>
Date:   Thu Apr 4 18:22:17 2024 +0200

    Change license from MIT to MIT-0 in NuspecGenerator (angularsen#1383)

commit 6c5d264
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Thu Mar 28 00:34:35 2024 +0100

    JsonNet: 6.0.0-pre005

commit 1536509
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Thu Mar 28 00:34:28 2024 +0100

    UnitsNet: 6.0.0-pre005

commit dc7d53c
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Thu Mar 28 00:33:41 2024 +0100

    👔Change nuget license from MIT to MIT-0 (angularsen#1380)

    Fixes angularsen#1379

    Change the license expression in the nugets to match the LICENSE file.
    Previously, nuget.org did not accept MIT-0 expression.

commit 8519cb1
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Tue Mar 12 20:23:37 2024 +0100

    JsonNet: 6.0.0-pre004

commit 28c534e
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Tue Mar 12 20:23:31 2024 +0100

    UnitsNet: 6.0.0-pre004

commit 6c4faa9
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Tue Mar 12 20:21:13 2024 +0100

    UnitsNet: 5.49.0

commit 1b47e7d
Author: Muximize <[email protected]>
Date:   Tue Mar 12 20:20:17 2024 +0100

    Upgrade to .NET 8 (angularsen#1375)

    As we're doing breaking changes in v6, it might be worth upgrading to the current [Long Term Support](https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core) version of dotnet.

    This enables us to use C# 12 features and remove a bunch of conditional compilation (net 5328 deletions)

commit 6ab5f21
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Sun Mar 3 20:45:46 2024 +0100

    JsonNet: 6.0.0-pre003

commit 0413634
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Sun Mar 3 20:45:41 2024 +0100

    UnitsNet: 6.0.0-pre003

commit 6a9ae6c
Author: UrielZyx <[email protected]>
Date:   Sun Mar 3 21:44:35 2024 +0200

    Add type safety and improve type inference (angularsen#1374)

    Some of the extension methods in `UnitMath.cs` (e.g. Average) take an
    `Enum unitType` argument.
    The compiler should be able to detect when the units type doesn't match
    the quantity type.

    * This will break backwards compatibility, but only for:
      * People doing really weird things.
    * People using the explicit generic type instead of inference (e.g.
    `Average<Length>(LengthUnit.Inch)`), in which case they can fix the code
    by changing it to e.g. `Average<Length, LengthUnit>(LengthUnit.Inch)`
    * This change might be warranted in `UnitConverter.cs` as well, but
    can't be implemented as a straight-forward refactor since it breaks
    compatibility in generated code (e.g.
    `unitConverter.SetConversionFunction<ElectricPotential>` in
    `ElectricPotential` should be `SetConversionFunction<ElectricPotential,
    ElectricPotentialUnit>`

    In addition, had to remove a few unit tests that were asserting type
    safety.
    All tests that were removed:
    * Were only testing that an incorrect behavior throws an exception
    (`Assert.Throws`)
    * Don't compile after the changes in `UnitMath.cs`

commit aa61d5a
Author: Tim-Borcherding <[email protected]>
Date:   Fri Mar 1 21:23:27 2024 +0100

    Add TemperatureDelta / Duration = TemperatureChangeRate (angularsen#1370)

    Added missing operator to divide a TemperatureDelta by a time/duration
    to get the TemperatureChangeRate.

    ---------

    Co-authored-by: Andreas Gullberg Larsen <[email protected]>

commit d389213
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Tue Feb 27 13:44:34 2024 +0100

    UnitsNet: 5.48.0

commit ace4fb4
Author: José Simões <[email protected]>
Date:   Tue Feb 27 12:42:49 2024 +0000

    Add MagenticField and Acceleration for .NET nanoFramework (angularsen#1369)

commit 4159603
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Sat Feb 24 00:04:57 2024 +0100

    UnitsNet: 5.47.0

commit 7f2a39e
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Fri Feb 23 23:11:12 2024 +0100

    README: Link to 6.x upgrade guide

commit 1b3647f
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Sun Feb 18 17:54:23 2024 +0100

    nano: ✏️ Fix xmldoc of MaxValue, MinValue

commit 3a2a6e8
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Sun Feb 18 15:31:56 2024 +0100

    UnitsNet: 5.46.0-pre

commit 91d8969
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Sun Feb 18 15:30:53 2024 +0100

    fixup! Add Btu/ft² in irradiation (angularsen#1364)

    Fix remaining after rename.

commit ff39807
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Sun Feb 18 15:28:52 2024 +0100

    Regen

commit 3b74e19
Author: Piotr Stenke <[email protected]>
Date:   Sat Feb 17 17:28:48 2024 +0100

    Add radiation equivalent dose (angularsen#1352)

    Added radiation equivalent dose and its two units - Sievert (SI-derived)
    and Roentgen equivalent man (or, simply, rem).

    Sievert comes with three prefixes - milli, micro, nano. This will
    support 99% of usecases, ranging from radiation caused by Earth itself
    (~6 to 83nSv/h, per this
    [article](https://www.epa.gov/radnet/about-exposure-and-dose-rates)),
    [eating bananas](https://en.wikipedia.org/wiki/Banana_equivalent_dose)
    (yep, that's a real unit), X-rays or tomography scans, as well as
    measuring health risk of radiation-induced cancer and radiation
    sickness.

    Roentgen equivalent man is mostly used in the US, and it can be easily
    converted to sieverts (1 sievert = 100 rems). It includes only one
    prefix - milli - as I did not find any sources with other prefixes being
    used.

    Wikipedia pages:

    https://en.wikipedia.org/wiki/Equivalent_dose
    https://en.wikipedia.org/wiki/Sievert
    https://en.wikipedia.org/wiki/Roentgen_equivalent_man
    https://en.wikipedia.org/wiki/Orders_of_magnitude_(radiation)

    ---------

    Co-authored-by: Piotr Stenke <[email protected]>

commit 85aec1b
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Sat Feb 17 14:39:58 2024 +0100

    Regen

commit b1f88bf
Author: Mingbo Peng <[email protected]>
Date:   Sat Feb 17 21:39:24 2024 +0800

    Add Btu/ft² in irradiation (angularsen#1364)

    Added a new unit Btu/ft² in irradiation. This is commonly used in
    building industry.

    ---------

    Co-authored-by: Andreas Gullberg Larsen <[email protected]>

commit 545c8b9
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Wed Feb 14 07:39:49 2024 +0100

    UnitsNet: 5.45.0-pre

commit c23cf15
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Wed Feb 14 07:37:45 2024 +0100

    UnitsNet: UnitsNet/5.45.0-pre

commit d980ec1
Author: Andre Rodi <[email protected]>
Date:   Wed Feb 14 07:35:01 2024 +0100

    Add VolumeFlow prefixes Deca, Hecto (angularsen#1362)

    Fixes angularsen#1253

    Added the prefixes 'deca' and 'hecto', since, at least for the 'hecto'
    prefix, it's a very common unit used in the brewing industry. Added the
    'deca' prefix while I was at it.

    ---------

    Co-authored-by: André Rodi <[email protected]>

commit 6604660
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Wed Feb 14 07:27:54 2024 +0100

    UnitsNet: 5.44.0-pre

    Pre-release of angularsen#1363

commit aa2a743
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Wed Feb 14 07:26:22 2024 +0100

    💥🐛Change DataMember ordering to 1-indexed (angularsen#1360) (angularsen#1363)

    Related angularsen#1200
    Fixes angularsen#1356

    Apparently, protobuf-net does not support 0-indexed order values.

    - Change `DataMember` explicit order from 0-indexed to 1-indexed

    Some investigation indicates that the actual order value is not
    important, only the relative ordering:
    angularsen#1356 (comment)

    - WCF should tolerate this, according to its docs.
    angularsen#1356 (comment)
    - Binary formatters hopefully only care about relative ordering and thus
    still compatible?
    	- Protobuf-net, it never worked and should be OK
    -
    [BinaryFormatter](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter?view=net-8.0)
    is obsolete and generally not recommended for years, so hopefully no
    users are affected
    	- A bunch of others exist too, haven't looked into how they handle this
    - XML/JSON serializers should tolerate any order, can't imagine it
    breaking these?

commit e86eb17
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Tue Jan 23 21:47:01 2024 +0100

    UnitsNet: 5.43.0

commit 6ffa8b1
Author: Andreas Gullberg Larsen <[email protected]>
Date:   Tue Jan 23 21:46:35 2024 +0100

    🐛Fix precision of Volume.CubicInch (angularsen#1358)

    Fixes angularsen#1357

    An imprecise value was used, fixed by using the definition of inch as 2.54e-2 per meter.
    Verified that the related unit CubicFoot is already precise.
@angularsen angularsen pinned this issue Jul 8, 2024
@angularsen angularsen added the pinned Issues that should not be auto-closed due to inactivity. label Jul 8, 2024
@lipchev
Copy link
Collaborator

lipchev commented Sep 24, 2024

MassMomentOfInertia.json has the unit TonneSquareMilimeter (with as single "l")

PS I'm surprised this is the only one- I often miss the second "l" as well.. Should I we add it for v6?

@lipchev
Copy link
Collaborator

lipchev commented Sep 24, 2024

In #721 it was (I think correctly) suggested that the ThermalResistance.json should be renamed to ThermalInsulance, what do you think about it? That would open up the "name" for the actual quantity (which I think was never added just because of the naming collision). There looks to be a nice triangle of relations that could be formed between these (also there are also the Thermal Conductivity/Resistivity etc..).

@lipchev
Copy link
Collaborator

lipchev commented Sep 24, 2024

Finally, I think we should change the BaseUnit of the Angle from Degrees to Radians (as per the SI definition), but more importantly, since SI doesn't have a base dimension for the Angle it relies on the fact that:

the SI radian is a dimensionless unit equal to 1. In SI 2019, the SI radian is defined accordingly as 1 rad = 1.

This is similar to how the Ratio or the MassFraction have their BaseUnit to the DecimalFraction (i.e. the "unit-less 1").

This would enable (and speed up) the "generic-conversions" with all other Rotational quantities (which are all based on the Radian).

@lipchev
Copy link
Collaborator

lipchev commented Sep 24, 2024

Ok, one more: what do you think about removing the TorquePerLength (added in #712 ) - I've been looking for half an hour already, and cannot find a single reference to any of it's units on the internet. The only document that was found using the XmlDocSummary:

"The magnitude of torque per unit length."

Was found on this page, where they say that

The torque coefficient is a dimensionless physical quantity that represents the magnitude of torque per unit length.

And down below there's a table with unit-less values.
Finally, there it isn't even possible to have a Torque / Length operator, as it is already taken (resulting in Force).

@dschuermans Is this quantity still useful to you? Do you think this is actually used by anyone else?

@dschuermans
Copy link
Contributor

@lipchev i'm no longer working at that company, so I honestly don't care 😅
I don't think they'll be able to upgrade to v6 anyway with the breaking change in equality comparison (1m != 100cm) so meh 🤷

@angularsen
Copy link
Owner Author

angularsen commented Sep 29, 2024

I agree with all above suggestions.

Finally, I think we should change the BaseUnit of the Angle from Degrees to Radians (as per the SI definition)

Sure, we can do that. I think it's fairly safe to change base unit since it should be considered an implementation detail, although I'm not sure I see a huge benefit here.

I'll add these todo items to the top description:

  • Fix typo in TonneSquareMilimeter, missing l
  • Remove TorquePerLength: dimensionless, domain specific, conflicts with Force = Torque / Length
  • Rename ThermalResistance to ThermalInsulance: enables future conversions between thermal conductivity, resistivity if added
  • Change Angle base unit from Degree to Radian

@lipchev
Copy link
Collaborator

lipchev commented Oct 4, 2024

I'd like to also suggest a few modifications to the operators / UnitRelations.json:

double = SpecificEnergy.JoulePerKilogram * BrakeSpecificFuelConsumption.KilogramPerJoule

Here's an interpretation from the AI about it:

The result is a dimensionless value, which is effectively a conversion efficiency factor. It represents how efficiently the energy in the fuel is being converted into useful work (in terms of specific energy and fuel consumption).

If the result equals 1, it indicates a perfect match between the energy content of the fuel and the amount consumed to produce energy. If the result is less than 1, it could indicate some inefficiency or losses in the energy conversion process.

I would therefore suggest that we change it accordingly:

  • Ratio.DecimalFraction = SpecificEnergy.JoulePerKilogram * BrakeSpecificFuelConsumption.KilogramPerJoule -- NoInferredDivision

I think the same reasoning would also apply to:
double = TemperatureDelta.Kelvin * CoefficientOfThermalExpansion.PerKelvin

The result is dimensionless and represents the relative expansion or contraction of a material when its temperature changes by a given amount.

  • If the result is 0.01, for example, it means that the material expands (or contracts) by 1% of its original size due to the given temperature change.
  • This result could be used to calculate the actual size change of an object by multiplying this coefficient by its initial size (e.g., length, volume).
  • Ratio.DecimalFraction = TemperatureDelta.Kelvin * CoefficientOfThermalExpansion.PerKelvin -- NoInferredDivision

@lipchev
Copy link
Collaborator

lipchev commented Oct 4, 2024

And finally, I think that instead of
double = Density.KilogramPerCubicMeter * SpecificVolume.CubicMeterPerKilogram
we should have an inverse relationship:
1 = Density.KilogramPerCubicMeter * SpecificVolume.CubicMeterPerKilogram

"XmlDocSummary": "In thermodynamics, the specific volume of a substance is the ratio of the substance's volume to its mass. It is the reciprocal of density and an intrinsic property of matter as well.",

This would remove the last of the doubles from the UnitRelations.json.

PS What do you think about having an implicit operator for these relationships:

"1 = Area.SquareMeter * ReciprocalArea.InverseSquareMeter",
"1 = Density.KilogramPerCubicMeter * SpecificVolume.CubicMeterPerKilogram",
"1 = ElectricResistivity.OhmMeter * ElectricConductivity.SiemensPerMeter",
"1 = Length.Meter * ReciprocalLength.InverseMeter",

I don't there is anything stopping us from having:
var length = Length.FromMeters(10);
ReciprocalLength inverseOfLength = length; // same as calling length.Inverse()

@tm-ren
Copy link
Contributor

tm-ren commented Oct 4, 2024

I'd like to also suggest a few modifications to the operators / UnitRelations.json:

double = SpecificEnergy.JoulePerKilogram * BrakeSpecificFuelConsumption.KilogramPerJoule

Here's an interpretation from the AI about it:

The result is a dimensionless value, which is effectively a conversion efficiency factor. It represents how efficiently the energy in the fuel is being converted into useful work (in terms of specific energy and fuel consumption).
If the result equals 1, it indicates a perfect match between the energy content of the fuel and the amount consumed to produce energy. If the result is less than 1, it could indicate some inefficiency or losses in the energy conversion process.

I would therefore suggest that we change it accordingly:

  • Ratio.DecimalFraction = SpecificEnergy.JoulePerKilogram * BrakeSpecificFuelConsumption.KilogramPerJoule -- NoInferredDivision

I think the same reasoning would also apply to: double = TemperatureDelta.Kelvin * CoefficientOfThermalExpansion.PerKelvin

The result is dimensionless and represents the relative expansion or contraction of a material when its temperature changes by a given amount.

  • If the result is 0.01, for example, it means that the material expands (or contracts) by 1% of its original size due to the given temperature change.
  • This result could be used to calculate the actual size change of an object by multiplying this coefficient by its initial size (e.g., length, volume).
  • Ratio.DecimalFraction = TemperatureDelta.Kelvin * CoefficientOfThermalExpansion.PerKelvin -- NoInferredDivision

My only comment on the above is consistency throughout the project.
For example, currently Length / Length is a double not a Ratio (it could be an Angle which is also technically a ratio.)
The use of the Ratio type is not particularly consistent throughout the project.
Why/when would you use double over ratio or vice versa?

@lipchev
Copy link
Collaborator

lipchev commented Oct 4, 2024

@tm-ren I agree, personally I'd say that all of these should return a Ratio, but that would be much more impactful of a change..

On the other hand, I actually consider the operations like Q3 Divide<Q1, Q2, Q3>(Q1 left, Q2 right), and Q2 Convert<Q1, Q2>(Q1 from) as being part of the "relations" (or the custom mapping functions), which I really want to constrain to operate on descendants of IQuantity..

@lipchev
Copy link
Collaborator

lipchev commented Oct 4, 2024

@tm-ren I agree, personally I'd say that all of these should return a Ratio, but that would be much more impactful of a change..

Although, this would require also replacing the * operators with double (or QuantityValue).. Note however that, there is a very natural implicit conversion between my fraction-based QuantityValue and the Ratio..

@lipchev
Copy link
Collaborator

lipchev commented Oct 4, 2024

I'd like to also suggest a few modifications to the operators / UnitRelations.json:

double = SpecificEnergy.JoulePerKilogram * BrakeSpecificFuelConsumption.KilogramPerJoule

Here's an interpretation from the AI about it:

The result is a dimensionless value, which is effectively a conversion efficiency factor. It represents how efficiently the energy in the fuel is being converted into useful work (in terms of specific energy and fuel consumption).
If the result equals 1, it indicates a perfect match between the energy content of the fuel and the amount consumed to produce energy. If the result is less than 1, it could indicate some inefficiency or losses in the energy conversion process.

I would therefore suggest that we change it accordingly:

* [ ]    `Ratio.DecimalFraction = SpecificEnergy.JoulePerKilogram * BrakeSpecificFuelConsumption.KilogramPerJoule -- NoInferredDivision`

Oh, I just realized that this isn't a straightforward Ratio- the direct multiplication actually represents:

a scalar, which reflects how much energy is used in terms of fuel consumption per unit energy produced.

In most cases, what you’re really interested in is efficiency, i.e., how well fuel energy is converted into useful work. If you multiply these two values directly and get something like 8800, you’ve got an inverse efficiency in a sense because it’s showing how much extra energy is consumed compared to what you’re inputting.

To get an efficiency measure, you can invert this result to express it as a ratio (or percentage) of how much of the fuel’s energy is being used efficiently:

Efficiency=1 / Result

However this type of relationship isn't directly supported with the UnitRelations.json, and I'm not even sure that the "efficiency ratio" should be the default result here..

The direct fuel energy factor represents how much energy you get for a given amount of fuel. It could be used in calculations where you’re trying to scale fuel consumption based on the required energy output. This would be more useful in applications where you need to estimate fuel usage rather than focusing on efficiency directly.

The tests are called BrakeSpecificFuelConsumptionTimesSpecificEnergyEqualsEnergy, but that should probably be something like BrakeSpecificFuelConsumptionTimesSpecificEnergyEqualsEnergyFactor.

If we were to stick with IQuantity - then perhaps this should instead be defined as:

  • Scalar.Amount = SpecificEnergy.JoulePerKilogram * BrakeSpecificFuelConsumption.KilogramPerJoule

Alternatively we could keep it as it is, and I'll just ignore it in the QuantityConverter..

@lipchev
Copy link
Collaborator

lipchev commented Oct 15, 2024

@angularsen What would you say about adding the System.Runtime.CompilerServices.Unsafe package for netstandard2.0? It's dll size is only 18 KB, version 6.0.0 hasn't changed since 2021 and this would give a significant boost in performance (and remove some #if NET .. #else.. optimizations I've got setup at the moment).
I haven't got the final size of my proposal for UnitsNet v6 as I'm still keeping some of the old code (for testing), but I can guarantee an overall reduction in size (we're talking a lot more than 100K)..

@ebfortin
Copy link
Contributor

ebfortin commented Oct 15, 2024 via email

@angularsen
Copy link
Owner Author

@lipchev Can you explain like I'm 5, please? 😅

How would we use this package to save binary size?
This is not needed for net8.0 target then, I assume?

@lipchev
Copy link
Collaborator

lipchev commented Nov 11, 2024

@lipchev Can you explain like I'm 5, please? 😅

How would we use this package to save binary size? This is not needed for net8.0 target then, I assume?

@angularsen This isn't about reducing the binary size, in fact the dependency would add the System.Runtime.CompilerServices.Unsafe.dll (which is 18 KB) to the netstandard release only, but note that the nuget is fairly popular, and is often referenced by other libraries / larger projects) (the Fractions.dll, if included is another 45KB).

The reduction in size I was talking about comes from other means, notably removing as much code-duplication as possible (mostly using extension methods and exception helpers). I have the still-unreleased v6 with a lot more "actual code" than before down to 2 070 528 bytes (the v5 shows as 2 343 936 bytes). Its a shame that the extension properties didn't get released in this cycle, that would have had a huge impact on the size of the core library...

As for the benefits of the nuget itself, well.. other than the Span<T> which should definitely come in handy when optimizing the the string-related stuff, there are a few operations in the Unsafe class which would significantly reduce the amount of allocations we get from working with the T: Enum method (which is a lot).

Here are a few benchmarks to showcase the difference:


[ShortRunJob(RuntimeMoniker.Net48)]
[ShortRunJob(RuntimeMoniker.Net80)]
public class EnumToIntegerBenchmarks
{
    private static readonly MassUnit Unit = MassUnit.Gram;

    private static readonly int NbIterations = 500;

    [Benchmark(Baseline = true)]
    public int ConvertToInt32()
    {
        var unit = Unit;
        var total = 0;
        for (var i = 0; i < NbIterations; i++)
        {
            total += Convert.ToInt32(unit);
        }

        return total;
    }

    [Benchmark(Baseline = false)]
    public int ConvertWithCast()
    {
        var unit = Unit;
        var total = 0;
        for (var i = 0; i < NbIterations; i++)
        {
            total += (int)(unit);
        }

        return total;
    }

    // #if NET
    [Benchmark(Baseline = false)]
    public int ConvertWithUnsafe()
    {
        var unit = Unit;
        var total = 0;
        for (var i = 0; i < NbIterations; i++)
        {
            total += (Unsafe.As<MassUnit, int>(ref unit));
        }
    
        return total;
    }
    // #endif
}

And here are the results:

Method Job Runtime Mean Error StdDev Ratio
ConvertToInt32 ShortRun-.NET 8.0 .NET 8.0 3,332.85 ns 251.935 ns 13.809 ns 1.00
ConvertWithCast ShortRun-.NET 8.0 .NET 8.0 98.14 ns 1.980 ns 0.109 ns 0.03
ConvertWithUnsafe ShortRun-.NET 8.0 .NET 8.0 98.04 ns 2.834 ns 0.155 ns 0.03
ConvertToInt32 ShortRun-.NET Framework 4.8 .NET Framework 4.8 9,150.37 ns 1,283.718 ns 70.365 ns 1.00
ConvertWithCast ShortRun-.NET Framework 4.8 .NET Framework 4.8 98.38 ns 0.504 ns 0.028 ns 0.01
ConvertWithUnsafe ShortRun-.NET Framework 4.8 .NET Framework 4.8 98.60 ns 0.909 ns 0.050 ns 0.01

For the already boxed Enum there is also the Unsafe.Unbox<int>(unit) (which although better than the ConvertTo version, ended up having the same performance as (int)(object)unit:

Method Job Runtime Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
ConvertToInt32 .NET 8.0 .NET 8.0 2.775 μs 0.0364 μs 0.0340 μs 1.00 0.02 0.7172 12000 B 1.00
ConvertWithCast .NET 8.0 .NET 8.0 1.694 μs 0.0069 μs 0.0065 μs 0.61 0.01 - - 0.00
ConvertWithUnsafe .NET 8.0 .NET 8.0 1.694 μs 0.0088 μs 0.0082 μs 0.61 0.01 - - 0.00
ConvertToInt32 .NET Framework 4.8 .NET Framework 4.8 9.228 μs 0.0652 μs 0.0610 μs 1.00 0.01 1.9073 12035 B 1.00
ConvertWithCast .NET Framework 4.8 .NET Framework 4.8 3.402 μs 0.0252 μs 0.0236 μs 0.37 0.00 - - 0.00
ConvertWithUnsafe .NET Framework 4.8 .NET Framework 4.8 3.417 μs 0.0175 μs 0.0164 μs 0.37 0.00 - - 0.00

Funny enough, on net48, using an "Unsafe" implementation of a UnitEqualityComparer<TUnit> (that casts TUnit to int) has the exact performance as the == operator, which is 5x faster than the default equality comparer: EqualityComparer<MassUnit>.Default.Equals(unit, targetUnit).

@lipchev
Copy link
Collaborator

lipchev commented Nov 11, 2024

I also have a bit of an issue with GenericMathExtensions- I'd like to add IQuantity to the constraint, as the way it stands- the constraint is also a match for an array of integers, which have the same prototype in System.Linq.Enumerable.. I don't think we'd want anybody depending on UnitsNet for that kind of sums..
Also, there is slight performance gain to be had from converting the aggregation with an iterator based implementation:

        using IEnumerator<T> e = source.GetEnumerator();
        if (!e.MoveNext())
        {
            return T.AdditiveIdentity;
        }

        T result = e.Current;
        while (e.MoveNext())
        {
            result += e.Current;
        }

        return result;
Method NbOperations Mean Error StdDev Ratio Gen0 Allocated Alloc Ratio
SumOfVolumes 10 548.3 ns 2.30 ns 2.04 ns 1.00 0.0277 464 B 1.00
SumOfVolumesWithIterator 10 446.9 ns 1.98 ns 1.75 ns 0.82 0.0277 464 B 1.00
SumOfVolumes 100 5,994.4 ns 40.33 ns 35.75 ns 1.00 0.2670 4544 B 1.00
SumOfVolumesWithIterator 100 5,080.8 ns 17.51 ns 15.53 ns 0.85 0.2670 4592 B 1.01
SumOfVolumes 1000 61,025.7 ns 238.20 ns 222.82 ns 1.00 2.6855 45392 B 1.00
SumOfVolumesWithIterator 1000 55,652.2 ns 274.11 ns 242.99 ns 0.91 2.7466 46256 B 1.02

There is also a potential for misuse of the UnitMath extensions if passed a list of the "logarithmic" quantities such as PowerRatio and AmplitudeRatio. I believe we've already talked about the operations with Temperature before (which should not be acceptable for the Sum method, but would be fine to Average)...

I've been thinking about splitting the IArithmeticQuantity interface into 3 sub-types:

  1. IAffineArithmeticQuantity - the standard stuff, contains the Zero (IAdditiveIdentity)
  2. ILogarithmicArithmeticQuantity - this can have a LogBase, not sure about whether there should be a Zero - but in any way, I don't think we're allowed to use an IAdditiveIdentity of 0 ,
  3. INonAffineArithmeticQuantity - this wouldn't have an explicit Zero but can have an IAdditionOperators<TSelf, TOffet, TSelf> (with the Temperature being the only implementation I can think of right now)

With these 3 interfaces we can have safe implementations for the extension methods, even without the INumber interface (we could keep the non-specific IQuantity overload and switch-cast the input).

And while we're on the subject, I'd really love to remove the Zero property from the QuantityInfo - which I don't think is very useful (especially given that it doesn't really apply to all types).. If you want, we could even have a some "ArithmeticCategory" enum in its stead..

@angularsen
Copy link
Owner Author

angularsen commented Nov 12, 2024

Unsafe package

I'm fine adding this to netstandard2.0 if it does significantly help in some common use cases, but I don't see the performance gain in the benchmarks? It seems the mean is pretty much equivalent for ConvertWithUnsafe and ConvertWithCast, or did I miss it?

GenericMathExtensions

I'd like to add IQuantity to the constraint

Sounds good to me, if it supports adding numbers I think that is an oversight more than a conscious decision. It's been a long while and I don't recall the details anymore, but these extensions are still experimental in my mind and improvements are welcome.

I've been thinking about splitting the IArithmeticQuantity interface into 3 sub-types:

Yes, we probably need to introduce something like this to better support generic arithmetic.
On the surface this proposal looks good, but the design should be battle tested by someone actually needing generic math. For me, it's a theoretic exercise as I have never really need this feature myself for anything so I can't really contribute much in this direction. I'm happy to help review it, if you want to take a stab at it.

I'd really love to remove the Zero property from the QuantityInfo

I actually find it useful, since it works for most quantities. However, if we introduce more granular arithmetic interfaces as you propose, then I guess it can make sense to rethink this and only offer Zero where appropriate 👍

@lipchev
Copy link
Collaborator

lipchev commented Nov 12, 2024

@angularsen
Are you ok with renaming the IArithmeticQuantity to IAffineQuantity or would you rather we keep it (mark it as obsolete)? For the other two interfaces I was thinking of INonAffineQuantity and ILogarithmicQuantity with the latter having an additional read-only property called LogarithmicScalingFactor (this could be static for net8.0, but for netstandard it would have to be non-static for the extensions to work). Also, I previously mentioned having a LogBase but I guess we could start with the base-10 assumption and extend it later if needed.

Also, last night I realized that there isn't any point in having the Min/Max methods that take the extra Unit parameter- there isn't any good reason to use them (performance-wise), instead of the default Min/Max extensions from System.Linq (I must have just copy pasted the code from the Sum extensions when I first added them). I think you'll agree that these can be deprecated with "Duplicate of System.Linq.Min/Max".

@lipchev
Copy link
Collaborator

lipchev commented Nov 12, 2024

@angularsen Are you ok with renaming the IArithmeticQuantity to IAffineQuantity or would you rather we keep it (mark it as obsolete)? For the other two interfaces I was thinking of INonAffineQuantity and ILogarithmicQuantity with the latter having an additional read-only property called LogarithmicScalingFactor (this could be static for net8.0, but for netstandard it would have to be non-static for the extensions to work). Also, I previously mentioned having a LogBase but I guess we could start with the base-10 assumption and extend it later if needed.

Oh, there is a good explanation (and a better naming IMO) here:

  • Vector quantities may be added to each other, yielding a new vector quantity, and a vector quantity may be added to a suitable affine quantity (a vector space acts on an affine space), yielding a new affine quantity.
  • Affine quantities cannot be added, but may be subtracted, yielding relative quantities which are vectors, and these relative differences may then be added to each other or to an affine quantity.

So, I guess the proper interface would be IVectorQuantity or IVectorialQuantity if we want to be fancy :)

@angularsen
Copy link
Owner Author

angularsen commented Nov 12, 2024

Great article! It perfectly captures the confusion we've had with Temperature in the past. The term affine is unfamiliar to me and I suspect to most people. I'm all for following well defined terms and concepts on this, but I also want to ensure the library stays easy to use and understand. Ideally, most people won't ever have to know about affine vs vectorial to be able to use our Temperature quantity correctly and intuitively.

Then again, most are not using the interfaces anyway since it's usually only relevant when dealing with quantities generically, so maybe it's not a big deal.

At first glance, IAffineQuantity and IVectorQuantity seems like good names based on the article, plus the ILogarithmicQuantity you proposed. Maybe IVectorialQuantity is a more consistent naming along with the other two though. Then again, in the article and on google it seems they mostly talk about "affine space" vs "vector space", not anything vectorial. 🤷

Are you ok with renaming the IArithmeticQuantity to IAffineQuantity or would you rather we keep it (mark it as obsolete)?

Let's avoid breaking changes where we can, keep it and mark it obsolete. We can remove it later, as long as it doesn't add too much friction for us in the meantime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pinned Issues that should not be auto-closed due to inactivity.
Projects
None yet
Development

No branches or pull requests

8 participants