Skip to content

Fortran-Julia syntax comparison and Maxwell Solver in 2D using Yee numerical scheme and MPI topology

License

Notifications You must be signed in to change notification settings

pnavaro/fortran-vs-julia

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

81 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Julia Syntax: Comparison with Fortran

This is a simple cheatsheet and some performance comparison for scientific programmers who are interested in discover Julia. It is not an exhaustive list. This page is inspired from A Cheatsheet for Fortran 2008 Syntax: Comparison with Python 3.

Fortran Julia
Top-level constructs
the main program
program my_program
    ...
end program
function my_program()
    ...
end
my_program()
Not required but it is recommended to use a function for your main program
modules
module my_module
    ...
end module my_module
module MyModule
    ...
end
subroutines
subroutine my_subroutine
    ...
end subroutine my_subroutine
function my_subroutine!
    ...
end
The bang in the function name is a convention if a function mutates one or more of its arguments. The convention is that the modified arguments should (if possible) come first.
functions
function f(x) result(res)
    res = ...
end function f
function my_function(x)
    ...
    return res
end
Generic interface
module cube_root_functions
interface cube_root
    function s_cube_root(x)
      real :: s_cube_root
      real, intent(in) :: x
    end function s_cube_root
    function d_cube_root(x)
      double precision :: d_cube_root
      double precision, intent(in) :: x
    end function d_cube_root
  end interface
end module cube_root_functions
function s_cube_root(x)
    real :: s_cube_root
    real, intent(in) :: x
    s_cube_root = x ** (1.0/3.0)
end function s_cube_root
function d_cube_root(x)
    double precision :: d_cube_root
    double precision, intent(in) :: x
    d_cube_root = x ** (1.0d0/3.0d0)
end function d_cube_root
cube_root( x :: Float32 ) :: Float32 = x^(1/3)
cube_root( x :: Float64 ) :: Float64 = x^(1/3)
submodules
module main_module
    ...
contains
<submodule statements>
end module main_module
module MainModule
    ...
    module SubModule 
        ...
    end
end
import statement
use my_module
use my_module, only : fun1, var1
using MyModule
import MyModule: fun1, var1
call subroutines and functions
call my_subroutine(args)
my_function(args)
my_function(args)
abort a program
stop
exit()
inline comments
! This is a comment
# This is a comment
include external source files
include 'source_file_name'
include("source_file_name")
Control flow patterns
if construct
if <logical expr> then
    ...
else if <logical expr> then
    ...
else
    ...
end if
if <logical expr>
    ...
elseif <logical expr>
    ...
else
    ...
end
case construct
select case <expr>
    case <value>
        ...
    case <value>
        ...
    case default
        ...
end select
Not supported. Possible alternative is to use the ternary
 ? : 
syntax:
function case(x)
    x == 1     ? println(1) : 
    x + 1 == 3 ? println(2) :
    x == 3     ? println(3) :
    println("greater than 3")
end
do construct
do i = start_value, end_value, step
    ...
end do
for i in start:step:end
    ...
end
do while construct
do while <logical expr>
    ...
end do
while <logical expr>
    ...
end
break from a loop
exit
break
leave this iteration and continue to the next iteration
cycle
continue
Data types
declaration
integer(kind=8) :: n = 0
real(kind=8) :: x = 0.
n = 0
n :: Int64 = 0
x = 0.
x :: Float64 = 0.
named constants
integer, parameter :: answer = 42
real(8), parameter :: pi = 4d0 * atan(1d0)
const answer = 42
pi is a named constant in Julia standard.
complex number
complex :: z = (1., -1.)
z = 1 - 1im
z = complex(1, -1)
string
character(len=10) :: str_fixed_length
character(len=:), allocatable :: str_var_length
string = "this is a string"
pointer
real, pointer :: p
real, target :: r
p => r
p = Ref(r)
boolean
.true.
.false.
true
false
logical operators
.not.
.and.
.or.
.eqv.
.neqv.
!
&&
||
Other logical operators do not have built-in support.
equal to
==, .eq.
==
not equal to
/=, .ne.
!==
greater than
>, .gt.
>
less than
<, .lt.
<
greater than or equal to
>=, .ge.
>=
less than or equal to
<=, .ge.
<=
array declaration
real(8), dimension(3) :: a = [1., 2., 3.]
a = [1., 2., 3.]
string array declaration
character(len=20), dimension(3, 4) :: char_arr
char_arr = String[]
push!(char_arr, new_string)
There is no easy way to preallocate space for strings.
elementwise array operations
a op b
op can be +, -, *, /, **, =, ==, etc.
This is supported since the Fortran 90 standard.
Supported by using the broadcast operator `.`and the `f.(x)` syntax
first element
a(1)
a[1] (or a[begin] for general indexing) 
slicing
a(1:5)
This slice includes a(5).
a[1:5] (or @view(a[1:5]) for non-allocating slicing)
This slice includes a[5].
slicing with steps
a(1:100:2)
a[1:2:100]
size
size(a)
length(a)
shape
shape(a)
size(a)
shape along a dimension
size(a, dim)
size(a, dim)
Type conversion
to integer by truncation
int(x)
trunc(Int, x )
to integer by rounding
nint()
round()
integer to float
real(a[, kind])
float()
complex to real
real(z[, kind])
real()
to complex
cmplx(x [, y [, kind]])
complex()
to boolean
logical()
Bool()
Derived data types
definition
type Point
    real(8) :: x, y
end type Point
struct Point
    x :: Float64
    y :: Float64
end
instantiation
type(Point) :: point1 = Point(-1., 1.)
point1 = Point(-1., 1.)
get attributes
point1%x
point1%y
point1.x
point1.y
array of derived type
type(Point), dimension(:), allocatable :: point_arr
point_arr = Vector{Point}
type bound procedures (aka class method) Assume that Circle has a type bound procedure (subroutine) print_area.
type(Circle) :: c
call c%print_area
Assume that Circle has a method print_area(c :: Circle).
c = Circle()
print_area(c)
Built-in mathematical functions
functions with the same names
abs(), cos(), cosh(), exp(), floor(), log(),
log10(), max(), min(), sin(), sinh(), sqrt(),
sum(), tan(), tanh(), acos(), asin(), atan()
Have the same name in Julia.
functions with different names
aimag()
atan2(x, y)
ceiling()
conjg(z)
modulo()
call random_number()
imag()
atan(x, y)
ceil()
conj()
mod(), %
Random.rand()
Built-in string functions
string length
len()
length()
string to ASCII code
iachar()
Int()
ASCII code to string
achar()
String()
string slicing Same as 1D array slicing. Same as 1D array slicing.
find the position of a substring
index(string, substring)
findfirst(substring, string)
string concatenation
"hello" // "world"
"hello" * "world"
string("hello", "world")
Array constructs
where construct
where a > 0
    b = 0
elsewhere
    b = 1
end where
b[a .> 0] .= 0
b[a .<= 0] .= 1
or (faster - does not allocate an intermediate array):
for i in eachindex(a)
    a[i] > 0 ? b[i] = 0 : b[i] = 1
end
Comprehension
    
integer, parameter :: n = 20
integer, parameter :: m = n*(n+1)/2
integer :: i, j
complex, dimension(m) :: a
a = [ ( ( cmplx(i,j), i=j,n), j=1,n) ]
a = [ complex(i,j) for j=1:n for i=j:n]
forall construct
real, dimension(10, 10) :: a = 0
int :: i, j
...
forall (i = 1:10, j = 1:10, i <= j)
    a(i, j) = i + j
end forall
a = zeros(Float32, 10, 10)
for i in 1:10, j in 1:10
    if i <= j
        a[i, j] = i + j
    end
end
CPU time
call cpu_time()
time = @elapsed begin
...
end
command line arguments
call command_argument_count()
call get_command()
call get_command_argument()
For basic parsing, use ARGS
Input/output
print
print fmt, <output list>
println()
read from the command prompt
read fmt, <input list>
readline()
open a file
open(unit, file, ...)
f = open(file, 'r')
read from a file
read(unit, fmt, ...) <input list>
read(f)
readlines(f)
write to a file
write(unit, fmt, ...) <output list>
write(f, ...)
close a file
close(unit, ...)
close(f)
file inquiry
inquire(unit, ...)
isfile("my_file.txt")
backspace in a file
backspace(unit, ...)
skip(f, -1)
end of file (EOF)
endfile(unit, ...)
seekend(f)
return to the start of a file
rewind(unit, ...)
seekstart(f)

Maxwell parallel solver in 2D

Here an example of a Fortran to Julia translation. We use the Yee numerical scheme FDTD: Finite-Difference Time-Domain method and MPI topology. You can find a serial version and a parallel version using MPI library.

Test your MPI.jl installation with

$ mpirun -np 4 julia --project hello_mpi.jl
Hello world, I am 0 of 4
Hello world, I am 3 of 4
Hello world, I am 1 of 4
Hello world, I am 2 of 4

Performances (without disk IO)

On small program like this in Julia is really fast.

Serial computation

1200 x 1200 and 1000 iterations.

  • julia -O3 --check-bounds=no maxwell_serial.jl : 14 seconds
  • make && time ./maxwell_serial_fortran : 31 seconds

1200 x 1200 on 9 processors and 1000 iterations

  • make && time mpirun -np 9 ./maxwell_mpi_fortran : 7 seconds
  • mpirun -np 9 julia --project -O3 --check-bounds=no : 5 seconds

Plot the magnetic field

Uncomment the plot_fields call in Julia programs or change idiag value in input_data for fortran.

gnuplot bz.gnu