diff --git a/.github/workflows/icon4py-qa.yml b/.github/workflows/icon4py-qa.yml index 4e97149ca8..e40d32042b 100644 --- a/.github/workflows/icon4py-qa.yml +++ b/.github/workflows/icon4py-qa.yml @@ -35,21 +35,6 @@ jobs: python -m pip install --upgrade pip setuptools wheel python -m pip install -r ./requirements-dev.txt python -m pip list - - name: Run checks in icon4pytools + - name: Run checks run: | - pre-commit run --config tools/.pre-commit-config.yaml --all-files - - name: Run checks icon4py-model-common - run: | - pre-commit run --config model/common/.pre-commit-config.yaml --all-files - - name: Run checks icon4py-model-driver - run: | - pre-commit run --config model/driver/.pre-commit-config.yaml --all-files - - name: Run checks icon4py-model-atmosphere-dycore - run: | - pre-commit run --config model/atmosphere/dycore/.pre-commit-config.yaml --all-files - - name: Run checks icon4py-model-atmosphere-diffusion - run: | - pre-commit run --config model/atmosphere/diffusion/.pre-commit-config.yaml --all-files - - name: Run checks icon4py-model-atmosphere-advection - run: | - pre-commit run --config model/atmosphere/advection/.pre-commit-config.yaml --all-files + pre-commit run diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..44d7d4d544 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,44 @@ +repos: + - repo: local + hooks: + - id: run-common-precommit + name: Run Model Common Pre-commit + entry: pre-commit run --config model/common/.pre-commit-config.yaml --all-files + language: system + pass_filenames: false + always_run: true + + - id: run-driver-precommit + name: Run Model Driver Pre-commit + entry: pre-commit run --config model/driver/.pre-commit-config.yaml --all-files + language: system + pass_filenames: false + always_run: true + + - id: run-atmosphere-advection-precommit + name: Run Model Atmosphere Advection Pre-commit + entry: pre-commit run --config model/atmosphere/advection/.pre-commit-config.yaml --all-files + language: system + pass_filenames: false + always_run: true + + - id: run-atmosphere-diffusion-precommit + name: Run Model Atmosphere Diffusion Pre-commit + entry: pre-commit run --config model/atmosphere/diffusion/.pre-commit-config.yaml --all-files + language: system + pass_filenames: false + always_run: true + + - id: run-atmosphere-dycore-precommit + name: Run Model Atmosphere Dycore Pre-commit + entry: pre-commit run --config model/atmosphere/dycore/.pre-commit-config.yaml --all-files + language: system + pass_filenames: false + always_run: true + + - id: run-tools-precommit + name: Run Tools Pre-commit + entry: pre-commit run --config tools/.pre-commit-config.yaml --all-files + language: system + pass_filenames: false + always_run: true diff --git a/ci/cscs.yml b/ci/cscs.yml index bab0af8700..9a71720925 100644 --- a/ci/cscs.yml +++ b/ci/cscs.yml @@ -34,6 +34,7 @@ variables: - pyversion_no_dot="${PYTHON_VERSION//./}" - pip install tox clang-format - python -c "import cupy" + - ls ${SERIALIZED_DATA_PATH} variables: SLURM_JOB_NUM_NODES: 1 SLURM_NTASKS: 1 @@ -41,6 +42,8 @@ variables: CRAY_CUDA_MPS: 1 NUM_PROCESSES: auto VIRTUALENV_SYSTEM_SITE_PACKAGES: 1 + CSCS_NEEDED_DATA: icon4py + TEST_DATA_PATH: "/apps/daint/UES/jenkssl/ciext/icon4py" build_job: extends: .build_template @@ -49,14 +52,14 @@ test_model_job_roundtrip_simple_grid: extends: .test_template stage: test script: - - tox -r -c model/ --verbose -- --benchmark-skip -n auto + - tox -r -e run_stencil_tests -c model/ --verbose test_model_job_dace_cpu_simple_grid: extends: .test_template stage: test script: - pip install dace==$DACE_VERSION - - tox -r -e stencil_tests -c model/ --verbose -- --benchmark-skip -n auto --backend=dace_cpu + - tox -r -e run_stencil_tests -c model/ --verbose -- --backend=dace_cpu only: - main allow_failure: true @@ -66,7 +69,7 @@ test_model_job_dace_gpu_simple_grid: stage: test script: - pip install dace==$DACE_VERSION - - tox -r -e stencil_tests -c model/ --verbose -- --benchmark-skip -n auto --backend=dace_gpu + - tox -r -e run_stencil_tests -c model/ --verbose -- --backend=dace_gpu only: - main allow_failure: true @@ -75,13 +78,13 @@ test_model_job_gtfn_cpu_simple_grid: extends: .test_template stage: test script: - - tox -r -e stencil_tests -c model/ --verbose -- --benchmark-skip -n auto --backend=gtfn_cpu + - tox -r -e run_stencil_tests -c model/ --verbose -- --backend=gtfn_cpu test_model_job_gtfn_gpu_simple_grid: extends: .test_template stage: test script: - - tox -r -e stencil_tests -c model/ --verbose -- --benchmark-skip -n auto --backend=gtfn_gpu + - tox -r -e run_stencil_tests -c model/ --verbose -- --backend=gtfn_gpu test_tools_job: extends: .test_template @@ -89,34 +92,34 @@ test_tools_job: script: - tox -r -c tools/ --verbose -benchmark_model_dace_cpu_simple_grid: +benchmark_model_dace_cpu_icon_grid: extends: .test_template stage: benchmark script: - pip install dace==$DACE_VERSION - - tox -r -e stencil_tests -c model/ --verbose -- --benchmark-only --backend=dace_cpu --grid=simple_grid + - tox -r -e run_benchmarks -c model/ -- --backend=dace_cpu --grid=icon_grid only: - main when: manual -benchmark_model_dace_gpu_simple_grid: +benchmark_model_dace_gpu_icon_grid: extends: .test_template stage: benchmark script: - pip install dace==$DACE_VERSION - - tox -r -e stencil_tests -c model/ --verbose -- --benchmark-only --backend=dace_gpu --grid=simple_grid + - tox -r -e run_benchmarks -c model/ -- --backend=dace_gpu --grid=icon_grid only: - main when: manual -benchmark_model_gtfn_cpu_simple_grid: +benchmark_model_gtfn_cpu_icon_grid: extends: .test_template stage: benchmark script: - - tox -r -e stencil_tests -c model/ --verbose -- --benchmark-only --backend=gtfn_cpu --grid=simple_grid + - tox -r -e run_benchmarks -c model/ -- --backend=gtfn_cpu --grid=icon_grid -benchmark_model_gtfn_gpu_simple_grid: +benchmark_model_gtfn_gpu_icon_grid: extends: .test_template stage: benchmark script: - - tox -r -e stencil_tests -c model/ --verbose -- --benchmark-only --backend=gtfn_gpu --grid=simple_grid + - tox -r -e run_benchmarks -c model/ -- --backend=gtfn_gpu --grid=icon_grid diff --git a/model/atmosphere/diffusion/tests/conftest.py b/model/atmosphere/diffusion/tests/conftest.py index 117381f373..73877456fa 100644 --- a/model/atmosphere/diffusion/tests/conftest.py +++ b/model/atmosphere/diffusion/tests/conftest.py @@ -16,4 +16,5 @@ ) from icon4py.model.common.test_utils.helpers import ( # noqa : F401 # fixtures from test_utils backend, + uses_icon_grid_with_otf, ) diff --git a/model/atmosphere/diffusion/tests/diffusion_stencil_tests/test_apply_diffusion_to_theta_and_exner.py b/model/atmosphere/diffusion/tests/diffusion_stencil_tests/test_apply_diffusion_to_theta_and_exner.py index fa1e392aa0..228af398a5 100644 --- a/model/atmosphere/diffusion/tests/diffusion_stencil_tests/test_apply_diffusion_to_theta_and_exner.py +++ b/model/atmosphere/diffusion/tests/diffusion_stencil_tests/test_apply_diffusion_to_theta_and_exner.py @@ -81,7 +81,12 @@ def reference( return dict(theta_v=theta_v, exner=exner) @pytest.fixture - def input_data(self, grid): + def input_data(self, grid, uses_icon_grid_with_otf): + if uses_icon_grid_with_otf: + pytest.skip( + "Execution domain needs to be restricted or boundary taken into account in stencil." + ) + kh_smag_e = random_field(grid, EdgeDim, KDim) inv_dual_edge_length = random_field(grid, EdgeDim) theta_v_in = random_field(grid, CellDim, KDim) diff --git a/model/atmosphere/diffusion/tests/diffusion_stencil_tests/test_apply_diffusion_to_vn.py b/model/atmosphere/diffusion/tests/diffusion_stencil_tests/test_apply_diffusion_to_vn.py index e121b428c8..31784bfa6a 100644 --- a/model/atmosphere/diffusion/tests/diffusion_stencil_tests/test_apply_diffusion_to_vn.py +++ b/model/atmosphere/diffusion/tests/diffusion_stencil_tests/test_apply_diffusion_to_vn.py @@ -98,7 +98,12 @@ def reference( return dict(vn=vn) @pytest.fixture - def input_data(self, grid): + def input_data(self, grid, uses_icon_grid_with_otf): + if uses_icon_grid_with_otf: + pytest.skip( + "Execution domain needs to be restricted or boundary taken into account in stencil." + ) + edge = indices_field(EdgeDim, grid, is_halfdim=False, dtype=int32) u_vert = random_field(grid, VertexDim, KDim) diff --git a/model/atmosphere/diffusion/tests/diffusion_stencil_tests/test_calculate_nabla2_for_z.py b/model/atmosphere/diffusion/tests/diffusion_stencil_tests/test_calculate_nabla2_for_z.py index b44cd8a26c..d166bac1bc 100644 --- a/model/atmosphere/diffusion/tests/diffusion_stencil_tests/test_calculate_nabla2_for_z.py +++ b/model/atmosphere/diffusion/tests/diffusion_stencil_tests/test_calculate_nabla2_for_z.py @@ -14,7 +14,6 @@ import numpy as np import pytest from gt4py.next.ffront.fbuiltins import int32 -from gt4py.next.program_processors.otf_compile_executor import OTFCompileExecutor from icon4py.model.atmosphere.diffusion.stencils.calculate_nabla2_for_z import ( calculate_nabla2_for_z, @@ -55,11 +54,12 @@ def reference( return dict(z_nabla2_e=z_nabla2_e) @pytest.fixture - def input_data(self, grid, backend): - if isinstance(backend, OTFCompileExecutor): + def input_data(self, grid, uses_icon_grid_with_otf): + if uses_icon_grid_with_otf: pytest.skip( "Execution domain needs to be restricted or boundary taken into account in stencil." ) + kh_smag_e = random_field(grid, EdgeDim, KDim, dtype=vpfloat) inv_dual_edge_length = random_field(grid, EdgeDim, dtype=wpfloat) theta_v = random_field(grid, CellDim, KDim, dtype=wpfloat) diff --git a/model/atmosphere/dycore/tests/conftest.py b/model/atmosphere/dycore/tests/conftest.py index 117381f373..73877456fa 100644 --- a/model/atmosphere/dycore/tests/conftest.py +++ b/model/atmosphere/dycore/tests/conftest.py @@ -16,4 +16,5 @@ ) from icon4py.model.common.test_utils.helpers import ( # noqa : F401 # fixtures from test_utils backend, + uses_icon_grid_with_otf, ) diff --git a/model/atmosphere/dycore/tests/dycore_stencil_tests/test_fused_velocity_advection_stencil_19_to_20.py b/model/atmosphere/dycore/tests/dycore_stencil_tests/test_fused_velocity_advection_stencil_19_to_20.py index eaf3571d2a..23955d7fce 100644 --- a/model/atmosphere/dycore/tests/dycore_stencil_tests/test_fused_velocity_advection_stencil_19_to_20.py +++ b/model/atmosphere/dycore/tests/dycore_stencil_tests/test_fused_velocity_advection_stencil_19_to_20.py @@ -116,7 +116,12 @@ def reference( return dict(ddt_vn_apc=ddt_vn_apc) @pytest.fixture - def input_data(self, grid): + def input_data(self, grid, uses_icon_grid_with_otf): + if uses_icon_grid_with_otf: + pytest.skip( + "Execution domain needs to be restricted or boundary taken into account in stencil." + ) + z_kin_hor_e = random_field(grid, EdgeDim, KDim) coeff_gradekin = random_field(grid, EdgeDim, E2CDim) coeff_gradekin_new = as_1D_sparse_field(coeff_gradekin, ECDim) diff --git a/model/atmosphere/dycore/tests/dycore_stencil_tests/test_fused_velocity_advection_stencil_1_to_7.py b/model/atmosphere/dycore/tests/dycore_stencil_tests/test_fused_velocity_advection_stencil_1_to_7.py index 7d1c9c800f..0500c92b97 100644 --- a/model/atmosphere/dycore/tests/dycore_stencil_tests/test_fused_velocity_advection_stencil_1_to_7.py +++ b/model/atmosphere/dycore/tests/dycore_stencil_tests/test_fused_velocity_advection_stencil_1_to_7.py @@ -190,7 +190,12 @@ def reference( ) @pytest.fixture - def input_data(self, grid): + def input_data(self, grid, uses_icon_grid_with_otf): + if uses_icon_grid_with_otf: + pytest.skip( + "Execution domain needs to be restricted or boundary taken into account in stencil." + ) + c_intp = random_field(grid, VertexDim, V2CDim) vn = random_field(grid, EdgeDim, KDim) rbf_vec_coeff_e = random_field(grid, EdgeDim, E2C2EDim) diff --git a/model/common/src/icon4py/model/common/grid/icon.py b/model/common/src/icon4py/model/common/grid/icon.py index c99ad6c93a..5672ea9886 100644 --- a/model/common/src/icon4py/model/common/grid/icon.py +++ b/model/common/src/icon4py/model/common/grid/icon.py @@ -19,7 +19,6 @@ C2E2CDim, C2E2CODim, C2EDim, - C2VDim, CECDim, CEDim, CellDim, @@ -56,7 +55,6 @@ def __init__(self): "E2C2V": (self._get_offset_provider, E2C2VDim, EdgeDim, VertexDim), "V2E": (self._get_offset_provider, V2EDim, VertexDim, EdgeDim), "V2C": (self._get_offset_provider, V2CDim, VertexDim, CellDim), - "C2V": (self._get_offset_provider, C2VDim, CellDim, VertexDim), "E2ECV": (self._get_offset_provider_for_sparse_fields, E2C2VDim, EdgeDim, ECVDim), "C2CEC": (self._get_offset_provider_for_sparse_fields, C2E2CDim, CellDim, CECDim), "C2CE": (self._get_offset_provider_for_sparse_fields, C2EDim, CellDim, CEDim), diff --git a/model/common/src/icon4py/model/common/test_utils/datatest_fixtures.py b/model/common/src/icon4py/model/common/test_utils/datatest_fixtures.py index 25d45eb7aa..f449e600b6 100644 --- a/model/common/src/icon4py/model/common/test_utils/datatest_fixtures.py +++ b/model/common/src/icon4py/model/common/test_utils/datatest_fixtures.py @@ -20,7 +20,7 @@ DATA_URIS_APE, GLOBAL_EXPERIMENT, REGIONAL_EXPERIMENT, - SER_DATA_BASEPATH, + SERIALIZED_DATA_PATH, create_icon_serial_data_provider, get_datapath_for_experiment, get_processor_properties_for_run, @@ -40,7 +40,7 @@ def processor_props(request): @pytest.fixture(scope="session") def ranked_data_path(processor_props): - return get_ranked_data_path(SER_DATA_BASEPATH, processor_props) + return get_ranked_data_path(SERIALIZED_DATA_PATH, processor_props) @pytest.fixture diff --git a/model/common/src/icon4py/model/common/test_utils/datatest_utils.py b/model/common/src/icon4py/model/common/test_utils/datatest_utils.py index 011767bbe9..97dff04209 100644 --- a/model/common/src/icon4py/model/common/test_utils/datatest_utils.py +++ b/model/common/src/icon4py/model/common/test_utils/datatest_utils.py @@ -10,17 +10,29 @@ # distribution for a copy of the license or check <https://www.gnu.org/licenses/>. # # SPDX-License-Identifier: GPL-3.0-or-later - +import os from pathlib import Path from icon4py.model.common.decomposition.definitions import get_processor_properties -TEST_UTILS_PATH = Path(__file__).parent -MODEL_PATH = TEST_UTILS_PATH.parent.parent -COMMON_PATH = MODEL_PATH.parent.parent.parent.parent -BASE_PATH = COMMON_PATH.parent.joinpath("testdata") +DEFAULT_TEST_DATA_FOLDER = "testdata" + + +def get_test_data_root_path() -> Path: + test_utils_path = Path(__file__).parent + model_path = test_utils_path.parent.parent + common_path = model_path.parent.parent.parent.parent + env_base_path = os.getenv("TEST_DATA_PATH") + + if env_base_path: + return Path(env_base_path) + else: + return common_path.parent.joinpath(DEFAULT_TEST_DATA_FOLDER) + +TEST_DATA_ROOT = get_test_data_root_path() +SERIALIZED_DATA_PATH = TEST_DATA_ROOT.joinpath("ser_icondata") # TODO: a run that contains all the fields needed for dycore, diffusion, interpolation fields needs to be consolidated DATA_URIS = { @@ -29,7 +41,6 @@ 4: "https://polybox.ethz.ch/index.php/s/UIHOVJs6FVPpz9V/download", } DATA_URIS_APE = {1: "https://polybox.ethz.ch/index.php/s/SdlHTlsHCwcn5J5/download"} -SER_DATA_BASEPATH = BASE_PATH.joinpath("ser_icondata") REGIONAL_EXPERIMENT = "mch_ch_r04b09_dsl" GLOBAL_EXPERIMENT = "exclaim_ape_R02B04" diff --git a/model/common/src/icon4py/model/common/test_utils/grid_utils.py b/model/common/src/icon4py/model/common/test_utils/grid_utils.py index c787b9edae..bc3b742a5e 100644 --- a/model/common/src/icon4py/model/common/test_utils/grid_utils.py +++ b/model/common/src/icon4py/model/common/test_utils/grid_utils.py @@ -15,7 +15,7 @@ from icon4py.model.common.decomposition.definitions import SingleNodeRun from icon4py.model.common.test_utils.datatest_utils import ( - SER_DATA_BASEPATH, + SERIALIZED_DATA_PATH, create_icon_serial_data_provider, get_datapath_for_experiment, get_processor_properties_for_run, @@ -25,11 +25,11 @@ def get_icon_grid(): processor_properties = get_processor_properties_for_run(SingleNodeRun()) - ranked_path = get_ranked_data_path(SER_DATA_BASEPATH, processor_properties) + ranked_path = get_ranked_data_path(SERIALIZED_DATA_PATH, processor_properties) data_path = get_datapath_for_experiment(ranked_path) icon_data_provider = create_icon_serial_data_provider(data_path, processor_properties) grid_savepoint = icon_data_provider.from_savepoint_grid() - return grid_savepoint.construct_icon_grid(limited_area=True) + return grid_savepoint.construct_icon_grid() @pytest.fixture diff --git a/model/common/src/icon4py/model/common/test_utils/helpers.py b/model/common/src/icon4py/model/common/test_utils/helpers.py index 883a0aa75e..504fefa1d5 100644 --- a/model/common/src/icon4py/model/common/test_utils/helpers.py +++ b/model/common/src/icon4py/model/common/test_utils/helpers.py @@ -21,8 +21,10 @@ from gt4py.next import common as gt_common from gt4py.next import constructors from gt4py.next.ffront.decorator import Program +from gt4py.next.program_processors.otf_compile_executor import OTFCompileExecutor from ..grid.base import BaseGrid +from ..grid.icon import IconGrid try: @@ -176,12 +178,10 @@ def _test_execution_benchmark(self, pytestconfig, grid, backend, input_data, ben pytest.skip("Test skipped due to 'benchmark-disable' option.") else: input_data = allocate_data(backend, input_data) - benchmark.pedantic( + benchmark( self.PROGRAM.with_backend(backend), - args=(), - kwargs={**input_data, "offset_provider": grid.get_all_offset_providers()}, - iterations=1, - rounds=1, + **input_data, + offset_provider=grid.get_all_offset_providers(), ) else: @@ -219,3 +219,15 @@ def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) setattr(cls, f"test_{cls.__name__}", _test_validation) setattr(cls, f"test_{cls.__name__}_benchmark", _test_execution_benchmark) + + +@pytest.fixture +def uses_icon_grid_with_otf(backend, grid): + """Check whether we are using a compiled backend with the icon_grid. + + Is needed to skip certain stencils where the execution domain needs to be restricted or boundary taken into account. + """ + if hasattr(backend, "executor") and isinstance(grid, IconGrid): + if isinstance(backend.executor, OTFCompileExecutor): + return True + return False diff --git a/model/common/tests/grid_tests/conftest.py b/model/common/tests/grid_tests/conftest.py index eaca2ba48d..639e8e1990 100644 --- a/model/common/tests/grid_tests/conftest.py +++ b/model/common/tests/grid_tests/conftest.py @@ -26,10 +26,10 @@ processor_props, ranked_data_path, ) -from icon4py.model.common.test_utils.datatest_utils import BASE_PATH +from icon4py.model.common.test_utils.datatest_utils import TEST_DATA_ROOT -grids_path = BASE_PATH.joinpath("grids") +grids_path = TEST_DATA_ROOT.joinpath("grids") r04b09_dsl_grid_path = grids_path.joinpath("mch_ch_r04b09_dsl") r04b09_dsl_data_file = r04b09_dsl_grid_path.joinpath("mch_ch_r04b09_dsl_grids_v1.tar.gz").name r02b04_global_grid_path = grids_path.joinpath("r02b04_global") diff --git a/model/tox.ini b/model/tox.ini index 0783f3343e..7aed4fb051 100644 --- a/model/tox.ini +++ b/model/tox.ini @@ -11,6 +11,7 @@ skipsdist = true passenv = PIP_USER PYTHONUSERBASE + TEST_DATA_PATH deps = -r {toxinidir}/requirements-dev.txt commands = @@ -25,10 +26,16 @@ allowlist_externals = /bin/bash rm -[testenv:stencil_tests] +[testenv:run_stencil_tests] commands = - pytest -v -s -m "not slow_tests" --cov --cov-append atmosphere/diffusion/tests/diffusion_stencil_tests {posargs} - pytest -v -s -m "not slow_tests" --cov --cov-append atmosphere/dycore/tests/dycore_stencil_tests {posargs} + pytest -v -s -m "not slow_tests" --cov --cov-append atmosphere/diffusion/tests/diffusion_stencil_tests --benchmark-skip -n auto {posargs} + pytest -v -s -m "not slow_tests" --cov --cov-append atmosphere/dycore/tests/dycore_stencil_tests --benchmark-skip -n auto {posargs} + +[testenv:run_benchmarks] +commands = + pytest -s -m "not slow_tests" atmosphere/diffusion/tests/diffusion_stencil_tests --benchmark-only {posargs} + pytest -s -m "not slow_tests" atmosphere/dycore/tests/dycore_stencil_tests --benchmark-only {posargs} + [testenv:dev] setenv =