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

This file is part of the GIRAFFE Pipeline Reference Manual 2.19.4.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Fri Feb 6 2026 13:47:22 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2004