Source code for nes.nes_formats.wrf_chem_format

#!/usr/bin/env python

import nes
from numpy import float32, int32, ndarray, array, chararray
from netCDF4 import Dataset
from mpi4py import MPI
from copy import deepcopy

GLOBAL_ATTRIBUTES_ORDER = [
    "TITLE", "START_DATE", "WEST-EAST_GRID_DIMENSION", "SOUTH-NORTH_GRID_DIMENSION", "BOTTOM-TOP_GRID_DIMENSION", "DX",
    "DY", "GRIDTYPE", "DIFF_OPT", "KM_OPT", "DAMP_OPT", "DAMPCOEF", "KHDIF", "KVDIF", "MP_PHYSICS", "RA_LW_PHYSICS",
    "RA_SW_PHYSICS", "SF_SFCLAY_PHYSICS", "SF_SURFACE_PHYSICS", "BL_PBL_PHYSICS", "CU_PHYSICS", "SF_LAKE_PHYSICS",
    "SURFACE_INPUT_SOURCE", "SST_UPDATE", "GRID_FDDA", "GFDDA_INTERVAL_M", "GFDDA_END_H", "GRID_SFDDA",
    "SGFDDA_INTERVAL_M", "SGFDDA_END_H", "WEST-EAST_PATCH_START_UNSTAG", "WEST-EAST_PATCH_END_UNSTAG",
    "WEST-EAST_PATCH_START_STAG", "WEST-EAST_PATCH_END_STAG", "SOUTH-NORTH_PATCH_START_UNSTAG",
    "SOUTH-NORTH_PATCH_END_UNSTAG", "SOUTH-NORTH_PATCH_START_STAG", "SOUTH-NORTH_PATCH_END_STAG",
    "BOTTOM-TOP_PATCH_START_UNSTAG", "BOTTOM-TOP_PATCH_END_UNSTAG", "BOTTOM-TOP_PATCH_START_STAG",
    "BOTTOM-TOP_PATCH_END_STAG", "GRID_ID", "PARENT_ID", "I_PARENT_START", "J_PARENT_START", "PARENT_GRID_RATIO", "DT",
    "CEN_LAT", "CEN_LON", "TRUELAT1", "TRUELAT2", "MOAD_CEN_LAT", "STAND_LON", "POLE_LAT", "POLE_LON", "GMT", "JULYR",
    "JULDAY", "MAP_PROJ", "MMINLU", "NUM_LAND_CAT", "ISWATER", "ISLAKE", "ISICE", "ISURBAN", "ISOILWATER"]


# noinspection DuplicatedCode
[docs] def to_netcdf_wrf_chem(self, path, keep_open=False): """ Create the NetCDF using netcdf4-python methods. Parameters ---------- self : nes.Nes Source projection Nes Object. path : str Path to the output netCDF file. keep_open : bool Indicates if you want to keep open the NetCDH to fill the data by time-step. """ self.to_dtype(float32) set_global_attributes(self) change_variable_attributes(self) # Open NetCDF if self.info: print("Rank {0:03d}: Creating {1}".format(self.rank, path)) if self.size > 1: netcdf = Dataset(path, format="NETCDF4", mode="w", parallel=True, comm=self.comm, info=MPI.Info()) else: netcdf = Dataset(path, format="NETCDF4", mode="w", parallel=False) if self.info: print("Rank {0:03d}: NetCDF ready to write".format(self.rank)) # Create dimensions create_dimensions(self, netcdf) create_dimension_variables(self, netcdf) if self.info: print("Rank {0:03d}: Dimensions done".format(self.rank)) # Create variables create_variables(self, netcdf) for att_name in GLOBAL_ATTRIBUTES_ORDER: netcdf.setncattr(att_name, self.global_attrs[att_name]) # Close NetCDF if keep_open: self.dataset = netcdf else: netcdf.close() return None
[docs] def change_variable_attributes(self): """ Modify the emission list to be consistent to use the output as input for WRF-CHEM model. Parameters ---------- self : nes.Nes A Nes Object. """ for var_name in self.variables.keys(): if self.variables[var_name]["units"] == "mol.h-1.km-2": self.variables[var_name]["FieldType"] = int32(104) self.variables[var_name]["MemoryOrder"] = "XYZ" self.variables[var_name]["description"] = "EMISSIONS" self.variables[var_name]["units"] = "mol km^-2 hr^-1" self.variables[var_name]["stagger"] = "" self.variables[var_name]["coordinates"] = "XLONG XLAT" elif self.variables[var_name]["units"] == "ug.s-1.m-2": self.variables[var_name]["FieldType"] = int32(104) self.variables[var_name]["MemoryOrder"] = "XYZ" self.variables[var_name]["description"] = "EMISSIONS" self.variables[var_name]["units"] = "ug/m3 m/s" self.variables[var_name]["stagger"] = "" self.variables[var_name]["coordinates"] = "XLONG XLAT" else: raise TypeError("The unit '{0}' of specie {1} is not defined correctly. ".format( self.variables[var_name]["units"], var_name) + "Should be 'mol.h-1.km-2' or 'ug.s-1.m-2'") if "long_name" in self.variables[var_name].keys(): del self.variables[var_name]["long_name"] return None
[docs] def to_wrf_chem_units(self): """ Change the data values according to the WRF-CHEM conventions. Parameters ---------- self : nes.Nes A Nes Object. Returns ------- dict Variable in the MONARCH units. """ self.calculate_grid_area(overwrite=False) for var_name in self.variables.keys(): if isinstance(self.variables[var_name]["data"], ndarray): if self.variables[var_name]["units"] == "mol.h-1.km-2": # 10**6 -> from m2 to km2 # 10**3 -> from kmol to mol # 3600 -> from s to h self.variables[var_name]["data"] = array( self.variables[var_name]["data"] * 10 ** 6 * 10 ** 3 * 3600, dtype=float32) elif self.variables[var_name]["units"] == "ug.s-1.m-2": # 10**9 -> from kg to ug self.variables[var_name]["data"] = array( self.variables[var_name]["data"] * 10 ** 9, dtype=float32) else: raise TypeError("The unit '{0}' of specie {1} is not defined correctly. ".format( self.variables[var_name]["units"], var_name) + "Should be 'mol.h-1.km-2' or 'ug.s-1.m-2'") self.variables[var_name]["dtype"] = float32 return self.variables
[docs] def create_times_var(self): """ Create the content of the WRF-CHEM variable times. Parameters ---------- self : nes.Nes A Nes Object. Returns ------- numpy.ndarray Array with the content of TFLAG. """ aux_times = chararray((len(self.time), 19), itemsize=1) for i_d, aux_date in enumerate(self.time): aux_times[i_d] = list(aux_date.strftime("%Y-%m-%d_%H:%M:%S")) return aux_times
# noinspection DuplicatedCode
[docs] def set_global_attributes(self): """ Set the NetCDF global attributes Parameters ---------- self : nes.Nes A Nes Object. """ # now = datetime.now() # if len(self.time) > 1: # tstep = ((self.time[1] - self.time[0]).seconds // 3600) * 10000 # else: # tstep = 1 * 10000 current_attributes = deepcopy(self.global_attrs) del self.global_attrs self.global_attrs = {"TITLE": None, "START_DATE": self.time[0].strftime("%Y-%m-%d_%H:%M:%S"), "WEST-EAST_GRID_DIMENSION": None, # Projection dependent attributes "SOUTH-NORTH_GRID_DIMENSION": None, # Projection dependent attributes "BOTTOM-TOP_GRID_DIMENSION": int32(45), "DX": None, # Projection dependent attributes "DY": None, # Projection dependent attributes "GRIDTYPE": "C", "DIFF_OPT": int32(1), "KM_OPT": int32(4), "DAMP_OPT": int32(3), "DAMPCOEF": float32(0.2), "KHDIF": float32(0.), "KVDIF": float32(0.), "MP_PHYSICS": int32(6), "RA_LW_PHYSICS": int32(4), "RA_SW_PHYSICS": int32(4), "SF_SFCLAY_PHYSICS": int32(2), "SF_SURFACE_PHYSICS": int32(2), "BL_PBL_PHYSICS": int32(8), "CU_PHYSICS": int32(0), "SF_LAKE_PHYSICS": int32(0), "SURFACE_INPUT_SOURCE": None, # Projection dependent attributes "SST_UPDATE": int32(0), "GRID_FDDA": int32(0), "GFDDA_INTERVAL_M": int32(0), "GFDDA_END_H": int32(0), "GRID_SFDDA": int32(0), "SGFDDA_INTERVAL_M": int32(0), "SGFDDA_END_H": int32(0), "WEST-EAST_PATCH_START_UNSTAG": None, # Projection dependent attributes "WEST-EAST_PATCH_END_UNSTAG": None, # Projection dependent attributes "WEST-EAST_PATCH_START_STAG": None, # Projection dependent attributes "WEST-EAST_PATCH_END_STAG": None, # Projection dependent attributes "SOUTH-NORTH_PATCH_START_UNSTAG": None, # Projection dependent attributes "SOUTH-NORTH_PATCH_END_UNSTAG": None, # Projection dependent attributes "SOUTH-NORTH_PATCH_START_STAG": None, # Projection dependent attributes "SOUTH-NORTH_PATCH_END_STAG": None, # Projection dependent attributes "BOTTOM-TOP_PATCH_START_UNSTAG": None, "BOTTOM-TOP_PATCH_END_UNSTAG": None, "BOTTOM-TOP_PATCH_START_STAG": None, "BOTTOM-TOP_PATCH_END_STAG": None, "GRID_ID": int32(1), "PARENT_ID": int32(0), "I_PARENT_START": int32(1), "J_PARENT_START": int32(1), "PARENT_GRID_RATIO": int32(1), "DT": float32(18.), "CEN_LAT": None, # Projection dependent attributes "CEN_LON": None, # Projection dependent attributes "TRUELAT1": None, # Projection dependent attributes "TRUELAT2": None, # Projection dependent attributes "MOAD_CEN_LAT": None, # Projection dependent attributes "STAND_LON": None, # Projection dependent attributes "POLE_LAT": None, # Projection dependent attributes "POLE_LON": None, # Projection dependent attributes "GMT": float32(self.time[0].hour), "JULYR": int32(self.time[0].year), "JULDAY": int32(self.time[0].strftime("%j")), "MAP_PROJ": None, # Projection dependent attributes "MMINLU": "MODIFIED_IGBP_MODIS_NOAH", "NUM_LAND_CAT": int32(41), "ISWATER": int32(17), "ISLAKE": int32(-1), "ISICE": int32(15), "ISURBAN": int32(13), "ISOILWATER": int32(14), "HISTORY": "", # Editable } # Editable attributes float_atts = ["DAMPCOEF", "KHDIF", "KVDIF", "CEN_LAT", "CEN_LON", "DT"] int_atts = ["BOTTOM-TOP_GRID_DIMENSION", "DIFF_OPT", "KM_OPT", "DAMP_OPT", "MP_PHYSICS", "RA_LW_PHYSICS", "RA_SW_PHYSICS", "SF_SFCLAY_PHYSICS", "SF_SURFACE_PHYSICS", "BL_PBL_PHYSICS", "CU_PHYSICS", "SF_LAKE_PHYSICS", "SURFACE_INPUT_SOURCE", "SST_UPDATE", "GRID_FDDA", "GFDDA_INTERVAL_M", "GFDDA_END_H", "GRID_SFDDA", "SGFDDA_INTERVAL_M", "SGFDDA_END_H", "BOTTOM-TOP_PATCH_START_UNSTAG", "BOTTOM-TOP_PATCH_END_UNSTAG", "BOTTOM-TOP_PATCH_START_STAG", "BOTTOM-TOP_PATCH_END_STAG", "GRID_ID", "PARENT_ID", "I_PARENT_START", "J_PARENT_START", "PARENT_GRID_RATIO", "NUM_LAND_CAT", "ISWATER", "ISLAKE", "ISICE", "ISURBAN", "ISOILWATER"] str_atts = ["GRIDTYPE", "MMINLU", "HISTORY"] for att_name, att_value in current_attributes.items(): if att_name in int_atts: self.global_attrs[att_name] = int32(att_value) elif att_name in float_atts: self.global_attrs[att_name] = float32(att_value) elif att_name in str_atts: self.global_attrs[att_name] = str(att_value) # Projection dependent attributes if isinstance(self, nes.LCCNes) or isinstance(self, nes.MercatorNes): self.global_attrs["WEST-EAST_GRID_DIMENSION"] = int32(len(self._full_x["data"]) + 1) self.global_attrs["SOUTH-NORTH_GRID_DIMENSION"] = int32(len(self._full_y["data"]) + 1) self.global_attrs["DX"] = float32(self._full_x["data"][1] - self._full_x["data"][0]) self.global_attrs["DY"] = float32(self._full_y["data"][1] - self._full_y["data"][0]) self.global_attrs["SURFACE_INPUT_SOURCE"] = int32(1) self.global_attrs["WEST-EAST_PATCH_START_UNSTAG"] = int32(1) self.global_attrs["WEST-EAST_PATCH_END_UNSTAG"] = int32(len(self._full_x["data"])) self.global_attrs["WEST-EAST_PATCH_START_STAG"] = int32(1) self.global_attrs["WEST-EAST_PATCH_END_STAG"] = int32(len(self._full_x["data"]) + 1) self.global_attrs["SOUTH-NORTH_PATCH_START_UNSTAG"] = int32(1) self.global_attrs["SOUTH-NORTH_PATCH_END_UNSTAG"] = int32(len(self._full_y["data"])) self.global_attrs["SOUTH-NORTH_PATCH_START_STAG"] = int32(1) self.global_attrs["SOUTH-NORTH_PATCH_END_STAG"] = int32(len(self._full_y["data"]) + 1) self.global_attrs["POLE_LAT"] = float32(90) self.global_attrs["POLE_LON"] = float32(0) if isinstance(self, nes.LCCNes): self.global_attrs["MAP_PROJ"] = int32(1) self.global_attrs["TRUELAT1"] = float32(self.projection_data["standard_parallel"][0]) self.global_attrs["TRUELAT2"] = float32(self.projection_data["standard_parallel"][1]) self.global_attrs["MOAD_CEN_LAT"] = float32(self.projection_data["latitude_of_projection_origin"]) self.global_attrs["STAND_LON"] = float32(self.projection_data["longitude_of_central_meridian"]) self.global_attrs["CEN_LAT"] = float32(self.projection_data["latitude_of_projection_origin"]) self.global_attrs["CEN_LON"] = float32(self.projection_data["longitude_of_central_meridian"]) elif isinstance(self, nes.MercatorNes): self.global_attrs["MAP_PROJ"] = int32(3) self.global_attrs["TRUELAT1"] = float32(self.projection_data["standard_parallel"]) self.global_attrs["TRUELAT2"] = float32(0) self.global_attrs["MOAD_CEN_LAT"] = float32(self.projection_data["standard_parallel"]) self.global_attrs["STAND_LON"] = float32(self.projection_data["longitude_of_projection_origin"]) self.global_attrs["CEN_LAT"] = float32(self.projection_data["standard_parallel"]) self.global_attrs["CEN_LON"] = float32(self.projection_data["longitude_of_projection_origin"]) return None
[docs] def create_dimensions(self, netcdf): """ Create "time", "time_bnds", "lev", "lon" and "lat" dimensions. Parameters ---------- self : nes.Nes Nes Object. netcdf : Dataset netcdf4-python open dataset. """ netcdf.createDimension("Time", len(self.get_full_times())) netcdf.createDimension("DateStrLen", 19) netcdf.createDimension("emissions_zdim", len(self.get_full_levels()["data"])) if isinstance(self, nes.LCCNes): netcdf.createDimension("west_east", len(self._full_x["data"])) netcdf.createDimension("south_north", len(self._full_y["data"])) return None
[docs] def create_dimension_variables(self, netcdf): """ Create the "y" and "x" variables. Parameters ---------- self : nes.Nes A Nes Object. netcdf : Dataset NetCDF object. """ times = netcdf.createVariable("Times", "S1", ("Time", "DateStrLen", )) times[:] = create_times_var(self) return None
# noinspection DuplicatedCode
[docs] def create_variables(self, netcdf): """ Create the netCDF file variables. Parameters ---------- self : nes.Nes Nes Object. netcdf : Dataset netcdf4-python open dataset. """ for var_name, var_info in self.variables.items(): var = netcdf.createVariable(var_name, "f", ("Time", "emissions_zdim", "south_north", "west_east",), zlib=self.zip_lvl > 0, complevel=self.zip_lvl) var.FieldType = var_info["FieldType"] var.MemoryOrder = var_info["MemoryOrder"] var.description = var_info["description"] var.units = var_info["units"] var.stagger = var_info["stagger"] var.coordinates = var_info["coordinates"] if var_info["data"] is not None: if self.info: print("Rank {0:03d}: Filling {1})".format(self.rank, var_name)) if isinstance(var_info["data"], int) and var_info["data"] == 0: var[self.write_axis_limits["t_min"]:self.write_axis_limits["t_max"], self.write_axis_limits["z_min"]:self.write_axis_limits["z_max"], self.write_axis_limits["y_min"]:self.write_axis_limits["y_max"], self.write_axis_limits["x_min"]:self.write_axis_limits["x_max"]] = 0 elif len(var_info["data"].shape) == 4: var[self.write_axis_limits["t_min"]:self.write_axis_limits["t_max"], self.write_axis_limits["z_min"]:self.write_axis_limits["z_max"], self.write_axis_limits["y_min"]:self.write_axis_limits["y_max"], self.write_axis_limits["x_min"]:self.write_axis_limits["x_max"]] = var_info["data"] return None