def test_loop_fuse_different_spaces():
    ''' Test that we raise an error if we attempt to fuse loops that are
    over different grid-point types '''
    _, invoke = get_invoke("fuse_different_spaces_test.f90", API,
                           name="invoke_0")
    schedule = invoke.schedule
    lftrans = GOceanLoopFuseTrans()
    with pytest.raises(TransformationError):
        _, _ = lftrans.apply(schedule.children[0],
                             schedule.children[1])
Ejemplo n.º 2
0
def test_loop_fuse_different_spaces():
    ''' Test that we raise an error if we attempt to fuse loops that are
    over different grid-point types '''
    _, info = parse(os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                 "test_files", "gocean0p1",
                                 "fuse_different_spaces_test.f90"),
                    api=API)
    psy = PSyFactory(API).create(info)
    invokes = psy.invokes
    invoke = invokes.get('invoke_0')
    schedule = invoke.schedule
    lftrans = GOceanLoopFuseTrans()
    with pytest.raises(TransformationError):
        _, _ = lftrans.apply(schedule.children[0], schedule.children[1])
def test_loop_fuse_unexpected_error():
    ''' Test that we catch an unexpected error when loop fusing '''
    _, invoke = get_invoke("test14_module_inline_same_kernel.f90", 0)
    schedule = invoke.schedule

    lftrans = GOceanLoopFuseTrans()

    # cause an unexpected error
    schedule.children[0].children = None

    # Attempt to fuse two loops that are iterating over different
    # things
    with pytest.raises(TransformationError) as excinfo:
        _, _ = lftrans.apply(schedule.children[0], schedule.children[1])
    assert 'Unexpected exception' in str(excinfo.value)
def test_gocean_loop_fuse_with_not_a_loop():
    ''' Test that an appropriate error is raised by the GOceanLoopFuseTrans
    class when we attempt to fuse a loop with something that
    is not a loop '''
    _, invoke = get_invoke("openmp_fuse_test.f90", API, name="invoke_0")
    schedule = invoke.schedule
    # Use the bare LoopFuseTrans in order tests its error checking
    lftrans = GOceanLoopFuseTrans()
    ompf = GOceanOMPParallelLoopTrans()
    # Enclose the first loop within an OMP parallel do
    new_sched, _ = ompf.apply(schedule.children[0])
    # Attempt to (erroneously) fuse this OMP parallel do
    # with the next loop in the schedule
    with pytest.raises(TransformationError):
        _, _ = lftrans.apply(new_sched.children[0],
                             new_sched.children[1])
Ejemplo n.º 5
0
def test_gocean_loop_fuse_with_not_a_loop():
    ''' Test that an appropriate error is raised by the GOceanLoopFuseTrans
    class when we attempt to fuse a loop with something that
    is not a 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)
    invoke = psy.invokes.get('invoke_0')
    schedule = invoke.schedule
    # Use the bare LoopFuseTrans in order tests its error checking
    lftrans = GOceanLoopFuseTrans()
    ompf = GOceanOMPParallelLoopTrans()
    # Enclose the first loop within an OMP parallel do
    new_sched, _ = ompf.apply(schedule.children[0])
    # Attempt to (erroneously) fuse this OMP parallel do
    # with the next loop in the schedule
    with pytest.raises(TransformationError):
        _, _ = lftrans.apply(new_sched.children[0], new_sched.children[1])
Ejemplo n.º 6
0
def test_openmp_loop_fuse_trans():
    ''' test of the OpenMP transformation of a fused 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)
    invoke = psy.invokes.get('invoke_0')
    schedule = invoke.schedule
    lftrans = GOceanLoopFuseTrans()
    ompf = GOceanOMPParallelLoopTrans()

    # fuse all outer loops
    lf_schedule, _ = lftrans.apply(schedule.children[0],
                                   schedule.children[1])
    schedule, _ = lftrans.apply(lf_schedule.children[0],
                                lf_schedule.children[1])
    # fuse all inner loops
    lf_schedule, _ = lftrans.apply(schedule.children[0].
                                   children[0],
                                   schedule.children[0].
                                   children[1])
    schedule, _ = lftrans.apply(lf_schedule.children[0].
                                children[0],
                                lf_schedule.children[0].
                                children[1])

    # Add an OpenMP directive around the fused loop
    lf_schedule, _ = ompf.apply(schedule.children[0])

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

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

    # 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

    # The OpenMP 'parallel do' directive must occur immediately before
    # the DO loop itself
    assert outer_do_idx-omp_do_idx == 1 and\
        outer_do_idx-inner_do_idx == -1
def test_openmp_loop_fuse_trans():
    ''' test of the OpenMP transformation of a fused loop '''
    psy, invoke = get_invoke("openmp_fuse_test.f90", API, name="invoke_0")
    schedule = invoke.schedule
    lftrans = GOceanLoopFuseTrans()
    ompf = GOceanOMPParallelLoopTrans()

    # fuse all outer loops
    lf_schedule, _ = lftrans.apply(schedule.children[0],
                                   schedule.children[1])
    schedule, _ = lftrans.apply(lf_schedule.children[0],
                                lf_schedule.children[1])
    # fuse all inner loops
    lf_schedule, _ = lftrans.apply(schedule.children[0].loop_body[0],
                                   schedule.children[0].loop_body[1])
    schedule, _ = lftrans.apply(lf_schedule.children[0].loop_body[0],
                                lf_schedule.children[0].loop_body[1])

    # Add an OpenMP directive around the fused loop
    lf_schedule, _ = ompf.apply(schedule.children[0])

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

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

    # 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

    # The OpenMP 'parallel do' directive must occur immediately before
    # the DO loop itself
    assert outer_do_idx-omp_do_idx == 1 and\
        outer_do_idx-inner_do_idx == -1
def test_go_loop_swap_errors():
    ''' Test loop swapping transform with incorrect parameters. '''

    psy, invoke_loop1 = get_invoke("test27_loop_swap.f90", 1)

    schedule = invoke_loop1.schedule
    swap = GOLoopSwapTrans()
    assert swap.name == "GOLoopSwap"
    assert str(swap) == "Exchange the order of two nested loops: inner "\
        "becomes outer and vice versa"

    # Test error if given node is not the outer loop of at least
    # a double nested loop:
    with pytest.raises(TransformationError) as error:
        swap.apply(schedule.children[0].children[0])
    assert re.search(
        "Supplied node .* must be the outer loop of a loop nest "
        "but the first inner statement is not a loop, got .*",
        str(error.value)) is not None

    # Not a loop: use the cal to bc_ssh_code node as example for this test:
    with pytest.raises(TransformationError) as error:
        swap.apply(schedule.children[0].children[0].children[0])
    assert "Given node 'kern call: bc_ssh_code' is not a loop" in \
        str(error.value)

    # Now create an outer loop with more than one inner statement
    # ... by fusing the first and second outer loops :(
    invoke_loop2 = psy.invokes.get("invoke_loop2")
    schedule = invoke_loop2.schedule

    fuse = GOceanLoopFuseTrans()
    fused, _ = fuse.apply(schedule.children[0], schedule.children[1])
    invoke_loop2.schedule = fused

    with pytest.raises(TransformationError) as error:
        swap.apply(fused.children[0])
    assert re.search(
        "Supplied node .* must be the outer loop of a loop nest "
        "and must have exactly one inner loop, but this node "
        "has 2 inner statements, the first two being .* and .*",
        str(error.value)) is not None

    # Now remove the body of the first inner loop, and pass the first
    # inner loop --> i.e. a loop with an empty body
    del fused.children[0].children[0].children[0]

    with pytest.raises(TransformationError) as error:
        swap.apply(fused.children[0].children[0])
    assert re.search(
        "Supplied node .* must be the outer loop of a loop nest "
        "and must have one inner loop, but this node does not "
        "have any statements inside.", str(error.value)) is not None

    # Check if a non gocean1p0 API raises an error
    _, info = parse(os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                 "test_files", "dynamo0p3",
                                 "1.0.1_single_named_invoke.f90"),
                    api="dynamo0.3")
    psy = PSyFactory("dynamo0.3").create(info)
    invokes = psy.invokes
    invoke = invokes.get(invokes.names[0])
    with pytest.raises(TransformationError) as error:
        swap.apply(invoke.schedule.children[3])

    assert re.search(
        "Given node .* is not a GOLoop, "
        "but an instance of .*DynLoop", str(error.value)) is not None