Now that you have an idea of how to declare a variable of a specific numeric type, let’s declare some variables, constants, and arrays that we’ll use in the tsunami simulator.
Declaring variables
What kinds of variables will we need? As a reminder, based on our implementation strategy in section 2.3.1, we’ll need the following:
- Spatial array size,
grid_size, and number of time steps,num_time_steps - Physical constants, such as time step,
dt, grid size,dx, and background flow speed,c - One-dimensional arrays to carry the values of water height,
h, and its difference in space,dh - An integer index,
i, to reference individual array elements,h(i), and another to loop through time,n
Since we need to first specify grid_size before we declare the array h, let’s first declare scalar variables and constants, and declare the arrays afterward, as shown in the following listing.
Listing 2.4 Declaring and initializing integer and real variables
program tsunami
implicit none ❶
integer :: i, n ❷
integer :: grid_size ❷
integer :: num_time_steps ❷
real :: dt ! time step [s] ❸
real :: dx ! grid spacing [m] ❸
real :: c ! phase speed [m/s] ❸
grid_size = 100 ❹
num_time_steps = 100 ❹
dt = 1. ❺
dx = 1. ❺
c = 1. ❺
end program tsunami
❸ Real (floating point) declarations
The declaration section begins with implicit none and ends immediately before the first executable line of code (grid_size = 100). All declarations are done in one place, at the beginning of the program.
Fortran comments begin with an exclamation mark (!). They can start at the beginning of the line, or they can follow any valid statement.
Ideally, your code should be clear enough that it doesn’t need any comments. However, this is often not possible, and most programs need at least some comments. Use your best judgment. If the intent isn’t obvious from the code itself, describe it in comments.
Finally, having no comment is always better than having an inaccurate or outdated comment.
For variables that won’t change value for the duration of the program, it’s useful to declare them as constants. This allows the compiler to better optimize the code and prevents you from accidentally changing the value of a constant. We’ll declare constants in the next section.
Our program won’t do much for now, as we only have the data declarations in it. However, feel free to tweak it, recompile it, and, even better, try to break it! See if the compiler complains.
Declaring constants
Some of the variables will be constant, and Fortran allows you to declare them as such explicitly. Doing so will help you write correct code by triggering a compiler error if you try to change the value of a constant, and will help the compiler optimize the code. You can declare a constant (also known as immutable) using the parameter attribute, as shown in the following listing.
Listing 2.5 Declaring and initializing constants
integer, parameter :: grid_size = 100 ❶
integer, parameter :: num_time_steps = 100 ❷
real, parameter :: dt = 1, dx = 1, c = 1 ❸
❶ Declares grid size as a constant parameter
❷ Ditto for the number of time steps in the simulation
❸ Time step in seconds (s), grid spacing in meters (m), and background flow speed in meters per second (m/s)
Using the parameter attribute requires us to initialize the variable on the same line.
Tip If the value of a variable is known at compile time and won’t change for the duration of the program, declare it as a parameter.
Declaring arrays
Arrays are among Fortran’s most powerful features. They have several useful properties:
- Contiguous –Array elements are contiguous in memory. Indexing them and performing element-wise arithmetic on arrays is extremely efficient on modern processors.
- Multidimensional –The Fortran standard allows up to 15 dimensions for arrays. In contrast, in C you have to emulate multiple dimensions by defining arrays of arrays.
- Static or dynamic –Fortran arrays can be static, with dimensions set at compile time, or dynamic, with dimensions set at runtime.
- Whole-array arithmetic –You can use the usual scalar arithmetic operators and mathematical functions with arrays as well.
- Column-major indexing –Fortran arrays use column-major indexing, like MATLAB or R, and unlike C or Python, which are row-major. The first (leftmost) index thus varies fastest. For example,
a(1,1),a(2,1),a(3,1), and so on, are consecutive elements of arraya. Keep this in mind when you loop over elements of a multidimensional array.
Having declared the integer grid size as a parameter, we can use it to set the size of the array, h, that holds the water height values. You can declare a fixed-length (static) real array using the dimension attribute, and an integer parameter for the array size:
real, dimension(grid_size) :: h ❶
❶ Declares h as a real array with the number of elements equal to grid_size
The argument to dimension is the integer length of the array–in our case, the parameter grid_size.
Shorthand syntax for declaring arrays
You can declare arrays in an even shorter form by omitting the dimension attribute and specifying the array length in parentheses immediately after the array name:
real :: h(grid_size)
Whether you use the dimension attribute or the more concise form is completely up to you. However, to conserve space in code listings, I’ll use the shorthand syntax throughout this book.
As I mentioned earlier, one of Fortran’s strengths is its intrinsic support for multidimensional arrays. You can define an array of up to 15 dimensions by specifying it in the declaration statement, for example:
real, dimension(10, 5, 2) :: h
Here, h is declared as a three-dimensional array, with a total of 100 elements (10 * 5 * 2).
You may have noticed that in both array declarations, we used integer literals to set the size and shape of the array. However, what if our array dimensions are not known until runtime? Fortran provides excellent support for dynamic arrays, also known as allocatable arrays. When you declare an allocatable array, you only specify the rank (number of dimensions) of the array in the declaration, not the size of the dimensions. Once the size is known, the allocate statement is used to allocate the array with specified dimensions. Allocatable arrays can also be reallocated dynamically any number of times. You’ll see more on allocatable arrays in chapter 5, where we’ll put them to good use in our app.
For now, we need two arrays in our app–one for water height, h, and another for its finite difference, dh:
real :: h(grid_size), dh(grid_size)
Now that we have our data structures declared and ready for action, let’s see what we can do with them.