diff --git a/tools/README.md b/tools/README.md
index 41f040e0dc..2a2d5c29c6 100644
--- a/tools/README.md
+++ b/tools/README.md
@@ -192,6 +192,66 @@ Additionally, there are the following keyword arguments:
- `noaccenddata`: Takes a boolean string input and controls whether a `!$ACC END DATA` directive is generated or not. Defaults to false.
+#### `!$DSL FUSED START STENCIL()`
+
+This directive denotes the start of a fused stencil. Required arguments are `name`, `vertical_lower`, `vertical_upper`, `horizontal_lower`, `horizontal_upper`. The value for `name` must correspond to a stencil found in one of the stencil modules inside `icon4py`, and all fields defined in the directive must correspond to the fields defined in the respective icon4py stencil. Optionally, absolute and relative tolerances for the output fields can also be set using the `_tol` or `_abs` suffixes respectively. For each stencil, an ACC ENTER/EXIT DATA statements will be created. This ACC ENTER/EXIT DATA region contains the before fileds of the according stencil. An example call looks like this:
+
+```fortran
+ !$DSL START FUSED STENCIL(name=calculate_diagnostic_quantities_for_turbulence; &
+ !$DSL kh_smag_ec=kh_smag_ec(:,:,1); vn=p_nh_prog%vn(:,:,1); e_bln_c_s=p_int%e_bln_c_s(:,:,1); &
+ !$DSL geofac_div=p_int%geofac_div(:,:,1); diff_multfac_smag=diff_multfac_smag(:); &
+ !$DSL wgtfac_c=p_nh_metrics%wgtfac_c(:,:,1); div_ic=p_nh_diag%div_ic(:,:,1); &
+ !$DSL hdef_ic=p_nh_diag%hdef_ic(:,:,1); &
+ !$DSL div_ic_abs_tol=1e-18_wp; vertical_lower=2; &
+ !$DSL vertical_upper=nlev; horizontal_lower=i_startidx; horizontal_upper=i_endidx)
+```
+
+#### `!$DSL END FUSED STENCIL()`
+
+This directive denotes the end of a fused stencil. The required argument is `name`, which must match the name of the preceding `START STENCIL` directive.
+
+Note that each `START STENCIL` and `END STENCIL` will be transformed into a `DELETE` section, when using the `--fused` mode.
+Together, the `START FUSED STENCIL` and `END FUSED STENCIL` directives result in the following generated code at the start and end of a stencil respectively.
+
+```fortran
+ !$ACC DATA CREATE( &
+ !$ACC kh_smag_e_before, &
+ !$ACC kh_smag_ec_before, &
+ !$ACC z_nabla2_e_before ) &
+ !$ACC IF ( i_am_accel_node )
+
+#ifdef __DSL_VERIFY
+ !$ACC KERNELS IF( i_am_accel_node ) DEFAULT(PRESENT) ASYNC(1)
+ kh_smag_e_before(:, :, :) = kh_smag_e(:, :, :)
+ kh_smag_ec_before(:, :, :) = kh_smag_ec(:, :, :)
+ z_nabla2_e_before(:, :, :) = z_nabla2_e(:, :, :)
+ !$ACC END KERNELS
+```
+
+```fortran
+call wrap_run_calculate_diagnostic_quantities_for_turbulence( &
+ kh_smag_ec=kh_smag_ec(:, :, 1), &
+ vn=p_nh_prog%vn(:, :, 1), &
+ e_bln_c_s=p_int%e_bln_c_s(:, :, 1), &
+ geofac_div=p_int%geofac_div(:, :, 1), &
+ diff_multfac_smag=diff_multfac_smag(:), &
+ wgtfac_c=p_nh_metrics%wgtfac_c(:, :, 1), &
+ div_ic=p_nh_diag%div_ic(:, :, 1), &
+ div_ic_before=div_ic_before(:, :, 1), &
+ hdef_ic=p_nh_diag%hdef_ic(:, :, 1), &
+ hdef_ic_before=hdef_ic_before(:, :, 1), &
+ div_ic_abs_tol=1e-18_wp, &
+ vertical_lower=2, &
+ vertical_upper=nlev, &
+ horizontal_lower=i_startidx, &
+ horizontal_upper=i_endidx)
+
+!$ACC EXIT DATA DELETE( &
+!$ACC div_ic_before, &
+!$ACC hdef_ic_before ) &
+!$ACC IF ( i_am_accel_node )
+```
+
#### `!$DSL INSERT()`
This directive allows the user to generate any text that is placed between the parentheses. This is useful for situations where custom code generation is necessary.
@@ -204,6 +264,16 @@ This directive allows generating an nvtx start profile data statement, and takes
This directive allows generating an nvtx end profile statement.
+#### `!$DSL START DELETE
+
+This directive allows to disable code. The code is only disabled if both the fused mode and the substition mode are enabled.
+The `START DELETE` indicates the starting line from which on code is deleted.
+
+#### `!$DSL END DELETE`
+
+This directive allows to disable code. The code is only disabled if both the fused mode and the substition mode are enabled.
+The `END DELETE` indicates the ending line from which on code is deleted.
+
#### `!$DSL ENDIF()`
This directive generates an `#endif` statement.
diff --git a/tools/src/icon4pytools/liskov/parsing/transform.py b/tools/src/icon4pytools/liskov/parsing/transform.py
index 3537a2dc63..f5ace9add4 100644
--- a/tools/src/icon4pytools/liskov/parsing/transform.py
+++ b/tools/src/icon4pytools/liskov/parsing/transform.py
@@ -54,6 +54,7 @@ def __call__(self, data: Any = None) -> IntegrationCodeInterface:
else:
logger.info("Removing fused stencils.")
self._remove_fused_stencils()
+ self._remove_delete()
return self.parsed
@@ -113,3 +114,7 @@ def _remove_stencils(self, stencils_to_remove: list[CodeGenInput]) -> None:
def _remove_fused_stencils(self) -> None:
self.parsed.StartFusedStencil = []
self.parsed.EndFusedStencil = []
+
+ def _remove_delete(self) -> None:
+ self.parsed.StartDelete = []
+ self.parsed.EndDelete = []
diff --git a/tools/tests/liskov/test_transform.py b/tools/tests/liskov/test_transform.py
new file mode 100644
index 0000000000..4c2a60454c
--- /dev/null
+++ b/tools/tests/liskov/test_transform.py
@@ -0,0 +1,201 @@
+# ICON4Py - ICON inspired code in Python and GT4Py
+#
+# Copyright (c) 2022, ETH Zurich and MeteoSwiss
+# All rights reserved.
+#
+# This file is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or any later
+# version. See the LICENSE.txt file at the top-level directory of this
+# distribution for a copy of the license or check .
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+
+import pytest
+
+from icon4pytools.liskov.codegen.integration.interface import (
+ BoundsData,
+ DeclareData,
+ EndCreateData,
+ EndDeleteData,
+ EndFusedStencilData,
+ EndIfData,
+ EndProfileData,
+ EndStencilData,
+ FieldAssociationData,
+ ImportsData,
+ InsertData,
+ IntegrationCodeInterface,
+ StartCreateData,
+ StartDeleteData,
+ StartFusedStencilData,
+ StartProfileData,
+ StartStencilData,
+)
+from icon4pytools.liskov.parsing.transform import StencilTransformer
+
+
+@pytest.fixture
+def integration_code_interface():
+ start_fused_stencil_data = StartFusedStencilData(
+ name="fused_stencil1",
+ fields=[
+ FieldAssociationData("scalar1", "scalar1", inp=True, out=False, dims=None),
+ FieldAssociationData("inp1", "inp1(:,:,1)", inp=True, out=False, dims=2),
+ FieldAssociationData("out1", "out1(:,:,1)", inp=False, out=True, dims=2, abs_tol="0.5"),
+ FieldAssociationData(
+ "out2",
+ "p_nh%prog(nnew)%out2(:,:,1)",
+ inp=False,
+ out=True,
+ dims=3,
+ abs_tol="0.2",
+ ),
+ FieldAssociationData("out3", "p_nh%prog(nnew)%w(:,:,jb)", inp=False, out=True, dims=2),
+ FieldAssociationData("out4", "p_nh%prog(nnew)%w(:,:,1,2)", inp=False, out=True, dims=3),
+ FieldAssociationData(
+ "out5", "p_nh%prog(nnew)%w(:,:,:,ntnd)", inp=False, out=True, dims=3
+ ),
+ FieldAssociationData(
+ "out6", "p_nh%prog(nnew)%w(:,:,1,ntnd)", inp=False, out=True, dims=3
+ ),
+ ],
+ bounds=BoundsData("1", "10", "-1", "-10"),
+ startln=1,
+ acc_present=False,
+ )
+ end_fused_stencil_data = EndFusedStencilData(name="stencil1", startln=4)
+ start_stencil_data1 = StartStencilData(
+ name="stencil1",
+ fields=[
+ FieldAssociationData("scalar1", "scalar1", inp=True, out=False, dims=None),
+ FieldAssociationData("inp1", "inp1(:,:,1)", inp=True, out=False, dims=2),
+ FieldAssociationData("out1", "out1(:,:,1)", inp=False, out=True, dims=2, abs_tol="0.5"),
+ FieldAssociationData(
+ "out2",
+ "p_nh%prog(nnew)%out2(:,:,1)",
+ inp=False,
+ out=True,
+ dims=3,
+ abs_tol="0.2",
+ ),
+ FieldAssociationData("out3", "p_nh%prog(nnew)%w(:,:,jb)", inp=False, out=True, dims=2),
+ FieldAssociationData("out4", "p_nh%prog(nnew)%w(:,:,1,2)", inp=False, out=True, dims=3),
+ FieldAssociationData(
+ "out5", "p_nh%prog(nnew)%w(:,:,:,ntnd)", inp=False, out=True, dims=3
+ ),
+ FieldAssociationData(
+ "out6", "p_nh%prog(nnew)%w(:,:,1,ntnd)", inp=False, out=True, dims=3
+ ),
+ ],
+ bounds=BoundsData("1", "10", "-1", "-10"),
+ startln=2,
+ acc_present=False,
+ mergecopy=False,
+ copies=True,
+ )
+ end_stencil_data1 = EndStencilData(
+ name="stencil1", startln=3, noendif=False, noprofile=False, noaccenddata=False
+ )
+ start_stencil_data2 = StartStencilData(
+ name="stencil2",
+ fields=[
+ FieldAssociationData("scalar1", "scalar1", inp=True, out=False, dims=None),
+ FieldAssociationData("inp1", "inp1(:,:,1)", inp=True, out=False, dims=2),
+ FieldAssociationData("out1", "out1(:,:,1)", inp=False, out=True, dims=2, abs_tol="0.5"),
+ FieldAssociationData(
+ "out2",
+ "p_nh%prog(nnew)%out2(:,:,1)",
+ inp=False,
+ out=True,
+ dims=3,
+ abs_tol="0.2",
+ ),
+ FieldAssociationData("out3", "p_nh%prog(nnew)%w(:,:,jb)", inp=False, out=True, dims=2),
+ FieldAssociationData("out4", "p_nh%prog(nnew)%w(:,:,1,2)", inp=False, out=True, dims=3),
+ FieldAssociationData(
+ "out5", "p_nh%prog(nnew)%w(:,:,:,ntnd)", inp=False, out=True, dims=3
+ ),
+ FieldAssociationData(
+ "out6", "p_nh%prog(nnew)%w(:,:,1,ntnd)", inp=False, out=True, dims=3
+ ),
+ ],
+ bounds=BoundsData("1", "10", "-1", "-10"),
+ startln=5,
+ acc_present=False,
+ mergecopy=False,
+ copies=True,
+ )
+ end_stencil_data2 = EndStencilData(
+ name="stencil2", startln=6, noendif=False, noprofile=False, noaccenddata=False
+ )
+ declare_data = DeclareData(
+ startln=7,
+ declarations={"field2": "(nproma, p_patch%nlev, p_patch%nblks_e)"},
+ ident_type="REAL(wp)",
+ suffix="before",
+ )
+ imports_data = ImportsData(startln=8)
+ start_create_data = StartCreateData(extra_fields=["foo", "bar"], startln=9)
+ end_create_data = EndCreateData(startln=11)
+ endif_data = EndIfData(startln=12)
+ start_profile_data = StartProfileData(startln=13, name="test_stencil")
+ end_profile_data = EndProfileData(startln=14)
+ insert_data = InsertData(startln=15, content="print *, 'Hello, World!'")
+ start_delete_data = StartDeleteData(startln=16)
+ end_delete_data = EndDeleteData(startln=17)
+
+ return IntegrationCodeInterface(
+ StartStencil=[start_stencil_data1, start_stencil_data2],
+ EndStencil=[end_stencil_data1, end_stencil_data2],
+ StartFusedStencil=[start_fused_stencil_data],
+ EndFusedStencil=[end_fused_stencil_data],
+ StartDelete=[start_delete_data],
+ EndDelete=[end_delete_data],
+ Declare=[declare_data],
+ Imports=imports_data,
+ StartCreate=[start_create_data],
+ EndCreate=[end_create_data],
+ EndIf=[endif_data],
+ StartProfile=[start_profile_data],
+ EndProfile=[end_profile_data],
+ Insert=[insert_data],
+ )
+
+
+@pytest.fixture
+def stencil_transform_fused(integration_code_interface):
+ return StencilTransformer(integration_code_interface, fused=True)
+
+
+@pytest.fixture
+def stencil_transform_unfused(integration_code_interface):
+ return StencilTransformer(integration_code_interface, fused=False)
+
+
+def test_transform_fused(
+ stencil_transform_fused,
+):
+ # Check that the transformed interface is as expected
+ transformed = stencil_transform_fused()
+ assert len(transformed.StartFusedStencil) == 1
+ assert len(transformed.EndFusedStencil) == 1
+ assert len(transformed.StartStencil) == 1
+ assert len(transformed.EndStencil) == 1
+ assert len(transformed.StartDelete) == 2
+ assert len(transformed.EndDelete) == 2
+
+
+def test_transform_unfused(
+ stencil_transform_unfused,
+):
+ # Check that the transformed interface is as expected
+ transformed = stencil_transform_unfused()
+
+ assert not transformed.StartFusedStencil
+ assert not transformed.EndFusedStencil
+ assert len(transformed.StartStencil) == 2
+ assert len(transformed.EndStencil) == 2
+ assert not transformed.StartDelete
+ assert not transformed.EndDelete