def test_prolong_vector(tmpdir, f90, f90flags): ''' Check that we generate correct code when an inter-grid kernel takes a field vector as argument ''' _, invoke_info = parse(os.path.join(BASE_PATH, "22.4_intergrid_prolong_vec.f90"), api=API) psy = PSyFactory(API).create(invoke_info) output = str(psy.gen) print output if utils.TEST_COMPILE: assert utils.code_compiles(API, psy, tmpdir, f90, f90flags) assert "TYPE(field_type), intent(inout) :: field1(3)" in output assert "TYPE(field_proxy_type) field1_proxy(3)" in output # Make sure we always index into the field arrays assert " field1%" not in output assert " field2%" not in output assert ("ncpc_field1_field2, ncell_field1, field1_proxy(1)%data, " "field1_proxy(2)%data, field1_proxy(3)%data, field2_proxy(1)%data," " field2_proxy(2)%data, field2_proxy(3)%data, ndf_w1" in output) for idx in [1, 2, 3]: assert ( " IF (field2_proxy({0})%is_dirty(depth=1)) THEN\n" " CALL field2_proxy({0})%halo_exchange(depth=1)\n" " END IF \n".format(idx) in output) assert ("field1_proxy({0}) = field1({0})%get_proxy()".format(idx) in output) assert "CALL field1_proxy({0})%set_dirty()".format(idx) in output assert "CALL field1_proxy({0})%set_clean(1)".format(idx) in output
def test_field_qr_deref(tmpdir, f90, f90flags): ''' Tests that a call, with a set of fields requiring quadrature, produces correct code when the quadrature is supplied as the component of a derived type. ''' _, invoke_info = parse(os.path.join(BASE_PATH, "1.1.1_single_invoke_qr_deref.f90"), api="dynamo0.3") for dist_mem in [True, False]: psy = PSyFactory("dynamo0.3", distributed_memory=dist_mem).create(invoke_info) if utils.TEST_COMPILE: assert utils.code_compiles(API, psy, tmpdir, f90, f90flags) gen = str(psy.gen) print gen assert ( " SUBROUTINE invoke_0_testkern_qr_type(f1, f2, m1, a, m2, istp," " qr_data)\n" in gen) assert "TYPE(quadrature_xyoz_type), intent(in) :: qr_data" in gen
def code_compiles_invalid_api(tmpdir, f90, f90flags): ''' Check that utils.code_compiles() reject an unrecognised API ''' with pytest.raises(utils.CompileError) as excinfo: utils.code_compiles("not_an_api", "fake_psy", tmpdir, f90, f90flags) assert "Unsupported API in " in str(excinfo)
def test_field_xyoz(tmpdir, f90, f90flags): ''' Tests that a call, with a set of fields requiring XYoZ quadrature, produces correct code. ''' _, invoke_info = parse(os.path.join(BASE_PATH, "1.1.0_single_invoke_xyoz_qr.f90"), api=API) psy = PSyFactory(API).create(invoke_info) generated_code = str(psy.gen) print generated_code if utils.TEST_COMPILE: assert utils.code_compiles(API, psy, tmpdir, f90, f90flags) output_decls = ( " SUBROUTINE invoke_0_testkern_qr_type(f1, f2, m1, a, m2, istp," " qr)\n" " USE testkern_qr, ONLY: testkern_qr_code\n" " USE quadrature_xyoz_mod, ONLY: quadrature_xyoz_type, " "quadrature_xyoz_proxy_type\n" " USE function_space_mod, ONLY: BASIS, DIFF_BASIS\n" " USE mesh_mod, ONLY: mesh_type\n" " REAL(KIND=r_def), intent(in) :: a\n" " INTEGER, intent(in) :: istp\n" " TYPE(field_type), intent(inout) :: f1\n" " TYPE(field_type), intent(in) :: f2, m1, m2\n" " TYPE(quadrature_xyoz_type), intent(in) :: qr\n" " INTEGER cell\n" " REAL(KIND=r_def), allocatable :: basis_w1_qr(:,:,:,:), " "basis_w3_qr(:,:,:,:), diff_basis_w2_qr(:,:,:,:), " "diff_basis_w3_qr(:,:,:,:)\n" " INTEGER dim_w1, dim_w3, diff_dim_w2, diff_dim_w3\n" " REAL(KIND=r_def), pointer :: weights_xy_qr(:) => null(), " "weights_z_qr(:) => null()\n" " INTEGER np_xy_qr, np_z_qr\n" " INTEGER ndf_w1, undf_w1, ndf_w2, undf_w2, ndf_w3, undf_w3\n" " INTEGER nlayers\n" " TYPE(field_proxy_type) f1_proxy, f2_proxy, m1_proxy, m2_proxy\n" " TYPE(quadrature_xyoz_proxy_type) qr_proxy\n" " INTEGER, pointer :: map_w2(:,:) => null(), " "map_w3(:,:) => null(), map_w1(:,:) => null()\n" " TYPE(mesh_type), pointer :: mesh => null()\n") assert output_decls in generated_code init_output = (" !\n" " ! Initialise field and/or operator proxies\n" " !\n" " f1_proxy = f1%get_proxy()\n" " f2_proxy = f2%get_proxy()\n" " m1_proxy = m1%get_proxy()\n" " m2_proxy = m2%get_proxy()\n" " !\n" " ! Initialise number of layers\n" " !\n" " nlayers = f1_proxy%vspace%get_nlayers()\n" " !\n" " ! Create a mesh object\n" " !\n" " mesh => f1%get_mesh()\n" " !\n" " ! Look-up dofmaps for each function space\n" " !\n" " map_w2 => f2_proxy%vspace%get_whole_dofmap()\n" " map_w3 => m2_proxy%vspace%get_whole_dofmap()\n" " map_w1 => f1_proxy%vspace%get_whole_dofmap()\n" " !\n" " ! Initialise number of DoFs for w1\n" " !\n" " ndf_w1 = f1_proxy%vspace%get_ndf()\n" " undf_w1 = f1_proxy%vspace%get_undf()\n" " !\n" " ! Initialise number of DoFs for w2\n" " !\n" " ndf_w2 = f2_proxy%vspace%get_ndf()\n" " undf_w2 = f2_proxy%vspace%get_undf()\n" " !\n" " ! Initialise number of DoFs for w3\n" " !\n" " ndf_w3 = m2_proxy%vspace%get_ndf()\n" " undf_w3 = m2_proxy%vspace%get_undf()\n" " !\n" " ! Look-up quadrature variables\n" " !\n" " qr_proxy = qr%get_quadrature_proxy()\n" " np_xy_qr = qr_proxy%np_xy\n" " np_z_qr = qr_proxy%np_z\n" " weights_xy_qr => qr_proxy%weights_xy\n" " weights_z_qr => qr_proxy%weights_z\n") assert init_output in generated_code compute_output = ( " !\n" " ! Allocate basis arrays\n" " !\n" " dim_w1 = f1_proxy%vspace%get_dim_space()\n" " ALLOCATE (basis_w1_qr(dim_w1, ndf_w1, np_xy_qr, np_z_qr))\n" " dim_w3 = m2_proxy%vspace%get_dim_space()\n" " ALLOCATE (basis_w3_qr(dim_w3, ndf_w3, np_xy_qr, np_z_qr))\n" " !\n" " ! Allocate differential basis arrays\n" " !\n" " diff_dim_w2 = f2_proxy%vspace%get_dim_space_diff()\n" " ALLOCATE (diff_basis_w2_qr(diff_dim_w2, ndf_w2, np_xy_qr, " "np_z_qr))\n" " diff_dim_w3 = m2_proxy%vspace%get_dim_space_diff()\n" " ALLOCATE (diff_basis_w3_qr(diff_dim_w3, ndf_w3, np_xy_qr, " "np_z_qr))\n" " !\n" " ! Compute basis arrays\n" " !\n" " CALL qr%compute_function(BASIS, f1_proxy%vspace, dim_w1, " "ndf_w1, basis_w1_qr)\n" " CALL qr%compute_function(BASIS, m2_proxy%vspace, dim_w3, " "ndf_w3, basis_w3_qr)\n" " !\n" " ! Compute differential basis arrays\n" " !\n" " CALL qr%compute_function(DIFF_BASIS, f2_proxy%vspace, " "diff_dim_w2, ndf_w2, diff_basis_w2_qr)\n" " CALL qr%compute_function(DIFF_BASIS, m2_proxy%vspace, " "diff_dim_w3, ndf_w3, diff_basis_w3_qr)\n" " !\n" " ! Call kernels and communication routines\n" " !\n" " IF (f2_proxy%is_dirty(depth=1)) THEN\n" " CALL f2_proxy%halo_exchange(depth=1)\n" " END IF \n" " !\n" " IF (m1_proxy%is_dirty(depth=1)) THEN\n" " CALL m1_proxy%halo_exchange(depth=1)\n" " END IF \n" " !\n" " IF (m2_proxy%is_dirty(depth=1)) THEN\n" " CALL m2_proxy%halo_exchange(depth=1)\n" " END IF \n" " !\n" " DO cell=1,mesh%get_last_halo_cell(1)\n" " !\n" " CALL testkern_qr_code(nlayers, f1_proxy%data, f2_proxy%data, " "m1_proxy%data, a, m2_proxy%data, istp, ndf_w1, undf_w1, " "map_w1(:,cell), basis_w1_qr, ndf_w2, undf_w2, map_w2(:,cell), " "diff_basis_w2_qr, ndf_w3, undf_w3, map_w3(:,cell), basis_w3_qr, " "diff_basis_w3_qr, np_xy_qr, np_z_qr, weights_xy_qr, weights_z_qr)\n" " END DO \n" " !\n" " ! Set halos dirty/clean for fields modified in the above loop\n" " !\n" " CALL f1_proxy%set_dirty()\n" " !\n" " !\n" " ! Deallocate basis arrays\n" " !\n" " DEALLOCATE (diff_basis_w2_qr, basis_w1_qr, basis_w3_qr, " "diff_basis_w3_qr)\n" " !\n" " END SUBROUTINE invoke_0_testkern_qr_type") assert compute_output in generated_code
def test_restrict_prolong_chain(tmpdir, f90, f90flags): ''' Test when we have a single invoke containing a chain of restrictions and prolongations ''' _, invoke_info = parse(os.path.join(BASE_PATH, "22.2_intergrid_3levels.f90"), api=API) for distmem in [False, True]: psy = PSyFactory(API, distributed_memory=distmem).create(invoke_info) output = str(psy.gen) if utils.TEST_COMPILE: assert utils.code_compiles(API, psy, tmpdir, f90, f90flags) expected = ( " ! Look-up mesh objects and loop limits for inter-grid " "kernels\n" " !\n" " mesh_fld_m => fld_m%get_mesh()\n" " mesh_fld_c => fld_c%get_mesh()\n" " mmap_fld_m_fld_c => mesh_fld_c%get_mesh_map(mesh_fld_m)\n" " cell_map_fld_c => mmap_fld_m_fld_c%get_whole_cell_map()\n") assert expected in output if distmem: expected = ( " ncell_fld_m = mesh_fld_m%get_last_halo_cell(depth=2)\n" " ncpc_fld_m_fld_c = mmap_fld_m_fld_c%" "get_ntarget_cells_per_source_cell()\n" " mesh_fld_f => fld_f%get_mesh()\n" " mmap_fld_f_fld_m => mesh_fld_m%get_mesh_map" "(mesh_fld_f)\n" " cell_map_fld_m => mmap_fld_f_fld_m%get_whole_cell_map" "()\n" " ncell_fld_f = mesh_fld_f%get_last_halo_cell(depth=2)\n" " ncpc_fld_f_fld_m = mmap_fld_f_fld_m%" "get_ntarget_cells_per_source_cell()\n") else: expected = ( " ncell_fld_m = fld_m_proxy%vspace%get_ncell()\n" " ncpc_fld_m_fld_c = mmap_fld_m_fld_c%" "get_ntarget_cells_per_source_cell()\n" " mesh_fld_f => fld_f%get_mesh()\n" " mmap_fld_f_fld_m => mesh_fld_m%get_mesh_map" "(mesh_fld_f)\n" " cell_map_fld_m => mmap_fld_f_fld_m%get_whole_cell_map" "()\n" " ncell_fld_f = fld_f_proxy%vspace%get_ncell()\n" " ncpc_fld_f_fld_m = mmap_fld_f_fld_m%get_ntarget_cells_" "per_source_cell()\n") assert expected in output # Check that we haven't got duplicated output assert output.count("mesh_fld_m => fld_m%get_mesh") == 1 assert output.count("ncell_fld_m = ") == 1 assert output.count("ncell_fld_f = ") == 1 if distmem: # Have a potential halo exchange before 1st prolong expected = ( " IF (fld_c_proxy%is_dirty(depth=1)) THEN\n" " CALL fld_c_proxy%halo_exchange(depth=1)\n" " END IF \n" " !\n" " DO cell=1,mesh_fld_c%get_last_halo_cell(1)\n") assert expected in output # Since we loop into L1 halo of the coarse mesh, the L1 halo # of the fine(r) mesh will now be clean. Therefore, no halo # swap before the next prolongation expected = ( " ! Set halos dirty/clean for fields modified in the " "above loop\n" " !\n" " CALL fld_m_proxy%set_dirty()\n" " CALL fld_m_proxy%set_clean(1)\n" " !\n" " DO cell=1,mesh_fld_m%get_last_halo_cell(1)\n") assert expected in output # Again the L1 halo for fld_f will now be clean but for restriction # we need the L2 halo to be clean. There's a set_clean(1) for # fld_f because the above loop over the coarser fld_m will go # into the L2 halo of fld_f. However, it is a continuous field # so only the L1 halo will actually be clean. expected = (" CALL fld_f_proxy%set_dirty()\n" " CALL fld_f_proxy%set_clean(1)\n" " !\n" " CALL fld_f_proxy%halo_exchange(depth=2)\n" " !\n" " DO cell=1,mesh_fld_m%get_last_halo_cell(1)\n" " !\n" " CALL restrict_kernel_code") assert expected in output # For the final restriction we need the L2 halo of fld_m to be # clean. There's no set_clean() call on fld_m because it is # only updated out to the L1 halo and it is a continuous field # so the shared dofs in the L1 halo will still be dirty. expected = (" CALL fld_m_proxy%set_dirty()\n" " !\n" " CALL fld_m_proxy%halo_exchange(depth=2)\n" " !\n" " DO cell=1,mesh_fld_c%get_last_halo_cell(1)\n" " !\n" " CALL restrict_kernel_code") assert expected in output else: expected = ( " DO cell=1,fld_c_proxy%vspace%get_ncell()\n" " !\n" " CALL prolong_kernel_code(nlayers, cell_map_fld_c(:," "cell), ncpc_fld_m_fld_c, ncell_fld_m, fld_m_proxy%data, " "fld_c_proxy%data, ndf_w1, undf_w1, map_w1, undf_w2, " "map_w2(:,cell))\n" " END DO \n" " DO cell=1,fld_m_proxy%vspace%get_ncell()\n" " !\n" " CALL prolong_kernel_code(nlayers, cell_map_fld_m(:," "cell), ncpc_fld_f_fld_m, ncell_fld_f, fld_f_proxy%data, " "fld_m_proxy%data, ndf_w1, undf_w1, map_w1, undf_w2, " "map_w2(:,cell))\n" " END DO \n" " DO cell=1,fld_m_proxy%vspace%get_ncell()\n" " !\n" " CALL restrict_kernel_code(nlayers, cell_map_fld_m(:," "cell), ncpc_fld_f_fld_m, ncell_fld_f, fld_m_proxy%data, " "fld_f_proxy%data, undf_any_space_1_fld_m, " "map_any_space_1_fld_m(:,cell), ndf_any_space_2_fld_f, " "undf_any_space_2_fld_f, map_any_space_2_fld_f)\n" " END DO \n" " DO cell=1,fld_c_proxy%vspace%get_ncell()\n" " !\n" " CALL restrict_kernel_code(nlayers, cell_map_fld_c(:," "cell), ncpc_fld_m_fld_c, ncell_fld_m, fld_c_proxy%data, " "fld_m_proxy%data, undf_any_space_1_fld_c, " "map_any_space_1_fld_c(:,cell), ndf_any_space_2_fld_m, " "undf_any_space_2_fld_m, map_any_space_2_fld_m)\n") assert expected in output
def test_field_restrict(tmpdir, f90, f90flags): ''' Test that we generate correct code for an invoke containing a single restriction operation (read from find, write to coarse) ''' _, invoke_info = parse(os.path.join(BASE_PATH, "22.1_intergrid_restrict.f90"), api=API) for distmem in [False, True]: psy = PSyFactory(API, distributed_memory=distmem).create(invoke_info) output = str(psy.gen) print output if utils.TEST_COMPILE: assert utils.code_compiles(API, psy, tmpdir, f90, f90flags) defs = ( " USE restrict_kernel_mod, ONLY: restrict_kernel_code\n" " USE mesh_map_mod, ONLY: mesh_map_type\n" " USE mesh_mod, ONLY: mesh_type\n" " TYPE(field_type), intent(inout) :: field1\n" " TYPE(field_type), intent(in) :: field2\n") assert defs in output defs2 = ( " INTEGER nlayers\n" " TYPE(field_proxy_type) field1_proxy, field2_proxy\n" " INTEGER, pointer :: map_any_space_2_field2(:,:) => null(), " "map_any_space_1_field1(:,:) => null()\n" " INTEGER ncell_field2, ncpc_field2_field1\n" " INTEGER, pointer :: cell_map_field1(:,:) => null()\n" " TYPE(mesh_map_type), pointer :: mmap_field2_field1 => " "null()\n" " TYPE(mesh_type), pointer :: mesh_field2 => null()\n" " TYPE(mesh_type), pointer :: mesh_field1 => null()\n") assert defs2 in output inits = ( " !\n" " ! Look-up mesh objects and loop limits for inter-grid " "kernels\n" " !\n" " mesh_field2 => field2%get_mesh()\n" " mesh_field1 => field1%get_mesh()\n" " mmap_field2_field1 => mesh_field1%get_mesh_map(" "mesh_field2)\n" " cell_map_field1 => mmap_field2_field1%" "get_whole_cell_map()\n") if distmem: inits += (" ncell_field2 = mesh_field2%" "get_last_halo_cell(depth=2)\n") else: inits += (" ncell_field2 = field2_proxy%vspace%" "get_ncell()\n") inits += ( " ncpc_field2_field1 = mmap_field2_field1%" "get_ntarget_cells_per_source_cell()\n" " !\n" " ! Look-up dofmaps for each function space\n" " !\n" " map_any_space_2_field2 => field2_proxy%vspace%" "get_whole_dofmap()\n" " map_any_space_1_field1 => field1_proxy%vspace%" "get_whole_dofmap()\n") assert inits in output if distmem: # We write out to the L1 halo on the coarse mesh which means # we require up-to-date values out to the L2 halo on the fine. # Since we are incrementing the coarse field we also need # up-to-date values for it in the L1 halo. halo_exchs = ( " ! Call kernels and communication routines\n" " !\n" " IF (field1_proxy%is_dirty(depth=1)) THEN\n" " CALL field1_proxy%halo_exchange(depth=1)\n" " END IF \n" " !\n" " IF (field2_proxy%is_dirty(depth=2)) THEN\n" " CALL field2_proxy%halo_exchange(depth=2)\n" " END IF \n" " !\n" " DO cell=1,mesh_field1%get_last_halo_cell(1)\n") assert halo_exchs in output # We pass the whole dofmap for the fine mesh (we are reading from). # This is associated with the second kernel argument. kern_call = ( " !\n" " CALL restrict_kernel_code(nlayers, " "cell_map_field1(:,cell), ncpc_field2_field1, ncell_field2, " "field1_proxy%data, field2_proxy%data, " "undf_any_space_1_field1, map_any_space_1_field1(:,cell), " "ndf_any_space_2_field2, undf_any_space_2_field2, " "map_any_space_2_field2)\n" " END DO \n" " !\n") assert kern_call in output if distmem: set_dirty = " CALL field1_proxy%set_dirty()\n" assert set_dirty in output
def test_field_prolong(tmpdir, f90, f90flags): ''' Check that we generate correct psy-layer code for an invoke containing a kernel that performs a prolongation operation ''' _, invoke_info = parse(os.path.join(BASE_PATH, "22.0_intergrid_prolong.f90"), api=API) for distmem in [False, True]: psy = PSyFactory(API, distributed_memory=distmem).create(invoke_info) gen_code = str(psy.gen) if utils.TEST_COMPILE: assert utils.code_compiles(API, psy, tmpdir, f90, f90flags) expected = ( " USE prolong_kernel_mod, ONLY: prolong_kernel_code\n" " USE mesh_map_mod, ONLY: mesh_map_type\n" " USE mesh_mod, ONLY: mesh_type\n" " TYPE(field_type), intent(inout) :: field1\n" " TYPE(field_type), intent(in) :: field2\n" " INTEGER cell\n") assert expected in gen_code expected = ( " INTEGER ncell_field1, ncpc_field1_field2\n" " INTEGER, pointer :: cell_map_field2(:,:) => null()\n" " TYPE(mesh_map_type), pointer :: " "mmap_field1_field2 => null()\n" " TYPE(mesh_type), pointer :: mesh_field2 => null()\n" " TYPE(mesh_type), pointer :: mesh_field1 => null()\n") assert expected in gen_code expected = ( " ! Look-up mesh objects and loop limits for inter-grid " "kernels\n" " !\n" " mesh_field1 => field1%get_mesh()\n" " mesh_field2 => field2%get_mesh()\n" " mmap_field1_field2 => mesh_field2%get_mesh_map" "(mesh_field1)\n" " cell_map_field2 => mmap_field1_field2%" "get_whole_cell_map()\n") if distmem: expected += ( " ncell_field1 = mesh_field1%get_last_halo_cell(" "depth=2)\n") else: expected += \ " ncell_field1 = field1_proxy%vspace%get_ncell()\n" expected += ( " ncpc_field1_field2 = mmap_field1_field2%" "get_ntarget_cells_per_source_cell()\n") assert expected in gen_code if distmem: # We are writing to a continuous field on the fine mesh, we # only need to halo swap to depth one on the coarse. expected = ( " IF (field2_proxy%is_dirty(depth=1)) THEN\n" " CALL field2_proxy%halo_exchange(depth=1)\n" " END IF \n" " !\n" " DO cell=1,mesh_field2%get_last_halo_cell(1)\n") assert expected in gen_code else: assert "DO cell=1,field2_proxy%vspace%get_ncell()\n" in gen_code expected = ( " CALL prolong_kernel_code(nlayers, " "cell_map_field2(:,cell), ncpc_field1_field2, ncell_field1, " "field1_proxy%data, field2_proxy%data, ndf_w1, undf_w1, map_w1, " "undf_w2, map_w2(:,cell))\n" " END DO \n") assert expected in gen_code if distmem: set_dirty = " CALL field1_proxy%set_dirty()\n" assert set_dirty in gen_code