-
Notifications
You must be signed in to change notification settings - Fork 68
Transferring fortran90.org "Fortran Best Practices" into a mini-book #246
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
6f3648d
Transfer fortran90.org Best Practices content (#79)
vmagnin b713a9b
Removed vestigial best_practices.md (2020-04-15)
vmagnin 16b65a5
Replaced HTML code by Markdown italics *...*
vmagnin 1a256d3
Typo: replaced 3 'enddo' by 'end do'
vmagnin 6c15dea
Consistent indentation (2 spaces) with quickstart minibook
awvwgk c84e3f0
Minor adjustments, wording, stop -> error stop
awvwgk 3383039
Extend the alloctable arrays section
awvwgk 976fd62
Expand chapter on arrays
awvwgk c358ace
Expand chapter on integer division
awvwgk 4114262
Add definition of callback
awvwgk 4b3de58
Add intent in callback snippet
awvwgk f146342
Rework multidimensional array section
awvwgk b885654
Rework floating point chapter
awvwgk 1fb3b44
Remove parallel_programming from best practices
awvwgk 7a9fcbc
Remove Python interfacing chapter from best practices
awvwgk f5978fb
Remove Fortran-C interfacing from best practice
awvwgk 81aa4d5
Apply suggestions from code review
awvwgk e17cba7
Fix some typos in allocatable arrays section
awvwgk e73677a
Add correct syntax highlighting
awvwgk 158a970
Rework file IO section
awvwgk a8da64c
Rework module and program chapter
awvwgk 5cf80cc
Update authors and introduction for best practice minibook
awvwgk 5a86e10
Fix typo
awvwgk c1e492a
Rework callback section in best practise minibook
awvwgk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
--- | ||
layout: book | ||
title: Allocatable Arrays | ||
permalink: /learn/best_practices/allocatable_arrays | ||
--- | ||
|
||
The ``allocatable`` attribute provides safe way for memory handling. | ||
In comparison to variables with ``pointer`` attribute the memory is managed | ||
automatically and will be deallocated automatically once the variable goes | ||
out-of-scope. Using ``allocatable`` variables removes the possibility to | ||
create memory leaks in an application. | ||
|
||
They can be used in subroutines to create scratch or work arrays, where | ||
automatic arrays would become too large to fit on the stack. | ||
|
||
```fortran | ||
real(dp), allocatable :: temp(:) | ||
allocate(temp(10)) | ||
``` | ||
|
||
The allocation status can be checked using the ``allocated`` intrinsic | ||
to avoid uninitialized access | ||
|
||
```fortran | ||
subroutine show_arr(arr) | ||
integer, allocatable, intent(in) :: arr(:) | ||
|
||
if (allocated(arr)) then | ||
print *, arr | ||
end if | ||
end subroutine show_arr | ||
``` | ||
|
||
To allocate variables inside a procedure the dummy argument has to carry | ||
the ``allocatable`` attribute. Using it in combination with ``intent(out)`` | ||
will deallocate previous allocations before entering the procedure: | ||
|
||
```fortran | ||
subroutine foo(lam) | ||
real(dp), allocatable, intent(out) :: lam(:) | ||
allocate(lam(5)) | ||
end subroutine foo | ||
``` | ||
|
||
The allocated array can be used afterwards like a normal array | ||
|
||
```fortran | ||
real(dp), allocatable :: lam(:) | ||
call foo(lam) | ||
``` | ||
|
||
An already allocated array cannot be allocated again without prior deallocation. | ||
Similarly, deallocation can only be invoked for allocated arrays. To reallocate | ||
an array use | ||
|
||
```fortran | ||
if (allocated(lam)) deallocate(lam) | ||
allocate(lam(10)) | ||
``` | ||
|
||
Passing allocated arrays to procedures does not require the ``allocatable`` attribute | ||
for the dummy arguments anymore. | ||
|
||
```fortran | ||
subroutine show_arr(arr) | ||
integer, intent(in) :: arr(:) | ||
|
||
print *, arr | ||
end subroutine show_arr | ||
|
||
subroutine proc | ||
integer :: i | ||
integer, allocatable :: arr | ||
|
||
allocate(arr(5)) | ||
|
||
do i = 1, size(arr) | ||
arr(i) = 2*i + 1 | ||
end do | ||
call show_arr(arr) | ||
end subroutine proc | ||
``` | ||
|
||
Passing an unallocated array in this context will lead to an invalid memory access. | ||
Allocatable arrays can be passed to ``optional`` dummy arguments, if they are unallocated | ||
the argument will not be present. The ``allocatable`` attribute is not limited to | ||
arrays and can also be associated with scalars, which can be useful in combination | ||
with ``optional`` dummy arguments. | ||
|
||
Allocations can be moved between different arrays with ``allocatable`` attribute | ||
using the ``move_alloc`` intrinsic subroutine. | ||
|
||
```fortran | ||
subroutine resize(var, n) | ||
real(wp), allocatable, intent(inout) :: var(:) | ||
integer, intent(in), optional :: n | ||
integer :: this_size, new_size | ||
integer, parameter :: inital_size = 16 | ||
|
||
if (allocated(var)) then | ||
this_size = size(var, 1) | ||
call move_alloc(var, tmp) | ||
else | ||
this_size = initial_size | ||
end if | ||
|
||
if (present(n)) then | ||
new_size = n | ||
else | ||
new_size = this_size + this_size/2 + 1 | ||
end if | ||
|
||
allocate(var(new_size)) | ||
|
||
if (allocated(tmp)) then | ||
this_size = min(size(tmp, 1), size(var, 1)) | ||
var(:this_size) = tmp(:this_size) | ||
end if | ||
end subroutine resize | ||
``` | ||
|
||
Finally, allocations do not initialize the array, the content of the uninitialized | ||
array is most likely just the bytes of whatever was previously at the respective address. | ||
The allocation supports initialization using the source attribute: | ||
|
||
```fortran | ||
real(dp), allocatable :: arr(:) | ||
allocate(arr(10), source=0.0_dp) | ||
``` | ||
|
||
The ``source`` keyword supports scalar and array valued variables and constants. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
--- | ||
layout: book | ||
title: Arrays | ||
permalink: /learn/best_practices/arrays | ||
--- | ||
|
||
Arrays are a central object in Fortran. The creation of dynamic sized arrays | ||
is discussed in the [allocatable arrays arrays](./allocatable_arrays.html). | ||
|
||
To pass arrays to procedures four ways are available | ||
|
||
1. *assumed-shape* arrays | ||
4. *assumed-rank* arrays | ||
2. *explicit-shape* arrays | ||
3. *assumed-size* arrays | ||
|
||
The preferred way to pass arrays to procedures is as *assumed-shape* arrays | ||
|
||
```fortran | ||
subroutine f(r) | ||
real(dp), intent(out) :: r(:) | ||
integer :: n, i | ||
n = size(r) | ||
do i = 1, n | ||
r(i) = 1.0_dp / i**2 | ||
end do | ||
end subroutine f | ||
``` | ||
|
||
Higher dimensional arrays can be passed in a similar way. | ||
|
||
```fortran | ||
subroutine g(A) | ||
real(dp), intent(in) :: A(:, :) | ||
... | ||
end subroutine g | ||
``` | ||
|
||
The array is simply passed by | ||
|
||
```fortran | ||
real(dp) :: r(5) | ||
call f(r) | ||
``` | ||
|
||
In this case no array copy is done, which has the advantage that the shape and size | ||
information is automatically passed along and checked at compile and optionally at | ||
runtime. | ||
Similarly, array strides can be passed without requiring a copy of the array but as | ||
*assumed-shape* discriptor: | ||
|
||
```fortran | ||
real(dp) :: r(10) | ||
call f(r(1:10:2)) | ||
call f(r(2:10:2)) | ||
``` | ||
|
||
This should always be your default way of passing arrays in and out of subroutines. | ||
Avoid passing arrays as whole slices, as it obfuscates the actual intent of the code: | ||
|
||
```fortran | ||
real(dp) :: r(10) | ||
call f(r(:)) | ||
``` | ||
|
||
In case more general arrays should be passed to a procedure the *assumed-rank* | ||
functionality introduced in the Fortran 2018 standard can be used | ||
|
||
```fortran | ||
subroutine h(r) | ||
real(dp), intent(in) :: r(..) | ||
select rank(r) | ||
rank(1) | ||
! ... | ||
rank(2) | ||
! ... | ||
end select | ||
end subroutine h | ||
``` | ||
|
||
The actual rank can be queried at runtime using the ``select rank`` construct. | ||
This easily allows to create more generic functions that have to deal with | ||
differnet array ranks. | ||
|
||
*Explicit-shape* arrays can be useful for returning data from functions. | ||
Most of their functionality can be provided by *assumed-shape* and *assumed-rank* | ||
arrays but they find frequent use for interfacing with C or in legacy Fortran | ||
procedures, therefore they will be discussed briefly here. | ||
|
||
To use *explicit-shape* arrays, the dimension has to be passed explicitly as dummy | ||
argument like in the example below | ||
|
||
``` fortran | ||
subroutine f(n, r) | ||
integer, intent(in) :: n | ||
real(dp), intent(out) :: r(n) | ||
integer :: i | ||
do i = 1, n | ||
r(i) = 1.0_dp / i**2 | ||
end do | ||
end subroutine | ||
``` | ||
|
||
For high-dimensional arrays additional indices have to be passed. | ||
|
||
``` fortran | ||
subroutine g(m, n, A) | ||
integer, intent(in) :: m, n | ||
real(dp), intent(in) :: A(m, n) | ||
... | ||
end subroutine | ||
``` | ||
|
||
The routines can be invoked by | ||
|
||
``` fortran | ||
real(dp) :: r(5), s(3, 4) | ||
call f(size(r), r) | ||
call g(size(s, 1), size(s, 2), s) | ||
``` | ||
|
||
Note that the shape is not checked, therefore the following would be valid code | ||
with will potentially yield incorrect results: | ||
|
||
```fortran | ||
real(dp) :: s(3, 4) | ||
call g(size(s), 1, s) ! s(12, 1) in g | ||
call g(size(s, 2), size(s, 1), s) ! s(4, 3) in g | ||
``` | ||
|
||
In this case the memory layout is preserved but the shape is changed. | ||
Also, *explicit-shape* arrays require contiguous memory and will create temporary | ||
arrays in case non-contiguous array strides are passed. | ||
|
||
To return an array from a function with *explicit-shape* use | ||
|
||
``` fortran | ||
function f(n) result(r) | ||
integer, intent(in) :: n | ||
real(dp) :: r(n) | ||
integer :: i | ||
do i = 1, n | ||
r(i) = 1.0_dp / i**2 | ||
end do | ||
end function | ||
``` | ||
|
||
Finally, there are *assumed-size* arrays, which provide the least compile and runtime | ||
checking and can be found be found frequently in legacy code, they should be avoided | ||
in favour of *assumed-shape* or *assumed-rank* arrays. | ||
An *assumed-size* array dummy argument is identified by an asterisk as the last dimension, | ||
this disables the usage of this array with many intrinsic functions, like ``size`` or | ||
``shape``. | ||
|
||
To check for the correct size and shape of an *assumed-shape* array the ``size`` and | ||
``shape`` intrinsic functions can be used to query for those properties | ||
|
||
```fortran | ||
if (size(r) /= 4) error stop "Incorrect size of 'r'" | ||
if (any(shape(r) /= [2, 2])) error stop "Incorrect shape of 'r'" | ||
``` | ||
|
||
Note that ``size`` returns the total size of all dimensions, to obtain the shape of | ||
a specific dimension add it as second argument to the function. | ||
|
||
Arrays can be initialized by using an array constructor | ||
|
||
```fortran | ||
integer :: r(5) | ||
r = [1, 2, 3, 4, 5] | ||
``` | ||
|
||
The array constructor can be annoted with the type of the constructed array | ||
|
||
```fortran | ||
real(dp) :: r(5) | ||
r = [real(dp) :: 1, 2, 3, 4, 5] | ||
``` | ||
|
||
Implicit do loops can be used inside an array constructor as well | ||
|
||
```fortran | ||
integer :: i | ||
real(dp) :: r(5) | ||
r = [(real(i**2, dp), i = 1, size(r))] | ||
``` | ||
|
||
In order for the array to start with different index than 1, do: | ||
|
||
```fortran | ||
subroutine print_eigenvalues(kappa_min, lam) | ||
integer, intent(in) :: kappa_min | ||
real(dp), intent(in) :: lam(kappa_min:) | ||
|
||
integer :: kappa | ||
do kappa = kappa_min, ubound(lam, 1) | ||
print *, kappa, lam(kappa) | ||
end do | ||
end subroutine print_eigenvalues | ||
``` |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.