feat(gas-calculators): add a real-gas nitrox/trimix blender - #815
Conversation
The gas calculators cover analysis (MOD, best mix, END, consumption) but not blending -- "how do I fill this mix?" So add a sixth tab: a partial-pressure blender that outputs the fill order and the intermediate pressures to top up to. - Real-gas behaviour via Van der Waals virial coefficients (not ideal gas), so intermediate pressures are accurate at fill pressures. - Solves both nitrox (two-gas O2 balance) and trimix (three-gas He/N2/O2 system), with up to three configurable fill gases in a chosen order. - Reports why a blend is impossible (target not higher than start, invalid mix, identical gases, no helium source, negative amount). - Pressures respect the diver's pressure unit (bar/psi). The algorithm is ported from the open-source Blei-Log blender; the domain tests cross-check the Dart port against the reference implementation's intermediate pressures and volumes for EAN32 and Tmx 18/45. New localizations for all ten locales.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
📦 Build artifacts for this PR · commit
Artifacts expire in 7 days. Downloading requires being signed in to GitHub. The macOS build is ad-hoc signed — right-click → Open on first launch. Updated automatically on each push. |
There was a problem hiding this comment.
🟡 Not ready to approve
The nitrox solver currently rejects valid “no fillGas1 needed” cases (e.g., air→air top-ups), and the new UI’s unit formatting can become inconsistent if settings change while the tab is mounted.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
Adds a new “Blender” tab to the Gas Calculators feature, providing a real-gas partial-pressure blending tool (with localized UI) plus domain + widget tests.
Changes:
- Introduces real-gas blending math (
zFactor, volume/pressure conversions, andcomputeBlend) with structured results and typed error reasons. - Adds a new Gas Blender calculator UI tab wired into shared gas-calculator providers and reset-all behavior.
- Extends localization (ARB + generated
AppLocalizations*) and adds domain + widget test coverage for common blends and error paths.
File summaries
| File | Description |
|---|---|
| lib/features/gas_calculators/domain/gas_blender.dart | Implements real-gas blending solver + typed errors/results. |
| lib/features/gas_calculators/presentation/widgets/gas_blender_calculator.dart | New UI for blender inputs/results, unit-aware pressure display. |
| lib/features/gas_calculators/presentation/providers/gas_calculators_providers.dart | Adds blender state providers, computed outcome provider, and reset hook. |
| lib/features/gas_calculators/presentation/pages/gas_calculators_page.dart | Adds 6th tab and mounts the blender widget. |
| test/features/gas_calculators/domain/gas_blender_test.dart | Domain tests validating solver outputs vs reference and exercising error paths. |
| test/features/gas_calculators/gas_blender_calculator_widget_test.dart | Widget tests for default render, error rendering, and a trimix procedure. |
| lib/l10n/arb/app_en.arb | Adds Blender tab strings + placeholders (source locale). |
| lib/l10n/arb/app_de.arb | Adds German translations for blender strings. |
| lib/l10n/arb/app_ar.arb | Adds Arabic translations for blender strings. |
| lib/l10n/arb/app_es.arb | Adds Spanish translations for blender strings. |
| lib/l10n/arb/app_fr.arb | Adds French translations for blender strings. |
| lib/l10n/arb/app_he.arb | Adds Hebrew translations for blender strings. |
| lib/l10n/arb/app_hu.arb | Adds Hungarian translations for blender strings. |
| lib/l10n/arb/app_it.arb | Adds Italian translations for blender strings. |
| lib/l10n/arb/app_nl.arb | Adds Dutch translations for blender strings. |
| lib/l10n/arb/app_pt.arb | Adds Portuguese translations for blender strings. |
| lib/l10n/arb/app_zh.arb | Adds Chinese translations for blender strings. |
| lib/l10n/arb/app_localizations.dart | Generated base localization API extended with blender strings. |
| lib/l10n/arb/app_localizations_en.dart | Generated English localization implementation for blender strings. |
| lib/l10n/arb/app_localizations_de.dart | Generated German localization implementation for blender strings. |
| lib/l10n/arb/app_localizations_ar.dart | Generated Arabic localization implementation for blender strings. |
| lib/l10n/arb/app_localizations_es.dart | Generated Spanish localization implementation for blender strings. |
| lib/l10n/arb/app_localizations_fr.dart | Generated French localization implementation for blender strings. |
| lib/l10n/arb/app_localizations_he.dart | Generated Hebrew localization implementation for blender strings. |
| lib/l10n/arb/app_localizations_hu.dart | Generated Hungarian localization implementation for blender strings. |
| lib/l10n/arb/app_localizations_it.dart | Generated Italian localization implementation for blender strings. |
| lib/l10n/arb/app_localizations_nl.dart | Generated Dutch localization implementation for blender strings. |
| lib/l10n/arb/app_localizations_pt.dart | Generated Portuguese localization implementation for blender strings. |
| lib/l10n/arb/app_localizations_zh.dart | Generated Chinese localization implementation for blender strings. |
Review details
- Files reviewed: 29/29 changed files
- Comments generated: 3
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
| _units = UnitFormatter(ref.read(settingsProvider)); | ||
|
|
||
| String p(double bar) => _units.convertPressure(bar).toStringAsFixed(0); | ||
| String n(double v) => v.toStringAsFixed(0); |
| child: MaterialApp( | ||
| localizationsDelegates: AppLocalizations.localizationsDelegates, | ||
| supportedLocales: AppLocalizations.supportedLocales, | ||
| home: Scaffold( | ||
| body: Consumer( |
| if (top1 <= 0 || top2 < -0.01) { | ||
| throw const BlendException(BlendError.negativeAmountRequired); | ||
| } | ||
|
|
||
| final mix1 = _blend(gasI, iVol, g1, top1); | ||
| final p1 = pressureForVolume(mix1, iVol + top1); | ||
|
|
||
| steps | ||
| ..add( | ||
| BlendStep( | ||
| fillGas: g1, | ||
| pressureBar: p1, | ||
| resultingMix: mix1, | ||
| addedVolumePerLiter: top1, | ||
| ), | ||
| ) | ||
| ..add( | ||
| BlendStep( | ||
| fillGas: g2, | ||
| pressureBar: pf, | ||
| resultingMix: gasF, | ||
| addedVolumePerLiter: top2, | ||
| ), | ||
| ); | ||
| } |
What
The Gas Calculators cover analysis (MOD, best mix, END, gas consumption, rock bottom) but there's no blending tool — "how do I fill this mix?". This adds a sixth tab: a partial-pressure blender that outputs the fill order and the intermediate pressures to top up to.
Related community request: discussion is around blending; this is the fill-side companion to the existing analysis calculators.
Highlights
Implementation
domain/gas_blender.dart— pure, dependency-free math (zFactor,normalVolume,pressureForVolume,computeBlend) returning a structured fill procedure or a typedBlendError.presentation/widgets/gas_blender_calculator.dart— the tab UI (cylinder state, target, three fill gases, step-by-step result).GasCalculatorsPage; blender state + reset in the shared providers.The algorithm is ported from the open-source Blei-Log blender.
Tests
gas_blender_test.dartcross-checks the Dart port against the reference implementation's exact intermediate pressures and volumes (EAN32 nitrox and Tmx 18/45 trimix), plus every error path.gas_blender_calculator_widget_test.dartcovers the default nitrox render, the below-start-pressure error, and a trimix procedure.Verified locally (Flutter 3.44.8 / Dart 3.12.2):
flutter analyze libclean,dart formatclean,flutter gen-l10nrun, gas-calculator + arb-parity suites green. NewgasCalculators_blender_*strings are translated into all ten non-English locales.