def compile(self, compiler): """ Writes kernel code to file and compiles it.""" with open(self.src_file, 'w') as f: f.write(self.ccode) compiler.compile(self.src_file, self.lib_file, self.log_file) logger.info("Compiled %s ==> %s" % (self.name, self.lib_file)) self._cleanup_files = finalize(self, cleanup_remove_files, self.delete_cfiles, self.src_file, self.lib_file, self.log_file)
def create_parcelsfig_axis(spherical, land=True, projection=None, central_longitude=0): try: import matplotlib.pyplot as plt except: logger.info("Visualisation is not possible. Matplotlib not found.") return None, None, None, None # creating axes was not possible if projection is not None and not spherical: raise RuntimeError('projection not accepted when Field doesn''t have geographic coordinates') if spherical: try: import cartopy except: logger.info("Visualisation of field with geographic coordinates is not possible. Cartopy not found.") return None, None, None, None # creating axes was not possible projection = cartopy.crs.PlateCarree(central_longitude) if projection is None else projection fig, ax = plt.subplots(1, 1, subplot_kw={'projection': projection}) try: # gridlines not supported for all projections gl = ax.gridlines(crs=projection, draw_labels=True) gl.xlabels_top, gl.ylabels_right = (False, False) gl.xformatter = cartopy.mpl.gridliner.LONGITUDE_FORMATTER gl.yformatter = cartopy.mpl.gridliner.LATITUDE_FORMATTER except: pass if land: ax.coastlines() else: cartopy = None fig, ax = plt.subplots(1, 1) ax.grid() return plt, fig, ax, cartopy
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) logger.info("Compiled %s ==> %s" % ("random", self.lib_file)) self._lib = npct.load_library(self.lib_file, '.') return self._lib
def test_plotting(pset_mode, mode, tmpdir): if mode == '3d' and sys.platform in ['linux', 'linux2']: logger.info( 'Skipping 3d test in linux Travis, since it fails to find display to connect' ) return fp = create_outputfiles(tmpdir, pset_mode) plotTrajectoriesFile(fp, mode=mode, show_plt=False)
def remove_deleted(self, pset, output_file, endtime): """ Utility to remove all particles that signalled deletion. This version is generally applicable to all structures and collections """ # Indices marked for deletion. indices = [i for i, p in enumerate(pset) if p.state == OperationCode.Delete] if len(indices) > 0: logger.info("Deleted {} particles.".format(len(indices))) if len(indices) > 0 and output_file is not None: output_file.write(pset, endtime, deleted_only=indices) pset.remove_indices(indices)
def write(self, filename): """Write FieldSet to NetCDF file using NEMO convention :param filename: Basename of the output fileset""" logger.info("Generating NEMO FieldSet output with basename: %s" % filename) if hasattr(self, 'U'): self.U.write(filename, varname='vozocrtx') if hasattr(self, 'V'): self.V.write(filename, varname='vomecrty') for v in self.get_fields(): if (v.name != 'U') and (v.name != 'V'): v.write(filename)
def compile(self, compiler): """ Writes kernel code to file and compiles it.""" all_files_array = [] if self.src_file is None: for dyn_src in self.dyn_srcs: with open(dyn_src, 'w') as f: f.write(self.ccode) all_files_array.append(dyn_src) compiler.compile(self.dyn_srcs, self.lib_file, self.log_file) else: with open(self.src_file, 'w') as f: f.write(self.ccode) all_files_array.append(self.src_file) compiler.compile(self.src_file, self.lib_file, self.log_file) logger.info("Compiled %s ==> %s" % (self.name, self.lib_file)) all_files_array.append(self.log_file) self._cleanup_files = finalize(self, BaseKernel.cleanup_remove_files, self.lib_file, all_files_array, self.delete_cfiles)
def compile(self, compiler=None): if self.src_file is None or self.lib_file is None or self.log_file is None: basename = 'parcels_random_%s' % uuid.uuid4() lib_filename = "lib" + basename basepath = path.join(get_cache_dir(), "%s" % basename) libpath = path.join(get_cache_dir(), "%s" % lib_filename) self.src_file = "%s.c" % basepath self.lib_file = "%s.so" % libpath self.log_file = "%s.log" % basepath ccompiler = compiler if ccompiler is None: cppargs = [] incdirs = [path.join(get_package_dir(), 'include'), ] ccompiler = GNUCompiler(cppargs=cppargs, incdirs=incdirs) if self._lib is None: with open(self.src_file, 'w+') as f: f.write(self.ccode) ccompiler.compile(self.src_file, self.lib_file, self.log_file) logger.info("Compiled %s ==> %s" % ("ParcelsRandom", self.lib_file))
def compile(self, compiler): """ Writes kernel code to file and compiles it.""" all_files_array = [] if self.src_file is None: if self.dyn_srcs is not None: for dyn_src in self.dyn_srcs: with open(dyn_src, 'w') as f: f.write(self.ccode) all_files_array.append(dyn_src) compiler.compile(self.dyn_srcs, self.lib_file, self.log_file) else: if self.src_file is not None: with open(self.src_file, 'w') as f: f.write(self.ccode) if self.src_file is not None: all_files_array.append(self.src_file) compiler.compile(self.src_file, self.lib_file, self.log_file) if len(all_files_array) > 0: logger.info("Compiled %s ==> %s" % (self.name, self.lib_file)) if self.log_file is not None: all_files_array.append(self.log_file)
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 plotfield(field, show_time=None, domain=None, depth_level=0, projection=None, land=True, vmin=None, vmax=None, savefile=None, **kwargs): """Function to plot a Parcels Field :param show_time: Time at which to show the Field :param domain: Four-vector (latN, latS, lonE, lonW) defining domain to show :param depth_level: depth level to be plotted (default 0) :param projection: type of cartopy projection to use (default PlateCarree) :param land: Boolean whether to show land. This is ignored for flat meshes :param vmin: minimum colour scale (only in single-plot mode) :param vmax: maximum colour scale (only in single-plot mode) :param savefile: Name of a file to save the plot to :param animation: Boolean whether result is a single plot, or an animation """ if type(field) is VectorField: spherical = True if field.U.grid.mesh == 'spherical' else False field = [field.U, field.V] plottype = 'vector' elif type(field) is Field: spherical = True if field.grid.mesh == 'spherical' else False field = [field] plottype = 'scalar' else: raise RuntimeError('field needs to be a Field or VectorField object') plt, fig, ax, cartopy = create_parcelsfig_axis(spherical, land, projection=projection) if plt is None: return None, None, None, None # creating axes was not possible data = {} plotlon = {} plotlat = {} for i, fld in enumerate(field): show_time = fld.grid.time[0] if show_time is None else show_time if fld.grid.defer_load: fld.fieldset.computeTimeChunk(show_time, 1) (idx, periods) = fld.time_index(show_time) show_time -= periods * (fld.grid.time[-1] - fld.grid.time[0]) if show_time > fld.grid.time[-1] or show_time < fld.grid.time[0]: raise TimeExtrapolationError(show_time, field=fld, msg='show_time') latN, latS, lonE, lonW = parsedomain(domain, fld) if isinstance(fld.grid, CurvilinearGrid): plotlon[i] = fld.grid.lon[latS:latN, lonW:lonE] plotlat[i] = fld.grid.lat[latS:latN, lonW:lonE] else: plotlon[i] = fld.grid.lon[lonW:lonE] plotlat[i] = fld.grid.lat[latS:latN] if i > 0 and not np.allclose(plotlon[i], plotlon[0]): raise RuntimeError('VectorField needs to be on an A-grid for plotting') if fld.grid.time.size > 1: if fld.grid.zdim > 1: data[i] = np.squeeze(fld.temporal_interpolate_fullfield(idx, show_time))[depth_level, latS:latN, lonW:lonE] else: data[i] = np.squeeze(fld.temporal_interpolate_fullfield(idx, show_time))[latS:latN, lonW:lonE] else: if fld.grid.zdim > 1: data[i] = np.squeeze(fld.data)[depth_level, latS:latN, lonW:lonE] else: data[i] = np.squeeze(fld.data)[latS:latN, lonW:lonE] if plottype is 'vector': spd = data[0] ** 2 + data[1] ** 2 speed = np.sqrt(spd, where=spd > 0) vmin = speed.min() if vmin is None else vmin vmax = speed.max() if vmax is None else vmax if isinstance(field[0].grid, CurvilinearGrid): x, y = plotlon[0], plotlat[0] else: x, y = np.meshgrid(plotlon[0], plotlat[0]) nonzerospd = speed != 0 u, v = (np.zeros_like(data[0]) * np.nan, np.zeros_like(data[1]) * np.nan) np.place(u, nonzerospd, data[0][nonzerospd] / speed[nonzerospd]) np.place(v, nonzerospd, data[1][nonzerospd] / speed[nonzerospd]) if cartopy: cs = ax.quiver(x, y, u, v, speed, cmap=plt.cm.gist_ncar, clim=[vmin, vmax], scale=50, transform=cartopy.crs.PlateCarree()) else: cs = ax.quiver(x, y, u, v, speed, cmap=plt.cm.gist_ncar, clim=[vmin, vmax], scale=50) else: vmin = data[0].min() if vmin is None else vmin vmax = data[0].max() if vmax is None else vmax if cartopy: cs = ax.pcolormesh(plotlon[0], plotlat[0], data[0], transform=cartopy.crs.PlateCarree()) else: cs = ax.pcolormesh(plotlon[0], plotlat[0], data[0]) if cartopy is None or projection is None: ax.set_xlim(np.nanmin(plotlon[0]), np.nanmax(plotlon[0])) ax.set_ylim(np.nanmin(plotlat[0]), np.nanmax(plotlat[0])) elif domain is not None: ax.set_extent([np.nanmin(plotlon[0]), np.nanmax(plotlon[0]), np.nanmin(plotlat[0]), np.nanmax(plotlat[0])]) cs.cmap.set_over('k') cs.cmap.set_under('w') cs.set_clim(vmin, vmax) cartopy_colorbar(cs, plt, fig, ax) timestr = parsetimestr(field[0].grid.time_origin, show_time) titlestr = kwargs.pop('titlestr', '') if field[0].grid.zdim > 1: if field[0].grid.gtype in [GridCode.CurvilinearZGrid, GridCode.RectilinearZGrid]: gphrase = 'depth' depth_or_level = field[0].grid.depth[depth_level] else: gphrase = 'level' depth_or_level = depth_level depthstr = ' at %s %g ' % (gphrase, depth_or_level) else: depthstr = '' if plottype is 'vector': ax.set_title(titlestr + 'Velocity field' + depthstr + timestr) else: ax.set_title(titlestr + field[0].name + depthstr + timestr) if not spherical: ax.set_xlabel('Zonal distance [m]') ax.set_ylabel('Meridional distance [m]') plt.draw() if savefile: plt.savefig(savefile) logger.info('Plot saved to ' + savefile + '.png') plt.close() return plt, fig, ax, cartopy
def plotparticles(particles, with_particles=True, show_time=None, field=None, domain=None, projection=None, land=True, vmin=None, vmax=None, savefile=None, animation=False): """Function to plot a Parcels ParticleSet :param show_time: Time at which to show the ParticleSet :param with_particles: Boolean whether particles are also plotted on Field :param field: Field to plot under particles (either None, a Field object, or 'vector') :param domain: Four-vector (latN, latS, lonE, lonW) defining domain to show :param projection: type of cartopy projection to use (default PlateCarree) :param land: Boolean whether to show land. This is ignored for flat meshes :param vmin: minimum colour scale (only in single-plot mode) :param vmax: maximum colour scale (only in single-plot mode) :param savefile: Name of a file to save the plot to :param animation: Boolean whether result is a single plot, or an animation """ show_time = particles[0].time if show_time is None else show_time if isinstance(show_time, datetime): show_time = np.datetime64(show_time) if isinstance(show_time, np.datetime64): if not particles.time_origin: raise NotImplementedError( 'If fieldset.time_origin is not a date, showtime cannot be a date in particleset.show()') show_time = particles.time_origin.reltime(show_time) if isinstance(show_time, delta): show_time = show_time.total_seconds() if np.isnan(show_time): show_time, _ = particles.fieldset.gridset.dimrange('time_full') if field is None: spherical = True if particles.fieldset.U.grid.mesh == 'spherical' else False plt, fig, ax, cartopy = create_parcelsfig_axis(spherical, land, projection) if plt is None: return # creating axes was not possible ax.set_title('Particles' + parsetimestr(particles.fieldset.U.grid.time_origin, show_time)) latN, latS, lonE, lonW = parsedomain(domain, particles.fieldset.U) if cartopy is None or projection is None: if domain is not None: if isinstance(particles.fieldset.U.grid, CurvilinearGrid): ax.set_xlim(particles.fieldset.U.grid.lon[latS, lonW], particles.fieldset.U.grid.lon[latN, lonE]) ax.set_ylim(particles.fieldset.U.grid.lat[latS, lonW], particles.fieldset.U.grid.lat[latN, lonE]) else: ax.set_xlim(particles.fieldset.U.grid.lon[lonW], particles.fieldset.U.grid.lon[lonE]) ax.set_ylim(particles.fieldset.U.grid.lat[latS], particles.fieldset.U.grid.lat[latN]) else: ax.set_xlim(np.nanmin(particles.fieldset.U.grid.lon), np.nanmax(particles.fieldset.U.grid.lon)) ax.set_ylim(np.nanmin(particles.fieldset.U.grid.lat), np.nanmax(particles.fieldset.U.grid.lat)) elif domain is not None: if isinstance(particles.fieldset.U.grid, CurvilinearGrid): ax.set_extent([particles.fieldset.U.grid.lon[latS, lonW], particles.fieldset.U.grid.lon[latN, lonE], particles.fieldset.U.grid.lat[latS, lonW], particles.fieldset.U.grid.lat[latN, lonE]]) else: ax.set_extent([particles.fieldset.U.grid.lon[lonW], particles.fieldset.U.grid.lon[lonE], particles.fieldset.U.grid.lat[latS], particles.fieldset.U.grid.lat[latN]]) else: if field is 'vector': field = particles.fieldset.UV elif not isinstance(field, Field): field = getattr(particles.fieldset, field) plt, fig, ax, cartopy = plotfield(field=field, animation=animation, show_time=show_time, domain=domain, projection=projection, land=land, vmin=vmin, vmax=vmax, savefile=None, titlestr='Particles and ') if plt is None: return # creating axes was not possible if with_particles: plon = np.array([p.lon for p in particles]) plat = np.array([p.lat for p in particles]) if cartopy: ax.scatter(plon, plat, s=20, color='black', zorder=20, transform=cartopy.crs.PlateCarree()) else: ax.scatter(plon, plat, s=20, color='black', zorder=20) if animation: plt.draw() plt.pause(0.0001) elif savefile is None: plt.show() else: plt.savefig(savefile) logger.info('Plot saved to ' + savefile + '.png') plt.close()
def plotfield(field, show_time=None, domain=None, depth_level=0, projection=None, land=True, vmin=None, vmax=None, savefile=None, **kwargs): """Function to plot a Parcels Field :param show_time: Time at which to show the Field :param domain: dictionary (with keys 'N', 'S', 'E', 'W') defining domain to show :param depth_level: depth level to be plotted (default 0) :param projection: type of cartopy projection to use (default PlateCarree) :param land: Boolean whether to show land. This is ignored for flat meshes :param vmin: minimum colour scale (only in single-plot mode) :param vmax: maximum colour scale (only in single-plot mode) :param savefile: Name of a file to save the plot to :param animation: Boolean whether result is a single plot, or an animation """ if type(field) is VectorField: spherical = True if field.U.grid.mesh == 'spherical' else False field = [field.U, field.V] plottype = 'vector' elif type(field) is Field: spherical = True if field.grid.mesh == 'spherical' else False field = [field] plottype = 'scalar' else: raise RuntimeError('field needs to be a Field or VectorField object') if field[0].grid.gtype in [ GridCode.CurvilinearZGrid, GridCode.CurvilinearSGrid ]: logger.warning( 'Field.show() does not always correctly determine the domain for curvilinear grids. ' 'Use plotting with caution and perhaps use domain argument as in the NEMO 3D tutorial' ) plt, fig, ax, cartopy = create_parcelsfig_axis(spherical, land, projection=projection) if plt is None: return None, None, None, None # creating axes was not possible data = {} plotlon = {} plotlat = {} for i, fld in enumerate(field): show_time = fld.grid.time[0] if show_time is None else show_time if fld.grid.defer_load: fld.fieldset.computeTimeChunk(show_time, 1) (idx, periods) = fld.time_index(show_time) show_time -= periods * (fld.grid.time_full[-1] - fld.grid.time_full[0]) if show_time > fld.grid.time[-1] or show_time < fld.grid.time[0]: raise TimeExtrapolationError(show_time, field=fld, msg='show_time') latN, latS, lonE, lonW = parsedomain(domain, fld) if isinstance(fld.grid, CurvilinearGrid): plotlon[i] = fld.grid.lon[latS:latN, lonW:lonE] plotlat[i] = fld.grid.lat[latS:latN, lonW:lonE] else: plotlon[i] = fld.grid.lon[lonW:lonE] plotlat[i] = fld.grid.lat[latS:latN] if i > 0 and not np.allclose(plotlon[i], plotlon[0]): raise RuntimeError( 'VectorField needs to be on an A-grid for plotting') if fld.grid.time.size > 1: if fld.grid.zdim > 1: data[i] = np.squeeze( fld.temporal_interpolate_fullfield(idx, show_time))[depth_level, latS:latN, lonW:lonE] else: data[i] = np.squeeze( fld.temporal_interpolate_fullfield(idx, show_time))[latS:latN, lonW:lonE] else: if fld.grid.zdim > 1: data[i] = np.squeeze(fld.data)[depth_level, latS:latN, lonW:lonE] else: data[i] = np.squeeze(fld.data)[latS:latN, lonW:lonE] if plottype == 'vector': if field[0].interp_method == 'cgrid_velocity': logger.warning_once( 'Plotting a C-grid velocity field is achieved via an A-grid projection, reducing the plot accuracy' ) d = np.empty_like(data[0]) d[:-1, :] = (data[0][:-1, :] + data[0][1:, :]) / 2. d[-1, :] = data[0][-1, :] data[0] = d d = np.empty_like(data[0]) d[:, :-1] = (data[0][:, :-1] + data[0][:, 1:]) / 2. d[:, -1] = data[0][:, -1] data[1] = d spd = data[0]**2 + data[1]**2 speed = np.where(spd > 0, np.sqrt(spd), 0) vmin = speed.min() if vmin is None else vmin vmax = speed.max() if vmax is None else vmax if isinstance(field[0].grid, CurvilinearGrid): x, y = plotlon[0], plotlat[0] else: x, y = np.meshgrid(plotlon[0], plotlat[0]) u = np.where(speed > 0., data[0] / speed, 0) v = np.where(speed > 0., data[1] / speed, 0) if cartopy: cs = ax.quiver(np.asarray(x), np.asarray(y), np.asarray(u), np.asarray(v), speed, cmap=plt.cm.gist_ncar, clim=[vmin, vmax], scale=50, transform=cartopy.crs.PlateCarree()) else: cs = ax.quiver(x, y, u, v, speed, cmap=plt.cm.gist_ncar, clim=[vmin, vmax], scale=50) else: vmin = data[0].min() if vmin is None else vmin vmax = data[0].max() if vmax is None else vmax assert len(data[0].shape) == 2 if field[0].interp_method == 'cgrid_tracer': d = data[0][1:, 1:] elif field[0].interp_method == 'cgrid_velocity': if field[0].fieldtype == 'U': d = np.empty_like(data[0]) d[:-1, :-1] = (data[0][1:, :-1] + data[0][1:, 1:]) / 2. elif field[0].fieldtype == 'V': d = np.empty_like(data[0]) d[:-1, :-1] = (data[0][:-1, 1:] + data[0][1:, 1:]) / 2. else: # W d = data[0][1:, 1:] else: # if A-grid d = (data[0][:-1, :-1] + data[0][1:, :-1] + data[0][:-1, 1:] + data[0][1:, 1:]) / 4. d = np.where(data[0][:-1, :-1] == 0, 0, d) d = np.where(data[0][1:, :-1] == 0, 0, d) d = np.where(data[0][1:, 1:] == 0, 0, d) d = np.where(data[0][:-1, 1:] == 0, 0, d) if cartopy: cs = ax.pcolormesh(plotlon[0], plotlat[0], d, transform=cartopy.crs.PlateCarree()) else: cs = ax.pcolormesh(plotlon[0], plotlat[0], d) if cartopy is None: ax.set_xlim(np.nanmin(plotlon[0]), np.nanmax(plotlon[0])) ax.set_ylim(np.nanmin(plotlat[0]), np.nanmax(plotlat[0])) elif domain is not None: ax.set_extent([ np.nanmin(plotlon[0]), np.nanmax(plotlon[0]), np.nanmin(plotlat[0]), np.nanmax(plotlat[0]) ], crs=cartopy.crs.PlateCarree()) cs.cmap.set_over('k') cs.cmap.set_under('w') cs.set_clim(vmin, vmax) cartopy_colorbar(cs, plt, fig, ax) timestr = parsetimestr(field[0].grid.time_origin, show_time) titlestr = kwargs.pop('titlestr', '') if field[0].grid.zdim > 1: if field[0].grid.gtype in [ GridCode.CurvilinearZGrid, GridCode.RectilinearZGrid ]: gphrase = 'depth' depth_or_level = field[0].grid.depth[depth_level] else: gphrase = 'level' depth_or_level = depth_level depthstr = ' at %s %g ' % (gphrase, depth_or_level) else: depthstr = '' if plottype == 'vector': ax.set_title(titlestr + 'Velocity field' + depthstr + timestr) else: ax.set_title(titlestr + field[0].name + depthstr + timestr) if not spherical: ax.set_xlabel('Zonal distance [m]') ax.set_ylabel('Meridional distance [m]') plt.draw() if savefile: plt.savefig(savefile) logger.info('Plot saved to ' + savefile + '.png') plt.close() return plt, fig, ax, cartopy
def compile(self, compiler): """ Writes kernel code to file and compiles it.""" with open(self.src_file, 'w') as f: f.write(self.ccode) compiler.compile(self.src_file, self.lib_file, self.log_file) logger.info("Compiled %s ==> %s" % (self.name, self.lib_file))