diff --git a/analysis_only.py b/analysis_only.py index 976c9b0e508d11e733d7713755a369eaa041b0a4..c1ae8b9bc8aa0f324a06d852e179b31ce57c7c8e 100755 --- a/analysis_only.py +++ b/analysis_only.py @@ -6,27 +6,17 @@ import os, sys, shutil import datetime as dt from dartwrf import utils -from config.cfg import exp -from config.clusters import cluster +from dartwrf.workflows import WorkFlows -############################### - prior_path_exp = '/mnt/jetfs/scratch/lkugler/data/sim_archive/exp_v1.19_P3_wbub7_noDA' prior_init_time = dt.datetime(2008,7,30,12) prior_valid_time = dt.datetime(2008,7,30,12,30) assim_time = prior_valid_time +w = WorkFlows(exp_config='cfg.py', server_config='srvx1.py') -cluster.setup() - -os.system( - cluster.python+' '+cluster.scripts_rundir+'/assim_synth_obs.py ' - +assim_time.strftime('%Y-%m-%d_%H:%M ') - +prior_init_time.strftime('%Y-%m-%d_%H:%M ') - +prior_valid_time.strftime('%Y-%m-%d_%H:%M ') - +prior_path_exp - ) +w.assimilate(assim_time, prior_init_time, prior_valid_time, prior_path_exp) diff --git a/config/cfg.py b/config/cfg.py index f70d03450ac5d6083efa2812e1b1e1f0efad6a89..46ea5e79ed96bff447c0cc862e01b85d9f09aea8 100755 --- a/config/cfg.py +++ b/config/cfg.py @@ -1,13 +1,13 @@ from dartwrf import utils -exp = utils.ExperimentConfiguration() -exp.expname = "exp_v1.22_P2_rr_VIS_obs10_loc20_inf5" +exp = utils.Experiment() +exp.expname = "test_newcode" exp.model_dx = 2000 -exp.n_ens = 40 +exp.n_ens = 10 exp.filter_kind = 1 -exp.prior_inflation = 5 -exp.post_inflation = 0 +exp.prior_inflation = 0 +exp.post_inflation = 4 exp.sec = True exp.reject_smallFGD = False exp.cov_loc_vert_km_horiz_km = (3, 20) @@ -20,13 +20,10 @@ exp.use_existing_obsseq = False # False or pathname (use precomputed obs_seq.ou #exp.use_existing_obsseq = '/gpfs/data/fs71386/lkugler/sim_archive/exp_v1.21_P3_wbub7_REFL2D_obs10_loc20_oe5/obs_seq_out/2008-07-30_%H:%M_obs_seq.out' #exp.use_existing_obsseq = '/gpfs/data/fs71386/lkugler/sim_archive/exp_v1.21_P2_rr_VIS_obs20_loc4/obs_seq_out/2008-07-30_%H:%M_obs_seq.out' -#exp.nature_wrfout = '/home/fs71386/lkugler/data/sim_archive/exp_v1.19_P5+su_nat2/2008-07-30_07:00/1/wrfout_d01_%Y-%m-%d_%H:%M:%S' -#exp.nature_wrfout = '/jetfs/home/lkugler/data/sim_archive/exp_v1.19_P3_wbub7_nat/2008-07-30_12:00/1/wrfout_d01_%Y-%m-%d_%H:%M:%S' -#exp.nature_wrfout = '/home/fs71386/lkugler/data/sim_archive/exp_v1.19_Pwbub5_nat/2008-07-30_12:00/1/wrfout_d01_%Y-%m-%d_%H:%M:%S' -exp.nature_wrfout = '/jetfs/home/lkugler/data/sim_archive/exp_v1.18_P1_nature/2008-07-30_06:00/1/wrfout_d01_%Y-%m-%d_%H:%M:%S' -#exp.nature_wrfout = '/home/fs71386/lkugler/data/sim_archive/exp_v1.19_P4_nat/2008-07-30_07:00/1/wrfout_d01_%Y-%m-%d_%H:%M:%S' +#exp.nature = '/mnt/jetfs/scratch/lkugler/data/sim_archive/exp_v1.19_P3_wbub7_nat/2008-07-30_12:00/1' +exp.nature = '/mnt/jetfs/scratch/lkugler/data/sim_archive/exp_v1.18_P1_nature/2008-07-30_06:00/1' -exp.input_profile = '/jetfs/home/lkugler/data/initial_profiles/wrf/ens/2022-03-31/raso.fc.<iens>.wrfprof' +exp.input_profile = '/mnt/jetfs/home/lkugler/data/initial_profiles/wrf/ens/2022-03-31/raso.fc.<iens>.wrfprof' #exp.input_profile = '/gpfs/data/fs71386/lkugler/initial_profiles/wrf/ens/2022-03-31/raso.nat.<iens>.wrfprof' #exp.input_profile = '/gpfs/data/fs71386/lkugler/initial_profiles/wrf/ens/2022-05-18/raso.fc.<iens>.wrfprof' @@ -71,11 +68,11 @@ radar = dict(plotname='Radar reflectivity', plotunits='[dBz]', t = dict(plotname='Temperature', plotunits='[K]', kind='RADIOSONDE_TEMPERATURE', - n_obs=22500, obs_locations='square_array_evenly_on_grid', - # n_obs=1, obs_locations=[(45., 0.)], + #n_obs=22500, obs_locations='square_array_evenly_on_grid', + n_obs=1, obs_locations=[(45., 0.)], error_generate=0.2, error_assimilate=0.2, heights=[1000,], #range(1000, 17001, 2000), - cov_loc_radius_km=1.5) + cov_loc_radius_km=50) q = dict(plotname='Specific humidity', plotunits='[kg/kg]', kind='RADIOSONDE_SPECIFIC_HUMIDITY', n_obs=1, @@ -93,7 +90,7 @@ psfc = dict(plotname='SYNOP Pressure', plotunits='[Pa]', error_generate=50., error_assimilate=100., cov_loc_radius_km=32) -exp.observations = [vis] +exp.observations = [t] exp.update_vars = ['U', 'V', 'W', 'THM', 'PH', 'MU', 'QVAPOR', 'QCLOUD', 'QICE', 'PSFC'] #exp.update_vars = ['U', 'V', 'W', 'T', 'PH', 'MU', 'QVAPOR', 'PSFC'] diff --git a/config/clusters.py b/config/clusters.py deleted file mode 100755 index f87748a208751a3589977cfe5ac2f25d946de739..0000000000000000000000000000000000000000 --- a/config/clusters.py +++ /dev/null @@ -1,155 +0,0 @@ -import os, sys -import datetime as dt -from dartwrf import utils -from config.cfg import exp - -"""Configuration name docs - -When coding, use attributes of a dictionary like this: -$ from cfg import exp, cluster -$ path = cluster.archivedir - - -attribute name | description ------------------------------------------------------- -name any string (currently unused) - -python path of python version to use -python_enstools path of python version to use for verification script (not provided) -ncks path to 'ncks' program; type 'which ncks' to find the path, - if it doesn't exist, try to load the module first ('module load nco') -ideal path to WRF's ideal.exe -wrfexe path to WRF's wrf.exe - -wrf_rundir_base path for temporary files for WRF -dart_rundir_base path for temporary files for DART -archive_base path for long-time output storage - -srcdir path to where WRF has been compiled, including the 'run' folder of WRF, e.g. /home/WRF-4.3/run -dart_srcdir path to DART compile directory, e.g. /home/DART-9.11.9/models/wrf/work -rttov_srcdir path to RTTOV compile directory, e.g. /home/RTTOV13/rtcoef_rttov13/ -scriptsdir path where DART-WRF scripts reside, e.g. /home/DART-WRF/scripts - -namelist path to a namelist template; strings like <hist_interval>, will be overwritten in scripts/prepare_namelist.py -run_WRF path to script which runs WRF on a node of the cluster -obs_impact_filename path to obs_impact_filename (see DART guide; module assim_tools_mod and program obs_impact_tool) -geo_em path to NetCDF file of WRF domain (see WRF guide) - -slurm_cfg python dictionary, containing options of SLURM - defined in SLURM docs (https://slurm.schedmd.com/sbatch.html) - this configuration can be overwritten later on, for example: - 'dict(cluster.slurm_cfg, **cfg_update)' where - 'cfg_update = {"nodes": "2"}' -""" - - -vsc = utils.ClusterConfig(exp) -vsc.name = 'vsc' -vsc.max_nproc = 20 -vsc.size_jobarray = 10 # 10 jobs with each 4 WRF processes per node -vsc.use_slurm = True - -# binaries -vsc.python = '/home/fs71386/lkugler/miniconda3/envs/DART/bin/python' -vsc.python_enstools = '/home/fs71386/lkugler/miniconda3/envs/enstools/bin/python' -vsc.ncks = '/home/fs71386/lkugler/miniconda3/envs/DART/bin/ncks' -vsc.ideal = '/home/fs71386/lkugler/compile/bin/ideal-v4.2.2_v1.22.exe' -vsc.wrfexe = '/home/fs71386/lkugler/compile/bin/wrf-v4.3_v1.22.exe' -vsc.container = '/home/fs71386/lkugler/run_container.sh python.gcc9.5.0.vsc4.sif' - -# paths for data output -vsc.wrf_rundir_base = '/gpfs/data/fs71386/lkugler/run_WRF/' # path for temporary files -vsc.dart_rundir_base = '/gpfs/data/fs71386/lkugler/run_DART/' # path for temporary files -vsc.archive_base = '/gpfs/data/fs71386/lkugler/sim_archive/' - -# paths used as input -vsc.srcdir = '/gpfs/data/fs71386/lkugler/compile/WRF/WRF-4.3/run' -vsc.dart_srcdir = '/gpfs/data/fs71386/lkugler/compile/DART/DART/models/wrf/work' -vsc.rttov_srcdir = '/gpfs/data/fs71386/lkugler/compile/RTTOV13/rtcoef_rttov13/' -vsc.scriptsdir = '/home/fs71386/lkugler/DART-WRF/dartwrf/' - -# templates/run scripts -vsc.namelist = vsc.scriptsdir+'/../templates/namelist.input' -vsc.run_WRF = '/home/fs71386/lkugler/DART-WRF/dartwrf/run_ens.vsc.sh' - -vsc.slurm_cfg = {"account": "p71386", "partition": "skylake_0384", "qos": "p71386_0384", - "nodes": "1", "ntasks": "1", "ntasks-per-node": "48", "ntasks-per-core": "1", - "mail-type": "FAIL", "mail-user": "lukas.kugler@univie.ac.at"} - -jet = utils.ClusterConfig(exp) -jet.name = 'jet' -jet.max_nproc = 12 -jet.use_slurm = True -jet.size_jobarray = 40 - -# binaries -jet.python = '/jetfs/home/lkugler/miniconda3/envs/DART/bin/python' -jet.python_verif = '/jetfs/home/lkugler/miniconda3/envs/enstools/bin/python' -jet.ncks = '/jetfs/spack/opt/spack/linux-rhel8-skylake_avx512/intel-20.0.2/nco-4.9.3-dhlqiyog7howjmaleyfhm6lkt7ra37xf/bin/ncks' -jet.ideal = '/jetfs/home/lkugler/bin/ideal-v4.3_v1.22.exe' -jet.wrfexe = '/jetfs/home/lkugler/bin/wrf-v4.3_v1.22.exe' -jet.container = '' - -# paths for data output -jet.wrf_rundir_base = '/jetfs/home/lkugler/data/run_WRF/' # path for temporary files -jet.dart_rundir_base = '/jetfs/home/lkugler/data/run_DART/' # path for temporary files -jet.archive_base = '/jetfs/home/lkugler/data/sim_archive/' - -# paths used as input -jet.srcdir = '/jetfs/home/lkugler/data/compile/WRF-4.3/run' -jet.dart_srcdir = '/jetfs/home/lkugler/data/compile/DART/DART-10.5.3/models/wrf/work' -jet.rttov_srcdir = '/jetfs/home/lkugler/data/compile/RTTOV13/rtcoef_rttov13/' -jet.scriptsdir = '/jetfs/home/lkugler/DART-WRF/dartwrf/' - -# other inputs -jet.geo_em = '/jetfs/home/lkugler/data/geo_em.d01.nc' -jet.obs_impact_filename = jet.scriptsdir+'/../templates/impactfactor_T.txt' -jet.namelist = jet.scriptsdir+'/../templates/namelist.input' -jet.run_WRF = '/jetfs/home/lkugler/DART-WRF/dartwrf/run_ens.jet.sh' - -jet.slurm_cfg = {"account": "lkugler", "partition": "compute", #"nodelist": "jet07", - "ntasks": "1", "ntasks-per-core": "1", "mem": "50G", - "mail-type": "FAIL", "mail-user": "lukas.kugler@univie.ac.at"} - - -srvx1 = utils.ClusterConfig(exp) -srvx1.name = 'srvx1' -srvx1.max_nproc = 6 -srvx1.use_slurm = False - -# binaries -srvx1.python = '/users/staff/lkugler/miniconda3/bin/python' -srvx1.python_verif = '/users/staff/lkugler/miniconda3/bin/python' -srvx1.ncks = '/home/swd/spack/opt/spack/linux-rhel8-skylake_avx512/gcc-8.5.0/nco-5.0.1-ntu44aoxlvwtr2tsrobfr4lht7cpvccf/bin/ncks' -srvx1.ideal = '' #/jetfs/home/lkugler/bin/ideal-v4.3_v1.22.exe' -srvx1.wrfexe = '' #/jetfs/home/lkugler/bin/wrf-v4.3_v1.22.exe' -srvx1.container = '' - -# paths for data output -srvx1.wrf_rundir_base = '/mnt/jetfs/home/lkugler/data/run_WRF/' # path for temporary files -srvx1.dart_rundir_base = '/users/staff/lkugler/AdvDA23/run_DART/' # path for temporary files -srvx1.archive_base = '/mnt/jetfs/scratch/lkugler/data/sim_archive/' - -# paths used as input -srvx1.srcdir = '/users/staff/lkugler/AdvDA23/DART/WRF-4.3/run' -srvx1.dart_srcdir = '/users/staff/lkugler/AdvDA23/DART/models/wrf/work' -srvx1.rttov_srcdir = '/users/staff/lkugler/AdvDA23/RTTOV13/rtcoef_rttov13/' -srvx1.scriptsdir = '/users/staff/lkugler/AdvDA23/DART-WRF/dartwrf/' -srvx1.geo_em = '/mnt/jetfs/scratch/lkugler/data/geo_em.d01.nc' - -# templates/run scripts -srvx1.namelist = srvx1.scriptsdir+'/../templates/namelist.input' -srvx1.run_WRF = srvx1.scriptsdir+'/run_ens.jet.sh' - -srvx1.slurm_cfg = {"account": "lkugler", "partition": "compute", - "ntasks": "1", "ntasks-per-core": "1", "mem": "50G", - "mail-type": "FAIL", "mail-user": "lukas.kugler@univie.ac.at"} - -################################# -# select cluster configuration -if 'jet' in os.uname().nodename: - cluster = jet -elif 'srvx1' in os.uname().nodename: - cluster = srvx1 -else: - cluster = vsc diff --git a/config/jet.py b/config/jet.py new file mode 100755 index 0000000000000000000000000000000000000000..96cffd303bdaede50016e6cfc12899640d70ff0a --- /dev/null +++ b/config/jet.py @@ -0,0 +1,79 @@ +import os, sys +import datetime as dt +from dartwrf import utils +from config.cfg import exp + +"""Configuration name docs + +When coding, use attributes of a dictionary like this: +$ from cfg import exp, cluster +$ path = cluster.archivedir + + +attribute name | description +------------------------------------------------------ +name any string (currently unused) + +python path of python version to use +python_enstools path of python version to use for verification script (not provided) +ncks path to 'ncks' program; type 'which ncks' to find the path, + if it doesn't exist, try to load the module first ('module load nco') +ideal path to WRF's ideal.exe +wrfexe path to WRF's wrf.exe + +wrf_rundir_base path for temporary files for WRF +dart_rundir_base path for temporary files for DART +archive_base path for long-time output storage + +srcdir path to where WRF has been compiled, including the 'run' folder of WRF, e.g. /home/WRF-4.3/run +dart_srcdir path to DART compile directory, e.g. /home/DART-9.11.9/models/wrf/work +rttov_srcdir path to RTTOV compile directory, e.g. /home/RTTOV13/rtcoef_rttov13/ +scriptsdir path where DART-WRF scripts reside, e.g. /home/DART-WRF/scripts + +namelist path to a namelist template; strings like <hist_interval>, will be overwritten in scripts/prepare_namelist.py +run_WRF path to script which runs WRF on a node of the cluster +obs_impact_filename path to obs_impact_filename (see DART guide; module assim_tools_mod and program obs_impact_tool) +geo_em path to NetCDF file of WRF domain (see WRF guide) + +slurm_cfg python dictionary, containing options of SLURM + defined in SLURM docs (https://slurm.schedmd.com/sbatch.html) + this configuration can be overwritten later on, for example: + 'dict(cluster.slurm_cfg, **cfg_update)' where + 'cfg_update = {"nodes": "2"}' +""" + + +cluster = utils.ClusterConfig(exp) +cluster.name = 'jet' +cluster.max_nproc = 12 +cluster.use_slurm = True +cluster.size_jobarray = 40 + +# binaries +cluster.python = '/jetfs/home/lkugler/miniconda3/envs/DART/bin/python' +cluster.python_verif = '/jetfs/home/lkugler/miniconda3/envs/enstools/bin/python' +cluster.ncks = '/jetfs/spack/opt/spack/linux-rhel8-skylake_avx512/intel-20.0.2/nco-4.9.3-dhlqiyog7howjmaleyfhm6lkt7ra37xf/bin/ncks' +cluster.ideal = '/jetfs/home/lkugler/bin/ideal-v4.3_v1.22.exe' +cluster.wrfexe = '/jetfs/home/lkugler/bin/wrf-v4.3_v1.22.exe' +cluster.container = '' + +# paths for data output +cluster.wrf_rundir_base = '/jetfs/home/lkugler/data/run_WRF/' # path for temporary files +cluster.dart_rundir_base = '/jetfs/home/lkugler/data/run_DART/' # path for temporary files +cluster.archive_base = '/jetfs/home/lkugler/data/sim_archive/' + +# paths used as input +cluster.srcdir = '/jetfs/home/lkugler/data/compile/WRF-4.3/run' +cluster.dart_srcdir = '/jetfs/home/lkugler/data/compile/DART/DART-10.5.3/models/wrf/work' +cluster.rttov_srcdir = '/jetfs/home/lkugler/data/compile/RTTOV13/rtcoef_rttov13/' +cluster.scriptsdir = '/jetfs/home/lkugler/DART-WRF/dartwrf/' + +# other inputs +cluster.geo_em = '/jetfs/home/lkugler/data/geo_em.d01.nc' +cluster.obs_impact_filename = cluster.scriptsdir+'/../templates/impactfactor_T.txt' +cluster.namelist = cluster.scriptsdir+'/../templates/namelist.input' +cluster.run_WRF = '/jetfs/home/lkugler/DART-WRF/dartwrf/run_ens.jet.sh' + +cluster.slurm_cfg = {"account": "lkugler", "partition": "compute", #"nodelist": "jet07", + "ntasks": "1", "ntasks-per-core": "1", "mem": "50G", + "mail-type": "FAIL", "mail-user": "lukas.kugler@univie.ac.at"} diff --git a/config/srvx1.py b/config/srvx1.py new file mode 100755 index 0000000000000000000000000000000000000000..9a60872f55e30e96ebdaf1fdf0ef4495dc740071 --- /dev/null +++ b/config/srvx1.py @@ -0,0 +1,76 @@ +import os, sys +import datetime as dt +from dartwrf import utils +from config.cfg import exp + +"""Configuration name docs + +When coding, use attributes of a dictionary like this: +$ from cfg import exp, cluster +$ path = cluster.archivedir + + +attribute name | description +------------------------------------------------------ +name any string (currently unused) + +python path of python version to use +python_enstools path of python version to use for verification script (not provided) +ncks path to 'ncks' program; type 'which ncks' to find the path, + if it doesn't exist, try to load the module first ('module load nco') +ideal path to WRF's ideal.exe +wrfexe path to WRF's wrf.exe + +wrf_rundir_base path for temporary files for WRF +dart_rundir_base path for temporary files for DART +archive_base path for long-time output storage + +srcdir path to where WRF has been compiled, including the 'run' folder of WRF, e.g. /home/WRF-4.3/run +dart_srcdir path to DART compile directory, e.g. /home/DART-9.11.9/models/wrf/work +rttov_srcdir path to RTTOV compile directory, e.g. /home/RTTOV13/rtcoef_rttov13/ +scriptsdir path where DART-WRF scripts reside, e.g. /home/DART-WRF/scripts + +namelist path to a namelist template; strings like <hist_interval>, will be overwritten in scripts/prepare_namelist.py +run_WRF path to script which runs WRF on a node of the cluster +obs_impact_filename path to obs_impact_filename (see DART guide; module assim_tools_mod and program obs_impact_tool) +geo_em path to NetCDF file of WRF domain (see WRF guide) + +slurm_cfg python dictionary, containing options of SLURM + defined in SLURM docs (https://slurm.schedmd.com/sbatch.html) + this configuration can be overwritten later on, for example: + 'dict(cluster.slurm_cfg, **cfg_update)' where + 'cfg_update = {"nodes": "2"}' +""" + +cluster = utils.ClusterConfig(exp) +cluster.name = 'srvx1' +cluster.max_nproc = 6 +cluster.use_slurm = False + +# binaries +cluster.python = '/users/staff/lkugler/miniconda3/bin/python' +cluster.python_verif = '/users/staff/lkugler/miniconda3/bin/python' +cluster.ncks = '/home/swd/spack/opt/spack/linux-rhel8-skylake_avx512/gcc-8.5.0/nco-5.0.1-ntu44aoxlvwtr2tsrobfr4lht7cpvccf/bin/ncks' +cluster.ideal = '' #/jetfs/home/lkugler/bin/ideal-v4.3_v1.22.exe' +cluster.wrfexe = '' #/jetfs/home/lkugler/bin/wrf-v4.3_v1.22.exe' +cluster.container = '' + +# paths for data output +cluster.wrf_rundir_base = '/users/staff/lkugler/AdvDA23/run_WRF/' # path for temporary files +cluster.dart_rundir_base = '/users/staff/lkugler/AdvDA23/run_DART/' # path for temporary files +cluster.archive_base = '/mnt/jetfs/scratch/lkugler/data/sim_archive/' + +# paths used as input +cluster.srcdir = '/users/staff/lkugler/AdvDA23/DART/WRF-4.3/run' +cluster.dart_srcdir = '/users/staff/lkugler/AdvDA23/DART/models/wrf/work' +cluster.rttov_srcdir = '/users/staff/lkugler/AdvDA23/RTTOV13/rtcoef_rttov13/' +cluster.scriptsdir = '/users/staff/lkugler/AdvDA23/DART-WRF/dartwrf/' +cluster.geo_em = '/mnt/jetfs/scratch/lkugler/data/geo_em.d01.nc' + +# templates/run scripts +cluster.namelist = cluster.scriptsdir+'/../templates/namelist.input' +cluster.run_WRF = cluster.scriptsdir+'/run_ens.jet.sh' + +cluster.slurm_cfg = {"account": "lkugler", "partition": "compute", + "ntasks": "1", "ntasks-per-core": "1", "mem": "50G", + "mail-type": "FAIL", "mail-user": "lukas.kugler@univie.ac.at"} diff --git a/config/vsc.py b/config/vsc.py new file mode 100755 index 0000000000000000000000000000000000000000..7f58398014db1ee1954a04799978c3dd309c06a3 --- /dev/null +++ b/config/vsc.py @@ -0,0 +1,77 @@ +import os, sys +import datetime as dt +from dartwrf import utils +from config.cfg import exp + +"""Configuration name docs + +When coding, use attributes of a dictionary like this: +$ from cfg import exp, cluster +$ path = cluster.archivedir + + +attribute name | description +------------------------------------------------------ +name any string (currently unused) + +python path of python version to use +python_enstools path of python version to use for verification script (not provided) +ncks path to 'ncks' program; type 'which ncks' to find the path, + if it doesn't exist, try to load the module first ('module load nco') +ideal path to WRF's ideal.exe +wrfexe path to WRF's wrf.exe + +wrf_rundir_base path for temporary files for WRF +dart_rundir_base path for temporary files for DART +archive_base path for long-time output storage + +srcdir path to where WRF has been compiled, including the 'run' folder of WRF, e.g. /home/WRF-4.3/run +dart_srcdir path to DART compile directory, e.g. /home/DART-9.11.9/models/wrf/work +rttov_srcdir path to RTTOV compile directory, e.g. /home/RTTOV13/rtcoef_rttov13/ +scriptsdir path where DART-WRF scripts reside, e.g. /home/DART-WRF/scripts + +namelist path to a namelist template; strings like <hist_interval>, will be overwritten in scripts/prepare_namelist.py +run_WRF path to script which runs WRF on a node of the cluster +obs_impact_filename path to obs_impact_filename (see DART guide; module assim_tools_mod and program obs_impact_tool) +geo_em path to NetCDF file of WRF domain (see WRF guide) + +slurm_cfg python dictionary, containing options of SLURM + defined in SLURM docs (https://slurm.schedmd.com/sbatch.html) + this configuration can be overwritten later on, for example: + 'dict(cluster.slurm_cfg, **cfg_update)' where + 'cfg_update = {"nodes": "2"}' +""" + + +cluster = utils.ClusterConfig(exp) +cluster.name = 'VSC' +cluster.max_nproc = 20 +cluster.size_jobarray = 10 # 10 jobs with each 4 WRF processes per node +cluster.use_slurm = True + +# binaries +cluster.python = '/home/fs71386/lkugler/miniconda3/envs/DART/bin/python' +cluster.python_enstools = '/home/fs71386/lkugler/miniconda3/envs/enstools/bin/python' +cluster.ncks = '/home/fs71386/lkugler/miniconda3/envs/DART/bin/ncks' +cluster.ideal = '/home/fs71386/lkugler/compile/bin/ideal-v4.2.2_v1.22.exe' +cluster.wrfexe = '/home/fs71386/lkugler/compile/bin/wrf-v4.3_v1.22.exe' +cluster.container = '/home/fs71386/lkugler/run_container.sh python.gcc9.5.0.vsc4.sif' + +# paths for data output +cluster.wrf_rundir_base = '/gpfs/data/fs71386/lkugler/run_WRF/' # path for temporary files +cluster.dart_rundir_base = '/gpfs/data/fs71386/lkugler/run_DART/' # path for temporary files +cluster.archive_base = '/gpfs/data/fs71386/lkugler/sim_archive/' + +# paths used as input +cluster.srcdir = '/gpfs/data/fs71386/lkugler/compile/WRF/WRF-4.3/run' +cluster.dart_srcdir = '/gpfs/data/fs71386/lkugler/compile/DART/DART/models/wrf/work' +cluster.rttov_srcdir = '/gpfs/data/fs71386/lkugler/compile/RTTOV13/rtcoef_rttov13/' +cluster.scriptsdir = '/home/fs71386/lkugler/DART-WRF/dartwrf/' + +# templates/run scripts +cluster.namelist = cluster.scriptsdir+'/../templates/namelist.input' +cluster.run_WRF = '/home/fs71386/lkugler/DART-WRF/dartwrf/run_ens.vsc.sh' + +cluster.slurm_cfg = {"account": "p71386", "partition": "skylake_0384", "qos": "p71386_0384", + "nodes": "1", "ntasks": "1", "ntasks-per-node": "48", "ntasks-per-core": "1", + "mail-type": "FAIL", "mail-user": "lukas.kugler@univie.ac.at"} diff --git a/dartwrf/assim_synth_obs.py b/dartwrf/assim_synth_obs.py index 53306f8be75bca76297ce38e9cfd7390372d103e..2b57fadbfb40ee55c56aae5b852c77f053fab4bb 100755 --- a/dartwrf/assim_synth_obs.py +++ b/dartwrf/assim_synth_obs.py @@ -3,15 +3,16 @@ import time as time_module import datetime as dt import numpy as np -from config.cfg import exp -from config.clusters import cluster from dartwrf.utils import symlink, copy, sed_inplace, append_file, mkdir, try_remove, print, shell from dartwrf.obs import error_models as err import dartwrf.create_obsseq as osq from dartwrf import wrfout_add_geo from dartwrf import obsseq +from config.cfg import exp +from config.cluster import cluster earth_radius_km = 6370 +wrfout_format = 'wrfout_d01_%Y-%m-%d_%H:%M:%S' def set_DART_nml(just_prior_values=False): @@ -71,14 +72,21 @@ def set_DART_nml(just_prior_values=False): def link_nature_to_dart_truth(time): + """Set a symlink from the WRFout file to be used as nature to the run_DART folder + + Args: + time (dt.datetime): Time at which observations will be made + """ + # get wrfout_d01 from nature run - shutil.copy(time.strftime(exp.nature_wrfout), cluster.dartrundir + "/wrfout_d01") + shutil.copy(time.strftime(exp.nature+'/'+wrfout_format), + cluster.dartrundir + "/wrfout_d01") + # DART may need a wrfinput file as well, which serves as a template for dimension sizes - symlink(cluster.dartrundir + "/wrfout_d01", cluster.dartrundir + "/wrfinput_d01") - print( - "linked", time.strftime(exp.nature_wrfout), - "to", cluster.dartrundir + "/wrfout_d01", - ) + symlink(cluster.dartrundir + "/wrfout_d01", + cluster.dartrundir + "/wrfinput_d01") + print("linked", time.strftime(exp.nature+'/'+wrfout_format), + "to", cluster.dartrundir + "/wrfout_d01") def prepare_nature_dart(time): @@ -103,7 +111,7 @@ def prepare_prior_ensemble(assim_time, prior_init_time, prior_valid_time, prior_ prior_path_exp + prior_init_time.strftime("/%Y-%m-%d_%H:%M/") + str(iens) - + prior_valid_time.strftime("/wrfout_d01_%Y-%m-%d_%H:%M:%S") + + prior_valid_time.strftime("/"+wrfout_format) ) dart_ensdir = cluster.dartrundir + "/prior_ens" + str(iens) wrfout_dart = dart_ensdir + "/wrfout_d01" @@ -484,27 +492,25 @@ def main(time, prior_init_time, prior_valid_time, prior_path_exp): print("prepare prior ensemble") prepare_prior_ensemble(time, prior_init_time, prior_valid_time, prior_path_exp) - ################################################ print(" 1) get observations with specified obs-error") oso = get_obsseq_out(time) - ################################################ - print('3.1) evaluate prior') # evaluate prior observations for all locations + print(" 2.1) evaluate prior for all observations (incl rejected)") osf_prior = evaluate(time, output_format="%Y-%m-%d_%H:%M_obs_seq.final-eval_prior_allobs") - print(" 3.2) assign observation-errors for assimilation ") + print(" 2.2) assign observation-errors for assimilation ") set_obserr_assimilate_in_obsseqout(oso, osf_prior, outfile=cluster.dartrundir + "/obs_seq.out") if getattr(exp, "reject_smallFGD", False): - print(" 3.3) reject observations? ") + print(" 2.3) reject observations? ") qc_obs(time, oso, osf_prior) - print(" 3.4) assimilate (run filter) ") + print(" 3) run filter ") set_DART_nml() filter(nproc=nproc) archive_filteroutput(time) - # evaluate posterior observations for all locations + print(" 4) evaluate posterior observations for all observations (incl rejected)") write_list_of_inputfiles_posterior(time) if getattr(exp, "reject_smallFGD", False): copy(cluster.archivedir+'/obs_seq_out/'+time.strftime('%Y-%m-%d_%H:%M_obs_seq.out-beforeQC'), diff --git a/dartwrf/create_obs_upfront.py b/dartwrf/create_obs_upfront.py index 3e107cf3e980326248056d6420de3a1c4e409faf..3ccd5a2e2f9e0c4e1ace1cfe35f4790d1be52ed2 100755 --- a/dartwrf/create_obs_upfront.py +++ b/dartwrf/create_obs_upfront.py @@ -5,7 +5,7 @@ import datetime as dt import numpy as np from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster from dartwrf.utils import copy, print import dartwrf.create_obsseq as osq from dartwrf import obsseq @@ -24,9 +24,12 @@ if __name__ == "__main__": for tstr in args: times.append(dt.datetime.strptime(tstr, tformat)) - dir_for_obsseqout = exp.nature_wrfout + '/../../../obs_seq_out/'+exp.use_existing_obsseq - os.makedirs(dir_for_obsseqout, exist_ok=True) # create directory to run DART in + # strange path? + # dir_for_obsseqout = exp.nature_wrfout + '/../../../obs_seq_out/'+exp.use_existing_obsseq + raise NotImplementedError('where to save obsseq to?') + dir_for_obsseqout = '' # TODO: solve this when necessary print('will save obsseq to', dir_for_obsseqout) + os.makedirs(dir_for_obsseqout, exist_ok=True) os.chdir(cluster.dartrundir) diff --git a/dartwrf/create_obsseq.py b/dartwrf/create_obsseq.py index 6caaf7e6f4b6d9d03c1a0ccc35645875f08a42c6..1789a9ec0038b847f3f0fd4410ed19af69a93893 100755 --- a/dartwrf/create_obsseq.py +++ b/dartwrf/create_obsseq.py @@ -8,7 +8,7 @@ import datetime as dt from pysolar.solar import get_altitude, get_azimuth from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster from dartwrf.obs import calculate_obs_locations as col def obskind_read(): diff --git a/dartwrf/create_wbubble_wrfinput.py b/dartwrf/create_wbubble_wrfinput.py index 5aed424644d76387bda65013cc71bb02edeaedfd..f586102f9189f3fca14d07ce056b85628417cc02 100644 --- a/dartwrf/create_wbubble_wrfinput.py +++ b/dartwrf/create_wbubble_wrfinput.py @@ -4,7 +4,7 @@ import datetime as dt import numpy as np from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster import netCDF4 as nc dx_km = 2 diff --git a/dartwrf/evaluate_posterior.py b/dartwrf/evaluate_posterior.py index dcc40b581735566c71d184ab0b2d12a4a500fbfc..59c5c028ced3b31d1ba26cf11fd761da82eac5cd 100755 --- a/dartwrf/evaluate_posterior.py +++ b/dartwrf/evaluate_posterior.py @@ -4,7 +4,7 @@ import datetime as dt import numpy as np from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster from dartwrf import assim_synth_obs as aso diff --git a/dartwrf/evaluate_prior.py b/dartwrf/evaluate_prior.py index f7d63c7142a4f1d09e0c56b11d1d2c0ab8b61883..21751acd2be1c8bf29e21db7907462325858d256 100755 --- a/dartwrf/evaluate_prior.py +++ b/dartwrf/evaluate_prior.py @@ -4,7 +4,7 @@ import datetime as dt import numpy as np from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster from dartwrf.utils import symlink, copy, sed_inplace, append_file, mkdir, try_remove, print, shell from dartwrf import assim_synth_obs as aso diff --git a/dartwrf/link_dart_rttov.py b/dartwrf/link_dart_rttov.py index 3eddcefa4704f97339fc78f7474845606c4a393b..6c733554ec5cb7b26569c8a9e591515b8e873ab8 100644 --- a/dartwrf/link_dart_rttov.py +++ b/dartwrf/link_dart_rttov.py @@ -1,6 +1,6 @@ import os from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster from utils import symlink, copy_scp_srvx8, copy, sed_inplace joinp = os.path.join diff --git a/dartwrf/obs/calculate_obs_locations.py b/dartwrf/obs/calculate_obs_locations.py index 1245cb5e9695f22756a727914938d2ad8f01c3f2..43e09c954a79752dc9de7ea1b320c4f1790b9fb3 100755 --- a/dartwrf/obs/calculate_obs_locations.py +++ b/dartwrf/obs/calculate_obs_locations.py @@ -8,7 +8,7 @@ import datetime as dt import xarray as xr from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster ##################### # Global variables diff --git a/dartwrf/obsseq.py b/dartwrf/obsseq.py index 0e3621613f464e4749fbc238f215e5ffdd343865..a5b02641f849d7be1af408d31795f9eeb74f307f 100755 --- a/dartwrf/obsseq.py +++ b/dartwrf/obsseq.py @@ -8,7 +8,7 @@ import numpy as np import pandas as pd from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster from dartwrf.utils import symlink, copy, sed_inplace, append_file, mkdir, try_remove diff --git a/dartwrf/obsseq_2dim.py b/dartwrf/obsseq_2dim.py index f4c675abd519c51f94b4894e4d0b713f598fbe01..966d709c963f6801689128544c995d17c6280303 100755 --- a/dartwrf/obsseq_2dim.py +++ b/dartwrf/obsseq_2dim.py @@ -9,7 +9,7 @@ import datetime as dt import numpy as np from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster from dartwrf import assim_synth_obs as aso from dartwrf import obsseq diff --git a/dartwrf/obsseq_to_netcdf.py b/dartwrf/obsseq_to_netcdf.py index fe3cc4a1ba7fb9c26c7c107d2fb14f8da4010e13..40765952cc46edf231b0ce306fdf00ddacdd36f6 100644 --- a/dartwrf/obsseq_to_netcdf.py +++ b/dartwrf/obsseq_to_netcdf.py @@ -1,7 +1,7 @@ import os, sys, glob, warnings from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster import run_obs_diag as rod def listdir_dirs(path): diff --git a/dartwrf/prep_IC_prior.py b/dartwrf/prep_IC_prior.py index 15e57e960fb293c2da8944fa64849ed9fdc635f8..c832ffd596071f9a98f16682385f693b0836a32e 100755 --- a/dartwrf/prep_IC_prior.py +++ b/dartwrf/prep_IC_prior.py @@ -3,7 +3,7 @@ import datetime as dt import numpy as np from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster from utils import copy, clean_wrfdir, try_remove """ diff --git a/dartwrf/prepare_namelist.py b/dartwrf/prepare_namelist.py index ac2ff1a55ce252f593796eb46b375bdf433e0823..8b5a05e92fe0f8a6087fc8fb6e4b70eb2d08b8cd 100755 --- a/dartwrf/prepare_namelist.py +++ b/dartwrf/prepare_namelist.py @@ -13,7 +13,7 @@ import datetime as dt from docopt import docopt from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster from utils import sed_inplace, copy, symlink, mkdir def run(iens, begin, end, hist_interval=5, radt=5, archive=True, diff --git a/dartwrf/prepare_wrfrundir.py b/dartwrf/prepare_wrfrundir.py index 3cfe2f9039961ee250919145c70792dcd2282cf2..87c22450e5a6ac13b4de61cdf2da5a32880d0d2e 100755 --- a/dartwrf/prepare_wrfrundir.py +++ b/dartwrf/prepare_wrfrundir.py @@ -2,7 +2,7 @@ import os, sys, shutil import datetime as dt from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster from utils import symlink, copy, link_contents import prepare_namelist diff --git a/dartwrf/run_obs_diag.py b/dartwrf/run_obs_diag.py index d292292cfa7e6e5dfccaf2dd9ec47bedad510518..9598a8eeadccca993d4cacb68796187e3992813c 100644 --- a/dartwrf/run_obs_diag.py +++ b/dartwrf/run_obs_diag.py @@ -1,7 +1,7 @@ import os, sys, shutil, glob from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster from utils import symlink, copy, sed_inplace, append_file, shell rundir_program = '/home/fs71386/lkugler/data/run_DART/' diff --git a/dartwrf/update_IC.py b/dartwrf/update_IC.py index 3247030ec4d855bcda80f3b7c7451f32c6c400b1..60cec760d2be7763cd39ec64fe4eee4d2e0ede56 100755 --- a/dartwrf/update_IC.py +++ b/dartwrf/update_IC.py @@ -3,7 +3,7 @@ import datetime as dt import netCDF4 as nc from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster def update_initials_in_WRF_rundir(time): """Updates wrfrst-files in `/run_WRF/` directory diff --git a/dartwrf/utils.py b/dartwrf/utils.py index 4da09e3c3801c0eddbe15bd03a4ea6a87d42ece0..b45b79f3fc5089230dc5c5e347ebbe5b6e92e5ed 100755 --- a/dartwrf/utils.py +++ b/dartwrf/utils.py @@ -2,35 +2,18 @@ import os, sys, shutil, glob, warnings import builtins as __builtin__ import subprocess import datetime as dt +import re, tempfile +import importlib -class ExperimentConfiguration(object): - """Collection of variables to use in code later on""" +class Experiment(object): + """Collection of variables regarding the experiment configuration""" def __init__(self): pass -class Shellslurm(): - """Like Slurmpy class, but runs locally""" - def __init__(self, *args, **kwargs): - pass - def run(self, *args, **kwargs): - print(args[0]) - os.system(args[0]) - class ClusterConfig(object): - """Collection of variables to use in code later on""" + """Collection of variables regarding the cluster configuration""" def __init__(self, exp): self.exp = exp - self.set_up = False - - def setup(self): - # Set paths and backup scripts - self.log_dir = self.archivedir+'/logs/' - self.slurm_scripts_dir = self.archivedir+'/slurm-scripts/' - print('logging to', self.log_dir) - print('scripts, which are submitted to SLURM:', self.slurm_scripts_dir) - - self.backup_scripts() - self.set_up = True @property def archivedir(self): @@ -43,49 +26,51 @@ class ClusterConfig(object): @property def scripts_rundir(self): + """Path to the directory where the DART-WRF scripts are executed + + Example: + `/user/data/sim_archive/DART-WRF/` + """ return self.archivedir+'/DART-WRF/' @property def dartrundir(self): + """Path to the directory where DART programs will run + Includes the experiment name + """ return self.dart_rundir_base+'/'+self.exp.expname+'/' def wrf_rundir(self, iens): + """Path to the directory where an ensemble member will run WRF + Includes the experiment name and the ensemble member index + """ return self.wrf_rundir_base+'/'+self.exp.expname+'/'+str(iens) - def create_job(self, *args, cfg_update=dict(), **kwargs): - """Shortcut to slurmpy's class; keep certain default kwargs - and only update some with kwarg `cfg_update` - see https://github.com/brentp/slurmpy + def run_job(self, cmd, jobname='', cfg_update=dict(), depends_on=None): + """Run scripts in a shell - depending on cluster config : run either locally or via SLURM - """ - if not self.set_up: - self.setup() + If not using SLURM: calls scripts through shell + if using SLURM: uses slurmpy to submit jobs, keep certain default kwargs and only update some with kwarg `overwrite_these_configurations` + Args: + cmd (str): Bash command(s) to run + jobname (str, optional): Name of SLURM job + cfg_update (dict): The config keywords will be overwritten with values + depends_on (int or None): SLURM job id of dependency, job will start after this id finished. + + Returns + None + """ if self.use_slurm: from slurmpy import Slurm - return Slurm(*args, slurm_kwargs=dict(self.slurm_cfg, **cfg_update), - log_dir=self.log_dir, - scripts_dir=self.slurm_scripts_dir, - **kwargs) + Slurm(jobname, slurm_kwargs=dict(self.slurm_cfg, **cfg_update), + log_dir=self.log_dir, + scripts_dir=self.slurm_scripts_dir, + **kwargs + ).run(cmd, depends_on=depends_on) else: - return Shellslurm(*args) - - def backup_scripts(self): - """Copies scripts and configuration to cluster.archivedir folder""" - os.makedirs(self.archivedir, exist_ok=True) - - try: - shutil.copytree(self.scriptsdir, self.scripts_rundir) - print('scripts have been copied to', self.archivedir) - except FileExistsError: - pass - except: - raise - # try: - # copy(os.path.basename(__file__), self.scripts_rundir+'/') - # except Exception as e: - # warnings.warn(str(e)) + print(cmd) + os.system(cmd) def shell(args): print(args) @@ -145,10 +130,19 @@ def copy_scp_srvx8(src, dst): os.system('scp '+src+' a1254888@srvx8.img.univie.ac.at:'+dst) def sed_inplace(filename, pattern, repl): - import re, tempfile - ''' - Perform the pure-Python equivalent of in-place `sed` substitution: e.g., - `sed -i -e 's/'${pattern}'/'${repl}' "${filename}"`. + '''Perform the pure-Python equivalent of in-place `sed` substitution + Like `sed -i -e 's/'${pattern}'/'${repl}' "${filename}"`. + + Args: + filename (str): path to file + pattern (str): string that will be replaced + repl (str): what shall be written instead of pattern? + + Example: + sed_inplace('namelist.input', '<dx>', str(int(exp.model_dx))) + + Returns: + None ''' # For efficiency, precompile the passed regular expression. pattern_compiled = re.compile(pattern) diff --git a/dartwrf/workflows.py b/dartwrf/workflows.py index bc60a01013c4ace927bc08b842fe6ddd83cb5087..acc8a8e4a3ba45f1e10bafaffc6ae18b099a6b3c 100644 --- a/dartwrf/workflows.py +++ b/dartwrf/workflows.py @@ -1,192 +1,232 @@ #!/usr/bin/python3 """ -Run a cycled OSSE with WRF and DART. +These functions mostly call python scripts via the shell, +e.g. assimilate() calls dartwrf/assim_synth_obs.py through the shell. + +This would not be necessary, but some users might want to use queueing systems (e.g. SLURM) which must call scripts. """ import os, sys, shutil, glob, warnings import datetime as dt +import importlib from dartwrf.utils import script_to_str from config.cfg import exp -from config.clusters import cluster -def prepare_WRFrundir(init_time): - """Create WRF/run directories and wrfinput files - """ - cmd = cluster.python+' '+cluster.scripts_rundir+'/prepare_wrfrundir.py '+init_time.strftime('%Y-%m-%d_%H:%M') - print(cmd) - os.system(cmd) - -def run_ideal(depends_on=None): - """Run ideal for every ensemble member""" - cmd = """# run ideal.exe in parallel, then add geodata -export SLURM_STEP_GRES=none -for ((n=1; n<="""+str(exp.n_ens)+"""; n++)) -do - rundir="""+cluster.wrf_rundir_base+'/'+exp.expname+"""/$n - echo $rundir - cd $rundir - mpirun -np 1 ./ideal.exe & -done -wait -for ((n=1; n<="""+str(exp.n_ens)+"""; n++)) -do - rundir="""+cluster.wrf_rundir_base+'/'+exp.expname+"""/$n - mv $rundir/rsl.out.0000 $rundir/rsl.out.input -done -""" - s = cluster.create_job("ideal", cfg_update={"ntasks": str(exp.n_ens), - "time": "10", "mem": "100G"}) - id = s.run(cmd, depends_on=[depends_on]) - return id - -def wrfinput_insert_wbubble(perturb=True, depends_on=None): - """Given that directories with wrfinput files exist, - update these wrfinput files with warm bubbles +class WorkFlows(object): + def __init__(self, exp_config='cfg.py', server_config='server.py'): + """Set up the experiment folder in `archivedir`. + + Args: + exp (str): Path to exp config file + config (str): Path to the cluster config file + """ + # At first, load config from present folder (later from scripts_rundir) + # exp = __import__('config/'+exp_config) + self.cluster = importlib.import_module('config.'+server_config.strip('.py')).cluster + + # Set paths and backup scripts + self.cluster.log_dir = self.cluster.archivedir+'/logs/' + print('logging to', self.cluster.log_dir) + + if self.cluster.use_slurm: + self.cluster.slurm_scripts_dir = self.cluster.archivedir+'/slurm-scripts/' + print('scripts, which are submitted to SLURM:', self.cluster.slurm_scripts_dir) + + # Copy scripts to self.cluster.archivedir folder + os.makedirs(self.cluster.archivedir, exist_ok=True) + try: + shutil.copytree(self.cluster.scriptsdir, self.cluster.scripts_rundir) + print('scripts have been copied to', self.cluster.archivedir) + except FileExistsError as e: + warnings.warn(str(e)) + except: + raise + + # Copy config files + shutil.copy('config/'+exp_config, self.cluster.scripts_rundir+'/cfg.py') + shutil.copy('config/'+server_config, self.cluster.scripts_rundir+'/cluster.py') # whatever server, the config name is always the same! + shutil.copy('config/'+server_config, 'config/cluster.py') # whatever server, the config name is always the same! + + + def prepare_WRFrundir(self, init_time): + """Create WRF/run directories and wrfinput files + """ + cmd = self.cluster.python+' '+self.cluster.scripts_rundir+'/prepare_wrfrundir.py '+init_time.strftime('%Y-%m-%d_%H:%M') + print(cmd) + os.system(cmd) + + def run_ideal(self, depends_on=None): + """Run ideal for every ensemble member""" + cmd = """# run ideal.exe in parallel, then add geodata + export SLURM_STEP_GRES=none + for ((n=1; n<="""+str(exp.n_ens)+"""; n++)) + do + rundir="""+self.cluster.wrf_rundir_base+'/'+exp.expname+"""/$n + echo $rundir + cd $rundir + mpirun -np 1 ./ideal.exe & + done + wait + for ((n=1; n<="""+str(exp.n_ens)+"""; n++)) + do + rundir="""+self.cluster.wrf_rundir_base+'/'+exp.expname+"""/$n + mv $rundir/rsl.out.0000 $rundir/rsl.out.input + done """ - s = cluster.create_job("ins_wbubble", cfg_update={"time": "5"}) - pstr = ' ' - if perturb: - pstr = ' perturb' - id = s.run(cluster.python+' '+cluster.scripts_rundir+'/create_wbubble_wrfinput.py'+pstr, - depends_on=[depends_on]) - return id - -def run_ENS(begin, end, depends_on=None, first_minute=True, - input_is_restart=True, restart_path=False, output_restart_interval=720): - """Run forecast for 1 minute, save output. - Then run whole timespan with 5 minutes interval. - - if input_is_restart: # start WRF in restart mode - """ - id = depends_on - restart_flag = '.false.' if not input_is_restart else '.true.' - - # if False: # doesnt work with restarts at the moment# first_minute: - # # first minute forecast (needed for validating an assimilation) - # hist_interval = 1 - # radt = 1 # calc CFRAC also in first minute - # begin_plus1 = begin+dt.timedelta(minutes=1) - # s = cluster.create_job("preWRF1", cfg_update=dict(time="2")) - # args = [cluster.python, cluster.scripts_rundir+'/prepare_namelist.py', - # begin.strftime('%Y-%m-%d_%H:%M'), - # begin_plus1.strftime('%Y-%m-%d_%H:%M'), - # str(hist_interval), - # '--radt='+str(radt), - # '--restart='+restart_flag,] - # id = s.run(' '.join(args), depends_on=[id]) - - # s = cluster.create_job("runWRF1", cfg_update={"nodes": "1", "array": "1-"+str(exp.n_nodes), - # "time": "2", "mem-per-cpu": "2G"}) - # cmd = script_to_str(cluster.run_WRF).replace('<expname>', exp.expname) - # id = s.run(cmd, depends_on=[id]) - - # # apply forward operator (DART filter without assimilation) - # s = cluster.create_job("fwOP-1m", cfg_update=dict(time="10", ntasks=48)) - # id = s.run(cluster.python+' '+cluster.scripts_rundir+'/apply_obs_op_dart.py ' - # + begin.strftime('%Y-%m-%d_%H:%M')+' ' - # + begin_plus1.strftime('%Y-%m-%d_%H:%M'), - # depends_on=[id]) - - # whole forecast timespan - hist_interval = 5 - radt = 5 - args = [cluster.python, - cluster.scripts_rundir+'/prepare_namelist.py', - begin.strftime('%Y-%m-%d_%H:%M'), - end.strftime('%Y-%m-%d_%H:%M'), - str(hist_interval), - '--radt='+str(radt), - '--restart='+restart_flag,] - if output_restart_interval: - args.append('--restart_interval='+str(int(float(output_restart_interval)))) - - s = cluster.create_job("preWRF", cfg_update=dict(time="2")) - id = s.run(' '.join(args), depends_on=[id]) - - time_in_simulation_hours = (end-begin).total_seconds()/3600 - runtime_wallclock_mins_expected = int(8+time_in_simulation_hours*9.5) # usually below 9 min/hour - s = cluster.create_job("WRF", cfg_update={"array": "1-"+str(cluster.size_jobarray), "ntasks": "10", "nodes": "1", - "time": str(runtime_wallclock_mins_expected), "mem": "140G"}) - cmd = script_to_str(cluster.run_WRF).replace('<exp.expname>', exp.expname - ).replace('<cluster.wrf_rundir_base>', cluster.wrf_rundir_base) - id = s.run(cmd, depends_on=[id]) - return id - - -def assimilate(assim_time, prior_init_time, prior_valid_time, prior_path_exp, - depends_on=None): - """Creates observations from a nature run and assimilates them. - - Args: - assim_time (dt.datetime): timestamp of prior wrfout files - prior_init_time (dt.datetime): timestamp to find the directory where the prior wrfout files are - prior_path_exp (str): use this directory to get prior state (i.e. cluster.archivedir) - """ - if not os.path.exists(prior_path_exp): - raise IOError('prior_path_exp does not exist: '+prior_path_exp) - - id = cluster.create_job("Assim", cfg_update={"ntasks": "12", "time": "60", - "mem": "200G", "ntasks-per-node": "12", "ntasks-per-core": "2"} - ).run(cluster.python+' '+cluster.scripts_rundir+'/assim_synth_obs.py ' - +assim_time.strftime('%Y-%m-%d_%H:%M ') - +prior_init_time.strftime('%Y-%m-%d_%H:%M ') - +prior_valid_time.strftime('%Y-%m-%d_%H:%M ') - +prior_path_exp, depends_on=[depends_on]) - return id - - -def prepare_IC_from_prior(prior_path_exp, prior_init_time, prior_valid_time, new_start_time=None, depends_on=None): - - if new_start_time != None: - tnew = new_start_time.strftime(' %Y-%m-%d_%H:%M') - else: - tnew = '' - - id = cluster.create_job("IC-prior", cfg_update=dict(time="8") - ).run(cluster.python+' '+cluster.scripts_rundir+'/prep_IC_prior.py ' - +prior_path_exp - +prior_init_time.strftime(' %Y-%m-%d_%H:%M') - +prior_valid_time.strftime(' %Y-%m-%d_%H:%M') - +tnew, depends_on=[depends_on]) - return id - - -def update_IC_from_DA(assim_time, depends_on=None): - id = cluster.create_job("IC-update", cfg_update=dict(time="8") - ).run(cluster.python+' '+cluster.scripts_rundir+'/update_IC.py ' - +assim_time.strftime('%Y-%m-%d_%H:%M'), depends_on=[depends_on]) - return id - - -def create_satimages(init_time, depends_on=None): - s = cluster.create_job("RTTOV", cfg_update={"ntasks": "12", "time": "80", "mem": "200G"}) - id = s.run(cluster.python_verif+' ~/RTTOV-WRF/run_init.py '+cluster.archivedir - +init_time.strftime('/%Y-%m-%d_%H:%M/'), - depends_on=[depends_on]) - return id - - -def gen_obsseq(depends_on=None): - s = cluster.create_job("obsseq_netcdf", cfg_update={"time": "10", "mail-type": "FAIL,END"}) - id = s.run(cluster.python+' '+cluster.scripts_rundir+'/obsseq_to_netcdf.py', - depends_on=[depends_on]) - return id - - -def verify_sat(depends_on=None): - s = cluster.create_job("verif-SAT-"+exp.expname, cfg_update={"time": "60", "mail-type": "FAIL,END", "ntasks": "20", - "ntasks-per-node": "20", "ntasks-per-core": "1", "mem": "100G",}) - cmd = cluster.python_verif+' /jetfs/home/lkugler/osse_analysis/plot_from_raw/analyze_fc.py '+exp.expname+' has_node sat verif1d FSS BS' - s.run(cmd, depends_on=[depends_on]) - -def verify_wrf(depends_on=None): - s = cluster.create_job("verif-WRF-"+exp.expname, cfg_update={"time": "120", "mail-type": "FAIL,END", "ntasks": "20", - "ntasks-per-node": "20", "ntasks-per-core": "1", "mem": "250G"}) - cmd = cluster.python_verif+' /jetfs/home/lkugler/osse_analysis/plot_from_raw/analyze_fc.py '+exp.expname+' has_node wrf verif1d verif3d FSS BS' - s.run(cmd, depends_on=[depends_on]) - -def verify_fast(depends_on=None): - s = cluster.create_job("verif-fast-"+exp.expname, cfg_update={"time": "10", "mail-type": "FAIL", "ntasks": "1", - "ntasks-per-node": "1", "ntasks-per-core": "1"}) - cmd = cluster.python_verif+' /jetfs/home/lkugler/osse_analysis/plot_fast/plot_single_exp.py '+exp.expname - s.run(cmd, depends_on=[depends_on]) + id = self.cluster.run_job(cmd, "ideal", cfg_update={"ntasks": str(exp.n_ens), + "time": "10", "mem": "100G"}, depends_on=[depends_on]) + return id + + def wrfinput_insert_wbubble(self, perturb=True, depends_on=None): + """Given that directories with wrfinput files exist, + update these wrfinput files with warm bubbles + """ + + pstr = ' ' + if perturb: + pstr = ' perturb' + cmd = self.cluster.python+' '+self.cluster.scripts_rundir+'/create_wbubble_wrfinput.py'+pstr + + id = self.cluster.run_job(cmd, "ins_wbubble", cfg_update={"time": "5"}, depends_on=[depends_on]) + return id + + def run_ENS(self, begin, end, depends_on=None, first_minute=True, + input_is_restart=True, restart_path=False, output_restart_interval=720): + """Run forecast for 1 minute, save output. + Then run whole timespan with 5 minutes interval. + + if input_is_restart: # start WRF in restart mode + """ + id = depends_on + restart_flag = '.false.' if not input_is_restart else '.true.' + + # if False: # doesnt work with restarts at the moment# first_minute: + # # first minute forecast (needed for validating an assimilation) + # hist_interval = 1 + # radt = 1 # calc CFRAC also in first minute + # begin_plus1 = begin+dt.timedelta(minutes=1) + # s = self.cluster.run_job("preWRF1", cfg_update=dict(time="2")) + # args = [self.cluster.python, self.cluster.scripts_rundir+'/prepare_namelist.py', + # begin.strftime('%Y-%m-%d_%H:%M'), + # begin_plus1.strftime('%Y-%m-%d_%H:%M'), + # str(hist_interval), + # '--radt='+str(radt), + # '--restart='+restart_flag,] + # id = s.run(' '.join(args), depends_on=[id]) + + # s = self.cluster.run_job("runWRF1", cfg_update={"nodes": "1", "array": "1-"+str(exp.n_nodes), + # "time": "2", "mem-per-cpu": "2G"}) + # cmd = script_to_str(self.cluster.run_WRF).replace('<expname>', exp.expname) + # id = s.run(cmd, depends_on=[id]) + + # # apply forward operator (DART filter without assimilation) + # s = self.cluster.run_job("fwOP-1m", cfg_update=dict(time="10", ntasks=48)) + # id = s.run(self.cluster.python+' '+self.cluster.scripts_rundir+'/apply_obs_op_dart.py ' + # + begin.strftime('%Y-%m-%d_%H:%M')+' ' + # + begin_plus1.strftime('%Y-%m-%d_%H:%M'), + # depends_on=[id]) + + # whole forecast timespan + hist_interval = 5 + radt = 5 + args = [self.cluster.python, + self.cluster.scripts_rundir+'/prepare_namelist.py', + begin.strftime('%Y-%m-%d_%H:%M'), + end.strftime('%Y-%m-%d_%H:%M'), + str(hist_interval), + '--radt='+str(radt), + '--restart='+restart_flag,] + if output_restart_interval: + args.append('--restart_interval='+str(int(float(output_restart_interval)))) + + id = self.cluster.run_job(' '.join(args), "preWRF", cfg_update=dict(time="2"), depends_on=[id]) + + cmd = script_to_str(self.cluster.run_WRF).replace('<exp.expname>', exp.expname + ).replace('<cluster.wrf_rundir_base>', self.cluster.wrf_rundir_base) + + time_in_simulation_hours = (end-begin).total_seconds()/3600 + runtime_wallclock_mins_expected = int(8+time_in_simulation_hours*9.5) # usually below 9 min/hour + + id = self.cluster.run_job(cmd, "WRF", cfg_update={"array": "1-"+str(self.cluster.size_jobarray), "ntasks": "10", "nodes": "1", + "time": str(runtime_wallclock_mins_expected), "mem": "140G"}, depends_on=[id]) + return id + + + def assimilate(self, assim_time, prior_init_time, prior_valid_time, prior_path_exp, + depends_on=None): + """Creates observations from a nature run and assimilates them. + + Args: + assim_time (dt.datetime): timestamp of prior wrfout files + prior_init_time (dt.datetime): timestamp to find the directory where the prior wrfout files are + prior_path_exp (str): use this directory to get prior state (i.e. self.cluster.archivedir) + """ + if not os.path.exists(prior_path_exp): + raise IOError('prior_path_exp does not exist: '+prior_path_exp) + + cmd = (self.cluster.python+' '+self.cluster.scripts_rundir+'/assim_synth_obs.py ' + +assim_time.strftime('%Y-%m-%d_%H:%M ') + +prior_init_time.strftime('%Y-%m-%d_%H:%M ') + +prior_valid_time.strftime('%Y-%m-%d_%H:%M ') + +prior_path_exp) + + id = self.cluster.run_job(cmd, "Assim", cfg_update={"ntasks": "12", "time": "60", + "mem": "200G", "ntasks-per-node": "12", "ntasks-per-core": "2"}, depends_on=[depends_on]) + return id + + + def prepare_IC_from_prior(self, prior_path_exp, prior_init_time, prior_valid_time, new_start_time=None, depends_on=None): + + if new_start_time != None: + tnew = new_start_time.strftime(' %Y-%m-%d_%H:%M') + else: + tnew = '' + + cmd = (self.cluster.python+' '+self.cluster.scripts_rundir+'/prep_IC_prior.py ' + +prior_path_exp + +prior_init_time.strftime(' %Y-%m-%d_%H:%M') + +prior_valid_time.strftime(' %Y-%m-%d_%H:%M') + +tnew) + id = self.cluster.run_job(cmd, "IC-prior", cfg_update=dict(time="8"), depends_on=[depends_on]) + return id + + + def update_IC_from_DA(self, assim_time, depends_on=None): + cmd = self.cluster.python+' '+self.cluster.scripts_rundir+'/update_IC.py '+assim_time.strftime('%Y-%m-%d_%H:%M') + id = self.cluster.run_job(cmd, "IC-update", cfg_update=dict(time="8"), depends_on=[depends_on]) + return id + + + def create_satimages(self, init_time, depends_on=None): + cmd = self.cluster.python_verif+' ~/RTTOV-WRF/run_init.py '+self.cluster.archivedir+init_time.strftime('/%Y-%m-%d_%H:%M/') + id = self.cluster.run_job(cmd, "RTTOV", cfg_update={"ntasks": "12", "time": "80", "mem": "200G"}, depends_on=[depends_on]) + return id + + + def gen_obsseq(self, depends_on=None): + cmd = self.cluster.python+' '+self.cluster.scripts_rundir+'/obsseq_to_netcdf.py' + id = self.cluster.run_job("obsseq_netcdf", cfg_update={"time": "10", "mail-type": "FAIL,END"}, + depends_on=[depends_on]) + return id + + + def verify_sat(self, depends_on=None): + cmd = self.cluster.python_verif+' /jetfs/home/lkugler/osse_analysis/plot_from_raw/analyze_fc.py '+exp.expname+' has_node sat verif1d FSS BS' + + self.cluster.run_job(cmd, "verif-SAT-"+exp.expname, + cfg_update={"time": "60", "mail-type": "FAIL,END", "ntasks": "20", + "ntasks-per-node": "20", "ntasks-per-core": "1", "mem": "100G",}, depends_on=[depends_on]) + + def verify_wrf(self, depends_on=None): + cmd = self.cluster.python_verif+' /jetfs/home/lkugler/osse_analysis/plot_from_raw/analyze_fc.py '+exp.expname+' has_node wrf verif1d verif3d FSS BS' + + self.cluster.run_job(cmd, "verif-WRF-"+exp.expname, + cfg_update={"time": "120", "mail-type": "FAIL,END", "ntasks": "20", + "ntasks-per-node": "20", "ntasks-per-core": "1", "mem": "250G"}, depends_on=[depends_on]) + + def verify_fast(self, depends_on=None): + cmd = self.cluster.python_verif+' /jetfs/home/lkugler/osse_analysis/plot_fast/plot_single_exp.py '+exp.expname + + self.cluster.run_job(cmd, "verif-fast-"+exp.expname, + cfg_update={"time": "10", "mail-type": "FAIL", "ntasks": "1", + "ntasks-per-node": "1", "ntasks-per-core": "1"}, depends_on=[depends_on]) diff --git a/dartwrf/wrfinput_add_geo.py b/dartwrf/wrfinput_add_geo.py index fe4e1552089e9f873b5e292867ccfdf106d515ad..1a2c2667ec50f9fd6ea13add4d0d7ded7a7309de 100755 --- a/dartwrf/wrfinput_add_geo.py +++ b/dartwrf/wrfinput_add_geo.py @@ -12,7 +12,7 @@ import os, sys import netCDF4 as nc from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster def run(geo_data_file, wrfinput_file): geo_ds = nc.Dataset(geo_data_file, 'r') diff --git a/dartwrf/wrfout_add_geo.py b/dartwrf/wrfout_add_geo.py index 99e1d85ef691c71ee13920ff683bcbbe4eb8caef..a13737c161482ebf2d1829a139d14fc702a1d8f2 100755 --- a/dartwrf/wrfout_add_geo.py +++ b/dartwrf/wrfout_add_geo.py @@ -2,7 +2,7 @@ import os, sys import netCDF4 as nc from config.cfg import exp -from config.clusters import cluster +from config.cluster import cluster fields_old = ["XLAT_M", "XLONG_M", "CLAT", "XLONG_U", "XLONG_V", "XLAT_U", "XLAT_V"]