-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathchapter_fortran.tex
2448 lines (1748 loc) · 98.5 KB
/
chapter_fortran.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
%---------------------------------------------------------------------
%---------------------------------------------------------------------
\chapter{The Fortran Language}
%---------------------------------------------------------------------
%---------------------------------------------------------------------
Fortran was the first high-level language and was developed in the fifties. The languages has since the developed through a number of standards Fortran IV (1966), Fortran 77, Fortran 90, Fortran 95, Fortran 2003, Fortran 2008 and the latest Fortran 2018. The advantages with standardised languages is that the code can be run on different computer architectures without modification. In every new standard the language has been extended with more modern language elements. To be compatible with previous standards older language elements are not removed. However, language elements that are considered bad or outdated can be removed after 2 standard revisions. As an example Fortran 90 is fully backwards compatible with Fortran 77, but in Fortran 95 some
older language constructs where removed.
The following sections gives a short introduction to the modern Fortran language from Fortran 90 and above. The description is centered on the most important language features. A more thorough description of the language can be found in the book Modern Fortran Explained \cite{metcalf00}
%---------------------------------------------------------------------
\section{Program structure}
%---------------------------------------------------------------------
Every Fortran-program must have a main program routine. From the main routine, other subroutines that make up the program are called. The syntax for a main program is:
\begin{fsyntax}
\textbf{program} program-name]\newline%
[specification statements]\newline%
[executable statements]\newline%
[contains]\newline%
[subroutines]\newline%
\textbf{end} [\textbf{program} [program-name]]
\end{fsyntax}
From the syntax it can be seen that the only identifier that must be included in main program definition is \fkeyw{end}.
The syntax for a subroutine and functions are defined in the same way, but the \fkeyw{program} identifier is replaced with \fkeyw{subroutine} or \fkeyw{function}. A proper way of organizing subroutines is to place these in separat files or place the in modules (covered in upcoming sections). Subroutines can also be placed in the main program \fkeyw{contains}-section, which is the preferred method if all subroutines are placed in the same source file. The code below shows a simple example of a main program with a subroutine in Fortran.
\mintforfile{source/sample1/sample1.f90}
The program source code can contain upper and lower case letters, numbers and special characters. However, it should be noted that Fortran does not differentiate between upper and lower case letters. The program source is written starting from the first position with one statement on each line. If a row is terminated with the charachter \foper{\&}, this indicates that the statement is continued on the the next line. All text placed after the character \foper{!} is a comment and wont affect the function of the program. Even if the comments don't have any function in the program they are important for source code readability. This is especially important for future modification of the program. In addition to the source code
form described above there is also the possibility of writing code in fixed form, as in Fortran 77 and earlier versions. In previous version of the Fortran standard this was the only source code form available.
%---------------------------------------------------------------------
\section{Variables}
%---------------------------------------------------------------------
Variables are named references to data stored in memory. When specifying variables in Fortran, the data type of the data must also be specified. This means that Fortran is a strongly typed language compared to Python where data types of variables can change during code execution. Python is a dynamically typed language.
By default Fortran assumes that variables starting with letters I to N are assumed to be integers all other variables are assumed to be floating point variables (real). This is also called the implict type rule and is considered bad practice in Fortran, but is default because many Fortran programs are still relying on this rule. Modern Fortran application should always disable the implict type rule by adding the following statement in each code unit:
\begin{fortrancodeenv}
implicit none
\end{fortrancodeenv}
This is also described in more detail in the following sections.
%---------------------------------------------------------------------
\subsection{Naming of variables}
%---------------------------------------------------------------------
Variables in modern Fortran consists of 1 to 31 alphanumeric characters (letters except and , underscore and numbers). The first character of a variable name must be a letter. Allowable variable names can be:
\begin{fortrancodeenv}
a
a_thing
x1
mass
q123
time_of_flight
\end{fortrancodeenv}
Variable names can consist of both upper case and lower case letters. It should be noted that \fvar{a} and \fvar{A} references the same variable. Invalid variables names can be:
\begin{fortrancodeenv}
1a ! First character not a letter
a thing ! Contains a space character
_ ! Contains a non-alphanumeric character
\end{fortrancodeenv}
%---------------------------------------------------------------------
\subsection{Data types and declarations}
%---------------------------------------------------------------------
There are 5 built-in data types in Fortran:
\begin{xlist}
\item \ftype{integer}, Integers
\item \ftype{real}, Floating point numbers
\item \ftype{complex}, Complex numbers
\item \ftype{logical}, Boolean values
\item \ftype{character}, Strings and characters
\end{xlist}
The syntax for a variable declaration is:
\begin{fsyntax}
type [[,attribute]... ::] entity-list
\end{fsyntax}
\fexpr{type} defines the variable type and can be\ftype{integer}, \ftype{real}, \ftype{complex}, \ftype{logical}, \ftype{character}, or \ftype{type}( type-name ). \ftype{attribute} defines additional special attributes or how the variable is to be used. The following examples shows
some typical Fortran variable declarations.
\begin{fortrancodeenv}
integer :: a ! Scalar integer variable
real :: b ! Scalar floating point variable
logical :: flag ! boolean variable
real :: D(10) ! Floating point array consisting of 10 elements
real :: K(20,20) ! Floating point array of 20x20 elements
integer, dimension(10) :: C ! Integer array of 10 elements
character :: ch ! Character
character, dimension(60) :: chv ! Array of characters
character(len=80) :: line ! Character string
character(len=80) :: lines(60) ! Array of strings
\end{fortrancodeenv}
Constants are declared by specifying an additional attribute, \fkeyw{parameter}. A declared constant can be used in following variable declarations. An example of use is shown in the following example.
\begin{fortrancodeenv}
integer, parameter :: A = 5 ! Integer constant
real :: C(A) ! Floating point array where
! the number of elements is
! specified by A
\end{fortrancodeenv}
The precision and size of the variable type can be specified by adding a parenthesis directly after the type declaration. The variables \fvar{A} and \fvar{B} in the following example are declared as floating point scalars with different precisions. The number in the parenthesis denotes for many architectures, how many bytes a floating point variable is represented with. This is however not standardised and should not be relied upon.
\begin{fortrancodeenv}
real(8) :: A
real(4) :: B
integer(4) :: I
\end{fortrancodeenv}
To be able to choose the correct precision for a floating point variable, Fortran has a built in function \fkeyw{selected\_real\_kind} that returns the value to be used in the declaration with a given precision. This is illustrated in the following example.
\begin{fortrancodeenv}
integer, parameter :: dp = selected_real_kind(15,300)
real(kind=ap) :: X,Y
\end{fortrancodeenv}
In this example the floating point variable should have at least 15 significant decimals and could represent numbers from 10$^{-300}$ to 10$^{300}$. For several common architectures \fkeyw{selected\_real\_kind} will return the value 8. The advantage of using the above approach is that the precision of the floating point values can be specified in a architectural independent way. The precision constant can also be used when
specifying numbers in variable assignments as the following example illustrate.
\begin{fortrancodeenv}
X = 6.0_dp
\end{fortrancodeenv}
The importance of specifying the precision for assigning scalar values to variables is illustrated in the following example.
\begin{fortrancodeenv}
program constants
implicit none
integer, parameter :: dp = selected_real_kind(15,300)
real(dp) :: pi1, pi2
pi1 = 3.141592653589793
pi2 = 3.141592653589793_dp
write(*,*) 'pi1 = ', pi1
write(*,*) 'pi2 = ', pi2
stop
end program constants
\end{fortrancodeenv}
The program gives the following results:
\cmdmode
\begin{fortrancodeenv}
pi1 = 3.14159274101257
pi2 = 3.14159265358979
\end{fortrancodeenv}
\fmode
The scalar number assigned to the variable \fvar{pi1} is chosen by the compiler to be represented by the least number of bytes floating point precision, in this case \ftype{real(4)}, which is shown in the output from the above program.
Variable declarations in Fortran always precedes the executable statements in the main program or in a subroutine. Declarations can also be placed directly after the \fkeyw{module} identifier in modules.
%---------------------------------------------------------------------
\subsection{Implicit type rule}
%---------------------------------------------------------------------
Variable do not have to be declared in Fortran. The default is that variables starting I, J,..., N are defined as \ftype{integer} and variables starting with A, B,... ,H or O, P,... , Z are defined as \ftype{real}. This kind of implicit variable declaration is not recommended as it can lead to programming errors when variables are misspelled. To avoid implicit variable declarations the following declaration can be placed first in a program or module:
\fmode
\begin{fortrancodeenv}
implicit none
\end{fortrancodeenv}
This statement forces the compiler to make sure that all variables are declared. If a variable is not declared the compilation is stopped with an error message. This is default for many other strongly typed languages such as, C, C++ and Java.
%---------------------------------------------------------------------
\subsection{Assignment of variables}
%---------------------------------------------------------------------
The syntax for scalar variable assignment is,
\begin{fsyntax}
variable = expr
\end{fsyntax}
where \fvar{variable} denotes the variable to be assigned and \fexpr{expr} the expression to be assigned. The following example assign the \fvar{a} variable the value 5.0 with the precision defined in the constant \fkeyw{ap}.
\begin{fortrancodeenv}
a = 5.0_dp
\end{fortrancodeenv}
Assignment of boolean variables are done in the same way using the keywords, \fkeyw{.false.} and \fkeyw{.true.} indicating a true or false value. A boolean expression can also be used int the assignment. In the following example the variable, \fvar{flag}, is assigned the value \fkeyw{.false.}.
\begin{fortrancodeenv}
flag =.false.
\end{fortrancodeenv}
Assignment of strings are illustrated in the following example.
\begin{fortrancodeenv}
character(40) :: first_name
character(40) :: last_name
character(20) :: company_name1
character(20) :: company_name2
...
first_name = 'Jan'
last_name = "Johansson"
company_name1 = "McDonald's"
company_name2 = 'McDonald''s'
\end{fortrancodeenv}
The first variable, \fvar{first\_name}, is assigned the text ''Jan'', remaining characters in the string will be padded with spaces. A string is assigned using citation marks, '' or apostrophes, '. This can be of help when apostrophes or citation marks is used in strings as shown in the assignemnt of the variables, \fvar{company\_name1} och \fvar{company\_name2}.
%---------------------------------------------------------------------
\subsection{Defined and undefined variables}
%---------------------------------------------------------------------
A variable in Fortran that has been assigned a value is considered to be defined and can be used safely. Variables that are not assigned values are said to be undefined and should not be used.
A program containing undefined variables will not fail compilation. Memory for undefined variables will be reserved and can be referenced. However, values of undefined variables are not automatically set to zero and references memory locations with unknown values. Often these variables will return garbage or random values. As rule always initialise variables to a default values or make sure they are assigned a value from another variable reference.
The following example shows an example of referencing defined and undefined variables.
\begin{fortrancodeenv}
program undef1
implicit none
integer, parameter :: dp = selected_real_kind(15,300)
real(dp) :: a, b
character(40) :: s1, s2
a = 42.0_dp ! --- Defined
s1 = 'My defined string'
print*, a
print*, b
print*, s1
print*, s2
end program undef1
\end{fortrancodeenv}
This will print the following:
\begin{fortrancodeenv}
42.000000000000000
8.2890460584580950E-317
My defined string
\end{fortrancodeenv}
The reason for the 8.28...E-317 value is that the variable reference \fvar{b} points to its given memory location, but no value has been assigned to this location and will contain whatever was in memory when the program was executed. Fortran will interpret the values at this location as a floating point value and present it as such. This illustrates why it is a good idea to initialise variables to 0.0\_dp or make sure they will be assigned values from other variable references.
%---------------------------------------------------------------------
\subsection{Derived datatypes}
%---------------------------------------------------------------------
In certain cases it can be beneficial to create your own data types to handle the behavior of your program. To create new data types in Fortran you can create derived data types using the \fkeyw{type}-statement. Using this statement a new data type can be created by grouping existing Fortran data types into a new type. In the following example a data type of a particle is defined:
\begin{fortrancodeenv}
type particle
real(dp) :: x
real(dp) :: y
real(dp) :: z
real(dp) :: m
end type particle
\end{fortrancodeenv}
The new data type can now be used like any other data type in Fortran. To create a variable reference to a derived data type the \fkeyw{type} keyword precedes the name of the data type in the declaration. As in the following example:
\begin{fortrancodeenv}
type(particle) :: p0
\end{fortrancodeenv}
The members of the derived data types are accessed using the \fkeyw{\%}-operator. In the following example the members of the variable \fvar{p0} are assigned values:
\begin{fortrancodeenv}
p0 % x = 0.0_dp
p0 % y = 0.0_dp
p0 % z = 0.0_dp
p0 % m = 1.0_dp
\end{fortrancodeenv}
Derived data types can contain multiple Fortran data types:
\begin{fortrancodeenv}
type particle
real(dp) :: x
real(dp) :: y
real(dp) :: z
real(dp) :: m
logical :: active
integer :: id
character :: name(8)
end type particle
\end{fortrancodeenv}
%---------------------------------------------------------------------
\section{Operators and expressions}
%---------------------------------------------------------------------
The following arithmetic operators are defined in Fortran:
\vspace{5mm}
\begin{tabular}{ll}
\foper{**} & power to \\
\foper{*} & multiplication \\
\foper{/} & division \\
\foper{+} & addition \\
\foper{-} & subtraction \\
\end{tabular}
\vspace{5mm}
Parenthesis are used to specify the order of different operators. If no parenthesis are given in an expression operators are evaluated in the following order:
\begin{enumerate}
\item Operations with \foper{**}%
\item Operations with \foper{*} or \foper{/}%
\item Operations with \foper{+} or \foper{--}
\end{enumerate}
\noindent The following code illustrates operator precedence.
\begin{fortrancodeenv}
c = a+b/2 ! is equivalent to $a+(b/2)$
c = (a+b)/2 ! in this case $(a+b)$ is evaluated and then $/$ 2
\end{fortrancodeenv}
\noindent Relational operators:
\vspace{5mm}
\begin{tabular}{lp{0.6\textwidth}}
% after \\: \hline or \cline{col1-col2} \cline{col3-col4} ...
\foper{<} or \foper{.lt.} & less than ({\underline {l}}ess {\underline {t}}han) \\
\foper{<=} or \foper{.le.} & less than or equal to ({\underline {l}}ess than or {\underline {e}}qual) \\
\foper{>} or \foper{.gt.} & greater than ({\underline{g}}reater {\underline {t}}han) \\
\foper{>=} or \foper{.ge.} & greater than or equal to ({\underline {g}}reater than or {\underline {e}}qual) \\
\foper{==} or \foper{.eq.} & equal to ({\underline {eq}}ual) \\
\foper{/=} or \foper{.ne.} & not equal to ({\underline {n}}ot {\underline {e}}qual) \\
\end{tabular}
\vspace{5mm}
\noindent Logical operators:
\vspace{5mm}
\begin{tabular}{ll}
\foper{.and.} & and \\
\foper{.or.} & or \\
\foper{.not.} & not \\
\end{tabular}
\vspace{5mm}
%---------------------------------------------------------------------
\subsection{Numeric expressions}
%---------------------------------------------------------------------
Numeric expressions in Fortran consist of operands of the built-in data types \fkeyw{integer}, \fkeyw{real} or \fkeyw{complex}.
If the operands only consists of integer it is important to note that integer divisions are rounded towards 0. The following example illustrates this:
\begin{fortrancodeenv}
program expr1
print*, 6/3
print*, 8/3 ! 2.6666... rounded down to 2
print*, -8/3 ! -2.666... rounded up to 2
end program expr1
\end{fortrancodeenv}
Running the program will result in the following output:
\begin{fortrancodeenv}
2
2
-2
\end{fortrancodeenv}
There are also some other things to be careful with when working with integer expressions. Consider the following example:
\begin{fortrancodeenv}
program expr2
print*, 2**3
print*, 2**(-3)
end program expr2
\end{fortrancodeenv}
When run will give the following output:
\begin{fortrancodeenv}
8
0
\end{fortrancodeenv}
The reason for the 0 in the second expression is that \fvar{2**(-3)} is the same as 1/2**3, which will be truncated to 0 as an integer expression.
When mixing data types in expressions, weaker data types will be converted to the stronger type. The result of the expression will be of the stronger type. \fkeyw{real} is stronger than \fkeyw{integer}. Consider the following code:
\begin{fortrancodeenv}
real(dp) :: a
integer :: i
real(dp) :: b
b = a * i
\end{fortrancodeenv}
Here, the i variable reference will be converted to \fvar{real(dp)} when the expression is evaluated.
%---------------------------------------------------------------------
\section{Arrays and matrices}
%---------------------------------------------------------------------
In scientific and technical applications matrices and arrays are important concepts. As Fortran is a language mainly for
technical computing, arrays and matrices play a vital role in the language.
Declaring arrays and matrices can be done in two ways. In the first method the dimensions are specified using the special attribute, \fkeyw{dimension}, after the data type declaration. The second method, the dimensions are specified by adding the dimensions directly after the variable name. The following code illustrate these methods of declaring arrays.
\begin{fortrancodeenv}
integer, parameter :: ap = selected_real_kind(15,300)
real(ap),dimension(20,20) :: K ! Matrix 20x20 elements
real(ap) :: fe(6) ! Array with 6 elements
\end{fortrancodeenv}
The default starting index in arrays is 1. It is however possible to define custom indices in the declaration, as the following example shows.
\begin{fortrancodeenv}
real(ap) :: idx(-3:3)
\end{fortrancodeenv}
This declares an array, \fvar{idx} with the indices [-3, -2,
-1, 0, 1, 2, 3], which contains 7 elements.
%---------------------------------------------------------------------
\subsection{Array assignment}
%---------------------------------------------------------------------
Arrays are assigned values either by explicit indices or the entire array in a single statement. The following code assigned the variable, \fvar{K}, the value 5.0 at position row 5 and column 6.
\begin{fortrancodeenv}
K(5,6) = 5.0
\end{fortrancodeenv}
If the assignment had been written as
\begin{fortrancodeenv}
K = 5.0
\end{fortrancodeenv}
the entire array, \fvar{K}, would have been assigned the value 5.0. This is an efficient way of assigning entire arrays
initial values.
Explicit values can be assigned to arrays in a single statement using the following assignment.
\begin{fortrancodeenv}
real(ap) :: v(5) ! Array with 5 elements
v = (/ 1.0, 2.0, 3.0, 4.0, 5.0 /)
\end{fortrancodeenv}
This is equivalent to an assignment using the following statements.
\begin{fortrancodeenv}
v(1) = 1.0
v(2) = 2.0
v(3) = 3.0
v(4) = 4.0
v(5) = 5.0
\end{fortrancodeenv}
The number of elements in the list must be the same as the number of elements in the array variable.
Assignments to specific parts of arrays can be achieved by slicing. The following example illustrates this concept.
\begin{fortrancodeenv}
program slicing
implicit none
real :: A(4,4)
real :: B(4)
real :: C(4)
B = A(2,:) ! Assigns B the values of row 2 in A
C = A(:,1) ! Assigns C the values of column 1 in A
stop
end program slicing
\end{fortrancodeenv}
Using slicing rows or columns can be assigned in single statements as shown in the following code:
\begin{fortrancodeenv}
! Assign row 5 in matrix K the values 1, 2, 3, 4, 5
K(5,:) = (/ 1.0, 2.0, 3.0, 4.0, 5.0 /)
! Assign the array v the values 5, 4, 3, 2, 1
v = (/ 5.0, 4.0, 3.0, 2.0, 1.0 /)
\end{fortrancodeenv}
%---------------------------------------------------------------------
\subsection{Array expressions}
%---------------------------------------------------------------------
In modern Fortran expressions can also be used on arrays. The operators will then apply element wise to the in the expression. For this to work the arrays must be of the same size. Array expression can also contain scalar values. These will be broadcast to the array elements. Consider the following arrays:
\begin{fortrancodeenv}
real :: a(10,20), b(10,20), c(10,20)
real :: u(5), v(5)
\end{fortrancodeenv}
The following expression will divide all values in \fvar{a} with the values in \fvar{b}.
\begin{fortrancodeenv}
c = a/b
\end{fortrancodeenv}
This is equivalent to:
\begin{fortrancodeenv}
do i=1,10
do j=1,20
c(i,j) = a(i,j)/b(i,j)
end do
end do
\end{fortrancodeenv}
It is important to make sure the resulting variable on the left side of the assignment has the same size as the resulting array expression.
The following expression adds a scalar value to all elements in the array \fvar{v}:
\begin{fortrancodeenv}
u = v + 1.0
\end{fortrancodeenv}
This is equivalent to:
\begin{fortrancodeenv}
do i=1,5
u(i) = v(i) + 1.0
end do
\end{fortrancodeenv}
It is also possible to use slicing to extract a ''slice'' that can be used in an array expression:
\begin{fortrancodeenv}
u = 5.0/v + a(1:5,5)
\end{fortrancodeenv}
Which is equivalent to:
\begin{fortrancodeenv}
do i=1,5
u(i) = 5.0/v(i) + a(i,5)
end do
\end{fortrancodeenv}
%---------------------------------------------------------------------
\subsection{Array storage}
%---------------------------------------------------------------------
Memory allocation by the operating system is done in linear blocks of bytes. The operating system does not have the concept of multidimensional arrays. This is a concept introduced by the programming language, in this case Fortran, to make it easier for us to implement algorithms and access values stored in memory.
There are 2 conventions of storing 2D arrays in memory, by column and by row. Fortran as a convention stores arrays by column and C by row. The following figure~\cite{array_storage} illustrates this concept:
\fignormal{array_storage}{Arrays in memory}{fig:array_storage}
The storage of arrays in memory is especially important when calling libraries implemented in other languages, which usually stores arrays by row. The Python library NumPy by default stores all arrays using the C convention. Calling a Fortran subroutine with a pointer to these arrays will probably result in undefined behavior. NumPy supports column ordered arrays by supplying the array-constructor with the option \pkeyw{order=F}.
%---------------------------------------------------------------------
\subsection{Allocatable arrays}
%---------------------------------------------------------------------
In Fortran 77 and earlier versions of the standard it was not possible to dynamically allocate memory during program execution. This capability is now available in Fortran 90 and later versions. To declare an array as dynamically allocatable, the attribute \fkeyw{allocatable} must be added to the array declaration. The dimensions are also replaced with a colon, :, indicating the number of dimensions in the declared variable. A typical allocatable array declaration is shown in the following example.
\begin{fortrancodeenv}
real, dimension(:,:), allocatable :: K
\end{fortrancodeenv}
In this example the two-dimensional array, K, is defined as allocatable. To indicate that the array is two-dimensional is done by specifying \fkeyw{dimension(:,:)} in the variable attribute. To declare a one-dimensional array the code becomes:
\begin{fortrancodeenv}
real, dimension(:), allocatable :: f
\end{fortrancodeenv}
Variables with the \fkeyw{allocatable} attribute can't be used until memory is allocated. Memory allocation is done using the \fkeyw{allocate} method. To allocate the variables, \fvar{K,f}, in the previous examples the following code is used.
\begin{fortrancodeenv}
allocate(K(20,20))
allocate(f(20))
\end{fortrancodeenv}
When the allocated memory is no longer needed it can be deallocated using the command, \fkeyw{deallocate}, as the following code illustrates.
\begin{fortrancodeenv}
deallocate(K)
deallocate(f)
\end{fortrancodeenv}
An important issue when using dynamically allocatable variable is to make sure the application does not ''leak''. ''Leaking'' is term used by applications that allocate memory during the execution and never deallocate used memory. If unchecked the application will use more and more resources and will eventually make the operating system start swapping and perhaps become also become unstable. A rule of thumb is that an
\fkeyw{allocate} statement should always have corresponding \fkeyw{deallocate}.
%---------------------------------------------------------------------
\subsection{Array subobjects}
%---------------------------------------------------------------------
In many situations you want to work on smaller parts or slices of existing arrays. In Modern Fortran this can be accomplished by using the subobject feature. We will illustrate the concept of subobjects by an example. Consider the following declarations of an 2D- and 1D array:
\begin{fortrancodeenv}
use utils
real(dp) :: A(10,10)
real(dp) :: v(10)
\end{fortrancodeenv}
To make sure we don't have any junk values in the arrays, we initialise these with random values between 1 and 0.
\begin{fortrancodeenv}
call init_rand()
call set_print_format(10, 4, 'F')
call rand_mat(A, 0.0_dp, 1.0_dp)
call rand_vec(v, 0.0_dp, 1.0_dp)
\end{fortrancodeenv}
We print the arrays, so that we can see the structure:
\begin{fortrancodeenv}
call print_matrix(A, 'a')
print*, loc(A)
call print_vector(v, 'c')
print*, loc(v)
\end{fortrancodeenv}
The print-statements are added to show the actual memory address, so we can see what happens when we create subobjects.
\begin{fortrancodeenv}
Matrix a ( 10 x 10) DP
---------------------------------------------------------------------- ...
0.7112 0.6538 0.9200 0.7415 0.7460 0.4596 0.9778 ... 0.4111 0.0399 0.7354 0.9505 0.4428 0.7871 0.7390 ...
0.9814 0.4057 0.4292 0.3406 0.6673 0.2120 0.9523 ...
0.1369 0.5396 0.4405 0.7577 0.9942 0.7274 0.2653 ...
0.7179 0.9883 0.5549 0.6374 0.1337 0.9464 0.7786 ...
0.2915 0.8634 0.3962 0.8088 0.5708 0.9827 0.8841 ...
0.8294 0.8191 0.5452 0.2079 0.1126 0.8199 0.2020 ...
0.0706 0.6945 0.7744 0.3474 0.2566 0.6155 0.0921 ...
0.3754 0.7855 0.9828 0.3965 0.9551 0.2119 0.2718 ...
0.4026 0.5287 0.6198 0.9967 0.5866 0.6598 0.4024 ...
---------------------------------------------------------------------- ...
140722079206560
Vector c ( 10) DP
---------------------------------------------------------------------- ...
0.9092 0.4976 0.6361 0.0355 0.7865 0.2197 0.8824 ...
---------------------------------------------------------------------- ...
140722079206480
\end{fortrancodeenv}
In the first example we are extracing the first column of the \fvar{A} array:
\begin{fortrancodeenv}
call print_vector(A(:,1))
print*, loc(A(:,1))
\end{fortrancodeenv}
This will give us the following output:
\begin{fortrancodeenv}
Vector ( 10)
---------------------------------------------------------------------- ...
0.7112 0.4111 0.9814 0.1369 0.7179 0.2915 0.8294 ...
---------------------------------------------------------------------- ...
140722079206560
\end{fortrancodeenv}
From the output we can see that i seems to be the first column of the \fvar{A} array. We can also see that the memory location of the subobject is equivalent to the memory location of the \fvar{A}-array. This is due to the fact that memory for the \fvar{A}-array is stored column wise access a slice in this direction can be done directly without copying.
If we instead extract the first row of the \fvar{A}-array:
\begin{fortrancodeenv}
call print_vector(A(1,:))
print*, loc(A(1,:))
\end{fortrancodeenv}
We get the following output:
\begin{fortrancodeenv}
Vector ( 10)
---------------------------------------------------------------------- ...
0.7112 0.6538 0.9200 0.7415 0.7460 0.4596 0.9778 ...
---------------------------------------------------------------------- ...
10278912
\end{fortrancodeenv}
Here we can see that the memory location is in a completely different location. This is due to the fact that the compiler needs to make a temporary copy to create this slice. This is important to think about especially if working with large array slices that are passed in subroutine calls. This can lead to crashes as these slices often are allocated on the stack.
Below illustrates some other examples of using array sub objects:
\begin{fortrancodeenv}
call print_matrix(A(1:2,1:2))
print*, loc(A(1:2,1:2))
call print_matrix(A(1:6:2,1:6:2))
print*, loc(A(1:6:2,1:6:2))
call print_matrix(A(:,1:2))
print*, loc(A(:,1:2))
\end{fortrancodeenv}
This gives the following output:
\begin{fortrancodeenv}
Matrix ( 2 x 2) DP
--------------------
0.1233 0.2606
0.8357 0.4479
--------------------
27678720
Matrix ( 3 x 3) DP
------------------------------
0.1233 0.7812 0.1668
0.4284 0.3687 0.6542
0.2531 0.3314 0.3557
------------------------------
27689200
Matrix ( 10 x 2) DP
--------------------
0.1233 0.2606
0.8357 0.4479
0.4284 0.2254
0.3541 0.3400
0.2531 0.6490
0.6420 0.5396
0.5006 0.5849
0.7185 0.3138
0.8209 0.6203
0.0232 0.9131
--------------------
140728814257696
\end{fortrancodeenv}
%---------------------------------------------------------------------
\section{Conditional statements}
%---------------------------------------------------------------------
One of the more important concepts in a programming language is the ability to execute code depending on certain conditions are fulfilled. Modern Fortran support this through the \fkeyw{if}- and the \fkeyw{select}-statement, which are described in this section.
The simplest form of if-statements in Fortran have the following syntax
\begin{fsyntax}
\textbf{if} (scalar-logical-expr) \textbf{then}\\
\ftab block\\
\textbf{end if}\\
\end{fsyntax}
where \fexpr{scalar-logical-expr} is a boolean expression, that has to be evaluated as true, (\fkeyw{.true.}), for the statements in, \fexpr{block}, to be executed. An extended version of the if-statement adds a \fkeyw{else}-block with the following syntax
\begin{fsyntax}
\textbf{if} (scalar-logical-expr) \textbf{then}\\
\ftab block1\\
\textbf{else}\\
\ftab block2\\
\textbf{end if}\\
\end{fsyntax}
In this form the \fexpr{block1} will be executed if \fexpr{scalar-logical-expr} is evaluated as true, otherwise \fexpr{block2} will be executed. A third form of if-statement contains one or more \fkeyw{else if}-statements with the following syntax:
\begin{fsyntax}
\textbf{if} (scalar-logical-expr1) \textbf{then}\\
\ftab block1\\
\textbf{else if} (scalar-logical-expr2) \textbf{then}\\
\ftab block2\\
\textbf{else}\\
\ftab block3\\
\textbf{end if}
\end{fsyntax}
In this form the \fexpr{scalar-logical-expr1} is evaluated first. If this expression is true \fexpr{block1} is executed, otherwise if \fexpr{scalar-logical-expr2} evaluates as true \fexpr{block2} is executed. If no other expressions are evaluated to true, \fexpr{block3} is executed. An if-statement can contain several \fkeyw{else if}-blocks. The use of if-statements is illustrated in the following example:
\mintforfile{source/logic/logic.f90}
Another conditional constructi is the case-statement.
\begin{fsyntax}
\textbf{select case} (expr)\\
\ftab\textbf{case} selector \\
\ftab\ftab block \\
\textbf{end select}
\end{fsyntax}
In this statement the expression, \fexpr{expr} is evaluated and the \fkeyw{case}-block with the corresponding \fexpr{selector} is executed. To handle the case when no \fkeyw{case}-block corresponds to the \fexpr{expr}, a \fkeyw{case}-block with the \fkeyw{default} keyword can be added. The syntax then becomes:
\begin{fsyntax}
\textbf{select case}(expr)\\
\ftab\textbf{case} selector\\
\ftab\ftab block \\
\ftab\textbf{case default}\\
\ftab\ftab block \\
\textbf{end select}
\end{fsyntax}
Example of case-statement use is shown in the following example:
\begin{fortrancodeenv}
select case (display_mode)
case (displacements)
...
case (geometry)
...
end select
\end{fortrancodeenv}
To handle the case when \fvar{display\_mode} does not correspone to any of the alternatives the above code is modified to the following code.
\begin{fortrancodeenv}
select case (display_mode) case (displacements)
...
case (geometry)
...
case default
...
end select
\end{fortrancodeenv}
The following program example illustrate how case-statements can be used.
\mintforfile{source/case/case.f90}
%---------------------------------------------------------------------
\section{Repetitive statements}
%---------------------------------------------------------------------
The most common repetitive statement in Fortran is the \fkeyw{do}-statement. The syntax is:
\begin{fsyntax}
\textbf{do} variable = expr1, expr2 [,expr3]\\
\ftab block\\
\textbf{end do}
\end{fsyntax}
\fexpr{variable} is the control-variable of the loop. \fexpr{expr1} is the starting value, \fexpr{expr2} is the end value and \fexpr{expr3} is the step interval. If the step interval is not given it is assumed to be 1. There are two ways of controlling the execution flow in a \fkeyw{do}-statement. The \fkeyw{exit} command terminates the loop and program execution is continued after the \fkeyw{do}-statement. The \fkeyw{cycle} command terminates the execution of the current block and continues execution with the next value of the control variable. The example below illustrates the use of a \fkeyw{do}-statement.
\begin{fortrancodeenv}
program loop_sample
implicit none
integer :: i
do i=1,20
if (i>10) then
write(*,*) 'Terminates do-statement.'
exit
else if (i<5) then
write(*,*) 'Cycling to next value.'
cycle
end if
write(*,*) i
end do
stop
end program loop_sample
\end{fortrancodeenv}
The above program gives the following output:
\cmdmode
\begin{fortrancodeenv}
Cycling to next value.
Cycling to next value.
Cycling to next value.
Cycling to next value.
5
6
7
8
9
10
Terminates do-statement.
\end{fortrancodeenv}
\fmode
Another repetitive statement available is the \fkeyw{do while}-statement. With this statement, the code block can execute until a certain condition is fulfilled. The syntax is:
\begin{fsyntax}
\textbf{do while} (scalar-logical-expr)\\
\ftab block\\
\textbf{end do}
\end{fsyntax}
The following code shows a simple \fkeyw{do while}-statement printing the function $f(x)=sin(x)$.
\begin{fortrancodeenv}
x = 0.0
do while x<1.05
f = sin(x)
x = x + 0.1
write(*,*) x, f
end do
\end{fortrancodeenv}
There are other repetitive statements such as \fkeyw{forall} and \fkeyw{where} covered int the array features sections.
%---------------------------------------------------------------------
\section{Built-in functions}
%---------------------------------------------------------------------
Fortran has a number of built-in functions covering a number of different areas. The following tables list a selection of these. For a more thorough description of the built-in function please see, Metcalf and Reid \cite{metcalf00}.
\begin{table}[!hb]
\begin{center}
\begin{tabular}{|l|l|}
\hline Function & Description \\ \hline
\lstinline!acos(x)! & Returns $\arccos(x)$ \\
\lstinline!asin(x)! & Returns $\arcsin(x)$ \\
\lstinline!atan(x)! & Returns $\arctan(x)$ \\
\lstinline!atan2(y,x)! & Returns $\arctan(\frac{y}{x})$ from $-\pi$ till $-\pi$ \\
\lstinline!cos(x)! & Returns $\cos(x)$ \\
\lstinline!cosh(x)! & Returns $\cosh(x)$ \\
\lstinline!exp(x)! & Returns $e^{x}$ \\
\lstinline!log(x)! & Returns $\ln(x)$ \\
\lstinline!log10(x)! & Returns $\lg(x)$ \\
\lstinline!sin(x)! & Returns $\sin(x)$ \\
\lstinline!sinh(x)! & Returns $\sinh(x)$ \\
\lstinline!sqrt(x)! & Returns $\sqrt{x}$\\
\lstinline!tan(x)! & Returns $\tan(x)$ \\
\lstinline!tanh(x)! & Returns $\tanh(x)$ \\ \hline
\end{tabular}
\end{center}
\caption{Mathematical functions}
\end{table}
\begin{table}[!hb]
\begin{center}
\begin{tabular}{|l|l|}
\hline Function & Description \\ \hline
\lstinline!abs(a)! & Returns absolute value of a \\
\lstinline!aint(a)! & Truncates a floating point value \\
\lstinline!int(a)! & Converts a floating point value to an integer \\
\lstinline!nint(a)! & Rounds a floating point value to the nearest integer \\
\lstinline!real(a)! & Converts an integer to a floating point value \\
\lstinline!max(a1,a2[,a3,...])! & Returns the maximum value of two or more values \\
\lstinline!min(a1,a2[,a3,...])! & Returns the minimum value of two or more values \\
\hline
\end{tabular}
\end{center}
\caption{Miscellaneous conversion functions}
\end{table}
\begin{table}[!hb]
\begin{center}
\begin{tabular}{|l|p{70mm}|}
\hline
Function & Description \\
\hline
\lstinline!dot_product(u, v)! & Returns the scalar product of $u\cdot v$ \\
& \\
\lstinline!matmul(A, B)! & Matrix multiplication. The result must have the same for as $\mathbf{AB}$ \\
& \\
\lstinline!transpose(C)! & Returns the transpose $\mathbf{C}^{T}$. Elementet $C^{T}_{ij}$ motsvarar $C_{ji}$ \\
\hline
\end{tabular}
\end{center}
\caption{Vector and matrix functions}
\end{table}
\begin{table}[!hb]
\begin{center}
\begin{tabular}{|l|p{75mm}|}
\hline
Function & Description \\