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))
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))
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))
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)
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
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)
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))
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))
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))
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))
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))
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)
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
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
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))
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"
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))
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)
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))