qmckl/org/qmckl_local_energy.org

58 KiB

Local Energy

Here we calculate the final expectation value of the local energy \[E_L\] as the sum of the kinetic energy and potential energy.

\[ E_L = KE + PE \]

Where the kinetic energy is given as:

\[ KE = -\frac{1}{2}\frac{\bigtriangleup \Psi}{\Psi} \]

The laplacian of the wavefunction in the single-determinant case is given as follows:

\[ \frac{\bigtriangleup \Psi(r)}{\Psi(r)} = \sum_{j=1}^{N_e} \bigtriangleup \Phi_j(r_i) D_{ji}^{-1}(r) \]

The potential energy is the sum of all the following terms

\[ PE = \mathcal{V}_{ee} + \mathcal{V}_{en} + \mathcal{V}_{nn} \]

The potential for is calculated as the sum of single electron contributions.

\[ \mathcal{V}_{ee} = \sum_{i=1}^{N_e}\sum_{j<i} \frac{1}{r_{ij}} \]

\[ \mathcal{V}_{en} = - \sum_{i=1}^{N_e}\sum_{A=1}^{N_n}\frac{Z_A}{r_{iA}} \]

\[ \mathcal{V}_{nn} = \sum_{A=1}^{N_n}\sum_{B<A}\frac{Z_A Z_B}{r_{AB}} \]

The remaining quantities that are required for the calculation of a single Monte-Carlo step are as follows:

  1. Drift Vector - \[F(x)\]

\[ F(x) = 2\frac{\nabla \Psi(r)}{\Psi(r)} \]

  1. Diffusion move - \[y\]

\[ y = x + D F(x) \delta t + \chi \]

Where \[\chi\] is a random number with gaussian distribution centered at 0 and width of \[2D\delta t\]. Here \[D\] is the drift parameter.

  1. Acceptance probability - \[min\left[1, q(y,x)\right]\]

\[ q(y,x) = Exp\left[\frac{\delta r}{2} ( F(x) + F(y)) - \frac{D\delta t}{4}\left( F(x)^2 - F(y)^2\right)\right] \]

With these quantities, a single determinant VMC simulation can be carried out.

Context

The following arrays are stored in the context:

Computed data:

e_kin [walk_num] total kinetic energy
e_pot [walk_num] total potential energy
e_local [walk_num] local energy
r_drift [3][walk_num][elec_num] The drift vector
y_move [3][walk_num] The diffusion move
accep_prob [walk_num] The acceptance probability

Data structure

typedef struct qmckl_local_energy_struct {
double  * e_kin;
double  * e_pot;
double  * e_local;
double  * accep_prob;
double  * r_drift;
double  * y_move;
uint64_t   e_kin_date;
uint64_t   e_pot_date;
uint64_t   e_local_date;
uint64_t   accep_prob_date;
uint64_t   r_drift_date;
uint64_t   y_move_date;

int32_t   uninitialized;
bool      provided;
} qmckl_local_energy_struct;

The uninitialized integer contains one bit set to one for each initialization function which has not been called. It becomes equal to zero after all initialization functions have been called. The struct is then initialized and provided == true. Some values are initialized by default, and are not concerned by this mechanism.

Access functions

When all the data for the local energy have been provided, the following function returns true.

bool      qmckl_local_energy_provided           (const qmckl_context context);

Computation

Kinetic energy

Where the kinetic energy is given as:

\[ KE = -\frac{1}{2}\frac{\bigtriangleup \Psi}{\Psi} \]

The laplacian of the wavefunction in the single-determinant case is given as follows:

\[ \frac{\bigtriangleup \Psi(r)}{\Psi(r)} = \sum_{j=1}^{N_e} \bigtriangleup \Phi_j(r_i) D_{ji}^{-1}(r) \]

Get

qmckl_exit_code qmckl_get_kinetic_energy(qmckl_context context, double* const kinetic_energy);

Provide

Compute kinetic enregy

qmckl_context context in Global state
int64_t walk_num in Number of walkers
int64_t det_num_alpha in Number of determinants
int64_t det_num_beta in Number of determinants
int64_t alpha_num in Number of electrons
int64_t beta_num in Number of electrons
int64_t elec_num in Number of electrons
int64_t mo_index_alpha[det_num_alpha][walk_num][alpha_num] in MO indices for electrons
int64_t mo_index_beta[det_num_beta][walk_num][beta_num] in MO indices for electrons
int64_t mo_num in Number of MOs
double mo_vgl[5][elec_num][mo_num] in Value, gradients and Laplacian of the MOs
double det_value_alpha[det_num_alpha][walk_num] in Det of wavefunction
double det_value_beta[det_num_beta][walk_num] in Det of wavefunction
double det_inv_matrix_alpha[det_num_alpha][walk_num][alpha_num][alpha_num] in Value, gradients and Laplacian of the Det
double det_inv_matrix_beta[det_num_beta][walk_num][beta_num][beta_num] in Value, gradients and Laplacian of the Det
double e_kin[walk_num] out Kinetic energy
integer function qmckl_compute_kinetic_energy_f(context, walk_num, &
 det_num_alpha, det_num_beta, alpha_num, beta_num, elec_num, mo_index_alpha, mo_index_beta, &
 mo_num, mo_vgl, det_value_alpha, det_value_beta, det_inv_matrix_alpha, det_inv_matrix_beta, e_kin) &
 result(info)
use qmckl
implicit none
integer(qmckl_context)  , intent(in)  :: context
integer*8, intent(in)             :: walk_num
integer*8, intent(in)             :: det_num_alpha
integer*8, intent(in)             :: det_num_beta
integer*8, intent(in)             :: alpha_num
integer*8, intent(in)             :: beta_num
integer*8, intent(in)             :: elec_num
integer*8, intent(in)             :: mo_num
integer*8, intent(in)             :: mo_index_alpha(alpha_num, walk_num, det_num_alpha)
integer*8, intent(in)             :: mo_index_beta(beta_num, walk_num, det_num_beta)
double precision, intent(in)      :: mo_vgl(mo_num, elec_num, 5)
double precision, intent(in)      :: det_value_alpha(walk_num, det_num_alpha)
double precision, intent(in)      :: det_value_beta(walk_num, det_num_beta)
double precision, intent(in)      :: det_inv_matrix_alpha(alpha_num, alpha_num, walk_num, det_num_alpha)
double precision, intent(in)      :: det_inv_matrix_beta(beta_num, beta_num, walk_num, det_num_beta)
double precision, intent(inout)   :: e_kin(walk_num)
double precision                  :: tmp_e
integer*8 :: idet, iwalk, ielec, mo_id, imo

info = QMCKL_SUCCESS

if (context == QMCKL_NULL_CONTEXT) then
 info = QMCKL_INVALID_CONTEXT
 return
endif

if (walk_num <= 0) then
 info = QMCKL_INVALID_ARG_2
 return
endif

if (alpha_num <= 0) then
 info = QMCKL_INVALID_ARG_3
 return
endif

if (beta_num < 0) then
 info = QMCKL_INVALID_ARG_4
 return
endif

if (elec_num <= 0) then
 info = QMCKL_INVALID_ARG_5
 return
endif

e_kin = 0.0d0
do idet = 1, det_num_alpha
do iwalk = 1, walk_num
! Alpha part
do imo = 1, alpha_num
do ielec = 1, alpha_num
  mo_id = mo_index_alpha(imo, iwalk, idet)
  e_kin(iwalk) = e_kin(iwalk) - 0.5d0 * det_inv_matrix_alpha(imo, ielec, iwalk, idet) * &
                                mo_vgl(mo_id, ielec, 5)
end do
end do
! Beta part
do imo = 1, beta_num
do ielec = 1, beta_num
  mo_id = mo_index_beta(imo, iwalk, idet)
  e_kin(iwalk) = e_kin(iwalk) - 0.5d0 * det_inv_matrix_beta(imo, ielec, iwalk, idet) * &
                                 mo_vgl(mo_id, alpha_num + ielec, 5)
end do
end do
end do
end do

end function qmckl_compute_kinetic_energy_f
qmckl_exit_code qmckl_compute_kinetic_energy (
const qmckl_context context,
const int64_t walk_num,
const int64_t det_num_alpha,
const int64_t det_num_beta,
const int64_t alpha_num,
const int64_t beta_num,
const int64_t elec_num,
const int64_t* mo_index_alpha,
const int64_t* mo_index_beta,
const int64_t mo_num,
const double* mo_vgl,
const double* det_value_alpha,
const double* det_value_beta,
const double* det_inv_matrix_alpha,
const double* det_inv_matrix_beta,
double* const e_kin );

Test

Potential energy

The potential energy is the sum of all the following terms

\[ PE = \mathcal{V}_{ee} + \mathcal{V}_{en} + \mathcal{V}_{nn} \]

The potential for is calculated as the sum of single electron contributions.

\[ \mathcal{V}_{ee} = \sum_{i=1}^{N_e}\sum_{j<i} \frac{1}{r_{ij}} \]

\[ \mathcal{V}_{en} = - \sum_{i=1}^{N_e}\sum_{A=1}^{N_n}\frac{Z_A}{r_{iA}} \]

\[ \mathcal{V}_{nn} = \sum_{A=1}^{N_n}\sum_{B<A}\frac{Z_A Z_B}{r_{AB}} \]

Get

qmckl_exit_code qmckl_get_potential_energy(qmckl_context context, double* const potential_energy);

Provide

Compute potential enregy

qmckl_context context in Global state
int64_t walk_num in Number of walkers
int64_t elec_num in Number of electrons
int64_t nucl_num in Number of MOs
double ee_potential[walk_num] in ee potential
double en_potential[walk_num] in en potential
double repulsion in en potential
double e_pot[walk_num] out Potential energy
integer function qmckl_compute_potential_energy_f(context, walk_num, &
 elec_num, nucl_num, ee_potential, en_potential, repulsion, e_pot) &
 result(info)
use qmckl
implicit none
integer(qmckl_context)  , intent(in)  :: context
integer*8, intent(in)             :: walk_num
integer*8, intent(in)             :: elec_num
integer*8, intent(in)             :: nucl_num
double precision, intent(in)      :: ee_potential(walk_num)
double precision, intent(in)      :: en_potential(walk_num)
double precision, intent(in)      :: repulsion
double precision, intent(inout)   :: e_pot(walk_num)
integer*8 :: idet, iwalk, ielec, mo_id, imo

info = QMCKL_SUCCESS

if (context == QMCKL_NULL_CONTEXT) then
 info = QMCKL_INVALID_CONTEXT
 return
endif

if (walk_num <= 0) then
 info = QMCKL_INVALID_ARG_2
 return
endif

if (elec_num <= 0) then
 info = QMCKL_INVALID_ARG_3
 return
endif

do iwalk = 1, walk_num
e_pot(iwalk) = ee_potential(iwalk) + en_potential(iwalk) + repulsion
end do

end function qmckl_compute_potential_energy_f
qmckl_exit_code qmckl_compute_potential_energy (
const qmckl_context context,
const int64_t walk_num,
const int64_t elec_num,
const int64_t nucl_num,
const double* ee_potential,
const double* en_potential,
const double repulsion,
double* const e_pot );

Test

Local energy

The local energy is the sum of kinetic and potential energies.

\[ E_L = KE + PE \]

Get

qmckl_exit_code qmckl_get_local_energy(qmckl_context context, double* const local_energy, const int64_t size_max);

Provide

Compute local enregy

qmckl_context context in Global state
int64_t walk_num in Number of walkers
double e_kin[walk_num] in e kinetic
double e_pot[walk_num] in e potential
double e_local[walk_num] out local energy
integer function qmckl_compute_local_energy_f(context, walk_num, &
 e_kin, e_pot, e_local) &
 result(info)
use qmckl
implicit none
integer(qmckl_context)  , intent(in)  :: context
integer*8, intent(in)             :: walk_num
double precision, intent(in)      :: e_kin(walk_num)
double precision, intent(in)      :: e_pot(walk_num)
double precision, intent(inout)   :: e_local(walk_num)
integer*8 :: idet, iwalk, ielec, mo_id, imo

info = QMCKL_SUCCESS

if (context == QMCKL_NULL_CONTEXT) then
 info = QMCKL_INVALID_CONTEXT
 return
endif

if (walk_num <= 0) then
 info = QMCKL_INVALID_ARG_2
 return
endif

e_local = 0.0d0
do iwalk = 1, walk_num
e_local(iwalk) = e_local(iwalk) + e_kin(iwalk) + e_pot(iwalk)
end do

end function qmckl_compute_local_energy_f
qmckl_exit_code qmckl_compute_local_energy (
const qmckl_context context,
const int64_t walk_num,
const double* e_kin,
const double* e_pot,
double* const e_local );

Test

Drift vector

The drift vector is calculated as the ration of the gradient with the determinant of the wavefunction.

\[ \mathbf{F} = 2 \frac{\nabla \Psi}{\Psi} \]

Get

qmckl_exit_code qmckl_get_drift_vector(qmckl_context context, double* const drift_vector);

Provide

Compute drift vector

qmckl_context context in Global state
int64_t walk_num in Number of walkers
int64_t det_num_alpha in Number of determinants
int64_t det_num_beta in Number of determinants
int64_t alpha_num in Number of electrons
int64_t beta_num in Number of electrons
int64_t elec_num in Number of electrons
int64_t mo_index_alpha[det_num_alpha][walk_num][alpha_num] in MO indices for electrons
int64_t mo_index_beta[det_num_beta][walk_num][beta_num] in MO indices for electrons
int64_t mo_num in Number of MOs
double mo_vgl[5][elec_num][mo_num] in Value, gradients and Laplacian of the MOs
double det_inv_matrix_alpha[det_num_alpha][walk_num][alpha_num][alpha_num] in Value, gradients and Laplacian of the Det
double det_inv_matrix_beta[det_num_beta][walk_num][beta_num][beta_num] in Value, gradients and Laplacian of the Det
double r_drift[walk_num][elec_num][3] out Kinetic energy
integer function qmckl_compute_drift_vector_f(context, walk_num, &
 det_num_alpha, det_num_beta, alpha_num, beta_num, elec_num, mo_index_alpha, mo_index_beta, &
 mo_num, mo_vgl, det_inv_matrix_alpha, det_inv_matrix_beta, r_drift) &
 result(info)
use qmckl
implicit none
integer(qmckl_context)  , intent(in)  :: context
integer*8, intent(in)             :: walk_num
integer*8, intent(in)             :: det_num_alpha
integer*8, intent(in)             :: det_num_beta
integer*8, intent(in)             :: alpha_num
integer*8, intent(in)             :: beta_num
integer*8, intent(in)             :: elec_num
integer*8, intent(in)             :: mo_num
integer*8, intent(in)             :: mo_index_alpha(alpha_num, walk_num, det_num_alpha)
integer*8, intent(in)             :: mo_index_beta(beta_num, walk_num, det_num_beta)
double precision, intent(in)      :: mo_vgl(mo_num, elec_num, 5)
double precision, intent(in)      :: det_inv_matrix_alpha(alpha_num, alpha_num, walk_num, det_num_alpha)
double precision, intent(in)      :: det_inv_matrix_beta(beta_num, beta_num, walk_num, det_num_beta)
double precision, intent(inout)   :: r_drift(3,elec_num,walk_num)
integer*8 :: idet, iwalk, ielec, mo_id, imo

info = QMCKL_SUCCESS

if (context == QMCKL_NULL_CONTEXT) then
 info = QMCKL_INVALID_CONTEXT
 return
endif

if (walk_num <= 0) then
 info = QMCKL_INVALID_ARG_2
 return
endif

if (alpha_num <= 0) then
 info = QMCKL_INVALID_ARG_3
 return
endif

if (beta_num < 0) then
 info = QMCKL_INVALID_ARG_4
 return
endif

if (elec_num <= 0) then
 info = QMCKL_INVALID_ARG_5
 return
endif

r_drift = 0.0d0
do idet = 1, det_num_alpha
do iwalk = 1, walk_num
! Alpha part
do imo = 1, alpha_num
do ielec = 1, alpha_num
  mo_id = mo_index_alpha(imo, iwalk, idet)
  r_drift(1,ielec,iwalk) = r_drift(1,ielec,iwalk) + 2.0d0 * det_inv_matrix_alpha(imo, ielec, iwalk, idet) * &
                                mo_vgl(mo_id, ielec, 2)
  r_drift(2,ielec,iwalk) = r_drift(2,ielec,iwalk) + 2.0d0 * det_inv_matrix_alpha(imo, ielec, iwalk, idet) * &
                                mo_vgl(mo_id, ielec, 3)
  r_drift(3,ielec,iwalk) = r_drift(3,ielec,iwalk) + 2.0d0 * det_inv_matrix_alpha(imo, ielec, iwalk, idet) * &
                                mo_vgl(mo_id, ielec, 4)
end do
end do
! Beta part
do imo = 1, beta_num
do ielec = 1, beta_num
  mo_id = mo_index_beta(imo, iwalk, idet)
  r_drift(1,alpha_num + ielec,iwalk) = r_drift(1,alpha_num + ielec,iwalk) + &
                                2.0d0 * det_inv_matrix_beta(imo, ielec, iwalk, idet) * &
                                mo_vgl(mo_id, alpha_num + ielec, 2)
  r_drift(2,alpha_num + ielec,iwalk) = r_drift(2,alpha_num + ielec,iwalk) + &
                                2.0d0 * det_inv_matrix_beta(imo, ielec, iwalk, idet) * &
                                mo_vgl(mo_id, alpha_num + ielec, 3)
  r_drift(3,alpha_num + ielec,iwalk) = r_drift(3,alpha_num + ielec,iwalk) + &
                                2.0d0 * det_inv_matrix_beta(imo, ielec, iwalk, idet) * &
                                mo_vgl(mo_id, alpha_num + ielec, 4)
end do
end do
end do
end do

end function qmckl_compute_drift_vector_f
qmckl_exit_code qmckl_compute_drift_vector (
const qmckl_context context,
const int64_t walk_num,
const int64_t det_num_alpha,
const int64_t det_num_beta,
const int64_t alpha_num,
const int64_t beta_num,
const int64_t elec_num,
const int64_t* mo_index_alpha,
const int64_t* mo_index_beta,
const int64_t mo_num,
const double* mo_vgl,
const double* det_inv_matrix_alpha,
const double* det_inv_matrix_beta,
double* const r_drift );

Test