Fortran Correspondence Table

This table is for looking up how to write in C++ and MISO what you used to write in Fortran. Find the Fortran form in the left column, then go to the form in the right column and to the chapter that explains it. The correspondences are between “forms used for the same purpose”, so the meanings are not always exactly the same. Rows with differences carry a note. Source paths are relative to the folder where you cloned MISO.

Contents

1. Program structure

Fortran

C++ / MISO

Notes

Explained in

program main to end program

int main(int argc, char **argv) { ... }

argc and argv are the command-line arguments

Chapter 2

implicit none

Not needed

In C++, you declare every variable before using it. There is no implicit typing

Chapter 2

! comment

// comment, /* ... */

Chapter 2

Line continuation &

Not needed

A statement ends with ;. A newline does not end a statement

Chapter 2

Case-insensitive

Case-sensitive

Real and real are different names

Chapter 2

end do, end if

}

The range enclosed in { } is a block

Chapter 2

include 'file.inc'

#include "file.hpp"

#include expands the contents of the file at that location

Chapter 2

Source file .F90 (with the preprocessor)

Header .hpp and implementation .cpp. MISO uses headers only (header-only)

Add inline to non-template functions in a header

Chapter 2

Call the initializations call xxx_init one after another

Creating an object runs its constructor (Model model(config);)

The destructor does the cleanup automatically

Chapter 4, Chapter 11

2. Types, declarations, and constants

Fortran

C++ / MISO

Notes

Explained in

integer :: n

int n;

Inside a function, int n; has an undefined value. Initialize it, as in int n = 0;

Chapter 2

real :: x (single precision)

float x;

Chapter 2

real(8) :: x, double precision :: x

double x;

Chapter 2

integer, parameter :: dp = kind(1.0d0) and real(dp) :: x

using Real = double; and Real x;

The MISO demos use using Real = float;. MISO components receive Real as a template argument

Chapter 2, Chapter 6

1.0 (single precision by default), 1.0d0, 1.0_dp

1.0f (float), 1.0 (double), Real(1.0)

1.0 in C++ is double precision. If you mix 0.5 into a calculation where Real is float, it is computed in double precision, so in MISO you write Real(0.5)

Chapter 2

logical :: f = .true.

bool f = true;

Chapter 2

character(len=*) :: s, // (concatenation)

std::string s;, +

The length is determined automatically, and trim is not needed

Chapter 2

real, parameter :: pi = 3.14159

constexpr double pi = 3.14159;, const

In MISO, \(\pi\) is pi<Real> (a variable template, miso/include/miso/constants.hpp)

Chapter 2, Chapter 6

real(x), dble(n) (type conversion)

Real(x), static_cast<double>(n)

Chapter 2

int(x)

static_cast<int>(x)

Both truncate toward zero

Chapter 2

There is no way to determine the type from the right-hand side without writing it

auto x = ...;

The type of the right-hand side becomes the type of the variable

Chapter 2

3. Operators and intrinsic functions

Fortran

C++ / MISO

Notes

Explained in

x**2, x**3

util::pow2(x), util::pow3(x) (miso/include/miso/utility.hpp), std::pow(x, y)

C++ has no exponentiation operator. ^ is exclusive OR

Chapter 2

7 / 2 (integer division)

7 / 2

Both give 3

Chapter 2

mod(a, b)

a % b (integers), std::fmod(a, b) (floating point)

The sign rule is the same for negative numbers (the sign of the dividend)

Chapter 2

==, /=, .eq., .ne.

==, !=

In C++, /= means “divide and assign”

Chapter 2

.and., .or., .not.

&&, ||, !

Chapter 2

x = x + 1

x = x + 1;, x += 1;, ++x;

Chapter 2

sqrt, exp, sin, abs

util::sqrt, util::exp, util::sin, util::fabs

The util:: functions support both float and double, and can also be called on the GPU

Chapter 2, Chapter 9

max(a, b), min(a, b, c)

util::max2(a, b), util::min3(a, b, c)

Chapter 2

sign(a, b)

util::copysign(a, b)

Chapter 2

write(str, '(i8.8)') n (zero-padded)

util::zfill(n, 8)

Used for output file names

Chapter 2

4. Control statements

Fortran

C++ / MISO

Notes

Explained in

if (c) then to else if to else to end if

if (c) { } else if (c2) { } else { }

Enclose the condition in ( )

Chapter 2

do i = 1, n

for (int i = 0; i < n; ++i)

It starts from 0 and does not include the upper limit n. The number of iterations is the same, n

Chapter 2

do i = n, 1, -1

for (int i = n - 1; i >= 0; --i)

Chapter 2

do while (t < tend)

while (t < tend) { }

The MISO time loop is while (time.time < time.tend) in ModelBase::run

Chapter 2, Chapter 11

exit, cycle

break, continue

continue in C++ means “go to the next iteration”, which is different from the Fortran continue statement (which does nothing).

Chapter 2

select case (d) to case (1)

switch (d) { case Direction::X: ...; break; }

Without a break at the end of each case, the next case is also executed

Chapter 2

Choose the code with #ifdef (at compile time)

#ifdef, or if constexpr

if constexpr chooses by type or constant, and the branch not chosen is not compiled

Chapter 2, Chapter 6

5. Arrays

5.1 Declaration and allocation

Fortran

C++ / MISO

Notes

Explained in

real, allocatable :: a(:,:,:) and allocate(a(nx, ny, nz))

Array3D<Real> a(nx, ny, nz);

Declaration and allocation happen together. With Array3D<Real, backend::CUDA>, the array is allocated on the GPU

Chapter 7

deallocate(a)

Not needed

The destructor frees a when its lifetime ends

Chapter 4, Chapter 7

real :: a(n) (1-D)

Array1D<Real> a(n);, std::vector<Real> a(n);

Chapter 2, Chapter 7

real :: q(nx, ny, nz, 9) (variables along the 4th index)

The nine members of Fields: ro, vx, … ph (each an Array3D)

MISO keeps a separate array for each variable (miso/include/miso/mhd_fields.hpp)

Chapter 4, Chapter 11

size(a), size(a, 1)

a.size(), a.extent(0)

Dimension numbers are also counted from 0

Chapter 7

5.2 Index base and ghost cells

Fortran

C++ / MISO

Notes

Explained in

a(1) is the first element

a(0) is the first element

Chapter 2, Chapter 7

real :: a(1-m:nx+m) (set the lower bound and hold ghost cells)

The index always runs from 0 to i_total - 1. The interior grid points run from i_margin to i_total - i_margin - 1

A MISO array cannot change its lower bound. i_total = i_size + 2 * i_margin (miso/include/miso/grid.hpp)

Chapter 8

do i = 1, nx (interior grid points)

for (int i = grid.i_margin; i < grid.i_total - grid.i_margin; ++i)

Chapter 8, Chapter 11

5.3 Memory layout

In Fortran, the first index is contiguous (column-major); in MISO’s Array3D, the last index is contiguous (row-major). Even when you write the same a(i, j, k), the elements that are adjacent in memory are different.

Fortran a(i, j, k)

MISO a(i, j, k)

Adjacent element in memory

a(i+1, j, k)

a(i, j, k+1)

Loop to run fast (innermost)

i

k

Element position (a(nx, ny, nz))

i + nx*(j + ny*k) (converted to 0-based)

(i*ny + j)*nz + k

The following short program prints how far each element of a MISO Array3D is from the start of memory (an example for explanation).

// order.cpp: check the memory layout of Array3D
#include <iostream>

#include <miso/array3d.hpp>

int main() {
  miso::Array3D<double> a(2, 3, 4);  // a(i, j, k): i < 2, j < 3, k < 4
  const double *p = a.data();       // address of the first element
  // difference between an element's address and the first address = offset from the start
  std::cout << "a(0,0,1): " << &a(0, 0, 1) - p << "\n";
  std::cout << "a(0,1,0): " << &a(0, 1, 0) - p << "\n";
  std::cout << "a(1,0,0): " << &a(1, 0, 0) - p << "\n";
  std::cout << "a(1,2,3): " << &a(1, 2, 3) - p << "\n";
}
a(0,0,1): 1
a(0,1,0): 4
a(1,0,0): 12
a(1,2,3): 23

Increasing k by 1 moves to the adjacent element (1 away), increasing j by 1 moves 4 elements ahead, and increasing i by 1 moves \(3 \times 4 = 12\) elements ahead. If you create an array of the same size in Fortran, a(2, 3, 4), then a(2, 1, 1) is the second element from the start (1-based), and the i direction is contiguous. MISO output files (mhd/mhd.*.bin) are also written in this order, so be careful about the order of the indices when you read them in Fortran or NumPy.

5.4 Array expressions and subarrays

Fortran

C++ / MISO

Notes

Explained in

a = 0.0 (assignment to the whole array)

A loop, or for_each

C++ array types have no notation for whole-array operations

Chapter 7

a(:,:,:) = b(:,:,:)

a.copy_from(b);

Also used for copies between the CPU and the GPU

Chapter 7, Chapter 9

maxval(a), minval(a), sum(a)

reduce(btag, range, init, map, op)

For the minimum, use util::min2 as op

Chapter 7

where (a < 0) a = 0

if inside a loop

Chapter 7

Pass a subarray as an argument: call s(a(:, :, k))

Pass a View (such as Array3DView), or pass the index range as arguments

A MISO View is a lightweight window onto the whole array. It cannot cut out a subarray

Chapter 4, Chapter 7

6. Procedures (subroutine and function)

Fortran

C++ / MISO

Notes

Explained in

subroutine s(a, b) and call s(a, b)

void s(T a, U b) { } and s(a, b);

A function with no return value

Chapter 2

function f(x) result(y)

Real f(Real x) { return y; }

Write the return type before the name and return the value with return

Chapter 2

Arguments are passed by reference (default)

Arguments are passed by value (default). To pass by reference, use T &a

Changing a inside void s(Real a) does not change the caller’s variable

Chapter 3

intent(in)

Pass small values by value (Real x) and large ones as const T &x

A const reference avoids the copy and also forbids modification

Chapter 3

intent(out), intent(inout)

T &x

Chapter 3

optional and present(x)

Default arguments void s(int n = 1), std::optional<T> with has_value() and value_or()

parse_config_filepath returns std::optional<std::string> (miso/include/miso/config.hpp)

Chapter 2, Chapter 4

Bundle several procedures under one name with interface (generic name)

Define functions with the same name and different argument types (overloading), or templates

space_centered_4th comes in three versions, taking 1 to 3 arrays (miso/include/miso/mhd_integrator.hpp)

Chapter 2, Chapter 6

Write the argument form of an external procedure with interface (explicit interface)

Function declaration (written in the header)

Chapter 2

Write a real version and a real(8) version separately

Function template template <typename Real> Real f(Real x)

One definition generates a function for each type

Chapter 6

pure, elemental

Call a __host__ __device__ inline function inside for_each

Add __host__ __device__ to functions called from the GPU

Chapter 7, Chapter 9

recursive

Not needed

A C++ function can call itself as is (merge_yaml)

Chapter 11

Internal procedures in contains

Lambda expression [=](int i) { ... }; in MISO, MISO_LAMBDA(int i) { ... }

Copies the surrounding variables into the lambda (capture)

Chapter 7

Pass a procedure as an argument (external, procedure dummy argument)

Pass a function object or lambda as an argument (for_each(btag, range, f))

Chapter 7

7. module and use

Fortran

C++ / MISO

Notes

Explained in

module m to contains to end module

A header file and namespace miso { ... }

The namespace avoids name collisions, and #include shares the contents

Chapter 2

use m

#include <miso/core.hpp> and using namespace miso;

Without using namespace, write the name with its namespace, as in miso::Config

Chapter 2

use m, only: f

using miso::limiter::dqq_eval;

Makes only one name available (miso/include/miso/mhd_artificial_viscosity_host.hpp)

Chapter 2

Submodules or nested classification

Nested namespaces miso::mhd, miso::util

Chapter 2

private (used only inside the module)

By convention, put it in a namespace such as impl_host

Namespaces have no private. Class members do have private

Chapter 2, Chapter 4

module variable (one for the whole program)

inline variable in a namespace

The MPI rank is mpi::world::rank (miso/include/miso/env.hpp)

Chapter 2, Chapter 8

Local variable with the save attribute

static local variable

Chapter 2

8. Derived types

Fortran

C++ / MISO

Notes

Explained in

type :: t to real :: x to end type

struct T { Real x; };

Chapter 4

type(t) :: a and a%x

T a; and a.x

Access a component with ., not %. Through a pointer, use p->x

Chapter 4

Structure constructor a = t(1.0)

Constructor T a(1.0);

Initialize members with a member initializer list : x(x_)

Chapter 4

Type-bound procedure procedure :: apply and call a%apply()

Member function void apply() const; and a.apply();

A const member function does not modify members

Chapter 4

final procedure

Destructor ~T()

Called automatically at the end of the lifetime

Chapter 4

extends(t) (type extension)

Inheritance struct U : public T { };

Chapter 5

Polymorphism with class(t) and deferred procedures

Virtual functions (virtual), or CRTP

MISO does not use virtual functions; it calls the derived class’s components through the CRTP of ModelBase<Model, Real, Backend>

Chapter 5

Parameterized derived type type t(k)

Class template template <typename Real> struct T

Chapter 6

9. pointer and allocatable

Fortran

C++ / MISO

Notes

Explained in

Alias of a dummy argument (modify the array received as an argument)

Reference T &r = a;

A reference is bound to its target when created and cannot be rebound

Chapter 3

real, pointer :: p and p => x (target attribute)

Real *p = &x; and *p

&x is the address and *p is the value pointed to. The meaning of * and & changes between declarations and expressions

Chapter 3

nullify(p), associated(p)

p = nullptr;, p != nullptr

Chapter 3

Point to an array with an array pointer and pass it

View (a.view(), a.const_view())

A View is a lightweight window that does not allocate or free memory, and it can be copied into a lambda and passed to the GPU as well

Chapter 4, Chapter 7

Derived type with allocatable components

A struct that has Array3D and similar types as members (Fields)

Copying is forbidden or explicit (copy_from)

Chapter 4

Forgetting to deallocate a pointer that was allocated

Do not write new/delete directly; let Array3D or std::unique_ptr own the memory

Chapter 3

10. Input/output and error handling

Fortran

C++ / MISO

Notes

Explained in

print *, x, write(*,*) x

std::cout << x << "\n";

Chapter 2

Read settings with namelist

A YAML configuration file and Config (config["time"]["tend"].as<Real>())

Values you did not write are filled in with the defaults in config_defaults.hpp

Chapter 11

open(..., form='unformatted', access='stream') and write(u) a

std::ofstream ofs(name, std::ios::binary); and ofs.write(...)

MISO output is in miso/include/miso/mhd_checkpoint.hpp

Chapter 11

Check for failure with iostat

if (!ofs.is_open()), if (!ofs)

Chapter 11

stop, error stop

throw std::runtime_error("..."), or MPI_Abort to stop all of MPI

MISO reports runtime errors with exceptions

Chapter 2

Concatenate file names as strings

std::filesystem::path and /

Write it like fs::path(dir) / "config.yaml"

Chapter 2

11. MPI

Fortran

C++ / MISO

Notes

Explained in

use mpi, include 'mpif.h'

#include <mpi.h>

Chapter 8

call MPI_Init(ierr) to call MPI_Finalize(ierr)

Env env(argc, argv); (calls MPI_Init(&argc, &argv) and calls MPI_Finalize at the end of its lifetime)

Chapter 8

Last argument ierr

The function’s return value

In C++, you do not write ierr as an argument

Chapter 8

MPI_COMM_WORLD

MPI_COMM_WORLD; in MISO, mpi::comm()

Chapter 8

MPI_REAL, MPI_DOUBLE_PRECISION

MPI_FLOAT, MPI_DOUBLE. In MISO, mpi::data_type<Real>()

Chosen to match Real (miso/include/miso/mpi_util.hpp)

Chapter 6, Chapter 8

call MPI_Allreduce(dt, dtg, 1, MPI_REAL, MPI_MIN, MPI_COMM_WORLD, ierr)

MPI_Allreduce(&dt, &dtg, 1, mpi::data_type<Real>(), MPI_MIN, mpi::comm());

For a scalar, pass the address with &dt; for an array, pass the address of the first element with a.data()

Chapter 3, Chapter 8

integer :: req(12), MPI_STATUSES_IGNORE

MPI_Request reqs[12];, MPI_STATUSES_IGNORE

Chapter 8

type(MPI_Comm) of use mpi_f08

MPI_Comm

Chapter 8

Rank number, coordinates from MPI_Cart_coords

Same

Ranks and coordinates start from 0 in Fortran too

Chapter 8

MPI_Cart_create, MPI_Cart_shift, MPI_PROC_NULL

The same functions (mpi::Shape)

In a periodic-boundary direction, the neighbor of one end is the opposite end

Chapter 8, Chapter 11

12. Parallelization directives and building

Fortran

C++ / MISO

Notes

Explained in

!$omp parallel do

for_each(backend::Host{}, range, f)

MISO does not use OpenMP. The CPU version of for_each is an ordinary for statement

Chapter 7

!$omp parallel do reduction(min:dt)

reduce(btag, range, init, map, op)

Chapter 7

!$acc parallel loop, CUDA Fortran kernels

for_each(backend::CUDA{}, range, f) and MISO_LAMBDA

The same lambda runs on both the CPU and the GPU

Chapter 9

!$acc data copyin(a), update host(a)

Array3D<Real, backend::CUDA> and copy_from

Chapter 9

Macros such as -DUSE_CUDA

Same (#ifdef USE_CUDA). In CMake, target_compile_definitions

Chapter 1, Chapter 2

Makefile

CMakeLists.txt with cmake -B build -S . and cmake --build build

Chapter 1

mpif90

mpicxx (in CMake, find_package(MPI))

Chapter 1

You write a calculation that combines the forms covered so far yourself in the heat conduction exercise of Chapter 10, and read it as the time evolution of MISO itself in Chapter 11.