目录
介绍
这个项目的最初灵感来自1999年美国宇航局火星气候轨道飞行器的损失。由于公制(SI)和美国习惯单位之间的混淆,它未能进入火星轨道。一个子系统向另一个子系统提供以磅力秒为单位的测量值,期望它们以牛顿秒为单位。当探测器刹车进入轨道时,它离行星大气层太近,要么燃烧殆尽,要么弹入太阳轨道。
因此,我尝试构建一个代码库,其中应通过设计排除此类错误。它具有以下功能:
- 它可用于执行物理和工程的许多标准计算。
- 它基于量纲分析,因此所有量都有相应的物理维度,例如长度或质量。
- 它是强类型的,因此不同维度的数量只能以科学有效的方式组合。
- 在内部,所有值都以S.I.(公制)单位存储。
- 值仅在其外部接口处转换为特定的单位系统,例如,在与string之间转换时。
它是使用C#版本9编写的,并利用.NET 5.0框架。
下面是正在使用的库的示例:
// Tsiolkovsky rocket equation
Mass EmptyMass = 120000.Kilograms();
Mass PropellantMass = 1200000.Kilograms();
Mass WetMass = EmptyMass + PropellantMass;
Velocity ExhaustVelocity = 3700.MetresPerSecond();
Velocity DeltaV = ExhaustVelocity * Math.Log(WetMass / EmptyMass);
// DeltaV = 8872.21251 m/s
在整篇文章中,以及在示例代码和单元测试中,我使用了我的旧文法学校物理教科书中的示例——Nelkon and Parker Advanced Level Physics。这是整个六七十年代英国标准的六年级物理书。
背景
该库基于维度和单位的概念。
维度
物理量的维数决定了它如何与一组基本量(如质量、长度和时间)相关。这些通常缩写为M、L、T等。通过使用乘法和除法将这些基本维度组合起来,可以得出新的维度。所以:
- 面积 = 长度 x 长度 = L²
- 体积 = 长度 x 长度 x 长度 = L³
- 密度 = 质量 / 体积 = M/L³ = ML⁻³
- 速度 = 长度 / 时间 = L/T = LT⁻¹
- 加速度 = 速度 / 时间 = LT⁻²
- 力 = 质量 * 加速度 = MLT⁻²
等等。
任何特定量的维数都可以表示为基本维数的幂序列(例如,上面的力 = MLT⁻²)。如果数量的维度不匹配,则尝试添加或减去数量是无效的。因此,例如,向体积添加质量是无效的。
国际单位制(S.I.)使用以下基本维度:
维度 | 符合 | 单位 | 单位符号 |
质量 | M | 千克 | kg |
长度 | L | 米 | m |
时间 | T | 秒 | s |
电流 | I | 安培 | A |
热力学温度 | Θ | 开尔文 | K |
物质量 | N | 摩尔 | mol |
发光强度 | J | 坎德拉 | cd |
该库定义了这些基本维度,以及许多派生维度。
单位
单位系统可以定义不同的基本单位以对应于各种维度。因此,虽然S.I.系统有一个千克单位的质量单位,但美国和英国的系统使用英镑。同样,我们用英尺代替米作为长度单位。在体积测量方面,美国和英国的系统也存在差异。值得庆幸的是,其他基本尺寸的单位在所有三个系统中都是相同的。
尽管该库对S.I.、美国和英国系统都有定义,但可以创建和使用新的系统。例如,您可以使用日本的shakkanho系统创建一个系统,将shaku(尺)作为长度单位,将kan(貫)作为质量单位。
使用代码
附加的ZIP中提供的代码由一个Visual Studio解决方案和两个项目组成:库本身和一个用于测试和演示库功能的命令行程序。若要在您自己的项目中使用该库,请在“\KMB.Library.Units\KMB.Library.Units.csproj“,然后将以下using语句添加到代码中:
using KMB.Library.Units;
using KMB.Library.Units.Metric;
using KMB.Library.Units.TimeUnits; // for hours, minutes etc.
using KMB.Library.Units.British; // For feet and pounds. Or use USA if you prefer
库的内容
该Units库定义了各种类和接口。这里讨论主要的:
类维度
此类用于表示物理维度或它们的组合。它有一个只读字段,用于显示每个维度的幂:
public readonly short M; // Mass
public readonly short L; // Length
public readonly short T; // Time
public readonly short I; // Current
public readonly short Θ; // Temperature
public readonly short N; // Amount of Substance
public readonly short J; // Luminous Intensity
public readonly short A; // Angle.
记下角度的值。严格的角度来看,角度是无量纲的,但将它们视为具有不同的维度是很方便的。这样,我们可以区分角度和无量纲量,例如,当转换为string时。
该类具有各种构造函数,并且还定义了乘法和除法的运算符:
public static Dimensions operator *(Dimensions d1, Dimensions d2)...
public static Dimensions operator /(Dimensions d1, Dimensions d2)...
使用此类,我们可以定义基本维度:
public static readonly Dimensions Dimensionless = new Dimensions(0, 0, 0);
public static readonly Dimensions Mass = new Dimensions(1, 0, 0);
public static readonly Dimensions Length = new Dimensions(0, 1, 0);
public static readonly Dimensions Time = new Dimensions(0, 0, 1);
:
并定义任何派生维度:
public static readonly Dimensions Area = Length * Length;
public static readonly Dimensions Volume = Area * Length;
public static readonly Dimensions Density = Mass / Volume;
public static readonly Dimensions Velocity = Length / Time;
public static readonly Dimensions AngularVelocity = Angle / Time;
:
Dimensions的重载ToString()方法输出每个维度的幂:
Dimensions.Pressure.ToString() // returns "M L⁻¹ T⁻²"
Dimensions.Resistivity.ToString() // returns "M L³ T⁻³ I⁻²"
接口IPhysicalQuantity
此接口是系统中所有物理量的基础。它有两个属性:
double Value { get; }
Dimensions Dimensions { get; }
对于Dimensions的每个定义值,将有一个相应的结构来实现IPhysicalQuantity接口。例如Length、Area、Mass等。
物理量示例——长度
该Length结构实现IPhysicalQuantity接口:
public readonly partial struct Length: IPhysicalQuantity
它具有只读Value属性:
public readonly double Value { get; init; }
和Dimensions属性:
public readonly Dimensions Dimensions { get { return Dimensions.Length; } }
请注意该Dimensions属性如何返回相应的静态定义的Dimensions值。
因此,给定此结构,我们现在可以定义一个变量来表示特定长度:
Length l0 = new Length(3.4); // 3.4 metres
struct定义了很多运算符。例如,您可以将length添加到另一个:
public static Length operator+(Length v1, Length v2)
{
return new Length(v1.Value + v2.Value);
}
或者比较两个length :
public static bool operator >(Length v1, Length v2)
{
return Compare(v1, v2) > 0;
}
或者,您可以通过将两个length相乘来创建Area:
public static Area operator*(Length v1, Length v2)
{
return new Area(v1.Value * v2.Value);
}
或者用length除以时间来创建Velocity:
public static Velocity operator/(Length v1, Time v2)
{
return new Velocity(v1.Value / v2.Value);
}
下面是正在使用的除法运算符:
Length l = 100.Metres();
Time t = 9.58.Seconds();
Velocity v = l / t; // v = 10.43 m/s
还有各种ToString()和Parse()方法:
public override string ToString();
public string ToString(UnitsSystem.FormatOption option);
public string ToString(UnitsSystem system, UnitsSystem.FormatOption option);
public string ToString(params Unit[] units);
public static Length Parse(string s);
public static Length Parse(string s, UnitsSystem system);
string的格式设置和分析实际上委托给当前单位系统。见下文。
下面是一些示例来演示ToString()和Parse()的各种选项:
Length l = 1234.567.Metres();
string s = l.ToString(); // s = "1.234567 km" (same as BestFit)
// Formatting options:
s = l.ToString(UnitsSystem.FormatOption.Standard); // s = "1234.567 m"
// (standard unit for length is metres)
s = l.ToString(UnitsSystem.FormatOption.BestFit); // s = "1.234567 km"
// (kilometres is the best fit unit
// for the value)
s = l.ToString(UnitsSystem.FormatOption.Multiple); // s = "1 km 234 m 56 cm 7 mm"
// (use multiple units in decreasing value)
// Specify the units:
s = l.ToString(MetricUnits.Metres, MetricUnits.Centimetres); // s = "1234 m 56.7 cm"
// British units:
s = l.ToString(BritishUnits.System, UnitsSystem.FormatOption.Standard); // s = "4050.41667 ft"
s = l.ToString(BritishUnits.System, UnitsSystem.FormatOption.BestFit); // s = "1350.13889 yd"
s = l.ToString(BritishUnits.System, UnitsSystem.FormatOption.Multiple); // s = "1350 yd 5 in"
// Specified British units:
s = l.ToString(BritishUnits.Miles,
BritishUnits.Feet, BritishUnits.Inches); // s = "4050 ft 5 in"
// Parsing
l = Length.Parse("42 m"); // l = 42 m
l = Length.Parse("42 m 76 cm"); // l = 42.76 m
l = Length.Parse("5 ft 4 in", BritishUnits.System); // l = 1.6256 m
// This will throw an exception
l = Length.Parse("42 m 76 kg");
由于数量需要如此多的类、运算符和方法,因此这些类是使用T4模板处理器生成的。请参阅代码生成部分。
温度
该库包含两个用于处理温度的类——绝对温度和温度变化。第一个用于绝对温度,就像您从温度计读取的那样:
AbsoluteTemperature t3 = 600.65.Kelvin(); // melting point of lead
AbsoluteTemperature c2 = 60.Celsius(); // c2 = 333.15 K
第二种用于许多公式中,其中温度变化很重要:
TemperatureChange deltaT = 100.Celsius() - 20.Celsius();
ThermalCapacity tcKettle = 100.CaloriesPerDegreeKelvin();
SpecificHeat shWater = 4184.JoulesPerKilogramPerDegreeKelvin();
Mass mWater = 1.Kilograms();
ThermalCapacity tcWater = mWater * shWater;
ThermalCapacity tcTotal = tcKettle + tcWater;
Energy e = tcTotal * deltaT; // e = 368208 J
结构体PhysicalQuantity
这是在强类型数量不起作用的情况下的出狱卡。它是弱类型的,因此具有自己的属性来表示维度:
public readonly partial struct PhysicalQuantity: IPhysicalQuantity
{
public double Value { get; init; }
public Dimensions Dimensions { get; init; }
与强类型数量一样,它也有用于加法等的运算符,但这些运算符是在运行时检查的,而不是阻止编译。所以可以这样做:
PhysicalQuantity l1 = new PhysicalQuantity(2.632, Dimensions.Length);
PhysicalQuantity l2 = new PhysicalQuantity(2.632, Dimensions.Length);
PhysicalQuantity sum = l1 + l2;
但这将引发异常:
PhysicalQuantity m = new PhysicalQuantity(65, Dimensions.Mass);
sum = l1 + m;
但是乘法和除法将正确计算出正确的维度:
PhysicalQuantity product = l1 * m;
string s = product.ToString(); // s = "171.08 kg⋅m"
形状
该库定义了一些实用程序类,用于执行与二维和三维形状相关的计算。例如,要获取半径为3 cm的圆的面积:
Circle circle = Circle.OfRadius(3.Centimetres());
Area area = circle.Area; // = 28.27 cm²
下面是一个使用实心3D形状的示例:
SolidCylinder cylinder = new SolidCylinder()
{
Mass = 20.Pounds(),
Radius = 2.Inches(),
Height = 6.Inches()
};
area = cylinder.SurfaceArea; // = 101.2 in²
Volume volume = cylinder.Volume; // = 75.23 in³
Density density = cylinder.Density // = 0.2653 lb/in³
Length radiusOfGyration = cylinder.RadiusOfGyration;// = 1.414 in
MomentOfInertia I = cylinder.MomentOfInertia; // = 0.2778 lb⋅ft²
类VectorOf
该库定义了一个可用于定向量(如位移、速度或力)的VectorOf类。我叫它VectorOf以避免与System.Numerics.Vector类的名称冲突。
public class VectorOf<T> where T: IPhysicalQuantity, new()
它具有采用3个标量值的构造函数:
public VectorOf(T x, T y, T z)
或幅度和方向(对于二维值):
public VectorOf(T magnitude, Angle direction)
或3-D矢量的幅度和两个角度(倾角和方位角):
public VectorOf(T magnitude, Angle inclination, Angle azimuth)
例如:
// Suppose a ship is travelling due east at 30 mph and a boy runs across the deck
// in a north west direction at 6 mph. What is the speed and direction of the boy
// relative to the sea?
var v2 = new VectorOf<velocity>(30.MilesPerHour(), 90.Degrees());
var v3 = new VectorOf<velocity>(6.MilesPerHour(), 315.Degrees());
var v4 = v2 + v3;
Velocity m2 = v4.Magnitude; // 26 mph
Angle a3 = v4.Direction; // 81 degrees
目前,该VectorOf类在内部使用该PhysicalQuantity类型。这是因为.Net 5不支持“泛型数学”。当我开始使用.Net 6或7版本时,我将在支持数学运算符的IPhysicalQuantity接口中定义静态方法,然后可以重新实现向量数学。
类UnitsSystem
该库为单位系统定义了一个abstract基类:
public abstract class UnitsSystem
UnitsSystem的子类负责将数量与string相互转换。因此,有多种虚拟string转换方法。还有一个对当前单位系统的静态引用,默认为Metric。
public static UnitsSystem Current = Metric;
默认情况下,ToString()和Parse()方法将使用当前单位制。
internal static string ToString(IPhysicalQuantity qty)
{
return Current.DoToString(qty);
}
internal static PhysicalQuantity Parse(string s)
{
return Current.DoParse(s);
}
或者,您可以指定要使用的系统:
internal static string ToString(IPhysicalQuantity qty, UnitsSystem system)
{
return system.DoToString(qty);
}
public static PhysicalQuantity Parse(string s, UnitsSystem system)
{
return system.DoParse(s);
}
默认情况下,单位系统将使用单位定义的查找表执行string转换。单元定义使用此类:
public class Unit
{
public readonly UnitsSystem System;
public readonly string Name;
public readonly string ShortName;
public readonly string CommonCode;
public readonly Dimensions Dimensions;
public readonly double ConversionFactor; //to convert from ISO units
:
因此,例如,以下是公制系统的一些定义:
public static Unit Metres =
new Unit(System, "metres", "m", "MTR", Dimensions.Length, 1.0, Unit.DisplayOption.Standard);
public static Unit SquareMetres =
new Unit(System, "squaremetres", "m²", "MTK", Dimensions.Area, 1.0, Unit.DisplayOption.Standard);
public static Unit CubicMetres =
new Unit(System, "cubicmetres", "m³", "MTQ", Dimensions.Volume, 1.0, Unit.DisplayOption.Standard);
public static Unit Kilograms =
new Unit(System, "kilograms", "kg", "KGM", Dimensions.Mass, 1.0, Unit.DisplayOption.Standard);
public static Unit Seconds =
new Unit(System, "seconds", "s", "SEC", Dimensions.Time, 1.0, Unit.DisplayOption.Standard);
或英国单位的类似:
public static Unit Feet = new Unit
(System, "feet", "ft", "FOT", Dimensions.Length, feetToMetres, Unit.DisplayOption.Standard);
public static Unit Inches = new Unit
(System, "inches", "in", "INH", Dimensions.Length, (feetToMetres/12.0), Unit.DisplayOption.Multi);
public static Unit Fortnight = new Unit
(System, "fortnight", "fn", "fn", Dimensions.Time, 3600.0*24.0*14.0, Unit.DisplayOption.Explicit);
public static Unit Pounds = new Unit
(System, "pounds", "lb", "LBR", Dimensions.Mass, poundsToKilogrammes, Unit.DisplayOption.Standard);
UN/CEFACT通用代码
每个单元都有唯一的UN/CEFACT代码。例如,英国加仑和美国加仑的代码分别为GLI和GLL。这些代码可以在电子交换数量信息时使用,例如在物料清单或采购订单中。在少数没有通用代码的情况下,用短名称代替。
例如:
string code = BritishUnits.CubicFeet.CommonCode; // code == "FTQ"
扩展方法
单位系统还定义了一组扩展方法,如下所示:
public static Length Metres(this double v)
{
return new Length(v);
}
这允许从浮点或整数值轻松创建数量:
Length l1 = 4.2.Metres();
Mass m1 = 12.Kilograms();
代码生成
如前所述,由于库包含大量重复代码,因此我们使用Visual Studio中提供的T4宏处理器。此工具允许我们通过创建包含C#代码和所需输出文本的混合模板文件来自动创建源代码。通常,我们从读取定义的XML文件开始,然后使用该模板生成所需的C#类和数据。
例如,下面是XML文件中定义公制单位系统的一行:
<unit name="Volts" shortname="volt" dimension="ElectricPotential" display="Standard" CommonCode="VLT" />
然后,此模板代码段将创建静态单元定义:
<#+ foreach(var ui in unitInfoList)
{
#>
public static Unit <# =ui.longName #>= new Unit(System, "<#= ui.longName.ToLower() #>",
"<#= ui.shortName #>", "<#= ui.CommonCode #>",
Dimensions.<#= ui.dimension #>, <#= ui.factor #>,
Unit.DisplayOption.<#= ui.displayOption #>);
<#+ } // end foreach ui
#>
在最终代码中生成如下行:
public static Unit Volts = new Unit
(System, "Volts", "volt", "VLT", Dimensions.ElectricPotential, 1.0, Unit.DisplayOption.Standard);
这种技术允许我们为每个数量类生成所需的大量运算符定义。例如,在 Dimensions.xml 文件中给定此定义:
<dimension name="Density" equals="Mass / Volume" />
我们可以生成Density类和以下所有运算符:
public static Density operator/(Mass v1, Volume v2)
public static Volume operator/(Mass v1, Density v2)
public static Mass operator*(Volume v1, Density v2)
提供了以下XML定义文件:
文件 | 描述 |
Dimensions.xml | 这定义了维度和它们之间的关系 |
MetricUnits.xml | 公制的单位定义 |
BritishUnits.xml | 英国单位,如英尺和磅 |
USAUnits.xml | 美国单位。这些与英国单位有些重叠。 |
TimeUnits.xml | 除秒以外的时间单位,例如小时和天 |
汇总表
下表总结了库支持的类别、尺寸、公式和单位:
名称 | 公式 | 维度 | 单位 |
AbsoluteTemperature | Θ | K (Kelvin) | |
Acceleration | Velocity / Time | L T⁻² | m/s² (MetresPerSecondSquared) |
AmountOfSubstance | N | mol (Mole) | |
AmountOfSubstanceByArea | AmountOfSubstance / Area | L⁻² N | m⁻²⋅mol |
AmountOfSubstanceByTime | AmountOfSubstance / Time | T⁻¹ N | mol⋅s⁻¹ |
Angle | A | rad (Radians) | |
AngularMomentum | MomentOfInertia * AngularVelocity | M L² T⁻¹ A | kg⋅m²/s (KilogramMetreSquaredPerSecond) |
AngularVelocity | Angle / Time | T⁻¹ A | rad⋅s⁻¹ |
AngularVelocitySquared | AngularVelocity * AngularVelocity | T⁻² A² | rad²⋅s⁻² |
Area | Length * Length | L² | m² (SquareMetres) |
ByArea | Dimensionless / Area | L⁻² | m⁻² |
ByLength | Dimensionless / Length | L⁻¹ | m⁻¹ |
CoefficientOfThermalExpansion | Dimensionless / TemperatureChange | Θ⁻¹ | K⁻¹ (PerDegreeKelvin) |
CoefficientOfViscosity | Force / KinematicViscosity | M L⁻¹ T⁻¹ | Pa⋅s (PascalSeconds) |
Current | I | amp (Ampere) | |
Density | Mass / Volume | M L⁻³ | kg/m³ (KilogramsPerCubicMetre) |
DiffusionFlux | AmountOfSubstanceByArea / Time | L⁻² T⁻¹ N | m⁻²⋅mol⋅s⁻¹ |
Dimensionless | 1 (Units) | ||
ElectricCharge | Current * Time | T I | amp⋅s |
ElectricPotential | Energy / ElectricCharge | M L² T⁻³ I⁻¹ | volt (Volts) |
ElectricPotentialSquared | ElectricPotential * ElectricPotential | M² L⁴ T⁻⁶ I⁻² | kg²⋅m⁴⋅amp⁻²⋅s⁻⁶ |
Energy | Force * Length | M L² T⁻² | J (Joules) |
EnergyFlux | Power / Area | M T⁻³ | kg⋅s⁻³ |
Force | Mass * Acceleration | M L T⁻² | N (Newtons) |
FourDimensionalVolume | Volume * Length | L⁴ | m⁴ |
Frequency | Dimensionless / Time | T⁻¹ | Hz (Hertz) |
Illuminance | LuminousFlux / Area | L⁻² J A² | lux (Lux) |
KinematicViscosity | Area / Time | L² T⁻¹ | m²/s (SquareMetresPerSecond) |
Length | L | m (Metres) | |
LuminousFlux | LuminousIntensity * SolidAngle | J A² | lm (Lumen) |
LuminousIntensity | J | cd (Candela) | |
Mass | M | kg (Kilograms) | |
MassByArea | Mass / Area | M L⁻² | kg⋅m⁻² |
MassByAreaByTimeSquared | MassByArea / TimeSquared | M L⁻² T⁻² | kg⋅m⁻²⋅s⁻² |
MassByLength | Mass / Length | M L⁻¹ | kg⋅m⁻¹ |
MassFlowRate | Mass / Time | M T⁻¹ | kg/s (KilogramsPerSecond) |
MolarConcentration | AmountOfSubstance / Volume | L⁻³ N | mol/m3 (MolesPerCubicMetre) |
MolarConcentrationGradient | MolarConcentration / Length | L⁻⁴ N | m⁻⁴⋅mol |
MolarConcentrationTimesAbsoluteTemperature | MolarConcentration * AbsoluteTemperature | L⁻³ Θ N | m⁻³⋅K⋅mol |
MolarMass | Mass / AmountOfSubstance | M N⁻¹ | kg/mol (KilogramsPerMole) |
MolarSpecificHeat | ThermalCapacity / AmountOfSubstance | M L² T⁻² Θ⁻¹ N⁻¹ | J⋅K⁻¹⋅mol⁻¹ (JoulesPerDegreeKelvinPerMole) |
MomentOfInertia | Mass * Area | M L² | kg⋅m² (KilogramMetreSquared) |
Momentum | Mass * Velocity | M L T⁻¹ | kg⋅m/s (KilogramMetresPerSecond) |
Power | Energy / Time | M L² T⁻³ | W (Watts) |
PowerGradient | Power / Length | M L T⁻³ | kg⋅m⋅s⁻³ |
Pressure | Force / Area | M L⁻¹ T⁻² | Pa (Pascals) |
Resistance | ElectricPotential / Current | M L² T⁻³ I⁻² | Ω (Ohms) |
ResistanceTimesArea | Resistance * Area | M L⁴ T⁻³ I⁻² | kg⋅m⁴⋅amp⁻²⋅s⁻³ |
ResistanceToFlow | MassFlowRate / FourDimensionalVolume | M L⁻⁴ T⁻¹ | kg⋅m⁻⁴⋅s⁻¹ |
Resistivity | Resistance * Length | M L³ T⁻³ I⁻² | Ω⋅m (OhmMetres) |
SolidAngle | Angle * Angle | A² | sr (Steradians) |
SpecificHeat | ThermalCapacity / Mass | L² T⁻² Θ⁻¹ | J⋅kg⁻¹⋅K⁻¹ (JoulesPerKilogramPerDegreeKelvin) |
SurfaceTension | Force / Length | M T⁻² | N/m (NewtonsPerMetre) |
TangentialVelocity | Velocity | L T⁻¹ | m/s (MetresPerSecond) |
TemperatureChange | Θ | K (Kelvin) | |
TemperatureGradient | TemperatureChange / Length | L⁻¹ Θ | m⁻¹⋅K |
ThermalCapacity | Energy / TemperatureChange | M L² T⁻² Θ⁻¹ | J/K (JoulesPerDegreeKelvin) |
ThermalCapacityByVolume | ThermalCapacity / Volume | M L⁻¹ T⁻² Θ⁻¹ | kg⋅m⁻¹⋅K⁻¹⋅s⁻² |
ThermalConductivity | EnergyFlux / TemperatureGradient | M L T⁻³ Θ⁻¹ | W⋅m⁻¹⋅K⁻¹ (WattsPerMetrePerDegree) |
Time | T | s (Seconds) | |
TimeSquared | Time * Time | T² | s² |
Velocity | Length / Time | L T⁻¹ | m/s (MetresPerSecond) |
VelocityByDensity | Velocity / Density | M⁻¹ L⁴ T⁻¹ | kg⁻¹⋅m⁴⋅s⁻¹ |
VelocityGradient | Velocity / Length | T⁻¹ | Hz (Hertz) |
VelocitySquared | Velocity * Velocity | L² T⁻² | m²⋅s⁻² |
Volume | Area * Length | L³ | m³ (CubicMetres) |
VolumeFlowRate | Volume / Time | L³ T⁻¹ | m³/s (CubicMetresPerSecond) |
更多示例
以下是使用该库的更多示例,基于Nelkon和Parker的问题。
鲁莽的跳楼者:
// A person of mass 50 kg who is jumping from a height of 5 metres
// will land on the ground
// with a velocity = √2gh = √ 2 x 9.8 x 5 = 9.9 m/s , assuming g = 9.8 m/s.
Mass m = 50.Kilograms();
Length h = 5.Metres();
Acceleration g = 9.80665.MetresPerSecondSquared();
Velocity v = Functions.Sqrt(2 * g * h); // v = 9.90285312 m/s
// If he does not flex his knees on landing,
// he will be brought to rest very quickly, say in
// 1/10th second. The force F acting is then given
// by momentum change/time = 50 * 9.9 / 0.1 = 4951 N
Momentum mm = m * v;
Time t = 0.1.Seconds();
Force f = mm / t; // f = 4951.42656 N
还有飞翔的板球:
// Suppose a cricket ball was thrown straight up with an initial velocity,
// u, of 30 m/s.
// The time taken to reach the top of its motion can be obtained from the equation
// v = u + at.
// The velocity, v, at the top is zero; and since u = 30 m and
// a = —9.8 or 10 m/s²(approx),
// we have 30 - 10t = 0.
// Therefore t = 30 / 10 = 3s
// The highest distance reached is thus given by
// d = ut + 1 / 2 at ^ 2 = 30x3 - 5x3 ^ 2 = 45 m.
var u = 30.MetresPerSecond();
var g = 9.80665.MetresPerSecondSquared();
var t = u / g; // t = 3.05914864 s
var d = u * t + -g * t * t / 2.0; // d = 45.8872296 m
表面张力:
// Calculate the work done against surface tension in blowing a bubble of 1 cm in diamter
// if surface tension of a soap solution = 25 dynes/cm.
Length r = 1.Centimetres() / 2;
SurfaceTension surfaceTensionOfSoapSolution = 25.DynesPerCentimetre();
// The initial surface area is zero
// The final surface area = 2 x 4π x 0.5² = 2π sq cm.
Area a = new Sphere(r).Area * 2;
// Therefor work done = T x increase in surface area = 25 x 2π = 157 ergs.
Energy e = surfaceTensionOfSoapSolution * a; // 157.1 erg
杨氏模量:
// If a 2kg weight is attached to the end of a wire of length 200cm and diameter 0.64mm
// and the extension is 0.6mm then what is the Young's Modulus E of the wire?
Force f = 2.KilogramWeight();
Area a = Circle.OfDiameter(0.64.Millimetres()).Area;
var stress = f / a;
Length l = 200.Centimetres();
Length e = 0.6.Millimetres();
var strain = e / l;
// E = (2000 x 980 / π x 0.032²) / (0.06/200) = 2e12 dynes/cm²
var E = stress / strain; // 2.032E+12 dyn/cm²
菲克第一定律:
// In a region of an unsaturated solution of sucrose the molar concentration gradient is -0.1 mol/L/cm.
// What quantity of sucrose molecules pass through an area of 1cm² in 10 minutes?
MolarConcentration c = 0.1.Mole() / 1.Litres();
MolarConcentrationGradient cg = -c / 1.Centimetres();
Area a = 1.SquareCentimetres();
Time t = 10.Minutes();
AreaFlowRate d = 0.522e-9.SquareMetresPerSecond(); // diffusion coefficient
DiffusionFlux j = d * cg;
AmountOfSubstance n = j * a * t; // -313.2 nmol
兴趣点
单元测试
示例程序还测试库,但不使用单元测试框架。相反,它使用一个简单的静态类Check,允许我们编写如下代码:
Check.Equal(42.0, d5, "wrong value for d5");
如果前两个参数不相等,这将引发异常。
性能
我曾希望通过创建不可变的数据类型并大量使用积极的内联和积极的优化提示,数量类的性能将与“原始”双精度的性能相当。但事实证明并非如此。为了测试这一点,我实现了两次相同的火箭模拟,一次使用普通双精度,另一次使用数量类。在发布版本中,使用double的版本大约快6倍。通过检查为某些典型算术生成的代码,可以看出原因。例如,此代码:
double d1 = 4.2;
double d2 = 5.3;
double d3 = 6.4;
double d4 = d1 + d2 + d3;
为添加生成代码,如下所示:
00007FFCCC4B6A46 vmovsd xmm3,qword ptr [rbp-8]
00007FFCCC4B6A4B vaddsd xmm3,xmm3,mmword ptr
[UnitTests.Program.TestDouble()+0B0h (07FFCCC4B6AC0h)]
00007FFCCC4B6A53 vaddsd xmm3,xmm3,mmword ptr [rbp-10h]
00007FFCCC4B6A58 vmovsd qword ptr [rbp-18h],xmm3
而使用类库的相同公式:
Dimensionless d1 = 4.2;
Dimensionless d2 = 5.3;
Dimensionless d3 = 6.4;
Dimensionless d4 = d1 + d2 + d3;
生成更长的代码:
00007FFCD5726B59 mov rcx,qword ptr [rsp+70h]
00007FFCD5726B5E mov qword ptr [rsp+58h],rcx
00007FFCD5726B63 mov rcx,qword ptr [rsp+68h]
00007FFCD5726B68 mov qword ptr [rsp+50h],rcx
00007FFCD5726B6D vmovsd xmm0,qword ptr [rsp+58h]
00007FFCD5726B73 vaddsd xmm0,xmm0,mmword ptr [rsp+50h]
00007FFCD5726B79 vmovsd qword ptr [rsp+48h],xmm0
00007FFCD5726B7F mov rcx,qword ptr [rsp+48h]
00007FFCD5726B84 mov qword ptr [rsp+40h],rcx
00007FFCD5726B89 mov rcx,qword ptr [rsp+60h]
00007FFCD5726B8E mov qword ptr [rsp+38h],rcx
00007FFCD5726B93 vmovsd xmm0,qword ptr [rsp+40h]
00007FFCD5726B99 vaddsd xmm0,xmm0,mmword ptr [rsp+38h]
00007FFCD5726B9F vmovsd qword ptr [rsp+30h],xmm0
00007FFCD5726BA5 mov rcx,qword ptr [rsp+30h]
00007FFCD5726BAA mov qword ptr [rsp+78h],rcx
有很多多余的移动指令。也许对JIT编译器有更深入了解的人可以对此有所了解。
https://www.codeproject.com/Articles/5306824/Taking-Your-Brain-to-Another-Dimension-A-Csharp-li