diff --git a/exp/frierson_dry_heating/frierson_test_case.py b/exp/frierson_dry_heating/frierson_test_case.py new file mode 100644 index 000000000..c0b430af1 --- /dev/null +++ b/exp/frierson_dry_heating/frierson_test_case.py @@ -0,0 +1,187 @@ +import os + +import numpy as np + +from isca import IscaCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +NCORES = 16 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = IscaCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics +exp = Experiment('frierson_test_experiment_dry_heating_mk8', codebase=cb) + +exp.inputfiles = [ os.path.join(base_dir,'input/heating_rate.nc'),os.path.join(GFDL_BASE,'input/rrtm_input_files/ozone_1990.nc')] + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_daily', 1, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk') +diag.add_field('dynamics', 'pk') +diag.add_field('atmosphere', 'precipitation', time_avg=True) +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) +diag.add_field('dynamics', 'vor', time_avg=True) +diag.add_field('dynamics', 'div', time_avg=True) +diag.add_field('hs_forcing', 'local_heating', time_avg=True) + +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 2, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos':720, + 'current_date' : [1,1,1,0,0,0], + 'calendar' : 'thirty_day' + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':3.21e-05, + 'two_stream_gray': True, #Use grey radiation + 'convection_scheme': 'SIMPLE_BETTS_MILLER', #Use the simple Betts Miller convection scheme from Frierson + 'do_local_heating':True, + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + #Use a large mixed-layer depth, and the Albedo of the CTRL case in Jucker & Gerber, 2017 + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':True, + 'evaporation':True, + 'depth': 2.5, #Depth of mixed layer used + 'albedo_value': 0.31, #Albedo value used + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.7, + 'Tmin':160., + 'Tmax':350. + }, + + 'betts_miller_nml': { + 'rhbm': .7 , + 'do_simp': False, + 'do_shallower': True, + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.25, # neg. value: time in *days* + 'sponge_pbottom': 5000., #Bottom of the model's sponge down to 50hPa (units are Pa) + 'do_conserve_energy': True, + }, + + 'two_stream_gray_rad_nml': { + 'rad_scheme': 'frierson', #Select radiation scheme to use, which in this case is Frierson + 'do_seasonal': False, #do_seasonal=false uses the p2 insolation profile from Frierson 2006. do_seasonal=True uses the GFDL astronomy module to calculate seasonally-varying insolation. + 'atm_abs': 0.2, # default: 0.0 + }, + + # FMS Framework configuration + 'diag_manager_nml': { + 'mix_snapshot_average_fields': False # time avg fields are labelled with time in middle of window + }, + + 'fms_nml': { + 'domains_stack_size': 600000 # default: 0 + }, + + 'fms_io_nml': { + 'threading_write': 'single', # default: multi + 'fileset_write': 'single', # default: multi + }, + + 'spectral_dynamics_nml': { + 'damping_order': 4, + 'water_correction_limit': 200.e2, + 'reference_sea_level_press':1.0e5, + 'num_levels':25, #How many model pressure levels to use + 'valid_range_t':[100.,800.], + 'initial_sphum':[2.e-6], + 'vert_coord_option':'input', #Use the vertical levels from Frierson 2006 + 'surf_res':0.5, + 'scale_heights' : 11.0, + 'exponent':7.0, + 'robert_coeff':0.03 + }, + 'vert_coordinate_nml': { + 'bk': [0.000000, 0.0117665, 0.0196679, 0.0315244, 0.0485411, 0.0719344, 0.1027829, 0.1418581, 0.1894648, 0.2453219, 0.3085103, 0.3775033, 0.4502789, 0.5244989, 0.5977253, 0.6676441, 0.7322627, 0.7900587, 0.8400683, 0.8819111, 0.9157609, 0.9422770, 0.9625127, 0.9778177, 0.9897489, 1.0000000], + 'pk': [0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000], + }, + + 'hs_forcing_nml' :{ + 'local_heating_option':'from_file', + 'local_heating_file':'heating_rate' + }, +}) + +#Lets do a run! +if __name__=="__main__": + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,121): + exp.run(i, num_cores=NCORES) diff --git a/exp/frierson_dry_heating/input/heating_rate.nc b/exp/frierson_dry_heating/input/heating_rate.nc new file mode 100644 index 000000000..4489dfe36 Binary files /dev/null and b/exp/frierson_dry_heating/input/heating_rate.nc differ diff --git a/exp/grey_mars/grey_mars.py b/exp/grey_mars/grey_mars.py new file mode 100644 index 000000000..81a1b22ff --- /dev/null +++ b/exp/grey_mars/grey_mars.py @@ -0,0 +1,249 @@ +import numpy as np + +from isca import IscaCodeBase, GreyCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress +# from ntfy import notify + +NCORES = 16 + +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = GreyCodeBase.from_directory(GFDL_BASE) +# cb = GreyCodeBase.from_repo(repo='git@github.com:sit23/Isca.git', commit='7145be9') + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create a diagnostics output file for daily snapshots +diag = DiagTable() +#diag.add_file('atmos_monthly', 30, 'days', time_units='days') +diag.add_file('atmos_daily',88440 , 'seconds', time_units='days') +#diag.add_file('atmos_e_daily',1 , 'days', time_units='days') +#diag.add_file('atmos_timestep', 240, 'seconds', time_units='days') +diag.add_file('atmos_2_hourly', 7370 , 'seconds', time_units='days') + +# Define diag table entries +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk', time_avg=True) +diag.add_field('dynamics', 'pk', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'height', time_avg=True) +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('atmosphere', 'precipitation', time_avg=True) + +diag.add_field('atmosphere', 'dt_tg_convection', time_avg=True) + +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) + +diag.add_field('two_stream', 'mars_solar_long', time_avg=True) +diag.add_field('two_stream', 'coszen', time_avg=True) +diag.add_field('two_stream', 'rrsun', time_avg=True) +diag.add_field('two_stream', 'swdn_toa', time_avg=True) +diag.add_field('two_stream', 'time_since_ae', time_avg=True) +diag.add_field('two_stream', 'true_anomaly', time_avg=True) +diag.add_field('two_stream', 'dec', time_avg=True) +diag.add_field('two_stream', 'ang', time_avg=True) + +# define namelist values as python dictionary +namelist = Namelist({ + 'main_nml': { + 'dt_atmos': 110, + 'days': 0, + 'seconds': 30*88440, + 'calendar': 'no_calendar' + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':0., + 'two_stream_gray': True, #Use grey radiation + 'do_lscale_cond': False, + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'use_actual_surface_temperatures':False, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':True, + 'evaporation':False, + 'albedo_value': 0.3, + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.0, + 'tau_bm':3600., + }, + + 'dry_convection_nml': { + 'tau':7200, + }, + + 'betts_miller_nml': { + 'rhbm': .7 , + 'do_simp': False, + 'do_shallower': True, + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'tcmin': -223, #Make sure low temperature limit of saturation vapour pressure is low enough that it doesn't cause an error (note that this giant planet has no moisture anyway, so doesn't directly affect calculation. + 'tcmax': 350, + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.125, # neg. value: time in *days* + 'sponge_pbottom': 0.5, #Bottom of the model's sponge down to 50hPa (units are Pa) + 'do_conserve_energy': True, + }, + + 'spectral_dynamics_nml': { + 'num_levels': 25, + 'exponent': 2.5, + 'scale_heights': 4, + 'surf_res': 0.1, + 'robert_coeff': 4e-2, + 'do_water_correction': False, + 'vert_coord_option': 'input', + 'initial_sphum': 0., + 'valid_range_T': [0, 700] + }, + + 'vert_coordinate_nml': { + 'bk': [ 0.00000000e+00, 1.53008955e-04, 4.63790800e-04, 1.10977640e-03, 2.37044150e-03, 4.74479200e-03, 9.12245300e-03, 1.70677050e-02, 3.12516100e-02, 5.59939500e-02, 9.76165000e-02, 1.63754900e-01, 2.60315150e-01, 3.85974250e-01, 5.28084800e-01, 6.65956600e-01, 7.81088000e-01, 8.65400050e-01, 9.21109250e-01, 9.55343500e-01, 9.75416950e-01, 9.86856800e-01, 9.93269300e-01, 9.96830200e-01, 9.98799150e-01, 1.00000000e+00], + 'pk': [0.]*26, + }, + + 'two_stream_gray_rad_nml': { + 'rad_scheme': 'frierson', #Select radiation scheme to use, which in this case is Frierson + 'do_seasonal': True, + 'atm_abs': 0.2, + 'sw_diff':0.0, + 'ir_tau_eq':0.2, + 'ir_tau_pole':0.2, + 'linear_tau': 1.0, + 'equinox_day':0.0, + 'use_time_average_coszen':True, + 'solar_constant':589.0, + }, + + +# configure the relaxation profile +# 'hs_forcing_nml': { +# 'equilibrium_t_option' : 'top_down', +# 'ml_depth': 10., +# 'spinup_time': 108000, +# 'ka': -2., +# 'ks': -2., +# 'tau_s': 0.2, +# 'calculate_insolation_from_orbit' : True, +# 'do_rayleigh_damping':False, +# 'albedo':0.25, +# 'pure_rad_equil':True, +# 'stratosphere_t_option':'pure_rad_equil', +# 'peri_time': 0., +# 'h_a': 10.8, +# 'use_olr_from_t_surf':True, +# 'frac_of_year_ae':0.3032894472101727, +# }, + + 'astronomy_nml': { + 'ecc':0.0935, + 'obliq':25.19, + 'use_mean_anom_in_rrsun_calc':True, + 'use_old_r_inv_squared':False + }, + + 'constants_nml': { + 'orbital_period': 59166360, + 'solar_const':589.0, + 'radius':3396.0e3, + 'rdgas':192.0, + 'kappa':0.22727, + 'rotation_period':88308, + }, + +}) + +if __name__=="__main__": + + conv_schemes = ['none', 'dry'] + + depths = [0.1, 0.5, 1.0] + + pers = [70.85] + + scale = 1. + + for conv in conv_schemes: + for depth_val in depths: + for per_value in pers: + exp = Experiment('grey_mars_mk36_per_value'+str((per_value))+'_'+conv+'_mld_'+str(depth_val), codebase=cb) + exp.clear_rundir() + + exp.diag_table = diag + exp.namelist = namelist.copy() + exp.namelist['constants_nml']['grav'] = scale * 3.71 + exp.namelist['constants_nml']['pstd'] = scale * 6100.0 + exp.namelist['constants_nml']['pstd_mks'] = scale * 610.0 + exp.namelist['spectral_dynamics_nml']['reference_sea_level_press'] = scale * 610.0 + exp.namelist['idealized_moist_phys_nml']['convection_scheme'] = conv + exp.namelist['mixed_layer_nml']['depth'] = depth_val + exp.namelist['astronomy_nml']['per'] = per_value + + try: + # with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2, 65): + # with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(i, num_cores=NCORES) + # notify('top down with conv scheme = '+conv+' has completed', 'isca') + except: + pass \ No newline at end of file diff --git a/exp/grey_titan/grey_titan_mk1.py b/exp/grey_titan/grey_titan_mk1.py new file mode 100644 index 000000000..611d69d4c --- /dev/null +++ b/exp/grey_titan/grey_titan_mk1.py @@ -0,0 +1,246 @@ +import numpy as np + +from isca import IscaCodeBase, GreyCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress +from ntfy import notify + +NCORES = 16 + +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = GreyCodeBase.from_directory(GFDL_BASE) +# cb = GreyCodeBase.from_repo(repo='git@github.com:sit23/Isca.git', commit='7145be9') + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create a diagnostics output file for daily snapshots +diag = DiagTable() +# diag.add_file('atmos_monthly', 30, 'days', time_units='days') +diag.add_file('atmos_daily',1379678 , 'seconds', time_units='days') +#diag.add_file('atmos_e_daily',1 , 'days', time_units='days') +#diag.add_file('atmos_timestep', 240, 'seconds', time_units='days') + +# Define diag table entries +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk', time_avg=True) +diag.add_field('dynamics', 'pk', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'height', time_avg=True) +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('atmosphere', 'precipitation', time_avg=True) + +diag.add_field('atmosphere', 'dt_tg_convection', time_avg=True) + +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) + +diag.add_field('two_stream', 'mars_solar_long', time_avg=True) +diag.add_field('two_stream', 'coszen', time_avg=True) +diag.add_field('two_stream', 'rrsun', time_avg=True) +diag.add_field('two_stream', 'swdn_toa', time_avg=True) +diag.add_field('two_stream', 'time_since_ae', time_avg=True) +diag.add_field('two_stream', 'true_anomaly', time_avg=True) +diag.add_field('two_stream', 'dec', time_avg=True) +diag.add_field('two_stream', 'ang', time_avg=True) +diag.add_field('vert_turb', 'z_pbl', time_avg=True) + + +# define namelist values as python dictionary +namelist = Namelist({ + 'main_nml': { + 'dt_atmos': 92, + 'days': 0., + 'seconds': 2.*1379678., + 'calendar': 'no_calendar' + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':0., + 'two_stream_gray': True, #Use grey radiation + 'do_lscale_cond': False, + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'use_actual_surface_temperatures':False, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':True, + 'evaporation':False, + 'albedo_value': 0.3, + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.0, + 'tau_bm':3600., + }, + + 'dry_convection_nml': { + 'tau':7200, + }, + + 'betts_miller_nml': { + 'rhbm': .7 , + 'do_simp': False, + 'do_shallower': True, + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'tcmin': -223, #Make sure low temperature limit of saturation vapour pressure is low enough that it doesn't cause an error (note that this giant planet has no moisture anyway, so doesn't directly affect calculation. + 'tcmax': 350., + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.125, # neg. value: time in *days* + 'sponge_pbottom': 0.5, #Bottom of the model's sponge down to 50hPa (units are Pa) + 'do_conserve_energy': True, + }, + + 'spectral_dynamics_nml': { + 'num_levels': 25, + 'exponent': 2.5, + 'scale_heights': 4, + 'surf_res': 0.1, + 'robert_coeff': 4e-2, + 'do_water_correction': False, + 'vert_coord_option': 'input', + 'initial_sphum': 0., + 'valid_range_T': [0, 700] + }, + + 'vert_coordinate_nml': { + 'bk': [ 0.00000000e+00, 1.53008955e-04, 4.63790800e-04, 1.10977640e-03, 2.37044150e-03, 4.74479200e-03, 9.12245300e-03, 1.70677050e-02, 3.12516100e-02, 5.59939500e-02, 9.76165000e-02, 1.63754900e-01, 2.60315150e-01, 3.85974250e-01, 5.28084800e-01, 6.65956600e-01, 7.81088000e-01, 8.65400050e-01, 9.21109250e-01, 9.55343500e-01, 9.75416950e-01, 9.86856800e-01, 9.93269300e-01, 9.96830200e-01, 9.98799150e-01, 1.00000000e+00], + 'pk': [0.]*26, + }, + + 'two_stream_gray_rad_nml': { + 'rad_scheme': 'frierson', #Select radiation scheme to use, which in this case is Frierson + 'do_seasonal': True, + 'atm_abs': 5.0, + 'sw_diff':0.0, + 'ir_tau_eq':0.2, + 'ir_tau_pole':0.2, + 'linear_tau': 1.0, + 'equinox_day':0.0, + 'use_time_average_coszen':True, + 'solar_constant':589.0, + }, + + +# configure the relaxation profile +# 'hs_forcing_nml': { +# 'equilibrium_t_option' : 'top_down', +# 'ml_depth': 10., +# 'spinup_time': 108000, +# 'ka': -2., +# 'ks': -2., +# 'tau_s': 0.2, +# 'calculate_insolation_from_orbit' : True, +# 'do_rayleigh_damping':False, +# 'albedo':0.25, +# 'pure_rad_equil':True, +# 'stratosphere_t_option':'pure_rad_equil', +# 'peri_time': 0., +# 'h_a': 10.8, +# 'use_olr_from_t_surf':True, +# 'frac_of_year_ae':0.3032894472101727, +# }, + + 'astronomy_nml': { #Titan Values + 'ecc':0.0565, + 'obliq':26.7, + 'use_mean_anom_in_rrsun_calc':True, + 'use_old_r_inv_squared':False + }, + + 'constants_nml': { #Titan Values + 'orbital_period': 928523294., + 'solar_const':15.078, + 'radius':2575.0e3, + 'rdgas':188.7, + 'kappa':0.172, + 'rotation_period':1377631., + }, + +}) + +if __name__=="__main__": + + conv_schemes = ['none'] + + depths = [2.] + + pers = [339.392] + + scale = 1. + + for conv in conv_schemes: + for depth_val in depths: + for per_value in pers: + exp = Experiment('grey_titan_12_year_1_stephen_mod_2_high_solar_abs', codebase=cb) #'grey_mars_titan_solarconst_mk36_per_value'+str((per_value))+'_'+conv+'_mld_'+str(depth_val), codebase=cb) + exp.clear_rundir() + exp.diag_table = diag + exp.namelist = namelist.copy() + exp.namelist['constants_nml']['grav'] = scale * 1.352 + exp.namelist['constants_nml']['pstd'] = scale * 14670000.0 + exp.namelist['constants_nml']['pstd_mks'] = scale * 146700.0 + exp.namelist['spectral_dynamics_nml']['reference_sea_level_press'] = scale * 146700.0 + exp.namelist['idealized_moist_phys_nml']['convection_scheme'] = conv + exp.namelist['mixed_layer_nml']['depth'] = depth_val + exp.namelist['astronomy_nml']['per'] = per_value + +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2, 450): +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(i, num_cores=NCORES) + notify('top down with conv scheme = '+conv+' has completed', 'isca') diff --git a/exp/grey_titan/grey_titan_regan_final_version.py b/exp/grey_titan/grey_titan_regan_final_version.py new file mode 100644 index 000000000..b26967a44 --- /dev/null +++ b/exp/grey_titan/grey_titan_regan_final_version.py @@ -0,0 +1,261 @@ +#!!!This script is for T42 resolution + +import numpy as np + +from isca import IscaCodeBase, GreyCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress +from ntfy import notify + +NCORES = 16 #8 cores for T21, 16 for T42 +RESOLUTION = 'T42', 25 #T42 horizontal resolution, 25 levels in pressure + +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = GreyCodeBase.from_directory(GFDL_BASE) +# cb = GreyCodeBase.from_repo(repo='git@github.com:sit23/Isca.git', commit='7145be9') + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create a diagnostics output file for daily snapshots +diag = DiagTable() +#for atmos_monthly: 30 'days' in a 'month' multiplied by length of day in seconds +#diag.add_file('atmos_monthly', 30*1379678, 'seconds', time_units='days') +#for atmos_daily: length of 'day' in seconds +diag.add_file('atmos_daily', 1379678 , 'seconds', time_units='days') +#diag.add_file('atmos_e_daily',1 , 'days', time_units='days') +#diag.add_file('atmos_timestep', 240, 'seconds', time_units='days') + +# Define diag table entries +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk', time_avg=True) +diag.add_field('dynamics', 'pk', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'height', time_avg=True) +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('atmosphere', 'precipitation', time_avg=True) + +diag.add_field('atmosphere', 'dt_tg_convection', time_avg=True) + +#below checks the momentum tendency being applied by the sponge layer +#should be non-zero over the top 3 model levels +diag.add_field('damping', 'udt_rdamp', time_avg=True) + +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) + +diag.add_field('two_stream', 'mars_solar_long', time_avg=True) +diag.add_field('two_stream', 'coszen', time_avg=True) +diag.add_field('two_stream', 'rrsun', time_avg=True) +diag.add_field('two_stream', 'swdn_toa', time_avg=True) +diag.add_field('two_stream', 'time_since_ae', time_avg=True) +diag.add_field('two_stream', 'true_anomaly', time_avg=True) +diag.add_field('two_stream', 'dec', time_avg=True) +diag.add_field('two_stream', 'ang', time_avg=True) + +# define namelist values as python dictionary +namelist = Namelist({ + 'main_nml': { + 'dt_atmos': 178, #time step in seconds, multiple of length of day + 'days': 0., + 'seconds': 2.*1379678, #length of time in each .nc file + 'calendar': 'no_calendar' + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':0., + 'two_stream_gray': True, #if 'True' use grey radiation scheme + 'do_lscale_cond': False, + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'use_actual_surface_temperatures':False, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + 'mixed_layer_nml': { + 'tconst' : 120., + 'prescribe_initial_dist':True, + 'evaporation':False, + 'albedo_value': 0.3, + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.0, + 'tau_bm':3600., + }, + + 'dry_convection_nml': { + 'tau':7200, + }, + + 'betts_miller_nml': { + 'rhbm': .7 , + 'do_simp': False, + 'do_shallower': True, + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'tcmin': -223, #Make sure low temperature limit of saturation vapour pressure is low enough that it doesn't cause an error (note that this giant planet has no moisture anyway, so doesn't directly affect calculation. + 'tcmax': 350., + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.125, # neg. value: time in *days* + 'sponge_pbottom': 1.1205, #Bottom of the model's sponge down to 1.1205 for 3 model layers for Titan (units are Pa) + }, + + 'spectral_dynamics_nml': { + 'num_levels': 25, + 'exponent': 2.5, + 'scale_heights': 4, + 'surf_res': 0.1, + 'robert_coeff': 4e-2, + 'do_water_correction': False, + 'vert_coord_option': 'input', + 'initial_sphum': 0., + 'valid_range_T': [0, 700] + }, + + 'vert_coordinate_nml': { + 'bk': [ 0.00000000e+00, 1.53008955e-04, 4.63790800e-04, 1.10977640e-03, 2.37044150e-03, 4.74479200e-03, 9.12245300e-03, 1.70677050e-02, 3.12516100e-02, 5.59939500e-02, 9.76165000e-02, 1.63754900e-01, 2.60315150e-01, 3.85974250e-01, 5.28084800e-01, 6.65956600e-01, 7.81088000e-01, 8.65400050e-01, 9.21109250e-01, 9.55343500e-01, 9.75416950e-01, 9.86856800e-01, 9.93269300e-01, 9.96830200e-01, 9.98799150e-01, 1.00000000e+00], + 'pk': [0.]*26, + }, + + 'two_stream_gray_rad_nml': { + 'rad_scheme': 'frierson', #Select radiation scheme to use, which in this case is Frierson + 'do_seasonal': True, + 'atm_abs': 30, + 'sw_diff':0.0, + 'ir_tau_eq':25, + 'ir_tau_pole':25, + 'linear_tau': 1.0, + 'equinox_day':0.0, + 'use_time_average_coszen':True, + 'solar_constant':15.08, + 'solar_exponent':0.65 #!!!line additional to Mars script + }, + + +# configure the relaxation profile +# 'hs_forcing_nml': { +# 'equilibrium_t_option' : 'top_down', +# 'ml_depth': 10., +# 'spinup_time': 108000, +# 'ka': -2., +# 'ks': -2., +# 'tau_s': 0.2, +# 'calculate_insolation_from_orbit' : True, +# 'do_rayleigh_damping':False, +# 'albedo':0.25, +# 'pure_rad_equil':True, +# 'stratosphere_t_option':'pure_rad_equil', +# 'peri_time': 0., +# 'h_a': 10.8, +# 'use_olr_from_t_surf':True, +# 'frac_of_year_ae':0.3032894472101727, +# }, + + + 'spectral_init_cond_nml': { #namelist additional to the Mars script + 'initial_temperature': 200. + }, + + 'astronomy_nml': { + 'ecc':0.054, #eccentricity of Saturn's orbit around the Sun + 'obliq':26.7, #obliquity wrt the Sun + 'use_mean_anom_in_rrsun_calc':True, + 'use_old_r_inv_squared':False + }, + + 'constants_nml': { + 'orbital_period': 928523294, #value calculated from day/year calculator script + 'solar_const':15.08, + 'radius':2575.0e3, + 'rdgas':290, + 'kappa':0.2727, + 'rotation_period':1377631, #value calculated from day/year calculator script + }, + +}) + + +if __name__=="__main__": + + conv_schemes = ['none'] + + depths = [2.] + + pers = [93] #longitude of perihelion + + scale = 1. + + for conv in conv_schemes: + for depth_val in depths: + for per_value in pers: + exp = Experiment('grey_titan_T42_daily', codebase=cb) #name of folder in which .nc files are output + exp.clear_rundir() + + exp.diag_table = diag + exp.namelist = namelist.copy() + exp.namelist['constants_nml']['grav'] = scale * 1.354 #surface gravity + exp.namelist['constants_nml']['pstd'] = scale * 14670000.0 #surface pressure - 100 times bigger than below values + exp.namelist['constants_nml']['pstd_mks'] = scale * 146700.0 #in Pa + exp.namelist['spectral_dynamics_nml']['reference_sea_level_press'] = scale * 146700.0 #in Pa + exp.namelist['idealized_moist_phys_nml']['convection_scheme'] = conv + exp.namelist['mixed_layer_nml']['depth'] = depth_val + exp.namelist['astronomy_nml']['per'] = per_value + exp.set_resolution(*RESOLUTION) + +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2, 401): +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(i, num_cores=NCORES) + notify('top down with conv scheme = '+conv+' has completed', 'isca') diff --git a/exp/grey_titan/grey_titan_regan_final_version_s_mod.py b/exp/grey_titan/grey_titan_regan_final_version_s_mod.py new file mode 100644 index 000000000..dc190261f --- /dev/null +++ b/exp/grey_titan/grey_titan_regan_final_version_s_mod.py @@ -0,0 +1,264 @@ +#!!!This script is for T42 resolution + +import numpy as np + +from isca import IscaCodeBase, GreyCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress +from ntfy import notify + +NCORES = 16 #8 cores for T21, 16 for T42 +RESOLUTION = 'T42', 60 #T42 horizontal resolution, 25 levels in pressure + +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = GreyCodeBase.from_directory(GFDL_BASE) +# cb = GreyCodeBase.from_repo(repo='git@github.com:sit23/Isca.git', commit='7145be9') + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create a diagnostics output file for daily snapshots +diag = DiagTable() +#for atmos_monthly: 30 'days' in a 'month' multiplied by length of day in seconds +#diag.add_file('atmos_monthly', 30*1379678, 'seconds', time_units='days') +#for atmos_daily: length of 'day' in seconds +diag.add_file('atmos_daily', 1379678 , 'seconds', time_units='days') +#diag.add_file('atmos_e_daily',1 , 'days', time_units='days') +#diag.add_file('atmos_timestep', 240, 'seconds', time_units='days') + +# Define diag table entries +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk', time_avg=True) +diag.add_field('dynamics', 'pk', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'height', time_avg=True) +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('atmosphere', 'precipitation', time_avg=True) + +diag.add_field('atmosphere', 'dt_tg_convection', time_avg=True) + +#below checks the momentum tendency being applied by the sponge layer +#should be non-zero over the top 3 model levels +diag.add_field('damping', 'udt_rdamp', time_avg=True) + +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) + +diag.add_field('two_stream', 'mars_solar_long', time_avg=True) +diag.add_field('two_stream', 'coszen', time_avg=True) +diag.add_field('two_stream', 'rrsun', time_avg=True) +diag.add_field('two_stream', 'swdn_toa', time_avg=True) +diag.add_field('two_stream', 'time_since_ae', time_avg=True) +diag.add_field('two_stream', 'true_anomaly', time_avg=True) +diag.add_field('two_stream', 'dec', time_avg=True) +diag.add_field('two_stream', 'ang', time_avg=True) +diag.add_field('two_stream', 'flux_sw', time_avg=True) +diag.add_field('two_stream', 'flux_lw', time_avg=True) +diag.add_field('two_stream', 'tau_sw', time_avg=True) +diag.add_field('two_stream', 'tau_lw', time_avg=True) + +# define namelist values as python dictionary +namelist = Namelist({ + 'main_nml': { + 'dt_atmos': 178, #time step in seconds, multiple of length of day + 'days': 0., + 'seconds': 2.*1379678, #length of time in each .nc file + 'calendar': 'no_calendar' + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':0., + 'two_stream_gray': True, #if 'True' use grey radiation scheme + 'do_lscale_cond': False, + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'use_actual_surface_temperatures':False, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + 'mixed_layer_nml': { + 'tconst' : 90., + 'prescribe_initial_dist':True, + 'evaporation':False, + 'albedo_value': 0.3, + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.0, + 'tau_bm':3600., + }, + + 'dry_convection_nml': { + 'tau':7200, + }, + + 'betts_miller_nml': { + 'rhbm': .7 , + 'do_simp': False, + 'do_shallower': True, + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'tcmin': -223, #Make sure low temperature limit of saturation vapour pressure is low enough that it doesn't cause an error (note that this giant planet has no moisture anyway, so doesn't directly affect calculation. + 'tcmax': 350., + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.125, # neg. value: time in *days* + 'sponge_pbottom': 1.1205, #Bottom of the model's sponge down to 1.1205 for 3 model layers for Titan (units are Pa) + }, + + 'spectral_dynamics_nml': { + 'num_levels':60 , + 'exponent': 7.0, + 'scale_heights': 11, + 'surf_res': 0.5, + 'robert_coeff': 4e-2, + 'do_water_correction': False, + 'vert_coord_option': 'uneven_sigma', + 'initial_sphum': 0., + 'valid_range_T': [0, 700] + }, + + 'two_stream_gray_rad_nml': { + 'rad_scheme': 'frierson', #Select radiation scheme to use, which in this case is Frierson + 'do_seasonal': True, + 'atm_abs': 5, + 'sw_diff':0.0, + 'ir_tau_eq':10, + 'ir_tau_pole':10, + 'linear_tau': 1.0, + 'equinox_day':0.0, + 'use_time_average_coszen':True, + 'solar_constant':15.08, + 'solar_exponent':0.65 #!!!line additional to Mars script + }, + + +# configure the relaxation profile +# 'hs_forcing_nml': { +# 'equilibrium_t_option' : 'top_down', +# 'ml_depth': 10., +# 'spinup_time': 108000, +# 'ka': -2., +# 'ks': -2., +# 'tau_s': 0.2, +# 'calculate_insolation_from_orbit' : True, +# 'do_rayleigh_damping':False, +# 'albedo':0.25, +# 'pure_rad_equil':True, +# 'stratosphere_t_option':'pure_rad_equil', +# 'peri_time': 0., +# 'h_a': 10.8, +# 'use_olr_from_t_surf':True, +# 'frac_of_year_ae':0.3032894472101727, +# }, + + + 'spectral_init_cond_nml': { #namelist additional to the Mars script + 'initial_temperature': 90. + }, + + 'astronomy_nml': { + 'ecc':0.054, #eccentricity of Saturn's orbit around the Sun + 'obliq':26.7, #obliquity wrt the Sun + 'use_mean_anom_in_rrsun_calc':True, + 'use_old_r_inv_squared':False + }, + + 'constants_nml': { + 'orbital_period': 928523294, #value calculated from day/year calculator script + 'solar_const':15.08, + 'radius':2575.0e3, + 'rdgas':290, + 'kappa':0.2727, + 'rotation_period':1377631, #value calculated from day/year calculator script + }, + + 'fms_nml': { + 'domains_stack_size': 620000 #Setting size of stack available to model, which needs to be higher than the default when running at high spatial resolution + }, + +}) + + +if __name__=="__main__": + + conv_schemes = ['none'] + + depths = [2.] + + pers = [93] #longitude of perihelion + + scale = 1. + + for conv in conv_schemes: + for depth_val in depths: + for per_value in pers: + exp = Experiment('grey_titan_T42_60_levels_tapio_optical_dep', codebase=cb) #name of folder in which .nc files are output + exp.clear_rundir() + + exp.diag_table = diag + exp.namelist = namelist.copy() + exp.namelist['constants_nml']['grav'] = scale * 1.354 #surface gravity + exp.namelist['constants_nml']['pstd'] = scale * 14670000.0 #surface pressure - 100 times bigger than below values + exp.namelist['constants_nml']['pstd_mks'] = scale * 146700.0 #in Pa + exp.namelist['spectral_dynamics_nml']['reference_sea_level_press'] = scale * 146700.0 #in Pa + exp.namelist['idealized_moist_phys_nml']['convection_scheme'] = conv + exp.namelist['mixed_layer_nml']['depth'] = depth_val + exp.namelist['astronomy_nml']['per'] = per_value + exp.set_resolution(*RESOLUTION) + +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2, 400): +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(i, num_cores=NCORES) + notify('top down with conv scheme = '+conv+' has completed', 'isca') diff --git a/exp/grey_titan/grey_titan_schneider_rad.py b/exp/grey_titan/grey_titan_schneider_rad.py new file mode 100644 index 000000000..e5191c286 --- /dev/null +++ b/exp/grey_titan/grey_titan_schneider_rad.py @@ -0,0 +1,272 @@ +#!!!This script is for T42 resolution + +import numpy as np + +from isca import IscaCodeBase, GreyCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress +from ntfy import notify + +NCORES = 8 #8 cores for T21, 16 for T42 +RESOLUTION = 'T21', 60 #T42 horizontal resolution, 25 levels in pressure + +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = GreyCodeBase.from_directory(GFDL_BASE) +# cb = GreyCodeBase.from_repo(repo='git@github.com:sit23/Isca.git', commit='7145be9') + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create a diagnostics output file for daily snapshots +diag = DiagTable() +#for atmos_monthly: 30 'days' in a 'month' multiplied by length of day in seconds +#diag.add_file('atmos_monthly', 30*1379678, 'seconds', time_units='days') +#for atmos_daily: length of 'day' in seconds +diag.add_file('atmos_daily', 1379678 , 'seconds', time_units='days') +#diag.add_file('atmos_e_daily',1 , 'days', time_units='days') +#diag.add_file('atmos_timestep', 240, 'seconds', time_units='days') + +# Define diag table entries +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk', time_avg=True) +diag.add_field('dynamics', 'pk', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'height', time_avg=True) +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('atmosphere', 'precipitation', time_avg=True) + +diag.add_field('atmosphere', 'dt_tg_convection', time_avg=True) + +#below checks the momentum tendency being applied by the sponge layer +#should be non-zero over the top 3 model levels +diag.add_field('damping', 'udt_rdamp', time_avg=True) + +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) + +diag.add_field('two_stream', 'mars_solar_long', time_avg=True) +diag.add_field('two_stream', 'coszen', time_avg=True) +diag.add_field('two_stream', 'rrsun', time_avg=True) +diag.add_field('two_stream', 'swdn_toa', time_avg=True) +diag.add_field('two_stream', 'time_since_ae', time_avg=True) +diag.add_field('two_stream', 'true_anomaly', time_avg=True) +diag.add_field('two_stream', 'dec', time_avg=True) +diag.add_field('two_stream', 'ang', time_avg=True) +diag.add_field('two_stream', 'tdt_rad', time_avg=True) +diag.add_field('two_stream', 'tdt_solar', time_avg=True) +diag.add_field('two_stream', 'flux_sw', time_avg=True) +diag.add_field('two_stream', 'flux_lw', time_avg=True) +diag.add_field('two_stream', 'tau_sw', time_avg=True) +diag.add_field('two_stream', 'tau_lw', time_avg=True) + +# define namelist values as python dictionary +namelist = Namelist({ + 'main_nml': { + 'dt_atmos': 2.*178, #time step in seconds, multiple of length of day + 'days': 0., + 'seconds': 2.*1379678, #length of time in each .nc file + 'calendar': 'no_calendar' + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':0., + 'two_stream_gray': True, #if 'True' use grey radiation scheme + 'do_lscale_cond': False, + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'use_actual_surface_temperatures':False, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + 'mixed_layer_nml': { + 'tconst' : 90., + 'prescribe_initial_dist':True, + 'evaporation':False, + 'albedo_value': 0.3, + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.0, + 'tau_bm':3600., + }, + + 'dry_convection_nml': { + 'tau':7200, + }, + + 'betts_miller_nml': { + 'rhbm': .7 , + 'do_simp': False, + 'do_shallower': True, + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'tcmin': -223, #Make sure low temperature limit of saturation vapour pressure is low enough that it doesn't cause an error (note that this giant planet has no moisture anyway, so doesn't directly affect calculation. + 'tcmax': 350., + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.125, # neg. value: time in *days* + 'sponge_pbottom': 112.05, #Bottom of the model's sponge down to 1.1205 for 3 model layers for Titan (units are Pa) + }, + + 'spectral_dynamics_nml': { + 'exponent': 2, + 'scale_heights': 4, + 'surf_res': 0.2, + 'robert_coeff': 4e-2, + 'do_water_correction': False, + 'vert_coord_option': 'uneven_sigma', + 'initial_sphum': 0., + 'valid_range_T': [0, 700], + 'make_symmetric':True, + }, + + 'diffusivity_nml': { + 'free_atm_diff':True,#Turn on vertical diffusion in the free atmosphere + }, + + 'vert_coordinate_nml': { + 'bk': [ 0.00000000e+00, 1.53008955e-04, 4.63790800e-04, 1.10977640e-03, 2.37044150e-03, 4.74479200e-03, 9.12245300e-03, 1.70677050e-02, 3.12516100e-02, 5.59939500e-02, 9.76165000e-02, 1.63754900e-01, 2.60315150e-01, 3.85974250e-01, 5.28084800e-01, 6.65956600e-01, 7.81088000e-01, 8.65400050e-01, 9.21109250e-01, 9.55343500e-01, 9.75416950e-01, 9.86856800e-01, 9.93269300e-01, 9.96830200e-01, 9.98799150e-01, 1.00000000e+00], + 'pk': [0.]*26, + }, + + 'two_stream_gray_rad_nml': { + 'rad_scheme': 'schneider_titan', #Select radiation scheme to use, which in this case is Frierson + 'do_seasonal': True, + 'atm_abs':5., + 'sw_diff':0.0, + 'solar_exponent':0.42, + 'ir_tau_eq':10, + 'ir_tau_pole':10, + 'linear_tau': 0.15, + 'wv_exponent':2., + 'equinox_day':0.0, + 'use_time_average_coszen':True, + 'solar_constant':15.08, + }, + + +# configure the relaxation profile +# 'hs_forcing_nml': { +# 'equilibrium_t_option' : 'top_down', +# 'ml_depth': 10., +# 'spinup_time': 108000, +# 'ka': -2., +# 'ks': -2., +# 'tau_s': 0.2, +# 'calculate_insolation_from_orbit' : True, +# 'do_rayleigh_damping':False, +# 'albedo':0.25, +# 'pure_rad_equil':True, +# 'stratosphere_t_option':'pure_rad_equil', +# 'peri_time': 0., +# 'h_a': 10.8, +# 'use_olr_from_t_surf':True, +# 'frac_of_year_ae':0.3032894472101727, +# }, + + + 'spectral_init_cond_nml': { #namelist additional to the Mars script + 'initial_temperature': 90. + }, + + 'astronomy_nml': { + 'ecc':0.054, #eccentricity of Saturn's orbit around the Sun + 'obliq':26.7, #obliquity wrt the Sun + 'use_mean_anom_in_rrsun_calc':True, + 'use_old_r_inv_squared':False + }, + + 'constants_nml': { + 'orbital_period': 928523294, #value calculated from day/year calculator script + 'solar_const':15.08, + 'radius':2575.0e3, + 'rdgas':290, + 'kappa':0.2727, + 'rotation_period':1377631, #value calculated from day/year calculator script + }, + +}) + + +if __name__=="__main__": + + conv_schemes = ['none'] + + depths = [2.] + + pers = [93] #longitude of perihelion + + scale = 1. + + for conv in conv_schemes: + for depth_val in depths: + for per_value in pers: + exp = Experiment('grey_titan_T21_daily_tapio_rad_with_sponge_mk12_tau_diag_60_levels_axisym', codebase=cb) #name of folder in which .nc files are output + exp.clear_rundir() + + exp.diag_table = diag + exp.namelist = namelist.copy() + exp.namelist['constants_nml']['grav'] = scale * 1.354 #surface gravity + exp.namelist['constants_nml']['pstd'] = scale * 14670000.0 #surface pressure - 100 times bigger than below values + exp.namelist['constants_nml']['pstd_mks'] = scale * 146700.0 #in Pa + exp.namelist['spectral_dynamics_nml']['reference_sea_level_press'] = scale * 146700.0 #in Pa + exp.namelist['idealized_moist_phys_nml']['convection_scheme'] = conv + exp.namelist['mixed_layer_nml']['depth'] = depth_val + exp.namelist['astronomy_nml']['per'] = per_value + exp.set_resolution(*RESOLUTION) + +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2, 12001): +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(i, num_cores=NCORES) + notify('top down with conv scheme = '+conv+' has completed', 'isca') diff --git a/exp/grey_titan/grey_titan_schneider_rad_60_levels.py b/exp/grey_titan/grey_titan_schneider_rad_60_levels.py new file mode 100644 index 000000000..c74e4bd40 --- /dev/null +++ b/exp/grey_titan/grey_titan_schneider_rad_60_levels.py @@ -0,0 +1,271 @@ +#!!!This script is for T42 resolution + +import numpy as np + +from isca import IscaCodeBase, GreyCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress +from ntfy import notify + +NCORES = 16 #8 cores for T21, 16 for T42 +RESOLUTION = 'T42', 60 #T42 horizontal resolution, 25 levels in pressure + +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = GreyCodeBase.from_directory(GFDL_BASE) +# cb = GreyCodeBase.from_repo(repo='git@github.com:sit23/Isca.git', commit='7145be9') + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create a diagnostics output file for daily snapshots +diag = DiagTable() +#for atmos_monthly: 30 'days' in a 'month' multiplied by length of day in seconds +#diag.add_file('atmos_monthly', 30*1379678, 'seconds', time_units='days') +#for atmos_daily: length of 'day' in seconds +diag.add_file('atmos_daily', 1379678 , 'seconds', time_units='days') +#diag.add_file('atmos_e_daily',1 , 'days', time_units='days') +#diag.add_file('atmos_timestep', 240, 'seconds', time_units='days') + +# Define diag table entries +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk', time_avg=True) +diag.add_field('dynamics', 'pk', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'height', time_avg=True) +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('atmosphere', 'precipitation', time_avg=True) + +diag.add_field('atmosphere', 'dt_tg_convection', time_avg=True) + +#below checks the momentum tendency being applied by the sponge layer +#should be non-zero over the top 3 model levels +diag.add_field('damping', 'udt_rdamp', time_avg=True) + +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) + +diag.add_field('two_stream', 'mars_solar_long', time_avg=True) +diag.add_field('two_stream', 'coszen', time_avg=True) +diag.add_field('two_stream', 'rrsun', time_avg=True) +diag.add_field('two_stream', 'swdn_toa', time_avg=True) +diag.add_field('two_stream', 'time_since_ae', time_avg=True) +diag.add_field('two_stream', 'true_anomaly', time_avg=True) +diag.add_field('two_stream', 'dec', time_avg=True) +diag.add_field('two_stream', 'ang', time_avg=True) +diag.add_field('two_stream', 'tdt_rad', time_avg=True) +diag.add_field('two_stream', 'tdt_solar', time_avg=True) +diag.add_field('two_stream', 'flux_sw', time_avg=True) +diag.add_field('two_stream', 'flux_lw', time_avg=True) +diag.add_field('two_stream', 'tau_sw', time_avg=True) +diag.add_field('two_stream', 'tau_lw', time_avg=True) + +# define namelist values as python dictionary +namelist = Namelist({ + 'main_nml': { + 'dt_atmos': 178, #time step in seconds, multiple of length of day + 'days': 0., + 'seconds': 2.*1379678, #length of time in each .nc file + 'calendar': 'no_calendar' + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':0., + 'two_stream_gray': True, #if 'True' use grey radiation scheme + 'do_lscale_cond': False, + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'use_actual_surface_temperatures':False, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + 'mixed_layer_nml': { + 'tconst' : 90., + 'prescribe_initial_dist':True, + 'evaporation':False, + 'albedo_value': 0.3, + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.0, + 'tau_bm':3600., + }, + + 'dry_convection_nml': { + 'tau':7200, + }, + + 'betts_miller_nml': { + 'rhbm': .7 , + 'do_simp': False, + 'do_shallower': True, + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'tcmin': -223, #Make sure low temperature limit of saturation vapour pressure is low enough that it doesn't cause an error (note that this giant planet has no moisture anyway, so doesn't directly affect calculation. + 'tcmax': 350., + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.125, # neg. value: time in *days* + 'sponge_pbottom': 112.05, #Bottom of the model's sponge down to 1.1205 for 3 model layers for Titan (units are Pa) + }, + + 'spectral_dynamics_nml': { + 'exponent': 2, + 'scale_heights': 4, + 'surf_res': 0.2, + 'robert_coeff': 4e-2, + 'do_water_correction': False, + 'vert_coord_option': 'uneven_sigma', + 'initial_sphum': 0., + 'valid_range_T': [0, 700] + }, + + 'vert_coordinate_nml': { + 'bk': [ 0.00000000e+00, 1.53008955e-04, 4.63790800e-04, 1.10977640e-03, 2.37044150e-03, 4.74479200e-03, 9.12245300e-03, 1.70677050e-02, 3.12516100e-02, 5.59939500e-02, 9.76165000e-02, 1.63754900e-01, 2.60315150e-01, 3.85974250e-01, 5.28084800e-01, 6.65956600e-01, 7.81088000e-01, 8.65400050e-01, 9.21109250e-01, 9.55343500e-01, 9.75416950e-01, 9.86856800e-01, 9.93269300e-01, 9.96830200e-01, 9.98799150e-01, 1.00000000e+00], + 'pk': [0.]*26, + }, + + 'two_stream_gray_rad_nml': { + 'rad_scheme': 'schneider_titan', #Select radiation scheme to use, which in this case is Frierson + 'do_seasonal': True, + 'atm_abs':5., + 'sw_diff':0.0, + 'solar_exponent':0.21, + 'ir_tau_eq':10, + 'ir_tau_pole':10, + 'linear_tau': 0.15, + 'wv_exponent':2., + 'equinox_day':0.0, + 'use_time_average_coszen':True, + 'solar_constant':15.08, + }, + + +# configure the relaxation profile +# 'hs_forcing_nml': { +# 'equilibrium_t_option' : 'top_down', +# 'ml_depth': 10., +# 'spinup_time': 108000, +# 'ka': -2., +# 'ks': -2., +# 'tau_s': 0.2, +# 'calculate_insolation_from_orbit' : True, +# 'do_rayleigh_damping':False, +# 'albedo':0.25, +# 'pure_rad_equil':True, +# 'stratosphere_t_option':'pure_rad_equil', +# 'peri_time': 0., +# 'h_a': 10.8, +# 'use_olr_from_t_surf':True, +# 'frac_of_year_ae':0.3032894472101727, +# }, + + + 'spectral_init_cond_nml': { #namelist additional to the Mars script + 'initial_temperature': 90. + }, + + 'astronomy_nml': { + 'ecc':0.054, #eccentricity of Saturn's orbit around the Sun + 'obliq':26.7, #obliquity wrt the Sun + 'use_mean_anom_in_rrsun_calc':True, + 'use_old_r_inv_squared':False + }, + + 'constants_nml': { + 'orbital_period': 928523294, #value calculated from day/year calculator script + 'solar_const':15.08, + 'radius':2575.0e3, + 'rdgas':290, + 'kappa':0.2727, + 'rotation_period':1377631, #value calculated from day/year calculator script + }, + + 'fms_nml': { + 'domains_stack_size': 61440 #Setting size of stack available to model, which needs to be higher than the default when running at high spatial resolution + }, + +}) + + +if __name__=="__main__": + + conv_schemes = ['none'] + + depths = [2.] + + pers = [93] #longitude of perihelion + + scale = 1. + + for conv in conv_schemes: + for depth_val in depths: + for per_value in pers: + exp = Experiment('grey_titan_T42_daily_tapio_rad_with_sponge_mk10_tau_diag_60_levels', codebase=cb) #name of folder in which .nc files are output + exp.clear_rundir() + + exp.diag_table = diag + exp.namelist = namelist.copy() + exp.namelist['constants_nml']['grav'] = scale * 1.354 #surface gravity + exp.namelist['constants_nml']['pstd'] = scale * 14670000.0 #surface pressure - 100 times bigger than below values + exp.namelist['constants_nml']['pstd_mks'] = scale * 146700.0 #in Pa + exp.namelist['spectral_dynamics_nml']['reference_sea_level_press'] = scale * 146700.0 #in Pa + exp.namelist['idealized_moist_phys_nml']['convection_scheme'] = conv + exp.namelist['mixed_layer_nml']['depth'] = depth_val + exp.namelist['astronomy_nml']['per'] = per_value + exp.set_resolution(*RESOLUTION) + +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2, 401): +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(i, num_cores=NCORES) + notify('top down with conv scheme = '+conv+' has completed', 'isca') diff --git a/exp/grey_titan/grey_titan_schneider_rad_axisym_forcing_and_dynamics.py b/exp/grey_titan/grey_titan_schneider_rad_axisym_forcing_and_dynamics.py new file mode 100644 index 000000000..29cea7914 --- /dev/null +++ b/exp/grey_titan/grey_titan_schneider_rad_axisym_forcing_and_dynamics.py @@ -0,0 +1,273 @@ +#!!!This script is for T42 resolution + +import numpy as np + +from isca import IscaCodeBase, GreyCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress +from ntfy import notify + +NCORES = 8 #8 cores for T21, 16 for T42 +RESOLUTION = 'T21', 60 #T42 horizontal resolution, 25 levels in pressure + +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = GreyCodeBase.from_directory(GFDL_BASE) +# cb = GreyCodeBase.from_repo(repo='git@github.com:sit23/Isca.git', commit='7145be9') + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create a diagnostics output file for daily snapshots +diag = DiagTable() +#for atmos_monthly: 30 'days' in a 'month' multiplied by length of day in seconds +#diag.add_file('atmos_monthly', 30*1379678, 'seconds', time_units='days') +#for atmos_daily: length of 'day' in seconds +diag.add_file('atmos_daily', 1379678 , 'seconds', time_units='days') +#diag.add_file('atmos_e_daily',1 , 'days', time_units='days') +#diag.add_file('atmos_timestep', 240, 'seconds', time_units='days') + +# Define diag table entries +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk', time_avg=True) +diag.add_field('dynamics', 'pk', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'height', time_avg=True) +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('atmosphere', 'precipitation', time_avg=True) + +diag.add_field('atmosphere', 'dt_tg_convection', time_avg=True) + +#below checks the momentum tendency being applied by the sponge layer +#should be non-zero over the top 3 model levels +diag.add_field('damping', 'udt_rdamp', time_avg=True) + +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) + +diag.add_field('two_stream', 'mars_solar_long', time_avg=True) +diag.add_field('two_stream', 'coszen', time_avg=True) +diag.add_field('two_stream', 'rrsun', time_avg=True) +diag.add_field('two_stream', 'swdn_toa', time_avg=True) +diag.add_field('two_stream', 'time_since_ae', time_avg=True) +diag.add_field('two_stream', 'true_anomaly', time_avg=True) +diag.add_field('two_stream', 'dec', time_avg=True) +diag.add_field('two_stream', 'ang', time_avg=True) +diag.add_field('two_stream', 'tdt_rad', time_avg=True) +diag.add_field('two_stream', 'tdt_solar', time_avg=True) +diag.add_field('two_stream', 'flux_sw', time_avg=True) +diag.add_field('two_stream', 'flux_lw', time_avg=True) +diag.add_field('two_stream', 'tau_sw', time_avg=True) +diag.add_field('two_stream', 'tau_lw', time_avg=True) + +# define namelist values as python dictionary +namelist = Namelist({ + 'main_nml': { + 'dt_atmos': 2.*178, #time step in seconds, multiple of length of day + 'days': 0., + 'seconds': 2.*1379678, #length of time in each .nc file + 'calendar': 'no_calendar' + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':0., + 'two_stream_gray': True, #if 'True' use grey radiation scheme + 'do_lscale_cond': False, + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'use_actual_surface_temperatures':False, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + 'mixed_layer_nml': { + 'tconst' : 90., + 'prescribe_initial_dist':True, + 'evaporation':False, + 'albedo_value': 0.3, + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.0, + 'tau_bm':3600., + }, + + 'dry_convection_nml': { + 'tau':7200, + }, + + 'betts_miller_nml': { + 'rhbm': .7 , + 'do_simp': False, + 'do_shallower': True, + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'tcmin': -223, #Make sure low temperature limit of saturation vapour pressure is low enough that it doesn't cause an error (note that this giant planet has no moisture anyway, so doesn't directly affect calculation. + 'tcmax': 350., + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.125, # neg. value: time in *days* + 'sponge_pbottom': 112.05, #Bottom of the model's sponge down to 1.1205 for 3 model layers for Titan (units are Pa) + }, + + 'spectral_dynamics_nml': { + 'exponent': 2, + 'scale_heights': 4, + 'surf_res': 0.2, + 'robert_coeff': 4e-2, + 'do_water_correction': False, + 'vert_coord_option': 'uneven_sigma', + 'initial_sphum': 0., + 'valid_range_T': [0, 700], + 'make_symmetric':True, + }, + + 'diffusivity_nml': { + 'free_atm_diff':True,#Turn on vertical diffusion in the free atmosphere + }, + + 'vert_coordinate_nml': { + 'bk': [ 0.00000000e+00, 1.53008955e-04, 4.63790800e-04, 1.10977640e-03, 2.37044150e-03, 4.74479200e-03, 9.12245300e-03, 1.70677050e-02, 3.12516100e-02, 5.59939500e-02, 9.76165000e-02, 1.63754900e-01, 2.60315150e-01, 3.85974250e-01, 5.28084800e-01, 6.65956600e-01, 7.81088000e-01, 8.65400050e-01, 9.21109250e-01, 9.55343500e-01, 9.75416950e-01, 9.86856800e-01, 9.93269300e-01, 9.96830200e-01, 9.98799150e-01, 1.00000000e+00], + 'pk': [0.]*26, + }, + + 'two_stream_gray_rad_nml': { + 'rad_scheme': 'schneider_titan', #Select radiation scheme to use, which in this case is Frierson + 'do_seasonal': True, + 'atm_abs':5., + 'sw_diff':0.0, + 'solar_exponent':0.42, + 'ir_tau_eq':10, + 'ir_tau_pole':10, + 'linear_tau': 0.15, + 'wv_exponent':2., + 'equinox_day':0.0, + 'use_time_average_coszen':True, + 'solar_constant':15.08, + 'dt_rad_avg': 1379678, + }, + + +# configure the relaxation profile +# 'hs_forcing_nml': { +# 'equilibrium_t_option' : 'top_down', +# 'ml_depth': 10., +# 'spinup_time': 108000, +# 'ka': -2., +# 'ks': -2., +# 'tau_s': 0.2, +# 'calculate_insolation_from_orbit' : True, +# 'do_rayleigh_damping':False, +# 'albedo':0.25, +# 'pure_rad_equil':True, +# 'stratosphere_t_option':'pure_rad_equil', +# 'peri_time': 0., +# 'h_a': 10.8, +# 'use_olr_from_t_surf':True, +# 'frac_of_year_ae':0.3032894472101727, +# }, + + + 'spectral_init_cond_nml': { #namelist additional to the Mars script + 'initial_temperature': 90. + }, + + 'astronomy_nml': { + 'ecc':0.054, #eccentricity of Saturn's orbit around the Sun + 'obliq':26.7, #obliquity wrt the Sun + 'use_mean_anom_in_rrsun_calc':True, + 'use_old_r_inv_squared':False + }, + + 'constants_nml': { + 'orbital_period': 928523294, #value calculated from day/year calculator script + 'solar_const':15.08, + 'radius':2575.0e3, + 'rdgas':290, + 'kappa':0.2727, + 'rotation_period':1377631, #value calculated from day/year calculator script + }, + +}) + + +if __name__=="__main__": + + conv_schemes = ['none'] + + depths = [2.] + + pers = [93] #longitude of perihelion + + scale = 1. + + for conv in conv_schemes: + for depth_val in depths: + for per_value in pers: + exp = Experiment('grey_titan_T21_daily_tapio_rad_with_sponge_mk12_tau_diag_60_levels_axisym_forcing_and_dynamics', codebase=cb) #name of folder in which .nc files are output + exp.clear_rundir() + + exp.diag_table = diag + exp.namelist = namelist.copy() + exp.namelist['constants_nml']['grav'] = scale * 1.354 #surface gravity + exp.namelist['constants_nml']['pstd'] = scale * 14670000.0 #surface pressure - 100 times bigger than below values + exp.namelist['constants_nml']['pstd_mks'] = scale * 146700.0 #in Pa + exp.namelist['spectral_dynamics_nml']['reference_sea_level_press'] = scale * 146700.0 #in Pa + exp.namelist['idealized_moist_phys_nml']['convection_scheme'] = conv + exp.namelist['mixed_layer_nml']['depth'] = depth_val + exp.namelist['astronomy_nml']['per'] = per_value + exp.set_resolution(*RESOLUTION) + +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2, 12001): +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(i, num_cores=NCORES) + notify('top down with conv scheme = '+conv+' has completed', 'isca') diff --git a/exp/grey_titan/grey_titan_schneider_rad_dry_conv.py b/exp/grey_titan/grey_titan_schneider_rad_dry_conv.py new file mode 100644 index 000000000..a4cad1d59 --- /dev/null +++ b/exp/grey_titan/grey_titan_schneider_rad_dry_conv.py @@ -0,0 +1,277 @@ +#!!!This script is for T42 resolution + +import numpy as np + +from isca import IscaCodeBase, GreyCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress +from ntfy import notify + +NCORES = 8 #8 cores for T21, 16 for T42 +RESOLUTION = 'T21', 60 #T42 horizontal resolution, 25 levels in pressure + +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = GreyCodeBase.from_directory(GFDL_BASE) +# cb = GreyCodeBase.from_repo(repo='git@github.com:sit23/Isca.git', commit='7145be9') + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create a diagnostics output file for daily snapshots +diag = DiagTable() +#for atmos_monthly: 30 'days' in a 'month' multiplied by length of day in seconds +#diag.add_file('atmos_monthly', 30*1379678, 'seconds', time_units='days') +#for atmos_daily: length of 'day' in seconds +diag.add_file('atmos_daily', 1379678 , 'seconds', time_units='days') +#diag.add_file('atmos_e_daily',1 , 'days', time_units='days') +#diag.add_file('atmos_timestep', 240, 'seconds', time_units='days') + +# Define diag table entries +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk', time_avg=True) +diag.add_field('dynamics', 'pk', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'height', time_avg=True) +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('atmosphere', 'precipitation', time_avg=True) + +diag.add_field('atmosphere', 'dt_tg_convection', time_avg=True) + +#below checks the momentum tendency being applied by the sponge layer +#should be non-zero over the top 3 model levels +diag.add_field('damping', 'udt_rdamp', time_avg=True) + +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) + +diag.add_field('two_stream', 'mars_solar_long', time_avg=True) +diag.add_field('two_stream', 'coszen', time_avg=True) +diag.add_field('two_stream', 'rrsun', time_avg=True) +diag.add_field('two_stream', 'swdn_toa', time_avg=True) +diag.add_field('two_stream', 'time_since_ae', time_avg=True) +diag.add_field('two_stream', 'true_anomaly', time_avg=True) +diag.add_field('two_stream', 'dec', time_avg=True) +diag.add_field('two_stream', 'ang', time_avg=True) +diag.add_field('two_stream', 'tdt_rad', time_avg=True) +diag.add_field('two_stream', 'tdt_solar', time_avg=True) +diag.add_field('two_stream', 'flux_sw', time_avg=True) +diag.add_field('two_stream', 'flux_lw', time_avg=True) +diag.add_field('two_stream', 'tau_sw', time_avg=True) +diag.add_field('two_stream', 'tau_lw', time_avg=True) + +diag.add_field('dry_convection', 'CAPE', time_avg=True) +diag.add_field('dry_convection', 'CIN', time_avg=True) +diag.add_field('dry_convection', 'parcel_temp', time_avg=True) +diag.add_field('dry_convection', 'ambient_temp', time_avg=True) +diag.add_field('dry_convection', 'dt_tg', time_avg=True) + + +# define namelist values as python dictionary +namelist = Namelist({ + 'main_nml': { + 'dt_atmos': 2.*178, #time step in seconds, multiple of length of day + 'days': 0., + 'seconds': 2.*1379678, #length of time in each .nc file + 'calendar': 'no_calendar' + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':0., + 'two_stream_gray': True, #if 'True' use grey radiation scheme + 'do_lscale_cond': False, + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'use_actual_surface_temperatures':False, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + 'mixed_layer_nml': { + 'tconst' : 90., + 'prescribe_initial_dist':True, + 'evaporation':False, + 'albedo_value': 0.3, + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.0, + 'tau_bm':3600., + }, + + 'dry_convection_nml': { + 'tau':14400., + }, + + 'betts_miller_nml': { + 'rhbm': .7 , + 'do_simp': False, + 'do_shallower': True, + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'tcmin': -223, #Make sure low temperature limit of saturation vapour pressure is low enough that it doesn't cause an error (note that this giant planet has no moisture anyway, so doesn't directly affect calculation. + 'tcmax': 350., + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.125, # neg. value: time in *days* + 'sponge_pbottom': 112.05, #Bottom of the model's sponge down to 1.1205 for 3 model layers for Titan (units are Pa) + }, + + 'spectral_dynamics_nml': { + 'exponent': 2, + 'scale_heights': 4, + 'surf_res': 0.2, + 'robert_coeff': 4e-2, + 'do_water_correction': False, + 'vert_coord_option': 'uneven_sigma', + 'initial_sphum': 0., + 'valid_range_T': [0, 700] + }, + + 'vert_coordinate_nml': { + 'bk': [ 0.00000000e+00, 1.53008955e-04, 4.63790800e-04, 1.10977640e-03, 2.37044150e-03, 4.74479200e-03, 9.12245300e-03, 1.70677050e-02, 3.12516100e-02, 5.59939500e-02, 9.76165000e-02, 1.63754900e-01, 2.60315150e-01, 3.85974250e-01, 5.28084800e-01, 6.65956600e-01, 7.81088000e-01, 8.65400050e-01, 9.21109250e-01, 9.55343500e-01, 9.75416950e-01, 9.86856800e-01, 9.93269300e-01, 9.96830200e-01, 9.98799150e-01, 1.00000000e+00], + 'pk': [0.]*26, + }, + + 'two_stream_gray_rad_nml': { + 'rad_scheme': 'schneider_titan', #Select radiation scheme to use, which in this case is Frierson + 'do_seasonal': False, + 'del_sol':0.85, + 'del_sw':0., + 'atm_abs':5., + 'sw_diff':0.0, + 'solar_exponent':0.42, + 'ir_tau_eq':10, + 'ir_tau_pole':10, + 'linear_tau': 0.15, + 'wv_exponent':2., + 'equinox_day':0.0, + 'use_time_average_coszen':True, + 'solar_constant':15.08, + 'dt_rad_avg': 1379678, + }, + + +# configure the relaxation profile +# 'hs_forcing_nml': { +# 'equilibrium_t_option' : 'top_down', +# 'ml_depth': 10., +# 'spinup_time': 108000, +# 'ka': -2., +# 'ks': -2., +# 'tau_s': 0.2, +# 'calculate_insolation_from_orbit' : True, +# 'do_rayleigh_damping':False, +# 'albedo':0.25, +# 'pure_rad_equil':True, +# 'stratosphere_t_option':'pure_rad_equil', +# 'peri_time': 0., +# 'h_a': 10.8, +# 'use_olr_from_t_surf':True, +# 'frac_of_year_ae':0.3032894472101727, +# }, + + + 'spectral_init_cond_nml': { #namelist additional to the Mars script + 'initial_temperature': 90. + }, + + 'astronomy_nml': { + 'ecc':0.054, #eccentricity of Saturn's orbit around the Sun + 'obliq':26.7, #obliquity wrt the Sun + 'use_mean_anom_in_rrsun_calc':True, + 'use_old_r_inv_squared':False + }, + + 'constants_nml': { + 'orbital_period': 928523294, #value calculated from day/year calculator script + 'solar_const':15.08, + 'radius':2575.0e3, + 'rdgas':290, + 'kappa':0.2727, + 'rotation_period':1377631, #value calculated from day/year calculator script + }, + +}) + + +if __name__=="__main__": + + conv_schemes = ['dry'] + + depths = [2.] + + pers = [93] #longitude of perihelion + + scale = 1. + + for conv in conv_schemes: + for depth_val in depths: + for per_value in pers: + exp = Experiment('grey_titan_T21_daily_tapio_rad_with_sponge_mk12_tau_diag_60_levels_dry_conv_fixed_no_diurnal_cycle_no_seasonal_cycle', codebase=cb) #name of folder in which .nc files are output + exp.clear_rundir() + + exp.diag_table = diag + exp.namelist = namelist.copy() + exp.namelist['constants_nml']['grav'] = scale * 1.354 #surface gravity + exp.namelist['constants_nml']['pstd'] = scale * 14670000.0 #surface pressure - 100 times bigger than below values + exp.namelist['constants_nml']['pstd_mks'] = scale * 146700.0 #in Pa + exp.namelist['spectral_dynamics_nml']['reference_sea_level_press'] = scale * 146700.0 #in Pa + exp.namelist['idealized_moist_phys_nml']['convection_scheme'] = conv + exp.namelist['mixed_layer_nml']['depth'] = depth_val + exp.namelist['astronomy_nml']['per'] = per_value + exp.set_resolution(*RESOLUTION) + +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2, 12001): +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(i, num_cores=NCORES) + notify('top down with conv scheme = '+conv+' has completed', 'isca') diff --git a/exp/radiative_eq_mars/gfdl_astro_mars.py b/exp/radiative_eq_mars/gfdl_astro_mars.py new file mode 100644 index 000000000..7aff5f3c5 --- /dev/null +++ b/exp/radiative_eq_mars/gfdl_astro_mars.py @@ -0,0 +1,228 @@ +import numpy as np + +from isca import IscaCodeBase, GreyCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress +from ntfy import notify + +NCORES = 16 + +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = GreyCodeBase.from_directory(GFDL_BASE) +#cb = GreyCodeBase.from_repo(repo='git@github.com:sit23/Isca.git', commit='fa26ea1') + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +# create a diagnostics output file for daily snapshots +diag = DiagTable() +diag.add_file('atmos_daily',88440 , 'seconds', time_units='days') + +# Define diag table entries +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk', time_avg=True) +diag.add_field('dynamics', 'pk', time_avg=True) +diag.add_field('dynamics', 'vor', time_avg=True) +diag.add_field('dynamics', 'div', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'ucomp_vcomp', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) +diag.add_field('dynamics', 'slp', time_avg=True) +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'height', time_avg=True) +# diag.add_field('dynamics', 'sphum', time_avg=True) +# diag.add_field('atmosphere', 'precipitation', time_avg=True) +# diag.add_field('atmosphere', 'rh', time_avg=True) + +diag.add_field('mixed_layer', 't_surf', time_avg=True) +#diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) + +diag.add_field('hs_forcing', 'teq', time_avg=True) +diag.add_field('hs_forcing', 'mars_solar_long', time_avg=True) +diag.add_field('hs_forcing', 'true_anom', time_avg=True) +diag.add_field('hs_forcing', 'incoming_sw', time_avg=True) +diag.add_field('hs_forcing', 'dec', time_avg=True) +diag.add_field('hs_forcing', 'rrsun', time_avg=True) +diag.add_field('hs_forcing', 'ang', time_avg=True) + +# define namelist values as python dictionary +namelist = Namelist({ + 'main_nml': { + 'dt_atmos': 110, + 'days': 0., + 'seconds': 30.*88440., + 'calendar': 'no_calendar' + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':0., + 'two_stream_gray': False, #Use grey radiation + 'do_newtonian_cooling_as_rad': True, #Use grey radiation + 'convection_scheme': 'none', #Use the simple Betts Miller convection scheme from Frierson + 'newt_relax_surface': False, + 'do_lscale_cond': False, + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'rh_target': 50., + 'delta_t_relax': 7200., + 'use_actual_surface_temperatures':False, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + #Use a large mixed-layer depth, and the Albedo of the CTRL case in Jucker & Gerber, 2017 + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':True, + 'evaporation':False, + 'update_sst_from_fluxes' : False, + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.0, + 'tau_bm':3600., + }, + + 'dry_convection_nml': { + 'tau':7200, + }, + + 'betts_miller_nml': { + 'rhbm': .7 , + 'do_simp': False, + 'do_shallower': True, + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.25, # neg. value: time in *days* + 'sponge_pbottom': 0.5, #Bottom of the model's sponge down to 50hPa (units are Pa) + 'do_conserve_energy': True, + }, + + 'spectral_dynamics_nml': { + 'num_levels': 25, + 'exponent': 2.5, + 'scale_heights': 4, + 'surf_res': 0.1, + 'robert_coeff': 4e-2, + 'do_water_correction': False, + 'vert_coord_option': 'input', + 'initial_sphum': 0., + 'valid_range_T': [0, 700] + }, + + 'vert_coordinate_nml': { + 'bk': [ 0.00000000e+00, 1.53008955e-04, 4.63790800e-04, 1.10977640e-03, 2.37044150e-03, 4.74479200e-03, 9.12245300e-03, 1.70677050e-02, 3.12516100e-02, 5.59939500e-02, 9.76165000e-02, 1.63754900e-01, 2.60315150e-01, 3.85974250e-01, 5.28084800e-01, 6.65956600e-01, 7.81088000e-01, 8.65400050e-01, 9.21109250e-01, 9.55343500e-01, 9.75416950e-01, 9.86856800e-01, 9.93269300e-01, 9.96830200e-01, 9.98799150e-01, 1.00000000e+00], + 'pk': [0.]*26, + }, + + + # configure the relaxation profile + 'hs_forcing_nml': { + 'equilibrium_t_option' : 'top_down', + 'ml_depth': 2., + 'spinup_time': 108000, + 'ka': -40., + 'ks': -2., + 'tau_s': 0.2, + 'calculate_insolation_from_orbit' : True, + 'do_rayleigh_damping':False, + 'albedo':0.3, + 'pure_rad_equil':True, + 'stratosphere_t_option':'pure_rad_equil', + 'h_a': 10.8, + 'use_olr_from_t_surf':True, + 'equinox_day': 0., + 'use_gfdl_astronomy':True, + }, + + 'astronomy_nml': { + 'ecc':0.0935, + 'obliq':25.19, + 'per':70.85, + 'use_mean_anom_in_rrsun_calc':True, + 'use_old_r_inv_squared':False + }, + + 'constants_nml': { + 'orbital_period': 59166360, + 'solar_const':589.0, + 'radius':3396.0e3, + 'rdgas':192.0, + 'kappa':0.22727, + 'rotation_period':88308, + }, + +}) + +if __name__=="__main__": + + conv_schemes = ['none'] + + scales = [ 1.] + + for conv in conv_schemes: + for scale in scales: + exp = Experiment('radiaitive_eq_mars_40_rad_ka_gfdl_astro_macda_levels_no_conv_scheme', codebase=cb) + exp.clear_rundir() + + exp.diag_table = diag + exp.namelist = namelist.copy() + exp.namelist['constants_nml']['grav'] = scale * 3.71 + exp.namelist['constants_nml']['pstd'] = scale * 6100.0 + exp.namelist['constants_nml']['pstd_mks'] = scale * 610.0 + exp.namelist['spectral_dynamics_nml']['reference_sea_level_press'] = scale * 610.0 + exp.namelist['idealized_moist_phys_nml']['convection_scheme'] = conv + + notify('top down with conv scheme = '+conv+' has started', 'isca') + + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2, 361): + exp.run(i, num_cores=NCORES) + notify('top down with conv scheme = '+conv+' has completed', 'isca') diff --git a/exp/socrates_mars/socrates_mars.py b/exp/socrates_mars/socrates_mars.py new file mode 100644 index 000000000..350f4d3ac --- /dev/null +++ b/exp/socrates_mars/socrates_mars.py @@ -0,0 +1,282 @@ +import numpy as np + +from isca import IscaCodeBase, SocratesCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress +#from ntfy import notify +import os + +NCORES = 16 +base_dir = os.path.dirname(os.path.realpath(__file__)) + +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = SocratesCodeBase.from_directory(GFDL_BASE) +# cb = GreyCodeBase.from_repo(repo='git@github.com:sit23/Isca.git', commit='7145be9') + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +inputfiles = [os.path.join(base_dir,'input/sp_lw_17_dsa_mars'), os.path.join(base_dir,'input/sp_sw_42_dsa_mars_sun'), os.path.join(base_dir,'input/sp_lw_17_dsa_mars_k'), os.path.join(base_dir,'input/sp_sw_42_dsa_mars_sun_k'),] + +# create a diagnostics output file for daily snapshots +diag = DiagTable() +#diag.add_file('atmos_monthly', 30, 'days', time_units='days') +diag.add_file('atmos_daily',88440 , 'seconds', time_units='days') +#diag.add_file('atmos_e_daily',1 , 'days', time_units='days') +#diag.add_file('atmos_timestep', 240, 'seconds', time_units='days') + +# Define diag table entries +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk', time_avg=True) +diag.add_field('dynamics', 'pk', time_avg=True) +diag.add_field('dynamics', 'zsurf', time_avg=True) + +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'height', time_avg=True) +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('atmosphere', 'precipitation', time_avg=True) + +diag.add_field('atmosphere', 'dt_tg_convection', time_avg=True) + +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) + +diag.add_field('socrates', 'mars_solar_long', time_avg=True) +diag.add_field('socrates', 'soc_coszen', time_avg=True) +diag.add_field('socrates', 'rrsun', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_down', time_avg=True) +diag.add_field('socrates', 'time_since_ae', time_avg=True) +diag.add_field('socrates', 'true_anomaly', time_avg=True) +diag.add_field('socrates', 'dec', time_avg=True) +diag.add_field('socrates', 'ang', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_lw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_lw_down', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw_down', time_avg=True) + + +# define namelist values as python dictionary +namelist = Namelist({ + 'main_nml': { + 'dt_atmos': 110, + 'days': 0., + 'seconds': 30.*88440., + 'calendar': 'no_calendar' + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':0., + 'two_stream_gray': False, + 'do_lscale_cond': False, + 'do_socrates_radiation': True, + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'use_actual_surface_temperatures':False, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':True, + 'evaporation':False, + 'albedo_value': 0.3, + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.0, + 'tau_bm':3600., + }, + + 'dry_convection_nml': { + 'tau':7200, + }, + + 'betts_miller_nml': { + 'rhbm': .7 , + 'do_simp': False, + 'do_shallower': True, + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'tcmin': -223, #Make sure low temperature limit of saturation vapour pressure is low enough that it doesn't cause an error (note that this giant planet has no moisture anyway, so doesn't directly affect calculation. + 'tcmax': 350., + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.125, # neg. value: time in *days* + 'sponge_pbottom': 0.5, #Bottom of the model's sponge down to 50hPa (units are Pa) + 'do_conserve_energy': True, + }, + + 'spectral_dynamics_nml': { + 'num_levels': 25, + 'exponent': 2.5, + 'scale_heights': 4, + 'surf_res': 0.1, + 'robert_coeff': 4e-2, + 'do_water_correction': False, + 'vert_coord_option': 'input', + 'initial_sphum': 0., + 'valid_range_T': [0, 700], + 'ocean_topog_smoothing': 0.8, + }, + + 'vert_coordinate_nml': { + 'bk': [ 0.00000000e+00, 1.53008955e-04, 4.63790800e-04, 1.10977640e-03, 2.37044150e-03, 4.74479200e-03, 9.12245300e-03, 1.70677050e-02, 3.12516100e-02, 5.59939500e-02, 9.76165000e-02, 1.63754900e-01, 2.60315150e-01, 3.85974250e-01, 5.28084800e-01, 6.65956600e-01, 7.81088000e-01, 8.65400050e-01, 9.21109250e-01, 9.55343500e-01, 9.75416950e-01, 9.86856800e-01, 9.93269300e-01, 9.96830200e-01, 9.98799150e-01, 1.00000000e+00], + 'pk': [0.]*26, + }, + + # 'two_stream_gray_rad_nml': { + # 'rad_scheme': 'frierson', #Select radiation scheme to use, which in this case is Frierson + # 'do_seasonal': True, + # 'atm_abs': 0.2, + # 'sw_diff':0.0, + # 'ir_tau_eq':0.2, + # 'ir_tau_pole':0.2, + # 'linear_tau': 1.0, + # 'equinox_day':0.0, + # 'use_time_average_coszen':True, + # 'solar_constant':589.0, + # }, + + 'socrates_rad_nml': { + 'stellar_constant':589., + 'lw_spectral_filename':'INPUT/sp_lw_17_dsa_mars', + 'sw_spectral_filename':'INPUT/sp_sw_42_dsa_mars_sun', + 'do_read_ozone': False, + 'dt_rad':1320, + 'store_intermediate_rad':True, + 'chunk_size': 16, + 'use_pressure_interp_for_half_levels':False, + 'tidally_locked':False, + 'inc_h2o':False, + 'inc_o3':False, + 'inc_co2':True, + 'inc_n2':True, + 'equinox_day': 0.0, + 'co2_ppmv': 0.949*1e6, + 'n2_mix_ratio': 0.026*(28.0134)/(1000.*8.314/192.0) + }, + +# configure the relaxation profile +# 'hs_forcing_nml': { +# 'equilibrium_t_option' : 'top_down', +# 'ml_depth': 10., +# 'spinup_time': 108000, +# 'ka': -2., +# 'ks': -2., +# 'tau_s': 0.2, +# 'calculate_insolation_from_orbit' : True, +# 'do_rayleigh_damping':False, +# 'albedo':0.25, +# 'pure_rad_equil':True, +# 'stratosphere_t_option':'pure_rad_equil', +# 'peri_time': 0., +# 'h_a': 10.8, +# 'use_olr_from_t_surf':True, +# 'frac_of_year_ae':0.3032894472101727, +# }, + + 'astronomy_nml': { + 'ecc':0.0935, + 'obliq':25.19, + 'use_mean_anom_in_rrsun_calc':True, + 'use_old_r_inv_squared':False + }, + + 'constants_nml': { + 'orbital_period': 59166360, + 'solar_const':589.0, + 'radius':3396.0e3, + 'rdgas':192.0, + 'kappa':0.22727, + 'rotation_period':88308, + }, + +# 'spectral_init_cond_nml': { +# 'topog_file_name': 't42_mola_mars.nc', +# 'topography_option': 'input', +# }, + +}) + +if __name__=="__main__": + + conv_schemes = ['none'] + + depths = [2.] + + pers = [70.85] + + scale = 1. + + for conv in conv_schemes: + for depth_val in depths: + for per_value in pers: + exp = Experiment('soc_mars_mk36_per_value'+str((per_value))+'_'+conv+'_mld_'+str(depth_val)+'_extra_soc_outputs', codebase=cb) + exp.clear_rundir() + + exp.diag_table = diag + exp.inputfiles = inputfiles + exp.namelist = namelist.copy() + exp.namelist['constants_nml']['grav'] = scale * 3.71 + exp.namelist['constants_nml']['pstd'] = scale * 6100.0 + exp.namelist['constants_nml']['pstd_mks'] = scale * 610.0 + exp.namelist['spectral_dynamics_nml']['reference_sea_level_press'] = scale * 610.0 + exp.namelist['idealized_moist_phys_nml']['convection_scheme'] = conv + exp.namelist['mixed_layer_nml']['depth'] = depth_val + exp.namelist['astronomy_nml']['per'] = per_value + +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2, 4): +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(i, num_cores=NCORES) + # notify('top down with conv scheme = '+conv+' has completed', 'isca') diff --git a/exp/socrates_mars/socrates_mars_with_mola.py b/exp/socrates_mars/socrates_mars_with_mola.py new file mode 100644 index 000000000..46dbf40d3 --- /dev/null +++ b/exp/socrates_mars/socrates_mars_with_mola.py @@ -0,0 +1,278 @@ +import numpy as np + +from isca import IscaCodeBase, SocratesCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress +# from ntfy import notify +import os + +NCORES = 16 +base_dir = os.path.dirname(os.path.realpath(__file__)) + +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = SocratesCodeBase.from_directory(GFDL_BASE) +# cb = GreyCodeBase.from_repo(repo='git@github.com:sit23/Isca.git', commit='7145be9') + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + +inputfiles = [os.path.join(base_dir,'input/sp_lw_17_dsa_mars'), os.path.join(base_dir,'input/sp_sw_42_dsa_mars_sun'), os.path.join(base_dir,'input/sp_lw_17_dsa_mars_k'), os.path.join(base_dir,'input/sp_sw_42_dsa_mars_sun_k'), os.path.join(base_dir,'input/t42_mola_mars.nc')] + +# create a diagnostics output file for daily snapshots +diag = DiagTable() +#diag.add_file('atmos_monthly', 30, 'days', time_units='days') +diag.add_file('atmos_daily',88440 , 'seconds', time_units='days') +#diag.add_file('atmos_e_daily',1 , 'days', time_units='days') +#diag.add_file('atmos_timestep', 240, 'seconds', time_units='days') +diag.add_file('atmos_2_hourly', 7370 , 'seconds', time_units='days') + +# Define diag table entries +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk', time_avg=True) +diag.add_field('dynamics', 'pk', time_avg=True) +diag.add_field('dynamics', 'zsurf', time_avg=True) + +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'height', time_avg=True) +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('atmosphere', 'precipitation', time_avg=True) + +diag.add_field('atmosphere', 'dt_tg_convection', time_avg=True) + +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) + +diag.add_field('socrates', 'mars_solar_long', time_avg=True) +diag.add_field('socrates', 'soc_coszen', time_avg=True) +diag.add_field('socrates', 'rrsun', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_down', time_avg=True) +diag.add_field('socrates', 'time_since_ae', time_avg=True) +diag.add_field('socrates', 'true_anomaly', time_avg=True) +diag.add_field('socrates', 'dec', time_avg=True) +diag.add_field('socrates', 'ang', time_avg=True) + +# define namelist values as python dictionary +namelist = Namelist({ + 'main_nml': { + 'dt_atmos': 110, + 'days': 0, + 'seconds': 30*88440, + 'calendar': 'no_calendar' + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':0., + 'two_stream_gray': False, + 'do_lscale_cond': False, + 'do_socrates_radiation': True, + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'use_actual_surface_temperatures':False, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':True, + 'evaporation':False, + 'albedo_value': 0.3, + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.0, + 'tau_bm':3600., + }, + + 'dry_convection_nml': { + 'tau':7200, + }, + + 'betts_miller_nml': { + 'rhbm': .7 , + 'do_simp': False, + 'do_shallower': True, + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'tcmin': -223, #Make sure low temperature limit of saturation vapour pressure is low enough that it doesn't cause an error (note that this giant planet has no moisture anyway, so doesn't directly affect calculation. + 'tcmax': 350, + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.125, # neg. value: time in *days* + 'sponge_pbottom': 0.5, #Bottom of the model's sponge down to 50hPa (units are Pa) + 'do_conserve_energy': True, + }, + + 'spectral_dynamics_nml': { + 'num_levels': 25, + 'exponent': 2.5, + 'scale_heights': 4, + 'surf_res': 0.1, + 'robert_coeff': 4e-2, + 'do_water_correction': False, + 'vert_coord_option': 'input', + 'initial_sphum': 0., + 'valid_range_T': [0, 700], + 'ocean_topog_smoothing': 0.8, + }, + + 'vert_coordinate_nml': { + 'bk': [ 0.00000000e+00, 1.53008955e-04, 4.63790800e-04, 1.10977640e-03, 2.37044150e-03, 4.74479200e-03, 9.12245300e-03, 1.70677050e-02, 3.12516100e-02, 5.59939500e-02, 9.76165000e-02, 1.63754900e-01, 2.60315150e-01, 3.85974250e-01, 5.28084800e-01, 6.65956600e-01, 7.81088000e-01, 8.65400050e-01, 9.21109250e-01, 9.55343500e-01, 9.75416950e-01, 9.86856800e-01, 9.93269300e-01, 9.96830200e-01, 9.98799150e-01, 1.00000000e+00], + 'pk': [0.]*26, + }, + + # 'two_stream_gray_rad_nml': { + # 'rad_scheme': 'frierson', #Select radiation scheme to use, which in this case is Frierson + # 'do_seasonal': True, + # 'atm_abs': 0.2, + # 'sw_diff':0.0, + # 'ir_tau_eq':0.2, + # 'ir_tau_pole':0.2, + # 'linear_tau': 1.0, + # 'equinox_day':0.0, + # 'use_time_average_coszen':True, + # 'solar_constant':589.0, + # }, + + 'socrates_rad_nml': { + 'stellar_constant':589., + 'lw_spectral_filename':'INPUT/sp_lw_17_dsa_mars', + 'sw_spectral_filename':'INPUT/sp_sw_42_dsa_mars_sun', + 'do_read_ozone': False, + 'dt_rad':1320, + 'store_intermediate_rad':True, + 'chunk_size': 16, + 'use_pressure_interp_for_half_levels':False, + 'tidally_locked':False, + 'inc_h2o':False, + 'inc_o3':False, + 'inc_co2':True, + 'inc_n2':True, + 'equinox_day': 0.0, + 'co2_ppmv': 0.949*1e6, + 'n2_mix_ratio': 0.026*(28.0134)/(1000.*8.314/192.0) + }, + +# configure the relaxation profile +# 'hs_forcing_nml': { +# 'equilibrium_t_option' : 'top_down', +# 'ml_depth': 10., +# 'spinup_time': 108000, +# 'ka': -2., +# 'ks': -2., +# 'tau_s': 0.2, +# 'calculate_insolation_from_orbit' : True, +# 'do_rayleigh_damping':False, +# 'albedo':0.25, +# 'pure_rad_equil':True, +# 'stratosphere_t_option':'pure_rad_equil', +# 'peri_time': 0., +# 'h_a': 10.8, +# 'use_olr_from_t_surf':True, +# 'frac_of_year_ae':0.3032894472101727, +# }, + + 'astronomy_nml': { + 'ecc':0.0935, + 'obliq':25.19, + 'use_mean_anom_in_rrsun_calc':True, + 'use_old_r_inv_squared':False + }, + + 'constants_nml': { + 'orbital_period': 59166360, + 'solar_const':589.0, + 'radius':3396.0e3, + 'rdgas':192.0, + 'kappa':0.22727, + 'rotation_period':88308, + }, + + 'spectral_init_cond_nml': { + 'topog_file_name': 't42_mola_mars.nc', + 'topography_option': 'input', + }, + +}) + +if __name__=="__main__": + + conv_schemes = ['none'] + + depths = [2.] + + pers = [70.85] + + scale = 1. + + for conv in conv_schemes: + for depth_val in depths: + for per_value in pers: + exp = Experiment('soc_mars_mk36_per_value'+str((per_value))+'_'+conv+'_mld_'+str(depth_val)+'_with_mola_topo_2024', codebase=cb) + exp.clear_rundir() + + exp.diag_table = diag + exp.inputfiles = inputfiles + exp.namelist = namelist.copy() + exp.namelist['constants_nml']['grav'] = scale * 3.71 + exp.namelist['constants_nml']['pstd'] = scale * 6100.0 + exp.namelist['constants_nml']['pstd_mks'] = scale * 610.0 + exp.namelist['spectral_dynamics_nml']['reference_sea_level_press'] = scale * 610.0 + exp.namelist['idealized_moist_phys_nml']['convection_scheme'] = conv + exp.namelist['mixed_layer_nml']['depth'] = depth_val + exp.namelist['astronomy_nml']['per'] = per_value + +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(113, use_restart=False, num_cores=NCORES, restart_file='/home/links/sit204/Isca/exp/socrates_mars/input/res0112.tar.gz') + for i in range(114, 241): +# with exp_progress(exp, description='o%.0f d{day}' % scale): + exp.run(i, num_cores=NCORES) + # notify('top down with conv scheme = '+conv+' has completed', 'isca') diff --git a/postprocessing/compile_mppn.sh b/postprocessing/compile_mppn.sh index 981ee9950..58e6b7eef 100755 --- a/postprocessing/compile_mppn.sh +++ b/postprocessing/compile_mppn.sh @@ -11,7 +11,9 @@ ppdir=./ # path to directory containing the tool for combining d # 2. Load the necessary tools into the environment source $GFDL_BASE/src/extra/env/$GFDL_ENV -netcdf_flags=`nc-config --cflags --libs` +netcdf_flags=`nc-config --cflags` +netcdf_flags+=' -L/usr/local/Cellar/netcdf/4.6.1_2/lib -lnetcdf' +echo ${netcdf_flags} #-------------------------------------------------------------------------------------------------------- # compile combine tool #cd $ppdir diff --git a/postprocessing/plevel_interpolation/exec/.plev.x.cppdefs b/postprocessing/plevel_interpolation/exec/.plev.x.cppdefs index 468b64d36..41e78312e 100644 --- a/postprocessing/plevel_interpolation/exec/.plev.x.cppdefs +++ b/postprocessing/plevel_interpolation/exec/.plev.x.cppdefs @@ -1 +1 @@ - use_netCDF + use_netCDF diff --git a/postprocessing/plevel_interpolation/scripts/plevel_fn.py b/postprocessing/plevel_interpolation/scripts/plevel_fn.py index d6266cc3d..2b7ae3f02 100644 --- a/postprocessing/plevel_interpolation/scripts/plevel_fn.py +++ b/postprocessing/plevel_interpolation/scripts/plevel_fn.py @@ -5,8 +5,10 @@ import sys import os import sh +import xarray as xar +import pdb -def plevel_call(nc_file_in,nc_file_out, var_names = '-a', p_levels='default', mask_below_surface_option=' '): +def plevel_call(nc_file_in,nc_file_out, var_names = '-a', p_levels='default', mask_below_surface_option=' ', add_back_scalar_axis_vars=False): check_gfdl_directories_set() @@ -23,6 +25,37 @@ def plevel_call(nc_file_in,nc_file_out, var_names = '-a', p_levels='default', ma command = interper + nc_file + out_file + plev +' '+mask_below_surface_option+ var_names print(command) subprocess.call([command], shell=True) + + if add_back_scalar_axis_vars: + add_back_scalar_axis_vars_fn(nc_file_in, nc_file_out) + +def add_back_scalar_axis_vars_fn(file_in, file_out): + ''' For some reason the plevel interpolator will not put variables + with `scalar_axis` as a dimension in the interpolated output file. + This piece of python adds them back after the interpolation. Particularly + important for Mars work. + ''' + + ds_in = xar.open_dataset(file_in, decode_times=False) + ds_out = xar.open_dataset(file_out, decode_times=False) + + try: + ds_in.dims['scalar_axis'] + except KeyError: + return + + list_of_vars_to_copy=[] + + for name in ds_in.var().keys(): + if 'scalar_axis' in ds_in[name].dims and name not in ds_out.var().keys(): + list_of_vars_to_copy.append(name) + + ds_out.coords['scalar_axis'] = ('scalar_axis', ds_in['scalar_axis'].values) + + for out_name in list_of_vars_to_copy: + ds_out[out_name] = (ds_in[out_name].dims, ds_in[out_name].values) + + ds_out.to_netcdf(path=file_out) def daily_average(nc_file_in, nc_file_out): subprocess.call('cdo daymean '+nc_file_in+' '+nc_file_out, shell=True) @@ -50,6 +83,11 @@ def join_files(files_in, file_name_out): subprocess.call('cdo mergetime '+files_in+' '+file_name_out, shell=True) +def join_files_base_dir(base_dir, files_in, file_name_out): + + os.chdir(base_dir) + subprocess.call('cdo mergetime '+files_in+' '+file_name_out, shell=True) + def climatology(file_in, file_name_out): subprocess.call('cdo mergetime '+files_in+' '+file_name_out, shell=True) diff --git a/postprocessing/plevel_interpolation/scripts/run_plevel.py b/postprocessing/plevel_interpolation/scripts/run_plevel.py index 6b6b9bfe7..b6a7cfce6 100644 --- a/postprocessing/plevel_interpolation/scripts/run_plevel.py +++ b/postprocessing/plevel_interpolation/scripts/run_plevel.py @@ -7,12 +7,13 @@ import subprocess start_time=time.time() -base_dir='/disca/share/sit204/data_from_isca_cpu/cssp_perturb_exps/anoms/' +# base_dir='/disca/share/sit204/data_from_isca_cpu/cssp_perturb_exps/anoms/' +base_dir = os.environ['GFDL_DATA'] #exp_name_list = ['soc_ga3_files_smooth_topo_fftw_mk1_fresh_compile_long', 'soc_ga3_files_smooth_topo_old_fft_mk2_long'] -exp_name_list = [f'soc_ga3_do_simple_false_cmip_o3_bucket_perturbed_ens_{f}' for f in range(100, 200)] -avg_or_daily_list=['monthly'] -start_file=1 -end_file=1 +exp_name_list = ['ml_test_with_ml_full_sd_mean_clim_half_month'] +avg_or_daily_list=['half_monthly'] +start_file=133 +end_file=134 nfiles=(end_file-start_file)+1 do_extra_averaging=False #If true, then 6hourly data is averaged into daily data using cdo @@ -33,6 +34,8 @@ plevs['monthly']=' -p "3 16 51 138 324 676 1000 1266 2162 3407 5014 6957 9185 10000 11627 14210 16864 19534 20000 22181 24783 27331 29830 32290 34731 37173 39637 42147 44725 47391 50164 53061 56100 59295 62661 66211 70000 73915 78095 82510 85000 87175 92104 97312"' + plevs['half_monthly']=' -p "3 16 51 138 324 676 1000 1266 2162 3407 5014 6957 9185 10000 11627 14210 16864 19534 20000 22181 24783 27331 29830 32290 34731 37173 39637 42147 44725 47391 50164 53061 56100 59295 62661 66211 70000 73915 78095 82510 85000 87175 92104 97312"' + plevs['timestep']=' -p "3 16 51 138 324 676 1000 1266 2162 3407 5014 6957 9185 10000 11627 14210 16864 19534 20000 22181 24783 27331 29830 32290 34731 37173 39637 42147 44725 47391 50164 53061 56100 59295 62661 66211 70000 73915 78095 82510 85000 87175 92104 97312"' plevs['pentad']=' -p "3 16 51 138 324 676 1000 1266 2162 3407 5014 6957 9185 10000 11627 14210 16864 19534 20000 22181 24783 27331 29830 32290 34731 37173 39637 42147 44725 47391 50164 53061 56100 59295 62661 66211 70000 73915 78095 82510 85000 87175 92104 97312"' @@ -41,6 +44,7 @@ plevs['daily'] =' -p "1000 10000 25000 50000 85000 92500"' var_names['monthly']='-a slp height' + var_names['half_monthly']='-a slp height' var_names['pentad']='-a slp height' var_names['timestep']='-a' var_names['6hourly']='ucomp slp height vor t_surf vcomp omega' @@ -65,17 +69,8 @@ for avg_or_daily in avg_or_daily_list: print(n+start_file) - number_prefix='' - - if n+start_file < 1000: - number_prefix='0' - if n+start_file < 100: - number_prefix='00' - if n+start_file < 10: - number_prefix = '000' - - nc_file_in = base_dir+'/'+exp_name+'/run'+number_prefix+str(n+start_file)+'/atmos_'+avg_or_daily+'.nc' - nc_file_out = out_dir+'/'+exp_name+'/run'+number_prefix+str(n+start_file)+'/atmos_'+avg_or_daily+file_suffix+'.nc' + nc_file_in = base_dir+'/'+exp_name+'/run%04d'%(n+start_file)+'/atmos_'+avg_or_daily+'.nc' + nc_file_out = out_dir+'/'+exp_name+'/run%04d'%(n+start_file)+'/atmos_'+avg_or_daily+file_suffix+'.nc' if not os.path.isfile(nc_file_out): plevel_call(nc_file_in,nc_file_out, var_names = var_names[avg_or_daily], p_levels = plevs[avg_or_daily], mask_below_surface_option=mask_below_surface_set) diff --git a/src/atmos_param/dry_convection/dry_convection.f90 b/src/atmos_param/dry_convection/dry_convection.f90 index b113fa998..103b2fec2 100644 --- a/src/atmos_param/dry_convection/dry_convection.f90 +++ b/src/atmos_param/dry_convection/dry_convection.f90 @@ -31,8 +31,8 @@ module dry_convection_mod private ! --- namelist --- - real :: tau, & !< relaxation timescale [seconds] - gamma !< prescibed lapse rate [non-dim] + real :: tau = 7200. !< relaxation timescale [seconds] + real :: gamma = 1. !< prescibed lapse rate [non-dim] namelist /dry_convection_nml/ tau, gamma diff --git a/src/atmos_param/frierson_monin_obukhov/frierson_monin_obukhov.F90 b/src/atmos_param/frierson_monin_obukhov/frierson_monin_obukhov.F90 new file mode 100644 index 000000000..d6b81698b --- /dev/null +++ b/src/atmos_param/frierson_monin_obukhov/frierson_monin_obukhov.F90 @@ -0,0 +1,1406 @@ + +module frierson_monin_obukhov_mod + + !====================================================================== + ! + ! MONIN-OBUKHOV MODULE + ! + ! Routines for computing surface drag coefficients from + ! data at the lowest model level using + ! Monin-Obukhov scaling + ! + !====================================================================== + + + use constants_mod, only : grav, vonkarm + + use fms_mod, only : file_exist, check_nml_error, & + open_namelist_file, write_version_number, & + mpp_pe, mpp_root_pe, stdlog, close_file, & + error_mesg, FATAL + + implicit none + private + + ! public interfaces + !======================================================================= + public frierson_mo_drag + public mo_profile + public mo_diff + public stable_mix + !======================================================================= + + + ! form of interfaces + !======================================================================= + ! call mo_drag (pt, pt0, z, z0, zt, zq, speed, & + ! drag_m, drag_t, drag_q, & + ! u_star, b_star, [mask]) + ! + ! (In the following the phrase "dimension(:) or (:,:)" means + ! that this routine can be called either with all of the + ! variables so designated being either 1d or 2d arrays. + ! All of these arrays should conform exactly.) + ! + ! input: + ! + ! pt, real, dimension(:) or (:,:) + ! virtual potential temperature at lowest model level + ! degrees Kelvin + ! + ! pt0, real, dimension(:) or (:,:) + ! virtual potential temperature at surface + ! degrees Kelvin + ! + ! z, real, dimension(:) or (:,:) + ! height above surface of lowest model layer + ! meters + ! + ! z0, real, dimension(:) or (:,:) + ! surface roughness for momentum + ! meters + ! + ! zt, real, dimension(:) or (:,:) + ! surface roughness for temperature + ! meters + ! + ! zq, real, dimension(:) or (:,:) + ! surface roughness for moisture + ! meters + ! + ! speed, real, dimension(:) or (:,:) + ! wind speed at lowest model level with respect to surface + ! (any "gustiness" factor should be included in speed) + ! meters/sec + ! + ! inout: + ! + ! drag_m, real, dimension(:) or (:,:) + ! non-dimensional drag coefficient for momentum + ! + ! drag_t, real, dimension(:) or (:,:) + ! non-dimensional drag coefficient for temperature + ! + ! drag_q, real, dimension(:) or (:,:) + ! non-dimensional drag coefficient for moisture + ! + ! (the input values are used only if the time-smoothing + ! option is turned on) + ! + ! output: + ! + ! u_star, real, dimension(:) or (:,:) + ! friction velocity + ! meters/sec + ! + ! b_star, real, dimension(:) or (:,:) + ! buoyancy scale + ! (meters/sec)**2 + ! + ! + ! The magnitude of the wind stress is + ! density*(ustar**2) + ! The drag coefficient for momentum is + ! (u_star/speed)**2 + ! The buoyancy flux is + ! density*ustar*bstar + ! The drag coefficient for heat etc is + ! (u_star/speed)*(b_star/delta_b) + ! where delta_b is the buoyancy difference between + ! the surface and the lowest model level + ! + ! + ! + ! optional: + ! mask : logical, dimension(:) + ! computation performed only where mask = .true. + ! NOTE(!) : mask option is only available for 1d verison + ! + !========================================================================== + ! + ! subroutine mo_profile(zref, z, z0, zt, zq, u_star, b_star, q_star, & + ! del_m, del_h, del_q, [mask]) + ! + ! (In the following the phrase "dimension(:) or (:,:)" means + ! that this routine can be called either with all of the + ! variables so designated being either 1d or 2d arrays. + ! All of these arrays should conform exactly.) + ! + ! input: + ! + ! zref, real + ! height above surface to which interpolation is requested + ! meters + ! + ! z, real, dimension(:) or (:,:) + ! height of lowest model layer + ! meters + ! + ! z0, real, dimension(:) or (:,:) + ! surface roughness for momentum + ! meters + ! + ! zt, real, dimension(:) or (:,:) + ! surface roughness for temperature + ! meters + ! + ! zq, real, dimension(:) or (:,:) + ! surface roughness for moisture + ! meters + ! + ! u_star, real, dimension(:) or (:,:) + ! friction velocity + ! meters/sec + ! + ! b_star, real, dimension(:) or (:,:) + ! buoyancy scale + ! (meters/sec)**2 + ! + ! q_star, real, dimension(:) or (:,:) + ! moisture scale + ! kg/kg + ! + ! (Note: u_star and b_star are output from mo_drag, + ! q_star = flux_q/u_star/rho ) + ! + ! optional input: + ! + ! mask, logical, dimension(:) + ! computation performed only where mask = .true. + ! NOTE(!): mask option is only available for 1d verison + ! + ! + ! output: + ! + ! del_m, real, dimension(:) or (:,:) + ! dimensionless ratio, as defined below, for momentum + ! + ! del_h, real, dimension(:) or (:,:) + ! dimensionless ratio, as defined below, for temperature + ! + ! del_q, real, dimension(:) or (:,:) + ! dimensionless ratio, as defined below, for moisture + ! + ! Ratios are (f(zref) - f_surf)/(f(z) - f_surf) + ! + !========================================================================== + ! + ! subroutine mo_diff(z, u_star, b_star, k_m, k_h, [mask]) + ! + ! input: + ! z, real, dimension(see below) + ! height above surface of at which diffusivities + ! are desired + ! meters + ! + ! u_star, real, dimension(see below) + ! surface friction velocity + ! meters/sec + ! + ! b_star, real, dimension(see below) + ! buoyancy scale + ! (meters/sec)**2 + ! + ! (Note: u_star and b_star are output from mo_drag) + ! + ! optional input: + ! mask : logical, dimension(:) + ! computation performed only where mask = .true. + ! NOTE: mask option is only available for 1d verisons -- see below + ! + ! output: + ! + ! k_m : real, dimension(see below) + ! kinematic diffusivity for momentum + ! (meters**2/sec) + ! + ! k_h : real, dimension(see below) + ! kinemtatic diffusivity for temperature + ! (meters**2/sec) + ! + ! dimensions: any of the following four options can be used + ! + ! 1) diffusivities desired on multiple levels, with 2d (x,y) input + ! z(:,:,:), k_m(:,:,:), k_h(:,:,:) + ! u_star(:,:), b_star(:,:) corresponding to the 1st and 2nd + ! indices of z -- vertical level is third index + ! mask option NOT available + ! + ! 2) as in 1), but with only 1d input + ! z(:,:), k_m(:,:), k_h(:,:) + ! u_star(:), b_star(:) corresponding to the 1st index of z + ! vertical level is second index + ! mask option available + ! + ! 3) diffusivities desired on one level only, with 2d input + ! z(:,:), k_m(:,:), k_h(:,:),u_star(:,:), b_star(:,:) + ! mask option NOT available + ! + ! 4) diffusivities desired on one level only, with 1d input + ! z(:), k_m(:), k_h(:), u_star(:), b_star(:) + ! mask option available + ! + !======================================================================= + + interface frierson_mo_drag + module procedure mo_drag_1d, mo_drag_2d + end interface + + interface mo_profile + module procedure mo_profile_1d, mo_profile_2d + end interface + + interface mo_diff + module procedure mo_diff_0d, mo_diff_1d, mo_diff_2d + module procedure mo_diff_one_lev_0d, mo_diff_one_lev_1d, mo_diff_one_lev_2d + end interface + + interface stable_mix + module procedure stable_mix_0d, stable_mix_1d + module procedure stable_mix_2d, stable_mix_3d + end interface + + + !--------------------- version number --------------------------------- + + character(len=128) :: version = '$Id: frierson_monin_obukhov.f90,v 1.5.4.1 2005/05/13 18:16:40 pjp Exp $' + character(len=128) :: tagname = '$Name: $' + + !======================================================================= + + ! DEFAULT VALUES OF NAMELIST PARAMETERS: + + real :: rich_crit = 2.0 + real :: drag_min = 1.e-05 + real :: relax_time = 0. + logical :: neutral = .false. + integer :: stable_option = 1 + real :: zeta_trans = 0.5 + + namelist /frierson_monin_obukhov_nml/ rich_crit, neutral, drag_min, relax_time, & + stable_option, zeta_trans + + !======================================================================= + + ! MODULE VARIABLES + + real, parameter :: small = 1.e-04 + real :: b_stab, r_crit, sqrt_drag_min, lambda, rich_trans + logical :: init = .false. + + + contains + + !======================================================================= + + subroutine monin_obukhov_init + + integer :: unit, ierr, io + + !------------------- read namelist input ------------------------------- + + if (file_exist('input.nml')) then + unit = open_namelist_file ( ) + ierr=1; do while (ierr /= 0) + read (unit, nml=frierson_monin_obukhov_nml, iostat=io, end=10) + ierr = check_nml_error(io,'frierson_monin_obukhov_nml') + enddo + 10 call close_file (unit) + endif + + !---------- output namelist to log------------------------------------- + + call write_version_number ( version, tagname ) + if ( mpp_pe() == mpp_root_pe() ) then + write (stdlog(), nml=frierson_monin_obukhov_nml) + endif + + if(rich_crit.le.0.25) call error_mesg( & + 'MONIN_OBUKHOV_INIT in MONIN_OBUKHOV_MOD', & + 'rich_crit in monin_obukhov_mod must be > 0.25', FATAL) + + if(drag_min.le.0.0) call error_mesg( & + 'MONIN_OBUKHOV_INIT in MONIN_OBUKHOV_MOD', & + 'drag_min in monin_obukhov_mod must be >= 0.0', FATAL) + + b_stab = 1.0/rich_crit + r_crit = 0.95*rich_crit + + sqrt_drag_min = 0.0 + if(drag_min.ne.0.0) sqrt_drag_min = sqrt(drag_min) + + lambda = 1.0 + (5.0 - b_stab)*zeta_trans ! used only if stable_option = 2 + rich_trans = zeta_trans/(1.0 + 5.0*zeta_trans) ! used only if stable_option = 2 + + init = .true. + + return + end subroutine monin_obukhov_init + + !======================================================================= + + subroutine mo_drag_1d & + (pt, pt0, z, z0, zt, zq, speed, drag_m, drag_t, drag_q, u_star, b_star, mask) + + real, intent(in) , dimension(:) :: pt, pt0, z, z0, zt, zq, speed + real, intent(out), dimension(:) :: drag_m, drag_t, drag_q + real, intent(out), dimension(:) :: u_star, b_star + logical, intent(in), optional, dimension(:) :: mask + + real :: vk2, r_crit1 + + real , dimension(size(pt)) :: rich, fm, fh, fq + integer, dimension(size(pt)) :: stabl, unstabl + logical, dimension(size(pt)) :: avail + + real , dimension(size(pt)) :: & + rich_1d, z_1d, z0_1d, zt_1d, zq_1d, fm_1d, fh_1d, fq_1d, zeta_1d, delta_b, us, bs, qs, & + u_star_eq, b_star_eq, drag_m_eq, drag_t_eq, drag_q_eq + + real :: xi, xi_1, xi_2 + + integer :: i,npts,nptu + + if(.not.init) call monin_obukhov_init + + avail = .true. + if (present(mask)) avail = mask + + vk2 = vonkarm**2 + + ! delta_b is the difference in buoyancy b/w first level and surface + ! rich is the richardson number: (dB/dz)/(du/dz)^2 + where(avail) + delta_b = grav*(pt0 - pt)/pt0 + rich = - z*delta_b/(speed*speed + small) + else where + rich = 0.0 + end where + + if(neutral) then + + where(avail) + fm = log(z/z0) + fh = log(z/zt) + fq = log(z/zq) + us = vonkarm/fm + bs = vonkarm/fh + qs = vonkarm/fq + drag_m = us*us + drag_t = us*bs + drag_q = us*qs + u_star = us*speed + b_star = bs*delta_b + end where + + else + + ! large richardson number -- small velocity or large buoyancy + where(avail .and. rich >= r_crit) + drag_m_eq = drag_min + drag_t_eq = drag_min + drag_q_eq = drag_min + us = sqrt_drag_min + bs = sqrt_drag_min + qs = sqrt_drag_min + end where + + ! solve for zeta on stable and unstable points separately + + ! this subroutine takes in the richardson numbers, and puts into the arrays + ! stabl and unstabl (which have size npts and nptu) based on the sign of rich + call separate_stabilities(npts, nptu, stabl, unstabl, rich, avail, r_crit) + ! unstable points -- now treated as the neutral case. + if(nptu > 0) then + + ! do i =1, nptu + ! rich_1d(i) = rich(unstabl(i)) + ! z_1d (i) = z (unstabl(i)) + ! z0_1d (i) = z0 (unstabl(i)) + ! zt_1d (i) = zt (unstabl(i)) + ! zq_1d (i) = zq (unstabl(i)) + ! end do + ! + ! call solve_zeta & + ! (nptu, rich_1d, z_1d, z0_1d, zt_1d, zq_1d, fm_1d, fh_1d, fq_1d, & + ! unstable = .true.) + ! + do i = 1, nptu + ! fm(unstabl(i)) = fm_1d(i) + ! fh(unstabl(i)) = fh_1d(i) + ! fq(unstabl(i)) = fq_1d(i) + fm(unstabl(i)) = log(z(unstabl(i))/z0(unstabl(i))) + fh(unstabl(i)) = log(z(unstabl(i))/zt(unstabl(i))) + fq(unstabl(i)) = log(z(unstabl(i))/zq(unstabl(i))) + end do + end if + + ! stable points -- now treated with phi = 1 + zeta (so drag coeff goes to + ! zero near Ri = 1). + ! no iteration is necessary -- we have an analytic solution + if(npts > 0) then + ! do i =1, npts + ! rich_1d(i) = rich(stabl(i)) + ! z_1d (i) = z (stabl(i)) + ! z0_1d (i) = z0 (stabl(i)) + ! zt_1d (i) = zt (stabl(i)) + ! zq_1d (i) = zq (stabl(i)) + ! end do + ! + ! call solve_zeta & + ! (npts, rich_1d, z_1d, z0_1d, zt_1d, zq_1d, fm_1d, fh_1d, fq_1d, & + ! unstable = .false.) + ! + + do i = 1, npts + fm(stabl(i)) = log(z(stabl(i))/z0(stabl(i)))/(1. - & + rich(stabl(i))*(1. - z0(stabl(i))/z(stabl(i)))) + fh(stabl(i)) = log(z(stabl(i))/zt(stabl(i)))/(1. - & + rich(stabl(i))*(1. - zt(stabl(i))/z(stabl(i)))) + fq(stabl(i)) = log(z(stabl(i))/zq(stabl(i)))/(1. - & + rich(stabl(i))*(1. - zq(stabl(i))/z(stabl(i)))) + ! fm(stabl(i)) = fm_1d(i) + ! fh(stabl(i)) = fh_1d(i) + ! fq(stabl(i)) = fq_1d(i) + end do + end if + + where (avail .and. rich < r_crit) + us = max(vonkarm/fm, sqrt_drag_min) + bs = max(vonkarm/fh, sqrt_drag_min) + qs = max(vonkarm/fq, sqrt_drag_min) + drag_m_eq = us*us + drag_t_eq = us*bs + drag_q_eq = us*qs + end where + + if(relax_time.ne.0.0) then + call error_mesg('mo_drag', & + 'Non-zero relax_time not allowed in this version of monin_obukhov',FATAL) + ! xi = dt/relax_time + ! xi_1 = 1.0/(1.0 + xi) + ! xi_2 = xi/(1.0 + xi) + ! where (avail) + ! drag_m = xi_1*drag_m + xi_2*drag_m_eq + ! drag_t = xi_1*drag_t + xi_2*drag_t_eq + ! drag_q = xi_1*drag_q + xi_2*drag_q_eq + ! us = sqrt(drag_m) + ! bs = drag_t/us + ! u_star = us*speed + ! b_star = bs*delta_b + ! end where + else + where (avail) + drag_m = drag_m_eq + drag_t = drag_t_eq + drag_q = drag_q_eq + u_star = us*speed + b_star = bs*delta_b + end where + endif + + end if + + return + end subroutine mo_drag_1d + + !======================================================================= + + subroutine solve_zeta(n, rich, z, z0, zt, zq, f_m, f_h, f_q, unstable) + + integer, intent(in) :: n + real , intent(in), dimension(n) :: rich, z, z0, zt, zq + logical, intent (in) :: unstable + real , intent(out), dimension(n) :: f_m, f_h, f_q + + real :: vk, vk2, error, max_cor, zeta_min + real, dimension(n) :: & + d_rich, rich_1, correction, & + z_z0, z_zt, z_zq, ln_z_z0, ln_z_zt, ln_z_zq, zeta, zeta_q, corr + + integer :: iter, max_iter,nn + + vk2 = vonkarm**2 + error = 1.e-4 + max_iter = 20 + zeta_min = 1.e-6 + + z_z0 = z/z0 + z_zt = z/zt + z_zq = z/zq + ln_z_z0 = log(z_z0) + ln_z_zt = log(z_zt) + ln_z_zq = log(z_zq) + + zeta = rich*ln_z_z0*ln_z_z0/ln_z_zt + if(.not.unstable) zeta = zeta/(1.0 - rich/rich_crit) + + iter_loop: do iter = 1, max_iter + + where (abs(zeta).lt.zeta_min) zeta = zeta_min + call solve_zeta_internal & + (unstable, zeta,z_z0,z_zt,ln_z_z0,ln_z_zt,f_m,f_h,rich_1,d_rich) + correction = (rich - rich_1)/d_rich + corr = min(abs(correction),abs(correction/zeta)) + max_cor= maxval(corr) + + if(max_cor .gt. error) then + where(corr.gt. error) zeta = zeta + correction + cycle iter_loop + else + zeta_q = zeta/z_zq + if(unstable) then + call mo_integral_uh(f_q, zeta, zeta_q, ln_z_zq) + else + call mo_integral_s (f_q, zeta, zeta_q, ln_z_zq) + end if + return + end if + + end do iter_loop + + call error_mesg ('solve_zeta in monin_obukhov_mod', & + 'no convergence in surface drag iteration', FATAL) + + end subroutine solve_zeta + + !======================================================================= + + subroutine solve_zeta_internal(unstable, zeta, z_z0, z_zt, & + ln_z_z0, ln_z_zt, f_m, f_h, rich, d_rich) + + logical, intent(in) :: unstable + real, intent(in), dimension(:) :: zeta, z_z0, z_zt, ln_z_z0, ln_z_zt + real, intent(out), dimension(:) :: f_m, f_h, rich, d_rich + + real, dimension(size(zeta)) :: & + x, x_0, x_h, x_h_0, rzeta, zeta_0, zeta_h, df_m, df_h + + rzeta = 1.0/zeta + zeta_0 = zeta/z_z0 + zeta_h = zeta/z_zt + + if(unstable) then + call mo_derivative_um(x , zeta ) + call mo_derivative_um(x_0, zeta_0) + else + call mo_derivative_s(x , zeta ) + call mo_derivative_s(x_0, zeta_0) + endif + + df_m = (x - x_0 )*rzeta + + if(unstable) then + call mo_derivative_uh(x , zeta ) + call mo_derivative_uh(x_0, zeta_h) + else + call mo_derivative_s(x , zeta ) + call mo_derivative_s(x_0, zeta_h) + endif + + df_h = (x - x_0 )*rzeta + + if(unstable) then + call mo_integral_um(f_m, zeta, zeta_0, ln_z_z0) + call mo_integral_uh(f_h, zeta, zeta_h, ln_z_zt) + else + call mo_integral_s(f_m, zeta, zeta_0, ln_z_z0) + call mo_integral_s(f_h, zeta, zeta_h, ln_z_zt) + end if + + rich = zeta*f_h/(f_m*f_m) + d_rich = rich*( rzeta + df_h/f_h - 2.0 *df_m/f_m) + + return + end subroutine solve_zeta_internal + + !======================================================================= + + subroutine mo_derivative_um(phi, zeta) + + real , intent(out), dimension(:) :: phi + real , intent(in), dimension(:) :: zeta + + + ! phi = (1 - 16.0*zeta)**(-0.25) + + phi = (1 - 16.0*zeta)**(-0.5) + phi = sqrt(phi) + + return + end subroutine mo_derivative_um + !======================================================================= + + subroutine mo_derivative_uh(phi, zeta) + + real , intent(out), dimension(:) :: phi + real , intent(in), dimension(:) :: zeta + + + phi = (1 - 16.0*zeta)**(-0.5) + + + return + end subroutine mo_derivative_uh + !======================================================================= + + subroutine mo_derivative_s(phi, zeta) + + real , intent(out), dimension(:) :: phi + real , intent(in), dimension(:) :: zeta + + + phi = 1.0 + zeta*(5.0 + b_stab*zeta)/(1.0 + zeta) + + return + end subroutine mo_derivative_s + + !======================================================================= + + subroutine mo_integral_um (psi, zeta, zeta_0, ln_z_z0_in) + + real , intent(out), dimension(:) :: psi + real , intent(in), dimension(:) :: zeta, zeta_0 + real , optional, dimension(:) :: ln_z_z0_in + + real, dimension(size(zeta)) :: x, x_0, x1, x1_0, ln_z_z0, num, denom, y + + if(present(ln_z_z0_in)) then + ln_z_z0 = ln_z_z0_in + else + ln_z_z0 = log(zeta/zeta_0) + end if + + ! x = (1 - 16.0*zeta)**(0.25) + ! x_0 = (1 - 16.0*zeta_0)**(0.25) + + x = sqrt(sqrt(1 - 16.0*zeta)) + x_0 = sqrt(sqrt(1 - 16.0*zeta_0)) + + x1 = 1.0 + x + x1_0 = 1.0 + x_0 + num = x1*x1*(1.0 + x*x) + denom = x1_0*x1_0*(1.0 + x_0*x_0) + y = atan(x) - atan(x_0) + psi = ln_z_z0 - log(num/denom) + 2*y + + return + end subroutine mo_integral_um + + !======================================================================= + subroutine mo_integral_uh (psi, zeta, zeta_0, ln_z_z0_in) + + real , intent(out), dimension(:) :: psi + real , intent(in), dimension(:) :: zeta, zeta_0 + real , optional, dimension(:) :: ln_z_z0_in + + real, dimension(size(zeta)) :: x, x_0, ln_z_z0 + + if(present(ln_z_z0_in)) then + ln_z_z0 = ln_z_z0_in + else + ln_z_z0 = log(zeta/zeta_0) + end if + + x = sqrt(1 - 16.0*zeta) + x_0 = sqrt(1 - 16.0*zeta_0) + psi = ln_z_z0 - 2.0*log( (1.0 + x)/(1.0 + x_0) ) + + return + end subroutine mo_integral_uh + + !======================================================================= + + subroutine mo_integral_s (psi, zeta, zeta_0, ln_z_z0_in) + + real , intent(out), dimension(:) :: psi + real , intent(in), dimension(:) :: zeta, zeta_0 + real , optional, dimension(:) :: ln_z_z0_in + + real, dimension(size(zeta)) :: ln_z_z0 + + if(present(ln_z_z0_in)) then + ln_z_z0 = ln_z_z0_in + else + ln_z_z0 = log(zeta/zeta_0) + end if + + + psi = ln_z_z0 + (5.0 - b_stab)*log((1.0 + zeta)/(1.0 + zeta_0)) & + + b_stab*(zeta - zeta_0) + + return + end subroutine mo_integral_s + + !======================================================================= + + subroutine mo_profile_1d(zref, zref_t, z, z0, zt, zq, u_star, b_star, q_star, del_m, del_h, del_q, mask) + ! zref_t only for compatability with lima revision of flux_exchange.f90. Nothing is done with it. + + real, intent(in) :: zref, zref_t + real, intent(in) , dimension(:) :: z, z0, zt, zq, u_star, b_star, q_star + real, intent(out), dimension(:) :: del_m, del_h, del_q + logical, optional, dimension(:) :: mask + + integer, dimension(size(z)) :: stabl, unstabl + real , allocatable, dimension(:) :: & + zeta_1d, zeta_0_1d, zeta_h_1d, zeta_q_1d, zeta_ref_1d, & + ln_z_z0_1d, ln_z_zt_1d, ln_z_zq_1d, ln_z_zref_1d, & + f_m_1d, f_h_1d, f_q_1d, f_m_ref_1d, f_h_ref_1d, f_q_ref_1d + + integer :: i,j, npts,nptu + + real, dimension(size(z)) :: zeta, zeta_0, zeta_h, zeta_q, zeta_ref, & + ln_z_z0, ln_z_zt, ln_z_zq, ln_z_zref, & + f_m_ref, f_m_0, f_h_ref, f_h_0, f_q_ref, f_q_0, & + mo_length_inv + + logical, dimension(size(z)) :: avail, avail1 + + + if(.not.init) call monin_obukhov_init + + !-- zero output arrays -- + del_m = 0.0 + del_h = 0.0 + del_q = 0.0 + + avail = .true. + if (present(mask)) avail = mask + + where(avail) + ln_z_z0 = log(z/z0) + ln_z_zt = log(z/zt) + ln_z_zq = log(z/zq) + ln_z_zref = log(z/zref) + end where + + if(neutral) then + + where(avail) + del_m = 1.0 - ln_z_zref/ln_z_z0 + del_h = 1.0 - ln_z_zref/ln_z_zt + del_q = 1.0 - ln_z_zref/ln_z_zq + end where + + else + + avail1 = avail .and. (u_star.ne.0.0) + + where(u_star .eq. 0.0) + del_m = 0.0 + del_h = 0.0 + del_q = 0.0 + end where + + where(avail1) + mo_length_inv = - vonkarm * b_star/(u_star*u_star) + zeta = z *mo_length_inv + zeta_0 = z0 *mo_length_inv + zeta_h = zt *mo_length_inv + zeta_q = zq *mo_length_inv + zeta_ref = zref*mo_length_inv + elsewhere + zeta = 0.0 + end where + + call separate_stabilities(npts, nptu, stabl, unstabl, zeta, avail1) + + if(nptu > 0) then + ! + ! allocate(zeta_1d (nptu)) + ! allocate(zeta_0_1d (nptu)) + ! allocate(zeta_h_1d (nptu)) + ! allocate(zeta_q_1d (nptu)) + ! allocate(zeta_ref_1d (nptu)) + ! allocate(ln_z_z0_1d (nptu)) + ! allocate(ln_z_zt_1d (nptu)) + ! allocate(ln_z_zq_1d (nptu)) + ! allocate(ln_z_zref_1d (nptu)) + ! allocate(f_m_1d (nptu)) + ! allocate(f_h_1d (nptu)) + ! allocate(f_q_1d (nptu)) + ! allocate(f_m_ref_1d (nptu)) + ! allocate(f_h_ref_1d (nptu)) + ! allocate(f_q_ref_1d (nptu)) + ! + ! do i =1, nptu + ! zeta_1d (i) = zeta (unstabl(i)) + ! zeta_0_1d (i) = zeta_0 (unstabl(i)) + ! zeta_h_1d (i) = zeta_h (unstabl(i)) + ! zeta_q_1d (i) = zeta_q (unstabl(i)) + ! zeta_ref_1d (i) = zeta_ref (unstabl(i)) + ! ln_z_z0_1d(i) = ln_z_z0 (unstabl(i)) + ! ln_z_zt_1d(i) = ln_z_zt (unstabl(i)) + ! ln_z_zq_1d(i) = ln_z_zq (unstabl(i)) + ! ln_z_zref_1d(i) = ln_z_zref(unstabl(i)) + ! end do + ! + ! call mo_integral_um(f_m_1d ,zeta_1d, zeta_0_1d, ln_z_z0_1d ) + ! call mo_integral_uh(f_h_1d ,zeta_1d, zeta_h_1d, ln_z_zt_1d ) + ! call mo_integral_uh(f_q_1d ,zeta_1d, zeta_q_1d, ln_z_zq_1d ) + ! call mo_integral_um(f_m_ref_1d ,zeta_1d, zeta_ref_1d, ln_z_zref_1d) + ! call mo_integral_uh(f_h_ref_1d ,zeta_1d, zeta_ref_1d, ln_z_zref_1d) + ! f_q_ref_1d = f_h_ref_1d + ! + do i = 1, nptu + del_m(unstabl(i)) = 1.0 - ln_z_zref(unstabl(i))/ln_z_z0(unstabl(i)) + del_h(unstabl(i)) = 1.0 - ln_z_zref(unstabl(i))/ln_z_zt(unstabl(i)) + del_q(unstabl(i)) = 1.0 - ln_z_zref(unstabl(i))/ln_z_zq(unstabl(i)) + ! del_m(unstabl(i)) = 1.0 - f_m_ref_1d(i)/f_m_1d(i) + ! del_h(unstabl(i)) = 1.0 - f_h_ref_1d(i)/f_h_1d(i) + ! del_q(unstabl(i)) = 1.0 - f_q_ref_1d(i)/f_q_1d(i) + end do + + ! deallocate(zeta_1d ) + ! deallocate(zeta_0_1d ) + ! deallocate(zeta_h_1d ) + ! deallocate(zeta_q_1d ) + ! deallocate(zeta_ref_1d ) + ! deallocate(ln_z_z0_1d ) + ! deallocate(ln_z_zt_1d ) + ! deallocate(ln_z_zq_1d ) + ! deallocate(ln_z_zref_1d ) + ! deallocate(f_m_1d ) + ! deallocate(f_h_1d ) + ! deallocate(f_q_1d ) + ! deallocate(f_m_ref_1d ) + ! deallocate(f_h_ref_1d ) + ! deallocate(f_q_ref_1d ) + + end if + + if(npts > 0) then + + ! allocate(zeta_1d (npts)) + ! allocate(zeta_0_1d (npts)) + ! allocate(zeta_h_1d (npts)) + ! allocate(zeta_q_1d (npts)) + ! allocate(zeta_ref_1d (npts)) + ! allocate(ln_z_z0_1d (npts)) + ! allocate(ln_z_zt_1d (npts)) + ! allocate(ln_z_zq_1d (npts)) + ! allocate(ln_z_zref_1d (npts)) + ! allocate(f_m_1d (npts)) + ! allocate(f_h_1d (npts)) + ! allocate(f_q_1d (npts)) + ! allocate(f_m_ref_1d (npts)) + ! allocate(f_h_ref_1d (npts)) + ! allocate(f_q_ref_1d (npts)) + + ! do i =1, npts + ! zeta_1d (i) = zeta (stabl(i)) + ! zeta_0_1d (i) = zeta_0 (stabl(i)) + ! zeta_h_1d (i) = zeta_h (stabl(i)) + ! zeta_q_1d (i) = zeta_q (stabl(i)) + ! zeta_ref_1d (i) = zeta_ref (stabl(i)) + ! ln_z_z0_1d(i) = ln_z_z0 (stabl(i)) + ! ln_z_zt_1d(i) = ln_z_zt (stabl(i)) + ! ln_z_zq_1d(i) = ln_z_zq (stabl(i)) + ! ln_z_zref_1d(i) = ln_z_zref(stabl(i)) + ! end do + + ! call mo_integral_s(f_m_1d , zeta_1d, zeta_0_1d, ln_z_z0_1d ) + ! call mo_integral_s(f_h_1d , zeta_1d, zeta_h_1d, ln_z_zt_1d ) + ! call mo_integral_s(f_q_1d , zeta_1d, zeta_q_1d, ln_z_zq_1d ) + ! call mo_integral_s(f_m_ref_1d , zeta_1d, zeta_ref_1d, ln_z_zref_1d) + ! f_h_ref_1d = f_m_ref_1d + ! f_q_ref_1d = f_m_ref_1d + + do i = 1, npts + del_m(stabl(i)) = ( ln_z_z0(stabl(i)) - ln_z_zref(stabl(i)) + & + zeta_ref(stabl(i)) - zeta_0(stabl(i)) )/ & + ( ln_z_z0(stabl(i)) + zeta(stabl(i)) - zeta_0(stabl(i)) ) + del_h(stabl(i)) = ( ln_z_zt(stabl(i)) - ln_z_zref(stabl(i)) + & + zeta_ref(stabl(i)) - zeta_h(stabl(i)) )/ & + ( ln_z_zt(stabl(i)) + zeta(stabl(i)) - zeta_h(stabl(i)) ) + del_q(stabl(i)) = ( ln_z_zq(stabl(i)) - ln_z_zref(stabl(i)) + & + zeta_ref(stabl(i)) - zeta_q(stabl(i)) )/ & + ( ln_z_zq(stabl(i)) + zeta(stabl(i)) - zeta_q(stabl(i)) ) + ! del_m(stabl(i)) = 1.0 - f_m_ref_1d(i)/f_m_1d(i) + ! del_h(stabl(i)) = 1.0 - f_h_ref_1d(i)/f_h_1d(i) + ! del_q(stabl(i)) = 1.0 - f_q_ref_1d(i)/f_q_1d(i) + end do + + ! deallocate(zeta_1d ) + ! deallocate(zeta_0_1d ) + ! deallocate(zeta_h_1d ) + ! deallocate(zeta_q_1d ) + ! deallocate(zeta_ref_1d ) + ! deallocate(ln_z_z0_1d ) + ! deallocate(ln_z_zt_1d ) + ! deallocate(ln_z_zq_1d ) + ! deallocate(ln_z_zref_1d ) + ! deallocate(f_m_1d ) + ! deallocate(f_h_1d ) + ! deallocate(f_q_1d ) + ! deallocate(f_m_ref_1d ) + ! deallocate(f_h_ref_1d ) + ! deallocate(f_q_ref_1d ) + + end if + + end if + + return + end subroutine mo_profile_1d + + !======================================================================= + + subroutine mo_diff_1d(z, u_star, b_star, k_m, k_h, mask) + + real, intent(in), dimension(:,:) :: z + real, intent(in), dimension(:) :: u_star, b_star + real, intent(out), dimension(:,:) :: k_m, k_h + logical, optional, dimension(:) :: mask + + + real , dimension(size(z,1),size(z,2)) :: phi_m , phi_h , zeta + real , allocatable, dimension(:) :: phi_m1, phi_h1, zeta1 + real , dimension(size(z,1)) :: mo_length_inv + integer, dimension(size(z,1)) :: stab, unstab + logical, dimension(size(z,1)) :: avail, avail1 + integer :: n, i, k, ns, nu + + + if(.not.init) call monin_obukhov_init + + avail = .true. + if (present(mask)) avail = mask + + if(neutral) then + + do k =1, size(z,2) + where(avail) + k_m(:,k) = vonkarm * u_star * z(:,k) + k_h(:,k) = k_m(:,k) + end where + end do + + else + + avail1 = avail .and. (u_star.ne.0.0) + + do k =1, size(z,2) + where(u_star .eq. 0.0) + k_m(:,k) = 0.0 + k_h(:,k) = 0.0 + end where + end do + + do k =1, size(z,2) + where(avail1) + mo_length_inv = - vonkarm * b_star/(u_star*u_star) + zeta(:,k) = z(:,k)*mo_length_inv + elsewhere + mo_length_inv = 0.0 + endwhere + end do + + call separate_stabilities(ns, nu, stab, unstab, mo_length_inv, avail1) + + if(nu > 0) then + + + ! allocate(zeta1 (nu)) + ! allocate(phi_m1(nu)) + ! allocate(phi_h1(nu)) + + + do k = 1, size(z,2) + ! do i = 1, nu + ! zeta1(i) = zeta(unstab(i),k) + ! end do + + ! call mo_derivative_um(phi_m1, zeta1) + ! call mo_derivative_uh(phi_h1, zeta1) + + do i = 1, nu + ! phi_m(unstab(i),k) = phi_m1(i) + ! phi_h(unstab(i),k) = phi_h1(i) + phi_m(unstab(i),k) = 1.0 + phi_h(unstab(i),k) = 1.0 + end do + end do + + ! deallocate(zeta1) + ! deallocate(phi_m1) + ! deallocate(phi_h1) + + end if + + if(ns > 0) then + + ! allocate(zeta1 (ns)) + ! allocate(phi_m1(ns)) + ! allocate(phi_h1(ns)) + + do k = 1, size(z,2) + ! do i =1, ns + ! zeta1(i) = zeta(stab(i),k) + ! end do + + ! call mo_derivative_s(phi_m1, zeta1) + ! phi_h1 = phi_m1 + + do i = 1, ns + phi_m(stab(i),k) = 1.0 + zeta(stab(i),k) + phi_h(stab(i),k) = 1.0 + zeta(stab(i),k) + ! phi_m(stab(i),k) = phi_m1(i) + ! phi_h(stab(i),k) = phi_h1(i) + end do + end do + + ! deallocate(zeta1) + ! deallocate(phi_m1) + ! deallocate(phi_h1) + + end if + + do k =1, size(z,2) + where(avail1) + k_m(:,k) = vonkarm * u_star * z(:,k)/phi_m(:,k) + k_h(:,k) = vonkarm * u_star * z(:,k)/phi_h(:,k) + end where + end do + + endif + + return + end subroutine mo_diff_1d + + !======================================================================= + + subroutine separate_stabilities(ns, nu, stab, unstab, x, mask, crit) + + real , intent(in), dimension(:) :: x + logical, intent(in), dimension(:) :: mask + real, intent(in), optional :: crit + integer, intent(out) :: ns ,nu + integer, intent(out), dimension(:) :: stab, unstab + + real :: xcrit + integer :: i,j + + ns=0 + nu=0 + + xcrit = 1.e10 + if(present(crit)) xcrit = crit + + do i=1,size(x) + if (mask(i) .and. x(i) >= 0.0 .and. x(i) < xcrit) then + ns=ns+1 + stab(ns)=i + else if (mask(i) .and. x(i) < 0.0) then + nu=nu+1 + unstab(nu)=i + endif + enddo + + return + end subroutine separate_stabilities + + !======================================================================= + ! The following routines allow some of the above subroutines to be called + ! with different dimensions of the input and output + ! + !======================================================================= + + subroutine mo_drag_2d & + (pt, pt0, z, z0, zt, zq, speed, drag_m, drag_t, drag_q, u_star, b_star) + + real, intent(in), dimension(:,:) :: z, speed, pt, pt0, z0, zt, zq + real, intent(out), dimension(:,:) :: drag_m, drag_t, drag_q + real, intent(out), dimension(:,:) :: u_star, b_star + + real, dimension(size(pt,1)*size(pt,2)) :: z_1d, speed_1d, pt_1d, pt0_1d, & + z0_1d, zt_1d, zq_1d, drag_m_1d, drag_t_1d, drag_q_1d, u_star_1d, b_star_1d + + integer :: shape1(1), shape2(2) + + shape1 = size(pt,1) * size(pt,2) + shape2 = (/ size(pt,1), size(pt,2) /) + + z_1d = reshape (z , shape1) + speed_1d = reshape (speed , shape1) + pt_1d = reshape (pt , shape1) + pt0_1d = reshape (pt0 , shape1) + z0_1d = reshape (z0 , shape1) + zt_1d = reshape (zt , shape1) + zq_1d = reshape (zq , shape1) + u_star_1d = reshape (u_star , shape1) + b_star_1d = reshape (b_star , shape1) + + call mo_drag_1d & + (pt_1d, pt0_1d, z_1d, z0_1d, zt_1d, zq_1d, speed_1d,& + drag_m_1d, drag_t_1d, drag_q_1d, u_star_1d, b_star_1d) + + drag_m = reshape (drag_m_1d , shape2) + drag_t = reshape (drag_t_1d , shape2) + drag_q = reshape (drag_q_1d , shape2) + u_star = reshape (u_star_1d , shape2) + b_star = reshape (b_star_1d , shape2) + + + return + end subroutine mo_drag_2d + !======================================================================= + + subroutine mo_profile_2d(zref, zref_t, z, z0, zt, zq, u_star, b_star, q_star, del_m, del_h, del_q) + ! zref_t only for compatability with lima revision of flux_exchange.f90. Nothing is done with it. + + real, intent(in) :: zref, zref_t + real, intent(in) , dimension(:,:) :: z, z0, zt, zq, u_star, b_star, q_star + real, intent(out), dimension(:,:) :: del_m, del_h, del_q + + real, dimension(size(z,1)*size(z,2)) :: z_1d, z0_1d, zt_1d, zq_1d, & + u_star_1d, b_star_1d, q_star_1d, & + del_m_1d, del_h_1d, del_q_1d + + integer :: shape1(1), shape2(2) + + shape1 = size(z,1) * size(z,2) + shape2 = (/ size(z,1), size(z,2) /) + + z_1d = reshape (z , shape1) + z0_1d = reshape (z0 , shape1) + zt_1d = reshape (zt , shape1) + zq_1d = reshape (zq , shape1) + u_star_1d = reshape (u_star , shape1) + b_star_1d = reshape (b_star , shape1) + q_star_1d = reshape (q_star , shape1) + + call mo_profile_1d& + (zref, zref_t, z_1d, z0_1d, zt_1d, zq_1d, u_star_1d, b_star_1d, q_star_1d, del_m_1d, del_h_1d, del_q_1d) + + del_m = reshape (del_m_1d , shape2) + del_h = reshape (del_h_1d , shape2) + del_q = reshape (del_q_1d , shape2) + + return + end subroutine mo_profile_2d + + !======================================================================= + + subroutine mo_diff_one_lev_1d(z, u_star, b_star, k_m, k_h, mask) + + real, intent(in), dimension(:) :: z + real, intent(in), dimension(:) :: u_star, b_star + real, intent(out), dimension(:) :: k_m, k_h + logical, optional, dimension(:) :: mask + + real, dimension(size(z),1) :: z_nlev, k_m_nlev, k_h_nlev + logical, dimension(size(z)) :: avail + + avail = .true. + if (present(mask)) avail = mask + + z_nlev(:,1) = z + + call mo_diff_1d(z_nlev, u_star, b_star, k_m_nlev, k_h_nlev, avail) + + where(avail) + k_m = k_m_nlev(:,1) + k_h = k_h_nlev(:,1) + end where + + + return + end subroutine mo_diff_one_lev_1d + + !======================================================================= + + subroutine mo_diff_one_lev_0d(z, u_star, b_star, k_m, k_h) + + real, intent(in) :: z + real, intent(in) :: u_star, b_star + real, intent(out) :: k_m, k_h + + real, dimension(1,1) :: z1, k_m1, k_h1 + real, dimension(1) :: u_star1, b_star1 + + z1 = z + u_star1 = u_star + b_star1 = b_star + call mo_diff_1d(z1, u_star1, b_star1, k_m1, k_h1) + k_m = k_m1(1,1) + k_h = k_h1(1,1) + + return + end subroutine mo_diff_one_lev_0d + + !======================================================================= + + subroutine mo_diff_0d(z, u_star, b_star, k_m, k_h) + + real, intent(in), dimension(:) :: z + real, intent(in) :: u_star, b_star + real, intent(out), dimension(:) :: k_m, k_h + + real, dimension(size(z,1),1) :: z1, k_m1, k_h1 + real, dimension(1) :: u_star1, b_star1 + + z1(:,1) = z + u_star1 = u_star + b_star1 = b_star + call mo_diff_1d(z1, u_star1, b_star1, k_m1, k_h1) + k_m = k_m1(:,1) + k_h = k_h1(:,1) + + return + end subroutine mo_diff_0d + !======================================================================= + + subroutine mo_diff_2d(z, u_star, b_star, k_m, k_h) + + real, intent(in), dimension(:,:,:) :: z + real, intent(in), dimension(:,:) :: u_star, b_star + real, intent(out), dimension(:,:,:) :: k_m, k_h + + real, dimension(size(z,1)*size(z,2),size(z,3)) :: z1, k_m1, k_h1 + + real, dimension(size(z,1)*size(z,2)) :: u_star1, b_star1 + + integer :: i, j, k, ii + + do j = 1, size(z,2) + do i = 1, size(z,1) + ii = i + (j -1)*size(z,1) + u_star1(ii) = u_star(i,j) + b_star1(ii) = b_star(i,j) + do k = 1, size(z,3) + z1(ii,k) = z(i,j,k) + end do + end do + end do + + call mo_diff_1d(z1,u_star1,b_star1,k_m1,k_h1) + + do j = 1, size(z,2) + do i = 1, size(z,1) + ii = i + (j -1)*size(z,1) + do k = 1, size(z,3) + k_m(i,j,k) = k_m1(ii,k) + k_h(i,j,k) = k_h1(ii,k) + end do + end do + end do + + return + end subroutine mo_diff_2d + + !======================================================================= + + subroutine mo_diff_one_lev_2d(z, u_star, b_star, k_m, k_h) + + real, intent(in), dimension(:,:) :: z + real, intent(in), dimension(:,:) :: u_star, b_star + real, intent(out), dimension(:,:) :: k_m, k_h + + real, dimension(size(z,1),size(z,2),1) :: z_nlev, k_m_nlev, k_h_nlev + + z_nlev(:,:,1) = z + + call mo_diff_2d(z_nlev, u_star,b_star, k_m_nlev, k_h_nlev) + + k_m = k_m_nlev(:,:,1) + k_h = k_h_nlev(:,:,1) + + return + end subroutine mo_diff_one_lev_2d + !======================================================================= + + subroutine stable_mix_3d(rich, mix) + + real, intent(in) , dimension(:,:,:) :: rich + real, intent(out), dimension(:,:,:) :: mix + + real, dimension(size(rich,1),size(rich,2),size(rich,3)) :: & + r, a, b, c, zeta, phi + + mix = 0.0 + + if(stable_option == 1) then + + where(rich > 0.0 .and. rich < rich_crit) + r = 1.0/rich + a = r - b_stab + b = r - (1.0 + 5.0) + c = - 1.0 + + zeta = (-b + sqrt(b*b - 4.0*a*c))/(2.0*a) + phi = 1.0 + b_stab*zeta + (5.0 - b_stab)*zeta/(1.0 + zeta) + mix = 1./(phi*phi) + end where + + else if(stable_option == 2) then + + where(rich > 0.0 .and. rich <= rich_trans) + mix = (1.0 - 5.0*rich)**2 + end where + where(rich > rich_trans .and. rich < rich_crit) + mix = ((1.0 - b_stab*rich)/lambda)**2 + end where + + end if + + return + end subroutine stable_mix_3d + !======================================================================= + + subroutine stable_mix_2d(rich, mix) + + real, intent(in) , dimension(:,:) :: rich + real, intent(out), dimension(:,:) :: mix + + real, dimension(size(rich,1),size(rich,2),1) :: rich_3d, mix_3d + + rich_3d(:,:,1) = rich + + call stable_mix_3d(rich_3d, mix_3d) + + mix = mix_3d(:,:,1) + + return + end subroutine stable_mix_2d + + !======================================================================= + + subroutine stable_mix_1d(rich, mix) + + real, intent(in) , dimension(:) :: rich + real, intent(out), dimension(:) :: mix + + real, dimension(size(rich),1,1) :: rich_3d, mix_3d + + rich_3d(:,1,1) = rich + + call stable_mix_3d(rich_3d, mix_3d) + + mix = mix_3d(:,1,1) + + return + end subroutine stable_mix_1d + + !======================================================================= + + subroutine stable_mix_0d(rich, mix) + + real, intent(in) :: rich + real, intent(out) :: mix + + real, dimension(1,1,1) :: rich_3d, mix_3d + + rich_3d(1,1,1) = rich + + call stable_mix_3d(rich_3d, mix_3d) + + mix = mix_3d(1,1,1) + + return + end subroutine stable_mix_0d + !======================================================================= + + end module frierson_monin_obukhov_mod + \ No newline at end of file diff --git a/src/atmos_param/hs_forcing/hs_forcing.F90 b/src/atmos_param/hs_forcing/hs_forcing.F90 index 70fc3d23c..3a2f08900 100644 --- a/src/atmos_param/hs_forcing/hs_forcing.F90 +++ b/src/atmos_param/hs_forcing/hs_forcing.F90 @@ -46,7 +46,7 @@ module hs_forcing_mod use tracer_manager_mod, only: query_method, get_number_tracers use interpolator_mod, only: interpolate_type, interpolator_init, & interpolator, interpolator_end, & - CONSTANT, INTERP_WEIGHTED_P + CONSTANT, INTERP_WEIGHTED_P, ZERO use astronomy_mod, only: diurnal_exoplanet, astronomy_init, obliq, ecc #ifdef COLUMN_MODEL @@ -62,7 +62,7 @@ module hs_forcing_mod !----------------------------------------------------------------------- !---------- interfaces ------------ - public :: hs_forcing, hs_forcing_init, hs_forcing_end + public :: hs_forcing, hs_forcing_init, hs_forcing_end, local_heating type(interpolate_type),save :: heating_source_interp type(interpolate_type),save :: u_interp @@ -105,6 +105,7 @@ module hs_forcing_mod real :: heat_capacity=4.2e6 ! equivalent to a 1m mixed layer water ocean real :: ml_depth=1 ! depth for heat capacity calculation real :: spinup_time=10800. ! number of days to spin up heat capacity for - req. multiple of orbital_period + real :: equinox_day = 0. !N.b. the definition of declination is different here to what's in astronomy.F90 (the astronomy.F90 version has a minus sign. So to get equivalent behaviour to two-stream-grey/rrtm/socrates, the equinox_day here ought to be different by 0.5. I.e. here it should be 0.25 to get Earth-like calendar, rather than 0.75 elsewhere.) !----------------------------------------------------------------------- @@ -119,8 +120,8 @@ module hs_forcing_mod u_wind_file, v_wind_file, equilibrium_t_option,& equilibrium_t_file, p_trop, alpha, peri_time, smaxis, albedo, & lapse, h_a, tau_s, orbital_period, & - heat_capacity, ml_depth, spinup_time, stratosphere_t_option, P00 - + heat_capacity, ml_depth, spinup_time, stratosphere_t_option, & + equinox_day, P00 !----------------------------------------------------------------------- character(len=128) :: version='$Id: hs_forcing.F90,v 19.0 2012/01/06 20:10:01 fms Exp $' @@ -233,8 +234,6 @@ subroutine hs_forcing ( is, ie, js, je, dt, Time, lon, lat, p_half, p_full, & if(trim(local_heating_option) /= '') then call local_heating ( Time, is, js, lon, lat, ps, p_full, p_half, ttnd ) tdt = tdt + ttnd -! if (id_local_heating > 0) used = send_data ( id_local_heating, ttnd, Time, is, js) - if (id_local_heating > 0) used = send_data ( id_local_heating, ttnd, Time) endif ! if (id_tdt > 0) used = send_data ( id_tdt, tdt, Time, is, js) @@ -285,7 +284,7 @@ subroutine hs_forcing_init ( axes, Time, lonb, latb, lat ) integer, intent(in) :: axes(4) type(time_type), intent(in) :: Time real, intent(in), dimension(:,:) :: lat - real, intent(in), optional, dimension(:,:) :: lonb, latb + real, intent(in), dimension(:,:) :: lonb, latb !----------------------------------------------------------------------- @@ -453,7 +452,7 @@ subroutine hs_forcing_init ( axes, Time, lonb, latb, lat ) endif if(trim(local_heating_option) == 'from_file') then - call interpolator_init(heating_source_interp, trim(local_heating_file)//'.nc', lonb, latb, data_out_of_bounds=(/CONSTANT/)) + call interpolator_init(heating_source_interp, trim(local_heating_file)//'.nc', lonb, latb, data_out_of_bounds=(/ZERO/)) endif if(trim(equilibrium_t_option) == 'from_file') then call interpolator_init (temp_interp, trim(equilibrium_t_file)//'.nc', lonb, latb, data_out_of_bounds=(/CONSTANT/)) @@ -739,6 +738,8 @@ subroutine local_heating ( Time, is, js, lon, lat, ps, p_full, p_half, tdt ) real, dimension(size(lon,1),size(lon,2)) :: lon_factor real, dimension(size(lat,1),size(lat,2)) :: lat_factor real, dimension(size(p_half,1),size(p_half,2),size(p_half,3)) :: p_half2 +logical :: used + do i=1,size(p_half,3) p_half2(:,:,i)=p_half(:,:,size(p_half,3)-i+1) enddo @@ -746,7 +747,7 @@ subroutine local_heating ( Time, is, js, lon, lat, ps, p_full, p_half, tdt ) tdt(:,:,:)=0. if(trim(local_heating_option) == 'from_file') then - call interpolator( heating_source_interp, p_half, tdt, trim(local_heating_file)) + call interpolator( heating_source_interp, Time, p_half, tdt, trim(local_heating_file)) else if(trim(local_heating_option) == 'Isidoro') then do j=1,size(lon,2) do i=1,size(lon,1) @@ -766,6 +767,8 @@ subroutine local_heating ( Time, is, js, lon, lat, ps, p_full, p_half, tdt ) call error_mesg ('hs_forcing_nml','"'//trim(local_heating_option)//'" is not a valid value for local_heating_option',FATAL) endif +if (id_local_heating > 0) used = send_data ( id_local_heating, tdt, Time) + end subroutine local_heating !####################################################################### @@ -832,7 +835,7 @@ subroutine update_orbit(current_time, dec, orb_dist) call calc_ecc_anomaly(mean_anomaly, ecc, ecc_anomaly) true_anomaly = 2*atan(((1 + ecc)/(1 - ecc))**0.5 * tan(ecc_anomaly/2)) orb_dist = smaxis * (1 - ecc**2)/(1 + ecc*cos(true_anomaly)) - theta = 2*pi*current_time/(orbital_period*86400) + theta = 2*pi*modulo((current_time/(orbital_period*86400))-equinox_day, 1.0) dec = asin(sin(obliq*pi/180)*sin(theta)) end subroutine update_orbit diff --git a/src/atmos_param/socrates/interface/read_control.F90 b/src/atmos_param/socrates/interface/read_control.F90 index c877f9fbe..c8d3785e5 100644 --- a/src/atmos_param/socrates/interface/read_control.F90 +++ b/src/atmos_param/socrates/interface/read_control.F90 @@ -14,9 +14,10 @@ SUBROUTINE read_control(control, spectrum, do_clouds) USE rad_pcf USE def_control, ONLY: StrCtrl, allocate_control USE def_spectrum, ONLY: StrSpecData + USE socrates_config_mod, ONLY: l_planet_grey_surface, inc_h2o, inc_co2, inc_co, & inc_o3, inc_n2o, inc_ch4, inc_o2, inc_so2, inc_cfc11, & - inc_cfc12, inc_cfc113, inc_hcfc22, inc_hfc134a + inc_cfc12, inc_cfc113, inc_hcfc22, inc_hfc134a, inc_n2 IMPLICIT NONE @@ -54,6 +55,7 @@ SUBROUTINE read_control(control, spectrum, do_clouds) control%l_co = inc_co control%l_o3 = inc_o3 control%l_n2o = inc_n2o + control%l_n2 = inc_n2 control%l_ch4 = inc_ch4 control%l_o2 = inc_o2 control%l_so2 = inc_so2 @@ -70,6 +72,7 @@ SUBROUTINE read_control(control, spectrum, do_clouds) control%l_co = inc_co control%l_o3 = inc_o3 control%l_n2o = inc_n2o + control%l_n2 = inc_n2 control%l_ch4 = inc_ch4 control%l_so2 = inc_so2 control%l_cfc11 = inc_cfc11 diff --git a/src/atmos_param/socrates/interface/set_atm.F90 b/src/atmos_param/socrates/interface/set_atm.F90 index 7e98dfaa7..7c985b61d 100644 --- a/src/atmos_param/socrates/interface/set_atm.F90 +++ b/src/atmos_param/socrates/interface/set_atm.F90 @@ -17,9 +17,9 @@ SUBROUTINE set_atm(control, dimen, spectrum, atm, n_profile, n_layer, & USE def_dimen, ONLY: StrDim USE def_spectrum, ONLY: StrSpecData USE def_atm, ONLY: StrAtm, allocate_atm -USE socrates_config_mod, only: co_mix_ratio, n2o_mix_ratio, ch4_mix_ratio, o2_mix_ratio, so2_mix_ratio, cfc11_mix_ratio, cfc12_mix_ratio, cfc113_mix_ratio, hcfc22_mix_ratio, hfc134a_mix_ratio +USE socrates_config_mod, only: co_mix_ratio, n2o_mix_ratio, ch4_mix_ratio, o2_mix_ratio, so2_mix_ratio, cfc11_mix_ratio, cfc12_mix_ratio, cfc113_mix_ratio, hcfc22_mix_ratio, hfc134a_mix_ratio, n2_mix_ratio USE gas_list_pcf, ONLY: ip_h2o, ip_co2, ip_o3, ip_n2o, ip_ch4, ip_o2, ip_so2, & - ip_cfc11, ip_cfc12, ip_cfc113, ip_hcfc22, ip_hfc134a, ip_co + ip_cfc11, ip_cfc12, ip_cfc113, ip_hcfc22, ip_hfc134a, ip_co, ip_n2 use soc_constants_mod, only: i_def, r_def @@ -125,6 +125,13 @@ SUBROUTINE set_atm(control, dimen, spectrum, atm, n_profile, n_layer, & END DO END DO +CASE(ip_n2) + DO i=1, n_layer + DO l=1, n_profile + atm%gas_mix_ratio(l, i, i_gas) = n2_mix_ratio + END DO + END DO + CASE(ip_ch4) DO i=1, n_layer DO l=1, n_profile diff --git a/src/atmos_param/socrates/interface/socrates_config_mod.f90 b/src/atmos_param/socrates/interface/socrates_config_mod.f90 index 58ff3f64f..f7588cf0b 100644 --- a/src/atmos_param/socrates/interface/socrates_config_mod.f90 +++ b/src/atmos_param/socrates/interface/socrates_config_mod.f90 @@ -62,6 +62,7 @@ module socrates_config_mod ! Well mixed gas concentrations (kg / kg) #Don't know the source of these numbers. Need to check them. e.g. co mix ratio. REAL(r_def) :: co_mix_ratio = 0.0 REAL(r_def) :: n2o_mix_ratio = 4.945e-07 + REAL(r_def) :: n2_mix_ratio = 0.0 REAL(r_def) :: ch4_mix_ratio = 1.006e-06 REAL(r_def) :: o2_mix_ratio = 0.2314 REAL(r_def) :: so2_mix_ratio = 0.0 @@ -89,6 +90,9 @@ module socrates_config_mod logical :: inc_n2o = .FALSE. ! control%l_n2o = .TRUE. + logical :: inc_n2 = .FALSE. + ! control%l_n2 = .TRUE. + logical :: inc_ch4 = .FALSE. ! control%l_ch4 = .TRUE. @@ -123,11 +127,11 @@ module socrates_config_mod solday, do_rad_time_avg, equinox_day, & store_intermediate_rad, dt_rad_avg, dt_rad, & chunk_size, & - co_mix_ratio, n2o_mix_ratio, ch4_mix_ratio, & + co_mix_ratio, n2o_mix_ratio, ch4_mix_ratio, n2_mix_ratio, & o2_mix_ratio, so2_mix_ratio, cfc11_mix_ratio, & cfc12_mix_ratio, cfc113_mix_ratio, hcfc22_mix_ratio, & hfc134a_mix_ratio, & - inc_h2o, inc_co2, inc_co, inc_o3, inc_n2o, inc_ch4, inc_o2, & + inc_h2o, inc_co2, inc_co, inc_o3, inc_n2o, inc_n2, inc_ch4, inc_o2, & inc_so2, inc_cfc11, inc_cfc12, inc_cfc113, inc_hcfc22, inc_hfc134a, & use_pressure_interp_for_half_levels, & frierson_solar_rad, del_sol, del_sw, do_scm_ozone, scm_ozone diff --git a/src/atmos_param/socrates/interface/socrates_interface.F90 b/src/atmos_param/socrates/interface/socrates_interface.F90 index f970ba98c..29f6d6d8b 100644 --- a/src/atmos_param/socrates/interface/socrates_interface.F90 +++ b/src/atmos_param/socrates/interface/socrates_interface.F90 @@ -33,9 +33,9 @@ MODULE socrates_interface_mod USE def_control, ONLY: StrCtrl, allocate_control, deallocate_control USE def_spectrum USE constants_mod, only: grav, rdgas, rvgas, cp_air - USE fms_mod, only: stdlog, FATAL, WARNING, error_mesg - USE interpolator_mod, only: interpolate_type - USE soc_constants_mod + USE fms_mod, only: stdlog, FATAL, WARNING, error_mesg, check_nml_error + USE interpolator_mod, only: interpolate_type + USE soc_constants_mod IMPLICIT NONE @@ -67,6 +67,10 @@ MODULE socrates_interface_mod INTEGER :: n_soc_bands_lw, n_soc_bands_sw INTEGER :: n_soc_bands_lw_hires, n_soc_bands_sw_hires INTEGER :: id_soc_bins_lw, id_soc_bins_sw + INTEGER :: id_mars_solar_long, id_rrsun, id_true_anom, id_time_since_ae, id_dec, id_ang + + REAL(r_def) :: mars_solar_long_store, time_since_ae_store, dec_store, ang_out_store, true_anomaly_store, rrsun_store + INTEGER :: id_soc_tot_cloud_cover CHARACTER(len=10), PARAMETER :: soc_mod_name = 'socrates' REAL :: missing_value = -999 @@ -89,6 +93,7 @@ MODULE socrates_interface_mod REAL(r_def), allocatable, dimension(:) :: soc_bins_lw, soc_bins_sw LOGICAL :: do_clouds = .false. + CONTAINS SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb, delta_t_atmos, do_cloud_simple, do_cloud_spookie) @@ -106,8 +111,8 @@ SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb REAL, INTENT(in) , DIMENSION(:,:) :: lat REAL, INTENT(in) , DIMENSION(:,:) :: lonb, latb LOGICAL, INTENT(IN) :: do_cloud_simple, do_cloud_spookie - - integer :: io, stdlog_unit + + integer :: io, stdlog_unit, ierr integer :: res, time_step_seconds real :: day_in_s_check @@ -119,8 +124,11 @@ SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb #else if ( file_exist('input.nml') ) then nml_unit = open_namelist_file() - read (nml_unit, socrates_rad_nml, iostat=io) - call close_file(nml_unit) + do while (ierr/=0) + read (nml_unit, socrates_rad_nml, iostat=io) + ierr = check_nml_error (io, 'socrates_rad_nml') + enddo + call close_file(nml_unit) endif #endif stdlog_unit = stdlog() @@ -376,6 +384,23 @@ SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb 'socrates Co2', & 'mmr', missing_value=missing_value ) + id_mars_solar_long = register_diag_field ( soc_mod_name, 'mars_solar_long', & + Time, 'Martian solar longitude', 'deg') + + id_true_anom = register_diag_field ( soc_mod_name, 'true_anomaly', & + Time, 'True anomaly', 'deg') + + id_rrsun = register_diag_field ( soc_mod_name, 'rrsun', & + Time, 'inverse planet sun distance', 'none') + + id_time_since_ae = register_diag_field ( soc_mod_name, 'time_since_ae', & + Time, 'time since ae', 'none') + + id_dec = register_diag_field ( soc_mod_name, 'dec', & + Time, 'dec', 'none') + + id_ang = register_diag_field ( soc_mod_name, 'ang', & + Time, 'ang', 'none') id_soc_tot_cloud_cover = & register_diag_field ( soc_mod_name, 'soc_tot_cloud_cover', axes(1:2), Time, & 'socrates Total cloud cover', & @@ -511,6 +536,25 @@ SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb endif endif + if (id_mars_solar_long > 0) then + mars_solar_long_store = 0. + endif + if (id_time_since_ae > 0) then + time_since_ae_store = 0. + endif + if (id_dec > 0) then + dec_store = 0. + endif + if (id_ang > 0) then + ang_out_store = 0. + endif + if (id_true_anom > 0) then + true_anomaly_store = 0. + endif + if (id_rrsun > 0) then + rrsun_store = 0. + endif + endif ! Print Socrates init data from one processor @@ -907,7 +951,7 @@ subroutine run_socrates(Time, Time_diag, rad_lat, rad_lon, temp_in, q_in, t_surf logical :: soc_lw_mode, used integer :: seconds, days, year_in_s real :: r_seconds, r_days, r_total_seconds, frac_of_day, frac_of_year, gmt, time_since_ae, rrsun, & - dt_rad_radians, day_in_s, r_solday, r_dt_rad_avg + dt_rad_radians, day_in_s, r_solday, r_dt_rad_avg, mars_solar_long, dec, ang_out, true_anomaly real, dimension(size(temp_in,1), size(temp_in,2)) :: coszen, fracsun, surf_lw_net, olr, toa_sw, & p2, toa_sw_down, surf_sw_down, & olr_clr, toa_sw_clr, toa_sw_up, toa_sw_up_clr, & @@ -1017,6 +1061,26 @@ subroutine run_socrates(Time, Time_diag, rad_lat, rad_lon, temp_in, q_in, t_surf if (id_soc_spectral_olr > 0) then outputted_soc_spectral_olr = spectral_olr_store endif + + if (id_mars_solar_long > 0) then + mars_solar_long = mars_solar_long_store + endif + if (id_time_since_ae > 0) then + time_since_ae = time_since_ae_store + endif + if (id_dec > 0) then + dec = dec_store + endif + if (id_ang > 0) then + ang_out= ang_out_store + endif + if (id_true_anom > 0) then + true_anomaly = true_anomaly_store + endif + if (id_rrsun > 0) then + rrsun = rrsun_store + endif + else output_heating_rate_sw = 0. output_heating_rate_lw = 0. @@ -1043,6 +1107,12 @@ subroutine run_socrates(Time, Time_diag, rad_lat, rad_lon, temp_in, q_in, t_surf ozone_in = 0. co2_in = 0. outputted_soc_spectral_olr = 0. + mars_solar_long = 0. + time_since_ae = 0. + dec = 0. + ang_out = 0. + true_anomaly = 0. + rrsun = 0. endif temp_tend(:,:,:) = temp_tend(:,:,:) + real(output_heating_rate_sw)+real(output_heating_rate_lw) @@ -1129,8 +1199,26 @@ subroutine run_socrates(Time, Time_diag, rad_lat, rad_lon, temp_in, q_in, t_surf endif if(id_soc_spectral_olr > 0) then used = send_data ( id_soc_spectral_olr, outputted_soc_spectral_olr, Time_diag) - endif - ! Diagnostics sent + endif + if (id_mars_solar_long > 0) then + used = send_data ( id_mars_solar_long, mars_solar_long, Time_diag) + endif + if (id_time_since_ae > 0) then + used = send_data ( id_time_since_ae, time_since_ae, Time_diag) + endif + if (id_dec > 0) then + used = send_data ( id_dec, dec, Time_diag) + endif + if (id_ang > 0) then + used = send_data ( id_ang, ang_out, Time_diag) + endif + if (id_true_anom > 0) then + used = send_data ( id_true_anom, true_anomaly, Time_diag) + endif + if (id_rrsun > 0) then + used = send_data ( id_rrsun, rrsun, Time_diag) + endif + ! Diagnostics sent return !not time yet @@ -1179,12 +1267,16 @@ subroutine run_socrates(Time, Time_diag, rad_lat, rad_lon, temp_in, q_in, t_surf if(do_rad_time_avg) then r_dt_rad_avg=real(dt_rad_avg) dt_rad_radians = (r_dt_rad_avg/day_in_s)*2.0*pi - call diurnal_solar(rad_lat, rad_lon, gmt, time_since_ae, coszen, fracsun, rrsun, dt_rad_radians) + call diurnal_solar(rad_lat, rad_lon, gmt, time_since_ae, coszen, fracsun, rrsun, dt_rad_radians, true_anom=true_anomaly, dec_out=dec, ang_out=ang_out) else ! Seasonal Cycle: Use astronomical parameters to calculate insolation - call diurnal_solar(rad_lat, rad_lon, gmt, time_since_ae, coszen, fracsun, rrsun) + call diurnal_solar(rad_lat, rad_lon, gmt, time_since_ae, coszen, fracsun, rrsun, true_anom=true_anomaly, dec_out=dec, ang_out=ang_out) end if + if (id_mars_solar_long > 0) then + mars_solar_long = modulo((180./pi)*(true_anomaly-1.905637),360.) + endif + endif ozone_in = 0.0 @@ -1417,6 +1509,25 @@ subroutine run_socrates(Time, Time_diag, rad_lat, rad_lon, temp_in, q_in, t_surf spectral_olr_store = outputted_soc_spectral_olr endif + if (id_mars_solar_long > 0) then + mars_solar_long_store = mars_solar_long + endif + if (id_time_since_ae > 0) then + time_since_ae_store = time_since_ae + endif + if (id_dec > 0) then + dec_store = dec + endif + if (id_ang > 0) then + ang_out_store = ang_out + endif + if (id_true_anom > 0) then + true_anomaly_store = true_anomaly + endif + if (id_rrsun > 0) then + rrsun_store = rrsun + endif + endif ! Send diagnostics @@ -1502,6 +1613,25 @@ subroutine run_socrates(Time, Time_diag, rad_lat, rad_lon, temp_in, q_in, t_surf if(id_soc_tot_cloud_cover > 0) then used = send_data ( id_soc_tot_cloud_cover, tot_cloud_cover*1e2, Time_diag) endif + if (id_mars_solar_long > 0) then + used = send_data ( id_mars_solar_long, mars_solar_long, Time_diag) + endif + if (id_time_since_ae > 0) then + used = send_data ( id_time_since_ae, time_since_ae, Time_diag) + endif + if (id_dec > 0) then + used = send_data ( id_dec, dec, Time_diag) + endif + if (id_ang > 0) then + used = send_data ( id_ang, ang_out, Time_diag) + endif + if (id_true_anom > 0) then + used = send_data ( id_true_anom, true_anomaly, Time_diag) + endif + if (id_rrsun > 0) then + used = send_data ( id_rrsun, rrsun, Time_diag) + endif + ! Diagnostics sent end subroutine run_socrates diff --git a/src/atmos_param/two_stream_gray_rad/two_stream_gray_rad.F90 b/src/atmos_param/two_stream_gray_rad/two_stream_gray_rad.F90 index 9849c30c0..0c0ee340a 100644 --- a/src/atmos_param/two_stream_gray_rad/two_stream_gray_rad.F90 +++ b/src/atmos_param/two_stream_gray_rad/two_stream_gray_rad.F90 @@ -89,7 +89,7 @@ module two_stream_gray_rad_mod character(len=32) :: rad_scheme = 'frierson' integer, parameter :: B_GEEN = 1, B_FRIERSON = 2, & - B_BYRNE = 3, B_SCHNEIDER_LIU=4 + B_BYRNE = 3, B_SCHNEIDER_LIU=4, B_SCHNEIDER_TITAN=5 integer, private :: sw_scheme = B_FRIERSON integer, private :: lw_scheme = B_FRIERSON @@ -113,6 +113,11 @@ module two_stream_gray_rad_mod real :: diabatic_acce = 1.0 real,save :: gp_albedo, Ga_asym, g_asym +!constants for Schneider Titan radiation +real :: titan_asymmetry_factor = 0.65 +real :: titan_single_albedo = 0.95 +real :: titan_albedo_surface = 0.3 + ! parameters for Byrne and OGorman radiation scheme real :: bog_a = 0.8678 real :: bog_b = 1997.9 @@ -130,7 +135,8 @@ module two_stream_gray_rad_mod real, allocatable, dimension(:,:,:) :: b_win, sw_dtrans real, allocatable, dimension(:,:) :: sw_wv, del_sol_tau, sw_tau_k, lw_del_tau, lw_del_tau_win -real, save :: pi, deg_to_rad , rad_to_deg +real, save :: pi, deg_to_rad , rad_to_deg, Ga, albedo_inf, albedo_mod, intensity_inflation + !extras for reading in co2 concentration logical :: do_read_co2=.false. @@ -138,6 +144,12 @@ module two_stream_gray_rad_mod character(len=256) :: co2_file='co2' ! file name of co2 file to read character(len=256) :: co2_variable_name='co2' ! file name of co2 file to read +!extras for reading in huygens probe sw for Titan +logical :: overwrite_net_sw_flux_with_huygens_data=.false. +type(interpolate_type),save :: huygens_sw_interp ! use external file for huygens sw data +character(len=256) :: huygens_sw_file='frac_sw_huygens' ! file name of huygens sw file to read +character(len=256) :: huygens_sw_variable_name='frac_sw' ! file name of huygens sw file to read +real, dimension(61) :: huygens_sw_profile_values namelist/two_stream_gray_rad_nml/ solar_constant, del_sol, & ir_tau_eq, ir_tau_pole, odp, atm_abs, sw_diff, & @@ -145,17 +157,22 @@ module two_stream_gray_rad_mod solar_exponent, do_seasonal, & ir_tau_co2_win, ir_tau_wv_win1, ir_tau_wv_win2, & ir_tau_co2, ir_tau_wv1, ir_tau_wv2, & - window, carbon_conc, rad_scheme, & + window, carbon_conc, rad_scheme, & do_read_co2, co2_file, co2_variable_name, solday, equinox_day, bog_a, bog_b, bog_mu, & use_time_average_coszen, dt_rad_avg,& - diabatic_acce !Schneider Liu values + diabatic_acce, & !Schneider Liu values + titan_asymmetry_factor, titan_single_albedo, & + titan_albedo_surface, & + overwrite_net_sw_flux_with_huygens_data, huygens_sw_file, & + huygens_sw_variable_name, huygens_sw_profile_values !================================================================================== !-------------------- diagnostics fields ------------------------------- integer :: id_olr, id_swdn_sfc, id_swdn_toa, id_net_lw_surf, id_lwdn_sfc, id_lwup_sfc, & id_tdt_rad, id_tdt_solar, id_flux_rad, id_flux_lw, id_flux_sw, id_coszen, id_fracsun, & - id_lw_dtrans, id_lw_dtrans_win, id_sw_dtrans, id_co2 + id_lw_dtrans, id_lw_dtrans_win, id_sw_dtrans, id_co2, id_mars_solar_long, id_rrsun, id_true_anom, & + id_time_since_ae, id_dec, id_ang, id_tau_lw, id_tau_sw character(len=10), parameter :: mod_name = 'two_stream' @@ -224,6 +241,9 @@ subroutine two_stream_gray_rad_init(is, ie, js, je, num_levels, axes, Time, lonb else if(uppercase(trim(rad_scheme)) == 'SCHNEIDER') then lw_scheme = B_SCHNEIDER_LIU call error_mesg('two_stream_gray_rad','Using Schneider & Liu (2009) radiation scheme for GIANT PLANETS.', NOTE) +else if(uppercase(trim(rad_scheme)) == 'SCHNEIDER_TITAN') then + lw_scheme = B_SCHNEIDER_TITAN + call error_mesg('two_stream_gray_rad','Using Schneider et al (2012) radiation scheme for TITAN.', NOTE) else call error_mesg('two_stream_gray_rad','"'//trim(rad_scheme)//'"'//' is not a valid radiation scheme.', FATAL) endif @@ -237,10 +257,34 @@ subroutine two_stream_gray_rad_init(is, ie, js, je, num_levels, axes, Time, lonb Ga_asym = 2.*sqrt( (1. - single_albedo) * (1. - g_asym*single_albedo) ); endif +if(lw_scheme == B_SCHNEIDER_TITAN) then + ! Modified by LJJ + ! albedo of a semi-inifinite scattering atmosphere + albedo_inf = ( sqrt(1. - titan_asymmetry_factor*titan_single_albedo) - sqrt(1. - titan_single_albedo) ) & + / ( sqrt(1. - titan_asymmetry_factor*titan_single_albedo) + sqrt(1. - titan_single_albedo) ); + + ! End modification + + !auxiliary quantities in computation of solar flux + albedo_mod = (-titan_albedo_surface + albedo_inf) / (1. - titan_albedo_surface*albedo_inf) + !N.B. switched the sign of the numerator in albedo mod compared to tapios github as tapio version had albedo_mod<0. New one has albedo_mod ~ 0.2, as in Schneider 2012 supplemental material. + + ! rescaling factor for optical depth due to scattering + Ga = 2.*sqrt( (1. - titan_single_albedo) * (1. - titan_asymmetry_factor*titan_single_albedo) ); + + intensity_inflation = 1. / (1. + albedo_mod * albedo_inf * exp(-2*Ga*atm_abs)) +endif + if ((lw_scheme == B_BYRNE).or.(lw_scheme == B_GEEN)) then if (pstd_mks/=pstd_mks_earth) call error_mesg('two_stream_gray_rad','Pstd_mks and pstd_mks_earth are not the same in the this run, but lw scheme will use pstd_mks_earth because abs coeffs in Byrne and Geen schemes are non-dimensionalized by Earth surface pressure.', NOTE) endif +if(sw_scheme == B_SCHNEIDER_TITAN) then + if (overwrite_net_sw_flux_with_huygens_data) then + call interpolator_init (huygens_sw_interp, trim(huygens_sw_file)//'.nc', lonb, latb, data_out_of_bounds=(/ZERO/)) + endif +endif + initialized = .true. @@ -348,6 +392,16 @@ subroutine two_stream_gray_rad_init(is, ie, js, je, num_levels, axes, Time, lonb 'Net shortwave radiative flux (positive up)', & 'W/m^2', missing_value=missing_value ) + id_tau_lw = & + register_diag_field ( mod_name, 'tau_lw', axes(half), Time, & + 'Long-wave optical depth', & + 'None', missing_value=missing_value ) + + id_tau_sw = & + register_diag_field ( mod_name, 'tau_sw', axes(half), Time, & + 'Short-wave optical depth', & + 'None', missing_value=missing_value ) + id_coszen = & register_diag_field ( mod_name, 'coszen', axes(1:2), Time, & 'cosine of zenith angle', & @@ -378,6 +432,25 @@ subroutine two_stream_gray_rad_init(is, ie, js, je, num_levels, axes, Time, lonb register_diag_field ( mod_name, 'lw_dtrans', axes(1:3), Time, & 'LW transmission (non window)', & 'none', missing_value=missing_value ) + + id_mars_solar_long = register_diag_field ( mod_name, 'mars_solar_long', & + Time, 'Martian solar longitude', 'deg') + + id_true_anom = register_diag_field ( mod_name, 'true_anomaly', & + Time, 'True anomaly', 'deg') + + id_rrsun = register_diag_field ( mod_name, 'rrsun', & + Time, 'inverse planet sun distance', 'none') + + id_time_since_ae = register_diag_field ( mod_name, 'time_since_ae', & + Time, 'time since ae', 'none') + + id_dec = register_diag_field ( mod_name, 'dec', & + Time, 'dec', 'none') + + id_ang = register_diag_field ( mod_name, 'ang', & + Time, 'ang', 'none') + return end subroutine two_stream_gray_rad_init @@ -398,7 +471,7 @@ subroutine two_stream_gray_rad_down (is, js, Time_diag, lat, lon, p_half, t, integer :: i, j, k, n, dyofyr integer :: seconds, year_in_s, days -real :: r_seconds, frac_of_day, frac_of_year, gmt, time_since_ae, rrsun, day_in_s, r_solday, r_total_seconds, r_days, r_dt_rad_avg, dt_rad_radians +real :: r_seconds, frac_of_day, frac_of_year, gmt, time_since_ae, rrsun, day_in_s, r_solday, r_total_seconds, r_days, r_dt_rad_avg, dt_rad_radians, true_anomaly, dec, ang_out logical :: used @@ -406,10 +479,6 @@ subroutine two_stream_gray_rad_down (is, js, Time_diag, lat, lon, p_half, t, n = size(t,3) - - -! albedo(:,:) = albedo_value !s albedo now set in mixed_layer_init. - ! ================================================================================= ! SHORTWAVE RADIATION @@ -418,34 +487,47 @@ subroutine two_stream_gray_rad_down (is, js, Time_diag, lat, lon, p_half, t, ! Seasonal Cycle: Use astronomical parameters to calculate insolation call get_time(Time_diag, seconds, days) call get_time(length_of_year(), year_in_s) - r_seconds = real(seconds) day_in_s = length_of_day() - frac_of_day = r_seconds / day_in_s + r_seconds = real(seconds) + r_days=real(days) + r_total_seconds=r_seconds+(r_days*86400.) + + frac_of_day = r_total_seconds / day_in_s if(solday .ge. 0) then r_solday=real(solday) frac_of_year = (r_solday*day_in_s) / year_in_s else - r_days=real(days) - r_total_seconds=r_seconds+(r_days*day_in_s) frac_of_year = r_total_seconds / year_in_s endif gmt = abs(mod(frac_of_day, 1.0)) * 2.0 * pi - + time_since_ae = modulo(frac_of_year-equinox_day, 1.0) * 2.0 * pi if(use_time_average_coszen) then r_dt_rad_avg=real(dt_rad_avg) dt_rad_radians = (r_dt_rad_avg/day_in_s)*2.0*pi - - call diurnal_solar(lat, lon, gmt, time_since_ae, coszen, fracsun, rrsun, dt_rad_radians) + call diurnal_solar(lat, lon, gmt, time_since_ae, coszen, fracsun, rrsun, dt_rad_radians, true_anom=true_anomaly, dec_out=dec, ang_out=ang_out) else - call diurnal_solar(lat, lon, gmt, time_since_ae, coszen, fracsun, rrsun) + call diurnal_solar(lat, lon, gmt, time_since_ae, coszen, fracsun, rrsun, true_anom=true_anomaly, dec_out=dec, ang_out=ang_out) end if - insolation = solar_constant * coszen + insolation = solar_constant * coszen * rrsun + + if (id_mars_solar_long > 0) used = send_data ( id_mars_solar_long, modulo((180./pi)*(true_anomaly-1.905637),360.), Time_diag) + + + if (id_time_since_ae > 0) used = send_data ( id_time_since_ae, time_since_ae, Time_diag) + if (id_dec > 0) used = send_data ( id_dec, dec, Time_diag) + if (id_ang > 0) used = send_data ( id_ang, ang_out, Time_diag) + + + if (id_true_anom > 0) used = send_data ( id_true_anom, true_anomaly, Time_diag) + + if (id_rrsun > 0) used = send_data ( id_rrsun, rrsun, Time_diag) + else if (sw_scheme==B_SCHNEIDER_LIU) then insolation = (solar_constant/pi)*cos(lat) @@ -505,6 +587,21 @@ subroutine two_stream_gray_rad_down (is, js, Time_diag, lat, lon, p_half, t, sw_down(:,:,k) = insolation(:,:) * (1.0-gp_albedo)* exp(- Ga_asym * sw_tau(:,:,k)) end do +case(B_SCHNEIDER_TITAN) + + sw_tau_0 = (1.0 - sw_diff*sin(lat)**2)*atm_abs + + do k = 1, n+1 + sw_tau(:,:,k) = sw_tau_0 * (p_half(:,:,k)/pstd_mks)**solar_exponent + end do + + ! compute downward shortwave flux + do k = 1, n+1 + sw_down(:,:,k) = insolation(:,:) * intensity_inflation & + * (albedo_inf * albedo_mod * exp(Ga * (sw_tau(:,:,k) - 2.*sw_tau_0)) & + + exp(-Ga * sw_tau(:,:,k))) + end do + case default call error_mesg('two_stream_gray_rad','invalid radiation scheme',FATAL) @@ -571,7 +668,7 @@ subroutine two_stream_gray_rad_down (is, js, Time_diag, lat, lon, p_half, t, lw_down(:,:,k+1) = lw_down(:,:,k)*lw_dtrans(:,:,k) + b(:,:,k)*(1. - lw_dtrans(:,:,k)) end do -case(B_FRIERSON) +case(B_FRIERSON, B_SCHNEIDER_TITAN) ! longwave optical thickness function of latitude and pressure lw_tau_0 = ir_tau_eq + (ir_tau_pole - ir_tau_eq)*sin(lat)**2 lw_tau_0 = lw_tau_0 * odp ! scale by optical depth parameter - default 1 @@ -668,7 +765,7 @@ subroutine two_stream_gray_rad_up (is, js, Time_diag, lat, p_half, t_surf, t, td real, intent(inout), dimension(:,:,:) :: tdt -integer :: i, j, k, n +integer :: i, j, k, n, num_lat, num_lon logical :: used @@ -687,7 +784,7 @@ subroutine two_stream_gray_rad_up (is, js, Time_diag, lat, p_half, t_surf, t, td end do lw_up = lw_up + lw_up_win -case(B_FRIERSON, B_BYRNE) +case(B_FRIERSON, B_BYRNE, B_SCHNEIDER_TITAN) ! compute upward longwave flux by integrating upward lw_up(:,:,n+1) = b_surf do k = n, 1, -1 @@ -708,13 +805,47 @@ subroutine two_stream_gray_rad_up (is, js, Time_diag, lat, p_half, t_surf, t, td end select ! compute upward shortwave flux (here taken to be constant) -do k = 1, n+1 - sw_up(:,:,k) = albedo(:,:) * sw_down(:,:,n+1) -end do +select case(sw_scheme) + case(B_SCHNEIDER_TITAN) + do k = 1,n+1 + sw_up(:,:,k) = insolation(:,:) * intensity_inflation & + * (albedo_mod * exp(Ga * (sw_tau(:,:,k) - 2.*sw_tau_0)) & + + albedo_inf * exp(-Ga * sw_tau(:,:,k))) + end do + case default + do k = 1, n+1 + sw_up(:,:,k) = albedo(:,:) * sw_down(:,:,n+1) + end do +end select ! net fluxes (positive up) lw_flux = lw_up - lw_down -sw_flux = sw_up - sw_down + +select case(sw_scheme) + case(B_SCHNEIDER_TITAN) + if (overwrite_net_sw_flux_with_huygens_data) then + ! write(6,*) 'shape phalf', shape(p_half) + ! write(6,*) 'shape swflux', size(sw_flux, 1) + ! call interpolator( huygens_sw_interp, p_half, sw_flux, trim(huygens_sw_variable_name)) + + num_lat = size(sw_flux,2) + num_lon = size(sw_flux,1) + + do i = 1, num_lon + do j = 1, num_lat + do k = 1, n+1 + sw_flux(i,j, k) = -1.*huygens_sw_profile_values(k) * sw_down(i, j, 1) ! Huygens data is net sw flux as a fraction of TOA incoming SW + end do + end do + end do + else + sw_flux = sw_up - sw_down + endif + case default + sw_flux = sw_up - sw_down +end select + + rad_flux = lw_flux + sw_flux do k = 1, n @@ -771,6 +902,14 @@ subroutine two_stream_gray_rad_up (is, js, Time_diag, lat, p_half, t_surf, t, td if ( id_sw_dtrans > 0 ) then used = send_data ( id_sw_dtrans, sw_dtrans, Time_diag) endif +!---------optical depths (at half levels) --------- +if ( id_tau_lw > 0 ) then + used = send_data ( id_tau_lw, lw_tau, Time_diag) +endif +if ( id_tau_sw > 0 ) then + used = send_data ( id_tau_sw, sw_tau, Time_diag) +endif + return end subroutine two_stream_gray_rad_up @@ -800,6 +939,10 @@ subroutine two_stream_gray_rad_end deallocate (b_surf_gp) endif +if(sw_scheme.eq.B_SCHNEIDER_TITAN .and. overwrite_net_sw_flux_with_huygens_data) then + call interpolator_end(huygens_sw_interp) +endif + if(do_read_co2)call interpolator_end(co2_interp) end subroutine two_stream_gray_rad_end diff --git a/src/atmos_solo/atmos_model.F90 b/src/atmos_solo/atmos_model.F90 index fe1388ce4..56d4e6a56 100644 --- a/src/atmos_solo/atmos_model.F90 +++ b/src/atmos_solo/atmos_model.F90 @@ -399,7 +399,7 @@ subroutine atmos_model_end if ( mpp_pe() == mpp_root_pe() ) then call mpp_open (unit, 'RESTART/atmos_model.res', form=MPP_ASCII, action=MPP_OVERWR, & access=MPP_SEQUENTIAL, threading=MPP_SINGLE, nohdrs=.true. ) - write (unit,'(6i6,8x,a)') date, & + write (unit,'(6i10,8x,a)') date, & 'Current model time: year, month, day, hour, minute, second' write (unit,'(i6,8x,a)') calendar_type, & '(Calendar: no_calendar=0, thirty_day_months=1, julian=2, gregorian=3, noleap=4)' diff --git a/src/atmos_spectral/driver/solo/idealized_moist_phys.F90 b/src/atmos_spectral/driver/solo/idealized_moist_phys.F90 index 81963dbc6..7d568c57f 100644 --- a/src/atmos_spectral/driver/solo/idealized_moist_phys.F90 +++ b/src/atmos_spectral/driver/solo/idealized_moist_phys.F90 @@ -10,7 +10,7 @@ module idealized_moist_phys_mod FATAL, WARNING, read_data, field_size, uppercase, mpp_pe, check_nml_error ! cp_air needed for rrtmg and pstd_mks needed for pref calculation -use constants_mod, only: grav, rdgas, rvgas, cp_air, PSTD_MKS, dens_h2o +use constants_mod, only: grav, rdgas, rvgas, cp_air, PSTD_MKS, dens_h2o, dens_vapor use time_manager_mod, only: time_type, get_time, operator( + ) @@ -43,12 +43,12 @@ module idealized_moist_phys_mod use spec_mpp_mod, only: get_grid_domain, grid_domain #else use transforms_mod, only: get_grid_domain, grid_domain -use spectral_dynamics_mod, only: get_axis_id, get_num_levels, get_surf_geopotential +use spectral_dynamics_mod, only: get_axis_id, get_num_levels, get_surf_geopotential, diffuse_surf_water #endif use surface_flux_mod, only: surface_flux, gp_surface_flux -use sat_vapor_pres_mod, only: lookup_es ! needed for relative humdity to be calculated in a consistent way. +use sat_vapor_pres_mod, only: lookup_es, escomp !s Have added this to allow relative humdity to be calculated in a consistent way. use damping_driver_mod, only: damping_driver, damping_driver_init, damping_driver_end ! MiMA uses damping @@ -62,6 +62,8 @@ module idealized_moist_phys_mod use rayleigh_bottom_drag_mod, only: rayleigh_bottom_drag_init, compute_rayleigh_bottom_drag +use hs_forcing_mod, only: hs_forcing_init, local_heating, hs_forcing_end + #ifdef RRTM_NO_COMPILE ! RRTM_NO_COMPILE not included #else @@ -116,6 +118,7 @@ module idealized_moist_phys_mod logical :: do_bm = .false. logical :: do_ras = .false. +logical :: do_lscale_cond = .true. ! Cloud options logical :: do_cloud_simple = .false. ! SimCloud cloud scheme logical :: do_cloud_spookie = .false. ! SPOOKIE protocol cloud scheme @@ -152,8 +155,14 @@ module idealized_moist_phys_mod real :: max_bucket_depth_land = 0.15 ! default from Manabe 1969 real :: robert_bucket = 0.04 ! default robert coefficient for bucket depth LJJ real :: raw_bucket = 0.53 ! default raw coefficient for bucket depth LJJ +real :: damping_coeff_bucket = 0. ! default damping coefficient for diffusing of surface water - default is no diffusion. [degrees/year] +logical :: finite_bucket_depth_over_land = .true. !When using bucket model do we want finite bucket depth over land? Default is true. Some applications where bucket depth is not well known or testable (e.g. for Titan) we may want this option as false. ! end Add bucket +!s Adding localised heating option from Held-Suarez +logical :: do_local_heating = .false. +!s end Adding localised heating option from Held-Suarez + namelist / idealized_moist_phys_nml / turb, lwet_convection, do_bm, do_ras, roughness_heat, & do_cloud_simple, do_cloud_spookie, & two_stream_gray, do_rrtm_radiation, do_damping,& @@ -164,12 +173,14 @@ module idealized_moist_phys_mod gp_surface, convection_scheme, & bucket, init_bucket_depth, init_bucket_depth_land, & max_bucket_depth_land, robert_bucket, raw_bucket, & - do_socrates_radiation, do_lcl_diffusivity_depth + do_lscale_cond, do_socrates_radiation, do_lcl_diffusivity_depth, damping_coeff_bucket, & + finite_bucket_depth_over_land, & + do_local_heating integer, parameter :: num_time_levels = 2 ! Add bucket - number of time levels added to allow timestepping in this module real, allocatable, dimension(:,:,:) :: bucket_depth -real, allocatable, dimension(:,: ) :: dt_bucket, filt +real, allocatable, dimension(:,: ) :: dt_bucket, filt, bucket_diffusion real, allocatable, dimension(:,:) :: & z_surf, & ! surface height @@ -205,6 +216,7 @@ module idealized_moist_phys_mod dedq_atm, & ! d(latent heat flux)/d(atmospheric mixing rat.) dtaudv_atm, & ! d(stress component)/d(atmos wind) dtaudu_atm, & ! d(stress component)/d(atmos wind) + q_surf_out, & ! Saturated specific humidity of mixed-layer fracland, & ! fraction of land in gridbox rough, & ! roughness for vert_turb_driver albedo, & ! albedo now defined in mixed_layer_init @@ -285,6 +297,7 @@ module idealized_moist_phys_mod id_bucket_depth_conv, & ! bucket depth variation induced by convection id_bucket_depth_cond, & ! bucket depth variation induced by condensation id_bucket_depth_lh, & ! bucket depth variation induced by LH + id_bucket_diffusion, & ! diffused surface water depth id_rh, & ! Relative humidity id_diss_heat_ray,& ! Heat dissipated by rayleigh bottom drag if gp_surface=.True. id_z_tg, & ! Relative humidity @@ -296,7 +309,16 @@ module idealized_moist_phys_mod id_u_10m, & ! used for 10m winds and 2m temp id_v_10m, & ! used for 10m winds and 2m temp id_q_2m, & ! used for 2m specific humidity - id_rh_2m ! used for 2m relative humidity + id_rh_2m, & ! used for 2m relative humidity + id_cd_t, & + id_cd_q, & + id_cd_m, & + id_w_atm, & + id_drag_m, & + id_drag_t, & + id_drag_q, & + id_rho_drag, & + id_q_surf0, id_flux_q_surf_part, id_flux_q_atm_part, id_flux_t_surf_part, id_flux_t_atm_part, id_e_sat integer, allocatable, dimension(:,:) :: convflag ! indicates which qe convection subroutines are used real, allocatable, dimension(:,:) :: rad_lat, rad_lon @@ -439,7 +461,7 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l end if else call error_mesg('idealized_moist_phys','"'//trim(convection_scheme)//'"'//' is not a valid convection scheme.'// & - ' Choices are NONE, SIMPLE_BETTS, FULL_BETTS_MILLER, RAS, DRY', FATAL) + ' Choices are NONE, SIMPLE_BETTS_MILLER, FULL_BETTS_MILLER, RAS, DRY', FATAL) endif if(lwet_convection .and. do_bm) & @@ -473,6 +495,7 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l allocate(depth_change_lh(is:ie, js:je)) allocate(depth_change_cond(is:ie, js:je)) allocate(depth_change_conv(is:ie, js:je)) +allocate(bucket_diffusion(is:ie, js:je)) allocate(z_surf (is:ie, js:je)) allocate(t_surf (is:ie, js:je)) allocate(q_surf (is:ie, js:je)); q_surf = 0.0 @@ -503,6 +526,7 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l allocate(dedq_atm (is:ie, js:je)) allocate(dtaudv_atm (is:ie, js:je)) allocate(dtaudu_atm (is:ie, js:je)) +allocate(q_surf_out (is:ie, js:je)) allocate(ex_del_m (is:ie, js:je)) allocate(ex_del_h (is:ie, js:je)) allocate(ex_del_q (is:ie, js:je)) @@ -593,8 +617,10 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l where(land_ones > 0.) land = .true. elseif(trim(land_option) .eq. 'zsurf')then - ! wherever zsurf is greater than some threshold height then make land = .true. - where ( z_surf > 10. ) land = .true. + ! wherever zsurf is greater than some threshold height then make land = .true. + where ( z_surf > 10. ) land = .true. +elseif(trim(land_option) .eq. 'all_land')then + land = .true. endif !option to alter surface roughness length over land @@ -680,6 +706,29 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l id_flux_v = register_diag_field(mod_name, 'flux_v', & axes(1:2), Time, 'Meridional momentum flux', 'Pa') +id_cd_t = register_diag_field(mod_name, 'cd_t', & + axes(1:2), Time, 'Temperature coeff for surface fluxes','None') +id_cd_m = register_diag_field(mod_name, 'cd_m', & + axes(1:2), Time, 'Momentum coeff for surface fluxes','None') +id_cd_q = register_diag_field(mod_name, 'cd_q', & + axes(1:2), Time, 'Sphum coeff for surface fluxes','None') +id_w_atm = register_diag_field(mod_name, 'w_atm', & + axes(1:2), Time, 'Speed component of surface drag','m/s') + +id_drag_m = register_diag_field(mod_name, 'drag_m', axes(1:2), Time, 'momentum drag_m from surface_flux', '.') +id_drag_t = register_diag_field(mod_name, 'drag_t', axes(1:2), Time, 't drag_t from surface_flux', '.') +id_drag_q = register_diag_field(mod_name, 'drag_q', axes(1:2), Time, 'q drag_q from surface_flux', '.') +id_rho_drag = register_diag_field(mod_name, 'rho_drag', axes(1:2), Time, 'rho_drag from sensible heat', '.') +id_q_surf0 = register_diag_field(mod_name, 'q_surf0', axes(1:2), Time, 'q_surf0 from surface flux', '.') +id_flux_q_surf_part = register_diag_field(mod_name, 'flux_q_surf_part', axes(1:2), Time, 'flux_q_surf_part', '.') +id_flux_q_atm_part = register_diag_field(mod_name, 'flux_q_atm_part', axes(1:2), Time, 'flux_q_atm_part', '.') +id_flux_u = register_diag_field(mod_name, 'flux_u', axes(1:2), Time, 'flux zonal momentum', '.') +id_flux_v = register_diag_field(mod_name, 'flux_v', axes(1:2), Time, 'flux meridional momentum', '.') +id_flux_t_surf_part = register_diag_field(mod_name, 'flux_t_surf_part', axes(1:2), Time, 'flux_t_surf_part', '.') +id_flux_t_atm_part = register_diag_field(mod_name, 'flux_t_atm_part', axes(1:2), Time, 'flux_t_atm_part', '.') + +id_e_sat = register_diag_field(mod_name, 'e_sat', axes(1:2), Time, 'e_sat', '.') + if(bucket) then id_bucket_depth = register_diag_field(mod_name, 'bucket_depth', & axes(1:2), Time, 'Depth of surface reservoir', 'm') @@ -689,6 +738,8 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l axes(1:2), Time, 'Tendency of bucket depth induced by Condensation', 'm/s') id_bucket_depth_lh = register_diag_field(mod_name, 'bucket_depth_lh', & axes(1:2), Time, 'Tendency of bucket depth induced by LH', 'm/s') + id_bucket_diffusion = register_diag_field(mod_name, 'bucket_diffusion', & + axes(1:2), Time, 'Diffusion rate of bucket','m/s') endif id_temp_2m = register_diag_field(mod_name, 'temp_2m', & @@ -767,6 +818,10 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l axes(1:2), Time, 'Rain from convection','kg/m/m/s') !endif +if (r_conv_scheme .eq. DRY_CONV .and. do_lscale_cond .eqv. .true.) then + call error_mesg('idealized_moist_phys','do_lscale_cond is .true. but r_conv_scheme is dry. These options may not be consistent.', WARNING) +endif + if(two_stream_gray) call two_stream_gray_rad_init(is, ie, js, je, num_levels, get_axis_id(), Time, rad_lonb_2d, rad_latb_2d, dt_real) #ifdef RRTM_NO_COMPILE @@ -814,6 +869,11 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l id_rh = register_diag_field ( mod_name, 'rh', & axes(1:3), Time, 'relative humidity', 'percent') +if (do_local_heating) then + call hs_forcing_init(get_axis_id(), Time, rad_lonb_2d, rad_latb_2d, rad_lat_2d) +endif + + end subroutine idealized_moist_phys_init !================================================================================================================================= subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, psg, wg_full, tg, grid_tracers, & @@ -828,7 +888,8 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, ps real, dimension(:,:,:,:), intent(inout) :: dt_tracers real :: delta_t -real, dimension(size(ug,1), size(ug,2), size(ug,3)) :: tg_tmp, qg_tmp, RH,tg_interp, mc, dt_ug_conv, dt_vg_conv +real, dimension(size(ug,1), size(ug,2), size(ug,3)) :: tg_tmp, qg_tmp, RH,tg_interp, mc, dt_ug_conv, dt_vg_conv, tdt_local_heating +real, dimension(size(ug,1), size(ug,2)) :: flux_q_surf_part, flux_q_atm_part, flux_t_surf_part, flux_t_atm_part, e_sat_out ! Simple cloud scheme variabilies to pass to radiation real, dimension(size(ug,1), size(ug,2), size(ug,3)) :: cf_rad, reff_rad, qcl_rad, cca_rad @@ -848,6 +909,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, ps if (bucket) then dt_bucket = 0.0 filt = 0.0 + bucket_diffusion = 0.0 endif @@ -876,7 +938,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, ps conv_dt_tg = conv_dt_tg/delta_t conv_dt_qg = conv_dt_qg/delta_t - depth_change_conv = rain/dens_h2o + depth_change_conv = rain/dens_vapor rain = rain/delta_t precip = rain @@ -905,7 +967,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, ps conv_dt_tg = conv_dt_tg/delta_t conv_dt_qg = conv_dt_qg/delta_t - depth_change_conv = rain/dens_h2o + depth_change_conv = rain/dens_vapor rain = rain/delta_t precip = rain @@ -974,7 +1036,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, ps convective_rain = precip ! Perform large scale convection -if (r_conv_scheme .ne. DRY_CONV) then +if ( do_lscale_cond .eqv. .true.) then ! Large scale convection is a function of humidity only. This is ! inconsistent with the dry convection scheme, don't run it! rain = 0.0; snow = 0.0 @@ -986,7 +1048,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, ps cond_dt_tg = cond_dt_tg/delta_t cond_dt_qg = cond_dt_qg/delta_t - depth_change_cond = rain/dens_h2o + depth_change_cond = rain/dens_vapor rain = rain/delta_t snow = snow/delta_t precip = precip + rain + snow @@ -1118,6 +1180,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, ps dedq_atm(:,:), & dtaudu_atm(:,:), & dtaudv_atm(:,:), & + q_surf_out(:,:), & ! is intent(out) ex_del_m(:,:), & ex_del_h(:,:), & ex_del_q(:,:), & @@ -1129,7 +1192,30 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, ps delta_t, & land(:,:), & .not.land(:,:), & - avail(:,:) ) + avail(:,:) ) + + if(id_w_atm > 0) used = send_data(id_w_atm, w_atm, Time) + if(id_drag_m > 0) used = send_data(id_drag_m, drag_m*w_atm, Time) + if(id_drag_t > 0) used = send_data(id_drag_t, drag_t*w_atm, Time) + if(id_drag_q > 0) used = send_data(id_drag_q, drag_q*w_atm, Time) + if(id_cd_m > 0) used = send_data(id_cd_m, drag_m, Time) + if(id_cd_t > 0) used = send_data(id_cd_t, drag_t, Time) + if(id_cd_q > 0) used = send_data(id_cd_q, drag_q, Time) + if(id_rho_drag > 0) used = send_data(id_rho_drag, dhdt_surf, Time) + if(id_q_surf0 > 0) used = send_data(id_q_surf0, q_surf_out, Time) + flux_q_surf_part = dhdt_surf * q_surf_out + flux_q_atm_part = dhdt_surf * grid_tracers(:,:,num_levels,previous,nsphum) + if(id_flux_q_surf_part > 0) used = send_data(id_flux_q_surf_part, flux_q_surf_part, Time) + if(id_flux_q_atm_part > 0) used = send_data(id_flux_q_atm_part, flux_q_atm_part, Time) + flux_t_surf_part = dhdt_surf * t_surf + flux_t_atm_part = dhdt_surf * tg(:,:,num_levels,previous) + if(id_flux_t_surf_part > 0) used = send_data(id_flux_t_surf_part, flux_t_surf_part, Time) + if(id_flux_t_atm_part > 0) used = send_data(id_flux_t_atm_part, flux_t_atm_part, Time) + if(id_flux_u > 0) used = send_data(id_flux_u, flux_u, Time) + if(id_flux_v > 0) used = send_data(id_flux_v, flux_v, Time) + + call escomp ( t_surf, e_sat_out ) ! saturation vapor pressure + if(id_e_sat > 0) used = send_data(id_e_sat, e_sat_out, Time) if(id_flux_u > 0) used = send_data(id_flux_u, flux_u, Time) if(id_flux_v > 0) used = send_data(id_flux_v, flux_v, Time) @@ -1177,6 +1263,13 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, ps endif #endif +if (do_local_heating) then + call local_heating ( Time, is, js, rad_lon, rad_lat, & + p_half(:,:,num_levels+1,current), p_full(:,:,:,current), & + p_half(:,:,:,current), tdt_local_heating ) + dt_tg = dt_tg + tdt_local_heating +endif + #ifdef SOC_NO_COMPILE if (do_socrates_radiation) then call error_mesg('idealized_moist_phys','do_socrates_radiation is .true. but compiler flag -D SOC_NO_COMPILE used. Stopping.', FATAL) @@ -1236,7 +1329,6 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, ps z_pbl) ! have taken the names of arrays etc from vert_turb_driver below. Watch ntp from 2006 call to this routine? endif - if(turb) then call vert_turb_driver( 1, 1, & @@ -1337,8 +1429,10 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, ps endif ! if(turb) then ! Adding relative humidity calculation so as to allow comparison with Frierson's thesis. - call rh_calc (p_full(:,:,:,previous),tg_tmp,qg_tmp,RH) - if(id_rh >0) used = send_data(id_rh, RH*100., Time) + if (id_rh > 0) then + call rh_calc (p_full(:,:,:,previous),tg_tmp,qg_tmp,RH) + used = send_data(id_rh, RH*100., Time) + endif ! Add bucket ! Timestepping for bucket. @@ -1359,6 +1453,9 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, ps dt_bucket = depth_change_cond + depth_change_conv - depth_change_lh !change in bucket depth in one leapfrog timestep [m] + !diffuse_surf_water transforms dt_bucket to spherical, diffuses water, and transforms back + call diffuse_surf_water(dt_bucket,bucket_depth(:,:,previous),delta_t,damping_coeff_bucket,bucket_diffusion) + ! use the raw filter in leapfrog time stepping filt(:,:) = bucket_depth(:,:,previous) - 2.0 * bucket_depth(:,:,current) @@ -1380,14 +1477,17 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, ps where (bucket_depth <= 0.) bucket_depth = 0. ! truncate surface reservoir over land points + if (finite_bucket_depth_over_land) then where(land .and. (bucket_depth(:,:,future) > max_bucket_depth_land)) bucket_depth(:,:,future) = max_bucket_depth_land end where + endif if(id_bucket_depth > 0) used = send_data(id_bucket_depth, bucket_depth(:,:,future), Time) if(id_bucket_depth_conv > 0) used = send_data(id_bucket_depth_conv, depth_change_conv(:,:), Time) if(id_bucket_depth_cond > 0) used = send_data(id_bucket_depth_cond, depth_change_cond(:,:), Time) if(id_bucket_depth_lh > 0) used = send_data(id_bucket_depth_lh, depth_change_lh(:,:), Time) + if(id_bucket_diffusion > 0) used = send_data(id_bucket_diffusion, bucket_diffusion(:,:)/delta_t, Time) endif ! end Add bucket section @@ -1409,6 +1509,7 @@ subroutine idealized_moist_phys_end if(mixed_layer_bc) call mixed_layer_end(t_surf, bucket_depth, bucket) if(do_damping) call damping_driver_end +if(do_local_heating) call hs_forcing_end #ifdef SOC_NO_COMPILE !No need to end socrates #else diff --git a/src/atmos_spectral/driver/solo/mixed_layer.F90 b/src/atmos_spectral/driver/solo/mixed_layer.F90 index 051d985fe..ce7e1541d 100644 --- a/src/atmos_spectral/driver/solo/mixed_layer.F90 +++ b/src/atmos_spectral/driver/solo/mixed_layer.F90 @@ -123,6 +123,9 @@ module mixed_layer_mod real,dimension(10) :: slandlon=0,slandlat=0,elandlon=-1,elandlat=-1 !s End mj extra options +logical :: specify_constant_sst = .false. +real :: sst_prescribed_constant = 273. + character(len=256) :: qflux_file_name = 'ocean_qflux' character(len=256) :: qflux_field_name = 'ocean_qflux' !Only used when using a non-time-varying q-flux. Otherwise code assumes field_name = file_name. @@ -153,7 +156,9 @@ module mixed_layer_mod ice_albedo_value, specify_sst_over_ocean_only, & ice_concentration_threshold, ice_albedo_method,& add_latent_heat_flux_anom,flux_lhe_anom_file_name,& - flux_lhe_anom_field_name, do_ape_sst, qflux_field_name + flux_lhe_anom_field_name, specify_constant_sst,& + sst_prescribed_constant, & + do_ape_sst, qflux_field_name !================================================================================================================================= @@ -306,7 +311,7 @@ subroutine mixed_layer_init(is, ie, js, je, num_levels, t_surf, bucket_depth, ax call get_deg_lon(deg_lon) !s Adding MiMA options - if(do_sc_sst) do_read_sst = .true. + ! if(do_sc_sst) do_read_sst = .true. trop_capacity = trop_depth*RHO_CP land_capacity = land_depth*RHO_CP if(trop_capacity .le. 0.) trop_capacity = depth*RHO_CP @@ -679,16 +684,21 @@ subroutine mixed_layer ( & !s Surface heat_capacity calculation based on that in MiMA by mj if(do_sc_sst) then !mj sst read from input file - ! read at the new time, as that is what we are stepping to - call interpolator( sst_interp, Time_next, sst_new, trim(sst_file) ) - - if(specify_sst_over_ocean_only) then - where (.not.land_ice_mask) delta_t_surf = sst_new - t_surf - where (.not.land_ice_mask) t_surf = t_surf + delta_t_surf - else - delta_t_surf = sst_new - t_surf - t_surf = t_surf + delta_t_surf - endif + ! read at the new time, as that is what we are stepping to + if (specify_constant_sst) then + sst_new = sst_prescribed_constant + else + call interpolator( sst_interp, Time_next, sst_new, trim(sst_file) ) + endif + + if(specify_sst_over_ocean_only) then + where (.not.land_ice_mask) delta_t_surf = sst_new - t_surf + where (.not.land_ice_mask) t_surf = t_surf + delta_t_surf + else + delta_t_surf = sst_new - t_surf + t_surf = t_surf + delta_t_surf + endif + end if if (do_ape_sst) then diff --git a/src/atmos_spectral/model/spectral_dynamics.F90 b/src/atmos_spectral/model/spectral_dynamics.F90 index ab2e0b9ae..82e291ecd 100644 --- a/src/atmos_spectral/model/spectral_dynamics.F90 +++ b/src/atmos_spectral/model/spectral_dynamics.F90 @@ -96,6 +96,7 @@ module spectral_dynamics_mod public :: get_use_virtual_temperature, get_reference_sea_level_press, get_surf_geopotential public :: get_pk_bk, complete_robert_filter, complete_update_of_future public :: get_axis_id, spectral_diagnostics, get_initial_fields +public :: diffuse_surf_water !=============================================================================================== @@ -156,7 +157,8 @@ module spectral_dynamics_mod use_implicit = .true., & triang_trunc = .true., & graceful_shutdown = .false., & - make_symmetric = .false. !GC/RG Add namelist option to run model as zonally symmetric + make_symmetric = .false., & !GC/RG Add namelist option to run model as zonally symmetric + do_spec_tracer_filter = .false. integer :: damping_order = 2, & @@ -221,7 +223,8 @@ module spectral_dynamics_mod raw_filter_coeff, & !st graceful_shutdown, json_logging, & graceful_shutdown, & - make_symmetric !GC/RG add make_symmetric option + make_symmetric, & !GC/RG add make_symmetric option + do_spec_tracer_filter contains @@ -1154,18 +1157,38 @@ subroutine update_tracers(tracer_attributes, dt_tr, wg, p_half, delta_t, part_fi call trans_spherical_to_grid (spec_tracers(:,:,:,future,ntr), grid_tracers(:,:,:,future,ntr)) else if(trim(tracer_attributes(ntr)%numerical_representation) == 'grid') then tr_future = grid_tracers(:,:,:,previous,ntr) + delta_t*dt_tr(:,:,:,ntr) - dt_tr(:,:,:,ntr) = 0.0 - call a_grid_horiz_advection (ug(:,:,:,current), vg(:,:,:,current), tr_future, delta_t, dt_tr(:,:,:,ntr)) - tr_future = tr_future + delta_t*dt_tr(:,:,:,ntr) + + dt_tmp = 0.0 + call a_grid_horiz_advection (ug(:,:,:,current), vg(:,:,:,current), tr_future, delta_t, dt_tmp) + dt_tr(:,:,:,ntr) = dt_tr(:,:,:,ntr) + dt_tmp + tr_future = tr_future + delta_t*dt_tmp + dp = p_half(:,:,2:num_levels+1) - p_half(:,:,1:num_levels) call vert_advection(delta_t, wg, dp, tr_future, dt_tmp, scheme=tracer_vert_advect_scheme(ntr), form=ADVECTIVE_FORM) - tr_future = tr_future + delta_t*dt_tmp + dt_tr(:,:,:,ntr) = dt_tr(:,:,:,ntr) + dt_tmp + + ! [TS/LJJ mod:] added spectral damping of grid tracer + if(do_spec_tracer_filter) then !added rwills - spec_tracer_filter only needed for titan, causes water conservation problems in def run + call trans_grid_to_spherical (dt_tr(:,:,:,ntr), dt_trs(:,:,:)) + call trans_grid_to_spherical (grid_tracers(:,:,:,previous,ntr), spec_tracers(:,:,:,previous,ntr)) + call compute_spectral_damping (spec_tracers(:,:,:,previous,ntr), dt_trs(:,:,:), delta_t) + call trans_spherical_to_grid (dt_trs(:,:,:), dt_tr(:,:,:,ntr)) + endif + tr_future = grid_tracers(:,:,:,previous, ntr) + delta_t * dt_tr(:,:,:,ntr) + ! End spectral damping modifications !!!! + + !End result of this modified version shouild be exacty as before. a_grid now updates dt_tmp, which is used to increment tr_future. tr_future is fed into vert_advection, which updates dt_tr. Then we have the option of sending the whole dt_tr through the spectral filter. But either way, we end up with a total dt_tr that is used to increment tr_future, and write over the temporary modifications made to tr_future after a_grid. + + + if(step_number == num_steps) then part_filt_tr_out(:,:,:,ntr)=grid_tracers(:,:,:,previous,ntr) - 2.0*grid_tracers(:,:,:,current,ntr) grid_tracers(:,:,:,current,ntr) = grid_tracers(:,:,:,current,ntr) + & tracer_attributes(ntr)%robert_coeff*(part_filt_tr_out(:,:,:,ntr))*raw_filter_coeff robert_complete_for_tracers = .false. + grid_tracers(:,:,:,future,ntr) = tr_future !Moved because if false then future value is updated. But default is num_steps=1, so shouldn't change existing functionality. + else part_filt_tr_out(:,:,:,ntr)=grid_tracers(:,:,:,previous,ntr) - 2.0*grid_tracers(:,:,:,current,ntr)+tr_future @@ -1177,7 +1200,6 @@ subroutine update_tracers(tracer_attributes, dt_tr, wg, p_half, delta_t, part_fi robert_complete_for_tracers = .true. endif - grid_tracers(:,:,:,future,ntr) = tr_future else call error_mesg('update_tracers',trim(tracer_attributes(ntr)%numerical_representation)// & ' is an invalid numerical_representation', FATAL) @@ -1931,4 +1953,43 @@ subroutine spectral_diagnostics_end end subroutine spectral_diagnostics_end !=================================================================================== +subroutine diffuse_surf_water(dt_bucket,bucket_depth,delta_t,damping_coeff_bucket,bucket_diffusion) + !This subroutine diffuses the surface water reservoir more the bigger damping_coeff_bucket is. + !dt_bucket is the tendency for the current timestep, and gets changed by this subroutine + !to reflect the diffusion that occurs over time delta_t. + ! Taken from srcmods section of Titan model in Tapio Schneider's Github - https://github.com/tapios/fms-idealized/blob/master/exp/titan/srcmods/spectral_dynamics.f90 + + real, intent(inout), dimension(is:ie, js:je) :: dt_bucket + real, intent(in), dimension(is:ie, js:je) :: bucket_depth + real, intent(inout), dimension(is:ie, js:je) :: bucket_diffusion + real, intent(in) :: delta_t,damping_coeff_bucket + + complex, dimension(ms:me, ns:ne) :: dt_bucket_sph + complex, dimension(ms:me, ns:ne) :: bucket_depth_sph + complex, dimension(ms:me, ns:ne) :: bucket_diffusion_sph !for output only, not used in calculation + real, dimension(size(bucket_depth_sph,1),size(bucket_depth_sph,2)) :: coeff + real :: damping_order_bucket = 1 + real, allocatable, dimension(:,:) :: bucket_damping, eigen + + allocate(bucket_damping (0:num_fourier, 0:num_spherical)) + allocate(eigen (0:num_fourier,0:num_spherical)) + + call get_eigen_laplacian(eigen) + + call trans_grid_to_spherical(dt_bucket,dt_bucket_sph) + call trans_grid_to_spherical(bucket_depth,bucket_depth_sph) + + bucket_damping(:,:) = damping_coeff_bucket * (eigen(:,:)**damping_order_bucket) + coeff = 1.0/(1.0 + bucket_damping(ms:me,ns:ne)*delta_t) + bucket_diffusion_sph = dt_bucket_sph + dt_bucket_sph = coeff * (dt_bucket_sph - bucket_damping(ms:me,ns:ne)*bucket_depth_sph*delta_t) + bucket_diffusion_sph = bucket_diffusion_sph*(-1.) + dt_bucket_sph + + call trans_spherical_to_grid(bucket_diffusion_sph,bucket_diffusion) + call trans_spherical_to_grid(dt_bucket_sph,dt_bucket) + + end subroutine diffuse_surf_water + + ! =================================================================================== + end module spectral_dynamics_mod diff --git a/src/coupler/surface_flux.F90 b/src/coupler/surface_flux.F90 index d38850528..5971afebb 100644 --- a/src/coupler/surface_flux.F90 +++ b/src/coupler/surface_flux.F90 @@ -35,11 +35,12 @@ module surface_flux_mod ! ! ============================================================================ -use fms_mod, only: FATAL, close_file, mpp_pe, mpp_root_pe, write_version_number +use fms_mod, only: FATAL, close_file, mpp_pe, mpp_root_pe, write_version_number, error_mesg, FATAL use fms_mod, only: file_exist, check_nml_error, open_namelist_file, stdlog use monin_obukhov_mod, only: mo_drag, mo_profile +use frierson_monin_obukhov_mod, only: frierson_mo_drag use sat_vapor_pres_mod, only: escomp, descomp -use constants_mod, only: cp_air, hlv, stefan, rdgas, rvgas, grav, vonkarm, dens_h2o +use constants_mod, only: cp_air, hlv, stefan, rdgas, rvgas, grav, vonkarm, dens_vapor use mpp_mod, only: input_nml_file implicit none @@ -264,8 +265,12 @@ module surface_flux_mod real :: land_humidity_prefactor = 1.0 ! Default is that land makes no difference to evaporative fluxes real :: land_evap_prefactor = 1.0 ! Default is that land makes no difference to evaporative fluxes -real :: flux_heat_gp = 5.7 ! Default value for Jupiter of 5.7 Wm^-2 -real :: diabatic_acce = 1.0 ! Diabatic acceleration?? +logical :: use_actual_surface_temperatures = .true. !Always true, apart from when running a dry model, where you can set this to false so that escomp is called with temperatures of 200k always, preventing bad temperature errors. + +logical :: use_frierson_mo_drag = .false. !Isca by default uses the full monin-obukhov formulae. When true we switch to using the simplified formulae from 10.1175/JAS3753.1 eq 12-14. + +real :: flux_heat_gp = 5.7 !s Default value for Jupiter of 5.7 Wm^-2 +real :: diabatic_acce = 1.0 !s Diabatic acceleration?? namelist /surface_flux_nml/ no_neg_q, & @@ -280,10 +285,10 @@ module surface_flux_mod raoult_sat_vap, & do_simple, & land_humidity_prefactor, & ! Added to make land 'dry', i.e. to decrease the evaporative heat flux in areas of land. - land_evap_prefactor, & ! Added to make land 'dry', i.e. to decrease the evaporative heat flux in areas of land. + land_evap_prefactor, & ! Added to make land 'dry', i.e. to decrease the evaporative heat flux in areas of land. flux_heat_gp, & ! prescribed lower boundary heat flux on a giant planet - diabatic_acce - + diabatic_acce, use_actual_surface_temperatures, & + use_frierson_mo_drag contains @@ -347,6 +352,7 @@ subroutine surface_flux_1d ( & w_atm, u_star, b_star, q_star, & dhdt_surf, dedt_surf, dedq_surf, drdt_surf, & dhdt_atm, dedq_atm, dtaudu_atm, dtaudv_atm, & + q_surf_out, & ex_del_m, ex_del_h, ex_del_q, & temp_2m, u_10m, v_10m, & q_2m, rh_2m, & @@ -367,7 +373,7 @@ subroutine surface_flux_1d ( & dhdt_surf, dedt_surf, dedq_surf, drdt_surf, & dhdt_atm, dedq_atm, dtaudu_atm,dtaudv_atm, & w_atm, u_star, b_star, q_star, & - cd_m, cd_t, cd_q, & + cd_m, cd_t, cd_q, q_surf_out, & ex_del_m, ex_del_h, ex_del_q, & temp_2m, u_10m, v_10m, & q_2m, rh_2m @@ -397,12 +403,13 @@ subroutine surface_flux_1d ( & integer :: i, nbad - if (do_init) call surface_flux_init !---- use local value of surf temp ---- t_surf0 = 200. ! avoids out-of-bounds in es lookup + +if (use_actual_surface_temperatures) then where (avail) where (land) t_surf0 = t_ca @@ -410,12 +417,28 @@ subroutine surface_flux_1d ( & t_surf0 = t_surf endwhere endwhere +else + if ((maxval(q_atm_in) > 0.).and. ( mpp_pe() == mpp_root_pe() )) then + call error_mesg('surface_flux_mod','Note that you are passing fixed surface temperatures to the calculation of flux_lhe because you have set use_actual_surface_temperatures=.false. This option is designed only for use with dry models.', FATAL) + endif +endif t_surf1 = t_surf0 + del_temp call escomp ( t_surf0, e_sat ) ! saturation vapor pressure call escomp ( t_surf1, e_sat1 ) ! perturbed vapor pressure +if (.not. use_actual_surface_temperatures) then + where (avail) + where (land) + t_surf0 = t_ca + elsewhere + t_surf0 = t_surf + endwhere + endwhere +endif + + if(use_mixing_ratio) then ! surface mixing ratio at saturation q_sat = d622*e_sat /(p_surf-e_sat ) @@ -447,13 +470,16 @@ subroutine surface_flux_1d ( & ! initialize surface air humidity depending on whether surface is dry or wet (bucket empty or not) if (bucket) then - where (bucket_depth <= 0.0) - q_surf0 = q_atm - elsewhere - q_surf0 = q_sat ! everything else assumes saturated sfc humidity - end where + where (bucket_depth <= 0.0) + q_surf0 = q_atm + elsewhere + q_surf0 = q_sat ! everything else assumes saturated sfc humidity + end where endif + q_surf_out = q_surf0 + + ! generate information needed by monin_obukhov where (avail) p_ratio = (p_surf/p_atm)**kappa @@ -501,9 +527,17 @@ subroutine surface_flux_1d ( & endif ! monin-obukhov similarity theory - call mo_drag (thv_atm, thv_surf, z_atm, & - rough_mom, rough_heat, rough_moist, w_atm, & - cd_m, cd_t, cd_q, u_star, b_star, avail ) + if (use_frierson_mo_drag) then + + call frierson_mo_drag (thv_atm, thv_surf, z_atm, & + rough_mom, rough_heat, rough_moist, w_atm, & + cd_m, cd_t, cd_q, u_star, b_star, avail ) + else + + call mo_drag (thv_atm, thv_surf, z_atm, & + rough_mom, rough_heat, rough_moist, w_atm, & + cd_m, cd_t, cd_q, u_star, b_star, avail ) + endif ! added for 10m winds and 2m temperature add mo_profile() @@ -591,18 +625,18 @@ subroutine surface_flux_1d ( & where (bucket_depth >= max_bucket_depth_land*0.75) flux_q = rho_drag * (q_surf0 - q_atm) elsewhere - flux_q = bucket_depth/(max_bucket_depth_land*0.75) * rho_drag * (q_surf0 - q_atm) ! flux of water vapor (Kg/(m**2 s)) + flux_q = bucket_depth/(max_bucket_depth_land*0.75) * rho_drag * (q_surf0 - q_atm) ! flux of water vapor (Kg/(m**2 s)) end where elsewhere flux_q = rho_drag * (q_surf0 - q_atm) ! flux of water vapor (Kg/(m**2 s)) end where - depth_change_lh_1d = flux_q * dt/dens_h2o + depth_change_lh_1d = flux_q * dt/dens_vapor where (flux_q > 0.0 .and. bucket_depth < depth_change_lh_1d) ! where more evaporation than what's in bucket, empty bucket - flux_q = bucket_depth * dens_h2o / dt - depth_change_lh_1d = flux_q * dt / dens_h2o - end where - + flux_q = bucket_depth * dens_vapor / dt + depth_change_lh_1d = flux_q * dt / dens_vapor + end where + where (bucket_depth <= 0.0) dedt_surf = 0. dedq_surf = 0. @@ -610,12 +644,16 @@ subroutine surface_flux_1d ( & elsewhere dedq_surf = 0. dedq_atm = -rho_drag ! d(latent heat flux)/d(atmospheric mixing ratio) - where(land) - where (bucket_depth >= max_bucket_depth_land*0.75) - dedt_surf = rho_drag * (q_sat1 - q_sat) *del_temp_inv - elsewhere - dedt_surf = bucket_depth/(max_bucket_depth_land*0.75) * rho_drag * (q_sat1 - q_sat) *del_temp_inv - end where + where(land) + ! if (finite_bucket_depth_over_land) then + where (bucket_depth >= max_bucket_depth_land*0.75) + dedt_surf = rho_drag * (q_sat1 - q_sat) *del_temp_inv + elsewhere + dedt_surf = bucket_depth/(max_bucket_depth_land*0.75) * rho_drag * (q_sat1 - q_sat) *del_temp_inv + end where + ! else + ! dedt_surf = rho_drag * (q_sat1 - q_sat) *del_temp_inv + ! endif elsewhere dedt_surf = rho_drag * (q_sat1 - q_sat) *del_temp_inv end where @@ -709,6 +747,7 @@ subroutine surface_flux_0d ( & w_atm_0, u_star_0, b_star_0, q_star_0, & dhdt_surf_0, dedt_surf_0, dedq_surf_0, drdt_surf_0, & dhdt_atm_0, dedq_atm_0, dtaudu_atm_0, dtaudv_atm_0, & + q_surf_out_0, & ex_del_m_0, ex_del_h_0, ex_del_q_0, & temp_2m_0, u_10m_0, v_10m_0, & q_2m_0, rh_2m_0, & @@ -726,7 +765,7 @@ subroutine surface_flux_0d ( & dhdt_surf_0, dedt_surf_0, dedq_surf_0, drdt_surf_0, & dhdt_atm_0, dedq_atm_0, dtaudu_atm_0,dtaudv_atm_0, & w_atm_0, u_star_0, b_star_0, q_star_0, & - cd_m_0, cd_t_0, cd_q_0, & + cd_m_0, cd_t_0, cd_q_0, q_surf_out_0, & ex_del_m_0, ex_del_h_0, ex_del_q_0, & temp_2m_0, u_10m_0, v_10m_0, & q_2m_0, rh_2m_0 @@ -746,7 +785,7 @@ subroutine surface_flux_0d ( & dhdt_surf, dedt_surf, dedq_surf, drdt_surf, & dhdt_atm, dedq_atm, dtaudu_atm,dtaudv_atm, & w_atm, u_star, b_star, q_star, & - cd_m, cd_t, cd_q, & + cd_m, cd_t, cd_q, q_surf_out, & ex_del_m, ex_del_h, ex_del_q, & temp_2m, u_10m, v_10m, & q_2m, rh_2m @@ -792,6 +831,7 @@ subroutine surface_flux_0d ( & w_atm, u_star, b_star, q_star, & dhdt_surf, dedt_surf, dedq_surf, drdt_surf, & dhdt_atm, dedq_atm, dtaudu_atm, dtaudv_atm, & + q_surf_out, & ex_del_m, ex_del_h, ex_del_q, & temp_2m, u_10m, v_10m, & q_2m, rh_2m, & @@ -818,6 +858,7 @@ subroutine surface_flux_0d ( & cd_m_0 = cd_m(1) cd_t_0 = cd_t(1) cd_q_0 = cd_q(1) + q_surf_out_0 = q_surf_out(1) ex_del_m_0 = ex_del_m(1) ex_del_h_0 = ex_del_h(1) ex_del_q_0 = ex_del_q(1) @@ -841,6 +882,7 @@ subroutine surface_flux_2d ( & w_atm, u_star, b_star, q_star, & dhdt_surf, dedt_surf, dedq_surf, drdt_surf, & dhdt_atm, dedq_atm, dtaudu_atm, dtaudv_atm, & + q_surf_out, & ex_del_m, ex_del_h, ex_del_q, & temp_2m, u_10m, v_10m, & q_2m, rh_2m, & @@ -858,7 +900,7 @@ subroutine surface_flux_2d ( & dhdt_surf, dedt_surf, dedq_surf, drdt_surf, & dhdt_atm, dedq_atm, dtaudu_atm,dtaudv_atm, & w_atm, u_star, b_star, q_star, & - cd_m, cd_t, cd_q, & + cd_m, cd_t, cd_q, q_surf_out, & ex_del_m, ex_del_h, ex_del_q, & temp_2m, u_10m, v_10m, & q_2m, rh_2m @@ -887,6 +929,7 @@ subroutine surface_flux_2d ( & w_atm(:,j), u_star(:,j), b_star(:,j), q_star(:,j), & dhdt_surf(:,j), dedt_surf(:,j), dedq_surf(:,j), drdt_surf(:,j), & dhdt_atm(:,j), dedq_atm(:,j), dtaudu_atm(:,j), dtaudv_atm(:,j), & + q_surf_out(:,j), & ex_del_m(:,j), ex_del_h(:,j), ex_del_q(:,j), & temp_2m(:,j), u_10m(:,j), v_10m(:,j), & q_2m(:,j), rh_2m(:,j), & @@ -906,7 +949,7 @@ subroutine surface_flux_init ! read namelist #ifdef INTERNAL_FILE_NML read (input_nml_file, surface_flux_nml, iostat=io) - ierr = check_nml_error(io,'surface_flux_nml') + ierr = check_nml_error(io, 'surface_flux_nml') #else if ( file_exist('input.nml')) then unit = open_namelist_file () diff --git a/src/extra/env/docker b/src/extra/env/docker index cc2567cc4..d14afbcf2 100755 --- a/src/extra/env/docker +++ b/src/extra/env/docker @@ -5,3 +5,4 @@ export GFDL_MKMF_TEMPLATE=gfort export F90=mpifort export CC=mpicc +export NETCDF_LIBS = `nf-config --fflags --flibs` \ No newline at end of file diff --git a/src/extra/env/isca b/src/extra/env/isca index 9ceb3dbf8..a4cf1f14c 100644 --- a/src/extra/env/isca +++ b/src/extra/env/isca @@ -1,9 +1,10 @@ echo loadmodules for Isca -export F90=mpiifort -export CC=mpiicc - module purge module load intel/2016b module load HDF5/1.8.16-intel-2016b -module load netCDF-Fortran/4.4.2-intel-2016b \ No newline at end of file +module load netCDF-Fortran/4.4.2-intel-2016b + +export F90=mpiifort +export CC=mpiicc +export NETCDF_LIBS = `nf-config --fflags --flibs` \ No newline at end of file diff --git a/src/extra/env/mac_brew b/src/extra/env/mac_brew new file mode 100644 index 000000000..4beff9c56 --- /dev/null +++ b/src/extra/env/mac_brew @@ -0,0 +1,7 @@ +echo loadmodules for Isca on mac using homebrew + +export GFDL_COMPILER=gfort +export F90=mpifort +export CC=gcc-8 +export CDEFS=" -DMACOS " +export NETCDF_LIBS=" -I/usr/local/include " \ No newline at end of file diff --git a/src/extra/model/grey/path_names b/src/extra/model/grey/path_names index 1bdea444c..8edec5d9f 100644 --- a/src/extra/model/grey/path_names +++ b/src/extra/model/grey/path_names @@ -21,6 +21,7 @@ atmos_param/qflux/qflux.f90 atmos_param/monin_obukhov/monin_obukhov_interfaces.h atmos_param/monin_obukhov/monin_obukhov_kernel.F90 atmos_param/monin_obukhov/monin_obukhov.F90 +atmos_param/frierson_monin_obukhov/frierson_monin_obukhov.F90 atmos_param/dry_convection/dry_convection.f90 atmos_param/ras/ras.f90 atmos_param/rayleigh_bottom_drag/rayleigh_bottom_drag.F90 diff --git a/src/extra/model/isca/path_names b/src/extra/model/isca/path_names index 4b08c4edb..abcba6b80 100644 --- a/src/extra/model/isca/path_names +++ b/src/extra/model/isca/path_names @@ -22,6 +22,7 @@ atmos_param/qflux/qflux.f90 atmos_param/monin_obukhov/monin_obukhov_interfaces.h atmos_param/monin_obukhov/monin_obukhov_kernel.F90 atmos_param/monin_obukhov/monin_obukhov.F90 +atmos_param/frierson_monin_obukhov/frierson_monin_obukhov.F90 atmos_param/dry_convection/dry_convection.f90 atmos_param/rayleigh_bottom_drag/rayleigh_bottom_drag.F90 atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/parkind.f90 diff --git a/src/extra/model/socrates/path_names b/src/extra/model/socrates/path_names index 59dc4cc24..cab81e74d 100644 --- a/src/extra/model/socrates/path_names +++ b/src/extra/model/socrates/path_names @@ -22,6 +22,7 @@ atmos_param/qflux/qflux.f90 atmos_param/monin_obukhov/monin_obukhov_interfaces.h atmos_param/monin_obukhov/monin_obukhov_kernel.F90 atmos_param/monin_obukhov/monin_obukhov.F90 +atmos_param/frierson_monin_obukhov/frierson_monin_obukhov.F90 atmos_param/dry_convection/dry_convection.f90 atmos_param/rayleigh_bottom_drag/rayleigh_bottom_drag.F90 atmos_param/damping_driver/damping_driver.f90 diff --git a/src/extra/python/isca/templates/compile.sh b/src/extra/python/isca/templates/compile.sh index d20377492..0db5b4e26 100755 --- a/src/extra/python/isca/templates/compile.sh +++ b/src/extra/python/isca/templates/compile.sh @@ -18,9 +18,9 @@ template_debug={{ template_dir }}/mkmf.template.debug execdir={{ execdir }} # where code is compiled and executable is created executable={{ executable_name }} -netcdf_flags=`nf-config --fflags --flibs` +#netcdf_flags=`nf-config --fflags --flibs` -ulimit -s unlimited # Set stack size to unlimited +#ulimit -s unlimited # Set stack size to unlimited export MALLOC_CHECK_=0 # 3. compile the mppncombine tool if it hasn't yet been done. @@ -59,7 +59,7 @@ $mkmf -a $sourcedir -t $template_debug -p $executable -c "$cppDefs" $pathnames else # execute mkmf to create makefile -cppDefs="-Duse_libMPI -Duse_netCDF -Duse_LARGEFILE -DINTERNAL_FILE_NML -DOVERLOAD_C8 {{compile_flags}}" +cppDefs="-Duse_libMPI -Duse_netCDF -Duse_LARGEFILE -DINTERNAL_FILE_NML -DOVERLOAD_C8 ${CDEFS} {{compile_flags}}" $mkmf -a $sourcedir -t $template -p $executable -c "$cppDefs" $pathnames $sourcedir/shared/include $sourcedir/shared/mpp/include fi diff --git a/src/extra/python/isca/templates/mkmf.template.gfort b/src/extra/python/isca/templates/mkmf.template.gfort index b35336dbc..63500db3b 100755 --- a/src/extra/python/isca/templates/mkmf.template.gfort +++ b/src/extra/python/isca/templates/mkmf.template.gfort @@ -2,7 +2,6 @@ # typical use with mkmf # mkmf -t template.ifc -c"-Duse_libMPI -Duse_netCDF" path_names /usr/local/include CPPFLAGS = -I/usr/local/include -NETCDF_LIBS = `nf-config --fflags --flibs` # FFLAGS: # -cpp: Use the fortran preprocessor @@ -23,5 +22,5 @@ FFLAGS = $(CPPFLAGS) $(NETCDF_LIBS) -cpp -fcray-pointer \ FC = $(F90) LD = $(F90) $(NETCDF_LIBS) -LDFLAGS = -lnetcdff -lnetcdf -lmpi +LDFLAGS = -lnetcdff -lnetcdf -lmpi -L/usr/local/Cellar/netcdf/4.6.1_2/lib CFLAGS = -D__IFC diff --git a/src/extra/python/scripts/create_local_heating_input_file.py b/src/extra/python/scripts/create_local_heating_input_file.py new file mode 100644 index 000000000..a75e3f6d3 --- /dev/null +++ b/src/extra/python/scripts/create_local_heating_input_file.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*-s +import numpy as np +from calendar_calc import day_number_to_date +from netCDF4 import date2num +import xarray as xar +import pdb +import create_timeseries as cts +from finite_difference import diffz + +grav = 9.81 +c_p = 287.04 / (2./7.) + +#Read in radiation-scheme output file(s) Will need to use open_mfdataset if more than one. +resolution_file = xar.open_dataset('~/Desktop/full_continents_global_monthly_control_test.nc', decode_times=False) + +lons = resolution_file['lon'] +lats = resolution_file['lat'] + + +#Set up output file variables. + +latb_temp=np.zeros(lats.shape[0]+1) + +for tick in np.arange(1,lats.shape[0]): + latb_temp[tick]=(lats[tick-1]+lats[tick])/2. + +lonb_temp=np.zeros(lons.shape[0]+1) + +for tick in np.arange(1,lons.shape[0]): + lonb_temp[tick]=(lons[tick-1]+lons[tick])/2. + +latb_temp[0]=-90. +latb_temp[-1]=90. + +lonb_temp[0]=0. +lonb_temp[-1]=360. + +latbs=latb_temp +lonbs=lonb_temp + +nlon=lons.shape[0] +nlat=lats.shape[0] + +nlonb=len(lonbs) +nlatb=latbs.shape[0] + +p_full = resolution_file['pfull'][::-1] + +p_half = resolution_file['phalf'][::-1] + +time_arr = resolution_file['time'].values + +nphalf =p_half.shape[0] + +heating_rate = np.zeros((nphalf, nlat, nlon)) + + +# DEFINE A HEATING RATE HERE. + + +heating_rate_new = np.zeros((12, heating_rate.shape[1], nlat, nlon)) + +#Arrange 12 months to be 30 days apart +time_arr_new = np.arange(0,12,1)*30. +time_arr = time_arr_new + + +#Repeat 1 month 12 times just because I only have 1 input file. This would need replacing with different values each month. +for t in range(len(time_arr)): + heating_rate_new[t,...] = heating_rate + +heating_rate = heating_rate_new[:,:,...] + +date_arr=np.mod((time_arr-time_arr[0]),12) +date_arr_new=np.zeros(12) + +#Find grid and time numbers + +npfull=p_full.shape[0] +nphalf=p_half.shape[0] + +ntime=len(time_arr) + +#Output it to a netcdf file. +file_name='full_continents_heating_input_file.nc' +variable_name='heating_rate' + +number_dict={} +number_dict['nlat']=nlat +number_dict['nlon']=nlon +number_dict['nlatb']=nlatb +number_dict['nlonb']=nlonb +number_dict['npfull']=npfull +number_dict['nphalf']=nphalf +number_dict['ntime']=ntime + +#Line that would need changing depending on year-on-year repeating heating or timeseries heating. +time_units='days since 0000-01-01 00:00:00.0' + +cts.output_to_file(heating_rate[:,::-1,...],lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units,file_name,variable_name,number_dict, dims=total_flux.dims) + diff --git a/src/extra/python/scripts/create_timeseries.py b/src/extra/python/scripts/create_timeseries.py index b18d241d5..32e960c2d 100644 --- a/src/extra/python/scripts/create_timeseries.py +++ b/src/extra/python/scripts/create_timeseries.py @@ -107,7 +107,7 @@ def create_time_arr(num_years,is_climatology,time_spacing): return time_arr,day_number,ntime,time_units,time_bounds -def output_multiple_variables_to_file(data_dict,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units,file_name,number_dict, time_bounds=None): +def output_multiple_variables_to_file(data_dict,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units,file_name,number_dict, time_bounds=None, on_half_levels=False): """Default is to accept multiple data variables to put in the file. Input format in data_dict = {variable_name:variable_array}. Currently only accepts all 2D fields or all 3D fields. @@ -120,6 +120,11 @@ def output_multiple_variables_to_file(data_dict,lats,lons,latbs,lonbs,p_full,p_h else: is_thd=True + # if dims is not None and is_thd: + # p_half_variable = 'phalf' in dims or 'p_half' in dims + # else: + # p_half_variable=False + if time_arr is None: add_time = False else: @@ -208,6 +213,22 @@ def output_multiple_variables_to_file(data_dict,lats,lons,latbs,lonbs,p_full,p_h times.bounds = 'time_bounds' + + if is_thd: + if on_half_levels: + dims_list = ['time','phalf', 'lat','lon',] + else: + dims_list = ['time','pfull', 'lat','lon',] + else: + dims_list = ['time','lat','lon',] + + if time_arr is None: + dims_list.remove('time') + + # pdb.set_trace() + output_array_netcdf = output_file.createVariable(variable_name,'f4', tuple(dims_list)) + + latitudes[:] = lats longitudes[:] = lons diff --git a/src/extra/python/scripts/finite_difference.py b/src/extra/python/scripts/finite_difference.py new file mode 100644 index 000000000..fa97f3504 --- /dev/null +++ b/src/extra/python/scripts/finite_difference.py @@ -0,0 +1,422 @@ +""" +Routines associated with finite differencing on the sphere. +""" +import numpy as np + +# Static parameters +RAD = np.pi / 180.0 +EARTH_R = 6.371e6 + + +class NDSlicer(object): + """N-Dimensional slice class for numpy arrays.""" + + def __init__(self, axis, ndim, start=None, stop=None, step=None): + """ + Create an n-dimensional slice list. + + Parameters + ---------- + axis : integer + Axis on which to apply the slice + ndim : integer + Total number of dimensions of array to be sliced + start, stop, step : integer, optional + Index of beginning, stop and step width of the slice [start:stop:step] + default for each is None. + + """ + self.axis = axis + self.ndim = ndim + self.start = start + self.stop = stop + self.step = step + self.slicer = None + self.__getitem__(slice(start, stop, step)) + + def __getitem__(self, key): + """ + Create an n-dimensional slice list. + + Parameters + ---------- + axis : integer + Axis on which to apply the slice + ndim : integer + Total number of dimensions of array to be sliced + start, stop, step : integer, optional + Index of beginning, stop and step width of the slice [start:stop:step] + default for each is None. + + Returns + ------- + slicer : list + list of slices such that all data at other axes are kept, one axis is sliced + + Examples + -------- + Create random array, slice it:: + + x = np.random.randn(5, 3) + + # Create slicer equivalent to [1:-1, :] + slc = NDSlicer(0, x.ndim) + print(x) + [[ 0.68470539 0.87880216 -0.45086367] + [ 1.06804045 0.63094676 -0.76633033] + [-1.69841915 0.35207064 -0.4582049 ] + [-0.56431067 0.62833728 -0.04101542] + [-0.02760744 2.02814338 0.13195714]] + print(x[slc[1:-1]]) + [[ 1.06804045 0.63094676 -0.76633033] + [-1.69841915 0.35207064 -0.4582049 ] + [-0.56431067 0.62833728 -0.04101542]] + + """ + if isinstance(key, slice): + self.start = key.start + self.stop = key.stop + self.step = key.step + elif isinstance(key, int): + self.start = key + self.stop = key + 1 + self.step = None + + self.slicer = [slice(None)] * self.ndim + self.slicer[self.axis] = slice(self.start, self.stop, self.step) + return self.slicer + + def slice(self, start=None, stop=None, step=None): + """Legacy compatibility method, calls `__getitem__`.""" + self.__getitem__(slice(start, stop, step)) + + +def cfd(data, lons, lats, cyclic=False): + """ + Vectorized central finite difference for N-D array data by lon / lat. + + Parameters + ---------- + data : array_like + N-dimensional array to be differentiated + lons, lats : array_like + 1D coordinate arrays for longitude and latitude dimensions + cyclic : Boolean, optional + Data is cyclic on if true + + Returns + ------- + diff_lon, diff_lat : array_like + ND arrays of longitudinal, latitudinal centered finite differences of `data` + respectively, with same dimensionality as `data` + + """ + # Find the axis where `data` shape matches longitudes + axis_x = np.where(np.array(data.shape) == lons.shape[0])[0][0] + + # Find the axis where `data` shape matches latitudes + axis_y = np.where(np.array(data.shape) == lats.shape[0])[0][0] + + dlong, dlatg = dlon_dlat(lons, lats, cyclic=cyclic) + diff_x = diff_cfd(data, axis_x, cyclic=cyclic) / dlong + diff_y = diff_cfd(data, axis_y, cyclic=False) / dlatg + + return diff_x, diff_y + + +def convert_radians_latlon(lat, lon): + """ + Convert input lat/lon array to radians if input is degrees, do nothing if radians. + + Parameters + ---------- + lat : array_like + ND array of latitude + lon : array_like + ND array of longitude + + Returns + ---------- + lat : array_like + ND array of latitude in radians + lon : array_like + ND array of longitude in radians + + """ + if (np.max(np.abs(lat)) - np.pi / 2.0) > 1.0: + lat_out = lat * RAD + else: + lat_out = lat + + if(np.min(lon) < 0 and np.max(lon) > 0 and + np.abs(np.max(np.abs(lon)) - np.pi) > np.pi): + lon_out = lon * RAD + elif np.abs(np.max(np.abs(lon)) - np.pi * 2) > np.pi: + lon_out = lon * RAD + else: + lon_out = lon + + return lat_out, lon_out + + +def dlon_dlat(lon, lat, cyclic=True): + """ + Compute horizontal center finite differences of latitude/longitude on Earth [m]. + + Parameters + ---------- + lon, lat : array_like + 1D array of longitudes and latitudes respectively + cyclic : boolean + If True, longitudes are cyclic + + Returns + ------- + dlong, dlatg : array_like + Longitudinal, latitudinal centered finite differences in m + Shapes are (lat.shape[0], lon.shape[0]) + + """ + # Check that lat/lon are in radians + lat, lon = convert_radians_latlon(lat, lon) + + # Calculate centre finite difference of lon / lat + dlon = lon[2:] - lon[:-2] + dlat = lat[2:] - lat[:-2] + + # If we want cyclic data, repeat dlon[0] and dlon[-1] at edges + if cyclic: + dlon = np.append(dlon[0], dlon) # cyclic boundary in East + dlon = np.append(dlon, dlon[-1]) # cyclic boundary in West + _, lat2d = np.meshgrid(lon, lat) + else: + _, lat2d = np.meshgrid(lon[1:-1], lat) + + dlat = np.append(lat[1] - lat[0], dlat) # boundary in South + dlat = np.append(dlat, lat[-1] - lat[-2]) # boundary in North + dlong, dlatg = np.meshgrid(dlon, dlat) + + # Lon/Lat differences in spherical coords + dlong *= EARTH_R * np.cos(lat2d) + dlatg *= EARTH_R + + return dlong, dlatg + + +def diff_cfd(data, axis=-1, cyclic=False, ndiff=1): + """ + Calculate centered finite difference of a field along an axis with *even* spacing. + + Parameters + ---------- + data : array_like + ND array of data of which to calculate the differences + axis : integer + Axis of `data` on which differences are calculated + cyclic : bool + Flag to indicate whether `data` is cyclic on `axis` + + Returns + ------- + diff : array_like + ND array of central finite differences of `data` along `axis` + + Notes + ----- + The output of this must be divided by 2x the grid spacing. For example, say `x` is + the coordinate, and y is the `data` + + >>> x = np.array([-3, -2, -1, 0, 1, 2]) + >>> y = np.array([0.0, -0.9, -0.9, 0.0, 0.9, 0.9]) + >>> diff_y = diff_cfd(y, cyclic=True) + >>> dydx = diff_y / (x[2] - x[0]) + + Since the coordinate itself isn't cyclic, and the spacing is constant using + the difference across a point works (shown is the difference across index == 1) + + Also, note that if data is *not* cyclic, then differences on the boundaries are + forward / backward differences, thus the coordinate must be differenced in the + same fashion (e.g. using this function on the coordinate variable) + + """ + # Calculate centred differences along longitude direction + # Equivalent to: diff = data[..., 2:] - data[..., :-2] for axis == -1 + slc = NDSlicer(axis, data.ndim) + if ndiff == 1: + diff = data[slc[2:]] - data[slc[:-2]] + elif ndiff == 2: + diff = data[slc[2:]] - 2 * data[slc[1:-1]] + data[slc[:-2]] + + if cyclic: + # Cyclic boundary in "East" + if ndiff == 1: + # Equiv to diff[..., 0] = data[..., 1:2] - data[..., -1:] + d_1 = data[slc[1:2]] - data[slc[-1:]] + # Cyclic boundary in "West" + # Equiv to diff[..., -1] = data[..., 0:1] - data[..., -2:-1] + d_2 = data[slc[0:1]] - data[slc[-2:-1]] + + elif ndiff == 2: + d_1 = data[slc[1:2]] - 2 * data[slc[0:1]] + data[slc[-1:]] + d_2 = data[slc[0:1]] - 2 * data[slc[-1:]] + data[slc[-2:-1]] + + else: + if ndiff == 1: + # Otherwise edges are forward/backward differences + # Boundary in "South", (data[..., 1:2] - data[..., 0:1]) + d_1 = data[slc[1:2]] - data[slc[0:1]] + + # Boundary in "North" (data[..., -1:] - data[..., -2:-1]) + d_2 = data[slc[-1:]] - data[slc[-2:-1]] + + elif ndiff == 2: + d_1 = data[slc[2:3]] - 2 * data[slc[1:2]] + data[slc[0:1]] + d_2 = data[slc[-3:-2]] - 2 * data[slc[-2:-1]] + data[slc[-1:None]] + + diff = np.append(d_1, diff, axis=axis) + diff = np.append(diff, d_2, axis=axis) + + return diff + + +def diffz(data, vcoord, axis=None): + """ + Calculate vertical derivative for data on uneven vertical levels. + + Parameters + ---------- + data : array_like + N-D array of input data to be differentiated, where + data.shape[axis] == vcoord.shape[0] + vcoord : array_like + Vertical coordinate, 1D + axis : integer + Axis where data.shape[axis] == vcoord.shape[0] + + Returns + ------- + dxdz : array_like + N-D array of d(data)/d(vcoord), same shape as input `data` + + """ + if axis is None: + # Find matching axis between data and vcoord + axis = np.where(np.array(data.shape) == vcoord.shape[0])[0][0] + + # Create array to hold vertical derivative + dxdz = np.ones(data.shape) + + slc = NDSlicer(axis, data.ndim) + # Create an n-dimensional broadcast along matching axis, same as [None, :, None, None] + # for axis=1, ndim=4 + bcast = [np.newaxis] * data.ndim + bcast[axis] = slice(None) + + dz1 = vcoord[1:-1] - vcoord[:-2] # z[i] - z[i - 1] + dz2 = vcoord[2:] - vcoord[1:-1] # z[i + 1] - z[i] + dz1 = dz1[bcast] + dz2 = dz2[bcast] + + dxdz[slc[1:-1]] = ((dz1**2 * data[slc[2:]] + (dz2**2 - dz1**2) * data[slc[1:-1]] - + dz2**2 * data[slc[:-2]]) / (dz1 * dz2 * (dz2 + dz1))) + + # Do forward difference at 0th level [:, 1, :, :] - [:, 0, :, :] + i = 0 + dz1 = vcoord[i + 1] - vcoord[i] + dz2 = vcoord[i + 2] - vcoord[i + 1] + dxdz[slc[i]] = (-dz1**2 * data[slc[i + 2]] + (dz1 + dz2)**2 * data[slc[i + 1]] + - (dz2**2 + 2 * dz1 * dz2) * data[slc[i]]) / (dz1 * dz2 * (dz1 + dz2)) + + # Do backward difference at Nth level [:, -1, :, :] - [:, -2, :, :] + i = data.shape[axis] - 1 + dz1 = vcoord[i - 1] - vcoord[i - 2] + dz2 = vcoord[i] - vcoord[i - 1] + dxdz[slc[i]] = (((dz1**2 + 2 * dz1 * dz2) * data[slc[i]] - + (dz1 + dz2)**2 * data[slc[i - 1]] + dz2**2 * data[slc[i - 2]]) / + (dz1 * dz2 * (dz1 + dz2))) + + return dxdz + + +def diff2z(data, vcoord, axis=None): + """ + Calculate 2nd order vertical derivative for data on uneven vertical levels. + + Parameters + ---------- + data : array_like + N-D array of input data to be differentiated, where + data.shape[axis] == vcoord.shape[0] + vcoord : array_like + Vertical coordinate, 1D + axis : integer + Axis where data.shape[axis] == vcoord.shape[0] + + Returns + ------- + d2xdz2 : array_like + N-D array of d**2(data)/d(vcoord)**2, same shape as input `data` + + """ + if axis is None: + # Find matching axis between data and vcoord + axis = np.where(np.array(data.shape) == vcoord.shape[0])[0][0] + + # Create array to hold vertical derivative + d2xdz2 = np.ones(data.shape) + + slc = NDSlicer(axis, data.ndim) + # Create an n-dimensional broadcast along matching axis, + # this is the same as [None, :, None, None] for axis=1, ndim=4 + bcast = [np.newaxis] * data.ndim + bcast[axis] = slice(None) + + dz1 = vcoord[1:-1] - vcoord[:-2] # z[i] - z[i - 1] + dz2 = vcoord[2:] - vcoord[1:-1] # z[i + 1] - z[i] + dz1 = dz1[bcast] + dz2 = dz2[bcast] + + d2xdz2[slc[1:-1]] = (2 * (dz1 * data[slc[2:]] - (dz1 + dz2) * data[slc[1:-1]] + + dz2 * data[slc[:-2]]) / (dz1 * dz2 * (dz1 + dz2))) + + i = 0 + dz1 = vcoord[i + 1] - vcoord[i] + dz2 = vcoord[i + 2] - vcoord[i + 1] + d2xdz2[slc[i]] = 2 * (dz1 * data[slc[i + 2]] - (dz1 + dz2) * data[slc[i + 1]] + + dz2 * data[slc[i]]) / (dz1 * dz2 * (dz1 + dz2)) + + i = data.shape[axis] - 1 + dz1 = vcoord[i - 1] - vcoord[i - 2] + dz2 = vcoord[i] - vcoord[i - 1] + d2xdz2[slc[i]] = 2 * (dz1 * data[slc[i]] - (dz1 + dz2) * data[slc[i - 1]] + + dz2 * data[slc[i - 2]]) / (dz1 * dz2 * (dz1 + dz2)) + + return d2xdz2 + + +def dp(pres): + """ + Calculate centered finite difference on pressure field with evenly spaced levels. + + Note + ---- + Difference will be negative if input pressure is monotonic decreasing + (surface is 0th element) + + Parameters + ---------- + pres : array_like + 1D array of pressure level data + + Returns + ------- + dp : array_like + 1D array of same shape as `pres`, that is the centered finite difference for + elements [1:-1] and forward/backward difference for elements [0] and [-1] resp. + + """ + d_p = pres[2:] - pres[:-2] + d_p = np.append(pres[1] - pres[0], dp) # boundary at surface + d_p = np.append(dp, pres[-1] - pres[-2]) # boundary aloft + + return dp \ No newline at end of file diff --git a/src/extra/python/scripts/grey_rad_parm_check.py b/src/extra/python/scripts/grey_rad_parm_check.py new file mode 100644 index 000000000..197af812b --- /dev/null +++ b/src/extra/python/scripts/grey_rad_parm_check.py @@ -0,0 +1,41 @@ +import numpy as np +import matplotlib.pyplot as plt +import pdb + +sigma=5.67e-8 +solar_constant = 589.0 + + +tau_s_range = np.arange(0.01, 2.0, 0.01) +albedo_range = np.arange(0.1, 0.8, 0.01) + +n_tau_s = np.shape(tau_s_range)[0] +n_albedo = np.shape(albedo_range)[0] + +tau_s_arr = np.zeros((n_tau_s, n_albedo)) +albedo_arr = np.zeros((n_tau_s, n_albedo)) + +for al in range(n_albedo): + for ts in range(n_tau_s): + tau_s_arr[ts, al] = tau_s_range[ts] + albedo_arr[ts,al] = albedo_range[al] + +def calc_t_surf(albedo_in, tau_s_in): + olr = (1.-albedo_in)* solar_constant + + t_surf = (olr*(tau_s_in+1.)/(2.*sigma))**0.25 + + return t_surf + +t_surf_arr = calc_t_surf(albedo_arr, tau_s_arr) + +cs = plt.contourf(albedo_range, tau_s_range, t_surf_arr) +cn = plt.contour(albedo_range, tau_s_range, t_surf_arr,levels=[210., 260.], colors=['k']) +plt.clabel(cn, inline=1, fontsize=10.) +fig = plt.gcf() + +cb = fig.colorbar(cs) + +t_surf_value = calc_t_surf(0.25, 0.2) +t_surf_shiny = calc_t_surf(0.7, 0.2) + diff --git a/src/extra/python/scripts/len_of_day_year_check.py b/src/extra/python/scripts/len_of_day_year_check.py new file mode 100644 index 000000000..8801d117a --- /dev/null +++ b/src/extra/python/scripts/len_of_day_year_check.py @@ -0,0 +1,91 @@ +import numpy as np +import matplotlib.pyplot as plt +import texttable as tt +import pdb + +""" The idea of this script is to try to find orbital periods and rotation rates that avoid leap years in planetary calendars. Like the way that we have 360-day calendars for Earth.""" + +planet_to_check = 'titan' + +if planet_to_check=='mars': + n_day_range = np.arange(660, 680, 1) + len_day_range = np.arange(88000, 89000, 1) + target_number_sols = 668.5991 + target_length_solar_day = (86400.)+(39.*60.)+35. +elif planet_to_check =='earth': + n_day_range = np.arange(350, 370, 1) + len_day_range = np.arange(86000, 88000, 1) + target_number_sols = 365.25 + target_length_solar_day = 86400 +elif planet_to_check =='titan': + n_day_range = np.arange(655, 675, 1) + len_day_range = np.arange(1377550, 1377750, 1) + + target_length_sidereal_day = 15.945*86400. + target_length_orbital_period = 10759.22*86400. + + target_length_solar_day = target_length_sidereal_day / (1.-target_length_sidereal_day/target_length_orbital_period) + target_number_sols = target_length_orbital_period / target_length_solar_day + + +n_days = np.shape(n_day_range)[0] +n_len_day = np.shape(len_day_range)[0] + +n_day_arr = np.zeros((n_days, n_len_day)) +n_len_day_arr = np.zeros((n_days, n_len_day)) + +for al in range(n_len_day): + for ts in range(n_days): + n_day_arr[ts, al] = n_day_range[ts] + n_len_day_arr[ts,al] = len_day_range[al] + +integer_values = n_day_arr * n_len_day_arr / (n_day_arr -1) #equation here is essentially number of sols * length of sol = number of sidereal days * length of sidereal day. Here integer_values = length_of_sol, n_day_arr is number of sidereal days, n_len_day_arr is length of sidereal day, and n_day_arr - 1 = number of sols. + +res_arr = np.mod(integer_values,1) #we are checking here that the length of the solar day is an integer number of seconds. +where_integer = np.where(res_arr==0.0) + +cs = plt.contourf(n_len_day_arr, n_day_arr, integer_values) +fig = plt.gcf() + +cb = fig.colorbar(cs) + +table_to_print = tt.Texttable() + +headings = ['i, j', 'nsol', 'nsid', 'lsol', 'lsid', 'orbital_period'] +table_to_print.header(headings) + +names = ["{0:3d}, {1:3d}".format(where_integer[0][i], where_integer[1][i]) for i in range(len(where_integer[0]))] + +nsol = ["{0:6.2f}".format(-1+n_day_arr[where_integer[0][i], where_integer[1][i]]) for i in range(len(where_integer[0]))] #n_day_arr - 1 = number of sols. +nsid = ["{0:6.2f}".format(n_day_arr[where_integer[0][i], where_integer[1][i]]) for i in range(len(where_integer[0]))] #n_day_arr is number of sidereal days +lsid = ["{0:6.2f}".format(n_len_day_arr[where_integer[0][i], where_integer[1][i]]) for i in range(len(where_integer[0]))] #n_len_day_arr is length of sidereal day +lsol = ["{0:6.2f}".format(integer_values[where_integer[0][i], where_integer[1][i]]) for i in range(len(where_integer[0]))] #integer_values = length_of_sol +orbital_period_1 = ["{0:6.2f}".format(float(nsol[i])*float(lsol[i])) for i in range(len(nsol))] #Checking orbital period = number_of_sols * length_of_sol +orbital_period_2 = ["{0:6.2f}".format(float(nsid[i])*float(lsid[i])) for i in range(len(nsid))] #checking orbital period = number of sidereal days * length of sidereal day + +if orbital_period_1 != orbital_period_2: + raise ValueError('orbital period from sidereal day not equal to orbital period from solar day') + +for row in zip(names, nsol, nsid, lsol, lsid, orbital_period_1): + table_to_print.add_row(row) + +s = table_to_print.draw() +print('Table for '+planet_to_check, ' with target number of sols = {0:6.3f} and length of solar day = {1:9.2f}'.format(target_number_sols, target_length_solar_day)) +print(s) +#For Isca, set the following: + +#diag.add_file('atmos_daily',length_of_solar_day , 'seconds', time_units='days') + + # 'main_nml': { + # 'dt_atmos': whatever_you_like, + # 'days': 0., + # 'seconds': number_of_solar_days_to_put_in_one_file*length_of_solar_day, + # 'calendar': 'no_calendar' + # }, + + # 'constants_nml': { + # 'orbital_period': orbital_period_from_this_script, + # 'rotation_period':length_of_SIDEREAL_day, + # }, + +# By doing this, the length of a year will be an integer number of solar and sidereal days. \ No newline at end of file diff --git a/src/extra/python/scripts/mars_topo_maker.py b/src/extra/python/scripts/mars_topo_maker.py new file mode 100644 index 000000000..2f14eee27 --- /dev/null +++ b/src/extra/python/scripts/mars_topo_maker.py @@ -0,0 +1,40 @@ +import xarray as xar +import gauss_grid as gg +import numpy as np +import mpl_toolkits.basemap as basemap +import matplotlib.pyplot as plt +from netCDF4 import Dataset + +num_lat_out = 128 +num_lon_out = 256 + + +mola_original = xar.open_dataset('/scratch/sit204/planet_topo_files/mars/mola32.nc') + +longitudes_out = np.arange(0., 360., (360./num_lon_out)) +latitudes_out = gg.gaussian_latitudes(int(num_lat_out/2.))[0] + +lon_array, lat_array = np.meshgrid(longitudes_out, latitudes_out) + +output_array = basemap.interp(mola_original.alt.values[::-1,:], mola_original.longitude.values, mola_original.latitude.values[::-1], lon_array, lat_array, order=1) + +land_array = np.zeros_like(output_array) + +nlat = latitudes_out.shape[0] +nlon = longitudes_out.shape[0] + +#Write land and topography arrays to file +topo_filename = './t85_mola_mars.nc' +topo_file = Dataset(topo_filename, 'w', format='NETCDF3_CLASSIC') +lat = topo_file.createDimension('lat', nlat) +lon = topo_file.createDimension('lon', nlon) +latitudes = topo_file.createVariable('lat','f4',('lat',)) +longitudes = topo_file.createVariable('lon','f4',('lon',)) +topo_array_netcdf = topo_file.createVariable('zsurf','f4',('lat','lon',)) +land_array_netcdf = topo_file.createVariable('land_mask','f4',('lat','lon',)) +latitudes[:] = latitudes_out +longitudes[:] = longitudes_out +topo_array_netcdf[:] = output_array +land_array_netcdf[:] = land_array +topo_file.close() + diff --git a/src/extra/python/scripts/vert_coord_options.py b/src/extra/python/scripts/vert_coord_options.py index 1ce52fecc..7bc8c77cb 100644 --- a/src/extra/python/scripts/vert_coord_options.py +++ b/src/extra/python/scripts/vert_coord_options.py @@ -58,12 +58,12 @@ def p_half_to_p_full(p_half, num_levels): if __name__ == "__main__": - num_levels = 41 #number of half levels + num_levels = 19 #number of half levels - vert_coord_option = "uneven_sigma" - surf_res = 0.5 - scale_heights = 11.0 - exponent = 7.0 + vert_coord_option = "even_sigma" + surf_res = 0.1 + scale_heights = 4.0 + exponent = 2.5 if vert_coord_option == "uneven_sigma": diff --git a/src/shared/astronomy/astronomy.f90 b/src/shared/astronomy/astronomy.f90 index e3e88854e..e63ecb434 100644 --- a/src/shared/astronomy/astronomy.f90 +++ b/src/shared/astronomy/astronomy.f90 @@ -160,11 +160,14 @@ module astronomy_mod integer :: num_angles = 3600 ! number of intervals into which the year ! is divided to compute orbital positions +logical :: use_mean_anom_in_rrsun_calc = .TRUE. !It appears the standard astronomy module uses the mean anomaly for calculating the orbital distances on eccentric orbits, rather than the true anomaly. Keeping this behaviour default for legacy, but .FALSE. seems more correct. +logical :: use_old_r_inv_squared namelist /astronomy_nml/ ecc, obliq, per, period, & year_ae, month_ae, day_ae, & hour_ae, minute_ae, second_ae, & - num_angles + num_angles, use_mean_anom_in_rrsun_calc, & + use_old_r_inv_squared !-------------------------------------------------------------------- !------ public data ---------- @@ -1122,7 +1125,7 @@ end subroutine get_ref_date_of_ae ! subroutine diurnal_solar_2d (lat, lon, gmt, time_since_ae, cosz, & fracday, rrsun, dt, allow_negative_cosz, & - half_day_out) + half_day_out, true_anom, dec_out, ang_out) !--------------------------------------------------------------------- ! diurnal_solar_2d returns 2d fields of cosine of zenith angle, @@ -1138,14 +1141,14 @@ subroutine diurnal_solar_2d (lat, lon, gmt, time_since_ae, cosz, & real, intent(in), optional :: dt logical, intent(in), optional :: allow_negative_cosz real, dimension(:,:), intent(out), optional :: half_day_out - +real, intent(out), optional :: true_anom, dec_out, ang_out !--------------------------------------------------------------------- ! local variables real, dimension(size(lat,1),size(lat,2)) :: t, tt, h, aa, bb, & st, stt, sh - real :: ang, dec + real :: dec, ang logical :: Lallow_negative !--------------------------------------------------------------------- @@ -1187,7 +1190,20 @@ subroutine diurnal_solar_2d (lat, lon, gmt, time_since_ae, cosz, & !--------------------------------------------------------------------- ang = angle(time_since_ae) dec = declination(ang) - rrsun = r_inv_squared(ang) + + if (present(ang_out)) ang_out = ang + if (present(dec_out)) dec_out = dec + + if (use_old_r_inv_squared) then + rrsun = r_inv_squared(ang) + else + if (present(true_anom)) then + call r_inv_squared_alt(ang,rrsun, true_anom) + else + call r_inv_squared_alt(ang,rrsun) + endif + + endif !--------------------------------------------------------------------- ! define terms needed in the cosine zenith angle equation. @@ -2080,7 +2096,7 @@ subroutine daily_mean_solar_2d (lat, time_since_ae, cosz, h_out, rr_out) ! local variables real, dimension(size(lat,1),size(lat,2)) :: h - real :: ang, dec, rr + real :: ang, dec, rr, ta !-------------------------------------------------------------------- ! be sure the time in the annual cycle is legitimate. @@ -2097,7 +2113,11 @@ subroutine daily_mean_solar_2d (lat, time_since_ae, cosz, h_out, rr_out) ang = angle (time_since_ae) dec = declination(ang) h = half_day (lat, dec) - rr = r_inv_squared (ang) + if (use_old_r_inv_squared) then + rr = r_inv_squared(ang) + else + call r_inv_squared_alt(ang,rr, ta) + endif !--------------------------------------------------------------------- ! where the entire day is dark, define cosz to be zero. otherwise @@ -3250,13 +3270,127 @@ function r_inv_squared (ang) ! its square (r_inv_squared) to the calling routine. !-------------------------------------------------------------------- rad_per = per*deg_to_rad - r = (1. - ecc**2)/(1. + ecc*cos(ang - rad_per)) - r_inv_squared = r**(-2) + + if (ecc.eq.0.) then + r = 1. + r_inv_squared = 1. + else + r = (1. - ecc**2)/(1. + ecc*cos(ang - rad_per)) + r_inv_squared = r**(-2) + endif + end function r_inv_squared +subroutine r_inv_squared_alt (ang, r_inv_squared_out, true_anomaly_out) + +!-------------------------------------------------------------------- +! r_inv_squared returns the inverse of the square of the earth-sun +! distance relative to the mean distance at angle ang in the earth's +! orbit. +!-------------------------------------------------------------------- + +!-------------------------------------------------------------------- +real, intent(in) :: ang +real, intent(out) :: r_inv_squared_out +real, intent(out), optional:: true_anomaly_out +!-------------------------------------------------------------------- + +!--------------------------------------------------------------------- +! +! intent(in) variables: +! +! ang angular position of earth in its orbit, relative to a +! value of 0.0 at the NH autumnal equinox, value between +! 0.0 and 2 * pi +! [ radians ] +! +!--------------------------------------------------------------------- + +!--------------------------------------------------------------------- +! local variables + + real :: r, rad_per, mean_anomaly, ecc_anomaly, true_anomaly +!--------------------------------------------------------------------- +! local variables: +! +! r_inv_squared the inverse of the square of the earth-sun +! distance relative to the mean distance +! [ dimensionless ] +! r earth-sun distance relative to mean distance +! [ dimensionless ] +! rad_per angular position of perihelion +! [ radians ] +! +!-------------------------------------------------------------------- + +!-------------------------------------------------------------------- +! define the earth-sun distance (r) and then return the inverse of +! its square (r_inv_squared) to the calling routine. +!-------------------------------------------------------------------- + rad_per = per*deg_to_rad + + if (ecc.eq.0.) then + r = 1. + r_inv_squared_out = 1. + else + + if (use_mean_anom_in_rrsun_calc) then + r = (1. - ecc**2)/(1. + ecc*cos(ang - rad_per)) + true_anomaly = ang-rad_per !This obviously isn't true, but is the approximation made by the old astronomy code. + else + mean_anomaly = ang - rad_per + call calc_ecc_anomaly(mean_anomaly, ecc, ecc_anomaly) + true_anomaly = 2*atan(((1 + ecc)/(1 - ecc))**0.5 * tan(ecc_anomaly/2)) + r = (1. - ecc**2)/(1. + ecc*cos(true_anomaly)) + endif + + r_inv_squared_out = r**(-2) + + endif + + if (present(true_anomaly_out)) then + true_anomaly_out = modulo(true_anomaly, twopi) + + endif + +end subroutine + + +!####################################################################### +!Written by Alex Paterson in hs_forcing.f90 and copied to astronomy by SIT 11th May '18 + + subroutine calc_ecc_anomaly( mean_anomaly, ecc, ecc_anomaly) + + real, intent(in) :: mean_anomaly, ecc + real, intent(out) :: ecc_anomaly + real :: dE, d + integer, parameter :: maxiter = 30 + real, parameter :: tol = 1.d-10 + integer :: k + + ecc_anomaly = mean_anomaly + d = ecc_anomaly - ecc*sin(ecc_anomaly) - mean_anomaly + do k=1,maxiter + dE = d/(1 - ecc*cos(ecc_anomaly)) + ecc_anomaly = ecc_anomaly - dE + d = ecc_anomaly - ecc*sin(ecc_anomaly) - mean_anomaly + if (abs(d) < tol) then + exit + endif + enddo + + if (k > maxiter) then + if (abs(d) > tol) then + print *, '*** Warning: eccentric anomaly has not converged' + endif + endif + + end subroutine calc_ecc_anomaly + +!################################################################### !#################################################################### @@ -3720,4 +3854,4 @@ end subroutine diurnal_exoplanet - end module astronomy_mod \ No newline at end of file + end module astronomy_mod diff --git a/src/shared/constants/constants.F90 b/src/shared/constants/constants.F90 index 42ee900a5..a598008be 100644 --- a/src/shared/constants/constants.F90 +++ b/src/shared/constants/constants.F90 @@ -94,6 +94,9 @@ module constants_mod ! Humidity factor. Controls the humidity content of the atmosphere through ! the Saturation Vapour Pressure expression when using DO_SIMPLE. ! +! +! Prefactor in version of Clausius-Clapeyron when using DO_SIMPLE. +! ! ! gas constant for water vapor ! @@ -117,13 +120,14 @@ module constants_mod ! real, public, parameter :: DEF_ES0 = 1.0 -real, public, parameter :: RVGAS = 461.50 -real, public, parameter :: CP_VAPOR = 4.0*RVGAS +real, public, parameter :: TRIPLE_POINT_PRES_WATER = 610.78 +real, public, parameter :: RVGAS_WATER = 461.50 +real, public, parameter :: CP_VAPOR_WATER = 4.0*RVGAS_WATER real, public, parameter :: DENS_H2O = 1000. -real, public, parameter :: HLV = 2.500e6 -real, public, parameter :: HLF = 3.34e5 -real, public, parameter :: HLS = HLV + HLF -real, public, parameter :: TFREEZE = 273.16 +real, public, parameter :: HLV_WATER = 2.500e6 +real, public, parameter :: HLF_WATER = 3.34e5 +real, public, parameter :: HLS_WATER = HLV_WATER + HLF_WATER +real, public, parameter :: TFREEZE_WATER = 273.16 !-------------- radiation constants ----------------- @@ -164,7 +168,7 @@ module constants_mod ! real, public, parameter :: WTMAIR = 2.896440E+01 -real, public, parameter :: WTMH2O = WTMAIR*(EARTH_RDGAS/RVGAS) !pjp OK to change value because not used yet. +real, public, parameter :: WTMH2O = WTMAIR*(EARTH_RDGAS/RVGAS_WATER) !pjp OK to change value because not used yet. !real, public, parameter :: WTMO3 = 47.99820E+01 real, public, parameter :: WTMOZONE = 47.99820 real, public, parameter :: WTMC = 12.00000 @@ -254,6 +258,7 @@ module constants_mod real, public :: RADIUS = 6376.0e3 real, public :: GRAV = EARTH_GRAV real, public :: OMEGA = EARTH_OMEGA ! planetary rotation rate in s^-1 +real, public :: ROTATION_PERIOD = -1.0 real, public :: ORBITAL_PERIOD = EARTH_ORBITAL_PERIOD ! orbital period (periapse -> periapse) in s real, public :: SECONDS_PER_SOL = SECONDS_PER_DAY real, public :: orbital_rate ! this is calculated from 2pi/orbital_period @@ -262,12 +267,22 @@ module constants_mod real, public :: PSTD = 1.013250E+06 real, public :: PSTD_MKS = PSTD_MKS_EARTH real, public :: RDGAS = EARTH_RDGAS +real, public :: RVGAS = RVGAS_WATER +real, public :: CP_VAPOR = 4.0*RVGAS_WATER +real, public :: DENS_VAPOR = DENS_H2O +real, public :: HLV = HLV_WATER +real, public :: HLF = HLF_WATER +real, public :: HLS = HLV_WATER + HLF_WATER +real, public :: TFREEZE = TFREEZE_WATER +real, public :: TRIPLE_POINT_PRES = TRIPLE_POINT_PRES_WATER real, public :: KAPPA = EARTH_KAPPA real, public :: CP_AIR = EARTH_CP_AIR real, public :: es0 = DEF_ES0 logical :: earthday_multiple = .false. -namelist/constants_nml/ radius, grav, omega, orbital_period, pstd, pstd_mks, rdgas, kappa, solar_const, earthday_multiple, es0 +namelist/constants_nml/ radius, grav, omega, orbital_period, pstd, pstd_mks, rdgas, kappa, solar_const, earthday_multiple, rotation_period, es0, & + rvgas, hlv, hlf, tfreeze, triple_point_pres, dens_vapor + !----------------------------------------------------------------------- ! version and tagname published @@ -299,6 +314,14 @@ subroutine constants_init if (mpp_pe() == mpp_root_pe()) write (stdlog(),nml=constants_nml) + if (rotation_period.gt.0) then + omega = 2.*pi/rotation_period + + call error_mesg( 'constants_init','rotation_period has been specified, so omega calculated from rotation_period', NOTE) + + endif + + !> SECONDS_PER_SOL is the exoplanet equivalent of seconds_per_day. !! It is the number of seconds between sucessive solar zeniths at longitude 0. !! (The concept of seconds_per_day is kept as seconds per earth day @@ -315,6 +338,9 @@ subroutine constants_init CP_AIR = RDGAS/KAPPA + CP_VAPOR = 4.0*RVGAS + HLS = HLV + HLF + constants_initialised = .true. end subroutine constants_init diff --git a/src/shared/mpp/affinity.c b/src/shared/mpp/affinity.c index f0eb78149..9ea3ba3a4 100644 --- a/src/shared/mpp/affinity.c +++ b/src/shared/mpp/affinity.c @@ -21,6 +21,16 @@ #define _GNU_SOURCE +#ifdef MACOS +// Mac OS doesn't permit thread pinning. So we just ignore it... +// https://developer.apple.com/library/archive/releasenotes/Performance/RN-AffinityAPI/index.html +// see also Neil's question on Stack Overflow +// https://stackoverflow.com/questions/45227009/alternative-to-shed-getaffinity-cpu-set-t-etc +int get_cpu_affinity(void) { return -1; }; +void set_cpu_affinity( int cpu ) {}; + +#else + #include #include #include @@ -64,7 +74,6 @@ int get_cpu_affinity(void) return (last_cpu == -1) ? first_cpu : -1; } -int get_cpu_affinity_(void) { return get_cpu_affinity(); } /* Fortran interface */ /* @@ -81,4 +90,7 @@ void set_cpu_affinity( int cpu ) } } +#endif + +int get_cpu_affinity_(void) { return get_cpu_affinity(); } /* Fortran interface */ void set_cpu_affinity_(int *cpu) { set_cpu_affinity(*cpu); } /* Fortran interface */ diff --git a/src/shared/sat_vapor_pres/sat_vapor_pres.F90 b/src/shared/sat_vapor_pres/sat_vapor_pres.F90 index f182831a6..a0f9b72e8 100644 --- a/src/shared/sat_vapor_pres/sat_vapor_pres.F90 +++ b/src/shared/sat_vapor_pres/sat_vapor_pres.F90 @@ -132,7 +132,7 @@ module sat_vapor_pres_mod ! Description summarizing public interface. ! - use constants_mod, only: TFREEZE, RDGAS, RVGAS, HLV, ES0 + use constants_mod, only: TFREEZE, TFREEZE_WATER, RDGAS, RVGAS, HLV, ES0, triple_point_pres use fms_mod, only: write_version_number, stdout, stdlog, mpp_pe, mpp_root_pe, & mpp_error, FATAL, fms_error_handler, open_namelist_file, & error_mesg, & @@ -2310,8 +2310,8 @@ subroutine sat_vapor_pres_init(err_msg) endif nsize = (tcmax-tcmin)*esres+1 nlim = nsize-1 - call sat_vapor_pres_init_k(nsize, real(tcmin), real(tcmax), TFREEZE, HLV, & - RVGAS, ES0, err_msg_local, use_exact_qs, do_simple, & + call sat_vapor_pres_init_k(nsize, real(tcmin), real(tcmax), TFREEZE, TFREEZE_WATER, HLV, & + RVGAS, ES0, triple_point_pres, err_msg_local, use_exact_qs, do_simple, & construct_table_wrt_liq, & construct_table_wrt_liq_and_ice, & teps, tmin, dtinv) diff --git a/src/shared/sat_vapor_pres/sat_vapor_pres_k.F90 b/src/shared/sat_vapor_pres/sat_vapor_pres_k.F90 index 1c1cded34..259874001 100644 --- a/src/shared/sat_vapor_pres/sat_vapor_pres_k.F90 +++ b/src/shared/sat_vapor_pres/sat_vapor_pres_k.F90 @@ -158,7 +158,7 @@ module sat_vapor_pres_k_mod contains - subroutine sat_vapor_pres_init_k(table_size, tcmin, tcmax, TFREEZE, HLV, RVGAS, ES0, err_msg, & + subroutine sat_vapor_pres_init_k(table_size, tcmin, tcmax, TFREEZE, TFREEZE_WATER, HLV, RVGAS, ES0, triple_point_pres, err_msg, & use_exact_qs_input, do_simple, & construct_table_wrt_liq, & construct_table_wrt_liq_and_ice, & @@ -169,7 +169,7 @@ subroutine sat_vapor_pres_init_k(table_size, tcmin, tcmax, TFREEZE, HLV, RVGAS, integer, intent(in) :: table_size real, intent(in) :: tcmin ! TABLE(1) = sat vapor pressure at temperature tcmin (deg C) real, intent(in) :: tcmax ! TABLE(table_size) = sat vapor pressure at temperature tcmax (deg C) - real, intent(in) :: TFREEZE, HLV, RVGAS, ES0 + real, intent(in) :: TFREEZE, TFREEZE_WATER, HLV, RVGAS, ES0, triple_point_pres logical, intent(in) :: use_exact_qs_input, do_simple logical, intent(in) :: construct_table_wrt_liq logical, intent(in) :: construct_table_wrt_liq_and_ice @@ -212,7 +212,7 @@ subroutine sat_vapor_pres_init_k(table_size, tcmin, tcmax, TFREEZE, HLV, RVGAS, table_siz = table_size dtres = (tcmax - tcmin)/(table_size-1) - tminl = real(tcmin)+TFREEZE ! minimum valid temp in table + tminl = real(tcmin)+TFREEZE_WATER ! minimum valid temp in table. Always use TFREEZE_WATER here, as this is just converting tcmin from celcius to kelvin. dtinvl = 1./dtres tepsl = .5*dtres tinrc = .1*dtres @@ -234,7 +234,7 @@ subroutine sat_vapor_pres_init_k(table_size, tcmin, tcmax, TFREEZE, HLV, RVGAS, do i = 1, table_size tem(1) = tminl + dtres*real(i-1) - TABLE(i) = ES0*610.78*exp(-hlv/rvgas*(1./tem(1) - 1./tfreeze)) + TABLE(i) = ES0*triple_point_pres*exp(-hlv/rvgas*(1./tem(1) - 1./tfreeze)) DTABLE(i) = hlv*TABLE(i)/rvgas/tem(1)**2. enddo