diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 21a9034ee..4c5011800 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -27,10 +27,10 @@ jobs: os: [ubuntu-latest] #, macos-12] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Cache conda - uses: actions/cache@v1 + uses: actions/cache@v4 env: CACHE_NUMBER: 0 # Increase this value to reset cache if .github/mrcpp-gha.yml has not changed with: @@ -45,7 +45,7 @@ jobs: activate-environment: mrcpp-gha environment-file: .github/mrcpp-gha.yml channel-priority: true - python-version: 3.6 + python-version: 3.11 use-only-tar-bz2: true # IMPORTANT: This needs to be set for caching to work properly! - name: Configure diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 26ea24f02..f25bb30d9 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up environment uses: conda-incubator/setup-miniconda@v3 @@ -26,7 +26,7 @@ jobs: activate-environment: mrcpp-codecov environment-file: .github/mrcpp-codecov.yml channel-priority: true - python-version: 3.6 + python-version: 3.11 - name: Configure shell: bash -l {0} diff --git a/api/MWFunctions b/api/MWFunctions index 24c562364..ae266023d 100644 --- a/api/MWFunctions +++ b/api/MWFunctions @@ -34,7 +34,7 @@ #include "treebuilders/grid.h" #include "treebuilders/project.h" -#include "treebuilders/map.h" +#include "treebuilders/treeMap.h" #include "treebuilders/multiply.h" #include "treebuilders/add.h" diff --git a/cmake/MRCPPConfig.cmake.in b/cmake/MRCPPConfig.cmake.in index f7a06bd3d..adccf088b 100644 --- a/cmake/MRCPPConfig.cmake.in +++ b/cmake/MRCPPConfig.cmake.in @@ -94,7 +94,7 @@ if(NOT TARGET ${PN}::mrcpp) get_filename_component(_fext ${${PN}_LIBRARY} EXT) include("${CMAKE_CURRENT_LIST_DIR}/${PN}Targets.cmake") - find_package(Eigen3 3.3 CONFIG REQUIRED) + find_package(Eigen3 CONFIG REQUIRED) get_target_property(MRCPP_HAS_OMP MRCPP::mrcpp MRCPP_HAS_OMP) if(MRCPP_HAS_OMP) diff --git a/docs/mrcpp_api/mwfunctions.rst b/docs/mrcpp_api/mwfunctions.rst index 527497f5c..37cd426f7 100644 --- a/docs/mrcpp_api/mwfunctions.rst +++ b/docs/mrcpp_api/mwfunctions.rst @@ -85,7 +85,7 @@ met. .. doxygenfunction:: mrcpp::square(double prec, FunctionTree &out, FunctionTree &tree, int maxIter, bool absPrec) .. doxygenfunction:: mrcpp::power(double prec, FunctionTree &out, FunctionTree &tree, double pow, int maxIter, bool absPrec) .. doxygenfunction:: mrcpp::dot(double prec, FunctionTree &out, FunctionTreeVector &inp_a, FunctionTreeVector &inp_b, int maxIter, bool absPrec) -.. doxygenfunction:: mrcpp::map(double prec, FunctionTree &out, FunctionTree &inp, FMap fmap, int maxIter, bool absPrec) +.. doxygenfunction:: mrcpp::treeMap(double prec, FunctionTree &out, FunctionTree &inp, FMap fmap, int maxIter, bool absPrec) Creating undefined FunctionTrees ++++++++++++++++++++++++++++++++ @@ -165,7 +165,7 @@ Constructing an MRA An MRA is defined in two steps, first the computational domain is given by a ``BoundingBox`` (D is the dimension), e.g. for a total domain of -:math:`[-32,32]^3` in three dimensions (eight root boxes of size :math:`[32]^3` +:math:`[-16,16]^3` in three dimensions (eight root boxes of size :math:`[16]^3` each): .. code-block:: cpp diff --git a/examples/addition.cpp b/examples/addition.cpp index 83736c9a8..bf8a5277d 100644 --- a/examples/addition.cpp +++ b/examples/addition.cpp @@ -65,5 +65,12 @@ int main(int argc, char **argv) { mrcpp::print::value(0, "Square norm", sq_norm); mrcpp::print::footer(0, timer, 2); + mrcpp::FunctionTree result = mrcpp::FunctionTree(f_tree_1, f_tree_2); + + mrcpp::Coord<3> r{ 0.0, 0.0, 0.0}; + auto val = result.evalf(r); + mrcpp::print::complex_value(0, "Value of cplx fcn at origin", val); + + return 0; } diff --git a/external/upstream/fetch_eigen3.cmake b/external/upstream/fetch_eigen3.cmake index be8f9e0bc..68a6683c8 100644 --- a/external/upstream/fetch_eigen3.cmake +++ b/external/upstream/fetch_eigen3.cmake @@ -1,4 +1,4 @@ -find_package(Eigen3 3.4 CONFIG QUIET +find_package(Eigen3 CONFIG QUIET NO_CMAKE_PATH NO_CMAKE_PACKAGE_REGISTRY ) diff --git a/src/core/CrossCorrelation.cpp b/src/core/CrossCorrelation.cpp index 8b04cf18d..788491efd 100644 --- a/src/core/CrossCorrelation.cpp +++ b/src/core/CrossCorrelation.cpp @@ -106,11 +106,11 @@ void CrossCorrelation::readCCCBin() { int K = this->order + 1; this->Left = MatrixXd::Zero(K * K, 2 * K); this->Right = MatrixXd::Zero(K * K, 2 * K); - double dL[2 * K]; - double dR[2 * K]; + std::vector dL(2 * K); + std::vector dR(2 * K); for (int i = 0; i < K * K; i++) { - L_fis.read((char *)dL, sizeof(double) * 2 * K); - R_fis.read((char *)dR, sizeof(double) * 2 * K); + L_fis.read(reinterpret_cast(dL.data()), sizeof(double) * 2 * K); + R_fis.read(reinterpret_cast(dR.data()), sizeof(double) * 2 * K); for (int j = 0; j < 2 * K; j++) { if (std::abs(dL[j]) < MachinePrec) dL[j] = 0.0; if (std::abs(dR[j]) < MachinePrec) dR[j] = 0.0; diff --git a/src/core/MWFilter.cpp b/src/core/MWFilter.cpp index 8ada4f2ca..19ed535cd 100644 --- a/src/core/MWFilter.cpp +++ b/src/core/MWFilter.cpp @@ -206,14 +206,14 @@ void MWFilter::generateBlocks() { int K = this->order + 1; - double dH[K]; - double dG[K]; + std::vector dH(K); + std::vector dG(K); /* read H0 and G0 from disk */ this->G0 = Eigen::MatrixXd::Zero(K, K); this->H0 = Eigen::MatrixXd::Zero(K, K); for (int i = 0; i < K; i++) { - H_fis.read((char *)dH, sizeof(double) * K); - G_fis.read((char *)dG, sizeof(double) * K); + H_fis.read(reinterpret_cast(dH.data()), sizeof(double) * K); + G_fis.read(reinterpret_cast(dG.data()), sizeof(double) * K); for (int j = 0; j < K; j++) { this->G0(i, j) = dG[j]; // G0 this->H0(i, j) = dH[j]; // H0 diff --git a/src/functions/GaussExp.cpp b/src/functions/GaussExp.cpp index a57fe6708..21432686d 100644 --- a/src/functions/GaussExp.cpp +++ b/src/functions/GaussExp.cpp @@ -41,7 +41,7 @@ namespace mrcpp { template double GaussExp::defaultScreening = 10.0; -template GaussExp::GaussExp(int nTerms, double prec) { +template GaussExp::GaussExp(int nTerms) { for (int i = 0; i < nTerms; i++) { this->funcs.push_back(nullptr); } } diff --git a/src/functions/GaussExp.h b/src/functions/GaussExp.h index a4315e381..382213214 100644 --- a/src/functions/GaussExp.h +++ b/src/functions/GaussExp.h @@ -35,8 +35,6 @@ namespace mrcpp { -#define GAUSS_EXP_PREC 1.e-10 - /** @class GaussExp * * @brief Gaussian expansion in D dimensions @@ -53,7 +51,7 @@ namespace mrcpp { template class GaussExp : public RepresentableFunction { public: - GaussExp(int nTerms = 0, double prec = GAUSS_EXP_PREC); + GaussExp(int nTerms = 0); GaussExp(const GaussExp &gExp); GaussExp &operator=(const GaussExp &gExp); ~GaussExp() override; diff --git a/src/functions/Gaussian.cpp b/src/functions/Gaussian.cpp index 6dbfa7c5b..0b8e3214b 100644 --- a/src/functions/Gaussian.cpp +++ b/src/functions/Gaussian.cpp @@ -194,7 +194,6 @@ template GaussExp Gaussian::periodify(const std::array auto needed_cells_vec = std::vector(); for (auto i = 0; i < D; i++) { auto upper_bound = pos[i] + x_std; - auto lower_bound = pos[i] - x_std; // number of cells upp and down relative to the center of the Gaussian needed_cells_vec.push_back(std::ceil(upper_bound / period[i])); } diff --git a/src/treebuilders/ABGVCalculator.cpp b/src/treebuilders/ABGVCalculator.cpp index 10252f46a..376068b5d 100644 --- a/src/treebuilders/ABGVCalculator.cpp +++ b/src/treebuilders/ABGVCalculator.cpp @@ -48,7 +48,7 @@ ABGVCalculator::ABGVCalculator(const ScalingBasis &basis, double a, double b) void ABGVCalculator::calcValueVectors(const ScalingBasis &basis) { int kp1 = basis.getQuadratureOrder(); - double sqrtCoef[kp1]; + std::vector sqrtCoef(kp1); for (int i = 0; i < kp1; i++) { sqrtCoef[i] = std::sqrt(2.0 * i + 1.0); } switch (basis.getScalingType()) { @@ -74,7 +74,7 @@ void ABGVCalculator::calcValueVectors(const ScalingBasis &basis) { void ABGVCalculator::calcKMatrix(const ScalingBasis &basis) { int kp1 = basis.getQuadratureOrder(); - double sqrtCoef[kp1]; + std::vector sqrtCoef(kp1); for (int i = 0; i < kp1; i++) { sqrtCoef[i] = std::sqrt(2.0 * i + 1.0); } getQuadratureCache(qCache); const VectorXd &roots = qCache.getRoots(kp1); @@ -105,7 +105,6 @@ void ABGVCalculator::calcNode(MWNode<2> &node) { node.zeroCoefs(); const auto &idx = node.getNodeIndex(); - int l = idx[1] - idx[0]; int np1 = idx.getScale() + 1; int kp1 = node.getKp1(); int kp1_d = node.getKp1_d(); diff --git a/src/treebuilders/CMakeLists.txt b/src/treebuilders/CMakeLists.txt index ab0ef0e80..4a1a95ab0 100644 --- a/src/treebuilders/CMakeLists.txt +++ b/src/treebuilders/CMakeLists.txt @@ -13,7 +13,7 @@ target_sources(mrcpp ${CMAKE_CURRENT_SOURCE_DIR}/add.cpp ${CMAKE_CURRENT_SOURCE_DIR}/apply.cpp ${CMAKE_CURRENT_SOURCE_DIR}/grid.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/map.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/treeMap.cpp ${CMAKE_CURRENT_SOURCE_DIR}/multiply.cpp ${CMAKE_CURRENT_SOURCE_DIR}/project.cpp ${CMAKE_CURRENT_SOURCE_DIR}/complex_apply.cpp @@ -48,7 +48,7 @@ list(APPEND ${_dirname}_h ${CMAKE_CURRENT_SOURCE_DIR}/add.h ${CMAKE_CURRENT_SOURCE_DIR}/apply.h ${CMAKE_CURRENT_SOURCE_DIR}/grid.h - ${CMAKE_CURRENT_SOURCE_DIR}/map.h + ${CMAKE_CURRENT_SOURCE_DIR}/treeMap.h ${CMAKE_CURRENT_SOURCE_DIR}/multiply.h ${CMAKE_CURRENT_SOURCE_DIR}/project.h ${CMAKE_CURRENT_SOURCE_DIR}/complex_apply.h diff --git a/src/treebuilders/ConvolutionCalculator.cpp b/src/treebuilders/ConvolutionCalculator.cpp index 497fe0dd8..8cfcac1e6 100644 --- a/src/treebuilders/ConvolutionCalculator.cpp +++ b/src/treebuilders/ConvolutionCalculator.cpp @@ -143,7 +143,6 @@ template MWNodeVector *ConvolutionCalculator::ma auto *band = new MWNodeVector; int o_depth = gNode.getScale() - this->oper->getOperatorRoot(); - int g_depth = gNode.getDepth(); int width = this->oper->getMaxBandWidth(o_depth); bool periodic = gNode.getMWTree().isPeriodic(); @@ -228,8 +227,8 @@ template void ConvolutionCalculator::calcNode(MWNodeoper->getOperatorRoot(); if (manipulateOperator and this->oper->getOperatorRoot() < 0) o_depth = gNode.getDepth(); - T tmpCoefs[gNode.getNCoefs()]; - OperatorState os(gNode, tmpCoefs); + std::vector tmpCoefs(gNode.getNCoefs()); + OperatorState os(gNode, tmpCoefs.data()); this->operStat.incrementGNodeCounters(gNode); // Get all nodes in f within the bandwith of O in g diff --git a/src/treebuilders/DerivativeCalculator.cpp b/src/treebuilders/DerivativeCalculator.cpp index b298d1b6e..f4936c01d 100644 --- a/src/treebuilders/DerivativeCalculator.cpp +++ b/src/treebuilders/DerivativeCalculator.cpp @@ -90,8 +90,8 @@ template void DerivativeCalculator::calcNode(MWNodeoper->getMaxBandWidth() > 1) MSG_ABORT("Only implemented for zero bw"); outNode.zeroCoefs(); int nComp = (1 << D); - T tmpCoefs[outNode.getNCoefs()]; - OperatorState os(outNode, tmpCoefs); + std::vector tmpCoefs(outNode.getNCoefs()); + OperatorState os(outNode, tmpCoefs.data()); os.setFNode(inpNode); os.setFIndex(inpNode.nodeIndex); @@ -116,8 +116,8 @@ template void DerivativeCalculator::calcNode(MWNode os(gNode, tmpCoefs); + std::vector tmpCoefs(gNode.getNCoefs()); + OperatorState os(gNode, tmpCoefs.data()); this->operStat.incrementGNodeCounters(gNode); // Get all nodes in f within the bandwith of O in g @@ -182,11 +182,8 @@ template void DerivativeCalculator::applyOperator_bw0( // cout<<" applyOperator "< &gNode = *os.gNode; MWNode &fNode = *os.fNode; - const NodeIndex &fIdx = *os.fIdx; - const NodeIndex &gIdx = gNode.getNodeIndex(); int depth = gNode.getDepth(); - double oNorm = 1.0; double **oData = os.getOperData(); for (int d = 0; d < D; d++) { @@ -218,7 +215,6 @@ template void DerivativeCalculator::applyOperator(Oper const NodeIndex &gIdx = gNode.getNodeIndex(); int depth = gNode.getDepth(); - double oNorm = 1.0; double **oData = os.getOperData(); for (int d = 0; d < D; d++) { @@ -236,8 +232,6 @@ template void DerivativeCalculator::applyOperator(Oper const OperatorNode &oNode = oTree.getNode(depth, oTransl); int oIdx = os.getOperIndex(d); - double ocn = oNode.getComponentNorm(oIdx); - oNorm *= ocn; if (this->applyDir == d) { oData[d] = const_cast(oNode.getCoefs()) + oIdx * os.kp1_2; } else { diff --git a/src/treebuilders/ProjectionCalculator.cpp b/src/treebuilders/ProjectionCalculator.cpp index 931733232..15ba014da 100644 --- a/src/treebuilders/ProjectionCalculator.cpp +++ b/src/treebuilders/ProjectionCalculator.cpp @@ -25,6 +25,7 @@ #include "ProjectionCalculator.h" #include "trees/MWNode.h" +#include using Eigen::MatrixXd; diff --git a/src/treebuilders/apply.cpp b/src/treebuilders/apply.cpp index 77d8674be..edc8db17e 100644 --- a/src/treebuilders/apply.cpp +++ b/src/treebuilders/apply.cpp @@ -118,6 +118,7 @@ template void apply(double prec, FunctionTree &out, Co */ template void apply(double prec, CompFunction &out, ConvolutionOperator &oper, const CompFunction &inp, const ComplexDouble (*metric)[4], int maxIter, bool absPrec) { + out = inp.paramCopy(true); for (int icomp = 0; icomp < inp.Ncomp(); icomp++) { for (int ocomp = 0; ocomp < 4; ocomp++) { if (std::norm(metric[icomp][ocomp]) > MachinePrec) { @@ -252,6 +253,7 @@ template void apply(double prec, FunctionTree &out, Co template void apply(double prec, CompFunction &out, ConvolutionOperator &oper, CompFunction &inp, FunctionTreeVector *precTrees, const ComplexDouble (*metric)[4], int maxIter, bool absPrec) { + out = inp.paramCopy(true); for (int icomp = 0; icomp < inp.Ncomp(); icomp++) { for (int ocomp = 0; ocomp < 4; ocomp++) { if (std::norm(metric[icomp][ocomp]) > MachinePrec) { @@ -295,6 +297,7 @@ template void apply_far_field(double prec, FunctionTree void apply_far_field(double prec, CompFunction &out, ConvolutionOperator &oper, CompFunction &inp, const ComplexDouble (*metric)[4], int maxIter, bool absPrec) { + out = inp.paramCopy(true); for (int icomp = 0; icomp < 4; icomp++) { if (inp.Comp[icomp] != nullptr) { for (int ocomp = 0; ocomp < 4; ocomp++) { @@ -411,6 +414,7 @@ template void apply(FunctionTree &out, DerivativeOpera template void apply(CompFunction &out, DerivativeOperator &oper, CompFunction &inp, int dir, const ComplexDouble (*metric)[4]) { // TODO: sums and not only each components independently, when concrete examples with non diagonal metric are tested + out = inp.paramCopy(true); // note that this will copy the factor of inp (inp.func_ptr->data.c1) for (int icomp = 0; icomp < inp.Ncomp(); icomp++) { for (int ocomp = 0; ocomp < 4; ocomp++) { if (std::norm(metric[icomp][ocomp]) > MachinePrec) { @@ -426,7 +430,7 @@ template void apply(CompFunction &out, DerivativeOperator &oper, C } else { if (inp.isreal()) { apply(*out.CompD[ocomp], oper, *inp.CompD[icomp], dir); - out.CompD[icomp]->CopyTreeToComplex(out.CompC[ocomp]); + out.CompC[ocomp] = out.CompD[ocomp]->CopyTreeToComplex(); out.func_ptr->isreal = 0; out.func_ptr->iscomplex = 1; } else { @@ -469,7 +473,7 @@ std::vector *> gradient(DerivativeOperator<3> &oper, CompFunctio for (int icomp = 0; icomp < inp.Ncomp(); icomp++) { for (int ocomp = 0; ocomp < 4; ocomp++) { if (std::norm(metric[icomp][ocomp]) > MachinePrec) { - grad_d->func_ptr->Ncomp = ocomp; + grad_d->func_ptr->Ncomp = ocomp + 1; if (inp.isreal()) { grad_d->func_ptr->isreal = 1; grad_d->func_ptr->iscomplex = 0; @@ -609,9 +613,6 @@ template void divergence<3, ComplexDouble>(FunctionTree<3, ComplexDouble> &out, template void divergence<1, ComplexDouble>(FunctionTree<1, ComplexDouble> &out, DerivativeOperator<1> &oper, std::vector *> &inp); template void divergence<2, ComplexDouble>(FunctionTree<2, ComplexDouble> &out, DerivativeOperator<2> &oper, std::vector *> &inp); template void divergence<3, ComplexDouble>(FunctionTree<3, ComplexDouble> &out, DerivativeOperator<3> &oper, std::vector *> &inp); -template FunctionTreeVector<1, ComplexDouble> gradient<1>(DerivativeOperator<1> &oper, FunctionTree<1, ComplexDouble> &inp); -template FunctionTreeVector<2, ComplexDouble> gradient<2>(DerivativeOperator<2> &oper, FunctionTree<2, ComplexDouble> &inp); -template FunctionTreeVector<3, ComplexDouble> gradient<3>(DerivativeOperator<3> &oper, FunctionTree<3, ComplexDouble> &inp); template void apply(CompFunction<3> &out, DerivativeOperator<3> &oper, CompFunction<3> &inp, int dir = -1, const ComplexDouble (*metric)[4]); diff --git a/src/treebuilders/apply.h b/src/treebuilders/apply.h index e50b3b870..fa5a43661 100644 --- a/src/treebuilders/apply.h +++ b/src/treebuilders/apply.h @@ -53,8 +53,7 @@ template void divergence(CompFunction &out, DerivativeOpe template void divergence(FunctionTree &out, DerivativeOperator &oper, std::vector *> &inp); template void divergence(CompFunction &out, DerivativeOperator &oper, std::vector *> *inp, const ComplexDouble (*metric)[4] = defaultMetric); template FunctionTreeVector gradient(DerivativeOperator &oper, FunctionTree &inp); -// template -std::vector*> gradient(DerivativeOperator<3> &oper, CompFunction<3> &inp, ComplexDouble (*metric)[4] = nullptr); +std::vector*> gradient(DerivativeOperator<3> &oper, CompFunction<3> &inp, const ComplexDouble (*metric)[4] = defaultMetric); // clang-format on } // namespace mrcpp diff --git a/src/treebuilders/grid.cpp b/src/treebuilders/grid.cpp index 0e7fb968b..eaaf3696b 100644 --- a/src/treebuilders/grid.cpp +++ b/src/treebuilders/grid.cpp @@ -113,7 +113,6 @@ template void build_grid(FunctionTree &out, const GaussExp &inp, i builder.build(out, calculator, adaptor, maxIter); } } else { - auto period = out.getMRA().getWorldBox().getScalingFactors(); for (auto i = 0; i < inp.size(); i++) { auto *gauss = inp.getFunc(i).copy(); build_grid(out, *gauss, maxIter); diff --git a/src/treebuilders/multiply.cpp b/src/treebuilders/multiply.cpp index 4e046126e..46587ffaa 100644 --- a/src/treebuilders/multiply.cpp +++ b/src/treebuilders/multiply.cpp @@ -334,8 +334,8 @@ template double node_norm_dot(FunctionTree &bra, Funct double result = 0.0; int ncoef = bra.getKp1_d() * bra.getTDim(); - T valA[ncoef]; - T valB[ncoef]; + std::vector valA(ncoef); + std::vector valB(ncoef); int nNodes = bra.getNEndNodes(); for (int n = 0; n < nNodes; n++) { @@ -345,8 +345,8 @@ template double node_norm_dot(FunctionTree &bra, Funct // convert to interpolating coef, take abs, convert back FunctionNode *mwNode = static_cast *>(ket.findNode(idx)); if (mwNode == nullptr) MSG_ABORT("Trees must have same grid"); - node.getAbsCoefs(valA); - mwNode->getAbsCoefs(valB); + node.getAbsCoefs(valA.data()); + mwNode->getAbsCoefs(valB.data()); for (int i = 0; i < ncoef; i++) result += std::norm(valA[i] * valB[i]); } else { // approximate by product of node norms diff --git a/src/treebuilders/map.cpp b/src/treebuilders/treeMap.cpp similarity index 66% rename from src/treebuilders/map.cpp rename to src/treebuilders/treeMap.cpp index b363bf806..ddf95fa26 100644 --- a/src/treebuilders/map.cpp +++ b/src/treebuilders/treeMap.cpp @@ -23,7 +23,7 @@ * */ -#include "map.h" +#include "treeMap.h" #include "MapCalculator.h" #include "MultiplicationCalculator.h" #include "TreeBuilder.h" @@ -38,7 +38,7 @@ namespace mrcpp { -/** @brief map a MW function onto another representations, adaptive grid +/** @brief treeMap a MW function onto another representations, adaptive grid * * @param[in] prec: Build precision of output function * @param[out] out: Output function to be built @@ -65,12 +65,12 @@ namespace mrcpp { * no coefs). * */ -template void map(double prec, FunctionTree &out, FunctionTree &inp, FMap fmap, int maxIter, bool absPrec) { +template void treeMap(double prec, FunctionTree &out, FunctionTree &inp, FMap fmap, int maxIter, bool absPrec) { int maxScale = out.getMRA().getMaxScale(); - TreeBuilder builder; - WaveletAdaptor adaptor(prec, maxScale, absPrec); - MapCalculator calculator(fmap, inp); + TreeBuilder builder; + WaveletAdaptor adaptor(prec, maxScale, absPrec); + MapCalculator calculator(fmap, inp); builder.build(out, calculator, adaptor, maxIter); @@ -88,8 +88,11 @@ template void map(double prec, FunctionTree &out, FunctionTre print::separator(10, ' '); } -template void map<1>(double prec, FunctionTree<1, double> &out, FunctionTree<1, double> &inp, FMap fmap, int maxIter, bool absPrec); -template void map<2>(double prec, FunctionTree<2, double> &out, FunctionTree<2, double> &inp, FMap fmap, int maxIter, bool absPrec); -template void map<3>(double prec, FunctionTree<3, double> &out, FunctionTree<3, double> &inp, FMap fmap, int maxIter, bool absPrec); +template void treeMap<1, double>(double prec, FunctionTree<1, double> &out, FunctionTree<1, double> &inp, FMap fmap, int maxIter, bool absPrec); +template void treeMap<2, double>(double prec, FunctionTree<2, double> &out, FunctionTree<2, double> &inp, FMap fmap, int maxIter, bool absPrec); +template void treeMap<3, double>(double prec, FunctionTree<3, double> &out, FunctionTree<3, double> &inp, FMap fmap, int maxIter, bool absPrec); +template void treeMap<1, ComplexDouble>(double prec, FunctionTree<1, ComplexDouble> &out, FunctionTree<1, ComplexDouble> &inp, FMap fmap, int maxIter, bool absPrec); +template void treeMap<2, ComplexDouble>(double prec, FunctionTree<2, ComplexDouble> &out, FunctionTree<2, ComplexDouble> &inp, FMap fmap, int maxIter, bool absPrec); +template void treeMap<3, ComplexDouble>(double prec, FunctionTree<3, ComplexDouble> &out, FunctionTree<3, ComplexDouble> &inp, FMap fmap, int maxIter, bool absPrec); } // Namespace mrcpp diff --git a/src/treebuilders/map.h b/src/treebuilders/treeMap.h similarity index 88% rename from src/treebuilders/map.h rename to src/treebuilders/treeMap.h index d1f86e201..8fe96a2e0 100644 --- a/src/treebuilders/map.h +++ b/src/treebuilders/treeMap.h @@ -30,6 +30,6 @@ namespace mrcpp { template class FunctionTree; -template void map(double prec, FunctionTree &out, FunctionTree &inp, FMap fmap, int maxIter = -1, bool absPrec = false); +template void treeMap(double prec, FunctionTree &out, FunctionTree &inp, FMap fmap, int maxIter = -1, bool absPrec = false); } // namespace mrcpp diff --git a/src/trees/FunctionNode.cpp b/src/trees/FunctionNode.cpp index ff23fb394..102e5bd26 100644 --- a/src/trees/FunctionNode.cpp +++ b/src/trees/FunctionNode.cpp @@ -130,7 +130,7 @@ template T FunctionNode::integrateInterpolating() cons int qOrder = this->getKp1(); getQuadratureCache(qc); const VectorXd &weights = qc.getWeights(qOrder); - double sqWeights[qOrder]; + std::vector sqWeights(qOrder); for (int i = 0; i < qOrder; i++) sqWeights[i] = std::sqrt(weights[i]); int kp1_p[D]; @@ -170,7 +170,7 @@ template T FunctionNode::integrateValues() const { this->getCoefs(coefs); int ncoefs = coefs.size(); int ncoefChild = ncoefs / (1 << D); - T cc[ncoefChild]; + std::vector cc(ncoefChild); // factorize out the children for (int i = 0; i < ncoefChild; i++) cc[i] = coefs[i]; for (int j = 1; j < (1 << D); j++) diff --git a/src/trees/FunctionTree.cpp b/src/trees/FunctionTree.cpp index a41581692..7d5b764a9 100644 --- a/src/trees/FunctionTree.cpp +++ b/src/trees/FunctionTree.cpp @@ -31,6 +31,7 @@ #include "NodeAllocator.h" #include "treebuilders/grid.h" +#include "treebuilders/add.h" #include "utils/Bank.h" #include "utils/Printer.h" #include "utils/Timer.h" @@ -54,7 +55,8 @@ namespace mrcpp { template FunctionTree::FunctionTree(const MultiResolutionAnalysis &mra, SharedMemory *sh_mem, const std::string &name) : MWTree(mra, name) - , RepresentableFunction(mra.getWorldBox().getLowerBounds().data(), mra.getWorldBox().getUpperBounds().data()) { + , RepresentableFunction(mra.getWorldBox().getLowerBounds().data(), + mra.getWorldBox().getUpperBounds().data()) { int nodesPerChunk = 2048; // Large chunks are required for not leading to memory fragmentation (32 MB on "Betzy" 2023) // nodesPerChunk is same for real and complex trees: the size (in MB) of the complex chunks are twice as large int coefsGenNodes = this->getKp1_d(); @@ -65,6 +67,22 @@ FunctionTree::FunctionTree(const MultiResolutionAnalysis &mra, SharedMe this->resetEndNodeTable(); } +template +template +FunctionTree::FunctionTree(FunctionTree &realTree, + FunctionTree &imagTree, + SharedMemory *sh_mem, + const std::string &name) + : FunctionTree(realTree.getMRA(), sh_mem, name) +{ + this->setZero(); + static_assert(std::is_same_v, "This constructor requires T = ComplexDouble"); + std::unique_ptr> real_p(realTree.CopyTreeToComplex()); + std::unique_ptr> imag_p(imagTree.CopyTreeToComplex()); + this->add_inplace(ComplexDouble(1.0, 0.0), *real_p); + this->add_inplace(ComplexDouble(0.0, 1.0), *imag_p); +} + template void FunctionTree::allocRootNodes() { auto &allocator = this->getNodeAllocator(); auto &rootbox = this->getRootBox(); @@ -290,9 +308,6 @@ template void FunctionTree::loadTreeTXT(const std::str * @param[in] file: File name */ template void FunctionTree::saveTreeTXT(const std::string &fname) { - int nRoots = this->getRootBox().size(); - MWNode **roots = this->getRootBox().getNodes(); - std::ofstream out(fname); out << std::setprecision(14); out << D << std::endl; @@ -306,6 +321,7 @@ template void FunctionTree::saveTreeTXT(const std::str int ncoefs = 1; for (int d = 0; d < D; d++) ncoefs *= kp1; int Tdim = std::pow(2, D); + std::vector values(ncoefs * Tdim); int nout = this->endNodeTable.size(); out << Tdim * nout << std::endl; // could output only scaling coeff? @@ -331,7 +347,9 @@ template void FunctionTree::saveTreeTXT(const std::str std::array l; NodeIndex idx = this->endNodeTable[count]->getNodeIndex(); MWNode *node = &(this->getNode(idx, false)); - T *values = node->getCoefs(); + T *coefs = node->getCoefs(); + for (int i = 0; i < ncoefs * Tdim; i++) values[i] = coefs[i]; + node->attachCoefs(values.data()); int n = idx.getScale(); node->mwTransform(Reconstruction); node->cvTransform(Forward); @@ -350,7 +368,8 @@ template void FunctionTree::saveTreeTXT(const std::str for (int i = 0; i < ncoefs; i++) out << values[cix * ncoefs + mapMRC[i]] << " "; out << std::endl; } - } + node->attachCoefs(coefs); // put back original coeff + } out.close(); } /** @brief Write the tree structure to disk, for later use @@ -358,9 +377,9 @@ template void FunctionTree::saveTreeTXT(const std::str */ template void FunctionTree::saveTree(const std::string &file) { Timer t1; + this->deleteGenerated(); auto &allocator = this->getNodeAllocator(); - std::stringstream fname; fname << file << ".tree"; std::fstream f; @@ -370,14 +389,12 @@ template void FunctionTree::saveTree(const std::string // Write size of tree int nChunks = allocator.getNChunksUsed(); f.write((char *)&nChunks, sizeof(int)); - // Write tree data, chunk by chunk for (int iChunk = 0; iChunk < nChunks; iChunk++) { - f.write((char *)allocator.getNodeChunk(iChunk), allocator.getNodeChunkSize()); - f.write((char *)allocator.getCoefChunk(iChunk), allocator.getCoefChunkSize()); + f.write((char *)allocator.getNodeChunk(iChunk), allocator.getNodeChunkSize()); + f.write((char *)allocator.getCoefChunk(iChunk), allocator.getCoefChunkSize()); } f.close(); - this->saveTreeTXT("MRC.dat"); print::time(10, "Time write", t1); } @@ -436,12 +453,31 @@ template T FunctionTree::integrate() const { return jacobian * result; } +template T FunctionTree::integrateSide(int dim, bool positiveSide) const { + T result = 0.0; + for (int i = 0; i < this->rootBox.size(); i++) { + const FunctionNode &fNode = getRootFuncNode(i); + if (fNode.getLowerBounds()[dim] == this->rootBox.getLowerBound(dim) && not positiveSide) + result += fNode.integrate(); + if (fNode.getUpperBounds()[dim] == this->rootBox.getUpperBound(dim) && positiveSide) + result += fNode.integrate(); + } + + // Handle potential scaling + auto scaling_factor = this->getMRA().getWorldBox().getScalingFactors(); + auto jacobian = 1.0; + for (const auto &sf_i : scaling_factor) jacobian *= std::sqrt(sf_i); + // Square root of scaling factor in each diection. The seemingly missing + // multiplication by the square root of sf_i is included in the basis + + return jacobian * result; +} + /** @returns Integral of a representable function over the grid given by the tree */ template <> double FunctionTree<3, double>::integrateEndNodes(RepresentableFunction_M &f) { // traverse tree, and treat end nodes only std::vector *> stack; // node from this for (int i = 0; i < this->getRootBox().size(); i++) stack.push_back(&(this->getRootFuncNode(i))); - int basis = getMRA().getScalingBasis().getScalingType(); double result = 0.0; while (stack.size() > 0) { FunctionNode<3> *Node = stack.back(); @@ -454,9 +490,9 @@ template <> double FunctionTree<3, double>::integrateEndNodes(RepresentableFunct double *coefs = Node->getCoefs(); // save position of coeff, but do not use them! // The data in fmat is not organized so that two consecutive points are stored after each other in memory, so needs to copy before mwtransform, cannot use memory adress directly. int nc = fmat.cols(); - double cc[nc]; + std::vector cc(nc); for (int i = 0; i < nc; i++) cc[i] = fmat(0, i); - Node->attachCoefs(cc); + Node->attachCoefs(cc.data()); result += Node->integrateValues(); Node->attachCoefs(coefs); // put back original coeff } @@ -878,8 +914,6 @@ void FunctionTree::makeCoeffVector(std::vector &coefs, indices.clear(); parent_indices.clear(); max_index = 0; - int sizecoeff = (1 << refTree.getDim()) * refTree.getKp1_d(); - int sizecoeffW = ((1 << refTree.getDim()) - 1) * refTree.getKp1_d(); std::vector *> refstack; // nodes from refTree std::vector *> thisstack; // nodes from this Tree for (int rIdx = 0; rIdx < this->getRootBox().size(); rIdx++) { @@ -1094,7 +1128,6 @@ template <> int FunctionTree<3, double>::saveNodesAndRmCoeff() { for (int rIdx = 0; rIdx < this->getRootBox().size(); rIdx++) { stack.push_back(this->getRootBox().getNodes()[rIdx]); } while (stack.size() > stack_p) { MWNode<3, double> *Node = stack[stack_p++]; - int id = 0; NodesCoeff->put_data(Node->getNodeIndex(), sizecoeff, Node->getCoefs()); for (int i = 0; i < Node->getNChildren(); i++) { stack.push_back(Node->children[i]); } } @@ -1117,7 +1150,6 @@ template <> int FunctionTree<3, ComplexDouble>::saveNodesAndRmCoeff() { for (int rIdx = 0; rIdx < this->getRootBox().size(); rIdx++) { stack.push_back(this->getRootBox().getNodes()[rIdx]); } while (stack.size() > stack_p) { MWNode<3, ComplexDouble> *Node = stack[stack_p++]; - int id = 0; NodesCoeff->put_data(Node->getNodeIndex(), sizecoeff, Node->getCoefs()); for (int i = 0; i < Node->getNChildren(); i++) { stack.push_back(Node->children[i]); } } @@ -1142,19 +1174,22 @@ template void FunctionTree::deep_copy(FunctionTree FunctionTree *FunctionTree::Real() { FunctionTree *out = new FunctionTree(this->getMRA(), this->getName()); -#pragma omp parallel num_threads(mrcpp_get_num_threads()) + out->setZero(); + // TODO: why does the omp parallelism not always work here? + //#pragma omp parallel num_threads(mrcpp_get_num_threads()) { int nNodes = this->getNEndNodes(); -#pragma omp for schedule(guided) + //#pragma omp for schedule(guided) for (int n = 0; n < nNodes; n++) { MWNode &inp_node = *this->endNodeTable[n]; - MWNode out_node = out->getNode(out_node.getNodeIndex()); // Full copy + MWNode &out_node = out->getNode(inp_node.getNodeIndex(), true); double *out_coefs = out_node.getCoefs(); const T *inp_coefs = inp_node.getCoefs(); for (int i = 0; i < inp_node.getNCoefs(); i++) { out_coefs[i] = std::real(inp_coefs[i]); } out_node.calcNorms(); } } + out->resetEndNodeTable(); out->mwTransform(BottomUp); out->calcSquareNorm(); return out; @@ -1164,13 +1199,14 @@ template FunctionTree *FunctionTree::Real() */ template FunctionTree *FunctionTree::Imag() { FunctionTree *out = new FunctionTree(this->getMRA(), this->getName()); -#pragma omp parallel num_threads(mrcpp_get_num_threads()) + out->setZero(); + //#pragma omp parallel num_threads(mrcpp_get_num_threads()) { int nNodes = this->getNEndNodes(); -#pragma omp for schedule(guided) + //#pragma omp for schedule(guided) for (int n = 0; n < nNodes; n++) { MWNode &inp_node = *this->endNodeTable[n]; - MWNode out_node = out->getNode(out_node.getNodeIndex()); // Full copy + MWNode &out_node = out->getNode(inp_node.getNodeIndex(), true); double *out_coefs = out_node.getCoefs(); const T *inp_coefs = inp_node.getCoefs(); for (int i = 0; i < inp_node.getNCoefs(); i++) { out_coefs[i] = std::imag(inp_coefs[i]); } @@ -1186,65 +1222,23 @@ template FunctionTree *FunctionTree::Imag() * From real to complex tree. Copy everything, and convert double to ComplexDouble for the coefficents. * Should use a deep_copy if generalized in the future. */ - -template <> void FunctionTree<3, double>::CopyTreeToComplex(FunctionTree<3, ComplexDouble> *&outTree) { - delete outTree; - double ref = 0.0; - outTree = new FunctionTree<3, ComplexDouble>(this->getMRA()); - std::vector *> instack; // node from this - std::vector *> outstack; // node from outTree - outTree->clearEndNodeTable(); - for (int rIdx = 0; rIdx < this->getRootBox().size(); rIdx++) { - instack.push_back(this->getRootBox().getNodes()[rIdx]); - outstack.push_back(outTree->getRootBox().getNodes()[rIdx]); - } - int nNodes = std::min(this->getNNodes(), this->getNodeAllocator().getMaxNodesPerChunk()); - int ncoefs = this->getNodeAllocator().getNCoefs(); - while (instack.size() > 0) { - // inNode and outNode are the same node in space, but on different trees - MWNode<3, ComplexDouble> *outNode = outstack.back(); - outstack.pop_back(); - MWNode<3, double> *inNode = instack.back(); - instack.pop_back(); - // copy coefficients: - double *incoefs = inNode->getCoefs(); - ComplexDouble *outcoefs = outNode->getCoefs(); - for (int i = 0; i < ncoefs; i++) outcoefs[i] = incoefs[i]; - outNode->setHasCoefs(); - outNode->calcNorms(); - - if (inNode->getNChildren() > 0) { - if (outNode->getNChildren() < inNode->getNChildren()) outNode->createChildren(true); - for (int i = 0; i < inNode->getNChildren(); i++) { - instack.push_back(inNode->children[i]); - outstack.push_back(outNode->children[i]); - } - } else { - outTree->endNodeTable.push_back(outNode); - } - } - outTree->calcSquareNorm(); - outTree->calcSquareNorm(true); -} - -template <> void FunctionTree<2, double>::CopyTreeToComplex(FunctionTree<2, ComplexDouble> *&outTree) { - delete outTree; - double ref = 0.0; - outTree = new FunctionTree<2, ComplexDouble>(this->getMRA()); - std::vector *> instack; // node from this - std::vector *> outstack; // node from outTree +template +template +FunctionTree* FunctionTree::CopyTreeToComplex() { + auto* outTree = new FunctionTree(this->getMRA()); + std::vector *> instack; // node from this + std::vector *> outstack; // node from outTree outTree->clearEndNodeTable(); for (int rIdx = 0; rIdx < this->getRootBox().size(); rIdx++) { instack.push_back(this->getRootBox().getNodes()[rIdx]); outstack.push_back(outTree->getRootBox().getNodes()[rIdx]); } - int nNodes = std::min(this->getNNodes(), this->getNodeAllocator().getMaxNodesPerChunk()); int ncoefs = this->getNodeAllocator().getNCoefs(); while (instack.size() > 0) { // inNode and outNode are the same node in space, but on different trees - MWNode<2, ComplexDouble> *outNode = outstack.back(); + MWNode *outNode = outstack.back(); outstack.pop_back(); - MWNode<2, double> *inNode = instack.back(); + MWNode *inNode = instack.back(); instack.pop_back(); // copy coefficients: double *incoefs = inNode->getCoefs(); @@ -1263,70 +1257,31 @@ template <> void FunctionTree<2, double>::CopyTreeToComplex(FunctionTree<2, Comp outTree->endNodeTable.push_back(outNode); } } - outTree->calcSquareNorm(); - outTree->calcSquareNorm(true); -} -template <> void FunctionTree<1, double>::CopyTreeToComplex(FunctionTree<1, ComplexDouble> *&outTree) { - delete outTree; - double ref = 0.0; - outTree = new FunctionTree<1, ComplexDouble>(this->getMRA()); - std::vector *> instack; // node from this - std::vector *> outstack; // node from outTree - outTree->clearEndNodeTable(); - for (int rIdx = 0; rIdx < this->getRootBox().size(); rIdx++) { - instack.push_back(this->getRootBox().getNodes()[rIdx]); - outstack.push_back(outTree->getRootBox().getNodes()[rIdx]); - } - int nNodes = std::min(this->getNNodes(), this->getNodeAllocator().getMaxNodesPerChunk()); - int ncoefs = this->getNodeAllocator().getNCoefs(); - while (instack.size() > 0) { - // inNode and outNode are the same node in space, but on different trees - MWNode<1, ComplexDouble> *outNode = outstack.back(); - outstack.pop_back(); - MWNode<1, double> *inNode = instack.back(); - instack.pop_back(); - // copy coefficients: - double *incoefs = inNode->getCoefs(); - ComplexDouble *outcoefs = outNode->getCoefs(); - for (int i = 0; i < ncoefs; i++) outcoefs[i] = incoefs[i]; - outNode->setHasCoefs(); - outNode->calcNorms(); - - if (inNode->getNChildren() > 0) { - if (outNode->getNChildren() < inNode->getNChildren()) outNode->createChildren(true); - for (int i = 0; i < inNode->getNChildren(); i++) { - instack.push_back(inNode->children[i]); - outstack.push_back(outNode->children[i]); - } - } else { - outTree->endNodeTable.push_back(outNode); - } - } outTree->calcSquareNorm(); outTree->calcSquareNorm(true); + return outTree; } // for testing -template <> void FunctionTree<3, double>::CopyTreeToReal(FunctionTree<3, double> *&outTree) { - delete outTree; - double ref = 0.0; - // FunctionTree<3, double>* inTree = this; - outTree = new FunctionTree<3, double>(this->getMRA()); - std::vector *> instack; // node from this - std::vector *> outstack; // node from outTree +template +template +FunctionTree* FunctionTree::CopyTreeToReal() { + // FunctionTree* inTree = this; + auto* outTree = new FunctionTree(this->getMRA()); + std::vector *> instack; // node from this + std::vector *> outstack; // node from outTree outTree->clearEndNodeTable(); for (int rIdx = 0; rIdx < this->getRootBox().size(); rIdx++) { instack.push_back(this->getRootBox().getNodes()[rIdx]); outstack.push_back(outTree->getRootBox().getNodes()[rIdx]); } - int nNodes = std::min(this->getNNodes(), this->getNodeAllocator().getMaxNodesPerChunk()); int ncoefs = this->getNodeAllocator().getNCoefs(); while (instack.size() > 0) { // inNode and outNode are the same node in space, but on different trees - MWNode<3, double> *outNode = outstack.back(); + MWNode *outNode = outstack.back(); outstack.pop_back(); - MWNode<3, double> *inNode = instack.back(); + MWNode *inNode = instack.back(); instack.pop_back(); // copy coefficients: double *incoefs = inNode->getCoefs(); @@ -1346,6 +1301,7 @@ template <> void FunctionTree<3, double>::CopyTreeToReal(FunctionTree<3, double> outTree->endNodeTable.push_back(outNode); } } + return outTree; } template class FunctionTree<1, double>; @@ -1356,4 +1312,31 @@ template class FunctionTree<1, ComplexDouble>; template class FunctionTree<2, ComplexDouble>; template class FunctionTree<3, ComplexDouble>; +template FunctionTree<1, ComplexDouble>* FunctionTree<1, double>::CopyTreeToComplex(); +template FunctionTree<2, ComplexDouble>* FunctionTree<2, double>::CopyTreeToComplex(); +template FunctionTree<3, ComplexDouble>* FunctionTree<3, double>::CopyTreeToComplex(); +template FunctionTree<1, double>* FunctionTree<1, double>::CopyTreeToReal(); +template FunctionTree<2, double>* FunctionTree<2, double>::CopyTreeToReal(); +template FunctionTree<3, double>* FunctionTree<3, double>::CopyTreeToReal(); +template FunctionTree<3, ComplexDouble>::FunctionTree( + FunctionTree<3, double>&, + FunctionTree<3, double>&, + SharedMemory*, + const std::string& +); + +template FunctionTree<2, ComplexDouble>::FunctionTree( + FunctionTree<2, double>&, + FunctionTree<2, double>&, + SharedMemory*, + const std::string& +); + +template FunctionTree<1, ComplexDouble>::FunctionTree( + FunctionTree<1, double>&, + FunctionTree<1, double>&, + SharedMemory*, + const std::string& +); + } // namespace mrcpp diff --git a/src/trees/FunctionTree.h b/src/trees/FunctionTree.h index 9d976d6be..cc992a82c 100644 --- a/src/trees/FunctionTree.h +++ b/src/trees/FunctionTree.h @@ -54,14 +54,62 @@ namespace mrcpp { template class FunctionTree final : public MWTree, public RepresentableFunction { public: - FunctionTree(const MultiResolutionAnalysis &mra, const std::string &name) + +/** + * @brief Constructor + * @returns New FunctionTree object + * + * @param[in] mra: Which MRA the function is defined + * @param[in] name: Name of the tree + * + * @details Constructs an uninitialized tree, containing only empty root nodes. + * Nonshared memory is used for the tree, which means that the tree is allocated locally. + */ FunctionTree(const MultiResolutionAnalysis &mra, const std::string &name) : FunctionTree(mra, nullptr, name) {} +/** + * @brief Constructor + * @returns New FunctionTree object + * + * @param[in] mra: Which MRA the function is defined + * @param[in] sh_mem: Pointer to MPI shared memory block + * @param[in] name: Name of the tree + * + * @details Constructs an uninitialized tree, containing only empty root nodes. + * If a shared memory pointer is provided the tree will be allocated in this + * shared memory window, otherwise it will be local to each MPI process. + */ FunctionTree(const MultiResolutionAnalysis &mra, SharedMemory *sh_mem = nullptr, const std::string &name = "nn"); +/** + * @brief Constructor (complex version only) + * @returns New FunctionTree object + * + * @param[in] mra: Which MRA the function is defined + * @param[in] sh_mem: Pointer to MPI shared memory block + * @param[in] name: Name of the tree + * + * @details Constructs a complex tree, given two real trees which define the real and imaginary parts of the complex function. + * If a shared memory pointer is provided the tree will be allocated in this + * shared memory window, otherwise it will be local to each MPI process. + */ + template >> + FunctionTree(FunctionTree &realTree, + FunctionTree &imagTree, + SharedMemory *sh_mem = nullptr, const std::string &name = "nn"); FunctionTree(const FunctionTree &tree) = delete; FunctionTree &operator=(const FunctionTree &tree) = delete; ~FunctionTree() override; T integrate() const; + + /** + * @brief Integrate over half of the space + * @param dim Dimension along which to split + * @param positiveSide If true, integrate over the positive side (x>0 if dim=0) + * @return Integral of the function over parts of the computational domain + */ + T integrateSide(int dim, bool positiveSide) const; + double integrateEndNodes(RepresentableFunction_M &f); T evalf_precise(const Coord &r); T evalf(const Coord &r) const override; @@ -120,10 +168,12 @@ template class FunctionTree final : public MWTree, pub void deep_copy(FunctionTree *out); FunctionTree *Real(); FunctionTree *Imag(); - void CopyTreeToComplex(FunctionTree<3, ComplexDouble> *&out); - void CopyTreeToComplex(FunctionTree<2, ComplexDouble> *&out); - void CopyTreeToComplex(FunctionTree<1, ComplexDouble> *&out); - void CopyTreeToReal(FunctionTree<3, double> *&out); // for testing + template >> + FunctionTree* CopyTreeToComplex(); + template >> + FunctionTree* CopyTreeToReal(); // for testing protected: std::unique_ptr> genNodeAllocator_p{nullptr}; diff --git a/src/trees/HilbertPath.cpp b/src/trees/HilbertPath.cpp index b052064a8..ac2529852 100644 --- a/src/trees/HilbertPath.cpp +++ b/src/trees/HilbertPath.cpp @@ -114,6 +114,21 @@ const int HilbertPath<3>::hTable[12][8] = { {4,5,7,6,3,2,0,1} }; +template +short int HilbertPath::getChildPath(int hIdx) const { + return pTable[this->path][hIdx]; +} + +template +int HilbertPath::getZIndex(int hIdx) const { + return zTable[this->path][hIdx]; +} + +template +int HilbertPath::getHIndex(int zIdx) const { + return hTable[this->path][zIdx]; +} + // clang-format on template class HilbertPath<1>; diff --git a/src/trees/HilbertPath.h b/src/trees/HilbertPath.h index 519e7ac73..981c5354f 100644 --- a/src/trees/HilbertPath.h +++ b/src/trees/HilbertPath.h @@ -42,10 +42,10 @@ template class HilbertPath final { } short int getPath() const { return this->path; } - short int getChildPath(int hIdx) const { return this->pTable[this->path][hIdx]; } + short int getChildPath(int hIdx) const; - int getZIndex(int hIdx) const { return this->zTable[this->path][hIdx]; } - int getHIndex(int zIdx) const { return this->hTable[this->path][zIdx]; } + int getZIndex(int hIdx) const; + int getHIndex(int zIdx) const; private: short int path{0}; @@ -54,4 +54,4 @@ template class HilbertPath final { static const int hTable[][8]; }; -} // namespace mrcpp +} // namespace mrcpp \ No newline at end of file diff --git a/src/trees/MWNode.cpp b/src/trees/MWNode.cpp index 2d521b468..9f40bed23 100644 --- a/src/trees/MWNode.cpp +++ b/src/trees/MWNode.cpp @@ -346,13 +346,9 @@ template void MWNode::giveChildrenCoefs(bool overwrite * copied/summed in the correct child node. */ template void MWNode::giveChildCoefs(int cIdx, bool overwrite) { - MWNode node_i = *this; - node_i.mwTransform(Reconstruction); - int kp1_d = this->getKp1_d(); - int nChildren = this->getTDim(); if (this->children[cIdx] == nullptr) MSG_ABORT("Child does not exist!"); MWNode &child = getMWChild(cIdx); @@ -457,8 +453,8 @@ template void MWNode::cvTransform(int operation, bool auto sb = this->getMWTree().getMRA().getScalingBasis(); const MatrixXd &S = sb.getCVMap(operation); - T o_vec[nCoefs]; - T *out_vec = o_vec; + std::vector o_vec(nCoefs); + T *out_vec = o_vec.data(); T *in_vec = this->coefs; int nChildren = this->getTDim(); @@ -566,8 +562,8 @@ template void MWNode::mwTransform(int operation) { const MWFilter &filter = getMWTree().getMRA().getFilter(); double overwrite = 0.0; - T o_vec[nCoefs]; - T *out_vec = o_vec; + std::vector o_vec(nCoefs); + T *out_vec = o_vec.data(); T *in_vec = this->coefs; for (int i = 0; i < D; i++) { @@ -1259,7 +1255,6 @@ template std::ostream &MWNode::print(std::ostream &o) * i.e. *not* same normalization as a squareNorm */ template void MWNode::setMaxSquareNorm() { - auto n = this->getScale(); this->maxWSquareNorm = calcScaledWSquareNorm(); this->maxSquareNorm = calcScaledSquareNorm(); @@ -1275,7 +1270,6 @@ template void MWNode::setMaxSquareNorm() { /** @brief recursively reset maxSquaredNorm and maxWSquareNorm of parent and descendants to value -1 */ template void MWNode::resetMaxSquareNorm() { - auto n = this->getScale(); this->maxSquareNorm = -1.0; this->maxWSquareNorm = -1.0; if (not this->isEndNode()) { diff --git a/src/trees/NodeAllocator.cpp b/src/trees/NodeAllocator.cpp index 5079459aa..cd3cf9fcc 100644 --- a/src/trees/NodeAllocator.cpp +++ b/src/trees/NodeAllocator.cpp @@ -128,10 +128,6 @@ template int NodeAllocator::alloc(int nNodes, bool coe // return value is index of first new node auto sIdx = this->topStack; - // we require that the index for first child is a multiple of 2**D - // so that we can find the sibling rank using rank=sIdx%(2**D) - if (sIdx % nNodes != 0) MSG_ERROR(" node allocate error"); - // fill stack status auto &status = this->stackStatus; for (int i = sIdx; i < sIdx + nNodes; i++) { diff --git a/src/trees/OperatorNode.cpp b/src/trees/OperatorNode.cpp index 37f576eac..6253ba51b 100644 --- a/src/trees/OperatorNode.cpp +++ b/src/trees/OperatorNode.cpp @@ -94,10 +94,6 @@ double OperatorNode::calcComponentNorm(int i) const { * */ MatrixXd OperatorNode::getComponent(int i) { - int depth = getDepth(); - double prec = getOperTree().getNormPrecision(); - double thrs = std::max(MachinePrec, prec / (8.0 * (1 << depth))); - VectorXd coef_vec; this->getCoefs(coef_vec); diff --git a/src/utils/Bank.cpp b/src/utils/Bank.cpp index f8c111a53..94152c06e 100644 --- a/src/utils/Bank.cpp +++ b/src/utils/Bank.cpp @@ -8,8 +8,10 @@ namespace mrcpp { using namespace Eigen; using namespace std; -int metadata_block[3]; // can add more metadata in future -int const size_metadata = 3; +#ifdef MRCPP_HAS_MPI + int metadata_block[3]; // can add more metadata in future + int const size_metadata = 3; +#endif Bank::~Bank() { // delete all data and accounts @@ -57,7 +59,9 @@ std::map *> get_orbid2block; // to get blo std::map mem; +#ifdef MRCPP_HAS_MPI int const MIN_SCALE = -999; // Smaller than smallest scale +#endif int naccounts = 0; void Bank::open() { diff --git a/src/utils/Bank.h b/src/utils/Bank.h index 69719c530..d0e032c3f 100644 --- a/src/utils/Bank.h +++ b/src/utils/Bank.h @@ -77,7 +77,10 @@ class Bank { void clear_bank(); void remove_account(int account); // remove the content and the account + #ifdef MRCPP_HAS_MPI long long totcurrentsize = 0ll; // number of kB used by all accounts + long long maxsize = 0; // max total deposited data size (without containers) + #endif std::vector accounts; // open bank accounts std::map *> get_deposits; // gives deposits of an account std::map *> get_id2ix; @@ -85,7 +88,6 @@ class Bank { std::map *> get_queue; // gives deposits of an account std::map> *> get_readytasks; // used by task manager std::map currentsize; // total deposited data size (without containers) - long long maxsize = 0; // max total deposited data size (without containers) }; class BankAccount { diff --git a/src/utils/CompFunction.cpp b/src/utils/CompFunction.cpp index 43306a346..3cddb3588 100644 --- a/src/utils/CompFunction.cpp +++ b/src/utils/CompFunction.cpp @@ -1,3 +1,28 @@ +/* + * MRCPP, a numerical library based on multiresolution analysis and + * the multiwavelet basis which provide low-scaling algorithms as well as + * rigorous error control in numerical computations. + * Copyright (C) 2021 Stig Rune Jensen, Jonas Juselius, Luca Frediani and contributors. + * + * This file is part of MRCPP. + * + * MRCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MRCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with MRCPP. If not, see . + * + * For information on the complete list of contributors to MRCPP, see: + * + */ + #include "CompFunction.h" #include "Bank.h" #include "Printer.h" @@ -13,7 +38,7 @@ * NComp is the number of components. If Ncomp>0, the corresponding trees must exist (can be only empty roots). * The other trees should be set to nullptr. * The trees and data can be shared among several CompFunction; this is managed automatically by "std::make_shared" - * Normally the CompFunction must be eiher real or complex (or none if noe is defined anyway). + * Normally the CompFunction must be eiher real or complex (or none if none is defined anyway). * Though it is allowed in some cases to have both and the code should preferably allow this. (It is used temporary * when we need a Complex type, but the trees are real: the tree is then copied as a complex tree in the same CompFunction). * TreePtr (aka func_ptr) is the part potentially shared with others with "std::make_shared". It contains the pointers to the trees. @@ -122,7 +147,10 @@ template * Returns a copy without defined trees. */ CompFunction CompFunction::paramCopy(bool alloc) const { - return CompFunction(func_ptr->data, alloc); + CompFunction out(func_ptr->data, alloc); + // we do not copy tree sizes: + for (int i = 0; i < 4; i++) out.func_ptr->data.Nchunks[i] = 0; + return out; } template void CompFunction::flushMRAData() { @@ -184,6 +212,15 @@ template ComplexDouble CompFunction::integrate() const { return integral; } +template ComplexDouble CompFunction::integrateSide(int dim, bool positiveSide) const { + ComplexDouble integral; + if (isreal()) + integral = CompD[0]->integrateSide(dim, positiveSide); + else + integral = CompC[0]->integrateSide(dim, positiveSide); + return integral; +} + template double CompFunction::norm() const { double norm = getSquareNorm(); if (norm > 0.0) norm = std::sqrt(norm); @@ -333,7 +370,6 @@ template const FunctionTree &CompFunction::complex( /* for backwards compatibility */ template void CompFunction::setReal(FunctionTree *tree, int i) { func_ptr->isreal = 1; - // if (CompD[i] != nullptr) delete CompD[i]; CompD[i] = tree; if (tree != nullptr) { func_ptr->Ncomp = std::max(Ncomp(), i + 1); @@ -344,7 +380,6 @@ template void CompFunction::setReal(FunctionTree *tree, in template void CompFunction::setCplx(FunctionTree *tree, int i) { func_ptr->iscomplex = 1; - // if (CompC[i] != nullptr) delete CompC[i]; CompC[i] = tree; if (tree != nullptr) { func_ptr->Ncomp = std::max(Ncomp(), i + 1); @@ -359,36 +394,101 @@ template void CompFunction::setCplx(FunctionTree *t * */ template void CompFunction::add(ComplexDouble c, CompFunction inp) { - - if (Ncomp() < inp.Ncomp()) { + if (inp.getSquareNorm() < MachineZero) { + // nothing to add + } else if(this->getSquareNorm() < MachineZero) { + //copy func_ptr->data = inp.func_ptr->data; alloc(inp.Ncomp(), true); - } - - for (int i = 0; i < inp.Ncomp(); i++) { - if (inp.isreal() and c.imag() < MachineZero) { - CompD[i]->add_inplace(c.real(), *inp.CompD[i]); - } else { - if (this->isreal()) { - CompD[i]->CopyTreeToComplex(CompC[i]); + for (int i = 0; i < inp.Ncomp(); i++) { + if(inp.isreal()) CompD[i]->add_inplace(c.real(), *inp.CompD[i]); + if(inp.iscomplex()) CompC[i]->add_inplace(c, *inp.CompC[i]); + } + } else { + // we must check if the result is real or complex + if (inp.isreal() and this->isreal() and abs(c.imag()) < MachineZero) { + if ((std::norm(func_ptr->data.c1[0]-inp.func_ptr->data.c1[0]) < MachineZero) and + abs(c.imag()) < MachineZero){ + // real (possibly with complex c1) + for (int i = 0; i < inp.Ncomp(); i++) { + CompD[i]->add_inplace(c.real(), *inp.CompD[i]); + } + }else if (abs(func_ptr->data.c1[0].imag()) > MachineZero or abs(inp.func_ptr->data.c1[0].imag()) > MachineZero){ + MSG_ABORT("Not implemented"); + } else { + if(abs(1.0 - func_ptr->data.c1[0].real()) > MachineZero){ + rescale(func_ptr->data.c1[0].real()); + for (int i = 1; i < Ncomp(); i++) { + if (std::norm(func_ptr->data.c1[0]-func_ptr->data.c1[i]) > MachineZero) + MSG_ABORT("different scaling not implemented"); + func_ptr->data.c1[i] = {1.0, 0.0}; + } + func_ptr->data.c1[0] = {1.0, 0.0}; + } + for (int i = 0; i < inp.Ncomp(); i++) { + CompD[i]->add_inplace(c.real()*inp.func_ptr->data.c1[i].real(), *inp.CompD[i]); + } + } + } else if (inp.isreal() and this->isreal() and abs(c.imag()) > MachineZero) { + MSG_ABORT("Not implemented"); + } else if( this->iscomplex() and inp.iscomplex()) { + if (std::norm(1.0 - func_ptr->data.c1[0]) > MachineZero) { + rescale(func_ptr->data.c1[0]); + for (int i = 1; i < Ncomp(); i++) { + if (std::norm(func_ptr->data.c1[0]-func_ptr->data.c1[i]) > MachineZero) + MSG_ABORT("different scaling not implemented"); + func_ptr->data.c1[i] = {1.0, 0.0}; + } + func_ptr->data.c1[0] = {1.0, 0.0}; + } + for (int i = 0; i < inp.Ncomp(); i++) { + CompC[i]->add_inplace(c*inp.func_ptr->data.c1[i], *inp.CompC[i]); + } + } else if( this->isreal()) { + // we set as complex + for (int i = 0; i < Ncomp(); i++) { + CompC[i] = CompD[i]->CopyTreeToComplex(); delete CompD[i]; CompD[i] = nullptr; - func_ptr->iscomplex = true; - func_ptr->isreal = false; } - CompC[i]->add_inplace(c, *inp.CompC[i]); + func_ptr->iscomplex = 1; + func_ptr->isreal = 0; + if (std::norm(1.0-func_ptr->data.c1[0]) > MachineZero) { + rescale(func_ptr->data.c1[0]); + for (int i = 1; i < Ncomp(); i++) { + if (std::norm(func_ptr->data.c1[0]-func_ptr->data.c1[i]) > MachineZero) + MSG_ABORT("different scaling not implemented"); + func_ptr->data.c1[i] = {1.0, 0.0}; + } + func_ptr->data.c1[0] = {1.0, 0.0}; + } + for (int i = 0; i < inp.Ncomp(); i++) { + CompC[i]->add_inplace(c*inp.func_ptr->data.c1[i], *inp.CompC[i]); + } + } else { + MSG_ABORT("Not implemented"); } } } - -template int CompFunction::crop(double prec) { + +template void CompFunction::upgradeToComplex(){ + for (int i = 0; i < Ncomp(); i++) { + CompC[i] = CompD[i]->CopyTreeToComplex(); + delete CompD[i]; + CompD[i] = nullptr; + } + func_ptr->iscomplex = 1; + func_ptr->isreal = 0; +} + +template int CompFunction::crop(double prec, bool absPrec) { if (prec < 0.0) return 0; int nChunksremoved = 0; for (int i = 0; i < Ncomp(); i++) { if (isreal()) { - nChunksremoved += CompD[i]->crop(prec, 1.0, false); + nChunksremoved += CompD[i]->crop(prec, 1.0, absPrec); } else { - nChunksremoved += CompC[i]->crop(prec, 1.0, false); + nChunksremoved += CompC[i]->crop(prec, 1.0, absPrec); } } return nChunksremoved; @@ -397,13 +497,15 @@ template int CompFunction::crop(double prec) { /** @brief In place multiply with scalar. Fully in-place.*/ template void CompFunction::rescale(ComplexDouble c) { bool need_to_rescale = not(isShared()) or mpi::share_master(); + bool need_complex_functions = ((abs(c.imag()) > MachineZero) and isreal()); + if(need_complex_functions) upgradeToComplex(); if (need_to_rescale) { for (int i = 0; i < Ncomp(); i++) { if (iscomplex()) { CompC[i]->rescale(c); } else { if (abs(c.imag()) > MachineZero) { // works only only for NComp==1) - CompD[i]->CopyTreeToComplex(CompC[i]); + CompC[i] = CompD[i]->CopyTreeToComplex(); delete CompD[i]; CompD[i] = nullptr; func_ptr->iscomplex = true; @@ -425,6 +527,25 @@ template class CompFunction<1>; template class CompFunction<2>; template class CompFunction<3>; +/** @brief Deep copy that changes type from real to complex + * + * Deep copy: makes an exact copy with type complex from a real input + */ +template void CopyToComplex(CompFunction &out, const CompFunction &inp) { + out.func_ptr->data = inp.func_ptr->data; + out.defcomplex(); + out.func_ptr->data.isreal = 0; + out.alloc(inp.Ncomp()); + if (inp.getNNodes() == 0) return; + for (int i = 0; i < inp.Ncomp(); i++) { + if (inp.isreal()) { + out.CompC[i] = inp.CompD[i]->CopyTreeToComplex(); + } else { + inp.CompC[i]->deep_copy(out.CompC[i]); + } + } +} + /** @brief Deep copy * * Deep copy: meta data is copied along with the content of each component. @@ -488,7 +609,7 @@ template void linear_combination(CompFunction &out, const std::vector out.func_ptr->data.shared = share; // we don' inherit the shareness bool iscomplex = false; for (int i = 0; i < inp.size(); i++) - if (inp[i].iscomplex() or c[i].imag() > MachineZero) iscomplex = true; + if (inp[i].iscomplex() or abs(c[i].imag()) > MachineZero) iscomplex = true; if (iscomplex) { out.func_ptr->data.iscomplex = 1; out.func_ptr->data.isreal = 0; @@ -518,7 +639,7 @@ template void linear_combination(CompFunction &out, const std::vector FunctionTreeVector fvec; // one component vector for (int i = 0; i < inp.size(); i++) { if (inp[i].isreal()) { - inp[i].CompD[comp]->CopyTreeToComplex(inp[i].CompC[comp]); + inp[i].CompC[comp] = inp[i].CompD[comp]->CopyTreeToComplex(); delete inp[i].CompD[comp]; inp[i].CompD[comp] = nullptr; inp[i].func_ptr->iscomplex = true; @@ -545,6 +666,26 @@ template void linear_combination(CompFunction &out, const std::vector } } +/** @brief out = conj(inp) * inp + * + * Note that output is always real + * + */ +template void make_density(CompFunction &out, CompFunction inp, double prec) { + multiply(prec, out, 1.0, inp, inp, -1, false, false, true); + if (out.iscomplex()) { + // copy onto real components + for (int i = 0; i < out.Ncomp(); i++) { + out.CompD[i] = out.CompC[i]->Real(); + delete out.CompC[i]; + out.CompC[i] = nullptr; + } + out.func_ptr->isreal = 1; + out.func_ptr->iscomplex = 0; + } +} + + /** @brief out = inp_a * inp_b * */ @@ -589,13 +730,14 @@ template void multiply(double prec, CompFunction &out, double coef, C // if one of the input is real, we simply make a new complex copy of it bool inp_aisReal = inp_a.isreal(); bool inp_bisReal = inp_b.isreal(); + // Here, we keep the real trees of the inputs, as well as creating the complex ones, to avoid copying the trees back after multiplication. We restore the original state of the inputs after multiplication. if (inp_aisReal) { - inp_a.CompD[comp]->CopyTreeToComplex(inp_a.CompC[comp]); + inp_a.CompC[comp] = inp_a.CompD[comp]->CopyTreeToComplex(); inp_a.func_ptr->iscomplex = true; inp_a.func_ptr->isreal = false; } if (inp_bisReal) { - inp_b.CompD[comp]->CopyTreeToComplex(inp_b.CompC[comp]); + inp_b.CompC[comp] = inp_b.CompD[comp]->CopyTreeToComplex(); inp_b.func_ptr->iscomplex = true; inp_b.func_ptr->isreal = false; } @@ -607,6 +749,8 @@ template void multiply(double prec, CompFunction &out, double coef, C out.func_ptr->isreal = 0; delete out.CompD[comp]; delete out.CompC[comp]; + out.CompD[comp] = nullptr; + out.CompC[comp] = nullptr; if (!out_allocated) out.alloc(out.Ncomp()); build_grid(*out.CompC[comp], *inp_a.CompC[comp]); build_grid(*out.CompC[comp], *inp_b.CompC[comp]); @@ -615,7 +759,7 @@ template void multiply(double prec, CompFunction &out, double coef, C // Adaptive grid if (out.CompD[comp] != nullptr) { // NB: func_ptr has alreadybeen overwritten! if (out.CompD[comp]->getNNodes() > 0) { - out.CompD[comp]->CopyTreeToComplex(out.CompC[comp]); + out.CompC[comp] = out.CompD[comp]->CopyTreeToComplex(); out.func_ptr->iscomplex = 1; out.func_ptr->isreal = 0; delete out.CompD[comp]; @@ -633,7 +777,7 @@ template void multiply(double prec, CompFunction &out, double coef, C mrcpp::multiply(prec, *out.CompC[comp], coef, *inp_a.CompC[comp], *inp_b.CompC[comp], maxIter, absPrec, useMaxNorms, conjugate); } } - // restore original tree + // restore original tree by deleting the temporary complex tree. The real tree still exists, but is not used in the multiplication. if (inp_aisReal) { delete inp_a.CompC[comp]; inp_a.CompC[comp] = nullptr; @@ -707,7 +851,7 @@ template void multiply(CompFunction &out, FunctionTree = int bra^\dag(r) * ket(r) dr. * * Sum of component dots. - * Notice that the ComplexDouble dot(CompFunction bra, CompFunction ket) { @@ -725,14 +869,10 @@ template ComplexDouble dot(CompFunction bra, CompFunction ket) { } else { dotprod += mrcpp::dot(*bra.CompC[comp], *ket.CompC[comp]); } - dotprod *= bra.func_ptr->data.c1[comp] * ket.func_ptr->data.c1[comp]; + dotprod *= std::conj(bra.func_ptr->data.c1[comp]) * ket.func_ptr->data.c1[comp]; dotprodtot += dotprod; } - if (bra.isreal() and ket.isreal()) { - return dotprodtot.real(); - } else { - return dotprodtot; - } + return dotprodtot; } /** @brief Compute = int |bra^\dag(r)| * |ket(r)| dr. @@ -767,7 +907,15 @@ void project(CompFunction<3> &out, std::function &r)> f, d mpi::share_function(out, 0, 123123, mpi::comm_share); } -// template +void project_real(CompFunction<3> &out, std::function &r)> f, double prec) { + bool need_to_project = not(out.isShared()) or mpi::share_master(); + out.func_ptr->isreal = 1; + out.func_ptr->iscomplex = 0; + if (out.Ncomp() < 1) out.alloc(1); + if (need_to_project) mrcpp::project<3>(prec, *out.CompD[0], f); + mpi::share_function(out, 0, 123123, mpi::comm_share); +} + void project(CompFunction<3> &out, std::function &r)> f, double prec) { bool need_to_project = not(out.isShared()) or mpi::share_master(); out.func_ptr->isreal = 0; @@ -777,11 +925,21 @@ void project(CompFunction<3> &out, std::function &r mpi::share_function(out, 0, 123123, mpi::comm_share); } +void project_cplx(CompFunction<3> &out, std::function &r)> f, double prec) { + bool need_to_project = not(out.isShared()) or mpi::share_master(); + out.func_ptr->isreal = 0; + out.func_ptr->iscomplex = 1; + if (out.Ncomp() < 1) out.alloc(1); + if (need_to_project) mrcpp::project<3>(prec, *out.CompC[0], f); + mpi::share_function(out, 0, 123123, mpi::comm_share); +} + template void project(CompFunction &out, RepresentableFunction &f, double prec) { bool need_to_project = not(out.isShared()) or mpi::share_master(); out.func_ptr->isreal = 1; out.func_ptr->iscomplex = 0; if (out.Ncomp() < 1) out.alloc(1); + if (need_to_project) build_grid(*out.CompD[0], f); if (need_to_project) mrcpp::project(prec, *out.CompD[0], f); mpi::share_function(out, 0, 132231, mpi::comm_share); } @@ -790,6 +948,7 @@ template void project(CompFunction &out, RepresentableFunctionisreal = 0; out.func_ptr->iscomplex = 1; if (out.Ncomp() < 1) out.alloc(1); + if (need_to_project) build_grid(*out.CompC[0], f); if (need_to_project) mrcpp::project(prec, *out.CompC[0], f); mpi::share_function(out, 0, 132231, mpi::comm_share); } @@ -823,7 +982,10 @@ void rotate_cplx(CompFunctionVector &Phi, const ComplexMatrix &U, CompFunctionVe int N = Phi.size(); int M = Psi.size(); for (int i = 0; i < M; i++) { - for (int j; j < 4; j++) delete Psi[i].CompD[j]; + for (int j = 0; j < 4; j++) { + delete Psi[i].CompD[j]; + Psi[i].CompD[j] = nullptr; + } Psi[i].func_ptr->isreal = 0; Psi[i].func_ptr->iscomplex = 1; } @@ -915,6 +1077,7 @@ void rotate_cplx(CompFunctionVector &Phi, const ComplexMatrix &U, CompFunctionVe int node_ix = indexVec_ref[n]; // SerialIx for this node in the reference tree // 4a) make a dense contiguous matrix with the coefficient from all the orbitals using node n std::vector orbjVec; // to remember which orbital correspond to each orbVec.size(); + if (node2orbVec.count(node_ix) == 0) continue; if (node2orbVec[node_ix].size() <= 0) continue; csize = sizecoeffW; if (parindexVec_ref[n] < 0) csize = sizecoeff; // for root nodes we include scaling coeff @@ -922,6 +1085,7 @@ void rotate_cplx(CompFunctionVector &Phi, const ComplexMatrix &U, CompFunctionVe int shift = sizecoeff - sizecoeffW; // to copy only wavelet part if (parindexVec_ref[n] < 0) shift = 0; ComplexMatrix coeffBlock(csize, node2orbVec[node_ix].size()); + for (int j : node2orbVec[node_ix]) { // loop over indices of the orbitals using this node int orb_node_ix = orb2node[j][node_ix]; for (int k = 0; k < csize; k++) coeffBlock(k, orbjVec.size()) = coeffVec[j][orb_node_ix][k + shift]; @@ -1203,6 +1367,7 @@ void rotate(CompFunctionVector &Phi, const ComplexMatrix &U, CompFunctionVector int node_ix = indexVec_ref[n]; // SerialIx for this node in the reference tree // 4a) make a dense contiguous matrix with the coefficient from all the orbitals using node n std::vector orbjVec; // to remember which orbital correspond to each orbVec.size(); + if (node2orbVec.count(node_ix) == 0) continue; if (node2orbVec[node_ix].size() <= 0) continue; csize = sizecoeffW; if (parindexVec_ref[n] < 0) csize = sizecoeff; // for root nodes we include scaling coeff @@ -1398,7 +1563,6 @@ void rotate(CompFunctionVector &Phi, const ComplexMatrix &U, double prec) { void save_nodes(CompFunctionVector &Phi, FunctionTree<3> &refTree, BankAccount &account, int sizes) { int sizecoeff = (1 << refTree.getDim()) * refTree.getKp1_d(); int sizecoeffW = ((1 << refTree.getDim()) - 1) * refTree.getKp1_d(); - int max_nNodes = refTree.getNNodes(); std::vector coeffVec; std::vector coeffVec_cplx; std::vector scalefac; @@ -1475,7 +1639,6 @@ CompFunctionVector multiply(CompFunctionVector &Phi, RepresentableFunction<3> &f // refine_grid(refTree, f); //to test mpi::allreduce_Tree_noCoeff(refTree, Phi, mpi::comm_wrk); - int kp1 = refTree.getKp1(); int kp1_d = refTree.getKp1_d(); int nCoefs = refTree.getTDim() * kp1_d; @@ -1564,11 +1727,12 @@ CompFunctionVector multiply(CompFunctionVector &Phi, RepresentableFunction<3> &f for (int n = 0; n < max_n; n++) { MWNode node(*(refNodes[n]), false); int node_ix = indexVec_ref[n]; // SerialIx for this node in the reference tree + if (node2orbVec.count(node_ix) == 0) continue; // 3a) make values for f at this node // 3a1) get coordinates of quadrature points for this node Eigen::MatrixXd pts; // Eigen::Zero(D, nCoefs); - double fval[nCoefs]; + std::vector fval(nCoefs); Coord r; double *originalCoef = nullptr; MWNode<3> *Fnode = nullptr; @@ -1589,7 +1753,7 @@ CompFunctionVector multiply(CompFunctionVector &Phi, RepresentableFunction<3> &f } else { originalCoef = Fnode->getCoefs(); for (int j = 0; j < nCoefs; j++) fval[j] = originalCoef[j]; - Fnode->attachCoefs(fval); // note that each thread has its own copy + Fnode->attachCoefs(fval.data()); // note that each thread has its own copy Fnode->mwTransform(Reconstruction); Fnode->cvTransform(Forward); } @@ -1635,8 +1799,6 @@ CompFunctionVector multiply(CompFunctionVector &Phi, RepresentableFunction<3> &f } } else { // MPI - int count1 = 0; - int count2 = 0; TaskManager tasks(max_n); for (int nn = 0; nn < max_n; nn++) { int n = tasks.next_task(); @@ -1646,7 +1808,7 @@ CompFunctionVector multiply(CompFunctionVector &Phi, RepresentableFunction<3> &f // 3a1) get coordinates of quadrature points for this node Eigen::MatrixXd pts; // Eigen::Zero(D, nCoefs); node.getExpandedChildPts(pts); // TODO: use getPrimitiveChildPts (less cache). - double fval[nCoefs]; + std::vector fval(nCoefs); Coord r; MWNode Fnode(*(refNodes[n]), false); if (Func == nullptr) { @@ -1656,17 +1818,15 @@ CompFunctionVector multiply(CompFunctionVector &Phi, RepresentableFunction<3> &f } } else { int nIdx = Func->real().getIx(node.getNodeIndex()); - count1++; if (nIdx < 0) { // use the function f instead of Func - count2++; for (int j = 0; j < nCoefs; j++) { for (int d = 0; d < D; d++) r[d] = pts(d, j); fval[j] = f.evalf(r); } } else { - Func->real().getNodeCoeff(nIdx, fval); // fetch coef from Bank - Fnode.attachCoefs(fval); + Func->real().getNodeCoeff(nIdx, fval.data()); // fetch coef from Bank + Fnode.attachCoefs(fval.data()); Fnode.mwTransform(Reconstruction); Fnode.cvTransform(Forward); } @@ -1874,7 +2034,6 @@ ComplexMatrix calc_overlap_matrix_cplx(CompFunctionVector &BraKet) { } // 3) make dot product for all the nodes and accumulate into S - int ibank = 0; #pragma omp parallel if (serial) { ComplexMatrix S_omp = ComplexMatrix::Zero(N, N); // copy for each thread @@ -1885,6 +2044,7 @@ ComplexMatrix calc_overlap_matrix_cplx(CompFunctionVector &BraKet) { int csize; int node_ix = indexVec_ref[n]; // SerialIx for this node in the reference tree std::vector orbVec; // identifies which orbitals use this node + if (serial and node2orbVec.count(node_ix) == 0) continue; if (serial and node2orbVec[node_ix].size() <= 0) continue; if (parindexVec_ref[n] < 0) csize = sizecoeff; @@ -1926,7 +2086,7 @@ ComplexMatrix calc_overlap_matrix_cplx(CompFunctionVector &BraKet) { if (BraKet[orbVec[i]].func_ptr->data.n1[0] != BraKet[orbVec[j]].func_ptr->data.n1[0] and BraKet[orbVec[i]].func_ptr->data.n1[0] != 0 and BraKet[orbVec[j]].func_ptr->data.n1[0] != 0) continue; - S_omp(orbVec[i], orbVec[j]) += S_temp(i, j); + S(orbVec[i], orbVec[j]) += S_temp(i, j); } } } @@ -2022,7 +2182,6 @@ ComplexMatrix calc_overlap_matrix(CompFunctionVector &BraKet) { } // 3) make dot product for all the nodes and accumulate into S - int ibank = 0; #pragma omp parallel if (serial) { ComplexMatrix S_omp = ComplexMatrix::Zero(N, N); // copy for each thread @@ -2033,6 +2192,7 @@ ComplexMatrix calc_overlap_matrix(CompFunctionVector &BraKet) { int csize; int node_ix = indexVec_ref[n]; // SerialIx for this node in the reference tree std::vector orbVec; // identifies which orbitals use this node + if (serial and node2orbVec.count(node_ix) == 0) continue; if (serial and node2orbVec[node_ix].size() <= 0) continue; if (parindexVec_ref[n] < 0) csize = sizecoeff; @@ -2121,16 +2281,20 @@ ComplexMatrix calc_overlap_matrix_cplx(CompFunctionVector &Bra, CompFunctionVect bool ketisreal = !Ket[0].iscomplex(); if (braisreal or ketisreal) { // temporary solution: copy as complex trees + // Here, we keep the real trees of the inputs, as well as creating the complex ones, to avoid copying the trees back after computing the overlap matrix. + // We restore the original state of the inputs after multiplication. if (braisreal) { for (int i = 0; i < Bra.size(); i++) { - Bra[i].CompD[0]->CopyTreeToComplex(Bra[i].CompC[0]); + Bra[i].CompC[0] = Bra[i].CompD[0]->CopyTreeToComplex(); Bra[i].func_ptr->iscomplex = 1; + Bra[i].func_ptr->isreal = 0; } } if (ketisreal) { for (int i = 0; i < Ket.size(); i++) { - Ket[i].CompD[0]->CopyTreeToComplex(Ket[i].CompC[0]); + Ket[i].CompC[0] = Ket[i].CompD[0]->CopyTreeToComplex(); Ket[i].func_ptr->iscomplex = 1; + Ket[i].func_ptr->isreal = 0; } } } @@ -2227,7 +2391,6 @@ ComplexMatrix calc_overlap_matrix_cplx(CompFunctionVector &Bra, CompFunctionVect int totsiz = 0; int totget = 0; int mxtotsiz = 0; - int ibank = 0; // the omp crashes sometime for unknown reasons? #pragma omp parallel if (serial) { @@ -2245,6 +2408,8 @@ ComplexMatrix calc_overlap_matrix_cplx(CompFunctionVector &Bra, CompFunctionVect csize = sizecoeffW; if (serial) { int node_ix = indexVec_ref[n]; // SerialIx for this node in the reference tree + if (node2orbVecBra.count(node_ix) == 0) continue; + if (node2orbVecKet.count(node_ix) == 0) continue; int shift = sizecoeff - sizecoeffW; // to copy only wavelet part ComplexMatrix coeffBlockBra(csize, node2orbVecBra[node_ix].size()); ComplexMatrix coeffBlockKet(csize, node2orbVecKet[node_ix].size()); @@ -2346,7 +2511,7 @@ ComplexMatrix calc_overlap_matrix_cplx(CompFunctionVector &Bra, CompFunctionVect for (int j = 0; j < M; j++) { S(i, j) *= std::conj(FacBra[i]) * FacKet[j]; } } - // restore input + // restore original tree by deleting the temporary complex tree. The real tree still exists, but is not used in the computation. if (braisreal) { for (int i = 0; i < Bra.size(); i++) { delete Bra[i].CompC[0]; @@ -2454,7 +2619,6 @@ ComplexMatrix calc_overlap_matrix(CompFunctionVector &Bra, CompFunctionVector &K int totsiz = 0; int totget = 0; int mxtotsiz = 0; - int ibank = 0; #pragma omp parallel if (serial) { DoubleMatrix S_omp = DoubleMatrix::Zero(N, M); // copy for each thread @@ -2471,6 +2635,8 @@ ComplexMatrix calc_overlap_matrix(CompFunctionVector &Bra, CompFunctionVector &K csize = sizecoeffW; if (serial) { int node_ix = indexVec_ref[n]; // SerialIx for this node in the reference tree + if (node2orbVecBra.count(node_ix) == 0) continue; + if (node2orbVecKet.count(node_ix) == 0) continue; int shift = sizecoeff - sizecoeffW; // to copy only wavelet part DoubleMatrix coeffBlockBra(csize, node2orbVecBra[node_ix].size()); DoubleMatrix coeffBlockKet(csize, node2orbVecKet[node_ix].size()); @@ -2603,15 +2769,18 @@ template ComplexDouble dot(CompFunction<3> bra, CompFunction<3> ket); template void project(CompFunction<3> &out, RepresentableFunction<3, double> &f, double prec); template void project(CompFunction<3> &out, RepresentableFunction<3, ComplexDouble> &f, double prec); template void multiply(CompFunction<3> &out, CompFunction<3> inp_a, CompFunction<3> inp_b, double prec, bool absPrec, bool useMaxNorms, bool conjugate); +template void multiply(double prec, CompFunction<3> &out, double coef, CompFunction<3> inp_a, CompFunction<3> inp_b, int maxIter = -1, bool absPrec = false, bool useMaxNorms = false, bool conjugate = false); template void multiply(CompFunction<3> &out, FunctionTree<3, double> &inp_a, RepresentableFunction<3, double> &f, double prec, int nrefine = 0, bool conjugate); template void multiply(CompFunction<3> &out, FunctionTree<3, ComplexDouble> &inp_a, RepresentableFunction<3, ComplexDouble> &f, double prec, int nrefine = 0, bool conjugate); template void multiply(CompFunction<3> &out, CompFunction<3> &inp_a, RepresentableFunction<3, double> &f, double prec, int nrefine = 0, bool conjugate); template void multiply(CompFunction<3> &out, CompFunction<3> &inp_a, RepresentableFunction<3, ComplexDouble> &f, double prec, int nrefine = 0, bool conjugate); +template void CopyToComplex(CompFunction<3> &out, const CompFunction<3> &inp); template void deep_copy(CompFunction<3> *out, const CompFunction<3> &inp); template void deep_copy(CompFunction<3> &out, const CompFunction<3> &inp); template void add(CompFunction<3> &out, ComplexDouble a, CompFunction<3> inp_a, ComplexDouble b, CompFunction<3> inp_b, double prec, bool conjugate); template void linear_combination(CompFunction<3> &out, const std::vector &c, std::vector> &inp, double prec, bool conjugate); template double node_norm_dot(CompFunction<3> bra, CompFunction<3> ket); template void orthogonalize(double prec, CompFunction<3> &Bra, CompFunction<3> &Ket); +template void make_density(CompFunction<3> &out, CompFunction<3> inp, double prec); } // namespace mrcpp diff --git a/src/utils/CompFunction.h b/src/utils/CompFunction.h index 82209819f..7f5cf463d 100644 --- a/src/utils/CompFunction.h +++ b/src/utils/CompFunction.h @@ -1,3 +1,28 @@ +/* + * MRCPP, a numerical library based on multiresolution analysis and + * the multiwavelet basis which provide low-scaling algorithms as well as + * rigorous error control in numerical computations. + * Copyright (C) 2021 Stig Rune Jensen, Jonas Juselius, Luca Frediani and contributors. + * + * This file is part of MRCPP. + * + * MRCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MRCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with MRCPP. If not, see . + * + * For information on the complete list of contributors to MRCPP, see: + * + */ + #pragma once #include "mpi_utils.h" @@ -115,13 +140,25 @@ template class CompFunction { int conj() const { return func_ptr->data.conj; } // soft conjugate int isreal() const { return func_ptr->data.isreal; } // T=double int iscomplex() const { return func_ptr->data.iscomplex; } // T=DoubleComplex - void defreal() { func_ptr->data.isreal = 1; } // define as real - void defcomplex() { func_ptr->data.iscomplex = 1; } // define as complex + void defreal() { func_ptr->data.isreal = 1; // define as real + func_ptr->data.iscomplex = 0;} + void defcomplex() { func_ptr->data.isreal = 0; // define as complex + func_ptr->data.iscomplex = 1;} int share() const { return func_ptr->data.shared; } int *Nchunks() const { return func_ptr->data.Nchunks; } // number of chunks of each component tree + ComplexDouble getFac() const { return func_ptr->data.c1[0]; } // returns the overall multiplicative factor + void setFac(ComplexDouble fac) { func_ptr->data.c1[0] = fac; } // sets the overall multiplicative factor CompFunction paramCopy(bool alloc = false) const; ComplexDouble integrate() const; + + /** + * @brief Integrate over half of the space + * @param dim Dimension along which to split + * @param positiveSide If true, integrate over the positive side (x>0 if dim=0) + * @return Resulting integral + */ + ComplexDouble integrateSide(int dim, bool positiveSide) const; //LUCA: This should rather become a utility function instead of being a member function, as it is not required for the "exsistence" of the CompFunction, but only a specific operation on it. double norm() const; double getSquareNorm() const; void alloc(int nalloc = 1, bool zero = true); @@ -132,7 +169,7 @@ template class CompFunction { const int getRank() const { return func_ptr->rank; }; void add(ComplexDouble c, CompFunction inp); - int crop(double prec); + int crop(double prec, bool absPrec = true); void rescale(ComplexDouble c); void free(); int getSizeNodes() const; @@ -156,29 +193,49 @@ template class CompFunction { FunctionTree &imag(int i = 0); // does not make sense now const FunctionTree &imag(int i = 0) const; // does not make sense now std::shared_ptr> func_ptr; + void upgradeToComplex(); }; +template void CopyToComplex(CompFunction &out, const CompFunction &inp); template void deep_copy(CompFunction *out, const CompFunction &inp); template void deep_copy(CompFunction &out, const CompFunction &inp); -template void add(CompFunction &out, ComplexDouble a, CompFunction inp_a, ComplexDouble b, CompFunction inp_b, double prec, bool conjugate = false); -template void linear_combination(CompFunction &out, const std::vector &c, std::vector> &inp, double prec, bool conjugate = false); -template void multiply(CompFunction &out, CompFunction inp_a, CompFunction inp_b, double prec, bool absPrec = false, bool useMaxNorms = false, bool conjugate = false); -template -void multiply(double prec, CompFunction &out, double coef, CompFunction inp_a, CompFunction inp_b, int maxIter = -1, bool absPrec = false, bool useMaxNorms = false, bool conjugate = false); -template void multiply(CompFunction &out, CompFunction inp_a, CompFunction inp_b, bool absPrec = false, bool useMaxNorms = false, bool conjugate = false); -template void multiply(CompFunction &out, CompFunction &inp_a, RepresentableFunction &f, double prec, int nrefine = 0, bool conjugate = false); -template void multiply(CompFunction &out, CompFunction &inp_a, RepresentableFunction &f, double prec, int nrefine = 0, bool conjugate = false); -template void multiply(CompFunction &out, FunctionTree &inp_a, RepresentableFunction &f, double prec, int nrefine = 0, bool conjugate = false); -template void multiply(CompFunction &out, FunctionTree &inp_a, RepresentableFunction &f, double prec, int nrefine = 0, bool conjugate = false); +template void add(CompFunction &out, ComplexDouble a, + CompFunction inp_a, ComplexDouble b, + CompFunction inp_b, double prec, bool conjugate = false); +template void linear_combination(CompFunction &out, const std::vector &c, + std::vector> &inp, double prec, bool conjugate = false); +template void multiply(CompFunction &out, CompFunction inp_a, CompFunction inp_b, + double prec, bool absPrec = false, bool useMaxNorms = false, bool conjugate = false); +template void multiply(double prec, CompFunction &out, double coef, CompFunction inp_a, + CompFunction inp_b, int maxIter = -1, bool absPrec = false, bool useMaxNorms = false, + bool conjugate = false); +template void multiply(CompFunction &out, CompFunction inp_a, + CompFunction inp_b, bool absPrec = false, + bool useMaxNorms = false, bool conjugate = false); +template void multiply(CompFunction &out, CompFunction &inp_a, + RepresentableFunction &f, double prec, + int nrefine = 0, bool conjugate = false); +template void multiply(CompFunction &out, CompFunction &inp_a, + RepresentableFunction &f, + double prec, int nrefine = 0, bool conjugate = false); +template void multiply(CompFunction &out, FunctionTree &inp_a, + RepresentableFunction &f, double prec, int nrefine = 0, + bool conjugate = false); +template void multiply(CompFunction &out, FunctionTree &inp_a, + RepresentableFunction &f, double prec, int nrefine = 0, + bool conjugate = false); +template void make_density(CompFunction &out, CompFunction inp, double prec); template ComplexDouble dot(CompFunction bra, CompFunction ket); template double node_norm_dot(CompFunction bra, CompFunction ket); -void project(CompFunction<3> &out, std::function &r)> f, double prec); +void project(CompFunction<3> &out, std::function &r)> f, double prec); //LUCA Why is this only defined for D=3? It should be defined for any D. +void project_real(CompFunction<3> &out, std::function &r)> f, double prec); //overload of project is not always recognized by the compiler void project(CompFunction<3> &out, std::function &r)> f, double prec); +void project_cplx(CompFunction<3> &out, std::function &r)> f, double prec); //overload of project is not always recognized by the compiler template void project(CompFunction &out, RepresentableFunction &f, double prec); template void project(CompFunction &out, RepresentableFunction &f, double prec); template void orthogonalize(double prec, CompFunction &Bra, CompFunction &Ket); -class CompFunctionVector : public std::vector> { +class CompFunctionVector : public std::vector> {//LUCA: Why is this only defined for D=3? It should be defined for any D. public: CompFunctionVector(int N = 0); MultiResolutionAnalysis<3> *vecMRA; diff --git a/src/utils/Printer.cpp b/src/utils/Printer.cpp index 24585feb3..bc6b9b2da 100644 --- a/src/utils/Printer.cpp +++ b/src/utils/Printer.cpp @@ -210,6 +210,36 @@ void print::value(int level, const std::string &txt, double v, const std::string println(level, o.str()); } +/** @brief Print a scalar value, including unit + * + * @param[in] level: Activation level for print statement + * @param[in] v: Complex number to print + * @param[in] unit: Unit of scalar + * @param[in] p: Floating point precision + * @param[in] sci: Use scientific notation + */ +void print::complex_value(int level, const std::string &txt, std::complex v, const std::string &unit, int p, bool sci) { + if (level > Printer::getPrintLevel()) return; + + if (p < 0) p = Printer::getPrecision(); + auto line_width = Printer::getWidth() - 2; + auto txt_width = line_width / 2; + auto unit_width = txt_width / 3; + auto val_width = line_width - (txt_width + unit_width); + auto n_spaces = std::max(0, txt_width - static_cast(txt.size())); + + std::stringstream o; + o << " " << txt; + o << std::string(n_spaces, ' '); + o << std::setw(unit_width) << unit; + if (sci) { + o << std::setw(val_width) << std::setprecision(p) << std::scientific << v; + } else { + o << std::setw(val_width) << std::setprecision(p) << std::fixed << v; + } + println(level, o.str()); +} + /** @brief Print tree parameters (nodes, memory) and wall time * * @param[in] level: Activation level for print statement diff --git a/src/utils/Printer.h b/src/utils/Printer.h index c021155e8..b916414c9 100644 --- a/src/utils/Printer.h +++ b/src/utils/Printer.h @@ -35,6 +35,7 @@ #include #include #include +#include namespace mrcpp { @@ -126,6 +127,7 @@ void header(int level, const std::string &txt, int newlines = 0, const char &c = void footer(int level, const Timer &timer, int newlines = 0, const char &c = '='); void memory(int level, const std::string &txt); void value(int level, const std::string &txt, double v, const std::string &unit = "", int p = -1, bool sci = true); +void complex_value(int level, const std::string &txt, std::complex v, const std::string &unit = "", int p = -1, bool sci = true); void time(int level, const std::string &txt, const Timer &timer); void tree(int level, const std::string &txt, int n, int m, double t); template void tree(int level, const std::string &txt, const MWTree &tree, const Timer &timer); diff --git a/src/utils/tree_utils.cpp b/src/utils/tree_utils.cpp index 333544f6e..b01c03347 100644 --- a/src/utils/tree_utils.cpp +++ b/src/utils/tree_utils.cpp @@ -118,8 +118,8 @@ template void tree_utils::mw_transform(const MWTree &t int kp1_dm1 = math_utils::ipow(kp1, D - 1); const MWFilter &filter = tree.getMRA().getFilter(); double overwrite = 0.0; - T tmpcoeff[kp1_d * tDim]; - T tmpcoeff2[kp1_d * tDim]; + std::vector tmpcoeff(kp1_d * tDim); + std::vector tmpcoeff2(kp1_d * tDim); int ftlim = tDim; int ftlim2 = tDim; int ftlim3 = tDim; @@ -135,7 +135,7 @@ template void tree_utils::mw_transform(const MWTree &t int i = 0; int mask = 1; for (int gt = 0; gt < tDim; gt++) { - T *out = tmpcoeff + gt * kp1_d; + T *out = tmpcoeff.data() + gt * kp1_d; for (int ft = 0; ft < ftlim; ft++) { // Operate in direction i only if the bits along other // directions are identical. The bit of the direction we @@ -155,13 +155,13 @@ template void tree_utils::mw_transform(const MWTree &t i++; mask = 2; // 1 << i; for (int gt = 0; gt < tDim; gt++) { - T *out = tmpcoeff2 + gt * kp1_d; + T *out = tmpcoeff2.data() + gt * kp1_d; for (int ft = 0; ft < ftlim2; ft++) { // Operate in direction i only if the bits along other // directions are identical. The bit of the direction we // operate on determines the appropriate filter/operator if ((gt | mask) == (ft | mask)) { - T *in = tmpcoeff + ft * kp1_d; + T *in = tmpcoeff.data() + ft * kp1_d; int filter_index = 2 * ((gt >> i) & 1) + ((ft >> i) & 1); const Eigen::MatrixXd &oper = filter.getSubFilter(filter_index, operation); @@ -184,7 +184,7 @@ template void tree_utils::mw_transform(const MWTree &t // directions are identical. The bit of the direction we // operate on determines the appropriate filter/operator if ((gt | mask) == (ft | mask)) { - T *in = tmpcoeff2 + ft * kp1_d; + T *in = tmpcoeff2.data() + ft * kp1_d; int filter_index = 2 * ((gt >> i) & 1) + ((ft >> i) & 1); const Eigen::MatrixXd &oper = filter.getSubFilter(filter_index, operation); @@ -201,8 +201,8 @@ template void tree_utils::mw_transform(const MWTree &t if (D < 3) { T *out; - if (D == 1) out = tmpcoeff; - if (D == 2) out = tmpcoeff2; + if (D == 1) out = tmpcoeff.data(); + if (D == 2) out = tmpcoeff2.data(); if (b_overwrite) { for (int j = 0; j < tDim; j++) { for (int i = 0; i < kp1_d; i++) { coeff_out[i + j * stride] = out[i + j * kp1_d]; } @@ -234,7 +234,7 @@ template void tree_utils::mw_transform_back(MWTree<3, T> &tree, T * int kp1_dm1 = math_utils::ipow(kp1, 2); const MWFilter &filter = tree.getMRA().getFilter(); double overwrite = 0.0; - T tmpcoeff[kp1_d * tDim]; + std::vector tmpcoeff(kp1_d * tDim); int ftlim = tDim; int ftlim2 = tDim; @@ -262,7 +262,7 @@ template void tree_utils::mw_transform_back(MWTree<3, T> &tree, T * i++; mask = 2; // 1 << i; for (int gt = 0; gt < tDim; gt++) { - T *out = tmpcoeff + gt * kp1_d; + T *out = tmpcoeff.data() + gt * kp1_d; for (int ft = 0; ft < ftlim2; ft++) { // Operate in direction i only if the bits along other // directions are identical. The bit of the direction we @@ -288,7 +288,7 @@ template void tree_utils::mw_transform_back(MWTree<3, T> &tree, T * // directions are identical. The bit of the direction we // operate on determines the appropriate filter/operator if ((gt | mask) == (ft | mask)) { - T *in = tmpcoeff + ft * kp1_d; + T *in = tmpcoeff.data() + ft * kp1_d; int filter_index = 2 * ((gt >> i) & 1) + ((ft >> i) & 1); const Eigen::MatrixXd &oper = filter.getSubFilter(filter_index, operation); diff --git a/tests/factory_functions.h b/tests/factory_functions.h index d1c14a98f..571123263 100644 --- a/tests/factory_functions.h +++ b/tests/factory_functions.h @@ -129,7 +129,6 @@ void initialize(mrcpp::GaussFunc **func) { double beta = 1.0e4; double alpha = std::pow(beta / mrcpp::pi, D / 2.0); double pos_data[3] = {-0.2, 0.5, 1.0}; - const auto pow = std::array{}; auto pos = mrcpp::details::convert_to_std_array(pos_data); diff --git a/tests/functions/CMakeLists.txt b/tests/functions/CMakeLists.txt index e322a7fd2..d7357907e 100644 --- a/tests/functions/CMakeLists.txt +++ b/tests/functions/CMakeLists.txt @@ -5,9 +5,9 @@ target_sources(mrcpp-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/periodify_gaussians.cpp ) -add_Catch_test(NAME legendre_poly LABELS legendre_poly) -add_Catch_test(NAME polynomials LABELS polynomials) -add_Catch_test(NAME gaussians LABELS gaussians) -add_Catch_test(NAME periodic_narrow_gaussian LABELS periodic_narrow_gaussian) -add_Catch_test(NAME periodic_wide_gaussian LABELS periodic_wide_gaussian) -add_Catch_test(NAME periodic_gaussExp LABELS periodic_gausExp) +add_Catch_test(NAME legendre_poly LABELS legendre_poly) +add_Catch_test(NAME polynomials LABELS polynomials) +add_Catch_test(NAME gaussians LABELS gaussians) +add_Catch_test(NAME periodic_narrow_gaussian LABELS periodic_narrow_gaussian) +add_Catch_test(NAME periodic_wide_gaussian LABELS periodic_wide_gaussian) +add_Catch_test(NAME periodic_gaussExp LABELS periodic_gausExp) diff --git a/tests/functions/polynomial.cpp b/tests/functions/polynomial.cpp index 48b09cc51..e643d1639 100644 --- a/tests/functions/polynomial.cpp +++ b/tests/functions/polynomial.cpp @@ -223,7 +223,6 @@ SCENARIO("Polynomials can be added and multiplied", "[poly_arithmetics], [polyno Polynomial R; R = P * Q; THEN("The coefficients of R are known") { - VectorXd &cr = R.getCoefs(); REQUIRE(R.getCoefs()[0] == Catch::Approx(0.0)); REQUIRE(R.getCoefs()[1] == Catch::Approx(0.0)); REQUIRE(R.getCoefs()[2] == Catch::Approx(1.0)); diff --git a/tests/operators/helmholtz_operator.cpp b/tests/operators/helmholtz_operator.cpp index 8f570691d..ce669e27c 100644 --- a/tests/operators/helmholtz_operator.cpp +++ b/tests/operators/helmholtz_operator.cpp @@ -52,7 +52,6 @@ TEST_CASE("Helmholtz' kernel", "[init_helmholtz], [helmholtz_operator], [mw_oper const double exp_prec = 1.0e-4; const double proj_prec = 1.0e-3; const double ccc_prec = 1.0e-3; - const double band_prec = 1.0e-3; const int n = -3; const int k = 5; diff --git a/tests/operators/poisson_operator.cpp b/tests/operators/poisson_operator.cpp index 23bb22a06..e6ad04f69 100644 --- a/tests/operators/poisson_operator.cpp +++ b/tests/operators/poisson_operator.cpp @@ -50,7 +50,6 @@ TEST_CASE("Initialize Poisson operator", "[init_poisson], [poisson_operator], [m const double exp_prec = 1.0e-4; const double proj_prec = 1.0e-3; const double ccc_prec = 1.0e-3; - const double band_prec = 1.0e-3; const int n = -3; const int k = 5; @@ -161,7 +160,6 @@ TEST_CASE("Apply Periodic Poisson' operator", "[apply_periodic_Poisson], [poisso // 2.0*pi periodic in all dirs // UPDATE ME auto scaling_factor = std::array{pi, pi, pi}; - auto periodic = true; auto corner = std::array{-1, -1, -1}; auto boxes = std::array{2, 2, 2}; diff --git a/tests/treebuilders/map.cpp b/tests/treebuilders/map.cpp index 6be2153f0..e05653fc4 100644 --- a/tests/treebuilders/map.cpp +++ b/tests/treebuilders/map.cpp @@ -30,7 +30,7 @@ #include "functions/GaussPoly.h" #include "treebuilders/WaveletAdaptor.h" #include "treebuilders/grid.h" -#include "treebuilders/map.h" +#include "treebuilders/treeMap.h" #include "treebuilders/project.h" using namespace mrcpp; @@ -80,7 +80,6 @@ template void testMapping() { const double ref_int = ref_tree.integrate(); const double ref_norm = ref_tree.getSquareNorm(); - const double inp_int = inp_tree.integrate(); const double inp_norm = inp_tree.getSquareNorm(); FMap fmap = [](double val) { return val * val; }; @@ -88,7 +87,7 @@ template void testMapping() { WHEN("the function is mapped") { FunctionTree out_tree(*mra); - map(prec, out_tree, inp_tree, fmap); + treeMap(prec, out_tree, inp_tree, fmap); THEN("the MW map equals the analytic map") { double out_int = out_tree.integrate(); @@ -111,3 +110,87 @@ template void testMapping() { } } // namespace mapping + +namespace complex_mapping { + +template void testComplexMapping(); + +SCENARIO("Map a complex MW tree", "[map], [tree_builder], [complex]") { + GIVEN("One complex MW functions in 1D") { + testComplexMapping<1>(); + } + GIVEN("One complex MW functions in 2D") { + testComplexMapping<2>(); + } + GIVEN("One complex MW functions in 3D") { + testComplexMapping<3>(); + } +} + +template void testComplexMapping() { + const double prec = 1.0e-4; + const ComplexDouble c_in(1.0, 1.0); // (1 + i) + const ComplexDouble c_ref = c_in * c_in; // (1 + i)^2 = 2i + + double alpha = 1.0; + double beta = 50.0; + double beta_ref = 100.0; + + double pos_c[3] = {-0.25, 0.35, 1.05}; + auto pos = details::convert_to_std_array(pos_c); + + // Real-valued Gaussian building blocks + GaussFunc inp_func(beta, alpha, pos); + GaussFunc ref_func(beta_ref, alpha, pos); + MultiResolutionAnalysis *mra = nullptr; + initialize(&mra); + + // Initialize complex trees + FunctionTree inp_tree(*mra); + FunctionTree ref_tree(*mra); + + // Complex analytic functions: + // inp(r) = (1 + i) * gauss(beta=50) + // ref(r) = (1 + i)^2 * gauss(beta=100) = 2i * gauss(beta=100) + // Mapping inp through z -> z*z gives inp^2 = ref exactly, since + // ((1+i) * gauss(50))^2 = (1+i)^2 * gauss(100) = ref. + std::function &)> inp_cplx = [&](const Coord &r) -> ComplexDouble { + return c_in * inp_func.evalf(r); + }; + std::function &)> ref_cplx = [&](const Coord &r) -> ComplexDouble { + return c_ref * ref_func.evalf(r); + }; + + // Project functions + project(prec, inp_tree, inp_cplx); + project(prec, ref_tree, ref_cplx); + const ComplexDouble ref_int = ref_tree.integrate(); + const double ref_norm = ref_tree.getSquareNorm(); + + FMap fmap = [](ComplexDouble val) { return val * val; }; + + WHEN("the complex function is mapped") { + FunctionTree out_tree(*mra); + treeMap(prec, out_tree, inp_tree, fmap); + THEN("the MW map equals the analytic map") { + ComplexDouble out_int = out_tree.integrate(); + double out_norm = out_tree.getSquareNorm(); + REQUIRE(out_int.real() == Catch::Approx(ref_int.real()).margin(1.0e-9)); + REQUIRE(out_int.imag() == Catch::Approx(ref_int.imag())); + REQUIRE(out_norm == Catch::Approx(ref_norm)); + } + } + WHEN("the complex functions is mapped in-place") { + inp_tree.map(fmap); + THEN("the function equals the analytic product") { + ComplexDouble inp_int = inp_tree.integrate(); + double inp_norm = inp_tree.getSquareNorm(); + REQUIRE(inp_int.real() == Catch::Approx(ref_int.real()).margin(1.0e-9)); + REQUIRE(inp_int.imag() == Catch::Approx(ref_int.imag())); + REQUIRE(inp_norm == Catch::Approx(ref_norm)); + } + } + finalize(&mra); +} +} // namespace complex_mapping + diff --git a/tests/trees/CMakeLists.txt b/tests/trees/CMakeLists.txt index fac77da54..ff70ee660 100644 --- a/tests/trees/CMakeLists.txt +++ b/tests/trees/CMakeLists.txt @@ -2,16 +2,18 @@ target_sources(mrcpp-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/band_width.cpp ${CMAKE_CURRENT_SOURCE_DIR}/bounding_box.cpp ${CMAKE_CURRENT_SOURCE_DIR}/function_tree.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/function_tree_constructors.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mw_tree.cpp ${CMAKE_CURRENT_SOURCE_DIR}/node_box.cpp ${CMAKE_CURRENT_SOURCE_DIR}/node_index.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tree_io.cpp ) -add_Catch_test(NAME band_width LABELS band_width) -add_Catch_test(NAME bounding_box LABELS bounding_box) -add_Catch_test(NAME function_tree LABELS function_tree) -add_Catch_test(NAME mw_tree LABELS mw_tree) -add_Catch_test(NAME node_box LABELS node_box) -add_Catch_test(NAME node_index LABELS node_index) -add_Catch_test(NAME tree_io LABELS tree_io) +add_Catch_test(NAME band_width LABELS "band_width;short") +add_Catch_test(NAME bounding_box LABELS "bounding_box;short") +add_Catch_test(NAME function_tree LABELS "function_tree;short") +add_Catch_test(NAME mw_tree LABELS "mw_tree;short") +add_Catch_test(NAME node_box LABELS "node_box;short") +add_Catch_test(NAME node_index LABELS "node_index;short") +add_Catch_test(NAME tree_io LABELS "tree_io;short") +add_Catch_test(NAME function_tree_constructors LABELS "function_tree_constructors;short") \ No newline at end of file diff --git a/tests/trees/function_tree.cpp b/tests/trees/function_tree.cpp index d9492342e..728e0866c 100644 --- a/tests/trees/function_tree.cpp +++ b/tests/trees/function_tree.cpp @@ -57,6 +57,7 @@ template void testZeroFunction() { } THEN("its squared norm is zero") { REQUIRE(tree.getSquareNorm() == Catch::Approx(0.0)); } THEN("it integrates to zero") { REQUIRE(tree.integrate() == Catch::Approx(0.0)); } + THEN("half of it integrates to zero") { REQUIRE(tree.integrateSide(0, true) == Catch::Approx(0.0)); } THEN("the dot product with itself is zero") { REQUIRE(dot(tree, tree) == Catch::Approx(0.0)); } } finalize(&mra); @@ -69,8 +70,7 @@ SCENARIO("Generating FunctionTree nodes", "[function_tree_generating], [function } template void testGeneratedNodes() { - const int depth = 3; - + unsigned int depth = 3; Coord r; if (r.size() >= 1) r[0] = -0.3; if (r.size() >= 2) r[1] = 0.6; @@ -86,7 +86,6 @@ template void testGeneratedNodes() { WHEN("a non-existing node is fetched") { MWNode &node = tree.getNode(r, depth); - THEN("there will be allocated GenNodes") { REQUIRE(tree.getNGenNodes() > 0); @@ -95,6 +94,7 @@ template void testGeneratedNodes() { THEN("there will be no GenNodes") { REQUIRE(tree.getNGenNodes() == 0); } } } + (void)&node; // Clean up the fetched node } finalize(&mra); } diff --git a/tests/trees/function_tree_constructors.cpp b/tests/trees/function_tree_constructors.cpp new file mode 100644 index 000000000..28c80fef7 --- /dev/null +++ b/tests/trees/function_tree_constructors.cpp @@ -0,0 +1,303 @@ +/* + * MRCPP, a numerical library based on multiresolution analysis and + * the multiwavelet basis which provide low-scaling algorithms as well as + * rigorous error control in numerical computations. + * Copyright (C) 2021 Stig Rune Jensen, Jonas Juselius, Luca Frediani and contributors. + * + * This file is part of MRCPP. + * + * MRCPP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MRCPP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with MRCPP. If not, see . + * + * For information on the complete list of contributors to MRCPP, see: + * + */ + + #include "catch2/catch_all.hpp" +#include "factory_functions.h" +#include "treebuilders/grid.h" +#include "treebuilders/project.h" + +using namespace mrcpp; + +namespace function_tree_constructors { + + template + void testBasicConstructor(); + + template + void testNamedConstructor(); + + template + void testSharedMemoryConstructor(); + + template + void testComplexConstructorFromTwoRealTrees(); + + template + void makeNonZeroTree(FunctionTree& tree, + double alpha, + double beta, + const Coord& pos, + const std::array(D)>& power); + + template + void checkComplexTreeMatchesParts(const FunctionTree& realTree, + const FunctionTree& imagTree, + const FunctionTree& ctree); + + template + void testComplexConstructor(bool useRealPart, bool useImagPart); + + SCENARIO("FunctionTree basic constructor", "[function_tree_constructors]") { + GIVEN("a 1D real FunctionTree") { testBasicConstructor<1, double>(); } + GIVEN("a 2D real FunctionTree") { testBasicConstructor<2, double>(); } + GIVEN("a 3D real FunctionTree") { testBasicConstructor<3, double>(); } + + GIVEN("a 1D complex FunctionTree") { testBasicConstructor<1, ComplexDouble>(); } + GIVEN("a 2D complex FunctionTree") { testBasicConstructor<2, ComplexDouble>(); } + GIVEN("a 3D complex FunctionTree") { testBasicConstructor<3, ComplexDouble>(); } + } + + SCENARIO("FunctionTree named constructor", "[function_tree_constructors]") { + GIVEN("a 1D real FunctionTree with explicit name") { testNamedConstructor<1, double>(); } + GIVEN("a 2D real FunctionTree with explicit name") { testNamedConstructor<2, double>(); } + GIVEN("a 3D real FunctionTree with explicit name") { testNamedConstructor<3, double>(); } + + GIVEN("a 1D complex FunctionTree with explicit name") { testNamedConstructor<1, ComplexDouble>(); } + GIVEN("a 2D complex FunctionTree with explicit name") { testNamedConstructor<2, ComplexDouble>(); } + GIVEN("a 3D complex FunctionTree with explicit name") { testNamedConstructor<3, ComplexDouble>(); } + } + + SCENARIO("FunctionTree shared-memory constructor with nullptr", "[function_tree_constructors]") { + GIVEN("a 1D real FunctionTree with explicit nullptr shared memory") { testSharedMemoryConstructor<1, double>(); } + GIVEN("a 2D real FunctionTree with explicit nullptr shared memory") { testSharedMemoryConstructor<2, double>(); } + GIVEN("a 3D real FunctionTree with explicit nullptr shared memory") { testSharedMemoryConstructor<3, double>(); } + + GIVEN("a 1D complex FunctionTree with explicit nullptr shared memory") { testSharedMemoryConstructor<1, ComplexDouble>(); } + GIVEN("a 2D complex FunctionTree with explicit nullptr shared memory") { testSharedMemoryConstructor<2, ComplexDouble>(); } + GIVEN("a 3D complex FunctionTree with explicit nullptr shared memory") { testSharedMemoryConstructor<3, ComplexDouble>(); } + } + + SCENARIO("Construct complex FunctionTree from two zero real FunctionTrees", + "[function_tree_constructors]") { + GIVEN("two real trees in 1D") { testComplexConstructorFromTwoRealTrees<1>(); } + GIVEN("two real trees in 2D") { testComplexConstructorFromTwoRealTrees<2>(); } + GIVEN("two real trees in 3D") { testComplexConstructorFromTwoRealTrees<3>(); } + } + + SCENARIO("Construct complex FunctionTree from two real FunctionTrees with nonzero values", + "[function_tree_constructors]") { + + GIVEN("real-only input") { + testComplexConstructor<1>(true, false); + testComplexConstructor<2>(true, false); + testComplexConstructor<3>(true, false); + } + + GIVEN("imag-only input") { + testComplexConstructor<1>(false, true); + testComplexConstructor<2>(false, true); + testComplexConstructor<3>(false, true); + } + + GIVEN("both inputs nonzero") { + testComplexConstructor<1>(true, true); + testComplexConstructor<2>(true, true); + testComplexConstructor<3>(true, true); + } + } + + template + void testBasicConstructor() { + MultiResolutionAnalysis* mra = nullptr; + initialize(&mra); + + FunctionTree tree(*mra); + + THEN("the tree starts without generated nodes") { + REQUIRE(tree.getNGenNodes() == 0); + } + + THEN("the generated-node allocator exists and starts empty") { + REQUIRE(tree.getGenNodeAllocator().getNNodes() == 0); + } + + THEN("the root box contains at least one root node") { + REQUIRE(tree.getRootBox().size() > 0); + } + THEN("the tree starts undefined, with negative square norm") { + REQUIRE(tree.getSquareNorm() < 0.0); + } + finalize(&mra); + } + + template + void testNamedConstructor() { + MultiResolutionAnalysis* mra = nullptr; + initialize(&mra); + + const std::string name = "ctor_test"; + FunctionTree tree(*mra, name); + + THEN("the tree starts without generated nodes") { + REQUIRE(tree.getNGenNodes() == 0); + } + + THEN("the tree name is stored") { + REQUIRE(tree.getName() == name); + } +/* + THEN("the tree starts undefined, with negative square norm") { + REQUIRE(tree.getSquareNorm() < 0.0); + } +*/ + finalize(&mra); + } + + template + void testSharedMemoryConstructor() { + MultiResolutionAnalysis* mra = nullptr; + initialize(&mra); + + SharedMemory* sh_mem = nullptr; + const std::string name = "ctor_shmem_test"; + FunctionTree tree(*mra, sh_mem, name); + + THEN("the tree starts without generated nodes") { + REQUIRE(tree.getNGenNodes() == 0); + } + + THEN("the generated-node allocator exists and starts empty") { + REQUIRE(tree.getGenNodeAllocator().getNNodes() == 0); + } + + THEN("the tree name is stored") { + REQUIRE(tree.getName() == name); + } +/* + THEN("the tree starts undefined, with negative square norm") { + REQUIRE(tree.getSquareNorm() < 0.0); + } +*/ + finalize(&mra); + } + + template + void testComplexConstructorFromTwoRealTrees() { + MultiResolutionAnalysis* mra = nullptr; + initialize(&mra); + + FunctionTree realTree(*mra); + FunctionTree imagTree(*mra); + + realTree.setZero(); + imagTree.setZero(); + + FunctionTree ctree(realTree, imagTree, nullptr, "complex_from_two_real"); + Coord r; + if (r.size() >= 1) r[0] = -0.20; + if (r.size() >= 2) r[1] = 0.60; + if (r.size() >= 3) r[2] = 0.76; + + THEN("the constructed complex tree evaluates to zero") { + auto val = ctree.evalf(r); + REQUIRE(val.real() == Catch::Approx(0.0)); + REQUIRE(val.imag() == Catch::Approx(0.0)); + } + THEN("the constructed complex tree has zero squared norm") { + REQUIRE(ctree.getSquareNorm() == Catch::Approx(0.0)); + } + THEN("the constructed complex tree integrates to zero") { + auto val = ctree.integrate(); + REQUIRE(val.real() == Catch::Approx(0.0)); + REQUIRE(val.imag() == Catch::Approx(0.0)); + } + finalize(&mra); + } + + template + void makeNonZeroTree(FunctionTree& tree, + double alpha, + double beta, + const Coord& pos, + const std::array(D)>& power) { + auto prec = 1.0e-4; + GaussFunc f(alpha, beta, pos, power); + build_grid(tree, f); + project(prec, tree, f); + } + + template + void checkComplexTreeMatchesParts(const FunctionTree& realTree, + const FunctionTree& imagTree, + const FunctionTree& ctree) { + Coord r1{}; + Coord r2{}; + + for (int i = 0; i < D; ++i) { + r1[i] = 0.10 * (i + 1); + r2[i] = -0.15 * (i + 1); + } + + for (const auto& r : {r1, r2}) { + auto z = ctree.evalf(r); + auto re = realTree.evalf(r); + auto im = imagTree.evalf(r); + + REQUIRE(z.real() == Catch::Approx(re)); + REQUIRE(z.imag() == Catch::Approx(im)); + } + } + + template + void testComplexConstructor(bool useRealPart, bool useImagPart) { + MultiResolutionAnalysis* mra = nullptr; + initialize(&mra); + + FunctionTree realTree(*mra); + FunctionTree imagTree(*mra); + + Coord posRe{}; + Coord posIm{}; + std::array(D)> powerRe{}; + std::array(D)> powerIm{}; + + for (int i = 0; i < D; ++i) { + posRe[i] = 0.10 * (i + 1); + posIm[i] = -0.07 * (i + 1); + powerRe[i] = 0; + powerIm[i] = (i == 0 ? 1 : 0); + } + + if (useRealPart) { + makeNonZeroTree(realTree, 1.0, 0.4, posRe, powerRe); + } else { + realTree.setZero(); + } + + if (useImagPart) { + makeNonZeroTree(imagTree, 0.6, 0.7, posIm, powerIm); + } else { + imagTree.setZero(); + } + + FunctionTree ctree(realTree, imagTree, nullptr, "complex_from_two_real"); + + checkComplexTreeMatchesParts(realTree, imagTree, ctree); + + finalize(&mra); + } + + +} // namespace function_tree_constructors \ No newline at end of file diff --git a/tests/trees/node_index.cpp b/tests/trees/node_index.cpp index 791c94396..78d684a6c 100644 --- a/tests/trees/node_index.cpp +++ b/tests/trees/node_index.cpp @@ -72,7 +72,6 @@ template void testConstructors() { } SECTION("Parent constructor") { - int i = D; auto cIdx = nIdx->parent(); REQUIRE(cIdx.getScale() == (nIdx->getScale() - 1)); } diff --git a/tools/betzy.env b/tools/betzy.env new file mode 100644 index 000000000..b5b5ee96a --- /dev/null +++ b/tools/betzy.env @@ -0,0 +1,5 @@ +# Module environment for Betzy +module --quiet purge +module load foss/2024a +module load CMake/3.29.3-GCCcore-13.3.0 +module load Python/3.12.3-GCCcore-13.3.0 diff --git a/tools/betzy.sh b/tools/betzy.sh new file mode 100755 index 000000000..92158cd78 --- /dev/null +++ b/tools/betzy.sh @@ -0,0 +1,26 @@ +#!/bin/bash -l + +if [ `hostname | grep -i login | wc -l` == 0 ]; then + echo "This script MUST be run on the LOGIN node as it requires internet access" + exit 1 +fi + +mrcpp_dir="$(pwd)" +source ${mrcpp_dir}/tools/betzy.env + +cd ${mrcpp_dir} +build_dir=${mrcpp_dir}/build +install_dir=${mrcpp_dir}/install + +if [ -d "${build_dir}" ]; then + echo "Build directory already exists, please remove" + exit 1 +else + ./setup --prefix=${install_dir} --omp --mpi --cxx=mpicxx ${build_dir} && \ + cd ${build_dir} && \ + make && \ + OMP_NUM_THREADS=1 ctest --output-on-failure && \ + make install +fi + +exit 0 diff --git a/tools/olivia.env b/tools/olivia.env new file mode 100644 index 000000000..037ee78a2 --- /dev/null +++ b/tools/olivia.env @@ -0,0 +1,7 @@ +# Module environment for Olivia +module reset +module load NRIS/CPU +module load Python/3.13.1-GCCcore-14.2.0 +module load OpenMPI/5.0.7-GCC-14.2.0 +module load CMake/3.31.3-GCCcore-14.2.0 +export CMAKE_TLS_VERIFY=0 diff --git a/tools/olivia.sh b/tools/olivia.sh new file mode 100755 index 000000000..f73571e93 --- /dev/null +++ b/tools/olivia.sh @@ -0,0 +1,32 @@ +#!/bin/bash -l + +if [ -z "$1" ]; then + # If not, prompt the user for input + echo "This script requires an NRIS account for compilation on a compute node" + read -p "Please state your account (nnxxxxk): " account +else + # Otherwise, use the argument + account="$1" +fi + +mrcpp_dir="$(pwd)" +source ${mrcpp_dir}/tools/olivia.env + +cd ${mrcpp_dir} +build_dir=${mrcpp_dir}/build +install_dir=${mrcpp_dir}/install + +if [ -d "${build_dir}" ]; then + echo "Build directory already exists, please remove" + exit 1 +else + ./setup --prefix=${install_dir} --omp --mpi ${build_dir} && \ + cd ${build_dir} && \ + srun --cpus-per-task=4 --mem-per-cpu=2G --time=01:00:00 --account=$account bash -c \ + "source ${mrcpp_dir}/tools/olivia.env ; \ + make -j4 ; \ + OMP_NUM_THREADS=4 ctest --output-on-failure ; \ + make install -j4" +fi + +exit 0 diff --git a/tools/saga.env b/tools/saga.env new file mode 100644 index 000000000..e6da8287f --- /dev/null +++ b/tools/saga.env @@ -0,0 +1,5 @@ +# Module environment for Saga +module --quiet purge +module load foss/2024a +module load CMake/3.29.3-GCCcore-13.3.0 +module load Python/3.12.3-GCCcore-13.3.0 diff --git a/tools/saga.sh b/tools/saga.sh new file mode 100755 index 000000000..285060b00 --- /dev/null +++ b/tools/saga.sh @@ -0,0 +1,26 @@ +#!/bin/bash -l + +if [ `hostname | grep -i login | wc -l` == 0 ]; then + echo "This script MUST be run on the LOGIN node as it requires internet access" + exit 1 +fi + +mrcpp_dir="$(pwd)" +source ${mrcpp_dir}/tools/saga.env + +cd ${mrcpp_dir} +build_dir=${mrcpp_dir}/build +install_dir=${mrcpp_dir}/install + +if [ -d "${build_dir}" ]; then + echo "Build directory already exists, please remove" + exit 1 +else + ./setup --prefix=${install_dir} --omp --mpi --cxx=mpicxx ${build_dir} && \ + cd ${build_dir} && \ + make && \ + OMP_NUM_THREADS=1 ctest --output-on-failure && \ + make install +fi + +exit 0