From ee52216b6c0afb9ed2b4ce8e58e899cd95e519ea Mon Sep 17 00:00:00 2001 From: Greg Troszak Date: Sun, 15 Mar 2026 15:22:02 -0400 Subject: [PATCH 1/3] feat(dim-unit): add roundScale utility for IEEE 754 composition noise Composing scale factors from unit properties (e.g., gram.scale / centimeter.scale ** 3) can introduce floating-point artifacts. roundScale rounds to 15 significant digits to eliminate these while preserving all meaningful precision. Exported from the package root for use at call sites where composition noise is visible. --- packages/dim-unit/README.md | 21 +++++++++++++++++++ packages/dim-unit/src/mod.ts | 1 + packages/dim-unit/src/round-scale.ts | 31 ++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 packages/dim-unit/src/round-scale.ts 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)); +} From d43bba51f6c17b93a594505e68fad1f2ae4dac6c Mon Sep 17 00:00:00 2001 From: Greg Troszak Date: Sun, 15 Mar 2026 15:22:12 -0400 Subject: [PATCH 2/3] feat(dim-si): add SI unit modules for new quantities Adds unit modules for density, specificVolume, momentum, angularVelocity, angularAcceleration, torque, dynamicViscosity, kinematicViscosity, surfaceTension, wavenumber, heatCapacity, specificHeatCapacity, specificEnergy, thermalConductivity, electricFieldStrength, permittivity, permeability, currentDensity, volumetricFlowRate, massFlowRate, concentration, and molarMass. Also adds non-trivial units to existing modules: kilometerPerHour (velocity), ampereHour/milliampereHour (charge), atmosphere (pressure), squareKilometer/squareCentimeter (area). Closes #11 --- packages/dim-si/README.md | 70 ++++++++++++------- packages/dim-si/deno.json | 24 ++++++- packages/dim-si/src/angular-acceleration.ts | 29 ++++++++ packages/dim-si/src/angular-velocity.ts | 39 +++++++++++ packages/dim-si/src/area.ts | 11 +++ packages/dim-si/src/charge.ts | 14 +++- packages/dim-si/src/concentration.ts | 36 ++++++++++ packages/dim-si/src/current-density.ts | 29 ++++++++ packages/dim-si/src/density.ts | 44 ++++++++++++ packages/dim-si/src/dynamic-viscosity.ts | 29 ++++++++ .../dim-si/src/electric-field-strength.ts | 29 ++++++++ packages/dim-si/src/heat-capacity.ts | 27 +++++++ packages/dim-si/src/kinematic-viscosity.ts | 29 ++++++++ packages/dim-si/src/mass-flow-rate.ts | 29 ++++++++ packages/dim-si/src/molar-mass.ts | 35 ++++++++++ packages/dim-si/src/momentum.ts | 27 +++++++ packages/dim-si/src/permeability.ts | 27 +++++++ packages/dim-si/src/permittivity.ts | 27 +++++++ packages/dim-si/src/pressure.ts | 3 + packages/dim-si/src/specific-energy.ts | 33 +++++++++ packages/dim-si/src/specific-heat-capacity.ts | 28 ++++++++ packages/dim-si/src/specific-volume.ts | 29 ++++++++ packages/dim-si/src/surface-tension.ts | 29 ++++++++ packages/dim-si/src/thermal-conductivity.ts | 29 ++++++++ packages/dim-si/src/torque.ts | 30 ++++++++ packages/dim-si/src/velocity.ts | 9 ++- packages/dim-si/src/volumetric-flow-rate.ts | 42 +++++++++++ packages/dim-si/src/wavenumber.ts | 27 +++++++ 28 files changed, 787 insertions(+), 27 deletions(-) create mode 100644 packages/dim-si/src/angular-acceleration.ts create mode 100644 packages/dim-si/src/angular-velocity.ts create mode 100644 packages/dim-si/src/concentration.ts create mode 100644 packages/dim-si/src/current-density.ts create mode 100644 packages/dim-si/src/density.ts create mode 100644 packages/dim-si/src/dynamic-viscosity.ts create mode 100644 packages/dim-si/src/electric-field-strength.ts create mode 100644 packages/dim-si/src/heat-capacity.ts create mode 100644 packages/dim-si/src/kinematic-viscosity.ts create mode 100644 packages/dim-si/src/mass-flow-rate.ts create mode 100644 packages/dim-si/src/molar-mass.ts create mode 100644 packages/dim-si/src/momentum.ts create mode 100644 packages/dim-si/src/permeability.ts create mode 100644 packages/dim-si/src/permittivity.ts create mode 100644 packages/dim-si/src/specific-energy.ts create mode 100644 packages/dim-si/src/specific-heat-capacity.ts create mode 100644 packages/dim-si/src/specific-volume.ts create mode 100644 packages/dim-si/src/surface-tension.ts create mode 100644 packages/dim-si/src/thermal-conductivity.ts create mode 100644 packages/dim-si/src/torque.ts create mode 100644 packages/dim-si/src/volumetric-flow-rate.ts create mode 100644 packages/dim-si/src/wavenumber.ts 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/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/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); From 8bc5b56539623275a0e1a7d088c0f8247708a42c Mon Sep 17 00:00:00 2001 From: Greg Troszak Date: Sun, 15 Mar 2026 15:22:18 -0400 Subject: [PATCH 3/3] docs(dim-si): align shared-dimension note phrasing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ' — the type system cannot distinguish them' to the shared-dimension notes in frequency.ts and absorbed-dose.ts, matching the phrasing used in the new angular-velocity, torque, and specific-energy modules. --- packages/dim-si/src/absorbed-dose.ts | 3 ++- packages/dim-si/src/frequency.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) 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/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