def test_all_args_same_mesh_error():
    ''' Check that we reject a kernel if all arguments are specified
    as being on the same mesh (coarse or fine). '''
    fparser.logging.disable(fparser.logging.CRITICAL)
    # Both on fine mesh
    code = RESTRICT_MDATA.replace("GH_COARSE", "GH_FINE", 1)
    ast = fpapi.parse(code, ignore_comments=False)
    name = "restrict_kernel_type"
    with pytest.raises(ParseError) as excinfo:
        _ = DynKernMetadata(ast, name=name)
    const = LFRicConstants()
    assert (
        "Inter-grid kernels in the Dynamo 0.3 API must have at least "
        "one field argument on each of the mesh types ({0}). However, "
        "kernel restrict_kernel_type has arguments only on ['gh_fine']".format(
            const.VALID_MESH_TYPES) in str(excinfo.value))
    # Both on coarse mesh
    code = RESTRICT_MDATA.replace("GH_FINE", "GH_COARSE", 1)
    ast = fpapi.parse(code, ignore_comments=False)
    with pytest.raises(ParseError) as excinfo:
        _ = DynKernMetadata(ast, name=name)
    assert ("Inter-grid kernels in the Dynamo 0.3 API must have at least "
            "one field argument on each of the mesh types ({0}). However, "
            "kernel restrict_kernel_type has arguments only on ['gh_coarse']".
            format(const.VALID_MESH_TYPES) in str(excinfo.value))
Exemple #2
0
def test_ad_field_invalid_data_type():
    ''' Tests that an error is raised when the argument descriptor
    metadata for a field has an invalid data type. '''
    fparser.logging.disable(fparser.logging.CRITICAL)
    name = "testkern_field_type"
    # Check real field
    code = FIELD_CODE.replace(
        "arg_type(gh_field,  gh_real,    gh_inc,   w1)",
        "arg_type(gh_field,  gh_unreal,  gh_inc,   w1)", 1)
    ast = fpapi.parse(code, ignore_comments=False)
    with pytest.raises(ParseError) as excinfo:
        _ = DynKernMetadata(ast, name=name)
    assert ("In the LFRic API the 2nd argument of a 'meta_arg' entry should "
            "be a valid data type (one of {0}), but found 'gh_unreal' "
            "in 'arg_type(gh_field, gh_unreal, gh_inc, w1)'.".
            format(LFRicArgDescriptor.VALID_FIELD_DATA_TYPES)
            in str(excinfo.value))
    # Check integer field
    code = FIELD_CODE.replace(
        "arg_type(gh_field,  gh_integer, gh_read,  w3)",
        "arg_type(gh_field,  gh_double,  gh_read,  w3)", 1)
    ast = fpapi.parse(code, ignore_comments=False)
    with pytest.raises(ParseError) as excinfo:
        _ = DynKernMetadata(ast, name=name)
    assert ("but found 'gh_double' in 'arg_type(gh_field, gh_double, "
            "gh_read, w3)'." in str(excinfo.value))
Exemple #3
0
def test_arg_descriptor_invalid_fs():
    ''' Tests that an error is raised when an invalid function space
    name is provided as the third argument for a field. '''
    fparser.logging.disable(fparser.logging.CRITICAL)
    name = "testkern_field_type"
    # Check real field
    code = FIELD_CODE.replace(
        "arg_type(gh_field,  gh_real,    gh_inc,   w1)",
        "arg_type(gh_field,  gh_real,    gh_inc,   w4)", 1)
    ast = fpapi.parse(code, ignore_comments=False)
    with pytest.raises(ParseError) as excinfo:
        _ = DynKernMetadata(ast, name=name)
    assert ("In the LFRic API argument 4 of a 'meta_arg' field entry "
            "must be a valid function-space name (one of {0}) if its "
            "first argument is of ['gh_field'] type, but found 'w4'".
            format(FunctionSpace.VALID_FUNCTION_SPACE_NAMES)
            in str(excinfo.value))
    # Check integer field
    code = FIELD_CODE.replace(
        "arg_type(gh_field,  gh_integer, gh_read,  w3)",
        "arg_type(gh_field,  gh_integer, gh_read,  w10)", 1)
    ast = fpapi.parse(code, ignore_comments=False)
    with pytest.raises(ParseError) as excinfo:
        _ = DynKernMetadata(ast, name=name)
    assert ("if its first argument is of ['gh_field'] type, but found 'w10'"
            in str(excinfo.value))
Exemple #4
0
def test_stub_operator_different_spaces():
    ''' Test that the correct function spaces are provided in the
    correct order when generating a kernel stub with an operator on
    different spaces.

    '''
    # Check the original code (to- and from- spaces both continuous)
    ast = fpapi.parse(OPERATOR_DIFFERENT_SPACES, ignore_comments=False)
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    result = str(kernel.gen_stub)
    assert "(cell, nlayers, op_1_ncell_3d, op_1, ndf_w0, ndf_w1)" in result
    assert "dimension(ndf_w0,ndf_w1,op_1_ncell_3d)" in result
    # Check for discontinuous to- and from- spaces
    code = OPERATOR_DIFFERENT_SPACES.replace(
        "(gh_operator, gh_real, gh_write, w0, w1)",
        "(gh_operator, gh_real, gh_write, w3, any_discontinuous_space_2)", 1)
    ast = fpapi.parse(code, ignore_comments=False)
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    result = str(kernel.gen_stub)
    assert ("(cell, nlayers, op_1_ncell_3d, op_1, ndf_w3, ndf_adspc2_op_1)"
            in result)
    assert "dimension(ndf_w3,ndf_adspc2_op_1,op_1_ncell_3d)" in result
    field_descriptor = metadata.arg_descriptors[0]
    result = str(field_descriptor)
    assert "function_space_to[3]='w3'" in result
    assert "function_space_from[4]='any_discontinuous_space_2'" in result
def test_arg_descriptor_scalar(scalar_ind, scalar_type):
    ''' Test that the LFRicArgDescriptor argument representation works
    as expected for all three types of valid scalar argument:
    'real', 'integer' and 'logical'.

    '''
    fparser.logging.disable(fparser.logging.CRITICAL)
    ast = fpapi.parse(CODE, ignore_comments=False)
    metadata = DynKernMetadata(ast, name="testkern_qr_type")
    scalar_descriptor = metadata.arg_descriptors[scalar_ind]

    # Assert correct string representation from LFRicArgDescriptor
    result = str(scalar_descriptor)
    expected_output = (
        "LFRicArgDescriptor object\n"
        "  argument_type[0]='gh_scalar'\n"
        "  data_type[1]='{0}'\n"
        "  access_descriptor[2]='gh_read'\n".format(scalar_type))
    assert expected_output in result

    # Check LFRicArgDescriptor argument properties
    assert scalar_descriptor.argument_type == "gh_scalar"
    assert scalar_descriptor.data_type == scalar_type
    assert scalar_descriptor.function_space is None
    assert scalar_descriptor.function_spaces == []
    assert str(scalar_descriptor.access) == "READ"
    assert scalar_descriptor.mesh is None
    assert scalar_descriptor.stencil is None
    assert scalar_descriptor.vector_size == 0
def test_enforce_op_bc_kernel_stub_gen():
    ''' Test that the enforce_operator_bc_kernel boundary dofs argument
    modification is handled correctly for kernel stubs.

    '''
    ast = fpapi.parse(os.path.join(BASE_PATH,
                                   "enforce_operator_bc_kernel_mod.F90"),
                      ignore_comments=False)
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    generated_code = str(kernel.gen_stub)
    output = ("  MODULE enforce_operator_bc_mod\n"
              "    IMPLICIT NONE\n"
              "    CONTAINS\n"
              "    SUBROUTINE enforce_operator_bc_code(cell, nlayers, "
              "op_1_ncell_3d, op_1, ndf_aspc1_op_1, ndf_aspc2_op_1, "
              "boundary_dofs_op_1)\n"
              "      USE constants_mod, ONLY: r_def, i_def\n"
              "      IMPLICIT NONE\n"
              "      INTEGER(KIND=i_def), intent(in) :: nlayers\n"
              "      INTEGER(KIND=i_def), intent(in) :: ndf_aspc1_op_1, "
              "ndf_aspc2_op_1\n"
              "      INTEGER(KIND=i_def), intent(in) :: cell\n"
              "      INTEGER(KIND=i_def), intent(in) :: op_1_ncell_3d\n"
              "      REAL(KIND=r_def), intent(inout), dimension("
              "ndf_aspc1_op_1,ndf_aspc2_op_1,op_1_ncell_3d) :: op_1\n"
              "      INTEGER(KIND=i_def), intent(in), "
              "dimension(ndf_aspc1_op_1,2) :: boundary_dofs_op_1\n"
              "    END SUBROUTINE enforce_operator_bc_code\n"
              "  END MODULE enforce_operator_bc_mod")
    assert output in generated_code
def test_vectors():
    ''' test that field vectors are handled correctly for kernel stubs '''
    ast = fpapi.parse(VECTORS, ignore_comments=False)
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    generated_code = kernel.gen_stub
    output = (
        "  MODULE dummy_mod\n"
        "    IMPLICIT NONE\n"
        "    CONTAINS\n"
        "    SUBROUTINE dummy_code(nlayers, field_1_w0_v1, "
        "field_1_w0_v2, field_1_w0_v3, ndf_w0, undf_w0, map_w0)\n"
        "      USE constants_mod, ONLY: r_def, i_def\n"
        "      IMPLICIT NONE\n"
        "      INTEGER(KIND=i_def), intent(in) :: nlayers\n"
        "      INTEGER(KIND=i_def), intent(in) :: ndf_w0\n"
        "      INTEGER(KIND=i_def), intent(in), dimension(ndf_w0) :: map_w0\n"
        "      INTEGER(KIND=i_def), intent(in) :: undf_w0\n"
        "      REAL(KIND=r_def), intent(inout), dimension(undf_w0) :: "
        "field_1_w0_v1\n"
        "      REAL(KIND=r_def), intent(inout), dimension(undf_w0) :: "
        "field_1_w0_v2\n"
        "      REAL(KIND=r_def), intent(inout), dimension(undf_w0) :: "
        "field_1_w0_v3\n"
        "    END SUBROUTINE dummy_code\n"
        "  END MODULE dummy_mod")
    assert output in str(generated_code)
Exemple #8
0
def test_lfric_stub_cma_operators():
    '''Check variable usage detection cma operators.
    mesh_ncell2d, cma_operator

    '''
    from psyclone.dynamo0p3 import DynKernMetadata, DynKern
    from psyclone.domain.lfric import KernStubArgList
    ast = get_ast("dynamo0.3", "columnwise_op_mul_2scalars_kernel_mod.F90")
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    var_accesses = VariablesAccessInfo()
    create_arg_list = KernStubArgList(kernel)
    create_arg_list.generate(var_accesses=var_accesses)
    var_info = str(var_accesses)
    for num in ["1", "3", "5"]:
        assert "ncell_2d: READ" in var_info
        assert "cma_op_"+num+": READ" in var_info
        assert "cma_op_"+num+"_nrow: READ" in var_info
        assert "cma_op_"+num+"_ncol: READ" in var_info
        assert "cma_op_"+num+"_bandwidth: READ" in var_info
        assert "cma_op_"+num+"_alpha: READ" in var_info
        assert "cma_op_"+num+"_beta: READ" in var_info
        assert "cma_op_"+num+"_gamma_m: READ" in var_info
        assert "cma_op_"+num+"_gamma_p: READ" in var_info
Exemple #9
0
def test_arg_descriptor_op():
    ''' Test that the LFRicArgDescriptor argument representation works
    as expected when we have an operator. '''
    ast = fpapi.parse(CODE, ignore_comments=False)
    name = "testkern_qr_type"
    metadata = DynKernMetadata(ast, name=name)
    operator_descriptor = metadata.arg_descriptors[3]

    # Assert correct string representation from LFRicArgDescriptor
    result = str(operator_descriptor)
    expected_output = ("LFRicArgDescriptor object\n"
                       "  argument_type[0]='gh_operator'\n"
                       "  data_type[1]='gh_real'\n"
                       "  access_descriptor[2]='gh_read'\n"
                       "  function_space_to[3]='w2'\n"
                       "  function_space_from[4]='w2'\n")
    assert expected_output in result

    # Check LFRicArgDescriptor argument properties
    assert operator_descriptor.argument_type == "gh_operator"
    assert operator_descriptor.data_type == "gh_real"
    assert operator_descriptor.function_space_to == "w2"
    assert operator_descriptor.function_space_from == "w2"
    assert operator_descriptor.function_space == "w2"
    assert operator_descriptor.function_spaces == ['w2', 'w2']
    assert str(operator_descriptor.access) == "READ"
    assert operator_descriptor.mesh is None
    assert operator_descriptor.stencil is None
    assert operator_descriptor.vector_size == 1
def test_intent():
    ''' test that field intent is generated correctly for kernel stubs '''
    ast = fpapi.parse(INTENT, ignore_comments=False)
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    generated_code = kernel.gen_stub
    output = (
        "  MODULE dummy_mod\n"
        "    IMPLICIT NONE\n"
        "    CONTAINS\n"
        "    SUBROUTINE dummy_code(nlayers, field_1_w3, field_2_w1, "
        "field_3_w1, ndf_w3, undf_w3, map_w3, ndf_w1, undf_w1, map_w1)\n"
        "      USE constants_mod, ONLY: r_def, i_def\n"
        "      IMPLICIT NONE\n"
        "      INTEGER(KIND=i_def), intent(in) :: nlayers\n"
        "      INTEGER(KIND=i_def), intent(in) :: ndf_w1\n"
        "      INTEGER(KIND=i_def), intent(in), dimension(ndf_w1) :: map_w1\n"
        "      INTEGER(KIND=i_def), intent(in) :: ndf_w3\n"
        "      INTEGER(KIND=i_def), intent(in), dimension(ndf_w3) :: map_w3\n"
        "      INTEGER(KIND=i_def), intent(in) :: undf_w3, undf_w1\n"
        "      REAL(KIND=r_def), intent(out), dimension(undf_w3) :: "
        "field_1_w3\n"
        "      REAL(KIND=r_def), intent(inout), dimension(undf_w1) :: "
        "field_2_w1\n"
        "      REAL(KIND=r_def), intent(in), dimension(undf_w1) :: "
        "field_3_w1\n"
        "    END SUBROUTINE dummy_code\n"
        "  END MODULE dummy_mod")
    assert output in str(generated_code)
Exemple #11
0
    def create(self, parse_tree, name=None):
        '''Create API-specific information about the kernel metadata and a
        reference to its code. The API is set when the factory is
        created.

        :param parse_tree: The fparser1 parse tree for the Kernel code.
        :type parse_tree: :py:class:`fparser.one.block_statements.BeginSource`

        :param name: the name of the Kernel. Defaults to None if \
        one is not provided.
        :type name: str or NoneType

        :raises ParseError: if the supplied API is not supported.

        '''
        if self._type == "dynamo0.1":
            from psyclone.dynamo0p1 import DynKernelType
            return DynKernelType(parse_tree, name=name)
        elif self._type == "dynamo0.3":
            from psyclone.dynamo0p3 import DynKernMetadata
            return DynKernMetadata(parse_tree, name=name)
        elif self._type == "gocean0.1":
            from psyclone.gocean0p1 import GOKernelType
            return GOKernelType(parse_tree, name=name)
        elif self._type == "gocean1.0":
            from psyclone.gocean1p0 import GOKernelType1p0
            return GOKernelType1p0(parse_tree, name=name)
        else:
            raise ParseError(
                "KernelTypeFactory:create: Unsupported kernel type '{0}' "
                "found.".format(self._type))
Exemple #12
0
def test_invalid_basis_domain_kernel():
    ''' Check that we reject a kernel with operates_on=domain if it requires
    basis functions. '''
    ast = fpapi.parse('''
module testkern_domain_mod
  type, extends(kernel_type) :: testkern_domain_type
     type(arg_type), meta_args(3) =                          &
          (/ arg_type(gh_scalar, gh_real, gh_read),          &
             arg_type(gh_field,  gh_real, gh_readwrite, w3), &
             arg_type(gh_field,  gh_real, gh_read,      w3)  &
           /)
     type(func_type), dimension(1) :: meta_funcs =  &
          (/ func_type(w3, gh_basis)                &
           /)
     integer :: operates_on = domain
     integer :: gh_shape = gh_quadrature_XYoZ
   contains
     procedure, nopass :: code => testkern_domain_code
  end type testkern_domain_type
contains
  subroutine testkern_domain_code(a, b, c, d)
  end subroutine testkern_domain_code
end module testkern_domain_mod
''')
    with pytest.raises(ParseError) as err:
        DynKernMetadata(ast, name="testkern_domain_type")
    assert ("'domain' cannot be passed basis/differential basis functions "
            "but the metadata for kernel 'testkern_domain_type' contains an "
            "entry for 'meta_funcs'" in str(err.value))
Exemple #13
0
def test_no_stencil_domain_kernel():
    ''' Check that we reject a domain kernel if it has an argument with a
    stencil access. '''
    ast = fpapi.parse('''module testkern_domain_mod
  type, extends(kernel_type) :: testkern_domain_type
     type(arg_type), meta_args(3) =                                         &
          (/ arg_type(gh_scalar, gh_real, gh_read),                         &
             arg_type(gh_field,  gh_real, gh_readwrite, w3),                &
             arg_type(gh_field,  gh_real, gh_read,      w3, stencil(cross)) &
           /)
     integer :: operates_on = domain
   contains
     procedure, nopass :: code => testkern_domain_code
  end type testkern_domain_type
contains
  subroutine testkern_domain_code(a, b, c, d)
  end subroutine testkern_domain_code
end module testkern_domain_mod
''',
                      ignore_comments=False)
    with pytest.raises(ParseError) as err:
        DynKernMetadata(ast, name="testkern_domain_type")
    assert ("domain are not permitted to have arguments with a stencil "
            "access but found: 'arg_type(gh_field, gh_real, gh_read, "
            "w3, stencil(cross))'" in str(err.value))
Exemple #14
0
def test_invalid_space_domain_kernel():
    ''' Check that we reject a domain kernel if its metadata specifies a
    field argument on a continuous space. '''
    ast = fpapi.parse('''module testkern_domain_mod
  type, extends(kernel_type) :: testkern_domain_type
     type(arg_type), meta_args(3) =                          &
          (/ arg_type(gh_scalar, gh_real, gh_read),          &
             arg_type(gh_field,  gh_real, gh_readwrite, w3), &
             arg_type(gh_field,  gh_real, gh_read,      w2)  &
           /)
     integer :: operates_on = domain
   contains
     procedure, nopass :: code => testkern_domain_code
  end type testkern_domain_type
contains
  subroutine testkern_domain_code(a, b, c, d)
  end subroutine testkern_domain_code
end module testkern_domain_mod
''',
                      ignore_comments=False)
    with pytest.raises(ParseError) as err:
        DynKernMetadata(ast, name="testkern_domain_type")
    assert ("domain only accept field arguments on discontinuous function "
            "spaces but found 'w2' in 'arg_type(gh_field, gh_real, "
            "gh_read, w2)'" in str(err.value))
Exemple #15
0
def test_ad_field_init_wrong_data_type(monkeypatch):
    ''' Test that an error is raised if an invalid data type
    is passed to the LFRicArgDescriptor._init_field() method. '''
    ast = fpapi.parse(FIELD_CODE, ignore_comments=False)
    name = "testkern_field_type"
    metadata = DynKernMetadata(ast, name=name)
    # Get a real field argument descriptor and set a wrong data type
    real_field_arg = metadata._inits[1]
    real_field_arg.args[1].name = "gh_double"
    # Get an integer field argument descriptor and set a wrong data type
    int_field_arg = metadata._inits[2]
    int_field_arg.args[1].name = "gh_double"
    # Now try to trip the error by making the initial test think
    # that 'gh_double' is actually a valid data type
    monkeypatch.setattr(
        target=LFRicArgDescriptor, name="VALID_ARG_DATA_TYPES",
        value=LFRicArgDescriptor.VALID_ARG_DATA_TYPES + ["gh_double"])
    # Check real field
    with pytest.raises(InternalError) as excinfo:
        LFRicArgDescriptor(
            real_field_arg, metadata.iterates_over)._init_field(
                real_field_arg, metadata.iterates_over)
    assert ("Expected one of {0} as the field data type but got 'gh_double'.".
            format(LFRicArgDescriptor.VALID_FIELD_DATA_TYPES) in
            str(excinfo.value))
    # Check integer field
    with pytest.raises(InternalError) as excinfo:
        LFRicArgDescriptor(
            int_field_arg, metadata.iterates_over)._init_field(
                int_field_arg, metadata.iterates_over)
    assert ("Expected one of {0} as the field data type but got 'gh_double'.".
            format(LFRicArgDescriptor.VALID_FIELD_DATA_TYPES) in
            str(excinfo.value))
def test_stub_basis_wrong_shape(monkeypatch):
    ''' Check that stub generation for a kernel requiring basis functions
    for quadrature raises the correct errors if the kernel meta-data is
    broken '''
    from psyclone import dynamo0p3
    ast = fpapi.parse(BASIS, ignore_comments=False)
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    monkeypatch.setattr(kernel, "_eval_shape", value="gh_quadrature_wrong")
    with pytest.raises(GenerationError) as excinfo:
        _ = kernel.gen_stub
    assert (
        "Internal error: unrecognised evaluator shape (gh_quadrature_wrong)"
        in str(excinfo))
    monkeypatch.setattr(dynamo0p3,
                        "VALID_QUADRATURE_SHAPES",
                        value=[
                            "gh_quadrature_xyz", "gh_quadrature_xyoz",
                            "gh_quadrature_xoyoz", "gh_quadrature_wrong"
                        ])
    with pytest.raises(GenerationError) as excinfo:
        _ = kernel.gen_stub
    assert ("shapes other than GH_QUADRATURE_XYoZ are not yet supported"
            in str(excinfo))
def test_stub_dbasis_wrong_shape(monkeypatch):
    ''' Check that stub generation for a kernel requiring differential basis
    functions for quadrature raises the correct errors if the kernel meta-data
    is broken '''
    from psyclone import dynamo0p3
    # Change meta-data to specify differential basis functions
    diff_basis = BASIS.replace("gh_basis", "gh_diff_basis")
    print diff_basis
    ast = fpapi.parse(diff_basis, ignore_comments=False)
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    monkeypatch.setattr(kernel, "_eval_shape", value="gh_quadrature_wrong")
    with pytest.raises(GenerationError) as excinfo:
        _ = kernel.gen_stub
    assert (
        "Internal error: unrecognised evaluator shape (gh_quadrature_wrong)"
        in str(excinfo))
    monkeypatch.setattr(dynamo0p3,
                        "VALID_QUADRATURE_SHAPES",
                        value=[
                            "gh_quadrature_xyz", "gh_quadrature_xyoz",
                            "gh_quadrature_xoyoz", "gh_quadrature_wrong"
                        ])
    with pytest.raises(NotImplementedError) as excinfo:
        _ = kernel.gen_stub
    assert ("diff-basis for quadrature shape 'gh_quadrature_wrong' not yet "
            "implemented" in str(excinfo))
Exemple #18
0
def test_invalid_mesh_props_domain_kernel():
    ''' Check that we reject a kernel with operates_on=domain if it requires
    properties of the mesh. '''
    ast = fpapi.parse('''
module testkern_domain_mod
  type, extends(kernel_type) :: testkern_domain_type
     type(arg_type), meta_args(2) =                         &
          (/ arg_type(gh_scalar, gh_real, gh_read),         &
             arg_type(gh_field,  gh_real, gh_readwrite, w3) &
           /)
     type(mesh_data_type), dimension(1) :: meta_mesh = &
                        (/ mesh_data_type(adjacent_face) /)
     integer :: operates_on = domain
   contains
     procedure, nopass :: code => testkern_domain_code
  end type testkern_domain_type
contains
  subroutine testkern_domain_code(a, b, c, d)
  end subroutine testkern_domain_code
end module testkern_domain_mod
''')
    with pytest.raises(ParseError) as err:
        DynKernMetadata(ast, name="testkern_domain_type")
    assert ("'testkern_domain_type' operates on the domain but requests "
            "properties of the mesh ([" in str(err.value))
    assert "ADJACENT_FACE" in str(err.value)
Exemple #19
0
def test_lfric_stub_args():
    '''Check that correct stub code is produced when there are multiple
    stencils.

    '''
    ast = get_ast("dynamo0.3", "testkern_stencil_multi_mod.f90")
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    var_accesses = VariablesAccessInfo()
    create_arg_list = KernStubArgList(kernel)
    create_arg_list.generate(var_accesses=var_accesses)
    var_info = str(var_accesses)
    assert "field_1_w1: READ+WRITE" in var_info
    assert "field_2_stencil_dofmap: READ" in var_info
    assert "field_2_stencil_size: READ" in var_info
    assert "field_2_w2: READ" in var_info
    assert "field_3_direction: READ" in var_info
    assert "field_3_stencil_dofmap: READ" in var_info
    assert "field_3_stencil_size: READ" in var_info
    assert "field_3_w2: READ" in var_info
    assert "field_4_stencil_dofmap: READ" in var_info
    assert "field_4_stencil_size: READ" in var_info
    assert "field_4_w3: READ" in var_info
    assert "map_w1: READ" in var_info
    assert "map_w2: READ" in var_info
    assert "map_w3: READ" in var_info
    assert "ndf_w1: READ" in var_info
    assert "ndf_w2: READ" in var_info
    assert "ndf_w3: READ" in var_info
    assert "nlayers: READ" in var_info
    assert "undf_w1: READ" in var_info
    assert "undf_w2: READ" in var_info
    assert "undf_w3: READ" in var_info
Exemple #20
0
def test_invalid_ref_elem_props_domain_kernel():
    ''' Check that we reject a kernel with operates_on=domain if it requires
    properties of the reference element. '''
    ast = fpapi.parse('''
module testkern_domain_mod
  type, extends(kernel_type) :: testkern_domain_type
     type(arg_type), meta_args(2) =                         &
          (/ arg_type(gh_scalar, gh_real, gh_read),         &
             arg_type(gh_field,  gh_real, gh_readwrite, w3) &
           /)
     type(reference_element_data_type), dimension(1) :: &
         meta_reference_element =                       &
            (/ reference_element_data_type(normals_to_horizontal_faces) /)
     integer :: operates_on = domain
   contains
     procedure, nopass :: code => testkern_domain_code
  end type testkern_domain_type
contains
  subroutine testkern_domain_code(a, b, c, d)
  end subroutine testkern_domain_code
end module testkern_domain_mod
''')
    with pytest.raises(ParseError) as err:
        DynKernMetadata(ast, name="testkern_domain_type")
    assert ("'testkern_domain_type' operates on the domain but requests "
            "properties of the reference element ([" in str(err.value))
    assert "NORMALS_TO_HORIZONTAL_FACES" in str(err.value)
def test_stub_stencil_multi():
    '''Check that correct stub code is produced when there are multiple
    stencils'''
    ast = fpapi.parse(os.path.join(BASE_PATH,
                                   "testkern_stencil_multi_mod.f90"),
                      ignore_comments=False)
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    generated_code = str(kernel.gen_stub)
    result1 = (
        "    SUBROUTINE testkern_stencil_multi_code(nlayers, field_1_w1, "
        "field_2_w2, field_2_stencil_size, field_2_stencil_dofmap, field_3_w2,"
        " field_3_stencil_size, field_3_direction, field_3_stencil_dofmap, "
        "field_4_w3, field_4_stencil_size, field_4_stencil_dofmap, ndf_w1, "
        "undf_w1, map_w1, ndf_w2, undf_w2, map_w2, ndf_w3, undf_w3, map_w3)")
    assert result1 in generated_code
    result2 = (
        "      REAL(KIND=r_def), intent(in), dimension(undf_w2) :: "
        "field_3_w2\n"
        "      REAL(KIND=r_def), intent(in), dimension(undf_w3) :: "
        "field_4_w3\n"
        "      INTEGER(KIND=i_def), intent(in) :: field_2_stencil_size, "
        "field_3_stencil_size, field_4_stencil_size\n"
        "      INTEGER(KIND=i_def), intent(in) :: field_3_direction\n"
        "      INTEGER(KIND=i_def), intent(in), "
        "dimension(ndf_w2,field_2_stencil_size) :: field_2_stencil_dofmap\n"
        "      INTEGER(KIND=i_def), intent(in), "
        "dimension(ndf_w2,field_3_stencil_size) :: field_3_stencil_dofmap\n"
        "      INTEGER(KIND=i_def), intent(in), "
        "dimension(ndf_w3,field_4_stencil_size) :: field_4_stencil_dofmap")

    assert result2 in generated_code
Exemple #22
0
def test_invalid_mg_domain_kernel():
    ''' Check that we reject a kernel with operates_on=domain if it involves
    multi-grid (fields on different grids). '''
    ast = fpapi.parse('''
module restrict_mod
type, public, extends(kernel_type) :: restrict_kernel_type
   private
   type(arg_type) :: meta_args(2) = (/                          &
       arg_type(GH_FIELD, GH_REAL, GH_READWRITE,                &
                ANY_DISCONTINUOUS_SPACE_1, mesh_arg=GH_COARSE), &
       arg_type(GH_FIELD, GH_REAL, GH_READ,                     &
                ANY_DISCONTINUOUS_SPACE_2, mesh_arg=GH_FINE  )  &
       /)
  integer :: operates_on = domain
contains
  procedure, nopass :: restrict_kernel_code
end type restrict_kernel_type
contains
  subroutine restrict_kernel_code()
  end subroutine restrict_kernel_code
end module restrict_mod
''')
    with pytest.raises(ParseError) as err:
        DynKernMetadata(ast, name="restrict_kernel_type")
    assert ("'restrict_kernel_type' operates on the domain but has fields on "
            "different mesh resolutions" in str(err.value))
Exemple #23
0
def test_domain_kernel():
    ''' Check that we can successfully parse metadata that specifies a
    kernel with operates_on = DOMAIN. '''
    ast = fpapi.parse('''
module testkern_domain_mod
  type, extends(kernel_type) :: testkern_domain_type
     type(arg_type), meta_args(5) =                             &
          (/ arg_type(gh_scalar, gh_real,    gh_read),          &
             arg_type(gh_field,  gh_real,    gh_readwrite, w3), &
             arg_type(gh_field,  gh_real,    gh_read,      w3), &
             arg_type(gh_field,  gh_real,    gh_read,      w3), &
             arg_type(gh_scalar, gh_integer, gh_read)           &
           /)
     integer :: operates_on = domain
   contains
     procedure, nopass :: code => testkern_domain_code
  end type testkern_domain_type
contains
  subroutine testkern_domain_code(a, b, c, d)
  end subroutine testkern_domain_code
end module testkern_domain_mod
''',
                      ignore_comments=False)
    dkm = DynKernMetadata(ast, name="testkern_domain_type")
    assert dkm.iterates_over == "domain"
Exemple #24
0
def test_invalid_arg_domain_kernel():
    ''' Check that we reject a domain kernel if its metadata specifies
    an operator argument. '''
    ast = fpapi.parse('''module testkern_domain_mod
  type, extends(kernel_type) :: testkern_domain_type
     type(arg_type), meta_args(4) =                               &
          (/ arg_type(gh_scalar,   gh_real, gh_read),             &
             arg_type(gh_field,    gh_real, gh_readwrite, w3),    &
             arg_type(gh_field,    gh_real, gh_read,      w3),    &
             arg_type(gh_operator, gh_real, gh_read,      w2, w2) &
           /)
     integer :: operates_on = domain
   contains
     procedure, nopass :: code => testkern_domain_code
  end type testkern_domain_type
contains
  subroutine testkern_domain_code(a, b, c, d)
  end subroutine testkern_domain_code
end module testkern_domain_mod
''',
                      ignore_comments=False)
    with pytest.raises(ParseError) as err:
        DynKernMetadata(ast, name="testkern_domain_type")
    assert ("'domain' is only permitted to accept scalar and field arguments "
            "but the metadata for kernel 'testkern_domain_type' includes an "
            "argument of type 'gh_operator'" in str(err.value))
Exemple #25
0
def test_mesh_prop_stub_gen():
    ''' Check that correct kernel stub code is produced when the kernel
    metadata contains a mesh property. '''
    ast = fpapi.parse(os.path.join(BASE_PATH,
                                   "testkern_mesh_prop_mod.F90"),
                      ignore_comments=False)
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    gen = str(kernel.gen_stub).lower()

    output = (
        "  module testkern_mesh_prop_mod\n"
        "    implicit none\n"
        "    contains\n"
        "    subroutine testkern_mesh_prop_code(nlayers, rscalar_1, "
        "field_2_w1, ndf_w1, undf_w1, map_w1, nfaces_re_h, adjacent_face)\n"
        "      use constants_mod, only: r_def, i_def\n"
        "      implicit none\n"
        "      integer(kind=i_def), intent(in) :: nlayers\n"
        "      integer(kind=i_def), intent(in) :: ndf_w1\n"
        "      integer(kind=i_def), intent(in), dimension(ndf_w1) :: map_w1\n"
        "      integer(kind=i_def), intent(in) :: undf_w1\n"
        "      real(kind=r_def), intent(in) :: rscalar_1\n"
        "      real(kind=r_def), intent(inout), dimension(undf_w1) :: "
        "field_2_w1\n"
        "      integer(kind=i_def), intent(in) :: nfaces_re_h\n"
        "      integer(kind=i_def), intent(in), dimension(nfaces_re_h) :: "
        "adjacent_face\n"
        "    end subroutine testkern_mesh_prop_code\n"
        "  end module testkern_mesh_prop_mod")
    assert output in gen
def test_refelem_quad_stub_gen():
    ''' Check that correct stub code is produced when the kernel metadata
    contain reference element and quadrature properties (quadrature
    properties should be placed at the end of subroutine argument list). '''
    ast = fpapi.parse(REF_ELEM_QUAD_MDATA, ignore_comments=False)
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    gen = str(kernel.gen_stub)

    output1 = (
        "  SUBROUTINE testkern_refelem_quad_code(nlayers, field_1_w1, "
        "field_2_wtheta, ndf_w1, undf_w1, map_w1, basis_w1_qr_xyoz, "
        "ndf_wtheta, undf_wtheta, map_wtheta, basis_wtheta_qr_xyoz, "
        "nfaces_re, normals_to_faces, out_normals_to_faces, np_xy_qr_xyoz, "
        "np_z_qr_xyoz, weights_xy_qr_xyoz, weights_z_qr_xyoz)")
    assert output1 in gen
    output2 = (
        "      INTEGER(KIND=i_def), intent(in) :: np_xy_qr_xyoz, "
        "np_z_qr_xyoz\n"
        "      REAL(KIND=r_def), intent(in), "
        "dimension(3,ndf_w1,np_xy_qr_xyoz,np_z_qr_xyoz) :: basis_w1_qr_xyoz\n"
        "      REAL(KIND=r_def), intent(in), "
        "dimension(1,ndf_wtheta,np_xy_qr_xyoz,np_z_qr_xyoz) :: "
        "basis_wtheta_qr_xyoz\n"
        "      REAL(KIND=r_def), intent(in), dimension(np_xy_qr_xyoz) :: "
        "weights_xy_qr_xyoz\n"
        "      REAL(KIND=r_def), intent(in), dimension(np_z_qr_xyoz) :: "
        "weights_z_qr_xyoz\n"
        "      INTEGER(KIND=i_def), intent(in) :: nfaces_re\n"
        "      REAL(KIND=r_def), intent(in), dimension(3,nfaces_re) :: "
        "normals_to_faces\n"
        "      REAL(KIND=r_def), intent(in), dimension(3,nfaces_re) :: "
        "out_normals_to_faces")
    assert output2 in gen
def test_enforce_bc_kernel_stub_gen():
    ''' Test that the enforce_bc_kernel boundary layer argument modification
    is handled correctly for kernel stubs.

    '''
    ast = fpapi.parse(os.path.join(BASE_PATH, "enforce_bc_kernel_mod.f90"),
                      ignore_comments=False)
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    generated_code = kernel.gen_stub
    output = ("  MODULE enforce_bc_mod\n"
              "    IMPLICIT NONE\n"
              "    CONTAINS\n"
              "    SUBROUTINE enforce_bc_code(nlayers, field_1_aspc1_field_1, "
              "ndf_aspc1_field_1, undf_aspc1_field_1, map_aspc1_field_1, "
              "boundary_dofs_field_1)\n"
              "      USE constants_mod, ONLY: r_def, i_def\n"
              "      IMPLICIT NONE\n"
              "      INTEGER(KIND=i_def), intent(in) :: nlayers\n"
              "      INTEGER(KIND=i_def), intent(in) :: ndf_aspc1_field_1\n"
              "      INTEGER(KIND=i_def), intent(in), "
              "dimension(ndf_aspc1_field_1) :: map_aspc1_field_1\n"
              "      INTEGER(KIND=i_def), intent(in) :: undf_aspc1_field_1\n"
              "      REAL(KIND=r_def), intent(inout), "
              "dimension(undf_aspc1_field_1) :: field_1_aspc1_field_1\n"
              "      INTEGER(KIND=i_def), intent(in), "
              "dimension(ndf_aspc1_field_1,2) :: boundary_dofs_field_1\n"
              "    END SUBROUTINE enforce_bc_code\n"
              "  END MODULE enforce_bc_mod")
    assert output in str(generated_code)
def test_dynkern_setup(monkeypatch):
    ''' Check that internal-consistency checks in DynKern._setup() work
    as expected '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "1.1.0_single_invoke_xyoz_qr.f90"),
                           api=API)
    psy = PSyFactory(API).create(invoke_info)
    # Get hold of a DynKern object
    schedule = psy.invokes.invoke_list[0].schedule
    kern = schedule.children[3].children[0]
    # Monkeypatch a couple of __init__ routines so that we can get past
    # them in the _setup() routine.
    from psyclone.psyGen import Kern
    monkeypatch.setattr(Kern, "__init__",
                        lambda me, ktype, kcall, parent, check: None)
    from psyclone.parse import KernelCall
    monkeypatch.setattr(KernelCall, "__init__",
                        lambda me, mname, ktype, args: None)
    # Break the shape of the quadrature for this kernel
    monkeypatch.setattr(kern, "_eval_shape", value="gh_wrong_shape")
    # Rather than try and mock-up a DynKernMetadata object, it's easier
    # to make one properly by parsing the kernel code.
    ast = fpapi.parse(os.path.join(BASE_PATH, "testkern_qr.F90"),
                      ignore_comments=False)
    name = "testkern_qr_type"
    dkm = DynKernMetadata(ast, name=name)
    # Finally, call the _setup() method
    with pytest.raises(GenerationError) as excinfo:
        kern._setup(dkm, "my module", None, None)
    assert ("Internal error: evaluator shape 'gh_wrong_shape' is not "
            "recognised" in str(excinfo))
def test_sub_name():
    ''' test for expected behaviour when the kernel subroutine does
    not conform to the convention of having "_code" at the end of its
    name. In this case we append "_code to the name and _mod to the
    kernel name.'''
    ast = fpapi.parse(SUB_NAME, ignore_comments=False)
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    generated_code = kernel.gen_stub
    output = (
        "  MODULE dummy_mod\n"
        "    IMPLICIT NONE\n"
        "    CONTAINS\n"
        "    SUBROUTINE dummy_code(nlayers, field_1_w1, "
        "ndf_w1, undf_w1, map_w1)\n"
        "      USE constants_mod, ONLY: r_def, i_def\n"
        "      IMPLICIT NONE\n"
        "      INTEGER(KIND=i_def), intent(in) :: nlayers\n"
        "      INTEGER(KIND=i_def), intent(in) :: ndf_w1\n"
        "      INTEGER(KIND=i_def), intent(in), dimension(ndf_w1) :: map_w1\n"
        "      INTEGER(KIND=i_def), intent(in) :: undf_w1\n"
        "      REAL(KIND=r_def), intent(inout), dimension(undf_w1) :: "
        "field_1_w1\n"
        "    END SUBROUTINE dummy_code\n"
        "  END MODULE dummy_mod")
    assert output in str(generated_code)
Exemple #30
0
def test_lfricfields_stub_err():
    ''' Check that the LFRicFields constructor raises the expected internal
    error if it encounters an unrecognised intrinsic type of a field
    argument when generating a kernel stub.

    '''
    fparser.logging.disable(fparser.logging.CRITICAL)
    ast = fpapi.parse(FIELD_CODE, ignore_comments=False)
    metadata = DynKernMetadata(ast)
    kernel = DynKern()
    kernel.load_meta(metadata)
    # Create an empty Kernel stub module and subroutine objects
    psy_module = ModuleGen("testkern_2qr_int_field_mod")
    sub_stub = SubroutineGen(psy_module,
                             name="testkern_2qr_int_field_code",
                             implicitnone=True)
    # Sabotage the field argument to make it have an invalid intrinsic type
    fld_arg = kernel.arguments.args[1]
    fld_arg.descriptor._data_type = "gh_invalid_type"
    print(fld_arg.descriptor._data_type)
    with pytest.raises(InternalError) as err:
        LFRicFields(kernel)._stub_declarations(sub_stub)
    assert ("Found an unsupported data type 'gh_invalid_type' in "
            "kernel stub declarations for the field argument 'field_2'. "
            "Supported types are {0}.".format(
                LFRicArgDescriptor.VALID_FIELD_DATA_TYPES) in str(err.value))