├── .github
└── workflows
│ ├── ci_hybrid-md.yaml
│ └── docs-build.yaml
├── .gitignore
├── .gitmodules
├── LICENSE.md
├── Makefile
├── README.md
├── clustering.F90
├── descriptors.F90
├── descriptors_noncommercial.inc
├── descriptors_noncommercial_permutations.inc
├── descriptors_noncommercial_types.inc
├── descriptors_wrapper.F90
├── doc_src
├── Makefile
├── _static
│ └── theme_overrides.css
├── _templates
│ └── layout.html
├── accelerated-aimd-castep-inputs.rst
├── accelerated-aimd-input-reference.rst
├── accelerated-aimd.rst
├── accelerated-aimd_decision-making.rst
├── accelerated-aimd_example_Si16.rst
├── accelerated-aimd_installation.rst
├── benzene_frames.xyz
├── conf.py
├── data.rst
├── gap_fit.rst
├── gap_fitting_tutorial.ipynb
├── gap_si_surface.ipynb
├── gap_si_surface
│ ├── __init__.py
│ └── visualise.py
├── index.rst
├── installation.rst
├── quippy-descriptor-tutorial.ipynb
├── quippy-potential-tutorial.ipynb
├── reference.rst
├── requirements.txt
├── soap.png
└── tutorials.rst
├── error.inc
├── find_water_triplets_noncommercial.F90
├── gap_fit.F90
├── gap_fit_module.F90
├── gapversion
├── gp_fit.F90
├── gp_predict.F90
├── hybrid_md_package
├── .gitignore
├── README.md
├── examples
│ ├── H2_in_C60
│ │ ├── build.py
│ │ ├── h2c60.cell
│ │ ├── h2c60.hybrid-md-input.yaml
│ │ └── h2c60.param
│ ├── check_installation
│ │ ├── readme.md
│ │ ├── sic_md.cell
│ │ ├── sic_md.hybrid-md-input.yaml
│ │ ├── sic_md.param
│ │ └── sic_md.param-v22
│ └── walkthrough
│ │ ├── 0-initial
│ │ ├── si16.cell
│ │ ├── si16.hybrid-md-input.yaml
│ │ └── si16.param
│ │ ├── 1-adaptive
│ │ ├── si16.cell
│ │ ├── si16.hybrid-md-input.yaml
│ │ └── si16.param
│ │ ├── 2-gap-parameters
│ │ ├── previous_si16.hybrid-md.xyz
│ │ ├── si16.cell
│ │ ├── si16.hybrid-md-input.yaml
│ │ └── si16.param
│ │ └── readme.md
├── hybrid_md
│ ├── __init__.py
│ ├── cli.py
│ ├── decision_making.py
│ ├── refit.py
│ ├── settings.py
│ └── state_objects.py
├── setup.py
└── tests
│ ├── __init__.py
│ ├── test_decision_maker.py
│ └── test_settings.py
├── make_permutations_noncommercial_v2.F90
├── meson.build
├── soap_turbo.f90
├── soap_turbo_angular.f90
├── soap_turbo_compress.f90
├── soap_turbo_functions.f90
├── soap_turbo_radial.f90
├── tensor_reduction_docs.md
└── translation_table.png
/.github/workflows/ci_hybrid-md.yaml:
--------------------------------------------------------------------------------
1 | name: HybridMD Python Package
2 |
3 | on:
4 | push:
5 | workflow_dispatch:
6 |
7 | env:
8 | root-directory: "${{github.workspace}}/hybrid_md_package"
9 |
10 | jobs:
11 | install-and-run-pytest:
12 | runs-on: ubuntu-latest
13 | strategy:
14 | matrix:
15 | python-version: [ "3.7", "3.8", "3.9", "3.10"]
16 | max-parallel: 5
17 |
18 | steps:
19 | - uses: actions/checkout@v3
20 | - name: Set up Python ${{ matrix.python-version }}
21 | uses: actions/setup-python@v4
22 | with:
23 | python-version: ${{ matrix.python-version }}
24 |
25 | - name: Install Dependencies
26 | run: pip install pytest pytest-cov numpy ase quippy-ase click pyyaml
27 |
28 | - name: Install package
29 | working-directory: ${{env.root-directory}}
30 | run: pip install .
31 |
32 | - name: Pytest + Coverage
33 | working-directory: ${{env.root-directory}}
34 | run: |
35 | pytest -v --cov=hybrid_md --cov-report term --cov-report term-missing --cov-report xml
36 |
37 | - uses: codecov/codecov-action@v3
38 | with:
39 | name: hybrid-md unittests
40 | fail_ci_if_error: true
41 | files: coverage.xml
42 |
--------------------------------------------------------------------------------
/.github/workflows/docs-build.yaml:
--------------------------------------------------------------------------------
1 | name: Sphinx & deploy - commit to gh-pages
2 |
3 | on:
4 | push:
5 | # branches:
6 | # - master
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v3
15 | with:
16 | persist-credentials: false
17 | - uses: actions/setup-python@v4
18 | with:
19 | python-version: '3.9'
20 | cache: 'pip'
21 |
22 | - name: Install dependencies
23 | run: sudo apt-get install pandoc
24 |
25 | - name: Install dependencies
26 | run: pip install -r doc_src/requirements.txt
27 |
28 | - name: Build
29 | working-directory: ${{github.workspace}}/doc_src
30 | run: |
31 | make html
32 |
33 | - name: Commit to gh-pages branch
34 | if: github.ref == 'refs/heads/main'
35 | uses: peaceiris/actions-gh-pages@v3
36 | with:
37 | github_token: ${{ secrets.GITHUB_TOKEN }}
38 | publish_dir: ./doc_src/_build/html
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "soap_turbo"]
2 | path = soap_turbo
3 | url = https://github.com/libAtoms/soap_turbo.git
4 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # Academic Software Licence (“ASL”)
2 |
3 | ## Preamble
4 |
5 | ASL is a software license that proposes to offer copyleft style rights to use the software in an academic non-commercial setting. The purpose of ASL is to encourage academic cooperation and collaboration free-of-charge whilst enabling academic institutions to earn revenue from the parallel licensing of valuable bodies of software code. Significant proportions of such revenue are typically reinvested in academic research.
6 |
7 | ASL is not an open-source licence because it does not allow commercial use – it is an “available source” licence, meaning that the source code is made available subject to the terms of this licence and only for academic non-commercial use.
8 |
9 | ASL is a reciprocal licence very similar to GPL and the core terms are identical to those of GNU GPLv2 (except for the limitation to non-commercial use), making it easier for those who know the GPLv2 to understand the licensing*. As a reciprocal licence, if you redistribute any derivative works you have created based on ASL licenced code, then you are required to license the new work under the ASL - including making your source code available and ensuring that licensees are aware of the terms of the ASL licence.
10 |
11 | The non-commercial limitation makes ASL incompatible with the GPL and other open-source licences with copyleft provisions because these licences require that modified versions of the original program are made available free of charge for any type of use, including commercial, and this is prevented by ASL. As the two sets of reciprocal terms are incompatible, any modified version of the original program combining ASL and copylefted open-source code can be used internally but cannot be licensed out.
12 |
13 | The non-commercial restriction is an integral part of the license and you may not remove it without the consent of the rights holder in the ASL licensed code. Please contact __Cambridge Enterprise__ for any questions and/or to enquire about commercial use rights.
14 |
15 | *The changes to the GPLv2 are: removing the Preamble, replacing the reference to the “General Public License” in clause 0 with a reference to the “ASL”, removing clause 9 and adding clause 13 with the non-commercial restriction and limited patent grant
16 |
17 | ## TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
18 |
19 | 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
20 |
21 | Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
22 |
23 | 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
24 | You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
25 |
26 | 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
27 |
28 | a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
29 |
30 | b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
31 |
32 | c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
33 | These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
34 | Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
35 | In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
36 |
37 | 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
38 |
39 | a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
40 |
41 | b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
42 |
43 | c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
44 | The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
45 | If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
46 |
47 | 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
48 |
49 | 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
50 |
51 | 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
52 |
53 | 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
54 | If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
55 | It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
56 | This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
57 |
58 | 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
59 |
60 | 9. Not used.
61 |
62 | 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
63 |
64 | ### NO WARRANTY
65 |
66 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
67 |
68 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
69 |
70 | ### Non Commercial Use; Limited Patent Rights Grant
71 |
72 | 13. The Preamble of the GPLv2 does not apply to this License. The Program is provided to you by the Licensor subject to the following conditions, which prevail over any clause or indication to the contrary in the GPLv2:
73 |
74 | a) The grant of rights under the License is for academic non-commercial use only. Academic non-commercial use is defined as use for academic research or other not-for-profit scholarly purposes, which are undertaken at an educational, non-profit, charitable or governmental institution, and which does not involve and is not intended to lead to the production or manufacture of products for sale, or the enhancement of a product or service in or proposed for commerce, or the performance of services for a fee.
75 |
76 | b) Subject to your compliance with the License, in the event the Licensor holds patent rights on the Program or any part of it, the Licensor grants you a perpetual, worldwide, non-exclusive, royalty-free, irrevocable (except as stated in this section), limited patent licence to make, use, import and otherwise run, modify and distribute the Program. For the avoidance of doubt, this patent licence is limited to academic non-commercial use, as described above. If you institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program constitutes direct or contributory patent infringement, then any patent license granted to you under this License for the Program shall terminate as of the date such litigation is filed.
77 |
78 | Use other than academic and non-commercial use as above is deemed to be commercial use and outside the scope of this License. If you intend to use the Program for a commercial use, then you must obtain a commercial use license for the Program. In that case, please contact the original licensor to enquire about commercial use licenses.
79 |
80 | ## END OF TERMS AND CONDITIONS
81 |
82 | ## Appendix: Suggested code header and licensing information
83 | [Program name] is © 2021, [Name of copyright holder]
84 |
85 | [Program name] is published and distributed under the Academic Software License v1.0 (ASL).
86 |
87 | [Program name] is distributed in the hope that it will be useful for non-commercial academic research, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ASL for more details.
88 |
89 | You should have received a copy of the ASL along with this program; if not, write to [Contact details of copyright holder]. It is also published at [URL where licence terms are published].
90 |
91 | You may contact the original licensor at [Contact details of original licensor].
92 |
93 |
94 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | #! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2 | #! HND X
3 | #! HND X GAP (Gaussian Approximation Potental)
4 | #! HND X
5 | #! HND X
6 | #! HND X Portions of GAP were written by Albert Bartok-Partay, Gabor Csanyi,
7 | #! HND X Copyright 2006-2021.
8 | #! HND X
9 | #! HND X Portions of GAP were written by Noam Bernstein as part of
10 | #! HND X his employment for the U.S. Government, and are not subject
11 | #! HND X to copyright in the USA.
12 | #! HND X
13 | #! HND X GAP is published and distributed under the
14 | #! HND X Academic Software License v1.0 (ASL)
15 | #! HND X
16 | #! HND X GAP is distributed in the hope that it will be useful for non-commercial
17 | #! HND X academic research, but WITHOUT ANY WARRANTY; without even the implied
18 | #! HND X warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | #! HND X ASL for more details.
20 | #! HND X
21 | #! HND X You should have received a copy of the ASL along with this program
22 | #! HND X (e.g. in a LICENSE.md file); if not, you can write to the original licensors,
23 | #! HND X Gabor Csanyi or Albert Bartok-Partay. The ASL is also published at
24 | #! HND X http://github.com/gabor1/ASL
25 | #! HND X
26 | #! HND X When using this software, please cite the following reference:
27 | #! HND X
28 | #! HND X A. P. Bartok et al Physical Review Letters vol 104 p136403 (2010)
29 | #! HND X
30 | #! HND X When using the SOAP kernel or its variants, please additionally cite:
31 | #! HND X
32 | #! HND X A. P. Bartok et al Physical Review B vol 87 p184115 (2013)
33 | #! HND X
34 | #! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
35 |
36 | CUSTOM_F90FLAGS += -frealloc-lhs
37 |
38 | ifeq (${QUIP_ARCH},)
39 | include Makefile.arch
40 | else
41 | include Makefile.${QUIP_ARCH}
42 | endif
43 | include Makefile.inc
44 | include Makefile.rules
45 |
46 |
47 | ifeq (${HAVE_DESCRIPTORS_NONCOMMERCIAL},1)
48 | DEFINES += -DDESCRIPTORS_NONCOMMERCIAL
49 | GAP1_F90_FILES = make_permutations_noncommercial_v2
50 | else
51 | GAP1_F90_FILES =
52 | endif
53 |
54 | SOAP_TURBO_F90_FILES = soap_turbo_functions soap_turbo_radial soap_turbo_angular soap_turbo_compress soap_turbo
55 | SOAP_TURBO_F90_SOURCES = ${addsuffix .f90, ${SOAP_TURBO_F90_FILES}}
56 | SOAP_TURBO_F90_OBJS = ${addsuffix .o, ${SOAP_TURBO_F90_FILES}}
57 |
58 | GAP1_F90_FILES += find_water_triplets_noncommercial descriptors gp_predict descriptors_wrapper clustering
59 | GAP1_F90_SOURCES = ${addsuffix .F90, ${GAP1_F90_FILES}}
60 | GAP1_F90_OBJS = ${addsuffix .o, ${GAP1_F90_FILES}}
61 |
62 | GAP2_F90_FILES = gp_fit gap_fit_module
63 | GAP2_F90_SOURCES = ${addsuffix .F90, ${GAP2_F90_FILES}}
64 | GAP2_F90_OBJS = ${addsuffix .o, ${GAP2_F90_FILES}}
65 |
66 | default: ${GAP_LIBFILE}
67 |
68 |
69 |
70 | ifeq (${USE_MAKEDEP},1)
71 | GAP1_F90_FPP_FILES = ${addsuffix .fpp, ${GAP1_F90_FILES}}
72 | GAP2_F90_FPP_FILES = ${addsuffix .fpp, ${GAP2_F90_FILES}}
73 | GAP1.depend: ${GAP1_F90_FPP_FILES}
74 | ${SCRIPT_PATH}/${MAKEDEP} ${MAKEDEP_ARGS} -- ${addprefix ../../src/GAP/,${GAP1_F90_SOURCES}} > GAP1.depend
75 | GAP2.depend: ${GAP2_F90_FPP_FILES} ${GAP1_F90_FPP_FILES}
76 | ${SCRIPT_PATH}/${MAKEDEP} ${MAKEDEP_ARGS} -- ${addprefix ../../src/GAP/,${GAP2_F90_SOURCES}} > GAP2.depend
77 |
78 | -include GAP1.depend
79 | -include GAP2.depend
80 | endif
81 |
82 |
83 | PROGRAMS = gap_fit
84 |
85 | LIBS = -L. -lquiputils -lquip_core -lgap -latoms
86 | ifeq (${HAVE_THIRDPARTY},1)
87 | LIBS += -lthirdparty
88 | endif
89 | LIBFILES = libatoms.a ${GAP_LIBFILE} libquip_core.a libquiputils.a
90 |
91 | .PHONY : clean allclean depend install
92 |
93 | Programs: ${PROGRAMS}
94 | #cp ${QUIP_ROOT}/src/GAP/teach_sparse .
95 |
96 | ${PROGRAMS}: % : ${LIBFILES} ${GAP2_F90_OBJS} ${GAPFIT_LIBFILE} %.o
97 | $(LINKER) $(LINKFLAGS) -o $@ ${F90OPTS} $@.o ${GAPFIT_LIBFILE} ${LIBS} ${LINKOPTS}
98 |
99 |
100 |
101 | ${GAP_LIBFILE}: ${SOAP_TURBO_F90_OBJS} ${GAP1_F90_OBJS}
102 | ifneq (${LIBTOOL},)
103 | ${LIBTOOL} -o ${GAP_LIBFILE} ${SOAP_TURBO_F90_OBJS} ${GAP1_F90_OBJS}
104 | else
105 | ${AR} ${AR_ADD} ${GAP_LIBFILE} $?
106 | endif
107 |
108 | ${GAPFIT_LIBFILE}: ${GAP2_F90_OBJS}
109 | ifneq (${LIBTOOL},)
110 | ${LIBTOOL} -o ${GAPFIT_LIBFILE} ${GAP2_F90_OBJS}
111 | else
112 | ${AR} ${AR_ADD} ${GAPFIT_LIBFILE} $?
113 | endif
114 |
115 |
116 |
117 |
118 | install:
119 | @if [ ! -d ${QUIP_INSTALLDIR} ]; then \
120 | echo "make install: QUIP_INSTALLDIR '${QUIP_INSTALLDIR}' doesn't exist or isn't a directory"; \
121 | exit 1; \
122 | else \
123 | for f in ${PROGRAMS} ; do \
124 | echo "Copying $$f to ${QUIP_INSTALLDIR}/$${f}${QUIP_MPI_SUFFIX}" ; \
125 | cp $$f ${QUIP_INSTALLDIR}/$${f}${QUIP_MPI_SUFFIX} ; \
126 | done ;\
127 | #cp ${QUIP_ROOT}/src/GAP/teach_sparse ${QUIP_INSTALLDIR}; \
128 | fi
129 |
130 |
131 | clean:
132 | rm -f *.o *.mod *.mod.save ${GAP_LIBFILE} ${GAPFIT_LIBFILE} ${PROGRAMS} GAP1.depend GAP2.depend
133 |
134 |
135 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GAP
2 |
3 | This package is part of [QUIP](http://github.com/libatoms/QUIP) (but with a different license!). In order to use it, you should clone QUIP with the `--recursive` option. QUIP is released under a GPL license , whereas GAP uses [ASL](http://github.com/gabor1/ASL) (Academic Software License).
4 |
5 | GAP-specific documentation is [here](https://libatoms.github.io/GAP), including fitted potentials.
6 |
--------------------------------------------------------------------------------
/descriptors_noncommercial_permutations.inc:
--------------------------------------------------------------------------------
1 | ! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2 | ! HND X
3 | ! HND X GAP (Gaussian Approximation Potental)
4 | ! HND X
5 | ! HND X
6 | ! HND X Portions of GAP were written by Albert Bartok-Partay, Gabor Csanyi,
7 | ! HND X Copyright 2006-2021.
8 | ! HND X
9 | ! HND X Portions of GAP were written by Noam Bernstein as part of
10 | ! HND X his employment for the U.S. Government, and are not subject
11 | ! HND X to copyright in the USA.
12 | ! HND X
13 | ! HND X GAP is published and distributed under the
14 | ! HND X Academic Software License v1.0 (ASL)
15 | ! HND X
16 | ! HND X GAP is distributed in the hope that it will be useful for non-commercial
17 | ! HND X academic research, but WITHOUT ANY WARRANTY; without even the implied
18 | ! HND X warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | ! HND X ASL for more details.
20 | ! HND X
21 | ! HND X You should have received a copy of the ASL along with this program
22 | ! HND X (e.g. in a LICENSE.md file); if not, you can write to the original licensors,
23 | ! HND X Gabor Csanyi or Albert Bartok-Partay. The ASL is also published at
24 | ! HND X http://github.com/gabor1/ASL
25 | ! HND X
26 | ! HND X When using this software, please cite the following reference:
27 | ! HND X
28 | ! HND X A. P. Bartok et al Physical Review Letters vol 104 p136403 (2010)
29 | ! HND X
30 | ! HND X When using the SOAP kernel or its variants, please additionally cite:
31 | ! HND X
32 | ! HND X A. P. Bartok et al Physical Review B vol 87 p184115 (2013)
33 | ! HND X
34 | ! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
35 |
36 | !
37 | !
38 | ! This file contains descriptors written by others, not A. P. Bartok and Gabor Csanyi
39 | ! and the code here is owned by their respective authors, as indicated below.
40 | !
41 |
42 | !
43 | ! Author: Alan Nichol
44 | !
45 |
46 | case(DT_AN_MONOMER)
47 | allocate(distance_matrix(this%descriptor_AN_monomer%N,this%descriptor_AN_monomer%N), atom_permutations(this%descriptor_AN_monomer%N,np))
48 |
49 | if(this%descriptor_AN_monomer%do_atomic) then
50 | atom_permutations(1,:) = 0
51 | call generate_AN_permutations(atom_permutations(2:this%descriptor_AN_monomer%N,:))
52 | atom_permutations = atom_permutations + 1
53 | else
54 | call generate_AN_permutations(atom_permutations(:,:))
55 | endif
56 |
57 | i = 0
58 | distance_matrix = 0
59 | do n = 2, this%descriptor_AN_monomer%N
60 | i = i + 1
61 | distance_matrix(1,n) = i
62 | distance_matrix(n,1) = i
63 | do m = n+1, this%descriptor_AN_monomer%N
64 | i = i + 1
65 | distance_matrix(m,n) = i
66 | distance_matrix(n,m) = i
67 | enddo
68 | enddo
69 |
70 | do ip = 1, np
71 | i = 0
72 | do n = 2, this%descriptor_AN_monomer%N
73 | i = i + 1
74 | permutations(i,ip) = distance_matrix(atom_permutations(1,ip),atom_permutations(n,ip))
75 | do m = n+1, this%descriptor_AN_monomer%N
76 | i = i + 1
77 | permutations(i,ip) = distance_matrix(atom_permutations(m,ip),atom_permutations(n,ip))
78 | enddo
79 | enddo
80 | enddo
81 | deallocate(distance_matrix,atom_permutations)
82 |
83 |
84 | case(DT_GENERAL_MONOMER)
85 | if (.not. this%descriptor_general_monomer%permutation_data%initialised) then
86 | RAISE_ERROR("descriptor_permutations: permutation_data not initialised "//this%descriptor_type,error)
87 | else if (this%descriptor_general_monomer%permutation_data%perm_number /= 1) then
88 | RAISE_ERROR("descriptor_permutations: permutation_data%perm_number must be initialised to one"//this%descriptor_type,error)
89 | end if
90 |
91 | call permutation_data_copy(my_permutation_data, this%descriptor_general_monomer%permutation_data)
92 |
93 | if (my_permutation_data%n_perms > 1) then
94 | call next(my_permutation_data, 1)
95 | end if
96 |
97 | permutations=my_permutation_data%dist_vec_permutations
98 |
99 | case(DT_GENERAL_DIMER)
100 | if (.not. this%descriptor_general_dimer%permutation_data%initialised)then
101 | RAISE_ERROR("descriptor_permutations: permutation_data not initialised "//this%descriptor_type,error)
102 | else if (this%descriptor_general_dimer%permutation_data%perm_number /= 1) then
103 | RAISE_ERROR("descriptor_permutations: permutation_data%perm_number must be initialised to one"//this%descriptor_type,error)
104 | end if
105 |
106 | call permutation_data_copy(my_permutation_data, this%descriptor_general_dimer%permutation_data)
107 |
108 | if (my_permutation_data%n_perms > 1) then
109 | call next(my_permutation_data, 1)
110 | end if
111 |
112 | permutations=my_permutation_data%dist_vec_permutations
113 |
114 | case(DT_GENERAL_TRIMER)
115 | if (.not. this%descriptor_general_trimer%permutation_data%initialised)then
116 | RAISE_ERROR("descriptor_permutations: permutation_data not initialised "//this%descriptor_type,error)
117 | else if (this%descriptor_general_trimer%permutation_data%perm_number /= 1) then
118 | RAISE_ERROR("descriptor_permutations: permutation_data%perm_number must be initialised to one"//this%descriptor_type,error)
119 | end if
120 |
121 | call permutation_data_copy(my_permutation_data, this%descriptor_general_trimer%permutation_data)
122 |
123 | if (my_permutation_data%n_perms > 1) then
124 | call next(my_permutation_data, 1)
125 | end if
126 |
127 | permutations=my_permutation_data%dist_vec_permutations
128 |
129 | case(DT_WATER_TRIMER)
130 | if (.not. this%descriptor_water_trimer%permutation_data%initialised)then
131 | RAISE_ERROR("descriptor_permutations: permutation_data not initialised "//this%descriptor_type,error)
132 | else if (this%descriptor_water_trimer%permutation_data%perm_number /= 1) then
133 | RAISE_ERROR("descriptor_permutations: permutation_data%perm_number must be initialised to one"//this%descriptor_type,error)
134 | end if
135 |
136 | call permutation_data_copy(my_permutation_data, this%descriptor_water_trimer%permutation_data)
137 |
138 | if (my_permutation_data%n_perms > 1) then
139 | call next(my_permutation_data, 1)
140 | end if
141 |
142 | permutations=my_permutation_data%dist_vec_permutations
143 |
144 | case(DT_MOLECULE_LO_D)
145 | if (.not. this%descriptor_molecule_lo_d%permutation_data%initialised) then
146 | RAISE_ERROR("descriptor_permutations: permutation_data not initialised "//this%descriptor_type,error)
147 | else if (this%descriptor_molecule_lo_d%permutation_data%perm_number /= 1) then
148 | RAISE_ERROR("descriptor_permutations: permutation_data%perm_number must be initialised to one"//this%descriptor_type,error)
149 | end if
150 |
151 | call permutation_data_copy(my_permutation_data, this%descriptor_molecule_lo_d%permutation_data)
152 |
153 | if (my_permutation_data%n_perms > 1) then
154 | call next(my_permutation_data, 1)
155 | end if
156 |
157 | allocate(sliced_permutations(size(this%descriptor_molecule_lo_d%included_components),my_permutation_data%n_perms))
158 | allocate(this_perm(size(this%descriptor_molecule_lo_d%included_components)))
159 | sliced_permutations =my_permutation_data%dist_vec_permutations(this%descriptor_molecule_lo_d%included_components,:)
160 |
161 | do j=1,my_permutation_data%n_perms
162 | this_perm=sliced_permutations(:,j)
163 | do i=1,size(this%descriptor_molecule_lo_d%included_components)
164 | unit_vec=maxloc(this%descriptor_molecule_lo_d%included_components, mask=this%descriptor_molecule_lo_d%included_components .eq. this_perm(i))
165 | if (unit_vec(1) == 0) then
166 | RAISE_ERROR("descriptor_permutations: you have specified symmetries between atoms with different connectivity",error)
167 | end if
168 | permutations(i,j) =unit_vec(1)
169 | end do
170 | end do
171 | ! begin brau
172 | if(size(this%descriptor_molecule_lo_d%included_components) > maxval(this%descriptor_molecule_lo_d%included_components)) then
173 | permutations=my_permutation_data%dist_vec_permutations
174 | end if
175 | ! end brau
--------------------------------------------------------------------------------
/descriptors_noncommercial_types.inc:
--------------------------------------------------------------------------------
1 | ! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2 | ! HND X
3 | ! HND X GAP (Gaussian Approximation Potental)
4 | ! HND X
5 | ! HND X
6 | ! HND X Portions of GAP were written by Albert Bartok-Partay, Gabor Csanyi,
7 | ! HND X Copyright 2006-2021.
8 | ! HND X
9 | ! HND X Portions of GAP were written by Noam Bernstein as part of
10 | ! HND X his employment for the U.S. Government, and are not subject
11 | ! HND X to copyright in the USA.
12 | ! HND X
13 | ! HND X GAP is published and distributed under the
14 | ! HND X Academic Software License v1.0 (ASL)
15 | ! HND X
16 | ! HND X GAP is distributed in the hope that it will be useful for non-commercial
17 | ! HND X academic research, but WITHOUT ANY WARRANTY; without even the implied
18 | ! HND X warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | ! HND X ASL for more details.
20 | ! HND X
21 | ! HND X You should have received a copy of the ASL along with this program
22 | ! HND X (e.g. in a LICENSE.md file); if not, you can write to the original licensors,
23 | ! HND X Gabor Csanyi or Albert Bartok-Partay. The ASL is also published at
24 | ! HND X http://github.com/gabor1/ASL
25 | ! HND X
26 | ! HND X When using this software, please cite the following reference:
27 | ! HND X
28 | ! HND X A. P. Bartok et al Physical Review Letters vol 104 p136403 (2010)
29 | ! HND X
30 | ! HND X When using the SOAP kernel or its variants, please additionally cite:
31 | ! HND X
32 | ! HND X A. P. Bartok et al Physical Review B vol 87 p184115 (2013)
33 | ! HND X
34 | ! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
35 |
36 | !
37 | !
38 | ! This file contains descriptors written by others, not A. P. Bartok and Gabor Csanyi
39 | ! and the code here is owned by their respective authors, as indicated below.
40 | !
41 |
42 | !!!! Author: Wojciech Slachta
43 | type bond_real_space
44 | real(dp) :: bond_cutoff
45 | real(dp) :: bond_transition_width
46 | real(dp) :: cutoff
47 | real(dp) :: transition_width
48 | real(dp) :: atom_sigma
49 | integer :: max_neighbours
50 |
51 | logical :: initialised = .false.
52 |
53 | endtype bond_real_space
54 |
55 | !!!! Authors: Alan Nichol and S. T. John
56 |
57 | type AN_monomer
58 | real(dp) :: cutoff
59 | integer :: atomic_number
60 | integer :: N
61 |
62 | logical :: initialised = .false.
63 | logical :: do_atomic = .false.
64 |
65 | endtype AN_monomer
66 |
67 | type general_monomer
68 | type(permutation_data_type) :: permutation_data
69 | integer, dimension(:), allocatable :: signature
70 | real(dp) :: cutoff, cutoff_transition_width
71 | logical :: atom_ordercheck, internal_swaps_only
72 | logical :: strict
73 | real(dp) :: power
74 | logical :: initialised = .false.
75 | endtype general_monomer
76 |
77 | type general_dimer
78 | type(permutation_data_type) :: permutation_data
79 | integer, dimension(:), allocatable :: signature_one, signature_two
80 | integer, dimension(:,:), allocatable :: component_atoms
81 | real(dp) :: cutoff, cutoff_transition_width, monomer_one_cutoff, monomer_two_cutoff
82 | logical :: atom_ordercheck, internal_swaps_only, use_smooth_cutoff, monomers_identical,double_count
83 | logical :: strict, use_com, mpifind, strict_mask
84 | type(transfer_parameters_type) :: transfer_parameters
85 | logical :: initialised = .false.
86 | logical, dimension(:), allocatable :: is_intermolecular, cutoff_contributor
87 | real(dp) :: power,dist_shift
88 | endtype general_dimer
89 |
90 | type general_trimer
91 | type(permutation_data_type) :: permutation_data
92 | integer, dimension(:), allocatable :: signature_one, signature_two, signature_three
93 | integer, dimension(:,:), allocatable :: component_atoms
94 | real(dp) :: cutoff, cutoff_transition_width, monomer_one_cutoff, monomer_two_cutoff, monomer_three_cutoff
95 | logical :: atom_ordercheck, internal_swaps_only, use_smooth_cutoff, one_two_identical, one_three_identical, two_three_identical
96 | logical :: strict, use_com, mpifind
97 | logical :: initialised = .false.
98 | logical, dimension(:), allocatable :: is_intermolecular, cutoff_contributor
99 | real(dp) :: power,dist_shift
100 | endtype general_trimer
101 |
102 |
103 | type water_trimer
104 | !!! 3-body water descriptor
105 | !!! -- Jonatan Öström, @sujona, jonatan.ostrom@gmail.com
106 | type(permutation_data_type) :: permutation_data
107 | integer, dimension(3) :: signature = [8,1,1]
108 | real(dp) :: cutoff, cutoff_transition_width, monomer_cutoff
109 | logical :: atom_ordercheck = .false., internal_swaps_only = .true., use_smooth_cutoff
110 | logical :: initialised = .false., strict
111 | real(dp) :: power,dist_shift
112 | integer, dimension(36,2):: component_atoms
113 | logical, dimension(36) :: is_intermolecular, cutoff_contributor
114 | endtype water_trimer
115 |
116 |
117 | type molecule_lo_d
118 | type(permutation_data_type) :: permutation_data
119 | type(Atoms) :: template_atoms
120 | integer :: n_atoms, max_dimension ! max_dimension is descriptor dimension if include all interatomic distances
121 | integer, dimension(:), allocatable :: signature, included_components
122 | integer, dimension(:,:), allocatable :: component_atoms
123 | real(dp) :: cutoff, cutoff_transition_width
124 | integer :: neighbour_graph_depth
125 | logical :: atom_ordercheck, use_smooth_cutoff
126 | logical :: initialised = .false.
127 | type(Table) :: bonds, atom_pairs
128 | integer :: distance_transform
129 | endtype molecule_lo_d
130 |
131 |
132 | type com_dimer
133 | integer, dimension(:), allocatable :: signature_one, signature_two
134 | real(dp) :: cutoff, cutoff_transition_width, monomer_one_cutoff, monomer_two_cutoff
135 | logical :: atom_ordercheck, use_smooth_cutoff, monomers_identical
136 | logical :: strict, mpifind
137 | type(transfer_parameters_type) :: transfer_parameters
138 | logical :: initialised = .false.
139 | logical, dimension(:), allocatable :: is_intermolecular, cutoff_contributor
140 | endtype com_dimer
141 |
142 | !!!!! Authors: A. P. Bartok and Miguel Caro
143 | type soap_express
144 | ! User controllable parameters
145 | real(dp) :: cutoff
146 | real(dp) :: cutoff_transition_width
147 | real(dp) :: cutoff_decay_rate
148 |
149 | real(dp) :: atom_sigma_radial, atom_sigma_angular, central_weight, covariance_sigma0, &
150 | atom_sigma_scaling_radial, atom_sigma_scaling_angular, amplitude_scaling
151 |
152 | integer :: l_max, n_max
153 |
154 | ! Internal pre-initialised variables
155 | real(dp), dimension(:,:), allocatable :: basis_transformation_coefficients, &
156 | overlap
157 | real(dp), dimension(:), allocatable :: semifactorial_table, Y_lm_prefactor
158 |
159 | integer :: angular_array_size
160 |
161 | logical :: initialised = .false.
162 | endtype soap_express
163 |
164 |
--------------------------------------------------------------------------------
/doc_src/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/doc_src/_static/theme_overrides.css:
--------------------------------------------------------------------------------
1 |
2 | /* override table width restrictions */
3 | .wy-table-responsive table td, .wy-table-responsive table th {
4 | /* !important prevents the common CSS stylesheets from
5 | overriding this as on RTD they are loaded after this stylesheet */
6 | white-space: normal !important;
7 | }
8 |
9 | .wy-table-responsive {
10 | overflow: visible !important;
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/doc_src/_templates/layout.html:
--------------------------------------------------------------------------------
1 | {% extends "!layout.html" %}
2 |
3 | {% block menu %}
4 | {{ super() }}
5 |
6 |
12 | {% endblock %}
13 |
--------------------------------------------------------------------------------
/doc_src/accelerated-aimd-castep-inputs.rst:
--------------------------------------------------------------------------------
1 | ..
2 | Copyright (c) Tamas K. Stenczel, 2023.
3 |
4 |
5 | ********************
6 | Castep Input File
7 | ********************
8 |
9 | This section is about setting up castep's inputs to handle the acceleration.
10 |
11 | Depending on which version you are using, the parameter names are a little different, see the two examples below.
12 |
13 | Castep param file
14 | =================
15 |
16 | The settings governing the acceleration interface are located in the ``.param`` file's ``DEVEL_CODE`` block at
17 | the moment, where the user needs to set a couple of things. However, this can be done once and copied over to
18 | other calculations as-is as long as you keep your ``hybrid-md`` executable in your path rather than setting it
19 | directly, and keeping the GAP model's name the same. Otherwise change these two things.
20 |
21 | The rest of the parameters listed here are recommendations, which should make your life easy or were found to
22 | be efficient.
23 |
24 | DEVEL_CODE block
25 | --------------------
26 |
27 | Set the following:
28 |
29 | ``PP=T`` & ``MD: PP=T :ENDMD`` & ``PP_HYBRID=T`` which turn of the use of non ab-initio force calculators.
30 |
31 | Turn on the usage of QUIP, initialisation arguments of a GAP model, and the path to it. Default is ``GAP.xml`` so
32 | might just keep it as that.
33 |
34 | .. code-block:: text
35 |
36 | pp:
37 | QUIP=T
38 | QUIP_PARAM_FILE=GAP.xml
39 | quip_init_args:IP GAP:endquip_init_args
40 | :endpp
41 |
42 |
43 | Finally, set the decision maker program's path with the ``PP_HYBRID_EXEC`` block.
44 | This is an "external executable" from Castep's point of view.
45 |
46 | **Note on Castep v22:** The code's initial version was included in Castep's v22 academic release, where restarting
47 | calculations was not possible yet, and parameter names had an ``MD_`` prefix. So if you are using v22, please
48 | use ``MD_PP_HYBRID=T`` for turning on and ``MD_PP_HYBRID_EXEC`` for the executable.
49 |
50 | Useful settings in .param
51 | -------------------------
52 |
53 | These are educated recommendations only, feel free to overwrite.
54 |
55 | ``NUM_BACKUP_ITER`` should be comparable to the number of expected MD steps between ab-initio calculations. This
56 | controls the interval between checkpoints, from where restarting is possible. Anything after the checkpoint is
57 | potentially lost if the job stops and needs restarting.
58 |
59 | Use ``continuation: default`` for continuing a calculation where checkpoints were made and the seed name is the same.
60 | Bear in mind, the MD steps is set to 0 after restart but time is incremented, so the directories with previous
61 | GAP models will have indices accordingly.
62 |
63 | ``FINITE_BASIS_CORR: 0`` is recommended. This cannot be done at each ab-initio calculation, and the initial correction
64 | value calculated at the beginning may not be applicable after a long MD run. If you know your system and your simulation
65 | goals do disregard this.
66 |
67 | Given that one is intending to use this method for longer MDs, ``md_sample_iter`` (interval of saving MD frames) should
68 | be adjusted accordingly, since there is some IO cost associated with it as well as disc usage.
69 |
70 | Use tight settings on energy and force convergence, since there provide the input data to your model. Worth starting
71 | with looser settings for initial tests and then tightening for production runs.
72 |
73 | In case of **variable cell calculations**, make sure to use very dense KPoint grid or tight spacing, otherwise the
74 | training data given to GAP may not be consistent enough between frames and will yield sub-optimal model accuracy.
75 |
76 | Example: Castep v23
77 | ======================
78 |
79 | If you are using Castep v23 (academic release only), then then add the following to your `seed.param` file.
80 | Note, the parameter prefixes are ``MD_PP_HYBRID`` unlike in v23.
81 |
82 | .. code-block:: text
83 |
84 | %BLOCK DEVEL_CODE
85 | ! generally turns on PP, this is needed together with "PP_HYBRID=T"
86 | PP=T
87 | MD: PP=T :ENDMD
88 |
89 | ! settings of model called through QUIP
90 | pp:
91 | QUIP=T
92 | QUIP_PARAM_FILE=GAP.xml
93 | quip_init_args:IP GAP:endquip_init_args
94 | :endpp
95 |
96 | ! settings of PP Hybrid MD
97 | PP_HYBRID=T
98 | PP_HYBRID_EXEC:
99 | hybrid-md
100 | :ENDPP_HYBRID_EXEC
101 | %ENDBLOCK DEVEL_CODE
102 |
103 |
104 |
105 | Example: Castep v22
106 | ======================
107 |
108 | If you are using Castep v22 (academic release only), then then add the following to your `seed.param` file.
109 | Note, the parameter prefixes are ``MD_PP_HYBRID`` unlike in v23.
110 |
111 | .. code-block:: text
112 |
113 | %BLOCK DEVEL_CODE
114 | ! generally turns on PP, this is needed together with "MD_PP_HYBRID=T"
115 | PP=T
116 | MD: PP=T :ENDMD
117 |
118 | ! settings of model called through QUIP
119 | pp:
120 | QUIP=T
121 | QUIP_PARAM_FILE=GAP.xml
122 | quip_init_args:IP GAP:endquip_init_args
123 | :endpp
124 |
125 | ! settings of PP Hybrid MD
126 | MD_PP_HYBRID=T
127 | MD_PP_HYBRID_EXEC:
128 | hybrid-md
129 | :ENDMD_PP_HYBRID_EXEC
130 | %ENDBLOCK DEVEL_CODE
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/doc_src/accelerated-aimd-input-reference.rst:
--------------------------------------------------------------------------------
1 | ..
2 | Copyright (c) Tamas K. Stenczel, 2023.
3 |
4 | .. _accelerated-aimd-input-reference:
5 |
6 | ********************
7 | Input file reference
8 | ********************
9 |
10 | The acceleration code has a single input file, which defines the parameters of the
11 | decision making and refitting. Everything else is handled in the ab-initio code.
12 |
13 | The file should be called as ``.hybrid-md-input.yaml`` where the ```` is
14 | the calculation's filename prefix supplied by the ab-initio code as well.
15 |
16 | See the possible contents and a couple of example below. For runnable example, see
17 | the simulation examples.
18 |
19 | Core & Mandatory parameters
20 | ===========================
21 |
22 | A minimal example:
23 |
24 | .. code-block:: yaml
25 |
26 | # this is a complete seed.hybrid-md-input.yaml files
27 | can_update: true
28 | check_interval: 5
29 | num_initial_steps: 2
30 | tolerances:
31 | ediff: 0.005 # eV
32 |
33 | This sets the following:
34 |
35 | * ``can_update: true``: the model can be updated (default false)
36 |
37 | * ``check_interval: 5``: interval between ab-initio calculations (see :ref:`Fixed interval `)
38 |
39 | * ``num_initial_steps: 2``: the first 2 steps are ab-initio and used to train the first model
40 |
41 | * ``tolerances:`` block of tolerances for accepting the result
42 | - ``ediff: 0.005``: 5 meV/atom is the maximum allowed for continuations with the same model
43 |
44 |
45 | Tolerance section
46 | =================
47 |
48 | .. code-block:: yaml
49 |
50 | # nb. partial input only
51 | tolerances:
52 | ediff: 0.01 # eV
53 | fmax: null # eV/A
54 | frmse: 0.100 # eV/A
55 | vmax: null # eV (virial)
56 |
57 |
58 | Tolerances can be specified for the following quantitities:
59 |
60 | * ``ediff``: energy per atom difference
61 |
62 | * ``fmax``: force-component maximum absolute difference (in eV/Å)
63 |
64 | * ``frmse``: RMSE of force components (in eV/Å)
65 |
66 | * ``vmax``: maximum virial stress difference, in eV
67 |
68 | At least one of these needs to be set, specifying ``null`` and omitting the parameter
69 | are equivalent. When checking them, the ones turned on are all used and the result needs
70 | to be below all of them to pass the check.
71 |
72 | Accuracy-Adapted method section
73 | ===============================
74 |
75 | Section of parameters for the :ref:`Accuracy-adapted checking interval mode `. The existence
76 | of the block turns this decision making method on.
77 |
78 | .. code-block:: yaml
79 |
80 | # nb. partial input only
81 | check_interval: 10
82 | adaptive_method_parameters:
83 | n_min: 10
84 | n_max: 5000
85 | factor: 1.3
86 |
87 |
88 | * ``check_interval: 10``: initial checking interval to start with
89 |
90 | * ``n_min`` & ``n_max``: bounds for the number of steps between ab-initio steps
91 |
92 | * ``factor: 1.3``: the geometric factor for increasing and decreasing the checking interval
93 |
94 | Refitting-specific settings
95 | ===========================
96 |
97 | Parameters for controlling the GAP model to be trained when the model is updated.
98 |
99 | You can use the pre-set SOAP parameters, or supply your own entirely.
100 |
101 | By default, the trained model includes a 2-body and a SOAP descriptor for all species, both with 5Å cutoff. *n.b. This may fail if you only have a single atom of any element, since there will be no data to train the self-interaction 2-body part for this element.*
102 |
103 | .. code-block:: yaml
104 |
105 | # nb. partial input only
106 | refit:
107 | e0_method: "isolated"
108 | num_threads: 128
109 |
110 | * ``e0_method`` sets the method for GAP to choose the element-specific constant shift.
111 | - default is ``average``
112 | - see :ref:`gap_fit page ` for further information
113 |
114 | * ``num_threads: 128`` tells the program to use 128 OMP threads for the fitting, this is set temporarily only and is reset to the previous value after fitting. Does not affect the parallelism of the ab-initio code. *n.b. this only single-node fitting is supported for GAP models at the moment for the acceleration program.*
115 |
116 | One can additionally set ``e0`` in this section for passing explicit values, as in the ``gap_fit`` program.
117 |
118 | .. code-block:: yaml
119 |
120 | # nb. partial input only
121 | refit:
122 | e0_method: "isolated"
123 | num_threads: 128
124 |
125 | Previous data
126 | -------------
127 |
128 | Previous data can be included in the model training, through specifying a list of xyz filenames under the ``previous_data`` parameter. These are expected to have their energy, force, and virial results saved with the same keywords as how the ab-initio code is saving them: ``QM_energy``, ``QM_forces``, ``QM_virial``, respectively.
129 |
130 |
131 |
132 | Descriptor Parameters
133 | ---------------------
134 |
135 | You can choose the descriptor parameters in multiple ways:
136 |
137 | * using pre-set SOAP parameter values: ``preset_soap_param: "fast" | "medium" | "accurate"``, which then includes a 2-body as well
138 |
139 | * using an explicit descriptor string: ``descriptor_str: "..."`` In this case the 2-body descriptor is not added.
140 |
141 | Other parameters
142 | ----------------
143 |
144 | The rest of the parameters, which are mostly mimicking the keywords of ``gap_fit``. See the :ref:`Defaults ` section for the for the default values of these.
145 |
146 | * ``gp_name`` name of model XML file
147 |
148 | * ``default_sigma`` default kernel regularisation
149 |
150 | * ``extra_gap_opts`` anything else to pass to ``gap_fit``
151 |
152 | * ``function_name`` allows the user to specify any python function installed in their environment by module import path for handling the refitting of the model. If this is set, then this function is used and everything else is ignored (though that function can see these parameters). This is an advanced developer feature.
153 |
154 |
155 | Defaults
156 | --------
157 |
158 | .. _defaults:
159 |
160 | The following input file contains the default values set explicitly. *n.b. this is not allowing refitting, due to* ``can_update: false``
161 |
162 | .. code-block:: yaml
163 |
164 | can_update: false
165 | check_interval: 1
166 | num_initial_steps: 0
167 | tolerances:
168 | ediff: 0.03
169 | fmax: null
170 | frmse: null
171 | vmax: null
172 | refit:
173 | function_name: null
174 | previous_data: []
175 | preset_soap_param: "fast"
176 | gp_name: "GAP.xml"
177 | default_sigma: "0.005 0.050 0.1 1.0"
178 | descriptor_str: null
179 | extra_gap_opts: "sparse_jitter=1.0e-8"
180 | e0: null
181 | e0_method: "average"
182 | num_threads: null
183 |
184 |
185 |
--------------------------------------------------------------------------------
/doc_src/accelerated-aimd.rst:
--------------------------------------------------------------------------------
1 | ..
2 | Copyright (c) Tamas K. Stenczel, 2023.
3 |
4 | .. _accelerated-aimd:
5 |
6 | *******************************************************
7 | Acceleration of Ab-initio MD with On-the-Fly GAP Models
8 | *******************************************************
9 |
10 | This is the documentation of the ``hybrid-md`` package bundled with GAP, which provides a general framework of
11 | accelerating Ab-initio MD (AIMD) with Machine Learning models generated "On-the-Fly".
12 |
13 | The framework is intended to be agnostic of the AIMD code and the ML code as well. For the time being, an interface
14 | between the CASTEP code and GAP is available publicly, integration of other ab-initio codes and ML frameworks is in
15 | the making. If you would have any software/research ideas, or would like to contribute in any way, please feel free
16 | to reach out to with G. Csányi and T. K. Stenczel for further details.
17 |
18 | Getting started & Usage
19 | ***********************
20 |
21 | .. toctree::
22 | :maxdepth: 2
23 |
24 | accelerated-aimd_installation.rst
25 | accelerated-aimd-input-reference.rst
26 | accelerated-aimd_decision-making.rst
27 | accelerated-aimd-castep-inputs.rst
28 |
29 | Examples
30 | ********
31 |
32 | .. toctree::
33 | :maxdepth: 2
34 |
35 | accelerated-aimd_example_Si16.rst
36 |
37 |
38 | Program structures and design
39 | *****************************
40 |
41 | The program is intended to be user-accessible and extendable. CASTEP implements
42 | an interface to external force evaluation codes - including QUIP/GAP.
43 |
44 | Design Principles
45 | #################
46 |
47 | The interface provides acceleration of an AIMD simulation, according to the following design principles:
48 |
49 | 1. MD is driven by the AIMD code (this is the main program)
50 |
51 | 2. Decision making program independent of AIMD code
52 |
53 | 3. ML code is used by the AIMD code only for force evaluation, and by decision making code for updating of models
54 |
55 | 4. The system is agnostic of programming languages used, the decision making code could be implemented in any other language as long as the API is the same
56 |
57 | Extendability & Development
58 | ***************************
59 |
60 | Users can extend the framework in multiple ways:
61 |
62 | #. Build custom decision making routines: e.g.
63 | - system specific adaptations
64 | - utilising confidence estimates of models
65 | - any external tools working out when the model is extrapolating / could use more data: e.g. sample more when connectivity changes - bond break/form
66 | #. Support other AIMD codes: There are no specific requirements apart from an internal MD loop being available. Please get in touch with G. Csányi and T. K. Stenczel for further details.
67 | #. Support for other ML tools - the framework is not depending on GAP
68 |
69 |
--------------------------------------------------------------------------------
/doc_src/accelerated-aimd_decision-making.rst:
--------------------------------------------------------------------------------
1 | ..
2 | Copyright (c) Tamas K. Stenczel, 2023.
3 |
4 | .. _accelerated-aimd-decision-making:
5 |
6 | Decision making
7 | ***************
8 |
9 | Decision making methods are governing which forces are used at each step, and when to
10 | refit a model. This is a key part of any acceleration and on-the-fly model improvement
11 | cycle.
12 |
13 | We currently supply two methods, which directly compare the GAP and ab-initio energy
14 | and forces. One is using a fixed interval for performing ab-initio calculations, while
15 | the other one adapts to the accuracy of the model. Both make use of the tolerances set
16 | by the user in the input file for deciding when to retrain the model.
17 |
18 | For general calculations, particularly ones starting from scratch, **we recommend using
19 | the accuracy-adapted decision making method.**
20 |
21 | Initial steps
22 | =============
23 |
24 | Regardless of the decision making method, the method allows for a number of initial
25 | ab-initio MD steps to be performed. This is useful for starting from scratch, where no
26 | previous model is available.
27 |
28 | In order to activate this set ``num_initial_steps > 0`` in the input file.
29 |
30 | Fixed Interval Decision Making
31 | ==============================
32 |
33 | .. _fixed interval:
34 |
35 | Ab-initio calculations are performed at fixed intervals. Set the fixed interval with the
36 | ``check_interval`` keywords in the input file.
37 |
38 | If any of the tolerance criteria are not met at this point, then the model is re-trained
39 | with the available data.
40 |
41 | Accuracy-Adapted Checking Interval
42 | ==================================
43 |
44 | .. _adaptive interval:
45 |
46 | This method adapts the interval between ab-initio steps according to a geometric factor
47 | parameter, within user-specified upper and lower bounds. You need to specify the starting
48 | interval, factor, and the bounds.
49 |
50 | Upon meeting the desired tolerances (i.e. being below the limits) at checking steps, the
51 | interval until the next one is increased, otherwise the model is re-trained with the
52 | newly available observations and the interval decreased by the factor specified.
53 |
54 | This method is turned on by supplying the ``adaptive_method_parameters: ...`` input
55 | block in the decision making input file.
56 |
57 | Further development
58 | ===================
59 |
60 | Further developments are planned and are very welcome. There are lots of open avenues that can be explored with the framework provided here. Some of these the current developers have thought of, but that is surely a small subset of what people can and want to use this for.
61 |
62 | See a few examples below. If you are interested in collaborating on any of these, or have any other idea then please feel free to reach out, we are more than happy to work together or help others get started with the code.
63 |
64 | * usage of bonding/structural information: trigger refitting when atomic connectivity is changed, or when the dynamics gets closer to potential bond breaking. Might be useful for modelling reactive systems, etc.
65 |
66 | * use similarity to a database, say through SOAP descriptor distance, trigger data acquisition when this hits a threshold of moving further from the previous data
67 |
68 | * integration of other ML and ab-initio codes (some in the making already)
69 |
70 |
--------------------------------------------------------------------------------
/doc_src/accelerated-aimd_example_Si16.rst:
--------------------------------------------------------------------------------
1 | ..
2 | Copyright (c) Tamas K. Stenczel, 2023.
3 |
4 | ******************************************************
5 | Si16 - Walkthrough of a Simple Calculation
6 | ******************************************************
7 |
8 | This is a simple walkthrough of an experiment that one can try out, even on a laptop.
9 |
10 | We will be running MD of a small Si cell.
11 |
12 | First calculation
13 | ******************************************************
14 |
15 | We will start out by running a short MD, starting with 3 steps of ab-initio calculations, training a model
16 | on that, and later only doing ab-initio calculations at every 10th step. We are running a total of 50 MD steps.
17 |
18 | Write the following in the input file ``si16.hybrid-md-input.yaml``.
19 |
20 | .. code-block:: yaml
21 |
22 | can_update: true
23 | check_interval: 10
24 | num_initial_steps: 3
25 | tolerances:
26 | ediff: 0.01 # eV
27 |
28 | You need ``si16.cell`` and ``si16.param`` files as well. See examples here, of 8 atom si.
29 |
30 | ``si16.cell``:
31 |
32 | .. code-block:: text
33 |
34 | %BLOCK LATTICE_CART
35 | 7.679180 0.000000 0.000000
36 | 0.000000 7.679180 0.000000
37 | 0.000000 0.000000 5.430000
38 | %ENDBLOCK LATTICE_CART
39 |
40 | %BLOCK POSITIONS_ABS
41 | Si 0.000000 0.000000 0.000000
42 | Si 1.919795 0.000000 1.357500
43 | Si 1.919795 1.919795 2.715000
44 | Si 0.000000 1.919795 4.072500
45 | Si 0.000000 3.839590 0.000000
46 | Si 1.919795 3.839590 1.357500
47 | Si 1.919795 5.759385 2.715000
48 | Si 0.000000 5.759385 4.072500
49 | Si 3.839590 0.000000 0.000000
50 | Si 5.759385 0.000000 1.357500
51 | Si 5.759385 1.919795 2.715000
52 | Si 3.839590 1.919795 4.072500
53 | Si 3.839590 3.839590 0.000000
54 | Si 5.759385 3.839590 1.357500
55 | Si 5.759385 5.759385 2.715000
56 | Si 3.839590 5.759385 4.072500
57 | %ENDBLOCK POSITIONS_ABS
58 |
59 |
60 |
61 | ``si16.param``:
62 |
63 | .. code-block:: text
64 |
65 | ###############################################################
66 | # functional & general settings
67 | ###############################################################
68 | xc_functional LDA
69 | basis_precision PRECISE ! basis set size from presets
70 | fix_occupancy true
71 | opt_strategy speed ! faster runtime with more memory use
72 |
73 | # allow continuation
74 | NUM_BACKUP_ITER = 10 ! interval between checkpoints (.check file)
75 | continuation: default ! continuation if .check file exists
76 |
77 | ###############################################################
78 | # MD settings
79 | ###############################################################
80 | task = molecular dynamics
81 |
82 | md_ensemble = NVT
83 | md_thermostat = Langevin
84 | md_num_iter = 50 ! number of MD steps we will take
85 | md_temperature = 300 K
86 | md_sample_iter = 10 ! interval between dumping MD frames
87 | md_delta_t = 1 fs
88 |
89 | ###############################################################
90 | # Devel code: contains the `PP_HYBRID` method settings
91 | ###############################################################
92 | %BLOCK DEVEL_CODE
93 | ! turn on the PP & acceleration modules
94 | PP=T
95 | MD: PP=T :ENDMD
96 | PP_HYBRID=T
97 |
98 | ! settings of model called through QUIP
99 | pp:
100 | QUIP=T
101 | QUIP_PARAM_FILE=GAP.xml
102 | quip_init_args:IP GAP:endquip_init_args
103 | :endpp
104 |
105 | ! settings of PP Hybrid MD
106 |
107 | PP_HYBRID_EXEC:
108 | hybrid-md
109 | :endPP_HYBRID_EXEC
110 | %ENDBLOCK DEVEL_CODE
111 |
112 | Let's run this!
113 |
114 | .. code-block:: bash
115 |
116 | mpirun -n 4 castep.mpi si-0
117 |
118 | The code should produce the following:
119 |
120 | * standard Castep outputs:
121 | * ``si16.castep`` main output file (worth reading)
122 | * ``si16.md`` MD trajectory
123 | * a few more castep files, including a bibliography of the parts used
124 | * ``GAP.xml`` model and associated ``GAP.xml.sparseX_`` sparse point files. *This is the latest model trained*
125 | * ``train.xyz``: the assembled training set of the last model
126 | * ``GAP.xml.fit-stderr.`` & ``GAP.xml.fit-stdout.`` are the output streams of the ``gap_fit`` program. Worth taking a look at.
127 | * ``GAP_model-step000..-at-/`` directories with previous GAP models. The number in the name is when the model was replaced by the next one
128 | * ``si16.hybrid-md.xyz``: all ab-initio observations from this calculation, where forces are gradients of the energy and don't include thermostat components.
129 |
130 |
131 | Accuracy-Adapted Checking Interval
132 | ******************************************************
133 |
134 | This was all well, but we can do much much better: once the model has some more training data then we likely don't need to
135 | perform an ab-initio calculation every 10 steps. We can use our other decision making method, where the number of steps between
136 | checks are adapting to the accuracy of the model. See more at :ref:`Accuracy-adapted checking interval mode `.
137 |
138 | Let's move to a new directory, change the ``si16.hybrid-md-input.yaml`` to the following, increase the number of MD steps we are taking
139 | (``md_num_iter = 200`` in ``si16.param``), and run the program again. Best done in a different directory.
140 |
141 | .. code-block:: text
142 |
143 | can_update: true
144 | check_interval: 10
145 | num_initial_steps: 3
146 | tolerances:
147 | ediff: 0.01 # eV
148 | adaptive_method_parameters: # <---
149 | n_min: 5 # <---
150 | n_max: 100 # <---
151 | factor: 1.5 # <---
152 |
153 | This should produce a more interesting output, where the number of steps between ab-initio calculations is not fixed, but lowe initially and
154 | then increases towards the end of the calculation. We can look at the output, where an error table is shown:
155 |
156 | .. code-block:: text
157 |
158 | Hybrid-MD: MD iteration 49 <-- Hybrid-MD
159 | -------------+------------------+------------------+------------+-----+ <-- Hybrid-MD
160 | Parameter | value | tolerance | units | OK? | <-- Hybrid-MD
161 | -------------+------------------+------------------+------------+-----+ <-- Hybrid-MD
162 | |Ediff| | 0.00228307 | 0.01000000 | eV/at | Yes | <-- Hybrid-MD
163 | max |Fdiff| | 1.24702641 | Off | eV/Å | | <-- Hybrid-MD
164 | force RMSE | 0.35238182 | Off | eV/Å | | <-- Hybrid-MD
165 | max |Vdiff| | 0.00000000 | Off | eV | | <-- Hybrid-MD
166 | -------------+------------------+------------------+------------+-----+ <-- Hybrid-MD
167 | No Refit <-- Hybrid-MD
168 | -------------+------------------+------------------+------------+-----+ <-- Hybrid-MD-Cumul
169 | Cumulative RMSE value count: 11 | units | <-- Hybrid-MD-Cumul
170 | -------------+------------------+------------------+------------+-----+ <-- Hybrid-MD-Cumul
171 | Energy 0.03409695 | eV/atom | <-- Hybrid-MD-Cumul
172 | Forces 0.82637217 | eV/Å | <-- Hybrid-MD-Cumul
173 | Virial 0.00000000 | eV | <-- Hybrid-MD-Cumul
174 | -------------+------------------+------------------+------------+-----+ <-- Hybrid-MD-Cumul
175 | Hybrid-MD: INCREASE interval to 7 at iter 49 <-- Hybrid-MD-Adapt
176 |
177 | Here we can see a checking step, where the tolerance was met, so we are not refitting and are continuing with the previous model,
178 | allowing for a longer interval until the next check. Otherwise we would refit and decrease the interval.
179 |
180 | One can see all decisions made quickly by ``grep CREAS si16.castep``, which should produce something like:
181 |
182 | .. code-block:: text
183 |
184 | Hybrid-MD: DECREASE interval to 6 at iter 13 <-- Hybrid-MD-Adapt
185 | Hybrid-MD: DECREASE interval to 5 at iter 19 <-- Hybrid-MD-Adapt
186 | Hybrid-MD: DECREASE interval to 5 at iter 24 <-- Hybrid-MD-Adapt
187 | Hybrid-MD: DECREASE interval to 5 at iter 29 <-- Hybrid-MD-Adapt
188 | Hybrid-MD: DECREASE interval to 5 at iter 34 <-- Hybrid-MD-Adapt
189 | Hybrid-MD: DECREASE interval to 5 at iter 39 <-- Hybrid-MD-Adapt
190 | Hybrid-MD: DECREASE interval to 5 at iter 44 <-- Hybrid-MD-Adapt
191 | Hybrid-MD: INCREASE interval to 7 at iter 49 <-- Hybrid-MD-Adapt
192 | Hybrid-MD: INCREASE interval to 10 at iter 56 <-- Hybrid-MD-Adapt
193 | Hybrid-MD: INCREASE interval to 15 at iter 66 <-- Hybrid-MD-Adapt
194 | Hybrid-MD: INCREASE interval to 22 at iter 81 <-- Hybrid-MD-Adapt
195 | Hybrid-MD: DECREASE interval to 14 at iter 103 <-- Hybrid-MD-Adapt
196 | Hybrid-MD: INCREASE interval to 21 at iter 117 <-- Hybrid-MD-Adapt
197 | Hybrid-MD: INCREASE interval to 31 at iter 138 <-- Hybrid-MD-Adapt
198 | Hybrid-MD: INCREASE interval to 46 at iter 169 <-- Hybrid-MD-Adapt
199 |
200 | Where we can see how the interval quickly increased after some initial burn-in time. It is worth experimenting with the settings here,
201 | seeing what is the maximum ``factor`` and ``n_max`` value which produces stable MD, so one can speed the calculation up as much as possible.
202 |
203 | Continue a calculation
204 | ******************************************************
205 |
206 | If we don't change anything in the directory where we ran our precious calculation, we can run the same again and
207 | continue where we left off. Well, at the last checkpoint. You can increase the number of MD steps to say 1000 now and
208 | see the results.
209 |
210 | As you can see below, the interval has hit the ceiling, then the model was re-trained once at step 384 and slowed down
211 | there, but hit the ceiling after again. Please note, that the number of steps is reset to 0 at the start of the MD run
212 | by Castep and only time is incremented, while concatenating to the same output file.
213 |
214 | .. code-block:: text
215 |
216 | Hybrid-MD: DECREASE interval to 6 at iter 13 <-- Hybrid-MD-Adapt
217 | Hybrid-MD: DECREASE interval to 5 at iter 19 <-- Hybrid-MD-Adapt
218 | Hybrid-MD: DECREASE interval to 5 at iter 24 <-- Hybrid-MD-Adapt
219 | Hybrid-MD: DECREASE interval to 5 at iter 29 <-- Hybrid-MD-Adapt
220 | Hybrid-MD: DECREASE interval to 5 at iter 34 <-- Hybrid-MD-Adapt
221 | Hybrid-MD: DECREASE interval to 5 at iter 39 <-- Hybrid-MD-Adapt
222 | Hybrid-MD: DECREASE interval to 5 at iter 44 <-- Hybrid-MD-Adapt
223 | Hybrid-MD: INCREASE interval to 7 at iter 49 <-- Hybrid-MD-Adapt
224 | Hybrid-MD: INCREASE interval to 10 at iter 56 <-- Hybrid-MD-Adapt
225 | Hybrid-MD: INCREASE interval to 15 at iter 66 <-- Hybrid-MD-Adapt
226 | Hybrid-MD: INCREASE interval to 22 at iter 81 <-- Hybrid-MD-Adapt
227 | Hybrid-MD: DECREASE interval to 14 at iter 103 <-- Hybrid-MD-Adapt
228 | Hybrid-MD: INCREASE interval to 21 at iter 117 <-- Hybrid-MD-Adapt
229 | Hybrid-MD: INCREASE interval to 31 at iter 138 <-- Hybrid-MD-Adapt
230 | Hybrid-MD: INCREASE interval to 46 at iter 169 <-- Hybrid-MD-Adapt
231 | Hybrid-MD: INCREASE interval to 69 at iter 15 <-- Hybrid-MD-Adapt
232 | Hybrid-MD: INCREASE interval to 100 at iter 84 <-- Hybrid-MD-Adapt
233 | Hybrid-MD: INCREASE interval to 100 at iter 184 <-- Hybrid-MD-Adapt
234 | Hybrid-MD: INCREASE interval to 100 at iter 284 <-- Hybrid-MD-Adapt
235 | Hybrid-MD: DECREASE interval to 66 at iter 384 <-- Hybrid-MD-Adapt # n.b. refitting the model here
236 | Hybrid-MD: INCREASE interval to 99 at iter 450 <-- Hybrid-MD-Adapt
237 | Hybrid-MD: INCREASE interval to 100 at iter 549 <-- Hybrid-MD-Adapt
238 | Hybrid-MD: INCREASE interval to 100 at iter 649 <-- Hybrid-MD-Adapt
239 | Hybrid-MD: INCREASE interval to 100 at iter 749 <-- Hybrid-MD-Adapt
240 | Hybrid-MD: INCREASE interval to 100 at iter 849 <-- Hybrid-MD-Adapt
241 | Hybrid-MD: INCREASE interval to 100 at iter 949 <-- Hybrid-MD-Adapt
242 |
243 | From here, you can restart the calculation again any time and change the settings of the adaptive interval. Try what
244 | happens if you change the ceiling it to something much longer and let it run a longer calculation.
245 |
246 | Re-use previous data & Set your own GAP parameters
247 | ******************************************************
248 |
249 | We can see above how to go from a single structure and a few lines of simple settings to close to 100x accelerated ab-initio MD
250 | plus a GAP model we can use for anything else as well.
251 |
252 | Let's see how we can refine the model. We are running MD of Si here, we might want to tune the GAP model's settings to
253 | the system. Along with that, we will allow the GAP model fitting to happen in parallel as well.
254 |
255 | Finally, we will include the previous calculation's ab-initio observations, by copying the ``si16.hybrid-md.xyz`` file into
256 | the new job directory as ``previous_si16.hybrid-md.xyz``.
257 |
258 | Let's update the input file to the following:
259 |
260 | .. code-block:: yaml
261 |
262 | can_update: true
263 | check_interval: 10
264 | num_initial_steps: 1
265 | tolerances:
266 | ediff: 0.01 # eV
267 | adaptive_method_parameters:
268 | n_min: 5
269 | n_max: 200
270 | factor: 1.5
271 | refit:
272 | e0_method: "average"
273 | num_threads: 4
274 | descriptor_str: "distance_Nb order=2 n_sparse=20 cutoff=5.5 cutoff_transition_width=1.0 compact_clusters covariance_type=ard_se theta_uniform=1.0 sparse_method=uniform f0=0.0 add_species=T delta=1.0 : soap n_sparse=1000 n_max=10 l_max=4 cutoff=5.0 cutoff_transition_width=1.0 atom_sigma=0.5 add_species=True covariance_type=dot_product zeta=4 sparse_method=cur_points delta=3.0 "
275 | previous_data: [
276 | "previous_si16.hybrid-md.xyz"
277 | ]
278 | default_sigma: "0.005 0.1 0.05 1.0"
279 |
280 | Let's see what we have done in the refitting section
281 |
282 | * increased the maximum number of steps between ab-initio evaluations to 200
283 | * ``num_threads: 4`` allows the ``gap_fit`` program to be executed with 4 OMP threads, while the main Castep program's 4 MPI processes are waiting
284 | * the ``descriptor_str`` line is settings the descriptors that GAP uses, these are cut down versions of the `GAP-18 Si model `_
285 | * the GP's kernel regularisation is updated with ``default_sigma``, :ref:`see usage of gap_fit `
286 |
287 | Additionally, we can rattle the Si structure at the start of the calculation. Castep can do this by adding ``positions_noise 0.1 Ang``
288 | to the ``si16.cell`` input file.
289 |
290 | Having done all of these, run the calculation just as before and see the results.
291 |
292 | You will notice in the ``gap_fit`` program outputs that 4 threads are used and that the previous data is included.
293 | The calculation will start with a single ab-initio evaluation and model training, which is then followed by the
294 | interval between checking steps quickly increasing.
295 |
296 | See the relevant lines from ``si16.castep``:
297 |
298 | .. code-block:: text
299 |
300 | Hybrid-MD: INCREASE interval to 15 at iter 11 <-- Hybrid-MD-Adapt
301 | Hybrid-MD: INCREASE interval to 22 at iter 26 <-- Hybrid-MD-Adapt
302 | Hybrid-MD: INCREASE interval to 33 at iter 48 <-- Hybrid-MD-Adapt
303 | Hybrid-MD: INCREASE interval to 49 at iter 81 <-- Hybrid-MD-Adapt
304 | Hybrid-MD: INCREASE interval to 73 at iter 130 <-- Hybrid-MD-Adapt
305 | Hybrid-MD: INCREASE interval to 109 at iter 203 <-- Hybrid-MD-Adapt
306 | Hybrid-MD: INCREASE interval to 163 at iter 312 <-- Hybrid-MD-Adapt
307 | Hybrid-MD: INCREASE interval to 200 at iter 475 <-- Hybrid-MD-Adapt
308 | Hybrid-MD: INCREASE interval to 200 at iter 675 <-- Hybrid-MD-Adapt
309 | Hybrid-MD: INCREASE interval to 200 at iter 875 <-- Hybrid-MD-Adapt
310 |
311 |
312 | What next
313 | ******************************************************
314 |
315 | You have learnt how to use the method supplied and were able to perform some short MD with a model trained on the fly.
316 | You can expand this, by running more meaningful longer MD, or trying different temperatures, or include defects in the
317 | structure and investigate those.
318 |
319 | The real test and valuable next step is trying this out for your own systems of interest. Think of what small simulation
320 | you could do which you may already have the structures and settings for. Try plugging it into Castep and turn the
321 | MD acceleration on, follow the ideas and steps learnt here, and see if anything useful comes out of it.
322 |
323 | If you need any assistance, don't hesitate to contact the authors of this package.
324 |
--------------------------------------------------------------------------------
/doc_src/accelerated-aimd_installation.rst:
--------------------------------------------------------------------------------
1 | ..
2 | Copyright (c) Tamas K. Stenczel, 2023.
3 |
4 | .. _accelerated-aimd-installation:
5 |
6 | Installation with CASTEP
7 | ************************
8 |
9 | You will need the following program components:
10 |
11 | #. QUIP with GAP
12 | - MPI-only library for linking to Castep
13 | - OpenMP ``gap_fit`` program
14 | #. CASTEP: Academic release v22 and above
15 | #. Python installation of ``hybrid-md`` - bundled with GAP
16 |
17 | The two builds of QUIP are needed due to the different parallelism supported by the fitting and evaluation.
18 |
19 | Linux - general setup
20 | #####################
21 |
22 | These are general instructions for a Linux machine, assuming the libraries needed
23 | for the respective packages are installed. See documentation of QUIP & CASTEP for
24 | these.
25 |
26 | QUIP+GAP installations
27 | ----------------------
28 |
29 | Clone the code, make sure to include the subpackages as well using the `--recursive` flag.
30 |
31 | .. code-block:: bash
32 |
33 | # clone the QUIP repository
34 | git clone --recursive https://github.com/libAtoms/QUIP.git
35 | cd QUIP
36 |
37 |
38 | Build the OpenMP version for fitting
39 |
40 | .. code-block:: bash
41 |
42 | # arch: any compiler + OpenMP
43 | export QUIP_ARCH=linux_x86_64_gfortran_openmp
44 |
45 | # configure: build - Make sure to say yes to GAP
46 | make config
47 |
48 | # build: the gap_fit program
49 | make gap_programs
50 |
51 | # copy the executable to your desired installation directory
52 | cp ./build/$QUIP_ARCH/gap_fit ...your bin dir...
53 |
54 | Build an MPI library version for linking to Castep. Make sure to use the same compiler
55 | as for the Castep installation later on.
56 |
57 | .. code-block:: bash
58 |
59 | # still in the QUIP directory created above
60 |
61 | # remember this directory for configuring CASTEP
62 | export QUIP_ROOT=$(pwd)
63 |
64 | # arch: any compiler + MPI
65 | export QUIP_ARCH=linux_x86_64_gfortran_openmpi
66 |
67 | # configure build - Make sure to say yes to GAP
68 | make config
69 |
70 | # build: only the library
71 | make libquip.a
72 |
73 | Castep
74 | ------
75 |
76 | See the installation notes and documentation in your CASTEP source distribution,
77 | the relevant part for this program is the inclusion of QUIP, which you can do by
78 | specifying ``QUIP=system`` in the main ``Makefile`` plus providing the
79 | ``QUIP_ROOT`` (root directory of QUIP repo) and ``QUIP_ARCH`` (the MPI one).
80 | These can be directly placed into the ``Makefile`` of CASTEP as well to be
81 | exported for easier resume of build or rebuild later.
82 |
83 | .. code-block:: makefile
84 |
85 | # make sure these are specified
86 | COMMS_ARCH := mpi
87 | QUIP := system
88 |
89 | # example of explicitly setting the location of QUIP
90 | export QUIP_ROOT=...directory from above...
91 | export QUIP_ARCH=linux_x86_64_gfortran_openmpi
92 |
93 | Having set these, just build and install CASTEP.
94 |
95 |
96 | ``hybrid-md`` Python package
97 | ----------------------------
98 |
99 | Simply install in the package from source, found in the ``GAP/hybrid_md_package/`` directory of this repo.
100 |
101 | This is located at ``QUIP/src/GAP/hybrid_md_package/`` if you are looking at the QUIP source downloaded above.
102 |
103 | .. code-block:: bash
104 |
105 | python -m pip install .
106 |
107 | Archer2 cluster
108 | ###############
109 |
110 | These are specific and tested instructions the UK's Archer2 https://www.archer2.ac.uk computer cluster.
111 |
112 | An important gotcha on Archer2 is that the built-in maths libraries of the compiler are linking MPI by default, which
113 | breaks the setup for the ``gap_fit`` program, so we need to build that explicitly without MPI, see below.
114 |
115 | Edit the CASTEP Makefile to include the following
116 |
117 | .. code-block:: makefile
118 |
119 | COMMS_ARCH := mpi
120 | FFT := fftw3
121 | BUILD := fast
122 | MATHLIBS := mkl # optional
123 |
124 | Full installation:
125 |
126 | .. code-block:: bash
127 |
128 | # create bin directory for executables
129 | mkdir bin
130 |
131 | # Clone QUIP
132 | git clone --recursive https://github.com/libAtoms/QUIP.git --depth 1 --single-branch
133 |
134 | # load the correct modules
135 | module load cray-python
136 | module switch PrgEnv-cray PrgEnv-gnu/8.1.0
137 | module load cpe/22.04
138 | module load cray-fftw
139 | module load mkl/2023.0.0 # if using MKL for Castep
140 |
141 | # step 1: Python interpreter & installation of hybrid-md
142 | python -m virtualenv venv
143 | source venv/bin/activate
144 |
145 | python -m pip install ./QUIP/src/GAP/hybrid_md_package/
146 |
147 | # step 2: QUIP with MPI
148 | cd QUIP
149 | export QUIP_ROOT=$(pwd)
150 | export QUIP_ARCH=archer2_mpich
151 | make config # configure: build - Make sure to say yes to GAP
152 | make libquip.a
153 | cd ../ # back to the starting dir
154 |
155 | # step 3. CASTEP with linking QUIP
156 | cd CASTEP/
157 | make -j8
158 | cp obj/linux_x86_64_gfortran10-XT--mpi/castep.mpi ../bin/
159 | cd ../ # back to the starting dir
160 |
161 | # step 4. Install gap_fit
162 |
163 | # IMPORTANT!! unload comms modules -> no MPI
164 | module load craype-network-none
165 | module remove cray-mpich
166 |
167 | cd QUIP
168 | export QUIP_ARCH=archer2_openmp
169 | make gap_programs
170 | cp build/archer2_openmp/gap_fit ../bin/
171 | cd ../ # back to the starting dir
172 |
173 |
--------------------------------------------------------------------------------
/doc_src/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # For the full list of built-in configuration values, see the documentation:
4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
5 |
6 | # -- Project information -----------------------------------------------------
7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8 |
9 | project = "GAP"
10 | copyright = "2019-2023, Albert Bartok-Partay, Noam Bernstein, Gabor Csanyi, James Kermode, and Tamas K Stenczel"
11 | author = "Albert Bartok-Partay, Noam Bernstein, Gabor Csanyi, James Kermode, and Tamas K Stenczel"
12 |
13 | # -- General configuration ---------------------------------------------------
14 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
15 |
16 | extensions = [
17 | "sphinx.ext.autodoc",
18 | "sphinx.ext.doctest",
19 | "sphinx.ext.mathjax",
20 | "sphinx.ext.autosummary",
21 | "sphinx.ext.intersphinx",
22 | "sphinx.ext.viewcode",
23 | "sphinx.ext.githubpages",
24 | "nbsphinx",
25 | "numpydoc",
26 | ]
27 |
28 | templates_path = ["_templates"]
29 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
30 |
31 | # -- Options for HTML output -------------------------------------------------
32 |
33 | html_theme = "sphinx_rtd_theme"
34 |
35 | # Theme options are theme-specific and customize the look and feel of a theme
36 | # further. For a list of options available for each theme, see the
37 | # documentation.
38 | html_theme_options = {}
39 |
40 | html_logo = "soap.png"
41 | html_favicon = "soap.png"
42 |
43 | # Add any paths that contain custom static files (such as style sheets) here,
44 | # relative to this directory. They are copied after the builtin static files,
45 | # so a file named "default.css" will overwrite the builtin "default.css".
46 | html_static_path = ["_static"]
47 |
--------------------------------------------------------------------------------
/doc_src/data.rst:
--------------------------------------------------------------------------------
1 | ************************
2 | GAP models and databases
3 | ************************
4 |
5 | We collect links to published GAP models and their fitting databases.
6 |
7 | .. list-table:: GAP models
8 | :widths: 25 50 50
9 | :header-rows: 1
10 |
11 | * - System
12 | - Reference
13 | - Comments
14 | * - `Li @ C `_
15 | - `S. Fujikake et al. J. Chem. Phys. 148, 241714 (2018) `_
16 | - The Li-C interaction potential is to be added onto a carbon potential that is not included
17 | * - `aC `_
18 | - `V. Deringer and G. Csanyi Phys. Rev. B 95, 094203 (2017) `_
19 | - Potential for amorphous carbon - not particularly accurate for crystals, and does not include any vdW interaction
20 | * - `Si `_
21 | - `A. P. Bartok et al. Physical Review X, 8(4), 1939 (2018) `_
22 | - A general purpose Si potential
23 | * - `Ge2Sb2Te5 `_
24 | - `F. Mocanu et al. J. Phys. Chem. B 122, 38, 8998-9006 (2018) `_
25 | - trained mostly for crystal and amorphous, with some liquid
26 | * - `HfO2 `_
27 | - `G. Sivaraman et al. Phys. Rev. Lett. 126, 156002 (2021) `_
28 | -
29 | * - `LiCl `_
30 | - `Sivaraman et al. J. Phys. Chem. Lett. 2021, 12, 4278−4285 `_
31 | -
32 | * - `LiCl-KCl `_
33 | - `Jicheng Guo et al. Phys. Rev. B 106, 014209 (2022) `_
34 | -
35 | * - `C `_
36 | - `Rowe et al. J. Chem. Phys. 153, 034702 (2020) `_
37 | - An updated version of the C potential, fitted to the optB88vdW functional
38 | * - `Si:H `_
39 | - `Unruh et al. Phys. Rev. Materials 6, 065603 (2022) `_
40 | - Includes a lot of the general Si potential plus interaction with H
--------------------------------------------------------------------------------
/doc_src/gap_fit.rst:
--------------------------------------------------------------------------------
1 | *******************
2 | The gap_fit program
3 | *******************
4 |
5 | .. _gap_fit:
6 |
7 | In order to fit an interatomic potential to data with ``gap_fit``, you need
8 |
9 | #. Data
10 | #. A definition of descriptors and kernels that serve as basis functions
11 | #. Some parameters that control the least squares fit
12 |
13 | We will go over each of these in turn, and reference the relevant ``gap_fit`` options.
14 |
15 |
16 | Data
17 | ****
18 |
19 | The input file to ``gap_fit`` is a series of atomic structures (they
20 | could be molecules, or periodic systems), with atomic numbers,
21 | cartesian positions of the atoms, and some associated data. The
22 | required file format is **extended XYZ**, which is similar to a
23 | concatenation of ordinary XYZ, except that the second line (after the
24 | first line that just contains the number of atoms) is not free format,
25 | but has to be a series of ``key=value`` pairs, with some mandatory keys
26 | and other restrictions. See the detailed definition [here].
27 |
28 | The data associated with each structure is an energy, optionally
29 | forces, and optionally virial stress. All of these are specified in
30 | the extended XYZ file. Following the definition of the extended XYZ
31 | file, the energy needs to be in units of eV, the forces in units of
32 | eV/A and the virial stress in units of eV (this is the regular stress
33 | multiplied by the volume). Watch out for the definition of the sign of
34 | the stress! For example VASP uses the opposite sign convention to QUIP.
35 | The atomic positions need to be in Angstroms, and the periodic unit cell is specified by three cartesian
36 | lattice vectors.
37 |
38 | An example structure in extended XYZ format::
39 |
40 | 1
41 | config_type=isolated_atom gap_energy=-157.72725320 dft_virial="0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000" dft_energy=-158.54496821 nneightol=1.20000000 pbc="T T T" Lattice="20.00000000 0.00000000 0.00000000 0.00000000 20.00000000 0.00000000 0.00000000 0.00000000 20.00000000" Properties=species:S:1:pos:R:3:Z:I:1:map_shift:I:3:n_neighb:I:1:gap_force:R:3:dft_force:R:3
42 | Si 10.00000000 10.00000000 10.00000000 14 -1 -1 -1 0 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000```
43 |
44 | The structure above has lots of data in it, not all of it is useful
45 | for fitting (e.g. ``gap_energy`` happens to be a prediction by some
46 | model, not relevant for making models, or ``nneightol`` which was
47 | inserted by some earlier manupulation of the file). Note how the
48 | value of the mandatory ``Properties`` key specifies the names and types
49 | of the columns in the rest of the configuration. ``S`` stands for
50 | 'string', ``R`` stands for 'real', ``I`` is for 'integer' and ``L`` is for
51 | logical. Columns ``species`` and ``pos`` (atom types and cartesian
52 | positions, respectively) are mandatory.
53 |
54 | **All structures in the extended XYZ format need a periodic lattice
55 | unit cell (and defined by the ``Lattice`` key), even if a particular
56 | direction (or any direction) is not supposed to be periodic. Just use
57 | a lattice vector which is bigger than twice the cutoff of any
58 | potential you plan to create or use.**
59 |
60 | To get good fits, it is extremely helpful of the energy and the forces
61 | are **consistent**, i.e. up to discretisation error (e.g. in k-space in
62 | case of a DFT code), the forces are the negative derivatives of the
63 | energy. E.g. if the data comes from density functional theory with a
64 | finite electronic temperature (as is almost always used to help
65 | convergence, even in the case of insulators, when there are defects),
66 | it is the *free energy* (i.e. including the electronic entropy) that
67 | is consistent with the Hellman-Feynman forces, rather than the
68 | potential energy. In ASE, this is obtained by specifying the
69 | ``force_consistent=true`` option to ``Atoms.get_potential_energy()``.
70 |
71 | Fits are tremendously improved by including forces. For 3N atoms,
72 | there is only one energy per structure, but 3N force components
73 | (obtained at almost the same cost as the single energy), so therefore
74 | contributing a lot more data. Virial stresses are again really
75 | important, especially for obtaining elastic constants for periodic
76 | solids. This is because the forces are always zero for an isotropic
77 | change away from equilibrium, and give no information on the curvature
78 | of the potential energy surface.
79 |
80 | The type of data included can vary from configuration to
81 | configuration, there is no restriction on having to include the same
82 | type of data for all configurations.
83 |
84 | Data field names
85 | ####################
86 |
87 | Conceptually, the data the potential is fitted to are the energy and
88 | its derivatives (forces and stresses), but the names with which these
89 | data are referred to in the XYZ file are arbitrary (but have to be
90 | consistent in the whole XYZ file). Unless otherwise indicated,
91 | ``gap_fit`` looks for data with keys ``energy``, ``force`` and ``virial``, and
92 | this can be controlled using the command line options
93 | ``energy_parameter name``, ``force_parameter_name``, and
94 | ``virial_parameter_name``, respectively. This facilitates specifying
95 | several types of energies and related quantities in the same XYZ, and
96 | selected the one you want to fit to on the command line. For example,
97 | energies computed with different DFT functionals can be stored in the
98 | same XYZ file, or even with a completely different method such as
99 | QMC.
100 |
101 | The above example has the keys ``dft_energy`` for example, so when it is
102 | used in fitting, the appropriate key names need to be specified.
103 |
104 | Isolated atom
105 | ###################
106 |
107 | .. _isolated atom:
108 |
109 | Most of the time, it makes sense to fit an interatomic potential to
110 | the **binding energy**, i.e. the total energy minus the energy of
111 | isolated atoms. There are several mechanisms in ``gap_fit`` to aid
112 | this. The default behaviour is to look for a configuration among the
113 | input structures in the input XYZ file that contains only one atom,
114 | and has an energy specified for it. This energy will then be taken
115 | and used to calculate the binding energies of all configurations
116 | before the fit is made. We give it the name ``e0``, and it is also
117 | stored in the XML file that specifies the potential. When predictions
118 | are made, the ``e0`` value is added back to the prediction of the
119 | binding energy. If the input XYZ contains multiple atom types, each
120 | one needs to appear once by itself among the configurations.
121 |
122 | Alternatively, if isolated atoms are not part of the input file, a
123 | value for ``e0`` can be specified on the command line, in case of
124 | multiple elements as a series of numbers:
125 | ``e0={H:123.456:O:789.10...}``. There is also an option to have an ``e0``
126 | value be taken as the average per-atom energy of the input data. This
127 | only makes sense if there is only one type of atom.
128 |
129 | Note that if you create a dataset where the specified energies are
130 | already binding energies, you should still specify 0.0 as the isolated atom
131 | energy (either via a configuration, preferred, or on the command
132 | line), otherwise the interatomic potential might (depending on the
133 | descriptor) not formally be zero for isolated atoms.
134 |
135 | SOAP hyperparameters
136 | ########################
137 |
138 | This section contains notes about choosing hyperparameters for the SOAP descriptor.
139 |
140 | cutoff
141 | ************
142 |
143 | Every finite range potential can be cast in the form of a sum over site
144 | energies or atomic energies, and the cut-off radius defines the range of
145 | this local term. The actual interaction range is of course twice the
146 | cut-off radius, because atoms up to this distance can potentially
147 | interact with one another via a many-body term centered on an atom in
148 | between them. When we approximate a quantum mechanical potential
149 | energy (which is not formally local) using a local site energy with
150 | cut-off radius, the error we necessarily incur can be
151 | characterised in theform of a force variance.
152 |
153 | Command line example
154 | ********************
155 |
156 | Here is an annotated fitting example.
157 |
158 | .. code-block:: bash
159 |
160 | gap_fit atoms_filename=database.xyz # input data in extended XYZ format
161 | gap={ # start of descriptor and kernel spec
162 | distance_Nb # first descriptor is interatomic distance based
163 | order=2 # descriptor is 2-body (i.e. a pair potential)
164 | cutoff=5.0 # distance cutoff in the kernel, in Angstrom
165 | n_sparse=15 # number of representative points, M in Sec. II
166 | covariance_type=ard_se # form of kernel: squared exponential (Gaussian)
167 | delta=2.0 # scaling of kernel, per descriptor, here it is per atom pair, in eV
168 | theta_uniform=2.5 # length scale in Gaussian kernel in Angstrom
169 | sparse_method=uniform # choice of representative points, here a uniform grid up to the cutoff
170 | compact_clusters=T # how cutoff is applied, here a spherical manner around each atom
171 | : # separator between descriptors
172 | soap # second descriptor is a SOAP
173 | l_max=6 n_max=12 # number of angular and radial basis functions for SOAP
174 | atom_sigma=0.5 # Gaussian smearing width of atom density for SOAP, in Angstrom
175 | cutoff=5.0 # distance cutoff in the kernel, in Angstrom
176 | radial_scaling=-0.5 # exponent of atom density scaling, power of distance
177 | cutoff_transition_width=1.0 # distance across which kernel is smoothly taken to zero, in Angstrom
178 | central_weight=1.0 # relative weight of central atom in atom density for SOAP
179 | n_sparse=8000 # number of representative points, M in Sec. II
180 | delta=0.2 # scaling of kernel, per descriptor, here for SOAP it is per atom, in eV
181 | covariance_type=dot_product # form of kernel
182 | zeta=4 # power kernel is raised to - together with dot_product gives a polynomial kernel
183 | sparse_method=cur_points # choice of representative points, here CUR decomposition of descriptor matrix
184 | } # end of descriptor and kernel spec
185 | default_sigma={0.002 0.2 0.2 0.0} # default regularisation corresponding to energy, force, virial, hessian
186 | config_type_sigma={ # start of per configuration-group regularisation spec, using groups defined in the input data file
187 | isolated_atom:0.0001:0.01:1.0:0.0:
188 | rss_rnd:0.03:0.4:0.5:0.0:
189 | rss_005:0.02:0.3:0.4:0.0:
190 | rss_200:0.01:0.2:0.2:0.0:
191 | rss_3c:0.005:0.1:0.1:0.00:
192 | cryst_dist:0.0003:0.03:0.05:0.00:
193 | cryst_dist_hp:0.005:0.1:0.1:0.0:
194 | liq_P4:0.003:0.3:0.5:0.0:
195 | liq_network:0.003:0.3:0.5:0.0:
196 | 2D:0.001:0.03:0.05:0.0:
197 | ribbons:0.01:0.5:0.2:0.0
198 | } # end of per configuration-group regularisation spec
199 | energy_parameter_name=energy # name of the key in the input data file corresponding to the total energy
200 | force_parameter_name=forces # name of the key in the input data file corresponding to the forces
201 | virial_parameter_name=virial # name of the key in the input data file corresponding to the virial stress
202 | sparse_jitter=1.0e-8 # extra diagonal regulariser
203 | do_copy_at_file=F # copy input data into potential XML file?
204 | sparse_separate_file=T # write representative point data into a separate file not in the main potential XML
205 | gp_file=gap.xml # name of output potential XML file
206 | core_param_file=P_r6_innercut.xml # name of XML file containing the baseline potential (QUIP format)
207 | core_ip_args={IP Glue} # initialisation string to call baseline potential
208 |
209 |
210 | Command line options
211 | ********************
212 |
213 | See `gap_fit --help` for description of options.
214 |
215 | GAP options
216 | ***********
217 |
218 | This does not exist any more: `quippy.gap_fit_parse_gap_str`.
219 |
220 | ``sparse_method`` options are:
221 | - RANDOM: default, chooses n_sparse random datapoints
222 | - PIVOT: based on the full covariance matrix finds the n_sparse "pivoting" points
223 | - CLUSTER: based on the full covariance matrix performs a k-medoid clustering into n_sparse clusters, returning the medoids
224 | - UNIFORM: makes a histogram of the data based on n_sparse and returns a data point from each bin
225 | - KMEANS: k-means clustering based on the data points
226 | - COVARIANCE: greedy data point selection based on the sparse covariance matrix, to minimise the GP variance of all datapoints
227 | - UNIQ: selects unique datapoints from the dataset
228 | - FUZZY: fuzzy k-means clustering
229 | - FILE: reads sparse points from a file
230 | - INDEX_FILE: reads indices of sparse points from a file
231 | - CUR_COVARIANCE: CUR, based on the full covariance matrix
232 | - CUR_POINTS: CUR, based on the datapoints
233 |
234 | Running with MPI
235 | ****************
236 |
237 | In order to run ``gap_fit`` with MPI, you need to configure and compile it
238 | accordingly, see the `QUIP Readme `_ for
239 | more details. Then run with ``mpirun -np …`` (or ``srun …`` for Slurm).
240 |
241 | Note that the sparsification is still done on a single process by collecting
242 | the relevant data from all processes and distributing the sparse points
243 | afterwards. This adds additional communication and requires more memory on
244 | the node of the first process. See below for an alternative way.
245 |
246 | Some sparse methods are not implemented for MPI runs: CLUSTER, COVARIANCE,
247 | CUR_COVARIANCE, INDEX_FILE. In these cases, sparse points may be chosen
248 | in a separate serial run, using the workflow below.
249 |
250 | #. Calculate without MPI to get the sparseX output files.
251 |
252 | * Use ``sparsify_only_no_fit=T`` to just create the sparseX files (fast, less memory needed).
253 |
254 | #. Convert output files to input files (``$QUIP_ROOT/bin/gap_prepare_sparsex_input.py gp.xml``).
255 |
256 | * Optional: Rename them to something shorter (e.g. ``1.input``, ``2.input`` etc.).
257 |
258 | #. Change input method (``sparse_method=FILE sparse_file=1.input``).
259 |
260 | * For more than one species use ``add_species=F`` and explicit input.
261 | * Check the number of braces (``gap={{$gap1}:{$gap2}:{$gap3}}``).
262 |
263 | #. Run with ``mpirun -np …`` (or ``srun …`` for Slurm).
264 |
--------------------------------------------------------------------------------
/doc_src/gap_si_surface/__init__.py:
--------------------------------------------------------------------------------
1 | from .visualise import ViewStructure, MyASEStructure
2 |
3 | __all__ = [ViewStructure, MyASEStructure]
4 |
--------------------------------------------------------------------------------
/doc_src/gap_si_surface/visualise.py:
--------------------------------------------------------------------------------
1 | import uuid
2 | import nglview
3 |
4 | import numpy as np
5 | import matplotlib.pyplot as plt
6 |
7 | from ipywidgets import Dropdown, FloatSlider, VBox, Output
8 |
9 |
10 | @nglview.register_backend('ase')
11 | class MyASEStructure(nglview.Structure):
12 | def __init__(self, atoms, bfactor=[], occupancy=[]):
13 | # super(MyASEStructure, self).__init__()
14 | self.ext = 'pdb'
15 | self.params = {}
16 | self._atoms = atoms
17 |
18 | self.bfactor = bfactor # [min, max]
19 | self.occupancy = occupancy # [0, 1]
20 |
21 | self.id = str(uuid.uuid4())
22 |
23 | def get_structure_string(self):
24 | """Example PDB file format:
25 | CRYST1 16.980 62.517 124.864 90.00 90.00 90.00 P 1
26 | MODEL 1
27 | ATOM 0 Fe MOL 1 15.431 60.277 6.801 1.00 0.00 FE
28 | ATOM 1 Fe MOL 1 1.273 3.392 93.940 1.00 0.00 FE
29 | """
30 |
31 | data = ""
32 |
33 | if self._atoms.get_pbc().any():
34 | cellpar = self._atoms.get_cell_lengths_and_angles()
35 |
36 | str_format = 'CRYST1' + '{:9.3f}' * 3 + '{:7.2f}' * 3 + ' P 1\n'
37 | data += str_format.format(*cellpar.tolist())
38 |
39 | data += 'MODEL 1\n'
40 |
41 | str_format = 'ATOM {:5d} {:>4s} MOL 1 {:8.3f}{:8.3f}{:8.3f}{:6.2f}{:6.2f} {:2s}\n'
42 | for index, atom in enumerate(self._atoms):
43 | data += str_format.format(
44 | index,
45 | atom.symbol,
46 | atom.position[0].tolist(),
47 | atom.position[1].tolist(),
48 | atom.position[2].tolist(),
49 | self.occupancy[index] if index <= len(self.occupancy) - 1 else 1.0,
50 | self.bfactor[index] if index <= len(self.bfactor) - 1 else 1.0,
51 | atom.symbol.upper()
52 | )
53 |
54 | data += 'ENDMDL\n'
55 |
56 | return data
57 |
58 |
59 | def ViewStructure(atoms, repetition=(1, 1, 1)):
60 | # visualisation
61 | view = nglview.NGLWidget(gui=False)
62 |
63 | view.stage.set_parameters(clip_dist=0)
64 | view.add_structure(MyASEStructure(atoms))
65 | view.add_unitcell()
66 |
67 | view.add_structure(MyASEStructure(atoms.repeat(repetition)))
68 |
69 | return view
70 |
71 |
72 | class AtomViewer(object):
73 | def __init__(self, atoms, data=[], xsize=1000, ysize=500):
74 | self.view = self._init_nglview(atoms, data, xsize, ysize)
75 |
76 | self.widgets = {
77 | 'radius': FloatSlider(
78 | value=0.8, min=0.0, max=1.5, step=0.01,
79 | description='Ball size'
80 | ),
81 | 'color_scheme': Dropdown(description='Solor scheme:'),
82 | 'colorbar': Output()
83 | }
84 | self.show_colorbar(data)
85 |
86 | self.widgets['radius'].observe(self._update_repr)
87 |
88 | self.gui = VBox([
89 | self.view,
90 | self.widgets['colorbar'],
91 | self.widgets['radius']])
92 |
93 | def _update_repr(self, chg=None):
94 | self.view.update_spacefill(
95 | radiusType='radius',
96 | radius=self.widgets['radius'].value
97 | )
98 |
99 | def show_colorbar(self, data):
100 | with self.widgets['colorbar']:
101 | # Have colormaps separated into categories:
102 | # http://matplotlib.org/examples/color/colormaps_reference.html
103 | cmap = 'rainbow'
104 |
105 | fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(16, 2))
106 | img = ax1.imshow([[min(data), max(data)]], aspect='auto', cmap=plt.get_cmap(cmap))
107 | ax1.remove()
108 | cbar = fig.colorbar(img, cax=ax2, orientation='horizontal')
109 |
110 | plt.show()
111 |
112 | @staticmethod
113 | def _init_nglview(atoms, data, xsize, ysize):
114 | view = nglview.NGLWidget(gui=False)
115 | view._remote_call(
116 | 'setSize',
117 | target='Widget',
118 | args=[
119 | '{:d}px'.format(xsize),
120 | '{:d}px'.format(ysize)
121 | ]
122 | )
123 |
124 | data = np.max(data) - data
125 |
126 | structure = MyASEStructure(atoms, bfactor=data)
127 | view.add_structure(structure)
128 |
129 | view.clear_representations()
130 | view.add_unitcell()
131 |
132 | view.add_spacefill(
133 | # radiusType='radius',
134 | # radius=1.0,
135 | color_scheme='bfactor',
136 | color_scale='rainbow'
137 | )
138 | view.update_spacefill(
139 | radiusType='radius',
140 | radius=1.0
141 | )
142 |
143 | # update camera type
144 | view.control.spin([1, 0, 0], np.pi / 2)
145 | view.control.spin([0, 0, 1], np.pi / 2)
146 | view.camera = 'orthographic'
147 | view.center()
148 |
149 | return view
150 |
--------------------------------------------------------------------------------
/doc_src/index.rst:
--------------------------------------------------------------------------------
1 | .. GAP documentation master file, created by
2 | sphinx-quickstart on Mon Oct 7 15:10:11 2019.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 |
7 | ==========================
8 | GAP and SOAP documentation
9 | ==========================
10 |
11 | .. module:: gap
12 |
13 | These are the documentation pages for Gaussian Approximation Potential
14 | (GAP) code. GAP is a plugin for QUIP, which is a set of molecular simulation
15 | tools. QUIP itself can be used as a plugin to LAMMPS or called from ASE.
16 |
17 | The purpose of the GAP code is to fit interatomic potentials and then
18 | use them for molecular simulation.
19 |
20 | **If you use the GAP code, please cite the original GAP paper:**
21 |
22 | `"The Accuracy of Quantum Mechanics, without the Electrons", Albert P. Bartók, Mike C. Payne, Risi Kondor, and Gábor Csányi. Phys. Rev. Lett. 104, 136403 (2010)
23 | `_
24 |
25 | **If you use the SOAP descriptor or kernel, please cite:**
26 |
27 | `"On representing chemical environments", Albert P. Bartók, Risi Kondor, and Gábor Csányi, Physical Review B 87, 184115 (2013)
28 | `_
29 |
30 | **For a general review of using SOAP and GAP for materials modelling, start here:**
31 |
32 | `"Gaussian Process Regression for Materials and Molecules", Volker L. Deringer, Albert P. Bartók, Noam Bernstein, David M. Wilkins, Michele Ceriotti, and Gábor Csányi, Chem. Rev. 2021, 121, 16, 10073–10141 (2021)
33 | `_
34 |
35 | Contents
36 | ========
37 |
38 | .. toctree::
39 | :maxdepth: 2
40 |
41 | installation.rst
42 | gap_fit.rst
43 | tutorials.rst
44 | reference.rst
45 | data.rst
46 | accelerated-aimd.rst
47 |
48 | Indices and tables
49 | ==================
50 |
51 | * :ref:`genindex`
52 | * :ref:`modindex`
53 | * :ref:`search`
54 |
--------------------------------------------------------------------------------
/doc_src/installation.rst:
--------------------------------------------------------------------------------
1 | .. _installation:
2 |
3 | Installation of QUIP, quippy and GAP
4 | ************************************
5 |
6 | ``GAP`` is now part of the ``QUIP`` package. For installation instructions
7 | please see the `QUIP Readme `_. Note that ``QUIP`` is released
8 | under a GPL license but ``GAP`` is released under `ASL `_ (Academic Software License).
9 |
--------------------------------------------------------------------------------
/doc_src/quippy-potential-tutorial.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Using QUIP potentials with Atomic Simulation Environment\n",
8 | "\n",
9 | "- quippy `Potential` objects can be used as ASE calculators\n",
10 | "- Communicate with other packages using ASE as *lingua franca*\n",
11 | "\n",
12 | "## Example: vacancy formation energy\n",
13 | "\n",
14 | "- Generate structure with `ASE` lattice tools\n",
15 | "- Stillinger-Weber potential implementation from `QUIP`\n",
16 | "- Elastic constant fitting routine from `matscipy`, internal relaxations with `ASE` FIRE minimiser"
17 | ]
18 | },
19 | {
20 | "cell_type": "code",
21 | "execution_count": 5,
22 | "metadata": {
23 | "scrolled": false
24 | },
25 | "outputs": [
26 | {
27 | "name": "stdout",
28 | "output_type": "stream",
29 | "text": [
30 | " Step Time Energy fmax\n",
31 | "*Force-consistent energies used in optimization.\n",
32 | "LBFGS: 0 16:55:25 -34.635777* 0.3247\n",
33 | "LBFGS: 1 16:55:25 -34.644590* 0.1504\n",
34 | "LBFGS: 2 16:55:25 -34.646997* 0.0001\n",
35 | " Step Time Energy fmax\n",
36 | "*Force-consistent energies used in optimization.\n",
37 | "LBFGS: 0 16:55:25 -34.670667* 0.1584\n",
38 | "LBFGS: 1 16:55:25 -34.672777* 0.0749\n",
39 | "LBFGS: 2 16:55:25 -34.673385* 0.0000\n",
40 | " Step Time Energy fmax\n",
41 | "*Force-consistent energies used in optimization.\n",
42 | "LBFGS: 0 16:55:25 -34.678737* 0.0000\n",
43 | " Step Time Energy fmax\n",
44 | "*Force-consistent energies used in optimization.\n",
45 | "LBFGS: 0 16:55:25 -34.660845* 0.1508\n",
46 | "LBFGS: 1 16:55:25 -34.662784* 0.0742\n",
47 | "LBFGS: 2 16:55:25 -34.663403* 0.0000\n",
48 | " Step Time Energy fmax\n",
49 | "*Force-consistent energies used in optimization.\n",
50 | "LBFGS: 0 16:55:25 -34.617822* 0.2945\n",
51 | "LBFGS: 1 16:55:25 -34.625262* 0.1477\n",
52 | "LBFGS: 2 16:55:25 -34.627763* 0.0001\n",
53 | "Fitting C_11\n",
54 | "Strain array([-0.02, -0.01, 0. , 0.01, 0.02])\n",
55 | "Stress array([-2.56044687, -1.01247671, 0.5027424 , 1.98366491, 3.42893711]) GPa\n",
56 | "Cij (gradient) / GPa : 149.74909575669915\n",
57 | "Error in Cij / GPa : 1.1696996170085603\n",
58 | "Correlation coefficient : 0.9999084935045536\n",
59 | "Setting C11 (1) to 0.934660 +/- 0.007301\n",
60 | "\n",
61 | "\n",
62 | "Fitting C_21\n",
63 | "Strain array([-0.02, -0.01, 0. , 0.01, 0.02])\n",
64 | "Stress array([-1.07663577, -0.26643655, 0.5027424 , 1.23345414, 1.92818198]) GPa\n",
65 | "Cij (gradient) / GPa : 75.0952617697293\n",
66 | "Error in Cij / GPa : 1.3149075235415504\n",
67 | "Correlation coefficient : 0.9995404242109733\n",
68 | "Setting C21 (7) to 0.468708 +/- 0.008207\n",
69 | "\n",
70 | "\n",
71 | "Fitting C_31\n",
72 | "Strain array([-0.02, -0.01, 0. , 0.01, 0.02])\n",
73 | "Stress array([-1.07663577, -0.26643655, 0.5027424 , 1.23345414, 1.92818198]) GPa\n",
74 | "Cij (gradient) / GPa : 75.09526176972929\n",
75 | "Error in Cij / GPa : 1.31490752354155\n",
76 | "Correlation coefficient : 0.9995404242109733\n",
77 | "Updating C31 (7) with value 0.468708 +/- 0.008207\n",
78 | "\n",
79 | "\n",
80 | "Fitting C_44\n",
81 | "Strain array([-0.02, -0.01, 0. , 0.01, 0.02])\n",
82 | "Stress array([-1.13572340e+00, -5.65842409e-01, -9.46072689e-15, 5.60142655e-01,\n",
83 | " 1.11304586e+00]) GPa\n",
84 | "Cij (gradient) / GPa : 56.23523568430934\n",
85 | "Error in Cij / GPa : 0.19437884854805132\n",
86 | "Correlation coefficient : 0.9999820790695022\n",
87 | "Setting C44 (4) to 0.350993 +/- 0.001213\n",
88 | "\n",
89 | "\n",
90 | "[[b C11 b C12 b C12 b b b ]\n",
91 | " [b C12 b C11 b C12 b b b ]\n",
92 | " [b C12 b C12 b C11 b b b ]\n",
93 | " [b b b b C44 b b ]\n",
94 | " [b b b b b C44 b ]\n",
95 | " [b b b b b b C44]]\n",
96 | "\n",
97 | " = \n",
98 | "\n",
99 | "[[149.75 75.1 75.1 0. 0. 0. ]\n",
100 | " [ 75.1 149.75 75.1 0. 0. 0. ]\n",
101 | " [ 75.1 75.1 149.75 0. 0. 0. ]\n",
102 | " [ 0. 0. 0. 56.24 0. 0. ]\n",
103 | " [ 0. 0. 0. 0. 56.24 0. ]\n",
104 | " [ 0. 0. 0. 0. 0. 56.24]]\n",
105 | "C_11 = 149.75 +/- 1.17 GPa\n",
106 | "C_12 = 75.10 +/- 1.31 GPa\n",
107 | "C_44 = 56.24 +/- 0.19 GPa\n",
108 | " Step Time Energy fmax\n",
109 | "*Force-consistent energies used in optimization.\n",
110 | "LBFGS: 0 16:55:25 -927.087471* 0.8332\n",
111 | "LBFGS: 1 16:55:25 -927.431001* 0.4667\n",
112 | "LBFGS: 2 16:55:25 -927.618490* 0.1404\n",
113 | "LBFGS: 3 16:55:25 -927.627345* 0.1185\n",
114 | "LBFGS: 4 16:55:25 -927.645023* 0.0525\n",
115 | "LBFGS: 5 16:55:25 -927.647485* 0.0567\n",
116 | "LBFGS: 6 16:55:25 -927.652217* 0.0421\n",
117 | "LBFGS: 7 16:55:25 -927.653638* 0.0372\n",
118 | "LBFGS: 8 16:55:25 -927.654968* 0.0298\n",
119 | "LBFGS: 9 16:55:25 -927.655716* 0.0221\n",
120 | "LBFGS: 10 16:55:25 -927.656230* 0.0185\n",
121 | "LBFGS: 11 16:55:25 -927.656496* 0.0136\n",
122 | "LBFGS: 12 16:55:25 -927.656686* 0.0109\n",
123 | "LBFGS: 13 16:55:25 -927.656814* 0.0104\n",
124 | "LBFGS: 14 16:55:25 -927.656908* 0.0091\n",
125 | "LBFGS: 15 16:55:25 -927.656982* 0.0072\n",
126 | "LBFGS: 16 16:55:25 -927.657045* 0.0061\n",
127 | "LBFGS: 17 16:55:25 -927.657090* 0.0049\n",
128 | "LBFGS: 18 16:55:25 -927.657121* 0.0045\n",
129 | "LBFGS: 19 16:55:25 -927.657144* 0.0046\n",
130 | "LBFGS: 20 16:55:25 -927.657163* 0.0033\n",
131 | "LBFGS: 21 16:55:25 -927.657178* 0.0029\n",
132 | "LBFGS: 22 16:55:25 -927.657189* 0.0027\n",
133 | "LBFGS: 23 16:55:25 -927.657199* 0.0024\n",
134 | "LBFGS: 24 16:55:25 -927.657207* 0.0025\n",
135 | "LBFGS: 25 16:55:25 -927.657212* 0.0021\n",
136 | "LBFGS: 26 16:55:25 -927.657216* 0.0016\n",
137 | "LBFGS: 27 16:55:25 -927.657218* 0.0012\n",
138 | "LBFGS: 28 16:55:25 -927.657220* 0.0013\n",
139 | "LBFGS: 29 16:55:25 -927.657221* 0.0010\n",
140 | "LBFGS: 30 16:55:25 -927.657222* 0.0012\n",
141 | "LBFGS: 31 16:55:25 -927.657223* 0.0010\n",
142 | "LBFGS: 32 16:55:25 -927.657224* 0.0008\n",
143 | "LBFGS: 33 16:55:25 -927.657225* 0.0006\n",
144 | "LBFGS: 34 16:55:25 -927.657225* 0.0006\n",
145 | "LBFGS: 35 16:55:25 -927.657225* 0.0005\n",
146 | "LBFGS: 36 16:55:25 -927.657226* 0.0004\n",
147 | "LBFGS: 37 16:55:25 -927.657226* 0.0004\n",
148 | "LBFGS: 38 16:55:25 -927.657226* 0.0003\n",
149 | "LBFGS: 39 16:55:25 -927.657226* 0.0002\n",
150 | "LBFGS: 40 16:55:25 -927.657226* 0.0002\n",
151 | "LBFGS: 41 16:55:25 -927.657226* 0.0002\n",
152 | "LBFGS: 42 16:55:25 -927.657226* 0.0001\n",
153 | "LBFGS: 43 16:55:25 -927.657226* 0.0001\n",
154 | "LBFGS: 44 16:55:25 -927.657226* 0.0001\n",
155 | "LBFGS: 45 16:55:25 -927.657226* 0.0001\n",
156 | "LBFGS: 46 16:55:25 -927.657226* 0.0001\n",
157 | "LBFGS: 47 16:55:25 -927.657226* 0.0001\n",
158 | "LBFGS: 48 16:55:25 -927.657226* 0.0001\n",
159 | "LBFGS: 49 16:55:25 -927.657226* 0.0001\n",
160 | "LBFGS: 50 16:55:25 -927.657226* 0.0000\n",
161 | "LBFGS: 51 16:55:25 -927.657226* 0.0000\n",
162 | "LBFGS: 52 16:55:25 -927.657226* 0.0000\n",
163 | "LBFGS: 53 16:55:25 -927.657226* 0.0000\n",
164 | "LBFGS: 54 16:55:25 -927.657226* 0.0000\n",
165 | "LBFGS: 55 16:55:25 -927.657226* 0.0000\n",
166 | "LBFGS: 56 16:55:25 -927.657226* 0.0000\n",
167 | "LBFGS: 57 16:55:26 -927.657226* 0.0000\n",
168 | "LBFGS: 58 16:55:26 -927.657226* 0.0000\n",
169 | "LBFGS: 59 16:55:26 -927.657226* 0.0000\n",
170 | "LBFGS: 60 16:55:26 -927.657226* 0.0000\n",
171 | "LBFGS: 61 16:55:26 -927.657226* 0.0000\n",
172 | "LBFGS: 62 16:55:26 -927.657226* 0.0000\n",
173 | "LBFGS: 63 16:55:26 -927.657226* 0.0000\n",
174 | "LBFGS: 64 16:55:26 -927.657226* 0.0000\n",
175 | "LBFGS: 65 16:55:26 -927.657226* 0.0000\n",
176 | "LBFGS: 66 16:55:26 -927.657226* 0.0000\n",
177 | "LBFGS: 67 16:55:26 -927.657226* 0.0000\n",
178 | "LBFGS: 68 16:55:26 -927.657226* 0.0000\n",
179 | "LBFGS: 69 16:55:26 -927.657226* 0.0000\n",
180 | "LBFGS: 70 16:55:26 -927.657226* 0.0000\n",
181 | "LBFGS: 71 16:55:26 -927.657226* 0.0000\n",
182 | "LBFGS: 72 16:55:26 -927.657226* 0.0000\n",
183 | "LBFGS: 73 16:55:26 -927.657226* 0.0000\n",
184 | "LBFGS: 74 16:55:26 -927.657226* 0.0000\n",
185 | "LBFGS: 75 16:55:26 -927.657226* 0.0000\n",
186 | "LBFGS: 76 16:55:26 -927.657226* 0.0000\n",
187 | "LBFGS: 77 16:55:26 -927.657226* 0.0000\n",
188 | "SW vacancy formation energy 4.333840201785051 eV\n"
189 | ]
190 | }
191 | ],
192 | "source": [
193 | "from ase.build import bulk\n",
194 | "from ase.optimize import LBFGS\n",
195 | "from quippy.potential import Potential\n",
196 | "\n",
197 | "si = bulk('Si', a=5.44, cubic=True)\n",
198 | "sw_pot = Potential('IP SW', param_str=\"\"\"\n",
199 | "\n",
200 | " \n",
201 | " \n",
203 | " \n",
205 | " \n",
206 | "\"\"\") # call into Fortran code\n",
207 | "si.set_calculator(sw_pot)\n",
208 | "e_bulk_per_atom = si.get_potential_energy()/len(si)\n",
209 | "\n",
210 | "# call general purpose elastic constants calculator \n",
211 | "# using ASE Atoms and QUIP Potential\n",
212 | "from matscipy.elasticity import fit_elastic_constants\n",
213 | "Cij = fit_elastic_constants(si, optimizer=LBFGS,\n",
214 | " symmetry='cubic', logfile='-')\n",
215 | "vac1 = si.copy()\n",
216 | "vac1 *= (3, 3, 3)\n",
217 | "half_cell = np.diag(vac1.cell)/2.\n",
218 | "vac_atom = ((vac1.positions - half_cell)**2).sum(axis=1).argmin()\n",
219 | "del vac1[vac_atom]\n",
220 | "\n",
221 | "vac1.set_calculator(sw_pot)\n",
222 | "vac1.rattle(0.01)\n",
223 | "\n",
224 | "opt = LBFGS(vac1)\n",
225 | "opt.run(fmax=1e-6)\n",
226 | "e_vac = vac1.get_potential_energy() - e_bulk_per_atom*len(vac1)\n",
227 | "print('SW vacancy formation energy', e_vac, 'eV')"
228 | ]
229 | },
230 | {
231 | "cell_type": "code",
232 | "execution_count": null,
233 | "metadata": {
234 | "collapsed": true
235 | },
236 | "outputs": [],
237 | "source": []
238 | }
239 | ],
240 | "metadata": {
241 | "kernelspec": {
242 | "display_name": "Python 3",
243 | "language": "python",
244 | "name": "python3"
245 | },
246 | "language_info": {
247 | "codemirror_mode": {
248 | "name": "ipython",
249 | "version": 3
250 | },
251 | "file_extension": ".py",
252 | "mimetype": "text/x-python",
253 | "name": "python",
254 | "nbconvert_exporter": "python",
255 | "pygments_lexer": "ipython3",
256 | "version": "3.6.6"
257 | }
258 | },
259 | "nbformat": 4,
260 | "nbformat_minor": 1
261 | }
262 |
--------------------------------------------------------------------------------
/doc_src/reference.rst:
--------------------------------------------------------------------------------
1 | **********************
2 | SOAP and GAP reference
3 | **********************
4 |
5 | This page collects useful information in working with the GAP code.
6 |
7 | SOAP vectors
8 | ************
9 |
10 | The description of the local environment by SOAP vectors is as follows. The neighbour density around each atom is expanded in a set of radial and angular basis functions, with a different expansion for each atomic species in the neighbour environment. The SOAP vector is then formed from these expansion coefficients. The array has two radial and one angular indices. The number of radial basis functions is `n_max`, the number of angular basis functions `l_max+1`. The order of the elements in the vector is built as follows.
11 |
12 | .. code-block::
13 |
14 | for i=1:n_species*n_max
15 | for j=i:n_species*n_max
16 | for l=0:l_max
17 |
--------------------------------------------------------------------------------
/doc_src/requirements.txt:
--------------------------------------------------------------------------------
1 | Sphinx==5.1.1
2 | sphinx-rtd-theme==1.0.0
3 | nbsphinx==0.8.9
4 | pandoc==2.2
5 | ipython==8.4.0
6 | numpydoc==1.4.0
--------------------------------------------------------------------------------
/doc_src/soap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/libAtoms/GAP/51e038baa66bcc6a1902ea94c884f4383c8b2931/doc_src/soap.png
--------------------------------------------------------------------------------
/doc_src/tutorials.rst:
--------------------------------------------------------------------------------
1 | Tutorials
2 | =========
3 |
4 | The following tutorials are currently available:
5 |
6 | .. toctree::
7 | :maxdepth: 1
8 |
9 | quippy-potential-tutorial.ipynb
10 | quippy-descriptor-tutorial.ipynb
11 | gap_fitting_tutorial.ipynb
12 | gap_si_surface.ipynb
13 |
--------------------------------------------------------------------------------
/error.inc:
--------------------------------------------------------------------------------
1 | ! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2 | ! HND X
3 | ! HND X GAP (Gaussian Approximation Potental)
4 | ! HND X
5 | ! HND X
6 | ! HND X Portions of GAP were written by Albert Bartok-Partay, Gabor Csanyi,
7 | ! HND X Copyright 2006-2021.
8 | ! HND X
9 | ! HND X Portions of GAP were written by Noam Bernstein as part of
10 | ! HND X his employment for the U.S. Government, and are not subject
11 | ! HND X to copyright in the USA.
12 | ! HND X
13 | ! HND X GAP is published and distributed under the
14 | ! HND X Academic Software License v1.0 (ASL)
15 | ! HND X
16 | ! HND X GAP is distributed in the hope that it will be useful for non-commercial
17 | ! HND X academic research, but WITHOUT ANY WARRANTY; without even the implied
18 | ! HND X warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | ! HND X ASL for more details.
20 | ! HND X
21 | ! HND X You should have received a copy of the ASL along with this program
22 | ! HND X (e.g. in a LICENSE.md file); if not, you can write to the original licensors,
23 | ! HND X Gabor Csanyi or Albert Bartok-Partay. The ASL is also published at
24 | ! HND X http://github.com/gabor1/ASL
25 | ! HND X
26 | ! HND X When using this software, please cite the following reference:
27 | ! HND X
28 | ! HND X A. P. Bartok et al Physical Review Letters vol 104 p136403 (2010)
29 | ! HND X
30 | ! HND X When using the SOAP kernel or its variants, please additionally cite:
31 | ! HND X
32 | ! HND X A. P. Bartok et al Physical Review B vol 87 p184115 (2013)
33 | ! HND X
34 | ! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
35 |
36 |
37 |
38 | !XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
39 | !X
40 | !X Error handling, see error.f95 for the functions called in these macros.
41 | !X
42 | !X Error passing works as follows:
43 | !X - *error* needs to be intent(out) and optional
44 | !X - all functions that receive *error* as an argument must call INIT_ERROR(error)
45 | !X - RAISE_ERROR is used whenever an error occurs. If *error* is not present,
46 | !X the program execution will be terminated immediately. If *error* is
47 | !X present it will be set to some value not equal ERROR_NONE and the execution
48 | !X of the subroutine will be stopped.
49 | !X - PASS_ERROR is used after a function or subroutine that returns error, i.e.
50 | !X call sub(..., error=error)
51 | !X PASS_ERROR(error)
52 | !X If no error occurs (i.e. error==ERROR_NONE), execution will proceed as
53 | !X usual. If an error occured, the current function will be terminated after
54 | !X the location of the error is passed to the error module.
55 | !X If the calling routine handles the error itself, rather than passing
56 | !X it up with PASS_ERROR(), CLEAR_ERROR() should be used to clear the error
57 | !X info stack
58 | !X - PASS_ERROR_WITH_INFO is like PASS_ERROR, just an additional string can be
59 | !X provided describing the error, or parameters.
60 | !X - HANDLE_ERROR will print the error history and stop execution of the program
61 | !X after an error occured.
62 | !X
63 | !XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
64 |
65 | #define INIT_ERROR(error) if (present(error)) then ; error = ERROR_NONE ; endif
66 | #define ASSERT(condition, message, error) if (.not. (condition)) then ; RAISE_ERROR(message, error) ; endif
67 |
68 | #define RAISE_ERROR(message, error) if (.true.) then ; call push_error_with_info(message, __FILE__, __LINE__) ; if (present(error)) then ; error = ERROR_UNSPECIFIED ; return ; else ; call error_abort(error) ; endif ; endif
69 |
70 | #define RAISE_ERROR_WITH_KIND(kind, message, error) if (.true.) then ; call push_error_with_info(message, __FILE__, __LINE__, kind) ; if (present(error)) then ; error = kind ; return ; else ; call error_abort(error) ; endif ; endif
71 |
72 | #define PASS_ERROR(error) if (present(error)) then ; if (error /= ERROR_NONE) then ; call push_error(__FILE__, __LINE__) ; return ; endif ; endif
73 |
74 | #define PASS_ERROR_WITH_INFO(message, error) if (present(error)) then ; if (error /= ERROR_NONE) then ; call push_error_with_info(message, __FILE__, __LINE__) ; return ; endif ; endif
75 |
76 | #define HANDLE_ERROR(error) if (error /= ERROR_NONE) then ; call push_error(__FILE__, __LINE__) ; call error_abort(error) ; endif
77 |
78 | #define CLEAR_ERROR(error) call error_clear_stack()
79 |
80 | #define PRINT_LINE_NUMBER if(.true.) then; print "('LINE ' i0)",__LINE__; endif
81 |
82 |
83 | !XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
84 | !X
85 | !X MPI errors
86 | !X
87 | !X MPI error string are obtained using mpi_error_string and then pushed
88 | !X onto the error stack.
89 | !X
90 | !XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
91 |
92 | #define PASS_MPI_ERROR(mperror, error) if (mperror /= MPI_SUCCESS) then ; call push_MPI_error(mperror, __FILE__, __LINE__) ; if (present(error)) then ; error = ERROR_MPI ; return ; else ; call error_abort(error) ; endif ; endif
93 |
94 | !XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
95 | !X
96 | !X MPI BCAST errors
97 | !X
98 | !X Extension of error handling macros to MPI cases where processes
99 | !X perform different tasks. If an error occurs on one process it will
100 | !X be broadcast to all others before the error is propagated
101 | !X upwards. Replace RAISE_ERROR with BCAST_RAISE_ERROR and PASS_ERROR
102 | !X with BCAST_PASS_ERROR. Additionally, BCAST_CHECK_ERROR must be
103 | !X called on the processes in which no error has occured. See
104 | !X CInOutput read() for an example usage of these macros.
105 | !X
106 | !XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
107 |
108 | #define BCAST_ASSERT(condition, message, error, mpi) if (.not. (condition)) then ; BCAST_RAISE_ERROR(message, error, mpi) ; endif
109 |
110 | #define BCAST_RAISE_ERROR(message, error, mpi) if (present(error)) call bcast(mpi, error); RAISE_ERROR(message, error)
111 |
112 | #define BCAST_RAISE_ERROR_WITH_KIND(kind, message, error, mpi) if (present(error)) then; error = kind; call bcast(mpi, error); end if; RAISE_ERROR_WITH_KIND(kind, message, error)
113 |
114 | #define BCAST_PASS_ERROR(error, mpi) if (present(error)) then; if (error /= ERROR_NONE) call bcast(mpi, error); endif; PASS_ERROR(error)
115 |
116 | #define BCAST_CHECK_ERROR(error, mpi) if (present(error)) then; call bcast(mpi, error); if (error /= ERROR_NONE) then; RAISE_ERROR_WITH_KIND(error, "An error occured on another MPI process", error); endif; endif
117 |
118 |
119 | !XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
120 | !X
121 | !X Delayed errors - for OpenMP loops
122 | !X
123 | !X A subroutine currently in an OpenMP section cannot be quit using
124 | !X the *return* statement. Hence, the error flag is set using
125 | !X RAISE_DELAYED_ERROR and TRACE_DELAYED_ERROR. After the OpenMP section
126 | !X has finished, INVOKE_DELAYED_ERROR will raise the error and exit
127 | !X the current subroutine if an error occured in the OpenMP section.
128 | !X
129 | !XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
130 |
131 | #define RAISE_DELAYED_ERROR(message, error_loc) if (error_loc == ERROR_NONE) then ; call push_error_with_info(message, __FILE__, __LINE__) ; error_loc = ERROR_UNSPECIFIED ; endif
132 |
133 | #define TRACE_DELAYED_ERROR(error_loc) if (error_loc /= ERROR_NONE) then ; call push_error(__FILE__, __LINE__) ; endif
134 |
135 | #define TRACE_DELAYED_ERROR_WITH_INFO(message, error_loc) if (error_loc /= ERROR_NONE) then ; call push_error_with_info(message, __FILE__, __LINE__) ; endif
136 |
137 | #define INVOKE_DELAYED_ERROR(error_loc, error) if (error_loc /= ERROR_NONE) then ; call push_error(__FILE__, __LINE__) ; if (present(error)) then ; error = error_loc ; else ; call error_abort(error) ; endif ; endif
138 |
139 |
--------------------------------------------------------------------------------
/gap_fit.F90:
--------------------------------------------------------------------------------
1 | ! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2 | ! HND X
3 | ! HND X GAP (Gaussian Approximation Potental)
4 | ! HND X
5 | ! HND X
6 | ! HND X Portions of GAP were written by Albert Bartok-Partay, Gabor Csanyi,
7 | ! HND X and Sascha Klawohn. Copyright 2006-2021.
8 | ! HND X
9 | ! HND X Portions of GAP were written by Noam Bernstein as part of
10 | ! HND X his employment for the U.S. Government, and are not subject
11 | ! HND X to copyright in the USA.
12 | ! HND X
13 | ! HND X GAP is published and distributed under the
14 | ! HND X Academic Software License v1.0 (ASL)
15 | ! HND X
16 | ! HND X GAP is distributed in the hope that it will be useful for non-commercial
17 | ! HND X academic research, but WITHOUT ANY WARRANTY; without even the implied
18 | ! HND X warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | ! HND X ASL for more details.
20 | ! HND X
21 | ! HND X You should have received a copy of the ASL along with this program
22 | ! HND X (e.g. in a LICENSE.md file); if not, you can write to the original licensors,
23 | ! HND X Gabor Csanyi or Albert Bartok-Partay. The ASL is also published at
24 | ! HND X http://github.com/gabor1/ASL
25 | ! HND X
26 | ! HND X When using this software, please cite the following reference:
27 | ! HND X
28 | ! HND X A. P. Bartok et al Physical Review Letters vol 104 p136403 (2010)
29 | ! HND X
30 | ! HND X When using the SOAP kernel or its variants, please additionally cite:
31 | ! HND X
32 | ! HND X A. P. Bartok et al Physical Review B vol 87 p184115 (2013)
33 | ! HND X
34 | ! HND XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
35 |
36 | program gap_fit_program
37 |
38 | use libatoms_module
39 | use gp_predict_module
40 | use gap_fit_module
41 | use task_manager_module
42 |
43 | implicit none
44 |
45 | type(gap_fit) :: main_gap_fit
46 |
47 | call system_initialise(verbosity=PRINT_NORMAL, enable_timing=.false.)
48 | call gap_fit_init_mpi_scalapack(main_gap_fit)
49 |
50 | call gap_fit_parse_command_line(main_gap_fit)
51 | call gap_fit_parse_gap_str(main_gap_fit)
52 |
53 | call gap_fit_read_core_param_file(main_gap_fit)
54 |
55 | call add_template_string(main_gap_fit) ! if descriptor requires a template xyz file and this is provided, write to a string and add to descriptor_str
56 |
57 | call read_descriptors(main_gap_fit) ! initialises descriptors from the descriptor_str and sets max_cutoff according to that.
58 | call read_fit_xyz(main_gap_fit) ! reads in xyz into an array of atoms objects. sets cutoff and does calc_connect on each frame
59 | call print('XYZ file read')
60 |
61 | call gap_fit_init_task_manager(main_gap_fit)
62 |
63 | call get_species_xyz(main_gap_fit) ! counts the number of species present in the xyz file.
64 | call add_multispecies_gaps(main_gap_fit)
65 |
66 | call get_n_sparseX_for_files(main_gap_fit)
67 | call parse_config_type_sigma(main_gap_fit)
68 | call parse_config_type_n_sparseX(main_gap_fit)
69 |
70 | if(any(main_gap_fit%add_species)) then ! descriptor_str might have changed. reinitialises descriptors from the descriptor_str and sets max_cutoff according to that.
71 | call read_descriptors(main_gap_fit)
72 | endif
73 | call print('Multispecies support added where requested')
74 |
75 | call fit_n_from_xyz(main_gap_fit) ! counts number of energies, forces, virials. computes number of descriptors and gradients.
76 | call gap_fit_distribute_tasks(main_gap_fit)
77 | if (main_gap_fit%task_manager%n_workers > 1) call fit_n_from_xyz(main_gap_fit)
78 | call gap_fit_set_mpi_blocksizes(main_gap_fit)
79 | call gap_fit_estimate_memory(main_gap_fit)
80 |
81 | if (main_gap_fit%dryrun) then
82 | call print('Exit before major allocations because dryrun is true.')
83 | call system_finalise()
84 | stop
85 | end if
86 |
87 | call set_baselines(main_gap_fit) ! sets e0 etc.
88 |
89 | call fit_data_from_xyz(main_gap_fit) ! converts atomic neighbourhoods (bond neighbourhoods etc.) do descriptors, and feeds those to the GP
90 | call print('Cartesian coordinates transformed to descriptors')
91 |
92 | if(main_gap_fit%sparsify_only_no_fit) then
93 | if (gap_fit_is_root(main_gap_fit)) then
94 | call initialise(main_gap_fit%gp_sp, main_gap_fit%my_gp)
95 | call gap_fit_print_xml(main_gap_fit, main_gap_fit%gp_file, main_gap_fit%sparseX_separate_file)
96 | end if
97 | call system_finalise()
98 | stop
99 | end if
100 |
101 | call enable_timing()
102 | call system_timer('GP sparsify')
103 |
104 | call gp_covariance_sparse(main_gap_fit%my_gp)
105 | call gap_fit_print_linear_system_dump_file(main_gap_fit)
106 | call gpSparse_fit(main_gap_fit%gp_sp, main_gap_fit%my_gp, main_gap_fit%task_manager, main_gap_fit%condition_number_norm)
107 |
108 | if (gap_fit_is_root(main_gap_fit)) call gap_fit_print_xml(main_gap_fit, main_gap_fit%gp_file, main_gap_fit%sparseX_separate_file)
109 |
110 | call system_timer('GP sparsify')
111 | call system_finalise()
112 |
113 | end program gap_fit_program
114 |
--------------------------------------------------------------------------------
/gapversion:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Determine the gap version from the git reported last changed date
4 | # of file (or the file GAP_VERSION if it exists). The gapversion
5 | # is the UNIX timestap of the most recently changed file.
6 |
7 | GAP_FILES="descriptors.f95 descriptors_wrapper.f95 \
8 | gp_predict.f95 \
9 | clustering.f95 gp_fit.f95 \
10 | gap_fit_module.f95 gap_fit.f95"
11 |
12 | GAP_ROOT=$(dirname "$0")
13 |
14 | # By default only show the date
15 | VERBOSE=false
16 |
17 | # Options for interactive use
18 | while getopts 'v' opt; do
19 | case $opt in
20 | v)
21 | VERBOSE=true
22 | ;;
23 | \?)
24 | echo "Usage: $0"
25 | echo "'-v' to output name of newest file to stderr"
26 | exit 1
27 | ;;
28 | esac
29 | done
30 |
31 | function git_date
32 | {
33 | if [ -e "$1" ]
34 | then
35 | DATE=$(git log -n 1 --format="%at" -- `/bin/ls | grep -v doc_src`)
36 | [[ $? -eq 0 && ! -z $DATE ]] && echo "$DATE" || echo 0
37 | else
38 | echo 0
39 | fi
40 | }
41 |
42 | if [ -s "${GAP_ROOT}/GAP_VERSION" ]; then
43 | echo -ne "$(cat "${GAP_ROOT}/GAP_VERSION")"
44 | exit 0
45 | elif [ -d "${GAP_ROOT}/.git" ] || [ -s "${GAP_ROOT}/.git" ]; then
46 | # Sort everything as "DATE filename" list and take the last one
47 | GAP_VERSION=$(
48 | for I in $GAP_FILES
49 | do
50 | if [ -d "${GAP_ROOT}/$(dirname "$I")" ]
51 | then
52 | cd "${GAP_ROOT}/$(dirname "$I")"
53 | echo $(git_date $(basename "$I")) "$I"
54 | cd - >/dev/null
55 | fi
56 | done | sort -n | tail -1 )
57 | # filename to stderr if requested;
58 | # split string with parameter expansion
59 | if [ $VERBOSE == "true" ]; then
60 | echo "${GAP_VERSION##* }" >&2
61 | fi
62 | echo "${GAP_VERSION%% *}"
63 | else
64 | echo "0"
65 | exit 0
66 | fi
67 |
--------------------------------------------------------------------------------
/hybrid_md_package/README.md:
--------------------------------------------------------------------------------
1 | # Hybrid MD
2 |
3 | This is a python pakage for running "Hybrid MD": accelerate MD in Quantum Mechanics (QM) codes with
4 | Force Fields (FF) and allow on the fly refinement as well.
5 |
6 | So far this has been used with GAP, particularly in the CASTEP code.
7 |
8 | The QM codes need the FF interfaced for calculation of energies and forces, which
9 | is independent of this package, and need to allow switching between the QM and FF forces for
10 | the MD steps. Preferably, this switching should not be costly an no expensive QM calculations
11 | should be done in cases when the FF is used.
12 |
13 | At specified points in the MD loop, the QM code needs to make decisions, for which this package
14 | was written. Given a simple interfce, this package allows rapid development of methods and
15 | tayloring those for specific questions or problems, all without touching the source code of the
16 | QM package used.
17 |
18 | The calls to this package can be done through a system call to the installed executable of
19 | `hybrid-md` which has subcommand for :
20 | - `initialise`: before the start of MD loop, decides to start with QM or FF
21 | - `pre-step`: at the start of the MD step, decides if QM calculation is required and if any
22 | comparison will be done later.
23 | - `post-step`: after QM calculation, but before first Velocity-Verlet step. This performs the
24 | comparison, decides if there is a need for refitting and tells the QM code if the FF
25 | parameters need updating.
26 |
27 | The subcommands need the file name prefix used in for the calculation's inputs and outputs,
28 | called seed here, and the MD iteration number (apart from at initialisation). For full details see
29 | the `--help` pages of the executable after installation or read the code.
30 |
31 | The QM code should write it's results into an XYZ file for the executable to read if needed,
32 | and the settings are stored in a yaml file `.hybrid-md-input.yaml`.
33 |
34 | For further details, see the code and feel free to tweak it to your needs.
35 |
36 | ## Installation
37 |
38 | Download the source code and run
39 |
40 | ```bash
41 | pip install .
42 | ```
--------------------------------------------------------------------------------
/hybrid_md_package/examples/H2_in_C60/build.py:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021.
4 | from ase.build import molecule
5 | import ase.io
6 | import numpy as np
7 |
8 | cell_a = 15.0
9 |
10 | # these should be centred at (0, 0, 0)
11 | c60 = molecule("C60")
12 | h2 = molecule("H2")
13 |
14 | # move CoM to origin
15 | c60.positions -= np.mean(c60.positions, axis=0)
16 | h2.positions -= np.mean(h2.positions, axis=0)
17 |
18 | # create a cell
19 | atoms = h2 + c60
20 | atoms.positions += cell_a / 2
21 | atoms.cell = [cell_a, cell_a, cell_a]
22 | atoms.set_pbc(True) # dummy really
23 |
24 | # write the cell file
25 | ase.io.write("h2c60.cell", atoms)
26 |
27 | # extra stuff in .cell
28 | extra_in_cell = """
29 | # calculation settings for gamma point only
30 | kpoints_MP_grid 1 1 1
31 | kpoints_MP_offset 0.0 0.0 0.0
32 |
33 | # geometry optimisation specific
34 | FIX_ALL_CELL: True
35 | FIX_COM: True
36 | """
37 |
38 | with open("h2c60.cell", "a") as file:
39 | file.write(extra_in_cell)
40 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/H2_in_C60/h2c60.cell:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021.
4 |
5 | %BLOCK LATTICE_CART
6 | 15.000000 0.000000 0.000000
7 | 0.000000 15.000000 0.000000
8 | 0.000000 0.000000 15.000000
9 | %ENDBLOCK LATTICE_CART
10 |
11 | %BLOCK POSITIONS_ABS
12 | H 7.500000 7.500000 7.868583
13 | H 7.500000 7.500000 7.131417
14 | C 9.710195 8.086663 10.166950
15 | C 10.607639 7.657701 9.130029
16 | C 8.828443 7.184106 10.736323
17 | C 10.590871 6.341500 8.701424
18 | C 10.687924 6.042540 7.300299
19 | C 10.721462 8.723097 8.173944
20 | C 10.816121 8.435159 6.823485
21 | C 10.798498 7.069886 6.379586
22 | C 7.051916 8.859148 10.708102
23 | C 7.967206 9.794983 10.117526
24 | C 7.474342 7.576422 11.008626
25 | C 9.272792 9.417658 9.852969
26 | C 9.895462 9.809569 8.618954
27 | C 7.238980 10.582094 9.162312
28 | C 7.840773 10.959239 7.974597
29 | C 9.195117 10.569245 7.697662
30 | C 5.374161 6.654115 10.170096
31 | C 4.937901 7.985520 9.853171
32 | C 6.621848 6.453802 10.736730
33 | C 5.758490 9.067996 10.119733
34 | C 5.873753 10.135703 9.164181
35 | C 4.201519 7.930187 8.620421
36 | C 4.312053 8.957390 7.699603
37 | C 5.163974 10.081363 7.976091
38 | C 6.999479 4.520223 9.294031
39 | C 5.705566 4.727091 8.704789
40 | C 7.448575 5.367116 10.293883
41 | C 4.910853 5.777417 9.132971
42 | C 4.183929 6.564936 8.176527
43 | C 5.804808 4.430742 7.302344
44 | C 5.104510 5.190315 6.381014
45 | C 4.278582 6.276817 6.826042
46 | C 9.675823 5.405374 9.292253
47 | C 9.211862 4.525032 8.255720
48 | C 8.813066 5.817058 10.294389
49 | C 7.895902 4.094861 8.255764
50 | C 7.159178 4.040812 7.025439
51 | C 9.836006 4.918550 7.023895
52 | C 9.126376 4.864265 5.835769
53 | C 7.761135 4.417873 5.837738
54 | C 5.289916 6.913136 4.832970
55 | C 5.727303 5.582103 5.146953
56 | C 7.032928 5.204949 4.882489
57 | C 6.171650 7.815768 4.263762
58 | C 5.324012 9.594538 5.707671
59 | C 4.409034 8.658347 6.298425
60 | C 4.392391 7.342155 5.869837
61 | C 6.186863 9.182829 4.705636
62 | C 8.000322 10.479964 5.705980
63 | C 7.103885 10.905282 6.744273
64 | C 5.787937 10.474912 6.744201
65 | C 7.551282 9.632948 4.706255
66 | C 9.625863 8.346081 4.829947
67 | C 10.089185 9.222774 5.867044
68 | C 9.294301 10.273068 6.295174
69 | C 8.378132 8.546351 4.263469
70 | C 7.948245 6.140894 4.291949
71 | C 9.241695 5.932044 4.880229
72 | C 10.062172 7.014647 5.146797
73 | C 7.525790 7.423643 3.991555
74 | %ENDBLOCK POSITIONS_ABS
75 |
76 |
77 | # calculation settings for gamma point only
78 | kpoints_MP_grid 1 1 1
79 | kpoints_MP_offset 0.0 0.0 0.0
80 |
81 | # geometry optimisation specific
82 | FIX_ALL_CELL: True
83 | FIX_COM: True
84 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/H2_in_C60/h2c60.hybrid-md-input.yaml:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021.
4 |
5 | # this is an input file for PP-Hybrid MD
6 | can_update: true # if updates are to be performed
7 | check_interval: 5
8 | num_initial_steps: 5
9 | tolerances:
10 | ediff: 0.01 # eV
11 | fmax: 0.250 # eV/A
12 | frmse: 0.200 # eV/A
13 | vmax: null # eV (virial)
14 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/H2_in_C60/h2c60.param:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021.
4 |
5 | # general parameters
6 | CUT_OFF_ENERGY: 400
7 | XC_FUNCTIONAL: pbe
8 | ELEC_ENERGY_TOL: 1e-08
9 |
10 | # dispersion correction
11 | sedc_apply: true
12 |
13 | # finite basis correction
14 | #FINITE_BASIS_CORR: 2
15 | #FINITE_BASIS_SPACING: 10
16 |
17 | # other settings
18 | SPIN_POLARIZED: TRUE
19 | PERC_EXTRA_BANDS: 300.0
20 | MAX_SCF_CYCLES: 200
21 | FIX_OCCUPANCY: FALSE
22 | SMEARING_WIDTH: 0.2
23 | NUM_DUMP_CYCLES: 0
24 |
25 | #############################
26 | # MD & Hybrid settings
27 | #############################
28 | comment = MD with Hybrid method - H2 in C60
29 | task = molecular dynamics
30 | md_ensemble = NVT
31 | md_thermostat = Langevin
32 | md_sample_iter = 1
33 |
34 | # dt & T
35 | md_delta_t = 1 fs # There is H in the system
36 | md_temperature = 600 K
37 | md_num_iter = 100
38 |
39 | # Hybrid-MD stuff
40 | %BLOCK DEVEL_CODE
41 |
42 | ! generally turns on PP, this is needed together with "MD_PP_HYBRID=T"
43 | PP=T
44 | MD: PP=T :ENDMD
45 |
46 | ! settings of model called through QUIP
47 | pp:
48 | QUIP=T
49 | QUIP_PARAM_FILE=GAP.xml
50 | quip_init_args:IP GAP:endquip_init_args
51 | :endpp
52 |
53 | ! settings of PP Hybrid MD
54 | MD_PP_HYBRID=T
55 | md_pp_hybrid_exec:
56 | hybrid-md
57 | :endmd_pp_hybrid_exec
58 | %ENDBLOCK DEVEL_CODE
59 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/check_installation/readme.md:
--------------------------------------------------------------------------------
1 | # Simple test job to test compilation
2 |
3 | 8-atom SiC cell, running 50 steps of MD.
4 |
5 | This is intended to check that your installation is correct and the needed executables are in your path. There is no
6 | scientific meaning of this test, the basis set and all settings are dumbed down a lot.
7 |
8 | However, this even runs on a laptop in serial.
9 |
10 | *n.b. If you are on Castep v22, then use the alternative param file `sic_md.param-v22` instead, which is the same in
11 | essence but has the relevant keywords.*
12 |
13 | ## how to execute
14 |
15 | You should have the following in your path:
16 |
17 | - `gap_fit`: compiled serial or with OpenMP (no MPI linked to it)
18 | - `hybrid-md`: python package installs this executable, you need to activate the correct Python env to have it
19 |
20 | ```bash
21 | mpirun -n 2 castep.mpi sic_md
22 | ```
23 |
24 | ## Success criteria
25 |
26 | Pretty much that no error is seen with the above, plus a number of files showing up:
27 |
28 | - `GAP_model_step000.../` directories showing up - these are the model versions retired at given steps of the run
29 | - `GAP.xml` + sparse files: this is the latest model
30 | - `sic_md.castep` castep output file
31 | - `sic_md.hybrid-md.xyz` ab-initio observations gathered during the calculation
32 |
33 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/check_installation/sic_md.cell:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2022.
4 |
5 | %BLOCK LATTICE_CART
6 | 4.350000 0.000000 0.000000
7 | 0.000000 4.350000 0.000000
8 | 0.000000 0.000000 4.350000
9 | %ENDBLOCK LATTICE_CART
10 |
11 | %BLOCK POSITIONS_ABS
12 | Si 0.000000 0.000000 0.010000
13 | C 1.087500 1.087500 1.087500
14 | Si 0.000000 2.175000 2.175000
15 | C 1.087500 3.262500 3.262500
16 | Si 2.175000 0.000000 2.175000
17 | C 3.262500 1.087500 3.262500
18 | Si 2.175000 2.175000 0.000000
19 | C 3.262500 3.262500 1.087500
20 | %ENDBLOCK POSITIONS_ABS
21 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/check_installation/sic_md.hybrid-md-input.yaml:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 |
5 | # this is an input file for PP-Hybrid MD
6 | can_update: true
7 | check_interval: 5
8 | num_initial_steps: 2
9 | tolerances:
10 | ediff: 0.01 # eV
11 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/check_installation/sic_md.param:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 |
5 | ###############################################################
6 | # functional & general settings
7 | ###############################################################
8 | CUT_OFF_ENERGY: 200 eV
9 | ELEC_ENERGY_TOL: 0.0001 eV
10 | FINITE_BASIS_CORR: 0
11 | FIX_OCCUPANCY = true
12 | backup_interval = 0
13 | calculate_stress = true
14 | popn_calculate = false
15 | write_checkpoint = none
16 |
17 | ###############################################################
18 | # MD settings
19 | ###############################################################
20 | task = molecular dynamics
21 |
22 | md_ensemble = NVT
23 | md_thermostat = Langevin
24 | md_num_iter = 50
25 | md_temperature = 100 K
26 | md_sample_iter = 10
27 | md_delta_t = 1 fs
28 |
29 | ###############################################################
30 | # Devel code: contains the `PP_HYBRID` method settings
31 | ###############################################################
32 | %BLOCK DEVEL_CODE
33 | ! generally turns on PP, this is needed together with "PP_HYBRID=T"
34 | PP=T
35 | MD: PP=T :ENDMD
36 |
37 | ! settings of model called through QUIP
38 | pp:
39 | QUIP=T
40 | QUIP_PARAM_FILE=GAP.xml
41 | quip_init_args:IP GAP:endquip_init_args
42 | :endpp
43 |
44 | ! settings of PP Hybrid MD
45 | PP_HYBRID=T
46 | PP_HYBRID_EXEC:
47 | hybrid-md
48 | :endPP_HYBRID_EXEC
49 | %ENDBLOCK DEVEL_CODE
50 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/check_installation/sic_md.param-v22:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 |
5 | ###############################################################
6 | # functional & general settings
7 | ###############################################################
8 | CUT_OFF_ENERGY: 200 eV
9 | ELEC_ENERGY_TOL: 0.0001 eV
10 | FINITE_BASIS_CORR: 0
11 | FIX_OCCUPANCY = true
12 | backup_interval = 0
13 | calculate_stress = true
14 | popn_calculate = false
15 | write_checkpoint = none
16 |
17 | ###############################################################
18 | # MD settings
19 | ###############################################################
20 | task = molecular dynamics
21 |
22 | md_ensemble = NVT
23 | md_thermostat = Langevin
24 | md_num_iter = 50
25 | md_temperature = 100 K
26 | md_sample_iter = 10
27 | md_delta_t = 1 fs
28 |
29 | ###############################################################
30 | # Devel code: contains the `PP_HYBRID` method settings
31 | # compatible with castep v22
32 | ###############################################################
33 | %BLOCK DEVEL_CODE
34 | ! generally turns on PP, this is needed together with "PP_HYBRID=T"
35 | PP=T
36 | MD: PP=T :ENDMD
37 |
38 | ! settings of model called through QUIP
39 | pp:
40 | QUIP=T
41 | QUIP_PARAM_FILE=GAP.xml
42 | quip_init_args:IP GAP:endquip_init_args
43 | :endpp
44 |
45 | ! settings of PP Hybrid MD
46 | MD_PP_HYBRID=T
47 | md_pp_hybrid_exec:
48 | hybrid-md
49 | :endmd_pp_hybrid_exec
50 | %ENDBLOCK DEVEL_CODE
51 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/walkthrough/0-initial/si16.cell:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 |
5 | FIX_ALL_CELL True
6 | FIX_COM True
7 |
8 | %BLOCK LATTICE_CART
9 | 7.679180 0.000000 0.000000
10 | 0.000000 7.679180 0.000000
11 | 0.000000 0.000000 5.430000
12 | %ENDBLOCK LATTICE_CART
13 |
14 | %BLOCK POSITIONS_ABS
15 | Si 0.000000 0.000000 0.000000
16 | Si 1.919795 0.000000 1.357500
17 | Si 1.919795 1.919795 2.715000
18 | Si 0.000000 1.919795 4.072500
19 | Si 0.000000 3.839590 0.000000
20 | Si 1.919795 3.839590 1.357500
21 | Si 1.919795 5.759385 2.715000
22 | Si 0.000000 5.759385 4.072500
23 | Si 3.839590 0.000000 0.000000
24 | Si 5.759385 0.000000 1.357500
25 | Si 5.759385 1.919795 2.715000
26 | Si 3.839590 1.919795 4.072500
27 | Si 3.839590 3.839590 0.000000
28 | Si 5.759385 3.839590 1.357500
29 | Si 5.759385 5.759385 2.715000
30 | Si 3.839590 5.759385 4.072500
31 | %ENDBLOCK POSITIONS_ABS
32 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/walkthrough/0-initial/si16.hybrid-md-input.yaml:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 |
5 | # Example input file for the first calculation
6 | #
7 |
8 | can_update: true
9 | check_interval: 10
10 | num_initial_steps: 3
11 | tolerances:
12 | ediff: 0.01 # eV
13 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/walkthrough/0-initial/si16.param:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 |
5 | ###############################################################
6 | # functional & general settings
7 | ###############################################################
8 | xc_functional LDA
9 | basis_precision PRECISE ! basis set size from presets
10 | fix_occupancy true
11 | opt_strategy speed ! faster runtime with more memory use
12 |
13 | # allow continuation
14 | NUM_BACKUP_ITER = 10 ! interval between checkpoints (.check file)
15 | continuation: default ! continuation if .check file exists
16 |
17 | ###############################################################
18 | # MD settings
19 | ###############################################################
20 | task = molecular dynamics
21 |
22 | md_ensemble = NVT
23 | md_thermostat = Langevin
24 | md_num_iter = 50
25 | md_temperature = 300 K
26 | md_sample_iter = 10 ! interval between dumping MD frames
27 | md_delta_t = 1 fs
28 |
29 | ###############################################################
30 | # Devel code: contains the `PP_HYBRID` method settings
31 | ###############################################################
32 | %BLOCK DEVEL_CODE
33 | ! turn on the PP & acceleration modules
34 | PP=T
35 | MD: PP=T :ENDMD
36 | PP_HYBRID=T
37 |
38 | ! settings of model called through QUIP
39 | pp:
40 | QUIP=T
41 | QUIP_PARAM_FILE=GAP.xml
42 | quip_init_args:IP GAP:endquip_init_args
43 | :endpp
44 |
45 | ! settings of PP Hybrid MD
46 |
47 | PP_HYBRID_EXEC:
48 | hybrid-md
49 | :endPP_HYBRID_EXEC
50 | %ENDBLOCK DEVEL_CODE
51 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/walkthrough/1-adaptive/si16.cell:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 |
5 | FIX_ALL_CELL True
6 | FIX_COM True
7 |
8 | %BLOCK LATTICE_CART
9 | 7.679180 0.000000 0.000000
10 | 0.000000 7.679180 0.000000
11 | 0.000000 0.000000 5.430000
12 | %ENDBLOCK LATTICE_CART
13 |
14 | %BLOCK POSITIONS_ABS
15 | Si 0.000000 0.000000 0.000000
16 | Si 1.919795 0.000000 1.357500
17 | Si 1.919795 1.919795 2.715000
18 | Si 0.000000 1.919795 4.072500
19 | Si 0.000000 3.839590 0.000000
20 | Si 1.919795 3.839590 1.357500
21 | Si 1.919795 5.759385 2.715000
22 | Si 0.000000 5.759385 4.072500
23 | Si 3.839590 0.000000 0.000000
24 | Si 5.759385 0.000000 1.357500
25 | Si 5.759385 1.919795 2.715000
26 | Si 3.839590 1.919795 4.072500
27 | Si 3.839590 3.839590 0.000000
28 | Si 5.759385 3.839590 1.357500
29 | Si 5.759385 5.759385 2.715000
30 | Si 3.839590 5.759385 4.072500
31 | %ENDBLOCK POSITIONS_ABS
32 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/walkthrough/1-adaptive/si16.hybrid-md-input.yaml:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 |
5 | # Example input file for the first calculation
6 | #
7 |
8 | can_update: true
9 | check_interval: 10
10 | num_initial_steps: 3
11 | tolerances:
12 | ediff: 0.01 # eV
13 | adaptive_method_parameters:
14 | n_min: 5
15 | n_max: 100
16 | factor: 1.5
17 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/walkthrough/1-adaptive/si16.param:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 |
5 | ###############################################################
6 | # functional & general settings
7 | ###############################################################
8 | xc_functional LDA
9 | basis_precision PRECISE ! basis set size from presets
10 | fix_occupancy true
11 | opt_strategy speed ! faster runtime with more memory use
12 |
13 | # allow continuation
14 | NUM_BACKUP_ITER = 10 ! interval between checkpoints (.check file)
15 | continuation: default ! continuation if .check file exists
16 |
17 | ###############################################################
18 | # MD settings
19 | ###############################################################
20 | task = molecular dynamics
21 |
22 | md_ensemble = NVT
23 | md_thermostat = Langevin
24 | md_num_iter = 1000 ! set 1000 for the continuation after
25 | md_temperature = 300 K
26 | md_sample_iter = 10 ! interval between dumping MD frames
27 | md_delta_t = 1 fs
28 |
29 | ###############################################################
30 | # Devel code: contains the `PP_HYBRID` method settings
31 | ###############################################################
32 | %BLOCK DEVEL_CODE
33 | ! turn on the PP & acceleration modules
34 | PP=T
35 | MD: PP=T :ENDMD
36 | PP_HYBRID=T
37 |
38 | ! settings of model called through QUIP
39 | pp:
40 | QUIP=T
41 | QUIP_PARAM_FILE=GAP.xml
42 | quip_init_args:IP GAP:endquip_init_args
43 | :endpp
44 |
45 | ! settings of PP Hybrid MD
46 |
47 | PP_HYBRID_EXEC:
48 | hybrid-md
49 | :endPP_HYBRID_EXEC
50 | %ENDBLOCK DEVEL_CODE
51 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/walkthrough/2-gap-parameters/si16.cell:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 |
5 | # start MD with slightly noisy positions
6 | positions_noise 0.1 Ang
7 | FIX_ALL_CELL True
8 | FIX_COM True
9 |
10 | %BLOCK LATTICE_CART
11 | 7.679180 0.000000 0.000000
12 | 0.000000 7.679180 0.000000
13 | 0.000000 0.000000 5.430000
14 | %ENDBLOCK LATTICE_CART
15 |
16 | %BLOCK POSITIONS_ABS
17 | Si 0.000000 0.000000 0.000000
18 | Si 1.919795 0.000000 1.357500
19 | Si 1.919795 1.919795 2.715000
20 | Si 0.000000 1.919795 4.072500
21 | Si 0.000000 3.839590 0.000000
22 | Si 1.919795 3.839590 1.357500
23 | Si 1.919795 5.759385 2.715000
24 | Si 0.000000 5.759385 4.072500
25 | Si 3.839590 0.000000 0.000000
26 | Si 5.759385 0.000000 1.357500
27 | Si 5.759385 1.919795 2.715000
28 | Si 3.839590 1.919795 4.072500
29 | Si 3.839590 3.839590 0.000000
30 | Si 5.759385 3.839590 1.357500
31 | Si 5.759385 5.759385 2.715000
32 | Si 3.839590 5.759385 4.072500
33 | %ENDBLOCK POSITIONS_ABS
34 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/walkthrough/2-gap-parameters/si16.hybrid-md-input.yaml:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 |
5 | # Example input file for the first calculation
6 | #
7 |
8 | can_update: true
9 | check_interval: 10
10 | num_initial_steps: 1
11 | tolerances:
12 | ediff: 0.01 # eV
13 | adaptive_method_parameters:
14 | n_min: 10
15 | n_max: 200
16 | factor: 1.5
17 | refit:
18 | e0_method: "average"
19 | num_threads: 4
20 | descriptor_str: "distance_Nb order=2 n_sparse=20 cutoff=5.5 cutoff_transition_width=1.0 compact_clusters covariance_type=ard_se theta_uniform=1.0 sparse_method=uniform f0=0.0 add_species=T delta=1.0 : soap n_sparse=1000 n_max=10 l_max=4 cutoff=5.0 cutoff_transition_width=1.0 atom_sigma=0.5 add_species=True covariance_type=dot_product zeta=4 sparse_method=cur_points delta=3.0 "
21 | previous_data: [
22 | "previous_si16.hybrid-md.xyz"
23 | ]
24 | default_sigma: "0.005 0.1 0.05 1.0"
25 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/walkthrough/2-gap-parameters/si16.param:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 |
5 | ###############################################################
6 | # functional & general settings
7 | ###############################################################
8 | xc_functional LDA
9 | basis_precision PRECISE ! basis set size from presets
10 | fix_occupancy true
11 | opt_strategy speed ! faster runtime with more memory use
12 |
13 | # allow continuation
14 | NUM_BACKUP_ITER = 10 ! interval between checkpoints (.check file)
15 | continuation: default ! continuation if .check file exists
16 |
17 | ###############################################################
18 | # MD settings
19 | ###############################################################
20 | task = molecular dynamics
21 |
22 | md_ensemble = NVT
23 | md_thermostat = Langevin
24 | md_num_iter = 1000 ! set 1000 for the continuation after
25 | md_temperature = 300 K
26 | md_sample_iter = 10 ! interval between dumping MD frames
27 | md_delta_t = 1 fs
28 |
29 | ###############################################################
30 | # Devel code: contains the `PP_HYBRID` method settings
31 | ###############################################################
32 | %BLOCK DEVEL_CODE
33 | ! turn on the PP & acceleration modules
34 | PP=T
35 | MD: PP=T :ENDMD
36 | PP_HYBRID=T
37 |
38 | ! settings of model called through QUIP
39 | pp:
40 | QUIP=T
41 | QUIP_PARAM_FILE=GAP.xml
42 | quip_init_args:IP GAP:endquip_init_args
43 | :endpp
44 |
45 | ! settings of PP Hybrid MD
46 |
47 | PP_HYBRID_EXEC:
48 | hybrid-md
49 | :endPP_HYBRID_EXEC
50 | %ENDBLOCK DEVEL_CODE
51 |
--------------------------------------------------------------------------------
/hybrid_md_package/examples/walkthrough/readme.md:
--------------------------------------------------------------------------------
1 | # Walkthrough of the settings with a simple calculation
2 |
3 | We are running MD of 16 Si atoms. You can run each of these, see full details in the documentation, under examples.
4 |
--------------------------------------------------------------------------------
/hybrid_md_package/hybrid_md/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/libAtoms/GAP/51e038baa66bcc6a1902ea94c884f4383c8b2931/hybrid_md_package/hybrid_md/__init__.py
--------------------------------------------------------------------------------
/hybrid_md_package/hybrid_md/cli.py:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 | """
5 | CLI of hybrid MD implementation.
6 |
7 | The QM calculators should call this with the subcommands at correct points in the
8 | calculation.
9 |
10 | """
11 | import sys
12 |
13 | import click
14 |
15 | from hybrid_md.decision_making import PreStepReturnNumber, get_decision_maker
16 | from hybrid_md.refit import refit
17 | from hybrid_md.state_objects import HybridMD
18 |
19 | VERBOSE = True
20 |
21 |
22 | @click.group("hybrid-md")
23 | def main():
24 | """
25 | Hybrid MD main CLI. See the subcommands for more details.
26 | """
27 |
28 |
29 | @main.command("initialise")
30 | @click.argument("seed", type=click.STRING)
31 | @click.argument("md-iteration", type=click.INT, default=0)
32 | def initialise(seed, md_iteration):
33 | """Initialisation of the Hybrid MD run.
34 |
35 | Answer: integer exit code (three bits encoded)
36 | - i_exit & 1: read log and put in code's log
37 | - i_exit & 2: start with ab-initio (on True), else GAP
38 | """
39 |
40 | # create the initial state object
41 | state = HybridMD(seed, md_iteration)
42 | state.carry.current_check_interval = state.settings.check_interval
43 | state.carry.next_is_pre_step = True
44 |
45 | # decide if we are continuing a calculation
46 | continuation = md_iteration > 0
47 | if continuation:
48 | state.handle_continuation()
49 |
50 | if VERBOSE:
51 | print(
52 | "Hybrid-MD: INIT Step, exit: "
53 | f"{0 if state.settings.num_initial_steps == 0 else 1} "
54 | f" -- num_initial_steps {state.settings.num_initial_steps}",
55 | f" -- continuation {continuation}",
56 | )
57 |
58 | # write state to disc, only `next_is_pre_step` relevant though
59 | state.carry.dump()
60 |
61 | # write the log for the .castep file
62 | state.io_initial_step_banner()
63 |
64 | # exit status -- log reading always ON
65 | if state.settings.num_initial_steps == 0:
66 | sys.exit(1)
67 | else:
68 | sys.exit(3)
69 |
70 |
71 | @main.command("pre-step")
72 | @click.argument("seed", type=click.STRING)
73 | @click.argument("md-iteration", type=click.INT)
74 | def pre_step(seed, md_iteration):
75 | """Start of the MD-Hybrid step, called by md step
76 |
77 | Answer: integer exit code (three bits encoded)
78 | - i_exit & 1: read log and put in code's log
79 | - i_exit & 2: do ab-initio now
80 | - i_exit & 4: do ab-initio in next iteration (rest of loop)
81 | - i_exit & 8: do update cell
82 | - i_exit & 16: use ab-initio forces in MD
83 | """
84 |
85 | # state of object
86 | state = HybridMD(seed, md_iteration)
87 | state.carry.load()
88 | state.carry.reset()
89 | if not state.carry.next_is_pre_step:
90 | raise RuntimeError(
91 | "Hybrid MD steps called in the wrong order, expected post-step"
92 | )
93 |
94 | # decision-making & update of state object
95 | decision = get_decision_maker(state).get_step_kind(md_iteration)
96 | converter = PreStepReturnNumber(state)
97 | return_value = converter.push_state(decision)
98 |
99 | # dump state
100 | state.carry.dump()
101 |
102 | if VERBOSE:
103 | print(
104 | f"Hybrid-MD: PRE Step, exit:{return_value:4}, "
105 | f"md_iteration:{md_iteration:3} -->",
106 | converter.do_ab_initio,
107 | converter.next_ab_initio,
108 | converter.do_update_cell,
109 | converter.use_qm_forces,
110 | )
111 |
112 | # pass the result back
113 | sys.exit(return_value)
114 |
115 |
116 | @main.command("post-step")
117 | @click.argument("seed", type=click.STRING)
118 | @click.argument("md-iteration", type=click.INT)
119 | def post_step(seed, md_iteration):
120 | """End of the MD-Hybrid step, called by md.f90
121 |
122 | Answer: integer exit code (three bits encoded)
123 | - i_exit & 1: read log and put in code's log
124 | - i_exit & 2: read the GAP model again
125 | """
126 |
127 | # state of object
128 | state = HybridMD(seed, md_iteration)
129 | state.carry.load()
130 | if state.carry.next_is_pre_step:
131 | raise RuntimeError(
132 | "Hybrid MD steps called in the wrong order, expected pre-step"
133 | )
134 |
135 | # main logic
136 | if state.carry.do_comparison:
137 | # 1. read output 2. calculate E, F, S errors for this frame and cumulatively,
138 | # force by species
139 | state.read_xyz()
140 |
141 | # 3. decide if we are fitting or not
142 | if not state.check_tolerances() and state.settings.can_update:
143 | state.carry.do_update_model = True
144 |
145 | # 4. IO: errors of this step and cumulative ones as well
146 | state.error_table()
147 | state.cumulative_error_table()
148 |
149 | # 5. update model -- triggered by either pre- or post-step
150 | if state.carry.do_update_model:
151 | refit(state)
152 |
153 | # 6. post-step call to decision maker
154 | get_decision_maker(state).post_step_action(md_iteration)
155 |
156 | # save state
157 | state.carry.next_is_pre_step = True
158 | state.carry.dump()
159 |
160 | if VERBOSE:
161 | print(
162 | f"Hybrid-MD: POST Step, exit:"
163 | f"{int(state.carry.do_update_model):4}, md_iteration:{md_iteration:3}"
164 | )
165 |
166 | # exit status -- log reading always ON
167 | exit_code = int(state.carry.do_comparison) + 2 * int(state.carry.do_update_model)
168 | sys.exit(exit_code)
169 |
170 |
171 | if __name__ == "__main__":
172 | main()
173 | # in case main() does not exit
174 | sys.exit(-999)
175 |
--------------------------------------------------------------------------------
/hybrid_md_package/hybrid_md/decision_making.py:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 | """
5 | Decision maker utilities for the Hybrid MD
6 | """
7 |
8 | from abc import ABC, abstractmethod
9 |
10 | from hybrid_md.state_objects import HybridMD, StepKinds
11 |
12 |
13 | def get_decision_maker(state: HybridMD):
14 | """Choose the decision maker class
15 |
16 | If you implement any new ones, please include them in here with the appropriate
17 | logic for selecting in based on the settings.
18 |
19 | """
20 | if state.settings.adaptive_method_parameters:
21 | return AdaptiveDecisionMaker(state)
22 | return SimpleDecisionMaker(state)
23 |
24 |
25 | class DecisionMakerBase(ABC):
26 | """Base class for decision-making
27 |
28 | used for pre-step decisions
29 | """
30 |
31 | def __init__(self, state: HybridMD):
32 | self.state = state
33 |
34 | @abstractmethod
35 | def get_step_kind(self, md_iteration: int) -> StepKinds:
36 | """Perform the decision-making in any way needed"""
37 |
38 | @abstractmethod
39 | def post_step_action(self, md_iteration: int):
40 | """Actions post any DFT calculation step
41 |
42 | This happens before dumping the state
43 | """
44 |
45 |
46 | class SimpleDecisionMaker(DecisionMakerBase):
47 | """
48 | Simple uniform interval, like we had it implemented so far
49 |
50 | refactor this out of the cli module
51 |
52 | """
53 |
54 | def get_step_kind(self, md_iteration: int) -> StepKinds:
55 | """Uniform checking, with optional initial DFT steps
56 |
57 | Parameters
58 | ----------
59 | md_iteration
60 |
61 | Returns
62 | -------
63 | step_kind
64 | """
65 |
66 | if self.state.carry.continuation:
67 | num_initial_steps = self.state.carry.continuation_initial_steps
68 | else:
69 | num_initial_steps = self.state.settings.num_initial_steps
70 |
71 | if md_iteration < num_initial_steps:
72 | return StepKinds.INITIAL
73 |
74 | if md_iteration == num_initial_steps:
75 | return StepKinds.LAST_INITIAL
76 |
77 | if (md_iteration - num_initial_steps) % self.state.settings.check_interval == 0:
78 | return StepKinds.CHECK
79 |
80 | return StepKinds.GENERIC
81 |
82 | def post_step_action(self, md_iteration: int):
83 | # not doing anything in this case
84 | pass
85 |
86 |
87 | class AdaptiveDecisionMaker(DecisionMakerBase):
88 | """
89 | Adaptive decision-making, with increasing and decreasing
90 | the interval based on accuracy target being met or not.
91 | """
92 |
93 | def __init__(self, state: HybridMD):
94 | super().__init__(state)
95 | self.settings = state.settings.adaptive_method_parameters
96 | self.step_kind = None
97 |
98 | def get_step_kind(self, md_iteration: int) -> StepKinds:
99 | if self.state.carry.continuation:
100 | num_initial_steps = self.state.carry.continuation_initial_steps
101 | else:
102 | num_initial_steps = self.state.settings.num_initial_steps
103 |
104 | if md_iteration < num_initial_steps:
105 | self.state.carry.current_check_interval = self.state.settings.check_interval
106 | return StepKinds.INITIAL
107 | if num_initial_steps == 0 and md_iteration == 0:
108 | # to function in case we have no initial steps
109 | self.state.carry.last_check_step = 0
110 | if md_iteration == num_initial_steps:
111 | # we need to remember this one as well
112 | self.state.carry.current_check_interval = self.state.settings.check_interval
113 | self.state.carry.last_check_step = md_iteration
114 | return StepKinds.LAST_INITIAL
115 | if (
116 | md_iteration - self.state.carry.last_check_step
117 | ) == self.state.carry.current_check_interval:
118 | # This is the crucial difference
119 | self.state.carry.last_check_step = md_iteration
120 | return StepKinds.CHECK
121 | return StepKinds.GENERIC
122 |
123 | def post_step_action(self, md_iteration: int):
124 | # we change the step size in case we had a checking step
125 | if self.state.carry.do_comparison:
126 | if self.state.check_tolerances():
127 | # increase N
128 | self.state.carry.current_check_interval = min(
129 | int(self.state.carry.current_check_interval * self.settings.factor),
130 | self.settings.n_max,
131 | )
132 | word = "INCREASE"
133 | else:
134 | # decrease N
135 | self.state.carry.current_check_interval = max(
136 | int(self.state.carry.current_check_interval / self.settings.factor),
137 | self.settings.n_min,
138 | )
139 | word = "DECREASE"
140 |
141 | # write log, common
142 | self.state.write_to_tmp_log(
143 | [
144 | f" Hybrid-MD: {word} interval to "
145 | f"{self.state.carry.current_check_interval:6} "
146 | f"at iter {md_iteration:8}"
147 | f" <-- Hybrid-MD-Adapt"
148 | ],
149 | append=True,
150 | )
151 |
152 |
153 | class PreStepReturnNumber:
154 | """
155 | Return number and changes to state object for Pre-step
156 |
157 | heavily mimicking the Fortran solution in terms of logic
158 | """
159 |
160 | do_ab_initio = False # 2
161 | next_ab_initio = False # 4
162 | do_update_cell = False # 8
163 | do_comparison = False # print the error table
164 | do_update_model = False # REFIT
165 | use_qm_forces = False # 16
166 |
167 | def __init__(self, state: HybridMD):
168 | self.state = state
169 |
170 | def push_state(self, value: StepKinds):
171 | """Actions to be done for the pre-step
172 |
173 | Parameters
174 | ----------
175 | value
176 |
177 | Returns
178 | -------
179 |
180 | """
181 | # set the internal booleans
182 | self._reset()
183 | self._set_internals(value)
184 |
185 | # save values into state
186 | self.state.carry.next_is_pre_step = False
187 | self.state.carry.next_ab_initio = self.next_ab_initio
188 | self.state.carry.do_comparison = self.do_comparison
189 |
190 | if self.do_update_model:
191 | if self.state.settings.can_update:
192 | self.state.carry.do_update_model = self.do_update_model
193 | else:
194 | raise RuntimeError(
195 | "Tried to update model, but input settings do not allow it!"
196 | )
197 |
198 | # return an integer
199 | return self._get_return_value()
200 |
201 | def _get_return_value(self):
202 | return_value = 0 # log reading off for this one
203 | for num, val in [
204 | (2, self.do_ab_initio),
205 | (4, self.next_ab_initio),
206 | (8, self.do_update_cell),
207 | (16, self.use_qm_forces),
208 | ]:
209 | return_value += num * int(val)
210 |
211 | return return_value
212 |
213 | def _reset(self):
214 | self.do_ab_initio = False # 2
215 | self.next_ab_initio = False # 4
216 | self.do_update_cell = False # 8
217 | self.do_comparison = False # print the error table
218 | self.do_update_model = False # REFIT
219 | self.use_qm_forces = False # 16
220 |
221 | def _set_internals(self, value: StepKinds):
222 | # sets internal values based on Step Kind
223 |
224 | if value == StepKinds.INITIAL:
225 | # initial steps with ab-initio, we have no PP model to work with
226 | self.do_ab_initio = True
227 | self.next_ab_initio = True
228 | self.use_qm_forces = True # we don't have GAP forces yet
229 | elif value == StepKinds.LAST_INITIAL:
230 | # last initial ab-initio step, ask for model update now
231 | self.do_ab_initio = True
232 | self.do_update_model = True
233 | self.use_qm_forces = True # we don't have GAP forces yet
234 | elif value == StepKinds.CHECK:
235 | # check steps during the run
236 | self.do_ab_initio = True
237 | # this is the only case when the previous electronic state needs changing
238 | self.do_update_cell = True
239 | self.do_comparison = True
240 | elif value == StepKinds.GENERIC:
241 | # this is a generic step with PP
242 | pass
243 | else:
244 | raise ValueError("Step kind given is not known")
245 |
--------------------------------------------------------------------------------
/hybrid_md_package/hybrid_md/refit.py:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 | """
5 | Refitting of model on the fly
6 |
7 | This is a generic refitting function, specific ones and tweaks of
8 | this one with the same interface are to be implemented here.
9 | """
10 |
11 | import importlib
12 | import os.path
13 | import shutil
14 | import subprocess
15 | from datetime import datetime
16 | from pathlib import Path
17 | from time import time
18 |
19 | import ase.io
20 | import numpy as np
21 |
22 | from hybrid_md.settings import SoapParamPreset
23 | from hybrid_md.state_objects import HybridMD
24 |
25 |
26 | def refit(state: HybridMD):
27 | """Refit a GAP model, with in-place update
28 |
29 | This is a generic one, which can import the function
30 |
31 | Parameters
32 | ----------
33 | state: HybridMD
34 |
35 | """
36 | refit_settings = state.settings.refit
37 |
38 | if refit_settings.function_name is None:
39 | return refit_generic(state)
40 |
41 | refit_function_import = state.settings.refit_function_name
42 |
43 | # separate import path
44 | module_name = ".".join(refit_function_import.split(".")[:-1])
45 | function_name = refit_function_import.split(".")[-1]
46 |
47 | # import the module of the refit function
48 | try:
49 | module = importlib.import_module(module_name)
50 | except ModuleNotFoundError as exc:
51 | raise RuntimeError(f"Refit function's module not found: {module_name}") from exc
52 |
53 | # class of the calculator
54 | if hasattr(module, function_name):
55 | refit_function = getattr(module, function_name)
56 | assert callable(refit_function)
57 | else:
58 | raise RuntimeError(
59 | f"Refit function ({function_name}) not found in module {module_name}"
60 | )
61 |
62 | # YAY, all great now
63 | return refit_function(state)
64 |
65 |
66 | def save_previous_model_files(state: HybridMD):
67 | """Handle Precious GAP-fit's files
68 |
69 | Saves them into a directory
70 |
71 | Parameters
72 | ----------
73 | state
74 |
75 | Returns
76 | -------
77 |
78 | """
79 | refit_settings = state.settings.refit
80 |
81 | main_xml = Path(refit_settings.gp_name)
82 | if not main_xml.is_file():
83 | # no file to deal with
84 | return
85 |
86 | # create new directory for saving the models to
87 | timestamp = datetime.utcnow().strftime("%Y-%m-%d_%H_%M_%S")
88 | save_to = Path(f"GAP_model-step{state.md_iteration:0>6}-at-{timestamp}")
89 | save_to.mkdir(exist_ok=True)
90 |
91 | for filename in sorted(Path("").glob(f"{refit_settings.gp_name}*")) + sorted(
92 | Path("").glob("train.xyz*")
93 | ):
94 | # all GAP files & training xyz
95 | if filename.is_file():
96 | shutil.move(filename, save_to / filename)
97 |
98 |
99 | def refit_generic(state: HybridMD):
100 | """Refit a GAP model, with in-place update
101 |
102 | This is a generic very simple solution, that should work as a
103 | first try starting from scratch. Change this function to your
104 | own system and fitting settings as needed.
105 |
106 | Parameters
107 | ----------
108 | state: HybridMD
109 | """
110 |
111 | # extract main settings
112 | refit_settings = state.settings.refit
113 |
114 | # move any files leftover from the last fit
115 | save_previous_model_files(state)
116 |
117 | # deal with parameters
118 |
119 | # 2B + SOAP model
120 | frames_train = ase.io.read(state.xyz_filename, ":") + state.get_previous_data()
121 |
122 | if refit_settings.descriptor_str is not None:
123 | descriptor_str = refit_settings.descriptor_str
124 | else:
125 | # generic 2B+SOAP, need the frames for delta
126 | delta = np.std([at.info["QM_energy"] / len(at) for at in frames_train]) / 4
127 | desc_str_2b = (
128 | "distance_Nb order=2 n_sparse=20 cutoff=5.0 cutoff_transition_width=1.0 "
129 | "compact_clusters covariance_type=ard_se theta_uniform=1.0 "
130 | f"sparse_method=uniform f0=0.0 add_species=T delta={delta}"
131 | )
132 |
133 | # SOAP parameters
134 | if refit_settings.preset_soap_param is not None:
135 | desc_str_soap = refit_settings.preset_soap_param.soap_str_stub
136 | else:
137 | desc_str_soap = SoapParamPreset.fast.soap_str_stub
138 |
139 | desc_str_soap += f"delta={delta}"
140 | descriptor_str = desc_str_2b + " : " + desc_str_soap
141 |
142 | # save the previous model
143 | if os.path.isfile(refit_settings.gp_name):
144 | shutil.move(refit_settings.gp_name, f"save__{time()}__{refit_settings.gp_name}")
145 |
146 | # training structures & delta
147 | ase.io.write("train.xyz", frames_train)
148 | if os.path.isfile("train.xyz.idx"):
149 | os.remove("train.xyz.idx")
150 |
151 | # deal with e0 & e0_method together
152 | e0_args = ""
153 | if refit_settings.e0_method:
154 | e0_args += " e0_method=" + refit_settings.e0_method
155 | if refit_settings.e0:
156 | e0_args += " e0=" + refit_settings.e0
157 |
158 | # assemble the fitting string
159 | fit_str = (
160 | f"gap_fit at_file=train.xyz gp_file={refit_settings.gp_name}"
161 | f" energy_parameter_name=QM_energy"
162 | f" force_parameter_name=QM_forces"
163 | f" virial_parameter_name=QM_virial"
164 | f" do_copy_at_file=F sparse_separate_file=T"
165 | f" default_sigma={{ {refit_settings.default_sigma} }}"
166 | f" {e0_args}"
167 | f" gap={{ {descriptor_str} }} "
168 | f" {refit_settings.extra_gap_opts}"
169 | )
170 |
171 | # change to OMP-parallel mode for fitting
172 | omp_num_threads_before = os.environ.get("OMP_NUM_THREADS", "1")
173 | if refit_settings.use_omp:
174 | os.environ["OMP_NUM_THREADS"] = str(refit_settings.num_threads)
175 |
176 | # fit the model
177 | proc = subprocess.run(
178 | fit_str, shell=True, capture_output=True, text=True, check=True
179 | )
180 |
181 | # set OMP_NUM_THREADS back to normal
182 | if refit_settings.use_omp:
183 | os.environ["OMP_NUM_THREADS"] = omp_num_threads_before
184 |
185 | # print the outputs to file
186 | with open(f"{refit_settings.gp_name}.fit-stdout.{time()}.txt", "w") as file:
187 | file.write(proc.stdout)
188 | with open(f"{refit_settings.gp_name}.fit-stderr.{time()}.txt", "w") as file:
189 | file.write(proc.stderr)
190 |
--------------------------------------------------------------------------------
/hybrid_md_package/hybrid_md/settings.py:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 | """
5 | Settings of the calculation
6 | """
7 | from dataclasses import dataclass, field
8 | from enum import Enum
9 | from typing import ClassVar, List, Optional, Type
10 |
11 | import marshmallow_dataclass
12 | import yaml
13 | from marshmallow import Schema
14 |
15 |
16 | @dataclass
17 | class Tolerances:
18 | """Represents the tolerances we are setting for the checking steps in the
19 | calculation
20 |
21 | """
22 |
23 | ediff: Optional[float] = None # in eV
24 | fmax: Optional[float] = None # in eV/A
25 | frmse: Optional[float] = None # in eV/A
26 | vmax: Optional[float] = None # in eV
27 |
28 | def __post_init__(self):
29 | # checking of inputs
30 | for key in ["ediff", "fmax", "frmse", "vmax"]:
31 | value = self.get(key)
32 | if value is None:
33 | continue
34 | if self.get(key) <= 0:
35 | raise ValueError(f"Tolerance value `{key}` needs to be positive if set")
36 |
37 | if not any([self.ediff, self.fmax, self.frmse, self.vmax]):
38 | raise ValueError("At least one of the tolerances need to be set")
39 |
40 | def get(self, key: str):
41 | if not isinstance(key, str):
42 | raise KeyError("Non-string keys are not supported")
43 | if hasattr(self, key):
44 | return getattr(self, key)
45 | raise KeyError(f"No attribute {key} found in the {self.__class__.__name__}")
46 |
47 |
48 | class SoapParamPreset(Enum):
49 | """Pre-set SOAP parameters for ease of use
50 |
51 | All of these are using cutoff=5.0 and atom_sigma=0.5 with
52 | (n_max,l_max,n_sparse) being changed.
53 |
54 | """
55 |
56 | fast = {"n_max": 6, "l_max": 3, "n_sparse": 500}
57 | medium = {"n_max": 8, "l_max": 4, "n_sparse": 1000}
58 | accurate = {"n_max": 12, "l_max": 6, "n_sparse": 2000}
59 |
60 | @property
61 | def soap_str_stub(self):
62 | """Stub of SOAP string"""
63 | template = (
64 | "soap n_sparse={n_sparse} n_max={n_max} l_max={l_max} cutoff=5.0 "
65 | "cutoff_transition_width=1.0 atom_sigma=0.5 add_species=True "
66 | "covariance_type=dot_product zeta=4 sparse_method=cur_points "
67 | )
68 | return template.format(**self.value)
69 |
70 |
71 | @dataclass
72 | class Refit:
73 | """Refitting related settings"""
74 |
75 | # custom function
76 | function_name: Optional[str] = None
77 |
78 | # list of paths to XYZs
79 | previous_data: Optional[List[str]] = field(default_factory=list)
80 |
81 | # GAP
82 | preset_soap_param: Optional[SoapParamPreset] = SoapParamPreset.fast
83 |
84 | # GAP parameters
85 | gp_name: str = "GAP.xml"
86 | default_sigma: str = "0.005 0.050 0.1 1.0"
87 | descriptor_str: Optional[str] = None
88 | extra_gap_opts: str = "sparse_jitter=1.0e-8"
89 | e0: Optional[str] = None
90 | e0_method: Optional[str] = "average"
91 |
92 | # for switching to OMP-parallel mode on the fly
93 | num_threads: Optional[int] = None
94 |
95 | @property
96 | def use_omp(self):
97 | """Whether we want to use OMP-parallel fitting"""
98 | return bool(self.num_threads) and self.num_threads > 1
99 |
100 |
101 | @dataclass
102 | class AdaptiveMethodSettings:
103 | """Settings for the adaptive method"""
104 |
105 | n_min: int
106 | n_max: int
107 | factor: float
108 |
109 | def __post_init__(self):
110 | """Validating inputs"""
111 | if self.n_min < 1:
112 | raise ValueError("Adaptive method's minimum interval needs to be positive")
113 | if self.n_max <= 1:
114 | raise ValueError(
115 | "Adaptive method's maximum interval needs to be greater than 1"
116 | )
117 | if self.factor <= 1:
118 | raise ValueError(
119 | "Adaptive method's increment factor needs to be greater than 1"
120 | )
121 |
122 | if self.n_min >= self.n_max:
123 | raise ValueError(
124 | "Adaptive method's maximum interval needs to be greater than it's "
125 | "minimum"
126 | )
127 |
128 |
129 | @marshmallow_dataclass.dataclass
130 | class MainSettings:
131 | """Main settings class of the calculation
132 |
133 | Includes nested sections, and allows for easy reading/writing through marshmallow,
134 | which generates a serializer for the class.
135 | """
136 |
137 | # nested objects
138 | tolerances: Tolerances
139 | adaptive_method_parameters: Optional[AdaptiveMethodSettings]
140 | refit: Refit = field(default_factory=Refit)
141 |
142 | # if model can be updated, if False then we can only measure performance
143 | can_update: bool = False
144 |
145 | # intervals
146 | check_interval: int = 1
147 | num_initial_steps: int = 0
148 |
149 | # for mypy & code editors (this is filled in by marshmallow_dataclass)
150 | Schema: ClassVar[Type[Schema]] = Schema
151 |
152 | @classmethod
153 | def read_input(cls, filename) -> "MainSettings":
154 | """Reads input settings of calculation
155 |
156 | Parameters
157 | ----------
158 | filename
159 |
160 | """
161 | with open(filename, "r") as file:
162 | data = yaml.safe_load(file)
163 |
164 | return cls.Schema().load(data)
165 |
--------------------------------------------------------------------------------
/hybrid_md_package/setup.py:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 | import setuptools
5 |
6 | with open("README.md", "r") as fh:
7 | long_description = fh.read()
8 |
9 | setuptools.setup(
10 | name="hybrid_md",
11 | version="0.0.1",
12 | packages=setuptools.find_packages(),
13 | install_requires=[
14 | "click>=7.0",
15 | "numpy",
16 | "ase",
17 | "pyyaml",
18 | "marshmallow",
19 | "marshmallow_enum",
20 | "marshmallow_dataclass",
21 | ],
22 | entry_points="""
23 | [console_scripts]
24 | hybrid-md=hybrid_md.cli:main
25 | """,
26 | description="Hybrid Molecular Dynamics for Quantum Mechanics codes with Force Fields",
27 | author="Tamas K. Stenczel",
28 | long_description=long_description,
29 | long_description_content_type="text/markdown",
30 | classifiers=[
31 | "Intended Audience :: Science/Research",
32 | "Programming Language :: Python :: 3",
33 | ],
34 | zip_safe=True,
35 | )
36 |
--------------------------------------------------------------------------------
/hybrid_md_package/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/libAtoms/GAP/51e038baa66bcc6a1902ea94c884f4383c8b2931/hybrid_md_package/tests/__init__.py
--------------------------------------------------------------------------------
/hybrid_md_package/tests/test_decision_maker.py:
--------------------------------------------------------------------------------
1 | # Hybrid MD decision making package
2 | #
3 | # Copyright (c) Tamas K. Stenczel 2021-2023.
4 | import os
5 | from pathlib import Path
6 |
7 | from hybrid_md.decision_making import SimpleDecisionMaker
8 | from hybrid_md.state_objects import HybridMD, StepKinds
9 |
10 | dummy_input_content = """
11 | can_update: true
12 | check_interval: 5
13 | num_initial_steps: 3
14 | tolerances:
15 | ediff: 0.01 # eV
16 | refit:
17 | e0_method: "average"
18 | """
19 |
20 |
21 | def test_simple_decision_maker(tmp_path):
22 | # go to a temp dir
23 | os.chdir(tmp_path)
24 |
25 | # create a dummy input file
26 | input_yaml = Path("dummy.hybrid-md-input.yaml")
27 | with input_yaml.open(mode="w") as file:
28 | file.write(dummy_input_content)
29 |
30 | # state object - reads the above
31 | state = HybridMD("dummy")
32 |
33 | for md_iteration, result in [
34 | # nb. 0 is the initial step and calls to this are from 1
35 | (1, StepKinds.INITIAL),
36 | (2, StepKinds.INITIAL),
37 | (3, StepKinds.LAST_INITIAL),
38 | (4, StepKinds.GENERIC),
39 | (8, StepKinds.CHECK),
40 | (14376, StepKinds.GENERIC),
41 | (6343, StepKinds.CHECK),
42 | ]:
43 | # print("num:", md_iteration, result)
44 | assert SimpleDecisionMaker(state).get_step_kind(md_iteration) == result
45 |
--------------------------------------------------------------------------------
/hybrid_md_package/tests/test_settings.py:
--------------------------------------------------------------------------------
1 | import os
2 | import random
3 | from abc import abstractmethod
4 | from pathlib import Path
5 | from string import ascii_letters
6 |
7 | import pytest
8 | from ase.io.extxyz import key_val_str_to_dict
9 | from pytest import raises
10 |
11 | from hybrid_md.settings import (
12 | Tolerances,
13 | SoapParamPreset,
14 | Refit,
15 | MainSettings,
16 | AdaptiveMethodSettings,
17 | )
18 |
19 |
20 | class TestTolerances:
21 | def test_init(self):
22 | Tolerances(ediff=0.1)
23 |
24 | def test_invalid_none_set(self):
25 | with raises(ValueError, match="At least one of the tolerances need to be set"):
26 | Tolerances()
27 |
28 | @pytest.mark.parametrize("key", ["ediff", "fmax", "frmse", "vmax"])
29 | @pytest.mark.parametrize("value", [-10000, -0.1, 0.0])
30 | def test_invalid_value(self, key, value):
31 | with raises(
32 | ValueError, match=f"Tolerance value `{key}` needs to be positive if set"
33 | ):
34 | Tolerances(**{key: value})
35 |
36 | def test_get_valid(self):
37 | # can get parameters which are valid
38 | tolerances = Tolerances(ediff=0.1)
39 | assert tolerances.get("ediff") == 0.1
40 |
41 | def test_get_none(self):
42 | tolerances = Tolerances(fmax=0.1)
43 | assert tolerances.get("ediff") is None
44 | assert tolerances.get("frmse") is None
45 | assert tolerances.get("vmax") is None
46 |
47 | def test_get_invalid_key(self):
48 | tolerances = Tolerances(fmax=0.1)
49 | with raises(KeyError):
50 | tolerances.get("dummy_key")
51 |
52 | def test_get_invalid_key_type(self):
53 | tolerances = Tolerances(fmax=0.1)
54 | with raises(KeyError):
55 | tolerances.get(1) # type: ignore
56 |
57 |
58 | class TestSoapParamPreset:
59 | def test_soap_str_fast(self):
60 | soap_params = SoapParamPreset.fast
61 | assert " n_sparse=500 " in soap_params.soap_str_stub
62 | assert " n_max=6 " in soap_params.soap_str_stub
63 | assert " l_max=3 " in soap_params.soap_str_stub
64 |
65 | def test_soap_str_medium(self):
66 | soap_params = SoapParamPreset.medium
67 | assert " n_sparse=1000 " in soap_params.soap_str_stub
68 | assert " n_max=8 " in soap_params.soap_str_stub
69 | assert " l_max=4 " in soap_params.soap_str_stub
70 |
71 | def test_soap_str_accurate(self):
72 | soap_params = SoapParamPreset.accurate
73 | assert " n_sparse=2000 " in soap_params.soap_str_stub
74 | assert " n_max=12 " in soap_params.soap_str_stub
75 | assert " l_max=6 " in soap_params.soap_str_stub
76 |
77 | @pytest.mark.parametrize(
78 | "soap_params",
79 | [SoapParamPreset.fast, SoapParamPreset.medium, SoapParamPreset.accurate],
80 | )
81 | def test_general_string(self, soap_params: SoapParamPreset):
82 | soap_str = soap_params.soap_str_stub
83 | assert soap_str.startswith("soap")
84 |
85 | soap_dict = key_val_str_to_dict(soap_params.soap_str_stub)
86 |
87 | # fixed keys
88 | for key, value in {
89 | "cutoff": 5.0,
90 | "cutoff_transition_width": 1.0,
91 | "atom_sigma": 0.5,
92 | "add_species": "True",
93 | "covariance_type": "dot_product",
94 | "zeta": 4,
95 | "sparse_method": "cur_points",
96 | }.items():
97 | assert key in soap_dict
98 | assert soap_dict[key] == value
99 |
100 | # non-fixed keys
101 | for key in {"n_max", "l_max", "n_sparse"}:
102 | assert key in soap_dict
103 |
104 |
105 | class TestAdaptiveMethodSettings:
106 | def test_init(self):
107 | AdaptiveMethodSettings(n_min=10, n_max=100, factor=1.6)
108 |
109 | @pytest.mark.parametrize("n_min", [-1, 0])
110 | def test_invalid_n_min(self, n_min):
111 | with raises(
112 | ValueError, match="Adaptive method's minimum interval needs to be positive"
113 | ):
114 | AdaptiveMethodSettings(n_min=n_min, n_max=100, factor=1.6)
115 |
116 | @pytest.mark.parametrize("n_max", [-1, 0, 1])
117 | def test_invalid_n_max(self, n_max):
118 | with raises(
119 | ValueError,
120 | match="Adaptive method's maximum interval needs to be greater than 1",
121 | ):
122 | AdaptiveMethodSettings(n_min=1, n_max=n_max, factor=1.6)
123 |
124 | def test_invalid_min_max(self):
125 | with raises(
126 | ValueError,
127 | match="Adaptive method's maximum interval needs to be greater than it's "
128 | "minimum",
129 | ):
130 | AdaptiveMethodSettings(n_min=10, n_max=9, factor=1.6)
131 |
132 | @pytest.mark.parametrize("factor", [-10.0, 0.0, 1.0])
133 | def test_invalid_factor(self, factor):
134 | with raises(
135 | ValueError,
136 | match="Adaptive method's increment factor needs to be greater than 1",
137 | ):
138 | AdaptiveMethodSettings(n_min=1, n_max=10, factor=factor)
139 |
140 |
141 | class TestRefit:
142 | def test_init(self):
143 | Refit()
144 |
145 | def test_defaults(self):
146 | refit = Refit()
147 |
148 | assert refit.previous_data == []
149 | assert refit.preset_soap_param == SoapParamPreset.fast
150 | assert refit.gp_name == "GAP.xml"
151 | assert refit.default_sigma == "0.005 0.050 0.1 1.0"
152 | assert refit.extra_gap_opts == "sparse_jitter=1.0e-8"
153 | assert refit.extra_gap_opts == "sparse_jitter=1.0e-8"
154 | assert refit.e0_method == "average"
155 |
156 | assert refit.descriptor_str is None
157 | assert refit.e0 is None
158 | assert refit.num_threads is None
159 |
160 | @pytest.mark.parametrize("num_threads", [2, 10, 999])
161 | def test_use_omp_true(self, num_threads):
162 | refit = Refit(num_threads=num_threads)
163 | assert refit.use_omp
164 |
165 | def test_use_omp_none(self):
166 | refit = Refit(num_threads=None)
167 | assert not refit.use_omp
168 |
169 | @pytest.mark.parametrize("num_threads", [-100, 0, 1])
170 | def test_use_omp_false(self, num_threads):
171 | refit = Refit(num_threads=num_threads)
172 | assert not refit.use_omp
173 |
174 |
175 | # -----------------------------------------
176 | # now a couple of tests for the main settings, re-using lots of boilerplate
177 | # so arranged into a class hierarchy
178 |
179 |
180 | class MainSettingsTestCase:
181 | @abstractmethod
182 | @pytest.fixture
183 | def input_file_content(self) -> str:
184 | """return input file (as YAML) and return as string"""
185 |
186 | @pytest.fixture(autouse=True)
187 | def tmp_input_file(self, tmp_path, input_file_content):
188 | """Sets up temporary directory & return written input file name"""
189 |
190 | # go to a temp dir
191 | os.chdir(tmp_path)
192 |
193 | # generate filename
194 | filename = Path(
195 | f"{''.join([random.choice(ascii_letters) for _ in range(10)])}.yaml"
196 | )
197 |
198 | # save content to file
199 | with filename.open(mode="w") as file:
200 | file.write(input_file_content)
201 |
202 | return filename
203 |
204 | @pytest.fixture
205 | def main_setting(self, tmp_input_file):
206 | # already read - use this for valid ones
207 | return MainSettings.read_input(tmp_input_file)
208 |
209 | @abstractmethod
210 | def test_inputs(self, *args):
211 | # implement this for checking what we read
212 | pass
213 |
214 |
215 | class TestMainSettingsDefaults(MainSettingsTestCase):
216 | @pytest.fixture
217 | def input_file_content(self) -> str:
218 | return """
219 | tolerances:
220 | ediff: 0.03 # eV
221 | """
222 |
223 | def test_inputs(self, main_setting):
224 | assert not main_setting.can_update
225 | assert main_setting.check_interval == 1
226 | assert main_setting.num_initial_steps == 0
227 |
228 | assert isinstance(main_setting.tolerances, Tolerances)
229 | assert main_setting.tolerances.ediff == 0.03
230 | assert main_setting.tolerances.fmax is None
231 | assert main_setting.tolerances.frmse is None
232 | assert main_setting.tolerances.vmax is None
233 |
234 | assert main_setting.adaptive_method_parameters is None
235 | assert isinstance(main_setting.refit, Refit)
236 |
237 | assert main_setting.refit.preset_soap_param == SoapParamPreset.fast
238 |
239 |
240 | class TestMainSettingsDefaultsAllSpecified(TestMainSettingsDefaults):
241 | # running the same check as the one inherited from, though here all the
242 | # settings are set with their defaults
243 | @pytest.fixture
244 | def input_file_content(self) -> str:
245 | return """
246 | can_update: false
247 | check_interval: 1
248 | num_initial_steps: 0
249 | tolerances:
250 | ediff: 0.03
251 | fmax: null
252 | frmse: null
253 | vmax: null
254 | refit:
255 | function_name: null
256 | previous_data: []
257 | preset_soap_param: "fast"
258 | gp_name: "GAP.xml"
259 | default_sigma: "0.005 0.050 0.1 1.0"
260 | descriptor_str: null
261 | extra_gap_opts: "sparse_jitter=1.0e-8"
262 | e0: null
263 | e0_method: "average"
264 | num_threads: null
265 | """
266 |
267 |
268 | class TestMainSettingsSimple(MainSettingsTestCase):
269 | @pytest.fixture
270 | def input_file_content(self) -> str:
271 | return """
272 | can_update: true
273 | check_interval: 5
274 | num_initial_steps: 3
275 | tolerances:
276 | ediff: 0.01 # eV
277 | """
278 |
279 | def test_inputs(self, main_setting):
280 | assert main_setting.can_update
281 | assert main_setting.check_interval == 5
282 | assert main_setting.num_initial_steps == 3
283 |
284 | assert isinstance(main_setting.tolerances, Tolerances)
285 | assert main_setting.tolerances.ediff == 0.01
286 |
287 |
288 | class TestMainSettingsAdaptive(MainSettingsTestCase):
289 | @pytest.fixture
290 | def input_file_content(self) -> str:
291 | return """
292 | can_update: true
293 | check_interval: 10
294 | tolerances:
295 | ediff: 0.01 # eV
296 | adaptive_method_parameters:
297 | n_min: 10
298 | n_max: 5000
299 | factor: 1.3
300 | """
301 |
302 | def test_inputs(self, main_setting):
303 | assert main_setting.can_update
304 | assert main_setting.check_interval == 10
305 | assert main_setting.num_initial_steps == 0
306 |
307 | assert isinstance(main_setting.tolerances, Tolerances)
308 | assert main_setting.tolerances.ediff == 0.01
309 |
310 | assert isinstance(
311 | main_setting.adaptive_method_parameters, AdaptiveMethodSettings
312 | )
313 |
314 | assert main_setting.adaptive_method_parameters.n_min == 10
315 | assert main_setting.adaptive_method_parameters.n_max == 5000
316 | assert main_setting.adaptive_method_parameters.factor == 1.3
317 |
318 |
319 | class TestMainSettingsRefit(MainSettingsTestCase):
320 | @pytest.fixture
321 | def input_file_content(self) -> str:
322 | return """
323 | tolerances:
324 | ediff: 0.01 # eV
325 | refit:
326 | function_name: null
327 | previous_data: [
328 | "filename1.xyz",
329 | "filename2.xyz",
330 | ]
331 | preset_soap_param: "fast"
332 | gp_name: "GAP888.xml"
333 | default_sigma: "0.01 0.09 1.5 1.09"
334 | descriptor_str: "Hello, this is a descriptor string"
335 | extra_gap_opts: "some extra GAP settings with numbers like 4.0"
336 | e0: "explicit E0 value"
337 | e0_method: "e0 method, any string OK here - GAP will need to validate"
338 | num_threads: 100
339 | """
340 |
341 | def test_inputs(self, main_setting):
342 | # same as otherwise
343 | assert not main_setting.can_update
344 | assert main_setting.check_interval == 1
345 | assert main_setting.num_initial_steps == 0
346 | assert isinstance(main_setting.tolerances, Tolerances)
347 | assert main_setting.tolerances.ediff == 0.01
348 |
349 | # refit settings
350 | refit = main_setting.refit
351 | assert refit.gp_name == "GAP888.xml"
352 | assert refit.previous_data == [
353 | "filename1.xyz",
354 | "filename2.xyz",
355 | ]
356 | assert refit.default_sigma == "0.01 0.09 1.5 1.09"
357 | assert refit.descriptor_str == "Hello, this is a descriptor string"
358 | assert refit.extra_gap_opts == "some extra GAP settings with numbers like 4.0"
359 | assert refit.e0 == "explicit E0 value"
360 | assert (
361 | refit.e0_method
362 | == "e0 method, any string OK here - GAP will need to validate"
363 | )
364 | assert refit.num_threads == 100
365 |
--------------------------------------------------------------------------------
/meson.build:
--------------------------------------------------------------------------------
1 | GAP_F90_sources = [
2 | 'clustering.F90',
3 | 'descriptors.F90',
4 | 'descriptors_wrapper.F90',
5 | 'find_water_triplets_noncommercial.F90',
6 | 'gp_fit.F90',
7 | 'gp_predict.F90',
8 | 'make_permutations_noncommercial_v2.F90',
9 | 'soap_turbo.f90',
10 | 'soap_turbo_angular.f90',
11 | 'soap_turbo_compress.f90',
12 | 'soap_turbo_functions.f90',
13 | 'soap_turbo_radial.f90',
14 | ]
15 |
16 | GAP = library('GAP',
17 | GAP_F90_sources,
18 | dependencies: [
19 | blas_dep,
20 | mpi_dep,
21 | ],
22 | link_with : [libAtoms,fox],
23 | )
24 |
--------------------------------------------------------------------------------
/soap_turbo.f90:
--------------------------------------------------------------------------------
1 | soap_turbo/src/soap_turbo.f90
--------------------------------------------------------------------------------
/soap_turbo_angular.f90:
--------------------------------------------------------------------------------
1 | soap_turbo/src/soap_turbo_angular.f90
--------------------------------------------------------------------------------
/soap_turbo_compress.f90:
--------------------------------------------------------------------------------
1 | soap_turbo/src/soap_turbo_compress.f90
--------------------------------------------------------------------------------
/soap_turbo_functions.f90:
--------------------------------------------------------------------------------
1 | soap_turbo/src/soap_turbo_functions.f90
--------------------------------------------------------------------------------
/soap_turbo_radial.f90:
--------------------------------------------------------------------------------
1 | soap_turbo/src/soap_turbo_radial.f90
--------------------------------------------------------------------------------
/tensor_reduction_docs.md:
--------------------------------------------------------------------------------
1 | ## SOAP Compression
2 |
3 | The length of the standard SOAP vector (powerspectrum)
4 |
5 | $$p^{\alpha\beta}_{nn'l} = \sum_m c^\alpha_{nlm} c^\beta_{n'lm} = \bf{c}^\alpha_{nl} \cdot \bf{c}^\beta_{n'l}$$
6 |
7 | is $\frac{1}{2}NS(NS+1)(L+1) + 1$ where $N$=`n_max`, $S$=`n_species`, $L$=`l_max` and the final element is `covariance_sigma0` (set after normalisation). Various compression strategies are available to reduce this $\mathcal{O}(N^2S^2)$ scaling.
8 |
9 | ## Tensor-reduction
10 |
11 | The tensor-reduction introudced in [Tensor-reduced atomic density representations](https://doi.org/10.48550/arXiv.2210.01705) can be applied by first selecting which "density channels" (radial, species or both) are mixed together before selecting how the mixed channels should be coupled together (tensor product, element-wise). Tensor decomposition using radial-species mixing with element-wise coupling is written as
12 |
13 | $$p_{kl} = \sum_m c_{klm} c_{klm} = \bf{c}_{kl} \cdot \bf{c}_{kl} $$
14 |
15 | where
16 | $$c_{klm} = \sum_{\alpha n} W^k_{\alpha n} c^\alpha_{nlm} $$
17 |
18 |
19 | | Keyword | Values | Description |
20 | | ----------- | ------------- | ----------- |
21 | | `R_mix` | `T` or `F` | mixes the radial channels |
22 | | `Z_mix` | `T` or `F` | mixes the species channels |
23 | | `sym_mix` | `T` or `F` | `T` means use "tensor-decomposition" whereas `F` means use "tensor-sketching" as described in the original [article](https://doi.org/10.48550/arXiv.2210.01705) |
24 | | `K` | int > 0 | How many mixed channels to create |
25 | | `coupling` | `T` or `F` | `T` means use tensor product coupling across mixed channels, `F` means use element-wise|
26 |
27 | Note that full tensor product coupling is always used across any "un-mixed" channels.
28 |
29 | Examples
30 | - `R_mix=T Z_mix=T K=5 sym_mix=T coupling=F` Means randomly mix the radial and species channels together to form 5 new channels then couple each of these channels to itself only. Length of final vector will be $K(L+1)$. If `coupling=T` then length would be $\frac{1}{2}K(K+1)(L+1)$ as each channel would be coupled to every other channel (per `l`).
31 | - `R_mix=F Z_mix=T K=5 sym_mix=F coupling=F` Means randomly mix the species channels together so that there are `n_max*K` channels. Final length will be $\frac{1}{2}N(N+1)K(L+1)$ from element-wise coupling between the `K` mixed species channels and full tensor-product coupling between the radial channels.
32 |
33 | ## Radial and species sensitive correlation orders
34 |
35 | The radial and species sensitive correlation orders can be set independently using
36 |
37 | | Keyword | Values | Description |
38 | | ----------- | -------- | ----------- |
39 | | `nu_R` | `0, 1, 2` | radially sensitive correlation order |
40 | | `nu_S` | `0, 1, 2` | species senstitive correlation order |
41 |
42 | The schematic below illustrates the physical interpretation and includes a correspondance between the `nu_R nu_S` notation used here and that used in the original article [Compressing local atomic neighbouhood descriptors ](https://www.nature.com/articles/s41524-022-00847-y).
43 |
44 |
45 |
46 |
47 |
48 | ## Other
49 |
50 | - Setting `diagonal_radial=T` only includes terms where $n=n'$ so that the length becomes $\frac{1}{2}NS(S+1)(L+1) + 1$
51 | - Different species can be grouped together using the `Z_map` keyword where commas separate groups of elements e.g. `Z_map={8, 23 41 42}` treats all the metals as identical and Oxygen as distinct. As the power spectrum is correlation order 2 it is also possible to specify two distinct groupings separated by a colon e.g. `Z_map={8, 23, 41, 42: 8, 23 41 42}` where in the first density every element is distinct whilst in the second again the metals are all treated as identical but Oxygen is distinct.
52 |
--------------------------------------------------------------------------------
/translation_table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/libAtoms/GAP/51e038baa66bcc6a1902ea94c884f4383c8b2931/translation_table.png
--------------------------------------------------------------------------------