diff --git a/dartwrf/utils.py b/dartwrf/utils.py
index 2e3f766019eb83bd17a74893ebd183508c4f9580..cc19b5d4dcad75b9c1758c8c0bfe40b51015a872 100755
--- a/dartwrf/utils.py
+++ b/dartwrf/utils.py
@@ -1,40 +1,40 @@
-import os, sys, shutil, glob, warnings
+"""Utility functions for DART-WRF
+
+Caution: You can not use the configuration files in here, 
+because loading this would lead to a circular import.
+"""
+
+import os
+import sys
+import shutil
+import glob
+import warnings
 import builtins as __builtin__
 import subprocess
 import datetime as dt
-import re, tempfile
-
-# class Stage(object):
-#     """Collection of variables describing the assimilation stage"""
-#     def __init__(self, **kwargs):
-#         self.superob_km = False  # False or int (spatial averaging of observations)
-#         self.use_existing_obsseq = False  # False or pathname (use precomputed obs_seq.out files)
-#         self.__dict__.update(kwargs)
+import re
+import tempfile
+import pickle
 
-#         # raise ValueError if attributes are not set
-#         needed_attributes = ['observations', 'dart_nml',]
-#         for attr in needed_attributes:
-#             if not hasattr(self, attr):
-#                 raise ValueError('Stage.'+attr+' is not set')
 
 class Experiment(object):
     """Collection of variables which define the experiment
-    
+
     Attributes:
         expname (str): Name of the experiment
         model_dx (int): WRF grid spacing in meters
         n_ens (int): Ensemble size
         do_quality_control (bool): If True, activate "quality control" function in assim_synth_obs.py
-        
+
         nature_wrfout_pattern (str): Path to the nature run, where we take observations from; 
             the path can contain wildcards (*,?), e.g. '/jetfs/exp1/*/1/wrfout_d01_%Y-%m-%d_%H:%M:%S'
         input_profile (str): Path to WRF idealized input profiles; 
             e.g. '/data/initial_profiles/wrf/ens/raso.fc.<iens>.wrfprof';
             <iens> is replaced by 001-040 for a 40-member ensemble
-        
+
         update_vars (list of str): Variables which will be updated after assimilation (update_IC.py)
             e.g. ['U', 'V', 'W', 'THM', 'PH', 'MU', 'QVAPOR',]
-            
+
         observations (list of dict): Dictionaries which define an observation;
             keys: 
             `error_generate`: measurement error standard-deviation;
@@ -42,21 +42,23 @@ class Experiment(object):
             `heights`: list of integers at which observations are taken;
             `loc_horiz_km`: float of horizontal localization half-width in km;
             `loc_vert_km`: float of vertical localization half-width in km;
-        
+
         use_existing_obsseq (str, False): Path to existing obs_seq.out file (False: generate new one);
             time string is replaced by actual time: /path/%Y-%m-%d_%H:%M_obs_seq.out
-        
+
         dart_nml (dict): updates to the default input.nml of DART (in dart_srcdir)
             keys are namelist section headers (e.g. &filter_nml)
             values are dictionaries of parameters and values (e.g. dict(ens_size=exp.n_ens,))
 
     """
+
     def __init__(self):
         pass
 
+
 class ClusterConfig(object):
     """Collection of variables regarding the cluster configuration
-    
+
     Configuration name docs
 
     When coding, use configuration settings like this:
@@ -89,7 +91,8 @@ class ClusterConfig(object):
         rttov_srcdir (str): Path to RTTOV compile directory, e.g. /home/RTTOV13/rtcoef_rttov13/
         dartwrf_dir (str): Path where DART-WRF scripts reside, e.g. /home/DART-WRF/
 
-        geo_em_for_WRF_ideal (str, False): Path to the geo_em.d01.nc file for idealized nature runs
+        geo_em_nature (str, False): Path to the geo_em.d01.nc file for idealized nature runs
+        geo_em_forecast (str, False): Path to the geo_em.d01.nc file for the forecast domain
         obs_impact_filename
         namelist (str): Path to a WRF namelist template; 
                         strings like <hist_interval>, will be overwritten in scripts/prepare_namelist.py
@@ -100,18 +103,19 @@ class ClusterConfig(object):
                             This configuration can be customized for any job (e.g. in workflows.py)
 
     """
+
     def __init__(self, exp):
         self.exp = exp  # to access the experiment config in here
 
         # defaults
         self.dart_modules = ''
-        self.wrf_modules = '' 
+        self.wrf_modules = ''
         self.size_jobarray = '1'
 
     @property
     def archivedir(self):
         """Path to the directory where data for the experiment is stored
-        
+
         Example:
             `/users/abcd/data/sim_archive/experiment1/`
         """
@@ -124,7 +128,7 @@ class ClusterConfig(object):
         Note:
             If you want to execute scripts from the folder where you develop code, use `self.dartwrf_dir` (not sure if this works)
             If you want to execute the code from a different place ('research'), then use `self.archivedir+'/DART-WRF/'`
-        
+
         Example:
             `/user/data/sim_archive/DART-WRF/dartwrf/`
         """
@@ -160,26 +164,35 @@ class ClusterConfig(object):
         """
         if self.use_slurm:
             from slurmpy import Slurm
-            return Slurm(jobname, slurm_kwargs=dict(self.slurm_cfg, **cfg_update), 
-                  log_dir=self.log_dir, 
-                  scripts_dir=self.slurm_scripts_dir,
-                  ).run(cmd, depends_on=depends_on)
+            return Slurm(jobname, slurm_kwargs=dict(self.slurm_cfg, **cfg_update),
+                         log_dir=self.log_dir,
+                         scripts_dir=self.slurm_scripts_dir,
+                         ).run(cmd, depends_on=depends_on)
         else:
             print(cmd)
             returncode = os.system(cmd)
             if returncode != 0:
                 raise Exception('Error running command >>> '+cmd)
 
+
 userhome = os.path.expanduser('~')
 
-def shell(args):
+
+def shell(args, pythonpath=None):
     print(args)
-    #subprocess.run(args.split(' ')) #, shell=True) #, stderr=subprocess.STDOUT) 
-    return os.system(args)
+    os.system(args)
+    # if pythonpath:
+    #     env = os.environ.copy()
+    #     env['PYTHONPATH'] = pythonpath
+    #     subprocess.check_output(args.split(' '), env=env)
+    # else:
+    #     subprocess.check_output(args.split(' '))
+
 
 def print(*args):
     __builtin__.print(*args, flush=True)
 
+
 def copy(src, dst, remove_if_exists=True):
     if src == dst:
         return  # the link already exists, nothing to do
@@ -190,54 +203,59 @@ def copy(src, dst, remove_if_exists=True):
             pass
     shutil.copy(src, dst)
 
+
 def try_remove(f):
     try:
         os.remove(f)
     except:
         pass
 
+
 def mkdir(path):
     os.system('mkdir -p '+path)
 
+
 def script_to_str(path):
     return open(path, 'r').read()
 
+
 def copy_contents(src, dst):
     os.system('cp -rf '+src+'/* '+dst+'/')
 
+
 def clean_wrfdir(dir):
     for s in ['wrfout_*', 'rsl.*', 'wrfrst_*']:
         for f in glob.glob(dir+'/'+s):
             os.remove(f)
 
+
 def symlink(src, dst):
     """Create a symbolic link from src to dst
+    Creates the folder if it does not exist
     """
-    try:
-        os.symlink(src, dst)
-    except FileExistsError:
-        # print('file exists')
-        if os.path.realpath(dst) == src:
-            pass  # print('link is correct')
-        else:
-            os.remove(dst)
-            os.symlink(src, dst)
-    except Exception as e:
-        raise e
+    try:  # this file may not exist
+        os.remove(dst)
+    except OSError:
+        pass
+
+    os.makedirs(os.path.dirname(dst), exist_ok=True)
+    os.symlink(src, dst)
+
 
 def link_contents(src, dst):
     """Create symbolic links for all files in src to dst
-    
+
     Args:
         src (str): Path to source directory
         dst (str): Path to destination directory
-        
+
     Returns:
         None
     """
     for f in os.listdir(src):
         symlink(src+'/'+f, dst+'/'+f)
 
+
 def sed_inplace(filename, pattern, repl):
     '''Perform the pure-Python equivalent of in-place `sed` substitution
     Like `sed -i -e 's/'${pattern}'/'${repl}' "${filename}"`.
@@ -270,6 +288,7 @@ def sed_inplace(filename, pattern, repl):
     shutil.copystat(filename, tmp_file.name)
     shutil.move(tmp_file.name, filename)
 
+
 def append_file(f_main, f_gets_appended):
     """Append the contents of one file to another
 
@@ -284,13 +303,14 @@ def append_file(f_main, f_gets_appended):
     if rc != 0:
         raise RuntimeError('cat '+f_gets_appended+' >> '+f_main)
 
+
 def write_txt(lines, fpath):
     """Write a list of strings to a text file
-    
+
     Args:
         lines (list): List of strings
         fpath (str): Path to file
-        
+
     Returns:
         None
     """
@@ -298,3 +318,13 @@ def write_txt(lines, fpath):
     with open(fpath, "w") as file:
         for line in lines:
             file.write(line+'\n')
+
+
+def save_dict(dictionary, fpath):
+    with open(fpath, 'wb') as f:
+        pickle.dump(dictionary, f)
+
+
+def load_dict(fpath):
+    with open(fpath, 'rb') as f:
+        return pickle.load(f)