Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALPulsar 7.1.1.1-3a66518
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
SuperskyMetrics.c
Go to the documentation of this file.
1//
2// Copyright (C) 2014--2017 Karl Wette
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 <config.h>
21#include <stdbool.h>
22#include <stdlib.h>
23#include <stdio.h>
24#include <float.h>
25#include <math.h>
26
27#include <gsl/gsl_math.h>
28#include <gsl/gsl_blas.h>
29#include <gsl/gsl_linalg.h>
30#include <gsl/gsl_eigen.h>
31#include <gsl/gsl_roots.h>
32
33#include <lal/SuperskyMetrics.h>
34#include <lal/LALConstants.h>
35#include <lal/LogPrintf.h>
36#include <lal/MetricUtils.h>
37#include <lal/ExtrapolatePulsarSpins.h>
38
39#include <lal/GSLHelpers.h>
40
41#ifdef __GNUC__
42#define UNUSED __attribute__ ((unused))
43#else
44#define UNUSED
45#endif
46
47// Square of a numbers
48#define SQR(x) ((x)*(x))
49
50// Real part of square root of a number, i.e. zero if x < ~0
51#define RE_SQRT(x) sqrt(GSL_MAX(DBL_EPSILON, (x)))
52
53// 2- and 3-dimensional vector dot products
54#define DOT2(x,y) ((x)[0]*(y)[0] + (x)[1]*(y)[1])
55#define DOT3(x,y) ((x)[0]*(y)[0] + (x)[1]*(y)[1] + (x)[2]*(y)[2])
56
57// Maximum number of sky offsets required
58#define MAX_SKY_OFFSETS PULSAR_MAX_SPINS
59
60// FIXME: replace 'SMAX' with either 'nspins', 'nsky_offsets - 1', or something else...
61#define SMAX nspins
62
63// Internal definition of reduced supersky metric coordinate transform data
65 UINT4 ndim; ///< Dimensions of the corresponding metric
66 LIGOTimeGPS ref_time; ///< Reference time of the metric
67 REAL8 fiducial_freq; ///< Fiducial frequency of metric
68 UINT4 nspins; ///< Number of spindown dimensions
69 REAL8 align_sky[3][3]; ///< Alignment transform of the supersky metric
70 UINT4 nsky_offsets; ///< Number of sky offsets
71 REAL8 sky_offsets[MAX_SKY_OFFSETS][3]; ///< Sky offsets of the supersky metric
72};
73
74// Check reduced supersky coordinate metric and/or transform data
75#define CHECK_RSSKY_METRIC_TRANSF(M, RT) \
76 ((M) != NULL && CHECK_RSSKY_TRANSF(RT) && (M)->size1 == (RT)->ndim && (M)->size2 == (RT)->ndim)
77#define CHECK_RSSKY_TRANSF(RT) \
78 ((RT) != NULL && (RT)->ndim > 0 && (RT)->fiducial_freq > 0 && (RT)->nsky_offsets == 1 + (RT)->nspins)
79
80// Determine which dimension stores the reduced supersky frequency/spindown of order 's'
81#define RSSKY_FKDOT_OFFSET(RT, S) (((S) == 0) ? ((RT)->SMAX) : ((size_t)((S) - 1)))
82#define RSSKY_FKDOT_DIM(RT, S) (2 + RSSKY_FKDOT_OFFSET(RT, S))
83
84///
85/// Fiducial frequency at which to numerically calculate metrics, which
86/// are then rescaled to user-requested frequency based on known scalings
87///
88const double fiducial_calc_freq = 100.0;
89
90// Structures for callback functions
91typedef struct tagSM_CallbackParam {
92 const SuperskyTransformData *rssky_transf; ///< Reduced supersky coordinate transform data
93 const SuperskyTransformData *rssky2_transf; ///< Other reduced supersky coordinate transform data
95typedef struct tagSM_CallbackOut {
96 PulsarDopplerParams min_phys; ///< Minimum physical range
97 PulsarDopplerParams max_phys; ///< Maximum physical range
98 double min_rssky2_array[32]; ///< Minimum range of other reduced supersky coordinates
99 gsl_vector_view min_rssky2_view;
100 double max_rssky2_array[32]; ///< Maximum range of other reduced supersky coordinates
101 gsl_vector_view max_rssky2_view;
103
104///
105/// Call XLALComputeDopplerPhaseMetric() to compute the phase metric for a given coordinate system.
106///
107static gsl_matrix *SM_ComputePhaseMetric(
108 const DopplerCoordinateSystem *coords, ///< [in] Coordinate system to compute metric for
109 const LIGOTimeGPS *ref_time, ///< [in] Reference time of the metric
110 const LIGOTimeGPS *start_time, ///< [in] Start time of the metric
111 const LIGOTimeGPS *end_time, ///< [in] End time of the metric
112 const MultiLALDetector *detectors, ///< [in] List of detectors to average metric over
113 const MultiNoiseFloor *detector_weights, ///< [in] Weights used to combine single-detector metrics (default: unit weights)
114 const DetectorMotionType detector_motion, ///< [in] Which detector motion to use
115 const EphemerisData *ephemerides ///< [in] Earth/Sun ephemerides
116)
117{
118
119 // Check input
120 XLAL_CHECK_NULL( coords != NULL, XLAL_EFAULT );
121 XLAL_CHECK_NULL( ref_time != NULL, XLAL_EFAULT );
123 XLAL_CHECK_NULL( end_time != NULL, XLAL_EFAULT );
125 XLAL_CHECK_NULL( detectors->length > 0, XLAL_EINVAL );
126 XLAL_CHECK_NULL( detector_motion > 0, XLAL_EINVAL );
128
129 // Supersky metric cannot (reliably) be computed for segment lengths <= ~24 hours
130 XLAL_CHECK_NULL( XLALGPSDiff( end_time, start_time ) >= 81000, XLAL_ERANGE, "Supersky metric cannot be computed for segment lengths <= ~24 hours" );
131
132 // Create parameters struct for XLALComputeDopplerPhaseMetric()
134
135 // Set coordinate system
136 par.coordSys = *coords;
137
138 // Set detector motion type
139 par.detMotionType = detector_motion;
140
141 // Set segment list
142 LALSegList segments;
144 LALSeg segment;
145 XLAL_CHECK_NULL( XLALSegSet( &segment, start_time, end_time, 0 ) == XLAL_SUCCESS, XLAL_EFUNC );
146 XLAL_CHECK_NULL( XLALSegListAppend( &segments, &segment ) == XLAL_SUCCESS, XLAL_EFUNC );
147 par.segmentList = segments;
148
149 // Set detectors and detector weights
150 par.multiIFO = *detectors;
151 if ( detector_weights != NULL ) {
152 par.multiNoiseFloor = *detector_weights;
153 } else {
154 par.multiNoiseFloor.length = 0; // Indicates unit weights
155 }
156
157 // Set reference time to segment mid-time, to improve stability of numerical calculation of metric
158 par.signalParams.Doppler.refTime = *start_time;
159 XLALGPSAdd( &par.signalParams.Doppler.refTime, 0.5 * XLALGPSDiff( end_time, start_time ) );
160
161 // Set fiducial frequency
162 par.signalParams.Doppler.fkdot[0] = fiducial_calc_freq;
163
164 // Do not project metric
165 par.projectCoord = -1;
166
167 // Do not include sky-position-dependent Roemer delay in time variable
168 par.approxPhase = 1;
169
170 // Call XLALComputeDopplerPhaseMetric() and check output
172 XLAL_CHECK_NULL( metric != NULL && metric->g_ij != NULL, XLAL_EFUNC, "XLALComputeDopplerPhaseMetric() failed" );
173
174 // Extract metric, while transforming its reference time from segment mid-time to time specified by 'ref_time'
175 gsl_matrix *g_ij = NULL;
176 const REAL8 Dtau = XLALGPSDiff( ref_time, &par.signalParams.Doppler.refTime );
177 XLAL_CHECK_NULL( XLALChangeMetricReferenceTime( &g_ij, NULL, metric->g_ij, coords, Dtau ) == XLAL_SUCCESS, XLAL_EFUNC );
178
179 // Cleanup
181 XLALSegListClear( &segments );
182
183 return g_ij;
184
185}
186
187///
188/// Find a least-squares linear fit to the orbital X and Y metric elements using the frequency and
189/// spindown metric elements. Outputs the intermediate \e fitted supersky metric, and updates the
190/// reduced supersky metric coordinate transform data.
191///
193 gsl_matrix *fitted_ssky_metric, ///< [out] Fitted supersky metric
194 SuperskyTransformData *rssky_transf, ///< [in,out] Reduced supersky metric coordinate transform data
195 const gsl_matrix *ussky_metric, ///< [in] Unrestricted supersky metric
196 const gsl_matrix *orbital_metric, ///< [in] Orbital metric in ecliptic coordinates
197 const DopplerCoordinateSystem *ocoords, ///< [in] Coordinate system of orbital metric
198 const LIGOTimeGPS *start_time, ///< [in] Start time of the metrics
199 const LIGOTimeGPS *end_time ///< [in] End time of the metrics
200)
201{
202
203 // Check input
204 XLAL_CHECK( fitted_ssky_metric != NULL, XLAL_EFAULT );
205 XLAL_CHECK( CHECK_RSSKY_TRANSF( rssky_transf ), XLAL_EINVAL );
206 XLAL_CHECK( ussky_metric != NULL, XLAL_EFAULT );
207 XLAL_CHECK( orbital_metric != NULL, XLAL_EFAULT );
208 XLAL_CHECK( ocoords != NULL, XLAL_EFAULT );
210 XLAL_CHECK( end_time != NULL, XLAL_EFAULT );
211
212 // Allocate memory
213 gsl_matrix *GAMAT( tmp, 3 + rssky_transf->SMAX, 3 + rssky_transf->SMAX );
214 gsl_vector *GAVEC( tmpv, 1 + rssky_transf->SMAX );
215
216 // Compute mid-time of segment list
217 LIGOTimeGPS mid_time = *start_time;
218 XLALGPSAdd( &mid_time, 0.5 * XLALGPSDiff( end_time, start_time ) );
219
220 // Internal copy of orbital metric, and various transforms performed on it
221 gsl_matrix *orb_metric = NULL, *mid_time_transf = NULL, *diag_norm_transf = NULL;
222
223 // Transform reference time of orbital metric from reference time to segment list mid-time
224 const REAL8 Dtau = XLALGPSDiff( &mid_time, &rssky_transf->ref_time );
225 XLAL_CHECK( XLALChangeMetricReferenceTime( &orb_metric, &mid_time_transf, orbital_metric, ocoords, Dtau ) == XLAL_SUCCESS, XLAL_EFUNC );
226
227 // Diagonally-normalize orbital metric
228 XLAL_CHECK( XLALDiagNormalizeMetric( &orb_metric, &diag_norm_transf, orb_metric ) == XLAL_SUCCESS, XLAL_EFUNC );
229
230 // 'fitA' contains the frequency and spindown elements of the orbital metric, used for fitting
231 gsl_matrix *GAMAT( fitA, 3 + rssky_transf->SMAX, 1 + rssky_transf->SMAX );
232 {
233 gsl_matrix_view orb_metric_fspin = gsl_matrix_submatrix( orb_metric, 0, 2, 3 + rssky_transf->SMAX, 1 + rssky_transf->SMAX );
234 gsl_matrix_memcpy( fitA, &orb_metric_fspin.matrix );
235 }
236
237 // Compute 'fitA^T * fitA'
238 gsl_matrix *GAMAT( fitAt_fitA, 1 + rssky_transf->SMAX, 1 + rssky_transf->SMAX );
239 gsl_blas_dgemm( CblasTrans, CblasNoTrans, 1.0, fitA, fitA, 0.0, fitAt_fitA );
240
241 // Find the singular value decomposition of 'fitA^T * fitA'
242 gsl_matrix *GAMAT( svd_U, 1 + rssky_transf->SMAX, 1 + rssky_transf->SMAX );
243 gsl_matrix *GAMAT( svd_V, 1 + rssky_transf->SMAX, 1 + rssky_transf->SMAX );
244 gsl_vector *GAVEC( svd_S, 1 + rssky_transf->SMAX );
245 gsl_matrix_memcpy( svd_U, fitAt_fitA );
246 XLAL_CHECK( gsl_linalg_SV_decomp( svd_U, svd_V, svd_S, tmpv ) == 0, XLAL_EFAILED );
247
248 // The columns of 'fitc' contain the least-square fitting coefficients for the orbital X and Y metric elements:
249 // fitc(:,j) = inv(fitA^T * fitA) * fitA^T * orb_metric(:,j)
250 // The singular decomposition of fitA^T * fitA is used for the inverse
251 gsl_matrix *GAMAT( fitc, 1 + rssky_transf->SMAX, 2 );
252 for ( size_t j = 0; j < 2; ++j ) {
253 gsl_vector_view orb_metric_j = gsl_matrix_column( orb_metric, j );
254 gsl_vector_view fitc_j = gsl_matrix_column( fitc, j );
255 gsl_blas_dgemv( CblasTrans, 1.0, fitA, &orb_metric_j.vector, 0.0, tmpv );
256 XLAL_CHECK( gsl_linalg_SV_solve( svd_U, svd_V, svd_S, tmpv, &fitc_j.vector ) == 0, XLAL_EFAILED );
257 }
258
259 // Construct the matrix 'subtract_orb', which subtracts the least-squares fit of
260 // the orbital X and Y metric elements from the orbital metric. Its layout is:
261 //
262 // #-------- sky --------#---f/s---#
263 // | | |
264 // sky identity matrix I | zeros |
265 // | | |
266 // |---------------------#---------#
267 // | | |
268 // f/s -fitc | I |
269 // | | |
270 // #---------------------#---------#
271 //
272 gsl_matrix *GAMAT( subtract_orb, 3 + rssky_transf->SMAX, 3 + rssky_transf->SMAX );
273 {
274 gsl_matrix_set_identity( subtract_orb );
275 gsl_matrix_view subtract_orb_fspin_sky = gsl_matrix_submatrix( subtract_orb, 2, 0, 1 + rssky_transf->SMAX, 2 );
276 gsl_matrix_memcpy( &subtract_orb_fspin_sky.matrix, fitc );
277 gsl_matrix_scale( &subtract_orb_fspin_sky.matrix, -1.0 );
278 }
279
280 // Multiply 'subtract_orb' by the diagonal-normalization and reference time transforms,
281 // to obtain the matrix that substracts the fit from the unconstrained supersky metric
282 gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, 1.0, diag_norm_transf, subtract_orb, 0.0, tmp );
283 gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, 1.0, mid_time_transf, tmp, 0.0, subtract_orb );
284
285 // Construct the matrix 'subtract_ussky', which subtracts the least-squares fit of the orbital X and Y metric
286 // elements from the *unconstrained* supersky metric, which is in *equatorial* coordinates. Its layout is:
287 //
288 // #------------------------------ sky -------------------------------#---f/s---#
289 // | | |
290 // sky identity matrix I | zeros |
291 // | | |
292 // |------------------------------------------------------------------#---------#
293 // | . . | |
294 // f/s sub_o(:,0) . sub_o(:,1)*LAL_COSIEARTH . sub_o(:,1)*LAL_SINIEARTH | I |
295 // | . . | |
296 // #------------------------------------------------------------------#---------#
297 //
298 // where 'sub_o' denotes 'subtract_orb'.
299 gsl_matrix *GAMAT( subtract_ussky, 4 + rssky_transf->SMAX, 4 + rssky_transf->SMAX );
300 {
301 gsl_matrix_set_identity( subtract_ussky );
302 gsl_matrix_view subtract_ussky_fspin_sky = gsl_matrix_submatrix( subtract_ussky, 3, 0, 1 + rssky_transf->SMAX, 3 );
303 gsl_matrix_view subtract_orb_fspin_sky = gsl_matrix_submatrix( subtract_orb, 2, 0, 1 + rssky_transf->SMAX, 2 );
304 {
305 gsl_vector_view subtract_ussky_fspin_sky_col = gsl_matrix_column( &subtract_ussky_fspin_sky.matrix, 0 );
306 gsl_vector_view subtract_orb_fspin_sky_col = gsl_matrix_column( &subtract_orb_fspin_sky.matrix, 0 );
307 gsl_vector_memcpy( &subtract_ussky_fspin_sky_col.vector, &subtract_orb_fspin_sky_col.vector );
308 }
309 {
310 gsl_vector_view subtract_ussky_fspin_sky_col = gsl_matrix_column( &subtract_ussky_fspin_sky.matrix, 1 );
311 gsl_vector_view subtract_orb_fspin_sky_col = gsl_matrix_column( &subtract_orb_fspin_sky.matrix, 1 );
312 gsl_vector_memcpy( &subtract_ussky_fspin_sky_col.vector, &subtract_orb_fspin_sky_col.vector );
313 gsl_vector_scale( &subtract_ussky_fspin_sky_col.vector, LAL_COSIEARTH );
314 }
315 {
316 gsl_vector_view subtract_ussky_fspin_sky_col = gsl_matrix_column( &subtract_ussky_fspin_sky.matrix, 2 );
317 gsl_vector_view subtract_orb_fspin_sky_col = gsl_matrix_column( &subtract_orb_fspin_sky.matrix, 1 );
318 gsl_vector_memcpy( &subtract_ussky_fspin_sky_col.vector, &subtract_orb_fspin_sky_col.vector );
319 gsl_vector_scale( &subtract_ussky_fspin_sky_col.vector, LAL_SINIEARTH );
320 }
321 }
322
323 // Transform the unconstrained supersky metric to the intermediate fitted supersky metric
324 XLAL_CHECK( XLALTransformMetric( &fitted_ssky_metric, subtract_ussky, ussky_metric ) == XLAL_SUCCESS, XLAL_EFUNC );
325
326 // Extract the sky offset vectors from 'subtract_ussky', and subtract them from the reduced supersky coordinate transform data
327 {
328 gsl_matrix_view sky_offsets = gsl_matrix_view_array( &rssky_transf->sky_offsets[0][0], rssky_transf->nsky_offsets, 3 );
329 gsl_matrix_view subtract_ussky_fspin_sky = gsl_matrix_submatrix( subtract_ussky, 3, 0, 1 + rssky_transf->SMAX, 3 );
330 gsl_matrix_sub( &sky_offsets.matrix, &subtract_ussky_fspin_sky.matrix );
331 }
332
333 // Cleanup
334 GFMAT( diag_norm_transf, fitA, fitAt_fitA, fitc, mid_time_transf, orb_metric, subtract_orb, subtract_ussky, svd_U, svd_V, tmp );
335 GFVEC( svd_S, tmpv );
336
337 return XLAL_SUCCESS;
338
339}
340
341///
342/// Decouple the sky--sky and freq+spin--freq+spin blocks of the fitted supersky metric. Outputs the
343/// intermediate \e decoupled supersky metric, and updates the reduced supersky metric coordinate
344/// transform data.
345///
347 gsl_matrix *decoupled_ssky_metric, ///< [out] Decoupled supersky metric
348 SuperskyTransformData *rssky_transf, ///< [in,out] Reduced supersky metric coordinate transform data
349 const gsl_matrix *fitted_ssky_metric ///< [in] Fitted supersky metric
350)
351{
352
353 // Check input
354 XLAL_CHECK( decoupled_ssky_metric != NULL, XLAL_EFAULT );
355 XLAL_CHECK( CHECK_RSSKY_TRANSF( rssky_transf ), XLAL_EINVAL );
356 XLAL_CHECK( fitted_ssky_metric != NULL, XLAL_EFAULT );
357
358 // Copy fitted metric to decoupled metric
359 gsl_matrix_memcpy( decoupled_ssky_metric, fitted_ssky_metric );
360
361 // Create views of the sky--sky, freq+spin--freq+spin, and off-diagonal blocks
362 gsl_matrix_view sky_sky = gsl_matrix_submatrix( decoupled_ssky_metric, 0, 0, 3, 3 );
363 gsl_matrix_view sky_fspin = gsl_matrix_submatrix( decoupled_ssky_metric, 0, 3, 3, 1 + rssky_transf->SMAX );
364 gsl_matrix_view fspin_sky = gsl_matrix_submatrix( decoupled_ssky_metric, 3, 0, 1 + rssky_transf->SMAX, 3 );
365 gsl_matrix_view fspin_fspin = gsl_matrix_submatrix( decoupled_ssky_metric, 3, 3, 1 + rssky_transf->SMAX, 1 + rssky_transf->SMAX );
366
367 // Diagonal-normalise the freq+spin--freq+spin block
368 gsl_matrix *fspin_fspin_dnorm = NULL, *fspin_fspin_dnorm_transf = NULL;
369 XLAL_CHECK( XLALDiagNormalizeMetric( &fspin_fspin_dnorm, &fspin_fspin_dnorm_transf, &fspin_fspin.matrix ) == XLAL_SUCCESS, XLAL_EFUNC );
370
371 // Invert the freq+spin--freq+spin block
372 gsl_matrix *GAMAT( fspin_fspin_dnorm_LU, 1 + rssky_transf->SMAX, 1 + rssky_transf->SMAX );
373 gsl_matrix *GAMAT( fspin_fspin_dnorm_inv, 1 + rssky_transf->SMAX, 1 + rssky_transf->SMAX );
374 gsl_permutation *GAPERM( fspin_fspin_dnorm_LU_perm, 1 + rssky_transf->SMAX );
375 int fspin_fspin_dnorm_LU_sign = 0;
376 gsl_matrix_memcpy( fspin_fspin_dnorm_LU, fspin_fspin_dnorm );
377 XLAL_CHECK( gsl_linalg_LU_decomp( fspin_fspin_dnorm_LU, fspin_fspin_dnorm_LU_perm, &fspin_fspin_dnorm_LU_sign ) == 0, XLAL_EFAILED );
378 XLAL_CHECK( gsl_linalg_LU_invert( fspin_fspin_dnorm_LU, fspin_fspin_dnorm_LU_perm, fspin_fspin_dnorm_inv ) == 0, XLAL_EFAILED );
379
380 // Compute the additional sky offsets required to decouple the sky--sky and frequency blocks:
381 // decouple_sky_offsets = fspin_fspin_dnorm_transf * inv(fspin_fspin_dnorm) * fspin_fspin_dnorm_transf * fspin_sky
382 // Uses fspin_sky as a temporary matrix, since it will be zeroed out anyway
383 gsl_matrix *GAMAT( decouple_sky_offsets, 1 + rssky_transf->SMAX, 3 );
384 gsl_blas_dtrmm( CblasLeft, CblasUpper, CblasNoTrans, CblasNonUnit, 1.0, fspin_fspin_dnorm_transf, &fspin_sky.matrix );
385 gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, 1.0, fspin_fspin_dnorm_inv, &fspin_sky.matrix, 0.0, decouple_sky_offsets );
386 gsl_blas_dtrmm( CblasLeft, CblasUpper, CblasNoTrans, CblasNonUnit, 1.0, fspin_fspin_dnorm_transf, decouple_sky_offsets );
387
388 // Add the additional sky offsets to the reduced supersky coordinate transform data
389 gsl_matrix_view sky_offsets = gsl_matrix_view_array( &rssky_transf->sky_offsets[0][0], rssky_transf->nsky_offsets, 3 );
390 gsl_matrix_add( &sky_offsets.matrix, decouple_sky_offsets );
391
392 // Apply the decoupling transform to the sky--sky block:
393 // sky_sky = sky_sky - sky_fspin * decouplp_sky_offsets
394 gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, -1.0, &sky_fspin.matrix, decouple_sky_offsets, 1.0, &sky_sky.matrix );
395
396 // Zero out the off-diagonal blocks
397 gsl_matrix_set_zero( &sky_fspin.matrix );
398 gsl_matrix_set_zero( &fspin_sky.matrix );
399
400 // Cleanup
401 GFPERM( fspin_fspin_dnorm_LU_perm );
402 GFMAT( fspin_fspin_dnorm, fspin_fspin_dnorm_LU, fspin_fspin_dnorm_inv, fspin_fspin_dnorm_transf, decouple_sky_offsets );
403
404 return XLAL_SUCCESS;
405
406}
407
408///
409/// Align the sky--sky block of the decoupled supersky metric with its eigenvalues by means of a
410/// rotation. Outputs the intermediate \e aligned supersky metric, and updates the reduced supersky
411/// metric coordinate transform data.
412///
414 gsl_matrix *aligned_ssky_metric, ///< [out] Aligned supersky metric
415 SuperskyTransformData *rssky_transf, ///< [in,out] Reduced supersky metric coordinate transform data
416 const gsl_matrix *decoupled_ssky_metric ///< [in] Decoupled supersky metric
417)
418{
419
420 // Check input
421 XLAL_CHECK( aligned_ssky_metric != NULL, XLAL_EFAULT );
422 XLAL_CHECK( CHECK_RSSKY_TRANSF( rssky_transf ), XLAL_EINVAL );
423 XLAL_CHECK( decoupled_ssky_metric != NULL, XLAL_EFAULT );
424
425 // Allocate memory
426 gsl_matrix *GAMAT( tmp, 1 + rssky_transf->SMAX, 3 );
427
428 // Copy decoupled metric to aligned metric
429 gsl_matrix_memcpy( aligned_ssky_metric, decoupled_ssky_metric );
430
431 // Compute the eigenvalues/vectors of the sky--sky block
432 gsl_vector *GAVEC( sky_eval, 3 );
433 gsl_matrix *GAMAT( sky_evec, 3, 3 );
434 gsl_eigen_symmv_workspace *GALLOC( wksp, gsl_eigen_symmv_alloc( 3 ) );
435 gsl_matrix_view sky_sky = gsl_matrix_submatrix( aligned_ssky_metric, 0, 0, 3, 3 );
436 XLAL_CHECK( gsl_eigen_symmv( &sky_sky.matrix, sky_eval, sky_evec, wksp ) == 0, XLAL_EFAILED );
437
438 // Sort the eigenvalues/vectors by descending absolute eigenvalue
439 XLAL_CHECK( gsl_eigen_symmv_sort( sky_eval, sky_evec, GSL_EIGEN_SORT_ABS_DESC ) == 0, XLAL_EFAILED );
440
441 // Set the sky--sky block to the diagonal matrix of eigenvalues
442 gsl_matrix_set_zero( &sky_sky.matrix );
443 gsl_vector_view sky_sky_diag = gsl_matrix_diagonal( &sky_sky.matrix );
444 gsl_vector_memcpy( &sky_sky_diag.vector, sky_eval );
445
446 // Ensure that the matrix of eigenvalues has a positive diagonal; this and
447 // the determinant constraints ensures fully constraints the eigenvector signs
448 for ( size_t j = 0; j < 3; ++j ) {
449 gsl_vector_view col = gsl_matrix_column( sky_evec, j );
450 if ( gsl_vector_get( &col.vector, j ) < 0.0 ) {
451 gsl_vector_scale( &col.vector, -1.0 );
452 }
453 }
454
455 // Store the alignment transform in the reduced supersky coordinate transform data
456 gsl_matrix_view align_sky = gsl_matrix_view_array( &rssky_transf->align_sky[0][0], 3, 3 );
457 gsl_matrix_transpose_memcpy( &align_sky.matrix, sky_evec );
458
459 // Ensure that the alignment transform has a positive determinant,
460 // to ensure that that it represents a rotation
461 gsl_permutation *GAPERM( LU_perm, 3 );
462 int LU_sign = 0;
463 XLAL_CHECK( gsl_linalg_LU_decomp( sky_evec, LU_perm, &LU_sign ) == 0, XLAL_EFAILED );
464 if ( gsl_linalg_LU_det( sky_evec, LU_sign ) < 0.0 ) {
465 gsl_vector_view col = gsl_matrix_column( &align_sky.matrix, 2 );
466 gsl_vector_scale( &col.vector, -1.0 );
467 }
468
469 // Multiply the sky offsets by the alignment transform to transform to aligned sky coordinates:
470 // aligned_sky_off = sky_offsets * alignsky^T;
471 gsl_matrix_view sky_offsets = gsl_matrix_view_array( &rssky_transf->sky_offsets[0][0], rssky_transf->nsky_offsets, 3 );
472 gsl_matrix_memcpy( tmp, &sky_offsets.matrix );
473 gsl_blas_dgemm( CblasNoTrans, CblasTrans, 1.0, tmp, &align_sky.matrix, 0.0, &sky_offsets.matrix );
474
475 // Cleanup
476 gsl_eigen_symmv_free( wksp );
477 GFMAT( sky_evec, tmp );
478 GFPERM( LU_perm );
479 GFVEC( sky_eval );
480
481 return XLAL_SUCCESS;
482
483}
484
485///
486/// Compute the reduced supersky metric
487///
489 gsl_matrix **rssky_metric, ///< [out] Reduced supersky metric
490 SuperskyTransformData **rssky_transf, ///< [out] Reduced supersky metric coordinate transform data
491 const size_t spindowns, ///< [in] Number of frequency+spindown coordinates
492 const gsl_matrix *ussky_metric, ///< [in] Unrestricted supersky metric
493 const DopplerCoordinateSystem *ucoords, ///< [in] Coordinate system of unrestricted supersky metric
494 const gsl_matrix *orbital_metric, ///< [in] Orbital metric in ecliptic coordinates
495 const DopplerCoordinateSystem *ocoords, ///< [in] Coordinate system of orbital metric
496 const LIGOTimeGPS *ref_time, ///< [in] Reference time of the metrics
497 const LIGOTimeGPS *start_time, ///< [in] Start time of the metrics
498 const LIGOTimeGPS *end_time ///< [in] End time of the metrics
499)
500{
501
502 // Check input
503 XLAL_CHECK( rssky_metric != NULL && *rssky_metric == NULL, XLAL_EFAULT );
504 XLAL_CHECK( rssky_transf != NULL && *rssky_transf == NULL, XLAL_EFAULT );
505 XLAL_CHECK( ussky_metric != NULL, XLAL_EFAULT );
506 XLAL_CHECK( ussky_metric->size1 == ussky_metric->size2, XLAL_ESIZE );
507 XLAL_CHECK( ucoords != NULL, XLAL_EFAULT );
508 XLAL_CHECK( orbital_metric != NULL, XLAL_EFAULT );
509 XLAL_CHECK( orbital_metric->size1 == orbital_metric->size2, XLAL_ESIZE );
510 XLAL_CHECK( ocoords != NULL, XLAL_EFAULT );
511 XLAL_CHECK( ref_time != NULL, XLAL_EFAULT );
513 XLAL_CHECK( end_time != NULL, XLAL_EFAULT );
514
515 // Allocate memory
516 GAMAT( *rssky_metric, orbital_metric->size1, orbital_metric->size1 );
517 *rssky_transf = XLALCalloc( 1, sizeof( **rssky_transf ) );
518 XLAL_CHECK( *rssky_transf != NULL, XLAL_ENOMEM );
519 gsl_matrix *GAMAT( assky_metric, 1 + orbital_metric->size1, 1 + orbital_metric->size1 );
520
521 // Set coordinate transform data
522 ( *rssky_transf )->ndim = ( *rssky_metric )->size1;
523 ( *rssky_transf )->ref_time = *ref_time;
524 ( *rssky_transf )->fiducial_freq = fiducial_calc_freq;
525 ( *rssky_transf )->nspins = spindowns;
526 ( *rssky_transf )->nsky_offsets = 1 + spindowns;
527
528 // Compute the aligned supersky metric from the unrestricted supersky metric and the orbital metric
529 XLAL_CHECK( SM_ComputeFittedSuperskyMetric( assky_metric, *rssky_transf, ussky_metric, orbital_metric, ocoords, start_time, end_time ) == XLAL_SUCCESS, XLAL_EFUNC );
530 XLAL_CHECK( SM_ComputeDecoupledSuperskyMetric( assky_metric, *rssky_transf, assky_metric ) == XLAL_SUCCESS, XLAL_EFUNC );
531 XLAL_CHECK( SM_ComputeAlignedSuperskyMetric( assky_metric, *rssky_transf, assky_metric ) == XLAL_SUCCESS, XLAL_EFUNC );
532
533 {
534 // Move the row/column of the aligned supersky metric corresponding to the frequency to the last row/column
535 const int ifreq = XLALFindDopplerCoordinateInSystem( ucoords, DOPPLERCOORD_FREQ );
536 XLAL_CHECK( 3 <= ifreq, XLAL_EFAILED ); // 'ucoords' has 3 sky positions
537 for ( size_t i = ifreq; i + 1 < assky_metric->size1; ++i ) {
538 gsl_matrix_swap_rows( assky_metric, i, i + 1 );
539 gsl_matrix_swap_columns( assky_metric, i, i + 1 );
540 }
541
542 // Move the row of the coordinate transform data corresponding to the frequency to the last row
543 const int isky_offset_freq = ifreq - 3; // 'ucoords' has 3 sky positions
544 gsl_matrix_view sky_offsets = gsl_matrix_view_array( &( *rssky_transf )->sky_offsets[0][0], ( *rssky_transf )->nsky_offsets, 3 );
545 for ( size_t i = isky_offset_freq; i + 1 < sky_offsets.matrix.size1; ++i ) {
546 gsl_matrix_swap_rows( &sky_offsets.matrix, i, i + 1 );
547 }
548
549 // Move the row/column of the aligned supersky metric corresponding to the 'n_c' sky coordinate to the last row/column, so it can be easily dropped
551 XLAL_CHECK( 0 <= inc && inc < ifreq, XLAL_EFAILED );
552 for ( size_t i = inc; i + 1 < assky_metric->size1; ++i ) {
553 gsl_matrix_swap_rows( assky_metric, i, i + 1 );
554 gsl_matrix_swap_columns( assky_metric, i, i + 1 );
555 }
556 }
557
558 // Copy all but the last row/column to the aligned supersky metric to the reduced supersky metric, dropping 'n_c'
559 gsl_matrix_view extract_rssky_metric = gsl_matrix_submatrix( assky_metric, 0, 0, ( *rssky_metric )->size1, ( *rssky_metric )->size1 );
560 gsl_matrix_memcpy( *rssky_metric, &extract_rssky_metric.matrix );
561
562 // Ensure reduced supersky metric is symmetric
563 for ( size_t i = 0; i < ( *rssky_metric )->size1; ++i ) {
564 for ( size_t j = i + 1; j < ( *rssky_metric )->size1; ++j ) {
565 const double gij = gsl_matrix_get( *rssky_metric, i, j );
566 const double gji = gsl_matrix_get( *rssky_metric, j, i );
567 const double g = 0.5 * ( gij + gji );
568 gsl_matrix_set( *rssky_metric, i, j, g );
569 gsl_matrix_set( *rssky_metric, j, i, g );
570 }
571 }
572
573 // Ensure reduced supersky metric is positive definite
574 for ( size_t s = 1; s <= ( *rssky_metric )->size1; ++s ) {
575 gsl_matrix_view rssky_metric_s = gsl_matrix_submatrix( *rssky_metric, 0, 0, s, s );
576 const double det_s = XLALMetricDeterminant( &rssky_metric_s.matrix );
577 XLAL_CHECK( det_s > 0, XLAL_EFAILED, "Reduced supersky metric is not positive definite (s=%zu, det_s=%0.3e)", s, det_s );
578 }
579
580 // Cleanup
581 GFMAT( assky_metric );
582
583 return XLAL_SUCCESS;
584
585}
586
589 const size_t spindowns,
590 const LIGOTimeGPS *ref_time,
591 const LALSegList *segments,
592 const double fiducial_freq,
593 const MultiLALDetector *detectors,
594 const MultiNoiseFloor *detector_weights,
595 const DetectorMotionType detector_motion,
597)
598{
599
600 // Check input
602 XLAL_CHECK_NULL( spindowns <= 4, XLAL_EINVAL );
603 XLAL_CHECK_NULL( ref_time != NULL, XLAL_EFAULT );
604 XLAL_CHECK_NULL( segments != NULL, XLAL_EFAULT );
606 XLAL_CHECK_NULL( segments->length > 0, XLAL_EINVAL );
607 XLAL_CHECK_NULL( fiducial_freq > 0, XLAL_EINVAL );
609 XLAL_CHECK_NULL( detectors->length > 0, XLAL_EINVAL );
610 XLAL_CHECK_NULL( detector_motion > 0, XLAL_EINVAL );
612
613 // Build coordinate system for the unrestricted supersky metric and orbital metric
616 {
617 ucoords.coordIDs[ucoords.dim++] = DOPPLERCOORD_N3X_EQU;
618 ucoords.coordIDs[ucoords.dim++] = DOPPLERCOORD_N3Y_EQU;
619 ucoords.coordIDs[ucoords.dim++] = DOPPLERCOORD_N3Z_EQU;
620 ocoords.coordIDs[ocoords.dim++] = DOPPLERCOORD_N3OX_ECL;
621 ocoords.coordIDs[ocoords.dim++] = DOPPLERCOORD_N3OY_ECL;
622 }
623 {
624 ucoords.coordIDs[ucoords.dim++] = DOPPLERCOORD_FREQ;
625 ocoords.coordIDs[ocoords.dim++] = DOPPLERCOORD_FREQ;
626 }
627 if ( spindowns >= 1 ) {
628 ucoords.coordIDs[ucoords.dim++] = DOPPLERCOORD_F1DOT;
629 ocoords.coordIDs[ocoords.dim++] = DOPPLERCOORD_F1DOT;
630 }
631 if ( spindowns >= 2 ) {
632 ucoords.coordIDs[ucoords.dim++] = DOPPLERCOORD_F2DOT;
633 ocoords.coordIDs[ocoords.dim++] = DOPPLERCOORD_F2DOT;
634 }
635 if ( spindowns >= 3 ) {
636 ucoords.coordIDs[ucoords.dim++] = DOPPLERCOORD_F3DOT;
637 ocoords.coordIDs[ocoords.dim++] = DOPPLERCOORD_F3DOT;
638 }
639 if ( spindowns >= 4 ) {
640 ucoords.coordIDs[ucoords.dim++] = DOPPLERCOORD_F4DOT;
641 ocoords.coordIDs[ocoords.dim++] = DOPPLERCOORD_F4DOT;
642 }
643
644 // Allocate memory for output struct
645 SuperskyMetrics *metrics = XLALCalloc( 1, sizeof( *metrics ) );
646 XLAL_CHECK_NULL( metrics != NULL, XLAL_ENOMEM );
647 metrics->num_segments = segments->length;
648
649 // Allocate memory for arrays of coherent metrics
650 metrics->coh_rssky_metric = XLALCalloc( metrics->num_segments, sizeof( *metrics->coh_rssky_metric ) );
651 XLAL_CHECK_NULL( metrics->coh_rssky_metric != NULL, XLAL_ENOMEM );
652 metrics->coh_rssky_transf = XLALCalloc( metrics->num_segments, sizeof( *metrics->coh_rssky_transf ) );
653 XLAL_CHECK_NULL( metrics->coh_rssky_transf != NULL, XLAL_ENOMEM );
654
655 // Allocate memory for averaged metrics
656 gsl_matrix *GAMAT_NULL( ussky_metric_avg, 4 + spindowns, 4 + spindowns );
657 gsl_matrix *GAMAT_NULL( orbital_metric_avg, 3 + spindowns, 3 + spindowns );
658
659 // Compute the coherent supersky metrics for each segment
660 for ( size_t n = 0; n < metrics->num_segments; ++n ) {
661 const LIGOTimeGPS *start_time_seg = &segments->segs[n].start;
662 const LIGOTimeGPS *end_time_seg = &segments->segs[n].end;
663
664 // Compute the unrestricted supersky metric
665 gsl_matrix *ussky_metric_seg = SM_ComputePhaseMetric( &ucoords, ref_time, start_time_seg, end_time_seg, detectors, detector_weights, detector_motion, ephemerides );
666 XLAL_CHECK_NULL( ussky_metric_seg != NULL, XLAL_EFUNC );
667 gsl_matrix_add( ussky_metric_avg, ussky_metric_seg );
668
669 // Compute the orbital metric in ecliptic coordinates
670 gsl_matrix *orbital_metric_seg = SM_ComputePhaseMetric( &ocoords, ref_time, start_time_seg, end_time_seg, detectors, detector_weights, detector_motion, ephemerides );
671 XLAL_CHECK_NULL( orbital_metric_seg != NULL, XLAL_EFUNC );
672 gsl_matrix_add( orbital_metric_avg, orbital_metric_seg );
673
674 // Compute the coherent reduced supersky metric
675 XLAL_CHECK_NULL( SM_ComputeReducedSuperskyMetric( &metrics->coh_rssky_metric[n], &metrics->coh_rssky_transf[n], spindowns, ussky_metric_seg, &ucoords, orbital_metric_seg, &ocoords, ref_time, start_time_seg, end_time_seg ) == XLAL_SUCCESS, XLAL_EFUNC );
676 LogPrintf( LOG_DEBUG, "Computed coherent reduced supersky metric for segment %zu/%zu\n", n, metrics->num_segments );
677
678 // Cleanup
679 GFMAT( ussky_metric_seg, orbital_metric_seg );
680
681 }
682
683 // Normalise averaged metrics by number of segments
684 gsl_matrix_scale( ussky_metric_avg, 1.0 / metrics->num_segments );
685 gsl_matrix_scale( orbital_metric_avg, 1.0 / metrics->num_segments );
686
687 // Compute the semicoherent supersky metric for all segments
688 const LIGOTimeGPS *start_time_avg = &segments->segs[0].start;
689 const LIGOTimeGPS *end_time_avg = &segments->segs[segments->length - 1].end;
690 XLAL_CHECK_NULL( SM_ComputeReducedSuperskyMetric( &metrics->semi_rssky_metric, &metrics->semi_rssky_transf, spindowns, ussky_metric_avg, &ucoords, orbital_metric_avg, &ocoords, ref_time, start_time_avg, end_time_avg ) == XLAL_SUCCESS, XLAL_EFUNC );
691 LogPrintf( LOG_DEBUG, "Computed semicoherent reduced supersky metric for %zu segments\n", metrics->num_segments );
692
693 // Rescale metrics to input fiducial frequency
694 XLALScaleSuperskyMetricsFiducialFreq( metrics, fiducial_freq );
695
696 // Cleanup
697 GFMAT( ussky_metric_avg, orbital_metric_avg );
698
699 return metrics;
700
701}
702
704 const SuperskyMetrics *metrics
705)
706{
707
708 // Check input
709 XLAL_CHECK_NULL( metrics != NULL, XLAL_EFAULT );
710
711 // Allocate memory for copied struct
712 SuperskyMetrics *copy_metrics = XLALCalloc( 1, sizeof( *copy_metrics ) );
713 XLAL_CHECK_NULL( copy_metrics != NULL, XLAL_ENOMEM );
714 copy_metrics->num_segments = metrics->num_segments;
715
716 // Allocate memory for copies of arrays of coherent metrics
717 copy_metrics->coh_rssky_metric = XLALCalloc( copy_metrics->num_segments, sizeof( *copy_metrics->coh_rssky_metric ) );
718 XLAL_CHECK_NULL( copy_metrics->coh_rssky_metric != NULL, XLAL_ENOMEM );
719 copy_metrics->coh_rssky_transf = XLALCalloc( copy_metrics->num_segments, sizeof( *copy_metrics->coh_rssky_transf ) );
720 XLAL_CHECK_NULL( copy_metrics->coh_rssky_transf != NULL, XLAL_ENOMEM );
721
722 // Copy coherent metrics and transform data
723 for ( size_t n = 0; n < metrics->num_segments; ++n ) {
724 GAMAT_NULL( copy_metrics->coh_rssky_metric[n], metrics->coh_rssky_metric[n]->size1, metrics->coh_rssky_metric[n]->size2 );
725 gsl_matrix_memcpy( copy_metrics->coh_rssky_metric[n], metrics->coh_rssky_metric[n] );
726 copy_metrics->coh_rssky_transf[n] = XLALCalloc( 1, sizeof( *copy_metrics->coh_rssky_transf[n] ) );
727 XLAL_CHECK_NULL( copy_metrics->coh_rssky_transf[n] != NULL, XLAL_ENOMEM );
728 memcpy( copy_metrics->coh_rssky_transf[n], metrics->coh_rssky_transf[n], sizeof( *copy_metrics->coh_rssky_transf[n] ) );
729 }
730
731 // Copy semocherent metrics and transform data
732 GAMAT_NULL( copy_metrics->semi_rssky_metric, metrics->semi_rssky_metric->size1, metrics->semi_rssky_metric->size2 );
733 gsl_matrix_memcpy( copy_metrics->semi_rssky_metric, metrics->semi_rssky_metric );
734 copy_metrics->semi_rssky_transf = XLALCalloc( 1, sizeof( *copy_metrics->semi_rssky_transf ) );
735 XLAL_CHECK_NULL( copy_metrics->semi_rssky_transf != NULL, XLAL_ENOMEM );
736 memcpy( copy_metrics->semi_rssky_transf, metrics->semi_rssky_transf, sizeof( *copy_metrics->semi_rssky_transf ) );
737
738 return copy_metrics;
739
740}
741
743 SuperskyMetrics *metrics
744)
745{
746 if ( metrics != NULL ) {
747 for ( size_t n = 0; n < metrics->num_segments; ++n ) {
748 GFMAT( metrics->coh_rssky_metric[n] );
749 XLALFree( metrics->coh_rssky_transf[n] );
750 }
751 XLALFree( metrics->coh_rssky_metric );
752 XLALFree( metrics->coh_rssky_transf );
753 GFMAT( metrics->semi_rssky_metric );
754 XLALFree( metrics->semi_rssky_transf );
755 XLALFree( metrics );
756 }
757}
758
759///
760/// Initialise a FITS table for writing/reading a table of SuperskyTransformData entries
761///
764)
765{
766 XLAL_CHECK( file != NULL, XLAL_EFAULT );
767 XLAL_FITS_TABLE_COLUMN_BEGIN( SuperskyTransformData );
775 return XLAL_SUCCESS;
776}
777
779 FITSFile *file,
780 const SuperskyMetrics *metrics
781)
782{
783
784 // Check input
785 XLAL_CHECK( file != NULL, XLAL_EFAULT );
786 XLAL_CHECK( metrics != NULL, XLAL_EFAULT );
787
788 // Write coherent metrics to a FITS array
789 {
790 size_t dims[3] = {
791 metrics->coh_rssky_metric[0]->size1,
792 metrics->coh_rssky_metric[0]->size2,
793 metrics->num_segments
794 };
795 XLAL_CHECK( XLALFITSArrayOpenWrite( file, "coh_rssky_metric", 3, dims, "coherent supersky metrics" ) == XLAL_SUCCESS, XLAL_EFUNC );
796 size_t idx[3];
797 for ( idx[2] = 0; idx[2] < dims[2]; ++idx[2] ) {
799 }
800 }
801
802 // Write coherent metric transform data to a FITS table
803 {
804 XLAL_CHECK( XLALFITSTableOpenWrite( file, "coh_rssky_transf", "coherent supersky metric transform data" ) == XLAL_SUCCESS, XLAL_EFUNC );
806 for ( size_t i = 0; i < metrics->num_segments; ++i ) {
808 }
809 }
810
811 // Write semicoherent metric to a FITS array
812 {
813 XLAL_CHECK( XLALFITSArrayOpenWrite2( file, "semi_rssky_metric", metrics->semi_rssky_metric->size1, metrics->semi_rssky_metric->size2, "semicoherent supersky metric" ) == XLAL_SUCCESS, XLAL_EFUNC );
815 }
816
817 // Write semicoherent metric transform data to a FITS table
818 {
819 XLAL_CHECK( XLALFITSTableOpenWrite( file, "semi_rssky_transf", "semicoherent supersky metric transform data" ) == XLAL_SUCCESS, XLAL_EFUNC );
822 }
823
824 return XLAL_SUCCESS;
825
826}
827
829 FITSFile *file,
830 SuperskyMetrics **metrics
831)
832{
833
834 // Check input
835 XLAL_CHECK( file != NULL, XLAL_EFAULT );
836 XLAL_CHECK( metrics != NULL && *metrics == NULL, XLAL_EFAULT );
837
838 // Allocate memory
839 *metrics = XLALCalloc( 1, sizeof( **metrics ) );
840 XLAL_CHECK( *metrics != NULL, XLAL_ENOMEM );
841
842 // Read coherent metrics from a FITS array
843 {
844 size_t ndim, dims[FFIO_MAX];
845 XLAL_CHECK( XLALFITSArrayOpenRead( file, "coh_rssky_metric", &ndim, dims ) == XLAL_SUCCESS, XLAL_EFUNC );
846 XLAL_CHECK( ndim == 3, XLAL_EIO );
847 ( *metrics )->num_segments = dims[2];
848 ( *metrics )->coh_rssky_metric = XLALCalloc( ( *metrics )->num_segments, sizeof( *( *metrics )->coh_rssky_metric ) );
849 XLAL_CHECK( ( *metrics )->coh_rssky_metric != NULL, XLAL_ENOMEM );
850 size_t idx[3];
851 for ( idx[2] = 0; idx[2] < dims[2]; ++idx[2] ) {
852 XLAL_CHECK( XLALFITSArrayReadGSLMatrix( file, idx, &( *metrics )->coh_rssky_metric[idx[2]] ) == XLAL_SUCCESS, XLAL_EFUNC );
853 }
854 }
855
856 // Read coherent metric transform data from a FITS table
857 {
858 UINT8 nrows = 0;
859 XLAL_CHECK( XLALFITSTableOpenRead( file, "coh_rssky_transf", &nrows ) == XLAL_SUCCESS, XLAL_EFUNC );
860 XLAL_CHECK( nrows == ( *metrics )->num_segments, XLAL_EIO );
862 ( *metrics )->coh_rssky_transf = XLALCalloc( ( *metrics )->num_segments, sizeof( *( *metrics )->coh_rssky_transf ) );
863 XLAL_CHECK( ( *metrics )->coh_rssky_transf != NULL, XLAL_ENOMEM );
864 for ( size_t i = 0; i < ( *metrics )->num_segments; ++i ) {
865 ( *metrics )->coh_rssky_transf[i] = XLALCalloc( 1, sizeof( *( *metrics )->coh_rssky_transf[i] ) );
866 XLAL_CHECK( ( *metrics )->coh_rssky_transf[i] != NULL, XLAL_ENOMEM );
867 XLAL_CHECK( XLALFITSTableReadRow( file, ( *metrics )->coh_rssky_transf[i], &nrows ) == XLAL_SUCCESS, XLAL_EFUNC );
868 }
869 }
870
871 // Read semicoherent metric from a FITS array
872 {
873 XLAL_CHECK( XLALFITSArrayOpenRead2( file, "semi_rssky_metric", NULL, NULL ) == XLAL_SUCCESS, XLAL_EFUNC );
874 XLAL_CHECK( XLALFITSArrayReadGSLMatrix( file, NULL, &( *metrics )->semi_rssky_metric ) == XLAL_SUCCESS, XLAL_EFUNC );
875 }
876
877 // Read semicoherent metric transform data from a FITS table
878 {
879 UINT8 nrows = 0;
880 XLAL_CHECK( XLALFITSTableOpenRead( file, "semi_rssky_transf", &nrows ) == XLAL_SUCCESS, XLAL_EFUNC );
881 XLAL_CHECK( nrows == 1, XLAL_EIO );
883 ( *metrics )->semi_rssky_transf = XLALCalloc( 1, sizeof( *( *metrics )->semi_rssky_transf ) );
884 XLAL_CHECK( ( *metrics )->semi_rssky_transf != NULL, XLAL_ENOMEM );
885 XLAL_CHECK( XLALFITSTableReadRow( file, ( *metrics )->semi_rssky_transf, &nrows ) == XLAL_SUCCESS, XLAL_EFUNC );
886 }
887
888 return XLAL_SUCCESS;
889
890}
891
893 const SuperskyMetrics *metrics,
894 size_t *spindowns
895)
896{
897
898 // Check input
899 XLAL_CHECK( metrics != NULL, XLAL_EFAULT );
900 XLAL_CHECK( metrics->num_segments > 0, XLAL_EINVAL );
901 for ( size_t n = 0; n < metrics->num_segments; ++n ) {
903 }
905
906 // Return dimensions
907 if ( spindowns != NULL ) {
908 *spindowns = metrics->semi_rssky_transf->nspins;
909 }
910
911 return XLAL_SUCCESS;
912
913}
914
915///
916/// Scale a given supersky metric and its coordinate transform data to a new fiducial frequency.
917///
919 gsl_matrix *rssky_metric, ///< [in] Reduced supersky metric
920 SuperskyTransformData *rssky_transf, ///< [in] Reduced supersky metric coordinate transform data
921 const double new_fiducial_freq ///< [in] New fiducial frequency
922)
923{
924
925 // Check input
926 XLAL_CHECK( CHECK_RSSKY_METRIC_TRANSF( rssky_metric, rssky_transf ), XLAL_EINVAL );
927 XLAL_CHECK( new_fiducial_freq > 0, XLAL_EINVAL );
928
929 // Rescale metrics to 'new_fiducial_freq' based on known scalings
930 const double fiducial_scale = new_fiducial_freq / rssky_transf->fiducial_freq;
931 gsl_matrix_view sky_sky = gsl_matrix_submatrix( rssky_metric, 0, 0, 2, 2 );
932 gsl_matrix_view sky_offsets = gsl_matrix_view_array( &rssky_transf->sky_offsets[0][0], rssky_transf->nsky_offsets, 3 );
933 gsl_matrix_scale( &sky_sky.matrix, SQR( fiducial_scale ) );
934 gsl_matrix_scale( &sky_offsets.matrix, fiducial_scale );
935
936 // Set new fiducial frequency
937 rssky_transf->fiducial_freq = new_fiducial_freq;
938
939 return XLAL_SUCCESS;
940
941}
942
944 SuperskyMetrics *metrics,
945 const double new_fiducial_freq
946)
947{
948
949 // Check input
950 XLAL_CHECK( metrics != NULL, XLAL_EFAULT );
951 XLAL_CHECK( metrics->num_segments > 0, XLAL_EINVAL );
952 for ( size_t n = 0; n < metrics->num_segments; ++n ) {
954 }
956 XLAL_CHECK( new_fiducial_freq > 0, XLAL_EINVAL );
957
958 // Rescale all metrics to 'new_fiducial_freq'
959 for ( size_t n = 0; n < metrics->num_segments; ++n ) {
961 }
963
964 return XLAL_SUCCESS;
965
966}
967
969 SuperskyMetrics *metrics,
970 const double coh_max_mismatch,
971 const double semi_max_mismatch
972)
973{
974
975 // Check input
976 XLAL_CHECK( metrics != NULL, XLAL_EFAULT );
977 XLAL_CHECK( metrics->num_segments > 0, XLAL_EINVAL );
978 for ( size_t n = 0; n < metrics->num_segments; ++n ) {
980 }
982 XLAL_CHECK( coh_max_mismatch > 0, XLAL_EINVAL );
983 XLAL_CHECK( semi_max_mismatch > 0, XLAL_EINVAL );
984
985 // Find the maximum, over both semicoherent and coherent metrics, of 'g_{ff} / mu',
986 // where 'g_{ff}' is the frequency-frequency metric element, and mu is the mismatch
987 const size_t ifreq = RSSKY_FKDOT_DIM( metrics->semi_rssky_transf, 0 );
988 double max_gff_d_mu = gsl_matrix_get( metrics->semi_rssky_metric, ifreq, ifreq ) / semi_max_mismatch;
989 for ( size_t n = 0; n < metrics->num_segments; ++n ) {
990 const double coh_gff_d_mu = gsl_matrix_get( metrics->coh_rssky_metric[n], ifreq, ifreq ) / coh_max_mismatch;
991 if ( max_gff_d_mu < coh_gff_d_mu ) {
992 max_gff_d_mu = coh_gff_d_mu;
993 }
994 }
995
996 // Rescale rows 'g_{f*}' and columns 'g_{*f}' of both semicoherent and coherent metrics by
997 // 'sqrt( max{ g_{ff} / mu } / ( g_{ff} / mu ) )'; this scales the frequency coordinate such
998 // that 'g_{ff}' will give the same frequency spacing for all metrics, while also scaling the
999 // off-diagonal frequency terms (e.g. frequency-spindown correlations) correctly.
1000 {
1001 const double semi_gff_d_mu = gsl_matrix_get( metrics->semi_rssky_metric, ifreq, ifreq ) / semi_max_mismatch;
1002 const double scale = sqrt( max_gff_d_mu / semi_gff_d_mu );
1003 XLAL_CHECK( scale >= 1, XLAL_EFAILED );
1004 {
1005 gsl_vector_view rssky_metric_f_row = gsl_matrix_row( metrics->semi_rssky_metric, ifreq );
1006 gsl_vector_scale( &rssky_metric_f_row.vector, scale );
1007 } {
1008 gsl_vector_view rssky_metric_f_col = gsl_matrix_column( metrics->semi_rssky_metric, ifreq );
1009 gsl_vector_scale( &rssky_metric_f_col.vector, scale );
1010 }
1011 }
1012 for ( size_t n = 0; n < metrics->num_segments; ++n ) {
1013 const double coh_gff_d_mu = gsl_matrix_get( metrics->coh_rssky_metric[n], ifreq, ifreq ) / coh_max_mismatch;
1014 const double scale = sqrt( max_gff_d_mu / coh_gff_d_mu );
1015 XLAL_CHECK( scale >= 1, XLAL_EFAILED );
1016 {
1017 gsl_vector_view rssky_metric_f_row = gsl_matrix_row( metrics->coh_rssky_metric[n], ifreq );
1018 gsl_vector_scale( &rssky_metric_f_row.vector, scale );
1019 } {
1020 gsl_vector_view rssky_metric_f_col = gsl_matrix_column( metrics->coh_rssky_metric[n], ifreq );
1021 gsl_vector_scale( &rssky_metric_f_col.vector, scale );
1022 }
1023 }
1024
1025 return XLAL_SUCCESS;
1026
1027}
1028
1030 PulsarDopplerParams *out_phys,
1031 const SuperskyTransformData *rssky_transf
1032)
1033{
1034
1035 // Check input
1036 XLAL_CHECK( out_phys != NULL, XLAL_EFAULT );
1037 XLAL_CHECK( CHECK_RSSKY_TRANSF( rssky_transf ), XLAL_EFAULT );
1038
1039 // Set output physical point reference time to that of of coordinate transform data
1040 out_phys->refTime = rssky_transf->ref_time;
1041
1042 return XLAL_SUCCESS;
1043
1044}
1045
1046/**
1047 * Convert from 2-dimensional reduced supersky coordinates to 3-dimensional aligned sky
1048 * coordinates.
1049 *
1050 * The 2-dimensional reduced supersky coordinates \c rss = (\c A, \c B) encode the two
1051 * hemispheres of the sky as two neighbouring unit disks. The conversion to 3-dimensional
1052 * aligned sky coordinates is illustrated in the following diagram:
1053 *
1054 \verbatim
1055 | as[1] = __________________________________________
1056 | B = 1_| _____ _____ |
1057 | | .-' '-. .-' '-. |
1058 | | .' '. .' '. |
1059 | | / \ / \ |
1060 | | ; ; ; |
1061 | 0-| | | | |
1062 | | ; ; ; |
1063 | | \ / \ / |
1064 | | '. .' '. .' |
1065 | _| '-._____.-' '-._____.-' |
1066 | -1 |__.________.________.________.________.__|
1067 | ' ' ' ' '
1068 | A = -2 -1 0 1 2
1069 | as[0] = 1 0 -1 0 1
1070 | as[2] = 0 -1 0 1 0
1071 \endverbatim
1072 *
1073 * Points outside the unit disks are moved radially onto their boundaries.
1074 */
1076 double as[3], ///< [out] 3-dimensional aligned sky coordinates
1077 const gsl_vector *rss, ///< [in] 2-dimensional reduced supersky coordinates
1078 const double hemi ///< [in] Supersky coordinate hemisphere; only sign is used
1079)
1080{
1081 const double A = GSL_SIGN( hemi ) * gsl_vector_get( rss, 0 ) - 1;
1082 const double B = gsl_vector_get( rss, 1 );
1083 const double R = sqrt( SQR( A ) + SQR( B ) );
1084 const double Rmax = GSL_MAX( 1.0, R );
1085 as[0] = A / Rmax;
1086 as[1] = B / Rmax;
1087 as[2] = GSL_SIGN( hemi ) * RE_SQRT( 1.0 - DOT2( as, as ) );
1088}
1089
1090///
1091/// Convert from 3-dimensional aligned sky coordinates to 2-dimensional reduced supersky
1092/// coordinates.
1093///
1095 gsl_vector *rss, ///< [out] 2-dimensional reduced supersky coordinates
1096 const double as[3] ///< [in] 3-dimensional aligned sky coordinates
1097)
1098{
1099 const double r = sqrt( DOT3( as, as ) );
1100 const double A = GSL_SIGN( as[2] ) * ( ( as[0] / r ) + 1.0 );
1101 const double B = as[1] / r;
1102 gsl_vector_set( rss, 0, A );
1103 gsl_vector_set( rss, 1, B );
1104}
1105
1107 gsl_vector *out_rssky,
1108 const PulsarDopplerParams *in_phys,
1109 const SuperskyTransformData *rssky_transf
1110)
1111{
1112
1113 // Check input
1114 XLAL_CHECK( out_rssky != NULL, XLAL_EFAULT );
1115 XLAL_CHECK( in_phys != NULL, XLAL_EFAULT );
1116 XLAL_CHECK( CHECK_RSSKY_TRANSF( rssky_transf ), XLAL_EFAULT );
1117 XLAL_CHECK( out_rssky->size == rssky_transf->ndim, XLAL_ESIZE );
1118
1119 // Transform input physical point to reference time of coordinate transform data
1120 PulsarDopplerParams in_phys_ref = *in_phys;
1121 {
1122 const REAL8 dtau = XLALGPSDiff( &rssky_transf->ref_time, &in_phys_ref.refTime );
1123 XLAL_CHECK( XLALExtrapolatePulsarSpins( in_phys_ref.fkdot, in_phys_ref.fkdot, dtau ) == XLAL_SUCCESS, XLAL_EFUNC );
1124 }
1125
1126 // Create array for intermediate coordinates
1127 double intm[4 + rssky_transf->SMAX];
1128 gsl_vector_view intm_sky2 = gsl_vector_view_array( &intm[0], 2 );
1129 gsl_vector_view intm_sky3 = gsl_vector_view_array( &intm[0], 3 );
1130 gsl_vector_view intm_fspin = gsl_vector_view_array( &intm[3], 1 + rssky_transf->SMAX );
1131
1132 // Convert right ascension and declination to equatorial coordinates
1133 {
1134 const double cos_Delta = cos( in_phys_ref.Delta );
1135 intm[0] = cos( in_phys_ref.Alpha ) * cos_Delta;
1136 intm[1] = sin( in_phys_ref.Alpha ) * cos_Delta;
1137 intm[2] = sin( in_phys_ref.Delta );
1138 }
1139
1140 // Copy frequency/spindowns to intermediate array; frequency goes last
1141 intm[3 + rssky_transf->SMAX] = in_phys_ref.fkdot[0];
1142 for ( size_t s = 1; s <= rssky_transf->SMAX; ++s ) {
1143 intm[2 + s] = in_phys_ref.fkdot[s];
1144 }
1145
1146 // Apply the alignment transform to the supersky position to produced the aligned sky position:
1147 // asky = align_sky * ssky
1148 double asky[3];
1149 gsl_vector_view asky_v = gsl_vector_view_array( asky, 3 );
1150 gsl_matrix_const_view align_sky = gsl_matrix_const_view_array( &rssky_transf->align_sky[0][0], 3, 3 );
1151 gsl_blas_dgemv( CblasNoTrans, 1.0, &align_sky.matrix, &intm_sky3.vector, 0.0, &asky_v.vector );
1152
1153 // Add the inner product of the sky offsets with the aligned sky position
1154 // to the supersky spins and frequency to get the reduced supersky quantities:
1155 // rssky_fspin[i] = ussky_fspin[i] + dot(sky_offsets[i], asky)
1156 gsl_matrix_const_view sky_offsets = gsl_matrix_const_view_array( &rssky_transf->sky_offsets[0][0], rssky_transf->nsky_offsets, 3 );
1157 gsl_blas_dgemv( CblasNoTrans, 1.0, &sky_offsets.matrix, &asky_v.vector, 1.0, &intm_fspin.vector );
1158
1159 // Convert from 3-dimensional aligned sky coordinates to 2-dimensional reduced supersky coordinates
1160 SM_AlignedToReduced( &intm_sky3.vector, asky );
1161
1162 // Copy intermediate array to output point
1163 {
1164 gsl_vector_view out_sky2 = gsl_vector_subvector( out_rssky, 0, 2 );
1165 gsl_vector_view out_fspin = gsl_vector_subvector( out_rssky, 2, 1 + rssky_transf->SMAX );
1166 gsl_vector_memcpy( &out_sky2.vector, &intm_sky2.vector );
1167 gsl_vector_memcpy( &out_fspin.vector, &intm_fspin.vector );
1168 }
1169
1170 return XLAL_SUCCESS;
1171
1172}
1173
1175 PulsarDopplerParams *out_phys,
1176 const gsl_vector *in_rssky,
1177 const gsl_vector *ref_rssky,
1178 const SuperskyTransformData *rssky_transf
1179)
1180{
1181
1182 // Check input
1183 XLAL_CHECK( out_phys != NULL, XLAL_EFAULT );
1184 XLAL_CHECK( in_rssky != NULL, XLAL_EFAULT );
1185 XLAL_CHECK( CHECK_RSSKY_TRANSF( rssky_transf ), XLAL_EFAULT );
1186 XLAL_CHECK( in_rssky->size == rssky_transf->ndim, XLAL_ESIZE );
1187 XLAL_CHECK( ref_rssky == NULL || in_rssky->size == rssky_transf->ndim, XLAL_ESIZE );
1188
1189 // Set output physical point reference time to that of of coordinate transform data
1190 out_phys->refTime = rssky_transf->ref_time;
1191
1192 // Create array for intermediate coordinates
1193 double intm[4 + rssky_transf->SMAX];
1194 gsl_vector_view intm_sky2 = gsl_vector_view_array( &intm[0], 2 );
1195 gsl_vector_view intm_sky3 = gsl_vector_view_array( &intm[0], 3 );
1196 gsl_vector_view intm_fspin = gsl_vector_view_array( &intm[3], 1 + rssky_transf->SMAX );
1197
1198 // Copy input point to intermediate array
1199 {
1200 gsl_vector_const_view in_sky2 = gsl_vector_const_subvector( in_rssky, 0, 2 );
1201 gsl_vector_const_view in_fspin = gsl_vector_const_subvector( in_rssky, 2, 1 + rssky_transf->SMAX );
1202 gsl_vector_memcpy( &intm_sky2.vector, &in_sky2.vector );
1203 gsl_vector_memcpy( &intm_fspin.vector, &in_fspin.vector );
1204 }
1205
1206 // Convert from 2-dimensional reduced supersky coordinates to 3-dimensional aligned sky coordinates
1207 const double A = gsl_vector_get( ref_rssky != NULL ? ref_rssky : in_rssky, 0 );
1208 double asky[3];
1209 SM_ReducedToAligned( asky, &intm_sky3.vector, A );
1210 gsl_vector_view asky_v = gsl_vector_view_array( asky, 3 );
1211
1212 // Subtract the inner product of the sky offsets with the aligned sky position
1213 // from the reduced supersky spins and frequency to get the supersky quantities:
1214 // ussky_fspin[i] = rssky_fspin[i] - dot(sky_offsets[i], asky)
1215 gsl_matrix_const_view sky_offsets = gsl_matrix_const_view_array( &rssky_transf->sky_offsets[0][0], rssky_transf->nsky_offsets, 3 );
1216 gsl_blas_dgemv( CblasNoTrans, -1.0, &sky_offsets.matrix, &asky_v.vector, 1.0, &intm_fspin.vector );
1217
1218 // Apply the inverse alignment transform to the aligned sky position to produced the supersky position:
1219 // ssky = align_sky^T * asky
1220 gsl_matrix_const_view align_sky = gsl_matrix_const_view_array( &rssky_transf->align_sky[0][0], 3, 3 );
1221 gsl_blas_dgemv( CblasTrans, 1.0, &align_sky.matrix, &asky_v.vector, 0.0, &intm_sky3.vector );
1222
1223 // Copy frequency/spindowns to output physical point; frequency goes first
1224 out_phys->fkdot[0] = intm[3 + rssky_transf->SMAX];
1225 for ( size_t s = 1; s <= rssky_transf->SMAX; ++s ) {
1226 out_phys->fkdot[s] = intm[2 + s];
1227 }
1228
1229 // Convert supersky position in equatorial coordinates to right ascension and declination
1230 out_phys->Alpha = atan2( intm[1], intm[0] );
1231 out_phys->Delta = atan2( intm[2], sqrt( SQR( intm[0] ) + SQR( intm[1] ) ) );
1232 XLALNormalizeSkyPosition( &out_phys->Alpha, &out_phys->Delta );
1233
1234 return XLAL_SUCCESS;
1235
1236}
1237
1239 gsl_vector *out_rssky,
1240 const SuperskyTransformData *out_rssky_transf,
1241 const gsl_vector *in_rssky,
1242 const gsl_vector *ref_rssky,
1243 const SuperskyTransformData *in_rssky_transf
1244)
1245{
1246
1247 // Check input
1248 XLAL_CHECK( out_rssky != NULL, XLAL_EFAULT );
1249 XLAL_CHECK( CHECK_RSSKY_TRANSF( out_rssky_transf ), XLAL_EFAULT );
1250 XLAL_CHECK( in_rssky != NULL, XLAL_EFAULT );
1251 XLAL_CHECK( CHECK_RSSKY_TRANSF( in_rssky_transf ), XLAL_EFAULT );
1252
1253 // Convert input reduced supersky point to physical coordinates
1255 XLAL_CHECK( XLALConvertSuperskyToPhysicalPoint( &phys, in_rssky, ref_rssky, in_rssky_transf ) == XLAL_SUCCESS, XLAL_EINVAL );
1256
1257 // Convert physical point to output reduced supersky coordinates
1258 XLAL_CHECK( XLALConvertPhysicalToSuperskyPoint( out_rssky, &phys, out_rssky_transf ) == XLAL_SUCCESS, XLAL_EINVAL );
1259
1260 return XLAL_SUCCESS;
1261
1262}
1263
1265 gsl_matrix **out_rssky,
1266 const gsl_matrix *in_phys,
1267 const SuperskyTransformData *rssky_transf
1268)
1269{
1270
1271 // Check input
1272 XLAL_CHECK( out_rssky != NULL, XLAL_EFAULT );
1273 XLAL_CHECK( in_phys != NULL, XLAL_EFAULT );
1274 XLAL_CHECK( CHECK_RSSKY_TRANSF( rssky_transf ), XLAL_EFAULT );
1275 XLAL_CHECK( in_phys->size1 == rssky_transf->ndim, XLAL_ESIZE );
1276
1277 // Resize or allocate output matrix, if required
1278 if ( *out_rssky != NULL ) {
1279 if ( ( *out_rssky )->size1 != in_phys->size1 || ( *out_rssky )->size2 != in_phys->size2 ) {
1280 GFMAT( *out_rssky );
1281 *out_rssky = NULL;
1282 }
1283 }
1284 if ( *out_rssky == NULL ) {
1285 GAMAT( *out_rssky, in_phys->size1, in_phys->size2 );
1286 }
1287
1288 // Loop over all input points
1289 for ( size_t j = 0; j < in_phys->size2; ++j ) {
1290
1291 // Fill PulsarDopplerParams struct from input point
1293 in_phys_j.refTime = rssky_transf->ref_time;
1294 in_phys_j.Alpha = gsl_matrix_get( in_phys, 0, j );
1295 in_phys_j.Delta = gsl_matrix_get( in_phys, 1, j );
1296 for ( size_t s = 0; s <= rssky_transf->SMAX; ++s ) {
1297 in_phys_j.fkdot[s] = gsl_matrix_get( in_phys, 2 + s, j );
1298 }
1299
1300 // Convert point from physical to supersky coordinates
1301 gsl_vector_view out_rssky_j = gsl_matrix_column( *out_rssky, j );
1302 XLAL_CHECK( XLALConvertPhysicalToSuperskyPoint( &out_rssky_j.vector, &in_phys_j, rssky_transf ) == XLAL_SUCCESS, XLAL_EFUNC );
1303
1304 }
1305
1306 return XLAL_SUCCESS;
1307
1308}
1309
1311 gsl_matrix **out_phys,
1312 const gsl_matrix *in_rssky,
1313 const SuperskyTransformData *rssky_transf
1314)
1315{
1316
1317 // Check input
1318 XLAL_CHECK( out_phys != NULL, XLAL_EFAULT );
1319 XLAL_CHECK( in_rssky != NULL, XLAL_EFAULT );
1320 XLAL_CHECK( CHECK_RSSKY_TRANSF( rssky_transf ), XLAL_EFAULT );
1321 XLAL_CHECK( in_rssky->size1 == rssky_transf->ndim, XLAL_ESIZE );
1322
1323 // Resize or allocate output matrix, if required
1324 if ( *out_phys != NULL ) {
1325 if ( ( *out_phys )->size1 != in_rssky->size1 || ( *out_phys )->size2 != in_rssky->size2 ) {
1326 GFMAT( *out_phys );
1327 *out_phys = NULL;
1328 }
1329 }
1330 if ( *out_phys == NULL ) {
1331 GAMAT( *out_phys, in_rssky->size1, in_rssky->size2 );
1332 }
1333
1334 // Loop over all input points
1335 for ( size_t j = 0; j < in_rssky->size2; ++j ) {
1336
1337 // Convert point from supersky to physical coordinates
1338 gsl_vector_const_view in_rssky_j = gsl_matrix_const_column( in_rssky, j );
1339 PulsarDopplerParams XLAL_INIT_DECL( out_phys_j );
1340 XLAL_CHECK( XLALConvertSuperskyToPhysicalPoint( &out_phys_j, &in_rssky_j.vector, NULL, rssky_transf ) == XLAL_SUCCESS, XLAL_EFUNC );
1341
1342 // Fill output point from PulsarDopplerParams struct
1343 gsl_matrix_set( *out_phys, 0, j, out_phys_j.Alpha );
1344 gsl_matrix_set( *out_phys, 1, j, out_phys_j.Delta );
1345 for ( size_t s = 0; s <= rssky_transf->SMAX; ++s ) {
1346 gsl_matrix_set( *out_phys, 2 + s, j, out_phys_j.fkdot[s] );
1347 }
1348
1349 }
1350
1351 return XLAL_SUCCESS;
1352
1353}
1354
1355static void SkyBoundCache(
1356 const size_t dim UNUSED,
1357 const gsl_vector *point,
1358 gsl_vector *cache
1359)
1360{
1361
1362 // Convert from 2-dimensional reduced supersky coordinates to 3-dimensional aligned sky coordinates
1363 const double A = gsl_vector_get( point, 0 );
1364 double as[3];
1365 SM_ReducedToAligned( as, point, A );
1366
1367 // Store aligned sky position in cache
1368 for ( size_t i = 0; i < 3; ++i ) {
1369 gsl_vector_set( cache, i, as[i] );
1370 }
1371
1372}
1373
1374typedef struct {
1375 double max_A;
1376 int type;
1377 double r;
1378 double na0;
1380 double angle0;
1381 double C;
1382 double S;
1383 double Z;
1385typedef struct {
1386 double min_A;
1388 double max_A;
1392
1393static double PhysicalSkyBound(
1394 const void *data,
1395 const size_t dim UNUSED,
1396 const gsl_matrix *cache UNUSED,
1397 const gsl_vector *point
1398)
1399{
1400
1401 // Get bounds data
1402 const PhysicalSkyBoundData *psbd = ( const PhysicalSkyBoundData * ) data;
1403
1404 // Decode the reduced supersky coordinates to get
1405 // na = as[0] = Q_na . n
1406 const double A = gsl_vector_get( point, 0 );
1407 const double rssky[2] = { A, 0 };
1408 gsl_vector_const_view rssky_view = gsl_vector_const_view_array( rssky, 2 );
1409 double as[3];
1410 SM_ReducedToAligned( as, &rssky_view.vector, A );
1411 const double na = as[0];
1412
1413 // Absolute limiting bound on 'nb = +/- sqrt(1 - na^2)'
1414 const double limit = RE_SQRT( 1 - SQR( na ) );
1415
1416 // If 'A' is outside range '(min_A, max_A)', set bound to 'min_A_bound' or 'max_A_bound'
1417 double bound = GSL_NAN;
1418 if ( A <= psbd->min_A ) {
1419 bound = psbd->min_A_bound;
1420 } else if ( A >= psbd->max_A ) {
1421 bound = psbd->max_A_bound;
1422 } else {
1423
1424 // Loop over bound pieces to find which one currently applies, based on 'max_A'
1425 for ( size_t i = 0; i < XLAL_NUM_ELEM( psbd->pieces ); ++i ) {
1426 const PhysicalSkyBoundPiece p = psbd->pieces[i];
1427 if ( A <= p.max_A ) {
1428
1429 if ( p.type != 0 ) {
1430
1431 // Set bound 'nb = +/- sqrt(1 - na^2)'
1432 bound = p.type * limit;
1433
1434 } else {
1435
1436 // Set bound 'nb' to either a constant right ascension or constant declination bound,
1437 // depending on bounds data set by XLALSetSuperskyPhysicalSkyBounds()
1438 const double c = ( na - p.na0 ) / p.r;
1439 double angle = asin( GSL_MAX( -1, GSL_MIN( c, 1 ) ) );
1440 if ( p.altroot ) {
1441 angle = LAL_PI - angle;
1442 }
1443 angle -= p.angle0;
1444 bound = p.C * cos( angle ) + p.S * sin( angle ) + p.Z;
1445
1446 }
1447
1448 break;
1449
1450 }
1451 }
1452
1453 }
1454
1455 return GSL_MAX( -limit, GSL_MIN( bound, limit ) );
1456
1457}
1458
1460 LatticeTiling *tiling,
1461 gsl_matrix *rssky_metric,
1462 SuperskyTransformData *rssky_transf,
1463 const double alpha1,
1464 const double alpha2,
1465 const double delta1,
1466 const double delta2
1467)
1468{
1469
1470 // Check input
1471 XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
1472 XLAL_CHECK( CHECK_RSSKY_METRIC_TRANSF( rssky_metric, rssky_transf ), XLAL_EINVAL );
1473 XLAL_CHECK( gsl_matrix_get( rssky_metric, 0, 1 ) == 0, XLAL_EINVAL );
1474 XLAL_CHECK( gsl_matrix_get( rssky_metric, 1, 0 ) == 0, XLAL_EINVAL );
1475
1476 // Decompose coordinate transform data
1477 gsl_matrix_view align_sky = gsl_matrix_view_array( &rssky_transf->align_sky[0][0], 3, 3 );
1478 gsl_matrix_view sky_offsets = gsl_matrix_view_array( &rssky_transf->sky_offsets[0][0], rssky_transf->nsky_offsets, 3 );
1479
1480 // Check sky bound ranges
1481 XLAL_CHECK( ( fabs( alpha1 - alpha2 ) > 0 ) == ( fabs( delta1 - delta2 ) > 0 ), XLAL_EINVAL,
1482 "|alpha1 - alpha2| and |delta1 - delta2| must either be both zero, or both nonzero" );
1483 if ( fabs( alpha1 - alpha2 ) >= LAL_TWOPI ) {
1484 XLAL_CHECK( fabs( delta1 ) >= LAL_PI_2 && fabs( delta2 ) >= LAL_PI_2, XLAL_EINVAL,
1485 "If |alpha1 - alpha2| covers the whole sky, then |delta1| == |delta2| == PI/2 is required" );
1486 } else {
1487 XLAL_CHECK( fabs( alpha1 - alpha2 ) <= LAL_PI, XLAL_EINVAL,
1488 "If |alpha1 - alpha2| does not cover the whole sky, then |alpha1 - alpha2| <= PI is required" );
1489 XLAL_CHECK( -LAL_PI_2 <= delta1 && delta1 <= LAL_PI_2, XLAL_EINVAL,
1490 "If |alpha1 - alpha2| does not cover the whole sky, then -PI/2 <= delta1 <= PI/2 is required" );
1491 XLAL_CHECK( -LAL_PI_2 <= delta2 && delta2 <= LAL_PI_2, XLAL_EINVAL,
1492 "If |alpha1 - alpha2| does not cover the whole sky, then -PI/2 <= delta2 <= PI/2 is required" );
1493 }
1494
1495 // Set parameter-space bound names
1498
1499 // If parameter space is a single point:
1500 if ( alpha1 == alpha2 && delta1 == delta2 ) {
1501
1502 // Convert physical point to reduced supersky coordinates A and B
1503 double rssky_point[rssky_metric->size1];
1504 gsl_vector_view rssky_point_view = gsl_vector_view_array( rssky_point, rssky_metric->size1 );
1505 PulsarDopplerParams XLAL_INIT_DECL( phys_point );
1506 phys_point.Alpha = alpha1;
1507 phys_point.Delta = delta1;
1508 XLAL_CHECK( XLALConvertPhysicalToSuperskyPoint( &rssky_point_view.vector, &phys_point, rssky_transf ) == XLAL_SUCCESS, XLAL_EFUNC );
1509
1510 // Set the parameter-space bounds on reduced supersky sky coordinates A and B
1511 for ( size_t dim = 0; dim < 2; ++dim ) {
1512 XLAL_CHECK( XLALSetLatticeTilingConstantBound( tiling, dim, rssky_point[dim], rssky_point[dim] ) == XLAL_SUCCESS, XLAL_EFUNC );
1513 }
1515
1516 return XLAL_SUCCESS;
1517
1518 }
1519
1520 // Initialise bounds data
1523 for ( size_t i = 0; i < XLAL_NUM_ELEM( data_lower.pieces ); ++i ) {
1524 data_lower.pieces[i].max_A = data_upper.pieces[i].max_A = GSL_NEGINF;
1525 }
1526
1527 // Special bounds data representing the lower/upper circular bounds on reduced supersky coordinate B
1528 const PhysicalSkyBoundPiece lower_circular = { .type = -1 };
1529 const PhysicalSkyBoundPiece upper_circular = { .type = 1 };
1530
1531 // If parameter space is the entire sky:
1532 if ( fabs( alpha1 - alpha2 ) >= LAL_TWOPI ) {
1533
1534 // Set bounds data to lower/upper circular bounds
1535 data_lower.min_A = data_upper.min_A = -2;
1536 data_lower.min_A_bound = data_upper.min_A_bound = 0;
1537 data_lower.max_A = data_upper.max_A = 2;
1538 data_lower.max_A_bound = data_upper.max_A_bound = 0;
1539 data_lower.pieces[0] = lower_circular;
1540 data_lower.pieces[0].max_A = GSL_POSINF;
1541 data_upper.pieces[0] = upper_circular;
1542 data_upper.pieces[0].max_A = GSL_POSINF;
1543
1544 // Set the parameter-space bounds on reduced supersky sky coordinates A and B
1545 XLAL_CHECK( XLALSetLatticeTilingConstantBound( tiling, 0, data_lower.min_A, data_lower.max_A ) == XLAL_SUCCESS, XLAL_EFUNC );
1546 XLAL_CHECK( XLALSetLatticeTilingBound( tiling, 1, PhysicalSkyBound, sizeof( data_lower ), &data_lower, &data_upper ) == XLAL_SUCCESS, XLAL_EFUNC );
1548
1549 return XLAL_SUCCESS;
1550
1551 }
1552
1553 // Determine the right-handed angle of rotation 'phi' in the reduced supersky
1554 // plane 'nc = 0' to apply to the matrix
1555 // Q^T = [Q_na; Q_nb; Q_nc] = rssky_transf[0:2, 0:2]
1556 // such that the physical sky point 'alpha = min(alpha1,alpha2), delta = 0' is
1557 // mapped to the reduced supersky point 'A = B = 0'. This will transform the
1558 // physical sky region '[alpha1,alpha2] x [delta1,delta2]' such that moving from
1559 // 'delta1' to 'delta2' will run through the 'A' coordinate, and moving from
1560 // 'alpha1' to 'alpha2' will roughly run through the 'B' coordinate:
1561 // __________________________________________
1562 // B = 1_| _____ _____ |
1563 // | .-' '-. .-' '-. |
1564 // | .' '. .' '. |
1565 // | / \ / \ |
1566 // | ; ; ; | \.
1567 // 0-| | | | | |---> ~direction of delta
1568 // | ; ; ; | <__/
1569 // | \ / \ / | ~direction of alpha
1570 // | '. .' '. .' |
1571 // _| '-._____.-' '-._____.-' |
1572 // -1 |__.________.________.________.________.__|
1573 // ' ' ' ' '
1574 // A = -2 -1 0 1 2
1575 // where '#' indicates the area being tiled.
1576 double phi = 0;
1577 {
1578 const double alpha = GSL_MIN( alpha1, alpha2 );
1579 const double cos_alpha = cos( alpha );
1580 const double sin_alpha = sin( alpha );
1581 const double Q_na_0 = gsl_matrix_get( &align_sky.matrix, 0, 0 );
1582 const double Q_na_1 = gsl_matrix_get( &align_sky.matrix, 0, 1 );
1583 const double Q_nb_0 = gsl_matrix_get( &align_sky.matrix, 1, 0 );
1584 const double Q_nb_1 = gsl_matrix_get( &align_sky.matrix, 1, 1 );
1585 const double na = Q_na_0 * cos_alpha + Q_na_1 * sin_alpha;
1586 const double nb = Q_nb_0 * cos_alpha + Q_nb_1 * sin_alpha;
1587 phi = atan2( -nb, na );
1588 const double na_rot = na * cos( phi ) + nb * sin( phi );
1589 if ( na_rot < 0 ) {
1590 phi -= LAL_PI;
1591 } else if ( na_rot > 0 ) {
1592 phi += LAL_PI;
1593 }
1594 }
1595 const double cos_phi = cos( phi );
1596 const double sin_phi = sin( phi );
1597
1598 // Apply the right-handed rotation matrix
1599 // R = [cos(phi), -sin(phi), 0; sin(phi), cos(phi), 0; 0, 0, 1]
1600 // to the reduced supersky coordinate transform data
1601 // rssky_transf = [Q^T; Delta^s]
1602 // where 'Q' is the sky alignment matrix and 'Delta^s' are the sky offset vectors.
1603 // The correct transformation to apply is:
1604 // Q^T ==> R * Q^T, Delta^s ==> Delta^s . R^T
1605 for ( size_t j = 0; j < 3; ++j ) {
1606 const double Q_na_j = gsl_matrix_get( &align_sky.matrix, 0, j );
1607 const double Q_nb_j = gsl_matrix_get( &align_sky.matrix, 1, j );
1608 const double Q_na_j_rot = Q_na_j * cos_phi - Q_nb_j * sin_phi;
1609 const double Q_nb_j_rot = Q_nb_j * cos_phi + Q_na_j * sin_phi;
1610 gsl_matrix_set( &align_sky.matrix, 0, j, Q_na_j_rot );
1611 gsl_matrix_set( &align_sky.matrix, 1, j, Q_nb_j_rot );
1612 }
1613 for ( size_t i = 0; i < sky_offsets.matrix.size1; ++i ) {
1614 const double Delta_0 = gsl_matrix_get( &sky_offsets.matrix, i, 0 );
1615 const double Delta_1 = gsl_matrix_get( &sky_offsets.matrix, i, 1 );
1616 const double Delta_0_rot = Delta_0 * cos_phi - Delta_1 * sin_phi;
1617 const double Delta_1_rot = Delta_1 * cos_phi + Delta_0 * sin_phi;
1618 gsl_matrix_set( &sky_offsets.matrix, i, 0, Delta_0_rot );
1619 gsl_matrix_set( &sky_offsets.matrix, i, 1, Delta_1_rot );
1620 }
1621
1622 // Apply 'R' to the sky-sky block of the reduced supersky metric
1623 // g_nn = [g_na_na, 0; 0, g_nb_nb]
1624 // to compensate for the changes to the coordinate transform data.
1625 // The correct transformation to apply is:
1626 // Lambda ==> R * Lambda * R^T
1627 // where 'Lambda' re-introduces the supressed 'nc' coordinate dimension
1628 // Lambda = [g_na_na, 0, 0; 0, g_nb_nb, 0; 0, 0, 0]
1629 // but since 'R' is a rotation in the 'nc = 0' plane, the metric in
1630 // this dimension need not be known, and thus can be assumed to be zero.
1631 {
1632 const double g_na_na = gsl_matrix_get( rssky_metric, 0, 0 );
1633 const double g_nb_nb = gsl_matrix_get( rssky_metric, 1, 1 );
1634 const double g_na_na_rot = g_na_na * SQR( cos_phi ) + g_nb_nb * SQR( sin_phi );
1635 const double g_na_nb_rot = ( g_na_na - g_nb_nb ) * cos_phi * sin_phi;
1636 const double g_nb_nb_rot = g_na_na * SQR( sin_phi ) + g_nb_nb * SQR( cos_phi );
1637 gsl_matrix_set( rssky_metric, 0, 0, g_na_na_rot );
1638 gsl_matrix_set( rssky_metric, 0, 1, g_na_nb_rot );
1639 gsl_matrix_set( rssky_metric, 1, 0, g_na_nb_rot );
1640 gsl_matrix_set( rssky_metric, 1, 1, g_nb_nb_rot );
1641 }
1642
1643 // Get components of the vectors 'Q_na', 'Q_nb', and 'Q_nc' from coordinate transform data
1644 const double Q_na[3] = { gsl_matrix_get( &align_sky.matrix, 0, 0 ), gsl_matrix_get( &align_sky.matrix, 0, 1 ), gsl_matrix_get( &align_sky.matrix, 0, 2 ) };
1645 const double Q_nb[3] = { gsl_matrix_get( &align_sky.matrix, 1, 0 ), gsl_matrix_get( &align_sky.matrix, 1, 1 ), gsl_matrix_get( &align_sky.matrix, 1, 2 ) };
1646 const double Q_nc[3] = { gsl_matrix_get( &align_sky.matrix, 2, 0 ), gsl_matrix_get( &align_sky.matrix, 2, 1 ), gsl_matrix_get( &align_sky.matrix, 2, 2 ) };
1647
1648 // Determine the minimum and maximum right ascension and declination
1649 const double alphas[2] = { GSL_MIN( alpha1, alpha2 ), GSL_MAX( alpha1, alpha2 ) };
1650 const double deltas[2] = { GSL_MIN( delta1, delta2 ), GSL_MAX( delta1, delta2 ) };
1651
1652 // Create bound data for declination bounds, at constant minimum/maximum right ascension:
1653 // Given known 'na' from previous bound, and known minimum/maximum 'alpha', solve
1654 // na = ( Q_na[0]*cos(alpha) + Q_na[1]*sin(alpha) )*cos(delta) + Q_na[2]*sin(delta)
1655 // for
1656 // delta = asin( ( na - na0 ) / r ) - angle0
1657 // and compute
1658 // nb = C*cos(delta) + S*sin(delta) + Z
1659 PhysicalSkyBoundPiece const_alpha[2];
1660 for ( size_t i = 0; i < 2; ++i ) {
1661 XLAL_INIT_MEM( const_alpha[i] );
1662 const_alpha[i].type = 0;
1663 const double cos_alpha = cos( alphas[i] );
1664 const double sin_alpha = sin( alphas[i] );
1665 const double x = Q_na[2];
1666 const double y = Q_na[0] * cos_alpha + Q_na[1] * sin_alpha;
1667 const_alpha[i].na0 = 0;
1668 const_alpha[i].r = sqrt( SQR( x ) + SQR( y ) );
1669 const_alpha[i].angle0 = atan2( y, x );
1670 const_alpha[i].C = Q_nb[0] * cos_alpha + Q_nb[1] * sin_alpha;
1671 const_alpha[i].S = Q_nb[2];
1672 const_alpha[i].Z = 0;
1673 }
1674
1675 // Create bound data for right ascension bounds, at constant minimum/maximum declination:
1676 // Given known 'na' from previous bound, and known minimum/maximum 'delta', solve
1677 // na = ( Q_na[0]*cos(alpha) + Q_na[1]*sin(alpha) )*cos(delta) + Q_na[2]*sin(delta)
1678 // for
1679 // alpha = asin( ( na - na0 ) / r ) - angle0
1680 // and compute
1681 // nb = C*cos(alpha) + S*sin(alpha) + Z
1682 PhysicalSkyBoundPiece const_delta[2];
1683 for ( size_t j = 0; j < 2; ++j ) {
1684 XLAL_INIT_MEM( const_delta[j] );
1685 const_delta[j].type = 0;
1686 const double cos_delta = cos( deltas[j] );
1687 const double sin_delta = sin( deltas[j] );
1688 const double x = Q_na[1] * cos_delta;
1689 const double y = Q_na[0] * cos_delta;
1690 const_delta[j].na0 = Q_na[2] * sin_delta;
1691 const_delta[j].r = sqrt( SQR( x ) + SQR( y ) );
1692 const_delta[j].angle0 = atan2( y, x );
1693 const_delta[j].C = Q_nb[0] * cos_delta;
1694 const_delta[j].S = Q_nb[1] * cos_delta;
1695 const_delta[j].Z = Q_nb[2] * sin_delta;
1696 }
1697
1698 // Determine corner points in reduced supersky coordinate A of the region
1699 // '[min(alpha),max(alpha)] x [min(delta),max(delta)]'
1700 double corner_A[2][2], corner_B[2][2];
1701 for ( size_t i = 0; i < 2; ++i ) {
1702 for ( size_t j = 0; j < 2; ++j ) {
1703 double rssky_point[rssky_metric->size1];
1704 gsl_vector_view rssky_point_view = gsl_vector_view_array( rssky_point, rssky_metric->size1 );
1705 PulsarDopplerParams XLAL_INIT_DECL( phys_point );
1706 phys_point.Alpha = alphas[i];
1707 phys_point.Delta = deltas[j];
1708 XLAL_CHECK( XLALConvertPhysicalToSuperskyPoint( &rssky_point_view.vector, &phys_point, rssky_transf ) == XLAL_SUCCESS, XLAL_EFUNC );
1709 corner_A[i][j] = rssky_point[0];
1710 corner_B[i][j] = rssky_point[1];
1711 }
1712 }
1713
1714 // Use corner points to classify parameter space into different shapes and set bounds data
1715 data_lower.min_A = data_upper.min_A = GSL_NEGINF;
1716 data_lower.max_A = data_upper.max_A = GSL_POSINF;
1717 if ( corner_A[1][0] < 0 && corner_A[1][1] <= 0 ) {
1718
1719 if ( corner_A[1][1] < corner_A[1][0] ) {
1720
1721 // __________________________________________ Lower bound(s) on B:
1722 // B = 1_| _____ _____ | 0 = right ascension bound at max(delta) until max_A
1723 // | .-' '-. .-' '-. |
1724 // | .' '. .' '. | Upper bound(s) on B:
1725 // | / \ / \ | 0 = declination bound at max(alpha) until corner_A[1][0]
1726 // | ; __2_ ; ; | 1 = right ascension bound at min(delta) until corner_A[0][0]
1727 // 0-| | /####| | | | 2 = declination bound at min(alpha) until max_A
1728 // | ; _1-#####; ; ; |
1729 // | \ 0/#######/ / \ / |
1730 // | '. /###0##.' .' '. .' |
1731 // _| '-._____.-' '-._____.-' |
1732 // -1 |__.________.________.________.________.__|
1733 // ' ' ' ' '
1734 // A = -2 -1 0 1 2
1735 data_lower.min_A = data_upper.min_A = corner_A[1][1];
1736 data_lower.min_A_bound = data_upper.min_A_bound = corner_B[1][1];
1737 data_lower.max_A = data_upper.max_A = corner_A[0][1];
1738 data_lower.max_A_bound = data_upper.max_A_bound = corner_B[0][1];
1739 data_lower.pieces[0] = const_delta[1];
1740 data_lower.pieces[0].max_A = GSL_POSINF;
1741 data_upper.pieces[0] = const_alpha[1];
1742 data_upper.pieces[0].max_A = corner_A[1][0];
1743 data_upper.pieces[1] = const_delta[0];
1744 data_upper.pieces[1].max_A = corner_A[0][0];
1745 data_upper.pieces[2] = const_alpha[0];
1746 data_upper.pieces[2].max_A = GSL_POSINF;
1747 data_upper.pieces[2].altroot = true;
1748
1749 } else {
1750
1751 // __________________________________________ Lower bound(s) on B:
1752 // B = 1_| _____ _____ | 0 = declination bound at max(alpha) until corner_A[1][1]
1753 // | .-' '-. .-' '-. | 1 = right ascension bound at max(delta) until max_A
1754 // | .' '. .' '. |
1755 // | / \ / \ | Upper bound(s) on B:
1756 // | ; __1_ ; ; | 0 = right ascension bound at min(delta) until corner_A[0][0]
1757 // 0-| | 0/####| | | | 1 = declination bound at min(alpha) until max_A
1758 // | ; -#####; ; ; |
1759 // | \ 0\####/ / \ / |
1760 // | '. \1.' .' '. .' |
1761 // _| '-._____.-' '-._____.-' |
1762 // -1 |__.________.________.________.________.__|
1763 // ' ' ' ' '
1764 // A = -2 -1 0 1 2
1765 data_lower.min_A = data_upper.min_A = corner_A[1][0];
1766 data_lower.min_A_bound = data_upper.min_A_bound = corner_B[1][0];
1767 data_lower.max_A = data_upper.max_A = corner_A[0][1];
1768 data_lower.max_A_bound = data_upper.max_A_bound = corner_B[0][1];
1769 data_lower.pieces[0] = const_alpha[1];
1770 data_lower.pieces[0].max_A = corner_A[1][1];
1771 data_lower.pieces[0].altroot = true;
1772 data_lower.pieces[1] = const_delta[1];
1773 data_lower.pieces[1].max_A = GSL_POSINF;
1774 data_upper.pieces[0] = const_delta[0];
1775 data_upper.pieces[0].max_A = corner_A[0][0];
1776 data_upper.pieces[1] = const_alpha[0];
1777 data_upper.pieces[1].max_A = GSL_POSINF;
1778 data_upper.pieces[1].altroot = true;
1779
1780 }
1781
1782 } else if ( 0 <= corner_A[1][0] && 0 < corner_A[1][1] ) {
1783
1784 if ( corner_A[1][1] < corner_A[1][0] ) {
1785
1786 // __________________________________________ Lower bound(s) on B:
1787 // B = 1_| _____ _____ | 0 = right ascension bound at min(delta) until max_A
1788 // | .-' '-. .-' '-. |
1789 // | .' '. .' '. | Upper bound(s) on B:
1790 // | / \ / \ | 0 = declination bound at min(alpha) until corner_A[0][1]
1791 // | ; ; _0__ ; | 1 = right ascension bound at max(delta) until corner_A[1][1x]
1792 // 0-| | | |####\ | | 2 = declination bound at max(alpha) until max_A
1793 // | ; ; ;#####-1_ ; |
1794 // | \ / \ \#######\2 / |
1795 // | '. .' '. '.##0###\ .' |
1796 // _| '-._____.-' '-._____.-' |
1797 // -1 |__.________.________.________.________.__|
1798 // ' ' ' ' '
1799 // A = -2 -1 0 1 2
1800 data_lower.min_A = data_upper.min_A = corner_A[0][0];
1801 data_lower.min_A_bound = data_upper.min_A_bound = corner_B[0][0];
1802 data_lower.max_A = data_upper.max_A = corner_A[1][0];
1803 data_lower.max_A_bound = data_upper.max_A_bound = corner_B[1][0];
1804 data_lower.pieces[0] = const_delta[0];
1805 data_lower.pieces[0].max_A = GSL_POSINF;
1806 data_upper.pieces[0] = const_alpha[0];
1807 data_upper.pieces[0].max_A = corner_A[0][1];
1808 data_upper.pieces[1] = const_delta[1];
1809 data_upper.pieces[1].max_A = corner_A[1][1];
1810 data_upper.pieces[2] = const_alpha[1];
1811 data_upper.pieces[2].max_A = GSL_POSINF;
1812 data_upper.pieces[2].altroot = true;
1813
1814 } else {
1815
1816 // __________________________________________ Lower bound(s) on B:
1817 // B = 1_| _____ _____ | 0 = right ascension bound at min(delta) until corner_A[1][0]
1818 // | .-' '-. .-' '-. | 1 = declination bound at max(alpha) until max_A
1819 // | .' '. .' '. |
1820 // | / \ / \ | Upper bound(s) on B:
1821 // | ; ; _0__ ; | 0 = declination bound at min(alpha) until corner_A[0][1]
1822 // 0-| | | |####\1 | | 1 = right ascension bound at max(delta) until max_A
1823 // | ; ; ;#####- ; |
1824 // | \ / \ \####/1 / |
1825 // | '. .' '. \0.' .' |
1826 // _| '-._____.-' '-._____.-' |
1827 // -1 |__.________.________.________.________.__|
1828 // ' ' ' ' '
1829 // A = -2 -1 0 1 2
1830 data_lower.min_A = data_upper.min_A = corner_A[0][0];
1831 data_lower.min_A_bound = data_upper.min_A_bound = corner_B[0][0];
1832 data_lower.max_A = data_upper.max_A = corner_A[1][1];
1833 data_lower.max_A_bound = data_upper.max_A_bound = corner_B[1][1];
1834 data_lower.pieces[0] = const_delta[0];
1835 data_lower.pieces[0].max_A = corner_A[1][0];
1836 data_lower.pieces[1] = const_alpha[1];
1837 data_lower.pieces[1].max_A = GSL_POSINF;
1838 data_upper.pieces[0] = const_alpha[0];
1839 data_upper.pieces[0].max_A = corner_A[0][1];
1840 data_upper.pieces[1] = const_delta[1];
1841 data_upper.pieces[1].max_A = GSL_POSINF;
1842
1843 }
1844
1845 } else {
1846
1847 // This parameter space straddles both reduced supersky hemispheres
1848 // Find the value of 'na' where this occurs at max(alpha) by solving
1849 // nc = ( Q_nc[0]*cos(max(alpha)) + Q_nc[1]*sin(max(alpha)) )*cos(delta) + Q_nc[2]*sin(delta)
1850 // for delta, then computing
1851 // na = ( Q_na[0]*cos(alpha) + Q_na[1]*sin(alpha) )*cos(delta) + Q_na[2]*sin(delta)
1852 const double cos_alpha_split = cos( alphas[1] );
1853 const double sin_alpha_split = sin( alphas[1] );
1854 const double delta_split = -1 * atan2( Q_nc[0] * cos_alpha_split + Q_nc[1] * sin_alpha_split, Q_nc[2] );
1855 const double na_split = ( Q_na[0] * cos_alpha_split + Q_na[1] * sin_alpha_split ) * cos( delta_split ) + Q_na[2] * sin( delta_split );
1856 const double split_A[2] = { -1 - na_split, 1 + na_split };
1857 const double split_B = -1 * RE_SQRT( 1 - SQR( na_split ) );
1858
1859 if ( split_A[0] < corner_A[1][0] ) {
1860 if ( corner_A[1][1] < split_A[1] ) {
1861
1862 // __________________________________________ Lower bound(s) on B:
1863 // B = 1_| _____ _____ | 0 = lower circular bound until max_A
1864 // | .-' '-. .-' '-. |
1865 // | .' '. .' '. | Upper bound(s) on B:
1866 // | / \ / \ | 0 = declination bound at max(alpha) until corner_A[1][0]
1867 // | ; __2__;___3___ ; | 1 = right ascension bound at min(delta) until corner_A[0][0]
1868 // 0-| | /#####|#######\ | | 2 = declination bound at min(alpha) until A = 0
1869 // | ; ___1-######;########-4_ ; | 3 = declination bound at min(alpha) until corner_A[0][1]
1870 // | \ /##########/ \##########\ / | 4 = right ascension bound at max(delta) until corner_A[1][1]
1871 // | '.0/#########.' '.#########\5.' | 5 = declination bound at max(alpha) until max_A
1872 // _| '-.##0##.-' '-.##0##.-' |
1873 // -1 |__.________.________.________.________.__|
1874 // ' ' ' ' '
1875 // A = -2 -1 0 1 2
1876 data_lower.min_A = data_upper.min_A = split_A[0];
1877 data_lower.min_A_bound = data_upper.min_A_bound = split_B;
1878 data_lower.max_A = data_upper.max_A = split_A[1];
1879 data_lower.max_A_bound = data_upper.max_A_bound = split_B;
1880 data_lower.pieces[0] = lower_circular;
1881 data_lower.pieces[0].max_A = GSL_POSINF;
1882 data_upper.pieces[0] = const_alpha[1];
1883 data_upper.pieces[0].max_A = corner_A[1][0];
1884 data_upper.pieces[1] = const_delta[0];
1885 data_upper.pieces[1].max_A = corner_A[0][0];
1886 data_upper.pieces[2] = const_alpha[0];
1887 data_upper.pieces[2].max_A = 0;
1888 data_upper.pieces[2].altroot = true;
1889 data_upper.pieces[3] = const_alpha[0];
1890 data_upper.pieces[3].max_A = corner_A[0][1];
1891 data_upper.pieces[4] = const_delta[1];
1892 data_upper.pieces[4].max_A = corner_A[1][1];
1893 data_upper.pieces[5] = const_alpha[1];
1894 data_upper.pieces[5].max_A = GSL_POSINF;
1895 data_upper.pieces[5].altroot = true;
1896
1897 } else {
1898
1899 // __________________________________________ Lower bound(s) on B:
1900 // B = 1_| _____ _____ | 0 = lower circular bound until split_A[1]
1901 // | .-' '-. .-' '-. | 1 = declination bound at max(alpha) until max_A
1902 // | .' '. .' '. |
1903 // | / \ / \ | Upper bound(s) on B:
1904 // | ; __2__;___3___ ; | 0 = declination bound at max(alpha) until corner_A[1][0]
1905 // 0-| | /#####|#######\ | | 1 = right ascension bound at min(delta) until corner_A[0][0]
1906 // | ; ___1-######;########-4_ ; | 2 = declination bound at min(alpha) until A = 0
1907 // | \ /##########/ \##########/ / | 3 = declination bound at min(alpha) until corner_A[0][1]
1908 // | '.0/#########.' '.#######/1 .' | 4 = right ascension bound at max(delta) until max_A
1909 // _| '-.##0##.-' '-.#0#/_.-' |
1910 // -1 |__.________.________.________.________.__|
1911 // ' ' ' ' '
1912 // A = -2 -1 0 1 2
1913 data_lower.min_A = data_upper.min_A = split_A[0];
1914 data_lower.min_A_bound = data_upper.min_A_bound = split_B;
1915 data_lower.max_A = data_upper.max_A = corner_A[1][1];
1916 data_lower.max_A_bound = data_upper.max_A_bound = corner_B[1][1];
1917 data_lower.pieces[0] = lower_circular;
1918 data_lower.pieces[0].max_A = split_A[1];
1919 data_lower.pieces[1] = const_alpha[1];
1920 data_lower.pieces[1].max_A = GSL_POSINF;
1921 data_upper.pieces[0] = const_alpha[1];
1922 data_upper.pieces[0].max_A = corner_A[1][0];
1923 data_upper.pieces[1] = const_delta[0];
1924 data_upper.pieces[1].max_A = corner_A[0][0];
1925 data_upper.pieces[2] = const_alpha[0];
1926 data_upper.pieces[2].max_A = 0;
1927 data_upper.pieces[2].altroot = true;
1928 data_upper.pieces[3] = const_alpha[0];
1929 data_upper.pieces[3].max_A = corner_A[0][1];
1930 data_upper.pieces[4] = const_delta[1];
1931 data_upper.pieces[4].max_A = GSL_POSINF;
1932
1933 }
1934
1935 } else {
1936 if ( corner_A[1][1] < split_A[1] ) {
1937
1938 // __________________________________________ Lower bound(s) on B:
1939 // B = 1_| _____ _____ | 0 = declination bound at max(alpha) until split_A[0]
1940 // | .-' '-. .-' '-. | 1 = lower circular bound until split_A[1]
1941 // | .' '. .' '. |
1942 // | / \ / \ | Upper bound(s) on B:
1943 // | ; __1__;___2___ ; | 0 = right ascension bound at min(delta) until corner_A[0][0]
1944 // 0-| | /#####|#######\ | | 1 = declination bound at min(alpha) until A = 0
1945 // | ; ___0-######;########-3_ ; | 2 = declination bound at min(alpha) until corner_A[0][1]
1946 // | \ \##########/ \##########\ / | 3 = right ascension bound at max(delta) until corner_A[1][1]
1947 // | '. 0\#######.' '.#########\4.' | 4 = declination bound at max(alpha) until max_A
1948 // _| '-._'.#1.-' '-.##1##.-' |
1949 // -1 |__.________.________.________.________.__|
1950 // ' ' ' ' '
1951 // A = -2 -1 0 1 2
1952 data_lower.min_A = data_upper.min_A = corner_A[1][0];
1953 data_lower.min_A_bound = data_upper.min_A_bound = corner_B[1][0];
1954 data_lower.max_A = data_upper.max_A = split_A[1];
1955 data_lower.max_A_bound = data_upper.max_A_bound = split_B;
1956 data_lower.pieces[0] = const_alpha[1];
1957 data_lower.pieces[0].max_A = split_A[0];
1958 data_lower.pieces[0].altroot = true;
1959 data_lower.pieces[1] = lower_circular;
1960 data_lower.pieces[1].max_A = GSL_POSINF;
1961 data_upper.pieces[0] = const_delta[0];
1962 data_upper.pieces[0].max_A = corner_A[0][0];
1963 data_upper.pieces[1] = const_alpha[0];
1964 data_upper.pieces[1].max_A = 0;
1965 data_upper.pieces[1].altroot = true;
1966 data_upper.pieces[2] = const_alpha[0];
1967 data_upper.pieces[2].max_A = corner_A[0][1];
1968 data_upper.pieces[3] = const_delta[1];
1969 data_upper.pieces[3].max_A = corner_A[1][1];
1970 data_upper.pieces[4] = const_alpha[1];
1971 data_upper.pieces[4].max_A = GSL_POSINF;
1972 data_upper.pieces[4].altroot = true;
1973
1974 } else {
1975
1976 // __________________________________________ Lower bound(s) on B:
1977 // B = 1_| _____ _____ | 0 = declination bound at max(alpha) until split_A[0]
1978 // | .-' '-. .-' '-. | 1 = lower circular bound until split_A[1]
1979 // | .' '. .' '. | 2 = declination bound at max(alpha) until max_A
1980 // | / \ / \ |
1981 // | ; __1__;___2___ ; | Upper bound(s) on B:
1982 // 0-| | /#####|#######\ | | 0 = right ascension bound at min(delta) until corner_A[0][0]
1983 // | ; ___0-######;########-3_ ; | 1 = declination bound at min(alpha) until A = 0
1984 // | \ \##########/ \##########/ / | 2 = declination bound at min(alpha) until corner_A[0][1]
1985 // | '. 0\#######.' '.#######/2 .' | 3 = right ascension bound at max(delta) until max_A
1986 // _| '-._'.#1.-' '-.#1.'_.-' |
1987 // -1 |__.________.________.________.________.__|
1988 // ' ' ' ' '
1989 // A = -2 -1 0 1 2
1990 data_lower.min_A = data_upper.min_A = corner_A[1][0];
1991 data_lower.min_A_bound = data_upper.min_A_bound = corner_B[1][0];
1992 data_lower.max_A = data_upper.max_A = corner_A[1][1];
1993 data_lower.max_A_bound = data_upper.max_A_bound = corner_B[1][1];
1994 data_lower.pieces[0] = const_alpha[1];
1995 data_lower.pieces[0].max_A = split_A[0];
1996 data_lower.pieces[0].altroot = true;
1997 data_lower.pieces[1] = lower_circular;
1998 data_lower.pieces[1].max_A = split_A[1];
1999 data_lower.pieces[2] = const_alpha[1];
2000 data_lower.pieces[2].max_A = GSL_POSINF;
2001 data_upper.pieces[0] = const_delta[0];
2002 data_upper.pieces[0].max_A = corner_A[0][0];
2003 data_upper.pieces[1] = const_alpha[0];
2004 data_upper.pieces[1].max_A = 0;
2005 data_upper.pieces[1].altroot = true;
2006 data_upper.pieces[2] = const_alpha[0];
2007 data_upper.pieces[2].max_A = corner_A[0][1];
2008 data_upper.pieces[3] = const_delta[1];
2009 data_upper.pieces[3].max_A = GSL_POSINF;
2010
2011 }
2012 }
2013
2014 }
2015
2016 // Set the parameter-space bounds on reduced supersky sky coordinates A and B
2017 XLAL_CHECK( XLALSetLatticeTilingConstantBound( tiling, 0, data_lower.min_A, data_lower.max_A ) == XLAL_SUCCESS, XLAL_EFUNC );
2018 XLAL_CHECK( XLALSetLatticeTilingBound( tiling, 1, PhysicalSkyBound, sizeof( data_lower ), &data_lower, &data_upper ) == XLAL_SUCCESS, XLAL_EFUNC );
2020
2021 // Set the parameter-space origin on reduced supersky sky coordinates A and B
2024
2025 return XLAL_SUCCESS;
2026
2027}
2028
2029static double ConstantBoundB(
2030 const void *data,
2031 const size_t dim UNUSED,
2032 const gsl_matrix *cache UNUSED,
2033 const gsl_vector *point
2034)
2035{
2036
2037 // Get bounds data
2038 const double bound = *( ( const double * ) data );
2039
2040 // Decode the reduced supersky coordinates to get
2041 // na = as[0] = Q_na . n
2042 const double A = gsl_vector_get( point, 0 );
2043 const double rssky[2] = { A, 0 };
2044 gsl_vector_const_view rssky_view = gsl_vector_const_view_array( rssky, 2 );
2045 double as[3];
2046 SM_ReducedToAligned( as, &rssky_view.vector, A );
2047 const double na = as[0];
2048
2049 // Absolute limiting bound on 'nb = +/- sqrt(1 - na^2)'
2050 const double limit = RE_SQRT( 1 - SQR( na ) );
2051
2052 return GSL_MAX( -limit, GSL_MIN( bound, limit ) );
2053
2054}
2055
2057 double A1,
2058 void *params
2059)
2060{
2061
2062 // Get parameters
2063 const double target_area = ( ( double * ) params )[0];
2064
2065 // Compute area of unit disk to the left of 'A1'
2066 const double area = LAL_PI_2 + A1 * RE_SQRT( 1 - SQR( A1 ) ) + asin( A1 );
2067
2068 return area - target_area;
2069
2070}
2071
2073 double B1,
2074 void *params
2075)
2076{
2077
2078 // Get parameters
2079 const double target_area = ( ( double * ) params )[0];
2080 double A0 = ( ( double * ) params )[1];
2081 double A1 = ( ( double * ) params )[2];
2082
2083 // Compute area of unit disk between 'A0' and 'A1'
2084 const double max_area = A1 * RE_SQRT( 1 - SQR( A1 ) ) - A0 * RE_SQRT( 1 - SQR( A0 ) ) + asin( A1 ) - asin( A0 );
2085
2086 // Work out where '-|B1| = const' line intersects unit disk boundary
2087 const double Ai = RE_SQRT( 1 - SQR( B1 ) );
2088
2089 // Restrict range '[A0, A1]' if '-|B1| = const' line intersects unit disk boundary within it
2090 if ( A0 < -Ai ) {
2091 A0 = -Ai;
2092 }
2093 if ( A1 > Ai ) {
2094 A1 = Ai;
2095 }
2096
2097 // Compute area of unit disk between 'A0' and 'A1' and below '-|B1|'
2098 double area = -fabs( B1 ) * ( A1 - A0 ) + 0.5 * ( A1 * RE_SQRT( 1 - SQR( A1 ) ) - A0 * RE_SQRT( 1 - SQR( A0 ) ) + asin( A1 ) - asin( A0 ) );
2099
2100 // For positive B, substract 'area' from 'max_area'
2101 if ( B1 > 0 ) {
2102 area = max_area - area;
2103 }
2104
2105 return area - target_area;
2106
2107}
2108
2110 LatticeTiling *tiling,
2111 const gsl_matrix *rssky_metric,
2112 const double max_mismatch,
2113 const UINT4 patch_count,
2114 const UINT4 patch_index
2115)
2116{
2117
2118 // Check input
2119 XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
2120 XLAL_CHECK( rssky_metric->size1 == rssky_metric->size2, XLAL_EINVAL );
2121 XLAL_CHECK( rssky_metric->size1 >= 2, XLAL_EINVAL );
2122 XLAL_CHECK( gsl_matrix_get( rssky_metric, 0, 1 ) == 0, XLAL_EINVAL );
2123 XLAL_CHECK( gsl_matrix_get( rssky_metric, 1, 0 ) == 0, XLAL_EINVAL );
2124 XLAL_CHECK( max_mismatch > 0, XLAL_EINVAL );
2125 XLAL_CHECK( patch_count > 0, XLAL_EINVAL );
2126 XLAL_CHECK( patch_count == 1 || patch_count % 2 == 0, XLAL_EINVAL, "'patch_count' must be either one or even" );
2127 XLAL_CHECK( patch_index < patch_count, XLAL_EINVAL );
2128
2129 // Set parameter-space bound names
2132
2133 // Parameter-space bounds on reduced supersky sky coordinates A and B
2134 double A_bound[2] = {GSL_NAN, GSL_NAN};
2135 double B_bound[2] = {-1, 1};
2136
2137 // Start with default padding on parameter-space bounds on coordinates A and B
2138 double A_lower_bbox_pad = -1, A_upper_bbox_pad = -1;
2139 double B_lower_bbox_pad = -1, B_upper_bbox_pad = -1;
2140
2141 // Handle special cases of 1 and 2 patches
2142 if ( patch_count <= 2 ) {
2143 A_bound[0] = -2 + 4 * ( ( double ) patch_index ) / ( ( double ) patch_count );
2144 A_bound[1] = A_bound[0] + 4 / ( ( double ) patch_count );
2145 } else {
2146
2147 // Number of patches per hemisphere for odd number of patches
2148 const UINT4 hemi_patch_count = patch_count / 2;
2149
2150 // Index of patch within hemisphere; increases to 'hemi_patch_count' then decreases to zero
2151 const UINT4 hemi_patch_index = patch_index < hemi_patch_count ? patch_index : patch_count - patch_index - 1;
2152
2153 // Minimum number of patch divisions in supersky coordinate A within one hemisphere
2154 // - Prevents patches from being too squashed in either A or B, which lead to overcoverage (i.e. the
2155 // sum of points over all sky patches being much larger than the number of points in the whole sky)
2156 const UINT4 min_A_count = ceil( sqrt( hemi_patch_count ) );
2157
2158 // Maximum extent of metric ellipse bounding box in supersky coordinate B
2159 const double max_B_bbox = 2 * sqrt( max_mismatch / gsl_matrix_get( rssky_metric, 1, 1 ) );
2160
2161 // Target number of patch divisions in supersky coordinate B
2162 const double target_B_count = 1.0 / ( 1.0 * max_B_bbox );
2163
2164 // Number of patch divisions in supersky coordinate A within one hemisphere
2165 const UINT4 A_count = GSL_MIN( min_A_count, ceil( hemi_patch_count / target_B_count ) );
2166
2167 // Minimum number of patch divisions in supersky coordinate B
2168 const UINT4 min_B_count = hemi_patch_count / A_count;
2169
2170 // Excess number of patches, which must be added on to get 'hemi_patch_count'
2171 INT4 patch_excess = hemi_patch_count - A_count * min_B_count;
2172 XLAL_CHECK( patch_excess >= 0, XLAL_EFAILED );
2173
2174 // Initialise number of patch divisions in 'B'; if there are excess patches, add an extra patch
2175 UINT4 B_count = min_B_count;
2176 if ( patch_excess > 0 ) {
2177 ++B_count;
2178 }
2179
2180 // Calculate range of indices in 'A', and number of patch divisions and index in 'B'.
2181 // - The divisions in 'A' are set in proportion to the range of 'A_index', i.e. the number of
2182 // divisions in 'B' for that range of 'A_index'. This is so that, if 'patch_excess' is
2183 // not zero, and therefore the number of divisions in 'B' is not constant, patch areas should
2184 // still be equal. Example:
2185 // hemi_patch_count=7 hemi_patch_index=0 | A_index=0--3 B_count=3 B_index=0
2186 // hemi_patch_count=7 hemi_patch_index=1 | A_index=0--3 B_count=3 B_index=1
2187 // hemi_patch_count=7 hemi_patch_index=2 | A_index=0--3 B_count=3 B_index=2
2188 // hemi_patch_count=7 hemi_patch_index=3 | A_index=3--5 B_count=2 B_index=0
2189 // hemi_patch_count=7 hemi_patch_index=4 | A_index=3--5 B_count=2 B_index=1
2190 // hemi_patch_count=7 hemi_patch_index=5 | A_index=5--7 B_count=2 B_index=0
2191 // hemi_patch_count=7 hemi_patch_index=6 | A_index=5--7 B_count=2 B_index=1
2192 UINT4 A_index[2] = {0, B_count};
2193 UINT4 B_index = hemi_patch_index;
2194 while ( B_index >= B_count ) {
2195
2196 // Decrease index in 'B'; we are done when 'B_index' < 'B_count'
2197 B_index -= B_count;
2198
2199 // Decrease number of excess patches; if zero, subtract extra patch from patch divisions in 'B'
2200 --patch_excess;
2201 if ( patch_excess == 0 ) {
2202 --B_count;
2203 }
2204
2205 // Store the current last 'A' index in 'A_index[0]', and increase
2206 // 'A_index[1]' by the current number of patch divisions in 'B'
2207 A_index[0] = A_index[1];
2208 A_index[1] += B_count;
2209
2210 }
2211
2212 // Decide which patches to add padding to (-1 denotes default padding)
2213 A_lower_bbox_pad = ( A_index[0] == 0 && patch_index < hemi_patch_count ) ? -1 : 0;
2214 A_upper_bbox_pad = ( A_index[0] == 0 && patch_index >= hemi_patch_count ) ? -1 : 0;
2215 B_lower_bbox_pad = ( B_index == 0 ) ? -1 : 0;
2216 B_upper_bbox_pad = ( B_index + 1 == B_count ) ? -1 : 0;
2217
2218 // Allocate a GSL root solver
2219 gsl_root_fsolver *fs = gsl_root_fsolver_alloc( gsl_root_fsolver_brent );
2220 XLAL_CHECK( fs != NULL, XLAL_ENOMEM );
2221
2222 // Find bounds on 'A' corresponding to the computed indexes
2223 for ( size_t i = 0; i < 2; ++i ) {
2224 const UINT4 A_index_i = A_index[i];
2225
2226 // Handle boundaries as special cases
2227 if ( A_index_i == 0 ) {
2228 A_bound[i] = -1;
2229 } else if ( A_index_i == hemi_patch_count ) {
2230 A_bound[i] = 1;
2231 } else {
2232
2233 // Calculate the target area of unit disk
2234 const double target_area = LAL_PI * ( ( double ) A_index_i ) / ( ( ( double ) patch_count ) / 2 );
2235
2236 // Set up GSL root solver
2237 double params[] = { target_area };
2238 gsl_function F = { .function = EqualAreaSkyBoundSolverA, .params = params };
2239 double A_lower = -1, A_upper = 1;
2240 XLAL_CHECK( gsl_root_fsolver_set( fs, &F, A_lower, A_upper ) == 0, XLAL_EFAILED );
2241
2242 // Try to find root
2243 int status = 0, iter = 0;
2244 do {
2245 XLAL_CHECK( gsl_root_fsolver_iterate( fs ) == 0, XLAL_EFAILED );
2246 A_lower = gsl_root_fsolver_x_lower( fs );
2247 A_upper = gsl_root_fsolver_x_upper( fs );
2248 status = gsl_root_test_interval( A_lower, A_upper, 1e-5, 1e-5 );
2249 } while ( status == GSL_CONTINUE && ++iter < 1000 );
2250 XLAL_CHECK( status == GSL_SUCCESS, XLAL_EMAXITER, "Could not find bound for A_index[%zu]=%i; best guess [%g, %g]", i, A_index_i, A_lower, A_upper );
2251
2252 // Store bound
2253 A_bound[i] = gsl_root_fsolver_root( fs );
2254
2255 }
2256
2257 }
2258
2259 // Find bounds on 'B' corresponding to the computed indexes
2260 for ( size_t i = 0; i < 2; ++i ) {
2261 const UINT4 B_index_i = B_index + i;
2262
2263 // Maximum possible value of 'B' within the region bound by 'A_bound'
2264 // - For a single patch in 'A', 'B' must span the entire unit disk
2265 double B_max = ( A_count == 1 ) ? 1 : RE_SQRT( 1 - GSL_MIN( SQR( A_bound[0] ), SQR( A_bound[1] ) ) );
2266
2267 // Handle boundaries as special cases
2268 if ( B_index_i == 0 ) {
2269 B_bound[i] = -B_max;
2270 } else if ( B_index_i == B_count ) {
2271 B_bound[i] = B_max;
2272 } else {
2273
2274 // Calculate the target area of unit disk
2275 const double A0 = A_bound[0];
2276 const double A1 = A_bound[1];
2277 const double target_area = ( A1 * RE_SQRT( 1 - SQR( A1 ) ) - A0 * RE_SQRT( 1 - SQR( A0 ) ) + asin( A1 ) - asin( A0 ) ) * ( ( double ) B_index_i ) / ( ( double ) B_count );
2278
2279 // Set up GSL root solver
2280 double params[] = { target_area, A0, A1 };
2281 gsl_function F = { .function = EqualAreaSkyBoundSolverB, .params = params };
2282 if ( EqualAreaSkyBoundSolverB( -B_max, params ) * EqualAreaSkyBoundSolverB( B_max, params ) > 0 ) {
2283 // [-B_max, B_max] does not bracket root, so use B_max = 1
2284 B_max = 1;
2285 }
2286 double B_lower = -B_max, B_upper = B_max;
2287 XLAL_CHECK( gsl_root_fsolver_set( fs, &F, B_lower, B_upper ) == 0, XLAL_EFAILED );
2288
2289 // Try to find root
2290 int status = 0, iter = 0;
2291 do {
2292 XLAL_CHECK( gsl_root_fsolver_iterate( fs ) == 0, XLAL_EFAILED );
2293 B_lower = gsl_root_fsolver_x_lower( fs );
2294 B_upper = gsl_root_fsolver_x_upper( fs );
2295 status = gsl_root_test_interval( B_lower, B_upper, 1e-5, 1e-5 );
2296 } while ( status == GSL_CONTINUE && ++iter < 1000 );
2297 XLAL_CHECK( status == GSL_SUCCESS, XLAL_EMAXITER, "Could not find bound for B_index[%zu]=%i; best guess [%g, %g]", i, B_index_i, B_lower, B_upper );
2298
2299 // Store bound
2300 B_bound[i] = gsl_root_fsolver_root( fs );
2301
2302 }
2303
2304 }
2305
2306 // Restrict range 'A' if 'B = const' bounds intersect unit disk boundary within it
2307 // - Only start to do this when there are 3 patches in 'B' direction
2308 // - Do not do this for the middle 'B' patch which straddles 'B = 0'
2309 if ( B_count >= 3 && B_index != ( B_count - 1 ) / 2 ) {
2310 const double Ai = RE_SQRT( 1 - GSL_MIN( SQR( B_bound[0] ), SQR( B_bound[1] ) ) );
2311 if ( A_bound[0] < -Ai ) {
2312 A_bound[0] = -Ai;
2313 }
2314 if ( A_bound[1] > Ai ) {
2315 A_bound[1] = Ai;
2316 }
2317 }
2318
2319 // Shift bounds on 'A' into the correct hemisphere, and put in correct order
2320 if ( patch_index < hemi_patch_count ) { // This patch is in the left hemisphere
2321 A_bound[0] = A_bound[0] - 1;
2322 A_bound[1] = A_bound[1] - 1;
2323 } else { // This patch is in the left hemisphere
2324 A_bound[0] = -A_bound[0] + 1;
2325 A_bound[1] = -A_bound[1] + 1;
2326 }
2327
2328 // Cleanup
2329 gsl_root_fsolver_free( fs );
2330
2331 }
2332
2333 // Set the parameter-space bounds on reduced supersky sky coordinates A and B
2334 XLAL_CHECK( XLALSetLatticeTilingConstantBound( tiling, 0, A_bound[0], A_bound[1] ) == XLAL_SUCCESS, XLAL_EFUNC );
2335 XLAL_CHECK( XLALSetLatticeTilingBound( tiling, 1, ConstantBoundB, sizeof( B_bound[0] ), &B_bound[0], &B_bound[1] ) == XLAL_SUCCESS, XLAL_EFUNC );
2337
2338 // Set the parameter-space origin on reduced supersky sky coordinates A and B
2341
2342 // Set the parameter-space padding control flags for reduced supersky sky coordinates A and B (-1 denotes default padding)
2343 XLAL_CHECK( XLALSetLatticeTilingPadding( tiling, 0, A_lower_bbox_pad, A_upper_bbox_pad, -1, -1, -1 ) == XLAL_SUCCESS, XLAL_EFUNC );
2344 XLAL_CHECK( XLALSetLatticeTilingPadding( tiling, 1, B_lower_bbox_pad, B_upper_bbox_pad, -1, -1, -1 ) == XLAL_SUCCESS, XLAL_EFUNC );
2345
2346 return XLAL_SUCCESS;
2347
2348}
2349
2350static double PhysicalSpinBound(
2351 const void *data,
2352 const size_t dim UNUSED,
2353 const gsl_matrix *cache,
2354 const gsl_vector *point UNUSED
2355)
2356{
2357
2358 // Get bounds data
2359 const double *sky_offsets = ( ( const double * ) data );
2360 double bound = ( ( const double * ) data )[3];
2361
2362 // Add the inner product of the sky offsets with the aligned sky
2363 // position to the physical bound to get the reduced supersky bound
2364 for ( size_t i = 0; i < 3; ++i ) {
2365 bound += sky_offsets[i] * gsl_matrix_get( cache, 1, i );
2366 }
2367
2368 return bound;
2369
2370}
2371
2373 LatticeTiling *tiling,
2374 const SuperskyTransformData *rssky_transf,
2375 const size_t s,
2376 const double bound1,
2377 const double bound2
2378)
2379{
2380
2381 // Check input
2382 XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
2383 XLAL_CHECK( CHECK_RSSKY_TRANSF( rssky_transf ), XLAL_EINVAL );
2384 XLAL_CHECK( isfinite( bound1 ), XLAL_EINVAL );
2385 XLAL_CHECK( isfinite( bound2 ), XLAL_EINVAL );
2386 XLAL_CHECK( s <= rssky_transf->SMAX, XLAL_ESIZE );
2387
2388 // Decompose coordinate transform data
2389 gsl_matrix_const_view sky_offsets = gsl_matrix_const_view_array( &rssky_transf->sky_offsets[0][0], rssky_transf->nsky_offsets, 3 );
2390
2391 // Set parameter-space bound name
2393
2394 // Copy the sky offset vector to bounds data
2395 double data_lower[4], data_upper[4];
2396 for ( size_t j = 0; j < 3; ++j ) {
2397 data_lower[j] = data_upper[j] = gsl_matrix_get( &sky_offsets.matrix, RSSKY_FKDOT_OFFSET( rssky_transf, s ), j );
2398 }
2399 data_lower[3] = GSL_MIN( bound1, bound2 );
2400 data_upper[3] = GSL_MAX( bound1, bound2 );
2401
2402 // Set the parameter-space bound on physical frequency/spindown coordinate
2403 XLAL_CHECK( XLALSetLatticeTilingBound( tiling, RSSKY_FKDOT_DIM( rssky_transf, s ), PhysicalSpinBound, sizeof( data_lower ), &data_lower, &data_upper ) == XLAL_SUCCESS, XLAL_EFUNC );
2404
2405 // Supersky metric requires searching over spindown if also searching over sky
2406 const int tiled_sskyA = XLALIsTiledLatticeTilingDimension( tiling, 0 );
2407 XLAL_CHECK( tiled_sskyA != XLAL_FAILURE, XLAL_EFUNC );
2408 const int tiled_sskyB = XLALIsTiledLatticeTilingDimension( tiling, 1 );
2409 XLAL_CHECK( tiled_sskyB != XLAL_FAILURE, XLAL_EFUNC );
2410 const int tiled_fkdot = XLALIsTiledLatticeTilingDimension( tiling, RSSKY_FKDOT_DIM( rssky_transf, s ) );
2411 XLAL_CHECK( tiled_fkdot != XLAL_FAILURE, XLAL_EFUNC );
2412 XLAL_CHECK( !( tiled_sskyA || tiled_sskyB ) || tiled_fkdot, XLAL_EINVAL, "Must search over %zu-order spindown if also searching over sky", s );
2413
2414 return XLAL_SUCCESS;
2415
2416}
2417
2419 LatticeTiling *tiling,
2420 const SuperskyTransformData *rssky_transf,
2421 const size_t s,
2422 const bool padding
2423)
2424{
2425
2426 // Check input
2427 XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
2428 XLAL_CHECK( CHECK_RSSKY_TRANSF( rssky_transf ), XLAL_EINVAL );
2429 XLAL_CHECK( s <= rssky_transf->SMAX, XLAL_ESIZE );
2430
2431 // Set padding (-1 denotes default padding)
2432 const double bbox_pad = padding ? -1 : 0;
2433 XLAL_CHECK( XLALSetLatticeTilingPadding( tiling, RSSKY_FKDOT_DIM( rssky_transf, s ), bbox_pad, bbox_pad, -1, -1, -1 ) == XLAL_SUCCESS, XLAL_EFUNC );
2434
2435 return XLAL_SUCCESS;
2436
2437}
2438
2440 const bool first_call,
2441 const LatticeTiling *tiling,
2442 const LatticeTilingIterator *itr,
2443 const gsl_vector *point,
2444 const size_t changed_i,
2445 const void *param,
2446 void *out
2447)
2448{
2449
2450 // Only care about changes in sky position
2451 if ( 2 <= changed_i ) {
2452 return XLAL_SUCCESS;
2453 }
2454
2455 // Get callback data
2456 const SM_CallbackParam *cparam = ( ( const SM_CallbackParam * ) param );
2457 SM_CallbackOut *cout = ( ( SM_CallbackOut * ) out );
2458
2459 // Initialise translation data
2460 if ( first_call ) {
2461 XLAL_INIT_MEM( cout->min_phys );
2462 XLAL_INIT_MEM( cout->max_phys );
2463 cout->min_phys.refTime = cparam->rssky_transf->ref_time;
2464 cout->max_phys.refTime = cparam->rssky_transf->ref_time;
2465 cout->min_phys.Alpha = GSL_POSINF;
2466 cout->max_phys.Alpha = GSL_NEGINF;
2467 cout->min_phys.Delta = GSL_POSINF;
2468 cout->max_phys.Delta = GSL_NEGINF;
2469 for ( size_t s = 0; s <= cparam->rssky_transf->SMAX; ++s ) {
2470 cout->min_phys.fkdot[s] = GSL_POSINF;
2471 cout->max_phys.fkdot[s] = GSL_NEGINF;
2472 }
2473 }
2474
2475 // Convert point from reduced supersky coordinates to physical coordinates
2478
2479 // Store minimum/maximum values of physical sky position
2480 cout->min_phys.Alpha = GSL_MIN( cout->min_phys.Alpha, phys.Alpha );
2481 cout->max_phys.Alpha = GSL_MAX( cout->max_phys.Alpha, phys.Alpha );
2482 cout->min_phys.Delta = GSL_MIN( cout->min_phys.Delta, phys.Delta );
2483 cout->max_phys.Delta = GSL_MAX( cout->max_phys.Delta, phys.Delta );
2484
2485 for ( size_t s = 0; s <= cparam->rssky_transf->SMAX; ++s ) {
2486
2487 // Get indexes of left/right-most point in current frequency block
2488 INT4 left = 0, right = 0;
2490
2491 // Get step size
2492 const double step = XLALLatticeTilingStepSize( tiling, RSSKY_FKDOT_DIM( cparam->rssky_transf, s ) );
2493
2494 // Store minimum/maximum values of physical frequency and spindowns
2496 cout->min_phys.fkdot[s] = GSL_MIN( cout->min_phys.fkdot[s], phys.fkdot[s] + step * ( left - 1 ) );
2497 cout->max_phys.fkdot[s] = GSL_MAX( cout->max_phys.fkdot[s], phys.fkdot[s] + step * ( right + 1 ) );
2498 } else {
2499 cout->min_phys.fkdot[s] = cout->max_phys.fkdot[s] = phys.fkdot[s];
2500 }
2501
2502 }
2503
2504 return XLAL_SUCCESS;
2505
2506}
2507
2509 LatticeTiling *tiling,
2510 const SuperskyTransformData *rssky_transf,
2511 const PulsarDopplerParams **min_phys,
2512 const PulsarDopplerParams **max_phys
2513)
2514{
2515
2516 // Check input
2517 XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
2518 XLAL_CHECK( CHECK_RSSKY_TRANSF( rssky_transf ), XLAL_EINVAL );
2519 XLAL_CHECK( min_phys != NULL, XLAL_EFAULT );
2520 XLAL_CHECK( max_phys != NULL, XLAL_EFAULT );
2521
2522 // Register callback function
2523 const SM_CallbackParam param = {
2524 .rssky_transf = rssky_transf,
2525 };
2526 const SM_CallbackOut *out = XLALRegisterLatticeTilingCallback( tiling, SM_LatticePhysicalRangeCallback, sizeof( param ), &param, sizeof( *out ) );
2527 XLAL_CHECK( out != NULL, XLAL_EFUNC );
2528
2529 // Set output parameters
2530 *min_phys = &out->min_phys;
2531 *max_phys = &out->max_phys;
2532
2533 return XLAL_SUCCESS;
2534
2535}
2536
2538 const bool first_call,
2539 const LatticeTiling *tiling,
2540 const LatticeTilingIterator *itr,
2541 const gsl_vector *point,
2542 const size_t changed_i,
2543 const void *param,
2544 void *out
2545)
2546{
2547
2548 // Only care about changes in sky position
2549 if ( 2 <= changed_i ) {
2550 return XLAL_SUCCESS;
2551 }
2552
2553 // Get callback data
2554 const SM_CallbackParam *cparam = ( ( const SM_CallbackParam * ) param );
2555 SM_CallbackOut *cout = ( ( SM_CallbackOut * ) out );
2556
2557 // Initialise translation data
2558 if ( first_call ) {
2559 cout->min_rssky2_view = gsl_vector_view_array( cout->min_rssky2_array, cparam->rssky2_transf->ndim );
2560 cout->max_rssky2_view = gsl_vector_view_array( cout->max_rssky2_array, cparam->rssky2_transf->ndim );
2561 gsl_vector_set_all( &cout->min_rssky2_view.vector, GSL_POSINF );
2562 gsl_vector_set_all( &cout->max_rssky2_view.vector, GSL_NEGINF );
2563 }
2564
2565 // Convert point from reduced supersky coordinates to other reduced supersky coordinates
2566 double rssky2_array[cparam->rssky_transf->ndim];
2567 gsl_vector_view rssky2_view = gsl_vector_view_array( rssky2_array, cparam->rssky2_transf->ndim );
2568 XLAL_CHECK( XLALConvertSuperskyToSuperskyPoint( &rssky2_view.vector, cparam->rssky2_transf, point, point, cparam->rssky_transf ) == XLAL_SUCCESS, XLAL_EFUNC );
2569
2570 // Store minimum/maximum values of other reduced supersky sky coordinates
2571 for ( size_t i = 0; i < 2; ++i ) {
2572 cout->min_rssky2_array[i] = GSL_MIN( cout->min_rssky2_array[i], rssky2_array[i] );
2573 cout->max_rssky2_array[i] = GSL_MAX( cout->max_rssky2_array[i], rssky2_array[i] );
2574 }
2575
2576 for ( size_t i = 2; i < cparam->rssky2_transf->ndim; ++i ) {
2577
2578 // Get indexes of left/right-most point in current frequency block
2579 INT4 left = 0, right = 0;
2581
2582 // Get step size
2583 const double step = XLALLatticeTilingStepSize( tiling, i );
2584
2585 // Store minimum/maximum values of other reduced supersky frequency and spindown coordinates
2587 cout->min_rssky2_array[i] = GSL_MIN( cout->min_rssky2_array[i], rssky2_array[i] + step * ( left - 1 ) );
2588 cout->max_rssky2_array[i] = GSL_MAX( cout->max_rssky2_array[i], rssky2_array[i] + step * ( right + 1 ) );
2589 } else {
2590 cout->min_rssky2_array[i] = cout->max_rssky2_array[i] = rssky2_array[i];
2591 }
2592
2593 }
2594
2595 return XLAL_SUCCESS;
2596
2597}
2598
2600 LatticeTiling *tiling,
2601 const SuperskyTransformData *rssky_transf,
2602 const SuperskyTransformData *rssky2_transf,
2603 const gsl_vector **min_rssky2,
2604 const gsl_vector **max_rssky2
2605)
2606{
2607
2608 // Check input
2609 XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
2610 XLAL_CHECK( CHECK_RSSKY_TRANSF( rssky_transf ), XLAL_EINVAL );
2611 XLAL_CHECK( CHECK_RSSKY_TRANSF( rssky2_transf ), XLAL_EINVAL );
2612 XLAL_CHECK( min_rssky2 != NULL, XLAL_EFAULT );
2613 XLAL_CHECK( max_rssky2 != NULL, XLAL_EFAULT );
2614
2615 // Register callback function
2616 const SM_CallbackParam param = {
2617 .rssky_transf = rssky_transf,
2618 .rssky2_transf = rssky2_transf,
2619 };
2620 const SM_CallbackOut *out = XLALRegisterLatticeTilingCallback( tiling, SM_LatticeSuperskyRangeCallback, sizeof( param ), &param, sizeof( *out ) );
2621 XLAL_CHECK( out != NULL, XLAL_EFUNC );
2622 XLAL_CHECK( rssky2_transf->ndim <= XLAL_NUM_ELEM( out->min_rssky2_array ), XLAL_EFAILED );
2623 XLAL_CHECK( rssky2_transf->ndim <= XLAL_NUM_ELEM( out->max_rssky2_array ), XLAL_EFAILED );
2624
2625 // Set output parameters
2626 *min_rssky2 = &out->min_rssky2_view.vector;
2627 *max_rssky2 = &out->max_rssky2_view.vector;
2628
2629 return XLAL_SUCCESS;
2630
2631}
2632
2634 LatticeTiling *tiling,
2635 const gsl_vector *min_rssky,
2636 const gsl_vector *max_rssky
2637)
2638{
2639
2640 // Check input
2641 XLAL_CHECK( tiling != NULL, XLAL_EFAULT );
2642 const size_t n = XLALTotalLatticeTilingDimensions( tiling );
2643 XLAL_CHECK( min_rssky != NULL, XLAL_EFAULT );
2644 XLAL_CHECK( min_rssky->size == n, XLAL_EINVAL );
2645 XLAL_CHECK( max_rssky != NULL, XLAL_EFAULT );
2646 XLAL_CHECK( max_rssky->size == n, XLAL_EINVAL );
2647
2648 // Set the parameter-space bounds on reduced supersky sky coordinates A and B
2649 double A_bound[2] = { gsl_vector_get( min_rssky, 0 ), gsl_vector_get( max_rssky, 0 ) };
2650 double B_bound[2] = { gsl_vector_get( min_rssky, 1 ), gsl_vector_get( max_rssky, 1 ) };
2651 XLAL_CHECK( XLALSetLatticeTilingConstantBound( tiling, 0, A_bound[0], A_bound[1] ) == XLAL_SUCCESS, XLAL_EFUNC );
2652 XLAL_CHECK( XLALSetLatticeTilingBound( tiling, 1, ConstantBoundB, sizeof( B_bound[0] ), &B_bound[0], &B_bound[1] ) == XLAL_SUCCESS, XLAL_EFUNC );
2654
2655 // Set the parameter-space bounds on all other coordinates
2656 for ( size_t j = 2; j < n; ++j ) {
2657 XLAL_CHECK( XLALSetLatticeTilingConstantBound( tiling, j, gsl_vector_get( min_rssky, j ), gsl_vector_get( max_rssky, j ) ) == XLAL_SUCCESS, XLAL_EFUNC );
2658 }
2659
2660 return XLAL_SUCCESS;
2661
2662}
2663
2664// Local Variables:
2665// c-file-style: "linux"
2666// c-basic-offset: 2
2667// End:
int XLALFITSArrayOpenWrite(FITSFile UNUSED *file, const CHAR UNUSED *name, const size_t UNUSED ndim, const size_t UNUSED dims[], const CHAR UNUSED *comment)
Definition: FITSFileIO.c:1587
int XLALFITSTableWriteRow(FITSFile UNUSED *file, const void UNUSED *record)
Definition: FITSFileIO.c:2550
int XLALFITSTableReadRow(FITSFile UNUSED *file, void UNUSED *record, UINT8 UNUSED *rem_nrows)
Definition: FITSFileIO.c:2621
int XLALFITSArrayReadGSLMatrix(FITSFile UNUSED *file, const size_t UNUSED idx[], gsl_matrix UNUSED **elems)
Definition: FITSFileIO.c:2118
int XLALFITSArrayOpenRead2(FITSFile UNUSED *file, const CHAR UNUSED *name, size_t UNUSED *dim0, size_t UNUSED *dim1)
Definition: FITSFileIO.c:1723
int XLALFITSArrayOpenRead(FITSFile UNUSED *file, const CHAR UNUSED *name, size_t UNUSED *ndim, size_t UNUSED dims[])
Definition: FITSFileIO.c:1634
int XLALFITSTableOpenWrite(FITSFile UNUSED *file, const CHAR UNUSED *name, const CHAR UNUSED *comment)
Definition: FITSFileIO.c:2162
int XLALFITSArrayOpenWrite2(FITSFile UNUSED *file, const CHAR UNUSED *name, const size_t UNUSED dim0, const size_t UNUSED dim1, const CHAR UNUSED *comment)
Definition: FITSFileIO.c:1710
int XLALFITSArrayWriteGSLMatrix(FITSFile UNUSED *file, const size_t UNUSED idx[], const gsl_matrix UNUSED *elems)
Definition: FITSFileIO.c:2070
int XLALFITSTableOpenRead(FITSFile UNUSED *file, const CHAR UNUSED *name, UINT8 UNUSED *nrows)
Definition: FITSFileIO.c:2198
int j
#define c
int XLALSetLatticeTilingBoundName(LatticeTiling *tiling, const size_t dim, const char *fmt,...)
UINT2 A
Definition: SFTnaming.c:46
UINT2 B
Definition: SFTnaming.c:47
#define MAX_SKY_OFFSETS
static int SM_LatticePhysicalRangeCallback(const bool first_call, const LatticeTiling *tiling, const LatticeTilingIterator *itr, const gsl_vector *point, const size_t changed_i, const void *param, void *out)
static void SkyBoundCache(const size_t dim UNUSED, const gsl_vector *point, gsl_vector *cache)
static double PhysicalSkyBound(const void *data, const size_t dim UNUSED, const gsl_matrix *cache UNUSED, const gsl_vector *point)
#define DOT3(x, y)
static double PhysicalSpinBound(const void *data, const size_t dim UNUSED, const gsl_matrix *cache, const gsl_vector *point UNUSED)
#define SMAX
#define RSSKY_FKDOT_DIM(RT, S)
const double fiducial_calc_freq
Fiducial frequency at which to numerically calculate metrics, which are then rescaled to user-request...
static int SM_ComputeReducedSuperskyMetric(gsl_matrix **rssky_metric, SuperskyTransformData **rssky_transf, const size_t spindowns, const gsl_matrix *ussky_metric, const DopplerCoordinateSystem *ucoords, const gsl_matrix *orbital_metric, const DopplerCoordinateSystem *ocoords, const LIGOTimeGPS *ref_time, const LIGOTimeGPS *start_time, const LIGOTimeGPS *end_time)
Compute the reduced supersky metric.
#define CHECK_RSSKY_TRANSF(RT)
static int SM_ComputeFittedSuperskyMetric(gsl_matrix *fitted_ssky_metric, SuperskyTransformData *rssky_transf, const gsl_matrix *ussky_metric, const gsl_matrix *orbital_metric, const DopplerCoordinateSystem *ocoords, const LIGOTimeGPS *start_time, const LIGOTimeGPS *end_time)
Find a least-squares linear fit to the orbital X and Y metric elements using the frequency and spindo...
#define CHECK_RSSKY_METRIC_TRANSF(M, RT)
static double ConstantBoundB(const void *data, const size_t dim UNUSED, const gsl_matrix *cache UNUSED, const gsl_vector *point)
static double EqualAreaSkyBoundSolverA(double A1, void *params)
static void SM_ReducedToAligned(double as[3], const gsl_vector *rss, const double hemi)
Convert from 2-dimensional reduced supersky coordinates to 3-dimensional aligned sky coordinates.
#define RE_SQRT(x)
#define RSSKY_FKDOT_OFFSET(RT, S)
static double EqualAreaSkyBoundSolverB(double B1, void *params)
#define SQR(x)
static int SM_ScaleSuperskyMetricFiducialFreq(gsl_matrix *rssky_metric, SuperskyTransformData *rssky_transf, const double new_fiducial_freq)
Scale a given supersky metric and its coordinate transform data to a new fiducial frequency.
static int SM_ComputeAlignedSuperskyMetric(gsl_matrix *aligned_ssky_metric, SuperskyTransformData *rssky_transf, const gsl_matrix *decoupled_ssky_metric)
Align the sky–sky block of the decoupled supersky metric with its eigenvalues by means of a rotation.
static int fits_table_init_SuperskyTransformData(FITSFile *file)
Initialise a FITS table for writing/reading a table of SuperskyTransformData entries.
static int SM_LatticeSuperskyRangeCallback(const bool first_call, const LatticeTiling *tiling, const LatticeTilingIterator *itr, const gsl_vector *point, const size_t changed_i, const void *param, void *out)
static int SM_ComputeDecoupledSuperskyMetric(gsl_matrix *decoupled_ssky_metric, SuperskyTransformData *rssky_transf, const gsl_matrix *fitted_ssky_metric)
Decouple the sky–sky and freq+spin–freq+spin blocks of the fitted supersky metric.
static void SM_AlignedToReduced(gsl_vector *rss, const double as[3])
Convert from 3-dimensional aligned sky coordinates to 2-dimensional reduced supersky coordinates.
#define DOT2(x, y)
static gsl_matrix * SM_ComputePhaseMetric(const DopplerCoordinateSystem *coords, const LIGOTimeGPS *ref_time, const LIGOTimeGPS *start_time, const LIGOTimeGPS *end_time, const MultiLALDetector *detectors, const MultiNoiseFloor *detector_weights, const DetectorMotionType detector_motion, const EphemerisData *ephemerides)
Call XLALComputeDopplerPhaseMetric() to compute the phase metric for a given coordinate system.
const double scale
multiplicative scaling factor of the coordinate
double e
int XLALExtrapolatePulsarSpins(PulsarSpins fkdot1, const PulsarSpins fkdot0, REAL8 dtau)
Extrapolate the Pulsar spin-parameters (fkdot0) from the initial reference-epoch to the new referen...
#define XLAL_FITS_TABLE_COLUMN_BEGIN(record_type)
Definition: FITSFileIO.h:239
#define FFIO_MAX
Maximum possible number of FITS array dimensions, and FITS table columns.
Definition: FITSFileIO.h:49
#define XLAL_FITS_TABLE_COLUMN_ADD(file, type, field)
Definition: FITSFileIO.h:243
struct tagFITSFile FITSFile
Representation of a FITS file.
Definition: FITSFileIO.h:54
#define XLAL_FITS_TABLE_COLUMN_ADD_ARRAY2(file, type, field)
Definition: FITSFileIO.h:255
#define LAL_PI_2
#define LAL_COSIEARTH
#define LAL_PI
#define LAL_TWOPI
#define LAL_SINIEARTH
#define XLAL_NUM_ELEM(x)
#define XLAL_INIT_MEM(x)
uint64_t UINT8
double REAL8
#define XLAL_INIT_DECL(var,...)
uint32_t UINT4
int32_t INT4
void * XLALCalloc(size_t m, size_t n)
void XLALFree(void *p)
int XLALIsTiledLatticeTilingDimension(const LatticeTiling *tiling, const size_t dim)
Return >0 if a lattice tiling dimension is tiled (i.e.
int XLALSetLatticeTilingBound(LatticeTiling *tiling, const size_t dim, const LatticeTilingBound func, const size_t data_len, const void *data_lower, const void *data_upper)
Set a parameter-space bound on a dimension of the lattice tiling.
size_t XLALTotalLatticeTilingDimensions(const LatticeTiling *tiling)
Return the total number of dimensions of the lattice tiling.
int XLALSetLatticeTilingConstantBound(LatticeTiling *tiling, const size_t dim, const double bound1, const double bound2)
Set a constant lattice tiling parameter-space bound, given by the minimum and maximum of the two supp...
REAL8 XLALLatticeTilingStepSize(const LatticeTiling *tiling, const size_t dim)
Return the step size of the lattice tiling in a given dimension, or 0 for non-tiled dimensions.
int XLALSetLatticeTilingBoundCacheFunction(LatticeTiling *tiling, const size_t dim, const LatticeTilingBoundCache func)
Set bound cache function for a lattice tiling parameter-space dimension.
int XLALCurrentLatticeTilingBlock(const LatticeTilingIterator *itr, const size_t dim, INT4 *left, INT4 *right)
Return indexes of the left-most and right-most points in the current block of points in the given dim...
int XLALSetLatticeTilingOrigin(LatticeTiling *tiling, const size_t dim, const double origin)
Set the physical parameter-space origin of the lattice tiling in the given dimension.
int XLALSetLatticeTilingPadding(LatticeTiling *tiling, const size_t dim, const double lower_bbox_pad, const double upper_bbox_pad, const int lower_intp_pad, const int upper_intp_pad, const int find_bound_extrema)
Control the padding of lattice tiling parameter-space bounds in the given dimension.
const void * XLALRegisterLatticeTilingCallback(LatticeTiling *tiling, const LatticeTilingCallback func, const size_t param_len, const void *param, const size_t out_len)
Register a callback function which can be used to compute properties of a lattice tiling.
void LogPrintf(LogLevel_t, const char *format,...) _LAL_GCC_PRINTF_FORMAT_(2
LOG_DEBUG
int XLALChangeMetricReferenceTime(gsl_matrix **p_gpr_ij, gsl_matrix **p_transform, const gsl_matrix *g_ij, const DopplerCoordinateSystem *coordSys, const double Dtau)
Compute the transform which changes the metric reference time .
Definition: MetricUtils.c:578
int XLALDiagNormalizeMetric(gsl_matrix **p_gpr_ij, gsl_matrix **p_transform, const gsl_matrix *g_ij)
Diagonally-normalize a metric .
Definition: MetricUtils.c:391
double XLALMetricDeterminant(const gsl_matrix *g_ij)
Compute the determinant of a metric .
Definition: MetricUtils.c:98
int XLALTransformMetric(gsl_matrix **p_gpr_ij, const gsl_matrix *transform, const gsl_matrix *g_ij)
Apply a transform to a metric such that , or equivalently .
Definition: MetricUtils.c:286
static const INT4 r
int XLALSegListInit(LALSegList *seglist)
int XLALSegListClear(LALSegList *seglist)
int XLALSegListIsInitialized(const LALSegList *seglist)
int XLALSegListAppend(LALSegList *seglist, const LALSeg *seg)
int XLALSegSet(LALSeg *seg, const LIGOTimeGPS *start, const LIGOTimeGPS *end, const INT4 id)
void XLALNormalizeSkyPosition(double *restrict longitude, double *restrict latitude)
int XLALConvertSuperskyToPhysicalPoints(gsl_matrix **out_phys, const gsl_matrix *in_rssky, const SuperskyTransformData *rssky_transf)
Convert a set of points from supersky to physical coordinates.
int XLALSetSuperskyPhysicalSkyBounds(LatticeTiling *tiling, gsl_matrix *rssky_metric, SuperskyTransformData *rssky_transf, const double alpha1, const double alpha2, const double delta1, const double delta2)
Set parameter-space bounds on the physical sky position for a lattice tiling using the reduced super...
void XLALDestroySuperskyMetrics(SuperskyMetrics *metrics)
Destroy a SuperskyMetrics struct.
SuperskyMetrics * XLALCopySuperskyMetrics(const SuperskyMetrics *metrics)
Copy a SuperskyMetrics struct.
int XLALSetSuperskyEqualAreaSkyBounds(LatticeTiling *tiling, const gsl_matrix *rssky_metric, const double max_mismatch, const UINT4 patch_count, const UINT4 patch_index)
Divide the reduced supersky parameter space into patch_count equal-area patches, and set parameter-sp...
int XLALConvertPhysicalToSuperskyPoints(gsl_matrix **out_rssky, const gsl_matrix *in_phys, const SuperskyTransformData *rssky_transf)
Convert a set of points from physical to supersky coordinates.
int XLALSuperskyMetricsDimensions(const SuperskyMetrics *metrics, size_t *spindowns)
Return dimensions of the supersky metrics.
SuperskyMetrics * XLALComputeSuperskyMetrics(const SuperskyMetricType type, const size_t spindowns, const LIGOTimeGPS *ref_time, const LALSegList *segments, const double fiducial_freq, const MultiLALDetector *detectors, const MultiNoiseFloor *detector_weights, const DetectorMotionType detector_motion, const EphemerisData *ephemerides)
Compute the supersky metrics, which are returned in a SuperskyMetrics struct.
int XLALEqualizeReducedSuperskyMetricsFreqSpacing(SuperskyMetrics *metrics, const double coh_max_mismatch, const double semi_max_mismatch)
Project and rescale the reduced supersky metrics in the frequency dimension, such that all reduced su...
SuperskyMetricType
Type of supersky metric to compute.
int XLALScaleSuperskyMetricsFiducialFreq(SuperskyMetrics *metrics, const double new_fiducial_freq)
Scale all supersky metrics and their coordinate transform data to a new fiducial frequency.
int XLALSetPhysicalPointSuperskyRefTime(PulsarDopplerParams *out_phys, const SuperskyTransformData *rssky_transf)
Set the reference time of a physical point to that of the reduced supersky coordinates.
int XLALConvertPhysicalToSuperskyPoint(gsl_vector *out_rssky, const PulsarDopplerParams *in_phys, const SuperskyTransformData *rssky_transf)
Convert a point from physical to supersky coordinates.
int XLALConvertSuperskyToPhysicalPoint(PulsarDopplerParams *out_phys, const gsl_vector *in_rssky, const gsl_vector *ref_rssky, const SuperskyTransformData *rssky_transf)
Convert a point from supersky to physical coordinates.
int XLALFITSWriteSuperskyMetrics(FITSFile *file, const SuperskyMetrics *metrics)
Write a SuperskyMetrics struct to a FITS file.
int XLALSetSuperskyRangeBounds(LatticeTiling *tiling, const gsl_vector *min_rssky, const gsl_vector *max_rssky)
Set parameter-space bounds on an entire lattice tiling given minimum and maximum ranges in reduced su...
int XLALRegisterSuperskyLatticePhysicalRangeCallback(LatticeTiling *tiling, const SuperskyTransformData *rssky_transf, const PulsarDopplerParams **min_phys, const PulsarDopplerParams **max_phys)
Register a lattice tiling callback function which computes the physical range covered by a reduced su...
int XLALConvertSuperskyToSuperskyPoint(gsl_vector *out_rssky, const SuperskyTransformData *out_rssky_transf, const gsl_vector *in_rssky, const gsl_vector *ref_rssky, const SuperskyTransformData *in_rssky_transf)
Convert a point between supersky coordinates.
int XLALSetSuperskyPhysicalSpinBoundPadding(LatticeTiling *tiling, const SuperskyTransformData *rssky_transf, const size_t s, const bool padding)
Set parameter-space bound padding on the physical frequency/spindowns for a lattice tiling using the...
int XLALSetSuperskyPhysicalSpinBound(LatticeTiling *tiling, const SuperskyTransformData *rssky_transf, const size_t s, const double bound1, const double bound2)
Set parameter-space bounds on the physical frequency/spindowns for a lattice tiling using the reduce...
int XLALRegisterSuperskyLatticeSuperskyRangeCallback(LatticeTiling *tiling, const SuperskyTransformData *rssky_transf, const SuperskyTransformData *rssky2_transf, const gsl_vector **min_rssky2, const gsl_vector **max_rssky2)
Register a lattice tiling callback function which computes the range covered by a reduced supersky la...
int XLALFITSReadSuperskyMetrics(FITSFile *file, SuperskyMetrics **metrics)
Read a SuperskyMetrics struct from a FITS file.
@ MAX_METRIC_TYPE
DetectorMotionType
Bitfield of different types of detector-motion to use in order to compute the Doppler-metric.
int XLALFindDopplerCoordinateInSystem(const DopplerCoordinateSystem *coordSys, const DopplerCoordinateID coordID)
Given a coordinate ID 'coordID', return its dimension within the given coordinate system 'coordSys',...
void XLALDestroyDopplerPhaseMetric(DopplerPhaseMetric *metric)
Free a DopplerPhaseMetric structure.
DopplerPhaseMetric * XLALComputeDopplerPhaseMetric(const DopplerMetricParams *metricParams, const EphemerisData *edat)
Calculate an approximate "phase-metric" with the specified parameters.
@ DOPPLERCOORD_N3X_EQU
X component of unconstrained super-sky position in equatorial coordinates [Units: none].
@ DOPPLERCOORD_N3OY_ECL
Y orbit-component of unconstrained super-sky position in ecliptic coordinates [Units: none].
@ DOPPLERCOORD_N3Z_EQU
Z component of unconstrained super-sky position in equatorial coordinates [Units: none].
@ DOPPLERCOORD_F2DOT
Second spindown [Units: Hz/s^2].
@ DOPPLERCOORD_N3Y_EQU
Y component of unconstrained super-sky position in equatorial coordinates [Units: none].
@ DOPPLERCOORD_F1DOT
First spindown [Units: Hz/s].
@ DOPPLERCOORD_F4DOT
Fourth spindown [Units: Hz/s^4].
@ DOPPLERCOORD_F3DOT
Third spindown [Units: Hz/s^3].
@ DOPPLERCOORD_N3OX_ECL
X orbit-component of unconstrained super-sky position in ecliptic coordinates [Units: none].
@ DOPPLERCOORD_FREQ
Frequency [Units: Hz].
#define XLAL_CHECK(assertion,...)
#define XLAL_CHECK_NULL(assertion,...)
XLAL_ENOMEM
XLAL_SUCCESS
XLAL_EFAULT
XLAL_ERANGE
XLAL_EFUNC
XLAL_EMAXITER
XLAL_ESIZE
XLAL_EIO
XLAL_EINVAL
XLAL_EFAILED
XLAL_FAILURE
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
REAL8 XLALGPSDiff(const LIGOTimeGPS *t1, const LIGOTimeGPS *t0)
float data[BLOCKSIZE]
Definition: hwinject.c:360
list y
start_time
n
out
LALDetector detectors[LAL_NUM_DETECTORS]
double alpha
type describing a Doppler coordinate system: lists the number of dimensions and the symbolic names of...
meta-info specifying a Doppler-metric
Struct to hold the output of XLALComputeDopplerPhaseMetric(), including meta-info on the number of di...
This structure contains all information about the center-of-mass positions of the Earth and Sun,...
LIGOTimeGPS end
LIGOTimeGPS start
UINT4 length
LALSeg * segs
array of detectors definitions 'LALDetector'
array of detector-specific 'noise floors' (ie PSD values), assumed constant over the frequency-band o...
PhysicalSkyBoundPiece pieces[6]
Type containing the 'Doppler-parameters' affecting the time-evolution of the phase.
PulsarSpins fkdot
Intrinsic spins: [Freq, f1dot, f2dot, ... ] where fkdot = d^kFreq/dt^k.
REAL8 Delta
Sky position: DEC (latitude) in equatorial coords and radians.
LIGOTimeGPS refTime
Reference time of pulsar parameters (in SSB!)
REAL8 Alpha
Sky position: RA (longitude) in equatorial coords and radians.
PulsarDopplerParams min_phys
Minimum physical range.
gsl_vector_view max_rssky2_view
double min_rssky2_array[32]
Minimum range of other reduced supersky coordinates.
double max_rssky2_array[32]
Maximum range of other reduced supersky coordinates.
gsl_vector_view min_rssky2_view
PulsarDopplerParams max_phys
Maximum physical range.
const SuperskyTransformData * rssky_transf
Reduced supersky coordinate transform data.
const SuperskyTransformData * rssky2_transf
Other reduced supersky coordinate transform data.
Computed supersky metrics, returned by XLALComputeSuperskyMetrics().
gsl_matrix * semi_rssky_metric
Semicoherent reduced supersky metric (2-dimensional sky)
gsl_matrix ** coh_rssky_metric
Coherent reduced supersky metric (2-dimensional sky) for each segment.
size_t num_segments
Number of segments.
SuperskyTransformData * semi_rssky_transf
Semicoherent reduced supersky metric coordinate transform data.
SuperskyTransformData ** coh_rssky_transf
Coherent reduced supersky metric coordinate transform data for each segment.
Supersky metric coordinate transform data.
UINT4 nspins
Number of spindown dimensions.
UINT4 ndim
Dimensions of the corresponding metric.
UINT4 nsky_offsets
Number of sky offsets.
REAL8 fiducial_freq
Fiducial frequency of metric.
REAL8 sky_offsets[MAX_SKY_OFFSETS][3]
Sky offsets of the supersky metric.
REAL8 align_sky[3][3]
Alignment transform of the supersky metric.
LIGOTimeGPS ref_time
Reference time of the metric.