Skip to content

Commit

Permalink
Merge pull request #12 from chen-0126/master
Browse files Browse the repository at this point in the history
Add support for CMAQ and bug fixes
  • Loading branch information
Yaqiang authored Jun 24, 2023
2 parents c2c33aa + 335651c commit 245645c
Show file tree
Hide file tree
Showing 32 changed files with 689 additions and 280 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
EMIPS:EMission Inventory Processing System
============================================

[![Join the chat at https://gitter.im/meteoinfo/community](https://badges.gitter.im/meteoinfo/community/meteoinfo.svg)](https://gitter.im/meteoinfo/community)

Installation
------------

Expand Down Expand Up @@ -34,6 +32,8 @@ Learn more about MeteoInfo and EMIPS in its official documentation at http://met
Version
-------

EMIPS-1.1 was released (2023-06-24). Added support for CMAQ and bug fixes.

EMIPS-1.0 was released (2023-01-26).

Author
Expand Down Expand Up @@ -61,7 +61,7 @@ Sci. Total Environ. 870, 161909. https://doi.org/10.1016/j.scitotenv.2023.161909
License
-------

Copyright 2019-2022, EMIPS Developers
Copyright 2019-2023, EMIPS Developers

Licensed under the LGPL License, Version 3.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
Binary file modified emips/__init__$py.class
Binary file not shown.
4 changes: 2 additions & 2 deletions emips/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
import chem_spec
import vertical_alloc

__version__ = '1.0'
__version__ = '1.1'

import os
ge_data_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'ge_data')

version = 'EMIPS-1.0 (2022.08.04)'
version = 'EMIPS-1.1 (2023.06.24)'
__all__ = ['ge_data_dir', 'utils', 'temp_alloc', 'spatial_alloc', 'chem_spec', 'vertical_alloc', 'version']
Binary file modified emips/chem_spec/chemical_speciation$py.class
Binary file not shown.
36 changes: 29 additions & 7 deletions emips/chem_spec/chemical_speciation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .pollutant import Pollutant
import mipylib.numeric as np

__all__ = ['get_pollutant_profile', 'read_file', 'speciation', 'get_model_species_wrf']
__all__ = ['get_pollutant_profile', 'read_file', 'speciation', 'get_model_species_WRFChem', 'get_model_species_CMAQ']


def get_pollutant_profile(poll_profiles, pollutant):
Expand Down Expand Up @@ -89,17 +89,17 @@ def speciation(data, pollutant_profile):
return s_data


def get_model_species_wrf(mechanism_name):
def get_model_species_WRFChem(mechanism_name):
"""
Get species in wrf-chem in different chemical mechanism.
Get species under different chemical mechanisms in WRF-Chem.
(E_PM10: saprc99, cb05 E_PM_10: radm2, mozart)
:param mechanism_name: (*string*) The name of chemical mechanism.
:return: (*list*) All species(out_species) and aerosols(out_species_aer) under chemical mechanisms.
"""
mechanism_name = mechanism_name.lower()
if mechanism_name == 'cb05':
##########################################
# ------CB05, emiss_opt=15, (52, 15)------#
#------CB05, emiss_opt=15, (52, 15)------#
##########################################
out_species = ['E_ACET', 'E_PAR', 'E_ALK3', 'E_ALK4', 'E_ALK5', 'E_TOL', 'E_XYL', 'E_BALD',
'E_ALD2', 'E_CCOOH', 'E_CO', 'E_CRES', 'E_ETH', 'E_ETHA', 'E_GLY', 'E_FORM',
Expand All @@ -113,7 +113,7 @@ def get_model_species_wrf(mechanism_name):

elif mechanism_name == 'radm2':
##########################################
# ------RADM2, emiss_opt=3, (42, 19)------#
#------RADM2, emiss_opt=3, (42, 19)------#
##########################################
out_species = ['E_ISO', 'E_SO2', 'E_NO', 'E_NO2', 'E_CO', 'E_CH4', 'E_ETH', 'E_HC3',
'E_HC5', 'E_HC8', 'E_XYL', 'E_OL2', 'E_OLT', 'E_OLI', 'E_TOL', 'E_CSL',
Expand All @@ -126,7 +126,7 @@ def get_model_species_wrf(mechanism_name):
'E_ORGJ_BB', 'E_CLI', 'E_CLJ']
elif mechanism_name == 'saprc99':
###########################################
# -----SAPRC99, emiss_opt=13, (55, 15)-----#
#-----SAPRC99, emiss_opt=13, (55, 15)-----#
###########################################
out_species = ['E_SO2', 'E_C2H6', 'E_C3H8', 'E_C2H2', 'E_ALK3', 'E_ALK4', 'E_ALK5', 'E_ETHENE',
'E_C3H6', 'E_OLE1', 'E_OLE2', 'E_ARO1', 'E_ARO2', 'E_HCHO', 'E_CCHO', 'E_RCHO',
Expand All @@ -140,7 +140,7 @@ def get_model_species_wrf(mechanism_name):

elif mechanism_name == 'mozart':
##########################################
# -----MOZART, emiss_opt=10, (53, 23)-----#
#-----MOZART, emiss_opt=10, (53, 23)-----#
##########################################
out_species = ['E_CO', 'E_NO', 'E_NO2', 'E_BIGALK', 'E_BIGENE', 'E_C2H4', 'E_C2H5OH', 'E_C2H6',
'E_C3H6', 'E_C3H8', 'E_CH2O', 'E_CH3CHO', 'E_CH3COCH3', 'E_CH3OH', 'E_MEK', 'E_SO2',
Expand All @@ -154,3 +154,25 @@ def get_model_species_wrf(mechanism_name):
'E_CO_A', 'E_ORGI_A', 'E_ORGJ_A', 'E_CO_BB', 'E_ORGI_BB', 'E_ORGJ_BB', 'E_PM_10']

return out_species, out_species_aer


def get_model_species_CMAQ(mechanism_name):
"""
Get species under different chemical mechanisms in CMAQ.
:param mechanism_name: (*string*) The name of chemical mechanism.
:return: (*list*) All species(out_species) and aerosols(out_species_aer) under chemical mechanisms.
"""
mechanism_name = mechanism_name.lower()
if mechanism_name == 'cb05':
########################################
#-----CB05, emiss_opt=10, (42, 19)-----#
########################################
out_species = ['ALD2', 'CO', 'ETH', 'FORM', 'ISOP', 'NH3', 'NO', 'NO2', 'UNR',
'OLE', 'PAR', 'PEC', 'PMC', 'PMOTHR', 'PNO3', 'POC', 'PSO4', 'PCL',
'PNH4', 'PNA', 'PMG', 'PK', 'PCA', 'PNCOM', 'PFE', 'PAL', 'PSI', 'PTI',
'PMN', 'PH2O', 'SO2', 'SULF', 'TERP', 'TOL', 'XYL', 'MEOH', 'ETOH',
'ETHA', 'ALDX', 'IOLE', 'CH4', 'AACD']
out_species_aer = ['PEC', 'PMC', 'PMOTHR', 'PNO3', 'POC', 'PSO4', 'PCL', 'PNH4', 'PNA',
'PMG', 'PK', 'PCA', 'PNCOM', 'PFE', 'PAL', 'PSI', 'PTI', 'PMN', 'PH2O']

return out_species, out_species_aer
Binary file modified emips/chem_spec/species_profile$py.class
Binary file not shown.
7 changes: 6 additions & 1 deletion emips/chem_spec/species_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ def read_string(cls, line, mechanism=None):
data = line.split()
pollutant = Pollutant(data[1])
if mechanism is None:
species = Species(data[2])
if data[2] == 'NO':
species = Species(data[2], molar_mass=30)
elif data[2] == 'NO2':
species = Species(data[2], molar_mass=46)
else:
species = Species(data[2])
else:
species = mechanism.species(data[2])
return SpeciesProfile(pollutant, species, float(data[3]), float(data[4]), float(data[5]))
2 changes: 1 addition & 1 deletion emips/loadApp.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class LoadApp(PluginBase):

def __init__(self):
self.setName("EMIPS")
self.setAuthor("Yaqiang Wang & Wenchong Chen")
self.setAuthor("Yaqiang Wang & Wencong Chen")
self.setVersion("1.0")
self.setDescription("EMission Inventory Processing System")
self.app_menu_item = None
Expand Down
8 changes: 8 additions & 0 deletions emips/run/run_with_script/for_CMAQ/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Read before running
----------------------

1, Set parameters(time, directory and dimension etc.) in "for_CMAQ.py".




Empty file.
137 changes: 137 additions & 0 deletions emips/run/run_with_script/for_CMAQ/for_CMAQ.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""
# Author: Wencong Chen
# Date: 2023-06-21
# Purpose: Convert model-ready emission file to meet CMAQ's input requirements.
(height allocation, merge files of different sectors, convert pollutants and projection)
"""

import time
start_time = time.clock()
#Set current working directory
from inspect import getsourcefile
dir_run = os.path.dirname(os.path.abspath(getsourcefile(lambda:0)))
if not dir_run in sys.path:
sys.path.append(dir_run)

import os
from emips.spatial_alloc import GridDesc
from emips import ge_data_dir
from emips.utils import SectorEnum
from emips.chem_spec import get_model_species_CMAQ
from collections import OrderedDict
import datetime
import height
import merge
import transform
import proj

#Choose Chemical mechanism
mechanism_name = 'cb05'
print('--------CHEMICAL MECHANISM: {}--------'.format(mechanism_name.upper()))

#Set year and month
year = 2019
month = 1

#Set input&output file path
dir_inter = os.path.join(r'G:\CMAQ', mechanism_name, r'merge\{0:}\{0:}{1:>02d}'.format(year, month))

#Set original grid
original_proj = geolib.projinfo()
original_grid = GridDesc(original_proj, x_orig=64., x_cell=0.25, x_num=324,
y_orig=15., y_cell=0.25, y_num=180)

#Set target grid
ncols = 164 #the number of x dimension
nrows = 97 #the number of y dimension
nlays = 7 #the number of z dimension
xcell = 36000.0 #Grid spacing in x dimensions
ycell = 36000.0 #Grid spacing in y dimensions
proj_name = 'lcc' #Name of the projection ('lcc' is Lambert Conformal Conic projection)
xcent = 110.0 #Longitude of projection center
ycent = 34.0 #Latitude of projection center
p_alp = 25.0 #First standard parallel
p_bet = 40.0 #Second standard parallel
p_gam = 110.0
radius = 6370000.0 #Radius of the sphere(unit: meter)

if ncols % 2 == 0:
xorig = (ncols-2) / 2.0 * xcell + xcell / 2.0
else:
xorig = (ncols-1) / 2.0 * xcell
if nrows % 2 == 0:
yorig = (nrows-2) / 2.0 * ycell + ycell / 2.0
else:
yorig = (nrows-1) / 2.0 * ycell
target_proj = geolib.projinfo(proj=proj_name, lon_0=xcent, lat_0=ycent, lat_1=p_alp, lat_2=p_bet, a=radius, b=radius)
target_grid = GridDesc(target_proj, x_orig=-xorig, x_cell=xcell, x_num=ncols,
y_orig=-yorig, y_cell=ycell, y_num=nrows)

#Set path of the vertical allocate file
vert_prof_file = os.path.join(ge_data_dir, 'height.txt')

#Set sectors that need to be processed
sectors = [SectorEnum.INDUSTRY, SectorEnum.AGRICULTURE, SectorEnum.ENERGY,
SectorEnum.RESIDENTIAL, SectorEnum.TRANSPORT]

#Acquisition of model species (gases and aerosol) and aerosol names based on chemical mechanisms
out_species, out_species_aer = get_model_species_CMAQ(mechanism_name)
varlist = ''
for i in out_species:
varlist = varlist + '{:<16s}'.format(i)

#Set global attributes
gattrs = OrderedDict()
#gattrs['Conventions'] = 'CF-1.6'
gattrs['TITLE'] = "Created using MeteoInfo, mechanism: {}".format(mechanism_name.upper())
gattrs['IOAPI_VERSION'] = "{:<80s}".format('$Id: @(#) ioapi library version 3.1 $')
gattrs['EXEC_ID'] = "{:<80s}".format('????????????????')
gattrs['FTYPE'] = 1
gattrs['CDATE'] = datetime.date(year=year, month=month, day=1).strftime("%Y%j") #test
gattrs['CTIME'] = 81255 #test
gattrs['WDATE'] = datetime.date(year=year, month=month, day=1).strftime("%Y%j") #test
gattrs['WTIME'] = 81255 #test
gattrs['SDATE'] = datetime.date(year=year, month=month, day=1).strftime("%Y%j")
gattrs['STIME'] = 0
gattrs['TSTEP'] = 10000
gattrs['NTHIK'] = 1
gattrs['NCOLS'] = ncols
gattrs['NROWS'] = nrows
gattrs['NLAYS'] = nlays
gattrs['NVARS'] = len(out_species)
gattrs['GDTYP'] = 2
gattrs['P_ALP'] = p_alp
gattrs['P_BET'] = p_bet
gattrs['P_GAM'] = p_gam
gattrs['XCENT'] = xcent
gattrs['YCENT'] = ycent
gattrs['XORIG'] = -xorig
gattrs['YORIG'] = -yorig
gattrs['XCELL'] = xcell
gattrs['YCELL'] = ycell
gattrs['VGTYP'] = 2 #test
gattrs['VGTOP'] = 10000.0
gattrs['VGLVLS'] = 1.0, 0.995, 0.988, 0.98, 0.97, 0.956, 0.938, 0.893, 0.839, 0.777, 0.702, 0.582, 0.4, 0.2, 0.0
gattrs['GDNAM'] = "{:<16s}".format('M_36_08CHINA')
gattrs['UPNAM'] = ""
gattrs['VAR-LIST'] = varlist
gattrs['FILEDESC'] = ""
gattrs['HISTORY'] = ""

#Run all scripts
print('Allocate according to height...')
height.run(year, month, dir_inter, original_grid, sectors, nlays, vert_prof_file)
print('Merge data from different sectors...')
merge.run(year, month, dir_inter, original_grid, sectors, nlays)
print('Distribution of particulate matter and change unit...')
transform.run(year, month, dir_inter, original_grid, out_species, out_species_aer, nlays)
print('Conversion projection...')
proj.run(year, month, dir_inter, original_grid, target_grid, out_species, out_species_aer, gattrs, mechanism_name, nlays)
print('-----------------------------')
print('--------All finished!--------')
print('-----------------------------')

#Calculate running time
end_time = time.clock()
time = (end_time - start_time) / 60.0
print('Time: {:.2f}min'.format(time))
93 changes: 93 additions & 0 deletions emips/run/run_with_script/for_CMAQ/height.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from emips.spatial_alloc import GridDesc
from collections import OrderedDict
from mipylib import dataset
import mipylib.numeric as np
import os
from emips.utils import emis_util
import emips

out_species_unit = ['PEC', 'POA', 'PMFINE', 'PNO3', 'PSO4', 'PMC']


def run(year, month, dir_inter, model_grid, sectors, nlays, z_file):
"""
Allocate data to different heights.
:param year: (*int*) Year.
:param month: (*int*) Month.
:param dir_inter: (*string*) The directory where data is stored.
:param model_grid: (*GridDesc*) Model data grid describe.
:param sectors: (*GridDesc*) The sectors need to be processed.
:param nlays: (*int*) The z-dimension of the output data.
:param z_file: (*string*) The path of the vertical allocate file.
"""
print('Define dimension and global attributes...')
tdim = np.dimension(np.arange(24), 'HOUR')
ydim = np.dimension(model_grid.y_coord, 'ROW', 'Y')
xdim = np.dimension(model_grid.x_coord, 'COL', 'X')
zdim = np.dimension(np.arange(nlays), 'LAY')
dims = [tdim, zdim, ydim, xdim]

gattrs = OrderedDict()
gattrs['Conventions'] = 'CF-1.6'
gattrs['Tools'] = 'Created using MeteoInfo'

for sector in sectors:
fn = dir_inter + '\emis_{}_{}_{}_hour.nc'.format(sector.name, year, month)
print('File input: {}'.format(fn))
dimvars = []
if os.path.exists(fn):
f = dataset.addfile(fn)
for var in f.varnames:
if var == 'lat' or var == 'lon':
continue
else:
dimvar = dataset.DimVariable()
dimvar.name = var
dimvar.dtype = np.dtype.float
dimvar.dims = dims
dimvar.addattr('description', "EMISSION_{}".format(var))
if var in out_species_unit:
dimvar.addattr('units', 'g/m2/s')
else:
dimvar.addattr('units', 'mole/m2/s')
dimvars.append(dimvar)

out_fn = dir_inter + '\emis_{}_{}_{}_hour_height.nc'.format(sector.name, year, month)
print('Create output file:{}'.format(out_fn))
ncfile = dataset.addfile(out_fn, 'c', largefile=True)
ncfile.nc_define(dims, gattrs, dimvars)

data = np.zeros((tdim.length, zdim.length, ydim.length, xdim.length))
dd = np.zeros((tdim.length, zdim.length, ydim.length, xdim.length))
# get vertical profiles
scc = emis_util.get_scc(sector)
vertical_pro = emips.vertical_alloc.read_file(z_file, scc)
if nlays != len(vertical_pro.get_ratios()):
print('----------Warning-----------\nThe z-dimensions in the vertical profile file and the output file do not match.\n----------Warning-----------')
# read, merge and output
if round(vertical_pro.get_ratios()[0], 2) != 1.0:
print('Allocating: {}'.format(sector.name))
else:
print('Do not need to be allocated: {}'.format(sector.name))
print('Write data to file...')
for var in f.varnames:
if var == 'lat' or var == 'lon':
continue
else:
print(var)
dd[:, 0, :, :] = f[var][:]
if round(vertical_pro.get_ratios()[0], 2) == 1.0:
data[:, 0, :, :] = dd[:, 0, :, :]
else:
for lay in np.arange(len(vertical_pro.get_ratios())):
data[:, lay, :, :] = dd[:, 0, :, :] * vertical_pro.get_ratios()[lay]
# Turn nan to zero
data[data == np.nan] = 0
ncfile.write(var, data)
ncfile.close()
f.close()
else:
print('File not exist: {}'.format(fn))
continue
print('Allocate of height finished!')
Loading

0 comments on commit 245645c

Please sign in to comment.