8000 fix: chemistry without unassurable gates (G1-G5, G8, G10, G11) by rafatahmed · Pull Request #219 · rafatahmed/Flahax · GitHub
[go: up one dir, main page]

Skip to content

fix: chemistry without unassurable gates (G1-G5, G8, G10, G11) - #219

Merged
rafatahmed merged 5 commits into
mainfrom
fix/chemistry-without-unassurable-gates
Nov 5, 2026
Merged

rafatahmed merged 5 commits into
mainfrom
fix/chemistry-without-unassurable-gates

Conversation

@rafatahmed
Copy link
Copy Markdown
Owner

Closes #214
Closes #215
Closes #216

Summary

 balance_water_analysis(totals, measured_ph, alkalinity)
-  close the charge on Cl / Na / carbonate
-  reject an imbalance above 5 %
+  keep the totals as measured
+  return residual_charge_eq + imbalance_percent

 calculate_equilibrium_ph(totals, ..., residual_charge)
-  assume the declared ions are complete (complete_analysis)
+  solve charge_balance - residual_charge = 0

 fertilizer_workflow.run
   chemistry / ph_adjustment
-    missing HCO3          -> blocked
+    missing HCO3          -> needs_input  "measure alkalinity (HCO3) of the water"
-    ionic strength > 0.1  -> blocked
+    ionic strength > 0.1  -> not_calculated

 product conversion
-  refuse a product whose label is off by more than 1 % of charge
+  label_reconciliation.chemistry_elements
+    unassigned charge is reported; chemistry uses label minus it
  • Water residual charge is carried through the pH solve, the EC solve, the acid-dose search and the joint search.
  • The formula's own ion imbalance is reported as a note, not a warning with a 10 % threshold.
  • Three stated model conditions (closed carbon, 25 °C, 1 L = 1 kg water) appear in the summary assumptions.

Evidence

  • Before: a well water without HCO3 gave stage blocked (missing_carbon); a 7 % cation excess was refused (charge_imbalance_exceeded); an imbalanced water forced the EC solve to pH 10.16.
    After: the same water gives needs_input with the recipe still produced; the 7 % water is accepted with imbalance_percent ≈ 7.0; the EC solve carries the residual charge and lands within 1e-6 pH of the equilibrium solve.
test_a_seven_percent_imbalance_is_accepted_and_reported
  balance_water_analysis(Na 1.1505e-3, Cl 1.0e-3, pH 7.0).imbalance_percent == 7.0  (±0.01)

test_the_ec_ph_solve_carries_the_waters_residual_charge
  forced zero charge pH - carried pH > 1

test_a_water_without_alkalinity_asks_for_it_and_still_gives_the_recipe
  chemistry.status == needs_input, formulation.salts non-empty, no stage blocked
  • New test files: test_ph_inputs, test_charge_balance_reporting, test_label_reconciliation; older tests moved to the new contract.
  • Local runs: style clean (ruff 0.8.4); full suite green in four shards, apart from test_file_headers failures caused by a stray untracked examples/tutorial/0.3.0/.venv on this machine.
  • The full pre-push gate (coverage comparison against main, wheel/sdist) was not run locally; CI is the first run of it.

Merge Danger

Door: one-way for API and CLI consumers.

Removed public names: complete_analysis, and the balance_water_analysis parameters balance_ion, maximum_imbalance_percent, 8000 balance_ion_uncertainty_percent, fixed. A water file that sets balance_ion is now refused with unknown_field.

Blast Radius: API and saved water files

  • Results change for any water that was previously closed on Cl/Na/carbonate: totals are now as measured, so pH, EC and doses can move.
  • test_product_phreeqc is patched to bypass label reconciliation, so its captured reference only proves the pre-reconciliation conversion (assertions unchanged). Re-capturing the fixture is a follow-up decision.
  • BalancedWaterAnalysis keeps now-unused fields (balance_ion, balance_adjustment_molal); removing them would be another public-API change.
  • Lettuce 2 still reports an overall blocked summary until the three-tank and saturation work in later PRs lands.

…as measured

Product chemistry (#214, G5): a label is reconciled with its compound. The charge the
compound's ions cannot balance is reported and left out of the chemistry; the nutrient fit keeps
the label. The 1 % label charge gate and its refusals are removed.

pH inputs (#215, G1-G4): calculate_equilibrium_ph drops complete_analysis and takes
residual_charge. A water without HCO3 or alkalinity gives needs_input, an ionic strength above
0.1 gives not_calculated, and the recipe is produced in both cases. The summary states closed
carbon, 25 C only and 1 L = 1 kg water.

Charge balance (#216, G10, G11): a water analysis is accepted as measured and its imbalance is
carried as a residual charge. Removed: the 5 % rejection, closing on Cl, Na or carbonate, the
0.1 % initial-charge gates of the acid and delivery planners, and the 10 % formula-imbalance
flag. Carbon from a titrated alkalinity stays.

Adds GLOSSARY.md and ADR 0001.
…nt search out of range

calculate_ec_from_totals takes residual_charge_eq_per_kgw for its calculated-pH mode, so an
imbalanced water is no longer forced to zero charge there. A reagent search that leaves the
activity model's range gives the pH-adjustment stage not_calculated instead of blocked. The
planning prompt no longer names complete_analysis.
@rafatahmed rafatahmed added the bug Something isn't working label Nov 5, 2026
@rafatahmed rafatahmed self-assigned this Nov 5, 2026
@read-the-docs-community
read-the-docs-community Bot commented Nov 5, 2026 •
Copy link
Copy Markdown

@codecov
codecov Bot commented Nov 5, 2026 •
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (3eef252) to head (debbb08).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #219   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          113       114    +1     
  Lines        13191     13186    -5     
  Branches      2617      2599   -18     
=========================================
- Hits        
8000
 13191     13186    -5     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rafatahmed
rafatahmed marked this pull request as ready for review November 5, 2026 17:41
@rafatahmed
rafatahmed merged commit 641e125 into main Nov 5, 2026
13 checks passed
@rafatahmed
rafatahmed deleted the fix/chemistry-without-unassurable-gates branch November 5, 2026 17:41
rafatahmed added a commit that referenced this pull request Nov 7, 2026
…able-gates

fix: chemistry without unassurable gates (G1-G5, G8, G10, G11)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

1 participant

0