In [1]:
import matplotlib.pyplot as plt
import iris
import iris.plot as iplt
import numpy
import iris.coord_categorisation
import re

In [2]:
%matplotlib inline

In [3]:
infile = '/g/data/ua6/DRSv2/CMIP5/CSIRO-Mk3-6-0/rcp85/mon/ocean/r1i1p1/tauuo/latest/tauuo_Omon_CSIRO-Mk3-6-0_rcp85_r1i1p1_200601-210012.nc'

In [4]:
cube = iris.load_cube(infile, 'surface_downward_x_stress')


/g/data/r87/dbi599/miniconda3/envs/ocean/lib/python3.6/site-packages/iris/fileformats/cf.py:1143: IrisDeprecation: NetCDF default loading behaviour currently does not expose variables which define reference surfaces for dimensionless vertical coordinates as independent Cubes. This behaviour is deprecated in favour of automatic promotion to Cubes. To switch to the new behaviour, set iris.FUTURE.netcdf_promote to True.
  warn_deprecated(msg)

In [5]:
print(cube)


surface_downward_x_stress / (N m-2) (time: 1140; latitude: 189; longitude: 192)
     Dimension coordinates:
          time                           x               -               -
          latitude                       -               x               -
          longitude                      -               -               x
     Attributes:
          Conventions: CF-1.4
          associated_files: baseURL: http://cmip-pcmdi.llnl.gov/CMIP5/dataLocation gridspecFile: g...
          branch_time: 56940.0
          cmor_version: 2.6.0
          comment: Data is stored on the native atmosphere grid on which the data was generated....
          contact: Project leaders: Stephen Jeffrey (Stephen.Jeffrey@qld.gov.au) & Leon Rotstayn...
          creation_date: 2011-08-10T00:12:17Z
          experiment: RCP8.5
          experiment_id: rcp85
          forcing: Ant,Nat (RCP 8.5)
          frequency: mon
          history: 2011-08-10T00:12:17Z altered by CMOR: replaced missing value flag (-7.77778e+06)...
          initialization_method: 1
          institute_id: CSIRO-QCCCE
          institution: Australian Commonwealth Scientific and Industrial Research Organization...
          model_id: CSIRO-Mk3-6-0
          modeling_realm: ocean
          original_name: taux
          parent_experiment: historical
          parent_experiment_id: historical
          parent_experiment_rip: r1i1p1
          physics_version: 1
          product: output
          project_id: CMIP5
          realization: 1
          references: a) Rotstayn, L., Collier, M., Dix, M., Feng, Y., Gordon, H., O\'Farrell,...
          source: CSIRO-Mk3-6-0 2010 atmosphere: AGCM v7.3.8 (T63 spectral, 1.875 degrees...
          table_id: Table Omon (27 April 2011) 9e1a53e4873bf6f26879903e165fe4a0
          title: CSIRO-Mk3-6-0 model output prepared for CMIP5 RCP8.5
          tracking_id: 563d1422-b831-4f26-badf-9b6b57798c7d
          version_number: v20110518
     Cell methods:
          mean: time
          mean where sea: area

In [6]:
def get_time_constraint(time_list):
    """Get the time constraint used for reading an iris cube."""
    
    if time_list:

        start_date, end_date = time_list

        date_pattern = '([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})'
        assert re.search(date_pattern, start_date)
        assert re.search(date_pattern, end_date)

        if (start_date == end_date):
            year, month, day = start_date.split('-')    
            time_constraint = iris.Constraint(time=iris.time.PartialDateTime(year=int(year), month=int(month), day=int(day)))
        else:  
            start_year, start_month, start_day = start_date.split('-') 
            end_year, end_month, end_day = end_date.split('-')
            time_constraint = iris.Constraint(time=lambda t: iris.time.PartialDateTime(year=int(start_year), month=int(start_month), day=int(start_day)) <= t.point <= iris.time.PartialDateTime(year=int(end_year), month=int(end_month), day=int(end_day)))
    else:
        time_constraint = iris.Constraint()

    return time_constraint

In [7]:
with iris.FUTURE.context(cell_datetime_objects=True):
    start_cube = cube.extract(get_time_constraint(['2006-01-01', '2025-12-31']))
    start_clim = start_cube.collapsed('time', iris.analysis.MEAN)

In [8]:
fig = plt.figure(figsize=[20,8])
iplt.contourf(start_clim, cmap='RdBu_r',
              levels=[-0.25, -0.2, -0.15, -0.1, -0.05, 0, 0.05, 0.1, 0.15, 0.20, 0.25],
              extend='both')
plt.gca().coastlines()
plt.gca().gridlines()
cbar = plt.colorbar()
cbar.set_label(str(start_clim.units))
plt.show()



In [9]:
with iris.FUTURE.context(cell_datetime_objects=True):
    end_cube = cube.extract(get_time_constraint(['2081-01-01', '2100-12-31']))
    end_clim = end_cube.collapsed('time', iris.analysis.MEAN)

In [10]:
fig = plt.figure(figsize=[20,8])
iplt.contourf(end_clim, cmap='RdBu_r',
              levels=[-0.25, -0.2, -0.15, -0.1, -0.05, 0, 0.05, 0.1, 0.15, 0.20, 0.25],
              extend='both')
plt.gca().coastlines()
plt.gca().gridlines()
cbar = plt.colorbar()
cbar.set_label(str(end_clim.units))
plt.show()


Metrics


In [11]:
iris.coord_categorisation.add_year(cube, 'time')
annual_cube = cube.aggregated_by(['year'], iris.analysis.MEAN)

In [12]:
zonal_mean = annual_cube.collapsed('longitude', iris.analysis.MEAN)

In [13]:
print(zonal_mean)


surface_downward_x_stress / (N m-2) (time: 95; latitude: 189)
     Dimension coordinates:
          time                           x             -
          latitude                       -             x
     Auxiliary coordinates:
          year                           x             -
     Scalar coordinates:
          longitude: 180.0 degrees, bound=(0.0, 360.0) degrees
     Attributes:
          Conventions: CF-1.4
          associated_files: baseURL: http://cmip-pcmdi.llnl.gov/CMIP5/dataLocation gridspecFile: g...
          branch_time: 56940.0
          cmor_version: 2.6.0
          comment: Data is stored on the native atmosphere grid on which the data was generated....
          contact: Project leaders: Stephen Jeffrey (Stephen.Jeffrey@qld.gov.au) & Leon Rotstayn...
          creation_date: 2011-08-10T00:12:17Z
          experiment: RCP8.5
          experiment_id: rcp85
          forcing: Ant,Nat (RCP 8.5)
          frequency: mon
          history: 2011-08-10T00:12:17Z altered by CMOR: replaced missing value flag (-7.77778e+06)...
          initialization_method: 1
          institute_id: CSIRO-QCCCE
          institution: Australian Commonwealth Scientific and Industrial Research Organization...
          model_id: CSIRO-Mk3-6-0
          modeling_realm: ocean
          original_name: taux
          parent_experiment: historical
          parent_experiment_id: historical
          parent_experiment_rip: r1i1p1
          physics_version: 1
          product: output
          project_id: CMIP5
          realization: 1
          references: a) Rotstayn, L., Collier, M., Dix, M., Feng, Y., Gordon, H., O\'Farrell,...
          source: CSIRO-Mk3-6-0 2010 atmosphere: AGCM v7.3.8 (T63 spectral, 1.875 degrees...
          table_id: Table Omon (27 April 2011) 9e1a53e4873bf6f26879903e165fe4a0
          title: CSIRO-Mk3-6-0 model output prepared for CMIP5 RCP8.5
          tracking_id: 563d1422-b831-4f26-badf-9b6b57798c7d
          version_number: v20110518
     Cell methods:
          mean: time
          mean where sea: area
          mean: year
          mean: longitude

In [14]:
iplt.plot(zonal_mean[0, :])
plt.show()



In [15]:
y_data = zonal_mean[0, :].data
x_data = zonal_mean.coord('latitude').points

In [16]:
y_data


Out[16]:
masked_array(data = [-- -- -- -- -- -- -- -0.006551775104728424 -0.005541126638288713
 -0.004716499098059204 -0.0036458040121942763 -0.005217424500277083
 -0.006729313382404106 -0.008996990504480589 -0.008189787629932638
 -0.011837010223196068 -0.009373870737041639 -0.01074861755740701
 -0.0035064355223713545 0.0010544152770454837 0.01224527168024946
 0.009799698651905227 0.017479358189221885 0.01497612709198335
 0.028343743124713928 0.03099885235361532 0.048601896426520554
 0.06619185684798544 0.08640515377434592 0.10663223535650306
 0.12209048414499395 0.13752565362180272 0.15045190612889
 0.16336677968502045 0.17447761868042921 0.1855270981368144
 0.19055650350031794 0.1948954338300313 0.19626948739465422
 0.1973352813592521 0.1958945153755099 0.19365096446258803
 0.18748714978044684 0.18099337075164612 0.1724175637776869
 0.16273734683467858 0.15260626042718853 0.14103220541620515
 0.13003971792577146 0.11825289924939474 0.10921708448381906
 0.098411172607821 0.08985287337076096 0.07960536288008803
 0.06821282419192716 0.055521400563727515 0.04394139367507314
 0.031379040165957696 0.02187578598187608 0.012052838823411805
 0.0040565619627493015 -0.004651035739513119 -0.012649207365312125
 -0.020879949242704268 -0.02848502671501289 -0.03569385330681795
 -0.041783844085821246 -0.048187287126453544 -0.05280684490670148
 -0.05719439389197518 -0.060210877773808506 -0.062405991660440016
 -0.06462293141804762 -0.06500964704026499 -0.06698126855882862
 -0.06788826813405388 -0.06814790095747021 -0.06602283799800644
 -0.06406613131957349 -0.060164359330144364 -0.05774757963359014
 -0.05499618299564141 -0.05044527376723178 -0.045758862033996234
 -0.044107285221707794 -0.04131444534264361 -0.04004468426886779
 -0.036250819802178556 -0.03452430504242436 -0.03202598218291581
 -0.03144628381039154 -0.03024523044440547 -0.028719348073079815
 -0.027010560833824793 -0.025994809895837096 -0.024440033255070017
 -0.02314335768460296 -0.021806689608540087 -0.020124141715277627
 -0.016258830666225143 -0.017041484937024766 -0.014630858162219854
 -0.018587383545107312 -0.021744779844761544 -0.030389527004520175
 -0.03706430424662197 -0.04364888793753303 -0.04881694558793786
 -0.05228952316067897 -0.05329287088782095 -0.055936857250829544
 -0.055354613242812004 -0.058352922335779545 -0.057614248532515296
 -0.058985878957163534 -0.0560318722029527 -0.057952132158442936
 -0.05474385855808895 -0.054492825155591104 -0.050113717414939606
 -0.046859751134152305 -0.039535717751716035 -0.034754166807515134
 -0.026058463497593266 -0.017381133448835167 -0.007799652522566532
 0.0023461715566615265 0.012536754202252875 0.02210495054491402
 0.030225751066357838 0.03769730401714102 0.043486138640558315
 0.04952133589550213 0.05358606854285407 0.05796220862682451
 0.05641077204237285 0.05836005540590527 0.057035351473027136
 0.06027871422637045 0.056730817840026135 0.06047115945589863
 0.056074533825807696 0.0579577700000226 0.052022372720812476
 0.051929652350483156 0.048900523249811104 0.048557648112539924
 0.043936332520726235 0.04005211767205899 0.03445043750495339
 0.029654359560811992 0.022135001735850462 0.01468967573453832
 0.003710815965940679 -0.004554404488040342 -0.012400577173513528
 -0.017113494208003854 -0.021143815346989673 -0.020903799105717828
 -0.025201173920261985 -0.028947683520264163 -0.0317776191006534
 -0.044360256981841914 -0.044602798532929505 -0.03891194948305686
 -0.030970986483317583 -0.030470820971661143 -0.02374036030382812
 -0.022353027554718446 -0.014253777154726591 -0.018195033599207173
 -0.017383642355122516 -0.019057700644822907 -0.015841919722812757
 -0.015386029278821652 -0.012002944190675435 -0.010813327805240467
 -0.0077958252362280685 -0.007279450302112218 -0.00570301465403663
 -0.0049791282717027326 -0.004553204765897535 -0.00454790829821496
 -0.00455828948224913 -0.004865951547125246 -0.0054944819678187035
 -0.005184634510290683 -0.004888652067772152 --],
             mask = [ True  True  True  True  True  True  True False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False False False False False
 False False False False False False False False  True],
       fill_value = 1e+20)

In [17]:
x_data


Out[17]:
array([ -8.77126160e+01,  -8.67669220e+01,  -8.58273010e+01,
        -8.48901672e+01,  -8.39544754e+01,  -8.30195847e+01,
        -8.20852280e+01,  -8.11512222e+01,  -8.02174835e+01,
        -7.92839279e+01,  -7.83505325e+01,  -7.74172440e+01,
        -7.64840317e+01,  -7.55508881e+01,  -7.46178207e+01,
        -7.36847916e+01,  -7.27518005e+01,  -7.18188400e+01,
        -7.08859253e+01,  -6.99530182e+01,  -6.90201492e+01,
        -6.80872955e+01,  -6.71544495e+01,  -6.62216187e+01,
        -6.52888107e+01,  -6.43560028e+01,  -6.34232140e+01,
        -6.24904366e+01,  -6.15576668e+01,  -6.06248932e+01,
        -5.96921425e+01,  -5.87593918e+01,  -5.78266449e+01,
        -5.68939018e+01,  -5.59611702e+01,  -5.50284386e+01,
        -5.40957069e+01,  -5.31629868e+01,  -5.22302628e+01,
        -5.12975426e+01,  -5.03648262e+01,  -4.94321175e+01,
        -4.84994087e+01,  -4.75667000e+01,  -4.66339951e+01,
        -4.57012901e+01,  -4.47685928e+01,  -4.38358917e+01,
        -4.29031982e+01,  -4.19705009e+01,  -4.10378075e+01,
        -4.01051140e+01,  -3.91724243e+01,  -3.82397385e+01,
        -3.73070488e+01,  -3.63743591e+01,  -3.54416733e+01,
        -3.45089874e+01,  -3.35763016e+01,  -3.26436195e+01,
        -3.17109413e+01,  -3.07782593e+01,  -2.98455791e+01,
        -2.89129009e+01,  -2.79802189e+01,  -2.70475388e+01,
        -2.61148605e+01,  -2.51821823e+01,  -2.42495079e+01,
        -2.33168297e+01,  -2.23841534e+01,  -2.14514771e+01,
        -2.05188007e+01,  -1.95861263e+01,  -1.86534519e+01,
        -1.77207794e+01,  -1.67881050e+01,  -1.58554316e+01,
        -1.49227571e+01,  -1.39900837e+01,  -1.30574121e+01,
        -1.21247387e+01,  -1.11920662e+01,  -1.02593946e+01,
        -9.32672310e+00,  -8.39405155e+00,  -7.46137857e+00,
        -6.52870750e+00,  -5.59603643e+00,  -4.66336489e+00,
        -3.73069406e+00,  -2.79802275e+00,  -1.86535180e+00,
        -9.32680488e-01,   3.69999924e-08,   9.32680488e-01,
         1.86535180e+00,   2.79802275e+00,   3.73069406e+00,
         4.66336489e+00,   5.59603643e+00,   6.52870750e+00,
         7.46137857e+00,   8.39405155e+00,   9.32672310e+00,
         1.02593946e+01,   1.11920662e+01,   1.21247387e+01,
         1.30574121e+01,   1.39900837e+01,   1.49227571e+01,
         1.58554316e+01,   1.67881050e+01,   1.77207794e+01,
         1.86534519e+01,   1.95861263e+01,   2.05188007e+01,
         2.14514771e+01,   2.23841534e+01,   2.33168297e+01,
         2.42495079e+01,   2.51821823e+01,   2.61148605e+01,
         2.70475388e+01,   2.79802189e+01,   2.89129009e+01,
         2.98455791e+01,   3.07782593e+01,   3.17109413e+01,
         3.26436195e+01,   3.35763016e+01,   3.45089874e+01,
         3.54416733e+01,   3.63743591e+01,   3.73070488e+01,
         3.82397385e+01,   3.91724243e+01,   4.01051140e+01,
         4.10378075e+01,   4.19705009e+01,   4.29031982e+01,
         4.38358917e+01,   4.47685928e+01,   4.57012901e+01,
         4.66339951e+01,   4.75667000e+01,   4.84994087e+01,
         4.94321175e+01,   5.03648262e+01,   5.12975426e+01,
         5.22302628e+01,   5.31629868e+01,   5.40957069e+01,
         5.50284386e+01,   5.59611702e+01,   5.68939018e+01,
         5.78266449e+01,   5.87593918e+01,   5.96921425e+01,
         6.06248932e+01,   6.15576668e+01,   6.24904366e+01,
         6.34232140e+01,   6.43560028e+01,   6.52888107e+01,
         6.62216187e+01,   6.71544495e+01,   6.80872955e+01,
         6.90201492e+01,   6.99530182e+01,   7.08859253e+01,
         7.18188400e+01,   7.27518005e+01,   7.36847916e+01,
         7.46178207e+01,   7.55508881e+01,   7.64840317e+01,
         7.74172440e+01,   7.83505325e+01,   7.92839279e+01,
         8.02174835e+01,   8.11512222e+01,   8.20852280e+01,
         8.30195847e+01,   8.39544754e+01,   8.48901672e+01,
         8.58273010e+01,   8.67669220e+01,   8.77126160e+01])

In [18]:
plt.plot(x_data[130:150], y_data[130:150], 'o-')


Out[18]:
[<matplotlib.lines.Line2D at 0x7ff159502a58>]

In [19]:
from scipy.interpolate import interp1d

In [20]:
#xnew = numpy.linspace(x_data[0], x_data[-1], num=1000, endpoint=True)
f_data = interp1d(x_data, y_data, kind='cubic')

In [25]:
xnew = numpy.linspace(x_data[0], x_data[-1], num=1000, endpoint=True)
ynew =  f_data(xnew)
plt.plot(x_data, y_data, 'o', xnew, ynew, '--')
plt.legend(['data', 'cubic'], loc='best')
plt.xlim(30, 55)
plt.ylim(0, 0.07)
plt.show()


Interpolation still noisy... need to fit a smooth curve instead.


In [28]:
xnew = numpy.linspace(x_data[0], x_data[-1], num=1000, endpoint=True)
ynew =  f_data(xnew)
plt.plot(x_data, y_data, 'o', xnew, ynew, '--')
plt.legend(['data', 'cubic'], loc='best')
plt.axhline()
plt.show()



In [29]:
type(ynew)


Out[29]:
numpy.ndarray

In [41]:
def wind_stress_metrics(xdata, ydata, hemisphere, direction):
    """Calculate location and magnitude metric for surface wind stress."""

    assert hemisphere in ['nh', 'sh']
    assert direction in ['westerly', 'easterly']
    
    # assert regularly spaced y values
    
    if (hemisphere == 'sh') and (direction == 'westerly'):
        print(hemisphere, direction)
        selection = (xdata < 0) & (ydata > 0)
    elif (hemisphere == 'nh') and (direction == 'westerly'):
        print(hemisphere, direction)
        selection = (xdata > 0) & (ydata > 0)
    elif (hemisphere == 'sh') and (direction == 'easterly'):
        print(hemisphere, direction)
        selection = (xdata < 0) & (xdata > -50) & (ydata < 0)
    elif (hemisphere == 'nh') and (direction == 'easterly'):
        print(hemisphere, direction)
        selection = (xdata > 0) & (xdata < 40) & (ydata < 0)
    
    x_filtered = numpy.where(selection, xdata, 0)
    y_filtered = numpy.where(selection, ydata, 0)

    location = numpy.sum(x_filtered * y_filtered) / numpy.sum(y_filtered)  # centre of gravity
    magnitude = numpy.sum(y_filtered)
    
    return location, magnitude

The centre of mass/gravity idea came from this post.


In [46]:
sw_loc, sw_mag = wind_stress_metrics(xnew, ynew, 'sh', 'westerly')
print(sw_loc, sw_mag)


sh westerly
-50.3229779891 23.290907155

In [47]:
nw_loc, nw_mag = wind_stress_metrics(xnew, ynew, 'nh', 'westerly')
print(nw_loc, nw_mag)


nh westerly
42.282506229 6.17450790211

In [48]:
se_loc, se_mag = wind_stress_metrics(xnew, ynew, 'sh', 'easterly')
print(se_loc, se_mag)


sh easterly
-15.7698587734 -8.04785156934

In [49]:
ne_loc, ne_mag = wind_stress_metrics(xnew, ynew, 'nh', 'easterly')
print(ne_loc, ne_mag)


nh easterly
16.0609242543 -6.31035344801

In [51]:
xnew = numpy.linspace(x_data[0], x_data[-1], num=1000, endpoint=True)
ynew =  f_data(xnew)
plt.plot(x_data, y_data, 'o', xnew, ynew, '--')
plt.legend(['data', 'cubic'], loc='best')
plt.axhline()
plt.axvline(sw_loc)
plt.axvline(nw_loc)
plt.axvline(se_loc)
plt.axvline(ne_loc)
plt.show()



In [ ]: