Skip to content

Commit 6e9d9df

Browse files
authored
Merge pull request #186 from rigoudyg/process_multiple_ensembles
Process multiple ensembles
2 parents b58eeac + c5c1138 commit 6e9d9df

File tree

7 files changed

+87
-37
lines changed

7 files changed

+87
-37
lines changed

climaf/cache.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,9 @@ def do_move(crs, filename, outfilename):
193193
time.sleep(0.1)
194194
waited += 1
195195
# time.sleep(0.5)
196-
if os.path.exists(filename):
196+
if not os.path.exists(filename):
197+
raise Climaf_Cache_Error("File %s wasn't created upstream (or not quick enough)" % (filename))
198+
else :
197199
if stamping is False:
198200
clogger.debug('No stamping')
199201
return do_move(crs, filename, outfilename)
@@ -242,8 +244,6 @@ def do_move(crs, filename, outfilename):
242244
elif stamping is None:
243245
clogger.critical("Cannot stamp by %s" % command)
244246
return True
245-
else:
246-
clogger.error("file %s does not exist (for crs %s)" % (filename, crs))
247247

248248

249249
def getCRS(filename):
@@ -427,11 +427,18 @@ def cdrop(obj, rm=True, force=False):
427427
else:
428428
clogger.error("%s is not a CliMAF object" % repr(obj))
429429
return
430+
fil=None
430431
if crs in crs2filename:
431432
clogger.info("Discarding cached value for %s (except if protected)" % crs)
432433
fil = crs2filename[crs]
433434
if not os.path.exists(fil):
434435
fil = alternate_filename(fil)
436+
else:
437+
# In case the cache index is not up-to-date
438+
fil=hasExactObject(obj)
439+
if fil :
440+
crs2filename[crs]=fil
441+
if fil:
435442
if rm:
436443
try:
437444
if force:

climaf/driver.py

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,9 @@ def capply_script(script_name, *operands, **parameters):
115115
opscopy = opscopy[1:]
116116
else:
117117
opscopy = list()
118-
if True in [isinstance(op, classes.cens) for op in opscopy]:
119-
raise Climaf_Driver_Error("Cannot yet have an ensemble as operand except as first one")
118+
# Next watch dog disabled for tests !!!!!
119+
#if True in [isinstance(op, classes.cens) for op in opscopy]:
120+
# raise Climaf_Driver_Error("Cannot yet have an ensemble as operand except as first one")
120121
#
121122
# If first operand is an ensemble, and the script is not ensemble-capable,
122123
# result is the ensemble of applying the script ot each member of first operand
@@ -311,7 +312,7 @@ def ceval_for_ctree(cobject, userflags=None, format="MaskedArray", deep=None, de
311312
clogger.debug("Evaluating compound object : " + repr(cobject))
312313
#################################################################
313314
if deep is not None:
314-
cache.cdrop(cobject.crs)
315+
cache.cdrop(cobject)
315316
#
316317
clogger.debug("Searching cache for exact object : " + repr(cobject))
317318
#################################################################
@@ -422,7 +423,7 @@ def ceval_for_scriptChild(cobject, userflags=None, format="MaskedArray", deep=No
422423
clogger.debug("Evaluating compound object : " + repr(cobject))
423424
#################################################################
424425
if deep is not None:
425-
cache.cdrop(cobject.crs)
426+
cache.cdrop(cobject)
426427
#
427428
clogger.debug("Searching cache for exact object : " + repr(cobject))
428429
#################################################################
@@ -507,7 +508,7 @@ def ceval_for_cpage(cobject, userflags=None, format="MaskedArray", deep=None, de
507508
clogger.debug("Evaluating compound object : " + repr(cobject))
508509
#################################################################
509510
if deep is not None:
510-
cache.cdrop(cobject.crs)
511+
cache.cdrop(cobject)
511512
#
512513
clogger.debug("Searching cache for exact object : " + repr(cobject))
513514
#################################################################
@@ -550,7 +551,7 @@ def ceval_for_cpage_pdf(cobject, userflags=None, format="MaskedArray", deep=None
550551
clogger.debug("Evaluating compound object : " + repr(cobject))
551552
#################################################################
552553
if deep is not None:
553-
cache.cdrop(cobject.crs)
554+
cache.cdrop(cobject)
554555
#
555556
clogger.debug("Searching cache for exact object : " + repr(cobject))
556557
#################################################################
@@ -594,7 +595,7 @@ def ceval_for_cens(cobject, userflags=None, format="MaskedArray", deep=None, der
594595
clogger.debug("Evaluating compound object : " + repr(cobject))
595596
#################################################################
596597
if deep is not None:
597-
cache.cdrop(cobject.crs)
598+
cache.cdrop(cobject)
598599
#
599600
clogger.debug("Searching cache for exact object : " + repr(cobject))
600601
#################################################################
@@ -1210,7 +1211,9 @@ def cfile(object, target=None, ln=None, hard=None, deep=None):
12101211
target_dir = os.path.dirname(target)
12111212
if isinstance(object, climaf.classes.cens):
12121213
raise Climaf_Driver_Error("Cannot yet copy or link result files for an ensemble")
1213-
if result is not None:
1214+
if result is None:
1215+
raise Climaf_Driver_Error("Issue when evaluating %s"%object)
1216+
else:
12141217
if ln or hard:
12151218
if ln and hard:
12161219
Climaf_Driver_Error("flags ln and hard are mutually exclusive")
@@ -1337,7 +1340,10 @@ def get_fig_sizes(figfile):
13371340
# On some sites, getoutput first lines have warning messages
13381341
# Furthermore, in case of missing file, last line could be an error -> only consider lines beginning with figfile
13391342
output_figsize = getoutput(" ".join(args_figsize)).split("\n")
1340-
output_figsize = [line for line in output_figsize if line.startswith(figfile)][-1]
1343+
with_figfile = [l for l in output_figsize if l.startswith(figfile)]
1344+
if len(with_figfile) == 0 :
1345+
raise ValueError("No relevant line for fig size in command (%s) output %s"%(args_figsize,output_figsize))
1346+
output_figsize = with_figfile[-1]
13411347
# comm_figsize = subprocess.Popen(args_figsize, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
13421348
# output_figsize = comm_figsize.stdout.read()
13431349
figsize = str(output_figsize).split(" ").pop(2)
@@ -1446,17 +1452,24 @@ def cfilePage(cobj, deep, recurse_list=None):
14461452
"-pointsize", "%d" % cobj.pt, "-annotate", annotate, '"%s"' % cobj.title])
14471453

14481454
args.append(out_fig)
1449-
clogger.debug("Compositing figures : %s" % repr(args))
14501455

1456+
command=" ".join(args)
1457+
clogger.debug("Compositing figures : %s" % command)
1458+
14511459
try:
14521460
with open("tmp.err", "w") as fic:
1453-
out = subprocess.check_output(" ".join(args), shell=True, stderr=fic)
1461+
out = subprocess.check_output(command, shell=True, stderr=fic)
14541462
except subprocess.CalledProcessError:
14551463
with open("tmp.err") as fic:
14561464
err = fic.read()
1457-
raise Climaf_Driver_Error("Compositing failed : %s" % err)
1458-
finally:
1459-
os.remove("tmp.err")
1465+
raise Climaf_Driver_Error("Compositing failed : %s for %s" % (err,command))
1466+
1467+
# There are cases where subprocess doesn't raise an Error, while compositing failed
1468+
if not os.path.exists(out_fig) :
1469+
with open("tmp.err") as fic:
1470+
err = fic.readlines()
1471+
raise Climaf_Driver_Error("Compositing failed %s for %s" % (err,command))
1472+
os.remove("tmp.err")
14601473

14611474
if cache.register(out_fig, cobj.crs):
14621475
clogger.debug("Registering file %s for cpage %s" % (out_fig, cobj.crs))

climaf/operators.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,8 @@ def __init__(self, name, command, format="nc", select=True, canOpendap=False,
118118
a single string (surrounded with double quotes) will carry
119119
multiple URLs
120120
121-
- **mmin** stands for the case where the script accepts an
122-
ensemble of datasets (only for first input stream
123-
yet). CliMAF will replace the keyword by a string
121+
- **mmin** stands for the case where the script accepts as argument
122+
an ensemble of datasets. CliMAF will replace the keyword by a string
124123
composed of the corresponding input filenames (not surrounded
125124
by quotes - please add them yourself in declaration); see also
126125
``labels`` below
@@ -257,9 +256,6 @@ def __init__(self, name, command, format="nc", select=True, canOpendap=False,
257256
serie = (oc.group("serie") is not None)
258257
multiple = (oc.group("mult") is not None)
259258
if multiple:
260-
if rank != 0:
261-
raise Climaf_Operator_Error(
262-
"Only first operand may accept members")
263259
if serie:
264260
raise Climaf_Operator_Error(
265261
"Operand %s cannot both accept"

climaf/period.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ def firstyears(period, nyears):
417417
Returns a period beginning at PERIOD's begin and which duration is at most NYEARS
418418
"""
419419
if isinstance(period, six.string_types):
420-
period = cperiod(period)
420+
period = init_period(period)
421421
rep = cperiod(period.start, period.end)
422422
yend = rep.end.year
423423
ystart = rep.start.year

doc/news.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ What's new
66

77
Changes, newest first:
88

9+
- V2.0.1:
10+
11+
- **Scripts can now process multiple ensembles, and ensembles which are not the first argument**:
12+
13+
- Each ensemble argument will be replaced by the list of files corresponding to the ensemble,
14+
each list being ordered by the ensemble order (see :py:func:`~climaf.classes.cens`);
15+
unless intentionnaly, the order is the same among all ensembles (if they are indexed by
16+
the same list of member labels)
17+
918
- V2.0.0:
1019

1120
- **Python 3 compatibility**:

scripts/gplot.ncl

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ begin
2525
else
2626

2727
if ismissing(str_match(str,".")) then ; integer or string
28-
val=stringtoint(str)
29-
if ismissing(val) then
28+
a=str_match_regex(str,"^[0-9]*$")
29+
if ismissing(a) then
3030
return(str)
3131
else
32-
return(val)
32+
return(stringtoint(str))
3333
end if
3434
else ; float (or string)
3535
valf=stringtofloat(str)
@@ -804,7 +804,7 @@ begin
804804
status_exit(1)
805805
end if
806806
print("---------------------")
807-
print(" STIPPLING FIELD ")
807+
print(" Shade2 FIELD ")
808808
print("---------------------")
809809
useLatDim2=False
810810
useXY_dummy=False
@@ -817,6 +817,12 @@ begin
817817
array_shade2=field_reduce(shade2_file, shade2_var, "lin", useXY_dummy, horizontal_dummy, \
818818
useLatDim2, vprofile_dummy, shade2_field_select, lat_exist2, lon_exist2, proj_data_dummy)
819819
fld_shade2=array_shade2[0]
820+
; If field min = field max, set some field values in order to force some contours
821+
; (otherwise, no shading occurs)
822+
if (min(fld_shade2) .eq. max(fld_shade2)) then
823+
fld_shade2(0,0) = str_convert(shade2_below)
824+
fld_shade2(0,1) = str_convert(shade2_above)
825+
end if
820826
end if
821827
end if
822828
end if
@@ -1294,6 +1300,7 @@ begin
12941300

12951301
res2@cnFillOn=False
12961302
res2@cnLineLabelsOn = False
1303+
res2@cnNoDataLabelOn = False
12971304
res2@cnLineLabelBackgroundColor = -1
12981305
res2@cnInfoLabelString = ""
12991306
res2@cnInfoLabelOn = False
@@ -1330,8 +1337,8 @@ begin
13301337
res2@lbLabelBarOn = False
13311338
res2@cnFillOn=True
13321339
;res2@cnFillColor = "black" ; foreground by default
1333-
opt = True
1334-
opt@gsnShadeFillType = "pattern" ; "color" or "pattern"
1340+
opt_shade = True
1341+
opt_shade@gsnShadeFillType = "pattern" ; "color" or "pattern"
13351342
end if
13361343

13371344
end if ; NOT AUX_FIELD
@@ -1699,9 +1706,26 @@ begin
16991706
plot1=gsn_csm_contour(wks,fld,res2)
17001707
end if
17011708
else
1702-
if isvar("aux_options") then res_list(aux_options, res2) end if
1703-
plot1=gsn_csm_contour(wks,fld2,res2)
1704-
if ( isvar("shade_below") .or. isvar("shade_above") ) then plot1 = shading(plot1,fld2,opt) end if
1709+
if isvar("aux_options") then
1710+
res_list(aux_options, res2)
1711+
end if
1712+
1713+
fmin=min(fld2)
1714+
fmax=max(fld2)
1715+
contour=True
1716+
if (ismissing(fmin) .and. ismissing(fmax)) then
1717+
contour=False
1718+
else
1719+
if (fmin .eq. fmax) then
1720+
contour=False
1721+
end if
1722+
end if
1723+
if (contour) then
1724+
plot1=gsn_csm_contour(wks,fld2,res2)
1725+
if ( isvar("shade_below") .or. isvar("shade_above") ) then
1726+
plot1 = shading(plot1,fld2,opt_shade)
1727+
end if
1728+
end if
17051729
end if
17061730

17071731
if isvar("fld_shade2") then
@@ -1716,6 +1740,7 @@ begin
17161740

17171741
res_shade2@cnFillOn=True
17181742
res_shade2@cnLineLabelsOn = False
1743+
res_shade2@cnNoDataLabelOn = False
17191744
res_shade2@cnLineLabelBackgroundColor = -1
17201745
res_shade2@cnInfoLabelString = ""
17211746
res_shade2@cnInfoLabelOn = False
@@ -1819,14 +1844,16 @@ begin
18191844
else
18201845
if (y .eq. "log") then plot1=gsn_csm_pres_hgt(wks,fld2,res2) end if
18211846
end if
1822-
if ( isvar("shade_below") .or. isvar("shade_above") ) then plot1 = shading(plot1,fld2,opt) end if
1847+
if ( isvar("shade_below") .or. isvar("shade_above") ) then
1848+
plot1 = shading(plot1,fld2,opt_shade)
1849+
end if
18231850
end if
18241851

18251852
end if ; cross-section or (t,z) profile
18261853

18271854
end if
18281855

1829-
if LEVELS_CONTOURS .or. AUX_FIELD then
1856+
if (LEVELS_CONTOURS .or. AUX_FIELD) .and. isvar("plot1") then
18301857
overlay(plot0,plot1)
18311858
end if
18321859

tests/test_operators.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,6 @@ def test_duplicate_entry(self):
7979
self.assertTrue("mycdo3" in sys.modules["__main__"].__dict__)
8080

8181
def test_errors(self):
82-
with self.assertRaises(Climaf_Operator_Error):
83-
cscript('mycdo2', '(cdo ${operator} ${in_1} ${mmin_2} ${out})')
8482
with self.assertRaises(Climaf_Operator_Error):
8583
cscript('mycdo2', '(cdo ${operator} ${in_1} ${mmins_2} ${out})')
8684
with self.assertRaises(Climaf_Operator_Error):

0 commit comments

Comments
 (0)