Exemplo n.º 1
0
def test_implicit_loop_different_rank():
    ''' Test that we reject implicit loops if the index positions of the
    colons differs. This is a restriction that could be lifted by
    using e.g. SIZE(zvab, 1) as the upper loop limit or (with a lot more
    work) by interrogating the parsed code to figure out the loop bound. '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "array_section_index_mismatch.f90"),
                           api=API,
                           line_length=False)
    psy = PSyFactory(API).create(invoke_info)
    sched = psy.invokes.invoke_list[0].schedule
    loop = sched.children[1]
    trans = TransInfo().get_trans_name('NemoExplicitLoopTrans')
    with pytest.raises(TransformationError) as err:
        _ = trans.apply(loop)
    assert ("implicit loops are restricted to cases where all array "
            "range specifications occur" in str(err))
    loop = sched.children[2]
    with pytest.raises(InternalError) as err:
        _ = trans.apply(loop)
    assert ("Expecting a colon for index 3 but array only has 2 "
            "dimensions: zab(" in str(err))
Exemplo n.º 2
0
def test_omp_parallel_errs():
    ''' Check that we raise the expected errors when incorrectly attempting
    to add an OpenMP parallel region containing more than one node. '''
    from psyclone.transformations import OMPParallelTrans
    otrans = OMPParallelTrans()
    _, invoke_info = parse(os.path.join(BASE_PATH, "imperfect_nest.f90"),
                           api=API,
                           line_length=False)
    psy = PSyFactory(API, distributed_memory=False).create(invoke_info)
    schedule = psy.invokes.get('imperfect_nest').schedule
    schedule.view()
    # Apply the OMP Parallel transformation so as to enclose the last two
    # loop nests (Python's slice notation is such that the expression below
    # gives elements 2-3).
    new_sched, _ = otrans.apply(schedule.children[0].loop_body[2:4])
    directive = new_sched.children[0].loop_body[2]
    # Break the AST by deleting some of it
    _ = new_sched.children[0].ast.content.remove(directive.children[0].ast)
    with pytest.raises(InternalError) as err:
        _ = psy.gen
    assert ("Failed to find locations to insert begin/end directives"
            in str(err))
Exemplo n.º 3
0
def test_operator_read_level1_halo(tmpdir):
    ''' Check that we raise an error if a kernel attempts to read from an
    operator beyond the level-1 halo. '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "10.7_operator_read.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info)

    assert LFRicBuild(tmpdir).code_compiles(psy)

    schedule = psy.invokes.invoke_list[0].schedule
    loop = schedule.children[0]
    # Modify the loop bound so that we attempt to read from the L2 halo
    # (of the operator)
    loop.set_upper_bound("cell_halo", index=2)
    # Attempt to generate the code
    with pytest.raises(GenerationError) as excinfo:
        _ = psy.gen
    assert ("Kernel 'testkern_operator_read_code' reads from an operator and "
            "therefore cannot be used for cells beyond the level 1 halo. "
            "However the containing loop goes out to level 2"
            in str(excinfo.value))
Exemplo n.º 4
0
def test_operator_deref(tmpdir, dist_mem):
    ''' Tests that we generate correct names for an operator in the PSy
    layer when obtained by de-referencing a derived type in the Algorithm
    layer '''
    _, invoke_info = parse(os.path.join(BASE_PATH, "10.8_operator_deref.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=dist_mem).create(invoke_info)
    generated_code = str(psy.gen)

    assert Dynamo0p3Build(tmpdir).code_compiles(psy)

    assert ("SUBROUTINE invoke_0_testkern_operator_type(mm_w0_op, chi, a, qr)"
            in generated_code)
    assert "TYPE(operator_type), intent(inout) :: mm_w0_op" in generated_code
    assert "TYPE(operator_proxy_type) mm_w0_op_proxy" in generated_code
    assert "mm_w0_op_proxy = mm_w0_op%get_proxy()" in generated_code
    assert ("CALL testkern_operator_code(cell, nlayers, "
            "mm_w0_op_proxy%ncell_3d, mm_w0_op_proxy%local_stencil, "
            "chi_proxy(1)%data, chi_proxy(2)%data, chi_proxy(3)%data, a, "
            "ndf_w0, undf_w0, map_w0(:,cell), basis_w0_qr, "
            "diff_basis_w0_qr, np_xy_qr, np_z_qr, weights_xy_qr, "
            "weights_z_qr)" in generated_code)
Exemplo n.º 5
0
def test_node_args():
    '''Test that the Node class args method returns the correct arguments
    for Nodes that do not have arguments themselves'''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "4_multikernel_invokes.f90"),
                           api="dynamo0.3")
    psy = PSyFactory("dynamo0.3", distributed_memory=False).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    schedule = invoke.schedule
    loop1 = schedule.children[0]
    kern1 = loop1.loop_body[0]
    loop2 = schedule.children[1]
    kern2 = loop2.loop_body[0]
    # 1) Schedule (not that this is useful)
    all_args = kern1.arguments.args
    all_args.extend(kern2.arguments.args)
    schedule_args = schedule.args
    for idx, arg in enumerate(all_args):
        assert arg == schedule_args[idx]
    # 2) Loop1
    loop1_args = loop1.args
    for idx, arg in enumerate(kern1.arguments.args):
        assert arg == loop1_args[idx]
    # 3) Loop2
    loop2_args = loop2.args
    for idx, arg in enumerate(kern2.arguments.args):
        assert arg == loop2_args[idx]
    # 4) Loop fuse
    ftrans = DynamoLoopFuseTrans()
    ftrans.same_space = True
    schedule, _ = ftrans.apply(schedule.children[0], schedule.children[1])
    loop = schedule.children[0]
    kern1 = loop.loop_body[0]
    kern2 = loop.loop_body[1]
    loop_args = loop.args
    kern_args = kern1.arguments.args
    kern_args.extend(kern2.arguments.args)
    for idx, arg in enumerate(kern_args):
        assert arg == loop_args[idx]
Exemplo n.º 6
0
def test_stencil_information(tmpdir):
    '''Test that the GOStencil class provides the expected stencil
    information. This exercises the "pointwise" name and the stencil
    description

    '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "test28_invoke_kernel_stencil.f90"),
                           api=API)
    psy = PSyFactory(API).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    schedule = invoke.schedule
    kernel = schedule.children[0].loop_body[0].loop_body[0]

    # args 1 and 3 specify pointwise as a stencil access
    for idx in [0, 2]:
        pointwise_arg = kernel.args[idx]
        assert pointwise_arg.stencil
        assert not pointwise_arg.stencil.has_stencil
        assert pointwise_arg.stencil.name == "go_pointwise"

    # arg 4 provides grid information so knows nothing about stencils
    grid_arg = kernel.args[3]
    with pytest.raises(AttributeError) as excinfo:
        _ = grid_arg.stencil
    assert "object has no attribute 'stencil'" in str(excinfo.value)

    # arg 2 has a stencil
    stencil_arg = kernel.args[1]
    assert stencil_arg.stencil.has_stencil
    for idx2 in range(-1, 2):
        for idx1 in range(-1, 2):
            if idx1 in [0, 1] and idx2 == 0:
                expected_depth = 1
            else:
                expected_depth = 0
            assert stencil_arg.stencil.depth(idx1, idx2) == expected_depth

    assert GOcean1p0Build(tmpdir).code_compiles(psy)
Exemplo n.º 7
0
def test_itn_space_any_any_discontinuous(dist_mem, tmpdir):
    ''' Check that generated loop over cells has correct upper
    bound when a kernel writes to fields on any_space (continuous)
    and any_discontinuous_space.

    '''
    _, invoke_info = parse(os.path.join(
        BASE_PATH, "1.5.3_single_invoke_write_any_anyd_space.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=dist_mem).create(invoke_info)
    generated_code = str(psy.gen)

    assert LFRicBuild(tmpdir).code_compiles(psy)

    if dist_mem:
        output = ("      !\n" "      DO cell=1,mesh%get_last_halo_cell(1)\n")
        assert output in generated_code
    else:
        output = ("      ! Call our kernels\n"
                  "      !\n"
                  "      DO cell=1,f1_proxy%vspace%get_ncell()\n")
        assert output in generated_code
Exemplo n.º 8
0
def test_no_data_ref_read(parser):
    ''' Check that we reject code that reads from a derived type. This
    limitation will be addressed in #1028. '''
    reader = FortranStringReader("program dtype_read\n"
                                 "use field_mod, only: fld_type\n"
                                 "real(kind=wp) :: sto_tmp(5)\n"
                                 "integer :: ji\n"
                                 "integer, parameter :: jpj = 10\n"
                                 "type(fld_type) :: fld\n"
                                 "do ji = 1,jpj\n"
                                 "sto_tmp(ji) = fld%data(ji) + 1._wp\n"
                                 "end do\n"
                                 "end program dtype_read\n")
    code = parser(reader)
    psy = PSyFactory(API, distributed_memory=False).create(code)
    schedule = psy.invokes.invoke_list[0].schedule
    acc_trans = TransInfo().get_trans_name('ACCDataTrans')
    acc_trans.apply(schedule.children)
    with pytest.raises(NotImplementedError) as err:
        _ = str(psy.gen)
    assert ("derived-type references on the RHS of assignments are not yet "
            "supported" in str(err.value))
Exemplo n.º 9
0
def test_no_copyin_intrinsics(parser):
    ''' Check that we don't generate a copyin/out for Fortran instrinsic
    functions (i.e. we don't mistake them for array accesses). '''
    acc_trans = TransInfo().get_trans_name('ACCDataTrans')
    for intrinsic in [
            "cos(ji)", "sin(ji)", "tan(ji)", "atan(ji)", "mod(ji, 5)"
    ]:
        reader = FortranStringReader(
            "program call_intrinsic\n"
            "integer :: ji, jpj\n"
            "real(kind=wp) :: sto_tmp(5)\n"
            "do ji = 1,jpj\n"
            "sto_tmp(ji) = {0}\n"
            "end do\n"
            "end program call_intrinsic\n".format(intrinsic))
        code = parser(reader)
        psy = PSyFactory(API, distributed_memory=False).create(code)
        schedule = psy.invokes.invoke_list[0].schedule
        acc_trans.apply(schedule.children[0:1])
        gen_code = str(psy.gen)
        idx = intrinsic.index("(")
        assert "copyin({0})".format(intrinsic[0:idx]) not in gen_code.lower()
Exemplo n.º 10
0
def test_no_kernels_error(parser):
    ''' Check that the transformation rejects an attempt to put things
    that aren't kernels inside a kernels region. '''
    reader = FortranStringReader("program write_out\n"
                                 "integer :: ji, jpj\n"
                                 "real(kind=wp) :: sto_tmp(5)\n"
                                 "do ji = 1,jpj\n"
                                 "read(*,*) sto_tmp(ji)\n"
                                 "end do\n"
                                 "do ji = 1,jpj\n"
                                 "write(*,*) sto_tmp(ji)\n"
                                 "end do\n"
                                 "end program write_out\n")
    code = parser(reader)
    psy = PSyFactory(API, distributed_memory=False).create(code)
    schedule = psy.invokes.invoke_list[0].schedule
    acc_trans = ACCKernelsTrans()
    with pytest.raises(TransformationError) as err:
        _, _ = acc_trans.apply(schedule.children[0:2],
                               {"default_present": True})
    assert ("cannot be enclosed by a ACCKernelsTrans transformation"
            in str(err.value))
Exemplo n.º 11
0
def test_kern_load_errors(monkeypatch):
    ''' Check that the various load methods of the NemoKern class raise
    the expected errors. '''
    _, invoke_info = parse(os.path.join(BASE_PATH, "explicit_do.f90"),
                           api=API,
                           line_length=False)
    psy = PSyFactory(API, distributed_memory=False).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    sched = invoke.schedule
    # The schedule should contain 3 loop objects
    kerns = sched.kern_calls()
    with pytest.raises(InternalError) as err:
        kerns[0].load("Not an fparser2 AST node")
    assert ("internal error: Expecting either Block_Nonlabel_Do_Construct "
            "or Assignment_Stmt but got " in str(err))
    # TODO why haven't the Kernel or Loop objects got a valid _ast?
    loop = sched.children[0].children[0].children[0]._ast
    monkeypatch.setattr(loop, "content", ["not_a_loop"])
    with pytest.raises(InternalError) as err:
        kerns[0]._load_from_loop(loop)
    assert ("Expecting Nonlabel_Do_Stmt as first child of "
            "Block_Nonlabel_Do_Construct but got" in str(err))
Exemplo n.º 12
0
def test_parallel_repeat_update(parser):
    ''' Check that calling ACCParallelDirective.update() a 2nd time
    does not alter the fparser2 parse tree. '''
    reader = FortranStringReader(SINGLE_LOOP)
    code = parser(reader)
    psy = PSyFactory(API, distributed_memory=False).create(code)
    schedule = psy.invokes.invoke_list[0].schedule
    data_trans = TransInfo().get_trans_name('ACCDataTrans')
    acc_trans = TransInfo().get_trans_name('ACCParallelTrans')
    acc_trans.apply(schedule.children[0:1])
    data_trans.apply(schedule[0])
    accdir = schedule[0].dir_body[0]
    assert isinstance(accdir, ACCParallelDirective)
    assert accdir._ast is None
    # Generate the code in order to trigger the update of the fparser2 tree
    _ = str(psy.gen)
    # Store the content of a part of the fparser2 parse tree
    orig_content = accdir._ast.parent.content[:]
    # Call update() a second time and then check that nothing has changed
    accdir.update()
    for idx, item in enumerate(orig_content):
        assert item is accdir._ast.parent.content[idx]
Exemplo n.º 13
0
def test_user_defined_variables(parser):
    ''' Test reading and writing to user defined variables. This is
    not supported atm because the dependence analysis for these PSyIR
    nodes has not yet been implemented (#1028).

    Also TODO #1028: is this a duplicate of test_derived_type in
    tests/psyir/dependency_tools_test.py?
    '''
    reader = FortranStringReader('''program test_prog
                                       use some_mod, only: my_type
                                       type(my_type) :: a, e
                                       integer :: ji, jj, d
                                       a%b%c(ji, jj) = d
                                       e%f = d
                                    end program test_prog''')
    prog = parser(reader)
    psy = PSyFactory("nemo", distributed_memory=False).create(prog)
    loops = psy.invokes.get("test_prog").schedule

    var_accesses = VariablesAccessInfo(loops)
    assert var_accesses[Signature("a % b % c")].is_written
    assert var_accesses[Signature("e % f")].is_written
Exemplo n.º 14
0
def test_dynbasisfns_compute(monkeypatch):
    ''' Check that the DynBasisFunctions._compute_basis_fns() method
    raises the expected InternalErrors if an unrecognised type or shape of
    basis function is encountered. '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "1.1.0_single_invoke_xyoz_qr.f90"),
                           api=API)
    psy = PSyFactory(API, distributed_memory=False).create(invoke_info)
    dinf = DynBasisFunctions(psy.invokes.invoke_list[0])
    mod = ModuleGen(name="testmodule")
    # First supply an invalid shape for one of the basis functions
    dinf._basis_fns[0]["shape"] = "not-a-shape"
    with pytest.raises(InternalError) as err:
        dinf._compute_basis_fns(mod)
    assert ("Unrecognised shape 'not-a-shape' specified for basis function. "
            "Should be one of: ['gh_quadrature_xyoz', " in str(err.value))
    # Now supply an invalid type for one of the basis functions
    monkeypatch.setattr(dinf, "_basis_fns", [{'type': 'not-a-type'}])
    with pytest.raises(InternalError) as err:
        dinf._compute_basis_fns(mod)
    assert ("Unrecognised type of basis function: 'not-a-type'. Expected "
            "one of 'basis' or 'diff-basis'" in str(err.value))
Exemplo n.º 15
0
def test_single_node_ompparalleldo_gocean1p0():
    ''' Test that applying Extract Transformation on a Node enclosed
    within an OMP Parallel DO Directive produces the correct result
    in GOcean1.0 API. '''
    from psyclone.transformations import GOceanOMPParallelLoopTrans

    etrans = GOceanExtractRegionTrans()
    otrans = GOceanOMPParallelLoopTrans()

    # Test a Loop nested within the OMP Parallel DO Directive
    _, invoke_info = parse(os.path.join(GOCEAN_BASE_PATH,
                                        "single_invoke_three_kernels.f90"),
                           api=GOCEAN_API)
    psy = PSyFactory(GOCEAN_API, distributed_memory=False).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    schedule = invoke.schedule

    # Apply GOceanOMPParallelLoopTrans to the second Loop
    schedule, _ = otrans.apply(schedule.children[1])
    # Now enclose the parallel region within an ExtractNode (inserted
    # at the previous location of the OMPParallelDoDirective
    schedule, _ = etrans.apply(schedule.children[1])

    code = str(psy.gen)
    output = ("      ! ExtractStart\n"
              "      ! CALL write_extract_arguments(argument_list)\n"
              "      !\n"
              "      !$omp parallel do default(shared), private(i,j), "
              "schedule(static)\n"
              "      DO j=2,jstop+1\n"
              "        DO i=2,istop\n"
              "          CALL compute_cv_code(i, j, cv_fld%data, "
              "p_fld%data, v_fld%data)\n"
              "        END DO \n"
              "      END DO \n"
              "      !$omp end parallel do\n"
              "      !\n"
              "      ! ExtractEnd\n")
    assert output in code
Exemplo n.º 16
0
def test_sched_getitem():
    '''Test that Schedule has the [int] operator overloaded to return the
    given index child'''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "15.9.1_X_innerproduct_Y_builtin.f90"),
                           api="dynamo0.3")
    psy = PSyFactory("dynamo0.3", distributed_memory=True).create(invoke_info)

    sched = psy.invokes.invoke_list[0].schedule
    for indx in range(len(sched._children)):
        assert sched[indx] is sched._children[indx]

    # Test range indexing
    children = sched[:]
    assert len(children) == 2
    assert children[0] is sched._children[0]
    assert children[1] is sched._children[1]

    # Test index out-of-bounds Error
    with pytest.raises(IndexError) as err:
        _ = sched[len(sched._children)]
    assert "list index out of range" in str(err.value)
Exemplo n.º 17
0
def test_node_dag_wrong_file_format(monkeypatch):
    ''' Test the handling of the error raised by graphviz when it is passed
    an invalid file format. We make this test independent of whether or not
    graphviz is actually available by monkeypatching the
    psyir.nodes.node._graphviz_digraph_class function to return a fake digraph
    class type that mimics the error. '''
    class FakeDigraph(object):
        ''' Fake version of graphviz.Digraph class that raises a ValueError
        when instantiated. '''

        # pylint: disable=redefined-builtin
        def __init__(self, format=None):
            raise ValueError(format)

    monkeypatch.setattr(node, "_graphviz_digraph_class", lambda: FakeDigraph)
    _, invoke_info = parse(os.path.join(BASE_PATH, "1_single_invoke.f90"),
                           api="dynamo0.3")
    psy = PSyFactory("dynamo0.3", distributed_memory=False).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    with pytest.raises(GenerationError) as err:
        invoke.schedule.dag()
    assert "unsupported graphviz file format 'svg' provided" in str(err.value)
Exemplo n.º 18
0
def test_data_ref(parser):
    '''Check code generation with an array accessed via a derived type.

    '''
    reader = FortranStringReader('''subroutine data_ref()
  use some_mod, only: prof_type
  INTEGER, parameter :: n=16
  INTEGER :: ji
  real :: a(n), fconst
  type(prof_type) :: prof
  do ji = 1, n
     prof%npind(ji) = 2.0*a(ji) + fconst
  end do
END subroutine data_ref
''')
    code = parser(reader)
    psy = PSyFactory(API, distributed_memory=False).create(code)
    schedule = psy.invokes.invoke_list[0].schedule
    acc_trans = TransInfo().get_trans_name('ACCDataTrans')
    acc_trans.apply(schedule.children)
    gen_code = str(psy.gen)
    assert "!$ACC DATA COPYIN(a) COPYOUT(prof,prof%npind)" in gen_code
Exemplo n.º 19
0
def test_loop_gen_code():
    ''' Check that the Loop gen_code method prints the proper loop '''
    base_path = os.path.join(
        os.path.dirname(
            os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
        "test_files", "dynamo0p3")
    _, invoke_info = parse(os.path.join(base_path,
                                        "1.0.1_single_named_invoke.f90"),
                           api="dynamo0.3")
    psy = PSyFactory("dynamo0.3", distributed_memory=True).create(invoke_info)

    # By default DynLoop has step = 1 and it is not printed in the Fortran DO
    gen = str(psy.gen)
    assert "DO cell=1,mesh%get_last_halo_cell(1)" in gen

    # Change step to 2
    loop = psy.invokes.get('invoke_important_invoke').schedule[4]
    loop.step_expr = Literal("2", INTEGER_SINGLE_TYPE, parent=loop)

    # Now it is printed in the Fortran DO with the expression  ",2" at the end
    gen = str(psy.gen)
    assert "DO cell=1,mesh%get_last_halo_cell(1),2" in gen
Exemplo n.º 20
0
def test_kernels_around_where_construct(parser):
    ''' Check that we can put a WHERE construct inside a KERNELS region. '''
    from psyclone.psyGen import Loop, ACCKernelsDirective
    reader = FortranStringReader("program where_test\n"
                                 "  integer :: flag\n"
                                 "  real :: a(:,:), b(:,:)\n"
                                 "  where (a(:,:) < flag)\n"
                                 "    b(:,:) = 0.0\n"
                                 "  end where\n"
                                 "end program where_test\n")
    code = parser(reader)
    psy = PSyFactory(API, distributed_memory=False).create(code)
    schedule = psy.invokes.invoke_list[0].schedule
    acc_trans = ACCKernelsTrans()
    sched, _ = acc_trans.apply(schedule)
    assert isinstance(sched[0], ACCKernelsDirective)
    assert isinstance(sched[0].dir_body[0], Loop)
    new_code = str(psy.gen)
    assert ("  !$ACC KERNELS\n"
            "  WHERE (a(:, :) < flag)" in new_code)
    assert ("  END WHERE\n"
            "  !$ACC END KERNELS\n" in new_code)
Exemplo n.º 21
0
def test_lfricfields_call_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 call.

    '''
    _, invoke_info = parse(os.path.join(BASE_PATH, "1.5_single_invoke_fs.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    kernel = invoke.schedule.coded_kernels()[0]
    # Sabotage the field argument to make it have an invalid intrinsic type
    fld_arg = kernel.arguments.args[0]
    fld_arg._intrinsic_type = "triple-type"
    with pytest.raises(InternalError) as err:
        LFRicFields(invoke)._invoke_declarations(ModuleGen(name="my_mod"))
    test_str = str(err.value)
    if six.PY2:
        test_str = test_str.replace("u'", "'")
    assert ("Found unsupported intrinsic types for the field arguments "
            "['f1'] to Invoke 'invoke_0_testkern_fs_type'. Supported "
            "types are ['real', 'integer']." in test_str)
Exemplo n.º 22
0
def test_scalar_invoke_uniq_declns_valid_intrinsic():
    ''' Tests that all valid intrinsic types for user-defined scalar arguments
    ('real' and 'integer') are accepted by Invoke.unique_declarations().

    '''
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "1.7_single_invoke_2scalar.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=False).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]

    # Test 'real' scalars
    scalars_real_args = invoke.unique_declarations(
        LFRicArgDescriptor.VALID_SCALAR_NAMES, intrinsic_type="real")
    scalars_real = [arg.declaration_name for arg in scalars_real_args]
    assert scalars_real == ["a"]

    # Test 'integer' scalars
    scalars_int_args = invoke.unique_declarations(
        LFRicArgDescriptor.VALID_SCALAR_NAMES, intrinsic_type="integer")
    scalars_int = [arg.declaration_name for arg in scalars_int_args]
    assert scalars_int == ["istep"]
Exemplo n.º 23
0
def test_dynbasisfns_initialise(monkeypatch):
    ''' Check that the DynBasisFunctions.initialise() method
    raises the expected InternalErrors. '''
    from psyclone.f2pygen import ModuleGen
    _, invoke_info = parse(os.path.join(BASE_PATH,
                                        "1.1.0_single_invoke_xyoz_qr.f90"),
                           api=API)
    psy = PSyFactory(API, distributed_memory=False).create(invoke_info)
    dinf = DynBasisFunctions(psy.invokes.invoke_list[0])
    mod = ModuleGen(name="testmodule")
    # Break the shape of the first basis function
    dinf._basis_fns[0]["shape"] = "not-a-shape"
    with pytest.raises(InternalError) as err:
        dinf.initialise(mod)
    assert ("Unrecognised evaluator shape: 'not-a-shape'. Should be "
            "one of " in str(err))
    # Break the internal list of basis functions
    monkeypatch.setattr(dinf, "_basis_fns", [{'type': 'not-a-type'}])
    with pytest.raises(InternalError) as err:
        dinf.initialise(mod)
    assert ("Unrecognised type of basis function: 'not-a-type'. Should be "
            "either 'basis' or 'diff-basis'" in str(err))
Exemplo n.º 24
0
def test_dynloop_load_unexpected_func_space():
    ''' The load function of an instance of the DynLoop class raises an
    error if an unexpected function space is found. This test makes
    sure this error works correctly. It's a little tricky to raise
    this error as it is unreachable. However, we can sabotage an
    earlier function to make it return an invalid value.

    '''
    # first create a working instance of the DynLoop class
    _, invoke_info = parse(os.path.join(BASE_PATH, "19.1_single_stencil.f90"),
                           api=TEST_API)
    psy = PSyFactory(TEST_API, distributed_memory=True).create(invoke_info)
    # now get access to the DynLoop class, the associated kernel class
    # and the associated field.
    schedule = psy.invokes.invoke_list[0].schedule
    loop = schedule.children[4]
    kernel = loop.loop_body[0]
    field = kernel.arguments.iteration_space_arg()
    # break the fields function space
    field._function_spaces[0]._orig_name = "broken"

    # create a function which always returns the broken field

    def broken_func():
        ''' Returns the above field no matter what '''
        return field

    # Replace the iteration_space_arg method with our broke
    # function. This is required as iteration_space_arg currently
    # never returns a field with an invalid function space.
    kernel.arguments.iteration_space_arg = broken_func
    # We can now raise the exception.
    with pytest.raises(GenerationError) as err:
        loop.load(kernel)
    const = LFRicConstants()
    assert ("Generation Error: Unexpected function space found. Expecting "
            "one of " + str(const.VALID_FUNCTION_SPACES) +
            " but found 'broken'" in str(err.value))
Exemplo n.º 25
0
def test_quad_rule_edge():
    '''Test that the KernelInterface class quad_rule method adds the expected
    classes to the symbol table and the _arglist list for edge quadrature

    '''
    _, invoke_info = parse(os.path.join(BASE_PATH, "1.1.5_edge_qr.f90"),
                           api="dynamo0.3")
    psy = PSyFactory("dynamo0.3", distributed_memory=False).create(invoke_info)
    schedule = psy.invokes.invoke_list[0].schedule
    kernel = schedule[0].loop_body[0]
    kernel_interface = KernelInterface(kernel)
    kernel_interface.quad_rule()

    # nedges declared and added to argument list
    nedges_symbol = kernel_interface._symbol_table.lookup("nedges")
    assert isinstance(nedges_symbol, lfric_psyir.NumberOfEdgesDataSymbol)
    assert isinstance(nedges_symbol.interface, ArgumentInterface)
    assert (
        nedges_symbol.interface.access == kernel_interface._read_access.access)
    assert kernel_interface._arglist[-3] is nedges_symbol
    # nqp declared and added to argument list
    nqp_symbol = kernel_interface._symbol_table.lookup("nqp_edges")
    assert isinstance(nqp_symbol,
                      lfric_psyir.NumberOfQrPointsInEdgesDataSymbol)
    assert isinstance(nqp_symbol.interface, ArgumentInterface)
    assert (
        nqp_symbol.interface.access == kernel_interface._read_access.access)
    assert kernel_interface._arglist[-2] is nqp_symbol
    # weights declared and added to argument list
    weights_symbol = kernel_interface._symbol_table.lookup("weights_edges")
    assert isinstance(weights_symbol, lfric_psyir.QrWeightsInEdgesDataSymbol)
    assert isinstance(weights_symbol.interface, ArgumentInterface)
    assert (weights_symbol.interface.access ==
            kernel_interface._read_access.access)
    assert kernel_interface._arglist[-1] is weights_symbol
    assert len(weights_symbol.shape) == 1
    assert isinstance(weights_symbol.shape[0], Reference)
    assert weights_symbol.shape[0].symbol is nqp_symbol
Exemplo n.º 26
0
def test_goschedule_view(capsys):
    ''' Test that the GOSchedule::view() method works as expected '''
    from psyclone.psyGen import colored, SCHEDULE_COLOUR_MAP
    _, invoke_info = parse(os.path.join(
        os.path.dirname(os.path.abspath(__file__)), "test_files", "gocean1p0",
        "single_invoke_two_kernels.f90"),
                           api=API)
    psy = PSyFactory(API).create(invoke_info)
    invoke = psy.invokes.invoke_list[0]
    invoke.schedule.view()

    # The view method writes to stdout and this is captured by py.test
    # by default. We have to query this captured output.
    out, _ = capsys.readouterr()

    # Ensure we check for the correct (colour) control codes in the output
    sched = colored("GOSchedule", SCHEDULE_COLOUR_MAP["Schedule"])
    loop = colored("Loop", SCHEDULE_COLOUR_MAP["Loop"])
    call = colored("KernCall", SCHEDULE_COLOUR_MAP["KernCall"])

    expected_output = (sched +
                       "[invoke='invoke_0',Constant loop bounds=True]\n"
                       "    " + loop + "[type='outer',field_space='cu',"
                       "it_space='internal_pts']\n"
                       "        " + loop + "[type='inner',field_space='cu',"
                       "it_space='internal_pts']\n"
                       "            " + call +
                       " compute_cu_code(cu_fld,p_fld,u_fld) "
                       "[module_inline=False]\n"
                       "    " + loop + "[type='outer',field_space='every',"
                       "it_space='internal_pts']\n"
                       "        " + loop + "[type='inner',field_space='every',"
                       "it_space='internal_pts']\n"
                       "            " + call +
                       " time_smooth_code(u_fld,unew_fld,uold_fld) "
                       "[module_inline=False]")

    assert expected_output in out
Exemplo n.º 27
0
def test_ne_offset_cf_points():
    ''' Test that we can generate code for a kernel that expects a NE
    offset and writes to a field on CF points '''
    _, invoke_info = parse(os.path.join(
        os.path.dirname(os.path.abspath(__file__)), "test_files", "gocean1p0",
        "test14_ne_offset_cf_updated_one_invoke.f90"),
                           api=API)
    psy = PSyFactory(API).create(invoke_info)
    generated_code = str(psy.gen)

    expected_output = (
        "  MODULE psy_single_invoke_test\n"
        "    USE field_mod\n"
        "    USE kind_params_mod\n"
        "    IMPLICIT NONE\n"
        "    CONTAINS\n"
        "    SUBROUTINE invoke_0_compute_vort(vort_fld, p_fld, u_fld, v_fld)\n"
        "      USE kernel_ne_offset_cf_mod, ONLY: compute_vort_code\n"
        "      TYPE(r2d_field), intent(inout) :: vort_fld, p_fld, u_fld, "
        "v_fld\n"
        "      INTEGER j\n"
        "      INTEGER i\n"
        "      INTEGER istop, jstop\n"
        "      !\n"
        "      ! Look-up loop bounds\n"
        "      istop = vort_fld%grid%simulation_domain%xstop\n"
        "      jstop = vort_fld%grid%simulation_domain%ystop\n"
        "      !\n"
        "      DO j=1,jstop-1\n"
        "        DO i=1,istop-1\n"
        "          CALL compute_vort_code(i, j, vort_fld%data, p_fld%data, "
        "u_fld%data, v_fld%data)\n"
        "        END DO \n"
        "      END DO \n"
        "    END SUBROUTINE invoke_0_compute_vort\n"
        "  END MODULE psy_single_invoke_test")

    assert generated_code.find(expected_output) != -1
Exemplo n.º 28
0
def test_scalar_float_arg():
    ''' Tests that an invoke containing a kernel call requiring
    a real, scalar argument produces correct code '''
    _, invoke_info = parse(os.path.join(
        os.path.dirname(os.path.abspath(__file__)), "test_files", "gocean1p0",
        "single_invoke_scalar_float_arg.f90"),
                           api=API)
    psy = PSyFactory(API).create(invoke_info)
    generated_code = str(psy.gen)

    expected_output = (
        "  MODULE psy_single_invoke_scalar_float_test\n"
        "    USE field_mod\n"
        "    USE kind_params_mod\n"
        "    IMPLICIT NONE\n"
        "    CONTAINS\n"
        "    SUBROUTINE invoke_0_bc_ssh(a_scalar, ssh_fld)\n"
        "      USE kernel_scalar_float, ONLY: bc_ssh_code\n"
        "      TYPE(r2d_field), intent(inout) :: ssh_fld\n"
        "      REAL(KIND=wp), intent(inout) :: a_scalar\n"
        "      INTEGER j\n"
        "      INTEGER i\n"
        "      INTEGER istop, jstop\n"
        "      !\n"
        "      ! Look-up loop bounds\n"
        "      istop = ssh_fld%grid%simulation_domain%xstop\n"
        "      jstop = ssh_fld%grid%simulation_domain%ystop\n"
        "      !\n"
        "      DO j=1,jstop+1\n"
        "        DO i=1,istop+1\n"
        "          CALL bc_ssh_code(i, j, a_scalar, ssh_fld%data, "
        "ssh_fld%grid%tmask)\n"
        "        END DO \n"
        "      END DO \n"
        "    END SUBROUTINE invoke_0_bc_ssh\n"
        "  END MODULE psy_single_invoke_scalar_float_test")

    assert generated_code.find(expected_output) != -1
Exemplo n.º 29
0
def test_openmp_loop_trans():
    ''' test of the OpenMP transformation of an all-points loop '''
    _, info = parse(os.path.join(os.path.
                                 dirname(os.path.abspath(__file__)),
                                 "test_files", "gocean0p1",
                                 "openmp_fuse_test.f90"),
                    api=API)
    psy = PSyFactory(API).create(info)
    invokes = psy.invokes
    invoke = invokes.get('invoke_0')
    schedule = invoke.schedule
    ompf = GOceanOMPParallelLoopTrans()

    omp1_schedule, _ = ompf.apply(schedule.children[0])

    # Replace the original loop schedule with the transformed one
    psy.invokes.get('invoke_0').schedule = omp1_schedule

    # Store the results of applying this code transformation as
    # a string
    gen = str(psy.gen)

    omp_do_idx = -1
    # Iterate over the lines of generated code
    for idx, line in enumerate(gen.split('\n')):
        if '!$omp parallel do' in line:
            omp_do_idx = idx
        if 'DO j=' in line:
            outer_do_idx = idx
        if 'DO i=' in line:
            inner_do_idx = idx
            if omp_do_idx > -1:
                break

    # The OpenMP 'parallel do' directive must occur immediately before
    # the DO loop itself
    assert outer_do_idx-omp_do_idx == 1 and\
        inner_do_idx-outer_do_idx == 1
Exemplo n.º 30
0
def test_explicit_loop(parser):
    ''' Check that we can apply the transformation to an explicit loop. '''
    reader = FortranStringReader("program do_loop\n"
                                 "integer :: ji\n"
                                 "integer, parameter :: jpj=13\n"
                                 "real :: sto_tmp(jpj), sto_tmp2(jpj)\n"
                                 "do ji = 1,jpj\n"
                                 "  sto_tmp(ji) = 1.0d0\n"
                                 "end do\n"
                                 "do ji = 1,jpj\n"
                                 "  sto_tmp2(ji) = 1.0d0\n"
                                 "end do\n"
                                 "end program do_loop\n")
    code = parser(reader)
    psy = PSyFactory(API, distributed_memory=False).create(code)
    schedule = psy.invokes.invoke_list[0].schedule
    acc_trans = TransInfo().get_trans_name('ACCLoopTrans')
    para_trans = TransInfo().get_trans_name('ACCParallelTrans')
    para_trans.apply(schedule.children)
    schedule, _ = acc_trans.apply(schedule[0].dir_body[0])
    schedule, _ = acc_trans.apply(schedule[0].dir_body[1],
                                  {"independent": False})
    code = str(psy.gen)
    assert ("PROGRAM do_loop\n"
            "  INTEGER :: ji\n"
            "  INTEGER, PARAMETER :: jpj = 13\n"
            "  REAL :: sto_tmp(jpj), sto_tmp2(jpj)\n"
            "  !$ACC PARALLEL\n"
            "  !$ACC LOOP INDEPENDENT\n"
            "  DO ji = 1, jpj\n"
            "    sto_tmp(ji) = 1.0D0\n"
            "  END DO\n"
            "  !$ACC LOOP\n"
            "  DO ji = 1, jpj\n"
            "    sto_tmp2(ji) = 1.0D0\n"
            "  END DO\n"
            "  !$ACC END PARALLEL\n"
            "END PROGRAM do_loop" in code)