ATLAS Offline Software
Loading...
Searching...
No Matches
samplers.py
Go to the documentation of this file.
1# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2
3import ROOT, math, random
4from ParticleGun.histsampling import TH1
5
6
7PI = math.pi
8TWOPI = 2*math.pi
9
10
12 "Base class for all samplers"
13
14 def shoot(self):
15 return RuntimeError("Can't sample from an abstract sampler object.")
16
17 def __call__(self):
18 """This is the call method that will actually be used (so that normal
19 functions can also be passed in as samplers)."""
20 return self.shoot()
21
22 # TODO: add a sampling weight?
23
24
26 "A special-case sampler which just returns one value rather than sampling."
27
28 def __init__(self, val):
29 self.val = val
30
31 def shoot(self):
32 return self.val
33
34 def __repr__(self):
35 return "ConstSampler[%s]" % str(self.val)
36
37
38
39
41 "Base class for samplers from continuous distributions."
42 pass
43
44
45class UniformSampler(ContinuousSampler):
46 "Uniformly sample in the range [low,high)."
47
48 def __init__(self, low, high):
49 assert(low <= high)
50 self.low = float(low)
51 self.high = float(high)
52
53 def shoot(self):
54 return random.uniform(self.low, self.high)
55
56
58 "Uniformly sample in the modulus range (-high,low]+[low,high)."
59
60 def __init__(self, low, high):
61 assert(low == abs(low) and high == abs(high))
62 assert(low <= high)
63 self.low = float(low)
64 self.high = float(high)
65
66 def shoot(self):
67 val = random.uniform(self.low, self.high)
68 if random.random() > 0.5:
69 val *= -1
70 return val
71
72
74 "Uniformly sample from a set of disjoint intervals."
75
76 def __init__(self, ranges):
77 """
78 The ranges variable can either be a list of increasing numbers or a
79 list of pairs of numbers.
80
81 The former case will be treated as
82 defining alternating on/off ranges for sampling, starting with an active
83 one (i.e. it's a list of bin edges). The latter way specifically lists
84 the 'on' regions only, with their start and end values in the pairs.
85
86 The behaviour is undefined if the numbers are not ordered or overlap --
87 i.e. it might work but hasn't been designed that way and might change in
88 future. Don't rely on this behaviour!
89 """
90 if not ranges:
91 raise Exception("You must supply at least one non-null sampling range")
92 if hasattr(ranges[0], "__len__"):
93 assert all(len(x) == 2 for x in ranges)
94 self.ranges = ranges
95 else:
96 assert len(ranges) > 1
97 lows = [x for x in ranges[:-1]]
98 highs = [x for x in ranges[1:]]
99 myranges = []
100 for i, pair in enumerate(zip(lows, highs)):
101 if i % 2 == 0:
102 myranges.append(pair)
103 assert len(myranges) == len(ranges) // 2
104 self.ranges = myranges
105
106 def _getRanges(self):
107 return self._ranges
108
109 def _setRanges(self, ranges):
110 # TODO: Check that ranges don't overlap
111 self._ranges = ranges
112 self._totalwidth = sum(r[1] - r[0] for r in ranges)
113
114 runningwidth = 0.0
115 self._divisions = [0.0]
116 for r in ranges:
117 assert(r[1] >= r[0])
118 runningwidth += float(r[1] - r[0])
119 self._divisions.append(runningwidth)
120 self._totalwidth = runningwidth
121 for i in range(len(self._divisions)):
122 self._divisions[i] = float(self._divisions[i]) / float(self._totalwidth)
123
124 ranges = property(_getRanges, _setRanges)
125
126 def _map_unit_to_val(self, x):
127 assert(x >= 0 and x <= 1)
128 idx = None
129 rem = None
130 for i in range(len(self._divisions) - 1):
131 if x >= self._divisions[i] and x < self._divisions[i+1]:
132 idx = i
133 rem = x - self._divisions[i]
134 break
135 if idx is None:
136 raise ValueError("No matching division found in unit interval! How?")
137 val = self.ranges[idx][0] + self._totalwidth * rem
138 return val
139
140 def shoot(self):
141 rand = random.random()
142 val = self._map_unit_to_val(rand)
143 return val
144
145
147 "Randomly sample from an exponential distribution (i.e. uniformly on a log scale)."
148
149 def __init__(self, low, high):
150 self.low = float(low)
151 self.high = float(high)
152
153 def shoot(self):
154 rand = random.random()
155 logval = rand * math.log(self.high) + (1 - rand) * math.log(self.low)
156 val = math.exp(logval)
157 return val
158
159
161 "Randomly sample from a 1D Gaussian distribution."
162
163 def __init__(self, mean, sigma):
164 self.mean = float(mean)
165 self.sigma = float(sigma)
166
167 def shoot(self):
168 return random.gauss(self.mean, self.sigma)
169
170
172 "Randomly sample from a 1/x distribution."
173
174 def __init__(self, low, high):
175 self.low = float(low)
176 self.high = float(high)
177
178 def shoot(self):
179 invx = random.uniform(1/self.high, 1/self.low) #< limit inversion not actually necessary
180 return 1./invx
181
182
183
184
185
187 "Randomly sample from a 1D ROOT histogram."
188
189 def __init__(self, *args):
190 self.hist = TH1(*args)
191 if self.hist.GetEntries() < 1:
192 raise Exception("Histogram %s is EMPTY! Cannot sample" % self.hist.GetName())
193
194 def shoot(self):
195 return self.hist.GetRandom()
196
197
198
199
200
201
202
204 "Base class for samplers from lists of discrete values"
205 pass
206
207
208class RandomSeqSampler(DiscreteSampler):
209 "Uniformly random sample from a list of values."
210
211 def __init__(self, *args):
212 if len(args) == 1:
213 self.sequence = args[0]
214 else:
215 self.sequence = args
216
217 def shoot(self):
218 return random.choice(self.sequence)
219# Alias:
220RndmSeq = RandomSeqSampler
221
222
224 "Sequentially sample from a list of values, returning to the beginning once exhausted."
225
226 def __init__(self, *args):
227 if len(args) == 1:
228 self.sequence = args[0]
229 else:
230 self.sequence = args
231 self.index = 0
232
233 def shoot(self):
234 self.index = (self.index + 1) % len(self.sequence)
235 return self.sequence[self.index]
236
237Sequence = CyclicSeqSampler
238
239
240
241
242
243
244
246 """
247 Automatically cast the provided object to a sampler type. This is used
248 extensively inside the particle and position samplers, so that the user
249 can pass in a primitive type like a number or list and it will be
250 treated as if the more verbose sampler constructors had been called.
251
252 Behaviour:
253 - if x can be called, i.e. x() is valid, we just return x;
254 - a Python list (square brackets) will be converted to a continuous
255 UniformSampler or DisjointUniformSampler;
256 - a Python tuple (round brackets/parentheses) will be treated
257 as a discrete CyclicSeqSampler;
258 - a Python set (curly brackets/braces) will be treated
259 as a discrete RandomSeqSampler;
260 - otherwise a ConstSampler will be created from x, so that x is
261 returned when the sampler is called.
262 """
263 if callable(x):
264 return x
265 elif type(x) is list:
266 # NB: disjoint ranges can be given as nested lists, e.g. [(1,2), (4,5)]
267 if len(x) == 2 and type(x[0]) in (int,float) and type(x[1]) in (int,float):
268 return UniformSampler(*x)
269 elif len(x) > 2 or (len(x) > 0 and type(x[0]) not in (int,float)):
270 return DisjointUniformSampler(x)
271 if len(x) < 2:
272 raise Exception("Supplied list could not be converted to a continuous sampler")
273 elif type(x) is tuple:
274 return CyclicSeqSampler(*x)
275 elif type(x) is set:
276 return RandomSeqSampler(*x)
277 else:
278 return ConstSampler(x)
279
280
281
282
283
284
285
287 """
288 Sampler of position 3-vectors, for modelling a beamspot.
289 """
290
291 def __init__(self, x, y, z, t=0):
292 self.x = x
293 self.y = y
294 self.z = z
295 self.t = t
296
297 @property
298 def x(self):
299 "x position sampler"
300 return self._x
301 @x.setter
302 def x(self, x):
303 self._x = mksampler(x)
304
305 @property
306 def y(self):
307 "y position sampler"
308 return self._y
309 @y.setter
310 def y(self, y):
311 self._y = mksampler(y)
312
313 @property
314 def z(self):
315 "z position sampler"
316 return self._z
317 @z.setter
318 def z(self, z):
319 self._z = mksampler(z)
320
321 @property
322 def t(self):
323 "Time sampler"
324 return self._t
325 @t.setter
326 def t(self, t):
327 self._t = mksampler(t)
328
329 def shoot(self):
330 x = self.x()
331 y = self.y()
332 z = self.z()
333 t = self.t()
334 #print "POS =", x, y, z, t
335 return ROOT.TLorentzVector(x, y, z, t)
336
337
338# TODO: Make a 3-Gaussian BeamspotSampler
339
340
341
342
344 """
345 Base class for four-momentum sampling.
346
347 There are many ways to unambiguously specify four-momenta. Not all are sensible/useful,
348 though. The following are implemented here:
349 * M,px,py,pz
350 * E,M,phi,eta
351 * E,M,phi,y
352 * E,M,phi,theta
353 * pT,M,phi,eta
354 * pT,M,phi,y
355 * pT,M,phi,theta
356
357 Possibly the following (not yet implemented) could be useful: let us know if you
358 need one of these:
359 * E,px,py,pz
360 * pT,E,M,phi
361 """
362 pass
363
364
366 "A momentum sampler which just returns a null vector with the given mass."
367
368 def __init__(self, mass=0.0):
369 self.mass = mass
370
371 @property
372 def mass(self):
373 "Mass sampler"
374 return self._m
375 @mass.setter
376 def mass(self, x):
377 self._m = mksampler(x)
378
379 def shoot(self):
380 v4 = ROOT.TLorentzVector(0, 0, 0, self.mass)
381 return v4
382
383
385 "Create a 4-momentum vector from mass, px, py, pz distributions/samplers."
386
387 def __init__(self, px, py, pz, mass=0.0):
388 self.mass = mass
389 self.px = px
390 self.py = py
391 self.pz = pz
392
393 @property
394 def mass(self):
395 "Mass sampler"
396 return self._m
397 @mass.setter
398 def mass(self, x):
399 self._m = mksampler(x)
400
401 @property
402 def px(self):
403 "px sampler"
404 return self._px
405 @px.setter
406 def px(self, x):
407 self._px = mksampler(x)
408
409 @property
410 def py(self):
411 "py sampler"
412 return self._py
413 @py.setter
414 def py(self, x):
415 self._py = mksampler(x)
416
417 @property
418 def pz(self):
419 "pz sampler"
420 return self._pz
421 @pz.setter
422 def pz(self, x):
423 self._pz = mksampler(x)
424
425 def shoot(self):
426 m = self.mass()
427 px = self.px()
428 py = self.py()
429 pz = self.pz()
430 e = math.sqrt(px**2 + py**2 + pz**2 + m**2)
431 v4 = ROOT.TLorentzVector(px, py, pz, e)
432 return v4
433
434
436 "Create a 4-momentum vector from E, eta, m and phi distributions/samplers."
437
438 # TODO: ensure that E >= m!
439
440 def __init__(self, energy, eta, mass=0.0, phi=[0, TWOPI]):
441 self.energy = energy
442 self.eta = eta
443 self.mass = mass
444 self.phi = phi
445
446 @property
447 def energy(self):
448 "Energy sampler"
449 return self._e
450 @energy.setter
451 def energy(self, x):
452 self._e = mksampler(x)
453
454 @property
455 def eta(self):
456 "Pseudorapidity sampler"
457 return self._eta
458 @eta.setter
459 def eta(self, x):
460 self._eta = mksampler(x)
461
462 @property
463 def mass(self):
464 "Mass sampler"
465 return self._m
466 @mass.setter
467 def mass(self, x):
468 self._m = mksampler(x)
469
470 @property
471 def phi(self):
472 "Azimuthal angle sampler"
473 return self._phi
474 @phi.setter
475 def phi(self, x):
476 self._phi = mksampler(x)
477
478 def shoot(self):
479 """
480 eta = - ln(tan(theta/2)) / 2
481 => theta = 2 atan( exp(-eta) )
482 """
483 eta = self.eta()
484 theta = 2 * math.atan(math.exp(-eta))
485 e = max(self.mass(), self.energy())
486 m = self.mass()
487 p = math.sqrt( max(0.0, e**2 - m**2) )
488 pz = p * math.cos(theta)
489 pt = p * math.sin(theta)
490 phi = self.phi()
491 px = pt * math.cos(phi)
492 py = pt * math.sin(phi)
493 v4 = ROOT.TLorentzVector(px, py, pz, e)
494 return v4
495
496
498 "Create a 4-momentum vector from E, y, m and phi distributions."
499
500 # TODO: ensure that E >= m!
501
502 def __init__(self, energy, rap, mass=0.0, phi=[0, TWOPI]):
503 self.energy = energy
504 self.rap = rap
505 self.mass = mass
506 self.phi = phi
507
508 @property
509 def energy(self):
510 "Energy sampler"
511 return self._e
512 @energy.setter
513 def energy(self, x):
514 self._e = mksampler(x)
515
516 @property
517 def rap(self):
518 "Rapidity sampler"
519 return self._rap
520 @rap.setter
521 def rap(self, x):
522 self._rap = mksampler(x)
523
524 @property
525 def mass(self):
526 "Mass sampler"
527 return self._m
528 @mass.setter
529 def mass(self, x):
530 self._m = mksampler(x)
531
532 @property
533 def phi(self):
534 "Azimuthal angle sampler"
535 return self._phi
536 @phi.setter
537 def phi(self, x):
538 self._phi = mksampler(x)
539
540 def shoot(self):
541 """
542 y = 0.5 * ln((E+pz)/(E-pz))
543 -> (E^2 - pz^2) exp(2y) = (E+pz)^2
544 & (E^2 - pz^2) exp(-2y) = (E-pz)^2
545 -> E = sqrt(pt^2 + m^2) cosh(y)
546 -> pz = sqrt(pt^2 + m^2) sinh(y)
547 -> sqrt(pt^2 + m^2) = E / cosh(y)
548 """
549 e = max(self.mass(),self.energy())
550 y = self.rap()
551 sqrt_pt2_m2 = e / math.cosh(y)
552 pz = sqrt_pt2_m2 * math.sinh(y)
553 m = self.mass()
554 pt = math.sqrt( sqrt_pt2_m2**2 - m**2 )
555 phi = self.phi()
556 px = pt * math.cos(phi)
557 py = pt * math.sin(phi)
558 v4 = ROOT.TLorentzVector(px, py, pz, e)
559 return v4
560
561
563 "Create a 4-momentum vector from E, theta, m and phi distributions/samplers."
564
565 # TODO: ensure that E >= m!
566
567 def __init__(self, energy, theta, mass=0.0, phi=[0, TWOPI]):
568 self.energy = energy
569 self.theta = theta
570 self.mass = mass
571 self.phi = phi
572
573 @property
574 def energy(self):
575 "Energy sampler"
576 return self._e
577 @energy.setter
578 def energy(self, x):
579 self._e = mksampler(x)
580
581 @property
582 def theta(self):
583 "Polar angle sampler"
584 return self._theta
585 @theta.setter
586 def theta(self, x):
588
589 @property
590 def mass(self):
591 "Mass sampler"
592 return self._m
593 @mass.setter
594 def mass(self, x):
595 self._m = mksampler(x)
596
597 @property
598 def phi(self):
599 "Azimuthal angle sampler"
600 return self._phi
601 @phi.setter
602 def phi(self, x):
603 self._phi = mksampler(x)
604
605 def shoot(self):
606 """
607 p = sqrt(e^2 - m^2)
608 pz = p cos(theta)
609 pt = p sin(theta)
610 """
611 e = max(self.energy(),self.mass())
612 m = self.mass()
613 p = math.sqrt( max(0.0,e**2 - m**2) )
614 theta = self.theta()
615 pz = p * math.cos(theta)
616 pt = p * math.sin(theta)
617 phi = self.phi()
618 px = pt * math.cos(phi)
619 py = pt * math.sin(phi)
620 v4 = ROOT.TLorentzVector(px, py, pz, e)
621 return v4
622
623
625 "Create a 4-momentum vector from pt, eta, m and phi distributions/samplers."
626
627 def __init__(self, pt, eta, mass=0.0, phi=[0, TWOPI]):
628 self.pt = pt
629 self.eta = eta
630 self.mass = mass
631 self.phi = phi
632
633 @property
634 def pt(self):
635 "Transverse momentum sampler"
636 return self._pt
637 @pt.setter
638 def pt(self, x):
639 self._pt = mksampler(x)
640
641 @property
642 def eta(self):
643 "Pseudorapidity sampler"
644 return self._eta
645 @eta.setter
646 def eta(self, x):
647 self._eta = mksampler(x)
648
649 @property
650 def mass(self):
651 "Mass sampler"
652 return self._m
653 @mass.setter
654 def mass(self, x):
655 self._m = mksampler(x)
656
657 @property
658 def phi(self):
659 "Azimuthal angle sampler"
660 return self._phi
661 @phi.setter
662 def phi(self, x):
663 self._phi = mksampler(x)
664
665 def shoot(self):
666 """
667 eta = - ln(tan(theta/2)) / 2
668 => theta = 2 atan( exp(-eta) )
669 """
670 eta = self.eta()
671 theta = 2 * math.atan(math.exp(-eta))
672 pt = self.pt()
673 p = pt / math.sin(theta)
674 phi = self.phi()
675 px = pt * math.cos(phi)
676 py = pt * math.sin(phi)
677 pz = p * math.cos(theta)
678 m = self.mass()
679 e = math.sqrt( p**2 + m**2 )
680 v4 = ROOT.TLorentzVector(px, py, pz, e)
681 return v4
682
683
685 "Create a 4-momentum vector from pt, y, m and phi distributions/samplers."
686
687 def __init__(self, pt, rap, mass=0.0, phi=[0, TWOPI]):
688 self.pt = pt
689 self.rap = rap
690 self.mass = mass
691 self.phi = phi
692
693 @property
694 def pt(self):
695 "Transverse momentum sampler"
696 return self._pt
697 @pt.setter
698 def pt(self, x):
699 self._pt = mksampler(x)
700
701 @property
702 def rap(self):
703 "Rapidity sampler"
704 return self._rap
705 @rap.setter
706 def rap(self, x):
707 self._rap = mksampler(x)
708
709 @property
710 def mass(self):
711 "Mass sampler"
712 return self._m
713 @mass.setter
714 def mass(self, x):
715 self._m = mksampler(x)
716
717 @property
718 def phi(self):
719 "Azimuthal angle sampler"
720 return self._phi
721 @phi.setter
722 def phi(self, x):
723 self._phi = mksampler(x)
724
725 def shoot(self):
726 """
727 y = 0.5 * ln((E+pz)/(E-pz))
728 -> (E^2 - pz^2) exp(2y) = (E+pz)^2
729 & (E^2 - pz^2) exp(-2y) = (E-pz)^2
730 -> E = sqrt(pt^2 + m^2) cosh(y)
731 -> pz = sqrt(pt^2 + m^2) sinh(y)
732 -> sqrt(pt^2 + m^2) = E / cosh(y)
733 """
734 pt = self.pt()
735 assert pt >= 0
736 m = self.mass()
737 assert m >= 0
738 sqrt_pt2_m2 = math.sqrt( pt**2 + m**2 )
739 y = self.rap()
740 e = sqrt_pt2_m2 * math.cosh(y)
741 pz = sqrt_pt2_m2 * math.sinh(y)
742 phi = self.phi()
743 px = pt * math.cos(phi)
744 py = pt * math.sin(phi)
745 v4 = ROOT.TLorentzVector(px, py, pz, e)
746 return v4
747
748
750 "Create a 4-momentum vector from pt, theta, m and phi distributions/samplers."
751
752 def __init__(self, pt, theta, mass=0.0, phi=[0, TWOPI]):
753 self.pt = pt
754 self.theta = theta
755 self.mass = mass
756 self.phi = phi
757
758 @property
759 def pt(self):
760 "Transverse momentum sampler"
761 return self._pt
762 @pt.setter
763 def pt(self, x):
764 self._pt = mksampler(x)
765
766 @property
767 def theta(self):
768 "Polar angle sampler"
769 return self._theta
770 @theta.setter
771 def theta(self, x):
773
774 @property
775 def mass(self):
776 "Mass sampler"
777 return self._m
778 @mass.setter
779 def mass(self, x):
780 self._m = mksampler(x)
781
782 @property
783 def phi(self):
784 "Azimuthal angle sampler"
785 return self._phi
786 @phi.setter
787 def phi(self, x):
788 self._phi = mksampler(x)
789
790 def shoot(self):
791 """
792 p = pt / math.sin(theta)
793 pz = p cos(theta)
794 pt = p sin(theta)
795 E = sqrt(p^2 + m^2)
796 """
797 theta = self.theta()
798 pt = self.pt()
799 p = pt / math.sin(theta)
800 phi = self.phi()
801 px = pt * math.cos(phi)
802 py = pt * math.sin(phi)
803 pz = p * math.cos(theta)
804 m = self.mass()
805 e = math.sqrt( p**2 + m**2 )
806 v4 = ROOT.TLorentzVector(px, py, pz, e)
807 return v4
808
809
810# TODO: add the missing ways to specify/sample 4-momenta
811
812
813
814
815
816
817
818
819
820MASSES = { 22 : 0.0, # photon
821 11 : 0.5, # electron
822 12 : 0.0, # nu_e
823 13 : 105.7, # muon
824 14 : 0.0, # nu_mu
825 15 : 1777.8, # tau
826 16 : 0.0, # nu_tau
827 2212 : 938.0, # proton
828 2112 : 940.0, # neutron
829 111 : 135.0, # pi0
830 211 : 140.0, # pi+-
831 221 : 547.0, # eta
832 321 : 494.0, # K+-
833 311 : 598.0 # K0
834 }
835
836
838 """
839 A particle object for use as a return value from the particle samplers.
840 """
841 def __init__(self, pid=None, mom=None, pos=None):
842 """
843 Constructor/initializer: PID is the (int) PDG particle ID code
844 of this particle, mom is its momentum 4-vector, and pos is
845 the vertex 4-position (both as ROOT.TLorentzVector, in MeV).
846 """
847 self.pid = pid
848 self.mom = mom or ROOT.TLorentzVector(0,0,0,0)
849 self.pos = pos or ROOT.TLorentzVector(0,0,0,0)
850 self.mass = None
851
852
854 """
855 A simple N-independent-particle sampler.
856 """
857
858 def __init__(self, pid=999,
859 mom=NullMomSampler(), # noqa: B008 (re-using same ConstSampler)
860 n=1,
861 pos=PosSampler(0, 0, 0)): # noqa: B008 (re-using same ConstSampler)
862 self.pid = pid
863 self.mom = mom
864 self.n = n
865 self.pos = pos
866 self.massdict = MASSES
867 self.mass_override = True
868
869 @property
870 def pid(self):
871 "Particle ID code sampler"
872 return self._pid
873 @pid.setter
874 def pid(self, x):
875 self._pid = mksampler(x)
876
877 @property
878 def n(self):
879 "Particle number sampler"
880 return self._n
881 @n.setter
882 def n(self, x):
883 self._n = mksampler(x)
884
885 def shoot(self):
886 "Return a vector of sampled particles"
887 numparticles = self.n()
888 rtn = []
889 for i in range(numparticles):
890
891 pid = self.pid()
892 p = SampledParticle(pid)
893
894 if self.mass_override and abs(pid) in self.massdict:
895 m = self.massdict[abs(pid)]
896 self.mom.mass = m
897 p.mass = m
898 # TODO: Should the particle generated_mass be set from the sampler by default?
899
900 p.mom = self.mom()
901 p.pos = self.pos()
902
903 rtn.append(p)
904 return rtn
TGraphErrors * GetEntries(TH2F *histo)
#define max(a, b)
Definition cfImp.cxx:41
Continuous distribution samplers.
Definition samplers.py:40
Discrete sequence samplers.
Definition samplers.py:203
__init__(self, energy, eta, mass=0.0, phi=[0, TWOPI])
Definition samplers.py:440
__init__(self, energy, rap, mass=0.0, phi=[0, TWOPI])
Definition samplers.py:502
__init__(self, energy, theta, mass=0.0, phi=[0, TWOPI])
Definition samplers.py:567
__init__(self, mean, sigma)
Definition samplers.py:163
__init__(self, low, high)
Definition samplers.py:174
__init__(self, low, high)
Definition samplers.py:149
__init__(self, px, py, pz, mass=0.0)
Definition samplers.py:387
Momentum sampling.
Definition samplers.py:343
__init__(self, pid=999, mom=NullMomSampler(), n=1, pos=PosSampler(0, 0, 0))
Definition samplers.py:861
massdict
Pass mass info to the v4 sampler and set same generated mass.
Definition samplers.py:866
Beam-spot (origin vertex) sampling.
Definition samplers.py:286
__init__(self, x, y, z, t=0)
Definition samplers.py:291
__init__(self, pt, eta, mass=0.0, phi=[0, TWOPI])
Definition samplers.py:627
__init__(self, pt, rap, mass=0.0, phi=[0, TWOPI])
Definition samplers.py:687
__init__(self, pt, theta, mass=0.0, phi=[0, TWOPI])
Definition samplers.py:752
__init__(self, pid=None, mom=None, pos=None)
Definition samplers.py:841
__init__(self, low, high)
Definition samplers.py:48
mksampler(x)
Convenience function for sampler-making from Python literals.
Definition samplers.py:245