void MMM1D_recalcTables() { /* polygamma, determine order */ int n; double err; double rhomax2nm2, rhomax2 = uz2*mmm1d_params.far_switch_radius_2; /* rhomax2 < 1, so rhomax2m2 falls monotonously */ n = 1; rhomax2nm2 = 1.0; do { create_mod_psi_up_to(n+1); /* |uz*z| <= 0.5 */ err = 2*n*fabs(mod_psi_even(n, 0.5))*rhomax2nm2; rhomax2nm2 *= rhomax2; n++; // fprintf(stderr, "%f\n", err); } while (err > 0.1*mmm1d_params.maxPWerror); }
static void prepare_polygamma_series(double maxPWerror, double maxrad2) { /* polygamma, determine order */ int n; double err; double rhomax2nm2, rhomax2 = uz2*maxrad2; /* rhomax2 < 1, so rhomax2m2 falls monotonously */ n = 1; rhomax2nm2 = 1.0; do { create_mod_psi_up_to(n+1); /* |uz*z| <= 0.5 */ err = 2*n*fabs(mod_psi_even(n, 0.5))*rhomax2nm2; rhomax2nm2 *= rhomax2; n++; // fprintf(stderr, "%f\n", err); } while (err > 0.1*maxPWerror); }
double mmm1d_coulomb_pair_energy(Particle *p1, Particle *p2, double d[3], double r2, double r) { double chpref = p1->p.q*p2->p.q; double rxy2, rxy2_d, z_d; double E; if (chpref == 0) return 0; rxy2 = d[0]*d[0] + d[1]*d[1]; rxy2_d = rxy2*uz2; z_d = d[2]*uz; if (rxy2 <= mmm1d_params.far_switch_radius_2) { /* near range formula */ double r2n, rt, shift_z; int n; E = -2*C_GAMMA; /* polygamma summation */ r2n = 1.0; for (n = 0; n < n_modPsi; n++) { double add = mod_psi_even(n, z_d)*r2n; E -= add; if (fabs(add) < mmm1d_params.maxPWerror) break; r2n *= rxy2_d; } E *= coulomb.prefactor*uz; /* real space parts */ E += coulomb.prefactor/r; shift_z = d[2] + box_l[2]; rt = sqrt(rxy2 + shift_z*shift_z); E += coulomb.prefactor/rt; shift_z = d[2] - box_l[2]; rt = sqrt(rxy2 + shift_z*shift_z); E += coulomb.prefactor/rt; } else { /* far range formula */ double rxy = sqrt(rxy2); double rxy_d = rxy*uz; int bp; /* The first Bessel term will compensate a little bit the log term, so add them close together */ E = -0.25*log(rxy2_d) + 0.5*(M_LN2 - C_GAMMA); for (bp = 1; bp < mmm1d_params.bessel_cutoff; bp++) { double fq = C_2PI*bp; E += K0(fq*rxy_d)*cos(fq*z_d); } E *= 4*coulomb.prefactor*uz; } return chpref*E; }
void add_mmm1d_coulomb_pair_force(double chpref, double d[3], double r2, double r, double force[3]) { int dim; double F[3]; double rxy2, rxy2_d, z_d; double pref; double Fx, Fy, Fz; rxy2 = d[0]*d[0] + d[1]*d[1]; rxy2_d = rxy2*uz2; z_d = d[2]*uz; if (rxy2 <= mmm1d_params.far_switch_radius_2) { /* near range formula */ double sr, sz, r2nm1, rt, rt2, shift_z; int n; /* polygamma summation */ sr = 0; sz = mod_psi_odd(0, z_d); r2nm1 = 1.0; for (n = 1; n < n_modPsi; n++) { double deriv = 2*n; double mpe = mod_psi_even(n, z_d); double mpo = mod_psi_odd(n, z_d); double r2n = r2nm1*rxy2_d; sz += r2n*mpo; sr += deriv*r2nm1*mpe; if (fabs(deriv*r2nm1*mpe) < mmm1d_params.maxPWerror) break; r2nm1 = r2n; } // fprintf(stderr, "max_n %d\n", n); Fx = prefL3_i*sr*d[0]; Fy = prefL3_i*sr*d[1]; Fz = prefuz2*sz; /* real space parts */ pref = coulomb.prefactor/(r2*r); Fx += pref*d[0]; Fy += pref*d[1]; Fz += pref*d[2]; shift_z = d[2] + box_l[2]; rt2 = rxy2 + shift_z*shift_z; rt = sqrt(rt2); pref = coulomb.prefactor/(rt2*rt); Fx += pref*d[0]; Fy += pref*d[1]; Fz += pref*shift_z; shift_z = d[2] - box_l[2]; rt2 = rxy2 + shift_z*shift_z; rt = sqrt(rt2); pref = coulomb.prefactor/(rt2*rt); Fx += pref*d[0]; Fy += pref*d[1]; Fz += pref*shift_z; F[0] = Fx; F[1] = Fy; F[2] = Fz; } else { /* far range formula */ double rxy = sqrt(rxy2); double rxy_d = rxy*uz; double sr = 0, sz = 0; int bp; for (bp = 1; bp < mmm1d_params.bessel_cutoff; bp++) { double fq = C_2PI*bp, k0, k1; #ifdef BESSEL_MACHINE_PREC k0 = K0(fq*rxy_d); k1 = K1(fq*rxy_d); #else LPK01(fq*rxy_d, &k0, &k1); #endif sr += bp*k1*cos(fq*z_d); sz += bp*k0*sin(fq*z_d); } sr *= uz2*4*C_2PI; sz *= uz2*4*C_2PI; pref = coulomb.prefactor*(sr/rxy + 2*uz/rxy2); F[0] = pref*d[0]; F[1] = pref*d[1]; F[2] = coulomb.prefactor*sz; } for (dim = 0; dim < 3; dim++) force[dim] += chpref * F[dim]; }