Processing math: 100%
LALPulsar 7.1.1.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
GenerateHyperbolicSpinOrbitCW.c
Go to the documentation of this file.
1/*
2* Copyright (C) 2007 Teviet Creighton
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#include <lal/LALStdio.h>
22#include <lal/LALStdlib.h>
23#include <lal/LALConstants.h>
24#include <lal/Units.h>
25#include <lal/AVFactories.h>
26#include <lal/SeqFactories.h>
27#include <lal/PulsarSimulateCoherentGW.h>
28#include <lal/GenerateSpinOrbitCW.h>
29
30/**
31 * \author Creighton, T. D.
32 *
33 * \brief Computes a continuous waveform with frequency drift and Doppler
34 * modulation from a hyperbolic orbital trajectory.
35 *
36 * This function computes a quaiperiodic waveform using the spindown and
37 * orbital parameters in <tt>*params</tt>, storing the result in
38 * <tt>*output</tt>.
39 *
40 * In the <tt>*params</tt> structure, the routine uses all the "input"
41 * fields specified in \ref GenerateSpinOrbitCW_h, and sets all of the
42 * "output" fields. If <tt>params->f</tt>=\c NULL, no spindown
43 * modulation is performed. If <tt>params->oneMinusEcc</tt> \f$ \not<0 \f$ (a
44 * non-hyperbolic orbit), or if
45 * <tt>params->rPeriNorm</tt> \f$ \times \f$ <tt>params->angularSpeed</tt> \f$ \geq1 \f$
46 * (faster-than-light speed at periapsis), an error is returned.
47 *
48 * In the <tt>*output</tt> structure, the field <tt>output->h</tt> is
49 * ignored, but all other pointer fields must be set to \c NULL. The
50 * function will create and allocate space for <tt>output->a</tt>,
51 * <tt>output->f</tt>, and <tt>output->phi</tt> as necessary. The
52 * <tt>output->shift</tt> field will remain set to \c NULL.
53 *
54 * ### Algorithm ###
55 *
56 * For hyperbolic orbits, we combine \eqref{eq_spinorbit-tr},
57 * \eqref{eq_spinorbit-t}, and \eqref{eq_spinorbit-upsilon} to get \f$ t_r \f$
58 * directly as a function of \f$ E \f$ :
59 * \f{eqnarray}{
60 * \label{eq_tr-e3}
61 * t_r = t_p & + & \left(\frac{r_p \sin i}{c}\right)\sin\omega\\
62 * & + & \frac{1}{n} \left( -E +
63 * \left[v_p(e-1)\cos\omega + e\right]\sinh E
64 * - \left[v_p\sqrt{\frac{e-1}{e+1}}\sin\omega\right]
65 * [\cosh E - 1]\right) \;,
66 * \f}
67 * where \f$ v_p=r_p\dot{\upsilon}_p\sin i/c \f$ is a normalized velocity at
68 * periapsis and \f$ n=\dot{\upsilon}_p\sqrt{(1-e)^3/(1+e)} \f$ is a normalized
69 * angular speed for the orbit (the hyperbolic analogue of the mean
70 * angular speed for closed orbits). For simplicity we write this as:
71 * \f{equation}{
72 * \label{eq_tr-e4}
73 * t_r = T_p + \frac{1}{n}\left( E + A\sinh E + B[\cosh E - 1] \right) \;,
74 * \f}
75 *
76 * \anchor inject_hanomaly
77 * \image html inject_hanomaly.png "Function to be inverted to find eccentric anomaly"
78 *
79 * where \f$ T_p \f$ is the \e observed time of periapsis passage. Thus
80 * the key numerical procedure in this routine is to invert the
81 * expression \f$ x=E+A\sinh E+B(\cosh E - 1) \f$ to get \f$ E(x) \f$ . This function
82 * is sketched to the right (solid line), along with an approximation
83 * used for making an initial guess (dotted line), as described later.
84 *
85 * We note that \f$ A^2-B^2<1 \f$ , although it approaches 1 when
86 * \f$ e\rightarrow1 \f$ , or when \f$ v_p\rightarrow1 \f$ and either \f$ e=0 \f$ or
87 * \f$ \omega=\pi \f$ . Except in this limit, Newton-Raphson methods will
88 * converge rapidly for any initial guess. In this limit, though, the
89 * slope \f$ dx/dE \f$ approaches zero at \f$ E=0 \f$ , and an initial guess or
90 * iteration landing near this point will send the next iteration off to
91 * unacceptably large or small values. A hybrid root-finding strategy is
92 * used to deal with this, and with the exponential behaviour of \f$ x \f$ at
93 * large \f$ E \f$ .
94 *
95 * First, we compute \f$ x=x_{\pm1} \f$ at \f$ E=\pm1 \f$ . If the desired \f$ x \f$ lies
96 * in this range, we use a straightforward Newton-Raphson root finder,
97 * with the constraint that all guesses of \f$ E \f$ are restricted to the
98 * domain \f$ [-1,1] \f$ . This guarantees that the scheme will eventually find
99 * itself on a uniformly-convergent trajectory.
100 *
101 * Second, for \f$ E \f$ outside of this range, \f$ x \f$ is dominated by the
102 * exponential terms: \f$ x\approx\frac{1}{2}(A+B)\exp(E) \f$ for \f$ E\gg1 \f$ , and
103 * \f$ x\approx-\frac{1}{2}(A-B)\exp(-E) \f$ for \f$ E\ll-1 \f$ . We therefore do an
104 * \e approximate Newton-Raphson iteration on the function \f$ \ln|x| \f$ ,
105 * where the approximation is that we take \f$ d\ln|x|/d|E|\approx1 \f$ . This
106 * involves computing an extra logarithm inside the loop, but gives very
107 * rapid convergence to high precision, since \f$ \ln|x| \f$ is very nearly
108 * linear in these regions.
109 *
110 * At the start of the algorithm, we use an initial guess of
111 * \f$ E=-\ln[-2(x-x_{-1})/(A-B)-\exp(1)] \f$ for \f$ x<x_{-1} \f$ , \f$ E=x/x_{-1} \f$ for
112 * \f$ x_{-1}\leq x\leq0 \f$ , \f$ E=x/x_{+1} \f$ for \f$ 0\leq x\leq x_{+1} \f$ , or
113 * \f$ E=\ln[2(x-x_{+1})/(A+B)-\exp(1)] \f$ for \f$ x>x_{+1} \f$ . We refine this
114 * guess until we get agreement to within 0.01 parts in part in
115 * \f$ N_\mathrm{cyc} \f$ (where \f$ N_\mathrm{cyc} \f$ is the larger of the number
116 * of wave cycles in a time \f$ 2\pi/n \f$ , or the number of wave cycles in the
117 * entire waveform being generated), or one part in \f$ 10^{15} \f$ (an order
118 * of magnitude off the best precision possible with \c REAL8
119 * numbers). The latter case indicates that \c REAL8 precision may
120 * fail to give accurate phasing, and one should consider modeling the
121 * orbit as a set of Taylor frequency coefficients \'{a} la
122 * <tt>LALGenerateTaylorCW()</tt>. On subsequent timesteps, we use the
123 * previous timestep as an initial guess, which is good so long as the
124 * timesteps are much smaller than \f$ 1/n \f$ .
125 *
126 * Once a value of \f$ E \f$ is found for a given timestep in the output
127 * series, we compute the system time \f$ t \f$ via \eqref{eq_spinorbit-t},
128 * and use it to determine the wave phase and (non-Doppler-shifted)
129 * frequency via \eqref{eq_taylorcw-freq}
130 * and \eqref{eq_taylorcw-phi}. The Doppler shift on the frequency is
131 * then computed using \eqref{eq_spinorbit-upsilon}
132 * and \eqref{eq_orbit-rdot}. We use \f$ \upsilon \f$ as an intermediate in
133 * the Doppler shift calculations, since expressing \f$ \dot{R} \f$ directly in
134 * terms of \f$ E \f$ results in expression of the form \f$ (e-1)/(e\cosh E-1) \f$ ,
135 * which are difficult to simplify and face precision losses when
136 * \f$ E\sim0 \f$ and \f$ e\rightarrow1 \f$ . By contrast, solving for \f$ \upsilon \f$ is
137 * numerically stable provided that the system <tt>atan2()</tt> function is
138 * well-designed.
139 *
140 * This routine does not account for relativistic timing variations, and
141 * issues warnings or errors based on the criterea of
142 * \eqref{eq_relativistic-orbit} in \ref LALGenerateEllipticSpinOrbitCW().
143 */
144void
148{
149 UINT4 n, i; /* number of and index over samples */
150 UINT4 nSpin = 0, j; /* number of and index over spindown terms */
151 REAL8 t, dt, tPow; /* time, interval, and t raised to a power */
152 REAL8 phi0, f0, twopif0; /* initial phase, frequency, and 2*pi*f0 */
153 REAL8 f, fPrev; /* current and previous values of frequency */
154 REAL4 df = 0.0; /* maximum difference between f and fPrev */
155 REAL8 phi; /* current value of phase */
156 REAL8 vDotAvg; /* nomalized orbital angular speed */
157 REAL8 vp; /* projected speed at periapsis */
158 REAL8 upsilon, argument; /* true anomaly, and argument of periapsis */
159 REAL8 eCosOmega; /* eccentricity * cosine of argument */
160 REAL8 tPeriObs; /* time of observed periapsis */
161 REAL8 spinOff; /* spin epoch - orbit epoch */
162 REAL8 x; /* observed ``mean anomaly'' */
163 REAL8 xPlus, xMinus; /* limits where exponentials dominate */
164 REAL8 dx, dxMax; /* current and target errors in x */
165 REAL8 a, b; /* constants in equation for x */
166 REAL8 ecc; /* orbital eccentricity */
167 REAL8 eccMinusOne, eccPlusOne; /* ecc - 1 and ecc + 1 */
168 REAL8 e; /* hyperbolic anomaly */
169 REAL8 sinhe, coshe; /* sinh of e, and cosh of e minus 1 */
170 REAL8 *fSpin = NULL; /* pointer to Taylor coefficients */
171 REAL4 *fData; /* pointer to frequency data */
172 REAL8 *phiData; /* pointer to phase data */
173
174 INITSTATUS( stat );
176
177 /* Make sure parameter and output structures exist. */
179 GENERATESPINORBITCWH_MSGENUL );
181 GENERATESPINORBITCWH_MSGENUL );
182
183 /* Make sure output fields don't exist. */
185 GENERATESPINORBITCWH_MSGEOUT );
187 GENERATESPINORBITCWH_MSGEOUT );
189 GENERATESPINORBITCWH_MSGEOUT );
191 GENERATESPINORBITCWH_MSGEOUT );
192
193 /* If Taylor coeficients are specified, make sure they exist. */
194 if ( params->f ) {
196 GENERATESPINORBITCWH_MSGENUL );
197 nSpin = params->f->length;
198 fSpin = params->f->data;
199 }
200
201 /* Set up some constants (to avoid repeated calculation or
202 dereferencing), and make sure they have acceptable values. */
203 eccMinusOne = -params->oneMinusEcc;
204 ecc = 1.0 + eccMinusOne;
205 eccPlusOne = 2.0 + eccMinusOne;
206 if ( eccMinusOne <= 0.0 ) {
208 GENERATESPINORBITCWH_MSGEECC );
209 }
210 vp = params->rPeriNorm * params->angularSpeed;
211 vDotAvg = params->angularSpeed
212 * sqrt( eccMinusOne * eccMinusOne * eccMinusOne / eccPlusOne );
213 n = params->length;
214 dt = params->deltaT;
215 f0 = fPrev = params->f0;
216 if ( vp >= 1.0 ) {
218 GENERATESPINORBITCWH_MSGEFTL );
219 }
220 if ( vp <= 0.0 || dt <= 0.0 || f0 <= 0.0 || vDotAvg <= 0.0 ||
221 n == 0 ) {
223 GENERATESPINORBITCWH_MSGESGN );
224 }
225
226 /* Set up some other constants. */
227 twopif0 = f0 * LAL_TWOPI;
228 phi0 = params->phi0;
229 argument = params->omega;
230 a = vp * eccMinusOne * cos( argument ) + ecc;
231 b = -vp * sqrt( eccMinusOne / eccPlusOne ) * sin( argument );
232 eCosOmega = ecc * cos( argument );
233 if ( n * dt * vDotAvg > LAL_TWOPI ) {
234 dxMax = 0.01 / ( f0 * n * dt );
235 } else {
236 dxMax = 0.01 / ( f0 * LAL_TWOPI / vDotAvg );
237 }
238 if ( dxMax < 1.0e-15 ) {
239 dxMax = 1.0e-15;
240 LALWarning( stat, "REAL8 arithmetic may not have sufficient"
241 " precision for this orbit" );
242 }
243 if ( lalDebugLevel & LALWARNING ) {
244 REAL8 tau = n * dt;
245 if ( tau > LAL_TWOPI / vDotAvg ) {
246 tau = LAL_TWOPI / vDotAvg;
247 }
248 if ( f0 * tau * vp * vp * ecc / eccPlusOne > 0.25 )
249 LALWarning( stat, "Orbit may have significant relativistic"
250 " effects that are not included" );
251 }
252
253 /* Compute offset between time series epoch and observed periapsis,
254 and betweem true periapsis and spindown reference epoch. */
255 tPeriObs = ( REAL8 )( params->orbitEpoch.gpsSeconds -
257 tPeriObs += 1.0e-9 * ( REAL8 )( params->orbitEpoch.gpsNanoSeconds -
259 tPeriObs += params->rPeriNorm * sin( params->omega );
260 spinOff = ( REAL8 )( params->orbitEpoch.gpsSeconds -
261 params->spinEpoch.gpsSeconds );
262 spinOff += 1.0e-9 * ( REAL8 )( params->orbitEpoch.gpsNanoSeconds -
263 params->spinEpoch.gpsNanoSeconds );
264
265 /* Determine bounds of hybrid root-finding algorithm, and initial
266 guess for e. */
267 xMinus = 1.0 + a * sinh( -1.0 ) + b * cosh( -1.0 ) - b;
268 xPlus = -1.0 + a * sinh( 1.0 ) + b * cosh( 1.0 ) - b;
269 x = -vDotAvg * tPeriObs;
270 if ( x < xMinus ) {
271 e = -log( -2.0 * ( x - xMinus ) / ( a - b ) - exp( 1.0 ) );
272 } else if ( x <= 0 ) {
273 e = x / xMinus;
274 } else if ( x <= xPlus ) {
275 e = x / xPlus;
276 } else {
277 e = log( 2.0 * ( x - xPlus ) / ( a + b ) - exp( 1.0 ) );
278 }
279 sinhe = sinh( e );
280 coshe = cosh( e ) - 1.0;
281
282 /* Allocate output structures. */
283 if ( ( output->a = ( REAL4TimeVectorSeries * )
284 LALMalloc( sizeof( REAL4TimeVectorSeries ) ) ) == NULL ) {
286 GENERATESPINORBITCWH_MSGEMEM );
287 }
288 memset( output->a, 0, sizeof( REAL4TimeVectorSeries ) );
289 if ( ( output->f = ( REAL4TimeSeries * )
290 LALMalloc( sizeof( REAL4TimeSeries ) ) ) == NULL ) {
291 LALFree( output->a );
292 output->a = NULL;
294 GENERATESPINORBITCWH_MSGEMEM );
295 }
296 memset( output->f, 0, sizeof( REAL4TimeSeries ) );
297 if ( ( output->phi = ( REAL8TimeSeries * )
298 LALMalloc( sizeof( REAL8TimeSeries ) ) ) == NULL ) {
299 LALFree( output->a );
300 output->a = NULL;
301 LALFree( output->f );
302 output->f = NULL;
304 GENERATESPINORBITCWH_MSGEMEM );
305 }
306 memset( output->phi, 0, sizeof( REAL8TimeSeries ) );
307
308 /* Set output structure metadata fields. */
309 output->position = params->position;
310 output->psi = params->psi;
311 output->a->epoch = output->f->epoch = output->phi->epoch
312 = params->epoch;
313 output->a->deltaT = n * params->deltaT;
314 output->f->deltaT = output->phi->deltaT = params->deltaT;
315 output->a->sampleUnits = lalStrainUnit;
316 output->f->sampleUnits = lalHertzUnit;
317 output->phi->sampleUnits = lalDimensionlessUnit;
318 snprintf( output->a->name, LALNameLength, "CW amplitudes" );
319 snprintf( output->f->name, LALNameLength, "CW frequency" );
320 snprintf( output->phi->name, LALNameLength, "CW phase" );
321
322 /* Allocate phase and frequency arrays. */
323 LALSCreateVector( stat->statusPtr, &( output->f->data ), n );
324 BEGINFAIL( stat ) {
325 LALFree( output->a );
326 output->a = NULL;
327 LALFree( output->f );
328 output->f = NULL;
329 LALFree( output->phi );
330 output->phi = NULL;
331 }
332 ENDFAIL( stat );
333 LALDCreateVector( stat->statusPtr, &( output->phi->data ), n );
334 BEGINFAIL( stat ) {
335 TRY( LALSDestroyVector( stat->statusPtr, &( output->f->data ) ),
336 stat );
337 LALFree( output->a );
338 output->a = NULL;
339 LALFree( output->f );
340 output->f = NULL;
341 LALFree( output->phi );
342 output->phi = NULL;
343 }
344 ENDFAIL( stat );
345
346 /* Allocate and fill amplitude array. */
347 {
348 CreateVectorSequenceIn in; /* input to create output->a */
349 in.length = 2;
350 in.vectorLength = 2;
351 LALSCreateVectorSequence( stat->statusPtr, &( output->a->data ), &in );
352 BEGINFAIL( stat ) {
353 TRY( LALSDestroyVector( stat->statusPtr, &( output->f->data ) ),
354 stat );
355 TRY( LALDDestroyVector( stat->statusPtr, &( output->phi->data ) ),
356 stat );
357 LALFree( output->a );
358 output->a = NULL;
359 LALFree( output->f );
360 output->f = NULL;
361 LALFree( output->phi );
362 output->phi = NULL;
363 }
364 ENDFAIL( stat );
365 output->a->data->data[0] = output->a->data->data[2] = params->aPlus;
366 output->a->data->data[1] = output->a->data->data[3] = params->aCross;
367 }
368
369 /* Fill frequency and phase arrays. */
370 fData = output->f->data->data;
371 phiData = output->phi->data->data;
372 for ( i = 0; i < n; i++ ) {
373
374 x = vDotAvg * ( i * dt - tPeriObs );
375
376 /* Use approximate Newton-Raphson method on ln|x| if |x| > 1. */
377 if ( x < xMinus ) {
378 x = log( -x );
379 while ( fabs( dx = log( e - a * sinhe - b * coshe ) - x ) > dxMax ) {
380 e += dx;
381 sinhe = sinh( e );
382 coshe = cosh( e ) - 1.0;
383 }
384 } else if ( x > xPlus ) {
385 x = log( x );
386 while ( fabs( dx = log( -e + a * sinhe + b * coshe ) - x ) > dxMax ) {
387 e -= dx;
388 sinhe = sinh( e );
389 coshe = cosh( e ) - 1.0;
390 }
391 }
392
393 /* Use ordinary Newton-Raphson method on x if |x| <= 1. */
394 else {
395 while ( fabs( dx = -e + a * sinhe + b * coshe - x ) > dxMax ) {
396 e -= dx / ( -1.0 + a * coshe + a + b * sinhe );
397 if ( e < -1.0 ) {
398 e = -1.0;
399 } else if ( e > 1.0 ) {
400 e = 1.0;
401 }
402 sinhe = sinh( e );
403 coshe = cosh( e ) - 1.0;
404 }
405 }
406
407 /* Compute source emission time, phase, and frequency. */
408 phi = t = tPow =
409 ( ecc * sinhe - e ) / vDotAvg + spinOff;
410 f = 1.0;
411 for ( j = 0; j < nSpin; j++ ) {
412 f += fSpin[j] * tPow;
413 phi += fSpin[j] * ( tPow *= t ) / ( j + 2.0 );
414 }
415
416 /* Appy frequency Doppler shift. */
417 upsilon = 2.0 * atan2( sqrt( eccPlusOne * coshe ),
418 sqrt( eccMinusOne * ( coshe + 2.0 ) ) );
419 f *= f0 / ( 1.0 + vp * ( cos( argument + upsilon ) + eCosOmega )
420 / eccPlusOne );
421 phi *= twopif0;
422 if ( fabs( f - fPrev ) > df ) {
423 df = fabs( f - fPrev );
424 }
425 *( fData++ ) = fPrev = f;
426 *( phiData++ ) = phi + phi0;
427 }
428
429 /* Set output field and return. */
430 params->dfdt = df * dt;
432 RETURN( stat );
433}
double cosh(double)
int j
#define LALMalloc(n)
#define LALFree(p)
#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 e
void LALSCreateVectorSequence(LALStatus *status, REAL4VectorSequence **vectorSequence, CreateVectorSequenceIn *vSeqParams)
#define GENERATESPINORBITCWH_EECC
Eccentricity out of range.
void LALGenerateHyperbolicSpinOrbitCW(LALStatus *stat, PulsarCoherentGW *output, SpinOrbitCWParamStruc *params)
Computes a continuous waveform with frequency drift and Doppler modulation from a hyperbolic orbital ...
#define GENERATESPINORBITCWH_ESGN
Sign error: positive parameter expected.
#define GENERATESPINORBITCWH_EMEM
Out of memory.
#define GENERATESPINORBITCWH_ENUL
Unexpected null pointer in arguments.
#define GENERATESPINORBITCWH_EOUT
Output field a, f, phi, or shift already exists.
#define GENERATESPINORBITCWH_EFTL
Periapsis motion is faster than light.
#define LAL_TWOPI
double REAL8
uint32_t UINT4
float REAL4
LALNameLength
#define lalDebugLevel
LALWARNING
int LALWarning(LALStatus *status, const char *warning)
static const INT4 a
const LALUnit lalStrainUnit
const LALUnit lalHertzUnit
const LALUnit lalDimensionlessUnit
void LALDCreateVector(LALStatus *, REAL8Vector **, UINT4)
void LALDDestroyVector(LALStatus *, REAL8Vector **)
void LALSDestroyVector(LALStatus *, REAL4Vector **)
void LALSCreateVector(LALStatus *, REAL4Vector **, UINT4)
n
INT4 gpsNanoSeconds
This structure stores a representation of a plane gravitational wave propagating from a particular po...
This structure stores the parameters for constructing a gravitational waveform with both a Taylor-pol...
LIGOTimeGPS epoch
double psi
double df