Skip to content
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

Add support for parallel 2D simulations on static, uniform mesh #167

Merged
merged 87 commits into from
Oct 12, 2020
Merged
Show file tree
Hide file tree
Changes from 86 commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
d0e85da
Add MPI as a dependency
sloede Sep 3, 2020
fb633ac
Create parallel mesh struct
sloede Sep 3, 2020
8f81b0b
Add "domain_ids" to parallel mesh and comment out duplicate methods
sloede Sep 4, 2020
8b7c9b4
Add some very basic parallelization methods
sloede Sep 4, 2020
3151f4b
Initialize MPI (if not yet done) at the beginning of `run`
sloede Sep 4, 2020
98c0339
Create mesh with parallel tree if running in parallel
sloede Sep 4, 2020
177c559
Serialize startup message
sloede Sep 4, 2020
f82d34d
Read in parameters in parallel
sloede Sep 4, 2020
4bb6914
Safe-guard non-parallelized code paths
sloede Sep 4, 2020
6d6eba7
Partition mesh statically by leaf cell count
sloede Sep 5, 2020
82751ba
Use OffsetArrays to store data by domain id with 0-based indices
sloede Sep 5, 2020
05abed5
Add initial setup for MPI exchange in Dg2D
sloede Sep 7, 2020
6d8792a
Move MPI-related methods to `parallel.jl`
sloede Sep 7, 2020
aaffd56
Sort interface by global interface id
sloede Sep 7, 2020
641c15a
First working parallel DG computations on 2 domains (L2/Linf values a…
sloede Sep 7, 2020
1ca6441
Add total and serial performance index
sloede Sep 7, 2020
a271e53
Move init_mpi() to its proper place
sloede Sep 7, 2020
0c75f91
Fix parallel output
sloede Sep 8, 2020
c7e0216
Merge branch 'master' into msl/mpi_parallel
sloede Sep 8, 2020
374bd19
Merge branch 'master' into msl/mpi_parallel
sloede Sep 9, 2020
a0bb098
Calculate total number of elements and local offset
sloede Sep 9, 2020
623041a
Fix L2/Linf error calculation for MPI
sloede Sep 9, 2020
9a3b277
MVector -> SVector for `center_level_0`
sloede Sep 9, 2020
3566b47
Add MeshType to Dg2D/Dg3D parameters
sloede Sep 9, 2020
f7d2463
Remove mesh from Dg2D struct again
sloede Sep 9, 2020
d8212ca
Also clean up Dg3D constructor
sloede Sep 10, 2020
baf999d
Store `n_elements_by_domain` in solver for MPI Gatherv/Scatterv opera…
sloede Sep 10, 2020
67d2c73
Writing restart files in parallel works (it seems)
sloede Sep 10, 2020
198779e
Merge branch 'master' into msl/mpi_parallel
sloede Sep 19, 2020
5533451
Fix errors from previous merge
sloede Sep 19, 2020
da2c11c
Initialize global MPI state in __init__()
sloede Sep 21, 2020
00aee1c
Make parse_parameters_file MPI-aware
sloede Sep 21, 2020
affacfb
Use MPI.COMM_WORLD directly
sloede Sep 21, 2020
10f94a8
Make save_xxx_file MPI-aware (parallel noet yet fully working)
sloede Sep 22, 2020
43ec9bf
Merge branch 'master' into msl/mpi_parallel
sloede Sep 22, 2020
3d82fcd
Merge branch 'master' into msl/mpi_parallel
sloede Sep 22, 2020
527fe01
save_restart_file and save_solution_file work in parallel
sloede Sep 22, 2020
757e2a2
Fix that only root actually creates output files
sloede Sep 23, 2020
6263538
Simplify MPI code
sloede Sep 24, 2020
9f56ff6
Parallel restarting works
sloede Sep 25, 2020
d8e44f3
added parallel versions of analyze_solution, calc_error_norms, and in…
sloede Sep 25, 2020
ac3c87b
Rename 'domain'/'domain_id'/'n_domains' -> 'rank'/'mpi_rank'/'n_mpi_r…
sloede Sep 25, 2020
6444e9f
Collect all MPI initialization in `init_mpi()`
sloede Sep 26, 2020
d79cadf
Fix several parallel I/O issues
sloede Sep 26, 2020
5d3dbce
Move partition! to parallel.jl
sloede Sep 26, 2020
f06da84
Fix several MPI calls
sloede Sep 26, 2020
d43f3d4
Parallel output fix
sloede Sep 26, 2020
db34d04
If MPI is already initialized, query for sufficient threading support
sloede Sep 26, 2020
2ecc439
Split calc_dt in serial and parallel version
sloede Sep 26, 2020
bcc4801
Fix AMR & allow shock capturing without smoothing in parallel
sloede Sep 27, 2020
3ce35b7
Hopefully fix 3D simulation
sloede Sep 27, 2020
8b1084c
Add first documentation on how to run Trixi in parallel
sloede Sep 27, 2020
40874dd
using MPI -> import MPI
sloede Sep 28, 2020
ccc7056
Simplify type hierarchy
sloede Sep 28, 2020
f69d3f8
Reduce code duplication in `generate_mesh`
sloede Sep 28, 2020
cc29ac4
Rename `Tree` -> `SerialTree` and move all generic tree functions to …
sloede Sep 28, 2020
786cc74
Refactor get_restart_mesh_filename
sloede Sep 28, 2020
1fa802f
Add MIME"text/plain" to multi-line `Base.show` methods
sloede Sep 28, 2020
223b081
Avoid constructing another `Val(false)`
sloede Sep 28, 2020
3c392ca
Further improve potential for overlapping communication & computation
sloede Sep 28, 2020
d635cbd
Update docs/src/parallelization.md
sloede Sep 28, 2020
b84fe79
Remove unused and non-canonical overload
sloede Sep 28, 2020
b1f1057
Add 1D tests to Travis
sloede Oct 1, 2020
99f360f
Merge branch 'master' into msl/mpi_parallel
sloede Oct 2, 2020
33dc1b0
Adapt 1D solver to new mesh type infrastructur
sloede Oct 2, 2020
04537ba
Ensure correct return values (l2, linf) on all ranks
sloede Oct 2, 2020
2b54b04
First attempt at MPI-parallel Trixi tests
sloede Oct 2, 2020
d91a7ff
Enable parallel 2D tests in Travis
sloede Oct 3, 2020
18a65d4
Fix AMR for 1D (hopefully)
sloede Oct 3, 2020
11e3d5b
Update manual tests
sloede Oct 3, 2020
4fd5569
Disable module precompilation for MPI tests
sloede Oct 4, 2020
68d10e1
Remove test for non-existent `ndims` method
sloede Oct 4, 2020
7eb678c
Disable MHD test in parallel since calc_mpi_interface_flux is not yet…
sloede Oct 9, 2020
98468a3
Remove wololo
sloede Oct 9, 2020
2c021ba
Restrict MPI ranks to 2 or 3 for testing
sloede Oct 9, 2020
a2ea737
Improve coverage on `show` for mortar container
sloede Oct 9, 2020
4d43e51
Improve TreeMesh coverage
sloede Oct 9, 2020
bd3874f
Remove unused methods
sloede Oct 9, 2020
fa329ce
Improve coverage for `show` of ParallelTree
sloede Oct 9, 2020
e5f9a31
Add parallel restart test
sloede Oct 9, 2020
f296a8f
Test for reset_data_structures! for ParallelTree
sloede Oct 9, 2020
f754fb8
Comment AMR-specific I/O for parallel cse
sloede Oct 9, 2020
346239a
Add additional tests and comment out unused methods for MPI + MHD and…
sloede Oct 11, 2020
73c02a5
Fix `integrate` and `calc_error_norms` for MPI
sloede Oct 11, 2020
166f06d
Fix calc_error_norms for MPI/non-2D runs
sloede Oct 11, 2020
e8ce36c
Sort Travis jobs by descending run time (such that longer-running job…
sloede Oct 11, 2020
a7011af
Prefix MPI-related methods with `mpi_`
sloede Oct 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ env:
global:
- COVERALLS_PARALLEL=true
jobs:
- TRIXI_TEST=1D
- TRIXI_TEST=2D
- TRIXI_TEST=3D
- TRIXI_TEST=misc
- TRIXI_TEST=paper-self-gravitating-gas-dynamics
- TRIXI_TEST=parallel_2d
- TRIXI_TEST=1D
- TRIXI_TEST=misc
notifications:
webhooks: https://coveralls.io/webhook
email: false
Expand Down
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e"
LoopVectorization = "bdcacae8-1622-11e9-2a5c-532679323890"
MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195"
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Profile = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"
Expand Down
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ makedocs(
"Home" => "index.md",
"Development" => "development.md",
"Visualization" => "visualization.md",
"Parallelization" => "parallelization.md",
"Style guide" => "styleguide.md",
"GitHub & Git" => "github-git.md",
"Reference" => [
Expand Down
98 changes: 98 additions & 0 deletions docs/src/parallelization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Parallelization

## Shared-memory parallelization with threads
Many compute-intensive loops in Trixi.jl are parallelized using the
[multi-threading](https://docs.julialang.org/en/v1/manual/multi-threading/)
support provided by Julia. You can recognize those loops by the
`Threads.@threads` macro prefixed to them, e.g.,
```julia
Threads.@threads for element_id in 1:dg.n_elements
...
end
```
This will statically assign an equal iteration count to each available thread.

To use multi-threading, you need to tell Julia at startup how many threads you
want to use by either setting the environment variable `JULIA_NUM_THREADS` or by
providing the `-t/--threads` command line argument. For example, to start Julia
with four threads, start Julia with
```bash
julia -t 4
```
If both the environment variable and the command line argument are specified at
the same time, the latter takes precedence.


## Distributed computing with MPI
In addition to the shared memory parallelization with multi-threading, Trixi.jl
supports distributed parallelism via
[MPI.jl](https://github.com/JuliaParallel/MPI.jl), which leverages the Message
Passing Interface (MPI). MPI.jl comes with its own MPI library binaries such
that there is no need to install MPI yourself. However, it is also possible to
instead use an existing MPI installation, which is recommended if you are
running MPI programs on a cluster or supercomputer
([see the MPI.jl docs](https://juliaparallel.github.io/MPI.jl/stable/configuration/)
to find out how to select the employed MPI library).

To start Trixi in parallel with MPI, there are three options:

1. **Run from the REPL with `mpiexec()`:** You can start a parallel execution directly from the
REPL by executing
```julia
julia> using MPI

julia> mpiexec() do cmd
run(`$cmd -n 3 $(Base.julia_cmd()) --project=. -e 'using Trixi; Trixi.run("examples/2d/parameters.toml")'`)
end
```
The parameter `-n 3` specifies that Trixi should run with three processes (or
*ranks* in MPI parlance) and should be adapted to your available
computing resources and problem size. The `$(Base.julia_cmd())` argument
ensures that Julia is executed in parallel with the same optimization level
etc. as you used for the REPL; if this is unnecessary or undesired, you can
also just use `julia`. Further, if you are not running Trixi from a local
clone but have installed it as a package, you need to omit the `--project=.`.
2. **Run from the command line with `mpiexecjl`:** Alternatively, you can
use the `mpiexecjl` script provided by MPI.jl, which allows you to start
Trixi in parallel directly from the command line. As a preparation, you need to
install the script *once* by running
```julia
julia> using MPI

julia> MPI.install_mpiexecjl(destdir="/somewhere/in/your/PATH")
```
Then, to execute Trixi in parallel, execute the following command from your
command line:
```bash
mpiexecjl -n 3 julia --project=. -e 'using Trixi; Trixi.run("examples/2d/parameters.toml")'
```
3. **Run interactively with `tmpi` (Linux/MacOS only):** If you are on a
Linux/macOS system, you have a third option which lets you run Julia in
parallel interactively from the REPL. This comes in handy especially during
development, as in contrast to the first two options, it allows to reuse the
compilation cache and thus facilitates much faster startup times after the
first execution. It requires [tmux](https://github.com/tmux/tmux) and the
[OpenMPI](https://www.open-mpi.org) library to be installed before, both of
which are usually available through a package manager. Once you have
installed both tools, you need to configure MPI.jl to use the OpenMPI for
your system, which is explained
[here](https://juliaparallel.github.io/MPI.jl/stable/configuration/#Using-a-system-provided-MPI).
Then, you can download and install the
[tmpi](https://github.com/Azrael3000/tmpi)
script by executing
```bash
curl https://raw.githubusercontent.com/Azrael3000/tmpi/master/tmpi -o /somewhere/in/your/PATH/tmpi
```
Finally, you can start and control multiple Julia REPLs simultaneously by
running
```bash
tmpi 3 julia --project=.
```
This will start Julia inside `tmux` three times and multiplexes all commands
you enter in one REPL to all other REPLs (try for yourself to understand what
it means). If you have no prior experience with `tmux`, handling the REPL
this way feels slightly weird in the beginning. However, there is a lot of
documentation for `tmux`
[available](https://github.com/tmux/tmux/wiki/Getting-Started) and once you
get the hang of it, developing Trixi in parallel becomes much smoother this
way.
10 changes: 9 additions & 1 deletion src/Trixi.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ module Trixi
# Include other packages that are used in Trixi
# (standard library packages first, other packages next, all of them sorted alphabetically)
using LinearAlgebra: dot
using Pkg.TOML: parsefile
using Pkg.TOML: parsefile, parse
using Printf: @printf, @sprintf, println
using Profile: clear_malloc_data
using Random: seed!

using EllipsisNotation
using HDF5: h5open, attrs
import MPI
using OffsetArrays: OffsetArray, OffsetVector
using StaticArrays: @MVector, @SVector, MVector, MMatrix, MArray, SVector, SMatrix, SArray
using TimerOutputs: @notimeit, @timeit, TimerOutput, print_timer, reset_timer!
using UnPack: @unpack
Expand All @@ -37,6 +39,7 @@ export globals

# Include all top-level source files
include("auxiliary/auxiliary.jl")
include("parallel/parallel.jl")
include("equations/equations.jl")
include("mesh/mesh.jl")
include("solvers/solvers.jl")
Expand All @@ -58,4 +61,9 @@ export flux_central, flux_lax_friedrichs, flux_hll,
export examples_dir, get_examples, default_example


function __init__()
init_mpi()
end


end
18 changes: 16 additions & 2 deletions src/auxiliary/auxiliary.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,24 @@ const parameters = Dict{Symbol,Any}()


# Parse parameters file into global dict
function parse_parameters_file(filename)
parse_parameters_file(filename) = parse_parameters_file(filename, mpi_parallel())
function parse_parameters_file(filename, mpi_parallel::Val{false})
parameters[:default] = parsefile(filename)
parameters[:default]["parameters_file"] = filename
end
function parse_parameters_file(filename, mpi_parallel::Val{true})
if is_mpi_root()
buffer = read(filename)
MPI.Bcast!(Ref(length(buffer)), mpi_root(), mpi_comm())
MPI.Bcast!(buffer, mpi_root(), mpi_comm())
else
count = MPI.Bcast!(Ref(0), mpi_root(), mpi_comm())
buffer = Vector{UInt8}(undef, count[])
MPI.Bcast!(buffer, mpi_root(), mpi_comm())
end
parameters[:default] = parse(String(buffer))
parameters[:default]["parameters_file"] = filename
end


# Return parameter by name, optionally taking a default value and a range of valid values.
Expand Down Expand Up @@ -118,7 +132,7 @@ function print_startup_message()
██║ ██║ ██║██║██╔╝ ██╗██║
╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚═╝
"""
println(s)
mpi_println(s)
end


Expand Down
12 changes: 12 additions & 0 deletions src/auxiliary/containers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,15 @@ function clear!(c::AbstractContainer)

return c
end


# Helpful overloads for `raw_copy`
function raw_copy!(c::AbstractContainer, first::Int, last::Int, destination::Int)
raw_copy!(c, c, first, last, destination)
end
function raw_copy!(target::AbstractContainer, source::AbstractContainer, from::Int, destination::Int)
raw_copy!(target, source, from, from, destination)
end
function raw_copy!(c::AbstractContainer, from::Int, destination::Int)
raw_copy!(c, c, from, from, destination)
end
40 changes: 22 additions & 18 deletions src/io/io.jl
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
include("parallel.jl")

# Load restart file and store solution in solver
function load_restart_file!(dg::AbstractDg, restart_filename)
load_restart_file!(dg, restart_filename) = load_restart_file!(dg, restart_filename, mpi_parallel())
function load_restart_file!(dg::AbstractDg, restart_filename, mpi_parallel::Val{false})
# Create variables to be returned later
time = NaN
step = -1

# Open file
h5open(restart_filename, "r") do file
equation = equations(dg)

# Read attributes to perform some sanity checks
if read(attrs(file)["ndims"]) != ndims(dg)
error("restart mismatch: ndims in solver differs from value in restart file")
end
if read(attrs(file)["equations"]) != get_name(equation)
if read(attrs(file)["equations"]) != get_name(equations(dg))
error("restart mismatch: equations in solver differs from value in restart file")
end
if read(attrs(file)["polydeg"]) != polydeg(dg)
Expand All @@ -28,7 +28,7 @@ function load_restart_file!(dg::AbstractDg, restart_filename)
step = read(attrs(file)["timestep"])

# Read data
varnames = varnames_cons(equation)
varnames = varnames_cons(equations(dg))
for v in 1:nvariables(dg)
# Check if variable name matches
var = file["variables_$v"]
Expand All @@ -37,7 +37,6 @@ function load_restart_file!(dg::AbstractDg, restart_filename)
end

# Read variable
println("Reading variables_$v ($name)...")
dg.elements.u[v, .., :] = read(file["variables_$v"])
end
end
Expand All @@ -48,7 +47,10 @@ end

# Save current DG solution with some context information as a HDF5 file for
# restarting.
function save_restart_file(dg::AbstractDg, mesh::TreeMesh, time, dt, timestep)
save_restart_file(dg, mesh, time, dt, timestep) = save_restart_file(dg, mesh, time, dt, timestep,
mpi_parallel())
function save_restart_file(dg::AbstractDg, mesh::TreeMesh, time, dt, timestep,
mpi_parallel::Val{false})
# Create output directory (if it does not exist)
output_directory = parameter("output_directory", "out")
mkpath(output_directory)
Expand All @@ -62,22 +64,20 @@ function save_restart_file(dg::AbstractDg, mesh::TreeMesh, time, dt, timestep)

# Open file (clobber existing content)
h5open(filename * ".h5", "w") do file
equation = equations(dg)

# Add context information as attributes
attrs(file)["ndims"] = ndims(dg)
attrs(file)["equations"] = get_name(equation)
attrs(file)["equations"] = get_name(equations(dg))
attrs(file)["polydeg"] = polydeg(dg)
attrs(file)["n_vars"] = nvariables(dg)
attrs(file)["n_elements"] = dg.n_elements
attrs(file)["n_elements"] = dg.n_elements_global
attrs(file)["mesh_file"] = splitdir(mesh.current_filename)[2]
attrs(file)["time"] = time
attrs(file)["dt"] = dt
attrs(file)["timestep"] = timestep

# Restart files always store conservative variables
data = dg.elements.u
varnames = varnames_cons(equation)
varnames = varnames_cons(equations(dg))

# Store each variable of the solution
for v in 1:nvariables(dg)
Expand All @@ -95,6 +95,11 @@ end
# Save current DG solution with some context information as a HDF5 file for
# postprocessing.
function save_solution_file(dg::AbstractDg, mesh::TreeMesh, time, dt, timestep, system="")
return save_solution_file(dg::AbstractDg, mesh::TreeMesh, time, dt, timestep, system,
mpi_parallel())
end
function save_solution_file(dg::AbstractDg, mesh::TreeMesh, time, dt, timestep, system,
mpi_parallel::Val{false})
# Create output directory (if it does not exist)
output_directory = parameter("output_directory", "out")
mkpath(output_directory)
Expand All @@ -112,11 +117,9 @@ function save_solution_file(dg::AbstractDg, mesh::TreeMesh, time, dt, timestep,

# Open file (clobber existing content)
h5open(filename * ".h5", "w") do file
equation = equations(dg)

# Add context information as attributes
attrs(file)["ndims"] = ndims(dg)
attrs(file)["equations"] = get_name(equation)
attrs(file)["equations"] = get_name(equations(dg))
attrs(file)["polydeg"] = polydeg(dg)
attrs(file)["n_vars"] = nvariables(dg)
attrs(file)["n_elements"] = dg.n_elements
Expand All @@ -130,15 +133,15 @@ function save_solution_file(dg::AbstractDg, mesh::TreeMesh, time, dt, timestep,
valid=["conservative", "primitive"])
if solution_variables == "conservative"
data = dg.elements.u
varnames = varnames_cons(equation)
varnames = varnames_cons(equations(dg))
else
# Reinterpret the solution array as an array of conservative variables,
# compute the primitive variables via broadcasting, and reinterpret the
# result as a plain array of floating point numbers
data = Array(reinterpret(eltype(dg.elements.u),
cons2prim.(reinterpret(SVector{nvariables(dg),eltype(dg.elements.u)}, dg.elements.u),
Ref(equations(dg)))))
varnames = varnames_prim(equation)
varnames = varnames_prim(equations(dg))
end

# Store each variable of the solution
Expand All @@ -165,7 +168,8 @@ end


# Save current mesh with some context information as an HDF5 file.
function save_mesh_file(mesh::TreeMesh, timestep=-1)
save_mesh_file(mesh, timestep=-1) = save_mesh_file(mesh, timestep, mpi_parallel())
function save_mesh_file(mesh::TreeMesh, timestep, mpi_parallel::Val{false})
# Create output directory (if it does not exist)
output_directory = parameter("output_directory", "out")
mkpath(output_directory)
Expand Down
Loading