Coverage for pesummary/gw/waveform.py: 44.3%
262 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-12-09 22:34 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-12-09 22:34 +0000
1# Licensed under an MIT style license -- see LICENSE.md
3import numpy as np
4import lalsimulation as lalsim
5from lalsimulation import (
6 SimInspiralGetSpinFreqFromApproximant, SIM_INSPIRAL_SPINS_CASEBYCASE,
7 SIM_INSPIRAL_SPINS_FLOW
8)
9from pesummary.utils.utils import iterator, logger
10from pesummary.utils.exceptions import EvolveSpinError
12__author__ = ["Charlie Hoy <charlie.hoy@ligo.org>"]
13_python_module_from_approximant_name = {
14 "SEOBNRv5": "lalsimulation.gwsignal.models.pyseobnr_model"
15}
17def _get_spin_freq_from_approximant(approximant):
18 """Determine whether the reference frequency is the starting frequency
19 for a given approximant string.
21 Parameters
22 ----------
23 approximant: str
24 Name of the approximant you wish to check
25 """
26 try:
27 # default to using LAL code
28 approx = getattr(lalsim, approximant)
29 return SimInspiralGetSpinFreqFromApproximant(approx)
30 except AttributeError:
31 from lalsimulation import (
32 SIM_INSPIRAL_SPINS_NONPRECESSING, SIM_INSPIRAL_SPINS_F_REF
33 )
34 # check to see if approximant is in gwsignal
35 approx = _gwsignal_generator_from_string(approximant)
36 meta = approx.metadata
37 if meta["type"] == "aligned_spin":
38 return SIM_INSPIRAL_SPINS_NONPRECESSING
39 elif meta["type"] == "precessing_spin":
40 if meta["f_ref_spin"]:
41 return SIM_INSPIRAL_SPINS_F_REF
42 return SIM_INSPIRAL_SPINS_FLOW
43 raise EvolveSpinError(
44 "Unable to evolve spins as '{}' does not have a set frequency "
45 "at which the spins are defined".format(approximant)
46 )
49def _get_start_freq_from_approximant(approximant, f_low, f_ref):
50 """Determine the starting frequency to use when evolving the spins for
51 a given approximant string.
53 Parameters
54 ----------
55 approximant: str
56 Name of the approximant you wish to check
57 f_low: float
58 Low frequency used when generating the posterior samples
59 f_ref: float
60 Reference frequency used when generating the posterior samples
61 """
62 try:
63 spinfreq_enum = _get_spin_freq_from_approximant(approximant)
64 except ValueError: # raised when approximant is not in gwsignal
65 raise EvolveSpinError(
66 "Unable to evolve spins as '{}' is unknown to lalsimulation "
67 "and gwsignal".format(approximant)
68 )
69 if spinfreq_enum == SIM_INSPIRAL_SPINS_CASEBYCASE:
70 _msg = (
71 "Unable to evolve spins as '{}' does not have a set frequency "
72 "at which the spins are defined".format(approximant)
73 )
74 logger.warning(_msg)
75 raise EvolveSpinError(_msg)
76 return float(np.where(
77 np.array(spinfreq_enum == SIM_INSPIRAL_SPINS_FLOW), f_low, f_ref
78 ))
81def _check_approximant_from_string(approximant):
82 """Check to see if the approximant is known to lalsimulation and/or
83 gwsignal
85 Parameters
86 ----------
87 approximant: str
88 approximant you wish to check
89 """
90 if hasattr(lalsim, approximant):
91 return True
92 else:
93 try:
94 _ = _gwsignal_generator_from_string(approximant)
95 except (ValueError, NameError):
96 return False
97 return True
100def _lal_approximant_from_string(approximant):
101 """Return the LAL approximant number given an approximant string
103 Parameters
104 ----------
105 approximant: str
106 approximant you wish to convert
107 """
108 return lalsim.GetApproximantFromString(approximant)
111def _gwsignal_generator_from_string(approximant, version="v1", **kwargs):
112 """Return the GW signal generator given an approximant string
114 Parameters
115 ----------
116 approximant: str
117 approximant you wish to convert
118 version: str, optional
119 version of gwsignal_get_waveform_generator to use. 'v1' ignores
120 all kwargs and simply passes the approximant name to
121 gwsignal_get_waveform_generator. 'v2' passes the approximant name
122 and python_module to gwsignal_get_waveform_generator. The python_module
123 can either be provided to _gwsignal_generator_from_string directly or if
124 not provided, PESummary will use the stored default values. Default 'v1'
125 kwargs: dict, optional
126 optional kwargs to pass to gwsignal_get_waveform_generator
127 """
128 from lalsimulation.gwsignal.models import gwsignal_get_waveform_generator
129 if version == "v1":
130 return gwsignal_get_waveform_generator(approximant)
131 elif version == "v2":
132 fallback = [
133 value for key, value in _python_module_from_approximant_name.items()
134 if key in approximant
135 ]
136 if len(fallback):
137 fallback = fallback[0]
138 else:
139 fallback = None
140 module = kwargs.get("python_module", fallback)
141 if module is None:
142 raise ValueError(
143 "Please provide a python_module defining the gwsignal "
144 "generator as pesummary does not have a default value"
145 )
146 return gwsignal_get_waveform_generator(
147 approximant, python_module=module
148 )
149 else:
150 raise ValueError(
151 "Unknown version {}. Please use either v1 or v2".format(version)
152 )
155def _insert_flags(flags, LAL_parameters=None):
156 """Insert waveform specific flags to a LAL dictionary
158 Parameters
159 ----------
160 flags: dict
161 dictionary of flags you wish to add to a LAL dictionary
162 LAL_parameters: LALDict, optional
163 An existing LAL dictionary to add mode array to. If not provided, a new
164 LAL dictionary is created. Default None.
165 """
166 if LAL_parameters is None:
167 import lal
168 LAL_parameters = lal.CreateDict()
169 for key, item in flags.items():
170 func = getattr(lalsim, "SimInspiralWaveformParamsInsert" + key, None)
171 if func is not None:
172 func(LAL_parameters, int(item))
173 else:
174 logger.warning(
175 "Unable to add flag {} to LAL dictionary as no function "
176 "SimInspiralWaveformParamsInsert{} found in "
177 "LALSimulation.".format(key, key)
178 )
179 return LAL_parameters
182def _insert_mode_array(modes, LAL_parameters=None):
183 """Add a mode array to a LAL dictionary
185 Parameters
186 ----------
187 modes: 2d list
188 2d list of modes you wish to add to a LAL dictionary. Must be of the
189 form [[l1, m1], [l2, m2]]
190 LAL_parameters: LALDict, optional
191 An existing LAL dictionary to add mode array to. If not provided, a new
192 LAL dictionary is created. Default None.
193 """
194 if LAL_parameters is None:
195 import lal
196 LAL_parameters = lal.CreateDict()
197 _mode_array = lalsim.SimInspiralCreateModeArray()
198 for l, m in modes:
199 lalsim.SimInspiralModeArrayActivateMode(_mode_array, l, m)
200 lalsim.SimInspiralWaveformParamsInsertModeArray(LAL_parameters, _mode_array)
201 return LAL_parameters
204def _waveform_args(samples, f_ref=20., ind=0, longAscNodes=0., eccentricity=0.):
205 """Arguments to be passed to waveform generation
207 Parameters
208 ----------
209 f_ref: float, optional
210 reference frequency to use when converting spherical spins to
211 cartesian spins
212 ind: int, optional
213 index for the sample you wish to plot
214 longAscNodes: float, optional
215 longitude of ascending nodes, degenerate with the polarization
216 angle. Default 0.
217 eccentricity: float, optional
218 eccentricity at reference frequency. Default 0.
219 """
220 from lal import MSUN_SI, PC_SI
222 key = list(samples.keys())[0]
223 if isinstance(samples[key], (list, np.ndarray)):
224 _samples = {key: value[ind] for key, value in samples.items()}
225 else:
226 _samples = samples.copy()
227 required = [
228 "mass_1", "mass_2", "luminosity_distance"
229 ]
230 if not all(param in _samples.keys() for param in required):
231 raise ValueError(
232 "Unable to generate a waveform. Please add samples for "
233 + ", ".join(required)
234 )
235 waveform_args = [
236 _samples["mass_1"] * MSUN_SI, _samples["mass_2"] * MSUN_SI
237 ]
238 spin_angles = [
239 "theta_jn", "phi_jl", "tilt_1", "tilt_2", "phi_12", "a_1", "a_2",
240 "phase"
241 ]
242 spin_angles_condition = all(
243 spin in _samples.keys() for spin in spin_angles
244 )
245 cartesian_spins = [
246 "spin_1x", "spin_1y", "spin_1z", "spin_2x", "spin_2y", "spin_2z"
247 ]
248 cartesian_spins_condition = any(
249 spin in _samples.keys() for spin in cartesian_spins
250 )
251 if spin_angles_condition and not cartesian_spins_condition:
252 from pesummary.gw.conversions import component_spins
253 data = component_spins(
254 _samples["theta_jn"], _samples["phi_jl"], _samples["tilt_1"],
255 _samples["tilt_2"], _samples["phi_12"], _samples["a_1"],
256 _samples["a_2"], _samples["mass_1"], _samples["mass_2"],
257 f_ref, _samples["phase"]
258 )
259 iota, spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z = data.T
260 spins = [spin_1x, spin_1y, spin_1z, spin_2x, spin_2y, spin_2z]
261 else:
262 iota = _samples["iota"]
263 spins = [
264 _samples[param] if param in _samples.keys() else 0. for param in
265 ["spin_1x", "spin_1y", "spin_1z", "spin_2x", "spin_2y", "spin_2z"]
266 ]
267 _zero_spins = np.isclose(spins, 0.)
268 if sum(_zero_spins):
269 spins = np.array(spins)
270 spins[_zero_spins] = 0.
271 spins = list(spins)
272 waveform_args += spins
273 phase = _samples["phase"] if "phase" in _samples.keys() else 0.
274 waveform_args += [
275 _samples["luminosity_distance"] * PC_SI * 10**6, iota, phase
276 ]
277 waveform_args += [longAscNodes, eccentricity, 0.]
278 return waveform_args, _samples
281def antenna_response(samples, ifo):
282 """
283 """
284 import importlib
286 mod = importlib.import_module("pesummary.gw.plots.plot")
287 func = getattr(mod, "__antenna_response")
288 antenna = func(
289 ifo, samples["ra"], samples["dec"], samples["psi"],
290 samples["geocent_time"]
291 )
292 return antenna
295def _project_waveform(ifo, hp, hc, ra, dec, psi, time):
296 """Project a waveform onto a given detector
298 Parameters
299 ----------
300 ifo: str
301 name of the detector you wish to project the waveform onto
302 hp: np.ndarray
303 plus gravitational wave polarization
304 hc: np.ndarray
305 cross gravitational wave polarization
306 ra: float
307 right ascension to be passed to antenna response function
308 dec: float
309 declination to be passed to antenna response function
310 psi: float
311 polarization to be passed to antenna response function
312 time: float
313 time to be passed to antenna response function
314 """
315 samples = {
316 "ra": ra, "dec": dec, "psi": psi, "geocent_time": time
317 }
318 antenna = antenna_response(samples, ifo)
319 ht = hp * antenna[0] + hc * antenna[1]
320 return ht
323def fd_waveform(
324 samples, approximant, delta_f, f_low, f_high, f_ref=20., project=None,
325 ind=0, longAscNodes=0., eccentricity=0., LAL_parameters=None,
326 mode_array=None, pycbc=False, flen=None, flags={}, debug=False,
327 **kwargs
328):
329 """Generate a gravitational wave in the frequency domain
331 Parameters
332 ----------
333 approximant: str
334 name of the approximant to use when generating the waveform
335 delta_f: float
336 spacing between frequency samples
337 f_low: float
338 frequency to start evaluating the waveform
339 f_high: float
340 frequency to stop evaluating the waveform
341 f_ref: float, optional
342 reference frequency
343 project: str, optional
344 name of the detector to project the waveform onto. If None,
345 the plus and cross polarizations are returned. Default None
346 ind: int, optional
347 index for the sample you wish to plot
348 longAscNodes: float, optional
349 longitude of ascending nodes, degenerate with the polarization
350 angle. Default 0.
351 eccentricity: float, optional
352 eccentricity at reference frequency. Default 0.
353 LAL_parameters: LALDict, optional
354 LAL dictionary containing accessory parameters. Default None
355 mode_array: 2d list
356 2d list of modes you wish to include in waveform. Must be of the form
357 [[l1, m1], [l2, m2]]
358 pycbc: Bool, optional
359 return a the waveform as a pycbc.frequencyseries.FrequencySeries
360 object
361 flen: int
362 Length of the frequency series in samples. Default is None. Only used
363 when pycbc=True
364 debug: bool, optional
365 if True, return the model object used to generate the waveform. Only
366 used when the waveform approximant is not in LALSimulation
367 flags: dict, optional
368 waveform specific flags to add to LAL dictionary
369 **kwargs: dict, optional
370 all kwargs passed to _calculate_hp_hc_fd
371 """
372 from gwpy.frequencyseries import FrequencySeries
374 waveform_args, _samples = _waveform_args(
375 samples, f_ref=f_ref, ind=ind, longAscNodes=longAscNodes,
376 eccentricity=eccentricity
377 )
378 if len(flags):
379 LAL_parameters = _insert_flags(
380 flags, LAL_parameters=LAL_parameters
381 )
382 if mode_array is not None:
383 LAL_parameters = _insert_mode_array(
384 mode_array, LAL_parameters=LAL_parameters
385 )
386 (hp, hc), model = _calculate_hp_hc_fd(
387 waveform_args, delta_f, f_low, f_high, f_ref, LAL_parameters, approximant,
388 debug=True, **kwargs
389 )
390 hp = FrequencySeries(hp.data.data, df=hp.deltaF, f0=0.)
391 hc = FrequencySeries(hc.data.data, df=hc.deltaF, f0=0.)
392 if pycbc:
393 hp, hc = hp.to_pycbc(), hc.to_pycbc()
394 if flen is not None:
395 hp.resize(flen)
396 hc.resize(flen)
397 if project is None and debug:
398 return {"h_plus": hp, "h_cross": hc}, model
399 elif project is None:
400 return {"h_plus": hp, "h_cross": hc}
401 ht = _project_waveform(
402 project, hp, hc, _samples["ra"], _samples["dec"], _samples["psi"],
403 _samples["geocent_time"]
404 )
405 if debug:
406 return ht, model
407 return ht
410def _wrapper_for_td_waveform(args):
411 """Wrapper function for td_waveform for a pool of workers
413 Parameters
414 ----------
415 args: tuple
416 All args passed to td_waveform
417 """
418 return td_waveform(*args[:-1], **args[-1])
421def td_waveform(
422 samples, approximant, delta_t, f_low, f_ref=20., project=None, ind=0,
423 longAscNodes=0., eccentricity=0., LAL_parameters=None, mode_array=None,
424 pycbc=False, level=None, multi_process=1, flags={}, debug=False,
425 **kwargs
426):
427 """Generate a gravitational wave in the time domain
429 Parameters
430 ----------
431 approximant: str
432 name of the approximant to use when generating the waveform
433 delta_t: float
434 spacing between frequency samples
435 f_low: float
436 frequency to start evaluating the waveform
437 f_ref: float, optional
438 reference frequency
439 project: str, optional
440 name of the detector to project the waveform onto. If None,
441 the plus and cross polarizations are returned. Default None
442 ind: int, optional
443 index for the sample you wish to plot
444 longAscNodes: float, optional
445 longitude of ascending nodes, degenerate with the polarization
446 angle. Default 0.
447 eccentricity: float, optional
448 eccentricity at reference frequency. Default 0.
449 LAL_parameters: LALDict, optional
450 LAL dictionary containing accessory parameters. Default None
451 mode_array: 2d list
452 2d list of modes you wish to include in waveform. Must be of the form
453 [[l1, m1], [l2, m2]]
454 pycbc: Bool, optional
455 return a the waveform as a pycbc.timeseries.TimeSeries object
456 level: list, optional
457 the symmetric confidence interval of the time domain waveform. Level
458 must be greater than 0 and less than 1
459 multi_process: int, optional
460 number of cores to run on when generating waveforms. Only used when
461 level is not None,
462 flags: dict, optional
463 waveform specific flags to add to LAL dictionary. Default {}
464 kwargs: dict, optional
465 all kwargs passed to _td_waveform
466 """
467 if len(flags):
468 LAL_parameters = _insert_flags(
469 flags, LAL_parameters=LAL_parameters
470 )
471 if mode_array is not None:
472 LAL_parameters = _insert_mode_array(
473 mode_array, LAL_parameters=LAL_parameters
474 )
475 if level is not None:
476 import multiprocessing
477 from pesummary.core.plots.interpolate import Bounded_interp1d
478 td_waveform_list = []
479 _key = list(samples.keys())[0]
480 N = len(samples[_key])
481 with multiprocessing.Pool(multi_process) as pool:
482 args = np.array([
483 [samples] * N, [approximant] * N, [delta_t] * N, [f_low] * N,
484 [f_ref] * N, [project] * N, np.arange(N), [longAscNodes] * N,
485 [eccentricity] * N, [LAL_parameters] * N, [mode_array] * N,
486 [pycbc] * N, [None] * N, [kwargs] * N
487 ], dtype="object").T
488 td_waveform_list = list(
489 iterator(
490 pool.imap(_wrapper_for_td_waveform, args),
491 tqdm=True, logger=logger, total=N,
492 desc="Generating waveforms"
493 )
494 )
495 td_waveform_array = np.array(td_waveform_list, dtype=object)
496 _level = (1 + np.array(level)) / 2
497 if project is None:
498 mint = np.min(
499 [
500 np.min([_.times[0].value for _ in waveform.values()]) for
501 waveform in td_waveform_array
502 ]
503 )
504 maxt = np.max(
505 [
506 np.max([_.times[-1].value for _ in waveform.values()]) for
507 waveform in td_waveform_array
508 ]
509 )
510 new_t = np.arange(mint, maxt, delta_t)
511 td_waveform_array = {
512 polarization: np.array(
513 [
514 Bounded_interp1d(
515 np.array(waveform[polarization].times, dtype=np.float64),
516 waveform[polarization], xlow=mint, xhigh=maxt
517 )(new_t) for waveform in td_waveform_array
518 ]
519 ) for polarization in ["h_plus", "h_cross"]
520 }
521 else:
522 mint = np.min([_.times[0].value for _ in td_waveform_array])
523 maxt = np.max([_.times[-1].value for _ in td_waveform_array])
524 new_t = np.arange(mint, maxt, delta_t)
525 td_waveform_array = {
526 "h_t": [
527 Bounded_interp1d(
528 np.array(waveform.times, dtype=np.float64), waveform,
529 xlow=mint, xhigh=maxt
530 )(new_t) for waveform in td_waveform_array
531 ]
532 }
534 upper = {
535 polarization: np.percentile(
536 td_waveform_array[polarization], _level * 100, axis=0
537 ) for polarization in td_waveform_array.keys()
538 }
539 lower = {
540 polarization: np.percentile(
541 td_waveform_array[polarization], (1 - _level) * 100, axis=0
542 ) for polarization in td_waveform_array.keys()
543 }
544 if len(upper) == 1:
545 upper = upper["h_t"]
546 lower = lower["h_t"]
548 waveform_args, _samples = _waveform_args(
549 samples, ind=ind, longAscNodes=longAscNodes, eccentricity=eccentricity,
550 f_ref=f_ref
551 )
552 waveform, model = _td_waveform(
553 waveform_args, approximant, delta_t, f_low, f_ref, LAL_parameters,
554 _samples, pycbc=pycbc, project=project, debug=True, **kwargs
555 )
556 if level is not None and debug:
557 return waveform, upper, lower, new_t, model
558 elif level is not None:
559 return waveform, upper, lower, new_t
560 elif debug:
561 return waveform, model
562 return waveform
565def _td_waveform(
566 waveform_args, approximant, delta_t, f_low, f_ref, LAL_parameters, samples,
567 pycbc=False, project=None, debug=False, **kwargs
568):
569 """Generate a gravitational wave in the time domain
571 Parameters
572 ----------
573 waveform_args: tuple
574 args to pass to lalsimulation.SimInspiralChooseTDWaveform
575 approximant: str
576 lalsimulation approximant number to use when generating a waveform
577 delta_t: float
578 spacing between time samples
579 f_low: float
580 frequency to start evaluating the waveform
581 f_ref: float, optional
582 reference frequency
583 LAL_parameters: LALDict
584 LAL dictionary containing accessory parameters. Default None
585 samples: dict
586 dictionary of posterior samples to use when projecting the waveform
587 onto a given detector
588 pycbc: Bool, optional
589 return a the waveform as a pycbc.timeseries.TimeSeries object
590 project: str, optional
591 name of the detector to project the waveform onto. If None,
592 the plus and cross polarizations are returned. Default None
593 debug: bool, optional
594 if True, return the model object used to generate the waveform. Only
595 used when the waveform approximant is not in LALSimulation
596 kwargs: dict, optional
597 all kwargs passed to _calculate_hp_hc_td
598 """
599 from gwpy.timeseries import TimeSeries
600 from astropy.units import Quantity
602 (hp, hc), model = _calculate_hp_hc_td(
603 waveform_args, delta_t, f_low, f_ref, LAL_parameters, approximant,
604 debug=True, **kwargs
605 )
606 hp = TimeSeries(hp.data.data, dt=hp.deltaT, t0=hp.epoch)
607 hc = TimeSeries(hc.data.data, dt=hc.deltaT, t0=hc.epoch)
608 if pycbc:
609 hp, hc = hp.to_pycbc(), hc.to_pycbc()
610 if project is None and debug:
611 return {"h_plus": hp, "h_cross": hc}, model
612 elif project is None:
613 return {"h_plus": hp, "h_cross": hc}
614 ht = _project_waveform(
615 project, hp, hc, samples["ra"], samples["dec"], samples["psi"],
616 samples["geocent_time"]
617 )
618 if "{}_time".format(project) not in samples.keys():
619 from pesummary.gw.conversions import time_in_each_ifo
620 try:
621 _detector_time = time_in_each_ifo(
622 project, samples["ra"], samples["dec"], samples["geocent_time"]
623 )
624 except Exception:
625 logger.warning(
626 "Unable to calculate samples for '{}_time' using the provided "
627 "posterior samples. Unable to shift merger to merger time in "
628 "the detector".format(project)
629 )
630 if debug:
631 return ht, model
632 return ht
633 else:
634 _detector_time = samples["{}_time".format(project)]
635 ht.times = (
636 Quantity(ht.times, unit="s") + Quantity(_detector_time, unit="s")
637 )
638 if debug:
639 return ht, model
640 return ht
643def _calculate_hp_hc_td(
644 waveform_args, delta_t, f_low, f_ref, LAL_parameters, approximant,
645 debug=False, **kwargs
646):
647 """Calculate the plus and cross polarizations in the time domain.
648 If the approximant is in LALSimulation, the
649 SimInspiralChooseTDWaveform function is used. If the approximant is
650 not in LALSimulation, gwsignal is used
652 Parameters
653 ----------
654 waveform_args: tuple
655 args to pass to lalsimulation.SimInspiralChooseTDWaveform
656 delta_t: float
657 spacing between time samples
658 f_low: float
659 frequency to start evaluating the waveform
660 f_ref: float, optional
661 reference frequency
662 LAL_parameters: LALDict
663 LAL dictionary containing accessory parameters. Default None
664 approximant: str
665 lalsimulation approximant number to use when generating a waveform
666 debug: bool, optional
667 if True, return the model object used to generate the waveform. Only
668 used when the waveform approximant is not in LALSimulation
669 **kwargs: dict, optional
670 all kwargs passed to _setup_gwsignal
671 """
672 if hasattr(lalsim, approximant):
673 approx = _lal_approximant_from_string(approximant)
674 if lalsim.SimInspiralImplementedFDApproximants(approx):
675 # if the waveform is defined in the frequency domain, use
676 # SimInspiralTD as it appropiately conditions the waveform so it
677 # is correct at f_low in the time domain
678 _func = lalsim.SimInspiralTD
679 else:
680 # if the waveform is defined in the time domain, use
681 # SimInspiralChooseTDWaveform as it doesnt taper the start of the
682 # waveform
683 _func = lalsim.SimInspiralChooseTDWaveform
684 result = _func(
685 *waveform_args, delta_t, f_low, f_ref, LAL_parameters, approx
686 )
687 if debug:
688 return result, None
689 return result
690 wfm_gen = _setup_gwsignal(
691 waveform_args, f_low, f_ref, approximant, delta_t=delta_t, **kwargs
692 )
693 hpc = wfm_gen.generate_td_polarizations()
694 if debug:
695 return hpc, wfm_gen
696 return hpc
699def _calculate_hp_hc_fd(
700 waveform_args, delta_f, f_low, f_high, f_ref, LAL_parameters, approximant,
701 debug=False, **kwargs
702):
703 """Calculate the plus and cross polarizations in the frequency domain.
704 If the approximant is in LALSimulation, the
705 SimInspiralChooseFDWaveform function is used. If the approximant is
706 not in LALSimulation, gwsignal is used
708 Parameters
709 ----------
710 waveform_args: tuple
711 args to pass to lalsimulation.SimInspiralChooseFDWaveform
712 delta_f: float
713 spacing between frequency samples
714 f_low: float
715 frequency to start evaluating the waveform
716 f_high: float, optional
717 frequency to stop evaluating the waveform
718 f_ref: float, optional
719 reference frequency
720 LAL_parameters: LALDict
721 LAL dictionary containing accessory parameters. Default None
722 approximant: str
723 lalsimulation approximant number to use when generating a waveform
724 debug: bool, optional
725 if True, return the model object used to generate the waveform. Only
726 used when the waveform approximant is not in LALSimulation
727 """
728 if hasattr(lalsim, approximant):
729 approx = _lal_approximant_from_string(approximant)
730 for func in [lalsim.SimInspiralChooseFDWaveform, lalsim.SimInspiralFD]:
731 try:
732 result = func(
733 *waveform_args, delta_f, f_low, f_high, f_ref,
734 LAL_parameters, approx
735 )
736 if debug:
737 return result, None
738 return result
739 except Exception:
740 continue
741 break
742 wfm_gen = _setup_gwsignal(
743 waveform_args, f_low, f_ref, approximant, delta_f=delta_f,
744 f_high=f_high, **kwargs
745 )
746 hpc = wfm_gen.generate_fd_polarizations()
747 if debug:
748 return hpc, wfm_gen
749 return hpc
752def _setup_gwsignal(
753 waveform_args, f_low, f_ref, approximant, delta_t=None, delta_f=None,
754 f_high=None, **kwargs
755):
756 """Setup a waveform generator with gwsignal
758 Parameters
759 ----------
760 waveform_args: tuple
761 args to pass to lalsimulation.SimInspiralChoose*DWaveform
762 f_low: float
763 frequency to start evaluating the waveform
764 f_ref: float, optional
765 reference frequency
766 approximant: str
767 lalsimulation approximant number to use when generating a waveform
768 delta_t: float, optional
769 spacing between frequency samples
770 delta_f: float, optional
771 spacing between frequency samples
772 f_high: float, optional
773 frequency to stop evaluating the waveform
774 kwargs: dict, optional
775 all kwargs passed to _gwsignal_generator_from_string
776 """
777 from lalsimulation.gwsignal.core.parameter_conventions import (
778 common_units_dictionary
779 )
780 from astropy import units
781 names = [
782 "mass1", "mass2", "spin1x", "spin1y", "spin1z", "spin2x", "spin2y",
783 "spin2z", "distance", "inclination", "phi_ref", "longAscNodes",
784 "eccentricity", "meanPerAno"
785 ]
786 params_dict = {name: waveform_args[ii] for ii, name in enumerate(names)}
787 if delta_t is not None:
788 params_dict["deltaT"] = delta_t
789 if delta_f is not None:
790 params_dict["deltaF"] = delta_f
791 params_dict["f22_start"] = f_low
792 params_dict["f22_ref"] = f_ref
793 if f_high is None:
794 params_dict["f_max"] = 0.5 / params_dict["deltaT"]
795 else:
796 params_dict["f_max"] = f_high
797 wf_gen = _gwsignal_generator_from_string(approximant, **kwargs)
798 for key, item in params_dict.items():
799 params_dict[key] = item * common_units_dictionary.get(key, 1)
800 params_dict["mass1"] *= units.kg
801 params_dict["mass2"] *= units.kg
802 params_dict["distance"] *= units.m
803 wfm_gen = wf_gen._generate_waveform_class(**params_dict)
804 return wfm_gen