pivtools 0.1.3__cp311-cp311-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.
Files changed (127) hide show
  1. pivtools-0.1.3.dist-info/METADATA +222 -0
  2. pivtools-0.1.3.dist-info/RECORD +127 -0
  3. pivtools-0.1.3.dist-info/WHEEL +5 -0
  4. pivtools-0.1.3.dist-info/entry_points.txt +3 -0
  5. pivtools-0.1.3.dist-info/top_level.txt +3 -0
  6. pivtools_cli/__init__.py +5 -0
  7. pivtools_cli/_build_marker.c +25 -0
  8. pivtools_cli/_build_marker.cp311-win_amd64.pyd +0 -0
  9. pivtools_cli/cli.py +225 -0
  10. pivtools_cli/example.py +139 -0
  11. pivtools_cli/lib/PIV_2d_cross_correlate.c +334 -0
  12. pivtools_cli/lib/PIV_2d_cross_correlate.h +22 -0
  13. pivtools_cli/lib/common.h +36 -0
  14. pivtools_cli/lib/interp2custom.c +146 -0
  15. pivtools_cli/lib/interp2custom.h +48 -0
  16. pivtools_cli/lib/peak_locate_gsl.c +711 -0
  17. pivtools_cli/lib/peak_locate_gsl.h +40 -0
  18. pivtools_cli/lib/peak_locate_gsl_print.c +736 -0
  19. pivtools_cli/lib/peak_locate_lm.c +751 -0
  20. pivtools_cli/lib/peak_locate_lm.h +27 -0
  21. pivtools_cli/lib/xcorr.c +342 -0
  22. pivtools_cli/lib/xcorr.h +31 -0
  23. pivtools_cli/lib/xcorr_cache.c +78 -0
  24. pivtools_cli/lib/xcorr_cache.h +26 -0
  25. pivtools_cli/piv/interp2custom/interp2custom.py +69 -0
  26. pivtools_cli/piv/piv.py +240 -0
  27. pivtools_cli/piv/piv_backend/base.py +825 -0
  28. pivtools_cli/piv/piv_backend/cpu_instantaneous.py +1005 -0
  29. pivtools_cli/piv/piv_backend/factory.py +28 -0
  30. pivtools_cli/piv/piv_backend/gpu_instantaneous.py +15 -0
  31. pivtools_cli/piv/piv_backend/infilling.py +445 -0
  32. pivtools_cli/piv/piv_backend/outlier_detection.py +306 -0
  33. pivtools_cli/piv/piv_backend/profile_cpu_instantaneous.py +230 -0
  34. pivtools_cli/piv/piv_result.py +40 -0
  35. pivtools_cli/piv/save_results.py +342 -0
  36. pivtools_cli/piv_cluster/cluster.py +108 -0
  37. pivtools_cli/preprocessing/filters.py +399 -0
  38. pivtools_cli/preprocessing/preprocess.py +79 -0
  39. pivtools_cli/tests/helpers.py +107 -0
  40. pivtools_cli/tests/instantaneous_piv/test_piv_integration.py +167 -0
  41. pivtools_cli/tests/instantaneous_piv/test_piv_integration_multi.py +553 -0
  42. pivtools_cli/tests/preprocessing/test_filters.py +41 -0
  43. pivtools_core/__init__.py +5 -0
  44. pivtools_core/config.py +703 -0
  45. pivtools_core/config.yaml +135 -0
  46. pivtools_core/image_handling/__init__.py +0 -0
  47. pivtools_core/image_handling/load_images.py +464 -0
  48. pivtools_core/image_handling/readers/__init__.py +53 -0
  49. pivtools_core/image_handling/readers/generic_readers.py +50 -0
  50. pivtools_core/image_handling/readers/lavision_reader.py +190 -0
  51. pivtools_core/image_handling/readers/registry.py +24 -0
  52. pivtools_core/paths.py +49 -0
  53. pivtools_core/vector_loading.py +248 -0
  54. pivtools_gui/__init__.py +3 -0
  55. pivtools_gui/app.py +687 -0
  56. pivtools_gui/calibration/__init__.py +0 -0
  57. pivtools_gui/calibration/app/__init__.py +0 -0
  58. pivtools_gui/calibration/app/views.py +1186 -0
  59. pivtools_gui/calibration/calibration_planar/planar_calibration_production.py +570 -0
  60. pivtools_gui/calibration/vector_calibration_production.py +544 -0
  61. pivtools_gui/config.py +703 -0
  62. pivtools_gui/image_handling/__init__.py +0 -0
  63. pivtools_gui/image_handling/load_images.py +464 -0
  64. pivtools_gui/image_handling/readers/__init__.py +53 -0
  65. pivtools_gui/image_handling/readers/generic_readers.py +50 -0
  66. pivtools_gui/image_handling/readers/lavision_reader.py +190 -0
  67. pivtools_gui/image_handling/readers/registry.py +24 -0
  68. pivtools_gui/masking/__init__.py +0 -0
  69. pivtools_gui/masking/app/__init__.py +0 -0
  70. pivtools_gui/masking/app/views.py +123 -0
  71. pivtools_gui/paths.py +49 -0
  72. pivtools_gui/piv_runner.py +261 -0
  73. pivtools_gui/pivtools.py +58 -0
  74. pivtools_gui/plotting/__init__.py +0 -0
  75. pivtools_gui/plotting/app/__init__.py +0 -0
  76. pivtools_gui/plotting/app/views.py +1671 -0
  77. pivtools_gui/plotting/plot_maker.py +220 -0
  78. pivtools_gui/post_processing/POD/__init__.py +0 -0
  79. pivtools_gui/post_processing/POD/app/__init__.py +0 -0
  80. pivtools_gui/post_processing/POD/app/views.py +647 -0
  81. pivtools_gui/post_processing/POD/pod_decompose.py +979 -0
  82. pivtools_gui/post_processing/POD/views.py +1096 -0
  83. pivtools_gui/post_processing/__init__.py +0 -0
  84. pivtools_gui/static/404.html +1 -0
  85. pivtools_gui/static/_next/static/chunks/117-d5793c8e79de5511.js +2 -0
  86. pivtools_gui/static/_next/static/chunks/484-cfa8b9348ce4f00e.js +1 -0
  87. pivtools_gui/static/_next/static/chunks/869-320a6b9bdafbb6d3.js +1 -0
  88. pivtools_gui/static/_next/static/chunks/app/_not-found/page-12f067ceb7415e55.js +1 -0
  89. pivtools_gui/static/_next/static/chunks/app/layout-b907d5f31ac82e9d.js +1 -0
  90. pivtools_gui/static/_next/static/chunks/app/page-334cc4e8444cde2f.js +1 -0
  91. pivtools_gui/static/_next/static/chunks/fd9d1056-ad15f396ddf9b7e5.js +1 -0
  92. pivtools_gui/static/_next/static/chunks/framework-f66176bb897dc684.js +1 -0
  93. pivtools_gui/static/_next/static/chunks/main-a1b3ced4d5f6d998.js +1 -0
  94. pivtools_gui/static/_next/static/chunks/main-app-8a63c6f5e7baee11.js +1 -0
  95. pivtools_gui/static/_next/static/chunks/pages/_app-72b849fbd24ac258.js +1 -0
  96. pivtools_gui/static/_next/static/chunks/pages/_error-7ba65e1336b92748.js +1 -0
  97. pivtools_gui/static/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  98. pivtools_gui/static/_next/static/chunks/webpack-4a8ca7c99e9bb3d8.js +1 -0
  99. pivtools_gui/static/_next/static/css/7d3f2337d7ea12a5.css +3 -0
  100. pivtools_gui/static/_next/static/vQeR20OUdSSKlK4vukC4q/_buildManifest.js +1 -0
  101. pivtools_gui/static/_next/static/vQeR20OUdSSKlK4vukC4q/_ssgManifest.js +1 -0
  102. pivtools_gui/static/file.svg +1 -0
  103. pivtools_gui/static/globe.svg +1 -0
  104. pivtools_gui/static/grid.svg +8 -0
  105. pivtools_gui/static/index.html +1 -0
  106. pivtools_gui/static/index.txt +8 -0
  107. pivtools_gui/static/next.svg +1 -0
  108. pivtools_gui/static/vercel.svg +1 -0
  109. pivtools_gui/static/window.svg +1 -0
  110. pivtools_gui/stereo_reconstruction/__init__.py +0 -0
  111. pivtools_gui/stereo_reconstruction/app/__init__.py +0 -0
  112. pivtools_gui/stereo_reconstruction/app/views.py +1985 -0
  113. pivtools_gui/stereo_reconstruction/stereo_calibration_production.py +606 -0
  114. pivtools_gui/stereo_reconstruction/stereo_reconstruction_production.py +544 -0
  115. pivtools_gui/utils.py +63 -0
  116. pivtools_gui/vector_loading.py +248 -0
  117. pivtools_gui/vector_merging/__init__.py +1 -0
  118. pivtools_gui/vector_merging/app/__init__.py +1 -0
  119. pivtools_gui/vector_merging/app/views.py +759 -0
  120. pivtools_gui/vector_statistics/app/__init__.py +1 -0
  121. pivtools_gui/vector_statistics/app/views.py +710 -0
  122. pivtools_gui/vector_statistics/ensemble_statistics.py +49 -0
  123. pivtools_gui/vector_statistics/instantaneous_statistics.py +311 -0
  124. pivtools_gui/video_maker/__init__.py +0 -0
  125. pivtools_gui/video_maker/app/__init__.py +0 -0
  126. pivtools_gui/video_maker/app/views.py +436 -0
  127. pivtools_gui/video_maker/video_maker.py +662 -0
@@ -0,0 +1,27 @@
1
+ #ifndef PEAK_LOCATE_LM_H
2
+ #define PEAK_LOCATE_LM_H
3
+
4
+ /**** defines ****/
5
+ /* Peak localization window size (odd numbers only: 3, 5, 7, 9, ...)
6
+ * 5×5 is optimal for most PIV applications
7
+ * 7×7 provides more robustness for noisy data but is ~2x slower
8
+ * 3×3 is faster but less accurate
9
+ */
10
+ #define PKSIZE_X 5
11
+ #define PKSIZE_Y 5
12
+
13
+ /******************************************************************************
14
+ * Fast Levenberg-Marquardt peak localization
15
+ *
16
+ * Drop-in replacement for GSL-based lsqpeaklocate
17
+ * Optimized for PIV correlation peak fitting with:
18
+ * - No external dependencies (GSL-free)
19
+ * - Direct Jacobian computation
20
+ * - Fast convergence for PIV peaks
21
+ * - Reduced iteration count
22
+ *****************************************************************************/
23
+
24
+ /* Main peak localization function - compatible with existing interface */
25
+ void lsqpeaklocate_lm(const float *xcorr, const int *N, float *peak_loc, int nPeaks, int iFitType, float *std_dev);
26
+
27
+ #endif
@@ -0,0 +1,342 @@
1
+ #include "xcorr.h"
2
+ #include "common.h"
3
+ #include <fftw3.h>
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+ #include <omp.h>
7
+
8
+ /****************************************************
9
+ * unsigned convolve(a, b, c, N)
10
+ * convolve a with b and store the result in c
11
+ * accomplished using zero padded cross-correlation of a and b
12
+ */
13
+ unsigned convolve(const float *fA, const float *fB, float *fC, const int *N)
14
+ {
15
+ int Npad[2];
16
+ int Nvox;
17
+ int i, j;
18
+ float *fApad, *fBpad, *fCpad;
19
+ unsigned uError;
20
+
21
+ /* allocate memory for padded versions of a, b and c */
22
+ Npad[0] = N[0]*2;
23
+ Npad[1] = N[1]*2;
24
+ Nvox = Npad[0] * Npad[1];
25
+
26
+ fApad = (float*)malloc(Nvox * sizeof(float));
27
+ fBpad = (float*)malloc(Nvox * sizeof(float));
28
+ fCpad = (float*)malloc(Nvox * sizeof(float));
29
+ if(!fApad || !fBpad || !fCpad)
30
+ {
31
+ if(fApad) free(fApad);
32
+ if(fBpad) free(fBpad);
33
+ if(fCpad) free(fCpad);
34
+ return ERROR_NOMEM;
35
+ }
36
+
37
+ /* Copy input to zero-padded arrays (row-major) */
38
+ memset(fApad, 0, Nvox * sizeof(float));
39
+ memset(fBpad, 0, Nvox * sizeof(float));
40
+ for(i = 0; i < N[0]; ++i) /* rows */
41
+ {
42
+ for(j = 0; j < N[1]; ++j) /* columns */
43
+ {
44
+ fApad[SUB2IND_2D(i+N[0]/2, j+N[1]/2, Npad[1])] =
45
+ fA[SUB2IND_2D(i, j, N[1])];
46
+ fBpad[SUB2IND_2D(i+N[0]/2, j+N[1]/2, Npad[1])] =
47
+ fB[SUB2IND_2D(i, j, N[1])];
48
+ }
49
+ }
50
+
51
+ /* cross-correlate */
52
+ uError = xcorr(fApad, fBpad, fCpad, Npad);
53
+
54
+ /* copy centre of fCpad into fC */
55
+ for(i = 0; i < N[0]; ++i)
56
+ {
57
+ for(j = 0; j < N[1]; ++j)
58
+ {
59
+ fC[SUB2IND_2D(i, j, N[1])] =
60
+ fCpad[SUB2IND_2D(i+N[0]/2, j+N[1]/2, Npad[1])];
61
+ }
62
+ }
63
+
64
+ /* free memory */
65
+ free(fApad);
66
+ free(fBpad);
67
+ free(fCpad);
68
+
69
+ return uError;
70
+ }
71
+
72
+
73
+ /****************************************************
74
+ * unsigned xcorr_create_plan(N, planstruct)
75
+ *
76
+ * create plans for cross-correlation
77
+ * xcorr_plan is NOT thread safe
78
+ * so run it inside an OMP critical directive
79
+ *
80
+ */
81
+
82
+ unsigned xcorr_create_plan(const int *N, sPlan *pPlanStruct)
83
+ {
84
+ fftwf_plan plan_AB_fft;
85
+ fftwf_plan plan_C_ifft;
86
+ fftwf_real *ab_copy;
87
+ fftwf_real *c_copy;
88
+ fftwf_complex *AB_copy;
89
+ fftwf_complex *C;
90
+ unsigned numel, numel_fft;
91
+ int Nbackwards[2];
92
+
93
+ if(!pPlanStruct)
94
+ return ERROR_NOMEM;
95
+
96
+ /* initialise some bits we need */
97
+ numel = N[0] * N[1];
98
+ numel_fft = N[1] * (N[0]/2+1);
99
+ Nbackwards[0] = N[1];
100
+ Nbackwards[1] = N[0];
101
+
102
+ /* allocate memory using fftw's aligned allocation for SIMD optimization
103
+ * fftwf_alloc_* ensures proper alignment for SSE/AVX instructions
104
+ */
105
+ ab_copy = (fftwf_real *) fftwf_alloc_real(numel * 2);
106
+ AB_copy = (fftwf_complex *) fftwf_alloc_complex(numel_fft * 2); // column major format
107
+ C = (fftwf_complex *) fftwf_alloc_complex(numel_fft );
108
+ c_copy = (fftwf_real *) fftwf_alloc_real(numel);
109
+ if(!ab_copy || !AB_copy || !C || !c_copy)
110
+ {
111
+ if(ab_copy) fftwf_free(ab_copy);
112
+ if(AB_copy) fftwf_free(AB_copy);
113
+ if(C) fftwf_free(C);
114
+ if(c_copy) fftwf_free(c_copy);
115
+ return ERROR_NOMEM;
116
+ }
117
+
118
+ /* create plans with FFTW_MEASURE for optimal performance
119
+ * FFTW_MEASURE takes more time initially but creates much faster plans
120
+ * The plans are reused many times, so this cost is amortized
121
+ */
122
+ //fftwf_plan_with_nthreads(1);
123
+ plan_AB_fft = fftwf_plan_many_dft_r2c( 2, Nbackwards, 2,
124
+ ab_copy, NULL,
125
+ 1, N[0]*N[1],
126
+ AB_copy, NULL,
127
+ 1, N[1]*(N[0]/2+1),
128
+ FFTW_MEASURE | FFTW_DESTROY_INPUT);
129
+ plan_C_ifft = fftwf_plan_dft_c2r_2d( N[1], N[0], C, c_copy, FFTW_MEASURE | FFTW_DESTROY_INPUT );
130
+ if(!plan_AB_fft || !plan_C_ifft)
131
+ {
132
+ fftwf_free(AB_copy);
133
+ fftwf_free(c_copy);
134
+ fftwf_free(ab_copy);
135
+ fftwf_free(C);
136
+ if(!plan_C_ifft)
137
+ return ERROR_NOPLAN_BWD;
138
+ return ERROR_NOPLAN_FWD;
139
+ }
140
+
141
+ /* put into output structure */
142
+ pPlanStruct->plan_AB_fft = plan_AB_fft;
143
+ pPlanStruct->plan_C_ifft = plan_C_ifft;
144
+ pPlanStruct->ab_copy = ab_copy;
145
+ pPlanStruct->AB_copy = AB_copy;
146
+ pPlanStruct->C = C;
147
+ pPlanStruct->c_copy = c_copy;
148
+ pPlanStruct->N[0] = N[0];
149
+ pPlanStruct->N[1] = N[1];
150
+
151
+ return ERROR_NONE;
152
+ }
153
+
154
+ /****************************************************
155
+ * unsigned xcorr_destroy_plan(planstruct)
156
+ *
157
+ * destroy plans created by xcorr_create_plan
158
+ * xcorr_destroy_plan is NOT thread safe
159
+ * so call it from an OMP critical section
160
+ */
161
+
162
+ unsigned xcorr_destroy_plan(sPlan *pPlanStruct)
163
+ {
164
+ if(!pPlanStruct)
165
+ return ERROR_NOMEM;
166
+
167
+ /* destroy plans first */
168
+ if(pPlanStruct->plan_AB_fft) fftwf_destroy_plan(pPlanStruct->plan_AB_fft);
169
+ if(pPlanStruct->plan_C_ifft) fftwf_destroy_plan(pPlanStruct->plan_C_ifft);
170
+
171
+ /* deallocate memory*/
172
+ if(pPlanStruct->ab_copy) fftwf_free(pPlanStruct->ab_copy);
173
+ if(pPlanStruct->AB_copy) fftwf_free(pPlanStruct->AB_copy);
174
+ if(pPlanStruct->C) fftwf_free(pPlanStruct->C);
175
+ if(pPlanStruct->c_copy) fftwf_free(pPlanStruct->c_copy);
176
+
177
+ return ERROR_NONE;
178
+ }
179
+
180
+ /****************************************************
181
+ * unsigned xcorr_preplanned(a, b, c, sPlan)
182
+ * same as xcorr(a,b,c,sPlan->N), but with preplanned FFTs
183
+ *
184
+ * calculates c = fftshift( IFFT(FFT(a) .* FFT(b)') )
185
+ * xcorr_preplanned is thread safe
186
+ * HOWEVER, you cannot use plans generated by other threads
187
+ *
188
+ */
189
+
190
+ unsigned xcorr_preplanned(const float *a, const float *b, float *c, sPlan *pPlanStruct)
191
+ {
192
+ fftwf_plan plan_AB_fft;
193
+ fftwf_plan plan_C_ifft;
194
+ fftwf_real *ab_copy;
195
+ fftwf_real *c_copy;
196
+ fftwf_complex *AB_copy;
197
+ fftwf_complex *C;
198
+ unsigned numel, numel_fft;
199
+ int N[2];
200
+ int jswap, j;
201
+ float mul;
202
+
203
+ if(!pPlanStruct)
204
+ return ERROR_NOMEM;
205
+
206
+ /* load plan from sPlanStruct */
207
+ N[0] = pPlanStruct->N[0];
208
+ N[1] = pPlanStruct->N[1];
209
+ plan_AB_fft = pPlanStruct->plan_AB_fft;
210
+ plan_C_ifft = pPlanStruct->plan_C_ifft;
211
+ ab_copy = pPlanStruct->ab_copy;
212
+ AB_copy = pPlanStruct->AB_copy;
213
+ C = pPlanStruct->C;
214
+ c_copy = pPlanStruct->c_copy;
215
+
216
+ /****
217
+ * initialise variables to be used for later
218
+ */
219
+ numel = N[0] * N[1];
220
+ numel_fft = N[1] * (N[0]/2+1);
221
+
222
+ /****
223
+ * copy in a and b, should be done after planning stage as fftwf_plan can overwrite
224
+ * input array
225
+ */
226
+ memcpy(&ab_copy[0 ], a, numel*sizeof(float));
227
+ memcpy(&ab_copy[numel], b, numel*sizeof(float));
228
+
229
+ /****
230
+ * execute forward transform
231
+ */
232
+ fftwf_execute(plan_AB_fft);
233
+
234
+ /****
235
+ * do C = A .* conj(B)
236
+ */
237
+ multiply_conjugate((const fftwf_complex*)&AB_copy[0], (const fftwf_complex*)&AB_copy[numel_fft], C, numel_fft);
238
+
239
+ /***
240
+ * execute backward transform and renormalise
241
+ */
242
+ fftwf_execute(plan_C_ifft);
243
+
244
+ /***
245
+ * Copy ifft result into c, after shifting data with fftshift
246
+ * For row-major arrays with shape [N[0], N[1]]:
247
+ * - N[0] = number of rows (height)
248
+ * - N[1] = number of columns (width)
249
+ * fftshift swaps quadrants: moves zero-frequency to center
250
+ */
251
+ // Renormalise first
252
+ mul = 1.0f / (float)numel;
253
+ for(j=0; j<(int)numel; ++j) { c_copy[j] = c_copy[j] * mul; }
254
+
255
+ /* Perform fftshift for row-major data
256
+ * For each row, swap left/right halves
257
+ * Also swap top/bottom halves
258
+ */
259
+ for(int row = 0; row < N[0]; ++row)
260
+ {
261
+ int row_swap = (row + N[0]/2) % N[0];
262
+ /* Copy left half of swapped row to right half of output */
263
+ memcpy(&c[SUB2IND_2D(row, N[1]/2, N[1])],
264
+ &c_copy[SUB2IND_2D(row_swap, 0, N[1])],
265
+ N[1]/2 * sizeof(float));
266
+ /* Copy right half of swapped row to left half of output */
267
+ memcpy(&c[SUB2IND_2D(row, 0, N[1])],
268
+ &c_copy[SUB2IND_2D(row_swap, N[1]/2, N[1])],
269
+ N[1]/2 * sizeof(float));
270
+ }
271
+
272
+ return ERROR_NONE;
273
+ }
274
+
275
+ /****************************************************
276
+ * unsigned xcorr(a, b, c, N)
277
+ *
278
+ * calculates c = fftshift( IFFT(FFT(a) .* FFT(b)') )
279
+ *
280
+ * a, b, and c are 2D arrays of size N[0]xN[1] supplied in row-major (C-contiguous) format
281
+ * where N[0] = number of rows (height), N[1] = number of columns (width)
282
+ *
283
+ * return value is the error code. return value of 0 means success.
284
+ * NOTE: everything apart from fftwf_execute in fftwf_ library is NOT thread safe
285
+ * so must encapsulate it in an omp critical section
286
+ *
287
+ */
288
+
289
+ unsigned xcorr(const float *a, const float *b, float *c, const int *N)
290
+ {
291
+ sPlan spPlan;
292
+ unsigned uError;
293
+
294
+ /* create plan */
295
+ #pragma omp critical
296
+ {
297
+ uError = xcorr_create_plan(N, &spPlan);
298
+ }
299
+ if(uError != ERROR_NONE)
300
+ return uError;
301
+
302
+ /* cross-correlate with xcorr_preplanned */
303
+ uError = xcorr_preplanned(a, b, c, &spPlan);
304
+
305
+ /* destroy plan */
306
+ #pragma omp critical
307
+ {
308
+ xcorr_destroy_plan(&spPlan);
309
+ }
310
+
311
+ return uError;
312
+ }
313
+
314
+
315
+ /****************************************************
316
+ * void multiply_conjugate(const fftwf_complex *A, const fftwf_complex *B, fftwf_complex *C, int N)
317
+ *
318
+ * performs C = A .* conj(B)
319
+ *
320
+ * A, B and C are treated as 1xN vectors of fftwf_complex
321
+ * Optimized with restrict pointers for better compiler optimization
322
+ */
323
+
324
+ void multiply_conjugate(const fftwf_complex * restrict A, const fftwf_complex * restrict B, fftwf_complex * restrict C, int N)
325
+ {
326
+ int i;
327
+
328
+ /* Use restrict to help compiler optimize and enable SIMD vectorization */
329
+ #pragma omp simd
330
+ for(i=0; i<N; ++i)
331
+ {
332
+ const float Ar = A[i][0];
333
+ const float Ai = A[i][1];
334
+ const float Br = B[i][0];
335
+ const float Bi = B[i][1];
336
+
337
+ C[i][0] = Ar*Br + Ai*Bi;
338
+ C[i][1] = Ai*Br - Ar*Bi;
339
+ }
340
+
341
+ return;
342
+ }
@@ -0,0 +1,31 @@
1
+ #ifndef XCORR_H
2
+ #define XCORR_H
3
+
4
+ #include <fftw3.h>
5
+
6
+ /**** defines ****/
7
+ #define fftwf_real float
8
+
9
+ /**** data structures ****/
10
+ typedef struct _sPlan
11
+ {
12
+ fftwf_plan plan_AB_fft;
13
+ fftwf_plan plan_C_ifft;
14
+ fftwf_real *ab_copy;
15
+ fftwf_real *c_copy;
16
+ fftwf_complex *AB_copy;
17
+ fftwf_complex *C;
18
+ int N[2];
19
+ } sPlan;
20
+
21
+ /**** functions ****/
22
+ unsigned convolve(const float *w1, const float *w2, float *conv, const int *N);
23
+ unsigned xcorr(const float *w1, const float *w2, float *corr, const int *N);
24
+
25
+ unsigned xcorr_create_plan(const int *N, sPlan *pPlanStruct);
26
+ unsigned xcorr_destroy_plan(sPlan *pPlanStruct);
27
+ unsigned xcorr_preplanned(const float *w1, const float *w2, float *corr, sPlan *pPlanStruct);
28
+
29
+ void multiply_conjugate(const fftwf_complex * restrict A, const fftwf_complex * restrict B, fftwf_complex * restrict C, int N);
30
+
31
+ #endif
@@ -0,0 +1,78 @@
1
+ #include "xcorr_cache.h"
2
+ #include "common.h"
3
+ #include <stdlib.h>
4
+ #include <string.h>
5
+ #include <stdio.h>
6
+ #include <sys/stat.h>
7
+ #include <errno.h>
8
+ #include <omp.h>
9
+
10
+ /******************************************************************************
11
+ * FFTW Wisdom Cache and Plan Reuse
12
+ *
13
+ * Implements persistent caching of FFTW plans to avoid expensive planning
14
+ * on repeated runs. Key optimizations:
15
+ *
16
+ * - Save/load FFTW wisdom to disk for persistent optimization
17
+ * - Cache plans by window size to avoid recreating identical plans
18
+ * - Thread-safe plan cache with minimal locking
19
+ *****************************************************************************/
20
+
21
+ /* Global wisdom management */
22
+ static omp_lock_t wisdom_lock;
23
+ static int wisdom_initialized = 0;
24
+
25
+ /* Initialize wisdom system */
26
+ void xcorr_cache_init(const char *wisdom_file)
27
+ {
28
+ if(!wisdom_initialized) {
29
+ omp_init_lock(&wisdom_lock);
30
+ wisdom_initialized = 1;
31
+
32
+ /* Try to load existing wisdom */
33
+ if(wisdom_file && wisdom_file[0] != '\0') {
34
+ omp_set_lock(&wisdom_lock);
35
+ FILE *f = fopen(wisdom_file, "r");
36
+ if(f) {
37
+ fftwf_import_wisdom_from_file(f);
38
+ fclose(f);
39
+ }
40
+ omp_unset_lock(&wisdom_lock);
41
+ }
42
+ }
43
+ }
44
+
45
+ /* Save wisdom to disk */
46
+ void xcorr_cache_save_wisdom(const char *wisdom_file)
47
+ {
48
+ if(!wisdom_initialized || !wisdom_file || wisdom_file[0] == '\0')
49
+ return;
50
+
51
+ omp_set_lock(&wisdom_lock);
52
+ FILE *f = fopen(wisdom_file, "w");
53
+ if(f) {
54
+ fftwf_export_wisdom_to_file(f);
55
+ fclose(f);
56
+ }
57
+ omp_unset_lock(&wisdom_lock);
58
+ }
59
+
60
+ /* Cleanup wisdom system */
61
+ void xcorr_cache_cleanup(void)
62
+ {
63
+ if(wisdom_initialized) {
64
+ omp_destroy_lock(&wisdom_lock);
65
+ wisdom_initialized = 0;
66
+ }
67
+ }
68
+
69
+ /* Get default wisdom file path (in user's home or temp directory) */
70
+ void xcorr_cache_get_default_wisdom_path(char *path, size_t max_len)
71
+ {
72
+ const char *home = getenv("HOME");
73
+ if(home) {
74
+ snprintf(path, max_len, "%s/.pypivtools_fftw_wisdom", home);
75
+ } else {
76
+ snprintf(path, max_len, "/tmp/.pypivtools_fftw_wisdom");
77
+ }
78
+ }
@@ -0,0 +1,26 @@
1
+ #ifndef XCORR_CACHE_H
2
+ #define XCORR_CACHE_H
3
+
4
+ #include <fftw3.h>
5
+ #include <stddef.h>
6
+
7
+ /******************************************************************************
8
+ * FFTW Wisdom Cache
9
+ *
10
+ * Provides persistent caching of FFTW optimization data (wisdom) to avoid
11
+ * expensive plan creation on repeated runs.
12
+ *****************************************************************************/
13
+
14
+ /* Initialize the wisdom cache system */
15
+ void xcorr_cache_init(const char *wisdom_file);
16
+
17
+ /* Save current wisdom to disk */
18
+ void xcorr_cache_save_wisdom(const char *wisdom_file);
19
+
20
+ /* Cleanup wisdom cache */
21
+ void xcorr_cache_cleanup(void);
22
+
23
+ /* Get default wisdom file path */
24
+ void xcorr_cache_get_default_wisdom_path(char *path, size_t max_len);
25
+
26
+ #endif
@@ -0,0 +1,69 @@
1
+ import ctypes
2
+ import os
3
+
4
+ import numpy as np
5
+
6
+ KERNEL_LANCZOS = 0
7
+ KERNEL_GAUSSIAN = 1
8
+
9
+
10
+ class Interp2Custom:
11
+ def __init__(self, lib_path=None):
12
+ if lib_path is None:
13
+ # Use platform-appropriate library extension
14
+ lib_extension = ".dll" if os.name == "nt" else ".so"
15
+ lib_path = os.path.join(
16
+ os.path.dirname(__file__), "../lib", f"libinterp2custom{lib_extension}"
17
+ )
18
+ self.lib = ctypes.CDLL(os.path.abspath(lib_path))
19
+
20
+ # Set C function signatures
21
+ self.lib.interp2custom.argtypes = [
22
+ np.ctypeslib.ndpointer(dtype=np.float32, flags="F_CONTIGUOUS"), # y
23
+ np.ctypeslib.ndpointer(dtype=np.uintp, flags="F_CONTIGUOUS"), # N
24
+ np.ctypeslib.ndpointer(dtype=np.float32, flags="F_CONTIGUOUS"), # f_i
25
+ np.ctypeslib.ndpointer(dtype=np.float32, flags="F_CONTIGUOUS"), # f_j
26
+ np.ctypeslib.ndpointer(
27
+ dtype=np.float32, flags="F_CONTIGUOUS"
28
+ ), # yi (output)
29
+ ctypes.c_int, # n_interp
30
+ ]
31
+ self.lib.interp2custom.restype = None
32
+
33
+ def interpolate(
34
+ self, y, i, j, kernel_type="lanczos", kernel_size=4, gaussian_std=0.65
35
+ ):
36
+ # -------------------
37
+ # Validate inputs
38
+ # -------------------
39
+ if y.ndim != 2:
40
+ raise ValueError("y must be a 2D array")
41
+ if y.dtype != np.float32:
42
+ y = y.astype(np.float32)
43
+ if i.shape != j.shape:
44
+ raise ValueError("i and j must have the same shape")
45
+ if i.dtype != np.float32:
46
+ i = i.astype(np.float32)
47
+ if j.dtype != np.float32:
48
+ j = j.astype(np.float32)
49
+
50
+ # Kernel type as int
51
+ if kernel_type.lower() == "lanczos":
52
+ ktype = KERNEL_LANCZOS
53
+ elif kernel_type.lower() == "gaussian":
54
+ ktype = KERNEL_GAUSSIAN
55
+ else:
56
+ raise ValueError(f"Unknown kernel type '{kernel_type}'")
57
+
58
+ self.lib.interp1custom_generatelut(
59
+ ktype, kernel_size, ctypes.c_float(gaussian_std)
60
+ )
61
+
62
+ N = np.asfortranarray([y.shape[0], y.shape[1]], dtype=np.uintp)
63
+ n_interp = i.size
64
+ yi = np.asfortranarray(np.zeros_like(i, dtype=np.float32))
65
+ y = np.asfortranarray(y)
66
+ i = np.asfortranarray(i)
67
+ j = np.asfortranarray(j)
68
+ self.lib.interp2custom(y, N, i, j, yi, n_interp)
69
+ return yi