So far, we’ve used this_image and num_images to instruct each image to tell us who it is and how many total images there are. This is useful information, but we still haven’t done anything with it yet. How can we use this information to split the weather buoy data between images? Recall from listing 7.1 that we define the list of data files by specifying the array of buoy IDs:
ids = ['42001', '42002', '42003', '42020', '42035',&
'42036', '42039', '42040', '42055']
To break down the data, we need to split this array into nearly equal pieces and assign each piece to an image. For example, if we work with two images, image 1 should have ids = ['42001', '42002', '42003', '42020', '42035'], and image 2 should have ids = ['42036', '42039', '42040', '42055']. For three images, each should have three buoy IDs total. You get the idea.
For exercise, let’s implement this as an external function. (See the “Exercise 1” sidebar.) You have all the ingredients: declaring arrays like we did in chapter 2, defining a function and input arguments like we did in chapter 3, and the built-in functions this_image and num_images that we just covered.
Exercise 1: Finding the array subranges on each image
Write a function tile_indices that does the following:
- Takes an integer size of a global array. In our example, this corresponds to
size(ids), or9. - Returns an integer array of size 2, which contains a start and end index that define the subset of the array on each image, or the so-called tile.
tile_indices(9)invoked on one image should return[1,9].tile_indices(9)invoked on two images should return[1,5]and[6,9]on images 1 and 2, respectively.tile_indices(9)invoked on three images should return[1,3],[4,6], and[7,9], on images 1, 2, and 3, respectively.
- Use
integer::tile_indices(2)to declare the result of the function. - Use the built-in function
mod(a,b)that returns the remainder ofadivided byb.
For bonus points: Can this function be declared as pure?
You can find the solution to this exercise in the “Answer key” section near the end of this chapter.
If you got the exercise right, great! Otherwise, no worries; you can take a look at my implementation in the “Answer key” section near the end of the chapter. This function is also a necessary ingredient for the parallel tsunami simulator, so we’ll reuse it there. Right now, let’s see how we can use it to distribute the buoy data between images, as listing 7.2 demonstrates.
Listing 7.2 Finding a subrange for buoy IDs on each image
program weather_stats_parallel
...
use mod_parallel, only: tile_indices ❶
...
integer :: is, ie, indices(2) ❷
ids = ['42001', '42002', '42003', '42020', '42035',&
'42036', '42039', '42040', '42055']
indices = tile_indices(size(ids)) ❸
is = indices(1) ❸
ie = indices(2) ❸
allocate(max_wind(is:ie), mean_wind(is:ie)) ❹
do i = is, ie ❺
call read_buoy('data/buoy_' // ids(i) // '.csv', time, wind_speed)
wind_speed = denan(wind_speed)
max_wind(i) = maxval(wind_speed)
mean_wind(i) = mean(wind_speed)
end do
...
end program weather_stats_parallel
❶ Imports the function from the external module
❸ Calculates and stores subrange indices
❹ Allocates arrays over the local subranges
❺ Each image loops over its own unique subrange.
In listing 7.2, I only included relevant additions to the serial version of the program. How does the data distribution work here? The key line is in the invocation of tile_indices(size(ids)). Even though each image invokes the same code, this function will return different start and end indices for each image! This is exactly the point at which our parallel universes start to bifurcate. At the end of the do loop, each image will have processed a subset of data files. However, our work is not done yet. Take a look at figure 7.5 again. We have one last step, which is to do a collective calculation over all nine elements of max_wind and mean_wind. But this may be trickier now that we’ve scattered the data across images. Enter coarrays!