<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Toy event simulation for array of 15 MST/NectarCAM
calin/examples/simulation/toy event simulation for mst nectarcam.ipynb - Stephen Fegan - 2017-01-26
Copyright 2017, Stephen Fegan &#115;&#102;&#101;&#103;&#97;&#110;&#64;&#108;&#108;&#114;&#46;&#105;&#110;&#50;&#112;&#51;&#46;&#102;&#114;
Laboratoire Leprince-Ringuet, CNRS/IN2P3, Ecole Polytechnique, Institut Polytechnique de Paris
This file is part of "calin". "calin" is free software
Step1:  2. Define telescope properties for ray tracer and construct array
Elevation 
Step2:  3. Construct detection efficiency curve, cone efficiency and atmosphere
Step3:  4. Use world magnetic model to calculate field vs height at IAC
If you wish to have no magnetic field then uncomment the line "bfield = None"
Step4:  5. Construct the hierarchy of actions to take for each track
Geant 4 generates tracks and calls a "visitor" class to process each one. Here we use a hierarchy of nested "visitors", each calling down to the next one to perform the quadrature integration. We construct the hierarchy in reverse order. Going forward from Geant 4 the hierarchy is as follows
Step5:  6. Track generators
Two track generators are constructed, the primary one using Geant 4 to simulate the interaction of the primary and secondary particles in the atmosphere. A second generator "StraightTrackGenerator" generates purely straight line tracks using the primary (i.e. has no physics at all) and is used here ONLY to generate "ballistic" muon tracks that do not bend in the magnetic field or generate secondart particles.
Step6:  7. PMT and Poisson noise generators
Step7:  8. Function to run simulation and return image
Step8:  9. Clipping function based on hyperbola
Hyperbola with asymptotes (y=x) and (y=C) with offset at x=0 removed and scaled to remove residual nonlinearity at x=0
Step9:  10. Plot image using matplotlib (using a PatchCollection)
Step10:  11. Put it all togerther 
Step11:  12. Set up widgets and connect them run simulation when button clicked
User can change
<ASSISTANT_TASK:>
Python Code:
%pylab inline
import calin.math.hex_array
import calin.provenance.system_info
import calin.simulation.vs_optics
import calin.simulation.geant4_shower_generator
import calin.simulation.ray_processor
import calin.simulation.tracker
import calin.simulation.detector_efficiency
import calin.simulation.atmosphere
import calin.simulation.world_magnetic_model
import calin.simulation.pmt
from IPython.display import clear_output
from ipywidgets.widgets import *
Explanation: Toy event simulation for array of 15 MST/NectarCAM
calin/examples/simulation/toy event simulation for mst nectarcam.ipynb - Stephen Fegan - 2017-01-26
Copyright 2017, Stephen Fegan &#115;&#102;&#101;&#103;&#97;&#110;&#64;&#108;&#108;&#114;&#46;&#105;&#110;&#50;&#112;&#51;&#46;&#102;&#114;
Laboratoire Leprince-Ringuet, CNRS/IN2P3, Ecole Polytechnique, Institut Polytechnique de Paris
This file is part of "calin". "calin" is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License version 2 or later, as published by
the Free Software Foundation. "calin" is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
Introduction
This notebook shows how use the calin interface to Geant 4 to generate sample events in NectarCAM. This is not a "true" simulation, in that it doesn't contain any simulation of the electronics, trigger, digitization etc. Particles in the air shower are generated and tracked by Geant 4 and mean Cherenkov emission at the telescope is calculated by quadratic integration in predefined steps along the charged-particle tracks and in predefined angular steps in the Cherenkov cone at each step. The test rays are traced through the MST optics onto the focal plane. Poisson and PMT fluctations can be added subsequently if desired.
The simulation contains the following elements:
An array of 15 MST-like telescopes at the altitude of the IAC observatory, 2.147km, pointed at the zenith. Telescopes are in CTA configuration prod3Nb.3AL4-HN15.
Primary particles (gamma-rays, protons, electrons, or muons) injected at the top of the atmosphere at 100km
Geant 4 simulation of air shower using a layered atmosphere, whose properties are read from a file (CTA Prod 3 atmospheric model number 36), and magnetic field at the IAC site, given by the World Magnetic Model
Quadratic integration of the mean signal in the telescope through generation of test rays on the Cherenkov cone stepped evenly along the tracks of all charged particles in air shower, controlled by parmeters dx=10cm and dphi=2deg:
Test rays are generated at points along tracks separated by at most:
50dx for emission heights > 40m above the telescope [note tan(thetac) ~= 1/50] or 
dx for heights < 40m
At each point test rays are generated along the Cherenkov cone with angular steps of at most:
dphi
dx / 2 pi D tan(theta_c), where D is distance from emission point to telescope
Test rays are raytraced through MST assuming shadowing by 3m x 3m square focus box
Weighting of test rays by
The Cherenkov emission formula (see equation 33.45, PDG 2016)
Wavelength and height dependent absorption by atmosphere using MODTRAN optical depths (atm_trans_2147_1_10_0_0_2147.dat from Prod 3)
Wavelength dependent effective detector efficiency combining:
Hamamatsu QE curve (qe_R12992-100-05.dat from Prod 3)
Mirror reflectivity (ref_AlSiO2HfO2.dat from Prod 3)
Transmission through the PMMA window (Aclylite8_tra_v2013ref.dat from Prod 3)
Arbitrary factor of 0.9 to account for shadowing (to be revised when shadowing is known)
Light-cone efficiency vs photon entry angle (CTA-LST_lightguide_eff.dat from Prod 3)
All PEs are summed (regardless of arrival time) to form image
Poisson and PMT noise, and night-sky background can be optionally added to make more realistic images
The following items (at least) are missing from the simulation:
Frequency dependence of refractive index, and effects on Cherenkov yield and geometry
Mie scattering and refraction of rays in atmosphere due to variable refractive index
Angular dependence of mirror and PMMA response, and wavelength dependence of cones
True "night sky" response, including the effct of NSB PEs that occur at the start/end of the integration window and after pulsing.
Conditioning of the signal by the PMT and ampifier
Trigger
Digitization, including: conversion to DC, high/low gain & saturation of the samples at 4096 DC. This latter effect leads to non-linearity for large signals, and eventually to a complete saturation. Here, a checkbox can be selected to apply an ad-hoc saturation function in PEs: a linear response for small signals (<2000 PEs) rolling over to complete saturation at 4000 PEs.
1. Import all required packages from calin and ipwidgets
End of explanation
# Column 1 : Telescope position N [cm]
# Column 2 : Telescope position W [cm]
# Column 3 : Telescope position UP offset from 2147m [cm]
scope_pos = array([[-21065.,   5051.,   6130.,    700.],
                   [-17906.,  22302.,   4210.,    700.],
                   [  2796.,  24356.,   2320.,    700.],
                   [ 12421., -13456.,   3960.,    700.],
                   [ 17627.,  12790.,   1930.,    700.],
                   [ -7473., -14416.,   5320.,    700.],
                   [-21479., -12252.,   7410.,    700.],
                   [ -9811.,  37645.,   2070.,    700.],
                   [  2108., -30761.,   7330.,    700.],
                   [-19640., -29024.,   8810.,    700.],
                   [-30285.,  38460.,   3330.,    700.],
                   [-34500.,  17500.,   6900.,    700.],
                   [-37499.,  -3339.,   9260.,    700.],
                   [   272.,   4342.,   4000.,    700.],
                   [ 27765.,  -3780.,   1800.,    700.]])
scope_pos_x = -scope_pos[:,1]
scope_pos_y =  scope_pos[:,0]
scope_pos_z =  scope_pos[:,2]
scope_pos_x -= 0.5*(max(scope_pos_x) + min(scope_pos_x))
scope_pos_y -= 0.5*(max(scope_pos_y) + min(scope_pos_y))
#polar(arctan2(scope_pos_y,scope_pos_x), sqrt(scope_pos_x**2+scope_pos_y**2)/100,'o')
plot(scope_pos_x/100, scope_pos_y/100, 'o')
xlabel('X coordinate [m]')
ylabel('Y coordinate [m]')
axis('square')
xmax = 400
axis([-xmax,xmax,-xmax,xmax])
xticks(frange(-xmax,xmax,100))
yticks(frange(-xmax,xmax,100))
grid()
def dms(d,m,s):
    # Note this function fails for "negative" d=0 (e.g. -00:30:00)
    sign = 1
    if(d<0):
        sign = -1
        d = abs(d)
    return sign * (d + m/60.0 + s/3600.0)
mst = calin.ix.simulation.vs_optics.IsotropicDCArrayParameters()
mst.mutable_array_origin().set_latitude(dms(28, 45, 47.36))
mst.mutable_array_origin().set_longitude(dms(-17, 53, 23.93))
mst.mutable_array_origin().set_elevation(2147 * 100.0)
for i in range(len(scope_pos_x)):
    scope = mst.mutable_prescribed_array_layout().add_scope_positions();
    scope.set_x(scope_pos_x[i])
    scope.set_y(scope_pos_y[i])
    scope.set_z(scope_pos_z[i] + mst.array_origin().elevation())
mst.mutable_reflector_frame().set_optic_axis_rotation(-90);
dc = mst.mutable_reflector()
dc.set_curvature_radius(1920)
dc.set_aperture(1230)
dc.set_facet_num_hex_rings(5)
dc.mutable_psf_align().set_object_plane(inf) # 10 * 1e5);
dc.set_alignment_image_plane(1600)
dc.set_facet_spacing(122)
dc.set_facet_size(120)
dc.set_facet_focal_length(1607)
dc.set_facet_focal_length_dispersion(1)
dc.set_facet_spot_size_probability(0.8)
dc.set_facet_spot_size(0.5 * 2.8) # Spot size of 28mm at 2F
dc.set_facet_spot_size_dispersion(0.5 * 0.02)
dc.set_facet_labeling_parity(True)
dc.set_weathering_factor(1.0)
for id in [1,62,67,72,77,82,87]: dc.add_facet_missing_list(id-1)
mst.mutable_focal_plane().set_camera_diameter(235)
mst.mutable_focal_plane().mutable_translation().set_y(1/(1.0/dc.alignment_image_plane()-1/(10 * 1e5)))
mst.mutable_pixel().set_spacing(5)
mst.mutable_pixel().set_cone_inner_diameter(5)
mst.mutable_pixel().set_cone_survival_prob(1)
mst.mutable_pixel().set_hex_module_size(1)
mst.mutable_pixel().set_module_num_hex_rings(9)
u1,v1 = calin.math.hex_array.cluster_hexid_to_center_uv(1,1)
x1,y1 = calin.math.hex_array.uv_to_xy(u1,v1)
rot = arctan2(-y1,x1)/pi*180 - 30
mst.mutable_pixel().set_grid_rotation(rot)
obs_camera_box = mst.add_obscurations()
obs_camera_box.aligned_box().max_corner().set_x(150)
obs_camera_box.aligned_box().max_corner().set_y(mst.focal_plane().translation().y()+150)
obs_camera_box.aligned_box().max_corner().set_z(150)
obs_camera_box.aligned_box().min_corner().set_x(-150)
obs_camera_box.aligned_box().min_corner().set_y(mst.focal_plane().translation().y())
obs_camera_box.aligned_box().min_corner().set_z(-150)
obs_camera_box.aligned_box().set_incoming_only(True)
rng = calin.math.rng.RNG()
cta = calin.simulation.vs_optics.VSOArray()
cta.generateFromArrayParameters(mst, rng)
cta.pointTelescopesAzEl(0,90.0/180.0*pi);
Explanation: 2. Define telescope properties for ray tracer and construct array
Elevation : 2147m (all values are in centimeters)
Fifteen telescopes from CTA layout prod3Nb.3AL4-HN15, as outlined below
Reflector radius : 1920cm
Facets : 120cm side-side, on hexagonal grid with spacing of 122cm between centers
Facet focal length : 1607cm
Aperture : 1230cm - 5 hexagonal rings of mirror facets with 7 facets missing
Alignment : image at infinity focused on plane at 1600m
Mirror "roughness" generating Gaussian with D80=28mm at radius of curvature
Camera plane : offset for focusing of source at 10km (approx 1602.5cm)
Camera : 9 hexagonal rings of modules, each of 7 PMTs
Obsucration by camera box 300cm x 300cm x 150cm
End of explanation
data_dir = calin.provenance.system_info.build_info().data_install_dir() + "/simulation/"
det_eff = calin.simulation.detector_efficiency.DetectionEfficiency()
det_eff.scaleEffFromFile(data_dir + 'qe_R12992-100-05.dat')
det_eff.scaleEffFromFile(data_dir + 'ref_AlSiO2HfO2.dat')
det_eff.scaleEffFromFile(data_dir + 'Aclylite8_tra_v2013ref.dat')
det_eff.scaleEffByConst(0.9)
cone_eff = calin.simulation.detector_efficiency.AngularEfficiency(data_dir + 'CTA-LST_lightguide_eff.dat')
atm = calin.simulation.atmosphere.LayeredAtmosphere(data_dir + 'atmprof36.dat')
atm_abs = calin.simulation.detector_efficiency.AtmosphericAbsorption(data_dir + 'atm_trans_2147_1_10_0_0_2147.dat')
Explanation: 3. Construct detection efficiency curve, cone efficiency and atmosphere
End of explanation
wmm = calin.simulation.world_magnetic_model.WMM()
bfield = wmm.field_vs_elevation(mst.array_origin().latitude(), mst.array_origin().longitude())
#bfield = None
Explanation: 4. Use world magnetic model to calculate field vs height at IAC
If you wish to have no magnetic field then uncomment the line "bfield = None"
End of explanation
pe_imager = calin.simulation.ray_processor.SimpleImagePEProcessor(cta.numTelescopes(),cta.telescope(0).numPixels())
dx = 10.0
qcfg = calin.ix.simulation.tracker.QuadratureIACTArrayIntegrationConfig();
qcfg.set_ray_spacing_linear(dx)
qcfg.set_ray_spacing_angular(2)
quad = calin.simulation.tracker.VSO_QuadratureIACTArrayIntegration(qcfg, cta, pe_imager)
#quad.add_trace_visitor(diag)
quad.set_detection_efficiencies(det_eff, atm_abs, cta.telescope(0).opticalAxis()[2], cone_eff)
iact = calin.simulation.tracker.IACTDetectorSphereCherenkovConeIntersectionFinder(quad)
act = calin.simulation.tracker.AirCherenkovParameterCalculatorTrackVisitor(iact, atm)
limiter_lo = calin.simulation.tracker.LengthLimitingTrackVisitor(act, dx, 
    mst.array_origin().elevation() + 40*100)
limiter_hi = calin.simulation.tracker.LengthLimitingTrackVisitor(limiter_lo, 50.0*dx)
Explanation: 5. Construct the hierarchy of actions to take for each track
Geant 4 generates tracks and calls a "visitor" class to process each one. Here we use a hierarchy of nested "visitors", each calling down to the next one to perform the quadrature integration. We construct the hierarchy in reverse order. Going forward from Geant 4 the hierarchy is as follows:
Main Geant 4 class to generate the tracks
LengthLimitingTrackVisitor "hi" : active over whole atmosphere, this divides long tracks into segments of maximum length "50dx"
LengthLimitingTrackVisitor "lo" : active only within 40m of the telescope, divides long tracks into segments of maximum length "dx"
AirCherenkovParameterCalculatorTrackVisitor : calculate Cherenkov parameters for charged particle tracks (discard all others)
IACTDetectorSphereCherenkovConeIntersectionFinder : calculates intersection of Cherenkov cone and series of spheres that encompass telescopes, discarding tracks that habe no intersection
VSO_QuadratureIACTArrayIntegration : perform angular quadrature integration over Cherenkov cone, ray tracing test rays through telescope optics, discarding rays that don not hit camera
SimpleImagePEProcessor : make simple image of all rays that hit camera, summing weight of each ray that impacts each channel.
The integration is contolled by one parameter "dx" which defaults to 10cm, which means there will be on average more than ~100 test rays per square meter (i.e. per mirror facet) from an infinitely long track.
End of explanation
generator = calin.simulation.geant4_shower_generator.\
    Geant4ShowerGenerator(limiter_hi, atm, 1000, mst.array_origin().elevation(), atm.top_of_atmosphere(), bfield,
                          calin.simulation.geant4_shower_generator.VerbosityLevel_SUPRESSED_STDOUT);
generator.set_minimum_energy_cut(20);
ballistic_generator = calin.simulation.tracker.\
    StraightTrackGenerator(limiter_hi, mst.array_origin().elevation())
Explanation: 6. Track generators
Two track generators are constructed, the primary one using Geant 4 to simulate the interaction of the primary and secondary particles in the atmosphere. A second generator "StraightTrackGenerator" generates purely straight line tracks using the primary (i.e. has no physics at all) and is used here ONLY to generate "ballistic" muon tracks that do not bend in the magnetic field or generate secondart particles.
End of explanation
pmt_cfg = calin.simulation.pmt.PMTSimPolya.cta_model_3()
pmt_cfg.set_signal_in_pe(True)
pmt = calin.simulation.pmt.PMTSimPolya(pmt_cfg,rng)
pmt_gain = mean(pmt.rvs(1000000))
noise_cfg = calin.ix.simulation.pmt.PoissonSignalSimConfig();
noise_gen = calin.simulation.pmt.PoissonSignalSim(pmt, noise_cfg, rng)
Explanation: 7. PMT and Poisson noise generators
End of explanation
def gen_image(theta=0, phi=0, bx=0, by=0, e=1000, pt='proton', threshold=20, nsb=4, noise=True):
    theta *= pi/180
    phi *= pi/180
    bx *= 100
    by *= 100
    e *= 1000
    u = asarray([sin(theta)*cos(phi), sin(theta)*sin(phi), -cos(theta)])
    x0 = asarray([bx,by,mst.array_origin().elevation()])+u/u[2]*100*1e5
    if(pt=='proton'):
        pt = calin.simulation.tracker.ParticleType_PROTON
        generator.generate_showers(1, pt, e, x0, u)
    elif(pt=='gamma'):
        pt = calin.simulation.tracker.ParticleType_GAMMA
        generator.generate_showers(1, pt, e, x0, u)
    elif(pt=='electron'):
        pt = calin.simulation.tracker.ParticleType_ELECTRON
        generator.generate_showers(1, pt, e, x0, u)
    elif(pt=='muon'):
        pt = calin.simulation.tracker.ParticleType_MUON
        generator.generate_showers(1, pt, e, x0, u)
    elif(pt=='muon (ballistic)'):
        pt = calin.simulation.tracker.ParticleType_MUON
        ballistic_generator.generate_showers(1, pt, e, x0, u)
    elif(pt=='nsb'):
        return asarray(pe_imager.scope_image(0))*0
    pix_data = asarray(pe_imager.scope_image(0))
    for i in range(1,cta.numTelescopes()):
        pix_data = maximum(pix_data, asarray(pe_imager.scope_image(i)))
    return pix_data
Explanation: 8. Function to run simulation and return image
End of explanation
def gentle_clip(x, C=4000, D=1200):
    # Hyperbola with offset and non-linearity cancelled
    C = sqrt(C**2 - D**2) # C is interpreted as max value
    return (x-sqrt((x-C)**2 + D**2)+sqrt(C**2 + D**2)) / (1+C/sqrt(C**2 + D**2))
Explanation: 9. Clipping function based on hyperbola
Hyperbola with asymptotes (y=x) and (y=C) with offset at x=0 removed and scaled to remove residual nonlinearity at x=0
End of explanation
def plot_image(pix_data):
    s = cta.telescope(0)
#    figure(figsize=(10,8))
    figure(figsize=(7,6))
    pix = []
    idx = []
    for pix_id in range(len(pix_data)):
#        if(pix_data[pix_id] == 0.0):
#            continue
        pix_hexid = s.pixel(pix_id).hexID()
        vx,vy = calin.math.hex_array.hexid_to_vertexes_xy_trans(pix_hexid,
            s.cosPixelRotation(), s.sinPixelRotation(), s.pixelSpacing()/s.focalPlanePosition()[1]/pi*180.0)
        vv = zeros((len(vx),2))
        vv[:,0] = vx
        vv[:,1] = vy
        pix.append(Polygon(vv,closed=True))
        idx.append(pix_id)
    pc = matplotlib.collections.PatchCollection(pix, cmap=matplotlib.cm.jet)
    pc.set_array(asarray(pix_data)[idx])
    pc.set_linewidths(0)
    clo = 0
    if(min(pix_data)<0):
        clo = -5
    pc.set_clim(clo,max(80,ceil(max(pix_data)/10)*10))
    gca().add_collection(pc)
    axis('square')
    axis(4.5*asarray([-1,1,-1,1]))
    xlabel('X coordinate [deg]')
    ylabel('Y coordinate [deg]')
    colorbar(pc)
    grid(color='w')
Explanation: 10. Plot image using matplotlib (using a PatchCollection)
End of explanation
def run_sim(theta=0, phi=0, bx=0, by=0, loge=3, pt='proton', threshold=20, nsb=4.0, noise=True, clip=True):
    e = 10**loge
    sim_image = gen_image(theta=theta,phi=phi,bx=bx,by=by,e=e,pt=pt,threshold=threshold,nsb=nsb,noise=noise)
    im = sim_image
    if(noise):
        im += nsb
        im = noise_gen.rvs(im)
        im -= noise_gen.pedestal_mean()
        im /= pmt_gain
        im -= nsb
    if(clip):
        im = gentle_clip(im)
    plot_image(im)
    text(0.025,0.975,'Energy: %s$\\,$GeV\nType: %s'%("{:,.1f}".format(e),pt),
         transform=gca().transAxes,va='top',ha='left')
    text(0.025,0.025,'$\\hat{u}$ : %g$^\\circ$, %g$^\\circ$\n$\\vec{b}$ : %g$\\,$m, %g$\\,$m'%(theta,phi,bx,by),
         transform=gca().transAxes,va='bottom',ha='left')
    text(0.975,0.975,'Size: %s$\\,$PE\nN(>%g$\\,$PE): %g'%("{:,.1f}".format(sum(im)), threshold, sum(im>threshold)),
         transform=gca().transAxes,va='top',ha='right')
    try:
        gcf().savefig('/CTA/event.pdf')
    except:
        pass
Explanation: 11. Put it all togerther : run the simulation, add noise and plot
If the directory "/CTA" exists then the image is saved as "/CTA/event.pdf"
End of explanation
wbx     = FloatSlider(min=-500.0, max=500.0, step=1, value=0, description="X impact point [m]")
wby     = FloatSlider(min=-500.0, max=500.0, step=1, value=0, description="Y impact point [m]")
wenergy = FloatSlider(min=1.0, max=5.0, step=0.0625, value=3, description="Log10 E/GeV", readout_format='.3f',)
wtheta  = FloatSlider(min=0.0,max=8.0,step=0.1,value=0.0, description="Theta [deg]")
wphi    = FloatSlider(min=0.0, max=360.0, step=5.0, value=0.0, description="Phi [deg]")
wtype   = Dropdown(options=['gamma', 'proton', 'electron', 'muon', 'muon (ballistic)', 'nsb'], 
                   value='gamma', description='Paricle type')
wnsb    = FloatSlider(min=0.0, max=20.0, step=0.2, value=4.0, description="Mean NSB [PE]")
wnoise  = Checkbox(value=True, description='Ph & PMT noise')
wclip  = Checkbox(value=True, description='Clipping')
button  = Button(description="Run simulation")
wbox    = VBox([HBox([wbx,wby,wenergy]),HBox([wtheta,wphi,wtype]),
                HBox([wnsb,wnoise,wclip,button])])
display(wbox)
def on_button_clicked(b):
    clear_output()
    run_sim(theta=wtheta.value, phi=wphi.value, bx=wbx.value, by=wby.value, loge=wenergy.value, pt=wtype.value, 
            threshold=30, nsb=wnsb.value, noise=wnoise.value, clip=wclip.value)
button.on_click(on_button_clicked)
Explanation: 12. Set up widgets and connect them run simulation when button clicked
User can change:
Impact point on ground radially in two dimensions (distance and angle)
Primary propagation direction in two radial dimensions (angular separation from optical axis and angle)
Energy (log10) in GeV
Event type
"gamma", "proton", "electron", or "muon" : primary particle interacting in atmosphere, simulated with Geant 4 starting from 100km
"muon (ballistic)" : a straight track with no physics interactions
"nsb" : simple NSB event
NSB level in average number of background PEs per channel per event
Whether to add noise from photon Poisson fluctuations and PMT
Whether to use the ad-hoc clipping/saturation function
End of explanation
<END_TASK>