Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALSimulation 6.2.0.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
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 */
353int 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 ...
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 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.
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)
COMPLEX16TimeSeries * XLALCreateCOMPLEX16TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
REAL8TimeSeries * XLALCreateREAL8TimeSeries(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