diff --git a/packages/dim-si/README.md b/packages/dim-si/README.md index 3370c65..5b64961 100644 --- a/packages/dim-si/README.md +++ b/packages/dim-si/README.md @@ -133,30 +133,52 @@ bun add @isentropic/dim-si ### Derived -| Quantity | Units | -| --------------------- | ------------------------------------------------------------------- | -| Area | `squareMeter`, `hectare` | -| Volume | `cubicMeter`, `liter`, `milliliter`, `microliter` | -| Velocity | `meterPerSecond` | -| Acceleration | `meterPerSecondSquared` | -| Force | `newton` | -| Pressure | `pascal`, `bar`, `millibar` | -| Energy | `joule`, `kilojoule`, `megajoule`, `kilowattHour` | -| Power | `watt`, `milliwatt`, `kilowatt`, `megawatt`, `gigawatt`, `terawatt` | -| Frequency | `hertz`, `kilohertz`, `megahertz`, `gigahertz`, `becquerel` | -| Voltage | `volt`, `millivolt`, `kilovolt` | -| Resistance | `ohm`, `milliohm`, `kilohm`, `megohm` | -| Capacitance | `farad`, `microfarad`, `nanofarad`, `picofarad` | -| Inductance | `henry`, `millihenry`, `microhenry` | -| Charge | `coulomb` | -| Magnetic Flux | `weber` | -| Magnetic Flux Density | `tesla` | -| Conductance | `siemens` | -| Illuminance | `lux` | -| Luminous Flux | `lumen` | -| Catalytic Activity | `katal` | -| Thermal Conductance | `wattPerKelvin`, `milliwattPerKelvin`, `kilowattPerKelvin` | -| Absorbed Dose | `gray`, `sievert` | +| Quantity | Units | +| ----------------------- | ------------------------------------------------------------------- | +| Area | `squareMeter`, `squareKilometer`, `squareCentimeter`, `hectare` | +| Volume | `cubicMeter`, `liter`, `milliliter`, `microliter` | +| Velocity | `meterPerSecond`, `kilometerPerHour` | +| Acceleration | `meterPerSecondSquared` | +| Force | `newton` | +| Pressure | `pascal`, `bar`, `millibar`, `atmosphere` | +| Energy | `joule`, `kilojoule`, `megajoule`, `kilowattHour` | +| Power | `watt`, `milliwatt`, `kilowatt`, `megawatt`, `gigawatt`, `terawatt` | +| Frequency | `hertz`, `kilohertz`, `megahertz`, `gigahertz`, `becquerel` | +| Density | `kilogramPerCubicMeter`, `gramPerLiter`, `gramPerCubicCentimeter` | +| Specific Volume | `cubicMeterPerKilogram` | +| Momentum | `newtonSecond` | +| Angular Velocity | `radianPerSecond`, `revolutionPerMinute` | +| Angular Acceleration | `radianPerSecondSquared` | +| Torque | `newtonMeter` | +| Dynamic Viscosity | `pascalSecond` | +| Kinematic Viscosity | `squareMeterPerSecond` | +| Surface Tension | `newtonPerMeter` | +| Wavenumber | `reciprocalMeter` | +| Voltage | `volt`, `millivolt`, `kilovolt` | +| Resistance | `ohm`, `milliohm`, `kilohm`, `megohm` | +| Capacitance | `farad`, `microfarad`, `nanofarad`, `picofarad` | +| Inductance | `henry`, `millihenry`, `microhenry` | +| Charge | `coulomb`, `ampereHour`, `milliampereHour` | +| Magnetic Flux | `weber` | +| Magnetic Flux Density | `tesla` | +| Conductance | `siemens` | +| Electric Field Strength | `voltPerMeter` | +| Permittivity | `faradPerMeter` | +| Permeability | `henryPerMeter` | +| Current Density | `amperePerSquareMeter` | +| Illuminance | `lux` | +| Luminous Flux | `lumen` | +| Catalytic Activity | `katal` | +| Thermal Conductance | `wattPerKelvin`, `milliwattPerKelvin`, `kilowattPerKelvin` | +| Heat Capacity | `joulePerKelvin` | +| Specific Heat Capacity | `joulePerKilogramKelvin` | +| Specific Energy | `joulePerKilogram` | +| Thermal Conductivity | `wattPerMeterKelvin` | +| Absorbed Dose | `gray`, `sievert` | +| Volumetric Flow Rate | `cubicMeterPerSecond`, `literPerSecond`, `literPerMinute` | +| Mass Flow Rate | `kilogramPerSecond` | +| Concentration | `molePerCubicMeter`, `molePerLiter` | +| Molar Mass | `kilogramPerMole`, `gramPerMole` | _\* [Affine quantity](https://github.com/isentropic-dev/dim/blob/main/packages/dim-unit/README.md#affine-units) diff --git a/packages/dim-si/deno.json b/packages/dim-si/deno.json index 51e9078..453c9c6 100644 --- a/packages/dim-si/deno.json +++ b/packages/dim-si/deno.json @@ -48,7 +48,29 @@ "./illuminance": "./src/illuminance.ts", "./absorbed-dose": "./src/absorbed-dose.ts", "./catalytic-activity": "./src/catalytic-activity.ts", - "./thermal-conductance": "./src/thermal-conductance.ts" + "./thermal-conductance": "./src/thermal-conductance.ts", + "./density": "./src/density.ts", + "./specific-volume": "./src/specific-volume.ts", + "./momentum": "./src/momentum.ts", + "./angular-velocity": "./src/angular-velocity.ts", + "./angular-acceleration": "./src/angular-acceleration.ts", + "./torque": "./src/torque.ts", + "./dynamic-viscosity": "./src/dynamic-viscosity.ts", + "./kinematic-viscosity": "./src/kinematic-viscosity.ts", + "./surface-tension": "./src/surface-tension.ts", + "./wavenumber": "./src/wavenumber.ts", + "./heat-capacity": "./src/heat-capacity.ts", + "./specific-heat-capacity": "./src/specific-heat-capacity.ts", + "./specific-energy": "./src/specific-energy.ts", + "./thermal-conductivity": "./src/thermal-conductivity.ts", + "./electric-field-strength": "./src/electric-field-strength.ts", + "./permittivity": "./src/permittivity.ts", + "./permeability": "./src/permeability.ts", + "./current-density": "./src/current-density.ts", + "./volumetric-flow-rate": "./src/volumetric-flow-rate.ts", + "./mass-flow-rate": "./src/mass-flow-rate.ts", + "./concentration": "./src/concentration.ts", + "./molar-mass": "./src/molar-mass.ts" }, "version": "0.4.3" } diff --git a/packages/dim-si/src/absorbed-dose.ts b/packages/dim-si/src/absorbed-dose.ts index 9825997..8a24aef 100644 --- a/packages/dim-si/src/absorbed-dose.ts +++ b/packages/dim-si/src/absorbed-dose.ts @@ -4,7 +4,8 @@ * SI unit: gray (Gy). * * Note: gray (absorbed radiation dose) and sievert (equivalent dose) - * share this dimension. This is intentional per SI. + * share this dimension. This is intentional per SI — the type system + * cannot distinguish them. * * @example Creating dose quantities * ```ts diff --git a/packages/dim-si/src/angular-acceleration.ts b/packages/dim-si/src/angular-acceleration.ts new file mode 100644 index 0000000..0c01c3b --- /dev/null +++ b/packages/dim-si/src/angular-acceleration.ts @@ -0,0 +1,29 @@ +/** + * Angular acceleration units (T⁻²). + * + * SI unit: radian per second squared (rad/s²). + * + * @example Creating an angular acceleration + * ```ts + * import { radianPerSecondSquared } from "@isentropic/dim-si/angular-acceleration"; + * + * const alpha = radianPerSecondSquared(5); + * ``` + * + * @module + */ + +import type { AngularAcceleration as AngularAccelerationDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { angularAcceleration } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI angular acceleration quantity. */ +export type AngularAcceleration = Linear; + +/** Radian per second squared (rad/s²) — SI unit of angular acceleration. */ +export const radianPerSecondSquared: BaseUnit = si.unit( + angularAcceleration, +); diff --git a/packages/dim-si/src/angular-velocity.ts b/packages/dim-si/src/angular-velocity.ts new file mode 100644 index 0000000..efcdfa5 --- /dev/null +++ b/packages/dim-si/src/angular-velocity.ts @@ -0,0 +1,39 @@ +/** + * Angular velocity units (T⁻¹). + * + * SI unit: radian per second (rad/s). + * + * Note: angular velocity shares this dimension with frequency + * (hertz). This is intentional per SI — the type system cannot + * distinguish them. + * + * @example Converting RPM to radians per second + * ```ts + * import { radianPerSecond, revolutionPerMinute } from "@isentropic/dim-si/angular-velocity"; + * import { valueIn } from "@isentropic/dim-si/ops"; + * + * const engine = revolutionPerMinute(3000); + * valueIn(engine, radianPerSecond); // ~314.16 + * ``` + * + * @module + */ + +import type { AngularVelocity as AngularVelocityDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { angularVelocity } from "@isentropic/dim-isq"; +import type { BaseUnit, ScaledUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI angular velocity quantity. */ +export type AngularVelocity = Linear; + +/** Radian per second (rad/s) — SI unit of angular velocity. */ +export const radianPerSecond: BaseUnit = si.unit( + angularVelocity, +); + +/** Revolution per minute (rpm) — 2π/60 rad/s. */ +export const revolutionPerMinute: ScaledUnit = + radianPerSecond.scaled(2 * Math.PI / 60); diff --git a/packages/dim-si/src/area.ts b/packages/dim-si/src/area.ts index ecee908..517dd90 100644 --- a/packages/dim-si/src/area.ts +++ b/packages/dim-si/src/area.ts @@ -21,6 +21,7 @@ import { area } from "@isentropic/dim-isq"; import type { BaseUnit, ScaledUnit } from "./types.ts"; import type { Si } from "./system.ts"; import { si } from "./system.ts"; +import { centimeter, kilometer } from "./length.ts"; /** An SI area quantity. */ export type Area = Linear; @@ -28,5 +29,15 @@ export type Area = Linear; /** Square meter (m²) — SI unit of area. */ export const squareMeter: BaseUnit = si.unit(area); +/** Square kilometer (km²) — 10⁶ square meters. */ +export const squareKilometer: ScaledUnit = squareMeter.scaled( + kilometer.scale ** 2, +); + +/** Square centimeter (cm²) — 10⁻⁴ square meters. */ +export const squareCentimeter: ScaledUnit = squareMeter.scaled( + centimeter.scale ** 2, +); + /** Hectare (ha) — 10000 square meters. */ export const hectare: ScaledUnit = squareMeter.scaled(10000); diff --git a/packages/dim-si/src/charge.ts b/packages/dim-si/src/charge.ts index fc88adc..61026ad 100644 --- a/packages/dim-si/src/charge.ts +++ b/packages/dim-si/src/charge.ts @@ -20,12 +20,24 @@ import type { Charge as ChargeDim } from "@isentropic/dim-isq"; import type { Linear } from "@isentropic/dim-unit"; import { charge } from "@isentropic/dim-isq"; -import type { BaseUnit } from "./types.ts"; +import type { BaseUnit, ScaledUnit } from "./types.ts"; import type { Si } from "./system.ts"; import { si } from "./system.ts"; +import { ampere, milliampere } from "./current.ts"; +import { hour } from "./time.ts"; /** An SI charge quantity. */ export type Charge = Linear; /** Coulomb (C) — SI unit of electric charge. */ export const coulomb: BaseUnit = si.unit(charge); + +/** Ampere-hour (Ah) — 3600 coulombs. */ +export const ampereHour: ScaledUnit = coulomb.scaled( + ampere.scale * hour.scale, +); + +/** Milliampere-hour (mAh) — 3.6 coulombs. */ +export const milliampereHour: ScaledUnit = coulomb.scaled( + milliampere.scale * hour.scale, +); diff --git a/packages/dim-si/src/concentration.ts b/packages/dim-si/src/concentration.ts new file mode 100644 index 0000000..10342f2 --- /dev/null +++ b/packages/dim-si/src/concentration.ts @@ -0,0 +1,36 @@ +/** + * Concentration units (N·L⁻³). + * + * SI unit: mole per cubic meter (mol/m³). + * + * @example Converting between concentration units + * ```ts + * import { molePerCubicMeter, molePerLiter } from "@isentropic/dim-si/concentration"; + * import { valueIn } from "@isentropic/dim-si/ops"; + * + * const c = molePerLiter(1); + * valueIn(c, molePerCubicMeter); // 1000 + * ``` + * + * @module + */ + +import type { Concentration as ConcentrationDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { concentration } from "@isentropic/dim-isq"; +import type { BaseUnit, ScaledUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; +import { KILO } from "./prefixes.ts"; + +/** An SI concentration quantity. */ +export type Concentration = Linear; + +/** Mole per cubic meter (mol/m³) — SI unit of concentration. */ +export const molePerCubicMeter: BaseUnit = si.unit( + concentration, +); + +/** Mole per liter (mol/L) — 1000 mol/m³. */ +export const molePerLiter: ScaledUnit = molePerCubicMeter + .scaled(KILO); diff --git a/packages/dim-si/src/current-density.ts b/packages/dim-si/src/current-density.ts new file mode 100644 index 0000000..6468f62 --- /dev/null +++ b/packages/dim-si/src/current-density.ts @@ -0,0 +1,29 @@ +/** + * Current density units (I·L⁻²). + * + * SI unit: ampere per square meter (A/m²). + * + * @example Creating a current density + * ```ts + * import { amperePerSquareMeter } from "@isentropic/dim-si/current-density"; + * + * const j = amperePerSquareMeter(1e6); + * ``` + * + * @module + */ + +import type { CurrentDensity as CurrentDensityDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { currentDensity } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI current density quantity. */ +export type CurrentDensity = Linear; + +/** Ampere per square meter (A/m²) — SI unit of current density. */ +export const amperePerSquareMeter: BaseUnit = si.unit( + currentDensity, +); diff --git a/packages/dim-si/src/density.ts b/packages/dim-si/src/density.ts new file mode 100644 index 0000000..f9c7389 --- /dev/null +++ b/packages/dim-si/src/density.ts @@ -0,0 +1,44 @@ +/** + * Density units (M·L⁻³). + * + * SI unit: kilogram per cubic meter (kg/m³). + * + * @example Converting between density units + * ```ts + * import { gramPerCubicCentimeter, gramPerLiter, kilogramPerCubicMeter } from "@isentropic/dim-si/density"; + * import { valueIn } from "@isentropic/dim-si/ops"; + * + * const water = kilogramPerCubicMeter(1000); + * valueIn(water, gramPerLiter); // 1000 + * valueIn(water, gramPerCubicCentimeter); // 1 + * ``` + * + * @module + */ + +import type { Density as DensityDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { density } from "@isentropic/dim-isq"; +import { roundScale } from "@isentropic/dim-unit"; +import type { BaseUnit, ScaledUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; +import { gram } from "./mass.ts"; +import { centimeter } from "./length.ts"; +import { liter } from "./volume.ts"; + +/** An SI density quantity. */ +export type Density = Linear; + +/** Kilogram per cubic meter (kg/m³) — SI unit of density. */ +export const kilogramPerCubicMeter: BaseUnit = si.unit(density); + +/** Gram per liter (g/L) — numerically equivalent to kg/m³. */ +export const gramPerLiter: ScaledUnit = kilogramPerCubicMeter + .scaled(gram.scale / liter.scale); + +/** Gram per cubic centimeter (g/cm³) — 1000 kg/m³. */ +export const gramPerCubicCentimeter: ScaledUnit = + kilogramPerCubicMeter.scaled( + roundScale(gram.scale / centimeter.scale ** 3), + ); diff --git a/packages/dim-si/src/dynamic-viscosity.ts b/packages/dim-si/src/dynamic-viscosity.ts new file mode 100644 index 0000000..358dea6 --- /dev/null +++ b/packages/dim-si/src/dynamic-viscosity.ts @@ -0,0 +1,29 @@ +/** + * Dynamic viscosity units (M·L⁻¹·T⁻¹). + * + * SI unit: pascal second (Pa·s). + * + * @example Creating a dynamic viscosity + * ```ts + * import { pascalSecond } from "@isentropic/dim-si/dynamic-viscosity"; + * + * const water = pascalSecond(0.001); + * ``` + * + * @module + */ + +import type { DynamicViscosity as DynamicViscosityDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { dynamicViscosity } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI dynamic viscosity quantity. */ +export type DynamicViscosity = Linear; + +/** Pascal second (Pa·s) — SI unit of dynamic viscosity. */ +export const pascalSecond: BaseUnit = si.unit( + dynamicViscosity, +); diff --git a/packages/dim-si/src/electric-field-strength.ts b/packages/dim-si/src/electric-field-strength.ts new file mode 100644 index 0000000..5f47dac --- /dev/null +++ b/packages/dim-si/src/electric-field-strength.ts @@ -0,0 +1,29 @@ +/** + * Electric field strength units (M·L·T⁻³·I⁻¹). + * + * SI unit: volt per meter (V/m). + * + * @example Creating an electric field strength + * ```ts + * import { voltPerMeter } from "@isentropic/dim-si/electric-field-strength"; + * + * const field = voltPerMeter(1000); + * ``` + * + * @module + */ + +import type { ElectricFieldStrength as ElectricFieldStrengthDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { electricFieldStrength } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI electric field strength quantity. */ +export type ElectricFieldStrength = Linear; + +/** Volt per meter (V/m) — SI unit of electric field strength. */ +export const voltPerMeter: BaseUnit = si.unit( + electricFieldStrength, +); diff --git a/packages/dim-si/src/frequency.ts b/packages/dim-si/src/frequency.ts index 8a203fe..e76c601 100644 --- a/packages/dim-si/src/frequency.ts +++ b/packages/dim-si/src/frequency.ts @@ -4,7 +4,8 @@ * SI unit: hertz (Hz). * * Note: becquerel (radioactive decay rate) shares this dimension - * with hertz (cycles per second). This is intentional per SI. + * with hertz (cycles per second). This is intentional per SI — the + * type system cannot distinguish them. * * @example Converting between frequency units * ```ts diff --git a/packages/dim-si/src/heat-capacity.ts b/packages/dim-si/src/heat-capacity.ts new file mode 100644 index 0000000..b5fe0f8 --- /dev/null +++ b/packages/dim-si/src/heat-capacity.ts @@ -0,0 +1,27 @@ +/** + * Heat capacity units (M·L²·T⁻²·Θ⁻¹). + * + * SI unit: joule per kelvin (J/K). + * + * @example Creating a heat capacity + * ```ts + * import { joulePerKelvin } from "@isentropic/dim-si/heat-capacity"; + * + * const capacity = joulePerKelvin(4186); + * ``` + * + * @module + */ + +import type { HeatCapacity as HeatCapacityDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { heatCapacity } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI heat capacity quantity. */ +export type HeatCapacity = Linear; + +/** Joule per kelvin (J/K) — SI unit of heat capacity. */ +export const joulePerKelvin: BaseUnit = si.unit(heatCapacity); diff --git a/packages/dim-si/src/kinematic-viscosity.ts b/packages/dim-si/src/kinematic-viscosity.ts new file mode 100644 index 0000000..1af308c --- /dev/null +++ b/packages/dim-si/src/kinematic-viscosity.ts @@ -0,0 +1,29 @@ +/** + * Kinematic viscosity units (L²·T⁻¹). + * + * SI unit: square meter per second (m²/s). + * + * @example Creating a kinematic viscosity + * ```ts + * import { squareMeterPerSecond } from "@isentropic/dim-si/kinematic-viscosity"; + * + * const water = squareMeterPerSecond(1e-6); + * ``` + * + * @module + */ + +import type { KinematicViscosity as KinematicViscosityDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { kinematicViscosity } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI kinematic viscosity quantity. */ +export type KinematicViscosity = Linear; + +/** Square meter per second (m²/s) — SI unit of kinematic viscosity. */ +export const squareMeterPerSecond: BaseUnit = si.unit( + kinematicViscosity, +); diff --git a/packages/dim-si/src/mass-flow-rate.ts b/packages/dim-si/src/mass-flow-rate.ts new file mode 100644 index 0000000..766c8f5 --- /dev/null +++ b/packages/dim-si/src/mass-flow-rate.ts @@ -0,0 +1,29 @@ +/** + * Mass flow rate units (M·T⁻¹). + * + * SI unit: kilogram per second (kg/s). + * + * @example Creating a mass flow rate + * ```ts + * import { kilogramPerSecond } from "@isentropic/dim-si/mass-flow-rate"; + * + * const flow = kilogramPerSecond(0.5); + * ``` + * + * @module + */ + +import type { MassFlowRate as MassFlowRateDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { massFlowRate } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI mass flow rate quantity. */ +export type MassFlowRate = Linear; + +/** Kilogram per second (kg/s) — SI unit of mass flow rate. */ +export const kilogramPerSecond: BaseUnit = si.unit( + massFlowRate, +); diff --git a/packages/dim-si/src/molar-mass.ts b/packages/dim-si/src/molar-mass.ts new file mode 100644 index 0000000..a02cd30 --- /dev/null +++ b/packages/dim-si/src/molar-mass.ts @@ -0,0 +1,35 @@ +/** + * Molar mass units (M·N⁻¹). + * + * SI unit: kilogram per mole (kg/mol). + * + * @example Converting between molar mass units + * ```ts + * import { gramPerMole, kilogramPerMole } from "@isentropic/dim-si/molar-mass"; + * import { valueIn } from "@isentropic/dim-si/ops"; + * + * const water = gramPerMole(18.015); + * valueIn(water, kilogramPerMole); // 0.018015 + * ``` + * + * @module + */ + +import type { MolarMass as MolarMassDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { molarMass } from "@isentropic/dim-isq"; +import type { BaseUnit, ScaledUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; +import { MILLI } from "./prefixes.ts"; + +/** An SI molar mass quantity. */ +export type MolarMass = Linear; + +/** Kilogram per mole (kg/mol) — SI unit of molar mass. */ +export const kilogramPerMole: BaseUnit = si.unit(molarMass); + +/** Gram per mole (g/mol) — 10⁻³ kg/mol. */ +export const gramPerMole: ScaledUnit = kilogramPerMole.scaled( + MILLI, +); diff --git a/packages/dim-si/src/momentum.ts b/packages/dim-si/src/momentum.ts new file mode 100644 index 0000000..b19d602 --- /dev/null +++ b/packages/dim-si/src/momentum.ts @@ -0,0 +1,27 @@ +/** + * Momentum units (M·L·T⁻¹). + * + * SI unit: newton second (N·s). + * + * @example Creating a momentum + * ```ts + * import { newtonSecond } from "@isentropic/dim-si/momentum"; + * + * const impulse = newtonSecond(50); + * ``` + * + * @module + */ + +import type { Momentum as MomentumDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { momentum } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI momentum quantity. */ +export type Momentum = Linear; + +/** Newton second (N·s) — SI unit of momentum. */ +export const newtonSecond: BaseUnit = si.unit(momentum); diff --git a/packages/dim-si/src/permeability.ts b/packages/dim-si/src/permeability.ts new file mode 100644 index 0000000..961834d --- /dev/null +++ b/packages/dim-si/src/permeability.ts @@ -0,0 +1,27 @@ +/** + * Permeability units (M·L·T⁻²·I⁻²). + * + * SI unit: henry per meter (H/m). + * + * @example Creating a permeability + * ```ts + * import { henryPerMeter } from "@isentropic/dim-si/permeability"; + * + * const vacuum = henryPerMeter(1.2566e-6); + * ``` + * + * @module + */ + +import type { Permeability as PermeabilityDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { permeability } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI permeability quantity. */ +export type Permeability = Linear; + +/** Henry per meter (H/m) — SI unit of permeability. */ +export const henryPerMeter: BaseUnit = si.unit(permeability); diff --git a/packages/dim-si/src/permittivity.ts b/packages/dim-si/src/permittivity.ts new file mode 100644 index 0000000..fe07f33 --- /dev/null +++ b/packages/dim-si/src/permittivity.ts @@ -0,0 +1,27 @@ +/** + * Permittivity units (M⁻¹·L⁻³·T⁴·I²). + * + * SI unit: farad per meter (F/m). + * + * @example Creating a permittivity + * ```ts + * import { faradPerMeter } from "@isentropic/dim-si/permittivity"; + * + * const vacuum = faradPerMeter(8.854e-12); + * ``` + * + * @module + */ + +import type { Permittivity as PermittivityDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { permittivity } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI permittivity quantity. */ +export type Permittivity = Linear; + +/** Farad per meter (F/m) — SI unit of permittivity. */ +export const faradPerMeter: BaseUnit = si.unit(permittivity); diff --git a/packages/dim-si/src/pressure.ts b/packages/dim-si/src/pressure.ts index 5e954b8..e4d926a 100644 --- a/packages/dim-si/src/pressure.ts +++ b/packages/dim-si/src/pressure.ts @@ -35,3 +35,6 @@ export const bar: ScaledUnit = pascal.scaled(100000); /** Millibar (mbar) — 100 pascals. */ export const millibar: ScaledUnit = bar.scaled(MILLI); + +/** Standard atmosphere (atm) — 101325 pascals. */ +export const atmosphere: ScaledUnit = pascal.scaled(101325); diff --git a/packages/dim-si/src/specific-energy.ts b/packages/dim-si/src/specific-energy.ts new file mode 100644 index 0000000..a3b4e29 --- /dev/null +++ b/packages/dim-si/src/specific-energy.ts @@ -0,0 +1,33 @@ +/** + * Specific energy units (L²·T⁻²). + * + * SI unit: joule per kilogram (J/kg). + * + * Note: specific energy shares this dimension with absorbed dose + * (gray/sievert). This is intentional per SI — the type system + * cannot distinguish them. + * + * @example Creating a specific energy + * ```ts + * import { joulePerKilogram } from "@isentropic/dim-si/specific-energy"; + * + * const lhv = joulePerKilogram(43e6); + * ``` + * + * @module + */ + +import type { SpecificEnergy as SpecificEnergyDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { specificEnergy } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI specific energy quantity. */ +export type SpecificEnergy = Linear; + +/** Joule per kilogram (J/kg) — SI unit of specific energy. */ +export const joulePerKilogram: BaseUnit = si.unit( + specificEnergy, +); diff --git a/packages/dim-si/src/specific-heat-capacity.ts b/packages/dim-si/src/specific-heat-capacity.ts new file mode 100644 index 0000000..7a9d7aa --- /dev/null +++ b/packages/dim-si/src/specific-heat-capacity.ts @@ -0,0 +1,28 @@ +/** + * Specific heat capacity units (L²·T⁻²·Θ⁻¹). + * + * SI unit: joule per kilogram kelvin (J/(kg·K)). + * + * @example Creating a specific heat capacity + * ```ts + * import { joulePerKilogramKelvin } from "@isentropic/dim-si/specific-heat-capacity"; + * + * const water = joulePerKilogramKelvin(4186); + * ``` + * + * @module + */ + +import type { SpecificHeatCapacity as SpecificHeatCapacityDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { specificHeatCapacity } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI specific heat capacity quantity. */ +export type SpecificHeatCapacity = Linear; + +/** Joule per kilogram kelvin (J/(kg·K)) — SI unit of specific heat capacity. */ +export const joulePerKilogramKelvin: BaseUnit = si + .unit(specificHeatCapacity); diff --git a/packages/dim-si/src/specific-volume.ts b/packages/dim-si/src/specific-volume.ts new file mode 100644 index 0000000..c11d673 --- /dev/null +++ b/packages/dim-si/src/specific-volume.ts @@ -0,0 +1,29 @@ +/** + * Specific volume units (M⁻¹·L³). + * + * SI unit: cubic meter per kilogram (m³/kg). + * + * @example Creating a specific volume + * ```ts + * import { cubicMeterPerKilogram } from "@isentropic/dim-si/specific-volume"; + * + * const sv = cubicMeterPerKilogram(0.001); + * ``` + * + * @module + */ + +import type { SpecificVolume as SpecificVolumeDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { specificVolume } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI specific volume quantity. */ +export type SpecificVolume = Linear; + +/** Cubic meter per kilogram (m³/kg) — SI unit of specific volume. */ +export const cubicMeterPerKilogram: BaseUnit = si.unit( + specificVolume, +); diff --git a/packages/dim-si/src/surface-tension.ts b/packages/dim-si/src/surface-tension.ts new file mode 100644 index 0000000..f070053 --- /dev/null +++ b/packages/dim-si/src/surface-tension.ts @@ -0,0 +1,29 @@ +/** + * Surface tension units (M·T⁻²). + * + * SI unit: newton per meter (N/m). + * + * @example Creating a surface tension + * ```ts + * import { newtonPerMeter } from "@isentropic/dim-si/surface-tension"; + * + * const water = newtonPerMeter(0.0728); + * ``` + * + * @module + */ + +import type { SurfaceTension as SurfaceTensionDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { surfaceTension } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI surface tension quantity. */ +export type SurfaceTension = Linear; + +/** Newton per meter (N/m) — SI unit of surface tension. */ +export const newtonPerMeter: BaseUnit = si.unit( + surfaceTension, +); diff --git a/packages/dim-si/src/thermal-conductivity.ts b/packages/dim-si/src/thermal-conductivity.ts new file mode 100644 index 0000000..64463a2 --- /dev/null +++ b/packages/dim-si/src/thermal-conductivity.ts @@ -0,0 +1,29 @@ +/** + * Thermal conductivity units (M·L·T⁻³·Θ⁻¹). + * + * SI unit: watt per meter kelvin (W/(m·K)). + * + * @example Creating a thermal conductivity + * ```ts + * import { wattPerMeterKelvin } from "@isentropic/dim-si/thermal-conductivity"; + * + * const copper = wattPerMeterKelvin(385); + * ``` + * + * @module + */ + +import type { ThermalConductivity as ThermalConductivityDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { thermalConductivity } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI thermal conductivity quantity. */ +export type ThermalConductivity = Linear; + +/** Watt per meter kelvin (W/(m·K)) — SI unit of thermal conductivity. */ +export const wattPerMeterKelvin: BaseUnit = si.unit( + thermalConductivity, +); diff --git a/packages/dim-si/src/torque.ts b/packages/dim-si/src/torque.ts new file mode 100644 index 0000000..d2ae598 --- /dev/null +++ b/packages/dim-si/src/torque.ts @@ -0,0 +1,30 @@ +/** + * Torque units (M·L²·T⁻²). + * + * SI unit: newton meter (N·m). + * + * Note: torque shares this dimension with energy (joule). This is + * intentional per SI — the type system cannot distinguish them. + * + * @example Creating a torque + * ```ts + * import { newtonMeter } from "@isentropic/dim-si/torque"; + * + * const tau = newtonMeter(50); + * ``` + * + * @module + */ + +import type { Torque as TorqueDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { torque } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI torque quantity. */ +export type Torque = Linear; + +/** Newton meter (N·m) — SI unit of torque. */ +export const newtonMeter: BaseUnit = si.unit(torque); diff --git a/packages/dim-si/src/velocity.ts b/packages/dim-si/src/velocity.ts index 67b42d1..776061b 100644 --- a/packages/dim-si/src/velocity.ts +++ b/packages/dim-si/src/velocity.ts @@ -20,12 +20,19 @@ import type { Velocity as VelocityDim } from "@isentropic/dim-isq"; import type { Linear } from "@isentropic/dim-unit"; import { velocity } from "@isentropic/dim-isq"; -import type { BaseUnit } from "./types.ts"; +import type { BaseUnit, ScaledUnit } from "./types.ts"; import type { Si } from "./system.ts"; import { si } from "./system.ts"; +import { kilometer } from "./length.ts"; +import { hour } from "./time.ts"; /** An SI velocity quantity. */ export type Velocity = Linear; /** Meter per second (m/s) — SI unit of velocity. */ export const meterPerSecond: BaseUnit = si.unit(velocity); + +/** Kilometer per hour (km/h) — 1/3.6 m/s. */ +export const kilometerPerHour: ScaledUnit = meterPerSecond.scaled( + kilometer.scale / hour.scale, +); diff --git a/packages/dim-si/src/volumetric-flow-rate.ts b/packages/dim-si/src/volumetric-flow-rate.ts new file mode 100644 index 0000000..6478f00 --- /dev/null +++ b/packages/dim-si/src/volumetric-flow-rate.ts @@ -0,0 +1,42 @@ +/** + * Volumetric flow rate units (L³·T⁻¹). + * + * SI unit: cubic meter per second (m³/s). + * + * @example Converting between flow rate units + * ```ts + * import { cubicMeterPerSecond, literPerMinute, literPerSecond } from "@isentropic/dim-si/volumetric-flow-rate"; + * import { valueIn } from "@isentropic/dim-si/ops"; + * + * const flow = cubicMeterPerSecond(1); + * valueIn(flow, literPerSecond); // 1000 + * valueIn(flow, literPerMinute); // 60000 + * ``` + * + * @module + */ + +import type { VolumetricFlowRate as VolumetricFlowRateDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { volumetricFlowRate } from "@isentropic/dim-isq"; +import type { BaseUnit, ScaledUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; +import { liter } from "./volume.ts"; +import { minute } from "./time.ts"; + +/** An SI volumetric flow rate quantity. */ +export type VolumetricFlowRate = Linear; + +/** Cubic meter per second (m³/s) — SI unit of volumetric flow rate. */ +export const cubicMeterPerSecond: BaseUnit = si.unit( + volumetricFlowRate, +); + +/** Liter per second (L/s) — 10⁻³ m³/s. */ +export const literPerSecond: ScaledUnit = + cubicMeterPerSecond.scaled(liter.scale); + +/** Liter per minute (L/min) — 10⁻³/60 m³/s. */ +export const literPerMinute: ScaledUnit = + cubicMeterPerSecond.scaled(liter.scale / minute.scale); diff --git a/packages/dim-si/src/wavenumber.ts b/packages/dim-si/src/wavenumber.ts new file mode 100644 index 0000000..cf0079e --- /dev/null +++ b/packages/dim-si/src/wavenumber.ts @@ -0,0 +1,27 @@ +/** + * Wavenumber units (L⁻¹). + * + * SI unit: reciprocal meter (m⁻¹). + * + * @example Creating a wavenumber + * ```ts + * import { reciprocalMeter } from "@isentropic/dim-si/wavenumber"; + * + * const k = reciprocalMeter(100); + * ``` + * + * @module + */ + +import type { Wavenumber as WavenumberDim } from "@isentropic/dim-isq"; +import type { Linear } from "@isentropic/dim-unit"; +import { wavenumber } from "@isentropic/dim-isq"; +import type { BaseUnit } from "./types.ts"; +import type { Si } from "./system.ts"; +import { si } from "./system.ts"; + +/** An SI wavenumber quantity. */ +export type Wavenumber = Linear; + +/** Reciprocal meter (m⁻¹) — SI unit of wavenumber. */ +export const reciprocalMeter: BaseUnit = si.unit(wavenumber); diff --git a/packages/dim-unit/README.md b/packages/dim-unit/README.md index c8d5d12..d67b670 100644 --- a/packages/dim-unit/README.md +++ b/packages/dim-unit/README.md @@ -178,6 +178,27 @@ The full set of linear/affine interactions: | Affine + Affine | Error | — | | scale/multiply/divide(Affine) | Error | — | +## Floating-Point Scale Composition + +When computing scale factors from other unit scales — especially with +exponentiation — IEEE 754 arithmetic can introduce small errors. For example, +`centimeter.scale ** 3` produces `1.0000000000000002e-6` instead of `1e-6`. + +The `roundScale` utility rounds a computed scale factor to 15 significant +digits, eliminating these artifacts while preserving all meaningful precision: + +```ts +import { roundScale } from "@isentropic/dim-unit"; + +// Without roundScale: gram.scale / centimeter.scale ** 3 = 999.9999999999999 +// With roundScale: 1000 +const gcm3 = kgPerM3.scaled(roundScale(gram.scale / centimeter.scale ** 3)); +``` + +Use `roundScale` only when the factor is computed from other scales and the +result has visible noise. Do not use it on direct expressions like `1000 / 3600` +that are already the best float64 approximation of the intended value. + ## Fluent API The `q()` chain tracks linear/affine state at the type level. `QLinear` supports diff --git a/packages/dim-unit/src/mod.ts b/packages/dim-unit/src/mod.ts index 49d022d..1e57093 100644 --- a/packages/dim-unit/src/mod.ts +++ b/packages/dim-unit/src/mod.ts @@ -19,6 +19,7 @@ */ export { defineUnitSystem } from "./system.ts"; +export { roundScale } from "./round-scale.ts"; export type { AffineUnit, BaseUnit, diff --git a/packages/dim-unit/src/round-scale.ts b/packages/dim-unit/src/round-scale.ts new file mode 100644 index 0000000..f70d7fa --- /dev/null +++ b/packages/dim-unit/src/round-scale.ts @@ -0,0 +1,31 @@ +/** + * Round a computed scale factor to eliminate IEEE 754 composition noise. + * + * When composing scale factors from unit properties (e.g., + * `gram.scale / centimeter.scale ** 3`), floating-point arithmetic can + * introduce small errors — `0.01 ** 3` produces `1.0000000000000002e-6` + * instead of `1e-6`. This function rounds to 15 significant digits, + * which preserves all meaningful precision while eliminating these + * artifacts. + * + * Use this when passing a computed expression to `.scaled()`. Do not + * use it on values that are already the best float64 approximation of + * the intended number (e.g., `1000 / 3600` for km/h) — rounding can + * make those less accurate. + * + * @param scale - The computed scale factor to round + * @returns The rounded scale factor + * + * @example Cleaning up composition noise + * ```ts + * import { roundScale } from "@isentropic/dim-unit"; + * + * // Without roundScale: 0.01 ** 3 = 1.0000000000000002e-6 + * // With roundScale: exactly 1e-6 + * const factor = roundScale(gram.scale / centimeter.scale ** 3); // 1000 + * const unit = baseUnit.scaled(factor); + * ``` + */ +export function roundScale(scale: number): number { + return parseFloat(scale.toPrecision(15)); +}