mirror of
https://git.gfz-potsdam.de/naaice/tug.git
synced 2025-12-16 02:48:23 +01:00
362 lines
11 KiB
C++
362 lines
11 KiB
C++
/**
|
|
* @file Velocities.hpp
|
|
* @brief API of Velocities class, holding information for a simulation of
|
|
* Hydraulic Charge and Darcy-Velocities. Holds a predifined Grid object and
|
|
* Boundary object.
|
|
*
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "tug/Core/Numeric/SimulationInput.hpp"
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <stdlib.h>
|
|
#include <string>
|
|
|
|
#include <tug/Boundary.hpp>
|
|
#include <tug/Core/BaseSimulation.hpp>
|
|
#include <tug/Core/Matrix.hpp>
|
|
#include <tug/Core/Numeric/BTCS.hpp>
|
|
#include <tug/Core/Numeric/FTCS.hpp>
|
|
#include <tug/Core/TugUtils.hpp>
|
|
#include <tug/Diffusion/Diffusion.hpp>
|
|
|
|
#ifdef _OPENMP
|
|
#include <omp.h>
|
|
#else
|
|
#define omp_get_num_procs() 1
|
|
#endif
|
|
|
|
using namespace Eigen;
|
|
namespace tug {
|
|
|
|
enum class HYDRAULIC_MODE { TRANSIENT, STEADY_STATE };
|
|
enum class HYDRAULIC_RESOLVE { EXPLICIT, IMPLICIT };
|
|
|
|
template <class T, HYDRAULIC_MODE hyd_mode, HYDRAULIC_RESOLVE hyd_resolve>
|
|
class Velocities : public BaseSimulationGrid<T> {
|
|
private:
|
|
int innerIterations{1};
|
|
T timestep{-1};
|
|
T epsilon{1E-5};
|
|
int numThreads{omp_get_num_procs()};
|
|
|
|
RowMajMat<T> velocitiesX;
|
|
RowMajMat<T> velocitiesY;
|
|
|
|
RowMajMat<T> alphaX;
|
|
RowMajMat<T> alphaY;
|
|
|
|
public:
|
|
Velocities(RowMajMat<T> &origin)
|
|
: BaseSimulationGrid<T>(origin),
|
|
velocitiesX(origin.rows(), origin.cols() + 1),
|
|
velocitiesY(origin.rows() + 1, origin.cols()),
|
|
alphaX(origin.rows(), origin.cols()),
|
|
alphaY(origin.rows(), origin.cols()) {};
|
|
|
|
Velocities(T *data, std::size_t rows, std::size_t cols)
|
|
: BaseSimulationGrid<T>(data, rows, cols), velocitiesX(rows, cols + 1),
|
|
velocitiesY(rows + 1, cols), alphaX(rows, cols), alphaY(rows, cols) {};
|
|
|
|
Velocities(T *data, std::size_t length)
|
|
: BaseSimulationGrid<T>(data, 1, length), velocitiesX(1, length + 1),
|
|
alphaX(1, length) {};
|
|
|
|
/**
|
|
* @brief Set the epsilon value, the relativ error allowed for convergence
|
|
*
|
|
* @param epsilon the new epsilon value
|
|
*/
|
|
void setEpsilon(T epsilon) {
|
|
tug_assert(0 <= epsilon && epsilon < 1,
|
|
"Relative Error epsilon must be between 0 and 1");
|
|
|
|
this->epsilon = epsilon;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the alphaX matrix.
|
|
*
|
|
* @return RowMajMat<T>& Reference to the alphaX matrix.
|
|
*/
|
|
RowMajMat<T> &getAlphaX() { return alphaX; }
|
|
|
|
/**
|
|
* @brief Get the alphaY matrix.
|
|
*
|
|
* @return RowMajMat<T>& Reference to the alphaY matrix.
|
|
*/
|
|
RowMajMat<T> &getAlphaY() {
|
|
tug_assert(
|
|
this->getDim(),
|
|
"Grid is not two dimensional, there is no domain in y-direction!");
|
|
|
|
return alphaY;
|
|
}
|
|
|
|
/**
|
|
* @brief Set the alphaX matrix.
|
|
*
|
|
* @param alphaX The new alphaX matrix.
|
|
*/
|
|
void setAlphaX(const RowMajMat<T> &alphaX) { this->alphaX = alphaX; }
|
|
|
|
/**
|
|
* @brief Set the alphaY matrix.
|
|
*
|
|
* @param alphaY The new alphaY matrix.
|
|
*/
|
|
void setAlphaY(const RowMajMat<T> &alphaY) {
|
|
tug_assert(
|
|
this->getDim(),
|
|
"Grid is not two dimensional, there is no domain in y-direction!");
|
|
|
|
this->alphaY = alphaY;
|
|
}
|
|
|
|
/**
|
|
* @brief Set the timestep per iteration
|
|
*
|
|
* @param timestep timestep per iteration
|
|
*/
|
|
void setTimestep(T timestep) override {
|
|
if (timestep <= 0) {
|
|
throw std::invalid_argument("Timestep must be greater than zero");
|
|
}
|
|
this->timestep = timestep;
|
|
const T deltaColSquare = this->deltaCol() * this->deltaCol();
|
|
const T deltaRowSquare = this->deltaRow() * this->deltaRow();
|
|
const T minDeltaSquare = std::min(deltaColSquare, deltaRowSquare);
|
|
|
|
const T maxK = std::max(this->alphaX.maxCoeff(), this->alphaY.maxCoeff());
|
|
|
|
T cfl = minDeltaSquare / (4 * maxK);
|
|
if (timestep > cfl) {
|
|
this->innerIterations = (int)ceil(timestep / cfl);
|
|
this->timestep = timestep / (double)innerIterations;
|
|
std::cerr << "Warning :: Timestep was adjusted, because of stability "
|
|
"conditions. Time duration was approximately preserved by "
|
|
"adjusting internal number of iterations."
|
|
<< std::endl;
|
|
std::cout << "FTCS" << "_" << "2D" << " :: Required "
|
|
<< this->innerIterations
|
|
<< " inner iterations with dt=" << this->timestep << std::endl;
|
|
} else {
|
|
this->innerIterations = 1;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Set the number of desired openMP Threads.
|
|
*
|
|
* @param num_threads Number of desired threads. Must have a value between
|
|
* 1 and the maximum available number of processors. The
|
|
* maximum number of processors is set as the default case during Velocities
|
|
* construction.
|
|
*/
|
|
void setNumberThreads(int num_threads) {
|
|
if (num_threads > 0 && num_threads <= omp_get_num_procs()) {
|
|
this->numThreads = num_threads;
|
|
} else {
|
|
int maxThreadNumber = omp_get_num_procs();
|
|
throw std::invalid_argument(
|
|
"Number of threads exceeds the number of processor cores (" +
|
|
std::to_string(maxThreadNumber) + ") or is less than 1.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Getter function for outx, the matrix containing velocities in
|
|
* x-Direction; returns a reference to outx
|
|
*
|
|
* */
|
|
const RowMajMat<T> &getVelocitiesX() const { return this->velocitiesX; }
|
|
|
|
/**
|
|
* @brief Getter function for outy, the matrix containing velocities in
|
|
* y-Direction; return a reference to outy
|
|
*/
|
|
const RowMajMat<T> &getVelocitiesY() const { return this->velocitiesY; }
|
|
|
|
/**
|
|
* @brief Simulation of hydraulic charge either until convergence,
|
|
* or for a number of selected timesteps. Calculation of Darcy-velocities.
|
|
*/
|
|
void run() override {
|
|
// if iterations < 1 calculate hydraulic charge until steady state is
|
|
// reached
|
|
|
|
SimulationInput<T> input = {.concentrations =
|
|
this->getConcentrationMatrix(),
|
|
.alphaX = this->getAlphaX(),
|
|
.alphaY = this->getAlphaY(),
|
|
.boundaries = this->getBoundaryConditions(),
|
|
.dim = this->getDim(),
|
|
.timestep = this->timestep,
|
|
.rowMax = this->rows(),
|
|
.colMax = this->cols(),
|
|
.deltaRow = this->deltaRow(),
|
|
.deltaCol = this->deltaCol()};
|
|
|
|
if constexpr (hyd_mode == HYDRAULIC_MODE::STEADY_STATE) {
|
|
const T deltaColSquare = this->deltaCol() * this->deltaCol();
|
|
const T deltaRowSquare = this->deltaRow() * this->deltaRow();
|
|
const T minDeltaSquare = std::min(deltaColSquare, deltaRowSquare);
|
|
|
|
const T maxK = std::max(this->alphaX.maxCoeff(), this->alphaY.maxCoeff());
|
|
// Calculate largest possible timestep, depending on K and gridsize
|
|
|
|
setTimestep(minDeltaSquare / (4 * maxK));
|
|
|
|
input.timestep = this->timestep;
|
|
|
|
RowMajMat<T> oldConcentrations;
|
|
do {
|
|
oldConcentrations = this->getConcentrationMatrix();
|
|
(void)calculate_hydraulic_flow(input);
|
|
} while (!checkConvergance(oldConcentrations));
|
|
} else {
|
|
if (timestep == -1) {
|
|
throw_invalid_argument("Timestep is not set");
|
|
}
|
|
for (int i = 0; i < innerIterations; i++) {
|
|
(void)calculate_hydraulic_flow(input);
|
|
}
|
|
}
|
|
|
|
(void)computeFluidVelocities();
|
|
};
|
|
|
|
private:
|
|
/**
|
|
* @brief Calculate the new hydraulic charge using FTCS
|
|
*/
|
|
void calculate_hydraulic_flow(SimulationInput<T> &sim_in) {
|
|
if constexpr (hyd_resolve == HYDRAULIC_RESOLVE::EXPLICIT) {
|
|
FTCS_2D(sim_in, numThreads);
|
|
} else {
|
|
BTCS_2D(sim_in, ThomasAlgorithm, numThreads);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief checks if the matrix of Hydraulic Heads has converged
|
|
*
|
|
* @return bool true if for all corresponding cells of the matrices,
|
|
* containing old and new Charge values, the relative error is below the
|
|
* selected Epsilon
|
|
*/
|
|
bool checkConvergance(const RowMajMat<T> &oldHeads) {
|
|
const auto abs_err = (oldHeads - this->getConcentrationMatrix()).cwiseAbs();
|
|
const auto rel_err = abs_err.cwiseQuotient(this->getConcentrationMatrix());
|
|
|
|
return rel_err.maxCoeff() < epsilon;
|
|
}
|
|
|
|
/**
|
|
* @brief Update the matrices containing Darcy velocities in x and y
|
|
* directions
|
|
*/
|
|
void computeFluidVelocities() {
|
|
const std::size_t rows = this->rows();
|
|
const std::size_t cols = this->cols();
|
|
const T dx = this->deltaCol();
|
|
const T dy = this->deltaRow();
|
|
const RowMajMat<T> &hydraulicCharges = this->getConcentrationMatrix();
|
|
|
|
const RowMajMat<T> &permKX = this->alphaX;
|
|
const RowMajMat<T> &permKY = this->alphaY;
|
|
|
|
const Boundary<T> &bc = this->getBoundaryConditions();
|
|
|
|
// calculate velocities in x-direction
|
|
for (std::size_t i_rows = 0; i_rows < rows; i_rows++) {
|
|
const auto bc_left = bc.getBoundaryElement(BC_SIDE_LEFT, i_rows);
|
|
switch (bc_left.getType()) {
|
|
case BC_TYPE_CLOSED: {
|
|
velocitiesX(i_rows, 0) = 0;
|
|
break;
|
|
}
|
|
case BC_TYPE_CONSTANT: {
|
|
velocitiesX(i_rows, 0) =
|
|
-this->alphaX(i_rows, 0) *
|
|
(hydraulicCharges(i_rows, 0) - bc_left.getValue()) / (dx / 2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
const auto bc_right = bc.getBoundaryElement(BC_SIDE_RIGHT, i_rows);
|
|
switch (bc_right.getType()) {
|
|
case BC_TYPE_CLOSED: {
|
|
velocitiesX(i_rows, cols) = 0;
|
|
break;
|
|
}
|
|
case BC_TYPE_CONSTANT: {
|
|
velocitiesX(i_rows, cols) =
|
|
-permKX(i_rows, cols - 1) *
|
|
(bc_right.getValue() - hydraulicCharges(i_rows, cols - 1)) /
|
|
(dx / 2);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// main loop for calculating velocities in x-direction for inner cells
|
|
#pragma omp parallel for num_threads(numThreads)
|
|
for (int i = 0; i < rows; i++) {
|
|
for (int j = 1; j < cols; j++) {
|
|
velocitiesX(i, j) =
|
|
-permKX(i, j - 1) *
|
|
(hydraulicCharges(i, j) - hydraulicCharges(i, j - 1)) / dx;
|
|
}
|
|
}
|
|
|
|
// calculate velocities in y-direction
|
|
for (std::size_t i_cols = 0; i_cols < cols; i_cols++) {
|
|
const auto bc_top = bc.getBoundaryElement(BC_SIDE_TOP, i_cols);
|
|
switch (bc_top.getType()) {
|
|
case BC_TYPE_CLOSED: {
|
|
velocitiesY(0, i_cols) = 0;
|
|
break;
|
|
}
|
|
case BC_TYPE_CONSTANT: {
|
|
velocitiesY(0, i_cols) =
|
|
-permKY(0, i_cols) *
|
|
(hydraulicCharges(0, i_cols) - bc_top.getValue()) / (dy / 2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
const auto bc_bottom = bc.getBoundaryElement(BC_SIDE_BOTTOM, i_cols);
|
|
switch (bc_bottom.getType()) {
|
|
case BC_TYPE_CLOSED: {
|
|
velocitiesY(rows, i_cols) = 0;
|
|
break;
|
|
}
|
|
case BC_TYPE_CONSTANT: {
|
|
velocitiesY(rows, i_cols) =
|
|
-permKY(rows - 1, i_cols) *
|
|
(bc_bottom.getValue() - hydraulicCharges(rows - 1, i_cols)) /
|
|
(dy / 2);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// main loop for calculating velocities in y-direction for inner cells
|
|
#pragma omp parallel for num_threads(numThreads)
|
|
for (int i = 1; i < rows; i++) {
|
|
for (int j = 0; j < cols; j++) {
|
|
velocitiesY(i, j) =
|
|
-permKY(i, j) *
|
|
(hydraulicCharges(i, j) - hydraulicCharges(i - 1, j)) / dy;
|
|
}
|
|
}
|
|
};
|
|
};
|
|
} // namespace tug
|