Triumvirate C++ API 0.5.0.post1.dev301+g026f21751
Three-point clustering measurements in large-scale structure analyses.
Loading...
Searching...
No Matches
fftlog.cpp
Go to the documentation of this file.
1// Triumvirate: Three-Point Clustering Measurements in LSS
2//
3// Copyright (C) 2023 Mike S Wang & Naonori S Sugiyama [GPL-3.0-or-later]
4//
5// This file is part of the Triumvirate program. See the COPYRIGHT
6// and LICENCE files at the top-level directory of this distribution
7// for details of copyright and licensing.
8//
9// This program is free software: you can redistribute it and/or modify it
10// under the terms of the GNU General Public License as published by
11// the Free Software Foundation, either version 3 of the License, or
12// (at your option) any later version.
13//
14// This program is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17// See the GNU General Public License for more details.
18//
19// You should have received a copy of the GNU General Public License
20// along with this program. If not, see <https://www.gnu.org/licenses/>.
21
28
29#include "fftlog.hpp"
30
31namespace trva = trv::array;
32namespace trvm = trv::maths;
33namespace trvs = trv::sys;
34
35namespace trv {
36
37namespace maths {
38
39HankelTransform::HankelTransform(double mu, double q, bool threaded) {
40 // Check transform parameters.
41 if (
42 ((mu + 1. + q) <= 0. && std::floor(mu + 1. + q) == (mu + 1. + q)) ||
43 ((mu + 1. - q) <= 0. && std::floor(mu + 1. - q) == (mu + 1. - q))
44 ) {
46 "Invalid values of order `mu` and bias `q`: "
47 "negative poles in the gamma function."
48 );
49 }
50
51 this->order = mu;
52 this->bias = q;
53
54 this->threaded = threaded;
55
56#if defined(TRV_USE_OMP) && defined(TRV_USE_FFTWOMP)
57 // Initialise FFTW multithreading.
58 if ((!trvs::is_gpu_enabled()) && this->threaded) {
59 fftw_init_threads();
60 fftw_plan_with_nthreads(omp_get_max_threads());
61 }
62#endif // TRV_USE_OMP && TRV_USE_FFTWOMP
63}
64
66 this->reset();
67}
68
69void HankelTransform::reset() {
70 if (this->plan_init) {
72 // Destroy GPU FFT buffers/descriptors and plans.
73#if defined(TRV_USE_HIP)
74 HIP_EXEC(hipFree(this->d_buffer));
75 HIPFFT_EXEC(hipfftDestroy(this->plan_gpu));
76#elif defined(TRV_USE_CUDA) // !TRV_USE_HIP && TRV_USE_CUDA
77 CUDA_EXEC(cudaFree(this->d_buffer));
78 CUFFT_EXEC(cufftDestroy(this->plan_gpu));
79 #ifdef _CUDA_STREAM
80 // Destroy CUDA streams.
81 CUDA_EXEC(cudaStreamDestroy(this->custream));
82 #endif // _CUDA_STREAM
83#endif // TRV_USE_HIP
84 } else {
85 // Destroy CPU FFT plans.
86 fftw_destroy_plan(this->pre_plan);
87 fftw_destroy_plan(this->post_plan);
88 }
89 this->plan_init = false;
90 }
91
92 if (this->pre_buffer != nullptr) {
93 fftw_free(this->pre_buffer);
94 this->pre_buffer = nullptr;
95 }
96 if (this->post_buffer != nullptr) {
97 fftw_free(this->post_buffer);
98 this->post_buffer = nullptr;
99 }
100}
101
103 std::vector<double> sample_pts, double kr_c, bool lowring,
104 trva::ExtrapOption extrap, double extrap_exp
105) {
106 // Initialise pre-sample points.
107 if (sample_pts.size() < 2) {
109 "The number of sample points must be at least 2."
110 );
111 }
112 if (trva::check_1d_array(sample_pts, false, true, false) != 0) {
114 "The sample points are not logarithmically spaced."
115 );
116 }
117
118 this->pre_sampts = sample_pts;
119 this->nsamp = sample_pts.size();
120 this->logres =
121 std::log(sample_pts.back() / sample_pts.front()) / (this->nsamp - 1);
122
123 this->extrap = extrap;
124 if (this->extrap != trva::ExtrapOption::NONE) {
125 if (this->nsamp % 2 != 0) {
127 "The number of sample points must be even for extrapolation."
128 );
129 }
130 this->nsamp_trans = std::pow(
131 2, std::ceil(std::log2(extrap_exp * this->nsamp))
132 );
133 if (this->nsamp_trans < this->nsamp) {
135 "The sample size expansion factor results in a shrunken sample size."
136 );
137 }
138
139 this->n_ext = (this->nsamp_trans - this->nsamp) / 2;
141 this->pre_sampts, this->n_ext, this->pre_sampts_extrap
142 );
143 } else {
144 this->nsamp_trans = this->nsamp;
145 this->n_ext = 0;
146 }
147
148 // Initialise the transform.
149 if (lowring) {
150 this->pivot = this->calc_lowring_pivot(this->logres, kr_c);
151 } else {
152 if (kr_c <= 0.) {
153 throw trvs::InvalidParameterError("Pivot value must be positive.");
154 }
155 this->pivot = kr_c;
156 }
157
158 this->kernel = this->compute_kernel_coeff();
159
160 // Initialise post-sample points.
161 // Note the end-point of the periodic interval is not included in
162 // the sample points, thus the shift in the product from the pivot
163 // by one sample point.
164 double kr_aprod = this->pivot * std::exp(- this->logres);
165
166 this->post_sampts.resize(this->nsamp);
167 for (int j = 0; j < this->nsamp; j++) {
168 this->post_sampts[j] = kr_aprod / this->pre_sampts[this->nsamp - j - 1];
169 }
170
171 if (this->extrap != trva::ExtrapOption::NONE) {
172 this->post_sampts_extrap.resize(this->nsamp_trans);
173 for (int j = 0; j < this->nsamp_trans; j++) {
174 this->post_sampts_extrap[j] =
175 kr_aprod / this->pre_sampts[this->nsamp_trans - j - 1];
176 }
177 }
178
179 // ---->
180 // // Alternative to the for-loop block above, note that k_c and r_c are
181 // // both exp(L/2) times `k0` and `r0`.
182 // double kr_0 = kr_aprod * std::exp(- this->nsamp * this->logres);
183 // double r0 = this->pre_sampts[0];
184 // double k0 = kr_0 / r0;
185 // ...
186 // ----<
187
188 // Initialise FFT(W) plans.
189 this->reset();
190
191 std::vector<int> gpus = trvs::get_gpu_ids();
192 if (trvs::is_gpu_enabled()) {
193#ifdef _CUDA_STREAM
194 CUDA_EXEC(cudaStreamCreate(&this->custream));
195#endif // _CUDA_STREAM
196 }
197
198 this->pre_buffer = fftw_alloc_complex(this->nsamp_trans);
199
200 if (trvs::is_gpu_enabled()) {
201#if defined(TRV_USE_HIP)
202 HIPFFT_EXEC(hipfftPlan1d(
203 &this->plan_gpu, this->nsamp_trans,
204 HIPFFT_Z2Z, 1
205 ));
206 HIP_EXEC(hipMalloc(
207 &this->d_buffer, sizeof(fft_double_complex) * this->nsamp_trans
208 ));
209#elif defined(TRV_USE_CUDA) // !TRV_USE_HIP && TRV_USE_CUDA
210 CUFFT_EXEC(cufftCreate(&this->plan_gpu));
211
212 #ifdef _CUDA_STREAM
213 CUFFT_EXEC(cufftSetStream(this->plan_gpu, this->custream));
214 #endif // _CUDA_STREAM
215
216 std::size_t workspace_sizes[gpus.size()];
217 CUFFT_EXEC(cufftMakePlan1d(
218 this->plan_gpu, this->nsamp_trans,
219 CUFFT_Z2Z, 1, workspace_sizes
220 ));
221
222 CUDA_EXEC(cudaMalloc(
223 &this->d_buffer, sizeof(fft_double_complex) * this->nsamp_trans
224 ));
225#endif // TRV_USE_HIP
226 } else {
227 this->pre_plan = fftw_plan_dft_1d(
228 this->nsamp_trans, this->pre_buffer, this->pre_buffer,
229 FFTW_FORWARD, FFTW_ESTIMATE
230 );
231 }
232
233 this->post_buffer = fftw_alloc_complex(this->nsamp_trans);
234
235 if (trvs::is_gpu_enabled()) {
236 } else {
237 this->post_plan = fftw_plan_dft_1d(
238 this->nsamp_trans, this->post_buffer, this->post_buffer,
239 FFTW_FORWARD, FFTW_ESTIMATE
240 );
241 }
242
243 this->plan_init = true;
244}
245
247 std::vector<double> sample_pts, double kr_c, bool lowring,
248 int extrap, double extrap_exp
249) {
250 this->initialise(
251 sample_pts, kr_c, lowring, trva::ExtrapOption(extrap), extrap_exp
252 );
253}
254
255double HankelTransform::calc_lowring_pivot(double delta, double kr_c) {
256 // STYLE: Standard naming convention is not followed below.
257 double mu = this->order;
258 double q = this->bias;
259 double dL = delta;
260
261 double x_p = (mu + 1. + q)/2.;
262 double x_m = (mu + 1. - q)/2.;
263 double y = M_PI / (2.*dL);
264
265 if (kr_c > 0.) {
266 // Note that no minus sign is involved in the phase by
267 // complex conjugation of the gamma function.
268 double lnr_p, lnr_m, phi_p, phi_m;
269 trvm::get_lngamma_parts(x_p, y, lnr_p, phi_p);
270 trvm::get_lngamma_parts(x_m, y, lnr_m, phi_m);
271
272 double argphase = std::log(2./kr_c) / dL + (phi_p + phi_m) / M_PI;
273
274 kr_c *= std::exp(dL * (argphase - std::round(argphase)));
275 } else {
276 // Alternatively, simply set the value as follows.
277 std::complex<double> gamma_lnratio = eval_gamma_lnratio(mu, q + 2*y*M_I);
278
279 double lnkr_c = std::log(2) + gamma_lnratio.imag() / (2*y);
280
281 kr_c = std::exp(lnkr_c - std::floor(lnkr_c / dL) * dL);
282 }
283
284 return kr_c;
285}
286
287std::vector< std::complex<double> > HankelTransform::compute_kernel_coeff() {
288 // STYLE: Standard naming convention is not followed below.
289 double mu = this->order;
290 double q = this->bias;
291 int N_trans = this->nsamp_trans;
292 double dL = this->logres;
293 double kr_c = this->pivot;
294
295 if (N_trans <= 0 || dL <= 0. || kr_c <= 0.) {
296 throw std::runtime_error(
297 "This instance of trv::maths::HankelTransform has not been "
298 "initialised with `initialise`."
299 );
300 }
301
302 double x_p = (mu + 1. + q)/2.;
303 double x_m = (mu + 1. - q)/2.;
304 double x_eq = (mu + 1.)/2.;
305
306 double y = M_PI / (N_trans * dL);
307
308 double t = -2. * y * std::log(kr_c/2.);
309
310 // Note that no minus sign is involved in the phase by
311 // complex conjugation of the gamma function.
312 std::vector< std::complex<double> > u(N_trans);
313 if (q == 0.) {
314 for (int m = 0; m <= N_trans/2; m++) {
315 double lnr_, phi_eq;
316 trvm::get_lngamma_parts(x_eq, m*y, lnr_, phi_eq);
317
318 u[m] = trvm::eval_complex_in_polar(1., m*t + 2*phi_eq);
319 }
320 } else {
321 double qln2 = q * std::log(2.);
322 for (int m = 0; m <= N_trans/2; m++) {
323 double lnr_p, phi_p;
324 double lnr_m, phi_m;
325 trvm::get_lngamma_parts(x_p, m*y, lnr_p, phi_p);
326 trvm::get_lngamma_parts(x_m, m*y, lnr_m, phi_m);
327
329 std::exp(qln2 + lnr_p - lnr_m), m*t + phi_p + phi_m
330 );
331 }
332 }
333
334 for (int m = N_trans/2 + 1; m < N_trans; m++) {
335 u[m] = conj(u[N_trans - m]);
336 }
337
338 if (N_trans % 2 == 0) {
339 // Make the mid-point real by log-periodicity.
340 u[N_trans/2] = u[N_trans/2].real() + trvm::M_I * 0.;
341 }
342
343 return u;
344}
345
347 std::complex<double>* a, std::complex<double>* b
348) {
349 // STYLE: Standard naming convention is not followed below.
350 int N = this->nsamp;
351 int N_trans = this->nsamp_trans;
352
353 if (this->kernel.empty() || N <= 2 || N_trans <= 2) {
354 throw std::runtime_error(
355 "This instance of trv::maths::HankelTransform has not been "
356 "initialised with `initialise`."
357 );
358 }
359
360 // Perform any extrapolation required.
361 if (this->extrap == trva::ExtrapOption::NONE) {
362 for (int j = 0; j < N_trans; j++) {
363 this->pre_buffer[j][0] = (a[j]).real();
364 this->pre_buffer[j][1] = (a[j]).imag();
365 this->post_buffer[j][0] = 0.;
366 this->post_buffer[j][1] = 0.;
367 }
368 } else {
369 // Assume reality with extrapolation.
370 std::vector<double> a_vec(N);
371 for (int j = 0; j < N; j++) {
372 a_vec[j] = a[j].real();
373 }
374
375 std::vector<double> a_trans_vec(N_trans);
376 switch (this->extrap) {
378 break;
381 a_vec, this->n_ext, 0., 0., a_trans_vec
382 );
383 break;
386 a_vec, this->n_ext, a_vec.front(), a_vec.back(), a_trans_vec
387 );
388 break;
390 trva::extrap_lin(a_vec, this->n_ext, a_trans_vec);
391 break;
393 trva::extrap_loglin(a_vec, this->n_ext, a_trans_vec);
394 break;
395 default:
396 throw trvs::InvalidParameterError("Unsupported extrapolation option.");
397 }
398
399 for (int j = 0; j < N_trans; j++) {
400 this->pre_buffer[j][0] = a_trans_vec[j];
401 this->pre_buffer[j][1] = 0.;
402 this->post_buffer[j][0] = 0.;
403 this->post_buffer[j][1] = 0.;
404 }
405 }
406
407 // Compute the convolution b = a * u using FFT.
408 if (trvs::is_gpu_enabled()) {
409#if defined(TRV_USE_HIP)
410 trva::copy_complex_array_htod(
411 this->pre_buffer, this->d_buffer, this->nsamp_trans
412 );
413 HIPFFT_EXEC(hipfftExecZ2Z(
414 this->plan_gpu, this->d_buffer, this->d_buffer,
415 HIPFFT_FORWARD
416 ));
417 trva::copy_complex_array_dtoh(
418 this->d_buffer, this->pre_buffer, this->nsamp_trans
419 );
420#elif defined(TRV_USE_CUDA) // !TRV_USE_HIP && TRV_USE_CUDA
421 trva::copy_complex_array_htod(
422 this->pre_buffer, this->d_buffer, this->nsamp_trans
423 );
424 CUFFT_EXEC(cufftXtExec(
425 this->plan_gpu, this->d_buffer, this->d_buffer,
426 CUFFT_FORWARD
427 ));
428 trva::copy_complex_array_dtoh(
429 this->d_buffer, this->pre_buffer, this->nsamp_trans
430 );
431#endif // TRV_USE_HIP
432 } else {
433 fftw_execute(this->pre_plan);
434 }
435
436 for (int m = 0; m < N_trans; m++) {
437 // Divide by `N` to normalise the inverse DFT.
438 std::complex<double> a_(this->pre_buffer[m][0], this->pre_buffer[m][1]);
439 std::complex<double> b_ = a_ * this->kernel[m] / double(N_trans);
440 this->post_buffer[m][0] = b_.real();
441 this->post_buffer[m][1] = b_.imag();
442 }
443
444 if (trvs::is_gpu_enabled()) {
445#if defined(TRV_USE_HIP)
446 trva::copy_complex_array_htod(
447 this->post_buffer, this->d_buffer, this->nsamp_trans
448 );
449 HIPFFT_EXEC(hipfftExecZ2Z(
450 this->plan_gpu, this->d_buffer, this->d_buffer,
451 HIPFFT_FORWARD
452 ));
453 trva::copy_complex_array_dtoh(
454 this->d_buffer, this->post_buffer, this->nsamp_trans
455 );
456#elif defined(TRV_USE_CUDA) // !TRV_USE_HIP && TRV_USE_CUDA
457 trva::copy_complex_array_htod(
458 this->post_buffer, this->d_buffer, this->nsamp_trans
459 );
460 CUFFT_EXEC(cufftXtExec(
461 this->plan_gpu, this->d_buffer, this->d_buffer,
462 CUFFT_FORWARD
463 ));
464 trva::copy_complex_array_dtoh(
465 this->d_buffer, this->post_buffer, this->nsamp_trans
466 );
467#endif // TRV_USE_HIP
468 } else {
469 fftw_execute(this->post_plan);
470 }
471
472 // Trim any extrapolation.
473 for (int j = 0; j < N; j++) {
474 b[j] = std::complex<double>(
475 this->post_buffer[j + this->n_ext][0],
476 this->post_buffer[j + this->n_ext][1]
477 );
478 }
479
480 // ---->
481 // // Reverse and shift the array `b_trans` when inverse FFT is used above
482 // // instead of FFT, and trim any extrapolation.
483 // for (int m = 0; m < N_trans/2; m++) {
484 // double b_real_ = this->post_buffer[m][0];
485 // double b_imag_ = this->post_buffer[m][1];
486
487 // this->post_buffer[m][0] = this->post_buffer[N_trans - m - 1][0];
488 // this->post_buffer[m][1] = this->post_buffer[N_trans - m - 1][1];
489
490 // this->post_buffer[N_trans - m - 1][0] = b_real_;
491 // this->post_buffer[N_trans - m - 1][1] = b_imag_;
492 // }
493 // for (int j = 0; j < N; j++) {
494 // int j_ = (j + N_trans - 1) % N_trans + this->n_ext;
495 // b[j] = std::complex<double>(
496 // this->post_buffer[j_][0],
497 // this->post_buffer[j_][1]
498 // );
499 // }
500 // ----<
501}
502
504 int ell, int n, bool threaded
505) : HankelTransform(ell + 1./2, double(n), threaded) {
506 this->degree = ell;
507}
508
510 std::vector<double> sample_pts, double kr_c, bool lowring,
511 trva::ExtrapOption extrap, double extrap_exp
512) {
513 HankelTransform::initialise(sample_pts, kr_c, lowring, extrap, extrap_exp);
514}
515
517 std::vector<double> sample_pts, double kr_c, bool lowring,
518 int extrap, double extrap_exp
519) {
520 HankelTransform::initialise(sample_pts, kr_c, lowring, extrap, extrap_exp);
521}
522
524 std::vector< std::complex<double> >& a,
525 std::vector< std::complex<double> >& b
526) {
527 // STYLE: Standard naming convention is not followed below.
528 std::size_t N = static_cast<std::size_t>(this->nsamp);
529
530 if (a.size() != N) {
532 "The size of array `a` must be equal to the number of samples."
533 );
534 }
535 if (a.size() != N) {
536 b.resize(N);
537 }
538
539 std::complex<double> A[N];
540 std::complex<double> B[N];
541
542 for (std::size_t j = 0; j < N; j++) {
543 A[j] = std::pow(this->pre_sampts[j], 3./2) * a[j];
544 }
545
547
548 for (std::size_t j = 0; j < N; j++) {
549 b[j] = std::pow(2*M_PI / this->post_sampts[j], 3./2) * B[j];
550 }
551}
552
554 int dir,
555 std::vector< std::complex<double> >& pre_samples,
556 std::vector< std::complex<double> >& post_samples
557) {
558 if (abs(dir) != 1) {
560 "The transform direction must be either +1 (forward) or -1 (backward).\n"
561 );
562 }
563
564 double pifactor = (dir == -1) ? std::pow(2*M_PI, -3) : 1.;
565 std::complex<double> parity = std::pow(trvm::M_I, - dir * this->degree);
566 std::complex<double> prefactor = pifactor * parity;
567
568 this->biased_transform(pre_samples, post_samples);
569
570 for (int j = 0; j <this->nsamp; j++) {
571 post_samples[j] *= prefactor;
572 }
573}
574
575} // namespace trv::maths
576
577} // namespace trv
HankelTransform(double mu, double q, bool threaded=true)
Construct the Hankel transform.
Definition fftlog.cpp:39
int nsamp
number of samples provided
Definition fftlog.hpp:72
std::vector< double > post_sampts
logarithmically linearly-spaced sample points post-transform
Definition fftlog.hpp:81
void biased_transform(std::complex< double > *a, std::complex< double > *b)
Perform the (forward biased) Hankel transform.
Definition fftlog.cpp:346
double order
order of the Hankel transform
Definition fftlog.hpp:70
void initialise(std::vector< double > sample_pts, double kr_c, bool lowring=true, trva::ExtrapOption extrap=trva::ExtrapOption::NONE, double extrap_exp=2.)
Initialise the Hankel transform.
Definition fftlog.cpp:102
double calc_lowring_pivot(double delta, double kr_c=1.)
Calculate a low-ringing FFTLog transform pivot value .
Definition fftlog.cpp:255
double bias
power-law bias index
Definition fftlog.hpp:71
int nsamp_trans
number of samples transformed
Definition fftlog.hpp:73
~HankelTransform()
Destruct the Hankel transform.
Definition fftlog.cpp:65
double pivot
pivot value
Definition fftlog.hpp:75
double logres
logarithmic interval sample spacing
Definition fftlog.hpp:74
std::vector< std::complex< double > > compute_kernel_coeff()
Compute the FFTLog transform kernel coefficients .
Definition fftlog.cpp:287
std::vector< double > pre_sampts
logarithmically linearly-spaced sample points pre-transform
Definition fftlog.hpp:78
void biased_transform(std::vector< std::complex< double > > &a, std::vector< std::complex< double > > &b)
Perform the (forward biased) spherical Bessel transform.
Definition fftlog.cpp:523
void initialise(std::vector< double > sample_pts, double kr_c, bool lowring=true, trva::ExtrapOption extrap=trva::ExtrapOption::NONE, double extrap_exp=2.)
Initialise the spherical Bessel transform.
Definition fftlog.cpp:509
void transform_cosmological_multipole(int dir, std::vector< std::complex< double > > &pre_samples, std::vector< std::complex< double > > &post_samples)
Transform csomological multipole samples.
Definition fftlog.cpp:553
int degree
degree of the spherical Bessel transform
Definition fftlog.hpp:261
SphericalBesselTransform(int ell, int n, bool threaded=true)
Construct the spherical Bessel transform.
Definition fftlog.cpp:503
Exception raised when parameters are invalid.
Definition monitor.hpp:731
FFTLog algorithm for Hankel and related transforms and its application to cosmological functions.
int check_1d_array(std::vector< double > &a, bool check_lin, bool check_loglin, bool check_sign)
Check the linearity or log-linearity of a 1-d array.
Definition arrayops.cpp:60
ExtrapOption
Extrapolation scheme.
Definition arrayops.hpp:102
@ NONE
0: none
Definition arrayops.hpp:103
@ LOGLIN
4: log-linear
Definition arrayops.hpp:107
@ PAD
2: pad
Definition arrayops.hpp:105
@ ZERO
1: zero
Definition arrayops.hpp:104
@ LIN
3: linear
Definition arrayops.hpp:106
void extrap_loglin(std::vector< double > &a, int N_ext, std::vector< double > &a_ext)
Extrapolate a 1-d array exponentially (i.e. log-linearly).
Definition arrayops.cpp:123
void extrap_lin(std::vector< double > &a, int N_ext, std::vector< double > &a_ext)
Extrapolate a 1-d array linearly.
Definition arrayops.cpp:94
void extrap_pad(std::vector< double > &a, int N_ext, double c_lower, double c_upper, std::vector< double > &a_ext)
Extrapolate a 1-d array by constant padding.
Definition arrayops.cpp:182
std::complex< double > eval_complex_in_polar(double r, double theta)
Evaluate a complex number in the polar form.
Definition maths.cpp:44
void get_lngamma_parts(double x, double y, double &lnr, double &theta)
Get the real and imaginary parts of the log-gamma function .
Definition maths.cpp:122
std::complex< double > eval_gamma_lnratio(double mu, std::complex< double > nu)
Evaluate the logarithm of the ratio of two gamma functions.
Definition maths.cpp:130
const std::complex< double > M_I
imaginary unit
std::vector< int > get_gpu_ids()
Get the indices of GPUs available for use.
Definition monitor.cpp:284
bool is_gpu_enabled()
Check if GPU mode is enabled.
Definition monitor.cpp:297