CheMPS2
DMRGSCFintegrals.cpp
1 /*
2  CheMPS2: a spin-adapted implementation of DMRG for ab initio quantum chemistry
3  Copyright (C) 2013-2016 Sebastian Wouters
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License along
16  with this program; if not, write to the Free Software Foundation, Inc.,
17  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 #include <assert.h>
21 
22 #include "DMRGSCFintegrals.h"
23 
25 
26  numberOfIrreps = iHandler->getNirreps();
27  NCORE = new int[ numberOfIrreps ];
28  NVIRTUAL = new int[ numberOfIrreps ];
29  NTOTAL = new int[ numberOfIrreps ];
30 
31  for (int irrep = 0; irrep < numberOfIrreps; irrep++){
32  NCORE[ irrep ] = iHandler->getNOCC( irrep ) + iHandler->getNDMRG( irrep );
33  NVIRTUAL[ irrep ] = iHandler->getNVIRT( irrep );
34  NTOTAL[ irrep ] = iHandler->getNORB( irrep );
35  }
36 
37  coulomb_size = calcNumCoulombElements( true );
38  exchange_size = calcNumExchangeElements( true );
39  coulomb_array = new double[ coulomb_size ];
40  exchange_array = new double[ exchange_size ];
41 
42 }
43 
44 long long CheMPS2::DMRGSCFintegrals::calcNumCoulombElements(const bool allocate){
45 
46  // The object sizes
47  long long theSize = 0;
48 
49  if (allocate){ coulomb_ptr = new long long***[ numberOfIrreps ]; }
50  for (int I_cc = 0; I_cc < numberOfIrreps; I_cc++){ // Loop the irrep I_cc = I_c1 x I_c2 = I_a1 x I_a2
51  if (allocate){ coulomb_ptr[ I_cc ] = new long long**[ numberOfIrreps ]; }
52  for (int I_c1 = 0; I_c1 < numberOfIrreps; I_c1++){
53  const int I_c2 = Irreps::directProd( I_cc , I_c1 );
54  if ( ( NCORE[ I_c1 ] > 0 ) && ( NCORE[ I_c2 ] > 0 ) && ( I_c1 <= I_c2 ) ){
55  if (allocate){ coulomb_ptr[ I_cc ][ I_c1 ] = new long long*[ numberOfIrreps ]; }
56  for (int I_a1 = 0; I_a1 < numberOfIrreps; I_a1++){
57  const int I_a2 = Irreps::directProd( I_cc, I_a1 );
58  if ( ( NTOTAL[ I_a1 ] > 0 ) && ( NTOTAL[ I_a2 ] > 0 ) && ( I_a1 <= I_a2 ) ){
59  if ( I_cc == 0 ){ // I_c1 == I_c2 and I_a1 == I_a2
60  if (allocate){
61  const long long coretriangle = ( NCORE[ I_c1 ] * ( NCORE[ I_c1 ] + 1 ) ) / 2;
62  const long long alltriangle = ( NTOTAL[ I_a1 ] * ( NTOTAL[ I_a1 ] + 1 ) ) / 2;
63  coulomb_ptr[ I_cc ][ I_c1 ][ I_a1 ] = new long long[ coretriangle ];
64  for (int combinedcore = 0; combinedcore < coretriangle; combinedcore++){
65  coulomb_ptr[ I_cc ][ I_c1 ][ I_a1 ][ combinedcore ] = theSize;
66  theSize += alltriangle;
67  }
68  } else { delete [] coulomb_ptr[ I_cc ][ I_c1 ][ I_a1 ]; }
69  } else { // I_c1 < I_c2 and I_a1 < I_a2
70  if (allocate){
71  const long long coresquare = NCORE[ I_c1 ] * NCORE[ I_c2 ];
72  const long long allsquare = NTOTAL[ I_a1 ] * NTOTAL[ I_a2 ];
73  coulomb_ptr[ I_cc ][ I_c1 ][ I_a1 ] = new long long[ coresquare ];
74  for (int combinedcore = 0; combinedcore < coresquare; combinedcore++){
75  coulomb_ptr[ I_cc ][ I_c1 ][ I_a1 ][ combinedcore ] = theSize;
76  theSize += allsquare;
77  }
78  } else { delete [] coulomb_ptr[ I_cc ][ I_c1 ][ I_a1 ]; }
79  }
80  }
81  }
82  if (!allocate){ delete [] coulomb_ptr[ I_cc ][ I_c1 ]; }
83  }
84  }
85  if (!allocate){ delete [] coulomb_ptr[ I_cc ]; }
86  }
87  if (!allocate){ delete [] coulomb_ptr; }
88 
89  return theSize;
90 
91 }
92 
93 long long CheMPS2::DMRGSCFintegrals::calcNumExchangeElements(const bool allocate){
94 
95  // The object sizes
96  long long theSize = 0;
97 
98  if (allocate){ exchange_ptr = new long long***[ numberOfIrreps ]; }
99  for (int I_cc = 0; I_cc < numberOfIrreps; I_cc++){ // Loop the irrep I_cc = I_c1 x I_c2 = I_v1 x I_v2
100  if (allocate){ exchange_ptr[ I_cc ] = new long long**[ numberOfIrreps ]; }
101  for (int I_c1 = 0; I_c1 < numberOfIrreps; I_c1++){
102  const int I_c2 = Irreps::directProd( I_cc , I_c1 );
103  if ( ( NCORE[ I_c1 ] > 0 ) && ( NCORE[ I_c2 ] > 0 ) && ( I_c1 <= I_c2 ) ){
104  if (allocate){ exchange_ptr[ I_cc ][ I_c1 ] = new long long*[ numberOfIrreps ]; }
105  for (int I_v1 = 0; I_v1 < numberOfIrreps; I_v1++){
106  const int I_v2 = Irreps::directProd( I_cc, I_v1 );
107  if ( ( NTOTAL[ I_v1 ] > 0 ) && ( NTOTAL[ I_v2 ] > 0 ) ){ // Here no I_v1 <= I_v2 !!
108  const long long virtualsquare = NVIRTUAL[ I_v1 ] * NVIRTUAL[ I_v2 ];
109  if ( I_cc == 0 ){ // I_c1 == I_c2 and I_v1 == I_v2
110  if (allocate){
111  const long long coretriangle = ( NCORE[ I_c1 ] * ( NCORE[ I_c1 ] + 1 ) ) / 2;
112  exchange_ptr[ I_cc ][ I_c1 ][ I_v1 ] = new long long[ coretriangle ];
113  for (int combinedcore = 0; combinedcore < coretriangle; combinedcore++){
114  exchange_ptr[ I_cc ][ I_c1 ][ I_v1 ][ combinedcore ] = theSize;
115  theSize += virtualsquare;
116  }
117  } else { delete [] exchange_ptr[ I_cc ][ I_c1 ][ I_v1 ]; }
118  } else { // I_c1 < I_c2 and I_v1 != I_v2
119  if (allocate){
120  const long long coresquare = NCORE[ I_c1 ] * NCORE[ I_c2 ];
121  exchange_ptr[ I_cc ][ I_c1 ][ I_v1 ] = new long long[ coresquare ];
122  for (int combinedcore = 0; combinedcore < coresquare; combinedcore++){
123  exchange_ptr[ I_cc ][ I_c1 ][ I_v1 ][ combinedcore ] = theSize;
124  theSize += virtualsquare;
125  }
126  } else { delete [] exchange_ptr[ I_cc ][ I_c1 ][ I_v1 ]; }
127  }
128  }
129  }
130  if (!allocate){ delete [] exchange_ptr[ I_cc ][ I_c1 ]; }
131  }
132  }
133  if (!allocate){ delete [] exchange_ptr[ I_cc ]; }
134  }
135  if (!allocate){ delete [] exchange_ptr; }
136 
137  return theSize;
138 
139 }
140 
142 
143  delete [] coulomb_array;
144  delete [] exchange_array;
145 
146  calcNumCoulombElements( false );
147  calcNumExchangeElements( false );
148 
149  delete [] NCORE;
150  delete [] NVIRTUAL;
151  delete [] NTOTAL;
152 
153 }
154 
156 
157  for (long long counter = 0; counter < coulomb_size; counter++){ coulomb_array[ counter ] = 0.0; }
158  for (long long counter = 0; counter < exchange_size; counter++){ exchange_array[ counter ] = 0.0; }
159 
160 }
161 
162 long long CheMPS2::DMRGSCFintegrals::get_coulomb_ptr( const int Ic1, const int Ic2, const int Ia1, const int Ia2, const int c1, const int c2, const int a1, const int a2 ) const{
163 
164  const int Icc = Irreps::directProd( Ic1, Ic2 );
165  assert( Icc == Irreps::directProd( Ia1, Ia2 ) );
166 
167  if ( Icc == 0 ){ // Ic1 == Ic2 and Ia1 == Ia2
168  const int index_c = ( c1 <= c2 ) ? c1 + (c2 * ( c2 + 1 ))/2 : c2 + (c1 * ( c1 + 1 ))/2 ;
169  const int index_a = ( a1 <= a2 ) ? a1 + (a2 * ( a2 + 1 ))/2 : a2 + (a1 * ( a1 + 1 ))/2 ;
170  return coulomb_ptr[ Icc ][ Ic1 ][ Ia1 ][ index_c ] + index_a ;
171  }
172 
173  // Ic1 != Ic2 and Ia1 != Ia2
174  const int irrep_c = ( Ic1 < Ic2 ) ? Ic1 : Ic2 ;
175  const int irrep_a = ( Ia1 < Ia2 ) ? Ia1 : Ia2 ;
176  const int index_c = ( Ic1 < Ic2 ) ? c1 + NCORE[ Ic1 ] * c2 : c2 + NCORE[ Ic2 ] * c1 ;
177  const int index_a = ( Ia1 < Ia2 ) ? a1 + NTOTAL[ Ia1 ] * a2 : a2 + NTOTAL[ Ia2 ] * a1 ;
178  return coulomb_ptr[ Icc ][ irrep_c ][ irrep_a ][ index_c ] + index_a ;
179 
180 }
181 
182 void CheMPS2::DMRGSCFintegrals::set_coulomb(const int Ic1, const int Ic2, const int Ia1, const int Ia2, const int c1, const int c2, const int a1, const int a2, const double val){
183 
184  coulomb_array[ get_coulomb_ptr( Ic1, Ic2, Ia1, Ia2, c1, c2, a1, a2 ) ] = val;
185 
186 }
187 
188 void CheMPS2::DMRGSCFintegrals::add_coulomb(const int Ic1, const int Ic2, const int Ia1, const int Ia2, const int c1, const int c2, const int a1, const int a2, const double val){
189 
190  coulomb_array[ get_coulomb_ptr( Ic1, Ic2, Ia1, Ia2, c1, c2, a1, a2 ) ] += val;
191 
192 }
193 
194 double CheMPS2::DMRGSCFintegrals::get_coulomb(const int Ic1, const int Ic2, const int Ia1, const int Ia2, const int c1, const int c2, const int a1, const int a2) const{
195 
196  return coulomb_array[ get_coulomb_ptr( Ic1, Ic2, Ia1, Ia2, c1, c2, a1, a2 ) ];
197 
198 }
199 
200 long long CheMPS2::DMRGSCFintegrals::get_exchange_ptr( const int Ic1, const int Ic2, const int Iv1, const int Iv2, const int c1, const int c2, const int v1, const int v2 ) const{
201 
202  const int Icc = Irreps::directProd( Ic1, Ic2 );
203  assert( Icc == Irreps::directProd( Iv1, Iv2 ) );
204 
205  if ( Icc == 0 ){ // Ic1 == Ic2 and Iv1 == Iv2
206 
207  if ( c1 <= c2 ){
208  return exchange_ptr[ Icc ][ Ic1 ][ Iv1 ][ c1 + (c2 * ( c2 + 1 ))/2 ] + v1 - NCORE[ Iv1 ] + NVIRTUAL[ Iv1 ] * ( v2 - NCORE[ Iv2 ] ) ;
209  } else {
210  return exchange_ptr[ Icc ][ Ic2 ][ Iv2 ][ c2 + (c1 * ( c1 + 1 ))/2 ] + v2 - NCORE[ Iv2 ] + NVIRTUAL[ Iv2 ] * ( v1 - NCORE[ Iv1 ] ) ;
211  }
212 
213  } else { // Ic1 != Ic2
214 
215  if ( Ic1 < Ic2 ){
216  return exchange_ptr[ Icc ][ Ic1 ][ Iv1 ][ c1 + NCORE[ Ic1 ] * c2 ] + v1 - NCORE[ Iv1 ] + NVIRTUAL[ Iv1 ] * ( v2 - NCORE[ Iv2 ] ) ;
217  } else {
218  return exchange_ptr[ Icc ][ Ic2 ][ Iv2 ][ c2 + NCORE[ Ic2 ] * c1 ] + v2 - NCORE[ Iv2 ] + NVIRTUAL[ Iv2 ] * ( v1 - NCORE[ Iv1 ] ) ;
219  }
220 
221  }
222 
223  return -1;
224 
225 }
226 
227 void CheMPS2::DMRGSCFintegrals::set_exchange(const int Ic1, const int Ic2, const int Iv1, const int Iv2, const int c1, const int c2, const int v1, const int v2, const double val){
228 
229  exchange_array[ get_exchange_ptr( Ic1, Ic2, Iv1, Iv2, c1, c2, v1, v2 ) ] = val;
230 
231 }
232 
233 void CheMPS2::DMRGSCFintegrals::add_exchange(const int Ic1, const int Ic2, const int Iv1, const int Iv2, const int c1, const int c2, const int v1, const int v2, const double val){
234 
235  exchange_array[ get_exchange_ptr( Ic1, Ic2, Iv1, Iv2, c1, c2, v1, v2 ) ] += val;
236 
237 }
238 
239 double CheMPS2::DMRGSCFintegrals::get_exchange(const int Ic1, const int Ic2, const int Iv1, const int Iv2, const int c1, const int c2, const int v1, const int v2) const{
240 
241  return exchange_array[ get_exchange_ptr( Ic1, Ic2, Iv1, Iv2, c1, c2, v1, v2 ) ];
242 
243 }
244 
245 double CheMPS2::DMRGSCFintegrals::FourIndexAPI(const int I1, const int I2, const int I3, const int I4, const int index1, const int index2, const int index3, const int index4) const{
246 
247  assert( Irreps::directProd( I1, I2 ) == Irreps::directProd( I3, I4 ) );
248 
249  const bool core1 = ( index1 < NCORE[I1] ) ? true : false;
250  const bool core2 = ( index2 < NCORE[I2] ) ? true : false;
251  const bool core3 = ( index3 < NCORE[I3] ) ? true : false;
252  const bool core4 = ( index4 < NCORE[I4] ) ? true : false;
253 
254  const int numCore = ( ( core1 ) ? 1 : 0 ) + ( ( core2 ) ? 1 : 0 ) + ( ( core3 ) ? 1 : 0 ) + ( ( core4 ) ? 1 : 0 );
255  assert( numCore >= 2 );
256 
257  if ( numCore == 4 ){
258  return get_coulomb(I1, I3, I2, I4, index1, index3, index2, index4);
259  }
260 
261  if ( numCore == 3 ){
262  if (( !core1 ) || ( !core3 )){ return get_coulomb(I2, I4, I1, I3, index2, index4, index1, index3); }
263  if (( !core2 ) || ( !core4 )){ return get_coulomb(I1, I3, I2, I4, index1, index3, index2, index4); }
264  }
265 
266  if ( numCore == 2 ){
267  if ( !core1 ){
268  if ( !core2 ){ return get_exchange(I3, I4, I1, I2, index3, index4, index1, index2); }
269  if ( !core3 ){ return get_coulomb( I2, I4, I1, I3, index2, index4, index1, index3); }
270  if ( !core4 ){ return get_exchange(I3, I2, I1, I4, index3, index2, index1, index4); }
271  }
272  if ( !core2 ){
273  if ( !core3 ){ return get_exchange(I4, I1, I2, I3, index4, index1, index2, index3); }
274  if ( !core4 ){ return get_coulomb( I1, I3, I2, I4, index1, index3, index2, index4); }
275  }
276  return get_exchange(I1, I2, I3, I4, index1, index2, index3, index4);
277  }
278 
279  assert( 0 == 1 );
280  return 0.0;
281 
282 }
283 
double get_coulomb(const int Ic1, const int Ic2, const int Ia1, const int Ia2, const int c1, const int c2, const int a1, const int a2) const
Get an element of the Coulomb object ( c1 c2 | a1 a2 )
int getNORB(const int irrep) const
Get the number of orbitals for an irrep.
virtual ~DMRGSCFintegrals()
Destructor.
void add_coulomb(const int Ic1, const int Ic2, const int Ia1, const int Ia2, const int c1, const int c2, const int a1, const int a2, const double val)
Add a double to an element of the Coulomb object ( c1 c2 | a1 a2 )
double FourIndexAPI(const int I1, const int I2, const int I3, const int I4, const int index1, const int index2, const int index3, const int index4) const
Get a two-body matrix element with at most 2 virtual indices, using the FourIndex API...
void set_exchange(const int Ic1, const int Ic2, const int Iv1, const int Iv2, const int c1, const int c2, const int v1, const int v2, const double val)
Set an element of the Exchange object ( c1 v1 | c2 v2 )
static int directProd(const int Irrep1, const int Irrep2)
Get the direct product of the irreps with numbers Irrep1 and Irrep2: a bitwise XOR for psi4&#39;s convent...
Definition: Irreps.h:123
void add_exchange(const int Ic1, const int Ic2, const int Iv1, const int Iv2, const int c1, const int c2, const int v1, const int v2, const double val)
Add a double to an element of the Exchange object ( c1 v1 | c2 v2 )
int getNDMRG(const int irrep) const
Get the number of active orbitals for an irrep.
double get_exchange(const int Ic1, const int Ic2, const int Iv1, const int Iv2, const int c1, const int c2, const int v1, const int v2) const
Get an element of the Exchange object ( c1 v1 | c2 v2 )
DMRGSCFintegrals(DMRGSCFindices *iHandler)
Constructor.
int getNOCC(const int irrep) const
Get the number of occupied orbitals for an irrep.
void clear()
Set the storage objects to zero.
void set_coulomb(const int Ic1, const int Ic2, const int Ia1, const int Ia2, const int c1, const int c2, const int a1, const int a2, const double val)
Set an element of the Coulomb object ( c1 c2 | a1 a2 )
int getNVIRT(const int irrep) const
Get the number of virtual orbitals for an irrep.
int getNirreps() const
Get the number of irreps.