NCAR / ADF

A unified collection of python scripts used to generate standard plots from CAM outputs.
Creative Commons Attribution 4.0 International
36 stars 29 forks source link

Add vector lat/lon plot #83

Closed juliecaron closed 2 years ago

juliecaron commented 2 years ago

New feature type

New plot and/or plot enhancement

What is this new feature?

we have lat/lon plots of scalar fields, but i'm adding the vector field option

Assistance required?

Yes, I will need some assistance before making a PR

Extra info

wish me luck!!

brianpm commented 2 years ago

@juliecaron I was messing around with vector plots and I think there are a few decisions that have to be made while designing these plots.

There are at least two distinct options for making the "mean" plots: quiver and streamplot. Playing around with them, I see pros and cons for each. They can both look good, but the quiver option with a contourf to show magnitude might be more information dense. I'll label that one as "Candidate 1" in this example, and the streamplot as "Candidate 2". For the difference panel, I'd also lean toward a quiver plot with a contour. In the example, I boosted the opacity parameter for the contour colors (alpha) to make the vectors more visible.

In both quiver plots, the density of the vectors needs to be reduced. I did that by striding through the data, I'm not sure if there is a better way. The main problem there is that how big the strides should be is dependent on the size of the data. Maybe an option is to make a guess for how many vectors should fit within the plot and then set the stride based on that and the grid size of the data.

Here's my example code and resulting plot:

vector_example

proj = ccrs.PlateCarree()
lons, lats = np.meshgrid(uplev['lon'], uplev['lat'])

fig = plt.figure(figsize=(20,12))

# LAYOUT WITH GRIDSPEC
gs = gridspec.GridSpec(2, 4)
gs.update(wspace=0.25)
ax1 = plt.subplot(gs[0, :2], projection=proj)
ax2 = plt.subplot(gs[0, 2:], projection=proj)
ax3 = plt.subplot(gs[1, 1:3], projection=proj)

# too many vectors to see well, so prune by striding through data:
skip=(slice(None,None,5),slice(None,None,8))

# Candidate 1:
#  - contourf to show magnitude w/ colorbar
#  - vectors (colored or not) to show flow --> subjective (?) choice for how to thin out vectors to be legible
img1 = ax1.contourf(lons, lats, np.sqrt(uplev**2 + vplev**2), cmap='Greys', transform=ccrs.PlateCarree())
ax1.quiver(lons[skip], lats[skip], uplev[skip], vplev[skip], np.sqrt(uplev**2 + vplev**2).values[skip], transform=ccrs.PlateCarree(), cmap='Reds')
ax1.set_title(f"CAM5 Wind [{plev} hPa]")
## Add colorbar to Candidate 1:
cb_c1_ax = inset_axes(ax1,
                   width="3%",  # width = 5% of parent_bbox width
                   height="90%",  # height : 50%
                   loc='lower left',
                   bbox_to_anchor=(1.01, 0.05, 1, 1),
                   bbox_transform=ax1.transAxes,
                   borderpad=0,
                   )
fig.colorbar(img1, cax=cb_c1_ax)

# Candidate 2:
#  - streamplot that shows the flow, colored to show magnitude (with colorbar)
strm = ax2.streamplot(lons, lats, u2plev.values, v2plev.values, density=2, color=np.sqrt(u2plev**2 + v2plev**2).values, cmap='Reds', transform=ccrs.PlateCarree())
ax2.set_title(f"CAM4 [{plev} hPa]")
cb_c2_ax = inset_axes(ax2,
                   width="3%",  # width = 5% of parent_bbox width
                   height="90%",  # height : 50%
                   loc='lower left',
                   bbox_to_anchor=(1.01, 0.05, 1, 1),
                   bbox_transform=ax2.transAxes,
                   borderpad=0,
                   )
fig.colorbar(strm.lines, cax=cb_c2_ax)

# Showing the difference is probably easiest with a contour plot, 
# but then the anomalous circulation is hard to see, so could add vectors on top
udif = u2plev - uplev
vdif = v2plev - vplev

img3 = ax3.contourf(lons, lats, np.sqrt(uplev**2 + vplev**2)-np.sqrt(u2plev**2 + v2plev**2), transform=ccrs.PlateCarree(), norm=mpl.colors.TwoSlopeNorm(vmin=-5, vcenter=0.0, vmax=5), cmap='PuOr', alpha=0.5)
ax3.quiver(lons[skip], lats[skip], udif[skip], vdif[skip], transform=ccrs.PlateCarree())

# img3 = ax3.streamplot(lons, lats, udif.values, vdif.values, transform=ccrs.PlateCarree())
# img3 = ax3.barbs(lons[skip], lats[skip], udif.values[skip], vdif.values[skip], transform=ccrs.PlateCarree()) # does not look good for global data

ax3.set_title(f"Difference []", loc='left')
cb_d_ax = inset_axes(ax3,
                   width="3%",  # width = 5% of parent_bbox width
                   height="90%",  # height : 50%
                   loc='lower left',
                   bbox_to_anchor=(1.01, 0.05, 1, 1),
                   bbox_transform=ax3.transAxes,
                   borderpad=0
                   )
fig.colorbar(img3, cax=cb_d_ax)

[a.coastlines() for a in [ax1,ax2,ax3]]
juliecaron commented 2 years ago

hi brian, thanks for this. i haven't been able to work on this recently, and i hadn't gotten to the plotting part yet. i think i read in the vector pairs read and interpolated to pressure levels and such. but this is a great comparison to see. it's interesting to me that there isn't a stride option for the vectors in the plotting routine. there is in NCL. i'll see if i can back to this soon. i'm sure i'll have questions for you and jesse :)

On Thu, Feb 3, 2022 at 1:59 PM Brian Medeiros @.***> wrote:

@juliecaron https://github.com/juliecaron I was messing around with vector plots and I think there are a few decisions that have to be made while designing these plots.

There are at least two distinct options for making the "mean" plots: quiver and streamplot. Playing around with them, I see pros and cons for each. They can both look good, but the quiver option with a contourf to show magnitude might be more information dense. I'll label that one as "Candidate 1" in this example, and the streamplot as "Candidate 2". For the difference panel, I'd also lean toward a quiver plot with a contour. In the example, I boosted the opacity parameter for the contour colors (alpha) to make the vectors more visible.

In both quiver plots, the density of the vectors needs to be reduced. I did that by striding through the data, I'm not sure if there is a better way. The main problem there is that how big the strides should be is dependent on the size of the data. Maybe an option is to make a guess for how many vectors should fit within the plot and then set the stride based on that and the grid size of the data.

Here's my example code and resulting plot:

[image: vector_example] https://user-images.githubusercontent.com/5471157/152427443-2c0fa9e4-6095-4f4c-94a4-2d53fb16cc4a.png

proj = ccrs.PlateCarree()lons, lats = np.meshgrid(uplev['lon'], uplev['lat']) fig = plt.figure(figsize=(20,12))

LAYOUT WITH GRIDSPECgs = gridspec.GridSpec(2, 4)gs.update(wspace=0.25)ax1 = plt.subplot(gs[0, :2], projection=proj)ax2 = plt.subplot(gs[0, 2:], projection=proj)ax3 = plt.subplot(gs[1, 1:3], projection=proj)

too many vectors to see well, so prune by striding through data:skip=(slice(None,None,5),slice(None,None,8))

Candidate 1:# - contourf to show magnitude w/ colorbar# - vectors (colored or not) to show flow --> subjective (?) choice for how to thin out vectors to be legibleimg1 = ax1.contourf(lons, lats, np.sqrt(uplev2 + vplev2), cmap='Greys', transform=ccrs.PlateCarree())ax1.quiver(lons[skip], lats[skip], uplev[skip], vplev[skip], np.sqrt(uplev2 + vplev2).values[skip], transform=ccrs.PlateCarree(), cmap='Reds')ax1.set_title(f"CAM5 Wind [{plev} hPa]")## Add colorbar to Candidate 1:cb_c1_ax = inset_axes(ax1,

               width="3%",  # width = 5% of parent_bbox width
               height="90%",  # height : 50%
               loc='lower left',
               bbox_to_anchor=(1.01, 0.05, 1, 1),
               bbox_transform=ax1.transAxes,
               borderpad=0,
               )fig.colorbar(img1, cax=cb_c1_ax)

Candidate 2:# - streamplot that shows the flow, colored to show magnitude (with colorbar)strm = ax2.streamplot(lons, lats, u2plev.values, v2plev.values, density=2, color=np.sqrt(u2plev2 + v2plev2).values, cmap='Reds', transform=ccrs.PlateCarree())ax2.set_title(f"CAM4 [{plev} hPa]")cb_c2_ax = inset_axes(ax2,

               width="3%",  # width = 5% of parent_bbox width
               height="90%",  # height : 50%
               loc='lower left',
               bbox_to_anchor=(1.01, 0.05, 1, 1),
               bbox_transform=ax2.transAxes,
               borderpad=0,
               )fig.colorbar(strm.lines, cax=cb_c2_ax)

Showing the difference is probably easiest with a contour plot, # but then the anomalous circulation is hard to see, so could add vectors on topudif = u2plev - uplevvdif = v2plev - vplev

img3 = ax3.contourf(lons, lats, np.sqrt(uplev2 + vplev2)-np.sqrt(u2plev2 + v2plev2), transform=ccrs.PlateCarree(), norm=mpl.colors.TwoSlopeNorm(vmin=-5, vcenter=0.0, vmax=5), cmap='PuOr', alpha=0.5)ax3.quiver(lons[skip], lats[skip], udif[skip], vdif[skip], transform=ccrs.PlateCarree())

img3 = ax3.streamplot(lons, lats, udif.values, vdif.values, transform=ccrs.PlateCarree())# img3 = ax3.barbs(lons[skip], lats[skip], udif.values[skip], vdif.values[skip], transform=ccrs.PlateCarree()) # does not look good for global data

ax3.set_title(f"Difference []", loc='left')cb_d_ax = inset_axes(ax3, width="3%", # width = 5% of parent_bbox width height="90%", # height : 50% loc='lower left', bbox_to_anchor=(1.01, 0.05, 1, 1), bbox_transform=ax3.transAxes, borderpad=0 )fig.colorbar(img3, cax=cb_d_ax)

[a.coastlines() for a in [ax1,ax2,ax3]]

— Reply to this email directly, view it on GitHub https://github.com/NCAR/ADF/issues/83#issuecomment-1029395772, or unsubscribe https://github.com/notifications/unsubscribe-auth/AKODUKEWV4UAROWEZB2VIWLUZLUC3ANCNFSM5JSCHX5Q . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you were mentioned.Message ID: @.***>

andrewgettelman commented 2 years ago

Hi @brianpm,

I definitely like quiver. Seems like the stride can be a function of resolution in lat and lon: if we know how many vectors we want and/or define the spacing (e.g. one every 5 deg of lat and lon), then we can use that to set the number of points to skip as a function of the length of the lat and lon coordinates.

I think black arrows as in the difference work best for overplotting (red is hard to see), and using 'alpha' is great.

Thanks very much for playing with this!