-
Notifications
You must be signed in to change notification settings - Fork 16
[Refactor | Feature] Introducing NDSL backend #370
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
Merged
romanc
merged 51 commits into
NOAA-GFDL:develop
from
FlorianDeconinck:feature/NDSL_Backend
Mar 4, 2026
Merged
Changes from 29 commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
e8f0f7f
Introducing Backend and updating downstream NDSL code
FlorianDeconinck 706a120
Lint new files
FlorianDeconinck 24d61aa
Some docs
FlorianDeconinck 359882a
Re-order init to go around the circluar dependency (and skip isort on…
FlorianDeconinck 52d5f59
Add backend exists check
FlorianDeconinck b849948
Add Backend equal and hash operators
FlorianDeconinck ba7909b
Fix forwarding of NDSL backend into GT4Py
FlorianDeconinck 3ece5dd
Fix forwarding of NDSL backend into GT4Py
FlorianDeconinck c61cd1e
Fix forwarding of NDSL backend into GT4Py
FlorianDeconinck 18cf989
Update all tests
FlorianDeconinck d4be82b
Save Backend as string in Quantity.attrs
FlorianDeconinck 2fb0de5
Fix bad backend given to stree merge test
FlorianDeconinck ee7e872
Introduce short name shortcuts
FlorianDeconinck 942dd7f
Merge branch 'develop' into feature/NDSL_Backend
FlorianDeconinck 305e785
Lint
FlorianDeconinck e7e4481
Fix translate: Backned is already properly built
FlorianDeconinck e1cdc6e
Add concatenation operations
FlorianDeconinck e68bcfc
Deprecate inlined `is_gpu_backend` check
FlorianDeconinck 03b4c50
Documentation
FlorianDeconinck bc5590e
Rework shortcuts for Final[Backend] variable
FlorianDeconinck d0381a8
Move `is_fortran_asligned` to Backend
FlorianDeconinck 998b3f2
Remove wrong default to `| None`
FlorianDeconinck 5d83835
Simplify the G2G comms test
FlorianDeconinck 31a8586
Better checking of expected Error
FlorianDeconinck 8e5dcfe
Lint
FlorianDeconinck 09ae4fe
Properly use global backend shortcuts within API shortcuts
FlorianDeconinck b66c2bd
Lint
FlorianDeconinck 32b4372
Fix unit tests
FlorianDeconinck 5e6dbdb
Lint
FlorianDeconinck b18bc5e
Added guardrail for Boilerplate code
FlorianDeconinck dac69a6
Merge branch 'develop' into feature/NDSL_Backend
FlorianDeconinck dd8dfc8
Expose loop_order, rework python backends
FlorianDeconinck d02683e
Fix tests
FlorianDeconinck b0b695d
Fix test caches
FlorianDeconinck d3c0b17
More fix to tests
FlorianDeconinck 1837d96
PR updartes: move test in config/, made loop_order an enum
FlorianDeconinck ffa6e39
Add a `force_build` option on the `set_distributed_cache` for fortran…
FlorianDeconinck b664b32
Merge branch 'develop' into feature/NDSL_Backend
FlorianDeconinck eec242d
Lint
FlorianDeconinck d270177
[TMP CI] Shift `pyFV3` translate to update branch
FlorianDeconinck 08891fc
Space change to trigger CI
FlorianDeconinck 4cc245c
Pull on test branch for pySHiELD CI
FlorianDeconinck 5cdb599
Update `CompilationConfig` to turn backend as a string into `Backend`
FlorianDeconinck 2d940a7
Typo
FlorianDeconinck 8bf39c8
Fix tests for CompilationConfig
FlorianDeconinck 5c13eae
Fix backend save in dace_config
FlorianDeconinck 40f0a2d
Fix dace config load
FlorianDeconinck ebcb258
[TMP CI] Update Pace branch
FlorianDeconinck c321fd6
tests: simple cleanup as code review
romanc a25176b
Merge remote-tracking branch 'origin/develop' into feature/NDSL_Backend
romanc 01e8717
tmp ci: point to noop translate test in hooks
romanc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| from .backend import ( | ||
| Backend, | ||
| BackendFramework, | ||
| BackendStrategy, | ||
| BackendTargetDevice, | ||
| backend_cpu, | ||
| backend_gpu, | ||
| backend_python, | ||
| ) | ||
|
|
||
|
|
||
| __all__ = [ | ||
| "Backend", | ||
| "BackendFramework", | ||
| "BackendStrategy", | ||
| "BackendTargetDevice", | ||
| "backend_python", | ||
| "backend_cpu", | ||
| "backend_gpu", | ||
| ] |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from enum import Enum | ||
| from typing import Any, Final | ||
|
|
||
| import gt4py.cartesian.backend as gt_backend | ||
|
|
||
|
|
||
| class BackendStrategy(Enum): | ||
| """Strategy for the code execution""" | ||
|
|
||
| STENCIL = "st" | ||
| ORCHESTRATION = "orch" | ||
|
|
||
|
|
||
| class BackendTargetDevice(Enum): | ||
| """Target device""" | ||
|
|
||
| CPU = "cpu" | ||
| GPU = "gpu" | ||
|
|
||
|
|
||
| class BackendFramework(Enum): | ||
| """Main lower-level framework (or language) backend relies on""" | ||
|
|
||
| GRIDTOOLS = "gt" | ||
| DACE = "dace" | ||
| PYTHON = "python" | ||
|
|
||
|
|
||
| _NDSL_TO_GT4PY_BACKEND_NAMING = { | ||
| "st:python:cpu:debug": "debug", | ||
| "st:python:cpu:numpy": "numpy", | ||
| "st:gt:cpu:IJK": "gt:cpu_kfirst", | ||
| "st:gt:cpu:KJI": "gt:cpu_ifirst", | ||
| "st:gt:gpu:KJI": "gt:gpu", | ||
| "st:dace:cpu:IJK": "dace:cpu_kfirst", | ||
| "orch:dace:cpu:IJK": "dace:cpu_kfirst", | ||
| "st:dace:cpu:KIJ": "dace:cpu", | ||
| "orch:dace:cpu:KIJ": "dace:cpu", | ||
| "st:dace:cpu:KJI": "dace:cpu_KJI", | ||
| "orch:dace:cpu:KJI": "dace:cpu_KJI", | ||
| "st:dace:gpu:KJI": "dace:gpu", | ||
| "orch:dace:gpu:KJI": "dace:gpu", | ||
| } | ||
| """Internal: match the NDSL backend names with the GT4Py names""" | ||
|
|
||
| _FORTRAN_LOOP_LAYOUT = (2, 1, 0) | ||
| """Fortran is a column-first (or stride-first) memory system, | ||
| which in the internal gt4py loop layout means I (or axis[0]) has | ||
| the higher value, e.g. "higher importance to run first": | ||
|
|
||
| for k # Layout=0 | ||
| for j # Layout=1 | ||
| for i # Layout=2 | ||
|
|
||
| """ | ||
|
|
||
|
|
||
| class Backend: | ||
| """Backend for NDSL. | ||
|
|
||
| The backend is a string concatenating information on the intent of the user | ||
| for a given execution separated by a ':'. | ||
|
|
||
| It describes to NDSL the strategy, device and framework to be used | ||
| on the frontend code. Additionally, it gives a hint toward the macro-strategy | ||
| for loop ordering (IJK, KJI, etc.) or a more broad intent (debug, numpy). | ||
|
|
||
| For convenience, shorcuts are given to the most common needs ( | ||
| `backend_python`, `backend_cpu`, `backend_gpu`). | ||
| """ | ||
|
|
||
| def __init__(self, ndsl_backend: str) -> None: | ||
| # Checks for existence and form | ||
| if ndsl_backend not in _NDSL_TO_GT4PY_BACKEND_NAMING: | ||
| raise ValueError( | ||
| f"Unknown {ndsl_backend}, options are {list(_NDSL_TO_GT4PY_BACKEND_NAMING.keys())}" | ||
| ) | ||
| parts = ndsl_backend.split(":") | ||
| if len(parts) != 4: | ||
| raise ValueError(f"Backend {ndsl_backend} is ill-formed.") | ||
|
romanc marked this conversation as resolved.
|
||
|
|
||
| # Breakdown and save into internal parameters | ||
| self._humanly_readable = ndsl_backend | ||
| self._strategy = BackendStrategy(parts[0].lower()) | ||
| self._framework = BackendFramework(parts[1].lower()) | ||
| self._device = BackendTargetDevice(parts[2].lower()) | ||
| self._loop_order = parts[3] | ||
|
FlorianDeconinck marked this conversation as resolved.
Outdated
FlorianDeconinck marked this conversation as resolved.
Outdated
|
||
|
|
||
| # Check GPU capacity | ||
| if ( | ||
| self._device == BackendTargetDevice.GPU | ||
| and gt_backend.from_name(self.as_gt4py()).storage_info["device"] != "gpu" | ||
| ): | ||
| raise ValueError( | ||
| f"NDSL backend requested ({self._humanly_readable}) tagets GPU," | ||
| f"but requests a non-GPU backend from GT4Py ({self.as_gt4py()})." | ||
| ) | ||
|
|
||
| def __str__(self) -> str: | ||
| return self.as_humanly_readable() | ||
|
|
||
| def __repr__(self) -> str: | ||
| return self.as_humanly_readable() | ||
|
|
||
| def __eq__(self, other: object) -> bool: | ||
| return self._humanly_readable == self._humanly_readable | ||
|
romanc marked this conversation as resolved.
Outdated
|
||
|
|
||
| def __hash__(self) -> int: | ||
| return hash(self._humanly_readable) | ||
|
|
||
| def __add__(self, other: str) -> Any: | ||
| """Concatenation operators""" | ||
| if isinstance(other, Backend): | ||
| raise TypeError("OperationError: Backend cannot add to another Backend") | ||
| return str(self) + other | ||
|
|
||
| def __radd__(self, other: str) -> Any: | ||
| """Concatenation operators""" | ||
| if isinstance(other, Backend): | ||
| raise TypeError("OperationError: Backend cannot add to another Backend") | ||
| return other + str(self) | ||
|
|
||
| @staticmethod | ||
| def python() -> Backend: | ||
| """Default backend for quick iterative work.""" | ||
| return backend_python | ||
|
|
||
| @staticmethod | ||
| def cpu() -> Backend: | ||
| """Default performance backend targeting CPU device""" | ||
| return backend_cpu | ||
|
|
||
| @staticmethod | ||
| def gpu() -> Backend: | ||
| """Default performance backend targeting GPU device""" | ||
| return backend_gpu | ||
|
romanc marked this conversation as resolved.
|
||
|
|
||
| @property | ||
| def device(self) -> BackendTargetDevice: | ||
| return self._device | ||
|
|
||
| @property | ||
| def framework(self) -> BackendFramework: | ||
| return self._framework | ||
|
|
||
|
FlorianDeconinck marked this conversation as resolved.
|
||
| def as_gt4py(self) -> str: | ||
| """Given an NDSL backend, give back a GT4Py equivalent""" | ||
| return _NDSL_TO_GT4PY_BACKEND_NAMING[self._humanly_readable] | ||
|
|
||
| def as_humanly_readable(self) -> str: | ||
| return self._humanly_readable | ||
|
|
||
| def as_safe_for_path(self) -> str: | ||
| return self._humanly_readable.replace(":", "_") | ||
|
|
||
|
FlorianDeconinck marked this conversation as resolved.
|
||
| def is_orchestrated(self) -> bool: | ||
| return self._strategy == BackendStrategy.ORCHESTRATION | ||
|
|
||
| def is_stencil(self) -> bool: | ||
| return self._strategy == BackendStrategy.STENCIL | ||
|
|
||
| def is_gpu_backend(self) -> bool: | ||
| return self._device == BackendTargetDevice.GPU | ||
|
|
||
| def is_fortran_aligned(self) -> bool: | ||
| """Check that the standard 3D field on cartesian axis is memory-aligned with Fortran | ||
| striding.""" | ||
| return _FORTRAN_LOOP_LAYOUT == gt_backend.from_name( | ||
|
FlorianDeconinck marked this conversation as resolved.
|
||
| self.as_gt4py() | ||
| ).storage_info["layout_map"](("I", "J", "K")) | ||
|
|
||
|
|
||
| backend_python: Final[Backend] = Backend("st:python:cpu:debug") | ||
| """Default backend for quick iterative work.""" | ||
|
|
||
| backend_cpu: Final[Backend] = Backend("orch:dace:cpu:IJK") | ||
| """Default performance backend targeting CPU device""" | ||
|
FlorianDeconinck marked this conversation as resolved.
Outdated
|
||
|
|
||
| backend_gpu: Final[Backend] = Backend("orch:dace:gpu:KJI") | ||
| """Default performance backend targeting GPU device""" | ||
|
FlorianDeconinck marked this conversation as resolved.
Outdated
|
||
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.