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 static GiModelData _gimodels[];
36 
37 const GiModelData *const giraffe_models = _gimodels;
38 
39 
40 /*
41  * Optical model argument indices
42  */
43 
44 enum {
45  LMI_WLEN = 0,
46  LMI_XFIB = 1,
47  LMI_YFIB = 2
48 };
49 
50 
51 /*
52  * Optical model parameter indices
53  */
54 
55 enum {
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 
74 enum {
75  LMP_AMPL = 0,
76  LMP_CENT = 1,
77  LMP_BACK = 2,
78  LMP_WID1 = 3,
79  LMP_WID2 = 4
80 };
81 
82 
83 
84 static const cxint DW_DEGREE = 3;
85 static 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 
100 inline 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 
118 inline 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 
176 inline 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 
233 inline 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 
416 inline 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 
473 inline 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 
591 inline 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 
654 inline 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 
918 inline 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 
981 inline 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 
1139 inline 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 
1188 inline 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 
1255 inline 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 
1306 inline 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 
1392 inline 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 
1443 inline 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 
1562 inline 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 
1613 inline 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 
1729 inline 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 
1772 inline 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 
1820 static 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.16.10.
Documentation copyright © 2002-2006 European Southern Observatory.
Generated on Thu Dec 15 2022 21:18:51 by doxygen 1.9.1 written by Dimitri van Heesch, © 1997-2004