External Forcing Conditions#

Up next, we will generate the external forcing conditions that will be used in the California regional model.

First, import packages to re-create and visualize the model fields here:

import os
import numpy as np
from scipy.interpolate import griddata
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import netCDF4 as nc4
import cmocean.cm as cm

Next, define the location of the input directory for the model. This is the same directory that holds the bathymetry file and the initial conditions file generated in the previous notebooks for this model example.

# define the input directory
input_dir = '../data/input'

External Forcing Source Files#

For this example, I will use the external forcing fields from the ECCO Version 5 state estimate. I will prepare these fields in 5 steps:

  1. download 7 external forcing fields used in the ECCO model

  2. read the external forcing fields used in the ECCO model as well as the ECCO grid

  3. read in the bathymetry for my model as well as its grid

  4. interpolate the ECCO fields onto my model grid and store each as a binary file

  5. plot the interpolated fields to ensure they look as expected

Step 1: Download the ECCO external forcing fields#

To begin, download the ECCO external forcing fields used in the ECCO Version 5 Alpha state estimate. These fields are available HERE. For my model, I donwloaded the following list of files for my year of interest (2008):

Variable

File Name

ATEMP

EIG_tmp2m_degC_plus_ECCO_v4r1_ctrl

AQH

EIG_spfh2m_plus_ECCO_v4r1_ctrl

SWDOWN

EIG_dsw_plus_ECCO_v4r1_ctrl

LWDOWN

EIG_dlw_plus_ECCO_v4r1_ctrl

UWIND

EIG_u10m

VWIND

EIG_v10m

PRECIP

EIG_rain_plus_ECCO_v4r1_ctrl

Define the location where these grids are stored:

data_folder = '../data/ECCO'

Step 2: Read in the external forcing fields#

To read in the ECCO fields, I will rely on the exf module from the eccoseas package. I import them here:

from eccoseas.ecco import exf

Next, I will loop through all of the files I downloaded, reading them in with the exf module.

# make a file dictionary to loop over
file_prefix_dict = {'ATEMP':'EIG_tmp2m_degC_plus_ECCO_v4r1_ctrl',
                    'AQH':'EIG_spfh2m_plus_ECCO_v4r1_ctrl',
                    'SWDOWN':'EIG_dsw_plus_ECCO_v4r1_ctrl',
                    'LWDOWN':'EIG_dlw_plus_ECCO_v4r1_ctrl',
                    'UWIND':'EIG_u10m',
                    'VWIND':'EIG_v10m',
                    'PRECIP':'EIG_rain_plus_ECCO_v4r1_ctrl'}

variable_names = list(file_prefix_dict.keys())
# make a list to hold all of the exf grid
exf_grids = []
year=2008

# loop through each variable to read in the grid
for field in variable_names:
    exf_lon, exf_lat, exf_grid = exf.read_ecco_exf_file(data_folder, file_prefix_dict[field], year)
    exf_grids.append(exf_grid)

With an eye toward the interpolation that will come in step 4, I will make 2D grids of longitudes and latitudes to use in the interpolation

Exf_Lon, Exf_Lat = np.meshgrid(exf_lon, exf_lat)
ecco_points = np.column_stack([Exf_Lon.ravel(), Exf_Lat.ravel()])

In addition, I will make a mask to determine where the interpolation will take place. Since the external forcing conditions are provided everywhere, I just set the entire grid to 1:

ecco_mask = np.ones((np.size(Exf_Lon),))

Step 3: Read in the Model Grid#

Next, I will recreate the grid I will use in my model and read in the bathymetry file (see previous notebooks for details):

# define the parameters that will be used in the data file
delX = 1/12
delY = 1/16
xgOrigin = -135
ygOrigin = 29
n_rows = 360
n_cols = 240

# recreate the grids that will be used in the model
xc = np.arange(xgOrigin+delX/2, xgOrigin+n_cols*delX, delX)
yc = np.arange(ygOrigin+delY/2, ygOrigin+n_rows*delY+delY/2, delY)
XC, YC = np.meshgrid(xc, yc)

# read in the bathymetry file
bathy = np.fromfile(os.path.join(input_dir,'CA_bathymetry.bin'),'>f4').reshape(np.shape(XC))

Similar to above, I will make a mask to determine where the interpolatation will take place. I will create this mask using the hFac module from the eccoseas package:

from eccoseas.downscale import hFac
surface_mask = hFac.create_surface_hFacC_grid(bathy, delR=1)

To double check the mask was created as expected, I will plot it in comparison to the bathymetry here:

plt.figure(figsize=(10,5))

plt.subplot(1,2,1)
C = plt.pcolormesh(XC, YC, bathy, vmin=-5000, vmax=0, cmap='Blues_r')
plt.colorbar(C, orientation = 'horizontal')
plt.title('Model Bathymetry')

plt.subplot(1,2,2)
C = plt.pcolormesh(XC, YC, surface_mask, vmin=0, vmax=1, cmap='Greys')
plt.colorbar(C, orientation = 'horizontal')
plt.title('Surface Mask')

plt.show()
../_images/bff342c1dc922eb5e0f90b30e792bd6e7cb39ec46f7d3e04a85a6768fc5c6418.png

Step 4: Interpolate the Fields onto the Model Grid#

Next, I will interpolate the ECCO external fields I read in onto my model domain. I will use the horizonal module from the eccoseas package to accomplish this interpolation.

from eccoseas.downscale import horizontal

❗ Warning#

This code block may take a while to run. Further, it will generate 7 files sized ~500MB. Plan accordingly.

# ensure the output folder exists
if 'exf' not in os.listdir(input_dir):
    os.mkdir(os.path.join(input_dir, 'exf'))
# number of timesteps
n_timesteps = 1
# n_timesteps = np.shape(exf_grid)[0]): # uncomment after testing

# loop through each variable and corresponding ECCO grid
for variable_name, exf_grid in zip(variable_names, exf_grids):

    # print a message to keep track of which variable we are working on
    # uncomment to use
    print('    - Interpolating the '+variable_name+' grid')

    # create a grid of zeros to fill in
    interpolated_grid = np.zeros((np.shape(exf_grid)[0], np.shape(XC)[0], np.shape(XC)[1]))

    # loop through each timestep to generate the interpolated grid
    for timestep in range(n_timesteps):

        # print a message every 100 timesteps to show where we are in the stack
        # uncomment to use
        if timestep%100==0:
            print('        - Working on timestep '+str(timestep)+' of '+str(np.shape(interpolated_grid)[0]))

        # grab the values for this timestep and run the interpolation function
        ecco_values = exf_grid[timestep, :, :].ravel()
        interp_timestep = horizontal.downscale_2D_points_with_zeros(ecco_points, ecco_values, ecco_mask,
                                                                    XC, YC, surface_mask)
        interpolated_grid[timestep,:,:] = interp_timestep

    # convert ECCO values to MITgcm defaults
    if variable_name=='ATEMP':
        interpolated_grid += 273.15
    if variable_name in ['SWDOWN','LWDOWN']:
        interpolated_grid *=-1

    # output the interpolated grid
    output_file = os.path.join(input_dir,'exf',variable_name+'_'+str(year))
    interpolated_grid.ravel('C').astype('>f4').tofile(output_file)
    - Interpolating the ATEMP grid
        - Working on timestep 0 of 1464
    - Interpolating the AQH grid
        - Working on timestep 0 of 1464
    - Interpolating the SWDOWN grid
        - Working on timestep 0 of 1464
    - Interpolating the LWDOWN grid
        - Working on timestep 0 of 1464
    - Interpolating the UWIND grid
        - Working on timestep 0 of 1464
    - Interpolating the VWIND grid
        - Working on timestep 0 of 1464
    - Interpolating the PRECIP grid
        - Working on timestep 0 of 1464

Step 5: Plotting the External Forcing Fields#

Now that the fields have been generated, I will plot them to ensure they look as expected. First, I’ll generate some metadata for each one:

meta_dict = {'ATEMP':[273, 290, cm.thermal, '$^{\circ}$C'],
             'AQH':[0, 0.025, cm.tempo, 'kg/kg'],
             'PRECIP':[0, 1e-6, cm.tempo, 'm/s'],
             'SWDOWN':[-10,20,cm.solar,'W/m$^2$'],
             'LWDOWN':[-100, 500,cm.solar,'W/m$^2$'],
             'UWIND':[-20, 20, cm.balance, 'm/s'],
             'VWIND':[-20, 20, cm.balance, 'm/s'],
             'RUNOFF':[0, 2e-8, cm.tempo, 'm/s']}

Then, I’ll create all of the subplots:

fig = plt.figure(figsize=(8,10))
gs = GridSpec(4, 2, wspace=0.4, hspace=0.03, 
              left=0.11, right=0.9, top=0.95, bottom=0.05)


for i in range(len(variable_names)):
    variable_name = variable_names[i]
    
    CA_exf_grid = np.fromfile(os.path.join(input_dir,'exf',variable_name+'_'+str(year)),'>f4')
    CA_exf_grid = CA_exf_grid.reshape((np.shape(exf_grid)[0],np.shape(XC)[0], np.shape(XC)[1]))

    # choose just the first timestep for plotting
    CA_exf_grid = CA_exf_grid[0, :, :]
    
    ax1 = fig.add_subplot(gs[i])
    C = plt.pcolormesh(XC, YC, CA_exf_grid,
                       vmin=meta_dict[variable_names[i]][0],
                       vmax=meta_dict[variable_names[i]][1],
                       cmap=meta_dict[variable_names[i]][2])
    plt.colorbar(C, label=variable_names[i]+' ('+meta_dict[variable_names[i]][3]+')',fraction=0.026)

    if i<5:
        plt.gca().set_xticklabels([])
    else:
        plt.gca().set_xlabel('Longitude')
    if i%2==1:
        plt.gca().set_yticklabels([])
    if i==7:
        plt.gca().axis('off')
    if i==2:
        plt.gca().set_ylabel('Latitude')

plt.show()
../_images/ad3cf339720367318427ca2f2dc9836ac5571b2040bf4a6954ab400d9516490a.png

Looks good! Only one more step and then we’re ready to run the model.