Complex k support for new ewald sums.
Basic tests give good results (only up to rounding error differences compared to the previous versions), the time increase is almost negligible. Former-commit-id: 0907517ca94f130a0d8b140ba7b525653e24090f
This commit is contained in:
parent
d2b34f9407
commit
fd1aed02ca
58
qpms/ewald.c
58
qpms/ewald.c
|
@ -176,11 +176,11 @@ void qpms_ewald32_constants_free(qpms_ewald32_constants_t *c) {
|
||||||
|
|
||||||
int ewald3_sigma0(complex double *result, double *err,
|
int ewald3_sigma0(complex double *result, double *err,
|
||||||
const qpms_ewald32_constants_t *c,
|
const qpms_ewald32_constants_t *c,
|
||||||
const double eta, const double k)
|
const double eta, const complex double k)
|
||||||
{
|
{
|
||||||
qpms_csf_result gam;
|
qpms_csf_result gam;
|
||||||
int retval = complex_gamma_inc_e(-0.5, -sq(k/(2*eta)), &gam);
|
int retval = complex_gamma_inc_e(-0.5, -csq(k/(2*eta)), &gam);
|
||||||
gam.val = conj(gam.val); // We take the other branch, cf. [Linton, p. 642 in the middle]
|
// FIXME DO THIS CORRECTLY gam.val = conj(gam.val); // We take the other branch, cf. [Linton, p. 642 in the middle]
|
||||||
if (0 != retval)
|
if (0 != retval)
|
||||||
abort();
|
abort();
|
||||||
*result = gam.val * c->legendre0[gsl_sf_legendre_array_index(0,0)] / 2 / M_SQRTPI;
|
*result = gam.val * c->legendre0[gsl_sf_legendre_array_index(0,0)] / 2 / M_SQRTPI;
|
||||||
|
@ -381,7 +381,7 @@ int ewald3_21_xy_sigma_long (
|
||||||
complex double *target, // must be c->nelem_sc long
|
complex double *target, // must be c->nelem_sc long
|
||||||
double *err,
|
double *err,
|
||||||
const qpms_ewald32_constants_t *c,
|
const qpms_ewald32_constants_t *c,
|
||||||
const double eta, const double k /* TODO COMPLEX */,
|
const double eta, const complex double k,
|
||||||
const double unitcell_volume /* with the corresponding lattice dimensionality */,
|
const double unitcell_volume /* with the corresponding lattice dimensionality */,
|
||||||
const LatticeDimensionality latdim,
|
const LatticeDimensionality latdim,
|
||||||
PGen *pgen_K, const bool pgen_generates_shifted_points
|
PGen *pgen_K, const bool pgen_generates_shifted_points
|
||||||
|
@ -394,6 +394,7 @@ int ewald3_21_xy_sigma_long (
|
||||||
const cart3_t particle_shift
|
const cart3_t particle_shift
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
const bool k_is_real = (cimag(k) == 0);
|
||||||
assert((latdim & LAT_XYONLY) && (latdim & SPACE3D));
|
assert((latdim & LAT_XYONLY) && (latdim & SPACE3D));
|
||||||
assert((latdim & LAT1D) || (latdim & LAT2D));
|
assert((latdim & LAT1D) || (latdim & LAT2D));
|
||||||
const qpms_y_t nelem_sc = c->nelem_sc;
|
const qpms_y_t nelem_sc = c->nelem_sc;
|
||||||
|
@ -408,8 +409,9 @@ int ewald3_21_xy_sigma_long (
|
||||||
memset(err, 0, nelem_sc * sizeof(double));
|
memset(err, 0, nelem_sc * sizeof(double));
|
||||||
}
|
}
|
||||||
|
|
||||||
const double commonfac = 1/(k*k*unitcell_volume); // used in the very end (CFC) //TODO COMPLEX
|
const complex double commonfac = 1/(k*k*unitcell_volume); // used in the very end (CFC)
|
||||||
assert(commonfac > 0);
|
if (k_is_real)
|
||||||
|
assert(creal(commonfac) > 0);
|
||||||
|
|
||||||
PGenSphReturnData pgen_retdata;
|
PGenSphReturnData pgen_retdata;
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
@ -418,7 +420,7 @@ int ewald3_21_xy_sigma_long (
|
||||||
// recycleable values if rbeta_pq stays the same:
|
// recycleable values if rbeta_pq stays the same:
|
||||||
complex double gamma_pq;
|
complex double gamma_pq;
|
||||||
complex double z;
|
complex double z;
|
||||||
double factor1d = 1; // the "additional" factor for the 1D case (then it is not 1)
|
complex double factor1d = 1; // the "additional" factor for the 1D case (then it is not 1)
|
||||||
// space for Gamma_pq[j]'s
|
// space for Gamma_pq[j]'s
|
||||||
qpms_csf_result Gamma_pq[lMax/2+1];
|
qpms_csf_result Gamma_pq[lMax/2+1];
|
||||||
|
|
||||||
|
@ -452,18 +454,18 @@ int ewald3_21_xy_sigma_long (
|
||||||
|
|
||||||
// R-DEPENDENT BEGIN
|
// R-DEPENDENT BEGIN
|
||||||
if (new_rbeta_pq) {
|
if (new_rbeta_pq) {
|
||||||
gamma_pq = lilgamma(rbeta_pq/k /*TODO COMPLEX*/);
|
gamma_pq = clilgamma(rbeta_pq/k);
|
||||||
z = csq(gamma_pq*k/(2*eta)); // Když o tom tak přemýšlím, tak tohle je vlastně vždy reálné
|
z = csq(gamma_pq*k/(2*eta));
|
||||||
for(qpms_l_t j = 0; j <= lMax/2; ++j) {
|
for(qpms_l_t j = 0; j <= lMax/2; ++j) {
|
||||||
// TODO COMPLEX FIXME check the branches in the old lilgamma case
|
// TODO COMPLEX FIXME check the branches in the old lilgamma case
|
||||||
int retval = complex_gamma_inc_e(0.5-j, z, Gamma_pq+j);
|
int retval = complex_gamma_inc_e(0.5-j, z, Gamma_pq+j);
|
||||||
// we take the other branch, cf. [Linton, p. 642 in the middle]: FIXME instead use the C11 CMPLX macros and fill in -O*I part to z in the line above
|
// we take the other branch, cf. [Linton, p. 642 in the middle]: FIXME instead use the C11 CMPLX macros and fill in -O*I part to z in the line above
|
||||||
if(creal(z) < 0)
|
//if(creal(z) < 0)
|
||||||
Gamma_pq[j].val = conj(Gamma_pq[j].val); //FIXME as noted above
|
// Gamma_pq[j].val = conj(Gamma_pq[j].val); //FIXME as noted above
|
||||||
if(!(retval==0 || retval==GSL_EUNDRFLW)) abort();
|
if(!(retval==0 || retval==GSL_EUNDRFLW)) abort();
|
||||||
}
|
}
|
||||||
if (latdim & LAT1D)
|
if (latdim & LAT1D)
|
||||||
factor1d = k /*TODO COMPLEX */ * M_SQRT1_2 * .5 * gamma_pq;
|
factor1d = M_SQRT1_2 * .5 * k * gamma_pq;
|
||||||
}
|
}
|
||||||
// R-DEPENDENT END
|
// R-DEPENDENT END
|
||||||
|
|
||||||
|
@ -479,7 +481,7 @@ int ewald3_21_xy_sigma_long (
|
||||||
double jsum_err, jsum_err_c; kahaninit(&jsum_err, &jsum_err_c); // TODO do I really need to kahan sum errors?
|
double jsum_err, jsum_err_c; kahaninit(&jsum_err, &jsum_err_c); // TODO do I really need to kahan sum errors?
|
||||||
assert((n-abs(m))/2 == c->s1_jMaxes[y]);
|
assert((n-abs(m))/2 == c->s1_jMaxes[y]);
|
||||||
for(qpms_l_t j = 0; j <= c->s1_jMaxes[y]/*(n-abs(m))/2*/; ++j) { // FIXME </<= ?
|
for(qpms_l_t j = 0; j <= c->s1_jMaxes[y]/*(n-abs(m))/2*/; ++j) { // FIXME </<= ?
|
||||||
complex double summand = pow(rbeta_pq/k, n-2*j)
|
complex double summand = cpow(rbeta_pq/k, n-2*j)
|
||||||
* e_imalpha_pq * c->legendre0[gsl_sf_legendre_array_index(n,abs(m))] * min1pow_m_neg(m) // This line can actually go outside j-loop
|
* e_imalpha_pq * c->legendre0[gsl_sf_legendre_array_index(n,abs(m))] * min1pow_m_neg(m) // This line can actually go outside j-loop
|
||||||
* cpow(gamma_pq, 2*j-1) // * Gamma_pq[j] bellow (GGG) after error computation
|
* cpow(gamma_pq, 2*j-1) // * Gamma_pq[j] bellow (GGG) after error computation
|
||||||
* c->s1_constfacs[y][j];
|
* c->s1_constfacs[y][j];
|
||||||
|
@ -516,7 +518,7 @@ int ewald3_1_z_sigma_long (
|
||||||
complex double *target, // must be c->nelem_sc long
|
complex double *target, // must be c->nelem_sc long
|
||||||
double *err,
|
double *err,
|
||||||
const qpms_ewald32_constants_t *c,
|
const qpms_ewald32_constants_t *c,
|
||||||
const double eta, const double k,
|
const double eta, const complex double k,
|
||||||
const double unitcell_volume /* length (periodicity) in this case */,
|
const double unitcell_volume /* length (periodicity) in this case */,
|
||||||
const LatticeDimensionality latdim,
|
const LatticeDimensionality latdim,
|
||||||
PGen *pgen_K, const bool pgen_generates_shifted_points
|
PGen *pgen_K, const bool pgen_generates_shifted_points
|
||||||
|
@ -573,13 +575,13 @@ int ewald3_1_z_sigma_long (
|
||||||
const complex double phasefac = cexp(I * K_z * particle_shift_z); // POINT-DEPENDENT (PFC) // !!!CHECKSIGN!!!
|
const complex double phasefac = cexp(I * K_z * particle_shift_z); // POINT-DEPENDENT (PFC) // !!!CHECKSIGN!!!
|
||||||
|
|
||||||
// R-DEPENDENT BEGIN
|
// R-DEPENDENT BEGIN
|
||||||
complex double gamma_pq = lilgamma(rbeta_mu/k); // For real beta and k this is real or pure imaginary ...
|
complex double gamma_pq = clilgamma(rbeta_mu/k); // For real beta and k this is real or pure imaginary ...
|
||||||
const complex double z = csq(gamma_pq*k/(2*eta));// ... so the square (this) is in fact real.
|
const complex double z = csq(gamma_pq*k/(2*eta));// ... so the square (this) is in fact real.
|
||||||
for(qpms_l_t j = 0; j <= lMax/2; ++j) {
|
for(qpms_l_t j = 0; j <= lMax/2; ++j) {
|
||||||
int retval = complex_gamma_inc_e(0.5-j, z, Gamma_pq+j);
|
int retval = complex_gamma_inc_e(0.5-j, z, Gamma_pq+j);
|
||||||
// we take the other branch, cf. [Linton, p. 642 in the middle]: FIXME instead use the C11 CMPLX macros and fill in -O*I part to z in the line above
|
// we take the other branch, cf. [Linton, p. 642 in the middle]: FIXME instead use the C11 CMPLX macros and fill in -O*I part to z in the line above
|
||||||
if(creal(z) < 0)
|
//if(creal(z) < 0)
|
||||||
Gamma_pq[j].val = conj(Gamma_pq[j].val); //FIXME as noted above
|
// Gamma_pq[j].val = conj(Gamma_pq[j].val); //FIXME as noted above
|
||||||
if(!(retval==0 || retval==GSL_EUNDRFLW)) abort();
|
if(!(retval==0 || retval==GSL_EUNDRFLW)) abort();
|
||||||
}
|
}
|
||||||
// R-DEPENDENT END
|
// R-DEPENDENT END
|
||||||
|
@ -632,7 +634,7 @@ int ewald3_sigma_long (
|
||||||
complex double *target, // must be c->nelem_sc long
|
complex double *target, // must be c->nelem_sc long
|
||||||
double *err,
|
double *err,
|
||||||
const qpms_ewald32_constants_t *c,
|
const qpms_ewald32_constants_t *c,
|
||||||
const double eta, const double k,
|
const double eta, const complex double k,
|
||||||
const double unitcell_volume /* with the corresponding lattice dimensionality */,
|
const double unitcell_volume /* with the corresponding lattice dimensionality */,
|
||||||
const LatticeDimensionality latdim,
|
const LatticeDimensionality latdim,
|
||||||
PGen *pgen_K, const bool pgen_generates_shifted_points
|
PGen *pgen_K, const bool pgen_generates_shifted_points
|
||||||
|
@ -880,7 +882,7 @@ int ewald3_sigma_short(
|
||||||
complex double *target, // must be c->nelem_sc long
|
complex double *target, // must be c->nelem_sc long
|
||||||
double *err, // must be c->nelem_sc long or NULL
|
double *err, // must be c->nelem_sc long or NULL
|
||||||
const qpms_ewald32_constants_t *c,
|
const qpms_ewald32_constants_t *c,
|
||||||
const double eta, const double k /* TODO COMPLEX */,
|
const double eta, const complex double k /* TODO COMPLEX */,
|
||||||
const LatticeDimensionality latdim, // apart from asserts and possible optimisations ignored, as the SR formula stays the same
|
const LatticeDimensionality latdim, // apart from asserts and possible optimisations ignored, as the SR formula stays the same
|
||||||
PGen *pgen_R, const bool pgen_generates_shifted_points
|
PGen *pgen_R, const bool pgen_generates_shifted_points
|
||||||
/* If false, the behaviour corresponds to the old ewald32_sigma_short_points_and_shift,
|
/* If false, the behaviour corresponds to the old ewald32_sigma_short_points_and_shift,
|
||||||
|
@ -893,6 +895,8 @@ int ewald3_sigma_short(
|
||||||
const cart3_t particle_shift
|
const cart3_t particle_shift
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
const bool k_is_real = (cimag(k) == 0); // TODO check how the compiler optimises the loops
|
||||||
|
const double kreal = creal(k);
|
||||||
const qpms_y_t nelem_sc = c->nelem_sc;
|
const qpms_y_t nelem_sc = c->nelem_sc;
|
||||||
const qpms_l_t lMax = c->lMax;
|
const qpms_l_t lMax = c->lMax;
|
||||||
gsl_integration_workspace *workspace =
|
gsl_integration_workspace *workspace =
|
||||||
|
@ -916,6 +920,7 @@ int ewald3_sigma_short(
|
||||||
#endif
|
#endif
|
||||||
// recyclable variables if r_pq_shifted stays the same:
|
// recyclable variables if r_pq_shifted stays the same:
|
||||||
double intres[lMax+1], interr[lMax+1];
|
double intres[lMax+1], interr[lMax+1];
|
||||||
|
complex double cintres[lMax+1];
|
||||||
|
|
||||||
// CHOOSE POINT BEGIN
|
// CHOOSE POINT BEGIN
|
||||||
// TODO check whether _next_sph is the optimal coordinate system choice here
|
// TODO check whether _next_sph is the optimal coordinate system choice here
|
||||||
|
@ -971,11 +976,18 @@ int ewald3_sigma_short(
|
||||||
}
|
}
|
||||||
|
|
||||||
for(qpms_l_t n = 0; n <= lMax; ++n) {
|
for(qpms_l_t n = 0; n <= lMax; ++n) {
|
||||||
const double complex prefacn = - I * pow(2./k /*TODO COMPLEX*/, n+1) * M_2_SQRTPI / 2; // profiling TODO put outside the R-loop and multiply in the end?
|
const double complex prefacn = - I * (k_is_real ? pow(2./creal(k),n+1) : cpow(2./k, n+1)) * M_2_SQRTPI / 2; // profiling TODO put outside the R-loop and multiply in the end?
|
||||||
const double R_pq_pown = pow(r_pq_shifted, n); // profiling TODO: maybe put this into the new_r_pq_shifted condition as well?
|
const double R_pq_pown = pow(r_pq_shifted, n); // profiling TODO: maybe put this into the new_r_pq_shifted condition as well?
|
||||||
if (new_r_pq_shifted) {
|
if (new_r_pq_shifted) {
|
||||||
int retval = ewald32_sr_integral(r_pq_shifted, k /*TODO COMPLEX*/, n, eta,
|
int retval;
|
||||||
intres + n, interr + n, workspace);
|
if (k_is_real) {
|
||||||
|
double intres_real;
|
||||||
|
retval = ewald32_sr_integral(r_pq_shifted, kreal, n, eta,
|
||||||
|
&intres_real, interr + n, workspace);
|
||||||
|
cintres[n] = intres_real;
|
||||||
|
} else
|
||||||
|
retval = ewald32_sr_integral_ck(r_pq_shifted, k, n, eta,
|
||||||
|
cintres+n, interr + n, workspace);
|
||||||
if (retval) abort();
|
if (retval) abort();
|
||||||
} // otherwise recycle the integrals
|
} // otherwise recycle the integrals
|
||||||
for (qpms_m_t m = -n; m <= n; ++m){
|
for (qpms_m_t m = -n; m <= n; ++m){
|
||||||
|
@ -1000,7 +1012,7 @@ int ewald3_sigma_short(
|
||||||
kahanadd(err + y, err_c + y, cabs(leg * (prefacn / I) * R_pq_pown
|
kahanadd(err + y, err_c + y, cabs(leg * (prefacn / I) * R_pq_pown
|
||||||
* interr[n])); // TODO include also other errors
|
* interr[n])); // TODO include also other errors
|
||||||
ckahanadd(target + y, target_c + y,
|
ckahanadd(target + y, target_c + y,
|
||||||
prefacn * R_pq_pown * leg * intres[n] * e_beta_Rpq * e_imf * min1pow_m_neg(m));
|
prefacn * R_pq_pown * leg * cintres[n] * e_beta_Rpq * e_imf * min1pow_m_neg(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,14 +145,14 @@ int ewald32_sr_integral(double r, double k, double n, double eta, double *result
|
||||||
|
|
||||||
int ewald3_sigma0(complex double *result, double *err,
|
int ewald3_sigma0(complex double *result, double *err,
|
||||||
const qpms_ewald32_constants_t *c,
|
const qpms_ewald32_constants_t *c,
|
||||||
double eta, double k
|
double eta, complex double k
|
||||||
);
|
);
|
||||||
|
|
||||||
int ewald3_sigma_short(
|
int ewald3_sigma_short(
|
||||||
complex double *target_sigmasr_y, // must be c->nelem_sc long
|
complex double *target_sigmasr_y, // must be c->nelem_sc long
|
||||||
double *target_sigmasr_y_err, // must be c->nelem_sc long or NULL
|
double *target_sigmasr_y_err, // must be c->nelem_sc long or NULL
|
||||||
const qpms_ewald32_constants_t *c,
|
const qpms_ewald32_constants_t *c,
|
||||||
const double eta, const double k,
|
const double eta, const complex double k,
|
||||||
const LatticeDimensionality latdim, // apart from asserts and possible optimisations ignored, as the SR formula stays the same
|
const LatticeDimensionality latdim, // apart from asserts and possible optimisations ignored, as the SR formula stays the same
|
||||||
PGen *pgen_R, const bool pgen_generates_shifted_points
|
PGen *pgen_R, const bool pgen_generates_shifted_points
|
||||||
/* If false, the behaviour corresponds to the old ewald32_sigma_short_points_and_shift,
|
/* If false, the behaviour corresponds to the old ewald32_sigma_short_points_and_shift,
|
||||||
|
@ -169,7 +169,7 @@ int ewald3_sigma_long( // calls ewald3_21_sigma_long or ewald3_3_sigma_long, dep
|
||||||
complex double *target_sigmalr_y, // must be c->nelem_sc long
|
complex double *target_sigmalr_y, // must be c->nelem_sc long
|
||||||
double *target_sigmalr_y_err, // must be c->nelem_sc long or NULL
|
double *target_sigmalr_y_err, // must be c->nelem_sc long or NULL
|
||||||
const qpms_ewald32_constants_t *c,
|
const qpms_ewald32_constants_t *c,
|
||||||
const double eta, const double k,
|
const double eta, const complex double k,
|
||||||
const double unitcell_volume /* with the corresponding lattice dimensionality */,
|
const double unitcell_volume /* with the corresponding lattice dimensionality */,
|
||||||
const LatticeDimensionality latdim,
|
const LatticeDimensionality latdim,
|
||||||
PGen *pgen_K, const bool pgen_generates_shifted_points
|
PGen *pgen_K, const bool pgen_generates_shifted_points
|
||||||
|
|
Loading…
Reference in New Issue