Merge branch 'abstract_scatsystem'

"Abstract" scattering system for the finite case.


Former-commit-id: 1be9cb6196f660beaca04e8bd998b225cca30e94
This commit is contained in:
Marek Nečada 2020-01-21 15:07:46 +02:00
commit 76171179e7
13 changed files with 1063 additions and 274 deletions

View File

@ -3,6 +3,9 @@ find_package(GSL 2.0 REQUIRED)
find_package(BLAS REQUIRED)
find_package(LAPACK REQUIRED)
# disable an annoying warning that gives false positives probably due to a bug in gcc
# and other not very relevant warnings
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-int-in-bool-context -Wno-comment")
#includes
set (DIRS ${GSL_INCLUDE_DIRS} ${GSLCBLAS_INCLUDE_DIRS})
@ -26,7 +29,7 @@ target_link_libraries (qpms
)
target_include_directories (qpms PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_options(qpms PRIVATE -Wall -Wno-return-type)
target_compile_options(qpms PRIVATE -Wall -Wno-return-type -Wno-unused-variable -Wno-unused-function -Wno-unused-but-set-variable -Wno-unused-label)
target_compile_definitions(qpms PRIVATE LATTICESUMS32 QPMS_VECTORS_NICE_TRANSFORMATIONS
EWALD_AUTO_CUTOFF
)

View File

@ -1,5 +1,5 @@
cimport numpy as np
from .qpms_cdefs cimport qpms_tmatrix_t, cdouble, qpms_tmatrix_interpolator_t
from .qpms_cdefs cimport qpms_tmatrix_t, cdouble, qpms_tmatrix_interpolator_t, qpms_tmatrix_generator_t, qpms_tmatrix_function_t
from .cybspec cimport BaseSpec
cdef class TMatrixInterpolator:
@ -14,6 +14,27 @@ cdef class TMatrixInterpolator:
cdef inline qpms_tmatrix_interpolator_t *rawpointer(self):
return self.interp
cdef class TMatrixGenerator:
cdef qpms_tmatrix_generator_t g
cdef object holder
cdef inline qpms_tmatrix_generator_t raw(self):
return self.g
cdef inline qpms_tmatrix_generator_t *rawpointer(self):
return &(self.g)
cdef class TMatrixFunction:
cdef qpms_tmatrix_function_t f
cdef readonly TMatrixGenerator generator # reference holder
cdef readonly BaseSpec spec # reference holder
cdef inline qpms_tmatrix_function_t raw(self):
return self.f
cdef inline qpms_tmatrix_function_t *rawpointer(self):
return &self.f
cdef class TMatrixGeneratorTransformed:
pass
cdef class CTMatrix: # N.B. there is another type called TMatrix in tmatrices.py!
cdef readonly np.ndarray m # Numpy array holding the matrix data
cdef readonly BaseSpec spec # Here we hold the base spec for the correct reference counting; TODO check if it gets copied

View File

@ -228,13 +228,31 @@ cdef class __AxialSymParams:
qpms_tmatrix_generator_axialsym_RQ_transposed_fill(&arrview[0][0], omega, &self.p, norm, QPMS_BESSEL_REGULAR)
return arr
cdef class TMatrixFunction:
'''
Wrapper over qpms_tmatrix_function_t. The main functional difference between this
and TMatrixGenerator is that this remembers a specific BaseSpec
and its __call__ method takes only one mandatory argument (in addition to self).
'''
def __init__(self, TMatrixGenerator tmg, BaseSpec spec):
self.generator = tmg
self.spec = spec
self.f.gen = self.generator.rawpointer()
self.f.spec = self.spec.rawpointer()
def __call__(self, cdouble omega, fill = None):
cdef CTMatrix tm
if fill is None: # make a new CTMatrix
tm = CTMatrix(self.spec, None)
else: # TODO check whether fill has the same bspec as self?
tm = fill
if self.f.gen.function(tm.rawpointer(), omega, self.f.gen.params) != 0:
raise ValueError("Something went wrong")
else:
return tm
cdef class TMatrixGenerator:
cdef qpms_tmatrix_generator_t g
cdef object holder
cdef qpms_tmatrix_generator_t raw(self):
return self.g
def __init__(self, what):
if isinstance(what, __MieParams):
self.holder = what

View File

@ -24,6 +24,12 @@ typedef struct qpms_epsmu_generator_t {
const void *params;
} qpms_epsmu_generator_t;
/// Convenience function for generating material properties at given frequency.
static inline qpms_epsmu_t qpms_epsmu_generator_eval(
qpms_epsmu_generator_t gen, complex double omega) {
return gen.function(omega, gen.params);
}
/// Constant optical property "generator" for qpms_epsmu_generator_t.
qpms_epsmu_t qpms_epsmu_const_g(complex double omega, ///< Frequency ignored.
const void *epsmu ///< Points to the qpms_epsmu_t to be returned.

View File

@ -12,7 +12,8 @@ from .cyquaternions cimport IRot3, CQuat
from .cybspec cimport BaseSpec
from .cycommon cimport make_c_string
from .cycommon import string_c2py, PointGroupClass
from .cytmatrices cimport CTMatrix
from .cytmatrices cimport CTMatrix, TMatrixFunction, TMatrixGenerator
from .cymaterials cimport EpsMuGenerator
from libc.stdlib cimport malloc, free, calloc
import warnings
@ -256,17 +257,37 @@ cdef class Particle:
Wrapper over the qpms_particle_t structure.
'''
cdef qpms_particle_t p
cdef readonly CTMatrix t # We hold the reference to the T-matrix to ensure correct reference counting
cdef readonly TMatrixFunction f # Reference to ensure correct reference counting
def __cinit__(Particle self, pos, CTMatrix t):
def __cinit__(Particle self, pos, t, bspec = None):
cdef TMatrixGenerator tgen
cdef BaseSpec spec
if(len(pos)>=2 and len(pos) < 4):
self.p.pos.x = pos[0]
self.p.pos.y = pos[1]
self.p.pos.z = pos[2] if len(pos)==3 else 0
else:
raise ValueError("Position argument has to contain 3 or 2 cartesian coordinates")
self.t = t
self.p.tmatrix = self.t.rawpointer()
if isinstance(t, CTMatrix):
tgen = TMatrixGenerator(t)
elif isinstance(t, TMatrixGenerator):
tgen = <TMatrixGenerator>t
else: raise TypeError('t must be either CTMatrix or TMatrixGenerator, was %s' % str(type(t)))
if bspec is not None:
spec = bspec
else:
if isinstance(tgen.holder, CTMatrix):
spec = (<CTMatrix>tgen.holder).spec
else:
raise ValueError("bspec argument must be specified separately for str(type(t))")
self.f = TMatrixFunction(tgen, spec)
self.p.tmg = self.f.rawpointer()
# TODO non-trivial transformations later; if modified, do not forget to update ScatteringSystem constructor
self.p.op = qpms_tmatrix_operation_noop
def __dealloc__(self):
qpms_tmatrix_operation_clear(&self.p.op)
cdef qpms_particle_t *rawpointer(Particle self):
'''Pointer to the qpms_particle_p structure.
@ -310,56 +331,109 @@ cpdef void scatsystem_set_nthreads(long n):
qpms_scatsystem_set_nthreads(n)
return
cdef class ScatteringSystem:
'''
Wrapper over the C qpms_scatsys_t structure.
'''
cdef list basespecs # Here we keep the references to occuring basespecs
cdef list tmgobjs # here we keep the references to occuring TMatrixFunctions (and hence BaseSpecs and TMatrixGenerators)
#cdef list Tmatrices # Here we keep the references to occuring T-matrices
cdef EpsMuGenerator medium_holder # Here we keep the reference to medium generator
cdef qpms_scatsys_t *s
def __cinit__(self, particles, FinitePointGroup sym):
'''TODO doc.
Takes the particles (which have to be a sequence of instances of Particle),
fills them together with their t-matrices to the "proto-qpms_scatsys_t"
orig and calls qpms_scatsys_apply_symmetry
(and then cleans orig)
'''
def check_s(self): # cdef instead?
if self.s == <qpms_scatsys_t *>NULL:
raise ValueError("ScatteringSystem's s-pointer not set. You must not use the default constructor; use the create() method instead")
#TODO is there a way to disable the constructor outside this module?
@staticmethod # We don't have any "standard" constructor for this right now
def create(particles, medium, FinitePointGroup sym, cdouble omega): # TODO tolerances
# These we are going to construct
cdef ScatteringSystem self
cdef _ScatteringSystemAtOmega pyssw
cdef qpms_scatsys_t orig # This should be automatically init'd to 0 (CHECKME)
cdef qpms_ss_pi_t p_count = len(particles)
cdef qpms_ss_tmi_t tm_count = 0
cdef qpms_ss_pi_t pi, p_count = len(particles)
cdef qpms_ss_tmi_t tmi, tm_count = 0
cdef qpms_ss_tmgi_t tmgi, tmg_count = 0
cdef qpms_scatsys_at_omega_t *ssw
cdef qpms_scatsys_t *ss
cdef Particle p
tmgindices = dict()
tmgobjs = list()
tmindices = dict()
tmobjs = list()
self.basespecs=list()
for p in particles: # find and enumerate unique t-matrices
if id(p.t) not in tmindices:
tmindices[id(p.t)] = tm_count
tmobjs.append(p.t)
tmlist = list()
for p in particles: # find and enumerate unique t-matrix generators
if p.p.op.typ != QPMS_TMATRIX_OPERATION_NOOP:
raise NotImplementedError("currently, only no-op T-matrix operations are allowed in ScatteringSystem constructor")
tmg_key = id(p.f)
if tmg_key not in tmgindices:
tmgindices[tmg_key] = tmg_count
tmgobjs.append(p.f) # Save the references on BaseSpecs and TMatrixGenerators (via TMatrixFunctions)
tmg_count += 1
# Following lines have to be adjusted when nontrivial operations allowed:
tm_derived_key = (tmg_key, None) # TODO unique representation of p.p.op instead of None
if tm_derived_key not in tmindices:
tmindices[tm_derived_key] = tm_count
tmlist.append(tm_derived_key)
tm_count += 1
cdef EpsMuGenerator mediumgen = EpsMuGenerator(medium)
orig.medium = mediumgen.g
orig.tmg_count = tmg_count
orig.tm_count = tm_count
orig.p_count = p_count
for tm in tmobjs: # create references to BaseSpec objects
self.basespecs.append(tm.spec)
try:
orig.tm = <qpms_tmatrix_t **>malloc(orig.tm_count * sizeof(orig.tm[0]))
orig.tmg = <qpms_tmatrix_function_t *>malloc(orig.tmg_count * sizeof(orig.tmg[0]))
if not orig.tmg: raise MemoryError
orig.tm = <qpms_ss_derived_tmatrix_t *>malloc(orig.tm_count * sizeof(orig.tm[0]))
if not orig.tm: raise MemoryError
orig.p = <qpms_particle_tid_t *>malloc(orig.p_count * sizeof(orig.p[0]))
if not orig.p: raise MemoryError
for tmgi in range(orig.tmg_count):
orig.tmg[tmgi] = (<TMatrixFunction?>tmgobjs[tmgi]).raw()
for tmi in range(tm_count):
orig.tm[tmi] = (<CTMatrix?>(tmobjs[tmi])).rawpointer()
tm_derived_key = tmlist[tmi]
tmgi = tmgindices[tm_derived_key[0]]
orig.tm[tmi].tmgi = tmgi
orig.tm[tmi].op = qpms_tmatrix_operation_noop # TODO adjust when notrivial operations allowed
for pi in range(p_count):
orig.p[pi].pos = (<Particle?>(particles[pi])).cval().pos
orig.p[pi].tmatrix_id = tmindices[id(particles[pi].t)]
self.s = qpms_scatsys_apply_symmetry(&orig, sym.rawpointer())
p = particles[pi]
tmg_key = id(p.f)
tm_derived_key = (tmg_key, None) # TODO unique representation of p.p.op instead of None
orig.p[pi].pos = p.cval().pos
orig.p[pi].tmatrix_id = tmindices[tm_derived_key]
ssw = qpms_scatsys_apply_symmetry(&orig, sym.rawpointer(), omega, &QPMS_TOLERANCE_DEFAULT)
ss = ssw[0].ss
finally:
free(orig.tmg)
free(orig.tm)
free(orig.p)
self = ScatteringSystem()
self.medium_holder = mediumgen
self.s = ss
self.tmgobjs = tmgobjs
pyssw = _ScatteringSystemAtOmega()
pyssw.ssw = ssw
pyssw.ss_pyref = self
return self, pyssw
def __call__(self, cdouble omega):
self.check_s()
cdef _ScatteringSystemAtOmega pyssw = _ScatteringSystemAtOmega()
pyssw.ssw = qpms_scatsys_at_omega(self.s, omega)
pyssw.ss_pyref = self
return pyssw
def __dealloc__(self):
if(self.s):
qpms_scatsys_free(self.s)
property particles_tmi:
def __get__(self):
self.check_s()
r = list()
cdef qpms_ss_pi_t pi
for pi in range(self.s[0].p_count):
@ -367,20 +441,27 @@ cdef class ScatteringSystem:
return r
property fecv_size:
def __get__(self): return self.s[0].fecv_size
def __get__(self):
self.check_s()
return self.s[0].fecv_size
property saecv_sizes:
def __get__(self):
self.check_s()
return [self.s[0].saecv_sizes[i]
for i in range(self.s[0].sym[0].nirreps)]
property irrep_names:
def __get__(self):
self.check_s()
return [string_c2py(self.s[0].sym[0].irreps[iri].name)
if (self.s[0].sym[0].irreps[iri].name) else None
for iri in range(self.s[0].sym[0].nirreps)]
property nirreps:
def __get__(self): return self.s[0].sym[0].nirreps
def __get__(self):
self.check_s()
return self.s[0].sym[0].nirreps
def pack_vector(self, vect, iri):
self.check_s()
if len(vect) != self.fecv_size:
raise ValueError("Length of a full vector has to be %d, not %d"
% (self.fecv_size, len(vect)))
@ -392,6 +473,7 @@ cdef class ScatteringSystem:
qpms_scatsys_irrep_pack_vector(&target_view[0], &vect_view[0], self.s, iri)
return target_np
def unpack_vector(self, packed, iri):
self.check_s()
if len(packed) != self.saecv_sizes[iri]:
raise ValueError("Length of %d. irrep-packed vector has to be %d, not %d"
% (iri, self.saecv_sizes, len(packed)))
@ -404,6 +486,7 @@ cdef class ScatteringSystem:
self.s, iri, 0)
return target_np
def pack_matrix(self, fullmatrix, iri):
self.check_s()
cdef size_t flen = self.s[0].fecv_size
cdef size_t rlen = self.saecv_sizes[iri]
fullmatrix = np.array(fullmatrix, dtype=complex, copy=False, order='C')
@ -418,6 +501,7 @@ cdef class ScatteringSystem:
self.s, iri)
return target_np
def unpack_matrix(self, packedmatrix, iri):
self.check_s()
cdef size_t flen = self.s[0].fecv_size
cdef size_t rlen = self.saecv_sizes[iri]
packedmatrix = np.array(packedmatrix, dtype=complex, copy=False, order='C')
@ -432,29 +516,8 @@ cdef class ScatteringSystem:
self.s, iri, 0)
return target_np
def modeproblem_matrix_full(self, double k):
cdef size_t flen = self.s[0].fecv_size
cdef np.ndarray[np.complex_t, ndim=2] target = np.empty(
(flen,flen),dtype=complex, order='C')
cdef cdouble[:,::1] target_view = target
qpms_scatsys_build_modeproblem_matrix_full(&target_view[0][0], self.s, k)
return target
def modeproblem_matrix_packed(self, double k, qpms_iri_t iri, version='pR'):
cdef size_t rlen = self.saecv_sizes[iri]
cdef np.ndarray[np.complex_t, ndim=2] target = np.empty(
(rlen,rlen),dtype=complex, order='C')
cdef cdouble[:,::1] target_view = target
if (version == 'R'):
qpms_scatsys_build_modeproblem_matrix_irrep_packed_orbitorderR(&target_view[0][0], self.s, iri, k)
elif (version == 'pR'):
with nogil:
qpms_scatsys_build_modeproblem_matrix_irrep_packed(&target_view[0][0], self.s, iri, k)
else:
qpms_scatsys_build_modeproblem_matrix_irrep_packed_serial(&target_view[0][0], self.s, iri, k)
return target
def translation_matrix_full(self, double k, J = QPMS_HANKEL_PLUS):
self.check_s()
cdef size_t flen = self.s[0].fecv_size
cdef np.ndarray[np.complex_t, ndim=2] target = np.empty(
(flen,flen),dtype=complex, order='C')
@ -463,6 +526,7 @@ cdef class ScatteringSystem:
return target
def translation_matrix_packed(self, double k, qpms_iri_t iri, J = QPMS_HANKEL_PLUS):
self.check_s()
cdef size_t rlen = self.saecv_sizes[iri]
cdef np.ndarray[np.complex_t, ndim=2] target = np.empty(
(rlen,rlen),dtype=complex, order='C')
@ -473,6 +537,7 @@ cdef class ScatteringSystem:
property fullvec_psizes:
def __get__(self):
self.check_s()
cdef np.ndarray[int32_t, ndim=1] ar = np.empty((self.s[0].p_count,), dtype=np.int32)
cdef int32_t[::1] ar_view = ar
for pi in range(self.s[0].p_count):
@ -482,6 +547,7 @@ cdef class ScatteringSystem:
property fullvec_poffsets:
def __get__(self):
self.check_s()
cdef np.ndarray[intptr_t, ndim=1] ar = np.empty((self.s[0].p_count,), dtype=np.intp)
cdef intptr_t[::1] ar_view = ar
cdef intptr_t offset = 0
@ -492,6 +558,7 @@ cdef class ScatteringSystem:
property positions:
def __get__(self):
self.check_s()
cdef np.ndarray[np.double_t, ndim=2] ar = np.empty((self.s[0].p_count, 3), dtype=float)
cdef np.double_t[:,::1] ar_view = ar
for pi in range(self.s[0].p_count):
@ -501,6 +568,7 @@ cdef class ScatteringSystem:
return ar
def planewave_full(self, k_cart, E_cart):
self.check_s()
k_cart = np.array(k_cart)
E_cart = np.array(E_cart)
if k_cart.shape != (3,) or E_cart.shape != (3,):
@ -520,7 +588,27 @@ cdef class ScatteringSystem:
self.s, qpms_incfield_planewave, <void *>&p, 0)
return target_np
cdef class _ScatteringSystemAtOmega:
'''
Wrapper over the C qpms_scatsys_at_omega_t structure
that keeps the T-matrix and background data evaluated
at specific frequency.
'''
cdef qpms_scatsys_at_omega_t *ssw
cdef ScatteringSystem ss_pyref
def check(self): # cdef instead?
if not self.ssw:
raise ValueError("_ScatteringSystemAtOmega's ssw-pointer not set. You must not use the default constructor; ScatteringSystem.create() instead")
self.ss_pyref.check_s()
#TODO is there a way to disable the constructor outside this module?
def __dealloc__(self):
if (self.ssw):
qpms_scatsys_at_omega_free(self.ssw)
def apply_Tmatrices_full(self, a):
self.check()
if len(a) != self.fecv_size:
raise ValueError("Length of a full vector has to be %d, not %d"
% (self.fecv_size, len(a)))
@ -529,31 +617,67 @@ cdef class ScatteringSystem:
cdef np.ndarray[np.complex_t, ndim=1] target_np = np.empty(
(self.fecv_size,), dtype=complex, order='C')
cdef cdouble[::1] target_view = target_np
qpms_scatsys_apply_Tmatrices_full(&target_view[0], &a_view[0], self.s)
qpms_scatsysw_apply_Tmatrices_full(&target_view[0], &a_view[0], self.ssw)
return target_np
cdef qpms_scatsys_t *rawpointer(self):
return self.s
cdef qpms_scatsys_at_omega_t *rawpointer(self):
return self.ssw
def scatter_solver(self, iri=None):
self.check()
return ScatteringMatrix(self, iri)
property fecv_size:
def __get__(self): return self.ss_pyref.fecv_size
property saecv_sizes:
def __get__(self): return self.ss_pyref.saecv_sizes
property irrep_names:
def __get__(self): return self.ss_pyref.irrep_names
property nirreps:
def __get__(self): return self.ss_pyref.nirreps
def modeproblem_matrix_full(self):
self.check()
cdef size_t flen = self.ss_pyref.s[0].fecv_size
cdef np.ndarray[np.complex_t, ndim=2] target = np.empty(
(flen,flen),dtype=complex, order='C')
cdef cdouble[:,::1] target_view = target
qpms_scatsysw_build_modeproblem_matrix_full(&target_view[0][0], self.ssw)
return target
def modeproblem_matrix_packed(self, qpms_iri_t iri, version='pR'):
self.check()
cdef size_t rlen = self.saecv_sizes[iri]
cdef np.ndarray[np.complex_t, ndim=2] target = np.empty(
(rlen,rlen),dtype=complex, order='C')
cdef cdouble[:,::1] target_view = target
if (version == 'R'):
qpms_scatsysw_build_modeproblem_matrix_irrep_packed_orbitorderR(&target_view[0][0], self.ssw, iri)
elif (version == 'pR'):
with nogil:
qpms_scatsysw_build_modeproblem_matrix_irrep_packed(&target_view[0][0], self.ssw, iri)
else:
qpms_scatsysw_build_modeproblem_matrix_irrep_packed_serial(&target_view[0][0], self.ssw, iri)
return target
def scatter_solver(self, double k, iri=None):
return ScatteringMatrix(self, k, iri)
cdef class ScatteringMatrix:
'''
Wrapper over the C qpms_ss_LU structure that keeps the factorised mode problem matrix.
'''
cdef ScatteringSystem ss # Here we keep the reference to the parent scattering system
cdef _ScatteringSystemAtOmega ssw # Here we keep the reference to the parent scattering system
cdef qpms_ss_LU lu
def __cinit__(self, ScatteringSystem ss, double k, iri=None):
self.ss = ss
def __cinit__(self, _ScatteringSystemAtOmega ssw, iri=None):
ssw.check()
self.ssw = ssw
# TODO? pre-allocate the matrix with numpy to make it transparent?
if iri is None:
self.lu = qpms_scatsys_build_modeproblem_matrix_full_LU(
NULL, NULL, ss.rawpointer(), k)
self.lu = qpms_scatsysw_build_modeproblem_matrix_full_LU(
NULL, NULL, ssw.rawpointer())
else:
self.lu = qpms_scatsys_build_modeproblem_matrix_irrep_packed_LU(
NULL, NULL, ss.rawpointer(), iri, k)
self.lu = qpms_scatsysw_build_modeproblem_matrix_irrep_packed_LU(
NULL, NULL, ssw.rawpointer(), iri)
def __dealloc__(self):
qpms_ss_LU_free(self.lu)
@ -566,13 +690,13 @@ cdef class ScatteringMatrix:
cdef size_t vlen
cdef qpms_iri_t iri = -1;
if self.lu.full:
vlen = self.lu.ss[0].fecv_size
vlen = self.lu.ssw[0].ss[0].fecv_size
if len(a_inc) != vlen:
raise ValueError("Length of a full coefficient vector has to be %d, not %d"
% (vlen, len(a_inc)))
else:
iri = self.lu.iri
vlen = self.lu.ss[0].saecv_sizes[iri]
vlen = self.lu.ssw[0].ss[0].saecv_sizes[iri]
if len(a_inc) != vlen:
raise ValueError("Length of a %d. irrep packed coefficient vector has to be %d, not %d"
% (iri, vlen, len(a_inc)))

View File

@ -102,6 +102,7 @@ cdef extern from "qpms_types.h":
QPMS_VSWF_ELECTRIC
QPMS_VSWF_MAGNETIC
QPMS_VSWF_LONGITUDINAL
ctypedef int32_t qpms_ss_tmgi_t
ctypedef int32_t qpms_ss_tmi_t
ctypedef int32_t qpms_ss_pi_t
ctypedef int qpms_gmi_t
@ -150,6 +151,11 @@ cdef extern from "qpms_error.h":
qpms_dbgmsg_flags qpms_dbgmsg_enable(qpms_dbgmsg_flags types)
qpms_dbgmsg_flags qpms_dbgmsg_disable(qpms_dbgmsg_flags types)
cdef extern from "tolerances.h":
struct qpms_tolerance_spec_t:
pass # TODO
const qpms_tolerance_spec_t QPMS_TOLERANCE_DEFAULT
# This is due to the fact that cython apparently cannot nest the unnamed struct/unions in an obvious way
ctypedef union qpms_incfield_planewave_params_k:
@ -476,6 +482,24 @@ cdef extern from "tmatrices.h":
double omega, cdouble epsilon_fg, cdouble epsilon_bg)
qpms_errno_t qpms_tmatrix_generator_axialsym_RQ_transposed_fill(cdouble *target, cdouble omega,
const qpms_tmatrix_generator_axialsym_param_t *param, qpms_normalisation_t norm, qpms_bessel_t J)
struct qpms_tmatrix_function_t:
const qpms_vswf_set_spec_t *spec
const qpms_tmatrix_generator_t *gen
ctypedef enum qpms_tmatrix_operation_kind_t:
QPMS_TMATRIX_OPERATION_NOOP
QPMS_TMATRIX_OPERATION_LRMATRIX
QPMS_TMATRIX_OPERATION_IROT3
QPMS_TMATRIX_OPERATION_IROT3ARR
QPMS_TMATRIX_OPERATION_COMPOSE_SUM
QPMS_TMATRIX_OPERATION_COMPOSE_CHAIN
QPMS_TMATRIX_OPERATION_SCMULZ
QPMS_TMATRIX_OPERATION_FINITE_GROUP_SYMMETRISE
struct qpms_tmatrix_operation_t:
qpms_tmatrix_operation_kind_t typ
pass # TODO add the op union later if needed
const qpms_tmatrix_operation_t qpms_tmatrix_operation_noop
void qpms_tmatrix_operation_clear(qpms_tmatrix_operation_t *)
cdef extern from "pointgroups.h":
bint qpms_pg_is_finite_axial(qpms_pointgroup_class cls)
@ -496,12 +520,19 @@ cdef extern from "scatsystem.h":
void qpms_scatsystem_set_nthreads(long n)
struct qpms_particle_t:
cart3_t pos
const qpms_tmatrix_t *tmatrix
const qpms_tmatrix_function_t *tmg
qpms_tmatrix_operation_t op
struct qpms_particle_tid_t:
cart3_t pos
qpms_ss_tmi_t tmatrix_id
struct qpms_ss_derived_tmatrix_t:
qpms_ss_tmgi_t tmgi
qpms_tmatrix_operation_t op
struct qpms_scatsys_t:
qpms_tmatrix_t **tm
qpms_epsmu_generator_t medium
qpms_tmatrix_function_t *tmg
qpms_ss_tmgi_t tmg_count
qpms_ss_derived_tmatrix_t *tm
qpms_ss_tmi_t tm_count
qpms_particle_tid_t *p
qpms_ss_pi_t p_count
@ -509,10 +540,19 @@ cdef extern from "scatsystem.h":
size_t fecv_size
size_t *saecv_sizes
const qpms_finite_group_t *sym
qpms_scatsys_t *qpms_scatsys_apply_symmetry(const qpms_scatsys_t *orig, const qpms_finite_group_t *sym)
void qpms_scatsys_free(qpms_scatsys_t *s)
qpms_errno_t qpms_scatsys_dump(qpms_scatsys_t *ss, char *path) #NI
qpms_scatsys_t *qpms_scatsys_load(char *path) #NI
struct qpms_scatsys_at_omega_t:
const qpms_scatsys_t *ss
qpms_tmatrix_t **tm,
cdouble omega
qpms_epsmu_t medium
cdouble wavenumber
qpms_scatsys_at_omega_t *qpms_scatsys_apply_symmetry(const qpms_scatsys_t *orig, const qpms_finite_group_t *sym,
cdouble omega, const qpms_tolerance_spec_t *tol)
qpms_scatsys_at_omega_t *qpms_scatsys_at_omega(const qpms_scatsys_t *ss, cdouble omega)
void qpms_scatsys_at_omega_free(qpms_scatsys_at_omega_t *ssw)
cdouble *qpms_scatsys_irrep_pack_matrix(cdouble *target_packed,
const cdouble *orig_full, const qpms_scatsys_t *ss, qpms_iri_t iri)
cdouble *qpms_scatsys_irrep_unpack_matrix(cdouble *target_full,
@ -521,41 +561,43 @@ cdef extern from "scatsystem.h":
const cdouble *orig_full, const qpms_scatsys_t *ss, qpms_iri_t iri)
cdouble *qpms_scatsys_irrep_unpack_vector(cdouble *target_full,
const cdouble *orig_packed, const qpms_scatsys_t *ss, qpms_iri_t iri, bint add)
cdouble *qpms_scatsys_build_modeproblem_matrix_full(cdouble *target,
const qpms_scatsys_t *ss, cdouble k)
cdouble *qpms_scatsysw_build_modeproblem_matrix_full(cdouble *target,
const qpms_scatsys_at_omega_t *ssw)
cdouble *qpms_scatsys_build_translation_matrix_full(cdouble *target,
const qpms_scatsys_t *ss, cdouble k)
cdouble *qpms_scatsys_build_translation_matrix_e_full(cdouble *target,
const qpms_scatsys_t *ss, cdouble k, qpms_bessel_t J)
cdouble *qpms_scatsys_build_modeproblem_matrix_irrep_packed(cdouble *target,
const qpms_scatsys_t *ss, qpms_iri_t iri, cdouble k) nogil
cdouble *qpms_scatsysw_build_modeproblem_matrix_irrep_packed(cdouble *target,
const qpms_scatsys_at_omega_t *ssw, qpms_iri_t iri) nogil
cdouble *qpms_scatsys_build_translation_matrix_e_irrep_packed(cdouble *target,
const qpms_scatsys_t *ss, qpms_iri_t iri, cdouble k, qpms_bessel_t J) nogil
cdouble *qpms_scatsys_build_modeproblem_matrix_irrep_packed_orbitorderR(
cdouble *target, const qpms_scatsys_t *ss, qpms_iri_t iri, cdouble k) nogil
cdouble *qpms_scatsys_build_modeproblem_matrix_irrep_packed_serial(
cdouble *target, const qpms_scatsys_t *ss, qpms_iri_t iri, cdouble k) nogil
cdouble *qpms_scatsysw_build_modeproblem_matrix_irrep_packed_orbitorderR(
cdouble *target, const qpms_scatsys_at_omega_t *ssw, qpms_iri_t iri) nogil
cdouble *qpms_scatsysw_build_modeproblem_matrix_irrep_packed_serial(
cdouble *target, const qpms_scatsys_at_omega_t *ssw, qpms_iri_t iri) nogil
cdouble *qpms_scatsys_incident_field_vector_full(cdouble *target_full,
const qpms_scatsys_t *ss, qpms_incfield_t field_at_point,
const void *args, bint add)
cdouble *qpms_scatsys_apply_Tmatrices_full(cdouble *target_full, const cdouble *inc_full,
const qpms_scatsys_t *ss)
cdouble *qpms_scatsysw_apply_Tmatrices_full(cdouble *target_full, const cdouble *inc_full,
const qpms_scatsys_at_omega_t *ssw)
struct qpms_ss_LU:
const qpms_scatsys_t *ss
const qpms_scatsys_at_omega_t *ssw
bint full
qpms_iri_t iri
cdouble *a
int *ipiv
void qpms_ss_LU_free(qpms_ss_LU lu)
qpms_ss_LU qpms_scatsys_build_modeproblem_matrix_full_LU(cdouble *target,
int *target_piv, const qpms_scatsys_t *ss, cdouble k)
qpms_ss_LU qpms_scatsys_build_modeproblem_matrix_irrep_packed_LU(cdouble *target,
int *target_piv, const qpms_scatsys_t *ss, qpms_iri_t iri, cdouble k)
qpms_ss_LU qpms_scatsys_modeproblem_matrix_full_factorise(cdouble *modeproblem_matrix_full,
int *target_piv, const qpms_scatsys_t *ss)
qpms_ss_LU qpms_scatsys_modeproblem_matrix_irrep_packed_factorise(cdouble *modeproblem_matrix_full,
int *target_piv, const qpms_scatsys_t *ss, qpms_iri_t iri)
qpms_ss_LU qpms_scatsysw_build_modeproblem_matrix_full_LU(cdouble *target,
int *target_piv, const qpms_scatsys_at_omega_t *ssw)
qpms_ss_LU qpms_scatsysw_build_modeproblem_matrix_irrep_packed_LU(cdouble *target,
int *target_piv, const qpms_scatsys_at_omega_t *ssw, qpms_iri_t iri)
qpms_ss_LU qpms_scatsysw_modeproblem_matrix_full_factorise(cdouble *modeproblem_matrix_full,
int *target_piv, const qpms_scatsys_at_omega_t *ssw)
qpms_ss_LU qpms_scatsysw_modeproblem_matrix_irrep_packed_factorise(cdouble *modeproblem_matrix_full,
int *target_piv, const qpms_scatsys_at_omega_t *ssw, qpms_iri_t iri)
cdouble *qpms_scatsys_scatter_solve(cdouble *target_f, const cdouble *a_inc, qpms_ss_LU ludata)
const qpms_vswf_set_spec_t *qpms_ss_bspec_tmi(const qpms_scatsys_t *ss, qpms_ss_tmi_t tmi)
const qpms_vswf_set_spec_t *qpms_ss_bspec_pi(const qpms_scatsys_t *ss, qpms_ss_pi_t pi)
cdef extern from "ewald.h":
struct qpms_csf_result:

View File

@ -56,6 +56,15 @@ qpms_dbgmsg_flags qpms_dbgmsg_enable(qpms_dbgmsg_flags types);
(size_t) (size));\
}
#define QPMS_CRASHING_MALLOCPY(dest, src, size) {\
(dest) = malloc(size);\
if(QPMS_UNLIKELY(!(dest) && (size)))\
qpms_pr_debug_at_flf(__FILE__,__LINE__,__func__,\
"Allocation of %zd bytes for " #dest " failed.",\
(size_t) (size));\
memcpy((dest), (src), (size));\
}
#define QPMS_CRASHING_CALLOC(pointer, nmemb, size) {\
(pointer) = calloc((nmemb), (size));\
if(QPMS_UNLIKELY(!(pointer) && (nmemb) && (size)))\

View File

@ -297,6 +297,9 @@ typedef struct qpms_vswf_set_spec_t {
/// T-matrix index used in qpms_scatsys_t and related structures.
typedef int32_t qpms_ss_tmi_t;
/// T-matrix generator index used in qpms_scatsys_t and related structures.
typedef int32_t qpms_ss_tmgi_t;
/// Particle index used in qpms_scatsys_t and related structures.
typedef int32_t qpms_ss_pi_t;
@ -407,6 +410,7 @@ typedef struct qpms_epsmu_t {
complex double mu; ///< Relative permeability.
} qpms_epsmu_t;
struct qpms_tolerance_spec_t; // See tolerances.h
#define lmcheck(l,m) assert((l) >= 1 && abs(m) <= (l))
#endif // QPMS_TYPES

View File

@ -21,6 +21,7 @@
#include "tmatrices.h"
#include <pthread.h>
#include "kahansum.h"
#include "tolerances.h"
#ifdef QPMS_SCATSYSTEM_USE_OWN_BLAS
#include "qpmsblas.h"
@ -80,7 +81,7 @@ static void add_orbit_type(qpms_scatsys_t *ss, const qpms_ss_orbit_type_t *ot_cu
qpms_ss_orbit_type_t * const ot_new = & (ss->orbit_types[ss->orbit_type_count]);
ot_new->size = ot_current->size;
const qpms_vswf_set_spec_t *bspec = ss->tm[ot_current->tmatrices[0]]->spec;
const qpms_vswf_set_spec_t *bspec = qpms_ss_bspec_tmi(ss, ot_current->tmatrices[0]);
const size_t bspecn = bspec->n;
ot_new->bspecn = bspecn;
@ -143,7 +144,9 @@ static void add_orbit_type(qpms_scatsys_t *ss, const qpms_ss_orbit_type_t *ot_cu
// Almost 200 lines. This whole thing deserves a rewrite!
qpms_scatsys_t *qpms_scatsys_apply_symmetry(const qpms_scatsys_t *orig, const qpms_finite_group_t *sym) {
qpms_scatsys_at_omega_t *qpms_scatsys_apply_symmetry(const qpms_scatsys_t *orig,
const qpms_finite_group_t *sym, complex double omega,
const qpms_tolerance_spec_t *tol) {
// TODO check data sanity
qpms_l_t lMax = 0; // the overall lMax of all base specs.
@ -171,49 +174,82 @@ qpms_scatsys_t *qpms_scatsys_apply_symmetry(const qpms_scatsys_t *orig, const qp
for (qpms_ss_pi_t j = 0; j < i; ++j)
assert(!cart3_isclose(orig->p[i].pos, orig->p[j].pos, 0, QPMS_SCATSYS_LEN_RTOL * lenscale));
// Allocate T-matrix, particle and particle orbit info arrays
qpms_scatsys_t *ss = malloc(sizeof(qpms_scatsys_t));
qpms_scatsys_t *ss;
QPMS_CRASHING_MALLOC(ss, sizeof(qpms_scatsys_t));
ss->lenscale = lenscale;
ss->sym = sym;
ss->medium = orig->medium;
// Copy the qpms_tmatrix_fuction_t from orig
ss->tmg_count = orig->tmg_count;
QPMS_CRASHING_MALLOC(ss->tmg, ss->tmg_count * sizeof(*(ss->tmg)));
memcpy(ss->tmg, orig->tmg, ss->tmg_count * sizeof(*(ss->tmg)));
ss->tm_capacity = sym->order * orig->tm_count;
ss->tm = malloc(ss->tm_capacity * sizeof(qpms_tmatrix_t *));
QPMS_CRASHING_MALLOC(ss->tm, ss->tm_capacity * sizeof(*(ss->tm)));
ss->p_capacity = sym->order * orig->p_count;
ss->p = malloc(ss->p_capacity * sizeof(qpms_particle_tid_t));
ss->p_orbitinfo = malloc(ss->p_capacity * sizeof(qpms_ss_particle_orbitinfo_t));
QPMS_CRASHING_MALLOC(ss->p, ss->p_capacity * sizeof(qpms_particle_tid_t));
QPMS_CRASHING_MALLOC(ss->p_orbitinfo, ss->p_capacity * sizeof(qpms_ss_particle_orbitinfo_t));
for (qpms_ss_pi_t pi = 0; pi < ss->p_capacity; ++pi) {
ss->p_orbitinfo[pi].t = QPMS_SS_P_ORBITINFO_UNDEF;
ss->p_orbitinfo[pi].p = QPMS_SS_P_ORBITINFO_UNDEF;
}
// Copy T-matrices; checking for duplicities
// Evaluate the original T-matrices at omega
qpms_tmatrix_t **tm_orig_omega;
QPMS_CRASHING_MALLOC(tm_orig_omega, orig->tmg_count * sizeof(*tm_orig_omega));
for(qpms_ss_tmgi_t i = 0; i < orig->tmg_count; ++i)
tm_orig_omega[i] = qpms_tmatrix_init_from_function(orig->tmg[i], omega);
// Evaluate the medium and derived T-matrices at omega.
qpms_scatsys_at_omega_t *ssw;
QPMS_CRASHING_MALLOC(ssw, sizeof(*ssw)); // returned
ssw->ss = ss;
ssw->omega = omega;
ssw->medium = qpms_epsmu_generator_eval(ss->medium, omega);
ssw->wavenumber = qpms_wavenumber(omega, ssw->medium);
// we will be using ss->tm_capacity also for ssw->tm
QPMS_CRASHING_MALLOC(ssw->tm, ss->tm_capacity * sizeof(*(ssw->tm))); // returned
// Evaluate T-matrices at omega; checking for duplicities
ss->max_bspecn = 0; // We'll need it later.for memory alloc estimates.
qpms_ss_tmi_t tm_dupl_remap[ss->tm_capacity]; // Auxilliary array to label remapping the indices after ignoring t-matrix duplicities
qpms_ss_tmi_t tm_dupl_remap[ss->tm_capacity]; // Auxilliary array to label remapping the indices after ignoring t-matrix duplicities; VLA!
ss->tm_count = 0;
for (qpms_ss_tmi_t i = 0; i < orig->tm_count; ++i) {
qpms_tmatrix_t *ti = qpms_tmatrix_apply_operation(&(orig->tm[i].op), tm_orig_omega[orig->tm[i].tmgi]);
qpms_ss_tmi_t j;
for (j = 0; j < ss->tm_count; ++j)
if (qpms_tmatrix_isclose(orig->tm[i], ss->tm[j], QPMS_SCATSYS_TMATRIX_RTOL, QPMS_SCATSYS_TMATRIX_ATOL)) {
if (qpms_tmatrix_isclose(ti, ssw->tm[j], tol->rtol, tol->atol)) {
break;
}
if (j == ss->tm_count) { // duplicity not found, copy the t-matrix
ss->tm[j] = qpms_tmatrix_copy(orig->tm[i]);
ss->max_bspecn = MAX(ss->tm[j]->spec->n, ss->max_bspecn);
lMax = MAX(lMax, ss->tm[j]->spec->lMax);
if (j == ss->tm_count) { // duplicity not found, save both the "abstract" and "at omega" T-matrices
qpms_tmatrix_operation_copy(&ss->tm[j].op, &orig->tm[i].op);
ss->tm[j].tmgi = orig->tm[i].tmgi; // T-matrix functions are preserved.
ssw->tm[j] = ti;
ss->max_bspecn = MAX(ssw->tm[j]->spec->n, ss->max_bspecn);
lMax = MAX(lMax, ssw->tm[j]->spec->lMax);
++(ss->tm_count);
}
else qpms_tmatrix_free(ti);
tm_dupl_remap[i] = j;
if (normalisation == QPMS_NORMALISATION_UNDEF)
normalisation = ss->tm[i]->spec->norm;
normalisation = ssw->tm[i]->spec->norm;
// We expect all bspec norms to be the same.
else QPMS_ENSURE(normalisation == ss->tm[j]->spec->norm,
else QPMS_ENSURE(normalisation == ssw->tm[j]->spec->norm,
"Normalisation convention must be the same for all T-matrices."
" %d != %d\n", normalisation, ss->tm[j]->spec->norm);
" %d != %d\n", normalisation, ssw->tm[j]->spec->norm);
}
// Free the original T-matrices at omega
for(qpms_ss_tmgi_t i = 0; i < orig->tmg_count; ++i)
qpms_tmatrix_free(tm_orig_omega[i]);
free(tm_orig_omega);
// Copy particles, remapping the t-matrix indices
for (qpms_ss_pi_t i = 0; i < orig->p_count; ++(i)) {
ss->p[i] = orig->p[i];
@ -222,44 +258,57 @@ qpms_scatsys_t *qpms_scatsys_apply_symmetry(const qpms_scatsys_t *orig, const qp
ss->p_count = orig->p_count;
// allocate t-matrix symmetry map
ss->tm_sym_map = malloc(sizeof(qpms_ss_tmi_t) * sym->order * sym->order * ss->tm_count);
QPMS_CRASHING_MALLOC(ss->tm_sym_map, sizeof(qpms_ss_tmi_t) * sym->order * sym->order * ss->tm_count);
// Extend the T-matrices list by the symmetry operations
for (qpms_ss_tmi_t tmi = 0; tmi < ss->tm_count; ++tmi)
for (qpms_gmi_t gmi = 0; gmi < sym->order; ++gmi){
const size_t d = ss->tm[tmi]->spec->n;
complex double M[d][d]; // transformation matrix
qpms_irot3_uvswfi_dense(M[0], ss->tm[tmi]->spec, sym->rep3d[gmi]);
qpms_tmatrix_t *transformed = qpms_tmatrix_apply_symop(ss->tm[tmi], M[0]);
const size_t d = ssw->tm[tmi]->spec->n;
complex double *m;
QPMS_CRASHING_MALLOC(m, d*d*sizeof(complex double)); // ownership passed to ss->tm[ss->tm_count].op
qpms_irot3_uvswfi_dense(m, ssw->tm[tmi]->spec, sym->rep3d[gmi]);
qpms_tmatrix_t *transformed = qpms_tmatrix_apply_symop(ssw->tm[tmi], m);
qpms_ss_tmi_t tmj;
for (tmj = 0; tmj < ss->tm_count; ++tmj)
if (qpms_tmatrix_isclose(transformed, ss->tm[tmj], QPMS_SCATSYS_TMATRIX_RTOL, QPMS_SCATSYS_TMATRIX_ATOL))
if (qpms_tmatrix_isclose(transformed, ssw->tm[tmj], tol->rtol, tol->atol))
break;
if (tmj < ss->tm_count) { // HIT, transformed T-matrix already exists
//TODO some "rounding error cleanup" symmetrisation could be performed here?
qpms_tmatrix_free(transformed);
} else { // MISS, save the matrix and increment the count
ss->tm[ss->tm_count] = transformed;
} else { // MISS, save the matrix (also the "abstract" one)
ssw->tm[ss->tm_count] = transformed;
ss->tm[ss->tm_count].tmgi = ss->tm[tmi].tmgi;
qpms_tmatrix_operation_compose_chain_init(&(ss->tm[ss->tm_count].op), 2, 1);
struct qpms_tmatrix_operation_compose_chain * const o = &(ss->tm[ss->tm_count].op.op.compose_chain);
o->ops[0] = & ss->tm[tmi].op; // Let's just borrow this
o->ops_owned[0] = false;
o->opmem[0].typ = QPMS_TMATRIX_OPERATION_LRMATRIX;
o->opmem[0].op.lrmatrix.m = m;
o->opmem[0].op.lrmatrix.m_size = d * d;
o->ops[1] = o->opmem;
o->ops_owned[1] = true;
++(ss->tm_count);
}
ss->tm_sym_map[gmi + tmi * sym->order] = tmj; // In any case, tmj now indexes the correct transformed matrix
}
// Possibly free some space using the new ss->tm_count instead of (old) ss->tm_count*sym->order
ss->tm_sym_map = realloc(ss->tm_sym_map, sizeof(qpms_ss_tmi_t) * sym->order * ss->tm_count);
QPMS_CRASHING_REALLOC(ss->tm_sym_map, sizeof(qpms_ss_tmi_t) * sym->order * ss->tm_count);
// tm could be realloc'd as well, but those are just pointers, not likely many.
// allocate particle symmetry map
ss->p_sym_map = malloc(sizeof(qpms_ss_pi_t) * sym->order * sym->order * ss->p_count);
QPMS_CRASHING_MALLOC(ss->p_sym_map, sizeof(qpms_ss_pi_t) * sym->order * sym->order * ss->p_count);
// allocate orbit type array (TODO realloc later if too long)
ss->orbit_type_count = 0;
ss->orbit_types = calloc(ss->p_count, sizeof(qpms_ss_orbit_type_t));
QPMS_CRASHING_CALLOC(ss->orbit_types, ss->p_count, sizeof(qpms_ss_orbit_type_t));
ss->otspace_end = ss->otspace = malloc( // reallocate later
QPMS_CRASHING_MALLOC(ss->otspace, // reallocate later
(sizeof(qpms_ss_orbit_pi_t) * sym->order * sym->order
+ sizeof(qpms_ss_tmi_t) * sym->order
+ 3*sizeof(size_t) * sym->nirreps
+ sizeof(complex double) * SQ(sym->order * ss->max_bspecn)) * ss->p_count
);
ss->otspace_end = ss->otspace;
// Workspace for the orbit type determination
qpms_ss_orbit_type_t ot_current;
@ -348,15 +397,15 @@ qpms_scatsys_t *qpms_scatsys_apply_symmetry(const qpms_scatsys_t *orig, const qp
}
}
// Possibly free some space using the new ss->p_count instead of (old) ss->p_count*sym->order
ss->p_sym_map = realloc(ss->p_sym_map, sizeof(qpms_ss_pi_t) * sym->order * ss->p_count);
ss->p = realloc(ss->p, sizeof(qpms_particle_tid_t) * ss->p_count);
ss->p_orbitinfo = realloc(ss->p_orbitinfo, sizeof(qpms_ss_particle_orbitinfo_t)*ss->p_count);
QPMS_CRASHING_REALLOC(ss->p_sym_map, sizeof(qpms_ss_pi_t) * sym->order * ss->p_count);
QPMS_CRASHING_REALLOC(ss->p, sizeof(qpms_particle_tid_t) * ss->p_count);
QPMS_CRASHING_REALLOC(ss->p_orbitinfo, sizeof(qpms_ss_particle_orbitinfo_t)*ss->p_count);
ss->p_capacity = ss->p_count;
{ // Reallocate the orbit type data space and update the pointers if needed.
size_t otspace_sz = ss->otspace_end - ss->otspace;
char *old_otspace = ss->otspace;
ss->otspace = realloc(ss->otspace, otspace_sz);
QPMS_CRASHING_REALLOC(ss->otspace, otspace_sz);
ptrdiff_t shift = ss->otspace - old_otspace;
if(shift) {
for (size_t oi = 0; oi < ss->orbit_type_count; ++oi) {
@ -373,15 +422,14 @@ qpms_scatsys_t *qpms_scatsys_apply_symmetry(const qpms_scatsys_t *orig, const qp
// Set ss->fecv_size and ss->fecv_pstarts
ss->fecv_size = 0;
ss->fecv_pstarts = malloc(ss->p_count * sizeof(size_t));
QPMS_CRASHING_MALLOC(ss->fecv_pstarts, ss->p_count * sizeof(size_t));
for (qpms_ss_pi_t pi = 0; pi < ss->p_count; ++pi) {
ss->fecv_pstarts[pi] = ss->fecv_size;
ss->fecv_size += ss->tm[ss->p[pi].tmatrix_id]->spec->n; // That's a lot of dereferencing!
ss->fecv_size += ssw->tm[ss->p[pi].tmatrix_id]->spec->n; // That's a lot of dereferencing!
}
ss->saecv_sizes = malloc(sizeof(size_t) * sym->nirreps); if (!ss->saecv_sizes) abort();
ss->saecv_ot_offsets = malloc(sizeof(size_t) * sym->nirreps * ss->orbit_type_count);
if (!ss->saecv_ot_offsets) abort();
QPMS_CRASHING_MALLOC(ss->saecv_sizes, sizeof(size_t) * sym->nirreps);
QPMS_CRASHING_MALLOC(ss->saecv_ot_offsets, sizeof(size_t) * sym->nirreps * ss->orbit_type_count);
for(qpms_iri_t iri = 0; iri < sym->nirreps; ++iri) {
ss->saecv_sizes[iri] = 0;
for(qpms_ss_oti_t oti = 0; oti < ss->orbit_type_count; ++oti) {
@ -408,13 +456,14 @@ qpms_scatsys_t *qpms_scatsys_apply_symmetry(const qpms_scatsys_t *orig, const qp
}
ss->c = qpms_trans_calculator_init(lMax, normalisation);
return ss;
return ssw;
}
void qpms_scatsys_free(qpms_scatsys_t *ss) {
if(ss) {
free(ss->tm);
free(ss->tmg);
free(ss->p);
free(ss->fecv_pstarts);
free(ss->tm_sym_map);
@ -429,7 +478,59 @@ void qpms_scatsys_free(qpms_scatsys_t *ss) {
free(ss);
}
void qpms_scatsys_at_omega_refill(qpms_scatsys_at_omega_t *ssw,
complex double omega) {
const qpms_scatsys_t * const ss = ssw->ss;
ssw->omega = omega;
ssw->medium = qpms_epsmu_generator_eval(ss->medium, omega);
ssw->wavenumber = qpms_wavenumber(omega, ssw->medium);
qpms_tmatrix_t **tmatrices_preop;
QPMS_CRASHING_CALLOC(tmatrices_preop, ss->tmg_count, sizeof(*tmatrices_preop));
for (qpms_ss_tmgi_t tmgi = 0; tmgi < ss->tmg_count; ++tmgi)
tmatrices_preop[tmgi] = qpms_tmatrix_init_from_function(ss->tmg[tmgi], omega);
for (qpms_ss_tmi_t tmi = 0; tmi < ss->tm_count; ++tmi)
qpms_tmatrix_apply_operation_replace(ssw->tm[tmi], &ss->tm[tmi].op,
tmatrices_preop[ss->tm[tmi].tmgi]);
for (qpms_ss_tmgi_t tmgi = 0; tmgi < ss->tmg_count; ++tmgi)
qpms_tmatrix_free(tmatrices_preop[tmgi]);
free(tmatrices_preop);
}
qpms_scatsys_at_omega_t *qpms_scatsys_at_omega(const qpms_scatsys_t *ss,
complex double omega) {
// TODO
qpms_scatsys_at_omega_t *ssw;
QPMS_CRASHING_MALLOC(ssw, sizeof(*ssw));
ssw->omega = omega;
ssw->ss = ss;
ssw->medium = qpms_epsmu_generator_eval(ss->medium, omega);
ssw->wavenumber = qpms_wavenumber(omega, ssw->medium);
QPMS_CRASHING_CALLOC(ssw->tm, ss->tm_count, sizeof(*ssw->tm));
qpms_tmatrix_t **tmatrices_preop;
QPMS_CRASHING_CALLOC(tmatrices_preop, ss->tmg_count, sizeof(*tmatrices_preop));
for (qpms_ss_tmgi_t tmgi = 0; tmgi < ss->tmg_count; ++tmgi)
tmatrices_preop[tmgi] = qpms_tmatrix_init_from_function(ss->tmg[tmgi], omega);
for (qpms_ss_tmi_t tmi = 0; tmi < ss->tm_count; ++tmi) {
ssw->tm[tmi] = qpms_tmatrix_apply_operation(&ss->tm[tmi].op,
tmatrices_preop[ss->tm[tmi].tmgi]); //<- main difference to .._refill()
QPMS_ENSURE(ssw->tm[tmi],
"Got NULL pointer from qpms_tmatrix_apply_operation");
}
for (qpms_ss_tmgi_t tmgi = 0; tmgi < ss->tmg_count; ++tmgi)
qpms_tmatrix_free(tmatrices_preop[tmgi]);
free(tmatrices_preop);
return ssw;
}
void qpms_scatsys_at_omega_free(qpms_scatsys_at_omega_t *ssw) {
if (ssw) {
if(ssw->tm)
for(qpms_ss_tmi_t i = 0; i < ssw->ss->tm_count; ++i)
qpms_tmatrix_free(ssw->tm[i]);
free(ssw->tm);
}
free(ssw);
}
// (copypasta from symmetries.c)
// TODO at some point, maybe support also other norms.
@ -442,7 +543,7 @@ static inline void check_norm_compat(const qpms_vswf_set_spec_t *s)
case QPMS_NORMALISATION_NORM_SPHARM:
break;
default:
abort(); // Only SPHARM and POWER norms are supported right now.
QPMS_WTF; // Only SPHARM and POWER norms are supported right now.
}
}
@ -456,8 +557,7 @@ complex double *qpms_orbit_action_matrix(complex double *target,
const size_t n = bspec->n;
const qpms_gmi_t N = ot->size;
if (target == NULL)
target = malloc(n*n*N*N*sizeof(complex double));
if (target == NULL) abort();
QPMS_CRASHING_MALLOC(target, n*n*N*N*sizeof(complex double));
memset(target, 0, n*n*N*N*sizeof(complex double));
complex double tmp[n][n]; // this is the 'per-particle' action
qpms_irot3_uvswfi_dense(tmp[0], bspec, sym->rep3d[g]);
@ -500,8 +600,7 @@ complex double *qpms_orbit_irrep_projector_matrix(complex double *target,
const size_t n = bspec->n;
const qpms_gmi_t N = ot->size;
if (target == NULL)
target = malloc(n*n*N*N*sizeof(complex double));
if (target == NULL) abort();
QPMS_CRASHING_MALLOC(target, n*n*N*N*sizeof(complex double));
memset(target, 0, n*n*N*N*sizeof(complex double));
// Workspace for the orbit group action matrices
complex double *tmp = malloc(n*n*N*N*sizeof(complex double));
@ -556,7 +655,6 @@ complex double *qpms_orbit_irrep_basis(size_t *basis_size,
const bool newtarget = (target == NULL);
if (newtarget)
QPMS_CRASHING_MALLOC(target,n*n*N*N*sizeof(complex double));
if (target == NULL) abort();
memset(target, 0, n*n*N*N*sizeof(complex double));
// Get the projector (also workspace for right sg. vect.)
@ -564,12 +662,10 @@ complex double *qpms_orbit_irrep_basis(size_t *basis_size,
ot, bspec, sym, iri);
if(!projector) abort();
// Workspace for the right singular vectors.
complex double *V_H = malloc(n*n*N*N*sizeof(complex double));
if(!V_H) abort();
complex double *V_H; QPMS_CRASHING_MALLOC(V_H, n*n*N*N*sizeof(complex double));
// THIS SHOULD NOT BE NECESSARY
complex double *U = malloc(n*n*N*N*sizeof(complex double));
if(!U) abort();
double *s = malloc(n*N*sizeof(double)); if(!s) abort();
complex double *U; QPMS_CRASHING_MALLOC(U, n*n*N*N*sizeof(complex double));
double *s; QPMS_CRASHING_MALLOC(s, n*N*sizeof(double));
int info = LAPACKE_zgesdd(LAPACK_ROW_MAJOR,
'A', // jobz; overwrite projector with left sg.vec. and write right into V_H
@ -646,8 +742,7 @@ complex double *qpms_scatsys_irrep_pack_matrix_stupid(complex double *target_pac
return target_packed;
const size_t full_len = ss->fecv_size;
if (target_packed == NULL)
target_packed = malloc(SQ(packedlen)*sizeof(complex double));
if (target_packed == NULL) abort();
QPMS_CRASHING_MALLOC(target_packed, SQ(packedlen)*sizeof(complex double));
memset(target_packed, 0, SQ(packedlen)*sizeof(complex double));
// Workspace for the intermediate matrix
@ -684,8 +779,7 @@ complex double *qpms_scatsys_irrep_unpack_matrix_stupid(complex double *target_f
const size_t packedlen = ss->saecv_sizes[iri];
const size_t full_len = ss->fecv_size;
if (target_full == NULL)
target_full = malloc(SQ(full_len)*sizeof(complex double));
if (target_full == NULL) abort();
QPMS_CRASHING_MALLOC(target_full, SQ(full_len)*sizeof(complex double));
if(!add) memset(target_full, 0, SQ(full_len)*sizeof(complex double));
if(!packedlen) return target_full; // Empty irrep, do nothing.
@ -724,13 +818,12 @@ complex double *qpms_scatsys_irrep_pack_matrix(complex double *target_packed,
return target_packed;
const size_t full_len = ss->fecv_size;
if (target_packed == NULL)
target_packed = malloc(SQ(packedlen)*sizeof(complex double));
if (target_packed == NULL) abort();
QPMS_CRASHING_MALLOC(target_packed, SQ(packedlen)*sizeof(complex double));
memset(target_packed, 0, SQ(packedlen)*sizeof(complex double));
// Workspace for the intermediate particle-orbit matrix result
complex double *tmp = malloc(sizeof(complex double) * SQ(ss->max_bspecn)
* ss->sym->order); if (!tmp) abort();
complex double *tmp;
QPMS_CRASHING_MALLOC(tmp, sizeof(complex double) * SQ(ss->max_bspecn) * ss->sym->order);
const complex double one = 1, zero = 0;
@ -806,15 +899,14 @@ complex double *qpms_scatsys_irrep_unpack_matrix(complex double *target_full,
const size_t packedlen = ss->saecv_sizes[iri];
const size_t full_len = ss->fecv_size;
if (target_full == NULL)
target_full = malloc(SQ(full_len)*sizeof(complex double));
if (target_full == NULL) abort();
QPMS_CRASHING_MALLOC(target_full, SQ(full_len)*sizeof(complex double));
if(!add) memset(target_full, 0, SQ(full_len)*sizeof(complex double));
if(!packedlen) return target_full; // Empty irrep, do nothing.
// Workspace for the intermediate particle-orbit matrix result
complex double *tmp = malloc(sizeof(complex double) * SQ(ss->max_bspecn)
* ss->sym->order); if (!tmp) abort();
complex double *tmp;
QPMS_CRASHING_MALLOC(tmp, sizeof(complex double) * SQ(ss->max_bspecn) * ss->sym->order);
const complex double one = 1, zero = 0;
@ -889,8 +981,7 @@ complex double *qpms_scatsys_irrep_pack_vector(complex double *target_packed,
const size_t packedlen = ss->saecv_sizes[iri];
if (!packedlen) return target_packed; // Empty irrep
if (target_packed == NULL)
target_packed = malloc(packedlen*sizeof(complex double));
if (target_packed == NULL) abort();
QPMS_CRASHING_MALLOC(target_packed, packedlen*sizeof(complex double));
memset(target_packed, 0, packedlen*sizeof(complex double));
const complex double one = 1;
@ -928,8 +1019,7 @@ complex double *qpms_scatsys_irrep_unpack_vector(complex double *target_full,
const qpms_iri_t iri, bool add) {
const size_t full_len = ss->fecv_size;
if (target_full == NULL)
target_full = malloc(full_len*sizeof(complex double));
if (target_full == NULL) abort();
QPMS_CRASHING_MALLOC(target_full, full_len*sizeof(complex double));
if (!add) memset(target_full, 0, full_len*sizeof(complex double));
const complex double one = 1;
@ -989,11 +1079,11 @@ complex double *qpms_scatsys_build_translation_matrix_e_full(
{ // Non-diagonal part; M[piR, piC] = T[piR] S(piR<-piC)
size_t fullvec_offsetR = 0;
for(qpms_ss_pi_t piR = 0; piR < ss->p_count; ++piR) {
const qpms_vswf_set_spec_t *bspecR = ss->tm[ss->p[piR].tmatrix_id]->spec;
const qpms_vswf_set_spec_t *bspecR = qpms_ss_bspec_pi(ss, piR);
const cart3_t posR = ss->p[piR].pos;
size_t fullvec_offsetC = 0;
for(qpms_ss_pi_t piC = 0; piC < ss->p_count; ++piC) {
const qpms_vswf_set_spec_t *bspecC = ss->tm[ss->p[piC].tmatrix_id]->spec;
const qpms_vswf_set_spec_t *bspecC = qpms_ss_bspec_pi(ss, piC);
if(piC != piR) { // The diagonal will be dealt with later.
const cart3_t posC = ss->p[piC].pos;
QPMS_ENSURE_SUCCESS(qpms_trans_calculator_get_trans_array_lc3p(ss->c,
@ -1003,7 +1093,7 @@ complex double *qpms_scatsys_build_translation_matrix_e_full(
}
fullvec_offsetC += bspecC->n;
}
assert(fullvec_offsetC = full_len);
assert(fullvec_offsetC == full_len);
fullvec_offsetR += bspecR->n;
}
assert(fullvec_offsetR == full_len);
@ -1014,13 +1104,14 @@ complex double *qpms_scatsys_build_translation_matrix_e_full(
complex double *qpms_scatsys_build_modeproblem_matrix_full(
complex double *qpms_scatsysw_build_modeproblem_matrix_full(
/// Target memory with capacity for ss->fecv_size**2 elements. If NULL, new will be allocated.
complex double *target,
const qpms_scatsys_t *ss,
complex double k ///< Wave number to use in the translation matrix.
const qpms_scatsys_at_omega_t *ssw
)
{
const complex double k = ssw->wavenumber;
const qpms_scatsys_t *ss = ssw->ss;
const size_t full_len = ss->fecv_size;
if(!target)
QPMS_CRASHING_MALLOC(target, SQ(full_len) * sizeof(complex double));
@ -1031,13 +1122,13 @@ complex double *qpms_scatsys_build_modeproblem_matrix_full(
{ // Non-diagonal part; M[piR, piC] = -T[piR] S(piR<-piC)
size_t fullvec_offsetR = 0;
for(qpms_ss_pi_t piR = 0; piR < ss->p_count; ++piR) {
const qpms_vswf_set_spec_t *bspecR = ss->tm[ss->p[piR].tmatrix_id]->spec;
const qpms_vswf_set_spec_t *bspecR = ssw->tm[ss->p[piR].tmatrix_id]->spec;
const cart3_t posR = ss->p[piR].pos;
size_t fullvec_offsetC = 0;
// dest particle T-matrix
const complex double *tmmR = ss->tm[ss->p[piR].tmatrix_id]->m;
const complex double *tmmR = ssw->tm[ss->p[piR].tmatrix_id]->m;
for(qpms_ss_pi_t piC = 0; piC < ss->p_count; ++piC) {
const qpms_vswf_set_spec_t *bspecC = ss->tm[ss->p[piC].tmatrix_id]->spec;
const qpms_vswf_set_spec_t *bspecC = ssw->tm[ss->p[piC].tmatrix_id]->spec;
if(piC != piR) { // The diagonal will be dealt with later.
const cart3_t posC = ss->p[piC].pos;
QPMS_ENSURE_SUCCESS(qpms_trans_calculator_get_trans_array_lc3p(ss->c,
@ -1064,19 +1155,20 @@ complex double *qpms_scatsys_build_modeproblem_matrix_full(
}
// Serial reference implementation.
complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed_serial(
complex double *qpms_scatsysw_build_modeproblem_matrix_irrep_packed_serial(
/// Target memory with capacity for ss->saecv_sizes[iri]**2 elements. If NULL, new will be allocated.
complex double *target_packed,
const qpms_scatsys_t *ss, qpms_iri_t iri,
complex double k ///< Wave number to use in the translation matrix.
const qpms_scatsys_at_omega_t *ssw,
qpms_iri_t iri
)
{
const qpms_scatsys_t *ss = ssw->ss;
const complex double k = ssw->wavenumber;
const size_t packedlen = ss->saecv_sizes[iri];
if (!packedlen) // THIS IS A BIT PROBLEMATIC, TODO how to deal with empty irreps?
return target_packed;
if (target_packed == NULL)
target_packed = malloc(SQ(packedlen)*sizeof(complex double));
if (target_packed == NULL) abort();
QPMS_CRASHING_MALLOC(target_packed, SQ(packedlen)*sizeof(complex double));
memset(target_packed, 0, SQ(packedlen)*sizeof(complex double));
// some of the following workspaces are probably redundant; TODO optimize later.
@ -1102,7 +1194,7 @@ complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed_serial(
const size_t packed_orbit_offsetR =
ss->saecv_ot_offsets[iri*ss->orbit_type_count + otiR]
+ osnR * otR->irbase_sizes[iri];
const qpms_vswf_set_spec_t *bspecR = ss->tm[ss->p[piR].tmatrix_id]->spec;
const qpms_vswf_set_spec_t *bspecR = ssw->tm[ss->p[piR].tmatrix_id]->spec;
// Orbit coeff vector's full size:
const size_t orbit_fullsizeR = otR->size * otR->bspecn;
const size_t particle_fullsizeR = otR->bspecn; // == bspecR->n
@ -1112,7 +1204,7 @@ complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed_serial(
const cart3_t posR = ss->p[piR].pos;
if(orbit_packedsizeR) { // avoid zgemm crash on empty irrep
// dest particle T-matrix
const complex double *tmmR = ss->tm[ss->p[piR].tmatrix_id]->m;
const complex double *tmmR = ssw->tm[ss->p[piR].tmatrix_id]->m;
for(qpms_ss_pi_t piC = 0; piC < ss->p_count; ++piC) { //Column loop
const qpms_ss_oti_t otiC = ss->p_orbitinfo[piC].t;
const qpms_ss_orbit_type_t *const otC = ss->orbit_types + otiC;
@ -1122,7 +1214,7 @@ complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed_serial(
const size_t packed_orbit_offsetC =
ss->saecv_ot_offsets[iri*ss->orbit_type_count + otiC]
+ osnC * otC->irbase_sizes[iri];
const qpms_vswf_set_spec_t *bspecC = ss->tm[ss->p[piC].tmatrix_id]->spec;
const qpms_vswf_set_spec_t *bspecC = ssw->tm[ss->p[piC].tmatrix_id]->spec;
// Orbit coeff vector's full size:
const size_t orbit_fullsizeC = otC->size * otC->bspecn;
const size_t particle_fullsizeC = otC->bspecn; // == bspecC->n
@ -1174,19 +1266,19 @@ complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed_serial(
return target_packed;
}
complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed_orbitorderR(
complex double *qpms_scatsysw_build_modeproblem_matrix_irrep_packed_orbitorderR(
/// Target memory with capacity for ss->saecv_sizes[iri]**2 elements. If NULL, new will be allocated.
complex double *target_packed,
const qpms_scatsys_t *ss, qpms_iri_t iri,
complex double k ///< Wave number to use in the translation matrix.
const qpms_scatsys_at_omega_t *ssw, qpms_iri_t iri
)
{
const qpms_scatsys_t *ss = ssw->ss;
const complex double k = ssw->wavenumber;
const size_t packedlen = ss->saecv_sizes[iri];
if (!packedlen) // THIS IS A BIT PROBLEMATIC, TODO how to deal with empty irreps?
return target_packed;
if (target_packed == NULL)
target_packed = malloc(SQ(packedlen)*sizeof(complex double));
if (target_packed == NULL) abort();
QPMS_CRASHING_MALLOC(target_packed, SQ(packedlen)*sizeof(complex double));
memset(target_packed, 0, SQ(packedlen)*sizeof(complex double));
// some of the following workspaces are probably redundant; TODO optimize later.
@ -1215,7 +1307,7 @@ complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed_orbitorderR(
if(orbit_packedsizeR) { // avoid zgemm crash on empty irrep
const size_t particle_fullsizeR = otR->bspecn; // == bspecR->n
const qpms_vswf_set_spec_t *bspecR = ss->tm[ss->p[orbitstartpiR].tmatrix_id]->spec;
const qpms_vswf_set_spec_t *bspecR = ssw->tm[ss->p[orbitstartpiR].tmatrix_id]->spec;
// This is the orbit-level matrix projecting the whole orbit onto the irrep.
const complex double *omR = otR->irbases + otR->irbase_offsets[iri];
// Orbit coeff vector's full size:
@ -1231,7 +1323,7 @@ complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed_orbitorderR(
assert(ss->p_orbitinfo[piR].osn == osnR);
const cart3_t posR = ss->p[piR].pos;
// dest particle T-matrix
const complex double *tmmR = ss->tm[ss->p[piR].tmatrix_id]->m;
const complex double *tmmR = ssw->tm[ss->p[piR].tmatrix_id]->m;
for(qpms_ss_pi_t piC = 0; piC < ss->p_count; ++piC) { //Column loop
const qpms_ss_oti_t otiC = ss->p_orbitinfo[piC].t;
const qpms_ss_orbit_type_t *const otC = ss->orbit_types + otiC;
@ -1241,7 +1333,7 @@ complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed_orbitorderR(
const size_t packed_orbit_offsetC =
ss->saecv_ot_offsets[iri*ss->orbit_type_count + otiC]
+ osnC * otC->irbase_sizes[iri];
const qpms_vswf_set_spec_t *bspecC = ss->tm[ss->p[piC].tmatrix_id]->spec;
const qpms_vswf_set_spec_t *bspecC = ssw->tm[ss->p[piC].tmatrix_id]->spec;
// Orbit coeff vector's full size:
const size_t orbit_fullsizeC = otC->size * otC->bspecn;
const size_t particle_fullsizeC = otC->bspecn; // == bspecC->n
@ -1294,20 +1386,21 @@ complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed_orbitorderR(
return target_packed;
}
struct qpms_scatsys_build_modeproblem_matrix_irrep_packed_parallelR_thread_arg{
const qpms_scatsys_t *ss;
struct qpms_scatsysw_build_modeproblem_matrix_irrep_packed_parallelR_thread_arg{
const qpms_scatsys_at_omega_t *ssw;
qpms_ss_pi_t *opistartR_ptr;
pthread_mutex_t *opistartR_mutex;
qpms_iri_t iri;
complex double *target_packed;
complex double k;
};
static void *qpms_scatsys_build_modeproblem_matrix_irrep_packed_parallelR_thread(void *arg)
static void *qpms_scatsysw_build_modeproblem_matrix_irrep_packed_parallelR_thread(void *arg)
{
const struct qpms_scatsys_build_modeproblem_matrix_irrep_packed_parallelR_thread_arg
const struct qpms_scatsysw_build_modeproblem_matrix_irrep_packed_parallelR_thread_arg
*a = arg;
const qpms_scatsys_t *ss = a->ss;
const qpms_scatsys_at_omega_t *ssw = a->ssw;
const complex double k = ssw->wavenumber;
const qpms_scatsys_t *ss = ssw->ss;
const qpms_iri_t iri = a->iri;
const size_t packedlen = ss->saecv_sizes[iri];
@ -1347,7 +1440,7 @@ static void *qpms_scatsys_build_modeproblem_matrix_irrep_packed_parallelR_thread
if(orbit_packedsizeR) { // avoid zgemm crash on empty irrep
const size_t particle_fullsizeR = otR->bspecn; // == bspecR->n
const qpms_vswf_set_spec_t *bspecR = ss->tm[ss->p[orbitstartpiR].tmatrix_id]->spec;
const qpms_vswf_set_spec_t *bspecR = ssw->tm[ss->p[orbitstartpiR].tmatrix_id]->spec;
// This is the orbit-level matrix projecting the whole orbit onto the irrep.
const complex double *omR = otR->irbases + otR->irbase_offsets[iri];
// Orbit coeff vector's full size:
@ -1363,7 +1456,7 @@ static void *qpms_scatsys_build_modeproblem_matrix_irrep_packed_parallelR_thread
assert(ss->p_orbitinfo[piR].osn == osnR);
const cart3_t posR = ss->p[piR].pos;
// dest particle T-matrix
const complex double *tmmR = ss->tm[ss->p[piR].tmatrix_id]->m;
const complex double *tmmR = ssw->tm[ss->p[piR].tmatrix_id]->m;
for(qpms_ss_pi_t piC = 0; piC < ss->p_count; ++piC) { //Column loop
const qpms_ss_oti_t otiC = ss->p_orbitinfo[piC].t;
const qpms_ss_orbit_type_t *const otC = ss->orbit_types + otiC;
@ -1373,7 +1466,7 @@ static void *qpms_scatsys_build_modeproblem_matrix_irrep_packed_parallelR_thread
const size_t packed_orbit_offsetC =
ss->saecv_ot_offsets[iri*ss->orbit_type_count + otiC]
+ osnC * otC->irbase_sizes[iri];
const qpms_vswf_set_spec_t *bspecC = ss->tm[ss->p[piC].tmatrix_id]->spec;
const qpms_vswf_set_spec_t *bspecC = ssw->tm[ss->p[piC].tmatrix_id]->spec;
// Orbit coeff vector's full size:
const size_t orbit_fullsizeC = otC->size * otC->bspecn;
const size_t particle_fullsizeC = otC->bspecn; // == bspecC->n
@ -1387,7 +1480,7 @@ static void *qpms_scatsys_build_modeproblem_matrix_irrep_packed_parallelR_thread
QPMS_ENSURE_SUCCESS(qpms_trans_calculator_get_trans_array_lc3p(ss->c,
Sblock, // Sblock is S(piR->piC)
bspecR, bspecC->n, bspecC, 1,
a->k, posR, posC, QPMS_HANKEL_PLUS));
k, posR, posC, QPMS_HANKEL_PLUS));
SERIAL_ZGEMM(CblasRowMajor, CblasNoTrans, CblasNoTrans,
bspecR->n /*m*/, bspecC->n /*n*/, bspecR->n /*k*/,
@ -1484,7 +1577,7 @@ static void *qpms_scatsys_build_translation_matrix_e_irrep_packed_parallelR_thre
if(orbit_packedsizeR) { // avoid zgemm crash on empty irrep
const size_t particle_fullsizeR = otR->bspecn; // == bspecR->n
const qpms_vswf_set_spec_t *bspecR = ss->tm[ss->p[orbitstartpiR].tmatrix_id]->spec;
const qpms_vswf_set_spec_t *bspecR = qpms_ss_bspec_pi(ss, orbitstartpiR);
// This is the orbit-level matrix projecting the whole orbit onto the irrep.
const complex double *omR = otR->irbases + otR->irbase_offsets[iri];
// Orbit coeff vector's full size:
@ -1508,7 +1601,7 @@ static void *qpms_scatsys_build_translation_matrix_e_irrep_packed_parallelR_thre
const size_t packed_orbit_offsetC =
ss->saecv_ot_offsets[iri*ss->orbit_type_count + otiC]
+ osnC * otC->irbase_sizes[iri];
const qpms_vswf_set_spec_t *bspecC = ss->tm[ss->p[piC].tmatrix_id]->spec;
const qpms_vswf_set_spec_t *bspecC = qpms_ss_bspec_pi(ss, piC);
// Orbit coeff vector's full size:
const size_t orbit_fullsizeC = otC->size * otC->bspecn;
const size_t particle_fullsizeC = otC->bspecn; // == bspecC->n
@ -1567,7 +1660,7 @@ complex double *qpms_scatsys_build_translation_matrix_e_irrep_packed(
complex double *target_packed,
const qpms_scatsys_t *ss,
qpms_iri_t iri,
complex double k, ///< Wave number to use in the translation matrix.
const complex double k,
qpms_bessel_t J
)
{
@ -1576,8 +1669,7 @@ complex double *qpms_scatsys_build_translation_matrix_e_irrep_packed(
if (!packedlen) // THIS IS A BIT PROBLEMATIC, TODO how to deal with empty irreps?
return target_packed;
if (target_packed == NULL)
target_packed = malloc(SQ(packedlen)*sizeof(complex double));
if (target_packed == NULL) abort();
QPMS_CRASHING_MALLOC(target_packed, SQ(packedlen)*sizeof(complex double));
memset(target_packed, 0, SQ(packedlen)*sizeof(complex double));
qpms_ss_pi_t opistartR = 0;
@ -1618,26 +1710,24 @@ complex double *qpms_scatsys_build_translation_matrix_e_irrep_packed(
// Parallel implementation, now default
complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed(
complex double *qpms_scatsysw_build_modeproblem_matrix_irrep_packed(
/// Target memory with capacity for ss->saecv_sizes[iri]**2 elements. If NULL, new will be allocated.
complex double *target_packed,
const qpms_scatsys_t *ss, qpms_iri_t iri,
complex double k ///< Wave number to use in the translation matrix.
const qpms_scatsys_at_omega_t *ssw, qpms_iri_t iri
)
{
const size_t packedlen = ss->saecv_sizes[iri];
const size_t packedlen = ssw->ss->saecv_sizes[iri];
if (!packedlen) // THIS IS A BIT PROBLEMATIC, TODO how to deal with empty irreps?
return target_packed;
if (target_packed == NULL)
target_packed = malloc(SQ(packedlen)*sizeof(complex double));
if (target_packed == NULL) abort();
QPMS_CRASHING_MALLOC(target_packed,SQ(packedlen)*sizeof(complex double));
memset(target_packed, 0, SQ(packedlen)*sizeof(complex double));
qpms_ss_pi_t opistartR = 0;
pthread_mutex_t opistartR_mutex;
QPMS_ENSURE_SUCCESS(pthread_mutex_init(&opistartR_mutex, NULL));
const struct qpms_scatsys_build_modeproblem_matrix_irrep_packed_parallelR_thread_arg
arg = {ss, &opistartR, &opistartR_mutex, iri, target_packed, k};
const struct qpms_scatsysw_build_modeproblem_matrix_irrep_packed_parallelR_thread_arg
arg = {ssw, &opistartR, &opistartR_mutex, iri, target_packed};
// FIXME THIS IS NOT PORTABLE:
long nthreads;
@ -1658,7 +1748,7 @@ complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed(
pthread_t thread_ids[nthreads];
for(long thi = 0; thi < nthreads; ++thi)
QPMS_ENSURE_SUCCESS(pthread_create(thread_ids + thi, NULL,
qpms_scatsys_build_modeproblem_matrix_irrep_packed_parallelR_thread,
qpms_scatsysw_build_modeproblem_matrix_irrep_packed_parallelR_thread,
(void *) &arg));
for(long thi = 0; thi < nthreads; ++thi) {
void *retval;
@ -1696,18 +1786,18 @@ complex double *qpms_scatsys_incident_field_vector_irrep_packed(
#endif
complex double *qpms_scatsys_apply_Tmatrices_full(
complex double *qpms_scatsysw_apply_Tmatrices_full(
complex double *target_full, const complex double *inc_full,
const qpms_scatsys_t *ss) {
const qpms_scatsys_at_omega_t *ssw) {
QPMS_UNTESTED;
const qpms_scatsys_t *ss = ssw->ss;
if (!target_full) QPMS_CRASHING_CALLOC(target_full, ss->fecv_size,
sizeof(complex double));
for(qpms_ss_pi_t pi = 0; pi < ss->p_count; ++pi) {
complex double *ptarget = target_full + ss->fecv_pstarts[pi];
const complex double *psrc = inc_full + ss->fecv_pstarts[pi];
const qpms_vswf_set_spec_t *bspec = qpms_ss_bspec_pi(ss, pi);
// TODO check whether T-matrix is non-virtual after virtual t-matrices are implemented.
const qpms_tmatrix_t *T = ss->tm[ss->p[pi].tmatrix_id];
const qpms_tmatrix_t *T = ssw->tm[ss->p[pi].tmatrix_id];
qpms_apply_tmatrix(ptarget, psrc, T);
}
return target_full;
@ -1751,8 +1841,9 @@ void qpms_ss_LU_free(qpms_ss_LU lu) {
free(lu.ipiv);
}
qpms_ss_LU qpms_scatsys_modeproblem_matrix_full_factorise(complex double *mpmatrix_full,
int *target_piv, const qpms_scatsys_t *ss) {
qpms_ss_LU qpms_scatsysw_modeproblem_matrix_full_factorise(complex double *mpmatrix_full,
int *target_piv, const qpms_scatsys_at_omega_t *ssw) {
const qpms_scatsys_t *ss = ssw->ss;
QPMS_ENSURE(mpmatrix_full, "A non-NULL pointer to the pre-calculated mode matrix is required");
if (!target_piv) QPMS_CRASHING_MALLOC(target_piv, ss->fecv_size * sizeof(int));
QPMS_ENSURE_SUCCESS(LAPACKE_zgetrf(LAPACK_ROW_MAJOR, ss->fecv_size, ss->fecv_size,
@ -1760,45 +1851,45 @@ qpms_ss_LU qpms_scatsys_modeproblem_matrix_full_factorise(complex double *mpmatr
qpms_ss_LU lu;
lu.a = mpmatrix_full;
lu.ipiv = target_piv;
lu.ss = ss;
lu.ssw = ssw;
lu.full = true;
lu.iri = -1;
return lu;
}
qpms_ss_LU qpms_scatsys_modeproblem_matrix_irrep_packed_factorise(complex double *mpmatrix_packed,
int *target_piv, const qpms_scatsys_t *ss, qpms_iri_t iri) {
qpms_ss_LU qpms_scatsysw_modeproblem_matrix_irrep_packed_factorise(complex double *mpmatrix_packed,
int *target_piv, const qpms_scatsys_at_omega_t *ssw, qpms_iri_t iri) {
QPMS_ENSURE(mpmatrix_packed, "A non-NULL pointer to the pre-calculated mode matrix is required");
size_t n = ss->saecv_sizes[iri];
size_t n = ssw->ss->saecv_sizes[iri];
if (!target_piv) QPMS_CRASHING_MALLOC(target_piv, n * sizeof(int));
QPMS_ENSURE_SUCCESS(LAPACKE_zgetrf(LAPACK_ROW_MAJOR, n, n,
mpmatrix_packed, n, target_piv));
qpms_ss_LU lu;
lu.a = mpmatrix_packed;
lu.ipiv = target_piv;
lu.ss = ss;
lu.ssw = ssw;
lu.full = false;
lu.iri = iri;
return lu;
}
qpms_ss_LU qpms_scatsys_build_modeproblem_matrix_full_LU(
qpms_ss_LU qpms_scatsysw_build_modeproblem_matrix_full_LU(
complex double *target, int *target_piv,
const qpms_scatsys_t *ss, complex double k){
target = qpms_scatsys_build_modeproblem_matrix_full(target, ss, k);
return qpms_scatsys_modeproblem_matrix_full_factorise(target, target_piv, ss);
const qpms_scatsys_at_omega_t *ssw){
target = qpms_scatsysw_build_modeproblem_matrix_full(target, ssw);
return qpms_scatsysw_modeproblem_matrix_full_factorise(target, target_piv, ssw);
}
qpms_ss_LU qpms_scatsys_build_modeproblem_matrix_irrep_packed_LU(
qpms_ss_LU qpms_scatsysw_build_modeproblem_matrix_irrep_packed_LU(
complex double *target, int *target_piv,
const qpms_scatsys_t *ss, qpms_iri_t iri, complex double k){
target = qpms_scatsys_build_modeproblem_matrix_irrep_packed(target, ss, iri, k);
return qpms_scatsys_modeproblem_matrix_irrep_packed_factorise(target, target_piv, ss, iri);
const qpms_scatsys_at_omega_t *ssw, qpms_iri_t iri){
target = qpms_scatsysw_build_modeproblem_matrix_irrep_packed(target, ssw, iri);
return qpms_scatsysw_modeproblem_matrix_irrep_packed_factorise(target, target_piv, ssw, iri);
}
complex double *qpms_scatsys_scatter_solve(
complex double *f, const complex double *a_inc, qpms_ss_LU lu) {
const size_t n = lu.full ? lu.ss->fecv_size : lu.ss->saecv_sizes[lu.iri];
const size_t n = lu.full ? lu.ssw->ss->fecv_size : lu.ssw->ss->saecv_sizes[lu.iri];
if (!f) QPMS_CRASHING_MALLOC(f, n * sizeof(complex double));
memcpy(f, a_inc, n*sizeof(complex double)); // It will be rewritten by zgetrs
QPMS_ENSURE_SUCCESS(LAPACKE_zgetrs(LAPACK_ROW_MAJOR, 'N' /*trans*/, n /*n*/, 1 /*nrhs number of right hand sides*/,

View File

@ -12,6 +12,7 @@
#define QPMS_SCATSYSTEM_H
#include "qpms_types.h"
#include "vswf.h"
#include "tmatrices.h"
#include <stdbool.h>
/// Overrides the number of threads spawned by the paralellized functions.
@ -19,10 +20,13 @@
void qpms_scatsystem_set_nthreads(long n);
/// A particle, defined by its T-matrix and position.
/** This is rather only an auxillary intermediate structure to ultimately
* build an qpms_scatsys_t instance */
typedef struct qpms_particle_t {
// Does it make sense to ever use other than cartesian coords for this?
cart3_t pos; ///< Particle position in cartesian coordinates.
const qpms_tmatrix_t *tmatrix; ///< T-matrix; not owned by qpms_particle_t.
const qpms_tmatrix_function_t *tmg; ///< T-matrix function; not owned by qpms_particle_t.
qpms_tmatrix_operation_t op; ///< T-matrix transformation operation w.r.t. \a tmg.
} qpms_particle_t;
struct qpms_finite_group_t;
@ -121,13 +125,32 @@ typedef struct qpms_ss_particle_orbitinfo {
qpms_ss_orbit_pi_t p; ///< Order (sija, ei rankki) of the particle inside that orbit type.
} qpms_ss_particle_orbitinfo_t;
/// Auxillary type used in qpms_scatsys_t: A recepy to create another T-matrices by symmetry operations.
typedef struct qpms_ss_derived_tmatrix_t {
qpms_ss_tmgi_t tmgi; ///< Index of the corresponding qpms_scatsys_t::tm element.
struct qpms_tmatrix_operation_t op; ///< Operation to derive this particular T-matrix.
} qpms_ss_derived_tmatrix_t;
struct qpms_trans_calculator;
struct qpms_epsmu_generator_t;
typedef struct qpms_scatsys_t {
// TODO does bspec belong here?
qpms_tmatrix_t **tm; ///< T-matrices in the system
qpms_ss_tmi_t tm_count; ///< Number of all different T-matrices
struct qpms_epsmu_generator_t medium; ///< Optical properties of the background medium.
/// (Template) T-matrix functions in the system.
/** The qpms_abstract_tmatrix_t objects (onto which this array member point)
* are NOT owned by this and must be kept alive for the whole lifetime
* of all qpms_scatsys_t objects that are built upon them.
*/
qpms_tmatrix_function_t *tmg;
qpms_ss_tmgi_t tmg_count; ///< Number of all different original T-matrix generators in the system.
/// All the different T-matrix functions in the system, including those derived from \a tmg elements by symmetries.
qpms_ss_derived_tmatrix_t *tm;
qpms_ss_tmi_t tm_count; ///< Number of all different T-matrices in the system (length of tm[]).
qpms_ss_tmi_t tm_capacity; ///< Capacity of tm[].
qpms_particle_tid_t *p; ///< Particles.
qpms_ss_pi_t p_count; ///< Size of particles array.
qpms_ss_pi_t p_capacity; ///< Capacity of p[].
@ -167,23 +190,77 @@ typedef struct qpms_scatsys_t {
struct qpms_trans_calculator *c;
} qpms_scatsys_t;
/// Convenience function to access pi'th particle's bspec.
static inline const qpms_vswf_set_spec_t *qpms_ss_bspec_pi(const qpms_scatsys_t *ss, qpms_ss_pi_t pi) {
return ss->tm[ss->p[pi].tmatrix_id]->spec;
/// Retrieve the bspec of \a tmi'th element of \a ss->tm.
static inline const qpms_vswf_set_spec_t *qpms_ss_bspec_tmi(const qpms_scatsys_t *ss, qpms_ss_tmi_t tmi) {
return ss->tmg[ss->tm[tmi].tmgi].spec;
}
/// Creates a new scatsys by applying a symmetry group, copying particles if needed.
/** In fact, it copies everything except the vswf set specs, so keep them alive until scatsys is destroyed.
* The following fields must be filled:
* orig->tm
* orig->tm_count
* orig->p
* orig->p_count
/// Retrieve the bspec of \a pi'th particle in \a ss->p.
static inline const qpms_vswf_set_spec_t *qpms_ss_bspec_pi(const qpms_scatsys_t *ss, qpms_ss_pi_t pi) {
return ss->tmg[ss->tm[ss->p[pi].tmatrix_id].tmgi].spec;
}
typedef struct qpms_scatsys_at_omega_t {
const qpms_scatsys_t *ss; ///< Parent scattering system.
/// T-matrices from \a ss, evaluated at \a omega.
/** The T-matrices are in the same order as in \a ss,
* i.e in the order corresponding to \a ss->tm.
*/
qpms_scatsys_t *qpms_scatsys_apply_symmetry(const qpms_scatsys_t *orig, const struct qpms_finite_group_t *sym);
qpms_tmatrix_t **tm;
complex double omega; ///< Angular frequency
qpms_epsmu_t medium; ///< Background medium optical properties at the given frequency
complex double wavenumber; ///< Background medium wavenumber
} qpms_scatsys_at_omega_t;
/// Creates a new scatsys by applying a symmetry group onto a "proto-scatsys", copying particles if needed.
/** In fact, it copies everything except the vswf set specs and qpms_abstract_tmatrix_t instances,
* so keep them alive until scatsys is destroyed.
*
* The following fields must be filled in the "proto- scattering system" \a orig:
* * orig->medium The pointers are copied to the new qpms_scatsys_t instance;
* the target qpms_abstract_tmatrix_t objects must be kept alive before all the resulting
* qpms_scatsys_t instances are properly destroyed.
* * orig->tmg The pointers are copied to the new qpms_scatsys_t instance;
* the target qpms_abstract_tmatrix_t objects must be kept alive before all the resulting
* qpms_scatsys_t instances are properly destroyed. The pointers from orig->tmg, however, are copied.
* * orig->tmg_count
* * orig->tm Must be filled, although the operations will typically be identities
* (QPMS_TMATRIX_OPERATION_NOOP). N.B. these NOOPs might be replaced with some symmetrisation operation
* in the resulting "full" qpms_scatsys_t instance.
* * orig->tm_count
* * orig->p
* * orig->p_count
*
* The resulting qpms_scatsys_t is obtained by actually evaluating the T-matrices
* at the given frequency \a omega and where applicable, these are compared
* by their values with given tolerances. The T-matrix generators are expected
* to preserve the point group symmetries for all frequencies.
*
* This particular function will likely change in the future.
*
* \returns An instance \a sso of qpms_scatsys_omega_t. Note that \a sso->ss
* must be saved by the caller before destroying \a sso
* (with qpms_scatsys_at_omega_free(), and destroyed only afterwards with
* qpms_scatsys_free() when not needed anymore.
* \a sso->ss can be reused for different frequency by a
* qpms_scatsys_at_omega() call.
*
*/
qpms_scatsys_at_omega_t *qpms_scatsys_apply_symmetry(const qpms_scatsys_t *orig, const struct qpms_finite_group_t *sym,
complex double omega, const struct qpms_tolerance_spec_t *tol);
/// Destroys the result of qpms_scatsys_apply_symmetry or qpms_scatsys_load.
void qpms_scatsys_free(qpms_scatsys_t *s);
/// Destroys a qpms_scatsys_at_omega_t.
/** Used on results of qpms_scatsys_apply_symmetry() and qpms_scatsys_at_omega(). */
void qpms_scatsys_at_omega_free(qpms_scatsys_at_omega_t *ssw);
/// Evaluates scattering system T-matrices at a given frequency.
/** Free the result using qpms_scatsys_at_omega_free() when done. */
qpms_scatsys_at_omega_t *qpms_scatsys_at_omega(const qpms_scatsys_t *ss,
complex double omega);
/// Creates a "full" transformation matrix U that takes a full vector and projects it onto an symmetry adapted basis.
/** Mostly as a reference and a debugging tool, as multiplicating these big matrices would be inefficient.
*
@ -266,37 +343,36 @@ complex double *qpms_scatsys_build_translation_matrix_e_irrep_packed(
);
/// Creates the full \f$ (I - TS) \f$ matrix of the scattering system.
complex double *qpms_scatsys_build_modeproblem_matrix_full(
complex double *qpms_scatsysw_build_modeproblem_matrix_full(
/// Target memory with capacity for ss->fecv_size**2 elements. If NULL, new will be allocated.
complex double *target,
const qpms_scatsys_t *ss,
complex double k ///< Wave number to use in the translation matrix.
const qpms_scatsys_at_omega_t *ssw
);
/// Creates the mode problem matrix \f$ (I - TS) \f$ directly in the irrep-packed form.
complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed(
complex double *qpms_scatsysw_build_modeproblem_matrix_irrep_packed(
/// Target memory with capacity for ss->fecv_size**2 elements. If NULL, new will be allocated.
complex double *target,
const qpms_scatsys_t *ss, qpms_iri_t iri,
complex double k ///< Wave number to use in the translation matrix.
const qpms_scatsys_at_omega_t *ssw,
qpms_iri_t iri
);
/// Alternative implementation of qpms_scatsys_build_modeproblem_matrix_irrep_packed().
complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed_orbitorderR(
/// Alternative implementation of qpms_scatsysw_build_modeproblem_matrix_irrep_packed().
complex double *qpms_scatsysw_build_modeproblem_matrix_irrep_packed_orbitorderR(
/// Target memory with capacity for ss->fecv_size**2 elements. If NULL, new will be allocated.
complex double *target,
const qpms_scatsys_t *ss, qpms_iri_t iri,
complex double k ///< Wave number to use in the translation matrix.
const qpms_scatsys_at_omega_t *ssw,
qpms_iri_t iri
);
/// Alternative (serial reference) implementation of qpms_scatsys_build_modeproblem_matrix_irrep_packed().
complex double *qpms_scatsys_build_modeproblem_matrix_irrep_packed_orbitorder_serial(
/// Alternative (serial reference) implementation of qpms_scatsysw_build_modeproblem_matrix_irrep_packed().
complex double *qpms_scatsysw_build_modeproblem_matrix_irrep_packed_serial(
/// Target memory with capacity for ss->fecv_size**2 elements. If NULL, new will be allocated.
complex double *target,
const qpms_scatsys_t *ss, qpms_iri_t iri,
complex double k ///< Wave number to use in the translation matrix.
const qpms_scatsys_at_omega_t *ssw,
qpms_iri_t iri
);
/// LU factorisation (LAPACKE_zgetrf) result holder.
typedef struct qpms_ss_LU {
const qpms_scatsys_t *ss;
const qpms_scatsys_at_omega_t *ssw;
bool full; ///< true if full matrix; false if irrep-packed.
qpms_iri_t iri; ///< Irrep index if `full == false`.
/// LU decomposition array.
@ -307,33 +383,33 @@ typedef struct qpms_ss_LU {
void qpms_ss_LU_free(qpms_ss_LU);
/// Builds an LU-factorised mode/scattering problem \f$ (I - TS) \f$ matrix from scratch.
qpms_ss_LU qpms_scatsys_build_modeproblem_matrix_full_LU(
qpms_ss_LU qpms_scatsysw_build_modeproblem_matrix_full_LU(
complex double *target, ///< Pre-allocated target array. Optional (if NULL, new one is allocated).
int *target_piv, ///< Pre-allocated pivot array. Optional (if NULL, new one is allocated).
const qpms_scatsys_t *ss,
complex double k ///< Wave number to use in the translation matrix.
const qpms_scatsys_at_omega_t *ssw
);
/// Builds an irrep-packed LU-factorised mode/scattering problem matrix from scratch.
qpms_ss_LU qpms_scatsys_build_modeproblem_matrix_irrep_packed_LU(
qpms_ss_LU qpms_scatsysw_build_modeproblem_matrix_irrep_packed_LU(
complex double *target, ///< Pre-allocated target array. Optional (if NULL, new one is allocated).
int *target_piv, ///< Pre-allocated pivot array. Optional (if NULL, new one is allocated).
const qpms_scatsys_t *ss, qpms_iri_t iri,
complex double k ///< Wave number to use in the translation matrix.
const qpms_scatsys_at_omega_t *ssw,
qpms_iri_t iri
);
/// Computes LU factorisation of a pre-calculated mode/scattering problem matrix, replacing its contents.
qpms_ss_LU qpms_scatsys_modeproblem_matrix_full_factorise(
qpms_ss_LU qpms_scatsysw_modeproblem_matrix_full_factorise(
complex double *modeproblem_matrix_full, ///< Pre-calculated mode problem matrix (I-TS). Mandatory.
int *target_piv, ///< Pre-allocated pivot array. Optional (if NULL, new one is allocated).
const qpms_scatsys_t *ss
const qpms_scatsys_at_omega_t *ssw
);
/// Computes LU factorisation of a pre-calculated irrep-packed mode/scattering problem matrix, replacing its contents.
qpms_ss_LU qpms_scatsys_modeproblem_matrix_irrep_packed_factorise(
qpms_ss_LU qpms_scatsysw_modeproblem_matrix_irrep_packed_factorise(
complex double *modeproblem_matrix_irrep_packed, ///< Pre-calculated mode problem matrix (I-TS). Mandatory.
int *target_piv, ///< Pre-allocated pivot array. Optional (if NULL, new one is allocated).
const qpms_scatsys_t *ss, qpms_iri_t iri
const qpms_scatsys_at_omega_t *ssw,
qpms_iri_t iri
);
/// Solves a (possibly partial, irrep-packed) scattering problem \f$ (I-TS)f = Ta_\mathrm{inc} \f$ using a pre-factorised \f$ (I-TS) \f$.
@ -422,10 +498,10 @@ complex double *qpms_scatsys_incident_field_vector_full(
);
/// Applies T-matrices onto an incident field vector in the full basis.
complex double *qpms_scatsys_apply_Tmatrices_full(
complex double *qpms_scatsysw_apply_Tmatrices_full(
complex double *target_full, /// Target vector array. If NULL, a new one is allocated.
const complex double *inc_full, /// Incident field coefficient vector. Must not be NULL.
const qpms_scatsys_t *ss
const qpms_scatsys_at_omega_t *ssw
);

View File

@ -53,6 +53,15 @@ qpms_tmatrix_t *qpms_tmatrix_init(const qpms_vswf_set_spec_t *bspec) {
return t;
}
qpms_tmatrix_t *qpms_tmatrix_init_from_generator(
const qpms_vswf_set_spec_t *bspec,
qpms_tmatrix_generator_t gen,
complex double omega) {
qpms_tmatrix_t *t = qpms_tmatrix_init(bspec);
QPMS_ENSURE_SUCCESS(gen.function(t, omega, gen.params));
return t;
}
qpms_tmatrix_t *qpms_tmatrix_copy(const qpms_tmatrix_t *T) {
qpms_tmatrix_t *t = qpms_tmatrix_init(T->spec);
size_t n = T->spec->n;
@ -991,3 +1000,224 @@ qpms_errno_t qpms_tmatrix_generator_constant(qpms_tmatrix_t *t,
return QPMS_SUCCESS;
}
void qpms_tmatrix_operation_clear(qpms_tmatrix_operation_t *f) {
switch(f->typ) {
case QPMS_TMATRIX_OPERATION_NOOP:
break;
case QPMS_TMATRIX_OPERATION_LRMATRIX:
if(f->op.lrmatrix.owns_m)
free(f->op.lrmatrix.m);
break;
case QPMS_TMATRIX_OPERATION_SCMULZ:
if(f->op.scmulz.owns_m)
free(f->op.scmulz.m);
break;
case QPMS_TMATRIX_OPERATION_IROT3:
break;
case QPMS_TMATRIX_OPERATION_IROT3ARR:
if(f->op.irot3arr.owns_ops)
free(f->op.irot3arr.ops);
break;
case QPMS_TMATRIX_OPERATION_FINITE_GROUP_SYMMETRISE: // Group never owned
break;
case QPMS_TMATRIX_OPERATION_COMPOSE_SUM:
{
struct qpms_tmatrix_operation_compose_sum * const o =
&(f->op.compose_sum);
if(o->opmem) {
for(size_t i = 0; i < o->n; ++i)
qpms_tmatrix_operation_clear(&(o->opmem[i]));
free(o->opmem);
}
free(o->ops);
}
break;
case QPMS_TMATRIX_OPERATION_COMPOSE_CHAIN:
{
struct qpms_tmatrix_operation_compose_chain * const o =
&(f->op.compose_chain);
if(o->opmem) {
for(size_t i = 0; i < o->n; ++i)
if(o->ops_owned[i]) {
// A bit complicated yet imperfect sanity/bound check,
// but this way we at least get rid of the const discard warning.
ptrdiff_t d = o->ops[i] - o->opmem;
QPMS_ENSURE(d >= 0 && d < o->opmem_size,
"Bound check failed; is there a bug in the"
" construction of compose_chain operation?\n"
"opmem_size = %zu, o->ops[%zu] - o->opmem = %td.",
o->opmem_size, i, d);
qpms_tmatrix_operation_clear(o->opmem + d);
}
free(o->opmem);
free(o->ops_owned);
}
free(o->ops);
}
break;
default:
QPMS_WTF;
}
}
void qpms_tmatrix_operation_copy(qpms_tmatrix_operation_t *dest, const qpms_tmatrix_operation_t *src) {
memcpy(dest, src, sizeof(qpms_tmatrix_operation_t));
switch(dest->typ) {
case QPMS_TMATRIX_OPERATION_NOOP:
break;
case QPMS_TMATRIX_OPERATION_LRMATRIX:
QPMS_CRASHING_MALLOCPY(dest->op.lrmatrix.m, src->op.lrmatrix.m,
sizeof(complex double) * src->op.lrmatrix.m_size);
dest->op.lrmatrix.owns_m = true;
break;
case QPMS_TMATRIX_OPERATION_SCMULZ:
QPMS_CRASHING_MALLOCPY(dest->op.scmulz.m, src->op.scmulz.m,
sizeof(complex double) * src->op.scmulz.m_size);
dest->op.scmulz.owns_m = true;
break;
case QPMS_TMATRIX_OPERATION_IROT3:
break;
case QPMS_TMATRIX_OPERATION_IROT3ARR:
QPMS_CRASHING_MALLOCPY(dest->op.irot3arr.ops, src->op.irot3arr.ops,
src->op.irot3arr.n * sizeof(qpms_irot3_t));
dest->op.irot3arr.owns_ops = true;
break;
case QPMS_TMATRIX_OPERATION_FINITE_GROUP_SYMMETRISE: // Group never owned
break;
case QPMS_TMATRIX_OPERATION_COMPOSE_SUM:
{
struct qpms_tmatrix_operation_compose_sum * const o =
&(dest->op.compose_sum);
QPMS_CRASHING_MALLOC(o->ops, o->n * sizeof(*(o->ops)));
QPMS_CRASHING_MALLOC(o->opmem, o->n * sizeof(*(o->opmem)));
for(size_t i = 0; i < o->n; ++i) {
qpms_tmatrix_operation_copy(o->opmem + i, src->op.compose_sum.ops[i]);
o->ops[i] = o->opmem + i;
}
}
break;
case QPMS_TMATRIX_OPERATION_COMPOSE_CHAIN:
{
struct qpms_tmatrix_operation_compose_chain * const o =
&(dest->op.compose_chain);
QPMS_CRASHING_MALLOC(o->ops, o->n * sizeof(*(o->ops)));
QPMS_CRASHING_MALLOC(o->ops_owned, o->n * sizeof(*(o->ops_owned)));
QPMS_CRASHING_MALLOC(o->opmem, o->n * sizeof(*(o->opmem)));
for(size_t i = 0; i < o->n; ++i) {
qpms_tmatrix_operation_copy(o->opmem + i, src->op.compose_chain.ops[i]);
o->ops[i] = o->opmem + i;
o->ops_owned[i] = true;
}
}
break;
default:
QPMS_WTF;
}
dest->typ = src->typ;
}
void qpms_tmatrix_operation_compose_chain_init(qpms_tmatrix_operation_t *dest,
size_t nops, size_t opmem_size) {
if (nops == 0) {
QPMS_WARN("Tried to create a composed (chain) operation of zero size;"
"setting to no-op instead.");
*dest = qpms_tmatrix_operation_noop;
}
if (nops < opmem_size)
QPMS_WARN("Allocating buffer for %zu operations, in a chained operation of"
" only %zu elemens, that does not seem to make sense.", opmem_size, nops);
dest->typ = QPMS_TMATRIX_OPERATION_COMPOSE_CHAIN;
struct qpms_tmatrix_operation_compose_chain * const o =
&(dest->op.compose_chain);
o->n = nops;
QPMS_CRASHING_MALLOC(o->ops, nops * sizeof(*(o->ops)));
if (opmem_size != 0) {
QPMS_CRASHING_CALLOC(o->ops_owned, o->n, sizeof(_Bool));
QPMS_CRASHING_MALLOC(o->opmem, opmem_size * sizeof(*(o->opmem)));
} else {
o->ops_owned = NULL;
o->opmem = NULL;
}
o->opmem_size = opmem_size;
}
qpms_tmatrix_t *qpms_tmatrix_mv(qpms_tmatrix_t *dest,
const qpms_tmatrix_t *orig) {
QPMS_ENSURE(qpms_vswf_set_spec_isidentical(dest->spec, orig->spec),
"Basis specifications must be identical!");
memcpy(dest->m, orig->m, SQ(orig->spec->n)*sizeof(complex double));
return dest;
}
qpms_tmatrix_t *qpms_tmatrix_apply_operation_replace(qpms_tmatrix_t *dest,
const qpms_tmatrix_operation_t *op, const qpms_tmatrix_t *orig) {
//QPMS_ENSURE(qpms_vswf_set_spec_isidentical(dest->spec, orig->spec),
// "Basis specifications must be identical!");
// TODO should I check also for dest->owns_m?
// FIXME this is highly inoptimal; the hierarchy should be such
// that _operation() and operation_inplace() call this, and not the
// other way around
qpms_tmatrix_mv(dest, orig);
return qpms_tmatrix_apply_operation_inplace(op, dest);
}
qpms_tmatrix_t *qpms_tmatrix_apply_operation(
const qpms_tmatrix_operation_t *f, const qpms_tmatrix_t *orig) {
// Certain operations could be optimized, but the effect would be marginal.
qpms_tmatrix_t *res = qpms_tmatrix_copy(orig);
return qpms_tmatrix_apply_operation_inplace(f, res);
}
static qpms_tmatrix_t *qtao_compose_sum_inplace(qpms_tmatrix_t *T,
const struct qpms_tmatrix_operation_compose_sum *cs) {
qpms_tmatrix_t *tmp_target = qpms_tmatrix_init(T->spec);
qpms_tmatrix_t *sum = qpms_tmatrix_init(T->spec);
for (size_t i = 0; i < cs->n; ++i) {
memcpy(tmp_target->m, T->m, SQ(T->spec->n) * sizeof(complex double));
QPMS_ENSURE(qpms_tmatrix_apply_operation_inplace(cs->ops[i] , tmp_target),
"Got NULL pointer from qpms_tmatrix_apply_operation_inplace, hupsis!");
for (size_t j = 0; j < SQ(T->spec->n); ++j)
sum->m[j] += tmp_target->m[j];
}
for(size_t j = 0; j < SQ(T->spec->n); ++j)
T->m[j] = sum->m[j] * cs->factor;
qpms_tmatrix_free(sum);
qpms_tmatrix_free(tmp_target);
return T;
}
static qpms_tmatrix_t *qtao_compose_chain_inplace(qpms_tmatrix_t *T,
const struct qpms_tmatrix_operation_compose_chain *cc) {
for(size_t i = 0; i < cc->n; ++i)
qpms_tmatrix_apply_operation_inplace(cc->ops[i], T);
return T;
}
static qpms_tmatrix_t *qtao_scmulz_inplace(qpms_tmatrix_t *T,
const struct qpms_tmatrix_operation_scmulz *s) {
for(size_t i = 0; i < SQ(T->spec->n); ++i)
T->m[i] *= s->m[i];
return T;
}
qpms_tmatrix_t *qpms_tmatrix_apply_operation_inplace(
const qpms_tmatrix_operation_t *f, qpms_tmatrix_t *T) {
switch(f->typ) {
case QPMS_TMATRIX_OPERATION_NOOP:
return T;
case QPMS_TMATRIX_OPERATION_LRMATRIX:
return qpms_tmatrix_apply_symop_inplace(T, f->op.lrmatrix.m);
case QPMS_TMATRIX_OPERATION_IROT3:
return qpms_tmatrix_symmetrise_irot3arr_inplace(T, 1, &(f->op.irot3));
case QPMS_TMATRIX_OPERATION_FINITE_GROUP_SYMMETRISE:
return qpms_tmatrix_symmetrise_finite_group_inplace(T, f->op.finite_group);
case QPMS_TMATRIX_OPERATION_COMPOSE_SUM:
return qtao_compose_sum_inplace(T, &(f->op.compose_sum));
case QPMS_TMATRIX_OPERATION_COMPOSE_CHAIN:
return qtao_compose_chain_inplace(T, &(f->op.compose_chain));
case QPMS_TMATRIX_OPERATION_SCMULZ:
return qtao_scmulz_inplace(T, &(f->op.scmulz));
default:
QPMS_WTF;
}
}

View File

@ -8,6 +8,8 @@
#include "materials.h"
#include <stdio.h>
struct qpms_finite_group_t;
typedef struct qpms_finite_group_t qpms_finite_group_t;
@ -17,11 +19,21 @@ static inline complex double *qpms_tmatrix_row(qpms_tmatrix_t *t, size_t rowno){
}
/// Initialises a zero T-matrix.
/** \sa qpms_tmatrix_init_from_generator()
* \sa qpms_tmatrix_init_from_function() */
qpms_tmatrix_t *qpms_tmatrix_init(const qpms_vswf_set_spec_t *bspec);
/// Copies a T-matrix, allocating new array for the T-matrix data.
qpms_tmatrix_t *qpms_tmatrix_copy(const qpms_tmatrix_t *T);
/// Copies a T-matrix contents between two pre-allocated T-matrices.
/** orig->spec and dest->spec must be identical.
*
* \returns \a dest
*/
qpms_tmatrix_t *qpms_tmatrix_mv(qpms_tmatrix_t *dest, const qpms_tmatrix_t *orig);
/// Destroys a T-matrix.
void qpms_tmatrix_free(qpms_tmatrix_t *t);
@ -233,7 +245,7 @@ qpms_tmatrix_t *qpms_tmatrix_symmetrise_finite_group_inplace(
complex double *qpms_apply_tmatrix(
complex double *f_target, ///< Scattered field coefficient array of size T->spec->n; if NULL, a new one is allocated.
const complex double *a, ///< Incident field coefficient array of size T->spec->n.
const qpms_tmatrix_t *T
const qpms_tmatrix_t *T ///< T-matrix \a T to apply.
);
/// Generic T-matrix generator function that fills a pre-initialised qpms_tmatrix_t given a frequency.
@ -251,6 +263,13 @@ typedef struct qpms_tmatrix_generator_t {
const void *params; ///< Parameter pointer passed to the function.
} qpms_tmatrix_generator_t;
/// Initialises and evaluates a new T-matrix using a generator.
qpms_tmatrix_t *qpms_tmatrix_init_from_generator(
const qpms_vswf_set_spec_t *bspec,
qpms_tmatrix_generator_t gen,
complex double omega);
/// Implementation of qpms_matrix_generator_t that just copies a constant matrix.
/** N.B. this does almost no checks at all, so use it only for t-matrices with
* the same base spec.
@ -427,7 +446,7 @@ qpms_arc_function_retval_t qpms_arc_cylinder(double theta,
);
/// Arc parametrisation of spherical particle; for qpms_arc_function_t.
/** Useful mostly only for benchmarks, as one can use the Mie-Lorentz solution. */
/** Useful mostly only for benchmarks or debugging, as one can use the Mie-Lorentz solution. */
qpms_arc_function_retval_t qpms_arc_sphere(double theta,
const void *R; ///< Points to double containing particle's radius.
);
@ -503,6 +522,137 @@ qpms_errno_t qpms_tmatrix_axialsym_RQ_transposed_fill(complex double *target,
);
/// An "abstract" T-matrix, contains a T-matrix generator instead of actual data.
typedef struct qpms_tmatrix_function_t {
/** \brief VSWF basis specification, NOT owned by qpms_tmatrix_t by default.
*
* Usually not checked for meaningfulness by the functions (methods),
* so the caller should take care that \a spec->ilist does not
* contain any duplicities and that for each wave with order \a m
* there is also one with order \a m.
*/
const qpms_vswf_set_spec_t *spec;
const qpms_tmatrix_generator_t *gen; ///< A T-matrix generator function.
} qpms_tmatrix_function_t;
/// Convenience function to create a new T-matrix from qpms_tmatrix_function_t.
// FIXME the name is not very intuitive.
static inline qpms_tmatrix_t *qpms_tmatrix_init_from_function(qpms_tmatrix_function_t f, complex double omega) {
return qpms_tmatrix_init_from_generator(f.spec, *f.gen, omega);
}
/// Specifies different kinds of operations done on T-matrices
typedef enum {
QPMS_TMATRIX_OPERATION_NOOP, ///< Identity operation.
QPMS_TMATRIX_OPERATION_LRMATRIX, ///< General matrix transformation \f$ T' = MTM^\dagger \f$; see @ref qpms_tmatrix_operation_lrmatrix.
QPMS_TMATRIX_OPERATION_IROT3, ///< Single rotoreflection specified by a qpms_irot3_t.
QPMS_TMATRIX_OPERATION_IROT3ARR, ///< Symmetrise using an array of rotoreflection; see @ref qpms_tmatrix_operation_irot3arr.
QPMS_TMATRIX_OPERATION_COMPOSE_SUM, ///< Apply several transformations and sum the results, see @ref qpms_tmatrix_operation_compose_sum.
QPMS_TMATRIX_OPERATION_COMPOSE_CHAIN, ///< Chain several different transformations; see @ref qpms_tmatrix_operation_compose_chain.
QPMS_TMATRIX_OPERATION_SCMULZ, ///< Elementwise scalar multiplication with a complex matrix; see @ref qpms_tmatrix_operation_scmulz.
//QPMS_TMATRIX_OPERATION_POINTGROUP, ///< TODO the new point group in pointgroup.h
QPMS_TMATRIX_OPERATION_FINITE_GROUP_SYMMETRISE ///< Legacy qpms_finite_group_t with filled rep3d; see @ref qpms_tmatrix_operation_finite_group.
} qpms_tmatrix_operation_kind_t;
/// General matrix transformation \f[ T' = MTM^\dagger \f] spec for qpms_tmatrix_operation_t.
struct qpms_tmatrix_operation_lrmatrix {
/// Raw matrix data of \a M in row-major order.
/** The matrix must be taylored for the given bspec! */
complex double *m;
size_t m_size; ///< Total size of \a m matrix in terms of sizeof(complex double).
bool owns_m; ///< Whether \a m is owned by this;
};
struct qpms_tmatrix_operation_t; // Forward declaration for the composed operations.
/// Specifies a composed operation of type \f$ T' = c\sum_i f_i'(T) \f$ for qpms_tmatrix_operation_t.
struct qpms_tmatrix_operation_compose_sum {
size_t n; ///< Number of operations in ops;
const struct qpms_tmatrix_operation_t **ops; ///< Operations array. (Pointers array \a ops[] always owned by this.)
double factor; ///< Overall factor \a c.
/// (Optional) operations buffer into which elements of \a ops point.
/** Owned by this or NULL. If not NULL, all \a ops members are assumed to point into
* the memory held by \a opmem and to be properly initialised.
* Each \a ops member has to point to _different_ elements of \a opmem.
*/
struct qpms_tmatrix_operation_t *opmem;
};
/// Specifies a composed operation of type \f$ T' = f_{n-1}(f_{n-2}(\dots f_0(T)\dots))) \f$ for qpms_tmatrix_operation_t.
struct qpms_tmatrix_operation_compose_chain {
size_t n; ///< Number of operations in ops;
const struct qpms_tmatrix_operation_t **ops; ///< Operations array. (Pointers owned by this.)
struct qpms_tmatrix_operation_t *opmem; ///< (Optional) operations buffer into which elements of \a ops point. (Owned by this or NULL.)
size_t opmem_size; ///< Length of the opmem array.
_Bool *ops_owned; ///< True for all sub operations owned by this and saved in opmem. NULL if opmem is NULL.
};
/// Specifies an elementwise complex multiplication of type \f$ T'_{ij} = M_{ij}T_{ij} \f$ for qpms_tmatrix_operation_t.
struct qpms_tmatrix_operation_scmulz {
/// Raw matrix data of \a M in row-major order.
/** The matrix must be taylored for the given bspec! */
complex double *m;
size_t m_size; ///< Total size of \a m matrix in terms of sizeof(complex double).
bool owns_m; ///< Whether \a m is owned by this.
};
/// Specifies a symmetrisation using a set of rotoreflections (with equal weights) for qpms_tmatrix_operation_t.
/** Internally, this is evaluated using a call to qpms_symmetrise_tmdata_irot3arr()
* or qpms_symmetrise_tmdata_irot3arr_inplace(). */
struct qpms_tmatrix_operation_irot3arr {
size_t n; ///< Number of rotoreflections;
qpms_irot3_t *ops; ///< Rotoreflection array of size \a n.
bool owns_ops; ///< Whether \a ops array is owned by this.
};
/// A generic T-matrix transformation operator.
typedef struct qpms_tmatrix_operation_t {
/// Specifies the operation kind to be performed and which type \op actually contains.
qpms_tmatrix_operation_kind_t typ;
union {
struct qpms_tmatrix_operation_lrmatrix lrmatrix;
struct qpms_tmatrix_operation_scmulz scmulz;
qpms_irot3_t irot3; ///< Single rotoreflection; \a typ = QPMS_TMATRIX_OPERATION_IROT3
struct qpms_tmatrix_operation_irot3arr irot3arr;
struct qpms_tmatrix_operation_compose_sum compose_sum;
struct qpms_tmatrix_operation_compose_chain compose_chain;
/// Finite group for QPMS_TMATRIX_OPERATION_FINITE_GROUP_SYMMETRISE.
/** Not owned by this; \a rep3d must be filled. */
const qpms_finite_group_t *finite_group;
} op; ///< Operation data; actual type is determined by \a typ.
} qpms_tmatrix_operation_t;
static const qpms_tmatrix_operation_t qpms_tmatrix_operation_noop = {.typ = QPMS_TMATRIX_OPERATION_NOOP};
/// Apply an operation on a T-matrix, returning a newly allocated result.
qpms_tmatrix_t *qpms_tmatrix_apply_operation(const qpms_tmatrix_operation_t *op, const qpms_tmatrix_t *orig);
/// Apply an operation on a T-matrix and replace it with the result.
qpms_tmatrix_t *qpms_tmatrix_apply_operation_inplace(const qpms_tmatrix_operation_t *op, qpms_tmatrix_t *orig);
/// Apply an operation on a T-matrix and replace another one it with the result.
qpms_tmatrix_t *qpms_tmatrix_apply_operation_replace(qpms_tmatrix_t *dest,
const qpms_tmatrix_operation_t *op, const qpms_tmatrix_t *orig);
/// (Recursively) deallocates internal data of qpms_tmatrix_operation_t.
/** It does NOT clear the memory pointed to by op it self, but only
* heap-allocated data of certain operations, if labeled as owned by it.
* In case of compose operations, the recursion stops if the children are
* not owned by them (the opmem pointer is NULL).
*/
void qpms_tmatrix_operation_clear(qpms_tmatrix_operation_t *f);
/// (Recursively) copies an qpms_tmatrix_operation_t.
/** Makes copies of all the internal data and takes ownership over them if needed */
void qpms_tmatrix_operation_copy(qpms_tmatrix_operation_t *target, const qpms_tmatrix_operation_t *src);
/// Inits a new "chain" of composed operations, some of which might be owned.
void qpms_tmatrix_operation_compose_chain_init(
qpms_tmatrix_operation_t *target, ///< The operation structure that will be set to the chain.
size_t nops, ///< Number of chained operations (length of the \a ops array)
size_t opmem_size ///< Size of the own operations buffer (length of the \a opmem array)
);
#if 0
// Abstract types that describe T-matrix/particle/scatsystem symmetries
// To be implemented later. See also the thoughts in the beginning of groups.h.

15
qpms/tolerances.h Normal file
View File

@ -0,0 +1,15 @@
/*! \file tolerances.h */
#ifndef QPMS_TOLERANCES_H
#define QPMS_TOLERANCES_H
// TODO DOC
typedef struct qpms_tolerance_spec_t {
double atol;
double rtol;
} qpms_tolerance_spec_t;
/// A rather arbitrary default tolerance.
static const qpms_tolerance_spec_t QPMS_TOLERANCE_DEFAULT = {.atol = 1e-15, .rtol = 1e-8};
#endif // QPMS_TOLERANCES_H