From 96ec4dc2567b51d3bd457bec553893bf755fb10b Mon Sep 17 00:00:00 2001
From: Chichi Lalescu <chichilalescu@gmail.com>
Date: Thu, 4 May 2017 22:46:25 +0200
Subject: [PATCH] add DNS option to executable script

---
 bfps/DNS.py       | 185 +++++++++++++++++++++++++++++++++++-----------
 bfps/__main__.py  |  12 ++-
 tests/test_DNS.py |  13 ++--
 3 files changed, 159 insertions(+), 51 deletions(-)

diff --git a/bfps/DNS.py b/bfps/DNS.py
index b90c6d46..0d8571b0 100644
--- a/bfps/DNS.py
+++ b/bfps/DNS.py
@@ -45,12 +45,22 @@ class DNS(_code):
     def __init__(
             self,
             work_dir = './',
-            simname = 'test',
-            dns_type = 'NSVE',
-            fluid_dtype = 'single'):
-        _code.__init__(self, work_dir = work_dir, simname = simname)
-        self.dns_type = dns_type
-        self.name = self.dns_type + '-v' + bfps.__version__
+            simname = 'test'):
+        _code.__init__(
+                self,
+                work_dir = work_dir,
+                simname = simname)
+        self.host_info = {'type'        : 'cluster',
+                          'environment' : None,
+                          'deltanprocs' : 1,
+                          'queue'       : '',
+                          'mail_address': '',
+                          'mail_events' : None}
+        self.generate_default_parameters()
+        return None
+    def set_precision(
+            self,
+            fluid_dtype):
         if fluid_dtype in [np.float32, np.float64]:
             self.fluid_dtype = fluid_dtype
         elif fluid_dtype in ['single', 'double']:
@@ -62,9 +72,13 @@ class DNS(_code):
         if self.rtype == np.float32:
             self.ctype = np.dtype(np.complex64)
             self.C_field_dtype = 'float'
+            self.fluid_precision = 'single'
         elif self.rtype == np.float64:
             self.ctype = np.dtype(np.complex128)
             self.C_field_dtype = 'double'
+            self.fluid_precision = 'double'
+        return None
+    def write_src(self):
         self.version_message = (
                 '/***********************************************************************\n' +
                 '* this code automatically generated by bfps\n' +
@@ -94,13 +108,22 @@ class DNS(_code):
                 return main_code< {0} >(argc, argv, fpe);
             }}
             """.format(self.dns_type + '<{0}>'.format(self.C_field_dtype))
-        self.host_info = {'type'        : 'cluster',
-                          'environment' : None,
-                          'deltanprocs' : 1,
-                          'queue'       : '',
-                          'mail_address': '',
-                          'mail_events' : None}
-        self.generate_default_parameters()
+        self.includes = '\n'.join(
+                ['#include ' + hh
+                 for hh in self.include_list])
+        with open(self.name + '.cpp', 'w') as outfile:
+            outfile.write(self.version_message + '\n\n')
+            outfile.write(self.includes + '\n\n')
+            outfile.write(self.cread_pars(
+                template_class = '{0}<rnumber>::'.format(self.dns_type),
+                template_prefix = 'template <typename rnumber> ',
+                simname_variable = 'simname.c_str()') + '\n\n')
+            for rnumber in ['float', 'double']:
+                outfile.write(self.cread_pars(
+                    template_class = '{0}<{1}>::'.format(self.dns_type, rnumber),
+                    template_prefix = 'template '.format(rnumber),
+                    just_declaration = True) + '\n\n')
+            outfile.write(self.main + '\n')
         return None
     def generate_default_parameters(self):
         # these parameters are relevant for all DNS classes
@@ -123,30 +146,12 @@ class DNS(_code):
         self.parameters['max_velocity_estimate'] = float(1)
         self.parameters['max_vorticity_estimate'] = float(1)
         # parameters specific to particle version
-        if self.dns_type == 'NSVEp':
-            self.parameters['niter_part'] = int(1)
-            self.parameters['nparticles'] = int(10)
-            self.parameters['tracers0_integration_steps'] = int(4)
-            self.parameters['tracers0_neighbours'] = int(1)
-            self.parameters['tracers0_smoothness'] = int(1)
-        return None
-    def write_src(self):
-        self.includes = '\n'.join(
-                ['#include ' + hh
-                 for hh in self.include_list])
-        with open(self.name + '.cpp', 'w') as outfile:
-            outfile.write(self.version_message + '\n\n')
-            outfile.write(self.includes + '\n\n')
-            outfile.write(self.cread_pars(
-                template_class = '{0}<rnumber>::'.format(self.dns_type),
-                template_prefix = 'template <typename rnumber> ',
-                simname_variable = 'simname.c_str()') + '\n\n')
-            for rnumber in ['float', 'double']:
-                outfile.write(self.cread_pars(
-                    template_class = '{0}<{1}>::'.format(self.dns_type, rnumber),
-                    template_prefix = 'template '.format(rnumber),
-                    just_declaration = True) + '\n\n')
-            outfile.write(self.main + '\n')
+        self.NSVEp_extra_parameters = {}
+        self.NSVEp_extra_parameters['niter_part'] = int(1)
+        self.NSVEp_extra_parameters['nparticles'] = int(10)
+        self.NSVEp_extra_parameters['tracers0_integration_steps'] = int(4)
+        self.NSVEp_extra_parameters['tracers0_neighbours'] = int(1)
+        self.NSVEp_extra_parameters['tracers0_smoothness'] = int(1)
         return None
     def get_kspace(self):
         kspace = {}
@@ -376,7 +381,6 @@ class DNS(_code):
             for k in vec_stat_datasets:
                 time_chunk = 2**20//(8*3*3*nshells)
                 time_chunk = max(time_chunk, 1)
-                print(ofile)
                 ofile.create_dataset('statistics/spectra/' + k + '_' + k,
                                      (1, nshells, 3, 3),
                                      chunks = (time_chunk, nshells, 3, 3),
@@ -434,10 +438,71 @@ class DNS(_code):
                         (3,)),
                     dtype = np.float)
         return None
-    def specific_parser_arguments(
+    def job_parser_arguments(
             self,
             parser):
-        _code.specific_parser_arguments(self, parser)
+        parser.add_argument(
+                '--ncpu',
+                type = int,
+                dest = 'ncpu',
+                default = -1)
+        parser.add_argument(
+                '--np', '--nprocesses',
+                metavar = 'NPROCESSES',
+                help = 'number of mpi processes to use',
+                type = int,
+                dest = 'nb_processes',
+                default = 4)
+        parser.add_argument(
+                '--ntpp', '--nthreads-per-process',
+                type = int,
+                dest = 'nb_threads_per_process',
+                metavar = 'NTHREADS_PER_PROCESS',
+                help = 'number of threads to use per MPI process',
+                default = 1)
+        parser.add_argument(
+                '--no-submit',
+                action = 'store_true',
+                dest = 'no_submit')
+        parser.add_argument(
+                '--environment',
+                type = str,
+                dest = 'environment',
+                default = None)
+        parser.add_argument(
+                '--minutes',
+                type = int,
+                dest = 'minutes',
+                default = 5,
+                help = 'If environment supports it, this is the requested wall-clock-limit.')
+        parser.add_argument(
+               '--njobs',
+               type = int, dest = 'njobs',
+               default = 1)
+        return None
+    def simulation_parser_arguments(
+            self,
+            parser):
+        parser.add_argument(
+                '--simname',
+                type = str, dest = 'simname',
+                default = 'test')
+        parser.add_argument(
+               '-n', '--cube-size',
+               type = int,
+               dest = 'n',
+               default = 32,
+               metavar = 'N',
+               help = 'code is run by default in a grid of NxNxN')
+        parser.add_argument(
+                '--wd',
+                type = str, dest = 'work_dir',
+                default = './')
+        parser.add_argument(
+                '--precision',
+                choices = ['single', 'double'],
+                type = str,
+                default = 'single')
         parser.add_argument(
                 '--src-wd',
                 type = str,
@@ -453,10 +518,6 @@ class DNS(_code):
                 type = int,
                 dest = 'src_iteration',
                 default = 0)
-        parser.add_argument(
-               '--njobs',
-               type = int, dest = 'njobs',
-               default = 1)
         parser.add_argument(
                '--kMeta',
                type = float,
@@ -468,6 +529,10 @@ class DNS(_code):
                dest = 'dtfactor',
                default = 0.5,
                help = 'dt is computed as DTFACTOR / N')
+        return None
+    def particle_parser_arguments(
+            self,
+            parser):
         parser.add_argument(
                '--particle-rand-seed',
                type = int,
@@ -503,6 +568,31 @@ class DNS(_code):
                 dest = 'smoothness',
                 default = 1)
         return None
+    def add_parser_arguments(
+            self,
+            parser):
+        subparsers = parser.add_subparsers(
+                dest = 'DNS_class',
+                help = 'type of simulation to run')
+        subparsers.required = True
+        parser_NSVE = subparsers.add_parser(
+                'NSVE',
+                help = 'plain Navier-Stokes vorticity formulation')
+        self.simulation_parser_arguments(parser_NSVE)
+        self.job_parser_arguments(parser_NSVE)
+        self.parameters_to_parser_arguments(parser_NSVE)
+
+        parser_NSVEp = subparsers.add_parser(
+                'NSVEp',
+                help = 'plain Navier-Stokes vorticity formulation, with basic fluid tracers')
+        self.simulation_parser_arguments(parser_NSVEp)
+        self.job_parser_arguments(parser_NSVEp)
+        self.particle_parser_arguments(parser_NSVEp)
+        self.parameters_to_parser_arguments(parser_NSVEp)
+        self.parameters_to_parser_arguments(
+                parser_NSVEp,
+                self.NSVEp_extra_parameters)
+        return None
     def prepare_launch(
             self,
             args = []):
@@ -532,6 +622,13 @@ class DNS(_code):
 
         """
         opt = _code.prepare_launch(self, args = args)
+        self.set_precision(opt.precision)
+        self.dns_type = opt.DNS_class
+        self.name = self.dns_type + '-' + self.fluid_precision + '-v' + bfps.__version__
+        # merge parameters if needed
+        if self.dns_type == 'NSVEp':
+            for k in self.NSVEp_extra_parameters.keys():
+                self.parameters[k] = self.NSVEp_extra_parameters[k]
         self.parameters['nu'] = (opt.kMeta * 2 / opt.n)**(4./3)
         self.parameters['dt'] = (opt.dtfactor / opt.n)
         # custom famplitude for 288 and 576
diff --git a/bfps/__main__.py b/bfps/__main__.py
index 9db5e350..f1ee7278 100644
--- a/bfps/__main__.py
+++ b/bfps/__main__.py
@@ -28,6 +28,7 @@ import sys
 import argparse
 
 import bfps
+from .DNS import DNS
 from .NavierStokes import NavierStokes
 from .NSVorticityEquation import NSVorticityEquation
 from .FluidResize import FluidResize
@@ -64,13 +65,22 @@ def main():
                'NSManyParticles-double']
     parser.add_argument(
             'base_class',
-            choices = NSoptions + NSVEoptions + FRoptions + FCoptions + NSMPopt,
+            choices = ['DNS'] +
+                      NSoptions +
+                      NSVEoptions +
+                      FRoptions +
+                      FCoptions +
+                      NSMPopt,
             type = str)
     # first option is the choice of base class or -h or -v
     # all other options are passed on to the base_class instance
     opt = parser.parse_args(sys.argv[1:2])
     # error is thrown if first option is not a base class, so launch
     # cannot be executed by mistake.
+    if opt.base_class == 'DNS':
+        c = DNS()
+        c.launch(args = sys.argv[2:])
+        return None
     if 'double' in opt.base_class:
         precision = 'double'
     else:
diff --git a/tests/test_DNS.py b/tests/test_DNS.py
index 4e8cb2f8..9169cef9 100644
--- a/tests/test_DNS.py
+++ b/tests/test_DNS.py
@@ -2,12 +2,13 @@ from bfps.DNS import DNS
 
 
 def main():
-    niterations = 16
-    nparticles = 100
-    c = DNS(dns_type = 'NSVEp')
+    niterations = 32
+    nparticles = 10000
+    c = DNS()
     c.launch(
-            ['-n', '32',
-             '--simname', 'vorticity_equation',
+            ['NSVEp',
+             '-n', '32',
+             '--simname', 'dns_nsvep',
              '--np', '4',
              '--ntpp', '1',
              '--niter_todo', '{0}'.format(niterations),
@@ -16,7 +17,7 @@ def main():
              '--checkpoints_per_file', '{0}'.format(3),
              '--nparticles', '{0}'.format(nparticles),
              '--particle-rand-seed', '2',
-             #'--njobs', '2',
+             '--njobs', '2',
              '--wd', './'])
     return None
 
-- 
GitLab