def generate(filename, api=""): ''' Generates an empty kernel subroutine with the required arguments and datatypes (which we call a stub) when presented with Kernel Metadata. This is useful for Kernel developers to make sure they are using the correct arguments in the correct order. The Kernel Metadata must be presented in the standard Kernel format. :param str filename: the name of the file for which to create a \ kernel stub for. :param str api: the name of the API for which to create a kernel \ stub. Must be one of the supported stub APIs. :returns: root of fparser1 parse tree for the stub routine. :rtype: :py:class:`fparser.one.block_statements.Module` :raises GenerationError: if an invalid stub API is specified. :raises IOError: if filename does not specify a file. :raises ParseError: if the given file could not be parsed. :raises GenerationError: if a kernel stub does not have a supported \ iteration space (currently only "cells"). ''' if api == "": api = Config.get().default_stub_api if api not in Config.get().supported_stub_apis: raise GenerationError( "Kernel stub generator: Unsupported API '{0}' specified. " "Supported APIs are {1}.".format(api, Config.get().supported_stub_apis)) if not os.path.isfile(filename): raise IOError( "Kernel stub generator: File '{0}' not found.".format(filename)) # Drop cache fparser.one.parsefortran.FortranParser.cache.clear() fparser.logging.disable(fparser.logging.CRITICAL) try: ast = fparser.api.parse(filename, ignore_comments=False) except (fparser.common.utils.AnalyzeError, AttributeError) as error: raise ParseError("Kernel stub generator: Code appears to be invalid " "Fortran: {0}.".format(str(error))) metadata = DynKernMetadata(ast) kernel = DynKern() kernel.load_meta(metadata) # Check kernel iteration space before generating code if (api == "dynamo0.3" and kernel.iterates_over not in USER_KERNEL_ITERATION_SPACES): raise GenerationError( "The LFRic API kernel stub generator supports kernels that operate" " on one of {0}, but found '{1}' in kernel '{2}'.".format( USER_KERNEL_ITERATION_SPACES, kernel.iterates_over, kernel.name)) return kernel.gen_stub
def test_stub_cross2d_stencil(): ''' Check that the correct stub code is generated when using a CROSS2D stencil ''' ast = fpapi.parse(os.path.join(BASE_PATH, "testkern_stencil_cross2d_mod.f90"), ignore_comments=False) metadata = DynKernMetadata(ast) kernel = DynKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) print(generated_code) result1 = ( " SUBROUTINE testkern_stencil_cross2d_code(nlayers, field_1_w1, " "field_2_w2, field_2_stencil_size, field_2_max_branch_length, " "field_2_stencil_dofmap, field_3_w2, field_4_w3, ndf_w1, undf_w1, " "map_w1, ndf_w2, undf_w2, map_w2, ndf_w3, undf_w3, map_w3)" ) assert result1 in generated_code result2 = ( " INTEGER(KIND=i_def), intent(in), dimension(4) :: " "field_2_stencil_size\n" " INTEGER(KIND=i_def), intent(in) :: field_2_max_branch_length\n" " INTEGER(KIND=i_def), intent(in), dimension(ndf_w2," "field_2_max_branch_length,4) :: field_2_stencil_dofmap") assert result2 in generated_code
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") ast = fpapi.parse(diff_basis, ignore_comments=False) metadata = DynKernMetadata(ast) kernel = DynKern() kernel.load_meta(metadata) monkeypatch.setattr(kernel, "_eval_shapes", value=["gh_quadrature_wrong"]) with pytest.raises(InternalError) as excinfo: _ = kernel.gen_stub assert ("Unrecognised evaluator shape: 'gh_quadrature_wrong'" in str(excinfo.value)) monkeypatch.setattr(dynamo0p3, "VALID_QUADRATURE_SHAPES", value=[ "gh_quadrature_xyz", "gh_quadrature_xyoz", "gh_quadrature_xoyoz", "gh_quadrature_wrong" ]) # Add a fake QR rule for the invalid shape (so that we can get to the bit # of code we want to test) kernel.qr_rules["gh_quadrature_wrong"] = kernel.QRRule( "arg", "arg_name", []) with pytest.raises(NotImplementedError) as excinfo: _ = kernel.gen_stub assert ("Unrecognised shape 'gh_quadrature_wrong' specified in " "dynamo0p3.qr_basis_alloc_args(). Should be" in str(excinfo.value))
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_null_loop(): ''' Check that we can create a 'null'-type loop and that the validation check in the load() method behaves as expected. ''' loop = DynLoop(loop_type="null") assert loop.loop_type == "null" assert loop.node_str(colour=False) == "Loop[type='null']" # Create a kernel by parsing some metadata ast = fpapi.parse(''' module testkern_mod type, extends(kernel_type) :: testkern_type type(arg_type), meta_args(2) = & (/ arg_type(gh_scalar, gh_real, gh_read), & arg_type(gh_field, gh_real, gh_readwrite, w3) & /) integer :: operates_on = cell_column contains procedure, nopass :: code => testkern_code end type testkern_type contains subroutine testkern_code(a, b, c, d) end subroutine testkern_code end module testkern_mod ''', ignore_comments=False) dkm = DynKernMetadata(ast, name="testkern_type") kern = DynKern() kern.load_meta(dkm) with pytest.raises(GenerationError) as err: loop.load(kern) assert ("A DynLoop of type 'null' can only contain a kernel that " "operates on the 'domain' but kernel 'testkern_code' operates " "on 'cell_column'" in str(err.value))
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_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_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 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))
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_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_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_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_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_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_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_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 generate(filename, api=""): '''Generates an empty kernel subroutine with the required arguments and datatypes (which we call a stub) when presented with Kernel Metadata. This is useful for Kernel developers to make sure they are using the correct arguments in the correct order. The Kernel Metadata must be presented in the standard Kernel format. ''' if api == "": api = DEFAULTSTUBAPI if api not in SUPPORTEDSTUBAPIS: print "Unsupported API '{0}' specified. Supported API's are {1}.".\ format(api, SUPPORTEDSTUBAPIS) raise GenerationError( "generate: Unsupported API '{0}' specified. Supported types are " "{1}.".format(api, SUPPORTEDSTUBAPIS)) if not os.path.isfile(filename): raise IOError("file '{0}' not found".format(filename)) # drop cache fparser.one.parsefortran.FortranParser.cache.clear() fparser.logging.disable('CRITICAL') try: ast = fparser.api.parse(filename, ignore_comments=False) except (fparser.common.utils.AnalyzeError, AttributeError) as error: raise ParseError("Code appears to be invalid Fortran: " + str(error)) metadata = DynKernMetadata(ast) kernel = DynKern() kernel.load_meta(metadata) return kernel.gen_stub
def test_int_field_all_stencils_gen_stub(): ''' Test that we generate correct code for kernel stubs that contain integer-valued fields with all supported stencil accesses. ''' ast = fpapi.parse(os.path.join(BASE_PATH, "testkern_stencil_multi_int_field_mod.f90"), ignore_comments=False) metadata = DynKernMetadata(ast) kernel = DynKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) output = ( " MODULE testkern_stencil_multi_int_field_mod\n" " IMPLICIT NONE\n" " CONTAINS\n" " SUBROUTINE testkern_stencil_multi_int_field_code(nlayers, " "field_1_w2broken, field_2_w1, field_2_stencil_size, " "field_2_stencil_dofmap, field_3_w0, field_3_stencil_size, " "field_3_direction, field_3_stencil_dofmap, field_4_w2v, " "field_4_stencil_size, field_4_stencil_dofmap, ndf_w2broken, " "undf_w2broken, map_w2broken, ndf_w1, undf_w1, map_w1, " "ndf_w0, undf_w0, map_w0, ndf_w2v, undf_w2v, map_w2v)\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) :: ndf_w1\n" " INTEGER(KIND=i_def), intent(in), dimension(ndf_w1) :: map_w1\n" " INTEGER(KIND=i_def), intent(in) :: ndf_w2broken\n" " INTEGER(KIND=i_def), intent(in), dimension(ndf_w2broken) :: " "map_w2broken\n" " INTEGER(KIND=i_def), intent(in) :: ndf_w2v\n" " INTEGER(KIND=i_def), intent(in), dimension(ndf_w2v) :: " "map_w2v\n" " INTEGER(KIND=i_def), intent(in) :: undf_w2broken, undf_w1, " "undf_w0, undf_w2v\n" " INTEGER(KIND=i_def), intent(inout), " "dimension(undf_w2broken) :: field_1_w2broken\n" " INTEGER(KIND=i_def), intent(in), dimension(undf_w1) :: " "field_2_w1\n" " INTEGER(KIND=i_def), intent(in), dimension(undf_w0) :: " "field_3_w0\n" " INTEGER(KIND=i_def), intent(in), dimension(undf_w2v) :: " "field_4_w2v\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_w1," "field_2_stencil_size) :: field_2_stencil_dofmap\n" " INTEGER(KIND=i_def), intent(in), dimension(ndf_w0," "field_3_stencil_size) :: field_3_stencil_dofmap\n" " INTEGER(KIND=i_def), intent(in), dimension(ndf_w2v," "field_4_stencil_size) :: field_4_stencil_dofmap\n" " END SUBROUTINE testkern_stencil_multi_int_field_code\n" " END MODULE testkern_stencil_multi_int_field_mod") assert output in generated_code
def test_load_meta_wrong_type(): ''' Test that the load_meta function raises an appropriate error if the meta-data contains an un-recognised type ''' fparser.logging.disable(fparser.logging.CRITICAL) ast = fpapi.parse(INTENT, ignore_comments=False) metadata = DynKernMetadata(ast) kernel = DynKern() # Break the meta-data metadata.arg_descriptors[0]._type = "gh_hedge" with pytest.raises(GenerationError) as excinfo: kernel.load_meta(metadata) assert "load_meta expected one of '['gh_field'," in str(excinfo.value)
def test_spaces(): ''' test that field spaces are handled correctly for kernel stubs ''' ast = fpapi.parse(SPACES, ignore_comments=False) metadata = DynKernMetadata(ast) kernel = DynKern() kernel.load_meta(metadata) generated_code = str(kernel.gen_stub) output = ( " MODULE dummy_mod\n" " IMPLICIT NONE\n" " CONTAINS\n" " SUBROUTINE dummy_code(nlayers, field_1_w0, field_2_w1, " "field_3_w2, field_4_w3, field_5_wtheta, field_6_w2h, field_7_w2v, " "ndf_w0, undf_w0, map_w0, ndf_w1, undf_w1, map_w1, ndf_w2, undf_w2, " "map_w2, ndf_w3, undf_w3, map_w3, ndf_wtheta, undf_wtheta, " "map_wtheta, ndf_w2h, undf_w2h, map_w2h, ndf_w2v, undf_w2v, " "map_w2v)\n" " USE constants_mod, ONLY: r_def\n" " IMPLICIT NONE\n" " INTEGER, intent(in) :: nlayers\n" " INTEGER, intent(in) :: ndf_w0\n" " INTEGER, intent(in), dimension(ndf_w0) :: map_w0\n" " INTEGER, intent(in) :: ndf_w1\n" " INTEGER, intent(in), dimension(ndf_w1) :: map_w1\n" " INTEGER, intent(in) :: ndf_w2\n" " INTEGER, intent(in), dimension(ndf_w2) :: map_w2\n" " INTEGER, intent(in) :: ndf_w2h\n" " INTEGER, intent(in), dimension(ndf_w2h) :: map_w2h\n" " INTEGER, intent(in) :: ndf_w2v\n" " INTEGER, intent(in), dimension(ndf_w2v) :: map_w2v\n" " INTEGER, intent(in) :: ndf_w3\n" " INTEGER, intent(in), dimension(ndf_w3) :: map_w3\n" " INTEGER, intent(in) :: ndf_wtheta\n" " INTEGER, intent(in), dimension(ndf_wtheta) :: map_wtheta\n" " INTEGER, intent(in) :: undf_w0, undf_w1, undf_w2, undf_w3, " "undf_wtheta, undf_w2h, undf_w2v\n" " REAL(KIND=r_def), intent(out), dimension(undf_w0) :: " "field_1_w0\n" " REAL(KIND=r_def), intent(out), dimension(undf_w1) :: " "field_2_w1\n" " REAL(KIND=r_def), intent(out), dimension(undf_w2) :: " "field_3_w2\n" " REAL(KIND=r_def), intent(out), dimension(undf_w3) :: " "field_4_w3\n" " REAL(KIND=r_def), intent(out), dimension(undf_wtheta) :: " "field_5_wtheta\n" " REAL(KIND=r_def), intent(out), dimension(undf_w2h) :: " "field_6_w2h\n" " REAL(KIND=r_def), intent(out), dimension(undf_w2v) :: " "field_7_w2v\n" " END SUBROUTINE dummy_code\n" " END MODULE dummy_mod") assert output in generated_code
def test_lfric_stub_boundary_dofs(): '''Check variable usage detection for boundary dofs. ''' ast = get_ast("dynamo0.3", "enforce_bc_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) assert "boundary_dofs_field_1: READ" in str(var_accesses)
def test_kernel_stub_invalid_iteration_space(): ''' Check that we raise an exception if we attempt to generate kernel stub for a kernel with an unsupported iteration space. ''' ast = fpapi.parse(os.path.join(BASE_PATH, "testkern_dofs_mod.f90"), ignore_comments=False) metadata = DynKernMetadata(ast) kernel = DynKern() kernel.load_meta(metadata) with pytest.raises(InternalError) as excinfo: _ = kernel.gen_stub assert ("Expected the kernel to operate on one of " "['cells', 'cell_column'] but found 'dof' in kernel " "'testkern_dofs_code'." in str(excinfo.value))
def test_lfric_stub_indirection_dofmap(): '''Check variable usage detection in indirection dofmap. ''' ast = get_ast("dynamo0.3", "columnwise_op_app_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) assert "cma_indirection_map_aspc1_field_1: READ" in var_info assert "cma_indirection_map_aspc2_field_2: READ" in var_info
def test_lfric_stub_banded_dofmap(): '''Check variable usage detection for banded dofmaps. ''' ast = get_ast("dynamo0.3", "columnwise_op_asm_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) assert "cbanded_map_adspc1_op_1: READ" in var_info assert "cbanded_map_adspc2_op_1: READ" in var_info
def test_load_meta_wrong_type(): ''' Test that the load_meta function raises an appropriate error if the meta-data contains an un-recognised type. ''' fparser.logging.disable(fparser.logging.CRITICAL) ast = fpapi.parse(INTENT, ignore_comments=False) metadata = DynKernMetadata(ast) kernel = DynKern() # Break the meta-data metadata.arg_descriptors[0]._argument_type = "gh_hedge" with pytest.raises(GenerationError) as excinfo: kernel.load_meta(metadata) assert ("DynKern.load_meta() expected one of {0} but found " "'gh_hedge'".format(LFRicArgDescriptor.VALID_ARG_TYPE_NAMES) in str(excinfo.value))
def test_kernstubarglist_arglist_error(): '''Check that we raise an exception if we call the arglist method in kernstubarglist without first calling the generate method''' ast = get_ast(TEST_API, "testkern_one_int_scalar_mod.f90") metadata = DynKernMetadata(ast) kernel = DynKern() kernel.load_meta(metadata) # Now call KernStubArgList to raise an exception create_arg_list = KernStubArgList(kernel) with pytest.raises(InternalError) as excinfo: _ = create_arg_list.arglist assert ("The argument list in KernStubArgList is " "empty. Has the generate() method been " "called?") in str(excinfo.value)
def test_lfric_stub_boundary_dofmap(): '''Check variable usage detection in boundary_dofs array fix for operators. ''' from psyclone.dynamo0p3 import DynKernMetadata, DynKern from psyclone.domain.lfric import KernStubArgList ast = get_ast("dynamo0.3", "enforce_operator_bc_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) assert "boundary_dofs_op_1: READ" in str(var_accesses)
def test_orientation_stubs(): ''' Test that orientation is handled correctly for kernel stubs ''' # Read-in the meta-data from file (it's in a file because it's also # used when testing the genkernelstub script from the command # line). with open(os.path.join(BASE_PATH, "dummy_orientation_mod.f90"), "r") as myfile: orientation = myfile.read() ast = fpapi.parse(orientation, ignore_comments=False) metadata = DynKernMetadata(ast) kernel = DynKern() kernel.load_meta(metadata) generated_code = kernel.gen_stub assert ORIENTATION_OUTPUT in str(generated_code)