def lib(self, compiler=GNUCompiler()): if self._lib is None: with open(self.src_file, 'w') as f: f.write(self.ccode) compiler.compile(self.src_file, self.lib_file, self.log_file) print("Compiled %s ==> %s" % ("random", self.lib_file)) self._lib = npct.load_library(self.lib_file, '.') return self._lib
def execute(self, pyfunc=AdvectionRK4, endtime=None, runtime=None, dt=1., moviedt=None, recovery=None, output_file=None, movie_background_field=None, verbose_progress=None): """Execute a given kernel function over the particle set for multiple timesteps. Optionally also provide sub-timestepping for particle output. :param pyfunc: Kernel function to execute. This can be the name of a defined Python function or a :class:`parcels.kernel.Kernel` object. Kernels can be concatenated using the + operator :param endtime: End time for the timestepping loop. It is either a datetime object or a positive double. :param runtime: Length of the timestepping loop. Use instead of endtime. It is either a timedelta object or a positive double. :param dt: Timestep interval to be passed to the kernel. It is either a timedelta object or a double. Use a negative value for a backward-in-time simulation. :param moviedt: Interval for inner sub-timestepping (leap), which dictates the update frequency of animation. It is either a timedelta object or a positive double. None value means no animation. :param output_file: :mod:`parcels.particlefile.ParticleFile` object for particle output :param recovery: Dictionary with additional `:mod:parcels.tools.error` recovery kernels to allow custom recovery behaviour in case of kernel errors. :param movie_background_field: field plotted as background in the movie if moviedt is set. 'vector' shows the velocity as a vector field. :param verbose_progress: Boolean for providing a progress bar for the kernel execution loop. """ # check if pyfunc has changed since last compile. If so, recompile if self.kernel is None or (self.kernel.pyfunc is not pyfunc and self.kernel is not pyfunc): # Generate and store Kernel if isinstance(pyfunc, Kernel): self.kernel = pyfunc else: self.kernel = self.Kernel(pyfunc) # Prepare JIT kernel execution if self.ptype.uses_jit: self.kernel.remove_lib() self.kernel.compile(compiler=GNUCompiler()) self.kernel.load_lib() # Convert all time variables to seconds if isinstance(endtime, delta): raise RuntimeError('endtime must be either a datetime or a double') if isinstance(endtime, datetime): endtime = np.datetime64(endtime) if isinstance(endtime, np.datetime64): if self.time_origin.calender is None: raise NotImplementedError( 'If fieldset.time_origin is not a date, execution endtime must be a double' ) endtime = self.time_origin.reltime(endtime) if isinstance(runtime, delta): runtime = runtime.total_seconds() if isinstance(dt, delta): dt = dt.total_seconds() outputdt = output_file.outputdt if output_file else np.infty if isinstance(outputdt, delta): outputdt = outputdt.total_seconds() if isinstance(moviedt, delta): moviedt = moviedt.total_seconds() assert runtime is None or runtime >= 0, 'runtime must be positive' assert outputdt is None or outputdt >= 0, 'outputdt must be positive' assert moviedt is None or moviedt >= 0, 'moviedt must be positive' # Set particle.time defaults based on sign of dt, if not set at ParticleSet construction for p in self: if np.isnan(p.time): mintime, maxtime = self.fieldset.gridset.dimrange('time_full') p.time = mintime if dt >= 0 else maxtime # Derive _starttime and endtime from arguments or fieldset defaults if runtime is not None and endtime is not None: raise RuntimeError( 'Only one of (endtime, runtime) can be specified') _starttime = min([p.time for p in self]) if dt >= 0 else max( [p.time for p in self]) if self.repeatdt is not None and self.repeat_starttime is None: self.repeat_starttime = _starttime if runtime is not None: endtime = _starttime + runtime * np.sign(dt) elif endtime is None: mintime, maxtime = self.fieldset.gridset.dimrange('time_full') endtime = maxtime if dt >= 0 else mintime if abs(endtime - _starttime) < 1e-5 or dt == 0 or runtime == 0: dt = 0 runtime = 0 endtime = _starttime logger.warning_once( "dt or runtime are zero, or endtime is equal to Particle.time. " "The kernels will be executed once, without incrementing time") # Initialise particle timestepping for p in self: p.dt = dt # First write output_file, because particles could have been added if output_file: output_file.write(self, _starttime) if moviedt: self.show(field=movie_background_field, show_time=_starttime, animation=True) if moviedt is None: moviedt = np.infty time = _starttime if self.repeatdt: next_prelease = self.repeat_starttime + ( abs(time - self.repeat_starttime) // self.repeatdt + 1) * self.repeatdt * np.sign(dt) else: next_prelease = np.infty if dt > 0 else -np.infty next_output = time + outputdt if dt > 0 else time - outputdt next_movie = time + moviedt if dt > 0 else time - moviedt next_input = self.fieldset.computeTimeChunk(time, np.sign(dt)) tol = 1e-12 if verbose_progress is None: walltime_start = time_module.time() if verbose_progress: try: pbar = progressbar.ProgressBar( max_value=abs(endtime - _starttime)).start() except: # for old versions of progressbar pbar = progressbar.ProgressBar( maxvalue=abs(endtime - _starttime)).start() while (time < endtime and dt > 0) or (time > endtime and dt < 0) or dt == 0: if verbose_progress is None and time_module.time( ) - walltime_start > 10: # Showing progressbar if runtime > 10 seconds pbar = progressbar.ProgressBar( max_value=abs(endtime - _starttime)).start() verbose_progress = True if dt > 0: time = min(next_prelease, next_input, next_output, next_movie, endtime) else: time = max(next_prelease, next_input, next_output, next_movie, endtime) self.kernel.execute(self, endtime=time, dt=dt, recovery=recovery, output_file=output_file) if abs(time - next_prelease) < tol: pset_new = ParticleSet(fieldset=self.fieldset, time=time, lon=self.repeatlon, lat=self.repeatlat, depth=self.repeatdepth, pclass=self.repeatpclass) for p in pset_new: p.dt = dt self.add(pset_new) next_prelease += self.repeatdt * np.sign(dt) if abs(time - next_output) < tol: if output_file: output_file.write(self, time) next_output += outputdt * np.sign(dt) if abs(time - next_movie) < tol: self.show(field=movie_background_field, show_time=time, animation=True) next_movie += moviedt * np.sign(dt) next_input = self.fieldset.computeTimeChunk(time, dt) if dt == 0: break if verbose_progress: pbar.update(abs(time - _starttime)) if output_file: output_file.write(self, time) if verbose_progress: pbar.finish()
def execute(self, pyfunc=AdvectionRK4, endtime=None, runtime=None, dt=1., moviedt=None, recovery=None, output_file=None, movie_background_field=None, verbose_progress=None, postIterationCallbacks=None, callbackdt=None): """Execute a given kernel function over the particle set for multiple timesteps. Optionally also provide sub-timestepping for particle output. :param pyfunc: Kernel function to execute. This can be the name of a defined Python function or a :class:`parcels.kernel.Kernel` object. Kernels can be concatenated using the + operator :param endtime: End time for the timestepping loop. It is either a datetime object or a positive double. :param runtime: Length of the timestepping loop. Use instead of endtime. It is either a timedelta object or a positive double. :param dt: Timestep interval to be passed to the kernel. It is either a timedelta object or a double. Use a negative value for a backward-in-time simulation. :param moviedt: Interval for inner sub-timestepping (leap), which dictates the update frequency of animation. It is either a timedelta object or a positive double. None value means no animation. :param output_file: :mod:`parcels.particlefile.ParticleFile` object for particle output :param recovery: Dictionary with additional `:mod:parcels.tools.error` recovery kernels to allow custom recovery behaviour in case of kernel errors. :param movie_background_field: field plotted as background in the movie if moviedt is set. 'vector' shows the velocity as a vector field. :param verbose_progress: Boolean for providing a progress bar for the kernel execution loop. :param postIterationCallbacks: (Optional) Array of functions that are to be called after each iteration (post-process, non-Kernel) :param callbackdt: (Optional, in conjecture with 'postIterationCallbacks) timestep inverval to (latestly) interrupt the running kernel and invoke post-iteration callbacks from 'postIterationCallbacks' """ # check if pyfunc has changed since last compile. If so, recompile if self.kernel is None or (self.kernel.pyfunc is not pyfunc and self.kernel is not pyfunc): # Generate and store Kernel if isinstance(pyfunc, Kernel): self.kernel = pyfunc else: self.kernel = self.Kernel(pyfunc) # Prepare JIT kernel execution if self.ptype.uses_jit: self.kernel.remove_lib() cppargs = ['-DDOUBLE_COORD_VARIABLES' ] if self.lonlatdepth_dtype == np.float64 else None self.kernel.compile(compiler=GNUCompiler(cppargs=cppargs)) self.kernel.load_lib() # Convert all time variables to seconds if isinstance(endtime, delta): raise RuntimeError('endtime must be either a datetime or a double') if isinstance(endtime, datetime): endtime = np.datetime64(endtime) if isinstance(endtime, np.datetime64): if self.time_origin.calendar is None: raise NotImplementedError( 'If fieldset.time_origin is not a date, execution endtime must be a double' ) endtime = self.time_origin.reltime(endtime) if isinstance(runtime, delta): runtime = runtime.total_seconds() if isinstance(dt, delta): dt = dt.total_seconds() outputdt = output_file.outputdt if output_file else np.infty if isinstance(outputdt, delta): outputdt = outputdt.total_seconds() if isinstance(moviedt, delta): moviedt = moviedt.total_seconds() if isinstance(callbackdt, delta): callbackdt = callbackdt.total_seconds() assert runtime is None or runtime >= 0, 'runtime must be positive' assert outputdt is None or outputdt >= 0, 'outputdt must be positive' assert moviedt is None or moviedt >= 0, 'moviedt must be positive' mintime, maxtime = self.fieldset.gridset.dimrange( 'time_full') if self.fieldset is not None else (0, 1) if np.any(np.isnan(self.particle_data['time'])): self.particle_data['time'][np.isnan( self.particle_data['time'])] = mintime if dt >= 0 else maxtime # Derive _starttime and endtime from arguments or fieldset defaults if runtime is not None and endtime is not None: raise RuntimeError( 'Only one of (endtime, runtime) can be specified') _starttime = self.particle_data['time'].min( ) if dt >= 0 else self.particle_data['time'].max() if self.repeatdt is not None and self.repeat_starttime is None: self.repeat_starttime = _starttime if runtime is not None: endtime = _starttime + runtime * np.sign(dt) elif endtime is None: mintime, maxtime = self.fieldset.gridset.dimrange('time_full') endtime = maxtime if dt >= 0 else mintime execute_once = False if abs(endtime - _starttime) < 1e-5 or dt == 0 or runtime == 0: dt = 0 runtime = 0 endtime = _starttime logger.warning_once( "dt or runtime are zero, or endtime is equal to Particle.time. " "The kernels will be executed once, without incrementing time") execute_once = True self.particle_data['dt'][:] = dt # First write output_file, because particles could have been added if output_file: output_file.write(self, _starttime) if moviedt: self.show(field=movie_background_field, show_time=_starttime, animation=True) if moviedt is None: moviedt = np.infty if callbackdt is None: interupt_dts = [np.infty, moviedt, outputdt] if self.repeatdt is not None: interupt_dts.append(self.repeatdt) callbackdt = np.min(np.array(interupt_dts)) time = _starttime if self.repeatdt: next_prelease = self.repeat_starttime + ( abs(time - self.repeat_starttime) // self.repeatdt + 1) * self.repeatdt * np.sign(dt) else: next_prelease = np.infty if dt > 0 else -np.infty next_output = time + outputdt if dt > 0 else time - outputdt next_movie = time + moviedt if dt > 0 else time - moviedt next_callback = time + callbackdt if dt > 0 else time - callbackdt next_input = self.fieldset.computeTimeChunk( time, np.sign(dt)) if self.fieldset is not None else np.inf tol = 1e-12 if verbose_progress is None: walltime_start = time_module.time() if verbose_progress: pbar = self.__create_progressbar(_starttime, endtime) while (time < endtime and dt > 0) or (time > endtime and dt < 0) or dt == 0: if verbose_progress is None and time_module.time( ) - walltime_start > 10: # Showing progressbar if runtime > 10 seconds if output_file: logger.info('Temporary output files are stored in %s.' % output_file.tempwritedir_base) logger.info( 'You can use "parcels_convert_npydir_to_netcdf %s" to convert these ' 'to a NetCDF file during the run.' % output_file.tempwritedir_base) pbar = self.__create_progressbar(_starttime, endtime) verbose_progress = True if dt > 0: time = min(next_prelease, next_input, next_output, next_movie, next_callback, endtime) else: time = max(next_prelease, next_input, next_output, next_movie, next_callback, endtime) self.kernel.execute(self, endtime=time, dt=dt, recovery=recovery, output_file=output_file, execute_once=execute_once) if abs(time - next_prelease) < tol: pset_new = ParticleSet( fieldset=self.fieldset, time=time, lon=self.repeatlon, lat=self.repeatlat, depth=self.repeatdepth, pclass=self.repeatpclass, lonlatdepth_dtype=self.lonlatdepth_dtype, partitions=False, pid_orig=self.repeatpid, **self.repeatkwargs) p = pset_new.data_accessor() for i in range(pset_new.size): p.set_index(i) p.dt = dt self.add(pset_new) next_prelease += self.repeatdt * np.sign(dt) if abs(time - next_output) < tol: if output_file: output_file.write(self, time) next_output += outputdt * np.sign(dt) if abs(time - next_movie) < tol: self.show(field=movie_background_field, show_time=time, animation=True) next_movie += moviedt * np.sign(dt) # ==== insert post-process here to also allow for memory clean-up via external func ==== # if abs(time - next_callback) < tol: if postIterationCallbacks is not None: for extFunc in postIterationCallbacks: extFunc() next_callback += callbackdt * np.sign(dt) if time != endtime: next_input = self.fieldset.computeTimeChunk(time, dt) if dt == 0: break if verbose_progress: pbar.update(abs(time - _starttime)) if output_file: output_file.write(self, time) if verbose_progress: pbar.finish()
def execute(self, pyfunc=AdvectionRK4, starttime=None, endtime=None, dt=1., runtime=None, interval=None, recovery=None, output_file=None, show_movie=False): """Execute a given kernel function over the particle set for multiple timesteps. Optionally also provide sub-timestepping for particle output. :param pyfunc: Kernel function to execute. This can be the name of a defined Python function or a :class:`parcels.kernel.Kernel` object. Kernels can be concatenated using the + operator :param starttime: Starting time for the timestepping loop. Defaults to 0.0. :param endtime: End time for the timestepping loop :param runtime: Length of the timestepping loop. Use instead of endtime. :param dt: Timestep interval to be passed to the kernel :param interval: Interval for inner sub-timestepping (leap), which dictates the update frequency of file output and animation. :param output_file: :mod:`parcels.particlefile.ParticleFile` object for particle output :param recovery: Dictionary with additional `:mod:parcels.kernels.error` recovery kernels to allow custom recovery behaviour in case of kernel errors. :param show_movie: True shows particles; name of field plots that field as background """ # check if pyfunc has changed since last compile. If so, recompile if self.kernel is None or (self.kernel.pyfunc is not pyfunc and self.kernel is not pyfunc): # Generate and store Kernel if isinstance(pyfunc, Kernel): self.kernel = pyfunc else: self.kernel = self.Kernel(pyfunc) # Prepare JIT kernel execution if self.ptype.uses_jit: self.kernel.remove_lib() self.kernel.compile(compiler=GNUCompiler()) self.kernel.load_lib() # Convert all time variables to seconds if isinstance(starttime, delta): starttime = starttime.total_seconds() if isinstance(endtime, delta): endtime = endtime.total_seconds() if isinstance(runtime, delta): runtime = runtime.total_seconds() if isinstance(dt, delta): dt = dt.total_seconds() if isinstance(interval, delta): interval = interval.total_seconds() if isinstance(starttime, datetime): starttime = (starttime - self.time_origin).total_seconds() if isinstance(endtime, datetime): endtime = (endtime - self.time_origin).total_seconds() # Derive starttime, endtime and interval from arguments or fieldset defaults if runtime is not None and endtime is not None: raise RuntimeError('Only one of (endtime, runtime) can be specified') if starttime is None: starttime = self.fieldset.U.time[0] if dt > 0 else self.fieldset.U.time[-1] if runtime is not None: if runtime < 0: runtime = np.abs(runtime) logger.warning("Negating runtime because it has to be positive") endtime = starttime + runtime * np.sign(dt) else: if endtime is None: endtime = self.fieldset.U.time[-1] if dt > 0 else self.fieldset.U.time[0] if interval is None: interval = endtime - starttime # Ensure that dt and interval have the correct sign if endtime > starttime: # Time-forward mode if dt < 0: dt *= -1. logger.warning("Negating dt because running in time-forward mode") if interval < 0: interval *= -1. logger.warning("Negating interval because running in time-forward mode") if endtime < starttime: # Time-backward mode if dt > 0.: dt *= -1. logger.warning("Negating dt because running in time-backward mode") if interval > 0.: interval *= -1. logger.warning("Negating interval because running in time-backward mode") # Initialise particle timestepping for p in self: p.time = starttime p.dt = dt # Execute time loop in sub-steps (timeleaps) timeleaps = int((endtime - starttime) / interval) assert(timeleaps >= 0) leaptime = starttime for _ in range(timeleaps): # First write output_file, because particles could have been added if output_file: output_file.write(self, leaptime) if show_movie: self.show(field=show_movie, show_time=leaptime) leaptime += interval self.kernel.execute(self, endtime=leaptime, dt=dt, recovery=recovery) # Write out a final output_file if output_file: output_file.write(self, leaptime)
def execute(self, pyfunc=AdvectionRK4, endtime=None, runtime=None, dt=1., interval=None, recovery=None, output_file=None, show_movie=False): """Execute a given kernel function over the particle set for multiple timesteps. Optionally also provide sub-timestepping for particle output. :param pyfunc: Kernel function to execute. This can be the name of a defined Python function or a :class:`parcels.kernel.Kernel` object. Kernels can be concatenated using the + operator :param endtime: End time for the timestepping loop :param runtime: Length of the timestepping loop. Use instead of endtime. :param dt: Timestep interval to be passed to the kernel :param interval: Interval for inner sub-timestepping (leap), which dictates the update frequency of file output and animation. :param output_file: :mod:`parcels.particlefile.ParticleFile` object for particle output :param recovery: Dictionary with additional `:mod:parcels.kernels.error` recovery kernels to allow custom recovery behaviour in case of kernel errors. :param show_movie: True shows particles; name of field plots that field as background """ # check if pyfunc has changed since last compile. If so, recompile if self.kernel is None or (self.kernel.pyfunc is not pyfunc and self.kernel is not pyfunc): # Generate and store Kernel if isinstance(pyfunc, Kernel): self.kernel = pyfunc else: self.kernel = self.Kernel(pyfunc) # Prepare JIT kernel execution if self.ptype.uses_jit: self.kernel.remove_lib() self.kernel.compile(compiler=GNUCompiler()) self.kernel.load_lib() # Convert all time variables to seconds if isinstance(endtime, delta): endtime = endtime.total_seconds() elif isinstance(endtime, datetime): endtime = (endtime - self.time_origin).total_seconds() if isinstance(runtime, delta): runtime = runtime.total_seconds() if isinstance(dt, delta): dt = dt.total_seconds() if isinstance(interval, delta): interval = interval.total_seconds() # Set particle.time defaults based on sign of dt, if not set at ParticleSet construction for p in self: if np.isnan(p.time): p.time = self.fieldset.U.grid.time[ 0] if dt >= 0 else self.fieldset.U.grid.time[-1] # Derive _starttime, endtime and interval from arguments or fieldset defaults if runtime is not None and endtime is not None: raise RuntimeError( 'Only one of (endtime, runtime) can be specified') _starttime = min([p.time for p in self]) if dt >= 0 else max( [p.time for p in self]) if self.repeatdt is not None and self.repeat_starttime is None: self.repeat_starttime = _starttime if runtime is not None: if runtime < 0: runtime = np.abs(runtime) logger.warning( "Negating runtime because it has to be positive") endtime = _starttime + runtime * np.sign(dt) elif endtime is None: endtime = self.fieldset.U.grid.time[ -1] if dt >= 0 else self.fieldset.U.grid.time[0] if interval is None: interval = endtime - _starttime elif dt < 0 and interval > 0.: interval *= -1. logger.warning( "Negating interval because running in time-backward mode") if abs(endtime - _starttime) < 1e-5 or interval == 0 or dt == 0 or runtime == 0: timeleaps = 1 dt = 0 runtime = 0 endtime = _starttime logger.warning_once( "dt or runtime are zero, or endtime is equal to Particle.time. " "The kernels will be executed once, without incrementing time") else: timeleaps = int((endtime - _starttime) / interval) if self.repeatdt is not None and self.repeatdt % interval != 0: raise ("repeatdt should be multiple of interval") # Initialise particle timestepping for p in self: p.dt = dt # set dt_initial to the original dt p.dt_initial = dt # Execute time loop in sub-steps (timeleaps) assert (timeleaps >= 0) leaptime = _starttime for _ in range(timeleaps): # First write output_file, because particles could have been added if output_file: output_file.write(self, leaptime) if show_movie: self.show(field=show_movie, show_time=leaptime) leaptime += interval self.kernel.execute(self, endtime=leaptime, dt=dt, recovery=recovery) # Add new particles if repeatdt is used if self.repeatdt is not None and abs( leaptime - self.repeat_starttime) % self.repeatdt == 0: self.add( ParticleSet(fieldset=self.fieldset, time=leaptime, lon=self.repeatlon, lat=self.repeatlat, depth=self.repeatdepth, pclass=self.repeatpclass)) # Write out a final output_file if output_file: output_file.write(self, leaptime)