===================================== Testing CMORisation on Actual Files ===================================== This guide covers comprehensive testing of the CMORisation pipeline using actual model output files. .. contents:: Table of Contents :local: :depth: 2 Overview ======== The ACCESS-MOPPy test suite includes integration tests that process real data files through the full CMORisation pipeline. These tests validate that variables are correctly transformed to CMIP6/CMIP7 standards. **Key Features:** - **Variable-level granularity**: Test individual variables, not just entire tables - **Multiple frequencies**: Support for monthly, daily, 3-hourly, and 6-hourly data - **CMIP6 & CMIP7**: Test both standards with automatic table mapping - **Compliance validation**: Verify output against CMOR standards using PrePARE or WCRP compliance-checker - **Flexible filtering**: Run tests for specific tables, variables, or frequencies Setting Up Test Data ==================== External Data Root ------------------ Set the ``ACCESS_MOPPY_DATA_ROOT`` environment variable to point to your test data directory: .. code-block:: bash export ACCESS_MOPPY_DATA_ROOT=/path/to/test/data Expected Directory Structure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The test data should follow this structure: .. code-block:: text ACCESS_MOPPY_DATA_ROOT/ ├── output1/ │ ├── atmosphere/netCDF/ │ │ ├── *_mon.nc # Monthly atmospheric data │ │ ├── *_dai.nc # Daily atmospheric data │ │ ├── *_3hr.nc # 3-hourly atmospheric data │ │ └── *_6hr.nc # 6-hourly atmospheric data │ ├── ocean/ │ │ ├── *-variable-1monthly-mean*.nc # Monthly ocean (CMIP6 convention) │ │ └── *-variable-1mon-mean*.nc # Monthly ocean (alternate convention) │ └── ice/ │ └── iceh-1monthly-mean_*.nc # Sea ice monthly data ├── output2/ └── output3/ The test discovery looks for files matching these patterns in any ``output*`` subdirectory, so you can have multiple ensemble members or runs. Running Tests ============= Before Running Tests -------------------- 1. Set up the pixi environment: .. code-block:: bash cd /path/to/ACCESS-MOPPy pixi shell -e dev 2. Set the data root (if not already in your shell): .. code-block:: bash export ACCESS_MOPPY_DATA_ROOT=/home/romain/PROJECTS/CMIP7_Test_data/esm-historical Basic Test Execution --------------------- **Run all CMORisation tests:** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -v This runs 372 tests: 240 CMIP6 + 132 CMIP7 across all supported tables and variables. **Run with minimal output:** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -q **Show test collection only (no execution):** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py --collect-only -q Advanced Filtering ================== Filter by Variable Name ----------------------- Test a specific variable across all frequencies: .. code-block:: bash # Test 'tas' in all tables pytest tests/integration/test_full_cmorisation.py -k "tas" -v # Test temperature variables only pytest tests/integration/test_full_cmorisation.py -k "tas or tasa or tasmin or tasmax" -v # Test 'tos' (ocean temperature) pytest tests/integration/test_full_cmorisation.py -k "tos" -v Filter by Table --------------- Test all variables in a specific table: .. code-block:: bash # Test all Amon (monthly atmosphere) variables pytest tests/integration/test_full_cmorisation.py -k "Amon" -v # Test all ocean variables pytest tests/integration/test_full_cmorisation.py -k "Omon" -v # Test all sea ice variables pytest tests/integration/test_full_cmorisation.py -k "SImon" -v # Test daily frequency only pytest tests/integration/test_full_cmorisation.py -k "day" -v Combined Filtering ------------------- **Test tas in Amon table only:** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -k "Amon and tas" -v **Test ocean variables but exclude SImon:** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -k "Omon or SImon or ocean" -v **Test a specific variable in multiple frequencies:** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -k "pr" -v # Tests: Amon-pr, CFmon-pr, day-pr, 6hrPlev-pr, etc. Filter by CMIP Version ---------------------- **Test CMIP6 only (no CMIP7):** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -k "not cmip7" -v **Test CMIP7 only:** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -k "cmip7" -v # Specific CMIP7 variable pytest tests/integration/test_full_cmorisation.py -k "cmip7 and tas" -v Test Granularity Examples -------------------------- .. code-block:: bash # Single variable test (most specific) pytest tests/integration/test_full_cmorisation.py::TestFullCMORIntegration::test_cmorisation_variable -k "Amon-ACCESS" -v # Note: Use the full test ID with the parametrized variable name # All variables in Amon pytest tests/integration/test_full_cmorisation.py -k "Amon" -v # All monthly variables (Amon, Lmon, AERmon, etc.) pytest tests/integration/test_full_cmorisation.py -k "mon" -v # All atmospheric variables pytest tests/integration/test_full_cmorisation.py -k "Amon or day or 3hr or 6hr or Eday or CFday" -v Understanding Test Output ========================== Successful Test --------------- .. code-block:: text tests/integration/test_full_cmorisation.py::TestFullCMORIntegration::test_cmorisation_variable[Amon-ACCESS-ESM1-6-CMIP6_Amon.json-CMIP6-tas] PASSED [100%] **What this means:** - Variable ``tas`` in table ``Amon`` was successfully CMORised - Output files were created and validated Skipped Test ----------- .. code-block:: text tests/integration/test_full_cmorisation.py::TestFullCMORIntegration::test_cmorisation_variable[Omon-...] SKIPPED Reason: Ocean data directory not available; set ACCESS_MOPPY_DATA_ROOT **Solutions:** 1. Set ``ACCESS_MOPPY_DATA_ROOT`` to a directory containing ocean files 2. Ensure ocean files exist at ``$ACCESS_MOPPY_DATA_ROOT/output*/ocean/`` Failed Test ----------- .. code-block:: text tests/integration/test_full_cmorisation.py::TestFullCMORIntegration::test_cmorisation_variable[Amon-...-tas] FAILED AssertionError: No output files found for tas in /tmp/cmor_output_Amon_tas **Common causes:** - Input data file is missing or malformed - CMORiser encountered a mapping error - Output file wasn't created for other reasons **Debugging:** 1. Check the full error message with ``-v`` flag 2. Verify input files exist: ``ls $ACCESS_MOPPY_DATA_ROOT/output*/atmosphere/netCDF/`` 3. Check CMORiser logs if available Compliance Validation ===================== Default Behavior (PrePARE) -------------------------- By default, tests validate output using PrePARE: .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -k "Amon-tas" -v PrePARE validates: - CMOR table compliance - Dimensions and coordinates - NetCDF structure - Metadata Using WCRP Compliance Checker ----------------------------- To use the WCRP compliance-checker instead (more strict): .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -k "Amon-tas" \ --compliance-tool=wcrp -v **Requirements:** - WCRP compliance-checker must be installed - cc-plugin-wcrp must be installed **Verify availability:** .. code-block:: bash compliance-checker --list-tests Should show ``wcrp_cmip6:1.0`` in the output. Skipping Compliance Validation ------------------------------ To test CMORisation without compliance validation: .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -k "Amon" \ --compliance-tool=none -v **Note:** Some tests automatically skip compliance validation: - ``Omon`` (ocean) - non-standard grid structures - ``Ofx`` (ocean fixed fields) - fixed variables Practical Testing Workflows ============================ Scenario 1: Test a New Variable -------------------------------- You've added a new variable mapping and want to verify it works: .. code-block:: bash # 1. Run just that variable across all tables pytest tests/integration/test_full_cmorisation.py -k "mynewvar" -v # 2. If it passes, run with CMIP7 too pytest tests/integration/test_full_cmorisation.py -k "mynewvar and cmip7" -v # 3. Verify compliance output pytest tests/integration/test_full_cmorisation.py -k "Amon-mynewvar" -v -s Scenario 2: Validate a Frequency Tier -------------------------------------- You want to ensure all daily variables work correctly: .. code-block:: bash # Test all daily (day) variables pytest tests/integration/test_full_cmorisation.py -k "day" -v --tb=short # Count how many pass/fail pytest tests/integration/test_full_cmorisation.py -k "day" -v --tb=no -q Scenario 3: Quality Check Before Release ----------------------------------------- Run comprehensive tests to ensure everything works: .. code-block:: bash # Test CMIP6 only (stable baseline) pytest tests/integration/test_full_cmorisation.py -k "not cmip7" \ --tb=short -v 2>&1 | tee test_results.log # Check for any failures grep -E "(FAILED|ERROR)" test_results.log # If clean, test CMIP7 pytest tests/integration/test_full_cmorisation.py -k "cmip7" \ --tb=short -v 2>&1 | tee test_results_cmip7.log Scenario 4: Debug a Single Variable ----------------------------------- A test is failing and you need detailed information: .. code-block:: bash # Run with full traceback and print statements pytest tests/integration/test_full_cmorisation.py -k "Amon-tas" -vv -s --tb=long # Show which input files were discovered pytest tests/integration/test_full_cmorisation.py -k "Amon-tas" -vv -s Interpreting Variable Names ============================ Test IDs follow the pattern: ``TableName-VariableName`` or ``TableName-VariableName-cmip7`` Examples: .. list-table:: :header-rows: 1 * - Test ID - Meaning - Frequency * - ``Amon-tas`` - Temperature in Amon (monthly atmosphere) - Monthly * - ``day-pr`` - Precipitation in daily table - Daily * - ``ocean-tos-cmip7`` - Ocean temperature (CMIP7 atmos variant) - Monthly * - ``SImon-sithick`` - Sea ice thickness - Monthly * - ``6hrPlev-ta`` - Air temperature on pressure levels - 6-hourly * - ``Ofx-areacello`` - Ocean grid cell area (fixed) - Fixed (no time) CMIP7 Table Mapping ~~~~~~~~~~~~~~~~~~~ CMIP7 tests use these table mappings internally: - ``atmos`` → CMIP6 ``Amon`` (monthly atmospheric) - ``ocean`` → CMIP6 ``Omon`` (monthly ocean) - ``seaIce`` → CMIP6 ``SImon`` (monthly sea ice) - ``aerosol`` → CMIP6 ``AERmon`` (monthly aerosol) When you run a CMIP7 test, it processes using the CMIP6 table but validates against CMIP7 standards. Troubleshooting =============== No Tests Collected ------------------ **Error:** .. code-block:: text collected 0 items **Causes & Solutions:** 1. **Wrong path:** .. code-block:: bash # Wrong pytest test_full_cmorisation.py # Correct pytest tests/integration/test_full_cmorisation.py 2. **Pixi environment not activated:** .. code-block:: bash pixi shell -e dev pytest tests/integration/test_full_cmorisation.py 3. **Invalid -k filter:** .. code-block:: bash # Wrong pytest tests/integration/test_full_cmorisation.py -k "Amon\-tas" # Correct pytest tests/integration/test_full_cmorisation.py -k "Amon and tas" All Tests Skipped ------------------ **Error:** .. code-block:: text 372 skipped, 1 warning in X.XXs Reason: Ocean data directory not available; set ACCESS_MOPPY_DATA_ROOT **Solution:** .. code-block:: bash # Check current setting echo $ACCESS_MOPPY_DATA_ROOT # Set it export ACCESS_MOPPY_DATA_ROOT=/home/romain/PROJECTS/CMIP7_Test_data/esm-historical # Verify path exists ls $ACCESS_MOPPY_DATA_ROOT/output*/atmosphere/netCDF/ | head -5 Input Files Not Found --------------------- **Error:** .. code-block:: text Required input files not available for Omon.tos; set ACCESS_MOPPY_DATA_ROOT **Solutions:** 1. **Verify data root:** .. code-block:: bash ls $ACCESS_MOPPY_DATA_ROOT/output*/ocean/ 2. **Check filename conventions:** Ocean files should match: - ``*-variable-1monthly-mean*.nc`` (preferred) - ``*-variable-1mon-mean*.nc`` (alternate) 3. **List available files:** .. code-block:: bash find $ACCESS_MOPPY_DATA_ROOT -name "*.nc" | grep ocean | head -10 Compliance Validation Failures ------------------------------ **Error:** .. code-block:: text WCRP compliance validation failed mandatory checks: - check_dimensions: ... **Solutions:** 1. **Review the specific check:** Run with verbose output to see full details: .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -k "Amon-tas" \ -vv --tb=long 2. **Compare against known good output:** Check if the issue is with your data or the CMORiser: .. code-block:: bash # Run with PrePARE instead pytest tests/integration/test_full_cmorisation.py -k "Amon-tas" \ --compliance-tool=prepare -v 3. **Inspect the output file:** .. code-block:: bash ncdump -h /tmp/cmor_output_Amon_tas/tas_Amon_*.nc | head -50 Running Tests in Batch ====================== Run Multiple Variables Sequentially ----------------------------------- .. code-block:: bash pytest tests/integration/test_full_cmorisation.py \ -k "tas or pr or hus or huss" \ -v --tb=short # Output counts pytest tests/integration/test_full_cmorisation.py \ -k "tas or pr or hus or huss" \ -q --tb=no Test Monthly Data Only ---------------------- .. code-block:: bash # Run all monthly frequency tables pytest tests/integration/test_full_cmorisation.py \ -k "Amon or Lmon or AERmon or Omon or SImon or CFmon or Emon or Ofx" \ -v --tb=short # More concise - use 'mon' pytest tests/integration/test_full_cmorisation.py \ -k "mon or Emon or Ofx" \ -v Subset of Variables by Category -------------------------------- **Atmospheric dynamics:** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py \ -k "Amon and (ua or va or ta or ps or zg)" -v **Radiation:** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py \ -k "Amon and (rsds or rlds or rsus or rlus)" -v **Water cycle:** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py \ -k "Amon and (pr or prc or prw or evspsbl)" -v Performance Considerations ========================== Test Execution Time ------------------- - **Single variable test**: ~1-2 seconds - **All Amon variables**: ~30-60 seconds - **All tables (CMIP6)**: ~3-5 minutes - **All tables (CMIP6 + CMIP7)**: ~5-8 minutes Reduce Execution Time ~~~~~~~~~~~~~~~~~~~~~ 1. **Test specific table only:** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -k "Amon" -v # Instead of running all 372 tests 2. **Skip compliance validation (faster):** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py \ --compliance-tool=none -q 3. **Use parallel execution (requires pytest-xdist):** .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -n auto -v Parallel Execution ------------------ To install pytest-xdist: .. code-block:: bash pixi add -e dev pytest-xdist Run tests in parallel: .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -n auto -v Run with 4 workers: .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -n 4 -v Tips & Best Practices ===================== 1. **Always set ACCESS_MOPPY_DATA_ROOT before testing** Add to your shell profile (``.bashrc``, ``.zshrc``) to avoid repetition: .. code-block:: bash export ACCESS_MOPPY_DATA_ROOT=/home/romain/PROJECTS/CMIP7_Test_data/esm-historical 2. **Start with a single variable test** When debugging, narrow the scope: .. code-block:: bash # Good for debugging pytest tests/integration/test_full_cmorisation.py -k "Amon-tas" -vv -s 3. **Use --tb=short for cleaner output** Reduces verbosity while keeping error context: .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -k "Amon" --tb=short 4. **Save test results to file** For later analysis: .. code-block:: bash pytest tests/integration/test_full_cmorisation.py -v \ 2>&1 | tee test_results_$(date +%Y%m%d_%H%M%S).log 5. **Test CMIP6 before CMIP7** CMIP6 is more stable; use it as baseline: .. code-block:: bash pytest tests/integration/test_full_cmorisation.py \ -k "not cmip7" -v # If passing, test CMIP7 pytest tests/integration/test_full_cmorisation.py \ -k "cmip7" -v 6. **Use collection-only to verify filter** Before running expensive tests: .. code-block:: bash pytest tests/integration/test_full_cmorisation.py \ -k "Amon and (tas or pr)" --collect-only -q # Verify you get the tests you expect Additional Resources ==================== - :doc:`getting_started` - Basic CMORisation usage - :doc:`mapping_reference` - Variable mapping details - :doc:`compliance_testing` - Compliance validation - `PrePARE Documentation `_ - `CMOR Standards `_ For help or issues, refer to the project README or open an issue on GitHub.