├── .gitmodules ├── .readthedocs.yaml ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── docs ├── api.md ├── assets │ ├── SALTED_icon_vanilla_long.png │ └── SALTED_icon_vanilla_square.png ├── examples │ ├── au_cp2k.md │ ├── water_aims.md │ └── water_pyscf.md ├── index.md ├── input.md ├── installation.md ├── javascripts │ └── mathjax.js ├── requirements.txt ├── stylesheets │ └── extra.css ├── theory.md ├── tutorial │ ├── dataset.md │ ├── predict.md │ └── training.md └── workflow.md ├── example ├── Au100-Na_CP2K │ ├── Au-RI_AUTO_OPT-ccGRB │ ├── Au-RI_AUTO_OPT-ccGRB-small │ ├── README.rst │ ├── coords-qmmm.xyz │ ├── cp2k-inputs │ │ ├── get_RI-AUTO_basis.inp │ │ └── qmmm_RI-print.inp │ └── inp.yaml ├── water_monomer_AIMS │ ├── README.rst │ ├── control.in │ ├── control_read.in │ ├── control_read_setup.in │ ├── inp.yaml │ ├── run-aims-predict-reorder.sbatch │ ├── run-aims-predict.sbatch │ ├── run-aims.sbatch │ ├── run-ml.sbatch │ ├── water_dimers_10.xyz │ └── water_monomers_100.xyz ├── water_monomer_AIMS_response │ ├── README │ ├── control.in │ ├── control_read.in │ ├── covariance_test.py │ ├── inp.yaml │ ├── move_data.py │ ├── move_rho1_data.py │ ├── run-aims.sbatch │ ├── run_test_serial.sh │ └── water_monomers_1k.xyz └── water_monomer_PySCF │ ├── README.rst │ ├── covariance_test.py │ ├── inp.yaml │ ├── interface.py │ ├── run_test_parallel.sh │ ├── run_test_serial.sh │ ├── water_dimers_10.xyz │ ├── water_monomers_1k.xyz │ └── water_total.xyz ├── mkdocs.yaml ├── salted ├── __init__.py ├── aims │ ├── _deprecated_ │ │ └── get_basis_info_deprecated.py │ ├── collect_energies.py │ ├── get_basis_info.py │ ├── get_df_err.py │ ├── get_ml_err.py │ ├── make_geoms.py │ ├── move_data.py │ ├── move_data_in.py │ └── move_data_in_reorder.py ├── basis.py ├── basis_client.py ├── cp2k │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-39.pyc │ │ └── cp2k2salted.cpython-39.pyc │ ├── _deprecated_ │ │ ├── alphas-cp2k.py │ │ ├── contracted │ │ │ ├── coefs-contra-cp2k.py │ │ │ ├── contract_coefs-cp2k.py │ │ │ ├── contract_projs-cp2k.py │ │ │ ├── contraction-cp2k.py │ │ │ ├── contraction-multi-cp2k.py │ │ │ ├── df2cube-cp2k-contra.py │ │ │ ├── dp2cube-cp2k-contra.py │ │ │ ├── feature_vector-contracted.py │ │ │ ├── get_contra-averages.py │ │ │ ├── init_data-cp2k-contra.py │ │ │ ├── matrices-contracted.py │ │ │ ├── minimize_loss-parallel-contracted.py │ │ │ ├── minimize_loss-serial-contracted.py │ │ │ ├── overlap-contracted-cp2k.py │ │ │ ├── prediction-contracted-cp2k.py │ │ │ ├── rebuild_contra-projs.py │ │ │ └── validation-contracted-cp2k.py │ │ ├── overlap-cp2k.py │ │ ├── projs-cp2k-perat-unrestricted.py │ │ └── projs-cp2k-perat.py │ ├── cp2k2salted.py │ ├── df2cube.py │ ├── get_basis_info.py │ ├── polarizability.py │ ├── uncontract_ri_basis.py │ ├── utils.py │ └── xyz2sys.py ├── efield.py ├── get_averages.py ├── get_basis_info.py ├── hessian_matrix.py ├── init_pred.py ├── initialize.py ├── minimize_loss.py ├── ortho │ ├── ortho_error.py │ ├── ortho_projections.py │ └── ortho_regression.py ├── prediction.py ├── pyscf │ ├── __init__.py │ ├── _deprecated_ │ │ ├── dm2df-pyscf_deprecated.py │ │ └── run-pyscf_deprecated.py │ ├── dm2df.py │ ├── electro_energy.py │ ├── electro_error.py │ ├── get_basis_info.py │ └── run_pyscf.py ├── rkhs_projector.py ├── rkhs_vector.py ├── salted_prediction.py ├── scalar_vector.py ├── solve_regression.py ├── sparse-gpr_energies.py ├── sparse_descriptor.py ├── sparse_selection.py ├── sparsify_features.py ├── sph_utils.py ├── sys_utils.py ├── validation.py └── wigner.py ├── setup.py └── src ├── antiequicomb.f90 ├── antiequicombnonorm.f90 ├── antiequicombsparse.f90 ├── equicomb.f90 ├── equicombfield.f90 ├── equicombfps.f90 ├── equicombnonorm.f90 ├── equicombsparse.f90 ├── kernelequicomb.f90 ├── kernelnorm.f90 ├── ovlp2c.f90 ├── ovlp2cXYperiodic.f90 ├── ovlp2cnonperiodic.f90 ├── ovlp3c.f90 ├── ovlp3cXYperiodic.f90 └── ovlp3cnonperiodic.f90 /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreagrisafi/SALTED/15e84a809b6b674793f19763d62acc2492a7b108/.gitmodules -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.10" 13 | 14 | mkdocs: 15 | configuration: mkdocs.yaml 16 | 17 | # Optionally declare the Python requirements required to build your docs 18 | python: 19 | install: 20 | - requirements: docs/requirements.txt 21 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include salted/lib/equicomb.so 2 | include salted/lib/equicombnonorm.so 3 | include salted/lib/equicombsparse.so 4 | include salted/lib/equicombfps.so 5 | include salted/lib/equicombnonorm.so 6 | include salted/lib/equicombsparse.so 7 | include salted/lib/antiequicomb.so 8 | include salted/lib/antiequicombnonorm.so 9 | include salted/lib/antiequicombsparse.so 10 | include salted/lib/kernelequicomb.so 11 | include salted/lib/kernelnorm.so 12 | include salted/lib/equicombfield.so 13 | include salted/lib/ovlp2cnonperiodic.so 14 | include salted/lib/ovlp2c.so 15 | include salted/lib/ovlp2cXYperiodic.so 16 | include salted/lib/ovlp3cnonperiodic.so 17 | include salted/lib/ovlp3c.so 18 | include salted/lib/ovlp3cXYperiodic.so 19 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | --- 4 | 5 | ## Density fitting basis IO 6 | 7 | ::: salted.basis_client.BasisClient 8 | options: 9 | show_root_heading: true 10 | 11 |
12 | 13 | --- 14 | 15 | ## Utility functions 16 | 17 | ::: salted.sys_utils.sort_grid_data 18 | options: 19 | show_root_heading: true 20 | 21 |
22 | 23 | --- 24 | 25 | ## Input file IO 26 | 27 | ::: salted.sys_utils.ParseConfig 28 | 29 | 30 |
31 | 32 | --- 33 | 34 | -------------------------------------------------------------------------------- /docs/assets/SALTED_icon_vanilla_long.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreagrisafi/SALTED/15e84a809b6b674793f19763d62acc2492a7b108/docs/assets/SALTED_icon_vanilla_long.png -------------------------------------------------------------------------------- /docs/assets/SALTED_icon_vanilla_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreagrisafi/SALTED/15e84a809b6b674793f19763d62acc2492a7b108/docs/assets/SALTED_icon_vanilla_square.png -------------------------------------------------------------------------------- /docs/examples/au_cp2k.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreagrisafi/SALTED/15e84a809b6b674793f19763d62acc2492a7b108/docs/examples/au_cp2k.md -------------------------------------------------------------------------------- /docs/examples/water_aims.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreagrisafi/SALTED/15e84a809b6b674793f19763d62acc2492a7b108/docs/examples/water_aims.md -------------------------------------------------------------------------------- /docs/examples/water_pyscf.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreagrisafi/SALTED/15e84a809b6b674793f19763d62acc2492a7b108/docs/examples/water_pyscf.md -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Welcome to SALTED documentation! 2 | 3 | Ab initio electronic-structure methods are computationally expensive, making the calculation of electronic properties impractical for large systems and/or long simulation trajectories. In this context, the **S**ymmetry-**A**dapted **L**earning of **T**hree-dimensional **E**lectron **D**ensities (SALTED) program represents a highly transferable machine-learning method that can be used to perform inexpensive, yet accurate, predictions of the electronic charge density of a system. The transferability of the model is derived from a suitable decomposition of the electron density, which follows density-fitting, a.k.a. resolution of the identity (RI), approximations, commonly used in electronic-structure codes. In particular, we rely on a linear expansion of the electron density over a basis made of atom-centered radial functions and spherical harmonics, which can be used to represent the three-dimensional scalar field via a set of local atom-centered coefficients. From this representation of the electron density, a symmetry-adapted extension of Gaussian Process Regression is then used to perform equivariant predictions of the expansion coefficients, thus bypassing the need to learn the rotational symmetry of spherical harmonics from data. 4 | 5 | The core ideas of the method have been first introduced in [10.1021/acscentsci.8b00551](https://pubs.acs.org/doi/10.1021/acscentsci.8b00551) and [10.1039/C9SC02696G](https://pubs.rsc.org/en/content/articlelanding/2019/sc/c9sc02696g) with applications to isolated molecules. SALTED has then been formally presented in [10.1021/acs.jctc.1c00576 ](https://pubs.acs.org/doi/10.1021/acs.jctc.1c00576) in the context of extending the method to periodic condensed-phase systems. The current implementation of SALTED, as well as the present documentation, follows the advancements reported in [10.1021/acs.jctc.2c00850 ](https://pubs.acs.org/doi/full/10.1021/acs.jctc.2c00850), where an optimization of the method is carried out by recasting the learning problem into the Reproducing Kernel Hilbert Space (RKHS). 6 | 7 | 8 | Check out [installation](installation) to install the project. You can find out more in the [theory](theory) and [tutorial](tutorial) sections. 9 | 10 | !!! note 11 | 12 | This project is under active development. 13 | 14 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | !!! warning "Linux only 🐧" 4 | SALTED is only available on Linux OS. For Windows users, please use WSL or virtual machines. 5 | 6 | ## Install SALTED 7 | 8 | You can find the SALTED program on [GitHub](https://github.com/andreagrisafi/SALTED). In the SALTED directory, simply run `make`, followed by `pip install .` 9 | 10 | ??? note "Editable python package" 11 | If you want to modify the code, you can install SALTED with the following command: 12 | 13 | ```bash 14 | python -m pip install -e . 15 | ``` 16 | 17 | where `-e` means editable installation, which means you can modify the code and the changes will be reflected in the installed package. 18 | This is useful for looking into the code / debugging. 19 | 20 | 21 | ### Dependencies 22 | 23 | - `featomic`: featomic installation requires a RUST compiler. To install a RUST compiler, run: `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh && source "$HOME/.cargo/env"`. featomic can then be installed using `pip install git+https://github.com/metatensor/featomic.git`. 24 | 25 | - `mpi4py`: mpi4py is required to use MPI parallelisation; SALTED can nonetheless be run without this. A parallel h5py installation is required to use MPI parellelisation. This can be installed by running: `HDF5_MPI="ON" CC=mpicc pip install --no-cache-dir --no-binary=h5py h5py` provided HDF5 has been compiled with MPI support. 26 | 27 | - `pip install meson ninja` to run f2py using meson backend following versions of Python > 3.12. 28 | 29 | ## Install electronic-structure codes 30 | 31 | SALTED is to date interfaced with the following electronic-structure codes: *CP2K*, *PySCF*, and *FHI-aims*. If you are interested in using SALTED in combination with other codes, please contact one of the developers. 32 | 33 | ### PySCF 34 | 35 | To install PySCF, you can follow the instructions [here](https://pyscf.org/install.html). 36 | 37 | Please note that PySCF works well with small systems like molecules and clusters, but it lacks the scalability to handle periodic systems. 38 | We suggest using CP2K or FHI-aims for these applications. 39 | 40 | 41 | ### FHI-aims 42 | 43 | 44 | Please use recent versions of FHI-aims, the tutorial presented in this documentation will use the version `240403`. 45 | 46 | To install FHI-aims on your cluster or PC, you will need a FHI-aims licence and you can find further information [here](https://fhi-aims.org/get-the-code). 47 | Then you can follow the tutorial [Basics of Running FHI-aims](https://fhi-aims-club.gitlab.io/tutorials/basics-of-running-fhi-aims/preparations/) to install FHI-aims. 48 | The `CMake` file is important and you can find more information in the [CMake Tutorial for Compiling FHI-aims (parallel version)](https://aims-git.rz-berlin.mpg.de/aims/FHIaims/-/wikis/CMake%20Tutorial). 49 | 50 | Especially, you can find an FHI-aims focused tutorial on SALTED [here in FHI-aims-club](https://fhi-aims-club.gitlab.io/tutorials/fhi-aims-with-salted). 51 | 52 | ### CP2K 53 | 54 | Printing of RI density coefficients and 2-center auxiliary integrals needed to train SALTED is made available starting from the v2023.1 release of CP2K. 55 | -------------------------------------------------------------------------------- /docs/javascripts/mathjax.js: -------------------------------------------------------------------------------- 1 | window.MathJax = { 2 | tex: { 3 | inlineMath: [["\\(", "\\)"]], 4 | displayMath: [["\\[", "\\]"]], 5 | processEscapes: true, 6 | processEnvironments: true 7 | }, 8 | options: { 9 | ignoreHtmlClass: ".*|", 10 | processHtmlClass: "arithmatex" 11 | } 12 | }; 13 | 14 | document$.subscribe(() => { 15 | MathJax.startup.output.clearCache() 16 | MathJax.typesetClear() 17 | MathJax.texReset() 18 | MathJax.typesetPromise() 19 | }) 20 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with python 3.10 3 | # To update, run: 4 | # 5 | # pip-compile docs/requirements.in 6 | # 7 | click==8.1.3 8 | # via mkdocs 9 | ghp-import==2.1.0 10 | # via mkdocs 11 | griffe==0.22.0 12 | # via mkdocstrings-python 13 | importlib-metadata==4.12.0 14 | # via mkdocs 15 | jinja2==3.1.2 16 | # via 17 | # mkdocs 18 | # mkdocstrings 19 | markdown==3.3.7 20 | # via 21 | # markdown-include 22 | # mkdocs 23 | # mkdocs-autorefs 24 | # mkdocstrings 25 | # pymdown-extensions 26 | markdown-include==0.6.0 27 | # via -r docs/requirements.in 28 | markupsafe==2.1.1 29 | # via 30 | # jinja2 31 | # mkdocstrings 32 | mergedeep==1.3.4 33 | # via mkdocs 34 | mkdocs==1.3.0 35 | # via 36 | # -r docs/requirements.in 37 | # mkdocs-autorefs 38 | # mkdocstrings 39 | mkdocs-autorefs==0.4.1 40 | # via mkdocstrings 41 | mkdocstrings[python]==0.19.0 42 | # via 43 | # -r docs/requirements.in 44 | # mkdocstrings-python 45 | mkdocstrings-python==0.7.1 46 | # via mkdocstrings 47 | packaging==21.3 48 | # via mkdocs 49 | pymdown-extensions==9.5 50 | # via mkdocstrings 51 | pyparsing==3.0.9 52 | # via packaging 53 | python-dateutil==2.8.2 54 | # via ghp-import 55 | pyyaml==6.0 56 | # via 57 | # mkdocs 58 | # pyyaml-env-tag 59 | pyyaml-env-tag==0.1 60 | # via mkdocs 61 | six==1.16.0 62 | # via python-dateutil 63 | watchdog==2.1.9 64 | # via mkdocs 65 | zipp==3.8.0 66 | # via importlib-metadata -------------------------------------------------------------------------------- /docs/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | /* Maximum space for text block 2 | see https://squidfunk.github.io/mkdocs-material/setup/setting-up-navigation/#content-area-width 3 | */ 4 | .md-grid { 5 | max-width: 100%; /* 100%, if you want to stretch to full-width */ 6 | } 7 | 8 | /* wrapping lines in tables instead of adding horizontal scroll bars 9 | see https://github.com/mkdocs/mkdocs/discussions/3035 10 | */ 11 | .wy-table-responsive table td, .wy-table-responsive table th { 12 | white-space: normal !important; 13 | } 14 | 15 | .wy-table-responsive { 16 | overflow : visible !important; 17 | } -------------------------------------------------------------------------------- /docs/tutorial/dataset.md: -------------------------------------------------------------------------------- 1 | # Prepare Dataset 2 | 3 | This section describes how to prepare the dataset for training the SALTED model with different ab initio software packages. 4 | 5 | ## What do we need? 6 | 7 | 1. Product basis overlap matrices 8 | 1. Density fitting coefficients 9 | 10 | ## Generate Dataset 11 | 12 | To date, support for generating these overlap matrices and coefficients is included in three electronic structure packages - PySCF, FHI-aims and CP2K. If you develop another package and would like to develop SALTED integration, please contact one of the developers. 13 | 14 | Whichever code is used, the result should be the generation of new directories named `overlaps` and `coefficients` in the `saltedpath` directory. These will be used to train a SALTED model as described in the next section. 15 | 16 | ### PySCF 17 | 18 | 1. The following input arguments must be added to the `inp.qm` section: 19 | - `qmcode`: define the quantum-mechanical code as `pyscf` 20 | - `path2qm`: set the path where the PySCF data are going to be saved 21 | - `qmbasis`: define the wave function basis set for the Kohn-Sham calculation (example: `cc-pvqz`) 22 | - `functional`: define the functional for the Kohn-Sham calculation (example: `b3lyp`) 23 | 1. Define the auxiliary basis set using the input variable `dfbasis`, as provided in the `inp.qm` section. This must be chosen consistently with the wave function basis set (example: `RI-cc-pvqz`). Then, add this basis set information to SALTED by running: 24 | ```bash 25 | python3 -m salted.get_basis_info 26 | ``` 27 | 1. Run PySCF to compute the Kohn-Sham density matrices: 28 | ```bash 29 | python3 -m salted.pyscf.run_pyscf 30 | ``` 31 | 1. From the computed density matrices, perform the density fitting on the selected auxiliary basis set by running: 32 | ```bash 33 | python3 -m salted.pyscf.dm2df 34 | ``` 35 | 36 | ### FHI-aims 37 | 38 | A detailed description of how to generate the training data for SALTED using FHI-aims can be found at [the dedicated SALTED/FHI-aims tutorial](https://fhi-aims-club.gitlab.io/tutorials/fhi-aims-with-salted). 39 | 40 | 41 | ### CP2K 42 | 43 | 1. The following input arguments must be added to the `inp.qm` section: 44 | - `qmcode`: define quantum-mechanical code as `cp2k` 45 | - `path2qm`: set the path where the CP2K data are going to be saved 46 | - `periodic`: set the periodicity of the system (`0D,2D,3D`) 47 | - `coeffile`: filename of RI density coefficients as printed by CP2K 48 | - `ovlpfile`: filename of 2-center auxiliary integrals as printed by CP2K 49 | - `dfbasis`: define auxiliary basis for the electron density expansion 50 | - `pseudocharge`: define pseudocharge according to the adopted GTH pseudopotential 51 | 1. Initialize the systems used for the CP2K calculation by running: 52 | ```bash 53 | python3 -m salted.cp2k.xyz2sys 54 | ``` 55 | System cells and coordinates will be automatically saved in folders named `conf_1`, `conf_2`, ... up to the total number of structures included in the dataset, located into the selected `inp.qm.path2qm`. 56 | 1. Print auxiliary basis set information from the CP2K automatically generated RI basis set, as described in https://doi.org/10.1021/acs.jctc.6b01041. An example of a CP2K input file can be found in `cp2k-inputs/get_RI-AUTO_basis.inp`. 57 | 1. An uncontracted version of this basis can be produced to increase the efficiency of the RI printing workflow, by running: 58 | ```bash 59 | python3 -m salted.cp2k.uncontract_ri_basis contracted_basis_file uncontracted_basis_file 60 | ``` 61 | Then, copy `uncontracted_basis_file` to the `cp2k/data/` folder in order to use this basis set to produce the reference density-fitting data, and set the corresponding filename in the `inp.qm.dfbasis` input variable. 62 | 1. Add the selected auxiliary basis to SALTED by running: 63 | ```bash 64 | python3 -m salted.get_basis_info 65 | ``` 66 | 1. Run the CP2K calculations using the selected auxiliary basis and print out the training data made of reference RI coefficients and 2-center auxiliary integrals. An example of a CP2K input file can be found in `cp2k-inputs/qmmm_RI-print.inp`. 67 | 1. Set the `inp.qm.coeffile` and `inp.qm.ovlpfile` variables according to the filename of the generated training data and convert them to SALTED format by running: 68 | ```bash 69 | python3 -m salted.cp2k.cp2k2salted 70 | ``` 71 | -------------------------------------------------------------------------------- /docs/tutorial/predict.md: -------------------------------------------------------------------------------- 1 | # Predict Properties 2 | 3 | (for developing, please ref https://gitlab.com/FHI-aims-club/tutorials/fhi-aims-with-salted/-/blob/optimization/Tutorial/Tutorial-4/README.md?ref_type=heads&plain=1) 4 | -------------------------------------------------------------------------------- /docs/workflow.md: -------------------------------------------------------------------------------- 1 | # Workflow 2 | 3 | The general SALTED workflow is summarised below. Detailed descriptions of each step will given in the following sections of the tutorial. 4 | 5 | ## SALTED workflow 6 | 7 | 1. Calculate electron density and [density fitting (DF)](theory.md#density-fitting-method) coefficients. 8 | 1. Generate (optionally sparse) [$\lambda$-SOAP descriptors](theory.md#symmetry-adapted-descriptor) by rascaline, and sparsify the atomic environments by [farthest point sampling (FPS) method](theory.md#farthest-point-sampling). This step uses the salted functions `initialize`, `sparse_selection`, and `sparse_descriptor`. 9 | 1. Calculate [RKHS](theory.md#reproducing-kernel-hilbert-space-rkhs) related quantities, including kernel matrix $\mathbf{K}_{MM}$, associated projectors, and the feature vector $\mathbf{\Psi}_{ND}$. This step uses the salted functions `rkhs_projector` and `rkhs_vector`. 10 | 1. Optimize [GPR weights](theory.md#gpr-optimization) by either [direct inversion](theory.md#direct-inversion) or [CG method](theory.md#conjugate-gradient-method), and save the optimized weights. This uses either the functions `hessian_matrix` and `solve_regression`, or `minimize_loss`. 11 | 1. Validate the model using the `validation` function. 12 | 1. Predict [density fitting coefficients](theory.md#density-fitting-method) of new structures using the [GPR weights](theory.md#gpr-optimization) obtained in the previous step, saving the predicted density coefficients. This uses the `prediction` function. 13 | 1. Use the predicted density coefficients to calculate properties derived from the predicted electron density. 14 | 15 | -------------------------------------------------------------------------------- /example/Au100-Na_CP2K/Au-RI_AUTO_OPT-ccGRB: -------------------------------------------------------------------------------- 1 | Au RI_AUTO_OPT-ccGRB 2 | 42 3 | 1 0 0 1 1 4 | 10.0801255113 1.0 5 | 1 0 0 1 1 6 | 5.6000697285 1.0 7 | 1 0 0 1 1 8 | 3.1111498492 1.0 9 | 1 0 0 1 1 10 | 1.7284165829 1.0 11 | 1 0 0 1 1 12 | 0.9602314349 1.0 13 | 1 0 0 1 1 14 | 0.5334619083 1.0 15 | 1 0 0 1 1 16 | 0.2963677268 1.0 17 | 1 0 0 1 1 18 | 0.1646487371 1.0 19 | 1 1 1 1 1 20 | 10.0801255113 1.0 21 | 1 1 1 1 1 22 | 5.6000697285 1.0 23 | 1 1 1 1 1 24 | 3.1111498492 1.0 25 | 1 1 1 1 1 26 | 1.7284165829 1.0 27 | 1 1 1 1 1 28 | 0.9602314349 1.0 29 | 1 1 1 1 1 30 | 0.5334619083 1.0 31 | 1 1 1 1 1 32 | 0.2963677268 1.0 33 | 1 1 1 1 1 34 | 0.1646487371 1.0 35 | 1 2 2 1 1 36 | 10.0801255113 1.0 37 | 1 2 2 1 1 38 | 5.6000697285 1.0 39 | 1 2 2 1 1 40 | 3.1111498492 1.0 41 | 1 2 2 1 1 42 | 1.7284165829 1.0 43 | 1 2 2 1 1 44 | 0.9602314349 1.0 45 | 1 2 2 1 1 46 | 0.5334619083 1.0 47 | 1 2 2 1 1 48 | 0.2963677268 1.0 49 | 1 2 2 1 1 50 | 0.1646487371 1.0 51 | 1 3 3 1 1 52 | 10.0801255113 1.0 53 | 1 3 3 1 1 54 | 5.6000697285 1.0 55 | 1 3 3 1 1 56 | 3.1111498492 1.0 57 | 1 3 3 1 1 58 | 1.7284165829 1.0 59 | 1 3 3 1 1 60 | 0.9602314349 1.0 61 | 1 3 3 1 1 62 | 0.5334619083 1.0 63 | 1 3 3 1 1 64 | 0.2963677268 1.0 65 | 1 3 3 1 1 66 | 0.1646487371 1.0 67 | 1 4 4 1 1 68 | 10.0801255113 1.0 69 | 1 4 4 1 1 70 | 5.6000697285 1.0 71 | 1 4 4 1 1 72 | 3.1111498492 1.0 73 | 1 4 4 1 1 74 | 1.7284165829 1.0 75 | 1 4 4 1 1 76 | 0.9602314349 1.0 77 | 1 4 4 1 1 78 | 0.5334619083 1.0 79 | 1 4 4 1 1 80 | 0.2963677268 1.0 81 | 1 4 4 1 1 82 | 0.1646487371 1.0 83 | 1 5 5 1 1 84 | 0.9742243686 1.0 85 | 1 6 6 1 1 86 | 0.9742243686 1.0 87 | -------------------------------------------------------------------------------- /example/Au100-Na_CP2K/Au-RI_AUTO_OPT-ccGRB-small: -------------------------------------------------------------------------------- 1 | Au RI_AUTO_OPT-ccGRB-small 2 | 38 3 | 1 0 0 1 1 4 | 10.0801255113 1.0 5 | 1 0 0 1 1 6 | 5.6000697285 1.0 7 | 1 0 0 1 1 8 | 3.1111498492 1.0 9 | 1 0 0 1 1 10 | 1.7284165829 1.0 11 | 1 0 0 1 1 12 | 0.9602314349 1.0 13 | 1 0 0 1 1 14 | 0.5334619083 1.0 15 | 1 0 0 1 1 16 | 0.2963677268 1.0 17 | 1 0 0 1 1 18 | 0.1646487371 1.0 19 | 1 1 1 1 1 20 | 10.0801255113 1.0 21 | 1 1 1 1 1 22 | 5.6000697285 1.0 23 | 1 1 1 1 1 24 | 3.1111498492 1.0 25 | 1 1 1 1 1 26 | 1.7284165829 1.0 27 | 1 1 1 1 1 28 | 0.9602314349 1.0 29 | 1 1 1 1 1 30 | 0.5334619083 1.0 31 | 1 1 1 1 1 32 | 0.2963677268 1.0 33 | 1 1 1 1 1 34 | 0.1646487371 1.0 35 | 1 2 2 1 1 36 | 10.0801255113 1.0 37 | 1 2 2 1 1 38 | 5.6000697285 1.0 39 | 1 2 2 1 1 40 | 3.1111498492 1.0 41 | 1 2 2 1 1 42 | 1.7284165829 1.0 43 | 1 2 2 1 1 44 | 0.9602314349 1.0 45 | 1 2 2 1 1 46 | 0.5334619083 1.0 47 | 1 2 2 1 1 48 | 0.2963677268 1.0 49 | 1 2 2 1 1 50 | 0.1646487371 1.0 51 | 1 3 3 1 1 52 | 10.0801255113 1.0 53 | 1 3 3 1 1 54 | 5.6000697285 1.0 55 | 1 3 3 1 1 56 | 3.1111498492 1.0 57 | 1 3 3 1 1 58 | 1.7284165829 1.0 59 | 1 3 3 1 1 60 | 0.9602314349 1.0 61 | 1 3 3 1 1 62 | 0.5334619083 1.0 63 | 1 3 3 1 1 64 | 0.2963677268 1.0 65 | 1 3 3 1 1 66 | 0.1646487371 1.0 67 | 1 4 4 1 1 68 | 10.0801255113 1.0 69 | 1 4 4 1 1 70 | 5.6000697285 1.0 71 | 1 4 4 1 1 72 | 3.1111498492 1.0 73 | 1 4 4 1 1 74 | 1.7284165829 1.0 75 | 1 4 4 1 1 76 | 0.9602314349 1.0 77 | 1 4 4 1 1 78 | 0.5334619083 1.0 79 | -------------------------------------------------------------------------------- /example/Au100-Na_CP2K/README.rst: -------------------------------------------------------------------------------- 1 | Generate QM/MM training data using CP2K 2 | --------------------------------------- 3 | In what follows, we describe how to generate training electron densities of a dataset made of Au(100) slabs that interact with a classical Gaussian charge, using the CP2K simulation program. NB: this is made possible through to the official development version of CP2K (https://github.com/cp2k/cp2k). 4 | 5 | 1. The following input arguments must be added to the :code:`inp.qm` section: 6 | 7 | :code:`qmcode`: define quantum-mechanical code as :code:`cp2k` 8 | 9 | :code:`path2qm`: set the path where the CP2K data are going to be saved 10 | 11 | :code:`periodic`: set the periodicity of the system (:code:`0D,2D,3D`) 12 | 13 | :code:`coeffile`: filename of RI density coefficients as printed by CP2K 14 | 15 | :code:`ovlpfile`: filename of 2-center auxiliary integrals as printed by CP2K 16 | 17 | :code:`dfbasis`: define auxiliary basis for the electron density expansion 18 | 19 | :code:`pseudocharge`: define pseudocharge according to the adopted GTH pseudopotential 20 | 21 | 2. Initialize the systems used for the CP2K calculation by running: 22 | 23 | :code:`python3 -m salted.cp2k.xyz2sys` 24 | 25 | System cells and coordinates will be automatically saved in folders named :code:`conf_1`, :code:`conf_2`, ... up to the total number of structures included in the dataset, located into the selected :code:`inp.qm.path2qm`. 26 | 27 | 2. Print auxiliary basis set information from the CP2K automatically generated RI basis set, as described in https://doi.org/10.1021/acs.jctc.6b01041. An example of a CP2K input file can be found in :code:`cp2k-inputs/get_RI-AUTO_basis.inp`. 28 | 29 | 3. An uncontracted version of this basis can be produced to increase the efficiency of the RI printing workflow, by running: 30 | 31 | :code:`python3 -m salted.cp2k.uncontract_ri_basis contracted_basis_file uncontracted_basis_file` 32 | 33 | Then, copy :code:`uncontracted_basis_file` to the :code:`cp2k/data/` folder in order to use this basis set to produce the reference density-fitting data, and set the corresponding filename in the :code:`inp.qm.dfbasis` input variable. 34 | 35 | 4. Add the selected auxiliary basis to SALTED by running: 36 | 37 | :code:`python3 -m salted.get_basis_info` 38 | 39 | 5. Run the CP2K calculations using the selected auxiliary basis and print out the training data made of reference RI coefficients and 2-center auxiliary integrals. An example of a CP2K input file can be found in :code:`cp2k-inputs/qmmm_RI-print.inp`. 40 | 41 | 6. Set the :code:`inp.qm.coeffile` and :code:`inp.qm.ovlpfile` variables according to the filename of the generated training data and convert them to SALTED format by running: 42 | 43 | :code:`python3 -m salted.cp2k.cp2k2salted` 44 | 45 | -------------------------------------------------------------------------------- /example/Au100-Na_CP2K/cp2k-inputs/get_RI-AUTO_basis.inp: -------------------------------------------------------------------------------- 1 | @set PROJECT Au 2 | @set RUN ENERGY 3 | 4 | &FORCE_EVAL 5 | METHOD QMMM 6 | &DFT 7 | BASIS_SET_FILE_NAME BASIS_ccGRB_UZH 8 | POTENTIAL_FILE_NAME POTENTIAL_UZH 9 | ! Select RI basis set size among SMALL, MEDIUM, LARGE 10 | AUTO_BASIS RI_HFX MEDIUM 11 | &MGRID 12 | CUTOFF 800 13 | REL_CUTOFF 80 14 | COMMENSURATE 15 | &END MGRID 16 | &POISSON 17 | PERIODIC XY 18 | POISSON_SOLVER MT 19 | &END POISSON 20 | ! Print basis set information 21 | &PRINT 22 | &BASIS_SET_FILE 23 | &END 24 | &END 25 | &SCF 26 | SCF_GUESS ATOMIC 27 | EPS_SCF 1.0E-8 28 | MAX_SCF 1 29 | ADDED_MOS 2000 30 | CHOLESKY INVERSE 31 | &SMEAR ON 32 | METHOD FERMI_DIRAC 33 | ELECTRONIC_TEMPERATURE [K] 300 34 | &END SMEAR 35 | &DIAGONALIZATION 36 | ALGORITHM STANDARD 37 | &END DIAGONALIZATION 38 | &MIXING 39 | METHOD BROYDEN_MIXING 40 | ALPHA 0.1 41 | BETA 1.5 42 | NBROYDEN 8 43 | &END MIXING 44 | &PRINT 45 | &RESTART 46 | &END 47 | &END 48 | &END SCF 49 | &XC 50 | &XC_FUNCTIONAL PBE 51 | &END XC_FUNCTIONAL 52 | &HF 53 | FRACTION 0.0 54 | &RI 55 | RI_METRIC IDENTITY 56 | &END 57 | &INTERACTION_POTENTIAL 58 | POTENTIAL_TYPE IDENTITY 59 | &END 60 | &END 61 | &END XC 62 | &END DFT 63 | &MM 64 | &FORCEFIELD 65 | &CHARGE 66 | ATOM Na 67 | CHARGE 1.000000 68 | &END CHARGE 69 | &CHARGE 70 | ATOM Au 71 | CHARGE 0.0 72 | &END CHARGE 73 | &NONBONDED 74 | &LENNARD-JONES 75 | atoms Na Na 76 | EPSILON 0.0 77 | SIGMA 3.166 78 | RCUT 11.4 79 | &END LENNARD-JONES 80 | &LENNARD-JONES 81 | atoms Na Au 82 | EPSILON 0.0 83 | SIGMA 3.6705 84 | RCUT 11.4 85 | &END LENNARD-JONES 86 | &LENNARD-JONES 87 | atoms Au Au 88 | EPSILON 0.0 89 | SIGMA 3.6705 90 | RCUT 11.4 91 | &END LENNARD-JONES 92 | &END NONBONDED 93 | &END FORCEFIELD 94 | &POISSON 95 | &EWALD 96 | EWALD_TYPE ewald 97 | ALPHA .44 98 | GMAX 21 99 | &END EWALD 100 | &END POISSON 101 | &END MM 102 | &QMMM 103 | MM_POTENTIAL_FILE_NAME MM_POTENTIAL 104 | USE_GEEP_LIB 12 105 | @include cell.sys 106 | ECOUPL GAUSS 107 | NOCOMPATIBILITY 108 | &PERIODIC 109 | &POISSON 110 | PERIODIC XY 111 | POISSON_SOLVER MT 112 | &END POISSON 113 | &MULTIPOLE OFF 114 | &END 115 | &END PERIODIC 116 | &MM_KIND Na 117 | RADIUS 2.27 !ionic radius 118 | &END MM_KIND 119 | &QM_KIND Au 120 | MM_INDEX 1..12 121 | &END QM_KIND 122 | &END QMMM 123 | &SUBSYS 124 | @include cell.sys 125 | @include coords.sys 126 | &KIND Au 127 | BASIS_SET ccGRB-D-q11 128 | POTENTIAL GTH-PBE-q11 129 | &END KIND 130 | &KIND Na 131 | BASIS_SET ccGRB-D-q11 132 | POTENTIAL GTH-PBE-q11 133 | &END KIND 134 | &TOPOLOGY 135 | &END TOPOLOGY 136 | &END SUBSYS 137 | &END FORCE_EVAL 138 | &GLOBAL 139 | PROJECT ${PROJECT} 140 | RUN_TYPE ${RUN} 141 | PRINT_LEVEL MEDIUM 142 | EXTENDED_FFT_LENGTHS 143 | &END GLOBAL 144 | -------------------------------------------------------------------------------- /example/Au100-Na_CP2K/cp2k-inputs/qmmm_RI-print.inp: -------------------------------------------------------------------------------- 1 | @set PROJECT Au 2 | @set RUN ENERGY 3 | 4 | &FORCE_EVAL 5 | METHOD QMMM 6 | &DFT 7 | BASIS_SET_FILE_NAME BASIS_ccGRB_UZH 8 | BASIS_SET_FILE_NAME RI_AUTO_OPT 9 | POTENTIAL_FILE_NAME POTENTIAL_UZH 10 | &MGRID 11 | CUTOFF 800 12 | REL_CUTOFF 80 13 | COMMENSURATE 14 | &END MGRID 15 | &POISSON 16 | PERIODIC XY 17 | POISSON_SOLVER MT 18 | &END POISSON 19 | &SCF 20 | SCF_GUESS ATOMIC 21 | EPS_SCF 1.0E-8 22 | MAX_SCF 1000 23 | ADDED_MOS 2000 24 | CHOLESKY INVERSE 25 | &SMEAR ON 26 | METHOD FERMI_DIRAC 27 | ELECTRONIC_TEMPERATURE [K] 300 28 | &END SMEAR 29 | &DIAGONALIZATION 30 | ALGORITHM STANDARD 31 | &END DIAGONALIZATION 32 | &MIXING 33 | METHOD BROYDEN_MIXING 34 | ALPHA 0.1 35 | BETA 1.5 36 | NBROYDEN 8 37 | &END MIXING 38 | &PRINT 39 | &RESTART 40 | &END 41 | &END 42 | &END SCF 43 | &XC 44 | &XC_FUNCTIONAL PBE 45 | &END XC_FUNCTIONAL 46 | !The following HF section has no effect on the result of the calculation 47 | !it is only there because the relevant quantities are only available in RI-HFX 48 | &HF 49 | FRACTION 0.0 !we do not want any HF 50 | &INTERACTION_POTENTIAL 51 | !this is completely irrelevant, except that we need a short range potential for the calculation 52 | !to go through in PBCs, so IDENTITY (=overlap) is fine 53 | POTENTIAL_TYPE IDENTITY 54 | &END 55 | !This is the critical section 56 | &RI 57 | RI_METRIC IDENTITY !this is the metric used for the projection of the density (overlap here) 58 | MEMORY_CUT 1 59 | &PRINT 60 | !Subsection required to have the RI density coefficient printed (in demo-RI_DENSITY_COEFFS.dat) 61 | &RI_DENSITY_COEFFS 62 | !MULT_BY_S !uncomment to have the RI coefficients directly multiplied by the metric (overlap here) 63 | &END 64 | !To get the metric 2c integrals, use the following (in demo-RI_2C_INTS.fm), unformated 65 | &RI_METRIC_2C_INTS 66 | &END 67 | &END 68 | &END 69 | &END 70 | &END XC 71 | &END DFT 72 | &MM 73 | &FORCEFIELD 74 | &CHARGE 75 | ATOM Na 76 | CHARGE 1.000000 77 | &END CHARGE 78 | &CHARGE 79 | ATOM Au 80 | CHARGE 0.0 81 | &END CHARGE 82 | &NONBONDED 83 | &LENNARD-JONES 84 | atoms Na Na 85 | EPSILON 0.0 86 | SIGMA 3.166 87 | RCUT 11.4 88 | &END LENNARD-JONES 89 | &LENNARD-JONES 90 | atoms Na Au 91 | EPSILON 0.0 92 | SIGMA 3.6705 93 | RCUT 11.4 94 | &END LENNARD-JONES 95 | &LENNARD-JONES 96 | atoms Au Au 97 | EPSILON 0.0 98 | SIGMA 3.6705 99 | RCUT 11.4 100 | &END LENNARD-JONES 101 | &END NONBONDED 102 | &END FORCEFIELD 103 | &POISSON 104 | &EWALD 105 | EWALD_TYPE ewald 106 | ALPHA .44 107 | GMAX 21 108 | &END EWALD 109 | &END POISSON 110 | &END MM 111 | &QMMM 112 | MM_POTENTIAL_FILE_NAME MM_POTENTIAL 113 | USE_GEEP_LIB 12 114 | @include cell.sys 115 | ECOUPL GAUSS 116 | NOCOMPATIBILITY 117 | &PERIODIC 118 | &POISSON 119 | PERIODIC XY 120 | POISSON_SOLVER MT 121 | &END POISSON 122 | &MULTIPOLE OFF 123 | &END 124 | &END PERIODIC 125 | &MM_KIND Na 126 | RADIUS 2.27 !ionic radius 127 | &END MM_KIND 128 | &QM_KIND Au 129 | MM_INDEX 1..12 130 | &END QM_KIND 131 | &END QMMM 132 | &SUBSYS 133 | @include cell.sys 134 | @include coords.sys 135 | &KIND Au 136 | BASIS_SET ccGRB-D-q11 137 | BASIS_SET RI_HFX RI_AUTO_OPT-ccGRB 138 | POTENTIAL GTH-PBE-q11 139 | &END KIND 140 | &KIND Na 141 | BASIS_SET ccGRB-D-q11 142 | BASIS_SET RI_HFX RI_AUTO_OPT-ccGRB 143 | POTENTIAL GTH-PBE-q11 144 | &END KIND 145 | &TOPOLOGY 146 | &END TOPOLOGY 147 | &END SUBSYS 148 | &END FORCE_EVAL 149 | &GLOBAL 150 | PROJECT ${PROJECT} 151 | RUN_TYPE ${RUN} 152 | PRINT_LEVEL MEDIUM 153 | EXTENDED_FFT_LENGTHS 154 | &END GLOBAL 155 | -------------------------------------------------------------------------------- /example/Au100-Na_CP2K/inp.yaml: -------------------------------------------------------------------------------- 1 | # workflow label and root directory 2 | salted: 3 | saltedname: rc8.0_rho-sg0.5_V-sg0.5 4 | saltedpath: ./ 5 | 6 | # system general parameters 7 | system: 8 | filename: ./coords-qmmm.xyz 9 | species: [Au] 10 | parallel: False 11 | 12 | # quantum mechanical info 13 | qm: 14 | path2qm: ./ 15 | qmcode: cp2k 16 | periodic: 2D 17 | dfbasis: RI_AUTO_OPT-ccGRB-small 18 | coeffile: Au-RI_DENSITY_COEFFS.dat 19 | ovlpfile: Au-RI_2C_INTS.fm 20 | pseudocharge: 11.0 21 | 22 | # descriptor parameters 23 | descriptor: 24 | rep1: 25 | type: rho 26 | rcut: 8.0 27 | sig: 0.5 28 | nrad: 6 29 | nang: 6 30 | neighspe: [Au,Na] 31 | rep2: 32 | type: V 33 | rcut: 8.0 34 | sig: 0.5 35 | nrad: 6 36 | nang: 6 37 | neighspe: [Au,Na] 38 | 39 | # Gaussian process regression variables 40 | gpr: 41 | z: 2.0 42 | Menv: 50 43 | Ntrain: 32 44 | trainfrac: 1.0 45 | trainsel: sequential 46 | blocksize: 100 47 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS/README.rst: -------------------------------------------------------------------------------- 1 | Please refer to the following link for how to use SALTED in combination with FHI-AIMS: 2 | 3 | https://gitlab.com/FHI-aims-club/tutorials/fhi-aims-with-salted 4 | 5 | 6 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS/control.in: -------------------------------------------------------------------------------- 1 | ######################################################################################### 2 | # 3 | # Volker Blum, 2017 : Test run input file control.in for simple H2O 4 | # 5 | ######################################################################################### 6 | # 7 | # Physical model 8 | # 9 | xc pbe 10 | spin none 11 | relativistic none 12 | charge 0. 13 | k_grid 1 1 1 14 | ri_density_restart write 15 | ri_full_output .True. 16 | ri_output_density_only .True. 17 | 18 | default_max_l_prodbas 5 19 | default_prodbas_acc 1d-4 20 | wave_threshold 1e-8 21 | 22 | ################################################################################ 23 | 24 | ################################################################################ 25 | # 26 | # FHI-aims code project 27 | # VB, Fritz-Haber Institut, 2009 28 | # 29 | # Suggested "light" defaults for O atom (to be pasted into control.in file) 30 | # Be sure to double-check any results obtained with these settings for post-processing, 31 | # e.g., with the "tight" defaults and larger basis sets. 32 | # 33 | ################################################################################ 34 | species O 35 | # global species definitions 36 | nucleus 8 37 | mass 15.9994 38 | # 39 | l_hartree 6 40 | # 41 | cut_pot 4.0 2.0 1.0 42 | basis_dep_cutoff 1e-4 43 | # 44 | radial_base 36 7.0 45 | radial_multiplier 2 46 | angular_grids specified 47 | division 0.2659 50 48 | division 0.4451 110 49 | division 0.6052 194 50 | division 0.7543 302 51 | division 0.8014 434 52 | # division 0.8507 590 53 | # division 0.8762 770 54 | # division 0.9023 974 55 | # division 1.2339 1202 56 | # outer_grid 974 57 | outer_grid 434 58 | ################################################################################ 59 | # 60 | # Definition of "minimal" basis 61 | # 62 | ################################################################################ 63 | # valence basis states 64 | valence 2 s 2. 65 | valence 2 p 4. 66 | # ion occupancy 67 | ion_occ 2 s 1. 68 | ion_occ 2 p 3. 69 | ################################################################################ 70 | # 71 | # Suggested additional basis functions. For production calculations, 72 | # uncomment them one after another (the most important basis functions are 73 | # listed first). 74 | # 75 | # 76 | ################################################################################ 77 | # "First tier" - improvements: -699.05 meV to -159.38 meV 78 | hydro 2 p 1.8 79 | hydro 3 d 7.6 80 | hydro 3 s 6.4 81 | # "Second tier" - improvements: -49.91 meV to -5.39 meV 82 | hydro 4 f 11.6 83 | hydro 3 p 6.2 84 | hydro 3 d 5.6 85 | hydro 5 g 17.6 86 | hydro 1 s 0.75 87 | # "Third tier" - improvements: -2.83 meV to -0.50 meV 88 | # ionic 2 p auto 89 | # hydro 4 f 10.8 90 | # hydro 4 d 4.7 91 | # hydro 2 s 6.8 92 | # "Fourth tier" - improvements: -0.40 meV to -0.12 meV 93 | # hydro 3 p 5 94 | # hydro 3 s 3.3 95 | # hydro 5 g 15.6 96 | # hydro 4 f 17.6 97 | # hydro 4 d 14 98 | # Further basis functions - -0.08 meV and below 99 | # hydro 3 s 2.1 100 | # hydro 4 d 11.6 101 | # hydro 3 p 16 102 | # hydro 2 s 17.2 103 | ################################################################################ 104 | # 105 | # FHI-aims code project 106 | # VB, Fritz-Haber Institut, 2009 107 | # 108 | # Suggested "light" defaults for H atom (to be pasted into control.in file) 109 | # Be sure to double-check any results obtained with these settings for post-processing, 110 | # e.g., with the "tight" defaults and larger basis sets. 111 | # 112 | ################################################################################ 113 | species H 114 | # global species definitions 115 | nucleus 1 116 | mass 1.00794 117 | # 118 | l_hartree 6 119 | # 120 | cut_pot 4.0 2.0 1.0 121 | basis_dep_cutoff 1e-4 122 | # 123 | radial_base 24 7.0 124 | radial_multiplier 2 125 | angular_grids specified 126 | division 0.2421 50 127 | division 0.3822 110 128 | division 0.4799 194 129 | division 0.5341 302 130 | division 0.5626 434 131 | # division 0.5922 590 132 | # division 0.6542 770 133 | # division 0.6868 1202 134 | # outer_grid 770 135 | outer_grid 434 136 | ################################################################################ 137 | # 138 | # Definition of "minimal" basis 139 | # 140 | ################################################################################ 141 | # valence basis states 142 | valence 1 s 1. 143 | # ion occupancy 144 | ion_occ 1 s 0.5 145 | ################################################################################ 146 | # 147 | # Suggested additional basis functions. For production calculations, 148 | # uncomment them one after another (the most important basis functions are 149 | # listed first). 150 | # 151 | # Basis constructed for dimers: 0.5 A, 0.7 A, 1.0 A, 1.5 A, 2.5 A 152 | # 153 | ################################################################################ 154 | # "First tier" - improvements: -1014.90 meV to -62.69 meV 155 | hydro 2 s 2.1 156 | hydro 2 p 3.5 157 | # "Second tier" - improvements: -12.89 meV to -1.83 meV 158 | hydro 1 s 0.85 159 | hydro 2 p 3.7 160 | hydro 2 s 1.2 161 | hydro 3 d 7 162 | # "Third tier" - improvements: -0.25 meV to -0.12 meV 163 | # hydro 4 f 11.2 164 | # hydro 3 p 4.8 165 | # hydro 4 d 9 166 | # hydro 3 s 3.2 167 | # 168 | 169 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS/control_read.in: -------------------------------------------------------------------------------- 1 | ######################################################################################### 2 | # 3 | # Volker Blum, 2017 : Test run input file control.in for simple H2O 4 | # 5 | ######################################################################################### 6 | # 7 | # Physical model 8 | # 9 | xc pbe 10 | spin none 11 | relativistic none 12 | charge 0. 13 | # k_grid 1 1 1 14 | ri_density_restart read 0 15 | ri_full_output .True. 16 | 17 | default_max_l_prodbas 5 18 | default_prodbas_acc 1d-4 19 | 20 | ################################################################################ 21 | 22 | ################################################################################ 23 | # 24 | # FHI-aims code project 25 | # VB, Fritz-Haber Institut, 2009 26 | # 27 | # Suggested "light" defaults for O atom (to be pasted into control.in file) 28 | # Be sure to double-check any results obtained with these settings for post-processing, 29 | # e.g., with the "tight" defaults and larger basis sets. 30 | # 31 | ################################################################################ 32 | species O 33 | # global species definitions 34 | nucleus 8 35 | mass 15.9994 36 | # 37 | l_hartree 6 38 | # 39 | cut_pot 4.0 2.0 1.0 40 | basis_dep_cutoff 1e-4 41 | # 42 | radial_base 36 7.0 43 | radial_multiplier 2 44 | angular_grids specified 45 | division 0.2659 50 46 | division 0.4451 110 47 | division 0.6052 194 48 | division 0.7543 302 49 | division 0.8014 434 50 | # division 0.8507 590 51 | # division 0.8762 770 52 | # division 0.9023 974 53 | # division 1.2339 1202 54 | # outer_grid 974 55 | outer_grid 434 56 | ################################################################################ 57 | # 58 | # Definition of "minimal" basis 59 | # 60 | ################################################################################ 61 | # valence basis states 62 | valence 2 s 2. 63 | valence 2 p 4. 64 | # ion occupancy 65 | ion_occ 2 s 1. 66 | ion_occ 2 p 3. 67 | ################################################################################ 68 | # 69 | # Suggested additional basis functions. For production calculations, 70 | # uncomment them one after another (the most important basis functions are 71 | # listed first). 72 | # 73 | # 74 | ################################################################################ 75 | # "First tier" - improvements: -699.05 meV to -159.38 meV 76 | hydro 2 p 1.8 77 | hydro 3 d 7.6 78 | hydro 3 s 6.4 79 | # "Second tier" - improvements: -49.91 meV to -5.39 meV 80 | hydro 4 f 11.6 81 | hydro 3 p 6.2 82 | hydro 3 d 5.6 83 | hydro 5 g 17.6 84 | hydro 1 s 0.75 85 | # "Third tier" - improvements: -2.83 meV to -0.50 meV 86 | # ionic 2 p auto 87 | # hydro 4 f 10.8 88 | # hydro 4 d 4.7 89 | # hydro 2 s 6.8 90 | # "Fourth tier" - improvements: -0.40 meV to -0.12 meV 91 | # hydro 3 p 5 92 | # hydro 3 s 3.3 93 | # hydro 5 g 15.6 94 | # hydro 4 f 17.6 95 | # hydro 4 d 14 96 | # Further basis functions - -0.08 meV and below 97 | # hydro 3 s 2.1 98 | # hydro 4 d 11.6 99 | # hydro 3 p 16 100 | # hydro 2 s 17.2 101 | ################################################################################ 102 | # 103 | # FHI-aims code project 104 | # VB, Fritz-Haber Institut, 2009 105 | # 106 | # Suggested "light" defaults for H atom (to be pasted into control.in file) 107 | # Be sure to double-check any results obtained with these settings for post-processing, 108 | # e.g., with the "tight" defaults and larger basis sets. 109 | # 110 | ################################################################################ 111 | species H 112 | # global species definitions 113 | nucleus 1 114 | mass 1.00794 115 | # 116 | l_hartree 6 117 | # 118 | cut_pot 4.0 2.0 1.0 119 | basis_dep_cutoff 1e-4 120 | # 121 | radial_base 24 7.0 122 | radial_multiplier 2 123 | angular_grids specified 124 | division 0.2421 50 125 | division 0.3822 110 126 | division 0.4799 194 127 | division 0.5341 302 128 | division 0.5626 434 129 | # division 0.5922 590 130 | # division 0.6542 770 131 | # division 0.6868 1202 132 | # outer_grid 770 133 | outer_grid 434 134 | ################################################################################ 135 | # 136 | # Definition of "minimal" basis 137 | # 138 | ################################################################################ 139 | # valence basis states 140 | valence 1 s 1. 141 | # ion occupancy 142 | ion_occ 1 s 0.5 143 | ################################################################################ 144 | # 145 | # Suggested additional basis functions. For production calculations, 146 | # uncomment them one after another (the most important basis functions are 147 | # listed first). 148 | # 149 | # Basis constructed for dimers: 0.5 A, 0.7 A, 1.0 A, 1.5 A, 2.5 A 150 | # 151 | ################################################################################ 152 | # "First tier" - improvements: -1014.90 meV to -62.69 meV 153 | hydro 2 s 2.1 154 | hydro 2 p 3.5 155 | # "Second tier" - improvements: -12.89 meV to -1.83 meV 156 | hydro 1 s 0.85 157 | hydro 2 p 3.7 158 | hydro 2 s 1.2 159 | hydro 3 d 7 160 | # "Third tier" - improvements: -0.25 meV to -0.12 meV 161 | # hydro 4 f 11.2 162 | # hydro 3 p 4.8 163 | # hydro 4 d 9 164 | # hydro 3 s 3.2 165 | # 166 | 167 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS/control_read_setup.in: -------------------------------------------------------------------------------- 1 | ######################################################################################### 2 | # 3 | # Volker Blum, 2017 : Test run input file control.in for simple H2O 4 | # 5 | ######################################################################################### 6 | # 7 | # Physical model 8 | # 9 | xc pbe 10 | spin none 11 | relativistic none 12 | charge 0. 13 | # k_grid 1 1 1 14 | ri_density_restart read_init 15 | 16 | default_max_l_prodbas 5 17 | default_prodbas_acc 1d-4 18 | 19 | ################################################################################ 20 | 21 | ################################################################################ 22 | # 23 | # FHI-aims code project 24 | # VB, Fritz-Haber Institut, 2009 25 | # 26 | # Suggested "light" defaults for O atom (to be pasted into control.in file) 27 | # Be sure to double-check any results obtained with these settings for post-processing, 28 | # e.g., with the "tight" defaults and larger basis sets. 29 | # 30 | ################################################################################ 31 | species O 32 | # global species definitions 33 | nucleus 8 34 | mass 15.9994 35 | # 36 | l_hartree 6 37 | # 38 | cut_pot 4.0 2.0 1.0 39 | basis_dep_cutoff 1e-4 40 | # 41 | radial_base 36 7.0 42 | radial_multiplier 2 43 | angular_grids specified 44 | division 0.2659 50 45 | division 0.4451 110 46 | division 0.6052 194 47 | division 0.7543 302 48 | division 0.8014 434 49 | # division 0.8507 590 50 | # division 0.8762 770 51 | # division 0.9023 974 52 | # division 1.2339 1202 53 | # outer_grid 974 54 | outer_grid 434 55 | ################################################################################ 56 | # 57 | # Definition of "minimal" basis 58 | # 59 | ################################################################################ 60 | # valence basis states 61 | valence 2 s 2. 62 | valence 2 p 4. 63 | # ion occupancy 64 | ion_occ 2 s 1. 65 | ion_occ 2 p 3. 66 | ################################################################################ 67 | # 68 | # Suggested additional basis functions. For production calculations, 69 | # uncomment them one after another (the most important basis functions are 70 | # listed first). 71 | # 72 | # 73 | ################################################################################ 74 | # "First tier" - improvements: -699.05 meV to -159.38 meV 75 | hydro 2 p 1.8 76 | hydro 3 d 7.6 77 | hydro 3 s 6.4 78 | # "Second tier" - improvements: -49.91 meV to -5.39 meV 79 | hydro 4 f 11.6 80 | hydro 3 p 6.2 81 | hydro 3 d 5.6 82 | hydro 5 g 17.6 83 | hydro 1 s 0.75 84 | # "Third tier" - improvements: -2.83 meV to -0.50 meV 85 | # ionic 2 p auto 86 | # hydro 4 f 10.8 87 | # hydro 4 d 4.7 88 | # hydro 2 s 6.8 89 | # "Fourth tier" - improvements: -0.40 meV to -0.12 meV 90 | # hydro 3 p 5 91 | # hydro 3 s 3.3 92 | # hydro 5 g 15.6 93 | # hydro 4 f 17.6 94 | # hydro 4 d 14 95 | # Further basis functions - -0.08 meV and below 96 | # hydro 3 s 2.1 97 | # hydro 4 d 11.6 98 | # hydro 3 p 16 99 | # hydro 2 s 17.2 100 | ################################################################################ 101 | # 102 | # FHI-aims code project 103 | # VB, Fritz-Haber Institut, 2009 104 | # 105 | # Suggested "light" defaults for H atom (to be pasted into control.in file) 106 | # Be sure to double-check any results obtained with these settings for post-processing, 107 | # e.g., with the "tight" defaults and larger basis sets. 108 | # 109 | ################################################################################ 110 | species H 111 | # global species definitions 112 | nucleus 1 113 | mass 1.00794 114 | # 115 | l_hartree 6 116 | # 117 | cut_pot 4.0 2.0 1.0 118 | basis_dep_cutoff 1e-4 119 | # 120 | radial_base 24 7.0 121 | radial_multiplier 2 122 | angular_grids specified 123 | division 0.2421 50 124 | division 0.3822 110 125 | division 0.4799 194 126 | division 0.5341 302 127 | division 0.5626 434 128 | # division 0.5922 590 129 | # division 0.6542 770 130 | # division 0.6868 1202 131 | # outer_grid 770 132 | outer_grid 434 133 | ################################################################################ 134 | # 135 | # Definition of "minimal" basis 136 | # 137 | ################################################################################ 138 | # valence basis states 139 | valence 1 s 1. 140 | # ion occupancy 141 | ion_occ 1 s 0.5 142 | ################################################################################ 143 | # 144 | # Suggested additional basis functions. For production calculations, 145 | # uncomment them one after another (the most important basis functions are 146 | # listed first). 147 | # 148 | # Basis constructed for dimers: 0.5 A, 0.7 A, 1.0 A, 1.5 A, 2.5 A 149 | # 150 | ################################################################################ 151 | # "First tier" - improvements: -1014.90 meV to -62.69 meV 152 | hydro 2 s 2.1 153 | hydro 2 p 3.5 154 | # "Second tier" - improvements: -12.89 meV to -1.83 meV 155 | hydro 1 s 0.85 156 | hydro 2 p 3.7 157 | hydro 2 s 1.2 158 | hydro 3 d 7 159 | # "Third tier" - improvements: -0.25 meV to -0.12 meV 160 | # hydro 4 f 11.2 161 | # hydro 3 p 4.8 162 | # hydro 4 d 9 163 | # hydro 3 s 3.2 164 | # 165 | 166 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS/inp.yaml: -------------------------------------------------------------------------------- 1 | # workflow label and root directory 2 | salted: 3 | saltedname: test 4 | saltedpath: ./ 5 | 6 | # system general parameters 7 | system: 8 | filename: ./water_monomers_100.xyz 9 | species: [H, O] 10 | parallel: False 11 | 12 | # quantum mechanical info 13 | qm: 14 | path2qm: ./ 15 | qmcode: aims 16 | dfbasis: FHI-aims-clusters 17 | 18 | # prediction data 19 | prediction: 20 | filename: ./water_dimers_10.xyz 21 | predname: prediction 22 | predict_data: ./aims_pred_data 23 | 24 | # atomic environment parameters (for rascaline) 25 | descriptor: 26 | rep1: 27 | type: rho 28 | rcut: 4.0 29 | sig: 0.3 30 | nrad: 8 31 | nang: 6 32 | neighspe: [H, O] 33 | rep2: 34 | type: rho 35 | rcut: 4.0 36 | sig: 0.3 37 | nrad: 8 38 | nang: 6 39 | neighspe: [H, O] 40 | 41 | # Gaussian process regression variables 42 | gpr: 43 | z: 2.0 44 | Menv: 100 45 | Ntrain: 40 46 | trainfrac: 1.0 47 | blocksize: 10 48 | trainsel: "random" 49 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS/run-aims-predict-reorder.sbatch: -------------------------------------------------------------------------------- 1 | #! /bin/bash -l 2 | 3 | #SBATCH -o ./predict.out.%j 4 | #SBATCH -e ./predict.err.%j 5 | #SBATCH -J predict 6 | #SBATCH --partition=public 7 | #SBATCH --nodes=1 8 | #SBATCH --ntasks-per-node=16 9 | #SBATCH --mem-per-cpu=3800 10 | 11 | export OMP_NUM_THREADS=1 12 | ulimit -s unlimited 13 | 14 | DATADIR='qmdata/predicted_data' 15 | AIMS=~/aims.master.gnu.x 16 | 17 | mkdir $DATADIR 18 | 19 | python -m salted.aims.make_geoms --predict 20 | 21 | n=$(ls $DATADIR/geoms | grep -c 'in') 22 | 23 | for (( i=1; i<=$n; i++ )); do 24 | mkdir $DATADIR/$i 25 | cp control_read_setup.in ${DATADIR}/$i/control.in 26 | cp $DATADIR/geoms/$i.in $DATADIR/$i/geometry.in 27 | 28 | cd ${DATADIR}/$i 29 | srun --exclusive -n 1 $AIMS < /dev/null > temp.out 30 | cd - 31 | done 32 | 33 | wait 34 | 35 | python -m salted.aims.move_data_in_reorder 36 | 37 | for (( i=1; i<=$n; i++ )); do 38 | cp control_read.in ${DATADIR}/$i/control.in 39 | 40 | cd ${DATADIR}/$i 41 | 42 | mv ri_restart_coeffs_predicted.out ri_restart_coeffs.out 43 | srun --exclusive -n 1 $AIMS < /dev/null > aims_predict.out && mv rho_rebuilt_ri.out rho_ml.out && mv ri_restart_coeffs.out ri_restart_coeffs_ml.out & 44 | 45 | cd - 46 | done 47 | 48 | wait 49 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS/run-aims-predict.sbatch: -------------------------------------------------------------------------------- 1 | #! /bin/bash -l 2 | 3 | #SBATCH -o ./predict.out.%j 4 | #SBATCH -e ./predict.err.%j 5 | #SBATCH -J predict 6 | #SBATCH --partition=public 7 | #SBATCH --nodes=1 8 | #SBATCH --ntasks-per-node=16 9 | #SBATCH --mem-per-cpu=3800 10 | 11 | export OMP_NUM_THREADS=1 12 | ulimit -s unlimited 13 | 14 | DATADIR='qmdata/predicted_data' 15 | AIMS=~/aims.master.gnu.x 16 | 17 | mkdir $DATADIR 18 | 19 | python -m salted.aims.make_geoms --predict 20 | 21 | n=$(ls $DATADIR/geoms | grep -c 'in') 22 | 23 | for (( i=1; i<=$n; i++ )); do 24 | mkdir $DATADIR/$i 25 | cp control_read.in ${DATADIR}/$i/control.in 26 | cp $DATADIR/geoms/$i.in $DATADIR/$i/geometry.in 27 | cd - 28 | done 29 | 30 | wait 31 | 32 | python -m salted.aims.move_data_in 33 | 34 | for (( i=1; i<=$n; i++ )); do 35 | cd ${DATADIR}/$i 36 | mv ri_restart_coeffs_predicted.out ri_restart_coeffs.out 37 | srun --exclusive -n 1 $AIMS < /dev/null > aims_predict.out && mv rho_rebuilt_ri.out rho_ml.out && mv ri_restart_coeffs.out ri_restart_coeffs_ml.out & 38 | 39 | cd - 40 | done 41 | 42 | wait 43 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS/run-aims.sbatch: -------------------------------------------------------------------------------- 1 | #! /bin/bash -l 2 | 3 | #SBATCH -o ./gen_data.out.%j 4 | #SBATCH -e ./gen_data.err.%j 5 | #SBATCH -J gen_data 6 | #SBATCH --partition=XXXX 7 | #SBATCH --nodes=4 8 | #SBATCH --ntasks-per-node=16 9 | #SBATCH --mem-per-cpu=3800 10 | #SBATCH --cpus-per-task=1 11 | 12 | export OMP_NUM_THREADS=1 13 | ulimit -s unlimited 14 | 15 | QMDIR='qmdata/' 16 | AIMS=~/aims.master.mkl.x 17 | 18 | DATADIR=${QMDIR}data 19 | 20 | n=$(ls $DATADIR/geoms | grep -c 'in') 21 | 22 | for (( i=1; i<=$n; i++ )); do 23 | mkdir ${DATADIR}/$i 24 | cp control.in ${DATADIR}/$i 25 | cp ${DATADIR}/geoms/$i.in ${DATADIR}/$i/geometry.in 26 | cd ${DATADIR}/$i 27 | 28 | srun --exclusive -n 1 $AIMS < /dev/null > aims.out && mv rho_rebuilt_ri.out rho_df.out && mv ri_restart_coeffs.out ri_restart_coeffs_df.out & 29 | 30 | cd - 31 | done 32 | 33 | wait 34 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS/run-ml.sbatch: -------------------------------------------------------------------------------- 1 | #!/bin/bash -l 2 | # Standard output and error: 3 | #SBATCH -o ./ML-water.out 4 | ##SBATCH -e ./ML-setup.err 5 | # Initial working directory: 6 | ##SBATCH -D ./ 7 | # Job Name: 8 | #SBATCH -J ML-water 9 | # Queue (Partition): 10 | #SBATCH --partition=XXXX 11 | #SBATCH --nodes=1 12 | ##SBATCH --ntasks-per-node=4 13 | ##SBATCH --ntasks=32 14 | # for OpenMP: 15 | ##SBATCH --cpus-per-task=1 16 | # 17 | # Memory usage of the job [MB], 3800 MB per task: 18 | #SBATCH --mem-per-cpu=3800 19 | # 20 | #SBATCH --mail-type=none 21 | # 22 | # Wall clock limit: 23 | #SBATCH --time=8:00:00 24 | 25 | ### SET UP ENVIRONMENT VARIABLES: (uncomment and edit as needed) 26 | 27 | 28 | ### RUN YOUR CODE: 29 | 30 | python -m salted.get_basis_info 31 | python -m salted.initialize 32 | python -m salted.sparse_selection 33 | srun -n 16 python -m salted.sparse_descriptor 34 | 35 | python -m salted.rkhs_projector 36 | srun -n 16 python -m salted.rkhs_vector 37 | 38 | srun -n 16 python -m salted.hessian_matrix 39 | python -m salted.solve_regression 40 | srun -n 16 python -m salted.validation > validation.out 41 | 42 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS/water_dimers_10.xyz: -------------------------------------------------------------------------------- 1 | 6 2 | 3 | O -1.448231 -0.081379 -0.105305 4 | H -2.349368 -0.238126 0.154288 5 | H -0.920321 -0.870113 0.130561 6 | O -8.266929320802502 -2.341042516841843 -0.00028421402118565704 7 | H -7.334035320802503 -2.2343155168418427 -0.10325621402118565 8 | H -8.743914320802503 -1.5216505168418428 0.30555678597881436 9 | 6 10 | 11 | O 1.308677 -0.061683 -0.077667 12 | H 2.340557 -0.170448 0.241996 13 | H 1.279453 0.913258 0.023276 14 | O 8.486827629955043 1.0649779302103308 0.28930622645369747 15 | H 9.100163629955041 1.0056199302103308 0.9824172264536974 16 | H 9.009458629955041 1.006449930210331 -0.5068677735463025 17 | 6 18 | 19 | O -1.436475 0.007308 -0.081158 20 | H -1.273543 0.605991 -0.704585 21 | H -0.885895 0.167717 0.795994 22 | O -8.319075031526259 2.0908455599035407 -0.47403010037300913 23 | H -8.964200031526259 1.6040195599035407 -1.0067271003730092 24 | H -8.450736031526258 1.8567655599035406 0.49321489962699083 25 | 6 26 | 27 | O -1.376878 -0.007296 0.016908 28 | H -1.309553 0.419908 -0.823355 29 | H -1.076478 0.503867 0.678168 30 | O -8.325719323806974 2.3383487970677272 -0.10811253738857235 31 | H -8.598829323806973 1.9752257970677274 -0.9174885373885724 32 | H -8.529544323806974 1.8290127970677275 0.5829354626114276 33 | 6 34 | 35 | O -1.55773 -0.004156 0.049576 36 | H -1.487733 0.448778 -0.852129 37 | H -0.970713 0.463031 0.65099 38 | O -8.167161388575565 2.1289035370670355 0.044094007693886404 39 | H -8.367817388575565 1.7520255370670357 -0.8549559923061136 40 | H -8.870769388575566 1.7348755370670357 0.6456740076938864 41 | 6 42 | 43 | O -1.276934 -0.02905 -0.012972 44 | H -1.386544 0.562856 -0.773419 45 | H -1.313408 0.573499 0.729404 46 | O -8.333387448758744 2.6570453552258666 -0.15680694056552374 47 | H -8.504184448758744 2.1604503552258665 -0.8789999405655238 48 | H -8.205527448758744 2.0732593552258667 0.6219210594344762 49 | 6 50 | 51 | O -1.346976 -0.026594 0.039616 52 | H -1.366962 0.361552 -0.880903 53 | H -1.050459 0.579139 0.622652 54 | O -8.298008527645383 2.1785521398715555 -0.12868296149798286 55 | H -8.786513527645383 2.0197171398715557 -1.006903961497983 56 | H -8.712882527645382 1.7218061398715556 0.4903430385020171 57 | 6 58 | 59 | O -1.484872 0.02172 0.003656 60 | H -1.239773 0.471165 -0.757217 61 | H -0.848837 0.252188 0.742181 62 | O -8.328408842146587 2.093994952519581 0.00286879736356536 63 | H -8.469418842146588 1.5813689525195809 -0.7588772026364345 64 | H -8.858521842146587 1.799968952519581 0.7426337973635654 65 | 6 66 | 67 | O -1.633346 0.025042 0.009668 68 | H -0.899352 0.394495 -0.649576 69 | H -1.374678 0.397302 0.850931 70 | O -8.181342937068466 2.116557378592031 0.3702035727861667 71 | H -8.303163937068465 1.5629343785920309 -0.3840154272138333 72 | H -8.759776937068466 1.752001378592031 1.1194335727861668 73 | 6 74 | 75 | O 0.004454 0.09803 1.583859 76 | H 0.74712 -0.146956 0.971211 77 | H -0.740049 -0.307239 0.965109 78 | O -0.10757567447600846 -0.45437469214350495 8.539116526214325 79 | H -0.09577867447600846 -1.187761692143505 7.925768526214325 80 | H 0.06139832552399152 0.32860630785649503 7.994576526214325 81 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS_response/README: -------------------------------------------------------------------------------- 1 | Note that in this folder we are creating RI coeffs of the density response 2 | for a periodic calculation with DFPT dielectric. Change to DFPT polarizability 3 | for non-periodic systems. 4 | 5 | The workflow for the ML learning and prediction is then the same as for the other examples. 6 | Only the data generation and the calculation of derived quantities (reading in RI coefficients) 7 | is different in FHI-aims and the files in this folder cover these aspects. 8 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS_response/covariance_test.py: -------------------------------------------------------------------------------- 1 | from ase.io import read 2 | import numpy as np 3 | import spherical 4 | import quaternionic 5 | 6 | def complex_to_real_transformation(sizes): 7 | """Transformation matrix from complex to real spherical harmonics""" 8 | matrices = [] 9 | for i in range(len(sizes)): 10 | lval = int((sizes[i]-1)/2) 11 | st = (-1.0)**(lval+1) 12 | transformation_matrix = np.zeros((sizes[i],sizes[i]),dtype=complex) 13 | for j in range(lval): 14 | transformation_matrix[j][j] = 1.0j 15 | transformation_matrix[j][sizes[i]-j-1] = st*1.0j 16 | transformation_matrix[sizes[i]-j-1][j] = 1.0 17 | transformation_matrix[sizes[i]-j-1][sizes[i]-j-1] = st*-1.0 18 | st = st * -1.0 19 | transformation_matrix[lval][lval] = np.sqrt(2.0) 20 | transformation_matrix /= np.sqrt(2.0) 21 | matrices.append(transformation_matrix) 22 | return matrices 23 | 24 | f = read("water_monomers_1k.xyz",":") 25 | 26 | itest = 10 27 | 28 | # shift coordinates about position center 29 | coords_1 = f[0].get_positions() 30 | coords_2 = f[itest].get_positions() 31 | center_1 = np.mean(coords_1,axis=0) 32 | center_2 = np.mean(coords_2,axis=0) 33 | coords_1 -= center_1 34 | coords_2 -= center_2 35 | 36 | # compute OH distance vectors 37 | d1_OH1 = coords_1[1]-coords_1[0] 38 | d1_OH2 = coords_1[2]-coords_1[0] 39 | d2_OH1 = coords_2[1]-coords_2[0] 40 | d2_OH2 = coords_2[2]-coords_2[0] 41 | 42 | # Define dipole unit vectors 43 | v1 = (d1_OH1+d1_OH2)/np.linalg.norm(d1_OH1+d1_OH2) 44 | v2 = (d2_OH1+d2_OH2)/np.linalg.norm(d2_OH1+d2_OH2) 45 | 46 | # Compute alignement matrix of v2 onto v1 47 | v = np.cross(v2,v1) 48 | c = np.dot(v2,v1) 49 | 50 | vmat = np.zeros((3,3)) 51 | vmat[0,1] = -v[2] 52 | vmat[0,2] = v[1] 53 | vmat[1,0] = v[2] 54 | vmat[1,2] = -v[0] 55 | vmat[2,0] = -v[1] 56 | vmat[2,1] = v[0] 57 | 58 | vmat2 = np.dot(vmat,vmat) 59 | 60 | rmat1 = np.eye(3) + vmat + vmat2 / (1+c) 61 | 62 | rot_coords_2 = np.zeros((3,3)) 63 | 64 | # rotate coordinates 65 | for i in range(3): 66 | rot_coords_2[i] = np.dot(rmat1,coords_2[i]) 67 | 68 | # compute HH distance vectors of rotated molecules 69 | d1_HH = coords_1[2]-coords_1[1] 70 | d2_HH = rot_coords_2[2]-rot_coords_2[1] 71 | 72 | # define unit vectors 73 | v1 = d1_HH/np.linalg.norm(d1_HH) 74 | v2 = d2_HH/np.linalg.norm(d2_HH) 75 | 76 | # Compute alignement matrix of v2 onto v1 77 | v = np.cross(v2,v1) 78 | c = np.dot(v2,v1) 79 | 80 | vmat = np.zeros((3,3)) 81 | vmat[0,1] = -v[2] 82 | vmat[0,2] = v[1] 83 | vmat[1,0] = v[2] 84 | vmat[1,2] = -v[0] 85 | vmat[2,0] = -v[1] 86 | vmat[2,1] = v[0] 87 | 88 | vmat2 = np.dot(vmat,vmat) 89 | 90 | rmat2 = np.eye(3) + vmat + vmat2 / (1+c) 91 | 92 | # rotate coordinates 93 | for i in range(3): 94 | coords_2[i] = np.dot(rmat2,rot_coords_2[i]) 95 | 96 | # check alignement 97 | #print(coords_2-coords_1) 98 | 99 | # compute global rotation matrix 100 | rmat = np.dot(rmat2,rmat1) 101 | 102 | # compute quaternionic representation of rotation 103 | R = quaternionic.array.from_rotation_matrix(rmat) 104 | 105 | # compute Wigner-D matrices up to lmax 106 | lmax = 1 107 | wigner = spherical.Wigner(lmax) 108 | wigner_D = wigner.D(R) 109 | 110 | # select Wigner-D matrix for the given L 111 | L = 1 112 | msize = 2*L+1 113 | D = wigner_D[1:].reshape(msize,msize) 114 | 115 | # compute complex to real transformation 116 | c2r = complex_to_real_transformation([msize])[0] 117 | 118 | # make Wigner-D matrix real 119 | D_real = np.real(np.dot(c2r,np.dot(D,np.conj(c2r.T)))) 120 | np.save("Dreal_L1_from_"+str(itest)+"_to_0.npy",D_real) 121 | 122 | #cvec_1 = np.load("coefficients/coefficients_conf0.npy")[10:13] 123 | #cvec_2 = np.load("coefficients/coefficients_conf"+str(itest)+".npy")[10:13] 124 | #print(cvec_1) 125 | #print(np.dot(D_real,cvec_2)) 126 | 127 | path2qm = "./" 128 | 129 | # L=0 covariance test 130 | print("L=0 : ") 131 | 132 | cvec_1 = np.zeros(3) 133 | cvec_1[0] = np.load(path2qm+"coefficients/x/coefficients_conf0.npy")[0] 134 | cvec_1[1] = np.load(path2qm+"coefficients/y/coefficients_conf0.npy")[0] 135 | cvec_1[2] = np.load(path2qm+"coefficients/z/coefficients_conf0.npy")[0] 136 | 137 | cvec_2 = np.zeros(3) 138 | cvec_2[0] = np.load(path2qm+"coefficients/x/coefficients_conf1.npy")[0] 139 | cvec_2[1] = np.load(path2qm+"coefficients/y/coefficients_conf1.npy")[0] 140 | cvec_2[2] = np.load(path2qm+"coefficients/z/coefficients_conf1.npy")[0] 141 | 142 | print(cvec_1) 143 | print(np.dot(rmat,cvec_2)) 144 | print("error:") 145 | print(np.dot(rmat,cvec_2)-cvec_1) 146 | 147 | print("") 148 | 149 | # L=1 covariance test 150 | print("L=1 : ") 151 | 152 | cvec_1 = np.zeros((3,3)) 153 | cvec_1[0,:] = np.load(path2qm+"coefficients/x/coefficients_conf0.npy")[9:12] 154 | cvec_1[1,:] = np.load(path2qm+"coefficients/y/coefficients_conf0.npy")[9:12] 155 | cvec_1[2,:] = np.load(path2qm+"coefficients/z/coefficients_conf0.npy")[9:12] 156 | 157 | cvec_2 = np.zeros((3,3)) 158 | cvec_2[0,:] = np.load(path2qm+"coefficients/x/coefficients_conf1.npy")[9:12] 159 | cvec_2[1,:] = np.load(path2qm+"coefficients/y/coefficients_conf1.npy")[9:12] 160 | cvec_2[2,:] = np.load(path2qm+"coefficients/z/coefficients_conf1.npy")[9:12] 161 | 162 | print(cvec_1) 163 | print(np.dot(rmat,np.dot(cvec_2,D_real.T))) 164 | print("error:") 165 | print(np.dot(rmat,np.dot(cvec_2,D_real.T))-cvec_1) 166 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS_response/inp.yaml: -------------------------------------------------------------------------------- 1 | 2 | # workflow label and root directory 3 | salted: 4 | saltedname: test 5 | saltedpath: ./ 6 | saltedtype: 'density-response' 7 | 8 | # system general parameters 9 | system: 10 | filename: ./water_monomers_1k.xyz 11 | species: [H, O] 12 | parallel: False 13 | average: False 14 | 15 | # quantum mechanical info 16 | qm: 17 | path2qm: ./ 18 | qmcode: aims 19 | dfbasis: RI-aims 20 | 21 | # atomic environment parameters (for rascaline) 22 | descriptor: 23 | rep1: 24 | type: rho 25 | rcut: 4.0 26 | sig: 0.3 27 | nrad: 8 28 | nang: 6 29 | neighspe: [H, O] 30 | rep2: 31 | type: rho 32 | rcut: 4.0 33 | sig: 0.3 34 | nrad: 8 35 | nang: 6 36 | neighspe: [H, O] 37 | 38 | # Gaussian process regression variables 39 | gpr: 40 | z: 2.0 41 | Menv: 100 42 | Ntrain: 100 43 | trainfrac: 1.0 44 | trainsel: "random" 45 | regul: 1e-8 46 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS_response/move_data.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | import inp 4 | from mpi4py import MPI 5 | from ase.io import read 6 | 7 | # MPI information 8 | comm = MPI.COMM_WORLD 9 | size = comm.Get_size() 10 | rank = comm.Get_rank() 11 | print('This is task',rank+1,'of',size,flush=True) 12 | 13 | if (rank == 0): 14 | if not os.path.exists(inp.path2qm+inp.ovlpdir): 15 | os.mkdir(inp.path2qm+inp.ovlpdir) 16 | if not os.path.exists(inp.path2qm+'coefficients/'): 17 | os.mkdir(inp.path2qm+'coefficients/') 18 | if not os.path.exists(inp.path2qm+'projections/'): 19 | os.mkdir(inp.path2qm+'projections/') 20 | 21 | xyzfile = read(inp.filename,":") 22 | ndata = len(xyzfile) 23 | 24 | # Distribute structures to tasks 25 | if rank == 0: 26 | conf_range = [[] for _ in range(size)] 27 | blocksize = int(round(ndata/float(size))) 28 | for i in range(size): 29 | if i == (size-1): 30 | conf_range[i] = list(range(ndata))[i*blocksize:ndata] 31 | else: 32 | conf_range[i] = list(range(ndata))[i*blocksize:(i+1)*blocksize] 33 | else: 34 | conf_range = None 35 | conf_range = comm.scatter(conf_range,root=0) 36 | 37 | for i in conf_range: 38 | dirpath = inp.path2qm+'data/'+str(i+1)+'/' 39 | #idx = np.loadtxt(dirpath+'idx_prodbas.out').astype(int) 40 | #cs_list = np.loadtxt(dirpath+'prodbas_condon_shotley_list.out').astype(int) 41 | #idx -= 1 42 | #cs_list -= 1 43 | #idx = list(idx) 44 | #cs_list = list(cs_list) 45 | o = np.loadtxt(dirpath+'ri_projections.out').reshape(-1) 46 | t = np.loadtxt(dirpath+'ri_restart_coeffs.out').reshape(-1) 47 | ovlp = np.loadtxt(dirpath+'ri_ovlp.out').reshape(-1) 48 | 49 | n = len(o) 50 | ovlp = ovlp.reshape(n,n) 51 | 52 | #for j in cs_list: 53 | # ovlp[j,:] *= -1 54 | # ovlp[:,j] *= -1 55 | # o[j] *= -1 56 | # t[j] *= -1 57 | 58 | #o = o[idx] 59 | #t = t[idx] 60 | #ovlp = ovlp[idx,:] 61 | #ovlp = ovlp[:,idx] 62 | np.save(inp.path2qm+inp.ovlpdir+'overlap_conf'+str(i)+'.npy',ovlp) 63 | np.save(inp.path2qm+'projections/projections_conf'+str(i)+'.npy',o) 64 | np.save(inp.path2qm+'coefficients/coefficients_conf'+str(i)+'.npy',t) 65 | os.remove(dirpath+'ri_ovlp.out') 66 | os.remove(dirpath+'ri_projections.out') 67 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS_response/move_rho1_data.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | import inp 4 | from mpi4py import MPI 5 | from ase.io import read 6 | from sys import argv 7 | 8 | # MPI information 9 | comm = MPI.COMM_WORLD 10 | size = comm.Get_size() 11 | rank = comm.Get_rank() 12 | print('This is task',rank+1,'of',size,flush=True) 13 | #rank = 0 14 | 15 | if (rank == 0): 16 | if not os.path.exists(inp.path2qm+'coefficients_rho1'): 17 | os.mkdir(inp.path2qm+'coefficients_rho1') 18 | if not os.path.exists(inp.path2qm+'coefficients_rho1/x'): 19 | os.mkdir(inp.path2qm+'coefficients_rho1/x') 20 | if not os.path.exists(inp.path2qm+'coefficients_rho1/y'): 21 | os.mkdir(inp.path2qm+'coefficients_rho1/y') 22 | if not os.path.exists(inp.path2qm+'coefficients_rho1/z'): 23 | os.mkdir(inp.path2qm+'coefficients_rho1/z') 24 | if not os.path.exists(inp.path2qm+'projections_rho1'): 25 | os.mkdir(inp.path2qm+'projections_rho1') 26 | if not os.path.exists(inp.path2qm+'projections_rho1/x'): 27 | os.mkdir(inp.path2qm+'projections_rho1/x') 28 | if not os.path.exists(inp.path2qm+'projections_rho1/y'): 29 | os.mkdir(inp.path2qm+'projections_rho1/y') 30 | if not os.path.exists(inp.path2qm+'projections_rho1/z'): 31 | os.mkdir(inp.path2qm+'projections_rho1/z') 32 | 33 | xyzfile = read(inp.filename,":") 34 | ndata = len(xyzfile) 35 | 36 | # Distribute structures to tasks 37 | if rank == 0: 38 | conf_range = [[] for _ in range(size)] 39 | blocksize = int(round(ndata/float(size))) 40 | for i in range(size): 41 | if i == (size-1): 42 | conf_range[i] = list(range(ndata))[i*blocksize:ndata] 43 | else: 44 | conf_range[i] = list(range(ndata))[i*blocksize:(i+1)*blocksize] 45 | else: 46 | conf_range = None 47 | conf_range = comm.scatter(conf_range,root=0) 48 | 49 | for i in conf_range: 50 | dirpath = inp.path2qm+'data/'+str(i+1)+'/' 51 | #idx = np.loadtxt(dirpath+'idx_prodbas.out').astype(int) 52 | #cs_list = np.loadtxt(dirpath+'prodbas_condon_shotley_list.out').astype(int) 53 | #idx -= 1 54 | #cs_list -= 1 55 | #idx = list(idx) 56 | #cs_list = list(cs_list) 57 | o = np.loadtxt(dirpath+'ri_projections_rho1.out').transpose() 58 | t = [] 59 | t.append(np.loadtxt(dirpath+'ri_rho1_restart_coeffs_1.out')) 60 | t.append(np.loadtxt(dirpath+'ri_rho1_restart_coeffs_2.out')) 61 | t.append(np.loadtxt(dirpath+'ri_rho1_restart_coeffs_3.out')) 62 | t = np.array(t) 63 | 64 | n = len(o) 65 | 66 | #for j in cs_list: 67 | # o[:,j] *= -1 68 | # t[:,j] *= -1 69 | 70 | #o = o[:,idx] 71 | #t = t[:,idx] 72 | np.save(inp.path2qm+'projections_rho1/x/projections_conf'+str(i)+'.npy',o[0,:]) 73 | np.save(inp.path2qm+'projections_rho1/y/projections_conf'+str(i)+'.npy',o[1,:]) 74 | np.save(inp.path2qm+'projections_rho1/z/projections_conf'+str(i)+'.npy',o[2,:]) 75 | np.save(inp.path2qm+'coefficients_rho1/x/coefficients_conf'+str(i)+'.npy',t[0,:]) 76 | np.save(inp.path2qm+'coefficients_rho1/y/coefficients_conf'+str(i)+'.npy',t[1,:]) 77 | np.save(inp.path2qm+'coefficients_rho1/z/coefficients_conf'+str(i)+'.npy',t[2,:]) 78 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS_response/run-aims.sbatch: -------------------------------------------------------------------------------- 1 | #! /bin/bash -l 2 | 3 | #SBATCH -o ./gen_data.out.%j 4 | #SBATCH -e ./gen_data.err.%j 5 | #SBATCH -J gen_data 6 | #SBATCH --partition=p.ada 7 | #SBATCH --nodes=1 8 | #SBATCH --ntasks-per-node=72 9 | #SBATCH --mem-per-cpu=6000 10 | #SBATCH --time=1:00:00 11 | 12 | 13 | export OMP_NUM_THREADS=1 14 | ulimit -s unlimited 15 | #export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$INTEL_HOME/compilers_and_libraries/linux/lib/intel64/:$MKL_HOME/lib/intel64/:$HOME/.local/lib 16 | 17 | QMDIR='/ada/ptmp/mpsd/alewis/water_aligned/qmdata/' 18 | AIMS=~/aims.x 19 | 20 | DATADIR=${QMDIR}data 21 | 22 | python make_geoms.py 23 | 24 | n=$(ls $DATADIR/geoms | grep -c 'in') 25 | for (( i=1; i<=$n; i++ )); do 26 | let j=$i-1 27 | mkdir ${DATADIR}/$i 28 | cp control.in ${DATADIR}/$i 29 | cp ${DATADIR}/geoms/$i.in ${DATADIR}/$i/geometry.in 30 | cd ${DATADIR}/$i 31 | 32 | srun --exclusive -n 16 $AIMS < /dev/null > aims.out && mv rho_rebuilt_ri.out rho_df.out && mv ri_restart_coeffs.out ri_restart_coeffs_df.out & 33 | 34 | cd - 35 | done 36 | 37 | wait 38 | 39 | srun -n 72 python move_data.py 40 | srun -n 72 python move_rho1_data.py 41 | -------------------------------------------------------------------------------- /example/water_monomer_AIMS_response/run_test_serial.sh: -------------------------------------------------------------------------------- 1 | python3 -m salted.initialize 2 | python3 -m salted.sparse_selection 3 | python3 -m salted.sparse_descriptor 4 | python3 -m salted.rkhs_projector 5 | python3 -m salted.rkhs_vector 6 | python3 -m salted.hessian_matrix 7 | python3 -m salted.solve_regression 8 | python3 -m salted.validation 9 | -------------------------------------------------------------------------------- /example/water_monomer_PySCF/README.rst: -------------------------------------------------------------------------------- 1 | Generate training data using PySCF 2 | ---------------------------------- 3 | 4 | In what follows, we describe how to generate the training electron density data using the PySCF quantum-chemistry program. 5 | 6 | 1. The following input arguments must be added to the :code:`inp.qm` section: 7 | 8 | :code:`qmcode:`: define the quantum-mechanical code as :code:`pyscf` 9 | 10 | :code:`path2qm`: set the path where the PySCF data are going to be saved 11 | 12 | :code:`qmbasis`: define the wave function basis set for the Kohn-Sham calculation (example: :code:`cc-pvqz`) 13 | 14 | :code:`functional`: define the functional for the Kohn-Sham calculation (example: :code:`b3lyp`) 15 | 16 | 2. Define the auxiliary basis set using the input variable :code:`dfbasis`, as provided in the :code:`inp.qm` section. This must be chosen consistently with the wave function basis set (example: :code:`RI-cc-pvqz`). Then, add this basis set information to SALTED by running: 17 | 18 | :code:`python3 -m salted.get_basis_info` 19 | 20 | 3. Run PySCF to compute the Kohn-Sham density matrices: 21 | 22 | :code:`python3 -m salted.pyscf.run_pyscf` 23 | 24 | 4. From the computed density matrices, perform the density fitting on the selected auxiliary basis set by running: 25 | 26 | :code:`python3 -m salted.pyscf.dm2df` 27 | 28 | Indirect prediction of electrostatic energies 29 | --------------------------------------------- 30 | 31 | From the predicted density coefficients, it is possible to validate the model computing the electrostatic energy and compare it against the reference PySCF values. 32 | 33 | 1. Calculate the reference energies of the water molecules used for validation, by running: 34 | 35 | :code:`python3 -m salted.pyscf.electro_energy` 36 | 37 | 2. Calculate the energies derived from the predicted densities on the validation set and evaluate the error in kcal/mol, by running: 38 | 39 | :code:`python3 -m salted.pyscf.electro_error` 40 | -------------------------------------------------------------------------------- /example/water_monomer_PySCF/covariance_test.py: -------------------------------------------------------------------------------- 1 | from ase.io import read 2 | import numpy as np 3 | import spherical 4 | import quaternionic 5 | 6 | def complex_to_real_transformation(sizes): 7 | """Transformation matrix from complex to real spherical harmonics""" 8 | matrices = [] 9 | for i in range(len(sizes)): 10 | lval = int((sizes[i]-1)/2) 11 | st = (-1.0)**(lval+1) 12 | transformation_matrix = np.zeros((sizes[i],sizes[i]),dtype=complex) 13 | for j in range(lval): 14 | transformation_matrix[j][j] = 1.0j 15 | transformation_matrix[j][sizes[i]-j-1] = st*1.0j 16 | transformation_matrix[sizes[i]-j-1][j] = 1.0 17 | transformation_matrix[sizes[i]-j-1][sizes[i]-j-1] = st*-1.0 18 | st = st * -1.0 19 | transformation_matrix[lval][lval] = np.sqrt(2.0) 20 | transformation_matrix /= np.sqrt(2.0) 21 | matrices.append(transformation_matrix) 22 | return matrices 23 | 24 | f = read("water_monomers_1k.xyz",":") 25 | 26 | itest = 1 27 | 28 | # shift coordinates about position center 29 | coords_1 = f[0].get_positions() 30 | coords_2 = f[itest].get_positions() 31 | center_1 = np.mean(coords_1,axis=0) 32 | center_2 = np.mean(coords_2,axis=0) 33 | coords_1 -= center_1 34 | coords_2 -= center_2 35 | 36 | # compute OH distance vectors 37 | d1_OH1 = coords_1[1]-coords_1[0] 38 | d1_OH2 = coords_1[2]-coords_1[0] 39 | d2_OH1 = coords_2[1]-coords_2[0] 40 | d2_OH2 = coords_2[2]-coords_2[0] 41 | 42 | # Define dipole unit vectors 43 | v1 = (d1_OH1+d1_OH2)/np.linalg.norm(d1_OH1+d1_OH2) 44 | v2 = (d2_OH1+d2_OH2)/np.linalg.norm(d2_OH1+d2_OH2) 45 | 46 | # Compute alignement matrix of v2 onto v1 47 | v = np.cross(v2,v1) 48 | c = np.dot(v2,v1) 49 | 50 | vmat = np.zeros((3,3)) 51 | vmat[0,1] = -v[2] 52 | vmat[0,2] = v[1] 53 | vmat[1,0] = v[2] 54 | vmat[1,2] = -v[0] 55 | vmat[2,0] = -v[1] 56 | vmat[2,1] = v[0] 57 | 58 | vmat2 = np.dot(vmat,vmat) 59 | 60 | rmat1 = np.eye(3) + vmat + vmat2 / (1+c) 61 | 62 | rot_coords_2 = np.zeros((3,3)) 63 | 64 | # rotate coordinates 65 | for i in range(3): 66 | rot_coords_2[i] = np.dot(rmat1,coords_2[i]) 67 | 68 | # compute HH distance vectors of rotated molecules 69 | d1_HH = coords_1[2]-coords_1[1] 70 | d2_HH = rot_coords_2[2]-rot_coords_2[1] 71 | 72 | # define unit vectors 73 | v1 = d1_HH/np.linalg.norm(d1_HH) 74 | v2 = d2_HH/np.linalg.norm(d2_HH) 75 | 76 | # Compute alignement matrix of v2 onto v1 77 | v = np.cross(v2,v1) 78 | c = np.dot(v2,v1) 79 | 80 | vmat = np.zeros((3,3)) 81 | vmat[0,1] = -v[2] 82 | vmat[0,2] = v[1] 83 | vmat[1,0] = v[2] 84 | vmat[1,2] = -v[0] 85 | vmat[2,0] = -v[1] 86 | vmat[2,1] = v[0] 87 | 88 | vmat2 = np.dot(vmat,vmat) 89 | 90 | rmat2 = np.eye(3) + vmat + vmat2 / (1+c) 91 | 92 | # rotate coordinates 93 | for i in range(3): 94 | coords_2[i] = np.dot(rmat2,rot_coords_2[i]) 95 | 96 | # check alignement 97 | #print(coords_2-coords_1) 98 | 99 | # compute global rotation matrix 100 | rmat = np.dot(rmat2,rmat1) 101 | 102 | # compute quaternionic representation of rotation 103 | R = quaternionic.array.from_rotation_matrix(rmat) 104 | 105 | # compute Wigner-D matrices up to lmax 106 | lmax = 1 107 | wigner = spherical.Wigner(lmax) 108 | wigner_D = wigner.D(R) 109 | 110 | # select Wigner-D matrix for the give L 111 | L = 1 112 | msize = 2*L+1 113 | D = wigner_D[1:].reshape(msize,msize) 114 | 115 | # compute complex to real transformation 116 | c2r = complex_to_real_transformation([msize])[0] 117 | 118 | # make Wigner-D matrix real 119 | D_real = np.real(np.dot(c2r,np.dot(D,np.conj(c2r.T)))) 120 | 121 | cvec_1 = np.load("coefficients/coefficients_conf0.npy")[10:13] 122 | cvec_2 = np.load("coefficients/coefficients_conf"+str(itest)+".npy")[10:13] 123 | print(cvec_1) 124 | print(np.dot(D_real,cvec_2)) 125 | 126 | #path2qm = "/gpfsstore/rech/kln/ulo49cx/water_monomer/" 127 | # 128 | ## L=0 covariance test 129 | # 130 | #cvec_1 = np.zeros(3) 131 | #cvec_1[0] = np.load(path2qm+"coefficients/coefficients-x_conf0.npy")[0] 132 | #cvec_1[1] = np.load(path2qm+"coefficients/coefficients-y_conf0.npy")[0] 133 | #cvec_1[2] = np.load(path2qm+"coefficients/coefficients-z_conf0.npy")[0] 134 | # 135 | #cvec_2 = np.zeros(3) 136 | #cvec_2[0] = np.load(path2qm+"coefficients/coefficients-x_conf1.npy")[0] 137 | #cvec_2[1] = np.load(path2qm+"coefficients/coefficients-y_conf1.npy")[0] 138 | #cvec_2[2] = np.load(path2qm+"coefficients/coefficients-z_conf1.npy")[0] 139 | # 140 | #print(cvec_1) 141 | #print(np.dot(rmat,cvec_2)) 142 | # 143 | ## L=1 covariance test 144 | # 145 | #cvec_1 = np.zeros((3,3)) 146 | #cvec_1[0,:] = np.load(path2qm+"coefficients/coefficients-x_conf0.npy")[9:12] 147 | #cvec_1[1,:] = np.load(path2qm+"coefficients/coefficients-y_conf0.npy")[9:12] 148 | #cvec_1[2,:] = np.load(path2qm+"coefficients/coefficients-z_conf0.npy")[9:12] 149 | # 150 | #cvec_2 = np.zeros((3,3)) 151 | #cvec_2[0,:] = np.load(path2qm+"coefficients/coefficients-x_conf1.npy")[9:12] 152 | #cvec_2[1,:] = np.load(path2qm+"coefficients/coefficients-y_conf1.npy")[9:12] 153 | #cvec_2[2,:] = np.load(path2qm+"coefficients/coefficients-z_conf1.npy")[9:12] 154 | # 155 | #print(cvec_1) 156 | #print(np.dot(rmat,np.dot(cvec_2,D_real.T))) 157 | -------------------------------------------------------------------------------- /example/water_monomer_PySCF/inp.yaml: -------------------------------------------------------------------------------- 1 | # workflow label and root directory 2 | salted: 3 | saltedname: test 4 | saltedpath: ./ 5 | 6 | # system general parameters 7 | system: 8 | filename: ./water_total.xyz 9 | #filename: ./water_monomers_1k.xyz 10 | species: [H, O] 11 | parallel: False 12 | 13 | # quantum mechanical info 14 | qm: 15 | path2qm: ./ 16 | qmcode: pyscf 17 | dfbasis: RI-cc-pvqz 18 | qmbasis: cc-pvqz 19 | functional: b3lyp 20 | 21 | # prediction data 22 | prediction: 23 | filename: ./water_dimers_10.xyz 24 | predname: dimer 25 | 26 | # descriptor parameters 27 | descriptor: 28 | rep1: 29 | type: rho 30 | rcut: 4.0 31 | sig: 0.3 32 | nrad: 8 33 | nang: 6 34 | neighspe: [H, O] 35 | rep2: 36 | type: rho 37 | rcut: 4.0 38 | sig: 0.3 39 | nrad: 8 40 | nang: 6 41 | neighspe: [H, O] 42 | sparsify: 43 | nsamples: 100 44 | ncut: 1000 45 | 46 | # Gaussian process regression variables 47 | gpr: 48 | z: 2.0 49 | Menv: 100 50 | Ntrain: 1000 51 | trainfrac: 1.0 52 | trainsel: sequential 53 | #blocksize: 250 54 | -------------------------------------------------------------------------------- /example/water_monomer_PySCF/interface.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | import numpy as np 4 | from ase.io import read 5 | from salted import init_pred 6 | from salted import salted_prediction 7 | 8 | from salted.sys_utils import ParseConfig 9 | inp = ParseConfig().parse_input() 10 | 11 | # Initialize SALTED prediction 12 | lmax,nmax,lmax_max,weights,power_env_sparse,Mspe,Vmat,vfps,charge_integrals = init_pred.build() 13 | 14 | # do prediction for the given structure 15 | frames = read(inp.prediction.filename,":") 16 | for i in range(len(frames)): 17 | structure = frames[i] 18 | coefs = salted_prediction.build(lmax,nmax,lmax_max,weights,power_env_sparse,Mspe,Vmat,vfps,charge_integrals,structure) 19 | np.savetxt("dynamics/COEFFS-"+str(i+1)+".dat",coefs) 20 | -------------------------------------------------------------------------------- /example/water_monomer_PySCF/run_test_parallel.sh: -------------------------------------------------------------------------------- 1 | python3 -m salted.initialize 2 | python3 -m salted.sparse_selection 3 | mpirun -n 4 python3 -m salted.sparse_descriptor 4 | python3 -m salted.rkhs_projector 5 | mpirun -n 4 python3 -m salted.rkhs_vector 6 | mpirun -n 4 python3 -m salted.hessian_matrix 7 | python3 -m salted.solve_regression 8 | python3 -m salted.validation 9 | -------------------------------------------------------------------------------- /example/water_monomer_PySCF/run_test_serial.sh: -------------------------------------------------------------------------------- 1 | python3 -m salted.initialize 2 | python3 -m salted.sparse_selection 3 | python3 -m salted.sparse_descriptor 4 | python3 -m salted.rkhs_projector 5 | python3 -m salted.rkhs_vector 6 | python3 -m salted.hessian_matrix 7 | python3 -m salted.solve_regression 8 | python3 -m salted.validation 9 | -------------------------------------------------------------------------------- /example/water_monomer_PySCF/water_dimers_10.xyz: -------------------------------------------------------------------------------- 1 | 6 2 | 3 | O -1.448231 -0.081379 -0.105305 4 | H -2.349368 -0.238126 0.154288 5 | H -0.920321 -0.870113 0.130561 6 | O -8.266929320802502 -2.341042516841843 -0.00028421402118565704 7 | H -7.334035320802503 -2.2343155168418427 -0.10325621402118565 8 | H -8.743914320802503 -1.5216505168418428 0.30555678597881436 9 | 6 10 | 11 | O 1.308677 -0.061683 -0.077667 12 | H 2.340557 -0.170448 0.241996 13 | H 1.279453 0.913258 0.023276 14 | O 8.486827629955043 1.0649779302103308 0.28930622645369747 15 | H 9.100163629955041 1.0056199302103308 0.9824172264536974 16 | H 9.009458629955041 1.006449930210331 -0.5068677735463025 17 | 6 18 | 19 | O -1.436475 0.007308 -0.081158 20 | H -1.273543 0.605991 -0.704585 21 | H -0.885895 0.167717 0.795994 22 | O -8.319075031526259 2.0908455599035407 -0.47403010037300913 23 | H -8.964200031526259 1.6040195599035407 -1.0067271003730092 24 | H -8.450736031526258 1.8567655599035406 0.49321489962699083 25 | 6 26 | 27 | O -1.376878 -0.007296 0.016908 28 | H -1.309553 0.419908 -0.823355 29 | H -1.076478 0.503867 0.678168 30 | O -8.325719323806974 2.3383487970677272 -0.10811253738857235 31 | H -8.598829323806973 1.9752257970677274 -0.9174885373885724 32 | H -8.529544323806974 1.8290127970677275 0.5829354626114276 33 | 6 34 | 35 | O -1.55773 -0.004156 0.049576 36 | H -1.487733 0.448778 -0.852129 37 | H -0.970713 0.463031 0.65099 38 | O -8.167161388575565 2.1289035370670355 0.044094007693886404 39 | H -8.367817388575565 1.7520255370670357 -0.8549559923061136 40 | H -8.870769388575566 1.7348755370670357 0.6456740076938864 41 | 6 42 | 43 | O -1.276934 -0.02905 -0.012972 44 | H -1.386544 0.562856 -0.773419 45 | H -1.313408 0.573499 0.729404 46 | O -8.333387448758744 2.6570453552258666 -0.15680694056552374 47 | H -8.504184448758744 2.1604503552258665 -0.8789999405655238 48 | H -8.205527448758744 2.0732593552258667 0.6219210594344762 49 | 6 50 | 51 | O -1.346976 -0.026594 0.039616 52 | H -1.366962 0.361552 -0.880903 53 | H -1.050459 0.579139 0.622652 54 | O -8.298008527645383 2.1785521398715555 -0.12868296149798286 55 | H -8.786513527645383 2.0197171398715557 -1.006903961497983 56 | H -8.712882527645382 1.7218061398715556 0.4903430385020171 57 | 6 58 | 59 | O -1.484872 0.02172 0.003656 60 | H -1.239773 0.471165 -0.757217 61 | H -0.848837 0.252188 0.742181 62 | O -8.328408842146587 2.093994952519581 0.00286879736356536 63 | H -8.469418842146588 1.5813689525195809 -0.7588772026364345 64 | H -8.858521842146587 1.799968952519581 0.7426337973635654 65 | 6 66 | 67 | O -1.633346 0.025042 0.009668 68 | H -0.899352 0.394495 -0.649576 69 | H -1.374678 0.397302 0.850931 70 | O -8.181342937068466 2.116557378592031 0.3702035727861667 71 | H -8.303163937068465 1.5629343785920309 -0.3840154272138333 72 | H -8.759776937068466 1.752001378592031 1.1194335727861668 73 | 6 74 | 75 | O 0.004454 0.09803 1.583859 76 | H 0.74712 -0.146956 0.971211 77 | H -0.740049 -0.307239 0.965109 78 | O -0.10757567447600846 -0.45437469214350495 8.539116526214325 79 | H -0.09577867447600846 -1.187761692143505 7.925768526214325 80 | H 0.06139832552399152 0.32860630785649503 7.994576526214325 81 | -------------------------------------------------------------------------------- /mkdocs.yaml: -------------------------------------------------------------------------------- 1 | site_name: "SALTED: Symmetry-Adapted Learning of Three-dimensional Electron Densities" 2 | 3 | theme: 4 | name: readthedocs 5 | highlightjs: true 6 | # logo: assets/SALTED_icon_vanilla_long.png 7 | # favicon: assets/SALTED_icon_vanilla_long.png 8 | 9 | site_url: https://github.com/andreagrisafi/SALTED 10 | 11 | nav: 12 | - Home: index.md 13 | - Installation: installation.md 14 | - Theory: theory.md 15 | - Workflow: workflow.md 16 | - Input: input.md 17 | - Tutorial: 18 | - Part 1 - Dataset: tutorial/dataset.md 19 | - Part 2 - Training: tutorial/training.md 20 | # - Part 3 - Predict Properties: tutorial/predict.md 21 | # - Examples: 22 | # - Example 1 - Water & PySCF: examples/water_pyscf.md 23 | # - Example 2 - Water & FHI-aims: examples/water_aims.md 24 | # - Example 3 - Au slab & CP2K: examples/au_cp2k.md 25 | - API: api.md 26 | 27 | plugins: 28 | - search 29 | - mkdocstrings: 30 | handlers: 31 | # See: https://mkdocstrings.github.io/python/usage/ 32 | python: 33 | options: 34 | docstring_style: google 35 | show_root_heading: true 36 | show_source: false 37 | 38 | markdown_extensions: 39 | - markdown_include.include: 40 | base_path: . 41 | - admonition 42 | - pymdownx.arithmatex: 43 | generic: true 44 | - pymdownx.superfences 45 | - attr_list 46 | - footnotes 47 | - md_in_html 48 | - pymdownx.details 49 | - pymdownx.snippets 50 | 51 | extra_javascript: 52 | - javascripts/mathjax.js # mathjax support 53 | - https://polyfill.io/v3/polyfill.min.js?features=es6 54 | - https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js 55 | 56 | extra_css: 57 | - stylesheets/extra.css 58 | -------------------------------------------------------------------------------- /salted/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreagrisafi/SALTED/15e84a809b6b674793f19763d62acc2492a7b108/salted/__init__.py -------------------------------------------------------------------------------- /salted/aims/_deprecated_/get_basis_info_deprecated.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path as osp 3 | 4 | import numpy as np 5 | from ase.io import read 6 | 7 | from salted import basis 8 | import inp 9 | 10 | # read species 11 | spelist = inp.species 12 | 13 | xyzfile = read(inp.filename,":") 14 | 15 | done_list = [] 16 | 17 | dirpath = osp.join(inp.path2qm, 'data') 18 | 19 | afname = basis.__file__ 20 | 21 | n_list = {} 22 | l_list = {} 23 | for iconf in range(len(xyzfile)): 24 | natoms = len(xyzfile[iconf]) 25 | atomic_symbols = xyzfile[iconf].get_chemical_symbols() 26 | for iat in range(natoms): 27 | spe = atomic_symbols[iat] 28 | if spe not in done_list: 29 | f = open(osp.join(dirpath, str(iconf+1), 'basis_info.out')) 30 | l = 0 31 | while True: 32 | line = f.readline() 33 | if not line: 34 | f.close() 35 | break 36 | 37 | line = line.split() 38 | 39 | if line[0] == 'atom' and int(line[1])-1 == iat: 40 | read_atom = True 41 | continue 42 | 43 | if line[1] == 'atom' and int(line[2])-1 == iat: 44 | read_atom = False 45 | l_list[spe] = l 46 | f.close() 47 | break 48 | 49 | if read_atom: 50 | n_list[spe,l] = int(line[6]) 51 | l += 1 52 | 53 | 54 | done_list.append(spe) 55 | if sorted(done_list) == sorted(spelist): 56 | f = open('new_basis_entry','w+') 57 | g = open(afname,'a') 58 | f.write(' if basis=="'+inp.dfbasis+'":\n\n') 59 | g.write(' if basis=="'+inp.dfbasis+'":\n\n') 60 | for spe in spelist: 61 | f.write(' lmax["'+spe+'"] = '+str(l_list[spe]-1)+'\n') 62 | g.write(' lmax["'+spe+'"] = '+str(l_list[spe]-1)+'\n') 63 | f.write('\n') 64 | g.write('\n') 65 | for spe in spelist: 66 | for l in range(l_list[spe]): 67 | f.write(' nmax[("'+spe+'",'+str(l)+')] = '+str(n_list[spe,l])+'\n') 68 | g.write(' nmax[("'+spe+'",'+str(l)+')] = '+str(n_list[spe,l])+'\n') 69 | f.write('\n') 70 | g.write('\n') 71 | f.write(' return [lmax,nmax]\n\n') 72 | g.write(' return [lmax,nmax]\n\n') 73 | 74 | f.close() 75 | g.close() 76 | exit 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /salted/aims/collect_energies.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import numpy as np 5 | 6 | from salted.sys_utils import ParseConfig 7 | inp = ParseConfig().parse_input() 8 | 9 | dn = os.path.join(inp.qm.path2qm, inp.prediction.predict_data) 10 | l = os.listdir(os.path.join(dn, "geoms")) 11 | nfiles = len(l) 12 | testset = list(range(nfiles)) 13 | testset = [x+1 for x in testset] 14 | 15 | es = [] 16 | xcs = [] 17 | eles = [] 18 | n_atoms = [] 19 | 20 | 21 | for k,i in enumerate(testset): 22 | e = [] 23 | xc = [] 24 | har = [] 25 | ele = [] 26 | dirn = os.path.join(dn, str(i)) 27 | 28 | f1 = open(os.path.join(dirn, 'aims.out')) 29 | for line in f1: 30 | if line.find('| Number of atoms') != -1: 31 | n_atoms.append(line.split()[5]) 32 | elif line.find('| Electrostatic energy') != -1: 33 | ele.append(line.split()[6]) 34 | elif line.find('XC energy correction') != -1: 35 | xc.append(line.split()[7]) 36 | elif line.find('| Electronic free energy per atom') != -1: 37 | e.append(line.split()[7]) 38 | else: 39 | continue 40 | 41 | f1 = open(os.path.join(dirn, 'aims_predict.out')) 42 | for line in f1: 43 | if line.find('| Electrostatic energy') != -1: 44 | ele.append(line.split()[6]) 45 | elif line.find('XC energy correction') != -1: 46 | xc.append(line.split()[7]) 47 | elif line.find('| Electronic free energy per atom') != -1: 48 | e.append(line.split()[7]) 49 | else: 50 | continue 51 | 52 | es.append([]) 53 | xcs.append([]) 54 | eles.append([]) 55 | es[k].append(e[-2]) 56 | xcs[k].append(xc[-2]) 57 | eles[k].append(ele[-2]) 58 | es[k].append(e[-1]) 59 | xcs[k].append(xc[-1]) 60 | eles[k].append(ele[-1]) 61 | 62 | es = np.array(es,dtype = float) 63 | xcs = np.array(xcs,dtype = float) 64 | eles = np.array(eles,dtype = float) 65 | n_atoms = np.array(n_atoms,dtype = float) 66 | 67 | for i in range(2): 68 | xcs[:,i] /= n_atoms 69 | eles[:,i] /= n_atoms 70 | 71 | np.savetxt('predict_reference_electrostatic_energy.dat',np.vstack([eles[:,1],eles[:,0]]).T) 72 | np.savetxt('predict_reference_xc_energy.dat',np.vstack([xcs[:,1],xcs[:,0]]).T) 73 | np.savetxt('predict_reference_total_energy.dat',np.vstack([es[:,1],es[:,0]]).T) 74 | 75 | print('Mean absolute errors (eV/atom):') 76 | print('Electrostatic energy:',np.average(np.abs(eles[:,1]-eles[:,0]))) 77 | print('XC energy:',np.average(np.abs(xcs[:,1]-xcs[:,0]))) 78 | print('Total energy:',np.average(np.abs(es[:,1]-es[:,0]))) 79 | -------------------------------------------------------------------------------- /salted/aims/get_basis_info.py: -------------------------------------------------------------------------------- 1 | """Translate basis info from FHI-aims calculation to SALTED basis info""" 2 | import os 3 | from typing import Dict, List 4 | from ase.io import read 5 | 6 | from salted.basis_client import ( 7 | BasisClient, 8 | SpeciesBasisData, 9 | compare_species_basis_data, 10 | ) 11 | from salted.get_basis_info import get_parser 12 | from salted.sys_utils import ParseConfig 13 | 14 | 15 | def build(dryrun: bool = False, force_overwrite: bool = False): 16 | """Scheme: parse all basis_info.out one by one, 17 | update the basis_data dict, 18 | and write to the database when all species are recorded. 19 | """ 20 | inp = ParseConfig().parse_input() 21 | assert inp.qm.qmcode.lower() == "aims", f"{inp.qm.qmcode=}, but expected 'aims'" 22 | 23 | spe_set = set(inp.system.species) 24 | geoms_list = read(inp.system.filename, ":") 25 | qmdata_dpath = os.path.join(inp.qm.path2qm, "data") 26 | basis_data: Dict[str, SpeciesBasisData] = {} # hold all species basis data 27 | 28 | for iconf, geom in enumerate(geoms_list): 29 | """parse basis_info.out for this geometry""" 30 | basis_info_fpath = os.path.join(qmdata_dpath, str(iconf + 1), "basis_info.out") 31 | spe_basis_data_list = parse_file_basis_info(basis_info_fpath) 32 | chem_syms = geom.get_chemical_symbols() 33 | assert len(chem_syms) == len( 34 | spe_basis_data_list 35 | ), f"{len(chem_syms)=}, {len(spe_basis_data_list)=}, {basis_info_fpath=}" 36 | chem_syms_uniq_idxes = [chem_syms.index(spe) for spe in set(chem_syms)] 37 | chem_syms_uniq = [chem_syms[i] for i in chem_syms_uniq_idxes] 38 | spe_basis_data_list_uniq = [ 39 | spe_basis_data_list[i] for i in chem_syms_uniq_idxes 40 | ] 41 | 42 | """update/compare basis data for each species""" 43 | for spe, spe_basis_data in zip( 44 | chem_syms_uniq, spe_basis_data_list_uniq, strict=True 45 | ): 46 | if spe not in basis_data.keys(): 47 | basis_data[spe] = spe_basis_data 48 | else: 49 | if not compare_species_basis_data(basis_data[spe], spe_basis_data): 50 | raise ValueError( 51 | f"Species {spe} has inconsistent basis data: {basis_data[spe]} and {spe_basis_data}, " 52 | f"file: {basis_info_fpath}" 53 | ) 54 | 55 | """check if all species are recorded""" 56 | if set(basis_data.keys()) == spe_set: 57 | break 58 | 59 | """double check if all species are recorded""" 60 | if set(basis_data.keys()) != spe_set: 61 | raise ValueError( 62 | f"Not all species are recorded: {basis_data.keys()} vs {spe_set}" 63 | ) 64 | 65 | """write to the database""" 66 | if dryrun: 67 | print("Dryrun mode, not writing to the database") 68 | print(f"{basis_data=}") 69 | else: 70 | BasisClient().write(inp.qm.dfbasis, basis_data, force_overwrite) 71 | 72 | 73 | def parse_file_basis_info(basis_info_fpath: str) -> List[SpeciesBasisData]: 74 | """Parse basis_info.out by FHI-aims. 75 | Parse by str.strip().split(). 76 | 77 | File example: (might have multiple atoms, has a space at the beginning of each line) 78 | ```text 79 | atom 1 80 | For L = 0 there are 9 radial functions 81 | For L = 1 there are 10 radial functions 82 | For L = 2 there are 9 radial functions 83 | For L = 3 there are 8 radial functions 84 | For L = 4 there are 6 radial functions 85 | For L = 5 there are 4 radial functions 86 | For atom 1 max L = 5 87 | ``` 88 | 89 | Return: in the format of List[SpeciesBasisData] 90 | ```python 91 | [ 92 | { 93 | "lmax": 5, 94 | "nmax": [9, 10, 9, 8, 6, 4], 95 | }, 96 | ... # other atoms 97 | ] 98 | ``` 99 | """ 100 | 101 | with open(basis_info_fpath) as f: 102 | lines_raw = f.readlines() 103 | lines_raw = [i.strip().split() for i in lines_raw] 104 | 105 | """Step 1: further split the current list by the keyword `atom`""" 106 | boundary_1atom = [ 107 | idx for idx, line in enumerate(lines_raw) if line[0] == "atom" 108 | ] + [len(lines_raw)] 109 | lines_by_atoms = [ 110 | lines_raw[boundary_1atom[i] : boundary_1atom[i + 1]] 111 | for i in range(len(boundary_1atom) - 1) 112 | ] 113 | # if dryrun: 114 | # print(f"{boundary_1atom=}") 115 | # print(f"{lines_by_atoms=}") 116 | 117 | """Step 2: derive SpeciesBasisData and do some checks (to ensure the file is not corrupted)""" 118 | basis_data: List[SpeciesBasisData] = [] 119 | for lines_atom in lines_by_atoms: 120 | err_msg = f"{basis_info_fpath=}, {lines_atom=}" 121 | assert lines_atom[0][0] == "atom", err_msg # check the first line 122 | assert lines_atom[-1][0] == "For", err_msg # check the last line 123 | assert ( 124 | len(lines_atom) == int(lines_atom[-1][-1]) + 3 125 | ), err_msg # check the number of lines 126 | basis_spe_data = { 127 | "lmax": int(lines_atom[-1][-1]), 128 | "nmax": [int(i[6]) for i in lines_atom[1:-1]], 129 | } 130 | assert ( 131 | len(basis_spe_data["nmax"]) == basis_spe_data["lmax"] + 1 132 | ), f"{basis_info_fpath=}, {basis_spe_data=}" 133 | basis_data.append(basis_spe_data) 134 | 135 | return basis_data 136 | 137 | 138 | if __name__ == "__main__": 139 | print("Please call `python -m salted.get_basis_info` instead of this file") 140 | 141 | parser = get_parser() 142 | args = parser.parse_args() 143 | 144 | build(dryrun=args.dryrun, force_overwrite=args.force_overwrite) 145 | -------------------------------------------------------------------------------- /salted/aims/get_df_err.py: -------------------------------------------------------------------------------- 1 | """ 2 | Compare the density from DF and from SCF, output the real space electron density MAE 3 | TODO: parallelize the comparison code 4 | """ 5 | 6 | import time 7 | import sys 8 | import os.path as osp 9 | 10 | import numpy as np 11 | 12 | from salted.sys_utils import ParseConfig, read_system, sort_grid_data 13 | 14 | def main(): 15 | inp = ParseConfig().parse_input() 16 | spelist, lmax, nmax, llmax, nnmax, ndata, atomic_symbols, natoms, natmax = read_system() 17 | 18 | start_time = time.time() 19 | 20 | dirname = osp.join(inp.qm.path2qm, 'data') 21 | av_err = 0 22 | errs = np.zeros(ndata) 23 | g = open(osp.join(inp.salted.saltedpath, 'df_maes'),'w+') # stay in salted dir 24 | for i in range(1,ndata+1): 25 | dirn = osp.join(dirname, str(i)) 26 | # f = open(dirn+'rho_scf.out') 27 | # r_con = [float(line.split()[-1]) for line in f] 28 | # f = open(dirn+'rho_df.out') 29 | # r_ri = [float(line.split()[-1]) for line in f] 30 | # f = open(dirn+'partition_tab.out') 31 | # part = [float(line.split()[-1]) for line in f] 32 | 33 | r_con = np.loadtxt(osp.join(dirn, 'rho_scf.out')) 34 | r_ri = np.loadtxt(osp.join(dirn, 'rho_df.out')) 35 | part = np.loadtxt(osp.join(dirn, 'partition_tab.out')) 36 | # print("before sort by coord") 37 | # print(i, np.allclose(r_ri[:,:3], r_con[:,:3]), np.allclose(r_ri[:,:3], part[:,:3])) 38 | # print(i, np.linalg.norm(r_ri[:,:3] - r_con[:,:3]), np.linalg.norm(r_ri[:,:3] - part[:,:3])) 39 | r_con = sort_grid_data(r_con) 40 | r_ri = sort_grid_data(r_ri) 41 | part = sort_grid_data(part) 42 | # r_con.view('f8,f8,f8,f8').sort(order=['f0','f1','f2'],axis = 0) 43 | # r_ri.view('f8,f8,f8,f8').sort(order=['f0','f1','f2'],axis = 0) 44 | # part.view('f8,f8,f8,f8').sort(order=['f0','f1','f2'],axis = 0) 45 | # print("after sort by coord") 46 | # print(i, np.allclose(r_ri[:,:3], r_con[:,:3]), np.allclose(r_ri[:,:3], part[:,:3])) 47 | # print(i, np.linalg.norm(r_ri[:,:3] - r_con[:,:3]), np.linalg.norm(r_ri[:,:3] - part[:,:3])) 48 | 49 | err = np.abs(r_ri[:,3]-r_con[:,3]) 50 | norm = np.dot(r_con[:,3],part[:,3]) 51 | int_err = np.dot(err,part[:,3])*100/norm 52 | errs[i-1] = int_err 53 | g.write(str(i)+' '+str(int_err)+'\n') 54 | g.flush() 55 | 56 | g.close() 57 | av_err = np.average(errs) 58 | sem = np.std(errs)/np.sqrt(ndata) 59 | 60 | print('% MAE =', av_err) 61 | end_time = time.time() 62 | print(f"time_cost = {end_time - start_time:.2f} s") 63 | 64 | if __name__ == '__main__': 65 | main() 66 | -------------------------------------------------------------------------------- /salted/aims/get_ml_err.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import sys 4 | import os.path as osp 5 | 6 | import numpy as np 7 | 8 | from salted.sys_utils import ParseConfig, sort_grid_data, read_system 9 | 10 | 11 | def main(): 12 | inp = ParseConfig().parse_input() 13 | spelist, lmax, nmax, llmax, nnmax, ndata, atomic_symbols, natoms, natmax = read_system() 14 | 15 | start_time = time.time() 16 | 17 | dirname = osp.join(inp.qm.path2qm, inp.prediction.predict_data) 18 | av_err = 0 19 | errs = np.zeros(ndata) 20 | g = open('ml_maes', 'w+') 21 | for i in range(1,ndata+1): 22 | dirn = osp.join(dirname, str(i)) 23 | # f = open(dirn+'rho_scf.out') 24 | # r_con = [float(line.split()[-1]) for line in f] 25 | # f = open(dirn+'rho_df.out') 26 | # r_ri = [float(line.split()[-1]) for line in f] 27 | # f = open(dirn+'partition_tab.out') 28 | # part = [float(line.split()[-1]) for line in f] 29 | 30 | r_con = np.loadtxt(osp.join(dirn, 'rho_scf.out')) 31 | r_ri = np.loadtxt(osp.join(dirn, 'rho_ml.out')) 32 | part = np.loadtxt(osp.join(dirn, 'partition_tab.out')) 33 | # r_con.view('f8,f8,f8,f8').sort(order=['f0','f1','f2'],axis = 0) 34 | # r_ri.view('f8,f8,f8,f8').sort(order=['f0','f1','f2'],axis = 0) 35 | # part.view('f8,f8,f8,f8').sort(order=['f0','f1','f2'],axis = 0) 36 | r_con = sort_grid_data(r_con) 37 | r_ri = sort_grid_data(r_ri) 38 | part = sort_grid_data(part) 39 | 40 | err = np.abs(r_ri[:,3]-r_con[:,3]) 41 | norm = np.dot(r_con[:,3],part[:,3]) 42 | int_err = np.dot(err,part[:,3])*100/norm 43 | errs[i-1] = int_err 44 | g.write(str(i)+' '+str(int_err)+'\n') 45 | g.flush() 46 | 47 | g.close() 48 | av_err = np.average(errs) 49 | sem = np.std(errs)/np.sqrt(ndata) 50 | 51 | print('% MAE =', av_err) 52 | end_time = time.time() 53 | print(f"time_cost = {end_time - start_time:.2f} s") 54 | 55 | main() 56 | -------------------------------------------------------------------------------- /salted/aims/make_geoms.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | import os.path as osp 4 | 5 | from ase.io import read, write 6 | 7 | from salted.sys_utils import ParseConfig 8 | inp = ParseConfig().parse_input() 9 | 10 | def add_command_line_arguments_contraction(): 11 | parser = argparse.ArgumentParser() 12 | parser.add_argument("-pr", "--predict", action='store_true', help="Prepare geometries for a true prediction") 13 | args = parser.parse_args() 14 | return args 15 | 16 | args = add_command_line_arguments_contraction() 17 | predict = args.predict 18 | 19 | if predict: 20 | datadir = inp.prediction.predict_data 21 | fname = inp.prediction.filename 22 | else: 23 | datadir = "data/" 24 | fname = inp.system.filename 25 | 26 | dirpath = osp.join(inp.qm.path2qm, datadir, "geoms") 27 | if not osp.exists(dirpath): 28 | os.makedirs(dirpath) 29 | 30 | xyz_file = read(fname,":") 31 | n = len(xyz_file) 32 | for i in range(n): 33 | write(osp.join(dirpath, f"{i+1}.in"), xyz_file[i]) 34 | -------------------------------------------------------------------------------- /salted/aims/move_data.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path as osp 3 | 4 | import numpy as np 5 | from ase.io import read 6 | from salted.sys_utils import ParseConfig, get_conf_range 7 | 8 | def build(): 9 | inp = ParseConfig().parse_input() 10 | 11 | if inp.system.parallel: 12 | from mpi4py import MPI 13 | # MPI information 14 | comm = MPI.COMM_WORLD 15 | size = comm.Get_size() 16 | rank = comm.Get_rank() 17 | print('This is task',rank+1,'of',size,flush=True) 18 | else: 19 | rank = 0 20 | size = 1 21 | 22 | if (rank == 0): 23 | """check if all subdirectories exist, if not create them""" 24 | sub_dirs = [ 25 | osp.join(inp.salted.saltedpath, d) 26 | for d in ("overlaps", "coefficients", "projections") 27 | ] 28 | for sub_dir in sub_dirs: 29 | if not osp.exists(sub_dir): 30 | os.mkdir(sub_dir) 31 | 32 | xyzfile = read(inp.system.filename,":") 33 | ndata = len(xyzfile) 34 | 35 | # Distribute structures to tasks 36 | if inp.system.parallel: 37 | conf_range = get_conf_range(rank,size,ndata,list(range(ndata))) 38 | conf_range = comm.scatter(conf_range,root=0) 39 | else: 40 | conf_range = list(range(ndata)) 41 | 42 | def get_reorder_bool(dirpath): 43 | """Determine the version of FHI-aims used. 44 | If a version newer than 240403, coefficients are 45 | internally reordered on input/output, and the 46 | SALTED helper functions should not also reorder coefficients. 47 | 48 | Args: 49 | dirpath (string): directory containing AIMS outputs 50 | Returns: 51 | boolean: whether SALTED helper functions should reorder 52 | """ 53 | 54 | with open(osp.join(dirpath,'aims.out'),'r') as afile: 55 | for i,line in enumerate(afile): 56 | if i == 51: 57 | if line.split()[:2] == ['FHI-aims','version']: 58 | if int(line.split()[-1]) >= 240403: 59 | reorder = False 60 | else: 61 | reorder = True 62 | return reorder 63 | else: 64 | print('The aims.out file does not have the FHI-aims version listed on line 52 as expected') 65 | break 66 | elif i > 51: 67 | print('The aims.out file does not have the FHI-aims version listed on line 52 as expected') 68 | break 69 | else: 70 | print('The aims.out is very short; FHI-aims has not executed properly') 71 | 72 | for i in conf_range: 73 | 74 | dirpath = osp.join(inp.qm.path2qm, 'data', str(i+1)) 75 | reorder = get_reorder_bool(dirpath) 76 | 77 | o = np.loadtxt(osp.join(dirpath, 'ri_projections.out')).reshape(-1) 78 | t = np.loadtxt(osp.join(dirpath, 'ri_restart_coeffs_df.out')).reshape(-1) 79 | ovlp = np.loadtxt(osp.join(dirpath, 'ri_ovlp.out')).reshape(-1) 80 | 81 | n = len(o) 82 | ovlp = ovlp.reshape(n,n) 83 | 84 | if reorder: 85 | idx = np.loadtxt(osp.join(dirpath, 'idx_prodbas.out')).astype(int) 86 | cs_list = np.loadtxt(osp.join(dirpath, 'prodbas_condon_shotley_list.out')).astype(int) 87 | idx -= 1 88 | cs_list -= 1 89 | idx = list(idx) 90 | cs_list = list(cs_list) 91 | 92 | 93 | for j in cs_list: 94 | ovlp[j,:] *= -1 95 | ovlp[:,j] *= -1 96 | o[j] *= -1 97 | t[j] *= -1 98 | 99 | o = o[idx] 100 | t = t[idx] 101 | ovlp = ovlp[idx,:] 102 | ovlp = ovlp[:,idx] 103 | 104 | np.save(osp.join(inp.salted.saltedpath, "overlaps", f"overlap_conf{i}.npy"), ovlp) 105 | np.save(osp.join(inp.salted.saltedpath, "projections", f"projections_conf{i}.npy"), o) 106 | np.save(osp.join(inp.salted.saltedpath, "coefficients", f"coefficients_conf{i}.npy"), t) 107 | 108 | if size > 1: comm.Barrier() 109 | 110 | """delte ri basis overlap and proj coeffs files""" 111 | 112 | for i in conf_range: 113 | dirpath = osp.join(inp.qm.path2qm, 'data', str(i+1)) 114 | os.remove(osp.join(dirpath, 'ri_ovlp.out')) 115 | os.remove(osp.join(dirpath, 'ri_projections.out')) 116 | 117 | if __name__ == "__main__": 118 | build() 119 | -------------------------------------------------------------------------------- /salted/aims/move_data_in.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import numpy as np 5 | 6 | from salted.sys_utils import ParseConfig, get_conf_range, read_system 7 | 8 | 9 | def build(): 10 | inp = ParseConfig().parse_input() 11 | 12 | if inp.system.parallel: 13 | from mpi4py import MPI 14 | # MPI information 15 | comm = MPI.COMM_WORLD 16 | size = comm.Get_size() 17 | rank = comm.Get_rank() 18 | print('This is task',rank+1,'of',size,flush=True) 19 | else: 20 | rank = 0 21 | size = 1 22 | 23 | if rank == 0: print("WARNING! This script assumes you will use an AIMS version >= 240403 to read the predicted RI coefficients. If this is not true, please use move_data_in_reorder instead.") 24 | 25 | species, lmax, nmax, lmax_max, nnmax, ndata, atomic_symbols, natoms, natmax = read_system(filename=inp.prediction.filename,spelist = inp.system.species, dfbasis = inp.qm.dfbasis) 26 | 27 | pdir = f"predictions_{inp.salted.saltedname}_{inp.prediction.predname}" 28 | 29 | ntrain = int(inp.gpr.trainfrac*inp.gpr.Ntrain) 30 | 31 | # Distribute structures to tasks 32 | if inp.system.parallel: 33 | conf_range = get_conf_range(rank,size,ndata,list(range(ndata))) 34 | conf_range = comm.scatter(conf_range,root=0) 35 | else: 36 | conf_range = list(range(ndata)) 37 | 38 | for i in conf_range: 39 | print(f"processing {i+1}/{ndata} frame") 40 | t = np.loadtxt(os.path.join( 41 | inp.salted.saltedpath, pdir, 42 | f"M{inp.gpr.Menv}_zeta{inp.gpr.z}", f"N{ntrain}_reg{int(np.log10(inp.gpr.regul))}", 43 | f"COEFFS-{i+1}.dat", 44 | )) 45 | n = len(t) 46 | 47 | dirpath = os.path.join(inp.qm.path2qm, inp.prediction.predict_data, f"{i+1}") 48 | if not os.path.exists(dirpath): 49 | os.makedirs(dirpath, exist_ok=True) 50 | 51 | np.savetxt(os.path.join(dirpath, f"ri_restart_coeffs_predicted.out"), t) 52 | 53 | if __name__ == "__main__": 54 | build() 55 | -------------------------------------------------------------------------------- /salted/aims/move_data_in_reorder.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import numpy as np 5 | from salted.sys_utils import ParseConfig, read_system, get_conf_range 6 | 7 | def build(): 8 | inp = ParseConfig().parse_input() 9 | 10 | print("WARNING! This script assumes you will use an AIMS version < 240403 to read the predicted RI coefficients. If this is not true, please use move_data_in instead.") 11 | 12 | if inp.system.parallel: 13 | from mpi4py import MPI 14 | # MPI information 15 | comm = MPI.COMM_WORLD 16 | size = comm.Get_size() 17 | rank = comm.Get_rank() 18 | print('This is task',rank+1,'of',size,flush=True) 19 | else: 20 | rank = 0 21 | size = 1 22 | 23 | species, lmax, nmax, lmax_max, nnmax, ndata, atomic_symbols, natoms, natmax = read_system() 24 | 25 | pdir = f"predictions_{inp.salted.saltedname}_{inp.prediction.predname}" 26 | 27 | ntrain = int(inp.gpr.trainfrac * inp.gpr.Ntrain) 28 | 29 | # Distribute structures to tasks 30 | if inp.system.parallel: 31 | conf_range = get_conf_range(rank,size,ndata,list(range(ndata))) 32 | conf_range = comm.scatter(conf_range,root=0) 33 | else: 34 | conf_range = list(range(ndata)) 35 | 36 | for i in conf_range: 37 | print(f"processing {i+1}/{ndata} frame") 38 | t = np.load(os.path.join( 39 | inp.salted.saltedpath, pdir, 40 | f"M{inp.gpr.Menv}_zeta{inp.gpr.z}", f"N{ntrain}_reg{int(np.log10(inp.gpr.regul))}", 41 | f"prediction_conf{i}.npy", 42 | )) 43 | n = len(t) 44 | 45 | dirpath = os.path.join(inp.qm.path2qm, inp.prediction.predict_data, f"{i+1}") 46 | 47 | idx = np.loadtxt(os.path.join(dirpath, f"idx_prodbas.out")).astype(int) 48 | idx -= 1 49 | 50 | # accelerated method 51 | idx_rev = np.empty_like(idx) 52 | idx_rev[idx] = np.arange(len(idx)) 53 | 54 | cs_list = np.loadtxt(os.path.join( 55 | dirpath, f"prodbas_condon_shotley_list.out" 56 | )).astype(int) 57 | cs_list -= 1 58 | 59 | # accelerated method 60 | t = t[idx_rev] 61 | t[cs_list] *= -1 62 | 63 | np.savetxt(os.path.join(dirpath, f"ri_restart_coeffs_predicted.out"), t) 64 | 65 | if __name__ == "__main__": 66 | build() 67 | -------------------------------------------------------------------------------- /salted/basis.py: -------------------------------------------------------------------------------- 1 | from salted.basis_client import BasisClient 2 | 3 | 4 | def basiset(basis: str): 5 | """read basis data and return as the old format 6 | 7 | WARNING: Please use BasisClient() to read basis data instead of this function. 8 | See BasisClient docstring for more information. 9 | 10 | Return: 11 | (lmax, nmax), using the old format 12 | 13 | Old format: 14 | ```python 15 | lmax = { 16 | "H": 1, 17 | "O": 2, 18 | } 19 | nmax = { 20 | ("H", 0): 4, 21 | ("H", 1): 3, 22 | ("O", 0): 5, 23 | ("O", 1): 4, 24 | ("O", 2): 3, 25 | } 26 | ``` 27 | """ 28 | return BasisClient().read_as_old_format(basis) # use default basis data file 29 | -------------------------------------------------------------------------------- /salted/cp2k/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreagrisafi/SALTED/15e84a809b6b674793f19763d62acc2492a7b108/salted/cp2k/__init__.py -------------------------------------------------------------------------------- /salted/cp2k/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreagrisafi/SALTED/15e84a809b6b674793f19763d62acc2492a7b108/salted/cp2k/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /salted/cp2k/__pycache__/cp2k2salted.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreagrisafi/SALTED/15e84a809b6b674793f19763d62acc2492a7b108/salted/cp2k/__pycache__/cp2k2salted.cpython-39.pyc -------------------------------------------------------------------------------- /salted/cp2k/_deprecated_/alphas-cp2k.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import math 4 | import numpy as np 5 | from ase.io import read 6 | from itertools import islice 7 | import copy 8 | import argparse 9 | import ctypes 10 | import time 11 | 12 | from salted.sys_utils import ParseConfig 13 | inp = ParseConfig().parse_input() 14 | 15 | species = inp.system.species 16 | 17 | print("Reading AOs info...") 18 | laomax = {} 19 | naomax = {} 20 | npgf = {} 21 | aoalphas = {} 22 | contra = {} 23 | lmax = {} 24 | nmax = {} 25 | for spe in species: 26 | with open("BASIS_MOLOPT") as f: 27 | for line in f: 28 | if line.rstrip().split()[0] == spe and line.rstrip().split()[1] == inp.qm.qmbasis: 29 | line = list(islice(f, 2))[1] 30 | laomax[spe] = int(line.split()[2]) 31 | npgf[spe] = int(line.split()[3]) 32 | for l in range(laomax[spe]+1): 33 | naomax[(spe,l)] = int(line.split()[4+l]) 34 | contra[(spe,l)] = np.zeros((naomax[(spe,l)],npgf[spe])) 35 | lines = list(islice(f, npgf[spe])) 36 | aoalphas[spe] = np.zeros(npgf[spe]) 37 | for ipgf in range(npgf[spe]): 38 | line = lines[ipgf].split() 39 | aoalphas[spe][ipgf] = float(line[0]) 40 | icount = 0 41 | for l in range(laomax[spe]+1): 42 | for n in range(naomax[(spe,l)]): 43 | contra[(spe,l)][n,ipgf] = line[1+icount] 44 | icount += 1 45 | break 46 | nalphas = npgf[spe]*3 47 | alphamin = min(aoalphas[spe]) 48 | alphamax = max(aoalphas[spe]) 49 | alphamin -= alphamin/3 50 | alphamax += alphamax/3 51 | r=(alphamax/alphamin)**(1.0/float(nalphas-1)) 52 | alphas=np.zeros(nalphas) 53 | for i in range(nalphas): 54 | alphas[i] = 2*alphamin*r**i 55 | np.savetxt("alphas-"+str(spe)+".txt",-np.sort(-alphas)) 56 | -------------------------------------------------------------------------------- /salted/cp2k/_deprecated_/contracted/contract_coefs-cp2k.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import math 4 | import numpy as np 5 | from ase.io import read 6 | from scipy import special 7 | from itertools import islice 8 | from scipy.optimize import minimize 9 | import copy 10 | import argparse 11 | import time 12 | 13 | SALTEDPATHLIB = str(pathlib.Path(__file__).parent.resolve())+"/../" 14 | sys.path.append(SALTEDPATHLIB) 15 | import basis 16 | 17 | def add_command_line_arguments(parsetext): 18 | parser = argparse.ArgumentParser(description=parsetext,formatter_class=argparse.ArgumentDefaultsHelpFormatter) 19 | parser.add_argument("-iconf", "--confidx", type=int, default=1, help="Structure index") 20 | args = parser.parse_args() 21 | return args 22 | 23 | def set_variable_values(args): 24 | iconf = args.confidx 25 | return iconf 26 | 27 | args = add_command_line_arguments("") 28 | iconf = set_variable_values(args) 29 | 30 | #print("conf", iconf) 31 | iconf -= 1 # 0-based indexing 32 | 33 | bohr2angs = 0.529177210670 34 | 35 | sys.path.insert(0, './') 36 | import inp 37 | 38 | xyzfile = read(inp.filename,":") 39 | ndata = len(xyzfile) 40 | species = inp.species 41 | [lmax,nmax] = basis.basiset(inp.dfbasis) 42 | 43 | # get basis set info from CP2K BASIS_LRIGPW_AUXMOLOPT 44 | alphas = {} 45 | sigmas = {} 46 | for spe in species: 47 | for l in range(lmax[spe]+1): 48 | avals = np.loadtxt(spe+"-"+inp.dfbasis+"-alphas-L"+str(l)+".dat") 49 | if nmax[(spe,l)]==1: 50 | alphas[(spe,l,0)] = float(avals) 51 | sigmas[(spe,l,0)] = np.sqrt(0.5/alphas[(spe,l,0)]) # bohr 52 | else: 53 | for n in range(nmax[(spe,l)]): 54 | alphas[(spe,l,n)] = avals[n] 55 | sigmas[(spe,l,n)] = np.sqrt(0.5/alphas[(spe,l,n)]) # bohr 56 | 57 | # init geometry 58 | geom = xyzfile[iconf] 59 | geom.wrap() 60 | symbols = geom.get_chemical_symbols() 61 | valences = geom.get_atomic_numbers() 62 | coords = geom.get_positions()/bohr2angs 63 | cell = geom.get_cell()/bohr2angs 64 | natoms = len(coords) 65 | 66 | projector = {} 67 | ncut = {} 68 | normfact = {} 69 | for spe in species: 70 | for l in range(lmax[spe]+1): 71 | projector[(spe,l)] = np.load("contractions/contra_spe"+str(spe)+"_l"+str(l)+".npy") 72 | ncut[(spe,l)] = projector[(spe,l)].shape[-1] 73 | normfact[(spe,l)] = np.zeros(nmax[(spe,l)]) 74 | for n in range(nmax[(spe,l)]): 75 | inner = 0.5*special.gamma(l+1.5)*(sigmas[(spe,l,n)]**2)**(l+1.5) 76 | normfact[(spe,l)][n] = np.sqrt(inner) 77 | 78 | norm_projector = {} 79 | norm_primitive = {} 80 | nfact = {} 81 | for spe in species: 82 | for l in range(lmax[spe]+1): 83 | prefac = 2.0**l*(2.0/np.pi)**0.75 84 | expalpha = 0.25*float(2*l + 3) 85 | norm_projector[(spe,l)] = np.zeros((nmax[(spe,l)],ncut[(spe,l)])) 86 | norm_primitive[(spe,l)] = np.zeros(nmax[(spe,l)]) 87 | for n in range(nmax[(spe,l)]): 88 | norm_primitive[(spe,l)][n] = 1.0/(prefac*alphas[(spe,l,n)]**expalpha) 89 | norm_projector[(spe,l)][n] = projector[(spe,l)][n] * prefac*alphas[(spe,l,n)]**expalpha 90 | nfact[(spe,l)] = np.zeros(ncut[(spe,l)]) 91 | for n in range(ncut[(spe,l)]): 92 | nfact_temp = 0.0 93 | for ipgf1 in range(nmax[(spe,l)]): 94 | for ipgf2 in range(nmax[(spe,l)]): 95 | nfact_temp += norm_projector[(spe,l)][ipgf1,n] * norm_projector[(spe,l)][ipgf2,n] * 0.5 * special.gamma(l+1.5) / ( (alphas[(spe,l,ipgf1)] + alphas[(spe,l,ipgf2)])**(l+1.5) ) 96 | nfact[(spe,l)][n] = np.sqrt(nfact_temp) 97 | 98 | aux_projs = np.loadtxt(inp.path2qm+"conf_"+str(iconf+1)+"/Au-RI_DENSITY_COEFFS.dat") 99 | #aux_projs = np.loadtxt(inp.path2qm+"conf_"+str(iconf+1)+"/efield/Au-RI_DENSITY_COEFFS.dat") 100 | print("starting dimension:",len(aux_projs)) 101 | 102 | naux_proj = 0 103 | for iat in range(natoms): 104 | spe = symbols[iat] 105 | for l in range(lmax[spe]+1): 106 | naux_proj += ncut[(spe,l)]*(2*l+1) 107 | print("final dimension:",naux_proj,flush=True) 108 | 109 | # contract 110 | contr_proj = np.zeros(naux_proj) 111 | iaux = 0 112 | iaux_proj = 0 113 | for iat in range(natoms): 114 | spe = symbols[iat] 115 | for l in range(lmax[spe]+1): 116 | blocksize = nmax[(spe,l)]*(2*l+1) 117 | blocksize_proj = ncut[(spe,l)]*(2*l+1) 118 | proj_slice = aux_projs[iaux:iaux+blocksize].reshape(nmax[(spe,l)],2*l+1) 119 | contr_proj[iaux_proj:iaux_proj+blocksize_proj] = np.dot(projector[(spe,l)].T,proj_slice).reshape(blocksize_proj) 120 | iaux += blocksize 121 | iaux_proj += blocksize_proj 122 | 123 | # rescale for different CP2K normalization 124 | iaux = 0 125 | contra_coefs_renorm = np.zeros(naux_proj) 126 | for iat in range(natoms): 127 | spe = symbols[iat] 128 | for l in range(lmax[spe]+1): 129 | blocksize = ncut[(spe,l)]*(2*l+1) 130 | coefs_primit = np.einsum('a,ab->ab',norm_primitive[(spe,l)]/normfact[(spe,l)],np.dot(projector[(spe,l)],contr_proj[iaux:iaux+blocksize].reshape(ncut[(spe,l)],2*l+1))) 131 | contra_coefs_renorm[iaux:iaux+blocksize] = np.einsum('a,ab->ab',nfact[(spe,l)],np.dot(projector[(spe,l)].T,coefs_primit)).reshape(blocksize) 132 | iaux += blocksize 133 | 134 | np.savetxt(inp.path2qm+"conf_"+str(iconf+1)+"/Au-RI_DENSITY_COEFFS_CONTRACTED.dat",contra_coefs_renorm) 135 | #np.savetxt(inp.path2qm+"conf_"+str(iconf+1)+"/efield/Au-RI_DENSITY_COEFFS_CONTRACTED.dat",contra_coefs_renorm) 136 | -------------------------------------------------------------------------------- /salted/cp2k/_deprecated_/contracted/contract_projs-cp2k.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import math 4 | import numpy as np 5 | from ase.io import read 6 | from scipy import special 7 | from itertools import islice 8 | from scipy.optimize import minimize 9 | import copy 10 | import argparse 11 | import time 12 | 13 | import basis 14 | 15 | def add_command_line_arguments(parsetext): 16 | parser = argparse.ArgumentParser(description=parsetext,formatter_class=argparse.ArgumentDefaultsHelpFormatter) 17 | parser.add_argument("-iconf", "--confidx", type=int, default=1, help="Structure index") 18 | args = parser.parse_args() 19 | return args 20 | 21 | def set_variable_values(args): 22 | iconf = args.confidx 23 | return iconf 24 | 25 | args = add_command_line_arguments("") 26 | iconf = set_variable_values(args) 27 | 28 | #print("conf", iconf) 29 | iconf -= 1 # 0-based indexing 30 | 31 | bohr2angs = 0.529177210670 32 | 33 | sys.path.insert(0, './') 34 | import inp 35 | 36 | xyzfile = read(inp.filename,":") 37 | ndata = len(xyzfile) 38 | species = inp.species 39 | [lmax,nmax] = basis.basiset(inp.dfbasis) 40 | 41 | # get basis set info from CP2K BASIS_LRIGPW_AUXMOLOPT 42 | print("Reading auxiliary basis info...") 43 | alphas = {} 44 | sigmas = {} 45 | for spe in species: 46 | avals = np.loadtxt("alphas-"+str(spe)+".txt") 47 | for l in range(lmax[spe]+1): 48 | for n in range(nmax[(spe,l)]): 49 | alphas[(spe,l,n)] = avals[n] 50 | sigmas[(spe,l,n)] = np.sqrt(0.5/alphas[(spe,l,n)]) # bohr 51 | 52 | # init geometry 53 | geom = xyzfile[iconf] 54 | geom.wrap() 55 | symbols = geom.get_chemical_symbols() 56 | valences = geom.get_atomic_numbers() 57 | coords = geom.get_positions()/bohr2angs 58 | cell = geom.get_cell()/bohr2angs 59 | natoms = len(coords) 60 | 61 | aux_projs = np.load(inp.path2qm+inp.primprojdir+"projections_conf"+str(iconf)+".npy") 62 | print("starting dimension:",len(aux_projs)) 63 | 64 | projector = {} 65 | ncut = {} 66 | for spe in species: 67 | for l in range(lmax[spe]+1): 68 | projector[(spe,l)] = np.load("contractions/contra_spe"+str(spe)+"_l"+str(l)+".npy") 69 | ncut[(spe,l)] = projector[(spe,l)].shape[-1] 70 | 71 | naux_proj = 0 72 | for iat in range(natoms): 73 | spe = symbols[iat] 74 | for l in range(lmax[spe]+1): 75 | naux_proj += ncut[(spe,l)]*(2*l+1) 76 | print("final dimension:",naux_proj,flush=True) 77 | 78 | # project overlap over most relevant radial channels 79 | contr_proj = np.zeros(naux_proj) 80 | iaux = 0 81 | iaux_proj = 0 82 | for iat in range(natoms): 83 | spe = symbols[iat] 84 | for l in range(lmax[spe]+1): 85 | blocksize = nmax[(spe,l)]*(2*l+1) 86 | blocksize_proj = ncut[(spe,l)]*(2*l+1) 87 | # contract projections 88 | proj_slice = aux_projs[iaux:iaux+blocksize].reshape(nmax[(spe,l)],2*l+1) 89 | contr_proj[iaux_proj:iaux_proj+blocksize_proj] = np.dot(projector[(spe,l)].T,proj_slice).reshape(blocksize_proj) 90 | iaux += blocksize 91 | iaux_proj += blocksize_proj 92 | 93 | dirpath = os.path.join(inp.path2qm, inp.projdir) 94 | if not os.path.exists(dirpath): 95 | os.mkdir(dirpath) 96 | np.save(inp.path2qm+inp.projdir+"projections_conf"+str(iconf)+".npy",contr_proj) 97 | -------------------------------------------------------------------------------- /salted/cp2k/_deprecated_/contracted/get_contra-averages.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import numpy as np 3 | import time 4 | import ase 5 | from ase import io 6 | from ase.io import read 7 | 8 | import basis 9 | sys.path.insert(0, './') 10 | import inp 11 | 12 | # read species 13 | species = inp.species 14 | 15 | # read basis 16 | [lmax,nmax] = basis.basiset(inp.dfbasis) 17 | 18 | 19 | # read system 20 | xyzfile = read(inp.filename,":") 21 | ndata = len(xyzfile) 22 | 23 | #======================= system parameters 24 | atomic_symbols = [] 25 | atomic_valence = [] 26 | natoms = np.zeros(ndata,int) 27 | for i in range(len(xyzfile)): 28 | atomic_symbols.append(xyzfile[i].get_chemical_symbols()) 29 | atomic_valence.append(xyzfile[i].get_atomic_numbers()) 30 | natoms[i] = int(len(atomic_symbols[i])) 31 | natmax = max(natoms) 32 | 33 | projector = {} 34 | ncut = {} 35 | for spe in species: 36 | for l in range(lmax[spe]+1): 37 | projector[(spe,l)] = np.load("contractions/contra_spe"+str(spe)+"_l"+str(l)+".npy") 38 | ncut[(spe,l)] = projector[(spe,l)].shape[-1] 39 | 40 | avcoefs = {} 41 | nat_per_species = {} 42 | for spe in species: 43 | nat_per_species[spe] = 0 44 | avcoefs[spe] = np.zeros(nmax[(spe,0)],float) 45 | 46 | print("computing averages...") 47 | for iconf in range(ndata): 48 | atoms = atomic_symbols[iconf] 49 | coefs = np.load(inp.path2qm+"coefficients/coefficients_conf"+str(iconf)+".npy") 50 | i = 0 51 | for iat in range(natoms[iconf]): 52 | spe = atoms[iat] 53 | nat_per_species[spe] += 1 54 | for l in range(lmax[spe]+1): 55 | for n in range(ncut[(spe,l)]): 56 | for im in range(2*l+1): 57 | if l==0: 58 | avcoefs[spe][n] += coefs[i] 59 | i += 1 60 | 61 | for spe in species: 62 | avcoefs[spe] /= nat_per_species[spe] 63 | np.save("averages_"+str(spe)+".npy",avcoefs[spe]) 64 | -------------------------------------------------------------------------------- /salted/cp2k/_deprecated_/contracted/matrices-contracted.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import numpy as np 4 | import time 5 | import ase 6 | from ase import io 7 | from ase.io import read 8 | import random 9 | from random import shuffle 10 | from scipy import sparse 11 | 12 | import basis 13 | 14 | sys.path.insert(0, './') 15 | import inp 16 | 17 | # system definition 18 | spelist = inp.species 19 | xyzfile = read(inp.filename,":") 20 | ndata = len(xyzfile) 21 | 22 | # basis definition 23 | [lmax,nmax] = basis.basiset(inp.dfbasis) 24 | 25 | llist = [] 26 | nlist = [] 27 | for spe in spelist: 28 | llist.append(lmax[spe]) 29 | for l in range(lmax[spe]+1): 30 | nlist.append(nmax[(spe,l)]) 31 | llmax = max(llist) 32 | nnmax = max(nlist) 33 | 34 | # sparse-GPR parameters 35 | M = inp.Menv 36 | eigcut = inp.eigcut 37 | reg = inp.regul 38 | 39 | pdir = inp.preddir 40 | coefdir = inp.coefdir 41 | regrdir = inp.regrdir 42 | featdir = inp.featdir 43 | 44 | # species dependent arrays 45 | atoms_per_spe = {} 46 | natoms_per_spe = {} 47 | for iconf in range(ndata): 48 | for spe in spelist: 49 | atoms_per_spe[(iconf,spe)] = [] 50 | natoms_per_spe[(iconf,spe)] = 0 51 | 52 | atomic_symbols = [] 53 | valences = [] 54 | natoms = np.zeros(ndata,int) 55 | for iconf in range(ndata): 56 | atomic_symbols.append(xyzfile[iconf].get_chemical_symbols()) 57 | valences.append(xyzfile[iconf].get_atomic_numbers()) 58 | natoms[iconf] = int(len(atomic_symbols[iconf])) 59 | for iat in range(natoms[iconf]): 60 | spe = atomic_symbols[iconf][iat] 61 | atoms_per_spe[(iconf,spe)].append(iat) 62 | natoms_per_spe[(iconf,spe)] += 1 63 | natmax = max(natoms) 64 | 65 | projector = {} 66 | ncut = {} 67 | for spe in spelist: 68 | for l in range(lmax[spe]+1): 69 | projector[(spe,l)] = np.load("contractions/contra_spe"+str(spe)+"_l"+str(l)+".npy") 70 | ncut[(spe,l)] = projector[(spe,l)].shape[-1] 71 | 72 | p = sparse.load_npz(inp.path2ml+featdir+"M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/psi-nm_conf0.npz") 73 | totsize = p.shape[-1] 74 | print("problem dimensionality:", totsize,flush=True) 75 | if totsize>70000: 76 | print("ERROR: problem dimension too large, minimize directly loss-function instead!") 77 | sys.exit(0) 78 | 79 | # load average density coefficients 80 | av_coefs = {} 81 | for spe in spelist: 82 | av_coefs[spe] = np.load("averages_"+str(spe)+".npy") 83 | 84 | # define training set at random 85 | dataset = list(range(ndata)) 86 | #random.Random(3).shuffle(dataset) 87 | trainrangetot = dataset[:inp.Ntrain] 88 | np.savetxt("training_set_N"+str(inp.Ntrain)+".txt",trainrangetot,fmt='%i') 89 | #trainrangetot = np.loadtxt("training_set_upto443.txt",int) 90 | #trainrangetot = np.loadtxt("training_set_upto663.txt",int) 91 | 92 | # Distribute structures to tasks 93 | ntrain = int(inp.trainfrac*inp.Ntrain) 94 | trainrange = trainrangetot[:ntrain] 95 | 96 | dirpath = os.path.join(inp.path2ml, regrdir) 97 | if not os.path.exists(dirpath): 98 | os.mkdir(dirpath) 99 | dirpath = os.path.join(inp.path2ml+regrdir, "M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))) 100 | if not os.path.exists(dirpath): 101 | os.mkdir(dirpath) 102 | 103 | print("computing regression matrices...") 104 | Avec = np.zeros(totsize) 105 | Bmat = np.zeros((totsize,totsize)) 106 | #Avec = np.load(inp.path2ml+regrdir+"M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/Avec_N90.npy")*90 107 | #Bmat = np.load(inp.path2ml+regrdir+"M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/Bmat_N90.npy")*90 108 | for iconf in trainrange: 109 | print("conf:", iconf+1,flush=True) 110 | 111 | # load reference QM data 112 | ref_coefs = np.load(inp.path2qm+coefdir+"coefficients_conf"+str(iconf)+".npy") 113 | over = np.load(inp.path2qm+"overlaps/overlap_conf"+str(iconf)+".npy") 114 | psivec = sparse.load_npz(inp.path2ml+featdir+"M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/psi-nm_conf"+str(iconf)+".npz") 115 | psi = psivec.toarray() 116 | 117 | #TODO comment this to test learning of difference! 118 | Av_coeffs = np.zeros(ref_coefs.shape[0]) 119 | i = 0 120 | for iat in range(natoms[iconf]): 121 | spe = atomic_symbols[iconf][iat] 122 | for l in range(lmax[spe]+1): 123 | for n in range(ncut[(spe,l)]): 124 | if l==0: 125 | Av_coeffs[i] = av_coefs[spe][n] 126 | i += 2*l+1 127 | 128 | ref_coefs -= Av_coeffs 129 | ref_projs = np.dot(over,ref_coefs) 130 | 131 | Avec += np.dot(psi.T,ref_projs) 132 | Bmat += np.dot(psi.T,np.dot(over,psi)) 133 | 134 | if iconf+1==4 or iconf+1==8 or iconf+1==16 or iconf+1==32 or iconf+1==39: 135 | np.save(inp.path2ml+regrdir+"M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/Avec_N"+str(iconf+1)+".npy",Avec/float(iconf+1)) 136 | np.save(inp.path2ml+regrdir+"M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/Bmat_N"+str(iconf+1)+".npy",Bmat/float(iconf+1)) 137 | 138 | Avec /= float(ntrain) 139 | Bmat /= float(ntrain) 140 | 141 | np.save(inp.path2ml+regrdir+"M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/Avec_N"+str(ntrain)+".npy",Avec) 142 | np.save(inp.path2ml+regrdir+"M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/Bmat_N"+str(ntrain)+".npy",Bmat) 143 | -------------------------------------------------------------------------------- /salted/cp2k/_deprecated_/contracted/rebuild_contra-projs.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import math 4 | import numpy as np 5 | from ase.io import read 6 | import copy 7 | import argparse 8 | import time 9 | 10 | import basis 11 | 12 | def add_command_line_arguments(parsetext): 13 | parser = argparse.ArgumentParser(description=parsetext,formatter_class=argparse.ArgumentDefaultsHelpFormatter) 14 | parser.add_argument("-iconf", "--confidx", type=int, default=1, help="Structure index") 15 | args = parser.parse_args() 16 | return args 17 | 18 | def set_variable_values(args): 19 | iconf = args.confidx 20 | return [iconf] 21 | 22 | args = add_command_line_arguments("") 23 | [iconf] = set_variable_values(args) 24 | 25 | print("conf", iconf) 26 | iconf -= 1 # 0-based indexing 27 | 28 | sys.path.insert(0, './') 29 | import inp 30 | 31 | species = inp.species 32 | 33 | qmpath = inp.path2qm 34 | 35 | xyzfile = read(inp.filename,":") 36 | [lmax,nmax] = basis.basiset(inp.dfbasis) 37 | 38 | # init geometry 39 | geom = xyzfile[iconf] 40 | symbols = geom.get_chemical_symbols() 41 | natoms = len(symbols) 42 | 43 | # compute total number of auxiliary functions 44 | psize = 0 45 | for iat in range(natoms): 46 | spe = symbols[iat] 47 | for l in range(lmax[spe]+1): 48 | for n in range(nmax[(spe,l)]): 49 | psize += 2*l+1 50 | 51 | 52 | dirpath = os.path.join(qmpath, inp.projdir) 53 | if not os.path.exists(dirpath): 54 | os.mkdir(dirpath) 55 | 56 | projs = np.zeros(psize) 57 | i = 0 58 | for iat in range(natoms): 59 | projs_perat = np.load(qmpath+inp.projdir+"projections_conf"+str(iconf)+"_atom"+str(iat)+".npy") 60 | psize_perat = len(projs_perat) 61 | projs[i:i+psize_perat] = projs_perat 62 | i += psize_perat 63 | 64 | projector = {} 65 | ncut = {} 66 | for spe in species: 67 | for l in range(lmax[spe]+1): 68 | projector[(spe,l)] = np.load("contractions/contra_spe"+str(spe)+"_l"+str(l)+".npy") 69 | ncut[(spe,l)] = projector[(spe,l)].shape[-1] 70 | 71 | naux_proj = 0 72 | for iat in range(natoms): 73 | spe = symbols[iat] 74 | for l in range(lmax[spe]+1): 75 | naux_proj += ncut[(spe,l)]*(2*l+1) 76 | 77 | # project overlap over most relevant radial channels 78 | contr_proj = np.zeros(naux_proj) 79 | iaux = 0 80 | iaux_proj = 0 81 | for iat in range(natoms): 82 | spe = symbols[iat] 83 | for l in range(lmax[spe]+1): 84 | blocksize = nmax[(spe,l)]*(2*l+1) 85 | blocksize_proj = ncut[(spe,l)]*(2*l+1) 86 | # contract projections 87 | proj_slice = projs[iaux:iaux+blocksize].reshape(nmax[(spe,l)],2*l+1) 88 | contr_proj[iaux_proj:iaux_proj+blocksize_proj] = np.dot(projector[(spe,l)].T,proj_slice).reshape(blocksize_proj) 89 | iaux += blocksize 90 | iaux_proj += blocksize_proj 91 | 92 | np.save(qmpath+inp.projdir+"projections_conf"+str(iconf)+".npy",contr_proj) 93 | -------------------------------------------------------------------------------- /salted/cp2k/cp2k2salted.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import math 4 | import numpy as np 5 | from ase.io import read 6 | from scipy import special 7 | from itertools import islice 8 | import copy 9 | import time 10 | 11 | from salted import basis 12 | from salted.sys_utils import ParseConfig, read_system, get_atom_idx, get_conf_range 13 | 14 | inp = ParseConfig().parse_input() 15 | 16 | xyzfile = read(inp.system.filename,":") 17 | ndata = len(xyzfile) 18 | species = inp.system.species 19 | [lmax,nmax] = basis.basiset(inp.qm.dfbasis) 20 | 21 | if inp.system.parallel: 22 | 23 | from mpi4py import MPI 24 | comm = MPI.COMM_WORLD 25 | size = comm.Get_size() 26 | rank = comm.Get_rank() 27 | 28 | if rank==0: 29 | 30 | dirpath = os.path.join(inp.salted.saltedpath, "coefficients") 31 | if not os.path.exists(dirpath): 32 | os.mkdir(dirpath) 33 | 34 | if inp.salted.saltedtype=="density-response": 35 | for icart in ["x","y","z"]: 36 | dirpath = os.path.join(inp.salted.saltedpath, "coefficients", f"{icart}") 37 | if not os.path.exists(dirpath): 38 | os.mkdir(dirpath) 39 | 40 | 41 | if inp.system.parallel: 42 | 43 | comm.Barrier() 44 | 45 | if ndata < size: 46 | if rank == 0: 47 | raise ValueError( 48 | f"More processes {size=} have been requested than confiturations {ndata=}. " 49 | f"Please reduce the number of processes." 50 | ) 51 | else: 52 | exit() 53 | conf_range = get_conf_range(rank, size, ndata, np.arange(ndata,dtype=int)) 54 | conf_range = comm.scatter(conf_range, root=0) 55 | print( 56 | f"Task {rank+1} handles the following configurations: {conf_range}", flush=True 57 | ) 58 | 59 | else: 60 | 61 | conf_range = np.arange(ndata,dtype=int) 62 | 63 | 64 | # init geometry 65 | for iconf in conf_range: 66 | 67 | geom = xyzfile[iconf] 68 | symbols = geom.get_chemical_symbols() 69 | natoms = len(symbols) 70 | # compute basis set size 71 | nRI = 0 72 | for iat in range(natoms): 73 | spe = symbols[iat] 74 | if spe in species: 75 | for l in range(lmax[spe]+1): 76 | for n in range(nmax[(spe,l)]): 77 | nRI += 2*l+1 78 | 79 | print("conf", iconf+1, "size =", nRI, flush=True) 80 | 81 | # save overlap matrix in SALTED format 82 | overlap = np.zeros((nRI, nRI)).astype(np.double) 83 | for i in range(nRI): 84 | offset = 4 + i*((nRI+1)*8) 85 | overlap[:, i] = np.fromfile(os.path.join( 86 | inp.qm.path2qm, f"conf_{iconf+1}", inp.qm.ovlpfile 87 | ), dtype=np.float64, offset = offset, count=nRI) 88 | 89 | dirpath = os.path.join(inp.salted.saltedpath, "overlaps") 90 | if not os.path.exists(dirpath): 91 | os.mkdir(dirpath) 92 | np.save(os.path.join(inp.salted.saltedpath, "overlaps", f"overlap_conf{iconf}.npy"), overlap) 93 | 94 | if inp.salted.saltedtype=="density": 95 | 96 | # load density coefficients and check dimension 97 | coefficients = np.loadtxt(os.path.join(inp.qm.path2qm, f"conf_{iconf+1}", inp.qm.coeffile)) 98 | if len(coefficients)!=nRI: 99 | print("ERROR: basis set size does not correspond to size of coefficients vector!") 100 | sys.exit(0) 101 | 102 | # save coefficients vector in SALTED format 103 | #if natoms%2 != 0: 104 | # coefficients = np.sum(coefficients,axis=1) 105 | np.save(os.path.join(inp.salted.saltedpath, "coefficients", f"coefficients_conf{iconf}.npy"), coefficients) 106 | 107 | 108 | elif inp.salted.saltedtype=="density-response": 109 | 110 | # load density-response coefficients and check dimension 111 | for icart in ["x","y","z"]: 112 | # Estimate derivative by finite differences applying an electric field of 0.01V/angs 113 | coefficients = np.loadtxt(os.path.join(inp.qm.path2qm, f"conf_{iconf+1}", f"{icart}_positive", inp.qm.coeffile)) 114 | coefficients -= np.loadtxt(os.path.join(inp.qm.path2qm, f"conf_{iconf+1}", f"{icart}_negative", inp.qm.coeffile)) 115 | coefficients /= (2*0.0001945) # 0.01 V/angs 116 | if len(coefficients)!=nRI: 117 | print("ERROR: basis set size does not correspond to size of coefficients vector!") 118 | sys.exit(0) 119 | 120 | # save coefficients vector in SALTED format 121 | np.save(os.path.join(inp.salted.saltedpath, "coefficients", f"{icart}", f"coefficients_conf{iconf}.npy"), coefficients) 122 | 123 | ## save projections vector in SALTED format 124 | #projections = np.dot(overlap,coefficients) 125 | #dirpath = os.path.join(inp.salted.saltedpath, "projections") 126 | #if not os.path.exists(dirpath): 127 | # os.mkdir(dirpath) 128 | #np.save(inp.salted.saltedpath+"projections/projections_conf"+str(iconf)+".npy",projections) 129 | -------------------------------------------------------------------------------- /salted/cp2k/polarizability.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | import os.path as osp 5 | 6 | import numpy as np 7 | from scipy import special 8 | from scipy import sparse 9 | 10 | from salted import basis 11 | from salted.sys_utils import ParseConfig, read_system, get_atom_idx, get_conf_range, init_property_file 12 | from salted.cp2k.utils import init_moments, compute_charge_and_dipole, compute_polarizability 13 | 14 | def build(iconf,ref_coefs): 15 | """Compute polarizability tensor for the given structure and related set of density-response coefficients.""" 16 | 17 | inp = ParseConfig().parse_input() 18 | (saltedname, saltedpath, saltedtype, 19 | filename, species, average, parallel, 20 | path2qm, qmcode, qmbasis, dfbasis, 21 | filename_pred, predname, predict_data, alpha_only, 22 | rep1, rcut1, sig1, nrad1, nang1, neighspe1, 23 | rep2, rcut2, sig2, nrad2, nang2, neighspe2, 24 | sparsify, nsamples, ncut, 25 | zeta, Menv, Ntrain, trainfrac, regul, eigcut, 26 | gradtol, restart, blocksize, trainsel, nspe1, nspe2, HYPER_PARAMETERS_DENSITY, HYPER_PARAMETERS_POTENTIAL) = ParseConfig().get_all_params() 27 | 28 | species, lmax, nmax, lmax_max, nnmax, ndata, atomic_symbols, natoms, natmax = read_system() 29 | 30 | if qmcode=="cp2k": 31 | from ase.io import read 32 | xyzfile = read(filename, ":") 33 | # Initialize calculation of density/density-response moments 34 | charge_integrals,dipole_integrals = init_moments(inp,species,lmax,nmax,0) 35 | 36 | ref_alpha = compute_polarizability(xyzfile[iconf],natoms[iconf],atomic_symbols[iconf],lmax,nmax,species,charge_integrals,dipole_integrals,ref_coefs) 37 | 38 | # Save polarizabilities 39 | return ref_alpha 40 | #print(ref_alpha[("x","x")],ref_alpha[("x","y")],ref_alpha[("x","z")]) 41 | #print(ref_alpha[("y","x")],ref_alpha[("y","y")],ref_alpha[("y","z")]) 42 | #print(ref_alpha[("z","x")],ref_alpha[("z","y")],ref_alpha[("z","z")]) 43 | -------------------------------------------------------------------------------- /salted/cp2k/uncontract_ri_basis.py: -------------------------------------------------------------------------------- 1 | # Given a CP2K output file using an automatically generated RI HFX basis, this script produces a 2 | #CP2K basis set file containing a fully decontracted version of the same basis, for each element 3 | 4 | #usage: 5 | #python uncontract_ri_basis.py cp2k_output_file new_basis_set_file 6 | 7 | import sys 8 | 9 | #reading arguments 10 | cp2k_output = sys.argv[1] 11 | basis_file = sys.argv[2] 12 | 13 | #defining some helper functions 14 | def get_l_from_string(string): 15 | if "s" in string: 16 | return 0 17 | if "p" in string: 18 | return 1 19 | if "d" in string: 20 | return 2 21 | if "f" in string: 22 | return 3 23 | if "g" in string: 24 | return 4 25 | if "h" in string: 26 | return 5 27 | if "i" in string: 28 | return 6 29 | if "j" in string: 30 | return 7 31 | if "k" in string: 32 | return 8 33 | return "not a valid l quantum number" 34 | 35 | def get_set_exponents(kind, iset, lines): 36 | correct_kind = False 37 | correct_basis = False 38 | correct_set = False 39 | 40 | exps = [] 41 | momenta = [] 42 | for line in lines: 43 | if " Atomic kind: " in line: 44 | if line.split()[3] == kind: 45 | correct_kind = True 46 | else: 47 | correct_kind = False 48 | 49 | if "Basis Set " in line: 50 | if "RI HFX " in line: 51 | correct_basis = True 52 | if not "RI-AUX" in line: 53 | print("Warning: the script is meant to be used with automatically generated RI basis sets") 54 | else: 55 | correct_basis = False 56 | 57 | if len(line) == 1: 58 | correct_set = False 59 | 60 | if correct_kind and correct_basis and len(line) > 1: 61 | if line.split()[0] == str(iset): 62 | correct_set = True 63 | momenta.append(get_l_from_string(line.split()[2])) 64 | 65 | if correct_kind and correct_basis and correct_set: 66 | exps.append(line.split()[-2]) 67 | 68 | if " Atomic covalent radius " in line: 69 | break 70 | 71 | #remove values appearing multiple times before return 72 | exps = [float(e) for e in dict.fromkeys(exps).keys()] 73 | momenta = [l for l in dict.fromkeys(momenta).keys()] 74 | 75 | return exps, momenta 76 | 77 | def get_kinds(lines): 78 | kinds = [] 79 | for line in output_lines: 80 | if " Atomic kind: " in line: 81 | kinds.append(line.split()[3]) 82 | return kinds 83 | 84 | def get_sets(lines): 85 | nsets = [] 86 | for i, line in enumerate(output_lines): 87 | if " Number of orbital shell sets: " in line: 88 | if "RI HFX Basis Set" in output_lines[i-2]: 89 | nsets.append(int(line.split()[-1])) 90 | return nsets 91 | 92 | 93 | #reading the CP2K output file 94 | with open(cp2k_output, "r") as myfile: 95 | output_lines = myfile.readlines() 96 | 97 | #parse the number of atomic kinds 98 | kinds = get_kinds(output_lines) 99 | nkinds = len(kinds) 100 | 101 | #parse the number of sets per kind 102 | nsets = get_sets(output_lines) 103 | 104 | #printing to new basis set file 105 | with open(basis_file, "w") as myfile: 106 | 107 | for ikind, kind in enumerate(kinds): 108 | 109 | #get total number of basis functions for this kind 110 | nfunc = 0 111 | nset = nsets[ikind] 112 | for iset in range(1, nset + 1): 113 | exps, momenta = get_set_exponents(kind, iset, output_lines) 114 | nfunc += len(exps)*len(momenta) 115 | 116 | myfile.write("{} RI_AUTO_uncontracted\n".format(kind)) 117 | myfile.write(" {}\n".format(nfunc)) 118 | 119 | for iset in range(1, nset+1): 120 | exps, momenta = get_set_exponents(kind, iset, output_lines) 121 | nfunc += len(exps)*len(momenta) 122 | for l in momenta: 123 | for exp in exps: 124 | myfile.write(" 1 {} {} 1 1\n".format(l,l)) 125 | myfile.write(" {:11.6f} 1.0\n".format(exp)) 126 | 127 | myfile.write("\n") 128 | 129 | -------------------------------------------------------------------------------- /salted/cp2k/xyz2sys.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import argparse 4 | 5 | from ase.io import read 6 | 7 | from salted.sys_utils import ParseConfig 8 | 9 | inp = ParseConfig().parse_input() 10 | 11 | filename = inp.system.filename 12 | path2qm = inp.qm.path2qm 13 | periodic = inp.qm.periodic 14 | 15 | xyz = read(filename, ":") 16 | ndata = len(xyz) 17 | 18 | if periodic=="0D": 19 | PERIODIC = "None" 20 | elif periodic=="2D": 21 | PERIODIC = "XY" 22 | elif periodic=="3D": 23 | PERIODIC = "XYZ" 24 | 25 | for iconf in range(ndata): 26 | dirpath = os.path.join(path2qm, "conf_"+str(iconf+1)) 27 | if not os.path.exists(dirpath): 28 | os.mkdir(dirpath) 29 | symbol = xyz[iconf].get_chemical_symbols() 30 | coords = xyz[iconf].get_positions() 31 | natoms = len(coords) 32 | f = open(os.path.join(path2qm, f"conf_{iconf+1}", "coords.sys"), "w") 33 | print("&COORD",file=f) 34 | for iat in range(natoms): 35 | print(symbol[iat],coords[iat,0],coords[iat,1],coords[iat,2],file=f) 36 | print("&END COORD",file=f) 37 | f.close() 38 | cell = xyz[iconf].get_cell() 39 | f = open(os.path.join(path2qm, f"conf_{iconf+1}", "cell.sys"), "w") 40 | print("&CELL",file=f) 41 | print("PERIODIC "+str(PERIODIC),file=f) 42 | print("A",cell[0,0],cell[0,1],cell[0,2],file=f) 43 | print("B",cell[1,0],cell[1,1],cell[1,2],file=f) 44 | print("C",cell[2,0],cell[2,1],cell[2,2],file=f) 45 | print("&END CELL",file=f) 46 | f.close() 47 | -------------------------------------------------------------------------------- /salted/efield.py: -------------------------------------------------------------------------------- 1 | import sys,os 2 | import numpy as np 3 | import scipy.special as sc 4 | 5 | def setup_orthomatrix(nmax,rc): 6 | """Compute orthogonalization matrix""" 7 | 8 | sigma = np.zeros(nmax,float) 9 | for i in range(nmax): 10 | sigma[i] = max(np.sqrt(float(i)),1.0)*(rc)/float(nmax) 11 | 12 | overlap = np.zeros((nmax,nmax),float) 13 | for n1 in range(nmax): 14 | for n2 in range(nmax): 15 | overlap[n1,n2] = (0.5/(sigma[n1])**2 + 0.5/(sigma[n2])**2)**(-0.5*(3.0 +n1 +n2)) \ 16 | /(sigma[n1]**n1 * sigma[n2]**n2)*\ 17 | sc.gamma(0.5*(3.0 + n1 + n2))/ ( \ 18 | (sigma[n1]*sigma[n2])**1.5 * np.sqrt(sc.gamma(1.5+n1)*sc.gamma(1.5+n2)) ) 19 | 20 | eigenvalues, unitary = np.linalg.eig(overlap) 21 | sqrteigen = np.sqrt(eigenvalues) 22 | diagoverlap = np.diag(sqrteigen) 23 | newoverlap = np.dot(np.conj(unitary),np.dot(diagoverlap,unitary.T)) 24 | orthomatrix = np.linalg.inv(newoverlap) 25 | 26 | return [orthomatrix,sigma] 27 | 28 | 29 | def radint_efield(nmax,sigma): 30 | """Compute external field contribution to local potential""" 31 | 32 | # compute radial integrals int_0^\infty dr r^3 R_n(r) 33 | radint = np.zeros(nmax) 34 | for n in range(nmax): 35 | inner = 0.5*sc.gamma(n+1.5)*(sigma[n]**2)**(n+1.5) 36 | radint[n] = 2**float(1.0+float(n)/2.0) * sigma[n]**(4+n) * sc.gamma(2.0+float(n)/2.0) / np.sqrt(inner) 37 | 38 | return radint 39 | 40 | def get_efield_sph(nmax,rc): 41 | """Compute the SPH components (l=1, m=0) of a uniform and constant field aligned along Z""" 42 | 43 | [orthomatrix,sigma] = setup_orthomatrix(nmax,rc) 44 | 45 | radint = radint_efield(nmax,sigma) 46 | 47 | orthoradint = np.dot(orthomatrix,radint) 48 | 49 | efield_coef = np.zeros(nmax,complex) 50 | for n in range(nmax): 51 | efield_coef[n] = complex(np.sqrt(4.0*np.pi/3.0)*orthoradint[n],0.0) 52 | 53 | return efield_coef 54 | -------------------------------------------------------------------------------- /salted/get_averages.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import numpy as np 4 | import os.path as osp 5 | 6 | from salted.sys_utils import ParseConfig, read_system 7 | 8 | def build(): 9 | 10 | inp = ParseConfig().parse_input() 11 | 12 | spelist, lmax, nmax, llmax, nnmax, ndata, atomic_symbols, natoms, natmax = read_system() 13 | 14 | avcoefs = {} 15 | nat_per_species = {} 16 | for spe in spelist: 17 | nat_per_species[spe] = 0 18 | avcoefs[spe] = np.zeros(nmax[(spe,0)],float) 19 | 20 | print("computing averages...") 21 | for iconf in range(ndata): 22 | atoms = atomic_symbols[iconf] 23 | coefs = np.load(os.path.join(inp.salted.saltedpath, "coefficients", f"coefficients_conf{iconf}.npy")) 24 | i = 0 25 | for iat in range(natoms[iconf]): 26 | spe = atoms[iat] 27 | nat_per_species[spe] += 1 28 | for l in range(lmax[spe]+1): 29 | for n in range(nmax[(spe,l)]): 30 | for im in range(2*l+1): 31 | if l==0: 32 | avcoefs[spe][n] += coefs[i] 33 | i += 1 34 | 35 | adir = os.path.join(inp.salted.saltedpath, "coefficients", "averages") 36 | if not osp.exists(adir): 37 | os.mkdir(adir) 38 | 39 | for spe in spelist: 40 | avcoefs[spe] /= nat_per_species[spe] 41 | np.save(os.path.join(inp.salted.saltedpath, "coefficients", "averages", f"averages_{spe}.npy"), avcoefs[spe]) 42 | 43 | return 44 | 45 | if __name__ == "__main__": 46 | build() 47 | -------------------------------------------------------------------------------- /salted/get_basis_info.py: -------------------------------------------------------------------------------- 1 | """Translate density fitting basis info from FHI-aims / CP2K to SALTED density fitting basis info. 2 | This is just an entry point for the actual implementation. 3 | in salted/aims/get_basis_info.py and salted/cp2k/get_basis_info.py. 4 | """ 5 | 6 | import argparse 7 | import sys 8 | 9 | from salted.sys_utils import ParseConfig 10 | 11 | 12 | def get_parser(): 13 | parser = argparse.ArgumentParser() 14 | parser.add_argument( 15 | "--dryrun", 16 | action="store_true", 17 | help="run without writing to files, and print the result", 18 | ) 19 | parser.add_argument( 20 | "--force_overwrite", 21 | action="store_true", 22 | help="force overwrite the existing basis data", 23 | ) 24 | return parser 25 | 26 | 27 | if __name__ == "__main__": 28 | parser = get_parser() 29 | args = parser.parse_args() 30 | 31 | inp = ParseConfig().parse_input() 32 | if inp.qm.qmcode.lower() == "aims": 33 | from salted.aims.get_basis_info import build 34 | elif inp.qm.qmcode.lower() == "cp2k": 35 | from salted.cp2k.get_basis_info import build 36 | elif inp.qm.qmcode.lower() == "pyscf": 37 | from salted.pyscf.get_basis_info import build 38 | else: 39 | raise ValueError(f"Unknown qmcode: {inp.qm.qmcode}") 40 | 41 | build(dryrun=args.dryrun, force_overwrite=args.force_overwrite) 42 | -------------------------------------------------------------------------------- /salted/init_pred.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | import os.path as osp 5 | 6 | import h5py 7 | import numpy as np 8 | from scipy import special 9 | 10 | from salted import basis 11 | from salted.sys_utils import ParseConfig, get_feats_projs 12 | 13 | def build(): 14 | 15 | inp = ParseConfig().parse_input() 16 | 17 | saltedname = inp.salted.saltedname 18 | saltedpath = inp.salted.saltedpath 19 | species = inp.system.species 20 | Menv = inp.gpr.Menv 21 | zeta = inp.gpr.z 22 | reg = inp.gpr.regul 23 | ncut = inp.descriptor.sparsify.ncut 24 | sparsify = True if inp.descriptor.sparsify.ncut > 0 else False 25 | 26 | # read basis 27 | [lmax,nmax] = basis.basiset(inp.qm.dfbasis) 28 | llist = [] 29 | nlist = [] 30 | for spe in species: 31 | llist.append(lmax[spe]) 32 | for l in range(lmax[spe]+1): 33 | nlist.append(nmax[(spe,l)]) 34 | lmax_max = max(llist) 35 | 36 | charge_integrals = {} 37 | if inp.qm.qmcode=="cp2k": 38 | # Initialize calculation of density/density-response moments 39 | from salted.cp2k.utils import init_moments 40 | charge_integrals,dipole_integrals = init_moments(inp,species,lmax,nmax,0) 41 | 42 | loadstart = time.time() 43 | 44 | # Load feature space sparsification information if required 45 | if sparsify: 46 | vfps = {} 47 | for lam in range(lmax_max+1): 48 | vfps[lam] = np.load(osp.join( 49 | saltedpath, f"equirepr_{saltedname}", f"fps{ncut}-{lam}.npy" 50 | )) 51 | 52 | # Load training feature vectors and RKHS projection matrix 53 | Vmat,Mspe,power_env_sparse = get_feats_projs(species,lmax) 54 | 55 | # load regression weights 56 | ntrain = int(inp.gpr.Ntrain*inp.gpr.trainfrac) 57 | weights = np.load(osp.join( 58 | saltedpath, f"regrdir_{saltedname}", f"M{Menv}_zeta{zeta}", f"weights_N{ntrain}_reg{int(np.log10(reg))}.npy" 59 | )) 60 | 61 | print("load time:", (time.time()-loadstart)) 62 | 63 | return [lmax,nmax,lmax_max,weights,power_env_sparse,Mspe,Vmat,vfps,charge_integrals] 64 | 65 | if __name__ == "__main__": 66 | build() 67 | -------------------------------------------------------------------------------- /salted/initialize.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | 5 | from salted import wigner, sparsify_features, scalar_vector 6 | from salted.sys_utils import ParseConfig 7 | 8 | 9 | def build(): 10 | 11 | inp = ParseConfig().parse_input() 12 | # check for destructive interactions 13 | if inp.system.average == True and inp.salted.saltedtype == "density-response": 14 | raise ValueError( 15 | "Invalid configuration: 'average' cannot be True when 'saltedtype' is 'density-response'. Please change your input settings." 16 | ) 17 | 18 | # Precompute and save the required Wigner-3j symbols and Clebsch-Gordan, depending on SALTED target 19 | wigner.build() 20 | 21 | # Sparsify the feature space of symmetry-adapted descriptors? 22 | if inp.descriptor.sparsify.ncut > 0: 23 | 24 | if inp.salted.saltedtype == "density-response": 25 | print( 26 | "ERROR: feature space sparsification not allowed with inp.salted.saltedtype: density-response!" 27 | ) 28 | sys.exit(0) 29 | 30 | # Precompute and save the feature space sparsification details 31 | sparsify_features.build() 32 | 33 | # Compute and save the sparsified scalar descriptor 34 | scalar_vector.build() 35 | 36 | else: 37 | 38 | # Compute and save the unsparsified scalar descriptor 39 | scalar_vector.build() 40 | 41 | 42 | if __name__ == "__main__": 43 | build() 44 | -------------------------------------------------------------------------------- /salted/ortho/ortho_error.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import numpy as np 4 | import sys 5 | 6 | from sys_utils import read_system 7 | 8 | sys.path.insert(0, './') 9 | import inp 10 | 11 | spelist, lmax, nmax, llmax, nnmax, ndata, atomic_symbols, natoms, natmax = read_system() 12 | 13 | # number of sparse environments 14 | M = inp.Menv 15 | eigcut = inp.eigcut 16 | 17 | pdir = inp.valcdir 18 | 19 | # load predicted coefficients for test structures 20 | trainrangetot = np.loadtxt("training_set.txt",int) 21 | ntrain = int(inp.trainfrac*len(trainrangetot)) 22 | testrange = np.setdiff1d(list(range(ndata)),trainrangetot) 23 | ntest = len(testrange) 24 | natoms_test = natoms[testrange] 25 | 26 | dirpath = os.path.join(inp.path2qm+pdir+"M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/","N_"+str(ntrain)) 27 | if not os.path.exists(dirpath): 28 | os.mkdir(dirpath) 29 | 30 | ortho_coeffs = np.load(inp.path2qm+pdir+"M"+str(M)+"_eigcut"+str(int(np.log10(inp.eigcut)))+"/ortho-predictions_N"+str(ntrain)+"_reg"+str(int(np.log10(inp.regul)))+".npy") 31 | 32 | av_coefs = {} 33 | for spe in spelist: 34 | av_coefs[spe] = np.load("averages_"+str(spe)+".npy") 35 | 36 | itest=0 37 | Oerror_density = 0.0 38 | variance = 0.0 39 | preds = np.zeros((ntest,natmax,llmax+1,nnmax,2*llmax+1)) 40 | for iconf in testrange: 41 | # print(iconf+1) 42 | start = time.time() 43 | atoms = atomic_symbols[iconf] 44 | #================================================ 45 | projs_ref = np.load(inp.path2qm+inp.projdir+"projections_conf"+str(iconf)+".npy") 46 | coeffs_ref = np.load(inp.path2qm+inp.coefdir+"coefficients_conf"+str(iconf)+".npy") 47 | size_coeffs = coeffs_ref.shape 48 | # compute orthogonalization matrix 49 | orthomatrix = np.load(inp.path2qm+inp.ovlpdir+"orthomatrix_"+str(iconf)+".npy") 50 | OCoeffs = np.zeros(size_coeffs) 51 | i = 0 52 | for iat in range(natoms[iconf]): 53 | for l in range(lmax[atoms[iat]]+1): 54 | for n in range(nmax[(atoms[iat],l)]): 55 | for im in range(2*l+1): 56 | OCoeffs[i] = ortho_coeffs[itest,iat,l,n,im] 57 | i+=1 58 | OCoef = np.dot(orthomatrix,OCoeffs) 59 | #================================================ 60 | coefficients = np.zeros(size_coeffs,float) 61 | averages = np.zeros(size_coeffs,float) 62 | icoeff = 0 63 | for iat in range(natoms[iconf]): 64 | for l in range(lmax[atoms[iat]]+1): 65 | for n in range(nmax[(atoms[iat],l)]): 66 | for im in range(2*l+1): 67 | if l==0: 68 | OCoef[icoeff] += av_coefs[atoms[iat]][n] 69 | averages[icoeff] = av_coefs[atoms[iat]][n] 70 | preds[itest,iat,l,n,im] = OCoef[icoeff] 71 | icoeff +=1 72 | np.save(inp.path2qm+pdir+"M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/N_"+str(ntrain)+"/prediction_conf"+str(iconf)+".npy",OCoef) 73 | overl = np.load(inp.path2qm+inp.ovlpdir+"overlap_conf"+str(iconf)+".npy") 74 | OProj = np.dot(overl,OCoef) 75 | #================================================ 76 | Oerror = np.dot(OCoef-coeffs_ref,OProj-projs_ref) 77 | Oerror_density += Oerror 78 | projs_ref -= np.dot(overl,averages) 79 | coeffs_ref -= averages 80 | var = np.dot(coeffs_ref,projs_ref) 81 | variance += var 82 | print(iconf+1, ":", np.sqrt(Oerror/var)*100, "% RMSE",flush=True) 83 | # print("time:",time.time()-start) 84 | itest+=1 85 | 86 | 87 | print("% RMSE =", 100*np.sqrt(Oerror_density/variance)) 88 | #np.save(inp.path2qm+pdir+"M"+str(M)+"_eigcut"+str(int(np.log10(inp.eigcut)))+"/pred-coeffs_N"+str(ntrain)+"_reg"+str(int(np.log10(inp.regul)))+".npy",preds) 89 | -------------------------------------------------------------------------------- /salted/ortho/ortho_projections.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import time 4 | #import argparse 5 | import sys 6 | sys.path.insert(0, './') 7 | import inp 8 | from sys_utils import read_system, get_atom_idx 9 | 10 | spelist, lmax, nmax, llmax, nnmax, ndata, atomic_symbols, natoms, natmax = read_system() 11 | atom_idx, natoms_spe = get_atom_idx(ndata,natoms,spelist,atomic_symbols) 12 | 13 | #def add_command_line_arguments_contraction(parsetext): 14 | # parser = argparse.ArgumentParser(description=parsetext) 15 | # parser.add_argument("-j1", "--istart", type=int, default=0, help="starting index") 16 | # parser.add_argument("-j2", "--iend", type=int, default=0, help="ending index") 17 | # parser.add_argument("-iconf", "--iselection", type=int, default=0, help="selected conf") 18 | # args = parser.parse_args() 19 | # return args 20 | 21 | #args = add_command_line_arguments_contraction("dataset subselection") 22 | # dataset slice boundaries 23 | #istart = args.istart-1 24 | #iend = args.iend 25 | #isel = args.iselection # 0 based 26 | 27 | ocut = inp.overcut 28 | 29 | # init averages 30 | av_coefs = {} 31 | for spe in spelist: 32 | av_coefs[spe] = np.zeros(nmax[(spe,0)],float) 33 | 34 | # compute averages 35 | for iconf in range(ndata): 36 | species = atomic_symbols[iconf] 37 | #================================================== 38 | Coef = np.load(inp.path2qm+inp.coefdir+"coefficients_conf"+str(iconf)+".npy") 39 | #================================================== 40 | i = 0 41 | for iat in range(natoms[iconf]): 42 | spe = species[iat] 43 | for l in range(lmax[spe]+1): 44 | for n in range(nmax[(spe,l)]): 45 | for im in range(2*l+1): 46 | if l==0: 47 | av_coefs[spe][n] += Coef[i] 48 | i += 1 49 | 50 | nenv = {} 51 | for spe in spelist: 52 | nenv[spe] = 0 53 | for iconf in range(ndata): 54 | nenv[spe] += natoms_spe[iconf,spe] 55 | 56 | # save averages 57 | for spe in spelist: 58 | av_coefs[spe] /= nenv[spe] 59 | np.save("averages_"+str(spe)+".npy",av_coefs[spe]) 60 | for l in range(lmax[spe]+1): 61 | for n in range(nmax[(spe,l)]): 62 | dirpath = os.path.join(inp.path2qm+inp.projdir, "spe"+str(spe)+"_l"+str(l)+"_n"+str(n)) 63 | if not os.path.exists(dirpath): 64 | os.mkdir(dirpath) 65 | 66 | #for iconf in range(istart,iend): 67 | for iconf in range(ndata): 68 | print(iconf+1) 69 | 70 | start = time.time() 71 | species = atomic_symbols[iconf] 72 | # init orthogonal projections 73 | projs = {} 74 | for spe in spelist: 75 | for l in range(lmax[spe]+1): 76 | for n in range(nmax[(spe,l)]): 77 | projs[(spe,l,n)] = np.zeros((natoms_spe[iconf,spe],(2*l+1))) 78 | # compute coefficients 79 | Coef = np.load(inp.path2qm+inp.coefdir+"coefficients_conf"+str(iconf)+".npy") 80 | Over = np.load(inp.path2qm+inp.ovlpdir+"overlap_conf"+str(iconf)+".npy") 81 | # remove L=0 average 82 | i = 0 83 | for iat in range(natoms[iconf]): 84 | spe = species[iat] 85 | for l in range(lmax[spe]+1): 86 | for n in range(nmax[(spe,l)]): 87 | for im in range(2*l+1): 88 | if l==0: 89 | Coef[i] -= av_coefs[spe][n] 90 | i += 1 91 | # compute baselined projections 92 | DProj = np.dot(Over,Coef) 93 | # compute orthogonalization matrix 94 | eigenvalues, unitary = np.linalg.eigh(Over) 95 | eigenvalues = eigenvalues[eigenvalues>ocut] 96 | Mcut = len(eigenvalues) 97 | sqrteigen = np.sqrt(eigenvalues) 98 | diagoverlap = np.diag(1.0/sqrteigen) 99 | orthomatrix = np.dot(np.conj(unitary[:,-Mcut:]),np.dot(diagoverlap,unitary[:,-Mcut:].T)) 100 | np.save(inp.path2qm+inp.ovlpdir+"orthomatrix_"+str(iconf)+".npy",orthomatrix) 101 | # orthogonalize projections 102 | OProj = np.dot(orthomatrix,DProj) 103 | # init species counting 104 | specount = {} 105 | for spe in spelist: 106 | specount[spe] = 0 107 | # fill array of orthogonal projections 108 | i = 0 109 | for iat in range(natoms[iconf]): 110 | spe = species[iat] 111 | for l in range(lmax[spe]+1): 112 | for n in range(nmax[(spe,l)]): 113 | for im in range(2*l+1): 114 | projs[(spe,l,n)][specount[spe],im] = OProj[i] 115 | i += 1 116 | specount[spe] += 1 117 | # save orthogonal projections 118 | for spe in spelist: 119 | for l in range(lmax[spe]+1): 120 | for n in range(nmax[(spe,l)]): 121 | np.save(inp.path2qm+inp.projdir+"spe"+str(spe)+"_l"+str(l)+"_n"+str(n)+"/ortho_projections_conf"+str(iconf)+".npy",projs[(spe,l,n)].reshape(natoms_spe[(iconf,spe)]*(2*l+1))) 122 | 123 | print((time.time()-start)/60.0, "minutes") 124 | -------------------------------------------------------------------------------- /salted/ortho/ortho_regression.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import random 4 | import sys 5 | sys.path.insert(0, './') 6 | import inp 7 | from sys_utils import read_system, get_atom_idx 8 | 9 | spelist, lmax, nmax, llmax, nnmax, ndata, atomic_symbols, natoms, natmax = read_system() 10 | atom_idx, natom_dict = get_atom_idx(ndata,natoms,spelist,atomic_symbols) 11 | 12 | # number of sparse environments 13 | M = inp.Menv 14 | # number of training configurations 15 | N = inp.Ntrain 16 | # training set fraction 17 | frac = inp.trainfrac 18 | # number of sparse environments 19 | reg = inp.regul 20 | eigcut = inp.eigcut 21 | kdir = inp.kerndir 22 | pdir = inp.valcdir 23 | 24 | dirpath = os.path.join(inp.path2qm, pdir) 25 | if not os.path.exists(dirpath): 26 | os.mkdir(dirpath) 27 | dirpath = os.path.join(inp.path2qm+pdir, "M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))) 28 | if not os.path.exists(dirpath): 29 | os.mkdir(dirpath) 30 | 31 | # training set selection 32 | dataset = list(range(ndata)) 33 | random.Random(3).shuffle(dataset) 34 | trainrangetot = dataset[:N] 35 | np.savetxt("training_set.txt",trainrangetot,fmt='%i') 36 | #trainrangetot = np.loadtxt("training_set2.txt",int) 37 | ntrain = int(frac*len(trainrangetot)) 38 | trainrange = trainrangetot[0:ntrain] 39 | natoms_train = natoms[trainrange] 40 | print("Number of training configurations =", ntrain) 41 | testrange = np.setdiff1d(list(range(ndata)),trainrangetot) 42 | ntest = len(testrange) 43 | natoms_test = natoms[testrange] 44 | 45 | ortho_preds = np.zeros((ntest,natmax,llmax+1,nnmax,2*llmax+1)) 46 | for spe in spelist: 47 | 48 | for l in range(lmax[spe]+1): 49 | 50 | # get truncated size 51 | Mcut = np.load(inp.path2ml+kdir+"spe"+str(spe)+"_l"+str(l)+"/M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/psi-nm_conf"+str(0)+".npy").shape[1] 52 | # compute B matrix 53 | B = np.zeros((Mcut,Mcut)) 54 | for iconf in trainrange: 55 | psi_nm = np.load(inp.path2ml+kdir+"spe"+str(spe)+"_l"+str(l)+"/M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/psi-nm_conf"+str(iconf)+".npy") 56 | B += np.dot(psi_nm.T,psi_nm) 57 | B /= ntrain 58 | 59 | for n in range(nmax[(spe,l)]): 60 | 61 | # compute A vector 62 | A = np.zeros(Mcut) 63 | for iconf in trainrange: 64 | psi_nm = np.load(inp.path2ml+kdir+"spe"+str(spe)+"_l"+str(l)+"/M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/psi-nm_conf"+str(iconf)+".npy") 65 | ortho_projs = np.load(inp.path2qm+inp.projdir+"spe"+str(spe)+"_l"+str(l)+"_n"+str(n)+"/ortho_projections_conf"+str(iconf)+".npy") 66 | 67 | A += np.dot(psi_nm.T,ortho_projs) 68 | A /= ntrain 69 | 70 | print("") 71 | print("spe:",spe,"L:",l,"n:",n) 72 | print("------------------------") 73 | 74 | x = np.linalg.solve( B + reg*np.eye(Mcut) , A ) 75 | 76 | error_total = 0 77 | variance = 0 78 | itest = 0 79 | for iconf in testrange: 80 | 81 | # predict 82 | psi_nm = np.load(inp.path2ml+kdir+"spe"+str(spe)+"_l"+str(l)+"/M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/psi-nm_conf"+str(iconf)+".npy") 83 | ortho_projs = np.dot(psi_nm,x) 84 | 85 | # reference 86 | ortho_projs_ref = np.load(inp.path2qm+inp.projdir+"spe"+str(spe)+"_l"+str(l)+"_n"+str(n)+"/ortho_projections_conf"+str(iconf)+".npy") 87 | 88 | # compute error 89 | delta = ortho_projs-ortho_projs_ref 90 | error_total += np.dot(delta,delta) 91 | variance += np.dot(ortho_projs_ref,ortho_projs_ref) 92 | #print iconf+1, ":", np.sqrt(error/var)*100, "% RMSE" 93 | 94 | i = 0 95 | for iat in atom_idx[(iconf,spe)]: 96 | for im in range(2*l+1): 97 | ortho_preds[itest,iat,l,n,im] = ortho_projs.reshape(len(atom_idx[(iconf,spe)]),2*l+1)[i,im] 98 | i+=1 99 | itest += 1 100 | 101 | print("% RMSE =", 100*np.sqrt(error_total/variance)) 102 | 103 | np.save(inp.path2qm+pdir+"M"+str(M)+"_eigcut"+str(int(np.log10(eigcut)))+"/ortho-predictions_N"+str(ntrain)+"_reg"+str(int(np.log10(reg)))+".npy",ortho_preds) 104 | -------------------------------------------------------------------------------- /salted/pyscf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreagrisafi/SALTED/15e84a809b6b674793f19763d62acc2492a7b108/salted/pyscf/__init__.py -------------------------------------------------------------------------------- /salted/pyscf/_deprecated_/run-pyscf_deprecated.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import sys 4 | 5 | import numpy as np 6 | from ase.io import read 7 | from pyscf import gto 8 | from pyscf import scf,dft 9 | from pyscf import grad 10 | from scipy import special 11 | 12 | from salted.sys_utils import ParseConfig 13 | 14 | inp = ParseConfig().parse_input() 15 | 16 | def add_command_line_arguments(parsetext): 17 | parser = argparse.ArgumentParser(description=parsetext,formatter_class=argparse.ArgumentDefaultsHelpFormatter) 18 | parser.add_argument("-iconf", "--confidx", type=int, default=-1, help="Structure index") 19 | args = parser.parse_args() 20 | return args 21 | 22 | def set_variable_values(args): 23 | iconf = args.confidx 24 | return iconf 25 | 26 | args = add_command_line_arguments("") 27 | iconf = set_variable_values(args) 28 | 29 | # Initialize geometry 30 | geoms = read(inp.system.filename,":") 31 | 32 | dirpath = inp.qm.path2qm 33 | if not os.path.exists(dirpath): 34 | os.mkdir(dirpath) 35 | 36 | if iconf != -1: 37 | print("Calculating density matrix for configuration", iconf) 38 | iconf -= 1 # 0-based indexing 39 | conf_list = [iconf] 40 | else: 41 | conf_list = range(len(geoms)) 42 | 43 | for iconf in conf_list: 44 | geom = geoms[iconf] 45 | symb = geom.get_chemical_symbols() 46 | coords = geom.get_positions() 47 | natoms = len(coords) 48 | atoms = [] 49 | for i in range(natoms): 50 | coord = coords[i] 51 | atoms.append([symb[i],(coord[0],coord[1],coord[2])]) 52 | 53 | # Get PySCF objects for wave-function and density-fitted basis 54 | mol = gto.M(atom=atoms,basis=inp.qm.qmbasis) 55 | m = dft.RKS(mol) 56 | m.xc = inp.qm.functional 57 | # Save density matrix 58 | m.kernel() 59 | 60 | #ks_scanner = m.apply(grad.RKS).as_scanner() 61 | #etot, grad = ks_scanner(mol) 62 | # 63 | #f = open("gradients/grad_conf"+str(iconf+1)+".dat","w") 64 | #for i in range(natoms): 65 | # print >> f, symb[i], grad[i,0], grad[i,1], grad[i,2] 66 | #f.close() 67 | 68 | dm = m.make_rdm1() 69 | 70 | dirpath = os.path.join(inp.qm.path2qm, "density_matrices") 71 | if not os.path.exists(dirpath): 72 | os.mkdir(dirpath) 73 | 74 | np.save(os.path.join(dirpath, f"dm_conf{iconf+1}.npy"), dm) 75 | -------------------------------------------------------------------------------- /salted/pyscf/get_basis_info.py: -------------------------------------------------------------------------------- 1 | """Translate basis info from PySCF calculation to SALTED basis info""" 2 | 3 | from typing import Dict, List 4 | 5 | from pyscf import df 6 | from pyscf.gto import basis 7 | 8 | from salted.basis_client import ( 9 | BasisClient, 10 | SpeciesBasisData, 11 | ) 12 | from salted.get_basis_info import get_parser 13 | from salted.sys_utils import ParseConfig 14 | 15 | 16 | def build(dryrun: bool = False, force_overwrite: bool = False): 17 | """Scheme: load density fitting basis from pyscf module, 18 | update the basis_data dict, 19 | and write to the database when all species are recorded. 20 | """ 21 | inp = ParseConfig().parse_input() 22 | assert inp.qm.qmcode.lower() == "pyscf", f"{inp.qm.qmcode=}, but expected 'pyscf'" 23 | 24 | spe_set = set(inp.system.species) # remove duplicates 25 | qmbasis = inp.qm.qmbasis 26 | 27 | """load density fitting basis from pyscf module""" 28 | basis_data: Dict[str, SpeciesBasisData] = load_from_pyscf(list(spe_set), qmbasis) 29 | 30 | """write to the database""" 31 | if dryrun: 32 | print("Dryrun mode, not writing to the database") 33 | print(f"{basis_data=}") 34 | else: 35 | BasisClient().write(inp.qm.dfbasis, basis_data, force_overwrite) 36 | 37 | 38 | 39 | def load_from_pyscf(species_list: List[str], qmbasis: str): 40 | """load the xxx-jkfit density fitting basis from PySCF 41 | 42 | Args: 43 | species_list: list of species, e.g. [H, O] 44 | qmbasis: quantum chemistry basis set name, e.g. cc-pvdz 45 | 46 | Returns: 47 | Dict[str, SpeciesBasisData]: species and basis data 48 | """ 49 | ribasis = df.addons.DEFAULT_AUXBASIS[basis._format_basis_name(qmbasis)][0] # get the proper DF basis name in PySCF 50 | print(f"{species_list=}, {qmbasis=}, and the parsed {ribasis=}") 51 | spe_ribasis_info = {spe: basis.load(ribasis, spe) for spe in species_list} # load with PySCF basis module 52 | """ 53 | Each dict value is like: 54 | format: [angular_momentum, [exponents, coefficients], ...] 55 | there might be multiple [exponents, coefficients] for one atomic orbital 56 | [ 57 | [ 58 | 0, 59 | [883.9992943, 0.33024477], 60 | [286.8428015, 0.80999791], 61 | ], 62 | [0, [48.12711454, 1.0]], 63 | [0, [2.50686566, 1.0]], 64 | [0, [0.1918516, 1.0]], 65 | [1, [102.99176249, 1.0]], 66 | [1, [3.3490545, 1.0]], 67 | [1, [0.20320063, 1.0]], 68 | [2, [10.59406836, 1.0]], 69 | [2, [0.51949765, 1.0]], 70 | ... 71 | ] 72 | 73 | Extract the l numbers and compose the Dict[str, SpeciesBasisData] (species and basis data) 74 | """ 75 | basis_data = {spe: collect_l_nums(ribasis_info) for spe, ribasis_info in spe_ribasis_info.items()} 76 | return basis_data 77 | 78 | 79 | # def collect_l_nums(data:List[int, List[float]]) -> SpeciesBasisData: 80 | # use Annotated 81 | def collect_l_nums(data: List) -> SpeciesBasisData: 82 | """collect l numbers for each species based on the data from PySCF 83 | input: above dict value, 84 | e.g. 85 | [ 86 | [ 87 | 0, 88 | [883.9992943, 0.33024477], 89 | [286.8428015, 0.80999791], 90 | ], 91 | [0, [48.12711454, 1.0]], 92 | [1, [102.99176249, 1.0]], 93 | [2, [10.59406836, 1.0]], 94 | ... 95 | ] 96 | there might be multiple [exponents, coefficients] for one atomic orbital 97 | output: max l number, and a list of counts of each l number 98 | """ 99 | l_nums = [d for d, *_ in data] # [0, 0, 0, 0, 1, 1, 1, 2, 2, ...] 100 | l_max = max(l_nums) 101 | l_cnt = [0 for _ in range(l_max + 1)] # [0, 0, 0, ...] to the max l number 102 | for l_num in l_nums: 103 | l_cnt[l_num] += 1 104 | return { 105 | "lmax": max(l_nums), 106 | "nmax": l_cnt, 107 | } 108 | 109 | 110 | 111 | if __name__ == "__main__": 112 | print("Please call `python -m salted.get_basis_info` instead of this file") 113 | 114 | parser = get_parser() 115 | args = parser.parse_args() 116 | 117 | build(dryrun=args.dryrun, force_overwrite=args.force_overwrite) 118 | 119 | -------------------------------------------------------------------------------- /salted/pyscf/run_pyscf.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import sys 4 | import time 5 | from typing import List, Tuple, Union 6 | 7 | import numpy as np 8 | from ase.io import read 9 | from pyscf import dft, gto, lib 10 | 11 | from salted.sys_utils import ARGHELP_INDEX_STR, ParseConfig, parse_index_str 12 | 13 | 14 | def run_pyscf( 15 | atoms: List, 16 | basis: str, 17 | xc: str, 18 | ): 19 | mol = gto.M(atom=atoms, basis=basis, verbose=0) 20 | mf = dft.RKS(mol, xc=xc) 21 | mf.kernel() 22 | return mf.make_rdm1() 23 | 24 | 25 | def main(geom_indexes: Union[List[int], None], num_threads: int = None): 26 | inp = ParseConfig().parse_input() 27 | geoms_all = read(inp.system.filename, ":") 28 | if geom_indexes is None: 29 | geom_indexes = list(range(len(geoms_all))) 30 | else: 31 | geom_indexes = [i for i in geom_indexes if i < len(geoms_all)] # indexes start from 0 32 | print(f"Calculating density matrix for configurations: {geom_indexes}") 33 | geoms = [geoms_all[i] for i in geom_indexes] 34 | 35 | """ prepare the output directory """ 36 | dirpath = os.path.join(inp.qm.path2qm, "density_matrices") 37 | if not os.path.exists(dirpath): 38 | os.mkdir(dirpath) 39 | 40 | """ set pyscf.lib.num_threads """ 41 | if num_threads is not None: 42 | lib.num_threads(num_threads) 43 | 44 | """ do DFT calculation """ 45 | start_time = time.time() 46 | for cal_idx, (geom_idx, geom) in enumerate(zip(geom_indexes, geoms)): 47 | print(f"calcualte {geom_idx=}, progress: {cal_idx}/{len(geom_indexes)}") 48 | symb = geom.get_chemical_symbols() 49 | coords = geom.get_positions() 50 | atoms = [(s, c) for s, c in zip(symb, coords)] 51 | 52 | dm = run_pyscf(atoms, inp.qm.qmbasis, inp.qm.functional) 53 | np.save(os.path.join(dirpath, f"dm_conf{geom_idx+1}.npy"), dm) 54 | end_time = time.time() 55 | print(f"Calculation finished, wall time cost on DFT: {end_time - start_time:.2f}s") 56 | 57 | 58 | if __name__ == "__main__": 59 | parser = argparse.ArgumentParser() 60 | # create a parser obj, which accepts the indexes to calculate, start from 0 61 | # formats: 1,2,3 or 1-3 or None (all structures) 62 | parser.add_argument( 63 | "-i", "--idx", type=str, default="all", 64 | help=ARGHELP_INDEX_STR, 65 | ) 66 | parser.add_argument( 67 | "-c", "--cpu", type=int, default=None, 68 | help="Number of CPU cores to use. Default is None (for do nothing)." 69 | ) 70 | args = parser.parse_args() 71 | 72 | main(parse_index_str(args.idx), args.cpu) 73 | -------------------------------------------------------------------------------- /salted/scalar_vector.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | import os.path as osp 5 | from ase.io import read 6 | import h5py 7 | 8 | import numpy as np 9 | from scipy import sparse 10 | from ase.data import atomic_numbers 11 | 12 | from salted.sys_utils import read_system,get_atom_idx,get_conf_range 13 | 14 | from salted import sph_utils 15 | from salted import basis 16 | from salted.sys_utils import ParseConfig 17 | 18 | from salted.lib import equicomb 19 | from salted.lib import equicombsparse 20 | 21 | def build(): 22 | 23 | inp = ParseConfig().parse_input() 24 | # salted parameters 25 | (saltedname, saltedpath, saltedtype, 26 | filename, species, average, parallel, 27 | path2qm, qmcode, qmbasis, dfbasis, 28 | filename_pred, predname, predict_data, alpha_only, 29 | rep1, rcut1, sig1, nrad1, nang1, neighspe1, 30 | rep2, rcut2, sig2, nrad2, nang2, neighspe2, 31 | sparsify, nsamples, ncut, 32 | zeta, Menv, Ntrain, trainfrac, regul, eigcut, 33 | gradtol, restart, blocksize, trainsel, nspe1, nspe2, HYPER_PARAMETERS_DENSITY, HYPER_PARAMETERS_POTENTIAL) = ParseConfig().get_all_params() 34 | 35 | sdir = osp.join(saltedpath, f"equirepr_{saltedname}") 36 | 37 | if sparsify==False: 38 | # Generate directories for saving descriptors 39 | if not osp.exists(sdir): 40 | os.mkdir(sdir) 41 | 42 | species, lmax, nmax, lmax_max, nnmax, ndata, atomic_symbols, natoms, natmax = read_system() 43 | atom_idx, natom_dict = get_atom_idx(ndata,natoms,species,atomic_symbols) 44 | 45 | # Load feature space sparsification information if required 46 | if sparsify: 47 | vfps = {} 48 | for lam in range(lmax_max+1): 49 | vfps[lam] = np.load(osp.join( 50 | saltedpath, f"equirepr_{saltedname}", f"fps{ncut}-{lam}.npy" 51 | )) 52 | 53 | frames = read(filename,":") 54 | natoms_total = sum(natoms) 55 | conf_range = range(ndata) 56 | 57 | lam = 0 58 | llmax, llvec = sph_utils.get_angular_indexes_symmetric(lam,nang1,nang2) 59 | 60 | # Load the relevant Wigner-3J symbols associated with the given triplet (lam, lmax1, lmax2) 61 | wigner3j = np.loadtxt(os.path.join( 62 | saltedpath, "wigners", f"wigner_lam-{lam}_lmax1-{nang1}_lmax2-{nang2}.dat" 63 | )) 64 | wigdim = wigner3j.size 65 | 66 | omega1 = sph_utils.get_representation_coeffs(frames,rep1,HYPER_PARAMETERS_DENSITY,HYPER_PARAMETERS_POTENTIAL,0,neighspe1,species,nang1,nrad1,natoms_total) 67 | omega2 = sph_utils.get_representation_coeffs(frames,rep2,HYPER_PARAMETERS_DENSITY,HYPER_PARAMETERS_POTENTIAL,0,neighspe2,species,nang2,nrad2,natoms_total) 68 | 69 | # Reshape arrays of expansion coefficients for optimal Fortran indexing 70 | v1 = np.transpose(omega1,(2,0,3,1)) 71 | v2 = np.transpose(omega2,(2,0,3,1)) 72 | 73 | # Compute complex to real transformation matrix for the given lambda value 74 | c2r = sph_utils.complex_to_real_transformation([2*lam+1])[0] 75 | 76 | start = time.time() 77 | 78 | if sparsify: 79 | 80 | featsize = nspe1*nspe2*nrad1*nrad2*llmax 81 | nfps = len(vfps[lam]) 82 | p = equicombsparse.equicombsparse(natoms_total,nang1,nang2,nspe1*nrad1,nspe2*nrad2,v1,v2,wigdim,wigner3j,llmax,llvec.T,lam,c2r,featsize,nfps,vfps[lam]) 83 | p = np.transpose(p,(2,0,1)) 84 | featsize = ncut 85 | 86 | else: 87 | 88 | featsize = nspe1*nspe2*nrad1*nrad2*llmax 89 | p = equicomb.equicomb(natoms_total,nang1,nang2,nspe1*nrad1,nspe2*nrad2,v1,v2,wigdim,wigner3j,llmax,llvec.T,lam,c2r,featsize) 90 | p = np.transpose(p,(2,0,1)) 91 | 92 | print("time = ", time.time()-start) 93 | 94 | #TODO modify SALTED to directly deal with compact natoms_total dimension 95 | p = p.reshape(natoms_total,featsize) 96 | pvec = np.zeros((ndata,natmax,featsize)) 97 | 98 | j = 0 99 | for i,iconf in enumerate(conf_range): 100 | for iat in range(natoms[iconf]): 101 | pvec[i,iat] = p[j] 102 | j += 1 103 | 104 | h5f = h5py.File(osp.join(sdir, f"FEAT-0.h5"), 'w') 105 | h5f.create_dataset("descriptor",data=pvec) 106 | h5f.close() 107 | 108 | if __name__ == "__main__": 109 | build() 110 | -------------------------------------------------------------------------------- /salted/solve_regression.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path as osp 3 | import sys 4 | import time 5 | 6 | import numpy as np 7 | 8 | from salted.sys_utils import ParseConfig 9 | 10 | 11 | def build(): 12 | 13 | inp = ParseConfig().parse_input() 14 | saltedname, saltedpath = inp.salted.saltedname, inp.salted.saltedpath 15 | 16 | # sparse-GPR parameters 17 | Menv = inp.gpr.Menv 18 | regul = inp.gpr.regul 19 | zeta = inp.gpr.z 20 | 21 | fdir = f"rkhs-vectors_{saltedname}" 22 | rdir = f"regrdir_{saltedname}" 23 | 24 | # define training set size 25 | ntrain = round(inp.gpr.trainfrac*inp.gpr.Ntrain) 26 | 27 | # load regression matrices 28 | Avec = np.load(osp.join(saltedpath, rdir, f"M{Menv}_zeta{zeta}", f"Avec_N{ntrain}.npy")) 29 | totsize = Avec.shape[0] 30 | print("problem dimensionality:", totsize,flush=True) 31 | if totsize > 100000: 32 | raise ValueError(f"problem dimension too large ({totsize=}), minimize directly loss-function instead!") 33 | Bmat = np.load(osp.join(saltedpath, rdir, f"M{Menv}_zeta{zeta}", f"Bmat_N{ntrain}.npy")) 34 | 35 | start = time.time() 36 | 37 | w = np.linalg.solve(Bmat+np.eye(totsize)*regul,Avec) 38 | 39 | print(f"regression time: {((time.time()-start)/60):.3f} minutes",flush=True) 40 | 41 | np.save(osp.join(saltedpath, rdir, f"M{Menv}_zeta{zeta}", f"weights_N{ntrain}_reg{int(np.log10(regul))}.npy"), w) 42 | 43 | return 44 | 45 | if __name__ == "__main__": 46 | build() 47 | -------------------------------------------------------------------------------- /salted/sparse_selection.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import sys 3 | import h5py 4 | import os 5 | import os.path as osp 6 | 7 | from salted.sys_utils import ParseConfig, read_system, get_atom_idx, do_fps 8 | 9 | def build(): 10 | inp = ParseConfig().parse_input() 11 | 12 | species, lmax, nmax, llmax, nnmax, ndata, atomic_symbols, natoms, natmax = read_system() 13 | 14 | atom_idx, natom_dict = get_atom_idx(ndata,natoms,species,atomic_symbols) 15 | 16 | # number of sparse environments 17 | M, zeta, eigcut = inp.gpr.Menv, inp.gpr.z, inp.gpr.eigcut 18 | sdir = osp.join(inp.salted.saltedpath, f"equirepr_{inp.salted.saltedname}") 19 | 20 | # compute number of atomic environments for each species 21 | ispe = 0 22 | species_idx = {} 23 | for spe in species: 24 | species_idx[spe] = ispe 25 | ispe += 1 26 | 27 | species_array = np.zeros((ndata,natmax),int) 28 | natoms_total = 0 29 | for iconf in range(ndata): 30 | for iat in range(natoms[iconf]): 31 | spe = atomic_symbols[iconf][iat] 32 | species_array[iconf,iat] = species_idx[spe] 33 | natoms_total += 1 34 | species_array = species_array.reshape(ndata*natmax) 35 | 36 | # load lambda=0 power spectrum 37 | power = h5py.File(osp.join(sdir, "FEAT-0.h5"), 'r')['descriptor'][:] 38 | nfeat = power.shape[-1] 39 | 40 | power_dense = np.zeros((natoms_total,nfeat)) 41 | idx = 0 42 | for iconf in range(ndata): 43 | power_dense[idx:idx+natoms[iconf]] = power[iconf,:natoms[iconf]] 44 | idx += natoms[iconf] 45 | # compute sparse set with FPS 46 | fps_idx = np.array(do_fps(power_dense,M),int) 47 | fps_species = species_array[fps_idx] 48 | sparse_set = np.vstack((fps_idx,fps_species)).T 49 | print("Computed sparse set made of ", M, "environments") 50 | np.savetxt(osp.join(sdir, f"sparse_set_{M}.txt"), sparse_set, fmt='%i') 51 | 52 | if __name__ == "__main__": 53 | build() 54 | -------------------------------------------------------------------------------- /salted/sparsify_features.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | import sys 4 | import time 5 | import os.path as osp 6 | 7 | import numpy as np 8 | import h5py 9 | from ase.data import atomic_numbers 10 | from ase.io import read 11 | 12 | from salted import sph_utils 13 | from salted import basis 14 | 15 | from salted.lib import equicomb, equicombfps 16 | from salted.sys_utils import ParseConfig, read_system, get_atom_idx, get_conf_range, do_fps 17 | 18 | def build(): 19 | 20 | inp = ParseConfig().parse_input() 21 | (saltedname, saltedpath, saltedtype, 22 | filename, species, average, parallel, 23 | path2qm, qmcode, qmbasis, dfbasis, 24 | filename_pred, predname, predict_data, alpha_only, 25 | rep1, rcut1, sig1, nrad1, nang1, neighspe1, 26 | rep2, rcut2, sig2, nrad2, nang2, neighspe2, 27 | sparsify, nsamples, ncut, 28 | zeta, Menv, Ntrain, trainfrac, regul, eigcut, 29 | gradtol, restart, blocksize, trainsel, nspe1, nspe2, HYPER_PARAMETERS_DENSITY, HYPER_PARAMETERS_POTENTIAL) = ParseConfig().get_all_params() 30 | 31 | # Generate directories for saving descriptors 32 | sdir = osp.join(saltedpath, f"equirepr_{saltedname}") 33 | if not osp.exists(sdir): 34 | os.mkdir(sdir) 35 | 36 | if not sparsify: 37 | print( 38 | "ERROR: inp parameter sparsify=False. " 39 | "Make sure to include a sparsify section with ncut>0 if you want to sparsify the descriptor\n", 40 | file=sys.stderr 41 | ) 42 | sys.exit(1) 43 | 44 | species, lmax, nmax, lmax_max, nnmax, ndata, atomic_symbols, natoms, natmax = read_system() 45 | atom_idx, natom_dict = get_atom_idx(ndata,natoms,species,atomic_symbols) 46 | 47 | start = time.time() 48 | 49 | ndata_true = ndata 50 | print(f"The dataset contains {ndata_true} frames.") 51 | 52 | conf_range = list(range(ndata_true)) 53 | random.Random(3).shuffle(conf_range) 54 | 55 | if nsamples <= ndata: 56 | ndata = nsamples 57 | else: 58 | print("ERROR: nsamples cannot be greater than ndata!") 59 | sys.exit(1) 60 | 61 | conf_range = conf_range[:ndata] 62 | print(f"Selected {ndata} frames.") 63 | 64 | frames = read(filename,":") 65 | frames = list( frames[i] for i in conf_range ) 66 | natoms = list( natoms[i] for i in conf_range ) 67 | natoms_total = sum(natoms) 68 | 69 | omega1 = sph_utils.get_representation_coeffs(frames,rep1,HYPER_PARAMETERS_DENSITY,HYPER_PARAMETERS_POTENTIAL,0,neighspe1,species,nang1,nrad1,natoms_total) 70 | omega2 = sph_utils.get_representation_coeffs(frames,rep2,HYPER_PARAMETERS_DENSITY,HYPER_PARAMETERS_POTENTIAL,0,neighspe2,species,nang2,nrad2,natoms_total) 71 | 72 | # Reshape arrays of expansion coefficients for optimal Fortran indexing 73 | v1 = np.transpose(omega1,(2,0,3,1)) 74 | v2 = np.transpose(omega2,(2,0,3,1)) 75 | 76 | # Compute equivariant descriptors for each lambda value entering the SPH expansion of the electron density 77 | for lam in range(lmax_max+1): 78 | 79 | llmax, llvec = sph_utils.get_angular_indexes_symmetric(lam,nang1,nang2) 80 | 81 | # Load the relevant Wigner-3J symbols associated with the given triplet (lam, lmax1, lmax2) 82 | wigner3j = np.loadtxt(osp.join(saltedpath, "wigners", f"wigner_lam-{lam}_lmax1-{nang1}_lmax2-{nang2}.dat")) 83 | wigdim = wigner3j.size 84 | 85 | # Compute complex to real transformation matrix for the given lambda value 86 | c2r = sph_utils.complex_to_real_transformation([2*lam+1])[0] 87 | 88 | # compute normalized equivariant descriptor 89 | featsize = nspe1*nspe2*nrad1*nrad2*llmax 90 | 91 | print(f"lambda = {lam}, feature space size = {featsize}") 92 | 93 | # Do feature selection with FPS sparsification 94 | if ncut >= featsize: 95 | print("ERROR: requested number of sparse features larger than total feature space size! Please get rid of the inp.descriptor.sparsify section.") 96 | sys.exit(1) 97 | 98 | pvec = equicombfps.equicombfps(natoms_total,nang1,nang2,nspe1*nrad1,nspe2*nrad2,v1,v2,wigdim,wigner3j,llmax,llvec.T,lam,c2r,featsize) 99 | vfps = do_fps(pvec,ncut) 100 | np.save(osp.join(sdir, f"fps{ncut}-{lam}.npy"), vfps) 101 | 102 | if __name__ == "__main__": 103 | build() 104 | -------------------------------------------------------------------------------- /salted/wigner.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | import os.path as osp 5 | import io 6 | 7 | import ase 8 | import numpy as np 9 | from sympy.physics.wigner import wigner_3j 10 | 11 | from salted import sph_utils 12 | from salted.sys_utils import ParseConfig 13 | 14 | def build(): 15 | inp = ParseConfig().parse_input() 16 | 17 | from salted.sys_utils import read_system, get_atom_idx 18 | species, lmax, nmax, lmax_max, nnmax, ndata, atomic_symbols, natoms, natmax = read_system() 19 | atom_idx, natom_dict = get_atom_idx(ndata,natoms,species,atomic_symbols) 20 | 21 | nang1, nang2 = inp.descriptor.rep1.nang, inp.descriptor.rep2.nang 22 | 23 | # Generate directories for saving descriptors 24 | dirpath = os.path.join(inp.salted.saltedpath, "wigners") 25 | if not os.path.exists(dirpath): 26 | os.mkdir(dirpath) 27 | 28 | def get_wigner3j(llmax:int, llvec:np.ndarray, lam:int, wig:io.TextIOWrapper): 29 | """Compute and save Wigner-3J symbols needed for symmetry-adapted combination""" 30 | 31 | for il in range(llmax): 32 | l1 = int(llvec[il,0]) 33 | l2 = int(llvec[il,1]) 34 | for imu in range(2*lam+1): 35 | mu = imu-lam 36 | for im1 in range(2*l1+1): 37 | m1 = im1-l1 38 | m2 = m1-mu 39 | if abs(m2) <= l2: 40 | im2 = m2+l2 41 | # for wigner_3j, all the parameters should be integers or half-integers 42 | w3j = wigner_3j(lam,l2,l1,mu,m2,-m1) * (-1.0)**(m1) 43 | print(float(w3j),file=wig) 44 | 45 | if inp.salted.saltedtype=="density-response": 46 | lmax_max += 1 47 | for spe in species: 48 | lmax[spe] += 1 49 | 50 | for lam in range(lmax_max+1): 51 | 52 | [llmax,llvec] = sph_utils.get_angular_indexes_symmetric(lam,nang1,nang2) 53 | 54 | wig = open(osp.join( 55 | inp.salted.saltedpath, "wigners", f"wigner_lam-{lam}_lmax1-{nang1}_lmax2-{nang2}.dat" 56 | ), "a") 57 | get_wigner3j(llmax,llvec,lam,wig) 58 | wig.close() 59 | 60 | if inp.salted.saltedtype=="density-response": 61 | 62 | for lam in range(1,lmax_max): 63 | 64 | llmax, llvec = sph_utils.get_angular_indexes_antisymmetric(lam,nang1,nang2) 65 | 66 | wig = open(osp.join( 67 | inp.salted.saltedpath, "wigners", f"wigner_antisymm_lam-{lam}_lmax1-{nang1}_lmax2-{nang2}.dat" 68 | ), "a") 69 | get_wigner3j(llmax,llvec,lam,wig) 70 | wig.close() 71 | 72 | for L in [lam-1,lam,lam+1]: 73 | 74 | cgfile = open(osp.join( 75 | inp.salted.saltedpath, "wigners", f"cg_response_lam-{lam}_L-{L}.dat" 76 | ), "a") 77 | 78 | icg = 0 79 | for imu in range(2*lam+1): 80 | mu = imu-lam 81 | for ik in range(3): 82 | k = ik-1 83 | M = mu+k 84 | if abs(M)<=L: 85 | cg = wigner_3j(lam,1,L,mu,k,-M) * (-1.0)**(-lam+1-M) * np.sqrt(float(2*L+1)) 86 | print(float(cg),file=cgfile) 87 | icg += 1 88 | 89 | cgfile.close() 90 | 91 | if __name__ == "__main__": 92 | build() 93 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='salted', 5 | version='3.0.0', 6 | description='Symmetry-Adapted Learning of Three-Dimensional Electron Densities', 7 | url='https://github.com/andreagrisafi/SALTED', 8 | author='Andrea Grisafi, Alan Lewis', 9 | author_email='andrea.grisafi@ens.psl.eu, alan.m.lewis@york.ac.uk', 10 | license='GNU GENERAL PUBLIC LICENSE', 11 | packages=['salted','salted.cp2k','salted.pyscf','salted.aims','salted.lib'], 12 | install_requires=['mpi4py','featomic','ase','numpy','scipy','h5py','sympy','pyyaml'], 13 | include_package_data=True, 14 | package_data={"salted": ["salted/lib/*.so"]}, 15 | classifiers=[ 16 | 'Development Status :: 1 - Planning', 17 | 'Intended Audience :: Science/Research', 18 | 'License :: OSI Approved :: BSD License', 19 | 'Operating System :: POSIX :: Linux', 20 | 'Programming Language :: Python :: 3', 21 | ], 22 | ) 23 | -------------------------------------------------------------------------------- /src/antiequicomb.f90: -------------------------------------------------------------------------------- 1 | SUBROUTINE antiequicomb(natoms,nang1,nang2,nrad1,nrad2,v1,v2,& 2 | wigdim,w3j,llmax,llvec,lam,c2r,featsize,p) 3 | 4 | !use omp_lib 5 | IMPLICIT NONE 6 | INTEGER:: natoms,nang1,nang2,nrad1,nrad2,llmax,lam,wigdim,ifeat 7 | INTEGER:: iat,n1,n2,iwig,l1,l2,il,imu,im1,im2,mu,m1,m2,featsize 8 | INTEGER, DIMENSION(2,llmax):: llvec 9 | REAL*8, DIMENSION(wigdim):: w3j 10 | REAL*8, DIMENSION(2*lam+1):: pimag 11 | COMPLEX*16, DIMENSION(2*lam+1):: pcmplx 12 | COMPLEX*16, DIMENSION(2*lam+1,2*lam+1):: c2r 13 | COMPLEX*16, DIMENSION(2*nang1+1,nang1+1,nrad1,natoms):: v1 14 | COMPLEX*16, DIMENSION(2*nang2+1,nang2+1,nrad2,natoms):: v2 15 | REAL*8, DIMENSION(2*lam+1,featsize):: ptemp 16 | REAL*8, DIMENSION(2*lam+1,featsize,natoms):: p 17 | REAL*8:: inner, normfact 18 | 19 | !f2py intent(in) natoms,nang1,nang2,nrad1,nrad2,v1,v2,wigdim,w3j,llmax,llvec,lam,c2r 20 | !f2py intent(in) featsize 21 | !f2py intent(out) p 22 | !f2py depend(natoms) p, v1, v2 23 | !f2py depend(nrad1) v1 24 | !f2py depend(nrad2) v2 25 | !f2py depend(nang1) v1 26 | !f2py depend(nang2) v2 27 | !f2py depend(lam) p, c2r 28 | !f2py depend(llmax) llvec 29 | !f2py depend(wigdim) w3j 30 | !f2py depend(featsize) p 31 | 32 | p = 0.d0 33 | 34 | !$OMP PARALLEL DEFAULT(private) & 35 | !$OMP FIRSTPRIVATE(natoms,nang1,nang2,nrad1,nrad2,w3j,llmax,llvec,lam,c2r,featsize) & 36 | !$OMP SHARED(p,v1,v2) 37 | !$OMP DO SCHEDULE(dynamic) 38 | do iat=1,natoms 39 | inner = 0.0 40 | ptemp = 0.0 41 | ifeat = 1 42 | do n1=1,nrad1 43 | do n2=1,nrad2 44 | iwig = 1 45 | do il=1,llmax 46 | l1 = llvec(1,il) 47 | l2 = llvec(2,il) 48 | pcmplx = dcmplx(0.0,0.0) 49 | do imu=1,2*lam+1 50 | mu = imu-1-lam 51 | do im1=1,2*l1+1 52 | m1 = im1-1-l1 53 | m2 = m1-mu 54 | if (abs(m2)<=l2) then 55 | im2 = m2+l2+1 56 | pcmplx(imu) = pcmplx(imu) & 57 | + w3j(iwig) * v1(im1,l1+1,n1,iat) * dconjg(v2(im2,l2+1,n2,iat)) 58 | iwig = iwig + 1 59 | endif 60 | enddo 61 | enddo 62 | pimag = dimag(matmul(c2r,pcmplx)) 63 | do imu=1,2*lam+1 64 | inner = inner + pimag(imu)**2 65 | ptemp(imu,ifeat) = pimag(imu) 66 | enddo 67 | ifeat = ifeat + 1 68 | enddo 69 | enddo 70 | enddo 71 | normfact = dsqrt(inner) 72 | do ifeat=1,featsize 73 | do imu=1,2*lam+1 74 | p(imu,ifeat,iat) = ptemp(imu,ifeat) / normfact 75 | enddo 76 | enddo 77 | enddo 78 | !$OMP END DO 79 | !$OMP END PARALLEL 80 | 81 | return 82 | END 83 | -------------------------------------------------------------------------------- /src/antiequicombnonorm.f90: -------------------------------------------------------------------------------- 1 | SUBROUTINE antiequicombnonorm(natoms,nang1,nang2,nrad1,nrad2,v1,v2,& 2 | wigdim,w3j,llmax,llvec,lam,c2r,featsize,p) 3 | 4 | !use omp_lib 5 | IMPLICIT NONE 6 | INTEGER:: natoms,nang1,nang2,nrad1,nrad2,llmax,lam,wigdim,ifeat 7 | INTEGER:: iat,n1,n2,iwig,l1,l2,il,imu,im1,im2,mu,m1,m2,featsize 8 | INTEGER, DIMENSION(2,llmax):: llvec 9 | REAL*8, DIMENSION(wigdim):: w3j 10 | REAL*8, DIMENSION(2*lam+1):: pimag 11 | COMPLEX*16, DIMENSION(2*lam+1):: pcmplx 12 | COMPLEX*16, DIMENSION(2*lam+1,2*lam+1):: c2r 13 | COMPLEX*16, DIMENSION(2*nang1+1,nang1+1,nrad1,natoms):: v1 14 | COMPLEX*16, DIMENSION(2*nang2+1,nang2+1,nrad2,natoms):: v2 15 | REAL*8, DIMENSION(2*lam+1,featsize):: ptemp 16 | REAL*8, DIMENSION(2*lam+1,featsize,natoms):: p 17 | 18 | !f2py intent(in) natoms,nang1,nang2,nrad1,nrad2,v1,v2,wigdim,w3j,llmax,llvec,lam,c2r 19 | !f2py intent(in) featsize 20 | !f2py intent(out) p 21 | !f2py depend(natoms) p, v1, v2 22 | !f2py depend(nrad1) v1 23 | !f2py depend(nrad2) v2 24 | !f2py depend(nang1) v1 25 | !f2py depend(nang2) v2 26 | !f2py depend(lam) p, c2r 27 | !f2py depend(llmax) llvec 28 | !f2py depend(wigdim) w3j 29 | !f2py depend(featsize) p 30 | 31 | p = 0.d0 32 | 33 | !$OMP PARALLEL DEFAULT(private) & 34 | !$OMP FIRSTPRIVATE(natoms,nang1,nang2,nrad1,nrad2,w3j,llmax,llvec,lam,c2r,featsize) & 35 | !$OMP SHARED(p,v1,v2) 36 | !$OMP DO SCHEDULE(dynamic) 37 | do iat=1,natoms 38 | ptemp = 0.0 39 | ifeat = 1 40 | do n1=1,nrad1 41 | do n2=1,nrad2 42 | iwig = 1 43 | do il=1,llmax 44 | l1 = llvec(1,il) 45 | l2 = llvec(2,il) 46 | pcmplx = dcmplx(0.0,0.0) 47 | do imu=1,2*lam+1 48 | mu = imu-1-lam 49 | do im1=1,2*l1+1 50 | m1 = im1-1-l1 51 | m2 = m1-mu 52 | if (abs(m2)<=l2) then 53 | im2 = m2+l2+1 54 | pcmplx(imu) = pcmplx(imu) & 55 | + w3j(iwig) * v1(im1,l1+1,n1,iat) * dconjg(v2(im2,l2+1,n2,iat)) 56 | iwig = iwig + 1 57 | endif 58 | enddo 59 | enddo 60 | pimag = dimag(matmul(c2r,pcmplx)) 61 | do imu=1,2*lam+1 62 | ptemp(imu,ifeat) = pimag(imu) 63 | enddo 64 | ifeat = ifeat + 1 65 | enddo 66 | enddo 67 | enddo 68 | do ifeat=1,featsize 69 | do imu=1,2*lam+1 70 | p(imu,ifeat,iat) = ptemp(imu,ifeat) 71 | enddo 72 | enddo 73 | enddo 74 | !$OMP END DO 75 | !$OMP END PARALLEL 76 | 77 | return 78 | END 79 | -------------------------------------------------------------------------------- /src/antiequicombsparse.f90: -------------------------------------------------------------------------------- 1 | SUBROUTINE antiequicombsparse(natoms,nang1,nang2,nrad1,nrad2,v1,v2,& 2 | wigdim,w3j,llmax,llvec,lam,c2r,& 3 | featsize,nfps,vfps,p) 4 | 5 | !use omp_lib 6 | IMPLICIT NONE 7 | INTEGER:: natoms,nang1,nang2,nrad1,nrad2,llmax,lam,wigdim,ifps,ifeat,n 8 | INTEGER:: iat,n1,n2,iwig,l1,l2,il,imu,im1,im2,mu,m1,m2,featsize,nfps 9 | INTEGER, DIMENSION(nfps):: vfps 10 | INTEGER, DIMENSION(2,llmax):: llvec 11 | REAL*8, DIMENSION(wigdim):: w3j 12 | REAL*8, DIMENSION(2*lam+1):: pimag 13 | COMPLEX*16, DIMENSION(2*lam+1):: pcmplx 14 | COMPLEX*16, DIMENSION(2*lam+1,2*lam+1):: c2r 15 | COMPLEX*16, DIMENSION(2*nang1+1,nang1+1,nrad1,natoms):: v1 16 | COMPLEX*16, DIMENSION(2*nang2+1,nang2+1,nrad2,natoms):: v2 17 | REAL*8, DIMENSION(2*lam+1,featsize):: ptemp 18 | REAL*8, DIMENSION(2*lam+1,nfps,natoms):: p 19 | REAL*8:: inner,normfact 20 | 21 | !f2py intent(in) natoms,nang1,nang2,nrad1,nrad2,v1,v2,wigdim,w3j,llmax,llvec,lam,c2r 22 | !f2py intent(in) featsize, nfps, vfps 23 | !f2py intent(out) p 24 | !f2py depend(natoms) p, v1, v2 25 | !f2py depend(nrad1) v1 26 | !f2py depend(nrad2) v2 27 | !f2py depend(nang1) v1 28 | !f2py depend(nang2) v2 29 | !f2py depend(lam) p, c2r 30 | !f2py depend(llmax) llvec 31 | !f2py depend(wigdim) w3j 32 | !f2py depend(nfps) vfps, p 33 | 34 | p = 0.d0 35 | 36 | !$OMP PARALLEL DEFAULT(private) & 37 | !$OMP FIRSTPRIVATE(natoms,nang1,nang2,nrad1,nrad2,w3j,llmax,llvec,lam,c2r,nfps,vfps) & 38 | !$OMP SHARED(p,v1,v2) 39 | !$OMP DO SCHEDULE(dynamic) 40 | do iat=1,natoms 41 | inner = 0.0 42 | ptemp = 0.0 43 | ifeat = 1 44 | do n1=1,nrad1 45 | do n2=1,nrad2 46 | iwig = 1 47 | do il=1,llmax 48 | l1 = llvec(1,il) 49 | l2 = llvec(2,il) 50 | pcmplx = dcmplx(0.0,0.0) 51 | do imu=1,2*lam+1 52 | mu = imu-1-lam 53 | do im1=1,2*l1+1 54 | m1 = im1-1-l1 55 | m2 = m1-mu 56 | if (abs(m2)<=l2) then 57 | im2 = m2+l2+1 58 | pcmplx(imu) = pcmplx(imu) & 59 | + w3j(iwig) * v1(im1,l1+1,n1,iat) * dconjg(v2(im2,l2+1,n2,iat)) 60 | iwig = iwig + 1 61 | endif 62 | enddo 63 | enddo 64 | pimag = dimag(matmul(c2r,pcmplx)) 65 | do imu=1,2*lam+1 66 | inner = inner + pimag(imu)**2 67 | ptemp(imu,ifeat) = pimag(imu) 68 | enddo 69 | ifeat = ifeat + 1 70 | enddo 71 | enddo 72 | enddo 73 | normfact = dsqrt(inner) 74 | do n=1,nfps 75 | ifps = vfps(n) + 1 76 | do imu=1,2*lam+1 77 | p(imu,n,iat) = ptemp(imu,ifps) / normfact 78 | enddo 79 | enddo 80 | enddo 81 | !$OMP END DO 82 | !$OMP END PARALLEL 83 | 84 | return 85 | END 86 | -------------------------------------------------------------------------------- /src/equicomb.f90: -------------------------------------------------------------------------------- 1 | SUBROUTINE equicomb(natoms,nang1,nang2,nrad1,nrad2,v1,v2,& 2 | wigdim,w3j,llmax,llvec,lam,c2r,featsize,p) 3 | 4 | !use omp_lib 5 | IMPLICIT NONE 6 | INTEGER:: natoms,nang1,nang2,nrad1,nrad2,llmax,lam,wigdim,ifeat 7 | INTEGER:: iat,n1,n2,iwig,l1,l2,il,imu,im1,im2,mu,m1,m2,featsize 8 | INTEGER, DIMENSION(2,llmax):: llvec 9 | REAL*8, DIMENSION(wigdim):: w3j 10 | REAL*8, DIMENSION(2*lam+1):: preal 11 | COMPLEX*16, DIMENSION(2*lam+1):: pcmplx 12 | COMPLEX*16, DIMENSION(2*lam+1,2*lam+1):: c2r 13 | COMPLEX*16, DIMENSION(2*nang1+1,nang1+1,nrad1,natoms):: v1 14 | COMPLEX*16, DIMENSION(2*nang2+1,nang2+1,nrad2,natoms):: v2 15 | REAL*8, DIMENSION(2*lam+1,featsize):: ptemp 16 | REAL*8, DIMENSION(2*lam+1,featsize,natoms):: p 17 | REAL*8:: inner, normfact 18 | 19 | !f2py intent(in) natoms,nang1,nang2,nrad1,nrad2,v1,v2,wigdim,w3j,llmax,llvec,lam,c2r 20 | !f2py intent(in) featsize 21 | !f2py intent(out) p 22 | !f2py depend(natoms) p, v1, v2 23 | !f2py depend(nrad1) v1 24 | !f2py depend(nrad2) v2 25 | !f2py depend(nang1) v1 26 | !f2py depend(nang2) v2 27 | !f2py depend(lam) p, c2r 28 | !f2py depend(llmax) llvec 29 | !f2py depend(wigdim) w3j 30 | !f2py depend(featsize) p 31 | 32 | p = 0.d0 33 | 34 | !$OMP PARALLEL DEFAULT(private) & 35 | !$OMP FIRSTPRIVATE(natoms,nang1,nang2,nrad1,nrad2,w3j,llmax,llvec,lam,c2r,featsize) & 36 | !$OMP SHARED(p,v1,v2) 37 | !$OMP DO SCHEDULE(dynamic) 38 | do iat=1,natoms 39 | inner = 0.0 40 | ptemp = 0.0 41 | ifeat = 1 42 | do n1=1,nrad1 43 | do n2=1,nrad2 44 | iwig = 1 45 | do il=1,llmax 46 | l1 = llvec(1,il) 47 | l2 = llvec(2,il) 48 | pcmplx = dcmplx(0.0,0.0) 49 | do imu=1,2*lam+1 50 | mu = imu-1-lam 51 | do im1=1,2*l1+1 52 | m1 = im1-1-l1 53 | m2 = m1-mu 54 | if (abs(m2)<=l2) then 55 | im2 = m2+l2+1 56 | pcmplx(imu) = pcmplx(imu) & 57 | + w3j(iwig) * v1(im1,l1+1,n1,iat) * dconjg(v2(im2,l2+1,n2,iat)) 58 | iwig = iwig + 1 59 | endif 60 | enddo 61 | enddo 62 | preal = dreal(matmul(c2r,pcmplx)) 63 | do imu=1,2*lam+1 64 | inner = inner + preal(imu)**2 65 | ptemp(imu,ifeat) = preal(imu) 66 | enddo 67 | ifeat = ifeat + 1 68 | enddo 69 | enddo 70 | enddo 71 | normfact = dsqrt(inner) 72 | do ifeat=1,featsize 73 | do imu=1,2*lam+1 74 | p(imu,ifeat,iat) = ptemp(imu,ifeat) / normfact 75 | enddo 76 | enddo 77 | enddo 78 | !$OMP END DO 79 | !$OMP END PARALLEL 80 | 81 | return 82 | END 83 | -------------------------------------------------------------------------------- /src/equicombfield.f90: -------------------------------------------------------------------------------- 1 | SUBROUTINE equicombfield(natoms,nang1,nrad1,nrad2,v1,v2,wigdim,w3j,llmax,llvec,lam,c2r,p) 2 | 3 | !use omp_lib 4 | IMPLICIT NONE 5 | INTEGER:: natoms,nang1,nrad1,nrad2,llmax,lam,wigdim 6 | INTEGER:: iat,n1,n2,iwig,l1,l2,il,imu,im1,im2,mu,m1,m2 7 | INTEGER, DIMENSION(2,llmax):: llvec 8 | REAL*8, DIMENSION(wigdim):: w3j 9 | REAL*8, DIMENSION(2*lam+1):: preal 10 | COMPLEX*16, DIMENSION(2*lam+1):: pcmplx 11 | COMPLEX*16, DIMENSION(2*lam+1,2*lam+1):: c2r 12 | COMPLEX*16, DIMENSION(2*nang1+1,nang1+1,nrad1,natoms):: v1 13 | COMPLEX*16, DIMENSION(nrad2,natoms):: v2 14 | REAL*8, DIMENSION(2*lam+1,llmax,nrad2,nrad1,natoms):: p 15 | 16 | !f2py intent(in) natoms,nang1,nrad1,nrad2,v1,v2,wigdim,w3j,llmax,llvec,lam,c2r 17 | !f2py intent(out) p 18 | !f2py depend(natoms) p, v1, v2 19 | !f2py depend(nrad1) p, v1 20 | !f2py depend(nrad2) p, v2 21 | !f2py depend(nang1) p, v1 22 | !f2py depend(lam) p, c2r 23 | !f2py depend(llmax) p,llvec 24 | !f2py depend(wigdim) w3j 25 | 26 | p = dcmplx(0.d0,0.d0) 27 | 28 | !$OMP PARALLEL DEFAULT(private) & 29 | !$OMP FIRSTPRIVATE(natoms,nang1,nrad1,nrad2,w3j,llmax,llvec,lam,c2r) & 30 | !$OMP SHARED(p,v1,v2) 31 | !$OMP DO SCHEDULE(dynamic) 32 | do iat=1,natoms 33 | do n1=1,nrad1 34 | do n2=1,nrad2 35 | iwig = 1 36 | do il=1,llmax 37 | l1 = llvec(1,il) 38 | l2 = llvec(2,il) 39 | pcmplx = dcmplx(0.0,0.0) 40 | do imu=1,2*lam+1 41 | mu = imu-1-lam 42 | do im1=1,2*l1+1 43 | m1 = im1-1-l1 44 | m2 = m1-mu 45 | if (abs(m2)<=l2 .and. m2==0) then 46 | im2 = m2+l2+1 47 | pcmplx(imu) = pcmplx(imu) & 48 | + w3j(iwig) * v1(im1,l1+1,n1,iat) * dconjg(v2(n2,iat)) 49 | iwig = iwig + 1 50 | endif 51 | enddo 52 | enddo 53 | preal = dreal(matmul(c2r,pcmplx)) 54 | do imu=1,2*lam+1 55 | p(imu,il,n2,n1,iat) = preal(imu) 56 | enddo 57 | enddo 58 | enddo 59 | enddo 60 | enddo 61 | !$OMP END DO 62 | !$OMP END PARALLEL 63 | 64 | return 65 | END 66 | -------------------------------------------------------------------------------- /src/equicombfps.f90: -------------------------------------------------------------------------------- 1 | SUBROUTINE equicombfps(natoms, nang1, nang2, nrad1, nrad2, v1, v2, & 2 | wigdim, w3j, llmax, llvec, lam, c2r, featsize, pvec) 3 | 4 | use omp_lib 5 | IMPLICIT NONE 6 | INTEGER :: natoms, nang1, nang2, nrad1, nrad2, llmax, lam, wigdim, ifeat 7 | INTEGER :: iat, n1, n2, iwig, l1, l2, il, imu, im1, im2, mu, m1, m2, featsize, temp1, temp2 8 | INTEGER, DIMENSION(2, llmax) :: llvec 9 | REAL*8, DIMENSION(wigdim) :: w3j 10 | REAL*8, DIMENSION(2*lam+1) :: preal 11 | COMPLEX*16, DIMENSION(2*lam+1) :: pcmplx 12 | COMPLEX*16, DIMENSION(2*lam+1, 2*lam+1) :: c2r 13 | COMPLEX*16, DIMENSION(2*nang1+1, nang1+1, nrad1, natoms) :: v1 14 | COMPLEX*16, DIMENSION(2*nang2+1, nang2+1, nrad2, natoms) :: v2 15 | REAL*8, DIMENSION(2*lam+1, featsize) :: ptemp 16 | REAL*8, DIMENSION(featsize, natoms * (2*lam+1)) :: pvec 17 | REAL*8 :: inner, normfact 18 | 19 | !f2py intent(in) natoms, nang1, nang2, nrad1, nrad2, v1, v2, wigdim, w3j, llmax, llvec, lam, c2r 20 | !f2py intent(in) featsize 21 | !f2py intent(out) pvec 22 | !f2py depend(natoms) pvec, v1, v2 23 | !f2py depend(nrad1) v1 24 | !f2py depend(nrad2) v2 25 | !f2py depend(nang1) v1 26 | !f2py depend(nang2) v2 27 | !f2py depend(lam) pvec, c2r 28 | !f2py depend(llmax) llvec 29 | !f2py depend(wigdim) w3j 30 | !f2py depend(featsize) pvec 31 | 32 | !$OMP PARALLEL DEFAULT(private) & 33 | !$OMP FIRSTPRIVATE(natoms, nang1, nang2, nrad1, nrad2, w3j, llmax, llvec, lam, c2r, featsize) & 34 | !$OMP SHARED(pvec, v1, v2) 35 | !$OMP DO SCHEDULE(dynamic) 36 | do iat = 1, natoms 37 | inner = 0.0 38 | ptemp = 0.0 39 | ifeat = 1 40 | do n1 = 1, nrad1 41 | do n2 = 1, nrad2 42 | iwig = 1 43 | do il = 1, llmax 44 | l1 = llvec(1, il) 45 | l2 = llvec(2, il) 46 | pcmplx = dcmplx(0.0, 0.0) 47 | do imu = 1, 2*lam+1 48 | mu = imu - 1 - lam 49 | do im1 = 1, 2*l1+1 50 | m1 = im1 - 1 - l1 51 | m2 = m1 - mu 52 | if (abs(m2) <= l2) then 53 | im2 = m2 + l2 + 1 54 | pcmplx(imu) = pcmplx(imu) & 55 | + w3j(iwig) * v1(im1, l1+1, n1, iat) * dconjg(v2(im2, l2+1, n2, iat)) 56 | iwig = iwig + 1 57 | endif 58 | enddo 59 | enddo 60 | preal = dreal(matmul(c2r, pcmplx)) 61 | do imu = 1, 2*lam+1 62 | inner = inner + preal(imu)**2 63 | ptemp(imu, ifeat) = preal(imu) 64 | enddo 65 | ifeat = ifeat + 1 66 | enddo 67 | enddo 68 | enddo 69 | normfact = dsqrt(inner) 70 | do ifeat = 1, featsize 71 | do imu = 1, 2*lam+1 72 | pvec(ifeat, (iat-1)*(2*lam+1) + imu) = ptemp(imu, ifeat) / normfact 73 | enddo 74 | enddo 75 | enddo 76 | !$OMP END DO 77 | !$OMP END PARALLEL 78 | 79 | return 80 | END SUBROUTINE equicombfps -------------------------------------------------------------------------------- /src/equicombnonorm.f90: -------------------------------------------------------------------------------- 1 | SUBROUTINE equicombnonorm(natoms,nang1,nang2,nrad1,nrad2,v1,v2,& 2 | wigdim,w3j,llmax,llvec,lam,c2r,featsize,p) 3 | 4 | !use omp_lib 5 | IMPLICIT NONE 6 | INTEGER:: natoms,nang1,nang2,nrad1,nrad2,llmax,lam,wigdim,ifeat 7 | INTEGER:: iat,n1,n2,iwig,l1,l2,il,imu,im1,im2,mu,m1,m2,featsize 8 | INTEGER, DIMENSION(2,llmax):: llvec 9 | REAL*8, DIMENSION(wigdim):: w3j 10 | REAL*8, DIMENSION(2*lam+1):: preal 11 | COMPLEX*16, DIMENSION(2*lam+1):: pcmplx 12 | COMPLEX*16, DIMENSION(2*lam+1,2*lam+1):: c2r 13 | COMPLEX*16, DIMENSION(2*nang1+1,nang1+1,nrad1,natoms):: v1 14 | COMPLEX*16, DIMENSION(2*nang2+1,nang2+1,nrad2,natoms):: v2 15 | REAL*8, DIMENSION(2*lam+1,featsize):: ptemp 16 | REAL*8, DIMENSION(2*lam+1,featsize,natoms):: p 17 | 18 | !f2py intent(in) natoms,nang1,nang2,nrad1,nrad2,v1,v2,wigdim,w3j,llmax,llvec,lam,c2r 19 | !f2py intent(in) featsize 20 | !f2py intent(out) p 21 | !f2py depend(natoms) p, v1, v2 22 | !f2py depend(nrad1) v1 23 | !f2py depend(nrad2) v2 24 | !f2py depend(nang1) v1 25 | !f2py depend(nang2) v2 26 | !f2py depend(lam) p, c2r 27 | !f2py depend(llmax) llvec 28 | !f2py depend(wigdim) w3j 29 | !f2py depend(featsize) p 30 | 31 | p = 0.d0 32 | 33 | !$OMP PARALLEL DEFAULT(private) & 34 | !$OMP FIRSTPRIVATE(natoms,nang1,nang2,nrad1,nrad2,w3j,llmax,llvec,lam,c2r,featsize) & 35 | !$OMP SHARED(p,v1,v2) 36 | !$OMP DO SCHEDULE(dynamic) 37 | do iat=1,natoms 38 | ptemp = 0.0 39 | ifeat = 1 40 | do n1=1,nrad1 41 | do n2=1,nrad2 42 | iwig = 1 43 | do il=1,llmax 44 | l1 = llvec(1,il) 45 | l2 = llvec(2,il) 46 | pcmplx = dcmplx(0.0,0.0) 47 | do imu=1,2*lam+1 48 | mu = imu-1-lam 49 | do im1=1,2*l1+1 50 | m1 = im1-1-l1 51 | m2 = m1-mu 52 | if (abs(m2)<=l2) then 53 | im2 = m2+l2+1 54 | pcmplx(imu) = pcmplx(imu) & 55 | + w3j(iwig) * v1(im1,l1+1,n1,iat) * dconjg(v2(im2,l2+1,n2,iat)) 56 | iwig = iwig + 1 57 | endif 58 | enddo 59 | enddo 60 | preal = dreal(matmul(c2r,pcmplx)) 61 | do imu=1,2*lam+1 62 | ptemp(imu,ifeat) = preal(imu) 63 | enddo 64 | ifeat = ifeat + 1 65 | enddo 66 | enddo 67 | enddo 68 | do ifeat=1,featsize 69 | do imu=1,2*lam+1 70 | p(imu,ifeat,iat) = ptemp(imu,ifeat) 71 | enddo 72 | enddo 73 | enddo 74 | !$OMP END DO 75 | !$OMP END PARALLEL 76 | 77 | return 78 | END 79 | -------------------------------------------------------------------------------- /src/equicombsparse.f90: -------------------------------------------------------------------------------- 1 | SUBROUTINE equicombsparse(natoms,nang1,nang2,nrad1,nrad2,v1,v2,& 2 | wigdim,w3j,llmax,llvec,lam,c2r,& 3 | featsize,nfps,vfps,p) 4 | 5 | !use omp_lib 6 | IMPLICIT NONE 7 | INTEGER:: natoms,nang1,nang2,nrad1,nrad2,llmax,lam,wigdim,ifps,ifeat,n 8 | INTEGER:: iat,n1,n2,iwig,l1,l2,il,imu,im1,im2,mu,m1,m2,featsize,nfps 9 | INTEGER, DIMENSION(nfps):: vfps 10 | INTEGER, DIMENSION(2,llmax):: llvec 11 | REAL*8, DIMENSION(wigdim):: w3j 12 | REAL*8, DIMENSION(2*lam+1):: preal 13 | COMPLEX*16, DIMENSION(2*lam+1):: pcmplx 14 | COMPLEX*16, DIMENSION(2*lam+1,2*lam+1):: c2r 15 | COMPLEX*16, DIMENSION(2*nang1+1,nang1+1,nrad1,natoms):: v1 16 | COMPLEX*16, DIMENSION(2*nang2+1,nang2+1,nrad2,natoms):: v2 17 | REAL*8, DIMENSION(2*lam+1,featsize):: ptemp 18 | REAL*8, DIMENSION(2*lam+1,nfps,natoms):: p 19 | REAL*8:: inner,normfact 20 | 21 | !f2py intent(in) natoms,nang1,nang2,nrad1,nrad2,v1,v2,wigdim,w3j,llmax,llvec,lam,c2r 22 | !f2py intent(in) featsize, nfps, vfps 23 | !f2py intent(out) p 24 | !f2py depend(natoms) p, v1, v2 25 | !f2py depend(nrad1) v1 26 | !f2py depend(nrad2) v2 27 | !f2py depend(nang1) v1 28 | !f2py depend(nang2) v2 29 | !f2py depend(lam) p, c2r 30 | !f2py depend(llmax) llvec 31 | !f2py depend(wigdim) w3j 32 | !f2py depend(nfps) vfps, p 33 | 34 | p = 0.d0 35 | 36 | !$OMP PARALLEL DEFAULT(private) & 37 | !$OMP FIRSTPRIVATE(natoms,nang1,nang2,nrad1,nrad2,w3j,llmax,llvec,lam,c2r,nfps,vfps) & 38 | !$OMP SHARED(p,v1,v2) 39 | !$OMP DO SCHEDULE(dynamic) 40 | do iat=1,natoms 41 | inner = 0.0 42 | ptemp = 0.0 43 | ifeat = 1 44 | do n1=1,nrad1 45 | do n2=1,nrad2 46 | iwig = 1 47 | do il=1,llmax 48 | l1 = llvec(1,il) 49 | l2 = llvec(2,il) 50 | pcmplx = dcmplx(0.0,0.0) 51 | do imu=1,2*lam+1 52 | mu = imu-1-lam 53 | do im1=1,2*l1+1 54 | m1 = im1-1-l1 55 | m2 = m1-mu 56 | if (abs(m2)<=l2) then 57 | im2 = m2+l2+1 58 | pcmplx(imu) = pcmplx(imu) & 59 | + w3j(iwig) * v1(im1,l1+1,n1,iat) * dconjg(v2(im2,l2+1,n2,iat)) 60 | iwig = iwig + 1 61 | endif 62 | enddo 63 | enddo 64 | preal = dreal(matmul(c2r,pcmplx)) 65 | do imu=1,2*lam+1 66 | inner = inner + preal(imu)**2 67 | ptemp(imu,ifeat) = preal(imu) 68 | enddo 69 | ifeat = ifeat + 1 70 | enddo 71 | enddo 72 | enddo 73 | normfact = dsqrt(inner) 74 | do n=1,nfps 75 | ifps = vfps(n) + 1 76 | do imu=1,2*lam+1 77 | p(imu,n,iat) = ptemp(imu,ifps) / normfact 78 | enddo 79 | enddo 80 | enddo 81 | !$OMP END DO 82 | !$OMP END PARALLEL 83 | 84 | return 85 | END 86 | -------------------------------------------------------------------------------- /src/kernelequicomb.f90: -------------------------------------------------------------------------------- 1 | SUBROUTINE kernelequicomb(n1,n2,lam1,lam2,L,nsize,msize,& 2 | cgsize,cgcoefs,knm,k0,kernel) 3 | 4 | !use omp_lib 5 | IMPLICIT NONE 6 | INTEGER:: n1,n2,lam1,lam2,L,nsize,msize,cgsize 7 | REAL*8, DIMENSION(cgsize):: cgcoefs 8 | REAL*8, DIMENSION(n2,n1):: k0 9 | COMPLEX*16, DIMENSION(n2*(2*L+1),n1*(2*L+1)):: knm 10 | COMPLEX*16, DIMENSION(msize,nsize):: kernel 11 | 12 | INTEGER:: i1,i2,iM1,iM2,idx1,idx2,icg1,icg2,mu1size,mu2size 13 | INTEGER:: imu1,imu2,mu1,mu2,ik1,ik2,k1,k2,M1,M2,j1,j2 14 | REAL*8:: cg1,cg2 15 | 16 | !f2py intent(in) n1,n2,lam1,lam2,L,nsize,msize 17 | !f2py intent(in) cgsize,cgcoefs,knm,k0 18 | !f2py intent(out) kernel 19 | !f2py depend(nsize) kernel 20 | !f2py depend(msize) kernel 21 | !f2py depend(cgsize) cgcoefs 22 | !f2py depend(n1) k0,knm 23 | !f2py depend(n2) k0,knm 24 | !f2py depend(L) knm 25 | 26 | mu1size = 2*lam1+1 27 | mu2size = 2*lam2+1 28 | 29 | kernel = 0.d0 30 | 31 | iM1 = 1 32 | idx1 = 1 33 | do i1=1,n1 34 | icg1 = 1 35 | do imu1=1,mu1size 36 | mu1 = imu1-1-lam1 37 | do ik1=1,mu2size 38 | k1 = ik1-1-lam2 39 | M1 = mu1+k1 40 | if (abs(M1).le.L) then 41 | j1 = M1+L 42 | cg1 = cgcoefs(icg1) 43 | iM2 = 1 44 | idx2 = 1 45 | do i2=1,n2 46 | icg2 = 1 47 | do imu2=1,mu1size 48 | mu2 = imu2-1-lam1 49 | do ik2=1,mu2size 50 | k2 = ik2-1-lam2 51 | M2 = mu2+k2 52 | if (abs(M2).le.L) then 53 | j2 = M2+L 54 | cg2 = cgcoefs(icg2) 55 | kernel(iM2,iM1) = kernel(iM2,iM1) + cg1 * cg2 & 56 | * knm(idx2+j2,idx1+j1) * k0(i2,i1) 57 | icg2 = icg2 + 1 58 | endif 59 | iM2 = iM2 + 1 60 | enddo 61 | enddo 62 | idx2 = idx2 + 2*L+1 63 | enddo 64 | icg1 = icg1 + 1 65 | endif 66 | iM1 = iM1 + 1 67 | enddo 68 | enddo 69 | idx1 = idx1 + 2*L+1 70 | enddo 71 | 72 | return 73 | END 74 | -------------------------------------------------------------------------------- /src/kernelnorm.f90: -------------------------------------------------------------------------------- 1 | SUBROUTINE kernelnorm(n1,n2,msize,normfact1,normfact2,kernel,knorm) 2 | 3 | !use omp_lib 4 | IMPLICIT NONE 5 | INTEGER:: n1,n2,msize,i1,i2,j1,j2,im1,im2 6 | REAL*8, DIMENSION(n2*msize,n1*msize):: kernel, knorm 7 | REAL*8, DIMENSION(n1):: normfact1 8 | REAL*8, DIMENSION(n2):: normfact2 9 | 10 | !f2py intent(in) n1,n2,msize,normfact1,normfact2,kernel 11 | !f2py intent(out) knorm 12 | !f2py depend(n1) kernel, knorm , normfact1 13 | !f2py depend(n2) kernel, knorm , normfact2 14 | !f2py depend(msize) kernel, knorm 15 | 16 | j1 = 1 17 | do i1=1,n1 18 | do im1=1,msize 19 | j2 = 1 20 | do i2=1,n2 21 | do im2=1,msize 22 | knorm(j2,j1) = kernel(j2,j1) / dsqrt(normfact1(i1)*normfact2(i2)) 23 | j2 = j2 + 1 24 | enddo 25 | enddo 26 | j1 = j1 + 1 27 | enddo 28 | enddo 29 | 30 | return 31 | END 32 | --------------------------------------------------------------------------------