Example: convert DEPHY forcing to DHARMA LES and ModelE3 SCM formats#

This code reads the COMBLE-MIP LES/SCM forcing file that is supplied in the DEPHY format and writes input files that are formatted for the DHARMA LES (ASCII) and the ModelE3 ESM SCM (NetCDF)

Contributed by Ann Fridlind from NASA/GISS on 1/24/23

Import libraries#

import numpy as np
import pandas as pd
import xarray as xr
import netCDF4
from netCDF4 import Dataset

Specify local write directory#

If on the ARM JupyterHub, it is recommended to create and specify a local directory that is outside of the COMBLE-MIP repository to write input files in your model’s format

If on the ARM JupyterHub, these files can be conveniently downloaded to your local machine and will remain when you log out

my_write_dir = '../../../my_input_files/'

Specify wind forcing type#

do_geo = True

Read the COMBLE-MIP forcing file#

NOTE: ERROR 1 message can be ignored

dephy_filename = '../forcing/COMBLE_INTERCOMPARISON_FORCING_V2.3.nc'
dephy = xr.open_dataset(dephy_filename)
dephy = dephy.squeeze()
dephy
ERROR 1: PROJ: proj_create_from_database: Open of /opt/conda/share/proj failed
<xarray.Dataset>
Dimensions:        (time: 21, lev: 136, zw_grid: 160)
Coordinates:
    t0             datetime64[ns] 2020-03-12T22:00:00
    lat            float32 74.5
    lon            float32 9.9
  * time           (time) datetime64[ns] 2020-03-12T22:00:00 ... 2020-03-13T1...
  * lev            (lev) float64 18.17 37.83 59.35 ... 6.851e+04 7.117e+04
  * zw_grid        (zw_grid) float64 0.0 20.0 45.0 ... 6.89e+03 6.95e+03 7e+03
Data variables: (12/19)
    pressure       (lev) float64 ...
    u              (lev) float64 ...
    v              (lev) float64 ...
    temp           (lev) float64 ...
    theta          (lev) float64 ...
    qv             (lev) float64 ...
    ...             ...
    theta_nudging  (time, lev) float64 ...
    qv_nudging     (time, lev) float64 ...
    w_nudging      (time, lev) float64 ...
    ts             (time) float64 ...
    lat_ref        (time) float32 ...
    lon_ref        (time) float32 ...
Attributes: (12/25)
    title:                          Forcing and initial conditions for 13 Mar...
    reference:                      https://arm-development.github.io/comble-...
    authors:                        Timothy W. Juliano (NCAR/RAL, tjuliano@uc...
    version:                        Created on 2023-12-01
    format_version:                 DEPHY SCM format version 2.0
    script:                         create_comble_forcing_v2.3.ipynb
    ...                             ...
    lat:                            74.5 deg N
    droplet_activation_diagnostic:  droplet number concentration fixed to 20 ...
    ice_nucleation_diagnostic:      total ice number concentration fixed to 2...
    droplet_activation_prognostic:  time and space varying aerosol initialize...
    aerosol_surface_source:         modal emissions based on Jaegle et al. (2...
    ice_nucleation_prognostic:      assume that all sea spray mode and half o...

Create input files for DHARMA LES in ASCII format#

Write initial sounding, ozone profile, forcing and vertical grid files, and echo some values for manual entry to the input parameter file

Echo values for manual input#

# echo initial surface pressure to enter into DHARMA input parameter file manually
dephy.ps.values.round(1)
99544.5
# echo surface temperature hourly values to enter into DHARMA input parameter file manually
dephy.ts.round(1)
<xarray.DataArray 'ts' (time: 21)>
array([247. , 247. , 249.9, 271.1, 275.4, 275.2, 275.5, 275.8, 276.3,
       276.6, 276.8, 277.3, 277.8, 278. , 278.2, 278.5, 279.1, 279.4,
       279.4, 279.3, 277.8])
Coordinates:
    t0       datetime64[ns] 2020-03-12T22:00:00
    lat      float32 74.5
    lon      float32 9.9
  * time     (time) datetime64[ns] 2020-03-12T22:00:00 ... 2020-03-13T18:00:00
Attributes:
    units:      K
    long_name:  surface temperature
dephy
<xarray.Dataset>
Dimensions:        (time: 21, lev: 136, zw_grid: 160)
Coordinates:
    t0             datetime64[ns] 2020-03-12T22:00:00
    lat            float32 74.5
    lon            float32 9.9
  * time           (time) datetime64[ns] 2020-03-12T22:00:00 ... 2020-03-13T1...
  * lev            (lev) float64 18.17 37.83 59.35 ... 6.851e+04 7.117e+04
  * zw_grid        (zw_grid) float64 0.0 20.0 45.0 ... 6.89e+03 6.95e+03 7e+03
Data variables: (12/19)
    pressure       (lev) float64 ...
    u              (lev) float64 ...
    v              (lev) float64 ...
    temp           (lev) float64 ...
    theta          (lev) float64 ...
    qv             (lev) float64 ...
    ...             ...
    theta_nudging  (time, lev) float64 ...
    qv_nudging     (time, lev) float64 ...
    w_nudging      (time, lev) float64 ...
    ts             (time) float64 247.0 247.0 249.9 271.1 ... 279.4 279.3 277.8
    lat_ref        (time) float32 ...
    lon_ref        (time) float32 ...
Attributes: (12/25)
    title:                          Forcing and initial conditions for 13 Mar...
    reference:                      https://arm-development.github.io/comble-...
    authors:                        Timothy W. Juliano (NCAR/RAL, tjuliano@uc...
    version:                        Created on 2023-12-01
    format_version:                 DEPHY SCM format version 2.0
    script:                         create_comble_forcing_v2.3.ipynb
    ...                             ...
    lat:                            74.5 deg N
    droplet_activation_diagnostic:  droplet number concentration fixed to 20 ...
    ice_nucleation_diagnostic:      total ice number concentration fixed to 2...
    droplet_activation_prognostic:  time and space varying aerosol initialize...
    aerosol_surface_source:         modal emissions based on Jaegle et al. (2...
    ice_nucleation_prognostic:      assume that all sea spray mode and half o...

Write input sounding file#

# use a list to accumulate output ASCII lines
lines_sounding = []

# number of lines to be read
n_z = dephy.dims['lev']
str_firstline = str(n_z+1) # number of heights after adding surface point below
lines_sounding.append(str_firstline)

# arrays to be reported in columns
z_arr = dephy.coords['lev'].values
th_arr = dephy['theta'].values.squeeze() # K
qt_arr = dephy['qv'].values.squeeze()*1000. # g/kg
u_arr = dephy['u'].values.squeeze() # m/s
v_arr = dephy['v'].values.squeeze() # m/s

# add a surface point (required by DHARMA code)
z0_arr = np.array([0.,th_arr[0],qt_arr[0],u_arr[0],v_arr[0]]) # well-mixed assumption
str_z0 = np.array2string(z0_arr,formatter={'float_kind':lambda z0_arr:"%12.4f" % z0_arr})[1:-1]
lines_sounding.append(str_z0)                        

for kk in range(n_z):
    vars_arr = np.array([z_arr[kk],th_arr[kk],qt_arr[kk],u_arr[kk],v_arr[kk]])
    str_vars = np.array2string(vars_arr,formatter={'float_kind':lambda vars_arr:"%12.4f" % vars_arr})[1:-1]
    lines_sounding.append(str_vars)
    
str_headline = str('      z(m)        th(K)     qt(g/kg)       u(m/s)       v(m/s)')
lines_sounding.append(str_headline)

# write list contents to ASCII file
filename_sounding_LES = my_write_dir + 'DHARMA_LES_ASCII_comble.input_sounding'
with open(filename_sounding_LES,mode='wt',encoding='utf-8') as sounding_file:
    sounding_file.write('\n'.join(lines_sounding))

Write ozone profile#

# use a list to accumulate output ASCII lines
lines_sounding = []

# number of lines to be read
n_z = dephy.dims['lev']
str_firstline = str(n_z+1) # number of heights after adding surface point below
lines_sounding.append(str_firstline)

# arrays to be reported in columns
z_arr = dephy.coords['lev'].values
p_arr = dephy['pressure'].values.squeeze()/100. # mb
o3_arr = dephy['o3'].values.squeeze() # kg/kg
o3ppb_arr = o3_arr*28.96/48.*1e9 # convert with molecular weights

# add a surface point (required by DHARMA code)
z0_arr = np.array([0.,dephy.ps.values,o3ppb_arr[0],o3_arr[0]]) # well-mixed assumption
str_z0 = np.array2string(z0_arr,formatter={'float_kind':lambda z0_arr:"%12.4e" % z0_arr})[1:-1]
lines_sounding.append(str_z0)                        

for kk in range(n_z):
    vars_arr = np.array([z_arr[kk],p_arr[kk],o3ppb_arr[kk],o3_arr[kk]])
    str_vars = np.array2string(vars_arr,formatter={'float_kind':lambda vars_arr:"%12.4e" % vars_arr})[1:-1]
    lines_sounding.append(str_vars)
    
str_headline = str('      z(m)        P(mb)     O3(ppbv)       O3(kg/kg)')
lines_sounding.append(str_headline)

# write list contents to ASCII file
filename_o3_LES = my_write_dir + 'DHARMA_LES_ASCII_comble.input_o3'
with open(filename_o3_LES,mode='wt',encoding='utf-8') as sounding_file:
    sounding_file.write('\n'.join(lines_sounding))

Write forcing file#

# create thermodynamic forcing file contents as list
lines_forcing = []

# limit to bottom 8 km for brevity
n_zf = np.max(np.where(dephy.lev<8000.))

n_t = dephy.dims['time']
str_firstline = str(n_zf+1)+' '+str(n_t) # extra line for z=0
lines_forcing.append(str_firstline) # number of heights, times

for tt in range(n_t):
    if do_geo:
        ## for geostrophic wind
        u_arr = dephy['ug'][tt,:].values # m/s, sensitivity test
        v_arr = dephy['vg'][tt,:].values # m/s, sensitivity test
    else:
        ## for nudging
        u_arr = dephy['u_nudging'][tt,:].values # m/s, sensitivity test
        v_arr = dephy['v_nudging'][tt,:].values # m/s, sensitivity test
    om_arr = dephy['w_nudging'][tt,:].values # Pa/s
    p_arr = dephy['pressure'].values # Pa
    t_arr = dephy['temp'].values # K
    qv_arr = dephy['qv'].values # kg/kg
    rho_arr = p_arr/(t_arr*287.*(1.+0.608*qv_arr)) # kg/m3
    w_arr = -om_arr/(9.81*rho_arr.squeeze())*100. # cm/s, sensitivity test
    th_arr = -np.ones(n_zf) # no thermo forcing
    qv_arr = -np.ones(n_zf)
    
    z0_arr = np.array([0.,0.,-1.,-1.,u_arr[0],v_arr[0]])
    str_z0 = np.array2string(z0_arr,formatter={'float_kind':lambda z0_arr:"%10.2f" % z0_arr})[1:-1]
    lines_forcing.append(str_z0)
    
    for kk in range(n_zf):
        vars_arr = np.array([z_arr[kk],w_arr[kk],th_arr[kk],qv_arr[kk],u_arr[kk],v_arr[kk]])
        str_vars = np.array2string(vars_arr,formatter={'float_kind':lambda vars_arr:"%10.2f" % vars_arr})[1:-1]
        lines_forcing.append(str_vars)
        
    lines_forcing.append(str(tt))
    
str_endlines1 = str('      z(m)    w(cm/s)      th(K)  qv(kg/kg)    ug(m/s)    vg(m/s)')
lines_forcing.append(str_endlines1)

str_endlines2 = str('time(h)')
lines_forcing.append(str_endlines2)

# write list contents to ASCII file
filename_forcing_LES = my_write_dir + 'DHARMA_LES_ASCII_comble.input_forcing'
with open(filename_forcing_LES,mode='wt',encoding='utf-8') as forcing_file:
    forcing_file.write('\n'.join(lines_forcing))

Write vertical grid file#

# create vertical grid file contents as list
lines_zw = []

n_zw = dephy.dims['zw_grid'] # number of heights
lines_zw.append(str(n_zw))

for kk in range(n_zw):
    lines_zw.append(str(dephy['zw_grid'][kk].values))

# write vertical grid to ASCII file
filename_zw_LES = my_write_dir + 'DHARMA_LES_ASCII_comble.input_z'
with open(filename_zw_LES,mode='wt',encoding='utf-8') as zw_file:
    zw_file.write('\n'.join(lines_zw))

Create input files for ModelE3 SCM#

Write initialization and forcing files in NetCDF format

Write initialization file#

# open file with all components at initial time only
filename_init_SCM = my_write_dir + 'SCM_COMBLE-MIP_init.nc'
scm_init = Dataset('./' + filename_init_SCM,mode='w',format='NETCDF3_CLASSIC')

# create dimensions
lev_dim = scm_init.createDimension('lev', n_z) # level axis is pressure for SCM
time_dim = scm_init.createDimension('time', 1) # initial time only

# create variables
lev = scm_init.createVariable('lev', np.float64, ('lev',))
lev.long_name = 'air pressure'
lev.units = 'mb'
lev[:] = dephy['pressure'].values/100.

Ps = scm_init.createVariable('Ps', np.float64, ('time',))
Ps.long_name = 'surface pressure'
Ps.units = 'mb'
Ps[:] = dephy.ps.values.round(1)/100.

T = scm_init.createVariable('T', np.float64, ('lev',))
T.long_name = 'air temperature'
T.units = 'K'
T[:] = dephy['temp'].values

Q = scm_init.createVariable('Q', np.float64, ('lev',))
Q.long_name = 'water vapor mixing ratio'
Q.units = 'kg kg-1'
Q[:] = dephy['qv'].values

# create time arrays
year = scm_init.createVariable('year', np.int16, ('time',))
year[:] = pd.to_datetime(dephy['time'].values[0]).year
month = scm_init.createVariable('month', np.int16, ('time',))
month[:] = pd.to_datetime(dephy['time'].values[0]).month
day = scm_init.createVariable('day', np.int16, ('time',))
day[:] = pd.to_datetime(dephy['time'].values[0]).day
hour = scm_init.createVariable('hour', np.int16, ('time',))
hour[:] = pd.to_datetime(dephy['time'].values[0]).hour

# close
scm_init.close()

Write forcing file#

# open forcing file with all components on same time dimension
filename_forcing_SCM = my_write_dir + 'SCM_COMBLE-MIP_force.nc'
scm_force = Dataset('./' + filename_forcing_SCM,mode='w',format='NETCDF3_CLASSIC')

# create dimensions
lev_dim = scm_force.createDimension('lev', n_z) # level axis is pressure for SCM
time_dim = scm_force.createDimension('time', n_t)

# create variables
lev = scm_force.createVariable('lev', np.float64, ('lev'))
lev.long_name = 'air pressure'
lev.units = 'mb'
lev[:] = dephy['pressure'].values/100.

ts = scm_force.createVariable('Tskin', np.float32, ('time'))
ts.long_name = 'surface temperature'
ts.units = 'K'
ts[:] = dephy['ts'].values

ug = scm_force.createVariable('Ug', np.float64, ('time','lev',))
ug.units = 'm s-1'
ug.long_name = 'geostrophic zonal wind'
ug[:] = dephy['ug'].values

vg = scm_force.createVariable('Vg', np.float64, ('time','lev',))
vg.units = 'm s-1'
vg.long_name = 'geostrophic meridional wind'
vg[:] = dephy['vg'].values

# create time arrays
year = scm_force.createVariable('year', np.int16, ('time',))
year[:] = pd.to_datetime(dephy['time'].values).year
month = scm_force.createVariable('month', np.int16, ('time',))
month[:] = pd.to_datetime(dephy['time'].values).month
day = scm_force.createVariable('day', np.int16, ('time',))
day[:] = pd.to_datetime(dephy['time'].values).day
hour = scm_force.createVariable('hour', np.int16, ('time',))
hour[:] = pd.to_datetime(dephy['time'].values).hour

# close
scm_force.close()