-
Notifications
You must be signed in to change notification settings - Fork 0
/
Simulations.py
3046 lines (2511 loc) · 113 KB
/
Simulations.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -*- coding: utf-8 -*-
"""
Created on Tue Nov 1 15:16:34 2011
@author: dave
"""
# NOTE TO SELF: PLOTTING IN DIFFERENT MODULE. KEEP THIS AS SIMPLE AS POSSIBLE
# for the the dependencies, so you can run it easily on the cluster
from __future__ import division
# standard python library
import os
import subprocess as sproc
import copy
import zipfile
import shutil
import datetime
import math
import pickle
# what is actually the difference between warnings and logging.warn?
# for which context is which better?
#import warnings
import logging
from operator import itemgetter
from time import time
# numpy and scipy only used in HtcMaster._all_in_one_blade_tag
import numpy as np
import scipy
import scipy.interpolate as interpolate
# custom libraries
import misc
import HawcPy
def load_pickled_file(source):
FILE = open(source, 'rb')
result = pickle.load(FILE)
FILE.close()
return result
def save_pickle(source, variable):
FILE = open(source, 'wb')
pickle.dump(variable, FILE, protocol=2)
FILE.close()
def write_file(file_path, file_contents, mode):
"""
INPUT:
file_path: path/to/file/name.csv
string : file contents is a string
mode : reading (r), writing (w), append (a),...
"""
FILE = open(file_path, mode)
FILE.write(file_contents)
FILE.close()
def create_multiloop_list(iter_dict, debug=False):
"""
Create a list based on multiple nested loops
============================================
Considerd the following example
>>> for v in range(V_start, V_end, V_delta):
>>> for y in range(y_start, y_end, y_delta):
>>> for c in range(c_start, c_end, c_delta):
>>> print v, y, c
Could be replaced by a list with all these combinations. In order to
replicate this with create_multiloop_list, iter_dict should have
the following structure
>>> iter_dict = dict()
>>> iter_dict['v'] = range(V_start, V_end, V_delta)
>>> iter_dict['y'] = range(y_start, y_end, y_delta)
>>> iter_dict['c'] = range(c_start, c_end, c_delta)
>>> iter_list = create_multiloop_list(iter_dict)
>>> for case in iter_list:
>>> print case['v'], case['y'], case['c']
Parameters
----------
iter_dict : dictionary
Key holds a valid tag as used in HtcMaster.tags. The corresponding
value shouuld be a list of values to be considered.
Output
------
iter_list : list
List containing dictionaries. Each entry is a combination of the
given iter_dict keys.
Example
-------
>>> iter_dict={'[wind]':[5,6,7],'[coning]':[0,-5,-10]}
>>> create_multiloop_list(iter_dict)
[{'[wind]': 5, '[coning]': 0},
{'[wind]': 5, '[coning]': -5},
{'[wind]': 5, '[coning]': -10},
{'[wind]': 6, '[coning]': 0},
{'[wind]': 6, '[coning]': -5},
{'[wind]': 6, '[coning]': -10},
{'[wind]': 7, '[coning]': 0},
{'[wind]': 7, '[coning]': -5},
{'[wind]': 7, '[coning]': -10}]
"""
iter_list = []
# fix the order of the keys
key_order = iter_dict.keys()
nr_keys = len(key_order)
nr_values,indices = [],[]
# determine how many items on each key
for key in key_order:
# each value needs to be an iterable! len() will fail if it isn't
# count how many values there are for each key
nr_values.append(len(iter_dict[key]))
# create an initial indices list
indices.append(0)
if debug: print nr_values, indices
go_on = True
# keep track on which index you are counting, start at the back
loopkey = nr_keys -1
cc = 0
while go_on:
if debug: print indices
# Each entry on the list is a dictionary with the parameter combination
iter_list.append(dict())
# save all the different combination into one list
for keyi in range(len(key_order)):
key = key_order[keyi]
# add the current combination of values as one dictionary
iter_list[cc][key] = iter_dict[key][indices[keyi]]
# +1 on the indices of the last entry, the overflow principle
indices[loopkey] += 1
# cycle backwards thourgh all dimensions and propagate the +1 if the
# current dimension is full. Hence overflow.
for k in range(loopkey,-1,-1):
# if the current dimension is over its max, set to zero and change
# the dimension of the next. Remember we are going backwards
if not indices[k] < nr_values[k] and k > 0:
# +1 on the index of the previous dimension
indices[k-1] += 1
# set current loopkey index back to zero
indices[k] = 0
# if the previous dimension is not on max, break out
if indices[k-1] < nr_values[k-1]:
break
# if we are on the last dimension, break out if that is also on max
elif k == 0 and not indices[k] < nr_values[k]:
if debug: print cc
go_on = False
# fail safe exit mechanism...
if cc > 20000:
raise UserWarning, 'multiloop_list has already '+str(cc)+' items..'
go_on = False
cc += 1
return iter_list
def local_shell_script(htc_dict, sim_id):
"""
"""
shellscript = ''
breakline = '"' + '*'*80 + '"'
nr_cases = len(htc_dict)
nr = 1
for case in htc_dict:
shellscript += 'echo ""' + '\n'
shellscript += 'echo ' + breakline + '\n' + 'echo '
shellscript += '" ===> Progress:'+str(nr)+'/'+str(nr_cases)+'"\n'
# get a shorter version for the current cases tag_dict:
scriptpath = htc_dict[case]['[run_dir]'] + 'runall.sh'
wine = 'WINEDEBUG=-all WINEARCH=win32 WINEPREFIX=~/.wine32 wine'
shellscript += wine + " hawc2mb.exe htc/" + case + "\n"
shellscript += 'echo ' + breakline + '\n'
nr+=1
write_file(scriptpath, shellscript, 'w')
print '\nrun local shell script written to:'
print scriptpath
def run_local(cases, silent=False, check_log=True):
"""
Run all HAWC2 simulations locally from cases
===============================================
Run all case present in a cases dict locally and wait until HAWC2 is ready.
In verbose mode, each HAWC2 simulation is also timed
Parameters
----------
cases : dict{ case : dict{tag : value} }
Dictionary where each case is a key and its value a dictionary holding
all the tags/value pairs as used for that case
check_log : boolean, default=False
Check the log file emmidiately after execution of the HAWC2 case
silent : boolean, default=False
When False, usefull information will be printed and the HAWC2
simulation time will be calculated from the Python perspective. The
silent variable is also passed on to logcheck_case
Returns
-------
cases : dict{ case : dict{tag : value} }
Update cases with the STDOUT of the respective HAWC2 simulation
"""
# remember the current working directory
cwd = os.getcwd()
nr = len(cases)
if not silent:
print ''
print '='*79
print 'Be advised, launching %i HAWC2 simulation(s) sequentially' % nr
print 'run dir: %s' % cases[cases.keys()[0]]['[run_dir]']
print ''
if check_log:
errorlogs = ErrorLogs(silent=silent)
for ii, case in enumerate(cases):
# all tags for the current case
tags = cases[case]
# the launch command
cmd = 'WINEDEBUG=-all WINEARCH=win32 WINEPREFIX=~/.wine32 wine'
cmd += " hawc2mb.exe " + tags['[htc_dir]'] + case
# remove any escaping in tags and case for security reasons
cmd = cmd.replace('\\','')
# browse to the correct launch path for the HAWC2 simulation
os.chdir(tags['[run_dir]'])
if not silent:
start = time()
progress = '%4i/%i : %s%s' % (ii+1, nr, tags['[htc_dir]'], case)
print '*'*75
print progress
# and launch the HAWC2 simulation
p = sproc.Popen(cmd,stdout=sproc.PIPE,stderr=sproc.STDOUT,shell=True)
# save the output that HAWC2 sends to the shell to the cases
# note that this is a list, each item holding a line
cases[case]['sim_STDOUT'] = p.stdout.readlines()
# wait until HAWC2 finished doing its magic
p.wait()
if not silent:
# print the simulation command line output
print ' ' + '-'*75
print ''.join(cases[case]['sim_STDOUT'])
print ' ' + '-'*75
# caclulation time
stp = time() - start
stpmin = stp/60.
print 'HAWC2 execution time: %8.2f sec (%8.2f min)' % (stp,stpmin)
# where there any errors in the output? If yes, abort
for k in cases[case]['sim_STDOUT']:
kstart = k[:14]
if kstart in [' *** ERROR ***', 'forrtl: severe']:
cases[case]['[hawc2_sim_ok]'] = False
#raise UserWarning, 'Found error in HAWC2 STDOUT'
else:
cases[case]['[hawc2_sim_ok]'] = True
# check the log file strait away if required
if check_log:
start = time()
errorlogs = logcheck_case(errorlogs, cases, case, silent=silent)
stop = time() - start
if case.endswith('.htc'):
kk = case[:-4] + '.log'
else:
kk = case + '.log'
errors = errorlogs.MsgListLog2[kk][0]
exitok = errorlogs.MsgListLog2[kk][1]
if not silent:
print 'log checks took %5.2f sec' % stop
print ' found error: ', errors
print ' exit correctly: ', exitok
print '*'*75
print
# also save in cases
if not errors and exitok:
cases[case]['[hawc2_sim_ok]'] = True
else:
cases[case]['[hawc2_sim_ok]'] = False
if check_log:
# take the last case to determine sim_id, run_dir and log_dir
sim_id = cases[case]['[sim_id]']
run_dir = cases[case]['[run_dir]']
log_dir = cases[case]['[log_dir]']
# save the extended (.csv format) errorlog list?
# but put in one level up, so in the logfiles folder directly
errorlogs.ResultFile = sim_id + '_ErrorLog.csv'
# use the model path of the last encoutered case in cases
errorlogs.PathToLogs = run_dir + log_dir
errorlogs.save()
# just in case, browse back the working path relevant for the python magic
os.chdir(cwd)
if not silent:
print '\nHAWC2 has done all of its sequential magic!'
print '='*79
print ''
return cases
def prepare_launch(iter_dict, opt_tags, master, variable_tag_func,
write_htc=True, runmethod='local', verbose=False,
copyback_turb=True, msg='', silent=False, check_log=True):
"""
Create the htc files, pbs scripts and replace the tags in master file
=====================================================================
Do not use any uppercase letters in the filenames, since HAWC2 will
convert all of them to lower case results file names (.sel, .dat, .log)
create sub folders according to sim_id, in order to not create one
folder for the htc, results, logfiles which grows very large in due
time!!
opt_tags is a list of dictionaries of tags:
[ {tag1=12,tag2=23,..},{tag1=11, tag2=33, tag9=5,...},...]
for each wind, yaw and coning combi, each tag dictionary in the list
will be set.
Make sure to always define all dictionary keys in each list, otherwise
the value of the first appareance will remain set for the remaining
simulations in the list.
For instance, in the example above, if tag9=5 is not set for subsequent
lists, tag9 will remain having value 5 for these subsequent sets
The tags for each case are consequently set in following order (or
presedence):
* master
* opt_tags
* iter_dict
* variable_tag_func
Parameters
----------
iter_dict : dict
opt_tags : dict
master : HtcMaster object
variable_tag_func : function object
write_htc : boolean, default=True
verbose : boolean, default=False
runmethod : {'local' (default),'thyra','gorm','local-script','none'}
Specify how/what to run where. For local, each case in cases is
run locally via python directly. If set to 'local-script' a shell
script is written to run all cases locally sequential. If set to
'thyra' or 'gorm', PBS scripts are written to the respective server.
msg : str, default=''
A descriptive message of the simulation series is saved at
"post_dir + master.tags['[sim_id]'] + '_tags.txt'". Additionally, this
tagfile also holds the opt_tags and iter_dict values.
Returns
-------
cases : dict{ case : dict{tag : value} }
Dictionary where each case is a key and its value a dictionary holding
all the tags/value pairs as used for that case
"""
cases = dict()
# if empty, just create a dummy item so we get into the loops
if len(iter_dict) == 0:
iter_dict = {'__dummy__': [0]}
combi_list = create_multiloop_list(iter_dict)
# load the master htc file as a string under the master.tags
master.loadmaster()
# create the execution folder structure and copy all data to it
master.copy_model_data()
# create the zip file
master.create_model_zip()
# ignore if the opt_tags is empty, will result in zero
if len(opt_tags) > 0:
sim_total = len(combi_list)*len(opt_tags)
else:
sim_total = len(combi_list)
# if no opt_tags specified, create an empty dummy tag
opt_tags = [dict({'__DUMMY_TAG__' : 0})]
sim_nr = 0
# cycle thourgh all the combinations
for it in combi_list:
for ot in opt_tags:
sim_nr += 1
# update the tags from the opt_tags list
if not '__DUMMY_TAG__' in ot:
master.tags.update(ot)
# update the tags set in the combi_list
master.tags.update(it)
# -----------------------------------------------------------
# start variable tags update
master = variable_tag_func(master)
# end variable tags
# -----------------------------------------------------------
if not silent:
print 'htc progress: ' + format(sim_nr, '3.0f') + '/' + \
format(sim_total, '3.0f')
if verbose:
print '===master.tags===\n', master.tags
# returns a dictionary with all the tags used for this
# specific case
htc = master.createcase(write_htc=write_htc)
#htc=master.createcase_check(cases_repo,write_htc=write_htc)
# make sure the current cases is unique!
if htc.keys()[0] in cases:
msg = 'non unique case in cases: %s' % htc.keys()[0]
raise KeyError, msg
# save in the big cases. Note that values() gives a copy!
cases[htc.keys()[0]] = htc.values()[0]
if verbose:
print 'created cases for: ' + \
master.tags['[case_id]'] + '.htc\n'
post_dir = master.tags['[post_dir]']
# create directory if post_dir does not exists
try:
os.mkdir(post_dir)
except OSError:
pass
FILE = open(post_dir + master.tags['[sim_id]'] + '.pkl', 'wb')
pickle.dump(cases, FILE, protocol=2)
FILE.close()
if not silent:
print '\ncases saved at:'
print post_dir + master.tags['[sim_id]'] + '.pkl'
# also save the iter_dict and opt_tags in a text file for easy reference
# or quick checks on what each sim_id actually contains
# sort the taglist for convienent reading/comparing
tagfile = msg + '\n\n'
tagfile += '='*79 + '\n'
tagfile += 'iter_dict\n'.rjust(30)
tagfile += '='*79 + '\n'
iter_dict_list = sorted(iter_dict.iteritems(), key=itemgetter(0))
for k in iter_dict_list:
tagfile += str(k[0]).rjust(30) + ' : ' + str(k[1]).ljust(20) + '\n'
tagfile += '\n'
tagfile += '='*79 + '\n'
tagfile += 'opt_tags\n'.rjust(30)
tagfile += '='*79 + '\n'
for k in opt_tags:
tagfile += '\n'
tagfile += '-'*79 + '\n'
tagfile += 'opt_tags set\n'.rjust(30)
tagfile += '-'*79 + '\n'
opt_dict = sorted(k.iteritems(), key=itemgetter(0), reverse=False)
for kk in opt_dict:
tagfile += str(kk[0]).rjust(30)+' : '+str(kk[1]).ljust(20) + '\n'
write_file(post_dir + master.tags['[sim_id]'] + '_tags.txt', tagfile, 'w')
launch(cases, runmethod=runmethod, verbose=verbose,
copyback_turb=copyback_turb, check_log=check_log)
return cases
def prepare_relaunch(cases, runmethod='gorm', verbose=False, write_htc=True,
copyback_turb=True, silent=False, check_log=True):
"""
Instead of redoing everything, we know recreate the HTC file for those
in the given cases dict. Nothing else changes. The data and zip files
are not updated, the convience tagfile is not recreated. However, the
saved (pickled) cases dict corresponding to the sim_id is updated!
This method is usefull to correct mistakes made for some cases.
It is adviced to not change the case_id, sim_id, from the cases.
"""
# initiate the HtcMaster object, load the master file
master = HtcMaster()
# for invariant tags, load random case. Necessary before we can load
# the master file, otherwise we don't know which master to load
master.tags = cases[cases.keys()[0]]
master.loadmaster()
# load the original cases dict
post_dir = master.tags['[post_dir]']
FILE = open(post_dir + master.tags['[sim_id]'] + '.pkl', 'rb')
cases_orig = pickle.load(FILE)
FILE.close()
sim_nr = 0
sim_total = len(cases)
for case, casedict in cases.iteritems():
sim_nr += 1
# set all the tags in the HtcMaster file
master.tags = casedict
# returns a dictionary with all the tags used for this
# specific case
htc = master.createcase(write_htc=write_htc)
#htc=master.createcase_check(cases_repo,write_htc=write_htc)
if not silent:
print 'htc progress: ' + format(sim_nr, '3.0f') + '/' + \
format(sim_total, '3.0f')
if verbose:
print '===master.tags===\n', master.tags
# make sure the current cases already exists, otherwise we are not
# relaunching!
if case not in cases_orig:
msg = 'relaunch only works for existing cases: %s' % case
raise KeyError, msg
# save in the big cases. Note that values() gives a copy!
# remark, what about the copying done at the end of master.createcase?
# is that redundant then?
cases[htc.keys()[0]] = htc.values()[0]
if verbose:
print 'created cases for: ' + \
master.tags['[case_id]'] + '.htc\n'
launch(cases, runmethod=runmethod, verbose=verbose, check_log=check_log,
copyback_turb=copyback_turb, silent=silent)
# update the original file: overwrite the newly set cases
FILE = open(post_dir + master.tags['[sim_id]'] + '.pkl', 'wb')
cases_orig.update(cases)
pickle.dump(cases_orig, FILE, protocol=2)
FILE.close()
def prepare_launch_cases(cases, runmethod='gorm', verbose=False,write_htc=True,
copyback_turb=True, silent=False, check_log=True,
variable_tag_func=None, sim_id_new=None):
"""
Same as prepare_launch, but now the input is just a cases object (cao).
If relaunching some earlier defined simulations, make sure to at least
rename the sim_id, otherwise it could become messy: things end up in the
same folder, sim_id post file get overwritten, ...
In case you do not use a variable_tag_fuc, make sure all your tags are
defined in cases. First and foremost, this means that the case_id does not
get updated to have a new sim_id, the path's are not updated, etc
When given a variable_tag_func, make sure it is properly
defined: do not base a variable tag's value on itself to avoid value chains
The master htc file will be loaded and alls tags defined in the cases dict
will be applied to it as is.
"""
# initiate the HtcMaster object, load the master file
master = HtcMaster()
# for invariant tags, load random case. Necessary before we can load
# the master file, otherwise we don't know which master to load
master.tags = cases[cases.keys()[0]]
# load the master htc file as a string under the master.tags
master.loadmaster()
# create the execution folder structure and copy all data to it
# but reset to the correct launch dirs first
sim_id = master.tags['[sim_id]']
if runmethod in ['local', 'local-script', 'none']:
master.tags['[run_dir]'] = 'simulations/hawc2/%s/' % sim_id
elif runmethod == 'thyra':
master.tags['[run_dir]'] = '/mnt/thyra/HAWC2/ojf_post/%s/' % sim_id
elif runmethod == 'gorm':
master.tags['[run_dir]'] = '/mnt/gorm/HAWC2/ojf_post/%s/' % sim_id
else:
msg='unsupported runmethod, options: none, local, thyra, gorm, opt'
raise ValueError, msg
master.copy_model_data()
# create the zip file
master.create_model_zip()
sim_nr = 0
sim_total = len(cases)
# for safety, create a new cases dict. At the end of the ride both cases
# and cases_new should be identical!
cases_new = {}
# cycle thourgh all the combinations
for case, casedict in cases.iteritems():
sim_nr += 1
sim_id = casedict['[sim_id]']
# reset the launch dirs
if runmethod in ['local', 'local-script', 'none']:
casedict['[run_dir]'] = 'simulations/hawc2/%s/' % sim_id
elif runmethod == 'thyra':
casedict['[run_dir]'] = '/mnt/thyra/HAWC2/ojf_post/%s/' % sim_id
elif runmethod == 'gorm':
casedict['[run_dir]'] = '/mnt/gorm/HAWC2/ojf_post/%s/' % sim_id
else:
msg='unsupported runmethod, options: none, local, thyra, gorm, opt'
raise ValueError, msg
# -----------------------------------------------------------
# set all the tags in the HtcMaster file
master.tags = casedict
# apply the variable tags if applicable
if variable_tag_func:
master = variable_tag_func(master)
elif sim_id_new:
# TODO: finish this
# replace all the sim_id occurences with the updated one
# this means also the case_id tag changes!
pass
# -----------------------------------------------------------
# returns a dictionary with all the tags used for this specific case
htc = master.createcase(write_htc=write_htc)
if not silent:
print 'htc progress: ' + format(sim_nr, '3.0f') + '/' + \
format(sim_total, '3.0f')
if verbose:
print '===master.tags===\n', master.tags
# make sure the current cases is unique!
if htc.keys()[0] in cases_new:
msg = 'non unique case in cases: %s' % htc.keys()[0]
raise KeyError, msg
# save in the big cases. Note that values() gives a copy!
# remark, what about the copying done at the end of master.createcase?
# is that redundant then?
cases_new[htc.keys()[0]] = htc.values()[0]
if verbose:
print 'created cases for: ' + \
master.tags['[case_id]'] + '.htc\n'
post_dir = master.tags['[post_dir]']
# create directory if post_dir does not exists
try:
os.mkdir(post_dir)
except OSError:
pass
FILE = open(post_dir + master.tags['[sim_id]'] + '.pkl', 'wb')
pickle.dump(cases_new, FILE, protocol=2)
FILE.close()
if not silent:
print '\ncases saved at:'
print post_dir + master.tags['[sim_id]'] + '.pkl'
launch(cases_new, runmethod=runmethod, verbose=verbose,
copyback_turb=copyback_turb, check_log=check_log)
return cases_new
def launch(cases, runmethod='local', verbose=False, copyback_turb=True,
silent=False, check_log=True):
"""
The actual launching of all cases in the Cases dictionary. Note that here
only the PBS files are written and not the actuall htc files.
Parameters
----------
cases : dict
Dictionary with the case name as key and another dictionary as value.
The latter holds all the tag/value pairs used in the respective
simulation.
verbose : boolean, default=False
runmethod : {'local' (default),'thyra','gorm','local-script','none'}
Specify how/what to run where. For local, each case in cases is
run locally via python directly. If set to 'local-script' a shell
script is written to run all cases locally sequential. If set to
'thyra' or 'gorm', PBS scripts are written to the respective server.
"""
random_case = cases.keys()[0]
sim_id = cases[random_case]['[sim_id]']
pbs_out_dir = cases[random_case]['[pbs_out_dir]']
if runmethod == 'local-script':
local_shell_script(cases, sim_id)
elif runmethod in ['thyra','gorm']:
# create the pbs object
pbs = PBS(cases, server=runmethod)
pbs.copyback_turb = copyback_turb
pbs.verbose = verbose
pbs.pbs_out_dir = pbs_out_dir
pbs.create()
elif runmethod == 'local':
cases = run_local(cases, silent=silent, check_log=check_log)
elif runmethod == 'none':
pass
else:
msg = 'unsupported runmethod, valid options: local, thyra, gorm or opt'
raise ValueError, msg
def post_launch(cases):
"""
Do some basics checks: do all launched cases have a result and LOG file
and are there any errors in the LOG files?
Parameters
----------
cases : either a string (path to file) or the cases itself
"""
# TODO: finish support for default location of the cases and file name
# two scenario's: either pass on an cases and get from their the
# post processing path or pass on the simid and load from the cases
# from the default location
# in case run_local, do not check PBS!
# in case it is a path, load the cases
if type(cases).__name__ == 'str':
cases = load_pickled_file(cases)
# saving output to textfile and print at the same time
LOG = Log()
LOG.print_logging = True
# load one case dictionary from the cases to get data that is the same
# over all simulations in the cases
master = cases.keys()[0]
post_dir = cases[master]['[post_dir]']
sim_id = cases[master]['[sim_id]']
run_dir = cases[master]['[run_dir]']
log_dir = cases[master]['[log_dir]']
# for how many of the created cases are there actually result, log files
pbs = PBS(cases)
pbs.cases = cases
cases_fail = pbs.check_results(cases)
# add the failed cases to the LOG:
LOG.add(['number of failed cases: ' + str(len(cases_fail))])
LOG.add(list(cases_fail))
# for k in cases_fail:
# print k
# initiate the object to check the log files
errorlogs = ErrorLogs()
LOG.add(['checking ' + str(len(cases)) + ' LOG files...'])
nr = 1
nr_tot = len(cases)
tmp = cases.keys()[0]
print 'checking logs, path (from a random item in cases):'
print run_dir + log_dir
for k in cases:
# if it did not fail, we can read the logfile, otherwise not
if k not in cases_fail:
# see if there is an htc extension still standing
if k.endswith('.htc'):
kk = k[:-4] + '.log'
else:
kk = k + '.log'
errorlogs.PathToLogs = run_dir + log_dir + kk
errorlogs.check()
print 'checking logfile progress: ' + str(nr) + '/' + str(nr_tot)
nr += 1
# if simulation did not ended correctly, put it on the fail list
if not errorlogs.MsgListLog2[kk][1]:
cases_fail[k] = cases[k]
# now see how many cases resulted in an error and add to the general LOG
# determine how long the first case name is
spacing = len(errorlogs.MsgListLog2.keys()[0]) + 9
LOG.add(['display log check'.ljust(spacing) + 'found_error?'.ljust(15) + \
'exit_correctly?'])
for k in errorlogs.MsgListLog2:
LOG.add([k.ljust(spacing)+str(errorlogs.MsgListLog2[k][0]).ljust(15)+\
str(errorlogs.MsgListLog2[k][1]) ])
# save the extended (.csv format) errorlog list?
# but put in one level up, so in the logfiles folder directly
errorlogs.ResultFile = sim_id + '_ErrorLog.csv'
# use the model path of the last encoutered case in cases
errorlogs.PathToLogs = run_dir + log_dir
errorlogs.save()
# save the error LOG list, this is redundant, since it already exists in
# the general LOG file (but only as a print, not the python variable)
tmp = post_dir + sim_id + '_MsgListLog2'
save_pickle(tmp, errorlogs.MsgListLog2)
# save the list of failed cases
save_pickle(post_dir + sim_id + '_fail.pkl', cases_fail)
return cases_fail
def logcheck_case(errorlogs, cases, case, silent=False):
"""
Check logfile of a single case
==============================
Given the cases and a case, check that single case on errors in the
logfile.
"""
#post_dir = cases[case]['[post_dir]']
#sim_id = cases[case]['[sim_id]']
run_dir = cases[case]['[run_dir]']
log_dir = cases[case]['[log_dir]']
if case.endswith('.htc'):
caselog = case[:-4] + '.log'
else:
caselog = case + '.log'
errorlogs.PathToLogs = run_dir + log_dir + caselog
errorlogs.check()
# in case we find an error, abort or not?
errors = errorlogs.MsgListLog2[caselog][0]
exitcorrect = errorlogs.MsgListLog2[caselog][1]
if errors:
# print all error messages
#logs.MsgListLog : [ [case, line nr, error1, line nr, error2, ....], ]
# difficult: MsgListLog is not a dict!!
#raise UserWarning, 'HAWC2 simulation has errors in logfile, abort!'
#warnings.warn('HAWC2 simulation has errors in logfile!')
logging.warn('HAWC2 simulation has errors in logfile!')
elif not exitcorrect:
#raise UserWarning, 'HAWC2 simulation did not ended correctly, abort!'
#warnings.warn('HAWC2 simulation did not ended correctly!')
logging.warn('HAWC2 simulation did not ended correctly!')
# no need to do that, aborts on failure anyway and OK log check will be
# printed in run_local when also printing how long it took to check
#if not silent:
#print 'log checks ok'
#print ' found error: %s' % errorlogs.MsgListLog2[caselog][0]
#print 'exit correctly: %s' % errorlogs.MsgListLog2[caselog][1]
return errorlogs
## save the extended (.csv format) errorlog list?
## but put in one level up, so in the logfiles folder directly
#errorlogs.ResultFile = sim_id + '_ErrorLog.csv'
## use the model path of the last encoutered case in cases
#errorlogs.PathToLogs = run_dir + log_dir
#errorlogs.save()
def get_htc_dict(post_dir, simid):
"""
Load the htc_dict, remove failed cases
"""
htc_dict = load_pickled_file(post_dir + simid + '.pkl')
# if the post processing is done on simulations done by thyra/gorm, and is
# downloaded locally, change path to results
for case in htc_dict:
if htc_dict[case]['[run_dir]'][:4] == '/mnt':
htc_dict[case]['[run_dir]'] = 'simulations/hawc2/%s/' % simid
try:
htc_dict_fail = load_pickled_file(post_dir + simid + '_fail.pkl')
except IOError:
return htc_dict
# ditch all the failed cases out of the htc_dict
# otherwise we will have fails when reading the results data files
for k in htc_dict_fail:
del htc_dict[k]
print 'removed from htc_dict due to error: ' + k
return htc_dict
class Log:
"""
Class for convinient logging. Create an instance and add lines to the
logfile as a list with the function add.
The added items will be printed if
self.print_logging = True. Default value is False
Create the instance, add with .add('lines') (lines=list), save with
.save(target), print current log to screen with .printLog()
"""
def __init__(self):
self.log = []
# option, should the lines added to the log be printed as well?
self.print_logging = False
self.file_mode = 'a'
def add(self, lines):
# the input is a list, where each entry is considered as a new line
for k in lines:
self.log.append(k)
if self.print_logging:
print k
def save(self, target):
# tread every item in the log list as a new line
FILE = open(target, self.file_mode)
for k in self.log:
FILE.write(k + '\n')
FILE.close()
# and empty the log again
self.log = []
def printscreen(self):
for k in self.log:
print k
class HtcMaster:
"""
"""
def __init__(self, verbose=False, silent=False):
"""
"""
# TODO: make HtcMaster callable, so that when called you actually
# set a value for a certain tag or add a new one. In doing so,
# you can actually warn when you are overwriting a tag, or when
# a different tag has the same name, etc
# create a dictionary with the tag name as key as the default value
self.tags = dict()
# should we print where the file is written?
self.verbose = verbose
self.silent = silent
# following tags are required
#---------------------------------------------------------------------
self.tags['[case_id]'] = None
self.tags['[master_htc_file]'] = None
self.tags['[master_htc_dir]'] = None
# path to model zip file, needs to accessible from the server
# relative from the directory where the pbs files are launched on the
# server. Suggestions is to always place the zip file in the model
# folder, so only the zip file name has to be defined
self.tags['[model_zip]'] = None
# path to HAWTOPT blade result file: quasi/res/blade.dat
self.tags['[blade_hawtopt_dir]'] = None
self.tags['[blade_hawtopt]'] = None
self.tags['[zaxis_fact]'] = 1.0
# TODO: rename to execution dir, that description fits much better!
self.tags['[run_dir]'] = None
# following dirs are relative to the run_dir!!
# they indicate the location of the SAVED (!!) results, they can be
# different from the execution dirs on the node which are set in PBS
self.tags['[res_dir]'] = 'results/'
self.tags['[log_dir]'] = 'logfiles/'
self.tags['[turb_dir]'] = 'turb/'
self.tags['[animation_dir]'] = 'animation/'
self.tags['[eigenfreq_dir]'] = 'eigenfreq/'