diff --git a/.gitignore b/.gitignore index e18d19c5b68084e3bef12fa670095f1b73b7af45..ff58edaef9b1c0512d146c6ab80654b6531263ce 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ bfps/install_info.pickle bfps/lib*.a obj build* -venv* \ No newline at end of file +venv* +cpp/turtle_config.hpp diff --git a/TurTLE/DNS.py b/TurTLE/DNS.py index bdd2cc323a4e31dfc968ae74286996d519ec3492..87cf869306997fa294d82b030ee420a2b6a1da6b 100644 --- a/TurTLE/DNS.py +++ b/TurTLE/DNS.py @@ -1447,6 +1447,7 @@ class DNS(_code): c.set_ylim(bottom = 0) c.set_xlabel('$t / \\tau_K$') c.legend(loc = 'best') + fig.suptitle(self.simname) fig.tight_layout() fig.savefig(os.path.join(fig_dir, 'stats' + fig_suffix + '.' + fig_format)) return None diff --git a/TurTLE/test/test_fftw.py b/TurTLE/test/test_fftw.py index bb35935a4bad1660fd15893a162efd24ef7e3867..b0a787079608add7349ea8c6c37d1a377e4c2a1c 100644 --- a/TurTLE/test/test_fftw.py +++ b/TurTLE/test/test_fftw.py @@ -3,6 +3,7 @@ import numpy as np import h5py import sys +import argparse import TurTLE from TurTLE import TEST @@ -18,8 +19,12 @@ except: cmocean = None def main(): + parser = argparse.ArgumentParser('Compare FFTW with `numpy.fft`.') + parser.add_argument('-p', '--plot', dest = 'plot_on', action = 'store_true') + opt = parser.parse_args() + niterations = 10 - nlist = [16, 32, 48, 24, 64, 12] + nlist = [8, 24, 32, 16, 12] for ii in range(len(nlist)): c = TEST() c.launch( @@ -54,9 +59,15 @@ def main(): print('found maximum difference {0} between field0 and field1 at {1}'.format(diff[ii[0], ii[1], ii[2]], ii)) assert(np.max(np.abs(diff)) < 1e-5) + + def L2norm_Fourier(ff): + return np.sqrt( + np.sum(ff[:, :, 0 ] * np.conj(ff[:, :, 0 ])) + + 2*np.sum(ff[:, :, 1:] * np.conj(ff[:, :, 1:]))) + # now compare numpy with fftw np_field1_real = np.fft.irfftn(field1_complex, axes = (0, 1, 2)).transpose(1, 0, 2, 3) - L2normr = np.sqrt(np.mean(np.sum(field1_real**2, axis = 3))) + L2normr = np.sqrt(np.mean(np.sum( field1_real**2, axis = 3))) np_L2normr = np.sqrt(np.mean(np.sum(np_field1_real**2, axis = 3))) err = np.max(np.abs(field1_real - np_field1_real*npoints)) / L2normr print('found maximum difference {0} between field1_real and np_field1_real'.format(err)) @@ -64,15 +75,15 @@ def main(): np_field1_complex = np.fft.rfftn(field1_real.transpose(1, 0, 2, 3), axes = (0, 1, 2)) / npoints - L2norm0 = np.sqrt(np.sum(np.abs(field1_complex[:, :, 0])**2) + 2*np.sum(np.abs(field1_complex[:, :, 1:])**2)) - L2norm1 = np.sqrt(np.sum(np.abs(np_field1_complex[:, :, 0])**2) + 2*np.sum(np.abs(np_field1_complex[:, :, 1:])**2)) + L2norm0 = L2norm_Fourier(field1_complex) + L2norm1 = L2norm_Fourier(np_field1_complex) err = np.max(np.abs(np_field1_complex - field1_complex)) / L2norm0 assert(err < 1e-5) err = abs(L2normr - L2norm0) / L2norm0 assert(err < 1e-5) - if not type(plt) == type(None): + if ((not type(plt) == type(None)) and (opt.plot_on)): f = plt.figure() a = f.add_subplot(221) a.imshow(np.log(np.abs(np_field1_complex[:, :, 0, 0])), interpolation = 'nearest') diff --git a/TurTLE/test/test_fluid_convergence.py b/TurTLE/test/test_fluid_convergence.py new file mode 100644 index 0000000000000000000000000000000000000000..d79cd0039837232c33c36dc49c5151902780ef5d --- /dev/null +++ b/TurTLE/test/test_fluid_convergence.py @@ -0,0 +1,151 @@ +#! /usr/bin/env python +####################################################################### +# # +# Copyright 2021 Max Planck Institute # +# for Dynamics and Self-Organization # +# # +# 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 # +# # +####################################################################### + + + +import os +import numpy as np +import h5py +import sys +import argparse + +import TurTLE +from TurTLE import DNS, PP + + +try: + import matplotlib.pyplot as plt +except: + plt = None + +def main_NSE(): + niterations = 32 + c0 = DNS() + c0.launch( + ['NSE', + '-n', '64', + '--simname', 'nse0', + '--np', '16', + '--ntpp', '1', + '--fftw_plan_rigor', 'FFTW_PATIENT', + '--niter_todo', '{0}'.format(niterations), + '--niter_out', '{0}'.format(niterations//16), + '--niter_stat', '1', + '--checkpoints_per_file', '{0}'.format(3), + '--wd', './']) + c0.compute_statistics() + c0.plot_basic_stats() + #cc = PP() + #cc.launch( + # ['get_velocity', + # '--simname', 'nsve_for_long_comparison', + # '--np', '16', + # '--ntpp', '1', + # '--iter0', '0', + # '--iter1', '{0}'.format(niterations)]) + #c1 = DNS() + #c1.launch( + # ['NSE', + # '-n', '64', + # '--src-simname', 'nsve_for_long_comparison', + # '--src-wd', './', + # '--src-iteration', '0', + # '--simname', 'nse_for_long_comparison', + # '--np', '16', + # '--ntpp', '1', + # '--fftw_plan_rigor', 'FFTW_PATIENT', + # '--niter_todo', '{0}'.format(niterations), + # '--niter_out', '{0}'.format(niterations), + # '--niter_stat', '1', + # '--forcing_type', 'linear', + # '--checkpoints_per_file', '{0}'.format(3), + # '--njobs', '{0}'.format(njobs), + # '--wd', './']) + #def get_err(a, b): + # return np.abs(a - b) / (0.5 * (a + b)) + #if not type(plt) == type(None): + # c0 = DNS(simname = 'nsve_for_long_comparison') + # c1 = DNS(simname = 'nse_for_long_comparison') + # for cc in [c0, c1]: + # cc.compute_statistics() + # cc.plot_basic_stats() + # ########## + # fig = plt.figure() + # a = fig.add_subplot(211) + # a.plot(c0.statistics['energy(t)'], + # label = 'NSVE') + # a.plot(c1.statistics['energy(t)'], + # label = 'NSE') + # a.legend(loc = 'best') + # a = fig.add_subplot(212) + # a.plot(c0.statistics['kshell'], + # c0.statistics['energy(k)'], + # label = 'NSVE') + # a.plot(c1.statistics['kshell'], + # c1.statistics['energy(k)'], + # label = 'NSE') + # a.set_xscale('log') + # a.set_yscale('log') + # a.set_ylim(1e-7, 10) + # fig.tight_layout() + # fig.savefig('comparison_energy_long.pdf') + # plt.close(fig) + # ########## + # fig = plt.figure() + # a = fig.add_subplot(211) + # a.plot(c0.statistics['vel_max(t)'], + # label = 'NSVE') + # a.plot(c1.statistics['vel_max(t)'], + # label = 'NSE') + # a.legend(loc = 'best') + # a = fig.add_subplot(212) + # err = get_err( + # c0.statistics['vel_max(t)'], + # c1.statistics['vel_max(t)']) + # a.plot(c0.statistics['t'] / c0.statistics['tauK'], + # err, + # label = 'velocity') + # vort0_max = c0.get_data_file()['statistics/moments/vorticity'][:, 9, 3] + # vort1_max = c1.get_data_file()['statistics/moments/vorticity'][:, 9, 3] + # a.plot(c0.statistics['t'] / c0.statistics['tauK'], + # get_err(vort0_max, vort1_max), + # label = 'vorticity') + # a.legend(loc = 'best') + # a.set_yscale('log') + # a.set_ylim(bottom = 1e-6) + # fig.tight_layout() + # fig.savefig('comparison_velmax_long.pdf') + # plt.close(fig) + return None + +if __name__ == '__main__': + parser = argparse.ArgumentParser('') + parser.add_argument('--NSE', dest = 'NSE', action = 'store_true') + opt = parser.parse_args() + if opt.NSE: + main_NSE() + else: + pass + diff --git a/TurTLE/test/test_turtle_NSE.py b/TurTLE/test/test_turtle_NSE.py index 6fe5633dfa35e2a5ed1acf4ebc5b4000bb8792d2..62ff60fcf398d7bd97de3843c9ee7ebb02732ae4 100644 --- a/TurTLE/test/test_turtle_NSE.py +++ b/TurTLE/test/test_turtle_NSE.py @@ -29,11 +29,11 @@ import os import numpy as np import h5py import sys +import argparse import TurTLE from TurTLE import DNS, PP -from TurTLE_addons import NSReader try: import matplotlib.pyplot as plt @@ -61,8 +61,7 @@ def main_basic(): '--niter_stat', '1', '--checkpoints_per_file', '{0}'.format(3), '--njobs', '{0}'.format(njobs), - '--wd', './'] + - sys.argv[1:]) + '--wd', './']) cc = PP() cc.launch( ['get_velocity', @@ -70,8 +69,7 @@ def main_basic(): '--np', '4', '--ntpp', '2', '--iter0', '0', - '--iter1', '64'] + - sys.argv[1:]) + '--iter1', '64']) c1 = DNS() c1.launch( ['NSE', @@ -89,8 +87,13 @@ def main_basic(): '--forcing_type', 'linear', '--checkpoints_per_file', '{0}'.format(3), '--njobs', '{0}'.format(njobs), - '--wd', './'] + - sys.argv[1:]) + '--wd', './']) + if not type(plt) == type(None): + c0 = DNS(simname = 'nsve_for_long_comparison') + c1 = DNS(simname = 'nse_for_long_comparison') + for cc in [c0, c1]: + cc.compute_statistics() + cc.plot_basic_stats() f0 = h5py.File( 'nsve_for_comparison.h5', 'r') f1 = h5py.File( @@ -133,8 +136,8 @@ def main_long(): '--src-wd', TurTLE.data_dir, '--src-iteration', '0', '--simname', 'nsve_for_long_comparison', - '--np', '4', - '--ntpp', '2', + '--np', '16', + '--ntpp', '1', '--fftw_plan_rigor', 'FFTW_PATIENT', '--niter_todo', '{0}'.format(niterations), '--niter_out', '{0}'.format(niterations), @@ -142,17 +145,15 @@ def main_long(): '--niter_stat', '1', '--checkpoints_per_file', '{0}'.format(3), '--njobs', '{0}'.format(njobs), - '--wd', './'] + - sys.argv[1:]) + '--wd', './']) cc = PP() cc.launch( ['get_velocity', '--simname', 'nsve_for_long_comparison', - '--np', '4', - '--ntpp', '2', + '--np', '16', + '--ntpp', '1', '--iter0', '0', - '--iter1', '64'] + - sys.argv[1:]) + '--iter1', '{0}'.format(niterations)]) c1 = DNS() c1.launch( ['NSE', @@ -161,8 +162,8 @@ def main_long(): '--src-wd', './', '--src-iteration', '0', '--simname', 'nse_for_long_comparison', - '--np', '4', - '--ntpp', '2', + '--np', '16', + '--ntpp', '1', '--fftw_plan_rigor', 'FFTW_PATIENT', '--niter_todo', '{0}'.format(niterations), '--niter_out', '{0}'.format(niterations), @@ -170,19 +171,22 @@ def main_long(): '--forcing_type', 'linear', '--checkpoints_per_file', '{0}'.format(3), '--njobs', '{0}'.format(njobs), - '--wd', './'] + - sys.argv[1:]) - c0 = NSReader(simname = 'nsve_for_long_comparison') - c1 = NSReader(simname = 'nse_for_long_comparison') - c0.do_plots() - c1.do_plots() + '--wd', './']) + def get_err(a, b): + return np.abs(a - b) / (0.5 * (a + b)) if not type(plt) == type(None): + c0 = DNS(simname = 'nsve_for_long_comparison') + c1 = DNS(simname = 'nse_for_long_comparison') + for cc in [c0, c1]: + cc.compute_statistics() + cc.plot_basic_stats() + ########## fig = plt.figure() a = fig.add_subplot(211) a.plot(c0.statistics['energy(t)'], label = 'NSVE') a.plot(c1.statistics['energy(t)'], - label = 'NSVE') + label = 'NSE') a.legend(loc = 'best') a = fig.add_subplot(212) a.plot(c0.statistics['kshell'], @@ -197,9 +201,40 @@ def main_long(): fig.tight_layout() fig.savefig('comparison_energy_long.pdf') plt.close(fig) + ########## + fig = plt.figure() + a = fig.add_subplot(211) + a.plot(c0.statistics['vel_max(t)'], + label = 'NSVE') + a.plot(c1.statistics['vel_max(t)'], + label = 'NSE') + a.legend(loc = 'best') + a = fig.add_subplot(212) + err = get_err( + c0.statistics['vel_max(t)'], + c1.statistics['vel_max(t)']) + a.plot(c0.statistics['t'] / c0.statistics['tauK'], + err, + label = 'velocity') + vort0_max = c0.get_data_file()['statistics/moments/vorticity'][:, 9, 3] + vort1_max = c1.get_data_file()['statistics/moments/vorticity'][:, 9, 3] + a.plot(c0.statistics['t'] / c0.statistics['tauK'], + get_err(vort0_max, vort1_max), + label = 'vorticity') + a.legend(loc = 'best') + a.set_yscale('log') + a.set_ylim(bottom = 1e-6) + fig.tight_layout() + fig.savefig('comparison_velmax_long.pdf') + plt.close(fig) return None if __name__ == '__main__': - main_basic() - #main_long() + parser = argparse.ArgumentParser('compare NSE with NSVE') + parser.add_argument('--long', dest = 'long', action = 'store_true') + opt = parser.parse_args() + if opt.long: + main_long() + else: + main_basic() diff --git a/cpp/full_code/NSE.cpp b/cpp/full_code/NSE.cpp index 1ed1339fa0d089f8a05ace5693d23920e07b4c3f..52bee4186629bb564c39c895c5e466d90e8fcb28 100644 --- a/cpp/full_code/NSE.cpp +++ b/cpp/full_code/NSE.cpp @@ -34,8 +34,7 @@ int NSE<rnumber>::initialize(void) TIMEZONE("NSE::initialize"); this->read_iteration(); this->read_parameters(); - if (this->myrank == 0) - { + if (this->myrank == 0) { // set caching parameters hid_t fapl = H5Pcreate(H5P_FILE_ACCESS); herr_t cache_err = H5Pset_cache(fapl, 0, 521, 134217728, 1.0); @@ -47,11 +46,11 @@ int NSE<rnumber>::initialize(void) fapl); } int data_file_problem; - if (this->myrank == 0) + if (this->myrank == 0) { data_file_problem = this->grow_file_datasets(); + } MPI_Bcast(&data_file_problem, 1, MPI_INT, 0, this->comm); - if (data_file_problem > 0) - { + if (data_file_problem > 0) { std::cerr << data_file_problem << " problems growing file datasets.\ntrying to exit now." << @@ -61,53 +60,82 @@ int NSE<rnumber>::initialize(void) /* allocate vector fields */ this->velocity = new field<rnumber, FFTW, THREE>( - this->nx, this->ny, this->nz, + this->nx, + this->ny, + this->nz, this->comm, fftw_planner_string_to_flag[this->fftw_plan_rigor]); this->tmp_velocity1 = new field<rnumber, FFTW, THREE>( - this->nx, this->ny, this->nz, + this->nx, + this->ny, + this->nz, this->comm, fftw_planner_string_to_flag[this->fftw_plan_rigor]); this->tmp_velocity2 = new field<rnumber, FFTW, THREE>( - this->nx, this->ny, this->nz, + this->nx, + this->ny, + this->nz, this->comm, fftw_planner_string_to_flag[this->fftw_plan_rigor]); this->tmp_vec_field0 = new field<rnumber, FFTW, THREE>( - this->nx, this->ny, this->nz, + this->nx, + this->ny, + this->nz, this->comm, fftw_planner_string_to_flag[this->fftw_plan_rigor]); this->tmp_vec_field1 = new field<rnumber, FFTW, THREE>( - this->nx, this->ny, this->nz, + this->nx, + this->ny, + this->nz, this->comm, fftw_planner_string_to_flag[this->fftw_plan_rigor]); /* initialize kspace */ this->kk = new kspace<FFTW, SMOOTH>( this->velocity->clayout, this->dkx, this->dky, this->dkz); + if (this->myrank == 0 && this->iteration == 0) { + this->kk->store(stat_file); + } - { - this->velocity->real_space_representation = false; - this->velocity->io( - this->get_current_fname(), - "velocity", - this->iteration, - true); - TIMEZONE("NSE::initialize::read_initial_condition"); + /* initialize field */ + this->velocity->real_space_representation = false; + if ((this->iteration == 0) + && (this->field_random_seed != 0)) { + TIMEZONE("NSE::initialize::generate_initial_condition"); + // generate initial condition + make_gaussian_random_field( + this->kk, + this->velocity, + this->field_random_seed, + this->injection_rate, + 1.0, // Lint + 1.5 / this->kk->kM, // etaK + 6.78, + 0.40, + 3./2.); this->kk->template low_pass<rnumber, THREE>( this->velocity->get_cdata(), this->kk->kM); - this->kk->template force_divfree<rnumber>( + this->kk->template project_divfree<rnumber>( this->velocity->get_cdata()); this->velocity->symmetrize(); - MPI_Barrier(this->comm); + this->velocity->io( + this->get_current_fname(), + "velocity", + this->iteration, + false); + } else { + TIMEZONE("NSE::initialize::read_initial_condition"); + this->velocity->io( + this->get_current_fname(), + "velocity", + this->iteration, + true); } - - if (this->myrank == 0 && this->iteration == 0) - this->kk->store(stat_file); return EXIT_SUCCESS; } @@ -557,6 +585,15 @@ int NSE<rnumber>::do_stats() this->iteration / niter_stat, this->max_velocity_estimate/std::sqrt(3)); + double CFL_velocity = tmp_vec_field0->compute_CFL_velocity(); + if (this->myrank == 0) + hdf5_tools::update_time_series_with_single_rank<double>( + stat_group, + "CFL_velocity", + this->iteration / niter_stat, + 1, + &CFL_velocity); + compute_curl(this->kk, this->velocity, this->tmp_vec_field0); @@ -582,6 +619,7 @@ int NSE<rnumber>::read_parameters(void) this->nu = hdf5_tools::read_value<double>(parameter_file, "parameters/nu"); this->dt = hdf5_tools::read_value<double>(parameter_file, "parameters/dt"); this->fmode = hdf5_tools::read_value<int>(parameter_file, "parameters/fmode"); + this->field_random_seed = hdf5_tools::read_value<int>(parameter_file, "parameters/field_random_seed"); this->famplitude = hdf5_tools::read_value<double>(parameter_file, "parameters/famplitude"); this->friction_coefficient = hdf5_tools::read_value<double>(parameter_file, "parameters/friction_coefficient"); this->injection_rate = hdf5_tools::read_value<double>(parameter_file, "parameters/injection_rate"); diff --git a/cpp/full_code/NSE.hpp b/cpp/full_code/NSE.hpp index ef0813e74aa3a68980468d0d0a377015eda0edb2..4ff447e1afe2fd5900e8a7630803b903a771ed46 100644 --- a/cpp/full_code/NSE.hpp +++ b/cpp/full_code/NSE.hpp @@ -55,6 +55,7 @@ class NSE: public direct_numerical_simulation double energy; double injection_rate; int fmode; + int field_random_seed; std::string forcing_type; int histogram_bins; double max_velocity_estimate; diff --git a/cpp/full_code/NSVE.cpp b/cpp/full_code/NSVE.cpp index 24fc890fef55d50d07d2b2e0439f276b86e54913..9f323c587579ed301a717c404b8503f27bcf52b4 100644 --- a/cpp/full_code/NSVE.cpp +++ b/cpp/full_code/NSVE.cpp @@ -112,6 +112,7 @@ int NSVE<rnumber>::initialize(void) compute_curl(this->fs->kk, this->fs->cvelocity, this->fs->cvorticity); this->fs->kk->template project_divfree<rnumber>( this->fs->cvorticity->get_cdata()); + this->fs->cvorticity->symmetrize(); this->fs->io_checkpoint(false); } else { this->fs->io_checkpoint(); diff --git a/cpp/full_code/code_base.hpp b/cpp/full_code/code_base.hpp index 2886447dc7b3a5d8658d1a8f08ec8d994318cc59..3cce833bab8490ad365ed8dccdbf8dfdc35b39f4 100644 --- a/cpp/full_code/code_base.hpp +++ b/cpp/full_code/code_base.hpp @@ -109,6 +109,12 @@ class code_base return EXIT_SUCCESS; } + inline MPI_Comm get_communicator() const {return this->comm;} + + inline int get_nx() const {return this->nx;} + inline int get_ny() const {return this->ny;} + inline int get_nz() const {return this->nz;} + /** Reads parameters * \warning This method should ensure the parameter file is closed by * all MPI processes when finished. diff --git a/cpp/full_code/shared_array_test.cpp b/cpp/full_code/shared_array_test.cpp index e1be24be04b0b1bf26beda67c45a7c80ee3122e0..1791adec63a9b07e2f4c1143d747779a9379a40f 100644 --- a/cpp/full_code/shared_array_test.cpp +++ b/cpp/full_code/shared_array_test.cpp @@ -57,9 +57,9 @@ template <typename rnumber> int shared_array_test<rnumber>::do_work(void) { TIMEZONE("shared_array_test::do_work"); - const int array_size = 1000; + const int array_size = 500; double value = 0.0; - for (auto ii = 0; ii < 2000; ii++) + for (auto ii = 0; ii < 1000; ii++) { shared_array<double> local_value( array_size, diff --git a/examples/field_convergence/EDNS.py b/examples/field_convergence/EDNS.py new file mode 100644 index 0000000000000000000000000000000000000000..de8e5056ac5ac62763665cf4e0d6d676c87ea8a3 --- /dev/null +++ b/examples/field_convergence/EDNS.py @@ -0,0 +1,296 @@ +import os +import sys +import getpass +import subprocess + +import numpy as np +import h5py +import matplotlib.pyplot as plt + +import TurTLE +import TurTLE._base +import TurTLE.DNS +from TurTLE._code import _code + +username = getpass.getuser() +homefolder = os.path.expanduser('~') +cpp_location = None + +import pathlib +cpp_location = pathlib.Path(__file__).parent.resolve() + + +class EDNS(TurTLE.DNS): + def __init__( + self, + **kwargs): + TurTLE.DNS.__init__(self, **kwargs) + self.list_of_particle_codes = [] + self.extra_parameters = { + 'kraichnan_field' : {}, + 'NSVE_Stokes_particles' : {}, + 'NSVEp_extra_sampling' : {}} + self.check_current_vorticity_exists = False + 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_error' + self.dns_type = 'fluid_error' + self.definitions = '' + self.definitions += open( + os.path.join( + cpp_location, 'abstract_fluid_solver.hpp'), 'r').read() + self.definitions += open( + os.path.join( + cpp_location, 'fluid_solver.hpp'), 'r').read() + self.definitions += open( + os.path.join( + cpp_location, 'fluid_error.hpp'), 'r').read() + self.definitions += open( + os.path.join( + cpp_location, 'fluid_error.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 generate_default_parameters(self): + TurTLE.DNS.generate_default_parameters(self) + self.extra_parameters['fluid_error'] = { + 'tfs' : 1, + 'max_err_estimate' : 1e-5, + 'max_err_relative_estimate' : 1e-4, + } + return None + + def write_par( + self, + iter0 = 0): + assert (self.parameters['niter_todo'] % self.parameters['niter_stat'] == 0) + assert (self.parameters['niter_todo'] % self.parameters['niter_out'] == 0) + assert (self.parameters['niter_out'] % self.parameters['niter_stat'] == 0) + #os.chdir(os.path.join(self.work_dir, self.simname)) + _code.write_par(self, iter0 = iter0) + with h5py.File(self.get_data_file_name(), 'r+') as ofile: + ofile['code_info/exec_name'] = self.name + kspace = self.get_kspace() + for k in kspace.keys(): + ofile['kspace/' + k] = kspace[k] + nshells = kspace['nshell'].shape[0] + kspace = self.get_kspace() + nshells = kspace['nshell'].shape[0] + vec_stat_datasets = [ + 'velocity_err1', + 'velocity_err2', + 'velocity_err1_relative', + 'velocity_err2_relative', + 'vorticity_err1', + 'vorticity_err2', + 'vorticity_err1_relative', + 'vorticity_err2_relative'] + scal_stat_datasets = [] + for k in vec_stat_datasets: + time_chunk = 2**20//(8*3*3*nshells) + time_chunk = max(time_chunk, 1) + ofile.create_dataset('statistics/spectra/' + k + '_' + k, + (1, nshells, 3, 3), + chunks = (time_chunk, nshells, 3, 3), + maxshape = (None, nshells, 3, 3), + dtype = np.float64) + time_chunk = 2**20//(8*4*10) + time_chunk = max(time_chunk, 1) + a = ofile.create_dataset('statistics/moments/' + k, + (1, 10, 4), + chunks = (time_chunk, 10, 4), + maxshape = (None, 10, 4), + dtype = np.float64) + time_chunk = 2**20//(8*4*self.parameters['histogram_bins']) + time_chunk = max(time_chunk, 1) + ofile.create_dataset('statistics/histograms/' + k, + (1, + self.parameters['histogram_bins'], + 4), + chunks = (time_chunk, + self.parameters['histogram_bins'], + 4), + maxshape = (None, + self.parameters['histogram_bins'], + 4), + dtype = np.int64) + ofile['checkpoint'] = int(0) + for member_id in [1, 2, 4]: + simname = self.simname + str(member_id) + cc = TurTLE.DNS() + fluid_type = 'NSVE' + if self.parameters['tfs'] == 1: + fluid_type = 'NSE' + cc.prepare_launch( + args = [ + fluid_type, + '--simname', simname, + '--wd', self.work_dir]) + for kk in cc.parameters.keys(): + cc.parameters[kk] = self.parameters[kk] + # member-specific parameters + cc.parameters['dt'] = self.parameters['dt'] / member_id + cc.parameters['niter_todo'] = self.parameters['niter_todo'] * member_id + cc.parameters['niter_out'] = self.parameters['niter_out'] * member_id + cc.parameters['niter_stat'] = self.parameters['niter_stat'] * member_id + cc.write_par() + return None + def prepare_launch( + self, + **kwargs): + opt = TurTLE.DNS.prepare_launch(self, **kwargs) + return opt + def launch( + self, + args = [], + **kwargs): + # if no --simname argument is added to args with the same value as self.ensname (or self.simname), + # self.simname is overwritten with the DNS default inside of prepare_launch. + # to avoid multiple assignments of self.ensname, we enforce the correct naming scheme here. + assert('--simname' not in args) + args += ['--simname', self.simname] + + opt = self.prepare_launch(args = args) + if not os.path.exists(self.get_data_file_name()): + self.write_par() + self.run( + nb_processes = opt.nb_processes, + nb_threads_per_process = opt.nb_threads_per_process, + njobs = opt.njobs, + hours = opt.minutes // 60, + minutes = opt.minutes % 60, + no_submit = opt.no_submit, + no_debug = opt.no_debug) + return None + def run_sanity_check( + self, + fname = None): + if type(fname) == type(None): + fname = self.simname + ofile = open(fname + '.tex', 'w') + ofile.write('\\documentclass{article}\n') + ofile.write('\\usepackage{graphicx}\n') + ofile.write('\\usepackage[a4paper,' + + 'bindingoffset = 0cm,' + + 'left = 1cm,' + + 'right = 1cm,' + + 'top = 1cm,' + + 'bottom = 2cm]{geometry}\n') + ofile.write('\\begin{document}\n') + members = self.get_member_DNS_list() + for cc in members: + cc.read_parameters() + print(cc.simname) + print(cc.parameters) + cc.compute_statistics() + cc.plot_basic_stats(fig_dir = './figs') + ofile.write('\\verb|' + cc.work_dir + '|\n\n') + ofile.write('\\verb|' + cc.simname + '|\n\n') + ofile.write('forcing type is \\verb|' + cc.parameters['forcing_type'] + '|\n\n') + ofile.write('\\includegraphics[width=\\textwidth]{' + + './figs/stats_' + cc.simname + '.pdf}\n') + ofile.write('\\newpage\n') + ofile.write('\\end{document}\n') + ofile.close() + subprocess.check_call(['pdflatex', fname + '.tex']) + return members + def get_member_DNS_list(self): + """ + Method called "get_member_DNS_list" to make it obvious that + this returns a list of DNS objects. + """ + member_list = [ TurTLE.DNS( + simname = '{0}{1}'.format(self.simname, i), + work_dir = self.work_dir) + for i in [1, 2, 4]] + return member_list + def read_error_statistics( + self): + df = self.get_data_file() + for ff in ['velocity', 'vorticity']: + for ii in [1, 2]: + self.statistics['error/moments/{0}_err{1}'.format(ff, ii)] = \ + df['statistics/moments/{0}_err{1}'.format(ff, ii)][()] + self.statistics['error/moments/{0}_err{1}_relative'.format(ff, ii)] = \ + df['statistics/moments/{0}_err{1}_relative'.format(ff, ii)][()] + self.statistics['error/histograms/{0}_err{1}'.format(ff, ii)] = \ + df['statistics/histograms/{0}_err{1}'.format(ff, ii)][()] + self.statistics['error/histograms/{0}_err{1}_relative'.format(ff, ii)] = \ + df['statistics/histograms/{0}_err{1}_relative'.format(ff, ii)][()] + df.close() + return None + def plot_error_statistics( + self, + fname = None, + err_norm = 1.0): + if type(fname) == type(None): + fname = self.simname + '_err_stats' + for ff in ['velocity', 'vorticity']: + f = plt.figure(figsize = (8, 8)) + a = f.add_subplot(221) + a.plot(self.statistics['error/moments/{0}_err1'.format(ff)][:, 2, 3]**0.5) + a.plot(self.statistics['error/moments/{0}_err2'.format(ff)][:, 2, 3]**0.5) + a.set_title('absolute') + a.set_xlabel('iteration') + a = f.add_subplot(222) + a.plot(self.statistics['error/moments/{0}_err1_relative'.format(ff)][:, 2, 3]**0.5) + a.plot(self.statistics['error/moments/{0}_err2_relative'.format(ff)][:, 2, 3]**0.5) + a.set_title('relative') + a.set_xlabel('iteration') + a = f.add_subplot(223) + a.plot(self.statistics['error/histograms/{0}_err1'.format(ff)][-1, :, 3]) + a.plot(self.statistics['error/histograms/{0}_err2'.format(ff)][-1, :, 3]) + a.set_title('absolute') + a.set_yscale('log') + a = f.add_subplot(224) + a.plot(self.statistics['error/histograms/{0}_err1_relative'.format(ff)][-1, :, 3]) + a.plot(self.statistics['error/histograms/{0}_err2_relative'.format(ff)][-1, :, 3]) + a.set_title('relative') + a.set_yscale('log') + f.tight_layout() + f.suptitle(ff) + f.savefig(fname + '_' + ff + '.pdf') + plt.close(f) + return None + +def main(): + bla = EDNS() + bla.launch( + ['fluid_error', + '--np', '4', + '--ntpp', '1', + '--tfs', '1', + '--field_random_seed', '2'] + + sys.argv[1:]) + bla.read_parameters() + mm = bla.run_sanity_check() + bla.read_error_statistics() + bla.plot_error_statistics(err_norm = mm[0].statistics['Uint']) + return None + +if __name__ == '__main__': + main() diff --git a/examples/field_convergence/README.rst b/examples/field_convergence/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..530f46f2e2075e496ad0f9de81d334a13426add7 --- /dev/null +++ b/examples/field_convergence/README.rst @@ -0,0 +1,13 @@ +Error analysis for Navier-Stokes solvers +======================================== + +This is a code to analyze errors of the fluid solver in TurTLE. + +There are two main goals to achieve: + +* perform error analysis for Navier-Stokes solvers. + +* provide an example of TurTLE facilitating "exotic" studies, i.e. use TurTLE as a framework. + +This folder contains a basic C++ class to compute the errors, as well as +a Python wrapper that will launch jobs and read the results. diff --git a/examples/field_convergence/abstract_fluid_solver.hpp b/examples/field_convergence/abstract_fluid_solver.hpp new file mode 100644 index 0000000000000000000000000000000000000000..37debf4bf1e1b9f74742349671d17131c4df7b56 --- /dev/null +++ b/examples/field_convergence/abstract_fluid_solver.hpp @@ -0,0 +1,80 @@ +/****************************************************************************** +* * +* Copyright 2023 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 ABSTRACT_FLUID_SOLVER_HPP +#define ABSTRACT_FLUID_SOLVER_HPP + +#include "field.hpp" +#include "kspace.hpp" + +/** \brief Abstract fluid solver class + */ + +template <typename rnumber> +class abstract_fluid_solver +{ + public: + abstract_fluid_solver( + const MPI_Comm COMM, + const std::string &simname) + {} + virtual ~abstract_fluid_solver() noexcept(false) {} + + virtual MPI_Comm get_communicator() const = 0; + virtual int get_nx() const = 0; + virtual int get_ny() const = 0; + virtual int get_nz() const = 0; + + virtual int write_checkpoint(void) = 0; + virtual int initialize(void) = 0; + virtual int step(void) = 0; + virtual int do_stats(void) = 0; + virtual int finalize(void) = 0; + + virtual int get_cvelocity(field<rnumber, FFTW, THREE> *) = 0; + virtual int get_cvorticity(field<rnumber, FFTW, THREE> *) = 0; + + virtual int copy_state_from(abstract_fluid_solver<rnumber> &) = 0; + + virtual kspace<FFTW, SMOOTH>* get_kspace() = 0; + + virtual unsigned get_fftw_plan_rigor() const = 0; + + int get_rvelocity(field<rnumber, FFTW, THREE> *destination) + { + this->get_cvelocity(destination); + destination->ift(); + return EXIT_SUCCESS; + } + int get_rvorticity(field<rnumber, FFTW, THREE> *destination) + { + this->get_cvorticity(destination); + destination->ift(); + return EXIT_SUCCESS; + } +}; + +#endif//ABSTRACT_FLUID_SOLVER_HPP + diff --git a/examples/field_convergence/fluid_error.cpp b/examples/field_convergence/fluid_error.cpp new file mode 100644 index 0000000000000000000000000000000000000000..245085def49b5e9ad446ddac3cb96a08ccabb5b7 --- /dev/null +++ b/examples/field_convergence/fluid_error.cpp @@ -0,0 +1,319 @@ +/****************************************************************************** +* * +* Copyright 2023 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_error.hpp" +#include "scope_timer.hpp" + +#include <string> +#include <cmath> +#include <filesystem> + +template <typename rnumber> +int fluid_error<rnumber>::initialize(void) +{ + TIMEZONE("fluid_error::intialize"); + this->read_parameters(); + + this->fs1 = create_fluid_solver<rnumber>( + this->tfs, + this->get_communicator(), + this->simname + "1"); + + this->fs2 = create_fluid_solver<rnumber>( + this->tfs, + this->get_communicator(), + this->simname + "2"); + + this->fs4 = create_fluid_solver<rnumber>( + this->tfs, + this->get_communicator(), + this->simname + "4"); + + this->fs1->initialize(); + this->fs2->initialize(); + this->fs4->initialize(); + + this->fs2->copy_state_from(*(this->fs1)); + this->fs4->copy_state_from(*(this->fs1)); + + // open stat file + if (this->myrank == 0) { + // set caching parameters + hid_t fapl = H5Pcreate(H5P_FILE_ACCESS); + herr_t cache_err = H5Pset_cache(fapl, 0, 521, 134217728, 1.0); + variable_used_only_in_assert(cache_err); + DEBUG_MSG("when setting stat_file cache I got %d\n", cache_err); + this->stat_file = H5Fopen( + (this->simname + ".h5").c_str(), + H5F_ACC_RDWR, + fapl); + } + int data_file_problem; + if (this->myrank == 0) { + data_file_problem = this->grow_file_datasets(); + } + MPI_Bcast(&data_file_problem, 1, MPI_INT, 0, this->get_communicator()); + if (data_file_problem > 0) { + std::cerr << + data_file_problem << + " problems growing file datasets.\ntrying to exit now." << + std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +template <typename rnumber> +int fluid_error<rnumber>::step(void) +{ + TIMEZONE("fluid_error::step"); + this->fs1->step(); + + this->fs2->step(); + this->fs2->step(); + + this->fs4->step(); + this->fs4->step(); + this->fs4->step(); + this->fs4->step(); + + this->iteration++; + return EXIT_SUCCESS; +} + +template <typename rnumber> +int fluid_error<rnumber>::write_checkpoint(void) +{ + TIMEZONE("fluid_error::write_checkpoint"); + this->fs1->write_checkpoint(); + this->fs2->write_checkpoint(); + this->fs4->write_checkpoint(); + this->write_iteration(); + return EXIT_SUCCESS; +} + +template <typename rnumber> +int fluid_error<rnumber>::finalize(void) +{ + TIMEZONE("fluid_error::finalize"); + this->fs1->finalize(); + this->fs2->finalize(); + this->fs4->finalize(); + delete this->fs1; + delete this->fs2; + delete this->fs4; + if (this->myrank == 0) + H5Fclose(this->stat_file); + return EXIT_SUCCESS; +} + +template <typename rnumber> +inline rnumber relative_difference( + const rnumber a, + const rnumber b) +{ + const rnumber difference = a - b; + const rnumber denominator2 = a*a + b*b; + return ((denominator2 > std::numeric_limits<rnumber>::epsilon()) + ? (difference / std::sqrt(denominator2)) + : 0); +} + +template <typename rnumber> +int compare_real_fields( + kspace<FFTW, SMOOTH>* kk, + const field<rnumber, FFTW, THREE>* a, + const field<rnumber, FFTW, THREE>* b, + const std::string prefix, + const std::string suffix, + const hid_t stat_group, + const int ii, + const double max_err_estimate, + const double max_err_relative_estimate) +{ + TIMEZONE("compare_real_fields"); + field<rnumber, FFTW, THREE> * err; + field<rnumber, FFTW, THREE> * err_relative; + err = new field<rnumber, FFTW, THREE>( + a->get_nx(), a->get_ny(), a->get_nz(), + a->comm, + a->fftw_plan_rigor); + err_relative = new field<rnumber, FFTW, THREE>( + a->get_nx(), a->get_ny(), a->get_nz(), + a->comm, + a->fftw_plan_rigor); + err->RLOOP( + [&](const ptrdiff_t rindex, + const ptrdiff_t xindex, + const ptrdiff_t yindex, + const ptrdiff_t zindex){ + for (auto ii = 0; ii < 3; ii++) { + err->rval(rindex, ii) = ( + a->rval(rindex, ii) + - b->rval(rindex, ii)); + err_relative->rval(rindex, ii) = relative_difference( + a->rval(rindex, ii), + b->rval(rindex, ii)); + } + }); + err->compute_stats( + kk, + stat_group, + prefix + "_err" + suffix, + ii, + max_err_estimate); + err_relative->compute_stats( + kk, + stat_group, + prefix + "_err" + suffix + "_relative", + ii, + max_err_relative_estimate); + delete err; + delete err_relative; + return EXIT_SUCCESS; +} + +template <typename rnumber> +int fluid_error<rnumber>::do_stats() +{ + TIMEZONE("fluid_error::do_stats"); + this->fs1->do_stats(); + this->fs2->do_stats(); + this->fs4->do_stats(); + // compute errors and error stats + if (!(this->iteration % this->niter_stat == 0)) + return EXIT_SUCCESS; + hid_t stat_group; + if (this->myrank == 0) + stat_group = H5Gopen( + this->stat_file, + "statistics", + H5P_DEFAULT); + else + stat_group = 0; + field<rnumber, FFTW, THREE> * tmp_vec0; + field<rnumber, FFTW, THREE> * tmp_vec1; + tmp_vec0 = new field<rnumber, FFTW, THREE>( + this->fs1->get_nx(), + this->fs1->get_ny(), + this->fs1->get_nz(), + this->fs1->get_communicator(), + this->fs1->get_fftw_plan_rigor()); + tmp_vec1 = new field<rnumber, FFTW, THREE>( + this->fs2->get_nx(), + this->fs2->get_ny(), + this->fs2->get_nz(), + this->fs2->get_communicator(), + this->fs2->get_fftw_plan_rigor()); + + this->fs1->get_rvelocity(tmp_vec0); + this->fs2->get_rvelocity(tmp_vec1); + compare_real_fields( + this->fs1->get_kspace(), + tmp_vec1, + tmp_vec0, + "velocity", + "1", + stat_group, + this->iteration / niter_stat, + this->max_err_estimate, + this->max_err_relative_estimate); + this->fs1->get_rvorticity(tmp_vec0); + this->fs2->get_rvorticity(tmp_vec1); + compare_real_fields( + this->fs1->get_kspace(), + tmp_vec1, + tmp_vec0, + "vorticity", + "1", + stat_group, + this->iteration / niter_stat, + this->max_err_estimate, + this->max_err_relative_estimate); + + // clumsy delete/new because in the future we may want different + // problem sizes for the different individual DNS. + delete tmp_vec0; + tmp_vec0 = new field<rnumber, FFTW, THREE>( + this->fs4->get_nx(), + this->fs4->get_ny(), + this->fs4->get_nz(), + this->fs4->get_communicator(), + this->fs4->get_fftw_plan_rigor()); + + this->fs4->get_rvorticity(tmp_vec0); + compare_real_fields( + this->fs2->get_kspace(), + tmp_vec0, + tmp_vec1, + "vorticity", + "2", + stat_group, + this->iteration / niter_stat, + this->max_err_estimate, + this->max_err_relative_estimate); + this->fs2->get_rvelocity(tmp_vec1); + this->fs4->get_rvelocity(tmp_vec0); + compare_real_fields( + this->fs2->get_kspace(), + tmp_vec0, + tmp_vec1, + "velocity", + "2", + stat_group, + this->iteration / niter_stat, + this->max_err_estimate, + this->max_err_relative_estimate); + + delete tmp_vec1; + delete tmp_vec0; + + if (this->myrank == 0) + H5Gclose(stat_group); + return EXIT_SUCCESS; +} + +template <typename rnumber> +int fluid_error<rnumber>::read_parameters(void) +{ + TIMEZONE("fluid_error::read_parameters"); + this->direct_numerical_simulation::read_parameters(); + hid_t parameter_file = H5Fopen( + (this->simname + ".h5").c_str(), + H5F_ACC_RDONLY, + H5P_DEFAULT); + this->tfs = type_of_fluid_solver(hdf5_tools::read_value<int>( + parameter_file, "parameters/tfs")); + this->max_err_estimate = hdf5_tools::read_value<double>( + parameter_file, "parameters/max_err_estimate"); + this->max_err_relative_estimate = hdf5_tools::read_value<double>( + parameter_file, "parameters/max_err_relative_estimate"); + H5Fclose(parameter_file); + MPI_Barrier(this->get_communicator()); // "lock" on parameter file + return EXIT_SUCCESS; +} + +template class fluid_error<float>; +template class fluid_error<double>; + diff --git a/examples/field_convergence/fluid_error.hpp b/examples/field_convergence/fluid_error.hpp new file mode 100644 index 0000000000000000000000000000000000000000..af4fd90924b66bf5e63f1589c3019907247f2407 --- /dev/null +++ b/examples/field_convergence/fluid_error.hpp @@ -0,0 +1,67 @@ +/****************************************************************************** +* * +* Copyright 2023 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_ERROR_HPP +#define FLUID_ERROR_HPP + +//#include "fluid_solver.hpp" + +/** \brief Error analysis for fluid solver. + */ + +template <typename rnumber> +class fluid_error: public direct_numerical_simulation +{ + public: + + /* fundamental ensemble entities */ + type_of_fluid_solver tfs; + abstract_fluid_solver<rnumber> * fs1; + abstract_fluid_solver<rnumber> * fs2; + abstract_fluid_solver<rnumber> * fs4; + + double max_err_estimate; + double max_err_relative_estimate; + + fluid_error( + const MPI_Comm COMMUNICATOR, + const std::string &simname): + direct_numerical_simulation( + COMMUNICATOR, + simname) + {} + ~fluid_error(){} + + int initialize(void); + int step(void); + int finalize(void); + + int read_parameters(void); + int write_checkpoint(void); + int do_stats(void); +}; + +#endif//FLUID_ERROR_HPP + diff --git a/examples/field_convergence/fluid_solver.hpp b/examples/field_convergence/fluid_solver.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a963745ef14c50f98ccbeb47ba12daa111028d87 --- /dev/null +++ b/examples/field_convergence/fluid_solver.hpp @@ -0,0 +1,182 @@ +/****************************************************************************** +* * +* Copyright 2023 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_HPP +#define FLUID_SOLVER_HPP + +//#include "abstract_fluid_solver.hpp" +#include "full_code/NSVE.hpp" +#include "full_code/NSE.hpp" + + +template <typename rnumber> +class fluid_solver_NSVE: + public abstract_fluid_solver<rnumber>, + public NSVE<rnumber> +{ + public: + fluid_solver_NSVE(const MPI_Comm COMMUNICATOR, + const std::string &simulation_name) + : abstract_fluid_solver<rnumber>(COMMUNICATOR, simulation_name) + , NSVE<rnumber>(COMMUNICATOR, simulation_name){} + + ~fluid_solver_NSVE() noexcept(false) {} + + int get_cvelocity(field<rnumber, FFTW, THREE> *destination) + { + this->fs->compute_velocity(this->fs->cvorticity); + *destination = *(this->fs->cvelocity); + return EXIT_SUCCESS; + } + int get_cvorticity(field<rnumber, FFTW, THREE> *destination) + { + *destination = *(this->fs->cvorticity); + return EXIT_SUCCESS; + } + int copy_state_from(abstract_fluid_solver<rnumber> &src) + { + src.get_cvorticity(this->fs->cvorticity); + return EXIT_SUCCESS; + } + kspace<FFTW, SMOOTH>* get_kspace() + { + return this->fs->kk; + } + unsigned get_fftw_plan_rigor() const + { + return this->fs->cvorticity->fftw_plan_rigor; + } + + inline MPI_Comm get_communicator() const + { + return this->NSVE<rnumber>::get_communicator(); + } + inline int get_nx() const {return this->NSVE<rnumber>::get_nx();} + inline int get_ny() const {return this->NSVE<rnumber>::get_ny();} + inline int get_nz() const {return this->NSVE<rnumber>::get_nz();} + + inline int write_checkpoint(void) + {return this->NSVE<rnumber>::write_checkpoint();} + inline int initialize(void) + {return this->NSVE<rnumber>::initialize();} + inline int step(void) + {return this->NSVE<rnumber>::step();} + inline int do_stats(void) + {return this->NSVE<rnumber>::do_stats();} + inline int finalize(void) + {return this->NSVE<rnumber>::finalize();} +}; + + +template <typename rnumber> +class fluid_solver_NSE: + public abstract_fluid_solver<rnumber>, + public NSE<rnumber> +{ + public: + fluid_solver_NSE(const MPI_Comm COMMUNICATOR, + const std::string &simulation_name) + : abstract_fluid_solver<rnumber>(COMMUNICATOR, simulation_name) + , NSE<rnumber>(COMMUNICATOR, simulation_name){} + ~fluid_solver_NSE() noexcept(false) {} + + int get_cvelocity(field<rnumber, FFTW, THREE> *destination) + { + *destination = *(this->velocity); + return EXIT_SUCCESS; + } + int get_cvorticity(field<rnumber, FFTW, THREE> *destination) + { + compute_curl(this->kk, + this->velocity, + destination); + return EXIT_SUCCESS; + } + int copy_state_from(abstract_fluid_solver<rnumber> &src) + { + src.get_cvelocity(this->velocity); + return EXIT_SUCCESS; + } + kspace<FFTW, SMOOTH>* get_kspace() + { + return this->kk; + } + unsigned get_fftw_plan_rigor() const + { + return this->velocity->fftw_plan_rigor; + } + + inline MPI_Comm get_communicator() const + { + return this->NSE<rnumber>::get_communicator(); + } + inline int get_nx() const {return this->NSE<rnumber>::get_nx();} + inline int get_ny() const {return this->NSE<rnumber>::get_ny();} + inline int get_nz() const {return this->NSE<rnumber>::get_nz();} + + inline int write_checkpoint(void) + {return this->NSE<rnumber>::write_checkpoint();} + inline int initialize(void) + {return this->NSE<rnumber>::initialize();} + inline int step(void) + {return this->NSE<rnumber>::step();} + inline int do_stats(void) + {return this->NSE<rnumber>::do_stats();} + inline int finalize(void) + {return this->NSE<rnumber>::finalize();} +}; + +enum type_of_fluid_solver { + tfs_NSVE = 0, + tfs_NSE = 1 +}; + +template <typename rnumber> +abstract_fluid_solver<rnumber> * create_fluid_solver( + const type_of_fluid_solver tt, + const MPI_Comm COMMUNICATOR, + const std::string &simulation_name) +{ + switch (tt) + { + case tfs_NSVE: + return new fluid_solver_NSVE<rnumber>( + COMMUNICATOR, + simulation_name); + break; + case tfs_NSE: + return new fluid_solver_NSE<rnumber>( + COMMUNICATOR, + simulation_name); + break; + default: + DEBUG_MSG("wrong type of fluid solver passed to create_fluid_solver.\n"); + assert(false); + break; + } +} + +#endif//FLUID_SOLVER_HPP +