1 /* Copyright (c) 2015  Gerald Knizia
2  *
3  * This file is part of the IboView program (see: http://www.iboview.org)
4  *
5  * IboView 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, version 3.
8  *
9  * IboView is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with bfint (LICENSE). If not, see http://www.gnu.org/licenses/
16  *
17  * Please see IboView documentation in README.txt for:
18  * -- A list of included external software and their licenses. The included
19  *    external software's copyright is not touched by this agreement.
20  * -- Notes on re-distribution and contributions to/further development of
21  *    the IboView software
22  */
23 
24 #include <stdexcept>
25 #include <iostream>
26 #include <string.h> // for memcpy
27 
28 #include <fstream> // for importing stuff (cp2k data)
29 #include <stdexcept>
30 
31 #include <boost/format.hpp>
32 #include <boost/algorithm/string.hpp>
33 // #include <boost/program_options.hpp>
34 // namespace po = boost::program_options;
35 
36 // #include "CtCommon.h"
37 #include "CtIo.h"
38 #include "CtAtomSet.h"
39 #include "CtBasisLibrary.h"
40 #include "CtBasisSet.h"
41 // #include "CtInt1e.h"
42 #include "CtMatrix.h"
43 // #include "AicDrv.h" // for making integrals
44 
45 #include "CtTiming.h" // for embdedded HF timer.
46 #include "CtConstants.h"
47 
48 #include "CxNumpyArray.h"
49 // #include "AicGridOps.h"
50 
51 using boost::format;
52 using boost::str;
53 
54 #define HAVE_GRID_KERNEL
55 
56 namespace ct {
57 
58 
59 #if 0
60 void FormIntIJA(double *pIntFai, FMatrixView OrbA, FMatrixView OrbI, FMatrixView Jcd, FRawBasis const *pOrbBasis, FRawBasis const *pFitBasis, ir::FIntegralKernel &IntKernel, FMemoryStack &Mem)
61 {
62    void
63       *pBaseOfMemory = Mem.Alloc(0);
64    size_t
65       nAo = pOrbBasis->nFn(),
66       nFit = pFitBasis->nFn(),
67       nOrbA = OrbA.nCols,
68       nOcc = OrbI.nCols;
69    if ( nAo != OrbI.nRows || nAo != OrbA.nRows )
70       throw std::runtime_error("MakeFittingIntegrals: Input orbitals not consistent with orbital basis set.");
71    double
72       *pDF_NFi;
73    Mem.Alloc(pDF_NFi, nAo * nFit * nOcc);
74 
75    for ( size_t iShF = 0; iShF != pFitBasis->Shells.size(); ++ iShF ){
76       ir::FRawShell const &ShF = pFitBasis->Shells[iShF];
77       size_t nFnF = ShF.nFn();
78 
79       double
80          *pMNF;
81       Mem.Alloc(pMNF, nAo * nAo * nFnF );
82 
83       for ( size_t iShB = 0; iShB < pOrbBasis->Shells.size(); ++ iShB ){
84          ir::FRawShell const &ShB = pOrbBasis->Shells[iShB];
85          size_t nFnB = ShB.nFn();
86          for ( size_t iShA = iShB; iShA < pOrbBasis->Shells.size(); ++ iShA ) {
87             ir::FRawShell const &ShA = pOrbBasis->Shells[iShA];
88             size_t
89                nFnA = ShA.nFn(),
90                Strides[3] = {1, nFnA, nFnA * nFnB};
91             double
92                *pIntData;
93             Mem.Alloc(pIntData, nFnA * nFnB * nFnF );
94 
95             ir::EvalInt2e3c(pIntData, Strides, &ShA, &ShB, &ShF,1, 1.0, &IntKernel, Mem);
96 
97             for ( size_t iF = 0; iF < nFnF; ++ iF )
98                for ( size_t iB = 0; iB < nFnB; ++ iB )
99                   for ( size_t iA = 0; iA < nFnA; ++ iA ) {
100                      double
101                         f = pIntData[iA + nFnA * (iB + nFnB * iF)];
102                      // assign to (\mu\nu| and (\nu\mu|. (int has perm symmetry).
103                      pMNF[(pOrbBasis->iFn(iShA) + iA) + nAo * (pOrbBasis->iFn(iShB) + iB) + nAo * nAo * iF] = f;
104                      pMNF[(pOrbBasis->iFn(iShB) + iB) + nAo * (pOrbBasis->iFn(iShA) + iA) + nAo * nAo * iF] = f;
105                   }
106 
107             Mem.Free(pIntData);
108          }
109       }
110 
111       // D[\nu A i] = (i\nu|A) = C(\mu,i) (\mu [\nu|A)]
112       FMatrixView
113          DF_NFI(pDF_NFi + nAo * pFitBasis->iFn(iShF), nAo * nFnF, nOcc, 1, nFit * nAo);
114       Mxm( DF_NFI,
115            FMatrixView(pMNF, nAo * nFnF, nAo, nAo, 1),
116            OrbI );
117 
118       Mem.Free(pMNF);
119    }
120 
121    // Jcd version:
122    //   Solve[Jcd[AB] D[\nu B i]] -> D[\nu A I]
123    for ( uint i = 0; i < nOcc; ++ i ){
124       FMatrixView
125          D_NF(pDF_NFi + nAo * nFit * i, nAo, nFit),
126          K_Fa(pIntFai + nFit * nOrbA * i, nFit, nOrbA);
127 //       TriangularSolve(Transpose(D_NF), Jcd);
128 //       Mxm(K_Fa, Transpose(D_NF), OrbA);
129 // ^- hm... I think I thought this was a good idea for some reason,
130 //          but I cannot remember why.
131       Mxm(K_Fa, Transpose(D_NF), OrbA);
132       TriangularSolve(K_Fa, Jcd);
133    }
134 
135    Mem.Free(pDF_NFi);
136    Mem.Free(pBaseOfMemory);
137 }
138 
139 
140 
141 
142 void MakeFittingIntegrals(double *pIntFai, FMatrixView OrbA, FMatrixView OrbI, FBasisSet const *pOrbBasis, FBasisSet const *pFitBasis, FMemoryStack &Mem)
143 {
144    uint
145       nAo = pOrbBasis->nFn(),
146       nFit = pFitBasis->nFn();
147 
148    xout << boost::format(" ORBITAL BASIS %s:%35t%5i FUNCTIONS") % pOrbBasis->Name % nAo << "\n"
149         << boost::format(" FITTING BASIS %s:%35t%5i FUNCTIONS") % pFitBasis->Name % nFit << "\n"
150         << std::endl;
151 
152    // Make fitting coefficients J^{-1/2}.
153    FTimer TimerJmh;
154    FStackMatrix
155       Jmh(nFit, nFit, &Mem);
156    MakeIntMatrix(Jmh, *pFitBasis, *pFitBasis, FKrn2i_Direct(&ir::g_IrCoulombKernel), Mem);
157    CalcCholeskyFactors(Jmh);
158    xout << format(pTimingFmt) % "J^{-1/2} of fitting basis" % (double)TimerJmh; xout.flush();
159 
160    // make (F|ai) the lame way: assume we can keep (ai|A) in memory.
161    FTimer TimerIJA;
162    FormIntIJA(pIntFai, OrbA, OrbI, Jmh,
163       &*pOrbBasis->pRawBasis, &*pFitBasis->pRawBasis, ir::g_IrCoulombKernel, Mem);
164    xout << format(pTimingFmt) % "3-index integrals" % (double)TimerIJA; xout.flush();
165 };
166 #endif // 0
167 
168 #ifdef HAVE_GRID_KERNEL
Vec3Eq(double const * pA,double const * pB)169 bool Vec3Eq(double const *pA, double const *pB) {
170    return pA[0] == pB[0] && pA[1] == pB[1] && pA[2] == pB[2];
171 }
172 
MakeGridValues(double * pOut,FMatrixView Grid,FMatrixView Orb,uint GridDxOrder,FBasisSet const * pBasis,FMemoryStack & Mem_)173 void MakeGridValues(double *pOut, FMatrixView Grid, FMatrixView Orb, uint GridDxOrder, FBasisSet const *pBasis, FMemoryStack &Mem_)
174 {
175    void
176       *pBaseOfMemory = Mem_.Alloc(0);
177    size_t
178       nGridPt_Total = Grid.nCols,
179       nComp = 1,
180       nGridStep = 128,
181       nAo = pBasis->nFn(),
182       nOrb = Orb.nCols;
183    if ( GridDxOrder == 1 ) nComp = 4;
184    if ( GridDxOrder == 2 ) nComp = 10;
185 //    std::cout << "nAo = " << nAo << "  Orb.nRows =" << Orb.nRows << std::endl;
186    if ( nAo != Orb.nRows )
187       throw std::runtime_error("MakeGridValues: Input orbitals not consistent with orbital basis set.");
188 
189    // convert basis format for low level driver input...
190    FRawBasis::FShellArray
191       &ShellsA = const_cast<FBasisSet*>(pBasis)->pRawBasis->Shells;
192 
193    assert(Grid.nRowSt == 1 && Grid.nRows == 3);
194 
195    {
196       FMemoryStackArray MemStacks(Mem_);
197 //       #pragma omp parallel for schedule(dynamic)
198 //       for ( size_t iGridPt = 0; iGridPt < nGridPt_Total; iGridPt += nGridStep ) {
199       int nGridPt_Batches = int((nGridPt_Total + nGridStep - 1)/nGridStep);
200       #pragma omp parallel for schedule(dynamic)
201       for ( int iGridPt_Batch = 0; iGridPt_Batch < nGridPt_Batches; ++ iGridPt_Batch ) {
202          size_t iGridPt = size_t(iGridPt_Batch) * nGridStep;
203          // ^- that's for OMP 2.0 which allows only 'int' variables for loop control.
204          FMemoryStack &Mem = MemStacks.GetStackOfThread();
205          size_t
206             nGridPt = nGridStep;
207          if ( iGridPt + nGridPt > nGridPt_Total )
208             nGridPt = nGridPt_Total - iGridPt;
209          if (nGridPt == 0)
210             continue;
211          void
212             *pMem1 = Mem.Alloc(0);
213          size_t
214             *pMap, nMap = 0, iFnBase = 0;
215          double
216             *pOrbValAo, *pOrbsCompressed;
217          Mem.Alloc(pOrbValAo, nGridPt * nComp * nAo);
218          Mem.Alloc(pOrbsCompressed, nAo * nOrb);
219 
220          ir::FRawShell
221             *pLastBf = &ShellsA[0];
222          Mem.Alloc(pMap, nAo);
223          for ( ir::FRawShell *pFirstBf = &ShellsA[0]; pFirstBf != &ShellsA[0] + ShellsA.size(); pFirstBf = pLastBf ) {
224             pLastBf = pFirstBf;
225             size_t nFn = 0;
226             while ( pLastBf != &ShellsA[0] + ShellsA.size() && Vec3Eq(pLastBf->vCen, pFirstBf->vCen) ) {
227                nFn += pLastBf->nFn();
228                ++pLastBf;
229             }
230             size_t
231                nBfStride = nGridPt*nComp;
232             EvalShellGroupOnGrid(&pOrbValAo[nBfStride*nMap], nGridPt, nBfStride, // comp stride, bf stride
233                pMap, nMap, iFnBase, &Grid(0,iGridPt), Grid.nColSt, nGridPt,
234                pFirstBf, pLastBf, &pFirstBf->vCen[0],
235                GridDxOrder, 1e-10, 40, Mem);
236             iFnBase += nFn;
237          }
238          assert(iFnBase == nAo && nMap <= nAo);
239          assert(pLastBf == &ShellsA[0] + ShellsA.size());
240 
241          FMatrixView
242             OrbCmpr(pOrbsCompressed, nMap, nOrb),
243             OrbValAo(pOrbValAo, nGridPt*nComp, nMap);
244          // compress input orbital matrix: delete basis functions not occurring in nMap.
245          for ( size_t iOrb = 0; iOrb != nOrb; ++ iOrb )
246             for ( size_t iMap_ = 0; iMap_ != nMap; ++ iMap_ )
247                OrbCmpr(iMap_, iOrb) = Orb(pMap[iMap_], iOrb);
248          for (uint iComp = 0; iComp < nComp; ++ iComp) {
249             FMatrixView
250                Out(&pOut[iGridPt+nGridPt_Total*iComp], nGridPt, nOrb, 1, nGridPt_Total*nComp),
251                OrbValAoComp = OrbValAo;
252             if (nMap == 0) {
253                Out.Clear();
254             } else {
255                OrbValAoComp.nRows = nGridPt;
256                OrbValAoComp.pData += nGridPt * iComp;
257                Mxm(Out, OrbValAoComp, OrbCmpr);
258             }
259          }
260          Mem.Free(pMem1);
261       }
262    }
263 
264    Mem_.Free(pBaseOfMemory);
265 }
266 #endif
267 
268 
269 #if 0
270 void WriteMatrixToFile_Rect(std::string const &FileName, std::string const Desc, double *pData, uint nRows, uint nCols, char const *pNumFmt)
271 {
272     if ( pNumFmt == 0 )
273         pNumFmt = "%21.14f";
274     std::ofstream
275         File(FileName.c_str());
276     File << format("! %s, %i x %i\n") % Desc % nRows % nCols;
277     if ( !File.good() )
278         throw std::runtime_error("failed to open file '" + FileName + "' for writing.");
279     for ( uint iRow = 0; iRow < nRows; ++ iRow ) {
280         for ( uint iCol = 0; iCol < nCols; ++ iCol ) {
281             File << " " << format(pNumFmt) % pData[iRow + nRows*iCol];
282         };
283         File << "\n";
284     }
285 }
286 
287 
288 void WriteMatrixtoFile2(std::string const &FileName, ct::FMatrixView const &M, std::string const &MatrixName, std::string const &MatrixFormat, int Verbosity = 0)
289 {
290    if ( MatrixFormat == "npy" ) {
291       if ( M.nRowSt != 1 || M.nColSt != M.nRows )
292          throw std::runtime_error("Sorry, WriteNpy can currently only export continuous matrices in default layout.");
293       ct::WriteNpy(FileName, M.pData, ct::MakeShape(M.nRows, M.nCols));
294    }
295    else if ( MatrixFormat == "list" )
296       WriteMatrixToFile(FileName, M, MatrixName);
297    else if ( MatrixFormat == "rect" )
298       WriteMatrixToFile_Rect(FileName, MatrixName, M.pData, M.nRows, M.nCols, " %22.15e");
299    else
300       throw std::runtime_error("output matrix format not recognized: '" + MatrixFormat + "'");
301    if ( Verbosity >= 1 )
302       ct::xout << format(" * wrote %s matrix to '%s'.") % MatrixName % FileName << std::endl;
303 };
304 
305 // Set Out := L^T In R.
306 void BasisChange2( FMatrixView &Out, FMatrixView const &L,
307     FMatrixView const &In, FMatrixView const &R, FMemoryStack &Mem)
308 {
309     assert( L.nRows == In.nRows && R.nRows == In.nCols );
310 //     Out = FMatrixView( 0, L.nCols, R.nCols );
311 //     Mem.Alloc(Out.pData, Out.GetStridedSize());
312     assert( Out.nRows == L.nCols && Out.nCols == R.nCols );
313 
314     if ( L.nRows * L.nCols <=  R.nRows * R.nCols ) {
315         // T1 := L^T In
316         FStackMatrix
317             T1(L.nCols, In.nCols, &Mem);
318         Mxm(T1, Transpose(L), In);
319         Mxm(Out, T1, R);
320     } else {
321         // T1 := In * R
322         FStackMatrix
323             T1(In.nRows, R.nCols, &Mem);
324         Mxm(T1, In, R);
325         Mxm(Out, Transpose(L), T1);
326     }
327 
328     if ( L.pData == R.pData && In.IsSymmetric(1e-10) )
329        Symmetrize(Out);
330 }
331 #endif // 0
332 
333 } // namespace ct
334 
335 #if 0
336 int main_integral_export(int argc, char *argv[])
337 {
338    using namespace ct;
339    std::cout << " :::: IR/WMME [v20150427] -- Write Molecular Matrix Elements (Gerald Knizia, 2013) ::::"
340              << std::endl;
341 
342 
343    po::options_description options_desc("Options");
344    std::vector< std::string >
345       FileNames_BasisLibs;
346    std::string
347       BasisName_Orbital,
348       BasisName_Orbital2,
349       BasisName_Fit,
350       FileName_Overlap,
351       FileName_Overlap12,
352       FileName_Overlap2,
353       FileName_CoreH,
354       FileName_VNucN,
355       FileName_RSqN,
356       FileName_Dipole,
357       FileName_2eFit,
358       FileName_Atoms,
359       FileName_AtomsAu,
360       FileName_AtomsAng,
361       FileName_InputOrbitals,
362       FileName_GridCoords,
363       FileName_GridValues,
364       FileName_GridOps,
365       Name_OrbitalTrafo,
366       MatrixFormat;
367    int
368       GridDxOrder = 0;
369    options_desc.add_options()
370       ("help",
371           "print this help message")
372       ("basis-lib", po::value<std::vector<std::string> >(&FileNames_BasisLibs)->composing(),
373           "file names of .libmol basis set libraries to load (Molpro basis lib format). Can occur multiple times. "
374           "all basis sets used in the input must be defined in one of those files.")
375       ("save-overlap", po::value<std::string>(&FileName_Overlap)->default_value(""),
376           "if given, save the AO overlap matrix S <mu|nu> to this file.")
377       ("save-coreh", po::value<std::string>(&FileName_CoreH)->default_value(""),
378           "if given, save the core Hamiltonian (1e terms) to this file.")
379 //       ("save-vnucN", po::value<std::string>(&FileName_VNucN)->default_value(""),
380 //           "if given, save the individual nuclear repulsion matrices (nOrb x nOrb x nAtoms) to this file.")
381 //       ("save-rsqN", po::value<std::string>(&FileName_RSqN)->default_value(""),
382 //           "if given, save the individual <mu|(r-rA)^2|nu> matrices, for rA going over the nuclei (nOrb x nOrb x nAtoms) to this file.")
383 //       ("save-dipole", po::value<std::string>(&FileName_Dipole)->default_value(""),
384 //           "if given, save the dipole operator matrices (nOrb x nOrb x 3) to this file.")
385       ("save-fint2e", po::value<std::string>(&FileName_2eFit)->default_value(""),
386           "if given, save the full (F|mu nu) 2e fitting integral matrix to this file. "
387           "Fitting metric is already absorbed in F; i.e., the 2e integral "
388           "(mu nu|eta xi) \\approx \\sum_F (mu nu|F)(F|eta xi).")
389       ("basis-orb", po::value<std::string>(&BasisName_Orbital)->default_value("MINAO"),
390           "name of the orbital basis to use for r,s. (e.g., cc-pVTZ, def2-SVP)")
391       ("basis-fit", po::value<std::string>(&BasisName_Fit)->default_value("univ-JKFIT"),
392           "name of the fitting basis to use for F (e.g., def2-TZVPP-JKFIT, def2-TZVPP-MP2FI)")
393       ("orb-trafo", po::value<std::string>(&Name_OrbitalTrafo)->default_value("None"),
394           "can be 'None' or 'Smh'. In the latter case, all output integrals (except "
395           "for overlap matrices) are stored in terms of symmetrically orthogonalized "
396           "basis functions for mu,nu instead of raw basis functions.")
397       ("matrix-format", po::value<std::string>(&MatrixFormat)->default_value("list"),
398           "Can be 'npy', 'list' or 'rect'. Defines file format of output matrices. "
399           "npy is a binary format that can be loaded by numpy.load(..).")
400       ("atoms-ang", po::value<std::string>(&FileName_AtomsAng)->default_value(""),
401           "File name of input atoms (xyz format). Input coordinates are given in "
402           "angstroms (1.0 == 10^-{10} meter). Normally .xyz files use these units.")
403       ("atoms-au", po::value<std::string>(&FileName_AtomsAu)->default_value(""),
404           "File name of input atoms (xyz format). Input coordinates are given in "
405           "atomic units (1.0 == 1.0 bohr radius)")
406       ("basis-orb-2", po::value<std::string>(&BasisName_Orbital2)->default_value(""),
407           "name of a second orbital basis; if given, --save-overlap-2 and --save-overlap-12 "
408           "can be used to calculate the overlap between two orbital basis sets.")
409       ("save-overlap-2", po::value<std::string>(&FileName_Overlap2)->default_value(""),
410           "if given, save the overlap matrix in the second orbital basis to this file")
411       ("save-overlap-12", po::value<std::string>(&FileName_Overlap12)->default_value(""),
412           "if given, save the overlap matrix between the first and the second basis to this file")
413 #ifdef HAVE_GRID_KERNEL
414       ("eval-orbitals", po::value<std::string>(&FileName_InputOrbitals)->default_value(""),
415           "if given, evaluate given orbitals (nAo x nOrb matrix) on grid. nAo must be comaptible with basis-orb. If given, --grid-coords must also be given.")
416       ("save-grid-ops", po::value<std::string>(&FileName_GridOps)->default_value(""),
417           "if given, evaluate position operators (nAo x nAo x nGrid matrix) on grid. If given, --grid-coords must also be given.")
418       ("grid-coords", po::value<std::string>(&FileName_GridCoords)->default_value(""),
419           "read grid coordinates(3 x nGrid matrix) from this file.")
420       ("save-grid-values", po::value<std::string>(&FileName_GridValues)->default_value(""),
421           "save values of orbitals on grid to this file.")
422       ("eval-orbitals-dx", po::value<int>(&GridDxOrder)->default_value(0),
423           "order of derivatives to evaluate for orbitals on grid: 0: values only ... 2: up to 2nd derivatives.")
424 #endif
425    ;
426 //       Args =    [("--eval-orbitals-dx=%s" % DerivativeOrder)]
427 //       Inputs =  [("--grid-coords", "GRID", Grid)]
428 //       Outputs = [("--grid-values", "ORBS_ON_GRID")]
429 
430    po::variables_map vm;
431    po::store(po::parse_command_line(argc, argv, options_desc), vm);
432    po::notify(vm);
433 
434    if (vm.count("help") || argc == 1) {
435       xout << "\nThis program calculates molecular matrix elements (``integrals'') over Gaussian"
436               "\nbasis functions and writes them to disk. See README for an explanation of the"
437               "\nterms, the intended usage, and more information on input and output formats."
438               "\n"
439            << std::endl;
440       xout << options_desc << std::endl;
441       return 1;
442    }
443 
444    // find file name of input atomic coordinates.
445    double
446       fInputAtomScale = 1.0;
447    if ( FileName_AtomsAu != "" ) {
448       FileName_Atoms = FileName_AtomsAu;
449       fInputAtomScale = ToAng;
450    } else if ( FileName_AtomsAng != "" ) {
451       FileName_Atoms = FileName_AtomsAng;
452       fInputAtomScale = 1.0;
453    } else {
454       throw std::runtime_error("either --atoms-au or --atoms-ang arguments must be given.");
455    }
456 
457    // check combinations of other input options.
458    if ( FileNames_BasisLibs.empty() )
459       throw std::runtime_error("you must specify at least one basis set library file to load (--basis-lib missing)");
460    if ( BasisName_Fit.empty() && !FileName_2eFit.empty() )
461       throw std::runtime_error("if calculating 2e integrals, you must specify a fitting basis (--basis-fit)");
462    bool
463       TransformToSmh = false;
464    if ( Name_OrbitalTrafo != "None" and Name_OrbitalTrafo != "Smh" )
465       throw std::runtime_error("orbital transformation not recognized (--orb-trafo)");
466    if ( Name_OrbitalTrafo == "Smh" )
467       TransformToSmh = true;
468    if ( MatrixFormat != "npy" && MatrixFormat != "list" && MatrixFormat != "rect" )
469       throw std::runtime_error("Output matrix format must be 'npy', 'list' or 'rect' (--matrix-format)");
470    if ( FileName_InputOrbitals == "" ) {
471       if ( FileName_GridCoords != "" || FileName_GridValues != "" || GridDxOrder != 0 )
472          throw std::runtime_error("no input orbitals provided. Grid options are to be used in conjunction with --eval-orbitals.");
473    } else {
474       if ( FileName_GridCoords == "" || FileName_GridValues == "" )
475          throw std::runtime_error("when --eval-orbitals is given, --grid-coords and --save-grid-values must also be given.");
476       if ( GridDxOrder != 0 && GridDxOrder != 1 && GridDxOrder != 2 )
477          throw std::runtime_error("grid derivatives can only be evaluated up to 2nd order.");
478    }
479    if ( BasisName_Orbital2 == "" && (FileName_Overlap2 != "" || FileName_Overlap12 != "") )
480       throw std::runtime_error("overlaps <2|2> or <1|2> between two orbital basis sets were requested, but no second orbital basis was specified. Use --basis-orb-2 to set one.");
481 
482 //    xout << fmt::ind();
483 
484    MajorProgramIntro(xout, "BASIS LIBRARY");
485    for ( uint iBasisLib = 0; iBasisLib < FileNames_BasisLibs.size(); ++ iBasisLib )
486       g_BasisSetLibrary.ImportMolproLib(FileNames_BasisLibs[iBasisLib], &xout);
487 
488    boost::intrusive_ptr<FAtomSet>
489       pAtoms = new FAtomSet;
490    FBasisDescs
491       DefaultBases;
492    DefaultBases[BASIS_Orbital] = BasisName_Orbital;
493    DefaultBases[BASIS_JkFit] = BasisName_Fit;
494    if ( BasisName_Orbital2 != "" )
495       DefaultBases[BASIS_Guess] = BasisName_Orbital2;
496       // ^- slight abuse of the orbital context
497 
498    pAtoms->AddAtomsFromXyzFile(FileName_Atoms, DefaultBases);
499    if ( fInputAtomScale != 1.0 ) {
500       for ( uint iAtom = 0; iAtom < pAtoms->size(); ++ iAtom )
501          (*pAtoms)[iAtom].vPos *= fInputAtomScale;
502    }
503 
504    MajorProgramIntro(xout, "GEOMETRY & ENVIRONMENT");
505    xout << " ATOMIC CONFIGURATION\n\n";
506    xout << *pAtoms;
507 
508    MajorProgramIntro(xout, "EXPORT OF INTEGRALS");
509 
510    // instanciate the basis sets.
511    FBasisSetPtr
512       pOrbBasis = new FBasisSet(*pAtoms, BASIS_Orbital),
513       pFitBasis;
514    if ( FileName_2eFit != "" )
515       pFitBasis = new FBasisSet(*pAtoms, BASIS_JkFit);
516    uint
517       nAo = pOrbBasis->nFn(),
518       nFit = 0;
519    if ( pFitBasis )
520       nFit = pFitBasis->nFn();
521 
522    std::size_t
523       RequiredMem = 4*nAo*nAo + (1500<<20);
524    if ( FileName_2eFit != "" )
525       RequiredMem += (2*nFit*nAo*nAo) + nFit*nFit;
526    FMemoryStack2
527       Mem(RequiredMem);
528 
529    // we'll just do the transformation always, and push a indentity matrix
530    // if raw output integrals are requested. Can simply use MakeFittingIntegrals
531    // then.
532    FStackMatrix
533       S(nAo, nAo, &Mem),
534       Orbs(nAo, nAo, &Mem);
535    pAtoms->MakeOverlapMatrix(S, *pOrbBasis, *pOrbBasis, Mem);
536 //    MakeIntMatrix(S, *pOrbBasis, *pOrbBasis, ir::g_IrOverlapKernel, Mem);
537 //    S.Print(xout, "OVERLAP");
538    if ( !TransformToSmh ) {
539       Orbs.SetIdentity();
540    } else {
541       Move(Orbs, S);
542       CalcSmhMatrix(Orbs, Mem, FSmhOptions(1e-15,1e-15,(" S^{-1/2} for orbital basis ("+BasisName_Orbital+")").c_str(), &ct::xout));
543 //       S.SetIdentity();
544    }
545    if ( FileName_Overlap != "" )
546       WriteMatrixtoFile2(FileName_Overlap, S, "Overlap", MatrixFormat, 1);
547 
548    if ( FileName_CoreH != "" ) {
549       FStackMatrix
550          CoreH_Ao(nAo, nAo, &Mem),
551          CoreH_Mo(nAo, nAo, &Mem);
552       pAtoms->MakeCoreHamiltonMatrix(CoreH_Ao, *pOrbBasis, *pOrbBasis, Mem);
553       BasisChange2(CoreH_Mo, Orbs, CoreH_Ao, Orbs, Mem);
554       WriteMatrixtoFile2(FileName_CoreH, CoreH_Mo, "CoreH", MatrixFormat, 1);
555    }
556 
557 //    if ( FileName_VNucN != "" ) {
558 //       FStackMatrix
559 //          VNucI_Ao(nAo, nAo, &Mem),
560 //          VNucN_Mo(nAo*nAo, pAtoms->size(), &Mem);
561 //       for (uint iAtom = 0; iAtom < pAtoms->size(); ++ iAtom) {
562 //          FMatrixView
563 //             VNucI_Mo(VNucN_Mo.pData + nAo*nAo * iAtom, nAo, nAo);
564 //          pAtoms->MakeVNucMatrixI(VNucI_Ao, *pOrbBasis, *pOrbBasis, iAtom, Mem);
565 //          BasisChange2(VNucI_Mo, Orbs, VNucI_Ao, Orbs, Mem);
566 //       }
567 //       WriteMatrixtoFile2(FileName_VNucN, VNucN_Mo, "VNucN [nOrb x nOrb x nAtoms]", MatrixFormat, 1);
568 //    }
569 //
570 //    if ( FileName_RSqN != "" ) {
571 //       FStackMatrix
572 //          VRsqI_Ao(nAo, nAo, &Mem),
573 //          VRsqN_Mo(nAo*nAo, pAtoms->size(), &Mem);
574 //       for (uint iAtom = 0; iAtom < pAtoms->size(); ++ iAtom) {
575 //          FMatrixView
576 //             VRsqI_Mo(VRsqN_Mo.pData + nAo*nAo * iAtom, nAo, nAo);
577 //          pAtoms->MakeRsqMatrix(VRsqI_Ao, *pOrbBasis, *pOrbBasis, (*pAtoms)[iAtom].vPos, Mem);
578 //          BasisChange2(VRsqI_Mo, Orbs, VRsqI_Ao, Orbs, Mem);
579 //       }
580 //       WriteMatrixtoFile2(FileName_RSqN, VRsqN_Mo, "(r-rA)^2 [nOrb x nOrb x nAtoms]", MatrixFormat, 1);
581 //    }
582 //
583 //
584 //    if ( FileName_Dipole != "" ) {
585 //       FStackMatrix
586 //          DipI_Ao(nAo, nAo, &Mem),
587 //          DipN_Mo(nAo*nAo, 3, &Mem);
588 //       for (uint ixyz = 0; ixyz < 3; ++ ixyz) {
589 //          FVector3
590 //             vDir(0,0,0),
591 //             vExpPoint(0,0,0);
592 //          vDir[ixyz] = 1.;
593 //          FMatrixView
594 //             DipI_Mo(DipN_Mo.pData + nAo*nAo * ixyz, nAo, nAo);
595 //          DipI_Ao.Clear();
596 //          pAtoms->AddDipoleMatrix(DipI_Ao, *pOrbBasis, *pOrbBasis, vDir, vExpPoint, Mem);
597 //          BasisChange2(DipI_Mo, Orbs, DipI_Ao, Orbs, Mem);
598 //       }
599 //       WriteMatrixtoFile2(FileName_Dipole, DipN_Mo, "Dipole operator [nOrb x nOrb x 3]", MatrixFormat, 1);
600 //    }
601 
602    if ( FileName_2eFit != "" ) {
603       FStackMatrix
604          IntFai(nFit, nAo*nAo, &Mem);
605       MakeFittingIntegrals(IntFai.pData, Orbs, Orbs, &*pOrbBasis, &*pFitBasis, Mem);
606       WriteMatrixtoFile2(FileName_2eFit, IntFai, "2e-(F|rs)", MatrixFormat, 1);
607    }
608 
609    if ( BasisName_Orbital2 != "" && (FileName_Overlap2 != "" || FileName_Overlap12 != "") ) {
610       // make overlap/stuff matrices for overlap with second basis.
611       FBasisSetPtr
612          pOrbBasis2 = new FBasisSet(*pAtoms, BASIS_Guess);
613       uint
614          nAo2 = pOrbBasis2->nFn();
615       if ( FileName_Overlap2 != "" ) {
616          FStackMatrix
617             S2(nAo2, nAo2, &Mem);
618          pAtoms->MakeOverlapMatrix(S2, *pOrbBasis2, *pOrbBasis2, Mem);
619          WriteMatrixtoFile2(FileName_Overlap2, S2, "Overlap/<basis2|basis2>", MatrixFormat, 1);
620       }
621       if ( FileName_Overlap12 != "" ) {
622          FStackMatrix
623             S12(nAo, nAo2, &Mem);
624          pAtoms->MakeOverlapMatrix(S12, *pOrbBasis, *pOrbBasis2, Mem);
625          WriteMatrixtoFile2(FileName_Overlap12, S12, "Overlap/<basis1|basis2>", MatrixFormat, 1);
626       }
627    }
628 
629 #ifdef HAVE_GRID_KERNEL
630    if ( FileName_InputOrbitals != "" ) {
631       FArrayNpy
632          Grid, Orbs;
633       ReadNpy(Grid, FileName_GridCoords);
634       ReadNpy(Orbs, FileName_InputOrbitals);
635       if ( Grid.Rank() != 2 || Grid.Shape[0] != 3 )
636          throw std::runtime_error("input grid array must have rank 2 and shape (3,*).");
637       if ( Orbs.Rank() != 2 || Orbs.Shape[0] != nAo )
638          throw std::runtime_error("input orbital value array must have rank 2 and shape (nAo,*), where nAo is the number of functions in --basis-orb.");
639 
640       uint const
641          nDerivativeComps_[] = {1,4,10},
642          nGrid = Grid.Shape[1],
643          nComps = nDerivativeComps_[GridDxOrder];
644       TArray<double>
645          GridValues(nAo * nComps * nGrid);
646       FMatrixView
647          Orb_(&Orbs.Data[0], Orbs.Shape[0], Orbs.Shape[1], Orbs.Strides[0], Orbs.Strides[1]),
648          Grid1_(&Grid.Data[0], Grid.Shape[0], Grid.Shape[1], Grid.Strides[0], Grid.Strides[1]);
649       FStackMatrix
650          Grid_(Grid.Shape[0], Grid.Shape[1], &Mem);
651       Move(Grid_, Grid1_); // <- copy over the array to get rid of funky strides.
652       GridValues.clear_data();
653       MakeGridValues(&GridValues[0], Grid_, Orb_, GridDxOrder, pOrbBasis.get(), Mem);
654       FMatrixView
655          GridValues_(&GridValues[0], nComps*Grid.Shape[1], Orbs.Shape[1]);
656       WriteMatrixtoFile2(FileName_GridValues, GridValues_, "Orbitals on grid: (nGridPt x nDerivComp) x nOrb", MatrixFormat, 1);
657    }
658 #endif
659    xout << "\n";
660    xout << format(pResultFmt) % "Nuclear repulsion energy" % pAtoms->NuclearRepulsionEnergy();
661 
662    xout.flush();
663    return 0;
664 };
665 
666 
667 int main(int argc, char *argv[])
668 {
669 //    return mainWheHeehee();
670    return main_integral_export(argc, argv);
671 };
672 
673 #endif
674