diff --git a/docs/Reference/config/config_schema.json b/docs/Reference/config/config_schema.json index 696ff76b1..a112b09ec 100644 --- a/docs/Reference/config/config_schema.json +++ b/docs/Reference/config/config_schema.json @@ -2838,7 +2838,7 @@ "value": 1.0 } ], - "description": "Cryoscopic depression factor controlling the width of the mushy zone (partially molten region) in the PALEOS unified EOS. Defines the solidus as T_sol = T_liq * mushy_zone_factor. 1.0 = sharp phase boundary (no mushy zone). 0.8 = solidus at 80% of the liquidus temperature, roughly matching the Stixrude+2014 cryoscopic depression for MgSiO3. Must be in [0.7, 1.0]. Only applies to PALEOS unified EOS; ignored for WolfBower2018 and RTPress100TPa (which use explicit melting curve files). This factor is applied consistently across Zalmoxis (density interpolation), SPIDER (phase boundaries), and the VolatileProfile phi-blending.", + "description": "Cryoscopic depression factor controlling the width of the mushy zone (partially molten region) in the PALEOS EOS family. Defines the solidus as T_sol = T_liq * mushy_zone_factor. 1.0 = sharp phase boundary (no mushy zone). 0.8 = solidus at 80% of the liquidus temperature, roughly matching the Stixrude+2014 cryoscopic depression for MgSiO3. Must be in [0.7, 1.0]. Applies to the PALEOS EOS family (PALEOS, PALEOS-2phase, PALEOS-API, PALEOS-API-2phase); ignored for WolfBower2018 and RTPress100TPa (which use explicit melting curve files) and for Seager2007/Analytic (no derived solidus).", "doc_source": "attributes", "group_order": 0, "group_position": 3, diff --git a/docs/Reference/config/interior.md b/docs/Reference/config/interior.md index 5e14b4fc3..cf364f4a0 100644 --- a/docs/Reference/config/interior.md +++ b/docs/Reference/config/interior.md @@ -49,7 +49,7 @@ belong to the experimental binodal-aware mode. | `core_eos` | str | `"PALEOS:iron"` | EOS for the core layer. Format: ":". Tabulated: "PALEOS:iron" (default), "Seager2007:iron". Analytic: "Analytic:iron", "Analytic:MgFeSiO3", etc. | | `mantle_eos` | str | `"PALEOS:MgSiO3"` | EOS for the mantle layer. Format: ":". Tabulated: "PALEOS:MgSiO3" (default), "PALEOS-2phase:MgSiO3", "Seager2007:MgSiO3", "WolfBower2018:MgSiO3". Analytic: "Analytic:MgSiO3", "Analytic:MgFeSiO3", etc. | | `ice_layer_eos` | str or none | `none` | EOS for the ice/water layer (3-layer model). 'none' for 2-layer model (core + mantle only). Tabulated: "PALEOS:H2O", "Seager2007:H2O". Analytic: "Analytic:H2O". | -| `mushy_zone_factor` | float | `0.8` | Cryoscopic depression factor controlling the width of the mushy zone (partially molten region) in the PALEOS unified EOS. Defines the solidus as T_sol = T_liq * mushy_zone_factor. 1.0 = sharp phase boundary (no mushy zone). 0.8 = solidus at 80% of the liquidus temperature, roughly matching the Stixrude+2014 cryoscopic depression for MgSiO3. Must be in \[0.7, 1.0\]. Only applies to PALEOS unified EOS; ignored for WolfBower2018 and RTPress100TPa (which use explicit melting curve files). This factor is applied consistently across Zalmoxis (density interpolation), SPIDER (phase boundaries), and the VolatileProfile phi-blending. Must be >= 0.7 and <= 1.0. | +| `mushy_zone_factor` | float | `0.8` | Cryoscopic depression factor controlling the width of the mushy zone (partially molten region) in the PALEOS EOS family. Defines the solidus as T_sol = T_liq * mushy_zone_factor. 1.0 = sharp phase boundary (no mushy zone). 0.8 = solidus at 80% of the liquidus temperature, roughly matching the Stixrude+2014 cryoscopic depression for MgSiO3. Must be in \[0.7, 1.0\]. Applies to the PALEOS EOS family (PALEOS, PALEOS-2phase, PALEOS-API, PALEOS-API-2phase); ignored for WolfBower2018 and RTPress100TPa (which use explicit melting curve files) and for Seager2007/Analytic (no derived solidus). Must be >= 0.7 and <= 1.0. | | `mantle_mass_fraction` | float | `0` | Fraction of the planet's interior mass corresponding to the mantle. Required for 3-layer models (with ice layer) and for T-dependent 2-layer models (WolfBower2018, RTPress100TPa) where it partitions mass between core and mantle layers. Must be >= 0 and < 1. | | `dry_mantle` | bool | `true` | Structure EOS assumes a dry mantle. Set False for melt-fraction-aware dissolved-volatile mixing in the mantle density (per-shell volatile profile); the dissolved mass then stays inside the interior mass target. | diff --git a/src/proteus/config/_struct.py b/src/proteus/config/_struct.py index 7a0bfe455..fc0a5a14a 100644 --- a/src/proteus/config/_struct.py +++ b/src/proteus/config/_struct.py @@ -50,12 +50,13 @@ def valid_zalmoxis(instance, attribute, value): import logging as _logging _log = _logging.getLogger('fwl.' + __name__) - # mushy_zone_factor only applies to PALEOS unified tables + # mushy_zone_factor scales the derived solidus only for the PALEOS EOS family + _MZF_EOS_PREFIXES = ('PALEOS:', 'PALEOS-2phase:', 'PALEOS-API:', 'PALEOS-API-2phase:') mzf = getattr(instance.zalmoxis, 'mushy_zone_factor', 0.8) - if mzf < 1.0 and not mantle_eos.startswith('PALEOS:'): + if mzf < 1.0 and not mantle_eos.startswith(_MZF_EOS_PREFIXES): _log.warning( 'mushy_zone_factor=%.2f has no effect with mantle EOS %s. ' - 'The mushy zone factor only applies to PALEOS unified tables. ' + 'The mushy zone factor applies only to the PALEOS EOS family. ' 'For WolfBower2018/RTPress100TPa, the mushy zone is defined by ' 'the solidus/liquidus melting curve files.', mzf, @@ -102,16 +103,15 @@ class Zalmoxis: Tabulated: "PALEOS:H2O", "Seager2007:H2O". Analytic: "Analytic:H2O". mushy_zone_factor: float Cryoscopic depression factor controlling the width of the mushy - zone (partially molten region) in the PALEOS unified EOS. + zone (partially molten region) in the PALEOS EOS family. Defines the solidus as T_sol = T_liq * mushy_zone_factor. 1.0 = sharp phase boundary (no mushy zone). 0.8 = solidus at 80% of the liquidus temperature, roughly matching the Stixrude+2014 cryoscopic depression for MgSiO3. - Must be in [0.7, 1.0]. Only applies to PALEOS unified EOS; - ignored for WolfBower2018 and RTPress100TPa (which use explicit - melting curve files). This factor is applied consistently across - Zalmoxis (density interpolation), SPIDER (phase boundaries), - and the VolatileProfile phi-blending. + Must be in [0.7, 1.0]. Applies to the PALEOS EOS family (PALEOS, + PALEOS-2phase, PALEOS-API, PALEOS-API-2phase); ignored for + WolfBower2018 and RTPress100TPa (which use explicit melting curve + files) and for Seager2007/Analytic (no derived solidus). mantle_mass_fraction: float Fraction of the planet's interior mass corresponding to the mantle. Required for 3-layer models (with ice layer) and for T-dependent diff --git a/tests/config/test_struct.py b/tests/config/test_struct.py index 69e714c03..a8bd30de7 100644 --- a/tests/config/test_struct.py +++ b/tests/config/test_struct.py @@ -13,6 +13,8 @@ from __future__ import annotations +import logging + import pytest from proteus.config._struct import Struct, Zalmoxis @@ -160,3 +162,52 @@ def test_spider_module_skips_the_gate(self): # absent. with pytest.raises(ValueError, match='core_eos'): Struct(module='zalmoxis', zalmoxis=Zalmoxis(core_eos='no_colon')) + + +class TestZalmoxisMushyZoneWarning: + """The mushy_zone_factor no-effect warning must fire only for the EOS + families that actually ignore the factor. + + mushy_zone_factor scales the derived solidus ``T_sol = T_liq * mzf`` for + the PALEOS family (unified, 2-phase, and the API variants) through + ``load_zalmoxis_solidus_liquidus_functions``. For WolfBower2018 and + RTPress100TPa the melting curves come from file and the factor is inert, + so there the warning is correct and must still fire. + """ + + @staticmethod + def _warns(caplog, mantle_eos, mzf=0.8): + """Construct a zalmoxis Struct and report whether the no-effect + warning fired for ``mantle_eos`` at the given factor.""" + caplog.clear() + with caplog.at_level(logging.WARNING, logger='fwl.proteus.config._struct'): + Struct( + module='zalmoxis', + zalmoxis=Zalmoxis(mantle_eos=mantle_eos, mushy_zone_factor=mzf), + ) + return any('has no effect' in r.getMessage() for r in caplog.records) + + def test_warning_silent_for_the_paleos_family(self, caplog): + """Each PALEOS-family EOS feeds the factor into the derived solidus, so + the no-effect warning must stay silent for all four.""" + for eos in ( + 'PALEOS:MgSiO3', + 'PALEOS-2phase:MgSiO3', + 'PALEOS-API:MgSiO3', + 'PALEOS-API-2phase:MgSiO3', + ): + assert not self._warns(caplog, eos), eos + + def test_warning_fires_for_file_curve_eos(self, caplog): + """WolfBower2018 and RTPress100TPa read melting curves from file, so the + factor has no effect and the warning must fire.""" + for eos in ('WolfBower2018:MgSiO3', 'RTPress100TPa:MgSiO3'): + assert self._warns(caplog, eos), eos + + def test_warning_tracks_the_factor_not_only_the_eos(self, caplog): + """The warning depends on mushy_zone_factor < 1: at the sharp-boundary + value 1.0 an ignored setting is not misreported.""" + assert not self._warns(caplog, 'WolfBower2018:MgSiO3', mzf=1.0) + # Paired positive: the same EOS at 0.8 fires, so silence at 1.0 is the + # factor guard rather than the EOS being exempt. + assert self._warns(caplog, 'WolfBower2018:MgSiO3', mzf=0.8)