def antenna_pattern(self, skypoints, time=None, psi=0): """ Compute antenna response of a detector for a list of skypoints skypoints: Skypoint object or list of Skypoint objects time: time date when to compute the antenna pattern (default: None) psi: optional polarization angle (default: 0) This function uses XLALComputeDetAMResponse() that takes equatorial coordinates (RA and dec) and the Greenwich Mean Sidereal Time (GMST) to define the sky location. If the skypoints are given in the equatorial coordinate system, the antenna pattern is computed at the provided time if not None, otherwise it is computed at the reference time of the coordinate system of the skypoints. If the skypoints are given in the geographic coordinate system, they are mapped to fiducial equatorial coordinate system with GMST = 0 hr (REFDATE_GMST_ZERO). """ if not isinstance(skypoints, list): skypoints = [skypoints] f = [] for p in skypoints: assert isinstance(p, Skypoint), "Requires Skypoint objects" assert p.coordsystem.is_valid(), "Unsupported coordinate system" # XLALComputeDetAMResponse() requires equatorial coordinates. # We transform the points with Earth-fixed coordsystem ('geographic') # to the fiducial equatorial coordinates system with GMST = 0 hr. if p.coordsystem.name == 'geographic': assert time is None, \ 'time cannot be forced for skypoints in the geographic coordinate system' p = p.transformed_to(FIDUCIAL_EQUATORIAL_COORDSYS_GMST_ZERO) gmst_rad = lal.GreenwichMeanSiderealTime( p.coordsystem.ref_time) else: gmst_rad = lal.GreenwichMeanSiderealTime(time if time is not None else \ p.coordsystem.ref_time) # XLALComputeDetAMResponse() takes equatorial coordinates (RA and dec) # and gmst to define a sky location f.append(lal.ComputeDetAMResponse(self.descriptor.response, \ *p.coords(fmt='lonlat',unit='radians'), \ psi, gmst_rad)) f = numpy.squeeze(numpy.array(f)) if f.ndim == 1: return tuple(f) # fplus, fcross else: return f[:, 0], f[:, 1] # fplus, fcross
def delay_and_amplitude_correct(event, ra, dec): # retrieve station metadata detector = lal.cached_detector_by_prefix[event.ifo] # delay-correct the event to the geocentre delay = lal.TimeDelayFromEarthCenter(detector.location, ra, dec, event.peak) event.peak -= delay event.period = event.period.shift(-delay) try: event.ms_peak -= delay except AttributeError: pass try: event.ms_period = event.ms_period.shift(-delay) except AttributeError: pass # amplitude-correct the event using the polarization-averaged # antenna response fp, fc = lal.ComputeDetAMResponse( detector.response, ra, dec, 0, lal.GreenwichMeanSiderealTime(event.peak)) mean_response = math.sqrt(fp**2 + fc**2) event.amplitude /= mean_response event.ms_hrss /= mean_response # done return event
def optimal_orientation(self, t_gps): """Return the optimal orientation in right ascension and declination for a given GPS time. """ ra = self.longitude + (lal.GreenwichMeanSiderealTime(t_gps) % (2*np.pi)) dec = self.latitude return ra, dec
def time_delay_geocentric(detector1, detector2, ra, dec, time): """ Calculate time delay between two detectors in geocentric coordinates based on XLALArrivaTimeDiff in TimeDelay.c Parameters ------- detector1: array_like Cartesian coordinate vector for the first detector in the geocentric frame generated by the Interferometer class as self.vertex. detector2: array_like Cartesian coordinate vector for the second detector in the geocentric frame. To get time delay from Earth center, use detector2 = np.array([0,0,0]) ra: float Right ascension of the source in radians dec: float Declination of the source in radians time: float GPS time in the geocentric frame Returns ------- float: Time delay between the two detectors in the geocentric frame """ gmst = fmod(lal.GreenwichMeanSiderealTime(time), 2 * np.pi) theta, phi = ra_dec_to_theta_phi(ra, dec, gmst) omega = np.array([ np.sin(theta) * np.cos(phi), np.sin(theta) * np.sin(phi), np.cos(theta) ]) delta_d = detector2 - detector1 return np.dot(omega, delta_d) / speed_of_light
def magnitude_b(burst): gmst = lal.GreenwichMeanSiderealTime(burst.peak) fplus, fcross = lal.ComputeDetAMResponse( lal.cached_detector_by_prefix[burst.ifo].response, SimBurstUtils.MW_CENTER_J2000_RA_RAD, SimBurstUtils.MW_CENTER_J2000_DEC_RAD, 0.0, gmst) return (burst.ms_hrss**2.0 / (fplus**2.0 + fcross**2.0))**0.5
def test_antenna_pattern(self): vals = zip(self.ra, self.dec, self.pol, self.time) for ifo in self.d: fp = [] fc = [] for ra1, dec1, pol1, time1 in vals: gmst = lal.GreenwichMeanSiderealTime(time1) fp1, fc1 = tuple( lal.ComputeDetAMResponse(ifo.response, ra1, dec1, pol1, gmst)) fp.append(fp1) fc.append(fc1) fp2, fc2 = ifo.antenna_pattern(self.ra, self.dec, self.pol, self.time) fp = numpy.array(fp) fc = numpy.array(fc) diff1 = fp - fp2 diff2 = fc - fc2 diff = abs(numpy.concatenate([diff1, diff2])) tolerance = 1e-4 print("Max antenna diff:", ifo.name, diff.max()) self.assertLess(diff.max(), tolerance)
def test_optimal_orientation(self): for d1 in self.d: ra, dec = d1.optimal_orientation(self.time[0]) ra1 = d1.longitude + lal.GreenwichMeanSiderealTime(self.time[0]) % (numpy.pi *2) dec1 = d1.latitude self.assertAlmostEqual(ra, ra1, 3) self.assertAlmostEqual(dec, dec1, 7)
def antenna_pattern(self, right_ascension, declination, polarization, t_gps): """Return the detector response. """ gmst = lal.GreenwichMeanSiderealTime(t_gps) return tuple( lal.ComputeDetAMResponse(self.response, right_ascension, declination, polarization, gmst))
def getDetResp(detector, ra, dec, time=0., psi=0.): # Set time gmst = lal.GreenwichMeanSiderealTime(lal.LIGOTimeGPS(time)) #Get Detector Responses det_response = lal.CachedDetectors[det_index[detector]].response # Get Fplus, Fcross fplus, fcross = lal.ComputeDetAMResponse(det_response, ra, dec, psi, gmst) return fplus, fcross
def __params_timedelay_and_response_map_call(new_params, ): """ Function used in multi process map call to calculate some parameters required for waveform/likelihood evaluation. """ #new_params=bagwacl_lal.cbc_params(new_params_ul) for wfm_idx, (pn_param, param_a, run_param) in enumerate( zip(pn_params, new_params, run_params)): #Note: ordering of parameters is determined here... param = bagwacl_lal.cbc_params_sub(*param_a) distance = param.distance rightascension = param.rightascension declination = param.declination polarisation = param.polarisation # Calculate PN params and init LAL TF2 data struct bagwacl_tf2.tf2_pn_params_init( pn_param, param.phase, run_param.deltaF, param.mass1 * lal.LAL_MSUN_SI, param.mass2 * lal.LAL_MSUN_SI, 0., #param.S1z, 0., #param.S2z, run_param.fStart, run_param.fEnd, distance * lal.LAL_PC_SI * 1e6, 0., #param.lambda1, 0., #param.lambda2, run_param.spinO, run_param.tidalO, run_param.phaseO, run_param.amplitudeO, ) # Calculate effect of inclination of binary to LOS cfac = np.cos(param.inclination) pfac = 0.5 * (1 + cfac * cfac) # Calculate LALGPSTime structs from gpstimes doubles for each waveform using LAL gpst = lal.LIGOTimeGPS(param.time) #...calculate greenwich mean sideral times from gps times using LAL gmst = lal.GreenwichMeanSiderealTime(gpst) for ifo_idx, (detresp, detloc) in enumerate(run_param.detectors): # Calculate detector response functions for each waveform/IFO combo *and* scale det. resp. by distance... (fplus, fcross) = ComputeDetAMResponse(detresp, rightascension, declination, polarisation, gmst) ifo_response[2 * (wfm_idx * nIFOs + ifo_idx)] = fplus * pfac ifo_response[2 * (wfm_idx * nIFOs + ifo_idx) + 1] = fcross * cfac # Calculate signal arrival time for each IFO/waveform (relative to geocenter) timedelays[wfm_idx * nIFOs + ifo_idx] = TimeDelayFromEarthCenter( detloc, rightascension, declination, gpst)
def gmst_0h(gps): """ Truncate a LIGOTimeGPS to Greenwich mean sidereal 0 rad. """ gmst = lal.GreenwichMeanSiderealTime(gps) residual = gmst % (2.0 * math.pi) if residual: gmst -= residual return lal.GreenwichMeanSiderealTimeToGPS(gmst)
def __init__(self, Dmax=0, gps=1000000000, params=None): """ Initialize event :param Dmax: maximum distance to consider :param gmst: greenwich mean sidereal time :param params: parameters in form used by first 2 years paper """ if params is not None: self.D = params["distance"] self.ra = radians(params["RAdeg"]) self.dec = radians(params["DEdeg"]) try: self.gps = params['gps'] except: t = Time(params["MJD"], format='mjd') self.gps = lal.LIGOTimeGPS(int(t.gps), int(1e9 * (t.gps % 1))) self.gmst = lal.GreenwichMeanSiderealTime(self.gps) # self.gmst = float(t.sidereal_time("mean", "greenwich")/units.hourangle) self.phi = radians(params["coa-phase"]) self.psi = radians(params["polarization"]) self.cosi = cos(radians(params["inclination"])) self.mchirp = params["mass1"]**(3./5) * params["mass2"]**(3./5) * (params["mass1"] + params["mass2"])**(-1./5) elif Dmax: self.D = random.uniform(0, 1) ** (1. / 3) * Dmax self.ra = random.uniform(0, 2 * math.pi) self.dec = arcsin(random.uniform(-1, 1)) self.gps = lal.LIGOTimeGPS(gps,0) self.gmst = lal.GreenwichMeanSiderealTime(self.gps) self.psi = random.uniform(0, 2 * math.pi) self.phi = random.uniform(0, 2 * math.pi) self.cosi = random.uniform(-1, 1) self.mchirp = 1.4 * 2**(-1./5) else: raise ValueError("Must provide either list of params or maximum distance") # general content: self.xyz = detectors.xyz(self.ra - self.gmst, self.dec) self.ifos = [] self.mirror = False self.detected = False self.localization = {} self.area = {} self.patches = {}
def get_polarization_tensor(ra, dec, time, psi, mode): """ Calculate the polarization tensor for a given sky location and time See Nishizawa et al. (2009) arXiv:0903.0528 for definitions of the polarisation tensors. [u, v, w] represent the Earth-frame [m, n, omega] represent the wave-frame Note: there is a typo in the definition of the wave-frame in Nishizawa et al. Parameters ------- ra: float right ascension in radians dec: float declination in radians time: float geocentric GPS time psi: float binary polarisation angle counter-clockwise about the direction of propagation mode: str polarisation mode Returns ------- array_like: A 3x3 representation of the polarization_tensor for the specified mode. """ gmst = fmod(lal.GreenwichMeanSiderealTime(time), 2 * np.pi) theta, phi = ra_dec_to_theta_phi(ra, dec, gmst) u = np.array([ np.cos(phi) * np.cos(theta), np.cos(theta) * np.sin(phi), -np.sin(theta) ]) v = np.array([-np.sin(phi), np.cos(phi), 0]) m = -u * np.sin(psi) - v * np.cos(psi) n = -u * np.cos(psi) + v * np.sin(psi) if mode.lower() == 'plus': return np.einsum('i,j->ij', m, m) - np.einsum('i,j->ij', n, n) elif mode.lower() == 'cross': return np.einsum('i,j->ij', m, n) + np.einsum('i,j->ij', n, m) elif mode.lower() == 'breathing': return np.einsum('i,j->ij', m, m) + np.einsum('i,j->ij', n, n) # Calculating omega here to avoid calculation when model in [plus, cross, breathing] omega = np.cross(m, n) if mode.lower() == 'longitudinal': return np.einsum('i,j->ij', omega, omega) elif mode.lower() == 'x': return np.einsum('i,j->ij', m, omega) + np.einsum('i,j->ij', omega, m) elif mode.lower() == 'y': return np.einsum('i,j->ij', n, omega) + np.einsum('i,j->ij', omega, n) else: raise ValueError("{} not a polarization mode!".format(mode))
def vector_compute_arrival_time_at_detector(det, RA, DEC, tref, tref_geo=None): """ Transfer a geocentered reference time 'tref' to the detector-based arrival time as a function of sky position. If the treef_geo argument is specified, then the tref_geo is used to calculate an hour angle as provided by `XLALGreenwichMeanSiderealTime`. This can significantly speed up the function with a sacrifice (likely minimal) of accuracy. """ RA = np.array(RA, copy=False, ndmin=1) DEC = np.array(DEC, copy=False, ndmin=1) tref = np.array(tref, copy=False, ndmin=1) # Calculate hour angle if tref_geo is None: time = np.array([lal.GreenwichMeanSiderealTime(t) for t in tref], dtype=float) else: # FIXME: Could be called once and moved outside time = lal.GreenwichMeanSiderealTime(tref_geo) hr_angle = np.array(time - RA, dtype=float) DEC = np.array(DEC, dtype=float) # compute spherical coordinate position of the detector # based on hour angle and declination cos_hr = np.cos(hr_angle) sin_hr = np.sin(hr_angle) cos_dec = np.cos(DEC) sin_dec = np.sin(DEC) # compute source vector source_xyz = np.array([cos_dec * cos_hr, cos_dec * -sin_hr, sin_dec]) # get the detector vector # must be careful - the C function is designed to compute the # light time delay between two detectors as \vec{det2} - \vec{det1}. # But timeDelay.c gets the earth center time delay by passing # {0., 0., 0.} as the 2nd arg. So det_xyz needs an extra - sign. # FIXME: Could be called once and moved outside det_xyz = -lalsim.DetectorPrefixToLALDetector(det).location return tref + np.dot(np.transpose(det_xyz), source_xyz) / lal.C_SI
def simulate(seed_sim_inspiral, psds, responses, locations, measurement_error, f_low=None, waveform=None): from ..bayestar import filter seed, sim_inspiral = seed_sim_inspiral np.random.seed(seed) # Unpack some values from the row in the table. DL = sim_inspiral.distance ra = sim_inspiral.longitude dec = sim_inspiral.latitude inc = sim_inspiral.inclination # phi = sim_inspiral.coa_phase # arbitrary? psi = sim_inspiral.polarization epoch = sim_inspiral.time_geocent gmst = lal.GreenwichMeanSiderealTime(epoch) f_low = f_low or sim_inspiral.f_lower waveform = waveform or sim_inspiral.waveform # Signal models for each detector. H = filter.sngl_inspiral_psd(waveform, mass1=sim_inspiral.mass1, mass2=sim_inspiral.mass2, spin1x=sim_inspiral.spin1x, spin1y=sim_inspiral.spin1y, spin1z=sim_inspiral.spin1z, spin2x=sim_inspiral.spin2x, spin2y=sim_inspiral.spin2y, spin2z=sim_inspiral.spin2z, f_min=f_low) return [ simulate_snr(ra, dec, psi, inc, DL, epoch, gmst, H, S, response, location, measurement_error=measurement_error) for S, response, location in zip(psds, responses, locations) ]
def complex_antenna_factor(det, RA, DEC, psi, tref): """ Function to compute the complex-valued antenna pattern function: F+ + i Fx 'det' is a detector prefix string (e.g. 'H1') 'RA' and 'DEC' are right ascension and declination (in radians) 'psi' is the polarization angle 'tref' is the reference GPS time """ detector = lalsim.DetectorPrefixToLALDetector(det) Fp, Fc = lal.ComputeDetAMResponse(detector.response, RA, DEC, psi, lal.GreenwichMeanSiderealTime(tref)) return Fp + 1j * Fc
def hrss_in_instrument(sim, instrument, offsetvector): """ Given an injection and an instrument, compute and return the h_rss of the injection as should be observed in the instrument. That is, project the waveform onto the instrument, and return the root integrated strain squared. """ # FIXME: this function is really only correct for sine-Gaussian # injections. that's OK because I only quote sensitivities in # units of hrss when discussing sine-Gaussians. # # the problem is the following. first, # # h = F+ h+ + Fx hx # # so # # h^{2} = F+^2 h+^2 + Fx^2 hx^2 + 2 F+ Fx h+ hx # # which means to calculate the hrss in the instrument you need to # know: mean-square h in the + polarization, mean-square h in the # x polarization, and the correlation between the polarizations <h+ # hx>. these could be recorded in the sim_burst table, but they # aren't at present. # semimajor and semiminor axes of polarization ellipse a = 1.0 / math.sqrt(2.0 - sim.pol_ellipse_e**2) b = a * math.sqrt(1.0 - sim.pol_ellipse_e**2) # hrss in plus and cross polarizations hplusrss = sim.hrss * (a * math.cos(sim.pol_ellipse_angle) - b * math.sin(sim.pol_ellipse_angle)) hcrossrss = sim.hrss * (b * math.cos(sim.pol_ellipse_angle) + a * math.sin(sim.pol_ellipse_angle)) # antenna response factors fplus, fcross = lal.ComputeDetAMResponse( lal.cached_detector_by_prefix[instrument].response, sim.ra, sim.dec, sim.psi, lal.GreenwichMeanSiderealTime( sim.time_at_instrument(instrument, offsetvector))) # hrss in detector return math.sqrt((fplus * hplusrss)**2 + (fcross * hcrossrss)**2)
def fill_sim_inspiral_row(self, row): # using dummy values for many fields, should work for our purposes row.waveform = 'TaylorT4threePointFivePN' row.distance = self.distance total_mass = self.mass1 + self.mass2 row.mass1 = self.mass1 row.mass2 = self.mass2 row.eta = self.mass1 * self.mass2 / total_mass**2 row.mchirp = total_mass * row.eta**(3. / 5.) row.latitude = self.latitude row.longitude = self.longitude row.inclination = self.inclination row.polarization = self.polarization row.phi0 = 0 row.f_lower = 20 row.f_final = lal.C_SI ** 3 / \ (6. ** (3. / 2.) * lal.PI * lal.G_SI * total_mass) row.spin1x = row.spin1y = row.spin1z = 0 row.spin2x = row.spin2y = row.spin2z = 0 row.alpha1 = 0 row.alpha2 = 0 row.alpha3 = 0 row.alpha4 = 0 row.alpha5 = 0 row.alpha6 = 0 row.alpha = 0 row.beta = 0 row.theta0 = 0 row.psi0 = 0 row.psi3 = 0 row.geocent_end_time = int(self.end_time) row.geocent_end_time_ns = int(1e9 * (self.end_time - row.geocent_end_time)) row.end_time_gmst = lal.GreenwichMeanSiderealTime( lal.LIGOTimeGPS(self.end_time)) for d in 'lhvgt': row.__setattr__('eff_dist_' + d, row.distance) row.__setattr__(d + '_end_time', row.geocent_end_time) row.__setattr__(d + '_end_time_ns', row.geocent_end_time_ns) row.amp_order = 0 row.coa_phase = 0 row.bandpass = 0 row.taper = self.taper row.numrel_mode_min = 0 row.numrel_mode_max = 0 row.numrel_data = None row.source = 'ANTANI'
def radectoearth(ra, dec, gps, filename=''): gmst = np.mod(lal.GreenwichMeanSiderealTime(gps), 2 * np.pi) * 86400 / 2 / np.pi gmst_deg = gmst / 86400 * 360 # ---- Compute Earth-based coordinates of targeted sky location, in degrees. phi_deg = ra - gmst_deg theta_deg = 90 - dec # ---- Convert to radians. phi = phi_deg * 2 * np.pi / 360 phi = np.mod(phi + np.pi, 2 * np.pi) - np.pi theta = theta_deg * 2 * np.pi / 360 if filename: with open(filename, 'w') as f: f.write('{0} {1}'.format(phi, theta)) f.close() return else: return phi, theta
def string_amplitude_in_instrument(sim, instrument, offsetvector): """ Given a string cusp injection and an instrument, compute and return the amplitude of the injection as should be observed in the instrument. """ assert sim.waveform == "StringCusp" # antenna response factors fplus, fcross = lal.ComputeDetAMResponse( lal.cached_detector_by_prefix[instrument].response, sim.ra, sim.dec, sim.psi, lal.GreenwichMeanSiderealTime( sim.time_at_instrument(instrument, offsetvector))) # amplitude in detector return fplus * sim.amplitude
def zenith_azimuth_to_ra_dec(zenith, azimuth, geocent_time, ifos): """ Convert from the 'detector frame' to the Earth frame. Parameters kappa: float The zenith angle in the detector frame eta: float The azimuthal angle in the detector frame geocent_time: float GPS time at geocenter ifos: list List of Interferometer objects defining the detector frame Returns ------- ra, dec: float The zenith and azimuthal angles in the sky frame. """ theta, phi = zenith_azimuth_to_theta_phi(zenith, azimuth, ifos) gmst = lal.GreenwichMeanSiderealTime(geocent_time) ra, dec = theta_phi_to_ra_dec(theta, phi, gmst) ra = ra % (2 * np.pi) return ra, dec
def test_antenna_pattern_vs_lal(self): gmst = lal.GreenwichMeanSiderealTime(self.gpstime) f_bilby = np.zeros((self.trial, 6)) f_lal = np.zeros((self.trial, 6)) for n, ifo_name in enumerate(self.ifo_names): response = lalsimulation.DetectorPrefixToLALDetector( self.lal_prefixes[ifo_name]).response ifo = self.ifos[n] for i in range(self.trial): ra = 2. * np.pi * np.random.uniform() dec = np.pi * np.random.uniform() - np.pi / 2. psi = np.pi * np.random.uniform() f_lal[i] = lal.ComputeDetAMResponseExtraModes( response, ra, dec, psi, gmst) for m, pol in enumerate(self.polarizations): f_bilby[i, m] = ifo.antenna_response(ra, dec, self.gpstime, psi, pol) std = np.std(f_bilby - f_lal, axis=0) for m, pol in enumerate(self.polarizations): with self.subTest(':'.join((ifo_name, pol))): self.assertAlmostEqual(std[m], 0.0, places=7)
def pick_coinc(): """Pick a coincidence from the "First Two Years" paper.""" filename = pkg_resources.resource_filename( __name__, '../data/first2years/2016/gstlal.xml.gz') xmldoc = utils.load_filename(filename, contenthandler=ContentHandler) root, = xmldoc.childNodes # Remove unneeded tables for lsctable in (lsctables.FilterTable, lsctables.SegmentTable, lsctables.SegmentDefTable, lsctables.SimInspiralTable, lsctables.SummValueTable, lsctables.SearchSummVarsTable): root.removeChild(lsctable.get_table(xmldoc)) coinc_inspiral_table = table = lsctables.CoincInspiralTable.get_table( xmldoc) # Determine event with most recent sideral time gps_time_now = lal.GPSTimeNow() gmsts = np.asarray([lal.GreenwichMeanSiderealTime(_.end) for _ in table]) gmst_now = lal.GreenwichMeanSiderealTime(gps_time_now) div, rem = divmod(gmst_now - gmsts, 2 * np.pi) i = np.argmin(rem) new_gmst = div[i] * 2 * np.pi + gmsts[i] old_time = table[i].end new_time = lal.LIGOTimeGPS() result = lal.GreenwichMeanSiderealTimeToGPS(new_gmst, new_time) result.disown() del result delta_t = new_time - old_time target_coinc_event_id = int(table[i].coinc_event_id) # Remove unneeded rows table[:] = [ row for row in table if int(row.coinc_event_id) == target_coinc_event_id ] target_end_time = table[0].get_end() coinc_table = table = lsctables.CoincTable.get_table(xmldoc) table[:] = [ row for row in table if int(row.coinc_event_id) == target_coinc_event_id ] table = lsctables.CoincMapTable.get_table(xmldoc) table[:] = [ row for row in table if int(row.coinc_event_id) == target_coinc_event_id ] target_sngl_inspirals = frozenset(row.event_id for row in table) sngl_inspiral_table = table = lsctables.SnglInspiralTable.get_table(xmldoc) table[:] = [row for row in table if row.event_id in target_sngl_inspirals] table = lsctables.ProcessTable.get_table(xmldoc) table[:] = [row for row in table if row.program == 'gstlal_inspiral'] target_process_ids = frozenset(row.process_id for row in table) table = lsctables.SearchSummaryTable.get_table(xmldoc) table[:] = [ row for row in table if target_end_time in row.get_out() and row.process_id in target_process_ids ] target_process_ids = frozenset(row.process_id for row in table) table = lsctables.ProcessTable.get_table(xmldoc) table[:] = [row for row in table if row.process_id in target_process_ids] table = lsctables.ProcessParamsTable.get_table(xmldoc) table[:] = [row for row in table if row.process_id in target_process_ids] # Shift event times for row in coinc_inspiral_table: row.end += delta_t for row in sngl_inspiral_table: row.end += delta_t row.end_time_gmst = lal.GreenwichMeanSiderealTime(row.end) # The old version of gstlal used to produce the "First Two Years" data set # stored likelihood in the coinc_event.likelihood column, but newer # versions store the *natural log* of the likelihood here. The p_astro # calculation requires this to be log likelihood. for row in coinc_table: row.likelihood = np.log(row.likelihood) # Gstlal stores the template's SVD bank index in the Gamma1 column. # Fill this in so that we can calculate p_astro # (see :mod:`gwcelery.tasks.p_astro_gstlal`). for row in sngl_inspiral_table: row.Gamma1 = 16 coinc_xml = io.BytesIO() utils.write_fileobj(xmldoc, coinc_xml) return coinc_xml.getvalue()
def response(gpsTime, rightAscension, declination, inclination, polarization, unit, det): """ response( gpsTime, rightAscension, declination, inclination, polarization, unit, detector ) Calculates the antenna factors for a detector 'detector' (e.g. 'H1') at a given gps time (as integer) for a given sky location (rightAscension, declination) in some unit (degree/radians). This computation also takes into account a specific inclination and polarization. The returned values are: (f-plus, f-cross, f-average, q-value). Example: antenna.response( 854378604.780, 11.089, 42.308, 0, 0, 'radians', 'H1' ) """ # check the input arguments if unit == 'radians': ra_rad = rightAscension de_rad = declination psi_rad = polarization iota_rad = inclination elif unit == 'degree': ra_rad = rightAscension / 180.0 * pi de_rad = declination / 180.0 * pi psi_rad = polarization / 180.0 * pi iota_rad = inclination / 180.0 * pi else: raise ValueError, "Unknown unit %s" % unit # calculate GMST if the GPS time gps = lal.LIGOTimeGPS(gpsTime) gmst_rad = lal.GreenwichMeanSiderealTime(gps) # create detector-name map detMap = { 'H1': 'LHO_4k', 'H2': 'LHO_2k', 'L1': 'LLO_4k', 'G1': 'GEO_600', 'V1': 'VIRGO', 'T1': 'TAMA_300' } try: detector = detMap[det] except KeyError: raise ValueError, "ERROR. Key %s is not a valid detector name."\ % (det) # get detector if detector not in inject.cached_detector.keys(): raise ValueError, "%s is not a cached detector. "\ "Cached detectors are: %s" \ % (det, inject.cached_detector.keys()) # get the correct response data response = inject.cached_detector[detector].response # actual computation of antenna factors f_plus, f_cross = lal.ComputeDetAMResponse(response, ra_rad, de_rad, psi_rad, gmst_rad) f_ave = sqrt((f_plus * f_plus + f_cross * f_cross) / 2.0) ci = cos(iota_rad) cc = ci * ci # calculate q-value, e.g. ratio of effective to real distance # ref: Duncans PhD, eq. (4.3) on page 57 f_q = sqrt(f_plus * f_plus * (1 + cc) * (1 + cc) / 4.0 + f_cross * f_cross * cc) # output return f_plus, f_cross, f_ave, f_q
def localize(event, waveform='o2-uberbank', f_low=30.0, min_inclination=0, max_inclination=np.pi / 2, min_distance=None, max_distance=None, prior_distance_power=None, cosmology=False, mcmc=False, chain_dump=None, enable_snr_series=True, f_high_truncate=0.95): """Localize a compact binary signal using the BAYESTAR algorithm. Parameters ---------- event : `ligo.skymap.io.events.Event` The event candidate. waveform : str, optional The name of the waveform approximant. f_low : float, optional The low frequency cutoff. min_distance, max_distance : float, optional The limits of integration over luminosity distance, in Mpc (default: determine automatically from detector sensitivity). prior_distance_power : int, optional The power of distance that appears in the prior (default: 2, uniform in volume). cosmology: bool, optional Set to enable a uniform in comoving volume prior (default: false). mcmc : bool, optional Set to use MCMC sampling rather than more accurate Gaussian quadrature. chain_dump : str, optional Save posterior samples to this filename if `mcmc` is set. enable_snr_series : bool, optional Set to False to disable SNR time series. f_high_truncate : float, optional Truncate the noise power spectral densities at this factor times the highest sampled frequency to suppress artifacts caused by incorrect PSD conditioning by some matched filter pipelines. Returns ------- skymap : `astropy.table.Table` A 3D sky map in multi-order HEALPix format. """ # Hide event parameters, but show all other arguments def formatvalue(value): if isinstance(value, Event): return '=...' else: return '=' + repr(value) frame = inspect.currentframe() argstr = inspect.formatargvalues(*inspect.getargvalues(frame), formatvalue=formatvalue) run_time = time.perf_counter() epoch, sample_rate, toas, snr_series, responses, locations, horizons = \ condition(event, waveform=waveform, f_low=f_low, enable_snr_series=enable_snr_series, f_high_truncate=f_high_truncate) min_distance, max_distance, prior_distance_power, cosmology = \ condition_prior(horizons, min_distance, max_distance, prior_distance_power, cosmology) gmst = lal.GreenwichMeanSiderealTime(epoch) # Time and run sky localization. log.debug('starting computationally-intensive section') args = (min_inclination, max_inclination, min_distance, max_distance, prior_distance_power, cosmology, gmst, sample_rate, toas, snr_series, responses, locations, horizons) if mcmc: max_abs_t = 2 * snr_series.data.shape[1] / sample_rate skymap = localize_emcee( args=args, xmin=[0, -1, min_distance, -1, 0, 0], xmax=[2 * np.pi, 1, max_distance, 1, 2 * np.pi, 2 * max_abs_t], chain_dump=chain_dump) else: skymap, log_bci, log_bsn = core.toa_phoa_snr(*args) skymap = Table(skymap) skymap.meta['log_bci'] = log_bci skymap.meta['log_bsn'] = log_bsn # Convert distance moments to parameters try: distmean = skymap.columns.pop('DISTMEAN') diststd = skymap.columns.pop('DISTSTD') except KeyError: distmean, diststd, _ = distance.parameters_to_moments( skymap['DISTMU'], skymap['DISTSIGMA']) else: skymap['DISTMU'], skymap['DISTSIGMA'], skymap['DISTNORM'] = \ distance.moments_to_parameters(distmean, diststd) # Add marginal distance moments good = np.isfinite(distmean) & np.isfinite(diststd) prob = (moc.uniq2pixarea(skymap['UNIQ']) * skymap['PROBDENSITY'])[good] distmean = distmean[good] diststd = diststd[good] rbar = (prob * distmean).sum() r2bar = (prob * (np.square(diststd) + np.square(distmean))).sum() skymap.meta['distmean'] = rbar skymap.meta['diststd'] = np.sqrt(r2bar - np.square(rbar)) run_time = time.perf_counter() - run_time end_time = lal.GPSTimeNow() log.info('finished computationally-intensive section in %.3f s', run_time) # Fill in metadata and return. program, _ = os.path.splitext(os.path.basename(sys.argv[0])) skymap.meta.update(metadata_for_version_module(version)) skymap.meta['creator'] = 'BAYESTAR' skymap.meta['origin'] = 'LIGO/Virgo' skymap.meta['gps_time'] = float(epoch) skymap.meta['runtime'] = float(run_time) skymap.meta['instruments'] = {single.detector for single in event.singles} skymap.meta['gps_creation_time'] = end_time skymap.meta['history'] = [ '', 'Generated by calling the following Python function:', '{}.{}{}'.format(__name__, frame.f_code.co_name, argstr), '', 'This was the command line that started the program:', ' '.join([program] + sys.argv[1:]) ] return skymap
progress.max = len(opts.fitsfilenames) matplotlib.rc('path', simplify=True, simplify_threshold=1) if opts.colormap is None: colors = ['k'] * len(opts.fitsfilenames) else: colors = matplotlib.cm.get_cmap(opts.colormap) colors = colors(np.linspace(0, 1, len(opts.fitsfilenames))) for count_records, (color, fitsfilename) in enumerate(zip(colors, opts.fitsfilenames)): progress.update(count_records, fitsfilename) skymap, metadata = fits.read_sky_map(fitsfilename, nest=None) nside = hp.npix2nside(len(skymap)) gmst = lal.GreenwichMeanSiderealTime(metadata['gps_time']) % (2 * np.pi) indices = np.argsort(-skymap) region = np.empty(skymap.shape) region[indices] = 100 * np.cumsum(skymap[indices]) plot.healpix_contour(region, nest=metadata['nest'], dlon=-gmst, colors=[color], linewidths=0.5, levels=[opts.contour], alpha=opts.alpha) progress.update(-1, 'saving figure') # Add a white outline to all text to make it stand out from the background.
def ligolw_sky_map(sngl_inspirals, approximant, amplitude_order, phase_order, f_low, min_distance=None, max_distance=None, prior=None, method="toa_snr", reference_frequency=None, psds=None, nside=-1): """Convenience function to produce a sky map from LIGO-LW rows. Note that min_distance and max_distance should be in Mpc.""" if method == "toa_snr" and prior is None: raise ValueError( "For method='toa_snr', the argument prior is required.") ifos = [sngl_inspiral.ifo for sngl_inspiral in sngl_inspirals] # Extract masses from the table. mass1s = np.asarray( [sngl_inspiral.mass1 for sngl_inspiral in sngl_inspirals]) mass2s = np.asarray( [sngl_inspiral.mass2 for sngl_inspiral in sngl_inspirals]) # Extract SNRs from table. # FIXME: should get complex SNR, but MBTAOnline events don't populate the # coa_phase column, and we are not using coa_phase yet. snrs = np.asarray([sngl_inspiral.snr for sngl_inspiral in sngl_inspirals]) # Extract TOAs from table. toas_ns = np.asarray( [sngl_inspiral.get_end().ns() for sngl_inspiral in sngl_inspirals], dtype=np.int64) # Optionally apply reference frequency shift. if reference_frequency is not None: toas_ns -= [ int( round(1e9 * lalsimulation.SimInspiralTaylorF2ReducedSpinChirpTime( reference_frequency, m1 * lal.LAL_MSUN_SI, m2 * lal.LAL_MSUN_SI, 0, 4))) for m1, m2 in zip(mass1s, mass2s) ] # Convert TOAs from nanoseconds to seconds. toas = 1e-9 * toas_ns # Find average Greenwich mean sidereal time of event. mean_toa_ns = sum(toas_ns) // len(toas_ns) epoch = lal.LIGOTimeGPS(0, long(mean_toa_ns)) gmst = lal.GreenwichMeanSiderealTime(epoch) # Power spectra for each detector. if psds is None: psds = [timing.get_noise_psd_func(ifo) for ifo in ifos] # Signal models for each detector. signal_models = [ timing.SignalModel(mass1, mass2, psd, f_low, approximant, amplitude_order, phase_order) for mass1, mass2, psd in zip(mass1s, mass2s, psds) ] # Get SNR=1 horizon distances for each detector. horizons = [ signal_model.get_horizon_distance() for signal_model in signal_models ] # Estimate TOA uncertainty (squared) using CRB or BRB evaluated at MEASURED # values of the SNRs. s2_toas = [ np.square(signal_model.get_toa_uncert(np.abs(snr))) for signal_model, snr in zip(signal_models, snrs) ] # Look up physical parameters for detector. detectors = [ lalsimulation.InstrumentNameToLALDetector(str(ifo)) for ifo in ifos ] responses = [det.response for det in detectors] locations = [det.location for det in detectors] # Use half the minimum effective distance as the default value for # min_distance and twice the maximum effective distance as the default # value for max_distance. if min_distance is None or max_distance is None: effective_distances = np.asarray(horizons) / np.abs(snrs) if min_distance is None: min_distance = 0.5 * min(effective_distances) if max_distance is None: max_distance = 2 * max(effective_distances) # Time and run sky localization. start_time = time.time() if method == "toa": prob = sky_map.tdoa(gmst, toas, s2_toas, locations, nside=nside) elif method == "toa_snr": prob = sky_map.tdoa_snr(gmst, toas, snrs, s2_toas, responses, locations, horizons, min_distance, max_distance, prior, nside=nside) else: raise ValueError("Unrecognized method: %s" % method) end_time = time.time() # Find elapsed run time. elapsed_time = end_time - start_time # Done! return prob, epoch, elapsed_time
import healpy as hp import lal from lalinference import fits from lalinference import plot fig = plt.figure(figsize=(opts.figure_width, opts.figure_height), frameon=False) ax = plt.subplot(111, projection='mollweide' if opts.geo else 'astro mollweide') ax.cla() ax.grid() skymap, metadata = fits.read_sky_map(infilename, nest=None) nside = hp.npix2nside(len(skymap)) if opts.geo: dlon = -lal.GreenwichMeanSiderealTime(lal.LIGOTimeGPS(metadata['gps_time'])) % (2*np.pi) else: dlon = 0 # Convert sky map from probability to probability per square degree. probperdeg2 = skymap / hp.nside2pixarea(nside, degrees=True) # Plot sky map. vmax = probperdeg2.max() plot.healpix_heatmap( probperdeg2, dlon=dlon, nest=metadata['nest'], vmin=0., vmax=vmax, cmap=plt.get_cmap(opts.colormap)) if opts.colorbar: # Plot colorbar. cb = plot.colorbar()
def localize(event, waveform='o2-uberbank', f_low=30.0, min_distance=None, max_distance=None, prior_distance_power=None, cosmology=False, method='toa_phoa_snr', nside=-1, chain_dump=None, enable_snr_series=True, f_high_truncate=0.95): """Convenience function to produce a sky map from LIGO-LW rows. Note that min_distance and max_distance should be in Mpc. Returns a 'NESTED' ordering HEALPix image as a Numpy array. """ frame = inspect.currentframe() argstr = inspect.formatargvalues(*inspect.getargvalues(frame)) start_time = lal.GPSTimeNow() singles = event.singles if not enable_snr_series: singles = [single for single in singles if single.snr is not None] ifos = [single.detector for single in singles] # Extract SNRs from table. snrs = np.ma.asarray([ np.ma.masked if single.snr is None else single.snr for single in singles ]) # Look up physical parameters for detector. detectors = [ lalsimulation.DetectorPrefixToLALDetector(str(ifo)) for ifo in ifos ] responses = np.asarray([det.response for det in detectors]) locations = np.asarray([det.location for det in detectors]) # Power spectra for each detector. psds = [single.psd for single in singles] psds = [ timing.InterpolatedPSD(filter.abscissa(psd), psd.data.data, f_high_truncate=f_high_truncate) for psd in psds ] log.debug('calculating templates') H = filter.sngl_inspiral_psd(waveform, f_min=f_low, **event.template_args) log.debug('calculating noise PSDs') HS = [filter.signal_psd_series(H, S) for S in psds] # Signal models for each detector. log.debug('calculating Fisher matrix elements') signal_models = [timing.SignalModel(_) for _ in HS] # Get SNR=1 horizon distances for each detector. horizons = np.asarray([ signal_model.get_horizon_distance() for signal_model in signal_models ]) weights = np.ma.asarray([ 1 / np.square(signal_model.get_crb_toa_uncert(snr)) for signal_model, snr in zip(signal_models, snrs) ]) # Center detector array. locations -= np.sum(locations * weights.reshape(-1, 1), axis=0) / np.sum(weights) if cosmology: log.warn('Enabling cosmological prior. ' 'This feature is UNREVIEWED.') if enable_snr_series: log.warn('Enabling input of SNR time series. ' 'This feature is UNREVIEWED.') snr_series = [single.snr_series for single in singles] if all(s is None for s in snr_series): snr_series = None else: snr_series = None # Maximum barycentered arrival time error: # |distance from array barycenter to furthest detector| / c + 5 ms. # For LHO+LLO, this is 15.0 ms. # For an arbitrary terrestrial detector network, the maximum is 26.3 ms. max_abs_t = np.max(np.sqrt(np.sum(np.square(locations / lal.C_SI), axis=1))) + 0.005 if snr_series is None: log.warn( "No SNR time series found, so we are creating a zero-noise " "SNR time series from the whitened template's autocorrelation " "sequence. The sky localization uncertainty may be " "underestimated.") acors, sample_rates = zip( *[filter.autocorrelation(_, max_abs_t) for _ in HS]) sample_rate = sample_rates[0] deltaT = 1 / sample_rate nsamples = len(acors[0]) assert all(sample_rate == _ for _ in sample_rates) assert all(nsamples == len(_) for _ in acors) nsamples = nsamples * 2 - 1 snr_series = [] for acor, single in zip(acors, singles): series = lal.CreateCOMPLEX8TimeSeries('fake SNR', 0, 0, deltaT, lal.StrainUnit, nsamples) series.epoch = single.time - 0.5 * (nsamples - 1) * deltaT acor = np.concatenate((np.conj(acor[:0:-1]), acor)) series.data.data = single.snr * filter.exp_i(single.phase) * acor snr_series.append(series) # Ensure that all of the SNR time series have the same sample rate. # FIXME: for now, the Python wrapper expects all of the SNR time sries to # also be the same length. deltaT = snr_series[0].deltaT sample_rate = 1 / deltaT if any(deltaT != series.deltaT for series in snr_series): raise ValueError('BAYESTAR does not yet support SNR time series with ' 'mixed sample rates') # Ensure that all of the SNR time series have odd lengths. if any(len(series.data.data) % 2 == 0 for series in snr_series): raise ValueError('SNR time series must have odd lengths') # Trim time series to the desired length. max_abs_n = int(np.ceil(max_abs_t * sample_rate)) desired_length = 2 * max_abs_n - 1 for i, series in enumerate(snr_series): length = len(series.data.data) if length > desired_length: snr_series[i] = lal.CutCOMPLEX8TimeSeries( series, length // 2 + 1 - max_abs_n, desired_length) # FIXME: for now, the Python wrapper expects all of the SNR time sries to # also be the same length. nsamples = len(snr_series[0].data.data) if any(nsamples != len(series.data.data) for series in snr_series): raise ValueError('BAYESTAR does not yet support SNR time series of ' 'mixed lengths') # Perform sanity checks that the middle sample of the SNR time series match # the sngl_inspiral records. Relax valid interval slightly from # +/- 0.5 deltaT to +/- 0.6 deltaT for floating point roundoff error. for single, series in zip(singles, snr_series): if np.abs(0.5 * (nsamples - 1) * series.deltaT + float(series.epoch - single.time)) >= 0.6 * deltaT: raise ValueError('BAYESTAR expects the SNR time series to be ' 'centered on the single-detector trigger times') # Extract the TOAs in GPS nanoseconds from the SNR time series, assuming # that the trigger happened in the middle. toas_ns = [ series.epoch.ns() + 1e9 * 0.5 * (len(series.data.data) - 1) * series.deltaT for series in snr_series ] # Collect all of the SNR series in one array. snr_series = np.vstack([series.data.data for series in snr_series]) # Center times of arrival and compute GMST at mean arrival time. # Pre-center in integer nanoseconds to preserve precision of # initial datatype. epoch = sum(toas_ns) // len(toas_ns) toas = 1e-9 * (np.asarray(toas_ns) - epoch) # FIXME: np.average does not yet support masked arrays. # Replace with np.average when numpy 1.13.0 is available. mean_toa = np.sum(toas * weights) / np.sum(weights) toas -= mean_toa epoch += int(np.round(1e9 * mean_toa)) epoch = lal.LIGOTimeGPS(0, int(epoch)) gmst = lal.GreenwichMeanSiderealTime(epoch) # Translate SNR time series back to time of first sample. toas -= 0.5 * (nsamples - 1) * deltaT # If minimum distance is not specified, then default to 0 Mpc. if min_distance is None: min_distance = 0 # If maximum distance is not specified, then default to the SNR=4 # horizon distance of the most sensitive detector. if max_distance is None: max_distance = max(horizons) / 4 # If prior_distance_power is not specified, then default to 2 # (p(r) ~ r^2, uniform in volume). if prior_distance_power is None: prior_distance_power = 2 # Raise an exception if 0 Mpc is the minimum effective distance and the # prior is of the form r**k for k<0 if min_distance == 0 and prior_distance_power < 0: raise ValueError( ('Prior is a power law r^k with k={}, ' 'undefined at min_distance=0').format(prior_distance_power)) # Time and run sky localization. log.debug('starting computationally-intensive section') if method == 'toa_phoa_snr': skymap, log_bci, log_bsn = _sky_map.toa_phoa_snr( min_distance, max_distance, prior_distance_power, cosmology, gmst, sample_rate, toas, snr_series, responses, locations, horizons) skymap = Table(skymap) skymap.meta['log_bci'] = log_bci skymap.meta['log_bsn'] = log_bsn elif method == 'toa_phoa_snr_mcmc': skymap = localize_emcee( logl=_sky_map.log_likelihood_toa_phoa_snr, loglargs=(gmst, sample_rate, toas, snr_series, responses, locations, horizons), logp=toa_phoa_snr_log_prior, logpargs=(min_distance, max_distance, prior_distance_power, max_abs_t), xmin=[0, -1, min_distance, -1, 0, 0], xmax=[2 * np.pi, 1, max_distance, 1, 2 * np.pi, 2 * max_abs_t], nside=nside, chain_dump=chain_dump) else: raise ValueError('Unrecognized method: %s' % method) # Convert distance moments to parameters distmean = skymap.columns.pop('DISTMEAN') diststd = skymap.columns.pop('DISTSTD') skymap['DISTMU'], skymap['DISTSIGMA'], skymap['DISTNORM'] = \ distance.moments_to_parameters(distmean, diststd) # Add marginal distance moments good = np.isfinite(distmean) & np.isfinite(diststd) prob = (moc.uniq2pixarea(skymap['UNIQ']) * skymap['PROBDENSITY'])[good] distmean = distmean[good] diststd = diststd[good] rbar = (prob * distmean).sum() r2bar = (prob * (np.square(diststd) + np.square(distmean))).sum() skymap.meta['distmean'] = rbar skymap.meta['diststd'] = np.sqrt(r2bar - np.square(rbar)) log.debug('finished computationally-intensive section') end_time = lal.GPSTimeNow() # Fill in metadata and return. program, _ = os.path.splitext(os.path.basename(sys.argv[0])) skymap.meta['creator'] = 'BAYESTAR' skymap.meta['origin'] = 'LIGO/Virgo' skymap.meta['vcs_info'] = vcs_info skymap.meta['gps_time'] = float(epoch) skymap.meta['runtime'] = float(end_time - start_time) skymap.meta['instruments'] = {single.detector for single in singles} skymap.meta['gps_creation_time'] = end_time skymap.meta['history'] = [ '', 'Generated by calling the following Python function:', '{}.{}{}'.format(__name__, frame.f_code.co_name, argstr), '', 'This was the command line that started the program:', ' '.join([program] + sys.argv[1:]) ] return skymap
for sim_inspiral in progress.iterate(sim_inspiral_table): # Unpack some values from the row in the table. m1 = sim_inspiral.mass1 m2 = sim_inspiral.mass2 f_low = sim_inspiral.f_lower if opts.f_low is None else opts.f_low DL = sim_inspiral.distance ra = sim_inspiral.longitude dec = sim_inspiral.latitude inc = sim_inspiral.inclination phi = sim_inspiral.coa_phase psi = sim_inspiral.polarization epoch = lal.LIGOTimeGPS(sim_inspiral.geocent_end_time, sim_inspiral.geocent_end_time_ns) gmst = lal.GreenwichMeanSiderealTime(epoch) waveform = (sim_inspiral.waveform if opts.waveform is None else opts.waveform) # FIXME: Set tranverse spin components to 0 sim_inspiral.spin1x = 0 sim_inspiral.spin1y = 0 sim_inspiral.spin2x = 0 sim_inspiral.spin2y = 0 # Pre-evaluate some trigonometric functions that we will need. u = np.cos(inc) u2 = np.square(u) # Signal models for each detector. H = filter.sngl_inspiral_psd(waveform,