LALInspiral 5.0.3.1-eeff03c
GeneratePPNInspiral.c
Go to the documentation of this file.
1/*
2* Copyright (C) 2007 Duncan Brown, Jolien Creighton, Teviet Creighton, John Whelan
3*
4* This program is free software; you can redistribute it and/or modify
5* it under the terms of the GNU General Public License as published by
6* the Free Software Foundation; either version 2 of the License, or
7* (at your option) any later version.
8*
9* This program is distributed in the hope that it will be useful,
10* but WITHOUT ANY WARRANTY; without even the implied warranty of
11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12* GNU General Public License for more details.
13*
14* You should have received a copy of the GNU General Public License
15* along with with program; see the file COPYING. If not, write to the
16* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17* MA 02110-1301 USA
18*/
19
20/*********************************************************************
21 * PREAMBLE *
22 *********************************************************************/
23
24#include <math.h>
25#include <lal/LALStdio.h>
26#include <lal/LALStdlib.h>
27#include <lal/LALConstants.h>
28#include <lal/Units.h>
29#include <lal/FindRoot.h>
30#include <lal/AVFactories.h>
31#include <lal/SeqFactories.h>
32#include <lal/SimulateCoherentGW.h>
33#include <lal/GeneratePPNInspiral.h>
34
35/* Define some constants used in this module. */
36#define MAXORDER 6 /* Maximum number of N and PN terms */
37#define BUFFSIZE 1024 /* Number of timesteps buffered */
38#define ACCURACY (1.0e-6) /* Accuracy of root finder */
39#define TWOTHIRDS (0.6666666667) /* 2/3 */
40#define ONEMINUSEPS (0.99999) /* Something close to 1 */
41
42/* A macro to computing the (normalized) frequency. It appears in
43 many places, including in the main loop, and I don't want the
44 overhead of a function call. The following variables are required
45 to be defined and set outside of the macro:
46
47 REAL4 c0, c1, c2, c3, c4, c5; PN frequency coefficients
48 BOOLEAN b0, b1, b2, b3, b4, b5; whether to include each PN term
49
50 The following variables must be defined outside the macro, but are
51 set inside it:
52
53 REAL4 x2, x3, x4, x5; the input x raised to power 2, 3, 4, and 5 */
54#define FREQ( f, x ) \
55do { \
56 x2 = (x)*(x); \
57 x3 = x2*(x); \
58 x4 = x3*(x); \
59 x5 = x4*(x); \
60 (f) = 0; \
61 if ( b0 ) \
62 (f) += c0; \
63 if ( b1 ) \
64 (f) += c1*(x); \
65 if ( b2 ) \
66 (f) += c2*x2; \
67 if ( b3 ) \
68 (f) += c3*x3; \
69 if ( b4 ) \
70 (f) += c4*x4; \
71 if ( b5 ) \
72 (f) += c5*x5; \
73 (f) *= x3; \
74} while (0)
75
76/* Definition of a data structure used by FreqDiff() below. */
77typedef struct tagFreqDiffParamStruc {
78 REAL4 *c; /* PN coefficients of frequency series */
79 BOOLEAN *b; /* whether to include each PN term */
80 REAL4 y0; /* normalized frequency being sought */
82
83/* A function to compute the difference between the current and
84 requested normalized frequency, used by the root bisector. */
85static void
86FreqDiff( LALStatus *stat, REAL4 *y, REAL4 x, void *p )
87{
88 FreqDiffParamStruc *par; /* *p cast to its proper type */
89 REAL4 c0, c1, c2, c3, c4, c5; /* PN frequency coefficients */
90 BOOLEAN b0, b1, b2, b3, b4, b5; /* whether each order is nonzero */
91 REAL4 x2, x3, x4, x5; /* x^2, x^3, x^4, and x^5 */
92
93 INITSTATUS(stat);
94 ASSERT( p, stat, 1, "Null pointer" );
95
96 /* Set constants used by FREQ() macro. */
97 par = (FreqDiffParamStruc *)( p );
98 c0 = par->c[0];
99 c1 = par->c[1];
100 c2 = par->c[2];
101 c3 = par->c[3];
102 c4 = par->c[4];
103 c5 = par->c[5];
104 b0 = par->b[0];
105 b1 = par->b[1];
106 b2 = par->b[2];
107 b3 = par->b[3];
108 b4 = par->b[4];
109 b5 = par->b[5];
110
111 /* Evaluate frequency and compare with reference. */
112 FREQ( *y, x );
113 *y -= par->y0;
114 RETURN( stat );
115}
116
117/* Definition of a data buffer list for storing the waveform. */
118typedef struct tagPPNInspiralBuffer {
119 REAL4 a[2*BUFFSIZE]; /* amplitude data */
120 REAL8 phi[BUFFSIZE]; /* phase data */
121 REAL4 f[BUFFSIZE]; /* frequency data */
122 struct tagPPNInspiralBuffer *next; /* next buffer in list */
124
125/* Definition of a macro to free the tail of said list, from a given
126 node onward. */
127#define FREELIST( node ) \
128do { \
129 PPNInspiralBuffer *herePtr = (node); \
130 while ( herePtr ) { \
131 PPNInspiralBuffer *lastPtr = herePtr; \
132 herePtr = herePtr->next; \
133 LALFree( lastPtr ); \
134 } \
135} while (0)
136
137
138/*********************************************************************
139 * MAIN FUNCTION *
140 *********************************************************************/
141
142/**
143 * \author Creighton, T. D.
144 *
145 * \brief Computes a parametrized post-Newtonian inspiral waveform.
146 *
147 * ### Description ###
148 *
149 * This function computes an inspiral waveform using the parameters in
150 * <tt>*params</tt>, storing the result in <tt>*output</tt>.
151 *
152 * In the <tt>*params</tt> structure, the routine uses all the "input"
153 * fields specified in \ref GeneratePPNInspiral.h, and sets all of the
154 * "output" fields. If <tt>params->ppn=NULL</tt>, a normal
155 * post\f${}^2\f$-Newtonian waveform is generated; i.e.\ \f$p_0=1\f$, \f$p_1=0\f$,
156 * \f$p_2=1\f$, \f$p_3=1\f$, \f$p_4=1\f$, \f$p_{5+}=0\f$.
157 *
158 * In the <tt>*output</tt> structure, the field <tt>output->h</tt> is
159 * ignored, but all other pointer fields must be set to \c NULL. The
160 * function will create and allocate space for <tt>output->a</tt>,
161 * <tt>output->f</tt>, and <tt>output->phi</tt> as necessary. The
162 * <tt>output->shift</tt> field will remain set to \c NULL, as it is
163 * not required to describe a nonprecessing binary.
164 *
165 * ### Algorithm ###
166 *
167 * This function is a fairly straightforward calculation of
168 * \eqref{eq_ppn_freq}--\eqref{eq_ppn_across} in
169 * \ref GeneratePPNInspiral.h. However, there are some nontrivial
170 * issues involved, which are discussed in some depth in Secs.\ 6.4, 6.6,
171 * and 6.9.2 of \cite GRASP2000 . What follows is a brief
172 * discussion of these issues and how this routine deals with them.
173 *
174 * <tt>Computing the start time:</tt>
175 *
176 * When building a waveform for data analysis, one would generally like
177 * to start the waveform at some well-defined frequency where it first
178 * enters the band of interest; one then defines the start time of the
179 * integration by inverting \eqref{eq_ppn_freq} if
180 * \ref GeneratePPNInspiral.h. The current algorithm follows this
181 * standard approach by requiring the calling routine to specify
182 * <tt>params->fStartIn</tt>, which is then inverted to find
183 * \f$\Theta_\mathrm{start}\f$. This inversion is in fact the most
184 * algorithmically complicated part of the routine, so we will discuss it
185 * in depth.
186 *
187 * To help clarify the problem, let us rewrite the equation in
188 * dimensionless parameters \f$y=8\pi fT_\odot m_\mathrm{tot}/M_\odot\f$ and
189 * \f$x=\Theta^{-1/8}\f$:
190 * \f{equation}{
191 * \label{eq_ppn_fnorm}
192 * y = \sum_{k=0}^{5} C_k x^{k+3} \; ,
193 * \f}
194 * where:
195 * \f{eqnarray}{
196 * C_0 & = & p_0 \;,\\
197 * C_1 & = & p_1 \;,\\
198 * C_2 & = & p_2\left(\frac{743}{2688}+\frac{11}{32}\eta\right) \;,\\
199 * C_3 & = & p_3\frac{3\pi}{10} \;,\\
200 * C_4 & = & p_4\left(\frac{1855099}{14450688}+\frac{56975}{258048}\eta+
201 * \frac{371}{2048}\eta^2\right) \;,\\
202 * C_5 & = & p_5\left(\frac{7729}{21504}+\frac{3}{256}\eta\right)\pi \;.
203 * \f}
204 * We note that \f$x\f$ is a time parameter mapping the range
205 * \f$t=-\infty\rightarrow t_c\f$ to \f$x=0\rightarrow\infty\f$.
206 *
207 * In a normal post-Newtonian expansion it is possible to characterize
208 * the general behaviour of this equation quite accurately, since the
209 * values of \f$p_k\f$ are known and since \f$\eta\f$ varies only over the range
210 * \f$[0,1/4]\f$. In a parametrized post-Newtonian expansion, however, even
211 * the relative orders of magnitude of the coefficients can vary
212 * significantly, making a robust generic root finder impractical.
213 * However, we are saved by the fact that we can restrict our search to
214 * domains where the post-Newtonian expansion is a valid approximation.
215 * We define the post-Newtonian expansion \e not to be valid if
216 * \e any of the following conditions occur:
217 * <ol>
218 * <li> A higher-order term in the frequency expansion becomes larger in
219 * magnitude than the leading (lowest-order nonzero) term.</li>
220 * <li> The inferred orbital radius, approximated by
221 * \f$r\sim4m_\mathrm{tot}\Theta^{1/4}\f$, drops below \f$2m_\mathrm{tot}\f$;
222 * i.e.\ \f$\Theta<1/16\f$ or \f$x>\sqrt{2}\f$.</li>
223 * <li> The frequency evolution becomes non-monotonic.</li>
224 * </ol>
225 * We can further require as a matter of convention that the lowest-order
226 * nonzero coefficient in the frequency expansion be positive; this is
227 * simply a sign convention stating that the frequency of a system be
228 * positive at large radii.
229 *
230 * The first two conditions above allow us to set firm limits on the
231 * range of the initial \f$x_\mathrm{start}\f$. Let \f$C_j\f$ be the
232 * lowest-order nonzero coefficient; then for every nonzero \f$C_{k>j}\f$
233 * we can define a point \f$x_k=|C_j/C_k|^{1/(k-j)}\f$ where that term
234 * exceeds the leading-order term in magnitude. We can therefore limit
235 * the range of \f$x\f$ to values less than \f$x_\mathrm{max}\f$, which is the
236 * minimum of \f$\sqrt{2}\f$ and all \f$x_k\f$. We note that even if we were
237 * to extend the post-Newtonian expansion in \eqref{eq_ppn_fnorm} to an
238 * infinite number of terms, this definition of \f$x_\mathrm{max}\f$ implies
239 * that the frequency is guaranteed to be monotonic up to
240 * \f$x_\mathrm{max}(5-\sqrt{7})/6\f$, and positive up to \f$x_\mathrm{max}/2\f$.
241 * Thus we can confidently begin our search for \f$x_\mathrm{start}\f$ in the
242 * domain \f$(0,0.39x_\mathrm{max})\f$, where the leading-order term
243 * dominates, and end it if we ever exceed \f$x_\mathrm{max}\f$.
244 *
245 * We therefore bracket our value of \f$x_\mathrm{start}\f$ as follows: We
246 * start with an initial guess
247 * \f$x_\mathrm{guess}=(y_\mathrm{start}/C_j)^{1/(j+3)}\f$, or
248 * \f$0.39x_\mathrm{max}\f$, whichever is less. If
249 * \f$y(x_\mathrm{guess})>y_\mathrm{start}\f$, we iteratively decrease \f$x\f$ by
250 * factors of 0.95 until \f$y(x)<y_\mathrm{start}\f$; this is guaranteed to
251 * occur within a few iterations, since we are moving into a regime where
252 * the leading-order behaviour dominates more and more. If
253 * \f$y(x_\mathrm{guess})<y_\mathrm{start}\f$, we iteratively increase \f$x\f$ by
254 * factors of 1.05 until \f$y(x)>y_\mathrm{start}\f$, or until
255 * \f$x>x_\mathrm{max}\f$; this is also guaranteed to occur quickly because,
256 * in the worst case, it only takes about 20 iterations to step from
257 * \f$0.39x_\mathrm{max}\f$ to \f$x_\mathrm{max}\f$, and if \f$x_\mathrm{guess}\f$
258 * were much lower than \f$0.39x_\mathrm{max}\f$ it would have been a pretty
259 * good guess to begin with. If at any point while increasing \f$x\f$ we
260 * find that \f$y\f$ is decreasing, we determine that the starting frequency
261 * is already in a regime where the post-Newtonian approximation is
262 * invalid, and we return an error. Otherwise, once we have bracketed
263 * the value of \f$x_\mathrm{start}\f$, we use <tt>LALSBisectionFindRoot()</tt>
264 * to pin down the value to an accuracy of a part in \f$10^6\f$.
265 *
266 * <tt>Computing the phase and amplitudes:</tt>
267 *
268 * Once we have \f$x_\mathrm{start}\f$, we can find \f$\Theta_\mathrm{start}\f$,
269 * and begin incrementing it; at each timestep we compute \f$x\f$ and hence
270 * \f$f\f$, \f$\phi\f$, \f$A_+\f$, and \f$A_\times\f$ according to
271 * \eqref{eq_ppn_freq}, \eqref{eq_ppn_phi}, \eqref{eq_ppn_aplus},
272 * and \eqref{eq_ppn_across}. The routine progressively creates a list of
273 * length-1024 arrays and fills them. The process stops when any of the
274 * following occurs:
275 * <ol>
276 * <li> The frequency exceeds the requested termination frequency.</li>
277 * <li> The number of steps reaches the suggested maximum length in
278 * <tt>*params</tt>.</li>
279 * <li> The frequency is no longer increasing.</li>
280 * <li> The parameter \f$x>x_\mathrm{max}\f$.</li>
281 * <li> We run out of memory.</li>
282 * </ol>
283 * In the last case an error is returned; otherwise the waveform is
284 * deemed "complete". Output arrays are created of the appropriate
285 * length and are filled with the data.
286 *
287 * Internally, the routine keeps a list of all coefficients, as well as a
288 * list of booleans indicating which terms are nonzero. The latter
289 * allows the code to avoid lengthy floating-point operations (especially
290 * the logarithm in the post\f${}^{5/2}\f$-Newtonian phase term) when these
291 * are not required.
292 *
293 * When generating the waveform, we note that the sign of \f$\dot{f}\f$ is
294 * the same as the sign of \f$y'/x^2 = \sum (k+3)C_k x^k\f$, and use this
295 * series to test whether the frequency has stopped increasing. (The
296 * reason is that for waveforms far from coalescence \f$f\f$ is nearly
297 * constant: numerical errors can cause positive <em>and negative</em>
298 * fluctuations \f$\Delta f\f$ bewteen timesteps. The analytic formulae for
299 * \f$\dot{f}\f$ or \f$y'\f$ are less susceptible to this.) The coefficients
300 * \f$(k+3)C_k\f$ are also precomputed for added efficiency.
301 *
302 * <tt>Warnings and suggestions:</tt>
303 *
304 * If no post-Newtonian parameters are provided (i.e.\
305 * <tt>params->ppn=NULL</tt>), we generate a post\f${}^2\f$-Newtonian waveform,
306 * \e not a post\f${}^{5/2}\f$-Newtonian waveform. This is done not only
307 * for computationally efficiency, but also because the accuracy and
308 * reliability of the post\f${}^{5/2}\f$-Newtonian waveform is actually
309 * worse. You can of course specify a post\f${}^{5/2}\f$-Newtonian waveform
310 * with an appropriate assignment of <tt>params->ppn</tt>, but you do so at
311 * your own risk!
312 *
313 * This routine also performs no sanity checking on the requested
314 * sampling interval \f$\Delta t=\f$<tt>params->deltaT</tt>, because this
315 * depends very much on how one intends to use the generated waveform.
316 * If you plan to generate actual wave functions \f$h_{+,\times}(t)\f$ at the
317 * same sample rate, then you will generally want a sampling interval
318 * \f$\Delta t<1/2f_\mathrm{max}\f$; you can enforce this by specifying a
319 * suitable <tt>params->fStopIn</tt>.
320 *
321 * However, many routines (such as those in \ref SimulateCoherentGW.h)
322 * generate actual wave functions by linear interpolation of the
323 * amplitude and phase data, which then need only be sampled on
324 * timescales \f$\sim\dot{f}^{-1/2}\f$ rather than \f$\sim f^{-1}\f$. More
325 * precisely, we would like our interpolated phase to differ from the
326 * actual phase by no more than some specified amount, say \f$\pi/2\f$
327 * radians. The largest deviation from linear phase evolution will
328 * typically be on the order of \f$\Delta\phi\approx(1/2)\ddot{\phi}(\Delta
329 * t/2)^2\approx(\pi/4)\Delta f\Delta t\f$, where \f$\Delta f\f$ is the
330 * frequency shift over the timestep. Thus in general we would like to
331 * have
332 * \f[
333 * \Delta f \Delta t \lesssim 2
334 * \f]
335 * for our linear interpolation to be valid. This routine helps out by
336 * setting the output parameter field <tt>params->dfdt</tt> equal to the
337 * maximum value of \f$\Delta f\Delta t\f$ encountered during the
338 * integration.
339 */
340void
341LALGeneratePPNInspiral( LALStatus *stat, /**< UNDOCUMENTED */
342 CoherentGW *output, /**< UNDOCUMENTED */
343 PPNParamStruc *params /**< UNDOCUMENTED */
344 )
345{
346
347 /* System-derived constants. */
348 BOOLEAN b0, b1, b2, b3, b4, b5; /* whether each order is nonzero */
349 BOOLEAN b[MAXORDER]; /* vector of above coefficients */
350 REAL4 c0, c1, c2, c3, c4, c5; /* PN frequency coefficients */
351 REAL4 c[MAXORDER]; /* vector of above coefficients */
352 REAL4 d0, d1, d2, d3, d4, d5; /* PN phase coefficients */
353 REAL4 e0, e1, e2, e3, e4, e5; /* PN dy/dx coefficients */
354 REAL4 p[MAXORDER]; /* PN parameter values */
355 REAL4 mTot, mu; /* total mass and reduced mass */
356 REAL4 eta, etaInv; /* mass ratio and its inverse */
357 REAL4 phiC; /* phase at coalescence */
358 REAL4 cosI; /* cosine of system inclination */
359 REAL4 fFac; /* SI normalization for f and t */
360 REAL4 f2aFac; /* factor multiplying f in amplitude function */
361 REAL4 apFac, acFac; /* extra factor in plus and cross amplitudes */
362
363 /* Integration parameters. */
364 UINT4 i; /* index over PN terms */
365 UINT4 j; /* index of leading nonzero PN term */
366 UINT4 n, nMax; /* index over timesteps, and its maximum + 1 */
367 UINT4 nNext; /* index where next buffer starts */
368 REAL8 t, t0, dt; /* dimensionless time, start time, and increment */
369 REAL4 tStop = 0.0625; /* time when orbit reaches minimum radius */
370 REAL4 x, xStart, xMax; /* x = t^(-1/8), and its maximum range */
371 REAL4 y, yStart, yMax; /* normalized frequency and its range */
372 REAL4 yOld, dyMax; /* previous timestep y, and maximum y - yOld */
373 REAL4 x2, x3, x4, x5; /* x^2, x^3, x^4, and x^5 */
374 REAL4 *a, *f; /* pointers to generated amplitude and frequency data */
375 REAL8 *phi; /* pointer to generated phase data */
376 PPNInspiralBuffer *head, *here; /* pointers to buffered data */
377
378 INITSTATUS(stat);
379 ATTATCHSTATUSPTR( stat );
380
381 /*******************************************************************
382 * CHECK INPUT PARAMETERS *
383 *******************************************************************/
384
385 /* Dumb initialization to shut gcc up. */
386 head = here = NULL;
387 b0 = b1 = b2 = b3 = b4 = b5 = 0;
388 c0 = c1 = c2 = c3 = c4 = c5 = d0 = d1 = d2 = d3 = d4 = d5 = 0.0;
389
390 /* Make sure parameter and output structures exist. */
392 GENERATEPPNINSPIRALH_MSGENUL );
394 GENERATEPPNINSPIRALH_MSGENUL );
395
396 /* Make sure output fields don't exist. */
398 GENERATEPPNINSPIRALH_MSGEOUT );
400 GENERATEPPNINSPIRALH_MSGEOUT );
401 ASSERT( !( output->phi ), stat, GENERATEPPNINSPIRALH_EOUT,
402 GENERATEPPNINSPIRALH_MSGEOUT );
403 ASSERT( !( output->shift ), stat, GENERATEPPNINSPIRALH_EOUT,
404 GENERATEPPNINSPIRALH_MSGEOUT );
405
406 /* Get PN parameters, if they are specified; otherwise use
407 post2-Newtonian. */
408 if ( params->ppn ) {
409 ASSERT( params->ppn->data, stat, GENERATEPPNINSPIRALH_ENUL,
410 GENERATEPPNINSPIRALH_MSGENUL );
411 j = params->ppn->length;
412 if ( j > MAXORDER )
413 j = MAXORDER;
414 for ( i = 0; i < j; i++ )
415 p[i] = params->ppn->data[i];
416 for ( ; i < MAXORDER; i++ )
417 p[i] = 0.0;
418 } else {
419 p[0] = 1.0;
420 p[1] = 0.0;
421 p[2] = 1.0;
422 p[3] = 1.0;
423 p[4] = 1.0;
424 for ( i = 5; i < MAXORDER; i++ )
425 p[i] = 0.0;
426 }
427
428 /*******************************************************************
429 * COMPUTE SYSTEM PARAMETERS *
430 *******************************************************************/
431
432 /* Compute parameters of the system. */
433 mTot = params->mTot;
434 ASSERT( mTot != 0.0, stat, GENERATEPPNINSPIRALH_EMBAD,
435 GENERATEPPNINSPIRALH_MSGEMBAD );
436 eta = params->eta;
437 ASSERT( eta != 0.0, stat, GENERATEPPNINSPIRALH_EMBAD,
438 GENERATEPPNINSPIRALH_MSGEMBAD );
439 etaInv = 2.0 / eta;
440 mu = eta*mTot;
441 cosI = cos( params->inc );
442 phiC = params->phi;
443
444 /* Compute frequency, phase, and amplitude factors. */
445 fFac = 1.0 / ( 4.0*LAL_TWOPI*LAL_MTSUN_SI*mTot );
446 dt = -params->deltaT * eta / ( 5.0*LAL_MTSUN_SI*mTot );
448 GENERATEPPNINSPIRALH_MSGETBAD );
449 f2aFac = LAL_PI*LAL_MTSUN_SI*mTot*fFac;
450 ASSERT( params->d != 0.0, stat, GENERATEPPNINSPIRALH_EDBAD,
451 GENERATEPPNINSPIRALH_MSGEDBAD );
452 apFac = acFac = -2.0*mu*LAL_MRSUN_SI/params->d;
453 apFac *= 1.0 + cosI*cosI;
454 acFac *= 2.0*cosI;
455
456 /* Compute PN expansion coefficients. */
457 c0 = c[0] = p[0];
458 c1 = c[1] = p[1];
459 c2 = c[2] = p[2]*( 743.0/2688.0 + eta*11.0/32.0 );
460 c3 = c[3] = -p[3]*( 3.0*LAL_PI/10.0 );
461 c4 = c[4] = p[4]*( 1855099.0/14450688.0 + eta*56975.0/258048.0 +
462 eta*eta*371.0/2048.0 );
463 c5 = c[5] = -p[5]*( 7729.0/21504.0 + eta*3.0/256.0 )*LAL_PI;
464
465 /* Compute expansion coefficients for series in phi and dy/dx. */
466 d0 = c0;
467 d1 = c1*5.0/4.0;
468 d2 = c2*5.0/3.0;
469 d3 = c3*5.0/2.0;
470 d4 = c4*5.0;
471 d5 = c5*5.0/8.0;
472 e0 = c0*3.0;
473 e1 = c1*4.0;
474 e2 = c2*5.0;
475 e3 = c3*6.0;
476 e4 = c4*7.0;
477 e5 = c5*8.0;
478
479 /* Use Boolean variables to exclude terms that are zero. */
480 b0 = b[0] = ( c0 == 0.0 ? 0 : 1 );
481 b1 = b[1] = ( c1 == 0.0 ? 0 : 1 );
482 b2 = b[2] = ( c2 == 0.0 ? 0 : 1 );
483 b3 = b[3] = ( c3 == 0.0 ? 0 : 1 );
484 b4 = b[4] = ( c4 == 0.0 ? 0 : 1 );
485 b5 = b[5] = ( c5 == 0.0 ? 0 : 1 );
486
487 /* Find the leading-order frequency term. */
488 for ( j = 0; ( j < MAXORDER ) && ( b[j] == 0 ); j++ )
489 ;
490 if ( j == MAXORDER ) {
492 GENERATEPPNINSPIRALH_MSGEPBAD );
493 }
494
495 /*******************************************************************
496 * COMPUTE START TIME *
497 *******************************************************************/
498
499 /* First, find the normalized start frequency, and the best guess as
500 to the start time from the leading-order term. We require the
501 frequency to be increasing. */
502 yStart = params->fStartIn / fFac;
503 if ( params->fStopIn == 0.0 )
504 yMax = LAL_REAL4_MAX;
505 else {
506 ASSERT( fabs( params->fStopIn ) > params->fStartIn, stat,
507 GENERATEPPNINSPIRALH_EFBAD, GENERATEPPNINSPIRALH_MSGEFBAD );
508 yMax = fabs( params->fStopIn ) / fFac;
509 }
510 if ( ( c[j]*fFac < 0.0 ) || ( yStart < 0.0 ) || ( yMax < 0.0 ) ) {
512 GENERATEPPNINSPIRALH_MSGEPBAD );
513 }
514 xStart = pow( yStart/c[j], 1.0/( j + 3.0 ) );
515 xMax = LAL_SQRT2;
516
517 /* The above is exact if the leading-order term is the only one in
518 the expansion. Check to see if there are any other terms. */
519 for ( i = j + 1; ( i < MAXORDER ) && ( b[i] == 0 ); i++ )
520 ;
521 if ( i < MAXORDER ) {
522 /* There are other terms, so we have to use bisection to find the
523 start time. */
524 REAL4 xLow, xHigh; /* ultimately these will bracket xStart */
525 REAL4 yLow, yHigh; /* the normalized frequency at these times */
526
527 /* If necessary, revise the estimate of the cutoff where we know
528 the PN approximation goes bad, and revise our initial guess to
529 lie well within the valid regime. */
530 for ( i = j + 1; i < MAXORDER; i++ )
531 if ( b[i] != 0 ) {
532 x = pow( fabs( c[j]/c[i] ), 1.0/(REAL4)( i - j ) );
533 if ( x < xMax )
534 xMax = x;
535 }
536 if ( xStart > 0.39*xMax )
537 xStart = 0.39*xMax;
538
539 /* If we are ignoring PN breakdown, adjust xMax (so that it won't
540 interfere with the start time search) and tStop. */
541 if ( params->fStopIn < 0.0 ) {
542 xMax = LAL_REAL4_MAX;
543 tStop = 0.0;
544 }
545
546 /* If our frequency is too high, step backwards and/or forwards
547 until we have bracketed the correct frequency. */
548 xLow = xHigh = xStart;
549 FREQ( yHigh, xStart );
550 yLow = yHigh;
551 while ( yLow > yStart ) {
552 xHigh = xLow;
553 yHigh = yLow;
554 xLow *= 0.95;
555 FREQ( yLow, xLow );
556 }
557 while ( yHigh < yStart ) {
558 xLow = xHigh;
559 yLow = yHigh;
560 xHigh *= 1.05;
561 FREQ( yHigh, xHigh );
562 /* Check for PN breakdown. */
563 if ( ( yHigh < yLow ) || ( xHigh > xMax ) ) {
565 GENERATEPPNINSPIRALH_MSGEFBAD );
566 }
567 }
568
569 /* We may have gotten lucky and nailed the frequency right on.
570 Otherwise, find xStart by root bisection. */
571 if ( yLow == yStart )
572 xStart = xLow;
573 else if ( yHigh == yStart )
574 xStart = xHigh;
575 else {
576 SFindRootIn in;
578 in.xmax = xHigh;
579 in.xmin = xLow;
580 in.xacc = ACCURACY;
581 in.function = FreqDiff;
582 par.c = c;
583 par.b = b;
584 par.y0 = yStart;
585 TRY( LALSBisectionFindRoot( stat->statusPtr, &xStart, &in,
586 (void *)( &par ) ), stat );
587 }
588 }
589
590 /* If we are ignoring PN breakdown, adjust xMax and tStop, if they
591 haven't been adjusted already. */
592 else if ( params->fStopIn < 0.0 ) {
593 xMax = LAL_REAL4_MAX;
594 tStop = 0.0;
595 }
596
597 /* Compute initial dimensionless time, record actual initial
598 frequency (in case it is different), and record dimensional
599 time-to-coalescence. */
600 t0 = pow( xStart, -8.0 );
601 FREQ( yStart, xStart );
602 if ( yStart >= yMax ) {
604 GENERATEPPNINSPIRALH_MSGEFBAD );
605 }
606 params->fStart = yStart*fFac;
607 params->tc = t0 * ( 5.0*LAL_MTSUN_SI*mTot ) / eta;
608
609 /*******************************************************************
610 * GENERATE WAVEFORM *
611 *******************************************************************/
612
613 /* Set up data pointers and storage. */
614 here = head = (PPNInspiralBuffer *)
615 LALMalloc( sizeof(PPNInspiralBuffer) );
616 if ( !here ) {
618 GENERATEPPNINSPIRALH_MSGEMEM );
619 }
620 here->next = NULL;
621 a = here->a;
622 f = here->f;
623 phi = here->phi;
624 nMax = (UINT4)( -1 );
625 if ( params->lengthIn > 0 )
626 nMax = params->lengthIn;
627 nNext = BUFFSIZE;
628 if ( nNext > nMax )
629 nNext = nMax;
630
631 /* Start integrating! Inner loop exits each time a new buffer is
632 required. Outer loop has no explicit test; when a termination
633 condition is met, we jump directly from the inner loop using a
634 goto statement. All goto statements jump to the terminate: label
635 at the end of the outer loop. */
636 n = 0;
637 t = t0;
638 dyMax = 0.0;
639 y = yOld = 0.0;
640 x = xStart;
641 while ( 1 ) {
642 while ( n < nNext ) {
643 REAL4 f2a; /* value inside 2/3 power in amplitude functions */
644 REAL4 phase = 0.0; /* wave phase excluding overall constants */
645 REAL4 dydx2 = 0.0; /* dy/dx divided by x^2 */
646
647 /* Check if we're still in a valid PN regime. */
648 if ( x > xMax ) {
650 params->termDescription = GENERATEPPNINSPIRALH_MSGEPNFAIL;
651 goto terminate;
652 }
653
654 /* Compute the normalized frequency. This also computes the
655 variables x2, x3, x4, and x5, which are used later. */
656 FREQ( y, x );
657 if ( y > yMax ) {
659 params->termDescription = GENERATEPPNINSPIRALH_MSGEFSTOP;
660 goto terminate;
661 }
662
663 /* Check that frequency is still increasing. */
664 if ( b0 )
665 dydx2 += e0;
666 if ( b1 )
667 dydx2 += e1*x;
668 if ( b2 )
669 dydx2 += e2*x2;
670 if ( b3 )
671 dydx2 += e3*x3;
672 if ( b4 )
673 dydx2 += e4*x4;
674 if ( b5 )
675 dydx2 += e5*x5;
676 if ( dydx2 < 0.0 ) {
678 params->termDescription = GENERATEPPNINSPIRALH_MSGEFNOTMON;
679 goto terminate;
680 }
681 if ( y - yOld > dyMax )
682 dyMax = y - yOld;
683 *(f++) = fFac*y;
684
685 /* Compute the amplitude from the frequency. */
686 f2a = pow( f2aFac*y, TWOTHIRDS );
687 *(a++) = apFac*f2a;
688 *(a++) = acFac*f2a;
689
690 /* Compute the phase. */
691 if ( b0 )
692 phase += d0;
693 if ( b1 )
694 phase += d1*x;
695 if ( b2 )
696 phase += d2*x2;
697 if ( b3 )
698 phase += d3*x3;
699 if ( b4 )
700 phase += d4*x4;
701 if ( b5 )
702 phase += d5*log(t)*x5;
703 phase *= t*x3*etaInv;
704 *(phi++) = phiC - phase;
705
706 /* Increment the timestep. */
707 n++;
708 t = t0 + n*dt;
709 yOld = y;
710 if ( t <= tStop ) {
712 params->termDescription = GENERATEPPNINSPIRALH_MSGERTOOSMALL;
713 goto terminate;
714 }
715 x = pow( t, -0.125 );
716 }
717
718 /* We've either filled the buffer or we've exceeded the maximum
719 length. If the latter, we're done! */
720 if ( n >= nMax ) {
722 params->termDescription = GENERATEPPNINSPIRALH_MSGELENGTH;
723 goto terminate;
724 }
725
726 /* Otherwise, allocate the next buffer. */
727 here->next =
729 here = here->next;
730 if ( !here ) {
731 FREELIST( head );
733 GENERATEPPNINSPIRALH_MSGEMEM );
734 }
735 here->next = NULL;
736 a = here->a;
737 f = here->f;
738 phi = here->phi;
739 nNext += BUFFSIZE;
740 if ( nNext > nMax )
741 nNext = nMax;
742 }
743
744 /*******************************************************************
745 * CLEANUP *
746 *******************************************************************/
747
748 /* The above loop only exits by triggering one of the termination
749 conditions, which jumps to the following point for cleanup and
750 return. */
751 terminate:
752
753 /* First, set remaining output parameter fields. */
754 params->dfdt = dyMax*fFac*params->deltaT;
755 params->fStop = yOld*fFac;
756 params->length = n;
757
758 /* Allocate the output structures. */
759 if ( ( output->a = (REAL4TimeVectorSeries *)
760 LALMalloc( sizeof(REAL4TimeVectorSeries) ) ) == NULL ) {
761 FREELIST( head );
763 GENERATEPPNINSPIRALH_MSGEMEM );
764 }
765 memset( output->a, 0, sizeof(REAL4TimeVectorSeries) );
766 if ( ( output->f = (REAL4TimeSeries *)
767 LALMalloc( sizeof(REAL4TimeSeries) ) ) == NULL ) {
768 FREELIST( head );
769 LALFree( output->a ); output->a = NULL;
771 GENERATEPPNINSPIRALH_MSGEMEM );
772 }
773 memset( output->f, 0, sizeof(REAL4TimeSeries) );
774 if ( ( output->phi = (REAL8TimeSeries *)
775 LALMalloc( sizeof(REAL8TimeSeries) ) ) == NULL ) {
776 FREELIST( head );
777 LALFree( output->a ); output->a = NULL;
778 LALFree( output->f ); output->f = NULL;
780 GENERATEPPNINSPIRALH_MSGEMEM );
781 }
782 memset( output->phi, 0, sizeof(REAL8TimeSeries) );
783
784 /* Allocate the output data fields. */
785 {
787 in.length = n;
788 in.vectorLength = 2;
789 LALSCreateVectorSequence( stat->statusPtr, &( output->a->data ), &in );
790 BEGINFAIL( stat ) {
791 FREELIST( head );
792 LALFree( output->a ); output->a = NULL;
793 LALFree( output->f ); output->f = NULL;
794 LALFree( output->phi ); output->phi = NULL;
795 } ENDFAIL( stat );
796 LALSCreateVector( stat->statusPtr, &( output->f->data ), n );
797 BEGINFAIL( stat ) {
798 TRY( LALSDestroyVectorSequence( stat->statusPtr, &( output->a->data ) ),
799 stat );
800 FREELIST( head );
801 LALFree( output->a ); output->a = NULL;
802 LALFree( output->f ); output->f = NULL;
803 LALFree( output->phi ); output->phi = NULL;
804 } ENDFAIL( stat );
805 LALDCreateVector( stat->statusPtr, &( output->phi->data ), n );
806 BEGINFAIL( stat ) {
807 TRY( LALSDestroyVectorSequence( stat->statusPtr, &( output->a->data ) ),
808 stat );
809 TRY( LALSDestroyVector( stat->statusPtr, &( output->f->data ) ),
810 stat );
811 FREELIST( head );
812 LALFree( output->a ); output->a = NULL;
813 LALFree( output->f ); output->f = NULL;
814 LALFree( output->phi ); output->phi = NULL;
815 } ENDFAIL( stat );
816 }
817
818 /* Structures have been successfully allocated; now fill them. We
819 deallocate the list as we go along. */
820 output->position = params->position;
821 output->psi = params->psi;
822 output->a->epoch = output->f->epoch = output->phi->epoch
823 = params->epoch;
824 output->a->deltaT = output->f->deltaT = output->phi->deltaT
825 = params->deltaT;
826 output->a->sampleUnits = lalStrainUnit;
827 output->f->sampleUnits = lalHertzUnit;
828 output->phi->sampleUnits = lalDimensionlessUnit;
829 snprintf( output->a->name, LALNameLength, "PPN inspiral amplitudes" );
830 snprintf( output->f->name, LALNameLength, "PPN inspiral frequency" );
831 snprintf( output->phi->name, LALNameLength, "PPN inspiral phase" );
832 a = output->a->data->data;
833 f = output->f->data->data;
834 phi = output->phi->data->data;
835 here = head;
836 while ( here && ( n > 0 ) ) {
837 PPNInspiralBuffer *last = here;
838 UINT4 nCopy = BUFFSIZE;
839 if ( nCopy > n )
840 nCopy = n;
841 memcpy( a, here->a, 2*nCopy*sizeof(REAL4) );
842 memcpy( f, here->f, nCopy*sizeof(REAL4) );
843 memcpy( phi, here->phi, nCopy*sizeof(REAL8) );
844 a += 2*nCopy;
845 f += nCopy;
846 phi += nCopy;
847 n -= nCopy;
848 here = here->next;
849 LALFree( last );
850 }
851
852 /* This shouldn't happen, but free any extra buffers in the
853 list. */
854 FREELIST( here );
855
856 /* Everything's been stored and cleaned up, so there's nothing left
857 to do but quit! */
858 DETATCHSTATUSPTR( stat );
859 RETURN( stat );
860}
#define FREELIST(node)
#define BUFFSIZE
#define FREQ(f, x)
#define MAXORDER
#define TWOTHIRDS
#define ACCURACY
static void FreqDiff(LALStatus *stat, REAL4 *y, REAL4 x, void *p)
#define LALMalloc(n)
#define LALFree(p)
const double b1
const double c1
const double b2
const double b0
const double c2
const double c0
#define ABORT(statusptr, code, mesg)
#define ENDFAIL(statusptr)
#define TRY(func, statusptr)
#define ATTATCHSTATUSPTR(statusptr)
#define ASSERT(assertion, statusptr, code, mesg)
#define DETATCHSTATUSPTR(statusptr)
#define INITSTATUS(statusptr)
#define RETURN(statusptr)
#define BEGINFAIL(statusptr)
double i
void LALSDestroyVectorSequence(LALStatus *status, REAL4VectorSequence **vectorSequence)
void LALSCreateVectorSequence(LALStatus *status, REAL4VectorSequence **vectorSequence, CreateVectorSequenceIn *vSeqParams)
void LALSBisectionFindRoot(LALStatus *status, REAL4 *root, SFindRootIn *input, void *params)
#define GENERATEPPNINSPIRALH_EMEM
Out of memory.
#define GENERATEPPNINSPIRALH_EFNOTMON
Frequency no longer increasing monotonically.
#define GENERATEPPNINSPIRALH_EDBAD
Bad distance.
#define GENERATEPPNINSPIRALH_EOUT
output field a, f, phi, or shift already exists
void LALGeneratePPNInspiral(LALStatus *stat, CoherentGW *output, PPNParamStruc *params)
Computes a parametrized post-Newtonian inspiral waveform.
#define GENERATEPPNINSPIRALH_ERTOOSMALL
Orbital radius too small for PN approximation.
#define GENERATEPPNINSPIRALH_EMBAD
Bad masses.
#define GENERATEPPNINSPIRALH_EFSTOP
Reached requested termination frequency.
#define GENERATEPPNINSPIRALH_EPNFAIL
Evolution dominated by higher-order PN terms.
#define GENERATEPPNINSPIRALH_ENUL
Unexpected null pointer in arguments.
#define GENERATEPPNINSPIRALH_EPBAD
Bad post-Newtonian parameters.
#define GENERATEPPNINSPIRALH_EFBAD
Bad starting frequency; could not get valid start time.
#define GENERATEPPNINSPIRALH_ELENGTH
Reached maximum length, or end of provided time series vector.
#define GENERATEPPNINSPIRALH_ETBAD
Bad sampling interval.
#define LAL_REAL4_MAX
#define LAL_PI
#define LAL_TWOPI
#define LAL_MTSUN_SI
#define LAL_SQRT2
#define LAL_MRSUN_SI
unsigned char BOOLEAN
double REAL8
uint32_t UINT4
float REAL4
LALNameLength
static const INT4 a
const LALUnit lalStrainUnit
const LALUnit lalHertzUnit
const LALUnit lalDimensionlessUnit
void LALDCreateVector(LALStatus *, REAL8Vector **, UINT4)
void LALSDestroyVector(LALStatus *, REAL4Vector **)
void LALSCreateVector(LALStatus *, REAL4Vector **, UINT4)
list y
list mu
c
int n
p
x
double t0
struct tagLALStatus * statusPtr
struct tagPPNInspiralBuffer * next
This structure stores the parameters for constructing a restricted post-Newtonian waveform.
void(* function)(LALStatus *s, REAL4 *y, REAL4 x, void *p)
LIGOTimeGPS epoch
double psi
char output[FILENAME_MAX]