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