Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pycbc/waveform/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
get_fd_waveform_modes)
from pycbc.waveform.plugin import (retrieve_waveform_plugins,
add_custom_waveform,
add_custom_waveform_modes,

Check warning

Code scanning / qlty

pycbc.waveform.plugin.add_custom_waveform_modes imported but unused; consider removing, adding to __all__, or using a redundant alias Warning

pycbc.waveform.plugin.add_custom_waveform_modes imported but unused; consider removing, adding to __all__, or using a redundant alias
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
add_length_estimator)
retrieve_waveform_plugins()
51 changes: 51 additions & 0 deletions pycbc/waveform/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,49 @@
"'time' or 'frequency'".format(domain))


def add_custom_waveform_modes(approximant, function, domain, force=False):
""" Make a custom mode-by-mode waveform generator available to pycbc's
get_td_waveform_modes/get_fd_waveform_modes.

Unlike add_custom_waveform (which registers a generator returning the already-summed
(h_+, h_x) polarizations), this registers a generator returning a pair of dicts
(ulm, vlm), each mapping a mode label -> TimeSeries/FrequencySeries, following the
convention documented in pycbc.waveform.waveform_modes.get_td_waveform_modes /
get_fd_waveform_modes. The mode label is usually an (l, m) tuple, but plugins whose
modes carry an extra harmonic index (e.g. eccentric sub-harmonics; see the note in
parse_mode_array's docstring) may use longer tuples instead -- pycbc itself does not
interpret the label, it is only used as a dict key.

Parameters
----------
approximant : str
The name of the waveform
function : function
The function to generate the modes. Must accept the same keyword arguments as
get_td_waveform_modes/get_fd_waveform_modes and return (ulm, vlm) as described above.
domain : str
Either 'frequency' or 'time' to indicate the domain of the waveform.
force : bool, False
Overwrite an existing registration for this approximant/domain instead of raising.
"""
from pycbc.waveform.waveform_modes import _mode_waveform_td, _mode_waveform_fd

Check warning

Code scanning / qlty

Import block is un-sorted or un-formatted Warning

Import block is un-sorted or un-formatted
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

used = RuntimeError("Can't load plugin waveform modes generator {}, the name is"
" already in use.".format(approximant))

if domain == 'time':
if not force and (approximant in _mode_waveform_td):
raise used
_mode_waveform_td[approximant] = function
elif domain == 'frequency':
if not force and (approximant in _mode_waveform_fd):
raise used
_mode_waveform_fd[approximant] = function
else:
raise ValueError("Invalid domain ({}), should be "
"'time' or 'frequency'".format(domain))


def add_length_estimator(approximant, function):
""" Add length estimator for an approximant

Expand Down Expand Up @@ -123,6 +166,14 @@
for plugin in entry_points(group='pycbc.waveform.td'):
add_custom_waveform(plugin.name, plugin.load(), 'time')

# Check for mode-by-mode fd waveforms (feed get_fd_waveform_modes)
for plugin in entry_points(group='pycbc.waveform.fd_modes'):
add_custom_waveform_modes(plugin.name, plugin.load(), 'frequency')

# Check for mode-by-mode td waveforms (feed get_td_waveform_modes)
for plugin in entry_points(group='pycbc.waveform.td_modes'):
add_custom_waveform_modes(plugin.name, plugin.load(), 'time')

# Check for waveform length estimates
for plugin in entry_points(group='pycbc.waveform.length'):
add_length_estimator(plugin.name, plugin.load())
Expand Down
21 changes: 20 additions & 1 deletion pycbc/waveform/waveform.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ def _check_lal_pars(p):
if p['side_bands']:
lalsimulation.SimInspiralWaveformParamsInsertSideband(lal_pars, p['side_bands'])
if p['mode_array'] is not None:
# LAL's mode array only understands (l, m) pairs: it has no concept of the eccentric/sub-harmonic
# index n that some (non-LAL) plugin waveforms use to sub-divide a mode further, see parse_mode_array.
# Most LAL approximants have no eccentricity content at all (at most higher-order modes), so fail
# loudly here instead of silently dropping n or letting the "for l, m in ..." unpack raise a
# confusing ValueError.
bad = [entry for entry in p['mode_array'] if len(entry) != 2]
if bad:
raise ValueError("mode_array entries %s have more than (l, m); this LAL-based approximant only "
"supports selecting modes by (l, m), not by an additional harmonic index n. "
"Pass a plain (l, m) mode_array for this approximant." % (bad,))
ma = lalsimulation.SimInspiralCreateModeArray()
for l,m in p['mode_array']:
lalsimulation.SimInspiralModeArrayActivateMode(ma, l, m)
Expand Down Expand Up @@ -400,6 +410,14 @@ def parse_mode_array(input_params):
ints (e.g., ``[(2, 2), (3, 3), (4, 4)]``), a space-separated string giving
the modes (e.g., ``22 33 44``), or an array of ints or floats (e.g.,
``[22., 33., 44.]``.

Some (non-LAL) mode-by-mode plugin waveforms further sub-divide a mode by an integer harmonic
index n (e.g. eccentric waveforms, where a given (l, m) multipole has contributions at several
harmonics of the orbital frequency). For those, ``mode_array`` entries may instead be 3-tuples
``(l, m, n)``; these are passed through unchanged (only the string/scalar shorthand above is
restricted to plain (l, m)). Approximants that do not support this extra index (which is most of
them; at most LAL approximants support higher-order modes, not eccentric sub-harmonics) will raise
a clear error if given a 3-tuple, rather than silently ignoring n or failing an unpack.
"""
if 'mode_array' in input_params and input_params['mode_array'] is not None:
mode_array = input_params['mode_array']
Expand Down Expand Up @@ -1081,7 +1099,8 @@ def seobnrv4hm_length_in_time(**kwargs):
def get_hm_length_in_time(lor_approx, maxm_default, **kwargs):
kwargs = parse_mode_array(kwargs)
if 'mode_array' in kwargs and kwargs['mode_array'] is not None:
maxm = max(m for _, m in kwargs['mode_array'])
# entries may be (l, m) or (l, m, n) (see parse_mode_array); m is always the second element
maxm = max(entry[1] for entry in kwargs['mode_array'])
else:
maxm = maxm_default
try:
Expand Down
7 changes: 7 additions & 0 deletions pycbc/waveform/waveform_modes.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,13 @@ def get_imrphenomxh_modes(**params):
mode_array = params.pop('mode_array', None)
if mode_array is None:
mode_array = default_modes(approx)
else:
# IMRPhenomXHM has no eccentric content, so it only knows how to select modes by (l, m);
# see the note on the extra harmonic index n in parse_mode_array's docstring.
bad = [entry for entry in mode_array if len(entry) != 2]
if bad:
raise ValueError("mode_array entries %s have more than (l, m); %s only supports selecting "
"modes by (l, m), not by an additional harmonic index n."%(bad, approx))
if 'f_final' not in params:
# setting to 0 will default to ringdown frequency
params['f_final'] = 0.
Expand Down
Loading