Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* Added `EnergyDensity`, a new linear quantity for energy per unit volume (dimensionally equivalent to `Pressure`), supporting `J/m³`, `J/L`, and `Wh/L`, with `EnergyDensity.from(Energy, Volume)`, `energyOf(Volume)`, and `volumeFor(Energy)` dimensional-analysis helpers, plus a granular `energy_density.dart` entry point.
* Added the `VolumetricFlowRate` linear quantity (`m³/s`, `m³/min`, `m³/h`, `L/s`, `L/min`, `L/h`, `mL/min`, `ft³/s`, `ft³/min`, `gal/min`, `gal/h`), with `VolumetricFlowRate.from(Volume, Time)`, `volumeOver(Time)`, and `timeFor(Volume)` dimensional-analysis bridges.
* Added a granular entry point for the new family: `volumetric_flow_rate.dart`.

Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ The library supports a comprehensive range of physical quantities, including all
| **Torque** | ✅ | **`N·m`** (newton-meter), `mN·m`, `kN·m`, `MN·m`, `lbf·ft`, `lbf·in`, `kgf·m`, `ozf·in`, `dyn·cm` | Derived SI: N·m. Dimensionally = Energy but type-distinct. |
| **Power** | ✅ | **`W`** (Watt), `mW`, `kW`, `MW`, `GW`, `TW`, `μW`, `nW`, `hp`, `PS` (metric hp), `Btu/h`, `erg/s` | Derived SI: J/s |
| **Density** | ✅ | **`kg/m³`** (kilogram per cubic meter), `g/cm³`, `g/mL` | Derived SI: kg/m³ |
| **Energy Density** | ✅ | **`J/m³`** (joule per cubic meter), `J/L`, `Wh/L` | Derived SI: J/m³. Dimensionally = Pressure but type-distinct. |
| **Specific Energy** | ✅ | **`J/kg`** (joule per kilogram), `kJ/kg`, `Wh/kg`, `kWh/kg` | Derived SI: J/kg |
| **Information** | ✅ | **`bit`**, `B` (byte); SI bits: `kbit`…`Ybit`; SI bytes: `kB`…`YB`; IEC binary: `KiB`…`YiB` | IEC 80000-13. Three tracks: SI bit, SI byte, IEC binary. |
| **Voltage** | ✅ | **`V`** (volt), `nV`, `µV`, `mV`, `kV`, `MV`, `GV`, `statV`, `abV` | Derived SI: W/A. Also: `ElectricPotential` type alias. |
Expand Down Expand Up @@ -421,6 +422,10 @@ final angle = av.totalAngleOver(2.minutes); // 2.0 revolutions
final density = Density.from(13.5.g, 1.0.cm3); // 13.5 g/cm³ ← result in g/cm³
final mass = density.massOf(10.cm3); // 135.0 g ← result in g

// EnergyDensity = Energy / Volume ↔ energy/volume retrieval
final energyDensity = EnergyDensity.from(2.Wh, 1.0.L); // 2.0 Wh/L ← result in Wh/L
final storedEnergy = energyDensity.energyOf(5.L); // 10.0 Wh ← result in Wh

// VolumetricFlowRate = Volume / Time ↔ Volume = FlowRate × Time
final flow = VolumetricFlowRate.from(120.L, 1.minutes); // 120.0 L/min
final filled = flow.volumeOver(5.minutes); // 600.0 L
Expand Down
10 changes: 10 additions & 0 deletions lib/energy_density.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/// Exports the 'EnergyDensity' quantity, its units, and convenience extensions.
///
/// This library provides all necessary components to work with energy density
/// conversions and calculations.
library;

export 'src/units/energy_density/energy_density.dart';
export 'src/units/energy_density/energy_density_extensions.dart';
export 'src/units/energy_density/energy_density_factors.dart';
export 'src/units/energy_density/energy_density_unit.dart';
4 changes: 4 additions & 0 deletions lib/quantify.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ export 'src/units/electric_charge/electric_charge_unit.dart';
export 'src/units/energy/energy.dart';
export 'src/units/energy/energy_extensions.dart';
export 'src/units/energy/energy_unit.dart';
// Exporting units and their extensions for energy density
export 'src/units/energy_density/energy_density.dart';
export 'src/units/energy_density/energy_density_extensions.dart';
export 'src/units/energy_density/energy_density_unit.dart';
// Exporting units and their extensions for force
export 'src/units/force/force.dart';
export 'src/units/force/force_extensions.dart';
Expand Down
139 changes: 139 additions & 0 deletions lib/src/units/energy_density/energy_density.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import 'package:meta/meta.dart';

import '../../core/linear_quantity.dart';
import '../../core/quantity_format.dart';
import '../../core/quantity_parser.dart';
import '../energy/energy.dart';
import '../energy/energy_extensions.dart';
import '../energy/energy_unit.dart';
import '../volume/volume.dart';
import '../volume/volume_extensions.dart';
import '../volume/volume_unit.dart';
import 'energy_density_unit.dart';

/// Represents a quantity of energy density.
///
/// Energy density is a physical quantity representing the amount of energy
/// stored per unit volume. It is dimensionally identical to Pressure
/// (J/m³ ≡ Pa), but is modeled as a distinct type to preserve semantic
/// meaning. The SI derived unit is Joule per Cubic Meter (J/m³).
@immutable
final class EnergyDensity extends LinearQuantity<EnergyDensityUnit, EnergyDensity> {
/// Creates a new `EnergyDensity` with a given [value] and [unit].
const EnergyDensity(super._value, super._unit);

/// Creates an `EnergyDensity` from [energy] and [volume] (u = E / V).
///
/// If the combination of [energy]'s unit and [volume]'s unit matches a
/// standard energy density unit (J + m³ → J/m³, J + L → J/L, Wh + L → Wh/L),
/// the result uses that unit. Otherwise the result is in
/// [EnergyDensityUnit.joulePerCubicMeter].
/// If [volume] is zero, the result follows IEEE 754 semantics.
///
/// ```dart
/// EnergyDensity.from(1000.J, 2.m3); // 500.0 J/m³
/// EnergyDensity.from(500.J, 1.0.L); // 500.0 J/L
/// EnergyDensity.from(1.Wh, 1.0.L); // 1.0 Wh/L
/// ```
factory EnergyDensity.from(Energy energy, Volume volume) {
final target = _correspondingEnergyDensityUnit(energy.unit, volume.unit);
if (target != null) return EnergyDensity(energy.value / volume.value, target);
return EnergyDensity(
energy.inJoules / volume.inCubicMeters,
EnergyDensityUnit.joulePerCubicMeter,
);
}

/// Maps an [EnergyUnit] × [VolumeUnit] pair to its natural [EnergyDensityUnit].
static EnergyDensityUnit? _correspondingEnergyDensityUnit(EnergyUnit e, VolumeUnit v) =>
switch ((e, v)) {
(EnergyUnit.joule, VolumeUnit.cubicMeter) => EnergyDensityUnit.joulePerCubicMeter,
(EnergyUnit.joule, VolumeUnit.litre) => EnergyDensityUnit.joulePerLiter,
(EnergyUnit.wattHour, VolumeUnit.litre) => EnergyDensityUnit.wattHourPerLiter,
_ => null,
};

@override
@protected
EnergyDensity create(double value, EnergyDensityUnit unit) => EnergyDensity(value, unit);

/// The parser instance used to convert strings into [EnergyDensity] objects.
///
/// The parser supports both strict symbol aliases and case-insensitive name
/// aliases configured in [EnergyDensityUnit].
static final QuantityParser<EnergyDensityUnit, EnergyDensity> parser =
QuantityParser<EnergyDensityUnit, EnergyDensity>(
symbolAliases: EnergyDensityUnit.symbolAliases,
nameAliases: EnergyDensityUnit.nameAliases,
factory: EnergyDensity.new,
);

/// Parses a string representation of energy density into an [EnergyDensity] object.
///
/// The [formats] list controls how the numeric portion is interpreted.
static EnergyDensity parse(
String input, {
List<QuantityFormat> formats = const [QuantityFormat.invariant],
}) {
return parser.parse(input, formats: formats);
}

/// Parses a string representation of energy density into an [EnergyDensity] object,
/// returning `null` when parsing fails.
///
/// See [parse] for formatting and matching behavior.
static EnergyDensity? tryParse(
String input, {
List<QuantityFormat> formats = const [QuantityFormat.invariant],
}) {
return parser.tryParse(input, formats: formats);
}

// --- Dimensional Analysis ---

/// Calculates the [Energy] stored in a given [Volume] at this energy density.
///
/// The result's unit matches the energy component of this energy density's
/// unit: `J/m³` → joules, `J/L` → joules, `Wh/L` → watt-hours.
///
/// ```dart
/// final density = 500.joulesPerLiter;
/// density.energyOf(2.0.L); // 1000.0 J
/// ```
Energy energyOf(Volume volume) {
final energyUnit = _correspondingEnergyUnit(unit);
final volUnit = _correspondingVolumeUnit(unit);
return Energy(value * volume.getValue(volUnit), energyUnit);
}

/// Calculates the [Volume] required to store a given amount of [Energy]
/// at this energy density.
///
/// The result's unit matches the volume component of this energy density's
/// unit: `J/m³` → m³, `J/L` → L, `Wh/L` → L.
/// If the energy density is zero, the result follows IEEE 754 semantics.
///
/// ```dart
/// final density = 500.joulesPerLiter;
/// density.volumeFor(1000.J); // 2.0 L
/// ```
Volume volumeFor(Energy energy) {
final energyUnit = _correspondingEnergyUnit(unit);
final volUnit = _correspondingVolumeUnit(unit);
return Volume(energy.getValue(energyUnit) / value, volUnit);
}

/// Maps an [EnergyDensityUnit] to its energy component unit.
static EnergyUnit _correspondingEnergyUnit(EnergyDensityUnit u) => switch (u) {
EnergyDensityUnit.joulePerCubicMeter => EnergyUnit.joule,
EnergyDensityUnit.joulePerLiter => EnergyUnit.joule,
EnergyDensityUnit.wattHourPerLiter => EnergyUnit.wattHour,
};

/// Maps an [EnergyDensityUnit] to its volume component unit.
static VolumeUnit _correspondingVolumeUnit(EnergyDensityUnit u) => switch (u) {
EnergyDensityUnit.joulePerCubicMeter => VolumeUnit.cubicMeter,
EnergyDensityUnit.joulePerLiter => VolumeUnit.litre,
EnergyDensityUnit.wattHourPerLiter => VolumeUnit.litre,
};
}
40 changes: 40 additions & 0 deletions lib/src/units/energy_density/energy_density_extensions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'energy_density.dart';
import 'energy_density_unit.dart';

/// Provides convenient access to [EnergyDensity] values in specific units.
extension EnergyDensityValueGetters on EnergyDensity {
/// Returns the energy density value in Joules per Cubic Meter (J/m³).
double get inJoulesPerCubicMeter => getValue(EnergyDensityUnit.joulePerCubicMeter);

/// Returns the energy density value in Joules per Liter (J/L).
double get inJoulesPerLiter => getValue(EnergyDensityUnit.joulePerLiter);

/// Returns the energy density value in Watt-hours per Liter (Wh/L).
double get inWattHoursPerLiter => getValue(EnergyDensityUnit.wattHourPerLiter);

/// Returns a new [EnergyDensity] object representing this energy density in
/// Joules per Cubic Meter (J/m³).
EnergyDensity get asJoulesPerCubicMeter => convertTo(EnergyDensityUnit.joulePerCubicMeter);

/// Returns a new [EnergyDensity] object representing this energy density in
/// Joules per Liter (J/L).
EnergyDensity get asJoulesPerLiter => convertTo(EnergyDensityUnit.joulePerLiter);

/// Returns a new [EnergyDensity] object representing this energy density in
/// Watt-hours per Liter (Wh/L).
EnergyDensity get asWattHoursPerLiter => convertTo(EnergyDensityUnit.wattHourPerLiter);
}

/// Provides convenient factory methods for creating [EnergyDensity] instances from [num].
extension EnergyDensityCreation on num {
/// Creates an [EnergyDensity] instance from this value in Joules per Cubic Meter (J/m³).
EnergyDensity get joulesPerCubicMeter =>
EnergyDensity(toDouble(), EnergyDensityUnit.joulePerCubicMeter);

/// Creates an [EnergyDensity] instance from this value in Joules per Liter (J/L).
EnergyDensity get joulesPerLiter => EnergyDensity(toDouble(), EnergyDensityUnit.joulePerLiter);

/// Creates an [EnergyDensity] instance from this value in Watt-hours per Liter (Wh/L).
EnergyDensity get wattHoursPerLiter =>
EnergyDensity(toDouble(), EnergyDensityUnit.wattHourPerLiter);
}
16 changes: 16 additions & 0 deletions lib/src/units/energy_density/energy_density_factors.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/// Defines base conversion factors for various energy density units relative to
/// the Joule per Cubic Meter (J/m³), which is the SI derived unit for energy
/// density.
///
/// These constants represent: `1 [Unit] = Z [Joules Per Cubic Meter]`.
class EnergyDensityFactors {
/// Joules per Cubic Meter: 1 J/m³ = 1.0 J/m³.
static const double joulePerCubicMeter = 1;

/// Joules per Liter: 1 J/L = 1000 J/m³ (since 1 L = 0.001 m³).
static const double joulePerLiter = 1000;

/// Watt-hours per Liter: 1 Wh/L = 3,600,000 J/m³
/// (since 1 Wh = 3600 J and 1 L = 0.001 m³, so 1 Wh/L = 3600 J / 0.001 m³).
static const double wattHourPerLiter = 3600000;
}
78 changes: 78 additions & 0 deletions lib/src/units/energy_density/energy_density_unit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import 'package:meta/meta.dart';

import '../../core/unit.dart';
import 'energy_density_factors.dart';

/// Represents units of energy density.
///
/// This enum implements the [LinearUnit] interface to provide conversion capabilities
/// and a display [symbol] for each energy density unit.
/// All conversion factors are pre-calculated in the constructor relative to
/// Joule per Cubic Meter (J/m³).
enum EnergyDensityUnit implements LinearUnit<EnergyDensityUnit> {
/// Joule per Cubic Meter (J/m³), the SI derived unit of energy density.
joulePerCubicMeter(EnergyDensityFactors.joulePerCubicMeter, 'J/m³'),

/// Joule per Liter (J/L), a common unit for energy density of fuels and batteries.
joulePerLiter(EnergyDensityFactors.joulePerLiter, 'J/L'),

/// Watt-hour per Liter (Wh/L), commonly used for battery energy density.
wattHourPerLiter(EnergyDensityFactors.wattHourPerLiter, 'Wh/L');

/// Constant constructor for enum members.
const EnergyDensityUnit(double toJPerM3Factor, this.symbol)
: _toJPerM3Factor = toJPerM3Factor,
_factorToJoulePerCubicMeter = toJPerM3Factor / 1.0,
_factorToJoulePerLiter = toJPerM3Factor / EnergyDensityFactors.joulePerLiter,
_factorToWattHourPerLiter = toJPerM3Factor / EnergyDensityFactors.wattHourPerLiter;

/// SI and unit symbols matched **strictly case-sensitive**.
static const Map<String, EnergyDensityUnit> symbolAliases = {
'J/m³': joulePerCubicMeter,
'J/L': joulePerLiter,
'Wh/L': wattHourPerLiter,
};

/// Full word-form names and non-SI abbreviations matched **case-insensitively**.
static const Map<String, EnergyDensityUnit> nameAliases = {
'joule per cubic meter': joulePerCubicMeter,
'joule per cubic metre': joulePerCubicMeter,
'joulepercubicmeter': joulePerCubicMeter,
'j per m3': joulePerCubicMeter,
'jperm3': joulePerCubicMeter,
'joule per liter': joulePerLiter,
'joule per litre': joulePerLiter,
'jouleperliter': joulePerLiter,
'j per l': joulePerLiter,
'jperl': joulePerLiter,
'watt hour per liter': wattHourPerLiter,
'watt hour per litre': wattHourPerLiter,
'watthourperliter': wattHourPerLiter,
'wh per l': wattHourPerLiter,
'whperl': wattHourPerLiter,
};

// ignore: unused_field // Used to store the conversion factor to J/m³.
final double _toJPerM3Factor;

@override
final String symbol;

// --- Pre-calculated direct conversion factors ---
final double _factorToJoulePerCubicMeter;
final double _factorToJoulePerLiter;
final double _factorToWattHourPerLiter;

@override
@internal
double factorTo(EnergyDensityUnit targetUnit) {
switch (targetUnit) {
case EnergyDensityUnit.joulePerCubicMeter:
return _factorToJoulePerCubicMeter;
case EnergyDensityUnit.joulePerLiter:
return _factorToJoulePerLiter;
case EnergyDensityUnit.wattHourPerLiter:
return _factorToWattHourPerLiter;
}
}
}
Loading
Loading