def test_omp_do_within_if(): ''' Check that we can insert an OpenMP parallel do within an if block. ''' from psyclone.transformations import OMPParallelLoopTrans otrans = OMPParallelLoopTrans() _, 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 loop = schedule[0].loop_body[1].else_body[0].else_body[0] assert isinstance(loop, nemo.NemoLoop) # Apply the transformation to a loop within an else clause schedule, _ = otrans.apply(loop) gen = str(psy.gen) expected = (" ELSE\n" " !$omp parallel do default(shared), private(ji,jj), " "schedule(static)\n" " DO jj = 1, jpj, 1\n" " DO ji = 1, jpi, 1\n" " zdkt(ji, jj) = (ptb(ji, jj, jk - 1, jn) - " "ptb(ji, jj, jk, jn)) * wmask(ji, jj, jk)\n" " END DO\n" " END DO\n" " !$omp end parallel do\n" " END IF\n") assert expected in gen
def test_omp_do_children_err(): ''' Tests that we raise the expected error when an OpenMP parallel do directive has more than one child. ''' from psyclone.transformations import OMPParallelLoopTrans from psyclone.psyGen import OMPParallelDoDirective otrans = OMPParallelLoopTrans() psy, invoke_info = get_invoke("imperfect_nest.f90", api=API, idx=0) schedule = invoke_info.schedule otrans.apply(schedule[0].loop_body[2]) directive = schedule[0].loop_body[2] assert isinstance(directive, OMPParallelDoDirective) # Make the schedule invalid by adding a second child to the # OMPParallelDoDirective directive.dir_body.children.append(Statement()) with pytest.raises(GenerationError) as err: _ = psy.gen assert ("An OpenMP PARALLEL DO can only be applied to a single loop but " "this Node has 2 children:" in str(err.value))
def test_omp_do_within_if(): ''' Check that we can insert an OpenMP parallel do within an if block. ''' from psyclone.transformations import OMPParallelLoopTrans otrans = OMPParallelLoopTrans() psy, invoke_info = get_invoke("imperfect_nest.f90", api=API, idx=0) schedule = invoke_info.schedule loop = schedule[0].loop_body[1].else_body[0].else_body[0] assert isinstance(loop, nemo.NemoLoop) # Apply the transformation to a loop within an else clause otrans.apply(loop) gen = str(psy.gen).lower() expected = (" else\n" " !$omp parallel do default(shared), private(ji,jj), " "schedule(static)\n" " do jj = 1, jpj, 1\n" " do ji = 1, jpi, 1\n" " zdkt(ji, jj) = (ptb(ji, jj, jk - 1, jn) - " "ptb(ji, jj, jk, jn)) * wmask(ji, jj, jk)\n" " end do\n" " end do\n" " !$omp end parallel do\n" " end if\n") assert expected in gen
def test_profile_nemo_openmp(parser): ''' Check that the automatic kernel-level profiling handles a tightly-nested loop that has been parallelised using OpenMP. ''' omptrans = OMPParallelLoopTrans() Profiler.set_options([Profiler.KERNELS]) psy, schedule = get_nemo_schedule( parser, "program do_loop\n" "integer, parameter :: jpi=5, jpj=5\n" "integer :: ji, jj\n" "real :: sto_tmp(jpi,jpj)\n" "do jj = 1, jpj\n" " do ji = 1,jpi\n" " sto_tmp(ji,jj) = 1.0d0\n" " end do\n" "end do\n" "end program do_loop\n") omptrans.apply(schedule[0]) Profiler.add_profile_nodes(schedule, Loop) code = str(psy.gen).lower() assert (" type(profile_psydatatype), target, save :: profile_psy_data0\n" " call profile_psy_data0 % prestart('do_loop', 'r0', 0, 0)\n" " !$omp parallel do default(shared), private(ji,jj), " "schedule(static)\n" " do jj = 1, jpj" in code)
def test_omp_do_missing_parent(monkeypatch): ''' Check that we raise the expected error when we cannot find the parent node in the fparser2 AST. ''' from psyclone.transformations import OMPParallelLoopTrans otrans = OMPParallelLoopTrans() _, 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, _ = otrans.apply(schedule.children[0]) # Remove the reference to the fparser2 AST from the Schedule node monkeypatch.setattr(schedule, "_ast", None) with pytest.raises(InternalError) as err: _ = psy.gen assert ("Failed to find parent node in which to insert OpenMP parallel " "do directive" in str(err))
def test_omp_do_children_err(): ''' Tests that we raise the expected error when an OpenMP parallel do directive has more than one child. ''' from psyclone.transformations import OMPParallelLoopTrans from psyclone.psyGen import OMPParallelDoDirective otrans = OMPParallelLoopTrans() _, 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 new_sched, _ = otrans.apply(schedule.children[0].children[2]) directive = new_sched.children[0].children[2] assert isinstance(directive, OMPParallelDoDirective) # Make the schedule invalid by adding a second child to the # OMPParallelDoDirective directive.children.append(new_sched.children[0].children[3]) with pytest.raises(GenerationError) as err: _ = psy.gen assert ("An OpenMP PARALLEL DO can only be applied to a single loop but " "this Node has 2 children:" in str(err))
def test_invalid_apply(): '''Test the exceptions that should be raised by ReadOnlyVerifyTrans. ''' _, invoke = get_invoke("test11_different_iterates_over_one_invoke.f90", "gocean1.0", idx=0) read_only = ReadOnlyVerifyTrans() omp = OMPParallelLoopTrans() _, _ = omp.apply(invoke.schedule[0]) with pytest.raises(TransformationError) as err: _, _ = read_only.apply(invoke.schedule[0].dir_body[0], options={"region_name": ("a", "b")}) assert "Error in ReadOnlyVerifyTrans: Application to a Loop without its "\ "parent Directive is not allowed." in str(err.value) with pytest.raises(TransformationError) as err: _, _ = read_only.apply(invoke.schedule[0].dir_body[0].loop_body[0], options={"region_name": ("a", "b")}) assert "Error in ReadOnlyVerifyTrans: Application to Nodes enclosed " \ "within a thread-parallel region is not allowed." in str(err.value)