Which libint version is being used?

I am trying to modify the integrals used in by Psi4, which requires modifying libint. I have git cloned the official repository and checked out to versions 2.9.0 and 2.8.2. I ran autogen.sh, ran configure in a new directory, used make export to generate the source tarball as described in the wiki, then included this in Psi4’s ExternalProject_Add for libint2 by changing the URL. Psi4 builds fine. However, when I run it, I get 'NoneType' object is not subscriptable. Upon investigating, I find that this is because psi4.core._libint2_configuration() returns "(nyi)". So, clearly, something is not yet implemented in this libint2 version.

Where can I get libint2 source that is compatible with Psi4?

Second, how are people configuring the development environment? I’ve switched the URL in the CMakeLists.txt file for libint2 within Psi4 to a SOURCE_DIR variable, which points to the extracted archive output. However, from what I can tell, I still need a full rebuild of both libint (i.e, even rebuilding the compiler) and Psi4 every time I change something.

So I found out that libint actually expects you to cut-and-paste this string from the output of a Python script into the C++ file. Its documented here rather than in the wiki like everything else. I copied the config options from here (THANK YOU for including them), added them to the config script, and generated the corresponding string, and modified the CMakeLists.txt file.

Now it just segfaults on libint v2.9.0. Still puzzled. GDB output is

run ~/work/tmp/test.py
Starting program: /home/ubuntu/miniconda3/envs/p4env/bin/python ~/work/tmp/test.py
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Detaching after fork from child process 705]

  Memory set to 476.837 MiB by Python driver.

Scratch directory: /tmp/
LIBINT 2 CONFIG IS... ss;multipole_nn_d0;multipole_mm_d0;multipole_ll_d0;multipole_kk_d0;multipole_ii_d0;multipole_hh_d0;multipole_gg_d0;multipole_ff_d0;multipole_dd_d0;eri_hhhh_d0;eri_gggg_d0;eri_ffff_d0;eri_dddd_d0;eri_gggg_d1;eri_ffff_d1;eri_dddd_d1;eri_ffff_d2;eri_dddd_d2;eri_iiI_d0;eri_hhI_d0;eri_hhH_d0;eri_ggI_d0;eri_ggH_d0;eri_ggG_d0;eri_ffI_d0;eri_ffH_d0;eri_ffG_d0;eri_ffF_d0;eri_ddI_d0;eri_ddH_d0;eri_ddG_d0;eri_ddF_d0;eri_ddD_d0;eri_hhH_d1;eri_ggH_d1;eri_ggG_d1;eri_ffH_d1;eri_ffG_d1;eri_ffF_d1;eri_ddH_d1;eri_ddG_d1;eri_ddF_d1;eri_ddD_d1;eri_ggG_d2;eri_ffG_d2;eri_ffF_d2;eri_ddG_d2;eri_ddF_d2;eri_ddD_d2;eri_iii_d0;eri_hhi_d0;eri_hhh_d0;eri_ggi_d0;eri_ggh_d0;eri_ggg_d0;eri_ffi_d0;eri_ffh_d0;eri_ffg_d0;eri_fff_d0;eri_ddi_d0;eri_ddh_d0;eri_ddg_d0;eri_ddf_d0;eri_ddd_d0;eri_hhh_d1;eri_ggh_d1;eri_ggg_d1;eri_ffh_d1;eri_ffg_d1;eri_fff_d1;eri_ddh_d1;eri_ddg_d1;eri_ddf_d1;eri_ddd_d1;eri_ggg_d2;eri_ffg_d2;eri_fff_d2;eri_ddg_d2;eri_ddf_d2;eri_ddd_d2;eri_II_d0;eri_HH_d0;eri_GG_d0;eri_FF_d0;eri_DD_d0;eri_HH_d1;eri_GG_d1;eri_FF_d1;eri_DD_d1;eri_GG_d2;eri_FF_d2;eri_DD_d2;eri_ii_d0;eri_hh_d0;eri_gg_d0;eri_ff_d0;eri_dd_d0;eri_hh_d1;eri_gg_d1;eri_ff_d1;eri_dd_d1;eri_gg_d2;eri_ff_d2;eri_dd_d2;g12_gggg_d0;g12_ffff_d0;g12_dddd_d0   => Libint2 <=

    Primary   basis highest AM E, G, H:  5, 4, 3
    Auxiliary basis highest AM E, G, H:  6, 5, 4
    Onebody   basis highest AM E, G, H:  -, -, -
    Solid Harmonics ordering:            Gaussian

*** tstart() called on 4e8d9e4fe790
*** at Thu Jul 11 00:05:08 2024

   => Loading Basis Set <=

    Name: CC-PVDZ
    Role: ORBITAL
    Keyword: BASIS
    atoms 1   entry O          line   198 file /home/ubuntu/build/stage/share/psi4/basis/cc-pvdz.gbs 
    atoms 2-3 entry H          line    22 file /home/ubuntu/build/stage/share/psi4/basis/cc-pvdz.gbs 

[Detaching after fork from child process 706]
[New Thread 0x7f50b6f1b700 (LWP 707)]
[New Thread 0x7f50b671a700 (LWP 708)]
[Thread 0x7f50b671a700 (LWP 708) exited]
[Thread 0x7f50b6f1b700 (LWP 707) exited]
   => B97-D: Empirical Dispersion <=

    Grimme's -D2 Dispersion Correction
    Grimme, S. (2006), J. Comp. Chem., 27: 1787-1799

        s6 =       1.250000
    alpha6 =      20.000000
       sr6 =       1.100000

               by Justin Turney, Rob Parrish, Andy Simmonett
                          and Daniel G. A. Smith
                              RKS Reference
                        1 Threads,    476 MiB Core

  ==> Geometry <==

    Molecular point group: c2v
    Full point group: C2v

    Geometry (in Angstrom), charge = 0, multiplicity = 1:

       Center              X                  Y                   Z               Mass       
    ------------   -----------------  -----------------  -----------------  -----------------
         O            0.000000000000     0.000000000000    -0.065775570547    15.994914619570
         H            0.000000000000    -0.759061990794     0.521953018286     1.007825032230
         H            0.000000000000     0.759061990794     0.521953018286     1.007825032230

  Running in c2v symmetry.

  Rotational constants: A =     27.26297  B =     14.51533  C =      9.47217 [cm^-1]
  Rotational constants: A = 817323.21126  B = 435158.60141  C = 283968.37536 [MHz]
  Nuclear repulsion =    9.168193296424349

  Charge       = 0
  Multiplicity = 1
  Electrons    = 10
  Nalpha       = 5
  Nbeta        = 5

  ==> Algorithm <==

  SCF Algorithm Type is DF.
  DIIS enabled.
  MOM disabled.
  Fractional occupation disabled.
  Guess Type is SAD.
  Energy threshold   = 1.00e-06
  Density threshold  = 1.00e-06
  Integral threshold = 1.00e-12

  ==> Primary Basis <==

  Basis Set: CC-PVDZ
    Blend: CC-PVDZ
    Number of shells: 12
    Number of basis functions: 24
    Number of Cartesian functions: 25
    Spherical Harmonics?: true
    Max angular momentum: 2

  => MolecularGrid: Standard Orientation for default <= 
  Total Charge: 10
  Symmetry: Asymmetric top
  Center of charge: (0.000000, 0.000000, 0.097831)
  Principal moments of charge: 0.000000, 1.973656, 4.115117
      /  1.000000   0.000000  -0.000000 \
  Q = |  0.000000   0.000000   1.000000 |
      \  0.000000   1.000000   0.000000 /

  ==> DFT Potential <==

   => LibXC <=

    Version 6.2.2
    S. Lehtola, C. Steigemann, M. J.T. Oliveira, and M. A.L. Marques.,  SoftwareX 7, 1–5 (2018) (10.1016/j.softx.2017.11.002)

   => Composite Functional: B97-D <= 

    B97-D GGA Exchange-Correlation Functional

    S. Grimme.,  J. Comput. Chem. 27, 1787 (2006) (10.1002/jcc.20495)

    Deriv               =              1
    GGA                 =           TRUE
    Meta                =          FALSE

    Exchange Hybrid     =          FALSE
    MP2 Hybrid          =          FALSE

   => Exchange-Correlation Functionals <=

    1.0000       Becke 97-D

   => LibXC Density Thresholds  <==

    XC_GGA_XC_B97_D:  1.00E-14 

   => Molecular Quadrature <=

    Radial Scheme          =       TREUTLER
    Pruning Scheme         =           NONE
    Nuclear Scheme         =       TREUTLER

    Blocking Scheme        =         OCTREE
    BS radius alpha        =              1
    Pruning alpha          =              1
    Radial Points          =             75
    Spherical Points       =            302
    Total Points           =          66186
    Total Blocks           =            554
    Max Points             =            256
    Max Functions          =             24
    Weights Tolerance      =       1.00E-15

   => Loading Basis Set <=

    Name: (CC-PVDZ AUX)
    Role: JKFIT
    Keyword: DF_BASIS_SCF
    atoms 1   entry O          line   221 file /home/ubuntu/build/stage/share/psi4/basis/cc-pvdz-jkfit.gbs 
    atoms 2-3 entry H          line    51 file /home/ubuntu/build/stage/share/psi4/basis/cc-pvdz-jkfit.gbs 

  ==> Integral Setup <==

Thread 1 "python" received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) trace
Tracepoint 1 at 0x0

Which DFT you use obviously doesn’t matter, I just ran an old test file with B97-D. It also has a print statement for the libint options.

Hi, I’m one of the libint developers. If I understand you correctly, you are trying to change integrals exported by Libint to trick Psi4 into manipulating integrals that aren’t the integrals it thinks it’s using. That is a very bad idea, and whatever it is you’re trying to accomplish, there is probably a better way.

Correct me if I’ve misunderstood you. What exactly are you trying to accomplish?

No, sorry; I haven’t made any modifications yet. I’m just trying to get it to build from the source on GitHub.

Psi4 can currently build from a tarball of Libint. From what I can tell, this is exported from the first stage of building (./autogen.sh, configure, make export). I want to make some small modifications to the integrals by adding another class to do so (hence, avoiding the problematic trickery, which would probably be a bad idea as you suggest). Specifically, I would be adding a tweaked version of the error function for ERI integrals. However, I can’t seem to get Psi4 to run when I generate that tarball myself due to the segfault.

Thank you

Right, there are two issues here.

  1. Getting Psi4 to work with a non-conda L2
  2. Actually doing the modifications

I’m more interested in getting (2) straightened out first before worrying about (1). L2 is a difficult enough code to work with that I want to know what exactly you have in mind. The idea of homebrewed modifications has me very nervous.

Congratulations on making it this far – you’ve done a lot of investigating to find those configs and sources. Sorry about the python script to generate the patch – that’s a hack so a libtool-based build can provide the info that a cmake-based build could introspect. A few notes/observations:

  • do use the recent L2 releases v2.8.1, v2.8.2, or v2.9.0 as they have Jonathon’s fix for an erf problem. looks like you’re doing that already
  • more configurations and build models at libint-feedstock/recipe/NOTES at main · conda-forge/libint-feedstock · GitHub and surrounding files
  • to use an external build of L2 in psi4, build L2 from tarball, install it, and note the directory where the libint2-config.cmake file is placed ( $PREFIX/lib/cmake/libint2). When configuring psi4, pass that in as cmake -S. -Bobjdir -D Libint2_DIR=$PREFIX/lib/cmake/libint2 so that cmake reports “Found Libint2” rather than “Building Libint2”
  • the whole patching of generated src from the results of the python script is helpful for end users to provide a clue as to the contents of the L2 library, but it’s not strictly necessary in any compiling/linking sense. At one point I checked that psi can run on an unpatched build. But also feel free to comment out psi4 cmake lines to get it to accept the L2 you want.
  • what can definitely cause psi4 to fail to compile is handing it an L2 that doesn’t have all the classes of ints psi4 demands. that’s why libint-feedstock/recipe/NOTES at main · conda-forge/libint-feedstock · GitHub is a good place to start. you can turn down AM, but you mustn’t turn off ints classes or deriv levels.
  • what can definitely cause psi4 to segfault is hot-swapping L2 built with any different configuration (incl. different AM). hot-swapping meaning linking psi4 against a L2 lib, then using a different config L2 lib (even at the same disk location) at runtime. You must re-link psi4 to use a different L2.

Hope these are a start. From Jonathon’s list, he’s just the person for (2), and I’m the right consult for (1).

Thanks for all the tips! You’re right now that I look at it; The segfault is being caused by a bad config string. I’m basing my build config options off the ones in compiler.config in libint-2.8.1-5-4-3-6-5-4_mm10f12ob2_0.tgz (from @loriab 's repo). Relevant ones are:


And that tarball has a config string of:


Mine, however, has a config string of: (which I generated by modifying the appropriate variables of the script)


(For reference, the config vars for the script are below)


So I notice two things:

  1. The multipole string has a different format; The letter portion is repeated twice on my build, but only once on the repo one. The line amstr[am].lower() * 2 makes it so that this letter always repeats twice, so it looks like I have a different version of the config script. I’ll probably git blame this to figure out what is going on.
  2. I have onebody disabled entirely; Perhaps I’m misunderstanding the meaning of --disable-1body-property-derivs. I need to research this also
  3. My generated src directory is 45 MB, while the repo one is 2 GB… Clearly, something is different with the build config. Is the compiler.config in the repo tarball accurate or am I misusing it?

I haven’t yet looked into any of the above and will do that now… Might have 30 second answers. Just wanted to provide details.

As for part (2) of this whole thing, I am working in Dr. Yan Alexander Wang’s research group at UBC to implement an idea of his. I can’t really dive into specifics yet (everything will likely be published at a later date), but I can say that the plan is to create a new version of the erf_coulomb operator modifying these two lines I think for a slightly different function, and changing the input parameters. I haven’t looked into this too much, I’ve just been trying to get an unmodified version to build for now.

Sorry for being a bit vague. I can see how difficult it is to get these integrals right, but I’m hoping that this change would not require too much difficulty. Unless there’s a way to add operators from outside libint, which didn’t seem possible based on my understanding?

Right, ok, so to answer my above questions:

  1. Psi4 doesn’t seem to care about the extra letters in the config options, just that they have the _dwhatever suffix and the letter right before that is correct, based on my reading of _l2_config_style_eri_llll.
  2. Next, I messed up g12_max_am (supposed to be 4,4, not 4) and onebody (supposed to be 5,4,3)

So my updated script reads:

export LIBINT_orderings=ss
export LIBINT_max_am=6,5,4
export LIBINT_multipole=10
export LIBINT_onebody=5,4,3
export LIBINT_eri_max_am=5,4,3
export LIBINT_eri3_max_am=6,5,4
export LIBINT_eri3_pure_sh=False
export LIBINT_eri2_max_am=6,5,4
export LIBINT_eri2_pure_sh=False
export LIBINT_g12_max_am=4,4

CONFIG_STR=$(python3 export/cmake/configuration-gen.py)
echo Using generated config string $CONFIG_STR

sed -i -E "s/return \"[^\"]*\";/return \"$CONFIG_STR\";/" src/lib/libint/configuration.cc

# Ok, so now configure will all of our options.
# I stole these from the Psi4 release: https://github.com/loriab/libint/releases/download/v0.1/libint-2.8.1-5-4-3-6-5-4_mm10f12ob2_0.tgz
cd $WD
$SCRIPT_DIR/configure '--enable-eri=2' '--enable-eri3=2' '--enable-eri2=2' '--enable-1body=2' '--enable-g12=1' '--disable-1body-property-derivs' '--with-multipole-max-order=10' '--with-g12-max-am=4,4' '--with-eri-max-am=5,4,3' '--with-eri3-max-am=6,5,4' '--with-eri2-max-am=6,5,4' '--with-1body-max-am=5,4,3' '--with-max-am=6,5,4'


(I wrote a bash script to do this reliably, and configuration_gen.py just reads the corresponding environment variables. I’d like to generate the config cmdline myself but I’m a bit lazy)

We’ll see what happens in about an hour :wink:

  • Disabling is the default, and you should disable. Enabling can cause problems. There’s a GH issue somewhere, and Erica has a possible PR workaround.

  • Yeah, I’ve trained psi4 to tolerate not having Hessian ERIs available (Windows didn’t fit into the 6h conda build window with Hessians), but it does demand through 2nd deriv for onebody ints (as you have found and corrected now).

  • Yes, adding operators has to be at the generator source stage. This is the class Jonathon added recently 1-e σpVσp by evaleev · Pull Request #273 · evaleev/libint · GitHub

  • The style for the eri_dddd_d2 symbols is largely there but still getting refinements. If the number of eri2/eri3 symbols is off by a factor of 2, it’s the pure-sh setting. Once you get your current dev setup working and stable, I’d switch to L2 master before tweaking the code.

Sure, I’m a bit worried because it’s still making the export file. The src directory is now up to 2.8 GB. Last few lines are:

b__up_ target=< i(0) r(1) | r12x0_r12y0_r12z0__r12x1_r12y0_r12z0__g12 | s(0) s(1) > ^ { {} }
generating code for VRRP0InBra_aB_i__0__t__1___r12_0_g12_s__0__s__1___Ab__up_0 target=< i(0) t(1) | r12_0_g12 | s(0) s(1) > ^ { {0} }
generating code for CR_aB_i__0__r__1___r12x0_r12y0_r12z0__r12x0_r12y1_r12z0__g12_s__0__s__1___Ab__up_ target=< i(0) r(1) | r12x0_r12y0_r12z0__r12x0_r12y1_r12z0__g12 | s(0) s(1) > ^ { {} }
generating code for CR_aB_i__0__r__1___r12x0_r12y0_r12z0__r12x0_r12y0_r12z1__g12_s__0__s__1___Ab__up_ target=< i(0) r(1) | r12x0_r12y0_r12z0__r12x0_r12y0_r12z1__g12 | s(0) s(1) > ^ { {} }
generating code for VRRP0InBra_aB_i__0__q__1___r12_minus_1_g12_s__0__s__1___Ab__up_0 target=< i(0) q(1) | r12_minus_1_g12 | s(0) s(1) > ^ { {0} }
generating code for CR_aB_i__0__q__1___g12_T1_g12_s__0__s__1___Ab__up_0 target=< i(0) q(1) | g12_T1_g12 | s(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__g__1___T1_g12_s__0__s__1___Ab__up_0 target=< g(0) g(1) | T1_g12 | s(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__f__1___T1_g12_s__0__s__1___Ab__up_0 target=< g(0) f(1) | T1_g12 | s(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__h__1___T1_g12_s__0__s__1___Ab__up_0 target=< g(0) h(1) | T1_g12 | s(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__i__1___T1_g12_s__0__s__1___Ab__up_0 target=< g(0) i(1) | T1_g12 | s(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__k__1___T1_g12_s__0__s__1___Ab__up_0 target=< g(0) k(1) | T1_g12 | s(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__l__1___T1_g12_s__0__s__1___Ab__up_0 target=< g(0) l(1) | T1_g12 | s(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__m__1___T1_g12_s__0__s__1___Ab__up_0 target=< g(0) m(1) | T1_g12 | s(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__n__1___T1_g12_s__0__s__1___Ab__up_0 target=< g(0) n(1) | T1_g12 | s(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__o__1___T1_g12_s__0__s__1___Ab__up_0 target=< g(0) o(1) | T1_g12 | s(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__q__1___T1_g12_s__0__s__1___Ab__up_0 target=< g(0) q(1) | T1_g12 | s(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__h__1___T1_g12_p__0__s__1___Ab__up_0 target=< g(0) h(1) | T1_g12 | p(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__g__1___T1_g12_p__0__s__1___Ab__up_0 target=< g(0) g(1) | T1_g12 | p(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__f__1___T1_g12_p__0__s__1___Ab__up_0 target=< g(0) f(1) | T1_g12 | p(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__i__1___T1_g12_p__0__s__1___Ab__up_0 target=< g(0) i(1) | T1_g12 | p(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__k__1___T1_g12_p__0__s__1___Ab__up_0 target=< g(0) k(1) | T1_g12 | p(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__l__1___T1_g12_p__0__s__1___Ab__up_0 target=< g(0) l(1) | T1_g12 | p(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__m__1___T1_g12_p__0__s__1___Ab__up_0 target=< g(0) m(1) | T1_g12 | p(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__n__1___T1_g12_p__0__s__1___Ab__up_0 target=< g(0) n(1) | T1_g12 | p(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__o__1___T1_g12_p__0__s__1___Ab__up_0 target=< g(0) o(1) | T1_g12 | p(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__q__1___T1_g12_p__0__s__1___Ab__up_0 target=< g(0) q(1) | T1_g12 | p(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__i__1___T1_g12_d__0__s__1___Ab__up_0 target=< g(0) i(1) | T1_g12 | d(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__h__1___T1_g12_d__0__s__1___Ab__up_0 target=< g(0) h(1) | T1_g12 | d(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__g__1___T1_g12_d__0__s__1___Ab__up_0 target=< g(0) g(1) | T1_g12 | d(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__f__1___T1_g12_d__0__s__1___Ab__up_0 target=< g(0) f(1) | T1_g12 | d(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__k__1___T1_g12_d__0__s__1___Ab__up_0 target=< g(0) k(1) | T1_g12 | d(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__l__1___T1_g12_d__0__s__1___Ab__up_0 target=< g(0) l(1) | T1_g12 | d(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__m__1___T1_g12_d__0__s__1___Ab__up_0 target=< g(0) m(1) | T1_g12 | d(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__n__1___T1_g12_d__0__s__1___Ab__up_0 target=< g(0) n(1) | T1_g12 | d(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__o__1___T1_g12_d__0__s__1___Ab__up_0 target=< g(0) o(1) | T1_g12 | d(0) s(1) > ^ { {0} }
generating code for CR_aB_g__0__q__1___T1_g12_d__0__s__1___Ab__up_0 target=< g(0) q(1) | T1_g12 | d(0) s(1) > ^ { {0} 

I left my laptop in the lab to run overnight. Currently been running for ~22 hours and the src dir is up to 7.8 GB. Clearly, something is wrong.

EDIT: Do you know which option I could’ve enabled to generate so much code? I only added --with-1body-max-am=5,4,3 and changed --with-g12-max-am=4 to --with-g12-max-am=4,4, and added --enable-1body=1. Somehow I need to add something to match the 2.0 GB from the GitHub repo, but not so much that I end up with 8+GB and a multi-day build.

I’d do libint-feedstock/recipe/NOTES at main · conda-forge/libint-feedstock · GitHub and you can dial the AM 7 down to 6 or 5 and the 6 down to 5 or 4. good news is that generation (single thread) is slower than build (multithread).

The 1-body=1 isn’t enough for psi, which wants 2nd derivs.

That’s great, thank you… Sorry to bug you again, but I was wondering how the onebody string is determined? Take the example in configuration-gen.py:

# An example

#./configure \
#    --enable-eri=1 \
#    --enable-eri3=1 \
#    --enable-eri2=1 \
#    --enable-1body=2 \
#    --enable-g12=1 \
#    --disable-1body-property-derivs \
#    --with-multipole-max-order=10 \
#    --with-g12-max-am=4 \
#    --with-eri-max-am=5,4 \
#    --with-eri3-max-am=6,5 \
#    --with-eri2-max-am=6,5 \
#    --with-max-am=6,5

## script headmatter
#orderings = "ss"
#max_am = [6, 5]
#multipole = [10]
#onebody = [6, 5, 4]
#eri_max_am = [5, 4]
#eri3_max_am = [6, 5]
#eri3_pure_sh = False
#eri2_max_am = [6, 5]
#eri2_pure_sh = False
#g12_max_am = [4, 4]

Where is the [6, 5, 4] for onebody coming from? For the rest of the parameters, I can just look at the config string… Obviously, the length has to match the corresponding --enable-1body parameter (plus one, the --enable-1body=1 was a typo in my above post), but where are the 6, 5, and 4 coming from?


When AM values aren’t given per-integral-class, they take their default from the max of --with-max-am, so --with-max-am=6,5 should probably be onebody = [6, 6, 6] in the py script. There’s some subtleties associated with with-max-am that I wasn’t clear on at time of script writing. (Fortunately the script is only a temporary solution until CMake can generate the eri_gggg_d0 etc. codes.) Any per-am version of with-max-am only serves as the AM for the paired centers in eri3 ints. Details at full cmake 2/N: integral classes and AM configuration by loriab · Pull Request #309 · evaleev/libint · GitHub .

If you’re delving into the options and the py script to understand what L2 can produce, fine and good. But if you’re doing it just to match that patch line, please don’t bother, as that’s superficial communication for the buildsys and runtime, and can be easily bypassed.

Thank you for all the help, libint builds now. CMake would definitely make this easier, and I have noticed all the work you have put in to the CMake modifications on your own fork/pull requests, so thank you for that. I was also able to make the changes I needed to libint, which were thankfully all in the header files, so make export didn’t have to rebuild much, though the modifications worked on the first try. For anyone’s reference, the following worked for me:



# Build the compiler for libint in your current directory
# This also unifies all the config options that are scattered throughout the code
# Granted, you still have to edit them in three different spots within this file, but it's a bit more manageable

set -e

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

if [[ "$WD/" == "$SCRIPT_DIR/" ]] || [[ "$WD/" == "$SCRIPT_DIR/"* ]]; then
  echo "Looks like you're in the libint source tree"
  echo "I'm told that you can't put your build directory in the source tree"
  echo "Stopping before you get a more frustrating error..."
  exit 1


# Patch the CMakeLists file
# Yes, its in the source tree, but this feels less cursed than unzipping and re-zipping
MAX_AM_ERI=2 # Note that this is just one minus the length of LIBINT_eri_max_am, I think
sed -i -E "s/set\(MAX_AM_ERI \"[0-9]*\"\)/set\(MAX_AM_ERI \"$MAX_AM_ERI\"\)/" export/cmake/CMakeLists.txt.export

# Get the config string

# * For each integrals class, supply a list with max AM for each enabled derivative level.
# * User is responsible for "distributing" defaults across classes and derivs.
#   e.g., `--enable-eri2=2 --with-max-am=4` (`--with-eri2-max-am` not specified) becomes `eri2_max_am = [4, 4, 4]`
export LIBINT_orderings=ss
# max am:
# * only used for paired on eri3.
# * must be same length as eri3_max_am.
# * if `--with-max-am` not given to ./configure, duplicate `eri3_max_am` as `max_am` here.
# * this script will NOT use this as default for other integrals classes.
export LIBINT_max_am=5,5
export LIBINT_multipole=10
export LIBINT_onebody=5,4,3
export LIBINT_eri_max_am=5,5
export LIBINT_eri3_max_am=5,5
export LIBINT_eri3_pure_sh=False
export LIBINT_eri2_max_am=5,5
export LIBINT_eri2_pure_sh=False
export LIBINT_g12_max_am=4,4

CONFIG_STR=$(python3 export/cmake/configuration-gen.py)
echo Using generated config string $CONFIG_STR

sed -i -E "s/return \"[^\"]*\";/return \"$CONFIG_STR\";/" src/lib/libint/configuration.cc

# Ok, so now configure will all of our options.
# I stole these from the Psi4 release: https://forum.psicode.org/t/which-libint-version-is-being-used/3117/13?u=kb1rd
cd $WD
$SCRIPT_DIR/configure \
    --enable-eri=1 \
    --enable-eri3=1 \
    --enable-eri2=1 \
    --enable-1body=2 \
    --enable-g12=1 \
    --disable-1body-property-derivs \
    --with-multipole-max-order=10 \
    --with-g12-max-am=4 \
    --with-eri-max-am=5,5 \
    --with-eri3-max-am=5,5 \
    --with-eri2-max-am=5,5 \

# Now actually make. I think you only need to re-run this if you change the header files in include/libint2/...
make export

echo "All set, export file created!"


# <<<  User edit

import os

def arg(nm): return os.environ["LIBINT_" + nm]
# https://stackoverflow.com/a/715455/7853604
# Leaving the full list in there because it makes me chuckle
def arg_bool(nm): arg(nm).lower() in ['true', '1', 't', 'y', 'yes', 'yeah', 'yup', 'certainly', 'uh-huh']
def arg_intarray(nm): return [int(a) for a in arg(nm).split(',')] if arg(nm).strip() else []

# * For each integrals class, supply a list with max AM for each enabled derivative level.
# * User is responsible for "distributing" defaults across classes and derivs.
#   e.g., `--enable-eri2=2 --with-max-am=4` (`--with-eri2-max-am` not specified) becomes `eri2_max_am = [4, 4, 4]`
orderings = arg('orderings')
# max am:
# * only used for paired on eri3.
# * must be same length as eri3_max_am.
# * if `--with-max-am` not given to ./configure, duplicate `eri3_max_am` as `max_am` here.
# * this script will NOT use this as default for other integrals classes.
max_am = arg_intarray("max_am")
multipole = arg_intarray("multipole")
onebody = arg_intarray("onebody")
eri_max_am = arg_intarray("eri_max_am")
eri3_max_am = arg_intarray("eri3_max_am")
eri3_pure_sh = arg_bool("eri3_pure_sh")
eri2_max_am = arg_intarray("eri2_max_am")
eri2_pure_sh = arg_bool("eri2_pure_sh")
g12_max_am = arg_intarray("g12_max_am")

# >>>  End user edit

components = [orderings]

# multipole
for deriv in range(len(multipole)):
    for am in range(multipole[deriv], 1, -1):  # don't enumerate s, p
        centers = amstr[am].lower() * 2
        comp = f"multipole_{centers}_d{deriv}"

# onebody
for deriv in range(len(onebody)):
    for am in range(onebody[deriv], 1, -1):
        centers = amstr[am].lower() * 2
        comp = f"onebody_{centers}_d{deriv}"

# eri (4-center)
for deriv in range(len(eri_max_am)):
    for am in range(eri_max_am[deriv], 1, -1):
        centers = amstr[am].lower() * 4
        comp = f"eri_{centers}_d{deriv}"

# eri3
no_pure_sh = []
for deriv in range(len(eri3_max_am)):
    for am_paired in range(max_am[deriv], 1, -1):
        for am_fitting in range(eri3_max_am[deriv], 1, -1):
            if am_fitting >= am_paired:
                centers = amstr[am_paired].lower() * 2 + amstr[am_fitting].upper()
                comp = f"eri_{centers}_d{deriv}"
                #print(deriv, am_fitting, am_paired, centers, comp)
if not eri3_pure_sh:

# eri2
no_pure_sh = []
for deriv in range(len(eri2_max_am)):
    for am in range(eri2_max_am[deriv], 1, -1):
        centers = amstr[am].upper() * 2
        comp = f"eri_{centers}_d{deriv}"
if not eri2_pure_sh:

# g12
for deriv in range(len(g12_max_am)):
    for am in range(g12_max_am[deriv], 1, -1):
        centers = amstr[am].lower() * 4
        comp = f"g12_{centers}_d{deriv}"

#for comp in components:
#    print(comp)
components = ";".join(components)