LALSimulation  5.4.0.1-fe68b98
LALSimInspiralPrecess.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012 Chris Pankow, Evan Ochsner
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 #include <lal/LALSimInspiralPrecess.h>
21 #include <lal/LALAtomicDatatypes.h>
22 
23 /**
24  * @addtogroup LALSimInspiralPrecess_h
25  * @{
26  */
27 
28 /**
29  * Takes in the h_lm spherical harmonic decomposed modes and rotates the modes
30  * by Euler angles alpha, beta, and gamma using the Wigner D matrices.
31  *
32  * e.g.
33  *
34  * \f$\tilde{h}_{l,m}(t) = D^l_{m',m} h_{l,m'}(t)\f$
35  */
37  SphHarmTimeSeries* h_lm, /**< spherical harmonic decomposed modes, modified in place */
38  REAL8TimeSeries* alpha, /**< alpha Euler angle time series */
39  REAL8TimeSeries* beta, /**< beta Euler angle time series */
40  REAL8TimeSeries* gam /**< gamma Euler angle time series */
41 ){
42 
43  unsigned int i;
44  int l, lmax, m, mp;
45  lmax = XLALSphHarmTimeSeriesGetMaxL( h_lm );
46  // Temporary holding variables
47  complex double *x_lm = XLALCalloc( 2*lmax+1, sizeof(complex double) );
48  COMPLEX16TimeSeries **h_xx = XLALCalloc( 2*lmax+1, sizeof(COMPLEX16TimeSeries) );
49 
50  for(i=0; i<alpha->data->length; i++){
51  for(l=2; l<=lmax; l++){
52  for(m=0; m<2*l+1; m++){
53  h_xx[m] = XLALSphHarmTimeSeriesGetMode(h_lm, l, m-l);
54  if( !h_xx[m] ){
55  x_lm[m] = 0;
56  } else {
57  x_lm[m] = h_xx[m]->data->data[i];
58  h_xx[m]->data->data[i] = 0;
59  }
60  }
61 
62  for(m=0; m<2*l+1; m++){
63  for(mp=0; mp<2*l+1; mp++){
64  if( !h_xx[m] ) continue;
65  if(!(creal(h_xx[m]->data->data[i])==0 && creal(x_lm[mp])==0)) {
66  h_xx[m]->data->data[i] +=
67  x_lm[mp] * XLALWignerDMatrix( l, mp-l, m-l, alpha->data->data[i], beta->data->data[i], gam->data->data[i] );
68  }
69  }
70  }
71  }
72  }
73 
74  XLALFree( x_lm );
75  XLALFree( h_xx );
76  return XLAL_SUCCESS;
77 }
78 
79 /**
80  * Takes in the l=2, abs(m)=2 decomposed modes as a strain time series and
81  * imposes the effect of a constant cone of precession. This is accomplished
82  * by taking the axis of the binary rotational plane and rotating the Y_lm
83  * such that it appears to "precess" around a fixed J direction.
84  * Note that h_2_2 and h_22 are modified in place.
85  *
86  * Future revisions will change the first two pointers to this:
87  * COMPLEX16TimeSeries** h_lm
88  *
89  * and add
90  * unsigned int l
91  *
92  * Thus the input h_lm will be considered a list of pointers to the h_lm for a
93  * given l and the appropriate action will be taken for *all* of the submodes.
94  */
96  SphHarmTimeSeries** h_lm_tmp, /**< (l,m) modes, modified in place */
97  double precess_freq, /**< Precession frequency in Hz */
98  double a, /**< Opening angle of precession cone in rads */
99  double phi_precess, /**< initial phase in cone of L around J */
100  double alpha_0, /**< azimuth btwn center of cone and line of sight */
101  double beta_0 /**< zenith btwn center of cone and line of sight */
102 ) {
103  /*
104  * Since the h_22 modes are the most likely to exist, we'll use those
105  * to do our error checking
106  */
107  SphHarmTimeSeries* h_lm = *h_lm_tmp;
108 
109  COMPLEX16TimeSeries *h_22, *h_2_2;
110  h_22 = XLALSphHarmTimeSeriesGetMode( h_lm, 2, 2 );
111  h_2_2 = XLALSphHarmTimeSeriesGetMode( h_lm, 2, -2 );
112 
113  if( !(h_22 && h_2_2) ){
114  XLALPrintError( "XLAL Error - %s: Currently, ConstantPrecessionConeWaveformModes requires the l=2 m=+/-2 modes to exist to continue.", __func__);
116  }
117 
118  // Error checking
119  // Since we need at least three points to do any of the numerical work
120  // we intend to, if the waveform is two points or smaller, we're in
121  // trouble. I don't expect this to be a problem.
122  if( h_2_2->data->length <= 2 ){
123  XLALPrintError( "XLAL Error - %s: Waveform length is too small to evolve accurately.", __func__);
125  }
126  if( h_2_2->data->length != h_22->data->length ){
127  XLALPrintError( "XLAL Error - %s: Input (2,2) and (2,-2) modes have different length.", __func__);
129  }
130 
131  unsigned int i;
132  double omg_p = 2*LAL_PI*precess_freq;
133  double t=0;
134 
135  // time evolved Euler angles
137  "euler angle alpha",
138  &(h_22->epoch),
139  h_22->f0,
140  h_22->deltaT,
141  &(h_22->sampleUnits),
142  h_22->data->length
143  );
145  "euler angle beta",
146  &(h_22->epoch),
147  h_22->f0,
148  h_22->deltaT,
149  &(h_22->sampleUnits),
150  h_22->data->length
151  );
153  "euler angle gamma",
154  &(h_22->epoch),
155  h_22->f0,
156  h_22->deltaT,
157  &(h_22->sampleUnits),
158  h_22->data->length
159  );
160 
161  // Minimal rotation constraint
162  // \gamma(t) = \int \cos(\beta(t)) \alpha'(t) dt
163  // Uses the second order finite difference to estimate dalpha/dt
164  // Then the trapezoid rule for the integration
165  for(i=0; i<alpha->data->length; i++){
166  t = h_22->deltaT*i;
167  alpha->data->data[i] = a*sin(omg_p * t + phi_precess) + alpha_0;
168  beta->data->data[i] = a*cos(omg_p * t + phi_precess) + beta_0;
169  }
170 
171  // NOTE: The step size cancels out between the difference and sum and
172  // thus does not appear below.
173  // two point forward difference
174  double dalpha_0 = alpha->data->data[1] - alpha->data->data[0];
175  // three point central difference
176  double dalpha_1 = 0.5*(alpha->data->data[2] - alpha->data->data[0]);
177  gam->data->data[0] = 0.;
178  gam->data->data[1] =
179  cos(beta->data->data[0])*dalpha_0 +
180  cos(beta->data->data[1])*dalpha_1;
181  for(i=2; i<gam->data->length-1; i++){
182  // three point central difference
183  dalpha_0 = dalpha_1;
184  dalpha_1 = 0.5*(alpha->data->data[i+1] - alpha->data->data[i-1]);
185  // Two point numerical integration over the interval
186  gam->data->data[i] =
187  gam->data->data[i-1] +
188  cos(beta->data->data[i-1])*dalpha_0 +
189  cos(beta->data->data[i])*dalpha_1;
190  }
191  // Use two point backward difference for last point
192  dalpha_0 = dalpha_1;
193  dalpha_1 = alpha->data->data[i] - alpha->data->data[i-1];
194  gam->data->data[i] = gam->data->data[i-1] +
195  cos(beta->data->data[i-1])*dalpha_0 +
196  cos(beta->data->data[i])*dalpha_1;
197 
198  // Rotate waveform
200 
204 
205  return XLAL_SUCCESS;
206 }
207 
208 /**
209  * Takes in the spherical harmonic decomposed modes as a strain time series and
210  * imposes the effect of a constant cone of precession. The result is returned
211  * in the physical waveforms hp, hx, after they have been resummed from the
212  * modified h_lm waveforms.
213  *
214  * NOTE: the h_lm modes will be modified in place
215  */
217  REAL8TimeSeries** hp, /**< Output precessing plus polarization */
218  REAL8TimeSeries** hx, /**< Output precessing cross polarization*/
219  SphHarmTimeSeries* h_lm, /**< Input non-precessing (l,m) modes */
220  double precess_freq, /**< Precession frequency in Hz */
221  double a, /**< Opening angle of precession cone in rads */
222  double phi_precess, /**< initial phase in cone of L around J */
223  double alpha_0, /**< azimuth btwn center of cone and line of sight */
224  double beta_0 /**< zenith btwn center of cone and line of sight */
225 ) {
227  &h_lm, precess_freq, a, phi_precess, alpha_0, beta_0 );
228  if( ret != XLAL_SUCCESS ) XLAL_ERROR( XLAL_EFUNC );
229 
230  // XLALSimInspiralConstantPrecessionConeWaveformModes transformed the
231  // modes assuming line of sight was down the z-axis. Therefore,
232  // the angles in the Ylm's are both zero.
233  double view_th = 0.0, view_ph = 0.0;
234  // Reconstitute the waveform from the h_lm
236  view_th, view_ph);
237  if( ret != XLAL_SUCCESS ) XLAL_ERROR( XLAL_EFUNC );
238 
239  return XLAL_SUCCESS;
240 }
241 
242 /**
243  * Determine the ``preferred direction'' matrix for the l=2 subspace from the
244  * relevant mode decomposed h. Modifies mtx in place.
245  */
247  REAL8 mtx[3][3], /**< 3x3 orientation matrix, modified in place */
248  COMPLEX16 h22, /**< l=2, m=2 h sample */
249  COMPLEX16 h2m2, /**< l=2, m=-2 h sample */
250  COMPLEX16 h21, /**< l=2, m=1 h sample */
251  COMPLEX16 h2m1, /**< l=2, m=-1 h sample */
252  COMPLEX16 h20 /**< l=2, m=0 h sample */
253 ) {
254 
255  COMPLEX16 I2, I1, I0, Izz;
256  COMPLEX16 nm;
257 
258  // Hardcode the result for the l=2 subspace, which dominates
259  // Derivation:
260  /* c[l_, m_] := Sqrt[l (l + 1) - m (m + 1)] */
261  /* Sum[1/2 conj[2, m + 2] h[2, m] c[2, m] c[2, m + 1], {m, -2, 0}] */
262  /* Sum[conj[2, m + 1] h[2, m] c[2, m] (m + 1/2), {m, -2, 1}] */
263 
264  I2 = sqrt(6.)*( conj(h20)*h2m2 + h20*conj(h22) ) + 3*conj(h21)*h2m1;
265  I0 = 1/2.*(
266  (2*(3) - 2*2)* conj(h22)*h22
267  + (2*(3) - 2*2) *conj(h2m2)*h2m2
268  + (2*(3) - 1*1)* conj(h21)*h21
269  + (2*(3) - 1*1) *conj(h2m1)*h2m1
270  + (2*(3) - 0 )* conj(h20)*h20
271  );
272  Izz = ( (2*2)* conj(h22)*h22
273  + ( 2*2) *conj(h2m2)*h2m2
274  + ( 1*1) *conj(h21)*h21
275  + ( 1*1) *conj(h2m1)*h2m1
276  + ( 0 ) *conj(h20)*h20
277  );
278  I1 = (
279  3*conj(h22)*h2m1 - 3 *conj(h2m1)*h2m2
280  + sqrt(3./2.)*(conj(h21)*h20 - conj(h20)*h2m1)
281  );
282 
283  nm = conj(h22)*h22 + conj(h21)*h21 + conj(h20)*h20 + conj(h2m1)*h2m1 + conj(h2m2)*h2m2;
284 
285  mtx[0][0] = creal( (I0 + creal(I2))/nm );
286  mtx[1][0] = mtx[0][1] = cimag(I2/nm);
287  mtx[2][0] = mtx[0][2] = creal(I1/nm);
288  mtx[1][1] = creal( (I0 - creal(I2))/nm );
289  mtx[2][1] = mtx[1][2] = cimag(I1/nm);
290  mtx[2][2] = creal(Izz/nm);
291 
292  return XLAL_SUCCESS;
293 }
294 
295 /**
296  * Determine a direction vector from the orientation matrix.
297  *
298  * NOTE: vec should be *initialized* to be a unit vector -- its value is used,
299  * then replaced
300  */
302  REAL8 vec[3], /**< 3 dim unit vector, modified in place */
303  REAL8 mtx[3][3] /**< 3x3 orientation matrix */
304 ) {
305  REAL8 vecLast[3];
306  gsl_matrix *m = gsl_matrix_alloc(3,3);
307  gsl_vector *eval = gsl_vector_alloc(3);
308  gsl_matrix *evec = gsl_matrix_alloc(3,3);
309  gsl_eigen_symmv_workspace *w = gsl_eigen_symmv_alloc(3);
310  int i,j;
311 
312  vecLast[0] = vec[0];
313  vecLast[1] = vec[1];
314  vecLast[2]=vec[2];
315  for (i=0; i<3; i++) {
316  for (j=0; j<3; j++) {
317  gsl_matrix_set(m,i,j,mtx[i][j]);
318  }
319  }
320  gsl_eigen_symmv(m, eval, evec, w);
321  gsl_eigen_symmv_free(w);
322 
323  gsl_eigen_symmv_sort (eval, evec, GSL_EIGEN_SORT_ABS_ASC);
324 
325  for (i=0; i<3; i++) {
326  vec[i] = gsl_matrix_get(evec,2,i);
327  }
328 
329  if (vecLast[0]*vec[0] + vecLast[1]*vec[1] + vecLast[2]*vec[2] <0) {
330  for (i=0; i<3; i++) {
331  vec[i] = - vec[i];
332  }
333  }
334 
335  gsl_vector_free(eval);
336  gsl_matrix_free(evec);
337 
338  return XLAL_SUCCESS;
339 }
340 
341 /**
342  * Takes in the h_lm spherical harmonic decomposed modes and rotates the modes
343  * by Euler angles alpha, beta, and gamma using the Wigner D matrices, analog to
344  * int XLALSimInspiralPrecessionRotateModes but with 2 crucial differences:
345  *
346  * * leaves unaltered the input SphericalHarmonicTimeSeries
347  * * The Wigner DMatrix rotation function XLALWignerDMatrix() is called
348  with arguments (alpha, -beta, gamma), instead of (alpha, beta, gamma),
349  * to ensure that
350  * (h+ + i hx)(alpha,beta,gamma)=Sum_{lmm'} Y_lm(0,0) D_mm'(alpha,beta,gamma) h_lm'(0,0,0)
351  *
352  */
353 int XLALSimInspiralPrecessionRotateModesOut(SphHarmTimeSeries **hlm_out, /**< spherical harmonic decomposed modes, output */
354  SphHarmTimeSeries *hlm_in, /**< spherical harmonic decomposed modes, input */
355  const REAL8TimeSeries *alpha, /**< alpha Euler angle time series */
356  const REAL8TimeSeries *beta, /**< beta Euler angle time series */
357  const REAL8TimeSeries *gam /**< gamma Euler angle time series */
358 ){
359 
360  if (*hlm_out)
362 
363  unsigned int i;
364  int l, m, mp;
365  int lmax = XLALSphHarmTimeSeriesGetMaxL( hlm_in );
366  int lmin = XLALSphHarmTimeSeriesGetMinL( hlm_in );
367  for( l=lmin; l <= lmax; l++ ) {
368  COMPLEX16TimeSeries **inmode= XLALCalloc( 2*lmax+1, sizeof(COMPLEX16TimeSeries) );
369  for( m=-l; m<=l; m++){
370  inmode[m+l] = XLALSphHarmTimeSeriesGetMode(hlm_in, l, m );
371  }
372  for( m=-l; m<=l; m++){
373  COMPLEX16TimeSeries *outmode = XLALCreateCOMPLEX16TimeSeries(inmode[m+l]->name,&inmode[m+l]->epoch,0.,inmode[m+l]->deltaT,&inmode[m+l]->sampleUnits,inmode[m+l]->data->length);
374  for(i=0; i<inmode[m+l]->data->length; i++)
375  outmode->data->data[i]=0.;
376  for(mp=-l; mp<=l; mp++){
377  for(i=0; i<inmode[m+l]->data->length; i++) {
378  outmode->data->data[i] += inmode[mp+l]->data->data[i] * XLALWignerDMatrix( l, mp, m, alpha->data->data[i], -beta->data->data[i], gam->data->data[i] );
379  }
380  }
381  *hlm_out=XLALSphHarmTimeSeriesAddMode(*hlm_out,outmode,l,m);
382  }
383  }
384  return XLAL_SUCCESS;
385 }
386 
387 /** @} */
static double beta(const double a, const double b, const sysq *system)
Internal function that computes the spin-orbit couplings.
const char * name
int l
Definition: bh_qnmode.c:135
double i
Definition: bh_ringdown.c:118
const double w
sigmaKerr data[0]
#define LAL_PI
double complex COMPLEX16
double REAL8
void * XLALCalloc(size_t m, size_t n)
void XLALFree(void *p)
int XLALSimInspiralPolarizationsFromSphHarmTimeSeries(REAL8TimeSeries **hp, REAL8TimeSeries **hc, SphHarmTimeSeries *hlms, REAL8 iota, REAL8 phiRef)
Compute the polarizations from all the -2 spin-weighted spherical harmonic modes stored in 'hlms'.
int XLALSimInspiralOrientationMatrixForL2(REAL8 mtx[3][3], COMPLEX16 h22, COMPLEX16 h2m2, COMPLEX16 h21, COMPLEX16 h2m1, COMPLEX16 h20)
Determine the `‘preferred direction’' matrix for the l=2 subspace from the relevant mode decomposed h...
int XLALSimInspiralOrientationMatrixDirection(REAL8 vec[3], REAL8 mtx[3][3])
Determine a direction vector from the orientation matrix.
int XLALSimInspiralPrecessionRotateModes(SphHarmTimeSeries *h_lm, REAL8TimeSeries *alpha, REAL8TimeSeries *beta, REAL8TimeSeries *gam)
Takes in the h_lm spherical harmonic decomposed modes and rotates the modes by Euler angles alpha,...
int XLALSimInspiralPrecessionRotateModesOut(SphHarmTimeSeries **hlm_out, SphHarmTimeSeries *hlm_in, const REAL8TimeSeries *alpha, const REAL8TimeSeries *beta, const REAL8TimeSeries *gam)
Takes in the h_lm spherical harmonic decomposed modes and rotates the modes by Euler angles alpha,...
int XLALSimInspiralConstantPrecessionConeWaveformModes(SphHarmTimeSeries **h_lm_tmp, double precess_freq, double a, double phi_precess, double alpha_0, double beta_0)
Takes in the l=2, abs(m)=2 decomposed modes as a strain time series and imposes the effect of a const...
int XLALSimInspiralConstantPrecessionConeWaveform(REAL8TimeSeries **hp, REAL8TimeSeries **hx, SphHarmTimeSeries *h_lm, double precess_freq, double a, double phi_precess, double alpha_0, double beta_0)
Takes in the spherical harmonic decomposed modes as a strain time series and imposes the effect of a ...
UINT4 XLALSphHarmTimeSeriesGetMinL(SphHarmTimeSeries *ts)
Get the smallest l index of any mode in the SphHarmTimeSeries linked list.
SphHarmTimeSeries * XLALSphHarmTimeSeriesAddMode(SphHarmTimeSeries *appended, const COMPLEX16TimeSeries *inmode, UINT4 l, INT4 m)
Prepend a node to a linked list of SphHarmTimeSeries, or create a new head.
COMPLEX16TimeSeries * XLALSphHarmTimeSeriesGetMode(SphHarmTimeSeries *ts, UINT4 l, INT4 m)
Get the time series of a waveform's (l,m) spherical harmonic mode from a SphHarmTimeSeries linked lis...
UINT4 XLALSphHarmTimeSeriesGetMaxL(SphHarmTimeSeries *ts)
Get the largest l index of any mode in the SphHarmTimeSeries linked list.
static const INT4 m
static const INT4 a
COMPLEX16 XLALWignerDMatrix(int l, int mp, int m, double alpha, double beta, double gam)
REAL8TimeSeries * XLALCreateREAL8TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
COMPLEX16TimeSeries * XLALCreateCOMPLEX16TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
#define XLAL_ERROR(...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
XLAL_EBADLEN
XLAL_SUCCESS
XLAL_EFUNC
XLAL_EINVAL
XLAL_EFAILED
double alpha
Definition: sgwb.c:106
COMPLEX16Sequence * data
COMPLEX16 * data
REAL8Sequence * data
REAL8 * data
Structure to carry a collection of spherical harmonic modes in COMPLEX16 time series.
LIGOTimeGPS epoch
Definition: unicorn.c:20
double deltaT
Definition: unicorn.c:24