Beispiel #1
0
def test_mesh_gen(tmpdir):
    ''' Basic test for code-generation for an invoke containing a single
    kernel requiring reference-element properties. '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "24.1_mesh_prop_invoke.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info)

    assert LFRicBuild(tmpdir).code_compiles(psy)
    gen = str(psy.gen).lower()
    # In order to provide the mesh property we need the reference element
    assert "use reference_element_mod, only: reference_element_type" in gen
    assert "integer(kind=i_def) nfaces_re_h" in gen
    assert ("integer(kind=i_def), pointer :: adjacent_face(:,:) => null()"
            in gen)
    assert ("class(reference_element_type), pointer :: reference_element "
            "=> null()" in gen)
    # We need a mesh object in order to get a reference_element object
    assert "mesh => f1_proxy%vspace%get_mesh()" in gen
    assert "reference_element => mesh%get_reference_element()" in gen
    assert ("nfaces_re_h = reference_element%get_number_horizontal_faces()"
            in gen)
    assert "adjacent_face => mesh%get_adjacent_face()" in gen
    assert "nfaces_re_v" not in gen
    # The kernel call
    assert ("call testkern_mesh_prop_code(nlayers, a, f1_proxy%data, "
            "ndf_w1, undf_w1, map_w1(:,cell), nfaces_re_h, "
            "adjacent_face(:,cell))" in gen)
Beispiel #2
0
def test_prolong_vector(tmpdir):
    ''' Check that we generate correct code when an inter-grid kernel
    takes a field vector as argument '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "22.4_intergrid_prolong_vec.f90"),
                           api=API)
    psy = PSyFactory(API, distributed_memory=True).create(invoke_info)
    output = str(psy.gen)

    assert LFRicBuild(tmpdir).code_compiles(psy)

    assert "TYPE(field_type), intent(in) :: field1(3)" in output
    assert "TYPE(field_proxy_type) field1_proxy(3)" in output
    # Make sure we always index into the field arrays
    assert " field1%" not in output
    assert " field2%" not in output
    assert ("ncpc_field1_field2, ncell_field1, field1_proxy(1)%data, "
            "field1_proxy(2)%data, field1_proxy(3)%data, field2_proxy(1)%data,"
            " field2_proxy(2)%data, field2_proxy(3)%data, ndf_w1" in output)
    for idx in [1, 2, 3]:
        assert ("      IF (field2_proxy({0})%is_dirty(depth=1)) THEN\n"
                "        CALL field2_proxy({0})%halo_exchange(depth=1)\n"
                "      END IF\n".format(idx) in output)
        assert ("field1_proxy({0}) = field1({0})%get_proxy()".format(idx)
                in output)
        assert "CALL field1_proxy({0})%set_dirty()".format(idx) in output
        assert "CALL field1_proxy({0})%set_clean(1)".format(idx) in output
Beispiel #3
0
def test_op_orient_different_space(tmpdir):
    ''' Tests that an operator on different spaces requiring orientation
    information is implemented correctly in the PSy layer. '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "10.4_operator_orient_different_"
                                        "space.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info)
    gen_str = str(psy.gen)

    assert LFRicBuild(tmpdir).code_compiles(psy)

    assert (
        "INTEGER(KIND=i_def), pointer :: orientation_w1(:) => null(), "
        "orientation_w2(:) => null()" in gen_str)
    assert "ndf_w2 = my_mapping_proxy%fs_from%get_ndf()" in gen_str
    assert "ndf_w1 = my_mapping_proxy%fs_to%get_ndf()" in gen_str
    assert "dim_w1 = my_mapping_proxy%fs_to%get_dim_space()" in gen_str
    assert ("CALL qr%compute_function(BASIS, my_mapping_proxy%fs_to, "
            "dim_w1, ndf_w1, basis_w1_qr)" in gen_str)
    assert (
        "orientation_w2 => my_mapping_proxy%fs_from%get_cell_orientation("
        "cell)" in gen_str)
    assert (
        "orientation_w1 => my_mapping_proxy%fs_to%get_cell_orientation(cell)"
        in gen_str)
    assert ("(cell, nlayers, my_mapping_proxy%ncell_3d, "
            "my_mapping_proxy%local_stencil, coord_proxy(1)%data, "
            "coord_proxy(2)%data, coord_proxy(3)%data, ndf_w1, basis_w1_qr, "
            "orientation_w1, ndf_w2, orientation_w2, ndf_w0, undf_w0, "
            "map_w0(:,cell), diff_basis_w0_qr, np_xy_qr, np_z_qr, "
            "weights_xy_qr, weights_z_qr)" in gen_str)
def test_psy_gen_domain_two_kernel(dist_mem, tmpdir):
    ''' Check the generation of the PSy layer for an invoke consisting of a
    kernel with operates_on=domain and another with operates_on=cell_column.
    '''
    _, info = parse(os.path.join(BASE_PATH, "25.1_2kern_domain.f90"),
                    api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=dist_mem).create(info)
    gen_code = str(psy.gen).lower()

    assert "mesh => f2_proxy%vspace%get_mesh()" in gen_code
    assert "integer(kind=i_def) ncell_2d" in gen_code

    expected = ("      end do\n" "      !\n")
    if dist_mem:
        expected += (
            "      ! set halos dirty/clean for fields modified in the above "
            "loop\n"
            "      !\n"
            "      call f2_proxy%set_dirty()\n"
            "      !\n"
            "      !\n")
    expected += (
        "      call testkern_domain_code(nlayers, ncell_2d, b, f1_proxy%data, "
        "ndf_w3, undf_w3, map_w3)\n")
    assert expected in gen_code
    if dist_mem:
        assert ("      ! set halos dirty/clean for fields modified in the "
                "above kernel\n"
                "      !\n"
                "      call f1_proxy%set_dirty()\n" in gen_code)

    assert LFRicBuild(tmpdir).code_compiles(psy)
Beispiel #5
0
def test_operator_nofield_scalar_deref(tmpdir, dist_mem):
    ''' Tests that an operator with no field and a
    scalar argument is implemented correctly in the PSy layer when both
    are obtained by dereferencing derived type objects. '''
    _, invoke_info = parse(os.path.join(
        BASE_PATH, "10.6.1_operator_no_field_scalar_deref.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=dist_mem).create(invoke_info)
    gen = str(psy.gen)

    assert LFRicBuild(tmpdir).code_compiles(psy)

    if dist_mem:
        assert "mesh => opbox_my_mapping_proxy%fs_from%get_mesh()" in gen
    assert "nlayers = opbox_my_mapping_proxy%fs_from%get_nlayers()" in gen
    assert "ndf_w2 = opbox_my_mapping_proxy%fs_from%get_ndf()" in gen
    assert ("qr_init_quadrature_symmetrical%compute_function(BASIS, "
            "opbox_my_mapping_proxy%fs_from, dim_w2, ndf_w2, "
            "basis_w2_qr_init_quadrature_symmetrical)" in gen)
    if dist_mem:
        assert "DO cell=1,mesh%get_last_halo_cell(1)" in gen
    else:
        assert ("DO cell=1,opbox_my_mapping_proxy%fs_from%get_ncell()" in gen)
    assert ("(cell, nlayers, opbox_my_mapping_proxy%ncell_3d, "
            "opbox_my_mapping_proxy%local_stencil, box_b, ndf_w2, "
            "basis_w2_qr_init_quadrature_symmetrical, "
            "np_xy_qr_init_quadrature_symmetrical, "
            "np_z_qr_init_quadrature_symmetrical, "
            "weights_xy_qr_init_quadrature_symmetrical, "
            "weights_z_qr_init_quadrature_symmetrical)" in gen)
Beispiel #6
0
def test_operator_nofield(tmpdir):
    ''' Tests that an operator with no field on the same space is
    implemented correctly in the PSy layer. '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "10.1_operator_nofield.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info)
    gen_code_str = str(psy.gen)

    assert LFRicBuild(tmpdir).code_compiles(psy)

    assert (
        "SUBROUTINE invoke_0_testkern_operator_nofield_type(mm_w2, coord, qr)"
        in gen_code_str)
    assert "TYPE(operator_type), intent(in) :: mm_w2" in gen_code_str
    assert "TYPE(operator_proxy_type) mm_w2_proxy" in gen_code_str
    assert "mm_w2_proxy = mm_w2%get_proxy()" in gen_code_str
    assert "undf_w2" not in gen_code_str
    assert "map_w2" not in gen_code_str
    assert ("CALL testkern_operator_nofield_code(cell, nlayers, "
            "mm_w2_proxy%ncell_3d, mm_w2_proxy%local_stencil, "
            "coord_proxy(1)%data, coord_proxy(2)%data, coord_proxy(3)%data, "
            "ndf_w2, basis_w2_qr, ndf_w0, undf_w0, "
            "map_w0(:,cell), diff_basis_w0_qr, np_xy_qr, np_z_qr, "
            "weights_xy_qr, weights_z_qr)" in gen_code_str)
Beispiel #7
0
def test_union_refelem_gen(tmpdir):
    ''' Check that code generation works for an invoke with kernels that
    only have a sub-set of reference-element properties in common. '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "23.3_shared_ref_elem_invoke.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info)

    assert LFRicBuild(tmpdir).code_compiles(psy)
    gen = str(psy.gen).lower()

    assert (
        "      reference_element => mesh%get_reference_element()\n"
        "      nfaces_re_h = reference_element%get_number_horizontal_faces()\n"
        "      nfaces_re_v = reference_element%get_number_vertical_faces()\n"
        "      call reference_element%get_normals_to_horizontal_faces("
        "normals_to_horiz_faces)\n"
        "      call reference_element%get_outward_normals_to_horizontal_faces("
        "out_normals_to_horiz_faces)\n"
        "      call reference_element%get_normals_to_vertical_faces("
        "normals_to_vert_faces)\n"
        "      call reference_element%get_outward_normals_to_vertical_faces("
        "out_normals_to_vert_faces)\n" in gen)
    assert ("call testkern_ref_elem_code(nlayers, a, f1_proxy%data, "
            "f2_proxy%data, m1_proxy%data, m2_proxy%data, ndf_w1, undf_w1, "
            "map_w1(:,cell), ndf_w2, undf_w2, map_w2(:,cell), ndf_w3, undf_w3,"
            " map_w3(:,cell), nfaces_re_h, nfaces_re_v, "
            "normals_to_horiz_faces, normals_to_vert_faces)" in gen)
    assert ("call testkern_ref_elem_out_code(nlayers, a, f3_proxy%data, "
            "f4_proxy%data, m3_proxy%data, m4_proxy%data, ndf_w1, undf_w1, "
            "map_w1(:,cell), ndf_w2, undf_w2, map_w2(:,cell), ndf_w3, undf_w3,"
            " map_w3(:,cell), nfaces_re_v, nfaces_re_h, "
            "out_normals_to_vert_faces, normals_to_vert_faces, "
            "out_normals_to_horiz_faces)" in gen)
Beispiel #8
0
def test_edge_qr(tmpdir, dist_mem):
    ''' Check that we generate correct code when a kernel specifies
    that it requires edge quadrature. '''
    _, invoke_info = parse(os.path.join(BASE_PATH, "1.1.5_edge_qr.f90"),
                           api=API)
    psy = PSyFactory(API, distributed_memory=dist_mem).create(invoke_info)
    assert LFRicBuild(tmpdir).code_compiles(psy)
    gen_code = str(psy.gen).lower()

    assert ("use quadrature_edge_mod, only: quadrature_edge_type, "
            "quadrature_edge_proxy_type\n" in gen_code)
    assert "type(quadrature_edge_type), intent(in) :: qr\n" in gen_code
    assert "integer(kind=i_def) np_xyz_qr, nedges_qr" in gen_code
    assert ("      qr_proxy = qr%get_quadrature_proxy()\n"
            "      np_xyz_qr = qr_proxy%np_xyz\n"
            "      nedges_qr = qr_proxy%nedges\n"
            "      weights_xyz_qr => qr_proxy%weights_xyz\n" in gen_code)

    assert ("      ! compute basis/diff-basis arrays\n"
            "      !\n"
            "      call qr%compute_function(basis, f1_proxy%vspace, dim_w1, "
            "ndf_w1, basis_w1_qr)\n"
            "      call qr%compute_function(diff_basis, f2_proxy%vspace, "
            "diff_dim_w2, ndf_w2, diff_basis_w2_qr)\n"
            "      call qr%compute_function(basis, m2_proxy%vspace, dim_w3, "
            "ndf_w3, basis_w3_qr)\n"
            "      call qr%compute_function(diff_basis, m2_proxy%vspace, "
            "diff_dim_w3, ndf_w3, diff_basis_w3_qr)\n" in gen_code)

    assert ("call testkern_qr_edges_code(nlayers, f1_proxy%data, "
            "f2_proxy%data, m1_proxy%data, a, m2_proxy%data, istp, "
            "ndf_w1, undf_w1, map_w1(:,cell), basis_w1_qr, ndf_w2, undf_w2, "
            "map_w2(:,cell), diff_basis_w2_qr, ndf_w3, undf_w3, "
            "map_w3(:,cell), basis_w3_qr, diff_basis_w3_qr, nedges_qr, "
            "np_xyz_qr, weights_xyz_qr)" in gen_code)
Beispiel #9
0
def test_duplicate_refelem_gen(tmpdir):
    ''' Test for code-generation for an invoke containing two kernels that
    require the same properties of the reference-element. '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "23.2_multi_ref_elem_invoke.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info)

    assert LFRicBuild(tmpdir).code_compiles(psy)
    gen = str(psy.gen).lower()
    assert gen.count(
        "real(kind=r_def), allocatable :: normals_to_horiz_faces(:,:)"
        ", normals_to_vert_faces(:,:)") == 1
    assert gen.count(
        "reference_element => mesh%get_reference_element") == 1
    assert gen.count(
        "nfaces_re_h = reference_element%get_number_horizontal_faces()") == 1
    assert gen.count(
        "nfaces_re_v = reference_element%get_number_vertical_faces()") == 1
    assert gen.count("call reference_element%get_normals_to_horizontal_faces("
                     "normals_to_horiz_faces)") == 1
    assert gen.count("call reference_element%get_normals_to_vertical_faces("
                     "normals_to_vert_faces)") == 1
    assert ("call testkern_ref_elem_code(nlayers, a, f1_proxy%data, "
            "f2_proxy%data, m1_proxy%data, m2_proxy%data, ndf_w1, undf_w1, "
            "map_w1(:,cell), ndf_w2, undf_w2, map_w2(:,cell), ndf_w3, "
            "undf_w3, map_w3(:,cell), nfaces_re_h, nfaces_re_v, "
            "normals_to_horiz_faces, normals_to_vert_faces)" in gen)
    assert ("call testkern_ref_elem_code(nlayers, a, f3_proxy%data, "
            "f4_proxy%data, m3_proxy%data, m4_proxy%data, ndf_w1, undf_w1, "
            "map_w1(:,cell), ndf_w2, undf_w2, map_w2(:,cell), ndf_w3, "
            "undf_w3, map_w3(:,cell), nfaces_re_h, nfaces_re_v, "
            "normals_to_horiz_faces, normals_to_vert_faces)" in gen)
Beispiel #10
0
def test_refelem_gen(tmpdir):
    ''' Basic test for code-generation for an invoke containing a single
    kernel requiring reference-element properties. '''
    _, invoke_info = parse(os.path.join(BASE_PATH, "23.1_ref_elem_invoke.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info)

    assert LFRicBuild(tmpdir).code_compiles(psy)
    gen = str(psy.gen).lower()
    assert "use reference_element_mod, only: reference_element_type" in gen
    assert "integer(kind=i_def) nfaces_re_h, nfaces_re_v" in gen
    assert ("real(kind=r_def), allocatable :: normals_to_horiz_faces(:,:), "
            "normals_to_vert_faces(:,:)" in gen)
    assert ("class(reference_element_type), pointer :: reference_element "
            "=> null()" in gen)
    # We need a mesh object in order to get a reference_element object
    assert "mesh => f1_proxy%vspace%get_mesh()" in gen
    assert "reference_element => mesh%get_reference_element()" in gen
    assert ("nfaces_re_h = reference_element%get_number_horizontal_faces()"
            in gen)
    assert "nfaces_re_v = reference_element%get_number_vertical_faces()" in gen
    assert ("call reference_element%get_normals_to_horizontal_faces("
            "normals_to_horiz_faces)" in gen)
    assert ("call reference_element%get_normals_to_vertical_faces("
            "normals_to_vert_faces)" in gen)
    # The kernel call
    assert ("call testkern_ref_elem_code(nlayers, a, f1_proxy%data, "
            "f2_proxy%data, m1_proxy%data, m2_proxy%data, ndf_w1, undf_w1, "
            "map_w1(:,cell), ndf_w2, undf_w2, map_w2(:,cell), ndf_w3, "
            "undf_w3, map_w3(:,cell), nfaces_re_h, nfaces_re_v, "
            "normals_to_horiz_faces, normals_to_vert_faces)" in gen)
Beispiel #11
0
def test_halo_for_discontinuous_2(tmpdir, monkeypatch, annexed):
    '''This test checks the case when our loop iterates over owned cells
    (e.g. it writes to a discontinuous field), we read from a
    continuous field, there are no stencil accesses, and the previous
    writer iterates over ndofs or nannexed.

    When the previous writer iterates over ndofs we have dirty annexed
    dofs so need to add a halo exchange. This is the case when
    api_config.compute_annexed_dofs is False.

    When the previous writer iterates over nannexed we have clean
    annexed dofs so do not need to add a halo exchange. This is the
    case when api_config.compute_annexed_dofs is True

    '''
    api_config = Config.get().api_conf(TEST_API)
    monkeypatch.setattr(api_config, "_compute_annexed_dofs", annexed)
    _, info = parse(os.path.join(BASE_PATH, "14.7_halo_annexed.f90"),
                    api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=True).create(info)
    result = str(psy.gen)
    if annexed:
        assert "halo_exchange" not in result
    else:
        assert "IF (f1_proxy%is_dirty(depth=1)) THEN" not in result
        assert "CALL f1_proxy%halo_exchange(depth=1)" in result
        assert "IF (f2_proxy%is_dirty(depth=1)) THEN" not in result
        assert "CALL f2_proxy%halo_exchange(depth=1)" in result
        assert "IF (m1_proxy%is_dirty(depth=1)) THEN" in result
        assert "CALL m1_proxy%halo_exchange(depth=1)" in result

    assert LFRicBuild(tmpdir).code_compiles(psy)
Beispiel #12
0
def test_setval_x_then_user(tmpdir, monkeypatch):
    ''' Check that the correct halo exchanges are added if redundant
    computation is enabled for a built-in kernel called before a
    user-supplied kernel. '''
    api_config = Config.get().api_conf(API)
    monkeypatch.setattr(api_config, "_compute_annexed_dofs", True)
    _, invoke_info = parse(os.path.join(
        BASE_PATH, "15.7.3_setval_X_before_user_kern.f90"),
                           api=API)
    psy = PSyFactory(API, distributed_memory=True).create(invoke_info)

    first_invoke = psy.invokes.invoke_list[0]
    # Since (redundant) computation over annexed dofs is enabled, there
    # should be no halo exchange before the first (builtin) kernel call
    assert isinstance(first_invoke.schedule[0], DynLoop)
    # There should be a halo exchange for field f1 before the second
    # kernel call
    assert isinstance(first_invoke.schedule[1], DynHaloExchange)
    assert first_invoke.schedule[1].field.name == "f1"
    # Now transform the first loop to perform redundant computation out to
    # the level-1 halo
    rtrans = Dynamo0p3RedundantComputationTrans()
    _, _ = rtrans.apply(first_invoke.schedule[0], options={"depth": 1})
    # There should now be a halo exchange for f1 before the first
    # (builtin) kernel call
    assert isinstance(first_invoke.schedule[0], DynHaloExchange)
    assert first_invoke.schedule[0].field.name == "f1"
    assert isinstance(first_invoke.schedule[1], DynLoop)
    # There should only be one halo exchange for field f1
    assert len([
        node for node in first_invoke.schedule.walk(DynHaloExchange)
        if node.field.name == "f1"
    ]) == 1
    assert LFRicBuild(tmpdir).code_compiles(psy)
def test_psy_gen_domain_kernel(dist_mem, tmpdir):
    ''' Check the generation of the PSy layer for an invoke consisting of a
    single kernel with operates_on=domain. '''
    _, info = parse(os.path.join(BASE_PATH, "25.0_domain.f90"), api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=dist_mem).create(info)
    gen_code = str(psy.gen).lower()

    # A domain kernel needs the number of columns in the mesh. Therefore
    # we require a mesh object.
    assert "type(mesh_type), pointer :: mesh => null()" in gen_code
    assert "mesh => f1_proxy%vspace%get_mesh()" in gen_code
    assert "integer(kind=i_def) ncell_2d" in gen_code
    assert "ncell_2d = mesh%get_ncells_2d()" in gen_code

    # Kernel call should include whole dofmap and not be within a loop
    if dist_mem:
        expected = "      ! call kernels and communication routines\n"
    else:
        expected = "      ! call our kernels\n"
    assert (expected + "      !\n"
            "      !\n"
            "      call testkern_domain_code(nlayers, ncell_2d, b, "
            "f1_proxy%data, ndf_w3, undf_w3, map_w3)" in gen_code)

    assert LFRicBuild(tmpdir).code_compiles(psy)
Beispiel #14
0
def test_operator_orientation(tmpdir):
    ''' Tests that an operator requiring orientation information is
    implemented correctly in the PSy layer. '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "10.2_operator_orient.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info)
    gen_str = str(psy.gen)

    assert LFRicBuild(tmpdir).code_compiles(psy)

    assert (
        "SUBROUTINE invoke_0_testkern_operator_orient_type(mm_w1, coord, qr)"
        in gen_str)
    assert "TYPE(operator_type), intent(in) :: mm_w1" in gen_str
    assert "TYPE(operator_proxy_type) mm_w1_proxy" in gen_str
    assert "mm_w1_proxy = mm_w1%get_proxy()" in gen_str
    assert (
        "orientation_w1 => mm_w1_proxy%fs_from%get_cell_orientation"
        "(cell)" in gen_str)
    assert ("CALL testkern_operator_orient_code(cell, nlayers, "
            "mm_w1_proxy%ncell_3d, mm_w1_proxy%local_stencil, "
            "coord_proxy(1)%data, coord_proxy(2)%data, coord_proxy(3)%data, "
            "ndf_w1, basis_w1_qr, orientation_w1, ndf_w0, undf_w0, "
            "map_w0(:,cell), diff_basis_w0_qr, np_xy_qr, np_z_qr, "
            "weights_xy_qr, weights_z_qr)" in gen_str)
Beispiel #15
0
def test_mesh_plus_face_quad_gen(tmpdir):
    ''' Test that we generate correct code when a kernel requires both a
    mesh property and face quadrature. '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "24.4_mesh_plus_face_qr_invoke.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info)

    assert LFRicBuild(tmpdir).code_compiles(psy)
    gen = str(psy.gen).lower()

    assert ("      qr_proxy = qr%get_quadrature_proxy()\n"
            "      np_xyz_qr = qr_proxy%np_xyz\n"
            "      nfaces_qr = qr_proxy%nfaces\n"
            "      weights_xyz_qr => qr_proxy%weights_xyz\n"
            "      !\n"
            "      ! allocate basis/diff-basis arrays\n"
            "      !\n"
            "      dim_w1 = f1_proxy%vspace%get_dim_space()\n"
            "      allocate (basis_w1_qr(dim_w1, ndf_w1, np_xyz_qr, "
            "nfaces_qr))" in gen)

    assert ("      reference_element => mesh%get_reference_element()\n"
            "      nfaces_re_h = reference_element%"
            "get_number_horizontal_faces()\n"
            "      !\n"
            "      ! initialise mesh properties\n"
            "      !\n"
            "      adjacent_face => mesh%get_adjacent_face()" in gen)

    assert ("call testkern_mesh_prop_face_qr_code(nlayers, a, f1_proxy%data, "
            "ndf_w1, undf_w1, map_w1(:,cell), basis_w1_qr, "
            "nfaces_re_h, adjacent_face(:,cell), "
            "nfaces_qr, np_xyz_qr, weights_xyz_qr)" in gen)
Beispiel #16
0
def test_2kern_trans(kernel_outputdir):
    ''' Check that we generate correct code when we transform two kernels
    within a single invoke. '''
    psy, invoke = get_invoke("4.5.2_multikernel_invokes.f90",
                             api="dynamo0.3",
                             idx=0)
    sched = invoke.schedule
    kernels = sched.walk(Kern)
    assert len(kernels) == 5
    ktrans = Dynamo0p3KernelConstTrans()
    _, _ = ktrans.apply(kernels[1], {"number_of_layers": 100})
    _, _ = ktrans.apply(kernels[2], {"number_of_layers": 100})
    # Generate the code (this triggers the generation of new kernels)
    code = str(psy.gen).lower()
    # Find the tags added to the kernel/module names
    for match in re.finditer('use testkern_any_space_2(.+?)_mod', code):
        tag = match.group(1)
        assert ("use testkern_any_space_2{0}_mod, only: "
                "testkern_any_space_2{0}_code".format(tag) in code)
        assert "call testkern_any_space_2{0}_code(".format(tag) in code
        filepath = os.path.join(str(kernel_outputdir),
                                "testkern_any_space_2{0}_mod.f90".format(tag))
        assert os.path.isfile(filepath)
        assert "nlayers = 100" in open(filepath).read()
    assert "use testkern_any_space_2_mod, only" not in code
    assert "call testkern_any_space_2_code(" not in code
    assert LFRicBuild(kernel_outputdir).code_compiles(psy)
Beispiel #17
0
def test_1kern_trans(kernel_outputdir):
    ''' Check that we generate the correct code when an invoke contains
    the same kernel more than once but only one of them is transformed. '''
    psy, invoke = get_invoke("4_multikernel_invokes.f90",
                             api="dynamo0.3",
                             idx=0)
    sched = invoke.schedule
    kernels = sched.coded_kernels()
    # We will transform the second kernel but not the first
    kern = kernels[1]
    rtrans = ACCRoutineTrans()
    _, _ = rtrans.apply(kern)
    # Generate the code (this triggers the generation of a new kernel)
    code = str(psy.gen).lower()
    tag = re.search('use testkern(.+?)_mod', code).group(1)
    # We should have a USE for the original kernel and a USE for the new one
    assert "use testkern{0}_mod, only: testkern{0}_code".format(tag) in code
    assert "use testkern_mod, only: testkern_code" in code
    # Similarly, we should have calls to both the original and new kernels
    assert "call testkern_code(" in code
    assert "call testkern{0}_code(".format(tag) in code
    first = code.find("call testkern_code(")
    second = code.find("call testkern{0}_code(".format(tag))
    assert first < second
    assert LFRicBuild(kernel_outputdir).code_compiles(psy)
Beispiel #18
0
def test_halo_for_discontinuous(tmpdir, monkeypatch, annexed):
    '''This test checks the case when our loop iterates over owned cells
    (e.g. it writes to a discontinuous field), we read from a
    continuous field, there are no stencil accesses, but we do not
    know anything about the previous writer.

    As we don't know anything about the previous writer we have to
    assume that it may have been over dofs. If so, we could have dirty
    annexed dofs so need to add a halo exchange (for the three
    continuous fields being read (f1, f2 and m1). This is the case
    when api_config.compute_annexed_dofs is False.

    If we always iterate over annexed dofs by default, our annexed
    dofs will always be clean. Therefore we do not need to add a halo
    exchange. This is the case when
    api_config.compute_annexed_dofs is True.

    '''
    api_config = Config.get().api_conf(TEST_API)
    monkeypatch.setattr(api_config, "_compute_annexed_dofs", annexed)
    _, info = parse(os.path.join(BASE_PATH, "1_single_invoke_w3.f90"),
                    api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=True).create(info)
    result = str(psy.gen)
    if annexed:
        assert "halo_exchange" not in result
    else:
        assert "IF (f1_proxy%is_dirty(depth=1)) THEN" in result
        assert "CALL f1_proxy%halo_exchange(depth=1)" in result
        assert "IF (f2_proxy%is_dirty(depth=1)) THEN" in result
        assert "CALL f2_proxy%halo_exchange(depth=1)" in result
        assert "IF (m1_proxy%is_dirty(depth=1)) THEN" in result
        assert "CALL m1_proxy%halo_exchange(depth=1)" in result

    assert LFRicBuild(tmpdir).code_compiles(psy)
Beispiel #19
0
def test_operator_deref(tmpdir, dist_mem):
    ''' Tests that we generate correct names for an operator in the PSy
    layer when obtained by de-referencing a derived type in the Algorithm
    layer. '''
    _, invoke_info = parse(os.path.join(BASE_PATH, "10.8_operator_deref.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API,
                     distributed_memory=dist_mem).create(invoke_info)
    generated_code = str(psy.gen)

    assert LFRicBuild(tmpdir).code_compiles(psy)

    assert (
        "SUBROUTINE invoke_0_testkern_operator_type(mm_w0_op, coord, a, qr)"
        in generated_code)
    assert "TYPE(operator_type), intent(in) :: mm_w0_op" in generated_code
    assert "TYPE(operator_proxy_type) mm_w0_op_proxy" in generated_code
    assert "mm_w0_op_proxy = mm_w0_op%get_proxy()" in generated_code
    assert (
        "CALL testkern_operator_code(cell, nlayers, "
        "mm_w0_op_proxy%ncell_3d, mm_w0_op_proxy%local_stencil, "
        "coord_proxy(1)%data, coord_proxy(2)%data, coord_proxy(3)%data, a, "
        "ndf_w0, undf_w0, map_w0(:,cell), basis_w0_qr, "
        "diff_basis_w0_qr, np_xy_qr, np_z_qr, weights_xy_qr, "
        "weights_z_qr)" in generated_code)
Beispiel #20
0
def test_gh_inc_nohex_1(tmpdir, monkeypatch):
    '''If COMPUTE_ANNEXED_DOFS is True, then a gh_inc access to a field in
    a kernel (iterating to the l1 halo) does not require a halo
    exchange when the previous writer is known and iterates over dofs
    to nannexed, halo(1), or halo max depth

    '''
    # ensure that COMPUTE_ANNEXED_DOFS is True
    config = Config.get()
    dyn_config = config.api_conf(API)
    monkeypatch.setattr(dyn_config, "_compute_annexed_dofs", True)

    # parse and get psy schedule
    _, info = parse(os.path.join(BASE_PATH, "14.12_halo_wdofs_to_inc.f90"),
                    api=API)
    psy = PSyFactory(API, distributed_memory=True).create(info)
    schedule = psy.invokes.invoke_list[0].schedule

    def check_schedule(schedule):
        '''Check this schedule has expected structure (loop, haloexchange,
        loop). In paricular there should be no halo exchange for the
        write-to-gh_inc dependence.

        :param schedule: a dynamo0.3 API schedule object
        :type schedule: :py:class:`psyclone.dynamo0p3.DynInvokeSchedule`.

        '''
        assert len(schedule.children) == 3
        loop1 = schedule.children[0]
        haloex = schedule.children[1]
        loop2 = schedule.children[2]
        assert isinstance(loop1, DynLoop)
        assert isinstance(haloex, DynHaloExchange)
        assert haloex.field.name == "f2"
        assert haloex.required() == (True, False)
        assert isinstance(loop2, DynLoop)

    # 1st loop should iterate over dofs to nannexed. Check output
    assert schedule.children[0].upper_bound_name == "nannexed"
    check_schedule(schedule)

    # just check compilation here (not later in this test) as
    # compilation of redundant computation is checked separately
    assert LFRicBuild(tmpdir).code_compiles(psy)

    # make 1st loop iterate over dofs to the level 1 halo and check output
    rc_trans = Dynamo0p3RedundantComputationTrans()
    rc_trans.apply(schedule.children[0], {"depth": 1})
    assert schedule.children[0].upper_bound_name == "dof_halo"
    assert schedule.children[0].upper_bound_halo_depth == 1
    check_schedule(schedule)

    # make 1st loop iterate over dofs to the maximum halo depth and
    # check output
    rc_trans.apply(schedule.children[0])
    assert schedule.children[0].upper_bound_name == "dof_halo"
    assert not schedule.children[0].upper_bound_halo_depth
    check_schedule(schedule)
def test_int_inc_X_times_Y(tmpdir, monkeypatch, annexed, dist_mem):
    ''' Test that 1) the str method of LFRicIntIncXTimesYKern returns
    the expected string and 2) we generate correct code for the built-in
    operation X = X*Y where X and Y are integer-valued fields. Test with and
    without annexed dofs being computed as this affects the generated code.

    '''
    api_config = Config.get().api_conf(API)
    monkeypatch.setattr(api_config, "_compute_annexed_dofs", annexed)
    _, invoke_info = parse(
        os.path.join(BASE_PATH,
                     "15.23.2_int_inc_X_times_Y_builtin.f90"),
        api=API)
    psy = PSyFactory(API, distributed_memory=dist_mem).create(invoke_info)
    # Test string method
    first_invoke = psy.invokes.invoke_list[0]
    kern = first_invoke.schedule.children[0].loop_body[0]
    assert str(kern) == ("Built-in: Multiply one integer-valued field "
                         "by another")
    # Test code generation
    code = str(psy.gen)

    assert LFRicBuild(tmpdir).code_compiles(psy)

    if not dist_mem:
        output = (
            "      f1_proxy = f1%get_proxy()\n"
            "      f2_proxy = f2%get_proxy()\n"
            "      !\n"
            "      ! Initialise number of DoFs for aspc1_f1\n"
            "      !\n"
            "      undf_aspc1_f1 = f1_proxy%vspace%get_undf()\n"
            "      !\n"
            "      ! Call our kernels\n"
            "      !\n"
            "      DO df=1,undf_aspc1_f1\n"
            "        f1_proxy%data(df) = f1_proxy%data(df) * "
            "f2_proxy%data(df)\n"
            "      END DO")
        assert output in code
    else:
        output_dm_2 = (
            "      !\n"
            "      ! Call kernels and communication routines\n"
            "      !\n"
            "      DO df=1,f1_proxy%vspace%get_last_dof_annexed()\n"
            "        f1_proxy%data(df) = f1_proxy%data(df) * "
            "f2_proxy%data(df)\n"
            "      END DO\n"
            "      !\n"
            "      ! Set halos dirty/clean for fields modified in the "
            "above loop\n"
            "      !\n"
            "      CALL f1_proxy%set_dirty()\n"
            "      !\n")
        if not annexed:
            output_dm_2 = output_dm_2.replace("dof_annexed", "dof_owned")
        assert output_dm_2 in code
Beispiel #22
0
def test_face_and_edge_qr(dist_mem, tmpdir):
    ''' Check that we can handle a kernel that requires two types of
    quadrature. '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "1.1.7_face_and_edge_qr.f90"),
                           api=API)
    psy = PSyFactory(API, distributed_memory=dist_mem).create(invoke_info)
    assert LFRicBuild(tmpdir).code_compiles(psy)
    gen_code = str(psy.gen)
    # Check that the qr-related variables are all declared
    assert ("      TYPE(quadrature_face_type), intent(in) :: qr_face\n"
            "      TYPE(quadrature_edge_type), intent(in) :: qr_edge\n"
            in gen_code)
    assert ("REAL(KIND=r_def), allocatable :: basis_w1_qr_face(:,:,:,:), "
            "basis_w1_qr_edge(:,:,:,:), diff_basis_w2_qr_face(:,:,:,:), "
            "diff_basis_w2_qr_edge(:,:,:,:), basis_w3_qr_face(:,:,:,:), "
            "diff_basis_w3_qr_face(:,:,:,:), basis_w3_qr_edge(:,:,:,:), "
            "diff_basis_w3_qr_edge(:,:,:,:)" in gen_code)
    assert ("      REAL(KIND=r_def), pointer :: weights_xyz_qr_edge(:,:) "
            "=> null()\n"
            "      INTEGER(KIND=i_def) np_xyz_qr_edge, nedges_qr_edge\n"
            "      REAL(KIND=r_def), pointer :: weights_xyz_qr_face(:,:) "
            "=> null()\n"
            "      INTEGER(KIND=i_def) np_xyz_qr_face, nfaces_qr_face\n"
            in gen_code)
    assert ("      TYPE(quadrature_edge_proxy_type) qr_edge_proxy\n"
            "      TYPE(quadrature_face_proxy_type) qr_face_proxy\n"
            in gen_code)
    # Allocation and computation of (some of) the basis functions
    assert ("      ALLOCATE (basis_w3_qr_face(dim_w3, ndf_w3, np_xyz_qr_face,"
            " nfaces_qr_face))\n"
            "      ALLOCATE (diff_basis_w3_qr_face(diff_dim_w3, ndf_w3, "
            "np_xyz_qr_face, nfaces_qr_face))\n"
            "      ALLOCATE (basis_w3_qr_edge(dim_w3, ndf_w3, np_xyz_qr_edge, "
            "nedges_qr_edge))\n"
            "      ALLOCATE (diff_basis_w3_qr_edge(diff_dim_w3, ndf_w3, "
            "np_xyz_qr_edge, nedges_qr_edge))\n" in gen_code)
    assert ("      CALL qr_face%compute_function(BASIS, m2_proxy%vspace, "
            "dim_w3, ndf_w3, basis_w3_qr_face)\n"
            "      CALL qr_face%compute_function(DIFF_BASIS, m2_proxy%vspace, "
            "diff_dim_w3, ndf_w3, diff_basis_w3_qr_face)\n"
            "      CALL qr_edge%compute_function(BASIS, m2_proxy%vspace, "
            "dim_w3, ndf_w3, basis_w3_qr_edge)\n"
            "      CALL qr_edge%compute_function(DIFF_BASIS, m2_proxy%vspace, "
            "diff_dim_w3, ndf_w3, diff_basis_w3_qr_edge)\n" in gen_code)
    # Check that the kernel call itself is correct
    assert (
        "CALL testkern_2qr_code(nlayers, f1_proxy%data, f2_proxy%data, "
        "m1_proxy%data, m2_proxy%data, "
        "ndf_w1, undf_w1, map_w1(:,cell), basis_w1_qr_face, basis_w1_qr_edge, "
        "ndf_w2, undf_w2, map_w2(:,cell), diff_basis_w2_qr_face, "
        "diff_basis_w2_qr_edge, "
        "ndf_w3, undf_w3, map_w3(:,cell), basis_w3_qr_face, basis_w3_qr_edge, "
        "diff_basis_w3_qr_face, diff_basis_w3_qr_edge, "
        "nfaces_qr_face, np_xyz_qr_face, weights_xyz_qr_face, "
        "nedges_qr_edge, np_xyz_qr_edge, weights_xyz_qr_edge)" in gen_code)
Beispiel #23
0
def test_no_halo_for_discontinuous(tmpdir):
    ''' Test that we do not create halo exchange calls when our loop
    only iterates over owned cells (e.g. it writes to a discontinuous
    field), we only read from a discontinuous field and there are no
    stencil accesses '''
    _, info = parse(os.path.join(BASE_PATH, "1_single_invoke_w2v.f90"),
                    api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=True).create(info)
    result = str(psy.gen)
    assert "halo_exchange" not in result

    assert LFRicBuild(tmpdir).code_compiles(psy)
Beispiel #24
0
def test_int_a_plus_X(tmpdir, monkeypatch, annexed, dist_mem):
    ''' Test that 1) the str method of LFRicIntAPlusXKern returns the
    expected string and 2) we generate correct code for the built-in
    operation Y = a + X where 'a' is an integer scalar and X and Y
    are integer-valued fields. Test with and without annexed dofs being
    computed as this affects the generated code.

    '''
    api_config = Config.get().api_conf(API)
    monkeypatch.setattr(api_config, "_compute_annexed_dofs", annexed)
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "15.21.3_int_a_plus_X_builtin.f90"),
                           api=API)

    psy = PSyFactory(API, distributed_memory=dist_mem).create(invoke_info)
    # Test string method
    first_invoke = psy.invokes.invoke_list[0]
    kern = first_invoke.schedule.children[0].loop_body[0]
    assert str(kern) == "Built-in: int_a_plus_X (integer-valued fields)"
    # Test code generation
    code = str(psy.gen)

    assert LFRicBuild(tmpdir).code_compiles(psy)

    if not dist_mem:
        output = ("      ! Call our kernels\n"
                  "      !\n"
                  "      DO df=1,undf_aspc1_f2\n"
                  "        f2_proxy%data(df) = a + f1_proxy%data(df)\n"
                  "      END DO\n"
                  "      !\n"
                  "    END SUBROUTINE invoke_0\n")
        assert output in code
    else:
        output_dm_2 = (
            "      !\n"
            "      ! Call kernels and communication routines\n"
            "      !\n"
            "      DO df=1,f2_proxy%vspace%get_last_dof_annexed()\n"
            "        f2_proxy%data(df) = a + f1_proxy%data(df)\n"
            "      END DO\n"
            "      !\n"
            "      ! Set halos dirty/clean for fields modified in the "
            "above loop\n"
            "      !\n"
            "      CALL f2_proxy%set_dirty()\n"
            "      !\n")
        if not annexed:
            output_dm_2 = output_dm_2.replace("dof_annexed", "dof_owned")
        assert output_dm_2 in code
Beispiel #25
0
def test_operator_nofield_scalar(tmpdir):
    ''' Tests that an operator with no field and a
    scalar argument is implemented correctly in the PSy layer '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "10.6_operator_no_field_scalar.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info)
    gen = str(psy.gen)

    assert LFRicBuild(tmpdir).code_compiles(psy)
    assert "mesh => my_mapping_proxy%fs_from%get_mesh()" in gen
    assert "nlayers = my_mapping_proxy%fs_from%get_nlayers()" in gen
    assert "ndf_w2 = my_mapping_proxy%fs_from%get_ndf()" in gen
    assert "DO cell=1,mesh%get_last_halo_cell(1)" in gen
    assert ("(cell, nlayers, my_mapping_proxy%ncell_3d, my_mapping_proxy%"
            "local_stencil, b, ndf_w2, basis_w2_qr, np_xy_qr, np_z_qr, "
            "weights_xy_qr, weights_z_qr)" in gen)
Beispiel #26
0
def test_node_list_error(tmpdir):
    ''' Test that applying Extract Transformation on objects which are not
    Nodes or a list of Nodes raises a TransformationError. Also raise
    transformation errors when the Nodes do not have the same parent
    if they are incorrectly ordered. '''
    etrans = LFRicExtractTrans()

    # First test for f1 readwrite to read dependency
    psy, _ = get_invoke("3.2_multi_functions_multi_named_invokes.f90",
                        DYNAMO_API,
                        idx=0,
                        dist_mem=False)
    invoke0 = psy.invokes.invoke_list[0]
    invoke1 = psy.invokes.invoke_list[1]
    # Supply an object which is not a Node or a list of Nodes
    with pytest.raises(TransformationError) as excinfo:
        etrans.apply(invoke0)
    assert ("Error in LFRicExtractTrans: Argument must be "
            "a single Node in a Schedule, a Schedule or a list of Nodes in a "
            "Schedule but have been passed an object of type: "
            "<class 'psyclone.dynamo0p3.DynInvoke'>") in str(excinfo.value)

    # Supply Nodes in incorrect order or duplicate Nodes
    node_list = [
        invoke0.schedule.children[0], invoke0.schedule.children[0],
        invoke0.schedule.children[1]
    ]
    with pytest.raises(TransformationError) as excinfo:
        etrans.apply(node_list)
    assert "Children are not consecutive children of one parent:" \
           in str(excinfo.value)
    assert "has position 0, but previous child had position 0."\
        in str(excinfo.value)

    # Supply Nodes which are not children of the same parent
    node_list = [
        invoke0.schedule.children[1], invoke1.schedule.children[0],
        invoke0.schedule.children[2]
    ]
    with pytest.raises(TransformationError) as excinfo:
        etrans.apply(node_list)
    assert ("supplied nodes are not children of the same "
            "parent.") in str(excinfo.value)

    assert LFRicBuild(tmpdir).code_compiles(psy)
Beispiel #27
0
def test_kerncallarglist_quad_rule_error(dist_mem, tmpdir):
    ''' Check that we raise the expected exception if we encounter an
    unsupported quadrature shape in the quad_rule() method. '''
    psy, _ = get_invoke("6_multiple_QR_per_invoke.f90",
                        TEST_API,
                        dist_mem=dist_mem,
                        idx=0)

    assert LFRicBuild(tmpdir).code_compiles(psy)

    schedule = psy.invokes.invoke_list[0].schedule
    loop = schedule.walk(DynLoop)[0]
    create_arg_list = KernCallArgList(loop.loop_body[0])
    # Add an invalid shape to the dict of qr rules
    create_arg_list._kern.qr_rules["broken"] = None
    with pytest.raises(NotImplementedError) as err:
        create_arg_list.quad_rule()
    assert ("no support implemented for quadrature with a shape of 'broken'"
            in str(err.value))
Beispiel #28
0
def test_field_qr_deref(tmpdir):
    ''' Tests that a call, with a set of fields requiring
    quadrature, produces correct code when the quadrature is supplied as the
    component of a derived type. '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "1.1.1_single_invoke_qr_deref.f90"),
                           api="dynamo0.3")
    for dist_mem in [True, False]:
        psy = PSyFactory("dynamo0.3",
                         distributed_memory=dist_mem).create(invoke_info)

        assert LFRicBuild(tmpdir).code_compiles(psy)
        gen = str(psy.gen)

        assert (
            "    SUBROUTINE invoke_0_testkern_qr_type(f1, f2, m1, a, m2, istp,"
            " unit_cube_qr_xyoz)\n" in gen)
        assert ("TYPE(quadrature_xyoz_type), intent(in) :: unit_cube_qr_xyoz"
                in gen)
Beispiel #29
0
def test_operator_nofield_different_space(tmpdir):
    ''' Tests that an operator with no field on different spaces is
    implemented correctly in the PSy layer. '''
    _, invoke_info = parse(os.path.join(
        BASE_PATH, "10.5_operator_no_field_different_"
        "space.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info)
    gen = str(psy.gen)

    assert LFRicBuild(tmpdir).code_compiles(psy)

    assert "mesh => my_mapping_proxy%fs_from%get_mesh()" in gen
    assert "nlayers = my_mapping_proxy%fs_from%get_nlayers()" in gen
    assert "ndf_w3 = my_mapping_proxy%fs_from%get_ndf()" in gen
    assert "ndf_w2 = my_mapping_proxy%fs_to%get_ndf()" in gen
    # We compute operators redundantly (out to the L1 halo)
    assert "DO cell=1,mesh%get_last_halo_cell(1)" in gen
    assert ("(cell, nlayers, my_mapping_proxy%ncell_3d, my_mapping_proxy%"
            "local_stencil, ndf_w2, ndf_w3)" in gen)
Beispiel #30
0
def test_operator(tmpdir):
    ''' Tests that an LMA operator is implemented correctly in the PSy
    layer. '''
    _, invoke_info = parse(os.path.join(BASE_PATH, "10_operator.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info)
    generated_code = str(psy.gen)

    assert LFRicBuild(tmpdir).code_compiles(psy)

    assert ("SUBROUTINE invoke_0_testkern_operator_type(mm_w0, coord, a, qr)"
            in generated_code)
    assert "TYPE(operator_type), intent(in) :: mm_w0" in generated_code
    assert "TYPE(operator_proxy_type) mm_w0_proxy" in generated_code
    assert "mm_w0_proxy = mm_w0%get_proxy()" in generated_code
    assert ("CALL testkern_operator_code(cell, nlayers, mm_w0_proxy%ncell_3d, "
            "mm_w0_proxy%local_stencil, coord_proxy(1)%data, "
            "coord_proxy(2)%data, coord_proxy(3)%data, a, ndf_w0, undf_w0, "
            "map_w0(:,cell), basis_w0_qr, diff_basis_w0_qr, np_xy_qr, "
            "np_z_qr, weights_xy_qr, weights_z_qr)") in generated_code