diff --git a/TurTLE/DNS.py b/TurTLE/DNS.py index b04c8b360307632e759eddbe81a03d555473ba96..606014b09a87b490f1dda781fc6bc6e501d12eb8 100644 --- a/TurTLE/DNS.py +++ b/TurTLE/DNS.py @@ -93,7 +93,7 @@ class DNS(_code): return None def generate_default_parameters(self): # these parameters are relevant for all DNS classes - self.parameters['fftw_plan_rigor'] = 'FFTW_ESTIMATE' + self.parameters['fftw_plan_rigor'] = str('FFTW_ESTIMATE') self.parameter_description['fftw_plan_rigor'] = 'FFTW plan rigor to use. One of `FFTW_ESTIMATE`, `FFTW_MEASURE`, `FFTW_PATIENT`. Please see FFTW documentation.' self.parameters['dealias_type'] = int(1) self.parameter_description['dealias_type'] = 'Dealiasing mehtod to use, integer. Options are: two-thirds (0) or smooth (1).' @@ -125,7 +125,7 @@ class DNS(_code): self.parameter_description['fk0'] = 'Forcing parameter: if forcing acts on wavenumber band, this is the smallest wavenumber where it acts (in code units).' self.parameters['fk1'] = float(4.0) self.parameter_description['fk1'] = 'Forcing parameter: if forcing acts on wavenumber band, this is the largest wavenumber where it acts (in code units).' - self.parameters['forcing_type'] = 'fixed_energy_injection_rate' + self.parameters['forcing_type'] = str('fixed_energy_injection_rate') self.parameter_description['forcing_type'] = 'Forcing parameter: what type of force to use.' self.parameters['histogram_bins'] = int(256) self.parameter_description['histogram_bins'] = 'During statistics, histograms of real-valued fields are computed using a number of `HISTOGRAM_BINS` bins.' @@ -324,22 +324,39 @@ class DNS(_code): '--src-wd', type = str, dest = 'src_work_dir', + help = ('Empty by default.' + + ' Location of simulation from which to source initial conditions.' + + ' See also `--src-simname`.'), default = '') parser.add_argument( '--src-simname', type = str, dest = 'src_simname', + help = ('Empty by default.' + + ' Name of simulation from which to source initial conditions.' + + ' If the parameter file of the source simulation is present, ' + + 'the new DNS will use the same physical parameters ' + + '(forcing parameters and viscosity) and the same dealiasing option.' + + ' Any other parameters (e.g. "nx") have to be specified separately.' + + ' See also `--overwrite-src-parameters`.'), default = '') parser.add_argument( '--src-iteration', type = int, dest = 'src_iteration', + help = ('0 by default.' + + ' Iteration of source simulation to be used as initial condition.' + + ' See also `--src-simname`.'), default = 0) parser.add_argument( '--overwrite-src-parameters', action = 'store_true', dest = 'overwrite_src_parameters', - help = 'False by default. If using a source simulation, do we keep its physical parameters? Note: if this option is switched on, default values are used for parameters that are not set explicitly by the command --- NOT the values of the source simulation.') + help = ('False by default.' + + ' If using a source simulation, do we keep its physical parameters?' + + ' Note: if this option is switched on, default values are used for ' + + 'parameters that are not set explicitly by the command --- ' + + 'NOT the values of the source simulation.')) parser.add_argument( '--kMeta', type = float, @@ -472,7 +489,23 @@ class DNS(_code): self, args = [], extra_parameters = None): - """Set up reasonable parameters. + """Assign DNS parameters. + + There are three ways to specify parameters: + + * Directly via the command line. + * If the simulation takes its initial condition from another simulation + (via the :code:`--src-dir` and :code:`--src-simname` parameters) and + some physical parameter is not provided on the command line, then this + method will attempt to read that parameter from the parameter file of + the source simulation. + * If a parameter is not specified via the command line, and a source + simulation is not provided, or the source parameter file is missing, or + the :code:`--overwrite-src-parameters` option is turned on, this method + will generate a "reasonable" value for that parameter. + "Reasonable" refers to either some fixed values for the forcing + parameters, or an empirical extrapolation of the viscosity to satisfy a + given value of the :code:`--kMeta` command line parameter. With the default Lundgren forcing applied in the band [2, 4], we can estimate the dissipation, therefore we can estimate @@ -520,10 +553,32 @@ class DNS(_code): for k in self.extra_parameters[self.dns_type].keys(): self.parameters[k] = self.extra_parameters[self.dns_type][k] + # for parameters there are three options, in order of preference: + # * value given from command line + # * value used by source simulation + # * default value + if ((self.parameters['niter_todo'] % self.parameters['niter_out']) != 0): self.parameters['niter_out'] = self.parameters['niter_todo'] - if len(opt.src_work_dir) == 0: - opt.src_work_dir = os.path.realpath(opt.work_dir) + + if len(opt.src_simname) > 0: + if len(opt.src_work_dir) == 0: + opt.src_work_dir = os.path.realpath(opt.work_dir) + src_file_name = os.path.join( + os.path.realpath(opt.src_work_dir), + opt.src_simname + '.h5') + if os.path.exists(src_file_name): + # open source file for use later + src_file = h5py.File(src_file_name, 'r') + else: + print('TurTLE WARNING: unable to copy parameters of source simulation.' + + 'File {0} is missing.\n'.format(src_file_name) + + 'DNS will use default values for physical parameters.\n') + src_file = None + else: + src_file = None + if opt.overwrite_src_parameters: + src_file = None if type(opt.dkx) == type(None): opt.dkx = 2. / opt.Lx if type(opt.dky) == type(None): @@ -536,16 +591,60 @@ class DNS(_code): opt.ny = opt.n if type(opt.nz) == type(None): opt.nz = opt.n + if type(opt.friction_coefficient) == type(None): + if type(src_file) != type(None): + self.parameters['friction_coefficient'] = type(self.parameters['friction_coefficient'])( + src_file['parameters/friction_coefficient'][()]) + else: + opt.friction_coefficient = self.parameters['friction_coefficient'] + if type(opt.fmode) == type(None): + if type(src_file) != type(None): + self.parameters['fmode'] = type(self.parameters['fmode'])( + src_file['parameters/fmode'][()]) + else: + opt.fmode = self.parameters['fmode'] + if type(opt.famplitude) == type(None): + if type(src_file) != type(None): + self.parameters['famplitude'] = type(self.parameters['famplitude'])( + src_file['parameters/famplitude'][()]) + else: + opt.famplitude = self.parameters['famplitude'] + if type(opt.energy) == type(None): + if type(src_file) != type(None): + self.parameters['energy'] = type(self.parameters['energy'])( + src_file['parameters/energy'][()]) + else: + opt.energy = self.parameters['energy'] if type(opt.fk0) == type(None): - opt.fk0 = self.parameters['fk0'] + if type(src_file) != type(None): + self.parameters['fk0'] = type(self.parameters['fk0'])( + src_file['parameters/fk0'][()]) + else: + opt.fk0 = self.parameters['fk0'] if type(opt.fk1) == type(None): - opt.fk1 = self.parameters['fk1'] + if type(src_file) != type(None): + self.parameters['fk1'] = type(self.parameters['fk1'])( + src_file['parameters/fk1'][()]) + else: + opt.fk1 = self.parameters['fk1'] if type(opt.forcing_type) == type(None): - opt.forcing_type = self.parameters['forcing_type'] + if type(src_file) != type(None): + self.parameters['forcing_type'] = str( + src_file['parameters/forcing_type'][()].decode()) + else: + opt.forcing_type = self.parameters['forcing_type'] if type(opt.injection_rate) == type(None): - opt.injection_rate = self.parameters['injection_rate'] + if type(src_file) != type(None): + self.parameters['injection_rate'] = type(self.parameters['injection_rate'])( + src_file['parameters/injection_rate'][()]) + else: + opt.injection_rate = self.parameters['injection_rate'] if type(opt.dealias_type) == type(None): - opt.dealias_type = self.parameters['dealias_type'] + if type(src_file) != type(None): + self.parameters['dealias_type'] = type(self.parameters['dealias_type'])( + src_file['parameters/dealias_type'][()]) + else: + opt.dealias_type = self.parameters['dealias_type'] if (opt.nx > opt.n or opt.ny > opt.n or opt.nz > opt.n): @@ -561,34 +660,33 @@ class DNS(_code): kM = opt.n * 0.5 if opt.dealias_type == 1: kM *= 0.8 - # tweak forcing/viscosity based on forcing type - if opt.forcing_type == 'linear': - # custom famplitude for 288 and 576 - if opt.n == 288: - self.parameters['famplitude'] = 0.45 - elif opt.n == 576: - self.parameters['famplitude'] = 0.47 - elif opt.forcing_type == 'fixed_energy_injection_rate': - # use the fact that mean dissipation rate is equal to injection rate - if type(opt.nu) == type(None): - opt.nu = ( - opt.injection_rate * - (opt.kMeta / kM)**4)**(1./3) - if opt.injection_rate == 0: - opt.nu = ( - (opt.kMeta / kM)**(4./3) * - (np.pi / 1.5)**(1./3) * - (2*self.parameters['energy'] / 3)**0.5) - elif opt.forcing_type == 'fixed_energy': - kf = 1. / (1./opt.fk0 + - 1./opt.fk1) - if type(opt.nu) == type(None): - opt.nu = ( - (opt.kMeta / kM)**(4./3) * - (np.pi / kf)**(1./3) * - (2*self.parameters['energy'] / 3)**0.5) + if type(opt.nu) == type(None): - opt.nu = (opt.kMeta * 2 / opt.n)**(4./3) + # check if we are using viscosity of source simulation + if type(src_file) != type(None): + opt.nu = type(self.parameters['nu'])( + src_file['parameters/nu'][()]) + # empirical viscosity based on forcing type + else: + if opt.forcing_type == 'fixed_energy_injection_rate': + # use the fact that mean dissipation rate is equal to injection rate + opt.nu = ( + opt.injection_rate * + (opt.kMeta / kM)**4)**(1./3) + if opt.injection_rate == 0: + opt.nu = ( + (opt.kMeta / kM)**(4./3) * + (np.pi / 1.5)**(1./3) * + (2*self.parameters['energy'] / 3)**0.5) + elif opt.forcing_type == 'fixed_energy': + kf = 1. / (1./opt.fk0 + + 1./opt.fk1) + opt.nu = ( + (opt.kMeta / kM)**(4./3) * + (np.pi / kf)**(1./3) * + (2*self.parameters['energy'] / 3)**0.5) + else: + opt.nu = (opt.kMeta * 2 / opt.n)**(4./3) self.parameters['nu'] = opt.nu if type(opt.checkpoints_per_file) == type(None): # hardcoded FFTW complex representation size @@ -898,35 +996,6 @@ class DNS(_code): checkpoint_field + '/complex/{0}'.format(opt.src_iteration), f, checkpoint_field + '/complex/{0}'.format(0)) - if not opt.overwrite_src_parameters: - print('DNS will use physical parameters from source simulation.\n' - 'This means `dealias_type`, `energy`, `famplitude`, `fk0`, `fk1`, `fmode`, `friction_coefficient`, `injection_rate`, `nu` and `forcing_type`.') - # timestep left out on purpose - src_file = os.path.join( - os.path.realpath(opt.src_work_dir), - opt.src_simname + '.h5') - if os.path.exists(src_file): - f0 = h5py.File(src_file, 'r') - for kk in ['dealias_type', - 'energy', - 'famplitude', - 'fk0', - 'fk1', - 'fmode', - 'friction_coefficient', - 'injection_rate', - 'nu']: - if (kk in f0['parameters'].keys()) and (kk in self.parameters.keys()): - self.parameters[kk] = type(self.parameters[kk])(f0['parameters/' + kk][()]) - self.parameters['forcing_type'] = str(f0['parameters/forcing_type'][()], 'ascii') - f0.close() - else: - print('TurTLE WARNING: unable to copy parameters of source simulation.' - + 'File {0} is missing.\n'.format(src_file) - + 'DNS will use default values for physical parameters.\n') - else: - print('TurTLE WARNING: DNS will use default values for physical parameters.\n' - 'This means `dealias_type`, `energy`, `famplitude`, `fk0`, `fk1`, `fmode`, `friction_coefficient`, `injection_rate`, `nu` and `forcing_type` may differ from values used by the source DNS.') else: if self.dns_type == 'NSVE_Stokes_particles': data = self.generate_vector_field(