def single_niramsii_run(params_dict): """ Run the NIRAMS II model with the specified parameters. Args: params_dict: Dict containing all model parameters and user-specified options. Returns: None. Model outptus are written to the specified HDF5 file (and GeoTiffs if specified). """ import input_output as io, snow as sn, drainage as dr import nitrate as ni, calendar, numpy.ma as ma, os # Paths to static nodes in the input HDF5 file nodes_dict = {'land_props' : r'/one_km_grids/old_land_properties/', 'soil_props' : r'/one_km_grids/soil_properties/', 'met_data' : r'/five_km_grids/meteorological_data/', 'iacs_pet' : r'/one_km_grids/iacs_pet_facts/', 'or' : r'/one_km_grids/organic_n/', 'in' : r'/one_km_grids/inorganic_n/', 'up' : r'/one_km_grids/n_uptake/', 'n_dep' : r'/one_km_grids/n_deposition/', 'time_series': r'/time_series/'} # Create output HDF5 file io.create_output_h5(params_dict) # Dicts storing number of days in each month (one for leap years; one for # non-leap years) days_in_month_dict = {1:31, 2:28, 3:31, 4:30, 5:31, 6:30, 7:31, 8:31, 9:30, 10:31, 11:30, 12:31} days_in_month_lpyr_dict = {1:31, 2:29, 3:31, 4:30, 5:31, 6:30, 7:31, 8:31, 9:30, 10:31, 11:30, 12:31} # Extract the grid indices for the bounding box into a dict indices_dict = io.get_grid_indices( params_dict['xmin'], params_dict['xmax'], params_dict['ymin'], params_dict['ymax']) # Extract the static grids from the HDF5 file fc, sat, calibl, calibv = io.read_static_grids( params_dict['Input HDF5 path'], nodes_dict['soil_props'], ['fc', 'sat', 'calibl', 'calibv'], indices_dict) # Extract the PET to AET correction factor grid from the HDF5 file default_pet_fact = io.read_static_grids( params_dict['Input HDF5 path'], nodes_dict['land_props'], [params_dict['Default PET to AET grid'],], indices_dict)[0] # Set an initial water level halfway between field and saturation capacity wat_lev = (fc + sat)/2 # Set an initial snow pack of zero rows = (params_dict['ymax']-params_dict['ymin'])/1000 cols = (params_dict['xmax']-params_dict['xmin'])/1000 snow_pk = ma.zeros((rows,cols)) # Set the initial amount of available N using a simple annual balance for # 2001 # Get the annual N grids for 2001 in a dict n_bud_dict = io.read_annual_n_grids(params_dict['Input HDF5 path'], nodes_dict, 2001, indices_dict) avail_n = ni.initial_n_budget(n_bud_dict, params_dict['Organic N factor']) # Begin looping over time series data for year in range(params_dict['Start year'], params_dict['End year']+1): # Choose PET to AET conversion grids based on user input if (params_dict['Use IACS'] == True) and (year in range(2001, 2011)): # Get the iacs_pet_fact grid for this year pet_fact = io.read_static_grids(params_dict['Input HDF5 path'], nodes_dict['iacs_pet'], ['pet_fact_%s' % year,], indices_dict)[0] else: # Use the default pet_fact grid pet_fact = default_pet_fact # Read the annual N grids annual_n_dict = io.read_annual_n_grids(params_dict['Input HDF5 path'], nodes_dict, year, indices_dict) # Calculate daily n_dep rate for this year if calendar.isleap(year) == True: daily_n_dep = annual_n_dict['n_dep'] / 366. else: daily_n_dep = annual_n_dict['n_dep'] / 365. # Keep track of annual totals an_n_leach = ma.zeros((rows,cols)) an_ssf = ma.zeros((rows,cols)) an_gwf = ma.zeros((rows,cols)) an_of = ma.zeros((rows,cols)) # Loop over months for month in range(1,13): # Allow for leap years if calendar.isleap(year) == True: days_in_month = days_in_month_lpyr_dict[month] else: days_in_month = days_in_month_dict[month] # Loop over days for day in range(1, days_in_month+1): # Get today's met data from the HDF5 file pptn, t_min, t_max, pet = io.read_met_data( params_dict['Input HDF5 path'], nodes_dict['met_data'], indices_dict, year, month, day, days_in_month) # Convert PET to AET using pet_fact aet = pet_fact*pet # Where the ground is already covered in snow, set AET to zero aet[snow_pk>0] = 0 # Reduce the AET if the soil is dry i.e. if wat_lev < 0.7*fc aet = dr.reduce_aet_if_dry(aet, wat_lev, fc) # Split today's pptn into rain and snow components rain, snow = sn.estimate_snow_and_rain(pptn, t_min, t_max, params_dict['T_snow']) # Calculate today's snow melt melt = sn.estimate_snow_melt(snow_pk, t_min, t_max, params_dict['T_melt'], params_dict['Degree-day factor']) # Estimate temp and moisture factors t_fact = ni.est_temp_factor(t_min, t_max) moist_fact = ni.est_moisture_fact(wat_lev, fc) # Calculate today's mineralisation n_mineral = ni.est_mineralisation( params_dict['Mineralisation parameter'], t_fact, moist_fact) # Calculate today's denitrification n_denit = ni.est_denitrification( params_dict['Denitrification parameter'], wat_lev, t_fact, moist_fact, avail_n) # Estimate amount of N added today ts_row = io.read_ts_table(params_dict['Input HDF5 path'], nodes_dict['time_series'], day, month) n_added = ni.estimate_n_added(annual_n_dict, daily_n_dep, params_dict['Organic N factor'], n_mineral, n_denit, ts_row) # Calculate today's drainage grids dr_list = dr.estimate_drainage(fc, sat, calibl, calibv, wat_lev, snow_pk, rain, snow, melt, aet) snow_pk, wat_lev, surf_ro, lat_dr, vert_dr, tot_dr = dr_list # Calculate today's N leaching n_leach_list = ni.calculate_n_leaching( avail_n, n_added, dr_list, fc, params_dict['N leaching parameter']) leached_n, avail_n = n_leach_list # Increment annual totals an_n_leach += leached_n an_gwf += vert_dr an_ssf += lat_dr an_of += surf_ro # Calculate yearly drainage an_drain = an_ssf+an_gwf+an_of an_ss_drain = an_ssf+an_gwf # Get path to output HDF5 hdf5_fold = params_dict['Output HDF5 folder'] run_id = params_dict['Run ID'] out_hdf5 = os.path.join(hdf5_fold, 'run_%03d.h5' % run_id) # Write to output file # Total drainage io.write_array_to_h5(out_hdf5, '/run_%03d' % run_id, 'total_drainage_%s' % year, an_drain, units='mm', xmin=params_dict['xmin'], xmax=params_dict['xmax'], ymin=params_dict['ymin'], ymax=params_dict['ymax']) # Sub-surface drainage io.write_array_to_h5(out_hdf5, '/run_%03d' % run_id, 'sub-surface_drainage_%s' % year, an_ss_drain, units='mm', xmin=params_dict['xmin'], xmax=params_dict['xmax'], ymin=params_dict['ymin'], ymax=params_dict['ymax']) # N leached io.write_array_to_h5(out_hdf5, '/run_%03d' % run_id, 'n_leached_%s' % year, an_n_leach, units='mm', xmin=params_dict['xmin'], xmax=params_dict['xmax'], ymin=params_dict['ymin'], ymax=params_dict['ymax']) # Write to GTiff if params_dict['Write GeoTiffs'] == True: # Total drainage tot_dr_path = os.path.join(params_dict['Output GeoTiff folder'], 'run_%03d_total_drainage_%s.tif' % (run_id, year)) io.ma_to_gtiff(params_dict['xmin'], params_dict['ymax'], 1000, tot_dr_path, an_drain) # Sub-surface drainage ss_dr_path = os.path.join(params_dict['Output GeoTiff folder'], 'run_%03d_sub-surface_drainage_%s.tif' % (run_id, year)) io.ma_to_gtiff(params_dict['xmin'], params_dict['ymax'], 1000, ss_dr_path, an_ss_drain) # N leached n_leach_path = os.path.join(params_dict['Output GeoTiff folder'], 'run_%03d_n_leached_%s.tif' % (run_id, year)) io.ma_to_gtiff(params_dict['xmin'], params_dict['ymax'], 1000, n_leach_path, an_n_leach)
def run_wbm(k_s, k_g, et): """ Run the WBM with the specified parameters. Args: k_s: Soil residence time (days). k_g: Groundwater residence time (days). et: Correction factor applied to the ET data (dimensionless). Returns: Pandas dataframe of monthly river flows. """ # Define dicts storing number of days in each period. One dict for leap years # the other for non-leap years days_in_month_dict = {1:31, 2:28, 3:31, 4:30, 5:31, 6:30, 7:31, 8:31, 9:30, 10:31, 11:30, 12:31} days_in_month_lpyr_dict = {1:31, 2:29, 3:31, 4:30, 5:31, 6:30, 7:31, 8:31, 9:30, 10:31, 11:30, 12:31} # Validate user input check_params() # Get array indices for bounding box xmin_idx, xmax_idx, ymin_idx, ymax_idx = get_grid_indices(xmin, xmax, ymin, ymax) # Open H5 file h5 = h5py.File(in_h5_path, 'r') # Get soil properties fc, sat, bfi = io.read_soil_properties(h5, xmin_idx, xmax_idx, ymin_idx, ymax_idx) # Get LU grid lu_grid = io.read_land_use(h5, lu_grid_path, xmin_idx, xmax_idx, ymin_idx, ymax_idx) # Get soil and groundwater time constant grids k_s = np.ones(shape=bfi.shape)*k_s k_g = np.ones(shape=bfi.shape)*k_g # Water between fc and sat phi = sat - fc # Loop over models for model in models: # Initial water level mid-way between fc and sat surf_lev = (fc + sat)/2. gw_lev = np.zeros(shape=phi.shape) # Dict to store data data_dict = {'Date':[], 'of':[], 'ssf':[], 'gwf':[], 'Runoff_m3/s':[]} # Loop over years for year in range(st_yr, end_yr+1): for month in range(1, 13): # Get date date = dt.date(year, month, 1) # Get number of days in month, allowing for leap years if calendar.isleap(year): days_in_month = days_in_month_lpyr_dict[month] else: days_in_month = days_in_month_dict[month] # Get met data pptn, pet = io.read_met_data(h5, model, xmin_idx, xmax_idx, ymin_idx, ymax_idx, year, month) # Correct grass reference PET for land use and apply ET # correction factor pet = pet*lu_grid*et # Convert from mm/month to mm/day pptn = pptn/days_in_month pet = pet/days_in_month # Calculate HER her = pptn - pet # mm/day # Get drainage params for this time step drainage_params = dr.calculate_drainage(her, surf_lev, gw_lev, fc, days_in_month, phi, k_s, k_g, bfi) # NB: the net_her value returned here is for the whole month # i.e. mm/month NOT mm/day like her, above net_her, of, ssf, gwf, surf_lev, gw_lev = drainage_params # Calculate monthly runoff ro = of + ssf + gwf # Apply mask ro[mask!=site_code] = np.nan of[mask!=site_code] = np.nan ssf[mask!=site_code] = np.nan gwf[mask!=site_code] = np.nan # Calculate monthly total in m3/s ro_mon = 1000.*bn.nansum(ro)/(days_in_month*24*60*60) of_mon = 1000.*bn.nansum(of)/(days_in_month*24*60*60) ssf_mon = 1000.*bn.nansum(ssf)/(days_in_month*24*60*60) gwf_mon = 1000.*bn.nansum(gwf)/(days_in_month*24*60*60) # Append to data dict data_dict['Date'].append(date) data_dict['of'].append(of_mon) data_dict['ssf'].append(ssf_mon) data_dict['gwf'].append(gwf_mon) data_dict['Runoff_m3/s'].append(ro_mon) # Close file h5.close() # Build df df = pd.DataFrame(data_dict) df.index = pd.to_datetime(df['Date']) del df['Date'] return df