GIRAFFE Pipeline Reference Manual

gimodels.c
1/*
2 * This file is part of the GIRAFFE Pipeline
3 * Copyright (C) 2002-2019 European Southern Observatory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
24#include <math.h>
25
26#include <cxtypes.h>
27#include <cxmemory.h>
28#include <cxmessages.h>
29#include <cxstrutils.h>
30
31#include "gimacros.h"
32#include "gimodels.h"
33
34
35static GiModelData _gimodels[];
36
37const GiModelData *const giraffe_models = _gimodels;
38
39
40/*
41 * Optical model argument indices
42 */
43
44enum {
45 LMI_WLEN = 0,
46 LMI_XFIB = 1,
47 LMI_YFIB = 2
48};
49
50
51/*
52 * Optical model parameter indices
53 */
54
55enum {
56 LMP_NX = 0,
57 LMP_NY = 0,
58 LMP_PXSIZ = 1,
59 LMP_FCOLL = 2,
60 LMP_CFACT = 3,
61 LMP_THETA = 4,
62 LMP_ORDER = 5,
63 LMP_SPACE = 6,
64 LMP_SOFFX = 7,
65 LMP_SOFFY = 8,
66 LMP_SPHI = 9
67};
68
69
70/*
71 * Line model parameter indices
72 */
73
74enum {
75 LMP_AMPL = 0,
76 LMP_CENT = 1,
77 LMP_BACK = 2,
78 LMP_WID1 = 3,
79 LMP_WID2 = 4
80};
81
82
83
84static const cxint DW_DEGREE = 3;
85static const cxdouble DW_LOG001 = 2.302585093; /* -log(0.1) */
86
87
88/*
89 * Utility function to calculate a weighted exponential
90 *
91 * DW_DEGREE
92 * exp( - | x - x0 | )
93 * w = -------------------------------
94 * / DW_DEGREE \
95 * | --------- |
96 * \ DW_LOG001 /
97 * dx
98 */
99
100inline static cxdouble
101_giraffe_dydaweight(cxdouble x, cxdouble x0, cxdouble dx)
102{
103
104 register cxdouble w;
105
106
107 w = exp(-pow(fabs(x - x0), DW_DEGREE) / pow(dx, DW_DEGREE / DW_LOG001));
108
109 if (isnan(w)) {
110 w = 1;
111 }
112
113 return w;
114
115}
116
117
118inline static void
119_giraffe_model_dtor(GiModel *self)
120{
121
122 if (self->name) {
123 cx_free(self->name);
124 self->name = NULL;
125 }
126
127
128 self->arguments.count = 0;
129
130 if (self->arguments.names) {
131 cpl_propertylist_delete(self->arguments.names);
132 self->arguments.names = NULL;
133 }
134
135 if (self->arguments.values) {
136 cpl_matrix_delete(self->arguments.values);
137 self->arguments.values = NULL;
138 }
139
140
141 self->parameters.count = 0;
142
143 if (self->parameters.names) {
144 cpl_propertylist_delete(self->parameters.names);
145 self->parameters.names = NULL;
146 }
147
148 if (self->parameters.values) {
149 cpl_matrix_delete(self->parameters.values);
150 self->parameters.values = NULL;
151 }
152
153 if (self->parameters.limits) {
154 cpl_matrix_delete(self->parameters.limits);
155 self->parameters.limits = NULL;
156 }
157
158 if (self->parameters.flags) {
159 cx_free(self->parameters.flags);
160 self->parameters.flags = NULL;
161 }
162
163
164 self->fit.iterations = 0;
165
166 if (self->fit.covariance) {
167 cpl_matrix_delete(self->fit.covariance);
168 self->fit.covariance = NULL;
169 }
170
171 return;
172
173}
174
175
176inline static void
177_giraffe_xoptmod_ctor(GiModel *self, const GiModelData *model)
178{
179
180 cx_assert(self != NULL);
181 cx_assert(model != NULL);
182
183 self->name = cx_strdup(model->name);
184 self->type = model->type;
185
186 self->model = model->eval;
187
188
189 /*
190 * Arguments
191 */
192
193 self->arguments.names = cpl_propertylist_new();
194
195 cpl_propertylist_append_int(self->arguments.names, "xf", LMI_XFIB);
196 cpl_propertylist_append_int(self->arguments.names, "yf", LMI_YFIB);
197 cpl_propertylist_append_int(self->arguments.names, "lambda", LMI_WLEN);
198
199 self->arguments.count = cpl_propertylist_get_size(self->arguments.names);
200 self->arguments.values = cpl_matrix_new(self->arguments.count, 1);
201
202
203 /*
204 * Parameters
205 */
206
207 self->parameters.names = cpl_propertylist_new();
208
209 cpl_propertylist_append_int(self->parameters.names, "Orientation",
210 LMP_NX);
211 cpl_propertylist_append_int(self->parameters.names, "Order",
212 LMP_ORDER);
213 cpl_propertylist_append_int(self->parameters.names, "PixelSize",
214 LMP_PXSIZ);
215 cpl_propertylist_append_int(self->parameters.names, "FocalLength",
216 LMP_FCOLL);
217 cpl_propertylist_append_int(self->parameters.names, "Magnification",
218 LMP_CFACT);
219 cpl_propertylist_append_int(self->parameters.names, "Angle",
220 LMP_THETA);
221 cpl_propertylist_append_int(self->parameters.names, "Spacing",
222 LMP_SPACE);
223
224 self->parameters.count =
225 cpl_propertylist_get_size(self->parameters.names);
226 self->parameters.values = cpl_matrix_new(self->parameters.count, 1);
227
228 return;
229
230}
231
232
233inline static void
234_giraffe_xoptmod_eval(cxdouble *y, cxdouble *x, cxdouble *a, cxint na,
235 cxdouble *dyda, cxdouble *r)
236{
237
238 const cxchar *const fctid = "_giraffe_xoptmod_eval";
239
240
241 cxdouble lambda;
242 cxdouble xfibre;
243 cxdouble yfibre;
244
245 cxdouble pixsize, nx;
246 cxdouble fcoll, cfact;
247 cxdouble gtheta, gorder, gspace;
248
249 register cxdouble xccd, d, X;
250 register cxdouble yfibre2, tmp, tmp2, d2, X2, gspace2;
251 register cxdouble sqtmp, costheta, sintheta;
252
253
254
255 if (na != 7) {
256 cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
257 return;
258 }
259
260 *y = 0.0;
261
262 if (dyda != NULL) {
263 dyda[LMP_NX] = 0.;
264 dyda[LMP_PXSIZ] = 0.;
265 dyda[LMP_FCOLL] = 0.;
266 dyda[LMP_CFACT] = 0.;
267 dyda[LMP_THETA] = 0.;
268 dyda[LMP_ORDER] = 0.;
269 dyda[LMP_SPACE] = 0.;
270 }
271
272 lambda = x[LMI_WLEN]; /* wavelength [nm] */
273 xfibre = x[LMI_XFIB]; /* X fibre [mm] */
274 yfibre = x[LMI_YFIB]; /* Y fibre [mm] */
275
276 nx = a[LMP_NX]; /* CCD size along X [pixels] */
277 pixsize = a[LMP_PXSIZ]; /* CCD pixel size [mm] */
278 fcoll = a[LMP_FCOLL]; /* collimator focal length [mm] */
279 cfact = a[LMP_CFACT]; /* camera magnification factor */
280 gtheta = a[LMP_THETA]; /* grating angle [radian] */
281 gorder = a[LMP_ORDER]; /* grating diffraction order */
282 gspace = a[LMP_SPACE]; /* grating groove spacing [mm] */
283
284
285 lambda *= GI_NM_TO_MM; /* wavelength [mm] */
286
287 yfibre2 = yfibre * yfibre;
288 gspace2 = gspace * gspace;
289 costheta = cos(gtheta);
290 sintheta = sin(gtheta);
291
292 d2 = xfibre * xfibre + yfibre2 + (fcoll * fcoll);
293 d = sqrt(d2);
294
295 X = (-lambda * gorder / gspace) + (xfibre * costheta / d) +
296 (fcoll * sintheta / d);
297 X2 = X * X;
298
299 sqtmp = sqrt(1.0 - yfibre2 / d2 - X2);
300 tmp = -sintheta * X + costheta * sqtmp;
301 tmp2 = tmp * tmp;
302 xccd = (cfact * fcoll * (X * costheta + sintheta * sqtmp)) / tmp;
303
304
305 /*
306 * Take care of model direction
307 */
308
309 if (nx < 0.0) {
310 *y = xccd / pixsize - 0.5 * nx;
311 }
312 else {
313 *y = -xccd / pixsize + 0.5 * nx;
314 }
315
316
317 /*
318 * If requested, compute the partial derivatives of y
319 * with respect to each parameter.
320 */
321
322 if (dyda != NULL) {
323
324 dyda[LMP_NX] = 0.5;
325 dyda[LMP_PXSIZ] = 0.0;
326
327 dyda[LMP_FCOLL] = cfact * (costheta * X + sintheta * sqtmp) / tmp +
328 cfact * fcoll * (costheta * (-X * fcoll / d2 + sintheta / d -
329 gorder * lambda * fcoll /
330 (d2 * gspace)) + 0.5 * sintheta *
331 (-2.0 * X * (-X * fcoll / d2 + sintheta / d -
332 gorder * lambda * fcoll /
333 (d2 * gspace)) +
334 2.0 * yfibre2 * fcoll / (d2 * d2)) / sqtmp) /
335 tmp - cfact * fcoll * (costheta * X + sintheta * sqtmp) *
336 (-sintheta * (-X * fcoll / d2 + sintheta / d - gorder * lambda *
337 fcoll / (d2 * gspace)) + 0.5 * costheta *
338 (-2.0 * X * (-X * fcoll / d2 + sintheta / d - gorder * lambda *
339 fcoll / (d2 * gspace)) + 2.0 * yfibre2 * fcoll /
340 (d2 * d2)) / sqtmp) / tmp2;
341
342 dyda[LMP_FCOLL] /= pixsize;
343 dyda[LMP_CFACT] = (xccd / cfact) / pixsize;
344
345 dyda[LMP_THETA] = cfact * fcoll * ((-xfibre * sintheta / d + fcoll *
346 costheta / d) * costheta -
347 sintheta * X - sintheta * X *
348 (-xfibre * sintheta / d + fcoll *
349 costheta / d) / sqtmp +
350 costheta * sqtmp) / tmp -
351 cfact * fcoll * (costheta * X + sintheta * sqtmp) *
352 (-(-xfibre * sintheta / d + fcoll * costheta / d) * sintheta -
353 costheta * X - costheta * X * (-xfibre * sintheta / d + fcoll *
354 costheta / d) /
355 sqtmp - sintheta * sqtmp) / tmp2;
356
357 dyda[LMP_THETA] /= pixsize;
358 dyda[LMP_ORDER] = 0.0;
359
360 dyda[LMP_SPACE] = cfact * fcoll * (lambda * gorder * costheta /
361 gspace2 - sintheta * X * lambda *
362 gorder / (sqtmp * gspace2)) /
363 tmp - cfact * fcoll * (X * costheta + sintheta * sqtmp) *
364 (-lambda * gorder * sintheta / gspace2 - costheta * X * lambda *
365 gorder / (sqtmp * gspace2)) / tmp2;
366
367 dyda[LMP_SPACE] /= pixsize;
368
369 if (nx > 0.) {
370 dyda[LMP_NX] = -dyda[LMP_NX];
371 dyda[LMP_PXSIZ] = -dyda[LMP_PXSIZ];
372 dyda[LMP_FCOLL] = -dyda[LMP_FCOLL];
373 dyda[LMP_CFACT] = -dyda[LMP_CFACT];
374 dyda[LMP_THETA] = -dyda[LMP_THETA];
375 dyda[LMP_ORDER] = -dyda[LMP_ORDER];
376 dyda[LMP_SPACE] = -dyda[LMP_SPACE];
377 }
378
379 if (r != NULL) {
380
381 register cxint k;
382
383 k = LMP_FCOLL << 1;
384 if (r[k+1] > 0) {
385 dyda[LMP_FCOLL] *= _giraffe_dydaweight(a[LMP_FCOLL], r[k],
386 r[k + 1]);
387 }
388
389 k = LMP_CFACT << 1;
390 if (r[k+1] > 0) {
391 dyda[LMP_CFACT] *= _giraffe_dydaweight(a[LMP_CFACT], r[k],
392 r[k + 1]);
393 }
394
395 k = LMP_THETA << 1;
396 if (r[k+1] > 0) {
397 dyda[LMP_THETA] *= _giraffe_dydaweight(a[LMP_THETA], r[k],
398 r[k + 1]);
399 }
400
401 k = LMP_SPACE << 1;
402 if (r[k+1] > 0) {
403 dyda[LMP_SPACE] *= _giraffe_dydaweight(a[LMP_SPACE], r[k],
404 r[k + 1]);
405 }
406
407 }
408
409 }
410
411 return;
412
413}
414
415
416inline static void
417_giraffe_yoptmod_ctor(GiModel *self, const GiModelData *model)
418{
419
420 cx_assert(self != NULL);
421 cx_assert(model != NULL);
422
423 self->name = cx_strdup(model->name);
424 self->type = model->type;
425
426 self->model = model->eval;
427
428
429 /*
430 * Arguments
431 */
432
433 self->arguments.names = cpl_propertylist_new();
434
435 cpl_propertylist_append_int(self->arguments.names, "xf", LMI_XFIB);
436 cpl_propertylist_append_int(self->arguments.names, "yf", LMI_YFIB);
437 cpl_propertylist_append_int(self->arguments.names, "lambda", LMI_WLEN);
438
439 self->arguments.count = cpl_propertylist_get_size(self->arguments.names);
440 self->arguments.values = cpl_matrix_new(self->arguments.count, 1);
441
442
443 /*
444 * Parameters
445 */
446
447 self->parameters.names = cpl_propertylist_new();
448
449 cpl_propertylist_append_int(self->parameters.names, "Orientation",
450 LMP_NY);
451 cpl_propertylist_append_int(self->parameters.names, "Order",
452 LMP_ORDER);
453 cpl_propertylist_append_int(self->parameters.names, "PixelSize",
454 LMP_PXSIZ);
455 cpl_propertylist_append_int(self->parameters.names, "FocalLength",
456 LMP_FCOLL);
457 cpl_propertylist_append_int(self->parameters.names, "Magnification",
458 LMP_CFACT);
459 cpl_propertylist_append_int(self->parameters.names, "Angle",
460 LMP_THETA);
461 cpl_propertylist_append_int(self->parameters.names, "Spacing",
462 LMP_SPACE);
463
464 self->parameters.count =
465 cpl_propertylist_get_size(self->parameters.names);
466 self->parameters.values = cpl_matrix_new(self->parameters.count, 1);
467
468 return;
469
470}
471
472
473inline static void
474_giraffe_yoptmod_eval(cxdouble *y, cxdouble *x, cxdouble *a, cxint na,
475 cxdouble *dyda, cxdouble *r)
476{
477
478 const cxchar *const fctid = "_giraffe_yoptmod_eval";
479
480 cxdouble lambda, xfibre, yfibre;
481 cxdouble pixsize, ny;
482 cxdouble fcoll,cfact;
483 cxdouble gtheta,gorder,gspace;
484
485 register cxdouble t2, t3, t4, t5, t6, t7, t8, t9;
486 register cxdouble t10, t12, t13, t15, t18;
487 register cxdouble t22, t24, t26, t27, t28, t29;
488 register cxdouble t30, t33;
489 register cxdouble t41, t45, t47;
490 register cxdouble t53, t56, t57;
491 register cxdouble t76;
492 register cxdouble t93, t94;
493
494
495 /* Not used */
496 (void) r;
497
498 if (na != 7) {
499 cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
500 return;
501 }
502
503 *y = 0.;
504
505 if (dyda != NULL) {
506 dyda[LMP_NY] = 0.;
507 dyda[LMP_PXSIZ] = 0.;
508 dyda[LMP_FCOLL] = 0.;
509 dyda[LMP_CFACT] = 0.;
510 dyda[LMP_THETA] = 0.;
511 dyda[LMP_ORDER] = 0.;
512 dyda[LMP_SPACE] = 0.;
513 }
514
515 lambda = x[LMI_WLEN];
516 xfibre = x[LMI_XFIB];
517 yfibre = x[LMI_YFIB];
518
519 ny = a[LMP_NY];
520 pixsize = a[LMP_PXSIZ];
521 fcoll = a[LMP_FCOLL];
522 cfact = a[LMP_CFACT];
523 gtheta = a[LMP_THETA];
524 gorder = a[LMP_ORDER];
525 gspace = a[LMP_SPACE];
526
527 lambda *= GI_NM_TO_MM;
528
529 t2 = cfact * fcoll * yfibre;
530 t3 = xfibre * xfibre;
531 t4 = yfibre * yfibre;
532 t5 = fcoll * fcoll;
533 t6 = t3 + t4 + t5;
534 t7 = sqrt(t6);
535 t8 = 1.0 / t7;
536 t9 = lambda * gorder;
537 t10 = 1.0 / gspace;
538 t12 = cos(gtheta);
539 t13 = xfibre * t12;
540 t15 = sin(gtheta);
541 t18 = -t9 * t10 + t13 * t8 + fcoll * t15 * t8;
542 t22 = t18 * t18;
543 t24 = sqrt(1.0 - t4 / t6 - t22);
544 t26 = -t18 * t15 + t12 * t24;
545 t27 = 1.0 / t26;
546 t28 = t8 * t27;
547 t29 = 1.0 / pixsize;
548 t30 = t28 * t29;
549 t33 = pixsize * pixsize;
550 t41 = 1.0 / t7 / t6;
551 t45 = t26 * t26;
552 t47 = t8 / t45;
553 t53 = -t13 * t41 * fcoll + t15 * t8 - t5 * t15 * t41;
554 t56 = t12 / t24;
555 t57 = t6 * t6;
556 t76 = -xfibre * t15 * t8 + fcoll * t12 * t8;
557 t93 = gspace * gspace;
558 t94 = 1.0 / t93;
559
560 *y = -t2 * t30 + 0.5 * ny;
561
562
563 /*
564 * If requested, compute the partial derivatives of y
565 * with respect to each parameter.
566 */
567
568 if (dyda != NULL) {
569
570 dyda[LMP_NY] = 0.5;
571 dyda[LMP_PXSIZ] = t2 * t28 / t33;
572 dyda[LMP_FCOLL] = -cfact * yfibre * t30 + cfact * t5 *
573 yfibre * t41 * t27 * t29 + t2 * t47 * t29 *
574 (-t53 * t15 + t56 * (2.0 * t4 / t57 * fcoll -
575 2.0 * t18 * t53) / 2.0);
576 dyda[LMP_CFACT] = -fcoll * yfibre * t30;
577 dyda[LMP_THETA] = t2 * t47 * t29 * (-t76 * t15 - t18 * t12 -
578 t15 * t24 - t56 * t18 * t76);
579 dyda[LMP_ORDER] = t2 * t47 *t29 *(lambda * t10 * t15 + t56 *
580 t18 * lambda * t10);
581 dyda[LMP_SPACE] = t2 * t47 * t29 * (-t9 * t94 * t15 -
582 t56 * t18 * t9 * t94);
583
584 }
585
586 return;
587
588}
589
590
591inline static void
592_giraffe_xoptmod2_ctor(GiModel *self, const GiModelData *model)
593{
594
595 cx_assert(self != NULL);
596 cx_assert(model != NULL);
597
598 self->name = cx_strdup(model->name);
599 self->type = model->type;
600
601 self->model = model->eval;
602
603
604 /*
605 * Arguments
606 */
607
608 self->arguments.names = cpl_propertylist_new();
609
610 cpl_propertylist_append_int(self->arguments.names, "xf", LMI_XFIB);
611 cpl_propertylist_append_int(self->arguments.names, "yf", LMI_YFIB);
612 cpl_propertylist_append_int(self->arguments.names, "lambda", LMI_WLEN);
613
614 self->arguments.count = cpl_propertylist_get_size(self->arguments.names);
615 self->arguments.values = cpl_matrix_new(self->arguments.count, 1);
616
617
618 /*
619 * Parameters
620 */
621
622 self->parameters.names = cpl_propertylist_new();
623
624 cpl_propertylist_append_int(self->parameters.names, "Orientation",
625 LMP_NX);
626 cpl_propertylist_append_int(self->parameters.names, "Order",
627 LMP_ORDER);
628 cpl_propertylist_append_int(self->parameters.names, "PixelSize",
629 LMP_PXSIZ);
630 cpl_propertylist_append_int(self->parameters.names, "FocalLength",
631 LMP_FCOLL);
632 cpl_propertylist_append_int(self->parameters.names, "Magnification",
633 LMP_CFACT);
634 cpl_propertylist_append_int(self->parameters.names, "Angle",
635 LMP_THETA);
636 cpl_propertylist_append_int(self->parameters.names, "Spacing",
637 LMP_SPACE);
638 cpl_propertylist_append_int(self->parameters.names, "Sdx",
639 LMP_SOFFX);
640 cpl_propertylist_append_int(self->parameters.names, "Sdy",
641 LMP_SOFFY);
642 cpl_propertylist_append_int(self->parameters.names, "Sphi",
643 LMP_SPHI);
644
645 self->parameters.count =
646 cpl_propertylist_get_size(self->parameters.names);
647 self->parameters.values = cpl_matrix_new(self->parameters.count, 1);
648
649 return;
650
651}
652
653
654inline static void
655_giraffe_xoptmod2_eval(cxdouble *y, cxdouble *x, cxdouble *a, cxint na,
656 cxdouble *dyda, cxdouble *r)
657{
658
659 const cxchar *const fctid = "_giraffe_xoptmod2_eval";
660
661
662 cxdouble lambda;
663 cxdouble xfibre;
664 cxdouble yfibre;
665
666 cxdouble pixsize, nx;
667 cxdouble fcoll, cfact;
668 cxdouble gtheta, gorder, gspace;
669 cxdouble slitdx, slitdy, slitphi;
670
671 register cxdouble t1, t2, t3, t4, t9;
672 register cxdouble t10, t11, t12, t14, t16, t17, t18, t19;
673 register cxdouble t20, t21, t23, t24, t26, t27, t28;
674 register cxdouble t30, t32, t33, t34, t35, t36, t37, t38, t39;
675 register cxdouble t40, t44, t49;
676 register cxdouble t52, t58;
677 register cxdouble t60, t61, t62, t64, t68;
678 register cxdouble t75, t76, t78;
679 register cxdouble t80;
680 register cxdouble t91, t93;
681 register cxdouble t104, t107;
682 register cxdouble t113, t119;
683 register cxdouble t120, t121, t124;
684 register cxdouble t136, t137, t138;
685 register cxdouble t143, t148;
686 register cxdouble t161, t162, t166, t168;
687 register cxdouble t173;
688 register cxdouble t191, t195, t196;
689 register cxdouble t201, t210;
690
691
692 if (na != 10) {
693 cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
694 return;
695 }
696
697 *y = 0.0;
698
699 if (dyda != NULL) {
700 dyda[LMP_NX] = 0.;
701 dyda[LMP_PXSIZ] = 0.;
702 dyda[LMP_FCOLL] = 0.;
703 dyda[LMP_CFACT] = 0.;
704 dyda[LMP_THETA] = 0.;
705 dyda[LMP_ORDER] = 0.;
706 dyda[LMP_SPACE] = 0.;
707 dyda[LMP_SOFFX] = 0.;
708 dyda[LMP_SOFFY] = 0.;
709 dyda[LMP_SPHI] = 0.;
710 }
711
712 lambda = x[LMI_WLEN]; /* wavelength [nm] */
713 xfibre = x[LMI_XFIB]; /* Y fibre [mm] */
714 yfibre = x[LMI_YFIB]; /* Y fibre [mm] */
715
716 nx = a[LMP_NX]; /* CCD size in X [pixels] */
717 pixsize = a[LMP_PXSIZ]; /* CCD pixel size [mm] */
718 fcoll = a[LMP_FCOLL]; /* collimator focal length [mm] */
719 cfact = a[LMP_CFACT]; /* camera magnification factor */
720 gtheta = a[LMP_THETA]; /* grating angle [radian] */
721 gorder = a[LMP_ORDER]; /* grating diffraction order */
722 gspace = a[LMP_SPACE]; /* grating groove spacing [mm] */
723 slitdx = a[LMP_SOFFX]; /* slit position x offset [mm] */
724 slitdy = a[LMP_SOFFY]; /* slit position y offset [mm] */
725 slitphi = a[LMP_SPHI]; /* slit position angle [radian] */
726
727 lambda *= GI_NM_TO_MM; /* wavelength [mm] */
728
729 t1 = cfact * fcoll;
730 t2 = cos(gtheta);
731 t3 = lambda * gorder;
732 t4 = 1.0 / gspace;
733 t9 = xfibre * (1.0 + slitphi * yfibre) + slitdx;
734 t10 = t9 * t2;
735 t11 = t9 * t9;
736 t12 = slitphi * slitphi;
737 t14 = sqrt(1.0 - t12);
738 t16 = yfibre * t14 + slitdy;
739 t17 = t16 * t16;
740 t18 = fcoll * fcoll;
741 t19 = t11 + t17 + t18;
742 t20 = sqrt(t19);
743 t21 = 1.0 / t20;
744 t23 = sin(gtheta);
745 t24 = fcoll * t23;
746 t26 = -t3 * t4 + t10 * t21 + t24 * t21;
747 t27 = t2 * t26;
748 t28 = 1.0 / t19;
749 t30 = t26 * t26;
750 t32 = sqrt(1.0 - t17 * t28 - t30);
751 t33 = t23 * t32;
752 t34 = t27 + t33;
753 t35 = t23 * t26;
754 t36 = t2 * t32;
755 t37 = -t35 + t36;
756 t38 = 1.0 / t37;
757 t39 = t34 * t38;
758 t40 = 1.0 / pixsize;
759 t44 = pixsize * pixsize;
760 t49 = t38 * t40;
761 t52 = 1.0 / t20 / t19;
762 t58 = -t10 * t52 * fcoll + t23 * t21 - t18 * t23 * t52;
763 t60 = 1.0 / t32;
764 t61 = t23 * t60;
765 t62 = t19 * t19;
766 t64 = t17 / t62;
767 t68 = 2.0 * t64 * fcoll - 2.0 * t26 * t58;
768 t75 = t1 * t34;
769 t76 = t37 * t37;
770 t78 = 1.0 / t76 * t40;
771 t80 = t2 * t60;
772 t91 = -t9 * t23 * t21 + fcoll * t2 * t21;
773 t93 = t26 * t91;
774 t104 = t2 * lambda;
775 t107 = t26 * lambda * t4;
776 t113 = t23 * lambda;
777 t119 = gspace * gspace;
778 t120 = 1.0 / t119;
779 t121 = gorder * t120;
780 t124 = t3 * t120;
781 t136 = t2 * t21;
782 t137 = 2.0 * t9;
783 t138 = t52 * t137;
784 t143 = t136 - t10 * t138 / 2.0 - t24 * t138 / 2.0;
785 t148 = t64 * t137 - 2.0 * t26 * t143;
786 t161 = 2.0 * t16;
787 t162 = t52 * t161;
788 t166 = -t10 * t162 / 2.0 - t24 * t162 / 2.0;
789 t168 = t16 * t28;
790 t173 = -2.0 * t168 + t64 * t161 - 2.0 * t26 * t166;
791 t191 = 1.0 / t14;
792 t195 = 2.0 * t9 * xfibre * yfibre - 2.0 * t16 * yfibre * t191 * slitphi;
793 t196 = t52 * t195;
794 t201 = xfibre * yfibre * t136 - t10 * t196 / 2.0 - t24 * t196 / 2.0;
795 t210 = 2.0 * t168 * yfibre * t191 * slitphi + t64 * t195 -
796 2.0 * t26 * t201;
797
798
799 /*
800 * Take care of model direction
801 */
802
803 if (nx < 0.0) {
804 *y = t1 * t39 * t40 - 0.5 * nx;
805 }
806 else {
807 *y = -t1 * t39 * t40 + 0.5 * nx;
808 }
809
810
811 /*
812 * If requested, compute the partial derivatives of y
813 * with respect to each parameter.
814 */
815
816 if (dyda != NULL) {
817
818 dyda[LMP_NX] = 0.5;
819 dyda[LMP_PXSIZ] = -t1 * t39 / t44;
820 dyda[LMP_FCOLL] = cfact * t34 * t49 + t1 *
821 (t2 * t58 + t61 * t68 / 2.0) * t38 * t40 -
822 t75 * t78 * (-t23 * t58 + t80 * t68 / 2.0);
823 dyda[LMP_CFACT] = fcoll * t34 * t49;
824 dyda[LMP_THETA] = t1 * (-t35 + t2 * t91 + t36 - t61 * t93) * t38 *
825 t40 - t75 * t78 * (-t27 - t23 * t91 - t33 - t80 * t93);
826 dyda[LMP_ORDER] = t1 * (-t104 * t4 + t61 * t107) * t38 * t40 - t75 *
827 t78 * (t113 * t4 + t80 * t107);
828 dyda[LMP_SPACE] = t1 * (t104 * t121 - t61 * t26 * t124) * t38 * t40 -
829 t75 * t78 * (-t113 * t121 - t80 * t26 * t124);
830 dyda[LMP_SOFFX] = t1 * (t2 * t143 + t61 * t148 / 2.0) * t38 * t40 -
831 t75 * t78 * (-t23 * t143 + t80 * t148 / 2.0);
832 dyda[LMP_SOFFY] = t1 * (t2 * t166 + t61 * t173 / 2.0) * t38 * t40 -
833 t75 * t78 * (-t23 * t166 + t80 * t173 / 2.0);
834 dyda[LMP_SPHI] = t1 * (t2 * t201 + t61 * t210 / 2.0) * t38 * t40 -
835 t75 * t78 * (-t23 * t201 + t80 * t210 / 2.0);
836
837 if (nx > 0.0) {
838 dyda[LMP_NX] = -dyda[LMP_NX];
839 dyda[LMP_PXSIZ] = -dyda[LMP_PXSIZ];
840 dyda[LMP_FCOLL] = -dyda[LMP_FCOLL];
841 dyda[LMP_CFACT] = -dyda[LMP_CFACT];
842 dyda[LMP_THETA] = -dyda[LMP_THETA];
843 dyda[LMP_ORDER] = -dyda[LMP_ORDER];
844 dyda[LMP_SPACE] = -dyda[LMP_SPACE];
845 dyda[LMP_SOFFX] = -dyda[LMP_SOFFX];
846 dyda[LMP_SOFFY] = -dyda[LMP_SOFFY];
847 dyda[LMP_SPHI] = -dyda[LMP_SPHI];
848 }
849
850 if (r != NULL) {
851
852 register cxint k;
853
854
855 k = LMP_PXSIZ << 1;
856 if (r[k + 1] > 0) {
857 dyda[LMP_PXSIZ] *= _giraffe_dydaweight(a[LMP_PXSIZ], r[k],
858 r[k + 1]);
859 }
860
861 k = LMP_FCOLL << 1;
862 if (r[k + 1] > 0) {
863 dyda[LMP_FCOLL] *= _giraffe_dydaweight(a[LMP_FCOLL], r[k],
864 r[k + 1]);
865 }
866
867 k = LMP_CFACT << 1;
868 if (r[k + 1] > 0) {
869 dyda[LMP_CFACT] *= _giraffe_dydaweight(a[LMP_CFACT], r[k],
870 r[k + 1]);
871 }
872
873 k = LMP_THETA << 1;
874 if (r[k + 1] > 0) {
875 dyda[LMP_THETA] *= _giraffe_dydaweight(a[LMP_THETA], r[k],
876 r[k + 1]);
877 }
878
879 k = LMP_ORDER << 1;
880 if (r[k + 1] > 0) {
881 dyda[LMP_ORDER] *= _giraffe_dydaweight(a[LMP_ORDER], r[k],
882 r[k + 1]);
883 }
884
885 k = LMP_SPACE << 1;
886 if (r[k + 1] > 0) {
887 dyda[LMP_SPACE] *= _giraffe_dydaweight(a[LMP_SPACE], r[k],
888 r[k + 1]);
889 }
890
891 k = LMP_SOFFX << 1;
892 if (r[k + 1] > 0) {
893 dyda[LMP_SOFFX] *= _giraffe_dydaweight(a[LMP_SOFFX], r[k],
894 r[k + 1]);
895 }
896
897 k = LMP_SOFFY << 1;
898 if (r[k + 1] > 0) {
899 dyda[LMP_SOFFY] *= _giraffe_dydaweight(a[LMP_SOFFY], r[k],
900 r[k + 1]);
901 }
902
903 k = LMP_SPHI << 1;
904 if (r[k + 1] > 0) {
905 dyda[LMP_SPHI] *= _giraffe_dydaweight(a[LMP_SPHI], r[k],
906 r[k + 1]);
907 }
908
909 }
910
911 }
912
913 return;
914
915}
916
917
918inline static void
919_giraffe_yoptmod2_ctor(GiModel *self, const GiModelData *model)
920{
921
922 cx_assert(self != NULL);
923 cx_assert(model != NULL);
924
925 self->name = cx_strdup(model->name);
926 self->type = model->type;
927
928 self->model = model->eval;
929
930
931 /*
932 * Arguments
933 */
934
935 self->arguments.names = cpl_propertylist_new();
936
937 cpl_propertylist_append_int(self->arguments.names, "xf", LMI_XFIB);
938 cpl_propertylist_append_int(self->arguments.names, "yf", LMI_YFIB);
939 cpl_propertylist_append_int(self->arguments.names, "lambda", LMI_WLEN);
940
941 self->arguments.count = cpl_propertylist_get_size(self->arguments.names);
942 self->arguments.values = cpl_matrix_new(self->arguments.count, 1);
943
944
945 /*
946 * Parameters
947 */
948
949 self->parameters.names = cpl_propertylist_new();
950
951 cpl_propertylist_append_int(self->parameters.names, "Orientation",
952 LMP_NY);
953 cpl_propertylist_append_int(self->parameters.names, "Order",
954 LMP_ORDER);
955 cpl_propertylist_append_int(self->parameters.names, "PixelSize",
956 LMP_PXSIZ);
957 cpl_propertylist_append_int(self->parameters.names, "FocalLength",
958 LMP_FCOLL);
959 cpl_propertylist_append_int(self->parameters.names, "Magnification",
960 LMP_CFACT);
961 cpl_propertylist_append_int(self->parameters.names, "Angle",
962 LMP_THETA);
963 cpl_propertylist_append_int(self->parameters.names, "Spacing",
964 LMP_SPACE);
965 cpl_propertylist_append_int(self->parameters.names, "Sdx",
966 LMP_SOFFX);
967 cpl_propertylist_append_int(self->parameters.names, "Sdy",
968 LMP_SOFFY);
969 cpl_propertylist_append_int(self->parameters.names, "Sphi",
970 LMP_SPHI);
971
972 self->parameters.count =
973 cpl_propertylist_get_size(self->parameters.names);
974 self->parameters.values = cpl_matrix_new(self->parameters.count, 1);
975
976 return;
977
978}
979
980
981inline static void
982_giraffe_yoptmod2_eval(cxdouble *y, cxdouble *x, cxdouble *a, cxint na,
983 cxdouble *dyda, cxdouble *r)
984{
985
986 const cxchar *const fctid = "_giraffe_yoptmod2_eval";
987
988
989 cxdouble lambda, xfibre, yfibre;
990 cxdouble pixsize, ny;
991 cxdouble fcoll, cfact;
992 cxdouble gtheta, gorder, gspace;
993 cxdouble slitdx, slitdy, slitphi;
994
995 register cxdouble t1, t2, t4, t6, t7;
996 register cxdouble t11, t12, t13, t14, t15, t16, t17, t18, t19;
997 register cxdouble t21, t22, t24, t25, t27, t29;
998 register cxdouble t31, t33, t35, t36, t37, t38, t39;
999 register cxdouble t42, t50, t51, t54, t56;
1000 register cxdouble t62, t65, t66, t68;
1001 register cxdouble t85;
1002 register cxdouble t102, t103;
1003 register cxdouble t112, t117, t118;
1004 register cxdouble t123;
1005 register cxdouble t136;
1006 register cxdouble t141, t145, t147;
1007 register cxdouble t159;
1008 register cxdouble t160;
1009 register cxdouble t172, t179;
1010 register cxdouble t184;
1011
1012
1013 /* Not used */
1014 (void) r;
1015
1016 if (na != 10) {
1017 cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
1018 return;
1019 }
1020
1021 *y = 0.0;
1022
1023 if (dyda != NULL) {
1024 dyda[LMP_NY] = 0.;
1025 dyda[LMP_PXSIZ] = 0.;
1026 dyda[LMP_FCOLL] = 0.;
1027 dyda[LMP_CFACT] = 0.;
1028 dyda[LMP_THETA] = 0.;
1029 dyda[LMP_ORDER] = 0.;
1030 dyda[LMP_SPACE] = 0.;
1031 dyda[LMP_SOFFX] = 0.;
1032 dyda[LMP_SOFFY] = 0.;
1033 dyda[LMP_SPHI] = 0.;
1034 }
1035
1036 lambda = x[LMI_WLEN];
1037 xfibre = x[LMI_XFIB];
1038 yfibre = x[LMI_YFIB];
1039
1040 ny = a[LMP_NY];
1041 pixsize = a[LMP_PXSIZ];
1042 fcoll = a[LMP_FCOLL];
1043 cfact = a[LMP_CFACT];
1044 gtheta = a[LMP_THETA];
1045 gorder = a[LMP_ORDER];
1046 gspace = a[LMP_SPACE];
1047 slitdx = a[LMP_SOFFX];
1048 slitdy = a[LMP_SOFFY];
1049 slitphi = a[LMP_SPHI];
1050
1051 lambda *= GI_NM_TO_MM;
1052
1053 t1 = cfact * fcoll;
1054 t2 = slitphi * slitphi;
1055 t4 = sqrt(1.0 - t2);
1056 t6 = yfibre * t4 + slitdy;
1057 t7 = t1 * t6;
1058 t11 = xfibre * (1.0 + slitphi * yfibre) + slitdx;
1059 t12 = t11 * t11;
1060 t13 = t6 * t6;
1061 t14 = fcoll * fcoll;
1062 t15 = t12 + t13 + t14;
1063 t16 = sqrt(t15);
1064 t17 = 1 / t16;
1065 t18 = lambda * gorder;
1066 t19 = 1 / gspace;
1067 t21 = cos(gtheta);
1068 t22 = t11 * t21;
1069 t24 = sin(gtheta);
1070 t25 = fcoll * t24;
1071 t27 = -t18 * t19 + t22 * t17 + t25 * t17;
1072 t29 = 1 / t15;
1073 t31 = t27 * t27;
1074 t33 = sqrt(1.0 - t13 * t29 - t31);
1075 t35 = -t27 * t24 + t21 * t33;
1076 t36 = 1 / t35;
1077 t37 = t17 * t36;
1078 t38 = 1 / pixsize;
1079 t39 = t37 * t38;
1080 t42 = pixsize * pixsize;
1081 t50 = 1 / t16 / t15;
1082 t51 = t50 * t36;
1083 t54 = t35 * t35;
1084 t56 = t17 / t54;
1085 t62 = -t22 * t50 * fcoll + t24 * t17 - t14 * t24 * t50;
1086 t65 = t21 / t33;
1087 t66 = t15 * t15;
1088 t68 = t13 / t66;
1089 t85 = -t11 * t24 * t17 + fcoll * t21 * t17;
1090 t102 = gspace * gspace;
1091 t103 = 1 / t102;
1092 t112 = 2.0 * t11;
1093 t117 = t21 * t17;
1094 t118 = t50 * t112;
1095 t123 = t117 - t22 * t118 / 2.0 - t25 * t118 / 2.0;
1096 t136 = 2.0 * t6;
1097 t141 = t50 * t136;
1098 t145 = -t22 * t141 / 2.0 - t25 * t141 / 2.0;
1099 t147 = t6 * t29;
1100 t159 = 1 / t4;
1101 t160 = yfibre * t159;
1102 t172 = 2.0 * t11 * xfibre * yfibre - 2.0 * t6 * yfibre * t159 * slitphi;
1103 t179 = t50 * t172;
1104 t184 = xfibre * yfibre * t117 - t22 * t179 / 2.0 - t25 * t179 / 2.0;
1105
1106 *y = -t7 * t39 + 0.5 * ny;
1107
1108 if (dyda != NULL) {
1109
1110 dyda[LMP_NY] = 0.5;
1111 dyda[LMP_PXSIZ] = t7 * t37 / t42;
1112 dyda[LMP_FCOLL] = -cfact * t6 * t39 + cfact * t14 * t6 * t51 * t38 +
1113 t7 * t56 * t38 * (-t62 * t24 + t65 * (2.0 * t68 * fcoll -
1114 2.0 * t27 * t62) / 2.0);
1115 dyda[LMP_CFACT] = -fcoll * t6 * t39;
1116 dyda[LMP_THETA] = t7 * t56 * t38 * (-t85 * t24 - t27 * t21 - t24 *
1117 t33 - t65 * t27 * t85);
1118 dyda[LMP_ORDER] = t7 * t56 * t38 * (lambda * t19 * t24 + t65 * t27 *
1119 lambda * t19);
1120 dyda[LMP_SPACE] = t7 * t56 * t38 * (-t18 * t103 * t24 - t65 * t27 *
1121 t18 * t103);
1122 dyda[LMP_SOFFX] = t7 * t51 * t38 * t112 / 2.0 + t7 * t56 * t38 *
1123 (-t123 * t24 + t65 * (t68 * t112 - 2.0 * t27 * t123) / 2.0);
1124 dyda[LMP_SOFFY] = -t1 * t39 + t7 * t51 * t38 * t136 / 2.0 + t7 *
1125 t56 * t38 * (-t145 * t24 + t65 * (-2.0 * t147 + t68 * t136 -
1126 2.0 * t27 * t145) / 2.0);
1127 dyda[LMP_SPHI] = t1 * t160 * slitphi * t17 * t36 * t38 + t7 * t51 *
1128 t38 * t172 / 2.0 + t7 * t56 * t38 *
1129 (-t184 * t24 + t65 * (2.0 * t147 * t160 * slitphi + t68 * t172 -
1130 2.0 * t27 * t184) / 2.0);
1131
1132 }
1133
1134 return;
1135
1136}
1137
1138
1139inline static void
1140_giraffe_gaussian_ctor(GiModel *self, const GiModelData *model)
1141{
1142
1143 cx_assert(self != NULL);
1144 cx_assert(model != NULL);
1145
1146 self->name = cx_strdup(model->name);
1147 self->type = model->type;
1148
1149 self->model = model->eval;
1150
1151
1152 /*
1153 * Arguments
1154 */
1155
1156 self->arguments.names = cpl_propertylist_new();
1157
1158 cpl_propertylist_append_int(self->arguments.names, "x", 0);
1159
1160 self->arguments.count = cpl_propertylist_get_size(self->arguments.names);
1161 self->arguments.values = cpl_matrix_new(self->arguments.count, 1);
1162
1163
1164 /*
1165 * Parameters
1166 */
1167
1168 self->parameters.names = cpl_propertylist_new();
1169
1170 cpl_propertylist_append_int(self->parameters.names, "Amplitude",
1171 LMP_AMPL);
1172 cpl_propertylist_append_int(self->parameters.names, "Center",
1173 LMP_CENT);
1174 cpl_propertylist_append_int(self->parameters.names, "Background",
1175 LMP_BACK);
1176 cpl_propertylist_append_int(self->parameters.names, "Width1",
1177 LMP_WID1);
1178
1179 self->parameters.count =
1180 cpl_propertylist_get_size(self->parameters.names);
1181 self->parameters.values = cpl_matrix_new(self->parameters.count, 1);
1182
1183 return;
1184
1185}
1186
1187
1188inline static void
1189_giraffe_gaussian_eval(cxdouble *y, cxdouble *x, cxdouble *a, cxint na,
1190 cxdouble *dyda, cxdouble *r)
1191{
1192
1193 const cxchar *const fctid = "_giraffe_gaussian_eval";
1194
1195
1196 cxdouble fac;
1197 cxdouble ex;
1198 cxdouble amplitude;
1199 cxdouble center;
1200 cxdouble backg;
1201 cxdouble width;
1202 cxdouble xred;
1203
1204
1205 /* Not used */
1206 (void) r;
1207
1208 if (na != 4) {
1209 cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
1210 return;
1211 }
1212
1213 *y = 0.0;
1214
1215 if (dyda != NULL) {
1216 dyda[LMP_AMPL] = 0.;
1217 dyda[LMP_CENT] = 0.;
1218 dyda[LMP_BACK] = 0.;
1219 dyda[LMP_WID1] = 0.;
1220 }
1221
1222
1223 amplitude = a[LMP_AMPL];
1224 center = a[LMP_CENT];
1225 backg = a[LMP_BACK];
1226 width = a[LMP_WID1];
1227
1228 xred = (x[0] - center) / width;
1229
1230 ex = exp(-xred * xred / 2.);
1231 fac = amplitude * xred * ex;
1232
1233 *y = amplitude * ex + backg;
1234
1235
1236 /*
1237 * If requested, compute the partial derivatives of y
1238 * with respect to each parameter.
1239 */
1240
1241 if (dyda != NULL) {
1242
1243 dyda[LMP_AMPL] = ex; /* d(y)/d(amplitude) */
1244 dyda[LMP_CENT] = fac / width; /* d(y)/d(center) */
1245 dyda[LMP_BACK] = 1.; /* d(y)/d(backg) */
1246 dyda[LMP_WID1] = (fac * xred) / width; /* d(y)/d(width) */
1247
1248 }
1249
1250 return;
1251
1252}
1253
1254
1255inline static void
1256_giraffe_psfcos_ctor(GiModel *self, const GiModelData *model)
1257{
1258
1259 cx_assert(self != NULL);
1260 cx_assert(model != NULL);
1261
1262 self->name = cx_strdup(model->name);
1263 self->type = model->type;
1264
1265 self->model = model->eval;
1266
1267
1268 /*
1269 * Arguments
1270 */
1271
1272 self->arguments.names = cpl_propertylist_new();
1273
1274 cpl_propertylist_append_int(self->arguments.names, "x", 0);
1275
1276 self->arguments.count = cpl_propertylist_get_size(self->arguments.names);
1277 self->arguments.values = cpl_matrix_new(self->arguments.count, 1);
1278
1279
1280 /*
1281 * Parameters
1282 */
1283
1284 self->parameters.names = cpl_propertylist_new();
1285
1286 cpl_propertylist_append_int(self->parameters.names, "Amplitude",
1287 LMP_AMPL);
1288 cpl_propertylist_append_int(self->parameters.names, "Center",
1289 LMP_CENT);
1290 cpl_propertylist_append_int(self->parameters.names, "Background",
1291 LMP_BACK);
1292 cpl_propertylist_append_int(self->parameters.names, "Width1",
1293 LMP_WID1);
1294 cpl_propertylist_append_int(self->parameters.names, "Width2",
1295 LMP_WID2);
1296
1297 self->parameters.count =
1298 cpl_propertylist_get_size(self->parameters.names);
1299 self->parameters.values = cpl_matrix_new(self->parameters.count, 1);
1300
1301 return;
1302
1303}
1304
1305
1306inline static void
1307_giraffe_psfcos_eval(cxdouble *y, cxdouble *x, cxdouble *a, cxint na,
1308 cxdouble *dyda, cxdouble *r)
1309{
1310
1311 const cxchar *const fctid = "_giraffe_psfcos_eval";
1312
1313
1314 cxdouble amplitude;
1315 cxdouble center;
1316 cxdouble background;
1317 cxdouble width1;
1318 cxdouble width2;
1319
1320 cxdouble t1, t2, t3, t4, t5, t6, t7, t8, t9;
1321 cxdouble t10, t13, t14, t15, t16;
1322 cxdouble t26;
1323
1324
1325 /* Not used */
1326 (void) r;
1327
1328 if (na != 5) {
1329 cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
1330 return;
1331 }
1332
1333 *y = 0.0;
1334
1335 if (dyda != NULL) {
1336 dyda[LMP_AMPL] = 0.;
1337 dyda[LMP_CENT] = 0.;
1338 dyda[LMP_BACK] = 0.;
1339 dyda[LMP_WID1] = 0.;
1340 dyda[LMP_WID2] = 0.;
1341 }
1342
1343 amplitude = a[LMP_AMPL];
1344 center = a[LMP_CENT];
1345 background = a[LMP_BACK];
1346 width1 = a[LMP_WID1];
1347 width2 = a[LMP_WID2];
1348
1349 t1 = x[0] - center;
1350 t2 = fabs(t1);
1351 t3 = 1.0 / width2;
1352 t4 = t2 * t3;
1353 t5 = pow(t4, width1);
1354 t6 = CX_PI * t5;
1355 t7 = cos(t6);
1356 t8 = 1.0 + t7;
1357 t9 = t8 * t8;
1358 t10 = t9 * t8;
1359 t13 = amplitude * t9;
1360 t14 = sin(t6);
1361 t15 = t13 * t14;
1362 t16 = log(t4);
1363 t26 = t1 > 0.0 ? 1.0 : -1.0;
1364
1365 if (t2 > width2) {
1366 *y = background;
1367
1368 if (dyda != NULL) {
1369 dyda[LMP_WID2] = 1.0;
1370 }
1371 }
1372 else {
1373 *y = amplitude * t10 / 8.0 + background;
1374
1375 if (dyda != NULL) {
1376
1377 dyda[LMP_AMPL] = t10 / 8.0;
1378 dyda[LMP_CENT] = 3.0 / 8.0 * t13 * t14 * CX_PI * t5 *
1379 width1 * t26 / t2;
1380 dyda[LMP_BACK] = 1.0;
1381 dyda[LMP_WID1] = -3.0 / 8.0 * t15 * t6 * t16;
1382 dyda[LMP_WID2] = 3.0 / 8.0 * t15 * t6 * width1 * t3;
1383
1384 }
1385 }
1386
1387 return;
1388
1389}
1390
1391
1392inline static void
1393_giraffe_psfexp_ctor(GiModel *self, const GiModelData *model)
1394{
1395
1396 cx_assert(self != NULL);
1397 cx_assert(model != NULL);
1398
1399 self->name = cx_strdup(model->name);
1400 self->type = model->type;
1401
1402 self->model = model->eval;
1403
1404
1405 /*
1406 * Arguments
1407 */
1408
1409 self->arguments.names = cpl_propertylist_new();
1410
1411 cpl_propertylist_append_int(self->arguments.names, "x", 0);
1412
1413 self->arguments.count = cpl_propertylist_get_size(self->arguments.names);
1414 self->arguments.values = cpl_matrix_new(self->arguments.count, 1);
1415
1416
1417 /*
1418 * Parameters
1419 */
1420
1421 self->parameters.names = cpl_propertylist_new();
1422
1423 cpl_propertylist_append_int(self->parameters.names, "Amplitude",
1424 LMP_AMPL);
1425 cpl_propertylist_append_int(self->parameters.names, "Center",
1426 LMP_CENT);
1427 cpl_propertylist_append_int(self->parameters.names, "Background",
1428 LMP_BACK);
1429 cpl_propertylist_append_int(self->parameters.names, "Width1",
1430 LMP_WID1);
1431 cpl_propertylist_append_int(self->parameters.names, "Width2",
1432 LMP_WID2);
1433
1434 self->parameters.count =
1435 cpl_propertylist_get_size(self->parameters.names);
1436 self->parameters.values = cpl_matrix_new(self->parameters.count, 1);
1437
1438 return;
1439
1440}
1441
1442
1443inline static void
1444_giraffe_psfexp_eval(cxdouble *y, cxdouble *x, cxdouble *a, cxint na,
1445 cxdouble *dyda, cxdouble *r)
1446{
1447
1448 const cxchar *const fctid = "_giraffe_psfexp_eval";
1449
1450 cxdouble amplitude;
1451 cxdouble center;
1452 cxdouble background;
1453 cxdouble width1;
1454 cxdouble width2;
1455
1456 cxdouble t1, t2, t3, t4, t6, t8;
1457 cxdouble t10, t15, t18;
1458
1459
1460 r = NULL;
1461
1462 if (na != 5) {
1463 cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
1464 return;
1465 }
1466
1467 *y = 0.0;
1468
1469 if (dyda != NULL) {
1470 dyda[LMP_AMPL] = 0.;
1471 dyda[LMP_CENT] = 0.;
1472 dyda[LMP_BACK] = 0.;
1473 dyda[LMP_WID1] = 0.;
1474 dyda[LMP_WID2] = 0.;
1475 }
1476
1477 amplitude = a[LMP_AMPL];
1478 center = a[LMP_CENT];
1479 background = a[LMP_BACK];
1480 width1 = a[LMP_WID1];
1481 width2 = a[LMP_WID2];
1482
1483 t1 = x[0] - center;
1484
1485 if (t1 > 0.0) {
1486 t2 = t1;
1487 t10 = 1.0;
1488 }
1489 else {
1490 t2 = -t1;
1491 t10 = -1.0;
1492 }
1493
1494 t3 = pow(t2, width2);
1495 t4 = 1.0 / width1;
1496 t6 = exp(-t3 * t4);
1497 t8 = amplitude * t3;
1498 t15 = width1 * width1;
1499 t18 = log(t2);
1500
1501 *y = amplitude * t6 + background;
1502
1503 if (dyda != NULL) {
1504 dyda[LMP_AMPL] = t6;
1505 dyda[LMP_BACK] = 1.0;
1506
1507 dyda[LMP_CENT] = t8 * width2 * t10 / t2 * t4 * t6;
1508
1509 if (isnan(dyda[LMP_CENT])) {
1510 dyda[LMP_CENT] = 0.;
1511 }
1512
1513 dyda[LMP_WID1] = t8 / t15 * t6;
1514
1515 if (isnan(dyda[LMP_WID1])) {
1516 dyda[LMP_WID1] = 0.;
1517 }
1518
1519 dyda[LMP_WID2] = -t8 * t18 * t4 * t6;
1520
1521 if (isnan(dyda[LMP_WID2])) {
1522 dyda[LMP_WID2] = 0.;
1523 }
1524
1525 if (r != NULL) {
1526
1527 register cxint k;
1528
1529 k = LMP_AMPL << 1;
1530 if (r[k + 1] > 0) {
1531 dyda[LMP_AMPL] *= _giraffe_dydaweight(a[LMP_AMPL], r[k],
1532 r[k + 1]);
1533 }
1534
1535 k = LMP_CENT << 1;
1536 if (r[k + 1] > 0) {
1537 dyda[LMP_CENT] *= _giraffe_dydaweight(a[LMP_CENT], r[k],
1538 r[k + 1]);
1539 }
1540
1541 k = LMP_WID1 << 1;
1542 if (r[k + 1] > 0) {
1543 dyda[LMP_WID1] *= _giraffe_dydaweight(a[LMP_WID1], r[k],
1544 r[k + 1]);
1545 }
1546
1547 k = LMP_WID2 << 1;
1548 if (r[k + 1] > 0) {
1549 dyda[LMP_WID2] *= _giraffe_dydaweight(a[LMP_WID2], r[k],
1550 r[k + 1]);
1551 }
1552
1553 }
1554
1555 }
1556
1557 return;
1558
1559}
1560
1561
1562inline static void
1563_giraffe_psfexp2_ctor(GiModel *self, const GiModelData *model)
1564{
1565
1566 cx_assert(self != NULL);
1567 cx_assert(model != NULL);
1568
1569 self->name = cx_strdup(model->name);
1570 self->type = model->type;
1571
1572 self->model = model->eval;
1573
1574
1575 /*
1576 * Arguments
1577 */
1578
1579 self->arguments.names = cpl_propertylist_new();
1580
1581 cpl_propertylist_append_int(self->arguments.names, "x", 0);
1582
1583 self->arguments.count = cpl_propertylist_get_size(self->arguments.names);
1584 self->arguments.values = cpl_matrix_new(self->arguments.count, 1);
1585
1586
1587 /*
1588 * Parameters
1589 */
1590
1591 self->parameters.names = cpl_propertylist_new();
1592
1593 cpl_propertylist_append_int(self->parameters.names, "Amplitude",
1594 LMP_AMPL);
1595 cpl_propertylist_append_int(self->parameters.names, "Center",
1596 LMP_CENT);
1597 cpl_propertylist_append_int(self->parameters.names, "Background",
1598 LMP_BACK);
1599 cpl_propertylist_append_int(self->parameters.names, "Width1",
1600 LMP_WID1);
1601 cpl_propertylist_append_int(self->parameters.names, "Width2",
1602 LMP_WID2);
1603
1604 self->parameters.count =
1605 cpl_propertylist_get_size(self->parameters.names);
1606 self->parameters.values = cpl_matrix_new(self->parameters.count, 1);
1607
1608 return;
1609
1610}
1611
1612
1613inline static void
1614_giraffe_psfexp2_eval(cxdouble *y, cxdouble *x, cxdouble *a, cxint na,
1615 cxdouble *dyda, cxdouble *r)
1616{
1617
1618 const cxchar *const fctid = "_giraffe_psfexp2_eval";
1619
1620 cxdouble amplitude;
1621 cxdouble center;
1622 cxdouble background;
1623 cxdouble width1;
1624 cxdouble width2;
1625
1626 cxdouble t1, t2, t3, t4, t5, t6, t8;
1627 cxdouble t10, t16;
1628
1629
1630 r = NULL;
1631
1632 if (na != 5) {
1633 cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
1634 return;
1635 }
1636
1637 *y = 0.0;
1638
1639 if (dyda != NULL) {
1640 dyda[LMP_AMPL] = 0.;
1641 dyda[LMP_CENT] = 0.;
1642 dyda[LMP_BACK] = 0.;
1643 dyda[LMP_WID1] = 0.;
1644 dyda[LMP_WID2] = 0.;
1645 }
1646
1647 amplitude = a[LMP_AMPL];
1648 center = a[LMP_CENT];
1649 background = a[LMP_BACK];
1650 width1 = a[LMP_WID1];
1651 width2 = a[LMP_WID2];
1652
1653 t1 = x[0] - center;
1654
1655 if (t1 > 0.0) {
1656 t2 = t1;
1657 t10 = 1.0;
1658 }
1659 else {
1660 t2 = -t1;
1661 t10 = -1.0;
1662 }
1663
1664 t3 = 1.0 / width1;
1665 t4 = t2 * t3;
1666 t5 = pow(t4, width2);
1667 t6 = exp(-t5);
1668 t8 = amplitude * t5;
1669 t16 = log(t4);
1670
1671 *y = amplitude * t6 + background;
1672
1673 if (dyda != NULL) {
1674
1675 dyda[LMP_AMPL] = t6;
1676
1677 dyda[LMP_CENT] = t8 * width2 * t10 / t2 * t6;
1678
1679 if (isnan(dyda[LMP_CENT])) {
1680 dyda[LMP_CENT] = 0.0;
1681 }
1682
1683 dyda[LMP_BACK] = 1.0;
1684 dyda[LMP_WID1] = t8 * width2 * t3 * t6;
1685
1686 dyda[LMP_WID2] = -t8 * t16 * t6;
1687
1688 if (isnan(dyda[LMP_WID2])) {
1689 dyda[LMP_WID2] = 0.0;
1690 }
1691
1692 if (r != NULL) {
1693
1694 register cxint k;
1695
1696 k = LMP_AMPL << 1;
1697 if (r[k + 1] > 0) {
1698 dyda[LMP_AMPL] *= _giraffe_dydaweight(a[LMP_AMPL], r[k],
1699 r[k + 1]);
1700 }
1701
1702 k = LMP_CENT << 1;
1703 if (r[k + 1] > 0) {
1704 dyda[LMP_CENT] *= _giraffe_dydaweight(a[LMP_CENT], r[k],
1705 r[k + 1]);
1706 }
1707
1708 k = LMP_WID1 << 1;
1709 if (r[k + 1] > 0) {
1710 dyda[LMP_WID1] *= _giraffe_dydaweight(a[LMP_WID1], r[k],
1711 r[k + 1]);
1712 }
1713
1714 k = LMP_WID2 << 1;
1715 if (r[k + 1] > 0) {
1716 dyda[LMP_WID2] *= _giraffe_dydaweight(a[LMP_WID2], r[k],
1717 r[k + 1]);
1718 }
1719
1720 }
1721
1722 }
1723
1724 return;
1725
1726}
1727
1728
1729inline static void
1730_giraffe_test_ctor(GiModel *self, const GiModelData *model)
1731{
1732
1733 cx_assert(self != NULL);
1734 cx_assert(model != NULL);
1735
1736 self->name = cx_strdup(model->name);
1737 self->type = model->type;
1738
1739 self->model = model->eval;
1740
1741
1742 /*
1743 * Arguments
1744 */
1745
1746 self->arguments.names = cpl_propertylist_new();
1747
1748 cpl_propertylist_append_int(self->arguments.names, "x", 0);
1749
1750 self->arguments.count = cpl_propertylist_get_size(self->arguments.names);
1751 self->arguments.values = cpl_matrix_new(self->arguments.count, 1);
1752
1753
1754 /*
1755 * Parameters
1756 */
1757
1758 self->parameters.names = cpl_propertylist_new();
1759
1760 cpl_propertylist_append_int(self->parameters.names, "Slope", 0);
1761 cpl_propertylist_append_int(self->parameters.names, "Intercept", 1);
1762
1763 self->parameters.count =
1764 cpl_propertylist_get_size(self->parameters.names);
1765 self->parameters.values = cpl_matrix_new(self->parameters.count, 1);
1766
1767 return;
1768
1769}
1770
1771
1772inline static void
1773_giraffe_test_eval(cxdouble *y, cxdouble *x, cxdouble *a, cxint na,
1774 cxdouble *dyda, cxdouble *r)
1775{
1776
1777 const cxchar *const fctid = "_giraffe_test_eval";
1778
1779
1780 cxdouble a1;
1781 cxdouble b1;
1782
1783
1784 /* Not used */
1785 (void) r;
1786
1787 if (na != 2) {
1788 cpl_error_set(fctid, CPL_ERROR_ILLEGAL_INPUT);
1789 return;
1790 }
1791
1792 *y = 0.0;
1793
1794 if (dyda != NULL) {
1795 dyda[0] = 0.;
1796 dyda[1] = 0.;
1797 }
1798
1799 a1 = a[0];
1800 b1 = a[1];
1801
1802 *y = a1 * x[0] + b1;
1803
1804 if (dyda != NULL) {
1805
1806 dyda[0] = x[0];
1807 dyda[1] = 0.0;
1808
1809 }
1810
1811 return;
1812
1813}
1814
1815
1816/*
1817 * Model registry
1818 */
1819
1820static GiModelData _gimodels[] = {
1821 {"xoptmod", GI_MODEL_XOPT,
1822 _giraffe_xoptmod_ctor, _giraffe_model_dtor, _giraffe_xoptmod_eval},
1823 {"yoptmod", GI_MODEL_YOPT,
1824 _giraffe_yoptmod_ctor, _giraffe_model_dtor, _giraffe_yoptmod_eval},
1825 {"xoptmod2", GI_MODEL_XOPT,
1826 _giraffe_xoptmod2_ctor, _giraffe_model_dtor, _giraffe_xoptmod2_eval},
1827 {"yoptmod2", GI_MODEL_XOPT,
1828 _giraffe_yoptmod2_ctor, _giraffe_model_dtor, _giraffe_yoptmod2_eval},
1829 {"gaussian", GI_MODEL_LINE,
1830 _giraffe_gaussian_ctor, _giraffe_model_dtor, _giraffe_gaussian_eval},
1831 {"psfcos", GI_MODEL_LINE,
1832 _giraffe_psfcos_ctor, _giraffe_model_dtor, _giraffe_psfcos_eval},
1833 {"psfexp", GI_MODEL_LINE,
1834 _giraffe_psfexp_ctor, _giraffe_model_dtor, _giraffe_psfexp_eval},
1835 {"psfexp2", GI_MODEL_LINE,
1836 _giraffe_psfexp2_ctor, _giraffe_model_dtor, _giraffe_psfexp2_eval},
1837 {"test", GI_MODEL_LINE,
1838 _giraffe_test_ctor, _giraffe_model_dtor, _giraffe_test_eval},
1839 {NULL, 0, NULL, NULL, NULL}
1840};

This file is part of the GIRAFFE Pipeline Reference Manual 2.18.3.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Wed Jun 4 2025 23:52:46 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004