diff --git a/src/wrf/extension.py b/src/wrf/extension.py index 6887f87..cd4acd5 100755 --- a/src/wrf/extension.py +++ b/src/wrf/extension.py @@ -929,34 +929,187 @@ def _wdir(u, v, outview=None): # OpenMP wrappers def omp_set_num_threads(num_threads): + """Specify the number of threads to use. + + The omp_set_num_threads routine affects the number of threads to be used + for subsequent parallel regions that do not specify a num_threads + clause, by setting the value of the first element of the nthreads-var + ICV of the current task. + + Args: + + num_threads (a positive :obj:`int`): The number of threads. Must be + positive. + + Returns: + + None. + + """ + if num_threads < 0: + raise ValueError("'num_threads' must be a positive integer.") + fomp_set_num_threads(num_threads) def omp_get_num_threads(): + """Return the number of threads in the current team. + + The omp_get_num_threads routine returns the number of threads in the + team executing the parallel region to which the routine region binds. + If called from the sequential part of a program, this routine returns 1. + + Note: + + This function always returns 1 when called from within Python. + + Returns: + + :obj:`int`: The number of threads in the current team. + + See Also: + + :meth:`wrf.omp_get_max_threads`, :meth:`wrf.omp_set_num_threads` + + """ return fomp_get_num_threads() def omp_get_max_threads(): + """Return the maximum number of threads that can be used in a parallel + region. + + The omp_get_max_threads routine returns an upper bound on the number of + threads that could be used to form a new team if a parallel construct + without a num_threads clause were encountered after execution returns from + this routine. + + Returns: + + :obj:`int`: The number of threads in the current team. + + See Also: + + :meth:`wrf.omp_set_num_threads` + + """ return fomp_get_max_threads() def omp_get_thread_num(): + """Return the thread number, within the current team, of the + calling thread. + + The omp_get_thread_num routine returns the thread number of the calling + thread, within the team executing the parallel region to which the routine + region binds. The thread number is an integer between 0 and one less than + the value returned by omp_get_num_threads, inclusive. The thread number of + the master thread of the team is 0. The routine returns 0 if it is called + from the sequential part of a program. + + Note: + + This function always returns 0 when called from within Python. + + Returns: + + :obj:`int`: The thread number. + + See Also: + + :meth:`wrf.omp_get_num_procs` + + """ return fomp_get_thread_num() def omp_get_num_procs(): + """Return the number of processors on the device. + + The omp_get_num_procs routine returns the number of processors that are + available to the device at the time the routine is called. This value may + change between the time that it is determined by the omp_get_num_procs + routine and the time that it is read in the calling context due to system + actions outside the control of the OpenMP implementation. + + Returns: + + :obj:`int`: The number of processors. + + """ return fomp_get_num_procs() def omp_in_parallel(): + """Returns 1 if the active-levels-var ICV is greater than zero; otherwise, + it returns 0. + + The effect of the omp_in_parallel routine is to return 1 if the current + task is enclosed by an active parallel region, and the parallel region is + enclosed by the outermost initial task region on the device; otherwise it + returns 0. + + Note: + + This function always returns 0 when called from within Python. + + Returns: + + :obj:`int`: Returns 1 if the active-levels-var ICV is greater than + zero. Otherwise, it returns 0. + + """ return fomp_in_parallel() def omp_set_dynamic(dynamic_threads): + """Enables or disables dynamic adjustment of the number of threads + available for the execution of subsequent parallel regions by setting the + value of the dyn-var ICV. + + For implementations that support dynamic adjustment of the number of + threads, if the argument to omp_set_dynamic evaluates to True, dynamic + adjustment is enabled for the current task; otherwise, dynamic adjustment + is disabled for the current task. For implementations that do not support + dynamic adjustment of the number of threads this routine has no effect: + the value of dyn-var remains false. + + Args: + + dynamic_threads (:obj:`bool`): Set to True to support the dynamic + adjustment of the number of threads. Otherwise, set to False. + + Returns: + + None. + + See Also: + + :meth:`wrf.omp_get_dynamic` + + """ fomp_set_dynamic(dynamic_threads) def omp_get_dynamic(): + """Returns the value of the dyn-var ICV, which determines whether + dynamic adjustment of the number of threads is enabled or disabled. + + This routine returns 1 if dynamic adjustment of the number of threads + is enabled for the current task; it returns 0, otherwise. If an + implementation does not support dynamic adjustment of the + number of threads, then this routine always returns false. + + Returns: + + :obj:`int`: Returns 1 if dynamic thread adjustment is enabled, 0 + if disabled. + + See Also: + + :meth:`wrf.omp_set_dynamic` + + """ return fomp_get_dynamic() @@ -981,7 +1134,7 @@ def omp_get_thread_limit(): def omp_set_max_active_levels(max_levels): - omp_set_max_active_levels(max_levels) + fomp_set_max_active_levels(max_levels) def omp_get_max_active_levels(): diff --git a/test/test_omp.py b/test/test_omp.py new file mode 100644 index 0000000..ab239c9 --- /dev/null +++ b/test/test_omp.py @@ -0,0 +1,121 @@ +from __future__ import (absolute_import, division, print_function, + unicode_literals) + +import unittest as ut +import numpy.testing as nt + +from wrf import (omp_set_num_threads, omp_get_num_threads, + omp_get_max_threads, omp_get_thread_num, + omp_get_num_procs, omp_in_parallel, + omp_set_dynamic, omp_get_dynamic, omp_set_nested, + omp_get_nested, omp_set_schedule, + omp_get_schedule, omp_get_thread_limit, + omp_set_max_active_levels, + omp_get_max_active_levels, omp_get_level, + omp_get_ancestor_thread_num, omp_get_team_size, + omp_get_active_level, omp_in_final, + omp_init_lock, omp_init_nest_lock, + omp_destroy_lock, omp_destroy_nest_lock, + omp_set_lock, omp_set_nest_lock, + omp_unset_lock, omp_unset_nest_lock, + omp_test_lock, omp_test_nest_lock, + omp_get_wtime, omp_get_wtick) +from wrf import Constants + + +class OmpTest(ut.TestCase): + longMessage = True + + def test_locks(self): + l = omp_init_lock() + omp_set_lock(l) + omp_unset_lock(l) + omp_test_lock(l) + omp_destroy_lock(l) + + nl = omp_init_nest_lock() + omp_set_nest_lock(nl) + omp_unset_nest_lock(nl) + omp_test_nest_lock(nl) + omp_destroy_nest_lock(nl) + + + def test_thread_set(self): + omp_set_num_threads(4) + max_threads = omp_get_max_threads() + self.assertEqual(max_threads, 4) + + num_threads = omp_get_num_threads() + self.assertEqual(num_threads, 1) # Always 1 outside of parallel region + + thread_num = omp_get_thread_num() + self.assertEqual(thread_num, 0) # Always 0 outside of parallel region + num_procs = omp_get_num_procs() + in_parallel = omp_in_parallel() + self.assertFalse(in_parallel) # Always False outside of parallel region + + limit = omp_get_thread_limit() + + + def test_dynamic(self): + omp_set_dynamic(True) + dynamic = omp_get_dynamic() + self.assertTrue(dynamic) + + omp_set_dynamic(False) + dynamic = omp_get_dynamic() + self.assertFalse(dynamic) + + def test_nested(self): + omp_set_nested(True) + nested = omp_get_nested() + self.assertTrue(nested) + + omp_set_nested(False) + nested = omp_get_nested() + self.assertFalse(nested) + + + def test_schedule(self): + omp_set_schedule(Constants.OMP_SCHED_STATIC, 100000) + kind, modifier = omp_get_schedule() + self.assertEqual(kind, Constants.OMP_SCHED_STATIC) + self.assertEqual(modifier, 100000) + + omp_set_schedule(Constants.OMP_SCHED_DYNAMIC, 10000) + kind, modifier = omp_get_schedule() + self.assertEqual(kind, Constants.OMP_SCHED_DYNAMIC) + self.assertEqual(modifier, 10000) + + omp_set_schedule(Constants.OMP_SCHED_GUIDED, 100) + kind, modifier = omp_get_schedule() + self.assertEqual(kind, Constants.OMP_SCHED_GUIDED) + self.assertEqual(modifier, 100) + + omp_set_schedule(Constants.OMP_SCHED_AUTO, 10) + kind, modifier = omp_get_schedule() + self.assertEqual(kind, Constants.OMP_SCHED_AUTO) + self.assertNotEqual(modifier, 10) # The modifier argument is ignored, + # so it will be set to the previous + # value of 100. + + + def test_team_level(self): + omp_set_max_active_levels(10) + active_levels = omp_get_max_active_levels() + self.assertEqual(active_levels, 10) + + level = omp_get_level() + ancestor_thread = omp_get_ancestor_thread_num(level) + team_size = omp_get_team_size(level) + active_level = omp_get_active_level() + in_final = omp_in_final() + + + def test_time(self): + wtime = omp_get_wtime() + wtick = omp_get_wtick() + +if __name__ == "__main__": + ut.main() + \ No newline at end of file