static void get_induced_dipole_field(struct efp *efp, size_t frag_idx, struct polarizable_pt *pt, vec_t *field, vec_t *field_conj) { struct frag *fr_i = efp->frags + frag_idx; *field = vec_zero; *field_conj = vec_zero; for (size_t j = 0; j < efp->n_frag; j++) { if (j == frag_idx || efp_skip_frag_pair(efp, frag_idx, j)) continue; struct frag *fr_j = efp->frags + j; struct swf swf = efp_make_swf(efp, fr_i, fr_j); for (size_t jj = 0; jj < fr_j->n_polarizable_pts; jj++) { struct polarizable_pt *pt_j = fr_j->polarizable_pts + jj; size_t idx = fr_j->polarizable_offset + jj; vec_t dr = { pt->x - pt_j->x + swf.cell.x, pt->y - pt_j->y + swf.cell.y, pt->z - pt_j->z + swf.cell.z }; double r = vec_len(&dr); double r3 = r * r * r; double r5 = r3 * r * r; double t1 = vec_dot(&efp->indip[idx], &dr); double t2 = vec_dot(&efp->indipconj[idx], &dr); double p1 = 1.0; if (efp->opts.pol_damp == EFP_POL_DAMP_TT) p1 = efp_get_pol_damp_tt(r, fr_i->pol_damp, fr_j->pol_damp); field->x -= swf.swf * p1 * (efp->indip[idx].x / r3 - 3.0 * t1 * dr.x / r5); field->y -= swf.swf * p1 * (efp->indip[idx].y / r3 - 3.0 * t1 * dr.y / r5); field->z -= swf.swf * p1 * (efp->indip[idx].z / r3 - 3.0 * t1 * dr.z / r5); field_conj->x -= swf.swf * p1 * (efp->indipconj[idx].x / r3 - 3.0 * t2 * dr.x / r5); field_conj->y -= swf.swf * p1 * (efp->indipconj[idx].y / r3 - 3.0 * t2 * dr.y / r5); field_conj->z -= swf.swf * p1 * (efp->indipconj[idx].z / r3 - 3.0 * t2 * dr.z / r5); } } }
static mat_t get_int_mat(const struct efp *efp, size_t i, size_t j, size_t ii, size_t jj) { mat_t m; const struct frag *fr_i = efp->frags + i; const struct frag *fr_j = efp->frags + j; const struct polarizable_pt *pt_i = fr_i->polarizable_pts + ii; const struct polarizable_pt *pt_j = fr_j->polarizable_pts + jj; struct swf swf = efp_make_swf(efp, fr_i, fr_j); vec_t dr = { pt_j->x - pt_i->x - swf.cell.x, pt_j->y - pt_i->y - swf.cell.y, pt_j->z - pt_i->z - swf.cell.z }; double p1 = 1.0; double r = vec_len(&dr); double r3 = r * r * r; double r5 = r3 * r * r; if (efp->opts.pol_damp == EFP_POL_DAMP_TT) p1 = efp_get_pol_damp_tt(r, fr_i->pol_damp, fr_j->pol_damp); m.xx = swf.swf * p1 * (3.0 * dr.x * dr.x / r5 - 1.0 / r3); m.xy = swf.swf * p1 * 3.0 * dr.x * dr.y / r5; m.xz = swf.swf * p1 * 3.0 * dr.x * dr.z / r5; m.yx = swf.swf * p1 * 3.0 * dr.y * dr.x / r5; m.yy = swf.swf * p1 * (3.0 * dr.y * dr.y / r5 - 1.0 / r3); m.yz = swf.swf * p1 * 3.0 * dr.y * dr.z / r5; m.zx = swf.swf * p1 * 3.0 * dr.z * dr.x / r5; m.zy = swf.swf * p1 * 3.0 * dr.z * dr.y / r5; m.zz = swf.swf * p1 * (3.0 * dr.z * dr.z / r5 - 1.0 / r3); return m; }
static void compute_grad_point(struct efp *efp, size_t frag_idx, size_t pt_idx) { const struct frag *fr_i = efp->frags + frag_idx; const struct polarizable_pt *pt_i = fr_i->polarizable_pts + pt_idx; size_t idx_i = fr_i->polarizable_offset + pt_idx; vec_t dipole_i = { 0.5 * (efp->indip[idx_i].x + efp->indipconj[idx_i].x), 0.5 * (efp->indip[idx_i].y + efp->indipconj[idx_i].y), 0.5 * (efp->indip[idx_i].z + efp->indipconj[idx_i].z) }; for (size_t j = 0; j < efp->n_frag; j++) { if (j == frag_idx || efp_skip_frag_pair(efp, frag_idx, j)) continue; struct frag *fr_j = efp->frags + j; struct swf swf = efp_make_swf(efp, fr_i, fr_j); /* energy without switching applied */ double energy = 0.0; /* induced dipole - nuclei */ for (size_t k = 0; k < fr_j->n_atoms; k++) { struct efp_atom *at_j = fr_j->atoms + k; vec_t dr = { at_j->x - pt_i->x - swf.cell.x, at_j->y - pt_i->y - swf.cell.y, at_j->z - pt_i->z - swf.cell.z }; double p1 = 1.0, p2 = 0.0; if (efp->opts.pol_damp == EFP_POL_DAMP_TT) { double r = vec_len(&dr); p1 = efp_get_pol_damp_tt(r, fr_i->pol_damp, fr_j->pol_damp); p2 = efp_get_pol_damp_tt_grad(r, fr_i->pol_damp, fr_j->pol_damp); } vec_t force, add_i, add_j; double e = -efp_charge_dipole_energy(at_j->znuc, &dipole_i, &dr); efp_charge_dipole_grad(at_j->znuc, &dipole_i, &dr, &force, &add_j, &add_i); vec_negate(&force); vec_scale(&force, p1); vec_scale(&add_i, p1); vec_scale(&add_j, p1); force.x += p2 * e * dr.x; force.y += p2 * e * dr.y; force.z += p2 * e * dr.z; vec_scale(&force, swf.swf); vec_scale(&add_i, swf.swf); vec_scale(&add_j, swf.swf); efp_add_force(efp->grad + frag_idx, CVEC(fr_i->x), CVEC(pt_i->x), &force, &add_i); efp_sub_force(efp->grad + j, CVEC(fr_j->x), CVEC(at_j->x), &force, &add_j); efp_add_stress(&swf.dr, &force, &efp->stress); energy += p1 * e; } /* induced dipole - multipoles */ for (size_t k = 0; k < fr_j->n_multipole_pts; k++) { struct multipole_pt *pt_j = fr_j->multipole_pts + k; vec_t dr = { pt_j->x - pt_i->x - swf.cell.x, pt_j->y - pt_i->y - swf.cell.y, pt_j->z - pt_i->z - swf.cell.z }; double p1 = 1.0, p2 = 0.0; if (efp->opts.pol_damp == EFP_POL_DAMP_TT) { double r = vec_len(&dr); p1 = efp_get_pol_damp_tt(r, fr_i->pol_damp, fr_j->pol_damp); p2 = efp_get_pol_damp_tt_grad(r, fr_i->pol_damp, fr_j->pol_damp); } double e = 0.0; vec_t force_, add_i_, add_j_; vec_t force = vec_zero, add_i = vec_zero, add_j = vec_zero; /* induced dipole - charge */ e -= efp_charge_dipole_energy(pt_j->monopole, &dipole_i, &dr); efp_charge_dipole_grad(pt_j->monopole, &dipole_i, &dr, &force_, &add_j_, &add_i_); vec_negate(&force_); add_3(&force, &force_, &add_i, &add_i_, &add_j, &add_j_); /* induced dipole - dipole */ e += efp_dipole_dipole_energy(&dipole_i, &pt_j->dipole, &dr); efp_dipole_dipole_grad(&dipole_i, &pt_j->dipole, &dr, &force_, &add_i_, &add_j_); vec_negate(&add_j_); add_3(&force, &force_, &add_i, &add_i_, &add_j, &add_j_); /* induced dipole - quadrupole */ e += efp_dipole_quadrupole_energy(&dipole_i, pt_j->quadrupole, &dr); efp_dipole_quadrupole_grad(&dipole_i, pt_j->quadrupole, &dr, &force_, &add_i_, &add_j_); add_3(&force, &force_, &add_i, &add_i_, &add_j, &add_j_); /* induced dipole - octupole interactions are ignored */ vec_scale(&force, p1); vec_scale(&add_i, p1); vec_scale(&add_j, p1); force.x += p2 * e * dr.x; force.y += p2 * e * dr.y; force.z += p2 * e * dr.z; vec_scale(&force, swf.swf); vec_scale(&add_i, swf.swf); vec_scale(&add_j, swf.swf); efp_add_force(efp->grad + frag_idx, CVEC(fr_i->x), CVEC(pt_i->x), &force, &add_i); efp_sub_force(efp->grad + j, CVEC(fr_j->x), CVEC(pt_j->x), &force, &add_j); efp_add_stress(&swf.dr, &force, &efp->stress); energy += p1 * e; } /* induced dipole - induced dipoles */ for (size_t jj = 0; jj < fr_j->n_polarizable_pts; jj++) { struct polarizable_pt *pt_j = fr_j->polarizable_pts + jj; size_t idx_j = fr_j->polarizable_offset + jj; vec_t dr = { pt_j->x - pt_i->x - swf.cell.x, pt_j->y - pt_i->y - swf.cell.y, pt_j->z - pt_i->z - swf.cell.z }; vec_t half_dipole_i = { 0.5 * efp->indip[idx_i].x, 0.5 * efp->indip[idx_i].y, 0.5 * efp->indip[idx_i].z }; double p1 = 1.0, p2 = 0.0; if (efp->opts.pol_damp == EFP_POL_DAMP_TT) { double r = vec_len(&dr); p1 = efp_get_pol_damp_tt(r, fr_i->pol_damp, fr_j->pol_damp); p2 = efp_get_pol_damp_tt_grad(r, fr_i->pol_damp, fr_j->pol_damp); } vec_t force, add_i, add_j; double e = efp_dipole_dipole_energy(&half_dipole_i, &efp->indipconj[idx_j], &dr); efp_dipole_dipole_grad(&half_dipole_i, &efp->indipconj[idx_j], &dr, &force, &add_i, &add_j); vec_negate(&add_j); vec_scale(&force, p1); vec_scale(&add_i, p1); vec_scale(&add_j, p1); force.x += p2 * e * dr.x; force.y += p2 * e * dr.y; force.z += p2 * e * dr.z; vec_scale(&force, swf.swf); vec_scale(&add_i, swf.swf); vec_scale(&add_j, swf.swf); efp_add_force(efp->grad + frag_idx, CVEC(fr_i->x), CVEC(pt_i->x), &force, &add_i); efp_sub_force(efp->grad + j, CVEC(fr_j->x), CVEC(pt_j->x), &force, &add_j); efp_add_stress(&swf.dr, &force, &efp->stress); energy += p1 * e; } vec_t force = { swf.dswf.x * energy, swf.dswf.y * energy, swf.dswf.z * energy }; six_atomic_add_xyz(efp->grad + frag_idx, &force); six_atomic_sub_xyz(efp->grad + j, &force); efp_add_stress(&swf.dr, &force, &efp->stress); } /* induced dipole - ab initio nuclei */ if (efp->opts.terms & EFP_TERM_AI_POL) { for (size_t j = 0; j < efp->n_ptc; j++) { vec_t dr = vec_sub(efp->ptc_xyz + j, CVEC(pt_i->x)); vec_t force, add_i, add_j; efp_charge_dipole_grad(efp->ptc[j], &dipole_i, &dr, &force, &add_j, &add_i); vec_negate(&add_i); vec_atomic_add(efp->ptc_grad + j, &force); efp_sub_force(efp->grad + frag_idx, CVEC(fr_i->x), CVEC(pt_i->x), &force, &add_i); } } }
static vec_t get_elec_field(const struct efp *efp, size_t frag_idx, size_t pt_idx) { const struct frag *fr_j = efp->frags + frag_idx; const struct polarizable_pt *pt = fr_j->polarizable_pts + pt_idx; vec_t elec_field = vec_zero; for (size_t i = 0; i < efp->n_frag; i++) { if (i == frag_idx || efp_skip_frag_pair(efp, i, frag_idx)) continue; const struct frag *fr_i = efp->frags + i; struct swf swf = efp_make_swf(efp, fr_i, fr_j); /* field due to nuclei */ for (size_t j = 0; j < fr_i->n_atoms; j++) { const struct efp_atom *at = fr_i->atoms + j; vec_t dr = { pt->x - at->x - swf.cell.x, pt->y - at->y - swf.cell.y, pt->z - at->z - swf.cell.z }; double r = vec_len(&dr); double r3 = r * r * r; double p1 = 1.0; if (efp->opts.pol_damp == EFP_POL_DAMP_TT) p1 = efp_get_pol_damp_tt(r, fr_i->pol_damp, fr_j->pol_damp); elec_field.x += swf.swf * at->znuc * dr.x / r3 * p1; elec_field.y += swf.swf * at->znuc * dr.y / r3 * p1; elec_field.z += swf.swf * at->znuc * dr.z / r3 * p1; } /* field due to multipoles */ for (size_t j = 0; j < fr_i->n_multipole_pts; j++) { const struct multipole_pt *mult_pt = fr_i->multipole_pts + j; vec_t mult_field = get_multipole_field(CVEC(pt->x), mult_pt, &swf); vec_t dr = { pt->x - mult_pt->x - swf.cell.x, pt->y - mult_pt->y - swf.cell.y, pt->z - mult_pt->z - swf.cell.z }; double r = vec_len(&dr); double p1 = 1.0; if (efp->opts.pol_damp == EFP_POL_DAMP_TT) p1 = efp_get_pol_damp_tt(r, fr_i->pol_damp, fr_j->pol_damp); elec_field.x += mult_field.x * p1; elec_field.y += mult_field.y * p1; elec_field.z += mult_field.z * p1; } } if (efp->opts.terms & EFP_TERM_AI_POL) { /* field due to nuclei from ab initio subsystem */ for (size_t i = 0; i < efp->n_ptc; i++) { vec_t dr = vec_sub(CVEC(pt->x), efp->ptc_xyz + i); double r = vec_len(&dr); double r3 = r * r * r; elec_field.x += efp->ptc[i] * dr.x / r3; elec_field.y += efp->ptc[i] * dr.y / r3; elec_field.z += efp->ptc[i] * dr.z / r3; } } return (elec_field); }