QU39 Bloom Timing Check and Time Series

To run this notebook, pickle files for each year of interest must be created in the ‘makePickles201905’ notebook.

[1]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib as mpl
import netCDF4 as nc
import datetime as dt
from salishsea_tools import evaltools as et, places, viz_tools, visualisations, bloomdrivers
import xarray as xr
import pandas as pd
import pickle
import os

%matplotlib inline

To recreate this notebook at a different location, follow these instructions: Change only the values in the following cell. If you change the startyear and endyear, the xticks (years) in the plots will need to be adjusted accordingly. If you did not make pickle files for 201812 bloom timing variables, remove the cell that loads that data ‘Load bloom timing variables for 201812 run’, as well as a section from the cell that plots bloom timing; ‘Bloom Date’.

[2]:
# The path to the directory where the pickle files are stored:
savedir='/ocean/aisabell/MEOPAR/extracted_files'

# Change 'S3' to the location of interest
loc='QU39'
# Note: x and y limits in the following cell (map of location) may need to be adjusted

# What is the start year and end year+1 of the time range of interest?
startyear=2007
endyear=2021 # does NOT include this value

# Note: pickle file with 'non-location specific variables' only need to be created for each year, not for each location
# Note: xticks (years) in the plots will need to be changed
# Note: 201812 bloom timing variable load and plotting will also need to be removed
[3]:
modver='201905'
# lat and lon information for place:
lon,lat=places.PLACES[loc]['lon lat']
# get place information on SalishSeaCast grid:
ij,ii=places.PLACES[loc]['NEMO grid ji']
jw,iw=places.PLACES[loc]['GEM2.5 grid ji']

fig, ax = plt.subplots(1,1,figsize = (6,6))
with xr.open_dataset('/data/vdo/MEOPAR/NEMO-forcing/grid/mesh_mask201702.nc') as mesh:
    ax.contour(mesh.nav_lon,mesh.nav_lat,mesh.tmask.isel(t=0,z=0),[0.1,],colors='k')
    tmask=np.array(mesh.tmask)
    gdept_1d=np.array(mesh.gdept_1d)
    e3t_0=np.array(mesh.e3t_0)
ax.plot(lon, lat, '.', markersize=14, color='red')
ax.set_ylim(48,51)
ax.set_xlim(-126,-121)
ax.set_title('Location of Station %s'%loc)
ax.set_xlabel('Longitude')
ax.set_ylabel('Latitude')
viz_tools.set_aspect(ax,coords='map')
[3]:
1.1363636363636362
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_5_1.png

Strait of Georgia Region

[4]:
# define sog region:
fig, ax = plt.subplots(1,2,figsize = (6,6))
with xr.open_dataset('/data/vdo/MEOPAR/NEMO-forcing/grid/bathymetry_201702.nc') as bathy:
    bath=np.array(bathy.Bathymetry)
ax[0].contourf(bath,np.arange(0,250,10))
viz_tools.set_aspect(ax[0],coords='grid')
sogmask=np.copy(tmask[:,:,:,:])
sogmask[:,:,740:,:]=0
sogmask[:,:,700:,170:]=0
sogmask[:,:,550:,250:]=0
sogmask[:,:,:,302:]=0
sogmask[:,:,:400,:]=0
sogmask[:,:,:,:100]=0
#sogmask250[bath<250]=0
ax[1].contourf(np.ma.masked_where(sogmask[0,0,:,:]==0,bathy.Bathymetry),[0,100,250,550])
[4]:
<matplotlib.contour.QuadContourSet at 0x7f39ece2d730>
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_7_1.png

Stop and check, have you made pickle files for all the years?

Combine separate year files into arrays:

[5]:
# loop through years (for location specific drivers)

years=list()
windjan=list()
windfeb=list()
windmar=list()
solarjan=list()
solarfeb=list()
solarmar=list()
parjan=list()
parfeb=list()
parmar=list()
tempjan=list()
tempfeb=list()
tempmar=list()
saljan=list()
salfeb=list()
salmar=list()
zoojan=list()
zoofeb=list()
zoomar=list()
mesozoojan=list()
mesozoofeb=list()
mesozoomar=list()
microzoojan=list()
microzoofeb=list()
microzoomar=list()
intzoojan=list()
intzoofeb=list()
intzoomar=list()
intmesozoojan=list()
intmesozoofeb=list()
intmesozoomar=list()
intmicrozoojan=list()
intmicrozoofeb=list()
intmicrozoomar=list()
midno3jan=list()
midno3feb=list()
midno3mar=list()

for year in range(startyear,endyear):
    fname=f'JanToMarch_TimeSeries_{year}_{loc}_{modver}.pkl'
    savepath=os.path.join(savedir,fname)
    bio_time,diat_alld,no3_alld,flag_alld,cili_alld,microzoo_alld,mesozoo_alld,\
            intdiat,intphyto,spar,intmesoz,intmicroz,grid_time,temp,salinity,u_wind,v_wind,twind,\
            solar,no3_30to90m,sno3,sdiat,sflag,scili,intzoop,fracdiat,zoop_alld,sphyto,phyto_alld,\
            percdiat,wspeed,winddirec=pickle.load(open(savepath,'rb'))
    # put code that calculates drivers here
    wind=bloomdrivers.D1_3monthly_avg(twind,wspeed)
    solar=bloomdrivers.D1_3monthly_avg(twind,solar)
    par=bloomdrivers.D1_3monthly_avg(bio_time,spar)
    temp=bloomdrivers.D1_3monthly_avg(grid_time,temp)
    sal=bloomdrivers.D1_3monthly_avg(grid_time,salinity)
    zoo=bloomdrivers.D2_3monthly_avg(bio_time,zoop_alld)
    mesozoo=bloomdrivers.D2_3monthly_avg(bio_time,mesozoo_alld)
    microzoo=bloomdrivers.D2_3monthly_avg(bio_time,microzoo_alld)
    intzoo=bloomdrivers.D1_3monthly_avg(bio_time,intzoop)
    intmesozoo=bloomdrivers.D1_3monthly_avg(bio_time,intmesoz)
    intmicrozoo=bloomdrivers.D1_3monthly_avg(bio_time,intmicroz)
    midno3=bloomdrivers.D1_3monthly_avg(bio_time,no3_30to90m)

    years.append(year)
    windjan.append(wind[0])
    windfeb.append(wind[1])
    windmar.append(wind[2])
    solarjan.append(solar[0])
    solarfeb.append(solar[1])
    solarmar.append(solar[2])
    parjan.append(par[0])
    parfeb.append(par[1])
    parmar.append(par[2])
    tempjan.append(temp[0])
    tempfeb.append(temp[1])
    tempmar.append(temp[2])
    saljan.append(sal[0])
    salfeb.append(sal[1])
    salmar.append(sal[2])
    zoojan.append(zoo[0])
    zoofeb.append(zoo[1])
    zoomar.append(zoo[2])
    mesozoojan.append(mesozoo[0])
    mesozoofeb.append(mesozoo[1])
    mesozoomar.append(mesozoo[2])
    microzoojan.append(microzoo[0])
    microzoofeb.append(microzoo[1])
    microzoomar.append(microzoo[2])
    intzoojan.append(intzoo[0])
    intzoofeb.append(intzoo[1])
    intzoomar.append(intzoo[2])
    intmesozoojan.append(intmesozoo[0])
    intmesozoofeb.append(intmesozoo[1])
    intmesozoomar.append(intmesozoo[2])
    intmicrozoojan.append(intmicrozoo[0])
    intmicrozoofeb.append(intmicrozoo[1])
    intmicrozoomar.append(intmicrozoo[2])
    midno3jan.append(midno3[0])
    midno3feb.append(midno3[1])
    midno3mar.append(midno3[2])

years=np.array(years)
windjan=np.array(windjan)
windfeb=np.array(windfeb)
windmar=np.array(windmar)
solarjan=np.array(solarjan)
solarfeb=np.array(solarfeb)
solarmar=np.array(solarmar)
parjan=np.array(parjan)
parfeb=np.array(parfeb)
parmar=np.array(parmar)
tempjan=np.array(tempjan)
tempfeb=np.array(tempfeb)
tempmar=np.array(tempmar)
saljan=np.array(saljan)
salfeb=np.array(salfeb)
salmar=np.array(salmar)
zoojan=np.array(zoojan)
zoofeb=np.array(zoofeb)
zoomar=np.array(zoomar)
mesozoojan=np.array(mesozoojan)
mesozoofeb=np.array(mesozoofeb)
mesozoomar=np.array(mesozoomar)
microzoojan=np.array(microzoojan)
microzoofeb=np.array(microzoofeb)
microzoomar=np.array(microzoomar)
intzoojan=np.array(intzoojan)
intzoofeb=np.array(intzoofeb)
intzoomar=np.array(intzoomar)
intmesozoojan=np.array(intmesozoojan)
intmesozoofeb=np.array(intmesozoofeb)
intmesozoomar=np.array(intmesozoomar)
intmicrozoojan=np.array(intmicrozoojan)
intmicrozoofeb=np.array(intmicrozoofeb)
intmicrozoomar=np.array(intmicrozoomar)
midno3jan=np.array(midno3jan)
midno3feb=np.array(midno3feb)
midno3mar=np.array(midno3mar)
[6]:
# loop through years (for non-location specific drivers)
fraserjan=list()
fraserfeb=list()
frasermar=list()
deepno3jan=list()
deepno3feb=list()
deepno3mar=list()

for year in range(startyear,endyear):
    fname2=f'JanToMarch_TimeSeries_{year}_{modver}.pkl'
    savepath2=os.path.join(savedir,fname2)
    no3_past250m,riv_time,rivFlow=pickle.load(open(savepath2,'rb'))
    # Code that calculates drivers here
    fraser=bloomdrivers.D1_3monthly_avg2(riv_time,rivFlow)
    fraserjan.append(fraser[0])
    fraserfeb.append(fraser[1])
    frasermar.append(fraser[2])

    fname=f'JanToMarch_TimeSeries_{year}_{loc}_{modver}.pkl'
    savepath=os.path.join(savedir,fname)
    bio_time,diat_alld,no3_alld,flag_alld,cili_alld,microzoo_alld,mesozoo_alld,\
            intdiat,intphyto,spar,intmesoz,intmicroz,grid_time,temp,salinity,u_wind,v_wind,twind,\
            solar,no3_30to90m,sno3,sdiat,sflag,scili,intzoop,fracdiat,zoop_alld,sphyto,phyto_alld,\
            percdiat,wspeed,winddirec=pickle.load(open(savepath,'rb'))
    deepno3=bloomdrivers.D1_3monthly_avg(bio_time,no3_past250m)

    deepno3jan.append(deepno3[0])
    deepno3feb.append(deepno3[1])
    deepno3mar.append(deepno3[2])

fraserjan=np.array(fraserjan)
fraserfeb=np.array(fraserfeb)
frasermar=np.array(frasermar)
deepno3jan=np.array(deepno3jan)
deepno3feb=np.array(deepno3feb)
deepno3mar=np.array(deepno3mar)
[7]:
# loop through years (for mixing drivers)

halojan=list()
halofeb=list()
halomar=list()

for year in range(startyear,endyear):
    fname4=f'JanToMarch_Mixing_{year}_{loc}_{modver}.pkl'
    savepath4=os.path.join(savedir,fname4)
    halocline,eddy,depth,grid_time,temp,salinity=pickle.load(open(savepath4,'rb'))
    fname=f'JanToMarch_TimeSeries_{year}_{loc}_{modver}.pkl'
    savepath=os.path.join(savedir,fname)
    bio_time,diat_alld,no3_alld,flag_alld,cili_alld,microzoo_alld,mesozoo_alld,\
            intdiat,intphyto,spar,intmesoz,intmicroz,grid_time,temp,salinity,u_wind,v_wind,twind,\
            solar,no3_30to90m,sno3,sdiat,sflag,scili,intzoop,fracdiat,zoop_alld,sphyto,phyto_alld,\
            percdiat,wspeed,winddirec=pickle.load(open(savepath,'rb'))
    # put code that calculates drivers here
    halo=bloomdrivers.D1_3monthly_avg(grid_time,halocline)
    halojan.append(halo[0])
    halofeb.append(halo[1])
    halomar.append(halo[2])

halojan=np.array(halojan)
halofeb=np.array(halofeb)
halomar=np.array(halomar)

Metric 2 (unchanged)

[8]:
# Metric 2:
def metric2b_bloomtime(phyto_alld,no3_alld,bio_time):
    ''' Given datetime array and two 2D arrays of phytoplankton and nitrate concentrations, over time
        and depth, returns a datetime value of the spring phytoplankton bloom date according to the
        following definition (now called 'metric 2'):

            'The first peak in which chlorophyll concentrations in upper 3m are above 5 ug/L for more than two days'

            Parameters:
                    phyto_alld: 2D array of phytoplankton concentrations (in uM N) over all depths and time
                                range of 'bio_time'
                      no3_alld: 2D array of nitrate concentrations (in uM N) over all depths and time
                                range of 'bio_time'
                  bio_time: 1D datetime array of the same time frame as sphyto and sno3
            Returns:
                    bloomtime2: the spring bloom date as a single datetime value

    '''
    # a) get avg phytplankton in upper 3m
    phyto_alld_df=pd.DataFrame(phyto_alld)
    upper_3m_phyto=pd.DataFrame(phyto_alld_df[[0,1,2,3]].mean(axis=1))
    upper_3m_phyto.columns=['sphyto']
    #upper_3m_phyto

    # b) get average no3 in upper 3m
    no3_alld_df=pd.DataFrame(no3_alld)
    upper_3m_no3=pd.DataFrame(no3_alld_df[[0,1,2,3]].mean(axis=1))
    upper_3m_no3.columns=['sno3']
    #upper_3m_no3

    # make bio_time into a dataframe
    bio_time_df=pd.DataFrame(bio_time)
    bio_time_df.columns=['bio_time']
    df=pd.concat((bio_time_df,upper_3m_phyto,upper_3m_no3), axis=1)

    # to find all the peaks:
    df['phytopeaks'] = df.sphyto[(df.sphyto.shift(1) < df.sphyto) & (df.sphyto.shift(-1) < df.sphyto)]

    # need to covert the value of interest from ug/L to uM N (conversion factor: 1.8 ug Chl per umol N)
    chlvalue=5/1.8

    # extract the bloom time date
    for i, row in df.iterrows():
        try:
            if df['sphyto'].iloc[i-1]>chlvalue and df['sphyto'].iloc[i-2]>chlvalue and pd.notna(df['phytopeaks'].iloc[i]):
                bloomtime2=df.bio_time[i]
                break
            elif df['sphyto'].iloc[i+1]>chlvalue and df['sphyto'].iloc[i+2]>chlvalue and pd.notna(df['phytopeaks'].iloc[i]):
                bloomtime2=df.bio_time[i]
                break
        except IndexError:
            bloomtime2=np.nan
            print('bloom not found')
    return bloomtime2

metric 3(changed to rolling average)

[9]:
# Metric 3:
def metric3b_bloomtime(sphyto,bio_time):
    ''' Given datetime array and a 1D array of surface phytplankton and nitrate concentrations
        over time, returns a datetime value of the spring phytoplankton bloom date according to the
        following definition (now called 'metric 3'):

            'The median + 5% of the annual Chl concentration is deemed “threshold value” for each year.
            For a given year, bloom initiation is determined to be the week that first reaches the
            threshold value (by looking at weekly averages) as long as one of the two following weeks
            was >70% of the threshold value'

            Parameters:
                    sphyto: 1D array of phytoplankton concentrations (in uM N) over time
                                range of 'bio_time'
                  bio_time: 1D datetime array of the same time frame as sphyto and sno3
            Returns:
                    bloomtime3: the spring bloom date as a single datetime value

    '''
    # 1) determine threshold value
    df = pd.DataFrame({'bio_time':bio_time, 'sphyto':sphyto})

    # a) find median chl value of that year, add 5% (this is only feb-june, should we do the whole year?)
    threshold=df['sphyto'].median()*1.05
    # b) secondthresh = find 70% of threshold value
    secondthresh=threshold*0.7

    # 2) Take the average of each week and make a dataframe with start date of week and weekly average
    weeklychl = pd.DataFrame(df.rolling(7, on='bio_time').mean())
    weeklychl.reset_index(inplace=True)

    # 3) Loop through the weeks and find the first week that reaches the threshold.
        # Is one of the two week values after this week > secondthresh?

    for i, row in weeklychl.iterrows():
        try:
            if weeklychl['sphyto'].iloc[i]>threshold and weeklychl['sphyto'].iloc[i+7]>secondthresh:
                bloomtime3=weeklychl.bio_time[i]
                break
            elif weeklychl['sphyto'].iloc[i]>threshold and weeklychl['sphyto'].iloc[i+14]>secondthresh:
                bloomtime3=weeklychl.bio_time[i]
                break
        except IndexError:
            bloomtime2=np.nan
            print('bloom not found')

    return bloomtime3

example year to check that metrics are working

[10]:
# loop through years of spring time series (mid feb-june) for bloom timing for 201905 run
year=2018

fname3=f'springBloomTime_{str(year)}_{loc}_{modver}.pkl'
savepath3=os.path.join(savedir,fname3)
bio_time0,sno30,sdiat0,sflag0,scili0,diat_alld0,no3_alld0,flag_alld0,cili_alld0,phyto_alld0,\
             intdiat0,intphyto0,fracdiat0,sphyto0,percdiat0=pickle.load(open(savepath3,'rb'))
# put code that calculates bloom timing here
bt2b=metric2b_bloomtime(phyto_alld0,no3_alld0,bio_time0)
bt3b=metric3b_bloomtime(sphyto0,bio_time0)

bloomtime2b=np.array(bt2b)
bloomtime3b=np.array(bt3b)

print(bloomtime2b)
print(bloomtime3b)
2018-03-21 12:30:00
2018-03-17 12:30:00

Check metric 2:

[11]:
# Check metric 2:
# a) get avg phytplankton in upper 3m
phyto_alld_df=pd.DataFrame(phyto_alld0)
upper_3m_phyto=pd.DataFrame(phyto_alld_df[[0,1,2,3]].mean(axis=1))
upper_3m_phyto.columns=['sphyto']
#upper_3m_phyto

# b) get average no3 in upper 3m
no3_alld_df=pd.DataFrame(no3_alld0)
upper_3m_no3=pd.DataFrame(no3_alld_df[[0,1,2,3]].mean(axis=1))
upper_3m_no3.columns=['sno3']
#upper_3m_no3

# make bio_time into a dataframe
bio_time_df=pd.DataFrame(bio_time0)
bio_time_df.columns=['bio_time']
df2b=pd.concat((bio_time_df,upper_3m_phyto,upper_3m_no3), axis=1)

# to find all the peaks:
df2b['phytopeaks'] = df2b.sphyto[(df2b.sphyto.shift(1) < df2b.sphyto) & (df2b.sphyto.shift(-1) < df2b.sphyto)]

# need to covert the value of interest from ug/L to uM N (conversion factor: 1.8 ug Chl per umol N)
chlvalue=5/1.8

[12]:
len(bio_time0)
[12]:
120
[13]:
chlvalue
[13]:
2.7777777777777777
[14]:
df2b.head(50)
[14]:
bio_time sphyto sno3 phytopeaks
0 2018-02-15 12:30:00 0.518133 23.955753 NaN
1 2018-02-16 12:30:00 0.560931 23.736099 0.560931
2 2018-02-17 12:30:00 0.541665 23.609259 NaN
3 2018-02-18 12:30:00 0.531890 24.018570 NaN
4 2018-02-19 12:30:00 0.513094 24.636204 NaN
5 2018-02-20 12:30:00 0.558158 24.006018 NaN
6 2018-02-21 12:30:00 0.625944 23.227230 NaN
7 2018-02-22 12:30:00 0.654409 22.818062 NaN
8 2018-02-23 12:30:00 0.664832 23.240711 0.664832
9 2018-02-24 12:30:00 0.512265 24.142550 NaN
10 2018-02-25 12:30:00 0.586309 23.639738 NaN
11 2018-02-26 12:30:00 0.641206 23.256149 NaN
12 2018-02-27 12:30:00 0.642998 23.203081 0.642998
13 2018-02-28 12:30:00 0.610948 23.353760 NaN
14 2018-03-01 12:30:00 0.317003 25.394751 NaN
15 2018-03-02 12:30:00 0.497790 23.681625 NaN
16 2018-03-03 12:30:00 0.620862 22.602259 NaN
17 2018-03-04 12:30:00 0.699807 21.920408 NaN
18 2018-03-05 12:30:00 0.766631 21.571028 NaN
19 2018-03-06 12:30:00 0.829634 21.483400 NaN
20 2018-03-07 12:30:00 0.922550 21.191242 NaN
21 2018-03-08 12:30:00 0.942459 21.012884 0.942459
22 2018-03-09 12:30:00 0.933780 21.178497 NaN
23 2018-03-10 12:30:00 1.013542 21.250294 NaN
24 2018-03-11 12:30:00 1.090128 20.911163 NaN
25 2018-03-12 12:30:00 1.331080 19.901075 NaN
26 2018-03-13 12:30:00 1.549234 19.146080 NaN
27 2018-03-14 12:30:00 1.552638 19.358665 1.552638
28 2018-03-15 12:30:00 1.539955 19.763485 NaN
29 2018-03-16 12:30:00 1.961931 18.593563 NaN
30 2018-03-17 12:30:00 2.632990 16.720835 NaN
31 2018-03-18 12:30:00 3.409768 15.069412 NaN
32 2018-03-19 12:30:00 4.525090 12.949618 NaN
33 2018-03-20 12:30:00 5.390754 11.931779 NaN
34 2018-03-21 12:30:00 6.501463 10.648335 6.501463
35 2018-03-22 12:30:00 4.843561 15.289260 NaN
36 2018-03-23 12:30:00 1.860669 21.907494 NaN
37 2018-03-24 12:30:00 1.774011 21.843761 NaN
38 2018-03-25 12:30:00 4.362767 16.256571 4.362767
39 2018-03-26 12:30:00 3.079419 18.749878 NaN
40 2018-03-27 12:30:00 1.407858 22.100061 NaN
41 2018-03-28 12:30:00 3.007690 17.239632 NaN
42 2018-03-29 12:30:00 4.430298 9.782684 4.430298
43 2018-03-30 12:30:00 4.068786 10.408074 NaN
44 2018-03-31 12:30:00 4.033803 7.243953 NaN
45 2018-04-01 12:30:00 4.423335 4.833238 4.423335
46 2018-04-02 12:30:00 2.943024 10.014181 NaN
47 2018-04-03 12:30:00 2.173126 9.957188 NaN
48 2018-04-04 12:30:00 1.645184 14.719564 NaN
49 2018-04-05 12:30:00 1.076441 18.206799 NaN

Metric 2 is doing what it is supposed to be doing

check metric 3:

[15]:
# check metric 3:
# 1) determine threshold value
df = pd.DataFrame({'bio_time':bio_time0, 'sphyto':sphyto0})

# a) find median chl value of that year, add 5% (this is only feb-june, should we do the whole year?)
threshold=df['sphyto'].median()*1.05
# b) secondthresh = find 70% of threshold value
secondthresh=threshold*0.7

# 2) Take the average of each week and make a dataframe with start date of week and weekly average
weeklychl = pd.DataFrame(df.rolling(7, on='bio_time').mean())
weeklychl.reset_index(inplace=True)

print(threshold)
print(secondthresh)
weeklychl.head(60)
1.5330673813819886
1.073147166967392
[15]:
index bio_time sphyto
0 0 2018-02-15 12:30:00 NaN
1 1 2018-02-16 12:30:00 NaN
2 2 2018-02-17 12:30:00 NaN
3 3 2018-02-18 12:30:00 NaN
4 4 2018-02-19 12:30:00 NaN
5 5 2018-02-20 12:30:00 NaN
6 6 2018-02-21 12:30:00 0.553067
7 7 2018-02-22 12:30:00 0.572784
8 8 2018-02-23 12:30:00 0.586768
9 9 2018-02-24 12:30:00 0.582421
10 10 2018-02-25 12:30:00 0.590560
11 11 2018-02-26 12:30:00 0.609113
12 12 2018-02-27 12:30:00 0.620490
13 13 2018-02-28 12:30:00 0.619089
14 14 2018-03-01 12:30:00 0.571099
15 15 2018-03-02 12:30:00 0.547893
16 16 2018-03-03 12:30:00 0.564366
17 17 2018-03-04 12:30:00 0.580490
18 18 2018-03-05 12:30:00 0.599302
19 19 2018-03-06 12:30:00 0.627254
20 20 2018-03-07 12:30:00 0.670663
21 21 2018-03-08 12:30:00 0.758632
22 22 2018-03-09 12:30:00 0.820857
23 23 2018-03-10 12:30:00 0.875245
24 24 2018-03-11 12:30:00 0.933015
25 25 2018-03-12 12:30:00 1.007605
26 26 2018-03-13 12:30:00 1.099691
27 27 2018-03-14 12:30:00 1.189634
28 28 2018-03-15 12:30:00 1.281203
29 29 2018-03-16 12:30:00 1.428852
30 30 2018-03-17 12:30:00 1.661942
31 31 2018-03-18 12:30:00 1.990871
32 32 2018-03-19 12:30:00 2.437461
33 33 2018-03-20 12:30:00 2.992171
34 34 2018-03-21 12:30:00 3.701179
35 35 2018-03-22 12:30:00 4.192181
36 36 2018-03-23 12:30:00 4.176736
37 37 2018-03-24 12:30:00 4.065426
38 38 2018-03-25 12:30:00 4.231903
39 39 2018-03-26 12:30:00 4.040669
40 40 2018-03-27 12:30:00 3.481890
41 41 2018-03-28 12:30:00 2.980586
42 42 2018-03-29 12:30:00 2.877671
43 43 2018-03-30 12:30:00 3.200152
44 44 2018-03-31 12:30:00 3.502593
45 45 2018-04-01 12:30:00 3.446226
46 46 2018-04-02 12:30:00 3.425633
47 47 2018-04-03 12:30:00 3.522947
48 48 2018-04-04 12:30:00 3.329282
49 49 2018-04-05 12:30:00 2.870850
50 50 2018-04-06 12:30:00 2.379755
51 51 2018-04-07 12:30:00 1.934548
52 52 2018-04-08 12:30:00 1.420051
53 53 2018-04-09 12:30:00 1.105807
54 54 2018-04-10 12:30:00 0.920031
55 55 2018-04-11 12:30:00 0.783417
56 56 2018-04-12 12:30:00 0.702750
57 57 2018-04-13 12:30:00 0.685069
58 58 2018-04-14 12:30:00 0.628042
59 59 2018-04-15 12:30:00 0.627330

Both Metric 2 and 3 seem to be working correctly.

Time series

Bloom date with different metric 3

[16]:
# loop through years of spring time series (mid feb-june) for bloom timing for 201905 run
years=list()
bloomtime1=list()
bloomtime2=list()
bloomtime3=list()
bloomtime3b=list()
for year in range(startyear,endyear):
    fname3=f'springBloomTime_{str(year)}_{loc}_{modver}.pkl'
    savepath3=os.path.join(savedir,fname3)
    bio_time0,sno30,sdiat0,sflag0,scili0,diat_alld0,no3_alld0,flag_alld0,cili_alld0,phyto_alld0,\
                 intdiat0,intphyto0,fracdiat0,sphyto0,percdiat0=pickle.load(open(savepath3,'rb'))
    # put code that calculates bloom timing here
    bt1=bloomdrivers.metric1_bloomtime(phyto_alld0,no3_alld0,bio_time0)
    bt2=bloomdrivers.metric2_bloomtime(phyto_alld0,no3_alld0,bio_time0)
    bt3=bloomdrivers.metric3_bloomtime(sphyto0,sno30,bio_time0)
    bt3b=metric3b_bloomtime(sphyto0,bio_time0)
    years.append(year)
    bloomtime1.append(bt1)
    bloomtime2.append(bt2)
    bloomtime3.append(bt3)
    bloomtime3b.append(bt3b)
years=np.array(years)
bloomtime1=np.array(bloomtime1)
bloomtime2=np.array(bloomtime2)
bloomtime3=np.array(bloomtime3)
bloomtime3b=np.array(bloomtime3b)

# get year day
yearday1=et.datetimeToYD(bloomtime1)  # convert to year day tool
yearday2=et.datetimeToYD(bloomtime2)
yearday3=et.datetimeToYD(bloomtime3)
yearday3b=et.datetimeToYD(bloomtime3b)
[17]:
# plot bloomtime for each year:
fig,ax=plt.subplots(1,1,figsize=(10,7))
p1=ax.plot(years,yearday1, 'o',color='firebrick',label='Metric1 (201905)')
p2=ax.plot(years,yearday2, 'o',color='b',label='Metric2 (201905)')
p3=ax.plot(years,yearday3, 'o',color='orange',label='Metric3 (201905)')
p4=ax.plot(years,yearday3b, 'o',color='g',label='Metric3b (201905)')
ax.set_ylabel('Year Day')
ax.set_xlabel('Year')
ax.set_title('Spring Phytoplankton Bloom Date by Year')
ax.set_xticks([2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020])
ax.legend(handles=[p1[0],p2[0],p3[0],p4[0]],bbox_to_anchor=(1.05, 1.0))
[17]:
<matplotlib.legend.Legend at 0x7f39e43fbf70>
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_31_1.png
[18]:
# loop through years of spring time series (mid feb-june) for bloom timing for 201905 run

bloomtime1=list()
bloomtime2=list()
bloomtime3=list()
#bloomtime3b=list()
for year in range(startyear,endyear):
    fname3=f'springBloomTime_{str(year)}_{loc}_{modver}.pkl'
    savepath3=os.path.join(savedir,fname3)
    bio_time0,sno30,sdiat0,sflag0,scili0,diat_alld0,no3_alld0,flag_alld0,cili_alld0,phyto_alld0,\
                 intdiat0,intphyto0,fracdiat0,sphyto0,percdiat0=pickle.load(open(savepath3,'rb'))
    # put code that calculates bloom timing here
    bt1=bloomdrivers.metric1_bloomtime(phyto_alld0,no3_alld0,bio_time0)
    bt2=bloomdrivers.metric2_bloomtime(phyto_alld0,no3_alld0,bio_time0)
    bt3=bloomdrivers.metric3_bloomtime(sphyto0,sno30,bio_time0)
   # bt3b=metric3b_bloomtime(sphyto0,bio_time0)

    # plot time series
    fig,ax=plt.subplots(1,1,figsize=(12,3))
    p1=ax.plot(bio_time0,sphyto0,
               '-',color='forestgreen',label='Phytoplankton')
    p2=ax.plot(bio_time0,sno30,
               '-',color='orange',label='Nitrate')
    ax.legend(handles=[p1[0],p2[0]],loc=1)
    ax.set_ylabel('Concentration ($\mu$M N)')
    ax.set_title('%i Surface Phytoplankton and Nitrate at Station QU39'%year)

    ax.axvline(x=bt1, label='Metric 1 Bloom Date:{}'.format(bt1), color='r')
    ax.axvline(x=bt2, label='Metric 2 Bloom Date:{}'.format(bt2), color='k')
    ax.axvline(x=bt3, label='Metric 3 Bloom Date:{}'.format(bt3), color='b')
   # ax.axvline(x=bt3b, label='Metric 3b Bloom Date:{}'.format(bt3b), color='c')
    ax.legend()
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_0.png
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_1.png
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_2.png
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_3.png
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_4.png
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_5.png
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_6.png
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_7.png
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_8.png
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_9.png
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_10.png
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_11.png
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_12.png
../../_images/bloom_timing_bloom_notebooks_bloom_func_check_32_13.png

Conlcusions: Metric 2 consistently lined up with the visual interpretation of the spring bloom based on phytoplankton concentrations. Therefore, metric 2 will be more heavily considered for correlation analysis.

[ ]: