Skip to content

Commit

Permalink
set ecc to zero for failures t/f > tmax/fmax
Browse files Browse the repository at this point in the history
  • Loading branch information
md-arif-shaikh committed Oct 24, 2023
1 parent a95e3f3 commit b392392
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 0 deletions.
55 changes: 55 additions & 0 deletions gw_eccentricity/eccDefinition.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import numpy as np
import matplotlib.pyplot as plt
import warnings
from .utils import peak_time_via_quadratic_fit, check_kwargs_and_set_defaults
from .utils import amplitude_using_all_modes
from .utils import time_deriv_4thOrder
Expand Down Expand Up @@ -1407,6 +1408,24 @@ def measure_ecc(self, tref_in=None, fref_in=None):
# get the tref_in and fref_out from fref_in
self.tref_in, self.fref_out \
= self.compute_tref_in_and_fref_out_from_fref_in(self.fref_in)
# As eccentricity naturally decays over time, some methods may
# encounter challenges when attempting to identify
# pericenters/apocenters at later times, i.e., at higher
# frequencies. This is because the oscillations induced by
# eccentricity become progressively smaller and may be difficult to
# detect. Depending on the initial eccentricity, the 'fref_max'
# (the maximum allowed frequency where eccentricity can be
# measured) could be considerably smaller than the frequency at the
# time of merger, making it impractical to measure eccentricity
# using these methods for frequencies greater than 'fref_max.' In
# such scenarios, if all values in 'fref_in' are greater than
# 'fref_max,' the 'fref_out' list will be empty. If the
# 'set_failures_to_zero' flag is set to True, eccentricity and mean
# anomaly will be set to zero in these cases.
if len(self.fref_out) == 0 and self.set_failures_to_zero:
self.raise_eccentricity_decay_warning()
return self.set_eccentricity_and_mean_anomaly_to_zero()

# We measure eccentricity and mean anomaly from tmin to tmax.
self.tref_out = self.tref_in[
np.logical_and(self.tref_in <= self.tmax,
Expand All @@ -1427,6 +1446,18 @@ def measure_ecc(self, tref_in=None, fref_in=None):

# Check if tref_out is reasonable
if len(self.tref_out) == 0:
# Because eccentricity decays with time, certain methods may
# struggle to identify pericenters/apocenters for later times,
# where the oscillations caused by eccentricity become too small to
# detect. Depending on the initial eccentricity, `tmax` may be
# significantly distant from the merger, rendering it impossible to
# measure eccentricity with these methods for times greater than
# `tmax`. In such scenarios, if all values in 'tref_in' >
# `tmax`, `tref_out` will be empty. If the `set_failures_to_zero`
# flag is True, eccentricity will be set to zero for such cases.
if all(self.tref_in > self.tmax) and self.set_failures_to_zero:
self.raise_eccentricity_decay_warning()
return self.set_eccentricity_and_mean_anomaly_to_zero()
self.check_input_limits(self.tref_in, self.tmin, self.tmax)
raise Exception(
"tref_out is empty. This can happen if the "
Expand Down Expand Up @@ -1489,6 +1520,17 @@ def set_eccentricity_and_mean_anomaly_to_zero(self):
self.mean_anomaly = np.zeros(out_len)
return self.make_return_dict_for_eccentricity_and_mean_anomaly()

def raise_eccentricity_decay_warning(self):
"""Raise warning about setting eccentricity to zero due to eccentricity
decay.
"""
max_val = self.tmax if self.domain == "time" else self.fref_max
warnings.warn(
f"The reference {self.domain} is/are greater than the maximum "
f"allowed {self.domain} = {max_val}. Since `set_failures_to_zero` "
f"is {self.set_failures_to_zero}, eccentricity and mean anomaly "
"are set to zero.")

def make_return_dict_for_eccentricity_and_mean_anomaly(self):
"""Prepare a dictionary with reference time/freq, ecc, and mean anomaly.
Expand Down Expand Up @@ -2308,6 +2350,12 @@ def compute_tref_in_and_fref_out_from_fref_in(self, fref_in):
# fref_out
fref_out = self.get_fref_out(fref_in, method)

# In some cases when `set_failures_to_zero` is True, `fref_out`
# could be empty. Just return it without doing any of the steps
# below
if len(fref_out) == 0:
return fref_out, fref_out

# Now that we have fref_out, we want to know the corresponding
# tref_in such that omega22_average(tref_in) = fref_out * 2 * pi
# This is done by first creating an interpolant of time as function
Expand Down Expand Up @@ -2389,6 +2437,13 @@ def get_fref_out(self, fref_in, method):
np.logical_and(fref_in >= fref_min,
fref_in < fref_max)]
if len(fref_out) == 0:
# If all `fref_in` are greater than `fref_max` and
# `set_failures_to_zero` is `True` then just return the empty
# `fref_out` and we set eccentricity to zero for these `fref_in`
if all(fref_in > fref_max) and self.set_failures_to_zero:
# need fref_max for warnings
self.fref_max = fref_max
return fref_out
self.check_input_limits(fref_in, fref_min, fref_max)
raise Exception("fref_out is empty. This can happen if the "
"waveform has insufficient identifiable "
Expand Down
66 changes: 66 additions & 0 deletions test/test_set_failures_to_zero.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,69 @@ def test_set_failures_to_zero():
np.testing.assert_allclose(
meanano_ref, 0.0 if ref == "scalar"
else np.zeros(len(fref_in[ref])))


def test_set_failures_to_zero_for_decayed_eccentricity():
"""Test that the interface works with set_failures_to_zero for tref/fref
greater than the tmax/fmax.
In certain situations, the maximum allowed time `tmax` or the maximum
allowed frequency `fmax` could be much smaller compared to the merger
time/frequency. Therefore, measurement of eccentricity closer to merger,
i.e., tref_in > tmax or fref_in > fmax would fail.
In cases where such a situation occurs, and if the user has
set 'set_failures_to_zero' to `True` in the 'extra_kwargs' parameter, both
the eccentricity and mean anomaly will be set to zero.
"""
# We will use an EccentricTD waveform such that the Amplitude/Frequency
# method can find a few initial pericenetrs/apoceneters but not all of them
# so that tmax/fmax will be much earlier than the merger time/frequency.
lal_kwargs = {"approximant": "EccentricTD",
"q": 1.0,
"chi1": [0.0, 0.0, 0.0],
"chi2": [0.0, 0.0, 0.0],
"Momega0": 0.01,
"ecc": 0.01,
"mean_ano": 0.0,
"include_zero_ecc": True}
# We want to test it with both a single reference point
# as well as an array of reference points
tref_in = {"scalar": -2000.0,
"array": np.arange(-2000.0, 0.)}
fref_in = {"scalar": 0.01,
"array": np.arange(0.01, 0.02, 0.005)}
dataDict = load_data.load_waveform(**lal_kwargs)
extra_kwargs = {"set_failures_to_zero": True}
for method in ["Amplitude", "Frequency"]:
for ref in tref_in:
gwecc_dict = measure_eccentricity(
tref_in=tref_in[ref],
method=method,
dataDict=dataDict,
extra_kwargs=extra_kwargs)
tref_out = gwecc_dict["tref_out"]
ecc_ref = gwecc_dict["eccentricity"]
meanano_ref = gwecc_dict["mean_anomaly"]
np.testing.assert_allclose(
ecc_ref, 0.0 if ref == "scalar"
else np.zeros(len(tref_in[ref])))
np.testing.assert_allclose(
meanano_ref, 0.0 if ref == "scalar"
else np.zeros(len(tref_in[ref])))
# test for reference frequency
for ref in fref_in:
gwecc_dict = measure_eccentricity(
fref_in=fref_in[ref],
method=method,
dataDict=dataDict,
extra_kwargs=extra_kwargs)
fref_out = gwecc_dict["fref_out"]
ecc_ref = gwecc_dict["eccentricity"]
meanano_ref = gwecc_dict["mean_anomaly"]
np.testing.assert_allclose(
ecc_ref, 0.0 if ref == "scalar"
else np.zeros(len(fref_in[ref])))
np.testing.assert_allclose(
meanano_ref, 0.0 if ref == "scalar"
else np.zeros(len(fref_in[ref])))

0 comments on commit b392392

Please sign in to comment.