From 81ef508ebcf51ee98f1c12c2235a82c5f1bdabe3 Mon Sep 17 00:00:00 2001
From: Kugler Lukas <lukas.kugler@univie.ac.at>
Date: Thu, 19 Nov 2020 18:07:20 +0100
Subject: [PATCH] first minute forecast capability

---
 config/cfg.py               |  8 ++--
 config/clusters.py          |  4 +-
 scheduler.py                | 80 +++++++++++++++++++------------------
 scripts/prepare_namelist.py |  6 ++-
 templates/namelist.input    |  2 +-
 5 files changed, 53 insertions(+), 47 deletions(-)

diff --git a/config/cfg.py b/config/cfg.py
index 1dabc00..8ce1a1b 100755
--- a/config/cfg.py
+++ b/config/cfg.py
@@ -9,11 +9,11 @@ class ExperimentConfiguration(object):
 
 
 exp = ExperimentConfiguration()
-exp.expname = "exp_v1.12_LMU_so_radar_vertloc1km"
+exp.expname = "exp_v1.12_LMU_so_VIS2"
 exp.model_dx = 2000
 exp.timestep = 10
-exp.n_ens = 20
-exp.n_nodes = 5
+exp.n_ens = 40
+exp.n_nodes = 10
 
 n_obs = 64  # radar: n_obs for each observation height level
 
@@ -27,7 +27,7 @@ radar = dict(sat=False, kind='RADAR', n_obs=n_obs, err_std=5.,
 psfc = dict(sat=False, kind='PSFC', n_obs=n_obs, err_std=50.,
              cov_loc_radius_km=10)
 
-exp.observations = [radar, ]
+exp.observations = [vis, ]
 
 # directory paths depend on the name of the experiment
 cluster.expname = exp.expname
diff --git a/config/clusters.py b/config/clusters.py
index 56d0c9a..e1f7af3 100755
--- a/config/clusters.py
+++ b/config/clusters.py
@@ -25,11 +25,11 @@ vsc.dart_srcdir = '/home/fs71386/lkugler/DART/DART_WRF_RTTOV_early_access/models
 vsc.dartrundir = '/home/fs71386/lkugler/run_DART'
 vsc.scriptsdir = '/home/fs71386/lkugler/DART-WRF/scripts'
 
-vsc.nature_wrfout = '/home/fs71386/lkugler/data/sim_archive/exp_v1.11_LMU_nature/2008-07-30_06:00/2/wrfout_d01_%Y-%m-%d_%H:%M:%S'
+vsc.nature_wrfout = '/home/fs71386/lkugler/data/sim_archive/exp_v1.12_LMU_nature/2008-07-30_06:00/2/wrfout_d01_%Y-%m-%d_%H:%M:%S'
 vsc.input_profile = '/home/fs71386/lkugler/wrf_sounding/data/wrf/ens/from_LMU/raso.raso.<iens>.wrfprof'
 
 vsc.ideal = vsc.userdir+'/compile/bin/ideal-v4.2.1_v1.11.exe'
-vsc.wrfexe = vsc.userdir+'/compile/bin/wrf-v4.2.1_v1.11.exe'
+vsc.wrfexe = vsc.userdir+'/compile/bin/wrf-v4.2.1_v1.12.exe'
 vsc.namelist = vsc.scriptsdir+'/../templates/namelist.input'
 vsc.run_WRF = '/gpfs/data/fs71386/lkugler/DART-WRF/scripts/osse/run_ens.vsc.sh'
 
diff --git a/scheduler.py b/scheduler.py
index 3eb006f..576641d 100755
--- a/scheduler.py
+++ b/scheduler.py
@@ -31,21 +31,6 @@ class Cmdline(object):
         print('running', self.name, 'without SLURM')
         os.system(cmd)
 
-def slurm_submit(bashcmd, name=None, cfg_update=None, depends_on=None):
-    """Submit a 'workflow task'=script=job to the SLURM queue.
-    Args:
-        bashcmd (str): command to run (i.e. call to script)
-        name (str): SLURM job name (useful for debugging)
-        cfg_update (dict): enforce these SLURM parameters
-        depends_on (int): SLURM id; job starts as soon as this id has finished
-    Returns:
-        int : SLURM job id, can be used in `depends_on` of another `slurm_submit` call
-    """
-    if name is None:  # slurm job name = name of calling function
-        name = sys._getframe(1).f_code.co_name
-    id = my_Slurm(name, cfg_update=cfg_update).run(bashcmd, depends_on=depends_on)
-    return id
-
 def clear_logs(backup_existing_to_archive=True):
     dirs = ['/logs/', '/slurm-scripts/']
     for d in dirs:
@@ -62,10 +47,8 @@ def clear_logs(backup_existing_to_archive=True):
 def prepare_wrfinput():
     """Create WRF/run directories and wrfinput files
     """
-    # s = my_Slurm("pre_osse", cfg_update={"time": "5", "mail-type": "BEGIN"})
-    # id = s.run(cluster.python+' '+cluster.scriptsdir+'/prepare_wrfinput.py')
-    id = slurm_submit(cluster.python+' '+cluster.scriptsdir+'/prepare_wrfinput.py',
-                      name='prep_wrfinput', cfg_update={"time": "5", "mail-type": "BEGIN"})
+    s = my_Slurm("prep_wrfinput", cfg_update={"time": "5", "mail-type": "BEGIN"})
+    id = s.run(cluster.python+' '+cluster.scriptsdir+'/prepare_wrfinput.py')
 
     cmd = """# run ideal.exe in parallel, then add geodata
 export SLURM_STEP_GRES=none
@@ -83,8 +66,9 @@ do
     mv $rundir/rsl.out.0000 $rundir/rsl.out.input
 done
 """
-    id = slurm_submit(cmd, name="ideal", cfg_update={"ntasks": str(exp.n_ens),
-                      "time": "10", "mem-per-cpu": "2G"}, depends_on=[id])
+    s = my_Slurm("prep_wrfinput", cfg_update={"ntasks": str(exp.n_ens),
+                                              "time": "10", "mem-per-cpu": "2G"})
+    id = s.run(cmd, depends_on=[id])
     return id
 
 def update_wrfinput_from_archive(time, background_init_time, exppath, depends_on=None):
@@ -101,17 +85,37 @@ def update_wrfinput_from_archive(time, background_init_time, exppath, depends_on
     return id
 
 def run_ENS(begin, end, depends_on=None):
+    """Run forecast for 1 minute, save output. 
+    Then run whole timespan with 5 minutes interval.
+    """
     prev_id = depends_on
 
-    s = my_Slurm("preWRF", cfg_update=dict(time="2"))
+    # first minute forecast (needed for validating an assimilation)
+    hist_interval = 1
+    s = my_Slurm("preWRF1", cfg_update=dict(time="2"))
     id = s.run(cluster.python+' '+cluster.scriptsdir+'/prepare_namelist.py '
                + begin.strftime('%Y-%m-%d_%H:%M')+' '
-               + end.strftime('%Y-%m-%d_%H:%M'),
+               + (begin+dt.timedelta(minutes=1)).strftime('%Y-%m-%d_%H:%M')+' '
+               + str(hist_interval), 
                depends_on=[prev_id])
 
-    runtime_real_hours = (end-begin).total_seconds()/3600
-    runtime_wallclock_mins_expected = int(5+runtime_real_hours*9)  # usually below 8 min/hour
-    s = my_Slurm("runWRF", cfg_update={"nodes": "1", "array": "1-"+str(exp.n_nodes),
+    s = my_Slurm("runWRF1", cfg_update={"nodes": "1", "array": "1-"+str(exp.n_nodes),
+                 "time": "5", "mem-per-cpu": "2G"})
+    cmd = script_to_str(cluster.run_WRF).replace('<expname>', exp.expname)
+    id2 = s.run(cmd, depends_on=[id])
+
+    # whole forecast timespan
+    hist_interval = 5
+    s = my_Slurm("preWRF2", cfg_update=dict(time="2"))
+    id = s.run(cluster.python+' '+cluster.scriptsdir+'/prepare_namelist.py '
+               + begin.strftime('%Y-%m-%d_%H:%M')+' '
+               + end.strftime('%Y-%m-%d_%H:%M')+' '
+               + str(hist_interval), 
+               depends_on=[prev_id])
+
+    time_in_simulation_hours = (end-begin).total_seconds()/3600
+    runtime_wallclock_mins_expected = int(5+time_in_simulation_hours*9)  # usually below 8 min/hour
+    s = my_Slurm("runWRF2", cfg_update={"nodes": "1", "array": "1-"+str(exp.n_nodes),
                  "time": str(runtime_wallclock_mins_expected), "mem-per-cpu": "2G"})
     cmd = script_to_str(cluster.run_WRF).replace('<expname>', exp.expname)
     id2 = s.run(cmd, depends_on=[id])
@@ -143,20 +147,19 @@ def assimilate(assim_time, background_init_time,
         prior_expdir = cluster.archivedir()
 
     # prepare state of nature run, from which observation is sampled
-    id = slurm_submit(cluster.python+' '+cluster.scriptsdir+'/prepare_nature.py '
-                      +time.strftime('%Y-%m-%d_%H:%M'), name='prep_nature',
-         cfg_update=dict(time="2"), depends_on=[depends_on])
+    s = my_Slurm("prepNature", cfg_update=dict(time="2"))
+    id = s.run(cluster.python+' '+cluster.scriptsdir+'/prepare_nature.py '
+               +time.strftime('%Y-%m-%d_%H:%M'), depends_on=[depends_on])
 
     # prepare prior model state
     s = my_Slurm("preAssim", cfg_update=dict(time="2"))
     id = s.run(cluster.python+' '+cluster.scriptsdir+'/pre_assim.py '
                +assim_time.strftime('%Y-%m-%d_%H:%M ')
                +background_init_time.strftime('%Y-%m-%d_%H:%M ')
-               +prior_expdir,
-               depends_on=[id])
+               +prior_expdir, depends_on=[id])
 
     # generate observations
-    s = my_Slurm("gensynthobs", cfg_update=dict(ntasks="48", time="10"))
+    s = my_Slurm("genSynthObs", cfg_update=dict(ntasks="48", time="10"))
     id = s.run(cluster.python+' '+cluster.scriptsdir+'/gen_synth_obs.py '
                +time.strftime('%Y-%m-%d_%H:%M'),
                depends_on=[id])
@@ -179,7 +182,7 @@ def assimilate(assim_time, background_init_time,
 
 
 def create_satimages(depends_on=None):
-    s = my_Slurm("pRTTOV", cfg_update={"ntasks": "48", "time": "20"})
+    s = my_Slurm("pRTTOV", cfg_update={"ntasks": "48", "time": "40"})
     s.run(cluster.python+' /home/fs71386/lkugler/RTTOV-WRF/loop.py '+exp.expname,
           depends_on=[depends_on])
 
@@ -192,13 +195,14 @@ def mailme(depends_on=None):
 ################################
 print('starting osse')
 
-timedelta_integrate = dt.timedelta(minutes=30)
+timedelta_integrate = dt.timedelta(minutes=45)
 timedelta_btw_assim = dt.timedelta(minutes=30)
 
-clear_logs(backup_existing_to_archive=True)
+clear_logs(backup_existing_to_archive=False)
+
 id = None
 
-start_from_existing_state = False
+start_from_existing_state = True
 is_new_run = not start_from_existing_state
 
 if is_new_run:
@@ -217,8 +221,8 @@ elif start_from_existing_state:
     id = prepare_wrfinput()  # create initial conditions
     
     # get initial conditions from archive
-    background_init_time = dt.datetime(2008, 7, 30, 10)
-    time = dt.datetime(2008, 7, 30, 10,30)
+    background_init_time = dt.datetime(2008, 7, 30, 6)
+    time = dt.datetime(2008, 7, 30, 10)
     exppath_arch = '/gpfs/data/fs71386/lkugler/sim_archive/exp_v1.11_LMU_filter'
     first_guess = exppath_arch
     id = update_wrfinput_from_archive(time, background_init_time, exppath_arch,
diff --git a/scripts/prepare_namelist.py b/scripts/prepare_namelist.py
index 0fd483a..9fab46a 100755
--- a/scripts/prepare_namelist.py
+++ b/scripts/prepare_namelist.py
@@ -5,13 +5,14 @@ sys.path.append(os.getcwd())
 from config.cfg import exp, cluster
 from utils import sed_inplace, copy, symlink, mkdir
 
-def run(cluster, iens, begin, end):
+def run(cluster, iens, begin, end, hist_interval=5):
     rundir = cluster.wrf_rundir(iens)
     print(rundir)
     copy(cluster.namelist, rundir+'/namelist.input')
 
     sed_inplace(rundir+'/namelist.input', '<dx>', str(int(exp.model_dx)))
     sed_inplace(rundir+'/namelist.input', '<timestep>', str(int(exp.timestep)))
+    sed_inplace(rundir+'/namelist.input', '<hist_interval>', str(int(hist_interval)))
 
     archdir = cluster.archivedir()+begin.strftime('/%Y-%m-%d_%H:%M/'+str(iens)+'/')
     print('namelist for run from', begin, end, 'output to', archdir)
@@ -41,7 +42,8 @@ def run(cluster, iens, begin, end):
 if __name__ == '__main__':
     begin = dt.datetime.strptime(sys.argv[1], '%Y-%m-%d_%H:%M')
     end = dt.datetime.strptime(sys.argv[2], '%Y-%m-%d_%H:%M')
+    hist_interval = int(sys.argv[3])
 
     print('prepare namelists for all ens members')
     for iens in range(1, exp.n_ens+1):
-        run(cluster, iens, begin, end)
+        run(cluster, iens, begin, end, hist_interval)
diff --git a/templates/namelist.input b/templates/namelist.input
index 74bc39c..6fe6f5a 100644
--- a/templates/namelist.input
+++ b/templates/namelist.input
@@ -11,7 +11,7 @@
  end_hour                            = <HH2>,   00,   00,
  end_minute                          = <MM2>,  120,  120,
  end_second                          = 0,   00,   00,
- history_interval                    = 15,   15,   15,
+ history_interval                    = <hist_interval>,   15,   15,
  frames_per_outfile                  = 1, 1, 1,
  history_outname 		     = '<archivedir>/wrfout_d<domain>_<date>'
  restart                             = .false.,
-- 
GitLab