00045 :
00046 if type(file) == type("str"):
00047 raise RuntimeError, "You should pass as argument to parseCards a file object, stream or a list of lines, not a string"
00048 ret = Datacard()
00049
00050 nbins = -1;
00051 nprocesses = -1;
00052 nuisances = -1;
00053 binline = []; processline = []; sigline = []
00054 for l in file:
00055 f = l.split();
00056 if len(f) < 1: continue
00057 if f[0] == "imax":
00058 nbins = int(f[1]) if f[1] != "*" else -1
00059 if f[0] == "jmax":
00060 nprocesses = int(f[1])+1 if f[1] != "*" else -1
00061 if f[0] == "kmax":
00062 nuisances = int(f[1]) if f[1] != "*" else -1
00063 if f[0] == "shapes":
00064 if not options.bin: raise RuntimeError, "Can use shapes only with binary output mode"
00065 if len(f) < 4: raise RuntimeError, "Malformed shapes line"
00066 if not ret.shapeMap.has_key(f[2]): ret.shapeMap[f[2]] = {}
00067 if ret.shapeMap[f[2]].has_key(f[1]): raise RuntimeError, "Duplicate definition for process '%s', channel '%s'" % (f[1], f[2])
00068 ret.shapeMap[f[2]][f[1]] = f[3:]
00069 if f[0] == "Observation" or f[0] == "observation":
00070 ret.obs = [ float(x) for x in f[1:] ]
00071 if nbins == -1: nbins = len(ret.obs)
00072 if len(ret.obs) != nbins: raise RuntimeError, "Found %d observations but %d bins have been declared" % (len(ret.obs), nbins)
00073 if binline != []:
00074 if len(binline) != len(ret.obs): raise RuntimeError, "Found %d bins (%s) but %d bins have been declared" % (len(ret.bins), ret.bins, nbins)
00075 ret.bins = binline
00076 ret.obs = dict([(b,ret.obs[i]) for i,b in enumerate(ret.bins)])
00077 binline = []
00078 if f[0] == "bin":
00079 binline = []
00080 for b in f[1:]:
00081 if re.match("[0-9]+", b): b = "bin"+b
00082 binline.append(b)
00083 if f[0] == "process":
00084 if processline == []:
00085 processline = f[1:]
00086 if len(binline) != len(processline): raise RuntimeError, "'bin' line has a different length than 'process' line."
00087 continue
00088 sigline = f[1:]
00089 if re.match("-?[0-9]+", processline[0]) and not re.match("-?[0-9]+", sigline[0]):
00090 (processline,sigline) = (sigline,processline)
00091 if len(sigline) != len(processline): raise RuntimeError, "'bin' line has a different length than 'process' line."
00092 hadBins = (len(ret.bins) > 0)
00093 for i,b in enumerate(binline):
00094 p = processline[i];
00095 s = (int(sigline[i]) <= 0)
00096 ret.keyline.append((b, processline[i], s))
00097 if hadBins:
00098 if b not in ret.bins: raise RuntimeError, "Bin %s not among the declared bins %s" % (b, ret.bins)
00099 else:
00100 if b not in ret.bins: ret.bins.append(b)
00101 if p not in ret.processes: ret.processes.append(p)
00102 if nprocesses == -1: nprocesses = len(ret.processes)
00103 if nbins == -1: nbins = len(ret.bins)
00104 if not options.noJMax:
00105 if nprocesses != len(ret.processes): raise RuntimeError, "Found %d processes (%s), declared jmax = %d" % (len(ret.processes),ret.processes,nprocesses)
00106 if nbins != len(ret.bins): raise RuntimeError, "Found %d bins (%s), declared imax = %d" % (len(ret.bins),ret.bins,nbins)
00107 ret.exp = dict([(b,{}) for b in ret.bins])
00108 ret.isSignal = dict([(p,None) for p in ret.processes])
00109 if ret.obs != [] and type(ret.obs) == list:
00110 ret.obs = dict([(b,ret.obs[i]) for i,b in enumerate(ret.bins)])
00111 for (b,p,s) in ret.keyline:
00112 if ret.isSignal[p] == None:
00113 ret.isSignal[p] = s
00114 elif ret.isSignal[p] != s:
00115 raise RuntimeError, "Process %s is declared as signal in some bin and as background in some other bin" % p
00116 ret.signals = [p for p,s in ret.isSignal.items() if s == True]
00117 if len(ret.signals) == 0: raise RuntimeError, "You must have at least one signal process (id <= 0)"
00118 if f[0] == "rate":
00119 if processline == []: raise RuntimeError, "Missing line with process names before rate line"
00120 if sigline == []: raise RuntimeError, "Missing line with process id before rate line"
00121 if len(f[1:]) != len(ret.keyline): raise RuntimeError, "Malformed rate line: length %d, while bins and process lines have length %d" % (len(f[1:]), len(ret.keyline))
00122 for (b,p,s),r in zip(ret.keyline,f[1:]):
00123 ret.exp[b][p] = float(r)
00124 break
00125
00126 for l in file:
00127 if l.startswith("--"): continue
00128 l = re.sub("\\s*#.*","",l)
00129 l = re.sub("(?<=\\s)-+(\\s|$)"," 0\\1",l);
00130 f = l.split();
00131 if len(f) <= 1: continue
00132 nofloat = False
00133 lsyst = f[0]; pdf = f[1]; args = []; numbers = f[2:];
00134 if lsyst.endswith("[nofloat]"):
00135 lsyst = lsyst.replace("[nofloat]","")
00136 nofloat = True
00137 if options.nuisancesToExclude and isVetoed(lsyst, options.nuisancesToExclude):
00138 if options.verbose > 0: stderr.write("Excluding nuisance %s selected by a veto pattern among %s\n" % (lsyst, options.nuisancesToExclude))
00139 if nuisances != -1: nuisances -= 1
00140 continue
00141 if re.match("[0-9]+",lsyst): lsyst = "theta"+lsyst
00142 if pdf == "lnN" or pdf == "lnU" or pdf == "gmM" or pdf == "trG" or pdf.startswith("shape"):
00143 pass
00144 elif pdf == "gmN":
00145 args = [int(f[2])]; numbers = f[3:];
00146 elif pdf == "unif":
00147 args = [float(f[2]), float(f[3])]; numbers = f[4:];
00148 elif pdf == "param":
00149
00150
00151 args = f[2:]
00152 if len(args) <= 1: raise RuntimeError, "Uncertainties of type 'param' must have at least two arguments (mean and sigma)"
00153 ret.systs.append([lsyst,nofloat,pdf,args,[]])
00154 continue
00155 elif pdf == "flatParam":
00156 ret.flatParamNuisances[lsyst] = True
00157
00158 continue
00159 else:
00160 raise RuntimeError, "Unsupported pdf %s" % pdf
00161 if len(numbers) < len(ret.keyline): raise RuntimeError, "Malformed systematics line %s of length %d: while bins and process lines have length %d" % (lsyst, len(numbers), len(ret.keyline))
00162 errline = dict([(b,{}) for b in ret.bins])
00163 nonNullEntries = 0
00164 for (b,p,s),r in zip(ret.keyline,numbers):
00165 if "/" in r:
00166 if (pdf not in ["lnN","lnU"]) and ("?" not in pdf): raise RuntimeError, "Asymmetric errors are allowed only for Log-normals"
00167 errline[b][p] = [ float(x) for x in r.split("/") ]
00168 else:
00169 errline[b][p] = float(r)
00170
00171 if pdf == "gmN" and ret.exp[b][p] == 0 and float(r) != 0: ret.exp[b][p] = 1e-6
00172 ret.systs.append([lsyst,nofloat,pdf,args,errline])
00173
00174 for b in ret.bins:
00175 np_bin = sum([(ret.exp[b][p] != 0) for (b1,p,s) in ret.keyline if b1 == b])
00176 ns_bin = sum([(ret.exp[b][p] != 0) for (b1,p,s) in ret.keyline if b1 == b and s == True])
00177 nb_bin = sum([(ret.exp[b][p] != 0) for (b1,p,s) in ret.keyline if b1 == b and s != True])
00178 if np_bin == 0: raise RuntimeError, "Bin %s has no processes contributing to it" % b
00179 if ns_bin == 0: raise RuntimeError, "Bin %s has no signal processes contributing to it" % b
00180 if nb_bin == 0: raise RuntimeError, "Bin %s has no background processes contributing to it" % b
00181
00182 syst2 = []
00183 for lsyst,nofloat,pdf,args,errline in ret.systs:
00184 nonNullEntries = 0
00185 if pdf == "param":
00186 syst2.append((lsyst,nofloat,pdf,args,errline))
00187 continue
00188 for (b,p,s) in ret.keyline:
00189 r = errline[b][p]
00190 nullEffect = (r == 0.0 or (pdf == "lnN" and r == 1.0))
00191 if not nullEffect and ret.exp[b][p] != 0: nonNullEntries += 1
00192 if nonNullEntries != 0: syst2.append((lsyst,nofloat,pdf,args,errline))
00193 elif nuisances != -1: nuisances -= 1
00194 ret.systs = syst2
00195
00196 if options.stat:
00197 nuisances = 0
00198 ret.systs = []
00199
00200 if nuisances == -1:
00201 nuisances = len(ret.systs)
00202 elif len(ret.systs) != nuisances:
00203 raise RuntimeError, "Found %d systematics, expected %d" % (len(ret.systs), nuisances)
00204
00205 ret.hasShapes = (len(ret.shapeMap) > 0)
00206
00207 return ret