def reset_antennas(): times = np.linspace(-20e-9, 80e-9, 2048, endpoint=False) pulse = pyrex.AskaryanSignal(times=times, energy=1e8, theta=np.radians(45)) times2 = np.linspace(180e-9, 280e-9, 2048, endpoint=False) pulse2 = pyrex.AskaryanSignal(times=times, energy=1e8, theta=np.radians(45), t0=200e-9) antenna = pyrex.Antenna((0,0,0), freq_range=(100e6, 400e6), noise_rms=25e-6) antenna.receive(pulse) antenna2 = pyrex.Antenna((0,0,0), freq_range=(100e6, 400e6), noise_rms=25e-6) antenna2.receive(pulse) antenna2.receive(pulse2) return antenna, antenna2
def test_filter_attenuation(): # filtered_spectrum = self.spectrum # responses = np.array(freq_response(self.frequencies)) # filtered_spectrum *= responses # self.values = np.real(scipy.fftpack.ifft(filtered_spectrum)) # freq_response = pf.attenuation # self = AskaryanSignal(times=np.linspace(-20e-9, 80e-9, 2048, endpoint=False), # energy=p.energy*1e-3, theta=psi, n=n) t = 0 pf = pyrex.PathFinder(pyrex.IceModel(), (0,0,-2800), (5000,5000,-200)) while not(pf.exists): z = np.random.random()*-2800 pf = pyrex.PathFinder(pyrex.IceModel(), (0,0,z), (5000,5000,-200)) p = pyrex.Particle(vertex=pf.from_point, direction=(1/np.sqrt(2),1/np.sqrt(2),0), energy=1e6) k = pf.emitted_ray epol = np.vdot(k, p.direction) * k - p.direction epol = epol / np.linalg.norm(epol) psi = np.arccos(np.vdot(p.direction, k)) n = pyrex.IceModel.index(p.vertex[2]) pulse = pyrex.AskaryanSignal(times=np.linspace(-20e-9, 80e-9, 2048, endpoint=False), energy=p.energy, theta=psi, n=n) t += performance_test("filtered_spectrum = pulse.spectrum", number=1000, use_globals={"pulse": pulse}) t += performance_test("fs = pulse.frequencies", number=1000, use_globals={"pulse": pulse}) fs = pulse.frequencies # performance_test("alen = ice.attenuation_length(-1000, fa*1e-6)", number=100, # use_globals={"ice": pyrex.IceModel(), "fa": np.abs(fs)}) t += performance_test("responses = freq_response(fs)", number=1000, use_globals={"freq_response": pf.attenuation, "fs": fs}) # t += performance_test("responses = np.array(freq_response(pulse.frequencies))", # number = 100, setup="import numpy as np", # use_globals={"freq_response": pf.attenuation, # "pulse": pulse}) filtered_spectrum = pulse.spectrum * np.array(pf.attenuation(pulse.frequencies)) t += performance_test("np.real(scipy.fftpack.ifft(filtered_spectrum))", number=1000, setup="import numpy as np; import scipy.fftpack", use_globals={"filtered_spectrum": filtered_spectrum}) print("Total time:", round(t*1000, 1), "milliseconds for", len(fs), "frequencies") print(" ", round(t*1e6/len(fs), 1), "microseconds per frequency")
def test_PathFinder_propagate(): t = 0 pf = pyrex.PathFinder(pyrex.IceModel(), (0,0,-2800), (5000,5000,-200)) while not(pf.exists): z = np.random.random()*-2800 pf = pyrex.PathFinder(pyrex.IceModel(), (0,0,z), (5000,5000,-200)) p = pyrex.Particle(vertex=pf.from_point, direction=(1/np.sqrt(2),1/np.sqrt(2),0), energy=1e6) k = pf.emitted_ray epol = np.vdot(k, p.direction) * k - p.direction epol = epol / np.linalg.norm(epol) psi = np.arccos(np.vdot(p.direction, k)) n = pyrex.IceModel.index(p.vertex[2]) pulse = pyrex.AskaryanSignal(times=np.linspace(-20e-9, 80e-9, 2048, endpoint=False), energy=p.energy, theta=psi, n=n) t += performance_test("signal.values *= 1 / pf.path_length", repeats=100, setup="import pyrex;"+ "signal = pyrex.Signal(pulse.times, pulse.values)", use_globals={"pf": pf, "pulse": pulse}) pulse.values *= 1 / pf.path_length t += performance_test("signal.filter_frequencies(pf.attenuation)", repeats=100, setup="import pyrex;"+ "signal = pyrex.Signal(pulse.times, pulse.values)", use_globals={"pf": pf, "pulse": pulse}) # pulse.filter_frequencies(pf.attenuation) t += performance_test("signal.times += pf.tof", repeats=100, setup="import pyrex;"+ "signal = pyrex.Signal(pulse.times, pulse.values)", use_globals={"pf": pf, "pulse": pulse}) print("Total time:", round(t*1000, 1), "milliseconds per signal")
def test_pyspice_envelope(): times, dt = np.linspace(0, 100e-9, 2048, endpoint=False, retstep=True) pulse = pyrex.AskaryanSignal(times=times, energy=1e8, theta=45*np.pi/180, n=1.75, t0=20e-9) performance_test("pyrex.Signal(pulse.times, pulse.envelope)", number=1000, setup="import pyrex", use_globals={"pulse": pulse}) spice_library = SpiceLibrary("/Users/bhokansonfasig/Documents/IceCube/"+ "scalable_radio_array/spice_models") class NgSpiceSharedSignal(NgSpiceShared): def __init__(self, **kwargs): super().__init__(**kwargs) self._signal = None def get_vsrc_data(self, voltage, time, node, ngspice_id): self._logger.debug('ngspice_id-{} get_vsrc_data @{} node {}'.format(ngspice_id, time, node)) voltage[0] = np.interp(time, self._signal.times, self._signal.values) return 0 ngspice_shared = NgSpiceSharedSignal() class NgSpiceSignal: def __init__(self, signal, shared=ngspice_shared): self.shared = ngspice_shared self.shared._signal = signal def setup_circuit(kind="biased"): if kind=="biased": circuit = Circuit('Biased Envelope Circuit') circuit.include(spice_library['hsms']) circuit.V('in', 'input', circuit.gnd, 'dc 0 external') # bias portion circuit.C(2, 'input', 1, 10@u_nF) circuit.R(2, 1, 2, 1@u_kOhm) circuit.X('D2', 'hsms', 2, circuit.gnd) circuit.R(3, 2, 'bias', 1@u_kOhm) circuit.V('bias', 'bias', circuit.gnd, 5@u_V) # envelope portion circuit.X('D1', 'hsms', 1, 'output') circuit.C(1, 'output', circuit.gnd, 220@u_pF) circuit.R(1, 'output', circuit.gnd, 50@u_Ohm) return circuit elif kind=="basic": circuit = Circuit('Biased Envelope Circuit') circuit.include(spice_library['hsms']) circuit.V('in', 'input', circuit.gnd, 'dc 0 external') # envelope portion circuit.X('D1', 'hsms', 'input', 'output') circuit.C(1, 'output', circuit.gnd, 220@u_pF) circuit.R(1, 'output', circuit.gnd, 50@u_Ohm) return circuit elif kind=="diode": circuit = Circuit('Diode Output') circuit.include(spice_library['hsms']) circuit.V('in', 'input', circuit.gnd, 'dc 0 external') circuit.X('D1', 'hsms', 'input', 'output') return circuit performance_test("ng_in = NgSpiceSignal(pulse); "+ "simulator = circuit.simulator(temperature=25, "+ "nominal_temperature=25, ngspice_shared=ng_in.shared); "+ "analysis = simulator.transient(step_time=dt, "+ "end_time=pulse.times[-1]); "+ "pyrex.Signal(analysis.output.abscissa, analysis.output)", number=10, setup="import pyrex; circuit = setup_circuit()", use_globals={"pulse": pulse, "dt": dt, "setup_circuit": setup_circuit, "NgSpiceSignal": NgSpiceSignal}, alternate_title="pyrex.Signal(analysis.output.abscissa, "+ "analysis.output)") def envelope_circuit(signal, cap=220e-12, res=50): v_c = 0 v_out = [] r_d = 25 i_s = 3e-6 n = 1.06 v_t = 26e-3 charge_exp = np.exp(-signal.dt/(res*cap)) discharge = i_s*res*(1-charge_exp) lambert_factor = n*v_t*res/r_d*(1-charge_exp) frac = i_s*r_d/n/v_t lambert_exponent = np.log(frac) + frac for v_in in signal.values: a = lambert_exponent + (v_in - v_c)/n/v_t if a>100: b = np.log(a) lambert_term = a - b + b/a else: lambert_term = np.real(lambertw(np.exp(a))) if np.isnan(lambert_term): lambert_term = 0 v_c = v_c*charge_exp - discharge + lambert_factor*lambert_term v_out.append(v_c) return pyrex.Signal(signal.times, v_out, value_type=pyrex.Signal.ValueTypes.voltage) performance_test("envelope(pulse)", number=100, use_globals={"pulse": pulse, "envelope": envelope_circuit}, alternate_title="analytic circuit simulation")
def test_EventKernel_event(energy=1e6): t = 0 t += performance_test("p = gen.create_particle()", number=100, setup="import pyrex;"+ "gen = pyrex.ShadowGenerator(dx=10000, dy=10000, "+ "dz=2800, energy_generator=lambda: "+str(energy)+")") t += performance_test("n = ice.index(p.vertex[2])", number=1000, setup="import pyrex;"+ "import numpy as np;"+ "z = np.random.random() * -2800;"+ "p = pyrex.Particle(vertex=(0,0,z), direction=(0,0,1), "+ "energy="+str(energy)+");"+ "ice = pyrex.IceModel();") t += performance_test("pf = PathFinder(ice, p.vertex, ant.position)", number=1000, setup="import pyrex;"+ "from pyrex import PathFinder;" "import numpy as np;"+ "z = np.random.random() * -2800;"+ "p = pyrex.Particle(vertex=(0,0,z), direction=(0,0,1), "+ "energy="+str(energy)+");"+ "ice = pyrex.IceModel();"+ "ant = pyrex.DipoleAntenna(name='ant', position=(5000,5000,-200), "+ "center_frequency=250, bandwidth=100, resistance=None, "+ "effective_height=1.0, trigger_threshold=3*(12e-6), noisy=False)") ice = pyrex.IceModel() ant = pyrex.DipoleAntenna(name='ant', position=(5000,5000,-200), center_frequency=250e6, bandwidth=100e6, resistance=None, effective_height=1, trigger_threshold=36e-6, noisy=False) def get_good_path(): z = np.random.random() * -2800 p = pyrex.Particle(vertex=(0,0,z), direction=(0,0,1), energy=energy) pf = pyrex.PathFinder(ice, p.vertex, ant.position) k = pf.emitted_ray epol = np.vdot(k, p.direction) * k - p.direction epol = epol / np.linalg.norm(epol) psi = np.arccos(np.vdot(p.direction, k)) if pf.exists and psi<np.pi/2: return p, pf, psi else: return get_good_path() p, pf, psi = get_good_path() t_loop = 0 t_loop += performance_test("pf.exists", number=1000, use_globals={"pf": pf}) print(" * ~1000 antennas") t_loop += performance_test("k = pf.emitted_ray; "+ "epol = np.vdot(k, p.direction) * k - p.direction; "+ "epol = epol / np.linalg.norm(epol); "+ "psi = np.arccos(np.vdot(p.direction, k))", number=1000, setup="import numpy as np", use_globals={"p": p, "pf": pf}, alternate_title="Set k, epol, psi") print(" * ~1000 antennas") t_loop += performance_test("times = np.linspace(-20e-9, 80e-9, 2048, endpoint=False)", number=1000, setup="import numpy as np") print(" * ~1000 antennas") t_loop += performance_test("pulse = AskaryanSignal(times=times, energy=p.energy, "+ "theta=psi, n=n)", number=100, setup="import numpy as np;"+ "from pyrex import AskaryanSignal;"+ "times = np.linspace(-20e-9, 80e-9, 2048, endpoint=False)", use_globals={"p": p, "pf": pf, "psi": psi, "n": ice.index(p.vertex[2])}) print(" * ~1000 antennas") times = np.linspace(-20e-9, 80e-9, 2048, endpoint=False) pulse = pyrex.AskaryanSignal(times=times, energy=p.energy*1e-3, theta=psi, n=ice.index(p.vertex[2])) t_loop += performance_test("pf.propagate(pulse)", repeats=100, setup="import numpy as np;"+ "import pyrex;"+ "times = np.linspace(-20e-9, 80e-9, 2048, endpoint=False);"+ "pulse = pyrex.AskaryanSignal(times=times, "+ "energy=p.energy, theta=psi, n=n)", use_globals={"p": p, "pf": pf, "psi": psi, "n": ice.index(p.vertex[2])}) print(" * ~1000 antennas") t_loop += performance_test("ant.receive(pulse)", repeats=100, setup="import numpy as np;"+ "import pyrex;"+ "times = np.linspace(-20e-9, 80e-9, 2048, endpoint=False);"+ "pulse = pyrex.AskaryanSignal(times=times, "+ "energy=p.energy, theta=psi, n=n)", use_globals={"p": p, "pf": pf, "psi": psi, "n": ice.index(p.vertex[2]), "ant": ant}) print(" * ~1000 antennas") print("Total time:", round(t+t_loop*800,1), "seconds per event on average")
import matplotlib.pyplot as plt import pyrex # First, set up a neutrino source and find the index of refraction at its depth. # Then use that index of refraction to calculate the Cherenkov angle. source = pyrex.Particle("nu_e", vertex=(0, 0, -1000), direction=(0, 0, -1), energy=1e8) n = pyrex.ice.index(source.vertex[2]) ch_angle = np.arccos(1/n) # Now, for a range of dthetas, generate an Askaryan pulse dtheta away from the # Chereknov angle and plot its frequency spectrum. for dtheta in np.radians(np.logspace(-1, 1, 5)): n_pts = 10001 pulse = pyrex.AskaryanSignal(times=np.linspace(-20e-9, 80e-9, n_pts), particle=source, viewing_angle=ch_angle+dtheta, viewing_distance=1000) plt.plot(pulse.frequencies[:int(n_pts/2)] * 1e-6, # Convert from Hz to MHz np.abs(pulse.spectrum)[:int(n_pts/2)], label=f"$\\Delta\\theta = {np.degrees(dtheta):.2f}$") plt.legend(loc='upper right') plt.title("Frequency Spectrum of Askaryan Pulse\n"+ "For Different Off-Cone Angles") plt.xlabel("Frequency (MHz)") plt.xlim(0, 3000) plt.tight_layout() plt.show() # Actually, we probably really want to see the frequency content after the # signal has propagated through the ice a bit. So first set up the ray tracer # from our neutrino source to some other point where our antenna might be