1 ////////////////////////////////////////////////////////////////////////////////////// 2 // This file is distributed under the University of Illinois/NCSA Open Source License. 3 // See LICENSE file in top directory for details. 4 // 5 // Copyright (c) 2016 Jeongnim Kim and QMCPACK developers. 6 // 7 // File developed by: 8 // 9 // File created by: Jeongnim Kim, jeongnim.kim@intel.com, Intel Corp. 10 ////////////////////////////////////////////////////////////////////////////////////// 11 // -*- C++ -*- 12 #ifndef QMCPLUSPLUS_ONEBODYJASTROW_OPTIMIZED_SOA_H 13 #define QMCPLUSPLUS_ONEBODYJASTROW_OPTIMIZED_SOA_H 14 #include "Configuration.h" 15 #include "QMCWaveFunctions/WaveFunctionComponent.h" 16 #include "QMCWaveFunctions/Jastrow/DiffOneBodyJastrowOrbital.h" 17 #include "Utilities/qmc_common.h" 18 #include "CPU/SIMD/aligned_allocator.hpp" 19 #include "CPU/SIMD/algorithm.hpp" 20 #include <map> 21 #include <numeric> 22 23 namespace qmcplusplus 24 { 25 /** @ingroup WaveFunctionComponent 26 * @brief Specialization for one-body Jastrow function using multiple functors 27 */ 28 template<class FT> 29 struct J1OrbitalSoA : public WaveFunctionComponent 30 { 31 ///alias FuncType 32 using FuncType = FT; 33 ///type of each component U, dU, d2U; 34 using valT = typename FT::real_type; 35 ///element position type 36 using posT = TinyVector<valT, OHMMS_DIM>; 37 ///use the same container 38 using DistRow = DistanceTableData::DistRow; 39 using DisplRow = DistanceTableData::DisplRow; 40 ///table index 41 const int myTableID; 42 ///number of ions 43 int Nions; 44 ///number of electrons 45 int Nelec; 46 ///number of groups 47 int NumGroups; 48 ///reference to the sources (ions) 49 const ParticleSet& Ions; 50 51 valT curAt; 52 valT curLap; 53 posT curGrad; 54 55 ///\f$Vat[i] = sum_(j) u_{i,j}\f$ 56 Vector<valT> Vat; 57 aligned_vector<valT> U, dU, d2U, d3U; 58 aligned_vector<valT> DistCompressed; 59 aligned_vector<int> DistIndice; 60 Vector<posT> Grad; 61 Vector<valT> Lap; 62 ///Container for \f$F[ig*NumGroups+jg]\f$ 63 std::vector<FT*> F; 64 J1OrbitalSoAJ1OrbitalSoA65 J1OrbitalSoA(const std::string& obj_name, const ParticleSet& ions, ParticleSet& els) 66 : WaveFunctionComponent("J1OrbitalSoA", obj_name), myTableID(els.addTable(ions)), Ions(ions) 67 { 68 if (myName.empty()) 69 throw std::runtime_error("J1OrbitalSoA object name cannot be empty!"); 70 initialize(els); 71 } 72 73 J1OrbitalSoA(const J1OrbitalSoA& rhs) = delete; 74 ~J1OrbitalSoAJ1OrbitalSoA75 ~J1OrbitalSoA() 76 { 77 for (int i = 0; i < F.size(); ++i) 78 if (F[i] != nullptr) 79 delete F[i]; 80 } 81 82 /* initialize storage */ initializeJ1OrbitalSoA83 void initialize(const ParticleSet& els) 84 { 85 Nions = Ions.getTotalNum(); 86 NumGroups = Ions.getSpeciesSet().getTotalNum(); 87 F.resize(std::max(NumGroups, 4), nullptr); 88 if (NumGroups > 1 && !Ions.IsGrouped) 89 { 90 NumGroups = 0; 91 } 92 Nelec = els.getTotalNum(); 93 Vat.resize(Nelec); 94 Grad.resize(Nelec); 95 Lap.resize(Nelec); 96 97 U.resize(Nions); 98 dU.resize(Nions); 99 d2U.resize(Nions); 100 d3U.resize(Nions); 101 DistCompressed.resize(Nions); 102 DistIndice.resize(Nions); 103 } 104 105 void addFunc(int source_type, FT* afunc, int target_type = -1) 106 { 107 if (F[source_type] != nullptr) 108 delete F[source_type]; 109 F[source_type] = afunc; 110 } 111 recomputeJ1OrbitalSoA112 void recompute(const ParticleSet& P) 113 { 114 const DistanceTableData& d_ie(P.getDistTable(myTableID)); 115 for (int iat = 0; iat < Nelec; ++iat) 116 { 117 computeU3(P, iat, d_ie.getDistRow(iat)); 118 Vat[iat] = simd::accumulate_n(U.data(), Nions, valT()); 119 Lap[iat] = accumulateGL(dU.data(), d2U.data(), d_ie.getDisplRow(iat), Grad[iat]); 120 } 121 } 122 evaluateLogJ1OrbitalSoA123 LogValueType evaluateLog(const ParticleSet& P, ParticleSet::ParticleGradient_t& G, ParticleSet::ParticleLaplacian_t& L) 124 { 125 return evaluateGL(P, G, L, true); 126 } 127 evaluateHessianJ1OrbitalSoA128 void evaluateHessian(ParticleSet& P, HessVector_t& grad_grad_psi) 129 { 130 const DistanceTableData& d_ie(P.getDistTable(myTableID)); 131 valT dudr, d2udr2; 132 133 Tensor<valT, DIM> ident; 134 grad_grad_psi = 0.0; 135 ident.diagonal(1.0); 136 137 for (int iel = 0; iel < Nelec; ++iel) 138 { 139 const auto& dist = d_ie.getDistRow(iel); 140 const auto& displ = d_ie.getDisplRow(iel); 141 for (int iat = 0; iat < Nions; iat++) 142 { 143 int gid = Ions.GroupID[iat]; 144 auto* func = F[gid]; 145 if (func != nullptr) 146 { 147 RealType r = dist[iat]; 148 RealType rinv = 1.0 / r; 149 PosType dr = displ[iat]; 150 func->evaluate(r, dudr, d2udr2); 151 grad_grad_psi[iel] -= rinv * rinv * outerProduct(dr, dr) * (d2udr2 - dudr * rinv) + ident * dudr * rinv; 152 } 153 } 154 } 155 } 156 ratioJ1OrbitalSoA157 PsiValueType ratio(ParticleSet& P, int iat) 158 { 159 UpdateMode = ORB_PBYP_RATIO; 160 curAt = computeU(P.getDistTable(myTableID).getTempDists()); 161 return std::exp(static_cast<PsiValueType>(Vat[iat] - curAt)); 162 } 163 evaluateRatiosJ1OrbitalSoA164 inline void evaluateRatios(const VirtualParticleSet& VP, std::vector<ValueType>& ratios) 165 { 166 for (int k = 0; k < ratios.size(); ++k) 167 ratios[k] = std::exp(Vat[VP.refPtcl] - computeU(VP.getDistTable(myTableID).getDistRow(k))); 168 } 169 computeUJ1OrbitalSoA170 inline valT computeU(const DistRow& dist) 171 { 172 valT curVat(0); 173 if (NumGroups > 0) 174 { 175 for (int jg = 0; jg < NumGroups; ++jg) 176 { 177 if (F[jg] != nullptr) 178 curVat += F[jg]->evaluateV(-1, Ions.first(jg), Ions.last(jg), dist.data(), DistCompressed.data()); 179 } 180 } 181 else 182 { 183 for (int c = 0; c < Nions; ++c) 184 { 185 int gid = Ions.GroupID[c]; 186 if (F[gid] != nullptr) 187 curVat += F[gid]->evaluate(dist[c]); 188 } 189 } 190 return curVat; 191 } 192 evaluateRatiosAlltoOneJ1OrbitalSoA193 void evaluateRatiosAlltoOne(ParticleSet& P, std::vector<ValueType>& ratios) 194 { 195 const auto& dist = P.getDistTable(myTableID).getTempDists(); 196 curAt = valT(0); 197 if (NumGroups > 0) 198 { 199 for (int jg = 0; jg < NumGroups; ++jg) 200 { 201 if (F[jg] != nullptr) 202 curAt += F[jg]->evaluateV(-1, Ions.first(jg), Ions.last(jg), dist.data(), DistCompressed.data()); 203 } 204 } 205 else 206 { 207 for (int c = 0; c < Nions; ++c) 208 { 209 int gid = Ions.GroupID[c]; 210 if (F[gid] != nullptr) 211 curAt += F[gid]->evaluate(dist[c]); 212 } 213 } 214 215 for (int i = 0; i < Nelec; ++i) 216 ratios[i] = std::exp(Vat[i] - curAt); 217 } 218 219 inline LogValueType evaluateGL(const ParticleSet& P, 220 ParticleSet::ParticleGradient_t& G, 221 ParticleSet::ParticleLaplacian_t& L, 222 bool fromscratch = false) 223 { 224 if (fromscratch) 225 recompute(P); 226 227 for (size_t iat = 0; iat < Nelec; ++iat) 228 G[iat] += Grad[iat]; 229 for (size_t iat = 0; iat < Nelec; ++iat) 230 L[iat] -= Lap[iat]; 231 return LogValue = -simd::accumulate_n(Vat.data(), Nelec, valT()); 232 } 233 234 /** compute gradient and lap 235 * @return lap 236 */ accumulateGLJ1OrbitalSoA237 inline valT accumulateGL(const valT* restrict du, const valT* restrict d2u, const DisplRow& displ, posT& grad) const 238 { 239 valT lap(0); 240 constexpr valT lapfac = OHMMS_DIM - RealType(1); 241 //#pragma omp simd reduction(+:lap) 242 for (int jat = 0; jat < Nions; ++jat) 243 lap += d2u[jat] + lapfac * du[jat]; 244 for (int idim = 0; idim < OHMMS_DIM; ++idim) 245 { 246 const valT* restrict dX = displ.data(idim); 247 valT s = valT(); 248 //#pragma omp simd reduction(+:s) 249 for (int jat = 0; jat < Nions; ++jat) 250 s += du[jat] * dX[jat]; 251 grad[idim] = s; 252 } 253 return lap; 254 } 255 256 /** compute U, dU and d2U 257 * @param P quantum particleset 258 * @param iat the moving particle 259 * @param dist starting address of the distances of the ions wrt the iat-th particle 260 */ computeU3J1OrbitalSoA261 inline void computeU3(const ParticleSet& P, int iat, const DistRow& dist) 262 { 263 if (NumGroups > 0) 264 { //ions are grouped 265 constexpr valT czero(0); 266 std::fill_n(U.data(), Nions, czero); 267 std::fill_n(dU.data(), Nions, czero); 268 std::fill_n(d2U.data(), Nions, czero); 269 270 for (int jg = 0; jg < NumGroups; ++jg) 271 { 272 if (F[jg] == nullptr) 273 continue; 274 F[jg]->evaluateVGL(-1, Ions.first(jg), Ions.last(jg), dist.data(), U.data(), dU.data(), d2U.data(), 275 DistCompressed.data(), DistIndice.data()); 276 } 277 } 278 else 279 { 280 for (int c = 0; c < Nions; ++c) 281 { 282 int gid = Ions.GroupID[c]; 283 if (F[gid] != nullptr) 284 { 285 U[c] = F[gid]->evaluate(dist[c], dU[c], d2U[c]); 286 dU[c] /= dist[c]; 287 } 288 } 289 } 290 } 291 292 /** compute the gradient during particle-by-particle update 293 * @param P quantum particleset 294 * @param iat particle index 295 */ evalGradJ1OrbitalSoA296 GradType evalGrad(ParticleSet& P, int iat) { return GradType(Grad[iat]); } 297 298 /** compute the gradient during particle-by-particle update 299 * @param P quantum particleset 300 * @param iat particle index 301 * 302 * Using getTempDists(). curAt, curGrad and curLap are computed. 303 */ ratioGradJ1OrbitalSoA304 PsiValueType ratioGrad(ParticleSet& P, int iat, GradType& grad_iat) 305 { 306 UpdateMode = ORB_PBYP_PARTIAL; 307 308 computeU3(P, iat, P.getDistTable(myTableID).getTempDists()); 309 curLap = accumulateGL(dU.data(), d2U.data(), P.getDistTable(myTableID).getTempDispls(), curGrad); 310 curAt = simd::accumulate_n(U.data(), Nions, valT()); 311 grad_iat += curGrad; 312 return std::exp(static_cast<PsiValueType>(Vat[iat] - curAt)); 313 } 314 315 /** Rejected move. Nothing to do */ restoreJ1OrbitalSoA316 inline void restore(int iat) {} 317 318 /** Accpted move. Update Vat[iat],Grad[iat] and Lap[iat] */ 319 void acceptMove(ParticleSet& P, int iat, bool safe_to_delay = false) 320 { 321 if (UpdateMode == ORB_PBYP_RATIO) 322 { 323 computeU3(P, iat, P.getDistTable(myTableID).getTempDists()); 324 curLap = accumulateGL(dU.data(), d2U.data(), P.getDistTable(myTableID).getTempDispls(), curGrad); 325 } 326 327 LogValue += Vat[iat] - curAt; 328 Vat[iat] = curAt; 329 Grad[iat] = curGrad; 330 Lap[iat] = curLap; 331 } 332 333 registerDataJ1OrbitalSoA334 inline void registerData(ParticleSet& P, WFBufferType& buf) 335 { 336 if (Bytes_in_WFBuffer == 0) 337 { 338 Bytes_in_WFBuffer = buf.current(); 339 buf.add(Vat.begin(), Vat.end()); 340 buf.add(Grad.begin(), Grad.end()); 341 buf.add(Lap.begin(), Lap.end()); 342 Bytes_in_WFBuffer = buf.current() - Bytes_in_WFBuffer; 343 // free local space 344 Vat.free(); 345 Grad.free(); 346 Lap.free(); 347 } 348 else 349 { 350 buf.forward(Bytes_in_WFBuffer); 351 } 352 } 353 354 inline LogValueType updateBuffer(ParticleSet& P, WFBufferType& buf, bool fromscratch = false) 355 { 356 evaluateGL(P, P.G, P.L, false); 357 buf.forward(Bytes_in_WFBuffer); 358 return LogValue; 359 } 360 copyFromBufferJ1OrbitalSoA361 inline void copyFromBuffer(ParticleSet& P, WFBufferType& buf) 362 { 363 Vat.attachReference(buf.lendReference<valT>(Nelec), Nelec); 364 Grad.attachReference(buf.lendReference<posT>(Nelec), Nelec); 365 Lap.attachReference(buf.lendReference<valT>(Nelec), Nelec); 366 } 367 makeCloneJ1OrbitalSoA368 WaveFunctionComponentPtr makeClone(ParticleSet& tqp) const 369 { 370 J1OrbitalSoA<FT>* j1copy = new J1OrbitalSoA<FT>(myName, Ions, tqp); 371 j1copy->Optimizable = Optimizable; 372 for (size_t i = 0, n = F.size(); i < n; ++i) 373 { 374 if (F[i] != nullptr) 375 j1copy->addFunc(i, new FT(*F[i])); 376 } 377 if (dPsi) 378 { 379 j1copy->dPsi = dPsi->makeClone(tqp); 380 } 381 return j1copy; 382 } 383 384 /**@{ WaveFunctionComponent virtual functions that are not essential for the development */ reportStatusJ1OrbitalSoA385 void reportStatus(std::ostream& os) 386 { 387 for (size_t i = 0, n = F.size(); i < n; ++i) 388 { 389 if (F[i] != nullptr) 390 F[i]->myVars.print(os); 391 } 392 } 393 checkInVariablesJ1OrbitalSoA394 void checkInVariables(opt_variables_type& active) 395 { 396 myVars.clear(); 397 for (size_t i = 0, n = F.size(); i < n; ++i) 398 { 399 if (F[i] != nullptr) 400 { 401 F[i]->checkInVariables(active); 402 F[i]->checkInVariables(myVars); 403 } 404 } 405 } checkOutVariablesJ1OrbitalSoA406 void checkOutVariables(const opt_variables_type& active) 407 { 408 myVars.getIndex(active); 409 Optimizable = myVars.is_optimizable(); 410 for (size_t i = 0, n = F.size(); i < n; ++i) 411 if (F[i] != nullptr) 412 F[i]->checkOutVariables(active); 413 if (dPsi) 414 dPsi->checkOutVariables(active); 415 } 416 resetParametersJ1OrbitalSoA417 void resetParameters(const opt_variables_type& active) 418 { 419 if (!Optimizable) 420 return; 421 for (size_t i = 0, n = F.size(); i < n; ++i) 422 if (F[i] != nullptr) 423 F[i]->resetParameters(active); 424 425 for (int i = 0; i < myVars.size(); ++i) 426 { 427 int ii = myVars.Index[i]; 428 if (ii >= 0) 429 myVars[i] = active[ii]; 430 } 431 if (dPsi) 432 dPsi->resetParameters(active); 433 } 434 /**@} */ 435 evalGradSourceJ1OrbitalSoA436 inline GradType evalGradSource(ParticleSet& P, ParticleSet& source, int isrc) 437 { 438 GradType g_return(0.0); 439 const DistanceTableData& d_ie(P.getDistTable(myTableID)); 440 for (int iat = 0; iat < Nelec; ++iat) 441 { 442 const auto& dist = d_ie.getDistRow(iat); 443 const auto& displ = d_ie.getDisplRow(iat); 444 int gid = source.GroupID[isrc]; 445 RealType r = dist[isrc]; 446 RealType rinv = 1.0 / r; 447 PosType dr = displ[isrc]; 448 449 if (F[gid] != nullptr) 450 { 451 U[isrc] = F[gid]->evaluate(dist[isrc], dU[isrc], d2U[isrc], d3U[isrc]); 452 g_return -= dU[isrc] * rinv * dr; 453 } 454 } 455 return g_return; 456 } 457 evalGradSourceJ1OrbitalSoA458 inline GradType evalGradSource(ParticleSet& P, 459 ParticleSet& source, 460 int isrc, 461 TinyVector<ParticleSet::ParticleGradient_t, OHMMS_DIM>& grad_grad, 462 TinyVector<ParticleSet::ParticleLaplacian_t, OHMMS_DIM>& lapl_grad) 463 { 464 GradType g_return(0.0); 465 const DistanceTableData& d_ie(P.getDistTable(myTableID)); 466 for (int iat = 0; iat < Nelec; ++iat) 467 { 468 const auto& dist = d_ie.getDistRow(iat); 469 const auto& displ = d_ie.getDisplRow(iat); 470 int gid = source.GroupID[isrc]; 471 RealType r = dist[isrc]; 472 RealType rinv = 1.0 / r; 473 PosType dr = displ[isrc]; 474 475 if (F[gid] != nullptr) 476 { 477 U[isrc] = F[gid]->evaluate(dist[isrc], dU[isrc], d2U[isrc], d3U[isrc]); 478 } 479 else 480 { 481 APP_ABORT("J1OrbitalSoa::evaluateGradSource: F[gid]==nullptr") 482 } 483 484 g_return -= dU[isrc] * rinv * dr; 485 486 //The following terms depend only on the radial component r. Thus, 487 //we compute them and mix with position vectors to acquire the full 488 //cartesian vector objects. 489 valT grad_component = (d2U[isrc] - dU[isrc] * rinv); 490 valT lapl_component = d3U[isrc] + 2 * rinv * grad_component; 491 492 for (int idim = 0; idim < OHMMS_DIM; idim++) 493 { 494 grad_grad[idim][iat] += dr[idim] * dr * rinv * rinv * grad_component; 495 grad_grad[idim][iat][idim] += rinv * dU[isrc]; 496 497 lapl_grad[idim][iat] -= lapl_component * rinv * dr[idim]; 498 } 499 } 500 return g_return; 501 } 502 }; 503 504 505 } // namespace qmcplusplus 506 #endif 507