pyreduce-astro 0.7a4__cp314-cp314-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyreduce/__init__.py +67 -0
- pyreduce/__main__.py +322 -0
- pyreduce/cli.py +342 -0
- pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp312-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp312-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp313-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp313-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp314-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp314-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_2d.obj +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp312-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp312-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp313-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp313-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp314-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp314-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_bd.obj +0 -0
- pyreduce/clib/__init__.py +0 -0
- pyreduce/clib/_slitfunc_2d.cp311-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_2d.cp312-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_2d.cp313-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_2d.cp314-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp311-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp312-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp313-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp314-win_amd64.pyd +0 -0
- pyreduce/clib/build_extract.py +75 -0
- pyreduce/clib/slit_func_2d_xi_zeta_bd.c +1313 -0
- pyreduce/clib/slit_func_2d_xi_zeta_bd.h +55 -0
- pyreduce/clib/slit_func_bd.c +362 -0
- pyreduce/clib/slit_func_bd.h +17 -0
- pyreduce/clipnflip.py +147 -0
- pyreduce/combine_frames.py +861 -0
- pyreduce/configuration.py +191 -0
- pyreduce/continuum_normalization.py +329 -0
- pyreduce/cwrappers.py +404 -0
- pyreduce/datasets.py +238 -0
- pyreduce/echelle.py +413 -0
- pyreduce/estimate_background_scatter.py +130 -0
- pyreduce/extract.py +1362 -0
- pyreduce/extraction_width.py +77 -0
- pyreduce/instruments/__init__.py +0 -0
- pyreduce/instruments/aj.py +9 -0
- pyreduce/instruments/aj.yaml +51 -0
- pyreduce/instruments/andes.py +102 -0
- pyreduce/instruments/andes.yaml +72 -0
- pyreduce/instruments/common.py +711 -0
- pyreduce/instruments/common.yaml +57 -0
- pyreduce/instruments/crires_plus.py +103 -0
- pyreduce/instruments/crires_plus.yaml +101 -0
- pyreduce/instruments/filters.py +195 -0
- pyreduce/instruments/harpn.py +203 -0
- pyreduce/instruments/harpn.yaml +140 -0
- pyreduce/instruments/harps.py +312 -0
- pyreduce/instruments/harps.yaml +144 -0
- pyreduce/instruments/instrument_info.py +140 -0
- pyreduce/instruments/jwst_miri.py +29 -0
- pyreduce/instruments/jwst_miri.yaml +53 -0
- pyreduce/instruments/jwst_niriss.py +98 -0
- pyreduce/instruments/jwst_niriss.yaml +60 -0
- pyreduce/instruments/lick_apf.py +35 -0
- pyreduce/instruments/lick_apf.yaml +60 -0
- pyreduce/instruments/mcdonald.py +123 -0
- pyreduce/instruments/mcdonald.yaml +56 -0
- pyreduce/instruments/metis_ifu.py +45 -0
- pyreduce/instruments/metis_ifu.yaml +62 -0
- pyreduce/instruments/metis_lss.py +45 -0
- pyreduce/instruments/metis_lss.yaml +62 -0
- pyreduce/instruments/micado.py +45 -0
- pyreduce/instruments/micado.yaml +62 -0
- pyreduce/instruments/models.py +257 -0
- pyreduce/instruments/neid.py +156 -0
- pyreduce/instruments/neid.yaml +61 -0
- pyreduce/instruments/nirspec.py +215 -0
- pyreduce/instruments/nirspec.yaml +63 -0
- pyreduce/instruments/nte.py +42 -0
- pyreduce/instruments/nte.yaml +55 -0
- pyreduce/instruments/uves.py +46 -0
- pyreduce/instruments/uves.yaml +65 -0
- pyreduce/instruments/xshooter.py +39 -0
- pyreduce/instruments/xshooter.yaml +63 -0
- pyreduce/make_shear.py +607 -0
- pyreduce/masks/mask_crires_plus_det1.fits.gz +0 -0
- pyreduce/masks/mask_crires_plus_det2.fits.gz +0 -0
- pyreduce/masks/mask_crires_plus_det3.fits.gz +0 -0
- pyreduce/masks/mask_ctio_chiron.fits.gz +0 -0
- pyreduce/masks/mask_elodie.fits.gz +0 -0
- pyreduce/masks/mask_feros3.fits.gz +0 -0
- pyreduce/masks/mask_flames_giraffe.fits.gz +0 -0
- pyreduce/masks/mask_harps_blue.fits.gz +0 -0
- pyreduce/masks/mask_harps_red.fits.gz +0 -0
- pyreduce/masks/mask_hds_blue.fits.gz +0 -0
- pyreduce/masks/mask_hds_red.fits.gz +0 -0
- pyreduce/masks/mask_het_hrs_2x5.fits.gz +0 -0
- pyreduce/masks/mask_jwst_miri_lrs_slitless.fits.gz +0 -0
- pyreduce/masks/mask_jwst_niriss_gr700xd.fits.gz +0 -0
- pyreduce/masks/mask_lick_apf_.fits.gz +0 -0
- pyreduce/masks/mask_mcdonald.fits.gz +0 -0
- pyreduce/masks/mask_nes.fits.gz +0 -0
- pyreduce/masks/mask_nirspec_nirspec.fits.gz +0 -0
- pyreduce/masks/mask_sarg.fits.gz +0 -0
- pyreduce/masks/mask_sarg_2x2a.fits.gz +0 -0
- pyreduce/masks/mask_sarg_2x2b.fits.gz +0 -0
- pyreduce/masks/mask_subaru_hds_red.fits.gz +0 -0
- pyreduce/masks/mask_uves_blue.fits.gz +0 -0
- pyreduce/masks/mask_uves_blue_binned_2_2.fits.gz +0 -0
- pyreduce/masks/mask_uves_middle.fits.gz +0 -0
- pyreduce/masks/mask_uves_middle_2x2_split.fits.gz +0 -0
- pyreduce/masks/mask_uves_middle_binned_2_2.fits.gz +0 -0
- pyreduce/masks/mask_uves_red.fits.gz +0 -0
- pyreduce/masks/mask_uves_red_2x2.fits.gz +0 -0
- pyreduce/masks/mask_uves_red_2x2_split.fits.gz +0 -0
- pyreduce/masks/mask_uves_red_binned_2_2.fits.gz +0 -0
- pyreduce/masks/mask_xshooter_nir.fits.gz +0 -0
- pyreduce/pipeline.py +619 -0
- pyreduce/rectify.py +138 -0
- pyreduce/reduce.py +2065 -0
- pyreduce/settings/settings_AJ.json +19 -0
- pyreduce/settings/settings_ANDES.json +89 -0
- pyreduce/settings/settings_CRIRES_PLUS.json +89 -0
- pyreduce/settings/settings_HARPN.json +73 -0
- pyreduce/settings/settings_HARPS.json +69 -0
- pyreduce/settings/settings_JWST_MIRI.json +55 -0
- pyreduce/settings/settings_JWST_NIRISS.json +55 -0
- pyreduce/settings/settings_LICK_APF.json +62 -0
- pyreduce/settings/settings_MCDONALD.json +58 -0
- pyreduce/settings/settings_METIS_IFU.json +77 -0
- pyreduce/settings/settings_METIS_LSS.json +77 -0
- pyreduce/settings/settings_MICADO.json +78 -0
- pyreduce/settings/settings_NEID.json +73 -0
- pyreduce/settings/settings_NIRSPEC.json +58 -0
- pyreduce/settings/settings_NTE.json +60 -0
- pyreduce/settings/settings_UVES.json +54 -0
- pyreduce/settings/settings_XSHOOTER.json +78 -0
- pyreduce/settings/settings_pyreduce.json +184 -0
- pyreduce/settings/settings_schema.json +850 -0
- pyreduce/tools/__init__.py +0 -0
- pyreduce/tools/combine.py +117 -0
- pyreduce/trace.py +979 -0
- pyreduce/util.py +1366 -0
- pyreduce/wavecal/MICADO_HK_3arcsec_chip5.npz +0 -0
- pyreduce/wavecal/atlas/thar.fits +4946 -13
- pyreduce/wavecal/atlas/thar_list.txt +4172 -0
- pyreduce/wavecal/atlas/une.fits +0 -0
- pyreduce/wavecal/convert.py +38 -0
- pyreduce/wavecal/crires_plus_J1228_Open_det1.npz +0 -0
- pyreduce/wavecal/crires_plus_J1228_Open_det2.npz +0 -0
- pyreduce/wavecal/crires_plus_J1228_Open_det3.npz +0 -0
- pyreduce/wavecal/harpn_harpn_2D.npz +0 -0
- pyreduce/wavecal/harps_blue_2D.npz +0 -0
- pyreduce/wavecal/harps_blue_pol_2D.npz +0 -0
- pyreduce/wavecal/harps_red_2D.npz +0 -0
- pyreduce/wavecal/harps_red_pol_2D.npz +0 -0
- pyreduce/wavecal/mcdonald.npz +0 -0
- pyreduce/wavecal/metis_lss_l_2D.npz +0 -0
- pyreduce/wavecal/metis_lss_m_2D.npz +0 -0
- pyreduce/wavecal/nirspec_K2.npz +0 -0
- pyreduce/wavecal/uves_blue_360nm_2D.npz +0 -0
- pyreduce/wavecal/uves_blue_390nm_2D.npz +0 -0
- pyreduce/wavecal/uves_blue_437nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_2x2_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_565nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_580nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_600nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_665nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_860nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_580nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_600nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_665nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_760nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_860nm_2D.npz +0 -0
- pyreduce/wavecal/xshooter_nir.npz +0 -0
- pyreduce/wavelength_calibration.py +1871 -0
- pyreduce_astro-0.7a4.dist-info/METADATA +106 -0
- pyreduce_astro-0.7a4.dist-info/RECORD +182 -0
- pyreduce_astro-0.7a4.dist-info/WHEEL +4 -0
- pyreduce_astro-0.7a4.dist-info/entry_points.txt +2 -0
- pyreduce_astro-0.7a4.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,1313 @@
|
|
|
1
|
+
#include <stdio.h>
|
|
2
|
+
#include <string.h>
|
|
3
|
+
#include <stdlib.h>
|
|
4
|
+
#include <math.h>
|
|
5
|
+
#include "slit_func_2d_xi_zeta_bd.h"
|
|
6
|
+
|
|
7
|
+
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
|
8
|
+
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
|
9
|
+
#define signum(a) (((a) > 0) ? 1 : ((a) < 0) ? -1 : 0)
|
|
10
|
+
#define DEBUG 0
|
|
11
|
+
|
|
12
|
+
// Store important sizes in global variables to make access easier
|
|
13
|
+
// When calculating the proper indices
|
|
14
|
+
// When not checking the indices just the variables directly
|
|
15
|
+
#if DEBUG
|
|
16
|
+
int _ncols = 0;
|
|
17
|
+
int _nrows = 0;
|
|
18
|
+
int _ny = 0;
|
|
19
|
+
int _nx = 0;
|
|
20
|
+
int _osample = 0;
|
|
21
|
+
int _n = 0;
|
|
22
|
+
int _nd = 0;
|
|
23
|
+
#else
|
|
24
|
+
#define _ncols ncols
|
|
25
|
+
#define _nrows nrows
|
|
26
|
+
#define _ny ny
|
|
27
|
+
#define _nx nx
|
|
28
|
+
#define _osample osample
|
|
29
|
+
#define _n n
|
|
30
|
+
#define _nd nd
|
|
31
|
+
#endif
|
|
32
|
+
|
|
33
|
+
// Define the sizes of each array
|
|
34
|
+
#define MAX_ZETA_X (_ncols)
|
|
35
|
+
#define MAX_ZETA_Y (_nrows)
|
|
36
|
+
#define MAX_ZETA_Z (3 * ((_osample) + 1))
|
|
37
|
+
#define MAX_ZETA (MAX_ZETA_X * MAX_ZETA_Y * MAX_ZETA_Z)
|
|
38
|
+
#define MAX_MZETA ((_ncols) * (_nrows))
|
|
39
|
+
#define MAX_XI ((_ncols) * (_ny)*4)
|
|
40
|
+
#define MAX_PSF_X (_ncols)
|
|
41
|
+
#define MAX_PSF_Y (3)
|
|
42
|
+
#define MAX_PSF (MAX_PSF_X * MAX_PSF_Y)
|
|
43
|
+
#define MAX_A ((_n) * (_nd))
|
|
44
|
+
#define MAX_R (_n)
|
|
45
|
+
#define MAX_SP (_ncols)
|
|
46
|
+
#define MAX_SL (_ny)
|
|
47
|
+
#define MAX_LAIJ_X (_ny)
|
|
48
|
+
#define MAX_LAIJ_Y (4 * (_osample) + 1)
|
|
49
|
+
#define MAX_LAIJ (MAX_LAIJ_X * MAX_LAIJ_Y)
|
|
50
|
+
#define MAX_PAIJ_X (_ncols)
|
|
51
|
+
#define MAX_PAIJ_Y (_nx)
|
|
52
|
+
#define MAX_PAIJ (MAX_PAIJ_X * MAX_PAIJ_Y)
|
|
53
|
+
#define MAX_LBJ (_ny)
|
|
54
|
+
#define MAX_PBJ (_ncols)
|
|
55
|
+
#define MAX_IM ((_ncols) * (_nrows))
|
|
56
|
+
|
|
57
|
+
// If we want to check the index use functions to represent the index
|
|
58
|
+
// Otherwise a simpler define will do, which should be faster ?
|
|
59
|
+
#if DEBUG
|
|
60
|
+
static long zeta_index(long x, long y, long z)
|
|
61
|
+
{
|
|
62
|
+
long i = z + y * MAX_ZETA_Z + x * MAX_ZETA_Z * _nrows;
|
|
63
|
+
if ((i < 0) | (i >= MAX_ZETA))
|
|
64
|
+
{
|
|
65
|
+
printf("INDEX OUT OF BOUNDS. Zeta[%li, %li, %li]\n", x, y, z);
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
return i;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static long mzeta_index(long x, long y)
|
|
72
|
+
{
|
|
73
|
+
long i = y + x * _nrows;
|
|
74
|
+
if ((i < 0) | (i >= MAX_MZETA))
|
|
75
|
+
{
|
|
76
|
+
printf("INDEX OUT OF BOUNDS. Mzeta[%li, %li]\n", x, y);
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
return i;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static long xi_index(long x, long y, long z)
|
|
83
|
+
{
|
|
84
|
+
long i = z + 4 * y + _ny * 4 * x;
|
|
85
|
+
if ((i < 0) | (i >= MAX_XI))
|
|
86
|
+
{
|
|
87
|
+
printf("INDEX OUT OF BOUNDS. Xi[%li, %li, %li]\n", x, y, z);
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
return i;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static long psf_index(long x, long y)
|
|
94
|
+
{
|
|
95
|
+
long i = ((x)*3 + (y));
|
|
96
|
+
if ((i < 0) | (i >= MAX_PSF))
|
|
97
|
+
{
|
|
98
|
+
printf("INDEX OUT OF BOUNDS. PSF[%li, %li]\n", x, y);
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
return i;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static long a_index(long x, long y)
|
|
105
|
+
{
|
|
106
|
+
long i = _n * y + x;
|
|
107
|
+
if ((i < 0) | (i >= MAX_A))
|
|
108
|
+
{
|
|
109
|
+
printf("INDEX OUT OF BOUNDS. a[%li, %li]\n", x, y);
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
return i;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static long r_index(long i)
|
|
116
|
+
{
|
|
117
|
+
if ((i < 0) | (i >= MAX_R))
|
|
118
|
+
{
|
|
119
|
+
printf("INDEX OUT OF BOUNDS. r[%li]\n", i);
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
return i;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static long sp_index(long i)
|
|
126
|
+
{
|
|
127
|
+
if ((i < 0) | (i >= MAX_SP))
|
|
128
|
+
{
|
|
129
|
+
printf("INDEX OUT OF BOUNDS. sP[%li]\n", i);
|
|
130
|
+
return 0;
|
|
131
|
+
}
|
|
132
|
+
return i;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
static long laij_index(long x, long y)
|
|
136
|
+
{
|
|
137
|
+
long i = ((y)*_ny) + (x);
|
|
138
|
+
if ((i < 0) | (i >= MAX_LAIJ))
|
|
139
|
+
{
|
|
140
|
+
printf("INDEX OUT OF BOUNDS. l_Aij[%li, %li]\n", x, y);
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
return i;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static long paij_index(long x, long y)
|
|
147
|
+
{
|
|
148
|
+
long i = ((y)*_ncols) + (x);
|
|
149
|
+
if ((i < 0) | (i >= MAX_PAIJ))
|
|
150
|
+
{
|
|
151
|
+
printf("INDEX OUT OF BOUNDS. p_Aij[%li, %li]\n", x, y);
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
return i;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
static long lbj_index(long i)
|
|
158
|
+
{
|
|
159
|
+
if ((i < 0) | (i >= MAX_LBJ))
|
|
160
|
+
{
|
|
161
|
+
printf("INDEX OUT OF BOUNDS. l_bj[%li]\n", i);
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
return i;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
static long pbj_index(long i)
|
|
168
|
+
{
|
|
169
|
+
if ((i < 0) | (i >= MAX_PBJ))
|
|
170
|
+
{
|
|
171
|
+
printf("INDEX OUT OF BOUNDS. p_bj[%li]\n", i);
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
return i;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
static long im_index(long x, long y)
|
|
178
|
+
{
|
|
179
|
+
long i = ((y)*_ncols) + (x);
|
|
180
|
+
if ((i < 0) | (i >= MAX_IM))
|
|
181
|
+
{
|
|
182
|
+
printf("INDEX OUT OF BOUNDS. im[%li, %li]\n", x, y);
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
185
|
+
return i;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
static long sl_index(long i)
|
|
189
|
+
{
|
|
190
|
+
if ((i < 0) | (i >= MAX_SL))
|
|
191
|
+
{
|
|
192
|
+
printf("INDEX OUT OF BOUNDS. sL[%li]\n", i);
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
195
|
+
return i;
|
|
196
|
+
}
|
|
197
|
+
#else
|
|
198
|
+
#define zeta_index(x, y, z) ((z) + (y)*MAX_ZETA_Z + (x)*MAX_ZETA_Z * _nrows)
|
|
199
|
+
#define mzeta_index(x, y) ((y) + (x)*_nrows)
|
|
200
|
+
#define xi_index(x, y, z) ((z) + 4 * (y) + _ny * 4 * (x))
|
|
201
|
+
#define psf_index(x, y) ((x)*3 + (y))
|
|
202
|
+
#define a_index(x, y) ((y)*n + (x))
|
|
203
|
+
#define r_index(i) (i)
|
|
204
|
+
#define sp_index(i) (i)
|
|
205
|
+
#define laij_index(x, y) ((y)*ny) + (x)
|
|
206
|
+
#define paij_index(x, y) ((y)*ncols) + (x)
|
|
207
|
+
#define lbj_index(i) (i)
|
|
208
|
+
#define pbj_index(i) (i)
|
|
209
|
+
#define im_index(x, y) ((y)*ncols) + (x)
|
|
210
|
+
#define sl_index(i) (i)
|
|
211
|
+
#endif
|
|
212
|
+
|
|
213
|
+
int bandsol(double *a, double *r, int n, int nd)
|
|
214
|
+
{
|
|
215
|
+
/*
|
|
216
|
+
bandsol solves a sparse system of linear equations with band-diagonal matrix.
|
|
217
|
+
Band is assumed to be symmetric relative to the main diaginal.
|
|
218
|
+
|
|
219
|
+
..math:
|
|
220
|
+
|
|
221
|
+
A * x = r
|
|
222
|
+
|
|
223
|
+
Parameters
|
|
224
|
+
----------
|
|
225
|
+
a : double array of shape [n,nd]
|
|
226
|
+
The left-hand-side of the equation system
|
|
227
|
+
The main diagonal should be in a(*,nd/2),
|
|
228
|
+
the first lower subdiagonal should be in a(1:n-1,nd/2-1),
|
|
229
|
+
the first upper subdiagonal is in a(0:n-2,nd/2+1) etc.
|
|
230
|
+
For example:
|
|
231
|
+
/ 0 0 X X X \
|
|
232
|
+
| 0 X X X X |
|
|
233
|
+
| X X X X X |
|
|
234
|
+
| X X X X X |
|
|
235
|
+
A = | X X X X X |
|
|
236
|
+
| X X X X X |
|
|
237
|
+
| X X X X X |
|
|
238
|
+
| X X X X 0 |
|
|
239
|
+
\ X X X 0 0 /
|
|
240
|
+
r : double array of shape [n]
|
|
241
|
+
the right-hand-side of the equation system
|
|
242
|
+
n : int
|
|
243
|
+
The number of equations
|
|
244
|
+
nd : int
|
|
245
|
+
The width of the band (3 for tri-diagonal system). Must be an odd number.
|
|
246
|
+
|
|
247
|
+
Returns
|
|
248
|
+
-------
|
|
249
|
+
code : int
|
|
250
|
+
0 on success, -1 on incorrect size of "a" and -4 on degenerate matrix.
|
|
251
|
+
*/
|
|
252
|
+
double aa;
|
|
253
|
+
int i, j, k;
|
|
254
|
+
|
|
255
|
+
#if DEBUG
|
|
256
|
+
_n = n;
|
|
257
|
+
_nd = nd;
|
|
258
|
+
#endif
|
|
259
|
+
|
|
260
|
+
/* Forward sweep */
|
|
261
|
+
for (i = 0; i < n - 1; i++)
|
|
262
|
+
{
|
|
263
|
+
aa = a[a_index(i, nd / 2)];
|
|
264
|
+
#if DEBUG
|
|
265
|
+
if (aa == 0)
|
|
266
|
+
{
|
|
267
|
+
printf("1, index: %i, %i\n", i, nd / 2);
|
|
268
|
+
aa = 1;
|
|
269
|
+
}
|
|
270
|
+
#endif
|
|
271
|
+
r[r_index(i)] /= aa;
|
|
272
|
+
for (j = 0; j < nd; j++)
|
|
273
|
+
a[a_index(i, j)] /= aa;
|
|
274
|
+
for (j = 1; j < min(nd / 2 + 1, n - i); j++)
|
|
275
|
+
{
|
|
276
|
+
aa = a[a_index(i + j, nd / 2 - j)];
|
|
277
|
+
r[r_index(i + j)] -= r[r_index(i)] * aa;
|
|
278
|
+
for (k = 0; k < nd - j; k++)
|
|
279
|
+
a[a_index(i + j, k)] -= a[a_index(i, k + j)] * aa;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* Backward sweep */
|
|
284
|
+
aa = a[a_index(n - 1, nd / 2)];
|
|
285
|
+
#if DEBUG
|
|
286
|
+
if (aa == 0)
|
|
287
|
+
{
|
|
288
|
+
printf("3, index: %i, %i\n", 0, nd / 2);
|
|
289
|
+
aa = 1;
|
|
290
|
+
}
|
|
291
|
+
#endif
|
|
292
|
+
r[r_index(n - 1)] /= aa;
|
|
293
|
+
for (i = n - 1; i > 0; i--)
|
|
294
|
+
{
|
|
295
|
+
for (j = 1; j <= min(nd / 2, i); j++)
|
|
296
|
+
r[r_index(i - j)] -= r[r_index(i)] * a[a_index(i - j, nd / 2 + j)];
|
|
297
|
+
r[r_index(i - 1)] /= a[a_index(i - 1, nd / 2)];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
aa = a[a_index(0, nd / 2)];
|
|
301
|
+
#if DEBUG
|
|
302
|
+
if (aa == 0)
|
|
303
|
+
{
|
|
304
|
+
printf("4, index: %i, %i\n", 0, nd / 2);
|
|
305
|
+
aa = 1;
|
|
306
|
+
}
|
|
307
|
+
#endif
|
|
308
|
+
r[r_index(0)] /= aa;
|
|
309
|
+
return 0;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// This is the faster median determination method.
|
|
313
|
+
// Algorithm from Numerical recipes in C of 1992
|
|
314
|
+
// see http://ndevilla.free.fr/median/median/
|
|
315
|
+
|
|
316
|
+
#define ELEM_SWAP(a, b) \
|
|
317
|
+
{ \
|
|
318
|
+
register double t = (a); \
|
|
319
|
+
(a) = (b); \
|
|
320
|
+
(b) = t; \
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
double quick_select_median(double arr[], unsigned int n)
|
|
324
|
+
{
|
|
325
|
+
int low, high;
|
|
326
|
+
int median;
|
|
327
|
+
int middle, ll, hh;
|
|
328
|
+
|
|
329
|
+
low = 0;
|
|
330
|
+
high = n - 1;
|
|
331
|
+
median = (low + high) / 2;
|
|
332
|
+
for (;;)
|
|
333
|
+
{
|
|
334
|
+
if (high <= low) /* One element only */
|
|
335
|
+
return arr[median];
|
|
336
|
+
if (high == low + 1)
|
|
337
|
+
{ /* Two elements only */
|
|
338
|
+
if (arr[low] > arr[high])
|
|
339
|
+
ELEM_SWAP(arr[low], arr[high]);
|
|
340
|
+
return arr[median];
|
|
341
|
+
}
|
|
342
|
+
/* Find median of low, middle and high items; swap into position low */
|
|
343
|
+
middle = (low + high) / 2;
|
|
344
|
+
if (arr[middle] > arr[high])
|
|
345
|
+
ELEM_SWAP(arr[middle], arr[high]);
|
|
346
|
+
if (arr[low] > arr[high])
|
|
347
|
+
ELEM_SWAP(arr[low], arr[high]);
|
|
348
|
+
if (arr[middle] > arr[low])
|
|
349
|
+
ELEM_SWAP(arr[middle], arr[low]);
|
|
350
|
+
/* Swap low item (now in position middle) into position (low+1) */
|
|
351
|
+
ELEM_SWAP(arr[middle], arr[low + 1]);
|
|
352
|
+
/* Nibble from each end towards middle, swapping items when stuck */
|
|
353
|
+
ll = low + 1;
|
|
354
|
+
hh = high;
|
|
355
|
+
for (;;)
|
|
356
|
+
{
|
|
357
|
+
do
|
|
358
|
+
ll++;
|
|
359
|
+
while (arr[low] > arr[ll]);
|
|
360
|
+
do
|
|
361
|
+
hh--;
|
|
362
|
+
while (arr[hh] > arr[low]);
|
|
363
|
+
if (hh < ll)
|
|
364
|
+
break;
|
|
365
|
+
ELEM_SWAP(arr[ll], arr[hh]);
|
|
366
|
+
}
|
|
367
|
+
/* Swap middle item (in position low) back into correct position */
|
|
368
|
+
ELEM_SWAP(arr[low], arr[hh]);
|
|
369
|
+
/* Re-set active partition */
|
|
370
|
+
if (hh <= median)
|
|
371
|
+
low = ll;
|
|
372
|
+
if (hh >= median)
|
|
373
|
+
high = hh - 1;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
double median_absolute_deviation(double arr[], unsigned int n)
|
|
378
|
+
{
|
|
379
|
+
double median = quick_select_median(arr, n);
|
|
380
|
+
for (size_t i = 0; i < n; i++)
|
|
381
|
+
{
|
|
382
|
+
arr[i] = fabs(arr[i] - median);
|
|
383
|
+
}
|
|
384
|
+
double mad = quick_select_median(arr, n);
|
|
385
|
+
return mad;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
int xi_zeta_tensors(
|
|
389
|
+
int ncols,
|
|
390
|
+
int nrows,
|
|
391
|
+
int ny,
|
|
392
|
+
double *ycen,
|
|
393
|
+
int *ycen_offset,
|
|
394
|
+
int y_lower_lim,
|
|
395
|
+
int osample,
|
|
396
|
+
double *PSF_curve,
|
|
397
|
+
xi_ref *xi,
|
|
398
|
+
zeta_ref *zeta,
|
|
399
|
+
int *m_zeta)
|
|
400
|
+
{
|
|
401
|
+
/*
|
|
402
|
+
Create the Xi and Zeta tensors, that describe the contribution of each pixel to the subpixels of the image,
|
|
403
|
+
Considering the curvature of the slit.
|
|
404
|
+
|
|
405
|
+
Parameters
|
|
406
|
+
----------
|
|
407
|
+
ncols : int
|
|
408
|
+
Swath width in pixels
|
|
409
|
+
nrows : int
|
|
410
|
+
Extraction slit height in pixels
|
|
411
|
+
ny : int
|
|
412
|
+
Size of the slit function array: ny = osample * (nrows + 1) + 1
|
|
413
|
+
ycen : double array of shape (ncols,)
|
|
414
|
+
Order centre line offset from pixel row boundary
|
|
415
|
+
ycen_offsets : int array of shape (ncols,)
|
|
416
|
+
Order image column shift
|
|
417
|
+
y_lower_lim : int
|
|
418
|
+
Number of detector pixels below the pixel containing the central line ycen
|
|
419
|
+
osample : int
|
|
420
|
+
Subpixel ovsersampling factor
|
|
421
|
+
PSF_curve : double array of shape (ncols, 3)
|
|
422
|
+
Parabolic fit to the slit image curvature.
|
|
423
|
+
For column d_x = PSF_curve[ncols][0] + PSF_curve[ncols][1] *d_y + PSF_curve[ncols][2] *d_y^2,
|
|
424
|
+
where d_y is the offset from the central line ycen.
|
|
425
|
+
Thus central subpixel of omega[x][y'][delta_x][iy'] does not stick out of column x.
|
|
426
|
+
xi : (out) xi_ref array of shape (ncols, ny, 4)
|
|
427
|
+
Convolution tensor telling the coordinates of detector
|
|
428
|
+
pixels on which {x, iy} element falls and the corresponding projections.
|
|
429
|
+
zeta : (out) zeta_ref array of shape (ncols, nrows, 3 * (osample + 1))
|
|
430
|
+
Convolution tensor telling the coordinates of subpixels {x, iy} contributing
|
|
431
|
+
to detector pixel {x, y}.
|
|
432
|
+
m_zeta : (out) int array of shape (ncols, nrows)
|
|
433
|
+
The actual number of contributing elements in zeta for each pixel
|
|
434
|
+
|
|
435
|
+
Returns
|
|
436
|
+
-------
|
|
437
|
+
code : int
|
|
438
|
+
0 on success, -1 on failure
|
|
439
|
+
*/
|
|
440
|
+
int x, xx, y, yy, ix, ix1, ix2, iy, iy1, iy2, m;
|
|
441
|
+
double step, delta, dy, w, d1, d2;
|
|
442
|
+
|
|
443
|
+
step = 1.e0 / osample;
|
|
444
|
+
|
|
445
|
+
/* Clean xi */
|
|
446
|
+
for (x = 0; x < ncols; x++)
|
|
447
|
+
{
|
|
448
|
+
for (iy = 0; iy < ny; iy++)
|
|
449
|
+
{
|
|
450
|
+
for (m = 0; m < 4; m++)
|
|
451
|
+
{
|
|
452
|
+
xi[xi_index(x, iy, m)].x = -1;
|
|
453
|
+
xi[xi_index(x, iy, m)].y = -1;
|
|
454
|
+
xi[xi_index(x, iy, m)].w = 0.;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/* Clean zeta */
|
|
460
|
+
for (x = 0; x < ncols; x++)
|
|
461
|
+
{
|
|
462
|
+
for (y = 0; y < nrows; y++)
|
|
463
|
+
{
|
|
464
|
+
m_zeta[mzeta_index(x, y)] = 0;
|
|
465
|
+
for (ix = 0; ix < MAX_ZETA_Z; ix++)
|
|
466
|
+
{
|
|
467
|
+
zeta[zeta_index(x, y, ix)].x = -1;
|
|
468
|
+
zeta[zeta_index(x, y, ix)].iy = -1;
|
|
469
|
+
zeta[zeta_index(x, y, ix)].w = 0.;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/*
|
|
475
|
+
Construct the xi and zeta tensors. They contain pixel references and contribution.
|
|
476
|
+
values going from a given subpixel to other pixels (xi) and coming from other subpixels
|
|
477
|
+
to a given detector pixel (zeta).
|
|
478
|
+
Note, that xi and zeta are used in the equations for sL, sP and for the model but they
|
|
479
|
+
do not involve the data, only the geometry. Thus it can be pre-computed once.
|
|
480
|
+
*/
|
|
481
|
+
for (x = 0; x < ncols; x++)
|
|
482
|
+
{
|
|
483
|
+
/*
|
|
484
|
+
I promised to reconsider the initial offset. Here it is. For the original layout
|
|
485
|
+
(no column shifts and discontinuities in ycen) there is pixel y that contains the
|
|
486
|
+
central line yc. There are two options here (by construction of ycen that can be 0
|
|
487
|
+
but cannot be 1): (1) yc is inside pixel y and (2) yc falls at the boundary between
|
|
488
|
+
pixels y and y-1. yc cannot be at the boundary of pixels y+1 and y because we would
|
|
489
|
+
select y+1 to be pixel y in that case.
|
|
490
|
+
|
|
491
|
+
Next we need to define starting and ending indices iy for sL subpixels that contribute
|
|
492
|
+
to pixel y. I call them iy1 and iy2. For both cases we assume osample+1 subpixels covering
|
|
493
|
+
pixel y (wierd). So for case 1 iy1 will be (y-1)*osample and iy2 == y*osample. Special
|
|
494
|
+
treatment of the boundary subpixels will compensate for introducing extra subpixel in
|
|
495
|
+
case 1. In case 2 things are more logical: iy1=(yc-y)*osample+(y-1)*osample;
|
|
496
|
+
iy2=(y+1-yc)*osample)+(y-1)*osample. ycen is yc-y making things simpler. Note also that
|
|
497
|
+
the same pattern repeates for all rows: we only need to initialize iy1 and iy2 and keep
|
|
498
|
+
incrementing them by osample.
|
|
499
|
+
*/
|
|
500
|
+
|
|
501
|
+
iy2 = osample - floor(ycen[x] * osample);
|
|
502
|
+
iy1 = iy2 - osample;
|
|
503
|
+
|
|
504
|
+
/*
|
|
505
|
+
Handling partial subpixels cut by detector pixel rows is again tricky. Here we have three
|
|
506
|
+
cases (mostly because of the decision to assume that we always have osample+1 subpixels
|
|
507
|
+
per one detector pixel). Here d1 is the fraction of the subpixel iy1 inside detector pixel y.
|
|
508
|
+
d2 is then the fraction of subpixel iy2 inside detector pixel y. By definition d1+d2==step.
|
|
509
|
+
Case 1: ycen falls on the top boundary of each detector pixel (ycen == 1). Here we conclude
|
|
510
|
+
that the first subpixel is fully contained inside pixel y and d1 is set to step.
|
|
511
|
+
Case 2: ycen falls on the bottom boundary of each detector pixel (ycen == 0). Here we conclude
|
|
512
|
+
that the first subpixel is totally outside of pixel y and d1 is set to 0.
|
|
513
|
+
Case 3: ycen falls inside of each pixel (0>ycen>1). In this case d1 is set to the fraction of
|
|
514
|
+
the first step contained inside of each pixel.
|
|
515
|
+
And BTW, this also means that central line coinsides with the upper boundary of subpixel iy2
|
|
516
|
+
when the y loop reaches pixel y_lower_lim. In other words:
|
|
517
|
+
|
|
518
|
+
dy=(iy-(y_lower_lim+ycen[x])*osample)*step-0.5*step
|
|
519
|
+
*/
|
|
520
|
+
|
|
521
|
+
d1 = fmod(ycen[x], step);
|
|
522
|
+
if (d1 == 0)
|
|
523
|
+
d1 = step;
|
|
524
|
+
d2 = step - d1;
|
|
525
|
+
|
|
526
|
+
/*
|
|
527
|
+
The final hurdle for 2D slit decomposition is to construct two 3D reference tensors. We proceed
|
|
528
|
+
similar to 1D case except that now each iy subpixel can be shifted left or right following
|
|
529
|
+
the curvature of the slit image on the detector. We assume for now that each subpixel is
|
|
530
|
+
exactly 1 detector pixel wide. This may not be exactly true if the curvature changes accross
|
|
531
|
+
the focal plane but will deal with it when the necessity will become apparent. For now we
|
|
532
|
+
just assume that a shift delta the weight w assigned to subpixel iy is divided between
|
|
533
|
+
ix1=int(delta) and ix2=int(delta)+signum(delta) as (1-|delta-ix1|)*w and |delta-ix1|*w.
|
|
534
|
+
|
|
535
|
+
The curvature is given by a quadratic polynomial evaluated from an approximation for column
|
|
536
|
+
x: delta = PSF_curve[x][0] + PSF_curve[x][1] * (y-yc[x]) + PSF_curve[x][2] * (y-yc[x])^2.
|
|
537
|
+
It looks easy except that y and yc are set in the global detector coordinate system rather than
|
|
538
|
+
in the shifted and cropped swath passed to slit_func_2d. One possible solution I will try here
|
|
539
|
+
is to modify PSF_curve before the call such as:
|
|
540
|
+
delta = PSF_curve'[x][0] + PSF_curve'[x][1] * (y'-ycen[x]) + PSF_curve'[x][2] * (y'-ycen[x])^2
|
|
541
|
+
where y' = y - floor(yc).
|
|
542
|
+
*/
|
|
543
|
+
|
|
544
|
+
/* Define initial distance from ycen */
|
|
545
|
+
/* It is given by the center of the first */
|
|
546
|
+
/* subpixel falling into pixel y_lower_lim */
|
|
547
|
+
dy = ycen[x] - floor((y_lower_lim + ycen[x]) / step) * step - step;
|
|
548
|
+
|
|
549
|
+
/*
|
|
550
|
+
Now we go detector pixels x and y incrementing subpixels looking for their controibutions
|
|
551
|
+
to the current and adjacent pixels. Note that the curvature/tilt of the projected slit
|
|
552
|
+
image could be so large that subpixel iy may no contribute to column x at all. On the
|
|
553
|
+
other hand, subpixels around ycen by definition must contribute to pixel x,y.
|
|
554
|
+
3rd index in xi refers corners of pixel xx,y: 0:LL, 1:LR, 2:UL, 3:UR.
|
|
555
|
+
*/
|
|
556
|
+
|
|
557
|
+
for (y = 0; y < nrows; y++)
|
|
558
|
+
{
|
|
559
|
+
iy1 += osample; // Bottom subpixel falling in row y
|
|
560
|
+
iy2 += osample; // Top subpixel falling in row y
|
|
561
|
+
dy -= step;
|
|
562
|
+
for (iy = iy1; iy <= iy2; iy++)
|
|
563
|
+
{
|
|
564
|
+
if (iy == iy1)
|
|
565
|
+
w = d1;
|
|
566
|
+
else if (iy == iy2)
|
|
567
|
+
w = d2;
|
|
568
|
+
else
|
|
569
|
+
w = step;
|
|
570
|
+
dy += step;
|
|
571
|
+
delta = (PSF_curve[psf_index(x, 1)] + PSF_curve[psf_index(x, 2)] * (dy - ycen[x])) * (dy - ycen[x]);
|
|
572
|
+
ix1 = delta;
|
|
573
|
+
ix2 = ix1 + signum(delta);
|
|
574
|
+
|
|
575
|
+
/* Three cases: subpixel on the bottom boundary of row y, intermediate subpixels and top boundary */
|
|
576
|
+
|
|
577
|
+
if (iy == iy1) /* Case A: Subpixel iy is entering detector row y */
|
|
578
|
+
{
|
|
579
|
+
if (ix1 < ix2) /* Subpixel iy shifts to the right from column x */
|
|
580
|
+
{
|
|
581
|
+
if (x + ix1 >= 0 && x + ix2 < ncols)
|
|
582
|
+
{
|
|
583
|
+
xx = x + ix1; /* Upper right corner of subpixel iy */
|
|
584
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
585
|
+
xi[xi_index(x, iy, 3)].x = xx;
|
|
586
|
+
xi[xi_index(x, iy, 3)].y = yy;
|
|
587
|
+
xi[xi_index(x, iy, 3)].w = w - fabs(delta - ix1) * w;
|
|
588
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && xi[xi_index(x, iy, 3)].w > 0)
|
|
589
|
+
{
|
|
590
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
591
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
592
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
593
|
+
zeta[zeta_index(xx, yy, m)].w = xi[xi_index(x, iy, 3)].w;
|
|
594
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
595
|
+
}
|
|
596
|
+
xx = x + ix2; /* Upper left corner of subpixel iy */
|
|
597
|
+
// This offset is required because the iy subpixel
|
|
598
|
+
// is going to contribute to the yy row in xx column
|
|
599
|
+
// of detector pixels where yy and y are in the same
|
|
600
|
+
// row. In the packed array this is not necessarily true.
|
|
601
|
+
// Instead, what we know is that:
|
|
602
|
+
// y+ycen_offset[x] == yy+ycen_offset[xx]
|
|
603
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
604
|
+
|
|
605
|
+
xi[xi_index(x, iy, 2)].x = xx;
|
|
606
|
+
xi[xi_index(x, iy, 2)].y = yy;
|
|
607
|
+
xi[xi_index(x, iy, 2)].w = fabs(delta - ix1) * w;
|
|
608
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && xi[xi_index(x, iy, 2)].w > 0)
|
|
609
|
+
{
|
|
610
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
611
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
612
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
613
|
+
zeta[zeta_index(xx, yy, m)].w = xi[xi_index(x, iy, 2)].w;
|
|
614
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
else if (ix1 > ix2) /* Subpixel iy shifts to the left from column x */
|
|
619
|
+
{
|
|
620
|
+
if (x + ix2 >= 0 && x + ix1 < ncols)
|
|
621
|
+
{
|
|
622
|
+
xx = x + ix2; /* Upper left corner of subpixel iy */
|
|
623
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
624
|
+
xi[xi_index(x, iy, 2)].x = xx;
|
|
625
|
+
xi[xi_index(x, iy, 2)].y = yy;
|
|
626
|
+
xi[xi_index(x, iy, 2)].w = fabs(delta - ix1) * w;
|
|
627
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && xi[xi_index(x, iy, 2)].w > 0)
|
|
628
|
+
{
|
|
629
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
630
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
631
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
632
|
+
zeta[zeta_index(xx, yy, m)].w = xi[xi_index(x, iy, 2)].w;
|
|
633
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
634
|
+
}
|
|
635
|
+
xx = x + ix1; /* Upper right corner of subpixel iy */
|
|
636
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
637
|
+
xi[xi_index(x, iy, 3)].x = xx;
|
|
638
|
+
xi[xi_index(x, iy, 3)].y = yy;
|
|
639
|
+
xi[xi_index(x, iy, 3)].w = w - fabs(delta - ix1) * w;
|
|
640
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && xi[xi_index(x, iy, 3)].w > 0)
|
|
641
|
+
{
|
|
642
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
643
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
644
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
645
|
+
zeta[zeta_index(xx, yy, m)].w = xi[xi_index(x, iy, 3)].w;
|
|
646
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
else
|
|
651
|
+
{
|
|
652
|
+
xx = x + ix1; /* Subpixel iy stays inside column x */
|
|
653
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
654
|
+
xi[xi_index(x, iy, 2)].x = xx;
|
|
655
|
+
xi[xi_index(x, iy, 2)].y = yy;
|
|
656
|
+
xi[xi_index(x, iy, 2)].w = w;
|
|
657
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && w > 0)
|
|
658
|
+
{
|
|
659
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
660
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
661
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
662
|
+
zeta[zeta_index(xx, yy, m)].w = w;
|
|
663
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
else if (iy == iy2) /* Case C: Subpixel iy is leaving detector row y */
|
|
668
|
+
{
|
|
669
|
+
if (ix1 < ix2) /* Subpixel iy shifts to the right from column x */
|
|
670
|
+
{
|
|
671
|
+
if (x + ix1 >= 0 && x + ix2 < ncols)
|
|
672
|
+
{
|
|
673
|
+
xx = x + ix1; /* Bottom right corner of subpixel iy */
|
|
674
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
675
|
+
xi[xi_index(x, iy, 1)].x = xx;
|
|
676
|
+
xi[xi_index(x, iy, 1)].y = yy;
|
|
677
|
+
xi[xi_index(x, iy, 1)].w = w - fabs(delta - ix1) * w;
|
|
678
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && xi[xi_index(x, iy, 1)].w > 0)
|
|
679
|
+
{
|
|
680
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
681
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
682
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
683
|
+
zeta[zeta_index(xx, yy, m)].w = xi[xi_index(x, iy, 1)].w;
|
|
684
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
685
|
+
}
|
|
686
|
+
xx = x + ix2; /* Bottom left corner of subpixel iy */
|
|
687
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
688
|
+
xi[xi_index(x, iy, 0)].x = xx;
|
|
689
|
+
xi[xi_index(x, iy, 0)].y = yy;
|
|
690
|
+
xi[xi_index(x, iy, 0)].w = fabs(delta - ix1) * w;
|
|
691
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && xi[xi_index(x, iy, 0)].w > 0)
|
|
692
|
+
{
|
|
693
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
694
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
695
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
696
|
+
zeta[zeta_index(xx, yy, m)].w = xi[xi_index(x, iy, 0)].w;
|
|
697
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
else if (ix1 > ix2) /* Subpixel iy shifts to the left from column x */
|
|
702
|
+
{
|
|
703
|
+
if (x + ix2 >= 0 && x + ix1 < ncols)
|
|
704
|
+
{
|
|
705
|
+
xx = x + ix2; /* Bottom left corner of subpixel iy */
|
|
706
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
707
|
+
xi[xi_index(x, iy, 0)].x = xx;
|
|
708
|
+
xi[xi_index(x, iy, 0)].y = yy;
|
|
709
|
+
xi[xi_index(x, iy, 0)].w = fabs(delta - ix1) * w;
|
|
710
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && xi[xi_index(x, iy, 0)].w > 0)
|
|
711
|
+
{
|
|
712
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
713
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
714
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
715
|
+
zeta[zeta_index(xx, yy, m)].w = xi[xi_index(x, iy, 0)].w;
|
|
716
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
717
|
+
}
|
|
718
|
+
xx = x + ix1; /* Bottom right corner of subpixel iy */
|
|
719
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
720
|
+
xi[xi_index(x, iy, 1)].x = xx;
|
|
721
|
+
xi[xi_index(x, iy, 1)].y = yy;
|
|
722
|
+
xi[xi_index(x, iy, 1)].w = w - fabs(delta - ix1) * w;
|
|
723
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && xi[xi_index(x, iy, 1)].w > 0)
|
|
724
|
+
{
|
|
725
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
726
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
727
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
728
|
+
zeta[zeta_index(xx, yy, m)].w = xi[xi_index(x, iy, 1)].w;
|
|
729
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
else /* Subpixel iy stays inside column x */
|
|
734
|
+
{
|
|
735
|
+
xx = x + ix1;
|
|
736
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
737
|
+
xi[xi_index(x, iy, 0)].x = xx;
|
|
738
|
+
xi[xi_index(x, iy, 0)].y = yy;
|
|
739
|
+
xi[xi_index(x, iy, 0)].w = w;
|
|
740
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && w > 0)
|
|
741
|
+
{
|
|
742
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
743
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
744
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
745
|
+
zeta[zeta_index(xx, yy, m)].w = w;
|
|
746
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
else /* CASE B: Subpixel iy is fully inside detector row y */
|
|
751
|
+
{
|
|
752
|
+
if (ix1 < ix2) /* Subpixel iy shifts to the right from column x */
|
|
753
|
+
{
|
|
754
|
+
if (x + ix1 >= 0 && x + ix2 < ncols)
|
|
755
|
+
{
|
|
756
|
+
xx = x + ix1; /* Bottom right corner of subpixel iy */
|
|
757
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
758
|
+
xi[xi_index(x, iy, 1)].x = xx;
|
|
759
|
+
xi[xi_index(x, iy, 1)].y = yy;
|
|
760
|
+
xi[xi_index(x, iy, 1)].w = w - fabs(delta - ix1) * w;
|
|
761
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && xi[xi_index(x, iy, 1)].w > 0)
|
|
762
|
+
{
|
|
763
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
764
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
765
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
766
|
+
zeta[zeta_index(xx, yy, m)].w = xi[xi_index(x, iy, 1)].w;
|
|
767
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
768
|
+
}
|
|
769
|
+
xx = x + ix2; /* Bottom left corner of subpixel iy */
|
|
770
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
771
|
+
xi[xi_index(x, iy, 0)].x = xx;
|
|
772
|
+
xi[xi_index(x, iy, 0)].y = yy;
|
|
773
|
+
xi[xi_index(x, iy, 0)].w = fabs(delta - ix1) * w;
|
|
774
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && xi[xi_index(x, iy, 0)].w > 0)
|
|
775
|
+
{
|
|
776
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
777
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
778
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
779
|
+
zeta[zeta_index(xx, yy, m)].w = xi[xi_index(x, iy, 0)].w;
|
|
780
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
else if (ix1 > ix2) /* Subpixel iy shifts to the left from column x */
|
|
785
|
+
{
|
|
786
|
+
if (x + ix2 >= 0 && x + ix1 < ncols)
|
|
787
|
+
{
|
|
788
|
+
xx = x + ix2; /* Bottom right corner of subpixel iy */
|
|
789
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
790
|
+
xi[xi_index(x, iy, 1)].x = xx;
|
|
791
|
+
xi[xi_index(x, iy, 1)].y = yy;
|
|
792
|
+
xi[xi_index(x, iy, 1)].w = fabs(delta - ix1) * w;
|
|
793
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && xi[xi_index(x, iy, 1)].w > 0)
|
|
794
|
+
{
|
|
795
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
796
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
797
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
798
|
+
zeta[zeta_index(xx, yy, m)].w = xi[xi_index(x, iy, 1)].w;
|
|
799
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
800
|
+
}
|
|
801
|
+
xx = x + ix1; /* Bottom left corner of subpixel iy */
|
|
802
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
803
|
+
xi[xi_index(x, iy, 0)].x = xx;
|
|
804
|
+
xi[xi_index(x, iy, 0)].y = yy;
|
|
805
|
+
xi[xi_index(x, iy, 0)].w = w - fabs(delta - ix1) * w;
|
|
806
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && xi[xi_index(x, iy, 0)].w > 0)
|
|
807
|
+
{
|
|
808
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
809
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
810
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
811
|
+
zeta[zeta_index(xx, yy, m)].w = xi[xi_index(x, iy, 0)].w;
|
|
812
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
else /* Subpixel iy stays inside column x */
|
|
817
|
+
{
|
|
818
|
+
xx = x + ix2;
|
|
819
|
+
yy = y + ycen_offset[x] - ycen_offset[xx];
|
|
820
|
+
xi[xi_index(x, iy, 0)].x = xx;
|
|
821
|
+
xi[xi_index(x, iy, 0)].y = yy;
|
|
822
|
+
xi[xi_index(x, iy, 0)].w = w;
|
|
823
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows && w > 0)
|
|
824
|
+
{
|
|
825
|
+
m = m_zeta[mzeta_index(xx, yy)];
|
|
826
|
+
zeta[zeta_index(xx, yy, m)].x = x;
|
|
827
|
+
zeta[zeta_index(xx, yy, m)].iy = iy;
|
|
828
|
+
zeta[zeta_index(xx, yy, m)].w = w;
|
|
829
|
+
m_zeta[mzeta_index(xx, yy)]++;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return 0;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
int slit_func_curved(int ncols,
|
|
840
|
+
int nrows,
|
|
841
|
+
int ny,
|
|
842
|
+
double *im,
|
|
843
|
+
double *pix_unc,
|
|
844
|
+
unsigned char *mask,
|
|
845
|
+
double *ycen,
|
|
846
|
+
int *ycen_offset,
|
|
847
|
+
int y_lower_lim,
|
|
848
|
+
int osample,
|
|
849
|
+
double lambda_sP,
|
|
850
|
+
double lambda_sL,
|
|
851
|
+
int maxiter,
|
|
852
|
+
double *PSF_curve,
|
|
853
|
+
double *sP,
|
|
854
|
+
double *sL,
|
|
855
|
+
double *model,
|
|
856
|
+
double *unc,
|
|
857
|
+
double *info)
|
|
858
|
+
{
|
|
859
|
+
/*
|
|
860
|
+
Extract the spectrum and slit illumination function for a curved slit
|
|
861
|
+
|
|
862
|
+
This function does not assign or free any memory,
|
|
863
|
+
therefore all working arrays are passed as parameters.
|
|
864
|
+
The contents of which will be overriden however
|
|
865
|
+
|
|
866
|
+
Parameters
|
|
867
|
+
----------
|
|
868
|
+
ncols : int
|
|
869
|
+
Swath width in pixels
|
|
870
|
+
nrows : int
|
|
871
|
+
Extraction slit height in pixels
|
|
872
|
+
nx : int
|
|
873
|
+
Range of columns affected by PSF tilt: nx = 2 * delta_x + 1
|
|
874
|
+
ny : int
|
|
875
|
+
Size of the slit function array: ny = osample * (nrows + 1) + 1
|
|
876
|
+
im : double array of shape (nrows, ncols)
|
|
877
|
+
Image to be decomposed
|
|
878
|
+
pix_unc : double array of shape (nrows, ncols)
|
|
879
|
+
Individual pixel uncertainties. Set to zero if unknown.
|
|
880
|
+
mask : byte array of shape (nrows, ncols)
|
|
881
|
+
Initial and final mask for the swath, both in and output
|
|
882
|
+
ycen : double array of shape (ncols,)
|
|
883
|
+
Order centre line offset from pixel row boundary.
|
|
884
|
+
Should only contain values between 0 and 1.
|
|
885
|
+
ycen_offset : int array of shape (ncols,)
|
|
886
|
+
Order image column shift
|
|
887
|
+
y_lower_lim : int
|
|
888
|
+
Number of detector pixels below the pixel containing
|
|
889
|
+
the central line yc.
|
|
890
|
+
osample : int
|
|
891
|
+
Subpixel ovsersampling factor
|
|
892
|
+
lambda_sP : double
|
|
893
|
+
Smoothing parameter for the spectrum, could be zero
|
|
894
|
+
lambda_sL : double
|
|
895
|
+
Smoothing parameter for the slit function, usually > 0
|
|
896
|
+
PSF_curve : double array of shape (ncols, 3)
|
|
897
|
+
Slit curvature parameters for each point along the spectrum
|
|
898
|
+
sP : (out) double array of shape (ncols,)
|
|
899
|
+
Spectrum resulting from decomposition
|
|
900
|
+
sL : (out) double array of shape (ny,)
|
|
901
|
+
Slit function resulting from decomposition
|
|
902
|
+
model : (out) double array of shape (ncols, nrows)
|
|
903
|
+
Model constructed from sp and sf
|
|
904
|
+
unc : (out) double array of shape (ncols,)
|
|
905
|
+
Spectrum uncertainties based on data - model and pix_unc
|
|
906
|
+
info : (out) double array of shape (5,)
|
|
907
|
+
Returns information about the fit results
|
|
908
|
+
Returns
|
|
909
|
+
-------
|
|
910
|
+
code : int
|
|
911
|
+
0 on success, -1 on failure (see also bandsol)
|
|
912
|
+
*/
|
|
913
|
+
int x, xx, xxx, y, yy, iy, jy, n, m, nx;
|
|
914
|
+
double norm, dev, lambda, diag_tot, ww, www;
|
|
915
|
+
double cost_old, ftol, tmp;
|
|
916
|
+
int iter, delta_x;
|
|
917
|
+
unsigned int isum;
|
|
918
|
+
|
|
919
|
+
// For the solving of the equation system
|
|
920
|
+
double *l_Aij, *l_bj, *p_Aij, *p_bj;
|
|
921
|
+
double *diff;
|
|
922
|
+
|
|
923
|
+
// For the geometry
|
|
924
|
+
xi_ref *xi;
|
|
925
|
+
zeta_ref *zeta;
|
|
926
|
+
int *m_zeta;
|
|
927
|
+
|
|
928
|
+
// The Optimization results
|
|
929
|
+
double success, status, cost;
|
|
930
|
+
|
|
931
|
+
// maxiter = 20; // Maximum number of iterations
|
|
932
|
+
ftol = 1e-7; // Maximum cost difference between two iterations to stop convergence
|
|
933
|
+
success = 1;
|
|
934
|
+
status = 0;
|
|
935
|
+
|
|
936
|
+
cost = INFINITY;
|
|
937
|
+
ny = osample * (nrows + 1) + 1; /* The size of the sL array. Extra osample is because ycen can be between 0 and 1. */
|
|
938
|
+
|
|
939
|
+
#if DEBUG
|
|
940
|
+
_ncols = ncols;
|
|
941
|
+
_nrows = nrows;
|
|
942
|
+
_ny = ny;
|
|
943
|
+
_osample = osample;
|
|
944
|
+
#endif
|
|
945
|
+
|
|
946
|
+
// If we want to smooth the spectrum we need at least delta_x = 1
|
|
947
|
+
// Otherwise delta_x = 0 works if there is no curvature
|
|
948
|
+
delta_x = lambda_sP == 0 ? 0 : 1;
|
|
949
|
+
for (x = 0; x < ncols; x++)
|
|
950
|
+
{
|
|
951
|
+
for (y = -y_lower_lim; y < nrows - y_lower_lim + 1; y++)
|
|
952
|
+
{
|
|
953
|
+
tmp = ceil(fabs(y * PSF_curve[psf_index(x, 1)] + y * y * PSF_curve[psf_index(x, 2)]));
|
|
954
|
+
delta_x = max(delta_x, tmp);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
nx = 4 * delta_x + 1; /* Maximum horizontal shift in detector pixels due to slit image curvature */
|
|
958
|
+
|
|
959
|
+
#if DEBUG
|
|
960
|
+
_nx = nx;
|
|
961
|
+
#endif
|
|
962
|
+
|
|
963
|
+
// The curvature is larger than the number of columns
|
|
964
|
+
// Usually that means that the curvature is messed up
|
|
965
|
+
if (nx > ncols)
|
|
966
|
+
{
|
|
967
|
+
info[0] = 0; //failed
|
|
968
|
+
info[1] = cost; //INFINITY
|
|
969
|
+
info[2] = -2; // curvature to large
|
|
970
|
+
info[3] = 0;
|
|
971
|
+
info[4] = delta_x;
|
|
972
|
+
return -1;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
l_Aij = malloc(MAX_LAIJ * sizeof(double));
|
|
976
|
+
p_Aij = malloc(MAX_PAIJ * sizeof(double));
|
|
977
|
+
l_bj = malloc(MAX_LBJ * sizeof(double));
|
|
978
|
+
p_bj = malloc(MAX_PBJ * sizeof(double));
|
|
979
|
+
xi = malloc(MAX_XI * sizeof(xi_ref));
|
|
980
|
+
zeta = malloc(MAX_ZETA * sizeof(zeta_ref));
|
|
981
|
+
m_zeta = malloc(MAX_MZETA * sizeof(int));
|
|
982
|
+
diff = malloc(MAX_IM * sizeof(double));
|
|
983
|
+
|
|
984
|
+
xi_zeta_tensors(ncols, nrows, ny, ycen, ycen_offset, y_lower_lim, osample, PSF_curve, xi, zeta, m_zeta);
|
|
985
|
+
|
|
986
|
+
/* Loop through sL , sP reconstruction until convergence is reached */
|
|
987
|
+
iter = 0;
|
|
988
|
+
do
|
|
989
|
+
{
|
|
990
|
+
// Save the total cost (chi-square) from the previous iteration
|
|
991
|
+
cost_old = cost;
|
|
992
|
+
|
|
993
|
+
/* Compute slit function sL */
|
|
994
|
+
|
|
995
|
+
/* Prepare the RHS and the matrix */
|
|
996
|
+
for (iy = 0; iy < MAX_LBJ; iy++)
|
|
997
|
+
l_bj[lbj_index(iy)] = 0.e0; /* Clean RHS */
|
|
998
|
+
for (iy = 0; iy < MAX_LAIJ; iy++)
|
|
999
|
+
l_Aij[iy] = 0;
|
|
1000
|
+
|
|
1001
|
+
/* Fill in SLE arrays for slit function */
|
|
1002
|
+
diag_tot = 0.e0;
|
|
1003
|
+
for (iy = 0; iy < ny; iy++)
|
|
1004
|
+
{
|
|
1005
|
+
for (x = 0; x < ncols; x++)
|
|
1006
|
+
{
|
|
1007
|
+
for (n = 0; n < 4; n++)
|
|
1008
|
+
{
|
|
1009
|
+
ww = xi[xi_index(x, iy, n)].w;
|
|
1010
|
+
if (ww > 0)
|
|
1011
|
+
{
|
|
1012
|
+
xx = xi[xi_index(x, iy, n)].x;
|
|
1013
|
+
yy = xi[xi_index(x, iy, n)].y;
|
|
1014
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows)
|
|
1015
|
+
{
|
|
1016
|
+
if (m_zeta[mzeta_index(xx, yy)] > 0)
|
|
1017
|
+
{
|
|
1018
|
+
for (m = 0; m < m_zeta[mzeta_index(xx, yy)]; m++)
|
|
1019
|
+
{
|
|
1020
|
+
xxx = zeta[zeta_index(xx, yy, m)].x;
|
|
1021
|
+
jy = zeta[zeta_index(xx, yy, m)].iy;
|
|
1022
|
+
www = zeta[zeta_index(xx, yy, m)].w;
|
|
1023
|
+
l_Aij[laij_index(iy, jy - iy + 2 * osample)] += sP[sp_index(xxx)] * sP[sp_index(x)] * www * ww * mask[im_index(xx, yy)];
|
|
1024
|
+
}
|
|
1025
|
+
l_bj[lbj_index(iy)] += im[im_index(xx, yy)] * mask[im_index(xx, yy)] * sP[sp_index(x)] * ww;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
diag_tot += l_Aij[laij_index(iy, 2 * osample)];
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/* Scale regularization parameters */
|
|
1035
|
+
lambda = lambda_sL * diag_tot / ny;
|
|
1036
|
+
|
|
1037
|
+
/* Add regularization parts for the SLE matrix */
|
|
1038
|
+
|
|
1039
|
+
l_Aij[laij_index(0, 2 * osample)] += lambda; /* Main diagonal */
|
|
1040
|
+
l_Aij[laij_index(0, 2 * osample + 1)] -= lambda; /* Upper diagonal */
|
|
1041
|
+
for (iy = 1; iy < ny - 1; iy++)
|
|
1042
|
+
{
|
|
1043
|
+
l_Aij[laij_index(iy, 2 * osample - 1)] -= lambda; /* Lower diagonal */
|
|
1044
|
+
l_Aij[laij_index(iy, 2 * osample)] += lambda * 2.e0; /* Main diagonal */
|
|
1045
|
+
l_Aij[laij_index(iy, 2 * osample + 1)] -= lambda; /* Upper diagonal */
|
|
1046
|
+
}
|
|
1047
|
+
l_Aij[laij_index(ny - 1, 2 * osample - 1)] -= lambda; /* Lower diagonal */
|
|
1048
|
+
l_Aij[laij_index(ny - 1, 2 * osample)] += lambda; /* Main diagonal */
|
|
1049
|
+
|
|
1050
|
+
/* Solve the system of equations */
|
|
1051
|
+
bandsol(l_Aij, l_bj, MAX_LAIJ_X, MAX_LAIJ_Y);
|
|
1052
|
+
|
|
1053
|
+
/* Normalize the slit function */
|
|
1054
|
+
|
|
1055
|
+
norm = 0.e0;
|
|
1056
|
+
for (iy = 0; iy < ny; iy++)
|
|
1057
|
+
{
|
|
1058
|
+
sL[sl_index(iy)] = l_bj[lbj_index(iy)];
|
|
1059
|
+
norm += sL[sl_index(iy)];
|
|
1060
|
+
}
|
|
1061
|
+
norm /= osample;
|
|
1062
|
+
for (iy = 0; iy < ny; iy++)
|
|
1063
|
+
sL[sl_index(iy)] /= norm;
|
|
1064
|
+
|
|
1065
|
+
/* Compute spectrum sP */
|
|
1066
|
+
for (x = 0; x < MAX_PBJ; x++)
|
|
1067
|
+
p_bj[pbj_index(x)] = 0;
|
|
1068
|
+
for (x = 0; x < MAX_PAIJ; x++)
|
|
1069
|
+
p_Aij[x] = 0;
|
|
1070
|
+
|
|
1071
|
+
for (x = 0; x < ncols; x++)
|
|
1072
|
+
{
|
|
1073
|
+
for (iy = 0; iy < ny; iy++)
|
|
1074
|
+
{
|
|
1075
|
+
for (n = 0; n < 4; n++)
|
|
1076
|
+
{
|
|
1077
|
+
ww = xi[xi_index(x, iy, n)].w;
|
|
1078
|
+
if (ww > 0)
|
|
1079
|
+
{
|
|
1080
|
+
xx = xi[xi_index(x, iy, n)].x;
|
|
1081
|
+
yy = xi[xi_index(x, iy, n)].y;
|
|
1082
|
+
if (xx >= 0 && xx < ncols && yy >= 0 && yy < nrows)
|
|
1083
|
+
{
|
|
1084
|
+
if (m_zeta[mzeta_index(xx, yy)] > 0)
|
|
1085
|
+
{
|
|
1086
|
+
for (m = 0; m < m_zeta[mzeta_index(xx, yy)]; m++)
|
|
1087
|
+
{
|
|
1088
|
+
xxx = zeta[zeta_index(xx, yy, m)].x;
|
|
1089
|
+
jy = zeta[zeta_index(xx, yy, m)].iy;
|
|
1090
|
+
www = zeta[zeta_index(xx, yy, m)].w;
|
|
1091
|
+
p_Aij[paij_index(x, xxx - x + 2 * delta_x)] += sL[sl_index(jy)] * sL[sl_index(iy)] * www * ww * mask[im_index(xx, yy)];
|
|
1092
|
+
}
|
|
1093
|
+
p_bj[pbj_index(x)] += im[im_index(xx, yy)] * mask[im_index(xx, yy)] * sL[sl_index(iy)] * ww;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
if (lambda_sP > 0.e0)
|
|
1102
|
+
{
|
|
1103
|
+
norm = 0.e0;
|
|
1104
|
+
for (x = 0; x < ncols; x++)
|
|
1105
|
+
{
|
|
1106
|
+
norm += sP[sp_index(x)];
|
|
1107
|
+
}
|
|
1108
|
+
norm /= ncols;
|
|
1109
|
+
lambda = lambda_sP * norm; /* Scale regularization parameter */
|
|
1110
|
+
|
|
1111
|
+
p_Aij[paij_index(0, 2 * delta_x)] += lambda; /* Main diagonal */
|
|
1112
|
+
p_Aij[paij_index(0, 2 * delta_x + 1)] -= lambda; /* Upper diagonal */
|
|
1113
|
+
for (x = 1; x < ncols - 1; x++)
|
|
1114
|
+
{
|
|
1115
|
+
p_Aij[paij_index(x, 2 * delta_x - 1)] -= lambda; /* Lower diagonal */
|
|
1116
|
+
p_Aij[paij_index(x, 2 * delta_x)] += lambda * 2.e0; /* Main diagonal */
|
|
1117
|
+
p_Aij[paij_index(x, 2 * delta_x + 1)] -= lambda; /* Upper diagonal */
|
|
1118
|
+
}
|
|
1119
|
+
p_Aij[paij_index(ncols - 1, 2 * delta_x - 1)] -= lambda; /* Lower diagonal */
|
|
1120
|
+
p_Aij[paij_index(ncols - 1, 2 * delta_x)] += lambda; /* Main diagonal */
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/* Solve the system of equations */
|
|
1124
|
+
bandsol(p_Aij, p_bj, MAX_PAIJ_X, MAX_PAIJ_Y);
|
|
1125
|
+
|
|
1126
|
+
for (x = 0; x < ncols; x++)
|
|
1127
|
+
sP[sp_index(x)] = p_bj[pbj_index(x)];
|
|
1128
|
+
|
|
1129
|
+
/* Compute the model */
|
|
1130
|
+
for (x = 0; x < MAX_IM; x++)
|
|
1131
|
+
{
|
|
1132
|
+
model[x] = 0.;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
for (y = 0; y < nrows; y++)
|
|
1136
|
+
{
|
|
1137
|
+
for (x = 0; x < ncols; x++)
|
|
1138
|
+
{
|
|
1139
|
+
for (m = 0; m < m_zeta[mzeta_index(x, y)]; m++)
|
|
1140
|
+
{
|
|
1141
|
+
xx = zeta[zeta_index(x, y, m)].x;
|
|
1142
|
+
iy = zeta[zeta_index(x, y, m)].iy;
|
|
1143
|
+
ww = zeta[zeta_index(x, y, m)].w;
|
|
1144
|
+
model[im_index(x, y)] += sP[xx] * sL[iy] * ww;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/* Compare model and data */
|
|
1150
|
+
// We use the Median absolute derivation to estimate the distribution
|
|
1151
|
+
// The MAD is more robust than the usual STD as it uses the median
|
|
1152
|
+
// However the MAD << STD, since we are not dealing with a Gaussian
|
|
1153
|
+
// at all, but a distribution with heavy wings.
|
|
1154
|
+
// Therefore we use the factor 40, instead of 6 to estimate a reasonable range
|
|
1155
|
+
// of values. The cutoff is roughly the same.
|
|
1156
|
+
// Technically the distribution might best be described by a Voigt profile
|
|
1157
|
+
// which we then would have to fit to the distrubtion and then determine,
|
|
1158
|
+
// the range that covers 99% of the data.
|
|
1159
|
+
// Since that is much more complicated we just use the MAD.
|
|
1160
|
+
cost = 0;
|
|
1161
|
+
isum = 0;
|
|
1162
|
+
for (y = 0; y < nrows; y++)
|
|
1163
|
+
{
|
|
1164
|
+
for (x = delta_x; x < ncols - delta_x; x++)
|
|
1165
|
+
{
|
|
1166
|
+
if (mask[im_index(x, y)])
|
|
1167
|
+
{
|
|
1168
|
+
tmp = model[im_index(x, y)] - im[im_index(x, y)];
|
|
1169
|
+
diff[isum] = tmp;
|
|
1170
|
+
tmp /= max(pix_unc[im_index(x, y)], 1);
|
|
1171
|
+
cost += tmp * tmp;
|
|
1172
|
+
isum++;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
cost /= (isum - (ncols + ny));
|
|
1177
|
+
dev = median_absolute_deviation(diff, isum);
|
|
1178
|
+
// This is the "conversion" factor betweem MAD and STD
|
|
1179
|
+
// i.e. a perfect normal distribution has MAD = sqrt(2/pi) * STD
|
|
1180
|
+
dev *= 1.4826;
|
|
1181
|
+
|
|
1182
|
+
/* Adjust the mask marking outlyers */
|
|
1183
|
+
for (y = 0; y < nrows; y++)
|
|
1184
|
+
{
|
|
1185
|
+
for (x = delta_x; x < ncols - delta_x; x++)
|
|
1186
|
+
{
|
|
1187
|
+
// The MAD is significantly smaller than the STD was, since it describes
|
|
1188
|
+
// only the central peak, not the distribution
|
|
1189
|
+
// The factor 40 was chosen, since it is roughly equal to 6 * STD
|
|
1190
|
+
if (fabs(model[im_index(x, y)] - im[im_index(x, y)]) < 40. * dev)
|
|
1191
|
+
mask[im_index(x, y)] = 1;
|
|
1192
|
+
else
|
|
1193
|
+
mask[im_index(x, y)] = 0;
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
#if DEBUG
|
|
1198
|
+
if (cost == 0)
|
|
1199
|
+
{
|
|
1200
|
+
printf("Iteration: %i, Reduced chi-square: %f\n", iter, cost);
|
|
1201
|
+
printf("dev: %f\n", dev);
|
|
1202
|
+
printf("isum: %i\n", isum);
|
|
1203
|
+
printf("iteration: %i\n", iter);
|
|
1204
|
+
printf("-----------\n");
|
|
1205
|
+
}
|
|
1206
|
+
#endif
|
|
1207
|
+
/* Check for convergence */
|
|
1208
|
+
} while (((iter++ < maxiter) && (cost_old - cost > ftol)) || ((isfinite(cost) == 0) || ((isfinite(cost_old) == 0))));
|
|
1209
|
+
|
|
1210
|
+
if (iter >= maxiter - 1)
|
|
1211
|
+
{
|
|
1212
|
+
status = -1; // ran out of iterations
|
|
1213
|
+
success = 0;
|
|
1214
|
+
}
|
|
1215
|
+
else if (cost_old - cost <= ftol)
|
|
1216
|
+
status = 1; // cost did not improve enough between iterations
|
|
1217
|
+
|
|
1218
|
+
/* Uncertainty estimate */
|
|
1219
|
+
|
|
1220
|
+
for (x = 0; x < ncols; x++)
|
|
1221
|
+
{
|
|
1222
|
+
unc[sp_index(x)] = 0.;
|
|
1223
|
+
p_bj[pbj_index(x)] = 0.;
|
|
1224
|
+
p_Aij[paij_index(x, 0)] = 0;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
for (y = 0; y < nrows; y++)
|
|
1228
|
+
{
|
|
1229
|
+
for (x = 0; x < ncols; x++)
|
|
1230
|
+
{
|
|
1231
|
+
for (m = 0; m < m_zeta[mzeta_index(x, y)]; m++) // Loop through all pixels contributing to x,y
|
|
1232
|
+
{
|
|
1233
|
+
if (mask[im_index(x, y)])
|
|
1234
|
+
{
|
|
1235
|
+
// Should pix_unc contribute here?
|
|
1236
|
+
xx = zeta[zeta_index(x, y, m)].x;
|
|
1237
|
+
iy = zeta[zeta_index(x, y, m)].iy;
|
|
1238
|
+
ww = zeta[zeta_index(x, y, m)].w;
|
|
1239
|
+
tmp = im[im_index(x, y)] - model[im_index(x, y)];
|
|
1240
|
+
unc[sp_index(xx)] += tmp * tmp * ww;
|
|
1241
|
+
p_bj[pbj_index(xx)] += ww; // Norm
|
|
1242
|
+
p_Aij[paij_index(xx, 0)] += ww * ww; // Norm squared
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
for (x = 0; x < ncols; x++)
|
|
1249
|
+
{
|
|
1250
|
+
norm = p_bj[pbj_index(x)] - p_Aij[paij_index(x, 0)] / p_bj[pbj_index(x)];
|
|
1251
|
+
unc[sp_index(x)] = sqrt(unc[sp_index(x)] / norm * nrows);
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
for (x = 0; x < delta_x; x++)
|
|
1255
|
+
{
|
|
1256
|
+
sP[sp_index(x)] = unc[sp_index(x)] = 0;
|
|
1257
|
+
}
|
|
1258
|
+
for (x = ncols - delta_x; x < ncols; x++)
|
|
1259
|
+
{
|
|
1260
|
+
sP[sp_index(x)] = unc[sp_index(x)] = 0;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
free(diff);
|
|
1264
|
+
free(l_Aij);
|
|
1265
|
+
free(p_Aij);
|
|
1266
|
+
free(p_bj);
|
|
1267
|
+
free(l_bj);
|
|
1268
|
+
|
|
1269
|
+
free(xi);
|
|
1270
|
+
free(zeta);
|
|
1271
|
+
free(m_zeta);
|
|
1272
|
+
|
|
1273
|
+
info[0] = success;
|
|
1274
|
+
info[1] = cost;
|
|
1275
|
+
info[2] = status;
|
|
1276
|
+
info[3] = iter;
|
|
1277
|
+
info[4] = delta_x;
|
|
1278
|
+
|
|
1279
|
+
return 0;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
int create_spectral_model(int ncols, int nrows, int osample, xi_ref* xi, double* spec, double* slitfunc, double* img){
|
|
1283
|
+
int ny, pix_x, pix_y, x, iy, m;
|
|
1284
|
+
double pix_w;
|
|
1285
|
+
|
|
1286
|
+
ny = (nrows + 1) * osample + 1;
|
|
1287
|
+
|
|
1288
|
+
for (x = 0; x < ncols; x++)
|
|
1289
|
+
{
|
|
1290
|
+
for (iy = 0; iy < nrows+1; iy++)
|
|
1291
|
+
{
|
|
1292
|
+
img[im_index(x, iy)] = 0;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
for (x = 0; x < ncols; x++)
|
|
1298
|
+
{
|
|
1299
|
+
for (iy = 0; iy < ny; iy++)
|
|
1300
|
+
{
|
|
1301
|
+
for (m = 0; m < 4; m++)
|
|
1302
|
+
{
|
|
1303
|
+
pix_x = xi[xi_index(x, iy, m)].x;
|
|
1304
|
+
pix_y = xi[xi_index(x, iy, m)].y;
|
|
1305
|
+
pix_w = xi[xi_index(x, iy, m)].w;
|
|
1306
|
+
if ((pix_x != -1) && (pix_y != -1) && (pix_w != 0)){
|
|
1307
|
+
img[im_index(pix_x, pix_y)] += pix_w * spec[x] * slitfunc[iy];
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
return 0;
|
|
1313
|
+
}
|