diff --git a/.github/workflows/buildbook.yaml b/.github/workflows/buildbook.yaml index 3891c461..731ba1fd 100644 --- a/.github/workflows/buildbook.yaml +++ b/.github/workflows/buildbook.yaml @@ -4,11 +4,13 @@ on: [push, pull_request] jobs: build: - runs-on: ubuntu-latest strategy: matrix: python-version: [3.8] + defaults: + run: + shell: bash -l {0} steps: - uses: actions/checkout@v2 @@ -16,13 +18,17 @@ jobs: uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt + - name: Set up Python modules with conda + uses: conda-incubator/setup-miniconda@v2 + with: + activate-environment: how_to_eurec4a + environment-file: environment.yml + python-version: ${{ matrix.python-version }} + auto-activate-base: false - name: build book run: | - jupyter-book build -W -n --keep-going how_to_eurec4a + conda info + jupyter-book build -W -n --keep-going how_to_eurec4a - name: Archive build artifacts if: always() uses: actions/upload-artifact@v2 diff --git a/environment.yml b/environment.yml new file mode 100644 index 00000000..1295608b --- /dev/null +++ b/environment.yml @@ -0,0 +1,8 @@ +name: how_to_eurec4a +dependencies: + - python>=3.7 + - anaconda + - cartopy + - pip + - pip: + - -r file:requirements.txt diff --git a/how_to_eurec4a/Miss-Piggy.jpeg b/how_to_eurec4a/Miss-Piggy.jpeg new file mode 100644 index 00000000..53075e09 Binary files /dev/null and b/how_to_eurec4a/Miss-Piggy.jpeg differ diff --git a/how_to_eurec4a/_config.yml b/how_to_eurec4a/_config.yml index 08d8de3d..05f466ec 100755 --- a/how_to_eurec4a/_config.yml +++ b/how_to_eurec4a/_config.yml @@ -5,9 +5,9 @@ ####################################################################################### # Book settings -title : How to EUREC4A # The title of the book. Will be placed in the left navbar. -author : EUREC4A community # The author of the book -copyright : "2020" # Copyright year to be placed in the footer +title : How to EUREC4A # The title of the book. Will be placed in the left navbar. +author : EUREC4A community # The author of the book +copyright : "2021" # Copyright year to be placed in the footer logo : logo_pyeurec4a.png # A path to the book logo repository: url : https://github.com/eurec4a/how_to_eurec4a diff --git a/how_to_eurec4a/_toc.yml b/how_to_eurec4a/_toc.yml index d324cc25..1f6c9fe2 100644 --- a/how_to_eurec4a/_toc.yml +++ b/how_to_eurec4a/_toc.yml @@ -12,6 +12,13 @@ - file: wales - file: hamp_comparison - file: cloudmasks +- file: pthree + sections: + - file: p3_flight_tracks + - file: p3_humidity_comparison + - file: p3_wband_radar + - file: p3_AXBTs + - file: p3_wsra - file: meteor sections: - file: meteor_cloudradar diff --git a/how_to_eurec4a/p3_AXBTs.md b/how_to_eurec4a/p3_AXBTs.md new file mode 100644 index 00000000..a566cbce --- /dev/null +++ b/how_to_eurec4a/p3_AXBTs.md @@ -0,0 +1,205 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.11.2 +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Ocean temperatures: AXBTs and SWIFT buoys + +During EUREC4A/ATOMIC the P-3 deployed 165 Airborne eXpendable BathyThermographs (AXBTs) +to measure profiles of ocean temperature. (These are kind of the oceanic equivalent of +dropsondes but they don't measure salinity.) Often these were dropped around +other ocean temperature measurements - for example the autonomous +Surface Wave Instrument Floats with Tracking (SWIFT) buoys deployed from the +Ron Brown by Elizabeth Thompson of NOAA and her colleagues. The SWIFT deployment +is described in {cite}`Quinn:2021`. + +Let's take a look at some of the AXBT measurements and how they compare to the +SWIFTs. + +```{code-cell} ipython3 +import xarray as xr +import numpy as np +import datetime + +import matplotlib.pyplot as plt +plt.style.use(["./mplstyle/book"]) +%matplotlib inline + +import eurec4a +cat = eurec4a.get_intake_catalog() +``` + +Mapping takes quite some setup. Maybe we'll encapsulate this later but for now we repeat code +in each notebook. + +```{code-cell} ipython3 +:tags: [hide-cell] + +import matplotlib as mpl +import matplotlib.ticker as mticker + +import cartopy.crs as ccrs +from cartopy.feature import LAND +from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER +def ax_to_map(ax, lon_w = -60.5, lon_e = -49, lat_s = 10, lat_n = 16.5): + # Defining boundaries of the plot + ax.set_extent([lon_w,lon_e,lat_s,lat_n]) # lon west, lon east, lat south, lat north + ax.coastlines(resolution='10m',linewidth=1.5,zorder=1); + ax.add_feature(LAND,facecolor='0.9') + +def set_up_map(plt, lon_w = -60.5, lon_e = -49, lat_s = 10, lat_n = 16.5): + ax = plt.axes(projection=ccrs.PlateCarree()) + ax_to_map(ax, lon_w, lon_e, lat_s, lat_n) + return(ax) + +def add_gridlines(ax): + # Assigning axes ticks + xticks = np.arange(-65,0,2.5) + yticks = np.arange(0,25,2.5) + gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=1, + color='black', alpha=0.5, linestyle='dotted') + gl.xlocator = mticker.FixedLocator(xticks) + gl.ylocator = mticker.FixedLocator(yticks) + gl.xformatter = LONGITUDE_FORMATTER + gl.yformatter = LATITUDE_FORMATTER + gl.xlabel_style = {'size': 10, 'color': 'k'} + gl.ylabel_style = {'size': 10, 'color': 'k'} + gl.right_labels = False + gl.bottom_labels = False + gl.xlabel = {'Latitude'} +``` + +What days did the P-3 fly on? We can find out via the flight segmentation files. + +```{code-cell} ipython3 +# On what days did the P-3 fly? These are UTC date +all_flight_segments = eurec4a.get_flight_segments() +flight_dates = np.unique([np.datetime64(flight["takeoff"]).astype("datetime64[D]") + for flight in all_flight_segments["P3"].values()]) +``` + +Now set up colors to code each flight date during the experiment. One could choose +a categorical palette so the colors were as different from each other as possible. +Here we'll choose from a continuous set that spans the experiment so days that are +close in time are also close in color. + +```{code-cell} ipython3 +:tags: [hide-cell] + +# Like mpl.colors.Normalize but works also with datetime64 objects +def mk_norm(vmin, vmax): + def norm(values): + return (values - vmin) / (vmax - vmin) + return norm +norm = mk_norm(np.datetime64("2020-01-15"), + np.datetime64("2020-02-15")) + +# color map for things coded by flight date +# Sample from a continuous color map running from start to end of experiment +def color_of_day(day): + return plt.cm.viridis(norm(day), alpha=0.9) +``` + +The P-3 only deployed AXBTs on some flights, and the SWIFT buoys were only deployed +on a subset of those dates. + +```{code-cell} ipython3 +axbts = cat.P3.AXBT.Level_3.to_dask() +swifts = [cat[s].all.to_dask() for s in list(cat) if "SWIFT" in s] +axbt_dates = np.intersect1d(np.unique(axbts.time.astype("datetime64[D]").values), + flight_dates) + +swift_candidates = np.unique(np.concatenate([swift.time.astype('datetime64[D]').values + for swift in swifts])) +# Dates with potential SWIFT/P-3 overlap +swift_dates = np.intersect1d(swift_candidates, axbt_dates) +``` + +For plotting purposes it'll be handy to define a one-day time window and to convert between date/time formats + +```{code-cell} ipython3 +one_day = np.timedelta64(1, "D") + +def to_datetime(dt64): + epoch = np.datetime64("1970-01-01") + second = np.timedelta64(1, "s") + return datetime.datetime.utcfromtimestamp((dt64 - epoch) / second) +``` + +Now we can make a map that shows where the AXBTs were deployed and where the SWIFTs +were on days there the two platforms overlapped + +```{code-cell} ipython3 +fig = plt.figure(figsize = (8.3, 9.4)) +ax = set_up_map(plt) +add_gridlines(ax) + +# +# AXBT locations +# +for d in axbt_dates: + flight = axbts.sel(time=slice(d, d + one_day)) + ax.scatter(flight.lon, flight.lat, + lw=2, alpha=0.5, color=color_of_day(d), + transform=ccrs.PlateCarree(), zorder=7, + label=f"{to_datetime(d):%m-%d}") + +# +# SWIFT locations on selected dates (where there's overlap) +# +for d in swift_dates: + flight = axbts.sel(time=slice(d, d + one_day)) + for swift in swifts: + drift = swift.sel(time = flight.time.mean(), method = "nearest") + ax.scatter(drift.lon, drift.lat, + alpha=1, color=color_of_day(d), + transform=ccrs.PlateCarree(), zorder=7, marker = "p") + +plt.legend(ncol=2,loc=(0.0,0.0),fontsize=12,framealpha=0.8,markerscale=1, + title="Flight date (MM-DD-2020)") +``` + +On 19 Jan and 3 Feb the AXBTs bracket the SWIFTs; on 23 Jan the SWIFTs are at +the southern end of the AXBT pattern. + +The next plot will focus on 19 Jan. +Let's look at the profile of ocean temperature in the first 150 m from the AXBTs +and compare the near-surface temperatures to the SWIFTs they are surrounding. + +```{code-cell} ipython3 +fig, ax = plt.subplots(figsize=[8.3, 9.4]) +d = np.datetime64("2020-01-19") +axbt_1day = axbts.sel(time=slice(d, d + one_day)) +# Swift data at mean of AXBT times +swifts_1day = [s.sel(time = axbt_1day.time.mean(), method = "nearest") for s in swifts] + +axbt_1day.temperature.where(axbt_1day.depth < 150).plot.line(y="depth", + add_legend=False, yincrease=False) +ax.set_xlabel("Sea water temperature (K)") +ax.set_ylabel("Depth (m)") + +# +# Inset plot! https://matplotlib.org/3.1.1/gallery/subplots_axes_and_figures/zoom_inset_axes.html +# +axin = ax.inset_axes([0.06, 0.84, 0.6, 0.12]) +axin.scatter([s.sea_water_temperature.values + 273.15 for s in swifts_1day], + # SWIFTs 16 and 17 report water temperature at 0.5 m depth; SWIFTs 23-25 report at 0.3 m + # See the variable long_name or Tables 8 and 9 of Quinn et al. + [0.5 if '0.5' in s.sea_water_temperature.long_name else 0.3 for s in swifts_1day], + color="0.25", + s = 1.5 * plt.rcParams['lines.markersize'] ** 2) +axbt_1day.temperature.where(axbt_1day.depth < 3).plot.line(y="depth", + add_legend=False, + yincrease=False, ax = axin) +axin.set_xlabel("Sea water temperature (K)") +axin.set_ylabel("Depth (m)") +ax.indicate_inset_zoom(axin) +``` diff --git a/how_to_eurec4a/p3_flight_tracks.md b/how_to_eurec4a/p3_flight_tracks.md new file mode 100644 index 00000000..a1d2b5c9 --- /dev/null +++ b/how_to_eurec4a/p3_flight_tracks.md @@ -0,0 +1,194 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.11.2 +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Flight tracks + +The P3 flew 95 hours of observations over eleven flights, many of which were coordinated +with the NOAA research ship R/V Ronald H. Brown and autonomous platforms deployed +from the ship. Each flight contained a mixture of sampling strategies including: +high-altitude circles with frequent dropsonde deployment to characterize the large-scale +environment; slow descents and ascents to measure the distribution of water vapor +and its isotopic composition; stacked legs aimed at sampling the microphysical +and thermodynamic state of the boundary layer; and offset straight flight legs for +observing clouds and the ocean surface with remote sensing instruments and the +thermal structure of the ocean with _in situ_ sensors dropped from the plane. + +As a result of this diverse sampling the flight tracks are much more variable +then for most of the other aircraft. + +General setup: + +```{code-cell} ipython3 +import xarray as xr +import numpy as np +import datetime +# +# Related to plotting +# +import matplotlib.pyplot as plt +plt.style.use(["./mplstyle/book"]) +%matplotlib inline +``` + +Now access the flight track data. + +```{code-cell} ipython3 +import eurec4a +cat = eurec4a.get_intake_catalog() +``` + +Mapping takes quite some setup. Maybe we'll encapsulate this later but for now we repeat code +in each notebook. + +```{code-cell} ipython3 +:tags: [hide-cell] + +import matplotlib as mpl +import matplotlib.ticker as mticker + +import cartopy.crs as ccrs +from cartopy.feature import LAND +from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER +def ax_to_map(ax, lon_w = -60.5, lon_e = -49, lat_s = 10, lat_n = 16.5): + # Defining boundaries of the plot + ax.set_extent([lon_w,lon_e,lat_s,lat_n]) # lon west, lon east, lat south, lat north + ax.coastlines(resolution='10m',linewidth=1.5,zorder=1); + ax.add_feature(LAND,facecolor='0.9') + +def set_up_map(plt, lon_w = -60.5, lon_e = -49, lat_s = 10, lat_n = 16.5): + ax = plt.axes(projection=ccrs.PlateCarree()) + ax_to_map(ax, lon_w, lon_e, lat_s, lat_n) + return(ax) + +def add_gridlines(ax): + # Assigning axes ticks + xticks = np.arange(-65,0,2.5) + yticks = np.arange(0,25,2.5) + gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=1, + color='black', alpha=0.5, linestyle='dotted') + gl.xlocator = mticker.FixedLocator(xticks) + gl.ylocator = mticker.FixedLocator(yticks) + gl.xformatter = LONGITUDE_FORMATTER + gl.yformatter = LATITUDE_FORMATTER + gl.xlabel_style = {'size': 10, 'color': 'k'} + gl.ylabel_style = {'size': 10, 'color': 'k'} + gl.right_labels = False + gl.bottom_labels = False + gl.xlabel = {'Latitude'} +``` + +What days did the P-3 fly on? We can find out via the flight segmentation files. + +```{code-cell} ipython3 +# On what days did the P-3 fly? These are UTC date +all_flight_segments = eurec4a.get_flight_segments() +flight_dates = np.unique([np.datetime64(flight["takeoff"]).astype("datetime64[D]") + for flight in all_flight_segments["P3"].values()]) +``` + +Now set up colors to code each flight date during the experiment. One could choose +a categorical palette so the colors were as different from each other as possible. +Here we'll choose from a continuous set that spans the experiment so days that are +close in time are also close in color. + +```{code-cell} ipython3 +:tags: [hide-cell] + +# Like mpl.colors.Normalize but works also with datetime64 objects +def mk_norm(vmin, vmax): + def norm(values): + return (values - vmin) / (vmax - vmin) + return norm +norm = mk_norm(np.datetime64("2020-01-15"), + np.datetime64("2020-02-15")) + +# color map for things coded by flight date +# Sample from a continuous color map running from start to end of experiment +def color_of_day(day): + return plt.cm.viridis(norm(day), alpha=0.9) +``` + +For plotting purposes it'll be handy to define a one-day time window and to convert between date/time formats + +```{code-cell} ipython3 +one_day = np.timedelta64(1, "D") + +def to_datetime(dt64): + epoch = np.datetime64("1970-01-01") + second = np.timedelta64(1, "s") + return datetime.datetime.utcfromtimestamp((dt64 - epoch) / second) +``` + +Most platforms available from the EUREC4A `intake` catalog have a `tracks` element but +we'll use the `flight-level` data instead. We're using `xr.concat()` with one `dask` array per day +to avoid loading the whole dataset into memory at once. + +```{code-cell} ipython3 +nav_data = xr.concat([entry.to_dask().chunk() for entry in cat.P3.flight_level.values()], + dim = "time") +``` + +A map showing each of the eleven flight tracks: + +```{code-cell} ipython3 +fig = plt.figure(figsize = (12,13.6)) +ax = set_up_map(plt) +add_gridlines(ax) + +for d in flight_dates: + flight = nav_data.sel(time=slice(d, d + one_day)) + ax.plot(flight.lon, flight.lat, + lw=2, alpha=0.5, c=color_of_day(d), + transform=ccrs.PlateCarree(), zorder=7, + label=f"{to_datetime(d):%m-%d}") + +plt.legend(ncol=3, loc=(0.0,0.0), fontsize=14, framealpha=0.8, markerscale=5, + title="Flight date (MM-DD-2020)") +``` + +Most dropsondes were deployed from regular dodecagons during the first part of the +experiment with short turns after each dropsonde providing an off-nadir look at +the ocean surface useful for calibrating the W-band radar. A change in pilots midway +through the experiment led to dropsondes being deployed from circular flight tracks +starting on 31 Jan. AXBTs were deployed in lawnmower patterns (parallel offset legs) +with small loops sometimes employed to lengthen the time between AXBT deployment +to allow time for data acquisition given the device's slow fall speeds. Profiling +and especially _in situ_ cloud sampling legs sometimes deviated from straight paths to avoid hazardous weather. + + +Side view using the same color scale: + +```{code-cell} ipython3 +fig = plt.figure(figsize = (8.3,5)) +ax = plt.axes() + +for d in flight_dates: + flight = nav_data.sel(time=slice(d, d + one_day)) + flight = flight.where(flight.alt > 80, drop=True) # Proxy for take-off time + plt.plot(flight.time - flight.time.min(), flight.alt/1000., + lw=2, alpha=0.5, c=color_of_day(d), + label=f"{to_datetime(d):%m-%d}") + +plt.xticks(np.arange(10) * 3600 * 1e9, labels = np.arange(10)) +ax.set_xlabel("Time after flight start (h)") +ax.set_ylabel("Aircraft altitude (km)") +``` + +Sondes were dropped from the P-3 at about 7.5 km, with each circle taking roughly an hour; +transits were frequently performed at this level to conserve fuel. Long intervals +near 3 km altitude were used to deploy AXBTs and/or characterize the ocean surface +with remote sensing. Stepped legs indicate times devoted to _in situ_ cloud sampling. +On most flights the aircraft climbed quickly to roughly 7.5 km, partly to deconflict +with other aircraft participating in the experiment. On the three night flights, +however, no other aircraft were operating at take-off times and cloud sampling +was performed first, nearer Barbados than on other flights. diff --git a/how_to_eurec4a/p3_humidity_comparison.md b/how_to_eurec4a/p3_humidity_comparison.md new file mode 100644 index 00000000..bdcdd503 --- /dev/null +++ b/how_to_eurec4a/p3_humidity_comparison.md @@ -0,0 +1,104 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.11.2 +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Humidity comparison: Hygrometer and isotope analyzer + +During EUREC4A and ATOMIC the P3 made two _in situ_ measurements of humidity, one +with the normal chilled-mirror hygrometer, and one with a cavity ring down spectrometer. +The spectrometer's main purpose was to measure isotopes of water vapor but it's +good for the total humidty as well. + +For this example we need to plot some `xarray` datasets from the catalog. + +```{code-cell} ipython3 +import xarray as xr +import numpy as np + +import matplotlib.pyplot as plt +plt.style.use(["./mplstyle/book"]) +import colorcet as cc +%matplotlib inline + +import eurec4a +cat = eurec4a.get_intake_catalog() +``` + +We'll create a combined data with relative humidity from the aircraft hygrometer +("fl" means "flight level") and the water vapor isotope analyzer ("iso") +from a single flight. Drop (remove) times when the aircraft is on the ground. + +```{code-cell} ipython3 +fl = cat.P3.flight_level["P3-0119"].to_dask() +pi = cat.P3.isotope_analyzer.water_vapor_1hz["P3-0119"].to_dask() +rhs = xr.Dataset({"press" :fl["press"], + "alt" :fl["alt"], + "rh_p3" :fl["RH"], + "rh_iso":pi["rh_iso"]}) +rhs = rhs.where(rhs.alt > 80., drop = True) +``` + +Adriana Bailey from NCAR, who was responsible for the isotope analyzer, finds +that while the two instruments agree most of the time, the hygrometer is subject +to both overshooting (e.g. when the measured signal surpasses the expected value +following a rapid rise in environmental water vapor concentration) and ringing +(i.e. rapid oscillations around the expected value) during rapid and large changes in +water vapor concentration. + +Here are two figures to illustrate the problem and shows why you'd want to use the +water vapor measurements from the isotope analyzer when they're available. The top +panel combines data from two profiles and shows large excursions in the water vapor +measured by the hygrometer. The lower panel shows that most measurements agree +well but it's clear that it's better to use the the isotope analyzer measurements of +humidity during the experiment. + +```{code-cell} ipython3 +fig, (ax1, ax2) = plt.subplots(nrows=2, sharex = True, figsize = (8.3, 16.6)) +# +# One time window +# +profiles = rhs.sel(time = slice(np.datetime64("2020-01-19T15:36:00"), + np.datetime64("2020-01-19T16:07:40"))) +marker = "." +ax1.scatter(profiles["rh_p3" ], profiles["press"], + color=cc.glasbey[3], marker = marker, label = "Hygrometer") +ax1.scatter(profiles["rh_iso"], profiles["press"], + color="0.5", marker = marker, label = "Isotope analyzer") +# +# A second time window, shown as squares of roughly the same size +# +profiles = rhs.sel(time = slice(np.datetime64("2020-01-19T20:31:12"), + np.datetime64("2020-01-19T20:41:16"))) +marker = "s" +ax1.scatter(profiles["rh_p3" ], profiles["press"], + color=cc.glasbey[3], marker = marker, s = .2 * plt.rcParams['lines.markersize']**2) +ax1.scatter(profiles["rh_iso"], profiles["press"], + color="0.5", marker = marker, s = .2 * plt.rcParams['lines.markersize']**2) + + +ax1.invert_yaxis() +ax1.legend(markerscale = 2) +ax1.set_xlim(0, 100) +ax1.set_ylabel("pressure (hPa)") + +ax1.annotate('Ringing', ( 5,890), fontsize="large") +ax1.annotate('Overshooting',(45,700), fontsize="large") +# +# Picarro vs. hygrometer RH - they mostly agree +# +ax2.scatter(rhs["rh_p3"],rhs["rh_iso"], s=3) +# 1:1 line +ax2.plot([0,102], [0,102], 'k-', color = 'black') +ax2.set_xlim(0, 102) +ax2.set_xlabel("Hygrometer relative humidity (%)") +ax2.set_ylabel("Picarro relative humidity (%)") +``` diff --git a/how_to_eurec4a/p3_wband_radar.md b/how_to_eurec4a/p3_wband_radar.md new file mode 100644 index 00000000..238a2e9c --- /dev/null +++ b/how_to_eurec4a/p3_wband_radar.md @@ -0,0 +1,78 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.11.2 +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# W-band radar example + + During EUREC4A and ATOMIC NOAA deployed W-band (94 GHz) radars on both the P-3 aircraft + and the ship Ron Brown. The airborne radar was operated with 220 30-meter range gates + with a dwell time of 0.5 seconds. The minimum detectable reflectivity of -36 dBZ at a range of + 1 km although accurate estimates of Doppler properties require about -30 dBZ at 1 km. + +The data are available through the EUREC4A intake catalog. + +```{code-cell} ipython3 +import datetime + +import matplotlib.pyplot as plt +import colorcet as cc +%matplotlib inline + +import eurec4a +cat = eurec4a.get_intake_catalog() +``` + +We'll select an hour's worth of observations from a single flight day, and mask +out any observations with signal-to-noise ratio less than -10 dB. + +```{code-cell} ipython3 +time_slice = slice(datetime.datetime(2020, 1, 19, hour=18), + datetime.datetime(2020, 1, 19, hour=19)) + +cloud_params = cat.P3.remote_sensing['P3-0119'].to_dask().sel(time=time_slice) + +w_band = cat.P3.w_band_radar['P3-0119'].to_dask().sel(time=time_slice, height=slice(0,3)) +w_band = w_band.where(w_band.snr > -10) +``` + +The three main quantities measured by the radar are the reflectivity, the Doppler +velocity, and the spectral width. + +```{code-cell} ipython3 +fig = plt.figure(figsize = (12,10.2)) + +axes = fig.subplots(3, 1, sharex=True) +w_band.corrected_reflectivity.plot(x="time", y="height", + ax = axes[0], + vmin = -45, vmax = 20, + cmap = cc.m_bgy) +w_band.corrected_doppler_velocity.plot(x="time", y="height", + ax = axes[1], + vmin = -3, vmax = 3, + cmap = cc.m_coolwarm) + +w_band.spectral_width.plot(x="time", y="height", + ax = axes[2], + vmin = 0, vmax = 2, + cmap = cc.m_bmy) + +for ax in axes[0:2]: + ax.tick_params( + axis='x', # changes apply to the x-axis + which='both', # both major and minor ticks are affected + bottom=False, # ticks along the bottom edge are off + top=False, # ticks along the top edge are off + labelbottom=False) # labels along the bottom edge are off + ax.xaxis.set_visible(False) + +fig.subplots_adjust(bottom = 0) +``` diff --git a/how_to_eurec4a/p3_wsra.md b/how_to_eurec4a/p3_wsra.md new file mode 100644 index 00000000..634e0d79 --- /dev/null +++ b/how_to_eurec4a/p3_wsra.md @@ -0,0 +1,123 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.11.2 +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# WSRA example: surface wave state + +During EUREC4A/ATOMIC the P-3 flew with a Wide Swath Radar Altimeter (WSRA), +a digital beam-forming radar altimeter operating at 16 GHz in the Ku band. It +generates 80 narrow beams spread over 30 deg to produce a topographic map of the +sea surface waves and their backscattered power. These measurements allow for +continuous reporting of directional ocean wave spectra and quantities derived from +this including significant wave height, sea surface mean square slope, and the +height, wavelength, and direction of propagation of primary and secondary wave fields. +WSRA measurements are processed by the private company [ProSensing](https://www.prosensing.com), +which designed and built the instrument. + +The WSRA also produces rainfall rate estimates from path-integrated attenuation but +we won't look at those here. + +The data are available through the EUREC4A intake catalog. + +```{code-cell} ipython3 +import xarray as xr +import numpy as np + +import matplotlib.pyplot as plt +plt.style.use(["./mplstyle/book"]) +import colorcet as cc +%matplotlib inline + +import eurec4a +cat = eurec4a.get_intake_catalog() +``` + +Mapping takes quite some setup. Maybe we'll encapsulate this later but for now we repeat code +in each notebook. + +```{code-cell} ipython3 +:tags: [hide-cell] + +import matplotlib.ticker as mticker + +import cartopy.crs as ccrs +from cartopy.feature import LAND +from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER + +def ax_to_map(ax, lon_w = -60.5, lon_e = -49, lat_s = 10, lat_n = 16.5): + # Defining boundaries of the plot + ax.set_extent([lon_w,lon_e,lat_s,lat_n]) # lon west, lon east, lat south, lat north + ax.coastlines(resolution='10m',linewidth=1.5,zorder=1); + ax.add_feature(LAND,facecolor='0.9') + +def set_up_map(plt, lon_w = -60.5, lon_e = -49, lat_s = 10, lat_n = 16.5): + ax = plt.axes(projection=ccrs.PlateCarree()) + ax_to_map(ax, lon_w, lon_e, lat_s, lat_n) + return(ax) + +def add_gridlines(ax): + # Assigning axes ticks + xticks = np.arange(-65,0,2.5) + yticks = np.arange(0,25,2.5) + gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=1, + color='black', alpha=0.5, linestyle='dotted') + gl.xlocator = mticker.FixedLocator(xticks) + gl.ylocator = mticker.FixedLocator(yticks) + gl.xformatter = LONGITUDE_FORMATTER + gl.yformatter = LATITUDE_FORMATTER + gl.xlabel_style = {'size': 10, 'color': 'k'} + gl.ylabel_style = {'size': 10, 'color': 'k'} + gl.right_labels = False + gl.bottom_labels = False + gl.xlabel = {'Latitude'} +``` + +We'll select an hour's worth of observations from a single flight day. WSRA data +are stored as "trajectories" - discrete times with associated positions and +observations. + +```{code-cell} ipython3 +wsra_example = cat.P3.wsra["P3-0119"].to_dask().sel(trajectory=slice(0,293)) +``` + +Now it's interesting to see how the wave slope (top panel) and the wave height (bottom) +vary spatially on a given day. + +```{code-cell} ipython3 +fig, (ax1, ax2) = plt.subplots(nrows=2, sharex = True, figsize = (12,8), + subplot_kw={'projection': ccrs.PlateCarree()}) + +# +# Mean square slope +# +ax_to_map(ax1, lon_e=-50, lon_w=-54.5, lat_s = 13, lat_n = 16) +add_gridlines(ax1) +pts = ax1.scatter(wsra_example.longitude,wsra_example.latitude, + c=wsra_example.sea_surface_mean_square_slope_median, + vmin = 0.02, vmax=0.04, + cmap=cc.cm.fire, + alpha=0.5, + transform=ccrs.PlateCarree(),zorder=7) +fig.colorbar(pts, ax=ax1, shrink=0.75, aspect=10, label="Mean Square Slope (rad$^2$)") +# +# Significant wave height +# +ax_to_map(ax2, lon_e=-50, lon_w=-54.5, lat_s = 13, lat_n = 16) +add_gridlines(ax2) +pts = ax2.scatter(wsra_example.longitude,wsra_example.latitude, + c=wsra_example.sea_surface_wave_significant_height, + vmin = 1, vmax=5, + cmap=cc.cm.bgy, + alpha=0.5, + transform=ccrs.PlateCarree(),zorder=7) +fig.colorbar(pts, ax=ax2, shrink=0.75, aspect=10, label="Significant Wave Height (m)") +``` diff --git a/how_to_eurec4a/pthree.md b/how_to_eurec4a/pthree.md new file mode 100644 index 00000000..da8dcb00 --- /dev/null +++ b/how_to_eurec4a/pthree.md @@ -0,0 +1,26 @@ +# NOAA P3 "Miss Piggy" + +During EUREC4A and ATOMIC the US agency NOAA operated a Lockheed WP-3D Orion research aircraft +from the island of Barbados during the period Jan 17 - Feb 11 2020. The aircraft, +known formally as N43RF and informally as "Miss Piggy," is one of two such aircraft +in NOAA's Hurricane Hunter fleet. (The other is known as "Kermit.") + +ATOMIC included a cruise by the NOAA ship Ronald H. Brown (RHB). Both the P-3 and the RHB +primarily operated east of the EUREC4A area (i.e. east of 57E), nominally upwind, +within the "Tradewind Alley" extending eastwards from Barbados towards the +Northwest Tropical Atlantic Station (NTAS) buoy near 15N, 51W. Many of the eleven +P-3 flights included excursions to the location of the RHB and sampling of +atmospheric and oceanic conditions around the ship and other ocean vehicles. Because +of its large size and long endurance (most flights were 8-9 hours long) the P-3 +was tasked with obtaining a wide array of observations including remote sensing +of clouds and the ocean surface, _in situ_ measurements within clouds and of +isotopic composition throughout the lower troposphere, and the deployment of +expendable profiling instruments in the atmosphere and ocean. + +Most observations obtained from the P-3 are described in {cite}`Pincus:2021`. + +```{figure} Miss-Piggy.jpeg +:alt: NOAA WP-3D "Miss Piggy" +:width: 600px +:align: center +``` diff --git a/how_to_eurec4a/running_locally.md b/how_to_eurec4a/running_locally.md index 0bac5093..bf068bf9 100644 --- a/how_to_eurec4a/running_locally.md +++ b/how_to_eurec4a/running_locally.md @@ -13,10 +13,28 @@ You can decide between the [quick an dirty](#quick-and-dirty) method and the met If you just like to run the code of a single notebook and don't care to much about the details, the quickest option might be to download the chapter you are viewing as an ipython notebook (`.ipynb`) via the download button () on the top right of the page. If you don't see the `.ipynb` option here, that's because the source of the page can not be interpreted as a notebook and thus is not available for direct execution. If you would just run the downloaded code, the chance is high that some required libraries are not yet installed on your system. You can either do try and error to find out which libraries are required for the chapter you downloaded, or you can simply installed all requirements for the entire book by running the following command on your command line: +````{panels} +Using pip +^^^ ```bash pip install jupyter pip install -r https://raw.githubusercontent.com/eurec4a/how_to_eurec4a/master/requirements.txt ``` ++++ +This won't work with any notebooks that use `cartopy` to make maps, `pip` does not manage +their dependencies well. +--- +Using conda +^^^ +```bash +wget https://raw.githubusercontent.com/eurec4a/how_to_eurec4a/master/requirements.txt +conda create -f requirements.txt +conda activate how_to_eurec4a +``` ++++ +This creates a conda environment called `how_to_eurec4a` which contains all dependencies including +`cartopy` +```` Afterwards, you can start a local notebook server (either `jupyter notebook` or `jupyter lab`) and run and modify the chapter locally. @@ -37,9 +55,9 @@ Please change into this directory. ````{admonition} Maybe use a virtual environment :class: dropdown, tip -You might want to use a virtual environment for the book if you like to keep the required libraries in a confined place, but it is entirely optional and up to your preferences. +If you use pip you might want to use a virtual environment for the book if you like to keep the required libraries in a confined place, but it is entirely optional and up to your preferences. There are many options to do so and [virtualenv](https://virtualenv.pypa.io/) is one of them. -Using virtualenv, you could create and aktivate an environment like: +Using virtualenv, you could create and activate an environment like: ```bash virtualenv venv . venv/bin/activate @@ -48,9 +66,27 @@ and the continue normally. ```` You'll have to install the dependencies as above, but as you already have all the files on your machine, you can also install it directly via: +````{panels} +Using pip +^^^ ```bash +pip install jupyter pip install -r requirements.txt ``` ++++ +This won't work with any notebooks that use `cartopy` to make maps, `pip` does not manage +their dependencies well. +--- +Using conda +^^^ +```bash +conda create -f requirements.txt +conda activate how_to_eurec4a +``` ++++ +This creates a conda environment called `how_to_eurec4a` which contains all dependencies including +`cartopy` +```` Depending on your needs, you can continue using [interactive notebooks](#interactive) or [compile the entire book](#compile-the-book). @@ -67,7 +103,7 @@ If that does not work, please have a look at the [installation instructions](htt ``` ### interactive -`jupyter` itself is not installed by the requirements file. You might want to install it as well: +`jupyter` itself is not installed by the requirements file. If you're using `pip` you might want to install it as well: ```bash pip install jupyter diff --git a/requirements.txt b/requirements.txt index ebad8f83..53bd35db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,3 +13,4 @@ aiohttp pydap ipyleaflet simplification +colorcet