From e6d057432513d4a8fb609210c869dd241649578a Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Tue, 11 Aug 2020 16:30:05 -0400 Subject: [PATCH 01/27] [WIP] Working on CUDA integration, developing auto generator for collocation kernels --- src/CMakeLists.txt | 16 + .../collocation_generator/__init__.py | 0 .../collocation_generator/gaueval_angular.py | 239 ++++++ .../gaueval_angular_cartesian.hpp | 679 ++++++++++++++++++ .../gaueval_angular_spherical.hpp | 539 ++++++++++++++ .../gaueval_angular_spherical_unnorm.hpp | 539 ++++++++++++++ .../gaueval_angular_template.hpp | 117 +++ .../gaueval_device_constants.hpp | 33 + .../gaueval_device_constants_template.hpp | 9 + .../gaueval_kernels_cartesian.cu | 129 ++++ .../gaueval_kernels_spherical.cu | 129 ++++ .../gaueval_kernels_spherical_unnorm.cu | 129 ++++ .../gaueval_kernels_template.cu | 129 ++++ .../collocation_generator/generate_bfeval.py | 421 +++++++++++ .../generate_gaueval_angular_eval.py | 147 ++++ src/cuda_integrator/cuda_collocation.cxx | 0 .../cuda_collocation_kernels.cu | 0 .../gauxc-cuda_integrator.cmake | 20 + 18 files changed, 3275 insertions(+) create mode 100644 src/cuda_integrator/collocation_generator/__init__.py create mode 100644 src/cuda_integrator/collocation_generator/gaueval_angular.py create mode 100644 src/cuda_integrator/collocation_generator/gaueval_angular_cartesian.hpp create mode 100644 src/cuda_integrator/collocation_generator/gaueval_angular_spherical.hpp create mode 100644 src/cuda_integrator/collocation_generator/gaueval_angular_spherical_unnorm.hpp create mode 100644 src/cuda_integrator/collocation_generator/gaueval_angular_template.hpp create mode 100644 src/cuda_integrator/collocation_generator/gaueval_device_constants.hpp create mode 100644 src/cuda_integrator/collocation_generator/gaueval_device_constants_template.hpp create mode 100644 src/cuda_integrator/collocation_generator/gaueval_kernels_cartesian.cu create mode 100644 src/cuda_integrator/collocation_generator/gaueval_kernels_spherical.cu create mode 100644 src/cuda_integrator/collocation_generator/gaueval_kernels_spherical_unnorm.cu create mode 100644 src/cuda_integrator/collocation_generator/gaueval_kernels_template.cu create mode 100644 src/cuda_integrator/collocation_generator/generate_bfeval.py create mode 100644 src/cuda_integrator/collocation_generator/generate_gaueval_angular_eval.py create mode 100644 src/cuda_integrator/cuda_collocation.cxx create mode 100644 src/cuda_integrator/cuda_collocation_kernels.cu create mode 100644 src/cuda_integrator/gauxc-cuda_integrator.cmake diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e8b235106..f4f8c93d8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -53,3 +53,19 @@ target_link_libraries( gauxc PUBLIC if( GAUXC_ENABLE_CEREAL ) target_link_libraries( gauxc PUBLIC cereal ) endif() + +if( GAUXC_ENABLE_DEVICE ) + + if( GAUXC_ENABLE_CUDA ) + include( cuda_integrator/gauxc-cuda_integrator.cmake ) + endif() + + get_target_property( DEVICE_TARGET_DEPENDS gauxc_device_integrator + INTERFACE_LINK_LIBRARIES ) + + target_link_libraries( gauxc PUBLIC + $ + ${DEVICE_TARGET_DEPENDS} + ) + +endif() diff --git a/src/cuda_integrator/collocation_generator/__init__.py b/src/cuda_integrator/collocation_generator/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cuda_integrator/collocation_generator/gaueval_angular.py b/src/cuda_integrator/collocation_generator/gaueval_angular.py new file mode 100644 index 000000000..40c8a2a6e --- /dev/null +++ b/src/cuda_integrator/collocation_generator/gaueval_angular.py @@ -0,0 +1,239 @@ +import os,sys +import re +import sympy +import math +import cmath +from math import factorial as fact +from sympy import factorial as symb_fact +from sympy import factorial2 as symb_fact2 +from scipy.special import binom as binomial +from sympy import exp as symb_exp +from sympy import I as symb_I + +def generate_cartesian_ls( L ): + + l = [] + for i in range(L+1): + lx = L - i + for j in range(i+1): + ly = i - j + lz = L - lx - ly + + l.append( [0, 0, 0] ) + + for k in range( lx - 1 ): + l[-1][0] = l[-1][0] + 1 + for k in range( ly - 1 ): + l[-1][1] = l[-1][1] + 1 + for k in range( lz - 1 ): + l[-1][2] = l[-1][2] + 1 + + if lx > 0: + l[-1][0] = l[-1][0] + 1 + if ly > 0: + l[-1][1] = l[-1][1] + 1 + if lz > 0: + l[-1][2] = l[-1][2] + 1 + + return l + + +def generate_spherical_coeff( l, m, lx, ly, lz ): + j = (lx + ly - abs(m)) + if j%2 == 0: + j = int(j / 2) + else: + return 0.0 + + prefactor = fact(2.*lx) * fact(2.*ly) * fact(2.*lz) * fact(l) + prefactor = prefactor * fact( l - abs(m) ) + prefactor = prefactor / (fact(2.*l) * fact(lx) * fact(ly) * fact(lz)) + prefactor = prefactor / fact( l + abs(m) ) + prefactor = math.sqrt( prefactor ) + + + term1 = 0.0 + for i in range( int((l - abs(m))/2)+1 ): + term1 = term1 + binomial(l,i) * binomial(i,j) * \ + math.pow(-1,i) * fact( 2*l - 2*i ) / \ + fact( l - abs(m) - 2*i ) + + term1 = term1 / math.pow(2,l) / fact(l) + + m_fact = 1. + if m < 0: + m_fact = -1. + + term2 = 0.0 + 0.0j + for k in range( j+1 ): + z = cmath.exp( m_fact * math.pi / 2. * (abs(m) - lx + 2*k) * 1.j ) + term2 = term2 + binomial(j,k) * binomial( abs(m), lx - 2*k ) * z + + val = prefactor * term1 * term2 + + if abs(val.real) < 1e-10: + val = 0.0 + val.imag*1j + if abs(val.imag) < 1e-10: + val = val.real + + return val + + +def generate_spherical_coeff_symb( l, m, lx, ly, lz, unnorm = False ): + j = (lx + ly - abs(m)) + if j%2 == 0: + j = int(j / 2) + else: + return sympy.Integer(0) + + + j_symb = sympy.Integer(j) + l_symb = sympy.Integer(l) + m_symb = sympy.Integer( abs(m) ) + lx_symb = sympy.Integer(lx) + ly_symb = sympy.Integer(ly) + lz_symb = sympy.Integer(lz) + + + + prefactor = symb_fact(2*lx_symb) * symb_fact(2*ly_symb) * symb_fact(2*lz_symb) * symb_fact(l_symb) + prefactor = prefactor * symb_fact( l_symb - m_symb ) + prefactor = prefactor / (symb_fact(2*l_symb) * symb_fact(lx_symb) * symb_fact(ly_symb) * symb_fact(lz_symb)) + prefactor = prefactor / symb_fact( l_symb + m_symb ) + + + # Ed's stupid normalization convention... + if unnorm: + prefactor = prefactor * symb_fact2( 2*l - 1 ) / symb_fact2( 2*lx - 1 ) / symb_fact2(2*ly - 1) / symb_fact2( 2*lz - 1 ) + + prefactor = sympy.sqrt( prefactor ) + + + term1 = sympy.Integer(0) + for i in range( int((l - abs(m))/2)+1 ): + term1 = term1 + sympy.Integer(binomial(l,i)) * sympy.Integer(binomial(i,j)) * \ + sympy.Integer(math.pow(-1,i)) * symb_fact( 2*l_symb - sympy.Integer(2*i) ) / \ + symb_fact( l_symb - m_symb - sympy.Integer(2*i) ) + + term1 = term1 / (2**l_symb) / symb_fact(l) + + + m_fact_symb = sympy.Integer(1) + if m < 0: + m_fact_symb = - m_fact_symb + + term2 = sympy.Integer(0) + for k in range( j+1 ): + z = sympy.exp( m_fact_symb * sympy.pi / 2 * (m_symb - lx_symb + sympy.Integer(2*k)) * symb_I ) + term2 = term2 + sympy.Integer(binomial(j,k)) * sympy.Integer(binomial( abs(m), lx - 2*k )) * z + + + return prefactor * term1 * term2 + + + +def generate_cartesian_angular( ls ): + [x,y,z,r] = sympy.symbols('x y z r', real=True) + + ang = [] + + for l in ls: + ang.append(r) + for i in range( l[0] ): ang[-1] = ang[-1] * x + for i in range( l[1] ): ang[-1] = ang[-1] * y + for i in range( l[2] ): ang[-1] = ang[-1] * z + + ang[-1] = ang[-1] / r + + return ang + +def generate_spherical_angular( L, unnorm = False ): + ls = generate_cartesian_ls( L ) + angs = generate_cartesian_angular( ls ) + + #r = sympy.symbols( 'r' ) + sph_angs = [] + for m in range( L + 1 ): + tmp_p = 0 + tmp_m = 0 + for i in range(len(ls)): + l = ls[i] + ang = angs[i] + + #c = generate_spherical_coeff( L, m, l[0],l[1],l[2] ) + c = generate_spherical_coeff_symb( L, m, l[0],l[1],l[2], unnorm ) + + if m == 0: + + tmp_p = tmp_p + c * ang + + else: + + c_p = ( c + sympy.conjugate(c) ) / sympy.sqrt(2) + c_m = ( c - sympy.conjugate(c) ) / sympy.sqrt(2) / symb_I + + tmp_p = tmp_p + c_p * ang + tmp_m = tmp_m + c_m * ang + + sph_angs.append( (m, tmp_p) ) + if m > 0: + sph_angs.append( (-m, tmp_m) ) + + sph_angs = sorted( sph_angs, key=lambda x: x[0] ) + + sph_angs_bare = [] + for a in sph_angs: + sph_angs_bare.append( sympy.simplify(a[1]) ) + + return sph_angs_bare + + +def generate_eval_lines( L, ang ): + [x,y,z,r] = sympy.symbols('x y z r', real=True) + [bf,bf_x,bf_y,bf_z] = sympy.symbols('bf bf_x bf_y bf_z',real=True) + + bf_eval_strs = [] + bf_x_eval_strs = [] + bf_y_eval_strs = [] + bf_z_eval_strs = [] + + for j in range(len(ang)): + + a = ang[j] + a_x = sympy.diff( a, x ) + a_y = sympy.diff( a, y ) + a_z = sympy.diff( a, z ) + + + + bf_eval = sympy.simplify( a * bf ) + bf_x_eval = sympy.simplify( a_x * bf + a * bf_x ) + bf_y_eval = sympy.simplify( a_y * bf + a * bf_y ) + bf_z_eval = sympy.simplify( a_z * bf + a * bf_z ) + + + bf_eval_str = 'eval[{}] = {};'.format(j,bf_eval) + bf_x_eval_str = 'eval_x[{}] = {};'.format(j,bf_x_eval) + bf_y_eval_str = 'eval_y[{}] = {};'.format(j,bf_y_eval) + bf_z_eval_str = 'eval_z[{}] = {};'.format(j,bf_z_eval) + + if L >= 2: + for k in range(2,L+1): + for X in ('x','y','z'): + pow_str = X + '**' + str(k) + repl_str = '' + for K in range(k-1): repl_str = repl_str + X + '*' + repl_str = repl_str + X + + bf_eval_str = bf_eval_str.replace(pow_str,repl_str) + bf_x_eval_str = bf_x_eval_str.replace(pow_str,repl_str) + bf_y_eval_str = bf_y_eval_str.replace(pow_str,repl_str) + bf_z_eval_str = bf_z_eval_str.replace(pow_str,repl_str) + + + bf_eval_strs .append(bf_eval_str ) + bf_x_eval_strs.append(bf_x_eval_str) + bf_y_eval_strs.append(bf_y_eval_str) + bf_z_eval_strs.append(bf_z_eval_str) + + return (bf_eval_strs, bf_x_eval_strs, bf_y_eval_strs, bf_z_eval_strs) diff --git a/src/cuda_integrator/collocation_generator/gaueval_angular_cartesian.hpp b/src/cuda_integrator/collocation_generator/gaueval_angular_cartesian.hpp new file mode 100644 index 000000000..20bf726ee --- /dev/null +++ b/src/cuda_integrator/collocation_generator/gaueval_angular_cartesian.hpp @@ -0,0 +1,679 @@ +#pragma once +#include "gaueval_device_constants.hpp" +#include + +#ifndef GPGAUEVAL_INLINE +# define GPGAUEVAL_INLINE __inline__ +#endif + +namespace gpgaueval { + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_0( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_0_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = bf_x; + + eval_y[0] = bf_y; + + eval_z[0] = bf_z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_1( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x; + eval[1] = bf*y; + eval[2] = bf*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_1_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = bf + bf_x*x; + eval_x[1] = bf_x*y; + eval_x[2] = bf_x*z; + + eval_y[0] = bf_y*x; + eval_y[1] = bf + bf_y*y; + eval_y[2] = bf_y*z; + + eval_z[0] = bf_z*x; + eval_z[1] = bf_z*y; + eval_z[2] = bf + bf_z*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_2( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x*x; + eval[1] = bf*x*y; + eval[2] = bf*x*z; + eval[3] = bf*y*y; + eval[4] = bf*y*z; + eval[5] = bf*z*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_2_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = x*(2*bf + bf_x*x); + eval_x[1] = y*(bf + bf_x*x); + eval_x[2] = z*(bf + bf_x*x); + eval_x[3] = bf_x*y*y; + eval_x[4] = bf_x*y*z; + eval_x[5] = bf_x*z*z; + + eval_y[0] = bf_y*x*x; + eval_y[1] = x*(bf + bf_y*y); + eval_y[2] = bf_y*x*z; + eval_y[3] = y*(2*bf + bf_y*y); + eval_y[4] = z*(bf + bf_y*y); + eval_y[5] = bf_y*z*z; + + eval_z[0] = bf_z*x*x; + eval_z[1] = bf_z*x*y; + eval_z[2] = x*(bf + bf_z*z); + eval_z[3] = bf_z*y*y; + eval_z[4] = y*(bf + bf_z*z); + eval_z[5] = z*(2*bf + bf_z*z); + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_3( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x*x*x; + eval[1] = bf*x*x*y; + eval[2] = bf*x*x*z; + eval[3] = bf*x*y*y; + eval[4] = bf*x*y*z; + eval[5] = bf*x*z*z; + eval[6] = bf*y*y*y; + eval[7] = bf*y*y*z; + eval[8] = bf*y*z*z; + eval[9] = bf*z*z*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_3_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = x*x*(3*bf + bf_x*x); + eval_x[1] = x*y*(2*bf + bf_x*x); + eval_x[2] = x*z*(2*bf + bf_x*x); + eval_x[3] = y*y*(bf + bf_x*x); + eval_x[4] = y*z*(bf + bf_x*x); + eval_x[5] = z*z*(bf + bf_x*x); + eval_x[6] = bf_x*y*y*y; + eval_x[7] = bf_x*y*y*z; + eval_x[8] = bf_x*y*z*z; + eval_x[9] = bf_x*z*z*z; + + eval_y[0] = bf_y*x*x*x; + eval_y[1] = x*x*(bf + bf_y*y); + eval_y[2] = bf_y*x*x*z; + eval_y[3] = x*y*(2*bf + bf_y*y); + eval_y[4] = x*z*(bf + bf_y*y); + eval_y[5] = bf_y*x*z*z; + eval_y[6] = y*y*(3*bf + bf_y*y); + eval_y[7] = y*z*(2*bf + bf_y*y); + eval_y[8] = z*z*(bf + bf_y*y); + eval_y[9] = bf_y*z*z*z; + + eval_z[0] = bf_z*x*x*x; + eval_z[1] = bf_z*x*x*y; + eval_z[2] = x*x*(bf + bf_z*z); + eval_z[3] = bf_z*x*y*y; + eval_z[4] = x*y*(bf + bf_z*z); + eval_z[5] = x*z*(2*bf + bf_z*z); + eval_z[6] = bf_z*y*y*y; + eval_z[7] = y*y*(bf + bf_z*z); + eval_z[8] = y*z*(2*bf + bf_z*z); + eval_z[9] = z*z*(3*bf + bf_z*z); + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_4( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x*x*x*x; + eval[1] = bf*x*x*x*y; + eval[2] = bf*x*x*x*z; + eval[3] = bf*x*x*y*y; + eval[4] = bf*x*x*y*z; + eval[5] = bf*x*x*z*z; + eval[6] = bf*x*y*y*y; + eval[7] = bf*x*y*y*z; + eval[8] = bf*x*y*z*z; + eval[9] = bf*x*z*z*z; + eval[10] = bf*y*y*y*y; + eval[11] = bf*y*y*y*z; + eval[12] = bf*y*y*z*z; + eval[13] = bf*y*z*z*z; + eval[14] = bf*z*z*z*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_4_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = x*x*x*(4*bf + bf_x*x); + eval_x[1] = x*x*y*(3*bf + bf_x*x); + eval_x[2] = x*x*z*(3*bf + bf_x*x); + eval_x[3] = x*y*y*(2*bf + bf_x*x); + eval_x[4] = x*y*z*(2*bf + bf_x*x); + eval_x[5] = x*z*z*(2*bf + bf_x*x); + eval_x[6] = y*y*y*(bf + bf_x*x); + eval_x[7] = y*y*z*(bf + bf_x*x); + eval_x[8] = y*z*z*(bf + bf_x*x); + eval_x[9] = z*z*z*(bf + bf_x*x); + eval_x[10] = bf_x*y*y*y*y; + eval_x[11] = bf_x*y*y*y*z; + eval_x[12] = bf_x*y*y*z*z; + eval_x[13] = bf_x*y*z*z*z; + eval_x[14] = bf_x*z*z*z*z; + + eval_y[0] = bf_y*x*x*x*x; + eval_y[1] = x*x*x*(bf + bf_y*y); + eval_y[2] = bf_y*x*x*x*z; + eval_y[3] = x*x*y*(2*bf + bf_y*y); + eval_y[4] = x*x*z*(bf + bf_y*y); + eval_y[5] = bf_y*x*x*z*z; + eval_y[6] = x*y*y*(3*bf + bf_y*y); + eval_y[7] = x*y*z*(2*bf + bf_y*y); + eval_y[8] = x*z*z*(bf + bf_y*y); + eval_y[9] = bf_y*x*z*z*z; + eval_y[10] = y*y*y*(4*bf + bf_y*y); + eval_y[11] = y*y*z*(3*bf + bf_y*y); + eval_y[12] = y*z*z*(2*bf + bf_y*y); + eval_y[13] = z*z*z*(bf + bf_y*y); + eval_y[14] = bf_y*z*z*z*z; + + eval_z[0] = bf_z*x*x*x*x; + eval_z[1] = bf_z*x*x*x*y; + eval_z[2] = x*x*x*(bf + bf_z*z); + eval_z[3] = bf_z*x*x*y*y; + eval_z[4] = x*x*y*(bf + bf_z*z); + eval_z[5] = x*x*z*(2*bf + bf_z*z); + eval_z[6] = bf_z*x*y*y*y; + eval_z[7] = x*y*y*(bf + bf_z*z); + eval_z[8] = x*y*z*(2*bf + bf_z*z); + eval_z[9] = x*z*z*(3*bf + bf_z*z); + eval_z[10] = bf_z*y*y*y*y; + eval_z[11] = y*y*y*(bf + bf_z*z); + eval_z[12] = y*y*z*(2*bf + bf_z*z); + eval_z[13] = y*z*z*(3*bf + bf_z*z); + eval_z[14] = z*z*z*(4*bf + bf_z*z); + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_5( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x*x*x*x*x; + eval[1] = bf*x*x*x*x*y; + eval[2] = bf*x*x*x*x*z; + eval[3] = bf*x*x*x*y*y; + eval[4] = bf*x*x*x*y*z; + eval[5] = bf*x*x*x*z*z; + eval[6] = bf*x*x*y*y*y; + eval[7] = bf*x*x*y*y*z; + eval[8] = bf*x*x*y*z*z; + eval[9] = bf*x*x*z*z*z; + eval[10] = bf*x*y*y*y*y; + eval[11] = bf*x*y*y*y*z; + eval[12] = bf*x*y*y*z*z; + eval[13] = bf*x*y*z*z*z; + eval[14] = bf*x*z*z*z*z; + eval[15] = bf*y*y*y*y*y; + eval[16] = bf*y*y*y*y*z; + eval[17] = bf*y*y*y*z*z; + eval[18] = bf*y*y*z*z*z; + eval[19] = bf*y*z*z*z*z; + eval[20] = bf*z*z*z*z*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_5_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = x*x*x*x*(5*bf + bf_x*x); + eval_x[1] = x*x*x*y*(4*bf + bf_x*x); + eval_x[2] = x*x*x*z*(4*bf + bf_x*x); + eval_x[3] = x*x*y*y*(3*bf + bf_x*x); + eval_x[4] = x*x*y*z*(3*bf + bf_x*x); + eval_x[5] = x*x*z*z*(3*bf + bf_x*x); + eval_x[6] = x*y*y*y*(2*bf + bf_x*x); + eval_x[7] = x*y*y*z*(2*bf + bf_x*x); + eval_x[8] = x*y*z*z*(2*bf + bf_x*x); + eval_x[9] = x*z*z*z*(2*bf + bf_x*x); + eval_x[10] = y*y*y*y*(bf + bf_x*x); + eval_x[11] = y*y*y*z*(bf + bf_x*x); + eval_x[12] = y*y*z*z*(bf + bf_x*x); + eval_x[13] = y*z*z*z*(bf + bf_x*x); + eval_x[14] = z*z*z*z*(bf + bf_x*x); + eval_x[15] = bf_x*y*y*y*y*y; + eval_x[16] = bf_x*y*y*y*y*z; + eval_x[17] = bf_x*y*y*y*z*z; + eval_x[18] = bf_x*y*y*z*z*z; + eval_x[19] = bf_x*y*z*z*z*z; + eval_x[20] = bf_x*z*z*z*z*z; + + eval_y[0] = bf_y*x*x*x*x*x; + eval_y[1] = x*x*x*x*(bf + bf_y*y); + eval_y[2] = bf_y*x*x*x*x*z; + eval_y[3] = x*x*x*y*(2*bf + bf_y*y); + eval_y[4] = x*x*x*z*(bf + bf_y*y); + eval_y[5] = bf_y*x*x*x*z*z; + eval_y[6] = x*x*y*y*(3*bf + bf_y*y); + eval_y[7] = x*x*y*z*(2*bf + bf_y*y); + eval_y[8] = x*x*z*z*(bf + bf_y*y); + eval_y[9] = bf_y*x*x*z*z*z; + eval_y[10] = x*y*y*y*(4*bf + bf_y*y); + eval_y[11] = x*y*y*z*(3*bf + bf_y*y); + eval_y[12] = x*y*z*z*(2*bf + bf_y*y); + eval_y[13] = x*z*z*z*(bf + bf_y*y); + eval_y[14] = bf_y*x*z*z*z*z; + eval_y[15] = y*y*y*y*(5*bf + bf_y*y); + eval_y[16] = y*y*y*z*(4*bf + bf_y*y); + eval_y[17] = y*y*z*z*(3*bf + bf_y*y); + eval_y[18] = y*z*z*z*(2*bf + bf_y*y); + eval_y[19] = z*z*z*z*(bf + bf_y*y); + eval_y[20] = bf_y*z*z*z*z*z; + + eval_z[0] = bf_z*x*x*x*x*x; + eval_z[1] = bf_z*x*x*x*x*y; + eval_z[2] = x*x*x*x*(bf + bf_z*z); + eval_z[3] = bf_z*x*x*x*y*y; + eval_z[4] = x*x*x*y*(bf + bf_z*z); + eval_z[5] = x*x*x*z*(2*bf + bf_z*z); + eval_z[6] = bf_z*x*x*y*y*y; + eval_z[7] = x*x*y*y*(bf + bf_z*z); + eval_z[8] = x*x*y*z*(2*bf + bf_z*z); + eval_z[9] = x*x*z*z*(3*bf + bf_z*z); + eval_z[10] = bf_z*x*y*y*y*y; + eval_z[11] = x*y*y*y*(bf + bf_z*z); + eval_z[12] = x*y*y*z*(2*bf + bf_z*z); + eval_z[13] = x*y*z*z*(3*bf + bf_z*z); + eval_z[14] = x*z*z*z*(4*bf + bf_z*z); + eval_z[15] = bf_z*y*y*y*y*y; + eval_z[16] = y*y*y*y*(bf + bf_z*z); + eval_z[17] = y*y*y*z*(2*bf + bf_z*z); + eval_z[18] = y*y*z*z*(3*bf + bf_z*z); + eval_z[19] = y*z*z*z*(4*bf + bf_z*z); + eval_z[20] = z*z*z*z*(5*bf + bf_z*z); + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_6( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x*x*x*x*x*x; + eval[1] = bf*x*x*x*x*x*y; + eval[2] = bf*x*x*x*x*x*z; + eval[3] = bf*x*x*x*x*y*y; + eval[4] = bf*x*x*x*x*y*z; + eval[5] = bf*x*x*x*x*z*z; + eval[6] = bf*x*x*x*y*y*y; + eval[7] = bf*x*x*x*y*y*z; + eval[8] = bf*x*x*x*y*z*z; + eval[9] = bf*x*x*x*z*z*z; + eval[10] = bf*x*x*y*y*y*y; + eval[11] = bf*x*x*y*y*y*z; + eval[12] = bf*x*x*y*y*z*z; + eval[13] = bf*x*x*y*z*z*z; + eval[14] = bf*x*x*z*z*z*z; + eval[15] = bf*x*y*y*y*y*y; + eval[16] = bf*x*y*y*y*y*z; + eval[17] = bf*x*y*y*y*z*z; + eval[18] = bf*x*y*y*z*z*z; + eval[19] = bf*x*y*z*z*z*z; + eval[20] = bf*x*z*z*z*z*z; + eval[21] = bf*y*y*y*y*y*y; + eval[22] = bf*y*y*y*y*y*z; + eval[23] = bf*y*y*y*y*z*z; + eval[24] = bf*y*y*y*z*z*z; + eval[25] = bf*y*y*z*z*z*z; + eval[26] = bf*y*z*z*z*z*z; + eval[27] = bf*z*z*z*z*z*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_6_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = x*x*x*x*x*(6*bf + bf_x*x); + eval_x[1] = x*x*x*x*y*(5*bf + bf_x*x); + eval_x[2] = x*x*x*x*z*(5*bf + bf_x*x); + eval_x[3] = x*x*x*y*y*(4*bf + bf_x*x); + eval_x[4] = x*x*x*y*z*(4*bf + bf_x*x); + eval_x[5] = x*x*x*z*z*(4*bf + bf_x*x); + eval_x[6] = x*x*y*y*y*(3*bf + bf_x*x); + eval_x[7] = x*x*y*y*z*(3*bf + bf_x*x); + eval_x[8] = x*x*y*z*z*(3*bf + bf_x*x); + eval_x[9] = x*x*z*z*z*(3*bf + bf_x*x); + eval_x[10] = x*y*y*y*y*(2*bf + bf_x*x); + eval_x[11] = x*y*y*y*z*(2*bf + bf_x*x); + eval_x[12] = x*y*y*z*z*(2*bf + bf_x*x); + eval_x[13] = x*y*z*z*z*(2*bf + bf_x*x); + eval_x[14] = x*z*z*z*z*(2*bf + bf_x*x); + eval_x[15] = y*y*y*y*y*(bf + bf_x*x); + eval_x[16] = y*y*y*y*z*(bf + bf_x*x); + eval_x[17] = y*y*y*z*z*(bf + bf_x*x); + eval_x[18] = y*y*z*z*z*(bf + bf_x*x); + eval_x[19] = y*z*z*z*z*(bf + bf_x*x); + eval_x[20] = z*z*z*z*z*(bf + bf_x*x); + eval_x[21] = bf_x*y*y*y*y*y*y; + eval_x[22] = bf_x*y*y*y*y*y*z; + eval_x[23] = bf_x*y*y*y*y*z*z; + eval_x[24] = bf_x*y*y*y*z*z*z; + eval_x[25] = bf_x*y*y*z*z*z*z; + eval_x[26] = bf_x*y*z*z*z*z*z; + eval_x[27] = bf_x*z*z*z*z*z*z; + + eval_y[0] = bf_y*x*x*x*x*x*x; + eval_y[1] = x*x*x*x*x*(bf + bf_y*y); + eval_y[2] = bf_y*x*x*x*x*x*z; + eval_y[3] = x*x*x*x*y*(2*bf + bf_y*y); + eval_y[4] = x*x*x*x*z*(bf + bf_y*y); + eval_y[5] = bf_y*x*x*x*x*z*z; + eval_y[6] = x*x*x*y*y*(3*bf + bf_y*y); + eval_y[7] = x*x*x*y*z*(2*bf + bf_y*y); + eval_y[8] = x*x*x*z*z*(bf + bf_y*y); + eval_y[9] = bf_y*x*x*x*z*z*z; + eval_y[10] = x*x*y*y*y*(4*bf + bf_y*y); + eval_y[11] = x*x*y*y*z*(3*bf + bf_y*y); + eval_y[12] = x*x*y*z*z*(2*bf + bf_y*y); + eval_y[13] = x*x*z*z*z*(bf + bf_y*y); + eval_y[14] = bf_y*x*x*z*z*z*z; + eval_y[15] = x*y*y*y*y*(5*bf + bf_y*y); + eval_y[16] = x*y*y*y*z*(4*bf + bf_y*y); + eval_y[17] = x*y*y*z*z*(3*bf + bf_y*y); + eval_y[18] = x*y*z*z*z*(2*bf + bf_y*y); + eval_y[19] = x*z*z*z*z*(bf + bf_y*y); + eval_y[20] = bf_y*x*z*z*z*z*z; + eval_y[21] = y*y*y*y*y*(6*bf + bf_y*y); + eval_y[22] = y*y*y*y*z*(5*bf + bf_y*y); + eval_y[23] = y*y*y*z*z*(4*bf + bf_y*y); + eval_y[24] = y*y*z*z*z*(3*bf + bf_y*y); + eval_y[25] = y*z*z*z*z*(2*bf + bf_y*y); + eval_y[26] = z*z*z*z*z*(bf + bf_y*y); + eval_y[27] = bf_y*z*z*z*z*z*z; + + eval_z[0] = bf_z*x*x*x*x*x*x; + eval_z[1] = bf_z*x*x*x*x*x*y; + eval_z[2] = x*x*x*x*x*(bf + bf_z*z); + eval_z[3] = bf_z*x*x*x*x*y*y; + eval_z[4] = x*x*x*x*y*(bf + bf_z*z); + eval_z[5] = x*x*x*x*z*(2*bf + bf_z*z); + eval_z[6] = bf_z*x*x*x*y*y*y; + eval_z[7] = x*x*x*y*y*(bf + bf_z*z); + eval_z[8] = x*x*x*y*z*(2*bf + bf_z*z); + eval_z[9] = x*x*x*z*z*(3*bf + bf_z*z); + eval_z[10] = bf_z*x*x*y*y*y*y; + eval_z[11] = x*x*y*y*y*(bf + bf_z*z); + eval_z[12] = x*x*y*y*z*(2*bf + bf_z*z); + eval_z[13] = x*x*y*z*z*(3*bf + bf_z*z); + eval_z[14] = x*x*z*z*z*(4*bf + bf_z*z); + eval_z[15] = bf_z*x*y*y*y*y*y; + eval_z[16] = x*y*y*y*y*(bf + bf_z*z); + eval_z[17] = x*y*y*y*z*(2*bf + bf_z*z); + eval_z[18] = x*y*y*z*z*(3*bf + bf_z*z); + eval_z[19] = x*y*z*z*z*(4*bf + bf_z*z); + eval_z[20] = x*z*z*z*z*(5*bf + bf_z*z); + eval_z[21] = bf_z*y*y*y*y*y*y; + eval_z[22] = y*y*y*y*y*(bf + bf_z*z); + eval_z[23] = y*y*y*y*z*(2*bf + bf_z*z); + eval_z[24] = y*y*y*z*z*(3*bf + bf_z*z); + eval_z[25] = y*y*z*z*z*(4*bf + bf_z*z); + eval_z[26] = y*z*z*z*z*(5*bf + bf_z*z); + eval_z[27] = z*z*z*z*z*(6*bf + bf_z*z); + +} + + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular( + const int64_t l, + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + +//switch( l ) { + + //case 0: + if( l == 0 ) { + gaueval_cartesian_angular_0( bf, x, y, z, eval ); + //break; + + //case 1: + } else if( l == 1 ) { + gaueval_cartesian_angular_1( bf, x, y, z, eval ); + //break; + + //case 2: + } else if( l == 2 ) { + gaueval_cartesian_angular_2( bf, x, y, z, eval ); + //break; + + //case 3: + } else if( l == 3 ) { + gaueval_cartesian_angular_3( bf, x, y, z, eval ); + //break; + + //case 4: + } else if( l == 4 ) { + gaueval_cartesian_angular_4( bf, x, y, z, eval ); + //break; + + //case 5: + } else if( l == 5 ) { + gaueval_cartesian_angular_5( bf, x, y, z, eval ); + //break; + + //case 6: + } else if( l == 6 ) { + gaueval_cartesian_angular_6( bf, x, y, z, eval ); + //break; + + //default: + } else { + assert( false && "L < L_MAX" ); + //break; + } + +//} // switch(l) + +} // gaueval_cartesian_angular + + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_deriv1( + const int64_t l, + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval, + double* eval_x, + double* eval_y, + double* eval_z +) { + +//switch( l ) { + +// case 0: + if( l == 0 ) { + gaueval_cartesian_angular_0( bf, x, y, z, eval ); + gaueval_cartesian_angular_0_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 1: + } else if( l == 1 ) { + gaueval_cartesian_angular_1( bf, x, y, z, eval ); + gaueval_cartesian_angular_1_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 2: + } else if( l == 2 ) { + gaueval_cartesian_angular_2( bf, x, y, z, eval ); + gaueval_cartesian_angular_2_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 3: + } else if( l == 3 ) { + gaueval_cartesian_angular_3( bf, x, y, z, eval ); + gaueval_cartesian_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 4: + } else if( l == 4 ) { + gaueval_cartesian_angular_4( bf, x, y, z, eval ); + gaueval_cartesian_angular_4_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 5: + } else if( l == 5 ) { + gaueval_cartesian_angular_5( bf, x, y, z, eval ); + gaueval_cartesian_angular_5_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 6: + } else if( l == 6 ) { + gaueval_cartesian_angular_6( bf, x, y, z, eval ); + gaueval_cartesian_angular_6_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// default: + } else { + assert( false && "L < L_MAX" ); +// break; + } + +//} // switch(l) + +} // gaueval_cartesian_angular_deriv1 + + + +} // namespace gpgaueval diff --git a/src/cuda_integrator/collocation_generator/gaueval_angular_spherical.hpp b/src/cuda_integrator/collocation_generator/gaueval_angular_spherical.hpp new file mode 100644 index 000000000..595b7c70b --- /dev/null +++ b/src/cuda_integrator/collocation_generator/gaueval_angular_spherical.hpp @@ -0,0 +1,539 @@ +#pragma once +#include "gaueval_device_constants.hpp" +#include + +#ifndef GPGAUEVAL_INLINE +# define GPGAUEVAL_INLINE __inline__ +#endif + +namespace gpgaueval { + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_0( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_0_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = bf_x; + + eval_y[0] = bf_y; + + eval_z[0] = bf_z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_1( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*y; + eval[1] = bf*z; + eval[2] = bf*x; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_1_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = bf_x*y; + eval_x[1] = bf_x*z; + eval_x[2] = bf + bf_x*x; + + eval_y[0] = bf + bf_y*y; + eval_y[1] = bf_y*z; + eval_y[2] = bf_y*x; + + eval_z[0] = bf_z*y; + eval_z[1] = bf + bf_z*z; + eval_z[2] = bf_z*x; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_2( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x*y; + eval[1] = bf*y*z; + eval[2] = bf*(-x*x - y*y + 2*z*z)/2; + eval[3] = bf*x*z; + eval[4] = sqrt_3*bf*(x*x - y*y)/2; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_2_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = y*(bf + bf_x*x); + eval_x[1] = bf_x*y*z; + eval_x[2] = -bf*x - bf_x*(x*x + y*y - 2*z*z)/2; + eval_x[3] = z*(bf + bf_x*x); + eval_x[4] = sqrt_3*(bf*x + bf_x*(x*x - y*y)/2); + + eval_y[0] = x*(bf + bf_y*y); + eval_y[1] = z*(bf + bf_y*y); + eval_y[2] = -bf*y - bf_y*(x*x + y*y - 2*z*z)/2; + eval_y[3] = bf_y*x*z; + eval_y[4] = sqrt_3*(-bf*y + bf_y*(x*x - y*y)/2); + + eval_z[0] = bf_z*x*y; + eval_z[1] = y*(bf + bf_z*z); + eval_z[2] = 2*bf*z - bf_z*(x*x + y*y - 2*z*z)/2; + eval_z[3] = x*(bf + bf_z*z); + eval_z[4] = sqrt_3*bf_z*(x*x - y*y)/2; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_3( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*y*(3*sqrt_2*x*x - sqrt_10*y*y)/4; + eval[1] = bf*x*y*z; + eval[2] = bf*y*(-sqrt_30*x*x - 5*sqrt_6*y*y + 4*sqrt_30*z*z)/20; + eval[3] = bf*z*(-3*sqrt_5*x*x - 3*sqrt_5*y*y + 10*z*z)/10; + eval[4] = bf*x*(-5*sqrt_6*x*x - sqrt_30*y*y + 4*sqrt_30*z*z)/20; + eval[5] = sqrt_3*bf*z*(x*x - y*y)/2; + eval[6] = bf*x*(sqrt_10*x*x - 3*sqrt_2*y*y)/4; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_3_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = y*(6*sqrt_2*bf*x + bf_x*(3*sqrt_2*x*x - sqrt_10*y*y))/4; + eval_x[1] = y*z*(bf + bf_x*x); + eval_x[2] = -y*(2*sqrt_30*bf*x + bf_x*(sqrt_30*x*x + 5*sqrt_6*y*y - 4*sqrt_30*z*z))/20; + eval_x[3] = -z*(6*sqrt_5*bf*x + bf_x*(3*sqrt_5*x*x + 3*sqrt_5*y*y - 10*z*z))/10; + eval_x[4] = -bf*(15*sqrt_6*x*x + sqrt_30*y*y - 4*sqrt_30*z*z)/20 - bf_x*x*(5*sqrt_6*x*x + sqrt_30*y*y - 4*sqrt_30*z*z)/20; + eval_x[5] = sqrt_3*z*(2*bf*x + bf_x*(x*x - y*y))/2; + eval_x[6] = 3*bf*(sqrt_10*x*x - sqrt_2*y*y)/4 + bf_x*x*(sqrt_10*x*x - 3*sqrt_2*y*y)/4; + + eval_y[0] = 3*bf*(sqrt_2*x*x - sqrt_10*y*y)/4 + bf_y*y*(3*sqrt_2*x*x - sqrt_10*y*y)/4; + eval_y[1] = x*z*(bf + bf_y*y); + eval_y[2] = -bf*(sqrt_30*x*x + 15*sqrt_6*y*y - 4*sqrt_30*z*z)/20 - bf_y*y*(sqrt_30*x*x + 5*sqrt_6*y*y - 4*sqrt_30*z*z)/20; + eval_y[3] = -z*(6*sqrt_5*bf*y + bf_y*(3*sqrt_5*x*x + 3*sqrt_5*y*y - 10*z*z))/10; + eval_y[4] = -x*(2*sqrt_30*bf*y + bf_y*(5*sqrt_6*x*x + sqrt_30*y*y - 4*sqrt_30*z*z))/20; + eval_y[5] = sqrt_3*z*(-2*bf*y + bf_y*(x*x - y*y))/2; + eval_y[6] = x*(-6*sqrt_2*bf*y + bf_y*(sqrt_10*x*x - 3*sqrt_2*y*y))/4; + + eval_z[0] = bf_z*y*(3*sqrt_2*x*x - sqrt_10*y*y)/4; + eval_z[1] = x*y*(bf + bf_z*z); + eval_z[2] = y*(8*sqrt_30*bf*z - bf_z*(sqrt_30*x*x + 5*sqrt_6*y*y - 4*sqrt_30*z*z))/20; + eval_z[3] = -3*bf*(sqrt_5*x*x + sqrt_5*y*y - 10*z*z)/10 - bf_z*z*(3*sqrt_5*x*x + 3*sqrt_5*y*y - 10*z*z)/10; + eval_z[4] = x*(8*sqrt_30*bf*z - bf_z*(5*sqrt_6*x*x + sqrt_30*y*y - 4*sqrt_30*z*z))/20; + eval_z[5] = sqrt_3*(bf + bf_z*z)*(x*x - y*y)/2; + eval_z[6] = bf_z*x*(sqrt_10*x*x - 3*sqrt_2*y*y)/4; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_4( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = sqrt_5*bf*x*y*(x*x - y*y)/2; + eval[1] = bf*y*z*(3*sqrt_2*x*x - sqrt_10*y*y)/4; + eval[2] = bf*x*y*(-sqrt_35*x*x - sqrt_35*y*y + 6*sqrt_7*z*z)/14; + eval[3] = bf*y*z*(-3*sqrt_14*x*x - 3*sqrt_70*y*y + 4*sqrt_70*z*z)/28; + eval[4] = bf*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 24*sqrt_105*x*x*z*z + 105*y*y*y*y - 24*sqrt_105*y*y*z*z + 280*z*z*z*z)/280; + eval[5] = bf*x*z*(-3*sqrt_70*x*x - 3*sqrt_14*y*y + 4*sqrt_70*z*z)/28; + eval[6] = bf*(-7*sqrt_5*x*x*x*x + 6*sqrt_21*x*x*z*z + 7*sqrt_5*y*y*y*y - 6*sqrt_21*y*y*z*z)/28; + eval[7] = bf*x*z*(sqrt_10*x*x - 3*sqrt_2*y*y)/4; + eval[8] = bf*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y)/8; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_4_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = sqrt_5*y*(bf*(3*x*x - y*y) + bf_x*x*(x*x - y*y))/2; + eval_x[1] = y*z*(6*sqrt_2*bf*x + bf_x*(3*sqrt_2*x*x - sqrt_10*y*y))/4; + eval_x[2] = -y*(bf*(3*sqrt_35*x*x + sqrt_35*y*y - 6*sqrt_7*z*z) + bf_x*x*(sqrt_35*x*x + sqrt_35*y*y - 6*sqrt_7*z*z))/14; + eval_x[3] = -y*z*(6*sqrt_14*bf*x + bf_x*(3*sqrt_14*x*x + 3*sqrt_70*y*y - 4*sqrt_70*z*z))/28; + eval_x[4] = 3*bf*x*(35*x*x + sqrt_105*y*y - 4*sqrt_105*z*z)/70 + bf_x*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 24*sqrt_105*x*x*z*z + 105*y*y*y*y - 24*sqrt_105*y*y*z*z + 280*z*z*z*z)/280; + eval_x[5] = -z*(bf*(9*sqrt_70*x*x + 3*sqrt_14*y*y - 4*sqrt_70*z*z) + bf_x*x*(3*sqrt_70*x*x + 3*sqrt_14*y*y - 4*sqrt_70*z*z))/28; + eval_x[6] = -bf*x*(7*sqrt_5*x*x - 3*sqrt_21*z*z)/7 - bf_x*(7*sqrt_5*x*x*x*x - 6*sqrt_21*x*x*z*z - 7*sqrt_5*y*y*y*y + 6*sqrt_21*y*y*z*z)/28; + eval_x[7] = z*(3*bf*(sqrt_10*x*x - sqrt_2*y*y) + bf_x*x*(sqrt_10*x*x - 3*sqrt_2*y*y))/4; + eval_x[8] = bf*x*(sqrt_35*x*x - 3*sqrt_3*y*y)/2 + bf_x*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y)/8; + + eval_y[0] = sqrt_5*x*(-bf*(-x*x + 3*y*y) + bf_y*y*(x*x - y*y))/2; + eval_y[1] = z*(-3*bf*(-sqrt_2*x*x + sqrt_10*y*y) + bf_y*y*(3*sqrt_2*x*x - sqrt_10*y*y))/4; + eval_y[2] = -x*(bf*(sqrt_35*x*x + 3*sqrt_35*y*y - 6*sqrt_7*z*z) + bf_y*y*(sqrt_35*x*x + sqrt_35*y*y - 6*sqrt_7*z*z))/14; + eval_y[3] = -z*(bf*(3*sqrt_14*x*x + 9*sqrt_70*y*y - 4*sqrt_70*z*z) + bf_y*y*(3*sqrt_14*x*x + 3*sqrt_70*y*y - 4*sqrt_70*z*z))/28; + eval_y[4] = 3*bf*y*(sqrt_105*x*x + 35*y*y - 4*sqrt_105*z*z)/70 + bf_y*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 24*sqrt_105*x*x*z*z + 105*y*y*y*y - 24*sqrt_105*y*y*z*z + 280*z*z*z*z)/280; + eval_y[5] = -x*z*(6*sqrt_14*bf*y + bf_y*(3*sqrt_70*x*x + 3*sqrt_14*y*y - 4*sqrt_70*z*z))/28; + eval_y[6] = bf*y*(7*sqrt_5*y*y - 3*sqrt_21*z*z)/7 - bf_y*(7*sqrt_5*x*x*x*x - 6*sqrt_21*x*x*z*z - 7*sqrt_5*y*y*y*y + 6*sqrt_21*y*y*z*z)/28; + eval_y[7] = x*z*(-6*sqrt_2*bf*y + bf_y*(sqrt_10*x*x - 3*sqrt_2*y*y))/4; + eval_y[8] = -bf*y*(3*sqrt_3*x*x - sqrt_35*y*y)/2 + bf_y*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y)/8; + + eval_z[0] = sqrt_5*bf_z*x*y*(x*x - y*y)/2; + eval_z[1] = y*(bf + bf_z*z)*(3*sqrt_2*x*x - sqrt_10*y*y)/4; + eval_z[2] = x*y*(12*sqrt_7*bf*z - bf_z*(sqrt_35*x*x + sqrt_35*y*y - 6*sqrt_7*z*z))/14; + eval_z[3] = y*(3*bf*(-sqrt_14*x*x - sqrt_70*y*y + 4*sqrt_70*z*z) - bf_z*z*(3*sqrt_14*x*x + 3*sqrt_70*y*y - 4*sqrt_70*z*z))/28; + eval_z[4] = -2*bf*z*(3*sqrt_105*x*x + 3*sqrt_105*y*y - 70*z*z)/35 + bf_z*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 24*sqrt_105*x*x*z*z + 105*y*y*y*y - 24*sqrt_105*y*y*z*z + 280*z*z*z*z)/280; + eval_z[5] = x*(3*bf*(-sqrt_70*x*x - sqrt_14*y*y + 4*sqrt_70*z*z) - bf_z*z*(3*sqrt_70*x*x + 3*sqrt_14*y*y - 4*sqrt_70*z*z))/28; + eval_z[6] = 3*sqrt_21*bf*z*(x*x - y*y)/7 - bf_z*(7*sqrt_5*x*x*x*x - 6*sqrt_21*x*x*z*z - 7*sqrt_5*y*y*y*y + 6*sqrt_21*y*y*z*z)/28; + eval_z[7] = x*(bf + bf_z*z)*(sqrt_10*x*x - 3*sqrt_2*y*y)/4; + eval_z[8] = bf_z*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y)/8; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_5( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*y*(5*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 3*sqrt_14*y*y*y*y)/16; + eval[1] = sqrt_5*bf*x*y*z*(x*x - y*y)/2; + eval[2] = bf*y*(-3*sqrt_70*x*x*x*x - 2*sqrt_30*x*x*y*y + 24*sqrt_6*x*x*z*z + 3*sqrt_70*y*y*y*y - 8*sqrt_30*y*y*z*z)/48; + eval[3] = sqrt_15*bf*x*y*z*(-x*x - y*y + 2*z*z)/6; + eval[4] = bf*y*(7*sqrt_15*x*x*x*x + 6*sqrt_35*x*x*y*y - 36*sqrt_7*x*x*z*z + 21*sqrt_15*y*y*y*y - 36*sqrt_35*y*y*z*z + 56*sqrt_15*z*z*z*z)/168; + eval[5] = bf*z*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 40*sqrt_21*x*x*z*z + 105*y*y*y*y - 40*sqrt_21*y*y*z*z + 168*z*z*z*z)/168; + eval[6] = bf*x*(21*sqrt_15*x*x*x*x + 6*sqrt_35*x*x*y*y - 36*sqrt_35*x*x*z*z + 7*sqrt_15*y*y*y*y - 36*sqrt_7*y*y*z*z + 56*sqrt_15*z*z*z*z)/168; + eval[7] = bf*z*(-sqrt_105*x*x*x*x + 6*sqrt_5*x*x*z*z + sqrt_105*y*y*y*y - 6*sqrt_5*y*y*z*z)/12; + eval[8] = bf*x*(-3*sqrt_70*x*x*x*x + 2*sqrt_30*x*x*y*y + 8*sqrt_30*x*x*z*z + 3*sqrt_70*y*y*y*y - 24*sqrt_6*y*y*z*z)/48; + eval[9] = bf*z*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y)/8; + eval[10] = bf*x*(3*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 5*sqrt_14*y*y*y*y)/16; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_5_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = y*(20*bf*x*(sqrt_14*x*x - sqrt_6*y*y) + bf_x*(5*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 3*sqrt_14*y*y*y*y))/16; + eval_x[1] = sqrt_5*y*z*(bf*(3*x*x - y*y) + bf_x*x*(x*x - y*y))/2; + eval_x[2] = -y*(4*bf*x*(3*sqrt_70*x*x + sqrt_30*y*y - 12*sqrt_6*z*z) + bf_x*(3*sqrt_70*x*x*x*x + 2*sqrt_30*x*x*y*y - 24*sqrt_6*x*x*z*z - 3*sqrt_70*y*y*y*y + 8*sqrt_30*y*y*z*z))/48; + eval_x[3] = -sqrt_15*y*z*(bf*(3*x*x + y*y - 2*z*z) + bf_x*x*(x*x + y*y - 2*z*z))/6; + eval_x[4] = y*(4*bf*x*(7*sqrt_15*x*x + 3*sqrt_35*y*y - 18*sqrt_7*z*z) + bf_x*(7*sqrt_15*x*x*x*x + 6*sqrt_35*x*x*y*y - 36*sqrt_7*x*x*z*z + 21*sqrt_15*y*y*y*y - 36*sqrt_35*y*y*z*z + 56*sqrt_15*z*z*z*z))/168; + eval_x[5] = z*(4*bf*x*(105*x*x + 3*sqrt_105*y*y - 20*sqrt_21*z*z) + bf_x*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 40*sqrt_21*x*x*z*z + 105*y*y*y*y - 40*sqrt_21*y*y*z*z + 168*z*z*z*z))/168; + eval_x[6] = 5*sqrt_15*bf*x*x*x*x/8 + 3*sqrt_35*bf*x*x*y*y/28 - 9*sqrt_35*bf*x*x*z*z/14 + sqrt_15*bf*y*y*y*y/24 - 3*sqrt_7*bf*y*y*z*z/14 + sqrt_15*bf*z*z*z*z/3 + sqrt_15*bf_x*x*x*x*x*x/8 + sqrt_35*bf_x*x*x*x*y*y/28 - 3*sqrt_35*bf_x*x*x*x*z*z/14 + sqrt_15*bf_x*x*y*y*y*y/24 - 3*sqrt_7*bf_x*x*y*y*z*z/14 + sqrt_15*bf_x*x*z*z*z*z/3; + eval_x[7] = -z*(4*bf*x*(sqrt_105*x*x - 3*sqrt_5*z*z) + bf_x*(sqrt_105*x*x*x*x - 6*sqrt_5*x*x*z*z - sqrt_105*y*y*y*y + 6*sqrt_5*y*y*z*z))/12; + eval_x[8] = -5*sqrt_70*bf*x*x*x*x/16 + sqrt_30*bf*x*x*y*y/8 + sqrt_30*bf*x*x*z*z/2 + sqrt_70*bf*y*y*y*y/16 - sqrt_6*bf*y*y*z*z/2 - sqrt_70*bf_x*x*x*x*x*x/16 + sqrt_30*bf_x*x*x*x*y*y/24 + sqrt_30*bf_x*x*x*x*z*z/6 + sqrt_70*bf_x*x*y*y*y*y/16 - sqrt_6*bf_x*x*y*y*z*z/2; + eval_x[9] = z*(4*bf*x*(sqrt_35*x*x - 3*sqrt_3*y*y) + bf_x*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y))/8; + eval_x[10] = 15*sqrt_14*bf*x*x*x*x/16 - 15*sqrt_6*bf*x*x*y*y/8 + 5*sqrt_14*bf*y*y*y*y/16 + 3*sqrt_14*bf_x*x*x*x*x*x/16 - 5*sqrt_6*bf_x*x*x*x*y*y/8 + 5*sqrt_14*bf_x*x*y*y*y*y/16; + + eval_y[0] = 5*sqrt_14*bf*x*x*x*x/16 - 15*sqrt_6*bf*x*x*y*y/8 + 15*sqrt_14*bf*y*y*y*y/16 + 5*sqrt_14*bf_y*x*x*x*x*y/16 - 5*sqrt_6*bf_y*x*x*y*y*y/8 + 3*sqrt_14*bf_y*y*y*y*y*y/16; + eval_y[1] = sqrt_5*x*z*(-bf*(-x*x + 3*y*y) + bf_y*y*(x*x - y*y))/2; + eval_y[2] = -sqrt_70*bf*x*x*x*x/16 - sqrt_30*bf*x*x*y*y/8 + sqrt_6*bf*x*x*z*z/2 + 5*sqrt_70*bf*y*y*y*y/16 - sqrt_30*bf*y*y*z*z/2 - sqrt_70*bf_y*x*x*x*x*y/16 - sqrt_30*bf_y*x*x*y*y*y/24 + sqrt_6*bf_y*x*x*y*z*z/2 + sqrt_70*bf_y*y*y*y*y*y/16 - sqrt_30*bf_y*y*y*y*z*z/6; + eval_y[3] = -sqrt_15*x*z*(bf*(x*x + 3*y*y - 2*z*z) + bf_y*y*(x*x + y*y - 2*z*z))/6; + eval_y[4] = sqrt_15*bf*x*x*x*x/24 + 3*sqrt_35*bf*x*x*y*y/28 - 3*sqrt_7*bf*x*x*z*z/14 + 5*sqrt_15*bf*y*y*y*y/8 - 9*sqrt_35*bf*y*y*z*z/14 + sqrt_15*bf*z*z*z*z/3 + sqrt_15*bf_y*x*x*x*x*y/24 + sqrt_35*bf_y*x*x*y*y*y/28 - 3*sqrt_7*bf_y*x*x*y*z*z/14 + sqrt_15*bf_y*y*y*y*y*y/8 - 3*sqrt_35*bf_y*y*y*y*z*z/14 + sqrt_15*bf_y*y*z*z*z*z/3; + eval_y[5] = z*(4*bf*y*(3*sqrt_105*x*x + 105*y*y - 20*sqrt_21*z*z) + bf_y*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 40*sqrt_21*x*x*z*z + 105*y*y*y*y - 40*sqrt_21*y*y*z*z + 168*z*z*z*z))/168; + eval_y[6] = x*(4*bf*y*(3*sqrt_35*x*x + 7*sqrt_15*y*y - 18*sqrt_7*z*z) + bf_y*(21*sqrt_15*x*x*x*x + 6*sqrt_35*x*x*y*y - 36*sqrt_35*x*x*z*z + 7*sqrt_15*y*y*y*y - 36*sqrt_7*y*y*z*z + 56*sqrt_15*z*z*z*z))/168; + eval_y[7] = z*(4*bf*y*(sqrt_105*y*y - 3*sqrt_5*z*z) - bf_y*(sqrt_105*x*x*x*x - 6*sqrt_5*x*x*z*z - sqrt_105*y*y*y*y + 6*sqrt_5*y*y*z*z))/12; + eval_y[8] = x*(4*bf*y*(sqrt_30*x*x + 3*sqrt_70*y*y - 12*sqrt_6*z*z) + bf_y*(-3*sqrt_70*x*x*x*x + 2*sqrt_30*x*x*y*y + 8*sqrt_30*x*x*z*z + 3*sqrt_70*y*y*y*y - 24*sqrt_6*y*y*z*z))/48; + eval_y[9] = z*(-4*bf*y*(3*sqrt_3*x*x - sqrt_35*y*y) + bf_y*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y))/8; + eval_y[10] = x*(-20*bf*y*(sqrt_6*x*x - sqrt_14*y*y) + bf_y*(3*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 5*sqrt_14*y*y*y*y))/16; + + eval_z[0] = bf_z*y*(5*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 3*sqrt_14*y*y*y*y)/16; + eval_z[1] = sqrt_5*x*y*(bf + bf_z*z)*(x*x - y*y)/2; + eval_z[2] = y*(16*bf*z*(3*sqrt_6*x*x - sqrt_30*y*y) - bf_z*(3*sqrt_70*x*x*x*x + 2*sqrt_30*x*x*y*y - 24*sqrt_6*x*x*z*z - 3*sqrt_70*y*y*y*y + 8*sqrt_30*y*y*z*z))/48; + eval_z[3] = sqrt_15*x*y*(bf*(-x*x - y*y + 6*z*z) - bf_z*z*(x*x + y*y - 2*z*z))/6; + eval_z[4] = y*(-8*bf*z*(9*sqrt_7*x*x + 9*sqrt_35*y*y - 28*sqrt_15*z*z) + bf_z*(7*sqrt_15*x*x*x*x + 6*sqrt_35*x*x*y*y - 36*sqrt_7*x*x*z*z + 21*sqrt_15*y*y*y*y - 36*sqrt_35*y*y*z*z + 56*sqrt_15*z*z*z*z))/168; + eval_z[5] = 5*bf*x*x*x*x/8 + sqrt_105*bf*x*x*y*y/28 - 5*sqrt_21*bf*x*x*z*z/7 + 5*bf*y*y*y*y/8 - 5*sqrt_21*bf*y*y*z*z/7 + 5*bf*z*z*z*z + 5*bf_z*x*x*x*x*z/8 + sqrt_105*bf_z*x*x*y*y*z/28 - 5*sqrt_21*bf_z*x*x*z*z*z/21 + 5*bf_z*y*y*y*y*z/8 - 5*sqrt_21*bf_z*y*y*z*z*z/21 + bf_z*z*z*z*z*z; + eval_z[6] = x*(-8*bf*z*(9*sqrt_35*x*x + 9*sqrt_7*y*y - 28*sqrt_15*z*z) + bf_z*(21*sqrt_15*x*x*x*x + 6*sqrt_35*x*x*y*y - 36*sqrt_35*x*x*z*z + 7*sqrt_15*y*y*y*y - 36*sqrt_7*y*y*z*z + 56*sqrt_15*z*z*z*z))/168; + eval_z[7] = -sqrt_105*bf*x*x*x*x/12 + 3*sqrt_5*bf*x*x*z*z/2 + sqrt_105*bf*y*y*y*y/12 - 3*sqrt_5*bf*y*y*z*z/2 - sqrt_105*bf_z*x*x*x*x*z/12 + sqrt_5*bf_z*x*x*z*z*z/2 + sqrt_105*bf_z*y*y*y*y*z/12 - sqrt_5*bf_z*y*y*z*z*z/2; + eval_z[8] = x*(16*bf*z*(sqrt_30*x*x - 3*sqrt_6*y*y) + bf_z*(-3*sqrt_70*x*x*x*x + 2*sqrt_30*x*x*y*y + 8*sqrt_30*x*x*z*z + 3*sqrt_70*y*y*y*y - 24*sqrt_6*y*y*z*z))/48; + eval_z[9] = (bf + bf_z*z)*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y)/8; + eval_z[10] = bf_z*x*(3*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 5*sqrt_14*y*y*y*y)/16; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_6( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x*y*(3*sqrt_42*x*x*x*x - 10*sqrt_10*x*x*y*y + 3*sqrt_42*y*y*y*y)/16; + eval[1] = bf*y*z*(5*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 3*sqrt_14*y*y*y*y)/16; + eval[2] = bf*x*y*(-3*sqrt_77*x*x*x*x + 10*sqrt_33*x*x*z*z + 3*sqrt_77*y*y*y*y - 10*sqrt_33*y*y*z*z)/44; + eval[3] = bf*y*z*(-3*sqrt_2310*x*x*x*x - 6*sqrt_110*x*x*y*y + 24*sqrt_110*x*x*z*z + 3*sqrt_2310*y*y*y*y - 40*sqrt_22*y*y*z*z)/176; + eval[4] = bf*x*y*(3*sqrt_2310*x*x*x*x + 30*sqrt_22*x*x*y*y - 48*sqrt_110*x*x*z*z + 3*sqrt_2310*y*y*y*y - 48*sqrt_110*y*y*z*z + 16*sqrt_2310*z*z*z*z)/528; + eval[5] = bf*y*z*(5*sqrt_231*x*x*x*x + 30*sqrt_11*x*x*y*y - 60*sqrt_11*x*x*z*z + 15*sqrt_231*y*y*y*y - 60*sqrt_55*y*y*z*z + 24*sqrt_231*z*z*z*z)/264; + eval[6] = bf*(-385*x*x*x*x*x*x - 35*sqrt_33*x*x*x*x*y*y + 210*sqrt_33*x*x*x*x*z*z - 35*sqrt_33*x*x*y*y*y*y + 36*sqrt_385*x*x*y*y*z*z - 280*sqrt_33*x*x*z*z*z*z - 385*y*y*y*y*y*y + 210*sqrt_33*y*y*y*y*z*z - 280*sqrt_33*y*y*z*z*z*z + 1232*z*z*z*z*z*z)/1232; + eval[7] = bf*x*z*(15*sqrt_231*x*x*x*x + 30*sqrt_11*x*x*y*y - 60*sqrt_55*x*x*z*z + 5*sqrt_231*y*y*y*y - 60*sqrt_11*y*y*z*z + 24*sqrt_231*z*z*z*z)/264; + eval[8] = bf*(11*sqrt_210*x*x*x*x*x*x + sqrt_770*x*x*x*x*y*y - 16*sqrt_770*x*x*x*x*z*z - sqrt_770*x*x*y*y*y*y + 16*sqrt_770*x*x*z*z*z*z - 11*sqrt_210*y*y*y*y*y*y + 16*sqrt_770*y*y*y*y*z*z - 16*sqrt_770*y*y*z*z*z*z)/352; + eval[9] = bf*x*z*(-3*sqrt_2310*x*x*x*x + 6*sqrt_110*x*x*y*y + 40*sqrt_22*x*x*z*z + 3*sqrt_2310*y*y*y*y - 24*sqrt_110*y*y*z*z)/176; + eval[10] = bf*(-33*sqrt_7*x*x*x*x*x*x + 5*sqrt_231*x*x*x*x*y*y + 10*sqrt_231*x*x*x*x*z*z + 5*sqrt_231*x*x*y*y*y*y - 36*sqrt_55*x*x*y*y*z*z - 33*sqrt_7*y*y*y*y*y*y + 10*sqrt_231*y*y*y*y*z*z)/176; + eval[11] = bf*x*z*(3*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 5*sqrt_14*y*y*y*y)/16; + eval[12] = bf*(sqrt_462*x*x*x*x*x*x - 15*sqrt_14*x*x*x*x*y*y + 15*sqrt_14*x*x*y*y*y*y - sqrt_462*y*y*y*y*y*y)/32; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_6_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = y*(15*sqrt_42*bf*x*x*x*x - 30*sqrt_10*bf*x*x*y*y + 3*sqrt_42*bf*y*y*y*y + 3*sqrt_42*bf_x*x*x*x*x*x - 10*sqrt_10*bf_x*x*x*x*y*y + 3*sqrt_42*bf_x*x*y*y*y*y)/16; + eval_x[1] = y*z*(20*bf*x*(sqrt_14*x*x - sqrt_6*y*y) + bf_x*(5*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 3*sqrt_14*y*y*y*y))/16; + eval_x[2] = y*(-15*sqrt_77*bf*x*x*x*x + 30*sqrt_33*bf*x*x*z*z + 3*sqrt_77*bf*y*y*y*y - 10*sqrt_33*bf*y*y*z*z - 3*sqrt_77*bf_x*x*x*x*x*x + 10*sqrt_33*bf_x*x*x*x*z*z + 3*sqrt_77*bf_x*x*y*y*y*y - 10*sqrt_33*bf_x*x*y*y*z*z)/44; + eval_x[3] = -y*z*(12*bf*x*(sqrt_2310*x*x + sqrt_110*y*y - 4*sqrt_110*z*z) + bf_x*(3*sqrt_2310*x*x*x*x + 6*sqrt_110*x*x*y*y - 24*sqrt_110*x*x*z*z - 3*sqrt_2310*y*y*y*y + 40*sqrt_22*y*y*z*z))/176; + eval_x[4] = y*(15*sqrt_2310*bf*x*x*x*x + 90*sqrt_22*bf*x*x*y*y - 144*sqrt_110*bf*x*x*z*z + 3*sqrt_2310*bf*y*y*y*y - 48*sqrt_110*bf*y*y*z*z + 16*sqrt_2310*bf*z*z*z*z + 3*sqrt_2310*bf_x*x*x*x*x*x + 30*sqrt_22*bf_x*x*x*x*y*y - 48*sqrt_110*bf_x*x*x*x*z*z + 3*sqrt_2310*bf_x*x*y*y*y*y - 48*sqrt_110*bf_x*x*y*y*z*z + 16*sqrt_2310*bf_x*x*z*z*z*z)/528; + eval_x[5] = y*z*(20*bf*x*(sqrt_231*x*x + 3*sqrt_11*y*y - 6*sqrt_11*z*z) + bf_x*(5*sqrt_231*x*x*x*x + 30*sqrt_11*x*x*y*y - 60*sqrt_11*x*x*z*z + 15*sqrt_231*y*y*y*y - 60*sqrt_55*y*y*z*z + 24*sqrt_231*z*z*z*z))/264; + eval_x[6] = -bf*x*(1155*x*x*x*x + 70*sqrt_33*x*x*y*y - 420*sqrt_33*x*x*z*z + 35*sqrt_33*y*y*y*y - 36*sqrt_385*y*y*z*z + 280*sqrt_33*z*z*z*z)/616 - bf_x*(385*x*x*x*x*x*x + 35*sqrt_33*x*x*x*x*y*y - 210*sqrt_33*x*x*x*x*z*z + 35*sqrt_33*x*x*y*y*y*y - 36*sqrt_385*x*x*y*y*z*z + 280*sqrt_33*x*x*z*z*z*z + 385*y*y*y*y*y*y - 210*sqrt_33*y*y*y*y*z*z + 280*sqrt_33*y*y*z*z*z*z - 1232*z*z*z*z*z*z)/1232; + eval_x[7] = z*(75*sqrt_231*bf*x*x*x*x + 90*sqrt_11*bf*x*x*y*y - 180*sqrt_55*bf*x*x*z*z + 5*sqrt_231*bf*y*y*y*y - 60*sqrt_11*bf*y*y*z*z + 24*sqrt_231*bf*z*z*z*z + 15*sqrt_231*bf_x*x*x*x*x*x + 30*sqrt_11*bf_x*x*x*x*y*y - 60*sqrt_55*bf_x*x*x*x*z*z + 5*sqrt_231*bf_x*x*y*y*y*y - 60*sqrt_11*bf_x*x*y*y*z*z + 24*sqrt_231*bf_x*x*z*z*z*z)/264; + eval_x[8] = bf*x*(33*sqrt_210*x*x*x*x + 2*sqrt_770*x*x*y*y - 32*sqrt_770*x*x*z*z - sqrt_770*y*y*y*y + 16*sqrt_770*z*z*z*z)/176 + bf_x*(11*sqrt_210*x*x*x*x*x*x + sqrt_770*x*x*x*x*y*y - 16*sqrt_770*x*x*x*x*z*z - sqrt_770*x*x*y*y*y*y + 16*sqrt_770*x*x*z*z*z*z - 11*sqrt_210*y*y*y*y*y*y + 16*sqrt_770*y*y*y*y*z*z - 16*sqrt_770*y*y*z*z*z*z)/352; + eval_x[9] = z*(-15*sqrt_2310*bf*x*x*x*x + 18*sqrt_110*bf*x*x*y*y + 120*sqrt_22*bf*x*x*z*z + 3*sqrt_2310*bf*y*y*y*y - 24*sqrt_110*bf*y*y*z*z - 3*sqrt_2310*bf_x*x*x*x*x*x + 6*sqrt_110*bf_x*x*x*x*y*y + 40*sqrt_22*bf_x*x*x*x*z*z + 3*sqrt_2310*bf_x*x*y*y*y*y - 24*sqrt_110*bf_x*x*y*y*z*z)/176; + eval_x[10] = bf*x*(-99*sqrt_7*x*x*x*x + 10*sqrt_231*x*x*y*y + 20*sqrt_231*x*x*z*z + 5*sqrt_231*y*y*y*y - 36*sqrt_55*y*y*z*z)/88 + bf_x*(-33*sqrt_7*x*x*x*x*x*x + 5*sqrt_231*x*x*x*x*y*y + 10*sqrt_231*x*x*x*x*z*z + 5*sqrt_231*x*x*y*y*y*y - 36*sqrt_55*x*x*y*y*z*z - 33*sqrt_7*y*y*y*y*y*y + 10*sqrt_231*y*y*y*y*z*z)/176; + eval_x[11] = z*(15*sqrt_14*bf*x*x*x*x - 30*sqrt_6*bf*x*x*y*y + 5*sqrt_14*bf*y*y*y*y + 3*sqrt_14*bf_x*x*x*x*x*x - 10*sqrt_6*bf_x*x*x*x*y*y + 5*sqrt_14*bf_x*x*y*y*y*y)/16; + eval_x[12] = 3*bf*x*(sqrt_462*x*x*x*x - 10*sqrt_14*x*x*y*y + 5*sqrt_14*y*y*y*y)/16 + bf_x*(sqrt_462*x*x*x*x*x*x - 15*sqrt_14*x*x*x*x*y*y + 15*sqrt_14*x*x*y*y*y*y - sqrt_462*y*y*y*y*y*y)/32; + + eval_y[0] = x*(3*sqrt_42*bf*x*x*x*x - 30*sqrt_10*bf*x*x*y*y + 15*sqrt_42*bf*y*y*y*y + 3*sqrt_42*bf_y*x*x*x*x*y - 10*sqrt_10*bf_y*x*x*y*y*y + 3*sqrt_42*bf_y*y*y*y*y*y)/16; + eval_y[1] = z*(5*sqrt_14*bf*x*x*x*x - 30*sqrt_6*bf*x*x*y*y + 15*sqrt_14*bf*y*y*y*y + 5*sqrt_14*bf_y*x*x*x*x*y - 10*sqrt_6*bf_y*x*x*y*y*y + 3*sqrt_14*bf_y*y*y*y*y*y)/16; + eval_y[2] = x*(-3*sqrt_77*bf*x*x*x*x + 10*sqrt_33*bf*x*x*z*z + 15*sqrt_77*bf*y*y*y*y - 30*sqrt_33*bf*y*y*z*z - 3*sqrt_77*bf_y*x*x*x*x*y + 10*sqrt_33*bf_y*x*x*y*z*z + 3*sqrt_77*bf_y*y*y*y*y*y - 10*sqrt_33*bf_y*y*y*y*z*z)/44; + eval_y[3] = z*(-3*sqrt_2310*bf*x*x*x*x - 18*sqrt_110*bf*x*x*y*y + 24*sqrt_110*bf*x*x*z*z + 15*sqrt_2310*bf*y*y*y*y - 120*sqrt_22*bf*y*y*z*z - 3*sqrt_2310*bf_y*x*x*x*x*y - 6*sqrt_110*bf_y*x*x*y*y*y + 24*sqrt_110*bf_y*x*x*y*z*z + 3*sqrt_2310*bf_y*y*y*y*y*y - 40*sqrt_22*bf_y*y*y*y*z*z)/176; + eval_y[4] = x*(3*sqrt_2310*bf*x*x*x*x + 90*sqrt_22*bf*x*x*y*y - 48*sqrt_110*bf*x*x*z*z + 15*sqrt_2310*bf*y*y*y*y - 144*sqrt_110*bf*y*y*z*z + 16*sqrt_2310*bf*z*z*z*z + 3*sqrt_2310*bf_y*x*x*x*x*y + 30*sqrt_22*bf_y*x*x*y*y*y - 48*sqrt_110*bf_y*x*x*y*z*z + 3*sqrt_2310*bf_y*y*y*y*y*y - 48*sqrt_110*bf_y*y*y*y*z*z + 16*sqrt_2310*bf_y*y*z*z*z*z)/528; + eval_y[5] = z*(5*sqrt_231*bf*x*x*x*x + 90*sqrt_11*bf*x*x*y*y - 60*sqrt_11*bf*x*x*z*z + 75*sqrt_231*bf*y*y*y*y - 180*sqrt_55*bf*y*y*z*z + 24*sqrt_231*bf*z*z*z*z + 5*sqrt_231*bf_y*x*x*x*x*y + 30*sqrt_11*bf_y*x*x*y*y*y - 60*sqrt_11*bf_y*x*x*y*z*z + 15*sqrt_231*bf_y*y*y*y*y*y - 60*sqrt_55*bf_y*y*y*y*z*z + 24*sqrt_231*bf_y*y*z*z*z*z)/264; + eval_y[6] = -bf*y*(35*sqrt_33*x*x*x*x + 70*sqrt_33*x*x*y*y - 36*sqrt_385*x*x*z*z + 1155*y*y*y*y - 420*sqrt_33*y*y*z*z + 280*sqrt_33*z*z*z*z)/616 - bf_y*(385*x*x*x*x*x*x + 35*sqrt_33*x*x*x*x*y*y - 210*sqrt_33*x*x*x*x*z*z + 35*sqrt_33*x*x*y*y*y*y - 36*sqrt_385*x*x*y*y*z*z + 280*sqrt_33*x*x*z*z*z*z + 385*y*y*y*y*y*y - 210*sqrt_33*y*y*y*y*z*z + 280*sqrt_33*y*y*z*z*z*z - 1232*z*z*z*z*z*z)/1232; + eval_y[7] = x*z*(20*bf*y*(3*sqrt_11*x*x + sqrt_231*y*y - 6*sqrt_11*z*z) + bf_y*(15*sqrt_231*x*x*x*x + 30*sqrt_11*x*x*y*y - 60*sqrt_55*x*x*z*z + 5*sqrt_231*y*y*y*y - 60*sqrt_11*y*y*z*z + 24*sqrt_231*z*z*z*z))/264; + eval_y[8] = -bf*y*(-sqrt_770*x*x*x*x + 2*sqrt_770*x*x*y*y + 33*sqrt_210*y*y*y*y - 32*sqrt_770*y*y*z*z + 16*sqrt_770*z*z*z*z)/176 + bf_y*(11*sqrt_210*x*x*x*x*x*x + sqrt_770*x*x*x*x*y*y - 16*sqrt_770*x*x*x*x*z*z - sqrt_770*x*x*y*y*y*y + 16*sqrt_770*x*x*z*z*z*z - 11*sqrt_210*y*y*y*y*y*y + 16*sqrt_770*y*y*y*y*z*z - 16*sqrt_770*y*y*z*z*z*z)/352; + eval_y[9] = x*z*(12*bf*y*(sqrt_110*x*x + sqrt_2310*y*y - 4*sqrt_110*z*z) + bf_y*(-3*sqrt_2310*x*x*x*x + 6*sqrt_110*x*x*y*y + 40*sqrt_22*x*x*z*z + 3*sqrt_2310*y*y*y*y - 24*sqrt_110*y*y*z*z))/176; + eval_y[10] = bf*y*(5*sqrt_231*x*x*x*x + 10*sqrt_231*x*x*y*y - 36*sqrt_55*x*x*z*z - 99*sqrt_7*y*y*y*y + 20*sqrt_231*y*y*z*z)/88 + bf_y*(-33*sqrt_7*x*x*x*x*x*x + 5*sqrt_231*x*x*x*x*y*y + 10*sqrt_231*x*x*x*x*z*z + 5*sqrt_231*x*x*y*y*y*y - 36*sqrt_55*x*x*y*y*z*z - 33*sqrt_7*y*y*y*y*y*y + 10*sqrt_231*y*y*y*y*z*z)/176; + eval_y[11] = x*z*(-20*bf*y*(sqrt_6*x*x - sqrt_14*y*y) + bf_y*(3*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 5*sqrt_14*y*y*y*y))/16; + eval_y[12] = -3*bf*y*(5*sqrt_14*x*x*x*x - 10*sqrt_14*x*x*y*y + sqrt_462*y*y*y*y)/16 + bf_y*(sqrt_462*x*x*x*x*x*x - 15*sqrt_14*x*x*x*x*y*y + 15*sqrt_14*x*x*y*y*y*y - sqrt_462*y*y*y*y*y*y)/32; + + eval_z[0] = bf_z*x*y*(3*sqrt_42*x*x*x*x - 10*sqrt_10*x*x*y*y + 3*sqrt_42*y*y*y*y)/16; + eval_z[1] = y*(bf + bf_z*z)*(5*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 3*sqrt_14*y*y*y*y)/16; + eval_z[2] = x*y*(20*sqrt_33*bf*z*(x*x - y*y) - bf_z*(3*sqrt_77*x*x*x*x - 10*sqrt_33*x*x*z*z - 3*sqrt_77*y*y*y*y + 10*sqrt_33*y*y*z*z))/44; + eval_z[3] = y*(-3*sqrt_2310*bf*x*x*x*x - 6*sqrt_110*bf*x*x*y*y + 72*sqrt_110*bf*x*x*z*z + 3*sqrt_2310*bf*y*y*y*y - 120*sqrt_22*bf*y*y*z*z - 3*sqrt_2310*bf_z*x*x*x*x*z - 6*sqrt_110*bf_z*x*x*y*y*z + 24*sqrt_110*bf_z*x*x*z*z*z + 3*sqrt_2310*bf_z*y*y*y*y*z - 40*sqrt_22*bf_z*y*y*z*z*z)/176; + eval_z[4] = x*y*(-32*bf*z*(3*sqrt_110*x*x + 3*sqrt_110*y*y - 2*sqrt_2310*z*z) + bf_z*(3*sqrt_2310*x*x*x*x + 30*sqrt_22*x*x*y*y - 48*sqrt_110*x*x*z*z + 3*sqrt_2310*y*y*y*y - 48*sqrt_110*y*y*z*z + 16*sqrt_2310*z*z*z*z))/528; + eval_z[5] = y*(5*sqrt_231*bf*x*x*x*x + 30*sqrt_11*bf*x*x*y*y - 180*sqrt_11*bf*x*x*z*z + 15*sqrt_231*bf*y*y*y*y - 180*sqrt_55*bf*y*y*z*z + 120*sqrt_231*bf*z*z*z*z + 5*sqrt_231*bf_z*x*x*x*x*z + 30*sqrt_11*bf_z*x*x*y*y*z - 60*sqrt_11*bf_z*x*x*z*z*z + 15*sqrt_231*bf_z*y*y*y*y*z - 60*sqrt_55*bf_z*y*y*z*z*z + 24*sqrt_231*bf_z*z*z*z*z*z)/264; + eval_z[6] = bf*z*(105*sqrt_33*x*x*x*x + 18*sqrt_385*x*x*y*y - 280*sqrt_33*x*x*z*z + 105*sqrt_33*y*y*y*y - 280*sqrt_33*y*y*z*z + 1848*z*z*z*z)/308 - bf_z*(385*x*x*x*x*x*x + 35*sqrt_33*x*x*x*x*y*y - 210*sqrt_33*x*x*x*x*z*z + 35*sqrt_33*x*x*y*y*y*y - 36*sqrt_385*x*x*y*y*z*z + 280*sqrt_33*x*x*z*z*z*z + 385*y*y*y*y*y*y - 210*sqrt_33*y*y*y*y*z*z + 280*sqrt_33*y*y*z*z*z*z - 1232*z*z*z*z*z*z)/1232; + eval_z[7] = x*(15*sqrt_231*bf*x*x*x*x + 30*sqrt_11*bf*x*x*y*y - 180*sqrt_55*bf*x*x*z*z + 5*sqrt_231*bf*y*y*y*y - 180*sqrt_11*bf*y*y*z*z + 120*sqrt_231*bf*z*z*z*z + 15*sqrt_231*bf_z*x*x*x*x*z + 30*sqrt_11*bf_z*x*x*y*y*z - 60*sqrt_55*bf_z*x*x*z*z*z + 5*sqrt_231*bf_z*y*y*y*y*z - 60*sqrt_11*bf_z*y*y*z*z*z + 24*sqrt_231*bf_z*z*z*z*z*z)/264; + eval_z[8] = -sqrt_770*bf*z*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z)/11 + bf_z*(11*sqrt_210*x*x*x*x*x*x + sqrt_770*x*x*x*x*y*y - 16*sqrt_770*x*x*x*x*z*z - sqrt_770*x*x*y*y*y*y + 16*sqrt_770*x*x*z*z*z*z - 11*sqrt_210*y*y*y*y*y*y + 16*sqrt_770*y*y*y*y*z*z - 16*sqrt_770*y*y*z*z*z*z)/352; + eval_z[9] = x*(-3*sqrt_2310*bf*x*x*x*x + 6*sqrt_110*bf*x*x*y*y + 120*sqrt_22*bf*x*x*z*z + 3*sqrt_2310*bf*y*y*y*y - 72*sqrt_110*bf*y*y*z*z - 3*sqrt_2310*bf_z*x*x*x*x*z + 6*sqrt_110*bf_z*x*x*y*y*z + 40*sqrt_22*bf_z*x*x*z*z*z + 3*sqrt_2310*bf_z*y*y*y*y*z - 24*sqrt_110*bf_z*y*y*z*z*z)/176; + eval_z[10] = bf*z*(5*sqrt_231*x*x*x*x - 18*sqrt_55*x*x*y*y + 5*sqrt_231*y*y*y*y)/44 + bf_z*(-33*sqrt_7*x*x*x*x*x*x + 5*sqrt_231*x*x*x*x*y*y + 10*sqrt_231*x*x*x*x*z*z + 5*sqrt_231*x*x*y*y*y*y - 36*sqrt_55*x*x*y*y*z*z - 33*sqrt_7*y*y*y*y*y*y + 10*sqrt_231*y*y*y*y*z*z)/176; + eval_z[11] = x*(bf + bf_z*z)*(3*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 5*sqrt_14*y*y*y*y)/16; + eval_z[12] = bf_z*(sqrt_462*x*x*x*x*x*x - 15*sqrt_14*x*x*x*x*y*y + 15*sqrt_14*x*x*y*y*y*y - sqrt_462*y*y*y*y*y*y)/32; + +} + + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular( + const int64_t l, + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + +//switch( l ) { + + //case 0: + if( l == 0 ) { + gaueval_spherical_angular_0( bf, x, y, z, eval ); + //break; + + //case 1: + } else if( l == 1 ) { + gaueval_spherical_angular_1( bf, x, y, z, eval ); + //break; + + //case 2: + } else if( l == 2 ) { + gaueval_spherical_angular_2( bf, x, y, z, eval ); + //break; + + //case 3: + } else if( l == 3 ) { + gaueval_spherical_angular_3( bf, x, y, z, eval ); + //break; + + //case 4: + } else if( l == 4 ) { + gaueval_spherical_angular_4( bf, x, y, z, eval ); + //break; + + //case 5: + } else if( l == 5 ) { + gaueval_spherical_angular_5( bf, x, y, z, eval ); + //break; + + //case 6: + } else if( l == 6 ) { + gaueval_spherical_angular_6( bf, x, y, z, eval ); + //break; + + //default: + } else { + assert( false && "L < L_MAX" ); + //break; + } + +//} // switch(l) + +} // gaueval_spherical_angular + + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_deriv1( + const int64_t l, + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval, + double* eval_x, + double* eval_y, + double* eval_z +) { + +//switch( l ) { + +// case 0: + if( l == 0 ) { + gaueval_spherical_angular_0( bf, x, y, z, eval ); + gaueval_spherical_angular_0_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 1: + } else if( l == 1 ) { + gaueval_spherical_angular_1( bf, x, y, z, eval ); + gaueval_spherical_angular_1_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 2: + } else if( l == 2 ) { + gaueval_spherical_angular_2( bf, x, y, z, eval ); + gaueval_spherical_angular_2_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 3: + } else if( l == 3 ) { + gaueval_spherical_angular_3( bf, x, y, z, eval ); + gaueval_spherical_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 4: + } else if( l == 4 ) { + gaueval_spherical_angular_4( bf, x, y, z, eval ); + gaueval_spherical_angular_4_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 5: + } else if( l == 5 ) { + gaueval_spherical_angular_5( bf, x, y, z, eval ); + gaueval_spherical_angular_5_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 6: + } else if( l == 6 ) { + gaueval_spherical_angular_6( bf, x, y, z, eval ); + gaueval_spherical_angular_6_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// default: + } else { + assert( false && "L < L_MAX" ); +// break; + } + +//} // switch(l) + +} // gaueval_spherical_angular_deriv1 + + + +} // namespace gpgaueval diff --git a/src/cuda_integrator/collocation_generator/gaueval_angular_spherical_unnorm.hpp b/src/cuda_integrator/collocation_generator/gaueval_angular_spherical_unnorm.hpp new file mode 100644 index 000000000..ee6f679c7 --- /dev/null +++ b/src/cuda_integrator/collocation_generator/gaueval_angular_spherical_unnorm.hpp @@ -0,0 +1,539 @@ +#pragma once +#include "gaueval_device_constants.hpp" +#include + +#ifndef GPGAUEVAL_INLINE +# define GPGAUEVAL_INLINE __inline__ +#endif + +namespace gpgaueval { + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_0( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_0_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = bf_x; + + eval_y[0] = bf_y; + + eval_z[0] = bf_z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_1( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*y; + eval[1] = bf*z; + eval[2] = bf*x; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_1_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = bf_x*y; + eval_x[1] = bf_x*z; + eval_x[2] = bf + bf_x*x; + + eval_y[0] = bf + bf_y*y; + eval_y[1] = bf_y*z; + eval_y[2] = bf_y*x; + + eval_z[0] = bf_z*y; + eval_z[1] = bf + bf_z*z; + eval_z[2] = bf_z*x; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_2( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = sqrt_3*bf*x*y; + eval[1] = sqrt_3*bf*y*z; + eval[2] = bf*(-x*x - y*y + 2*z*z)/2; + eval[3] = sqrt_3*bf*x*z; + eval[4] = sqrt_3*bf*(x*x - y*y)/2; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_2_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = sqrt_3*y*(bf + bf_x*x); + eval_x[1] = sqrt_3*bf_x*y*z; + eval_x[2] = -bf*x - bf_x*(x*x + y*y - 2*z*z)/2; + eval_x[3] = sqrt_3*z*(bf + bf_x*x); + eval_x[4] = sqrt_3*(bf*x + bf_x*(x*x - y*y)/2); + + eval_y[0] = sqrt_3*x*(bf + bf_y*y); + eval_y[1] = sqrt_3*z*(bf + bf_y*y); + eval_y[2] = -bf*y - bf_y*(x*x + y*y - 2*z*z)/2; + eval_y[3] = sqrt_3*bf_y*x*z; + eval_y[4] = sqrt_3*(-bf*y + bf_y*(x*x - y*y)/2); + + eval_z[0] = sqrt_3*bf_z*x*y; + eval_z[1] = sqrt_3*y*(bf + bf_z*z); + eval_z[2] = 2*bf*z - bf_z*(x*x + y*y - 2*z*z)/2; + eval_z[3] = sqrt_3*x*(bf + bf_z*z); + eval_z[4] = sqrt_3*bf_z*(x*x - y*y)/2; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_3( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = sqrt_10*bf*y*(3*x*x - y*y)/4; + eval[1] = sqrt_15*bf*x*y*z; + eval[2] = sqrt_6*bf*y*(-x*x - y*y + 4*z*z)/4; + eval[3] = bf*z*(-3*x*x - 3*y*y + 2*z*z)/2; + eval[4] = sqrt_6*bf*x*(-x*x - y*y + 4*z*z)/4; + eval[5] = sqrt_15*bf*z*(x*x - y*y)/2; + eval[6] = sqrt_10*bf*x*(x*x - 3*y*y)/4; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_3_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = sqrt_10*y*(6*bf*x + bf_x*(3*x*x - y*y))/4; + eval_x[1] = sqrt_15*y*z*(bf + bf_x*x); + eval_x[2] = -sqrt_6*y*(2*bf*x + bf_x*(x*x + y*y - 4*z*z))/4; + eval_x[3] = -z*(6*bf*x + bf_x*(3*x*x + 3*y*y - 2*z*z))/2; + eval_x[4] = -sqrt_6*(bf*(3*x*x + y*y - 4*z*z) + bf_x*x*(x*x + y*y - 4*z*z))/4; + eval_x[5] = sqrt_15*z*(2*bf*x + bf_x*(x*x - y*y))/2; + eval_x[6] = sqrt_10*(3*bf*(x*x - y*y) + bf_x*x*(x*x - 3*y*y))/4; + + eval_y[0] = sqrt_10*(-3*bf*(-x*x + y*y) + bf_y*y*(3*x*x - y*y))/4; + eval_y[1] = sqrt_15*x*z*(bf + bf_y*y); + eval_y[2] = -sqrt_6*(bf*(x*x + 3*y*y - 4*z*z) + bf_y*y*(x*x + y*y - 4*z*z))/4; + eval_y[3] = -z*(6*bf*y + bf_y*(3*x*x + 3*y*y - 2*z*z))/2; + eval_y[4] = -sqrt_6*x*(2*bf*y + bf_y*(x*x + y*y - 4*z*z))/4; + eval_y[5] = sqrt_15*z*(-2*bf*y + bf_y*(x*x - y*y))/2; + eval_y[6] = sqrt_10*x*(-6*bf*y + bf_y*(x*x - 3*y*y))/4; + + eval_z[0] = sqrt_10*bf_z*y*(3*x*x - y*y)/4; + eval_z[1] = sqrt_15*x*y*(bf + bf_z*z); + eval_z[2] = sqrt_6*y*(8*bf*z - bf_z*(x*x + y*y - 4*z*z))/4; + eval_z[3] = -3*bf*(x*x + y*y - 2*z*z)/2 - bf_z*z*(3*x*x + 3*y*y - 2*z*z)/2; + eval_z[4] = sqrt_6*x*(8*bf*z - bf_z*(x*x + y*y - 4*z*z))/4; + eval_z[5] = sqrt_15*(bf + bf_z*z)*(x*x - y*y)/2; + eval_z[6] = sqrt_10*bf_z*x*(x*x - 3*y*y)/4; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_4( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = sqrt_35*bf*x*y*(x*x - y*y)/2; + eval[1] = sqrt_70*bf*y*z*(3*x*x - y*y)/4; + eval[2] = sqrt_5*bf*x*y*(-x*x - y*y + 6*z*z)/2; + eval[3] = sqrt_10*bf*y*z*(-3*x*x - 3*y*y + 4*z*z)/4; + eval[4] = bf*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; + eval[5] = sqrt_10*bf*x*z*(-3*x*x - 3*y*y + 4*z*z)/4; + eval[6] = sqrt_5*bf*(-x*x*x*x + 6*x*x*z*z + y*y*y*y - 6*y*y*z*z)/4; + eval[7] = sqrt_70*bf*x*z*(x*x - 3*y*y)/4; + eval[8] = sqrt_35*bf*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_4_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = sqrt_35*y*(bf*(3*x*x - y*y) + bf_x*x*(x*x - y*y))/2; + eval_x[1] = sqrt_70*y*z*(6*bf*x + bf_x*(3*x*x - y*y))/4; + eval_x[2] = -sqrt_5*y*(bf*(3*x*x + y*y - 6*z*z) + bf_x*x*(x*x + y*y - 6*z*z))/2; + eval_x[3] = -sqrt_10*y*z*(6*bf*x + bf_x*(3*x*x + 3*y*y - 4*z*z))/4; + eval_x[4] = 3*bf*x*(x*x + y*y - 4*z*z)/2 + bf_x*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; + eval_x[5] = -sqrt_10*z*(bf*(9*x*x + 3*y*y - 4*z*z) + bf_x*x*(3*x*x + 3*y*y - 4*z*z))/4; + eval_x[6] = -sqrt_5*(bf*x*(x*x - 3*z*z) + bf_x*(x*x*x*x - 6*x*x*z*z - y*y*y*y + 6*y*y*z*z)/4); + eval_x[7] = sqrt_70*z*(3*bf*(x*x - y*y) + bf_x*x*(x*x - 3*y*y))/4; + eval_x[8] = sqrt_35*(4*bf*x*(x*x - 3*y*y) + bf_x*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; + + eval_y[0] = sqrt_35*x*(-bf*(-x*x + 3*y*y) + bf_y*y*(x*x - y*y))/2; + eval_y[1] = sqrt_70*z*(-3*bf*(-x*x + y*y) + bf_y*y*(3*x*x - y*y))/4; + eval_y[2] = -sqrt_5*x*(bf*(x*x + 3*y*y - 6*z*z) + bf_y*y*(x*x + y*y - 6*z*z))/2; + eval_y[3] = -sqrt_10*z*(bf*(3*x*x + 9*y*y - 4*z*z) + bf_y*y*(3*x*x + 3*y*y - 4*z*z))/4; + eval_y[4] = 3*bf*y*(x*x + y*y - 4*z*z)/2 + bf_y*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; + eval_y[5] = -sqrt_10*x*z*(6*bf*y + bf_y*(3*x*x + 3*y*y - 4*z*z))/4; + eval_y[6] = sqrt_5*(bf*y*(y*y - 3*z*z) - bf_y*(x*x*x*x - 6*x*x*z*z - y*y*y*y + 6*y*y*z*z)/4); + eval_y[7] = sqrt_70*x*z*(-6*bf*y + bf_y*(x*x - 3*y*y))/4; + eval_y[8] = sqrt_35*(-4*bf*y*(3*x*x - y*y) + bf_y*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; + + eval_z[0] = sqrt_35*bf_z*x*y*(x*x - y*y)/2; + eval_z[1] = sqrt_70*y*(bf + bf_z*z)*(3*x*x - y*y)/4; + eval_z[2] = sqrt_5*x*y*(12*bf*z - bf_z*(x*x + y*y - 6*z*z))/2; + eval_z[3] = sqrt_10*y*(3*bf*(-x*x - y*y + 4*z*z) - bf_z*z*(3*x*x + 3*y*y - 4*z*z))/4; + eval_z[4] = -2*bf*z*(3*x*x + 3*y*y - 2*z*z) + bf_z*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; + eval_z[5] = sqrt_10*x*(3*bf*(-x*x - y*y + 4*z*z) - bf_z*z*(3*x*x + 3*y*y - 4*z*z))/4; + eval_z[6] = sqrt_5*(12*bf*z*(x*x - y*y) - bf_z*(x*x*x*x - 6*x*x*z*z - y*y*y*y + 6*y*y*z*z))/4; + eval_z[7] = sqrt_70*x*(bf + bf_z*z)*(x*x - 3*y*y)/4; + eval_z[8] = sqrt_35*bf_z*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_5( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = 3*sqrt_14*bf*y*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; + eval[1] = 3*sqrt_35*bf*x*y*z*(x*x - y*y)/2; + eval[2] = sqrt_70*bf*y*(-3*x*x*x*x - 2*x*x*y*y + 24*x*x*z*z + y*y*y*y - 8*y*y*z*z)/16; + eval[3] = sqrt_105*bf*x*y*z*(-x*x - y*y + 2*z*z)/2; + eval[4] = sqrt_15*bf*y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8; + eval[5] = bf*z*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z)/8; + eval[6] = sqrt_15*bf*x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8; + eval[7] = sqrt_105*bf*z*(-x*x*x*x + 2*x*x*z*z + y*y*y*y - 2*y*y*z*z)/4; + eval[8] = sqrt_70*bf*x*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z)/16; + eval[9] = 3*sqrt_35*bf*z*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; + eval[10] = 3*sqrt_14*bf*x*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_5_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = 3*sqrt_14*y*(20*bf*x*(x*x - y*y) + bf_x*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y))/16; + eval_x[1] = 3*sqrt_35*y*z*(bf*(3*x*x - y*y) + bf_x*x*(x*x - y*y))/2; + eval_x[2] = -sqrt_70*y*(4*bf*x*(3*x*x + y*y - 12*z*z) + bf_x*(3*x*x*x*x + 2*x*x*y*y - 24*x*x*z*z - y*y*y*y + 8*y*y*z*z))/16; + eval_x[3] = -sqrt_105*y*z*(bf*(3*x*x + y*y - 2*z*z) + bf_x*x*(x*x + y*y - 2*z*z))/2; + eval_x[4] = sqrt_15*y*(4*bf*x*(x*x + y*y - 6*z*z) + bf_x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; + eval_x[5] = z*(20*bf*x*(3*x*x + 3*y*y - 4*z*z) + bf_x*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z))/8; + eval_x[6] = sqrt_15*(bf*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + 4*x*x*(x*x + y*y - 6*z*z) + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z) + bf_x*x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; + eval_x[7] = -sqrt_105*z*(4*bf*x*(x*x - z*z) + bf_x*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z))/4; + eval_x[8] = sqrt_70*(bf*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 4*x*x*(-x*x + y*y + 4*z*z) + 3*y*y*y*y - 24*y*y*z*z) + bf_x*x*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z))/16; + eval_x[9] = 3*sqrt_35*z*(4*bf*x*(x*x - 3*y*y) + bf_x*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; + eval_x[10] = 3*sqrt_14*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + bf_x*x*x*x*x*x - 10*bf_x*x*x*x*y*y + 5*bf_x*x*y*y*y*y)/16; + + eval_y[0] = 3*sqrt_14*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + 5*bf_y*x*x*x*x*y - 10*bf_y*x*x*y*y*y + bf_y*y*y*y*y*y)/16; + eval_y[1] = 3*sqrt_35*x*z*(-bf*(-x*x + 3*y*y) + bf_y*y*(x*x - y*y))/2; + eval_y[2] = sqrt_70*(-3*bf*x*x*x*x - 6*bf*x*x*y*y + 24*bf*x*x*z*z + 5*bf*y*y*y*y - 24*bf*y*y*z*z - 3*bf_y*x*x*x*x*y - 2*bf_y*x*x*y*y*y + 24*bf_y*x*x*y*z*z + bf_y*y*y*y*y*y - 8*bf_y*y*y*y*z*z)/16; + eval_y[3] = -sqrt_105*x*z*(bf*(x*x + 3*y*y - 2*z*z) + bf_y*y*(x*x + y*y - 2*z*z))/2; + eval_y[4] = sqrt_15*(bf*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 4*y*y*(x*x + y*y - 6*z*z) + 8*z*z*z*z) + bf_y*y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; + eval_y[5] = z*(20*bf*y*(3*x*x + 3*y*y - 4*z*z) + bf_y*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z))/8; + eval_y[6] = sqrt_15*x*(4*bf*y*(x*x + y*y - 6*z*z) + bf_y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; + eval_y[7] = sqrt_105*z*(4*bf*y*(y*y - z*z) - bf_y*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z))/4; + eval_y[8] = sqrt_70*x*(4*bf*y*(x*x + 3*y*y - 12*z*z) + bf_y*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z))/16; + eval_y[9] = 3*sqrt_35*z*(-4*bf*y*(3*x*x - y*y) + bf_y*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; + eval_y[10] = 3*sqrt_14*x*(-20*bf*y*(x*x - y*y) + bf_y*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y))/16; + + eval_z[0] = 3*sqrt_14*bf_z*y*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; + eval_z[1] = 3*sqrt_35*x*y*(bf + bf_z*z)*(x*x - y*y)/2; + eval_z[2] = sqrt_70*y*(16*bf*z*(3*x*x - y*y) - bf_z*(3*x*x*x*x + 2*x*x*y*y - 24*x*x*z*z - y*y*y*y + 8*y*y*z*z))/16; + eval_z[3] = sqrt_105*x*y*(bf*(-x*x - y*y + 6*z*z) - bf_z*z*(x*x + y*y - 2*z*z))/2; + eval_z[4] = sqrt_15*y*(-8*bf*z*(3*x*x + 3*y*y - 4*z*z) + bf_z*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; + eval_z[5] = bf*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z + z*z*(-80*x*x - 80*y*y + 32*z*z))/8 + bf_z*z*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z)/8; + eval_z[6] = sqrt_15*x*(-8*bf*z*(3*x*x + 3*y*y - 4*z*z) + bf_z*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; + eval_z[7] = sqrt_105*(bf*(-x*x*x*x + 2*x*x*z*z + y*y*y*y - 2*y*y*z*z + 4*z*z*(x*x - y*y)) - bf_z*z*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z))/4; + eval_z[8] = sqrt_70*x*(16*bf*z*(x*x - 3*y*y) + bf_z*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z))/16; + eval_z[9] = 3*sqrt_35*(bf + bf_z*z)*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; + eval_z[10] = 3*sqrt_14*bf_z*x*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_6( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = sqrt_462*bf*x*y*(3*x*x*x*x - 10*x*x*y*y + 3*y*y*y*y)/16; + eval[1] = 3*sqrt_154*bf*y*z*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; + eval[2] = 3*sqrt_7*bf*x*y*(-x*x*x*x + 10*x*x*z*z + y*y*y*y - 10*y*y*z*z)/4; + eval[3] = sqrt_210*bf*y*z*(-9*x*x*x*x - 6*x*x*y*y + 24*x*x*z*z + 3*y*y*y*y - 8*y*y*z*z)/16; + eval[4] = sqrt_210*bf*x*y*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z)/16; + eval[5] = sqrt_21*bf*y*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z)/8; + eval[6] = bf*(-5*x*x*x*x*x*x - 15*x*x*x*x*y*y + 90*x*x*x*x*z*z - 15*x*x*y*y*y*y + 180*x*x*y*y*z*z - 120*x*x*z*z*z*z - 5*y*y*y*y*y*y + 90*y*y*y*y*z*z - 120*y*y*z*z*z*z + 16*z*z*z*z*z*z)/16; + eval[7] = sqrt_21*bf*x*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z)/8; + eval[8] = sqrt_210*bf*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z)/32; + eval[9] = sqrt_210*bf*x*z*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z)/16; + eval[10] = 3*sqrt_7*bf*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z)/16; + eval[11] = 3*sqrt_154*bf*x*z*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; + eval[12] = sqrt_462*bf*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y)/32; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_6_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = sqrt_462*y*(15*bf*x*x*x*x - 30*bf*x*x*y*y + 3*bf*y*y*y*y + 3*bf_x*x*x*x*x*x - 10*bf_x*x*x*x*y*y + 3*bf_x*x*y*y*y*y)/16; + eval_x[1] = 3*sqrt_154*y*z*(20*bf*x*(x*x - y*y) + bf_x*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y))/16; + eval_x[2] = 3*sqrt_7*y*(-5*bf*x*x*x*x + 30*bf*x*x*z*z + bf*y*y*y*y - 10*bf*y*y*z*z - bf_x*x*x*x*x*x + 10*bf_x*x*x*x*z*z + bf_x*x*y*y*y*y - 10*bf_x*x*y*y*z*z)/4; + eval_x[3] = -sqrt_210*y*z*(12*bf*x*(3*x*x + y*y - 4*z*z) + bf_x*(9*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z - 3*y*y*y*y + 8*y*y*z*z))/16; + eval_x[4] = sqrt_210*y*(bf*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + 4*x*x*(x*x + y*y - 8*z*z) + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z) + bf_x*x*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z))/16; + eval_x[5] = sqrt_21*y*z*(20*bf*x*(x*x + y*y - 2*z*z) + bf_x*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; + eval_x[6] = -15*bf*x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8 - bf_x*(5*x*x*x*x*x*x + 15*x*x*x*x*y*y - 90*x*x*x*x*z*z + 15*x*x*y*y*y*y - 180*x*x*y*y*z*z + 120*x*x*z*z*z*z + 5*y*y*y*y*y*y - 90*y*y*y*y*z*z + 120*y*y*z*z*z*z - 16*z*z*z*z*z*z)/16; + eval_x[7] = sqrt_21*z*(bf*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 20*x*x*(x*x + y*y - 2*z*z) + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z) + bf_x*x*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; + eval_x[8] = sqrt_210*(2*bf*x*(3*x*x*x*x + 2*x*x*y*y - 32*x*x*z*z - y*y*y*y + 16*z*z*z*z) + bf_x*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z))/32; + eval_x[9] = sqrt_210*z*(-15*bf*x*x*x*x + 18*bf*x*x*y*y + 24*bf*x*x*z*z + 9*bf*y*y*y*y - 24*bf*y*y*z*z - 3*bf_x*x*x*x*x*x + 6*bf_x*x*x*x*y*y + 8*bf_x*x*x*x*z*z + 9*bf_x*x*y*y*y*y - 24*bf_x*x*y*y*z*z)/16; + eval_x[10] = 3*sqrt_7*(2*bf*x*(-3*x*x*x*x + 10*x*x*y*y + 20*x*x*z*z + 5*y*y*y*y - 60*y*y*z*z) + bf_x*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z))/16; + eval_x[11] = 3*sqrt_154*z*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + bf_x*x*x*x*x*x - 10*bf_x*x*x*x*y*y + 5*bf_x*x*y*y*y*y)/16; + eval_x[12] = sqrt_462*(6*bf*x*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y) + bf_x*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y))/32; + + eval_y[0] = sqrt_462*x*(3*bf*x*x*x*x - 30*bf*x*x*y*y + 15*bf*y*y*y*y + 3*bf_y*x*x*x*x*y - 10*bf_y*x*x*y*y*y + 3*bf_y*y*y*y*y*y)/16; + eval_y[1] = 3*sqrt_154*z*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + 5*bf_y*x*x*x*x*y - 10*bf_y*x*x*y*y*y + bf_y*y*y*y*y*y)/16; + eval_y[2] = 3*sqrt_7*x*(bf*(-x*x*x*x + 10*x*x*z*z + y*y*y*y - 10*y*y*z*z + 4*y*y*(y*y - 5*z*z)) - bf_y*y*(x*x*x*x - 10*x*x*z*z - y*y*y*y + 10*y*y*z*z))/4; + eval_y[3] = sqrt_210*z*(-9*bf*x*x*x*x - 18*bf*x*x*y*y + 24*bf*x*x*z*z + 15*bf*y*y*y*y - 24*bf*y*y*z*z - 9*bf_y*x*x*x*x*y - 6*bf_y*x*x*y*y*y + 24*bf_y*x*x*y*z*z + 3*bf_y*y*y*y*y*y - 8*bf_y*y*y*y*z*z)/16; + eval_y[4] = sqrt_210*x*(bf*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 4*y*y*(x*x + y*y - 8*z*z) + 16*z*z*z*z) + bf_y*y*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z))/16; + eval_y[5] = sqrt_21*z*(bf*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 20*y*y*(x*x + y*y - 2*z*z) + 8*z*z*z*z) + bf_y*y*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; + eval_y[6] = -15*bf*y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8 - bf_y*(5*x*x*x*x*x*x + 15*x*x*x*x*y*y - 90*x*x*x*x*z*z + 15*x*x*y*y*y*y - 180*x*x*y*y*z*z + 120*x*x*z*z*z*z + 5*y*y*y*y*y*y - 90*y*y*y*y*z*z + 120*y*y*z*z*z*z - 16*z*z*z*z*z*z)/16; + eval_y[7] = sqrt_21*x*z*(20*bf*y*(x*x + y*y - 2*z*z) + bf_y*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; + eval_y[8] = sqrt_210*(-2*bf*y*(-x*x*x*x + 2*x*x*y*y + 3*y*y*y*y - 32*y*y*z*z + 16*z*z*z*z) + bf_y*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z))/32; + eval_y[9] = sqrt_210*x*z*(12*bf*y*(x*x + 3*y*y - 4*z*z) + bf_y*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z))/16; + eval_y[10] = 3*sqrt_7*(2*bf*y*(5*x*x*x*x + 10*x*x*y*y - 60*x*x*z*z - 3*y*y*y*y + 20*y*y*z*z) + bf_y*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z))/16; + eval_y[11] = 3*sqrt_154*x*z*(-20*bf*y*(x*x - y*y) + bf_y*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y))/16; + eval_y[12] = sqrt_462*(-6*bf*y*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y) + bf_y*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y))/32; + + eval_z[0] = sqrt_462*bf_z*x*y*(3*x*x*x*x - 10*x*x*y*y + 3*y*y*y*y)/16; + eval_z[1] = 3*sqrt_154*y*(bf + bf_z*z)*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; + eval_z[2] = 3*sqrt_7*x*y*(20*bf*z*(x*x - y*y) - bf_z*(x*x*x*x - 10*x*x*z*z - y*y*y*y + 10*y*y*z*z))/4; + eval_z[3] = sqrt_210*y*(bf*(-9*x*x*x*x - 6*x*x*y*y + 24*x*x*z*z + 3*y*y*y*y - 8*y*y*z*z + 16*z*z*(3*x*x - y*y)) - bf_z*z*(9*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z - 3*y*y*y*y + 8*y*y*z*z))/16; + eval_z[4] = sqrt_210*x*y*(-32*bf*z*(x*x + y*y - 2*z*z) + bf_z*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z))/16; + eval_z[5] = sqrt_21*y*(-bf*(-5*x*x*x*x - 10*x*x*y*y + 20*x*x*z*z - 5*y*y*y*y + 20*y*y*z*z - 8*z*z*z*z + z*z*(40*x*x + 40*y*y - 32*z*z)) + bf_z*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; + eval_z[6] = 3*bf*z*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z)/4 - bf_z*(5*x*x*x*x*x*x + 15*x*x*x*x*y*y - 90*x*x*x*x*z*z + 15*x*x*y*y*y*y - 180*x*x*y*y*z*z + 120*x*x*z*z*z*z + 5*y*y*y*y*y*y - 90*y*y*y*y*z*z + 120*y*y*z*z*z*z - 16*z*z*z*z*z*z)/16; + eval_z[7] = sqrt_21*x*(-bf*(-5*x*x*x*x - 10*x*x*y*y + 20*x*x*z*z - 5*y*y*y*y + 20*y*y*z*z - 8*z*z*z*z + z*z*(40*x*x + 40*y*y - 32*z*z)) + bf_z*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; + eval_z[8] = sqrt_210*(-bf*z*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z) + bf_z*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z)/32); + eval_z[9] = sqrt_210*x*(bf*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z + 16*z*z*(x*x - 3*y*y)) + bf_z*z*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z))/16; + eval_z[10] = 3*sqrt_7*(20*bf*z*(x*x*x*x - 6*x*x*y*y + y*y*y*y) + bf_z*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z))/16; + eval_z[11] = 3*sqrt_154*x*(bf + bf_z*z)*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; + eval_z[12] = sqrt_462*bf_z*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y)/32; + +} + + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular( + const int64_t l, + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + +//switch( l ) { + + //case 0: + if( l == 0 ) { + gaueval_spherical_unnorm_angular_0( bf, x, y, z, eval ); + //break; + + //case 1: + } else if( l == 1 ) { + gaueval_spherical_unnorm_angular_1( bf, x, y, z, eval ); + //break; + + //case 2: + } else if( l == 2 ) { + gaueval_spherical_unnorm_angular_2( bf, x, y, z, eval ); + //break; + + //case 3: + } else if( l == 3 ) { + gaueval_spherical_unnorm_angular_3( bf, x, y, z, eval ); + //break; + + //case 4: + } else if( l == 4 ) { + gaueval_spherical_unnorm_angular_4( bf, x, y, z, eval ); + //break; + + //case 5: + } else if( l == 5 ) { + gaueval_spherical_unnorm_angular_5( bf, x, y, z, eval ); + //break; + + //case 6: + } else if( l == 6 ) { + gaueval_spherical_unnorm_angular_6( bf, x, y, z, eval ); + //break; + + //default: + } else { + assert( false && "L < L_MAX" ); + //break; + } + +//} // switch(l) + +} // gaueval_spherical_unnorm_angular + + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_deriv1( + const int64_t l, + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval, + double* eval_x, + double* eval_y, + double* eval_z +) { + +//switch( l ) { + +// case 0: + if( l == 0 ) { + gaueval_spherical_unnorm_angular_0( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_0_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 1: + } else if( l == 1 ) { + gaueval_spherical_unnorm_angular_1( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_1_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 2: + } else if( l == 2 ) { + gaueval_spherical_unnorm_angular_2( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_2_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 3: + } else if( l == 3 ) { + gaueval_spherical_unnorm_angular_3( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 4: + } else if( l == 4 ) { + gaueval_spherical_unnorm_angular_4( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_4_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 5: + } else if( l == 5 ) { + gaueval_spherical_unnorm_angular_5( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_5_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// case 6: + } else if( l == 6 ) { + gaueval_spherical_unnorm_angular_6( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_6_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +// default: + } else { + assert( false && "L < L_MAX" ); +// break; + } + +//} // switch(l) + +} // gaueval_spherical_unnorm_angular_deriv1 + + + +} // namespace gpgaueval diff --git a/src/cuda_integrator/collocation_generator/gaueval_angular_template.hpp b/src/cuda_integrator/collocation_generator/gaueval_angular_template.hpp new file mode 100644 index 000000000..afa035c87 --- /dev/null +++ b/src/cuda_integrator/collocation_generator/gaueval_angular_template.hpp @@ -0,0 +1,117 @@ +#pragma once +#include "gaueval_device_constants.hpp" +#include + +#ifndef GPGAUEVAL_INLINE +# define GPGAUEVAL_INLINE __inline__ +#endif + +namespace gpgaueval { + +$for( L in range(L_max + 1) )\ +GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_$(L)( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + $(body[L]) + +} + +GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_$(L)_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + $(body_d1[L]) + +} + +$endfor\ + +GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular( + const int64_t l, + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + +//switch( l ) { + +$for( L in range(L_max + 1) )\ + //case $(L): + $if( L == 0 )\ + if( l == $(L) ) { + $else\ + } else if( l == $(L) ) { + $endif\ + gaueval_$(name)_angular_$(L)( bf, x, y, z, eval ); + //break; + +$endfor\ + //default: + } else { + assert( false && "L < L_MAX" ); + //break; + } + +//} // switch(l) + +} // gaueval_$(name)_angular + + +GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_deriv1( + const int64_t l, + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval, + double* eval_x, + double* eval_y, + double* eval_z +) { + +//switch( l ) { + +$for( L in range(L_max + 1) )\ +// case $(L): + $if( L == 0 )\ + if( l == $(L) ) { + $else\ + } else if( l == $(L) ) { + $endif\ + gaueval_$(name)_angular_$(L)( bf, x, y, z, eval ); + gaueval_$(name)_angular_$(L)_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); +// break; + +$endfor\ +// default: + } else { + assert( false && "L < L_MAX" ); +// break; + } + +//} // switch(l) + +} // gaueval_$(name)_angular_deriv1 + + + +} // namespace gpgaueval diff --git a/src/cuda_integrator/collocation_generator/gaueval_device_constants.hpp b/src/cuda_integrator/collocation_generator/gaueval_device_constants.hpp new file mode 100644 index 000000000..a87dbd1d4 --- /dev/null +++ b/src/cuda_integrator/collocation_generator/gaueval_device_constants.hpp @@ -0,0 +1,33 @@ +#pragma once + +namespace gpgaueval { + + constexpr double sqrt_5 = 2.23606797749979; + constexpr double sqrt_30 = 5.477225575051661; + constexpr double sqrt_21 = 4.58257569495584; + constexpr double sqrt_42 = 6.48074069840786; + constexpr double sqrt_77 = 8.774964387392123; + constexpr double sqrt_6 = 2.449489742783178; + constexpr double sqrt_210 = 14.491376746189438; + constexpr double sqrt_770 = 27.748873851023216; + constexpr double sqrt_10 = 3.1622776601683795; + constexpr double sqrt_231 = 15.198684153570664; + constexpr double sqrt_7 = 2.6457513110645907; + constexpr double sqrt_154 = 12.409673645990857; + constexpr double sqrt_462 = 21.494185260204677; + constexpr double sqrt_385 = 19.621416870348583; + constexpr double sqrt_2310 = 48.062459362791664; + constexpr double sqrt_110 = 10.488088481701515; + constexpr double sqrt_33 = 5.744562646538029; + constexpr double sqrt_3 = 1.7320508075688772; + constexpr double sqrt_70 = 8.366600265340756; + constexpr double sqrt_22 = 4.69041575982343; + constexpr double sqrt_11 = 3.3166247903554; + constexpr double sqrt_2 = 1.4142135623730951; + constexpr double sqrt_15 = 3.872983346207417; + constexpr double sqrt_35 = 5.916079783099616; + constexpr double sqrt_55 = 7.416198487095663; + constexpr double sqrt_105 = 10.246950765959598; + constexpr double sqrt_14 = 3.7416573867739413; + +} // namespace gpgaueval diff --git a/src/cuda_integrator/collocation_generator/gaueval_device_constants_template.hpp b/src/cuda_integrator/collocation_generator/gaueval_device_constants_template.hpp new file mode 100644 index 000000000..986b5ae76 --- /dev/null +++ b/src/cuda_integrator/collocation_generator/gaueval_device_constants_template.hpp @@ -0,0 +1,9 @@ +#pragma once + +namespace gpgaueval { + +$for( x in const_lines )\ + $(x) +$endfor\ + +} // namespace gpgaueval diff --git a/src/cuda_integrator/collocation_generator/gaueval_kernels_cartesian.cu b/src/cuda_integrator/collocation_generator/gaueval_kernels_cartesian.cu new file mode 100644 index 000000000..8b0ca166a --- /dev/null +++ b/src/cuda_integrator/collocation_generator/gaueval_kernels_cartesian.cu @@ -0,0 +1,129 @@ +#include +#include +#include + +#include "gaueval_kernels.hpp" +#include "gaueval_angular_cartesian.hpp" +#include "gaueval_angular_spherical.hpp" +#include "gaueval_angular_spherical_unnorm.hpp" + +namespace gpgaueval { + +__global__ +void gaueval_device_cartesian_kernel( + const size_t nshells, + const size_t nbf, + const size_t npts, + const StaticShell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x< nshells and tid_y < npts ) { + + const size_t ish = tid_x; + const size_t ipt = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[ish]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = device::array_data( shell.O ); + const auto* alpha = device::array_data( shell.alpha ); + const auto* coeff = device::array_data( shell.coeff ); + + const double xc = pt[0] - O[0]; + const double yc = pt[1] - O[1]; + const double zc = pt[2] - O[2]; + + const double rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim; + double tmp = 0.; + for( size_t i = 0; i < nprim; ++i ) + tmp += coeff[i] * std::exp( - alpha[i] * rsq ); + + double * bf_eval = eval_device + ibf + ipt*nbf; + gaueval_cartesian_angular( shell.l, tmp, xc, yc, zc, bf_eval ); + + } + +} + + + +__global__ +void gaueval_device_cartesian_kernel_deriv1( + const size_t nshells, + const size_t nbf, + const size_t npts, + const StaticShell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device, + double* deval_device_x, + double* deval_device_y, + double* deval_device_z +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x< nshells and tid_y < npts ) { + + const size_t ish = tid_x; + const size_t ipt = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[ish]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = device::array_data( shell.O ); + const auto* alpha = device::array_data( shell.alpha ); + const auto* coeff = device::array_data( shell.coeff ); + + const double xc = pt[0] - O[0]; + const double yc = pt[1] - O[1]; + const double zc = pt[2] - O[2]; + + const double rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim; + double tmp = 0.; + double tmp_x = 0., tmp_y = 0., tmp_z = 0.; + for( size_t i = 0; i < nprim; ++i ) { + + const double a = alpha[i]; + const double e = coeff[i] * std::exp( - a * rsq ); + + const double ae = 2. * a * e; + + tmp += e; + tmp_x -= ae * xc; + tmp_y -= ae * yc; + tmp_z -= ae * zc; + + } + + double * bf_eval = eval_device + ibf + ipt*nbf; + double * dx_eval = deval_device_x + ibf + ipt*nbf; + double * dy_eval = deval_device_y + ibf + ipt*nbf; + double * dz_eval = deval_device_z + ibf + ipt*nbf; + + gaueval_cartesian_angular_deriv1( shell.l, tmp, tmp_x, tmp_y, tmp_z, xc, yc, zc, bf_eval, dx_eval, dy_eval, dz_eval ); + + } + + +} + + +} diff --git a/src/cuda_integrator/collocation_generator/gaueval_kernels_spherical.cu b/src/cuda_integrator/collocation_generator/gaueval_kernels_spherical.cu new file mode 100644 index 000000000..27cc813ec --- /dev/null +++ b/src/cuda_integrator/collocation_generator/gaueval_kernels_spherical.cu @@ -0,0 +1,129 @@ +#include +#include +#include + +#include "gaueval_kernels.hpp" +#include "gaueval_angular_cartesian.hpp" +#include "gaueval_angular_spherical.hpp" +#include "gaueval_angular_spherical_unnorm.hpp" + +namespace gpgaueval { + +__global__ +void gaueval_device_spherical_kernel( + const size_t nshells, + const size_t nbf, + const size_t npts, + const StaticShell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x< nshells and tid_y < npts ) { + + const size_t ish = tid_x; + const size_t ipt = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[ish]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = device::array_data( shell.O ); + const auto* alpha = device::array_data( shell.alpha ); + const auto* coeff = device::array_data( shell.coeff ); + + const double xc = pt[0] - O[0]; + const double yc = pt[1] - O[1]; + const double zc = pt[2] - O[2]; + + const double rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim; + double tmp = 0.; + for( size_t i = 0; i < nprim; ++i ) + tmp += coeff[i] * std::exp( - alpha[i] * rsq ); + + double * bf_eval = eval_device + ibf + ipt*nbf; + gaueval_spherical_angular( shell.l, tmp, xc, yc, zc, bf_eval ); + + } + +} + + + +__global__ +void gaueval_device_spherical_kernel_deriv1( + const size_t nshells, + const size_t nbf, + const size_t npts, + const StaticShell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device, + double* deval_device_x, + double* deval_device_y, + double* deval_device_z +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x< nshells and tid_y < npts ) { + + const size_t ish = tid_x; + const size_t ipt = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[ish]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = device::array_data( shell.O ); + const auto* alpha = device::array_data( shell.alpha ); + const auto* coeff = device::array_data( shell.coeff ); + + const double xc = pt[0] - O[0]; + const double yc = pt[1] - O[1]; + const double zc = pt[2] - O[2]; + + const double rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim; + double tmp = 0.; + double tmp_x = 0., tmp_y = 0., tmp_z = 0.; + for( size_t i = 0; i < nprim; ++i ) { + + const double a = alpha[i]; + const double e = coeff[i] * std::exp( - a * rsq ); + + const double ae = 2. * a * e; + + tmp += e; + tmp_x -= ae * xc; + tmp_y -= ae * yc; + tmp_z -= ae * zc; + + } + + double * bf_eval = eval_device + ibf + ipt*nbf; + double * dx_eval = deval_device_x + ibf + ipt*nbf; + double * dy_eval = deval_device_y + ibf + ipt*nbf; + double * dz_eval = deval_device_z + ibf + ipt*nbf; + + gaueval_spherical_angular_deriv1( shell.l, tmp, tmp_x, tmp_y, tmp_z, xc, yc, zc, bf_eval, dx_eval, dy_eval, dz_eval ); + + } + + +} + + +} diff --git a/src/cuda_integrator/collocation_generator/gaueval_kernels_spherical_unnorm.cu b/src/cuda_integrator/collocation_generator/gaueval_kernels_spherical_unnorm.cu new file mode 100644 index 000000000..0fc4ae190 --- /dev/null +++ b/src/cuda_integrator/collocation_generator/gaueval_kernels_spherical_unnorm.cu @@ -0,0 +1,129 @@ +#include +#include +#include + +#include "gaueval_kernels.hpp" +#include "gaueval_angular_cartesian.hpp" +#include "gaueval_angular_spherical.hpp" +#include "gaueval_angular_spherical_unnorm.hpp" + +namespace gpgaueval { + +__global__ +void gaueval_device_spherical_unnorm_kernel( + const size_t nshells, + const size_t nbf, + const size_t npts, + const StaticShell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x< nshells and tid_y < npts ) { + + const size_t ish = tid_x; + const size_t ipt = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[ish]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = device::array_data( shell.O ); + const auto* alpha = device::array_data( shell.alpha ); + const auto* coeff = device::array_data( shell.coeff ); + + const double xc = pt[0] - O[0]; + const double yc = pt[1] - O[1]; + const double zc = pt[2] - O[2]; + + const double rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim; + double tmp = 0.; + for( size_t i = 0; i < nprim; ++i ) + tmp += coeff[i] * std::exp( - alpha[i] * rsq ); + + double * bf_eval = eval_device + ibf + ipt*nbf; + gaueval_spherical_unnorm_angular( shell.l, tmp, xc, yc, zc, bf_eval ); + + } + +} + + + +__global__ +void gaueval_device_spherical_unnorm_kernel_deriv1( + const size_t nshells, + const size_t nbf, + const size_t npts, + const StaticShell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device, + double* deval_device_x, + double* deval_device_y, + double* deval_device_z +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x< nshells and tid_y < npts ) { + + const size_t ish = tid_x; + const size_t ipt = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[ish]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = device::array_data( shell.O ); + const auto* alpha = device::array_data( shell.alpha ); + const auto* coeff = device::array_data( shell.coeff ); + + const double xc = pt[0] - O[0]; + const double yc = pt[1] - O[1]; + const double zc = pt[2] - O[2]; + + const double rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim; + double tmp = 0.; + double tmp_x = 0., tmp_y = 0., tmp_z = 0.; + for( size_t i = 0; i < nprim; ++i ) { + + const double a = alpha[i]; + const double e = coeff[i] * std::exp( - a * rsq ); + + const double ae = 2. * a * e; + + tmp += e; + tmp_x -= ae * xc; + tmp_y -= ae * yc; + tmp_z -= ae * zc; + + } + + double * bf_eval = eval_device + ibf + ipt*nbf; + double * dx_eval = deval_device_x + ibf + ipt*nbf; + double * dy_eval = deval_device_y + ibf + ipt*nbf; + double * dz_eval = deval_device_z + ibf + ipt*nbf; + + gaueval_spherical_unnorm_angular_deriv1( shell.l, tmp, tmp_x, tmp_y, tmp_z, xc, yc, zc, bf_eval, dx_eval, dy_eval, dz_eval ); + + } + + +} + + +} diff --git a/src/cuda_integrator/collocation_generator/gaueval_kernels_template.cu b/src/cuda_integrator/collocation_generator/gaueval_kernels_template.cu new file mode 100644 index 000000000..c3edd0a2d --- /dev/null +++ b/src/cuda_integrator/collocation_generator/gaueval_kernels_template.cu @@ -0,0 +1,129 @@ +#include +#include +#include + +#include "gaueval_kernels.hpp" +#include "gaueval_angular_cartesian.hpp" +#include "gaueval_angular_spherical.hpp" +#include "gaueval_angular_spherical_unnorm.hpp" + +namespace gpgaueval { + +__global__ +void gaueval_device_$(ang_name)_kernel( + const size_t nshells, + const size_t nbf, + const size_t npts, + const StaticShell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x< nshells and tid_y < npts ) { + + const size_t ish = tid_x; + const size_t ipt = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[ish]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = device::array_data( shell.O ); + const auto* alpha = device::array_data( shell.alpha ); + const auto* coeff = device::array_data( shell.coeff ); + + const double xc = pt[0] - O[0]; + const double yc = pt[1] - O[1]; + const double zc = pt[2] - O[2]; + + const double rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim; + double tmp = 0.; + for( size_t i = 0; i < nprim; ++i ) + tmp += coeff[i] * std::exp( - alpha[i] * rsq ); + + double * bf_eval = eval_device + ibf + ipt*nbf; + gaueval_$(ang_name)_angular( shell.l, tmp, xc, yc, zc, bf_eval ); + + } + +} + + + +__global__ +void gaueval_device_$(ang_name)_kernel_deriv1( + const size_t nshells, + const size_t nbf, + const size_t npts, + const StaticShell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device, + double* deval_device_x, + double* deval_device_y, + double* deval_device_z +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x< nshells and tid_y < npts ) { + + const size_t ish = tid_x; + const size_t ipt = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[ish]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = device::array_data( shell.O ); + const auto* alpha = device::array_data( shell.alpha ); + const auto* coeff = device::array_data( shell.coeff ); + + const double xc = pt[0] - O[0]; + const double yc = pt[1] - O[1]; + const double zc = pt[2] - O[2]; + + const double rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim; + double tmp = 0.; + double tmp_x = 0., tmp_y = 0., tmp_z = 0.; + for( size_t i = 0; i < nprim; ++i ) { + + const double a = alpha[i]; + const double e = coeff[i] * std::exp( - a * rsq ); + + const double ae = 2. * a * e; + + tmp += e; + tmp_x -= ae * xc; + tmp_y -= ae * yc; + tmp_z -= ae * zc; + + } + + double * bf_eval = eval_device + ibf + ipt*nbf; + double * dx_eval = deval_device_x + ibf + ipt*nbf; + double * dy_eval = deval_device_y + ibf + ipt*nbf; + double * dz_eval = deval_device_z + ibf + ipt*nbf; + + gaueval_$(ang_name)_angular_deriv1( shell.l, tmp, tmp_x, tmp_y, tmp_z, xc, yc, zc, bf_eval, dx_eval, dy_eval, dz_eval ); + + } + + +} + + +} diff --git a/src/cuda_integrator/collocation_generator/generate_bfeval.py b/src/cuda_integrator/collocation_generator/generate_bfeval.py new file mode 100644 index 000000000..68d483ef5 --- /dev/null +++ b/src/cuda_integrator/collocation_generator/generate_bfeval.py @@ -0,0 +1,421 @@ +import os,sys +import re +import sympy +import math +import cmath +from math import factorial as fact +from sympy import factorial as symb_fact +from sympy import factorial2 as symb_fact2 +from scipy.special import binom as binomial +from sympy import exp as symb_exp +from sympy import I as symb_I + +def generate_cartesian_ls( L ): + + l = [] + for i in range(L+1): + lx = L - i + for j in range(i+1): + ly = i - j + lz = L - lx - ly + + l.append( [0, 0, 0] ) + + for k in range( lx - 1 ): + l[-1][0] = l[-1][0] + 1 + for k in range( ly - 1 ): + l[-1][1] = l[-1][1] + 1 + for k in range( lz - 1 ): + l[-1][2] = l[-1][2] + 1 + + if lx > 0: + l[-1][0] = l[-1][0] + 1 + if ly > 0: + l[-1][1] = l[-1][1] + 1 + if lz > 0: + l[-1][2] = l[-1][2] + 1 + + return l + + +def generate_spherical_coeff( l, m, lx, ly, lz ): + j = (lx + ly - abs(m)) + if j%2 == 0: + j = int(j / 2) + else: + return 0.0 + + prefactor = fact(2.*lx) * fact(2.*ly) * fact(2.*lz) * fact(l) + prefactor = prefactor * fact( l - abs(m) ) + prefactor = prefactor / (fact(2.*l) * fact(lx) * fact(ly) * fact(lz)) + prefactor = prefactor / fact( l + abs(m) ) + prefactor = math.sqrt( prefactor ) + + + term1 = 0.0 + for i in range( int((l - abs(m))/2)+1 ): + term1 = term1 + binomial(l,i) * binomial(i,j) * \ + math.pow(-1,i) * fact( 2*l - 2*i ) / \ + fact( l - abs(m) - 2*i ) + + term1 = term1 / math.pow(2,l) / fact(l) + + m_fact = 1. + if m < 0: + m_fact = -1. + + term2 = 0.0 + 0.0j + for k in range( j+1 ): + z = cmath.exp( m_fact * math.pi / 2. * (abs(m) - lx + 2*k) * 1.j ) + term2 = term2 + binomial(j,k) * binomial( abs(m), lx - 2*k ) * z + + val = prefactor * term1 * term2 + + if abs(val.real) < 1e-10: + val = 0.0 + val.imag*1j + if abs(val.imag) < 1e-10: + val = val.real + + return val + + +def generate_spherical_coeff_symb( l, m, lx, ly, lz, unnorm = False ): + j = (lx + ly - abs(m)) + if j%2 == 0: + j = int(j / 2) + else: + return sympy.Integer(0) + + + j_symb = sympy.Integer(j) + l_symb = sympy.Integer(l) + m_symb = sympy.Integer( abs(m) ) + lx_symb = sympy.Integer(lx) + ly_symb = sympy.Integer(ly) + lz_symb = sympy.Integer(lz) + + + + prefactor = symb_fact(2*lx_symb) * symb_fact(2*ly_symb) * symb_fact(2*lz_symb) * symb_fact(l_symb) + prefactor = prefactor * symb_fact( l_symb - m_symb ) + prefactor = prefactor / (symb_fact(2*l_symb) * symb_fact(lx_symb) * symb_fact(ly_symb) * symb_fact(lz_symb)) + prefactor = prefactor / symb_fact( l_symb + m_symb ) + + + # Ed's stupid normalization convention... + if unnorm: + prefactor = prefactor * symb_fact2( 2*l - 1 ) / symb_fact2( 2*lx - 1 ) / symb_fact2(2*ly - 1) / symb_fact2( 2*lz - 1 ) + + prefactor = sympy.sqrt( prefactor ) + + + term1 = sympy.Integer(0) + for i in range( int((l - abs(m))/2)+1 ): + term1 = term1 + sympy.Integer(binomial(l,i)) * sympy.Integer(binomial(i,j)) * \ + sympy.Integer(math.pow(-1,i)) * symb_fact( 2*l_symb - sympy.Integer(2*i) ) / \ + symb_fact( l_symb - m_symb - sympy.Integer(2*i) ) + + term1 = term1 / (2**l_symb) / symb_fact(l) + + + m_fact_symb = sympy.Integer(1) + if m < 0: + m_fact_symb = - m_fact_symb + + term2 = sympy.Integer(0) + for k in range( j+1 ): + z = sympy.exp( m_fact_symb * sympy.pi / 2 * (m_symb - lx_symb + sympy.Integer(2*k)) * symb_I ) + term2 = term2 + sympy.Integer(binomial(j,k)) * sympy.Integer(binomial( abs(m), lx - 2*k )) * z + + + return prefactor * term1 * term2 + + + +def generate_cartesian_angular( ls ): + [x,y,z,r] = sympy.symbols('x y z r', real=True) + + ang = [] + + for l in ls: + ang.append(r) + for i in range( l[0] ): ang[-1] = ang[-1] * x + for i in range( l[1] ): ang[-1] = ang[-1] * y + for i in range( l[2] ): ang[-1] = ang[-1] * z + + ang[-1] = ang[-1] / r + + return ang + +def generate_spherical_angular( L, unnorm = False ): + ls = generate_cartesian_ls( L ) + angs = generate_cartesian_angular( ls ) + + #r = sympy.symbols( 'r' ) + sph_angs = [] + for m in range( L + 1 ): + tmp_p = 0 + tmp_m = 0 + for i in range(len(ls)): + l = ls[i] + ang = angs[i] + + #c = generate_spherical_coeff( L, m, l[0],l[1],l[2] ) + c = generate_spherical_coeff_symb( L, m, l[0],l[1],l[2], unnorm ) + + if m == 0: + + tmp_p = tmp_p + c * ang + + else: + + c_p = ( c + sympy.conjugate(c) ) / sympy.sqrt(2) + c_m = ( c - sympy.conjugate(c) ) / sympy.sqrt(2) / symb_I + + tmp_p = tmp_p + c_p * ang + tmp_m = tmp_m + c_m * ang + + sph_angs.append( (m, tmp_p) ) + if m > 0: + sph_angs.append( (-m, tmp_m) ) + + sph_angs = sorted( sph_angs, key=lambda x: x[0] ) + + sph_angs_bare = [] + for a in sph_angs: + sph_angs_bare.append( sympy.simplify(a[1]) ) + + return sph_angs_bare + + +def generate_eval_lines( L, ang ): + [x,y,z,r] = sympy.symbols('x y z r', real=True) + [bf,bf_x,bf_y,bf_z] = sympy.symbols('bf bf_x bf_y bf_z',real=True) + + bf_eval_strs = [] + bf_x_eval_strs = [] + bf_y_eval_strs = [] + bf_z_eval_strs = [] + + for j in range(len(ang)): + + a = ang[j] + a_x = sympy.diff( a, x ) + a_y = sympy.diff( a, y ) + a_z = sympy.diff( a, z ) + + + + bf_eval = sympy.simplify( a * bf ) + bf_x_eval = sympy.simplify( a_x * bf + a * bf_x ) + bf_y_eval = sympy.simplify( a_y * bf + a * bf_y ) + bf_z_eval = sympy.simplify( a_z * bf + a * bf_z ) + + + bf_eval_str = 'eval[{}] = {};'.format(j,bf_eval) + bf_x_eval_str = 'eval_x[{}] = {};'.format(j,bf_x_eval) + bf_y_eval_str = 'eval_y[{}] = {};'.format(j,bf_y_eval) + bf_z_eval_str = 'eval_z[{}] = {};'.format(j,bf_z_eval) + + if L >= 2: + for k in range(2,L+1): + for X in ('x','y','z'): + pow_str = X + '**' + str(k) + repl_str = '' + for K in range(k-1): repl_str = repl_str + X + '*' + repl_str = repl_str + X + + bf_eval_str = bf_eval_str.replace(pow_str,repl_str) + bf_x_eval_str = bf_x_eval_str.replace(pow_str,repl_str) + bf_y_eval_str = bf_y_eval_str.replace(pow_str,repl_str) + bf_z_eval_str = bf_z_eval_str.replace(pow_str,repl_str) + + + bf_eval_strs .append(bf_eval_str ) + bf_x_eval_strs.append(bf_x_eval_str) + bf_y_eval_strs.append(bf_y_eval_str) + bf_z_eval_strs.append(bf_z_eval_str) + + return (bf_eval_strs, bf_x_eval_strs, bf_y_eval_strs, bf_z_eval_strs) + + + + +cart_header_fname = "gaueval_angular_cartesian.hpp" +sphr_header_fname = "gaueval_angular_spherical.hpp" +cons_header_fname = "gaueval_device_constants.hpp" + +cart_header_file = open( cart_header_fname, 'w' ) +sphr_header_file = open( sphr_header_fname, 'w' ) +cons_header_file = open( cons_header_fname, 'w' ) + +L_max = 4 +do_libint_norm = False +#do_libint_norm = True + +preamble = """ +#pragma once +#include "gaueval_device_constants.hpp" + +#define GPGAUEVAL_INLINE __inline__ + +namespace gpgaueval { +""" + + + +cart_header_file.write( preamble ) +sphr_header_file.write( preamble ) + +cartesian_bf_template = """ +GPGAUEVAL_INLINE __device__ void generate_cartesian_angular{}( + const double bf, + const double x, + const double y, + const double z, + double* eval +) {{ +""" + +cartesian_bf_deriv1_template = """ +GPGAUEVAL_INLINE __device__ void generate_cartesian_angular{}_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) {{ +""" + +spherical_bf_template = cartesian_bf_template.replace('cartesian','spherical') +spherical_bf_deriv1_template = cartesian_bf_deriv1_template.replace('cartesian','spherical') + + + +constant_lines = [] +for L in range( L_max + 1 ): + + sph_ang = generate_spherical_angular(L, do_libint_norm) + car_ang = generate_cartesian_angular( generate_cartesian_ls(L) ) + + sph_bf_eval_strs, sph_bf_x_eval_strs, sph_bf_y_eval_strs, sph_bf_z_eval_strs = generate_eval_lines( L, sph_ang ) + car_bf_eval_strs, car_bf_x_eval_strs, car_bf_y_eval_strs, car_bf_z_eval_strs = generate_eval_lines( L, car_ang ) + + + cartesian_bf_prototype = cartesian_bf_template.format( "_" + str(L) ) + spherical_bf_prototype = spherical_bf_template.format( "_" + str(L) ) + cartesian_bf_deriv1_prototype = cartesian_bf_deriv1_template.format( "_" + str(L) ) + spherical_bf_deriv1_prototype = spherical_bf_deriv1_template.format( "_" + str(L) ) + + + spherical_bf_func = spherical_bf_prototype + "\n" + for s in sph_bf_eval_strs: spherical_bf_func = spherical_bf_func + " " + s + "\n" + spherical_bf_func = spherical_bf_func + "\n}\n" + + spherical_bf_deriv1_func = spherical_bf_deriv1_prototype + "\n" + for s in sph_bf_x_eval_strs: spherical_bf_deriv1_func = spherical_bf_deriv1_func + " " + s + "\n" + spherical_bf_deriv1_func = spherical_bf_deriv1_func + "\n" + for s in sph_bf_y_eval_strs: spherical_bf_deriv1_func = spherical_bf_deriv1_func + " " + s + "\n" + spherical_bf_deriv1_func = spherical_bf_deriv1_func + "\n" + for s in sph_bf_z_eval_strs: spherical_bf_deriv1_func = spherical_bf_deriv1_func + " " + s + "\n" + spherical_bf_deriv1_func = spherical_bf_deriv1_func + "\n}\n" + + cartesian_bf_func = cartesian_bf_prototype + "\n" + for s in car_bf_eval_strs: cartesian_bf_func = cartesian_bf_func + " " + s + "\n" + cartesian_bf_func = cartesian_bf_func + "\n}\n" + + cartesian_bf_deriv1_func = cartesian_bf_deriv1_prototype + "\n" + for s in car_bf_x_eval_strs: cartesian_bf_deriv1_func = cartesian_bf_deriv1_func + " " + s + "\n" + cartesian_bf_deriv1_func = cartesian_bf_deriv1_func + "\n" + for s in car_bf_y_eval_strs: cartesian_bf_deriv1_func = cartesian_bf_deriv1_func + " " + s + "\n" + cartesian_bf_deriv1_func = cartesian_bf_deriv1_func + "\n" + for s in car_bf_z_eval_strs: cartesian_bf_deriv1_func = cartesian_bf_deriv1_func + " " + s + "\n" + cartesian_bf_deriv1_func = cartesian_bf_deriv1_func + "\n}\n" + + + sqrt_regex = "sqrt\([0-9]+\)" + + sqrt_finds = re.findall( sqrt_regex, spherical_bf_func ) + sqrt_finds = sqrt_finds + ( re.findall( sqrt_regex, spherical_bf_deriv1_func ) ) + sqrt_finds = sqrt_finds + ( re.findall( sqrt_regex, cartesian_bf_func ) ) + sqrt_finds = sqrt_finds + ( re.findall( sqrt_regex, cartesian_bf_deriv1_func ) ) + + sqrt_finds = list(set(sqrt_finds)) + + for x in sqrt_finds: + arg = x.strip('sqrt(').strip(')') + new_str = 'sqrt_' + arg + spherical_bf_func = spherical_bf_func.replace( x, new_str ) + spherical_bf_deriv1_func = spherical_bf_deriv1_func.replace( x, new_str ) + cartesian_bf_func = cartesian_bf_func.replace( x, new_str ) + cartesian_bf_deriv1_func = cartesian_bf_deriv1_func.replace( x, new_str ) + + + new_str = "constexpr double " + new_str + " = " + str( math.sqrt(int(arg)) ) + ";" + constant_lines.append( new_str ) + + + cart_header_file.write( cartesian_bf_func ) + cart_header_file.write( cartesian_bf_deriv1_func ) + sphr_header_file.write( spherical_bf_func ) + sphr_header_file.write( spherical_bf_deriv1_func ) + + + + +# Generate calling routines +cartesian_bf_calling_func = cartesian_bf_template.format('') +spherical_bf_calling_func = spherical_bf_template.format('') +cartesian_bf_deriv1_calling_func = cartesian_bf_deriv1_template.format('') +spherical_bf_deriv1_calling_func = spherical_bf_deriv1_template.format('') + +am_dispatch_template = "switch( shell.l ) {{\n" +am_dispatch_template_deriv1 = "switch( shell.l ) {{\n" +for L in range( L_max + 1 ): + bf_template = """ + case {0}: + gaueval_{{0}}_angular_{0}(tmp, xc, yc, zc, bf_eval); + break; +""".format(L) + + deriv1_template = """ + case {0}: + gaueval_{{0}}_angular_{0}(tmp, xc, yc, zc, bf_eval); + gaueval_{{0}}_angular_{0}_deriv1(tmp, tmp_x, tmp_y, tmp_z, xc, yc, zc, bf_eval, bf_x_eval, bf_y_eval, bf_z_eval); + break; +""".format(L) + + + am_dispatch_template = am_dispatch_template + bf_template + am_dispatch_template_deriv1 = am_dispatch_template_deriv1 + deriv1_template + + + +am_dispatch_template = am_dispatch_template + "}}\n" +am_dispatch_template_deriv1 = am_dispatch_template_deriv1 + "}}\n" + +print(am_dispatch_template_deriv1.format('cartesian')) +print(am_dispatch_template_deriv1.format('spherical')) + + + + +footer = "} // namespace gpgaueval" +cart_header_file.write( footer ) +sphr_header_file.write( footer ) + +constant_lines = list(set(constant_lines)) +preamble = """ +#pragma once + +namespace gpgaueval { +""" + +cons_header_file.write( preamble ) +for s in constant_lines: cons_header_file.write( " " + s + "\n" ) +cons_header_file.write(footer) diff --git a/src/cuda_integrator/collocation_generator/generate_gaueval_angular_eval.py b/src/cuda_integrator/collocation_generator/generate_gaueval_angular_eval.py new file mode 100644 index 000000000..9d07c5489 --- /dev/null +++ b/src/cuda_integrator/collocation_generator/generate_gaueval_angular_eval.py @@ -0,0 +1,147 @@ +import os,sys,re,math +import pyexpander.lib as expander +from gaueval_angular import generate_spherical_angular, generate_cartesian_angular, generate_cartesian_ls, generate_eval_lines + +from io import StringIO + + +L_max = 6 + +sphr_bf_body = [] +sphr_bf_d1_body = [] + +sphr_unnorm_bf_body = [] +sphr_unnorm_bf_d1_body = [] + +cart_bf_body = [] +cart_bf_d1_body = [] + + +for L in range( L_max + 1 ): + + print( "Processing L = {} ...".format(L) ) + sphr_ang = generate_spherical_angular( L, False ) + sphr_unnorm_ang = generate_spherical_angular( L, True ) + cart_ang = generate_cartesian_angular( generate_cartesian_ls( L ) ) + + sa, sa_x, sa_y, sa_z = generate_eval_lines( L, sphr_ang ) + sna, sna_x, sna_y, sna_z = generate_eval_lines( L, sphr_unnorm_ang ) + ca, ca_x, ca_y, ca_z = generate_eval_lines( L, cart_ang ) + + sphr_bf_body.append( "\n ".join(sa) ) + sphr_unnorm_bf_body.append( "\n ".join(sna) ) + cart_bf_body.append( "\n ".join(ca) ) + + s_d1 = "\n\n ".join(["\n ".join( sa_x ), "\n ".join(sa_y), "\n ".join(sa_z)]) + sn_d1 = "\n\n ".join(["\n ".join( sna_x ), "\n ".join(sna_y), "\n ".join(sna_z)]) + c_d1 = "\n\n ".join(["\n ".join( ca_x ), "\n ".join(ca_y), "\n ".join(ca_z)]) + + sphr_bf_d1_body.append( s_d1 ) + sphr_unnorm_bf_d1_body.append( sn_d1 ) + cart_bf_d1_body.append( c_d1 ) + + + +template_fname = 'gaueval_angular_template.hpp' + +sphr_var_dict = { 'L_max' : L_max, 'body' : sphr_bf_body, 'body_d1' : sphr_bf_d1_body, 'name' : 'spherical' } +sphr_unnorm_var_dict = { 'L_max' : L_max, 'body' : sphr_unnorm_bf_body, 'body_d1' : sphr_unnorm_bf_d1_body, 'name' : 'spherical_unnorm' } +cart_var_dict = { 'L_max' : L_max, 'body' : cart_bf_body, 'body_d1' : cart_bf_d1_body, 'name' : 'cartesian' } + + +old_sys_out = sys.stdout + +sys.stdout = cart_expand = StringIO() +expander.expandFile( template_fname, external_definitions=cart_var_dict, auto_indent=True ) +sys.stdout = sphr_expand = StringIO() +expander.expandFile( template_fname, external_definitions=sphr_var_dict, auto_indent=True ) +sys.stdout = sphr_unnorm_expand = StringIO() +expander.expandFile( template_fname, external_definitions=sphr_unnorm_var_dict, auto_indent=True ) + +sys.stdout = old_sys_out + +cart_expand = cart_expand.getvalue() +sphr_expand = sphr_expand.getvalue() +sphr_unnorm_expand = sphr_unnorm_expand.getvalue() + + + +# Handle Constants +constant_lines = [] + +# Sqrts +sqrt_regex = "sqrt\([0-9]+\)" +sqrt_finds = re.findall( sqrt_regex, "\n".join([cart_expand,sphr_expand,sphr_unnorm_expand]) ) + +sqrt_finds = list(set(sqrt_finds)) + +for x in sqrt_finds: + arg = x.strip('sqrt(').strip(')') + new_str = 'sqrt_' + arg + + cart_expand = cart_expand.replace( x, new_str ) + sphr_expand = sphr_expand.replace( x, new_str ) + sphr_unnorm_expand = sphr_unnorm_expand.replace( x, new_str ) + + new_str = "constexpr double " + new_str + " = " + str( math.sqrt(int(arg)) ) + ";" + constant_lines.append( new_str ) + +old_sys_out = sys.stdout + +sys.stdout = constant_expand = StringIO() +expander.expandFile( 'gaueval_device_constants_template.hpp', + external_definitions={ "const_lines" : constant_lines } ) + +sys.stdout = old_sys_out + +constant_expand = constant_expand.getvalue() + + +cart_header_fname = "gaueval_angular_cartesian.hpp" +sphr_header_fname = "gaueval_angular_spherical.hpp" +sphr_unnorm_header_fname = "gaueval_angular_spherical_unnorm.hpp" +cons_header_fname = "gaueval_device_constants.hpp" + +cart_header_file = open( cart_header_fname, 'w' ) +sphr_header_file = open( sphr_header_fname, 'w' ) +sphr_unnorm_header_file = open( sphr_unnorm_header_fname, 'w' ) +cons_header_file = open( cons_header_fname, 'w' ) + +cart_header_file.write( cart_expand ) +sphr_header_file.write( sphr_expand ) +sphr_unnorm_header_file.write( sphr_unnorm_expand ) +cons_header_file.write( constant_expand ) + + + + + + + + + +# Generate Kernel Driver + +old_sys_out = sys.stdout + +sys.stdout = gaueval_cartesian_kernel_expand = StringIO() +expander.expandFile( 'gaueval_kernels_template.cu', external_definitions={ 'ang_name' : 'cartesian' } ) + +sys.stdout = gaueval_spherical_kernel_expand = StringIO() +expander.expandFile( 'gaueval_kernels_template.cu', external_definitions={ 'ang_name' : 'spherical' } ) + +sys.stdout = gaueval_spherical_unnorm_kernel_expand = StringIO() +expander.expandFile( 'gaueval_kernels_template.cu', external_definitions={ 'ang_name' : 'spherical_unnorm' } ) + +sys.stdout = old_sys_out + +gaueval_cartesian_kernel_expand = gaueval_cartesian_kernel_expand.getvalue() +gaueval_spherical_kernel_expand = gaueval_spherical_kernel_expand.getvalue() +gaueval_spherical_unnorm_kernel_expand = gaueval_spherical_unnorm_kernel_expand.getvalue() + +with open( 'gaueval_kernels_cartesian.cu', 'w' ) as f: + f.write( gaueval_cartesian_kernel_expand ) +with open( 'gaueval_kernels_spherical.cu', 'w' ) as f: + f.write( gaueval_spherical_kernel_expand ) +with open( 'gaueval_kernels_spherical_unnorm.cu', 'w' ) as f: + f.write( gaueval_spherical_unnorm_kernel_expand ) diff --git a/src/cuda_integrator/cuda_collocation.cxx b/src/cuda_integrator/cuda_collocation.cxx new file mode 100644 index 000000000..e69de29bb diff --git a/src/cuda_integrator/cuda_collocation_kernels.cu b/src/cuda_integrator/cuda_collocation_kernels.cu new file mode 100644 index 000000000..e69de29bb diff --git a/src/cuda_integrator/gauxc-cuda_integrator.cmake b/src/cuda_integrator/gauxc-cuda_integrator.cmake new file mode 100644 index 000000000..c48c285b8 --- /dev/null +++ b/src/cuda_integrator/gauxc-cuda_integrator.cmake @@ -0,0 +1,20 @@ +add_library( gauxc_device_integrator OBJECT + cuda_integrator/cuda_collocation_kernels.cu + cuda_integrator/cuda_collocation.cxx +) +target_compile_features( gauxc_device_integrator PUBLIC cuda_std_14 cxx_std_14 ) +target_link_libraries( gauxc_device_integrator PUBLIC ExchCXX::exchcxx ) + +target_include_directories( gauxc_device_integrator + PRIVATE + $ + $ + $ +) + +target_compile_options( gauxc_device_integrator + PRIVATE + $<$: -Wall -Wextra -Wpedantic -Wnon-virtual-dtor> + $<$: -Xcudafe --diag_suppress=partial_override -Xptxas -v > +) + From 715cf7ec8028f472a5372ec39f411c68b4e77f83 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Tue, 11 Aug 2020 14:47:59 -0700 Subject: [PATCH 02/27] [WIP] Work on generator --- .gitignore | 1 + .../gaueval_kernels_template.cu | 40 +- .../{ => deprecated}/generate_bfeval.py | 0 .../gaueval_angular_cartesian.hpp | 679 ------------------ .../gaueval_angular_spherical.hpp | 539 -------------- .../gaueval_angular_spherical_unnorm.hpp | 539 -------------- .../gaueval_device_constants.hpp | 33 - ...ernels_cartesian.cu => gaueval_kernels.cu} | 71 +- .../collocation_generator/gaueval_kernels.hpp | 30 + .../gaueval_kernels_spherical.cu | 129 ---- .../gaueval_kernels_spherical_unnorm.cu | 129 ---- .../collocation_generator/gaueval_radial.hpp | 80 +++ .../{ => scripts}/__init__.py | 0 .../{ => scripts}/gaueval_angular.py | 0 .../generate_gaueval_angular_eval.py | 83 +-- .../gaueval_angular_template.hpp | 15 - .../gaueval_device_constants_template.hpp | 0 17 files changed, 214 insertions(+), 2154 deletions(-) create mode 100644 .gitignore rename src/cuda_integrator/collocation_generator/{ => deprecated}/gaueval_kernels_template.cu (80%) rename src/cuda_integrator/collocation_generator/{ => deprecated}/generate_bfeval.py (100%) delete mode 100644 src/cuda_integrator/collocation_generator/gaueval_angular_cartesian.hpp delete mode 100644 src/cuda_integrator/collocation_generator/gaueval_angular_spherical.hpp delete mode 100644 src/cuda_integrator/collocation_generator/gaueval_angular_spherical_unnorm.hpp delete mode 100644 src/cuda_integrator/collocation_generator/gaueval_device_constants.hpp rename src/cuda_integrator/collocation_generator/{gaueval_kernels_cartesian.cu => gaueval_kernels.cu} (59%) create mode 100644 src/cuda_integrator/collocation_generator/gaueval_kernels.hpp delete mode 100644 src/cuda_integrator/collocation_generator/gaueval_kernels_spherical.cu delete mode 100644 src/cuda_integrator/collocation_generator/gaueval_kernels_spherical_unnorm.cu create mode 100644 src/cuda_integrator/collocation_generator/gaueval_radial.hpp rename src/cuda_integrator/collocation_generator/{ => scripts}/__init__.py (100%) rename src/cuda_integrator/collocation_generator/{ => scripts}/gaueval_angular.py (100%) rename src/cuda_integrator/collocation_generator/{ => scripts}/generate_gaueval_angular_eval.py (56%) rename src/cuda_integrator/collocation_generator/{ => templates}/gaueval_angular_template.hpp (90%) rename src/cuda_integrator/collocation_generator/{ => templates}/gaueval_device_constants_template.hpp (100%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..5244e1e97 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*pycache** diff --git a/src/cuda_integrator/collocation_generator/gaueval_kernels_template.cu b/src/cuda_integrator/collocation_generator/deprecated/gaueval_kernels_template.cu similarity index 80% rename from src/cuda_integrator/collocation_generator/gaueval_kernels_template.cu rename to src/cuda_integrator/collocation_generator/deprecated/gaueval_kernels_template.cu index c3edd0a2d..f726b9b23 100644 --- a/src/cuda_integrator/collocation_generator/gaueval_kernels_template.cu +++ b/src/cuda_integrator/collocation_generator/deprecated/gaueval_kernels_template.cu @@ -1,4 +1,4 @@ -#include +//#include #include #include @@ -11,22 +11,22 @@ namespace gpgaueval { __global__ void gaueval_device_$(ang_name)_kernel( - const size_t nshells, - const size_t nbf, - const size_t npts, + size_t nshells, + size_t nbf, + size_t npts, const StaticShell* shells_device, const size_t* offs_device, - const double* pts_device, - double* eval_device + const double* pts_device, + double* eval_device ) { const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; - if( tid_x< nshells and tid_y < npts ) { + if( tid_x < npts and tid_y < nshells ) { - const size_t ish = tid_x; - const size_t ipt = tid_y; + const size_t ipt = tid_x; + const size_t ish = tid_y; const size_t ibf = offs_device[ish]; @@ -60,25 +60,25 @@ void gaueval_device_$(ang_name)_kernel( __global__ void gaueval_device_$(ang_name)_kernel_deriv1( - const size_t nshells, - const size_t nbf, - const size_t npts, + size_t nshells, + size_t nbf, + size_t npts, const StaticShell* shells_device, const size_t* offs_device, - const double* pts_device, - double* eval_device, - double* deval_device_x, - double* deval_device_y, - double* deval_device_z + const double* pts_device, + double* eval_device, + double* deval_device_x, + double* deval_device_y, + double* deval_device_z ) { const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; - if( tid_x< nshells and tid_y < npts ) { + if( tid_x < npts and tid_y < nshells ) { - const size_t ish = tid_x; - const size_t ipt = tid_y; + const size_t ipt = tid_x; + const size_t ish = tid_y; const size_t ibf = offs_device[ish]; diff --git a/src/cuda_integrator/collocation_generator/generate_bfeval.py b/src/cuda_integrator/collocation_generator/deprecated/generate_bfeval.py similarity index 100% rename from src/cuda_integrator/collocation_generator/generate_bfeval.py rename to src/cuda_integrator/collocation_generator/deprecated/generate_bfeval.py diff --git a/src/cuda_integrator/collocation_generator/gaueval_angular_cartesian.hpp b/src/cuda_integrator/collocation_generator/gaueval_angular_cartesian.hpp deleted file mode 100644 index 20bf726ee..000000000 --- a/src/cuda_integrator/collocation_generator/gaueval_angular_cartesian.hpp +++ /dev/null @@ -1,679 +0,0 @@ -#pragma once -#include "gaueval_device_constants.hpp" -#include - -#ifndef GPGAUEVAL_INLINE -# define GPGAUEVAL_INLINE __inline__ -#endif - -namespace gpgaueval { - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_0( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_0_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = bf_x; - - eval_y[0] = bf_y; - - eval_z[0] = bf_z; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_1( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*x; - eval[1] = bf*y; - eval[2] = bf*z; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_1_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = bf + bf_x*x; - eval_x[1] = bf_x*y; - eval_x[2] = bf_x*z; - - eval_y[0] = bf_y*x; - eval_y[1] = bf + bf_y*y; - eval_y[2] = bf_y*z; - - eval_z[0] = bf_z*x; - eval_z[1] = bf_z*y; - eval_z[2] = bf + bf_z*z; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_2( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*x*x; - eval[1] = bf*x*y; - eval[2] = bf*x*z; - eval[3] = bf*y*y; - eval[4] = bf*y*z; - eval[5] = bf*z*z; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_2_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = x*(2*bf + bf_x*x); - eval_x[1] = y*(bf + bf_x*x); - eval_x[2] = z*(bf + bf_x*x); - eval_x[3] = bf_x*y*y; - eval_x[4] = bf_x*y*z; - eval_x[5] = bf_x*z*z; - - eval_y[0] = bf_y*x*x; - eval_y[1] = x*(bf + bf_y*y); - eval_y[2] = bf_y*x*z; - eval_y[3] = y*(2*bf + bf_y*y); - eval_y[4] = z*(bf + bf_y*y); - eval_y[5] = bf_y*z*z; - - eval_z[0] = bf_z*x*x; - eval_z[1] = bf_z*x*y; - eval_z[2] = x*(bf + bf_z*z); - eval_z[3] = bf_z*y*y; - eval_z[4] = y*(bf + bf_z*z); - eval_z[5] = z*(2*bf + bf_z*z); - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_3( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*x*x*x; - eval[1] = bf*x*x*y; - eval[2] = bf*x*x*z; - eval[3] = bf*x*y*y; - eval[4] = bf*x*y*z; - eval[5] = bf*x*z*z; - eval[6] = bf*y*y*y; - eval[7] = bf*y*y*z; - eval[8] = bf*y*z*z; - eval[9] = bf*z*z*z; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_3_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = x*x*(3*bf + bf_x*x); - eval_x[1] = x*y*(2*bf + bf_x*x); - eval_x[2] = x*z*(2*bf + bf_x*x); - eval_x[3] = y*y*(bf + bf_x*x); - eval_x[4] = y*z*(bf + bf_x*x); - eval_x[5] = z*z*(bf + bf_x*x); - eval_x[6] = bf_x*y*y*y; - eval_x[7] = bf_x*y*y*z; - eval_x[8] = bf_x*y*z*z; - eval_x[9] = bf_x*z*z*z; - - eval_y[0] = bf_y*x*x*x; - eval_y[1] = x*x*(bf + bf_y*y); - eval_y[2] = bf_y*x*x*z; - eval_y[3] = x*y*(2*bf + bf_y*y); - eval_y[4] = x*z*(bf + bf_y*y); - eval_y[5] = bf_y*x*z*z; - eval_y[6] = y*y*(3*bf + bf_y*y); - eval_y[7] = y*z*(2*bf + bf_y*y); - eval_y[8] = z*z*(bf + bf_y*y); - eval_y[9] = bf_y*z*z*z; - - eval_z[0] = bf_z*x*x*x; - eval_z[1] = bf_z*x*x*y; - eval_z[2] = x*x*(bf + bf_z*z); - eval_z[3] = bf_z*x*y*y; - eval_z[4] = x*y*(bf + bf_z*z); - eval_z[5] = x*z*(2*bf + bf_z*z); - eval_z[6] = bf_z*y*y*y; - eval_z[7] = y*y*(bf + bf_z*z); - eval_z[8] = y*z*(2*bf + bf_z*z); - eval_z[9] = z*z*(3*bf + bf_z*z); - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_4( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*x*x*x*x; - eval[1] = bf*x*x*x*y; - eval[2] = bf*x*x*x*z; - eval[3] = bf*x*x*y*y; - eval[4] = bf*x*x*y*z; - eval[5] = bf*x*x*z*z; - eval[6] = bf*x*y*y*y; - eval[7] = bf*x*y*y*z; - eval[8] = bf*x*y*z*z; - eval[9] = bf*x*z*z*z; - eval[10] = bf*y*y*y*y; - eval[11] = bf*y*y*y*z; - eval[12] = bf*y*y*z*z; - eval[13] = bf*y*z*z*z; - eval[14] = bf*z*z*z*z; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_4_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = x*x*x*(4*bf + bf_x*x); - eval_x[1] = x*x*y*(3*bf + bf_x*x); - eval_x[2] = x*x*z*(3*bf + bf_x*x); - eval_x[3] = x*y*y*(2*bf + bf_x*x); - eval_x[4] = x*y*z*(2*bf + bf_x*x); - eval_x[5] = x*z*z*(2*bf + bf_x*x); - eval_x[6] = y*y*y*(bf + bf_x*x); - eval_x[7] = y*y*z*(bf + bf_x*x); - eval_x[8] = y*z*z*(bf + bf_x*x); - eval_x[9] = z*z*z*(bf + bf_x*x); - eval_x[10] = bf_x*y*y*y*y; - eval_x[11] = bf_x*y*y*y*z; - eval_x[12] = bf_x*y*y*z*z; - eval_x[13] = bf_x*y*z*z*z; - eval_x[14] = bf_x*z*z*z*z; - - eval_y[0] = bf_y*x*x*x*x; - eval_y[1] = x*x*x*(bf + bf_y*y); - eval_y[2] = bf_y*x*x*x*z; - eval_y[3] = x*x*y*(2*bf + bf_y*y); - eval_y[4] = x*x*z*(bf + bf_y*y); - eval_y[5] = bf_y*x*x*z*z; - eval_y[6] = x*y*y*(3*bf + bf_y*y); - eval_y[7] = x*y*z*(2*bf + bf_y*y); - eval_y[8] = x*z*z*(bf + bf_y*y); - eval_y[9] = bf_y*x*z*z*z; - eval_y[10] = y*y*y*(4*bf + bf_y*y); - eval_y[11] = y*y*z*(3*bf + bf_y*y); - eval_y[12] = y*z*z*(2*bf + bf_y*y); - eval_y[13] = z*z*z*(bf + bf_y*y); - eval_y[14] = bf_y*z*z*z*z; - - eval_z[0] = bf_z*x*x*x*x; - eval_z[1] = bf_z*x*x*x*y; - eval_z[2] = x*x*x*(bf + bf_z*z); - eval_z[3] = bf_z*x*x*y*y; - eval_z[4] = x*x*y*(bf + bf_z*z); - eval_z[5] = x*x*z*(2*bf + bf_z*z); - eval_z[6] = bf_z*x*y*y*y; - eval_z[7] = x*y*y*(bf + bf_z*z); - eval_z[8] = x*y*z*(2*bf + bf_z*z); - eval_z[9] = x*z*z*(3*bf + bf_z*z); - eval_z[10] = bf_z*y*y*y*y; - eval_z[11] = y*y*y*(bf + bf_z*z); - eval_z[12] = y*y*z*(2*bf + bf_z*z); - eval_z[13] = y*z*z*(3*bf + bf_z*z); - eval_z[14] = z*z*z*(4*bf + bf_z*z); - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_5( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*x*x*x*x*x; - eval[1] = bf*x*x*x*x*y; - eval[2] = bf*x*x*x*x*z; - eval[3] = bf*x*x*x*y*y; - eval[4] = bf*x*x*x*y*z; - eval[5] = bf*x*x*x*z*z; - eval[6] = bf*x*x*y*y*y; - eval[7] = bf*x*x*y*y*z; - eval[8] = bf*x*x*y*z*z; - eval[9] = bf*x*x*z*z*z; - eval[10] = bf*x*y*y*y*y; - eval[11] = bf*x*y*y*y*z; - eval[12] = bf*x*y*y*z*z; - eval[13] = bf*x*y*z*z*z; - eval[14] = bf*x*z*z*z*z; - eval[15] = bf*y*y*y*y*y; - eval[16] = bf*y*y*y*y*z; - eval[17] = bf*y*y*y*z*z; - eval[18] = bf*y*y*z*z*z; - eval[19] = bf*y*z*z*z*z; - eval[20] = bf*z*z*z*z*z; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_5_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = x*x*x*x*(5*bf + bf_x*x); - eval_x[1] = x*x*x*y*(4*bf + bf_x*x); - eval_x[2] = x*x*x*z*(4*bf + bf_x*x); - eval_x[3] = x*x*y*y*(3*bf + bf_x*x); - eval_x[4] = x*x*y*z*(3*bf + bf_x*x); - eval_x[5] = x*x*z*z*(3*bf + bf_x*x); - eval_x[6] = x*y*y*y*(2*bf + bf_x*x); - eval_x[7] = x*y*y*z*(2*bf + bf_x*x); - eval_x[8] = x*y*z*z*(2*bf + bf_x*x); - eval_x[9] = x*z*z*z*(2*bf + bf_x*x); - eval_x[10] = y*y*y*y*(bf + bf_x*x); - eval_x[11] = y*y*y*z*(bf + bf_x*x); - eval_x[12] = y*y*z*z*(bf + bf_x*x); - eval_x[13] = y*z*z*z*(bf + bf_x*x); - eval_x[14] = z*z*z*z*(bf + bf_x*x); - eval_x[15] = bf_x*y*y*y*y*y; - eval_x[16] = bf_x*y*y*y*y*z; - eval_x[17] = bf_x*y*y*y*z*z; - eval_x[18] = bf_x*y*y*z*z*z; - eval_x[19] = bf_x*y*z*z*z*z; - eval_x[20] = bf_x*z*z*z*z*z; - - eval_y[0] = bf_y*x*x*x*x*x; - eval_y[1] = x*x*x*x*(bf + bf_y*y); - eval_y[2] = bf_y*x*x*x*x*z; - eval_y[3] = x*x*x*y*(2*bf + bf_y*y); - eval_y[4] = x*x*x*z*(bf + bf_y*y); - eval_y[5] = bf_y*x*x*x*z*z; - eval_y[6] = x*x*y*y*(3*bf + bf_y*y); - eval_y[7] = x*x*y*z*(2*bf + bf_y*y); - eval_y[8] = x*x*z*z*(bf + bf_y*y); - eval_y[9] = bf_y*x*x*z*z*z; - eval_y[10] = x*y*y*y*(4*bf + bf_y*y); - eval_y[11] = x*y*y*z*(3*bf + bf_y*y); - eval_y[12] = x*y*z*z*(2*bf + bf_y*y); - eval_y[13] = x*z*z*z*(bf + bf_y*y); - eval_y[14] = bf_y*x*z*z*z*z; - eval_y[15] = y*y*y*y*(5*bf + bf_y*y); - eval_y[16] = y*y*y*z*(4*bf + bf_y*y); - eval_y[17] = y*y*z*z*(3*bf + bf_y*y); - eval_y[18] = y*z*z*z*(2*bf + bf_y*y); - eval_y[19] = z*z*z*z*(bf + bf_y*y); - eval_y[20] = bf_y*z*z*z*z*z; - - eval_z[0] = bf_z*x*x*x*x*x; - eval_z[1] = bf_z*x*x*x*x*y; - eval_z[2] = x*x*x*x*(bf + bf_z*z); - eval_z[3] = bf_z*x*x*x*y*y; - eval_z[4] = x*x*x*y*(bf + bf_z*z); - eval_z[5] = x*x*x*z*(2*bf + bf_z*z); - eval_z[6] = bf_z*x*x*y*y*y; - eval_z[7] = x*x*y*y*(bf + bf_z*z); - eval_z[8] = x*x*y*z*(2*bf + bf_z*z); - eval_z[9] = x*x*z*z*(3*bf + bf_z*z); - eval_z[10] = bf_z*x*y*y*y*y; - eval_z[11] = x*y*y*y*(bf + bf_z*z); - eval_z[12] = x*y*y*z*(2*bf + bf_z*z); - eval_z[13] = x*y*z*z*(3*bf + bf_z*z); - eval_z[14] = x*z*z*z*(4*bf + bf_z*z); - eval_z[15] = bf_z*y*y*y*y*y; - eval_z[16] = y*y*y*y*(bf + bf_z*z); - eval_z[17] = y*y*y*z*(2*bf + bf_z*z); - eval_z[18] = y*y*z*z*(3*bf + bf_z*z); - eval_z[19] = y*z*z*z*(4*bf + bf_z*z); - eval_z[20] = z*z*z*z*(5*bf + bf_z*z); - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_6( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*x*x*x*x*x*x; - eval[1] = bf*x*x*x*x*x*y; - eval[2] = bf*x*x*x*x*x*z; - eval[3] = bf*x*x*x*x*y*y; - eval[4] = bf*x*x*x*x*y*z; - eval[5] = bf*x*x*x*x*z*z; - eval[6] = bf*x*x*x*y*y*y; - eval[7] = bf*x*x*x*y*y*z; - eval[8] = bf*x*x*x*y*z*z; - eval[9] = bf*x*x*x*z*z*z; - eval[10] = bf*x*x*y*y*y*y; - eval[11] = bf*x*x*y*y*y*z; - eval[12] = bf*x*x*y*y*z*z; - eval[13] = bf*x*x*y*z*z*z; - eval[14] = bf*x*x*z*z*z*z; - eval[15] = bf*x*y*y*y*y*y; - eval[16] = bf*x*y*y*y*y*z; - eval[17] = bf*x*y*y*y*z*z; - eval[18] = bf*x*y*y*z*z*z; - eval[19] = bf*x*y*z*z*z*z; - eval[20] = bf*x*z*z*z*z*z; - eval[21] = bf*y*y*y*y*y*y; - eval[22] = bf*y*y*y*y*y*z; - eval[23] = bf*y*y*y*y*z*z; - eval[24] = bf*y*y*y*z*z*z; - eval[25] = bf*y*y*z*z*z*z; - eval[26] = bf*y*z*z*z*z*z; - eval[27] = bf*z*z*z*z*z*z; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_6_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = x*x*x*x*x*(6*bf + bf_x*x); - eval_x[1] = x*x*x*x*y*(5*bf + bf_x*x); - eval_x[2] = x*x*x*x*z*(5*bf + bf_x*x); - eval_x[3] = x*x*x*y*y*(4*bf + bf_x*x); - eval_x[4] = x*x*x*y*z*(4*bf + bf_x*x); - eval_x[5] = x*x*x*z*z*(4*bf + bf_x*x); - eval_x[6] = x*x*y*y*y*(3*bf + bf_x*x); - eval_x[7] = x*x*y*y*z*(3*bf + bf_x*x); - eval_x[8] = x*x*y*z*z*(3*bf + bf_x*x); - eval_x[9] = x*x*z*z*z*(3*bf + bf_x*x); - eval_x[10] = x*y*y*y*y*(2*bf + bf_x*x); - eval_x[11] = x*y*y*y*z*(2*bf + bf_x*x); - eval_x[12] = x*y*y*z*z*(2*bf + bf_x*x); - eval_x[13] = x*y*z*z*z*(2*bf + bf_x*x); - eval_x[14] = x*z*z*z*z*(2*bf + bf_x*x); - eval_x[15] = y*y*y*y*y*(bf + bf_x*x); - eval_x[16] = y*y*y*y*z*(bf + bf_x*x); - eval_x[17] = y*y*y*z*z*(bf + bf_x*x); - eval_x[18] = y*y*z*z*z*(bf + bf_x*x); - eval_x[19] = y*z*z*z*z*(bf + bf_x*x); - eval_x[20] = z*z*z*z*z*(bf + bf_x*x); - eval_x[21] = bf_x*y*y*y*y*y*y; - eval_x[22] = bf_x*y*y*y*y*y*z; - eval_x[23] = bf_x*y*y*y*y*z*z; - eval_x[24] = bf_x*y*y*y*z*z*z; - eval_x[25] = bf_x*y*y*z*z*z*z; - eval_x[26] = bf_x*y*z*z*z*z*z; - eval_x[27] = bf_x*z*z*z*z*z*z; - - eval_y[0] = bf_y*x*x*x*x*x*x; - eval_y[1] = x*x*x*x*x*(bf + bf_y*y); - eval_y[2] = bf_y*x*x*x*x*x*z; - eval_y[3] = x*x*x*x*y*(2*bf + bf_y*y); - eval_y[4] = x*x*x*x*z*(bf + bf_y*y); - eval_y[5] = bf_y*x*x*x*x*z*z; - eval_y[6] = x*x*x*y*y*(3*bf + bf_y*y); - eval_y[7] = x*x*x*y*z*(2*bf + bf_y*y); - eval_y[8] = x*x*x*z*z*(bf + bf_y*y); - eval_y[9] = bf_y*x*x*x*z*z*z; - eval_y[10] = x*x*y*y*y*(4*bf + bf_y*y); - eval_y[11] = x*x*y*y*z*(3*bf + bf_y*y); - eval_y[12] = x*x*y*z*z*(2*bf + bf_y*y); - eval_y[13] = x*x*z*z*z*(bf + bf_y*y); - eval_y[14] = bf_y*x*x*z*z*z*z; - eval_y[15] = x*y*y*y*y*(5*bf + bf_y*y); - eval_y[16] = x*y*y*y*z*(4*bf + bf_y*y); - eval_y[17] = x*y*y*z*z*(3*bf + bf_y*y); - eval_y[18] = x*y*z*z*z*(2*bf + bf_y*y); - eval_y[19] = x*z*z*z*z*(bf + bf_y*y); - eval_y[20] = bf_y*x*z*z*z*z*z; - eval_y[21] = y*y*y*y*y*(6*bf + bf_y*y); - eval_y[22] = y*y*y*y*z*(5*bf + bf_y*y); - eval_y[23] = y*y*y*z*z*(4*bf + bf_y*y); - eval_y[24] = y*y*z*z*z*(3*bf + bf_y*y); - eval_y[25] = y*z*z*z*z*(2*bf + bf_y*y); - eval_y[26] = z*z*z*z*z*(bf + bf_y*y); - eval_y[27] = bf_y*z*z*z*z*z*z; - - eval_z[0] = bf_z*x*x*x*x*x*x; - eval_z[1] = bf_z*x*x*x*x*x*y; - eval_z[2] = x*x*x*x*x*(bf + bf_z*z); - eval_z[3] = bf_z*x*x*x*x*y*y; - eval_z[4] = x*x*x*x*y*(bf + bf_z*z); - eval_z[5] = x*x*x*x*z*(2*bf + bf_z*z); - eval_z[6] = bf_z*x*x*x*y*y*y; - eval_z[7] = x*x*x*y*y*(bf + bf_z*z); - eval_z[8] = x*x*x*y*z*(2*bf + bf_z*z); - eval_z[9] = x*x*x*z*z*(3*bf + bf_z*z); - eval_z[10] = bf_z*x*x*y*y*y*y; - eval_z[11] = x*x*y*y*y*(bf + bf_z*z); - eval_z[12] = x*x*y*y*z*(2*bf + bf_z*z); - eval_z[13] = x*x*y*z*z*(3*bf + bf_z*z); - eval_z[14] = x*x*z*z*z*(4*bf + bf_z*z); - eval_z[15] = bf_z*x*y*y*y*y*y; - eval_z[16] = x*y*y*y*y*(bf + bf_z*z); - eval_z[17] = x*y*y*y*z*(2*bf + bf_z*z); - eval_z[18] = x*y*y*z*z*(3*bf + bf_z*z); - eval_z[19] = x*y*z*z*z*(4*bf + bf_z*z); - eval_z[20] = x*z*z*z*z*(5*bf + bf_z*z); - eval_z[21] = bf_z*y*y*y*y*y*y; - eval_z[22] = y*y*y*y*y*(bf + bf_z*z); - eval_z[23] = y*y*y*y*z*(2*bf + bf_z*z); - eval_z[24] = y*y*y*z*z*(3*bf + bf_z*z); - eval_z[25] = y*y*z*z*z*(4*bf + bf_z*z); - eval_z[26] = y*z*z*z*z*(5*bf + bf_z*z); - eval_z[27] = z*z*z*z*z*(6*bf + bf_z*z); - -} - - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular( - const int64_t l, - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - -//switch( l ) { - - //case 0: - if( l == 0 ) { - gaueval_cartesian_angular_0( bf, x, y, z, eval ); - //break; - - //case 1: - } else if( l == 1 ) { - gaueval_cartesian_angular_1( bf, x, y, z, eval ); - //break; - - //case 2: - } else if( l == 2 ) { - gaueval_cartesian_angular_2( bf, x, y, z, eval ); - //break; - - //case 3: - } else if( l == 3 ) { - gaueval_cartesian_angular_3( bf, x, y, z, eval ); - //break; - - //case 4: - } else if( l == 4 ) { - gaueval_cartesian_angular_4( bf, x, y, z, eval ); - //break; - - //case 5: - } else if( l == 5 ) { - gaueval_cartesian_angular_5( bf, x, y, z, eval ); - //break; - - //case 6: - } else if( l == 6 ) { - gaueval_cartesian_angular_6( bf, x, y, z, eval ); - //break; - - //default: - } else { - assert( false && "L < L_MAX" ); - //break; - } - -//} // switch(l) - -} // gaueval_cartesian_angular - - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_deriv1( - const int64_t l, - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval, - double* eval_x, - double* eval_y, - double* eval_z -) { - -//switch( l ) { - -// case 0: - if( l == 0 ) { - gaueval_cartesian_angular_0( bf, x, y, z, eval ); - gaueval_cartesian_angular_0_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 1: - } else if( l == 1 ) { - gaueval_cartesian_angular_1( bf, x, y, z, eval ); - gaueval_cartesian_angular_1_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 2: - } else if( l == 2 ) { - gaueval_cartesian_angular_2( bf, x, y, z, eval ); - gaueval_cartesian_angular_2_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 3: - } else if( l == 3 ) { - gaueval_cartesian_angular_3( bf, x, y, z, eval ); - gaueval_cartesian_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 4: - } else if( l == 4 ) { - gaueval_cartesian_angular_4( bf, x, y, z, eval ); - gaueval_cartesian_angular_4_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 5: - } else if( l == 5 ) { - gaueval_cartesian_angular_5( bf, x, y, z, eval ); - gaueval_cartesian_angular_5_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 6: - } else if( l == 6 ) { - gaueval_cartesian_angular_6( bf, x, y, z, eval ); - gaueval_cartesian_angular_6_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// default: - } else { - assert( false && "L < L_MAX" ); -// break; - } - -//} // switch(l) - -} // gaueval_cartesian_angular_deriv1 - - - -} // namespace gpgaueval diff --git a/src/cuda_integrator/collocation_generator/gaueval_angular_spherical.hpp b/src/cuda_integrator/collocation_generator/gaueval_angular_spherical.hpp deleted file mode 100644 index 595b7c70b..000000000 --- a/src/cuda_integrator/collocation_generator/gaueval_angular_spherical.hpp +++ /dev/null @@ -1,539 +0,0 @@ -#pragma once -#include "gaueval_device_constants.hpp" -#include - -#ifndef GPGAUEVAL_INLINE -# define GPGAUEVAL_INLINE __inline__ -#endif - -namespace gpgaueval { - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_0( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_0_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = bf_x; - - eval_y[0] = bf_y; - - eval_z[0] = bf_z; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_1( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*y; - eval[1] = bf*z; - eval[2] = bf*x; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_1_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = bf_x*y; - eval_x[1] = bf_x*z; - eval_x[2] = bf + bf_x*x; - - eval_y[0] = bf + bf_y*y; - eval_y[1] = bf_y*z; - eval_y[2] = bf_y*x; - - eval_z[0] = bf_z*y; - eval_z[1] = bf + bf_z*z; - eval_z[2] = bf_z*x; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_2( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*x*y; - eval[1] = bf*y*z; - eval[2] = bf*(-x*x - y*y + 2*z*z)/2; - eval[3] = bf*x*z; - eval[4] = sqrt_3*bf*(x*x - y*y)/2; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_2_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = y*(bf + bf_x*x); - eval_x[1] = bf_x*y*z; - eval_x[2] = -bf*x - bf_x*(x*x + y*y - 2*z*z)/2; - eval_x[3] = z*(bf + bf_x*x); - eval_x[4] = sqrt_3*(bf*x + bf_x*(x*x - y*y)/2); - - eval_y[0] = x*(bf + bf_y*y); - eval_y[1] = z*(bf + bf_y*y); - eval_y[2] = -bf*y - bf_y*(x*x + y*y - 2*z*z)/2; - eval_y[3] = bf_y*x*z; - eval_y[4] = sqrt_3*(-bf*y + bf_y*(x*x - y*y)/2); - - eval_z[0] = bf_z*x*y; - eval_z[1] = y*(bf + bf_z*z); - eval_z[2] = 2*bf*z - bf_z*(x*x + y*y - 2*z*z)/2; - eval_z[3] = x*(bf + bf_z*z); - eval_z[4] = sqrt_3*bf_z*(x*x - y*y)/2; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_3( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*y*(3*sqrt_2*x*x - sqrt_10*y*y)/4; - eval[1] = bf*x*y*z; - eval[2] = bf*y*(-sqrt_30*x*x - 5*sqrt_6*y*y + 4*sqrt_30*z*z)/20; - eval[3] = bf*z*(-3*sqrt_5*x*x - 3*sqrt_5*y*y + 10*z*z)/10; - eval[4] = bf*x*(-5*sqrt_6*x*x - sqrt_30*y*y + 4*sqrt_30*z*z)/20; - eval[5] = sqrt_3*bf*z*(x*x - y*y)/2; - eval[6] = bf*x*(sqrt_10*x*x - 3*sqrt_2*y*y)/4; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_3_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = y*(6*sqrt_2*bf*x + bf_x*(3*sqrt_2*x*x - sqrt_10*y*y))/4; - eval_x[1] = y*z*(bf + bf_x*x); - eval_x[2] = -y*(2*sqrt_30*bf*x + bf_x*(sqrt_30*x*x + 5*sqrt_6*y*y - 4*sqrt_30*z*z))/20; - eval_x[3] = -z*(6*sqrt_5*bf*x + bf_x*(3*sqrt_5*x*x + 3*sqrt_5*y*y - 10*z*z))/10; - eval_x[4] = -bf*(15*sqrt_6*x*x + sqrt_30*y*y - 4*sqrt_30*z*z)/20 - bf_x*x*(5*sqrt_6*x*x + sqrt_30*y*y - 4*sqrt_30*z*z)/20; - eval_x[5] = sqrt_3*z*(2*bf*x + bf_x*(x*x - y*y))/2; - eval_x[6] = 3*bf*(sqrt_10*x*x - sqrt_2*y*y)/4 + bf_x*x*(sqrt_10*x*x - 3*sqrt_2*y*y)/4; - - eval_y[0] = 3*bf*(sqrt_2*x*x - sqrt_10*y*y)/4 + bf_y*y*(3*sqrt_2*x*x - sqrt_10*y*y)/4; - eval_y[1] = x*z*(bf + bf_y*y); - eval_y[2] = -bf*(sqrt_30*x*x + 15*sqrt_6*y*y - 4*sqrt_30*z*z)/20 - bf_y*y*(sqrt_30*x*x + 5*sqrt_6*y*y - 4*sqrt_30*z*z)/20; - eval_y[3] = -z*(6*sqrt_5*bf*y + bf_y*(3*sqrt_5*x*x + 3*sqrt_5*y*y - 10*z*z))/10; - eval_y[4] = -x*(2*sqrt_30*bf*y + bf_y*(5*sqrt_6*x*x + sqrt_30*y*y - 4*sqrt_30*z*z))/20; - eval_y[5] = sqrt_3*z*(-2*bf*y + bf_y*(x*x - y*y))/2; - eval_y[6] = x*(-6*sqrt_2*bf*y + bf_y*(sqrt_10*x*x - 3*sqrt_2*y*y))/4; - - eval_z[0] = bf_z*y*(3*sqrt_2*x*x - sqrt_10*y*y)/4; - eval_z[1] = x*y*(bf + bf_z*z); - eval_z[2] = y*(8*sqrt_30*bf*z - bf_z*(sqrt_30*x*x + 5*sqrt_6*y*y - 4*sqrt_30*z*z))/20; - eval_z[3] = -3*bf*(sqrt_5*x*x + sqrt_5*y*y - 10*z*z)/10 - bf_z*z*(3*sqrt_5*x*x + 3*sqrt_5*y*y - 10*z*z)/10; - eval_z[4] = x*(8*sqrt_30*bf*z - bf_z*(5*sqrt_6*x*x + sqrt_30*y*y - 4*sqrt_30*z*z))/20; - eval_z[5] = sqrt_3*(bf + bf_z*z)*(x*x - y*y)/2; - eval_z[6] = bf_z*x*(sqrt_10*x*x - 3*sqrt_2*y*y)/4; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_4( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = sqrt_5*bf*x*y*(x*x - y*y)/2; - eval[1] = bf*y*z*(3*sqrt_2*x*x - sqrt_10*y*y)/4; - eval[2] = bf*x*y*(-sqrt_35*x*x - sqrt_35*y*y + 6*sqrt_7*z*z)/14; - eval[3] = bf*y*z*(-3*sqrt_14*x*x - 3*sqrt_70*y*y + 4*sqrt_70*z*z)/28; - eval[4] = bf*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 24*sqrt_105*x*x*z*z + 105*y*y*y*y - 24*sqrt_105*y*y*z*z + 280*z*z*z*z)/280; - eval[5] = bf*x*z*(-3*sqrt_70*x*x - 3*sqrt_14*y*y + 4*sqrt_70*z*z)/28; - eval[6] = bf*(-7*sqrt_5*x*x*x*x + 6*sqrt_21*x*x*z*z + 7*sqrt_5*y*y*y*y - 6*sqrt_21*y*y*z*z)/28; - eval[7] = bf*x*z*(sqrt_10*x*x - 3*sqrt_2*y*y)/4; - eval[8] = bf*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y)/8; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_4_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = sqrt_5*y*(bf*(3*x*x - y*y) + bf_x*x*(x*x - y*y))/2; - eval_x[1] = y*z*(6*sqrt_2*bf*x + bf_x*(3*sqrt_2*x*x - sqrt_10*y*y))/4; - eval_x[2] = -y*(bf*(3*sqrt_35*x*x + sqrt_35*y*y - 6*sqrt_7*z*z) + bf_x*x*(sqrt_35*x*x + sqrt_35*y*y - 6*sqrt_7*z*z))/14; - eval_x[3] = -y*z*(6*sqrt_14*bf*x + bf_x*(3*sqrt_14*x*x + 3*sqrt_70*y*y - 4*sqrt_70*z*z))/28; - eval_x[4] = 3*bf*x*(35*x*x + sqrt_105*y*y - 4*sqrt_105*z*z)/70 + bf_x*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 24*sqrt_105*x*x*z*z + 105*y*y*y*y - 24*sqrt_105*y*y*z*z + 280*z*z*z*z)/280; - eval_x[5] = -z*(bf*(9*sqrt_70*x*x + 3*sqrt_14*y*y - 4*sqrt_70*z*z) + bf_x*x*(3*sqrt_70*x*x + 3*sqrt_14*y*y - 4*sqrt_70*z*z))/28; - eval_x[6] = -bf*x*(7*sqrt_5*x*x - 3*sqrt_21*z*z)/7 - bf_x*(7*sqrt_5*x*x*x*x - 6*sqrt_21*x*x*z*z - 7*sqrt_5*y*y*y*y + 6*sqrt_21*y*y*z*z)/28; - eval_x[7] = z*(3*bf*(sqrt_10*x*x - sqrt_2*y*y) + bf_x*x*(sqrt_10*x*x - 3*sqrt_2*y*y))/4; - eval_x[8] = bf*x*(sqrt_35*x*x - 3*sqrt_3*y*y)/2 + bf_x*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y)/8; - - eval_y[0] = sqrt_5*x*(-bf*(-x*x + 3*y*y) + bf_y*y*(x*x - y*y))/2; - eval_y[1] = z*(-3*bf*(-sqrt_2*x*x + sqrt_10*y*y) + bf_y*y*(3*sqrt_2*x*x - sqrt_10*y*y))/4; - eval_y[2] = -x*(bf*(sqrt_35*x*x + 3*sqrt_35*y*y - 6*sqrt_7*z*z) + bf_y*y*(sqrt_35*x*x + sqrt_35*y*y - 6*sqrt_7*z*z))/14; - eval_y[3] = -z*(bf*(3*sqrt_14*x*x + 9*sqrt_70*y*y - 4*sqrt_70*z*z) + bf_y*y*(3*sqrt_14*x*x + 3*sqrt_70*y*y - 4*sqrt_70*z*z))/28; - eval_y[4] = 3*bf*y*(sqrt_105*x*x + 35*y*y - 4*sqrt_105*z*z)/70 + bf_y*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 24*sqrt_105*x*x*z*z + 105*y*y*y*y - 24*sqrt_105*y*y*z*z + 280*z*z*z*z)/280; - eval_y[5] = -x*z*(6*sqrt_14*bf*y + bf_y*(3*sqrt_70*x*x + 3*sqrt_14*y*y - 4*sqrt_70*z*z))/28; - eval_y[6] = bf*y*(7*sqrt_5*y*y - 3*sqrt_21*z*z)/7 - bf_y*(7*sqrt_5*x*x*x*x - 6*sqrt_21*x*x*z*z - 7*sqrt_5*y*y*y*y + 6*sqrt_21*y*y*z*z)/28; - eval_y[7] = x*z*(-6*sqrt_2*bf*y + bf_y*(sqrt_10*x*x - 3*sqrt_2*y*y))/4; - eval_y[8] = -bf*y*(3*sqrt_3*x*x - sqrt_35*y*y)/2 + bf_y*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y)/8; - - eval_z[0] = sqrt_5*bf_z*x*y*(x*x - y*y)/2; - eval_z[1] = y*(bf + bf_z*z)*(3*sqrt_2*x*x - sqrt_10*y*y)/4; - eval_z[2] = x*y*(12*sqrt_7*bf*z - bf_z*(sqrt_35*x*x + sqrt_35*y*y - 6*sqrt_7*z*z))/14; - eval_z[3] = y*(3*bf*(-sqrt_14*x*x - sqrt_70*y*y + 4*sqrt_70*z*z) - bf_z*z*(3*sqrt_14*x*x + 3*sqrt_70*y*y - 4*sqrt_70*z*z))/28; - eval_z[4] = -2*bf*z*(3*sqrt_105*x*x + 3*sqrt_105*y*y - 70*z*z)/35 + bf_z*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 24*sqrt_105*x*x*z*z + 105*y*y*y*y - 24*sqrt_105*y*y*z*z + 280*z*z*z*z)/280; - eval_z[5] = x*(3*bf*(-sqrt_70*x*x - sqrt_14*y*y + 4*sqrt_70*z*z) - bf_z*z*(3*sqrt_70*x*x + 3*sqrt_14*y*y - 4*sqrt_70*z*z))/28; - eval_z[6] = 3*sqrt_21*bf*z*(x*x - y*y)/7 - bf_z*(7*sqrt_5*x*x*x*x - 6*sqrt_21*x*x*z*z - 7*sqrt_5*y*y*y*y + 6*sqrt_21*y*y*z*z)/28; - eval_z[7] = x*(bf + bf_z*z)*(sqrt_10*x*x - 3*sqrt_2*y*y)/4; - eval_z[8] = bf_z*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y)/8; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_5( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*y*(5*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 3*sqrt_14*y*y*y*y)/16; - eval[1] = sqrt_5*bf*x*y*z*(x*x - y*y)/2; - eval[2] = bf*y*(-3*sqrt_70*x*x*x*x - 2*sqrt_30*x*x*y*y + 24*sqrt_6*x*x*z*z + 3*sqrt_70*y*y*y*y - 8*sqrt_30*y*y*z*z)/48; - eval[3] = sqrt_15*bf*x*y*z*(-x*x - y*y + 2*z*z)/6; - eval[4] = bf*y*(7*sqrt_15*x*x*x*x + 6*sqrt_35*x*x*y*y - 36*sqrt_7*x*x*z*z + 21*sqrt_15*y*y*y*y - 36*sqrt_35*y*y*z*z + 56*sqrt_15*z*z*z*z)/168; - eval[5] = bf*z*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 40*sqrt_21*x*x*z*z + 105*y*y*y*y - 40*sqrt_21*y*y*z*z + 168*z*z*z*z)/168; - eval[6] = bf*x*(21*sqrt_15*x*x*x*x + 6*sqrt_35*x*x*y*y - 36*sqrt_35*x*x*z*z + 7*sqrt_15*y*y*y*y - 36*sqrt_7*y*y*z*z + 56*sqrt_15*z*z*z*z)/168; - eval[7] = bf*z*(-sqrt_105*x*x*x*x + 6*sqrt_5*x*x*z*z + sqrt_105*y*y*y*y - 6*sqrt_5*y*y*z*z)/12; - eval[8] = bf*x*(-3*sqrt_70*x*x*x*x + 2*sqrt_30*x*x*y*y + 8*sqrt_30*x*x*z*z + 3*sqrt_70*y*y*y*y - 24*sqrt_6*y*y*z*z)/48; - eval[9] = bf*z*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y)/8; - eval[10] = bf*x*(3*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 5*sqrt_14*y*y*y*y)/16; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_5_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = y*(20*bf*x*(sqrt_14*x*x - sqrt_6*y*y) + bf_x*(5*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 3*sqrt_14*y*y*y*y))/16; - eval_x[1] = sqrt_5*y*z*(bf*(3*x*x - y*y) + bf_x*x*(x*x - y*y))/2; - eval_x[2] = -y*(4*bf*x*(3*sqrt_70*x*x + sqrt_30*y*y - 12*sqrt_6*z*z) + bf_x*(3*sqrt_70*x*x*x*x + 2*sqrt_30*x*x*y*y - 24*sqrt_6*x*x*z*z - 3*sqrt_70*y*y*y*y + 8*sqrt_30*y*y*z*z))/48; - eval_x[3] = -sqrt_15*y*z*(bf*(3*x*x + y*y - 2*z*z) + bf_x*x*(x*x + y*y - 2*z*z))/6; - eval_x[4] = y*(4*bf*x*(7*sqrt_15*x*x + 3*sqrt_35*y*y - 18*sqrt_7*z*z) + bf_x*(7*sqrt_15*x*x*x*x + 6*sqrt_35*x*x*y*y - 36*sqrt_7*x*x*z*z + 21*sqrt_15*y*y*y*y - 36*sqrt_35*y*y*z*z + 56*sqrt_15*z*z*z*z))/168; - eval_x[5] = z*(4*bf*x*(105*x*x + 3*sqrt_105*y*y - 20*sqrt_21*z*z) + bf_x*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 40*sqrt_21*x*x*z*z + 105*y*y*y*y - 40*sqrt_21*y*y*z*z + 168*z*z*z*z))/168; - eval_x[6] = 5*sqrt_15*bf*x*x*x*x/8 + 3*sqrt_35*bf*x*x*y*y/28 - 9*sqrt_35*bf*x*x*z*z/14 + sqrt_15*bf*y*y*y*y/24 - 3*sqrt_7*bf*y*y*z*z/14 + sqrt_15*bf*z*z*z*z/3 + sqrt_15*bf_x*x*x*x*x*x/8 + sqrt_35*bf_x*x*x*x*y*y/28 - 3*sqrt_35*bf_x*x*x*x*z*z/14 + sqrt_15*bf_x*x*y*y*y*y/24 - 3*sqrt_7*bf_x*x*y*y*z*z/14 + sqrt_15*bf_x*x*z*z*z*z/3; - eval_x[7] = -z*(4*bf*x*(sqrt_105*x*x - 3*sqrt_5*z*z) + bf_x*(sqrt_105*x*x*x*x - 6*sqrt_5*x*x*z*z - sqrt_105*y*y*y*y + 6*sqrt_5*y*y*z*z))/12; - eval_x[8] = -5*sqrt_70*bf*x*x*x*x/16 + sqrt_30*bf*x*x*y*y/8 + sqrt_30*bf*x*x*z*z/2 + sqrt_70*bf*y*y*y*y/16 - sqrt_6*bf*y*y*z*z/2 - sqrt_70*bf_x*x*x*x*x*x/16 + sqrt_30*bf_x*x*x*x*y*y/24 + sqrt_30*bf_x*x*x*x*z*z/6 + sqrt_70*bf_x*x*y*y*y*y/16 - sqrt_6*bf_x*x*y*y*z*z/2; - eval_x[9] = z*(4*bf*x*(sqrt_35*x*x - 3*sqrt_3*y*y) + bf_x*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y))/8; - eval_x[10] = 15*sqrt_14*bf*x*x*x*x/16 - 15*sqrt_6*bf*x*x*y*y/8 + 5*sqrt_14*bf*y*y*y*y/16 + 3*sqrt_14*bf_x*x*x*x*x*x/16 - 5*sqrt_6*bf_x*x*x*x*y*y/8 + 5*sqrt_14*bf_x*x*y*y*y*y/16; - - eval_y[0] = 5*sqrt_14*bf*x*x*x*x/16 - 15*sqrt_6*bf*x*x*y*y/8 + 15*sqrt_14*bf*y*y*y*y/16 + 5*sqrt_14*bf_y*x*x*x*x*y/16 - 5*sqrt_6*bf_y*x*x*y*y*y/8 + 3*sqrt_14*bf_y*y*y*y*y*y/16; - eval_y[1] = sqrt_5*x*z*(-bf*(-x*x + 3*y*y) + bf_y*y*(x*x - y*y))/2; - eval_y[2] = -sqrt_70*bf*x*x*x*x/16 - sqrt_30*bf*x*x*y*y/8 + sqrt_6*bf*x*x*z*z/2 + 5*sqrt_70*bf*y*y*y*y/16 - sqrt_30*bf*y*y*z*z/2 - sqrt_70*bf_y*x*x*x*x*y/16 - sqrt_30*bf_y*x*x*y*y*y/24 + sqrt_6*bf_y*x*x*y*z*z/2 + sqrt_70*bf_y*y*y*y*y*y/16 - sqrt_30*bf_y*y*y*y*z*z/6; - eval_y[3] = -sqrt_15*x*z*(bf*(x*x + 3*y*y - 2*z*z) + bf_y*y*(x*x + y*y - 2*z*z))/6; - eval_y[4] = sqrt_15*bf*x*x*x*x/24 + 3*sqrt_35*bf*x*x*y*y/28 - 3*sqrt_7*bf*x*x*z*z/14 + 5*sqrt_15*bf*y*y*y*y/8 - 9*sqrt_35*bf*y*y*z*z/14 + sqrt_15*bf*z*z*z*z/3 + sqrt_15*bf_y*x*x*x*x*y/24 + sqrt_35*bf_y*x*x*y*y*y/28 - 3*sqrt_7*bf_y*x*x*y*z*z/14 + sqrt_15*bf_y*y*y*y*y*y/8 - 3*sqrt_35*bf_y*y*y*y*z*z/14 + sqrt_15*bf_y*y*z*z*z*z/3; - eval_y[5] = z*(4*bf*y*(3*sqrt_105*x*x + 105*y*y - 20*sqrt_21*z*z) + bf_y*(105*x*x*x*x + 6*sqrt_105*x*x*y*y - 40*sqrt_21*x*x*z*z + 105*y*y*y*y - 40*sqrt_21*y*y*z*z + 168*z*z*z*z))/168; - eval_y[6] = x*(4*bf*y*(3*sqrt_35*x*x + 7*sqrt_15*y*y - 18*sqrt_7*z*z) + bf_y*(21*sqrt_15*x*x*x*x + 6*sqrt_35*x*x*y*y - 36*sqrt_35*x*x*z*z + 7*sqrt_15*y*y*y*y - 36*sqrt_7*y*y*z*z + 56*sqrt_15*z*z*z*z))/168; - eval_y[7] = z*(4*bf*y*(sqrt_105*y*y - 3*sqrt_5*z*z) - bf_y*(sqrt_105*x*x*x*x - 6*sqrt_5*x*x*z*z - sqrt_105*y*y*y*y + 6*sqrt_5*y*y*z*z))/12; - eval_y[8] = x*(4*bf*y*(sqrt_30*x*x + 3*sqrt_70*y*y - 12*sqrt_6*z*z) + bf_y*(-3*sqrt_70*x*x*x*x + 2*sqrt_30*x*x*y*y + 8*sqrt_30*x*x*z*z + 3*sqrt_70*y*y*y*y - 24*sqrt_6*y*y*z*z))/48; - eval_y[9] = z*(-4*bf*y*(3*sqrt_3*x*x - sqrt_35*y*y) + bf_y*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y))/8; - eval_y[10] = x*(-20*bf*y*(sqrt_6*x*x - sqrt_14*y*y) + bf_y*(3*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 5*sqrt_14*y*y*y*y))/16; - - eval_z[0] = bf_z*y*(5*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 3*sqrt_14*y*y*y*y)/16; - eval_z[1] = sqrt_5*x*y*(bf + bf_z*z)*(x*x - y*y)/2; - eval_z[2] = y*(16*bf*z*(3*sqrt_6*x*x - sqrt_30*y*y) - bf_z*(3*sqrt_70*x*x*x*x + 2*sqrt_30*x*x*y*y - 24*sqrt_6*x*x*z*z - 3*sqrt_70*y*y*y*y + 8*sqrt_30*y*y*z*z))/48; - eval_z[3] = sqrt_15*x*y*(bf*(-x*x - y*y + 6*z*z) - bf_z*z*(x*x + y*y - 2*z*z))/6; - eval_z[4] = y*(-8*bf*z*(9*sqrt_7*x*x + 9*sqrt_35*y*y - 28*sqrt_15*z*z) + bf_z*(7*sqrt_15*x*x*x*x + 6*sqrt_35*x*x*y*y - 36*sqrt_7*x*x*z*z + 21*sqrt_15*y*y*y*y - 36*sqrt_35*y*y*z*z + 56*sqrt_15*z*z*z*z))/168; - eval_z[5] = 5*bf*x*x*x*x/8 + sqrt_105*bf*x*x*y*y/28 - 5*sqrt_21*bf*x*x*z*z/7 + 5*bf*y*y*y*y/8 - 5*sqrt_21*bf*y*y*z*z/7 + 5*bf*z*z*z*z + 5*bf_z*x*x*x*x*z/8 + sqrt_105*bf_z*x*x*y*y*z/28 - 5*sqrt_21*bf_z*x*x*z*z*z/21 + 5*bf_z*y*y*y*y*z/8 - 5*sqrt_21*bf_z*y*y*z*z*z/21 + bf_z*z*z*z*z*z; - eval_z[6] = x*(-8*bf*z*(9*sqrt_35*x*x + 9*sqrt_7*y*y - 28*sqrt_15*z*z) + bf_z*(21*sqrt_15*x*x*x*x + 6*sqrt_35*x*x*y*y - 36*sqrt_35*x*x*z*z + 7*sqrt_15*y*y*y*y - 36*sqrt_7*y*y*z*z + 56*sqrt_15*z*z*z*z))/168; - eval_z[7] = -sqrt_105*bf*x*x*x*x/12 + 3*sqrt_5*bf*x*x*z*z/2 + sqrt_105*bf*y*y*y*y/12 - 3*sqrt_5*bf*y*y*z*z/2 - sqrt_105*bf_z*x*x*x*x*z/12 + sqrt_5*bf_z*x*x*z*z*z/2 + sqrt_105*bf_z*y*y*y*y*z/12 - sqrt_5*bf_z*y*y*z*z*z/2; - eval_z[8] = x*(16*bf*z*(sqrt_30*x*x - 3*sqrt_6*y*y) + bf_z*(-3*sqrt_70*x*x*x*x + 2*sqrt_30*x*x*y*y + 8*sqrt_30*x*x*z*z + 3*sqrt_70*y*y*y*y - 24*sqrt_6*y*y*z*z))/48; - eval_z[9] = (bf + bf_z*z)*(sqrt_35*x*x*x*x - 6*sqrt_3*x*x*y*y + sqrt_35*y*y*y*y)/8; - eval_z[10] = bf_z*x*(3*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 5*sqrt_14*y*y*y*y)/16; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_6( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*x*y*(3*sqrt_42*x*x*x*x - 10*sqrt_10*x*x*y*y + 3*sqrt_42*y*y*y*y)/16; - eval[1] = bf*y*z*(5*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 3*sqrt_14*y*y*y*y)/16; - eval[2] = bf*x*y*(-3*sqrt_77*x*x*x*x + 10*sqrt_33*x*x*z*z + 3*sqrt_77*y*y*y*y - 10*sqrt_33*y*y*z*z)/44; - eval[3] = bf*y*z*(-3*sqrt_2310*x*x*x*x - 6*sqrt_110*x*x*y*y + 24*sqrt_110*x*x*z*z + 3*sqrt_2310*y*y*y*y - 40*sqrt_22*y*y*z*z)/176; - eval[4] = bf*x*y*(3*sqrt_2310*x*x*x*x + 30*sqrt_22*x*x*y*y - 48*sqrt_110*x*x*z*z + 3*sqrt_2310*y*y*y*y - 48*sqrt_110*y*y*z*z + 16*sqrt_2310*z*z*z*z)/528; - eval[5] = bf*y*z*(5*sqrt_231*x*x*x*x + 30*sqrt_11*x*x*y*y - 60*sqrt_11*x*x*z*z + 15*sqrt_231*y*y*y*y - 60*sqrt_55*y*y*z*z + 24*sqrt_231*z*z*z*z)/264; - eval[6] = bf*(-385*x*x*x*x*x*x - 35*sqrt_33*x*x*x*x*y*y + 210*sqrt_33*x*x*x*x*z*z - 35*sqrt_33*x*x*y*y*y*y + 36*sqrt_385*x*x*y*y*z*z - 280*sqrt_33*x*x*z*z*z*z - 385*y*y*y*y*y*y + 210*sqrt_33*y*y*y*y*z*z - 280*sqrt_33*y*y*z*z*z*z + 1232*z*z*z*z*z*z)/1232; - eval[7] = bf*x*z*(15*sqrt_231*x*x*x*x + 30*sqrt_11*x*x*y*y - 60*sqrt_55*x*x*z*z + 5*sqrt_231*y*y*y*y - 60*sqrt_11*y*y*z*z + 24*sqrt_231*z*z*z*z)/264; - eval[8] = bf*(11*sqrt_210*x*x*x*x*x*x + sqrt_770*x*x*x*x*y*y - 16*sqrt_770*x*x*x*x*z*z - sqrt_770*x*x*y*y*y*y + 16*sqrt_770*x*x*z*z*z*z - 11*sqrt_210*y*y*y*y*y*y + 16*sqrt_770*y*y*y*y*z*z - 16*sqrt_770*y*y*z*z*z*z)/352; - eval[9] = bf*x*z*(-3*sqrt_2310*x*x*x*x + 6*sqrt_110*x*x*y*y + 40*sqrt_22*x*x*z*z + 3*sqrt_2310*y*y*y*y - 24*sqrt_110*y*y*z*z)/176; - eval[10] = bf*(-33*sqrt_7*x*x*x*x*x*x + 5*sqrt_231*x*x*x*x*y*y + 10*sqrt_231*x*x*x*x*z*z + 5*sqrt_231*x*x*y*y*y*y - 36*sqrt_55*x*x*y*y*z*z - 33*sqrt_7*y*y*y*y*y*y + 10*sqrt_231*y*y*y*y*z*z)/176; - eval[11] = bf*x*z*(3*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 5*sqrt_14*y*y*y*y)/16; - eval[12] = bf*(sqrt_462*x*x*x*x*x*x - 15*sqrt_14*x*x*x*x*y*y + 15*sqrt_14*x*x*y*y*y*y - sqrt_462*y*y*y*y*y*y)/32; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_6_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = y*(15*sqrt_42*bf*x*x*x*x - 30*sqrt_10*bf*x*x*y*y + 3*sqrt_42*bf*y*y*y*y + 3*sqrt_42*bf_x*x*x*x*x*x - 10*sqrt_10*bf_x*x*x*x*y*y + 3*sqrt_42*bf_x*x*y*y*y*y)/16; - eval_x[1] = y*z*(20*bf*x*(sqrt_14*x*x - sqrt_6*y*y) + bf_x*(5*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 3*sqrt_14*y*y*y*y))/16; - eval_x[2] = y*(-15*sqrt_77*bf*x*x*x*x + 30*sqrt_33*bf*x*x*z*z + 3*sqrt_77*bf*y*y*y*y - 10*sqrt_33*bf*y*y*z*z - 3*sqrt_77*bf_x*x*x*x*x*x + 10*sqrt_33*bf_x*x*x*x*z*z + 3*sqrt_77*bf_x*x*y*y*y*y - 10*sqrt_33*bf_x*x*y*y*z*z)/44; - eval_x[3] = -y*z*(12*bf*x*(sqrt_2310*x*x + sqrt_110*y*y - 4*sqrt_110*z*z) + bf_x*(3*sqrt_2310*x*x*x*x + 6*sqrt_110*x*x*y*y - 24*sqrt_110*x*x*z*z - 3*sqrt_2310*y*y*y*y + 40*sqrt_22*y*y*z*z))/176; - eval_x[4] = y*(15*sqrt_2310*bf*x*x*x*x + 90*sqrt_22*bf*x*x*y*y - 144*sqrt_110*bf*x*x*z*z + 3*sqrt_2310*bf*y*y*y*y - 48*sqrt_110*bf*y*y*z*z + 16*sqrt_2310*bf*z*z*z*z + 3*sqrt_2310*bf_x*x*x*x*x*x + 30*sqrt_22*bf_x*x*x*x*y*y - 48*sqrt_110*bf_x*x*x*x*z*z + 3*sqrt_2310*bf_x*x*y*y*y*y - 48*sqrt_110*bf_x*x*y*y*z*z + 16*sqrt_2310*bf_x*x*z*z*z*z)/528; - eval_x[5] = y*z*(20*bf*x*(sqrt_231*x*x + 3*sqrt_11*y*y - 6*sqrt_11*z*z) + bf_x*(5*sqrt_231*x*x*x*x + 30*sqrt_11*x*x*y*y - 60*sqrt_11*x*x*z*z + 15*sqrt_231*y*y*y*y - 60*sqrt_55*y*y*z*z + 24*sqrt_231*z*z*z*z))/264; - eval_x[6] = -bf*x*(1155*x*x*x*x + 70*sqrt_33*x*x*y*y - 420*sqrt_33*x*x*z*z + 35*sqrt_33*y*y*y*y - 36*sqrt_385*y*y*z*z + 280*sqrt_33*z*z*z*z)/616 - bf_x*(385*x*x*x*x*x*x + 35*sqrt_33*x*x*x*x*y*y - 210*sqrt_33*x*x*x*x*z*z + 35*sqrt_33*x*x*y*y*y*y - 36*sqrt_385*x*x*y*y*z*z + 280*sqrt_33*x*x*z*z*z*z + 385*y*y*y*y*y*y - 210*sqrt_33*y*y*y*y*z*z + 280*sqrt_33*y*y*z*z*z*z - 1232*z*z*z*z*z*z)/1232; - eval_x[7] = z*(75*sqrt_231*bf*x*x*x*x + 90*sqrt_11*bf*x*x*y*y - 180*sqrt_55*bf*x*x*z*z + 5*sqrt_231*bf*y*y*y*y - 60*sqrt_11*bf*y*y*z*z + 24*sqrt_231*bf*z*z*z*z + 15*sqrt_231*bf_x*x*x*x*x*x + 30*sqrt_11*bf_x*x*x*x*y*y - 60*sqrt_55*bf_x*x*x*x*z*z + 5*sqrt_231*bf_x*x*y*y*y*y - 60*sqrt_11*bf_x*x*y*y*z*z + 24*sqrt_231*bf_x*x*z*z*z*z)/264; - eval_x[8] = bf*x*(33*sqrt_210*x*x*x*x + 2*sqrt_770*x*x*y*y - 32*sqrt_770*x*x*z*z - sqrt_770*y*y*y*y + 16*sqrt_770*z*z*z*z)/176 + bf_x*(11*sqrt_210*x*x*x*x*x*x + sqrt_770*x*x*x*x*y*y - 16*sqrt_770*x*x*x*x*z*z - sqrt_770*x*x*y*y*y*y + 16*sqrt_770*x*x*z*z*z*z - 11*sqrt_210*y*y*y*y*y*y + 16*sqrt_770*y*y*y*y*z*z - 16*sqrt_770*y*y*z*z*z*z)/352; - eval_x[9] = z*(-15*sqrt_2310*bf*x*x*x*x + 18*sqrt_110*bf*x*x*y*y + 120*sqrt_22*bf*x*x*z*z + 3*sqrt_2310*bf*y*y*y*y - 24*sqrt_110*bf*y*y*z*z - 3*sqrt_2310*bf_x*x*x*x*x*x + 6*sqrt_110*bf_x*x*x*x*y*y + 40*sqrt_22*bf_x*x*x*x*z*z + 3*sqrt_2310*bf_x*x*y*y*y*y - 24*sqrt_110*bf_x*x*y*y*z*z)/176; - eval_x[10] = bf*x*(-99*sqrt_7*x*x*x*x + 10*sqrt_231*x*x*y*y + 20*sqrt_231*x*x*z*z + 5*sqrt_231*y*y*y*y - 36*sqrt_55*y*y*z*z)/88 + bf_x*(-33*sqrt_7*x*x*x*x*x*x + 5*sqrt_231*x*x*x*x*y*y + 10*sqrt_231*x*x*x*x*z*z + 5*sqrt_231*x*x*y*y*y*y - 36*sqrt_55*x*x*y*y*z*z - 33*sqrt_7*y*y*y*y*y*y + 10*sqrt_231*y*y*y*y*z*z)/176; - eval_x[11] = z*(15*sqrt_14*bf*x*x*x*x - 30*sqrt_6*bf*x*x*y*y + 5*sqrt_14*bf*y*y*y*y + 3*sqrt_14*bf_x*x*x*x*x*x - 10*sqrt_6*bf_x*x*x*x*y*y + 5*sqrt_14*bf_x*x*y*y*y*y)/16; - eval_x[12] = 3*bf*x*(sqrt_462*x*x*x*x - 10*sqrt_14*x*x*y*y + 5*sqrt_14*y*y*y*y)/16 + bf_x*(sqrt_462*x*x*x*x*x*x - 15*sqrt_14*x*x*x*x*y*y + 15*sqrt_14*x*x*y*y*y*y - sqrt_462*y*y*y*y*y*y)/32; - - eval_y[0] = x*(3*sqrt_42*bf*x*x*x*x - 30*sqrt_10*bf*x*x*y*y + 15*sqrt_42*bf*y*y*y*y + 3*sqrt_42*bf_y*x*x*x*x*y - 10*sqrt_10*bf_y*x*x*y*y*y + 3*sqrt_42*bf_y*y*y*y*y*y)/16; - eval_y[1] = z*(5*sqrt_14*bf*x*x*x*x - 30*sqrt_6*bf*x*x*y*y + 15*sqrt_14*bf*y*y*y*y + 5*sqrt_14*bf_y*x*x*x*x*y - 10*sqrt_6*bf_y*x*x*y*y*y + 3*sqrt_14*bf_y*y*y*y*y*y)/16; - eval_y[2] = x*(-3*sqrt_77*bf*x*x*x*x + 10*sqrt_33*bf*x*x*z*z + 15*sqrt_77*bf*y*y*y*y - 30*sqrt_33*bf*y*y*z*z - 3*sqrt_77*bf_y*x*x*x*x*y + 10*sqrt_33*bf_y*x*x*y*z*z + 3*sqrt_77*bf_y*y*y*y*y*y - 10*sqrt_33*bf_y*y*y*y*z*z)/44; - eval_y[3] = z*(-3*sqrt_2310*bf*x*x*x*x - 18*sqrt_110*bf*x*x*y*y + 24*sqrt_110*bf*x*x*z*z + 15*sqrt_2310*bf*y*y*y*y - 120*sqrt_22*bf*y*y*z*z - 3*sqrt_2310*bf_y*x*x*x*x*y - 6*sqrt_110*bf_y*x*x*y*y*y + 24*sqrt_110*bf_y*x*x*y*z*z + 3*sqrt_2310*bf_y*y*y*y*y*y - 40*sqrt_22*bf_y*y*y*y*z*z)/176; - eval_y[4] = x*(3*sqrt_2310*bf*x*x*x*x + 90*sqrt_22*bf*x*x*y*y - 48*sqrt_110*bf*x*x*z*z + 15*sqrt_2310*bf*y*y*y*y - 144*sqrt_110*bf*y*y*z*z + 16*sqrt_2310*bf*z*z*z*z + 3*sqrt_2310*bf_y*x*x*x*x*y + 30*sqrt_22*bf_y*x*x*y*y*y - 48*sqrt_110*bf_y*x*x*y*z*z + 3*sqrt_2310*bf_y*y*y*y*y*y - 48*sqrt_110*bf_y*y*y*y*z*z + 16*sqrt_2310*bf_y*y*z*z*z*z)/528; - eval_y[5] = z*(5*sqrt_231*bf*x*x*x*x + 90*sqrt_11*bf*x*x*y*y - 60*sqrt_11*bf*x*x*z*z + 75*sqrt_231*bf*y*y*y*y - 180*sqrt_55*bf*y*y*z*z + 24*sqrt_231*bf*z*z*z*z + 5*sqrt_231*bf_y*x*x*x*x*y + 30*sqrt_11*bf_y*x*x*y*y*y - 60*sqrt_11*bf_y*x*x*y*z*z + 15*sqrt_231*bf_y*y*y*y*y*y - 60*sqrt_55*bf_y*y*y*y*z*z + 24*sqrt_231*bf_y*y*z*z*z*z)/264; - eval_y[6] = -bf*y*(35*sqrt_33*x*x*x*x + 70*sqrt_33*x*x*y*y - 36*sqrt_385*x*x*z*z + 1155*y*y*y*y - 420*sqrt_33*y*y*z*z + 280*sqrt_33*z*z*z*z)/616 - bf_y*(385*x*x*x*x*x*x + 35*sqrt_33*x*x*x*x*y*y - 210*sqrt_33*x*x*x*x*z*z + 35*sqrt_33*x*x*y*y*y*y - 36*sqrt_385*x*x*y*y*z*z + 280*sqrt_33*x*x*z*z*z*z + 385*y*y*y*y*y*y - 210*sqrt_33*y*y*y*y*z*z + 280*sqrt_33*y*y*z*z*z*z - 1232*z*z*z*z*z*z)/1232; - eval_y[7] = x*z*(20*bf*y*(3*sqrt_11*x*x + sqrt_231*y*y - 6*sqrt_11*z*z) + bf_y*(15*sqrt_231*x*x*x*x + 30*sqrt_11*x*x*y*y - 60*sqrt_55*x*x*z*z + 5*sqrt_231*y*y*y*y - 60*sqrt_11*y*y*z*z + 24*sqrt_231*z*z*z*z))/264; - eval_y[8] = -bf*y*(-sqrt_770*x*x*x*x + 2*sqrt_770*x*x*y*y + 33*sqrt_210*y*y*y*y - 32*sqrt_770*y*y*z*z + 16*sqrt_770*z*z*z*z)/176 + bf_y*(11*sqrt_210*x*x*x*x*x*x + sqrt_770*x*x*x*x*y*y - 16*sqrt_770*x*x*x*x*z*z - sqrt_770*x*x*y*y*y*y + 16*sqrt_770*x*x*z*z*z*z - 11*sqrt_210*y*y*y*y*y*y + 16*sqrt_770*y*y*y*y*z*z - 16*sqrt_770*y*y*z*z*z*z)/352; - eval_y[9] = x*z*(12*bf*y*(sqrt_110*x*x + sqrt_2310*y*y - 4*sqrt_110*z*z) + bf_y*(-3*sqrt_2310*x*x*x*x + 6*sqrt_110*x*x*y*y + 40*sqrt_22*x*x*z*z + 3*sqrt_2310*y*y*y*y - 24*sqrt_110*y*y*z*z))/176; - eval_y[10] = bf*y*(5*sqrt_231*x*x*x*x + 10*sqrt_231*x*x*y*y - 36*sqrt_55*x*x*z*z - 99*sqrt_7*y*y*y*y + 20*sqrt_231*y*y*z*z)/88 + bf_y*(-33*sqrt_7*x*x*x*x*x*x + 5*sqrt_231*x*x*x*x*y*y + 10*sqrt_231*x*x*x*x*z*z + 5*sqrt_231*x*x*y*y*y*y - 36*sqrt_55*x*x*y*y*z*z - 33*sqrt_7*y*y*y*y*y*y + 10*sqrt_231*y*y*y*y*z*z)/176; - eval_y[11] = x*z*(-20*bf*y*(sqrt_6*x*x - sqrt_14*y*y) + bf_y*(3*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 5*sqrt_14*y*y*y*y))/16; - eval_y[12] = -3*bf*y*(5*sqrt_14*x*x*x*x - 10*sqrt_14*x*x*y*y + sqrt_462*y*y*y*y)/16 + bf_y*(sqrt_462*x*x*x*x*x*x - 15*sqrt_14*x*x*x*x*y*y + 15*sqrt_14*x*x*y*y*y*y - sqrt_462*y*y*y*y*y*y)/32; - - eval_z[0] = bf_z*x*y*(3*sqrt_42*x*x*x*x - 10*sqrt_10*x*x*y*y + 3*sqrt_42*y*y*y*y)/16; - eval_z[1] = y*(bf + bf_z*z)*(5*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 3*sqrt_14*y*y*y*y)/16; - eval_z[2] = x*y*(20*sqrt_33*bf*z*(x*x - y*y) - bf_z*(3*sqrt_77*x*x*x*x - 10*sqrt_33*x*x*z*z - 3*sqrt_77*y*y*y*y + 10*sqrt_33*y*y*z*z))/44; - eval_z[3] = y*(-3*sqrt_2310*bf*x*x*x*x - 6*sqrt_110*bf*x*x*y*y + 72*sqrt_110*bf*x*x*z*z + 3*sqrt_2310*bf*y*y*y*y - 120*sqrt_22*bf*y*y*z*z - 3*sqrt_2310*bf_z*x*x*x*x*z - 6*sqrt_110*bf_z*x*x*y*y*z + 24*sqrt_110*bf_z*x*x*z*z*z + 3*sqrt_2310*bf_z*y*y*y*y*z - 40*sqrt_22*bf_z*y*y*z*z*z)/176; - eval_z[4] = x*y*(-32*bf*z*(3*sqrt_110*x*x + 3*sqrt_110*y*y - 2*sqrt_2310*z*z) + bf_z*(3*sqrt_2310*x*x*x*x + 30*sqrt_22*x*x*y*y - 48*sqrt_110*x*x*z*z + 3*sqrt_2310*y*y*y*y - 48*sqrt_110*y*y*z*z + 16*sqrt_2310*z*z*z*z))/528; - eval_z[5] = y*(5*sqrt_231*bf*x*x*x*x + 30*sqrt_11*bf*x*x*y*y - 180*sqrt_11*bf*x*x*z*z + 15*sqrt_231*bf*y*y*y*y - 180*sqrt_55*bf*y*y*z*z + 120*sqrt_231*bf*z*z*z*z + 5*sqrt_231*bf_z*x*x*x*x*z + 30*sqrt_11*bf_z*x*x*y*y*z - 60*sqrt_11*bf_z*x*x*z*z*z + 15*sqrt_231*bf_z*y*y*y*y*z - 60*sqrt_55*bf_z*y*y*z*z*z + 24*sqrt_231*bf_z*z*z*z*z*z)/264; - eval_z[6] = bf*z*(105*sqrt_33*x*x*x*x + 18*sqrt_385*x*x*y*y - 280*sqrt_33*x*x*z*z + 105*sqrt_33*y*y*y*y - 280*sqrt_33*y*y*z*z + 1848*z*z*z*z)/308 - bf_z*(385*x*x*x*x*x*x + 35*sqrt_33*x*x*x*x*y*y - 210*sqrt_33*x*x*x*x*z*z + 35*sqrt_33*x*x*y*y*y*y - 36*sqrt_385*x*x*y*y*z*z + 280*sqrt_33*x*x*z*z*z*z + 385*y*y*y*y*y*y - 210*sqrt_33*y*y*y*y*z*z + 280*sqrt_33*y*y*z*z*z*z - 1232*z*z*z*z*z*z)/1232; - eval_z[7] = x*(15*sqrt_231*bf*x*x*x*x + 30*sqrt_11*bf*x*x*y*y - 180*sqrt_55*bf*x*x*z*z + 5*sqrt_231*bf*y*y*y*y - 180*sqrt_11*bf*y*y*z*z + 120*sqrt_231*bf*z*z*z*z + 15*sqrt_231*bf_z*x*x*x*x*z + 30*sqrt_11*bf_z*x*x*y*y*z - 60*sqrt_55*bf_z*x*x*z*z*z + 5*sqrt_231*bf_z*y*y*y*y*z - 60*sqrt_11*bf_z*y*y*z*z*z + 24*sqrt_231*bf_z*z*z*z*z*z)/264; - eval_z[8] = -sqrt_770*bf*z*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z)/11 + bf_z*(11*sqrt_210*x*x*x*x*x*x + sqrt_770*x*x*x*x*y*y - 16*sqrt_770*x*x*x*x*z*z - sqrt_770*x*x*y*y*y*y + 16*sqrt_770*x*x*z*z*z*z - 11*sqrt_210*y*y*y*y*y*y + 16*sqrt_770*y*y*y*y*z*z - 16*sqrt_770*y*y*z*z*z*z)/352; - eval_z[9] = x*(-3*sqrt_2310*bf*x*x*x*x + 6*sqrt_110*bf*x*x*y*y + 120*sqrt_22*bf*x*x*z*z + 3*sqrt_2310*bf*y*y*y*y - 72*sqrt_110*bf*y*y*z*z - 3*sqrt_2310*bf_z*x*x*x*x*z + 6*sqrt_110*bf_z*x*x*y*y*z + 40*sqrt_22*bf_z*x*x*z*z*z + 3*sqrt_2310*bf_z*y*y*y*y*z - 24*sqrt_110*bf_z*y*y*z*z*z)/176; - eval_z[10] = bf*z*(5*sqrt_231*x*x*x*x - 18*sqrt_55*x*x*y*y + 5*sqrt_231*y*y*y*y)/44 + bf_z*(-33*sqrt_7*x*x*x*x*x*x + 5*sqrt_231*x*x*x*x*y*y + 10*sqrt_231*x*x*x*x*z*z + 5*sqrt_231*x*x*y*y*y*y - 36*sqrt_55*x*x*y*y*z*z - 33*sqrt_7*y*y*y*y*y*y + 10*sqrt_231*y*y*y*y*z*z)/176; - eval_z[11] = x*(bf + bf_z*z)*(3*sqrt_14*x*x*x*x - 10*sqrt_6*x*x*y*y + 5*sqrt_14*y*y*y*y)/16; - eval_z[12] = bf_z*(sqrt_462*x*x*x*x*x*x - 15*sqrt_14*x*x*x*x*y*y + 15*sqrt_14*x*x*y*y*y*y - sqrt_462*y*y*y*y*y*y)/32; - -} - - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular( - const int64_t l, - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - -//switch( l ) { - - //case 0: - if( l == 0 ) { - gaueval_spherical_angular_0( bf, x, y, z, eval ); - //break; - - //case 1: - } else if( l == 1 ) { - gaueval_spherical_angular_1( bf, x, y, z, eval ); - //break; - - //case 2: - } else if( l == 2 ) { - gaueval_spherical_angular_2( bf, x, y, z, eval ); - //break; - - //case 3: - } else if( l == 3 ) { - gaueval_spherical_angular_3( bf, x, y, z, eval ); - //break; - - //case 4: - } else if( l == 4 ) { - gaueval_spherical_angular_4( bf, x, y, z, eval ); - //break; - - //case 5: - } else if( l == 5 ) { - gaueval_spherical_angular_5( bf, x, y, z, eval ); - //break; - - //case 6: - } else if( l == 6 ) { - gaueval_spherical_angular_6( bf, x, y, z, eval ); - //break; - - //default: - } else { - assert( false && "L < L_MAX" ); - //break; - } - -//} // switch(l) - -} // gaueval_spherical_angular - - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_angular_deriv1( - const int64_t l, - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval, - double* eval_x, - double* eval_y, - double* eval_z -) { - -//switch( l ) { - -// case 0: - if( l == 0 ) { - gaueval_spherical_angular_0( bf, x, y, z, eval ); - gaueval_spherical_angular_0_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 1: - } else if( l == 1 ) { - gaueval_spherical_angular_1( bf, x, y, z, eval ); - gaueval_spherical_angular_1_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 2: - } else if( l == 2 ) { - gaueval_spherical_angular_2( bf, x, y, z, eval ); - gaueval_spherical_angular_2_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 3: - } else if( l == 3 ) { - gaueval_spherical_angular_3( bf, x, y, z, eval ); - gaueval_spherical_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 4: - } else if( l == 4 ) { - gaueval_spherical_angular_4( bf, x, y, z, eval ); - gaueval_spherical_angular_4_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 5: - } else if( l == 5 ) { - gaueval_spherical_angular_5( bf, x, y, z, eval ); - gaueval_spherical_angular_5_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 6: - } else if( l == 6 ) { - gaueval_spherical_angular_6( bf, x, y, z, eval ); - gaueval_spherical_angular_6_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// default: - } else { - assert( false && "L < L_MAX" ); -// break; - } - -//} // switch(l) - -} // gaueval_spherical_angular_deriv1 - - - -} // namespace gpgaueval diff --git a/src/cuda_integrator/collocation_generator/gaueval_angular_spherical_unnorm.hpp b/src/cuda_integrator/collocation_generator/gaueval_angular_spherical_unnorm.hpp deleted file mode 100644 index ee6f679c7..000000000 --- a/src/cuda_integrator/collocation_generator/gaueval_angular_spherical_unnorm.hpp +++ /dev/null @@ -1,539 +0,0 @@ -#pragma once -#include "gaueval_device_constants.hpp" -#include - -#ifndef GPGAUEVAL_INLINE -# define GPGAUEVAL_INLINE __inline__ -#endif - -namespace gpgaueval { - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_0( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_0_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = bf_x; - - eval_y[0] = bf_y; - - eval_z[0] = bf_z; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_1( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*y; - eval[1] = bf*z; - eval[2] = bf*x; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_1_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = bf_x*y; - eval_x[1] = bf_x*z; - eval_x[2] = bf + bf_x*x; - - eval_y[0] = bf + bf_y*y; - eval_y[1] = bf_y*z; - eval_y[2] = bf_y*x; - - eval_z[0] = bf_z*y; - eval_z[1] = bf + bf_z*z; - eval_z[2] = bf_z*x; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_2( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = sqrt_3*bf*x*y; - eval[1] = sqrt_3*bf*y*z; - eval[2] = bf*(-x*x - y*y + 2*z*z)/2; - eval[3] = sqrt_3*bf*x*z; - eval[4] = sqrt_3*bf*(x*x - y*y)/2; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_2_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = sqrt_3*y*(bf + bf_x*x); - eval_x[1] = sqrt_3*bf_x*y*z; - eval_x[2] = -bf*x - bf_x*(x*x + y*y - 2*z*z)/2; - eval_x[3] = sqrt_3*z*(bf + bf_x*x); - eval_x[4] = sqrt_3*(bf*x + bf_x*(x*x - y*y)/2); - - eval_y[0] = sqrt_3*x*(bf + bf_y*y); - eval_y[1] = sqrt_3*z*(bf + bf_y*y); - eval_y[2] = -bf*y - bf_y*(x*x + y*y - 2*z*z)/2; - eval_y[3] = sqrt_3*bf_y*x*z; - eval_y[4] = sqrt_3*(-bf*y + bf_y*(x*x - y*y)/2); - - eval_z[0] = sqrt_3*bf_z*x*y; - eval_z[1] = sqrt_3*y*(bf + bf_z*z); - eval_z[2] = 2*bf*z - bf_z*(x*x + y*y - 2*z*z)/2; - eval_z[3] = sqrt_3*x*(bf + bf_z*z); - eval_z[4] = sqrt_3*bf_z*(x*x - y*y)/2; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_3( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = sqrt_10*bf*y*(3*x*x - y*y)/4; - eval[1] = sqrt_15*bf*x*y*z; - eval[2] = sqrt_6*bf*y*(-x*x - y*y + 4*z*z)/4; - eval[3] = bf*z*(-3*x*x - 3*y*y + 2*z*z)/2; - eval[4] = sqrt_6*bf*x*(-x*x - y*y + 4*z*z)/4; - eval[5] = sqrt_15*bf*z*(x*x - y*y)/2; - eval[6] = sqrt_10*bf*x*(x*x - 3*y*y)/4; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_3_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = sqrt_10*y*(6*bf*x + bf_x*(3*x*x - y*y))/4; - eval_x[1] = sqrt_15*y*z*(bf + bf_x*x); - eval_x[2] = -sqrt_6*y*(2*bf*x + bf_x*(x*x + y*y - 4*z*z))/4; - eval_x[3] = -z*(6*bf*x + bf_x*(3*x*x + 3*y*y - 2*z*z))/2; - eval_x[4] = -sqrt_6*(bf*(3*x*x + y*y - 4*z*z) + bf_x*x*(x*x + y*y - 4*z*z))/4; - eval_x[5] = sqrt_15*z*(2*bf*x + bf_x*(x*x - y*y))/2; - eval_x[6] = sqrt_10*(3*bf*(x*x - y*y) + bf_x*x*(x*x - 3*y*y))/4; - - eval_y[0] = sqrt_10*(-3*bf*(-x*x + y*y) + bf_y*y*(3*x*x - y*y))/4; - eval_y[1] = sqrt_15*x*z*(bf + bf_y*y); - eval_y[2] = -sqrt_6*(bf*(x*x + 3*y*y - 4*z*z) + bf_y*y*(x*x + y*y - 4*z*z))/4; - eval_y[3] = -z*(6*bf*y + bf_y*(3*x*x + 3*y*y - 2*z*z))/2; - eval_y[4] = -sqrt_6*x*(2*bf*y + bf_y*(x*x + y*y - 4*z*z))/4; - eval_y[5] = sqrt_15*z*(-2*bf*y + bf_y*(x*x - y*y))/2; - eval_y[6] = sqrt_10*x*(-6*bf*y + bf_y*(x*x - 3*y*y))/4; - - eval_z[0] = sqrt_10*bf_z*y*(3*x*x - y*y)/4; - eval_z[1] = sqrt_15*x*y*(bf + bf_z*z); - eval_z[2] = sqrt_6*y*(8*bf*z - bf_z*(x*x + y*y - 4*z*z))/4; - eval_z[3] = -3*bf*(x*x + y*y - 2*z*z)/2 - bf_z*z*(3*x*x + 3*y*y - 2*z*z)/2; - eval_z[4] = sqrt_6*x*(8*bf*z - bf_z*(x*x + y*y - 4*z*z))/4; - eval_z[5] = sqrt_15*(bf + bf_z*z)*(x*x - y*y)/2; - eval_z[6] = sqrt_10*bf_z*x*(x*x - 3*y*y)/4; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_4( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = sqrt_35*bf*x*y*(x*x - y*y)/2; - eval[1] = sqrt_70*bf*y*z*(3*x*x - y*y)/4; - eval[2] = sqrt_5*bf*x*y*(-x*x - y*y + 6*z*z)/2; - eval[3] = sqrt_10*bf*y*z*(-3*x*x - 3*y*y + 4*z*z)/4; - eval[4] = bf*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; - eval[5] = sqrt_10*bf*x*z*(-3*x*x - 3*y*y + 4*z*z)/4; - eval[6] = sqrt_5*bf*(-x*x*x*x + 6*x*x*z*z + y*y*y*y - 6*y*y*z*z)/4; - eval[7] = sqrt_70*bf*x*z*(x*x - 3*y*y)/4; - eval[8] = sqrt_35*bf*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_4_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = sqrt_35*y*(bf*(3*x*x - y*y) + bf_x*x*(x*x - y*y))/2; - eval_x[1] = sqrt_70*y*z*(6*bf*x + bf_x*(3*x*x - y*y))/4; - eval_x[2] = -sqrt_5*y*(bf*(3*x*x + y*y - 6*z*z) + bf_x*x*(x*x + y*y - 6*z*z))/2; - eval_x[3] = -sqrt_10*y*z*(6*bf*x + bf_x*(3*x*x + 3*y*y - 4*z*z))/4; - eval_x[4] = 3*bf*x*(x*x + y*y - 4*z*z)/2 + bf_x*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; - eval_x[5] = -sqrt_10*z*(bf*(9*x*x + 3*y*y - 4*z*z) + bf_x*x*(3*x*x + 3*y*y - 4*z*z))/4; - eval_x[6] = -sqrt_5*(bf*x*(x*x - 3*z*z) + bf_x*(x*x*x*x - 6*x*x*z*z - y*y*y*y + 6*y*y*z*z)/4); - eval_x[7] = sqrt_70*z*(3*bf*(x*x - y*y) + bf_x*x*(x*x - 3*y*y))/4; - eval_x[8] = sqrt_35*(4*bf*x*(x*x - 3*y*y) + bf_x*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; - - eval_y[0] = sqrt_35*x*(-bf*(-x*x + 3*y*y) + bf_y*y*(x*x - y*y))/2; - eval_y[1] = sqrt_70*z*(-3*bf*(-x*x + y*y) + bf_y*y*(3*x*x - y*y))/4; - eval_y[2] = -sqrt_5*x*(bf*(x*x + 3*y*y - 6*z*z) + bf_y*y*(x*x + y*y - 6*z*z))/2; - eval_y[3] = -sqrt_10*z*(bf*(3*x*x + 9*y*y - 4*z*z) + bf_y*y*(3*x*x + 3*y*y - 4*z*z))/4; - eval_y[4] = 3*bf*y*(x*x + y*y - 4*z*z)/2 + bf_y*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; - eval_y[5] = -sqrt_10*x*z*(6*bf*y + bf_y*(3*x*x + 3*y*y - 4*z*z))/4; - eval_y[6] = sqrt_5*(bf*y*(y*y - 3*z*z) - bf_y*(x*x*x*x - 6*x*x*z*z - y*y*y*y + 6*y*y*z*z)/4); - eval_y[7] = sqrt_70*x*z*(-6*bf*y + bf_y*(x*x - 3*y*y))/4; - eval_y[8] = sqrt_35*(-4*bf*y*(3*x*x - y*y) + bf_y*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; - - eval_z[0] = sqrt_35*bf_z*x*y*(x*x - y*y)/2; - eval_z[1] = sqrt_70*y*(bf + bf_z*z)*(3*x*x - y*y)/4; - eval_z[2] = sqrt_5*x*y*(12*bf*z - bf_z*(x*x + y*y - 6*z*z))/2; - eval_z[3] = sqrt_10*y*(3*bf*(-x*x - y*y + 4*z*z) - bf_z*z*(3*x*x + 3*y*y - 4*z*z))/4; - eval_z[4] = -2*bf*z*(3*x*x + 3*y*y - 2*z*z) + bf_z*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; - eval_z[5] = sqrt_10*x*(3*bf*(-x*x - y*y + 4*z*z) - bf_z*z*(3*x*x + 3*y*y - 4*z*z))/4; - eval_z[6] = sqrt_5*(12*bf*z*(x*x - y*y) - bf_z*(x*x*x*x - 6*x*x*z*z - y*y*y*y + 6*y*y*z*z))/4; - eval_z[7] = sqrt_70*x*(bf + bf_z*z)*(x*x - 3*y*y)/4; - eval_z[8] = sqrt_35*bf_z*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_5( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = 3*sqrt_14*bf*y*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; - eval[1] = 3*sqrt_35*bf*x*y*z*(x*x - y*y)/2; - eval[2] = sqrt_70*bf*y*(-3*x*x*x*x - 2*x*x*y*y + 24*x*x*z*z + y*y*y*y - 8*y*y*z*z)/16; - eval[3] = sqrt_105*bf*x*y*z*(-x*x - y*y + 2*z*z)/2; - eval[4] = sqrt_15*bf*y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8; - eval[5] = bf*z*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z)/8; - eval[6] = sqrt_15*bf*x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8; - eval[7] = sqrt_105*bf*z*(-x*x*x*x + 2*x*x*z*z + y*y*y*y - 2*y*y*z*z)/4; - eval[8] = sqrt_70*bf*x*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z)/16; - eval[9] = 3*sqrt_35*bf*z*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; - eval[10] = 3*sqrt_14*bf*x*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_5_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = 3*sqrt_14*y*(20*bf*x*(x*x - y*y) + bf_x*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y))/16; - eval_x[1] = 3*sqrt_35*y*z*(bf*(3*x*x - y*y) + bf_x*x*(x*x - y*y))/2; - eval_x[2] = -sqrt_70*y*(4*bf*x*(3*x*x + y*y - 12*z*z) + bf_x*(3*x*x*x*x + 2*x*x*y*y - 24*x*x*z*z - y*y*y*y + 8*y*y*z*z))/16; - eval_x[3] = -sqrt_105*y*z*(bf*(3*x*x + y*y - 2*z*z) + bf_x*x*(x*x + y*y - 2*z*z))/2; - eval_x[4] = sqrt_15*y*(4*bf*x*(x*x + y*y - 6*z*z) + bf_x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; - eval_x[5] = z*(20*bf*x*(3*x*x + 3*y*y - 4*z*z) + bf_x*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z))/8; - eval_x[6] = sqrt_15*(bf*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + 4*x*x*(x*x + y*y - 6*z*z) + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z) + bf_x*x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; - eval_x[7] = -sqrt_105*z*(4*bf*x*(x*x - z*z) + bf_x*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z))/4; - eval_x[8] = sqrt_70*(bf*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 4*x*x*(-x*x + y*y + 4*z*z) + 3*y*y*y*y - 24*y*y*z*z) + bf_x*x*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z))/16; - eval_x[9] = 3*sqrt_35*z*(4*bf*x*(x*x - 3*y*y) + bf_x*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; - eval_x[10] = 3*sqrt_14*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + bf_x*x*x*x*x*x - 10*bf_x*x*x*x*y*y + 5*bf_x*x*y*y*y*y)/16; - - eval_y[0] = 3*sqrt_14*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + 5*bf_y*x*x*x*x*y - 10*bf_y*x*x*y*y*y + bf_y*y*y*y*y*y)/16; - eval_y[1] = 3*sqrt_35*x*z*(-bf*(-x*x + 3*y*y) + bf_y*y*(x*x - y*y))/2; - eval_y[2] = sqrt_70*(-3*bf*x*x*x*x - 6*bf*x*x*y*y + 24*bf*x*x*z*z + 5*bf*y*y*y*y - 24*bf*y*y*z*z - 3*bf_y*x*x*x*x*y - 2*bf_y*x*x*y*y*y + 24*bf_y*x*x*y*z*z + bf_y*y*y*y*y*y - 8*bf_y*y*y*y*z*z)/16; - eval_y[3] = -sqrt_105*x*z*(bf*(x*x + 3*y*y - 2*z*z) + bf_y*y*(x*x + y*y - 2*z*z))/2; - eval_y[4] = sqrt_15*(bf*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 4*y*y*(x*x + y*y - 6*z*z) + 8*z*z*z*z) + bf_y*y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; - eval_y[5] = z*(20*bf*y*(3*x*x + 3*y*y - 4*z*z) + bf_y*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z))/8; - eval_y[6] = sqrt_15*x*(4*bf*y*(x*x + y*y - 6*z*z) + bf_y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; - eval_y[7] = sqrt_105*z*(4*bf*y*(y*y - z*z) - bf_y*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z))/4; - eval_y[8] = sqrt_70*x*(4*bf*y*(x*x + 3*y*y - 12*z*z) + bf_y*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z))/16; - eval_y[9] = 3*sqrt_35*z*(-4*bf*y*(3*x*x - y*y) + bf_y*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; - eval_y[10] = 3*sqrt_14*x*(-20*bf*y*(x*x - y*y) + bf_y*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y))/16; - - eval_z[0] = 3*sqrt_14*bf_z*y*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; - eval_z[1] = 3*sqrt_35*x*y*(bf + bf_z*z)*(x*x - y*y)/2; - eval_z[2] = sqrt_70*y*(16*bf*z*(3*x*x - y*y) - bf_z*(3*x*x*x*x + 2*x*x*y*y - 24*x*x*z*z - y*y*y*y + 8*y*y*z*z))/16; - eval_z[3] = sqrt_105*x*y*(bf*(-x*x - y*y + 6*z*z) - bf_z*z*(x*x + y*y - 2*z*z))/2; - eval_z[4] = sqrt_15*y*(-8*bf*z*(3*x*x + 3*y*y - 4*z*z) + bf_z*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; - eval_z[5] = bf*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z + z*z*(-80*x*x - 80*y*y + 32*z*z))/8 + bf_z*z*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z)/8; - eval_z[6] = sqrt_15*x*(-8*bf*z*(3*x*x + 3*y*y - 4*z*z) + bf_z*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; - eval_z[7] = sqrt_105*(bf*(-x*x*x*x + 2*x*x*z*z + y*y*y*y - 2*y*y*z*z + 4*z*z*(x*x - y*y)) - bf_z*z*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z))/4; - eval_z[8] = sqrt_70*x*(16*bf*z*(x*x - 3*y*y) + bf_z*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z))/16; - eval_z[9] = 3*sqrt_35*(bf + bf_z*z)*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; - eval_z[10] = 3*sqrt_14*bf_z*x*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_6( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = sqrt_462*bf*x*y*(3*x*x*x*x - 10*x*x*y*y + 3*y*y*y*y)/16; - eval[1] = 3*sqrt_154*bf*y*z*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; - eval[2] = 3*sqrt_7*bf*x*y*(-x*x*x*x + 10*x*x*z*z + y*y*y*y - 10*y*y*z*z)/4; - eval[3] = sqrt_210*bf*y*z*(-9*x*x*x*x - 6*x*x*y*y + 24*x*x*z*z + 3*y*y*y*y - 8*y*y*z*z)/16; - eval[4] = sqrt_210*bf*x*y*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z)/16; - eval[5] = sqrt_21*bf*y*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z)/8; - eval[6] = bf*(-5*x*x*x*x*x*x - 15*x*x*x*x*y*y + 90*x*x*x*x*z*z - 15*x*x*y*y*y*y + 180*x*x*y*y*z*z - 120*x*x*z*z*z*z - 5*y*y*y*y*y*y + 90*y*y*y*y*z*z - 120*y*y*z*z*z*z + 16*z*z*z*z*z*z)/16; - eval[7] = sqrt_21*bf*x*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z)/8; - eval[8] = sqrt_210*bf*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z)/32; - eval[9] = sqrt_210*bf*x*z*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z)/16; - eval[10] = 3*sqrt_7*bf*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z)/16; - eval[11] = 3*sqrt_154*bf*x*z*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; - eval[12] = sqrt_462*bf*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y)/32; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_6_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = sqrt_462*y*(15*bf*x*x*x*x - 30*bf*x*x*y*y + 3*bf*y*y*y*y + 3*bf_x*x*x*x*x*x - 10*bf_x*x*x*x*y*y + 3*bf_x*x*y*y*y*y)/16; - eval_x[1] = 3*sqrt_154*y*z*(20*bf*x*(x*x - y*y) + bf_x*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y))/16; - eval_x[2] = 3*sqrt_7*y*(-5*bf*x*x*x*x + 30*bf*x*x*z*z + bf*y*y*y*y - 10*bf*y*y*z*z - bf_x*x*x*x*x*x + 10*bf_x*x*x*x*z*z + bf_x*x*y*y*y*y - 10*bf_x*x*y*y*z*z)/4; - eval_x[3] = -sqrt_210*y*z*(12*bf*x*(3*x*x + y*y - 4*z*z) + bf_x*(9*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z - 3*y*y*y*y + 8*y*y*z*z))/16; - eval_x[4] = sqrt_210*y*(bf*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + 4*x*x*(x*x + y*y - 8*z*z) + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z) + bf_x*x*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z))/16; - eval_x[5] = sqrt_21*y*z*(20*bf*x*(x*x + y*y - 2*z*z) + bf_x*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; - eval_x[6] = -15*bf*x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8 - bf_x*(5*x*x*x*x*x*x + 15*x*x*x*x*y*y - 90*x*x*x*x*z*z + 15*x*x*y*y*y*y - 180*x*x*y*y*z*z + 120*x*x*z*z*z*z + 5*y*y*y*y*y*y - 90*y*y*y*y*z*z + 120*y*y*z*z*z*z - 16*z*z*z*z*z*z)/16; - eval_x[7] = sqrt_21*z*(bf*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 20*x*x*(x*x + y*y - 2*z*z) + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z) + bf_x*x*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; - eval_x[8] = sqrt_210*(2*bf*x*(3*x*x*x*x + 2*x*x*y*y - 32*x*x*z*z - y*y*y*y + 16*z*z*z*z) + bf_x*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z))/32; - eval_x[9] = sqrt_210*z*(-15*bf*x*x*x*x + 18*bf*x*x*y*y + 24*bf*x*x*z*z + 9*bf*y*y*y*y - 24*bf*y*y*z*z - 3*bf_x*x*x*x*x*x + 6*bf_x*x*x*x*y*y + 8*bf_x*x*x*x*z*z + 9*bf_x*x*y*y*y*y - 24*bf_x*x*y*y*z*z)/16; - eval_x[10] = 3*sqrt_7*(2*bf*x*(-3*x*x*x*x + 10*x*x*y*y + 20*x*x*z*z + 5*y*y*y*y - 60*y*y*z*z) + bf_x*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z))/16; - eval_x[11] = 3*sqrt_154*z*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + bf_x*x*x*x*x*x - 10*bf_x*x*x*x*y*y + 5*bf_x*x*y*y*y*y)/16; - eval_x[12] = sqrt_462*(6*bf*x*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y) + bf_x*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y))/32; - - eval_y[0] = sqrt_462*x*(3*bf*x*x*x*x - 30*bf*x*x*y*y + 15*bf*y*y*y*y + 3*bf_y*x*x*x*x*y - 10*bf_y*x*x*y*y*y + 3*bf_y*y*y*y*y*y)/16; - eval_y[1] = 3*sqrt_154*z*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + 5*bf_y*x*x*x*x*y - 10*bf_y*x*x*y*y*y + bf_y*y*y*y*y*y)/16; - eval_y[2] = 3*sqrt_7*x*(bf*(-x*x*x*x + 10*x*x*z*z + y*y*y*y - 10*y*y*z*z + 4*y*y*(y*y - 5*z*z)) - bf_y*y*(x*x*x*x - 10*x*x*z*z - y*y*y*y + 10*y*y*z*z))/4; - eval_y[3] = sqrt_210*z*(-9*bf*x*x*x*x - 18*bf*x*x*y*y + 24*bf*x*x*z*z + 15*bf*y*y*y*y - 24*bf*y*y*z*z - 9*bf_y*x*x*x*x*y - 6*bf_y*x*x*y*y*y + 24*bf_y*x*x*y*z*z + 3*bf_y*y*y*y*y*y - 8*bf_y*y*y*y*z*z)/16; - eval_y[4] = sqrt_210*x*(bf*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 4*y*y*(x*x + y*y - 8*z*z) + 16*z*z*z*z) + bf_y*y*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z))/16; - eval_y[5] = sqrt_21*z*(bf*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 20*y*y*(x*x + y*y - 2*z*z) + 8*z*z*z*z) + bf_y*y*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; - eval_y[6] = -15*bf*y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8 - bf_y*(5*x*x*x*x*x*x + 15*x*x*x*x*y*y - 90*x*x*x*x*z*z + 15*x*x*y*y*y*y - 180*x*x*y*y*z*z + 120*x*x*z*z*z*z + 5*y*y*y*y*y*y - 90*y*y*y*y*z*z + 120*y*y*z*z*z*z - 16*z*z*z*z*z*z)/16; - eval_y[7] = sqrt_21*x*z*(20*bf*y*(x*x + y*y - 2*z*z) + bf_y*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; - eval_y[8] = sqrt_210*(-2*bf*y*(-x*x*x*x + 2*x*x*y*y + 3*y*y*y*y - 32*y*y*z*z + 16*z*z*z*z) + bf_y*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z))/32; - eval_y[9] = sqrt_210*x*z*(12*bf*y*(x*x + 3*y*y - 4*z*z) + bf_y*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z))/16; - eval_y[10] = 3*sqrt_7*(2*bf*y*(5*x*x*x*x + 10*x*x*y*y - 60*x*x*z*z - 3*y*y*y*y + 20*y*y*z*z) + bf_y*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z))/16; - eval_y[11] = 3*sqrt_154*x*z*(-20*bf*y*(x*x - y*y) + bf_y*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y))/16; - eval_y[12] = sqrt_462*(-6*bf*y*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y) + bf_y*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y))/32; - - eval_z[0] = sqrt_462*bf_z*x*y*(3*x*x*x*x - 10*x*x*y*y + 3*y*y*y*y)/16; - eval_z[1] = 3*sqrt_154*y*(bf + bf_z*z)*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; - eval_z[2] = 3*sqrt_7*x*y*(20*bf*z*(x*x - y*y) - bf_z*(x*x*x*x - 10*x*x*z*z - y*y*y*y + 10*y*y*z*z))/4; - eval_z[3] = sqrt_210*y*(bf*(-9*x*x*x*x - 6*x*x*y*y + 24*x*x*z*z + 3*y*y*y*y - 8*y*y*z*z + 16*z*z*(3*x*x - y*y)) - bf_z*z*(9*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z - 3*y*y*y*y + 8*y*y*z*z))/16; - eval_z[4] = sqrt_210*x*y*(-32*bf*z*(x*x + y*y - 2*z*z) + bf_z*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z))/16; - eval_z[5] = sqrt_21*y*(-bf*(-5*x*x*x*x - 10*x*x*y*y + 20*x*x*z*z - 5*y*y*y*y + 20*y*y*z*z - 8*z*z*z*z + z*z*(40*x*x + 40*y*y - 32*z*z)) + bf_z*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; - eval_z[6] = 3*bf*z*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z)/4 - bf_z*(5*x*x*x*x*x*x + 15*x*x*x*x*y*y - 90*x*x*x*x*z*z + 15*x*x*y*y*y*y - 180*x*x*y*y*z*z + 120*x*x*z*z*z*z + 5*y*y*y*y*y*y - 90*y*y*y*y*z*z + 120*y*y*z*z*z*z - 16*z*z*z*z*z*z)/16; - eval_z[7] = sqrt_21*x*(-bf*(-5*x*x*x*x - 10*x*x*y*y + 20*x*x*z*z - 5*y*y*y*y + 20*y*y*z*z - 8*z*z*z*z + z*z*(40*x*x + 40*y*y - 32*z*z)) + bf_z*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; - eval_z[8] = sqrt_210*(-bf*z*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z) + bf_z*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z)/32); - eval_z[9] = sqrt_210*x*(bf*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z + 16*z*z*(x*x - 3*y*y)) + bf_z*z*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z))/16; - eval_z[10] = 3*sqrt_7*(20*bf*z*(x*x*x*x - 6*x*x*y*y + y*y*y*y) + bf_z*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z))/16; - eval_z[11] = 3*sqrt_154*x*(bf + bf_z*z)*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; - eval_z[12] = sqrt_462*bf_z*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y)/32; - -} - - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular( - const int64_t l, - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - -//switch( l ) { - - //case 0: - if( l == 0 ) { - gaueval_spherical_unnorm_angular_0( bf, x, y, z, eval ); - //break; - - //case 1: - } else if( l == 1 ) { - gaueval_spherical_unnorm_angular_1( bf, x, y, z, eval ); - //break; - - //case 2: - } else if( l == 2 ) { - gaueval_spherical_unnorm_angular_2( bf, x, y, z, eval ); - //break; - - //case 3: - } else if( l == 3 ) { - gaueval_spherical_unnorm_angular_3( bf, x, y, z, eval ); - //break; - - //case 4: - } else if( l == 4 ) { - gaueval_spherical_unnorm_angular_4( bf, x, y, z, eval ); - //break; - - //case 5: - } else if( l == 5 ) { - gaueval_spherical_unnorm_angular_5( bf, x, y, z, eval ); - //break; - - //case 6: - } else if( l == 6 ) { - gaueval_spherical_unnorm_angular_6( bf, x, y, z, eval ); - //break; - - //default: - } else { - assert( false && "L < L_MAX" ); - //break; - } - -//} // switch(l) - -} // gaueval_spherical_unnorm_angular - - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_deriv1( - const int64_t l, - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval, - double* eval_x, - double* eval_y, - double* eval_z -) { - -//switch( l ) { - -// case 0: - if( l == 0 ) { - gaueval_spherical_unnorm_angular_0( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_0_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 1: - } else if( l == 1 ) { - gaueval_spherical_unnorm_angular_1( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_1_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 2: - } else if( l == 2 ) { - gaueval_spherical_unnorm_angular_2( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_2_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 3: - } else if( l == 3 ) { - gaueval_spherical_unnorm_angular_3( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 4: - } else if( l == 4 ) { - gaueval_spherical_unnorm_angular_4( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_4_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 5: - } else if( l == 5 ) { - gaueval_spherical_unnorm_angular_5( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_5_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// case 6: - } else if( l == 6 ) { - gaueval_spherical_unnorm_angular_6( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_6_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; - -// default: - } else { - assert( false && "L < L_MAX" ); -// break; - } - -//} // switch(l) - -} // gaueval_spherical_unnorm_angular_deriv1 - - - -} // namespace gpgaueval diff --git a/src/cuda_integrator/collocation_generator/gaueval_device_constants.hpp b/src/cuda_integrator/collocation_generator/gaueval_device_constants.hpp deleted file mode 100644 index a87dbd1d4..000000000 --- a/src/cuda_integrator/collocation_generator/gaueval_device_constants.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -namespace gpgaueval { - - constexpr double sqrt_5 = 2.23606797749979; - constexpr double sqrt_30 = 5.477225575051661; - constexpr double sqrt_21 = 4.58257569495584; - constexpr double sqrt_42 = 6.48074069840786; - constexpr double sqrt_77 = 8.774964387392123; - constexpr double sqrt_6 = 2.449489742783178; - constexpr double sqrt_210 = 14.491376746189438; - constexpr double sqrt_770 = 27.748873851023216; - constexpr double sqrt_10 = 3.1622776601683795; - constexpr double sqrt_231 = 15.198684153570664; - constexpr double sqrt_7 = 2.6457513110645907; - constexpr double sqrt_154 = 12.409673645990857; - constexpr double sqrt_462 = 21.494185260204677; - constexpr double sqrt_385 = 19.621416870348583; - constexpr double sqrt_2310 = 48.062459362791664; - constexpr double sqrt_110 = 10.488088481701515; - constexpr double sqrt_33 = 5.744562646538029; - constexpr double sqrt_3 = 1.7320508075688772; - constexpr double sqrt_70 = 8.366600265340756; - constexpr double sqrt_22 = 4.69041575982343; - constexpr double sqrt_11 = 3.3166247903554; - constexpr double sqrt_2 = 1.4142135623730951; - constexpr double sqrt_15 = 3.872983346207417; - constexpr double sqrt_35 = 5.916079783099616; - constexpr double sqrt_55 = 7.416198487095663; - constexpr double sqrt_105 = 10.246950765959598; - constexpr double sqrt_14 = 3.7416573867739413; - -} // namespace gpgaueval diff --git a/src/cuda_integrator/collocation_generator/gaueval_kernels_cartesian.cu b/src/cuda_integrator/collocation_generator/gaueval_kernels.cu similarity index 59% rename from src/cuda_integrator/collocation_generator/gaueval_kernels_cartesian.cu rename to src/cuda_integrator/collocation_generator/gaueval_kernels.cu index 8b0ca166a..a8756df5b 100644 --- a/src/cuda_integrator/collocation_generator/gaueval_kernels_cartesian.cu +++ b/src/cuda_integrator/collocation_generator/gaueval_kernels.cu @@ -1,32 +1,30 @@ -#include #include #include #include "gaueval_kernels.hpp" #include "gaueval_angular_cartesian.hpp" -#include "gaueval_angular_spherical.hpp" #include "gaueval_angular_spherical_unnorm.hpp" -namespace gpgaueval { +namespace GauXC { __global__ -void gaueval_device_cartesian_kernel( - const size_t nshells, - const size_t nbf, - const size_t npts, - const StaticShell* shells_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device +void gaueval_device_kernel( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device ) { const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; - if( tid_x< nshells and tid_y < npts ) { + if( tid_x < npts and tid_y < nshells ) { - const size_t ish = tid_x; - const size_t ipt = tid_y; + const size_t ipt = tid_x; + const size_t ish = tid_y; const size_t ibf = offs_device[ish]; @@ -50,7 +48,12 @@ void gaueval_device_cartesian_kernel( tmp += coeff[i] * std::exp( - alpha[i] * rsq ); double * bf_eval = eval_device + ibf + ipt*nbf; - gaueval_cartesian_angular( shell.l, tmp, xc, yc, zc, bf_eval ); + + const bool do_sph = shell.pure; + if( do_sph ) + gaueval_spherical_unnorm_angular( shell.l, tmp, xc, yc, zc, bf_eval ); + else + gaueval_cartesian_angular( shell.l, tmp, xc, yc, zc, bf_eval ); } @@ -59,26 +62,26 @@ void gaueval_device_cartesian_kernel( __global__ -void gaueval_device_cartesian_kernel_deriv1( - const size_t nshells, - const size_t nbf, - const size_t npts, - const StaticShell* shells_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device, - double* deval_device_x, - double* deval_device_y, - double* deval_device_z +void gaueval_device_kernel_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device, + double* deval_device_x, + double* deval_device_y, + double* deval_device_z ) { const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; - if( tid_x< nshells and tid_y < npts ) { + if( tid_x < npts and tid_y < nshells ) { - const size_t ish = tid_x; - const size_t ipt = tid_y; + const size_t ipt = tid_x; + const size_t ish = tid_y; const size_t ibf = offs_device[ish]; @@ -118,7 +121,15 @@ void gaueval_device_cartesian_kernel_deriv1( double * dy_eval = deval_device_y + ibf + ipt*nbf; double * dz_eval = deval_device_z + ibf + ipt*nbf; - gaueval_cartesian_angular_deriv1( shell.l, tmp, tmp_x, tmp_y, tmp_z, xc, yc, zc, bf_eval, dx_eval, dy_eval, dz_eval ); + const bool do_sph = shell.pure; + if( do_sph ) + gaueval_spherical_unnorm_angular_deriv1( shell.l, tmp, tmp_x, tmp_y, tmp_z, + xc, yc, zc, bf_eval, dx_eval, + dy_eval, dz_eval ); + else + gaueval_cartesian_angular_deriv1( shell.l, tmp, tmp_x, tmp_y, tmp_z, + xc, yc, zc, bf_eval, dx_eval, + dy_eval, dz_eval ); } diff --git a/src/cuda_integrator/collocation_generator/gaueval_kernels.hpp b/src/cuda_integrator/collocation_generator/gaueval_kernels.hpp new file mode 100644 index 000000000..a840d79de --- /dev/null +++ b/src/cuda_integrator/collocation_generator/gaueval_kernels.hpp @@ -0,0 +1,30 @@ +#include + +namespace GauXC { + +__global__ +void gaueval_device_kernel( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device +); + +__global__ +void gaueval_device_kernel_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device, + double* deval_device_x, + double* deval_device_y, + double* deval_device_z +); + +} diff --git a/src/cuda_integrator/collocation_generator/gaueval_kernels_spherical.cu b/src/cuda_integrator/collocation_generator/gaueval_kernels_spherical.cu deleted file mode 100644 index 27cc813ec..000000000 --- a/src/cuda_integrator/collocation_generator/gaueval_kernels_spherical.cu +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include -#include - -#include "gaueval_kernels.hpp" -#include "gaueval_angular_cartesian.hpp" -#include "gaueval_angular_spherical.hpp" -#include "gaueval_angular_spherical_unnorm.hpp" - -namespace gpgaueval { - -__global__ -void gaueval_device_spherical_kernel( - const size_t nshells, - const size_t nbf, - const size_t npts, - const StaticShell* shells_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device -) { - - const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; - const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; - - if( tid_x< nshells and tid_y < npts ) { - - const size_t ish = tid_x; - const size_t ipt = tid_y; - - const size_t ibf = offs_device[ish]; - - const auto& shell = shells_device[ish]; - const auto* pt = pts_device + 3*ipt; - - - const auto* O = device::array_data( shell.O ); - const auto* alpha = device::array_data( shell.alpha ); - const auto* coeff = device::array_data( shell.coeff ); - - const double xc = pt[0] - O[0]; - const double yc = pt[1] - O[1]; - const double zc = pt[2] - O[2]; - - const double rsq = xc*xc + yc*yc + zc*zc; - - const size_t nprim = shell.nprim; - double tmp = 0.; - for( size_t i = 0; i < nprim; ++i ) - tmp += coeff[i] * std::exp( - alpha[i] * rsq ); - - double * bf_eval = eval_device + ibf + ipt*nbf; - gaueval_spherical_angular( shell.l, tmp, xc, yc, zc, bf_eval ); - - } - -} - - - -__global__ -void gaueval_device_spherical_kernel_deriv1( - const size_t nshells, - const size_t nbf, - const size_t npts, - const StaticShell* shells_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device, - double* deval_device_x, - double* deval_device_y, - double* deval_device_z -) { - - const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; - const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; - - if( tid_x< nshells and tid_y < npts ) { - - const size_t ish = tid_x; - const size_t ipt = tid_y; - - const size_t ibf = offs_device[ish]; - - const auto& shell = shells_device[ish]; - const auto* pt = pts_device + 3*ipt; - - - const auto* O = device::array_data( shell.O ); - const auto* alpha = device::array_data( shell.alpha ); - const auto* coeff = device::array_data( shell.coeff ); - - const double xc = pt[0] - O[0]; - const double yc = pt[1] - O[1]; - const double zc = pt[2] - O[2]; - - const double rsq = xc*xc + yc*yc + zc*zc; - - const size_t nprim = shell.nprim; - double tmp = 0.; - double tmp_x = 0., tmp_y = 0., tmp_z = 0.; - for( size_t i = 0; i < nprim; ++i ) { - - const double a = alpha[i]; - const double e = coeff[i] * std::exp( - a * rsq ); - - const double ae = 2. * a * e; - - tmp += e; - tmp_x -= ae * xc; - tmp_y -= ae * yc; - tmp_z -= ae * zc; - - } - - double * bf_eval = eval_device + ibf + ipt*nbf; - double * dx_eval = deval_device_x + ibf + ipt*nbf; - double * dy_eval = deval_device_y + ibf + ipt*nbf; - double * dz_eval = deval_device_z + ibf + ipt*nbf; - - gaueval_spherical_angular_deriv1( shell.l, tmp, tmp_x, tmp_y, tmp_z, xc, yc, zc, bf_eval, dx_eval, dy_eval, dz_eval ); - - } - - -} - - -} diff --git a/src/cuda_integrator/collocation_generator/gaueval_kernels_spherical_unnorm.cu b/src/cuda_integrator/collocation_generator/gaueval_kernels_spherical_unnorm.cu deleted file mode 100644 index 0fc4ae190..000000000 --- a/src/cuda_integrator/collocation_generator/gaueval_kernels_spherical_unnorm.cu +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include -#include - -#include "gaueval_kernels.hpp" -#include "gaueval_angular_cartesian.hpp" -#include "gaueval_angular_spherical.hpp" -#include "gaueval_angular_spherical_unnorm.hpp" - -namespace gpgaueval { - -__global__ -void gaueval_device_spherical_unnorm_kernel( - const size_t nshells, - const size_t nbf, - const size_t npts, - const StaticShell* shells_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device -) { - - const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; - const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; - - if( tid_x< nshells and tid_y < npts ) { - - const size_t ish = tid_x; - const size_t ipt = tid_y; - - const size_t ibf = offs_device[ish]; - - const auto& shell = shells_device[ish]; - const auto* pt = pts_device + 3*ipt; - - - const auto* O = device::array_data( shell.O ); - const auto* alpha = device::array_data( shell.alpha ); - const auto* coeff = device::array_data( shell.coeff ); - - const double xc = pt[0] - O[0]; - const double yc = pt[1] - O[1]; - const double zc = pt[2] - O[2]; - - const double rsq = xc*xc + yc*yc + zc*zc; - - const size_t nprim = shell.nprim; - double tmp = 0.; - for( size_t i = 0; i < nprim; ++i ) - tmp += coeff[i] * std::exp( - alpha[i] * rsq ); - - double * bf_eval = eval_device + ibf + ipt*nbf; - gaueval_spherical_unnorm_angular( shell.l, tmp, xc, yc, zc, bf_eval ); - - } - -} - - - -__global__ -void gaueval_device_spherical_unnorm_kernel_deriv1( - const size_t nshells, - const size_t nbf, - const size_t npts, - const StaticShell* shells_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device, - double* deval_device_x, - double* deval_device_y, - double* deval_device_z -) { - - const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; - const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; - - if( tid_x< nshells and tid_y < npts ) { - - const size_t ish = tid_x; - const size_t ipt = tid_y; - - const size_t ibf = offs_device[ish]; - - const auto& shell = shells_device[ish]; - const auto* pt = pts_device + 3*ipt; - - - const auto* O = device::array_data( shell.O ); - const auto* alpha = device::array_data( shell.alpha ); - const auto* coeff = device::array_data( shell.coeff ); - - const double xc = pt[0] - O[0]; - const double yc = pt[1] - O[1]; - const double zc = pt[2] - O[2]; - - const double rsq = xc*xc + yc*yc + zc*zc; - - const size_t nprim = shell.nprim; - double tmp = 0.; - double tmp_x = 0., tmp_y = 0., tmp_z = 0.; - for( size_t i = 0; i < nprim; ++i ) { - - const double a = alpha[i]; - const double e = coeff[i] * std::exp( - a * rsq ); - - const double ae = 2. * a * e; - - tmp += e; - tmp_x -= ae * xc; - tmp_y -= ae * yc; - tmp_z -= ae * zc; - - } - - double * bf_eval = eval_device + ibf + ipt*nbf; - double * dx_eval = deval_device_x + ibf + ipt*nbf; - double * dy_eval = deval_device_y + ibf + ipt*nbf; - double * dz_eval = deval_device_z + ibf + ipt*nbf; - - gaueval_spherical_unnorm_angular_deriv1( shell.l, tmp, tmp_x, tmp_y, tmp_z, xc, yc, zc, bf_eval, dx_eval, dy_eval, dz_eval ); - - } - - -} - - -} diff --git a/src/cuda_integrator/collocation_generator/gaueval_radial.hpp b/src/cuda_integrator/collocation_generator/gaueval_radial.hpp new file mode 100644 index 000000000..39d5f6d37 --- /dev/null +++ b/src/cuda_integrator/collocation_generator/gaueval_radial.hpp @@ -0,0 +1,80 @@ +#include +#include + +#include + + +namespace GauXC { + +__inline__ __device__ void gaueval_device_radial_eval( + const Shell& shell, + const double* pt, + double* eval_device +) { + + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); + + const double xc = pt[0] - O[0]; + const double yc = pt[1] - O[1]; + const double zc = pt[2] - O[2]; + + const double rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim; + double tmp = 0.; + for( size_t i = 0; i < nprim; ++i ) + tmp += coeff[i] * std::exp( - alpha[i] * rsq ); + + *eval_device = tmp; + +} + + + +__inline__ __device__void gaueval_device_radial_eval_deriv1( + const Shell& shell, + const double* pt, + double* eval_device, + double* deval_device_x, + double* deval_device_y, + double* deval_device_z +) { + + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); + + const double xc = pt[0] - O[0]; + const double yc = pt[1] - O[1]; + const double zc = pt[2] - O[2]; + + const double rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim; + double tmp = 0.; + double tmp_x = 0., tmp_y = 0., tmp_z = 0.; + for( size_t i = 0; i < nprim; ++i ) { + + const double a = alpha[i]; + const double e = coeff[i] * std::exp( - a * rsq ); + + const double ae = 2. * a * e; + + tmp += e; + tmp_x -= ae * xc; + tmp_y -= ae * yc; + tmp_z -= ae * zc; + + } + + *eval_device = tmp; + *deval_device_x = tmp_x; + *deval_device_y = tmp_y; + *deval_device_z = tmp_z; + +} + +} + diff --git a/src/cuda_integrator/collocation_generator/__init__.py b/src/cuda_integrator/collocation_generator/scripts/__init__.py similarity index 100% rename from src/cuda_integrator/collocation_generator/__init__.py rename to src/cuda_integrator/collocation_generator/scripts/__init__.py diff --git a/src/cuda_integrator/collocation_generator/gaueval_angular.py b/src/cuda_integrator/collocation_generator/scripts/gaueval_angular.py similarity index 100% rename from src/cuda_integrator/collocation_generator/gaueval_angular.py rename to src/cuda_integrator/collocation_generator/scripts/gaueval_angular.py diff --git a/src/cuda_integrator/collocation_generator/generate_gaueval_angular_eval.py b/src/cuda_integrator/collocation_generator/scripts/generate_gaueval_angular_eval.py similarity index 56% rename from src/cuda_integrator/collocation_generator/generate_gaueval_angular_eval.py rename to src/cuda_integrator/collocation_generator/scripts/generate_gaueval_angular_eval.py index 9d07c5489..56aa2e73d 100644 --- a/src/cuda_integrator/collocation_generator/generate_gaueval_angular_eval.py +++ b/src/cuda_integrator/collocation_generator/scripts/generate_gaueval_angular_eval.py @@ -7,8 +7,8 @@ L_max = 6 -sphr_bf_body = [] -sphr_bf_d1_body = [] +#sphr_bf_body = [] +#sphr_bf_d1_body = [] sphr_unnorm_bf_body = [] sphr_unnorm_bf_d1_body = [] @@ -20,31 +20,31 @@ for L in range( L_max + 1 ): print( "Processing L = {} ...".format(L) ) - sphr_ang = generate_spherical_angular( L, False ) + #sphr_ang = generate_spherical_angular( L, False ) sphr_unnorm_ang = generate_spherical_angular( L, True ) cart_ang = generate_cartesian_angular( generate_cartesian_ls( L ) ) - sa, sa_x, sa_y, sa_z = generate_eval_lines( L, sphr_ang ) + #sa, sa_x, sa_y, sa_z = generate_eval_lines( L, sphr_ang ) sna, sna_x, sna_y, sna_z = generate_eval_lines( L, sphr_unnorm_ang ) ca, ca_x, ca_y, ca_z = generate_eval_lines( L, cart_ang ) - sphr_bf_body.append( "\n ".join(sa) ) + #sphr_bf_body.append( "\n ".join(sa) ) sphr_unnorm_bf_body.append( "\n ".join(sna) ) cart_bf_body.append( "\n ".join(ca) ) - s_d1 = "\n\n ".join(["\n ".join( sa_x ), "\n ".join(sa_y), "\n ".join(sa_z)]) + #s_d1 = "\n\n ".join(["\n ".join( sa_x ), "\n ".join(sa_y), "\n ".join(sa_z)]) sn_d1 = "\n\n ".join(["\n ".join( sna_x ), "\n ".join(sna_y), "\n ".join(sna_z)]) c_d1 = "\n\n ".join(["\n ".join( ca_x ), "\n ".join(ca_y), "\n ".join(ca_z)]) - sphr_bf_d1_body.append( s_d1 ) + #sphr_bf_d1_body.append( s_d1 ) sphr_unnorm_bf_d1_body.append( sn_d1 ) cart_bf_d1_body.append( c_d1 ) -template_fname = 'gaueval_angular_template.hpp' +template_fname = 'templates/gaueval_angular_template.hpp' -sphr_var_dict = { 'L_max' : L_max, 'body' : sphr_bf_body, 'body_d1' : sphr_bf_d1_body, 'name' : 'spherical' } +#sphr_var_dict = { 'L_max' : L_max, 'body' : sphr_bf_body, 'body_d1' : sphr_bf_d1_body, 'name' : 'spherical' } sphr_unnorm_var_dict = { 'L_max' : L_max, 'body' : sphr_unnorm_bf_body, 'body_d1' : sphr_unnorm_bf_d1_body, 'name' : 'spherical_unnorm' } cart_var_dict = { 'L_max' : L_max, 'body' : cart_bf_body, 'body_d1' : cart_bf_d1_body, 'name' : 'cartesian' } @@ -53,15 +53,15 @@ sys.stdout = cart_expand = StringIO() expander.expandFile( template_fname, external_definitions=cart_var_dict, auto_indent=True ) -sys.stdout = sphr_expand = StringIO() -expander.expandFile( template_fname, external_definitions=sphr_var_dict, auto_indent=True ) +#sys.stdout = sphr_expand = StringIO() +#expander.expandFile( template_fname, external_definitions=sphr_var_dict, auto_indent=True ) sys.stdout = sphr_unnorm_expand = StringIO() expander.expandFile( template_fname, external_definitions=sphr_unnorm_var_dict, auto_indent=True ) sys.stdout = old_sys_out cart_expand = cart_expand.getvalue() -sphr_expand = sphr_expand.getvalue() +#sphr_expand = sphr_expand.getvalue() sphr_unnorm_expand = sphr_unnorm_expand.getvalue() @@ -71,7 +71,8 @@ # Sqrts sqrt_regex = "sqrt\([0-9]+\)" -sqrt_finds = re.findall( sqrt_regex, "\n".join([cart_expand,sphr_expand,sphr_unnorm_expand]) ) +#sqrt_finds = re.findall( sqrt_regex, "\n".join([cart_expand,sphr_expand,sphr_unnorm_expand]) ) +sqrt_finds = re.findall( sqrt_regex, "\n".join([cart_expand,sphr_unnorm_expand]) ) sqrt_finds = list(set(sqrt_finds)) @@ -80,7 +81,7 @@ new_str = 'sqrt_' + arg cart_expand = cart_expand.replace( x, new_str ) - sphr_expand = sphr_expand.replace( x, new_str ) + #sphr_expand = sphr_expand.replace( x, new_str ) sphr_unnorm_expand = sphr_unnorm_expand.replace( x, new_str ) new_str = "constexpr double " + new_str + " = " + str( math.sqrt(int(arg)) ) + ";" @@ -89,7 +90,7 @@ old_sys_out = sys.stdout sys.stdout = constant_expand = StringIO() -expander.expandFile( 'gaueval_device_constants_template.hpp', +expander.expandFile( 'templates/gaueval_device_constants_template.hpp', external_definitions={ "const_lines" : constant_lines } ) sys.stdout = old_sys_out @@ -98,17 +99,17 @@ cart_header_fname = "gaueval_angular_cartesian.hpp" -sphr_header_fname = "gaueval_angular_spherical.hpp" +#sphr_header_fname = "gaueval_angular_spherical.hpp" sphr_unnorm_header_fname = "gaueval_angular_spherical_unnorm.hpp" cons_header_fname = "gaueval_device_constants.hpp" cart_header_file = open( cart_header_fname, 'w' ) -sphr_header_file = open( sphr_header_fname, 'w' ) +#sphr_header_file = open( sphr_header_fname, 'w' ) sphr_unnorm_header_file = open( sphr_unnorm_header_fname, 'w' ) cons_header_file = open( cons_header_fname, 'w' ) cart_header_file.write( cart_expand ) -sphr_header_file.write( sphr_expand ) +#sphr_header_file.write( sphr_expand ) sphr_unnorm_header_file.write( sphr_unnorm_expand ) cons_header_file.write( constant_expand ) @@ -122,26 +123,26 @@ # Generate Kernel Driver -old_sys_out = sys.stdout - -sys.stdout = gaueval_cartesian_kernel_expand = StringIO() -expander.expandFile( 'gaueval_kernels_template.cu', external_definitions={ 'ang_name' : 'cartesian' } ) - -sys.stdout = gaueval_spherical_kernel_expand = StringIO() -expander.expandFile( 'gaueval_kernels_template.cu', external_definitions={ 'ang_name' : 'spherical' } ) - -sys.stdout = gaueval_spherical_unnorm_kernel_expand = StringIO() -expander.expandFile( 'gaueval_kernels_template.cu', external_definitions={ 'ang_name' : 'spherical_unnorm' } ) - -sys.stdout = old_sys_out - -gaueval_cartesian_kernel_expand = gaueval_cartesian_kernel_expand.getvalue() -gaueval_spherical_kernel_expand = gaueval_spherical_kernel_expand.getvalue() -gaueval_spherical_unnorm_kernel_expand = gaueval_spherical_unnorm_kernel_expand.getvalue() - -with open( 'gaueval_kernels_cartesian.cu', 'w' ) as f: - f.write( gaueval_cartesian_kernel_expand ) -with open( 'gaueval_kernels_spherical.cu', 'w' ) as f: - f.write( gaueval_spherical_kernel_expand ) -with open( 'gaueval_kernels_spherical_unnorm.cu', 'w' ) as f: - f.write( gaueval_spherical_unnorm_kernel_expand ) +#old_sys_out = sys.stdout + +#sys.stdout = gaueval_cartesian_kernel_expand = StringIO() +#expander.expandFile( 'gaueval_kernels_template.cu', external_definitions={ 'ang_name' : 'cartesian' } ) +# +#sys.stdout = gaueval_spherical_kernel_expand = StringIO() +#expander.expandFile( 'gaueval_kernels_template.cu', external_definitions={ 'ang_name' : 'spherical' } ) +# +#sys.stdout = gaueval_spherical_unnorm_kernel_expand = StringIO() +#expander.expandFile( 'gaueval_kernels_template.cu', external_definitions={ 'ang_name' : 'spherical_unnorm' } ) +# +#sys.stdout = old_sys_out +# +#gaueval_cartesian_kernel_expand = gaueval_cartesian_kernel_expand.getvalue() +#gaueval_spherical_kernel_expand = gaueval_spherical_kernel_expand.getvalue() +#gaueval_spherical_unnorm_kernel_expand = gaueval_spherical_unnorm_kernel_expand.getvalue() +# +#with open( 'gaueval_kernels_cartesian.cu', 'w' ) as f: +# f.write( gaueval_cartesian_kernel_expand ) +#with open( 'gaueval_kernels_spherical.cu', 'w' ) as f: +# f.write( gaueval_spherical_kernel_expand ) +#with open( 'gaueval_kernels_spherical_unnorm.cu', 'w' ) as f: +# f.write( gaueval_spherical_unnorm_kernel_expand ) diff --git a/src/cuda_integrator/collocation_generator/gaueval_angular_template.hpp b/src/cuda_integrator/collocation_generator/templates/gaueval_angular_template.hpp similarity index 90% rename from src/cuda_integrator/collocation_generator/gaueval_angular_template.hpp rename to src/cuda_integrator/collocation_generator/templates/gaueval_angular_template.hpp index afa035c87..e68c6c516 100644 --- a/src/cuda_integrator/collocation_generator/gaueval_angular_template.hpp +++ b/src/cuda_integrator/collocation_generator/templates/gaueval_angular_template.hpp @@ -49,27 +49,19 @@ GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular( double* eval ) { -//switch( l ) { - $for( L in range(L_max + 1) )\ - //case $(L): $if( L == 0 )\ if( l == $(L) ) { $else\ } else if( l == $(L) ) { $endif\ gaueval_$(name)_angular_$(L)( bf, x, y, z, eval ); - //break; $endfor\ - //default: } else { assert( false && "L < L_MAX" ); - //break; } -//} // switch(l) - } // gaueval_$(name)_angular @@ -88,10 +80,8 @@ GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_deriv1( double* eval_z ) { -//switch( l ) { $for( L in range(L_max + 1) )\ -// case $(L): $if( L == 0 )\ if( l == $(L) ) { $else\ @@ -99,17 +89,12 @@ GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_deriv1( $endif\ gaueval_$(name)_angular_$(L)( bf, x, y, z, eval ); gaueval_$(name)_angular_$(L)_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); -// break; $endfor\ -// default: } else { assert( false && "L < L_MAX" ); -// break; } -//} // switch(l) - } // gaueval_$(name)_angular_deriv1 diff --git a/src/cuda_integrator/collocation_generator/gaueval_device_constants_template.hpp b/src/cuda_integrator/collocation_generator/templates/gaueval_device_constants_template.hpp similarity index 100% rename from src/cuda_integrator/collocation_generator/gaueval_device_constants_template.hpp rename to src/cuda_integrator/collocation_generator/templates/gaueval_device_constants_template.hpp From 3001e29b4da1e3ba505f1c84db8add1ddd9ab82c Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Tue, 11 Aug 2020 14:54:12 -0700 Subject: [PATCH 03/27] [WIP] Added back minimal pregenerated files --- .../deprecated/gaueval_kernels_template.cu | 0 .../deprecated/generate_bfeval.py | 0 .../collocation/gaueval_angular_cartesian.hpp | 640 ++++++++++++++++++ .../gaueval_angular_spherical_unnorm.hpp | 500 ++++++++++++++ .../collocation/gaueval_device_constants.hpp | 20 + .../gaueval_kernels.cu | 0 .../gaueval_kernels.hpp | 0 .../gaueval_radial.hpp | 0 .../scripts/__init__.py | 0 .../scripts/gaueval_angular.py | 0 .../scripts/generate_gaueval_angular_eval.py | 0 .../templates/gaueval_angular_template.hpp | 0 .../gaueval_device_constants_template.hpp | 0 .../gauxc-cuda_integrator.cmake | 4 +- 14 files changed, 1162 insertions(+), 2 deletions(-) rename src/cuda_integrator/{collocation_generator => collocation}/deprecated/gaueval_kernels_template.cu (100%) rename src/cuda_integrator/{collocation_generator => collocation}/deprecated/generate_bfeval.py (100%) create mode 100644 src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp create mode 100644 src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp create mode 100644 src/cuda_integrator/collocation/gaueval_device_constants.hpp rename src/cuda_integrator/{collocation_generator => collocation}/gaueval_kernels.cu (100%) rename src/cuda_integrator/{collocation_generator => collocation}/gaueval_kernels.hpp (100%) rename src/cuda_integrator/{collocation_generator => collocation}/gaueval_radial.hpp (100%) rename src/cuda_integrator/{collocation_generator => collocation}/scripts/__init__.py (100%) rename src/cuda_integrator/{collocation_generator => collocation}/scripts/gaueval_angular.py (100%) rename src/cuda_integrator/{collocation_generator => collocation}/scripts/generate_gaueval_angular_eval.py (100%) rename src/cuda_integrator/{collocation_generator => collocation}/templates/gaueval_angular_template.hpp (100%) rename src/cuda_integrator/{collocation_generator => collocation}/templates/gaueval_device_constants_template.hpp (100%) diff --git a/src/cuda_integrator/collocation_generator/deprecated/gaueval_kernels_template.cu b/src/cuda_integrator/collocation/deprecated/gaueval_kernels_template.cu similarity index 100% rename from src/cuda_integrator/collocation_generator/deprecated/gaueval_kernels_template.cu rename to src/cuda_integrator/collocation/deprecated/gaueval_kernels_template.cu diff --git a/src/cuda_integrator/collocation_generator/deprecated/generate_bfeval.py b/src/cuda_integrator/collocation/deprecated/generate_bfeval.py similarity index 100% rename from src/cuda_integrator/collocation_generator/deprecated/generate_bfeval.py rename to src/cuda_integrator/collocation/deprecated/generate_bfeval.py diff --git a/src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp b/src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp new file mode 100644 index 000000000..ebbd920f6 --- /dev/null +++ b/src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp @@ -0,0 +1,640 @@ +#pragma once +#include "gaueval_device_constants.hpp" +#include + +#ifndef GPGAUEVAL_INLINE +# define GPGAUEVAL_INLINE __inline__ +#endif + +namespace gpgaueval { + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_0( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_0_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = bf_x; + + eval_y[0] = bf_y; + + eval_z[0] = bf_z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_1( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x; + eval[1] = bf*y; + eval[2] = bf*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_1_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = bf + bf_x*x; + eval_x[1] = bf_x*y; + eval_x[2] = bf_x*z; + + eval_y[0] = bf_y*x; + eval_y[1] = bf + bf_y*y; + eval_y[2] = bf_y*z; + + eval_z[0] = bf_z*x; + eval_z[1] = bf_z*y; + eval_z[2] = bf + bf_z*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_2( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x*x; + eval[1] = bf*x*y; + eval[2] = bf*x*z; + eval[3] = bf*y*y; + eval[4] = bf*y*z; + eval[5] = bf*z*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_2_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = x*(2*bf + bf_x*x); + eval_x[1] = y*(bf + bf_x*x); + eval_x[2] = z*(bf + bf_x*x); + eval_x[3] = bf_x*y*y; + eval_x[4] = bf_x*y*z; + eval_x[5] = bf_x*z*z; + + eval_y[0] = bf_y*x*x; + eval_y[1] = x*(bf + bf_y*y); + eval_y[2] = bf_y*x*z; + eval_y[3] = y*(2*bf + bf_y*y); + eval_y[4] = z*(bf + bf_y*y); + eval_y[5] = bf_y*z*z; + + eval_z[0] = bf_z*x*x; + eval_z[1] = bf_z*x*y; + eval_z[2] = x*(bf + bf_z*z); + eval_z[3] = bf_z*y*y; + eval_z[4] = y*(bf + bf_z*z); + eval_z[5] = z*(2*bf + bf_z*z); + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_3( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x*x*x; + eval[1] = bf*x*x*y; + eval[2] = bf*x*x*z; + eval[3] = bf*x*y*y; + eval[4] = bf*x*y*z; + eval[5] = bf*x*z*z; + eval[6] = bf*y*y*y; + eval[7] = bf*y*y*z; + eval[8] = bf*y*z*z; + eval[9] = bf*z*z*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_3_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = x*x*(3*bf + bf_x*x); + eval_x[1] = x*y*(2*bf + bf_x*x); + eval_x[2] = x*z*(2*bf + bf_x*x); + eval_x[3] = y*y*(bf + bf_x*x); + eval_x[4] = y*z*(bf + bf_x*x); + eval_x[5] = z*z*(bf + bf_x*x); + eval_x[6] = bf_x*y*y*y; + eval_x[7] = bf_x*y*y*z; + eval_x[8] = bf_x*y*z*z; + eval_x[9] = bf_x*z*z*z; + + eval_y[0] = bf_y*x*x*x; + eval_y[1] = x*x*(bf + bf_y*y); + eval_y[2] = bf_y*x*x*z; + eval_y[3] = x*y*(2*bf + bf_y*y); + eval_y[4] = x*z*(bf + bf_y*y); + eval_y[5] = bf_y*x*z*z; + eval_y[6] = y*y*(3*bf + bf_y*y); + eval_y[7] = y*z*(2*bf + bf_y*y); + eval_y[8] = z*z*(bf + bf_y*y); + eval_y[9] = bf_y*z*z*z; + + eval_z[0] = bf_z*x*x*x; + eval_z[1] = bf_z*x*x*y; + eval_z[2] = x*x*(bf + bf_z*z); + eval_z[3] = bf_z*x*y*y; + eval_z[4] = x*y*(bf + bf_z*z); + eval_z[5] = x*z*(2*bf + bf_z*z); + eval_z[6] = bf_z*y*y*y; + eval_z[7] = y*y*(bf + bf_z*z); + eval_z[8] = y*z*(2*bf + bf_z*z); + eval_z[9] = z*z*(3*bf + bf_z*z); + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_4( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x*x*x*x; + eval[1] = bf*x*x*x*y; + eval[2] = bf*x*x*x*z; + eval[3] = bf*x*x*y*y; + eval[4] = bf*x*x*y*z; + eval[5] = bf*x*x*z*z; + eval[6] = bf*x*y*y*y; + eval[7] = bf*x*y*y*z; + eval[8] = bf*x*y*z*z; + eval[9] = bf*x*z*z*z; + eval[10] = bf*y*y*y*y; + eval[11] = bf*y*y*y*z; + eval[12] = bf*y*y*z*z; + eval[13] = bf*y*z*z*z; + eval[14] = bf*z*z*z*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_4_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = x*x*x*(4*bf + bf_x*x); + eval_x[1] = x*x*y*(3*bf + bf_x*x); + eval_x[2] = x*x*z*(3*bf + bf_x*x); + eval_x[3] = x*y*y*(2*bf + bf_x*x); + eval_x[4] = x*y*z*(2*bf + bf_x*x); + eval_x[5] = x*z*z*(2*bf + bf_x*x); + eval_x[6] = y*y*y*(bf + bf_x*x); + eval_x[7] = y*y*z*(bf + bf_x*x); + eval_x[8] = y*z*z*(bf + bf_x*x); + eval_x[9] = z*z*z*(bf + bf_x*x); + eval_x[10] = bf_x*y*y*y*y; + eval_x[11] = bf_x*y*y*y*z; + eval_x[12] = bf_x*y*y*z*z; + eval_x[13] = bf_x*y*z*z*z; + eval_x[14] = bf_x*z*z*z*z; + + eval_y[0] = bf_y*x*x*x*x; + eval_y[1] = x*x*x*(bf + bf_y*y); + eval_y[2] = bf_y*x*x*x*z; + eval_y[3] = x*x*y*(2*bf + bf_y*y); + eval_y[4] = x*x*z*(bf + bf_y*y); + eval_y[5] = bf_y*x*x*z*z; + eval_y[6] = x*y*y*(3*bf + bf_y*y); + eval_y[7] = x*y*z*(2*bf + bf_y*y); + eval_y[8] = x*z*z*(bf + bf_y*y); + eval_y[9] = bf_y*x*z*z*z; + eval_y[10] = y*y*y*(4*bf + bf_y*y); + eval_y[11] = y*y*z*(3*bf + bf_y*y); + eval_y[12] = y*z*z*(2*bf + bf_y*y); + eval_y[13] = z*z*z*(bf + bf_y*y); + eval_y[14] = bf_y*z*z*z*z; + + eval_z[0] = bf_z*x*x*x*x; + eval_z[1] = bf_z*x*x*x*y; + eval_z[2] = x*x*x*(bf + bf_z*z); + eval_z[3] = bf_z*x*x*y*y; + eval_z[4] = x*x*y*(bf + bf_z*z); + eval_z[5] = x*x*z*(2*bf + bf_z*z); + eval_z[6] = bf_z*x*y*y*y; + eval_z[7] = x*y*y*(bf + bf_z*z); + eval_z[8] = x*y*z*(2*bf + bf_z*z); + eval_z[9] = x*z*z*(3*bf + bf_z*z); + eval_z[10] = bf_z*y*y*y*y; + eval_z[11] = y*y*y*(bf + bf_z*z); + eval_z[12] = y*y*z*(2*bf + bf_z*z); + eval_z[13] = y*z*z*(3*bf + bf_z*z); + eval_z[14] = z*z*z*(4*bf + bf_z*z); + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_5( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x*x*x*x*x; + eval[1] = bf*x*x*x*x*y; + eval[2] = bf*x*x*x*x*z; + eval[3] = bf*x*x*x*y*y; + eval[4] = bf*x*x*x*y*z; + eval[5] = bf*x*x*x*z*z; + eval[6] = bf*x*x*y*y*y; + eval[7] = bf*x*x*y*y*z; + eval[8] = bf*x*x*y*z*z; + eval[9] = bf*x*x*z*z*z; + eval[10] = bf*x*y*y*y*y; + eval[11] = bf*x*y*y*y*z; + eval[12] = bf*x*y*y*z*z; + eval[13] = bf*x*y*z*z*z; + eval[14] = bf*x*z*z*z*z; + eval[15] = bf*y*y*y*y*y; + eval[16] = bf*y*y*y*y*z; + eval[17] = bf*y*y*y*z*z; + eval[18] = bf*y*y*z*z*z; + eval[19] = bf*y*z*z*z*z; + eval[20] = bf*z*z*z*z*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_5_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = x*x*x*x*(5*bf + bf_x*x); + eval_x[1] = x*x*x*y*(4*bf + bf_x*x); + eval_x[2] = x*x*x*z*(4*bf + bf_x*x); + eval_x[3] = x*x*y*y*(3*bf + bf_x*x); + eval_x[4] = x*x*y*z*(3*bf + bf_x*x); + eval_x[5] = x*x*z*z*(3*bf + bf_x*x); + eval_x[6] = x*y*y*y*(2*bf + bf_x*x); + eval_x[7] = x*y*y*z*(2*bf + bf_x*x); + eval_x[8] = x*y*z*z*(2*bf + bf_x*x); + eval_x[9] = x*z*z*z*(2*bf + bf_x*x); + eval_x[10] = y*y*y*y*(bf + bf_x*x); + eval_x[11] = y*y*y*z*(bf + bf_x*x); + eval_x[12] = y*y*z*z*(bf + bf_x*x); + eval_x[13] = y*z*z*z*(bf + bf_x*x); + eval_x[14] = z*z*z*z*(bf + bf_x*x); + eval_x[15] = bf_x*y*y*y*y*y; + eval_x[16] = bf_x*y*y*y*y*z; + eval_x[17] = bf_x*y*y*y*z*z; + eval_x[18] = bf_x*y*y*z*z*z; + eval_x[19] = bf_x*y*z*z*z*z; + eval_x[20] = bf_x*z*z*z*z*z; + + eval_y[0] = bf_y*x*x*x*x*x; + eval_y[1] = x*x*x*x*(bf + bf_y*y); + eval_y[2] = bf_y*x*x*x*x*z; + eval_y[3] = x*x*x*y*(2*bf + bf_y*y); + eval_y[4] = x*x*x*z*(bf + bf_y*y); + eval_y[5] = bf_y*x*x*x*z*z; + eval_y[6] = x*x*y*y*(3*bf + bf_y*y); + eval_y[7] = x*x*y*z*(2*bf + bf_y*y); + eval_y[8] = x*x*z*z*(bf + bf_y*y); + eval_y[9] = bf_y*x*x*z*z*z; + eval_y[10] = x*y*y*y*(4*bf + bf_y*y); + eval_y[11] = x*y*y*z*(3*bf + bf_y*y); + eval_y[12] = x*y*z*z*(2*bf + bf_y*y); + eval_y[13] = x*z*z*z*(bf + bf_y*y); + eval_y[14] = bf_y*x*z*z*z*z; + eval_y[15] = y*y*y*y*(5*bf + bf_y*y); + eval_y[16] = y*y*y*z*(4*bf + bf_y*y); + eval_y[17] = y*y*z*z*(3*bf + bf_y*y); + eval_y[18] = y*z*z*z*(2*bf + bf_y*y); + eval_y[19] = z*z*z*z*(bf + bf_y*y); + eval_y[20] = bf_y*z*z*z*z*z; + + eval_z[0] = bf_z*x*x*x*x*x; + eval_z[1] = bf_z*x*x*x*x*y; + eval_z[2] = x*x*x*x*(bf + bf_z*z); + eval_z[3] = bf_z*x*x*x*y*y; + eval_z[4] = x*x*x*y*(bf + bf_z*z); + eval_z[5] = x*x*x*z*(2*bf + bf_z*z); + eval_z[6] = bf_z*x*x*y*y*y; + eval_z[7] = x*x*y*y*(bf + bf_z*z); + eval_z[8] = x*x*y*z*(2*bf + bf_z*z); + eval_z[9] = x*x*z*z*(3*bf + bf_z*z); + eval_z[10] = bf_z*x*y*y*y*y; + eval_z[11] = x*y*y*y*(bf + bf_z*z); + eval_z[12] = x*y*y*z*(2*bf + bf_z*z); + eval_z[13] = x*y*z*z*(3*bf + bf_z*z); + eval_z[14] = x*z*z*z*(4*bf + bf_z*z); + eval_z[15] = bf_z*y*y*y*y*y; + eval_z[16] = y*y*y*y*(bf + bf_z*z); + eval_z[17] = y*y*y*z*(2*bf + bf_z*z); + eval_z[18] = y*y*z*z*(3*bf + bf_z*z); + eval_z[19] = y*z*z*z*(4*bf + bf_z*z); + eval_z[20] = z*z*z*z*(5*bf + bf_z*z); + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_6( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*x*x*x*x*x*x; + eval[1] = bf*x*x*x*x*x*y; + eval[2] = bf*x*x*x*x*x*z; + eval[3] = bf*x*x*x*x*y*y; + eval[4] = bf*x*x*x*x*y*z; + eval[5] = bf*x*x*x*x*z*z; + eval[6] = bf*x*x*x*y*y*y; + eval[7] = bf*x*x*x*y*y*z; + eval[8] = bf*x*x*x*y*z*z; + eval[9] = bf*x*x*x*z*z*z; + eval[10] = bf*x*x*y*y*y*y; + eval[11] = bf*x*x*y*y*y*z; + eval[12] = bf*x*x*y*y*z*z; + eval[13] = bf*x*x*y*z*z*z; + eval[14] = bf*x*x*z*z*z*z; + eval[15] = bf*x*y*y*y*y*y; + eval[16] = bf*x*y*y*y*y*z; + eval[17] = bf*x*y*y*y*z*z; + eval[18] = bf*x*y*y*z*z*z; + eval[19] = bf*x*y*z*z*z*z; + eval[20] = bf*x*z*z*z*z*z; + eval[21] = bf*y*y*y*y*y*y; + eval[22] = bf*y*y*y*y*y*z; + eval[23] = bf*y*y*y*y*z*z; + eval[24] = bf*y*y*y*z*z*z; + eval[25] = bf*y*y*z*z*z*z; + eval[26] = bf*y*z*z*z*z*z; + eval[27] = bf*z*z*z*z*z*z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_6_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = x*x*x*x*x*(6*bf + bf_x*x); + eval_x[1] = x*x*x*x*y*(5*bf + bf_x*x); + eval_x[2] = x*x*x*x*z*(5*bf + bf_x*x); + eval_x[3] = x*x*x*y*y*(4*bf + bf_x*x); + eval_x[4] = x*x*x*y*z*(4*bf + bf_x*x); + eval_x[5] = x*x*x*z*z*(4*bf + bf_x*x); + eval_x[6] = x*x*y*y*y*(3*bf + bf_x*x); + eval_x[7] = x*x*y*y*z*(3*bf + bf_x*x); + eval_x[8] = x*x*y*z*z*(3*bf + bf_x*x); + eval_x[9] = x*x*z*z*z*(3*bf + bf_x*x); + eval_x[10] = x*y*y*y*y*(2*bf + bf_x*x); + eval_x[11] = x*y*y*y*z*(2*bf + bf_x*x); + eval_x[12] = x*y*y*z*z*(2*bf + bf_x*x); + eval_x[13] = x*y*z*z*z*(2*bf + bf_x*x); + eval_x[14] = x*z*z*z*z*(2*bf + bf_x*x); + eval_x[15] = y*y*y*y*y*(bf + bf_x*x); + eval_x[16] = y*y*y*y*z*(bf + bf_x*x); + eval_x[17] = y*y*y*z*z*(bf + bf_x*x); + eval_x[18] = y*y*z*z*z*(bf + bf_x*x); + eval_x[19] = y*z*z*z*z*(bf + bf_x*x); + eval_x[20] = z*z*z*z*z*(bf + bf_x*x); + eval_x[21] = bf_x*y*y*y*y*y*y; + eval_x[22] = bf_x*y*y*y*y*y*z; + eval_x[23] = bf_x*y*y*y*y*z*z; + eval_x[24] = bf_x*y*y*y*z*z*z; + eval_x[25] = bf_x*y*y*z*z*z*z; + eval_x[26] = bf_x*y*z*z*z*z*z; + eval_x[27] = bf_x*z*z*z*z*z*z; + + eval_y[0] = bf_y*x*x*x*x*x*x; + eval_y[1] = x*x*x*x*x*(bf + bf_y*y); + eval_y[2] = bf_y*x*x*x*x*x*z; + eval_y[3] = x*x*x*x*y*(2*bf + bf_y*y); + eval_y[4] = x*x*x*x*z*(bf + bf_y*y); + eval_y[5] = bf_y*x*x*x*x*z*z; + eval_y[6] = x*x*x*y*y*(3*bf + bf_y*y); + eval_y[7] = x*x*x*y*z*(2*bf + bf_y*y); + eval_y[8] = x*x*x*z*z*(bf + bf_y*y); + eval_y[9] = bf_y*x*x*x*z*z*z; + eval_y[10] = x*x*y*y*y*(4*bf + bf_y*y); + eval_y[11] = x*x*y*y*z*(3*bf + bf_y*y); + eval_y[12] = x*x*y*z*z*(2*bf + bf_y*y); + eval_y[13] = x*x*z*z*z*(bf + bf_y*y); + eval_y[14] = bf_y*x*x*z*z*z*z; + eval_y[15] = x*y*y*y*y*(5*bf + bf_y*y); + eval_y[16] = x*y*y*y*z*(4*bf + bf_y*y); + eval_y[17] = x*y*y*z*z*(3*bf + bf_y*y); + eval_y[18] = x*y*z*z*z*(2*bf + bf_y*y); + eval_y[19] = x*z*z*z*z*(bf + bf_y*y); + eval_y[20] = bf_y*x*z*z*z*z*z; + eval_y[21] = y*y*y*y*y*(6*bf + bf_y*y); + eval_y[22] = y*y*y*y*z*(5*bf + bf_y*y); + eval_y[23] = y*y*y*z*z*(4*bf + bf_y*y); + eval_y[24] = y*y*z*z*z*(3*bf + bf_y*y); + eval_y[25] = y*z*z*z*z*(2*bf + bf_y*y); + eval_y[26] = z*z*z*z*z*(bf + bf_y*y); + eval_y[27] = bf_y*z*z*z*z*z*z; + + eval_z[0] = bf_z*x*x*x*x*x*x; + eval_z[1] = bf_z*x*x*x*x*x*y; + eval_z[2] = x*x*x*x*x*(bf + bf_z*z); + eval_z[3] = bf_z*x*x*x*x*y*y; + eval_z[4] = x*x*x*x*y*(bf + bf_z*z); + eval_z[5] = x*x*x*x*z*(2*bf + bf_z*z); + eval_z[6] = bf_z*x*x*x*y*y*y; + eval_z[7] = x*x*x*y*y*(bf + bf_z*z); + eval_z[8] = x*x*x*y*z*(2*bf + bf_z*z); + eval_z[9] = x*x*x*z*z*(3*bf + bf_z*z); + eval_z[10] = bf_z*x*x*y*y*y*y; + eval_z[11] = x*x*y*y*y*(bf + bf_z*z); + eval_z[12] = x*x*y*y*z*(2*bf + bf_z*z); + eval_z[13] = x*x*y*z*z*(3*bf + bf_z*z); + eval_z[14] = x*x*z*z*z*(4*bf + bf_z*z); + eval_z[15] = bf_z*x*y*y*y*y*y; + eval_z[16] = x*y*y*y*y*(bf + bf_z*z); + eval_z[17] = x*y*y*y*z*(2*bf + bf_z*z); + eval_z[18] = x*y*y*z*z*(3*bf + bf_z*z); + eval_z[19] = x*y*z*z*z*(4*bf + bf_z*z); + eval_z[20] = x*z*z*z*z*(5*bf + bf_z*z); + eval_z[21] = bf_z*y*y*y*y*y*y; + eval_z[22] = y*y*y*y*y*(bf + bf_z*z); + eval_z[23] = y*y*y*y*z*(2*bf + bf_z*z); + eval_z[24] = y*y*y*z*z*(3*bf + bf_z*z); + eval_z[25] = y*y*z*z*z*(4*bf + bf_z*z); + eval_z[26] = y*z*z*z*z*(5*bf + bf_z*z); + eval_z[27] = z*z*z*z*z*(6*bf + bf_z*z); + +} + + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular( + const int64_t l, + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + if( l == 0 ) { + gaueval_cartesian_angular_0( bf, x, y, z, eval ); + + } else if( l == 1 ) { + gaueval_cartesian_angular_1( bf, x, y, z, eval ); + + } else if( l == 2 ) { + gaueval_cartesian_angular_2( bf, x, y, z, eval ); + + } else if( l == 3 ) { + gaueval_cartesian_angular_3( bf, x, y, z, eval ); + + } else if( l == 4 ) { + gaueval_cartesian_angular_4( bf, x, y, z, eval ); + + } else if( l == 5 ) { + gaueval_cartesian_angular_5( bf, x, y, z, eval ); + + } else if( l == 6 ) { + gaueval_cartesian_angular_6( bf, x, y, z, eval ); + + } else { + assert( false && "L < L_MAX" ); + } + +} // gaueval_cartesian_angular + + +GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_deriv1( + const int64_t l, + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval, + double* eval_x, + double* eval_y, + double* eval_z +) { + + + if( l == 0 ) { + gaueval_cartesian_angular_0( bf, x, y, z, eval ); + gaueval_cartesian_angular_0_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else if( l == 1 ) { + gaueval_cartesian_angular_1( bf, x, y, z, eval ); + gaueval_cartesian_angular_1_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else if( l == 2 ) { + gaueval_cartesian_angular_2( bf, x, y, z, eval ); + gaueval_cartesian_angular_2_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else if( l == 3 ) { + gaueval_cartesian_angular_3( bf, x, y, z, eval ); + gaueval_cartesian_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else if( l == 4 ) { + gaueval_cartesian_angular_4( bf, x, y, z, eval ); + gaueval_cartesian_angular_4_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else if( l == 5 ) { + gaueval_cartesian_angular_5( bf, x, y, z, eval ); + gaueval_cartesian_angular_5_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else if( l == 6 ) { + gaueval_cartesian_angular_6( bf, x, y, z, eval ); + gaueval_cartesian_angular_6_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else { + assert( false && "L < L_MAX" ); + } + +} // gaueval_cartesian_angular_deriv1 + + + +} // namespace gpgaueval diff --git a/src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp b/src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp new file mode 100644 index 000000000..88a1aeacb --- /dev/null +++ b/src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp @@ -0,0 +1,500 @@ +#pragma once +#include "gaueval_device_constants.hpp" +#include + +#ifndef GPGAUEVAL_INLINE +# define GPGAUEVAL_INLINE __inline__ +#endif + +namespace gpgaueval { + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_0( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_0_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = bf_x; + + eval_y[0] = bf_y; + + eval_z[0] = bf_z; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_1( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = bf*y; + eval[1] = bf*z; + eval[2] = bf*x; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_1_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = bf_x*y; + eval_x[1] = bf_x*z; + eval_x[2] = bf + bf_x*x; + + eval_y[0] = bf + bf_y*y; + eval_y[1] = bf_y*z; + eval_y[2] = bf_y*x; + + eval_z[0] = bf_z*y; + eval_z[1] = bf + bf_z*z; + eval_z[2] = bf_z*x; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_2( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = sqrt_3*bf*x*y; + eval[1] = sqrt_3*bf*y*z; + eval[2] = bf*(-x*x - y*y + 2*z*z)/2; + eval[3] = sqrt_3*bf*x*z; + eval[4] = sqrt_3*bf*(x*x - y*y)/2; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_2_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = sqrt_3*y*(bf + bf_x*x); + eval_x[1] = sqrt_3*bf_x*y*z; + eval_x[2] = -bf*x - bf_x*(x*x + y*y - 2*z*z)/2; + eval_x[3] = sqrt_3*z*(bf + bf_x*x); + eval_x[4] = sqrt_3*(bf*x + bf_x*(x*x - y*y)/2); + + eval_y[0] = sqrt_3*x*(bf + bf_y*y); + eval_y[1] = sqrt_3*z*(bf + bf_y*y); + eval_y[2] = -bf*y - bf_y*(x*x + y*y - 2*z*z)/2; + eval_y[3] = sqrt_3*bf_y*x*z; + eval_y[4] = sqrt_3*(-bf*y + bf_y*(x*x - y*y)/2); + + eval_z[0] = sqrt_3*bf_z*x*y; + eval_z[1] = sqrt_3*y*(bf + bf_z*z); + eval_z[2] = 2*bf*z - bf_z*(x*x + y*y - 2*z*z)/2; + eval_z[3] = sqrt_3*x*(bf + bf_z*z); + eval_z[4] = sqrt_3*bf_z*(x*x - y*y)/2; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_3( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = sqrt_10*bf*y*(3*x*x - y*y)/4; + eval[1] = sqrt_15*bf*x*y*z; + eval[2] = sqrt_6*bf*y*(-x*x - y*y + 4*z*z)/4; + eval[3] = bf*z*(-3*x*x - 3*y*y + 2*z*z)/2; + eval[4] = sqrt_6*bf*x*(-x*x - y*y + 4*z*z)/4; + eval[5] = sqrt_15*bf*z*(x*x - y*y)/2; + eval[6] = sqrt_10*bf*x*(x*x - 3*y*y)/4; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_3_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = sqrt_10*y*(6*bf*x + bf_x*(3*x*x - y*y))/4; + eval_x[1] = sqrt_15*y*z*(bf + bf_x*x); + eval_x[2] = -sqrt_6*y*(2*bf*x + bf_x*(x*x + y*y - 4*z*z))/4; + eval_x[3] = -z*(6*bf*x + bf_x*(3*x*x + 3*y*y - 2*z*z))/2; + eval_x[4] = -sqrt_6*(bf*(3*x*x + y*y - 4*z*z) + bf_x*x*(x*x + y*y - 4*z*z))/4; + eval_x[5] = sqrt_15*z*(2*bf*x + bf_x*(x*x - y*y))/2; + eval_x[6] = sqrt_10*(3*bf*(x*x - y*y) + bf_x*x*(x*x - 3*y*y))/4; + + eval_y[0] = sqrt_10*(-3*bf*(-x*x + y*y) + bf_y*y*(3*x*x - y*y))/4; + eval_y[1] = sqrt_15*x*z*(bf + bf_y*y); + eval_y[2] = -sqrt_6*(bf*(x*x + 3*y*y - 4*z*z) + bf_y*y*(x*x + y*y - 4*z*z))/4; + eval_y[3] = -z*(6*bf*y + bf_y*(3*x*x + 3*y*y - 2*z*z))/2; + eval_y[4] = -sqrt_6*x*(2*bf*y + bf_y*(x*x + y*y - 4*z*z))/4; + eval_y[5] = sqrt_15*z*(-2*bf*y + bf_y*(x*x - y*y))/2; + eval_y[6] = sqrt_10*x*(-6*bf*y + bf_y*(x*x - 3*y*y))/4; + + eval_z[0] = sqrt_10*bf_z*y*(3*x*x - y*y)/4; + eval_z[1] = sqrt_15*x*y*(bf + bf_z*z); + eval_z[2] = sqrt_6*y*(8*bf*z - bf_z*(x*x + y*y - 4*z*z))/4; + eval_z[3] = -3*bf*(x*x + y*y - 2*z*z)/2 - bf_z*z*(3*x*x + 3*y*y - 2*z*z)/2; + eval_z[4] = sqrt_6*x*(8*bf*z - bf_z*(x*x + y*y - 4*z*z))/4; + eval_z[5] = sqrt_15*(bf + bf_z*z)*(x*x - y*y)/2; + eval_z[6] = sqrt_10*bf_z*x*(x*x - 3*y*y)/4; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_4( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = sqrt_35*bf*x*y*(x*x - y*y)/2; + eval[1] = sqrt_70*bf*y*z*(3*x*x - y*y)/4; + eval[2] = sqrt_5*bf*x*y*(-x*x - y*y + 6*z*z)/2; + eval[3] = sqrt_10*bf*y*z*(-3*x*x - 3*y*y + 4*z*z)/4; + eval[4] = bf*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; + eval[5] = sqrt_10*bf*x*z*(-3*x*x - 3*y*y + 4*z*z)/4; + eval[6] = sqrt_5*bf*(-x*x*x*x + 6*x*x*z*z + y*y*y*y - 6*y*y*z*z)/4; + eval[7] = sqrt_70*bf*x*z*(x*x - 3*y*y)/4; + eval[8] = sqrt_35*bf*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_4_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = sqrt_35*y*(bf*(3*x*x - y*y) + bf_x*x*(x*x - y*y))/2; + eval_x[1] = sqrt_70*y*z*(6*bf*x + bf_x*(3*x*x - y*y))/4; + eval_x[2] = -sqrt_5*y*(bf*(3*x*x + y*y - 6*z*z) + bf_x*x*(x*x + y*y - 6*z*z))/2; + eval_x[3] = -sqrt_10*y*z*(6*bf*x + bf_x*(3*x*x + 3*y*y - 4*z*z))/4; + eval_x[4] = 3*bf*x*(x*x + y*y - 4*z*z)/2 + bf_x*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; + eval_x[5] = -sqrt_10*z*(bf*(9*x*x + 3*y*y - 4*z*z) + bf_x*x*(3*x*x + 3*y*y - 4*z*z))/4; + eval_x[6] = -sqrt_5*(bf*x*(x*x - 3*z*z) + bf_x*(x*x*x*x - 6*x*x*z*z - y*y*y*y + 6*y*y*z*z)/4); + eval_x[7] = sqrt_70*z*(3*bf*(x*x - y*y) + bf_x*x*(x*x - 3*y*y))/4; + eval_x[8] = sqrt_35*(4*bf*x*(x*x - 3*y*y) + bf_x*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; + + eval_y[0] = sqrt_35*x*(-bf*(-x*x + 3*y*y) + bf_y*y*(x*x - y*y))/2; + eval_y[1] = sqrt_70*z*(-3*bf*(-x*x + y*y) + bf_y*y*(3*x*x - y*y))/4; + eval_y[2] = -sqrt_5*x*(bf*(x*x + 3*y*y - 6*z*z) + bf_y*y*(x*x + y*y - 6*z*z))/2; + eval_y[3] = -sqrt_10*z*(bf*(3*x*x + 9*y*y - 4*z*z) + bf_y*y*(3*x*x + 3*y*y - 4*z*z))/4; + eval_y[4] = 3*bf*y*(x*x + y*y - 4*z*z)/2 + bf_y*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; + eval_y[5] = -sqrt_10*x*z*(6*bf*y + bf_y*(3*x*x + 3*y*y - 4*z*z))/4; + eval_y[6] = sqrt_5*(bf*y*(y*y - 3*z*z) - bf_y*(x*x*x*x - 6*x*x*z*z - y*y*y*y + 6*y*y*z*z)/4); + eval_y[7] = sqrt_70*x*z*(-6*bf*y + bf_y*(x*x - 3*y*y))/4; + eval_y[8] = sqrt_35*(-4*bf*y*(3*x*x - y*y) + bf_y*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; + + eval_z[0] = sqrt_35*bf_z*x*y*(x*x - y*y)/2; + eval_z[1] = sqrt_70*y*(bf + bf_z*z)*(3*x*x - y*y)/4; + eval_z[2] = sqrt_5*x*y*(12*bf*z - bf_z*(x*x + y*y - 6*z*z))/2; + eval_z[3] = sqrt_10*y*(3*bf*(-x*x - y*y + 4*z*z) - bf_z*z*(3*x*x + 3*y*y - 4*z*z))/4; + eval_z[4] = -2*bf*z*(3*x*x + 3*y*y - 2*z*z) + bf_z*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; + eval_z[5] = sqrt_10*x*(3*bf*(-x*x - y*y + 4*z*z) - bf_z*z*(3*x*x + 3*y*y - 4*z*z))/4; + eval_z[6] = sqrt_5*(12*bf*z*(x*x - y*y) - bf_z*(x*x*x*x - 6*x*x*z*z - y*y*y*y + 6*y*y*z*z))/4; + eval_z[7] = sqrt_70*x*(bf + bf_z*z)*(x*x - 3*y*y)/4; + eval_z[8] = sqrt_35*bf_z*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_5( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = 3*sqrt_14*bf*y*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; + eval[1] = 3*sqrt_35*bf*x*y*z*(x*x - y*y)/2; + eval[2] = sqrt_70*bf*y*(-3*x*x*x*x - 2*x*x*y*y + 24*x*x*z*z + y*y*y*y - 8*y*y*z*z)/16; + eval[3] = sqrt_105*bf*x*y*z*(-x*x - y*y + 2*z*z)/2; + eval[4] = sqrt_15*bf*y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8; + eval[5] = bf*z*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z)/8; + eval[6] = sqrt_15*bf*x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8; + eval[7] = sqrt_105*bf*z*(-x*x*x*x + 2*x*x*z*z + y*y*y*y - 2*y*y*z*z)/4; + eval[8] = sqrt_70*bf*x*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z)/16; + eval[9] = 3*sqrt_35*bf*z*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; + eval[10] = 3*sqrt_14*bf*x*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_5_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = 3*sqrt_14*y*(20*bf*x*(x*x - y*y) + bf_x*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y))/16; + eval_x[1] = 3*sqrt_35*y*z*(bf*(3*x*x - y*y) + bf_x*x*(x*x - y*y))/2; + eval_x[2] = -sqrt_70*y*(4*bf*x*(3*x*x + y*y - 12*z*z) + bf_x*(3*x*x*x*x + 2*x*x*y*y - 24*x*x*z*z - y*y*y*y + 8*y*y*z*z))/16; + eval_x[3] = -sqrt_105*y*z*(bf*(3*x*x + y*y - 2*z*z) + bf_x*x*(x*x + y*y - 2*z*z))/2; + eval_x[4] = sqrt_15*y*(4*bf*x*(x*x + y*y - 6*z*z) + bf_x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; + eval_x[5] = z*(20*bf*x*(3*x*x + 3*y*y - 4*z*z) + bf_x*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z))/8; + eval_x[6] = sqrt_15*(bf*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + 4*x*x*(x*x + y*y - 6*z*z) + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z) + bf_x*x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; + eval_x[7] = -sqrt_105*z*(4*bf*x*(x*x - z*z) + bf_x*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z))/4; + eval_x[8] = sqrt_70*(bf*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 4*x*x*(-x*x + y*y + 4*z*z) + 3*y*y*y*y - 24*y*y*z*z) + bf_x*x*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z))/16; + eval_x[9] = 3*sqrt_35*z*(4*bf*x*(x*x - 3*y*y) + bf_x*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; + eval_x[10] = 3*sqrt_14*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + bf_x*x*x*x*x*x - 10*bf_x*x*x*x*y*y + 5*bf_x*x*y*y*y*y)/16; + + eval_y[0] = 3*sqrt_14*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + 5*bf_y*x*x*x*x*y - 10*bf_y*x*x*y*y*y + bf_y*y*y*y*y*y)/16; + eval_y[1] = 3*sqrt_35*x*z*(-bf*(-x*x + 3*y*y) + bf_y*y*(x*x - y*y))/2; + eval_y[2] = sqrt_70*(-3*bf*x*x*x*x - 6*bf*x*x*y*y + 24*bf*x*x*z*z + 5*bf*y*y*y*y - 24*bf*y*y*z*z - 3*bf_y*x*x*x*x*y - 2*bf_y*x*x*y*y*y + 24*bf_y*x*x*y*z*z + bf_y*y*y*y*y*y - 8*bf_y*y*y*y*z*z)/16; + eval_y[3] = -sqrt_105*x*z*(bf*(x*x + 3*y*y - 2*z*z) + bf_y*y*(x*x + y*y - 2*z*z))/2; + eval_y[4] = sqrt_15*(bf*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 4*y*y*(x*x + y*y - 6*z*z) + 8*z*z*z*z) + bf_y*y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; + eval_y[5] = z*(20*bf*y*(3*x*x + 3*y*y - 4*z*z) + bf_y*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z))/8; + eval_y[6] = sqrt_15*x*(4*bf*y*(x*x + y*y - 6*z*z) + bf_y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; + eval_y[7] = sqrt_105*z*(4*bf*y*(y*y - z*z) - bf_y*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z))/4; + eval_y[8] = sqrt_70*x*(4*bf*y*(x*x + 3*y*y - 12*z*z) + bf_y*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z))/16; + eval_y[9] = 3*sqrt_35*z*(-4*bf*y*(3*x*x - y*y) + bf_y*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; + eval_y[10] = 3*sqrt_14*x*(-20*bf*y*(x*x - y*y) + bf_y*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y))/16; + + eval_z[0] = 3*sqrt_14*bf_z*y*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; + eval_z[1] = 3*sqrt_35*x*y*(bf + bf_z*z)*(x*x - y*y)/2; + eval_z[2] = sqrt_70*y*(16*bf*z*(3*x*x - y*y) - bf_z*(3*x*x*x*x + 2*x*x*y*y - 24*x*x*z*z - y*y*y*y + 8*y*y*z*z))/16; + eval_z[3] = sqrt_105*x*y*(bf*(-x*x - y*y + 6*z*z) - bf_z*z*(x*x + y*y - 2*z*z))/2; + eval_z[4] = sqrt_15*y*(-8*bf*z*(3*x*x + 3*y*y - 4*z*z) + bf_z*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; + eval_z[5] = bf*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z + z*z*(-80*x*x - 80*y*y + 32*z*z))/8 + bf_z*z*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z)/8; + eval_z[6] = sqrt_15*x*(-8*bf*z*(3*x*x + 3*y*y - 4*z*z) + bf_z*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; + eval_z[7] = sqrt_105*(bf*(-x*x*x*x + 2*x*x*z*z + y*y*y*y - 2*y*y*z*z + 4*z*z*(x*x - y*y)) - bf_z*z*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z))/4; + eval_z[8] = sqrt_70*x*(16*bf*z*(x*x - 3*y*y) + bf_z*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z))/16; + eval_z[9] = 3*sqrt_35*(bf + bf_z*z)*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; + eval_z[10] = 3*sqrt_14*bf_z*x*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_6( + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + eval[0] = sqrt_462*bf*x*y*(3*x*x*x*x - 10*x*x*y*y + 3*y*y*y*y)/16; + eval[1] = 3*sqrt_154*bf*y*z*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; + eval[2] = 3*sqrt_7*bf*x*y*(-x*x*x*x + 10*x*x*z*z + y*y*y*y - 10*y*y*z*z)/4; + eval[3] = sqrt_210*bf*y*z*(-9*x*x*x*x - 6*x*x*y*y + 24*x*x*z*z + 3*y*y*y*y - 8*y*y*z*z)/16; + eval[4] = sqrt_210*bf*x*y*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z)/16; + eval[5] = sqrt_21*bf*y*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z)/8; + eval[6] = bf*(-5*x*x*x*x*x*x - 15*x*x*x*x*y*y + 90*x*x*x*x*z*z - 15*x*x*y*y*y*y + 180*x*x*y*y*z*z - 120*x*x*z*z*z*z - 5*y*y*y*y*y*y + 90*y*y*y*y*z*z - 120*y*y*z*z*z*z + 16*z*z*z*z*z*z)/16; + eval[7] = sqrt_21*bf*x*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z)/8; + eval[8] = sqrt_210*bf*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z)/32; + eval[9] = sqrt_210*bf*x*z*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z)/16; + eval[10] = 3*sqrt_7*bf*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z)/16; + eval[11] = 3*sqrt_154*bf*x*z*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; + eval[12] = sqrt_462*bf*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y)/32; + +} + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_6_deriv1( + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval_x, + double* eval_y, + double* eval_z +) { + + eval_x[0] = sqrt_462*y*(15*bf*x*x*x*x - 30*bf*x*x*y*y + 3*bf*y*y*y*y + 3*bf_x*x*x*x*x*x - 10*bf_x*x*x*x*y*y + 3*bf_x*x*y*y*y*y)/16; + eval_x[1] = 3*sqrt_154*y*z*(20*bf*x*(x*x - y*y) + bf_x*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y))/16; + eval_x[2] = 3*sqrt_7*y*(-5*bf*x*x*x*x + 30*bf*x*x*z*z + bf*y*y*y*y - 10*bf*y*y*z*z - bf_x*x*x*x*x*x + 10*bf_x*x*x*x*z*z + bf_x*x*y*y*y*y - 10*bf_x*x*y*y*z*z)/4; + eval_x[3] = -sqrt_210*y*z*(12*bf*x*(3*x*x + y*y - 4*z*z) + bf_x*(9*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z - 3*y*y*y*y + 8*y*y*z*z))/16; + eval_x[4] = sqrt_210*y*(bf*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + 4*x*x*(x*x + y*y - 8*z*z) + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z) + bf_x*x*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z))/16; + eval_x[5] = sqrt_21*y*z*(20*bf*x*(x*x + y*y - 2*z*z) + bf_x*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; + eval_x[6] = -15*bf*x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8 - bf_x*(5*x*x*x*x*x*x + 15*x*x*x*x*y*y - 90*x*x*x*x*z*z + 15*x*x*y*y*y*y - 180*x*x*y*y*z*z + 120*x*x*z*z*z*z + 5*y*y*y*y*y*y - 90*y*y*y*y*z*z + 120*y*y*z*z*z*z - 16*z*z*z*z*z*z)/16; + eval_x[7] = sqrt_21*z*(bf*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 20*x*x*(x*x + y*y - 2*z*z) + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z) + bf_x*x*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; + eval_x[8] = sqrt_210*(2*bf*x*(3*x*x*x*x + 2*x*x*y*y - 32*x*x*z*z - y*y*y*y + 16*z*z*z*z) + bf_x*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z))/32; + eval_x[9] = sqrt_210*z*(-15*bf*x*x*x*x + 18*bf*x*x*y*y + 24*bf*x*x*z*z + 9*bf*y*y*y*y - 24*bf*y*y*z*z - 3*bf_x*x*x*x*x*x + 6*bf_x*x*x*x*y*y + 8*bf_x*x*x*x*z*z + 9*bf_x*x*y*y*y*y - 24*bf_x*x*y*y*z*z)/16; + eval_x[10] = 3*sqrt_7*(2*bf*x*(-3*x*x*x*x + 10*x*x*y*y + 20*x*x*z*z + 5*y*y*y*y - 60*y*y*z*z) + bf_x*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z))/16; + eval_x[11] = 3*sqrt_154*z*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + bf_x*x*x*x*x*x - 10*bf_x*x*x*x*y*y + 5*bf_x*x*y*y*y*y)/16; + eval_x[12] = sqrt_462*(6*bf*x*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y) + bf_x*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y))/32; + + eval_y[0] = sqrt_462*x*(3*bf*x*x*x*x - 30*bf*x*x*y*y + 15*bf*y*y*y*y + 3*bf_y*x*x*x*x*y - 10*bf_y*x*x*y*y*y + 3*bf_y*y*y*y*y*y)/16; + eval_y[1] = 3*sqrt_154*z*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + 5*bf_y*x*x*x*x*y - 10*bf_y*x*x*y*y*y + bf_y*y*y*y*y*y)/16; + eval_y[2] = 3*sqrt_7*x*(bf*(-x*x*x*x + 10*x*x*z*z + y*y*y*y - 10*y*y*z*z + 4*y*y*(y*y - 5*z*z)) - bf_y*y*(x*x*x*x - 10*x*x*z*z - y*y*y*y + 10*y*y*z*z))/4; + eval_y[3] = sqrt_210*z*(-9*bf*x*x*x*x - 18*bf*x*x*y*y + 24*bf*x*x*z*z + 15*bf*y*y*y*y - 24*bf*y*y*z*z - 9*bf_y*x*x*x*x*y - 6*bf_y*x*x*y*y*y + 24*bf_y*x*x*y*z*z + 3*bf_y*y*y*y*y*y - 8*bf_y*y*y*y*z*z)/16; + eval_y[4] = sqrt_210*x*(bf*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 4*y*y*(x*x + y*y - 8*z*z) + 16*z*z*z*z) + bf_y*y*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z))/16; + eval_y[5] = sqrt_21*z*(bf*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 20*y*y*(x*x + y*y - 2*z*z) + 8*z*z*z*z) + bf_y*y*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; + eval_y[6] = -15*bf*y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8 - bf_y*(5*x*x*x*x*x*x + 15*x*x*x*x*y*y - 90*x*x*x*x*z*z + 15*x*x*y*y*y*y - 180*x*x*y*y*z*z + 120*x*x*z*z*z*z + 5*y*y*y*y*y*y - 90*y*y*y*y*z*z + 120*y*y*z*z*z*z - 16*z*z*z*z*z*z)/16; + eval_y[7] = sqrt_21*x*z*(20*bf*y*(x*x + y*y - 2*z*z) + bf_y*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; + eval_y[8] = sqrt_210*(-2*bf*y*(-x*x*x*x + 2*x*x*y*y + 3*y*y*y*y - 32*y*y*z*z + 16*z*z*z*z) + bf_y*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z))/32; + eval_y[9] = sqrt_210*x*z*(12*bf*y*(x*x + 3*y*y - 4*z*z) + bf_y*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z))/16; + eval_y[10] = 3*sqrt_7*(2*bf*y*(5*x*x*x*x + 10*x*x*y*y - 60*x*x*z*z - 3*y*y*y*y + 20*y*y*z*z) + bf_y*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z))/16; + eval_y[11] = 3*sqrt_154*x*z*(-20*bf*y*(x*x - y*y) + bf_y*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y))/16; + eval_y[12] = sqrt_462*(-6*bf*y*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y) + bf_y*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y))/32; + + eval_z[0] = sqrt_462*bf_z*x*y*(3*x*x*x*x - 10*x*x*y*y + 3*y*y*y*y)/16; + eval_z[1] = 3*sqrt_154*y*(bf + bf_z*z)*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; + eval_z[2] = 3*sqrt_7*x*y*(20*bf*z*(x*x - y*y) - bf_z*(x*x*x*x - 10*x*x*z*z - y*y*y*y + 10*y*y*z*z))/4; + eval_z[3] = sqrt_210*y*(bf*(-9*x*x*x*x - 6*x*x*y*y + 24*x*x*z*z + 3*y*y*y*y - 8*y*y*z*z + 16*z*z*(3*x*x - y*y)) - bf_z*z*(9*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z - 3*y*y*y*y + 8*y*y*z*z))/16; + eval_z[4] = sqrt_210*x*y*(-32*bf*z*(x*x + y*y - 2*z*z) + bf_z*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z))/16; + eval_z[5] = sqrt_21*y*(-bf*(-5*x*x*x*x - 10*x*x*y*y + 20*x*x*z*z - 5*y*y*y*y + 20*y*y*z*z - 8*z*z*z*z + z*z*(40*x*x + 40*y*y - 32*z*z)) + bf_z*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; + eval_z[6] = 3*bf*z*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z)/4 - bf_z*(5*x*x*x*x*x*x + 15*x*x*x*x*y*y - 90*x*x*x*x*z*z + 15*x*x*y*y*y*y - 180*x*x*y*y*z*z + 120*x*x*z*z*z*z + 5*y*y*y*y*y*y - 90*y*y*y*y*z*z + 120*y*y*z*z*z*z - 16*z*z*z*z*z*z)/16; + eval_z[7] = sqrt_21*x*(-bf*(-5*x*x*x*x - 10*x*x*y*y + 20*x*x*z*z - 5*y*y*y*y + 20*y*y*z*z - 8*z*z*z*z + z*z*(40*x*x + 40*y*y - 32*z*z)) + bf_z*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; + eval_z[8] = sqrt_210*(-bf*z*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z) + bf_z*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z)/32); + eval_z[9] = sqrt_210*x*(bf*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z + 16*z*z*(x*x - 3*y*y)) + bf_z*z*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z))/16; + eval_z[10] = 3*sqrt_7*(20*bf*z*(x*x*x*x - 6*x*x*y*y + y*y*y*y) + bf_z*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z))/16; + eval_z[11] = 3*sqrt_154*x*(bf + bf_z*z)*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; + eval_z[12] = sqrt_462*bf_z*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y)/32; + +} + + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular( + const int64_t l, + const double bf, + const double x, + const double y, + const double z, + double* eval +) { + + if( l == 0 ) { + gaueval_spherical_unnorm_angular_0( bf, x, y, z, eval ); + + } else if( l == 1 ) { + gaueval_spherical_unnorm_angular_1( bf, x, y, z, eval ); + + } else if( l == 2 ) { + gaueval_spherical_unnorm_angular_2( bf, x, y, z, eval ); + + } else if( l == 3 ) { + gaueval_spherical_unnorm_angular_3( bf, x, y, z, eval ); + + } else if( l == 4 ) { + gaueval_spherical_unnorm_angular_4( bf, x, y, z, eval ); + + } else if( l == 5 ) { + gaueval_spherical_unnorm_angular_5( bf, x, y, z, eval ); + + } else if( l == 6 ) { + gaueval_spherical_unnorm_angular_6( bf, x, y, z, eval ); + + } else { + assert( false && "L < L_MAX" ); + } + +} // gaueval_spherical_unnorm_angular + + +GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_deriv1( + const int64_t l, + const double bf, + const double bf_x, + const double bf_y, + const double bf_z, + const double x, + const double y, + const double z, + double* eval, + double* eval_x, + double* eval_y, + double* eval_z +) { + + + if( l == 0 ) { + gaueval_spherical_unnorm_angular_0( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_0_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else if( l == 1 ) { + gaueval_spherical_unnorm_angular_1( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_1_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else if( l == 2 ) { + gaueval_spherical_unnorm_angular_2( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_2_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else if( l == 3 ) { + gaueval_spherical_unnorm_angular_3( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else if( l == 4 ) { + gaueval_spherical_unnorm_angular_4( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_4_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else if( l == 5 ) { + gaueval_spherical_unnorm_angular_5( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_5_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else if( l == 6 ) { + gaueval_spherical_unnorm_angular_6( bf, x, y, z, eval ); + gaueval_spherical_unnorm_angular_6_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + + } else { + assert( false && "L < L_MAX" ); + } + +} // gaueval_spherical_unnorm_angular_deriv1 + + + +} // namespace gpgaueval diff --git a/src/cuda_integrator/collocation/gaueval_device_constants.hpp b/src/cuda_integrator/collocation/gaueval_device_constants.hpp new file mode 100644 index 000000000..fcbeebb95 --- /dev/null +++ b/src/cuda_integrator/collocation/gaueval_device_constants.hpp @@ -0,0 +1,20 @@ +#pragma once + +namespace gpgaueval { + + constexpr double sqrt_10 = 3.1622776601683795; + constexpr double sqrt_14 = 3.7416573867739413; + constexpr double sqrt_154 = 12.409673645990857; + constexpr double sqrt_462 = 21.494185260204677; + constexpr double sqrt_210 = 14.491376746189438; + constexpr double sqrt_21 = 4.58257569495584; + constexpr double sqrt_70 = 8.366600265340756; + constexpr double sqrt_3 = 1.7320508075688772; + constexpr double sqrt_7 = 2.6457513110645907; + constexpr double sqrt_15 = 3.872983346207417; + constexpr double sqrt_5 = 2.23606797749979; + constexpr double sqrt_6 = 2.449489742783178; + constexpr double sqrt_35 = 5.916079783099616; + constexpr double sqrt_105 = 10.246950765959598; + +} // namespace gpgaueval diff --git a/src/cuda_integrator/collocation_generator/gaueval_kernels.cu b/src/cuda_integrator/collocation/gaueval_kernels.cu similarity index 100% rename from src/cuda_integrator/collocation_generator/gaueval_kernels.cu rename to src/cuda_integrator/collocation/gaueval_kernels.cu diff --git a/src/cuda_integrator/collocation_generator/gaueval_kernels.hpp b/src/cuda_integrator/collocation/gaueval_kernels.hpp similarity index 100% rename from src/cuda_integrator/collocation_generator/gaueval_kernels.hpp rename to src/cuda_integrator/collocation/gaueval_kernels.hpp diff --git a/src/cuda_integrator/collocation_generator/gaueval_radial.hpp b/src/cuda_integrator/collocation/gaueval_radial.hpp similarity index 100% rename from src/cuda_integrator/collocation_generator/gaueval_radial.hpp rename to src/cuda_integrator/collocation/gaueval_radial.hpp diff --git a/src/cuda_integrator/collocation_generator/scripts/__init__.py b/src/cuda_integrator/collocation/scripts/__init__.py similarity index 100% rename from src/cuda_integrator/collocation_generator/scripts/__init__.py rename to src/cuda_integrator/collocation/scripts/__init__.py diff --git a/src/cuda_integrator/collocation_generator/scripts/gaueval_angular.py b/src/cuda_integrator/collocation/scripts/gaueval_angular.py similarity index 100% rename from src/cuda_integrator/collocation_generator/scripts/gaueval_angular.py rename to src/cuda_integrator/collocation/scripts/gaueval_angular.py diff --git a/src/cuda_integrator/collocation_generator/scripts/generate_gaueval_angular_eval.py b/src/cuda_integrator/collocation/scripts/generate_gaueval_angular_eval.py similarity index 100% rename from src/cuda_integrator/collocation_generator/scripts/generate_gaueval_angular_eval.py rename to src/cuda_integrator/collocation/scripts/generate_gaueval_angular_eval.py diff --git a/src/cuda_integrator/collocation_generator/templates/gaueval_angular_template.hpp b/src/cuda_integrator/collocation/templates/gaueval_angular_template.hpp similarity index 100% rename from src/cuda_integrator/collocation_generator/templates/gaueval_angular_template.hpp rename to src/cuda_integrator/collocation/templates/gaueval_angular_template.hpp diff --git a/src/cuda_integrator/collocation_generator/templates/gaueval_device_constants_template.hpp b/src/cuda_integrator/collocation/templates/gaueval_device_constants_template.hpp similarity index 100% rename from src/cuda_integrator/collocation_generator/templates/gaueval_device_constants_template.hpp rename to src/cuda_integrator/collocation/templates/gaueval_device_constants_template.hpp diff --git a/src/cuda_integrator/gauxc-cuda_integrator.cmake b/src/cuda_integrator/gauxc-cuda_integrator.cmake index c48c285b8..56a3d0d11 100644 --- a/src/cuda_integrator/gauxc-cuda_integrator.cmake +++ b/src/cuda_integrator/gauxc-cuda_integrator.cmake @@ -1,6 +1,6 @@ add_library( gauxc_device_integrator OBJECT - cuda_integrator/cuda_collocation_kernels.cu - cuda_integrator/cuda_collocation.cxx + cuda_integrator/collocation/gaueval_kernels.cu + #cuda_integrator/collocation/.cxx ) target_compile_features( gauxc_device_integrator PUBLIC cuda_std_14 cxx_std_14 ) target_link_libraries( gauxc_device_integrator PUBLIC ExchCXX::exchcxx ) From d5820a00a5c24e3ab2807a0fe7467f1005c36f6f Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Tue, 11 Aug 2020 18:47:39 -0400 Subject: [PATCH 04/27] Got CUDA collocation kernels to compile --- include/gauxc/named_type.hpp | 8 +- include/gauxc/shell.hpp | 26 +++- .../gauxc/util/contiguous_container_data.hpp | 14 ++ .../deprecated/gaueval_kernels_template.cu | 4 +- .../collocation/deprecated/generate_bfeval.py | 6 +- .../collocation/gaueval_angular_cartesian.hpp | 4 +- .../gaueval_angular_spherical_unnorm.hpp | 4 +- .../collocation/gaueval_device_constants.hpp | 4 +- .../collocation/gaueval_kernels.cu | 128 +++++++++++------- .../collocation/gaueval_kernels.hpp | 36 ++--- .../templates/gaueval_angular_template.hpp | 4 +- .../gaueval_device_constants_template.hpp | 4 +- 12 files changed, 150 insertions(+), 92 deletions(-) create mode 100644 include/gauxc/util/contiguous_container_data.hpp diff --git a/include/gauxc/named_type.hpp b/include/gauxc/named_type.hpp index f952487ad..2670922b9 100644 --- a/include/gauxc/named_type.hpp +++ b/include/gauxc/named_type.hpp @@ -54,7 +54,7 @@ inline bool operator==( ) { return n2 == n1; } template > + typename = std::enable_if_t::value> > inline bool operator==( const NamedType& n1, @@ -62,7 +62,7 @@ inline bool operator==( ) { return n1.get() == T(n2); } template > + typename = std::enable_if_t::value> > inline bool operator==( const U & n1, @@ -93,7 +93,7 @@ inline bool operator!=( ) { return not( n1 == n2 ); } template > + typename = std::enable_if_t::value> > inline bool operator!=( const NamedType& n1, @@ -101,7 +101,7 @@ inline bool operator!=( ) { return not( n1 == n2 ); } template > + typename = std::enable_if_t::value> > inline bool operator!=( const U & n1, diff --git a/include/gauxc/shell.hpp b/include/gauxc/shell.hpp index 04737da4b..3aa914919 100644 --- a/include/gauxc/shell.hpp +++ b/include/gauxc/shell.hpp @@ -4,9 +4,11 @@ #include #include #include +#include #include #include +#include namespace GauXC { @@ -142,12 +144,24 @@ class Shell { inline HOST_DEVICE_ACCESSIBLE int32_t l() const { return l_; } inline HOST_DEVICE_ACCESSIBLE int32_t pure() const { return pure_; } - inline HOST_DEVICE_ACCESSIBLE const F* alpha_data() const { return &alpha_[0]; } - inline HOST_DEVICE_ACCESSIBLE const F* coeff_data() const { return &coeff_[0]; } - inline HOST_DEVICE_ACCESSIBLE const double* O_data() const { return &O_[0]; } - inline HOST_DEVICE_ACCESSIBLE F* alpha_data() { return &alpha_[0]; } - inline HOST_DEVICE_ACCESSIBLE F* coeff_data() { return &coeff_[0]; } - inline HOST_DEVICE_ACCESSIBLE double* O_data() { return &O_[0]; } + inline HOST_DEVICE_ACCESSIBLE const F* alpha_data() const { + return detail::contiguous_data(alpha_); + } + inline HOST_DEVICE_ACCESSIBLE const F* coeff_data() const { + return detail::contiguous_data(coeff_); + } + inline HOST_DEVICE_ACCESSIBLE const double* O_data() const { + return detail::contiguous_data(O_); + } + inline HOST_DEVICE_ACCESSIBLE F* alpha_data() { + return detail::contiguous_data(alpha_); + } + inline HOST_DEVICE_ACCESSIBLE F* coeff_data() { + return detail::contiguous_data(coeff_); + } + inline HOST_DEVICE_ACCESSIBLE double* O_data() { + return detail::contiguous_data(O_); + } inline HOST_DEVICE_ACCESSIBLE double cutoff_radius() const { return cutoff_radius_; diff --git a/include/gauxc/util/contiguous_container_data.hpp b/include/gauxc/util/contiguous_container_data.hpp new file mode 100644 index 000000000..5babf4461 --- /dev/null +++ b/include/gauxc/util/contiguous_container_data.hpp @@ -0,0 +1,14 @@ +#pragma once +#include +#include + +namespace GauXC { +namespace detail { + +template +inline HOST_DEVICE_ACCESSIBLE T* contiguous_data( const std::array& arr ) { + return reinterpret_cast( &const_cast&>(arr) ); +} + +} +} diff --git a/src/cuda_integrator/collocation/deprecated/gaueval_kernels_template.cu b/src/cuda_integrator/collocation/deprecated/gaueval_kernels_template.cu index f726b9b23..c9d0a8c77 100644 --- a/src/cuda_integrator/collocation/deprecated/gaueval_kernels_template.cu +++ b/src/cuda_integrator/collocation/deprecated/gaueval_kernels_template.cu @@ -1,4 +1,4 @@ -//#include +//#include #include #include @@ -7,7 +7,7 @@ #include "gaueval_angular_spherical.hpp" #include "gaueval_angular_spherical_unnorm.hpp" -namespace gpgaueval { +namespace GauXC { __global__ void gaueval_device_$(ang_name)_kernel( diff --git a/src/cuda_integrator/collocation/deprecated/generate_bfeval.py b/src/cuda_integrator/collocation/deprecated/generate_bfeval.py index 68d483ef5..81a593918 100644 --- a/src/cuda_integrator/collocation/deprecated/generate_bfeval.py +++ b/src/cuda_integrator/collocation/deprecated/generate_bfeval.py @@ -259,7 +259,7 @@ def generate_eval_lines( L, ang ): #define GPGAUEVAL_INLINE __inline__ -namespace gpgaueval { +namespace GauXC { """ @@ -405,7 +405,7 @@ def generate_eval_lines( L, ang ): -footer = "} // namespace gpgaueval" +footer = "} // namespace GauXC" cart_header_file.write( footer ) sphr_header_file.write( footer ) @@ -413,7 +413,7 @@ def generate_eval_lines( L, ang ): preamble = """ #pragma once -namespace gpgaueval { +namespace GauXC { """ cons_header_file.write( preamble ) diff --git a/src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp b/src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp index ebbd920f6..d8eba1e4c 100644 --- a/src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp +++ b/src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp @@ -6,7 +6,7 @@ # define GPGAUEVAL_INLINE __inline__ #endif -namespace gpgaueval { +namespace GauXC { GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_0( const double bf, @@ -637,4 +637,4 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_deriv1( -} // namespace gpgaueval +} // namespace GauXC diff --git a/src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp b/src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp index 88a1aeacb..8eeef9f6a 100644 --- a/src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp +++ b/src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp @@ -6,7 +6,7 @@ # define GPGAUEVAL_INLINE __inline__ #endif -namespace gpgaueval { +namespace GauXC { GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_0( const double bf, @@ -497,4 +497,4 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_deriv1( -} // namespace gpgaueval +} // namespace GauXC diff --git a/src/cuda_integrator/collocation/gaueval_device_constants.hpp b/src/cuda_integrator/collocation/gaueval_device_constants.hpp index fcbeebb95..b2192f4ed 100644 --- a/src/cuda_integrator/collocation/gaueval_device_constants.hpp +++ b/src/cuda_integrator/collocation/gaueval_device_constants.hpp @@ -1,6 +1,6 @@ #pragma once -namespace gpgaueval { +namespace GauXC { constexpr double sqrt_10 = 3.1622776601683795; constexpr double sqrt_14 = 3.7416573867739413; @@ -17,4 +17,4 @@ namespace gpgaueval { constexpr double sqrt_35 = 5.916079783099616; constexpr double sqrt_105 = 10.246950765959598; -} // namespace gpgaueval +} // namespace GauXC diff --git a/src/cuda_integrator/collocation/gaueval_kernels.cu b/src/cuda_integrator/collocation/gaueval_kernels.cu index a8756df5b..2b56780bd 100644 --- a/src/cuda_integrator/collocation/gaueval_kernels.cu +++ b/src/cuda_integrator/collocation/gaueval_kernels.cu @@ -7,15 +7,17 @@ namespace GauXC { + +template __global__ void gaueval_device_kernel( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device ) { const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; @@ -32,47 +34,59 @@ void gaueval_device_kernel( const auto* pt = pts_device + 3*ipt; - const auto* O = device::array_data( shell.O ); - const auto* alpha = device::array_data( shell.alpha ); - const auto* coeff = device::array_data( shell.coeff ); + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); - const double xc = pt[0] - O[0]; - const double yc = pt[1] - O[1]; - const double zc = pt[2] - O[2]; + const auto xc = pt[0] - O[0]; + const auto yc = pt[1] - O[1]; + const auto zc = pt[2] - O[2]; - const double rsq = xc*xc + yc*yc + zc*zc; + const auto rsq = xc*xc + yc*yc + zc*zc; - const size_t nprim = shell.nprim; - double tmp = 0.; + const size_t nprim = shell.nprim(); + auto tmp = 0.; for( size_t i = 0; i < nprim; ++i ) tmp += coeff[i] * std::exp( - alpha[i] * rsq ); - double * bf_eval = eval_device + ibf + ipt*nbf; + auto * bf_eval = eval_device + ibf + ipt*nbf; - const bool do_sph = shell.pure; + const bool do_sph = shell.pure(); if( do_sph ) - gaueval_spherical_unnorm_angular( shell.l, tmp, xc, yc, zc, bf_eval ); + gaueval_spherical_unnorm_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); else - gaueval_cartesian_angular( shell.l, tmp, xc, yc, zc, bf_eval ); + gaueval_cartesian_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); } } +template +__global__ +void gaueval_device_kernel( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device +); +template __global__ void gaueval_device_kernel_deriv1( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device, - double* deval_device_x, - double* deval_device_y, - double* deval_device_z + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + T* deval_device_x, + T* deval_device_y, + T* deval_device_z ) { const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; @@ -89,25 +103,25 @@ void gaueval_device_kernel_deriv1( const auto* pt = pts_device + 3*ipt; - const auto* O = device::array_data( shell.O ); - const auto* alpha = device::array_data( shell.alpha ); - const auto* coeff = device::array_data( shell.coeff ); + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); - const double xc = pt[0] - O[0]; - const double yc = pt[1] - O[1]; - const double zc = pt[2] - O[2]; + const auto xc = pt[0] - O[0]; + const auto yc = pt[1] - O[1]; + const auto zc = pt[2] - O[2]; - const double rsq = xc*xc + yc*yc + zc*zc; + const auto rsq = xc*xc + yc*yc + zc*zc; - const size_t nprim = shell.nprim; - double tmp = 0.; - double tmp_x = 0., tmp_y = 0., tmp_z = 0.; + const size_t nprim = shell.nprim(); + auto tmp = 0.; + auto tmp_x = 0., tmp_y = 0., tmp_z = 0.; for( size_t i = 0; i < nprim; ++i ) { - const double a = alpha[i]; - const double e = coeff[i] * std::exp( - a * rsq ); + const auto a = alpha[i]; + const auto e = coeff[i] * std::exp( - a * rsq ); - const double ae = 2. * a * e; + const auto ae = 2. * a * e; tmp += e; tmp_x -= ae * xc; @@ -116,18 +130,18 @@ void gaueval_device_kernel_deriv1( } - double * bf_eval = eval_device + ibf + ipt*nbf; - double * dx_eval = deval_device_x + ibf + ipt*nbf; - double * dy_eval = deval_device_y + ibf + ipt*nbf; - double * dz_eval = deval_device_z + ibf + ipt*nbf; + auto * bf_eval = eval_device + ibf + ipt*nbf; + auto * dx_eval = deval_device_x + ibf + ipt*nbf; + auto * dy_eval = deval_device_y + ibf + ipt*nbf; + auto * dz_eval = deval_device_z + ibf + ipt*nbf; - const bool do_sph = shell.pure; + const bool do_sph = shell.pure(); if( do_sph ) - gaueval_spherical_unnorm_angular_deriv1( shell.l, tmp, tmp_x, tmp_y, tmp_z, + gaueval_spherical_unnorm_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, xc, yc, zc, bf_eval, dx_eval, dy_eval, dz_eval ); else - gaueval_cartesian_angular_deriv1( shell.l, tmp, tmp_x, tmp_y, tmp_z, + gaueval_cartesian_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, xc, yc, zc, bf_eval, dx_eval, dy_eval, dz_eval ); @@ -136,5 +150,19 @@ void gaueval_device_kernel_deriv1( } +template +__global__ +void gaueval_device_kernel_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device, + double* deval_device_x, + double* deval_device_y, + double* deval_device_z +); } diff --git a/src/cuda_integrator/collocation/gaueval_kernels.hpp b/src/cuda_integrator/collocation/gaueval_kernels.hpp index a840d79de..568c083b5 100644 --- a/src/cuda_integrator/collocation/gaueval_kernels.hpp +++ b/src/cuda_integrator/collocation/gaueval_kernels.hpp @@ -2,29 +2,31 @@ namespace GauXC { +template __global__ void gaueval_device_kernel( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device ); +template __global__ void gaueval_device_kernel_deriv1( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device, - double* deval_device_x, - double* deval_device_y, - double* deval_device_z + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + T* deval_device_x, + T* deval_device_y, + T* deval_device_z ); } diff --git a/src/cuda_integrator/collocation/templates/gaueval_angular_template.hpp b/src/cuda_integrator/collocation/templates/gaueval_angular_template.hpp index e68c6c516..441b38ade 100644 --- a/src/cuda_integrator/collocation/templates/gaueval_angular_template.hpp +++ b/src/cuda_integrator/collocation/templates/gaueval_angular_template.hpp @@ -6,7 +6,7 @@ # define GPGAUEVAL_INLINE __inline__ #endif -namespace gpgaueval { +namespace GauXC { $for( L in range(L_max + 1) )\ GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_$(L)( @@ -99,4 +99,4 @@ GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_deriv1( -} // namespace gpgaueval +} // namespace GauXC diff --git a/src/cuda_integrator/collocation/templates/gaueval_device_constants_template.hpp b/src/cuda_integrator/collocation/templates/gaueval_device_constants_template.hpp index 986b5ae76..2339531d3 100644 --- a/src/cuda_integrator/collocation/templates/gaueval_device_constants_template.hpp +++ b/src/cuda_integrator/collocation/templates/gaueval_device_constants_template.hpp @@ -1,9 +1,9 @@ #pragma once -namespace gpgaueval { +namespace GauXC { $for( x in const_lines )\ $(x) $endfor\ -} // namespace gpgaueval +} // namespace GauXC From 247c9c9abbc66d07f927fd2f68b808fbfd47e341 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Tue, 11 Aug 2020 15:53:08 -0700 Subject: [PATCH 05/27] Reduced default LMAX in CUDA collocation to 3 --- .../collocation/gaueval_angular_cartesian.hpp | 364 ------------------ .../gaueval_angular_spherical_unnorm.hpp | 240 ------------ .../collocation/gaueval_device_constants.hpp | 12 +- .../scripts/generate_gaueval_angular_eval.py | 2 +- 4 files changed, 2 insertions(+), 616 deletions(-) diff --git a/src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp b/src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp index d8eba1e4c..e931daa4c 100644 --- a/src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp +++ b/src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp @@ -204,349 +204,6 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_3_deriv1( } -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_4( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*x*x*x*x; - eval[1] = bf*x*x*x*y; - eval[2] = bf*x*x*x*z; - eval[3] = bf*x*x*y*y; - eval[4] = bf*x*x*y*z; - eval[5] = bf*x*x*z*z; - eval[6] = bf*x*y*y*y; - eval[7] = bf*x*y*y*z; - eval[8] = bf*x*y*z*z; - eval[9] = bf*x*z*z*z; - eval[10] = bf*y*y*y*y; - eval[11] = bf*y*y*y*z; - eval[12] = bf*y*y*z*z; - eval[13] = bf*y*z*z*z; - eval[14] = bf*z*z*z*z; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_4_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = x*x*x*(4*bf + bf_x*x); - eval_x[1] = x*x*y*(3*bf + bf_x*x); - eval_x[2] = x*x*z*(3*bf + bf_x*x); - eval_x[3] = x*y*y*(2*bf + bf_x*x); - eval_x[4] = x*y*z*(2*bf + bf_x*x); - eval_x[5] = x*z*z*(2*bf + bf_x*x); - eval_x[6] = y*y*y*(bf + bf_x*x); - eval_x[7] = y*y*z*(bf + bf_x*x); - eval_x[8] = y*z*z*(bf + bf_x*x); - eval_x[9] = z*z*z*(bf + bf_x*x); - eval_x[10] = bf_x*y*y*y*y; - eval_x[11] = bf_x*y*y*y*z; - eval_x[12] = bf_x*y*y*z*z; - eval_x[13] = bf_x*y*z*z*z; - eval_x[14] = bf_x*z*z*z*z; - - eval_y[0] = bf_y*x*x*x*x; - eval_y[1] = x*x*x*(bf + bf_y*y); - eval_y[2] = bf_y*x*x*x*z; - eval_y[3] = x*x*y*(2*bf + bf_y*y); - eval_y[4] = x*x*z*(bf + bf_y*y); - eval_y[5] = bf_y*x*x*z*z; - eval_y[6] = x*y*y*(3*bf + bf_y*y); - eval_y[7] = x*y*z*(2*bf + bf_y*y); - eval_y[8] = x*z*z*(bf + bf_y*y); - eval_y[9] = bf_y*x*z*z*z; - eval_y[10] = y*y*y*(4*bf + bf_y*y); - eval_y[11] = y*y*z*(3*bf + bf_y*y); - eval_y[12] = y*z*z*(2*bf + bf_y*y); - eval_y[13] = z*z*z*(bf + bf_y*y); - eval_y[14] = bf_y*z*z*z*z; - - eval_z[0] = bf_z*x*x*x*x; - eval_z[1] = bf_z*x*x*x*y; - eval_z[2] = x*x*x*(bf + bf_z*z); - eval_z[3] = bf_z*x*x*y*y; - eval_z[4] = x*x*y*(bf + bf_z*z); - eval_z[5] = x*x*z*(2*bf + bf_z*z); - eval_z[6] = bf_z*x*y*y*y; - eval_z[7] = x*y*y*(bf + bf_z*z); - eval_z[8] = x*y*z*(2*bf + bf_z*z); - eval_z[9] = x*z*z*(3*bf + bf_z*z); - eval_z[10] = bf_z*y*y*y*y; - eval_z[11] = y*y*y*(bf + bf_z*z); - eval_z[12] = y*y*z*(2*bf + bf_z*z); - eval_z[13] = y*z*z*(3*bf + bf_z*z); - eval_z[14] = z*z*z*(4*bf + bf_z*z); - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_5( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*x*x*x*x*x; - eval[1] = bf*x*x*x*x*y; - eval[2] = bf*x*x*x*x*z; - eval[3] = bf*x*x*x*y*y; - eval[4] = bf*x*x*x*y*z; - eval[5] = bf*x*x*x*z*z; - eval[6] = bf*x*x*y*y*y; - eval[7] = bf*x*x*y*y*z; - eval[8] = bf*x*x*y*z*z; - eval[9] = bf*x*x*z*z*z; - eval[10] = bf*x*y*y*y*y; - eval[11] = bf*x*y*y*y*z; - eval[12] = bf*x*y*y*z*z; - eval[13] = bf*x*y*z*z*z; - eval[14] = bf*x*z*z*z*z; - eval[15] = bf*y*y*y*y*y; - eval[16] = bf*y*y*y*y*z; - eval[17] = bf*y*y*y*z*z; - eval[18] = bf*y*y*z*z*z; - eval[19] = bf*y*z*z*z*z; - eval[20] = bf*z*z*z*z*z; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_5_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = x*x*x*x*(5*bf + bf_x*x); - eval_x[1] = x*x*x*y*(4*bf + bf_x*x); - eval_x[2] = x*x*x*z*(4*bf + bf_x*x); - eval_x[3] = x*x*y*y*(3*bf + bf_x*x); - eval_x[4] = x*x*y*z*(3*bf + bf_x*x); - eval_x[5] = x*x*z*z*(3*bf + bf_x*x); - eval_x[6] = x*y*y*y*(2*bf + bf_x*x); - eval_x[7] = x*y*y*z*(2*bf + bf_x*x); - eval_x[8] = x*y*z*z*(2*bf + bf_x*x); - eval_x[9] = x*z*z*z*(2*bf + bf_x*x); - eval_x[10] = y*y*y*y*(bf + bf_x*x); - eval_x[11] = y*y*y*z*(bf + bf_x*x); - eval_x[12] = y*y*z*z*(bf + bf_x*x); - eval_x[13] = y*z*z*z*(bf + bf_x*x); - eval_x[14] = z*z*z*z*(bf + bf_x*x); - eval_x[15] = bf_x*y*y*y*y*y; - eval_x[16] = bf_x*y*y*y*y*z; - eval_x[17] = bf_x*y*y*y*z*z; - eval_x[18] = bf_x*y*y*z*z*z; - eval_x[19] = bf_x*y*z*z*z*z; - eval_x[20] = bf_x*z*z*z*z*z; - - eval_y[0] = bf_y*x*x*x*x*x; - eval_y[1] = x*x*x*x*(bf + bf_y*y); - eval_y[2] = bf_y*x*x*x*x*z; - eval_y[3] = x*x*x*y*(2*bf + bf_y*y); - eval_y[4] = x*x*x*z*(bf + bf_y*y); - eval_y[5] = bf_y*x*x*x*z*z; - eval_y[6] = x*x*y*y*(3*bf + bf_y*y); - eval_y[7] = x*x*y*z*(2*bf + bf_y*y); - eval_y[8] = x*x*z*z*(bf + bf_y*y); - eval_y[9] = bf_y*x*x*z*z*z; - eval_y[10] = x*y*y*y*(4*bf + bf_y*y); - eval_y[11] = x*y*y*z*(3*bf + bf_y*y); - eval_y[12] = x*y*z*z*(2*bf + bf_y*y); - eval_y[13] = x*z*z*z*(bf + bf_y*y); - eval_y[14] = bf_y*x*z*z*z*z; - eval_y[15] = y*y*y*y*(5*bf + bf_y*y); - eval_y[16] = y*y*y*z*(4*bf + bf_y*y); - eval_y[17] = y*y*z*z*(3*bf + bf_y*y); - eval_y[18] = y*z*z*z*(2*bf + bf_y*y); - eval_y[19] = z*z*z*z*(bf + bf_y*y); - eval_y[20] = bf_y*z*z*z*z*z; - - eval_z[0] = bf_z*x*x*x*x*x; - eval_z[1] = bf_z*x*x*x*x*y; - eval_z[2] = x*x*x*x*(bf + bf_z*z); - eval_z[3] = bf_z*x*x*x*y*y; - eval_z[4] = x*x*x*y*(bf + bf_z*z); - eval_z[5] = x*x*x*z*(2*bf + bf_z*z); - eval_z[6] = bf_z*x*x*y*y*y; - eval_z[7] = x*x*y*y*(bf + bf_z*z); - eval_z[8] = x*x*y*z*(2*bf + bf_z*z); - eval_z[9] = x*x*z*z*(3*bf + bf_z*z); - eval_z[10] = bf_z*x*y*y*y*y; - eval_z[11] = x*y*y*y*(bf + bf_z*z); - eval_z[12] = x*y*y*z*(2*bf + bf_z*z); - eval_z[13] = x*y*z*z*(3*bf + bf_z*z); - eval_z[14] = x*z*z*z*(4*bf + bf_z*z); - eval_z[15] = bf_z*y*y*y*y*y; - eval_z[16] = y*y*y*y*(bf + bf_z*z); - eval_z[17] = y*y*y*z*(2*bf + bf_z*z); - eval_z[18] = y*y*z*z*(3*bf + bf_z*z); - eval_z[19] = y*z*z*z*(4*bf + bf_z*z); - eval_z[20] = z*z*z*z*(5*bf + bf_z*z); - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_6( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = bf*x*x*x*x*x*x; - eval[1] = bf*x*x*x*x*x*y; - eval[2] = bf*x*x*x*x*x*z; - eval[3] = bf*x*x*x*x*y*y; - eval[4] = bf*x*x*x*x*y*z; - eval[5] = bf*x*x*x*x*z*z; - eval[6] = bf*x*x*x*y*y*y; - eval[7] = bf*x*x*x*y*y*z; - eval[8] = bf*x*x*x*y*z*z; - eval[9] = bf*x*x*x*z*z*z; - eval[10] = bf*x*x*y*y*y*y; - eval[11] = bf*x*x*y*y*y*z; - eval[12] = bf*x*x*y*y*z*z; - eval[13] = bf*x*x*y*z*z*z; - eval[14] = bf*x*x*z*z*z*z; - eval[15] = bf*x*y*y*y*y*y; - eval[16] = bf*x*y*y*y*y*z; - eval[17] = bf*x*y*y*y*z*z; - eval[18] = bf*x*y*y*z*z*z; - eval[19] = bf*x*y*z*z*z*z; - eval[20] = bf*x*z*z*z*z*z; - eval[21] = bf*y*y*y*y*y*y; - eval[22] = bf*y*y*y*y*y*z; - eval[23] = bf*y*y*y*y*z*z; - eval[24] = bf*y*y*y*z*z*z; - eval[25] = bf*y*y*z*z*z*z; - eval[26] = bf*y*z*z*z*z*z; - eval[27] = bf*z*z*z*z*z*z; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_6_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = x*x*x*x*x*(6*bf + bf_x*x); - eval_x[1] = x*x*x*x*y*(5*bf + bf_x*x); - eval_x[2] = x*x*x*x*z*(5*bf + bf_x*x); - eval_x[3] = x*x*x*y*y*(4*bf + bf_x*x); - eval_x[4] = x*x*x*y*z*(4*bf + bf_x*x); - eval_x[5] = x*x*x*z*z*(4*bf + bf_x*x); - eval_x[6] = x*x*y*y*y*(3*bf + bf_x*x); - eval_x[7] = x*x*y*y*z*(3*bf + bf_x*x); - eval_x[8] = x*x*y*z*z*(3*bf + bf_x*x); - eval_x[9] = x*x*z*z*z*(3*bf + bf_x*x); - eval_x[10] = x*y*y*y*y*(2*bf + bf_x*x); - eval_x[11] = x*y*y*y*z*(2*bf + bf_x*x); - eval_x[12] = x*y*y*z*z*(2*bf + bf_x*x); - eval_x[13] = x*y*z*z*z*(2*bf + bf_x*x); - eval_x[14] = x*z*z*z*z*(2*bf + bf_x*x); - eval_x[15] = y*y*y*y*y*(bf + bf_x*x); - eval_x[16] = y*y*y*y*z*(bf + bf_x*x); - eval_x[17] = y*y*y*z*z*(bf + bf_x*x); - eval_x[18] = y*y*z*z*z*(bf + bf_x*x); - eval_x[19] = y*z*z*z*z*(bf + bf_x*x); - eval_x[20] = z*z*z*z*z*(bf + bf_x*x); - eval_x[21] = bf_x*y*y*y*y*y*y; - eval_x[22] = bf_x*y*y*y*y*y*z; - eval_x[23] = bf_x*y*y*y*y*z*z; - eval_x[24] = bf_x*y*y*y*z*z*z; - eval_x[25] = bf_x*y*y*z*z*z*z; - eval_x[26] = bf_x*y*z*z*z*z*z; - eval_x[27] = bf_x*z*z*z*z*z*z; - - eval_y[0] = bf_y*x*x*x*x*x*x; - eval_y[1] = x*x*x*x*x*(bf + bf_y*y); - eval_y[2] = bf_y*x*x*x*x*x*z; - eval_y[3] = x*x*x*x*y*(2*bf + bf_y*y); - eval_y[4] = x*x*x*x*z*(bf + bf_y*y); - eval_y[5] = bf_y*x*x*x*x*z*z; - eval_y[6] = x*x*x*y*y*(3*bf + bf_y*y); - eval_y[7] = x*x*x*y*z*(2*bf + bf_y*y); - eval_y[8] = x*x*x*z*z*(bf + bf_y*y); - eval_y[9] = bf_y*x*x*x*z*z*z; - eval_y[10] = x*x*y*y*y*(4*bf + bf_y*y); - eval_y[11] = x*x*y*y*z*(3*bf + bf_y*y); - eval_y[12] = x*x*y*z*z*(2*bf + bf_y*y); - eval_y[13] = x*x*z*z*z*(bf + bf_y*y); - eval_y[14] = bf_y*x*x*z*z*z*z; - eval_y[15] = x*y*y*y*y*(5*bf + bf_y*y); - eval_y[16] = x*y*y*y*z*(4*bf + bf_y*y); - eval_y[17] = x*y*y*z*z*(3*bf + bf_y*y); - eval_y[18] = x*y*z*z*z*(2*bf + bf_y*y); - eval_y[19] = x*z*z*z*z*(bf + bf_y*y); - eval_y[20] = bf_y*x*z*z*z*z*z; - eval_y[21] = y*y*y*y*y*(6*bf + bf_y*y); - eval_y[22] = y*y*y*y*z*(5*bf + bf_y*y); - eval_y[23] = y*y*y*z*z*(4*bf + bf_y*y); - eval_y[24] = y*y*z*z*z*(3*bf + bf_y*y); - eval_y[25] = y*z*z*z*z*(2*bf + bf_y*y); - eval_y[26] = z*z*z*z*z*(bf + bf_y*y); - eval_y[27] = bf_y*z*z*z*z*z*z; - - eval_z[0] = bf_z*x*x*x*x*x*x; - eval_z[1] = bf_z*x*x*x*x*x*y; - eval_z[2] = x*x*x*x*x*(bf + bf_z*z); - eval_z[3] = bf_z*x*x*x*x*y*y; - eval_z[4] = x*x*x*x*y*(bf + bf_z*z); - eval_z[5] = x*x*x*x*z*(2*bf + bf_z*z); - eval_z[6] = bf_z*x*x*x*y*y*y; - eval_z[7] = x*x*x*y*y*(bf + bf_z*z); - eval_z[8] = x*x*x*y*z*(2*bf + bf_z*z); - eval_z[9] = x*x*x*z*z*(3*bf + bf_z*z); - eval_z[10] = bf_z*x*x*y*y*y*y; - eval_z[11] = x*x*y*y*y*(bf + bf_z*z); - eval_z[12] = x*x*y*y*z*(2*bf + bf_z*z); - eval_z[13] = x*x*y*z*z*(3*bf + bf_z*z); - eval_z[14] = x*x*z*z*z*(4*bf + bf_z*z); - eval_z[15] = bf_z*x*y*y*y*y*y; - eval_z[16] = x*y*y*y*y*(bf + bf_z*z); - eval_z[17] = x*y*y*y*z*(2*bf + bf_z*z); - eval_z[18] = x*y*y*z*z*(3*bf + bf_z*z); - eval_z[19] = x*y*z*z*z*(4*bf + bf_z*z); - eval_z[20] = x*z*z*z*z*(5*bf + bf_z*z); - eval_z[21] = bf_z*y*y*y*y*y*y; - eval_z[22] = y*y*y*y*y*(bf + bf_z*z); - eval_z[23] = y*y*y*y*z*(2*bf + bf_z*z); - eval_z[24] = y*y*y*z*z*(3*bf + bf_z*z); - eval_z[25] = y*y*z*z*z*(4*bf + bf_z*z); - eval_z[26] = y*z*z*z*z*(5*bf + bf_z*z); - eval_z[27] = z*z*z*z*z*(6*bf + bf_z*z); - -} - GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular( const int64_t l, @@ -569,15 +226,6 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular( } else if( l == 3 ) { gaueval_cartesian_angular_3( bf, x, y, z, eval ); - } else if( l == 4 ) { - gaueval_cartesian_angular_4( bf, x, y, z, eval ); - - } else if( l == 5 ) { - gaueval_cartesian_angular_5( bf, x, y, z, eval ); - - } else if( l == 6 ) { - gaueval_cartesian_angular_6( bf, x, y, z, eval ); - } else { assert( false && "L < L_MAX" ); } @@ -617,18 +265,6 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_deriv1( gaueval_cartesian_angular_3( bf, x, y, z, eval ); gaueval_cartesian_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); - } else if( l == 4 ) { - gaueval_cartesian_angular_4( bf, x, y, z, eval ); - gaueval_cartesian_angular_4_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); - - } else if( l == 5 ) { - gaueval_cartesian_angular_5( bf, x, y, z, eval ); - gaueval_cartesian_angular_5_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); - - } else if( l == 6 ) { - gaueval_cartesian_angular_6( bf, x, y, z, eval ); - gaueval_cartesian_angular_6_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); - } else { assert( false && "L < L_MAX" ); } diff --git a/src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp b/src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp index 8eeef9f6a..52832cc6e 100644 --- a/src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp +++ b/src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp @@ -188,225 +188,6 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_3_deriv1( } -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_4( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = sqrt_35*bf*x*y*(x*x - y*y)/2; - eval[1] = sqrt_70*bf*y*z*(3*x*x - y*y)/4; - eval[2] = sqrt_5*bf*x*y*(-x*x - y*y + 6*z*z)/2; - eval[3] = sqrt_10*bf*y*z*(-3*x*x - 3*y*y + 4*z*z)/4; - eval[4] = bf*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; - eval[5] = sqrt_10*bf*x*z*(-3*x*x - 3*y*y + 4*z*z)/4; - eval[6] = sqrt_5*bf*(-x*x*x*x + 6*x*x*z*z + y*y*y*y - 6*y*y*z*z)/4; - eval[7] = sqrt_70*bf*x*z*(x*x - 3*y*y)/4; - eval[8] = sqrt_35*bf*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_4_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = sqrt_35*y*(bf*(3*x*x - y*y) + bf_x*x*(x*x - y*y))/2; - eval_x[1] = sqrt_70*y*z*(6*bf*x + bf_x*(3*x*x - y*y))/4; - eval_x[2] = -sqrt_5*y*(bf*(3*x*x + y*y - 6*z*z) + bf_x*x*(x*x + y*y - 6*z*z))/2; - eval_x[3] = -sqrt_10*y*z*(6*bf*x + bf_x*(3*x*x + 3*y*y - 4*z*z))/4; - eval_x[4] = 3*bf*x*(x*x + y*y - 4*z*z)/2 + bf_x*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; - eval_x[5] = -sqrt_10*z*(bf*(9*x*x + 3*y*y - 4*z*z) + bf_x*x*(3*x*x + 3*y*y - 4*z*z))/4; - eval_x[6] = -sqrt_5*(bf*x*(x*x - 3*z*z) + bf_x*(x*x*x*x - 6*x*x*z*z - y*y*y*y + 6*y*y*z*z)/4); - eval_x[7] = sqrt_70*z*(3*bf*(x*x - y*y) + bf_x*x*(x*x - 3*y*y))/4; - eval_x[8] = sqrt_35*(4*bf*x*(x*x - 3*y*y) + bf_x*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; - - eval_y[0] = sqrt_35*x*(-bf*(-x*x + 3*y*y) + bf_y*y*(x*x - y*y))/2; - eval_y[1] = sqrt_70*z*(-3*bf*(-x*x + y*y) + bf_y*y*(3*x*x - y*y))/4; - eval_y[2] = -sqrt_5*x*(bf*(x*x + 3*y*y - 6*z*z) + bf_y*y*(x*x + y*y - 6*z*z))/2; - eval_y[3] = -sqrt_10*z*(bf*(3*x*x + 9*y*y - 4*z*z) + bf_y*y*(3*x*x + 3*y*y - 4*z*z))/4; - eval_y[4] = 3*bf*y*(x*x + y*y - 4*z*z)/2 + bf_y*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; - eval_y[5] = -sqrt_10*x*z*(6*bf*y + bf_y*(3*x*x + 3*y*y - 4*z*z))/4; - eval_y[6] = sqrt_5*(bf*y*(y*y - 3*z*z) - bf_y*(x*x*x*x - 6*x*x*z*z - y*y*y*y + 6*y*y*z*z)/4); - eval_y[7] = sqrt_70*x*z*(-6*bf*y + bf_y*(x*x - 3*y*y))/4; - eval_y[8] = sqrt_35*(-4*bf*y*(3*x*x - y*y) + bf_y*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; - - eval_z[0] = sqrt_35*bf_z*x*y*(x*x - y*y)/2; - eval_z[1] = sqrt_70*y*(bf + bf_z*z)*(3*x*x - y*y)/4; - eval_z[2] = sqrt_5*x*y*(12*bf*z - bf_z*(x*x + y*y - 6*z*z))/2; - eval_z[3] = sqrt_10*y*(3*bf*(-x*x - y*y + 4*z*z) - bf_z*z*(3*x*x + 3*y*y - 4*z*z))/4; - eval_z[4] = -2*bf*z*(3*x*x + 3*y*y - 2*z*z) + bf_z*(3*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z + 8*z*z*z*z)/8; - eval_z[5] = sqrt_10*x*(3*bf*(-x*x - y*y + 4*z*z) - bf_z*z*(3*x*x + 3*y*y - 4*z*z))/4; - eval_z[6] = sqrt_5*(12*bf*z*(x*x - y*y) - bf_z*(x*x*x*x - 6*x*x*z*z - y*y*y*y + 6*y*y*z*z))/4; - eval_z[7] = sqrt_70*x*(bf + bf_z*z)*(x*x - 3*y*y)/4; - eval_z[8] = sqrt_35*bf_z*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_5( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = 3*sqrt_14*bf*y*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; - eval[1] = 3*sqrt_35*bf*x*y*z*(x*x - y*y)/2; - eval[2] = sqrt_70*bf*y*(-3*x*x*x*x - 2*x*x*y*y + 24*x*x*z*z + y*y*y*y - 8*y*y*z*z)/16; - eval[3] = sqrt_105*bf*x*y*z*(-x*x - y*y + 2*z*z)/2; - eval[4] = sqrt_15*bf*y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8; - eval[5] = bf*z*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z)/8; - eval[6] = sqrt_15*bf*x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8; - eval[7] = sqrt_105*bf*z*(-x*x*x*x + 2*x*x*z*z + y*y*y*y - 2*y*y*z*z)/4; - eval[8] = sqrt_70*bf*x*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z)/16; - eval[9] = 3*sqrt_35*bf*z*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; - eval[10] = 3*sqrt_14*bf*x*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_5_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = 3*sqrt_14*y*(20*bf*x*(x*x - y*y) + bf_x*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y))/16; - eval_x[1] = 3*sqrt_35*y*z*(bf*(3*x*x - y*y) + bf_x*x*(x*x - y*y))/2; - eval_x[2] = -sqrt_70*y*(4*bf*x*(3*x*x + y*y - 12*z*z) + bf_x*(3*x*x*x*x + 2*x*x*y*y - 24*x*x*z*z - y*y*y*y + 8*y*y*z*z))/16; - eval_x[3] = -sqrt_105*y*z*(bf*(3*x*x + y*y - 2*z*z) + bf_x*x*(x*x + y*y - 2*z*z))/2; - eval_x[4] = sqrt_15*y*(4*bf*x*(x*x + y*y - 6*z*z) + bf_x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; - eval_x[5] = z*(20*bf*x*(3*x*x + 3*y*y - 4*z*z) + bf_x*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z))/8; - eval_x[6] = sqrt_15*(bf*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + 4*x*x*(x*x + y*y - 6*z*z) + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z) + bf_x*x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; - eval_x[7] = -sqrt_105*z*(4*bf*x*(x*x - z*z) + bf_x*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z))/4; - eval_x[8] = sqrt_70*(bf*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 4*x*x*(-x*x + y*y + 4*z*z) + 3*y*y*y*y - 24*y*y*z*z) + bf_x*x*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z))/16; - eval_x[9] = 3*sqrt_35*z*(4*bf*x*(x*x - 3*y*y) + bf_x*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; - eval_x[10] = 3*sqrt_14*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + bf_x*x*x*x*x*x - 10*bf_x*x*x*x*y*y + 5*bf_x*x*y*y*y*y)/16; - - eval_y[0] = 3*sqrt_14*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + 5*bf_y*x*x*x*x*y - 10*bf_y*x*x*y*y*y + bf_y*y*y*y*y*y)/16; - eval_y[1] = 3*sqrt_35*x*z*(-bf*(-x*x + 3*y*y) + bf_y*y*(x*x - y*y))/2; - eval_y[2] = sqrt_70*(-3*bf*x*x*x*x - 6*bf*x*x*y*y + 24*bf*x*x*z*z + 5*bf*y*y*y*y - 24*bf*y*y*z*z - 3*bf_y*x*x*x*x*y - 2*bf_y*x*x*y*y*y + 24*bf_y*x*x*y*z*z + bf_y*y*y*y*y*y - 8*bf_y*y*y*y*z*z)/16; - eval_y[3] = -sqrt_105*x*z*(bf*(x*x + 3*y*y - 2*z*z) + bf_y*y*(x*x + y*y - 2*z*z))/2; - eval_y[4] = sqrt_15*(bf*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 4*y*y*(x*x + y*y - 6*z*z) + 8*z*z*z*z) + bf_y*y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; - eval_y[5] = z*(20*bf*y*(3*x*x + 3*y*y - 4*z*z) + bf_y*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z))/8; - eval_y[6] = sqrt_15*x*(4*bf*y*(x*x + y*y - 6*z*z) + bf_y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; - eval_y[7] = sqrt_105*z*(4*bf*y*(y*y - z*z) - bf_y*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z))/4; - eval_y[8] = sqrt_70*x*(4*bf*y*(x*x + 3*y*y - 12*z*z) + bf_y*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z))/16; - eval_y[9] = 3*sqrt_35*z*(-4*bf*y*(3*x*x - y*y) + bf_y*(x*x*x*x - 6*x*x*y*y + y*y*y*y))/8; - eval_y[10] = 3*sqrt_14*x*(-20*bf*y*(x*x - y*y) + bf_y*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y))/16; - - eval_z[0] = 3*sqrt_14*bf_z*y*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; - eval_z[1] = 3*sqrt_35*x*y*(bf + bf_z*z)*(x*x - y*y)/2; - eval_z[2] = sqrt_70*y*(16*bf*z*(3*x*x - y*y) - bf_z*(3*x*x*x*x + 2*x*x*y*y - 24*x*x*z*z - y*y*y*y + 8*y*y*z*z))/16; - eval_z[3] = sqrt_105*x*y*(bf*(-x*x - y*y + 6*z*z) - bf_z*z*(x*x + y*y - 2*z*z))/2; - eval_z[4] = sqrt_15*y*(-8*bf*z*(3*x*x + 3*y*y - 4*z*z) + bf_z*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; - eval_z[5] = bf*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z + z*z*(-80*x*x - 80*y*y + 32*z*z))/8 + bf_z*z*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z)/8; - eval_z[6] = sqrt_15*x*(-8*bf*z*(3*x*x + 3*y*y - 4*z*z) + bf_z*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z))/8; - eval_z[7] = sqrt_105*(bf*(-x*x*x*x + 2*x*x*z*z + y*y*y*y - 2*y*y*z*z + 4*z*z*(x*x - y*y)) - bf_z*z*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z))/4; - eval_z[8] = sqrt_70*x*(16*bf*z*(x*x - 3*y*y) + bf_z*(-x*x*x*x + 2*x*x*y*y + 8*x*x*z*z + 3*y*y*y*y - 24*y*y*z*z))/16; - eval_z[9] = 3*sqrt_35*(bf + bf_z*z)*(x*x*x*x - 6*x*x*y*y + y*y*y*y)/8; - eval_z[10] = 3*sqrt_14*bf_z*x*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_6( - const double bf, - const double x, - const double y, - const double z, - double* eval -) { - - eval[0] = sqrt_462*bf*x*y*(3*x*x*x*x - 10*x*x*y*y + 3*y*y*y*y)/16; - eval[1] = 3*sqrt_154*bf*y*z*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; - eval[2] = 3*sqrt_7*bf*x*y*(-x*x*x*x + 10*x*x*z*z + y*y*y*y - 10*y*y*z*z)/4; - eval[3] = sqrt_210*bf*y*z*(-9*x*x*x*x - 6*x*x*y*y + 24*x*x*z*z + 3*y*y*y*y - 8*y*y*z*z)/16; - eval[4] = sqrt_210*bf*x*y*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z)/16; - eval[5] = sqrt_21*bf*y*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z)/8; - eval[6] = bf*(-5*x*x*x*x*x*x - 15*x*x*x*x*y*y + 90*x*x*x*x*z*z - 15*x*x*y*y*y*y + 180*x*x*y*y*z*z - 120*x*x*z*z*z*z - 5*y*y*y*y*y*y + 90*y*y*y*y*z*z - 120*y*y*z*z*z*z + 16*z*z*z*z*z*z)/16; - eval[7] = sqrt_21*bf*x*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z)/8; - eval[8] = sqrt_210*bf*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z)/32; - eval[9] = sqrt_210*bf*x*z*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z)/16; - eval[10] = 3*sqrt_7*bf*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z)/16; - eval[11] = 3*sqrt_154*bf*x*z*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; - eval[12] = sqrt_462*bf*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y)/32; - -} - -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_6_deriv1( - const double bf, - const double bf_x, - const double bf_y, - const double bf_z, - const double x, - const double y, - const double z, - double* eval_x, - double* eval_y, - double* eval_z -) { - - eval_x[0] = sqrt_462*y*(15*bf*x*x*x*x - 30*bf*x*x*y*y + 3*bf*y*y*y*y + 3*bf_x*x*x*x*x*x - 10*bf_x*x*x*x*y*y + 3*bf_x*x*y*y*y*y)/16; - eval_x[1] = 3*sqrt_154*y*z*(20*bf*x*(x*x - y*y) + bf_x*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y))/16; - eval_x[2] = 3*sqrt_7*y*(-5*bf*x*x*x*x + 30*bf*x*x*z*z + bf*y*y*y*y - 10*bf*y*y*z*z - bf_x*x*x*x*x*x + 10*bf_x*x*x*x*z*z + bf_x*x*y*y*y*y - 10*bf_x*x*y*y*z*z)/4; - eval_x[3] = -sqrt_210*y*z*(12*bf*x*(3*x*x + y*y - 4*z*z) + bf_x*(9*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z - 3*y*y*y*y + 8*y*y*z*z))/16; - eval_x[4] = sqrt_210*y*(bf*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + 4*x*x*(x*x + y*y - 8*z*z) + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z) + bf_x*x*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z))/16; - eval_x[5] = sqrt_21*y*z*(20*bf*x*(x*x + y*y - 2*z*z) + bf_x*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; - eval_x[6] = -15*bf*x*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8 - bf_x*(5*x*x*x*x*x*x + 15*x*x*x*x*y*y - 90*x*x*x*x*z*z + 15*x*x*y*y*y*y - 180*x*x*y*y*z*z + 120*x*x*z*z*z*z + 5*y*y*y*y*y*y - 90*y*y*y*y*z*z + 120*y*y*z*z*z*z - 16*z*z*z*z*z*z)/16; - eval_x[7] = sqrt_21*z*(bf*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 20*x*x*(x*x + y*y - 2*z*z) + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z) + bf_x*x*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; - eval_x[8] = sqrt_210*(2*bf*x*(3*x*x*x*x + 2*x*x*y*y - 32*x*x*z*z - y*y*y*y + 16*z*z*z*z) + bf_x*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z))/32; - eval_x[9] = sqrt_210*z*(-15*bf*x*x*x*x + 18*bf*x*x*y*y + 24*bf*x*x*z*z + 9*bf*y*y*y*y - 24*bf*y*y*z*z - 3*bf_x*x*x*x*x*x + 6*bf_x*x*x*x*y*y + 8*bf_x*x*x*x*z*z + 9*bf_x*x*y*y*y*y - 24*bf_x*x*y*y*z*z)/16; - eval_x[10] = 3*sqrt_7*(2*bf*x*(-3*x*x*x*x + 10*x*x*y*y + 20*x*x*z*z + 5*y*y*y*y - 60*y*y*z*z) + bf_x*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z))/16; - eval_x[11] = 3*sqrt_154*z*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + bf_x*x*x*x*x*x - 10*bf_x*x*x*x*y*y + 5*bf_x*x*y*y*y*y)/16; - eval_x[12] = sqrt_462*(6*bf*x*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y) + bf_x*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y))/32; - - eval_y[0] = sqrt_462*x*(3*bf*x*x*x*x - 30*bf*x*x*y*y + 15*bf*y*y*y*y + 3*bf_y*x*x*x*x*y - 10*bf_y*x*x*y*y*y + 3*bf_y*y*y*y*y*y)/16; - eval_y[1] = 3*sqrt_154*z*(5*bf*x*x*x*x - 30*bf*x*x*y*y + 5*bf*y*y*y*y + 5*bf_y*x*x*x*x*y - 10*bf_y*x*x*y*y*y + bf_y*y*y*y*y*y)/16; - eval_y[2] = 3*sqrt_7*x*(bf*(-x*x*x*x + 10*x*x*z*z + y*y*y*y - 10*y*y*z*z + 4*y*y*(y*y - 5*z*z)) - bf_y*y*(x*x*x*x - 10*x*x*z*z - y*y*y*y + 10*y*y*z*z))/4; - eval_y[3] = sqrt_210*z*(-9*bf*x*x*x*x - 18*bf*x*x*y*y + 24*bf*x*x*z*z + 15*bf*y*y*y*y - 24*bf*y*y*z*z - 9*bf_y*x*x*x*x*y - 6*bf_y*x*x*y*y*y + 24*bf_y*x*x*y*z*z + 3*bf_y*y*y*y*y*y - 8*bf_y*y*y*y*z*z)/16; - eval_y[4] = sqrt_210*x*(bf*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 4*y*y*(x*x + y*y - 8*z*z) + 16*z*z*z*z) + bf_y*y*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z))/16; - eval_y[5] = sqrt_21*z*(bf*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 20*y*y*(x*x + y*y - 2*z*z) + 8*z*z*z*z) + bf_y*y*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; - eval_y[6] = -15*bf*y*(x*x*x*x + 2*x*x*y*y - 12*x*x*z*z + y*y*y*y - 12*y*y*z*z + 8*z*z*z*z)/8 - bf_y*(5*x*x*x*x*x*x + 15*x*x*x*x*y*y - 90*x*x*x*x*z*z + 15*x*x*y*y*y*y - 180*x*x*y*y*z*z + 120*x*x*z*z*z*z + 5*y*y*y*y*y*y - 90*y*y*y*y*z*z + 120*y*y*z*z*z*z - 16*z*z*z*z*z*z)/16; - eval_y[7] = sqrt_21*x*z*(20*bf*y*(x*x + y*y - 2*z*z) + bf_y*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; - eval_y[8] = sqrt_210*(-2*bf*y*(-x*x*x*x + 2*x*x*y*y + 3*y*y*y*y - 32*y*y*z*z + 16*z*z*z*z) + bf_y*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z))/32; - eval_y[9] = sqrt_210*x*z*(12*bf*y*(x*x + 3*y*y - 4*z*z) + bf_y*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z))/16; - eval_y[10] = 3*sqrt_7*(2*bf*y*(5*x*x*x*x + 10*x*x*y*y - 60*x*x*z*z - 3*y*y*y*y + 20*y*y*z*z) + bf_y*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z))/16; - eval_y[11] = 3*sqrt_154*x*z*(-20*bf*y*(x*x - y*y) + bf_y*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y))/16; - eval_y[12] = sqrt_462*(-6*bf*y*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y) + bf_y*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y))/32; - - eval_z[0] = sqrt_462*bf_z*x*y*(3*x*x*x*x - 10*x*x*y*y + 3*y*y*y*y)/16; - eval_z[1] = 3*sqrt_154*y*(bf + bf_z*z)*(5*x*x*x*x - 10*x*x*y*y + y*y*y*y)/16; - eval_z[2] = 3*sqrt_7*x*y*(20*bf*z*(x*x - y*y) - bf_z*(x*x*x*x - 10*x*x*z*z - y*y*y*y + 10*y*y*z*z))/4; - eval_z[3] = sqrt_210*y*(bf*(-9*x*x*x*x - 6*x*x*y*y + 24*x*x*z*z + 3*y*y*y*y - 8*y*y*z*z + 16*z*z*(3*x*x - y*y)) - bf_z*z*(9*x*x*x*x + 6*x*x*y*y - 24*x*x*z*z - 3*y*y*y*y + 8*y*y*z*z))/16; - eval_z[4] = sqrt_210*x*y*(-32*bf*z*(x*x + y*y - 2*z*z) + bf_z*(x*x*x*x + 2*x*x*y*y - 16*x*x*z*z + y*y*y*y - 16*y*y*z*z + 16*z*z*z*z))/16; - eval_z[5] = sqrt_21*y*(-bf*(-5*x*x*x*x - 10*x*x*y*y + 20*x*x*z*z - 5*y*y*y*y + 20*y*y*z*z - 8*z*z*z*z + z*z*(40*x*x + 40*y*y - 32*z*z)) + bf_z*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; - eval_z[6] = 3*bf*z*(15*x*x*x*x + 30*x*x*y*y - 40*x*x*z*z + 15*y*y*y*y - 40*y*y*z*z + 8*z*z*z*z)/4 - bf_z*(5*x*x*x*x*x*x + 15*x*x*x*x*y*y - 90*x*x*x*x*z*z + 15*x*x*y*y*y*y - 180*x*x*y*y*z*z + 120*x*x*z*z*z*z + 5*y*y*y*y*y*y - 90*y*y*y*y*z*z + 120*y*y*z*z*z*z - 16*z*z*z*z*z*z)/16; - eval_z[7] = sqrt_21*x*(-bf*(-5*x*x*x*x - 10*x*x*y*y + 20*x*x*z*z - 5*y*y*y*y + 20*y*y*z*z - 8*z*z*z*z + z*z*(40*x*x + 40*y*y - 32*z*z)) + bf_z*z*(5*x*x*x*x + 10*x*x*y*y - 20*x*x*z*z + 5*y*y*y*y - 20*y*y*z*z + 8*z*z*z*z))/8; - eval_z[8] = sqrt_210*(-bf*z*(x*x*x*x - 2*x*x*z*z - y*y*y*y + 2*y*y*z*z) + bf_z*(x*x*x*x*x*x + x*x*x*x*y*y - 16*x*x*x*x*z*z - x*x*y*y*y*y + 16*x*x*z*z*z*z - y*y*y*y*y*y + 16*y*y*y*y*z*z - 16*y*y*z*z*z*z)/32); - eval_z[9] = sqrt_210*x*(bf*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z + 16*z*z*(x*x - 3*y*y)) + bf_z*z*(-3*x*x*x*x + 6*x*x*y*y + 8*x*x*z*z + 9*y*y*y*y - 24*y*y*z*z))/16; - eval_z[10] = 3*sqrt_7*(20*bf*z*(x*x*x*x - 6*x*x*y*y + y*y*y*y) + bf_z*(-x*x*x*x*x*x + 5*x*x*x*x*y*y + 10*x*x*x*x*z*z + 5*x*x*y*y*y*y - 60*x*x*y*y*z*z - y*y*y*y*y*y + 10*y*y*y*y*z*z))/16; - eval_z[11] = 3*sqrt_154*x*(bf + bf_z*z)*(x*x*x*x - 10*x*x*y*y + 5*y*y*y*y)/16; - eval_z[12] = sqrt_462*bf_z*(x*x*x*x*x*x - 15*x*x*x*x*y*y + 15*x*x*y*y*y*y - y*y*y*y*y*y)/32; - -} - GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular( const int64_t l, @@ -429,15 +210,6 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular( } else if( l == 3 ) { gaueval_spherical_unnorm_angular_3( bf, x, y, z, eval ); - } else if( l == 4 ) { - gaueval_spherical_unnorm_angular_4( bf, x, y, z, eval ); - - } else if( l == 5 ) { - gaueval_spherical_unnorm_angular_5( bf, x, y, z, eval ); - - } else if( l == 6 ) { - gaueval_spherical_unnorm_angular_6( bf, x, y, z, eval ); - } else { assert( false && "L < L_MAX" ); } @@ -477,18 +249,6 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_deriv1( gaueval_spherical_unnorm_angular_3( bf, x, y, z, eval ); gaueval_spherical_unnorm_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); - } else if( l == 4 ) { - gaueval_spherical_unnorm_angular_4( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_4_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); - - } else if( l == 5 ) { - gaueval_spherical_unnorm_angular_5( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_5_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); - - } else if( l == 6 ) { - gaueval_spherical_unnorm_angular_6( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_6_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); - } else { assert( false && "L < L_MAX" ); } diff --git a/src/cuda_integrator/collocation/gaueval_device_constants.hpp b/src/cuda_integrator/collocation/gaueval_device_constants.hpp index b2192f4ed..1fae75b8f 100644 --- a/src/cuda_integrator/collocation/gaueval_device_constants.hpp +++ b/src/cuda_integrator/collocation/gaueval_device_constants.hpp @@ -3,18 +3,8 @@ namespace GauXC { constexpr double sqrt_10 = 3.1622776601683795; - constexpr double sqrt_14 = 3.7416573867739413; - constexpr double sqrt_154 = 12.409673645990857; - constexpr double sqrt_462 = 21.494185260204677; - constexpr double sqrt_210 = 14.491376746189438; - constexpr double sqrt_21 = 4.58257569495584; - constexpr double sqrt_70 = 8.366600265340756; - constexpr double sqrt_3 = 1.7320508075688772; - constexpr double sqrt_7 = 2.6457513110645907; constexpr double sqrt_15 = 3.872983346207417; - constexpr double sqrt_5 = 2.23606797749979; + constexpr double sqrt_3 = 1.7320508075688772; constexpr double sqrt_6 = 2.449489742783178; - constexpr double sqrt_35 = 5.916079783099616; - constexpr double sqrt_105 = 10.246950765959598; } // namespace GauXC diff --git a/src/cuda_integrator/collocation/scripts/generate_gaueval_angular_eval.py b/src/cuda_integrator/collocation/scripts/generate_gaueval_angular_eval.py index 56aa2e73d..e59b47149 100644 --- a/src/cuda_integrator/collocation/scripts/generate_gaueval_angular_eval.py +++ b/src/cuda_integrator/collocation/scripts/generate_gaueval_angular_eval.py @@ -4,8 +4,8 @@ from io import StringIO - L_max = 6 +if len(sys.argv) > 1: L_max = int(sys.argv[1]) #sphr_bf_body = [] #sphr_bf_d1_body = [] From d58ba6796e6ac5a57594867e17432a36da799f65 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Tue, 11 Aug 2020 19:29:38 -0400 Subject: [PATCH 06/27] Refactored integrator source --- src/CMakeLists.txt | 14 ++++++++------ .../deprecated/gaueval_kernels_template.cu | 0 .../collocation/deprecated/generate_bfeval.py | 0 .../collocation/gaueval_angular_cartesian.hpp | 0 .../gaueval_angular_spherical_unnorm.hpp | 0 .../cuda}/collocation/gaueval_device_constants.hpp | 0 .../cuda}/collocation/gaueval_kernels.cu | 0 .../cuda}/collocation/gaueval_kernels.hpp | 0 .../cuda}/collocation/gaueval_radial.hpp | 0 .../cuda}/collocation/scripts/__init__.py | 0 .../cuda}/collocation/scripts/gaueval_angular.py | 0 .../scripts/generate_gaueval_angular_eval.py | 0 .../templates/gaueval_angular_template.hpp | 0 .../gaueval_device_constants_template.hpp | 0 .../cuda}/cuda_collocation.cxx | 0 .../cuda}/cuda_collocation_kernels.cu | 0 .../cuda}/gauxc-cuda_integrator.cmake | 2 +- src/{host_integrator => integrator/host}/blas.cxx | 0 src/{host_integrator => integrator/host}/blas.hpp | 0 .../host}/host_collocation.cxx | 0 .../host}/host_collocation.hpp | 0 .../host}/host_weights.cxx | 0 .../host}/host_weights.hpp | 0 .../host}/host_zmat.cxx | 0 .../host}/host_zmat.hpp | 0 src/{host_integrator => integrator/host}/util.hpp | 0 .../host}/xc_host_util.cxx | 0 27 files changed, 9 insertions(+), 7 deletions(-) rename src/{cuda_integrator => integrator/cuda}/collocation/deprecated/gaueval_kernels_template.cu (100%) rename src/{cuda_integrator => integrator/cuda}/collocation/deprecated/generate_bfeval.py (100%) rename src/{cuda_integrator => integrator/cuda}/collocation/gaueval_angular_cartesian.hpp (100%) rename src/{cuda_integrator => integrator/cuda}/collocation/gaueval_angular_spherical_unnorm.hpp (100%) rename src/{cuda_integrator => integrator/cuda}/collocation/gaueval_device_constants.hpp (100%) rename src/{cuda_integrator => integrator/cuda}/collocation/gaueval_kernels.cu (100%) rename src/{cuda_integrator => integrator/cuda}/collocation/gaueval_kernels.hpp (100%) rename src/{cuda_integrator => integrator/cuda}/collocation/gaueval_radial.hpp (100%) rename src/{cuda_integrator => integrator/cuda}/collocation/scripts/__init__.py (100%) rename src/{cuda_integrator => integrator/cuda}/collocation/scripts/gaueval_angular.py (100%) rename src/{cuda_integrator => integrator/cuda}/collocation/scripts/generate_gaueval_angular_eval.py (100%) rename src/{cuda_integrator => integrator/cuda}/collocation/templates/gaueval_angular_template.hpp (100%) rename src/{cuda_integrator => integrator/cuda}/collocation/templates/gaueval_device_constants_template.hpp (100%) rename src/{cuda_integrator => integrator/cuda}/cuda_collocation.cxx (100%) rename src/{cuda_integrator => integrator/cuda}/cuda_collocation_kernels.cu (100%) rename src/{cuda_integrator => integrator/cuda}/gauxc-cuda_integrator.cmake (93%) rename src/{host_integrator => integrator/host}/blas.cxx (100%) rename src/{host_integrator => integrator/host}/blas.hpp (100%) rename src/{host_integrator => integrator/host}/host_collocation.cxx (100%) rename src/{host_integrator => integrator/host}/host_collocation.hpp (100%) rename src/{host_integrator => integrator/host}/host_weights.cxx (100%) rename src/{host_integrator => integrator/host}/host_weights.hpp (100%) rename src/{host_integrator => integrator/host}/host_zmat.cxx (100%) rename src/{host_integrator => integrator/host}/host_zmat.hpp (100%) rename src/{host_integrator => integrator/host}/util.hpp (100%) rename src/{host_integrator => integrator/host}/xc_host_util.cxx (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f4f8c93d8..769249e76 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,11 +23,11 @@ add_library( gauxc STATIC load_balancer.cxx replicated_load_balancer.cxx integrator_common.cxx - host_integrator/xc_host_util.cxx - host_integrator/host_weights.cxx - host_integrator/host_collocation.cxx - host_integrator/host_zmat.cxx - host_integrator/blas.cxx + integrator/host/xc_host_util.cxx + integrator/host/host_weights.cxx + integrator/host/host_collocation.cxx + integrator/host/host_zmat.cxx + integrator/host/blas.cxx ) target_include_directories( gauxc @@ -57,7 +57,7 @@ endif() if( GAUXC_ENABLE_DEVICE ) if( GAUXC_ENABLE_CUDA ) - include( cuda_integrator/gauxc-cuda_integrator.cmake ) + include( integrator/cuda/gauxc-cuda_integrator.cmake ) endif() get_target_property( DEVICE_TARGET_DEPENDS gauxc_device_integrator @@ -68,4 +68,6 @@ if( GAUXC_ENABLE_DEVICE ) ${DEVICE_TARGET_DEPENDS} ) + add_dependencies( gauxc gauxc_device_integrator ) + endif() diff --git a/src/cuda_integrator/collocation/deprecated/gaueval_kernels_template.cu b/src/integrator/cuda/collocation/deprecated/gaueval_kernels_template.cu similarity index 100% rename from src/cuda_integrator/collocation/deprecated/gaueval_kernels_template.cu rename to src/integrator/cuda/collocation/deprecated/gaueval_kernels_template.cu diff --git a/src/cuda_integrator/collocation/deprecated/generate_bfeval.py b/src/integrator/cuda/collocation/deprecated/generate_bfeval.py similarity index 100% rename from src/cuda_integrator/collocation/deprecated/generate_bfeval.py rename to src/integrator/cuda/collocation/deprecated/generate_bfeval.py diff --git a/src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp b/src/integrator/cuda/collocation/gaueval_angular_cartesian.hpp similarity index 100% rename from src/cuda_integrator/collocation/gaueval_angular_cartesian.hpp rename to src/integrator/cuda/collocation/gaueval_angular_cartesian.hpp diff --git a/src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp b/src/integrator/cuda/collocation/gaueval_angular_spherical_unnorm.hpp similarity index 100% rename from src/cuda_integrator/collocation/gaueval_angular_spherical_unnorm.hpp rename to src/integrator/cuda/collocation/gaueval_angular_spherical_unnorm.hpp diff --git a/src/cuda_integrator/collocation/gaueval_device_constants.hpp b/src/integrator/cuda/collocation/gaueval_device_constants.hpp similarity index 100% rename from src/cuda_integrator/collocation/gaueval_device_constants.hpp rename to src/integrator/cuda/collocation/gaueval_device_constants.hpp diff --git a/src/cuda_integrator/collocation/gaueval_kernels.cu b/src/integrator/cuda/collocation/gaueval_kernels.cu similarity index 100% rename from src/cuda_integrator/collocation/gaueval_kernels.cu rename to src/integrator/cuda/collocation/gaueval_kernels.cu diff --git a/src/cuda_integrator/collocation/gaueval_kernels.hpp b/src/integrator/cuda/collocation/gaueval_kernels.hpp similarity index 100% rename from src/cuda_integrator/collocation/gaueval_kernels.hpp rename to src/integrator/cuda/collocation/gaueval_kernels.hpp diff --git a/src/cuda_integrator/collocation/gaueval_radial.hpp b/src/integrator/cuda/collocation/gaueval_radial.hpp similarity index 100% rename from src/cuda_integrator/collocation/gaueval_radial.hpp rename to src/integrator/cuda/collocation/gaueval_radial.hpp diff --git a/src/cuda_integrator/collocation/scripts/__init__.py b/src/integrator/cuda/collocation/scripts/__init__.py similarity index 100% rename from src/cuda_integrator/collocation/scripts/__init__.py rename to src/integrator/cuda/collocation/scripts/__init__.py diff --git a/src/cuda_integrator/collocation/scripts/gaueval_angular.py b/src/integrator/cuda/collocation/scripts/gaueval_angular.py similarity index 100% rename from src/cuda_integrator/collocation/scripts/gaueval_angular.py rename to src/integrator/cuda/collocation/scripts/gaueval_angular.py diff --git a/src/cuda_integrator/collocation/scripts/generate_gaueval_angular_eval.py b/src/integrator/cuda/collocation/scripts/generate_gaueval_angular_eval.py similarity index 100% rename from src/cuda_integrator/collocation/scripts/generate_gaueval_angular_eval.py rename to src/integrator/cuda/collocation/scripts/generate_gaueval_angular_eval.py diff --git a/src/cuda_integrator/collocation/templates/gaueval_angular_template.hpp b/src/integrator/cuda/collocation/templates/gaueval_angular_template.hpp similarity index 100% rename from src/cuda_integrator/collocation/templates/gaueval_angular_template.hpp rename to src/integrator/cuda/collocation/templates/gaueval_angular_template.hpp diff --git a/src/cuda_integrator/collocation/templates/gaueval_device_constants_template.hpp b/src/integrator/cuda/collocation/templates/gaueval_device_constants_template.hpp similarity index 100% rename from src/cuda_integrator/collocation/templates/gaueval_device_constants_template.hpp rename to src/integrator/cuda/collocation/templates/gaueval_device_constants_template.hpp diff --git a/src/cuda_integrator/cuda_collocation.cxx b/src/integrator/cuda/cuda_collocation.cxx similarity index 100% rename from src/cuda_integrator/cuda_collocation.cxx rename to src/integrator/cuda/cuda_collocation.cxx diff --git a/src/cuda_integrator/cuda_collocation_kernels.cu b/src/integrator/cuda/cuda_collocation_kernels.cu similarity index 100% rename from src/cuda_integrator/cuda_collocation_kernels.cu rename to src/integrator/cuda/cuda_collocation_kernels.cu diff --git a/src/cuda_integrator/gauxc-cuda_integrator.cmake b/src/integrator/cuda/gauxc-cuda_integrator.cmake similarity index 93% rename from src/cuda_integrator/gauxc-cuda_integrator.cmake rename to src/integrator/cuda/gauxc-cuda_integrator.cmake index 56a3d0d11..b99024e7c 100644 --- a/src/cuda_integrator/gauxc-cuda_integrator.cmake +++ b/src/integrator/cuda/gauxc-cuda_integrator.cmake @@ -1,5 +1,5 @@ add_library( gauxc_device_integrator OBJECT - cuda_integrator/collocation/gaueval_kernels.cu + integrator/cuda/collocation/gaueval_kernels.cu #cuda_integrator/collocation/.cxx ) target_compile_features( gauxc_device_integrator PUBLIC cuda_std_14 cxx_std_14 ) diff --git a/src/host_integrator/blas.cxx b/src/integrator/host/blas.cxx similarity index 100% rename from src/host_integrator/blas.cxx rename to src/integrator/host/blas.cxx diff --git a/src/host_integrator/blas.hpp b/src/integrator/host/blas.hpp similarity index 100% rename from src/host_integrator/blas.hpp rename to src/integrator/host/blas.hpp diff --git a/src/host_integrator/host_collocation.cxx b/src/integrator/host/host_collocation.cxx similarity index 100% rename from src/host_integrator/host_collocation.cxx rename to src/integrator/host/host_collocation.cxx diff --git a/src/host_integrator/host_collocation.hpp b/src/integrator/host/host_collocation.hpp similarity index 100% rename from src/host_integrator/host_collocation.hpp rename to src/integrator/host/host_collocation.hpp diff --git a/src/host_integrator/host_weights.cxx b/src/integrator/host/host_weights.cxx similarity index 100% rename from src/host_integrator/host_weights.cxx rename to src/integrator/host/host_weights.cxx diff --git a/src/host_integrator/host_weights.hpp b/src/integrator/host/host_weights.hpp similarity index 100% rename from src/host_integrator/host_weights.hpp rename to src/integrator/host/host_weights.hpp diff --git a/src/host_integrator/host_zmat.cxx b/src/integrator/host/host_zmat.cxx similarity index 100% rename from src/host_integrator/host_zmat.cxx rename to src/integrator/host/host_zmat.cxx diff --git a/src/host_integrator/host_zmat.hpp b/src/integrator/host/host_zmat.hpp similarity index 100% rename from src/host_integrator/host_zmat.hpp rename to src/integrator/host/host_zmat.hpp diff --git a/src/host_integrator/util.hpp b/src/integrator/host/util.hpp similarity index 100% rename from src/host_integrator/util.hpp rename to src/integrator/host/util.hpp diff --git a/src/host_integrator/xc_host_util.cxx b/src/integrator/host/xc_host_util.cxx similarity index 100% rename from src/host_integrator/xc_host_util.cxx rename to src/integrator/host/xc_host_util.cxx From 53f1ddea8a5c9346a330194bc819253bd629a05b Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Wed, 12 Aug 2020 13:40:32 -0400 Subject: [PATCH 07/27] Added C++ wrappers for CUDA collocation kernels --- include/gauxc/gauxc_config.hpp.in | 4 + include/gauxc/util/div_ceil.hpp | 67 +++++++++++++++ .../cuda/collocation/gaueval_device.cu | 86 +++++++++++++++++++ .../cuda/collocation/gaueval_device.hpp | 32 +++++++ src/integrator/cuda/cuda_collocation.cxx | 0 .../cuda/cuda_collocation_kernels.cu | 0 .../cuda/gauxc-cuda_integrator.cmake | 1 + 7 files changed, 190 insertions(+) create mode 100644 include/gauxc/util/div_ceil.hpp create mode 100644 src/integrator/cuda/collocation/gaueval_device.cu create mode 100644 src/integrator/cuda/collocation/gaueval_device.hpp delete mode 100644 src/integrator/cuda/cuda_collocation.cxx delete mode 100644 src/integrator/cuda/cuda_collocation_kernels.cu diff --git a/include/gauxc/gauxc_config.hpp.in b/include/gauxc/gauxc_config.hpp.in index 929237094..e3c2d8330 100644 --- a/include/gauxc/gauxc_config.hpp.in +++ b/include/gauxc/gauxc_config.hpp.in @@ -7,6 +7,10 @@ #else #define HOST_DEVICE_ACCESSIBLE #endif + +#ifdef GAUXC_ENABLE_CUDA + #include +#endif #ifdef GAUXC_ENABLE_CEREAL #include diff --git a/include/gauxc/util/div_ceil.hpp b/include/gauxc/util/div_ceil.hpp new file mode 100644 index 000000000..bd561447b --- /dev/null +++ b/include/gauxc/util/div_ceil.hpp @@ -0,0 +1,67 @@ +#pragma once +#include +#include + +namespace GauXC { + +namespace detail { + +template +struct are_integral; + +template +struct are_integral { + static constexpr bool value = std::is_integral::value and + are_integral::value; +}; + +template +struct are_integral { + static constexpr bool value = std::is_integral::value; +}; + + +template +struct largest; + +template +struct largest< Head, Tail... > { +private: + using tail_type = typename largest::type; +public: + using type = std::conditional_t< + (sizeof(Head) > sizeof(tail_type)), + Head, tail_type >; +}; + +template +struct largest { + using type = T; +}; + +template +using largest_t = typename largest::type; + +} + + +namespace util { + +template +intmax_t div_ceil( Integral1 i, Integral2 j ) { + + static_assert( detail::are_integral::value ); + assert( i >= 0 ); + assert( j > 0 ); + + intmax_t i_us = i; + intmax_t j_us = j; + + auto d = std::div(i_us,j_us); + return d.quot + !!d.rem; + +}; + + +} +} diff --git a/src/integrator/cuda/collocation/gaueval_device.cu b/src/integrator/cuda/collocation/gaueval_device.cu new file mode 100644 index 000000000..1040d91e1 --- /dev/null +++ b/src/integrator/cuda/collocation/gaueval_device.cu @@ -0,0 +1,86 @@ +#include "gaueval_kernels.hpp" +#include + +namespace GauXC { + +template +void gaueval_device( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + cudaStream_t stream +) { + + + dim3 threads(32, 32, 1); + dim3 blocks( util::div_ceil( npts, threads.x ), + util::div_ceil( nshells, threads.y ) ); + + gaueval_device_kernel + <<>> + ( nshells, nbf, npts, shells_device, offs_device, + pts_device, eval_device ); + +} + +template +void gaueval_device( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device, + cudaStream_t stream +); + + +template +void gaueval_device_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + T* deval_device_x, + T* deval_device_y, + T* deval_device_z, + cudaStream_t stream +) { + + dim3 threads(32, 32, 1); + dim3 blocks( util::div_ceil( npts, threads.x ), + util::div_ceil( nshells, threads.y ) ); + + gaueval_device_kernel_deriv1 + <<>> + ( nshells, nbf, npts, shells_device, offs_device, + pts_device, eval_device, deval_device_x, deval_device_y, + deval_device_z ); + +} + +template +void gaueval_device_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device, + double* deval_device_x, + double* deval_device_y, + double* deval_device_z, + cudaStream_t stream +); + + +} diff --git a/src/integrator/cuda/collocation/gaueval_device.hpp b/src/integrator/cuda/collocation/gaueval_device.hpp new file mode 100644 index 000000000..81261d1d7 --- /dev/null +++ b/src/integrator/cuda/collocation/gaueval_device.hpp @@ -0,0 +1,32 @@ +#include + +namespace GauXC { + +template +void gaueval_device( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + cudaStream_t stream +); + +template +void gaueval_device_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + T* deval_device_x, + T* deval_device_y, + T* deval_device_z, + cudaStream_t stream +); + +} diff --git a/src/integrator/cuda/cuda_collocation.cxx b/src/integrator/cuda/cuda_collocation.cxx deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/integrator/cuda/cuda_collocation_kernels.cu b/src/integrator/cuda/cuda_collocation_kernels.cu deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/integrator/cuda/gauxc-cuda_integrator.cmake b/src/integrator/cuda/gauxc-cuda_integrator.cmake index b99024e7c..f6bb272ae 100644 --- a/src/integrator/cuda/gauxc-cuda_integrator.cmake +++ b/src/integrator/cuda/gauxc-cuda_integrator.cmake @@ -1,5 +1,6 @@ add_library( gauxc_device_integrator OBJECT integrator/cuda/collocation/gaueval_kernels.cu + integrator/cuda/collocation/gaueval_device.cu #cuda_integrator/collocation/.cxx ) target_compile_features( gauxc_device_integrator PUBLIC cuda_std_14 cxx_std_14 ) From d1a66e503544bd1e49a17725817bb4f08ea4e0ed Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Wed, 12 Aug 2020 14:17:15 -0400 Subject: [PATCH 08/27] Refactor of integrator inclusion in the build system and redo of namespace parition for host/device integrator utils --- .../default_xc_host_integrator.hpp | 1 + include/gauxc/xc_integrator/xc_host_util.hpp | 4 ++- src/CMakeLists.txt | 25 ++------------- src/integrator/CMakeLists.txt | 32 +++++++++++++++++++ .../collocation/gaueval_angular_cartesian.hpp | 6 +++- .../gaueval_angular_spherical_unnorm.hpp | 6 +++- .../cuda/collocation/gaueval_device.cu | 8 +++-- .../cuda/collocation/gaueval_device.hpp | 8 +++-- .../collocation/gaueval_device_constants.hpp | 6 +++- .../cuda/collocation/gaueval_kernels.cu | 9 ++++-- .../cuda/collocation/gaueval_kernels.hpp | 8 +++-- .../cuda/collocation/gaueval_radial.hpp | 9 ++++-- .../templates/gaueval_angular_template.hpp | 8 +++-- .../gaueval_device_constants_template.hpp | 6 +++- .../cuda/gauxc-cuda_integrator.cmake | 6 ++-- src/integrator/host/host_collocation.cxx | 2 +- src/integrator/host/host_collocation.hpp | 2 +- src/integrator/host/host_weights.cxx | 2 +- src/integrator/host/host_weights.hpp | 2 +- src/integrator/host/host_zmat.cxx | 2 +- src/integrator/host/host_zmat.hpp | 2 +- src/integrator/host/xc_host_util.cxx | 8 ++--- src/{ => integrator}/integrator_common.cxx | 4 ++- src/{ => integrator}/integrator_common.hpp | 5 +-- src/{ => integrator}/integrator_constants.hpp | 4 ++- 25 files changed, 118 insertions(+), 57 deletions(-) create mode 100644 src/integrator/CMakeLists.txt rename src/{ => integrator}/integrator_common.cxx (95%) rename src/{ => integrator}/integrator_common.hpp (85%) rename src/{ => integrator}/integrator_constants.hpp (72%) diff --git a/include/gauxc/xc_integrator/default_xc_host_integrator.hpp b/include/gauxc/xc_integrator/default_xc_host_integrator.hpp index 945d04838..a4d76a407 100644 --- a/include/gauxc/xc_integrator/default_xc_host_integrator.hpp +++ b/include/gauxc/xc_integrator/default_xc_host_integrator.hpp @@ -6,6 +6,7 @@ namespace GauXC { namespace detail { +using namespace GauXC::integrator::host; template diff --git a/include/gauxc/xc_integrator/xc_host_util.hpp b/include/gauxc/xc_integrator/xc_host_util.hpp index 557b8d09f..c7a652ed5 100644 --- a/include/gauxc/xc_integrator/xc_host_util.hpp +++ b/include/gauxc/xc_integrator/xc_host_util.hpp @@ -4,7 +4,8 @@ #include namespace GauXC { -namespace detail { +namespace integrator { +namespace host { template @@ -35,3 +36,4 @@ inline void process_batches_host_replicated_p( size_t n_deriv, Args&&... args ) } } +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 769249e76..f2d0bcccf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,12 +22,6 @@ add_library( gauxc STATIC load_balancer_impl.cxx load_balancer.cxx replicated_load_balancer.cxx - integrator_common.cxx - integrator/host/xc_host_util.cxx - integrator/host/host_weights.cxx - integrator/host/host_collocation.cxx - integrator/host/host_zmat.cxx - integrator/host/blas.cxx ) target_include_directories( gauxc @@ -37,6 +31,8 @@ target_include_directories( gauxc $ ) +add_subdirectory( integrator ) + configure_file( ${PROJECT_SOURCE_DIR}/include/gauxc/gauxc_config.hpp.in ${PROJECT_BINARY_DIR}/include/gauxc/gauxc_config.hpp @@ -54,20 +50,3 @@ if( GAUXC_ENABLE_CEREAL ) target_link_libraries( gauxc PUBLIC cereal ) endif() -if( GAUXC_ENABLE_DEVICE ) - - if( GAUXC_ENABLE_CUDA ) - include( integrator/cuda/gauxc-cuda_integrator.cmake ) - endif() - - get_target_property( DEVICE_TARGET_DEPENDS gauxc_device_integrator - INTERFACE_LINK_LIBRARIES ) - - target_link_libraries( gauxc PUBLIC - $ - ${DEVICE_TARGET_DEPENDS} - ) - - add_dependencies( gauxc gauxc_device_integrator ) - -endif() diff --git a/src/integrator/CMakeLists.txt b/src/integrator/CMakeLists.txt new file mode 100644 index 000000000..8f9ec361f --- /dev/null +++ b/src/integrator/CMakeLists.txt @@ -0,0 +1,32 @@ +target_sources( gauxc + PRIVATE + integrator_common.cxx + host/xc_host_util.cxx + host/host_weights.cxx + host/host_collocation.cxx + host/host_zmat.cxx + host/blas.cxx +) + +target_include_directories( gauxc + PUBLIC + $ +) + +if( GAUXC_ENABLE_DEVICE ) + + if( GAUXC_ENABLE_CUDA ) + include( cuda/gauxc-cuda_integrator.cmake ) + endif() + + get_target_property( DEVICE_TARGET_DEPENDS gauxc_device_integrator + INTERFACE_LINK_LIBRARIES ) + + target_link_libraries( gauxc PUBLIC + $ + ${DEVICE_TARGET_DEPENDS} + ) + + add_dependencies( gauxc gauxc_device_integrator ) + +endif() diff --git a/src/integrator/cuda/collocation/gaueval_angular_cartesian.hpp b/src/integrator/cuda/collocation/gaueval_angular_cartesian.hpp index e931daa4c..aba140999 100644 --- a/src/integrator/cuda/collocation/gaueval_angular_cartesian.hpp +++ b/src/integrator/cuda/collocation/gaueval_angular_cartesian.hpp @@ -6,7 +6,9 @@ # define GPGAUEVAL_INLINE __inline__ #endif -namespace GauXC { +namespace GauXC { +namespace integrator { +namespace cuda { GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_0( const double bf, @@ -273,4 +275,6 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_deriv1( +} // namespace cuda +} // namespace integrator } // namespace GauXC diff --git a/src/integrator/cuda/collocation/gaueval_angular_spherical_unnorm.hpp b/src/integrator/cuda/collocation/gaueval_angular_spherical_unnorm.hpp index 52832cc6e..d835fa8b9 100644 --- a/src/integrator/cuda/collocation/gaueval_angular_spherical_unnorm.hpp +++ b/src/integrator/cuda/collocation/gaueval_angular_spherical_unnorm.hpp @@ -6,7 +6,9 @@ # define GPGAUEVAL_INLINE __inline__ #endif -namespace GauXC { +namespace GauXC { +namespace integrator { +namespace cuda { GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_0( const double bf, @@ -257,4 +259,6 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_deriv1( +} // namespace cuda +} // namespace integrator } // namespace GauXC diff --git a/src/integrator/cuda/collocation/gaueval_device.cu b/src/integrator/cuda/collocation/gaueval_device.cu index 1040d91e1..1e467d614 100644 --- a/src/integrator/cuda/collocation/gaueval_device.cu +++ b/src/integrator/cuda/collocation/gaueval_device.cu @@ -1,7 +1,9 @@ #include "gaueval_kernels.hpp" #include -namespace GauXC { +namespace GauXC { +namespace integrator { +namespace cuda { template void gaueval_device( @@ -83,4 +85,6 @@ void gaueval_device_deriv1( ); -} +} // namespace cuda +} // namespace integrator +} // namespace GauXC diff --git a/src/integrator/cuda/collocation/gaueval_device.hpp b/src/integrator/cuda/collocation/gaueval_device.hpp index 81261d1d7..2a3a617c0 100644 --- a/src/integrator/cuda/collocation/gaueval_device.hpp +++ b/src/integrator/cuda/collocation/gaueval_device.hpp @@ -1,6 +1,8 @@ #include -namespace GauXC { +namespace GauXC { +namespace integrator { +namespace cuda { template void gaueval_device( @@ -29,4 +31,6 @@ void gaueval_device_deriv1( cudaStream_t stream ); -} +} // namespace cuda +} // namespace integrator +} // namespace GauXC diff --git a/src/integrator/cuda/collocation/gaueval_device_constants.hpp b/src/integrator/cuda/collocation/gaueval_device_constants.hpp index 1fae75b8f..586ce98eb 100644 --- a/src/integrator/cuda/collocation/gaueval_device_constants.hpp +++ b/src/integrator/cuda/collocation/gaueval_device_constants.hpp @@ -1,10 +1,14 @@ #pragma once -namespace GauXC { +namespace GauXC { +namespace integrator { +namespace cuda { constexpr double sqrt_10 = 3.1622776601683795; constexpr double sqrt_15 = 3.872983346207417; constexpr double sqrt_3 = 1.7320508075688772; constexpr double sqrt_6 = 2.449489742783178; +} // namespace cuda +} // namespace integrator } // namespace GauXC diff --git a/src/integrator/cuda/collocation/gaueval_kernels.cu b/src/integrator/cuda/collocation/gaueval_kernels.cu index 2b56780bd..7bb2613e5 100644 --- a/src/integrator/cuda/collocation/gaueval_kernels.cu +++ b/src/integrator/cuda/collocation/gaueval_kernels.cu @@ -5,7 +5,10 @@ #include "gaueval_angular_cartesian.hpp" #include "gaueval_angular_spherical_unnorm.hpp" -namespace GauXC { +namespace GauXC { +namespace integrator { +namespace cuda { + template @@ -165,4 +168,6 @@ void gaueval_device_kernel_deriv1( double* deval_device_z ); -} +} // namespace cuda +} // namespace integrator +} // namespace GauXC diff --git a/src/integrator/cuda/collocation/gaueval_kernels.hpp b/src/integrator/cuda/collocation/gaueval_kernels.hpp index 568c083b5..9aaa1b25b 100644 --- a/src/integrator/cuda/collocation/gaueval_kernels.hpp +++ b/src/integrator/cuda/collocation/gaueval_kernels.hpp @@ -1,6 +1,8 @@ #include -namespace GauXC { +namespace GauXC { +namespace integrator { +namespace cuda { template __global__ @@ -29,4 +31,6 @@ void gaueval_device_kernel_deriv1( T* deval_device_z ); -} +} // namespace cuda +} // namespace integrator +} // namespace GauXC diff --git a/src/integrator/cuda/collocation/gaueval_radial.hpp b/src/integrator/cuda/collocation/gaueval_radial.hpp index 39d5f6d37..8222f0ca6 100644 --- a/src/integrator/cuda/collocation/gaueval_radial.hpp +++ b/src/integrator/cuda/collocation/gaueval_radial.hpp @@ -4,7 +4,9 @@ #include -namespace GauXC { +namespace GauXC { +namespace integrator { +namespace cuda { __inline__ __device__ void gaueval_device_radial_eval( const Shell& shell, @@ -76,5 +78,8 @@ __inline__ __device__void gaueval_device_radial_eval_deriv1( } -} +} // namespace cuda +} // namespace integrator +} // namespace GauXC + diff --git a/src/integrator/cuda/collocation/templates/gaueval_angular_template.hpp b/src/integrator/cuda/collocation/templates/gaueval_angular_template.hpp index 441b38ade..b6ac5692b 100644 --- a/src/integrator/cuda/collocation/templates/gaueval_angular_template.hpp +++ b/src/integrator/cuda/collocation/templates/gaueval_angular_template.hpp @@ -6,7 +6,9 @@ # define GPGAUEVAL_INLINE __inline__ #endif -namespace GauXC { +namespace GauXC { +namespace integrator { +namespace cuda { $for( L in range(L_max + 1) )\ GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_$(L)( @@ -98,5 +100,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_deriv1( } // gaueval_$(name)_angular_deriv1 - +} // namespace cuda +} // namespace integrator } // namespace GauXC + diff --git a/src/integrator/cuda/collocation/templates/gaueval_device_constants_template.hpp b/src/integrator/cuda/collocation/templates/gaueval_device_constants_template.hpp index 2339531d3..5245913ee 100644 --- a/src/integrator/cuda/collocation/templates/gaueval_device_constants_template.hpp +++ b/src/integrator/cuda/collocation/templates/gaueval_device_constants_template.hpp @@ -1,9 +1,13 @@ #pragma once -namespace GauXC { +namespace GauXC { +namespace integrator { +namespace cuda { $for( x in const_lines )\ $(x) $endfor\ +} // namespace cuda +} // namespace integrator } // namespace GauXC diff --git a/src/integrator/cuda/gauxc-cuda_integrator.cmake b/src/integrator/cuda/gauxc-cuda_integrator.cmake index f6bb272ae..637621a03 100644 --- a/src/integrator/cuda/gauxc-cuda_integrator.cmake +++ b/src/integrator/cuda/gauxc-cuda_integrator.cmake @@ -1,7 +1,6 @@ add_library( gauxc_device_integrator OBJECT - integrator/cuda/collocation/gaueval_kernels.cu - integrator/cuda/collocation/gaueval_device.cu - #cuda_integrator/collocation/.cxx + cuda/collocation/gaueval_kernels.cu + cuda/collocation/gaueval_device.cu ) target_compile_features( gauxc_device_integrator PUBLIC cuda_std_14 cxx_std_14 ) target_link_libraries( gauxc_device_integrator PUBLIC ExchCXX::exchcxx ) @@ -11,6 +10,7 @@ target_include_directories( gauxc_device_integrator $ $ $ + $ ) target_compile_options( gauxc_device_integrator diff --git a/src/integrator/host/host_collocation.cxx b/src/integrator/host/host_collocation.cxx index d0b2bab8e..7022a7384 100644 --- a/src/integrator/host/host_collocation.cxx +++ b/src/integrator/host/host_collocation.cxx @@ -1,7 +1,7 @@ #include "host_collocation.hpp" #include "gau2grid.h" -namespace GauXC::detail { +namespace GauXC::integrator::host { void eval_collocation( size_t npts, size_t nshells, diff --git a/src/integrator/host/host_collocation.hpp b/src/integrator/host/host_collocation.hpp index 88f1c83e9..536ba26a9 100644 --- a/src/integrator/host/host_collocation.hpp +++ b/src/integrator/host/host_collocation.hpp @@ -2,7 +2,7 @@ #include -namespace GauXC::detail { +namespace GauXC::integrator::host { void eval_collocation( size_t npts, size_t nshells, diff --git a/src/integrator/host/host_weights.cxx b/src/integrator/host/host_weights.cxx index 9f3e6aa41..a3ddd5263 100644 --- a/src/integrator/host/host_weights.cxx +++ b/src/integrator/host/host_weights.cxx @@ -1,7 +1,7 @@ #include "host_weights.hpp" #include -namespace GauXC::detail { +namespace GauXC::integrator::host { void ssf_weights_host( const Molecule& mol, diff --git a/src/integrator/host/host_weights.hpp b/src/integrator/host/host_weights.hpp index d05561ca0..11736de27 100644 --- a/src/integrator/host/host_weights.hpp +++ b/src/integrator/host/host_weights.hpp @@ -2,7 +2,7 @@ #include -namespace GauXC::detail { +namespace GauXC::integrator::host { void partition_weights_host( XCWeightAlg weight_alg, diff --git a/src/integrator/host/host_zmat.cxx b/src/integrator/host/host_zmat.cxx index 29d10811b..8e6c89236 100644 --- a/src/integrator/host/host_zmat.cxx +++ b/src/integrator/host/host_zmat.cxx @@ -2,7 +2,7 @@ #include "blas.hpp" namespace GauXC { -namespace detail { +namespace integrator::host { template void zmat_lda_host( int32_t npts, diff --git a/src/integrator/host/host_zmat.hpp b/src/integrator/host/host_zmat.hpp index bddc24069..f212f4067 100644 --- a/src/integrator/host/host_zmat.hpp +++ b/src/integrator/host/host_zmat.hpp @@ -2,7 +2,7 @@ #include namespace GauXC { -namespace detail { +namespace integrator::host { template void zmat_lda_host( int32_t npts, diff --git a/src/integrator/host/xc_host_util.cxx b/src/integrator/host/xc_host_util.cxx index cf94666d1..79b44131d 100644 --- a/src/integrator/host/xc_host_util.cxx +++ b/src/integrator/host/xc_host_util.cxx @@ -8,7 +8,7 @@ #include "util.hpp" namespace GauXC { -namespace detail { +namespace integrator::host { @@ -88,7 +88,7 @@ void process_batches_host_replicated_p( // Extrat Submatrix const F* den_ptr_use = P; if( nbe != nbf ) { - submat_set( nbf, nbf, nbe, nbe, P, nbf, nbe_scr, nbe, submat_map ); + detail::submat_set( nbf, nbf, nbe, nbe, P, nbf, nbe_scr, nbe, submat_map ); den_ptr_use = nbe_scr; } @@ -150,8 +150,8 @@ void process_batches_host_replicated_p( GauXC::blas::syr2k( 'L', 'N', nbe, npts, F(1.), basis_eval, nbe, zmat, nbe, F(0.), nbe_scr, nbe ); - inc_by_submat( nbf, nbf, nbe, nbe, VXC, nbf, nbe_scr, nbe, - submat_map ); + detail::inc_by_submat( nbf, nbf, nbe, nbe, VXC, nbf, nbe_scr, nbe, + submat_map ); } // Symmetrize diff --git a/src/integrator_common.cxx b/src/integrator/integrator_common.cxx similarity index 95% rename from src/integrator_common.cxx rename to src/integrator/integrator_common.cxx index 99602664a..7b7199600 100644 --- a/src/integrator_common.cxx +++ b/src/integrator/integrator_common.cxx @@ -1,7 +1,8 @@ #include "integrator_common.hpp" -namespace GauXC { +namespace GauXC { +namespace integrator { std::vector< std::pair > gen_compressed_submat_map( const BasisSet& basis, @@ -45,4 +46,5 @@ std::vector< std::pair > +} } diff --git a/src/integrator_common.hpp b/src/integrator/integrator_common.hpp similarity index 85% rename from src/integrator_common.hpp rename to src/integrator/integrator_common.hpp index 48cd4231b..2cfea6f13 100644 --- a/src/integrator_common.hpp +++ b/src/integrator/integrator_common.hpp @@ -3,12 +3,13 @@ #include "integrator_constants.hpp" #include -namespace GauXC { +namespace GauXC { +namespace integrator { std::vector< std::pair > gen_compressed_submat_map( const BasisSet& basis_set, const std::vector< int32_t >& shell_mask ); - +} } diff --git a/src/integrator_constants.hpp b/src/integrator/integrator_constants.hpp similarity index 72% rename from src/integrator_constants.hpp rename to src/integrator/integrator_constants.hpp index 81ef3f29d..f7ee15271 100644 --- a/src/integrator_constants.hpp +++ b/src/integrator/integrator_constants.hpp @@ -1,6 +1,7 @@ #pragma once -namespace GauXC { +namespace GauXC { +namespace integrator { template constexpr F magic_ssf_factor = 0.64; @@ -8,3 +9,4 @@ constexpr F magic_ssf_factor = 0.64; constexpr double ssf_weight_tol = 1e-10; } +} From ec0148e1b97e535d2d52bdb0811921aea1543dc3 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Wed, 12 Aug 2020 18:09:35 -0400 Subject: [PATCH 09/27] Added UTs for host collocation --- tests/CMakeLists.txt | 12 +- tests/collocation.cxx | 154 +++++++++++++++++++ tests/ref_data/water_cc-pVDZ_collocation.bin | Bin 0 -> 319344 bytes 3 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 tests/collocation.cxx create mode 100644 tests/ref_data/water_cc-pVDZ_collocation.bin diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 50bb099a7..e7405acd2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -30,7 +30,17 @@ endif() #add_executable( gauxc_grid_test grid_test.cxx ) #target_link_libraries( gauxc_grid_test PUBLIC gauxc catch2_main ) -add_executable( gauxc_test ut_main.cxx moltypes_test.cxx grid_test.cxx molgrid_test.cxx basisset_test.cxx load_balancer_test.cxx xc_integrator.cxx standards.cxx ) +add_executable( gauxc_test + ut_main.cxx + moltypes_test.cxx + grid_test.cxx + molgrid_test.cxx + basisset_test.cxx + load_balancer_test.cxx + xc_integrator.cxx + collocation.cxx + standards.cxx +) target_link_libraries( gauxc_test PUBLIC gauxc gauxc_catch2 ) diff --git a/tests/collocation.cxx b/tests/collocation.cxx new file mode 100644 index 000000000..47873e1df --- /dev/null +++ b/tests/collocation.cxx @@ -0,0 +1,154 @@ +#include "ut_common.hpp" +#include +#include +#include +#include +#include + +#include "host/host_collocation.hpp" + +using namespace GauXC; + + +struct ref_collocation_data { + std::vector mask; + std::vector> pts; + std::vector eval; + std::vector deval_x; + std::vector deval_y; + std::vector deval_z; + + template + void serialize( Archive& ar ) { + ar( mask, pts, eval, deval_x, deval_y, deval_z ); + } + +}; + +void generate_collocation_data( const Molecule& mol, const BasisSet& basis, + std::ofstream& out_file, size_t ntask_save = 10 ) { + + + MolGrid mg(AtomicGridSizeDefault::FineGrid, mol); + LoadBalancer lb(MPI_COMM_WORLD, mol, mg, basis); + auto& tasks = lb.get_tasks(); + + + std::vector< ref_collocation_data > ref_data; + + for( size_t i = 0; i < ntask_save; ++i ) { + auto& task = tasks[i]; + + auto& pts = task.points; + auto& mask = task.shell_list; + + // Only keep first 67 points to save on space + if( task.points.size() > 67 ) + task.points.erase( task.points.begin() + 67, task.points.end() ); + + const auto npts = task.points.size(); + const auto nbf = task.nbe; + + std::vector eval ( nbf * npts ), + deval_x( nbf * npts ), + deval_y( nbf * npts ), + deval_z( nbf * npts ); + + integrator::host::eval_collocation_deriv1( npts, mask.size(), nbf, + pts.data()->data(), basis, + mask.data(), + eval.data(), deval_x.data(), + deval_y.data(), deval_z.data() ); + + auto max_abs = *std::max_element( eval.begin(), eval.end(), + [](auto a, auto b){ return std::abs(a) < std::abs(b); } ); + if( std::abs(max_abs) < 1e-9 ) continue; + + ref_collocation_data d{ std::move(mask), std::move(pts), std::move(eval), + std::move(deval_x), std::move(deval_y), + std::move(deval_z) }; + + ref_data.emplace_back( std::move(d) ); + + } + + { + cereal::BinaryOutputArchive ar( out_file ); + ar( ref_data ); + } + +} + + + +void test_host_collocation( const BasisSet& basis, std::ifstream& in_file) { + + + + std::vector ref_data; + + { + cereal::BinaryInputArchive ar( in_file ); + ar( ref_data ); + } + + for( auto& d : ref_data ) { + + const auto npts = d.pts.size(); + const auto nbf = d.eval.size() / npts; + + const auto& mask = d.mask; + const auto& pts = d.pts; + + std::vector eval ( nbf * npts ), + deval_x( nbf * npts ), + deval_y( nbf * npts ), + deval_z( nbf * npts ); + + + integrator::host::eval_collocation_deriv1( npts, mask.size(), nbf, + pts.data()->data(), basis, + mask.data(), + eval.data(), deval_x.data(), + deval_y.data(), deval_z.data() ); + + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( eval[i] == Approx( d.eval[i] ) ); + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( deval_x[i] == Approx( d.deval_x[i] ) ); + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( deval_y[i] == Approx( d.deval_y[i] ) ); + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( deval_z[i] == Approx( d.deval_z[i] ) ); + } + +} + +//#define GENERATE_TESTS + +TEST_CASE( "Water / cc-pVDZ", "[collocation]" ) { + +#ifdef GENERATE_TESTS + int world_size; + MPI_Comm_size( MPI_COMM_WORLD, &world_size ); + if( world_size > 1 ) return; +#endif + + Molecule mol = make_water(); + BasisSet basis = make_ccpvdz( mol, SphericalType(true) ); + + for( auto& sh : basis ) sh.set_shell_tolerance( 1e-6 ); + +#ifdef GENERATE_TESTS + std::ofstream ref_data( "water_cc-pVDZ_collocation.bin", std::ios::binary ); + generate_collocation_data( mol, basis, ref_data ); +#else + std::ifstream ref_data( GAUXC_REF_DATA_PATH "/water_cc-pVDZ_collocation.bin", + std::ios::binary ); + + SECTION( "Host Eval" ) { + test_host_collocation( basis, ref_data ); + } +#endif + +} diff --git a/tests/ref_data/water_cc-pVDZ_collocation.bin b/tests/ref_data/water_cc-pVDZ_collocation.bin new file mode 100644 index 0000000000000000000000000000000000000000..0b1c5f933415c5046ad4942ba9c5871b0e6b3b08 GIT binary patch literal 319344 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%0DRk9G4I0@2m~cQm+Ea6dzZ< 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 literal 0 HcmV?d00001 From 2671c1556e1acb55e325a64997c6eaf55b4df0bf Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Wed, 12 Aug 2020 20:03:50 -0400 Subject: [PATCH 10/27] gaueval -> collocation, added UTs for CUDA collocation --- ....hpp => collocation_angular_cartesian.hpp} | 50 +++--- ... collocation_angular_spherical_unnorm.hpp} | 50 +++--- ...aueval_device.cu => collocation_device.cu} | 14 +- ...s.hpp => collocation_device_constants.hpp} | 0 ...eval_kernels.cu => collocation_kernels.cu} | 22 +-- ...al_kernels.hpp => collocation_kernels.hpp} | 4 +- ...eval_radial.hpp => collocation_radial.hpp} | 4 +- ...eval_angular.py => collocation_angular.py} | 0 ...y => generate_collocation_angular_eval.py} | 44 +++--- ...e.hpp => collocation_angular_template.hpp} | 20 +-- ...collocation_device_constants_template.hpp} | 0 ...eval_device.hpp => collocation_device.hpp} | 4 +- .../cuda/gauxc-cuda_integrator.cmake | 4 +- tests/collocation.cxx | 146 +++++++++++++++++- 14 files changed, 251 insertions(+), 111 deletions(-) rename src/integrator/cuda/collocation/{gaueval_angular_cartesian.hpp => collocation_angular_cartesian.hpp} (71%) rename src/integrator/cuda/collocation/{gaueval_angular_spherical_unnorm.hpp => collocation_angular_spherical_unnorm.hpp} (71%) rename src/integrator/cuda/collocation/{gaueval_device.cu => collocation_device.cu} (90%) rename src/integrator/cuda/collocation/{gaueval_device_constants.hpp => collocation_device_constants.hpp} (100%) rename src/integrator/cuda/collocation/{gaueval_kernels.cu => collocation_kernels.cu} (86%) rename src/integrator/cuda/collocation/{gaueval_kernels.hpp => collocation_kernels.hpp} (91%) rename src/integrator/cuda/collocation/{gaueval_radial.hpp => collocation_radial.hpp} (93%) rename src/integrator/cuda/collocation/scripts/{gaueval_angular.py => collocation_angular.py} (100%) rename src/integrator/cuda/collocation/scripts/{generate_gaueval_angular_eval.py => generate_collocation_angular_eval.py} (68%) rename src/integrator/cuda/collocation/templates/{gaueval_angular_template.hpp => collocation_angular_template.hpp} (69%) rename src/integrator/cuda/collocation/templates/{gaueval_device_constants_template.hpp => collocation_device_constants_template.hpp} (100%) rename src/integrator/cuda/{collocation/gaueval_device.hpp => collocation_device.hpp} (93%) diff --git a/src/integrator/cuda/collocation/gaueval_angular_cartesian.hpp b/src/integrator/cuda/collocation/collocation_angular_cartesian.hpp similarity index 71% rename from src/integrator/cuda/collocation/gaueval_angular_cartesian.hpp rename to src/integrator/cuda/collocation/collocation_angular_cartesian.hpp index aba140999..865a9e407 100644 --- a/src/integrator/cuda/collocation/gaueval_angular_cartesian.hpp +++ b/src/integrator/cuda/collocation/collocation_angular_cartesian.hpp @@ -1,5 +1,5 @@ #pragma once -#include "gaueval_device_constants.hpp" +#include "collocation_device_constants.hpp" #include #ifndef GPGAUEVAL_INLINE @@ -10,7 +10,7 @@ namespace GauXC { namespace integrator { namespace cuda { -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_0( +GPGAUEVAL_INLINE __device__ void collocation_cartesian_angular_0( const double bf, const double x, const double y, @@ -22,7 +22,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_0( } -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_0_deriv1( +GPGAUEVAL_INLINE __device__ void collocation_cartesian_angular_0_deriv1( const double bf, const double bf_x, const double bf_y, @@ -43,7 +43,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_0_deriv1( } -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_1( +GPGAUEVAL_INLINE __device__ void collocation_cartesian_angular_1( const double bf, const double x, const double y, @@ -57,7 +57,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_1( } -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_1_deriv1( +GPGAUEVAL_INLINE __device__ void collocation_cartesian_angular_1_deriv1( const double bf, const double bf_x, const double bf_y, @@ -84,7 +84,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_1_deriv1( } -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_2( +GPGAUEVAL_INLINE __device__ void collocation_cartesian_angular_2( const double bf, const double x, const double y, @@ -101,7 +101,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_2( } -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_2_deriv1( +GPGAUEVAL_INLINE __device__ void collocation_cartesian_angular_2_deriv1( const double bf, const double bf_x, const double bf_y, @@ -137,7 +137,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_2_deriv1( } -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_3( +GPGAUEVAL_INLINE __device__ void collocation_cartesian_angular_3( const double bf, const double x, const double y, @@ -158,7 +158,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_3( } -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_3_deriv1( +GPGAUEVAL_INLINE __device__ void collocation_cartesian_angular_3_deriv1( const double bf, const double bf_x, const double bf_y, @@ -207,7 +207,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_3_deriv1( } -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular( +GPGAUEVAL_INLINE __device__ void collocation_cartesian_angular( const int64_t l, const double bf, const double x, @@ -217,25 +217,25 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular( ) { if( l == 0 ) { - gaueval_cartesian_angular_0( bf, x, y, z, eval ); + collocation_cartesian_angular_0( bf, x, y, z, eval ); } else if( l == 1 ) { - gaueval_cartesian_angular_1( bf, x, y, z, eval ); + collocation_cartesian_angular_1( bf, x, y, z, eval ); } else if( l == 2 ) { - gaueval_cartesian_angular_2( bf, x, y, z, eval ); + collocation_cartesian_angular_2( bf, x, y, z, eval ); } else if( l == 3 ) { - gaueval_cartesian_angular_3( bf, x, y, z, eval ); + collocation_cartesian_angular_3( bf, x, y, z, eval ); } else { assert( false && "L < L_MAX" ); } -} // gaueval_cartesian_angular +} // collocation_cartesian_angular -GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_deriv1( +GPGAUEVAL_INLINE __device__ void collocation_cartesian_angular_deriv1( const int64_t l, const double bf, const double bf_x, @@ -252,26 +252,26 @@ GPGAUEVAL_INLINE __device__ void gaueval_cartesian_angular_deriv1( if( l == 0 ) { - gaueval_cartesian_angular_0( bf, x, y, z, eval ); - gaueval_cartesian_angular_0_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + collocation_cartesian_angular_0( bf, x, y, z, eval ); + collocation_cartesian_angular_0_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); } else if( l == 1 ) { - gaueval_cartesian_angular_1( bf, x, y, z, eval ); - gaueval_cartesian_angular_1_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + collocation_cartesian_angular_1( bf, x, y, z, eval ); + collocation_cartesian_angular_1_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); } else if( l == 2 ) { - gaueval_cartesian_angular_2( bf, x, y, z, eval ); - gaueval_cartesian_angular_2_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + collocation_cartesian_angular_2( bf, x, y, z, eval ); + collocation_cartesian_angular_2_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); } else if( l == 3 ) { - gaueval_cartesian_angular_3( bf, x, y, z, eval ); - gaueval_cartesian_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + collocation_cartesian_angular_3( bf, x, y, z, eval ); + collocation_cartesian_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); } else { assert( false && "L < L_MAX" ); } -} // gaueval_cartesian_angular_deriv1 +} // collocation_cartesian_angular_deriv1 diff --git a/src/integrator/cuda/collocation/gaueval_angular_spherical_unnorm.hpp b/src/integrator/cuda/collocation/collocation_angular_spherical_unnorm.hpp similarity index 71% rename from src/integrator/cuda/collocation/gaueval_angular_spherical_unnorm.hpp rename to src/integrator/cuda/collocation/collocation_angular_spherical_unnorm.hpp index d835fa8b9..befc8e392 100644 --- a/src/integrator/cuda/collocation/gaueval_angular_spherical_unnorm.hpp +++ b/src/integrator/cuda/collocation/collocation_angular_spherical_unnorm.hpp @@ -1,5 +1,5 @@ #pragma once -#include "gaueval_device_constants.hpp" +#include "collocation_device_constants.hpp" #include #ifndef GPGAUEVAL_INLINE @@ -10,7 +10,7 @@ namespace GauXC { namespace integrator { namespace cuda { -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_0( +GPGAUEVAL_INLINE __device__ void collocation_spherical_unnorm_angular_0( const double bf, const double x, const double y, @@ -22,7 +22,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_0( } -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_0_deriv1( +GPGAUEVAL_INLINE __device__ void collocation_spherical_unnorm_angular_0_deriv1( const double bf, const double bf_x, const double bf_y, @@ -43,7 +43,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_0_deriv1( } -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_1( +GPGAUEVAL_INLINE __device__ void collocation_spherical_unnorm_angular_1( const double bf, const double x, const double y, @@ -57,7 +57,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_1( } -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_1_deriv1( +GPGAUEVAL_INLINE __device__ void collocation_spherical_unnorm_angular_1_deriv1( const double bf, const double bf_x, const double bf_y, @@ -84,7 +84,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_1_deriv1( } -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_2( +GPGAUEVAL_INLINE __device__ void collocation_spherical_unnorm_angular_2( const double bf, const double x, const double y, @@ -100,7 +100,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_2( } -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_2_deriv1( +GPGAUEVAL_INLINE __device__ void collocation_spherical_unnorm_angular_2_deriv1( const double bf, const double bf_x, const double bf_y, @@ -133,7 +133,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_2_deriv1( } -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_3( +GPGAUEVAL_INLINE __device__ void collocation_spherical_unnorm_angular_3( const double bf, const double x, const double y, @@ -151,7 +151,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_3( } -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_3_deriv1( +GPGAUEVAL_INLINE __device__ void collocation_spherical_unnorm_angular_3_deriv1( const double bf, const double bf_x, const double bf_y, @@ -191,7 +191,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_3_deriv1( } -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular( +GPGAUEVAL_INLINE __device__ void collocation_spherical_unnorm_angular( const int64_t l, const double bf, const double x, @@ -201,25 +201,25 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular( ) { if( l == 0 ) { - gaueval_spherical_unnorm_angular_0( bf, x, y, z, eval ); + collocation_spherical_unnorm_angular_0( bf, x, y, z, eval ); } else if( l == 1 ) { - gaueval_spherical_unnorm_angular_1( bf, x, y, z, eval ); + collocation_spherical_unnorm_angular_1( bf, x, y, z, eval ); } else if( l == 2 ) { - gaueval_spherical_unnorm_angular_2( bf, x, y, z, eval ); + collocation_spherical_unnorm_angular_2( bf, x, y, z, eval ); } else if( l == 3 ) { - gaueval_spherical_unnorm_angular_3( bf, x, y, z, eval ); + collocation_spherical_unnorm_angular_3( bf, x, y, z, eval ); } else { assert( false && "L < L_MAX" ); } -} // gaueval_spherical_unnorm_angular +} // collocation_spherical_unnorm_angular -GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_deriv1( +GPGAUEVAL_INLINE __device__ void collocation_spherical_unnorm_angular_deriv1( const int64_t l, const double bf, const double bf_x, @@ -236,26 +236,26 @@ GPGAUEVAL_INLINE __device__ void gaueval_spherical_unnorm_angular_deriv1( if( l == 0 ) { - gaueval_spherical_unnorm_angular_0( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_0_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + collocation_spherical_unnorm_angular_0( bf, x, y, z, eval ); + collocation_spherical_unnorm_angular_0_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); } else if( l == 1 ) { - gaueval_spherical_unnorm_angular_1( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_1_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + collocation_spherical_unnorm_angular_1( bf, x, y, z, eval ); + collocation_spherical_unnorm_angular_1_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); } else if( l == 2 ) { - gaueval_spherical_unnorm_angular_2( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_2_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + collocation_spherical_unnorm_angular_2( bf, x, y, z, eval ); + collocation_spherical_unnorm_angular_2_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); } else if( l == 3 ) { - gaueval_spherical_unnorm_angular_3( bf, x, y, z, eval ); - gaueval_spherical_unnorm_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + collocation_spherical_unnorm_angular_3( bf, x, y, z, eval ); + collocation_spherical_unnorm_angular_3_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); } else { assert( false && "L < L_MAX" ); } -} // gaueval_spherical_unnorm_angular_deriv1 +} // collocation_spherical_unnorm_angular_deriv1 diff --git a/src/integrator/cuda/collocation/gaueval_device.cu b/src/integrator/cuda/collocation/collocation_device.cu similarity index 90% rename from src/integrator/cuda/collocation/gaueval_device.cu rename to src/integrator/cuda/collocation/collocation_device.cu index 1e467d614..ebd880ac0 100644 --- a/src/integrator/cuda/collocation/gaueval_device.cu +++ b/src/integrator/cuda/collocation/collocation_device.cu @@ -1,4 +1,4 @@ -#include "gaueval_kernels.hpp" +#include "collocation_kernels.hpp" #include namespace GauXC { @@ -6,7 +6,7 @@ namespace integrator { namespace cuda { template -void gaueval_device( +void eval_collocation( size_t nshells, size_t nbf, size_t npts, @@ -22,7 +22,7 @@ void gaueval_device( dim3 blocks( util::div_ceil( npts, threads.x ), util::div_ceil( nshells, threads.y ) ); - gaueval_device_kernel + collocation_device_kernel <<>> ( nshells, nbf, npts, shells_device, offs_device, pts_device, eval_device ); @@ -30,7 +30,7 @@ void gaueval_device( } template -void gaueval_device( +void eval_collocation( size_t nshells, size_t nbf, size_t npts, @@ -43,7 +43,7 @@ void gaueval_device( template -void gaueval_device_deriv1( +void eval_collocation_deriv1( size_t nshells, size_t nbf, size_t npts, @@ -61,7 +61,7 @@ void gaueval_device_deriv1( dim3 blocks( util::div_ceil( npts, threads.x ), util::div_ceil( nshells, threads.y ) ); - gaueval_device_kernel_deriv1 + collocation_device_kernel_deriv1 <<>> ( nshells, nbf, npts, shells_device, offs_device, pts_device, eval_device, deval_device_x, deval_device_y, @@ -70,7 +70,7 @@ void gaueval_device_deriv1( } template -void gaueval_device_deriv1( +void eval_collocation_deriv1( size_t nshells, size_t nbf, size_t npts, diff --git a/src/integrator/cuda/collocation/gaueval_device_constants.hpp b/src/integrator/cuda/collocation/collocation_device_constants.hpp similarity index 100% rename from src/integrator/cuda/collocation/gaueval_device_constants.hpp rename to src/integrator/cuda/collocation/collocation_device_constants.hpp diff --git a/src/integrator/cuda/collocation/gaueval_kernels.cu b/src/integrator/cuda/collocation/collocation_kernels.cu similarity index 86% rename from src/integrator/cuda/collocation/gaueval_kernels.cu rename to src/integrator/cuda/collocation/collocation_kernels.cu index 7bb2613e5..7e40981ca 100644 --- a/src/integrator/cuda/collocation/gaueval_kernels.cu +++ b/src/integrator/cuda/collocation/collocation_kernels.cu @@ -1,9 +1,9 @@ #include #include -#include "gaueval_kernels.hpp" -#include "gaueval_angular_cartesian.hpp" -#include "gaueval_angular_spherical_unnorm.hpp" +#include "collocation_kernels.hpp" +#include "collocation_angular_cartesian.hpp" +#include "collocation_angular_spherical_unnorm.hpp" namespace GauXC { namespace integrator { @@ -13,7 +13,7 @@ namespace cuda { template __global__ -void gaueval_device_kernel( +void collocation_device_kernel( size_t nshells, size_t nbf, size_t npts, @@ -56,9 +56,9 @@ void gaueval_device_kernel( const bool do_sph = shell.pure(); if( do_sph ) - gaueval_spherical_unnorm_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); + collocation_spherical_unnorm_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); else - gaueval_cartesian_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); + collocation_cartesian_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); } @@ -66,7 +66,7 @@ void gaueval_device_kernel( template __global__ -void gaueval_device_kernel( +void collocation_device_kernel( size_t nshells, size_t nbf, size_t npts, @@ -79,7 +79,7 @@ void gaueval_device_kernel( template __global__ -void gaueval_device_kernel_deriv1( +void collocation_device_kernel_deriv1( size_t nshells, size_t nbf, size_t npts, @@ -140,11 +140,11 @@ void gaueval_device_kernel_deriv1( const bool do_sph = shell.pure(); if( do_sph ) - gaueval_spherical_unnorm_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, + collocation_spherical_unnorm_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, xc, yc, zc, bf_eval, dx_eval, dy_eval, dz_eval ); else - gaueval_cartesian_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, + collocation_cartesian_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, xc, yc, zc, bf_eval, dx_eval, dy_eval, dz_eval ); @@ -155,7 +155,7 @@ void gaueval_device_kernel_deriv1( template __global__ -void gaueval_device_kernel_deriv1( +void collocation_device_kernel_deriv1( size_t nshells, size_t nbf, size_t npts, diff --git a/src/integrator/cuda/collocation/gaueval_kernels.hpp b/src/integrator/cuda/collocation/collocation_kernels.hpp similarity index 91% rename from src/integrator/cuda/collocation/gaueval_kernels.hpp rename to src/integrator/cuda/collocation/collocation_kernels.hpp index 9aaa1b25b..ba45b051c 100644 --- a/src/integrator/cuda/collocation/gaueval_kernels.hpp +++ b/src/integrator/cuda/collocation/collocation_kernels.hpp @@ -6,7 +6,7 @@ namespace cuda { template __global__ -void gaueval_device_kernel( +void collocation_device_kernel( size_t nshells, size_t nbf, size_t npts, @@ -18,7 +18,7 @@ void gaueval_device_kernel( template __global__ -void gaueval_device_kernel_deriv1( +void collocation_device_kernel_deriv1( size_t nshells, size_t nbf, size_t npts, diff --git a/src/integrator/cuda/collocation/gaueval_radial.hpp b/src/integrator/cuda/collocation/collocation_radial.hpp similarity index 93% rename from src/integrator/cuda/collocation/gaueval_radial.hpp rename to src/integrator/cuda/collocation/collocation_radial.hpp index 8222f0ca6..4a7aa54a3 100644 --- a/src/integrator/cuda/collocation/gaueval_radial.hpp +++ b/src/integrator/cuda/collocation/collocation_radial.hpp @@ -8,7 +8,7 @@ namespace GauXC { namespace integrator { namespace cuda { -__inline__ __device__ void gaueval_device_radial_eval( +__inline__ __device__ void collocation_device_radial_eval( const Shell& shell, const double* pt, double* eval_device @@ -35,7 +35,7 @@ __inline__ __device__ void gaueval_device_radial_eval( -__inline__ __device__void gaueval_device_radial_eval_deriv1( +__inline__ __device__void collocation_device_radial_eval_deriv1( const Shell& shell, const double* pt, double* eval_device, diff --git a/src/integrator/cuda/collocation/scripts/gaueval_angular.py b/src/integrator/cuda/collocation/scripts/collocation_angular.py similarity index 100% rename from src/integrator/cuda/collocation/scripts/gaueval_angular.py rename to src/integrator/cuda/collocation/scripts/collocation_angular.py diff --git a/src/integrator/cuda/collocation/scripts/generate_gaueval_angular_eval.py b/src/integrator/cuda/collocation/scripts/generate_collocation_angular_eval.py similarity index 68% rename from src/integrator/cuda/collocation/scripts/generate_gaueval_angular_eval.py rename to src/integrator/cuda/collocation/scripts/generate_collocation_angular_eval.py index e59b47149..bc46262bb 100644 --- a/src/integrator/cuda/collocation/scripts/generate_gaueval_angular_eval.py +++ b/src/integrator/cuda/collocation/scripts/generate_collocation_angular_eval.py @@ -1,6 +1,6 @@ import os,sys,re,math import pyexpander.lib as expander -from gaueval_angular import generate_spherical_angular, generate_cartesian_angular, generate_cartesian_ls, generate_eval_lines +from collocation_angular import generate_spherical_angular, generate_cartesian_angular, generate_cartesian_ls, generate_eval_lines from io import StringIO @@ -42,7 +42,7 @@ -template_fname = 'templates/gaueval_angular_template.hpp' +template_fname = 'templates/collocation_angular_template.hpp' #sphr_var_dict = { 'L_max' : L_max, 'body' : sphr_bf_body, 'body_d1' : sphr_bf_d1_body, 'name' : 'spherical' } sphr_unnorm_var_dict = { 'L_max' : L_max, 'body' : sphr_unnorm_bf_body, 'body_d1' : sphr_unnorm_bf_d1_body, 'name' : 'spherical_unnorm' } @@ -90,7 +90,7 @@ old_sys_out = sys.stdout sys.stdout = constant_expand = StringIO() -expander.expandFile( 'templates/gaueval_device_constants_template.hpp', +expander.expandFile( 'templates/collocation_device_constants_template.hpp', external_definitions={ "const_lines" : constant_lines } ) sys.stdout = old_sys_out @@ -98,10 +98,10 @@ constant_expand = constant_expand.getvalue() -cart_header_fname = "gaueval_angular_cartesian.hpp" -#sphr_header_fname = "gaueval_angular_spherical.hpp" -sphr_unnorm_header_fname = "gaueval_angular_spherical_unnorm.hpp" -cons_header_fname = "gaueval_device_constants.hpp" +cart_header_fname = "collocation_angular_cartesian.hpp" +#sphr_header_fname = "collocation_angular_spherical.hpp" +sphr_unnorm_header_fname = "collocation_angular_spherical_unnorm.hpp" +cons_header_fname = "collocation_device_constants.hpp" cart_header_file = open( cart_header_fname, 'w' ) #sphr_header_file = open( sphr_header_fname, 'w' ) @@ -125,24 +125,24 @@ #old_sys_out = sys.stdout -#sys.stdout = gaueval_cartesian_kernel_expand = StringIO() -#expander.expandFile( 'gaueval_kernels_template.cu', external_definitions={ 'ang_name' : 'cartesian' } ) +#sys.stdout = collocation_cartesian_kernel_expand = StringIO() +#expander.expandFile( 'collocation_kernels_template.cu', external_definitions={ 'ang_name' : 'cartesian' } ) # -#sys.stdout = gaueval_spherical_kernel_expand = StringIO() -#expander.expandFile( 'gaueval_kernels_template.cu', external_definitions={ 'ang_name' : 'spherical' } ) +#sys.stdout = collocation_spherical_kernel_expand = StringIO() +#expander.expandFile( 'collocation_kernels_template.cu', external_definitions={ 'ang_name' : 'spherical' } ) # -#sys.stdout = gaueval_spherical_unnorm_kernel_expand = StringIO() -#expander.expandFile( 'gaueval_kernels_template.cu', external_definitions={ 'ang_name' : 'spherical_unnorm' } ) +#sys.stdout = collocation_spherical_unnorm_kernel_expand = StringIO() +#expander.expandFile( 'collocation_kernels_template.cu', external_definitions={ 'ang_name' : 'spherical_unnorm' } ) # #sys.stdout = old_sys_out # -#gaueval_cartesian_kernel_expand = gaueval_cartesian_kernel_expand.getvalue() -#gaueval_spherical_kernel_expand = gaueval_spherical_kernel_expand.getvalue() -#gaueval_spherical_unnorm_kernel_expand = gaueval_spherical_unnorm_kernel_expand.getvalue() +#collocation_cartesian_kernel_expand = collocation_cartesian_kernel_expand.getvalue() +#collocation_spherical_kernel_expand = collocation_spherical_kernel_expand.getvalue() +#collocation_spherical_unnorm_kernel_expand = collocation_spherical_unnorm_kernel_expand.getvalue() # -#with open( 'gaueval_kernels_cartesian.cu', 'w' ) as f: -# f.write( gaueval_cartesian_kernel_expand ) -#with open( 'gaueval_kernels_spherical.cu', 'w' ) as f: -# f.write( gaueval_spherical_kernel_expand ) -#with open( 'gaueval_kernels_spherical_unnorm.cu', 'w' ) as f: -# f.write( gaueval_spherical_unnorm_kernel_expand ) +#with open( 'collocation_kernels_cartesian.cu', 'w' ) as f: +# f.write( collocation_cartesian_kernel_expand ) +#with open( 'collocation_kernels_spherical.cu', 'w' ) as f: +# f.write( collocation_spherical_kernel_expand ) +#with open( 'collocation_kernels_spherical_unnorm.cu', 'w' ) as f: +# f.write( collocation_spherical_unnorm_kernel_expand ) diff --git a/src/integrator/cuda/collocation/templates/gaueval_angular_template.hpp b/src/integrator/cuda/collocation/templates/collocation_angular_template.hpp similarity index 69% rename from src/integrator/cuda/collocation/templates/gaueval_angular_template.hpp rename to src/integrator/cuda/collocation/templates/collocation_angular_template.hpp index b6ac5692b..fdb272abb 100644 --- a/src/integrator/cuda/collocation/templates/gaueval_angular_template.hpp +++ b/src/integrator/cuda/collocation/templates/collocation_angular_template.hpp @@ -1,5 +1,5 @@ #pragma once -#include "gaueval_device_constants.hpp" +#include "collocation_device_constants.hpp" #include #ifndef GPGAUEVAL_INLINE @@ -11,7 +11,7 @@ namespace integrator { namespace cuda { $for( L in range(L_max + 1) )\ -GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_$(L)( +GPGAUEVAL_INLINE __device__ void collocation_$(name)_angular_$(L)( const double bf, const double x, const double y, @@ -23,7 +23,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_$(L)( } -GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_$(L)_deriv1( +GPGAUEVAL_INLINE __device__ void collocation_$(name)_angular_$(L)_deriv1( const double bf, const double bf_x, const double bf_y, @@ -42,7 +42,7 @@ GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_$(L)_deriv1( $endfor\ -GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular( +GPGAUEVAL_INLINE __device__ void collocation_$(name)_angular( const int64_t l, const double bf, const double x, @@ -57,17 +57,17 @@ GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular( $else\ } else if( l == $(L) ) { $endif\ - gaueval_$(name)_angular_$(L)( bf, x, y, z, eval ); + collocation_$(name)_angular_$(L)( bf, x, y, z, eval ); $endfor\ } else { assert( false && "L < L_MAX" ); } -} // gaueval_$(name)_angular +} // collocation_$(name)_angular -GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_deriv1( +GPGAUEVAL_INLINE __device__ void collocation_$(name)_angular_deriv1( const int64_t l, const double bf, const double bf_x, @@ -89,15 +89,15 @@ GPGAUEVAL_INLINE __device__ void gaueval_$(name)_angular_deriv1( $else\ } else if( l == $(L) ) { $endif\ - gaueval_$(name)_angular_$(L)( bf, x, y, z, eval ); - gaueval_$(name)_angular_$(L)_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); + collocation_$(name)_angular_$(L)( bf, x, y, z, eval ); + collocation_$(name)_angular_$(L)_deriv1( bf, bf_x, bf_y, bf_z, x, y, z, eval_x, eval_y, eval_z ); $endfor\ } else { assert( false && "L < L_MAX" ); } -} // gaueval_$(name)_angular_deriv1 +} // collocation_$(name)_angular_deriv1 } // namespace cuda diff --git a/src/integrator/cuda/collocation/templates/gaueval_device_constants_template.hpp b/src/integrator/cuda/collocation/templates/collocation_device_constants_template.hpp similarity index 100% rename from src/integrator/cuda/collocation/templates/gaueval_device_constants_template.hpp rename to src/integrator/cuda/collocation/templates/collocation_device_constants_template.hpp diff --git a/src/integrator/cuda/collocation/gaueval_device.hpp b/src/integrator/cuda/collocation_device.hpp similarity index 93% rename from src/integrator/cuda/collocation/gaueval_device.hpp rename to src/integrator/cuda/collocation_device.hpp index 2a3a617c0..dba402419 100644 --- a/src/integrator/cuda/collocation/gaueval_device.hpp +++ b/src/integrator/cuda/collocation_device.hpp @@ -5,7 +5,7 @@ namespace integrator { namespace cuda { template -void gaueval_device( +void eval_collocation( size_t nshells, size_t nbf, size_t npts, @@ -17,7 +17,7 @@ void gaueval_device( ); template -void gaueval_device_deriv1( +void eval_collocation_deriv1( size_t nshells, size_t nbf, size_t npts, diff --git a/src/integrator/cuda/gauxc-cuda_integrator.cmake b/src/integrator/cuda/gauxc-cuda_integrator.cmake index 637621a03..eb6ad1967 100644 --- a/src/integrator/cuda/gauxc-cuda_integrator.cmake +++ b/src/integrator/cuda/gauxc-cuda_integrator.cmake @@ -1,6 +1,6 @@ add_library( gauxc_device_integrator OBJECT - cuda/collocation/gaueval_kernels.cu - cuda/collocation/gaueval_device.cu + cuda/collocation/collocation_kernels.cu + cuda/collocation/collocation_device.cu ) target_compile_features( gauxc_device_integrator PUBLIC cuda_std_14 cxx_std_14 ) target_link_libraries( gauxc_device_integrator PUBLIC ExchCXX::exchcxx ) diff --git a/tests/collocation.cxx b/tests/collocation.cxx index 47873e1df..1f27c28aa 100644 --- a/tests/collocation.cxx +++ b/tests/collocation.cxx @@ -3,10 +3,67 @@ #include #include #include +#include #include + +#define MAX_NPTS_CHECK 67 + + #include "host/host_collocation.hpp" + + +#ifdef GAUXC_ENABLE_CUDA + +#include "cuda/collocation_device.hpp" +template +inline T* safe_cuda_malloc( size_t n ) { + + T* ptr; + auto stat = cudaMalloc( (void**)&ptr, n * sizeof(T) ); + if( stat != cudaSuccess ) + throw std::runtime_error(std::string("CUDA Malloc Failed: ") + + cudaGetErrorString(stat)); + + return ptr; +} + +template +inline void safe_cuda_free( T*& ptr ) { + auto stat = cudaFree( (void*)ptr ); + if( stat != cudaSuccess ) + throw std::runtime_error(std::string("CUDA Free Failed: ") + + cudaGetErrorString(stat)); + ptr = nullptr; +} + +template +inline void safe_cuda_free( T*& ptr, Args&&... args ) { + safe_cuda_free(ptr); + safe_cuda_free(std::forward(args)...); +} + +template +inline void safe_cuda_copy( size_t len, T* dest, const T* src ) { + + auto stat = cudaMemcpy( dest, src, len * sizeof(T), cudaMemcpyDefault ); + if( stat != cudaSuccess ) + throw std::runtime_error(std::string("CUDA Memcpy Failed: ") + + cudaGetErrorString(stat)); + +} + +inline void safe_cuda_device_sync() { + auto stat = cudaDeviceSynchronize(); + if( stat != cudaSuccess ) + throw std::runtime_error(std::string("CUDA Device Sync Failed: ") + + cudaGetErrorString(stat)); + +} + +#endif + using namespace GauXC; @@ -42,9 +99,9 @@ void generate_collocation_data( const Molecule& mol, const BasisSet& bas auto& pts = task.points; auto& mask = task.shell_list; - // Only keep first 67 points to save on space - if( task.points.size() > 67 ) - task.points.erase( task.points.begin() + 67, task.points.end() ); + // Only keep first MAX_NPTS_CHECK points to save on space + if( task.points.size() > MAX_NPTS_CHECK ) + task.points.erase( task.points.begin() + MAX_NPTS_CHECK, task.points.end() ); const auto npts = task.points.size(); const auto nbf = task.nbe; @@ -124,6 +181,82 @@ void test_host_collocation( const BasisSet& basis, std::ifstream& in_fil } + +#ifdef GAUXC_ENABLE_CUDA +void test_cuda_collocation( const BasisSet& basis, std::ifstream& in_file) { + + + + std::vector ref_data; + + { + cereal::BinaryInputArchive ar( in_file ); + ar( ref_data ); + } + + auto shells_device = safe_cuda_malloc>( basis.size() ); + auto offs_device = safe_cuda_malloc( basis.size() ); + auto pts_device = safe_cuda_malloc( 3 * MAX_NPTS_CHECK ); + auto eval_device = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto deval_device_x = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto deval_device_y = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto deval_device_z = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + + + + cudaStream_t stream = 0; + for( auto& d : ref_data ) { + const auto npts = d.pts.size(); + const auto nbf = d.eval.size() / npts; + + const auto& mask = d.mask; + const auto& pts = d.pts; + + safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + + std::vector offs( mask.size() ); + offs[0] = 0; + for( int i = 1; i < mask.size(); ++i ) + offs[i] = offs[i-1] + basis[mask[i-1]].size(); + safe_cuda_copy( offs.size(), offs_device, offs.data() ); + + std::vector> shells; + for( auto idx : mask ) shells.emplace_back(basis[idx]); + safe_cuda_copy( shells.size(), shells_device, shells.data() ); + + integrator::cuda::eval_collocation_deriv1( shells.size(), nbf, npts, + shells_device, offs_device, + pts_device, + eval_device, deval_device_x, + deval_device_y, deval_device_z, + stream ); + + std::vector eval ( nbf * npts ), + deval_x( nbf * npts ), + deval_y( nbf * npts ), + deval_z( nbf * npts ); + + safe_cuda_copy( nbf * npts, eval.data(), eval_device ); + safe_cuda_copy( nbf * npts, deval_x.data(), deval_device_x ); + safe_cuda_copy( nbf * npts, deval_y.data(), deval_device_y ); + safe_cuda_copy( nbf * npts, deval_z.data(), deval_device_z ); + + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( eval[i] == Approx( d.eval[i] ) ); + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( deval_x[i] == Approx( d.deval_x[i] ) ); + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( deval_y[i] == Approx( d.deval_y[i] ) ); + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( deval_z[i] == Approx( d.deval_z[i] ) ); + + } + safe_cuda_device_sync(); + safe_cuda_free(shells_device, offs_device, pts_device, eval_device, + deval_device_x, deval_device_y, deval_device_z); +} +#endif + //#define GENERATE_TESTS TEST_CASE( "Water / cc-pVDZ", "[collocation]" ) { @@ -149,6 +282,13 @@ TEST_CASE( "Water / cc-pVDZ", "[collocation]" ) { SECTION( "Host Eval" ) { test_host_collocation( basis, ref_data ); } + +#ifdef GAUXC_ENABLE_CUDA + SECTION( "CUDA Eval" ) { + test_cuda_collocation( basis, ref_data ); + } +#endif + #endif } From ecc32d8429f438b446c21cd0e806ca6cb8d4a3c2 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Wed, 12 Aug 2020 20:12:26 -0400 Subject: [PATCH 11/27] Added tests for both eval_collocation and eval_collocation_deriv1 --- tests/collocation.cxx | 98 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/tests/collocation.cxx b/tests/collocation.cxx index 1f27c28aa..d094dff3c 100644 --- a/tests/collocation.cxx +++ b/tests/collocation.cxx @@ -137,11 +137,44 @@ void generate_collocation_data( const Molecule& mol, const BasisSet& bas } - void test_host_collocation( const BasisSet& basis, std::ifstream& in_file) { + std::vector ref_data; + + { + cereal::BinaryInputArchive ar( in_file ); + ar( ref_data ); + } + + for( auto& d : ref_data ) { + + const auto npts = d.pts.size(); + const auto nbf = d.eval.size() / npts; + + const auto& mask = d.mask; + const auto& pts = d.pts; + + std::vector eval( nbf * npts ); + + + integrator::host::eval_collocation( npts, mask.size(), nbf, + pts.data()->data(), basis, + mask.data(), + eval.data() ); + + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( eval[i] == Approx( d.eval[i] ) ); + + } + +} + +void test_host_collocation_deriv1( const BasisSet& basis, std::ifstream& in_file) { + + + std::vector ref_data; { @@ -187,6 +220,61 @@ void test_cuda_collocation( const BasisSet& basis, std::ifstream& in_fil + std::vector ref_data; + + { + cereal::BinaryInputArchive ar( in_file ); + ar( ref_data ); + } + + auto shells_device = safe_cuda_malloc>( basis.size() ); + auto offs_device = safe_cuda_malloc( basis.size() ); + auto pts_device = safe_cuda_malloc( 3 * MAX_NPTS_CHECK ); + auto eval_device = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + + + + cudaStream_t stream = 0; + for( auto& d : ref_data ) { + const auto npts = d.pts.size(); + const auto nbf = d.eval.size() / npts; + + const auto& mask = d.mask; + const auto& pts = d.pts; + + safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + + std::vector offs( mask.size() ); + offs[0] = 0; + for( int i = 1; i < mask.size(); ++i ) + offs[i] = offs[i-1] + basis[mask[i-1]].size(); + safe_cuda_copy( offs.size(), offs_device, offs.data() ); + + std::vector> shells; + for( auto idx : mask ) shells.emplace_back(basis[idx]); + safe_cuda_copy( shells.size(), shells_device, shells.data() ); + + integrator::cuda::eval_collocation( shells.size(), nbf, npts, + shells_device, offs_device, + pts_device, + eval_device, stream ); + + std::vector eval( nbf * npts ); + + safe_cuda_copy( nbf * npts, eval.data(), eval_device ); + + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( eval[i] == Approx( d.eval[i] ) ); + + } + safe_cuda_device_sync(); + safe_cuda_free(shells_device, offs_device, pts_device, eval_device ); + +} +void test_cuda_collocation_deriv1( const BasisSet& basis, std::ifstream& in_file) { + + + std::vector ref_data; { @@ -283,10 +371,18 @@ TEST_CASE( "Water / cc-pVDZ", "[collocation]" ) { test_host_collocation( basis, ref_data ); } + SECTION( "Host Evali Grad" ) { + test_host_collocation_deriv1( basis, ref_data ); + } + #ifdef GAUXC_ENABLE_CUDA SECTION( "CUDA Eval" ) { test_cuda_collocation( basis, ref_data ); } + + SECTION( "CUDA Eval Grad" ) { + test_cuda_collocation_deriv1( basis, ref_data ); + } #endif #endif From 967401e01ed1a26d76d6af8135777d922821e3f5 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Thu, 13 Aug 2020 12:36:19 -0400 Subject: [PATCH 12/27] Typo --- tests/collocation.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/collocation.cxx b/tests/collocation.cxx index d094dff3c..f8d1841a0 100644 --- a/tests/collocation.cxx +++ b/tests/collocation.cxx @@ -371,7 +371,7 @@ TEST_CASE( "Water / cc-pVDZ", "[collocation]" ) { test_host_collocation( basis, ref_data ); } - SECTION( "Host Evali Grad" ) { + SECTION( "Host Eval Grad" ) { test_host_collocation_deriv1( basis, ref_data ); } From fb47b85138782aaaa69e9cbae02821c30f9bc766 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Thu, 13 Aug 2020 14:14:59 -0400 Subject: [PATCH 13/27] Added a masked version of the CUDA collocation: avoids copying of shells on a per-task basis --- .../{collocation => }/collocation_device.cu | 121 +++++++++++ src/integrator/cuda/collocation_device.hpp | 29 +++ .../{collocation => }/collocation_kernels.cu | 197 ++++++++++++++++++ .../{collocation => }/collocation_kernels.hpp | 29 +++ .../cuda/gauxc-cuda_integrator.cmake | 6 +- tests/collocation.cxx | 167 ++++++++++++++- 6 files changed, 541 insertions(+), 8 deletions(-) rename src/integrator/cuda/{collocation => }/collocation_device.cu (50%) rename src/integrator/cuda/{collocation => }/collocation_kernels.cu (50%) rename src/integrator/cuda/{collocation => }/collocation_kernels.hpp (53%) diff --git a/src/integrator/cuda/collocation/collocation_device.cu b/src/integrator/cuda/collocation_device.cu similarity index 50% rename from src/integrator/cuda/collocation/collocation_device.cu rename to src/integrator/cuda/collocation_device.cu index ebd880ac0..9661c646a 100644 --- a/src/integrator/cuda/collocation/collocation_device.cu +++ b/src/integrator/cuda/collocation_device.cu @@ -1,6 +1,7 @@ #include "collocation_kernels.hpp" #include + namespace GauXC { namespace integrator { namespace cuda { @@ -42,6 +43,67 @@ void eval_collocation( ); + + + + + + + +template +void eval_collocation( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* mask_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + cudaStream_t stream +) { + + dim3 threads(32, 32, 1); + dim3 blocks( util::div_ceil( npts, threads.x ), + util::div_ceil( nshells, threads.y ) ); + + collocation_device_kernel + <<>> + ( nshells, nbf, npts, shells_device, mask_device, + offs_device, pts_device, eval_device ); + +} + +template +void eval_collocation( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* mask_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device, + cudaStream_t stream +); + + + + + + + + + + + + + + + + + + template void eval_collocation_deriv1( size_t nshells, @@ -85,6 +147,65 @@ void eval_collocation_deriv1( ); + + + + + + + + + + + + + + +template +void eval_collocation_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* mask_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + T* deval_device_x, + T* deval_device_y, + T* deval_device_z, + cudaStream_t stream +) { + + dim3 threads(32, 32, 1); + dim3 blocks( util::div_ceil( npts, threads.x ), + util::div_ceil( nshells, threads.y ) ); + + collocation_device_kernel_deriv1 + <<>> + ( nshells, nbf, npts, shells_device, mask_device, offs_device, + pts_device, eval_device, deval_device_x, deval_device_y, + deval_device_z ); + +} + +template +void eval_collocation_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* mask_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device, + double* deval_device_x, + double* deval_device_y, + double* deval_device_z, + cudaStream_t stream +); + + } // namespace cuda } // namespace integrator } // namespace GauXC diff --git a/src/integrator/cuda/collocation_device.hpp b/src/integrator/cuda/collocation_device.hpp index dba402419..8b015724c 100644 --- a/src/integrator/cuda/collocation_device.hpp +++ b/src/integrator/cuda/collocation_device.hpp @@ -16,12 +16,41 @@ void eval_collocation( cudaStream_t stream ); +template +void eval_collocation( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* mask_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + cudaStream_t stream +); + +template +void eval_collocation_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + T* deval_device_x, + T* deval_device_y, + T* deval_device_z, + cudaStream_t stream +); + template void eval_collocation_deriv1( size_t nshells, size_t nbf, size_t npts, const Shell* shells_device, + const size_t* mask_device, const size_t* offs_device, const T* pts_device, T* eval_device, diff --git a/src/integrator/cuda/collocation/collocation_kernels.cu b/src/integrator/cuda/collocation_kernels.cu similarity index 50% rename from src/integrator/cuda/collocation/collocation_kernels.cu rename to src/integrator/cuda/collocation_kernels.cu index 7e40981ca..e8d85a546 100644 --- a/src/integrator/cuda/collocation/collocation_kernels.cu +++ b/src/integrator/cuda/collocation_kernels.cu @@ -77,6 +77,99 @@ void collocation_device_kernel( ); + + + + + + + + + + + + +template +__global__ +void collocation_device_kernel( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* mask_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x < npts and tid_y < nshells ) { + + const size_t ipt = tid_x; + const size_t ish = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[mask_device[ish]]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); + + const auto xc = pt[0] - O[0]; + const auto yc = pt[1] - O[1]; + const auto zc = pt[2] - O[2]; + + const auto rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim(); + auto tmp = 0.; + for( size_t i = 0; i < nprim; ++i ) + tmp += coeff[i] * std::exp( - alpha[i] * rsq ); + + auto * bf_eval = eval_device + ibf + ipt*nbf; + + const bool do_sph = shell.pure(); + if( do_sph ) + collocation_spherical_unnorm_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); + else + collocation_cartesian_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); + + } + +} + +template +__global__ +void collocation_device_kernel( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* mask_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device +); + + + + + + + + + + + + + + + template __global__ void collocation_device_kernel_deriv1( @@ -168,6 +261,110 @@ void collocation_device_kernel_deriv1( double* deval_device_z ); + + + + + + + + + + + +template +__global__ +void collocation_device_kernel_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* mask_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + T* deval_device_x, + T* deval_device_y, + T* deval_device_z +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x < npts and tid_y < nshells ) { + + const size_t ipt = tid_x; + const size_t ish = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[mask_device[ish]]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); + + const auto xc = pt[0] - O[0]; + const auto yc = pt[1] - O[1]; + const auto zc = pt[2] - O[2]; + + const auto rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim(); + auto tmp = 0.; + auto tmp_x = 0., tmp_y = 0., tmp_z = 0.; + for( size_t i = 0; i < nprim; ++i ) { + + const auto a = alpha[i]; + const auto e = coeff[i] * std::exp( - a * rsq ); + + const auto ae = 2. * a * e; + + tmp += e; + tmp_x -= ae * xc; + tmp_y -= ae * yc; + tmp_z -= ae * zc; + + } + + auto * bf_eval = eval_device + ibf + ipt*nbf; + auto * dx_eval = deval_device_x + ibf + ipt*nbf; + auto * dy_eval = deval_device_y + ibf + ipt*nbf; + auto * dz_eval = deval_device_z + ibf + ipt*nbf; + + const bool do_sph = shell.pure(); + if( do_sph ) + collocation_spherical_unnorm_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, + xc, yc, zc, bf_eval, dx_eval, + dy_eval, dz_eval ); + else + collocation_cartesian_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, + xc, yc, zc, bf_eval, dx_eval, + dy_eval, dz_eval ); + + } + + +} + +template +__global__ +void collocation_device_kernel_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* mask_device, + const size_t* offs_device, + const double* pts_device, + double* eval_device, + double* deval_device_x, + double* deval_device_y, + double* deval_device_z +); + } // namespace cuda } // namespace integrator } // namespace GauXC diff --git a/src/integrator/cuda/collocation/collocation_kernels.hpp b/src/integrator/cuda/collocation_kernels.hpp similarity index 53% rename from src/integrator/cuda/collocation/collocation_kernels.hpp rename to src/integrator/cuda/collocation_kernels.hpp index ba45b051c..686d21318 100644 --- a/src/integrator/cuda/collocation/collocation_kernels.hpp +++ b/src/integrator/cuda/collocation_kernels.hpp @@ -16,6 +16,34 @@ void collocation_device_kernel( T* eval_device ); +template +__global__ +void collocation_device_kernel( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* mask_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device +); + +template +__global__ +void collocation_device_kernel_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + T* deval_device_x, + T* deval_device_y, + T* deval_device_z +); + template __global__ void collocation_device_kernel_deriv1( @@ -23,6 +51,7 @@ void collocation_device_kernel_deriv1( size_t nbf, size_t npts, const Shell* shells_device, + const size_t* mask_device, const size_t* offs_device, const T* pts_device, T* eval_device, diff --git a/src/integrator/cuda/gauxc-cuda_integrator.cmake b/src/integrator/cuda/gauxc-cuda_integrator.cmake index eb6ad1967..d117ff1c9 100644 --- a/src/integrator/cuda/gauxc-cuda_integrator.cmake +++ b/src/integrator/cuda/gauxc-cuda_integrator.cmake @@ -1,6 +1,6 @@ add_library( gauxc_device_integrator OBJECT - cuda/collocation/collocation_kernels.cu - cuda/collocation/collocation_device.cu + cuda/collocation_kernels.cu + cuda/collocation_device.cu ) target_compile_features( gauxc_device_integrator PUBLIC cuda_std_14 cxx_std_14 ) target_link_libraries( gauxc_device_integrator PUBLIC ExchCXX::exchcxx ) @@ -11,6 +11,8 @@ target_include_directories( gauxc_device_integrator $ $ $ + $ + $ ) target_compile_options( gauxc_device_integrator diff --git a/tests/collocation.cxx b/tests/collocation.cxx index f8d1841a0..20c0e57e6 100644 --- a/tests/collocation.cxx +++ b/tests/collocation.cxx @@ -216,7 +216,10 @@ void test_host_collocation_deriv1( const BasisSet& basis, std::ifstream& #ifdef GAUXC_ENABLE_CUDA -void test_cuda_collocation( const BasisSet& basis, std::ifstream& in_file) { + + + +void test_cuda_collocation_petite( const BasisSet& basis, std::ifstream& in_file) { @@ -271,7 +274,73 @@ void test_cuda_collocation( const BasisSet& basis, std::ifstream& in_fil safe_cuda_free(shells_device, offs_device, pts_device, eval_device ); } -void test_cuda_collocation_deriv1( const BasisSet& basis, std::ifstream& in_file) { + + + + +void test_cuda_collocation_masked( const BasisSet& basis, std::ifstream& in_file) { + + + + std::vector ref_data; + + { + cereal::BinaryInputArchive ar( in_file ); + ar( ref_data ); + } + + auto shells_device = safe_cuda_malloc>( basis.size() ); + auto offs_device = safe_cuda_malloc( basis.size() ); + auto mask_device = safe_cuda_malloc( basis.size() ); + auto pts_device = safe_cuda_malloc( 3 * MAX_NPTS_CHECK ); + auto eval_device = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + + + std::vector> shells( basis ); + safe_cuda_copy( basis.size(), shells_device, shells.data() ); + + + cudaStream_t stream = 0; + for( auto& d : ref_data ) { + const auto npts = d.pts.size(); + const auto nbf = d.eval.size() / npts; + + const auto& mask = d.mask; + const auto& pts = d.pts; + + safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + + std::vector mask_ul( mask.size() ); + std::copy( mask.begin(), mask.end(), mask_ul.begin() ); + safe_cuda_copy( mask.size(), mask_device, mask_ul.data() ); + + std::vector offs( mask.size() ); + offs[0] = 0; + for( int i = 1; i < mask.size(); ++i ) + offs[i] = offs[i-1] + basis[mask[i-1]].size(); + safe_cuda_copy( offs.size(), offs_device, offs.data() ); + + + integrator::cuda::eval_collocation( mask.size(), nbf, npts, + shells_device, mask_device, + offs_device, pts_device, + eval_device, stream ); + + std::vector eval( nbf * npts ); + + safe_cuda_copy( nbf * npts, eval.data(), eval_device ); + + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( eval[i] == Approx( d.eval[i] ) ); + + } + safe_cuda_device_sync(); + safe_cuda_free(shells_device, offs_device, pts_device, eval_device ); + +} + + +void test_cuda_collocation_deriv1_petite( const BasisSet& basis, std::ifstream& in_file) { @@ -343,6 +412,86 @@ void test_cuda_collocation_deriv1( const BasisSet& basis, std::ifstream& safe_cuda_free(shells_device, offs_device, pts_device, eval_device, deval_device_x, deval_device_y, deval_device_z); } + + + + +void test_cuda_collocation_deriv1_masked( const BasisSet& basis, std::ifstream& in_file) { + + + + std::vector ref_data; + + { + cereal::BinaryInputArchive ar( in_file ); + ar( ref_data ); + } + + auto shells_device = safe_cuda_malloc>( basis.size() ); + auto offs_device = safe_cuda_malloc( basis.size() ); + auto mask_device = safe_cuda_malloc( basis.size() ); + auto pts_device = safe_cuda_malloc( 3 * MAX_NPTS_CHECK ); + auto eval_device = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto deval_device_x = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto deval_device_y = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto deval_device_z = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + + + std::vector> shells( basis ); + safe_cuda_copy( basis.size(), shells_device, shells.data() ); + + cudaStream_t stream = 0; + for( auto& d : ref_data ) { + const auto npts = d.pts.size(); + const auto nbf = d.eval.size() / npts; + + const auto& mask = d.mask; + const auto& pts = d.pts; + + safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + + std::vector mask_ul( mask.size() ); + std::copy( mask.begin(), mask.end(), mask_ul.begin() ); + safe_cuda_copy( mask.size(), mask_device, mask_ul.data() ); + + std::vector offs( mask.size() ); + offs[0] = 0; + for( int i = 1; i < mask.size(); ++i ) + offs[i] = offs[i-1] + basis[mask[i-1]].size(); + safe_cuda_copy( offs.size(), offs_device, offs.data() ); + + + integrator::cuda::eval_collocation_deriv1( mask.size(), nbf, npts, + shells_device, mask_device, + offs_device, pts_device, + eval_device, deval_device_x, + deval_device_y, deval_device_z, + stream ); + + std::vector eval ( nbf * npts ), + deval_x( nbf * npts ), + deval_y( nbf * npts ), + deval_z( nbf * npts ); + + safe_cuda_copy( nbf * npts, eval.data(), eval_device ); + safe_cuda_copy( nbf * npts, deval_x.data(), deval_device_x ); + safe_cuda_copy( nbf * npts, deval_y.data(), deval_device_y ); + safe_cuda_copy( nbf * npts, deval_z.data(), deval_device_z ); + + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( eval[i] == Approx( d.eval[i] ) ); + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( deval_x[i] == Approx( d.deval_x[i] ) ); + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( deval_y[i] == Approx( d.deval_y[i] ) ); + for( auto i = 0; i < npts * nbf; ++i ) + CHECK( deval_z[i] == Approx( d.deval_z[i] ) ); + + } + safe_cuda_device_sync(); + safe_cuda_free(shells_device, offs_device, pts_device, eval_device, + deval_device_x, deval_device_y, deval_device_z); +} #endif //#define GENERATE_TESTS @@ -376,12 +525,18 @@ TEST_CASE( "Water / cc-pVDZ", "[collocation]" ) { } #ifdef GAUXC_ENABLE_CUDA - SECTION( "CUDA Eval" ) { - test_cuda_collocation( basis, ref_data ); + SECTION( "CUDA Eval: Petite Shell List" ) { + test_cuda_collocation_petite( basis, ref_data ); + } + SECTION( "CUDA Eval: Masked" ) { + test_cuda_collocation_masked( basis, ref_data ); } - SECTION( "CUDA Eval Grad" ) { - test_cuda_collocation_deriv1( basis, ref_data ); + SECTION( "CUDA Eval Grad: Petite Shell List" ) { + test_cuda_collocation_deriv1_petite( basis, ref_data ); + } + SECTION( "CUDA Eval Grad: Masked" ) { + test_cuda_collocation_deriv1_masked( basis, ref_data ); } #endif From 4c18dfe9a5b88f95bf75756e7cea4df9812bf97a Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Thu, 13 Aug 2020 14:28:48 -0400 Subject: [PATCH 14/27] Updated naming for CUDA collocation functions --- src/integrator/cuda/collocation_device.cu | 24 +++++++-------- src/integrator/cuda/collocation_device.hpp | 8 ++--- src/integrator/cuda/collocation_kernels.cu | 16 +++++----- src/integrator/cuda/collocation_kernels.hpp | 8 ++--- tests/collocation.cxx | 34 ++++++++++----------- 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/integrator/cuda/collocation_device.cu b/src/integrator/cuda/collocation_device.cu index 9661c646a..8f77016bb 100644 --- a/src/integrator/cuda/collocation_device.cu +++ b/src/integrator/cuda/collocation_device.cu @@ -7,7 +7,7 @@ namespace integrator { namespace cuda { template -void eval_collocation( +void eval_collocation_petite( size_t nshells, size_t nbf, size_t npts, @@ -23,7 +23,7 @@ void eval_collocation( dim3 blocks( util::div_ceil( npts, threads.x ), util::div_ceil( nshells, threads.y ) ); - collocation_device_kernel + collocation_device_petite_kernel <<>> ( nshells, nbf, npts, shells_device, offs_device, pts_device, eval_device ); @@ -31,7 +31,7 @@ void eval_collocation( } template -void eval_collocation( +void eval_collocation_petite( size_t nshells, size_t nbf, size_t npts, @@ -51,7 +51,7 @@ void eval_collocation( template -void eval_collocation( +void eval_collocation_masked( size_t nshells, size_t nbf, size_t npts, @@ -67,7 +67,7 @@ void eval_collocation( dim3 blocks( util::div_ceil( npts, threads.x ), util::div_ceil( nshells, threads.y ) ); - collocation_device_kernel + collocation_device_masked_kernel <<>> ( nshells, nbf, npts, shells_device, mask_device, offs_device, pts_device, eval_device ); @@ -75,7 +75,7 @@ void eval_collocation( } template -void eval_collocation( +void eval_collocation_masked( size_t nshells, size_t nbf, size_t npts, @@ -105,7 +105,7 @@ void eval_collocation( template -void eval_collocation_deriv1( +void eval_collocation_petite_deriv1( size_t nshells, size_t nbf, size_t npts, @@ -123,7 +123,7 @@ void eval_collocation_deriv1( dim3 blocks( util::div_ceil( npts, threads.x ), util::div_ceil( nshells, threads.y ) ); - collocation_device_kernel_deriv1 + collocation_device_petite_kernel_deriv1 <<>> ( nshells, nbf, npts, shells_device, offs_device, pts_device, eval_device, deval_device_x, deval_device_y, @@ -132,7 +132,7 @@ void eval_collocation_deriv1( } template -void eval_collocation_deriv1( +void eval_collocation_petite_deriv1( size_t nshells, size_t nbf, size_t npts, @@ -162,7 +162,7 @@ void eval_collocation_deriv1( template -void eval_collocation_deriv1( +void eval_collocation_masked_deriv1( size_t nshells, size_t nbf, size_t npts, @@ -181,7 +181,7 @@ void eval_collocation_deriv1( dim3 blocks( util::div_ceil( npts, threads.x ), util::div_ceil( nshells, threads.y ) ); - collocation_device_kernel_deriv1 + collocation_device_masked_kernel_deriv1 <<>> ( nshells, nbf, npts, shells_device, mask_device, offs_device, pts_device, eval_device, deval_device_x, deval_device_y, @@ -190,7 +190,7 @@ void eval_collocation_deriv1( } template -void eval_collocation_deriv1( +void eval_collocation_masked_deriv1( size_t nshells, size_t nbf, size_t npts, diff --git a/src/integrator/cuda/collocation_device.hpp b/src/integrator/cuda/collocation_device.hpp index 8b015724c..5bc59c2fa 100644 --- a/src/integrator/cuda/collocation_device.hpp +++ b/src/integrator/cuda/collocation_device.hpp @@ -5,7 +5,7 @@ namespace integrator { namespace cuda { template -void eval_collocation( +void eval_collocation_petite( size_t nshells, size_t nbf, size_t npts, @@ -17,7 +17,7 @@ void eval_collocation( ); template -void eval_collocation( +void eval_collocation_masked( size_t nshells, size_t nbf, size_t npts, @@ -30,7 +30,7 @@ void eval_collocation( ); template -void eval_collocation_deriv1( +void eval_collocation_petite_deriv1( size_t nshells, size_t nbf, size_t npts, @@ -45,7 +45,7 @@ void eval_collocation_deriv1( ); template -void eval_collocation_deriv1( +void eval_collocation_masked_deriv1( size_t nshells, size_t nbf, size_t npts, diff --git a/src/integrator/cuda/collocation_kernels.cu b/src/integrator/cuda/collocation_kernels.cu index e8d85a546..357842d2d 100644 --- a/src/integrator/cuda/collocation_kernels.cu +++ b/src/integrator/cuda/collocation_kernels.cu @@ -13,7 +13,7 @@ namespace cuda { template __global__ -void collocation_device_kernel( +void collocation_device_petite_kernel( size_t nshells, size_t nbf, size_t npts, @@ -66,7 +66,7 @@ void collocation_device_kernel( template __global__ -void collocation_device_kernel( +void collocation_device_petite_kernel( size_t nshells, size_t nbf, size_t npts, @@ -91,7 +91,7 @@ void collocation_device_kernel( template __global__ -void collocation_device_kernel( +void collocation_device_masked_kernel( size_t nshells, size_t nbf, size_t npts, @@ -145,7 +145,7 @@ void collocation_device_kernel( template __global__ -void collocation_device_kernel( +void collocation_device_masked_kernel( size_t nshells, size_t nbf, size_t npts, @@ -172,7 +172,7 @@ void collocation_device_kernel( template __global__ -void collocation_device_kernel_deriv1( +void collocation_device_petite_kernel_deriv1( size_t nshells, size_t nbf, size_t npts, @@ -248,7 +248,7 @@ void collocation_device_kernel_deriv1( template __global__ -void collocation_device_kernel_deriv1( +void collocation_device_petite_kernel_deriv1( size_t nshells, size_t nbf, size_t npts, @@ -274,7 +274,7 @@ void collocation_device_kernel_deriv1( template __global__ -void collocation_device_kernel_deriv1( +void collocation_device_masked_kernel_deriv1( size_t nshells, size_t nbf, size_t npts, @@ -351,7 +351,7 @@ void collocation_device_kernel_deriv1( template __global__ -void collocation_device_kernel_deriv1( +void collocation_device_masked_kernel_deriv1( size_t nshells, size_t nbf, size_t npts, diff --git a/src/integrator/cuda/collocation_kernels.hpp b/src/integrator/cuda/collocation_kernels.hpp index 686d21318..e939e3cba 100644 --- a/src/integrator/cuda/collocation_kernels.hpp +++ b/src/integrator/cuda/collocation_kernels.hpp @@ -6,7 +6,7 @@ namespace cuda { template __global__ -void collocation_device_kernel( +void collocation_device_petite_kernel( size_t nshells, size_t nbf, size_t npts, @@ -18,7 +18,7 @@ void collocation_device_kernel( template __global__ -void collocation_device_kernel( +void collocation_device_masked_kernel( size_t nshells, size_t nbf, size_t npts, @@ -31,7 +31,7 @@ void collocation_device_kernel( template __global__ -void collocation_device_kernel_deriv1( +void collocation_device_petite_kernel_deriv1( size_t nshells, size_t nbf, size_t npts, @@ -46,7 +46,7 @@ void collocation_device_kernel_deriv1( template __global__ -void collocation_device_kernel_deriv1( +void collocation_device_masked_kernel_deriv1( size_t nshells, size_t nbf, size_t npts, diff --git a/tests/collocation.cxx b/tests/collocation.cxx index 20c0e57e6..b33f15357 100644 --- a/tests/collocation.cxx +++ b/tests/collocation.cxx @@ -257,7 +257,7 @@ void test_cuda_collocation_petite( const BasisSet& basis, std::ifstream& for( auto idx : mask ) shells.emplace_back(basis[idx]); safe_cuda_copy( shells.size(), shells_device, shells.data() ); - integrator::cuda::eval_collocation( shells.size(), nbf, npts, + integrator::cuda::eval_collocation_petite( shells.size(), nbf, npts, shells_device, offs_device, pts_device, eval_device, stream ); @@ -321,10 +321,10 @@ void test_cuda_collocation_masked( const BasisSet& basis, std::ifstream& safe_cuda_copy( offs.size(), offs_device, offs.data() ); - integrator::cuda::eval_collocation( mask.size(), nbf, npts, - shells_device, mask_device, - offs_device, pts_device, - eval_device, stream ); + integrator::cuda::eval_collocation_masked( mask.size(), nbf, npts, + shells_device, mask_device, + offs_device, pts_device, + eval_device, stream ); std::vector eval( nbf * npts ); @@ -381,12 +381,12 @@ void test_cuda_collocation_deriv1_petite( const BasisSet& basis, std::if for( auto idx : mask ) shells.emplace_back(basis[idx]); safe_cuda_copy( shells.size(), shells_device, shells.data() ); - integrator::cuda::eval_collocation_deriv1( shells.size(), nbf, npts, - shells_device, offs_device, - pts_device, - eval_device, deval_device_x, - deval_device_y, deval_device_z, - stream ); + integrator::cuda::eval_collocation_petite_deriv1( shells.size(), nbf, npts, + shells_device, offs_device, + pts_device, + eval_device, deval_device_x, + deval_device_y, deval_device_z, + stream ); std::vector eval ( nbf * npts ), deval_x( nbf * npts ), @@ -461,12 +461,12 @@ void test_cuda_collocation_deriv1_masked( const BasisSet& basis, std::if safe_cuda_copy( offs.size(), offs_device, offs.data() ); - integrator::cuda::eval_collocation_deriv1( mask.size(), nbf, npts, - shells_device, mask_device, - offs_device, pts_device, - eval_device, deval_device_x, - deval_device_y, deval_device_z, - stream ); + integrator::cuda::eval_collocation_masked_deriv1( mask.size(), nbf, npts, + shells_device, mask_device, + offs_device, pts_device, + eval_device, deval_device_x, + deval_device_y, deval_device_z, + stream ); std::vector eval ( nbf * npts ), deval_x( nbf * npts ), From 816aa468c3ecfec763025b460903f6a8c3a5612e Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Thu, 13 Aug 2020 14:43:26 -0400 Subject: [PATCH 15/27] Refactoring CUDA collocation kernel headers: moving to header only --- include/gauxc/util/div_ceil.hpp | 2 + src/integrator/cuda/collocation_device.cu | 3 +- src/integrator/cuda/collocation_kernels.cu | 3 +- .../cuda/collocation_masked_kernels.hpp | 155 +++++++++++++++++ .../cuda/collocation_petite_kernels.hpp | 160 ++++++++++++++++++ .../cuda/gauxc-cuda_integrator.cmake | 2 +- 6 files changed, 322 insertions(+), 3 deletions(-) create mode 100644 src/integrator/cuda/collocation_masked_kernels.hpp create mode 100644 src/integrator/cuda/collocation_petite_kernels.hpp diff --git a/include/gauxc/util/div_ceil.hpp b/include/gauxc/util/div_ceil.hpp index bd561447b..6c0921d5f 100644 --- a/include/gauxc/util/div_ceil.hpp +++ b/include/gauxc/util/div_ceil.hpp @@ -1,6 +1,8 @@ #pragma once #include #include +#include +#include namespace GauXC { diff --git a/src/integrator/cuda/collocation_device.cu b/src/integrator/cuda/collocation_device.cu index 8f77016bb..858b8e943 100644 --- a/src/integrator/cuda/collocation_device.cu +++ b/src/integrator/cuda/collocation_device.cu @@ -1,6 +1,7 @@ -#include "collocation_kernels.hpp" #include +#include "collocation_petite_kernels.hpp" +#include "collocation_masked_kernels.hpp" namespace GauXC { namespace integrator { diff --git a/src/integrator/cuda/collocation_kernels.cu b/src/integrator/cuda/collocation_kernels.cu index 357842d2d..9bf31ee68 100644 --- a/src/integrator/cuda/collocation_kernels.cu +++ b/src/integrator/cuda/collocation_kernels.cu @@ -1,7 +1,8 @@ #include #include -#include "collocation_kernels.hpp" +//#include "collocation_kernels.hpp" +#include "collocation_petite_kernels.hpp" #include "collocation_angular_cartesian.hpp" #include "collocation_angular_spherical_unnorm.hpp" diff --git a/src/integrator/cuda/collocation_masked_kernels.hpp b/src/integrator/cuda/collocation_masked_kernels.hpp new file mode 100644 index 000000000..298100d1e --- /dev/null +++ b/src/integrator/cuda/collocation_masked_kernels.hpp @@ -0,0 +1,155 @@ +#include +#include + +#include + +#include "collocation_angular_cartesian.hpp" +#include "collocation_angular_spherical_unnorm.hpp" + +namespace GauXC { +namespace integrator { +namespace cuda { + + +template +__global__ +void collocation_device_masked_kernel( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* mask_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x < npts and tid_y < nshells ) { + + const size_t ipt = tid_x; + const size_t ish = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[mask_device[ish]]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); + + const auto xc = pt[0] - O[0]; + const auto yc = pt[1] - O[1]; + const auto zc = pt[2] - O[2]; + + const auto rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim(); + auto tmp = 0.; + for( size_t i = 0; i < nprim; ++i ) + tmp += coeff[i] * std::exp( - alpha[i] * rsq ); + + auto * bf_eval = eval_device + ibf + ipt*nbf; + + const bool do_sph = shell.pure(); + if( do_sph ) + collocation_spherical_unnorm_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); + else + collocation_cartesian_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); + + } + +} + + + + + + + + +template +__global__ +void collocation_device_masked_kernel_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* mask_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + T* deval_device_x, + T* deval_device_y, + T* deval_device_z +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x < npts and tid_y < nshells ) { + + const size_t ipt = tid_x; + const size_t ish = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[mask_device[ish]]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); + + const auto xc = pt[0] - O[0]; + const auto yc = pt[1] - O[1]; + const auto zc = pt[2] - O[2]; + + const auto rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim(); + auto tmp = 0.; + auto tmp_x = 0., tmp_y = 0., tmp_z = 0.; + for( size_t i = 0; i < nprim; ++i ) { + + const auto a = alpha[i]; + const auto e = coeff[i] * std::exp( - a * rsq ); + + const auto ae = 2. * a * e; + + tmp += e; + tmp_x -= ae * xc; + tmp_y -= ae * yc; + tmp_z -= ae * zc; + + } + + auto * bf_eval = eval_device + ibf + ipt*nbf; + auto * dx_eval = deval_device_x + ibf + ipt*nbf; + auto * dy_eval = deval_device_y + ibf + ipt*nbf; + auto * dz_eval = deval_device_z + ibf + ipt*nbf; + + const bool do_sph = shell.pure(); + if( do_sph ) + collocation_spherical_unnorm_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, + xc, yc, zc, bf_eval, dx_eval, + dy_eval, dz_eval ); + else + collocation_cartesian_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, + xc, yc, zc, bf_eval, dx_eval, + dy_eval, dz_eval ); + + } + + +} + +} // namespace cuda +} // namespace integrator +} // namespace GauXC + diff --git a/src/integrator/cuda/collocation_petite_kernels.hpp b/src/integrator/cuda/collocation_petite_kernels.hpp new file mode 100644 index 000000000..a03eaf35c --- /dev/null +++ b/src/integrator/cuda/collocation_petite_kernels.hpp @@ -0,0 +1,160 @@ +#include +#include + +#include + +#include "collocation_angular_cartesian.hpp" +#include "collocation_angular_spherical_unnorm.hpp" + +namespace GauXC { +namespace integrator { +namespace cuda { + + + +template +__global__ +void collocation_device_petite_kernel( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x < npts and tid_y < nshells ) { + + const size_t ipt = tid_x; + const size_t ish = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[ish]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); + + const auto xc = pt[0] - O[0]; + const auto yc = pt[1] - O[1]; + const auto zc = pt[2] - O[2]; + + const auto rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim(); + auto tmp = 0.; + for( size_t i = 0; i < nprim; ++i ) + tmp += coeff[i] * std::exp( - alpha[i] * rsq ); + + auto * bf_eval = eval_device + ibf + ipt*nbf; + + const bool do_sph = shell.pure(); + if( do_sph ) + collocation_spherical_unnorm_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); + else + collocation_cartesian_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); + + } + +} + + + + + + + + + + + + + + + +template +__global__ +void collocation_device_petite_kernel_deriv1( + size_t nshells, + size_t nbf, + size_t npts, + const Shell* shells_device, + const size_t* offs_device, + const T* pts_device, + T* eval_device, + T* deval_device_x, + T* deval_device_y, + T* deval_device_z +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x < npts and tid_y < nshells ) { + + const size_t ipt = tid_x; + const size_t ish = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[ish]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); + + const auto xc = pt[0] - O[0]; + const auto yc = pt[1] - O[1]; + const auto zc = pt[2] - O[2]; + + const auto rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim(); + auto tmp = 0.; + auto tmp_x = 0., tmp_y = 0., tmp_z = 0.; + for( size_t i = 0; i < nprim; ++i ) { + + const auto a = alpha[i]; + const auto e = coeff[i] * std::exp( - a * rsq ); + + const auto ae = 2. * a * e; + + tmp += e; + tmp_x -= ae * xc; + tmp_y -= ae * yc; + tmp_z -= ae * zc; + + } + + auto * bf_eval = eval_device + ibf + ipt*nbf; + auto * dx_eval = deval_device_x + ibf + ipt*nbf; + auto * dy_eval = deval_device_y + ibf + ipt*nbf; + auto * dz_eval = deval_device_z + ibf + ipt*nbf; + + const bool do_sph = shell.pure(); + if( do_sph ) + collocation_spherical_unnorm_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, + xc, yc, zc, bf_eval, dx_eval, + dy_eval, dz_eval ); + else + collocation_cartesian_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, + xc, yc, zc, bf_eval, dx_eval, + dy_eval, dz_eval ); + + } + + +} + +} // namespace cuda +} // namespace integrator +} // namespace GauXC diff --git a/src/integrator/cuda/gauxc-cuda_integrator.cmake b/src/integrator/cuda/gauxc-cuda_integrator.cmake index d117ff1c9..a7e7fd315 100644 --- a/src/integrator/cuda/gauxc-cuda_integrator.cmake +++ b/src/integrator/cuda/gauxc-cuda_integrator.cmake @@ -1,5 +1,5 @@ add_library( gauxc_device_integrator OBJECT - cuda/collocation_kernels.cu + #cuda/collocation_kernels.cu cuda/collocation_device.cu ) target_compile_features( gauxc_device_integrator PUBLIC cuda_std_14 cxx_std_14 ) From 6e4f25ab014937f86090bde92f24a35a72e52906 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Thu, 13 Aug 2020 14:43:52 -0400 Subject: [PATCH 16/27] Removed deprecated files --- src/integrator/cuda/collocation_kernels.cu | 371 -------------------- src/integrator/cuda/collocation_kernels.hpp | 65 ---- 2 files changed, 436 deletions(-) delete mode 100644 src/integrator/cuda/collocation_kernels.cu delete mode 100644 src/integrator/cuda/collocation_kernels.hpp diff --git a/src/integrator/cuda/collocation_kernels.cu b/src/integrator/cuda/collocation_kernels.cu deleted file mode 100644 index 9bf31ee68..000000000 --- a/src/integrator/cuda/collocation_kernels.cu +++ /dev/null @@ -1,371 +0,0 @@ -#include -#include - -//#include "collocation_kernels.hpp" -#include "collocation_petite_kernels.hpp" -#include "collocation_angular_cartesian.hpp" -#include "collocation_angular_spherical_unnorm.hpp" - -namespace GauXC { -namespace integrator { -namespace cuda { - - - -template -__global__ -void collocation_device_petite_kernel( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* offs_device, - const T* pts_device, - T* eval_device -) { - - const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; - const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; - - if( tid_x < npts and tid_y < nshells ) { - - const size_t ipt = tid_x; - const size_t ish = tid_y; - - const size_t ibf = offs_device[ish]; - - const auto& shell = shells_device[ish]; - const auto* pt = pts_device + 3*ipt; - - - const auto* O = shell.O_data(); - const auto* alpha = shell.alpha_data(); - const auto* coeff = shell.coeff_data(); - - const auto xc = pt[0] - O[0]; - const auto yc = pt[1] - O[1]; - const auto zc = pt[2] - O[2]; - - const auto rsq = xc*xc + yc*yc + zc*zc; - - const size_t nprim = shell.nprim(); - auto tmp = 0.; - for( size_t i = 0; i < nprim; ++i ) - tmp += coeff[i] * std::exp( - alpha[i] * rsq ); - - auto * bf_eval = eval_device + ibf + ipt*nbf; - - const bool do_sph = shell.pure(); - if( do_sph ) - collocation_spherical_unnorm_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); - else - collocation_cartesian_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); - - } - -} - -template -__global__ -void collocation_device_petite_kernel( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device -); - - - - - - - - - - - - - - -template -__global__ -void collocation_device_masked_kernel( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* mask_device, - const size_t* offs_device, - const T* pts_device, - T* eval_device -) { - - const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; - const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; - - if( tid_x < npts and tid_y < nshells ) { - - const size_t ipt = tid_x; - const size_t ish = tid_y; - - const size_t ibf = offs_device[ish]; - - const auto& shell = shells_device[mask_device[ish]]; - const auto* pt = pts_device + 3*ipt; - - - const auto* O = shell.O_data(); - const auto* alpha = shell.alpha_data(); - const auto* coeff = shell.coeff_data(); - - const auto xc = pt[0] - O[0]; - const auto yc = pt[1] - O[1]; - const auto zc = pt[2] - O[2]; - - const auto rsq = xc*xc + yc*yc + zc*zc; - - const size_t nprim = shell.nprim(); - auto tmp = 0.; - for( size_t i = 0; i < nprim; ++i ) - tmp += coeff[i] * std::exp( - alpha[i] * rsq ); - - auto * bf_eval = eval_device + ibf + ipt*nbf; - - const bool do_sph = shell.pure(); - if( do_sph ) - collocation_spherical_unnorm_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); - else - collocation_cartesian_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); - - } - -} - -template -__global__ -void collocation_device_masked_kernel( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* mask_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device -); - - - - - - - - - - - - - - - -template -__global__ -void collocation_device_petite_kernel_deriv1( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* offs_device, - const T* pts_device, - T* eval_device, - T* deval_device_x, - T* deval_device_y, - T* deval_device_z -) { - - const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; - const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; - - if( tid_x < npts and tid_y < nshells ) { - - const size_t ipt = tid_x; - const size_t ish = tid_y; - - const size_t ibf = offs_device[ish]; - - const auto& shell = shells_device[ish]; - const auto* pt = pts_device + 3*ipt; - - - const auto* O = shell.O_data(); - const auto* alpha = shell.alpha_data(); - const auto* coeff = shell.coeff_data(); - - const auto xc = pt[0] - O[0]; - const auto yc = pt[1] - O[1]; - const auto zc = pt[2] - O[2]; - - const auto rsq = xc*xc + yc*yc + zc*zc; - - const size_t nprim = shell.nprim(); - auto tmp = 0.; - auto tmp_x = 0., tmp_y = 0., tmp_z = 0.; - for( size_t i = 0; i < nprim; ++i ) { - - const auto a = alpha[i]; - const auto e = coeff[i] * std::exp( - a * rsq ); - - const auto ae = 2. * a * e; - - tmp += e; - tmp_x -= ae * xc; - tmp_y -= ae * yc; - tmp_z -= ae * zc; - - } - - auto * bf_eval = eval_device + ibf + ipt*nbf; - auto * dx_eval = deval_device_x + ibf + ipt*nbf; - auto * dy_eval = deval_device_y + ibf + ipt*nbf; - auto * dz_eval = deval_device_z + ibf + ipt*nbf; - - const bool do_sph = shell.pure(); - if( do_sph ) - collocation_spherical_unnorm_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, - xc, yc, zc, bf_eval, dx_eval, - dy_eval, dz_eval ); - else - collocation_cartesian_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, - xc, yc, zc, bf_eval, dx_eval, - dy_eval, dz_eval ); - - } - - -} - -template -__global__ -void collocation_device_petite_kernel_deriv1( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device, - double* deval_device_x, - double* deval_device_y, - double* deval_device_z -); - - - - - - - - - - - - -template -__global__ -void collocation_device_masked_kernel_deriv1( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* mask_device, - const size_t* offs_device, - const T* pts_device, - T* eval_device, - T* deval_device_x, - T* deval_device_y, - T* deval_device_z -) { - - const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; - const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; - - if( tid_x < npts and tid_y < nshells ) { - - const size_t ipt = tid_x; - const size_t ish = tid_y; - - const size_t ibf = offs_device[ish]; - - const auto& shell = shells_device[mask_device[ish]]; - const auto* pt = pts_device + 3*ipt; - - - const auto* O = shell.O_data(); - const auto* alpha = shell.alpha_data(); - const auto* coeff = shell.coeff_data(); - - const auto xc = pt[0] - O[0]; - const auto yc = pt[1] - O[1]; - const auto zc = pt[2] - O[2]; - - const auto rsq = xc*xc + yc*yc + zc*zc; - - const size_t nprim = shell.nprim(); - auto tmp = 0.; - auto tmp_x = 0., tmp_y = 0., tmp_z = 0.; - for( size_t i = 0; i < nprim; ++i ) { - - const auto a = alpha[i]; - const auto e = coeff[i] * std::exp( - a * rsq ); - - const auto ae = 2. * a * e; - - tmp += e; - tmp_x -= ae * xc; - tmp_y -= ae * yc; - tmp_z -= ae * zc; - - } - - auto * bf_eval = eval_device + ibf + ipt*nbf; - auto * dx_eval = deval_device_x + ibf + ipt*nbf; - auto * dy_eval = deval_device_y + ibf + ipt*nbf; - auto * dz_eval = deval_device_z + ibf + ipt*nbf; - - const bool do_sph = shell.pure(); - if( do_sph ) - collocation_spherical_unnorm_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, - xc, yc, zc, bf_eval, dx_eval, - dy_eval, dz_eval ); - else - collocation_cartesian_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, - xc, yc, zc, bf_eval, dx_eval, - dy_eval, dz_eval ); - - } - - -} - -template -__global__ -void collocation_device_masked_kernel_deriv1( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* mask_device, - const size_t* offs_device, - const double* pts_device, - double* eval_device, - double* deval_device_x, - double* deval_device_y, - double* deval_device_z -); - -} // namespace cuda -} // namespace integrator -} // namespace GauXC diff --git a/src/integrator/cuda/collocation_kernels.hpp b/src/integrator/cuda/collocation_kernels.hpp deleted file mode 100644 index e939e3cba..000000000 --- a/src/integrator/cuda/collocation_kernels.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#include - -namespace GauXC { -namespace integrator { -namespace cuda { - -template -__global__ -void collocation_device_petite_kernel( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* offs_device, - const T* pts_device, - T* eval_device -); - -template -__global__ -void collocation_device_masked_kernel( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* mask_device, - const size_t* offs_device, - const T* pts_device, - T* eval_device -); - -template -__global__ -void collocation_device_petite_kernel_deriv1( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* offs_device, - const T* pts_device, - T* eval_device, - T* deval_device_x, - T* deval_device_y, - T* deval_device_z -); - -template -__global__ -void collocation_device_masked_kernel_deriv1( - size_t nshells, - size_t nbf, - size_t npts, - const Shell* shells_device, - const size_t* mask_device, - const size_t* offs_device, - const T* pts_device, - T* eval_device, - T* deval_device_x, - T* deval_device_y, - T* deval_device_z -); - -} // namespace cuda -} // namespace integrator -} // namespace GauXC From 6508f4f2123de4f55bbac2bf5ef76a676d71eb2e Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Thu, 13 Aug 2020 20:48:13 -0400 Subject: [PATCH 17/27] Added task batching CUDA collocation kernels + UTs --- include/gauxc/xc_task.hpp | 37 ++ src/integrator/cuda/collocation_device.cu | 159 ++++++ src/integrator/cuda/collocation_device.hpp | 43 ++ .../collocation_masked_combined_kernels.hpp | 188 +++++++ .../collocation_petite_combined_kernels.hpp | 186 +++++++ tests/collocation.cxx | 468 ++++++++++++++++++ 6 files changed, 1081 insertions(+) create mode 100644 src/integrator/cuda/collocation_masked_combined_kernels.hpp create mode 100644 src/integrator/cuda/collocation_petite_combined_kernels.hpp diff --git a/include/gauxc/xc_task.hpp b/include/gauxc/xc_task.hpp index eb3e759b5..2b2e44290 100644 --- a/include/gauxc/xc_task.hpp +++ b/include/gauxc/xc_task.hpp @@ -3,7 +3,10 @@ #include #include #include +#include +#include #include +#include namespace GauXC { @@ -68,4 +71,38 @@ struct XCTask { }; +#ifdef GAUXC_ENABLE_CUDA +namespace cuda { + +template +struct XCTaskDevice { + + size_t nbe; + size_t npts; + size_t ncut; + size_t nshells; + + double* points; + double* weights; + size_t* shell_list; + size_t* shell_offs; + int64_t* submat_cut; + + Shell* shells; + double* nbe_scr; + double* zmat; + double* bf, *dbfx, *dbfy, *dbfz; + double* den, *ddenx, *ddeny, *ddenz; + double* eps, *gamma; + double* vrho, *vgamma; + + size_t iParent; + double dist_nearest; + double * dist_scratch; + +}; + +} +#endif + } diff --git a/src/integrator/cuda/collocation_device.cu b/src/integrator/cuda/collocation_device.cu index 858b8e943..325aa5db0 100644 --- a/src/integrator/cuda/collocation_device.cu +++ b/src/integrator/cuda/collocation_device.cu @@ -1,7 +1,10 @@ #include +#include #include "collocation_petite_kernels.hpp" #include "collocation_masked_kernels.hpp" +#include "collocation_petite_combined_kernels.hpp" +#include "collocation_masked_combined_kernels.hpp" namespace GauXC { namespace integrator { @@ -91,10 +94,79 @@ void eval_collocation_masked( +template +void eval_collocation_petite_combined( + size_t ntasks, + size_t npts_max, + size_t nshells_max, + XCTaskDevice* device_tasks, + cudaStream_t stream +) { + + dim3 threads(32, 32, 1); + dim3 blocks( util::div_ceil( npts_max, threads.x ), + util::div_ceil( nshells_max, threads.y ), + ntasks ); + + collocation_device_petite_combined_kernel + <<>> + ( ntasks, device_tasks ); + +} + +template +void eval_collocation_petite_combined( + size_t ntasks, + size_t npts_max, + size_t nshells_max, + XCTaskDevice* device_tasks, + cudaStream_t stream +); + + + + + + + + + + +template +void eval_collocation_masked_combined( + size_t ntasks, + size_t npts_max, + size_t nshells_max, + Shell* shells_device, + XCTaskDevice* device_tasks, + cudaStream_t stream +) { + + dim3 threads(32, 32, 1); + dim3 blocks( util::div_ceil( npts_max, threads.x ), + util::div_ceil( nshells_max, threads.y ), + ntasks ); + + collocation_device_masked_combined_kernel + <<>> + ( ntasks, shells_device, device_tasks ); + +} + +template +void eval_collocation_masked_combined( + size_t ntasks, + size_t npts_max, + size_t nshells_max, + Shell* shells_device, + XCTaskDevice* device_tasks, + cudaStream_t stream +); + @@ -207,6 +279,93 @@ void eval_collocation_masked_deriv1( ); + + +template +void eval_collocation_petite_combined_deriv1( + size_t ntasks, + size_t npts_max, + size_t nshells_max, + XCTaskDevice* device_tasks, + cudaStream_t stream +) { + + dim3 threads(32, 32, 1); + dim3 blocks( util::div_ceil( npts_max, threads.x ), + util::div_ceil( nshells_max, threads.y ), + ntasks ); + + collocation_device_petite_combined_kernel_deriv1 + <<>> + ( ntasks, device_tasks ); + +} + +template +void eval_collocation_petite_combined_deriv1( + size_t ntasks, + size_t npts_max, + size_t nshells_max, + XCTaskDevice* device_tasks, + cudaStream_t stream +); + + + + + + + + + + + + + + +template +void eval_collocation_masked_combined_deriv1( + size_t ntasks, + size_t npts_max, + size_t nshells_max, + Shell* shells_device, + XCTaskDevice* device_tasks, + cudaStream_t stream +) { + + dim3 threads(32, 32, 1); + dim3 blocks( util::div_ceil( npts_max, threads.x ), + util::div_ceil( nshells_max, threads.y ), + ntasks ); + + collocation_device_masked_combined_kernel_deriv1 + <<>> + ( ntasks, shells_device, device_tasks ); + +} + +template +void eval_collocation_masked_combined_deriv1( + size_t ntasks, + size_t npts_max, + size_t nshells_max, + Shell* shells_device, + XCTaskDevice* device_tasks, + cudaStream_t stream +); + + + + + + + + + + + + + } // namespace cuda } // namespace integrator } // namespace GauXC diff --git a/src/integrator/cuda/collocation_device.hpp b/src/integrator/cuda/collocation_device.hpp index 5bc59c2fa..4e0a40242 100644 --- a/src/integrator/cuda/collocation_device.hpp +++ b/src/integrator/cuda/collocation_device.hpp @@ -1,9 +1,12 @@ #include +#include namespace GauXC { namespace integrator { namespace cuda { +using namespace GauXC::cuda; + template void eval_collocation_petite( size_t nshells, @@ -60,6 +63,46 @@ void eval_collocation_masked_deriv1( cudaStream_t stream ); +template +void eval_collocation_petite_combined( + size_t ntasks, + size_t npts_max, + size_t nshells_max, + XCTaskDevice* device_tasks, + cudaStream_t stream +); + +template +void eval_collocation_masked_combined( + size_t ntasks, + size_t npts_max, + size_t nshells_max, + Shell* shells_device, + XCTaskDevice* device_tasks, + cudaStream_t stream +); + + + +template +void eval_collocation_petite_combined_deriv1( + size_t ntasks, + size_t npts_max, + size_t nshells_max, + XCTaskDevice* device_tasks, + cudaStream_t stream +); + +template +void eval_collocation_masked_combined_deriv1( + size_t ntasks, + size_t npts_max, + size_t nshells_max, + Shell* shells_device, + XCTaskDevice* device_tasks, + cudaStream_t stream +); + } // namespace cuda } // namespace integrator } // namespace GauXC diff --git a/src/integrator/cuda/collocation_masked_combined_kernels.hpp b/src/integrator/cuda/collocation_masked_combined_kernels.hpp new file mode 100644 index 000000000..85b299e92 --- /dev/null +++ b/src/integrator/cuda/collocation_masked_combined_kernels.hpp @@ -0,0 +1,188 @@ +#include +#include + +#include +#include + +#include "collocation_angular_cartesian.hpp" +#include "collocation_angular_spherical_unnorm.hpp" + +namespace GauXC { +namespace integrator { +namespace cuda { + +using namespace GauXC::cuda; + +template +__global__ +void collocation_device_masked_combined_kernel( + size_t ntasks, + Shell* shells_device, + XCTaskDevice* device_tasks +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + const int batch_id = blockIdx.z; + + if( batch_id < ntasks ) { + + auto& task = device_tasks[ batch_id ]; + + const auto nshells = task.nshells; + const auto nbf = task.nbe; + const auto npts = task.npts; + const auto* pts_device = task.points; + const auto* mask_device = task.shell_list; + const auto* offs_device = task.shell_offs; + + auto* eval_device = task.bf; + + if( tid_x < npts and tid_y < nshells ) { + + const size_t ipt = tid_x; + const size_t ish = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[mask_device[ish]]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); + + const auto xc = pt[0] - O[0]; + const auto yc = pt[1] - O[1]; + const auto zc = pt[2] - O[2]; + + const auto rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim(); + auto tmp = 0.; + for( size_t i = 0; i < nprim; ++i ) + tmp += coeff[i] * std::exp( - alpha[i] * rsq ); + + auto * bf_eval = eval_device + ibf + ipt*nbf; + + const bool do_sph = shell.pure(); + if( do_sph ) + collocation_spherical_unnorm_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); + else + collocation_cartesian_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); + + } // shell / point idx check + + } // Batch idx check + +} + + + + + + + + + + + + + + + +template +__global__ +void collocation_device_masked_combined_kernel_deriv1( + size_t ntasks, + Shell* shells_device, + XCTaskDevice* device_tasks +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + const int batch_id = blockIdx.z; + + if( batch_id < ntasks ) { + + auto& task = device_tasks[ batch_id ]; + + const auto nshells = task.nshells; + const auto nbf = task.nbe; + const auto npts = task.npts; + const auto* pts_device = task.points; + const auto* mask_device = task.shell_list; + const auto* offs_device = task.shell_offs; + + auto* eval_device = task.bf; + auto* deval_device_x = task.dbfx; + auto* deval_device_y = task.dbfy; + auto* deval_device_z = task.dbfz; + + + if( tid_x < npts and tid_y < nshells ) { + + const size_t ipt = tid_x; + const size_t ish = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[mask_device[ish]]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); + + const auto xc = pt[0] - O[0]; + const auto yc = pt[1] - O[1]; + const auto zc = pt[2] - O[2]; + + const auto rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim(); + auto tmp = 0.; + auto tmp_x = 0., tmp_y = 0., tmp_z = 0.; + for( size_t i = 0; i < nprim; ++i ) { + + const auto a = alpha[i]; + const auto e = coeff[i] * std::exp( - a * rsq ); + + const auto ae = 2. * a * e; + + tmp += e; + tmp_x -= ae * xc; + tmp_y -= ae * yc; + tmp_z -= ae * zc; + + } + + auto * bf_eval = eval_device + ibf + ipt*nbf; + auto * dx_eval = deval_device_x + ibf + ipt*nbf; + auto * dy_eval = deval_device_y + ibf + ipt*nbf; + auto * dz_eval = deval_device_z + ibf + ipt*nbf; + + const bool do_sph = shell.pure(); + if( do_sph ) + collocation_spherical_unnorm_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, + tmp_z, xc, yc, zc, bf_eval, dx_eval, + dy_eval, dz_eval ); + else + collocation_cartesian_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, + xc, yc, zc, bf_eval, dx_eval, + dy_eval, dz_eval ); + + } // shell / point idx check + + } // Batch idx check + + +} + +} // namespace cuda +} // namespace integrator +} // namespace GauXC diff --git a/src/integrator/cuda/collocation_petite_combined_kernels.hpp b/src/integrator/cuda/collocation_petite_combined_kernels.hpp new file mode 100644 index 000000000..8483f0181 --- /dev/null +++ b/src/integrator/cuda/collocation_petite_combined_kernels.hpp @@ -0,0 +1,186 @@ +#include +#include + +#include +#include + +#include "collocation_angular_cartesian.hpp" +#include "collocation_angular_spherical_unnorm.hpp" + +namespace GauXC { +namespace integrator { +namespace cuda { + +using namespace GauXC::cuda; + +template +__global__ +void collocation_device_petite_combined_kernel( + size_t ntasks, + XCTaskDevice* device_tasks +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + const int batch_id = blockIdx.z; + + if( batch_id < ntasks ) { + + auto& task = device_tasks[ batch_id ]; + + const auto nshells = task.nshells; + const auto nbf = task.nbe; + const auto npts = task.npts; + const auto* shells_device = task.shells; + const auto* pts_device = task.points; + const auto* offs_device = task.shell_offs; + + auto* eval_device = task.bf; + + if( tid_x < npts and tid_y < nshells ) { + + const size_t ipt = tid_x; + const size_t ish = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[ish]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); + + const auto xc = pt[0] - O[0]; + const auto yc = pt[1] - O[1]; + const auto zc = pt[2] - O[2]; + + const auto rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim(); + auto tmp = 0.; + for( size_t i = 0; i < nprim; ++i ) + tmp += coeff[i] * std::exp( - alpha[i] * rsq ); + + auto * bf_eval = eval_device + ibf + ipt*nbf; + + const bool do_sph = shell.pure(); + if( do_sph ) + collocation_spherical_unnorm_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); + else + collocation_cartesian_angular( shell.l(), tmp, xc, yc, zc, bf_eval ); + + } // shell / point idx check + + } // Batch idx check + +} + + + + + + + + + + + + + + + +template +__global__ +void collocation_device_petite_combined_kernel_deriv1( + size_t ntasks, + XCTaskDevice* device_tasks +) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + const int batch_id = blockIdx.z; + + if( batch_id < ntasks ) { + + auto& task = device_tasks[ batch_id ]; + + const auto nshells = task.nshells; + const auto nbf = task.nbe; + const auto npts = task.npts; + const auto* shells_device = task.shells; + const auto* pts_device = task.points; + const auto* offs_device = task.shell_offs; + + auto* eval_device = task.bf; + auto* deval_device_x = task.dbfx; + auto* deval_device_y = task.dbfy; + auto* deval_device_z = task.dbfz; + + + if( tid_x < npts and tid_y < nshells ) { + + const size_t ipt = tid_x; + const size_t ish = tid_y; + + const size_t ibf = offs_device[ish]; + + const auto& shell = shells_device[ish]; + const auto* pt = pts_device + 3*ipt; + + + const auto* O = shell.O_data(); + const auto* alpha = shell.alpha_data(); + const auto* coeff = shell.coeff_data(); + + const auto xc = pt[0] - O[0]; + const auto yc = pt[1] - O[1]; + const auto zc = pt[2] - O[2]; + + const auto rsq = xc*xc + yc*yc + zc*zc; + + const size_t nprim = shell.nprim(); + auto tmp = 0.; + auto tmp_x = 0., tmp_y = 0., tmp_z = 0.; + for( size_t i = 0; i < nprim; ++i ) { + + const auto a = alpha[i]; + const auto e = coeff[i] * std::exp( - a * rsq ); + + const auto ae = 2. * a * e; + + tmp += e; + tmp_x -= ae * xc; + tmp_y -= ae * yc; + tmp_z -= ae * zc; + + } + + auto * bf_eval = eval_device + ibf + ipt*nbf; + auto * dx_eval = deval_device_x + ibf + ipt*nbf; + auto * dy_eval = deval_device_y + ibf + ipt*nbf; + auto * dz_eval = deval_device_z + ibf + ipt*nbf; + + const bool do_sph = shell.pure(); + if( do_sph ) + collocation_spherical_unnorm_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, + tmp_z, xc, yc, zc, bf_eval, dx_eval, + dy_eval, dz_eval ); + else + collocation_cartesian_angular_deriv1( shell.l(), tmp, tmp_x, tmp_y, tmp_z, + xc, yc, zc, bf_eval, dx_eval, + dy_eval, dz_eval ); + + } // shell / point idx check + + } // Batch idx check + + +} + +} // namespace cuda +} // namespace integrator +} // namespace GauXC diff --git a/tests/collocation.cxx b/tests/collocation.cxx index b33f15357..e45f14f2f 100644 --- a/tests/collocation.cxx +++ b/tests/collocation.cxx @@ -340,6 +340,225 @@ void test_cuda_collocation_masked( const BasisSet& basis, std::ifstream& } + + + + + + + + + + + + + +void test_cuda_collocation_petite_combined( const BasisSet& basis, std::ifstream& in_file) { + + + + std::vector ref_data; + + { + cereal::BinaryInputArchive ar( in_file ); + ar( ref_data ); + } + + + std::vector< cuda::XCTaskDevice > tasks; + + + cudaStream_t stream = 0; + for( auto& d : ref_data ) { + const auto npts = d.pts.size(); + const auto nbf = d.eval.size() / npts; + + const auto& mask = d.mask; + const auto& pts = d.pts; + + /// XXX: THIS DOES NOT POPULATE A VALID TASK, ONLY WHAT's REQUIRED FOR THIS + // TEST + auto& task = tasks.emplace_back(); + task.nbe = nbf; + task.npts = npts; + task.nshells = mask.size(); + + task.points = safe_cuda_malloc( 3 * npts ); + task.shell_offs = safe_cuda_malloc( mask.size() ); + task.shells = safe_cuda_malloc>(mask.size()); + task.bf = safe_cuda_malloc( nbf * npts ); + + auto* pts_device = task.points; + auto* offs_device = task.shell_offs; + auto* shells_device = task.shells; + + + safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + + std::vector offs( mask.size() ); + offs[0] = 0; + for( int i = 1; i < mask.size(); ++i ) + offs[i] = offs[i-1] + basis[mask[i-1]].size(); + safe_cuda_copy( offs.size(), offs_device, offs.data() ); + + std::vector> shells; + for( auto idx : mask ) shells.emplace_back(basis[idx]); + safe_cuda_copy( shells.size(), shells_device, shells.data() ); + + + } + + + const auto nshells_max = std::max_element( tasks.begin(), tasks.end(), + []( const auto& a, const auto& b ) { + return a.nshells < b.nshells; + })->nshells; + + const auto npts_max = std::max_element( tasks.begin(), tasks.end(), + []( const auto& a, const auto& b ) { + return a.npts < b.npts; + })->npts; + + auto* tasks_device = safe_cuda_malloc>( tasks.size() ); + safe_cuda_copy( tasks.size(), tasks_device, tasks.data() ); + + integrator::cuda::eval_collocation_petite_combined( tasks.size(), npts_max, + nshells_max, tasks_device, stream ); + + safe_cuda_device_sync(); + + + for( int i = 0; i < tasks.size(); i++ ) { + + auto* ref_eval = ref_data[i].eval.data(); + std::vector eval (tasks[i].nbe * tasks[i].npts); + safe_cuda_copy( eval.size(), eval.data(), tasks[i].bf ); + + for( auto i = 0; i < eval.size(); ++i ) + CHECK( eval[i] == Approx( ref_eval[i] ) ); + } + + + for( auto& t : tasks ) { + safe_cuda_free( t.points, t.shell_offs, t.shells, t.bf ); + } + safe_cuda_free( tasks_device ); +} + + +void test_cuda_collocation_masked_combined( const BasisSet& basis, std::ifstream& in_file) { + + + + std::vector ref_data; + + { + cereal::BinaryInputArchive ar( in_file ); + ar( ref_data ); + } + + + std::vector< cuda::XCTaskDevice > tasks; + + auto shells_device = safe_cuda_malloc>( basis.size() ); + std::vector> shells( basis ); + safe_cuda_copy( basis.size(), shells_device, shells.data() ); + + cudaStream_t stream = 0; + for( auto& d : ref_data ) { + const auto npts = d.pts.size(); + const auto nbf = d.eval.size() / npts; + + const auto& mask = d.mask; + const auto& pts = d.pts; + + /// XXX: THIS DOES NOT POPULATE A VALID TASK, ONLY WHAT's REQUIRED FOR THIS + // TEST + auto& task = tasks.emplace_back(); + task.nbe = nbf; + task.npts = npts; + task.nshells = mask.size(); + + task.points = safe_cuda_malloc( 3 * npts ); + task.shell_offs = safe_cuda_malloc( mask.size() ); + task.shell_list = safe_cuda_malloc( mask.size() ); + task.bf = safe_cuda_malloc( nbf * npts ); + + auto* pts_device = task.points; + auto* offs_device = task.shell_offs; + auto* mask_device = task.shell_list; + + + safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + + std::vector mask_ul( mask.size() ); + std::copy( mask.begin(), mask.end(), mask_ul.begin() ); + safe_cuda_copy( mask.size(), mask_device, mask_ul.data() ); + + std::vector offs( mask.size() ); + offs[0] = 0; + for( int i = 1; i < mask.size(); ++i ) + offs[i] = offs[i-1] + basis[mask[i-1]].size(); + safe_cuda_copy( offs.size(), offs_device, offs.data() ); + + + } + + + const auto nshells_max = std::max_element( tasks.begin(), tasks.end(), + []( const auto& a, const auto& b ) { + return a.nshells < b.nshells; + })->nshells; + + const auto npts_max = std::max_element( tasks.begin(), tasks.end(), + []( const auto& a, const auto& b ) { + return a.npts < b.npts; + })->npts; + + auto* tasks_device = safe_cuda_malloc>( tasks.size() ); + safe_cuda_copy( tasks.size(), tasks_device, tasks.data() ); + + integrator::cuda::eval_collocation_masked_combined( tasks.size(), npts_max, + nshells_max, shells_device, tasks_device, stream ); + + safe_cuda_device_sync(); + + + for( int i = 0; i < tasks.size(); i++ ) { + + auto* ref_eval = ref_data[i].eval.data(); + std::vector eval (tasks[i].nbe * tasks[i].npts); + safe_cuda_copy( eval.size(), eval.data(), tasks[i].bf ); + + for( auto i = 0; i < eval.size(); ++i ) + CHECK( eval[i] == Approx( ref_eval[i] ) ); + } + + + for( auto& t : tasks ) { + safe_cuda_free( t.points, t.shell_offs, t.shell_list, t.bf ); + } + safe_cuda_free( tasks_device, shells_device ); +} + + + + + + + + + + + + + + + + + + + void test_cuda_collocation_deriv1_petite( const BasisSet& basis, std::ifstream& in_file) { @@ -492,6 +711,243 @@ void test_cuda_collocation_deriv1_masked( const BasisSet& basis, std::if safe_cuda_free(shells_device, offs_device, pts_device, eval_device, deval_device_x, deval_device_y, deval_device_z); } + + + + + + + +void test_cuda_collocation_petite_combined_deriv1( const BasisSet& basis, std::ifstream& in_file) { + + + + std::vector ref_data; + + { + cereal::BinaryInputArchive ar( in_file ); + ar( ref_data ); + } + + + std::vector< cuda::XCTaskDevice > tasks; + + + cudaStream_t stream = 0; + for( auto& d : ref_data ) { + const auto npts = d.pts.size(); + const auto nbf = d.eval.size() / npts; + + const auto& mask = d.mask; + const auto& pts = d.pts; + + /// XXX: THIS DOES NOT POPULATE A VALID TASK, ONLY WHAT's REQUIRED FOR THIS + // TEST + auto& task = tasks.emplace_back(); + task.nbe = nbf; + task.npts = npts; + task.nshells = mask.size(); + + task.points = safe_cuda_malloc( 3 * npts ); + task.shell_offs = safe_cuda_malloc( mask.size() ); + task.shells = safe_cuda_malloc>(mask.size()); + task.bf = safe_cuda_malloc( nbf * npts ); + task.dbfx = safe_cuda_malloc( nbf * npts ); + task.dbfy = safe_cuda_malloc( nbf * npts ); + task.dbfz = safe_cuda_malloc( nbf * npts ); + + auto* pts_device = task.points; + auto* offs_device = task.shell_offs; + auto* shells_device = task.shells; + + + safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + + std::vector offs( mask.size() ); + offs[0] = 0; + for( int i = 1; i < mask.size(); ++i ) + offs[i] = offs[i-1] + basis[mask[i-1]].size(); + safe_cuda_copy( offs.size(), offs_device, offs.data() ); + + std::vector> shells; + for( auto idx : mask ) shells.emplace_back(basis[idx]); + safe_cuda_copy( shells.size(), shells_device, shells.data() ); + + + } + + + const auto nshells_max = std::max_element( tasks.begin(), tasks.end(), + []( const auto& a, const auto& b ) { + return a.nshells < b.nshells; + })->nshells; + + const auto npts_max = std::max_element( tasks.begin(), tasks.end(), + []( const auto& a, const auto& b ) { + return a.npts < b.npts; + })->npts; + + auto* tasks_device = safe_cuda_malloc>( tasks.size() ); + safe_cuda_copy( tasks.size(), tasks_device, tasks.data() ); + + integrator::cuda::eval_collocation_petite_combined_deriv1( tasks.size(), npts_max, + nshells_max, tasks_device, stream ); + + safe_cuda_device_sync(); + + + for( int i = 0; i < tasks.size(); i++ ) { + + auto* ref_eval = ref_data[i].eval.data(); + auto* ref_deval_x = ref_data[i].deval_x.data(); + auto* ref_deval_y = ref_data[i].deval_y.data(); + auto* ref_deval_z = ref_data[i].deval_z.data(); + + std::vector eval (tasks[i].nbe * tasks[i].npts); + std::vector deval_x (tasks[i].nbe * tasks[i].npts); + std::vector deval_y (tasks[i].nbe * tasks[i].npts); + std::vector deval_z (tasks[i].nbe * tasks[i].npts); + + safe_cuda_copy( eval.size(), eval.data(), tasks[i].bf ); + safe_cuda_copy( eval.size(), deval_x.data(), tasks[i].dbfx ); + safe_cuda_copy( eval.size(), deval_y.data(), tasks[i].dbfy ); + safe_cuda_copy( eval.size(), deval_z.data(), tasks[i].dbfz ); + + for( auto i = 0; i < eval.size(); ++i ) + CHECK( eval[i] == Approx( ref_eval[i] ) ); + for( auto i = 0; i < eval.size(); ++i ) + CHECK( deval_x[i] == Approx( ref_deval_x[i] ) ); + for( auto i = 0; i < eval.size(); ++i ) + CHECK( deval_y[i] == Approx( ref_deval_y[i] ) ); + for( auto i = 0; i < eval.size(); ++i ) + CHECK( deval_z[i] == Approx( ref_deval_z[i] ) ); + } + + + for( auto& t : tasks ) { + safe_cuda_free( t.points, t.shell_offs, t.shells, t.bf, t.dbfx, t.dbfy, + t.dbfz ); + } + safe_cuda_free( tasks_device ); +} + + +void test_cuda_collocation_masked_combined_deriv1( const BasisSet& basis, std::ifstream& in_file) { + + + + std::vector ref_data; + + { + cereal::BinaryInputArchive ar( in_file ); + ar( ref_data ); + } + + + std::vector< cuda::XCTaskDevice > tasks; + + auto shells_device = safe_cuda_malloc>( basis.size() ); + std::vector> shells( basis ); + safe_cuda_copy( basis.size(), shells_device, shells.data() ); + + cudaStream_t stream = 0; + for( auto& d : ref_data ) { + const auto npts = d.pts.size(); + const auto nbf = d.eval.size() / npts; + + const auto& mask = d.mask; + const auto& pts = d.pts; + + /// XXX: THIS DOES NOT POPULATE A VALID TASK, ONLY WHAT's REQUIRED FOR THIS + // TEST + auto& task = tasks.emplace_back(); + task.nbe = nbf; + task.npts = npts; + task.nshells = mask.size(); + + task.points = safe_cuda_malloc( 3 * npts ); + task.shell_offs = safe_cuda_malloc( mask.size() ); + task.shell_list = safe_cuda_malloc( mask.size() ); + task.bf = safe_cuda_malloc( nbf * npts ); + task.dbfx = safe_cuda_malloc( nbf * npts ); + task.dbfy = safe_cuda_malloc( nbf * npts ); + task.dbfz = safe_cuda_malloc( nbf * npts ); + + + auto* pts_device = task.points; + auto* offs_device = task.shell_offs; + auto* mask_device = task.shell_list; + + + safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + + std::vector mask_ul( mask.size() ); + std::copy( mask.begin(), mask.end(), mask_ul.begin() ); + safe_cuda_copy( mask.size(), mask_device, mask_ul.data() ); + + std::vector offs( mask.size() ); + offs[0] = 0; + for( int i = 1; i < mask.size(); ++i ) + offs[i] = offs[i-1] + basis[mask[i-1]].size(); + safe_cuda_copy( offs.size(), offs_device, offs.data() ); + + + } + + + const auto nshells_max = std::max_element( tasks.begin(), tasks.end(), + []( const auto& a, const auto& b ) { + return a.nshells < b.nshells; + })->nshells; + + const auto npts_max = std::max_element( tasks.begin(), tasks.end(), + []( const auto& a, const auto& b ) { + return a.npts < b.npts; + })->npts; + + auto* tasks_device = safe_cuda_malloc>( tasks.size() ); + safe_cuda_copy( tasks.size(), tasks_device, tasks.data() ); + + integrator::cuda::eval_collocation_masked_combined_deriv1( tasks.size(), npts_max, + nshells_max, shells_device, tasks_device, stream ); + + safe_cuda_device_sync(); + + + for( int i = 0; i < tasks.size(); i++ ) { + + auto* ref_eval = ref_data[i].eval.data(); + auto* ref_deval_x = ref_data[i].deval_x.data(); + auto* ref_deval_y = ref_data[i].deval_y.data(); + auto* ref_deval_z = ref_data[i].deval_z.data(); + + std::vector eval (tasks[i].nbe * tasks[i].npts); + std::vector deval_x (tasks[i].nbe * tasks[i].npts); + std::vector deval_y (tasks[i].nbe * tasks[i].npts); + std::vector deval_z (tasks[i].nbe * tasks[i].npts); + + safe_cuda_copy( eval.size(), eval.data(), tasks[i].bf ); + safe_cuda_copy( eval.size(), deval_x.data(), tasks[i].dbfx ); + safe_cuda_copy( eval.size(), deval_y.data(), tasks[i].dbfy ); + safe_cuda_copy( eval.size(), deval_z.data(), tasks[i].dbfz ); + + for( auto i = 0; i < eval.size(); ++i ) + CHECK( eval[i] == Approx( ref_eval[i] ) ); + for( auto i = 0; i < eval.size(); ++i ) + CHECK( deval_x[i] == Approx( ref_deval_x[i] ) ); + for( auto i = 0; i < eval.size(); ++i ) + CHECK( deval_y[i] == Approx( ref_deval_y[i] ) ); + for( auto i = 0; i < eval.size(); ++i ) + CHECK( deval_z[i] == Approx( ref_deval_z[i] ) ); + } + + + for( auto& t : tasks ) { + safe_cuda_free( t.points, t.shell_offs, t.shell_list, t.bf, t.dbfx, t.dbfy, + t.dbfz ); + } + safe_cuda_free( tasks_device, shells_device ); +} #endif //#define GENERATE_TESTS @@ -531,6 +987,12 @@ TEST_CASE( "Water / cc-pVDZ", "[collocation]" ) { SECTION( "CUDA Eval: Masked" ) { test_cuda_collocation_masked( basis, ref_data ); } + SECTION( "CUDA Eval: Petite Combined" ) { + test_cuda_collocation_petite_combined( basis, ref_data ); + } + SECTION( "CUDA Eval: Masked Combined" ) { + test_cuda_collocation_masked_combined( basis, ref_data ); + } SECTION( "CUDA Eval Grad: Petite Shell List" ) { test_cuda_collocation_deriv1_petite( basis, ref_data ); @@ -538,6 +1000,12 @@ TEST_CASE( "Water / cc-pVDZ", "[collocation]" ) { SECTION( "CUDA Eval Grad: Masked" ) { test_cuda_collocation_deriv1_masked( basis, ref_data ); } + SECTION( "CUDA Eval Grad: Petite Combined" ) { + test_cuda_collocation_petite_combined_deriv1( basis, ref_data ); + } + SECTION( "CUDA Eval: Masked Combined" ) { + test_cuda_collocation_masked_combined_deriv1( basis, ref_data ); + } #endif #endif From 1b9b5cbe72f8e6c26f409fcf7053a8d687d8de92 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Wed, 19 Aug 2020 16:17:25 -0400 Subject: [PATCH 18/27] [WIP] Adding back CUDA integrator: added convienience utilities + base memory allocation in XCCudaData --- cmake/modules/FindMAGMA.cmake | 39 +++ include/gauxc/exceptions/cublas_exception.hpp | 89 ++++++ include/gauxc/exceptions/cuda_exception.hpp | 45 +++ include/gauxc/exceptions/magma_exception.hpp | 45 +++ include/gauxc/gauxc_config.hpp.in | 2 + include/gauxc/util/cublas_util.hpp | 35 +++ include/gauxc/util/cuda_util.hpp | 94 ++++++ include/gauxc/util/magma_util.hpp | 46 +++ .../default_xc_cuda_integrator.hpp | 129 ++++++++ .../default_xc_host_integrator.hpp | 2 +- include/gauxc/xc_integrator/xc_cuda_data.hpp | 104 ++++++ src/integrator/cuda/buffer_adaptor.hpp | 50 +++ .../cuda/gauxc-cuda_integrator.cmake | 8 +- src/integrator/cuda/xc_cuda_data.cxx | 80 +++++ tests/collocation.cxx | 297 ++++++++---------- 15 files changed, 895 insertions(+), 170 deletions(-) create mode 100644 cmake/modules/FindMAGMA.cmake create mode 100644 include/gauxc/exceptions/cublas_exception.hpp create mode 100644 include/gauxc/exceptions/cuda_exception.hpp create mode 100644 include/gauxc/exceptions/magma_exception.hpp create mode 100644 include/gauxc/util/cublas_util.hpp create mode 100644 include/gauxc/util/cuda_util.hpp create mode 100644 include/gauxc/util/magma_util.hpp create mode 100644 include/gauxc/xc_integrator/default_xc_cuda_integrator.hpp create mode 100644 include/gauxc/xc_integrator/xc_cuda_data.hpp create mode 100644 src/integrator/cuda/buffer_adaptor.hpp create mode 100644 src/integrator/cuda/xc_cuda_data.cxx diff --git a/cmake/modules/FindMAGMA.cmake b/cmake/modules/FindMAGMA.cmake new file mode 100644 index 000000000..c0330ff16 --- /dev/null +++ b/cmake/modules/FindMAGMA.cmake @@ -0,0 +1,39 @@ +if( NOT DEFINED MAGMA_ROOT_DIR ) + find_package(PkgConfig) + pkg_check_modules( PC_MAGMA QUIET magma ) +endif() + +find_path( MAGMA_INCLUDE_DIR magma.h + HINTS ${PC_MAGMA_INCLUDEDIR} + ${PC_MAGMA_INCLUDE_DIRS} + PATHS ${MAGMA_ROOT_DIR} + PATH_SUFFIXES include +) + +find_library( MAGMA_LIBRARY NAMES magma + HINTS ${PC_MAGMA_LIBDIR} + ${PC_MAGMA_LIBRARY_DIRS} + PATHS ${MAGMA_ROOT_DIR} + PATH_SUFFIXES lib lib64 lib32 +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + MAGMA DEFAULT_MSG + MAGMA_LIBRARY + MAGMA_INCLUDE_DIR +) + +if( MAGMA_FOUND AND NOT TARGET MAGMA::magma ) + + set( MAGMA_INCLUDE_DIRS ${MAGMA_INCLUDE_DIR} ) + set( MAGMA_LIBRARIES ${MAGMA_LIBRARY} ) + + add_library( MAGMA::magma INTERFACE IMPORTED ) + set_target_properties( MAGMA::magma PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${MAGMA_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${MAGMA_LIBRARIES}" + ) + +endif() + diff --git a/include/gauxc/exceptions/cublas_exception.hpp b/include/gauxc/exceptions/cublas_exception.hpp new file mode 100644 index 000000000..315bcd535 --- /dev/null +++ b/include/gauxc/exceptions/cublas_exception.hpp @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include +#include + +#ifdef GAUXC_ENABLE_CUDA + +namespace GauXC { + +namespace detail { + +static std::string cublasGetErrorString(cublasStatus_t error) { + switch (error) + { + case CUBLAS_STATUS_SUCCESS: + return "CUBLAS_STATUS_SUCCESS"; + + case CUBLAS_STATUS_NOT_INITIALIZED: + return "CUBLAS_STATUS_NOT_INITIALIZED"; + + case CUBLAS_STATUS_ALLOC_FAILED: + return "CUBLAS_STATUS_ALLOC_FAILED"; + + case CUBLAS_STATUS_INVALID_VALUE: + return "CUBLAS_STATUS_INVALID_VALUE"; + + case CUBLAS_STATUS_ARCH_MISMATCH: + return "CUBLAS_STATUS_ARCH_MISMATCH"; + + case CUBLAS_STATUS_MAPPING_ERROR: + return "CUBLAS_STATUS_MAPPING_ERROR"; + + case CUBLAS_STATUS_EXECUTION_FAILED: + return "CUBLAS_STATUS_EXECUTION_FAILED"; + + case CUBLAS_STATUS_INTERNAL_ERROR: + return "CUBLAS_STATUS_INTERNAL_ERROR"; + + case CUBLAS_STATUS_NOT_SUPPORTED: + return "CUBLAS_STATUS_NOT_SUPPORTED"; + + case CUBLAS_STATUS_LICENSE_ERROR: + return "CUBLAS_STATUS_LICENSE_ERROR"; + } + + return ""; +} + +} + +class cublas_exception : public std::exception { + + std::string file_; + int line_; + std::string msg_prefix_; + cublasStatus_t err_code_; + + const char* what() const noexcept override { + std::stringstream ss; + ss << "CUBLAS Exception (" << msg_prefix_ << ")" << std::endl + << " Error Code " << int(err_code_) << ": \"" + << detail::cublasGetErrorString( err_code_ ) + << "\"" << std::endl + << " File " << file_ << std::endl + << " Line " << line_ << std::endl; + + auto msg = ss.str(); + + return strdup( msg.c_str() ); + } + +public: + + cublas_exception( std::string file, int line, std::string msg, + cublasStatus_t err ) : + file_(file), line_(line), msg_prefix_(msg), err_code_(err) { } + +}; + +} + + +#define GAUXC_CUBLAS_ERROR( MSG, ERR ) \ + if( ERR != CUBLAS_STATUS_SUCCESS ) \ + throw cublas_exception( __FILE__, __LINE__, MSG, ERR ); + +#endif diff --git a/include/gauxc/exceptions/cuda_exception.hpp b/include/gauxc/exceptions/cuda_exception.hpp new file mode 100644 index 000000000..e41fd2220 --- /dev/null +++ b/include/gauxc/exceptions/cuda_exception.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include +#include + +#ifdef GAUXC_ENABLE_CUDA + +namespace GauXC { + +class cuda_exception : public std::exception { + + std::string file_; + int line_; + std::string msg_prefix_; + cudaError_t err_code_; + + const char* what() const noexcept override { + std::stringstream ss; + ss << "CUDA Exception (" << msg_prefix_ << ")" << std::endl + << " Error Code " << int(err_code_) << ": \"" + << cudaGetErrorString( err_code_ ) << "\"" << std::endl + << " File " << file_ << std::endl + << " Line " << line_ << std::endl; + + auto msg = ss.str(); + + return strdup( msg.c_str() ); + } + +public: + + cuda_exception( std::string file, int line, std::string msg, cudaError_t err ) : + file_(file), line_(line), msg_prefix_(msg), err_code_(err) { } + +}; + +} + +#define GAUXC_CUDA_ERROR( MSG, ERR ) \ + if( ERR != cudaSuccess ) \ + throw cuda_exception( __FILE__, __LINE__, MSG, ERR ); + +#endif diff --git a/include/gauxc/exceptions/magma_exception.hpp b/include/gauxc/exceptions/magma_exception.hpp new file mode 100644 index 000000000..ab32bac67 --- /dev/null +++ b/include/gauxc/exceptions/magma_exception.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include +#include + +#ifdef GAUXC_ENABLE_MAGMA + +namespace GauXC { + +class magma_exception : public std::exception { + + std::string file_; + int line_; + std::string msg_prefix_; + magma_int_t err_code_; + + const char* what() const noexcept override { + std::stringstream ss; + ss << "MAGMA Exception (" << msg_prefix_ << ")" << std::endl + << " Error Code " << int(err_code_) << ": \"" + << magma_strerror( err_code_ ) << "\"" << std::endl + << " File " << file_ << std::endl + << " Line " << line_ << std::endl; + + auto msg = ss.str(); + + return strdup( msg.c_str() ); + } + +public: + + magma_exception( std::string file, int line, std::string msg, magma_int_t err ) : + file_(file), line_(line), msg_prefix_(msg), err_code_(err) { } + +}; + +} + +#define GAUXC_MAGMA_ERROR( MSG, ERR ) \ + if( ERR != MAGMA_SUCCESS ) \ + throw magma_exception( __FILE__, __LINE__, MSG, ERR ); + +#endif diff --git a/include/gauxc/gauxc_config.hpp.in b/include/gauxc/gauxc_config.hpp.in index e3c2d8330..319ed608c 100644 --- a/include/gauxc/gauxc_config.hpp.in +++ b/include/gauxc/gauxc_config.hpp.in @@ -10,6 +10,8 @@ #ifdef GAUXC_ENABLE_CUDA #include + #include + #include #endif #ifdef GAUXC_ENABLE_CEREAL diff --git a/include/gauxc/util/cublas_util.hpp b/include/gauxc/util/cublas_util.hpp new file mode 100644 index 000000000..787030bf3 --- /dev/null +++ b/include/gauxc/util/cublas_util.hpp @@ -0,0 +1,35 @@ +#pragma once +#include +#include + +#ifdef GAUXC_ENABLE_CUDA + +namespace GauXC { +namespace util { + +struct cublas_handle { + + cublasHandle_t handle; + inline cublas_handle() { + auto stat = cublasCreate( &handle ); + GAUXC_CUBLAS_ERROR("CUBLAS Handle Create Failed", stat); + } + + inline ~cublas_handle() noexcept { + if( handle != 0 ) cublasDestroy( handle ); + } + + cublas_handle( const cublas_handle& ) = delete; + inline cublas_handle( cublas_handle&& other ) noexcept { + handle = other.handle; + other.handle = 0; + }; + + inline operator cublasHandle_t() const { return handle; } + +}; + +} +} + +#endif diff --git a/include/gauxc/util/cuda_util.hpp b/include/gauxc/util/cuda_util.hpp new file mode 100644 index 000000000..af0beb006 --- /dev/null +++ b/include/gauxc/util/cuda_util.hpp @@ -0,0 +1,94 @@ +#pragma once +#include +#include + +#ifdef GAUXC_ENABLE_CUDA + +namespace GauXC { +namespace util { + +struct cuda_stream { + + cudaStream_t stream; + inline cuda_stream() { + auto stat = cudaStreamCreate( &stream ); + GAUXC_CUDA_ERROR("CUDA Stream Create Failed", stat); + } + + inline ~cuda_stream() noexcept { + if( stream != 0 ) cudaStreamDestroy( stream ); + } + + cuda_stream( const cuda_stream& ) = delete; + inline cuda_stream( cuda_stream&& other ) noexcept { + stream = other.stream; + other.stream = 0; + }; + + inline operator cudaStream_t() const { return stream; } + +}; + + + + + + + + +template +inline T* cuda_malloc( size_t n ) { + + T* ptr; + auto stat = cudaMalloc( (void**)&ptr, n * sizeof(T) ); + GAUXC_CUDA_ERROR( "CUDA Malloc Failed", stat ); + + return ptr; +} + + + + + + +template +inline void cuda_free( T*& ptr ) { + auto stat = cudaFree( (void*)ptr ); + GAUXC_CUDA_ERROR( "CUDA Free Failed", stat ); + ptr = nullptr; +} + +template +inline void cuda_free( T*& ptr, Args&&... args ) { + cuda_free(ptr); + cuda_free(std::forward(args)...); +} + + + + + +template +inline void cuda_copy( size_t len, T* dest, const T* src ) { + auto stat = cudaMemcpy( dest, src, len * sizeof(T), cudaMemcpyDefault ); + GAUXC_CUDA_ERROR( "CUDA Memcpy Failed", stat ); +} + +template +inline void cuda_copy_async( size_t len, T* dest, const T* src, cudaStream_t s ) { + auto stat = cudaMemcpyAsync( dest, src, len * sizeof(T), cudaMemcpyDefault, s ); + GAUXC_CUDA_ERROR( "CUDA Memcpy Async Failed", stat ); +} + + + + +inline void cuda_device_sync() { + auto stat = cudaDeviceSynchronize(); + GAUXC_CUDA_ERROR( "CUDA Device Sync Failed", stat ); +} + +} +} + +#endif diff --git a/include/gauxc/util/magma_util.hpp b/include/gauxc/util/magma_util.hpp new file mode 100644 index 000000000..2ff3c199c --- /dev/null +++ b/include/gauxc/util/magma_util.hpp @@ -0,0 +1,46 @@ +#pragma once +#include +#include + +#ifdef GAUXC_ENABLE_CUDA + +namespace GauXC { +namespace util { + +struct magma_queue { + + magma_queue_t queue; + + + + inline magma_queue(magma_int_t dev) { + magma_queue_create( dev, &queue ); + } + + inline magma_queue() : magma_queue(0) { } + + inline magma_queue( magma_int_t dev, cudaStream_t stream, cublasHandle_t handle ) { + magma_queue_create_from_cuda( dev, stream, handle, NULL, &queue ); + } + + inline magma_queue( cudaStream_t stream, cublasHandle_t handle ) : + magma_queue( 0, stream, handle ) { } + + inline ~magma_queue() noexcept { + if( queue != 0 ) magma_queue_destroy( queue ); + } + + magma_queue( const magma_queue& ) = delete; + inline magma_queue( magma_queue&& other ) noexcept { + queue = other.queue; + other.queue = 0; + }; + + inline operator magma_queue_t() const { return queue; } + +}; + +} +} + +#endif diff --git a/include/gauxc/xc_integrator/default_xc_cuda_integrator.hpp b/include/gauxc/xc_integrator/default_xc_cuda_integrator.hpp new file mode 100644 index 000000000..14799c7db --- /dev/null +++ b/include/gauxc/xc_integrator/default_xc_cuda_integrator.hpp @@ -0,0 +1,129 @@ +#pragma once +#include +#include +#include + +namespace GauXC { +namespace detail { + +using namespace GauXC::integrator::cuda; + + +template +class DefaultXCCudaIntegrator : public XCIntegratorImpl { + + using base_type = XCIntegratorImpl; + using matrix_type = typename base_type::matrix_type; + using value_type = typename base_type::value_type; + using basisset_type = typename base_type::basisset_type; + using exc_vxc_type = typename base_type::exc_vxc_type; + + std::shared_ptr< XCCudaData< value_type > > cuda_data_; + + exc_vxc_type eval_exc_vxc_( const MatrixType& ) override; + +public: + + template + DefaultXCCudaIntegrator( Args&&... args ) : + base_type( std::forward(args)... ) { } + + DefaultXCCudaIntegrator( const DefaultXCCudaIntegrator& ) = default; + DefaultXCCudaIntegrator( DefaultXCCudaIntegrator&& ) noexcept = default; + + ~DefaultXCCudaIntegrator() noexcept = default; + +}; + + + + +template +typename DefaultXCCudaIntegrator::exc_vxc_type + DefaultXCCudaIntegrator::eval_exc_vxc_( const MatrixType& P ) { + + + // Initialize MAGMA + { + auto ierr = magma_init(); + GAUXC_MAGMA_ERROR( "MAGMA Init Failed", ierr ); + } + + + size_t nbf = this->basis_->nbf(); + + //// TODO: Check that P is sane + + + auto& tasks = this->load_balancer_->get_tasks(); + + size_t max_npts = this->load_balancer_->max_npts(); + size_t max_nbe = this->load_balancer_->max_nbe(); + size_t max_npts_x_nbe = this->load_balancer_->max_npts_x_nbe(); + + size_t n_deriv = this->func_->is_gga() ? 1 : 0; + + // Allocate Memory + cuda_data_ = std::make_shared>( + n_deriv, nbf, max_npts, max_npts_x_nbe + ); + + + // Results + matrix_type VXC( nbf, nbf ); + value_type EXC, N_EL; + + // Compute Local contributions to EXC / VXC + process_batches_cuda_replicated_p< value_type>( + n_deriv, XCWeightAlg::SSF, *this->func_, *this->basis_, + this->load_balancer_->molecule(), this->load_balancer_->molmeta(), + *cuda_data_, tasks, P.data(), VXC.data(), &EXC, &N_EL + ); + + + int world_size; + MPI_Comm_size( this->comm_, &world_size ); + + if( world_size > 1 ) { + + // Test of communicator is an inter-communicator + // XXX: Can't think of a case when this would be true, but who knows... + int inter_flag; + MPI_Comm_test_inter( this->comm_, &inter_flag ); + + // Is Intra-communicator, Allreduce can be done inplace + if( not inter_flag ) { + + MPI_Allreduce( MPI_IN_PLACE, VXC.data(), nbf*nbf, MPI_DOUBLE, + MPI_SUM, this->comm_ ); + MPI_Allreduce( MPI_IN_PLACE, &EXC, 1, MPI_DOUBLE, MPI_SUM, this->comm_ ); + MPI_Allreduce( MPI_IN_PLACE, &N_EL, 1, MPI_DOUBLE, MPI_SUM, this->comm_ ); + + // Isn't Intra-communicator (weird), Allreduce can't be done inplace + } else { + + matrix_type VXC_cpy = VXC; + value_type EXC_cpy = EXC, N_EL_cpy = N_EL; + + MPI_Allreduce( VXC_cpy.data(), VXC.data(), nbf*nbf, MPI_DOUBLE, + MPI_SUM, this->comm_ ); + MPI_Allreduce( &EXC_cpy, &EXC, 1, MPI_DOUBLE, MPI_SUM, this->comm_ ); + MPI_Allreduce( &N_EL_cpy, &N_EL, 1, MPI_DOUBLE, MPI_SUM, this->comm_ ); + + + } + + } + + // Finalize MAGMA + { + auto ierr = magma_finalize(); + GAUXC_MAGMA_ERROR( "MAGMA Finalize Failed", ierr ); + } + + return exc_vxc_type{EXC, std::move(VXC)}; + +} + +} +} diff --git a/include/gauxc/xc_integrator/default_xc_host_integrator.hpp b/include/gauxc/xc_integrator/default_xc_host_integrator.hpp index a4d76a407..b140ec066 100644 --- a/include/gauxc/xc_integrator/default_xc_host_integrator.hpp +++ b/include/gauxc/xc_integrator/default_xc_host_integrator.hpp @@ -67,7 +67,7 @@ typename DefaultXCHostIntegrator::exc_vxc_type // Compute Local contributions to EXC / VXC process_batches_host_replicated_p< value_type>( - n_deriv,XCWeightAlg::SSF, *this->func_, *this->basis_, + n_deriv, XCWeightAlg::SSF, *this->func_, *this->basis_, this->load_balancer_->molecule(), this->load_balancer_->molmeta(), *host_data_, tasks, P.data(), VXC.data(), &EXC, &N_EL ); diff --git a/include/gauxc/xc_integrator/xc_cuda_data.hpp b/include/gauxc/xc_integrator/xc_cuda_data.hpp new file mode 100644 index 000000000..a8f1afe3a --- /dev/null +++ b/include/gauxc/xc_integrator/xc_cuda_data.hpp @@ -0,0 +1,104 @@ +#pragma once +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef GAUXC_ENABLE_CUDA + +namespace GauXC { + +template +struct XCCudaData { + + size_t nshells = 0; + size_t nbf = 0; + size_t n_deriv = 0; + size_t natoms = 0; + + bool denpack_host = false; + bool vxcinc_host = false; + + void* device_ptr = nullptr; + void* dynmem_ptr = nullptr; + size_t dynmem_sz = 0; + + Shell* shells_device = nullptr; + Shell* important_shells_device = nullptr; + + F* vxc_device = nullptr; + F* nbe_scr_device = nullptr; + F* dmat_device = nullptr; + F* zmat_device = nullptr; + F* bf_eval_device = nullptr; + + F* dbf_x_eval_device = nullptr; + F* dbf_y_eval_device = nullptr; + F* dbf_z_eval_device = nullptr; + + F* den_eval_device = nullptr; + F* den_x_eval_device = nullptr; + F* den_y_eval_device = nullptr; + F* den_z_eval_device = nullptr; + F* eps_eval_device = nullptr; + F* gamma_eval_device = nullptr; + + F* vrho_eval_device = nullptr; + F* vgamma_eval_device = nullptr; + + + F* exc_device = nullptr; + F* nel_device = nullptr; + + F* rab_device = nullptr; + F* coords_device = nullptr; + + F** dmat_array_device = nullptr; + F** zmat_array_device = nullptr; + F** bf_array_device = nullptr; + + int* m_array_device = nullptr; + int* n_array_device = nullptr; + int* k_array_device = nullptr; + int* lda_array_device = nullptr; + int* ldb_array_device = nullptr; + int* ldc_array_device = nullptr; + + F* dist_scratch_device = nullptr; + + // Buffer Vars + F* points_device_buffer = nullptr; + F* weights_device_buffer = nullptr; + size_t* shell_list_device_buffer = nullptr; + size_t* shell_offs_device_buffer = nullptr; + int64_t* submat_cut_device_buffer = nullptr; + int32_t* iparent_device_buffer = nullptr; + F* dist_nearest_buffer = nullptr; + + + // Execution management + std::unique_ptr master_stream = nullptr; + std::unique_ptr master_handle = nullptr; + std::unique_ptr master_magma_queue = nullptr; + + XCCudaData( size_t _natoms, + size_t _n_deriv, + size_t _nbf, + size_t _nshells, + bool _denpack_host = false, + bool _vxcinc_host = false ); + + ~XCCudaData() noexcept; + XCCudaData( const XCCudaData& ) = delete; + XCCudaData( XCCudaData&& ) noexcept = delete; + +}; + +} + +#endif diff --git a/src/integrator/cuda/buffer_adaptor.hpp b/src/integrator/cuda/buffer_adaptor.hpp new file mode 100644 index 000000000..130f1f101 --- /dev/null +++ b/src/integrator/cuda/buffer_adaptor.hpp @@ -0,0 +1,50 @@ +#pragma once + +namespace GauXC { + +class buffer_adaptor { + + size_t nalloc_; + size_t nleft_; + void* top_; + void* stack_; + +public: + + buffer_adaptor() = delete; + + inline buffer_adaptor( void* ptr, size_t len ) : + nalloc_(len), + nleft_(len), + top_(ptr), + stack_(ptr) { } + + template + T* aligned_alloc( size_t len, + size_t align = alignof(T) ) { + + char* old_stack = (char*)stack_; + if( std::align( align, + len*sizeof(T), + stack_, + nleft_ ) ) { + + T* result = reinterpret_cast(stack_); + stack_ = (char*)stack_ + len*sizeof(T); + nleft_ -= std::distance( old_stack, + (char*)stack_ ); + return result; + + } + + throw std::bad_alloc(); + + } + + inline void* stack() const {return stack_;} + inline size_t nleft() const { return nleft_; } + +}; + + +} diff --git a/src/integrator/cuda/gauxc-cuda_integrator.cmake b/src/integrator/cuda/gauxc-cuda_integrator.cmake index a7e7fd315..bb1e1e2b3 100644 --- a/src/integrator/cuda/gauxc-cuda_integrator.cmake +++ b/src/integrator/cuda/gauxc-cuda_integrator.cmake @@ -1,9 +1,15 @@ +find_package( CUDAToolkit REQUIRED ) +find_package( MAGMA REQUIRED ) + + add_library( gauxc_device_integrator OBJECT #cuda/collocation_kernels.cu cuda/collocation_device.cu + cuda/xc_cuda_data.cxx ) target_compile_features( gauxc_device_integrator PUBLIC cuda_std_14 cxx_std_14 ) -target_link_libraries( gauxc_device_integrator PUBLIC ExchCXX::exchcxx ) +target_link_libraries( gauxc_device_integrator PUBLIC ExchCXX::exchcxx + MAGMA::magma CUDA::cudart CUDA::cublas ) target_include_directories( gauxc_device_integrator PRIVATE diff --git a/src/integrator/cuda/xc_cuda_data.cxx b/src/integrator/cuda/xc_cuda_data.cxx new file mode 100644 index 000000000..9253b71e9 --- /dev/null +++ b/src/integrator/cuda/xc_cuda_data.cxx @@ -0,0 +1,80 @@ +#include +#include "buffer_adaptor.hpp" + +namespace GauXC { + +template +XCCudaData::XCCudaData( size_t _natoms, + size_t _n_deriv, + size_t _nbf, + size_t _nshells, + bool _denpack_host, + bool _vxcinc_host ): + nshells(_nshells), + nbf(_nbf), + n_deriv(_n_deriv), + natoms(_natoms), + denpack_host(_denpack_host), + vxcinc_host(_vxcinc_host) { + + + // TODO: Expose this + double fill_fraction = 0.9; + + cudaError_t stat; + + // Get Total Available Memory + size_t cuda_avail, cuda_total; + stat = cudaMemGetInfo( &cuda_avail, &cuda_total ); + GAUXC_CUDA_ERROR( "MemInfo Failed", stat ); + + // Allocate up to fill_fraction + size_t fill_sz = fill_fraction * cuda_total; + stat = cudaMalloc( &device_ptr, fill_sz ); + GAUXC_CUDA_ERROR( "CUDA Malloc Failed", stat ); + + // Allocate static memory with proper alignment + buffer_adaptor mem( device_ptr, fill_sz ); + + + shells_device = mem.aligned_alloc>( nshells ); + exc_device = mem.aligned_alloc( 1 ); + nel_device = mem.aligned_alloc( 1 ); + rab_device = mem.aligned_alloc( natoms * natoms ); + coords_device = mem.aligned_alloc( 3 * natoms ); + + if( not vxcinc_host ) + vxc_device = mem.aligned_alloc( nbf * nbf ); + if( not denpack_host ) + dmat_device = mem.aligned_alloc( nbf * nbf ); + + // Get current stack location + dynmem_ptr = mem.stack(); + dynmem_sz = mem.nleft(); + + // Create CUDA Stream and CUBLAS Handles and make them talk to eachother + master_stream = std::make_unique< util::cuda_stream >(); + master_handle = std::make_unique< util::cublas_handle >(); + + cublasSetStream( *master_handle, *master_stream ); + + // Create MAGMA Queue from CUDA Stream and CUBLAS Handle + master_magma_queue = + std::make_unique< util::magma_queue >( 0, *master_stream, *master_handle ); + +} + + + +template +XCCudaData::~XCCudaData() noexcept { + if( device_ptr ) util::cuda_free( device_ptr ); +} + + + + +// Explicit Instantiations +template struct XCCudaData; + +} diff --git a/tests/collocation.cxx b/tests/collocation.cxx index e45f14f2f..0e8dfd8eb 100644 --- a/tests/collocation.cxx +++ b/tests/collocation.cxx @@ -16,51 +16,9 @@ #ifdef GAUXC_ENABLE_CUDA +#include +#include #include "cuda/collocation_device.hpp" -template -inline T* safe_cuda_malloc( size_t n ) { - - T* ptr; - auto stat = cudaMalloc( (void**)&ptr, n * sizeof(T) ); - if( stat != cudaSuccess ) - throw std::runtime_error(std::string("CUDA Malloc Failed: ") + - cudaGetErrorString(stat)); - - return ptr; -} - -template -inline void safe_cuda_free( T*& ptr ) { - auto stat = cudaFree( (void*)ptr ); - if( stat != cudaSuccess ) - throw std::runtime_error(std::string("CUDA Free Failed: ") + - cudaGetErrorString(stat)); - ptr = nullptr; -} - -template -inline void safe_cuda_free( T*& ptr, Args&&... args ) { - safe_cuda_free(ptr); - safe_cuda_free(std::forward(args)...); -} - -template -inline void safe_cuda_copy( size_t len, T* dest, const T* src ) { - - auto stat = cudaMemcpy( dest, src, len * sizeof(T), cudaMemcpyDefault ); - if( stat != cudaSuccess ) - throw std::runtime_error(std::string("CUDA Memcpy Failed: ") + - cudaGetErrorString(stat)); - -} - -inline void safe_cuda_device_sync() { - auto stat = cudaDeviceSynchronize(); - if( stat != cudaSuccess ) - throw std::runtime_error(std::string("CUDA Device Sync Failed: ") + - cudaGetErrorString(stat)); - -} #endif @@ -230,10 +188,10 @@ void test_cuda_collocation_petite( const BasisSet& basis, std::ifstream& ar( ref_data ); } - auto shells_device = safe_cuda_malloc>( basis.size() ); - auto offs_device = safe_cuda_malloc( basis.size() ); - auto pts_device = safe_cuda_malloc( 3 * MAX_NPTS_CHECK ); - auto eval_device = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto shells_device = util::cuda_malloc>( basis.size() ); + auto offs_device = util::cuda_malloc( basis.size() ); + auto pts_device = util::cuda_malloc( 3 * MAX_NPTS_CHECK ); + auto eval_device = util::cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); @@ -245,17 +203,17 @@ void test_cuda_collocation_petite( const BasisSet& basis, std::ifstream& const auto& mask = d.mask; const auto& pts = d.pts; - safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + util::cuda_copy( 3*npts, pts_device, pts.data()->data() ); std::vector offs( mask.size() ); offs[0] = 0; for( int i = 1; i < mask.size(); ++i ) offs[i] = offs[i-1] + basis[mask[i-1]].size(); - safe_cuda_copy( offs.size(), offs_device, offs.data() ); + util::cuda_copy( offs.size(), offs_device, offs.data() ); std::vector> shells; for( auto idx : mask ) shells.emplace_back(basis[idx]); - safe_cuda_copy( shells.size(), shells_device, shells.data() ); + util::cuda_copy( shells.size(), shells_device, shells.data() ); integrator::cuda::eval_collocation_petite( shells.size(), nbf, npts, shells_device, offs_device, @@ -264,14 +222,14 @@ void test_cuda_collocation_petite( const BasisSet& basis, std::ifstream& std::vector eval( nbf * npts ); - safe_cuda_copy( nbf * npts, eval.data(), eval_device ); + util::cuda_copy( nbf * npts, eval.data(), eval_device ); for( auto i = 0; i < npts * nbf; ++i ) CHECK( eval[i] == Approx( d.eval[i] ) ); } - safe_cuda_device_sync(); - safe_cuda_free(shells_device, offs_device, pts_device, eval_device ); + util::cuda_device_sync(); + util::cuda_free(shells_device, offs_device, pts_device, eval_device ); } @@ -289,15 +247,15 @@ void test_cuda_collocation_masked( const BasisSet& basis, std::ifstream& ar( ref_data ); } - auto shells_device = safe_cuda_malloc>( basis.size() ); - auto offs_device = safe_cuda_malloc( basis.size() ); - auto mask_device = safe_cuda_malloc( basis.size() ); - auto pts_device = safe_cuda_malloc( 3 * MAX_NPTS_CHECK ); - auto eval_device = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto shells_device = util::cuda_malloc>( basis.size() ); + auto offs_device = util::cuda_malloc( basis.size() ); + auto mask_device = util::cuda_malloc( basis.size() ); + auto pts_device = util::cuda_malloc( 3 * MAX_NPTS_CHECK ); + auto eval_device = util::cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); std::vector> shells( basis ); - safe_cuda_copy( basis.size(), shells_device, shells.data() ); + util::cuda_copy( basis.size(), shells_device, shells.data() ); cudaStream_t stream = 0; @@ -308,17 +266,17 @@ void test_cuda_collocation_masked( const BasisSet& basis, std::ifstream& const auto& mask = d.mask; const auto& pts = d.pts; - safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + util::cuda_copy( 3*npts, pts_device, pts.data()->data() ); std::vector mask_ul( mask.size() ); std::copy( mask.begin(), mask.end(), mask_ul.begin() ); - safe_cuda_copy( mask.size(), mask_device, mask_ul.data() ); + util::cuda_copy( mask.size(), mask_device, mask_ul.data() ); std::vector offs( mask.size() ); offs[0] = 0; for( int i = 1; i < mask.size(); ++i ) offs[i] = offs[i-1] + basis[mask[i-1]].size(); - safe_cuda_copy( offs.size(), offs_device, offs.data() ); + util::cuda_copy( offs.size(), offs_device, offs.data() ); integrator::cuda::eval_collocation_masked( mask.size(), nbf, npts, @@ -328,14 +286,14 @@ void test_cuda_collocation_masked( const BasisSet& basis, std::ifstream& std::vector eval( nbf * npts ); - safe_cuda_copy( nbf * npts, eval.data(), eval_device ); + util::cuda_copy( nbf * npts, eval.data(), eval_device ); for( auto i = 0; i < npts * nbf; ++i ) CHECK( eval[i] == Approx( d.eval[i] ) ); } - safe_cuda_device_sync(); - safe_cuda_free(shells_device, offs_device, pts_device, eval_device ); + util::cuda_device_sync(); + util::cuda_free(shells_device, offs_device, pts_device, eval_device ); } @@ -383,27 +341,27 @@ void test_cuda_collocation_petite_combined( const BasisSet& basis, std:: task.npts = npts; task.nshells = mask.size(); - task.points = safe_cuda_malloc( 3 * npts ); - task.shell_offs = safe_cuda_malloc( mask.size() ); - task.shells = safe_cuda_malloc>(mask.size()); - task.bf = safe_cuda_malloc( nbf * npts ); + task.points = util::cuda_malloc( 3 * npts ); + task.shell_offs = util::cuda_malloc( mask.size() ); + task.shells = util::cuda_malloc>(mask.size()); + task.bf = util::cuda_malloc( nbf * npts ); auto* pts_device = task.points; auto* offs_device = task.shell_offs; auto* shells_device = task.shells; - safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + util::cuda_copy( 3*npts, pts_device, pts.data()->data() ); std::vector offs( mask.size() ); offs[0] = 0; for( int i = 1; i < mask.size(); ++i ) offs[i] = offs[i-1] + basis[mask[i-1]].size(); - safe_cuda_copy( offs.size(), offs_device, offs.data() ); + util::cuda_copy( offs.size(), offs_device, offs.data() ); std::vector> shells; for( auto idx : mask ) shells.emplace_back(basis[idx]); - safe_cuda_copy( shells.size(), shells_device, shells.data() ); + util::cuda_copy( shells.size(), shells_device, shells.data() ); } @@ -419,20 +377,20 @@ void test_cuda_collocation_petite_combined( const BasisSet& basis, std:: return a.npts < b.npts; })->npts; - auto* tasks_device = safe_cuda_malloc>( tasks.size() ); - safe_cuda_copy( tasks.size(), tasks_device, tasks.data() ); + auto* tasks_device = util::cuda_malloc>( tasks.size() ); + util::cuda_copy( tasks.size(), tasks_device, tasks.data() ); integrator::cuda::eval_collocation_petite_combined( tasks.size(), npts_max, nshells_max, tasks_device, stream ); - safe_cuda_device_sync(); + util::cuda_device_sync(); for( int i = 0; i < tasks.size(); i++ ) { auto* ref_eval = ref_data[i].eval.data(); std::vector eval (tasks[i].nbe * tasks[i].npts); - safe_cuda_copy( eval.size(), eval.data(), tasks[i].bf ); + util::cuda_copy( eval.size(), eval.data(), tasks[i].bf ); for( auto i = 0; i < eval.size(); ++i ) CHECK( eval[i] == Approx( ref_eval[i] ) ); @@ -440,9 +398,9 @@ void test_cuda_collocation_petite_combined( const BasisSet& basis, std:: for( auto& t : tasks ) { - safe_cuda_free( t.points, t.shell_offs, t.shells, t.bf ); + util::cuda_free( t.points, t.shell_offs, t.shells, t.bf ); } - safe_cuda_free( tasks_device ); + util::cuda_free( tasks_device ); } @@ -460,9 +418,9 @@ void test_cuda_collocation_masked_combined( const BasisSet& basis, std:: std::vector< cuda::XCTaskDevice > tasks; - auto shells_device = safe_cuda_malloc>( basis.size() ); + auto shells_device = util::cuda_malloc>( basis.size() ); std::vector> shells( basis ); - safe_cuda_copy( basis.size(), shells_device, shells.data() ); + util::cuda_copy( basis.size(), shells_device, shells.data() ); cudaStream_t stream = 0; for( auto& d : ref_data ) { @@ -479,27 +437,27 @@ void test_cuda_collocation_masked_combined( const BasisSet& basis, std:: task.npts = npts; task.nshells = mask.size(); - task.points = safe_cuda_malloc( 3 * npts ); - task.shell_offs = safe_cuda_malloc( mask.size() ); - task.shell_list = safe_cuda_malloc( mask.size() ); - task.bf = safe_cuda_malloc( nbf * npts ); + task.points = util::cuda_malloc( 3 * npts ); + task.shell_offs = util::cuda_malloc( mask.size() ); + task.shell_list = util::cuda_malloc( mask.size() ); + task.bf = util::cuda_malloc( nbf * npts ); auto* pts_device = task.points; auto* offs_device = task.shell_offs; auto* mask_device = task.shell_list; - safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + util::cuda_copy( 3*npts, pts_device, pts.data()->data() ); std::vector mask_ul( mask.size() ); std::copy( mask.begin(), mask.end(), mask_ul.begin() ); - safe_cuda_copy( mask.size(), mask_device, mask_ul.data() ); + util::cuda_copy( mask.size(), mask_device, mask_ul.data() ); std::vector offs( mask.size() ); offs[0] = 0; for( int i = 1; i < mask.size(); ++i ) offs[i] = offs[i-1] + basis[mask[i-1]].size(); - safe_cuda_copy( offs.size(), offs_device, offs.data() ); + util::cuda_copy( offs.size(), offs_device, offs.data() ); } @@ -515,20 +473,20 @@ void test_cuda_collocation_masked_combined( const BasisSet& basis, std:: return a.npts < b.npts; })->npts; - auto* tasks_device = safe_cuda_malloc>( tasks.size() ); - safe_cuda_copy( tasks.size(), tasks_device, tasks.data() ); + auto* tasks_device = util::cuda_malloc>( tasks.size() ); + util::cuda_copy( tasks.size(), tasks_device, tasks.data() ); integrator::cuda::eval_collocation_masked_combined( tasks.size(), npts_max, nshells_max, shells_device, tasks_device, stream ); - safe_cuda_device_sync(); + util::cuda_device_sync(); for( int i = 0; i < tasks.size(); i++ ) { auto* ref_eval = ref_data[i].eval.data(); std::vector eval (tasks[i].nbe * tasks[i].npts); - safe_cuda_copy( eval.size(), eval.data(), tasks[i].bf ); + util::cuda_copy( eval.size(), eval.data(), tasks[i].bf ); for( auto i = 0; i < eval.size(); ++i ) CHECK( eval[i] == Approx( ref_eval[i] ) ); @@ -536,9 +494,9 @@ void test_cuda_collocation_masked_combined( const BasisSet& basis, std:: for( auto& t : tasks ) { - safe_cuda_free( t.points, t.shell_offs, t.shell_list, t.bf ); + util::cuda_free( t.points, t.shell_offs, t.shell_list, t.bf ); } - safe_cuda_free( tasks_device, shells_device ); + util::cuda_free( tasks_device, shells_device ); } @@ -570,13 +528,13 @@ void test_cuda_collocation_deriv1_petite( const BasisSet& basis, std::if ar( ref_data ); } - auto shells_device = safe_cuda_malloc>( basis.size() ); - auto offs_device = safe_cuda_malloc( basis.size() ); - auto pts_device = safe_cuda_malloc( 3 * MAX_NPTS_CHECK ); - auto eval_device = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); - auto deval_device_x = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); - auto deval_device_y = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); - auto deval_device_z = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto shells_device = util::cuda_malloc>( basis.size() ); + auto offs_device = util::cuda_malloc( basis.size() ); + auto pts_device = util::cuda_malloc( 3 * MAX_NPTS_CHECK ); + auto eval_device = util::cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto deval_device_x = util::cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto deval_device_y = util::cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto deval_device_z = util::cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); @@ -588,17 +546,17 @@ void test_cuda_collocation_deriv1_petite( const BasisSet& basis, std::if const auto& mask = d.mask; const auto& pts = d.pts; - safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + util::cuda_copy( 3*npts, pts_device, pts.data()->data() ); std::vector offs( mask.size() ); offs[0] = 0; for( int i = 1; i < mask.size(); ++i ) offs[i] = offs[i-1] + basis[mask[i-1]].size(); - safe_cuda_copy( offs.size(), offs_device, offs.data() ); + util::cuda_copy( offs.size(), offs_device, offs.data() ); std::vector> shells; for( auto idx : mask ) shells.emplace_back(basis[idx]); - safe_cuda_copy( shells.size(), shells_device, shells.data() ); + util::cuda_copy( shells.size(), shells_device, shells.data() ); integrator::cuda::eval_collocation_petite_deriv1( shells.size(), nbf, npts, shells_device, offs_device, @@ -612,10 +570,10 @@ void test_cuda_collocation_deriv1_petite( const BasisSet& basis, std::if deval_y( nbf * npts ), deval_z( nbf * npts ); - safe_cuda_copy( nbf * npts, eval.data(), eval_device ); - safe_cuda_copy( nbf * npts, deval_x.data(), deval_device_x ); - safe_cuda_copy( nbf * npts, deval_y.data(), deval_device_y ); - safe_cuda_copy( nbf * npts, deval_z.data(), deval_device_z ); + util::cuda_copy( nbf * npts, eval.data(), eval_device ); + util::cuda_copy( nbf * npts, deval_x.data(), deval_device_x ); + util::cuda_copy( nbf * npts, deval_y.data(), deval_device_y ); + util::cuda_copy( nbf * npts, deval_z.data(), deval_device_z ); for( auto i = 0; i < npts * nbf; ++i ) CHECK( eval[i] == Approx( d.eval[i] ) ); @@ -627,8 +585,8 @@ void test_cuda_collocation_deriv1_petite( const BasisSet& basis, std::if CHECK( deval_z[i] == Approx( d.deval_z[i] ) ); } - safe_cuda_device_sync(); - safe_cuda_free(shells_device, offs_device, pts_device, eval_device, + util::cuda_device_sync(); + util::cuda_free(shells_device, offs_device, pts_device, eval_device, deval_device_x, deval_device_y, deval_device_z); } @@ -646,18 +604,18 @@ void test_cuda_collocation_deriv1_masked( const BasisSet& basis, std::if ar( ref_data ); } - auto shells_device = safe_cuda_malloc>( basis.size() ); - auto offs_device = safe_cuda_malloc( basis.size() ); - auto mask_device = safe_cuda_malloc( basis.size() ); - auto pts_device = safe_cuda_malloc( 3 * MAX_NPTS_CHECK ); - auto eval_device = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); - auto deval_device_x = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); - auto deval_device_y = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); - auto deval_device_z = safe_cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto shells_device = util::cuda_malloc>( basis.size() ); + auto offs_device = util::cuda_malloc( basis.size() ); + auto mask_device = util::cuda_malloc( basis.size() ); + auto pts_device = util::cuda_malloc( 3 * MAX_NPTS_CHECK ); + auto eval_device = util::cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto deval_device_x = util::cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto deval_device_y = util::cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); + auto deval_device_z = util::cuda_malloc( basis.nbf() * MAX_NPTS_CHECK ); std::vector> shells( basis ); - safe_cuda_copy( basis.size(), shells_device, shells.data() ); + util::cuda_copy( basis.size(), shells_device, shells.data() ); cudaStream_t stream = 0; for( auto& d : ref_data ) { @@ -667,17 +625,17 @@ void test_cuda_collocation_deriv1_masked( const BasisSet& basis, std::if const auto& mask = d.mask; const auto& pts = d.pts; - safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + util::cuda_copy( 3*npts, pts_device, pts.data()->data() ); std::vector mask_ul( mask.size() ); std::copy( mask.begin(), mask.end(), mask_ul.begin() ); - safe_cuda_copy( mask.size(), mask_device, mask_ul.data() ); + util::cuda_copy( mask.size(), mask_device, mask_ul.data() ); std::vector offs( mask.size() ); offs[0] = 0; for( int i = 1; i < mask.size(); ++i ) offs[i] = offs[i-1] + basis[mask[i-1]].size(); - safe_cuda_copy( offs.size(), offs_device, offs.data() ); + util::cuda_copy( offs.size(), offs_device, offs.data() ); integrator::cuda::eval_collocation_masked_deriv1( mask.size(), nbf, npts, @@ -692,10 +650,10 @@ void test_cuda_collocation_deriv1_masked( const BasisSet& basis, std::if deval_y( nbf * npts ), deval_z( nbf * npts ); - safe_cuda_copy( nbf * npts, eval.data(), eval_device ); - safe_cuda_copy( nbf * npts, deval_x.data(), deval_device_x ); - safe_cuda_copy( nbf * npts, deval_y.data(), deval_device_y ); - safe_cuda_copy( nbf * npts, deval_z.data(), deval_device_z ); + util::cuda_copy( nbf * npts, eval.data(), eval_device ); + util::cuda_copy( nbf * npts, deval_x.data(), deval_device_x ); + util::cuda_copy( nbf * npts, deval_y.data(), deval_device_y ); + util::cuda_copy( nbf * npts, deval_z.data(), deval_device_z ); for( auto i = 0; i < npts * nbf; ++i ) CHECK( eval[i] == Approx( d.eval[i] ) ); @@ -707,8 +665,8 @@ void test_cuda_collocation_deriv1_masked( const BasisSet& basis, std::if CHECK( deval_z[i] == Approx( d.deval_z[i] ) ); } - safe_cuda_device_sync(); - safe_cuda_free(shells_device, offs_device, pts_device, eval_device, + util::cuda_device_sync(); + util::cuda_free(shells_device, offs_device, pts_device, eval_device, deval_device_x, deval_device_y, deval_device_z); } @@ -748,30 +706,30 @@ void test_cuda_collocation_petite_combined_deriv1( const BasisSet& basis task.npts = npts; task.nshells = mask.size(); - task.points = safe_cuda_malloc( 3 * npts ); - task.shell_offs = safe_cuda_malloc( mask.size() ); - task.shells = safe_cuda_malloc>(mask.size()); - task.bf = safe_cuda_malloc( nbf * npts ); - task.dbfx = safe_cuda_malloc( nbf * npts ); - task.dbfy = safe_cuda_malloc( nbf * npts ); - task.dbfz = safe_cuda_malloc( nbf * npts ); + task.points = util::cuda_malloc( 3 * npts ); + task.shell_offs = util::cuda_malloc( mask.size() ); + task.shells = util::cuda_malloc>(mask.size()); + task.bf = util::cuda_malloc( nbf * npts ); + task.dbfx = util::cuda_malloc( nbf * npts ); + task.dbfy = util::cuda_malloc( nbf * npts ); + task.dbfz = util::cuda_malloc( nbf * npts ); auto* pts_device = task.points; auto* offs_device = task.shell_offs; auto* shells_device = task.shells; - safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + util::cuda_copy( 3*npts, pts_device, pts.data()->data() ); std::vector offs( mask.size() ); offs[0] = 0; for( int i = 1; i < mask.size(); ++i ) offs[i] = offs[i-1] + basis[mask[i-1]].size(); - safe_cuda_copy( offs.size(), offs_device, offs.data() ); + util::cuda_copy( offs.size(), offs_device, offs.data() ); std::vector> shells; for( auto idx : mask ) shells.emplace_back(basis[idx]); - safe_cuda_copy( shells.size(), shells_device, shells.data() ); + util::cuda_copy( shells.size(), shells_device, shells.data() ); } @@ -787,13 +745,13 @@ void test_cuda_collocation_petite_combined_deriv1( const BasisSet& basis return a.npts < b.npts; })->npts; - auto* tasks_device = safe_cuda_malloc>( tasks.size() ); - safe_cuda_copy( tasks.size(), tasks_device, tasks.data() ); + auto* tasks_device = util::cuda_malloc>( tasks.size() ); + util::cuda_copy( tasks.size(), tasks_device, tasks.data() ); integrator::cuda::eval_collocation_petite_combined_deriv1( tasks.size(), npts_max, nshells_max, tasks_device, stream ); - safe_cuda_device_sync(); + util::cuda_device_sync(); for( int i = 0; i < tasks.size(); i++ ) { @@ -808,10 +766,10 @@ void test_cuda_collocation_petite_combined_deriv1( const BasisSet& basis std::vector deval_y (tasks[i].nbe * tasks[i].npts); std::vector deval_z (tasks[i].nbe * tasks[i].npts); - safe_cuda_copy( eval.size(), eval.data(), tasks[i].bf ); - safe_cuda_copy( eval.size(), deval_x.data(), tasks[i].dbfx ); - safe_cuda_copy( eval.size(), deval_y.data(), tasks[i].dbfy ); - safe_cuda_copy( eval.size(), deval_z.data(), tasks[i].dbfz ); + util::cuda_copy( eval.size(), eval.data(), tasks[i].bf ); + util::cuda_copy( eval.size(), deval_x.data(), tasks[i].dbfx ); + util::cuda_copy( eval.size(), deval_y.data(), tasks[i].dbfy ); + util::cuda_copy( eval.size(), deval_z.data(), tasks[i].dbfz ); for( auto i = 0; i < eval.size(); ++i ) CHECK( eval[i] == Approx( ref_eval[i] ) ); @@ -825,10 +783,10 @@ void test_cuda_collocation_petite_combined_deriv1( const BasisSet& basis for( auto& t : tasks ) { - safe_cuda_free( t.points, t.shell_offs, t.shells, t.bf, t.dbfx, t.dbfy, + util::cuda_free( t.points, t.shell_offs, t.shells, t.bf, t.dbfx, t.dbfy, t.dbfz ); } - safe_cuda_free( tasks_device ); + util::cuda_free( tasks_device ); } @@ -846,9 +804,9 @@ void test_cuda_collocation_masked_combined_deriv1( const BasisSet& basis std::vector< cuda::XCTaskDevice > tasks; - auto shells_device = safe_cuda_malloc>( basis.size() ); + auto shells_device = util::cuda_malloc>( basis.size() ); std::vector> shells( basis ); - safe_cuda_copy( basis.size(), shells_device, shells.data() ); + util::cuda_copy( basis.size(), shells_device, shells.data() ); cudaStream_t stream = 0; for( auto& d : ref_data ) { @@ -865,13 +823,13 @@ void test_cuda_collocation_masked_combined_deriv1( const BasisSet& basis task.npts = npts; task.nshells = mask.size(); - task.points = safe_cuda_malloc( 3 * npts ); - task.shell_offs = safe_cuda_malloc( mask.size() ); - task.shell_list = safe_cuda_malloc( mask.size() ); - task.bf = safe_cuda_malloc( nbf * npts ); - task.dbfx = safe_cuda_malloc( nbf * npts ); - task.dbfy = safe_cuda_malloc( nbf * npts ); - task.dbfz = safe_cuda_malloc( nbf * npts ); + task.points = util::cuda_malloc( 3 * npts ); + task.shell_offs = util::cuda_malloc( mask.size() ); + task.shell_list = util::cuda_malloc( mask.size() ); + task.bf = util::cuda_malloc( nbf * npts ); + task.dbfx = util::cuda_malloc( nbf * npts ); + task.dbfy = util::cuda_malloc( nbf * npts ); + task.dbfz = util::cuda_malloc( nbf * npts ); auto* pts_device = task.points; @@ -879,17 +837,17 @@ void test_cuda_collocation_masked_combined_deriv1( const BasisSet& basis auto* mask_device = task.shell_list; - safe_cuda_copy( 3*npts, pts_device, pts.data()->data() ); + util::cuda_copy( 3*npts, pts_device, pts.data()->data() ); std::vector mask_ul( mask.size() ); std::copy( mask.begin(), mask.end(), mask_ul.begin() ); - safe_cuda_copy( mask.size(), mask_device, mask_ul.data() ); + util::cuda_copy( mask.size(), mask_device, mask_ul.data() ); std::vector offs( mask.size() ); offs[0] = 0; for( int i = 1; i < mask.size(); ++i ) offs[i] = offs[i-1] + basis[mask[i-1]].size(); - safe_cuda_copy( offs.size(), offs_device, offs.data() ); + util::cuda_copy( offs.size(), offs_device, offs.data() ); } @@ -905,13 +863,13 @@ void test_cuda_collocation_masked_combined_deriv1( const BasisSet& basis return a.npts < b.npts; })->npts; - auto* tasks_device = safe_cuda_malloc>( tasks.size() ); - safe_cuda_copy( tasks.size(), tasks_device, tasks.data() ); + auto* tasks_device = util::cuda_malloc>( tasks.size() ); + util::cuda_copy( tasks.size(), tasks_device, tasks.data() ); integrator::cuda::eval_collocation_masked_combined_deriv1( tasks.size(), npts_max, nshells_max, shells_device, tasks_device, stream ); - safe_cuda_device_sync(); + util::cuda_device_sync(); for( int i = 0; i < tasks.size(); i++ ) { @@ -926,10 +884,10 @@ void test_cuda_collocation_masked_combined_deriv1( const BasisSet& basis std::vector deval_y (tasks[i].nbe * tasks[i].npts); std::vector deval_z (tasks[i].nbe * tasks[i].npts); - safe_cuda_copy( eval.size(), eval.data(), tasks[i].bf ); - safe_cuda_copy( eval.size(), deval_x.data(), tasks[i].dbfx ); - safe_cuda_copy( eval.size(), deval_y.data(), tasks[i].dbfy ); - safe_cuda_copy( eval.size(), deval_z.data(), tasks[i].dbfz ); + util::cuda_copy( eval.size(), eval.data(), tasks[i].bf ); + util::cuda_copy( eval.size(), deval_x.data(), tasks[i].dbfx ); + util::cuda_copy( eval.size(), deval_y.data(), tasks[i].dbfy ); + util::cuda_copy( eval.size(), deval_z.data(), tasks[i].dbfz ); for( auto i = 0; i < eval.size(); ++i ) CHECK( eval[i] == Approx( ref_eval[i] ) ); @@ -943,10 +901,10 @@ void test_cuda_collocation_masked_combined_deriv1( const BasisSet& basis for( auto& t : tasks ) { - safe_cuda_free( t.points, t.shell_offs, t.shell_list, t.bf, t.dbfx, t.dbfy, + util::cuda_free( t.points, t.shell_offs, t.shell_list, t.bf, t.dbfx, t.dbfy, t.dbfz ); } - safe_cuda_free( tasks_device, shells_device ); + util::cuda_free( tasks_device, shells_device ); } #endif @@ -966,9 +924,12 @@ TEST_CASE( "Water / cc-pVDZ", "[collocation]" ) { for( auto& sh : basis ) sh.set_shell_tolerance( 1e-6 ); #ifdef GENERATE_TESTS + std::ofstream ref_data( "water_cc-pVDZ_collocation.bin", std::ios::binary ); generate_collocation_data( mol, basis, ref_data ); + #else + std::ifstream ref_data( GAUXC_REF_DATA_PATH "/water_cc-pVDZ_collocation.bin", std::ios::binary ); From cfcea771721456164bbe4900a0e97ef92f2b75b4 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Thu, 20 Aug 2020 14:06:05 -0400 Subject: [PATCH 19/27] Added device task setup --- include/gauxc/util/cuda_util.hpp | 9 +- include/gauxc/xc_integrator/xc_cuda_data.hpp | 13 +- src/integrator/cuda/xc_cuda_data.cxx | 398 +++++++++++++++++++ 3 files changed, 415 insertions(+), 5 deletions(-) diff --git a/include/gauxc/util/cuda_util.hpp b/include/gauxc/util/cuda_util.hpp index af0beb006..5ff7f9d6a 100644 --- a/include/gauxc/util/cuda_util.hpp +++ b/include/gauxc/util/cuda_util.hpp @@ -69,15 +69,16 @@ inline void cuda_free( T*& ptr, Args&&... args ) { template -inline void cuda_copy( size_t len, T* dest, const T* src ) { +inline void cuda_copy( size_t len, T* dest, const T* src, std::string m = "") { auto stat = cudaMemcpy( dest, src, len * sizeof(T), cudaMemcpyDefault ); - GAUXC_CUDA_ERROR( "CUDA Memcpy Failed", stat ); + GAUXC_CUDA_ERROR( "CUDA Memcpy Failed ["+m+"]", stat ); } template -inline void cuda_copy_async( size_t len, T* dest, const T* src, cudaStream_t s ) { +inline void cuda_copy_async( size_t len, T* dest, const T* src, cudaStream_t s, + std::string m = "" ) { auto stat = cudaMemcpyAsync( dest, src, len * sizeof(T), cudaMemcpyDefault, s ); - GAUXC_CUDA_ERROR( "CUDA Memcpy Async Failed", stat ); + GAUXC_CUDA_ERROR( "CUDA Memcpy Async Failed ["+m+"]", stat ); } diff --git a/include/gauxc/xc_integrator/xc_cuda_data.hpp b/include/gauxc/xc_integrator/xc_cuda_data.hpp index a8f1afe3a..3d784bada 100644 --- a/include/gauxc/xc_integrator/xc_cuda_data.hpp +++ b/include/gauxc/xc_integrator/xc_cuda_data.hpp @@ -2,7 +2,8 @@ #include #include #include -#include +#include +#include #include #include @@ -80,6 +81,7 @@ struct XCCudaData { int32_t* iparent_device_buffer = nullptr; F* dist_nearest_buffer = nullptr; + cuda::XCTaskDevice* device_tasks = nullptr; // Execution management std::unique_ptr master_stream = nullptr; @@ -97,6 +99,15 @@ struct XCCudaData { XCCudaData( const XCCudaData& ) = delete; XCCudaData( XCCudaData&& ) noexcept = delete; + + using task_iterator = std::vector< XCTask >::iterator; + using device_task_container = std::vector< cuda::XCTaskDevice >; + + std::tuple< task_iterator, device_task_container > + generate_buffers( const BasisSet& basis, + task_iterator task_begin, + task_iterator task_end ); + }; } diff --git a/src/integrator/cuda/xc_cuda_data.cxx b/src/integrator/cuda/xc_cuda_data.cxx index 9253b71e9..d8d731a7a 100644 --- a/src/integrator/cuda/xc_cuda_data.cxx +++ b/src/integrator/cuda/xc_cuda_data.cxx @@ -1,5 +1,6 @@ #include #include "buffer_adaptor.hpp" +#include "integrator_common.hpp" namespace GauXC { @@ -72,6 +73,403 @@ XCCudaData::~XCCudaData() noexcept { } +using task_iterator = std::vector< XCTask >::iterator; +template +using device_task_container = std::vector< cuda::XCTaskDevice >; + +template +std::tuple< task_iterator, device_task_container > + XCCudaData::generate_buffers( const BasisSet& basis, + task_iterator task_begin, + task_iterator task_end ) { + + // Host data packing arrays + std::vector< std::array > points_pack; + std::vector< double > weights_pack; + std::vector< size_t > shell_list_pack; + std::vector< size_t > shell_offs_pack; + std::vector< std::pair > submat_cut_pack; + std::vector< int32_t > iparent_pack; + std::vector< double > dist_nearest_pack; + + // Host copies for batched GEMM/SYRK arrays + std::vector< double* > dmat_array, bf_array, zmat_array; + std::vector< int > m_array, n_array, k_array, lda_array, ldb_array, ldc_array; + + + device_task_container tasks_device; + + + auto concat_iterable = []( auto& a, const auto& b ) { + a.insert( a.end(), b.begin(), b.end() ); + }; + + + size_t ntask = 0; + size_t total_npts = 0; + size_t total_nbe_nbe = 0; + size_t total_nbe_npts = 0; + size_t total_nshells = 0; + size_t total_ncut = 0; + size_t memleft = dynmem_sz; + + // Offset memory by the static requirement of an extra pointer element + // for each of the size batch arrays in MAGMA + memleft -= 6 * sizeof(int); //M,N,K,LDA,LDB,LDC + + auto task_it = task_begin; + while( task_it != task_end ) { + + auto iAtom = task_it->iParent; + auto points = task_it->points ; + auto weights = task_it->weights ; + auto shell_list = task_it->shell_list; + auto nbe = task_it->nbe; + auto dist_nearest = task_it->dist_nearest; + + // Generate map from compressed to non-compressed matrices + auto submat_cut = integrator::gen_compressed_submat_map( basis, shell_list ); + size_t ncut = submat_cut.size(); + size_t nshells = shell_list.size(); + size_t npts = points.size(); + + + size_t mem_points = 3 * npts; + size_t mem_weights = npts; + + size_t mem_shells = nshells; + size_t mem_shell_list = nshells; + size_t mem_shell_offs = nshells; + size_t mem_submat_cut = 2 * ncut; + + size_t mem_nbe_scr = nbe * nbe; + size_t mem_zmat = nbe * npts; + + size_t mem_bf = nbe * npts; + size_t mem_dbfx = mem_bf; + size_t mem_dbfy = mem_bf; + size_t mem_dbfz = mem_bf; + + size_t mem_den = npts; + size_t mem_denx = npts; + size_t mem_deny = npts; + size_t mem_denz = npts; + + size_t mem_eps = npts; + size_t mem_gamma = npts; + size_t mem_vrho = npts; + size_t mem_vgamma = npts; + + //size_t mem_partition_scr = natoms * npts; + size_t mem_dist_scr = natoms * npts; + size_t mem_iparent = npts; + size_t mem_dist_nearest = npts; + + size_t mem_batch_mat_arr = 3; // dmat/zmat/bf + size_t mem_batch_sz_arr = 6; // M/N/K/LDA/LDB/LDC + size_t mem_task = 1; + + + size_t mem_req_batch = + mem_points * sizeof(double) + + mem_weights * sizeof(double) + + mem_shells * sizeof(Shell) + + mem_shell_list * sizeof(size_t) + + mem_shell_offs * sizeof(size_t) + + mem_submat_cut * sizeof(int64_t) + + mem_nbe_scr * sizeof(double) + + mem_zmat * sizeof(double) + + mem_bf * sizeof(double) + + mem_dbfx * sizeof(double) + + mem_dbfy * sizeof(double) + + mem_dbfz * sizeof(double) + + mem_den * sizeof(double) + + mem_denx * sizeof(double) + + mem_deny * sizeof(double) + + mem_denz * sizeof(double) + + mem_eps * sizeof(double) + + mem_gamma * sizeof(double) + + mem_vrho * sizeof(double) + + mem_vgamma * sizeof(double) + + //mem_partition_scr * sizeof(double) + + mem_dist_scr * sizeof(double) + + mem_iparent * sizeof(int32_t) + + mem_dist_nearest * sizeof(double) + + mem_batch_mat_arr * sizeof(double*) + + mem_batch_sz_arr * sizeof(int32_t) + + mem_task * sizeof(cuda::XCTaskDevice); + + //std::cout << "Memory requirement for task " << ntask+1 << " " << mem_req_batch << " memleft " << memleft << std::endl; + + if( mem_req_batch > memleft ) break; + + // Update memory and increment task iterator + memleft -= mem_req_batch; + ntask++; + task_it++; + + // Update counters + total_npts += npts; + total_nbe_nbe += nbe*nbe; + total_nbe_npts += nbe*npts; + total_nshells += nshells; + total_ncut += ncut; + + // Compute offsets + std::vector< size_t > shell_offs( nshells ); + shell_offs.at(0) = 0; + for( auto i = 1ul; i < nshells; ++i ) + shell_offs.at(i) = shell_offs.at(i-1) + + basis.at( shell_list.at(i-1) ).size(); + + + // Pack the data on host + concat_iterable( points_pack, points ); + concat_iterable( weights_pack, weights ); + concat_iterable( shell_list_pack, shell_list ); + concat_iterable( shell_offs_pack, shell_offs ); + concat_iterable( submat_cut_pack, submat_cut ); + + m_array.emplace_back( nbe ); + n_array.emplace_back( npts ); + k_array.emplace_back( nbe ); + + lda_array.emplace_back( nbe ); + ldb_array.emplace_back( nbe ); + ldc_array.emplace_back( nbe ); + + iparent_pack.insert( iparent_pack.end(), npts, iAtom ); + dist_nearest_pack.insert( dist_nearest_pack.end(), npts, dist_nearest ); + + // Add task + tasks_device.emplace_back(); + + tasks_device.back().nbe = nbe; + tasks_device.back().npts = npts; + tasks_device.back().ncut = ncut; + tasks_device.back().nshells = nshells; + tasks_device.back().iParent = iAtom; + tasks_device.back().dist_nearest = dist_nearest; + } + + + std::cout << "XCDeviceData will stack allocate for " << tasks_device.size() << " tasks" << std::endl; + + // Allocate out of dynamic memory + buffer_adaptor mem( dynmem_ptr, dynmem_sz ); + + // (possibly) Large types + important_shells_device = mem.aligned_alloc>( total_nshells ); + device_tasks = mem.aligned_alloc>( ntask ); + + // 64-bit types + nbe_scr_device = mem.aligned_alloc( total_nbe_nbe ); + zmat_device = mem.aligned_alloc( total_nbe_npts ); + bf_eval_device = mem.aligned_alloc( total_nbe_npts ); + dbf_x_eval_device = mem.aligned_alloc( total_nbe_npts ); + dbf_y_eval_device = mem.aligned_alloc( total_nbe_npts ); + dbf_z_eval_device = mem.aligned_alloc( total_nbe_npts ); + + den_eval_device = mem.aligned_alloc( total_npts ); + eps_eval_device = mem.aligned_alloc( total_npts ); + vrho_eval_device = mem.aligned_alloc( total_npts ); + + den_x_eval_device = mem.aligned_alloc( total_npts ); + den_y_eval_device = mem.aligned_alloc( total_npts ); + den_z_eval_device = mem.aligned_alloc( total_npts ); + gamma_eval_device = mem.aligned_alloc( total_npts ); + vgamma_eval_device = mem.aligned_alloc( total_npts ); + + points_device_buffer = mem.aligned_alloc( 3 * total_npts ); + weights_device_buffer = mem.aligned_alloc( total_npts ); + shell_list_device_buffer = mem.aligned_alloc( total_nshells ); + shell_offs_device_buffer = mem.aligned_alloc( total_nshells ); + submat_cut_device_buffer = mem.aligned_alloc( 2 * total_ncut ); + + dist_scratch_device = mem.aligned_alloc( natoms * total_npts ); + dist_nearest_buffer = mem.aligned_alloc( total_npts ); + + dmat_array_device = mem.aligned_alloc( ntask ); + zmat_array_device = mem.aligned_alloc( ntask ); + bf_array_device = mem.aligned_alloc( ntask ); + + // 32-bit types + m_array_device = mem.aligned_alloc( ntask + 1 ); + n_array_device = mem.aligned_alloc( ntask + 1 ); + k_array_device = mem.aligned_alloc( ntask + 1 ); + lda_array_device = mem.aligned_alloc( ntask + 1 ); + ldb_array_device = mem.aligned_alloc( ntask + 1 ); + ldc_array_device = mem.aligned_alloc( ntask + 1 ); + + iparent_device_buffer = mem.aligned_alloc( total_npts ); + + + // Update tasks with allocated pointers + { + double* points_ptr = points_device_buffer; + double* weights_ptr = weights_device_buffer; + + size_t* shell_list_ptr = shell_list_device_buffer; + size_t* shell_offs_ptr = shell_offs_device_buffer; + int64_t* submat_cut_ptr = submat_cut_device_buffer; + Shell * shells_ptr = important_shells_device; + double* nbe_ptr = nbe_scr_device; + double* zmat_ptr = zmat_device; + + double* bf_ptr = bf_eval_device; + double* dbfx_ptr = dbf_x_eval_device; + double* dbfy_ptr = dbf_y_eval_device; + double* dbfz_ptr = dbf_z_eval_device; + + double* den_ptr = den_eval_device; + double* ddenx_ptr = den_x_eval_device; + double* ddeny_ptr = den_y_eval_device; + double* ddenz_ptr = den_z_eval_device; + + double* eps_ptr = eps_eval_device; + double* gamma_ptr = gamma_eval_device; + double* vrho_ptr = vrho_eval_device; + double* vgamma_ptr = vgamma_eval_device; + + + double* dist_scratch_ptr = dist_scratch_device; + + for( auto& task : tasks_device ) { + + task.points = points_ptr; + task.weights = weights_ptr; + task.shell_list = shell_list_ptr; + task.shell_offs = shell_offs_ptr; + task.submat_cut = submat_cut_ptr; + + task.shells = shells_ptr; + task.nbe_scr = nbe_ptr; + task.zmat = zmat_ptr; + task.bf = bf_ptr; + task.dbfx = dbfx_ptr; + task.dbfy = dbfy_ptr; + task.dbfz = dbfz_ptr; + task.den = den_ptr; + task.ddenx = ddenx_ptr; + task.ddeny = ddeny_ptr; + task.ddenz = ddenz_ptr; + + task.eps = eps_ptr; + task.gamma = gamma_ptr; + task.vrho = vrho_ptr; + task.vgamma = vgamma_ptr; + + task.dist_scratch = dist_scratch_ptr; + + auto npts = task.npts; + auto nbe = task.nbe; + auto nshells = task.nshells; + auto ncut = task.ncut; + + points_ptr += 3 * npts; + weights_ptr += npts; + shell_list_ptr += nshells; + shell_offs_ptr += nshells; + submat_cut_ptr += 2 * ncut; + + shells_ptr += nshells; + nbe_ptr += nbe * nbe; + zmat_ptr += nbe * npts; + + bf_ptr += nbe * npts; + dbfx_ptr += nbe * npts; + dbfy_ptr += nbe * npts; + dbfz_ptr += nbe * npts; + + den_ptr += npts; + ddenx_ptr += npts; + ddeny_ptr += npts; + ddenz_ptr += npts; + + eps_ptr += npts; + gamma_ptr += npts; + vrho_ptr += npts; + vgamma_ptr += npts; + + dist_scratch_ptr += natoms * npts; + + + + // Batched LA + dmat_array.emplace_back( task.nbe_scr ); + bf_array.emplace_back( task.bf ); + zmat_array.emplace_back( task.zmat ); + } + + } // End task setup + + + + + + + + try { + + // Send the data to the device + util::cuda_copy_async( 3*points_pack.size(), points_pack.data()->data(), + points_device_buffer, *master_stream, + "send points buffer" ); + util::cuda_copy_async( weights_pack.size(), weights_pack.data(), + weights_device_buffer, *master_stream, + "send weights buffer" ); + + util::cuda_copy_async( shell_list_pack.size(), shell_list_pack.data(), + shell_list_device_buffer, *master_stream, + "send_shell_list_buffer" ); + util::cuda_copy_async( shell_offs_pack.size(), shell_offs_pack.data(), + shell_offs_device_buffer, *master_stream, + "send_shell_offs_buffer" ); + util::cuda_copy_async( 2*submat_cut_pack.size(), &submat_cut_pack.data()->first, + submat_cut_device_buffer, *master_stream, + "send_submat_cut_buffer" ); + + util::cuda_copy_async( tasks_device.size(), tasks_device.data(), device_tasks, + *master_stream, "send_tasks_device" ); + + + util::cuda_copy_async( dmat_array.size(), dmat_array.data(), dmat_array_device, + *master_stream, "send dmat_array" ); + util::cuda_copy_async( zmat_array.size(), zmat_array.data(), zmat_array_device, + *master_stream, "send zmat_array" ); + util::cuda_copy_async( bf_array.size(), bf_array.data(), bf_array_device, + *master_stream, "send bf_array" ); + + util::cuda_copy_async( m_array.size(), m_array.data(), m_array_device, + *master_stream, "send m_array" ); + util::cuda_copy_async( n_array.size(), n_array.data(), n_array_device, + *master_stream, "send n_array" ); + util::cuda_copy_async( k_array.size(), k_array.data(), k_array_device, + *master_stream, "send k_array" ); + + util::cuda_copy_async( lda_array.size(), lda_array.data(), lda_array_device, + *master_stream, "send lda_array" ); + util::cuda_copy_async( ldb_array.size(), ldb_array.data(), ldb_array_device, + *master_stream, "send ldb_array" ); + util::cuda_copy_async( ldc_array.size(), ldc_array.data(), ldc_array_device, + *master_stream, "send ldc_array" ); + + util::cuda_copy_async( iparent_pack.size(), iparent_pack.data(), + iparent_device_buffer, *master_stream, "send iparent" ); + util::cuda_copy_async( dist_nearest_pack.size(), dist_nearest_pack.data(), + dist_nearest_buffer, *master_stream, "send dist_nearest" ); + + } catch(...) { + //teardown_(); throw; + throw; + } + + + // To avoid packed vectors going out of scope + cudaStreamSynchronize( *master_stream ); + + return std::make_tuple(task_it, tasks_device); +} // Explicit Instantiations From 533887502d4055f1ce3813e7ef9d59a206121752 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Tue, 25 Aug 2020 17:50:46 -0400 Subject: [PATCH 20/27] [WIP, breaks tests] overhaul of CMake build system for integrator related utilities --- include/gauxc/exceptions/magma_exception.hpp | 2 +- .../default_xc_cuda_integrator.hpp | 1 + include/gauxc/xc_integrator/impl.hpp | 2 +- .../xc_integrator/integrator_defaults.hpp | 6 + include/gauxc/xc_integrator/xc_cuda_util.hpp | 39 ++++ src/CMakeLists.txt | 8 +- src/integrator/CMakeLists.txt | 123 ++++++++--- .../cuda/gauxc-cuda_integrator.cmake | 53 +++-- src/integrator/cuda/xc_cuda_util.cxx | 191 ++++++++++++++++++ .../host/gauxc-host_integrator.cmake | 7 + 10 files changed, 390 insertions(+), 42 deletions(-) create mode 100644 include/gauxc/xc_integrator/xc_cuda_util.hpp create mode 100644 src/integrator/cuda/xc_cuda_util.cxx create mode 100644 src/integrator/host/gauxc-host_integrator.cmake diff --git a/include/gauxc/exceptions/magma_exception.hpp b/include/gauxc/exceptions/magma_exception.hpp index ab32bac67..3609a941d 100644 --- a/include/gauxc/exceptions/magma_exception.hpp +++ b/include/gauxc/exceptions/magma_exception.hpp @@ -5,7 +5,7 @@ #include #include -#ifdef GAUXC_ENABLE_MAGMA +#ifdef GAUXC_ENABLE_CUDA namespace GauXC { diff --git a/include/gauxc/xc_integrator/default_xc_cuda_integrator.hpp b/include/gauxc/xc_integrator/default_xc_cuda_integrator.hpp index 14799c7db..3c97fa81c 100644 --- a/include/gauxc/xc_integrator/default_xc_cuda_integrator.hpp +++ b/include/gauxc/xc_integrator/default_xc_cuda_integrator.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include diff --git a/include/gauxc/xc_integrator/impl.hpp b/include/gauxc/xc_integrator/impl.hpp index f9f626fcf..94ecc4f55 100644 --- a/include/gauxc/xc_integrator/impl.hpp +++ b/include/gauxc/xc_integrator/impl.hpp @@ -12,7 +12,7 @@ XCIntegrator::XCIntegrator( std::unique_ptr&& pimpl ) : template XCIntegrator::XCIntegrator( MPI_Comm comm, const functional_type& func, const basisset_type& basis, std::shared_ptr lb) : - XCIntegrator( detail::make_default_host_integrator( + XCIntegrator( detail::make_default_cuda_integrator( comm, std::make_shared(func), std::make_shared(basis), diff --git a/include/gauxc/xc_integrator/integrator_defaults.hpp b/include/gauxc/xc_integrator/integrator_defaults.hpp index ed005683e..0bab71135 100644 --- a/include/gauxc/xc_integrator/integrator_defaults.hpp +++ b/include/gauxc/xc_integrator/integrator_defaults.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace GauXC { namespace detail { @@ -10,5 +11,10 @@ std::unique_ptr> make_default_host_integrator( Args return std::make_unique>( std::forward(args)... ); } +template +std::unique_ptr> make_default_cuda_integrator( Args&&... args ) { + return std::make_unique>( std::forward(args)... ); +} + } } diff --git a/include/gauxc/xc_integrator/xc_cuda_util.hpp b/include/gauxc/xc_integrator/xc_cuda_util.hpp new file mode 100644 index 000000000..9be37757b --- /dev/null +++ b/include/gauxc/xc_integrator/xc_cuda_util.hpp @@ -0,0 +1,39 @@ +#pragma once +#include + +#include + +namespace GauXC { +namespace integrator { +namespace cuda { + + +template +void process_batches_cuda_replicated_p( + XCWeightAlg weight_alg, + const functional_type& func, + const BasisSet& basis, + const Molecule & mol, + const MolMeta & meta, + XCCudaData & cuda_data, + std::vector< XCTask >& local_work, + const F* P, + F* VXC, + F* exc, + F* n_el +); + + +template +inline void process_batches_cuda_replicated_p( size_t n_deriv, Args&&... args ) { + if( n_deriv == 0 ) + process_batches_cuda_replicated_p( std::forward(args)... ); + else if( n_deriv == 1 ) + process_batches_cuda_replicated_p( std::forward(args)... ); + else + throw std::runtime_error("MGGA NYI"); +} + +} +} +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f2d0bcccf..f6d4f8f86 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,7 +31,12 @@ target_include_directories( gauxc $ ) -add_subdirectory( integrator ) +target_compile_options( gauxc + PRIVATE + $<$: -Wall -Wextra -Wpedantic -Wnon-virtual-dtor> +) + + configure_file( ${PROJECT_SOURCE_DIR}/include/gauxc/gauxc_config.hpp.in @@ -50,3 +55,4 @@ if( GAUXC_ENABLE_CEREAL ) target_link_libraries( gauxc PUBLIC cereal ) endif() +add_subdirectory( integrator ) diff --git a/src/integrator/CMakeLists.txt b/src/integrator/CMakeLists.txt index 8f9ec361f..cd6702553 100644 --- a/src/integrator/CMakeLists.txt +++ b/src/integrator/CMakeLists.txt @@ -1,32 +1,107 @@ -target_sources( gauxc - PRIVATE - integrator_common.cxx - host/xc_host_util.cxx - host/host_weights.cxx - host/host_collocation.cxx - host/host_zmat.cxx - host/blas.cxx -) +##target_sources( gauxc +## PRIVATE +## integrator_common.cxx +## host/xc_host_util.cxx +## host/host_weights.cxx +## host/host_collocation.cxx +## host/host_zmat.cxx +## host/blas.cxx +##) +## +##target_include_directories( gauxc +## PUBLIC +## $ +##) +## +##if( GAUXC_ENABLE_DEVICE ) +## +## if( GAUXC_ENABLE_CUDA ) +## include( cuda/gauxc-cuda_integrator.cmake ) +## endif() +## +## get_target_property( DEVICE_TARGET_DEPENDS gauxc_device_integrator +## INTERFACE_LINK_LIBRARIES ) +## +## target_link_libraries( gauxc PUBLIC +## $ +## ${DEVICE_TARGET_DEPENDS} +## ) +## +## add_dependencies( gauxc gauxc_device_integrator ) +## +##endif() +# +# +# +#set( GAUXC_COMMON_INTEGRATOR_INCLUDES +# $ +# $ +# $ +# $ ) +# +#get_target_property( GAUXC_COMMON_INTEGRATOR_LIBRARIES +# gauxc INTERFACE_LINK_LIBRARIES ) +# +# +# +#add_library( gauxc_integrator_common STATIC integrator_common.cxx ) +# +#target_link_libraries( gauxc_integrator_common PUBLIC +# "${GAUXC_COMMON_INTEGRATOR_LIBRARIES}" ) +#target_include_directories( gauxc_integrator_common PUBLIC +# "${GAUXC_COMMON_INTEGRATOR_INCLUDES}" ) +# +#target_compile_features( gauxc_integrator_common PUBLIC cxx_std_17 ) +# +#add_library( gauxc_integrator_host STATIC host/xc_host_util.cxx +# host/host_weights.cxx +# host/host_collocation.cxx +# host/host_zmat.cxx +# host/blas.cxx ) +# +#target_link_libraries( gauxc_integrator_host PUBLIC gauxc_integrator_common ) +# +#set( GAUXC_INTEGRATOR_LIBRARIES gauxc_integrator_host ) +# +# +#target_link_libraries( gauxc PUBLIC gauxc_host_integrator ) +# +# +# +#if( GAUXC_ENABLE_DEVICE ) +# +# if( GAUXC_ENABLE_CUDA ) +# include( cuda/gauxc-cuda_integrator.cmake ) +# endif() +# +# #get_target_property( DEVICE_TARGET_DEPENDS gauxc_device_integrator +# # INTERFACE_LINK_LIBRARIES ) +# +# ##target_link_libraries( gauxc PUBLIC +# ## $ +# ## ${DEVICE_TARGET_DEPENDS} +# ##) +# +# #list( PREPEND GAUXC_INTEGRATOR_LIBRARIES +# # $ ) +# +# #list( APPEND GAUXC_INTEGRATOR_LIBRARIES ${DEVICE_TARGET_DEPENDS} ) +# target_link_libraries( gauxc PUBLIC gauxc_device_integrator ) +# +# +#endif() + +# Common Integrator Utilities +target_sources( gauxc PRIVATE integrator_common.cxx ) target_include_directories( gauxc PUBLIC $ ) -if( GAUXC_ENABLE_DEVICE ) - - if( GAUXC_ENABLE_CUDA ) - include( cuda/gauxc-cuda_integrator.cmake ) - endif() - - get_target_property( DEVICE_TARGET_DEPENDS gauxc_device_integrator - INTERFACE_LINK_LIBRARIES ) - - target_link_libraries( gauxc PUBLIC - $ - ${DEVICE_TARGET_DEPENDS} - ) - - add_dependencies( gauxc gauxc_device_integrator ) +# Host Integrator Utilities +include( host/gauxc-host_integrator.cmake ) +if( GAUXC_ENABLE_CUDA ) + include( cuda/gauxc-cuda_integrator.cmake ) endif() diff --git a/src/integrator/cuda/gauxc-cuda_integrator.cmake b/src/integrator/cuda/gauxc-cuda_integrator.cmake index bb1e1e2b3..76dc105cf 100644 --- a/src/integrator/cuda/gauxc-cuda_integrator.cmake +++ b/src/integrator/cuda/gauxc-cuda_integrator.cmake @@ -1,29 +1,52 @@ find_package( CUDAToolkit REQUIRED ) -find_package( MAGMA REQUIRED ) +find_package( MAGMA REQUIRED ) -add_library( gauxc_device_integrator OBJECT - #cuda/collocation_kernels.cu - cuda/collocation_device.cu - cuda/xc_cuda_data.cxx +#add_library( gauxc_device_integrator STATIC +# #cuda/collocation_kernels.cu +# cuda/collocation_device.cu +# cuda/xc_cuda_data.cxx +# cuda/xc_cuda_util.cxx +#) +#target_compile_features( gauxc_device_integrator PUBLIC cuda_std_14 cxx_std_14 ) +#target_link_libraries( gauxc_device_integrator PUBLIC MAGMA::magma CUDA::cudart +# CUDA::cublas gauxc_integrator_common ) +# +#target_include_directories( gauxc_device_integrator +# PRIVATE +# $ +# $ +# $ +# $ +# $ +# $ +#) +# +#target_compile_options( gauxc_device_integrator +# PRIVATE +# $<$: -Wall -Wextra -Wpedantic -Wnon-virtual-dtor> +# $<$: -Xcudafe --diag_suppress=partial_override -Xptxas -v > +#) +# + + +target_sources( gauxc PRIVATE cuda/collocation_device.cu + cuda/xc_cuda_data.cxx + cuda/xc_cuda_util.cxx ) -target_compile_features( gauxc_device_integrator PUBLIC cuda_std_14 cxx_std_14 ) -target_link_libraries( gauxc_device_integrator PUBLIC ExchCXX::exchcxx - MAGMA::magma CUDA::cudart CUDA::cublas ) -target_include_directories( gauxc_device_integrator +target_compile_features( gauxc PRIVATE cuda_std_14 ) +target_include_directories( gauxc PRIVATE - $ - $ - $ - $ $ $ ) -target_compile_options( gauxc_device_integrator +target_compile_options( gauxc PRIVATE - $<$: -Wall -Wextra -Wpedantic -Wnon-virtual-dtor> $<$: -Xcudafe --diag_suppress=partial_override -Xptxas -v > ) +target_link_libraries( gauxc PUBLIC MAGMA::magma CUDA::cublas ) + + diff --git a/src/integrator/cuda/xc_cuda_util.cxx b/src/integrator/cuda/xc_cuda_util.cxx new file mode 100644 index 000000000..dc3e66564 --- /dev/null +++ b/src/integrator/cuda/xc_cuda_util.cxx @@ -0,0 +1,191 @@ +#include + +//#include "cuda_weights.hpp" +#include "collocation_device.hpp" +//#include "cuda_zmat.hpp" +#include "integrator_common.hpp" +//#include "blas.hpp" +//#include "util.hpp" + +namespace GauXC { +namespace integrator::cuda { + + + +template +void process_batches_cuda_replicated_p( + XCWeightAlg weight_alg, + const functional_type& func, + const BasisSet& basis, + const Molecule & mol, + const MolMeta & meta, + XCCudaData & cuda_data, + std::vector< XCTask >& tasks, + const F* P, + F* VXC, + F* exc, + F* n_el +) { + +#if 0 + partition_weights_cuda( weight_alg, mol, meta, tasks ); + const auto nbf = basis.nbf(); + + std::fill( VXC, VXC + size_t(nbf)*nbf, F(0.) ); + + size_t ntasks = tasks.size(); + for( size_t iT = 0; iT < ntasks; ++iT ) { + + auto& task = tasks[iT]; + + const size_t npts = task.points.size(); + const size_t nbe = task.nbe; + const size_t nshells = task.shell_list.size(); + + const F* points = task.points.data()->data(); + const F* weights = task.weights.data(); + const int32_t* shell_list = task.shell_list.data(); + + F* basis_eval = cuda_data.basis_eval.data(); + F* den_eval = cuda_data.den_scr.data(); + F* nbe_scr = cuda_data.nbe_scr.data(); + F* zmat = cuda_data.zmat.data(); + + F* eps = cuda_data.eps.data(); + F* gamma = cuda_data.gamma.data(); + F* vrho = cuda_data.vrho.data(); + F* vgamma = cuda_data.vgamma.data(); + + F* dbasis_x_eval = nullptr; + F* dbasis_y_eval = nullptr; + F* dbasis_z_eval = nullptr; + F* dden_x_eval = nullptr; + F* dden_y_eval = nullptr; + F* dden_z_eval = nullptr; + + if( n_deriv > 0 ) { + dbasis_x_eval = basis_eval + npts * nbe; + dbasis_y_eval = dbasis_x_eval + npts * nbe; + dbasis_z_eval = dbasis_y_eval + npts * nbe; + dden_x_eval = den_eval + npts; + dden_y_eval = dden_x_eval + npts; + dden_z_eval = dden_y_eval + npts; + } + + + // Get the submatrix map for batch + auto submat_map = gen_compressed_submat_map( basis, task.shell_list ); + + + // Evaluate Collocation Matrix + if( n_deriv == 1 ) + eval_collocation_deriv1( npts, nshells, nbe, points, basis, shell_list, + basis_eval, dbasis_x_eval, dbasis_y_eval, + dbasis_z_eval ); + else + eval_collocation( npts, nshells, nbe, points, basis, shell_list, basis_eval ); + + + // Extrat Submatrix + const F* den_ptr_use = P; + if( nbe != nbf ) { + detail::submat_set( nbf, nbf, nbe, nbe, P, nbf, nbe_scr, nbe, submat_map ); + den_ptr_use = nbe_scr; + } + + // Z = P * BF + GauXC::blas::gemm( 'N', 'N', nbe, npts, nbe, 1., den_ptr_use, nbe, + basis_eval, nbe, 0., zmat, nbe ); + + + // Evaluate the density + for( int32_t i = 0; i < npts; ++i ) { + + const size_t ioff = size_t(i) * nbe; + const F* zmat_i = zmat + ioff; + + den_eval[i] = + 2. * GauXC::blas::dot( nbe, basis_eval + ioff, 1, zmat_i, 1 ); + + if( n_deriv > 0 ) { + const F dx = + 4. * GauXC::blas::dot( nbe, dbasis_x_eval + ioff, 1, zmat_i, 1 ); + const F dy = + 4. * GauXC::blas::dot( nbe, dbasis_y_eval + ioff, 1, zmat_i, 1 ); + const F dz = + 4. * GauXC::blas::dot( nbe, dbasis_z_eval + ioff, 1, zmat_i, 1 ); + + dden_x_eval[i] = dx; + dden_y_eval[i] = dy; + dden_z_eval[i] = dz; + + gamma[i] = dx*dx + dy*dy + dz*dz; + } + + } + + + // Evaluate XC functional + if( func.is_gga() ) + func.eval_exc_vxc( npts, den_eval, gamma, eps, vrho, vgamma ); + else + func.eval_exc_vxc( npts, den_eval, eps, vrho ); + + // Scalar integrations + if( n_el ) + for( int32_t i = 0; i < npts; ++i ) *n_el += weights[i] * den_eval[i]; + + for( int32_t i = 0; i < npts; ++i ) *exc += weights[i] * den_eval[i] * eps[i]; + + + // Assemble Z + if( func.is_gga() ) + zmat_gga_cuda( npts, nbe, weights, vrho, vgamma, basis_eval, dbasis_x_eval, + dbasis_y_eval, dbasis_z_eval, dden_x_eval, dden_y_eval, + dden_z_eval, zmat ); + else + zmat_lda_cuda( npts, nbe, weights, vrho, basis_eval, zmat ); + + + // Update VXC + GauXC::blas::syr2k( 'L', 'N', nbe, npts, F(1.), basis_eval, + nbe, zmat, nbe, F(0.), nbe_scr, nbe ); + + detail::inc_by_submat( nbf, nbf, nbe, nbe, VXC, nbf, nbe_scr, nbe, + submat_map ); + } + + // Symmetrize + for( int32_t j = 0; j < nbf; ++j ) + for( int32_t i = j+1; i < nbf; ++i ) + VXC[ j + i*nbf ] = VXC[ i + j*nbf ]; + + //std::cout << std::scientific << std::setprecision(12); + //std::cout << "NEL = " << *n_el << std::endl; + +#endif +} + + +#define CUDA_IMPL( F, ND ) \ +template \ +void process_batches_cuda_replicated_p(\ + XCWeightAlg weight_alg,\ + const functional_type& func,\ + const BasisSet& basis,\ + const Molecule & mol,\ + const MolMeta & meta,\ + XCCudaData & cuda_data,\ + std::vector< XCTask >& local_work,\ + const F* P,\ + F* VXC,\ + F* exc,\ + F* n_el\ +) + +CUDA_IMPL( double, 0 ); +CUDA_IMPL( double, 1 ); + +} +} + diff --git a/src/integrator/host/gauxc-host_integrator.cmake b/src/integrator/host/gauxc-host_integrator.cmake new file mode 100644 index 000000000..7a8f7814b --- /dev/null +++ b/src/integrator/host/gauxc-host_integrator.cmake @@ -0,0 +1,7 @@ +target_sources( gauxc PRIVATE host/xc_host_util.cxx + host/host_weights.cxx + host/host_collocation.cxx + host/host_zmat.cxx + host/blas.cxx +) + From 540e1cbce2e82aaf5cb6dfb1c4ffddb7a3dbde2a Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Tue, 25 Aug 2020 18:54:54 -0400 Subject: [PATCH 21/27] [WIP, breaks tests] Validate memory saturation + data movement --- include/gauxc/util/cuda_util.hpp | 6 ++ .../default_xc_cuda_integrator.hpp | 16 +++-- src/integrator/cuda/xc_cuda_data.cxx | 4 ++ src/integrator/cuda/xc_cuda_util.cxx | 71 ++++++++++++++++++- tests/xc_integrator.cxx | 6 +- 5 files changed, 93 insertions(+), 10 deletions(-) diff --git a/include/gauxc/util/cuda_util.hpp b/include/gauxc/util/cuda_util.hpp index 5ff7f9d6a..011782985 100644 --- a/include/gauxc/util/cuda_util.hpp +++ b/include/gauxc/util/cuda_util.hpp @@ -82,6 +82,12 @@ inline void cuda_copy_async( size_t len, T* dest, const T* src, cudaStream_t s, } +template +inline void cuda_set_zero( size_t len, T* ptr, std::string m = "" ) { + auto stat = cudaMemset( ptr, 0, len * sizeof(T) ); + GAUXC_CUDA_ERROR( "CUDA Memset Async Failed ["+m+"]", stat ); +} + inline void cuda_device_sync() { diff --git a/include/gauxc/xc_integrator/default_xc_cuda_integrator.hpp b/include/gauxc/xc_integrator/default_xc_cuda_integrator.hpp index 3c97fa81c..9939717bb 100644 --- a/include/gauxc/xc_integrator/default_xc_cuda_integrator.hpp +++ b/include/gauxc/xc_integrator/default_xc_cuda_integrator.hpp @@ -51,22 +51,28 @@ typename DefaultXCCudaIntegrator::exc_vxc_type } - size_t nbf = this->basis_->nbf(); + size_t nbf = this->basis_->nbf(); + size_t nshells = this->basis_->size(); //// TODO: Check that P is sane auto& tasks = this->load_balancer_->get_tasks(); - size_t max_npts = this->load_balancer_->max_npts(); - size_t max_nbe = this->load_balancer_->max_nbe(); - size_t max_npts_x_nbe = this->load_balancer_->max_npts_x_nbe(); + //size_t max_npts = this->load_balancer_->max_npts(); + //size_t max_nbe = this->load_balancer_->max_nbe(); + //size_t max_npts_x_nbe = this->load_balancer_->max_npts_x_nbe(); size_t n_deriv = this->func_->is_gga() ? 1 : 0; // Allocate Memory cuda_data_ = std::make_shared>( - n_deriv, nbf, max_npts, max_npts_x_nbe + this->load_balancer_->molecule().size(), + n_deriv, + nbf, + nshells, + false, + false ); diff --git a/src/integrator/cuda/xc_cuda_data.cxx b/src/integrator/cuda/xc_cuda_data.cxx index d8d731a7a..189f56069 100644 --- a/src/integrator/cuda/xc_cuda_data.cxx +++ b/src/integrator/cuda/xc_cuda_data.cxx @@ -34,6 +34,10 @@ XCCudaData::XCCudaData( size_t _natoms, stat = cudaMalloc( &device_ptr, fill_sz ); GAUXC_CUDA_ERROR( "CUDA Malloc Failed", stat ); + //std::cout << "NS = " << nshells << ", NA = " << natoms << ", NBF = " << nbf << std::endl; + + //std::cout << "XCDeviceData has allocated " << fill_sz << " bytes of data" + // << std::endl; // Allocate static memory with proper alignment buffer_adaptor mem( device_ptr, fill_sz ); diff --git a/src/integrator/cuda/xc_cuda_util.cxx b/src/integrator/cuda/xc_cuda_util.cxx index dc3e66564..356b2a10c 100644 --- a/src/integrator/cuda/xc_cuda_util.cxx +++ b/src/integrator/cuda/xc_cuda_util.cxx @@ -7,9 +7,13 @@ //#include "blas.hpp" //#include "util.hpp" +#include + namespace GauXC { namespace integrator::cuda { +//template +//void template @@ -23,10 +27,73 @@ void process_batches_cuda_replicated_p( std::vector< XCTask >& tasks, const F* P, F* VXC, - F* exc, - F* n_el + F* EXC, + F* NEL ) { + + auto task_comparator = []( const XCTask& a, const XCTask& b ) { + return (a.points.size() * a.nbe) > (b.points.size() * b.nbe); + }; + std::sort( tasks.begin(), tasks.end(), task_comparator ); + + + const auto nbf = basis.nbf(); + const auto natoms = meta.natoms(); + // Send static data to the device + + // Density + if( not cuda_data.denpack_host ) + util::cuda_copy( nbf * nbf, cuda_data.dmat_device, P, "P H2D" ); + + // Shells: TODO avoid copy? + std::vector> shells( basis ); + util::cuda_copy( shells.size(), cuda_data.shells_device, shells.data(), + "Shells H2D" ); + + // RAB + util::cuda_copy( natoms * natoms, cuda_data.rab_device, meta.rab().data(), + "RAB H2D" ); + + // Atomic coordinates + std::vector coords( 3*natoms ); + for( auto i = 0ul; i < natoms; ++i ) { + coords[ 3*i + 0 ] = mol[i].x; + coords[ 3*i + 1 ] = mol[i].y; + coords[ 3*i + 2 ] = mol[i].z; + } + util::cuda_copy( 3 * natoms, cuda_data.coords_device, coords.data(), + "Coords H2D" ); + + + // Zero out XC quantities + util::cuda_set_zero( nbf * nbf, cuda_data.vxc_device, "VXC Zero" ); + util::cuda_set_zero( 1 , cuda_data.exc_device, "EXC Zero" ); + util::cuda_set_zero( 1 , cuda_data.nel_device, "NEL Zero" ); + + + + // Processes batches in groups that saturadate available device memory + auto task_it = tasks.begin(); + while( task_it != tasks.end() ) { + + // Determine next task batch, send relevant data to device + auto [it, tasks_device] = + cuda_data.generate_buffers( basis, task_it, tasks.end() ); + + // TODO Process the batches + + task_it = it; + + } + + // Receive XC terms from host + if( not cuda_data.vxcinc_host ) + util::cuda_copy( nbf * nbf, VXC, cuda_data.vxc_device, "VXC D2H" ); + + util::cuda_copy( 1, EXC, cuda_data.exc_device, "EXC D2H" ); + util::cuda_copy( 1, NEL, cuda_data.nel_device, "NEL D2H" ); + #if 0 partition_weights_cuda( weight_alg, mol, meta, tasks ); const auto nbf = basis.nbf(); diff --git a/tests/xc_integrator.cxx b/tests/xc_integrator.cxx index bff50bc05..6c9eb9946 100644 --- a/tests/xc_integrator.cxx +++ b/tests/xc_integrator.cxx @@ -39,8 +39,8 @@ TEST_CASE( "Benzene / PBE0 / cc-pVDZ", "[xc-integrator]" ) { } auto [ EXC, VXC ] = integrator.eval_exc_vxc( P ); - CHECK( EXC == Approx( EXC_ref ) ); + //CHECK( EXC == Approx( EXC_ref ) ); - auto VXC_diff_nrm = ( VXC - VXC_ref ).norm(); - CHECK( VXC_diff_nrm / basis.nbf() < 1e-10 ); + //auto VXC_diff_nrm = ( VXC - VXC_ref ).norm(); + //CHECK( VXC_diff_nrm / basis.nbf() < 1e-10 ); } From b52112a8195d9814715eb770fcfc45e4cfa23ee7 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Thu, 27 Aug 2020 14:53:55 -0400 Subject: [PATCH 22/27] Silence compiler warnings --- src/CMakeLists.txt | 1 + src/grid.cxx | 2 +- src/grid_impl.cxx | 2 +- src/integrator/host/blas.cxx | 22 +++++++++++++++++++--- src/integrator/host/host_collocation.cxx | 4 ++-- src/integrator/host/host_weights.cxx | 6 +++--- src/integrator/host/util.hpp | 8 ++++++++ src/integrator/host/xc_host_util.cxx | 8 ++++---- src/load_balancer.cxx | 4 ++-- src/molmeta.cxx | 8 ++++---- src/replicated_load_balancer.cxx | 8 ++++---- 11 files changed, 49 insertions(+), 24 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f6d4f8f86..b0c52d5b8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,6 +47,7 @@ target_link_libraries( gauxc PUBLIC ExchCXX::exchcxx IntegratorXX::integratorxx MPI::MPI_C + OpenMP::OpenMP_CXX Eigen3::Eigen LAPACK::lapack gauxc_gau2grid diff --git a/src/grid.cxx b/src/grid.cxx index 7c4f9e280..162c8f36a 100644 --- a/src/grid.cxx +++ b/src/grid.cxx @@ -10,7 +10,7 @@ Grid::Grid( RadialQuad rq, RadialSize rs, AngularSize as, RadialScale rscal, pimpl_(std::make_shared(rq, rs, as, rscal, bs)) { } Grid::Grid( RadialQuad rq, RadialSize rs, AngularSize as, RadialScale rscal ) : - Grid( rq, rs, as, rscal, default_batch_sz ) { }; + Grid( rq, rs, as, rscal, default_batch_sz ) { } Grid::Grid( RadialSize rs, AngularSize as, RadialScale rscal, BatchSize bs ) : Grid( RadialQuad::MuraKnowles, rs, as, rscal, bs ) { } diff --git a/src/grid_impl.cxx b/src/grid_impl.cxx index 7c21cb597..5fc9c7228 100644 --- a/src/grid_impl.cxx +++ b/src/grid_impl.cxx @@ -10,7 +10,7 @@ namespace detail { GridImpl::GridImpl( RadialQuad rq, RadialSize rs, AngularSize as, RadialScale rscal, BatchSize bs ) : - n_rad_(rs), n_ang_(as), max_batch_sz_(bs), rquad_(rq), r_scal_(rscal) { + n_rad_(rs), n_ang_(as), max_batch_sz_(bs), r_scal_(rscal), rquad_(rq) { generate(); diff --git a/src/integrator/host/blas.cxx b/src/integrator/host/blas.cxx index 41b3e910a..dd05d15c8 100644 --- a/src/integrator/host/blas.cxx +++ b/src/integrator/host/blas.cxx @@ -54,9 +54,25 @@ void lacpy( char UPLO, int M, int N, const T* A, int LDA, T* B, else throw std::runtime_error("LACPY NYI"); */ - for( int j = 0; j < N; ++j ) - for( int i = 0; i < M; ++i ) - B[i + j*LDB] = A[i + j*LDA]; + if( UPLO == 'L' ) { + + for( int j = 0; j < N; ++j ) + for( int i = j; i < M; ++i ) + B[i + j*LDB] = A[i + j*LDA]; + + } else if( UPLO == 'U' ) { + + for( int j = 0; j < N; ++j ) + for( int i = 0; i <= j; ++i ) + B[i + j*LDB] = A[i + j*LDA]; + + } else { + + for( int j = 0; j < N; ++j ) + for( int i = 0; i < M; ++i ) + B[i + j*LDB] = A[i + j*LDA]; + + } } diff --git a/src/integrator/host/host_collocation.cxx b/src/integrator/host/host_collocation.cxx index 7022a7384..2ef53234c 100644 --- a/src/integrator/host/host_collocation.cxx +++ b/src/integrator/host/host_collocation.cxx @@ -15,7 +15,7 @@ void eval_collocation( size_t npts, auto* rv = a.allocate( npts * nbe ); size_t ncomp = 0; - for( int i = 0; i < nshells; ++i ) { + for( size_t i = 0; i < nshells; ++i ) { const auto& sh = basis.at(shell_mask[i]); int order = sh.pure() ? GG_SPHERICAL_CCA : GG_CARTESIAN_CCA; @@ -49,7 +49,7 @@ void eval_collocation_deriv1( size_t npts, auto* rv_z = rv_y + npts * nbe; size_t ncomp = 0; - for( int i = 0; i < nshells; ++i ) { + for( size_t i = 0; i < nshells; ++i ) { const auto& sh = basis.at(shell_mask[i]); int order = sh.pure() ? GG_SPHERICAL_CCA : GG_CARTESIAN_CCA; diff --git a/src/integrator/host/host_weights.cxx b/src/integrator/host/host_weights.cxx index a3ddd5263..044808396 100644 --- a/src/integrator/host/host_weights.cxx +++ b/src/integrator/host/host_weights.cxx @@ -56,7 +56,7 @@ void becke_weights_host( std::vector partitionScratch( natoms ); std::vector atomDist( natoms ); - #pragma omp for collapse(2) + #pragma omp for for( size_t iT = 0; iT < ntasks; ++iT ) for( size_t i = 0; i < tasks[iT].points.size(); ++i ) { @@ -127,7 +127,7 @@ void ssf_weights_host( std::vector partitionScratch( natoms ); std::vector atomDist( natoms ); - #pragma omp for collapse(2) + #pragma omp for for( size_t iT = 0; iT < ntasks; ++iT ) for( size_t i = 0; i < tasks[iT].points.size(); ++i ) { @@ -151,7 +151,7 @@ void ssf_weights_host( // Compute distances of each center to point for(size_t iA = 0; iA < natoms; iA++) { - if( iA == task.iParent ) continue; + if( iA == (size_t)task.iParent ) continue; const double da_x = point[0] - mol[iA].x; const double da_y = point[1] - mol[iA].y; diff --git a/src/integrator/host/util.hpp b/src/integrator/host/util.hpp index ea673b7d2..17a2a53a5 100644 --- a/src/integrator/host/util.hpp +++ b/src/integrator/host/util.hpp @@ -13,6 +13,10 @@ void submat_set(int32_t M, int32_t N, int32_t MSub, int32_t LDAS, std::vector> &submat_map) { + (void)(M); + (void)(N); + (void)(MSub); + (void)(NSub); int32_t i(0); for( auto& iCut : submat_map ) { @@ -43,6 +47,10 @@ void inc_by_submat(int32_t M, int32_t N, int32_t MSub, int32_t LDAS, std::vector> &submat_map) { + (void)(M); + (void)(N); + (void)(MSub); + (void)(NSub); int32_t i(0); for( auto& iCut : submat_map ) { diff --git a/src/integrator/host/xc_host_util.cxx b/src/integrator/host/xc_host_util.cxx index 79b44131d..c8b268b1d 100644 --- a/src/integrator/host/xc_host_util.cxx +++ b/src/integrator/host/xc_host_util.cxx @@ -28,7 +28,7 @@ void process_batches_host_replicated_p( ) { partition_weights_host( weight_alg, mol, meta, tasks ); - const auto nbf = basis.nbf(); + const int32_t nbf = basis.nbf(); std::fill( VXC, VXC + size_t(nbf)*nbf, F(0.) ); @@ -37,9 +37,9 @@ void process_batches_host_replicated_p( auto& task = tasks[iT]; - const size_t npts = task.points.size(); - const size_t nbe = task.nbe; - const size_t nshells = task.shell_list.size(); + const int32_t npts = task.points.size(); + const int32_t nbe = task.nbe; + const int32_t nshells = task.shell_list.size(); const F* points = task.points.data()->data(); const F* weights = task.weights.data(); diff --git a/src/load_balancer.cxx b/src/load_balancer.cxx index 6743cb26c..a5c25c498 100644 --- a/src/load_balancer.cxx +++ b/src/load_balancer.cxx @@ -25,11 +25,11 @@ LoadBalancer::LoadBalancer( MPI_Comm comm, const Molecule& mol, const MolGrid& m LoadBalancer::LoadBalancer( const LoadBalancer& other ) : - LoadBalancer(other.pimpl_->clone()){ }; + LoadBalancer(other.pimpl_->clone()){ } LoadBalancer::LoadBalancer( LoadBalancer&& ) noexcept = default; -LoadBalancer::~LoadBalancer() noexcept = default;; +LoadBalancer::~LoadBalancer() noexcept = default; const std::vector& LoadBalancer::get_tasks() const { diff --git a/src/molmeta.cxx b/src/molmeta.cxx index c20c1ab81..2aa958ac7 100644 --- a/src/molmeta.cxx +++ b/src/molmeta.cxx @@ -16,9 +16,9 @@ void MolMeta::compute_rab(const Molecule& mol) { rab_.resize( natoms_*natoms_ ); - for( int32_t i = 0; i < natoms_; ++i ) { + for( size_t i = 0; i < natoms_; ++i ) { rab_[i*(natoms_+1)] = 0.; - for( int32_t j = 0; j < i; ++j ) { + for( size_t j = 0; j < i; ++j ) { const double dab_x = mol[i].x - mol[j].x; const double dab_y = mol[i].y - mol[j].y; const double dab_z = mol[i].z - mol[j].z; @@ -33,11 +33,11 @@ void MolMeta::compute_rab(const Molecule& mol) { void MolMeta::compute_dist_nearest() { dist_nearest_.resize(natoms_); - for( int32_t i = 0; i < natoms_; ++i ) { + for( size_t i = 0; i < natoms_; ++i ) { double dn = std::numeric_limits::infinity(); auto at_begin = rab_.begin() + i*natoms_; - for( int32_t j = 0; j < natoms_; ++j ) + for( size_t j = 0; j < natoms_; ++j ) if( i != j and *(at_begin + j) < dn ) dn = *(at_begin + j); diff --git a/src/replicated_load_balancer.cxx b/src/replicated_load_balancer.cxx index d1d3e799a..aed58d0c6 100644 --- a/src/replicated_load_balancer.cxx +++ b/src/replicated_load_balancer.cxx @@ -69,7 +69,7 @@ auto micro_batch_screen( std::vector shell_list; shell_list.reserve(bs.nshells()); - for(auto iSh = 0; iSh < bs.size(); ++iSh) { + for(auto iSh = 0ul; iSh < bs.size(); ++iSh) { const auto& center = bs[iSh].O(); const auto crad = bs[iSh].cutoff_radius(); @@ -185,7 +185,7 @@ std::vector< XCTask > ReplicatedLoadBalancer::create_local_tasks_() const { // Assign Tasks to MPI ranks - if( (iCurrent+1) % atBatchSz == 0 or iCurrent == (natoms-1) ) { + if( (iCurrent+1) % atBatchSz == 0 or iCurrent == ((int32_t)natoms-1) ) { // Sort based on task index for deterministic assignment std::sort( temp_tasks.begin(), temp_tasks.end(), @@ -197,8 +197,8 @@ std::vector< XCTask > ReplicatedLoadBalancer::create_local_tasks_() const { for( size_t ibatch = 0; ibatch < temp_tasks.size(); ++ibatch ) { XCTask task = std::move(temp_tasks.at(ibatch).second); - auto& points = task.points; - auto nbe = task.nbe; + //auto& points = task.points; + //auto nbe = task.nbe; // Get rank with minimum work auto min_rank_it = From dada6881c8bbfe0abe0e824f641fef9cca008a66 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Thu, 27 Aug 2020 15:20:38 -0400 Subject: [PATCH 23/27] Added concept of an ExecutionSpace and analogous XCIntegrator factory. updates UTs, CUDA backend fails (obviously) --- include/gauxc/xc_integrator.hpp | 11 ++++- include/gauxc/xc_integrator/impl.hpp | 21 +++++---- .../xc_integrator/integrator_factory.hpp | 43 +++++++++++++++++++ tests/xc_integrator.cxx | 32 +++++++++++--- 4 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 include/gauxc/xc_integrator/integrator_factory.hpp diff --git a/include/gauxc/xc_integrator.hpp b/include/gauxc/xc_integrator.hpp index cc17d6f76..8cb0c881a 100644 --- a/include/gauxc/xc_integrator.hpp +++ b/include/gauxc/xc_integrator.hpp @@ -18,6 +18,11 @@ enum class XCWeightAlg { SSF }; +enum class ExecutionSpace { + Host, + Device +}; + template class XCIntegrator { @@ -40,8 +45,10 @@ class XCIntegrator { XCIntegrator(); XCIntegrator( std::unique_ptr&& pimpl ); - XCIntegrator( MPI_Comm, const functional_type&, const basisset_type&, - std::shared_ptr ); + XCIntegrator( ExecutionSpace, MPI_Comm, const functional_type&, + const basisset_type&, std::shared_ptr ); + XCIntegrator( MPI_Comm, const functional_type&, const basisset_type&, + std::shared_ptr ); //XCIntegrator( MPI_Comm, const functional_type&, const basisset_type&, // LoadBalancer&& ); diff --git a/include/gauxc/xc_integrator/impl.hpp b/include/gauxc/xc_integrator/impl.hpp index 94ecc4f55..62375a2b0 100644 --- a/include/gauxc/xc_integrator/impl.hpp +++ b/include/gauxc/xc_integrator/impl.hpp @@ -1,7 +1,6 @@ #pragma once - -#include +#include namespace GauXC { @@ -10,15 +9,19 @@ XCIntegrator::XCIntegrator( std::unique_ptr&& pimpl ) : pimpl_( std::move( pimpl ) ) { } template -XCIntegrator::XCIntegrator( MPI_Comm comm, const functional_type& func, - const basisset_type& basis, std::shared_ptr lb) : - XCIntegrator( detail::make_default_cuda_integrator( - comm, - std::make_shared(func), - std::make_shared(basis), - lb +XCIntegrator::XCIntegrator( ExecutionSpace ex, MPI_Comm comm, + const functional_type& func, + const basisset_type& basis, + std::shared_ptr lb) : + XCIntegrator( detail::integrator_factory( + ex, comm, func, basis, lb )) { } +template +XCIntegrator::XCIntegrator( MPI_Comm comm, const functional_type& func, + const basisset_type& basis, + std::shared_ptr lb) : + XCIntegrator( ExecutionSpace::Host, comm, func, basis, lb ) { }; template XCIntegrator::~XCIntegrator() noexcept = default; diff --git a/include/gauxc/xc_integrator/integrator_factory.hpp b/include/gauxc/xc_integrator/integrator_factory.hpp new file mode 100644 index 000000000..18847e9b2 --- /dev/null +++ b/include/gauxc/xc_integrator/integrator_factory.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +namespace GauXC { +namespace detail { + +template +std::unique_ptr> + integrator_factory( ExecutionSpace ex, MPI_Comm comm, const functional_type& func, + const BasisSet& basis, + std::shared_ptr lb) { + + if( ex == ExecutionSpace::Host ) { + + return make_default_host_integrator( + comm, + std::make_shared(func), + std::make_shared>(basis), + lb + ); + + } else { + +#ifdef GAUXC_ENABLE_CUDA + + return make_default_cuda_integrator( + comm, + std::make_shared(func), + std::make_shared>(basis), + lb + ); +#else + throw std::runtime_error("GAUXC_ENABLE_DEVICE is FALSE"); + return nullptr; +#endif + + } + + } + +} +} diff --git a/tests/xc_integrator.cxx b/tests/xc_integrator.cxx index 6c9eb9946..b74835658 100644 --- a/tests/xc_integrator.cxx +++ b/tests/xc_integrator.cxx @@ -6,10 +6,10 @@ using namespace GauXC; -TEST_CASE( "Benzene / PBE0 / cc-pVDZ", "[xc-integrator]" ) { - MPI_Comm comm = MPI_COMM_WORLD; - Molecule mol = make_benzene(); + +void test_xc_integrator( ExecutionSpace ex, MPI_Comm comm, Molecule mol ) { + BasisSet basis = make_ccpvdz( mol, SphericalType(true) ); for( auto& sh : basis ) @@ -25,7 +25,7 @@ TEST_CASE( "Benzene / PBE0 / cc-pVDZ", "[xc-integrator]" ) { functional_type func( ExchCXX::XCFunctional::Functional::PBE0, ExchCXX::Spin::Unpolarized ); using matrix_type = Eigen::MatrixXd; - XCIntegrator integrator( comm, func, basis, lb ); + XCIntegrator integrator( ex, comm, func, basis, lb ); matrix_type P,VXC_ref; @@ -39,8 +39,26 @@ TEST_CASE( "Benzene / PBE0 / cc-pVDZ", "[xc-integrator]" ) { } auto [ EXC, VXC ] = integrator.eval_exc_vxc( P ); - //CHECK( EXC == Approx( EXC_ref ) ); + CHECK( EXC == Approx( EXC_ref ) ); + + auto VXC_diff_nrm = ( VXC - VXC_ref ).norm(); + CHECK( VXC_diff_nrm / basis.nbf() < 1e-10 ); +} + + +TEST_CASE( "Benzene / PBE0 / cc-pVDZ", "[xc-integrator]" ) { + + MPI_Comm comm = MPI_COMM_WORLD; + Molecule mol = make_benzene(); + + SECTION( "Host" ) { + test_xc_integrator( ExecutionSpace::Host, comm, mol ); + } + +#ifdef GAUXC_ENABLE_CUDA + SECTION( "Device" ) { + test_xc_integrator( ExecutionSpace::Device, comm, mol ); + } +#endif - //auto VXC_diff_nrm = ( VXC - VXC_ref ).norm(); - //CHECK( VXC_diff_nrm / basis.nbf() < 1e-10 ); } From 5bc5e9cb0063a54e38e36664887146f0fce12871 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Thu, 27 Aug 2020 19:08:04 -0400 Subject: [PATCH 24/27] [WIP] Migrated CUDA partition weights, check that they run but not if they're correct --- include/gauxc/enums.hpp | 27 ++ include/gauxc/grid.hpp | 7 - include/gauxc/molgrid.hpp | 6 - include/gauxc/types.hpp | 2 + include/gauxc/xc_integrator.hpp | 9 - src/integrator/cuda/cuda_extensions.hpp | 83 ++++ src/integrator/cuda/cuda_weights.cu | 438 ++++++++++++++++++ src/integrator/cuda/cuda_weights.hpp | 26 ++ .../cuda/gauxc-cuda_integrator.cmake | 1 + src/integrator/cuda/xc_cuda_util.cxx | 213 +++------ 10 files changed, 649 insertions(+), 163 deletions(-) create mode 100644 include/gauxc/enums.hpp create mode 100644 src/integrator/cuda/cuda_extensions.hpp create mode 100644 src/integrator/cuda/cuda_weights.cu create mode 100644 src/integrator/cuda/cuda_weights.hpp diff --git a/include/gauxc/enums.hpp b/include/gauxc/enums.hpp new file mode 100644 index 000000000..07a7d6532 --- /dev/null +++ b/include/gauxc/enums.hpp @@ -0,0 +1,27 @@ +#pragma once + +namespace GauXC { + +enum class RadialQuad { + MuraKnowles, + MurrayHandyLaming +}; + +enum class AtomicGridSizeDefault { + FineGrid, + UltraFineGrid, + SuperFineGrid +}; + +enum class XCWeightAlg { + Becke, + SSF +}; + +enum class ExecutionSpace { + Host, + Device +}; + + +} diff --git a/include/gauxc/grid.hpp b/include/gauxc/grid.hpp index 010699bcc..9ef4e59d3 100644 --- a/include/gauxc/grid.hpp +++ b/include/gauxc/grid.hpp @@ -5,13 +5,6 @@ namespace GauXC { - -enum class RadialQuad { - MuraKnowles, - MurrayHandyLaming -}; - - using RadialSize = detail::NamedType< int64_t, struct RadialSizeType >; using AngularSize = detail::NamedType< int64_t, struct AngularSizeType >; using BatchSize = detail::NamedType< int64_t, struct BatchSizeType >; diff --git a/include/gauxc/molgrid.hpp b/include/gauxc/molgrid.hpp index aae6ab7d5..8ae258823 100644 --- a/include/gauxc/molgrid.hpp +++ b/include/gauxc/molgrid.hpp @@ -15,12 +15,6 @@ using atomic_grid_size_map = using atomic_grid_map = std::unordered_map< AtomicNumber, Grid >; -enum class AtomicGridSizeDefault { - FineGrid, - UltraFineGrid, - SuperFineGrid -}; - namespace detail { class MolGridImpl; } diff --git a/include/gauxc/types.hpp b/include/gauxc/types.hpp index c0d3b8505..577b91734 100644 --- a/include/gauxc/types.hpp +++ b/include/gauxc/types.hpp @@ -24,3 +24,5 @@ using batcher_type = IntegratorXX::SphericalMicroBatcher< >; } + +#include diff --git a/include/gauxc/xc_integrator.hpp b/include/gauxc/xc_integrator.hpp index 8cb0c881a..befb4ab19 100644 --- a/include/gauxc/xc_integrator.hpp +++ b/include/gauxc/xc_integrator.hpp @@ -13,15 +13,6 @@ namespace detail { class XCIntegratorImpl; } -enum class XCWeightAlg { - Becke, - SSF -}; - -enum class ExecutionSpace { - Host, - Device -}; template class XCIntegrator { diff --git a/src/integrator/cuda/cuda_extensions.hpp b/src/integrator/cuda/cuda_extensions.hpp new file mode 100644 index 000000000..bf7d60cc3 --- /dev/null +++ b/src/integrator/cuda/cuda_extensions.hpp @@ -0,0 +1,83 @@ +#pragma once +#include + +namespace GauXC { +namespace cuda { + +__inline__ __device__ +double warpReduceSum(double val) { + for(int i=16; i>=1; i/=2) + val += __shfl_xor_sync(0xffffffff, val, i, 32); + + return val; +} + +__inline__ __device__ +double blockReduceSum( double val ) { + + static __shared__ double shared[32]; + int lane = threadIdx.x % 32; + int wid = threadIdx.x / 32; + + val = warpReduceSum( val ); + + if( lane == 0 ) shared[wid] = val; + + __syncthreads(); + + val = (threadIdx.x < blockDim.x / 32) ? shared[lane] : 0; + if( wid == 0 ) val = warpReduceSum( val ); + + return val; + +} + +template +__inline__ __device__ T warp_prod_reduce( T val ) { + + for( int i = warp_size / 2; i >= 1; i /= 2 ) + val *= __shfl_xor_sync( 0xffffffff, val, i, warp_size ); + + return val; + +} + +template +__inline__ __device__ T block_prod_reduce( T val ) { + + static __shared__ T shared[32]; + const int lane = threadIdx.x % 32; + const int wid = threadIdx.x / 32; + + val = warp_prod_reduce( val ); + + if( lane == 0 ) shared[ wid ] = val; + __syncthreads(); + + val = ( threadIdx.x < blockDim.x / 32 ) ? shared[ lane ] : 0; + if( wid == 0 ) val = warp_prod_reduce( val ); + + return val; + +} + +__inline__ __device__ double atomicMul(double* address, double val) +{ + unsigned long long int* address_as_ull = + (unsigned long long int*)address; + unsigned long long int old = *address_as_ull, assumed; + + do { + assumed = old; + old = atomicCAS(address_as_ull, assumed, + __double_as_longlong(val * + __longlong_as_double(assumed))); + + // Note: uses integer comparison to avoid hang in case of NaN (since NaN != NaN) + } while (assumed != old); + + return __longlong_as_double(old); +} + +} +} diff --git a/src/integrator/cuda/cuda_weights.cu b/src/integrator/cuda/cuda_weights.cu new file mode 100644 index 000000000..b54008174 --- /dev/null +++ b/src/integrator/cuda/cuda_weights.cu @@ -0,0 +1,438 @@ +#include + +#include "cuda_weights.hpp" +#include "integrator_constants.hpp" +#include "cuda_extensions.hpp" + +constexpr double eps_d = std::numeric_limits::epsilon(); + + +namespace GauXC { +namespace integrator { +namespace cuda { + +using namespace GauXC::cuda; + + +__global__ void compute_point_center_dist( + size_t npts, + size_t natoms, + const double* coords, + const double* points, + double* dist +) { + + + const int tid_x = threadIdx.x + blockIdx.x * blockDim.x; + const int tid_y = threadIdx.y + blockIdx.y * blockDim.y; + + + if( tid_y < natoms and tid_x < npts ) { + + const int iAtom = tid_y; + const int iPt = tid_x; + + const double rx = points[3*iPt + 0] - coords[3*iAtom + 0]; + const double ry = points[3*iPt + 1] - coords[3*iAtom + 1]; + const double rz = points[3*iPt + 2] - coords[3*iAtom + 2]; + + dist[ iAtom + iPt * natoms ] = std::sqrt( rx*rx + ry*ry + rz*rz ); + + } + +} + +__global__ void modify_weights_becke_kernel( + size_t npts, + size_t natoms, + const double* RAB, + const double* coords, + const double* dist_scratch, + const int32_t* iparent_device, + double* weights_device +) { + + // Becke partition functions + auto hBecke = [](double x) {return 1.5 * x - 0.5 * x * x * x;}; // Eq. 19 + auto gBecke = [&](double x) {return hBecke(hBecke(hBecke(x)));}; // Eq. 20 f_3 + + + __shared__ double shared[2048]; + for( int ipt = blockIdx.x; ipt < npts; ipt += gridDim.x ) { + + const auto iParent = iparent_device[ipt]; + + double sum = 0.; + double parent_weight = 0.; + + const double* const local_dist_scratch = dist_scratch + ipt * natoms; + for( int iCenter = threadIdx.y; iCenter < natoms; iCenter += blockDim.y ) { + + const double ri = local_dist_scratch[ iCenter ]; + + const double* const local_rab = RAB + iCenter * natoms; + + double ps = 1.; + for( int jCenter = threadIdx.x; jCenter < natoms; jCenter += blockDim.x ) { + + const double rj = local_dist_scratch[ jCenter ]; + + const double mu = (ri - rj) / local_rab[ jCenter ]; // XXX: RAB is symmetric + const double s = 0.5 * ( 1. - gBecke( mu ) ); + + ps *= (iCenter == jCenter) ? 1. : s ; + + } + + ps = warp_prod_reduce( ps ); // XXX: Assumes blockDim.x == 32 + + if( iCenter == iParent ) parent_weight = ps; + + sum += ps; + + } + + // XXX: Assumes blockDim.x == blockDim.y == 32 + if( threadIdx.x == 0 ) { + shared[ threadIdx.y ] = sum; + shared[ threadIdx.y + 1024] = parent_weight; + } + + __syncthreads(); + sum = shared[ threadIdx.x ]; + sum = warpReduceSum( sum ); + + __syncthreads(); + parent_weight = shared[ threadIdx.x + 1024]; + parent_weight = __shfl_sync(0xffffffff, parent_weight, iParent % 32, 32 ); + + if( threadIdx.x == 0 and threadIdx.y == 0 ) + weights_device[ipt] *= parent_weight / sum; + + + } + + +} + + + +__global__ void modify_weights_ssf_kernel( + size_t npts, + size_t natoms, + const double* RAB, + const double* coords, + const double* dist_scratch, + const int32_t* iparent_device, + const double* dist_nearest_device, + double* weights_device +) { + + // Frisch partition functions + auto gFrisch = [](double x) { + + const double s_x = x / magic_ssf_factor<>; + const double s_x2 = s_x * s_x; + const double s_x3 = s_x * s_x2; + const double s_x5 = s_x3 * s_x2; + const double s_x7 = s_x5 * s_x2; + + return (35.*(s_x - s_x3) + 21.*s_x5 - 5.*s_x7) / 16.; + }; + + auto sFrisch = [&] (double x) { + const double g = 0.5 * (1. - gFrisch(x)); + return (x >= magic_ssf_factor<>) ? 0. : (x <= -magic_ssf_factor<>) ? 1. : g; + }; + + constexpr double weight_tol = 1e-10; + + __shared__ double shared[2048]; + for( int ipt = blockIdx.x; ipt < npts; ipt += gridDim.x ) { + + const auto iParent = iparent_device[ipt]; + + double sum = 0.; + double parent_weight = 0.; + + const double* const local_dist_scratch = dist_scratch + ipt * natoms; + const double dist_cutoff = 0.5 * (1 - magic_ssf_factor<> ) * + dist_nearest_device[ipt]; + if( local_dist_scratch[iParent] < dist_cutoff ) continue; + + for( int iCenter = threadIdx.y; iCenter < natoms; iCenter += blockDim.y ) { + + const double ri = local_dist_scratch[ iCenter ]; + + const double* const local_rab = RAB + iCenter * natoms; + + double ps = 1.; + for( int jCenter = threadIdx.x; jCenter < natoms; jCenter += blockDim.x ) + if( fabs(ps) > weight_tol ) { + + const double rj = local_dist_scratch[ jCenter ]; + + const double mu = (ri - rj) / local_rab[ jCenter ]; // XXX: RAB is symmetric + const double s = sFrisch( mu ); + ps *= (iCenter == jCenter) ? 1. : s ; + + } + + ps = warp_prod_reduce( ps ); // XXX: Assumes blockDim.x == 32 + + if( iCenter == iParent ) parent_weight = ps; + + sum += ps; + + } + + // XXX: Assumes blockDim.x == blockDim.y == 32 + if( threadIdx.x == 0 ) { + shared[ threadIdx.y ] = sum; + shared[ threadIdx.y + 1024] = parent_weight; + } + + __syncthreads(); + sum = shared[ threadIdx.x ]; + sum = warpReduceSum( sum ); + + __syncthreads(); + parent_weight = shared[ threadIdx.x + 1024]; + parent_weight = __shfl_sync(0xffffffff, parent_weight, iParent % 32, 32 ); + + if( threadIdx.x == 0 and threadIdx.y == 0 ) + weights_device[ipt] *= parent_weight / sum; + + + } + + +} + +// SIMT over points: 1D kernel +__global__ void modify_weights_ssf_kernel_1d( + size_t npts, + size_t natoms, + const double* RAB, + const double* coords, + const double* dist_scratch, + const int32_t* iparent_device, + const double* dist_nearest_device, + double* weights_device +) { + + // Frisch partition functions + auto gFrisch = [](double x) { + + const double s_x = x / magic_ssf_factor<>; + const double s_x2 = s_x * s_x; + const double s_x3 = s_x * s_x2; + const double s_x5 = s_x3 * s_x2; + const double s_x7 = s_x5 * s_x2; + + return (35.*(s_x - s_x3) + 21.*s_x5 - 5.*s_x7) / 16.; + }; + +#if 0 + auto sFrisch = [&] (double x) { + const double g = 0.5 * (1. - gFrisch(x)); + return (x >= magic_ssf_factor<>) ? 0. : (x <= -magic_ssf_factor<>) ? 1. : g; + }; +#else + auto sFrisch = [&] (double x) { + if( fabs(x) < magic_ssf_factor<> ) return 0.5 * (1. - gFrisch(x)); + else if( x >= magic_ssf_factor<> ) return 0.; + else return 1.; + }; +#endif + + constexpr double weight_tol = 1e-10; + + const int tid_x = threadIdx.x + blockIdx.x * blockDim.x; + const int nt_x = blockDim.x * gridDim.x; + + //__shared__ double shared[2048]; + for( int ipt = tid_x; ipt < npts; ipt += nt_x ) { + + const auto iParent = iparent_device[ipt]; + + double sum = 0.; + double parent_weight = 0.; + + const double* const local_dist_scratch = dist_scratch + ipt * natoms; + const double dist_cutoff = 0.5 * (1 - magic_ssf_factor<> ) * + dist_nearest_device[ipt]; + if( local_dist_scratch[iParent] < dist_cutoff ) continue; + +#if 0 + for( int iCenter = 0; iCenter < natoms; iCenter++ ) { + + const double ri = local_dist_scratch[ iCenter ]; + + const double* const local_rab = RAB + iCenter * natoms; + + double ps = 1.; + for( int jCenter = 0; jCenter < natoms; jCenter++ ) + if( fabs(ps) > weight_tol ) { + if( iCenter != jCenter ) { + + const double rj = local_dist_scratch[ jCenter ]; + + const double mu = (ri - rj) / local_rab[ jCenter ]; // XXX: RAB is symmetric + ps *= sFrisch( mu ); + + } + } else break; + + //__syncwarp(); + + if( iCenter == iParent ) parent_weight = ps; + + sum += ps; + + } +#else + + // Do iParent First + { + + const double ri = local_dist_scratch[ iParent ]; + const double* const local_rab = RAB + iParent * natoms; + + parent_weight = 1.; + for( int jCenter = 0; jCenter < natoms; jCenter++ ) + if( parent_weight > weight_tol ) { + if( iParent != jCenter ) { + + const double rj = local_dist_scratch[ jCenter ]; + + const double mu = (ri - rj) / local_rab[ jCenter ]; // XXX: RAB is symmetric + parent_weight *= sFrisch( mu ); + + } + } else break; + + //__syncwarp(); + sum += parent_weight; + + } + + if( parent_weight < eps_d ) { + weights_device[ipt] = 0.; + continue; + } + + for( int iCenter = 0; iCenter < natoms; iCenter++ ) + if( iParent != iCenter ) { + + const double ri = local_dist_scratch[ iCenter ]; + + const double* const local_rab = RAB + iCenter * natoms; + + double ps = 1.; + for( int jCenter = 0; jCenter < natoms; jCenter++ ) + if( ps > weight_tol ) { + if( iCenter != jCenter ) { + + const double rj = local_dist_scratch[ jCenter ]; + + const double mu = (ri - rj) / local_rab[ jCenter ]; // XXX: RAB is symmetric + ps *= sFrisch( mu ); + + } + } else break; + + //__syncwarp(); + sum += ps; + + } + +#endif + + weights_device[ipt] *= parent_weight / sum; + + + } + + +} + + + + +template +void partition_weights_cuda_SoA( XCWeightAlg weight_alg, + size_t npts, + size_t natoms, + const F* points_device, + const int32_t* iparent_device, + const F* dist_nearest_device, + const F* rab_device, + const F* atomic_coords_device, + F* weights_device, + F* dist_scratch_device, + cudaStream_t stream ) { + + + + // Evaluate point-to-atom collocation + { + + dim3 threads( 32, 32 ); + dim3 blocks( util::div_ceil( npts, threads.x ), + util::div_ceil( natoms, threads.y ) ); + + compute_point_center_dist<<< blocks, threads, 0, stream>>>( + npts, natoms, atomic_coords_device, points_device, dist_scratch_device + ); + + } + + const bool partition_weights_1d_kernel = true; + + if( partition_weights_1d_kernel ) { + + dim3 threads(1024); + dim3 blocks( util::div_ceil( npts, threads.x )); + modify_weights_ssf_kernel_1d<<< blocks, threads, 0, stream >>>( + npts, natoms, rab_device, atomic_coords_device, dist_scratch_device, + iparent_device, dist_nearest_device, weights_device + ); + + } else { + + dim3 threads( 32, 32 ); + dim3 blocks ( npts, 1 ); + + if( weight_alg == XCWeightAlg::SSF ) + modify_weights_ssf_kernel<<< blocks, threads, 0, stream >>>( + npts, natoms, rab_device, atomic_coords_device, dist_scratch_device, + iparent_device, dist_nearest_device, weights_device + ); + else + modify_weights_becke_kernel<<< blocks, threads, 0, stream >>>( + npts, natoms, rab_device, atomic_coords_device, dist_scratch_device, + iparent_device, weights_device + ); + + } + + +} + +template +void partition_weights_cuda_SoA( XCWeightAlg weight_alg, + size_t npts, + size_t natoms, + const double* points_device, + const int32_t* iparent_device, + const double* dist_nearest_device, + const double* rab_device, + const double* atomic_coords_device, + double* weights_device, + double* dist_scratch_device, + cudaStream_t stream ); + +} +} +} diff --git a/src/integrator/cuda/cuda_weights.hpp b/src/integrator/cuda/cuda_weights.hpp new file mode 100644 index 000000000..c33a8ce9b --- /dev/null +++ b/src/integrator/cuda/cuda_weights.hpp @@ -0,0 +1,26 @@ +#pragma once +#include +#include +#include + +namespace GauXC { +namespace integrator { +namespace cuda { + +template +void partition_weights_cuda_SoA( XCWeightAlg weight_alg, + size_t npts, + size_t natoms, + const F* points_device, + const int32_t* iparent_device, + const F* dist_nearest_device, + const F* rab_device, + const F* atomic_coords_device, + F* weights_device, + F* dist_scratch_device, + cudaStream_t stream ); + + +} +} +} diff --git a/src/integrator/cuda/gauxc-cuda_integrator.cmake b/src/integrator/cuda/gauxc-cuda_integrator.cmake index 76dc105cf..9f6835167 100644 --- a/src/integrator/cuda/gauxc-cuda_integrator.cmake +++ b/src/integrator/cuda/gauxc-cuda_integrator.cmake @@ -33,6 +33,7 @@ find_package( MAGMA REQUIRED ) target_sources( gauxc PRIVATE cuda/collocation_device.cu cuda/xc_cuda_data.cxx cuda/xc_cuda_util.cxx + cuda/cuda_weights.cu ) target_compile_features( gauxc PRIVATE cuda_std_14 ) diff --git a/src/integrator/cuda/xc_cuda_util.cxx b/src/integrator/cuda/xc_cuda_util.cxx index 356b2a10c..1b3a1f380 100644 --- a/src/integrator/cuda/xc_cuda_util.cxx +++ b/src/integrator/cuda/xc_cuda_util.cxx @@ -1,19 +1,84 @@ #include +#include -//#include "cuda_weights.hpp" +#include "cuda_weights.hpp" #include "collocation_device.hpp" //#include "cuda_zmat.hpp" #include "integrator_common.hpp" //#include "blas.hpp" //#include "util.hpp" -#include namespace GauXC { namespace integrator::cuda { -//template -//void +using host_task_iterator = std::vector::iterator; + +template +using cuda_task_iterator = typename std::vector>::iterator; + +template +void process_batches_cuda_replicated_all_device( + XCWeightAlg weight_alg, + const functional_type& func, + XCCudaData& cuda_data, + cuda_task_iterator task_begin, + cuda_task_iterator task_end +) { + + + // Get batch statistics for batches to process + auto nbe_comparator = + []( const auto& a, const auto& b ){ return a.nbe < b.nbe; }; + auto npts_comparator = + []( const auto& a, const auto& b ){ return a.npts < b.npts; }; + auto nshells_comparator = + []( const auto& a, const auto& b ){ return a.nshells < b.nshells; }; + + auto [min_nbe_it, max_nbe_it] = + std::minmax_element( task_begin, task_end, nbe_comparator ); + auto [min_npts_it, max_npts_it] = + std::minmax_element( task_begin, task_end, npts_comparator ); + auto [min_nshells_it, max_nshells_it] = + std::minmax_element( task_begin, task_end, nshells_comparator ); + + const auto min_nbe = min_nbe_it->nbe; + const auto max_nbe = max_nbe_it->nbe; + const auto min_npts = min_npts_it->npts; + const auto max_npts = max_npts_it->npts; + const auto min_nshells = min_nshells_it->nshells; + const auto max_nshells = max_nshells_it->nshells; + + const size_t total_npts = + std::accumulate( task_begin, task_end, 0ul, + []( const auto& a, const auto& b ) { return a + b.npts; } ); + + + + // Aliases + cudaStream_t master_stream = *cuda_data.master_stream; + cublasHandle_t master_handle = *cuda_data.master_handle; + magma_queue_t master_queue = *cuda_data.master_magma_queue; + + const auto* rab_device = cuda_data.rab_device; + const auto* coords_device = cuda_data.coords_device; + const auto* points_device = cuda_data.points_device_buffer; + const auto* iparent_device = cuda_data.iparent_device_buffer; + const auto* dist_nearest_device = cuda_data.dist_nearest_buffer; + + auto* weights_device = cuda_data.weights_device_buffer; + auto* dist_scratch_device = cuda_data.dist_scratch_device; + + + + // Evaluate partition weights + partition_weights_cuda_SoA( weight_alg, total_npts, cuda_data.natoms, + points_device, iparent_device, dist_nearest_device, + rab_device, coords_device, weights_device, + dist_scratch_device, master_stream ); + + +} template @@ -82,6 +147,9 @@ void process_batches_cuda_replicated_p( cuda_data.generate_buffers( basis, task_it, tasks.end() ); // TODO Process the batches + process_batches_cuda_replicated_all_device( + weight_alg, func, cuda_data, tasks_device.begin(), tasks_device.end() + ); task_it = it; @@ -94,143 +162,6 @@ void process_batches_cuda_replicated_p( util::cuda_copy( 1, EXC, cuda_data.exc_device, "EXC D2H" ); util::cuda_copy( 1, NEL, cuda_data.nel_device, "NEL D2H" ); -#if 0 - partition_weights_cuda( weight_alg, mol, meta, tasks ); - const auto nbf = basis.nbf(); - - std::fill( VXC, VXC + size_t(nbf)*nbf, F(0.) ); - - size_t ntasks = tasks.size(); - for( size_t iT = 0; iT < ntasks; ++iT ) { - - auto& task = tasks[iT]; - - const size_t npts = task.points.size(); - const size_t nbe = task.nbe; - const size_t nshells = task.shell_list.size(); - - const F* points = task.points.data()->data(); - const F* weights = task.weights.data(); - const int32_t* shell_list = task.shell_list.data(); - - F* basis_eval = cuda_data.basis_eval.data(); - F* den_eval = cuda_data.den_scr.data(); - F* nbe_scr = cuda_data.nbe_scr.data(); - F* zmat = cuda_data.zmat.data(); - - F* eps = cuda_data.eps.data(); - F* gamma = cuda_data.gamma.data(); - F* vrho = cuda_data.vrho.data(); - F* vgamma = cuda_data.vgamma.data(); - - F* dbasis_x_eval = nullptr; - F* dbasis_y_eval = nullptr; - F* dbasis_z_eval = nullptr; - F* dden_x_eval = nullptr; - F* dden_y_eval = nullptr; - F* dden_z_eval = nullptr; - - if( n_deriv > 0 ) { - dbasis_x_eval = basis_eval + npts * nbe; - dbasis_y_eval = dbasis_x_eval + npts * nbe; - dbasis_z_eval = dbasis_y_eval + npts * nbe; - dden_x_eval = den_eval + npts; - dden_y_eval = dden_x_eval + npts; - dden_z_eval = dden_y_eval + npts; - } - - - // Get the submatrix map for batch - auto submat_map = gen_compressed_submat_map( basis, task.shell_list ); - - - // Evaluate Collocation Matrix - if( n_deriv == 1 ) - eval_collocation_deriv1( npts, nshells, nbe, points, basis, shell_list, - basis_eval, dbasis_x_eval, dbasis_y_eval, - dbasis_z_eval ); - else - eval_collocation( npts, nshells, nbe, points, basis, shell_list, basis_eval ); - - - // Extrat Submatrix - const F* den_ptr_use = P; - if( nbe != nbf ) { - detail::submat_set( nbf, nbf, nbe, nbe, P, nbf, nbe_scr, nbe, submat_map ); - den_ptr_use = nbe_scr; - } - - // Z = P * BF - GauXC::blas::gemm( 'N', 'N', nbe, npts, nbe, 1., den_ptr_use, nbe, - basis_eval, nbe, 0., zmat, nbe ); - - - // Evaluate the density - for( int32_t i = 0; i < npts; ++i ) { - - const size_t ioff = size_t(i) * nbe; - const F* zmat_i = zmat + ioff; - - den_eval[i] = - 2. * GauXC::blas::dot( nbe, basis_eval + ioff, 1, zmat_i, 1 ); - - if( n_deriv > 0 ) { - const F dx = - 4. * GauXC::blas::dot( nbe, dbasis_x_eval + ioff, 1, zmat_i, 1 ); - const F dy = - 4. * GauXC::blas::dot( nbe, dbasis_y_eval + ioff, 1, zmat_i, 1 ); - const F dz = - 4. * GauXC::blas::dot( nbe, dbasis_z_eval + ioff, 1, zmat_i, 1 ); - - dden_x_eval[i] = dx; - dden_y_eval[i] = dy; - dden_z_eval[i] = dz; - - gamma[i] = dx*dx + dy*dy + dz*dz; - } - - } - - - // Evaluate XC functional - if( func.is_gga() ) - func.eval_exc_vxc( npts, den_eval, gamma, eps, vrho, vgamma ); - else - func.eval_exc_vxc( npts, den_eval, eps, vrho ); - - // Scalar integrations - if( n_el ) - for( int32_t i = 0; i < npts; ++i ) *n_el += weights[i] * den_eval[i]; - - for( int32_t i = 0; i < npts; ++i ) *exc += weights[i] * den_eval[i] * eps[i]; - - - // Assemble Z - if( func.is_gga() ) - zmat_gga_cuda( npts, nbe, weights, vrho, vgamma, basis_eval, dbasis_x_eval, - dbasis_y_eval, dbasis_z_eval, dden_x_eval, dden_y_eval, - dden_z_eval, zmat ); - else - zmat_lda_cuda( npts, nbe, weights, vrho, basis_eval, zmat ); - - - // Update VXC - GauXC::blas::syr2k( 'L', 'N', nbe, npts, F(1.), basis_eval, - nbe, zmat, nbe, F(0.), nbe_scr, nbe ); - - detail::inc_by_submat( nbf, nbf, nbe, nbe, VXC, nbf, nbe_scr, nbe, - submat_map ); - } - - // Symmetrize - for( int32_t j = 0; j < nbf; ++j ) - for( int32_t i = j+1; i < nbf; ++i ) - VXC[ j + i*nbf ] = VXC[ i + j*nbf ]; - - //std::cout << std::scientific << std::setprecision(12); - //std::cout << "NEL = " << *n_el << std::endl; - -#endif } From e9f9449198806cea47b50695e844afe5be6e8c3d Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Wed, 2 Sep 2020 16:40:33 -0400 Subject: [PATCH 25/27] Added weights UTs --- include/gauxc/atom.hpp | 10 ++ include/gauxc/molecule.hpp | 4 + include/gauxc/molmeta.hpp | 8 + include/gauxc/named_type.hpp | 8 + tests/CMakeLists.txt | 1 + tests/ref_data/benzene_weights_ssf.bin | Bin 0 -> 958236 bytes tests/weights.cxx | 226 +++++++++++++++++++++++++ 7 files changed, 257 insertions(+) create mode 100644 tests/ref_data/benzene_weights_ssf.bin create mode 100644 tests/weights.cxx diff --git a/include/gauxc/atom.hpp b/include/gauxc/atom.hpp index 9336dca7e..06e869a1e 100644 --- a/include/gauxc/atom.hpp +++ b/include/gauxc/atom.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace GauXC { @@ -14,9 +15,18 @@ struct Atom { double y; double z; + Atom() = default; + Atom( AtomicNumber _Z, double _x, double _y, double _z ) : Z(_Z), x(_x), y(_y), z(_z) { } +#ifdef GAUXC_ENABLE_CEREAL + template + void serialize( Archive& ar ) { + ar( Z, x, y, z ); + } +#endif + }; inline bool operator==( const Atom& a1, const Atom& a2 ) { diff --git a/include/gauxc/molecule.hpp b/include/gauxc/molecule.hpp index e54ecc632..c55e3f1b0 100644 --- a/include/gauxc/molecule.hpp +++ b/include/gauxc/molecule.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace GauXC { class Molecule : public std::vector { @@ -17,6 +19,8 @@ class Molecule : public std::vector { Molecule( const Molecule& ) = default; Molecule( Molecule&& ) noexcept = default; + Molecule& operator=( const Molecule& other ) = default; + size_t natoms() const { return this->size(); } AtomicNumber maxZ() const { diff --git a/include/gauxc/molmeta.hpp b/include/gauxc/molmeta.hpp index 896f8d354..020c9ba36 100644 --- a/include/gauxc/molmeta.hpp +++ b/include/gauxc/molmeta.hpp @@ -30,6 +30,14 @@ class MolMeta { const auto& dist_nearest() const { return dist_nearest_; } auto& dist_nearest() { return dist_nearest_; } + +#ifdef GAUXC_ENABLE_CEREAL + template + void serialize( Archive& ar ) { + ar( natoms_, rab_, dist_nearest_ ); + } +#endif + }; } diff --git a/include/gauxc/named_type.hpp b/include/gauxc/named_type.hpp index 2670922b9..af97100f5 100644 --- a/include/gauxc/named_type.hpp +++ b/include/gauxc/named_type.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include namespace GauXC { namespace detail { @@ -29,6 +30,13 @@ class NamedType { constexpr T& get() { return value_; } constexpr T const& get() const {return value_; } +#ifdef GAUXC_ENABLE_CEREAL + template + void serialize( Archive& ar ) { + ar( value_ ); + } +#endif + private: T value_; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e7405acd2..568ead2a1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,6 +39,7 @@ add_executable( gauxc_test load_balancer_test.cxx xc_integrator.cxx collocation.cxx + weights.cxx standards.cxx ) target_link_libraries( gauxc_test PUBLIC gauxc gauxc_catch2 ) diff --git a/tests/ref_data/benzene_weights_ssf.bin b/tests/ref_data/benzene_weights_ssf.bin new file mode 100644 index 0000000000000000000000000000000000000000..064d9f2f12e5a215637fa3b8f64911c6989db91c GIT binary patch literal 958236 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 >Vfxm(K~+3-tTiEPt+U*KOD7LF4KSf0oSI zy8TLjnwMnwQM}_nJYf%nHKW+qj<7+q?ti8ncmc@8G5LI#~=NOMJgkvHxc`{o7_Aj-|_fUFSBg* z>)#aLhP50!@b3)=eYa8yEsuTwnI!t0l77E0pVxQ&4^Q`HrZh&o6m>FVY#D zshZwEOm2-Y@|m2+<2j4Nz;hOdf#)m^0|!_>00&q;00&q;00&q;00&q;00&q;00&q; z00&q;z@O0-I4*hjzKNep40>hK-`~@mzDm$lv&M%w}>ZkK`LzQo>1C}?D{j1+9%(v%wdi}Y*$Ks^El82R^ zzjg#a=IJJDy63FQ`%D&QhW&ckp2E`&-yU->yW2>vzjj+S!bSZ3qe*i|Og5(Wao~-I zsT$Lg_}|MN+3k~c>l4}c&xm9wCh&BJQepXm>Z|5J(L>&oQ9l5I^ypNNji(}Cj_1#yVm>-!`uY~EH!Zb%yq zy(gQ&A@Ei_&&UgB~T{pCuzhA_YV?VDQ@le!2?5kxiubv#tA)Z{n@r3Z=uSQ~N@p;+n zvJejOWZnNq*LR2Y{JsApBa%I`Ls4XpB03VuN_Mu8hE*vVqM?nJk_stp5|U7!iIiEH zh3fUnDk|C8zvKG$c)s4hyX*7sxt`Z~-shg@-1p<$$9=-#^1e0f^4Ey=-IHPj?DLh@ zzkHp#vX=eoHMB+Ep;+<1;KTLQU8f7QPKx8;+tc9jDVvVS-n$2%?}leze|qAKfb)ex z{Ou!s&b()XGar6Bm=G`EJQCIQ_4owt|4+utKQt;!6mWjgSdh0`Z~h17(4tG~n~5g{ zoP(%76i8tiWsh%LuK;?z}5|tN~@9rvB`=$erig-wTpR{$*jz3vZB7PF`ZPDuS1byFV z5pN0k>T8&e_`36ih|h#vtnakzd)+&ZQMr7*F!j%#D+!FsubGvv+iU+5L` zZ1^wq3iUPo7y5vD9MuQZ@2Eb&F4i7l6V4B=Vs^#BuYat`6OeaRYp|o}SJvtyo~H|_ zC&KOqCEwZJU{~m%IBJ_u4N;K|J>4}{V9feoKoo%^~%9D zY`or`*_Gx=4D~qkZ5y_lwX9~(x1`+5ejm?J2e1F6cGJ23EerCT;BI|9mZ1)=vt7;9g#Y1_N^yPr2i+;4)%S--i(HB9%UzrvTV z*BNjuMv1=$4oRtIv(|mA$m)De#1Yj=2J1dJ^p3T=cRulm(Nz&gWIyv(#{~!9vh(}O zPs$q$jreA;Z=6M^j$1uB>7(Lwz$dM69WGZfx^+%+q@inSxg? zQ^X@xW{ti-KBtmBSo5g2vh_s~hgR6OWn4AiZx^oqx%_BD#yUu8GSXbMTz`x;+0YxxABAM=OGuwHT^GR zl9Hm%iQgj*)`f(tw!eN}97o)=%d@=Hx?_d_d@S_&WUWt+gYWVM;FO@xC*pISXs6u} zfNO$2+XSg!4~V`l00#wq{+;48A}cLN0A2<9hByd(3+xMV5O^5aBjTWlL--$Y5c`CF zS)L~8K9|JxVqXzhyv@XLWR^INeMszy<(VOJ+2T0%EpopmpELEoDvl>K>~mTcd%dq3 zd`+PC)vxE;isn6z3dmbhKZCp_)&)NUJQeKSv2uS-^QvqCcq-WY>Wr0b_Bve=kgtZl z`^tsS$zOL_K;94bKG19Ax01aX0`j8ZGyE9xqL2&x0P6n5g=?FSRD8=8NQ_)w9?cU_ zmxP|c@1gz(J%Qgt{SkTszlZu1^aOqn^(pWHeh>92@Bw}g^(pWHeh+p5|L?JKz?qgx zc?{$5Z(h~o!rSrpB{2^FmUq{5^2_KPhWZrv3I9p`|LZzmE@{G<=o!+xwJD;4lUwui2H+KKI=AmXCEAp&$nDQ=D#IY--X{#>& zSjW2V>2{&?gdpPBH(2#Ex4rm*xfooZ^F;ruh!duK08W_l0XSjG2jGM$AAl34d;q?e z@&Wi>$_La1s9vFt0J%f2P1U z&li|jpKaK;q$*sHmfyd`P|tKTPe5z5Q6B zc05VIJrU^DgA-XQ&grKF+!KLb$q&8k<>iqo;GPKd>Rz?qqBir-3b-c%y>j%GQ`o`M z1l$vWUbTxb{N%qNy^-IBzry_y=v&~dvf6R`G6dWofj&Imc1hW!_L6}6BOMy_A$fPv zOEsq~0ry9s4-S`qby(jsTfqGh=!4buac;K{TorJC1a=4hP`d+vsNI1-)b79^YIood zwL9>K+8y{n?E-wDb^$(6y8s`kU4Rc%&k^6Lo?|}%d@uAI`zzqLvELr0)>OAjC%4i5 z{D;^6`Q>_f0<{b5->6++zemqa`85Zw=+8-K^xQP(T$Q{=pEO4I1K4lV^VFy(7F+zf zo^5mCG5}Xepu(C#jIFcbGmQjz(tT=FZrgI1l-fXx{{WZRh6u|AmE-3 z)-^jgX?J#YTBCh3^os72p;vSt3;m({Sm+P-bt8NCZ$7_cwRmn4ab*qp0rbrPt_=G_ zj00DO{UOF_JjFhl#$W8yXuQTgjh=UW=J(U7{*x=>|FLhsH1n%U?X7Df{vZ4H6z{Ui z3&K@K_X*hV(LAL1eFI*d&JVsN1Xo^{{&*|$joKId3AHcyA8KFFLuy~pLuy~pLuy~p zLuy~pLuy~pLuy~pLz+iI{)*Jvjh@%AFQVr=?2G7m5c?u} ze#E|rp2x8-qUU$)i|BbD`y%K={*Li$dev8n`hf9zrK}DL@{PwcRJyOfsb1OW`>2PZ zogG%b5yxqs6M0LT7e$_v=24MfrFmWCV`&~3`CFQAhuzV9JNzomGb2At^C#Fhia1H) zXMmHWeg-&6>SyRV6nO^VrICk+Ujkkld3cNiFO57r#(|ec9tq>XOCyhjap0wqN5VMp z(#XSL9C&HuVK5H7H1aSQ2VNR^7>olijXVs-foGzc-rqD-g$l@7kc@@LOH?4x_j=@RQ=Zz)yf4N>=n>&vf>eRriv@QAjK>FbcLtPvAmL8e& zFZR`W%1{Rf9_IQVgBdwzpEK0WfuHe8`f{muemO&(9eA4ygqK20sN$VfW6RpU@!E)uovJuKqu^lt_$`;`2c&Ne1pAEKEqys zV}V>KPHeoUU&sTun~dVbR0sE+b%gg%pg6I_N5+Z1rUi`R#0K>0|6t6LB1Umy^&?cg zIx9V36em{p<@Ksh-ybrH6SGRn8sPZ*F{3!Kw)@QbTBwwWdQ0^mdJCKn^dEXl^`GL+ z1{aseAG6|kd4e}vSGje0ua~8a;>{G7R96Y?)w^Ebi`tZIs-svv#EY7iN%khEaUGFiI`-VCG z;96V|Gtb^{A&%oc_wGL(R2@DnHDg^FA8+_C z-t|tdzf`-WlHbQX_kZt+;~j9&L*Hiy{q%iyte3ve?vygpkxk!)mqt0I#dDk|eb+wg?82op+jCqc zc`qL8rSG9*z4SeFte3urj`h;_(1oEtH6oQ;X47}=t>?}8Xw{SVHz4oSXJsq9ua@Vy zQ1VWF*S)=8XxDRGI(etQbJDeT@2WW8$UF7WH>1=MpDSuhS?Gv^?}vX1WnQM$s%G}G z`?G8FwRxs7Hpm&?>z2c=XA+uHJg%c2lh zA-B%=r=_dxziJ(AmS-9J2s`&BeCV;8-}{@paKP!;5_VvdV13>GW~jHsQ+ z>G$6C*hG>gVF7(o4p;>q5Pp8M?r7p5LC;mGDA`B$-zHa*mE=ab3;Wk!4Q<26aUbesMdjil@_R-8 z+n48eh&Uk;`5*JfZ0z*_+4u6_H~-4J>L<=`Zf;TV{ey&Ie$WHG0zIG~dIkC+N2NEP z9vam&mZR2=j*ii98p}~RZerxaipFvbdz;(tVVO*h;6LaD|Am9c&kymCt!tdidGpB8 zp~8nxXRcg3!twAuO)F>IlQ7T0y&aY6mJ6HO)ZY6U!TG5;`J;6<*V~_~S1SIVFHEp1oh=YM&-gjJ;y)?xXU+vA_9Gt5)}#O>3>ClaP{OwYM_m#IST)kS^_`8;cDi--H> zO4z#R)rG@fEE5#>oGLpX%I)q%vZWgTZ`%Qz4=IM~3i~HX79Eb{_SO2%zE@HSQ@G1K zV+WWEmoKlIr^?5*=GA21dMaU^i)Y(8T3L#8LcX88dzm&f+$7`{J8wJ8=f^nc_v;zB ze0UhQ>ov~~`M;2`4N6a6?$epa*oAfDKZSEXl;re{yDedWPbcj-oiIa4>~&*)OD;$J*Wl*IEmrE2+4t#*fe-&fPt~w{EA#A3gxNP{ZBONT zg71&A*-BI6R*L+|=z7^?%u5NAgp7^Y5!Y9s|2^t+?01f;aU*?$o?uFP#Hc>Be$+jx>y!-w=7+u|x`u?L-mwt* zul45i08>s6^b`E=5Eh-H^-MNS?F)7axp=>yFW;?L_C3{SlMeIBAN5!(+As8KSW(xT z+2QL2{1@_-r1khHRCW-sF8Pkl%J<|+7+n|mhILUs+m4Gacrt97s3-EJ8jkZgpP?s9 z^KN^&{YS6B2h3wMC1rQoV+m{CZtFQ|#S})z757ZMnLBP{qxs91c*K{qG7+d8fB8k8 z3(%Uw0hj-0@32$whw35x68>v-cEYNA++X3puv7R$@Dq9hzX3ggytL#(A8fuWlNaWP zp9J3^m(=Ccht1;t2mFEkdMjvM?8D*h@s z`3!pppFt14zp$u}rFOKb$bZ-=_>cI4?{D>V==k>d0@043Z}6+%bv?9ab3K`oK6$2u zuQz&Iy29Uf#_UCsS8sv)bIjBAYLnB_rR$igPgrp1LD_i7_nwpO>$b4DT^4>g!sqcG z{H5~Q9SN(*(%Z15lQp|Jm%Tp6{hn67Wz!a1KaYP;KJswGMphM?_VOC{lUlbwevISx zb?foKBSsF3nZugjg8tza+4WbyzJ0>u$ke`ZZG$p*u$hvFoqltAybtFTU*&Y3S*dt` zfTD@`U(AF5UNl{Mzn0UPKk`Fe692ENrfKY!9-Eo)pBuNpa<*HZeblYl=CO! zntx?5=iBs=_a49VFcS8g%5iP)?&tm%|GlQG_v01kPhIno4lAo9%s6k9_TXtd zgjj<~6E6PSKUB?jZvR8VBCofXn?1;0&7zs(kI__~oy7GA^Oq`UzYRRRO*nPsn0!b6y+Ow-ES2}G-F0T*q?0@L z2o@K<+r<6bj}2cEy1Z7x^e2z@elpueP;mcwa}C$eZ1paMa@7)MR3E-qy~I%f{Z_*= z$IbpKVVECuf}YKHGKZ$!br$C-{%bhV?k|snwIj@Im8}JTEnk&MoDUes{NQJYZ}yVu zpCxRG;Vl&<48M_osV;KYYY9s*tZ`J#*J?CRPeZx%+fMUEy8}H~7xc6Kn}UyXGbwXv z{f%DX&7K^_DmK{!paNhUD@M0_6|*O!Z1B=H9$E-s2nDMf#Y} z`&I3CFsh&4j+-7wKec95&rcZccOF(?%c$LjD`)g-Jz)i-_Cm*Dr~QMQ_tf>X6z7M% zU+o$FeA*Z@2783POprFQd8W6PQNIzpKReNT-d0BK`uFY@X~)CYG3qzy{IGYd*II7G znFdncvaed(;&`9f`1QLr`T3<|lZzds(jay_ z+T?q{KmXv8zAN>ugoWkXbzIllfqA^naq&*z=R50BYwll?jhA|S3Cc<0aqaWQZg0=X z=+yHQmN=c_{;*7^RjYUzow2R@JbH6l7B6B{TKlJ0x`^~gtvGc17LT)_U$c)>7yn~C z??J}n7cWeh$mhqphDCV<)-DTTewkmxaydO1Zx^`fw?md6`%vVu|01U!<9plnHt3_j zm#sgaeYD2>NsH43jS8dIdg!{xuhlR+Qq-0fQ$^+%59icyfJD2kxIiC)QP2>`{7x>p$qc{N3&GLaw)TethpeJm<9SyT<%C z$UK#BmG3J+r$-CJd$BzJR(@%&zKZ+#x+{I`x9a<`XRQWih445YR-L-#2#>Q?p_Y%2 zwhdw)R?az>xt>ry+;o4Oygjk;Jl!pHv>*J7`zx*A7+&V*T$hPcqPDrn*4v`ry8zYN!??v2nY>`{*|Ybqn$7c0qP-{YOPd?T=?^KLoy+%W=A-i+7QNWZ zSz z@6N8a;C83xcVgo+PAB-_QF$Z?}++WqLc0Fvt9ENULc{nh`{f7sF6PsaOS_4Hutk68Cg<@QMV20h33y=Phdyj|Sb&l^5{ zmR-{^n882OI0FAn;|SUm&P^3M;ZrVkCYFv--I8Z-HrHu75|0*Z}PYK#ApBZnGJeU)av}pbnZ8(zAbGY z^sSPgQ>eZ%xpuQW|D7Xsh3X078nqYLsriuH^59z%wB^EjpaG(2M827Yd8 zs87vK(->&O`EMRs7kqiDtS(lh;_&M<_hU-;A{6q6NX0tZrl@U?mh*j7v3jK~Cy1+NOuAh%PxK#Yh|1>q2pwvMs)&Ym@&Qq1+`xg3N$SdDjv(+sv zDMK9!(tts8_DY zFYV&Z?Xk&+^)|X)q_TS7VV4hH+z-oZEK{mimx}d4>sM_Y7jb?XySXIFPm_xE#0fJO zyr|@UqsbGOVb9h{#rh-khyM3Vw!Uc~--pouK6xMG9r$k_LjGGC)1yf)*F*Z>u2b%Z zj^y#N(SIB6@2fpI^!)rR-mg$G^+n}NGY0=4=@etPyN{IhTzkwY`;)D>k5W3EV^ORj zWjV7HrtGj-!w@gR&eU9Kq9;lEXuZ^3yAazQ=>=PB?X<2X-&&lpF%2K|TAueEb1wPf(mO&a*K zcJjUGMcq0J*taAUn60=sP|E7n!~48{Yb(Hx681gu9Xe7*C&s-cXA9^*p=BDzBCFTOoS(I-$XHmZ4 zoJIMDeGB*iI&sdT{G@V+zKQ#d2Dv}FUbMhJV2gNO0R6BF#8c?uNP~@Go?2D{wL9o1 z;vLmD@ISRx;<|aZGX7&+{;j{tz>se+MgHMy)c4s4*gt^(qt}Kly?0}$KBW3muc5-#AtQq$C7@zX(#F-r{9R-}Tz@Hoa7j=m2x<){r0so!iup;Bu z4l6;-FE#j$r8;-psDIheh@a3O@WEn4k!u}un0R5zVxISs?mFn?37`0RAIgJ;v3o2j8k;bX1-~ayVl{ah%3Y#2p%E;s2>$LcFGa z34WFO8Q2l^=ZI@GjzFKO-olU3c!W4k?HB$H`Ud+_VQq#6KeuAkPr@$feg<}pd=d3~ z)E~l6(tWtYiKBb+w*)inW3iuKuq?2^rg)b)j(w(*TDS6pJpvg$FZ?vLxY8oWozeYy zH~;Dd12h~N-Jd%s7@f2k>c;4KWc;7O?t9idGwg3EJ@_8yf-WAKhO>@)GVl}UxT{g> zSwB7oi{m)QrOD+#>sN6=9LG6sz{JI|d#8Gd<2WxgF^K+sx8ojh9Os2;3zo|{mh6>{ zH=Gw7TdU-n4)7ECgLBQux;@(I9S$&@r*IAe|LJ+5O~;p#D^qr{o#edG`c8I@`~i1H z&qv?`Jx}4k^n8)DI#hF5CtpU-7sl5g^)h=AAkv9*Us^@vgEv=v#Q);_2zlYZI6p!k z=y?R|qUS{D13jl;z4V*{`sq0Z^h3`n{q!6M{e-?zJum;#TDa)T^Lx?Te)Jq7Vb)KK z^pA}V6(T&F2+KHrAguMy9U=O>&iHO-)-f9waeQRcQ+8htOW3Na^||w6gM^{uoW>P% z{AyS?uM-A4Bur;z#8maqE<#}PhJoAt`TVU4b7PltyxA9(W!lR;1d9=a&QIb0)#~i* z@ofUft@S>BDACVXcrtvH@_Ktde&xQM-F6v0w~w|NR_Ma>KNoJ6yLfWEXlYo9(w{)_ zzvh<@&3k`{<0k{Iw|uzDN9bYD`hmw@ejW^&+-e2?9{>HOaKU#g$BW{7&^aN^>+mj4 zXPwETVFj~g^Y`)lJazdl{{0C(!$4~po!i$MIk)oW^xP;NUl=N3ww{mIx9=C`YzyH02{5nTdXo3Y$M@g^=I?nv`laH8gCgHR|I6{yo=zVaAmG24 zXY_t0>-XBO0{8>^Qw)2L&z~f) zI%cnM&cRq)oAbx}Zd)@aI|&PaxWsf|ppWnxNc;%IP&$eU0Ip^0Mi?$4ru)AZt zmTpP1V8AVB_#Eur)s_EutewugU>gR!bd=KEDPQ?LlXs5pirUqiMO^X7__(H>0wnCx zAA@x($`&)=bt@fM<0S%nKncq~so1wDN{a#ioN?p8HD4!=A8Y%? zKm2q4yS7&*Z~;0rvxK^;lWE%b!ty8~3Q&gw5Xr8TE(v zN;HaN&G#_GLHP5xt&4l4mzeK=s~;#-|%0=JMf>zU&xEbBhXLd2>1;CApSxw zz~KO=*JYQa=+M!f0&q&e^|g(BbV=#*9uYT%`bE%>%sV+>cZ&ET;26UkTl}sYX)DeH zTp#Aae}OZ^e}OLouJ5Gw_D-KWIEr{4;4n?bOsv?~e5;7(0lx9W>$;@#7dMOeDA2i} z?6k+uAvOZ$$M={YxFGx&^8nWb`j_{paG9*IQ=oXlF8Z@K@9k+X;=%CWk?Urt-X7;7 z!>2XiCzUnEcUSVT7BD~fjQN3g0{#%|1)d6c#np=4Ul^_0B>)En{Nd$E&ELmbZxeuP z0)FysNa*2Rx_09Cz+uwwfmg)$_%HB^_%CpGkR#Rw+!}C_(1)7V%_gPtdpfAM;vD*D zQn{VOeqJ|hsAnP1Fk|;1r_z7>!-l#%@+}xgeGPfPzaNHeT+8nt0snyW_{GS+e_HZ! z;9d|Hmf5Nl4B+>YfV;r>g}Md5M{qh(2S?sgbFRg4f4<^diF^MlX8J7FArfIcAK74hO?=@`C0 z2kruSp}$`~-X6*K+o(q)e+4~69T|BH=nvL~Iu6wb)Pq1LzDIrod;<;&^i%mF?}2rp zz6?Fbe}V79Jd{r4Z!r$s5x$3fVULh6_=7q-^8An&@D-33|)FVADKXZjYD;e4~1Wd>!lvI1uF9 zK|gRTlz#Xb)Wt9la2lA0`V-_2p+CS&z&}7P@T483%w;7P-#r-PfC+r3II`A2MfL}sA=*90wPf2yC5FV#cDHOK{ax3;0q2zv+4kn#sO9QbXl7jY2z zXUq?r3G&9Bo4#V|JRVWGV74$z|9D4h|)`dD0 z^aTDNaSiK2{DK^@F6bfR7wiar32`6agAecz_#S!%yTiE~_oI=&#W@;$Lp~Y#7TgO) zz7%;3+^0sK7W3o&GUmtm8u!GJhetjE_tTO8M7{<0(~&Pleg^l`kzd7mAM+#s5BhPh z9P}fffb}A;jywtOxg+0;yb9Kf{4nwf;3x9T$OB-07Q9|LC6p{)5lxp8!51KM?R?;>P_7QZXNnJV3^( zq=$of-5L2?oYRdpjrIledOGse$nz*^_x;tBe~aZ zD(2^rhf&gK6ML5T3qzh6d4rL@KQ}*~BpXK_Da^=TYR2(k$PXhwld$r&hxtsYnD52+ z-ebFNR2(f8>jlVH6b5ukIk!wkKk_O8ZVRRa_LPcwW-%|c>%xaNW>PV448DcE>)x%D z>(o$e$yRq^0`IwiU2lWWZOYQku z&50b>f&U^u2Kjygl>{cs!a{1P%~+Md&%|iO6R{&r$b5 z-V*kO`W?*!17C-{H~k*@bNXM*5C4NYAo92Hx2SI-FAYC~|I++3aD6oY3|t@0KLgi? z{4@P8>H_e4sOw=}@MHKd^8AQjsJkIQ4F3jtX#OAcApZ~lk9sclA&9f6&te>Y335k$ zgz5?EDRfeTq2>JRu# z>4E-GKZE=&^;gKR!tUU2X?_*97QJ#n{RTFOzDH*-Z|1jjQ= z6qWk@=J%U^Cj|7|u`rC){K!AOi`P{RlzYT9?ImT0>{&~-AtB5nY-eCJzjtG9;3FvU zd(iy`PFNH7Gnx4eiktnfUrn+3k?JYdQf8k`EPGXVvT_HA{e>%VGQ8o$TgC&6%pTnzgz7qKlr zndc|JcGF7b_nPtjxR3f*2XS0|*y$U))j!C-Z!vvg-oN{a#Xr7<+VT5qSg#kmS?J7h z)RLD4nI5lY>pkywq5jUld+Yvs-*gsBndhK__^fHinfN{759X)*>>jFow0jy~7aR7m zn&XxOAI8gjkB(-W%2eH-UEuxD)=u^O#qq;^4+i#~Ga-)c(J)QvpULx_VQfs-2~xIq z=xvp?x09Gh%TI4VW^?+R>{MMohwqEngJiA!|?$Aq4!svoO(jU!(jiJ z+_8z_<8ATm??Qds>3Onq<;pc(m#rCpxY75lWM0A8DJR8oqb4VpywT|FjY04 z+Y8mVFi*3sGx@!eM*4RAhrRJ2X(N3r)Ew6-nd6+PUKMZu9IeFrAwaLdhmmS`e_Y`> zBJcrxF0Q^3F)&sl@>%+IndjK6us%j2`c?HI8~bTo<9^_S_sX1Pj=N~^S9a6C7b<4) z`E6gMb~+~!{ll2jd1GF1e^{@a{k@v&3H-y=iT$2l<$h`8q2CA1F3QHCp9a6yZ+N9E zZKR*ZuV#!_93~a@)8xR4Tan!F(Rcy-GEW;3kup*u+E=ORlFjzqZ>xP>+Bb{ih~UQ# zx7^}7hu@zvUVq}Ho{?-_Rkz2tzsqq`)Q{EZ?GUcdBsqWni(tMr-mUU#gF|A>$F_wQ-Ba@-N(qiOyA>2JB;HXqVr&TMY) zo_qYg_FV~O;1B&Ce4zgPW~EtaHTO$Y?y769EU)2x6sg=HU+Vv%Z(5NLOq=eJiu$Hl zs*_XA{e1o*-DSFyC2ZU6BiDQAMzRvUp@|CI&rto@Z`dMgDfcr}PoTHn^6JCR@$)zBv5>6%ML`r#iC*LDn7$P5VxV;@%Z6n5?8 z`kB+IeXjvBKCBwn=5pj+u0JCm-7#~Q*(3G?R1Xn!tn~dQ0U9y`}u8d>C2OPSXA7Q85mJKTq%WSv7iejBFoB_C<&b z)PAv_r28xE-{^jvf=eJ|ZFK`vCjtq0wI|CR4UAYa5~g)dDP z1^uUfhdAyPZuBFL`!RfvedY?2FPTIB(+3s%gr9u|&g=Xi90m3j>-DE-F4--c2m4;= z74=Kg*DvrL!2JOJ3p(`<4jANhPBsoY^J*XLvEgwU--FICgGXf5UY3o6&V`$r4sCf= zHV!)9O?^4_+6~z_=!D$qc@6QGo`Z0Xqvt-s=Kao2JWd&mvYAx;xPo<2Zqm%mD^76w zeb!2Y$7|zRKl>>9RInEJ-yh4=j1f#sY?~kBcn?XR0{t+(3buXG^FD8dL{N7u0UB>bhet-Gk@N4<(ggBm`ay@&W7r(FiW~{~7+$dp&V_DGz zzAo?iHG7vtOWFI+b!po>MF=6WR}R*4J)!jbcN^EN72j`H-Zou2VRQv!+UpaO%u|I2 ze%7sP_<2n}Z`v81-%>WRmGP#S&&fi~;8Z(Df$x|62l{-tD`gK@Nd4j?@xuNeB`UF( zxW77WQ}I1U%4XfnDO`3fMtE=Z;fOWYD>c(WvHh=BuKph>^%HtxtW+|r+hZwv8u@&L_QgY@eAUj`Zx}IjVxiyWxS)O~Cio`bS5W@6YOkMA&gDh*0sjp!@bywXSi%0r%@2=Jk?EDv%Hy{y zUsN!Sn>X~9AB`7o=8Ru5C7jD8((iP!C%^wb^OF3<=tF{Qf@95Mp07x)Rok*DmDf`a zZ^-QMC{oCJv*DZy&%>zs1-AZME@f@HWUAz>KQ7EzKIB+D$03A0c$V9wTLoJXv9_x8 z+6iH{RnMRjp68KwntP>uK?Up6$;s+&V1jTg&v(&z?kDR^Mh#OuRKcFPTwHzP;Yp#r z|E{2M+)nF%M4kIpQNa#+m#n*YGga`oQ8TdQ-+rFZc~_FS?;79FQ#yl>3pYOVxI^h| zJ>thx>obk%lSv&pQLo^{)m6`g4+e96MAmH>6@uEjn_faT9>`E zJ1yFUm3c(1LLjdP&GItZ5|AYN89EO8qVwRqY_MR@rMLYm*bYOtYw>>rMg6IN>u@Pr zu7br(>D#S;oEw8a)D3;@YkNz|dOLXCJYdJ~RW;n-&tKF-^>Y}{|2V6vFFm<~VgF;? zdTqPY1ya_1#h}O=p{@-3G5OG_ka0y)mh>SedD^Bm;=WETWk>9aqg)@F+ph@lTg#-5r8TKazz4{d>)JfTIgW^ozCR-WKgIXaP#aGrSGZR!Ej_v3nW!=)8w4X%n5zjkXEj%=QFG*SAlEv%Br*34} zk751~XGR+=Sh1GjyoT@3p4=C7K6{^lxQX@prDptf-4-ZdA4TOw^#tq1{));4@fv&< z%04A!zdtD8oKE>e$LAd0H{+Q60RiVGDo4ck4wplgJ4RG6<*7pd>Lf=2`}5+@d((%` zt6<$sLd_Gt?hxqrORoEGH$7?1==Vo%+_lxK+r)5w0Ux4fT`DSw4;65J!FmTjGRu@d z8YEypPxTPzi;SdwRUZ{9*qQU5D!vw`Vx9#2QU1Ab#Gv838qJe)?^x#$!+=J7a2n8Q z^Mit50q3u^NTMas-d+k_-cpn6QpkCc=Sr+%ts4HPSqlInH7wsC4n;(ll7xqBb z9{_m_@Q41_$jvQ!8utU3ALm)SIE=C z&m=wf%nrWqEg&BPK0u$5|AGIh3w!iAW!_$a@)LGJ{iNIh!{I~Mg$UG7BEC>R345ph z5OgAcMd`tL7W~wYj%a)5&rU%+&o=nU-s;QCk1vt+4}n~?Bkpd~*cB|$xBz>E91+*7 zelB`ank4IEfH-L5^z3q*iSCW!ruW}*TQ;bA$;Nk|6*^6?V8b=GgDRw$8E&`3GkPD5=_%Dr9@NdXV6gTiwp?uJdHwyv;>VM$psl1>ksE>dT z>kLaTy4`Z;_kjMdjsw2g#;6Kr2fPF-m)+^x{{%hs6Hu4LzAof{H&EG@BW6!-s^ULjtfK8SrtRIyF=>~q@%s-M_DpngsFQK%bY--Ue? z{T}-$`d{pS=(@1~r+R|@Kk`>p?#Ophxg$SD<&JzBl{@lpRPM;vQMuD|2li?7+<|=> za0^ub={X4UMcl!9)kaU~Z1Xcf#2ZjKQaywp!#v4M!QxS2%SwZGHcK7^CN9@9TQiEc?th;SD=-tC(4C_L^CGcTWd*g-S40#^pi~1N94DfK4;RKMk*_dUt_pn0Xv5{=e=i6Gn%i%dTG86{GoXs@RQ~t=YAZ__9TWfH|rP! zAE-Wn56F{1e`wwhdP4JrkUPyQLcTN)3Hj2zS(?T0Ro&NzFq$`Wxm4U?TSz#gd9wv7 zUkyL#MKPK;i%bshtau`Z(Y%@2fb-$Mtm7HYo2dqV@_vw(%xK>1V!+pLuO_52nl}TT zG;anvY2FNU(!3eyq1|`<0cK z$~L-2){z2>y6IBLMh8{QR_S3B<0-#c7bHmO&3#!#N; zSG&1Qy_1B2o&viAw)rOfznyH8Kkq1Gty3QR{m70I0`uj*U7E!6q7CmzV!Va=w?M~* zJRjS&z554^a^@pR9JPJX388dCu~)(nKHl&irMmpg+|;NTVU23ZJB7`%cR$|;FSePm zHc~J>nGi8^iR}B`Nv0RSc&7;4Did<8r}O`IIHH`KS;kabPMjY1I9^;A-X~4(DIK;( zAzpa!^pnzHJHFn$m4*S9>W(dqg}ps z)9@9tcQ7$eOn`-7@=BR}G0$xi?a>!D%J{#~ZTs^o`D8)!KvJ*FZ8H9YPOKO2nSwu% zBi>iVJXjar<-E9H(vw|g(IP+bj_33THZ^B{#tM)ln z+fixP%EbK4v(%I6V@C7)$i08qy&GLFj;}9kW!rQb=Yyt7Z_QBI__%IowC{4cTkTMo zVN@#{ull95{{q*adcA?YzV?yz@z^x%!@zPb-$JeOPTdU3SI__#;ws$&tA{pUq~D^Cb^E^t}N#D6g|7|-c9+t7%hhMzESMaXdM6ba(dQs zsXmu)o_Fkk)8*oSEvz~eeILu$yHtO%w^6zH-&5%}t-bnly`9^nfRiH4knrL!gF`-1!% z@hxiO(vRIzd>Q34_|U5Gd``+NZeMQibQT{eW$l~CT%O-2n7uHs?EQoLD=YIc%*M1# zq_e+yN1?eT=T9&9Ejjt6;=1VfYi~?=ocn$!qx=Ltl>R~&9q*Z){TN;E_psKB^iBsb zsy`#}>aFxk4jb*27TgP3C+q7ab*y=Kadq5VF_rV-{a9`N*fO@{dq7yHH@lhp+o^XQM)7_H{kN=o*Sn0pQ2X%g#vwPR+dTfA z^FRA~zu%~>M=7(tsQ%WwyCW8F(dKl9FKQQ?F|JgzOeoN#G?%rA|)+hckRk9Pst2ZPnl$j1Af z-(eZYDS;&|E7xJZaLcVleV-Bl^X>l9~_<(y-!OE6J-#88#e8WAg z0{u7JDtP@Je8xR2IuGv0S!@Z^_q)R5enWpe+=qHJVb_KO8Qh5#-}OZ!n12uZ!acrM8^8FKalXO6aPMznV6xeA&NtW> z?g{!&9e$BPN4N+<4J-W;g4=FrgRbBt09lsX?`vRR* zF5olOH^`UjC*(`z2)j7fOy#n`ab>hm+Ro*lmZfw0X`edxw>{?0ujF^tZ(QuW`D);)6Lp9DUw!Uu@K$5t>3i*N2AwoVe$;xadh6A8wDB2lwV)E&Fr5 z_#f`8;d|V7%PJL?#GRMTk9%#ZixQ{l@_A_AxoyEcFR$VAU|qQP2l{CrJgk@YqXU0v zzrF~U50?&coHO`|`+8=oa($2eyJytUZxi?F97@}{kLUbBe@@)LJJSA+P8ru{^z+0$ zKJbC|qXQpk|2pu2_LuAaC{hy1`35=SUL^E^_Q(75I`NDPw>$J5#XZfX&RgCm{ky-@ z(8m?`^57rP_n^s@WF@&#?gI1>{T%SV4D1^CPrP3PyT&-)yMbL}9Pi^`e)I>x{IGZQ zqqrSok|#fq_d9LyOXx=t&?n`9Rp0>u_KtoO)xO2k<|uj#uy^#4u*~zxi$2Edo(=vJ z{TY7f&5PZy>?goZ(GS7z$?TiIW&{ebQ}lVLaUU|&@>GZbKY(?;tf*W(M1HRTzkzYQ z3j;rcal8uy|Bt>Fc=reXAAK$G{tozqeh!pB=>I_ZgMJZ|Kj=FF{vb}F55v}$2V53~ zxC+n{@C|x`c!z!tt8bafr6+j`h|AC)ylaDXfuC3x+B`VNWTH}o?^ zUmfs)_OAmUXg@dbf%YxLdTD<=%uoAhVSd^_3iH#xaG0O=MWlQ~-zu-E<;nrKxLg|E zZA8DUUUyUWd+~Tp`<`JOedN#&ZNkKD^#y$YO#4S|^1IP~K9@WCCZV5Fa><;o=ea&Z z-_Q>V^U%I?_%G-||GMrEe-B&1asKFghCWvp!!*yG{HC{>7NPxg#J_gM4u@1D}#LDAJA_K zw)!;a9;4Sa?@qK_HoK|eI~lZ5`${-v-N^p}IZz`kgIRPYCS zi~eTd58?v)yltC&&FwsoN3{RyyhC3*U*zXO^v6R#Kv z|Azbn?MEl(RY*TN|h@BK|;ScF7ZLd=(1SjH7)Ku`^yR}mv`--ZOZ)X99|%5!e&nyfPsjy!NBi%=?$Cz~`E&R^ z+9wVE7X9UDezi%%`^Ctw!k^H-dGIIb*M~lJ@Jr~YhkQHzFTRJL$A3|Gfd8TW11UYA zlhT8AQF@SPMqHzP?GV>!A34M|+DDGkiFyjrH9rLKo6}WfF4>$06nN9P(C1Uk2p*F?;*}M>Ic|RkDz@E;U8dM z$lFtYg*-p}2K)^Aw!wbkH_(R-_71;+{5<>v{08#*mI*ckfgb87 zK@at-pa<~_aTD{?I0d=jd&E2ROQiK^XZ6ZgW}SIK3@91iZ6M`G9w; zsl4!BHuwN}iSPL~%`Q7PS|Yx04tW{HwpLX3lrZG^@!m1!M}7w1t7S||@4??^L_G-O z3CmP%l6W6T)RizE*30Pco#p)9NyXF`l`m!Ql|$~xqvCr>VJK7>3?e;tWRls zwDEuO{x$pz@^w^yP?v$cKtJk|7{`B6$HX}JgSsci!B5ml9pnn_T={yV62ERW+%I9s zi{f2!*bDN3cuySmg?u314Tn7)*K!5`H7C_fQ*sGcM4P`g0fiEY*A(HqWxO25bJ z92alyuV|d5c854c>4Y4qd_h0u1N{FZk44SHIDcU8&~q9G;m;|Z@aJ?M_zfDz!DpO< zfcrw;Pdq=8cb0M91nvv(GUI#-+!x-zzn5%4E9(;ioQdV#F~`>C zdW+u!XL4iM%ESJNZUX!U>UweUdsmv7bNri?bIxVH4?!KUlXsba{U&b#{uXt7<+H(M zl?%KC_$%NR+}MIcFKYLS{{?;q|3zE_-iFc%d=6a~@I2sy=(p=XE**VR!axt=7wURg z7veAKxmXwC1#k;Pyq?|)if|PW?@*V-{D?=W!w#wWUS&7$fB^pk+{MV4`)fYSxr+OC z;0HEXyULfkc?yUxbRMj$5ubg#jE+0O?}z?vKJi(tgn>@@Pv8)I7j`kQnYdrTz6JHr z$Ijyl&gA+F*yo^r`qJ*J>y$YM1;j7l7QA05zy5CKAs~*UZj1k7|3mqM{V?W%ys#g} zIOL1{Fvg(|*bieI`h$Hh#xXzky%@***!N-_^JCwOam1tGK+tf5c7H<-vdGC$&4s5%qe^13SWhb1s@~_Maaj+8xGSn@>w@byDUZFn;f> z)l0Q-9|8Lr;L1G8r)9eJ4-v4R1YRpYe*f&&UI7C3|MYw8d+GPsX9C{^{|5g9=_9W z-NDaf{#J|&4LCr^3;qGPLC6<*ExIn`B`6<|m!N!ue1RLpy6`=XJD8uw9q}CWsp5Ei zx@=vDVM{JoyZqrIQZwh@ls+G{P(@7>r-)g$l!d$;Axz)2@}>=BZu zR8CoYlHUVr9l3ftf9E3ddV9IqgY1RrE!9&m@^ej-n-3KB@OLYW>cjV{mpBT7QTXCg ze$J}=7`C&aF4! zcy-G}=)Ct_Ney3D>d&n|C!LY7C5E?Dl#EvkPaG=ts&lzVshl%3<_gMWIfpqO77E*qzE1Rp3p|BtON52xyj`d3LBL}^eG z6-6aVg;JYFrJ`A;q)DL+8A?JUp=4-6Aw)8jc{z-*?~N zzw0^AK5Kv0xYt_eY^Nz#hl_=WGf93#UVYS@=CYBCAH7Gh+XqayQ!+g6T{p4ZyQBFp zSN<=4Oj`W)bud2?MUN+&V7mi8Nxgk8kt{k@Sr?IZFtHs|BTBQ=<-IprAjt9(RS*x z;%9ebDI0z38hDeCVoHI%C>36uFgXkV*W%jb(E<4@w7sY%ZrQ`mXQNXJo7&uYFVS=- zl{a3&EgU-Nk5b9^?_o^N`e46GC!{82;pZh*Z=5f3$ASXAVx~9F{8@zW+Y*@HuBCsS z*7M|?huz+o&(FS(pIuk8ruB+H@z}B#AK55Pg||P{;xcWgu>PFu?20EZY$>p>p{!q@ z>)P39hJQ_}NSY%B`UdzMYKggHyV8=@PoR7KNiWU*WlQ7ZveKc!p@V}D@ZY^*#s?3a zKal+M%V`gO#W;}sC#yV4?x^MP4}J{z)LGExv}L+2y)Tz%*~~1AKe?|$`ELVnu)JVj z;BNu{x0|hI{}8%H!TRt#tPgU5^+7JM4(JK2qwi~(b`<;1!i8tMcVqllzufM?J7*gu zSedo`zkBWq>$4Y!m9SClyMN`j3TvpWeLJL_uwN4D5!DRHVWS^AZ@o5rtd86Y%D(1d zzhwSSS^pzmhj-?s_pNQJTyz1)O+|Sd&Qb8*p}L!co9ggACMy z^-xHYRkOg53NEeH}1r7kYBi7L8=hnE2m>tvawMH0X>;DGhDx!jbtn47BQZm zrtyiLTI$}}%E8C`L)OY47`NoPX{QvhzOjzod0ju{6b0+R`mhe{SLsvC(z7fM|FB=7 zr@_%mSU&*=*sn|fLH>DIKY+%l2LGXA`!V`kkfy{V|pc*dxiO;aBFG`+00+)byo%P5TLY{p8lpzZ0>af%Wq) zd5ibr_+osr@ySE%2hNBzbhvNTMC~^=Mv7s6VEy8SS7&_=I)vbPZSNU72C;p?^MD&X zKh#|8Y^1q`0-S-)j$iPd>K3B)5a{Yf-_6WrwjjWVxnIRX2kY~UrKV2=9;?xK0?vOM zRBU3xcT=QXz%BqcCCxrYaRdJER!aTahAu4{PnTDJ7A9jo2RiK6xbsbqP$x(3unvFo z>T8;Kzo2g<9pnOZc&{*1!}^-}MGDr3{SLZ_o}GBWgd%Z!fiy(6h3RqO)+75-ufX~g z7j8*M#4c_QKFeh<(?6iQp-YV^QeF|xzP_=g7pSSst3U2z{Yh4{E|05bqrYzc?>&|F z(Rdp23PufKd%3b9xU5cg2Nxam3j6^0AMksyK7X9$^onxM`XqgET)#nO^HIt*Hf)*` zwhO=q^x3}bYOM#hFOp8`3D^<%Tkw0}Zvh9;bHD-Y4(wuT&HTW*s%L0@1N#MigZF~{ zf_{Sk0Y3)*4b}&{hV?;j!A=2Zzz5_4_>l1eAjtC}4nF&PYWGrAGde$qxVdd{*bJ|98+>2t|KzJ6uen(~ zF|Egq5ahQ&_Z?oi#cqJJ4#am@2i^;L3%nQdF~~Q9h9d*2&zwVWzJR7$Z2>K)$DQbFb9e9vuYo1v!FzVSUKQ$n_x~BiDy~ z4Av*}=f}HF`U{%bA~N58=BapnM3IxXGsg~WvL3_nV*LCL`M>s>V~=&da_R|?{~Pyc zI0*jX)D7UAVlJD$LE{(a+zscNq2e3LgWoxI064#>&&oux{P^C_@wx<@kANSjKft+$ zAv33Z(icws0nS+iyz*D|dN}We^A5b1tPjBRP-g%>?Y0MK8cfFb0gcxsz%H2ES*Ef0 z9y;)QpeG^^b{p0I=F|ZoZx^vn6H&(ZnZmgk?1g#1XC4n;2kH-?=Smx|%>RJ*1$59K ziI3|&*5iG_ITZ9w=(inv=MOf5b1Uf2(C_K44)`8DIOmdl!g-&3FVqJ}K7kJQr5$^* ztYZ}4w>w^E0DlrFbFg+k))T-7>`sJN`bxnkHiCKri4X7t`e|;sc;NT{!Ws10_)~-M z^J$!WG)R7+ZUc7BtVTtWSl^&tLGlCj6Y_bew}71%rVjXD@8ay2q{Dkj{K>ip$cwCd zfPA6u5%;`&qGc_fqga}=P(Yn?-`S%W)-~T&oX1*k6Wnx)b6;DhuEMcRUvWL8F-rKO z1HR{I{2sAyHR0tCrt#tbK|RxLf5xWoaeKyUDVAK!$51b{{Va>;Fya2P36}Yfs4`9* zhx^y$s-pz0>N#|{uT3m{+6J2r4jt}yJ3Uqw!5ZMu;Xb&&D(l<%|2TBGKMwe@TWS97 z2)-8>_yPWn{SP?3#P=Y>J#+$h93J>B=VJ^l4ahlT!KUxYDtXcR_N6c%Lv?a($!SM6 zeXrK6Q0ap{6Zseeujgd{aAVW=aBbK;!&`3<_mMS^{9644-*Y{FZ`Xo|;bl#oIPbc% zb@!4WoPUnr^EKkkQu&Q>D0K0a>x#hVo$-6YbY<;4yfALv-|kdC#C0vWN9?To^uAN= zoc+Q*XbY^Yw(k4Jp~F3Bu&=_hy&{X(w{q>vsc)8$IKEFA?hAu`mG7-u@L)kJ*S@-g zy*rQ2#P=){`{Fl?@HxfHxxWndHFV+7TmKR6eKC2ArVb8r_SN@h5ueBeK04o{?`!+M zwxI#*?NIq_fn^Gu`|=j7Y{}8adTV#rM9XehD}BG7pk0^1>OTB`gLxJ&x9M^22EK3HO@GE)h*(SgIvh3bx>FB#9@ zR2aX4P5ULl4g3;`8~7!_P1Le5Oh1m75gR=9MZh4y`oQw?|-4C7^Po6eOX zy?}SGjCd=l<4-#`t=!7X;JK8vHJ(+1QlEDhg%(s%!<(k$2`%Df7?#R}EnirO+)d;j zo4%=`HcUvk6E}YfgGc>{2)-!{#q%m{^Lo)nEjcuEY0fYoBT)XU)ZNrv6!83h=XZ%# z%Kn1i%9v(8hD6@e8)>eE$k8~urrVlD?Llavehz;A-^ZLT(NdJ2KEWuxwuu@R9TiA? zgP%VW+_dyc8Jcx%TD6E#1J$!=^79aHK88?cR!8LC3dH*)`D^1`CM6yc|7y++K8B~^ z#4hoyO2iQPJ8kkn4b|_|hz2iUy5pMBgvHgUuQ5C=?rRmbz~%Cf^Rjrq1pl&%^R$m? z@N)5ACGM(u@jNdV|C9H2o=kVf&lCI~H(OzP`Z<1{;Qy?tIQt5gBgwy9=jB@70$wiu zPx9~Cw7e113I2~eTlSf3;bS-nneGp=sz4iy3&dw;v{G4ev+rG(=VNGX5RdHoP>Es` zjtAe{#-`fmWsVp~V%!{lqfN7`kqRYa_Lpj=<>-{5UoR>9mP(ObsLy9qPs@?Ve9@-# zea@7X^8Q*CojO{MJbPpOlV=2@4gJ4mf-P%kIr1EtCN}fddt_Uwch5Zn>(96x83vz@ z_#e$c@#2HmE{#^uax}D_|8w{D92D<;legNmoR%YxrDyuj2L;Hm?G_*7%LiJHfxR1b z{U4Pe?T}SW@3keg97+B`jwJseN0NV#BgsF=k>nrbNb(PIB>4w9lKew|lgD@$&ql|` zl%8*xRfjW^o)6(kITriO=m`oaimqI6vzDf--dgbTz_VC1zeu9wcyKjM=Lz=;x*eH{ zY$hIFH_EQ0=@LP?y1X;8kzVj3pIa&wG~Mayy7w+t`DkmW`QM>)Wi*|qZPSa@p2g@$ z_~&(tT}x>?yIa=H_v}aHzvSGEFHJ=>ong1RccV!*lF$nYNqN{x(^Z95$(CNsN3*TO zS42v%X}aO8-6~oui_xb62es9YnrV9Ai#D^#Q6Es|YZl|hj7FMn7_;K&-)rS)`2?dh zhd2&C60GSalXxr9(45GJX$~BGJV!s44Omto27CGQ3zs?g45-d(pITIn4(mrPOSoN0 znOL5?kt%`XtWPytG(84wk9c;-ZEp$XabotM{~SyYGuSh2g*WO@+IxLn)d%YLly~k? zQ!ss1{Fyu%e`<>w)30TFIc;C$I$)3FI$*!#IrW5g^Gqmo7;3pifiFomQU@Pk9!~UO$7e^0>CGg8k;ffb| z$sQL(toRr_r;;55Px(=8y)TDK6S2SQQ`#S8gz37MR;x%qLQ@jA3g0-yrcN|_Oxbbx zKYC~2Vf!eg6Sl&7E`JLZps|v_YzOvxU-Rerxg{g<$6m{x`7}}mPp8MIDdPM`P~A|u zg@JhYN+_RJucz*Owo~w2iQ`M9-|_jY^N_Kz&6X=m>Zs1tPLCLz|FbvmX_ZtcLbdN- z+VzQHKepx68DC-ie8I;D9luJ^KwndkfJznhAH;K`6@K`)kDWefu)qKjnMmb z_?#j?ls9QnWi?v<{%>(XaS`QO{#a;2C5}e~J|2F(t4-T5oxta3Zu6#;OTqf0S1J-{*DanbG@cMb@b=qd7b5`$kw1l|*=W|-Dv<}OSWg6ICYoK(M>ppsKk(PdMQQgGuSIOc z>l{-py!a~%#jiRTyxTM%36@4D2(8BdyHnk~YnDnXO1Bj0c=@Ujk@#;-xv)1!zYvl5 zyB-wZ`sH69BJoe%^Got;aSkH!k9KM4PNK39i9f7Q;t%VS_`~`n{;)p8WyTw=oqi+b z$eQWh_2*;(9gku!X!x%TsYJHMugfM1<scg1wUJ?$baJRp=U7qJFYM=^-I-|6 zSB}ab`SVI#$fG{wo1EUfa0(;v-Xa5yQyshzh2A{Kt zb^7cJb6fl`dt~$WFeuV5jYhl5WyEJwq+hc1lF9W9&ZS7d z6!JQ|ZgYG-Mf#<;UtJgY^cK=~%5&(|Qd>t)ijR4(zh^}bhd#J*X@u#vRTFl$DMJ1 z?kruWw9+96b-I-3pSH(#H17Z3jZS)$xa%dFutlQR>I~NB@puurse0q-dOzA88C8km zmrLHE&=8$;-$@L5zl^Qz)7_S5phwKhrw+(x(DXp(`O??x{rARGQ9qj^FS5 zxd2%gTuhxhE19ONRxXT;UWD@{-{(?-xd}AgeB`gE-Gp+q>!-q-H-YbIx+i<)1)OaCn5(X<(h7WIzgqe+=j#m!p()p;VF-(1K1L~>oVS#R20joIS1`E z`reiyoI~TIsuJue>{U(oUqBy({DZz({;uSrd%i#VxPMax7oFXo_dOutBNyH9`l#;h zH^p3ZRUOfj1zP!B^uS=h&lf_nxad5ePE5JHErpBjB%QN-cv=(}ou@G4`t2Pq-1PW^ z^JA|EQxK`Hb|NBZML)>3tKQzyCKL?b&!TwWJdJw{brV{EM2N@eW^!&yVAIb#SMJj9gP9^)m0}(lp*F zbY8tYvear$R6Mn8gMgZSD<^NCUbc^E75a{vm7>vHz~JQVdHfcvvpKPp>(l2pRmqq> z?jPX2!lzf;-VMY!jQdHTdnj5yD09X1asL4HxUZHUpI^a#d)!X~eUjj&3pIuqhjBj% zbXyN^AF~4(&v8EqbPMfd^Fz3f2mS%*FEiY~S;^va%DA5d`h0Kk;%t0w1^)naw@67H z`C0h+aX$(4DTni@{gyZnA#g4jaGkWv9n%RMwk#i-=pBOXi@+i0;Je;EX_!vn5b!)M z@C7vqXXwB1P*m0o}H1uu)iX3XqGpfT7c^+Bo1*UPh3Lhr%_0el z6hxkL%MC_ueEX1y$aAiRUx6@FFCLNp5bT%q|6uQAya4||<~dzrS=&FaiKfUr=gT^; zB`GeE6q)CE>0b$XBp5-Fd5*fxnD(};FpA7`+&6BYpX?Vxk$H}yNWjby|6q#Da~^%& zy?Av_AVuam;tLJico)2&&?S|-86Lb`=Zhu6hp$NCctM^oE=;*%u;~CV*ZG3XpFu8U z-VSmh^L&tt;N1IF%GiH8u`lgBS^JhU&)smd|J3Ne*yJLFsOe#qK6^gu3(IIW0LG5-om1NlBbpq_v<~@l=wU<*RPJKyrN9>Xm88c4erTQkn9A%)cF3&|X{SFL1Jd{X@)?}8bZ^QH< z*;VXvlcw9CcuG!W`lXt6c%AZXDT>LZ=;If+BhwDPqn3Du9KR}w^H{$3NshbA z5&x>Rd-v|dQo1q2f-C0Z{ImFbpsrvgstU=FbV!V%EN*|#tiXMCp+Z6N^9fZ5eg57i z9rT7mSDuHl@VSP>p^P_LcKIvpe+V2N?RYYOFd5Sc9Hx#Y9X;^@uS4LV(6TG$A{#$X z;1Hv`jHUP;pPL9A6yncDmrum!WdesK77J$Vn2pcf1P*e)9hU}5;Ccvw!(jc&i{EAO z`JKR_nRi{9r%io0L0 zs`jQMQcwQc)+N{(q#{yJk`9gB+W$8Rk$STJm5Iyi>;y#WNuZZRphwJmMC!?pAK^Vq zvf>b_Cwk{%mACfBAW~1n5`G_e`}i#)^~CkhY5{HM2(+b~>3{Gxmg9IG?6c5=D-H^= zNcPBlKYz$XjnQ6k$&~8&L)d%vvUyXSJ`t{m91Km zhe-W5I>XvKaI654c^b%_%#qmCq^?e$6Z>h? zTk3pcqCx%?Odqhz+0XX~y?xDJt<@4irA*&=xgXCnI3?w_9^Ms$R-{)C`h>rx6wlf# zzQz3lRfk344|k=azoM%q7d#22UP*`A{>1$PiOiH~9t*Qk+AlxB`EOoPY8M}C=YGO| z>2z36Nm@QKGWeoV)*M8gsE$508_$`jifDaaGra`acJO%}TpLIYZ}quc-+=4+TQdS9 zW|SdCk(_xecLh)uKE08`HP~P6tDiDp z+R9zrILw!f*k*?N0wfNRhK>^@abJMMK|uV$lB4?EIQ)#a7MXH}8;9e2J*TN&&Z zL+RsWb8Z}#@u~gLx8}wnt>W#+{nxm0ApIDeJ4inU=M>T}!MTR?OK=V%^(|-afrfda zk%-i{UrF2kIcA0RXPIqKONiA0qXw{zvQMuw_pXsc(-D)q8o{`XW-_vhNP+g^hV3 z(ypQ2Md~5oL+T;mL+T;mL+T;mL+T;mb5ha%&?_ArN5<>zkPkoEkmu%f73U%2bw9|5 zNxcO;NxcO;NxcO;NxcO;NxcO;NxcO;Nq-1+MbaNa9g_5iP`4!a3w2HsAE=9xaUANX zWE_XOE7?bYctrLQARdu@1c*mu9|7VKIoFgL+PL1=y$z9bO|LeuVgAH(h2&h*y*EEo zzaC^Ea;_=&pZf0XcTEW9VPGz3LHfaz#akN?IoC9!eypnXTOA_jnhJHc4@@hqLF8Q1 zPqBf(q~a<>&NTu5Fb@NBS-?Nc!@yh?@DKAaFxLe9!#oVhKg`3xTodpQ^Dr=%1^mN2 z49qnF|1i%0a}&HcpibA>$oIsxIk=kIjtuoKAIpe-19}2=V3@NyQ`e{cS)WPQjbY9zbo#F(af4c#4s%xG(~V>GZ&cHC zn6tXEE#Hcf~5tNwL)%h#DT({z}>%I{eo z7Si27(_#K<&Gv}ETOJ(TVE&5d`1dPWCpfsl{8i!7OOBT{IJm+5mF~+Q{Aq@jh@1=R zcHiDx7J}#8iMb$w<870jRoRG~3o`1yq$AqVjL5m570i16m*$O#oC})aetz)<$$CW2 z1)0CeSbA-=7Ljv7EkC>T2Z!PQ}Dz_N>TOI$prMTtf99pZj=j9pVej z%k7w8acH7{Ga~1!7q?A1buSb&SS&#G$Dd^@sNG(4dZP((rfGug*k@|-$eC2xe5M06Oj)Y`Bd&s;H?Ui{by`+_2U<7A ze{`{L6+LfY=r4HklUyfSuqOAtgntbM^=sY`>(HgIir|9N2`xT@8w&}MpF0V^tNvLK%cvn zQXjkvqXD4dux&gaVYs!{wEcDj$(`n^5OuEh>|Mdm`Jzf?%vu?{+qf{eZ zmyk~_+H02Gf+|v{n|2n*sk?VQ4znpZhYD4w4K9^$LDw48Yjc>b z^c>y5C86}0(RHZ#_SX9&#vkZ8I&;lM4H55|=!#U*)db^WdXA378Roc1oMDcOTp#AR z$n`;v(oa+hA5NGucqD8B)gDO za%R_(;&OU!YG{Jalf!6i7aT6H8p$6CLdf;j;v z{n2})+v?GkODUf}pR1%`t{`w(RM!iU29)_Yzp&A}oTBFt%Ef9wv^1d3C8K7Nc^~LG zx&b*mlRb|cQK#Xg4rXvM1?O4P?%@1O+8vyiNxOsdHEDNn9w+S%=B7Mj55KyVQBV6Z zIMudRsOuBlzlU^VAXRnG#OMTu&^AB@_@ZB8n4|9V+2mWDh5a_@^ z%nbq^))^b08w5J619O8whjn1TK<8PtBaXSZn8q#ktkbvq%b3V?ds0-|NzQi`v4=wi z_YX4B-8Kou_#zhly!qI?=?Mw-sM`P7k`P|bcNU(y-CCw54QTdFjpfZ|4V>qL+q7oS zZA2YHj#&#PalW%)zl^xH<$WWn6S}x zvL>W)Y`6B5M9%jnLaXK|?K#klOxiAn{rSN8-bB^W!KyN+8TAA&TlVw{=X;aDv?GIO zaePq^{^a*akMq4rH?`t)hIc0y4&CZ=k2J07BaR(O;L%CM(OD}hD;ouTv z^L}_?2Nw?IOT8C&l(ci<5V&Z#WXH{RE*xCc-Bp-d+PQF0YHmgb|Jt~4kWi2bewNe5 zg~Nc)mNrd~HW~+*Lm=}dm_s1*D|!xr$j4v~2lNf}3g%>h4tfQ1GC&8tf;kzWgI>X$ z4A4QZU`__;pjR*_19Z?Un3DlI=oQS#03GxS=45~ldIfVbKnJ~oIT@e>4lpMJbie`T zWPlDhz?=-w0SB0q0XpCSb22~&9AHic=zs&v$p9U2fH@hU0}e1J19ZRv=45~lIKZ3< znODO+Etx04{4JR$z`QP*C%}9!nJ2(JFqtR7{4kj(z?=-3=fhkLndgK4lX*Va3mM13 zzR0)ggGKIZo=FV88?AX^4tXbBF|YM7xG+2&s`Jw63l6n`4ZH^~Xzh>H<9yc% zbQkZPXDS=iPELFWIuFxqMaQ}S_1)$0pjlQW8@>Bt zk}ENgI_dKc?f2GbiJbr5NuQ6v zPZnBjY_72BqR&&{S6z07_r2!*M4!JvKed>j7GL3MqV*H{amIdfPrU|PX+EJJr#_lu zd_x%9#Wh z?B7b)o#6Z%-ZrB6<30Xg%6Q!g&cD)1=XjWBr#v z4$|+@bB32?q!$h_QO~^LZmEbi&OGXmJi`NT>XF8awWmKVY^CQ>g}w{MCTKSxJ3FJe z5)BqTk6NBEH|Wzy1JYjErJeeyiJnIlqTJ0aeHu|pp0dHF$qn>8>cAWGFO5r^&}!Yi zYJwqkoO#sCx6TE>ZbEO5MpZ2`t)b^pJ&!FpvSnE_%IFE6aYna_o<|+Hdvwa;$IZx4 zra2UqJvA{l)RUO z?rCi2(l_;hn9eyq?OggMzd0;C?qEBYzS-@rTiYSr&ZTeaa$A^I4Q*WdCLi)BWo2j^ zm%h0SU9qmQYU9#35(m&X5(m&X5(m&X5(m&X5(m&X5(m&X5(m&X5(m&X5(m%+5(m%+ z5(kLmq<@AuPWoqvwz8Lwg9kBrwaA4tY)m?tFTHOwE9@f!L> zWW0uRP;9_pzK%j8>V7Rc{6g&=eZGk8VdeYpXh6H=3?z&i<7v8T?9BHYP(3Qo-*n(u zd=gC;I$|60bqNz4mQ!$QDdT*v$}qACzSCQa`gessy1q7(e%|x>!G)Q9^(J|X^BPG++W#@8Y;98R4crrUVSwkQ^o(~ zP@VVu@GIOuW%q?2v+Rwh>OE~8T@P|UKcVk;-ETbiOY(E8_nOa z@_EuVCjMUv`Cjh;hi}IP_!#n1p~|iqjcD$>>zNIolBkqfjYi^&@ON$PV?i2U8c<^L zEU}8!X;i`GbvBnzV?GO0Q$+L{(YZ+fy;8<;RNVTe`u#fiKMAINiO8w}?JQ@9TeQ8W zjP#Fm{CCMPkDp)qv6ITWb^fu5H%ZrBRH;aZTKGWnqwdKeB3*iRb{0A}3 zvue+B^Do+`{L9XioBxt_gH~TlZvN}|1w_`{ar1vz+wR*52X6i&4o>k9xXI1GSk}+F zBX_y^2YX>J4|r^&-;CU)mS_5#rO@^gD>lQiJ+lc}`REq$%*vqcrCd2^_x4#$XwFGv zv2%ABw7rrup8 zz7wWF1+=}8{D8fX{D8fX{D8fX{D8fX{D8fX{G|1UxnwC~dnEjiukD`sf21*XFYA+ntPMQAjydVa=l~{Cz=yLeZ)R4M@Xcu}FeZ1d^FdG3+xi zeacl)S%*f{rKx#x`A{f2eC@DOVG3Sf`QG2sqfIDeUcJwCRxp}0xNfUOBK9+u((T(9 zG^1tKb^5Av0+C|i2b+c)_d6*P_YNB13~iUUXcGWp{=7&uBtBZYP{fea-oA z0J}NC^ln8HGXBi(7+rG#w(j*7;*)zrn2_hN?=O^)vwgXU1?v>vWx+ z%_!(5wNkt~j8f>aT>cUN7i*yS+|CZKCNxoWM$R_FHJ+8fY^TJ1+q88^|O+$yp3RP3LN=4!rdsz=49C(SbZ@1P6@Z~tukeV@F6 z=mJaMI<#{Fa^E>67VXy+$l7Iv?Rr$od)ff@pL=!*C(e&RgD0!vGfnV!C~aqqN9CGO zk(_t^v^7yEQb1$0;Q}|GIonkB95cl01jJm~e7^~OFh6a0M=XdEGyGCgbQG@>(>wMv zr4d;y)Qb;cyrLFdUUXpjUW~&_NB-5-4ai*`TT(E9H)nj)wk71`~&a@dcpta&Y)!$gXoBs+HLMNuT4;=@aR%Ra&1jmjL%)4~4@ zCUMhv{B3ji1CE|Io(WKxwLXX%nOUKB(2ZjkI(#oL?jL(aN!>o15^#@WNB(H;hTrpF zQ*%28G+b_B`ncUqIMva2=jI#gWSQ#q>5d#b-Ft6~=%ek?w4NIVZ7`CusYUt2(=81} zB51l6Uv=ocygKw$<;Nt3QW#A)PwF;d4lz-Tlg#qi$FFGmKy~}ZJF^7G7S&o2)(qN@F-qD%Om)AVx9HDpvMYL2DDK85 zh4Z>``vQG%sGQwqkN-bL`V-N>xWgLwKXRl$NfE8Hd$kJ7m+&W(5Z~_XO&qy#2R(p=&%Svm?SXWk)!A#pj;&Ws$urHK^0S;O|L}9zNK-sdTC2 zU5fO>pr54Q2K^-cJm@DG7eGJBI0E{qIrYSAp=`|0P3F;OPIYKuk&>VK4i7ZH=h!5K z;|}4!y>XY|}vd_$3b*irmMOyhoz zUCVz`W}o+KqWJ+nwJsj>Da&r;q6>LhC*)Q(aM9&OZcOegs^_9R#hSZxgfY43T0Me> zD#mqObi?pW_p38%X}a#34TfT!-1^qKe%8Ug_`mUx4}g9a2y7mD`cUN6Os6+ zzN@w_%XLRF-D?7(|NB4Qxc>*eO>?yol^@1=!npqjdWpeU<7@mKF8EcTFLNI{J{8Lm z{3_5VN3FlCj{o}yeii7a9|h>2sK)Xg_y0g23A!IA{{hGSasLl=Qtmpz>n9k+xFAwb zmVSuvtsl9KNWF@@zSNo(;*4Gf{%pFaiv2CIU*EAPgB}g;{oYwA^}+&wZ%giX)#$3F zLjUD6S)GA{x?$vg`9B=fyI6R$(>T|*I>AEvE*+)@>Y|F=!>3D1-Jg6Byb zz~7R2_09)10)onZh|KeQy@rn*hz~_%p1*I^u09LZ2t?-jPqu%0WxO&9O*Q2i@WJsK z^1ZP=N&v-r$ z{)f!Bfq&8ubbC8(wP1Tu7aFt{W@3K8KLFj~#u+g)TyOF8)jY9la}By;X5q+F_?l|1 z9zEBG>oexsM#FBwwMc@mpmVl#C{^>!VgK%4eD1i#V5femLx$Uv@3?0MQ(M*w_g!wo z_BGKxakpqa+9I~tB;F;E>K)6?(rdtedsOYvbGZg&`)HY5)sz5gk8O6qq$*rbaQnq4 zxT+CZtFmO(be~bf$6Nz-a2*33uoO2NP?Xl}WhNpUCeI7qgfuFCK#?X7alY76H?8<_Q7l@Saq<2qltu1_xNcm1Ub@=8>(I<$MZ;?_?r$m7_BIT>?6g#;)&l8dI z0{%&P0so}DfPYe6z&|N3;GdKi@K4GM_$TEB{KxTC-!k;YdPto2N&STLKB>=et|!k6 z;Frkr0{A8Jya0ZQ^e0dsApH;22S`5;=bAAhzlHpqtj|Dx9@R5Van*ly62kA1bkJuK zH^|?1ib>wTjq@DRuaf=^@*nbi1o;nnK7zc0Tp#R+JU2nUL7tl+-+($W^doj={QUFt zKmvlgG4wC0+FJ(YTizk4GedtNbpE2434yWnc@z3ILU)YBY{D2c9r`BgCtTHtr=N%Z)XZ3=2Z?c<|8qj0$nv{l z@w;yY^z%I9eWHm*E46vF@@P8riF6#LkF06u{HGN9M7#yV8w0~R`+`2v!SLwCA)7h- zffIm|Fcs7E1FZ4&;->0xtn6ZfLkC-q=x40g{|92w#BQ@tE zM5j$nL}Y(NQLOFy@V{h4_D3YGl|Du*rX#5LLO()wnUYj+B1Ov``UQ6_2O5fta}nqX z^b0IME}5QfU4Y1b3h)p0Vd$p-|4<)>{toaD^`b_ps z0DtJyk^6;p$o<0eq+P)Jq+O8xl)E+8qHee5BeI{8=$&S?!ZH_;{gesMHZ0RzmyO7N z%Cu9e>mtM%i0r3$*s1UQaXB54{gi($O>-5$CnK_-@`qv9IGB}y$bJgbaAJ^D<2yw5 zQ-FW6p91`o{S@Gz?56<#WIqM?C;KVDKiN+K{>gp{@DKI{{fl?9+YiS#C)4%?{flS* zb_OEO>9l=8|3a$$*C&gu4BEb+e_?(;si5a#Hf>+fzwmU4sy~nauMYMF{fiWN-(_|u z@c)>JJ}*y$b$jotINFX#KEaMiKEaMiKEaMiKEaMiKEaMiKFR(v@JaTg`F`c~y6VOw zvM;ka_1WR?$CD7*muX_GdDrcqipYL3(8>NXtV8yr;k{62kapv{cOTy~xFFm6Q{&0qGfj$8`rORDWYHy>m+qvN^OQ#~RRX}agn{?VG<`1{#1p$UHH)kt~ zwjxHh+L?^!Np!u4U&Q2EsZc8l7R(I3pc+s2ag@G&Ib=Bn-$yxi;$*=V&VRgNo_yCE z8A(HP&in?@!vm5IXH|0MH-LWU-=?xJUTO5cfUegi@=zm@qU*5|q2<}5o-Cy6a9L!) z?i~g7*a7o_&5spXD9rH3yOO&J6x3tOBZ4M6SGSHeKxH>kmn~iTtAiQ z!>08I>JM5qLS`-rtw`A?$51aXj-u-oJo~ozHMXL;Wrr$fCB#!u-*9nrTlZmd8?w1? z6uRP95(Rm(%Y+dZp%?hx)p0$9I>}Sb5*gj~Z78T<_AO11Gz#iDL&6slZ{fLOBb48+ z=SWddU(yzFIePD98~T2reKhT1D9<%Qb zv~%WvVwX5}MC@RpC+m~`9B|;Q!%j@#eesBe)CZl5K0o2iBbCQg?+vYEA??f{4b|tI z`6cuG{N4xC@%_R-vX#~Ta{P(N?b-for7_M^jJpz&-_!g!xt{x~uG_}NXSl$fPX}c2 zcZCF>-KWBPWGA(8@u@Z6qyBwUD;J;mXY?CmQ(C$B)LOk{VDMQh7oXvmYS2OaKN{eZ zuDgzZCjxjv-4*m1_=LJE&;d`Vy8<2Xgt{xx0Z*vA0v+&#x+~CuPtZ^Hrs(D$EEcVw zbRCu8AL^*E4)70kRG(RKv(qP0u;jAmO4m%VsqjPCu1|NlVROL;ssm|{9F|1aV+&tQF{ppoii9<dYknP$wbzgn9+ZC#+BM3G0)5LfxCh6X+!WpjTx4f_|l)NA1zgW^FW{(640bZVnE_ zxB;G^hXX{s13e_;9q1t$*FX=+xCVMf#x<#0~m`ByQ06BypqrBLr@M1IZ`g zK=KLaBQno~J};SPLY_vR7a%_*&liwSlIIb~Kgsh8yh24qw{uRAtTP(r4}8swXF(XLISZi0!3_m}6{OKS7^WC8IB<eUizi`*d1+JhY=v zve1QqDYNeT=FojQp$BZ;3t=pj@xuJ9xl$J0r;8OilsNGd3mIrm)q5JQj8sXyd7QQu^r;H`6U6!eKme4tN6;sbpm5+CRjk@!HLh{Ol_L?k}YCnE8IJ`srz z$Tyj-(!Xt28<%|5HwDk#v8atpz9I*LVj_RGa>+M2^0(}P%2qD<8h2$nO?cPJCExCx z_yK>PRxbI5Zz~@ey4uPu-?wG!`*B?e`2X|f1$qZOFS3w({hyBD$|qF+X^jU0c#c%-w{PUsA^g3k_fbn_Lx1X->HTg+ zd@mBL|9METW<5W?M`p-B{T=>Zz*xHIvdnBYGMFA3{s~{93UsYlxgTvfAD$*C;a9%y z8Ktmg@zZN~&I?Rb>R7)Ci|AM|K@`H=dU}})r@5L_vFO} zzM`CkA5T8mj{D}buSEW^X+cw?Yu)}Z0x5VtR^G4t!gdzA@#bvGPnT%=`2qW)Cx-Yt z<>RByUr+G9q4(=FwD{2VZ!EO4%CawRB$UQS=+L$1m11m^a!94_`uSi=_)X5t-X8qk zaXR2O_x7Up%|m$pVSF9HErTI(%YYyECCA?jd|GJk`)csn8_j$DvQ`?_JlPS7Y3eCei};` z4P*HVrJZjV|I9*@FBa+Z)cRBJ6(&l}$8%PO*Npw;aQ&n^wdLH+bwSh~L)q>Z-|#%- zT)|u4RRJ7TTXkr1ji&_4b(p6eJ83Xq6QZ$VOgn|6lPzNRwX9X`HC5Z_aCvuWxJ zgy{}jM`luy6t!w(wZ8a6?pzEfC^L)({${=zuJywd_J ztF8O+d(Ca8@7DUtqUn8a7V(Kp!0RhYE>e8Vp<6L+b2?|^dvg~3vF=^Rp&t)$@TtZ3 z3wzp?1jgsIaMr1^zMcOM>$$9l#QIyDxysXHWf80ajKh(%@YJQ8=f%>eZLq=fPM!=O zZ|OJ=U9LJx&c>T8Jwoa7n65+Ht zc+Ov5^p;sR_7C@$KfQlb@)p&pt8i=+wy$y}*6exeEF`CC)0(5^gC51OuKD8ct<3$d z_o+m&(D2i#N4Cd!qPXYf6D{#~XG%eTj;)@=M&(XhUG@mOA#(k_WjyC^>~%mSKZg0r zwR!!vhH4{C~IzOhKv092Hv7hmr+1k6Ul|}O@aL3_+-+yxK_t2Vgrx|J6si~Dd zzE73BXnp(gzx%|OnCbT}YC(mc_!Xwjzemjql|JY*5#!dse)F!-7Ua3v@Kx#eThxZl zGraZw`(AIW$ZB437P_Wt<4s9AQwt)7mo;JimoIx8GR2LBuKQNR9n*B6Mx0qHzp;EJ z_-{0?!S=Ffn%3R6FLso!teuA!mhaPJdaK_oW~0KZqBCcSSW_gQS5jm?yUf3fNIu)8 zzcuDRxrsVdiy-A`NUBnPekf@ z;JM@9=SAN~q+L9nymC=K^A49Dn%en}b$s=rNIgtM$5`%O4=I`NYa1G{etHhAikXA! zZ(v^wR<`75V|`{stX)}y{Q%gPpk0^1>OQRhTJ`q5N-eZ~?Mb-h4&3bBdNiP! zwl8vh;FDY*_C>A_IFRcDZc7%N?y?P@%;4AUT{~LNMxSH+%8&gas$uDO8+{(IK!BY7Rl(af{tCGBAyi(ea3NN zNP;o^#R1p1Yt<{|R$EXzM=pyg*G*<5Z_M2rau)ZSMhx@>Mi0_-{`6&!S`f!S%eEYs z|AO-qrO%nsWxLqu#l;C}KKeJQP<2_ULX5M^u{k!*%h~A9f~|v7=iQ~>EB|EF=X?qq z6%RQlXI{BS^XYP>J$Ckc{2!#Z?p_HDcMA6P^qY%bb|f3Q#U_tAUw%Zr_w3rbW_U8A z+i83F=~rw-wG6&t^!reE`b%y1V85qb*!ECyUn_divZB08%pHl%*zI+o8Shso?53J= zE1I(XE@Q3dBP5)#X()9L&NtLuDUmTYI+pv;>g-z!bm!Ja&DS`-g#VhS6#JWv0`fmx z**;)`M$Csq>v3Fo8k2uR<{KNuCES&~f8i{uFd5C4!E=esKffPa_p;HOZ?7VEn4CoQ z-@fm)!uYT|%F^$2vC%YMJ6%t=qX_T^96FURp0fXW6Fro*-64+e8?yU#%QoON8|{1M zzWXKr1L~xHy%9^Z=u^lLj$e7L9QNeOg0&su`xGSJto zzFZg-MC{Qx>)y@kAs)GfSUuFI9F&BtPnA$1Mz&-a{lmuT&S$TM~8?k^C&Wdavk# zwF4sg33rf^IsVoLfxapESDl^U*h=dg=%)+6jO=>@tPkV*33dVcR@!%Ev!$8~0-UvY z-X8m;#?f=wSFC|zVxu;P4*QZebuv>n}w$PT~XZw4juN@ePdAR zqCbZY{Fr~=e8=xJ#~y(nA)i>O*I3WNPJy4Nch6tiqr{=p`~>~6)5iU1cpmsESKKf= zavp~c{4lM41dB}N&`F%<>n``tf9#9`OakY-^IJED#uK zZHMiv=`O>&(uK0sERL4I@z<{Mqmnc3hYmcg`4O#ooAwV(ix36ziLJDM0DmGq;iMyD znBz}CACzwJt}_45@jqavzz_H#r8AAerF{pPwrM+AIockbROAlr)a#V%ja zI((b9YxTdU&vfE>gL4g626SCrX?(PsPDwFNw9@zhp4wZJRjaSG(s+V@274?>Q_H=t z@(97cz)#8tp9p81e1J$lsWEnbY;mj?Z7+86HKpfovC(RiHOto+`_OVxT9z2J$BT{P ze>Mu8H}K?=@1zF@)~qt}rAYtgJG$_GO|~gT`oneCMCy;%n^UC!3}jj|G~Qd$@dfs^ z+3&;U)z`gg{||9(RGpcApXU-H{e1Svm%_dWJSo!8+b^AMT2l?(uZ*l9?59u1BUcr&IAMkflzz^8Vhw`6EZaAML>7<_99JrbDdh8J$?+PncwE3=R zMX)~T+ta@fGxAjMT*^2i8noOlQEQTvU-qKjdi_z3)iy@3A& zxfrjUyiel<8AT(7@sTob;85VO!2uX(dwU9wHsA8Pw8RYD9-1-LXY0X`OKA9Qq=he_o({I z>>H19UKjp8wa3?;jf@U7r`#;QMd`nje0Ph5^L*(Ke=?u3k>n~>>#a^s)cMlksxkGu^1GDBMlbd5g-uifd3-{0Mx9I(l|Y`DWW}62CWV6VUjNG`8ChX z(_{U{>e8NLBU~LnCHB&#J#%{ZJGrjOART$Ir<4bKN_nuSlm~lCd9bIH2YVt9_9PCm zr_A%rFEY z@lL)aFEPK6m*o6HK9~7{{4DcBw$6T@fuDvMGC$<2VdQ_YX=H-D$NGZ2LEd9MLS4an zfx3b^f%OG-0{F~d{`C)DzO=#5F!AH&uOi_dhf}VcZ_W$kAL@UZuUIcY$NN&*s#P*a zne!lgT~1`mkV~DnGH*(_lm|M8`VO6ScWhXubOqJ@%!N0< zfAwAyptJ7wn%djdt4@?|>w2lclrigd*4;MQ2Ck~nE=nJneZKFNYnyb|-HED?o485U zS0PgqjhVc2ySX3bc6)2)i|RgAj~iX?j%gcU?q7K>Z#;gL%Fl<~kB?kFV57O;A**0w;5(b5Zv?vUX2(<;r4H{^0!K7dxu^eo}syKBH%*?iiUs z{=hrK9G&Y$CXoNwPR}+Gs@_I^_EE80k0Y*=myGb$*+&IF?^4R{?Vow-iq#sOeN^DXZYJtCee8O3KL`1}&Xx2jS7)WUpM$=i!mJNErC6e~ zkAyuNy(^-R+vsQRM`6#V%>#>VDHlJ12>radl5}qd@8v}-9OyCG;_MO zYs~$xW28R*V1#vl-=*gMUd-DmSLW_ib$id! z?X!3F)sZv!Qhl2|6`~TzIsRov>%3|`DCG>Re>KOP98n46j0srqsAYO}|4_)8vhT)) zovqaG*$O#hdp6s${98gfBNBB<_5OXloa{qlfBeq=A@ScNx>(5b>;XFajl_Qk`>G}V zJl2`&gneem%hKC?(uR?$GKb4CD(HGJ07ib9*SR(pYvAY2YosBOf>))9GXrZzoBK=5BW@3$I#+@6I524RFf~3#6gQU+ng{05926dO$r>2u*)pr5#rS6Kfy;Nth z^1D1wajXB$Hr@1SQ!nZ+m$HK#Bd*8Wfq8a-=dxF6=0%xt#k}Cv`*ZiGJyB*HGOxvc z_22zn{eKF14)~^X*;45iMJG51OgXC2(fiG!6PyDszIyv~qHOAS&c!)k(V6p7{kpI2 zJ&JR{9j}V^?XB{pJO?bgeCGCusZr)RAa#gaE3YfV6G!WuZ%X;)`5^Mk^Fid7=Yz;E z&j*oTo)03wJRd}Uc|M5!iF4^M;#}%0&yiEEE7twt#i#`5$lgs~PHq(tWu7C;cq0yF zyb-t5`NSJ>F5``HLEVTv@;niFI8T)GNa2gE=DvTwP3N8i^IHGeUUu55O*;1?mOrBNTXi`<+I;O-CGi4ZUCx7}_MAJjDBn``dwu`!eHG@5fYJH;G_SEr zm-FCYn@O+lj}Oq9kEnm3&wNCk1AXMD&VfGiQ|CZm&ij647QQb1-cOhFCiao_1NLP8 zrG86PBA45o=j(Ji4?gXT&nuNy2kjM@j(5MYwTv<4JVsk=$pC?ruD4jZy6Ung!>6|L%~0`>s2>@YUG(`y^9# zIo-?N%-^Ux^4{etQ{wNFy#DZJ&)`hUjGiw8tDoADP@dPWZ&wW5olu^WbLzJoqJH0o zbN^mDmUq8jXPMEt^AM|5iazK5gInCOeOANY81-Y(1PuzW&h2A&2Ts*hK*nWJ8T{rdnoZN@G zJ^nz4wb!;7Npqx*(f25O#@&ap%`|Gjg?WKSsxn`14cr-jAEw!|PEYTiTx%>?ZJhbA zE&e`C>MVx~o#?vSxDdH<*2yjL_hCLP>2}yUe7P~@%*%o|H^twFDO&Gf$^!w5j0|%} z&#(B~FG9{$XJ@q7tA2M#%GqE*&iOZe63SWEz4)pP>i2fIw}YHVo^;z?$SzTcwdbp?v;@Tkdu3QCC{Cy>ArG~qJi7Ii>8YD8QF-9o>H6EY+|${oee`Ba%&Q=$d*gh$hJ70KyWAE~vQ>IjT%=*x<6y8(r>Fo3OogdO--y9{+l?=-^Y`$;q zW7}Rz64GyevSTg_?)ukE7JvR*eaoZ$CC4Z_`>U55a!Gx!6nC!r)W@$vnaS!NPNP(l zE@qBD@4H#E#Ktt<3jg-$4sX@}3F3UyuH#YnmlKqn+veqXrS2PZzWJ+poguZ=?}x|a z8S^4TqWJp{o&k=v` zXmG)c=~k;efSlYr+BY(8lKlO%cgK`HOJwQl?0Z+|JPtX1p7e5vJa$j#9+3&paHqyG zb-$W>M8N%LCk@EG@4n7GBH*zrF7BI?Hd5yvQM$X@i5JT6GOpI?MYiWX5Fh`XQ*@rw zHe|Wdm+{3p1@y5q_mO~OXYL~bM=$OpneaHhxQ_%Jy||AA9KE=YbS}o@?h7^Uh!f5e zW&CqKE5{4xVzV13^KY%@N8*-qwDNbGlz$Ky59eOW-R`aWc&YhU#`BWoeJ33zPsMSn$A6U}yU%a})IQ5-1@LY@T z2EF^8AAI&b%v=7x&UvWJlbq|xyvjM?gH1m_IVnHN@hI~=aeld-5&Bu>PdQG{O)NXN zu*$b`oF1>5_ttSW-esKJO;o#)&$#&f$^EyXwZDX=I}#r!#9OiAJ@S@386PLa=QRCX z*q99o=euOJo1ZS>38O389&f}(DLNKjB1@i{lLAxu63L%w^HjkX`gSl1)F46@hR=o zw)4ls#iu8<&$OkkU)QT~A?=gqW8xO`Ri2UdNmcmD>rW~#N&84SsdKs?dD*L>T7Sv9 zsB`UY2U{Lhd_^54=N;-UIqz@}M8-Myf#ta1KCm1|+y|EOa9%Iv=lNwNPBY6qoM+|$@-1^ zZ!(Xem(2g@CF?`(r^$Mf`*O1Wl z3-?RqI*#!u*L{p%xn3jA<@ydk%KCx)zI4R0mS|!_c7$Y z4RqxG822>f{uuWk7 zkW=oDA*b9QLr%FrhMdG3`%&CqB0kxV;{Fx!%zhO2uZVy4qqx5$_mjDQg@0u{5a&`J z;#|r@oJ)C#b19G9mnP2TJ~nYK_q~yy{X@RTByPz|>}zqK3OM^(+`j_OejWGjfU{r6 zy*uFS*KzL-IQw*Jv8hf^8o8U?#U+_IAF(` z#)}QsiQE@0)$7TI7@L&_>q_o9J7x17GpAdCK^@3<6QlO_NpH;EXppzL#~z;PSB2rZ zwi@JZ?!C9){_g#(R)Ge2n|t!#)AlQrsNh<2KahL2Wln5)RD8IbN%X`=xwOl%;9t)E|4|_c{5VC@5o%@;_Gm z@qdxxz9)*xm%M7uH1Yeld{6YI-N1uI9xpNZ@;#B1hwq7`JbX`-K%ThoiKIMyPbB5x zdm`kaj^aB$t8X9gyQu#I#Xbq&v*2IqaPFz5aSPpfuID0yx}AHi^$v%(e*AU0L0!!~ z*kxsHYt7iR+MurHyQJDv7ndK?Wt~C&!2SQHm!_Y)IBAJNy}|baz9HW`G+4IEpg!UI zg1V>r-g9fV#-N_zdxUF~T7SshW`h}@d?!VGvTw<~$}nxURpMdG4A!B*@fYh*;P{Jm zC~*A6IutnmVjT(`f3Xe)j=xy<0Y@IzeZY~2bsuo#VciEDd06)WM;_LFz>$Y~lJ7yP z=9>4_{`qEu`jhV+wkR!Gh!0*(_e0PA~$$QX$mTr+p z<*@iX0NmB>!s&J^;>Rg)pCb>-C2h9WU|q>~rsN;im3(JP{$X9occwD`K)-Y1GX*!y z4K(9i>PwtUeNFlzzY+h^4#Yp-YY_kJ6Y`w}o9J@Wse_B*1ysYte2%7 zSf}&7)z=QW%9sAR*kGN`_YMy>6&rA9{7QrMAm1bO+%?~8#-*hO>qow8X!Uu`z!M$( z4c4K2r^7g6y)5aFpZP9Q=4ZZ(l=+$OUu1sfyA|>?`xP>OvaU}cf873|oL|@@)NIGX7$pneUzP7yE>KuL^zkg`}O? zf0lk>e^vUC_~H9d{momUSBQwXD;auVtOad?D*J<_lR@ z;4f(({3Y!}K9TDhbG;DvyAaIR>^sT%o%rE*Ey(l48^3Eoo+oe1`iFIpoTr#yxn3h*$$ZFol=+i+P3p@$DDC6aC(HRCfBarbhto+Ov~JbZ zknp2r4~3st)6Dps`hlKjjrzS5yWRH&UQyo-jdlAtE@KN5o^P1*hS00(_tkr*xL0C% zJihnEIe`@1TdFNJkHRcs+nk1uwQ;)rfbeX!dQe&e^ z_xeZEDR`pk8*b*i7O59lS7&gY1Fptkzw;kbuZ_p&JgDv&`%xtozRlW*VZW4|9VVt) z@NIlk1HLIwhi|*5a*qCwc|9s8q6);tL$V zIP@JiBd@|oFXWHyTX0khrLQIV=l2ULa&k?J1o9t!?ee)pu6TWI*53@yqWmuL-TEE( zb5-=gM;`1vdhEDoy+=1T?XdVlg&)=mpSVS@*cCgsw^a1e3p=7I;RA<0`a)mE z?fR)pJ|4fAM4>^&C z`1#ayc%@gR0-xtPpafn}tL&g{5+S2%9 zJQ63wd5;T2f4sZyY5*sGfTJ&YK|D^9*2Y*?Wjx&z_ zK__>^t4qFotYchgd%U^FAAB2D8a%#dXJ;cc`ssw?3LcjJd$^SvFN?ptPBQ$uqX8Z0 zLnrU0<^`a$&$&Q*f8vZFj~heKRF~vE%NwHpY{~qk8sKaJO4a@>aO6 z#`U{=fdiu543my~^GxM#MC$##obzU0)7qpHqvvV0RpCEAcBRC^-mQ$hyuN9qPo-qy5*tatJyQb(6q2M-0pTD@Jr@XWYnL z{BfYdM=#{TK9M2E?d&2V_0l(|_qjXC)%36Zg(7Rb72mb@I$pH-*2wfr%01PFB~kKQ zf`_D-({#@F7N&op|ID^RL#JWQ4eakwFwcOuiq2S@7F+KROvo2H#BFTL9l<3OK6!(< zvQArL=>{bya7lmZy`E{Rc{exXBxYc<2@cA?=u4afM^0(yhPIn;=2_Ulz|QzZ!m)qQ z`y^r6#%l@U3pyK7Fp|NN3x?NLo0!z)@BIcwai#d)~!J{negbJ2JL+H%_m7f4-!G``8_G z_HTDScV_ziUH2Zt0qV!W?={V?@=q_K%(N1tkEB(0xiQF{fC zEK+!1dqwAGCf6Jlp0_flKeONZLcxQyE#1doiqs3QOnz$k%(f=JqyxT`hx&8J<8{?< zEBbYc*@U0C>}JX@=_99<2RgyaD`#%4xW`tm>MQ`e2S>Grs?f@4qWDCD%? zm3nTlvJY|sM_=mjdHd_9YoX`_m+f?Io|2#W{mA8;eNO)Yr%sl9@uRde^@g+$@=NQBXMe0QyN;w%{ay&v`#sl&8VEoLE`~QPpm$pv0@b*9GMLeJ{ z>l)zLhjow~*Tl1oAL8Uo?{)8E{)64*IAy%ce8RYp`Ifp!<~`uZPu`)dU_AnT@(kFct^5wbr&}3} zN7gmaG4pWT{zi>)^P&q*Z)FgNtUG|4`6_Pxi(cd<_8$TYyqe_Pu9d;~W!;K>kVopp zxL{omU+l@c3BKej?10~~o75Nku%Cc^kW38-YBtCj& zs###_x8$wOx<&ehIApzyov|nT1K5)|XP<;PCyz-#GOx+FB2HL$L!bFb`jPovju*zM zj1%ysUd&(E2YVtv>vZA}`K8~9TZxbS(r);X{R+k@@gVI<{zp#mPxqevBSOs!-VVVp zqg1^y=*gmr89TNx?pLr~VfB0c*e*pMAGMwZo_N)TYflw^?A+uT8Yy^>Gi57>K5Aj4 ztJ7=6Ck6Mck|W<01%FnoS8nTuElqgv`vos@DEP%!_cCt0-Q0jL^n2`$+@0=lOJm&! zZ=YaA2Yl$DZ@zPW$$IZs^=h34eJ1DhGV@r>wv_#^sNZL{?^4$;wBmSU()`P|byuo= zyfGzD)>FS{>z*Teyi*$Wd(_K21k75e^z!(5YT}kiJzvR6*=?o_G%kE<*6zysg!uP& zKODSpm4d%AJPsX+)H|228a>OZpV2<{^=ey1KgKpNQ-|GZpJ8OOA8+i37&~8_zA;PT zOZuif-TX&v+#0E;T^@eW=Fm73-}~_3Q+tDyUZW!~FIqX=u>I0BPja4Wu1BeZ$MHRJcb<=5NIlBwUF?vO6~1K0MWjf~BD=dn@n9pCzWbKI%y@9Vc@ zboHUe7~cbPeU%^m3+-;)_^|rDDMzc<>jw5Wrc`XWu8n^}I!=`jv`xG^A)U+bdp(<^ z;F3;xpDS%ME4Y-if4H7}in6n`+gqOs5#N-ZrQPta>-=-iXDB~5ySIJH@Zy6^|GK3~ z7aX%x)x%#8pFi;_-j9~-=2>G*+Laq3^|z1TY}$LEPrQGH{gGed%lPaw>0qjyqZ^v> zOq_RHy1^l9yGCX_GmaYOZJ0cBr6wjm@mxRU{q+O$sQouFj{0=ANxG_N;{@>!eK{VX z&pCoigUXrvsr@c~ryaQ0h8@9K)V>z*X+v}0`dydidv$r%Acdd4)R=-X7n&M54#ZwM zsNm#>fa3!f{9M%3@VMxk@vws1L@!OdUEz1|ot-``e=`#vXx-@2)&Bqo|7d6XP4y$1 z8n)rpC;g@aed_l8%O=_HQ*gI?rS!3iPNmGgF})@=GbB9vLD8`7!{X%(o;dgM9EA@Z zsb-Bfg*jTq{GX=yVf*{4i7e%m3|eO1JB1#ecl!0za# z@$x_id7y(mQ-?VFmMhuJ0Pf}8`u0Xer}P*Xn+6@@<&W*}_VN86^20ZHkxSA=fAE!l z8CJ2OUG6UNa)N)xSeDEouxSFm=!HD!i#)*nt+F-wy>Bn&2Tq(IC-#9a@kSiBn^ViL z_|t~wITvvVocY4-z{Nv5e#d9rd_n$?{n`Arr`m@E&UlRQjqLwU;WJ+_j)J>Hov*0y znUBDCIGFd9zk)M=!58|>M~pA%gO6T8i@tB1qUgx;JK&t(N&1X;;ONV^mihvh@-yD0 zzT^$?k)M1e^@2|L&qIa0e~-s2k8=IqCuh85uszaxk%GsRfBPeig5%%5MSDj~Rq)7) zpxJZ>;;}!nmBJKrJDSXDY z-p8ZBLCBj3X((CHmk$I@0OwC`CuwO~SF8^dt6{@*sbcF3&qGQS_yqu_tiyjMNvuOMK>w z)ghAyrc~b#x@0`6??|bBpRoLg*>+yT^mJc7lv$#_$BYdbTjl*bwa?#p_Lx$ahUhcv zMvW`4zTbRfWj~_b(?~u40;`6@KnoI9!Smpz|07?a^^q`q$Uv5nqM`sq(D7f&&Gqnf`; zxAJp;6{-Iin{3bOrCm(D29!EnvB4+xyP$FM>|Hu5`jx8Fg4&1HI2skHkJ~eCQSND7 z4BrBiU)@&qQgEFXbsTT0|6fpU!_bD+dKmeujyhdU;k#T~G3i*zL(3ARNe2X-}ziNwt0boZbnkK;)8oDe4BxX%Ky9{Pbca^;}*?+)5-HVBd)uBXp}yyhIh13S(<5cKB=4^7AKBZqL+s`S zzI8(5_w-@QRE?1^83st*m@sp>TRLOg(9<#3_kwnh5J)3?g7HCE+G z=ua=>Ji3SSd(7I+y^1P%u!E$Na`NkO_qvZZ*Qd}A@+eh!*n}ZQu{^Wt9#i?#Z9&>v z@0GrRvAx@*bR1zEIeIZ)```A@nB@LAm6xWr?Di&A*Fna-G5MVBmE9bgygU}7@;~;0 zufvn=1-dIa@gs20N1L8ntNe)_GS8g3?U9PBp!?|q+ux1%@5%4ApPp9skF~Koa!2KV z{FQk2wzmC#uitM>d|y@h9sB#{?y&x>g3EZG_io@?YgIqU_=_jG=z9`14`NU3@7eM8qPY*^?T>wA{9_*( z&)7%CC*#i5dE3}M>iz)nbI2w5R7)kljDO%#e%qyC8Rx2TB*z8gOZtoPBK^hq3d(w@ zb9puY5om-|#9 z*9)$29nn&UKKo`_k0;t5)w+v`&%Rvrk(%B5W*Vr={kPR$Hm|tWt%ol6>jL`kOYNFT-#gbW;QWWaar<&L z@~lrg|L`zf?oYy3?oYy3?oYy({a(q}-0u~9<$f=Ako&#ZP43fTXW;BB;xCi0@Go+* zZ@jZzx+?|JbvFIWzV{NJ%A>zk?q|Z;_x7vPae0Qm!%aI&I^?~G{e6yiSQn|6x}5dg zv87$j`78EUcpXg)=ln*=jddRhrRa=8nQykIkiackv4DK&ALL;C-22Zclyv>?USc1 z&}UIPzv>2c5%ROoKz)V2z^TWu4{-8~L#N}BU-m`nslv1SXLasokXQ5hPN|ei+3ou+ z$6?z8>l);-h|Mj1g4O@Y`aW~{ZXbt+W?d@fVZMlsC~@KaLA4&apif;_z|*W(rJU5O zQcmXCh?hUs-`N|fTcz)}J1W{)XI{oW*&e5OnYm0`v))7gmD5-KIAP~z*1gD|XXon< zSMIhn;{m=a^#(RI*0(dH{2yvh+R(|*MJHb&|I<Snr(w~Z5geRrJiX~;Oi4$_a< zLHZFpP;bb1W*)@;Ln;Pbx>2fD0y*D%=jxuMac_gV0{kv_UWAo?(8jE*Wjx4nZSCGS zw9@Tjx{S|=&@;9*Y|81>QP9CJk}q;H50W>aPkq8T#b4AXj8ptYUB@^jKTzkRFY!rT z$M`~i>VN#pcqFbO>IGE(5D>qA2;BN&%aliUC4}d*y0v(*8jsMSjzZ4V2X2$RdT|&dbglo3l;sMVeUguD|z-UIym5Ju6BmhE8C%g zkIO`~Hahkz@@bf&xA)|Ig% z+jjLo5zjZ-*z>6=6?jnKf@U6p_9Tj9p;GD;roR&aN#rTvurF8hw%sjTD&F8BA`&Yf>oG;=wF z{Ym!Ic9hw8Y}v%p=Kdu6Z~5|M@BXBEMxFgM@V~CVGGfF}S6%KaTHVRl{d=sl&i)?z zkigmBW54cH_p56izSPy_zG8Ix!mnF=Yi9Cg9}hX9&;H?nU-|AmtXkCEkA*(+v){{p zX7Km#K^uxz)QQ{35*gEIYJaA0?^`3H%JeqlC-Thouv%)L3OIgG^k&wevDf>t{Q%>*Kv8LLmLQdizI?I*~ipuWY z(;P3r7r1{2arWtA+8MpD^P}V!N0k1(zf{aGeN$zJT6&osqxL%+8LxaAo8>qBX8pMv zlpja#S+y<1-rWE{=v>naWtBYO&)oO^a65(X(4j}#8%lo0qom_F#W9Qj{F){m_>OG7 zKC<1)wkA9%yzH4b%KrEE{mOp+uKI2(PM)9~D;nNb`zO$8vOK)Q0L9nyP*`B3I_Djf z@j=!})mj*3^Ob8dT*0ve@*pR0$0@~M`>&~Md@g=|ep7{Szu$jhaTO=uEA8#QcV)c< z@Re;JBso^Rb-bK$c0hjUdoP@DX|ozH@P)olwYqiPlzpHxd`5+{`&E7mcC1j|N$J}q zU&QT+$=VyxLC%qTidL^*)GdJ>@C$ZutDL=jfbt`Dz+WkC_hp^0#?k2UqpPoQXl%eY zcJJ=M+G<|{z7ik%gO8js?u!O@yi(1y4|-uYDNl>gy|v4XX%cU@IR7FK`1lcg_#!8K z%{&m7uR2wZ$a2R!YkXdc%WuGC-aB#sQ`Nxk&2*VJ+^W49S29;cbG(yJoUhr$zS^2h zXS_3xPHn5UF~f|8x*YFrJwLDY@-3HOya)R<&po(q1(ObW13EI#z?XTDc<_(he5G%X zO!4_V&JO6syiXo}lYGWvJ9oPTc^G}cC*Ok4JdVC$%a(1=bEAw-z9O%#J-ff~WS?3( z^Ei36lika)>oeyx^AGw$N9M5#;~QrwUb3-CpL{6klMkVfUxJ6v@`{dD_v_;75cG2J zcWKs3?UPe4A%EEQ%~>Bt#@BV|ySTvY9!J!EICO}Uh~k;%=2rXR)IaF!RkmO2*NQ%M zBk|_4)(C#5_E%*+hCI|`l0J2pl!y9|dBk;hvIj0|ADcWw9KM-8yLMkCCve7#&BWtd zGpl_?>Q&;(;iz@d=IT5beEj0&SmalIwI2>#>PwwQ+&ZV~uqB(KgMB1l>>%xfJkl@J zhmt<_k$%TM)M=a}Q;$&xa!xJlEAVA~#rZY$74;$K*U$kDed zvo4^%`Zcv{eiwz$`ir`Vbu@K5^$+w}mr-Ye&-#eEC3Ba7Q_sw@=~2E zv0kGts++gk;o(Y7)<@Khsd_y`?JoaK7_B7(+GNc?f3USo^Py1 zRaNJ_)HAt;97+B}$zQZn+U(oZ`jooGn02_q0wsUY$<93je&fg0hxhMpE7VNE!^-yS zH{p-<^}O#MwWlgN5`W<4p)G%(8%lh;ENx%aQt}*I`uK4^^fF>eWcxxr+ikA z!Q~WvX}1Lzk~}%4;F7-77x|@~se7egsJo>6)K`;QcG*@!>4m=3dlHTvq}`~KrJeDM zv?uhXzQ`lv0llQ%&2%6tW1@&@?z@?q?mWk~t7#wBci0Dgr9H{pQZMqPv;+QPU5p*TXMKvk>%OTQ~I5Fkak8d?66|(ic*&XyBWlT)C+v}Nw5RI=inTY_t|{455Vtf z@H+`!`mEwJ)V>72%fat;MC>{@C0y;#0O$8WJZ%~uZmjkn%-;iv`~3}n?@Ya4DLB8s z0pGw1-(PJ|^!dFG_(GrG3E_7|pu_KznDE65&$%jjpwI7DKp(xhAISH-{N4rk{rFu4 z`THJxR|{Oy;rBGS2gvV8@cSOzKjilo;LCkNzB^{U!~I71@_QhXKELn5Jwv_=hK}Tm zoKj!tOMCKr8q%Kp?uYajzo&tJi7SauT=BacCf~R?;rBgcynzpW{LAl+@Ov-B3G&PM z&ee5fIa-!vZnecJDoQi*T`W4{sxu$%=7W}PU`n4E5 zBixC8-;)pU86E!7sp;`2Kg)Iscbb!`?U%x1KRW^MUi;Uf^WQ(2&*1SM{NqcH3{O+) zvs3Aw?q|}^4tJ{QUTEaVh;XNQ_nbS}F9>(yJ#gsK(vN5O(4S}YKu`4GS=xd3=uLlG z>L8-*?iA4t?Y;Wr9H7Te#6iBg=g6xI`re2e&FM0{0Bct7d+sW^gu7(Te1iCqD4OF zBPY*N51zq8A6oh$H+rEbEpYnLN_lvOo{R_LO2!NEDB}%0T1ij#qd#!);mb4Q20Z8! zx8T##5BlH%M{n#T?TY>Ka+k0H+_%wBYj|d5{~qfFmEz;31!EksrL#xmu05@vDgD@$~BECnK%3 zXN5B^{a*IHQ@T^tUtT;>Lc45p_&_o1%o@)DH5xU!{3x?_EY*>`S?a!WO6zqZVxv_t zZDaN?xs#O6sO@-=^JvdR*PRA=7rZ?7WdSXs_1D)sex@?<@1`r1^7he88g0kKm9sBN z^166VqtO(dbpTUuB2X(0o57 zYUO{Vq-N(duYYiLVmd>%9Md1ftH-nGTweJQB{r$6s`{{Gy4 zu6>eH8ZG^J|DmMg)xjmQXa`$9ujk⪙-B!tWT;tH8e|b{DnQS4}M}?NV^ah*o9~8 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 z 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�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 literal 0 HcmV?d00001 diff --git a/tests/weights.cxx b/tests/weights.cxx new file mode 100644 index 000000000..79aed67e2 --- /dev/null +++ b/tests/weights.cxx @@ -0,0 +1,226 @@ +#include "ut_common.hpp" +#include +#include +#include +#include +#include +#include + +#include "host/host_weights.hpp" + +#ifdef GAUXC_ENABLE_CUDA +#include +#include +#include "cuda/cuda_weights.hpp" +#endif + +using namespace GauXC; + +struct ref_weights_data { + Molecule mol; + std::shared_ptr meta; + std::vector< XCTask > tasks_unm; + std::vector< XCTask > tasks_mod; // This is only the weights + + template + void load( Archive& ar ) { + ar( mol, tasks_unm, tasks_mod ); + meta = std::make_shared(mol); + } + template + void save( Archive& ar ) const { + ar( mol, tasks_unm, tasks_mod ); + } +}; + + +void generate_weights_data( const Molecule& mol, const BasisSet& basis, + std::ofstream& out_file, size_t ntask_save = 15 ) { + + + MolGrid mg(AtomicGridSizeDefault::FineGrid, mol); + LoadBalancer lb(MPI_COMM_WORLD, mol, mg, basis); + auto& tasks = lb.get_tasks(); + + ref_weights_data ref_data; + ref_data.mol = mol; + + auto abs_comparator = []( const auto& a, const auto& b ) { + return std::abs(a) < std::abs(b); + }; + + std::sort( tasks.begin(), tasks.end(), + [&]( const auto& a, const auto& b ) { + auto a_max = + *std::max_element( a.weights.begin(), a.weights.end(), + abs_comparator ); + auto b_max = + *std::max_element( b.weights.begin(), b.weights.end(), + abs_comparator ); + + return a_max < b_max; + }); + + if( tasks.size() > ntask_save ) + tasks.erase( tasks.begin() + ntask_save, tasks.end() ); + + ref_data.tasks_unm = tasks; // Make a copy of un modified tasks + + integrator::host::partition_weights_host( XCWeightAlg::SSF, + mol, lb.molmeta(), tasks ); + + // Clear out unneeded data + for( auto& task : tasks ) { + task.points.clear(); + task.shell_list.clear(); + } + ref_data.tasks_mod = tasks; + + { + cereal::BinaryOutputArchive ar( out_file ); + ar( ref_data ); + } + +} + + +void test_host_weights( std::ifstream& in_file ) { + + ref_weights_data ref_data; + { + cereal::BinaryInputArchive ar( in_file ); + ar( ref_data ); + } + + integrator::host::partition_weights_host( XCWeightAlg::SSF, + ref_data.mol, *ref_data.meta, ref_data.tasks_unm ); + + + size_t ntasks = ref_data.tasks_unm.size(); + for( size_t itask = 0; itask < ntasks; ++itask ) { + auto& task = ref_data.tasks_unm.at(itask); + auto& ref_task = ref_data.tasks_mod.at(itask); + + size_t npts = task.weights.size(); + for( size_t i = 0; i < npts; ++i ) { + CHECK( task.weights.at(i) == + Approx(ref_task.weights.at(i)) ); + } + } + +} + +#ifdef GAUXC_ENABLE_CUDA +void test_cuda_weights( std::ifstream& in_file ) { + + ref_weights_data ref_data; + { + cereal::BinaryInputArchive ar( in_file ); + ar( ref_data ); + } + + std::vector< std::array > points; + std::vector< double > weights, weights_ref; + std::vector< double > dist_nearest; + std::vector< int32_t > iparent; + + for( auto& task : ref_data.tasks_unm ) { + points.insert( points.end(), + task.points.begin(), + task.points.end() ); + weights.insert( weights.end(), + task.weights.begin(), + task.weights.end() ); + + size_t npts = task.points.size(); + dist_nearest.insert( dist_nearest.end(), npts, + task.dist_nearest ); + iparent.insert( iparent.end(), npts, task.iParent ); + } + + for( auto& task : ref_data.tasks_mod ) { + weights_ref.insert( weights_ref.end(), + task.weights.begin(), + task.weights.end() ); + } + + size_t npts = points.size(); + size_t natoms = ref_data.mol.natoms(); + + std::vector< double > coords( 3 * natoms ); + for( auto iat = 0 ; iat < natoms; ++iat ) { + coords[ 3*iat + 0 ] = ref_data.mol.at(iat).x; + coords[ 3*iat + 1 ] = ref_data.mol.at(iat).y; + coords[ 3*iat + 2 ] = ref_data.mol.at(iat).z; + } + + + auto* points_d = util::cuda_malloc( 3*npts ); + auto* weights_d = util::cuda_malloc( npts ); + auto* iparent_d = util::cuda_malloc( npts ); + auto* distnea_d = util::cuda_malloc( npts ); + auto* rab_d = util::cuda_malloc( natoms*natoms ); + auto* coords_d = util::cuda_malloc( 3*natoms ); + auto* dist_scr_d= util::cuda_malloc( npts*natoms ); + + util::cuda_copy( 3*npts, points_d, points.data()->data() ); + util::cuda_copy( npts, weights_d, weights.data() ); + util::cuda_copy( npts, iparent_d, iparent.data() ); + util::cuda_copy( npts, distnea_d, dist_nearest.data() ); + util::cuda_copy( natoms*natoms, rab_d, + ref_data.meta->rab().data() ); + util::cuda_copy( 3*natoms, coords_d, coords.data() ); + + cudaStream_t stream = 0; + integrator::cuda::partition_weights_cuda_SoA( + XCWeightAlg::SSF, npts, natoms, points_d, + iparent_d, distnea_d, rab_d, coords_d, + weights_d, dist_scr_d, stream ); + + util::cuda_device_sync(); + util::cuda_copy( npts, weights.data(), weights_d ); + util::cuda_free( points_d, weights_d, iparent_d, distnea_d, + rab_d, coords_d, dist_scr_d ); + + for( auto i = 0ul; i < npts; ++i ) + CHECK( weights.at(i) == Approx( weights_ref.at(i) ) ); + +} +#endif + +//#define GENERATE_TESTS +TEST_CASE( "Benzene", "[weights]" ) { + +#ifdef GENERATE_TESTS + int world_size; + MPI_Comm_size( MPI_COMM_WORLD, &world_size ); + if( world_size > 1 ) return; +#endif + + Molecule mol = make_benzene(); + +#ifdef GENERATE_TESTS + BasisSet basis = make_631Gd( mol, SphericalType(true) ); + for( auto& sh : basis ) sh.set_shell_tolerance( 1e-6 ); + + std::ofstream ref_data( "benzene_weights_ssf.bin", std::ios::binary ); + generate_weights_data( mol, basis, ref_data ); +#else + + std::ifstream ref_data( GAUXC_REF_DATA_PATH "/benzene_weights_ssf.bin", + std::ios::binary ); + + SECTION( "Host Weights" ) { + test_host_weights( ref_data ); + } + +#ifdef GAUXC_ENABLE_CUDA + SECTION( "Device Weights" ) { + test_cuda_weights( ref_data ); + } +#endif + +#endif +} + + From 523ec2a1538ea3298b2aa0800b006dfee2c44eaf Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Fri, 4 Sep 2020 14:42:12 -0400 Subject: [PATCH 26/27] CUDA XC Integrator Works --- include/gauxc/util/cublas_util.hpp | 8 + include/gauxc/util/cuda_util.hpp | 7 + include/gauxc/xc_integrator/xc_cuda_data.hpp | 1 + src/integrator/cuda/collocation_device.hpp | 1 + src/integrator/cuda/cublas_extensions.cu | 123 ++++++++++ src/integrator/cuda/cublas_extensions.hpp | 39 +++ src/integrator/cuda/cuda_eval_denvars.cu | 231 ++++++++++++++++++ src/integrator/cuda/cuda_eval_denvars.hpp | 36 +++ src/integrator/cuda/cuda_inc_potential.cu | 85 +++++++ src/integrator/cuda/cuda_inc_potential.hpp | 20 ++ src/integrator/cuda/cuda_pack_density.cu | 83 +++++++ src/integrator/cuda/cuda_pack_density.hpp | 19 ++ src/integrator/cuda/cuda_zmat.cu | 138 +++++++++++ src/integrator/cuda/cuda_zmat.hpp | 26 ++ .../cuda/gauxc-cuda_integrator.cmake | 32 +-- src/integrator/cuda/xc_cuda_data.cxx | 49 ++-- src/integrator/cuda/xc_cuda_util.cxx | 175 ++++++++++++- src/integrator/host/xc_host_util.cxx | 17 +- tests/xc_integrator.cxx | 7 + 19 files changed, 1031 insertions(+), 66 deletions(-) create mode 100644 src/integrator/cuda/cublas_extensions.cu create mode 100644 src/integrator/cuda/cublas_extensions.hpp create mode 100644 src/integrator/cuda/cuda_eval_denvars.cu create mode 100644 src/integrator/cuda/cuda_eval_denvars.hpp create mode 100644 src/integrator/cuda/cuda_inc_potential.cu create mode 100644 src/integrator/cuda/cuda_inc_potential.hpp create mode 100644 src/integrator/cuda/cuda_pack_density.cu create mode 100644 src/integrator/cuda/cuda_pack_density.hpp create mode 100644 src/integrator/cuda/cuda_zmat.cu create mode 100644 src/integrator/cuda/cuda_zmat.hpp diff --git a/include/gauxc/util/cublas_util.hpp b/include/gauxc/util/cublas_util.hpp index 787030bf3..7c01c7bf9 100644 --- a/include/gauxc/util/cublas_util.hpp +++ b/include/gauxc/util/cublas_util.hpp @@ -29,6 +29,14 @@ struct cublas_handle { }; + +inline static cudaStream_t get_stream( cublasHandle_t handle ) { + cudaStream_t stream; + auto stat = cublasGetStream(handle, &stream ); + GAUXC_CUBLAS_ERROR("CUBLAS GET STREAM FAILED", stat ); + return stream; +} + } } diff --git a/include/gauxc/util/cuda_util.hpp b/include/gauxc/util/cuda_util.hpp index 011782985..736ffcaa9 100644 --- a/include/gauxc/util/cuda_util.hpp +++ b/include/gauxc/util/cuda_util.hpp @@ -85,6 +85,13 @@ inline void cuda_copy_async( size_t len, T* dest, const T* src, cudaStream_t s, template inline void cuda_set_zero( size_t len, T* ptr, std::string m = "" ) { auto stat = cudaMemset( ptr, 0, len * sizeof(T) ); + GAUXC_CUDA_ERROR( "CUDA Memset Failed ["+m+"]", stat ); +} + +template +inline void cuda_set_zero_async( size_t len, T* ptr, cudaStream_t stream, + std::string m = "" ) { + auto stat = cudaMemsetAsync( ptr, 0, len * sizeof(T), stream ); GAUXC_CUDA_ERROR( "CUDA Memset Async Failed ["+m+"]", stat ); } diff --git a/include/gauxc/xc_integrator/xc_cuda_data.hpp b/include/gauxc/xc_integrator/xc_cuda_data.hpp index 3d784bada..143e17fc9 100644 --- a/include/gauxc/xc_integrator/xc_cuda_data.hpp +++ b/include/gauxc/xc_integrator/xc_cuda_data.hpp @@ -55,6 +55,7 @@ struct XCCudaData { F* exc_device = nullptr; F* nel_device = nullptr; + F* acc_scr_device = nullptr; F* rab_device = nullptr; F* coords_device = nullptr; diff --git a/src/integrator/cuda/collocation_device.hpp b/src/integrator/cuda/collocation_device.hpp index 4e0a40242..9a8957bac 100644 --- a/src/integrator/cuda/collocation_device.hpp +++ b/src/integrator/cuda/collocation_device.hpp @@ -1,3 +1,4 @@ +#pragma once #include #include diff --git a/src/integrator/cuda/cublas_extensions.cu b/src/integrator/cuda/cublas_extensions.cu new file mode 100644 index 000000000..da53d056a --- /dev/null +++ b/src/integrator/cuda/cublas_extensions.cu @@ -0,0 +1,123 @@ +#include "cublas_extensions.hpp" +#include +#include +#include + +namespace GauXC { +namespace cuda { +namespace blas { + +template +__global__ void increment_kernel( const T* X, T* Y ) { + const auto tid = blockIdx.x; + if( tid < 1 ) (*Y) += (*X); +} + +template +void increment( const T* X, T* Y, cudaStream_t stream ) { + increment_kernel<<<1,1,0,stream>>>(X,Y); +} + +template <> +void dot( cublasHandle_t handle, + int N, + const double* X, + int INCX, + const double* Y, + int INCY, + double* RES ) { + + auto stat = cublasDdot( handle, N, X, INCX, Y, INCY, RES ); + GAUXC_CUBLAS_ERROR("CUBLAS DDOT FAILED", stat ); + +} + +template +void gdot( cublasHandle_t handle, + int N, + const T* X, + int INCX, + const T* Y, + int INCY, + T* SCR, + T* RES ) { + + dot( handle, N, X, INCX, Y, INCY, SCR ); + auto stream = util::get_stream(handle); + increment( SCR, RES, stream ); + +} + +template +void gdot( cublasHandle_t handle, + int N, + const double* X, + int INCX, + const double* Y, + int INCY, + double* SCR, + double* RES ); + + + + + + + + + + +template +void __global__ hadamard_product_kernel( int M, + int N, + const T* A, + int LDA, + T* B, + int LDB ) { + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x < M and tid_y < N ) { + B[ tid_x + tid_y*LDB ] *= A[ tid_x + tid_y*LDA ]; + } + +} + + + +template +void hadamard_product( cublasHandle_t handle, + int M, + int N, + const T* A, + int LDA, + T* B, + int LDB ) { + + auto stream = util::get_stream(handle); + dim3 threads( 32, 32 ); + dim3 blocks( util::div_ceil( M, threads.x ), + util::div_ceil( N, threads.y ) ); + + hadamard_product_kernel<<< blocks, threads, 0, stream >>>( M, N, A, LDA, B, LDB ); + +} + +template +void hadamard_product( cublasHandle_t handle, + int M, + int N, + const double* A, + int LDA, + double* B, + int LDB ); + + + + + +} +} +} + diff --git a/src/integrator/cuda/cublas_extensions.hpp b/src/integrator/cuda/cublas_extensions.hpp new file mode 100644 index 000000000..dcad42ddc --- /dev/null +++ b/src/integrator/cuda/cublas_extensions.hpp @@ -0,0 +1,39 @@ +#pragma once +#include + +namespace GauXC { +namespace cuda { +namespace blas { + +template +void dot( cublasHandle_t handle, + int N, + const T* X, + int INCX, + const T* Y, + int INCY, + T* RES ); + +template +void gdot( cublasHandle_t handle, + int N, + const T* X, + int INCX, + const T* Y, + int INCY, + T* SCR, + T* RES ); + + +template +void hadamard_product( cublasHandle_t handle, + int M, + int N, + const T* A, + int LDA, + T* B, + int LDB ); + +} +} +} diff --git a/src/integrator/cuda/cuda_eval_denvars.cu b/src/integrator/cuda/cuda_eval_denvars.cu new file mode 100644 index 000000000..637b48669 --- /dev/null +++ b/src/integrator/cuda/cuda_eval_denvars.cu @@ -0,0 +1,231 @@ +#include "cuda_eval_denvars.hpp" +#include "cuda_extensions.hpp" +#include + + +namespace GauXC { +namespace integrator { +namespace cuda { + +template +__global__ void eval_uvars_lda_kernel( size_t ntasks, + XCTaskDevice* tasks_device ) { + + const int batch_idx = blockIdx.z; + if( batch_idx >= ntasks ) return; + + auto& task = tasks_device[ batch_idx ]; + + const auto npts = task.npts; + const auto nbf = task.nbe; + + auto* den_eval_device = task.den; + + const auto* basis_eval_device = task.bf; + + const auto* den_basis_prod_device = task.zmat; + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + register double den_reg = 0.; + + if( tid_x < nbf and tid_y < npts ) { + + const double* bf_col = basis_eval_device + tid_y*nbf; + const double* db_col = den_basis_prod_device + tid_y*nbf; + + den_reg = bf_col[ tid_x ] * db_col[ tid_x ]; + + } + + // Warp blocks are stored col major + den_reg = 2 * warpReduceSum( den_reg ); + + + if( threadIdx.x == 0 and tid_y < npts ) { + atomicAdd( den_eval_device + tid_y, den_reg ); + } + + +} + +template +__global__ void eval_uvars_gga_kernel( size_t ntasks, + XCTaskDevice* tasks_device ) { + + const int batch_idx = blockIdx.z; + if( batch_idx >= ntasks ) return; + + auto& task = tasks_device[ batch_idx ]; + + const auto npts = task.npts; + const auto nbf = task.nbe; + + auto* den_eval_device = task.den; + auto* den_x_eval_device = task.ddenx; + auto* den_y_eval_device = task.ddeny; + auto* den_z_eval_device = task.ddenz; + + const auto* basis_eval_device = task.bf; + const auto* dbasis_x_eval_device = task.dbfx; + const auto* dbasis_y_eval_device = task.dbfy; + const auto* dbasis_z_eval_device = task.dbfz; + + const auto* den_basis_prod_device = task.zmat; + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + register double den_reg = 0.; + register double dx_reg = 0.; + register double dy_reg = 0.; + register double dz_reg = 0.; + + if( tid_x < nbf and tid_y < npts ) { + + const double* bf_col = basis_eval_device + tid_y*nbf; + const double* bf_x_col = dbasis_x_eval_device + tid_y*nbf; + const double* bf_y_col = dbasis_y_eval_device + tid_y*nbf; + const double* bf_z_col = dbasis_z_eval_device + tid_y*nbf; + const double* db_col = den_basis_prod_device + tid_y*nbf; + + den_reg = bf_col[ tid_x ] * db_col[ tid_x ]; + dx_reg = bf_x_col[ tid_x ] * db_col[ tid_x ]; + dy_reg = bf_y_col[ tid_x ] * db_col[ tid_x ]; + dz_reg = bf_z_col[ tid_x ] * db_col[ tid_x ]; + + } + + // Warp blocks are stored col major + den_reg = 2 * warpReduceSum( den_reg ); + dx_reg = 4 * warpReduceSum( dx_reg ); + dy_reg = 4 * warpReduceSum( dy_reg ); + dz_reg = 4 * warpReduceSum( dz_reg ); + + + if( threadIdx.x == 0 and tid_y < npts ) { + atomicAdd( den_eval_device + tid_y, den_reg ); + atomicAdd( den_x_eval_device + tid_y, dx_reg ); + atomicAdd( den_y_eval_device + tid_y, dy_reg ); + atomicAdd( den_z_eval_device + tid_y, dz_reg ); + } + + +} + + +template +__global__ void eval_vvars_gga_kernel( + size_t npts, + const T* den_x_eval_device, + const T* den_y_eval_device, + const T* den_z_eval_device, + T* gamma_eval_device +) { + + const int tid = threadIdx.x + blockIdx.x * blockDim.x; + if( tid < npts ) { + + const double dx = den_x_eval_device[ tid ]; + const double dy = den_y_eval_device[ tid ]; + const double dz = den_z_eval_device[ tid ]; + + gamma_eval_device[tid] = dx*dx + dy*dy + dz*dz; + + } + +} + + + + +template +void eval_uvars_lda_device( size_t ntasks, + size_t max_nbf, + size_t max_npts, + XCTaskDevice* tasks_device, + cudaStream_t stream ) { + + dim3 threads( 32, 32, 1 ); + dim3 blocks( util::div_ceil( max_nbf , 32 ), + util::div_ceil( max_npts , 32 ), + ntasks ); + + eval_uvars_lda_kernel<<< blocks, threads, 0, stream >>>( ntasks, tasks_device ); + +} + +template +void eval_uvars_gga_device( size_t ntasks, + size_t max_nbf, + size_t max_npts, + XCTaskDevice* tasks_device, + cudaStream_t stream ) { + + dim3 threads( 32, 32, 1 ); + dim3 blocks( util::div_ceil( max_nbf , 32 ), + util::div_ceil( max_npts , 32 ), + ntasks ); + + eval_uvars_gga_kernel<<< blocks, threads, 0, stream >>>( ntasks, tasks_device ); + +} + + +template +void eval_vvars_gga_device( size_t npts, + const T* den_x_device, + const T* den_y_device, + const T* den_z_device, + T* gamma_device, + cudaStream_t stream ) { + + dim3 threads( 1024 ); + dim3 blocks( util::div_ceil( npts, 1024 ) ); + + eval_vvars_gga_kernel<<< blocks, threads, 0, stream >>>( + npts, den_x_device, den_y_device, den_z_device, gamma_device + ); + +} + + + + + + + + + + + + + + + +template +void eval_uvars_lda_device( size_t ntasks, + size_t max_nbf, + size_t max_npts, + XCTaskDevice* tasks_device, + cudaStream_t stream ); + +template +void eval_uvars_gga_device( size_t ntasks, + size_t max_nbf, + size_t max_npts, + XCTaskDevice* tasks_device, + cudaStream_t stream ); + +template +void eval_vvars_gga_device( size_t npts, + const double* den_x_device, + const double* den_y_device, + const double* den_z_device, + double* gamma_device, + cudaStream_t stream ); + +} +} +} diff --git a/src/integrator/cuda/cuda_eval_denvars.hpp b/src/integrator/cuda/cuda_eval_denvars.hpp new file mode 100644 index 000000000..e08874f76 --- /dev/null +++ b/src/integrator/cuda/cuda_eval_denvars.hpp @@ -0,0 +1,36 @@ +#pragma once +#include + +namespace GauXC { +namespace integrator { +namespace cuda { + +using namespace GauXC::cuda; + +template +void eval_uvars_lda_device( size_t ntasks, + size_t max_nbf, + size_t max_npts, + XCTaskDevice* tasks_device, + cudaStream_t stream ); + +template +void eval_uvars_gga_device( size_t ntasks, + size_t max_nbf, + size_t max_npts, + XCTaskDevice* tasks_device, + cudaStream_t stream ); + + +template +void eval_vvars_gga_device( size_t npts, + const T* den_x_device, + const T* den_y_device, + const T* den_z_device, + T* gamma_device, + cudaStream_t stream ); + + +} +} +} diff --git a/src/integrator/cuda/cuda_inc_potential.cu b/src/integrator/cuda/cuda_inc_potential.cu new file mode 100644 index 000000000..78f1e242d --- /dev/null +++ b/src/integrator/cuda/cuda_inc_potential.cu @@ -0,0 +1,85 @@ +#include "cuda_inc_potential.hpp" + +namespace GauXC { +namespace integrator { +namespace cuda { + + +template +__global__ void inc_by_submat_combined_kernel( size_t ntasks, + XCTaskDevice* device_tasks, + T* A, + size_t LDA ) { + + const int batch_id = blockIdx.z; + + if( batch_id < ntasks ) { + + auto& task = device_tasks[ batch_id ]; + + const auto ncut = task.ncut; + const auto* submat_cut_device = task.submat_cut; + const auto LDAS = task.nbe; + auto* ASmall_device = task.nbe_scr; + + //if( LDAS == LDAB ) return; + + + const int tid_x = blockDim.x * blockIdx.x + threadIdx.x; + const int tid_y = blockDim.y * blockIdx.y + threadIdx.y; + + int64_t i(0); + for( size_t i_cut = 0; i_cut < ncut; ++i_cut ) { + const int64_t i_cut_first = submat_cut_device[ 2*i_cut ]; + const int64_t i_cut_second = submat_cut_device[ 2*i_cut + 1 ]; + const int64_t delta_i = i_cut_second - i_cut_first; + + int64_t j(0); + for( size_t j_cut = 0; j_cut < ncut; ++j_cut ) { + const int64_t j_cut_first = submat_cut_device[ 2*j_cut ]; + const int64_t j_cut_second = submat_cut_device[ 2*j_cut + 1 ]; + const int64_t delta_j = j_cut_second - j_cut_first; + + auto* ASmall_begin = ASmall_device + i + j *LDAS; + auto* ABig_begin = A + i_cut_first + j_cut_first*LDA ; + + for( size_t J = tid_y; J < delta_j; J += blockDim.y ) + for( size_t I = tid_x; I < delta_i; I += blockDim.x ) + //ABig_begin[I + J*LDA] += ASmall_begin[I + J*LDAS]; + atomicAdd( ABig_begin + I + J*LDA, ASmall_begin[I+J*LDAS] ); + + j += delta_j; + } + i += delta_i; + } + + } // batch_id check +} + + +template +void task_inc_potential( size_t ntasks, + XCTaskDevice* device_tasks, + T* V_device, + size_t LDV, + cudaStream_t stream ) { + + dim3 threads(32,32,1), blocks(1,1,ntasks); + inc_by_submat_combined_kernel<<< blocks, threads, 0, stream >>>( + ntasks, device_tasks, V_device, LDV + ); + + +} + +template +void task_inc_potential( size_t ntasks, + XCTaskDevice* device_tasks, + double* V_device, + size_t LDV, + cudaStream_t stream ); + +} +} +} + diff --git a/src/integrator/cuda/cuda_inc_potential.hpp b/src/integrator/cuda/cuda_inc_potential.hpp new file mode 100644 index 000000000..53d7b069f --- /dev/null +++ b/src/integrator/cuda/cuda_inc_potential.hpp @@ -0,0 +1,20 @@ +#pragma once +#include + +namespace GauXC { +namespace integrator { +namespace cuda { + +using namespace GauXC::cuda; + +template +void task_inc_potential( size_t ntasks, + XCTaskDevice* device_tasks, + T* V_device, + size_t LDV, + cudaStream_t stream ); + +} +} +} + diff --git a/src/integrator/cuda/cuda_pack_density.cu b/src/integrator/cuda/cuda_pack_density.cu new file mode 100644 index 000000000..42689d75b --- /dev/null +++ b/src/integrator/cuda/cuda_pack_density.cu @@ -0,0 +1,83 @@ +#include "cuda_pack_density.hpp" + +namespace GauXC { +namespace integrator { +namespace cuda { + + +template +__global__ void submat_set_combined_kernel( size_t ntasks, + XCTaskDevice* device_tasks, + T* A, + size_t LDA ) { + + const int batch_id = blockIdx.z; + + if( batch_id < ntasks ) { + + auto& task = device_tasks[ batch_id ]; + + const auto ncut = task.ncut; + const auto* submat_cut_device = task.submat_cut; + const auto LDAS = task.nbe; + auto* ASmall_device = task.nbe_scr; + + //if( LDAS == LDAB ) return; + + + const int tid_x = blockDim.x * blockIdx.x + threadIdx.x; + const int tid_y = blockDim.y * blockIdx.y + threadIdx.y; + + int64_t i(0); + for( size_t i_cut = 0; i_cut < ncut; ++i_cut ) { + const int64_t i_cut_first = submat_cut_device[ 2*i_cut ]; + const int64_t i_cut_second = submat_cut_device[ 2*i_cut + 1 ]; + const int64_t delta_i = i_cut_second - i_cut_first; + + int64_t j(0); + for( size_t j_cut = 0; j_cut < ncut; ++j_cut ) { + const int64_t j_cut_first = submat_cut_device[ 2*j_cut ]; + const int64_t j_cut_second = submat_cut_device[ 2*j_cut + 1 ]; + const int64_t delta_j = j_cut_second - j_cut_first; + + auto* ASmall_begin = ASmall_device + i + j *LDAS; + auto* ABig_begin = A + i_cut_first + j_cut_first*LDA ; + + for( size_t J = tid_y; J < delta_j; J += blockDim.y ) + for( size_t I = tid_x; I < delta_i; I += blockDim.x ) + ASmall_begin[I + J*LDAS] = ABig_begin[I + J*LDA]; + + j += delta_j; + } + i += delta_i; + } + + } // batch_id check +} + + +template +void task_pack_density_matrix( size_t ntasks, + XCTaskDevice* device_tasks, + T* P_device, + size_t LDP, + cudaStream_t stream ) { + + dim3 threads(32,32,1), blocks(1,1,ntasks); + submat_set_combined_kernel<<< blocks, threads, 0, stream >>>( + ntasks, device_tasks, P_device, LDP + ); + + +} + +template +void task_pack_density_matrix( size_t ntasks, + XCTaskDevice* device_tasks, + double* P_device, + size_t LDP, + cudaStream_t stream ); + +} +} +} diff --git a/src/integrator/cuda/cuda_pack_density.hpp b/src/integrator/cuda/cuda_pack_density.hpp new file mode 100644 index 000000000..ae90ef333 --- /dev/null +++ b/src/integrator/cuda/cuda_pack_density.hpp @@ -0,0 +1,19 @@ +#pragma once +#include + +namespace GauXC { +namespace integrator { +namespace cuda { + +using namespace GauXC::cuda; + +template +void task_pack_density_matrix( size_t ntasks, + XCTaskDevice* device_tasks, + T* P_device, + size_t LDP, + cudaStream_t stream ); + +} +} +} diff --git a/src/integrator/cuda/cuda_zmat.cu b/src/integrator/cuda/cuda_zmat.cu new file mode 100644 index 000000000..d1d9016af --- /dev/null +++ b/src/integrator/cuda/cuda_zmat.cu @@ -0,0 +1,138 @@ +#include "cuda_zmat.hpp" +#include + +namespace GauXC { +namespace integrator { +namespace cuda { + + + +template +__global__ void zmat_lda_kernel( size_t ntasks, + XCTaskDevice* tasks_device ) { + + const int batch_idx = blockIdx.z; + if( batch_idx >= ntasks ) return; + + auto& task = tasks_device[ batch_idx ]; + const auto npts = task.npts; + const auto nbf = task.nbe; + const auto* vrho_device = task.vrho; + + const auto* basis_eval_device = task.bf; + + auto* z_matrix_device = task.zmat; + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x < nbf and tid_y < npts ) { + + const size_t ibfoff = tid_x + tid_y * nbf; + const double fact = 0.5 * vrho_device[tid_y]; + + z_matrix_device[ tid_x + tid_y * nbf ] = fact * basis_eval_device[ ibfoff ]; + + } + +} + + + + +template +void zmat_lda_cuda( size_t ntasks, + int32_t max_nbf, + int32_t max_npts, + XCTaskDevice* tasks_device, + cudaStream_t stream ) { + + + dim3 threads(32,32,1); + dim3 blocks( util::div_ceil( max_nbf, threads.x ), + util::div_ceil( max_npts, threads.y ), + ntasks ); + + zmat_lda_kernel<<< blocks, threads, 0, stream >>>( ntasks, tasks_device ); + +} + +template +void zmat_lda_cuda( size_t ntasks, + int32_t max_nbf, + int32_t max_npts, + XCTaskDevice* tasks_device, + cudaStream_t stream ); + + + + +template +__global__ void zmat_gga_kernel( size_t ntasks, + XCTaskDevice* tasks_device ) { + + const int batch_idx = blockIdx.z; + if( batch_idx >= ntasks ) return; + + auto& task = tasks_device[ batch_idx ]; + const auto npts = task.npts; + const auto nbf = task.nbe; + const auto* vrho_device = task.vrho; + const auto* vgamma_device = task.vgamma; + const auto* den_x_eval_device = task.ddenx; + const auto* den_y_eval_device = task.ddeny; + const auto* den_z_eval_device = task.ddenz; + + const auto* basis_eval_device = task.bf; + const auto* dbasis_x_eval_device = task.dbfx; + const auto* dbasis_y_eval_device = task.dbfy; + const auto* dbasis_z_eval_device = task.dbfz; + + auto* z_matrix_device = task.zmat; + + const int tid_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tid_y = blockIdx.y * blockDim.y + threadIdx.y; + + if( tid_x < nbf and tid_y < npts ) { + + const size_t ibfoff = tid_x + tid_y * nbf; + const double fact_1 = 0.5 * vrho_device[tid_y] ; + const double fact_2 = 2.0 * vgamma_device[tid_y]; + + const double dx = den_x_eval_device[ tid_y ] * dbasis_x_eval_device[ ibfoff ]; + const double dy = den_y_eval_device[ tid_y ] * dbasis_y_eval_device[ ibfoff ]; + const double dz = den_z_eval_device[ tid_y ] * dbasis_z_eval_device[ ibfoff ]; + + z_matrix_device[ tid_x + tid_y * nbf ] = + fact_1 * basis_eval_device[ ibfoff ] + fact_2 * ( dx + dy + dz ); + + } +} + +template +void zmat_gga_cuda( size_t ntasks, + int32_t max_nbf, + int32_t max_npts, + XCTaskDevice* tasks_device, + cudaStream_t stream ) { + + + dim3 threads(32,32,1); + dim3 blocks( util::div_ceil( max_nbf, threads.x ), + util::div_ceil( max_npts, threads.y ), + ntasks ); + + zmat_gga_kernel<<< blocks, threads, 0, stream >>>( ntasks, tasks_device ); + +} +template +void zmat_gga_cuda( size_t ntasks, + int32_t max_nbf, + int32_t max_npts, + XCTaskDevice* tasks_device, + cudaStream_t stream ); + +} +} +} + diff --git a/src/integrator/cuda/cuda_zmat.hpp b/src/integrator/cuda/cuda_zmat.hpp new file mode 100644 index 000000000..58769d897 --- /dev/null +++ b/src/integrator/cuda/cuda_zmat.hpp @@ -0,0 +1,26 @@ +#pragma once +#include + +namespace GauXC { +namespace integrator { +namespace cuda { + +using namespace GauXC::cuda; + +template +void zmat_lda_cuda( size_t ntasks, + int32_t max_nbf, + int32_t max_npts, + XCTaskDevice* tasks_device, + cudaStream_t stream ); + +template +void zmat_gga_cuda( size_t ntasks, + int32_t max_nbf, + int32_t max_npts, + XCTaskDevice* tasks_device, + cudaStream_t stream ); + +} +} +} diff --git a/src/integrator/cuda/gauxc-cuda_integrator.cmake b/src/integrator/cuda/gauxc-cuda_integrator.cmake index 9f6835167..1fb63a21a 100644 --- a/src/integrator/cuda/gauxc-cuda_integrator.cmake +++ b/src/integrator/cuda/gauxc-cuda_integrator.cmake @@ -2,38 +2,16 @@ find_package( CUDAToolkit REQUIRED ) find_package( MAGMA REQUIRED ) -#add_library( gauxc_device_integrator STATIC -# #cuda/collocation_kernels.cu -# cuda/collocation_device.cu -# cuda/xc_cuda_data.cxx -# cuda/xc_cuda_util.cxx -#) -#target_compile_features( gauxc_device_integrator PUBLIC cuda_std_14 cxx_std_14 ) -#target_link_libraries( gauxc_device_integrator PUBLIC MAGMA::magma CUDA::cudart -# CUDA::cublas gauxc_integrator_common ) -# -#target_include_directories( gauxc_device_integrator -# PRIVATE -# $ -# $ -# $ -# $ -# $ -# $ -#) -# -#target_compile_options( gauxc_device_integrator -# PRIVATE -# $<$: -Wall -Wextra -Wpedantic -Wnon-virtual-dtor> -# $<$: -Xcudafe --diag_suppress=partial_override -Xptxas -v > -#) -# - target_sources( gauxc PRIVATE cuda/collocation_device.cu cuda/xc_cuda_data.cxx cuda/xc_cuda_util.cxx cuda/cuda_weights.cu + cuda/cuda_pack_density.cu + cuda/cuda_eval_denvars.cu + cuda/cublas_extensions.cu + cuda/cuda_zmat.cu + cuda/cuda_inc_potential.cu ) target_compile_features( gauxc PRIVATE cuda_std_14 ) diff --git a/src/integrator/cuda/xc_cuda_data.cxx b/src/integrator/cuda/xc_cuda_data.cxx index 189f56069..87ff0e272 100644 --- a/src/integrator/cuda/xc_cuda_data.cxx +++ b/src/integrator/cuda/xc_cuda_data.cxx @@ -42,11 +42,12 @@ XCCudaData::XCCudaData( size_t _natoms, buffer_adaptor mem( device_ptr, fill_sz ); - shells_device = mem.aligned_alloc>( nshells ); - exc_device = mem.aligned_alloc( 1 ); - nel_device = mem.aligned_alloc( 1 ); - rab_device = mem.aligned_alloc( natoms * natoms ); - coords_device = mem.aligned_alloc( 3 * natoms ); + shells_device = mem.aligned_alloc>( nshells ); + exc_device = mem.aligned_alloc( 1 ); + nel_device = mem.aligned_alloc( 1 ); + acc_scr_device = mem.aligned_alloc( 1 ); + rab_device = mem.aligned_alloc( natoms * natoms ); + coords_device = mem.aligned_alloc( 3 * natoms ); if( not vxcinc_host ) vxc_device = mem.aligned_alloc( nbf * nbf ); @@ -410,57 +411,61 @@ std::tuple< task_iterator, device_task_container > + auto copy_rev = [&]( size_t n, const auto* src, auto* dest, cudaStream_t stream, + std::string m ) { + util::cuda_copy_async( n, dest, src, stream, m ); + }; try { // Send the data to the device - util::cuda_copy_async( 3*points_pack.size(), points_pack.data()->data(), + copy_rev( 3*points_pack.size(), points_pack.data()->data(), points_device_buffer, *master_stream, "send points buffer" ); - util::cuda_copy_async( weights_pack.size(), weights_pack.data(), + copy_rev( weights_pack.size(), weights_pack.data(), weights_device_buffer, *master_stream, "send weights buffer" ); - util::cuda_copy_async( shell_list_pack.size(), shell_list_pack.data(), + copy_rev( shell_list_pack.size(), shell_list_pack.data(), shell_list_device_buffer, *master_stream, "send_shell_list_buffer" ); - util::cuda_copy_async( shell_offs_pack.size(), shell_offs_pack.data(), + copy_rev( shell_offs_pack.size(), shell_offs_pack.data(), shell_offs_device_buffer, *master_stream, "send_shell_offs_buffer" ); - util::cuda_copy_async( 2*submat_cut_pack.size(), &submat_cut_pack.data()->first, + copy_rev( 2*submat_cut_pack.size(), &submat_cut_pack.data()->first, submat_cut_device_buffer, *master_stream, "send_submat_cut_buffer" ); - util::cuda_copy_async( tasks_device.size(), tasks_device.data(), device_tasks, + copy_rev( tasks_device.size(), tasks_device.data(), device_tasks, *master_stream, "send_tasks_device" ); - util::cuda_copy_async( dmat_array.size(), dmat_array.data(), dmat_array_device, + copy_rev( dmat_array.size(), dmat_array.data(), dmat_array_device, *master_stream, "send dmat_array" ); - util::cuda_copy_async( zmat_array.size(), zmat_array.data(), zmat_array_device, + copy_rev( zmat_array.size(), zmat_array.data(), zmat_array_device, *master_stream, "send zmat_array" ); - util::cuda_copy_async( bf_array.size(), bf_array.data(), bf_array_device, + copy_rev( bf_array.size(), bf_array.data(), bf_array_device, *master_stream, "send bf_array" ); - util::cuda_copy_async( m_array.size(), m_array.data(), m_array_device, + copy_rev( m_array.size(), m_array.data(), m_array_device, *master_stream, "send m_array" ); - util::cuda_copy_async( n_array.size(), n_array.data(), n_array_device, + copy_rev( n_array.size(), n_array.data(), n_array_device, *master_stream, "send n_array" ); - util::cuda_copy_async( k_array.size(), k_array.data(), k_array_device, + copy_rev( k_array.size(), k_array.data(), k_array_device, *master_stream, "send k_array" ); - util::cuda_copy_async( lda_array.size(), lda_array.data(), lda_array_device, + copy_rev( lda_array.size(), lda_array.data(), lda_array_device, *master_stream, "send lda_array" ); - util::cuda_copy_async( ldb_array.size(), ldb_array.data(), ldb_array_device, + copy_rev( ldb_array.size(), ldb_array.data(), ldb_array_device, *master_stream, "send ldb_array" ); - util::cuda_copy_async( ldc_array.size(), ldc_array.data(), ldc_array_device, + copy_rev( ldc_array.size(), ldc_array.data(), ldc_array_device, *master_stream, "send ldc_array" ); - util::cuda_copy_async( iparent_pack.size(), iparent_pack.data(), + copy_rev( iparent_pack.size(), iparent_pack.data(), iparent_device_buffer, *master_stream, "send iparent" ); - util::cuda_copy_async( dist_nearest_pack.size(), dist_nearest_pack.data(), + copy_rev( dist_nearest_pack.size(), dist_nearest_pack.data(), dist_nearest_buffer, *master_stream, "send dist_nearest" ); } catch(...) { diff --git a/src/integrator/cuda/xc_cuda_util.cxx b/src/integrator/cuda/xc_cuda_util.cxx index 1b3a1f380..1ba8505f3 100644 --- a/src/integrator/cuda/xc_cuda_util.cxx +++ b/src/integrator/cuda/xc_cuda_util.cxx @@ -3,15 +3,28 @@ #include "cuda_weights.hpp" #include "collocation_device.hpp" -//#include "cuda_zmat.hpp" +#include "cuda_pack_density.hpp" +#include "cuda_inc_potential.hpp" +#include "cuda_eval_denvars.hpp" +#include "cuda_zmat.hpp" #include "integrator_common.hpp" -//#include "blas.hpp" -//#include "util.hpp" + +#include "cublas_extensions.hpp" +#include + +inline static void unused() { } +template +inline static void unused( const T& t, Args&&... args ) { + (void)(t); + unused( std::forward(args)... ); +} namespace GauXC { namespace integrator::cuda { +using namespace GauXC::cuda::blas; + using host_task_iterator = std::vector::iterator; template @@ -26,6 +39,8 @@ void process_batches_cuda_replicated_all_device( cuda_task_iterator task_end ) { + const auto ntasks = std::distance( task_begin, task_end ); + const auto nbf = cuda_data.nbf; // Get batch statistics for batches to process auto nbe_comparator = @@ -49,35 +64,162 @@ void process_batches_cuda_replicated_all_device( const auto min_nshells = min_nshells_it->nshells; const auto max_nshells = max_nshells_it->nshells; + unused( min_nbe, min_npts, min_nshells ); + const size_t total_npts = std::accumulate( task_begin, task_end, 0ul, []( const auto& a, const auto& b ) { return a + b.npts; } ); - // Aliases cudaStream_t master_stream = *cuda_data.master_stream; - cublasHandle_t master_handle = *cuda_data.master_handle; magma_queue_t master_queue = *cuda_data.master_magma_queue; + cublasHandle_t master_handle = *cuda_data.master_handle; - const auto* rab_device = cuda_data.rab_device; - const auto* coords_device = cuda_data.coords_device; - const auto* points_device = cuda_data.points_device_buffer; - const auto* iparent_device = cuda_data.iparent_device_buffer; - const auto* dist_nearest_device = cuda_data.dist_nearest_buffer; + auto* dmat_device = cuda_data.dmat_device; + auto* shells_device = cuda_data.shells_device; + auto* tasks_device = cuda_data.device_tasks; + auto* dmat_array_device = cuda_data.dmat_array_device; + auto* zmat_array_device = cuda_data.zmat_array_device; + auto* bf_array_device = cuda_data.bf_array_device; auto* weights_device = cuda_data.weights_device_buffer; auto* dist_scratch_device = cuda_data.dist_scratch_device; + auto* den_eval_device = cuda_data.den_eval_device; + auto* dden_x_eval_device = cuda_data.den_x_eval_device; + auto* dden_y_eval_device = cuda_data.den_y_eval_device; + auto* dden_z_eval_device = cuda_data.den_z_eval_device; + + auto* eps_eval_device = cuda_data.eps_eval_device; + auto* gamma_eval_device = cuda_data.gamma_eval_device; + auto* vrho_eval_device = cuda_data.vrho_eval_device; + auto* vgamma_eval_device = cuda_data.vgamma_eval_device; + + + auto* exc_device = cuda_data.exc_device; + auto* vxc_device = cuda_data.vxc_device; + auto* nel_device = cuda_data.nel_device; + auto* acc_scr_device = cuda_data.acc_scr_device; + + auto* m_array_device = cuda_data.m_array_device; + auto* n_array_device = cuda_data.n_array_device; + auto* k_array_device = cuda_data.k_array_device; + auto* lda_array_device = cuda_data.lda_array_device; + auto* ldb_array_device = cuda_data.ldb_array_device; + auto* ldc_array_device = cuda_data.ldc_array_device; + + const auto* rab_device = cuda_data.rab_device; + const auto* coords_device = cuda_data.coords_device; + const auto* points_device = cuda_data.points_device_buffer; + const auto* iparent_device = cuda_data.iparent_device_buffer; + const auto* dist_nearest_device = cuda_data.dist_nearest_buffer; - // Evaluate partition weights + + + + // Evaluate Partition Weights partition_weights_cuda_SoA( weight_alg, total_npts, cuda_data.natoms, points_device, iparent_device, dist_nearest_device, rab_device, coords_device, weights_device, dist_scratch_device, master_stream ); + // Evaluate Collocation + if constexpr ( n_deriv == 1 ) + eval_collocation_masked_combined_deriv1( ntasks, max_npts, max_nshells, + shells_device, tasks_device, + master_stream ); + else + eval_collocation_masked_combined( ntasks, max_npts, max_nshells, shells_device, + tasks_device, master_stream ); + + // Pack Density Submatrices + task_pack_density_matrix( ntasks, tasks_device, dmat_device, nbf, master_stream ); + + + // Form Z = P * X + magmablas_dgemm_vbatched( MagmaNoTrans, MagmaNoTrans, + m_array_device, n_array_device, k_array_device, + 1., dmat_array_device, lda_array_device, + bf_array_device, ldb_array_device, + 0., zmat_array_device, ldc_array_device, + ntasks, master_queue ); + + + + // Zero UVars + util::cuda_set_zero_async( total_npts, den_eval_device, master_stream, "DenZero" ); + if( func.is_gga() ) { + util::cuda_set_zero_async( total_npts, dden_x_eval_device, master_stream, + "DenXZero" ); + util::cuda_set_zero_async( total_npts, dden_y_eval_device, master_stream, + "DenYZero" ); + util::cuda_set_zero_async( total_npts, dden_z_eval_device, master_stream, + "DenZZero" ); + } + + // Evaluate UVars + if( func.is_gga() ) { + eval_uvars_gga_device( ntasks, max_nbe, max_npts, tasks_device, master_stream ); + eval_vvars_gga_device( total_npts, dden_x_eval_device, dden_y_eval_device, + dden_z_eval_device, gamma_eval_device, master_stream ); + } else { + eval_uvars_lda_device( ntasks, max_nbe, max_npts, tasks_device, master_stream ); + } + + // Evaluate XC Functional + if( func.is_gga() ) + func.eval_exc_vxc_device( total_npts, den_eval_device, gamma_eval_device, + eps_eval_device, vrho_eval_device, + vgamma_eval_device, master_stream ); + else + func.eval_exc_vxc_device( total_npts, den_eval_device, eps_eval_device, + vrho_eval_device, master_stream ); + + + // Factor weights into XC output + hadamard_product( master_handle, total_npts, 1, weights_device, 1, + eps_eval_device, 1 ); + hadamard_product( master_handle, total_npts, 1, weights_device, 1, + vrho_eval_device, 1 ); + if( func.is_gga() ) + hadamard_product( master_handle, total_npts, 1, weights_device, 1, + vgamma_eval_device, 1 ); + + // Accumulate EXC / NEL + gdot( master_handle, total_npts, weights_device, 1, + den_eval_device, 1, acc_scr_device, nel_device ); + gdot( master_handle, total_npts, eps_eval_device, 1, + den_eval_device, 1, acc_scr_device, exc_device ); + + // Evaluate Z Matrix + if( func.is_gga() ) + zmat_gga_cuda( ntasks, max_nbe, max_npts, tasks_device, master_stream ); + else + zmat_lda_cuda( ntasks, max_nbe, max_npts, tasks_device, master_stream ); + + + + // Accumulate packed VXC = X * Z**T + Z * X**T + // XXX: Only updates LT + magmablas_dsyr2k_vbatched( MagmaLower, MagmaNoTrans, + m_array_device, n_array_device, + 1., bf_array_device, lda_array_device, + zmat_array_device, ldb_array_device, + 0., dmat_array_device, ldc_array_device, + ntasks, master_queue ); + + // Increment global VXC + task_inc_potential( ntasks, tasks_device, vxc_device, nbf, master_stream ); + + + // Synchronize on master stream + // XXX: There's no lifetime issues in this driver, should look into + // avoid this sync to allow for overlap with the host packing + cudaStreamSynchronize( master_stream ); + } @@ -105,13 +247,14 @@ void process_batches_cuda_replicated_p( const auto nbf = basis.nbf(); const auto natoms = meta.natoms(); + // Send static data to the device // Density if( not cuda_data.denpack_host ) util::cuda_copy( nbf * nbf, cuda_data.dmat_device, P, "P H2D" ); - // Shells: TODO avoid copy? + // Shells: TODO avoid host copy? std::vector> shells( basis ); util::cuda_copy( shells.size(), cuda_data.shells_device, shells.data(), "Shells H2D" ); @@ -146,7 +289,8 @@ void process_batches_cuda_replicated_p( auto [it, tasks_device] = cuda_data.generate_buffers( basis, task_it, tasks.end() ); - // TODO Process the batches + + // Process the batches process_batches_cuda_replicated_all_device( weight_alg, func, cuda_data, tasks_device.begin(), tasks_device.end() ); @@ -162,6 +306,11 @@ void process_batches_cuda_replicated_p( util::cuda_copy( 1, EXC, cuda_data.exc_device, "EXC D2H" ); util::cuda_copy( 1, NEL, cuda_data.nel_device, "NEL D2H" ); + // Symmetrize VXC + for( int32_t j = 0; j < nbf; ++j ) + for( int32_t i = j+1; i < nbf; ++i ) + VXC[ j + i*nbf ] = VXC[ i + j*nbf ]; + } diff --git a/src/integrator/host/xc_host_util.cxx b/src/integrator/host/xc_host_util.cxx index c8b268b1d..672deb312 100644 --- a/src/integrator/host/xc_host_util.cxx +++ b/src/integrator/host/xc_host_util.cxx @@ -7,6 +7,8 @@ #include "blas.hpp" #include "util.hpp" +#include + namespace GauXC { namespace integrator::host { @@ -27,6 +29,12 @@ void process_batches_host_replicated_p( F* n_el ) { + auto task_comparator = []( const XCTask& a, const XCTask& b ) { + return (a.points.size() * a.nbe) > (b.points.size() * b.nbe); + }; + std::sort( tasks.begin(), tasks.end(), task_comparator ); + + partition_weights_host( weight_alg, mol, meta, tasks ); const int32_t nbf = basis.nbf(); @@ -130,6 +138,7 @@ void process_batches_host_replicated_p( else func.eval_exc_vxc( npts, den_eval, eps, vrho ); + // Scalar integrations if( n_el ) for( int32_t i = 0; i < npts; ++i ) *n_el += weights[i] * den_eval[i]; @@ -146,21 +155,21 @@ void process_batches_host_replicated_p( zmat_lda_host( npts, nbe, weights, vrho, basis_eval, zmat ); - // Update VXC + + // Update VXC XXX: Only LT GauXC::blas::syr2k( 'L', 'N', nbe, npts, F(1.), basis_eval, nbe, zmat, nbe, F(0.), nbe_scr, nbe ); + detail::inc_by_submat( nbf, nbf, nbe, nbe, VXC, nbf, nbe_scr, nbe, submat_map ); } - // Symmetrize + // Symmetrize VXC for( int32_t j = 0; j < nbf; ++j ) for( int32_t i = j+1; i < nbf; ++i ) VXC[ j + i*nbf ] = VXC[ i + j*nbf ]; - //std::cout << std::scientific << std::setprecision(12); - //std::cout << "NEL = " << *n_el << std::endl; } diff --git a/tests/xc_integrator.cxx b/tests/xc_integrator.cxx index b74835658..52026eef9 100644 --- a/tests/xc_integrator.cxx +++ b/tests/xc_integrator.cxx @@ -41,6 +41,13 @@ void test_xc_integrator( ExecutionSpace ex, MPI_Comm comm, Molecule mol ) { auto [ EXC, VXC ] = integrator.eval_exc_vxc( P ); CHECK( EXC == Approx( EXC_ref ) ); + //std::cout << "VXC" << std::endl; + //std::cout << VXC << std::endl; + //std::cout << "VXC_ref" << std::endl; + //std::cout << VXC_ref << std::endl; + //std::cout << "DIFF" << std::endl; + //std::cout << (VXC-VXC_ref) << std::endl; + auto VXC_diff_nrm = ( VXC - VXC_ref ).norm(); CHECK( VXC_diff_nrm / basis.nbf() < 1e-10 ); } From 54ac4a4b42200dbdc17f7c7178f2c15d2387c776 Mon Sep 17 00:00:00 2001 From: David Williams-Young Date: Fri, 4 Sep 2020 14:49:30 -0400 Subject: [PATCH 27/27] Update Host integrator to adopt same weight convention as CUDA integrator (factored into XC output) --- src/integrator/host/host_zmat.cxx | 12 +++--------- src/integrator/host/host_zmat.hpp | 2 -- src/integrator/host/xc_host_util.cxx | 17 ++++++++++++++--- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/integrator/host/host_zmat.cxx b/src/integrator/host/host_zmat.cxx index 8e6c89236..f6cc3d59c 100644 --- a/src/integrator/host/host_zmat.cxx +++ b/src/integrator/host/host_zmat.cxx @@ -7,7 +7,6 @@ namespace integrator::host { template void zmat_lda_host( int32_t npts, int32_t nbf, - const F* weights, const F* vrho, const F* basis, F* z_matrix ) { @@ -19,7 +18,7 @@ void zmat_lda_host( int32_t npts, auto* z_col = z_matrix + i*nbf; - const F fact = 0.5 * vrho[i] * weights[i]; + const F fact = 0.5 * vrho[i]; GauXC::blas::scal( nbf, fact, z_col, 1 ); } @@ -29,14 +28,12 @@ void zmat_lda_host( int32_t npts, template void zmat_lda_host( int32_t npts, int32_t nbf, - const float* weights, const float* vrho, const float* basis, float* z_matrix ); template void zmat_lda_host( int32_t npts, int32_t nbf, - const double* weights, const double* vrho, const double* basis, double* z_matrix ); @@ -46,7 +43,6 @@ void zmat_lda_host( int32_t npts, template void zmat_gga_host( int32_t npts, int32_t nbf, - const F* weights, const F* vrho, const F* vgamma, const F* basis, @@ -70,10 +66,10 @@ void zmat_gga_host( int32_t npts, auto* bf_y_col = dbasis_y + ioff; auto* bf_z_col = dbasis_z + ioff; - const F lda_fact = 0.5 * vrho[i] * weights[i]; + const F lda_fact = 0.5 * vrho[i]; GauXC::blas::scal( nbf, lda_fact, z_col, 1 ); - const F gga_fact = 2. * vgamma[i] * weights[i]; + const F gga_fact = 2. * vgamma[i]; const auto x_fact = gga_fact * dden_x[i]; const auto y_fact = gga_fact * dden_y[i]; const auto z_fact = gga_fact * dden_z[i]; @@ -89,7 +85,6 @@ void zmat_gga_host( int32_t npts, template void zmat_gga_host( int32_t npts, int32_t nbf, - const float* weights, const float* vrho, const float* vgamma, const float* basis, @@ -104,7 +99,6 @@ void zmat_gga_host( int32_t npts, template void zmat_gga_host( int32_t npts, int32_t nbf, - const double* weights, const double* vrho, const double* vgamma, const double* basis, diff --git a/src/integrator/host/host_zmat.hpp b/src/integrator/host/host_zmat.hpp index f212f4067..ba3354147 100644 --- a/src/integrator/host/host_zmat.hpp +++ b/src/integrator/host/host_zmat.hpp @@ -7,7 +7,6 @@ namespace integrator::host { template void zmat_lda_host( int32_t npts, int32_t nbf, - const F* weights, const F* vrho, const F* basis, F* z_matrix ); @@ -15,7 +14,6 @@ void zmat_lda_host( int32_t npts, template void zmat_gga_host( int32_t npts, int32_t nbf, - const F* weights, const F* vrho, const F* vgamma, const F* basis, diff --git a/src/integrator/host/xc_host_util.cxx b/src/integrator/host/xc_host_util.cxx index 672deb312..9ee0276de 100644 --- a/src/integrator/host/xc_host_util.cxx +++ b/src/integrator/host/xc_host_util.cxx @@ -139,20 +139,31 @@ void process_batches_host_replicated_p( func.eval_exc_vxc( npts, den_eval, eps, vrho ); + // Factor weights into XC results + for( int32_t i = 0; i < npts; ++i ) { + eps[i] *= weights[i]; + vrho[i] *= weights[i]; + } + + if( func.is_gga() ) + for( int32_t i = 0; i < npts; ++i ) vgamma[i] *= weights[i]; + + + // Scalar integrations if( n_el ) for( int32_t i = 0; i < npts; ++i ) *n_el += weights[i] * den_eval[i]; - for( int32_t i = 0; i < npts; ++i ) *exc += weights[i] * den_eval[i] * eps[i]; + for( int32_t i = 0; i < npts; ++i ) *exc += eps[i] * den_eval[i]; // Assemble Z if( func.is_gga() ) - zmat_gga_host( npts, nbe, weights, vrho, vgamma, basis_eval, dbasis_x_eval, + zmat_gga_host( npts, nbe, vrho, vgamma, basis_eval, dbasis_x_eval, dbasis_y_eval, dbasis_z_eval, dden_x_eval, dden_y_eval, dden_z_eval, zmat ); else - zmat_lda_host( npts, nbe, weights, vrho, basis_eval, zmat ); + zmat_lda_host( npts, nbe, vrho, basis_eval, zmat );