Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Davide/remove hres eccb #59

Draft
wants to merge 23 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c9deb3b
Added integration test script
atteggiani Feb 14, 2025
284cf26
Added parallelization for tests with dynamic capture of PIDs and exit…
atteggiani Feb 21, 2025
e732985
Redirected tests STDOUT to /dev/null
atteggiani Feb 21, 2025
37c3ff0
Updated trap function. Updated compare function. Updated paths
atteggiani Feb 21, 2025
a1e85dd
Minor fix to a comment
atteggiani Feb 22, 2025
6701970
Updated integration README
atteggiani Feb 23, 2025
fb5ffa8
Updated README
atteggiani Feb 23, 2025
731e0bc
Added dependencies to dev environment
atteggiani Feb 24, 2025
2f97856
Added tests/test_integration.py to be run with pytest.
atteggiani Feb 25, 2025
8d225ce
Re-factored test to:
atteggiani Feb 26, 2025
ad7ea52
Added tests for hres_eccb entry point
atteggiani Feb 27, 2025
3b035eb
Deleted old ibash ntegration tests
atteggiani Feb 27, 2025
a750336
Revert changes on src/replace_landsurface files
atteggiani Feb 27, 2025
fdbebc5
Incorporated Tom's suggestions
atteggiani Feb 27, 2025
80f9957
Updated README
atteggiani Feb 28, 2025
ce90376
Removed fixture to set env variable
atteggiani Feb 28, 2025
0806a01
Turned get_error_message fixture to a normal function
atteggiani Feb 28, 2025
abe5945
Removed unused variable from fixture
atteggiani Feb 28, 2025
71800c3
Turned all path concatenation to 'os.path.join'
atteggiani Feb 28, 2025
deb4432
Fixed hres_eccb test to call the right function
atteggiani Feb 28, 2025
24359ce
Removed hres_eccb script and renamed 'hres_ic' to 'replace_landsurfac…
atteggiani Feb 28, 2025
8eec4fe
Embedded old 'test_hres_eccb' test (test n. 4) within the 'test_repla…
atteggiani Feb 28, 2025
3b0df76
Updated docstrings
atteggiani Feb 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .conda/env_dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
channels:
- accessnri
- conda-forge
- coecms
- nodefaults

dependencies:
- mule
- numpy <= 1.23.4 # https://stackoverflow.com/a/75148219/21024780
- scitools-iris
- xarray
- versioneer
- pytest
- pytest-cov
- pytest-xdist
- hypothesis
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
test_data/
.coverage
*.sh
__pycache__/
*.py[cod]
.ruff_cache/
Expand Down
49 changes: 46 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,48 @@
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)

# replace_landsurface

This repository contains Python scripts for use by Regional Nesting Suites to replace specific land surface fields in static input data.
## About

`replace_landsurface` is a `Python` utility to be used within ACCESS-NRI versions of the Regional Nesting Suites to replace specific land surface initial/boundary conditions.


## Development/Testing instructions
For development/testing, it is recommended to install `replace_landsurface` as a development package within a `micromamba`/`conda` testing environment.

### Clone replace_landsurface GitHub repo
```
git clone [email protected]:ACCESS-NRI/replace_landsurface.git
```

### Create a micromamba/conda testing environment
> [!TIP]
> In the following instructions `micromamba` can be replaced with `conda`.

```
cd replace_landsurface
micromamba env create -n replace_landsurface_dev --file .conda/env_dev.yml
micromamba activate replace_landsurface_dev
```

### Install replace_landsurface as a development package
```
pip install --no-deps --no-build-isolation -e .
```

### Running the tests

The test suite currently includes only integration tests.

To manually run the tests, from the `replace_landsurface` directory, you can:

1. Activate your [micromamba/conda testing environment](#create-a-micromamba-conda-testing-environment)
2. Run the following command:
```
pytest -n 4
```

> [!TIP]
> The `-n 4` option is a [pytest-xdist](https://pytest-xdist.readthedocs.io/en/stable/) option to run the tests in parallel across 4 different workers.

> [!IMPORTANT]
> Integration tests are designed to be run on `Gadi`.
> If you run tests on a local machine, the integration tests will be skipped.
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ dependencies = [
Repository = "https://github.com/ACCESS-NRI/replace_landsurface"

[project.scripts]
hres_eccb = "replace_landsurface.hres_eccb:main"
hres_ic = "replace_landsurface.hres_ic:main"
replace_landsurface = "replace_landsurface.replace_landsurface:main"

[build-system]
build-backend = "setuptools.build_meta"
Expand Down
63 changes: 0 additions & 63 deletions src/replace_landsurface/hres_eccb.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
# Created by: Chermelle Engel <[email protected]>

"""
Replace the land/surface fields in the astart file with higher-resolution
era5-land or BARRA2-R data (if requested).
Replace the land-surface fields in the astart file with higher-resolution data
"""

import argparse
Expand All @@ -25,16 +24,17 @@
def main():

"""
The main function that creates a worker pool and generates single GRIB files
for requested date/times in parallel.
Calls the command line argument parser and process the arguments using the right function.

Parameters
----------
None. The arguments are given via the command-line
None
The arguments are given via the command-line

Returns
-------
None. The astart file is updated and overwritten
None
An output file is written
"""

# Parse the command-line arguments
Expand Down
146 changes: 146 additions & 0 deletions tests/integration/test_integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import contextlib
import filecmp
import os
import shutil
import socket
from unittest.mock import patch
import pytest

# If not on Gadi, skip the tests because the test data is not available
GADI_HOSTNAME = "gadi.nci.org.au"
hostname = socket.gethostname()
# Marker to skip tests if not on Gadi
skip_marker = pytest.mark.skipif(
not hostname.endswith(GADI_HOSTNAME),
reason=f"Skipping integration tests because they cannot be executed on {hostname}.\n"
"Integration tests are specifically designed to run on Gadi (gadi.nci.org.au).",
)
# Marker to suppress warnings
warning_marker = pytest.mark.filterwarnings("ignore::Warning")
# Apply the markers to all tests in this file
pytestmark = [skip_marker, warning_marker]

############################################
## === Integration tests setup === ##
############################################
TEST_DATA_DIR = "/g/data/vk83/testing/data/replace_landsurface/integration_tests"
INPUT_DIR = os.path.join(TEST_DATA_DIR, "input_data")
OUTPUT_DIR = os.path.join(TEST_DATA_DIR, "expected_outputs")
DRIVING_DATA_DIR = os.path.join(TEST_DATA_DIR, "driving_data")
# Set the ROSE_DATA environment variable to the driving data directory
os.environ["ROSE_DATA"] = str(DRIVING_DATA_DIR)
from replace_landsurface import replace_landsurface # importing here because we need to set the ROSE_DATA env variable before importing # noqa


############################################
## === Integration tests === ##
############################################
def get_test_args(num, start, _type):
test_dir = f"test_{num}"
hres_ic = (
os.path.join(INPUT_DIR, test_dir, "hres_ic")
if _type == "astart"
else "NOT_USED"
)
return [
"script_name",
"--file",
os.path.join(INPUT_DIR, test_dir, "file" + ".tmp"),
"--mask",
os.path.join(INPUT_DIR, test_dir, "mask"),
"--start",
start,
"--type",
_type,
"--hres_ic",
hres_ic,
]


def get_error_msg(num, output, expected_output):
return f"Test {num}: Test output '{output}' does not match the expected output '{expected_output}'!"


@pytest.fixture
def mock_sys_argv():
@contextlib.contextmanager
def _mock_sys_argv(num, start, _type):
with patch("sys.argv", get_test_args(num, start, _type)):
yield mock_sys_argv

return _mock_sys_argv


@pytest.fixture(scope="module")
def working_dir(tmp_path_factory):
return tmp_path_factory.mktemp("replace_landsurface_integration_tests")


@pytest.fixture()
def get_output_path(working_dir):
def _get_output_path(num):
return os.path.join(working_dir, f"output_{num}")

return _get_output_path


@pytest.fixture()
def get_expected_output_path():
def _get_expected_output_path(num):
return os.path.join(OUTPUT_DIR, f"output_{num}")

return _get_expected_output_path

@pytest.fixture(scope="module")
def original_shutil_move():
return shutil.move


@pytest.fixture()
def new_shutil_move(original_shutil_move, get_output_path):
def _new_shutil_move(num):
def _wrapper(src, dst, **kwargs):
output_path = get_output_path(num)
return original_shutil_move(src=src, dst=output_path, **kwargs)

return _wrapper

return _new_shutil_move


@pytest.mark.parametrize(
"num, start, _type",
[
(1, "202202260000", "era5land"),
(2, "202008090000", "barra"),
(3, "202112310000", "astart"),
(4, "202305040000", "era5land"),
],
ids=[
"replace_landsurface_era5land",
"replace_landsurface_barra",
"replace_landsurface_astart",
"replace_landsurface_era5land_2",
],
)
def test_replace_landsurface(
new_shutil_move,
get_output_path,
get_expected_output_path,
mock_sys_argv,
num,
start,
_type,
):
"""
Test the replace_landsurface entry point
"""
with mock_sys_argv(num, start, _type):
with patch("shutil.move", side_effect=new_shutil_move(num)):
replace_landsurface.main()
output = get_output_path(num)
expected_output = get_expected_output_path(num)
# Compare the output file with the expected output
assert filecmp.cmp(output, expected_output), get_error_msg(
num, output, expected_output
)
Loading