def inject(self, movedata): """ Inject a charge using the coulomb_kmc internal accepted move data structure. :arg movedata: Data for injection. """ realdata = movedata[:7].view(dtype=REAL) new_position = realdata[3:6:] charge = realdata[6] # modify the multipole expansion for the coarest level self._lee.multipole_exp(spherical(tuple(new_position)), charge, self.multipole_exp) # modify the dot product coefficients self._lee.dot_vec(spherical(tuple(new_position)), charge, self.local_dot_coeffs)
def test_c_sph_harm_1(): rng = np.random.RandomState(9476213) N = 20 L = 20 ncomp = (L**2) * 2 offsets = np.zeros(N, dtype=INT64) positions = np.array(rng.uniform(low=0., high=1.0, size=(N, 3)), dtype=REAL) charges = np.array(rng.uniform(size=N), dtype=REAL) moments = np.array(rng.uniform(low=0.0, high=1.0, size=ncomp), dtype=REAL) centres = np.array((0.5, 0.5, 0.5), dtype=REAL) energy = np.zeros(N, dtype=REAL) lib, _ = kmc_octal.LocalCellExpansions._init_host_kernels(L) def cptr(arr): return arr.ctypes.get_as_parameter() lib(INT64(N), cptr(offsets), cptr(centres), cptr(positions), cptr(charges), cptr(moments), cptr(energy)) lee = LocalExpEval(L) for ix in range(N): pos = tuple(positions[ix, :] - centres) sph_coord = spherical(pos) e_py = lee.compute_phi_local(moments, sph_coord)[0] * charges[ix] err = abs(e_py - energy[ix]) assert err < 10.**-14
def test_c_local_dot_eval_multipole(): L = 20 lee = LocalExpEval(L) rng = np.random.RandomState(9476213) ncomp = (L**2) * 2 for tx in range(1): L_exp = np.zeros(ncomp, dtype=REAL) M_exp = np.zeros_like(L_exp) L2_exp = np.zeros_like(L_exp) M2_exp = np.zeros_like(L_exp) pos = (tuple(rng.uniform(size=3))) sph_pos = spherical(pos) lee.dot_vec(sph_pos, 1, L_exp) lee.multipole_exp(sph_pos, 1, M_exp) lee.dot_vec_multipole(sph_pos, 1, L2_exp, M2_exp) err_l = np.linalg.norm(L_exp - L2_exp, np.inf) assert err_l < 10.**-15 err_m = np.linalg.norm(M_exp - M2_exp, np.inf) assert err_m < 10.**-15
def test_split_concept_1(): L = 12 R = 3 N = 200 E = 1. rc = E/4 rng = np.random.RandomState(seed=12415) ncomp = (L**2)*2 half_ncomp = (L**2) A = state.State() A.domain = domain.BaseDomainHalo(extent=(E,E,E)) A.domain.boundary_condition = domain.BoundaryTypePeriodic() A.npart = N A.P = data.PositionDat(ncomp=3) A.Q = data.ParticleDat(ncomp=1) A.P[:] = rng.uniform(low=-0.5*E, high=0.5*E, size=(N,3)) for px in range(N): A.Q[px,0] = (-1.0)**(px+1) bias = np.sum(A.Q[:N:, 0])/N A.Q[:, 0] -= bias A.scatter_data_from(0) fmm_pbc = PyFMM(A.domain, N=N, free_space=False, r=R, l=L) fmm_27 = PyFMM(A.domain, N=N, free_space='27', r=R, l=L) energy_pbc = fmm_pbc(A.P, A.Q) energy_27 = fmm_27(A.P, A.Q) lee = LocalExpEval(L) local_dot_coeffs = np.zeros(ncomp, dtype=REAL) for px in range(N): lee.dot_vec(spherical(tuple(A.P[px, :])), A.Q[px, :], local_dot_coeffs) L_exp = np.zeros_like(fmm_pbc.tree_parent[1][0, 0, 0, :]) fmm_pbc._lr_mtl_func(fmm_pbc.tree_halo[0][2,2,2,:], L_exp) fmm_pbc.dipole_corrector(fmm_pbc.tree_halo[0][2,2,2,:], L_exp) lr_energy = 0.5 * np.dot(L_exp, local_dot_coeffs) err = abs(energy_pbc - (energy_27 + lr_energy)) / abs(energy_pbc) assert err < 10.**-14 fmm_pbc.free() fmm_27.free()
def initialise(self, positions, charges): """ Initialise the data structures K and E (see paper). :arg positions: Initial positions of charges. :arg charges: Initial charge values. """ self.multipole_exp.fill(0) self.local_dot_coeffs.fill(0) tmp_multipole_exp = np.zeros_like(self.multipole_exp) tmp_local_dot_coeffs = np.zeros_like(self.local_dot_coeffs) for px in range(positions.npart_local): # multipole expansion for the whole cell self._lee.multipole_exp( spherical(tuple(positions[px,:])), charges[px, 0], tmp_multipole_exp ) # dot product for the local expansion for the cell self._lee.dot_vec( spherical(tuple(positions[px,:])), charges[px, 0], tmp_local_dot_coeffs ) # MPI reduce all coefficients self.domain.comm.Allreduce(tmp_multipole_exp, self.multipole_exp) self.domain.comm.Allreduce(tmp_local_dot_coeffs, self.local_dot_coeffs) L_tmp = np.zeros_like(self.local_dot_coeffs) self.lrc(self.multipole_exp, L_tmp) return 0.5 * np.dot(L_tmp, self.local_dot_coeffs)
def _get_cell_disp(self, cell, position): """ Returns spherical coordinate of particle with local cell centre as an origin """ R = self.fmm.R extent = self.group.domain.extent sl = 2**(R - 1) csl = [extent[0] / sl, extent[1] / sl, extent[2] / sl] es = [extent[0] * -0.5, extent[1] * -0.5, extent[2] * -0.5] ec = [esx + 0.5 * cx + ccx * cx for esx, cx, ccx in zip(es, csl, cell)] disp = (position[0] - ec[0], position[1] - ec[1], position[2] - ec[2]) sph = spherical(disp) return sph
def get_cell_disp(s, ix, R): sl = 2 ** (R - 1) csl = [s.domain.extent[0] / sl, s.domain.extent[1] / sl, s.domain.extent[2] / sl] es = [s.domain.extent[0] * -0.5, s.domain.extent[1] * -0.5, s.domain.extent[2] * -0.5] cc = get_fmm_cell(s, ix, R) ec = [esx + 0.5 * cx + ccx * cx for esx, cx, ccx in zip(es, csl, cc)] px = (s.P[ix, 0], s.P[ix, 1], s.P[ix, 2]) disp = (px[0] - ec[0], px[1] - ec[1], px[2] - ec[2]) sph = spherical(disp) return sph
def test_c_local_expansion_creation(): L = 12 lee = LocalExpEval(L) rng = np.random.RandomState(9476213) ncomp = (L**2) * 2 for tx in range(10): to_test = np.zeros(ncomp, REAL) correct = np.zeros(ncomp, REAL) pos = (tuple(rng.uniform(size=3))) sph_pos = spherical(pos) lee.local_exp(sph_pos, 1.0, to_test) py_local_exp(L, sph_pos, 1.0, correct) err = np.linalg.norm(to_test - correct, np.inf) assert err < 10.**-10
def test_c_local_dot_eval(): L = 20 lee = LocalExpEval(L) rng = np.random.RandomState(9476213) ncomp = (L**2) * 2 for tx in range(50): L_exp = np.array(rng.uniform(size=ncomp), dtype=REAL) L_coe = np.zeros_like(L_exp) pos = (tuple(rng.uniform(size=3))) sph_pos = spherical(pos) lee.dot_vec(sph_pos, 1, L_coe) eng_c = np.dot(L_coe, L_exp) eng_p, _ = compute_phi_local(L, L_exp, sph_pos) err = abs(eng_c - eng_p) / abs(eng_c) assert err < 10.**-13
def eval_field(self, points, out, use_c=True): """ Evaluate the far-field contribution to the potential field. :arg points: Places to evaluate field. :arg out: Array to populate with the far-field contribution to the field. """ points = np.atleast_2d(points) if points.dtype == REAL and points.shape[1] == 3 and out.dtype == REAL and use_c: self._c_eval_field(points, out) else: assert use_c != 'force' npoints = points.shape[0] lexp = np.zeros(self.ncomp, REAL) self.lrc(self.multipole_exp, lexp) for px in range(npoints): pointx = points[px, :] lr_tmp = self._lee.compute_phi_local(lexp, spherical(tuple(pointx)))[0] out[px] += lr_tmp
def py_propose(self, total_movs, num_particles, host_data, cuda_data, arr, use_python=True): """ Propose a move using the coulomb_kmc internal proposed move data structures. For details see `coulomb_kmc.kmc_mpi_decomp.FMMMPIDecomp.setup_propose_with_dats`. Warning: uses Python and hence will be slow (for testing). """ es = host_data['exclusive_sum'] old_pos = host_data['old_positions'] new_pos = host_data['new_positions'] old_chr = host_data['old_charges'] assert es.dtype == INT64 assert old_pos.dtype == REAL assert old_chr.dtype == REAL assert new_pos.dtype == REAL assert arr.dtype == REAL # tmp vars to_remove = np.zeros(self.ncomp, dtype=REAL) prop_mexp = np.zeros_like(to_remove) to_remove_dot_vec = np.zeros_like(to_remove) dot_vec = np.zeros_like(to_remove) L_tmp = np.zeros_like(to_remove) # get current long range energy self.lrc(self.multipole_exp, L_tmp) old_energy = 0.5 * np.dot(L_tmp, self.local_dot_coeffs) for px in range(num_particles): # assumed charge doesn't change charge = old_chr[px] opos = old_pos[px, :] nprop = es[px+1, 0] - es[px, 0] # remove old multipole expansion coeffs to_remove.fill(0) self._lee.multipole_exp(spherical(tuple(opos)), -charge, to_remove) # remove dot product coeffs to_remove_dot_vec.fill(0) self._lee.dot_vec(spherical(tuple(opos)), -charge, to_remove_dot_vec) for movxi, movx in enumerate(range(es[px, 0], es[px+1, 0])): prop_mexp[:] = self.multipole_exp[:].copy() npos = new_pos[movx, :] # compute the mutipole expansion of the proposed config self._lee.multipole_exp(spherical(tuple(npos)), charge, prop_mexp) # remove the old pos prop_mexp[:] += to_remove # do the same for the dot product vector dot_vec[:] = self.local_dot_coeffs.copy() dot_vec[:] += to_remove_dot_vec[:] # add on the proposed position self._lee.dot_vec(spherical(tuple(npos)), charge, dot_vec) # apply long range mtl L_tmp.fill(0) self.lrc(prop_mexp, L_tmp) # compute long range energy contribution new_energy = 0.5 * np.dot(L_tmp, dot_vec) arr[px, movxi] += old_energy - new_energy