This shows Python/NumPy code side by side with Fortran. The Python code is simply executed directly. The Fortran code must be prefixed by the %%fortran
magic, which is defined in the fortranmagic.py
module, loaded by:
%load_ext fortranmagic
Then we take the Fortran code, if it starts with module
or program
, we leave it untouched, compile, execute and show the output (or compiler errors if it fails to compile).
If the code doesn't start with either module
or program
, then we intelligently prepend implicit none
(i.e. after any possible use
lines) and append end
, that way you can write Fortran code directly.
from numpy import array, size, shape, min, max, sum
a = array([1, 2, 3])
print shape(a)
print size(a)
print max(a)
print min(a)
print sum(a)
(3,) 3 3 1 6
%%fortran
integer :: a(3)
a = [1, 2, 3]
print *, shape(a)
print *, size(a)
print *, maxval(a)
print *, minval(a)
print *, sum(a)
3 3 3 1 6
from numpy import reshape
a = reshape([1, 2, 3, 4, 5, 6], (2, 3))
b = reshape([1, 2, 3, 4, 5, 6], (2, 3), order="F")
print a[0, :]
print a[1, :]
print
print b[0, :]
print b[1, :]
[1 2 3] [4 5 6] [1 3 5] [2 4 6]
%%fortran
integer :: a(2, 3), b(2, 3)
a = reshape([1, 2, 3, 4, 5, 6], [2, 3], order=[2, 1])
b = reshape([1, 2, 3, 4, 5, 6], [2, 3])
print *, a(1, :)
print *, a(2, :)
print *
print *, b(1, :)
print *, b(2, :)
1 2 3 4 5 6 1 3 5 2 4 6
from numpy import array, size, shape, max, min
a = array([[1, 2, 3], [4, 5, 6]])
print shape(a)
print size(a, 0)
print size(a, 1)
print max(a)
print min(a)
print a[0, 0], a[0, 1], a[0, 2]
print a[1, 0], a[1, 1], a[1, 2]
print a
(2, 3) 2 3 6 1 1 2 3 4 5 6 [[1 2 3] [4 5 6]]
%%fortran
integer :: a(2, 3)
a = reshape([1, 2, 3, 4, 5, 6], [2, 3], order=[2, 1])
print *, shape(a)
print *, size(a, 1)
print *, size(a, 2)
print *, maxval(a)
print *, minval(a)
print *, a(1, 1), a(1, 2), a(1, 3)
print *, a(2, 1), a(2, 2), a(2, 3)
print "(3i5)", transpose(a)
2 3 2 3 6 1 1 2 3 4 5 6 1 2 3 4 5 6
from numpy import array, dot
a = array([[1, 2], [3, 4]])
b = array([[2, 3], [4, 5]])
print a * b
print dot(a, b)
[[ 2 6] [12 20]] [[10 13] [22 29]]
%%fortran
integer :: a(2, 2), b(2, 2)
a = reshape([1, 2, 3, 4], [2, 2], order=[2, 1])
b = reshape([2, 3, 4, 5], [2, 2], order=[2, 1])
print *, a * b
print *, matmul(a, b)
2 12 6 20 10 22 13 29
%%fortran
module a
implicit none
integer :: i = 5
contains
integer function f(x) result(r)
integer, intent(in) :: x
r = x + 5
end function
integer function g(x) result(r)
integer, intent(in) :: x
r = x - 5
end function
end module
program main
use a, only: f, i
implicit none
print *, f(3)
print *, i
end program
8 5
The fortranmagic
implicitly defines two modules: types
and constants
(they contain code that most Fortran programmers tend define in all their projects one way or another):
module types
implicit none
private
public dp
integer, parameter :: dp=kind(0.d0) ! double precision
end module
module constants
use types, only: dp
implicit none
private
public pi, e_, i_
! Constants contain more digits than double precision, so that
! they are rounded correctly. Single letter constants contain underscore so
! that they do not clash with user variables ("e" and "i" are frequently used as
! loop variables)
real(dp), parameter :: pi = 3.1415926535897932384626433832795_dp
real(dp), parameter :: e_ = 2.7182818284590452353602874713527_dp
complex(dp), parameter :: i_ = (0, 1)
end module
NOTE: The formatting above is wrong due to a bug in IPython.
Here is how to use them:
%%fortran
program test
use types, only: dp
implicit none
real(dp) x
x = 5.3_dp
print *, x
end
5.2999999999999998
%%fortran
use types, only: dp
real(dp) x
x = 5.3_dp
print *, x
5.2999999999999998
Show the famous $e^{i \pi}=-1$ relation using Fortran:
%%fortran
use types, only: dp
use constants, only: pi, i_
complex(dp) x
x = exp(pi*i_)
print *, x
( -1.0000000000000000 , 1.22464679914735321E-016)
and Python:
from cmath import exp, pi
x = exp(pi*1j)
print x
(-1+1.22460635382e-16j)
Undefined variables:
%%fortran
print *, i ! undefined variable
err: a.f90:23.10: print *, i ! undefined variable 1 Error: Symbol 'i' at (1) has no IMPLICIT type *** FAILED TO COMPILE ***
%%fortran
use types, only: dp
real(dp) :: x
x = 5
print *, x
print *, i ! undefined variable
err: a.f90:27.10: print *, i ! undefined variable 1 Error: Symbol 'i' at (1) has no IMPLICIT type *** FAILED TO COMPILE ***
Invalid code:
%%fortran
integer :: i
i = "str"
err: a.f90:24.4: i = "str" 1 Error: Can't convert CHARACTER(1) to INTEGER(4) at (1) *** FAILED TO COMPILE ***
Fortran checks bounds of arrays, both at compile time if it has enough information:
%%fortran
use types, only: dp
real(dp) x(5)
x = [1, 2, 3, 4, 5]
print *, "OK:", x
x = [1, 2, 3, 4, 5, 6]
print *, "FAIL:", x
err: a.f90:27: x = [1, 2, 3, 4, 5, 6] 1 Error: Different shape for array assignment at (1) on dimension 1 (5 and 6) *** FAILED TO COMPILE ***
and at runtime, in debug mode (in this case Fortran actually also warns during compile time):
%%fortran
use types, only: dp
real(dp) x(5)
x = [1, 2, 3, 4, 5]
x(5) = 0
print *, "OK:", x
x(6) = 0 ! Fails
err: a.f90:28.2: x(6) = 0 ! Fails 1 Warning: Array reference at (1) is out of bounds (6 > 5) in dimension 1 OK: 1.0000000000000000 2.0000000000000000 3.0000000000000000 4.0000000000000000 0.0000000000000000 err: At line 28 of file a.f90 Fortran runtime error: Index '6' of dimension 1 of array 'x' above upper bound of 5 Backtrace for this error: + /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7faf148e676d] Non-zero exit code: 2
Here it only fails at runtime:
%%fortran
program test
use types, only: dp
implicit none
real(dp) x(5)
x = [1, 2, 3, 4, 5]
call modify(x, 5)
print *, "OK:", x
call modify(x, 6) ! Fails
contains
subroutine modify(a, i)
real(dp), intent(inout) :: a(:)
integer, intent(in) :: i
a(i) = 0
end subroutine
end program
OK: 1.0000000000000000 2.0000000000000000 3.0000000000000000 4.0000000000000000 0.0000000000000000 err: At line 36 of file a.f90 Fortran runtime error: Index '6' of dimension 1 of array 'a' above upper bound of 5 Backtrace for this error: + function modify (0x4008A1) at line 36 of file a.f90 + function test (0x400A6F) at line 29 of file a.f90 + /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7f597521b76d] Non-zero exit code: 2