A simple C++ code to solve the 1D advection equation:
Assuming you are already in the directory you want to place the code in, clone this repository from GitHub in the usual way, for example
git clone https://github.com/mirenradia/AdvecSolve.git-
CMake v3.8 or greater
- This code has been built successfully with:
- v3.8.1
- v3.16.3
- v3.17.0
- This code has been built successfully with:
-
A C++ compiler with C++17 support
- This code has been built successfully with:
- GCC
g++:- v8.4.0
- v9.3.0
- v10.2.0
- v11.1.0
- Intel C++ Compiler (Classic)
icpc:- v19.0.4.243
- LLVM Clang
clang++:- v10.0.0
- GCC
- This code has been built successfully with:
-
Note that older compiler versions with partial C++17 support will probably lead to compilation/linking errors due to the use of the
std::filesystemlibrary. -
[Optional] A non-ancient version of gnuplot for visualising the output.
- Make a
builddirectory and change into it:
mkdir /path/to/AdvecSolve/build
cd /path/to/AdvecSolve/build- Generate CMake configuration files:
cmake ..- Build using CMake:
cmake --build .Two executables will be created: advec-solve.ex and convergence-test.ex
in the build directory.
This test checks the convergence of the first order upwind evolution using initial data of a simple cosine function (since convergence can only be reliably checked with continuous initial data). To run it, simply execute the binary with no arguments:
./convergence-test.exIf successful, the final printed line will be
Convergence Test PASSED
and the code will exit with return code 0.
The parameters of the code are set by a simple parameter file:
params.txt. The parameters should be self-explanatory but there
are explanatory comments if not. To run the code, pass this as the first and
only argument to the binary, for example, in the build directory, you would
do:
./advec_solve.ex ../params.txtNote that, since the provided initial data is discontinuous and the evolution scheme used is first order upwind, a significant amount of diffusion is added to the solution which will lead to large errors for long evolutions.
After running, the code will write a single ASCII data file named
out.dat in the current directory. This contains the state vector at
regular intervals (controlled by the write_interval parameter). There will
also be an animation script named animation.gpi in the current directory
which can be used to visualise the data using
gnuplot:
gnuplot animation.gpiThe code is written in a C++ object-oriented style which may seem a little strange for those familiar with functional programming languages such as C or Fortran but it should not be too complicated. Rather than calling free functions, a solver object is created, and then member functions are called to set up and run the simulation (see Main.cpp).
All of the code is defined within a namespace:
namespace AdvecSolve;in order to avoid any conflicts with other libraries.
There are 2 classes: an abstract base evolution class:
class EvoBase;and a specialized child class
class FirstOrderUpwindBox;which inherits from EvoBase.
EvoBase is intended to be agnostic of the specific evolution scheme and
initial data. It provides/implements:
- Parameter reading and storage (
m_p) - State vector storage (
m_state_newandm_state_old) - Data output (
write_data()) - Error norm calculation - since we know the analytic solution to the advection
equation (
compute_error_l2_norm()) - Set up of initial grids (
set_initial_grids()) - The outer part of the main evolution loop in
run()which callstimestep()(defined in the child class) at each timestep
FirstOrderUpwindBox specialises EvoBase for a specific problem and evolution
scheme. It implements:
- The
timestep()function called byEvoBase::run()which implements the first-order upwind evolution scheme - The computation of the initial data (
initial_data())
If you wish to use the existing evolution scheme but just change the initial
data, you can define a new child class of FirstOrderUpwindBox which overrides
the virtual function initial_data() (for example, see ConvergenceTest.cpp).
If you wish to use a different evolution scheme, it may be easier to write your
own child class of EvoBase and implement its virtual functions as in
FirstOrderUpwindBox.
For more substantial changes, it is probably easier to read the code in order to understand how it works