LCOV - code coverage report
Current view: top level - src/grpp - grpp_integrals_gradient.c (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:936074a) Lines: 0.0 % 383 0
Test Date: 2025-12-04 06:27:48 Functions: 0.0 % 13 0

            Line data    Source code
       1              : /*----------------------------------------------------------------------------*/
       2              : /*  CP2K: A general program to perform molecular dynamics simulations         */
       3              : /*  Copyright 2000-2025 CP2K developers group <https://cp2k.org>              */
       4              : /*                                                                            */
       5              : /*  SPDX-License-Identifier: MIT                                              */
       6              : /*----------------------------------------------------------------------------*/
       7              : 
       8              : /*
       9              :  *  libgrpp - a library for the evaluation of integrals over
      10              :  *            generalized relativistic pseudopotentials.
      11              :  *
      12              :  *  Copyright (C) 2021-2023 Alexander Oleynichenko
      13              :  */
      14              : 
      15              : #include "libgrpp.h"
      16              : 
      17              : #include <stdio.h>
      18              : #include <stdlib.h>
      19              : #include <string.h>
      20              : 
      21              : #include "grpp_diff_gaussian.h"
      22              : #include "grpp_utils.h"
      23              : 
      24              : void grpp_gradient_diff_bra_contribution(
      25              :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
      26              :     libgrpp_grpp_t *grpp_operator, double *grpp_origin, double **grad_arep,
      27              :     double **grad_so_x, double **grad_so_y, double **grad_so_z, double factor);
      28              : 
      29              : void grpp_gradient_diff_bra_grpp_integrals(
      30              :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
      31              :     libgrpp_grpp_t *grpp_operator, double *grpp_origin,
      32              :     double **arep_matrix_down, double **so_x_matrix_down,
      33              :     double **so_y_matrix_down, double **so_z_matrix_down,
      34              :     double **arep_matrix_up, double **so_x_matrix_up, double **so_y_matrix_up,
      35              :     double **so_z_matrix_up, int *cart_size_down, int *cart_size_up);
      36              : 
      37              : void grpp_gradient_diff_ket_contribution(
      38              :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
      39              :     libgrpp_grpp_t *grpp_operator, double *grpp_origin, double **grad_arep,
      40              :     double **grad_so_x, double **grad_so_y, double **grad_so_z, double factor);
      41              : 
      42              : void grpp_gradient_diff_ket_grpp_integrals(
      43              :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
      44              :     libgrpp_grpp_t *grpp_operator, double *grpp_origin,
      45              :     double **arep_matrix_down, double **so_x_matrix_down,
      46              :     double **so_y_matrix_down, double **so_z_matrix_down,
      47              :     double **arep_matrix_up, double **so_x_matrix_up, double **so_y_matrix_up,
      48              :     double **so_z_matrix_up, int *cart_size_down, int *cart_size_up);
      49              : 
      50              : void grpp_gradient_contribution(libgrpp_shell_t *shell_A,
      51              :                                 libgrpp_shell_t *shell_B,
      52              :                                 libgrpp_grpp_t *grpp_operator,
      53              :                                 double *grpp_origin, double **grad_arep,
      54              :                                 double **grad_so_x, double **grad_so_y,
      55              :                                 double **grad_so_z, int diff_bra,
      56              :                                 double factor);
      57              : 
      58              : void grpp_gradient_diff_gaussian(
      59              :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
      60              :     libgrpp_grpp_t *grpp_operator, double *grpp_origin,
      61              :     double **arep_matrix_down, double **so_x_matrix_down,
      62              :     double **so_y_matrix_down, double **so_z_matrix_down,
      63              :     double **arep_matrix_up, double **so_x_matrix_up, double **so_y_matrix_up,
      64              :     double **so_z_matrix_up, int *cart_size_down, int *cart_size_up,
      65              :     int diff_bra);
      66              : 
      67              : extern int libgrpp_nlm_to_linear(int *nlm);
      68              : 
      69              : double **libgrpp_alloc_gradients(libgrpp_shell_t *bra, libgrpp_shell_t *ket);
      70              : 
      71              : void libgrpp_dealloc_gradients(double **grad);
      72              : 
      73              : /**
      74              :  * Analytic calculation of gradients of LOCAL potential integrals for a given
      75              :  * shell pair with respect to the point 'point_3d'.
      76              :  */
      77            0 : void libgrpp_type1_integrals_gradient(libgrpp_shell_t *shell_A,
      78              :                                       libgrpp_shell_t *shell_B,
      79              :                                       double *grpp_origin,
      80              :                                       libgrpp_potential_t *potential,
      81              :                                       double *point_3d, double **grad_arep) {
      82            0 :   libgrpp_grpp_t *grpp_operator = libgrpp_new_grpp();
      83            0 :   libgrpp_grpp_set_local_potential(grpp_operator, potential);
      84              : 
      85              :   /*
      86              :    * these arrays are not actually used.
      87              :    * they are needed only in order to use the
      88              :    * libgrpp_full_grpp_integrals_gradient() routine.
      89              :    */
      90            0 :   double **stub_grad_so_x = libgrpp_alloc_gradients(shell_A, shell_B);
      91            0 :   double **stub_grad_so_y = libgrpp_alloc_gradients(shell_A, shell_B);
      92            0 :   double **stub_grad_so_z = libgrpp_alloc_gradients(shell_A, shell_B);
      93              : 
      94            0 :   libgrpp_full_grpp_integrals_gradient(
      95              :       shell_A, shell_B, grpp_operator, grpp_origin, point_3d, grad_arep,
      96              :       stub_grad_so_x, stub_grad_so_y, stub_grad_so_z);
      97              : 
      98            0 :   libgrpp_dealloc_gradients(stub_grad_so_x);
      99            0 :   libgrpp_dealloc_gradients(stub_grad_so_y);
     100            0 :   libgrpp_dealloc_gradients(stub_grad_so_z);
     101              : 
     102            0 :   grpp_operator->U_L = NULL;
     103            0 :   libgrpp_delete_grpp(grpp_operator);
     104            0 : }
     105              : 
     106              : /**
     107              :  * Analytic calculation of gradients of SEMI-LOCAL potential integrals for a
     108              :  * given shell pair with respect to the point 'point_3d'.
     109              :  */
     110            0 : void libgrpp_type2_integrals_gradient(libgrpp_shell_t *shell_A,
     111              :                                       libgrpp_shell_t *shell_B,
     112              :                                       double *grpp_origin,
     113              :                                       libgrpp_potential_t *potential,
     114              :                                       double *point_3d, double **grad_arep) {
     115            0 :   libgrpp_grpp_t *grpp_operator = libgrpp_new_grpp();
     116            0 :   libgrpp_grpp_add_averaged_potential(grpp_operator, potential);
     117              : 
     118              :   /*
     119              :    * these arrays are not actually used.
     120              :    * they are needed only in order to use the
     121              :    * libgrpp_full_grpp_integrals_gradient() routine.
     122              :    */
     123            0 :   double **stub_grad_so_x = libgrpp_alloc_gradients(shell_A, shell_B);
     124            0 :   double **stub_grad_so_y = libgrpp_alloc_gradients(shell_A, shell_B);
     125            0 :   double **stub_grad_so_z = libgrpp_alloc_gradients(shell_A, shell_B);
     126              : 
     127            0 :   libgrpp_full_grpp_integrals_gradient(
     128              :       shell_A, shell_B, grpp_operator, grpp_origin, point_3d, grad_arep,
     129              :       stub_grad_so_x, stub_grad_so_y, stub_grad_so_z);
     130              : 
     131            0 :   libgrpp_dealloc_gradients(stub_grad_so_x);
     132            0 :   libgrpp_dealloc_gradients(stub_grad_so_y);
     133            0 :   libgrpp_dealloc_gradients(stub_grad_so_z);
     134              : 
     135            0 :   grpp_operator->n_arep = 0;
     136            0 :   libgrpp_delete_grpp(grpp_operator);
     137            0 : }
     138              : 
     139              : /**
     140              :  * Analytic calculation of gradients of integrals over the effective spin-orbit
     141              :  * operator (potential) for a given shell pair (with respect to the point
     142              :  * 'point_3d').
     143              :  */
     144            0 : void libgrpp_spin_orbit_integrals_gradient(
     145              :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *grpp_origin,
     146              :     libgrpp_potential_t *potential, double *point_3d, double **grad_so_x,
     147              :     double **grad_so_y, double **grad_so_z) {
     148            0 :   libgrpp_grpp_t *grpp_operator = libgrpp_new_grpp();
     149            0 :   libgrpp_grpp_add_spin_orbit_potential(grpp_operator, potential);
     150              : 
     151              :   /*
     152              :    * this array is not actually used and is needed only in order
     153              :    * to use the libgrpp_full_grpp_integrals_gradient() routine.
     154              :    */
     155            0 :   double **stub_grad_arep = libgrpp_alloc_gradients(shell_A, shell_B);
     156              : 
     157            0 :   libgrpp_full_grpp_integrals_gradient(shell_A, shell_B, grpp_operator,
     158              :                                        grpp_origin, point_3d, stub_grad_arep,
     159              :                                        grad_so_x, grad_so_y, grad_so_z);
     160              : 
     161              :   /*
     162              :    * inside the libgrpp_full_grpp_integrals_gradient() function
     163              :    * the SO potential was scaled by 2/(2L+1). Thus the result has to be
     164              :    * re-scaled by (2L+1)/2 to get rid of any problems with pre-factor
     165              :    */
     166            0 :   int L = potential->L;
     167            0 :   int buf_size = shell_A->cart_size * shell_B->cart_size;
     168            0 :   for (int icoord = 0; icoord < 3; icoord++) {
     169            0 :     for (int i = 0; i < buf_size; i++) {
     170            0 :       grad_so_x[icoord][i] *= (2.0 * L + 1.0) / 2.0;
     171            0 :       grad_so_y[icoord][i] *= (2.0 * L + 1.0) / 2.0;
     172            0 :       grad_so_z[icoord][i] *= (2.0 * L + 1.0) / 2.0;
     173              :     }
     174              :   }
     175              : 
     176            0 :   libgrpp_dealloc_gradients(stub_grad_arep);
     177              : 
     178            0 :   grpp_operator->n_esop = 0;
     179            0 :   libgrpp_delete_grpp(grpp_operator);
     180            0 : }
     181              : 
     182            0 : void libgrpp_outercore_potential_integrals_gradient(
     183              :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *rpp_origin,
     184              :     int num_oc_shells, libgrpp_potential_t **oc_potentials,
     185              :     libgrpp_shell_t **oc_shells, double *point_3d, double **grad_arep,
     186              :     double **grad_so_x, double **grad_so_y, double **grad_so_z) {
     187            0 :   libgrpp_grpp_t *grpp_operator = libgrpp_new_grpp();
     188            0 :   for (int ioc = 0; ioc < num_oc_shells; ioc++) {
     189            0 :     libgrpp_grpp_add_outercore_potential(grpp_operator, oc_potentials[ioc],
     190            0 :                                          oc_shells[ioc]);
     191              :   }
     192              : 
     193            0 :   libgrpp_full_grpp_integrals_gradient(shell_A, shell_B, grpp_operator,
     194              :                                        rpp_origin, point_3d, grad_arep,
     195              :                                        grad_so_x, grad_so_y, grad_so_z);
     196              : 
     197            0 :   grpp_operator->n_oc_shells = 0;
     198            0 :   libgrpp_delete_grpp(grpp_operator);
     199            0 : }
     200              : 
     201              : /**
     202              :  * Analytic calculation of gradients of GRPP integrals for a given shell pair
     203              :  * with respect to the point 'point_3d'.
     204              :  * (for the full GRPP operator which includes local, semi-local and non-local
     205              :  * parts)
     206              :  */
     207            0 : void libgrpp_full_grpp_integrals_gradient(
     208              :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
     209              :     libgrpp_grpp_t *grpp_operator, double *grpp_origin, double *point_3d,
     210              :     double **grad_arep, double **grad_so_x, double **grad_so_y,
     211              :     double **grad_so_z) {
     212            0 :   int cart_size_A = shell_A->cart_size;
     213            0 :   int cart_size_B = shell_B->cart_size;
     214            0 :   int buf_size = cart_size_A * cart_size_B;
     215              : 
     216              :   /*
     217              :    * initialization: set all gradients to zero
     218              :    */
     219            0 :   for (int icoord = 0; icoord < 3; icoord++) {
     220            0 :     memset(grad_arep[icoord], 0, sizeof(double) * buf_size);
     221            0 :     memset(grad_so_x[icoord], 0, sizeof(double) * buf_size);
     222            0 :     memset(grad_so_y[icoord], 0, sizeof(double) * buf_size);
     223            0 :     memset(grad_so_z[icoord], 0, sizeof(double) * buf_size);
     224              :   }
     225              : 
     226              :   /*
     227              :    * d<AAA>/d... = 0
     228              :    */
     229            0 :   if (points_are_equal(shell_A->origin, grpp_origin) &&
     230            0 :       points_are_equal(shell_B->origin, grpp_origin)) {
     231              :     return;
     232              :   }
     233              : 
     234              :   /*
     235              :    * d<ACB>/dD = 0
     236              :    */
     237            0 :   if (!points_are_equal(shell_A->origin, point_3d) &&
     238            0 :       !points_are_equal(shell_B->origin, point_3d) &&
     239            0 :       !points_are_equal(grpp_origin, point_3d)) {
     240              :     return;
     241              :   }
     242              : 
     243            0 :   double *A = shell_A->origin;
     244            0 :   double *B = shell_B->origin;
     245            0 :   double *C = grpp_origin;
     246            0 :   double *D = point_3d;
     247              : 
     248            0 :   const int diff_bra = 1;
     249            0 :   const int diff_ket = 0;
     250              : 
     251              :   /*
     252              :    * Type ACB
     253              :    */
     254            0 :   if (!points_are_equal(A, C) && !points_are_equal(C, B) &&
     255            0 :       !points_are_equal(A, B)) {
     256            0 :     if (points_are_equal(A, D)) {
     257            0 :       grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
     258              :                                  grad_arep, grad_so_x, grad_so_y, grad_so_z,
     259              :                                  diff_bra, +1.0);
     260              :     }
     261            0 :     if (points_are_equal(B, D)) {
     262            0 :       grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
     263              :                                  grad_arep, grad_so_x, grad_so_y, grad_so_z,
     264              :                                  diff_ket, +1.0);
     265              :     }
     266            0 :     if (points_are_equal(C, D)) {
     267            0 :       grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
     268              :                                  grad_arep, grad_so_x, grad_so_y, grad_so_z,
     269              :                                  diff_bra, -1.0);
     270            0 :       grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
     271              :                                  grad_arep, grad_so_x, grad_so_y, grad_so_z,
     272              :                                  diff_ket, -1.0);
     273              :     }
     274              :   }
     275              : 
     276              :   /*
     277              :    * Type ACA
     278              :    */
     279            0 :   if (points_are_equal(A, B) && !points_are_equal(A, C)) {
     280            0 :     if (points_are_equal(A, D)) {
     281            0 :       grpp_gradient_diff_bra_contribution(shell_A, shell_B, grpp_operator,
     282              :                                           grpp_origin, grad_arep, grad_so_x,
     283              :                                           grad_so_y, grad_so_z, +1.0);
     284            0 :       grpp_gradient_diff_ket_contribution(shell_A, shell_B, grpp_operator,
     285              :                                           grpp_origin, grad_arep, grad_so_x,
     286              :                                           grad_so_y, grad_so_z, +1.0);
     287              :     } else {
     288            0 :       grpp_gradient_diff_bra_contribution(shell_A, shell_B, grpp_operator,
     289              :                                           grpp_origin, grad_arep, grad_so_x,
     290              :                                           grad_so_y, grad_so_z, -1.0);
     291            0 :       grpp_gradient_diff_ket_contribution(shell_A, shell_B, grpp_operator,
     292              :                                           grpp_origin, grad_arep, grad_so_x,
     293              :                                           grad_so_y, grad_so_z, -1.0);
     294              :     }
     295              :   }
     296              : 
     297              :   /*
     298              :    * Type ACC
     299              :    */
     300            0 :   if (!points_are_equal(A, C) && points_are_equal(C, B)) {
     301            0 :     if (points_are_equal(A, D)) {
     302            0 :       grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
     303              :                                  grad_arep, grad_so_x, grad_so_y, grad_so_z,
     304              :                                  diff_bra, +1.0);
     305              :     } else {
     306            0 :       grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
     307              :                                  grad_arep, grad_so_x, grad_so_y, grad_so_z,
     308              :                                  diff_bra, -1.0);
     309              :     }
     310              :   }
     311              : 
     312              :   /*
     313              :    * Type CCB
     314              :    */
     315            0 :   if (points_are_equal(A, C) && !points_are_equal(C, B)) {
     316            0 :     if (points_are_equal(B, D)) {
     317            0 :       grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
     318              :                                  grad_arep, grad_so_x, grad_so_y, grad_so_z,
     319              :                                  diff_ket, +1.0);
     320              :     } else {
     321            0 :       grpp_gradient_contribution(shell_A, shell_B, grpp_operator, grpp_origin,
     322              :                                  grad_arep, grad_so_x, grad_so_y, grad_so_z,
     323              :                                  diff_ket, -1.0);
     324              :     }
     325              :   }
     326              : }
     327              : 
     328              : /**
     329              :  * Calculates contribution to gradients arising from the < df/dA | V | g > term:
     330              :  *
     331              :  * grad += factor * < df/dA | V | g >
     332              :  *
     333              :  * (bra basis function is differentiated).
     334              :  */
     335            0 : void grpp_gradient_diff_bra_contribution(
     336              :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
     337              :     libgrpp_grpp_t *grpp_operator, double *grpp_origin, double **grad_arep,
     338              :     double **grad_so_x, double **grad_so_y, double **grad_so_z, double factor) {
     339              :   /*
     340              :    * calculate integrals < df/dA | V | B >
     341              :    */
     342            0 :   double *arep_matrix_down = NULL;
     343            0 :   double *so_x_matrix_down = NULL;
     344            0 :   double *so_y_matrix_down = NULL;
     345            0 :   double *so_z_matrix_down = NULL;
     346            0 :   double *arep_matrix_up = NULL;
     347            0 :   double *so_x_matrix_up = NULL;
     348            0 :   double *so_y_matrix_up = NULL;
     349            0 :   double *so_z_matrix_up = NULL;
     350            0 :   int cart_size_down = 0;
     351            0 :   int cart_size_up = 0;
     352              : 
     353            0 :   grpp_gradient_diff_bra_grpp_integrals(
     354              :       shell_A, shell_B, grpp_operator, grpp_origin, &arep_matrix_down,
     355              :       &so_x_matrix_down, &so_y_matrix_down, &so_z_matrix_down, &arep_matrix_up,
     356              :       &so_x_matrix_up, &so_y_matrix_up, &so_z_matrix_up, &cart_size_down,
     357              :       &cart_size_up);
     358              : 
     359              :   /*
     360              :    * construct contributions to gradients:
     361              :    * d<A|V|B>/dA += < df/dA | V | B >
     362              :    */
     363            0 :   for (int icoord = 0; icoord < 3; icoord++) {
     364            0 :     for (int i = 0; i < shell_A->cart_size; i++) {
     365            0 :       for (int j = 0; j < shell_B->cart_size; j++) {
     366              : 
     367            0 :         int bra_nlm[3];
     368            0 :         bra_nlm[0] = shell_A->cart_list[3 * i + 0];
     369            0 :         bra_nlm[1] = shell_A->cart_list[3 * i + 1];
     370            0 :         bra_nlm[2] = shell_A->cart_list[3 * i + 2];
     371              : 
     372            0 :         int ket_nlm[3];
     373            0 :         ket_nlm[0] = shell_B->cart_list[3 * j + 0];
     374            0 :         ket_nlm[1] = shell_B->cart_list[3 * j + 1];
     375            0 :         ket_nlm[2] = shell_B->cart_list[3 * j + 2];
     376              : 
     377            0 :         int index = i * shell_B->cart_size + j;
     378              : 
     379              :         /*
     380              :          * contribution from the L-1 gaussian
     381              :          */
     382            0 :         if (shell_A->L > 0) {
     383            0 :           bra_nlm[icoord] -= 1;
     384            0 :           int bra_index = libgrpp_nlm_to_linear(bra_nlm);
     385            0 :           int ket_index = libgrpp_nlm_to_linear(ket_nlm);
     386            0 :           bra_nlm[icoord] += 1;
     387              : 
     388            0 :           grad_arep[icoord][index] -=
     389            0 :               factor * bra_nlm[icoord] *
     390            0 :               arep_matrix_down[shell_B->cart_size * bra_index + ket_index];
     391            0 :           grad_so_x[icoord][index] -=
     392            0 :               factor * bra_nlm[icoord] *
     393            0 :               so_x_matrix_down[shell_B->cart_size * bra_index + ket_index];
     394            0 :           grad_so_y[icoord][index] -=
     395            0 :               factor * bra_nlm[icoord] *
     396            0 :               so_y_matrix_down[shell_B->cart_size * bra_index + ket_index];
     397            0 :           grad_so_z[icoord][index] -=
     398            0 :               factor * bra_nlm[icoord] *
     399            0 :               so_z_matrix_down[shell_B->cart_size * bra_index + ket_index];
     400              :         }
     401              : 
     402              :         /*
     403              :          * contribution from the L+1 gaussian
     404              :          */
     405            0 :         bra_nlm[icoord] += 1;
     406            0 :         int bra_index = libgrpp_nlm_to_linear(bra_nlm);
     407            0 :         int ket_index = libgrpp_nlm_to_linear(ket_nlm);
     408            0 :         bra_nlm[icoord] -= 1;
     409              : 
     410            0 :         grad_arep[icoord][index] +=
     411            0 :             factor * arep_matrix_up[shell_B->cart_size * bra_index + ket_index];
     412            0 :         grad_so_x[icoord][index] +=
     413            0 :             factor * so_x_matrix_up[shell_B->cart_size * bra_index + ket_index];
     414            0 :         grad_so_y[icoord][index] +=
     415            0 :             factor * so_y_matrix_up[shell_B->cart_size * bra_index + ket_index];
     416            0 :         grad_so_z[icoord][index] +=
     417            0 :             factor * so_z_matrix_up[shell_B->cart_size * bra_index + ket_index];
     418              :       }
     419              :     }
     420              :   }
     421              : 
     422            0 :   if (arep_matrix_down) {
     423            0 :     free(arep_matrix_down);
     424            0 :     free(so_x_matrix_down);
     425            0 :     free(so_y_matrix_down);
     426            0 :     free(so_z_matrix_down);
     427              :   }
     428            0 :   free(arep_matrix_up);
     429            0 :   free(so_x_matrix_up);
     430            0 :   free(so_y_matrix_up);
     431            0 :   free(so_z_matrix_up);
     432            0 : }
     433              : 
     434              : /**
     435              :  * To assemble the contribution < df/dA | V | g > to gradients, one have to
     436              :  * differentiate a Gaussian function. Such a differentiation yields two
     437              :  * Gaussians with angular momenta L-1 ("down") and L+1 ("up"): dG/dA -> G(L-1)
     438              :  * and G(L+1)
     439              :  *
     440              :  * This function constructs overlap matrices with these "downgraded" and
     441              :  * "upgraded" Gaussian functions: < G(L-1) | V | G' > and < G(L+1) | V | G' >
     442              :  *
     443              :  */
     444            0 : void grpp_gradient_diff_bra_grpp_integrals(
     445              :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
     446              :     libgrpp_grpp_t *grpp_operator, double *grpp_origin,
     447              :     double **arep_matrix_down, double **so_x_matrix_down,
     448              :     double **so_y_matrix_down, double **so_z_matrix_down,
     449              :     double **arep_matrix_up, double **so_x_matrix_up, double **so_y_matrix_up,
     450              :     double **so_z_matrix_up, int *cart_size_down, int *cart_size_up) {
     451              :   /*
     452              :    * differentiation of contracted Gaussian functions
     453              :    */
     454            0 :   libgrpp_shell_t *shell_A_down = NULL;
     455            0 :   libgrpp_shell_t *shell_A_up = NULL;
     456            0 :   libgrpp_differentiate_shell(shell_A, &shell_A_down, &shell_A_up);
     457              : 
     458            0 :   *cart_size_down = 0;
     459            0 :   if (shell_A_down != NULL) {
     460            0 :     *cart_size_down = shell_A_down->cart_size;
     461              :   }
     462            0 :   *cart_size_up = shell_A_up->cart_size;
     463              : 
     464              :   /*
     465              :    * matrix < L-1 | V | L >
     466              :    */
     467            0 :   if (shell_A_down != NULL) {
     468            0 :     size_t mat_size_down = shell_A_down->cart_size * shell_B->cart_size;
     469            0 :     *arep_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
     470            0 :     *so_x_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
     471            0 :     *so_y_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
     472            0 :     *so_z_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
     473              : 
     474            0 :     libgrpp_full_grpp_integrals(
     475              :         shell_A_down, shell_B, grpp_operator, grpp_origin, *arep_matrix_down,
     476              :         *so_x_matrix_down, *so_y_matrix_down, *so_z_matrix_down);
     477              :   } else {
     478            0 :     *arep_matrix_down = NULL;
     479            0 :     *so_x_matrix_down = NULL;
     480            0 :     *so_y_matrix_down = NULL;
     481            0 :     *so_z_matrix_down = NULL;
     482              :   }
     483              : 
     484              :   /*
     485              :    * matrix < L+1 | V | L >
     486              :    */
     487            0 :   size_t mat_size_up = shell_A_up->cart_size * shell_B->cart_size;
     488            0 :   *arep_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
     489            0 :   *so_x_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
     490            0 :   *so_y_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
     491            0 :   *so_z_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
     492              : 
     493            0 :   libgrpp_full_grpp_integrals(shell_A_up, shell_B, grpp_operator, grpp_origin,
     494              :                               *arep_matrix_up, *so_x_matrix_up, *so_y_matrix_up,
     495              :                               *so_z_matrix_up);
     496              : 
     497              :   /*
     498              :    * clean up
     499              :    */
     500            0 :   if (shell_A_down) {
     501            0 :     libgrpp_delete_shell(shell_A_down);
     502              :   }
     503            0 :   libgrpp_delete_shell(shell_A_up);
     504            0 : }
     505              : 
     506              : /**
     507              :  * Calculates contribution to gradients arising from the < df/dA | V | g > term:
     508              :  *
     509              :  * grad += factor * < f | V | dg/dA >
     510              :  *
     511              :  * (bra basis function is differentiated).
     512              :  */
     513            0 : void grpp_gradient_diff_ket_contribution(
     514              :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
     515              :     libgrpp_grpp_t *grpp_operator, double *grpp_origin, double **grad_arep,
     516              :     double **grad_so_x, double **grad_so_y, double **grad_so_z, double factor) {
     517              :   /*
     518              :    * calculate integrals < df/dA | V | B >
     519              :    */
     520            0 :   double *arep_matrix_down = NULL;
     521            0 :   double *so_x_matrix_down = NULL;
     522            0 :   double *so_y_matrix_down = NULL;
     523            0 :   double *so_z_matrix_down = NULL;
     524            0 :   double *arep_matrix_up = NULL;
     525            0 :   double *so_x_matrix_up = NULL;
     526            0 :   double *so_y_matrix_up = NULL;
     527            0 :   double *so_z_matrix_up = NULL;
     528            0 :   int cart_size_down = 0;
     529            0 :   int cart_size_up = 0;
     530              : 
     531            0 :   grpp_gradient_diff_ket_grpp_integrals(
     532              :       shell_A, shell_B, grpp_operator, grpp_origin, &arep_matrix_down,
     533              :       &so_x_matrix_down, &so_y_matrix_down, &so_z_matrix_down, &arep_matrix_up,
     534              :       &so_x_matrix_up, &so_y_matrix_up, &so_z_matrix_up, &cart_size_down,
     535              :       &cart_size_up);
     536              : 
     537              :   /*
     538              :    * construct contributions to gradients:
     539              :    * d<A|B>/dA += < df/dA | V | B >
     540              :    */
     541            0 :   for (int icoord = 0; icoord < 3; icoord++) {
     542            0 :     for (int i = 0; i < shell_A->cart_size; i++) {
     543            0 :       for (int j = 0; j < shell_B->cart_size; j++) {
     544              : 
     545            0 :         int bra_nlm[3];
     546            0 :         bra_nlm[0] = shell_A->cart_list[3 * i + 0];
     547            0 :         bra_nlm[1] = shell_A->cart_list[3 * i + 1];
     548            0 :         bra_nlm[2] = shell_A->cart_list[3 * i + 2];
     549              : 
     550            0 :         int ket_nlm[3];
     551            0 :         ket_nlm[0] = shell_B->cart_list[3 * j + 0];
     552            0 :         ket_nlm[1] = shell_B->cart_list[3 * j + 1];
     553            0 :         ket_nlm[2] = shell_B->cart_list[3 * j + 2];
     554              : 
     555            0 :         int index = i * shell_B->cart_size + j;
     556              : 
     557              :         /*
     558              :          * contribution from the L-1 gaussian
     559              :          */
     560            0 :         if (shell_B->L > 0) {
     561            0 :           ket_nlm[icoord] -= 1;
     562            0 :           int bra_index = libgrpp_nlm_to_linear(bra_nlm);
     563            0 :           int ket_index = libgrpp_nlm_to_linear(ket_nlm);
     564            0 :           ket_nlm[icoord] += 1;
     565              : 
     566            0 :           grad_arep[icoord][index] -=
     567            0 :               factor * ket_nlm[icoord] *
     568            0 :               arep_matrix_down[cart_size_down * bra_index + ket_index];
     569            0 :           grad_so_x[icoord][index] -=
     570            0 :               factor * ket_nlm[icoord] *
     571            0 :               so_x_matrix_down[cart_size_down * bra_index + ket_index];
     572            0 :           grad_so_y[icoord][index] -=
     573            0 :               factor * ket_nlm[icoord] *
     574            0 :               so_y_matrix_down[cart_size_down * bra_index + ket_index];
     575            0 :           grad_so_z[icoord][index] -=
     576            0 :               factor * ket_nlm[icoord] *
     577            0 :               so_z_matrix_down[cart_size_down * bra_index + ket_index];
     578              :         }
     579              : 
     580              :         /*
     581              :          * contribution from the L+1 gaussian
     582              :          */
     583            0 :         ket_nlm[icoord] += 1;
     584            0 :         int bra_index = libgrpp_nlm_to_linear(bra_nlm);
     585            0 :         int ket_index = libgrpp_nlm_to_linear(ket_nlm);
     586            0 :         ket_nlm[icoord] -= 1;
     587              : 
     588            0 :         grad_arep[icoord][index] +=
     589            0 :             factor * arep_matrix_up[cart_size_up * bra_index + ket_index];
     590            0 :         grad_so_x[icoord][index] +=
     591            0 :             factor * so_x_matrix_up[cart_size_up * bra_index + ket_index];
     592            0 :         grad_so_y[icoord][index] +=
     593            0 :             factor * so_y_matrix_up[cart_size_up * bra_index + ket_index];
     594            0 :         grad_so_z[icoord][index] +=
     595            0 :             factor * so_z_matrix_up[cart_size_up * bra_index + ket_index];
     596              :       }
     597              :     }
     598              :   }
     599              : 
     600            0 :   if (arep_matrix_down) {
     601            0 :     free(arep_matrix_down);
     602            0 :     free(so_x_matrix_down);
     603            0 :     free(so_y_matrix_down);
     604            0 :     free(so_z_matrix_down);
     605              :   }
     606            0 :   free(arep_matrix_up);
     607            0 :   free(so_x_matrix_up);
     608            0 :   free(so_y_matrix_up);
     609            0 :   free(so_z_matrix_up);
     610            0 : }
     611              : 
     612              : /**
     613              :  * To assemble the contribution < df/dA | V | g > to gradients, one have to
     614              :  * differentiate Gaussian function. Such a differentiation yields two Gaussians
     615              :  * with angular momenta L-1 ("down") and L+1 ("up"): dG/dA -> G(L-1) and G(L+1)
     616              :  *
     617              :  * This function constructs matrices with these "downgraded" and "upgraded"
     618              :  * Gaussian functions:
     619              :  * < G(L-1) | V | G' > and < G(L+1) | V | G' >
     620              :  *
     621              :  */
     622            0 : void grpp_gradient_diff_ket_grpp_integrals(
     623              :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
     624              :     libgrpp_grpp_t *grpp_operator, double *grpp_origin,
     625              :     double **arep_matrix_down, double **so_x_matrix_down,
     626              :     double **so_y_matrix_down, double **so_z_matrix_down,
     627              :     double **arep_matrix_up, double **so_x_matrix_up, double **so_y_matrix_up,
     628              :     double **so_z_matrix_up, int *cart_size_down, int *cart_size_up) {
     629              :   /*
     630              :    * differentiation of contracted Gaussian functions
     631              :    */
     632            0 :   libgrpp_shell_t *shell_B_down = NULL;
     633            0 :   libgrpp_shell_t *shell_B_up = NULL;
     634            0 :   libgrpp_differentiate_shell(shell_B, &shell_B_down, &shell_B_up);
     635              : 
     636            0 :   *cart_size_down = 0;
     637            0 :   if (shell_B_down != NULL) {
     638            0 :     *cart_size_down = shell_B_down->cart_size;
     639              :   }
     640            0 :   *cart_size_up = shell_B_up->cart_size;
     641              : 
     642              :   /*
     643              :    * matrix < L-1 | L>
     644              :    */
     645            0 :   if (shell_B_down != NULL) {
     646            0 :     size_t mat_size_down = shell_A->cart_size * shell_B_down->cart_size;
     647            0 :     *arep_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
     648            0 :     *so_x_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
     649            0 :     *so_y_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
     650            0 :     *so_z_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
     651              : 
     652            0 :     libgrpp_full_grpp_integrals(
     653              :         // evaluate_grpp_integrals_shell_pair(
     654              :         shell_A, shell_B_down, grpp_operator, grpp_origin, *arep_matrix_down,
     655              :         *so_x_matrix_down, *so_y_matrix_down, *so_z_matrix_down);
     656              :   } else {
     657            0 :     *arep_matrix_down = NULL;
     658            0 :     *so_x_matrix_down = NULL;
     659            0 :     *so_y_matrix_down = NULL;
     660            0 :     *so_z_matrix_down = NULL;
     661              :   }
     662              : 
     663              :   /*
     664              :    * matrix < L+1 | L>
     665              :    */
     666            0 :   size_t mat_size_up = shell_A->cart_size * shell_B_up->cart_size;
     667            0 :   *arep_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
     668            0 :   *so_x_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
     669            0 :   *so_y_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
     670            0 :   *so_z_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
     671              : 
     672            0 :   libgrpp_full_grpp_integrals(
     673              :       // evaluate_grpp_integrals_shell_pair(
     674              :       shell_A, shell_B_up, grpp_operator, grpp_origin, *arep_matrix_up,
     675              :       *so_x_matrix_up, *so_y_matrix_up, *so_z_matrix_up);
     676              : 
     677              :   /*
     678              :    * clean up
     679              :    */
     680            0 :   if (shell_B_down) {
     681            0 :     libgrpp_delete_shell(shell_B_down);
     682              :   }
     683            0 :   libgrpp_delete_shell(shell_B_up);
     684            0 : }
     685              : 
     686            0 : void grpp_gradient_contribution(libgrpp_shell_t *shell_A,
     687              :                                 libgrpp_shell_t *shell_B,
     688              :                                 libgrpp_grpp_t *grpp_operator,
     689              :                                 double *grpp_origin, double **grad_arep,
     690              :                                 double **grad_so_x, double **grad_so_y,
     691              :                                 double **grad_so_z, int diff_bra,
     692              :                                 double factor) {
     693              :   //  int diff_ket = 0;
     694              : 
     695            0 :   if (diff_bra == 0) {
     696              :     diff_bra = 0;
     697              :     //    diff_ket = 1;
     698              :   } else {
     699            0 :     diff_bra = 1;
     700              :     //    diff_ket = 0;
     701              :   }
     702              : 
     703              :   /*
     704              :    * calculate overlap integrals < df/dA | V | B >
     705              :    */
     706            0 :   double *arep_matrix_down = NULL;
     707            0 :   double *so_x_matrix_down = NULL;
     708            0 :   double *so_y_matrix_down = NULL;
     709            0 :   double *so_z_matrix_down = NULL;
     710            0 :   double *arep_matrix_up = NULL;
     711            0 :   double *so_x_matrix_up = NULL;
     712            0 :   double *so_y_matrix_up = NULL;
     713            0 :   double *so_z_matrix_up = NULL;
     714            0 :   int cart_size_down = 0;
     715            0 :   int cart_size_up = 0;
     716              : 
     717            0 :   grpp_gradient_diff_gaussian(
     718              :       shell_A, shell_B, grpp_operator, grpp_origin, &arep_matrix_down,
     719              :       &so_x_matrix_down, &so_y_matrix_down, &so_z_matrix_down, &arep_matrix_up,
     720              :       &so_x_matrix_up, &so_y_matrix_up, &so_z_matrix_up, &cart_size_down,
     721              :       &cart_size_up, diff_bra);
     722              : 
     723              :   /*
     724              :    * construct contributions to gradients:
     725              :    * d<A|U|B>/dA += < df/dA | U | B >
     726              :    */
     727            0 :   for (int icoord = 0; icoord < 3; icoord++) {
     728            0 :     for (int i = 0; i < shell_A->cart_size; i++) {
     729            0 :       for (int j = 0; j < shell_B->cart_size; j++) {
     730              : 
     731            0 :         int bra_nlm[3];
     732            0 :         bra_nlm[0] = shell_A->cart_list[3 * i + 0];
     733            0 :         bra_nlm[1] = shell_A->cart_list[3 * i + 1];
     734            0 :         bra_nlm[2] = shell_A->cart_list[3 * i + 2];
     735              : 
     736            0 :         int ket_nlm[3];
     737            0 :         ket_nlm[0] = shell_B->cart_list[3 * j + 0];
     738            0 :         ket_nlm[1] = shell_B->cart_list[3 * j + 1];
     739            0 :         ket_nlm[2] = shell_B->cart_list[3 * j + 2];
     740              : 
     741            0 :         int index = i * shell_B->cart_size + j;
     742              : 
     743            0 :         int *diff_nlm = diff_bra ? bra_nlm : ket_nlm;
     744              : 
     745              :         /*
     746              :          * contribution from the L-1 gaussian
     747              :          */
     748            0 :         if (cart_size_down > 0) {
     749            0 :           diff_nlm[icoord] -= 1;
     750            0 :           int bra_index = libgrpp_nlm_to_linear(bra_nlm);
     751            0 :           int ket_index = libgrpp_nlm_to_linear(ket_nlm);
     752            0 :           diff_nlm[icoord] += 1;
     753              : 
     754            0 :           int n = diff_nlm[icoord];
     755            0 :           int row_len = diff_bra ? shell_B->cart_size : cart_size_down;
     756            0 :           int index_down = row_len * bra_index + ket_index;
     757              : 
     758            0 :           grad_arep[icoord][index] -= factor * n * arep_matrix_down[index_down];
     759            0 :           grad_so_x[icoord][index] -= factor * n * so_x_matrix_down[index_down];
     760            0 :           grad_so_y[icoord][index] -= factor * n * so_y_matrix_down[index_down];
     761            0 :           grad_so_z[icoord][index] -= factor * n * so_z_matrix_down[index_down];
     762              :         }
     763              : 
     764              :         /*
     765              :          * contribution from the L+1 gaussian
     766              :          */
     767            0 :         diff_nlm[icoord] += 1;
     768            0 :         int bra_index = libgrpp_nlm_to_linear(bra_nlm);
     769            0 :         int ket_index = libgrpp_nlm_to_linear(ket_nlm);
     770            0 :         diff_nlm[icoord] -= 1;
     771              : 
     772            0 :         int row_len = diff_bra ? shell_B->cart_size : cart_size_up;
     773            0 :         int index_up = row_len * bra_index + ket_index;
     774              : 
     775            0 :         grad_arep[icoord][index] += factor * arep_matrix_up[index_up];
     776            0 :         grad_so_x[icoord][index] += factor * so_x_matrix_up[index_up];
     777            0 :         grad_so_y[icoord][index] += factor * so_y_matrix_up[index_up];
     778            0 :         grad_so_z[icoord][index] += factor * so_z_matrix_up[index_up];
     779              :       }
     780              :     }
     781              :   }
     782              : 
     783            0 :   if (arep_matrix_down) {
     784            0 :     free(arep_matrix_down);
     785            0 :     free(so_x_matrix_down);
     786            0 :     free(so_y_matrix_down);
     787            0 :     free(so_z_matrix_down);
     788              :   }
     789            0 :   free(arep_matrix_up);
     790            0 :   free(so_x_matrix_up);
     791            0 :   free(so_y_matrix_up);
     792            0 :   free(so_z_matrix_up);
     793            0 : }
     794              : 
     795              : /**
     796              :  * To assemble the contribution < df/dA | V | g > to gradients, one have to
     797              :  * differentiate Gaussian function. Such a differentiation yields two Gaussians
     798              :  * with angular momenta L-1 ("down") and L+1 ("up"): dG/dA -> G(L-1) and G(L+1)
     799              :  *
     800              :  * This function constructs matrices with these "downgraded" and "upgraded"
     801              :  * Gaussian functions:
     802              :  * < G(L-1) | V | G' > and < G(L+1) | V | G' >
     803              :  *
     804              :  */
     805            0 : void grpp_gradient_diff_gaussian(
     806              :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B,
     807              :     libgrpp_grpp_t *grpp_operator, double *grpp_origin,
     808              :     double **arep_matrix_down, double **so_x_matrix_down,
     809              :     double **so_y_matrix_down, double **so_z_matrix_down,
     810              :     double **arep_matrix_up, double **so_x_matrix_up, double **so_y_matrix_up,
     811              :     double **so_z_matrix_up, int *cart_size_down, int *cart_size_up,
     812              :     int diff_bra) {
     813              :   // int diff_ket = 0;
     814              : 
     815            0 :   if (diff_bra == 0) {
     816              :     diff_bra = 0;
     817              :     //    diff_ket = 1;
     818              :   } else {
     819            0 :     diff_bra = 1;
     820              :     //    diff_ket = 0;
     821              :   }
     822              : 
     823              :   /*
     824              :    * which shell should be differentiated, bra or ket
     825              :    */
     826            0 :   libgrpp_shell_t *const_shell = NULL;
     827            0 :   libgrpp_shell_t *diff_shell = NULL;
     828            0 :   if (diff_bra) {
     829              :     diff_shell = shell_A;
     830              :     const_shell = shell_B;
     831              :   } else {
     832              :     diff_shell = shell_B;
     833              :     const_shell = shell_A;
     834              :   }
     835              : 
     836              :   /*
     837              :    * differentiation of contracted Gaussian functions
     838              :    */
     839            0 :   libgrpp_shell_t *shell_down = NULL;
     840            0 :   libgrpp_shell_t *shell_up = NULL;
     841            0 :   libgrpp_differentiate_shell(diff_shell, &shell_down, &shell_up);
     842              : 
     843            0 :   *cart_size_down = 0;
     844            0 :   if (shell_down != NULL) {
     845            0 :     *cart_size_down = shell_down->cart_size;
     846              :   }
     847            0 :   *cart_size_up = shell_up->cart_size;
     848              : 
     849              :   /*
     850              :    * GRPP matrix:
     851              :    * < L-1 | U | L > or < L | U | L-1 >
     852              :    */
     853            0 :   if (shell_down != NULL) {
     854            0 :     size_t mat_size_down = const_shell->cart_size * shell_down->cart_size;
     855            0 :     *arep_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
     856            0 :     *so_x_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
     857            0 :     *so_y_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
     858            0 :     *so_z_matrix_down = (double *)calloc(mat_size_down, sizeof(double));
     859              : 
     860            0 :     libgrpp_full_grpp_integrals(
     861              :         diff_bra ? shell_down : shell_A, diff_bra ? shell_B : shell_down,
     862              :         grpp_operator, grpp_origin, *arep_matrix_down, *so_x_matrix_down,
     863              :         *so_y_matrix_down, *so_z_matrix_down);
     864              :   } else {
     865            0 :     *arep_matrix_down = NULL;
     866            0 :     *so_x_matrix_down = NULL;
     867            0 :     *so_y_matrix_down = NULL;
     868            0 :     *so_z_matrix_down = NULL;
     869              :   }
     870              : 
     871              :   /*
     872              :    * GRPP matrix:
     873              :    * < L+1 | U | L > or < L | U | L+1 >
     874              :    */
     875            0 :   size_t mat_size_up = const_shell->cart_size * shell_up->cart_size;
     876            0 :   *arep_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
     877            0 :   *so_x_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
     878            0 :   *so_y_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
     879            0 :   *so_z_matrix_up = (double *)calloc(mat_size_up, sizeof(double));
     880              : 
     881            0 :   libgrpp_full_grpp_integrals(diff_bra ? shell_up : shell_A,
     882              :                               diff_bra ? shell_B : shell_up, grpp_operator,
     883              :                               grpp_origin, *arep_matrix_up, *so_x_matrix_up,
     884              :                               *so_y_matrix_up, *so_z_matrix_up);
     885              : 
     886              :   /*
     887              :    * clean up
     888              :    */
     889            0 :   if (shell_down) {
     890            0 :     libgrpp_delete_shell(shell_down);
     891              :   }
     892            0 :   libgrpp_delete_shell(shell_up);
     893            0 : }
     894              : 
     895              : /**
     896              :  * Allocates memory for gradients for a given shell pair.
     897              :  */
     898            0 : double **libgrpp_alloc_gradients(libgrpp_shell_t *bra, libgrpp_shell_t *ket) {
     899            0 :   size_t size = bra->cart_size * ket->cart_size;
     900              : 
     901            0 :   double **grad = (double **)calloc(3, sizeof(double *));
     902            0 :   for (int icoord = 0; icoord < 3; icoord++) {
     903            0 :     grad[icoord] = (double *)calloc(size, sizeof(double));
     904              :   }
     905              : 
     906            0 :   return grad;
     907              : }
     908              : 
     909              : /**
     910              :  * Deallocates arrays containing gradients of AO integrals.
     911              :  */
     912            0 : void libgrpp_dealloc_gradients(double **grad) {
     913            0 :   free(grad[0]);
     914            0 :   free(grad[1]);
     915            0 :   free(grad[2]);
     916            0 :   free(grad);
     917            0 : }
        

Generated by: LCOV version 2.0-1