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