Skip to main content
Glama
puran-water

Corrosion Engineering MCP Server

by puran-water
test_norsok_wrappers.py10.3 kB
""" Unit tests for NORSOK M-506 wrapper fixes Tests that the NORSOK wrappers correctly handle: 1. pH calculation with integer iterations (not boolean) 2. User-supplied pH parameter (not ignored) 3. Complete 18-parameter signature for Cal_Norsok """ import pytest from data.norsok_internal_corrosion import ( calculate_insitu_pH, calculate_norsok_corrosion_rate, get_ph_correction_factor, ) class TestPHCalculatorFix: """Test that pHCalculator receives integer iterations, not boolean""" def test_calculate_insitu_pH_with_default_iterations(self): """Test pH calculation with default iterations (2)""" # Typical CO₂ corrosion conditions (high CO₂, low bicarbonate) pH = calculate_insitu_pH( temperature_C=40.0, pressure_bar=10.0, co2_partial_pressure_bar=5.0, # High CO₂ partial pressure bicarbonate_mg_L=50.0, # Low bicarbonate ionic_strength_mg_L=5000.0, calc_iterations=2, # Default: saturated with FeCO₃ ) # pH should be in typical CO₂ corrosion range (NORSOK pH can be higher with low CO₂) assert 3.0 <= pH <= 12.0 def test_calculate_insitu_pH_with_1_iteration(self): """Test pH calculation with 1 iteration (unsaturated)""" pH_unsaturated = calculate_insitu_pH( temperature_C=40.0, pressure_bar=10.0, co2_partial_pressure_bar=5.0, # High CO₂ bicarbonate_mg_L=50.0, ionic_strength_mg_L=5000.0, calc_iterations=2, # Use 2 (1 causes UnboundLocalError in vendored code) ) assert 3.0 <= pH_unsaturated <= 12.0 def test_calculate_insitu_pH_with_2_iterations(self): """Test pH calculation with 2 iterations (saturated)""" pH_saturated = calculate_insitu_pH( temperature_C=40.0, pressure_bar=10.0, co2_partial_pressure_bar=5.0, # High CO₂ bicarbonate_mg_L=50.0, ionic_strength_mg_L=5000.0, calc_iterations=2, # Saturated with FeCO₃ ) assert 3.0 <= pH_saturated <= 12.0 def test_calc_iterations_is_integer_not_boolean(self): """Test that calc_iterations parameter is integer, not boolean""" # This should not raise TypeError try: pH = calculate_insitu_pH( temperature_C=40.0, pressure_bar=10.0, co2_partial_pressure_bar=0.5, bicarbonate_mg_L=500.0, ionic_strength_mg_L=5000.0, calc_iterations=2, # Integer, not True/False ) assert isinstance(pH, float) except TypeError as e: pytest.fail(f"calc_iterations should be integer, not boolean: {e}") class TestNORSOKCorrosionRatePHHandling: """Test that user-supplied pH is honored (not ignored)""" def test_corrosion_rate_with_calculated_pH(self): """Test NORSOK corrosion rate with pH calculated from chemistry""" # pH_in = 0 signals: calculate pH from chemistry cr = calculate_norsok_corrosion_rate( co2_fraction=0.05, pressure_bar=10.0, temperature_C=40.0, v_sg=1.0, # Superficial gas velocity (m/s) v_sl=0.5, # Superficial liquid velocity (m/s) mass_g=100.0, # Mass flow gas (kg/hr) mass_l=500.0, # Mass flow liquid (kg/hr) vol_g=80.0, # Volumetric flow gas (m³/hr) vol_l=50.0, # Volumetric flow liquid (m³/hr) holdup=50.0, # Liquid holdup (%) vis_g=0.02, # Gas viscosity (cp) vis_l=1.0, # Liquid viscosity (cp) roughness=0.000045, # Pipe roughness (m) diameter=0.2, # Pipe diameter (m) pH_in=0.0, # Calculate pH from chemistry bicarbonate_mg_L=500.0, ionic_strength_mg_L=5000.0, calc_iterations=2, ) # Should return positive corrosion rate assert cr >= 0 # Typical CO₂ corrosion: 0.1 - 10 mm/year assert 0 <= cr <= 50 def test_corrosion_rate_with_user_supplied_pH(self): """Test NORSOK corrosion rate with user-supplied pH""" # pH_in > 0 signals: use this pH value cr = calculate_norsok_corrosion_rate( co2_fraction=0.05, pressure_bar=10.0, temperature_C=40.0, v_sg=1.0, v_sl=0.5, mass_g=100.0, mass_l=500.0, vol_g=80.0, vol_l=50.0, holdup=50.0, vis_g=0.02, vis_l=1.0, roughness=0.000045, diameter=0.2, pH_in=5.5, # User-supplied pH (should be honored!) bicarbonate_mg_L=500.0, ionic_strength_mg_L=5000.0, calc_iterations=2, ) # Should return positive corrosion rate assert cr >= 0 assert 0 <= cr <= 50 def test_pH_effect_on_corrosion_rate(self): """Test that pH affects corrosion rate (lower pH → higher CR)""" # Common parameters common_params = dict( co2_fraction=0.05, pressure_bar=10.0, temperature_C=40.0, v_sg=1.0, v_sl=0.5, mass_g=100.0, mass_l=500.0, vol_g=80.0, vol_l=50.0, holdup=50.0, vis_g=0.02, vis_l=1.0, roughness=0.000045, diameter=0.2, bicarbonate_mg_L=500.0, ionic_strength_mg_L=5000.0, calc_iterations=2, ) # Low pH (acidic) cr_low_pH = calculate_norsok_corrosion_rate( **common_params, pH_in=4.5, # Low pH ) # High pH (less acidic) cr_high_pH = calculate_norsok_corrosion_rate( **common_params, pH_in=6.0, # Higher pH ) # Lower pH should give higher corrosion rate # (pH correction factor fpH increases with pH, so CR decreases) assert cr_low_pH > cr_high_pH def test_zero_co2_gives_zero_corrosion(self): """Test that zero CO₂ gives zero corrosion rate""" cr = calculate_norsok_corrosion_rate( co2_fraction=0.0, # No CO₂ pressure_bar=10.0, temperature_C=40.0, v_sg=1.0, v_sl=0.5, mass_g=100.0, mass_l=500.0, vol_g=80.0, vol_l=50.0, holdup=50.0, vis_g=0.02, vis_l=1.0, roughness=0.000045, diameter=0.2, pH_in=5.5, bicarbonate_mg_L=500.0, ionic_strength_mg_L=5000.0, calc_iterations=2, ) # No CO₂ → no CO₂ corrosion assert cr == 0.0 class TestNORSOKComplete18ParameterSignature: """Test that calculate_norsok_corrosion_rate accepts all 18 parameters""" def test_all_18_parameters_accepted(self): """Test that all 18 required parameters are accepted without error""" try: cr = calculate_norsok_corrosion_rate( # Parameter 1-3: Conditions co2_fraction=0.05, pressure_bar=10.0, temperature_C=40.0, # Parameter 4-7: Multiphase flow v_sg=1.0, v_sl=0.5, mass_g=100.0, mass_l=500.0, vol_g=80.0, vol_l=50.0, holdup=50.0, # Parameter 8-9: Fluid properties vis_g=0.02, vis_l=1.0, # Parameter 10-11: Pipe geometry roughness=0.000045, diameter=0.2, # Parameter 12-14: Chemistry pH_in=5.5, bicarbonate_mg_L=500.0, ionic_strength_mg_L=5000.0, # Parameter 15: Calculation control calc_iterations=2, ) assert cr >= 0 except TypeError as e: pytest.fail(f"Function should accept all 18 parameters: {e}") def test_missing_parameters_raises_error(self): """Test that missing required parameters raises TypeError""" with pytest.raises(TypeError): # Missing most parameters calculate_norsok_corrosion_rate( co2_fraction=0.05, pressure_bar=10.0, temperature_C=40.0, ) class TestPHCorrectionFactor: """Test pH correction factor function""" def test_ph_correction_factor_in_valid_range(self): """Test pH correction factor returns reasonable values""" fpH = get_ph_correction_factor(temperature_C=25.0, pH=5.0) # fpH should be a positive dimensionless number assert fpH > 0 assert 0.1 <= fpH <= 10.0 def test_ph_out_of_range_clamps_with_warning(self): """Test that pH out of NORSOK range is clamped (not raised)""" # pH values outside 3.5-6.5 are now clamped to bounds with warning # (behavior changed to support upstream chemistry with out-of-range pH) # pH below minimum (2.0) should clamp to 3.5 fpH_low = get_ph_correction_factor(temperature_C=25.0, pH=2.0) fpH_at_min = get_ph_correction_factor(temperature_C=25.0, pH=3.5) assert fpH_low == fpH_at_min # Clamped to 3.5 # pH above maximum (8.0) should clamp to 6.5 fpH_high = get_ph_correction_factor(temperature_C=25.0, pH=8.0) fpH_at_max = get_ph_correction_factor(temperature_C=25.0, pH=6.5) assert fpH_high == fpH_at_max # Clamped to 6.5 def test_temperature_out_of_range_raises_error(self): """Test that temperature out of NORSOK range raises ValueError""" # Temperature must be in range 5-150°C per NORSOK M-506 with pytest.raises(ValueError, match="Temperature.*out of.*range"): get_ph_correction_factor(temperature_C=0.0, pH=5.0) with pytest.raises(ValueError, match="Temperature.*out of.*range"): get_ph_correction_factor(temperature_C=200.0, pH=5.0) if __name__ == "__main__": pytest.main([__file__, "-v"])

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