From 236e0054aa6c4f4e247b01a2d2b4ff5da3d037c8 Mon Sep 17 00:00:00 2001
From: Karl-Hermann Wieners <karl-hermann.wieners@mpimet.mpg.de>
Date: Thu, 16 Jun 2022 09:38:27 +0200
Subject: [PATCH] mkexp/expconfig: replace deep copy of Sections by ordered
 dict copy

Fixes long run times for extended jobs/namelist groups
---
 CHANGES.txt  |  1 +
 expconfig.py | 24 ++++++++++++++++++++++++
 mkexp        | 11 ++++++-----
 3 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 89b0edd..001d56d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -20,6 +20,7 @@ Tools
 -----
 
 * importexp: fixed extraction of start/end date in input scripts
+* mkexp: fixed long run times for extended jobs/namelist groups
 
 Release 1.1.4
 =============
diff --git a/expconfig.py b/expconfig.py
index 419df57..977b5fc 100644
--- a/expconfig.py
+++ b/expconfig.py
@@ -4,6 +4,7 @@ Generate an earth system model configuration from the given configuration file.
 $Id$
 '''
 
+import collections
 import io
 import locale
 import os
@@ -36,6 +37,29 @@ def is_not_empty(arg):
     else:
         return [_f for _f in [x.rstrip() for x in arg] if _f]
 
+def odict(self):
+    '''Return a deepcopy of self as an ordered dictionary.
+
+    >>> n = odict(a)
+    >>> n == a
+    1
+    >>> n is a
+    0
+    '''
+    newdict = collections.OrderedDict()
+    for entry in self:
+        this_entry = self[entry]
+        if isinstance(this_entry, configobj.Section):
+            this_entry = odict(this_entry)
+        elif isinstance(this_entry, list):
+            # create a copy rather than a reference
+            this_entry = list(this_entry)
+        elif isinstance(this_entry, tuple):
+            # create a copy rather than a reference
+            this_entry = tuple(this_entry)
+        newdict[entry] = this_entry
+    return newdict
+
 def merge_comments(this, indict):
     '''Merge comments from indict into current configuration. 
 
diff --git a/mkexp b/mkexp
index 64de09a..04d666f 100755
--- a/mkexp
+++ b/mkexp
@@ -7,7 +7,6 @@
 
 from __future__ import print_function
 
-import copy
 import io
 import os
 import re
@@ -500,14 +499,15 @@ def extend(subjob, jobs_config, extended_jobs, job_dict=None):
 
         # Add parent config
         if extended_job:
-            pre_config.merge(copy.deepcopy(jobs_config[extended_job]))
+            pre_config.merge(expconfig.odict(jobs_config[extended_job]))
+            expconfig.merge_comments(pre_config, jobs_config[extended_job])
 
         # Add actual subjob config
         pre_config.merge(subconfig)
 
         # Replace subjob config by extended config
         jobs_config[subjob] = {}
-        jobs_config[subjob].merge(pre_config)
+        jobs_config[subjob].merge(expconfig.odict(pre_config))
         expconfig.merge_comments(jobs_config[subjob], pre_config)
         del pre_config
 
@@ -564,8 +564,9 @@ for subjob, subconfig in jobs_config.items():
         # Prepare namelists for inclusion in scripts
         var_format = job_config['JOB'].get('.var_format', '${%s}')
         var_list = set()
-        del job_config['jobs']
-        job_config['jobs'] = copy.deepcopy(jobs_config)
+        job_config['jobs'] = {}
+        job_config['jobs'].merge(expconfig.odict(jobs_config))
+        expconfig.merge_comments(job_config['jobs'], jobs_config)
         job_config.walk(format_vars, log=var_list, fmt=var_format)
         job_config['VARIABLES_'] = var_list
         for namelist, groups in job_config['namelists'].items():
-- 
GitLab