1 #define _POSIX_C_SOURCE 200809L // for getline()
3 #define lapack_complex_double complex double
4 #define lapack_complex_double_real(z) (creal(z))
5 #define lapack_complex_double_imag(z) (cimag(z))
11 #include "scatsystem.h"
15 #include "symmetries.h"
18 #include "quaternions.h"
20 #include "qpms_error.h"
21 #include "tmatrices.h"
22 #include "qpms_specfunc.h"
23 #include "normalisation.h"
26 #include <gsl/gsl_integration.h>
27 #include <gsl/gsl_errno.h>
31 // These are quite arbitrarily chosen constants for the quadrature in qpms_tmatrix_axialsym_fill()
32 #define TMATRIX_AXIALSYM_INTEGRAL_EPSREL (1e-5)
33 #define TMATRIX_AXIALSYM_INTEGRAL_EPSABS (1e-8)
35 #define HBAR (1.05457162825e-34)
36 #define ELECTRONVOLT (1.602176487e-19)
37 #define SCUFF_OMEGAUNIT (3e14)
39 #define SQ(x) ((x)*(x))
40 #define MAX(x,y) ((x) < (y) ? (y) : (x))
41 #define MIN(x,y) ((x) > (y) ? (y) : (x))
43 qpms_tmatrix_t
*qpms_tmatrix_init(const qpms_vswf_set_spec_t
*bspec
) {
45 QPMS_CRASHING_MALLOC(t
, sizeof(qpms_tmatrix_t
));
48 QPMS_CRASHING_CALLOC(t
->m
, n
*n
, sizeof(complex double));
53 qpms_tmatrix_t
*qpms_tmatrix_init_from_generator(
54 const qpms_vswf_set_spec_t
*bspec
,
55 qpms_tmatrix_generator_t gen
,
56 complex double omega
) {
57 qpms_tmatrix_t
*t
= qpms_tmatrix_init(bspec
);
58 QPMS_ENSURE_SUCCESS(gen
.function(t
, omega
, gen
.params
));
62 qpms_tmatrix_t
*qpms_tmatrix_copy(const qpms_tmatrix_t
*T
) {
63 qpms_tmatrix_t
*t
= qpms_tmatrix_init(T
->spec
);
64 size_t n
= T
->spec
->n
;
65 for(size_t i
= 0; i
< n
*n
; ++i
)
70 void qpms_tmatrix_free(qpms_tmatrix_t
*t
){
71 if(t
&& t
->owns_m
) free(t
->m
);
75 qpms_tmatrix_t
*qpms_tmatrix_apply_symop_inplace(
77 const complex double *M
80 //qpms_tmatrix_t *t = qpms_tmatrix_init(T->spec);
81 const size_t n
= T
->spec
->n
;
82 complex double tmp
[n
][n
];
84 const complex double one
= 1, zero
= 0;
85 cblas_zgemm(CblasRowMajor
,
88 n
, n
, n
, &one
, M
, n
, T
->m
, n
, &zero
, tmp
, n
);
89 // t->m = tmp M* = M T M*
90 cblas_zgemm(CblasRowMajor
,
93 n
, n
, n
, &one
, tmp
, n
, M
, n
, &zero
, T
->m
, n
);
97 qpms_tmatrix_t
*qpms_tmatrix_apply_symop(
98 const qpms_tmatrix_t
*T
,
99 const complex double *M
102 qpms_tmatrix_t
*t
= qpms_tmatrix_init(T
->spec
);
103 const size_t n
= T
->spec
->n
;
104 complex double tmp
[n
][n
];
106 const complex double one
= 1, zero
= 0;
107 cblas_zgemm(CblasRowMajor
,
110 n
, n
, n
, &one
, M
, n
, T
->m
, n
, &zero
, tmp
, n
);
111 // t->m = tmp M* = M T M*
112 cblas_zgemm(CblasRowMajor
,
115 n
, n
, n
, &one
, tmp
, n
, M
, n
, &zero
, t
->m
, n
);
119 qpms_errno_t
qpms_symmetrise_tmdata_irot3arr(
120 complex double *tmdata
, const size_t tmcount
,
121 const qpms_vswf_set_spec_t
*bspec
,
122 const size_t n_symops
, const qpms_irot3_t
*symops
) {
123 const size_t n
= bspec
->n
;
124 qpms_tmatrix_t
*tmcopy
= qpms_tmatrix_init(bspec
);
125 complex double *symop_matrices
= malloc(n
*n
*sizeof(complex double) * n_symops
);
126 if(!symop_matrices
) qpms_pr_error_at_flf(__FILE__
, __LINE__
, __func__
,
128 for (size_t i
= 0; i
< n_symops
; ++i
)
129 qpms_irot3_uvswfi_dense(symop_matrices
+ i
*n
*n
, bspec
, symops
[i
]);
130 complex double tmp
[n
][n
];
131 const complex double one
= 1, zero
= 0;
132 for (size_t tmi
= 0; tmi
< tmcount
; ++tmi
) {
133 // Move the data in tmcopy; we will then write the sum directly into tmdata.
134 memcpy(tmcopy
->m
, tmdata
+n
*n
*tmi
, n
*n
*sizeof(complex double));
135 memset(tmdata
+n
*n
*tmi
, 0, n
*n
*sizeof(complex double));
136 for (size_t i
= 0; i
< n_symops
; ++i
) {
137 const complex double *const M
= symop_matrices
+ i
*n
*n
;
139 cblas_zgemm(CblasRowMajor
, CblasNoTrans
, CblasNoTrans
,
140 n
, n
, n
, &one
, M
, n
, tmcopy
->m
, n
, &zero
, tmp
, n
);
141 // tmdata[...] += tmp M* = M T M*
142 cblas_zgemm(CblasRowMajor
, CblasNoTrans
, CblasConjTrans
,
143 n
, n
, n
, &one
, tmp
, n
, M
, n
, &one
, tmdata
+ tmi
*n
*n
, n
);
145 for (size_t ii
= 0; ii
< n
*n
; ++ii
)
146 tmdata
[n
*n
*tmi
+ii
] /= n_symops
;
148 free(symop_matrices
);
149 qpms_tmatrix_free(tmcopy
);
153 qpms_errno_t
qpms_symmetrise_tmdata_finite_group(
154 complex double *tmdata
, const size_t tmcount
,
155 const qpms_vswf_set_spec_t
*bspec
,
156 const qpms_finite_group_t
*pointgroup
) {
157 if (!(pointgroup
->rep3d
)) qpms_pr_error_at_flf(__FILE__
, __LINE__
, __func__
,
158 "This function requires pointgroup->rep3d to be set correctly!");
159 return qpms_symmetrise_tmdata_irot3arr(tmdata
, tmcount
, bspec
,
160 pointgroup
->order
, pointgroup
->rep3d
);
163 qpms_tmatrix_t
*qpms_tmatrix_symmetrise_irot3arr_inplace(
166 const qpms_irot3_t
*symops
168 if(qpms_symmetrise_tmdata_irot3arr(T
->m
, 1,
169 T
->spec
, n_symops
, symops
) != QPMS_SUCCESS
)
174 qpms_tmatrix_t
*qpms_tmatrix_symmetrise_finite_group_inplace(
176 const qpms_finite_group_t
*pointgroup
178 if(qpms_symmetrise_tmdata_finite_group(T
->m
, 1,
179 T
->spec
, pointgroup
) != QPMS_SUCCESS
)
184 qpms_tmatrix_t
*qpms_tmatrix_symmetrise_involution_inplace(
186 const complex double *M
189 qpms_tmatrix_t
*t
= qpms_tmatrix_apply_symop(T
, M
);
190 const size_t n
= T
->spec
->n
;
191 for(size_t i
= 0; i
< n
*n
; ++i
)
192 T
->m
[i
] = 0.5 * (t
->m
[i
] + T
->m
[i
]);
193 qpms_tmatrix_free(t
);
197 qpms_tmatrix_t
*qpms_tmatrix_symmetrise_involution(
198 const qpms_tmatrix_t
*T
,
199 const complex double *M
202 qpms_tmatrix_t
*t
= qpms_tmatrix_init(T
->spec
);
203 const size_t n
= T
->spec
->n
;
204 complex double tmp
[n
][n
];
206 const complex double one
= 1, zero
= 0;
207 cblas_zgemm(CblasRowMajor
,
210 n
, n
, n
, &one
, M
, n
, T
->m
, n
, &zero
, tmp
, n
);
211 // t->m = tmp M* = M T M*
212 cblas_zgemm(CblasRowMajor
,
215 n
, n
, n
, &one
, tmp
, n
, M
, n
, &zero
, t
->m
, n
);
216 for(size_t i
= 0; i
< n
*n
; ++i
)
217 t
->m
[i
] = 0.5 * (t
->m
[i
] + T
->m
[i
]);
221 qpms_tmatrix_t
*qpms_tmatrix_symmetrise_C_inf(const qpms_tmatrix_t
*T
) {
222 qpms_tmatrix_t
*t
= qpms_tmatrix_copy(T
);
223 return qpms_tmatrix_symmetrise_C_inf_inplace(t
);
226 qpms_tmatrix_t
*qpms_tmatrix_symmetrise_C_inf_inplace(qpms_tmatrix_t
*T
) {
227 const size_t n
= T
->spec
->n
;
228 for (size_t row
= 0; row
< n
; row
++) {
229 qpms_m_t rm
= qpms_uvswfi2m(T
->spec
->ilist
[row
]);
230 for (size_t col
= 0; col
< n
; col
++) {
231 qpms_m_t cm
= qpms_uvswfi2m(T
->spec
->ilist
[col
]);
233 ;// No-op // t->m[n*row + col] = T->m[n*row + col];
235 T
->m
[n
*row
+ col
] = 0;
241 qpms_tmatrix_t
*qpms_tmatrix_symmetrise_C_N(const qpms_tmatrix_t
*T
, int N
) {
242 qpms_tmatrix_t
*t
= qpms_tmatrix_copy(T
);
243 return qpms_tmatrix_symmetrise_C_N_inplace(t
, N
);
246 qpms_tmatrix_t
*qpms_tmatrix_symmetrise_C_N_inplace(qpms_tmatrix_t
*T
, int N
) {
247 const size_t n
= T
->spec
->n
;
248 for (size_t row
= 0; row
< n
; row
++) {
249 qpms_m_t rm
= qpms_uvswfi2m(T
->spec
->ilist
[row
]);
250 for (size_t col
= 0; col
< n
; col
++) {
251 qpms_m_t cm
= qpms_uvswfi2m(T
->spec
->ilist
[col
]);
252 if (((rm
- cm
) % N
) == 0)
253 ; // T->m[n*row + col] = T->m[n*row + col];
255 T
->m
[n
*row
+ col
] = 0;
261 bool qpms_tmatrix_isclose(const qpms_tmatrix_t
*A
, const qpms_tmatrix_t
*B
,
262 const double rtol
, const double atol
)
264 if (!qpms_vswf_set_spec_isidentical(A
->spec
, B
->spec
))
268 const size_t n
= A
->spec
->n
;
269 for (size_t i
= 0; i
< n
*n
; ++i
) {
270 const double tol
= atol
+ rtol
* (cabs(B
->m
[i
]));
271 if ( cabs(B
->m
[i
] - A
->m
[i
]) > tol
)
277 qpms_tmatrix_interpolator_t
*qpms_tmatrix_interpolator_create(const size_t incount
,
278 const double *freqs
, const qpms_tmatrix_t
*ta
, const gsl_interp_type
*iptype
//, const bool copy_bspec
280 if (incount
<= 0) return NULL
;
281 qpms_tmatrix_interpolator_t
*ip
= malloc(sizeof(qpms_tmatrix_interpolator_t
));
284 ip->bspec = qpms_vswf_set_spec_copy(ta[0].spec);
285 ip->owns_bspec = true;
289 ip
->bspec
= ta
[0].spec
;
290 // ip->owns_bspec = false;
292 const size_t n
= ip
->bspec
->n
;
294 // check if all matrices have the same bspec
295 for (size_t i
= 0; i
< incount
; ++i
)
296 QPMS_ENSURE(qpms_vswf_set_spec_isidentical(ip
->bspec
, ta
[i
].spec
),
297 "All T-matrices for interpolation must have the same basis!");
299 QPMS_CRASHING_CALLOC(ip
->splines_real
, n
*n
, sizeof(gsl_spline
*));
300 QPMS_CRASHING_CALLOC(ip
->splines_imag
, n
*n
,sizeof(gsl_spline
*));
301 for (size_t row
= 0; row
< n
; ++row
)
302 for (size_t col
= 0; col
< n
; ++col
) {
303 double y_real
[incount
], y_imag
[incount
];
304 bool n0_real
= false, n0_imag
= false;
305 for (size_t i
= 0; i
< incount
; ++i
) {
306 complex double telem
= ta
[i
].m
[n
* row
+ col
];
307 if ((y_real
[i
] = creal(telem
))) n0_real
= true;
308 if ((y_imag
[i
] = cimag(telem
))) n0_imag
= true;
312 ip
->splines_real
[n
* row
+ col
] = gsl_spline_alloc(iptype
, incount
);
313 QPMS_ENSURE_SUCCESS(gsl_spline_init(s
, freqs
, y_real
, incount
));
315 else ip
->splines_real
[n
* row
+ col
] = NULL
;
318 ip
->splines_imag
[n
* row
+ col
] = gsl_spline_alloc(iptype
, incount
);
319 QPMS_ENSURE_SUCCESS(gsl_spline_init(s
, freqs
, y_imag
, incount
));
321 else ip
->splines_imag
[n
* row
+ col
] = NULL
;
326 void qpms_tmatrix_interpolator_free(qpms_tmatrix_interpolator_t
*ip
) {
328 const size_t n
= ip
->bspec
->n
;
329 for (size_t i
= 0; i
< n
*n
; ++i
) {
330 if (ip
->splines_real
[i
]) gsl_spline_free(ip
->splines_real
[i
]);
331 if (ip
->splines_imag
[i
]) gsl_spline_free(ip
->splines_imag
[i
]);
333 //if (ip->owns_bspec)
334 // qpms_vswf_set_spec_free(ip->bspec);
339 qpms_tmatrix_t
*qpms_tmatrix_interpolator_eval(const qpms_tmatrix_interpolator_t
*ip
, double freq
) {
340 qpms_tmatrix_t
*t
= qpms_tmatrix_init(ip
->bspec
);
341 QPMS_ENSURE_SUCCESS(qpms_tmatrix_interpolator_eval_fill(t
, ip
, freq
));
345 qpms_errno_t
qpms_tmatrix_interpolator_eval_fill(qpms_tmatrix_t
*t
,
346 const qpms_tmatrix_interpolator_t
*ip
, double freq
) {
347 QPMS_ENSURE(qpms_vswf_set_spec_isidentical(t
->spec
, ip
->bspec
), "Tried to fill a T-matrix with an incompatible interpolator!");
348 const size_t n
= ip
->bspec
->n
;
349 for (size_t i
= 0; i
< n
*n
; ++i
){
350 if (ip
->splines_real
[i
]) t
->m
[i
] = gsl_spline_eval(ip
->splines_real
[i
], freq
, NULL
/*does this work?*/);
351 if (ip
->splines_imag
[i
]) t
->m
[i
] += I
* gsl_spline_eval(ip
->splines_imag
[i
], freq
, NULL
/*does this work?*/);
356 qpms_errno_t
qpms_tmatrix_generator_interpolator(qpms_tmatrix_t
*t
,
357 complex double omega
, const void *interp
)
359 return qpms_tmatrix_interpolator_eval_fill(t
,
360 (const qpms_tmatrix_interpolator_t
*) interp
, omega
);
363 double qpms_SU2eV(double e_SU
) {
364 return e_SU
* SCUFF_OMEGAUNIT
/ (ELECTRONVOLT
/ HBAR
);
367 double qpms_SU2SI(double e_SU
) {
368 return e_SU
* SCUFF_OMEGAUNIT
;
371 /// TODO doc and more checks
372 qpms_errno_t
qpms_read_scuff_tmatrix(
373 FILE *f
, ///< file handle
374 const qpms_vswf_set_spec_t
* bs
, ///< VSWF set spec
375 size_t *const n
, ///< Number of successfully loaded t-matrices
376 double* *const freqs
, ///< Frequencies in SI units
377 double* *const freqs_su
, ///< Frequencies in SCUFF units (optional)
378 qpms_tmatrix_t
* *const tmatrices_array
, ///< The resulting T-matrices (optional).
379 complex double* *const tmdata
381 if (!(freqs
&& n
&& tmdata
))
382 qpms_pr_error_at_flf(__FILE__
, __LINE__
, __func__
,
383 "freqs, n, and tmdata are mandatory arguments and must not be NULL.");
384 if(bs
->norm
& (QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE
385 | QPMS_NORMALISATION_SPHARM_REAL
))
386 QPMS_NOT_IMPLEMENTED("Sorry, only standard complex-spherical harmonic based waves are supported right now");
387 int n_alloc
= 128; // First chunk to allocate
389 *freqs
= malloc(n_alloc
* sizeof(double));
390 if (freqs_su
) *freqs_su
= malloc(n_alloc
* sizeof(double));
391 *tmdata
= malloc(sizeof(complex double) * bs
->n
* bs
->n
* n_alloc
);
392 if (!*freqs
|| (!freqs_su
!= !*freqs_su
) || !*tmdata
)
393 qpms_pr_error_at_flf(__FILE__
, __LINE__
, __func__
,
395 size_t linebufsz
= 256;
396 char *linebuf
= malloc(linebufsz
);
398 double lastfreq_su
= NAN
;
399 while((readchars
= getline(&linebuf
, &linebufsz
, f
)) != -1) {
400 if (linebuf
[0] == '#') continue;
401 int Alpha
, LAlpha
, MAlpha
, PAlpha
, Beta
, LBeta
, MBeta
, PBeta
;
402 double currentfreq_su
, tr
, ti
;
403 QPMS_ENSURE(11 == sscanf(linebuf
, "%lf %d %d %d %d %d %d %d %d %lf %lf",
404 ¤tfreq_su
, &Alpha
, &LAlpha
, &MAlpha
, &PAlpha
,
405 &Beta
, &LBeta
, &MBeta
, &PBeta
, &tr
, &ti
),
406 "Malformed T-matrix file");
407 if (currentfreq_su
!= lastfreq_su
) { // New frequency -> new T-matrix
409 lastfreq_su
= currentfreq_su
;
412 QPMS_CRASHING_REALLOC(*freqs
, n_alloc
* sizeof(double));
413 if (freqs_su
) {QPMS_CRASHING_REALLOC(*freqs_su
, n_alloc
* sizeof(double));}
414 QPMS_CRASHING_REALLOC(*tmdata
, sizeof(complex double) * bs
->n
* bs
->n
* n_alloc
);
416 if (freqs_su
) (*freqs_su
)[*n
-1] = currentfreq_su
;
417 (*freqs
)[*n
-1] = qpms_SU2SI(currentfreq_su
);
419 for(size_t i
= 0; i
< bs
->n
* bs
->n
; ++i
)
420 (*tmdata
)[(*n
-1)*bs
->n
*bs
->n
+ i
] = NAN
+ I
*NAN
;
422 qpms_vswf_type_t TAlpha
, TBeta
;
424 case 0: TAlpha
= QPMS_VSWF_MAGNETIC
; break;
425 case 1: TAlpha
= QPMS_VSWF_ELECTRIC
; break;
429 case 0: TBeta
= QPMS_VSWF_MAGNETIC
; break;
430 case 1: TBeta
= QPMS_VSWF_ELECTRIC
; break;
433 qpms_uvswfi_t srcui
= qpms_tmn2uvswfi(TAlpha
, MAlpha
, LAlpha
),
434 destui
= qpms_tmn2uvswfi(TBeta
, MBeta
, LBeta
);
435 ssize_t srci
= qpms_vswf_set_spec_find_uvswfi(bs
, srcui
),
436 desti
= qpms_vswf_set_spec_find_uvswfi(bs
, destui
);
437 if (srci
== -1 || desti
== -1)
438 /* This element has not been requested in bs->ilist. */
441 (*tmdata
)[(*n
-1)*bs
->n
*bs
->n
+ desti
*bs
->n
+ srci
] = (tr
+ I
*ti
)
442 * qpms_normalisation_factor_uvswfi(bs
->norm
, srcui
)
443 / qpms_normalisation_factor_uvswfi(bs
->norm
, destui
)
444 * qpms_normalisation_factor_uvswfi(QPMS_NORMALISATION_CONVENTION_SCUFF
, destui
)
445 / qpms_normalisation_factor_uvswfi(QPMS_NORMALISATION_CONVENTION_SCUFF
, srcui
);
448 // free some more memory
450 *freqs
= realloc(*freqs
, n_alloc
* sizeof(double));
451 if (freqs_su
) *freqs_su
= realloc(*freqs_su
, n_alloc
* sizeof(double));
452 if (tmatrices_array
) *tmatrices_array
= realloc(*tmatrices_array
, n_alloc
* sizeof(qpms_tmatrix_t
));
453 *tmdata
= realloc(*tmdata
, sizeof(complex double) * bs
->n
* bs
->n
* n_alloc
);
454 if (!*freqs
|| (!freqs_su
!= !*freqs_su
) || !*tmdata
)
455 qpms_pr_error_at_flf(__FILE__
, __LINE__
, __func__
,
456 "realloc() failed.");
457 if (tmatrices_array
) {
458 *tmatrices_array
= malloc(n_alloc
* sizeof(qpms_tmatrix_t
));
459 if (!*tmatrices_array
)
460 qpms_pr_error_at_flf(__FILE__
, __LINE__
, __func__
,
462 for (size_t i
= 0; i
< *n
; ++i
) {
463 (*tmatrices_array
)[i
].spec
= bs
;
464 (*tmatrices_array
)[i
].m
= (*tmdata
) + i
* bs
->n
* bs
->n
;
465 (*tmatrices_array
)[i
].owns_m
= false;
471 bool qpms_load_scuff_tmatrix_crash_on_failure
= true;
473 qpms_errno_t
qpms_load_scuff_tmatrix(
474 const char *path
, ///< file path
475 const qpms_vswf_set_spec_t
* bs
, ///< VSWF set spec
476 size_t *const n
, ///< Number of successfully loaded t-matrices
477 double **const freqs
, ///< Frequencies in SI units
478 double ** const freqs_su
, ///< Frequencies in SCUFF units (optional)
479 qpms_tmatrix_t
** const tmatrices_array
, ///< The resulting T-matrices (optional).
480 complex double ** const tmdata
482 FILE *f
= fopen(path
, "r");
484 if (qpms_load_scuff_tmatrix_crash_on_failure
) {
485 QPMS_PR_ERROR("Could not open T-matrix file %s", path
);
488 qpms_errno_t retval
=
489 qpms_read_scuff_tmatrix(f
, bs
, n
, freqs
, freqs_su
, tmatrices_array
, tmdata
);
491 for (size_t i
= 0; i
< *n
* bs
->n
* bs
->n
; ++i
)
492 if(isnan(creal((*tmdata
)[i
])) || isnan(cimag((*tmdata
)[i
]))) {
493 QPMS_WARN("Encountered NAN in a loaded T-matrix");
494 retval
|= QPMS_NAN_ENCOUNTERED
;
498 if(fclose(f
)) { QPMS_PR_ERROR("Could not close the T-matrix file %s "
499 "(well, that's weird, since it's read only).", path
); }
504 complex double *qpms_mie_coefficients_reflection(
505 complex double *target
, ///< Target array of length bspec->n. If NULL, a new one will be allocated.
506 const qpms_vswf_set_spec_t
*bspec
, ///< Defines which of the coefficients are calculated.
507 double a
, ///< Radius of the sphere.
508 complex double k_i
, ///< Wave number of the internal material of the sphere.
509 complex double k_e
, ///< Wave number of the surrounding medium.
510 complex double mu_i
, ///< Relative permeability of the sphere material.
511 complex double mu_e
, ///< Relative permeability of the surrounding medium.
514 // TODO J_ext, J_scat?
517 * This implementation pretty much copies mie_coefficients() from qpms_p.py, so
518 * any bugs there should affect this function as well and perhaps vice versa.
520 * Compared to mie_coefficients() from qpms_p.py, this has opposite sign.
522 QPMS_ENSURE(J_ext
!= J_scat
, "J_ext and J_scat must not be equal. Perhaps you want J_ext = 1 and J_scat = 3 anyways.");
523 if (!target
) QPMS_CRASHING_MALLOC(target
, bspec
->n
* sizeof(complex double));
524 qpms_l_t lMax
= bspec
->lMax
;
525 complex double x_i
= k_i
* a
;
526 complex double x_e
= k_e
* a
;
527 // complex double m = k_i / k_e;
528 complex double eta_inv_i
= k_i
/ mu_i
;
529 complex double eta_inv_e
= k_e
/ mu_e
;
531 complex double zi
[lMax
+ 2];
532 complex double ze
[lMax
+ 2];
533 complex double zs
[lMax
+ 2];
534 complex double RH
[lMax
+ 1] /* MAGNETIC */, RV
[lMax
+1] /* ELECTRIC */;
535 QPMS_ENSURE_SUCCESS(qpms_sph_bessel_fill(QPMS_BESSEL_REGULAR
, lMax
+1, x_i
, zi
));
536 QPMS_ENSURE_SUCCESS(qpms_sph_bessel_fill(J_ext
, lMax
+1, x_e
, ze
));
537 QPMS_ENSURE_SUCCESS(qpms_sph_bessel_fill(J_scat
, lMax
+1, x_e
, zs
));
538 for (qpms_l_t l
= 0; l
<= lMax
; ++l
) {
539 // Bessel function derivatives as in DLMF 10.51.2
540 complex double dzi
= -zi
[l
+1] + l
/x_i
*zi
[l
];
541 complex double dze
= -ze
[l
+1] + l
/x_e
*ze
[l
];
542 complex double dzs
= -zs
[l
+1] + l
/x_e
*zs
[l
];
543 complex double fi
= zi
[l
]/x_i
+dzi
;
544 complex double fs
= zs
[l
]/x_e
+dzs
;
545 complex double fe
= ze
[l
]/x_e
+dze
;
546 RV
[l
] = (-eta_inv_i
* fe
* zi
[l
] + eta_inv_e
* ze
[l
] * fi
)/(-eta_inv_e
* fi
* zs
[l
] + eta_inv_i
* zi
[l
] * fs
);
547 RH
[l
] = (-eta_inv_e
* fe
* zi
[l
] + eta_inv_i
* ze
[l
] * fi
)/(-eta_inv_i
* fi
* zs
[l
] + eta_inv_e
* zi
[l
] * fs
);
550 for (size_t i
= 0; i
< bspec
->n
; ++i
) {
551 qpms_l_t l
; qpms_m_t m
; qpms_vswf_type_t t
;
552 QPMS_ENSURE_SUCCESS(qpms_uvswfi2tmn(bspec
->ilist
[i
], &t
, &m
, &l
));
555 case QPMS_VSWF_ELECTRIC
:
558 case QPMS_VSWF_MAGNETIC
:
567 /// Replaces the contents of an existing T-matrix with that of a spherical nanoparticle calculated using the Lorentz-mie theory.
568 qpms_errno_t
qpms_tmatrix_spherical_fill(qpms_tmatrix_t
*t
, ///< T-matrix whose contents are to be replaced. Not NULL.
569 double a
, ///< Radius of the sphere.
570 complex double k_i
, ///< Wave number of the internal material of the sphere.
571 complex double k_e
, ///< Wave number of the surrounding medium.
572 complex double mu_i
, ///< Relative permeability of the sphere material.
573 complex double mu_e
///< Relative permeability of the surrounding medium.
575 complex double *miecoeffs
= qpms_mie_coefficients_reflection(NULL
, t
->spec
, a
, k_i
, k_e
, mu_i
, mu_e
,
576 QPMS_BESSEL_REGULAR
, QPMS_HANKEL_PLUS
);
577 memset(t
->m
, 0, SQ(t
->spec
->n
)*sizeof(complex double));
578 for(size_t i
= 0; i
< t
->spec
->n
; ++i
)
579 t
->m
[t
->spec
->n
*i
+ i
] = miecoeffs
[i
];
584 qpms_errno_t
qpms_tmatrix_generator_sphere(qpms_tmatrix_t
*t
,
585 complex double omega
, const void *params
)
587 const qpms_tmatrix_generator_sphere_param_t
*p
= params
;
588 qpms_epsmu_t out
= p
->outside
.function(omega
, p
->outside
.params
);
589 qpms_epsmu_t in
= p
->inside
.function(omega
, p
->inside
.params
);
590 return qpms_tmatrix_spherical_fill(t
,
592 qpms_wavenumber(omega
, in
),
593 qpms_wavenumber(omega
, out
),
597 /// Convenience function to calculate T-matrix of a non-magnetic spherical \
598 particle
using the permittivity values
, replacing existing T
-matrix data
.
599 qpms_errno_t
qpms_tmatrix_spherical_mu0_fill(
600 qpms_tmatrix_t
*t
, ///< T-matrix whose contents are to be replaced. Not NULL.
601 double a
, ///< Radius of the sphere.
602 double omega
, ///< Angular frequency.
603 complex double epsilon_fg
, ///< Permittivity of the sphere.
604 complex double epsilon_bg
///< Permittivity of the background medium.
607 complex double k_i
= csqrt(epsilon_fg
) * omega
/ SPEED_OF_LIGHT
;
608 complex double k_e
= csqrt(epsilon_bg
) * omega
/ SPEED_OF_LIGHT
;
609 return qpms_tmatrix_spherical_fill(t
, a
, k_i
, k_e
, 1, 1);
612 complex double *qpms_apply_tmatrix(
613 complex double *f
, const complex double *a
, const qpms_tmatrix_t
*T
)
615 const size_t n
= T
->spec
->n
;
617 QPMS_CRASHING_CALLOC(f
, n
, sizeof(complex double));
618 const complex double one
= 1;
619 const complex double zero
= 0;
620 cblas_zgemv(CblasRowMajor
, CblasNoTrans
, n
, n
, &one
, T
->m
, n
, a
, 1, &zero
, f
, 1);
624 qpms_arc_function_retval_t
qpms_arc_sphere(double theta
, const void *R
) {
625 qpms_arc_function_retval_t retval
= {*(const double*)R
, 0};
629 qpms_arc_function_retval_t
qpms_arc_cylinder(double theta
, const void *param
) {
630 const qpms_arc_cylinder_params_t
*p
= param
;
631 double thresh
= atan(2 * p
->R
/ p
->h
);
632 QPMS_ENSURE(theta
>= 0 && theta
<= M_PI
,
633 "theta = %g, but it must lie in interval [0, M_PI]", theta
);
634 qpms_arc_function_retval_t res
;
635 if (theta
< thresh
) {
637 res
.r
= 0.5 * p
->h
/ cos(theta
);
639 } else if (theta
<= M_PI
- thresh
) {
641 res
.r
= p
->R
/ cos(theta
- M_PI_2
);
642 res
.beta
= -theta
+ M_PI_2
;
645 res
.r
= 0.5 * p
->h
/ cos(theta
- M_PI
);
646 res
.beta
= -theta
+ M_PI
;
652 struct tmatrix_axialsym_integral_param_t
{
653 const qpms_vswf_set_spec_t
*bspec
;
655 qpms_m_t m
; // m_in = -m
656 qpms_vswf_type_t t
, t_in
;
657 qpms_arc_function_t f
;
658 complex double k_in
, k
, z_in
, z
;
659 bool realpart
; // Otherwise imaginary part
660 qpms_bessel_t btype
; // For Q QPMS_HANKEL_PLUS, for R QPMS_BESSEL_REGULAR
663 static double tmatrix_axialsym_integrand(double theta
, void *param
) {
664 // Pretty inefficient; either real or imaginary part is thrown away etc.
665 struct tmatrix_axialsym_integral_param_t
*p
= param
;
666 qpms_arc_function_retval_t rb
= p
->f
.function(theta
, p
->f
.params
);
667 csph_t kr
= {rb
.r
* p
->k
, theta
, 0},
668 kr_in
= {rb
.r
* p
->k_in
, theta
, 0};
670 csphvec_t y_el
= qpms_vswf_single_el_csph(p
->m
, p
->l
, kr
, p
->btype
, p
->bspec
->norm
);
671 csphvec_t y_mg
= qpms_vswf_single_mg_csph(p
->m
, p
->l
, kr
, p
->btype
, p
->bspec
->norm
);
672 csphvec_t v_in_el
= qpms_vswf_single_el_csph(-p
->m
, p
->l_in
, kr_in
, QPMS_BESSEL_REGULAR
, p
->bspec
->norm
);
673 csphvec_t v_in_mg
= qpms_vswf_single_mg_csph(-p
->m
, p
->l_in
, kr_in
, QPMS_BESSEL_REGULAR
, p
->bspec
->norm
);
674 csphvec_t y1
, y2
, v_in1
, v_in2
;
676 case QPMS_VSWF_ELECTRIC
: y1
= y_el
; y2
= y_mg
; break;
677 case QPMS_VSWF_MAGNETIC
: y1
= y_mg
; y2
= y_el
; break;
681 case QPMS_VSWF_ELECTRIC
: v_in1
= v_in_mg
; v_in2
= v_in_el
; break;
682 case QPMS_VSWF_MAGNETIC
: v_in1
= v_in_el
; v_in2
= v_in_mg
; break;
685 // Normal vector components
686 double nrc
= cos(rb
.beta
), nthetac
= sin(rb
.beta
);
687 // First triple product
688 complex double tp1
= nrc
* (y1
.thetac
* v_in1
.phic
- y1
.phic
* v_in1
.thetac
)
689 + nthetac
* (y1
.phic
* v_in1
.rc
- y1
.rc
* v_in1
.phic
);
690 // Second thiple product
691 complex double tp2
= nrc
* (y2
.thetac
* v_in2
.phic
- y2
.phic
* v_in2
.thetac
)
692 + nthetac
* (y2
.phic
* v_in2
.rc
- y2
.rc
* v_in2
.phic
);
693 double jac
= SQ(rb
.r
) * sin(theta
) / nrc
; // Jacobian
694 complex double res
= jac
* (p
->z
/p
->z_in
* tp1
+ tp2
);
695 return p
->realpart
? creal(res
) : cimag(res
);
700 long qpms_axsym_tmatrix_integration_nthreads_default
= 4;
701 long qpms_axsym_tmatrix_integration_nthreads_override
= 0;
703 void qpms_axsym_tmatrix_integration_set_nthreads(long n
) {
704 qpms_axsym_tmatrix_integration_nthreads_override
= n
;
707 struct qpms_tmatrix_axialsym_fill_integration_thread_arg
{
708 complex double *Q
, *R
;
709 const qpms_vswf_set_spec_t
*bspecQR
;
710 double epsrel
, epsabs
;
712 qpms_arc_function_t f
;
713 const struct tmatrix_axialsym_integral_param_t
*p_template
; // Thread must copy this and set its own realpart and btype fields.
715 pthread_mutex_t
*i2start_mutex
;
718 // This wrapper retries to calculate the T-matrix integral and if it fails, reduces torelance.
719 static inline int axint_wrapper(const gsl_function
*f
, double epsabs
, double epsrel
,
720 size_t intlimit
, gsl_integration_workspace
*w
, double *result
, double *abserr
) {
721 static int tol_exceeded
= 0;
724 for (errfac
= 1; epsabs
*errfac
< 0.01 && epsrel
*errfac
< 0.01; errfac
*= 8) {
725 retval
= gsl_integration_qag(f
, 0, M_PI
, epsabs
*errfac
, epsrel
*errfac
,
726 intlimit
, 2, w
, result
, abserr
);
727 if(QPMS_LIKELY(!retval
)) {
728 if(QPMS_LIKELY(errfac
== 1))
731 QPMS_DEBUG(QPMS_DBGMSG_INTEGRATION
,
732 "T-matrix quadrature succeeded only after %g-fold"
733 "tolerance reduction."); // TODO more details here?
737 else if(retval
== GSL_EROUND
) {
739 QPMS_WARN("T-matrix quadrature failed; retrying with worse tolerances."
740 " (this warning will be shown only once).");
744 else QPMS_ENSURE_SUCCESS(retval
);
746 QPMS_PR_ERROR("T-matrix quadrature failed even after harsh tolerance reduction.");
750 static void * qpms_tmatrix_axialsym_fill_integration_thread(void *arg
) {
751 const struct qpms_tmatrix_axialsym_fill_integration_thread_arg
*a
= arg
;
752 struct tmatrix_axialsym_integral_param_t p
= *a
->p_template
;
753 const gsl_function f
= {tmatrix_axialsym_integrand
, (void *) &p
};
754 gsl_integration_workspace
*w
= gsl_integration_workspace_alloc(a
->intlimit
);
757 QPMS_ENSURE_SUCCESS(pthread_mutex_lock(a
->i2start_mutex
));
758 if(*(a
->i2start_ptr
) >= a
->bspecQR
->n
) {// Everything already done, leave thread
759 QPMS_ENSURE_SUCCESS(pthread_mutex_unlock(a
->i2start_mutex
));
762 const size_t i2
= *(a
->i2start_ptr
);
764 QPMS_ENSURE_SUCCESS(pthread_mutex_unlock(a
->i2start_mutex
));
765 for(size_t i1
= 0; i1
< a
->bspecQR
->n
; ++i1
) {
766 // NOTE that the Q', R' matrices are !TRANSPOSED! here because of zgetrs
767 const size_t iQR
= i1
+ i2
* a
->bspecQR
->n
;
770 qpms_vswf_type_t t1
, t2
;
771 const qpms_uvswfi_t u1
= a
->bspecQR
->ilist
[i1
], u2
= a
->bspecQR
->ilist
[i2
];
772 QPMS_ENSURE_SUCCESS(qpms_uvswfi2tmn(u1
, &t1
, &m1
, &l1
));
773 QPMS_ENSURE_SUCCESS(qpms_uvswfi2tmn(u2
, &t2
, &m2
, &l2
));
779 p
.l
= l1
; p
.l_in
= l2
;
780 p
.t
= t1
; p
.t_in
= t2
;
783 double abserr
; // gsl_integration_qag() needs this; thrown away for now
785 p
.btype
= QPMS_BESSEL_REGULAR
;
787 axint_wrapper(&f
, a
->epsabs
, a
->epsrel
, a
->intlimit
, w
, &result
, &abserr
);
792 axint_wrapper(&f
, a
->epsabs
, a
->epsrel
, a
->intlimit
, w
, &result
, &abserr
);
793 a
->R
[iQR
] += I
*result
;
796 p
.btype
= QPMS_HANKEL_PLUS
;
798 axint_wrapper(&f
, a
->epsabs
, a
->epsrel
, a
->intlimit
, w
, &result
, &abserr
);
803 axint_wrapper(&f
, a
->epsabs
, a
->epsrel
, a
->intlimit
, w
, &result
, &abserr
);
804 a
->Q
[iQR
] += I
*result
;
808 gsl_integration_workspace_free(w
);
811 qpms_errno_t
qpms_tmatrix_axialsym_fill(
812 qpms_tmatrix_t
*t
, complex double omega
, qpms_epsmu_t outside
,
813 qpms_epsmu_t inside
,qpms_arc_function_t shape
, qpms_l_t lMaxQR
)
815 QPMS_UNTESTED
; // TODO also check whether this is valid for all norms.
816 const qpms_vswf_set_spec_t
*bspec
= t
->spec
;
817 struct tmatrix_axialsym_integral_param_t p
;
818 p
.k
= qpms_wavenumber(omega
, outside
);
819 p
.k_in
= qpms_wavenumber(omega
, inside
);
820 p
.z
= qpms_waveimpedance(outside
);
821 p
.z_in
= qpms_waveimpedance(inside
);
825 if (lMaxQR
< bspec
->lMax
) lMaxQR
= bspec
->lMax
;
826 qpms_vswf_set_spec_t
*bspecQR
= qpms_vswf_set_spec_from_lMax(lMaxQR
, bspec
->norm
);
827 size_t *reindex
= qpms_vswf_set_reindex(bspec
, bspecQR
);
829 // Q' and R' matrices
830 complex double *Q
, *R
;
831 QPMS_CRASHING_MALLOC(Q
, sizeof(complex double) * SQ(bspecQR
->n
));
832 QPMS_CRASHING_MALLOC(R
, sizeof(complex double) * SQ(bspecQR
->n
));
834 // Not sure what the size should be, but this should be more than enough.
835 const size_t intlimit
= 1024;
836 const double epsrel
= TMATRIX_AXIALSYM_INTEGRAL_EPSREL
;
837 const double epsabs
= TMATRIX_AXIALSYM_INTEGRAL_EPSABS
;
839 //==== multi-threaded integration begin
841 pthread_mutex_t i2start_mutex
;
842 QPMS_ENSURE_SUCCESS(pthread_mutex_init(&i2start_mutex
, NULL
));
843 const struct qpms_tmatrix_axialsym_fill_integration_thread_arg
844 arg
= {Q
, R
, bspecQR
, epsrel
, epsabs
, intlimit
, shape
, &p
, &i2start
, &i2start_mutex
};
846 // FIXME THIS IS NOT PORTABLE (_SC_NPROCESSORS_ONLN is not a standard POSIX thing)
848 if (qpms_axsym_tmatrix_integration_nthreads_override
> 0) {
849 nthreads
= qpms_axsym_tmatrix_integration_nthreads_override
;
850 QPMS_DEBUG(QPMS_DBGMSG_THREADS
, "Using overriding value of %ld thread(s).",
853 nthreads
= get_ncpus();
855 QPMS_DEBUG(QPMS_DBGMSG_THREADS
, "_SC_NPROCESSORS_ONLN returned %ld, using %ld thread(s) instead.",
856 nthreads
, qpms_axsym_tmatrix_integration_nthreads_default
);
857 nthreads
= qpms_axsym_tmatrix_integration_nthreads_default
;
859 QPMS_DEBUG(QPMS_DBGMSG_THREADS
, "_SC_NRPOCESSORS_ONLN returned %ld.", nthreads
);
862 nthreads
= MIN(nthreads
, bspec
->n
); // don't make redundant threads
863 pthread_t thread_ids
[nthreads
];
864 for(long thi
= 0; thi
< nthreads
; ++thi
)
865 QPMS_ENSURE_SUCCESS(pthread_create(thread_ids
+ thi
, NULL
,
866 qpms_tmatrix_axialsym_fill_integration_thread
,
868 for(long thi
= 0; thi
< nthreads
; ++thi
) {
870 QPMS_ENSURE_SUCCESS(pthread_join(thread_ids
[thi
], &retval
));
872 QPMS_ENSURE_SUCCESS(pthread_mutex_destroy(&i2start_mutex
));
873 //===== multi-threaded integration end
876 // Compute T-matrix; maybe it would be better solved with some sparse matrix mechanism,
878 const size_t n
= bspecQR
->n
;
880 QPMS_CRASHING_MALLOC(pivot
, sizeof(lapack_int
) * n
);
881 QPMS_ENSURE_SUCCESS(LAPACKE_zgetrf(LAPACK_ROW_MAJOR
, n
, n
, Q
, n
, pivot
));
882 // Solve Q'^T X = R'^T where X will be -T^T
883 // Note that Q'^T, R'^T are already (transposed) in Q, R.
884 QPMS_ENSURE_SUCCESS(LAPACKE_zgetrs(LAPACK_ROW_MAJOR
, 'N', n
, n
/*nrhs*/,
886 // R now contains -T^T.
887 for(size_t i1
= 0; i1
< bspec
->n
; ++i1
)
888 for(size_t i2
= 0; i2
< bspec
->n
; ++i2
) {
889 if (reindex
[i1
] == ~(size_t) 0 && reindex
[i2
] == ~(size_t) 0) QPMS_WTF
;
890 const size_t it
= i1
* bspec
->n
+ i2
;
891 const size_t iQR
= reindex
[i1
] + reindex
[i2
] * bspecQR
->n
;
895 free(pivot
); free(R
); free(Q
); free(reindex
);
896 qpms_vswf_set_spec_free(bspecQR
);
900 qpms_errno_t
qpms_tmatrix_axialsym_RQ_transposed_fill(complex double *target
,
901 complex double omega
, qpms_epsmu_t outside
, qpms_epsmu_t inside
,
902 qpms_arc_function_t shape
, qpms_l_t lMaxQR
, qpms_normalisation_t norm
,qpms_bessel_t J
)
904 QPMS_UNTESTED
; // TODO also check whether this is valid for all norms.
905 struct tmatrix_axialsym_integral_param_t p
;
907 qpms_vswf_set_spec_t
*bspecQR
= qpms_vswf_set_spec_from_lMax(lMaxQR
, norm
);
909 p
.k
= qpms_wavenumber(omega
, outside
);
910 p
.k_in
= qpms_wavenumber(omega
, inside
);
911 p
.z
= qpms_waveimpedance(outside
);
912 p
.z_in
= qpms_waveimpedance(inside
);
916 const gsl_function f
= {tmatrix_axialsym_integrand
, (void *) &p
};
919 // Not sure what the size should be, but this should be more than enough.
920 const size_t intlimit
= 1024;
921 const double epsrel
= TMATRIX_AXIALSYM_INTEGRAL_EPSREL
;
922 const double epsabs
= TMATRIX_AXIALSYM_INTEGRAL_EPSABS
;
923 gsl_integration_workspace
*w
= gsl_integration_workspace_alloc(intlimit
);
924 for(size_t i1
= 0; i1
< bspecQR
->n
; ++i1
)
925 for(size_t i2
= 0; i2
< bspecQR
->n
; ++i2
) {
926 // NOTE that the Q', R' matrices are !TRANSPOSED! here because of zgetrs
927 const size_t iQR
= i1
+ i2
* bspecQR
->n
;
930 qpms_vswf_type_t t1
, t2
;
931 const qpms_uvswfi_t u1
= bspecQR
->ilist
[i1
], u2
= bspecQR
->ilist
[i2
];
932 QPMS_ENSURE_SUCCESS(qpms_uvswfi2tmn(u1
, &t1
, &m1
, &l1
));
933 QPMS_ENSURE_SUCCESS(qpms_uvswfi2tmn(u2
, &t2
, &m2
, &l2
));
938 p
.l
= l1
; p
.l_in
= l2
;
939 p
.t
= t1
; p
.t_in
= t2
;
942 double abserr
; // gsl_integration_qag() needs this; thrown away for now
945 QPMS_ENSURE_SUCCESS(gsl_integration_qag(&f
, 0, M_PI
, epsabs
, epsrel
,
946 intlimit
, 2, w
, &result
, &abserr
));
947 target
[iQR
] = result
;
951 QPMS_ENSURE_SUCCESS(gsl_integration_qag(&f
, 0, M_PI
, epsabs
, epsrel
,
952 intlimit
, 2, w
, &result
, &abserr
));
953 target
[iQR
] += I
*result
;
956 gsl_integration_workspace_free(w
);
958 qpms_vswf_set_spec_free(bspecQR
);
962 qpms_errno_t
qpms_tmatrix_generator_axialsym_RQ_transposed_fill(complex double *target
,
963 complex double omega
, const qpms_tmatrix_generator_axialsym_param_t
*param
,
964 qpms_normalisation_t norm
, qpms_bessel_t J
)
966 const qpms_tmatrix_generator_axialsym_param_t
*p
= param
;
967 return qpms_tmatrix_axialsym_RQ_transposed_fill(target
, omega
,
968 p
->outside
.function(omega
, p
->outside
.params
),
969 p
->inside
.function(omega
, p
->inside
.params
),
971 p
->lMax_extend
, norm
, J
);
974 qpms_errno_t
qpms_tmatrix_generator_axialsym(qpms_tmatrix_t
*t
, complex double omega
, const void *param
)
976 const qpms_tmatrix_generator_axialsym_param_t
*p
= param
;
977 return qpms_tmatrix_axialsym_fill(t
, omega
,
978 p
->outside
.function(omega
, p
->outside
.params
),
979 p
->inside
.function(omega
, p
->inside
.params
),
984 qpms_errno_t
qpms_tmatrix_generator_constant(qpms_tmatrix_t
*t
,
985 complex double omega
, const void *tmorig
) {
987 // TODO more thorough check on bspec!
988 const qpms_tmatrix_t
*orig
= tmorig
;
989 const size_t tocopy
= SQ(MIN(t
->spec
->n
, orig
->spec
->n
));
990 memcpy(t
->m
, orig
->m
, tocopy
*sizeof(complex double));
991 memset(t
->m
+tocopy
, 0, (SQ(t
->spec
->n
)-tocopy
)*sizeof(complex double));
995 void qpms_tmatrix_operation_clear(qpms_tmatrix_operation_t
*f
) {
997 case QPMS_TMATRIX_OPERATION_NOOP
:
999 case QPMS_TMATRIX_OPERATION_LRMATRIX
:
1000 if(f
->op
.lrmatrix
.owns_m
)
1001 free(f
->op
.lrmatrix
.m
);
1003 case QPMS_TMATRIX_OPERATION_SCMULZ
:
1004 if(f
->op
.scmulz
.owns_m
)
1005 free(f
->op
.scmulz
.m
);
1007 case QPMS_TMATRIX_OPERATION_IROT3
:
1009 case QPMS_TMATRIX_OPERATION_IROT3ARR
:
1010 if(f
->op
.irot3arr
.owns_ops
)
1011 free(f
->op
.irot3arr
.ops
);
1013 case QPMS_TMATRIX_OPERATION_FINITE_GROUP_SYMMETRISE
: // Group never owned
1015 case QPMS_TMATRIX_OPERATION_COMPOSE_SUM
:
1017 struct qpms_tmatrix_operation_compose_sum
* const o
=
1018 &(f
->op
.compose_sum
);
1020 for(size_t i
= 0; i
< o
->n
; ++i
)
1021 qpms_tmatrix_operation_clear(&(o
->opmem
[i
]));
1027 case QPMS_TMATRIX_OPERATION_COMPOSE_CHAIN
:
1029 struct qpms_tmatrix_operation_compose_chain
* const o
=
1030 &(f
->op
.compose_chain
);
1032 for(size_t i
= 0; i
< o
->n
; ++i
)
1033 if(o
->ops_owned
[i
]) {
1034 // A bit complicated yet imperfect sanity/bound check,
1035 // but this way we at least get rid of the const discard warning.
1036 ptrdiff_t d
= o
->ops
[i
] - o
->opmem
;
1037 QPMS_ENSURE(d
>= 0 && d
< o
->opmem_size
,
1038 "Bound check failed; is there a bug in the"
1039 " construction of compose_chain operation?\n"
1040 "opmem_size = %zu, o->ops[%zu] - o->opmem = %td.",
1041 o
->opmem_size
, i
, d
);
1042 qpms_tmatrix_operation_clear(o
->opmem
+ d
);
1055 void qpms_tmatrix_operation_copy(qpms_tmatrix_operation_t
*dest
, const qpms_tmatrix_operation_t
*src
) {
1056 memcpy(dest
, src
, sizeof(qpms_tmatrix_operation_t
));
1058 case QPMS_TMATRIX_OPERATION_NOOP
:
1060 case QPMS_TMATRIX_OPERATION_LRMATRIX
:
1061 QPMS_CRASHING_MALLOCPY(dest
->op
.lrmatrix
.m
, src
->op
.lrmatrix
.m
,
1062 sizeof(complex double) * src
->op
.lrmatrix
.m_size
);
1063 dest
->op
.lrmatrix
.owns_m
= true;
1065 case QPMS_TMATRIX_OPERATION_SCMULZ
:
1066 QPMS_CRASHING_MALLOCPY(dest
->op
.scmulz
.m
, src
->op
.scmulz
.m
,
1067 sizeof(complex double) * src
->op
.scmulz
.m_size
);
1068 dest
->op
.scmulz
.owns_m
= true;
1070 case QPMS_TMATRIX_OPERATION_IROT3
:
1072 case QPMS_TMATRIX_OPERATION_IROT3ARR
:
1073 QPMS_CRASHING_MALLOCPY(dest
->op
.irot3arr
.ops
, src
->op
.irot3arr
.ops
,
1074 src
->op
.irot3arr
.n
* sizeof(qpms_irot3_t
));
1075 dest
->op
.irot3arr
.owns_ops
= true;
1077 case QPMS_TMATRIX_OPERATION_FINITE_GROUP_SYMMETRISE
: // Group never owned
1079 case QPMS_TMATRIX_OPERATION_COMPOSE_SUM
:
1081 struct qpms_tmatrix_operation_compose_sum
* const o
=
1082 &(dest
->op
.compose_sum
);
1083 QPMS_CRASHING_MALLOC(o
->ops
, o
->n
* sizeof(*(o
->ops
)));
1084 QPMS_CRASHING_MALLOC(o
->opmem
, o
->n
* sizeof(*(o
->opmem
)));
1085 for(size_t i
= 0; i
< o
->n
; ++i
) {
1086 qpms_tmatrix_operation_copy(o
->opmem
+ i
, src
->op
.compose_sum
.ops
[i
]);
1087 o
->ops
[i
] = o
->opmem
+ i
;
1091 case QPMS_TMATRIX_OPERATION_COMPOSE_CHAIN
:
1093 struct qpms_tmatrix_operation_compose_chain
* const o
=
1094 &(dest
->op
.compose_chain
);
1095 QPMS_CRASHING_MALLOC(o
->ops
, o
->n
* sizeof(*(o
->ops
)));
1096 QPMS_CRASHING_MALLOC(o
->ops_owned
, o
->n
* sizeof(*(o
->ops_owned
)));
1097 QPMS_CRASHING_MALLOC(o
->opmem
, o
->n
* sizeof(*(o
->opmem
)));
1098 for(size_t i
= 0; i
< o
->n
; ++i
) {
1099 qpms_tmatrix_operation_copy(o
->opmem
+ i
, src
->op
.compose_chain
.ops
[i
]);
1100 o
->ops
[i
] = o
->opmem
+ i
;
1101 o
->ops_owned
[i
] = true;
1108 dest
->typ
= src
->typ
;
1111 void qpms_tmatrix_operation_compose_chain_init(qpms_tmatrix_operation_t
*dest
,
1112 size_t nops
, size_t opmem_size
) {
1114 QPMS_WARN("Tried to create a composed (chain) operation of zero size;"
1115 "setting to no-op instead.");
1116 *dest
= qpms_tmatrix_operation_noop
;
1118 if (nops
< opmem_size
)
1119 QPMS_WARN("Allocating buffer for %zu operations, in a chained operation of"
1120 " only %zu elemens, that does not seem to make sense.", opmem_size
, nops
);
1121 dest
->typ
= QPMS_TMATRIX_OPERATION_COMPOSE_CHAIN
;
1122 struct qpms_tmatrix_operation_compose_chain
* const o
=
1123 &(dest
->op
.compose_chain
);
1125 QPMS_CRASHING_MALLOC(o
->ops
, nops
* sizeof(*(o
->ops
)));
1126 if (opmem_size
!= 0) {
1127 QPMS_CRASHING_CALLOC(o
->ops_owned
, o
->n
, sizeof(_Bool
));
1128 QPMS_CRASHING_MALLOC(o
->opmem
, opmem_size
* sizeof(*(o
->opmem
)));
1130 o
->ops_owned
= NULL
;
1133 o
->opmem_size
= opmem_size
;
1136 qpms_tmatrix_t
*qpms_tmatrix_mv(qpms_tmatrix_t
*dest
,
1137 const qpms_tmatrix_t
*orig
) {
1138 QPMS_ENSURE(qpms_vswf_set_spec_isidentical(dest
->spec
, orig
->spec
),
1139 "Basis specifications must be identical!");
1140 memcpy(dest
->m
, orig
->m
, SQ(orig
->spec
->n
)*sizeof(complex double));
1144 qpms_tmatrix_t
*qpms_tmatrix_apply_operation_replace(qpms_tmatrix_t
*dest
,
1145 const qpms_tmatrix_operation_t
*op
, const qpms_tmatrix_t
*orig
) {
1146 //QPMS_ENSURE(qpms_vswf_set_spec_isidentical(dest->spec, orig->spec),
1147 // "Basis specifications must be identical!");
1148 // TODO should I check also for dest->owns_m?
1149 // FIXME this is highly inoptimal; the hierarchy should be such
1150 // that _operation() and operation_inplace() call this, and not the
1152 qpms_tmatrix_mv(dest
, orig
);
1153 return qpms_tmatrix_apply_operation_inplace(op
, dest
);
1156 qpms_tmatrix_t
*qpms_tmatrix_apply_operation(
1157 const qpms_tmatrix_operation_t
*f
, const qpms_tmatrix_t
*orig
) {
1158 // Certain operations could be optimized, but the effect would be marginal.
1159 qpms_tmatrix_t
*res
= qpms_tmatrix_copy(orig
);
1160 return qpms_tmatrix_apply_operation_inplace(f
, res
);
1163 static qpms_tmatrix_t
*qtao_compose_sum_inplace(qpms_tmatrix_t
*T
,
1164 const struct qpms_tmatrix_operation_compose_sum
*cs
) {
1165 qpms_tmatrix_t
*tmp_target
= qpms_tmatrix_init(T
->spec
);
1166 qpms_tmatrix_t
*sum
= qpms_tmatrix_init(T
->spec
);
1167 for (size_t i
= 0; i
< cs
->n
; ++i
) {
1168 memcpy(tmp_target
->m
, T
->m
, SQ(T
->spec
->n
) * sizeof(complex double));
1169 QPMS_ENSURE(qpms_tmatrix_apply_operation_inplace(cs
->ops
[i
] , tmp_target
),
1170 "Got NULL pointer from qpms_tmatrix_apply_operation_inplace, hupsis!");
1171 for (size_t j
= 0; j
< SQ(T
->spec
->n
); ++j
)
1172 sum
->m
[j
] += tmp_target
->m
[j
];
1174 for(size_t j
= 0; j
< SQ(T
->spec
->n
); ++j
)
1175 T
->m
[j
] = sum
->m
[j
] * cs
->factor
;
1176 qpms_tmatrix_free(sum
);
1177 qpms_tmatrix_free(tmp_target
);
1181 static qpms_tmatrix_t
*qtao_compose_chain_inplace(qpms_tmatrix_t
*T
,
1182 const struct qpms_tmatrix_operation_compose_chain
*cc
) {
1183 for(size_t i
= 0; i
< cc
->n
; ++i
)
1184 qpms_tmatrix_apply_operation_inplace(cc
->ops
[i
], T
);
1188 static qpms_tmatrix_t
*qtao_scmulz_inplace(qpms_tmatrix_t
*T
,
1189 const struct qpms_tmatrix_operation_scmulz
*s
) {
1190 for(size_t i
= 0; i
< SQ(T
->spec
->n
); ++i
)
1195 qpms_tmatrix_t
*qpms_tmatrix_apply_operation_inplace(
1196 const qpms_tmatrix_operation_t
*f
, qpms_tmatrix_t
*T
) {
1198 case QPMS_TMATRIX_OPERATION_NOOP
:
1200 case QPMS_TMATRIX_OPERATION_LRMATRIX
:
1201 return qpms_tmatrix_apply_symop_inplace(T
, f
->op
.lrmatrix
.m
);
1202 case QPMS_TMATRIX_OPERATION_IROT3
:
1203 return qpms_tmatrix_symmetrise_irot3arr_inplace(T
, 1, &(f
->op
.irot3
));
1204 case QPMS_TMATRIX_OPERATION_FINITE_GROUP_SYMMETRISE
:
1205 return qpms_tmatrix_symmetrise_finite_group_inplace(T
, f
->op
.finite_group
);
1206 case QPMS_TMATRIX_OPERATION_COMPOSE_SUM
:
1207 return qtao_compose_sum_inplace(T
, &(f
->op
.compose_sum
));
1208 case QPMS_TMATRIX_OPERATION_COMPOSE_CHAIN
:
1209 return qtao_compose_chain_inplace(T
, &(f
->op
.compose_chain
));
1210 case QPMS_TMATRIX_OPERATION_SCMULZ
:
1211 return qtao_scmulz_inplace(T
, &(f
->op
.scmulz
));