Example #1
0
def TabulatePhotonsFromSource(tray,
                              name,
                              PhotonSource="cascade",
                              Zenith=0. * I3Units.degree,
                              Azimuth=0. * I3Units.degree,
                              ZCoordinate=0. * I3Units.m,
                              Energy=1. * I3Units.GeV,
                              FlasherWidth=127,
                              FlasherBrightness=127,
                              Seed=12345,
                              NEvents=100,
                              IceModel='spice_mie',
                              DisableTilt=False,
                              Filename="",
                              TabulateImpactAngle=False,
                              PhotonPrescale=1,
                              Axes=None,
                              Directions=None,
                              Sensor='DOM',
                              RecordErrors=False):
    """
    Tabulate the distribution of photoelectron yields on IceCube DOMs from various
    light sources. The light profiles of the sources are computed from the same
    parameterizations used in PPC, but like in the direct propagation mode can
    be computed using GEANT4 instead.

    The mode of tabulation is controlled primarily by the **PhotonSource** parameter.
    
    - *'cascade'* will simulate an electromagnetic cascade of **Energy** GeV at
      (0, 0, **ZCoordinate**), oriented according to **Zenith** and **Azimuth**.
      The default coordinate system is spherical and centered the given vertex,
      with 200 quadratically spaced bins in radius, 36 linear bins in azimuthal
      angle (only from 0 to 180 degrees by default), 100 linear bins in the
      cosine of the polar angle, and 105 quadratic bins in time residual w.r.t
      the direct path from (0, 0, **ZCoordinate**).
    - *'flasher'* will simulate a 405 nm LED flasher pulse with the given
      **FlasherWidth** and **FlasherBrightness** settings. The source position
      and coordinate system are the same as for the 'cascade' case.
    - *'infinite-muon'* will simulate a "bare" muon of infinite length. The
      coordinate system is cylindrical and centered on the axis of the muon.
      Since the muon's position is degenerate with time, the usual parallel
      distance is replaced by the z coordinate of the closest approach to the
      detection position, and the starting positions of the simulated muons are
      sampled randomly (**ZCoordinate** is ignored). There are 100 quadratic
      bins in perpendicular distance to the source axis, 36 linear bins in
      azimuthal angle (0 to :math:`\pi` radians), 100 linear bins in z
      coordinate of closest approach, and 105 quadratic bins in time residual
      w.r.t. the earliest possible Cherenkov photon.

    :param PhotonSource: the type of photon source ('cascade', 'flasher', or 'infinite-muon').
    :param Zenith: the orientation of the source
    :param ZCoordinate: the depth of the source
    :param Energy: the energy of the source (only for cascade tables)
    :param FlasherWidth: the width of the flasher pulse (only for flasher tables)
    :param FlasherBrightness: the brightness of the flasher pulse (only for flasher tables)
    :param Seed: the seed for the random number service
    :param NEvents: the number of events to simulate
    :param RecordErrors: record the squares of weights as well (useful for error bars)
    :param IceModel: the path to an ice model in $I3_SRC/clsim/resources/ice. Likely values include:
        'spice_mie' ppc-style SPICE-Mie parametrization
        'photonics_spice_1/Ice_table.spice.i3coords.cos080.10feb2010.txt' Photonics-style SPICE1 table
        'photonics_wham/Ice_table.wham.i3coords.cos094.11jul2011.txt' Photonics-style WHAM! table
    :param DisableTilt: if true, disable tilt in ice model
    :param Filename: the name of the FITS file to write
    :param TabulateImpactAngle: if True, tabulate the impact position of the
           photon on the DOM instead of weighting by the DOM's angular acceptance
    :param Axes: a subclass of :cpp:class:`clsim::tabulator::Axes` that defines the coordinate system.
                 If None, an appropriate default will be chosen based on **PhotonSource**.
    :param Directions: a set of directions to allow table generation for multiple sources.
                 If None, only one direction given by **Zenith** and **Azimuth** is used.
       """

    # check sanity of args
    PhotonSource = PhotonSource.lower()
    if PhotonSource not in ['cascade', 'flasher', 'infinite-muon']:
        raise ValueError(
            "photon source %s is unknown. Please specify either 'cascade', 'flasher', or 'infinite-muon'"
            % PhotonSource)

    from icecube import icetray, dataclasses, dataio, phys_services, sim_services, clsim
    from os.path import expandvars

    # a random number generator
    randomService = phys_services.I3GSLRandomService(Seed)

    tray.AddModule("I3InfiniteSource",
                   name + "streams",
                   Stream=icetray.I3Frame.DAQ)

    tray.AddModule("I3MCEventHeaderGenerator",
                   name + "gen_header",
                   Year=2009,
                   DAQTime=158100000000000000,
                   RunNumber=1,
                   EventID=1,
                   IncrementEventID=True)

    if Directions is None:
        Directions = numpy.asarray([(Zenith, Azimuth)])

    if PhotonSource in ('cascade', 'flasher', 'muon-segment'):

        if PhotonSource == 'muon-segment':
            ptype = I3Particle.ParticleType.MuMinus
        else:
            ptype = I3Particle.ParticleType.EMinus

        def reference_source(zenith, azimuth, scale):
            source = I3Particle()
            source.type = ptype
            source.energy = Energy * scale
            source.pos = I3Position(0., 0., ZCoordinate)
            source.dir = I3Direction(zenith, azimuth)
            source.time = 0.
            if PhotonSource == 'muon-segment':
                source.length = 3.
            else:
                source.length = 0.
            source.location_type = I3Particle.LocationType.InIce

            return source

    elif PhotonSource == 'infinite-muon':

        from icecube import MuonGun
        # pad depth to ensure that the track appears effectively infinite
        surface = MuonGun.Cylinder(1800, 800)
        # account for zenith-dependent distribution of track lengths
        length_scale = surface.area(dataclasses.I3Direction(
            0, 0)) / surface.area(dataclasses.I3Direction(Zenith, 0))

        ptype = I3Particle.ParticleType.MuMinus

        def reference_source(zenith, azimuth, scale):
            source = I3Particle()
            source.type = ptype
            source.energy = Energy * scale
            source.dir = I3Direction(zenith, azimuth)
            source.pos = surface.sample_impact_position(
                source.dir, randomService)
            crossings = surface.intersection(source.pos, source.dir)
            source.length = crossings.second - crossings.first
            source.time = 0.
            source.location_type = I3Particle.LocationType.InIce

            return source

    import copy

    class MakeParticle(icetray.I3Module):
        def __init__(self, ctx):
            super(MakeParticle, self).__init__(ctx)
            self.AddOutBox("OutBox")
            self.AddParameter("SourceFunction", "", lambda: None)
            self.AddParameter("NEvents", "", 100)

        def Configure(self):
            self.reference_source = self.GetParameter("SourceFunction")
            self.nevents = self.GetParameter("NEvents")
            self.emittedEvents = 0

        def DAQ(self, frame):
            if PhotonSource != "flasher":
                primary = I3Particle()
                mctree = I3MCTree()
                mctree.add_primary(primary)
                for zenith, azimuth in Directions:
                    source = self.reference_source(zenith, azimuth,
                                                   1. / len(Directions))
                    mctree.append_child(primary, source)
                frame["I3MCTree"] = mctree
                # use the emitting particle as a geometrical reference
                frame["ReferenceParticle"] = source
            else:
                pulseseries = I3CLSimFlasherPulseSeries()
                for zenith, azimuth in Directions:
                    pulse = makeFlasherPulse(0, 0, ZCoordinate, zenith,
                                             azimuth, FlasherWidth,
                                             FlasherBrightness,
                                             1. / len(Directions))
                    pulseseries.append(pulse)
                frame["I3FlasherPulseSeriesMap"] = pulseseries
                frame["ReferenceParticle"] = self.reference_source(
                    Zenith, Azimuth, 1.)

            self.PushFrame(frame)

            self.emittedEvents += 1
            if self.emittedEvents >= self.nevents:
                self.RequestSuspension()

    tray.AddModule(MakeParticle,
                   SourceFunction=reference_source,
                   NEvents=NEvents)

    if PhotonSource == "flasher":
        flasherpulse = "I3FlasherPulseSeriesMap"
        mctree = None
    else:
        flasherpulse = None
        mctree = "I3MCTree"

    header = dict(FITSTable.empty_header)
    header['zenith'] = Zenith / I3Units.degree
    header['azimuth'] = Azimuth / I3Units.degree
    header['z'] = ZCoordinate
    header['energy'] = Energy
    header['type'] = int(ptype)
    header['efficiency'] = Efficiency.RECEIVER | Efficiency.WAVELENGTH
    if PhotonSource == 'infinite-muon':
        header['n_events'] = length_scale * NEvents / float(PhotonPrescale)

    if Axes is None:
        if PhotonSource != "infinite-muon":
            dims = [
                clsim.tabulator.PowerAxis(0, 580, 200, 2),
                clsim.tabulator.LinearAxis(0, 180, 36),
                clsim.tabulator.LinearAxis(-1, 1, 100),
                clsim.tabulator.PowerAxis(0, 7e3, 105, 2),
            ]
            geo = clsim.tabulator.SphericalAxes
        else:
            dims = [
                clsim.tabulator.PowerAxis(0, 580, 100, 2),
                clsim.tabulator.LinearAxis(0, numpy.pi, 36),
                clsim.tabulator.LinearAxis(-8e2, 8e2, 80),
                clsim.tabulator.PowerAxis(0, 7e3, 105, 2),
            ]
            geo = clsim.tabulator.CylindricalAxes
        # Add a dimension for the impact angle
        if TabulateImpactAngle:
            dims.append(clsim.tabulator.LinearAxis(-1, 1, 20))
        Axes = geo(dims)

    if PhotonSource == "flasher":
        header['flasherwidth'] = FlasherWidth
        header['flasherbrightness'] = FlasherBrightness

    # some constants
    DOMRadius = 0.16510 * icetray.I3Units.m  # 13" diameter
    referenceArea = dataclasses.I3Constants.pi * DOMRadius**2

    # NB: GetIceCubeDOMAcceptance() calculates the quantum efficiency by
    #     dividing the geometric area (a circle of radius domRadius) by the
    #     tabulated effective area. Scaling that radius by *sqrt(prescale)*
    #     _reduces_ the effective quantum efficiency by a factor *prescale*.
    #     Since we draw photons directly from the QE-weighted Cherenkov
    #     spectrum, this causes *prescale* fewer photons to be progagated per
    #     light source. We compensate by dividing the number of events by
    #     *prescale* in the header above.
    #     to be propagated per light source.
    domAcceptance = clsim.GetIceCubeDOMAcceptance(
        domRadius=math.sqrt(PhotonPrescale) * DOMRadius)

    if Sensor.lower() == 'dom':
        angularAcceptance = clsim.GetIceCubeDOMAngularSensitivity(holeIce=True)
    elif Sensor.lower() == 'degg':
        referenceArea = dataclasses.I3Constants.pi * (300. * I3Units.mm / 2)**2
        angularAcceptance = Gen2Sensors.GetDEggAngularSensitivity(pmt='both')
        domAcceptance = Gen2Sensors.GetDEggAcceptance(active_fraction=1. /
                                                      PhotonPrescale)
    elif Sensor.lower() == 'wom':
        # outer diameter of the pressure vessel is 11.4 cm, walls are 9 mm thick
        referenceArea = (11 - 2 * 0.9) * 90 * icetray.I3Units.cm2
        angularAcceptance = Gen2Sensors.GetWOMAngularSensitivity()
        domAcceptance = Gen2Sensors.GetWOMAcceptance(active_fraction=1. /
                                                     PhotonPrescale)

    else:
        raise ValueError("Don't know how to simulate %ds yet" % (sensor))

    tray.AddSegment(
        I3CLSimTabulatePhotons,
        name + "makeCLSimPhotons",
        MCTreeName=
        mctree,  # if source is a cascade this will point to the I3MCTree
        FlasherPulseSeriesName=
        flasherpulse,  # if source is a flasher this will point to the I3CLSimFlasherPulseSeries
        MMCTrackListName=None,  # do NOT use MMC
        ParallelEvents=
        1,  # only work at one event at a time (it'll take long enough)
        RandomService=randomService,
        # UnWeightedPhotons=True,
        UseGPUs=
        False,  # table-making is not a workload particularly suited to GPUs
        UseCPUs=True,  # it should work fine on CPUs, though
        Area=referenceArea,
        WavelengthAcceptance=domAcceptance,
        AngularAcceptance=angularAcceptance,
        DoNotParallelize=True,  # no multithreading
        UseGeant4=False,
        OverrideApproximateNumberOfWorkItems=
        1,  # if you *would* use multi-threading, this would be the maximum number of jobs to run in parallel (OpenCL is free to split them)
        ExtraArgumentsToI3CLSimModule=dict(Filename=Filename,
                                           TableHeader=header,
                                           Axes=Axes,
                                           PhotonsPerBunch=200,
                                           EntriesPerPhoton=5000,
                                           RecordErrors=RecordErrors),
        MediumProperties=parseIceModel(
            expandvars("$I3_SRC/clsim/resources/ice/" + IceModel),
            disableTilt=DisableTilt),
    )
Example #2
0
        '$I3_TESTDATA/GCD/GeoCalibDetectorStatus_IC86.55697_corrected_V2.i3.gz'
    ))
parser.add_argument("outfile", help="save plot to file")
args = parser.parse_args()

from icecube import icetray, dataclasses, dataio
from icecube import phys_services, simclasses, MuonGun
from I3Tray import I3Tray
from os.path import expandvars

tray = I3Tray()

tray.context['I3RandomService'] = phys_services.I3GSLRandomService(1337)

from icecube.MuonGun.segments import GenerateBundles
outer = MuonGun.Cylinder(1600, 800)
inner = MuonGun.Cylinder(300, 150, dataclasses.I3Position(0, 0, -350))
spectrum = MuonGun.OffsetPowerLaw(5, 1e3, 1e1, 1e4)
model = MuonGun.load_model('GaisserH4a_atmod12_SIBYLL')
generator = MuonGun.EnergyDependentSurfaceInjector(
    outer, model.flux, spectrum, model.radius,
    MuonGun.ConstantSurfaceScalingFunction(inner))
tray.AddSegment(
    GenerateBundles,
    'BundleGen',
    NEvents=1000,
    Generator=generator,
    GCDFile=expandvars(
        '$I3_TESTDATA/GCD/GeoCalibDetectorStatus_IC86.55697_corrected_V2.i3.gz'
    ))
Example #3
0
"""

from argparse import ArgumentParser
from os.path import expandvars

parser = ArgumentParser()
parser.add_argument("outfile", help="save plot to file")
args = parser.parse_args()

import matplotlib

matplotlib.use('agg')
import pylab, numpy
from icecube import dataclasses, MuonGun

surface = MuonGun.Cylinder(1600, 800)
area = numpy.pi**2 * surface.radius * (surface.radius + surface.length)

# 1 file of E^-2.6 5-component 3e4-1e9 GeV (3:2:1:1:1)
soft = 4e5 * MuonGun.corsika_genprob('CascadeOptimized5Comp')
# 1 file of E^-2 5-component 6e2-1e11 GeV (10:5:3:2:1)
hard = 2.5e6 * MuonGun.corsika_genprob('Standard5Comp')
# In order to compare to "unweighted" CORSIKA, turn the Hoerandel flux
# into a probability (since we happen to know the integral)
areanorm = 0.131475115 * area
# 1 file of natural-spectrum ("unweighted") CORSIKA
unweighted = (2.5e7 / areanorm) * MuonGun.corsika_genprob('Hoerandel5')

model = MuonGun.load_model('GaisserH4a_atmod12_SIBYLL')
model.flux.min_multiplicity = 1
model.flux.max_multiplicity = 1
def GenerateAtmosphericNeutrinos(tray,
                                 name,
                                 Files,
                                 GCDFile="",
                                 AutoExtendMuonVolume=False,
                                 EnergyBiasPower=1,
                                 FlavorBias=[30, 1, 1],
                                 CylinderRadius=600 * I3Units.m,
                                 CylinderHeight=1200 * I3Units.m,
                                 CrossSections='csms',
                                 NEvents=-1,
                                 MakeNeutrino=True):
    r"""
    Read CORSIKA showers containing neutrinos, and force exactly one neutrino to interact.

    NB: this segment is deprecated. Use GenerateAirShowers, SelectNeutrino, and PropagateMuons instead.
    
    :param Files: a list of CORSIKA files to read
    :param GCDFile: I3 file with GCD information to read in before the CORSIKA files
    :param AutoExtendMuonVolume: allow :math:`\nu_{\mu}` to interact far before they reach the detector
    :param EnergyBiasPower: select a neutrino from the bundle with probability proportional to E^power
    :param FlavorBias: scale selection probability for :math:`\nu_{e}/\nu_{\mu}/\nu_{\tau}`
                       by these factors. The default value is appropriate for equal sampling
                       of conventional atmospheric :math:`\nu_{e}/\nu_{\mu}`.
    :param CylinderRadius: radius of upright-cylinder target volume
    :param CylinderHeight: full height of simulation volume
    :param CrossSections: cross-section tables to use ('cteq5', 'css', or 'csms')
    """
    import warnings
    warnings.warn(
        'GenerateAtmosphericNeutrinos is deprecated. Use GenerateAirShowers, SelectNeutrino, and PropagateMuons instead'
    )
    from operator import add
    from icecube import neutrino_generator, sim_services, MuonGun
    from icecube.sim_services.propagation import get_propagators
    icetray.load('corsika-reader', False)

    random = tray.context['I3RandomService']
    surface = MuonGun.Cylinder(CylinderHeight, CylinderRadius)
    tray.Add('I3CORSIKAReader',
             'reader',
             filenamelist=Files,
             NEvents=NEvents,
             CylinderHeight=surface.length,
             CylinderRadius=surface.radius,
             Prefix=GCDFile)

    # Drop showers where no particles reach the observation level
    tray.Add(lambda frame: len(frame['I3MCTree']) > 1,
             Streams=[icetray.I3Frame.DAQ])

    # Remove low-energy muons that can't reach the detector
    tray.Add('I3InIceCORSIKATrimmer')

    tray.Add(SelectNeutrino,
             AutoExtendMuonVolume=AutoExtendMuonVolume,
             EnergyBiasPower=EnergyBiasPower,
             FlavorBias=FlavorBias,
             CylinderRadius=CylinderRadius,
             CylinderHeight=CylinderHeight,
             CrossSections=CrossSections)

    base_propagators = get_propagators()
    propagators = tray.context['I3ParticleTypePropagatorServiceMap']
    for k in base_propagators.keys():
        propagators[k] = base_propagators[k]

    tray.Add('Rename', Keys=['I3MCTree', 'I3MCTree_preMuonProp'])
    tray.Add('I3PropagatorModule',
             PropagatorServices=propagators,
             InputMCTreeName="I3MCTree_preMuonProp",
             OutputMCTreeName="I3MCTree",
             RandomService='I3RandomService')
Example #5
0
#!/usr/bin/env python
"""
Use muon flux weights to calculate an effective livetime for combined
CORSIKA samples as a function of energy.
"""

import pylab, numpy
from icecube import MuonGun, icetray, dataclasses
from icecube.icetray import I3Units

outer = MuonGun.Cylinder(1600, 800)
#inner = MuonGun.Cylinder(1600, 800)
inner = MuonGun.Cylinder(500, 150, dataclasses.I3Position(46.3, -34.9, -300))
#inner = MuonGun.Cylinder(700, 125, dataclasses.I3Position(29.3,52.6,-150))
#inner = outer
#Most tested spectrum
#spectrum = MuonGun.OffsetPowerLaw(5.0, 7e2, 0, 1e5)
spectrum = MuonGun.OffsetPowerLaw(5.0, 7e2, 500, 1e5)

#Limited spectrum range
#spectrum = MuonGun.OffsetPowerLaw(5.2, 7e2, 150, 1e4)
#spectrum = MuonGun.OffsetPowerLaw(2, 1*I3Units.TeV, 1*I3Units.TeV, 1*I3Units.PeV)
#Good spectrum producing ~1 day with 10k evts above 200 GeV
#spectrum = MuonGun.OffsetPowerLaw(5.2, 7e2, 200, 1e6)
#Jackob's spectrum
#spectrum = MuonGun.OffsetPowerLaw(5.0, 5e2, 200, 1e6)
#Good spectrum producing ~1 day with 5000 evts above 400 GeV
#spectrum = MuonGun.OffsetPowerLaw(5.25, 1000, 1, 1e6)
model = MuonGun.load_model(
    '/home/mamday/icesim/build/MuonGun/resources/tables/GaisserH4a_atmod12_SIBYLL'
)
Example #6
0
def main(cfg, run_number, scratch):
    with open(cfg, 'r') as stream:
        cfg = yaml.load(stream, Loader=yaml.Loader)
    cfg['run_number'] = run_number
    cfg['run_folder'] = get_run_folder(run_number)

    infile = cfg['infile_pattern'].format(**cfg)
    infile = infile.replace(' ', '0')

    if scratch:
        outfile = cfg['scratchfile_pattern'].format(**cfg)
    else:
        outfile = cfg['outfile_pattern'].format(**cfg)
    outfile = outfile.replace(' ', '0')


    tray = I3Tray()
    """The main script"""
    tray.AddModule('I3Reader',
                   'i3 reader',
                   FilenameList=[cfg['gcd'], infile])


    detectorsurface = MuonGun.Cylinder(
        length=1600.*icetray.I3Units.m,
        radius=800.*icetray.I3Units.m,
        center=dataclasses.I3Position(0.*icetray.I3Units.m,
                                      0.*icetray.I3Units.m,
                                      0.*icetray.I3Units.m,))

    # filter secondaries that are not in detector volume
    tray.AddModule(MuonRemoveChildren, 'MuonRemoveChildren',
                   Detector=detectorsurface,
                   Output='I3MCTree')


    #--------------------------------------------------
    # Add MC Labels mirco trained his DNN on
    #--------------------------------------------------
    add_cascade_labels = False
    if add_cascade_labels:
        tray.AddModule(modules.MCLabelsCascades, 'MCLabelsCascade',
                       PulseMapString='InIcePulses',
                       PrimaryKey='MCPrimary1',
                       OutputKey='LabelsDeepLearning')
    else:
        tray.AddModule(modules.MCLabelsDeepLearning, 'MCLabelsDeepLearning',
                       PulseMapString='InIcePulses',
                       PrimaryKey='MCPrimary1',
                       MCPESeriesMapName=cfg['mcpe_series_map'],
                       OutputKey='LabelsDeepLearning',
                       IsMuonGun=True)


    tray.AddModule("I3Writer", "EventWriter",
                   filename=outfile,
                   Streams=[icetray.I3Frame.DAQ,
                            icetray.I3Frame.Physics,
                            icetray.I3Frame.TrayInfo,
                            icetray.I3Frame.Simulation],
                   DropOrphanStreams=[icetray.I3Frame.DAQ])

    tray.Execute()

    del tray
Example #7
0
def PolyplopiaSegment(
        tray,
        name,
        mctype='CORSIKA',
        RandomService=None,
        mctree_name="I3MCTree_preMuonProp",
        separate_coincident_mctree_name="",  # leave empty to combine
        bgfile=None,
        timewindow=40. * I3Units.microsecond,
        rate=float('nan'),
        If=lambda f: True):
    """
         There are three scenarios for polyplopia:
            1. bgfile: We are reading background MC from a separate file and
            injecting events to signal (or weighted background) based on 
            a Poisson distribution within the given time window.

            2. we are generating MuonGun bundles and
            injecting events to signal (or weighted background) based on 
            a Poisson distribution within the given time window.
      """

    from icecube import polyplopia, MuonGun

    #tray.AddModule("ParticleMapper","mapprimary")
    if bgfile:  # merge bg into signal
        background = polyplopia.CoincidentI3ReaderService(bgfile)

    else:
        # Use MuonGun
        # Default: use Hoerandel as a template for generating muons.
        model = MuonGun.load_model("Hoerandel5_atmod12_SIBYLL")
        #model = MuonGun.load_model('GaisserH4a_atmod12_SIBYLL')

        # Generate bundles (even if not 100 percent accurate).
        model.flux.max_multiplicity = 100

        gamma_index = 2.6
        energy_offset = 700.
        energy_min = 1e4
        energy_max = 1e7
        cylinder_length = 1600.
        cylinder_radius = 800.
        cylinder_x = 0.
        cylinder_y = 0.
        cylinder_z = 0.

        # Default: cylinder aligned with z-axis at detector center as injection
        # surface.
        outsurface_center = dataclasses.I3Position(cylinder_x * I3Units.m,
                                                   cylinder_y * I3Units.m,
                                                   cylinder_z * I3Units.m)

        outsurface = MuonGun.Cylinder(length=cylinder_length * I3Units.m,
                                      radius=cylinder_radius * I3Units.m,
                                      center=outsurface_center)

        generator = MuonGun.NaturalRateInjector(outsurface, model.flux,
                                                model.energy)

        background = polyplopia.MuonGunBackgroundService()
        background.set_generator(generator)
        background.set_rng(RandomService)
        background.set_rate(rate)
        background.set_mctree_name(mctree_name)

    tray.AddModule("PoissonMerger",
                   "merge",
                   CoincidentEventService=background,
                   PrimaryType=mctype,
                   MCTreeName=mctree_name,
                   Rate=rate,
                   SeparateMCTree=separate_coincident_mctree_name,
                   TimeWindow=timewindow)

    return tray
Example #8
0
def SelectNeutrino(
    tray,
    name,
    Propagators=None,
    AutoExtendMuonVolume=False,
    EnergyBiasPower=1,
    FlavorBias=[30, 1, 1],
    CylinderRadius=600 * I3Units.m,
    CylinderHeight=1200 * I3Units.m,
    CrossSections='csms',
):
    """
    Select a neutrino to interact, and add neutrino propagators to the context.

    :param AutoExtendMuonVolume: allow :math:`\nu_{\mu}` to interact far before they reach the detector
    :param EnergyBiasPower: select a neutrino from the bundle with probability proportional to E^power
    :param FlavorBias: scale selection probability for :math:`\nu_{e}/\nu_{\mu}/\nu_{\tau}`
                       by these factors. The default value is appropriate for equal sampling
                       of conventional atmospheric :math:`\nu_{e}/\nu_{\mu}`.
    :param CylinderRadius: radius of upright-cylinder target volume
    :param CylinderHeight: full height of simulation volume
    :param CrossSections: cross-section tables to use ('cteq5', 'css', or 'csms')
    """
    from operator import add
    from icecube import neutrino_generator, sim_services, MuonGun

    # Set up NeutrinoGenerator internals
    random = tray.context['I3RandomService']
    surface = MuonGun.Cylinder(CylinderHeight, CylinderRadius)
    config = neutrino_generator.Steering()
    config.detection_surface = surface
    config.do_muon_range_extension = AutoExtendMuonVolume
    interactions = neutrino_generator.I3NuGInteractionInfo(
        random, config, CrossSections)
    interactions.initialize()
    # I3NuGSourceSelector needs this in its context
    tray.context['NuGSteer'] = config

    # Remove all but one neutrino
    tray.Add('I3NuGSourceSelector',
             EnergyBiasPowerIndex=EnergyBiasPower,
             ParticleBiases=reduce(add, [[b] * 2 for b in FlavorBias]),
             KeepDarkNeutrinos=False)

    # Store propagators in the context
    if not 'I3ParticleTypePropagatorServiceMap' in tray.context:
        tray.context[
            'I3ParticleTypePropagatorServiceMap'] = sim_services.I3ParticleTypePropagatorServiceMap(
            )
    Propagators = tray.context['I3ParticleTypePropagatorServiceMap']

    # Use NeutrinoPropagator for neutrinos
    prop = neutrino_generator.I3NeutrinoPropagator(random, config,
                                                   interactions)
    # ensure that all nu_e and nu_mu reach the detector
    prop.prop_mode = neutrino_generator.PropagationMode.ncgrweighted
    tau_prop = neutrino_generator.I3NeutrinoPropagator(random, config,
                                                       interactions)
    # un-weighted propagation for nu_tau to allow for tau regeneration
    tau_prop.prop_mode = neutrino_generator.PropagationMode.nopropweight
    for flavor in 'E', 'Mu', 'Tau':
        for ptype in '', 'Bar':
            Propagators[getattr(dataclasses.I3Particle.ParticleType,
                                'Nu' + flavor +
                                ptype)] = tau_prop if flavor == 'Tau' else prop
Example #9
0
def GenerateNeutrinos(
    tray,
    name,
    RandomService=None,
    NumEvents=100,
    SimMode='Full',
    VTXGenMode='NuGen',  # currently only NuGen is supported
    InjectionMode='Surface',
    CylinderParams=[
        0, 0, 0, 0, 0
    ],  # CIRCLE[radius, active_height_before, active_height_after] SURFACE[radius, length, center_x, center_y, center_z]
    AutoExtendMuonVolume=True,
    Flavor="NuMu",
    GammaIndex=2.0,
    FromEnergy=1. * I3Units.TeV,
    ToEnergy=10. * I3Units.PeV,
    ZenithRange=[0, 180 * I3Units.degree],
    AzimuthRange=[0, 360 * I3Units.degree],
    CrossSections='csms',
    CrossSectionsPath=None,
    ParamsMap=dict()):

    from I3Tray import I3Units

    from icecube import icetray, dataclasses, dataio, phys_services, sim_services
    from icecube import neutrino_generator, MuonGun

    earthModelService = name + "_earthmodel"
    steering = name + "_steering"
    injector = name + "_injector"
    interactions = name + "_interactions"

    #-----------------------------
    # make ParamMap's key lowercase and
    # check multiple definitions.
    #-----------------------------

    params = dict()
    for key in ParamsMap:
        keylower = key.lower()
        if keylower in params:
            raise Exception('param %s is set twice!' % (keylower))
        params[keylower] = ParamsMap[key]

    #-----------------------------
    # configure earthmodel service
    #-----------------------------

    earthModelServiceArgs = dict()
    if "earthmodels" in params:
        if not isinstance(params["earthmodels"], (list, tuple)):
            raise Exception('EarthModels must be a list of strings')
        earthModelServiceArgs['EarthModels'] = params["earthmodels"]

    if "materialmodels" in params:
        if not isinstance(params["materialmodels"], (list, tuple)):
            raise Exception('MaterialModels must be a list of strings')
        earthModelServiceArgs['MaterialModels'] = params["materialmodels"]

    if "earthparamspath" in params:
        if not isinstance(params["earthparamspath"], (str, unicode)):
            raise Exception('EarthParamsPath must be a strings')
        earthModelServiceArgs['PathToDataFileDir'] = params["earthparamspath"]

    if "icecaptype" in params:
        if not isinstance(params["icecaptype"], (str, unicode)):
            raise Exception('IceCapType must be a strings')
        earthModelServiceArgs['IceCapType'] = params["icecaptype"]

    if "icecapsimpleangle" in params:
        if not isinstance(params["icecapsimpleangle"], float):
            raise Exception('IceCapType must be a float')
        earthModelServiceArgs['IceCapSimpleAngle'] = params[
            "icecapsimpleangle"]

    if "detectordepth" in params:
        if not isinstance(params["detectordepth"], float):
            raise Exception('DetectorDepth must be a float')
        earthModelServiceArgs['DetectorDepth'] = params["detectordepth"]

    tray.AddService("I3EarthModelServiceFactory", earthModelService,
                    **earthModelServiceArgs)

    #-----------------------------
    # configure steering factory
    #-----------------------------

    # this is default cylinder. If you want to change cylinder size
    # and cylinder center, set positive value to CylinderParams.
    surface = MuonGun.Cylinder(1900 * I3Units.m, 950 * I3Units.m)

    steeringServiceArgs = dict()
    steeringServiceArgs['NEvents'] = NumEvents
    steeringServiceArgs['SimMode'] = SimMode
    steeringServiceArgs['VTXGenMode'] = VTXGenMode
    steeringServiceArgs['InjectionMode'] = InjectionMode
    steeringServiceArgs['DetectionSurface'] = surface
    steeringServiceArgs['CylinderParams'] = CylinderParams
    steeringServiceArgs['DoMuonRangeExtension'] = AutoExtendMuonVolume
    if "globalxsecscalefactor" in params:
        if not isinstance(params["globalxsecscalefactor"], (list, tuple)):
            raise Exception('GlobalXsecScaleFactor must be a list of float')
        steeringServiceArgs['GlobalXsecScaleFactor'] = params[
            "globalxsecscalefactor"]

    tray.AddService("I3NuGSteeringFactory",
                    steering,
                    EarthModelName=earthModelService,
                    **steeringServiceArgs)

    #-----------------------------
    # configure injector
    #-----------------------------

    EnergyLogRange = [
        math.log10(FromEnergy / I3Units.GeV),
        math.log10(ToEnergy / I3Units.GeV)
    ]

    injectorServiceArgs = dict()
    injectorServiceArgs['NuFlavor'] = Flavor
    injectorServiceArgs['GammaIndex'] = GammaIndex
    injectorServiceArgs['ZenithMin'] = ZenithRange[0]
    injectorServiceArgs['ZenithMax'] = ZenithRange[1]
    injectorServiceArgs['AzimuthMin'] = AzimuthRange[0]
    injectorServiceArgs['AzimuthMax'] = AzimuthRange[1]
    injectorServiceArgs['EnergyMinLog'] = EnergyLogRange[0]
    injectorServiceArgs['EnergyMaxLog'] = EnergyLogRange[1]

    if "zenithweightparam" in params:
        if not isinstance(params["zenithweightparam"], float):
            raise Exception('ZenithWeightParam must be a float')
        injectorServiceArgs['ZenithWeightParam'] = params["zenithweightparam"]

    tray.AddService("I3NuGInjectorFactory",
                    injector,
                    SteeringName=steering,
                    **injectorServiceArgs)

    #-----------------------------
    # configure interaction service
    #-----------------------------

    interactionServiceArgs = dict()
    if CrossSectionsPath is not None:
        interactionServiceArgs['TablesDir'] = CrossSectionsPath
    interactionServiceArgs['CrossSectionModel'] = CrossSections

    tray.AddService("I3NuGInteractionInfoFactory",
                    interactions,
                    SteeringName=steering,
                    **interactionServiceArgs)

    #-----------------------------
    # configure neutrino generator
    #-----------------------------

    nugenArgs = dict()
    nugenArgs['MCTreeName'] = 'I3MCTree_preMuonProp'

    if "primarynuname" in params:
        if not isinstance(params["primarynuname"], (str, unicode)):
            raise Exception('PrimaryNuName must be a strings')
        nugenArgs['PrimaryNuName'] = params["primarynuname"]

    if "interactionweight" in params:
        if not isinstance(params["interactionweight"], (list, tuple)):
            raise Exception('InteractionWeight must be a list of float')
        nugenArgs['InteractionCCFactor'] = params["interactionweight"][0]
        nugenArgs['InteractionNCFactor'] = params["interactionweight"][1]
        nugenArgs['InteractionGRFactor'] = params["interactionweight"][2]

    if "propagationweightmode" in params:
        if not isinstance(params["propagationweightmode"], (str, unicode)):
            raise Exception('PropagationWeightMode must be a string')
        propmode = neutrino_generator.to_propagation_mode(
            params["propagationweightmode"])
        nugenArgs['PropagationWeightMode'] = propmode

    tray.AddModule("I3NeutrinoGenerator",
                   name + "_neutrino",
                   SteeringName=steering,
                   InjectorName=injector,
                   InteractionInfoName=interactions,
                   **nugenArgs)
def main(cfg, run_number, scratch):
    with open(cfg, 'r') as stream:
        if int(yaml.__version__[0]) < 5:
            # backwards compatibility for yaml versions before version 5
            cfg = yaml.load(stream)
        else:
            cfg = yaml.full_load(stream)
    cfg['run_number'] = run_number
    cfg['run_folder'] = get_run_folder(run_number)

    tray = I3Tray()

    random_services, _ = create_random_services(
        dataset_number=cfg['dataset_number'],
        run_number=cfg['run_number'],
        seed=cfg['seed'],
        n_services=2)

    random_service, random_service_prop = random_services

    tray.context['I3RandomService'] = random_service

    model = MuonGun.load_model(cfg['muongun_model'])
    model.flux.min_multiplicity = cfg['muongun_min_multiplicity']
    model.flux.max_multiplicity = cfg['muongun_max_multiplicity']
    spectrum = MuonGun.OffsetPowerLaw(cfg['gamma'],
                                      cfg['e_min'] * icetray.I3Units.GeV,
                                      cfg['e_min'] * icetray.I3Units.GeV,
                                      cfg['e_max'] * icetray.I3Units.GeV)
    surface = MuonGun.Cylinder(1600, 800,
                               dataclasses.I3Position(31.25, 19.64, 0))

    if cfg['muongun_generator'] == 'energy':
        scale = MuonGun.BasicSurfaceScalingFunction()
        scale.SetSideScaling(4., 17266, 3.41, 1.74)
        scale.SetCapScaling(4., 23710, 3.40, 1.88)
        generator = MuonGun.EnergyDependentSurfaceInjector(
            surface, model.flux, spectrum, model.radius, scale)
    elif cfg['muongun_generator'] == 'static':
        generator = MuonGun.StaticSurfaceInjector(surface, model.flux,
                                                  spectrum, model.radius)
    elif cfg['muongun_generator'] == 'floodlight':
        generator = MuonGun.Floodlight(
            surface=surface,
            energyGenerator=spectrum,
            cosMin=cfg['muongun_floodlight_min_cos'],
            cosMax=cfg['muongun_floodlight_max_cos'],
        )
    else:
        err_msg = 'MuonGun generator {} is not known.'
        err_msg += " Must be 'energy','static' or 'floodlight"
        raise ValueError(err_msg.format(cfg['muongun_generator']))

    tray.Add(MuonGun.segments.GenerateBundles,
             'MuonGenerator',
             Generator=generator,
             NEvents=cfg['n_events_per_run'],
             GCDFile=cfg['gcd'])

    tray.Add("Rename", keys=["I3MCTree", "I3MCTree_preMuonProp"])

    tray.AddSegment(segments.PropagateMuons,
                    "PropagateMuons",
                    RandomService=random_service_prop,
                    **cfg['muon_propagation_config'])
    if scratch:
        outfile = cfg['scratchfile_pattern'].format(**cfg)
    else:
        outfile = cfg['outfile_pattern'].format(**cfg)
    outfile = outfile.replace(' ', '0')
    if cfg['distance_splits'] is not None:
        click.echo('SplittingDistance: {}'.format(cfg['distance_splits']))
        distance_splits = np.atleast_1d(cfg['distance_splits'])
        dom_limits = np.atleast_1d(cfg['threshold_doms'])
        if len(dom_limits) == 1:
            dom_limits = np.ones_like(distance_splits) * cfg['threshold_doms']
        oversize_factors = np.atleast_1d(cfg['oversize_factors'])
        order = np.argsort(distance_splits)

        distance_splits = distance_splits[order]
        dom_limits = dom_limits[order]
        oversize_factors = oversize_factors[order]

        stream_objects = generate_stream_object(distance_splits, dom_limits,
                                                oversize_factors)
        tray.AddModule(OversizeSplitterNSplits,
                       "OversizeSplitterNSplits",
                       thresholds=distance_splits,
                       thresholds_doms=dom_limits,
                       oversize_factors=oversize_factors)
        for stream_i in stream_objects:
            outfile_i = stream_i.transform_filepath(outfile)
            tray.AddModule("I3Writer",
                           "writer_{}".format(stream_i.stream_name),
                           Filename=outfile_i,
                           Streams=[
                               icetray.I3Frame.DAQ, icetray.I3Frame.Physics,
                               icetray.I3Frame.Stream('S'),
                               icetray.I3Frame.Stream('M')
                           ],
                           If=stream_i)
            click.echo('Output ({}): {}'.format(stream_i.stream_name,
                                                outfile_i))
    else:
        click.echo('Output: {}'.format(outfile))
        tray.AddModule("I3Writer",
                       "writer",
                       Filename=outfile,
                       Streams=[
                           icetray.I3Frame.DAQ, icetray.I3Frame.Physics,
                           icetray.I3Frame.Stream('S'),
                           icetray.I3Frame.Stream('M')
                       ])
    click.echo('Scratch: {}'.format(scratch))
    tray.AddModule("TrashCan", "the can")
    tray.Execute()
    tray.Finish()
Example #11
0
def main(cfg, run_number, scratch):
    with open(cfg, 'r') as stream:
        cfg = yaml.load(stream)
    cfg['run_number'] = run_number
    cfg['run_folder'] = get_run_folder(run_number)

    tray = I3Tray()

    random_service, random_service_prop, _ = create_random_services(
        dataset_number=cfg['dataset_number'],
        run_number=cfg['run_number'],
        seed=cfg['seed'])

    tray.context['I3RandomService'] = random_service

    model = MuonGun.load_model(cfg['muongun_model'])
    model.flux.min_multiplicity = cfg['muongun_min_multiplicity']
    model.flux.max_multiplicity = cfg['muongun_max_multiplicity']
    spectrum = MuonGun.OffsetPowerLaw(  cfg['gamma'],
                                        cfg['e_min']*icetray.I3Units.TeV,
                                        cfg['e_min']*icetray.I3Units.TeV,
                                        cfg['e_max']*icetray.I3Units.TeV)
    surface = MuonGun.Cylinder(1600, 800,
                            dataclasses.I3Position(31.25, 19.64, 0))

    if cfg['muongun_generator'] == 'energy':
        scale = MuonGun.BasicSurfaceScalingFunction()
        scale.SetSideScaling(4., 17266, 3.41, 1.74)
        scale.SetCapScaling(4., 23710, 3.40, 1.88)
        generator = MuonGun.EnergyDependentSurfaceInjector(surface,
                                                            model.flux,
                                                            spectrum,
                                                            model.radius,
                                                            scale)
    elif cfg['muongun_generator'] == 'static':
        generator = MuonGun.StaticSurfaceInjector(surface,
                                                    model.flux,
                                                    spectrum,
                                                    model.radius)
    elif cfg['muongun_generator'] =='floodlight':
        generator = MuonGun.Floodlight(surface = surface,
                                       energyGenerator=spectrum,
                                       cosMin=cfg['muongun_floodlight_min_cos'],
                                       cosMax=cfg['muongun_floodlight_max_cos'],
                                       )
    else:
        err_msg = 'MuonGun generator {} is not known.'
        err_msg += " Must be 'energy','static' or 'floodlight"
        raise ValueError(err_msg.format(cfg['muongun_generator']))

    tray.Add(MuonGun.segments.GenerateBundles, 'MuonGenerator',
                            Generator=generator,
                            NEvents=cfg['n_events_per_run'],
                            GCDFile=cfg['gcd'])

    tray.Add("Rename", keys=["I3MCTree", "I3MCTree_preMuonProp"])

    tray.AddSegment(
        segments.PropagateMuons,
        "PropagateMuons",
        RandomService=random_service_prop)
    if scratch:
        outfile = cfg['scratchfile_pattern'].format(**cfg)
    else:
        outfile = cfg['outfile_pattern'].format(**cfg)
    outfile = outfile.replace(' ', '0')
    outfile = outfile.replace('.bz2', '')
    tray.AddModule("I3Writer", "writer",
                   Filename=outfile,
                   Streams=[icetray.I3Frame.DAQ,
                            icetray.I3Frame.Physics,
                            icetray.I3Frame.Stream('S'),
                            icetray.I3Frame.Stream('M')])

    tray.AddModule("TrashCan", "the can")
    tray.Execute()
    tray.Finish()
Example #12
0
def main(cfg, run_number, scratch):
    with open(cfg, 'r') as stream:
        cfg = yaml.load(stream, Loader=yaml.Loader)
    cfg['run_number'] = run_number
    cfg['run_folder'] = get_run_folder(run_number)

    if scratch:
        outfile = cfg['scratchfile_pattern'].format(**cfg)
    else:
        outfile = cfg['outfile_pattern'].format(**cfg)
    outfile = outfile.replace(' ', '0')

    if cfg['distance_splits'] is not None:
        click.echo('SplittingDistances: {}'.format(cfg['distance_splits']))
        click.echo('Oversizefactors: {}'.format(cfg['oversize_factors']))
    click.echo('NEvents: {}'.format(cfg['n_events_per_run']))
    click.echo('EMin: {}'.format(cfg['e_min']))
    click.echo('EMax: {}'.format(cfg['e_max']))
    click.echo('EBreak: {}'.format(cfg['muongun_e_break']))
    click.echo('Gamma: {}'.format(cfg['gamma']))
    click.echo('ZenithMin: {}'.format(cfg['zenith_min']))
    click.echo('ZenithMax: {}'.format(cfg['zenith_max']))

    # create convex hull
    if 'use_convex_hull' in cfg and cfg['use_convex_hull']:

        # hardcode icecube corner points
        # ToDo: read from geometry file
        points = [
            [-570.90002441, -125.13999939, 501],  # string 31
            [-256.14001465, -521.08001709, 501],  # string 1
            [361., -422.82998657, 501],  # string 6
            [576.36999512, 170.91999817, 501],  # string 50
            [338.44000244, 463.72000122, 501],  # string 74
            [101.04000092, 412.79000854, 501],  # string 72
            [22.11000061, 509.5, 501],  # string 78
            [-347.88000488, 451.51998901, 501],  # string 75
            [-570.90002441, -125.13999939, -502],  # string 31
            [-256.14001465, -521.08001709, -502],  # string 1
            [361., -422.82998657, -502],  # string 6
            [576.36999512, 170.91999817, -502],  # string 50
            [338.44000244, 463.72000122, -502],  # string 74
            [101.04000092, 412.79000854, -502],  # string 72
            [22.11000061, 509.5, -502],  # string 78
            [-347.88000488, 451.51998901, -502],  # string 75
        ]
        convex_hull = ConvexHull(points)
    else:
        convex_hull = None

    if 'extend_past_hull' not in cfg:
        cfg['extend_past_hull'] = 0.0

    random_services, _ = create_random_services(
        dataset_number=cfg['dataset_number'],
        run_number=cfg['run_number'],
        seed=cfg['seed'],
        n_services=2)

    random_service, random_service_prop = random_services

    # create muon
    muon = create_muon(
        azimuth_range=[cfg['azimuth_min'], cfg['azimuth_max']],
        zenith_range=[cfg['zenith_min'], cfg['zenith_max']],
        energy_range=[cfg['e_min'], cfg['e_max']],
        anchor_time_range=cfg['anchor_time_range'],
        anchor_x_range=cfg['anchor_x_range'],
        anchor_y_range=cfg['anchor_y_range'],
        anchor_z_range=cfg['anchor_z_range'],
        length_to_go_back=cfg['length_to_go_back'],
        convex_hull=convex_hull,
        extend_past_hull=cfg['extend_past_hull'],
        random_service=random_services[0],
    )

    tray = I3Tray()

    tray.context['I3RandomService'] = random_service

    tray.AddModule("I3InfiniteSource",
                   "TheSource",
                   Prefix=cfg['gcd'],
                   Stream=icetray.I3Frame.DAQ)

    if cfg['MuonGenerator'] == 'MuonGunSinglemuons':
        tray.AddSegment(segments.GenerateSingleMuons,
                        "GenerateCosmicRayMuons",
                        NumEvents=cfg['n_events_per_run'],
                        FromEnergy=cfg['e_min'] * icetray.I3Units.GeV,
                        ToEnergy=cfg['e_max'] * icetray.I3Units.GeV,
                        BreakEnergy=cfg['muongun_e_break'] *
                        icetray.I3Units.GeV,
                        GammaIndex=cfg['gamma'],
                        ZenithRange=[
                            cfg['zenith_min'] * icetray.I3Units.deg,
                            cfg['zenith_max'] * icetray.I3Units.deg
                        ])

    elif cfg['MuonGenerator'] == 'MuonGunGeneral':
        model = MuonGun.load_model(cfg['muongun_model'])
        model.flux.min_multiplicity = cfg['muongun_min_multiplicity']
        model.flux.max_multiplicity = cfg['muongun_max_multiplicity']
        spectrum = MuonGun.OffsetPowerLaw(cfg['gamma'],
                                          cfg['e_min'] * icetray.I3Units.GeV,
                                          cfg['e_min'] * icetray.I3Units.GeV,
                                          cfg['e_max'] * icetray.I3Units.GeV)
        surface = MuonGun.Cylinder(1600, 800,
                                   dataclasses.I3Position(31.25, 19.64, 0))

        if cfg['muongun_generator'] == 'energy':
            scale = MuonGun.BasicSurfaceScalingFunction()
            scale.SetSideScaling(4., 17266, 3.41, 1.74)
            scale.SetCapScaling(4., 23710, 3.40, 1.88)
            generator = MuonGun.EnergyDependentSurfaceInjector(
                surface, model.flux, spectrum, model.radius, scale)
        elif cfg['muongun_generator'] == 'static':
            generator = MuonGun.StaticSurfaceInjector(surface, model.flux,
                                                      spectrum, model.radius)
        elif cfg['muongun_generator'] == 'floodlight':
            generator = MuonGun.Floodlight(
                surface=surface,
                energyGenerator=spectrum,
                cosMin=cfg['muongun_floodlight_min_cos'],
                cosMax=cfg['muongun_floodlight_max_cos'],
            )
        else:
            err_msg = 'MuonGun generator {} is not known.'
            err_msg += " Must be 'energy','static' or 'floodlight"
            raise ValueError(err_msg.format(cfg['muongun_generator']))

        tray.Add(MuonGun.segments.GenerateBundles,
                 'MuonGenerator',
                 Generator=generator,
                 NEvents=cfg['n_events_per_run'],
                 GCDFile=cfg['gcd'])

        tray.Add("Rename", keys=["I3MCTree", "I3MCTree_preMuonProp"])

    elif cfg['MuonGenerator'] == 'MuonResimulation':
        tray.AddModule(ParticleMultiplier,
                       'make_particles',
                       num_events=cfg['n_events_per_run'],
                       primary=muon)
    else:
        err_msg = 'MuonGenerator {} is not known.'
        err_msg += " Must be 'MuonGunSinglemuons','MuonGunGeneral' or 'MuonResimulation"
        raise ValueError(err_msg.format(cfg['MuonGenerator']))

    # --------------------------------------
    # Propagate Muons
    # --------------------------------------
    tray.AddSegment(segments.PropagateMuons,
                    "PropagateMuons",
                    RandomService=random_service_prop,
                    **cfg['muon_propagation_config'])

    # --------------------------------------
    # Distance Splits
    # --------------------------------------
    if cfg['distance_splits'] is not None:
        click.echo('SplittingDistance: {}'.format(cfg['distance_splits']))
        distance_splits = np.atleast_1d(cfg['distance_splits'])
        dom_limits = np.atleast_1d(cfg['threshold_doms'])
        if len(dom_limits) == 1:
            dom_limits = np.ones_like(distance_splits) * cfg['threshold_doms']
        oversize_factors = np.atleast_1d(cfg['oversize_factors'])
        order = np.argsort(distance_splits)

        distance_splits = distance_splits[order]
        dom_limits = dom_limits[order]
        oversize_factors = oversize_factors[order]

        stream_objects = generate_stream_object(distance_splits, dom_limits,
                                                oversize_factors)
        tray.AddModule(OversizeSplitterNSplits,
                       "OversizeSplitterNSplits",
                       thresholds=distance_splits,
                       thresholds_doms=dom_limits,
                       oversize_factors=oversize_factors)
        for stream_i in stream_objects:
            outfile_i = stream_i.transform_filepath(outfile)
            tray.AddModule("I3Writer",
                           "writer_{}".format(stream_i.stream_name),
                           Filename=outfile_i,
                           Streams=[
                               icetray.I3Frame.DAQ, icetray.I3Frame.Physics,
                               icetray.I3Frame.Stream('S'),
                               icetray.I3Frame.Stream('M')
                           ],
                           If=stream_i)
            click.echo('Output ({}): {}'.format(stream_i.stream_name,
                                                outfile_i))
    else:
        click.echo('Output: {}'.format(outfile))
        tray.AddModule("I3Writer",
                       "writer",
                       Filename=outfile,
                       Streams=[
                           icetray.I3Frame.DAQ, icetray.I3Frame.Physics,
                           icetray.I3Frame.Stream('S'),
                           icetray.I3Frame.Stream('M')
                       ])

    click.echo('Scratch: {}'.format(scratch))

    tray.Execute()

    del tray