From 5caa1e6a50d0f1613d5ced8ba5f9536b54dd37b3 Mon Sep 17 00:00:00 2001
From: Cristian C Lalescu <Cristian.Lalescu@mpcdf.mpg.de>
Date: Thu, 20 Jun 2024 08:33:00 +0200
Subject: [PATCH] [wip] adds working folder for unit test of forcing

---
 examples/forcing_unit_test/README.rst         |  40 +++
 .../forcing_unit_test/fluid_solver_check.cpp  | 311 ++++++++++++++++++
 .../forcing_unit_test/fluid_solver_check.hpp  | 100 ++++++
 examples/forcing_unit_test/sanity_check.py    | 100 ++++++
 4 files changed, 551 insertions(+)
 create mode 100644 examples/forcing_unit_test/README.rst
 create mode 100644 examples/forcing_unit_test/fluid_solver_check.cpp
 create mode 100644 examples/forcing_unit_test/fluid_solver_check.hpp
 create mode 100644 examples/forcing_unit_test/sanity_check.py

diff --git a/examples/forcing_unit_test/README.rst b/examples/forcing_unit_test/README.rst
new file mode 100644
index 00000000..0e85aaf6
--- /dev/null
+++ b/examples/forcing_unit_test/README.rst
@@ -0,0 +1,40 @@
+Forcing unit test
+=================
+
+For this test, velocity field is initialized as a single mode.
+Numerical solution is compared with analytic solution for the different
+available forcing methods.
+
+
+**Linear forcing**
+
+initial condition
+
+.. math::
+    u(x,0) = U (e^{i\bar{k}\cdot x} + e^{-i\bar{k}\cdot x})
+    U \perp \bar{k}
+
+solution
+
+.. math::
+    u(x,t) = exp((-\nu\bar{k}^2 + \alpha)t) u(x,0)
+
+where :math:`\alpha` is the linear coefficient.
+
+**Fixed energy injection rate**
+
+initial condition
+
+.. math::
+    u(x,0) = U (e^{i\bar{k}\cdot x} + e^{-i\bar{k}\cdot x})
+    U \perp \bar{k}
+
+solution has same form as for linear forcing, but
+
+.. math::
+    U^2(t) = V^2 + (U^2(0) - V^2) exp(-2\nu\bar{k}^2 t)
+
+with
+
+.. math::
+    V^2 = \epsilon / (2\nu\bar{k}^2)
diff --git a/examples/forcing_unit_test/fluid_solver_check.cpp b/examples/forcing_unit_test/fluid_solver_check.cpp
new file mode 100644
index 00000000..6993d920
--- /dev/null
+++ b/examples/forcing_unit_test/fluid_solver_check.cpp
@@ -0,0 +1,311 @@
+/******************************************************************************
+*                                                                             *
+*  Copyright 2024 TurTLE team                                                 *
+*                                                                             *
+*  This file is part of TurTLE.                                               *
+*                                                                             *
+*  TurTLE is free software: you can redistribute it and/or modify             *
+*  it under the terms of the GNU General Public License as published          *
+*  by the Free Software Foundation, either version 3 of the License,          *
+*  or (at your option) any later version.                                     *
+*                                                                             *
+*  TurTLE 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 General Public License for more details.                               *
+*                                                                             *
+*  You should have received a copy of the GNU General Public License          *
+*  along with TurTLE.  If not, see <http://www.gnu.org/licenses/>             *
+*                                                                             *
+* Contact: Cristian.Lalescu@mpcdf.mpg.de                                      *
+*                                                                             *
+******************************************************************************/
+
+
+//#include "fluid_solver_check.hpp"
+#include "scope_timer.hpp"
+#include "shared_array.hpp"
+#include "fftw_tools.hpp"
+#include "full_code/NSVE.hpp"
+#include "full_code/NSE.hpp"
+
+#include <string>
+#include <cmath>
+#include <filesystem>
+
+template <typename rnumber>
+int fluid_solver_check<rnumber>::initialize(void)
+{
+    TIMEZONE("fluid_solver_check::intialize");
+    this->nx = 32;
+    this->ny = 32;
+    this->nz = 32;
+    this->dkx = 1.0;
+    this->dky = 1.0;
+    this->dkz = 1.0;
+    this->dealias_type = SMOOTH;
+
+    this->spectrum_dissipation = 0.4;
+    this->spectrum_Lint = 1.3;
+    this->spectrum_etaK = 0.3;
+    this->spectrum_large_scale_const = 6.78;
+    this->spectrum_small_scale_const = 0.40;
+    this->random_seed = 2;
+
+    this->dt                   = 0.1;
+    this->famplitude           = 0.5;
+    this->friction_coefficient = 0.1;
+    this->fk0                  = 1.4;
+    this->fk1                  = 2.3;
+    this->energy               = 0.5;
+    this->injection_rate       = 0.4;
+    this->variation_strength   = 0.25;
+    this->variation_time_scale = 1.0;
+    this->fmode                = 2;
+    this->forcing_type         = "linear";
+    this->nu                   = 0.1;
+
+    return EXIT_SUCCESS;
+}
+
+template <typename rnumber>
+int fluid_solver_check<rnumber>::finalize(void)
+{
+    TIMEZONE("fluid_solver_check::finalize");
+    return EXIT_SUCCESS;
+}
+
+template <typename rnumber,
+          field_components fc>
+int get_error(
+        field<rnumber, FFTW, fc>* a,
+        field<rnumber, FFTW, fc>* b,
+        double& diff_L2,
+        double& diff_max,
+        double& aunit_L2,
+        double& bunit_L2)
+{
+    diff_L2  = 0;
+    diff_max = 0;
+    aunit_L2 = 0;
+    bunit_L2 = 0;
+    shared_array<double> aunit_L2_threaded(
+            1,
+            [&](double *val_tmp){
+                *val_tmp = double(0.0);
+            });
+    shared_array<double> bunit_L2_threaded(
+            1,
+            [&](double *val_tmp){
+                *val_tmp = double(0.0);
+            });
+    shared_array<double> diff_L2_threaded(
+            1,
+            [&](double *val_tmp){
+                *val_tmp = double(0.0);
+            });
+    shared_array<double> diff_max_threaded(
+            1,
+            [&](double *val_tmp){
+                *val_tmp = double(0.0);
+            });
+    switch (fc) {
+        case THREE:
+            {
+            a->RLOOP(
+                [&](ptrdiff_t rindex,
+                    ptrdiff_t xindex,
+                    ptrdiff_t yindex,
+                    ptrdiff_t zindex){
+                for (auto cc = 0; cc < 3; cc++) {
+                    const double difference = std::abs(
+                            a->rval(rindex, cc) - b->rval(rindex, cc));
+                    double *diff_max = diff_max_threaded.getMine();
+                    double *diff_L2  = diff_L2_threaded.getMine();
+                    double *aunit_L2 = aunit_L2_threaded.getMine();
+                    double *bunit_L2 = bunit_L2_threaded.getMine();
+                    *diff_max  = std::max(*diff_max, difference);
+                    *diff_L2  += difference * difference;
+                    *aunit_L2 += a->rval(rindex, cc) * a->rval(rindex, cc);
+                    *bunit_L2 += b->rval(rindex, cc) * b->rval(rindex, cc);
+            }});
+            }
+            break;
+        case ONE:
+            {
+            a->RLOOP(
+                [&](ptrdiff_t rindex,
+                    ptrdiff_t xindex,
+                    ptrdiff_t yindex,
+                    ptrdiff_t zindex){
+                const double difference =
+                    std::abs(a->rval(rindex) - b->rval(rindex));
+                double *diff_max = diff_max_threaded.getMine();
+                double *diff_L2  = diff_L2_threaded.getMine();
+                double *aunit_L2 = aunit_L2_threaded.getMine();
+                double *bunit_L2 = bunit_L2_threaded.getMine();
+                *diff_max  = std::max(*diff_max, difference);
+                *diff_L2  += difference * difference;
+                *aunit_L2 += (a->rval(rindex) * a->rval(rindex));
+                *bunit_L2 += (b->rval(rindex) * b->rval(rindex));
+                });
+            }
+            break;
+        }
+
+    aunit_L2_threaded.mergeParallel();
+    bunit_L2_threaded.mergeParallel();
+    diff_L2_threaded.mergeParallel();
+    diff_max_threaded.mergeParallel(
+            [&](const int idx, const double& v1, const double& v2) -> double {
+                return std::max(v1, v2);
+            });
+
+    MPI_Allreduce(
+            aunit_L2_threaded.getMasterData(),
+            &aunit_L2,
+            1,
+            MPI_DOUBLE,
+            MPI_SUM,
+            a->comm);
+    MPI_Allreduce(
+            bunit_L2_threaded.getMasterData(),
+            &bunit_L2,
+            1,
+            MPI_DOUBLE,
+            MPI_SUM,
+            a->comm);
+    MPI_Allreduce(
+            diff_L2_threaded.getMasterData(),
+            &diff_L2,
+            1,
+            MPI_DOUBLE,
+            MPI_SUM,
+            a->comm);
+    MPI_Allreduce(
+            diff_max_threaded.getMasterData(),
+            &diff_max,
+            1,
+            MPI_DOUBLE,
+            MPI_MAX,
+            a->comm);
+    diff_L2 = std::sqrt(diff_L2 / (ncomp(fc)*a->npoints));
+    aunit_L2 = std::sqrt(aunit_L2 / (ncomp(fc)*a->npoints));
+    bunit_L2 = std::sqrt(bunit_L2 / (ncomp(fc)*a->npoints));
+    return EXIT_SUCCESS;
+}
+
+template <typename rnumber>
+int compare_solvers(
+        NSE<rnumber>*  nse,
+        NSVE<rnumber>* nsve)
+{
+    assert(nse->get_nx() == nsve->get_nx());
+    assert(nse->get_ny() == nsve->get_ny());
+    assert(nse->get_nz() == nsve->get_nz());
+
+    nse->print_debug_info();
+    nsve->print_debug_info();
+
+    // fix initial conditions
+    compute_curl(
+        nse->kk,
+        nse->velocity,
+        nsve->fs->cvorticity);
+    *nsve->fs->cvelocity = 0.0;
+
+    // step
+    //nse->Euler_step();
+    //nsve->fs->Euler_step(nsve->fs->dt);
+    nse->step();
+    nsve->fs->step(nsve->fs->dt);
+
+    // compare results
+    double diff_L2, diff_max;
+    double nse_uL2, nsve_uL2;
+    nsve->fs->compute_velocity(nsve->fs->cvorticity);
+
+    nse->velocity->ift();
+    nsve->fs->cvelocity->ift();
+    get_error(
+        nse->velocity,
+        nsve->fs->cvelocity,
+        diff_L2,
+        diff_max,
+        nse_uL2,
+        nsve_uL2);
+    DEBUG_MSG("#### compare solvers information\n");
+    DEBUG_MSG("nse->forcing_type = %s, nsve->forcing_type = %s\n",
+            nse->forcing_type.c_str(), nsve->fs->forcing_type.c_str());
+    DEBUG_MSG("diff_L2 = %g, diff_max = %g, nse_uL2 = %g, nsve_uL2 = %g\n",
+            diff_L2, diff_max, nse_uL2, nsve_uL2);
+    DEBUG_MSG("diff_L2 / nse_uL2 = %g, diff_max / nse_uL2 = %g\n",
+            diff_L2 / nse_uL2, diff_max / nse_uL2);
+    const bool difference_small =
+        diff_max / std::sqrt(nse_uL2*nsve_uL2) < 1e-5;
+    if (difference_small)
+        return EXIT_SUCCESS;
+    else
+        return EXIT_FAILURE;
+}
+
+template <typename rnumber>
+int fluid_solver_check<rnumber>::do_work(void)
+{
+    TIMEZONE("fluid_solver_check::do_work");
+
+    NSE<rnumber>* nse = new NSE<rnumber>(this->get_communicator(), this->simname);
+    clone_parameters(nse);
+    nse->allocate();
+
+    NSVE<rnumber>* nsve = new NSVE<rnumber>(this->get_communicator(), this->simname);
+    clone_parameters(nsve);
+    nsve->allocate();
+
+    // temporary field
+    field<rnumber, FFTW, THREE>* temp_vec_field = new field<rnumber, FFTW, THREE>(
+            nse->get_nx(), nse->get_ny(), nse->get_nz(),
+            nse->get_communicator(),
+            FFTW_ESTIMATE);
+    make_gaussian_random_field(
+            nse->kk,
+            temp_vec_field,
+            this->random_seed,
+            this->spectrum_dissipation,
+            this->spectrum_Lint,
+            this->spectrum_etaK,
+            this->spectrum_large_scale_const,
+            this->spectrum_small_scale_const,
+            3./2.);
+    nse->kk->template project_divfree<rnumber>(temp_vec_field->get_cdata());
+
+    *(nse->velocity) = temp_vec_field->get_cdata();
+    nse->forcing_type = "fixed_energy_injection_rate";
+    nsve->fs->forcing_type = "fixed_energy_injection_rate";
+    const int return_result_feir = compare_solvers(nse, nsve);
+    *(nse->velocity) = temp_vec_field->get_cdata();
+    nse->forcing_type = "linear";
+    nsve->fs->forcing_type = "linear";
+    const int return_result_linear = compare_solvers(nse, nsve);
+    *(nse->velocity) = temp_vec_field->get_cdata();
+    nse->injection_rate = 0;
+    nsve->fs->injection_rate = 0;
+    nse->famplitude = 0;
+    nsve->fs->famplitude = 0;
+    const int return_result_decay = compare_solvers(nse, nsve);
+
+    delete temp_vec_field;
+    delete nse;
+    delete nsve;
+
+    if ((return_result_linear == EXIT_SUCCESS)
+     && (return_result_feir   == EXIT_SUCCESS)
+     && (return_result_decay  == EXIT_SUCCESS))
+        return EXIT_SUCCESS;
+    else
+        return EXIT_FAILURE;
+}
+
+template class fluid_solver_check<float>;
+template class fluid_solver_check<double>;
+
diff --git a/examples/forcing_unit_test/fluid_solver_check.hpp b/examples/forcing_unit_test/fluid_solver_check.hpp
new file mode 100644
index 00000000..dc58515a
--- /dev/null
+++ b/examples/forcing_unit_test/fluid_solver_check.hpp
@@ -0,0 +1,100 @@
+/******************************************************************************
+*                                                                             *
+*  Copyright 2024 TurTLE team                                                 *
+*                                                                             *
+*  This file is part of TurTLE.                                               *
+*                                                                             *
+*  TurTLE is free software: you can redistribute it and/or modify             *
+*  it under the terms of the GNU General Public License as published          *
+*  by the Free Software Foundation, either version 3 of the License,          *
+*  or (at your option) any later version.                                     *
+*                                                                             *
+*  TurTLE 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 General Public License for more details.                               *
+*                                                                             *
+*  You should have received a copy of the GNU General Public License          *
+*  along with TurTLE.  If not, see <http://www.gnu.org/licenses/>             *
+*                                                                             *
+* Contact: Cristian.Lalescu@mpcdf.mpg.de                                      *
+*                                                                             *
+******************************************************************************/
+
+
+
+#ifndef FLUID_SOLVER_CHECK_HPP
+#define FLUID_SOLVER_CHECK_HPP
+
+/** \brief Error analysis for fluid solver.
+ */
+
+#include "full_code/test.hpp"
+
+template <typename rnumber>
+class fluid_solver_check: public test
+{
+    private:
+        double max_velocity_estimate;
+        double spectrum_dissipation;
+        double spectrum_Lint;
+        double spectrum_etaK;
+        double spectrum_large_scale_const;
+        double spectrum_small_scale_const;
+        int output_incompressible_field;
+        int random_seed;
+
+        double dt;
+        double famplitude;
+        double friction_coefficient;
+        double fk0;
+        double fk1;
+        double energy;
+        double injection_rate;
+        double variation_strength;
+        double variation_time_scale;
+        int fmode;
+        std::string forcing_type;
+        double nu;
+
+    public:
+        fluid_solver_check(
+                const MPI_Comm COMMUNICATOR,
+                const std::string &simname):
+            test(
+                    COMMUNICATOR,
+                    simname)
+        {}
+        ~fluid_solver_check(){}
+
+        int initialize(void);
+        int do_work(void);
+        int finalize(void);
+
+        template <class dtype>
+        inline int clone_parameters(dtype *dst) {
+            dst->nx                   = this->nx;
+            dst->ny                   = this->ny;
+            dst->nz                   = this->nz;
+            dst->dkx                  = this->dkx;
+            dst->dky                  = this->dky;
+            dst->dkz                  = this->dkz;
+            dst->dealias_type         = this->dealias_type;
+
+            dst->dt                   = this->dt;
+            dst->famplitude           = this->famplitude;
+            dst->friction_coefficient = this->friction_coefficient;
+            dst->fk0                  = this->fk0;
+            dst->fk1                  = this->fk1;
+            dst->energy               = this->energy;
+            dst->injection_rate       = this->injection_rate;
+            dst->fmode                = this->fmode;
+            dst->forcing_type         = this->forcing_type;
+            dst->nu                   = this->nu;
+            return EXIT_SUCCESS;
+        }
+
+};
+
+#endif//FLUID_SOLVER_CHECK_HPP
+
diff --git a/examples/forcing_unit_test/sanity_check.py b/examples/forcing_unit_test/sanity_check.py
new file mode 100644
index 00000000..4f3cc120
--- /dev/null
+++ b/examples/forcing_unit_test/sanity_check.py
@@ -0,0 +1,100 @@
+import os
+import sys
+import getpass
+import numpy as np
+import h5py
+
+import TurTLE
+import TurTLE.PP
+import TurTLE.DNS
+import TurTLE.TEST
+
+username = getpass.getuser()
+homefolder = os.path.expanduser('~')
+cpp_location = None
+
+import pathlib
+cpp_location = pathlib.Path(__file__).parent.resolve()
+
+
+class sanity_check(TurTLE.TEST):
+    def __init__(
+            self,
+            **kwargs):
+        TurTLE.PP.__init__(self, **kwargs)
+        return None
+    def write_src(
+            self):
+        self.version_message = (
+                '/***********************************************************************\n' +
+                '* this code automatically generated by TurTLE\n' +
+                '* version {0}\n'.format(TurTLE.__version__) +
+                '***********************************************************************/\n\n\n')
+        self.include_list = [
+                '"full_code/main_code.hpp"',
+                '<cmath>']
+        self.main = """
+            int main(int argc, char *argv[])
+            {{
+                bool fpe = (
+                    (getenv("BFPS_FPE_OFF") == nullptr) ||
+                    (getenv("BFPS_FPE_OFF") != std::string("TRUE")));
+                return main_code< {0} >(argc, argv, fpe);
+            }}
+            """.format(self.dns_type + '<{0}>'.format(self.C_field_dtype))
+        self.includes = '\n'.join(
+                ['#include ' + hh
+                 for hh in self.include_list])
+        self.name = 'fluid_solver_check'
+        self.dns_type = 'fluid_solver_check'
+        self.definitions = ''
+        self.definitions += open(
+            os.path.join(
+                cpp_location, 'fluid_solver_check.hpp'), 'r').read()
+        self.definitions += open(
+            os.path.join(
+                cpp_location, 'fluid_solver_check.cpp'), 'r').read()
+        with open(self.name + '.cpp', 'w') as outfile:
+            outfile.write(self.version_message + '\n\n')
+            outfile.write(self.includes + '\n\n')
+            outfile.write(self.definitions + '\n\n')
+            outfile.write(self.main + '\n')
+        return None
+    def add_parser_arguments(
+            self,
+            parser):
+        subparsers = parser.add_subparsers(
+                dest = 'TEST_class',
+                help = 'type of simulation to run')
+        subparsers.required = True
+        parser_ac = subparsers.add_parser(
+                'fluid_solver_check')
+        self.simulation_parser_arguments(parser_ac)
+        self.job_parser_arguments(parser_ac)
+        self.parameters_to_parser_arguments(parser_ac)
+        self.parameters_to_parser_arguments(
+            parser_ac,
+            parameters = self.extra_postprocessing_parameters(
+                'fluid_solver_check'))
+        return None
+    def extra_postprocessing_parameters(
+            self,
+            dns_type = None):
+        assert(dns_type == 'fluid_solver_check')
+        pars = {}
+        return pars
+
+import matplotlib.pyplot as plt
+
+def main():
+    bla = sanity_check()
+    bla.launch(
+            ['fluid_solver_check',
+             '--np', '1',
+             '--ntpp', '1',
+             '--simname', 'fluid_solver_onestep_sanity_check']
+            + sys.argv[1:])
+    return None
+
+if __name__ == '__main__':
+    main()
-- 
GitLab