Fortran

Guide To Learn

Writing user-defined operators for the Field type

In chapter 8, we implemented the tsunami solver using the Field derived type to model physical fields such as water height and velocity. This allowed us to initialize the fields and express the solver equation cleanly and concisely, as shown in the following listing.

Listing 9.9 Main tsunami solver using the Field derived type

use mod_field, only: Field                            ❶
...
type(Field) :: h, u, v, hm                            ❷
...
u = Field('u', [im, jm])                              ❸
v = Field('v', [im, jm])                              ❸
h = Field('h', [im, jm])                              ❸
hm = Field('hm', [im, jm])                            ❸
...
time_loop: do n = 1, nm
  ...
  u = u - (u * diffx(u) / dx + v * diffy(u) / dy &    ❹
    + g * diffx(h) / dx) * dt                         ❹
 
  v = v - (u * diffx(v) / dx + v * diffy(v) / dy &    ❺
    + g * diffy(h) / dy) * dt                         ❺
 
  h = h - (diffx(u * (hm + h)) / dx                   ❻
         + diffy(v * (hm + h)) / dy) * dt             ❻
  ...
end do time_loop

❶ Imports the type from the module

❷ Declares the Field instances

❸ Initializes fields for water velocity and height

❹ Computes the water velocity in x direction

❺ Computes the water velocity in y direction

❻ Computes the water height

If you worked through chapter 8, you learned how to use derived types to abstract away complex code into type components and methods. Specifically, in the tsunami simulator, we designed the Field type so that each instance carries all the metadata required for tedious bookkeeping, such as array start and end indices, the image indices of parallel neighbor tiles, and so on.

However, what’s not immediately obvious from listing 9.9 is how the computation of field instances uv, and h works under the hood. Notice that here we’re using the same code as we did in chapter 7, where we worked directly with coarrays. As you can probably guess by now, my use of built-in arithmetic operators (+-*, and /) with Field instances didn’t come for free. In fact, each of these operators needed at least a few specific functions defined. The following listing provides a sneak peek at implementing the addition (+) operator to add two instances of the Field type.

Listing 9.10 Defining the addition operator for the Field type

type :: Field
  ...                                            ❶
contains
  ...                                            ❷
  generic :: operator(+) => field_add_field      ❸
  ...
end type Field

❶ Components are declared here.

❷ Type methods are listed here.

❸ Defines the addition operator to invoke the field_add_field procedure

This is the familiar derived type definition block, with one new syntax element–the generic :: operator() statement, which specifies a built-in or custom operator and the specific procedure that the operator will invoke. This is just a taste of what’s coming.

In the next chapter, we dig deep into defining custom operators for derived types. You’ll learn how to use intrinsic operators together with your own custom types to significantly augment the base rules of the language. For the tsunami simulator, this will mean exciting and powerful capabilities, such as applying familiar arithmetic operators directly to instances of the Field type, automatically synchronizing parallel images on assignment, and more.

Writing user-defined operators for the Field type

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top