#include <type_traits>
#include <initializer_list>
#include <vector>
#include <iostream>
// ------------------------------- MESHDIMENSION----------------------- //
struct MeshDimension
{
MeshDimension(const int numCells, const double min, const double max):
numCells_(numCells),
minVal_(min),
maxVal_(max)
{}
const int numCells_;
const double minVal_, maxVal_;
};
#include <cassert>
namespace detail {
enum class enabler {};
}
template <bool Condition>
using EnableIf =
typename std::enable_if<Condition, detail::enabler>::type;
template<typename... Conds>
struct and_ : std::true_type {};
template<typename Cond, typename... Conds>
struct and_<Cond, Conds...>
: std::conditional<Cond::value, and_<Conds...>,
std::false_type>::type {};
template<typename Target, typename... Ts>
using areT = and_<std::is_same<Ts, Target>...>;
// -------------------------------------- MESH.H -------------------------//
template<size_t meshDim>
class Mesh
{
public:
// Constructor
template<class... DimArgs>
Mesh(DimArgs... dimensions) {
// Check the dimensions are consistent
checkDims<DimArgs...>();
// Push values to the dimSize vector
pushDimensions<DimArgs...>(dimensions...);
// Store positions (templated to call the correct function)
prepareVectors<DimArgs...>();
}
const size_t numCells() const;
// Always defined (can't have a 0D mesh)
const size_t xCells() const { return dimSize_[0]; }
// Get size if 2D or greater, return 0 otherwise
template<size_t meshD = meshDim, EnableIf<meshD>=2>...>
const size_t yCells() const { return dimSize_[1]; }
template<size_t meshD = meshDim, EnableIf<meshD<2>...>
const size_t yCells() const { return 0; }
template<size_t meshD = meshDim, EnableIf<meshD>=3>...>
const size_t zCells() const { return dimSize_[2]; }
template<size_t meshD = meshDim, EnableIf<meshD<3>...>
const size_t zCells() const { return 0; }
const double x(const int &i) const { return getPosition(0, i); }
template<size_t meshD = meshDim, EnableIf<meshD>=2>...>
const double y(const int &i) const { return getPosition(1, i); }
template<size_t meshD = meshDim, EnableIf<meshD>=3>...>
const double z(const int &i) const { return getPosition(2, i); }
template<size_t rhsMD>
bool operator==(const Mesh<rhsMD> &rhs) const;
private:
bool constSpacing_ = true;
std::vector<double> dimMin_;
std::vector<double> dimMax_;
std::vector<size_t> dimSize_;
std::vector<double> position_[meshDim];
void placeCentres(const int d, const bool constSpacing);
const double getPosition(const size_t d, const int &i) const;
template<class... numArgs>
void checkDims() const;
void checkBounds(std::vector<int> idxs) const;
template<typename... T, EnableIf<areT<MeshDimension, T...>::value>...>
void pushDimensions(T... dims);
template<typename... T, EnableIf<areT<int, T...>::value>...>
void pushDimensions(T... dims);
template<typename... T, EnableIf<areT<MeshDimension, T...>::value>...>
void prepareVectors();
template<typename... T, EnableIf<areT<int, T...>::value>...>
void prepareVectors();
enum class ScalingType {
Pivot,
Hyperbolic,
Exponential
};
};
// ------------------------- MESH.HXX ---------------------------------//
#include <math.h>
template<size_t meshDim>
template<size_t rhsMD>
bool Mesh<meshDim>::operator==(const Mesh<rhsMD> &rhs) const
{
if (meshDim != rhsMD) {
return false;
}
if ((dimSize_ != rhs.dimSize_)
|| (dimMin_ != rhs.dimMin_)
|| (dimMax_ != rhs.dimMax_)) {
return false;
}
if (constSpacing_ != rhs.constSpacing_) {
return false;
}
return true;
}
// Assert that number of arguments is the same as number of dimensions.
template<size_t meshDim>
template<class... numArgs>
void Mesh<meshDim>::checkDims() const {
static_assert(sizeof...(numArgs)==meshDim, "Wrong number of dimensions");
}
// pushDimensions function, for int and MeshDimension type arguments
template<size_t meshDim>
template<typename... T, EnableIf<areT<MeshDimension, T...>::value>...>
void Mesh<meshDim>::pushDimensions(T... dims) {
std::cout << "Constructed with MeshDimension structs" << std::endl;
std::vector<MeshDimension> list{dims...};
for (unsigned int i=0; i<meshDim; i++) {
dimSize_.push_back(list[i].numCells_);
dimMin_.push_back(list[i].minVal_);
dimMax_.push_back(list[i].maxVal_);
}
}
template<size_t meshDim>
template<typename... T, EnableIf<areT<int, T...>::value>...>
void Mesh<meshDim>::pushDimensions(T... dims) {
std::cout << "Constructed with ints. Must also add min/max values."
<< std::endl;
std::vector<int> list{dims...};
for (unsigned int i=0; i<meshDim; i++) {
dimSize_.push_back(list[i]);
}
}
// prepareVectors function, no-op if ints passed
template<size_t meshDim>
template<typename... T, EnableIf<areT<MeshDimension, T...>::value>...>
void Mesh<meshDim>::prepareVectors() {
std::cout << "meshDim structs" << std::endl;
for (unsigned int d=0; d<meshDim; d++) {
placeCentres(d, constSpacing_);
}
}
template<size_t meshDim>
template<typename... T, EnableIf<areT<int, T...>::value>...>
void Mesh<meshDim>::prepareVectors() {
std::cout << "no-op: ints" << std::endl;
}
template<size_t meshDim>
void Mesh<meshDim>::placeCentres(const int d, const bool constSpacing) {
position_[d].resize(dimSize_[d]);
const double range = dimMax_[d] - dimMin_[d];
if (constSpacing) {
// Constant spacing
const double dx = range / dimSize_[d];
for (unsigned int i=0; i<dimSize_[d]; i++) {
position_[d][i] = (dimMin_[d] + (dx*(i+0.5)));
}
} else {
// Set the choice of method
constexpr ScalingType scalingType = ScalingType::Pivot;
switch (scalingType)
{
case ScalingType::Exponential:
{
// Exponential spacing
constexpr double b = 3;
constexpr double dom = exp(b)-1;
for (unsigned int i=0; i<dimSize_[d]; i++) {
const double u = static_cast<double>(i+0.5) / dimSize_[d];
const double x = ((exp(b*u) - 1)/dom);
position_[d][i] = (x*range) + dimMin_[d];
}
}
break;
case ScalingType::Hyperbolic:
{
// Hyperbolic tangent spacing
constexpr double b = 3;
constexpr double c = 0.5; // 1-c is the location of the 'attractor'
constexpr double A = tanh(b*-1*c);
constexpr double B = tanh(b*(1-c));
for (unsigned int i=0; i<dimSize_[d]; i++) {
const double u = static_cast<double>(i+0.5) / dimSize_[d];
const double x = (tanh(b*(u-c)) - A) / (B-A);
position_[d][i] = (x*range) + dimMin_[d];
}
}
break;
case ScalingType::Pivot:
{
// Pivoted spacing - requires an even number of cells
assert(dimSize_[d]%2==0);
constexpr double p=0.35;
constexpr double b = 2;
constexpr double dom = exp(b)-1;
const double mid = (range * p) + dimMin_[d];
const size_t dim2 = dimSize_[d]/2;
for (unsigned int i=0; i<dim2; i++) {
const double u = static_cast<double>(i+0.5) / dim2;
const double x = ((exp(b*u) - 1)/dom);
const double step_lower = x*range*(p < 0.5 ? p : 1-p);
const double step_upper = x*range*(p < 0.5 ? 1-p : p);
// Upper half
position_[d][i+dim2] = mid + step_upper;
// Lower half
position_[d][dim2-i-1] = mid - step_lower;
}
}
break;
}
// for (unsigned int i=0; i<dimSize_[d]; i++) {
// std::cout << position_[d][i] << " 1" << std::endl;
// }
}
}
template<size_t meshDim>
const double Mesh<meshDim>::getPosition(const size_t d, const int &i) const
{
// Bounds checking if NDEBUG not defined
assert(i<dimSize_[d]);
assert(i>=0);
return position_[d][i];
}
template<size_t meshDim>
void Mesh<meshDim>::checkBounds(std::vector<int> idxs) const
{
for (unsigned int d=0; d<meshDim; d++) {
assert(idxs[d] < dimSize_[d]);
}
}
template<size_t meshDim>
const size_t Mesh<meshDim>::numCells() const
{
int numcells = 1;
for (unsigned int d=0; d<meshDim; d++) {
numcells *= dimSize_[d];
}
return numcells;
}
// ----------------------------------- FIELD.H ----------------------------//
template <typename T, size_t fD, size_t mD>
class Field
{
public:
// Constructor
Field(const Mesh<mD>& mesh, const std::string &fileName):
fileName_(fileName),
mesh_(mesh)
{
for (unsigned int d=0; d<fD; d++) {
field_[d].reserve(mesh.numCells());
}
}
// Copy constructor
Field(const Field<T,fD,mD>& refToCopy):
field_(refToCopy.field_),
fileName_(refToCopy.fileName_),
mesh_(refToCopy.mesh_)
{}
Field(const Field<T,fD,mD>& refToCopy, const std::string& name):
fileName_(name),
mesh_(refToCopy.mesh_)
{
for (size_t d=0; d<fD; d++) {
field_[d] = refToCopy.field_[d];
for (size_t i=0; i<mesh_.numCells(); i++) {
std::cout << "field_[" << d << "][" << i << "] = " << field_[d][i] << std::endl;
}
}
}
// Set all values
void setZero();
void setFixed(const T &val);
// Get the value at some point - now used only for 1D case.
template<size_t fd=fD, EnableIf<fd==1>..., typename... PosArgs, EnableIf<areT<size_t, PosArgs...>::value>...>
T& operator()(const PosArgs... indices) { return field_[0][singleIndex(0, indices...)]; }
const Mesh<mD> &mesh() const { return mesh_; }
// Return entire (const) vector<T>&
const std::vector<T> &x() const { return field_[0]; }
// Non-const
std::vector<T> &x() { return field_[0]; }
// Only specific values
template<typename... PosArgs, EnableIf<areT<size_t, PosArgs...>::value>...>
T &x(const size_t xIdx, const PosArgs... indices);
private:
std::vector<T> field_[fD];
std::string fileName_;
const Mesh<mD> &mesh_;
public:
// Index lookup - component dependent.
template<typename... PosArgs, EnableIf<areT<size_t, PosArgs...>::value>...>
const size_t singleIndex(const size_t component, PosArgs... indices) const;
};
// #include "Field.hxx" // The functions implemented in this aren't used
// Copying the singleIndex function for example
template<typename T, size_t D, size_t M>
template<typename... PosArgs, EnableIf<areT<size_t, PosArgs...>::value>...>
const size_t Field<T,D,M>::singleIndex(const size_t component, PosArgs... indices) const
{
//assert(component >= 0); // Commented out since MVCE doesn't #include <cassert>
//assert(component < M);
const size_t c0 = component;
const size_t c1 = (component+1)%3;
const size_t c2 = (component+2)%3;
const std::vector<size_t> &C = {mesh_.xCells(), mesh_.yCells(), mesh_.zCells()};
std::vector<size_t> idx{indices...};
idx.resize(3);
return (idx[c0] + (idx[c1]*C[c0]) + (idx[c2]*C[c1]*C[c0]));
}
// ----------------------------- FIELD.cpp ------------------------------//
template<typename T, size_t fD, size_t mD>
void Field<T,fD,mD>::setZero()
{
setFixed(T(0));
}
// This might need to be explicit.
// Currently, 'a' can be converted for Field<double, D>
template<typename T, size_t fD, size_t mD>
void Field<T,fD,mD>::setFixed(const T &val)
{
for (size_t d=0; d<fD; d++) {
std::fill(field_[d].begin(), field_[d].end(), val);
}
}
// ------------------------------- MAIN ----------------------------------//
using vectorField = Field<double, 2, 2>;
using scalarField = Field<double, 1, 2>;
int main()
{
MeshDimension xDim(20, 0, 1);
MeshDimension yDim(30, 0, 1);
Mesh<2> mesh(xDim, yDim);
vectorField U(mesh, "U");
U.setZero();
vectorField Phi(U, "copy-U");
std::cout << "Phi.x()[5] = " << Phi.x()[5] << std::endl;
}