Illustrative examples for a specific day

Using the data corresponding to the outputs of FLEXPART-WRF forced with ERA5 and the TROVA configuration file provided in the following repositories https://zenodo.org/records/14886887 and https://zenodo.org/records/14939160, the results shown below for October 17, 2014, can be reproduced. For this analysis, the Canary Islands are considered as the target region (magenta contour on the graphics shown). The Python codes to process these results and create the corresponding maps can be found in the repository https://zenodo.org/records/14886887.

Below we show a detailed analysis of the results obtained when we ran TROVA using the methodology of Sodemann et al (2008). This analysis is performed for a sample of 1000 particles. For this analysis, we used the files 20141017000000_dqdt_back.nc and 20141017000000_back.nc. We present four figures with the following variables:

Specific humidity changes every 6 h for the particles

def plot_line_dq_dt(lat, lon, m_id, lat_id, lon_id, name, title):

   rcParams.update({'font.size': 15})
   fig=plt.figure(figsize=(10,8))
   ax1=plt.subplot(111,projection=ccrs.PlateCarree())
   ax1.add_feature(cfeature.COASTLINE.with_scale('10m'), linewidth=0.9)
   ax1.set_extent([-116,43,-20,60], crs=ccrs.PlateCarree())

   unit=r'$\frac{dq}{dt}$'  +"  "+  r'$ [g kg^{-1} 6h^{-1}]$'
   lista=np.arange(0,2, 0.1)
   cmap=plt.get_cmap('RdYlGn_r')

   boundary_norm = BoundaryNorm(lista,
                          cmap.N)

   for i in range(len(m_id[:,0])):
     index=np.where(np.isnan(m_id[i,:]))
     if len(index[0])!=0:
         index_cero=np.where(m_id[i,:index[0][0]]==0)
         if len(index_cero[0])!=len(m_id[i,:index[0][0]]==0):
            ss=plt.scatter(lon_id[i,:index[0][0]],lat_id[i,:index[0][0]],s=(m_id[i,:index[0][0]]/np.abs(np.max(m_id[i,:index[0][0]])))*40, c=m_id[i,:index[0][0]]*1000, cmap=cmap, norm=boundary_norm)
     else:
         print (np.min(m_id[i,:]), np.max(m_id[i,:]))
         ss=plt.scatter(lon_id[i,:],lat_id[i,:],s=(m_id[i,:]/np.abs(np.max(m_id[i,:])))*40, c=m_id[i,:]*1000, cmap=cmap, norm=boundary_norm)

   lat_mask, lon_mask, mask = load_mask_grid_NR("CAN.nc", "mask", "lon", "lat", 2)
   ax1.contour(lon_mask, lat_mask, mask, levels=[0.5], colors='magenta', linewidths=3.5, transform=ccrs.PlateCarree())

   plt.title("Day: "+ title)
   cb=plt.colorbar(ss,orientation="horizontal", pad=0.06,shrink=0.8, extend="both")
   cb.set_label(label=unit, size=15)
   cb.ax.tick_params(labelsize=15)
   gl = ax1.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,linewidth=1, color='gray', alpha=0.2, linestyle='--')
   gl.xlabels_top = False
   gl.ylabels_left = True
   gl.ylabels_right = False
   gl.xlines = True
   gl.top_labels = False
   gl.bottom_labels = True
   gl.left_labels = True
   gl.right_labels = False
   gl.xlines = True
   paso_h=30
   lons=np.arange(np.ceil(-110),np.ceil(60),paso_h)
   gl.xlocator = mticker.FixedLocator(lons)
   gl.xformatter = LONGITUDE_FORMATTER
   gl.yformatter = LATITUDE_FORMATTER
   gl.xlabel_style = {'size': 15, 'color': 'gray'}
   gl.xlabel_style = {'color': 'black'}
   plt.savefig(name+".jpg", bbox_inches='tight', dpi = 300)
   plt.close()
dq_dt_part

Particle positions by day shown in colors

The colors ‘blue’, ‘green’, ‘red’, ‘cyan’, ‘magenta’, ‘yellow’, ‘black’, ‘purple’, ‘orange’, ‘brown’ are associated with days from 10 to 1 in backward in time mode.

def plot_day(lat, lon, m_id, lat_id, lon_id, name, title):

   rcParams.update({'font.size': 15})
   fig=plt.figure(figsize=(10,8))
   ax1=plt.subplot(111,projection=ccrs.PlateCarree())
   ax1.add_feature(cfeature.COASTLINE.with_scale('10m'), linewidth=0.9)
   ax1.set_extent([-116,43,-20,60], crs=ccrs.PlateCarree())
   colors = ['blue', 'green', 'red', 'cyan', 'magenta', 'yellow', 'black', 'purple', 'orange', 'brown']

   for j in range(m_id.shape[0]):
      values = m_id[j, :]
      values_lat = lat_id[j,:]
      values_lon = lon_id[j,:]
      selected_intervals = [values[i:i+4] for i in range(0, len(values), 4)]
      selected_intervals_lat = [values_lat[i:i+4] for i in range(0, len(values_lat), 4)]
      selected_intervals_lon = [values_lon[i:i+4] for i in range(0, len(values_lon), 4)]
      for idx, (lon, lat, interval) in enumerate(zip(selected_intervals_lon, selected_intervals_lat, selected_intervals)):
         plt.scatter(lon, lat, color=colors[idx],s=0.05)

   lat_mask, lon_mask, mask = load_mask_grid_NR("CAN.nc", "mask", "lon", "lat", 2)
   ax1.contour(lon_mask, lat_mask, mask, levels=[0.5], colors='magenta', linewidths=3.5, transform=ccrs.PlateCarree())

   plt.title("Day: "+ title)
   gl = ax1.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,linewidth=1, color='gray', alpha=0.2, linestyle='--')
   gl.xlabels_top = False
   gl.ylabels_left = True
   gl.ylabels_right = False
   gl.xlines = True
   gl.top_labels = False
   gl.bottom_labels = True
   gl.left_labels = True
   gl.right_labels = False
   gl.xlines = True
   paso_h=30
   lons=np.arange(np.ceil(-110),np.ceil(60),paso_h)
   gl.xlocator = mticker.FixedLocator(lons)
   gl.xformatter = LONGITUDE_FORMATTER
   gl.yformatter = LATITUDE_FORMATTER
   gl.xlabel_style = {'size': 15, 'color': 'gray'}
   gl.xlabel_style = {'color': 'black'}
   plt.savefig(name+"_day.jpg", bbox_inches='tight', dpi = 300)
   plt.close()
point_day_part

The trajectories of the particles and the height in colors every 6 h

def plot_line_z(lat, lon, m_id, lat_id, lon_id, name, title):

   rcParams.update({'font.size': 15})
   fig=plt.figure(figsize=(10,8))
   ax1=plt.subplot(111,projection=ccrs.PlateCarree())
   ax1.add_feature(cfeature.COASTLINE.with_scale('10m'), linewidth=0.9)
   ax1.set_extent([-116,43,-20,60], crs=ccrs.PlateCarree())
   unit="z (km)"
   lista=np.arange(0,10.1,1)
   cmap=plt.get_cmap('jet')
   boundary_norm = BoundaryNorm(lista,
                           cmap.N)

   for i in range(len(m_id[:,0])):
     index=np.where(np.isnan(m_id[i,:]))
     if len(index[0])!=0:
          index_cero=np.where(m_id[i,:index[0][0]]==0)
          if len(index_cero[0])!=len(m_id[i,:index[0][0]]==0):
            lc, line=colorline(lon_id[i,:index[0][0]],lat_id[i,:index[0][0]], z=m_id[i,:index[0][0]], lista=lista, cmap=cmap)
     else:
          print (np.min(m_id[i,:]), np.max(m_id[i,:]))
          lc, line=colorline(lon_id[i,:],lat_id[i,:], z=m_id[i,:], lista=lista, cmap=cmap)

   plt.title("Day: "+ title)
   lat_mask, lon_mask, mask = load_mask_grid_NR("CAN.nc", "mask", "lon", "lat", 2)
   ax1.contour(lon_mask, lat_mask, mask, levels=[0.5], colors='magenta', linewidths=3.5, transform=ccrs.PlateCarree())

   cb=plt.colorbar(lc,orientation="horizontal",pad=0.06,shrink=0.8, extend="both")
   cb.set_label(label=unit, size=15)
   cb.ax.tick_params(labelsize=15)
   gl = ax1.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,linewidth=1, color='gray', alpha=0.2, linestyle='--')
   gl.xlabels_top = False
   gl.ylabels_left = True
   gl.ylabels_right = False
   gl.xlines = True
   gl.top_labels = False
   gl.bottom_labels = True
   gl.left_labels = True
   gl.right_labels = False
   gl.xlines = True
   paso_h=30
   lons=np.arange(np.ceil(-110),np.ceil(60),paso_h)
   gl.xlocator = mticker.FixedLocator(lons)
   gl.xformatter = LONGITUDE_FORMATTER
   gl.yformatter = LATITUDE_FORMATTER
   gl.xlabel_style = {'size': 15, 'color': 'gray'}
   gl.xlabel_style = {'color': 'black'}
   plt.savefig(name+".jpg", bbox_inches='tight', dpi = 300)
   plt.close()
Track particles

Moisture source pattern (E-P > 0)

def plot_field(lon, lat, data, name, pallete, unit, title):

   rcParams.update({'font.size': 15})
   fig=plt.figure(figsize=(10,8))

   lon, lat = np.meshgrid(lon, lat)
   ax1 = plt.subplot(111, projection=ccrs.PlateCarree())
   ax1.set_extent([-80,43,15,60], crs=ccrs.PlateCarree())
   ax1.add_feature(cfeature.COASTLINE.with_scale('10m'), linewidth=0.9)

   band_a = 0.001
   data[np.abs(data) < band_a] = np.nan
   lat_mask, lon_mask, mask = load_landmask("CAN.nc")
   unit = unit
   cf = ax1.contourf(lon, lat, data, levels=np.arange(0, 2.2, 0.2), cmap=plt.get_cmap(pallete), extend='both', transform=ccrs.PlateCarree())
   ax1.contour(lon_mask, lat_mask, mask, levels=[0.5], colors='magenta', linewidths=3, transform=ccrs.PlateCarree())

   plt.title("Day: "+ title)
   cb = plt.colorbar(cf, orientation="horizontal", pad=0.06, shrink=0.8)
   cb.set_label(label=unit, size=20)
   cb.ax.tick_params(labelsize=20)
   gl = ax1.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=1, color='gray', alpha=0.5, linestyle='--')
   gl.xlabels_top = False
   gl.ylabels_left = True
   gl.ylabels_right = False
   gl.xlines = True
   gl.xlabels_top = False
   gl.ylabels_left = True
   gl.ylabels_right = False
   gl.xlines = True
   gl.top_labels = False
   gl.bottom_labels = True
   gl.left_labels = True
   gl.right_labels = False
   gl.xlines = True
   paso_h = 30
   dlat = 15
   lons=np.arange(np.ceil(-110),np.ceil(60),paso_h)
   gl.xlocator = mticker.FixedLocator(lons)
   gl.ylocator = mticker.MultipleLocator(dlat)
   gl.xformatter = LONGITUDE_FORMATTER
   gl.yformatter = LATITUDE_FORMATTER
   gl.xlabel_style = {'size': 15, 'color': 'black'}
   gl.ylabel_style = {'size': 15, 'color': 'black'}
   plt.savefig(name + ".png", bbox_inches='tight', dpi=300)
   plt.close()
Moisture Source

Water vapor residence time in the atmosphere

def plot_residence_time(residence_time_particles, residence_time_mean, output_dir, date, rank):

   import matplotlib.pyplot as plt
   plt.figure(figsize=(10, 6))
   plt.plot(residence_time_particles, 'o', markersize=2)
   plt.axhline(y=residence_time_mean, color='r', linestyle='--', label=f'Mean: {residence_time_mean:.2f} days')
   plt.xlabel('Particle number')
   plt.ylabel('Residence time (days)')
   plt.legend()
   plot_file = f"{output_dir}WVRT_plot_{date}.png"
   plt.savefig(plot_file, bbox_inches='tight', dpi=300)
   plt.close()
   if rank == 0:
      print("--------------------------------------------------------------------")
      print(f"Plot for the residence time for all particles saved to {plot_file}")
      print("--------------------------------------------------------------------")
Water vapor residence time