Skip to main content
Glama
puran-water

Corrosion Engineering MCP Server

by puran-water
test_cross_validation_degasser.py7.96 kB
""" Cross-validation with degasser-design-mcp PHREEQC implementation. Tests that our PHREEQC backend produces consistent results with the degasser-design-mcp water chemistry module. Test cases: - Municipal water composition - Brackish water composition - Seawater composition - pH calculations - Ionic strength calculations """ import pytest import json from pathlib import Path import sys # Add parent directory to path sys.path.insert(0, str(Path(__file__).parent.parent)) from core.chemistry_backend import PHREEQCBackend from tools.chemistry.run_speciation import run_phreeqc_speciation class TestDegasserCrossValidation: """Cross-validation tests with degasser-design-mcp water chemistry""" def test_municipal_water_comparison(self): """ Test municipal water composition matches degasser-design-mcp defaults. From degasser-design-mcp/utils/water_chemistry.py: "municipal": { "Na+": 50.0, "Ca2+": 40.0, "Mg2+": 10.0, "K+": 5.0, "Cl-": 60.0, "SO4-2": 30.0, "HCO3-": 120.0, "NO3-": 10.0, } """ ions_json = json.dumps({ "Na+": 50.0, "Ca2+": 40.0, "Mg2+": 10.0, "K+": 5.0, "Cl-": 60.0, "SO4-2": 30.0, "HCO3-": 120.0, "NO3-": 10.0, }) result = run_phreeqc_speciation(ions_json, temperature_C=25.0) # Municipal water should have near-neutral pH assert 7.0 <= result["pH"] <= 8.5 # Low ionic strength assert result["ionic_strength_M"] < 0.01 # Charge balance should be reasonable (degasser template has ~7% imbalance) assert abs(result["charge_balance_percent"]) < 10.0 def test_brackish_water_comparison(self): """ Test brackish water composition matches degasser-design-mcp defaults. From degasser-design-mcp/utils/water_chemistry.py: "brackish": { "Na+": 1000.0, "Ca2+": 100.0, "Mg2+": 50.0, "K+": 20.0, "Cl-": 1500.0, "SO4-2": 200.0, "HCO3-": 200.0, } """ ions_json = json.dumps({ "Na+": 1000.0, "Ca2+": 100.0, "Mg2+": 50.0, "K+": 20.0, "Cl-": 1500.0, "SO4-2": 200.0, "HCO3-": 200.0, }) result = run_phreeqc_speciation(ions_json, temperature_C=25.0) # Brackish water should have near-neutral pH assert 6.5 <= result["pH"] <= 8.0 # Moderate ionic strength assert 0.01 < result["ionic_strength_M"] < 0.1 # Charge balance should be good assert abs(result["charge_balance_percent"]) < 5.0 def test_seawater_comparison(self): """ Test seawater composition matches degasser-design-mcp defaults. From degasser-design-mcp/utils/water_chemistry.py: "seawater": { "Na+": 10770.0, "Mg2+": 1290.0, "Ca2+": 412.0, "K+": 399.0, "Sr2+": 7.9, "Cl-": 19350.0, "SO4-2": 2712.0, "HCO3-": 142.0, "Br-": 67.0, "B(OH)4-": 4.5, "F-": 1.3, } """ ions_json = json.dumps({ "Na+": 10770.0, "Mg2+": 1290.0, "Ca2+": 412.0, "K+": 399.0, "Sr2+": 7.9, "Cl-": 19350.0, "SO4-2": 2712.0, "HCO3-": 142.0, "Br-": 67.0, "B(OH)4-": 4.5, "F-": 1.3, }) result = run_phreeqc_speciation(ions_json, temperature_C=25.0) # Seawater pH can vary assert 6.5 <= result["pH"] <= 8.5 # High ionic strength (~0.7 M) assert 0.5 <= result["ionic_strength_M"] <= 0.9 # Charge balance should be excellent assert abs(result["charge_balance_percent"]) < 2.0 def test_ion_mapping_consistency(self): """Test that ion mappings match degasser-design-mcp""" backend = PHREEQCBackend() # Test a few key mappings from degasser ION_MAPPING ions = { "Na+": 1000.0, "Ca2+": 100.0, "Cl-": 1500.0, "SO4-2": 200.0, "HCO3-": 200.0, } phreeqc_solution = backend.convert_to_phreeqc_solution(ions) # Check mappings match degasser-design-mcp assert phreeqc_solution["Na"] == 1000.0 # ("Na", 1.0) assert phreeqc_solution["Ca"] == 100.0 # ("Ca", 1.0) assert phreeqc_solution["Cl"] == 1500.0 # ("Cl", 1.0) # Sulfate: Convert SO4-2 to S(6) # degasser: ("S(6)", 96.06 / 32.07) expected_s6 = 200.0 / (96.06 / 32.07) assert abs(phreeqc_solution["S(6)"] - expected_s6) < 0.1 # Bicarbonate: Alkalinity # NOTE: degasser-design-mcp uses ("Alkalinity", 1.0) which is incorrect # per Codex review (BUG-006). We fixed it to convert HCO3- → CaCO3 equivalents. # Correct conversion: 200.0 / (61.02 / 50.0) = 163.9 mg/L as CaCO3 expected_alkalinity = 200.0 / (61.02 / 50.0) assert abs(phreeqc_solution["Alkalinity"] - expected_alkalinity) < 1.0 def test_charge_balance_calculation(self): """Test charge balance calculation matches degasser-design-mcp logic""" from core.chemistry_backend import calculate_charge_balance # Test with balanced NaCl solution # Na+: 1000 mg/L / 22.99 g/mol * 1 = 43.5 meq/L # Cl-: 1545 mg/L / 35.45 g/mol * 1 = 43.6 meq/L # Balance should be near zero ions = { "Na+": 1000.0, "Cl-": 1545.0, } balance = calculate_charge_balance(ions) assert abs(balance) < 1.0 # Test with imbalanced solution ions_imbalanced = { "Na+": 2000.0, # Excess cations "Cl-": 1000.0, } balance_imbalanced = calculate_charge_balance(ions_imbalanced) assert balance_imbalanced > 10.0 # Should be significantly positive class TestPHREEQCConsistency: """Test that PHREEQC produces consistent results""" def test_repeated_calls_consistency(self): """Test that repeated calls produce identical results""" ions_json = json.dumps({ "Na+": 1000.0, "Ca2+": 100.0, "Cl-": 1500.0, "HCO3-": 200.0, }) # Run speciation 3 times result1 = run_phreeqc_speciation(ions_json, temperature_C=25.0) result2 = run_phreeqc_speciation(ions_json, temperature_C=25.0) result3 = run_phreeqc_speciation(ions_json, temperature_C=25.0) # pH should be identical assert abs(result1["pH"] - result2["pH"]) < 1e-6 assert abs(result2["pH"] - result3["pH"]) < 1e-6 # Ionic strength should be identical assert abs(result1["ionic_strength_M"] - result2["ionic_strength_M"]) < 1e-9 assert abs(result2["ionic_strength_M"] - result3["ionic_strength_M"]) < 1e-9 def test_temperature_sensitivity(self): """Test that temperature affects results appropriately""" ions_json = json.dumps({ "Ca2+": 120.0, "HCO3-": 250.0, "Cl-": 150.0, "Na+": 100.0, }) result_25C = run_phreeqc_speciation(ions_json, temperature_C=25.0) result_60C = run_phreeqc_speciation(ions_json, temperature_C=60.0) # Temperature should affect saturation indices # (Retrograde solubility of CaCO3) si_25C = result_25C["saturation_indices"].get("Calcite", -999) si_60C = result_60C["saturation_indices"].get("Calcite", -999) # Higher temperature should increase SI for calcite assert si_60C > si_25C if __name__ == "__main__": pytest.main([__file__, "-v", "--tb=short"])

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/puran-water/corrosion-engineering-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server