1 /********************************************************************
2 MakeProfiles - Create mock astronomical profiles.
3 MakeProfiles is part of GNU Astronomy Utilities (Gnuastro) package.
4
5 Original author:
6 Mohammad Akhlaghi <mohammad@akhlaghi.org>
7 Contributing author(s):
8 Copyright (C) 2015-2021, Free Software Foundation, Inc.
9
10 Gnuastro is free software: you can redistribute it and/or modify it
11 under the terms of the GNU General Public License as published by the
12 Free Software Foundation, either version 3 of the License, or (at your
13 option) any later version.
14
15 Gnuastro is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
22 **********************************************************************/
23 #include <config.h>
24
25 #include <math.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <error.h>
29 #include <stdlib.h>
30
31 #include <sys/time.h> /* generate random seed */
32 #include <gsl/gsl_rng.h> /* used in setrandoms */
33 #include <gsl/gsl_randist.h> /* To make noise. */
34 #include <gsl/gsl_integration.h> /* gsl_integration_qng */
35
36 #include <gnuastro/fits.h>
37 #include <gnuastro/pointer.h>
38 #include <gnuastro/dimension.h>
39 #include <gnuastro/statistics.h>
40
41 #include <gnuastro-internal/timing.h>
42
43 #include "main.h"
44
45 #include "mkprof.h" /* Needs main.h and astrthreads.h */
46 #include "profiles.h"
47 #include "oneprofile.h"
48
49
50
51
52
53
54 /****************************************************************
55 ************** Radial distance ******************
56 ****************************************************************/
57 /* Set the center position of the profile in the oversampled image. Note
58 that 'mkp->width' is in the non-oversampled scale. IMPORTANT: the
59 ordering is in FITS coordinate order. */
60 static void
oneprofile_center_oversampled(struct mkonthread * mkp)61 oneprofile_center_oversampled(struct mkonthread *mkp)
62 {
63 struct mkprofparams *p=mkp->p;
64
65 long os=p->oversample;
66 double *dim, r=1000000;
67 size_t i, id=mkp->ibq->id;
68 double val, pixfrac, intpart;
69
70 for(i=0;i<p->ndim;++i)
71 {
72 dim = i==0 ? p->x : (i==1 ? p->y : p->z);
73 pixfrac = modf(fabs(dim[id]), &intpart);
74 val = ( os*(mkp->width[i]/2 + pixfrac)
75 + (pixfrac<0.5f ? os/2 : -1*os/2-1) );
76 mkp->center[i] = round(val*r)/r;
77 }
78 }
79
80
81
82
83
84 static void
oneprofile_set_coord(struct mkonthread * mkp,size_t index)85 oneprofile_set_coord(struct mkonthread *mkp, size_t index)
86 {
87 size_t i, coord_c[3];
88 uint8_t os=mkp->p->oversample;
89 size_t ndim=mkp->ibq->image->ndim, *dsize=mkp->ibq->image->dsize;
90
91 /* Get the coordinates in C order. */
92 gal_dimension_index_to_coord(index, ndim, dsize, coord_c);
93
94 /* Convert these coordinates to one where the profile center is at the
95 center and the image is not over-sampled. Note that only 'coord_c' is
96 in C order.*/
97 for(i=0;i<ndim;++i)
98 mkp->coord[i] = ( coord_c[ndim-i-1] - mkp->center[i] )/os;
99 }
100
101
102
103
104
105 /* Convert cartesian coordinates to the rotated elliptical radius. See the
106 "Defining an ellipse and ellipsoid" section of the book for the full
107 derivation. */
108 static void
oneprofile_r_el(struct mkonthread * mkp)109 oneprofile_r_el(struct mkonthread *mkp)
110 {
111 double Xr, Yr, Zr; /* Rotated x, y, z. */
112 double q1=mkp->q[0], q2=mkp->q[1];
113 double c1=mkp->c[0], s1=mkp->s[0];
114 double c2=mkp->c[1], s2=mkp->s[1];
115 double c3=mkp->c[2], s3=mkp->s[2];
116 double x=mkp->coord[0], y=mkp->coord[1], z=mkp->coord[2];
117
118 switch(mkp->p->ndim)
119 {
120 case 2:
121 /* The parenthesis aren't necessary, but help in readability and
122 avoiding human induced bugs. */
123 Xr = x * ( c1 ) + y * ( s1 );
124 Yr = x * ( -1.0f*s1 ) + y * ( c1 );
125 mkp->r = sqrt( Xr*Xr + Yr*Yr/q1/q1 );
126 break;
127
128 case 3:
129 Xr = x*( c3*c1 - s3*c2*s1 ) + y*( c3*s1 + s3*c2*c1) + z*( s3*s2 );
130 Yr = x*( -1*s3*c1 - c3*c2*s1 ) + y*(-1*s3*s1 + c3*c2*c1) + z*( c3*s2 );
131 Zr = x*( s1*s2 ) + y*(-1*s2*c1 ) + z*( c2 );
132 mkp->r = sqrt( Xr*Xr + Yr*Yr/q1/q1 + Zr*Zr/q2/q2 );
133 break;
134
135 default:
136 error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to fix "
137 "the problem. The value %zu is not recognized for "
138 "'mkp->p->ndim'", __func__, PACKAGE_BUGREPORT, mkp->p->ndim);
139 }
140 }
141
142
143
144
145
146 /* Calculate the circular/spherical distance of a pixel to the profile
147 center. This is just used to add pixels in the stack. Later, when the
148 pixels are popped from the stack, the elliptical radius will be used to
149 give them a value.*/
150 static float
oneprofile_r_circle(size_t index,struct mkonthread * mkp)151 oneprofile_r_circle(size_t index, struct mkonthread *mkp)
152 {
153
154 size_t i, c[3];
155 double d, sum=0.0f;
156 size_t ndim=mkp->ibq->image->ndim, *dsize=mkp->ibq->image->dsize;
157
158 /* Convert the index into a coordinate. */
159 gal_dimension_index_to_coord(index, ndim, dsize, c);
160
161 /* Find the distance to the center along each dimension (in FITS
162 order). */
163 for(i=0;i<ndim;++i)
164 {
165 d = c[ndim-i-1] - mkp->center[i];
166 sum += d*d;
167 }
168
169 /* Return the distance. */
170 return sqrt(sum);
171 }
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191 /****************************************************************
192 ************** Random points ******************
193 ****************************************************************/
194 /* Fill pixel with random values */
195 float
oneprofile_randompoints(struct mkonthread * mkp)196 oneprofile_randompoints(struct mkonthread *mkp)
197 {
198 double r_before=mkp->r;
199 double range[3], sum=0.0f;
200 size_t i, j, numrandom=mkp->p->numrandom, ndim=mkp->p->ndim;
201 double coord_before[3]={mkp->coord[0], mkp->coord[1], mkp->coord[2]};
202
203 /* Set the range in each dimension. */
204 for(i=0;i<ndim;++i)
205 range[i] = mkp->higher[i] - mkp->lower[i];
206
207 /* Find the sum of the profile on the random positions. */
208 for(i=0;i<numrandom;++i)
209 {
210 for(j=0;j<ndim;++j)
211 mkp->coord[j] = mkp->lower[j] + gsl_rng_uniform(mkp->rng) * range[j];
212 oneprofile_r_el(mkp);
213 sum+=mkp->profile(mkp);
214 }
215
216 /* Reset the original distance and coordinate of the pixel and return the
217 average random value. The resetting is mostly redundant (only useful
218 in checks), but since it has a very negligible cost (compared to the
219 random checks above) cost, its good to reset it to help in debugging
220 when necessary (avoid confusion when un-commenting the checks in
221 'oneprofile_pix_by_pix'). */
222 mkp->r=r_before;
223 mkp->coord[0]=coord_before[0];
224 mkp->coord[1]=coord_before[1];
225 mkp->coord[2]=coord_before[2];
226 return sum/numrandom;
227 }
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248 /****************************************************************
249 ***************** 2D integration ********************
250 ****************************************************************/
251 /* This is an old implementation which we are not using now. But it is kept
252 here in case it can be useful */
253 #if 0
254 double
255 twod_over_x(double x, void *params)
256 {
257 struct mkonthread *mkp=(struct mkonthread *) params;
258
259 mkp->x=x;
260 oneprofile_r_el(mkp);
261 return mkp->profile(mkp);
262 }
263
264
265
266
267
268 /* Find the 2d integration over the region. */
269 double
270 twod_over_xy(double y, void *params)
271 {
272 gsl_function F;
273 static double abserr;
274 static size_t neval=0;
275 double epsabs=0, epsrel=EPSREL_FOR_INTEG, result;
276 struct mkonthread *mkp=(struct mkonthread *) params;
277
278 F.function = &twod_over_x;
279 F.params = params;
280
281 mkp->y=y;
282 gsl_integration_qng(&F, mkp->xl, mkp->xh, epsabs, epsrel,
283 &result, &abserr, &neval);
284 return result;
285 }
286
287
288
289
290 /* 2D integration of a profile.*/
291 double
292 integ2d(struct mkonthread *mkp)
293 {
294 gsl_function F;
295 static double abserr;
296 static size_t neval=0;
297 double epsabs=0, epsrel=EPSREL_FOR_INTEG, result;
298
299 F.function = &twod_over_xy;
300 F.params = mkp;
301 gsl_integration_qng(&F, mkp->yl, mkp->yh, epsabs,
302 epsrel, &result, &abserr, &neval);
303 return result;
304 }
305 #endif
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324 /**************************************************************/
325 /************ Pixel by pixel building *************/
326 /********* Positions are in C not FITS *********/
327 /**************************************************************/
328 /* 'oneprofile_center_oversampled' stored the center of the profile in
329 floating point coordinates. This function will convert that into a
330 pixel index. */
331 static size_t
oneprofile_center_pix_index(struct mkonthread * mkp)332 oneprofile_center_pix_index(struct mkonthread *mkp)
333 {
334 double pixfrac, intpart;
335 size_t *dsize=mkp->ibq->image->dsize;
336 size_t i, coord[3], ndim=mkp->p->ndim;
337
338 /* Find the coordinates of the center point. Note 'mkp->center' is in
339 FITS coordinates, while coord must be in C coordinates (to be used in
340 'gal_dimension_coord_to_index'). */
341 for(i=0;i<ndim;++i)
342 {
343 pixfrac = modf(mkp->center[i], &intpart);
344 coord[ndim-i-1] = (long)(mkp->center[i]) + ( pixfrac<0.5f ? 0 : 1 );
345 }
346
347 /* Retun the pixel index of this coordinate. */
348 return gal_dimension_coord_to_index(ndim, dsize, coord);
349 }
350
351
352
353
354
355 static void
oneprofile_pix_by_pix(struct mkonthread * mkp)356 oneprofile_pix_by_pix(struct mkonthread *mkp)
357 {
358 struct builtqueue *ibq=mkp->ibq;
359 size_t ndim=ibq->image->ndim, *dsize=ibq->image->dsize;
360
361 uint8_t *byt;
362 gal_list_sizet_t *Q=NULL;
363 int use_rand_points=1, ispeak=1;
364 double tolerance=mkp->p->tolerance;
365 float circ_r, *array=mkp->ibq->image->array;
366 double (*profile)(struct mkonthread *)=mkp->profile;
367 double truncr=mkp->truncr, approx, hp=0.5f/mkp->p->oversample;
368 size_t i, p, *dinc=gal_dimension_increment(ndim, dsize);
369
370 /* lQ: Largest. sQ: Smallest in queue */
371 gal_list_dosizet_t *lQ=NULL, *sQ;
372
373 /* Find the nearest pixel to the profile center and add it to the
374 queue. */
375 p=oneprofile_center_pix_index(mkp);
376
377 /* If this is a point source, just fill that one pixel and leave this
378 function. */
379 if(mkp->func==PROFILE_POINT)
380 { array[p]=1; return; }
381
382 /* Allocate the 'byt' array. It is used as a flag to make sure that we
383 don't re-calculate the profile value on a pixel more than once. */
384 byt = gal_pointer_allocate(GAL_TYPE_UINT8,
385 gal_dimension_total_size(ndim, dsize), 1,
386 __func__, "byt");
387
388 /* Start the queue: */
389 byt[p]=1;
390 gal_list_dosizet_add( &lQ, &sQ, p, oneprofile_r_circle(p, mkp) );
391
392 /* If random points are necessary, then do it: */
393 switch(mkp->func)
394 {
395 case PROFILE_SERSIC:
396 case PROFILE_MOFFAT:
397 case PROFILE_GAUSSIAN:
398 while(sQ)
399 {
400 /* In case you want to see the status of the twosided ordered
401 queue, increasing and decreasing side by side, uncomment this
402 line. Note that there will be a lot of lines printed! */
403 /*print_tossll(lQ, sQ);*/
404
405 /* Pop a pixel from the queue, convert its index into coordinates
406 and use them to estimate the elliptical radius of the
407 pixel. If the pixel is outside the truncation radius, ignore
408 it. */
409 p=gal_list_dosizet_pop_smallest(&lQ, &sQ, &circ_r);
410 oneprofile_set_coord(mkp, p);
411 oneprofile_r_el(mkp);
412 if(mkp->r > truncr) continue;
413
414 /* Set the range for this pixel. */
415 for(i=0;i<ndim;++i)
416 {
417 mkp->lower[i] = mkp->coord[i] - hp;
418 mkp->higher[i] = mkp->coord[i] + hp;
419 }
420
421 /* Find the random points and profile center. */
422 array[p]=oneprofile_randompoints(mkp);
423 approx=profile(mkp);
424 if (fabs(array[p]-approx)/array[p] < tolerance)
425 use_rand_points=0;
426
427 /* For a check:
428 printf("coord: %g, %g\n", mkp->coord[0], mkp->coord[1]);
429 printf("r_rand: %g (rand: %g, center: %g)\n\n", mkp->r, array[p],
430 approx);
431 */
432
433 /* Save the peak flux if this is the first pixel: */
434 if(ispeak) { mkp->peakflux=array[p]; ispeak=0; }
435
436 /* For the log file: */
437 ++ibq->numaccu;
438 ibq->accufrac+=array[p];
439
440 /* Go over the neighbors and add them to queue of elements to
441 check if they haven't been done already. */
442 GAL_DIMENSION_NEIGHBOR_OP(p, ndim, dsize, 1, dinc,
443 {
444 if(byt[nind]==0)
445 {
446 byt[nind]=1;
447 gal_list_dosizet_add( &lQ, &sQ, nind,
448 oneprofile_r_circle(nind, mkp) );
449 }
450 } );
451
452 if(use_rand_points==0) break;
453 }
454 }
455
456
457 /* All the pixels that required integration or random points are now
458 done, so we don't need an ordered array any more. */
459 gal_list_dosizet_to_sizet(lQ, &Q);
460
461
462 /* Order doesn't matter any more, add all the pixels you find. */
463 while(Q)
464 {
465 p=gal_list_sizet_pop(&Q);
466 oneprofile_set_coord(mkp, p);
467 oneprofile_r_el(mkp);
468
469 /* See if this pixel's radial distance is larger than the truncation
470 radius. If so, then don't add its neighbors to the queue and
471 continue to the next pixel in the queue. */
472 if(mkp->r>truncr)
473 {
474 /* For the circumference, if the profile is too elongated
475 and circumwidth is too small, then some parts of the
476 circumference will not be shown without this condition. */
477 if(mkp->func==PROFILE_CIRCUMFERENCE) array[p]=profile(mkp);
478 continue;
479 }
480
481 /* Find the value for this pixel: */
482 array[p]=profile(mkp);
483
484 /* For a check:
485 printf("r_center: %g\n", mkp->r);
486 */
487
488 /* Save the peak flux if this is the first pixel: */
489 if(ispeak) { mkp->peakflux=array[p]; ispeak=0; }
490
491 /* Go over the neighbours and add them to queue of elements
492 to check. */
493 GAL_DIMENSION_NEIGHBOR_OP(p, ndim, dsize, 1, dinc,
494 {
495 if(byt[nind]==0)
496 {
497 byt[nind]=1;
498 gal_list_sizet_add(&Q, nind);
499 }
500 } );
501 }
502
503 /* Clean up. */
504 free(byt);
505 free(dinc);
506 }
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526 /**************************************************************/
527 /************ Set profile parameters *************/
528 /**************************************************************/
529 int
oneprofile_ispsf(uint8_t fcode)530 oneprofile_ispsf(uint8_t fcode)
531 {
532 return fcode==PROFILE_MOFFAT || fcode==PROFILE_GAUSSIAN;
533 }
534
535
536
537
538
539 /* Prepare all the parameters for any type of profile. */
540 void
oneprofile_set_prof_params(struct mkonthread * mkp)541 oneprofile_set_prof_params(struct mkonthread *mkp)
542 {
543 struct mkprofparams *p=mkp->p;
544
545 double sigma;
546 int tp=p->tunitinp;
547 size_t id=mkp->ibq->id, ndim=p->ndim;
548
549 /* Fill the most basic profile agnostic parameters. */
550 mkp->brightness = ( p->mcolisbrightness
551 ? p->m[id]
552 : pow( 10, (p->zeropoint - p->m[id]) / 2.5f ) );
553 mkp->ibq->ispsf = p->kernel ? 1 : oneprofile_ispsf(p->f[id]);
554 mkp->func = mkp->ibq->func = p->f[id];
555
556
557 /* Fill in the dimension-dependent parameters. */
558 switch(ndim)
559 {
560 case 2:
561 /* Shifts were already multiplied with oversample. Just note that
562 p->x and p->y are in the FITS ordering, while p->shift is in C
563 ordering. */
564 mkp->q[0] = p->q1[id];
565 p->x[id] += p->shift[1]/p->oversample;
566 p->y[id] += p->shift[0]/p->oversample;
567 mkp->c[0] = cos( p->p1[id] * DEGREESTORADIANS );
568 mkp->s[0] = sin( p->p1[id] * DEGREESTORADIANS );
569 break;
570
571 case 3:
572 /* See comments for 2D. */
573 mkp->q[0] = p->q1[id];
574 mkp->q[1] = p->q2[id];
575 p->x[id] += p->shift[2]/p->oversample;
576 p->y[id] += p->shift[1]/p->oversample;
577 p->z[id] += p->shift[0]/p->oversample;
578 mkp->c[0] = cos( p->p1[id] * DEGREESTORADIANS );
579 mkp->s[0] = sin( p->p1[id] * DEGREESTORADIANS );
580 mkp->c[1] = cos( p->p2[id] * DEGREESTORADIANS );
581 mkp->s[1] = sin( p->p2[id] * DEGREESTORADIANS );
582 mkp->c[2] = cos( p->p3[id] * DEGREESTORADIANS );
583 mkp->s[2] = sin( p->p3[id] * DEGREESTORADIANS );
584 break;
585
586 default:
587 error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
588 "address the problem. The value '%zu' is not recognized for "
589 "'ndim'", __func__, PACKAGE_BUGREPORT, ndim);
590 }
591
592
593 /* Fill the profile-dependent parameters. */
594 switch (mkp->func)
595 {
596 case PROFILE_SERSIC:
597 mkp->correction = 1;
598 mkp->profile = &profiles_sersic;
599 mkp->sersic_re = p->r[id];
600 mkp->sersic_inv_n = 1.0f/p->n[id];
601 mkp->sersic_nb = -1.0f*profiles_sersic_b(p->n[id]);
602 mkp->truncr = tp ? p->t[id] : p->t[id]*p->r[id];
603 break;
604
605
606
607 case PROFILE_MOFFAT:
608 mkp->correction = 1;
609 mkp->profile = &profiles_moffat;
610 mkp->moffat_nb = -1.0f*p->n[id];
611 mkp->moffat_alphasq = profiles_moffat_alpha(p->r[id], p->n[id]);
612 mkp->moffat_alphasq *= mkp->moffat_alphasq;
613 mkp->truncr = tp ? p->t[id] : p->t[id]*p->r[id]/2;
614 if(p->psfinimg==0 && p->individual==0)
615 {
616 mkp->brightness = 1.0f; /* When the PSF is a separate image, */
617 p->x[id] = 0.0f; /* it should be centered and have a */
618 p->y[id] = 0.0f; /* total brightness of 1.0f. */
619 if(ndim==3)
620 p->z[id] = 0.0f;
621 }
622 break;
623
624
625
626 case PROFILE_GAUSSIAN:
627 mkp->correction = 1;
628 mkp->profile = &profiles_gaussian;
629 sigma = p->r[id]/2.35482f;
630 mkp->gaussian_c = -1.0f/(2.0f*sigma*sigma);
631 mkp->truncr = tp ? p->t[id] : p->t[id]*p->r[id]/2;
632 if(p->psfinimg==0 && p->individual==0)
633 {
634 mkp->brightness = 1.0f; /* Same as the explanations for */
635 p->x[id] = 0.0f; /* The Moffat profile. */
636 p->y[id] = 0.0f;
637 if(ndim==3)
638 p->z[id] = 0.0f;
639 }
640 break;
641
642
643
644 case PROFILE_POINT:
645 mkp->correction = 1;
646 mkp->fixedvalue = 1.0f;
647 mkp->profile = &profiles_flat;
648 break;
649
650
651
652 case PROFILE_FLAT:
653 mkp->profile = &profiles_flat;
654 mkp->truncr = tp ? p->t[id] : p->t[id]*p->r[id];
655 if(p->mforflatpix)
656 {
657 mkp->correction = 0;
658 mkp->fixedvalue = p->m[id];
659 }
660 else
661 {
662 mkp->correction = 1;
663 mkp->fixedvalue = 1.0f;
664 }
665 break;
666
667
668
669 case PROFILE_CIRCUMFERENCE:
670 mkp->profile = &profiles_circumference;
671 mkp->truncr = tp ? p->t[id] : p->t[id]*p->r[id];
672 mkp->intruncr = mkp->truncr - p->circumwidth;
673 if(p->mforflatpix)
674 {
675 mkp->correction = 0;
676 mkp->fixedvalue = p->m[id];
677 }
678 else
679 {
680 mkp->correction = 1;
681 mkp->fixedvalue = 1.0f;
682 }
683 if(mkp->intruncr<0.0f)
684 mkp->intruncr = 0.0f;
685 break;
686
687
688
689 case PROFILE_DISTANCE:
690 mkp->profile = profiles_radial_distance;
691 mkp->truncr = tp ? p->t[id] : p->t[id]*p->r[id];
692 mkp->correction = 0;
693 break;
694
695
696
697
698
699 case PROFILE_AZIMUTH:
700 mkp->profile = profiles_azimuth;
701 mkp->truncr = tp ? p->t[id] : p->t[id]*p->r[id];
702 mkp->correction = 0;
703 break;
704
705
706
707 case PROFILE_CUSTOM:
708 mkp->profile = profiles_custom_table;
709 mkp->truncr = tp ? p->t[id] : p->t[id]*p->r[id];
710 mkp->correction = 0;
711 break;
712
713
714
715 default:
716 error(EXIT_FAILURE, 0, "%s: a bug! Please contact us so we can "
717 "correct this problem. The profile code %u is not recognized.",
718 __func__, mkp->func);
719 }
720 }
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741 /**************************************************************/
742 /************ Outside functions *************/
743 /**************************************************************/
744 void
oneprofile_make(struct mkonthread * mkp)745 oneprofile_make(struct mkonthread *mkp)
746 {
747 struct mkprofparams *p=mkp->p;
748
749 double sum;
750 float *f, *ff;
751 size_t i, dsize[3], ndim=p->ndim;
752
753
754 /* Find the profile center in the over-sampled image in C
755 coordinates. IMPORTANT: width must not be oversampled.*/
756 oneprofile_center_oversampled(mkp);
757
758
759 /* From this point on, the widths will be in the actual pixel widths
760 (with oversampling). */
761 for(i=0;i<ndim;++i)
762 {
763 mkp->width[i] *= p->oversample;
764 dsize[ndim-i-1] = mkp->width[i];
765 }
766
767
768 /* Allocate and clear the array for this one profile. */
769 mkp->ibq->image=gal_data_alloc(NULL, GAL_TYPE_FLOAT32, ndim, dsize,
770 NULL, 1, p->cp.minmapsize, p->cp.quietmmap,
771 "MOCK", "Brightness", NULL);
772
773
774 /* Build the profile in the image. */
775 oneprofile_pix_by_pix(mkp);
776
777
778 /* Correct the sum of pixels in the profile so it has the fixed total
779 magnitude or pixel value, mkp->correction was set in
780 setprofparams. Note that the profiles were not normalized during the
781 building.*/
782 if(mkp->correction)
783 {
784 /* First get the sum of all the pixels in the profile. */
785 ff=(f=mkp->ibq->image->array) + mkp->ibq->image->size;
786 sum=0.0f; do sum+=*f++; while(f<ff);
787
788 /* Correct the fraction of brightness that was calculated
789 accurately (not using the pixel center). */
790 mkp->ibq->accufrac /= sum;
791
792 /* Correct all the profile pixels. Note that ideally, if a user wants
793 a NaN valued profile, they should use the 'flat' profile with
794 '--mforflatpix', which won't need this correction. However, it
795 might happen that they forget the later, or the catalog might be
796 generated by a script that gives a NaN value for the magnitude
797 with any kind of profile. In such cases if we don't check the NaN
798 value, then the whole profile's box is going to be NaN values,
799 which is inconvenient and with the simple check here we can avoid
800 it (only have the profile's pixels set to NaN. */
801 ff = (f=mkp->ibq->image->array) + mkp->ibq->image->size;
802 if(isnan(mkp->brightness))
803 do *f = *f ? NAN : *f ; while(++f<ff);
804 else
805 {
806 if(p->magatpeak)
807 do *f++ *= mkp->brightness/mkp->peakflux; while(f<ff);
808 else
809 do *f++ *= mkp->brightness/sum; while(f<ff);
810 }
811 }
812 }
813