feat: FAIR-based dynamic LCIA (radiative forcing & temperature) - #24
Draft
TimoDiepers wants to merge 20 commits into
Draft
TimoDiepers wants to merge 20 commits into
TimoDiepers wants to merge 20 commits into
Conversation
- Rename cached loader to _load_species_map_cached() and have public load_species_map() return a deep copy to prevent cache corruption - Update test_methane_resolves to capture and assert sign == 1 - Update test_unmappable_flow_returns_none to assert sign == 1 - Fix docstring to correctly state unmappable flows return (None, sign)
Changes:
1. Refactor find_calibration_files() to call require_fair() only once
- _find_in_fair() now accepts the fair module as argument
- find_calibration_files() calls require_fair() once, then passes module to both _find_in_fair() calls
2. Remove misleading '# pragma: no cover' from except ImportError in require_fair()
- This branch IS executed when fair is absent and should be covered
3. Add output validation in run_fair() before require_fair()
- Raises ValueError if output not in ('radiative_forcing', 'temperature')
- Validation happens before fair requirement, works without fair installed
4. Fix vacuous assertion in test_find_calibration_files_returns_tuple()
- Changed 'assert (params is None) == (props is None) or True' to proper assertion
5. Add test_run_fair_rejects_bad_output() for output validation
- Tests without requiring fair to be installed
Implements characterize_with_fair() that wires runner + allocation + species_map + scenario config into a long DataFrame with columns [date, amount, flow, activity, quantile]. Also exposes the pure helper _inventory_emissions_by_species() for unit testing without fair installed.
- Add early guard in characterize_with_fair to return empty DataFrame before calling require_fair() when inventory is empty - Reorder logic to compute by_species and check for unmapped flows before require_fair(), allowing unmapped flows to return early without requiring fair installation - Add logger.warning when no flows map to FAIR species for visibility - Add two new tests: test_zero_row_inventory_returns_empty and test_unmapped_flows_warn_and_return_empty; both pass without fair
- Add fair_radiative_forcing and fair_temperature to valid_metrics - Add FAIR dispatch branch in characterize(): validates scenario support, enriches inventory with flow names via _add_fair_flow_names(), then calls characterize_with_fair() from fair.core - Add _add_fair_flow_names() helper: best-effort bw2data lookup wrapped in try/except so FAIR path degrades gracefully without a configured project - Import prospective config at module level (not lazily) to avoid cross-test sys.modules pollution from test_prospective.py - Tests in tests/test_characterize_fair_dispatch.py (4 tests)
Other test modules install dynamic_characterization sub-package stubs at collection time which could shadow the real package; purge them before importing so this test validates the genuine module under any test order.
- Wrap scenario_supports("fair") check to catch RuntimeError (no scenario
set) and raise a clear ValueError with guidance to call set_scenario()
- Remove dead monkeypatch of _characterize_with_fair in dispatch test
- Add test_fair_metric_without_any_scenario_raises() test case
- Strengthen test_add_fair_flow_names_is_best_effort() with len check
- Update _add_fair_flow_names docstring: logs at debug instead of warns
- Add logger.debug() call on exception path in _add_fair_flow_names()
Add README section "FAIR climate model (optional)" covering install extra, fair_radiative_forcing / fair_temperature metrics, output shape with quantile column, scenario selection, and the 12 FAIR-capable scenarios (8 FAIR-native + 4 dual). Add available_scenarios() usage examples. Add CHANGES.md entry for v1.5.0. Add test guard (tests/test_docs_scenarios.py) that README contains the key strings.
…elog - Replace internal characterize_with_fair() in README with public API via characterize(..., metric="fair_radiative_forcing"/"fair_temperature") - Update CHANGES.md [1.5.0] heading to [Unreleased] for consistency with version state - Add prose note about time_horizon behavior for FAIR metrics (analysis end = last emission year + time_horizon, defaults to 2100)
…core test FIX 1: Clarify that the precursor table in species_map.yaml is informational metadata, not used for routing. FAIR applies precursor responses natively when the precursor emission species (NOx, VOC, Sulfur, etc.) is perturbed, so this package does not route them separately. FIX 2: Add autouse fixture _reset_scenario() to test_fair_core.py to reset the prospective config scenario state before and after each test, preventing global state leakage (mirrors pattern in test_prospective.py).
…ation) Verified against fair 2.2.4. The stock fair package ships only the species-properties defaults, not the calibrated 841-member AR6 ensemble, so run_fair now: - downloads + caches the calibration 1.4.1 params/species CSVs via pooch when they are not vendored in the install (content-hash pinned), else uses vendored files; - simulates from 1750 so the climate state spins up, slicing reporting years from the timebounds axis; - applies inventory perturbations on the timepoints axis with kg->FAIR unit conversion (CO2 in GtCO2, others in Mt); - initialises forcing/temperature/cumulative/airborne emissions before run. Probe-verified signs/magnitudes: +100 GtCO2 (2030) -> +0.035 K, +0.064 W/m2 by 2100 on ssp245; symmetric for negative. Full suite 113 passed / 1 skipped with fair installed.
Add optional FAIR climate-model dynamic LCIA path: fair_radiative_forcing / fair_temperature metrics, superset scenario registry with per-metric availability, trails-style ensemble + per-flow attribution. Verified against fair 2.2.4.
Standalone (no-Brightway) showcase of the fair_radiative_forcing and fair_temperature metrics with ensemble quantile bands and per-activity attribution, plus an SSP-background comparison and a Brightway + bw_temporalis integration template. Runnable section executed with embedded plots.
FAIR is a climate emulator, not an IAM, so overloading the iam slot with an iam='FAIR' sentinel was misleading. FAIR-native markers now live in a separate FAIR_NATIVE_SCENARIOS map selected with set_fair_scenario(ssp, rcp) and listed by available_fair_scenarios(); get_scenario()['iam'] is None for them. The four IAM scenarios that match a FAIR marker still enable FAIR via set_scenario. Updates dispatch error messages, tests, README/CHANGES/spec, and the demo notebook (re-executed).
TimoDiepers
force-pushed
the
feat/fair-dynamic-lcia
branch
from
August 3, 2026 10:38
d0da988 to
16bc3d4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an optional FAIR-based dynamic LCIA pathway: emission profiles from a dynamic inventory are fed into the FaIR simple climate model, and per-flow radiative forcing / temperature responses are returned as characterized results.
New metrics dispatched through the existing
characterize()API:fair_radiative_forcingfair_temperatureWhat's included
dynamic_characterization/fair/— species mapping (flow → FAIR species, YAML-backed), signed per-kg marginal allocation, FAIR runner with calibration discovery, and the core orchestration into a per-flow quantile DataFrame.set_fair_scenario(ssp, rcp)selects FAIR-native scenarios; the scenario registry is tagged with per-metric availability and FAIR markers.fairis an optional extra (fair>=2.2, pluspoochfor calibration download). Import is lazy and guarded — nothing changes for users who don't install it. Calibration data is not bundled and is fetched on first use.notebooks/fair_dynamic_lcia_demo.ipynb.Notes / open points
set_fair_scenario, metric names) before polishing.main; can rebase on request.