Skip to content

Commit 7d730bb

Browse files
Merge pull request #172 from E3SM-Project/2.2.5
2.2.5
2 parents 14a90e3 + 339b6fe commit 7d730bb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+2943
-1009
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ tags
152152

153153
#### visualstudiocode ####
154154
.vscode/*
155-
!.vscode/settings.json
155+
.vscode/settings.json
156156
!.vscode/tasks.json
157157
!.vscode/launch.json
158158
!.vscode/extensions.json

conda/build.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
$PYTHON setup.py install

conda/conda_build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/bin/bash
2-
export VERSION="2.2.4"
2+
export VERSION="2.2.5"
33
export BUILD_NAME="0"
44
export CONDA_BLD_PATH=~/conda-bld
55
USER="e3sm"

processflow/__main__.py

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88

99
from time import sleep
1010

11-
from processflow.lib.events import EventList
1211
from processflow.lib.finalize import finalize
1312
from processflow.lib.initialize import initialize
14-
from processflow.lib.util import print_debug, print_line, print_message
13+
from processflow.lib.util import print_debug, print_line
1514

1615
os.environ['UVCDAT_ANONYMOUS_LOG'] = 'no'
1716
os.environ['NCO_PATH_OVERRIDE'] = 'no'
@@ -27,8 +26,6 @@ def main(cl_args=None):
2726
kwargs (dict): when running in test mode, arguments are passed directly through the kwargs
2827
which bypasses the argument parsing.
2928
"""
30-
# create global EventList
31-
event_list = EventList()
3229

3330
# The master configuration object
3431
config = {}
@@ -43,12 +40,10 @@ def main(cl_args=None):
4340
" command line: {}".format(cl_args,
4441
' '.join(sys.argv[:])))
4542

46-
config, runmanager = initialize(
47-
argv=cl_args,
48-
event_list=event_list)
43+
config, runmanager = initialize(argv=cl_args)
4944

5045
if isinstance(config, int):
51-
print("Error in setup, exiting")
46+
print_line("Error in setup, exiting", status='error')
5247
return -1
5348
logging.info('Config setup complete')
5449
debug = True if config['global'].get('debug') else False
@@ -67,43 +62,43 @@ def main(cl_args=None):
6762
while True:
6863

6964
if debug:
70-
print_line(' -- checking data -- ', event_list)
65+
print_line(' -- checking data --')
7166
runmanager.check_data_ready()
7267

7368
if debug:
74-
print_line(' -- starting ready jobs --', event_list)
69+
print_line(' -- starting ready jobs --')
7570
runmanager.start_ready_jobs()
7671

7772
if debug:
78-
print_line(' -- monitoring running jobs --', event_list)
73+
print_line(' -- monitoring running jobs --')
7974
runmanager.monitor_running_jobs(debug=debug)
8075

8176
if debug:
82-
print_line(' -- writing out state -- ', event_list)
77+
print_line(' -- writing out state --')
8378
runmanager.write_job_sets(state_path)
8479

8580
status = runmanager.is_all_done()
8681
if status >= 0:
8782
msg = "Finishing up run"
88-
print_line(msg, event_list)
83+
print_line(msg)
8984
finalize(
9085
config=config,
91-
event_list=event_list,
9286
status=status,
9387
runmanager=runmanager)
9488
# SUCCESS EXIT
9589
return 0
9690

9791
if debug:
98-
print_line(' -- sleeping', event_list)
92+
print_line(' -- sleeping')
9993
sleep(loop_delay)
10094
except KeyboardInterrupt as e:
101-
print_message('\n----- KEYBOARD INTERRUPT -----')
102-
runmanager.write_job_sets(state_path)
103-
print_message('----- cleanup complete -----', 'ok')
95+
print_line('----- KEYBOARD INTERRUPT -----', status='err')
96+
if debug:
97+
import ipdb; ipdb.set_trace()
10498
except Exception as e:
105-
print_message('----- AN UNEXPECTED EXCEPTION OCCURED -----')
99+
print_line('----- AN UNEXPECTED EXCEPTION OCCURED -----', status='err')
106100
print_debug(e)
101+
finally:
107102
runmanager.write_job_sets(state_path)
108103
# -----------------------------------------------
109104

processflow/jobs/amwg.py

Lines changed: 96 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,13 @@ def __init__(self, *args, **kwargs):
2424
to pass to the resource manager
2525
"""
2626
super(AMWG, self).__init__(*args, **kwargs)
27+
if not os.environ.get('NCARG_ROOT'):
28+
msg = 'ERROR: NCL doesnt appear to be installed in your environment, unable to run AMWG'
29+
print_line(msg, status='err')
30+
self._status = JobStatus.FAILED
31+
2732
self._job_type = 'amwg'
28-
self._requires = 'climo'
33+
self._requires = ['climo']
2934
self._data_required = ['climo_regrid']
3035

3136
config = kwargs.get('config')
@@ -77,6 +82,7 @@ def __init__(self, *args, **kwargs):
7782
self._host_path = ''
7883
self._output_path = ''
7984

85+
self.setup_job_args(config)
8086
# -----------------------------------------------
8187

8288
def setup_data(self, config, filemanager, case):
@@ -92,7 +98,7 @@ def _dep_filter(self, job):
9298
find the climo job we're waiting for, assuming there's only
9399
one climo job in this case with the same start and end years
94100
"""
95-
if job.job_type != self._requires:
101+
if job.job_type not in self._requires:
96102
return False
97103
if job.start_year != self.start_year:
98104
return False
@@ -131,14 +137,13 @@ def setup_dependencies(self, *args, **kwargs):
131137
self.depends_on.append(self_climo.id)
132138
# -----------------------------------------------
133139

134-
def execute(self, config, event_list, custom_args=None, dryrun=False):
140+
def execute(self, config, *args, custom_args=None, dryrun=False, **kwargs):
135141
"""
136142
Execute the AMWG job
137143
138144
Parameters
139145
----------
140146
config (dict): the global config object
141-
event_list (EventList): an EventList to push notifications into
142147
dryrun (bool): if true this job will generate all scripts,
143148
setup data, and exit without submitting the job
144149
Returns
@@ -210,12 +215,11 @@ def execute(self, config, event_list, custom_args=None, dryrun=False):
210215
output_path=csh_template_out)
211216

212217
# create the run command and submit it
213-
self._has_been_executed = True
214218
cmd = ['csh', csh_template_out]
215-
return self._submit_cmd_to_manager(config, cmd, event_list)
219+
return self._submit_cmd_to_manager(config, cmd)
216220
# -----------------------------------------------
217221

218-
def postvalidate(self, config, event_list, *args, **kwargs):
222+
def postvalidate(self, config, *args, **kwargs):
219223
"""
220224
Validates that the job ran correctly
221225
@@ -261,7 +265,6 @@ def postvalidate(self, config, event_list, *args, **kwargs):
261265
return self._check_tar(
262266
img_source_tar,
263267
img_source,
264-
event_list,
265268
config,
266269
expected_files) == 0
267270
else:
@@ -307,45 +310,43 @@ def postvalidate(self, config, event_list, *args, **kwargs):
307310
number_missing = 0
308311
if os.path.exists(img_source_tar):
309312
number_missing = self._check_tar(
310-
img_source_tar, img_source, event_list, config, expected_files)
313+
img_source_tar, img_source, config, expected_files)
311314

312315
if number_missing == 0:
313316
msg = '{prefix}: all expected output images found'.format(
314317
prefix=self.msg_prefix())
315-
print_line(msg, event_list)
318+
print_line(msg)
316319
logging.info(msg)
317320
self._check_links(config, img_source)
318321
return True
319322
elif number_missing > 0 and number_missing <= 2:
320323
msg = '{prefix}: this job was found to be missing plots, please check the console output for additional information'.format(
321324
prefix=self.msg_prefix())
322-
print_line(msg, event_list)
325+
print_line(msg)
323326
self._check_links(config, img_source)
324327
return True
325328
else:
326329
return False
327330
# -----------------------------------------------
328331

329-
def handle_completion(self, filemanager, event_list, config, *args, **kwargs):
332+
def handle_completion(self, filemanager, config, *args, **kwargs):
330333
"""
331334
Sets up variables needed to web hosting
332335
333336
Parameters
334337
----------
335-
event_list (EventList): an EventList to push user notifications into
338+
filemanager: the global filemanager instance
336339
config (dict): the global config object
337340
"""
338-
if self.status == JobStatus.COMPLETED:
339-
msg = '{prefix}: Job complete'.format(
340-
prefix=self.msg_prefix())
341-
else:
342-
msg = '{prefix}: Job failed'.format(
343-
prefix=self.msg_prefix())
344-
print_line(msg, event_list)
345-
logging.info(msg)
341+
if self.status != JobStatus.COMPLETED:
342+
msg = f'{self.msg_prefix()}: Job failed, not running completion handler'
343+
print_line(msg, status='error')
344+
return
346345

347346
# if hosting is turned off, simply return
348347
if not config['global'].get('host'):
348+
msg = f'{self.msg_prefix()}: Job completion handler done\n'
349+
print_line(msg)
349350
return
350351

351352
img_source = os.path.join(
@@ -361,16 +362,18 @@ def handle_completion(self, filemanager, event_list, config, *args, **kwargs):
361362
else:
362363
msg = '{prefix}: Unable to find output directory or tar archive'.format(
363364
prefix=self.msg_prefix())
364-
print_line(msg, event_list)
365+
print_line(msg)
365366
self.status = JobStatus.FAILED
366367
logging.info(msg)
367368
return
368369

369370
self.setup_hosting(
370371
always_copy=config['global']['always_copy'],
371372
img_source=img_source,
372-
host_path=self._host_path,
373-
event_list=event_list)
373+
host_path=self._host_path)
374+
375+
msg = f'{self.msg_prefix()}: Job completion handler done\n'
376+
print_line(msg)
374377

375378
# -----------------------------------------------
376379

@@ -475,11 +478,11 @@ def _change_input_file_names(self):
475478
os.rename(input_file, new_name)
476479
# -----------------------------------------------
477480

478-
def _check_tar(self, img_source_tar, img_source, event_list, config, expected_files):
481+
def _check_tar(self, img_source_tar, img_source, config, expected_files):
479482
number_missing = 0
480483
msg = '{prefix}: extracting images from tar archive'.format(
481484
prefix=self.msg_prefix())
482-
print_line(msg, event_list)
485+
print_line(msg)
483486
call(['tar', '-xf', img_source_tar,
484487
'--directory', self._output_path])
485488
passed = True
@@ -497,7 +500,7 @@ def _check_tar(self, img_source_tar, img_source, event_list, config, expected_fi
497500
prefix=self.msg_prefix(),
498501
dir=setpath)
499502
logging.error(msg)
500-
print_line(msg, event_list)
503+
print_line(msg)
501504
else:
502505
count = len(os.listdir(setpath))
503506
if count < expected_files[setname]:
@@ -507,6 +510,71 @@ def _check_tar(self, img_source_tar, img_source, event_list, config, expected_fi
507510
numProduced=count,
508511
numExpected=expected_files[setname])
509512
logging.error(msg)
510-
print_line(msg, event_list)
513+
print_line(msg)
511514
number_missing += 1
512515
return number_missing
516+
517+
def validate(self, config):
518+
messages = []
519+
if 'job_options' in config.keys():
520+
amwg_global_config = config['job_options'].get('amwg')
521+
if amwg_global_config:
522+
if 'diag_home' not in amwg_global_config.keys():
523+
msg = "Global job_options does not contain the amwg code path"
524+
messages.append(msg)
525+
if not amwg_global_config.get('frequency'):
526+
config['job_options']['amwg']['frequency'] = set()
527+
if not isinstance(amwg_global_config['frequency'], set):
528+
amwg_global_config['frequency'] = set(amwg_global_config['frequency'])
529+
amwg_sets = amwg_global_config.get('plot_sets')
530+
if amwg_sets:
531+
if not isinstance(amwg_sets, set):
532+
amwg_sets = set(amwg_sets)
533+
allowed_sets = set([str(x) for x in range(1, 17)] + ['all', '4a'])
534+
for s in amwg_sets:
535+
if s not in allowed_sets:
536+
msg = "{} is not an allowed AMWG set".format(s)
537+
messages.append(msg)
538+
539+
for case in config['simulations']:
540+
if "amwg" not in case['jobs']:
541+
continue
542+
case_options = case['jobs']['amwg']
543+
544+
freqs = case_options.get('frequency')
545+
if freqs:
546+
if not isinstance(freqs, set):
547+
freqs = set(freqs)
548+
freqs.update(amwg_global_config['frequency'])
549+
550+
case_sets = case_options.get('plot_sets')
551+
if case_sets:
552+
if not isinstance(case_sets, set):
553+
case_sets = set(case_sets)
554+
allowed_sets = set([str(x) for x in range(1, 17)] + ['all', '4a'])
555+
for s in case_sets:
556+
if s not in allowed_sets:
557+
msg = "case {case} contains invalid AMWG set {set}".format(
558+
case=case_options['shortname'], set=s)
559+
messages.append(msg)
560+
case_sets.update(amwg_sets)
561+
562+
if not amwg_global_config.get('output_grid_name') and not case_options.get('output_grid_name'):
563+
msg = 'Please specify a name for the climo output_grid_name in either the global job_options under amwg, or under the job entry in each simulation\'s config'
564+
messages.append(msg)
565+
else:
566+
if not case_options.get('output_grid_name'):
567+
case_options['output_grid_name'] = amwg_global_config.get('output_grid_name')
568+
if not amwg_global_config.get('atm_map_path') and not case_options.get('atm_map_path'):
569+
msg = 'Please specify a atm_map_path in either the global job_options under amwg, or under the job entry in each simulation\'s config'
570+
messages.append(msg)
571+
else:
572+
if not case_options.get('atm_map_path'):
573+
case_options['atm_map_path'] = amwg_global_config.get('atm_map_path')
574+
575+
if messages:
576+
return messages
577+
return None
578+
579+
580+

0 commit comments

Comments
 (0)