1. Riemann Solver
F-wave Solver
Header file: .../Tsunami-Simulation/include/solvers/FWave.h
Implementation file: .../Tsunami-Simulation/src/solvers/FWave.cpp
Test file: .../Tsunami-Simulation/src/solvers/FWave.test.cpp
“The f-wave solver approximately solves the following Initial Value Problem (IVP) for the shallow water equations over time”[1] :
“Theory shows that the solution, arising from the discontinuity at \(x=0\), consist of two waves. Each wave is either a shock or a rarefaction wave. The f-wave solver uses two shock waves to approximate the true solution.”[1]
“First, we use the Roe eigenvalues \(\lambda^{\text{Roe}}_{1/2}\) in terms of the left and right quantities \(q_l\) and \(q_r\) with respect to position \(x=0\) to approximate the true wave speeds:”[1]
“where the height \(h^{\text{Roe}}\) and particle velocity \(u^{\text{Roe}}\) are given as:”[1]
void tsunami_lab::solvers::FWave::computeEigenvalues(t_real i_hL,
t_real i_hR,
t_real i_uL,
t_real i_uR,
t_real & o_eigenvalue1,
t_real & o_eigenvalue2){
// pre-compute square-root ops
t_real l_hSqrtL = std::sqrt( i_hL );
t_real l_hSqrtR = std::sqrt( i_hR );
// compute averages
t_real l_h = 0.5f * ( i_hL + i_hR );
t_real l_u = l_hSqrtL * i_uL + l_hSqrtR * i_uR;
l_u /= l_hSqrtL + l_hSqrtR;
// compute eigenvalues
t_real l_ghSqrtRoe = m_gSqrt * std::sqrt( l_h );
o_eigenvalue1 = l_u - l_ghSqrtRoe;
o_eigenvalue2 = l_u + l_ghSqrtRoe;
}
Compute flux function:
void tsunami_lab::solvers::FWave::computeDeltaFlux(t_real i_hL,
t_real i_hR,
t_real i_uL,
t_real i_uR,
t_real o_deltaFlux[2]){
t_real i_huL = i_hL * i_uL;
t_real i_huR = i_hR * i_uR;
o_deltaFlux[0] = i_huR - i_huL;
o_deltaFlux[1] = (i_hR * i_uR * i_uR + 0.5f * m_g * i_hR * i_hR)
-(i_hL * i_uL * i_uL + 0.5f * m_g * i_hL * i_hL);
}
“The eigencoefficients \(\alpha_p\) in Equation are obtained by multiplying the inverse of the matrix of right eigenvectors \(R=[r_1^\text{Roe}, r_2^\text{Roe}]\) with the jump in fluxes:”[1]
void tsunami_lab::solvers::FWave::computeEigencoefficients(t_real i_eigenvalue1,
t_real i_eigenvalue2,
t_real i_deltaFlux[2],
t_real &o_eigencoefficient1,
t_real &o_eigencoefficient2) {
// compute inverse matrix
t_real denominator = i_eigenvalue2 - i_eigenvalue1;
t_real invertedMatrix[2][2] = {
{i_eigenvalue2 / denominator, -1 / denominator},
{-i_eigenvalue1 / denominator, 1 / denominator}
};
// compute eigencoefficients
o_eigencoefficient1 = invertedMatrix[0][0] * i_deltaFlux[0] + invertedMatrix[0][1] * i_deltaFlux[1];
o_eigencoefficient2 = invertedMatrix[1][0] * i_deltaFlux[0] + invertedMatrix[1][1] * i_deltaFlux[1];
}
“Using the eigenvalues we can define corresponding eigenvectors \(r_{1/2}^{\text{Roe}}\)”[1]
Using the eigenvectors and our eigencoefficients we can compute the waves \(Z_{1/2}\)
“This leads to the definition of net updates which summarize the net effect of the waves to the left and right “cell”:”[1]
void tsunami_lab::solvers::FWave::netUpdates(t_real i_hL,
t_real i_hR,
t_real i_huL,
t_real i_huR,
t_real *o_netUpdateL,
t_real *o_netUpdateR) {
// compute particle velocities
t_real l_uL = i_huL / i_hL;
t_real l_uR = i_huR / i_hR;
// compute eigenvalues
t_real eigenvalue1 = 0;
t_real eigenvalue2 = 0;
computeEigenvalues(i_hL, i_hR, l_uL, l_uR, eigenvalue1, eigenvalue2);
// create eigenvectors
t_real eigenvector1[2] = {1, eigenvalue1};
t_real eigenvector2[2] = {1, eigenvalue2};
// compute delta flux
t_real deltaFlux[2];
computeDeltaFlux(i_hL, i_hR, l_uL, l_uR, deltaFlux);
// compute eigencoefficients
t_real eigencoefficient1 = 0;
t_real eigencoefficient2 = 0;
computeEigencoefficients(eigenvalue1, eigenvalue2, deltaFlux, eigencoefficient1, eigencoefficient2);
// compute waves / net updates
for( unsigned short l_qt = 0; l_qt < 2; l_qt++ )
{
// init
o_netUpdateL[l_qt] = 0;
o_netUpdateR[l_qt] = 0;
// 1st wave
if( eigenvalue1 < 0 )
{
o_netUpdateL[l_qt] += eigencoefficient1 * eigenvector1[l_qt];
}
else
{
o_netUpdateR[l_qt] += eigencoefficient1 * eigenvector1[l_qt];
}
// 2nd wave
if( eigenvalue2 < 0 )
{
o_netUpdateL[l_qt] += eigencoefficient2 * eigenvector2[l_qt];
}
else
{
o_netUpdateR[l_qt] += eigencoefficient2 * eigenvector2[l_qt];
}
}
}
F-wave Solver Unit Testing
Test the derivation of the F-Wave eigenvalues. Using the equation stated in (1.3.2) to calculate the test cases.
TEST_CASE( "Test the derivation of the F-Wave eigenvalues.", "[FWaveEigenvalue]" )
{
/*
* Test case:
* h: 10 | 9
* u: -3 | 3
*
* height: 9.5
* velocity: (sqrt(10) * -3 + 3 * 3) / ( sqrt(10) + sqrt(9) )
* = -0.0790021169691720
* eigenvalue: s1 = -0.079002116969172024 - sqrt(9.80665 * 9.5) = -9.7311093998375095
* s2 = -0.079002116969172024 + sqrt(9.80665 * 9.5) = 9.5731051658991654
*/
t_real l_eigenvalue1 = 0;
t_real l_eigenvalue2 = 0;
tsunami_lab::solvers::FWave::computeEigenvalues( 10,
9,
-3,
3,
l_eigenvalue1,
l_eigenvalue2 );
REQUIRE( l_eigenvalue1 == Approx( -9.7311093998375095 ) );
REQUIRE( l_eigenvalue2 == Approx( 9.5731051658991654 ) );
}
Test the computation of the delta flux. Using the equation flux function to calculate these test cases.
TEST_CASE( "Test the computation of the delta flux.", "[FWaveDeltaFlux]")
{
/*
* Test case:
* h: 10 | 9
* u: -3 | 3
*
* height: 9.5
* delta f: {9 * 3, 9 * 3 * 3 + 0.5 * m_g * 9 * 9} - {10 * -3, 10 * -3 * -3 + 0.5 * m_g * 10 * 10}
* delta f: {57, -102.163175}
*/
t_real l_deltaFlux[2] = {0};
tsunami_lab::solvers::FWave::computeDeltaFlux(10,
9,
-3,
3,
l_deltaFlux);
REQUIRE( l_deltaFlux[0] == Approx(57) );
REQUIRE( l_deltaFlux[1] == Approx(-102.163175) );
}
Test the computation of the eigencoefficients. Using the equation for the eigencoefficients to calculate the following cases.
TEST_CASE( "Test the computation of the eigencoefficients.", "[FWaveEigencoefficients]")
{
/*
* Test case:
* eigenvalue1: 4
* eigenvalue2: 5
* delta flux: {10, 2}
*
* Matrix of right eigenvectors:
*
* | 1 1 |
* R = | |
* | 4 5 |
*
* inverted = {{5, -1}, {-4, 1}}
*
* inverted * {10, 2} = {48, -38}
*/
t_real l_eigencoefficient1 = 0;
t_real l_eigencoefficient2 = 0;
t_real l_deltaFlux[2] = {10, 2};
tsunami_lab::solvers::FWave::computeEigencoefficients(4,
5,
l_deltaFlux,
l_eigencoefficient1,
l_eigencoefficient2);
REQUIRE( l_eigencoefficient1 == Approx(48) );
REQUIRE( l_eigencoefficient2 == Approx(-38) );
}
Test the derivation of the F-Wave net-updates. Finally the test cases for the net update combine all calculations above and using the eigenvector, the equation (1.3.3) and the effect on the left and right waves.
TEST_CASE( "Test the derivation of the F-Wave net-updates.", "[FWaveUpdate]" )
{
/*
* Test case:
*
* left | right
* h: 10 | 9
* u: -3 | 3
* hu: -30 | 27
*
* The derivation of the eigenvalues (s1, s2) and eigencoefficient (a1, a1) is given above.
*
* The net-updates are given through the scaled eigenvectors.
*
* | 1 | | 33.5590017014261447899292 |
* update #1: s1 * a1 * | | = | |
* | s1 | | -326.56631690591093200508 |
*
* | 1 | | 23.4409982985738561366777 |
* update #2: s2 * a2 * | | = | |
* | s2 | | 224.403141905910928927533 |
*/
float l_netUpdatesL[2] = { -5, 3 };
float l_netUpdatesR[2] = { 4, 7 };
tsunami_lab::solvers::FWave::netUpdates( 10,
9,
-30,
27,
l_netUpdatesL,
l_netUpdatesR );
REQUIRE( l_netUpdatesL[0] == Approx( 33.5590017014261447899292 ) );
REQUIRE( l_netUpdatesL[1] == Approx( -326.56631690591093200508 ) );
REQUIRE( l_netUpdatesR[0] == Approx( 23.4409982985738561366777 ) );
REQUIRE( l_netUpdatesR[1] == Approx( 224.403141905910928927533 ) );
/*
* Test case (dam break):
*
* left | right
* h: 10 | 8
* hu: 0 | 0
*
* eigenvalues are given as:
*
* s1 = -sqrt(9.80665 * 9)
* s2 = sqrt(9.80665 * 9)
*
* Inversion of the matrix of right Eigenvectors:
*
* wolframalpha.com query: invert {{1, 1}, {-sqrt(9.80665 * 9), sqrt(9.80665 * 9)}}
*
* | 0.5 -0.0532217 |
* Rinv = | |
* | 0.5 -0.0532217 |
*
* Multiplicaton with the jump in quantities gives the eigencoefficient:
*
* | 8 - 10 | | -1 | | a1 |
* Rinv * | | = | | = | |
* | 0 - 0 | | -1 | | a2 |
*
* The net-updates are given through the scaled eigenvectors.
*
* | 1 | | 9.394671362 |
* update #1: s1 * a1 * | | = | |
* | s1 | | -88.25985 |
*
* | 1 | | -9.394671362 |
* update #2: s2 * a2 * | | = | |
* | s2 | | -88.25985 |
*/
tsunami_lab::solvers::FWave::netUpdates( 10,
8,
0,
0,
l_netUpdatesL,
l_netUpdatesR );
REQUIRE( l_netUpdatesL[0] == Approx(9.394671362) );
REQUIRE( l_netUpdatesL[1] == -Approx(88.25985) );
REQUIRE( l_netUpdatesR[0] == -Approx(9.394671362) );
REQUIRE( l_netUpdatesR[1] == -Approx(88.25985) );
/*
* Test case (dam break):
*
* left | right
* h: 10 | 1
* hu: -100 | 0
*
* eigenvalues are calculated from computeEigenvalues as:
*
* s1 = -14.9416
* s2 = -0.253316
*
* eigencoefficient are calculated form computeEigencoefficients as:
*
* a1 = 99.4054
* a2 = 0.594551
*
* The net-updates are given through the scaled eigenvectors.
*
* | 1 | | 100 |
* update #1: s1 * a1 * | | = | |
* | s1 | | -1485.4292 |
*
* | 1 | | 0 |
* update #2: s2 * a2 * | | = | |
* | s2 | | 0 |
*/
tsunami_lab::solvers::FWave::netUpdates( 10,
1,
-100,
0,
l_netUpdatesL,
l_netUpdatesR );
REQUIRE( l_netUpdatesL[0] == Approx(100) );
REQUIRE( l_netUpdatesL[1] == Approx(-1485.4292) );
REQUIRE( l_netUpdatesR[0] == Approx(0) );
REQUIRE( l_netUpdatesR[1] == Approx(0) );
/*
* Test case (trivial steady state):
*
* left | right
* h: 10 | 10
* hu: 0 | 0
*/
tsunami_lab::solvers::FWave::netUpdates( 10,
10,
0,
0,
l_netUpdatesL,
l_netUpdatesR );
REQUIRE( l_netUpdatesL[0] == Approx(0) );
REQUIRE( l_netUpdatesL[1] == Approx(0) );
REQUIRE( l_netUpdatesR[0] == Approx(0) );
REQUIRE( l_netUpdatesR[1] == Approx(0) );
}
Visualization
Visualization of the Height and Momentum X with 1000 cells using ParaView.
Height
Momentum X
Sphinx with Doxygen
First we install
Doxygen
.We install
Sphinx
andbreathe
and set breath as an extension. An installation guide can be found at Building the Documentation.extensions = [ ... 'breathe', ... ]
We set up the Doxyfile by setting the variables
... OUTPUT_DIRECTORY = "_build" ... INPUT = "../include/" ... RECURSIVE = YES ... GENERATE_XML = YES ...
GENERATE_XML
is need, because breathe uses the xml files to generate the respective sphinx files.We include the following code at the top of the
conf.py
to build the doxygen documentation together with sphinx.import subprocess subprocess.call('doxygen Doxyfile.in', shell=True)
We set up breathe by adding the following configurations to the
conf.py
# -- Breathe configuration ------------------------------------------------- breathe_projects = { "Tsunami Simulation": "_build/xml/", } breathe_default_project = "Tsunami Simulation" breathe_default_members = ('members', 'undoc-members')
Now we can include a whole namespace e.g. the tsunami_lab::io namespace into sphinx
.. doxygennamespace:: tsunami_lab::io :project: Tsunami Simulation :content-only: :members: :private-members: :undoc-members:
More information about directives to include the doxygen into sphinx can be found at the official breathe website.
Contribution
All team members contributed equally to the tasks.