@@ -158,9 +158,11 @@ class Relative(DistMarg, BaseGaussianNoise):
158158 an intrinsic parameter compatible with the chosen approximant.
159159 gammas : array of floats, optional
160160 Frequency powerlaw indices to be used in computing frequency bins.
161- epsilon : float, optional
161+ epsilon : float or 'auto' , optional
162162 Tuning parameter used in calculating the frequency bins. Lower values
163- will result in higher resolution and more bins.
163+ will result in higher resolution and more bins. If 'auto', bins are
164+ added until they resolve the waveform ratio, so that the resolution
165+ is set by the problem rather than by hand.
164166 earth_rotation: boolean, optional
165167 Default is False. If True, then vary the fp/fc polarization values
166168 as a function of frequency bin, using a predetermined PN approximation
@@ -220,6 +222,10 @@ def __init__(
220222 if self .fid_params [k ] == 'REPLACE' :
221223 self .fid_params .pop (k )
222224
225+ # the shifted data and frequency limits the bin layout is built
226+ # from, kept only until the layout has been settled on
227+ shifted , limits = {}, {}
228+
223229 for ifo in data :
224230 # store data and frequencies
225231 d0 = self .data [ifo ]
@@ -291,22 +297,83 @@ def __init__(
291297 self .h00 [ifo ] = numpy .array (curr_wav ) # * tshift
292298 data_shifted = self .data [ifo ] * numpy .conjugate (tshift )
293299
300+ shifted [ifo ] = data_shifted
301+ limits [ifo ] = (f_lo , f_hi )
302+
303+ layout = (shifted , limits , gammas , earth_rotation ,
304+ int (earth_rotation_mode ))
305+ if str (epsilon ).lower () == 'auto' :
306+ self .epsilon = self .refine_epsilon (* layout )
307+ else :
308+ self .epsilon = float (epsilon )
309+ self .setup_bin_layout (self .epsilon , * layout )
310+ self .check_bin_resolution ()
311+
312+ def setup_bin_layout (self , epsilon , shifted , limits , gammas ,
313+ earth_rotation , earth_rotation_mode ):
314+ """Place the frequency bins and compute the summary data for them.
315+ """
316+ for ifo in self .data :
294317 logging .info ("Computing frequency bins" )
318+ f_lo , f_hi = limits [ifo ]
295319 fbin_ind = setup_bins (
296320 f_full = self .f [ifo ], f_lo = f_lo , f_hi = f_hi ,
297- gammas = gammas , eps = float ( epsilon ) ,
321+ gammas = gammas , eps = epsilon ,
298322 )
299323 logging .info ("Using %s bins for this model" , len (fbin_ind ))
300324
301325 self .fedges [ifo ] = self .f [ifo ][fbin_ind ]
302326 self .edges [ifo ] = fbin_ind
303- self .init_from_frequencies (data_shifted , self .h00 , fbin_ind , ifo )
327+ self .init_from_frequencies (shifted [ ifo ] , self .h00 , fbin_ind , ifo )
304328 self .antenna_time [ifo ] = self .setup_antenna (
305329 earth_rotation ,
306- int ( earth_rotation_mode ) ,
330+ earth_rotation_mode ,
307331 self .fedges [ifo ])
308332 self .combine_layout ()
309- self .check_bin_resolution ()
333+
334+ def refine_epsilon (self , * layout , start = 1.0 , threshold = 1e-3 , smallest = 0.01 ):
335+ """Halve epsilon until the bins resolve the waveform ratio.
336+
337+ Bins are added only where the model cannot do without them, so a
338+ problem needing few bins does not pay for one that needs many.
339+ Each step costs a bin layout and a handful of sparse waveforms,
340+ which is small against the analysis that follows.
341+
342+ Parameters
343+ ----------
344+ start : float, optional
345+ The first, coarsest value to try.
346+ threshold : float, optional
347+ Stop once the interpolation error falls below this. See
348+ :py:meth:`check_bin_resolution` for what it means.
349+ smallest : float, optional
350+ Give up at this value rather than refining without end.
351+
352+ Returns
353+ -------
354+ float
355+ The epsilon that was settled on.
356+ """
357+ epsilon = start
358+ while True :
359+ self .setup_bin_layout (epsilon , * layout )
360+ # inf so that only the final value is reported to the user
361+ error = self .check_bin_resolution (threshold = numpy .inf )
362+ if error <= threshold or epsilon <= smallest :
363+ break
364+ epsilon /= 2.
365+
366+ if error > threshold :
367+ logging .warning (
368+ "Refining epsilon stopped at %.3g without resolving the "
369+ "waveform ratio, which is still off by %.3g against a "
370+ "threshold of %.3g. The fiducial waveform is probably far "
371+ "from the posterior." , epsilon , error , threshold )
372+ else :
373+ logging .info ("Chose epsilon %.3g, giving %s bins and an "
374+ "interpolation error of %.3g" , epsilon ,
375+ len (self .fedges [list (self .data )[0 ]]), error )
376+ return epsilon
310377
311378 def init_from_frequencies (self , data , h00 , fbin_ind , ifo ):
312379 bins = numpy .array (
@@ -636,6 +703,7 @@ def write_metadata(self, fp, group=None):
636703 attrs = fp [group ].attrs
637704 for p , v in self .fid_params .items ():
638705 attrs ["{}_ref" .format (p )] = v
706+ attrs ["epsilon" ] = self .epsilon
639707
640708 def interpolation_error_from_reference (self ):
641709 """ Return the largest error made by interpolating the waveform
0 commit comments