├── .gitignore ├── .readthedocs.yaml ├── CITATION.cff ├── DFT_ref.bib ├── DFT_worksheet.pdf ├── LICENSE ├── README.md ├── docs ├── Makefile ├── requirements.txt └── source │ ├── DFT_ref.bib │ ├── NB1_functions.rst │ ├── NB2_functions.rst │ ├── NB3_functions.rst │ ├── citation.rst │ ├── conf.py │ ├── figures │ ├── 1D_hartree.png │ ├── 321_wavefunction.png │ ├── 512_prob_den.png │ ├── GGA_exch_plot.png │ ├── LDA_cor.png │ ├── LDA_exch.png │ ├── arrow.png │ ├── connect.png │ ├── connected.png │ ├── copy_drive_location.png │ ├── copy_to_drive.png │ ├── den_compare_blue.png │ ├── density_comparison.png │ ├── grid_density.png │ ├── heptacene.png │ ├── heptacene_HOMO3.png │ ├── heptacene_diagram.png │ ├── heptacene_scf.png │ ├── runtime1.png │ ├── runtime2.png │ ├── show_code.png │ ├── show_code_anim.gif │ ├── show_code_button.png │ └── wavefunction_anim.gif │ ├── index.rst │ ├── other_resources.rst │ └── running_code.rst ├── figures ├── NB1_wavefunction.png ├── NB2_anthracene.png ├── NB3_density.png └── graphical_abstract.png ├── notebooks ├── NB1_3D_PIB.ipynb ├── NB2_PAH_HF.ipynb ├── NB3_DFT_PIB.ipynb ├── NB3_DFT_PIB_calculator.ipynb ├── notebook_functions │ ├── NB1_functions.py │ ├── NB2_functions.py │ ├── NB3_functions.py │ └── __init__.py ├── old_NB3_DFT_PIB.ipynb └── old_NB3_DFT_PIB_calculator.ipynb └── offline_jupyter ├── README.md ├── offline_NB1_3D_PIB.ipynb ├── offline_NB2_PAH_HF.ipynb ├── offline_NB3_DFT_PIB.ipynb ├── old_ipv_compatible ├── info.txt ├── offline_NB1_3D_PIB.ipynb ├── offline_NB2_PAH_HF.ipynb ├── offline_NB3_DFT_PIB.ipynb ├── offline_NB3_DFT_PIB_calculator.ipynb └── requirements.txt └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Sphinx Documentation 2 | docs/build/ 3 | 4 | # Python 5 | notebooks/notebook_functions/__pycache__ 6 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.12" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: docs/source/conf.py 17 | 18 | # We recommend specifying your dependencies to enable reproducible builds: 19 | # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 20 | python: 21 | install: 22 | - requirements: docs/requirements.txt 23 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: Opening the Density Functional Theory Black Box 3 | type: software 4 | authors: 5 | - given-names: Jacob S. 6 | family-names: Hirschi 7 | affiliation: Oregon State University 8 | email: hirschij@oregonstate.edu 9 | orcid: 'https://orcid.org/0009-0004-3327-8671' 10 | - given-names: Dayana 11 | family-names: Bashirova 12 | affiliation: Oregon State University 13 | orcid: 'https://orcid.org/0009-0006-2283-717X' 14 | - given-names: Tim J. 15 | family-names: Zuehsldorff 16 | affiliation: Oregon State University 17 | orcid: 'https://orcid.org/0000-0002-6960-756X' 18 | identifiers: 19 | - type: doi 20 | value: 10.1021/acs.jchemed.3c00535 21 | repository-code: 'https://github.com/tjz21/DFT_PIB_Code' 22 | abstract: >- 23 | Interactive Jupyter Notebooks for learning the 24 | fundamentals of Density-Functional Theory (DFT). 25 | keywords: 26 | - quantum chemistry 27 | - density-functional theory 28 | - computational chemistry 29 | - open educational resource 30 | - electronic structure 31 | license: MIT 32 | date-released: '2023-10-16' 33 | preferred-citation: 34 | authors: 35 | - given-names: Jacob S. 36 | family-names: Hirschi 37 | affiliation: Oregon State University 38 | email: hirschij@oregonstate.edu 39 | orcid: 'https://orcid.org/0009-0004-3327-8671' 40 | - given-names: Dayana 41 | family-names: Bashirova 42 | affiliation: Oregon State University 43 | orcid: 'https://orcid.org/0009-0006-2283-717X' 44 | - given-names: Tim J. 45 | family-names: Zuehsldorff 46 | affiliation: Oregon State University 47 | orcid: 'https://orcid.org/0000-0002-6960-756X' 48 | date-published: 2023-10-16 49 | doi: 10.1021/acs.jchemed.3c00535 50 | issue: 11 51 | journal: Journal of Chemical Education 52 | publisher: 53 | name: American Chemical Society 54 | start: 4496 55 | title: "Opening the Density Functional Theory Black 56 | Box: A Collection of Pedagogic Jupyter Notebooks" 57 | type: article 58 | url: "https://pubs.acs.org/doi/10.1021/acs.jchemed.3c00535" 59 | volume: 100 60 | title: "Opening the Density Functional Theory Black 61 | Box: A Collection of Pedagogic Jupyter Notebooks" 62 | -------------------------------------------------------------------------------- /DFT_ref.bib: -------------------------------------------------------------------------------- 1 | @article{DFTBlackBox, 2 | author = {Hirschi, Jacob S. and Bashirova, Dayana and Zuehlsdorff, Tim J.}, 3 | title = {Opening the Density Functional Theory Black Box: A Collection of Pedagogic Jupyter Notebooks}, 4 | journal = {J. Chem. Educ.}, 5 | volume = {100}, 6 | number = {11}, 7 | pages = {4496-4503}, 8 | year = {2023}, 9 | doi = {10.1021/acs.jchemed.3c00535}, 10 | URL = {https://doi.org/10.1021/acs.jchemed.3c00535}, 11 | } 12 | -------------------------------------------------------------------------------- /DFT_worksheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/DFT_worksheet.pdf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-2024 Tim J. Zuehlsdorff 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Opening the Density-Functional Theory Black Box 2 | 3 | 4 | 5 | --- 6 | 7 | > 'It is nice to know that the computer understands the problem. But I would like to understand it too.' 8 | > 9 | > \- **Eugene Wigner** 10 | 11 | --- 12 | 13 | [![Documentation Status](https://readthedocs.org/projects/dft-pib-code/badge/?version=latest)](https://dft-pib-code.readthedocs.io/en/latest/?badge=latest) 14 | [![Python 3.10](https://img.shields.io/badge/python-3.10-blue.svg)](https://www.python.org/) 15 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 16 | [![Latest Release](https://img.shields.io/github/v/release/tjz21/DFT_PIB_Code)](https://github.com/tjz21/DFT_PIB_Code/releases/latest) 17 | [![ChemRxiv](http://img.shields.io/badge/ChemRxiv-10.26434/chemrxiv--2023--jfgcl-EEEA62.svg)](http://dx.doi.org/10.26434/chemrxiv-2023-jfgcl) 18 | [![DOI:<10.1021/acs.jchemed.3c00535>](http://img.shields.io/badge/JCE_Paper-10.1021/acs.jchemed.3c00535-blue.svg)](http://dx.doi.org/10.1021/acs.jchemed.3c00535) 19 | 20 | This repository contains three Google Colab notebooks that are designed to facilitate understanding of Density-Functional Theory (DFT) through interactive visualizations. Our motivation for developing this software stems from the knowledge deficiency that is often produced from using DFT as a black box in commercial software. By applying DFT to the familiar particle in a box model system employing a real-space grid basis, we hope to have reduced DFT to its fundamental essence fit for pedagogy. Brief instructions for executing the code are provided at the beginning of each notebook and a [problem sheet](https://github.com/tjz21/DFT_PIB_Code/blob/main/DFT_worksheet.pdf) for getting started is attached. The notebooks can be accessed without any installation through Google Colab by simply clicking on the links and signing in with a Google account (offline alternative is provided [here](offline_jupyter/README.md)). Python programming knowledge is not required, and docs are hosted through [ReadTheDocs](https://dft-pib-code.readthedocs.io/en/latest/). 21 |
22 |
23 |
24 | 25 | 26 | 27 | ## Notebook 1–Particle in a 3D Box 28 | 29 | In this notebook, we’ll consider the particle in a three-dimensional box system treated in any undergraduate physical chemistry textbook. High-quality energy level diagrams and isosurface renderings of the wavefunction can be generated from user-specified box lengths. Depicted here is the 321 state of an anthracene-like box of dimensions 16 x 8 x 3 Bohr. 30 |
31 |
32 | Click here to open the notebook in Google Colab: 33 | 34 |
35 | 36 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/tjz21/DFT_PIB_Code/blob/main/notebooks/NB1_3D_PIB.ipynb) 37 | 38 |
39 |
40 |
41 |
42 |
43 | 44 | 45 | ## Notebook 2–PAH Frontier Orbitals 46 | 47 | Next, we’ll look at a real chemical system in the form of polycyclic aromatic hydrocarbons (PAHs). We can perform Hartree-Fock/STO-3G calculations to find the shapes and energies of their frontier molecular orbitals, which can make for interesting comparisons with the analogous results from Notebook 1. 48 |
49 |
50 | 51 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/tjz21/DFT_PIB_Code/blob/main/notebooks/NB2_PAH_HF.ipynb) 52 | 53 |
54 |
55 | 56 | ## Notebook 3–Density-Functional Theory 57 | 58 | Finally, we’ll reconsider the system from Notebook 1, but now we’ll turn on electron-electron interaction through the Kohn-Sham potential. We’ll consider each term of the single-particle Hamiltonian and put everything together into a self-consistent field (SCF) DFT calculation. We can then analyze the how the density and eigeneneriges change as a function of SCF iteration number. LDA and PBE are the available exchange-correlation functionals.
59 |
60 | Full theory notebook: 61 | 62 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/tjz21/DFT_PIB_Code/blob/main/notebooks/NB3_DFT_PIB.ipynb) 63 | 64 |
65 | Abbreviated notebook with the DFT calculator and analysis tools: 66 | 67 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/tjz21/DFT_PIB_Code/blob/main/notebooks/NB3_DFT_PIB_calculator.ipynb) 68 | 69 |
70 | 71 | --- 72 | 73 | ### Citation 74 | If you would like to cite this work, please refer to the following publication ([BibTeX](DFT_ref.bib)): 75 | 76 | > Hirschi, J. S.; Bashirova, D.; Zuehlsdorff, T. J. 77 | > Opening the Density Functional Theory Black Box: A Collection of Pedagogic Jupyter Notebooks. 78 | > *J. Chem. Educ.* 79 | > **2023**, 80 | > *100* (11), 4496-4503. https://doi.org/10.1021/acs.jchemed.3c00535 81 | -------------------------------------------------------------------------------- /docs/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 = source 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 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinxcontrib-images 2 | sphinx-rtd-theme 3 | sphinx_design 4 | -------------------------------------------------------------------------------- /docs/source/DFT_ref.bib: -------------------------------------------------------------------------------- 1 | @article{DFTBlackBox, 2 | author = {Hirschi, Jacob S. and Bashirova, Dayana and Zuehlsdorff, Tim J.}, 3 | title = {Opening the Density Functional Theory Black Box: A Collection of Pedagogic Jupyter Notebooks}, 4 | journal = {J. Chem. Educ.}, 5 | volume = {100}, 6 | number = {11}, 7 | pages = {4496-4503}, 8 | year = {2023}, 9 | doi = {10.1021/acs.jchemed.3c00535}, 10 | URL = {https://doi.org/10.1021/acs.jchemed.3c00535}, 11 | } 12 | -------------------------------------------------------------------------------- /docs/source/NB1_functions.rst: -------------------------------------------------------------------------------- 1 | Notebook 1 Functions 2 | ==================== 3 | 4 | Below are the functions pertaining to the analytic solutions to the 3D PIB system used in `Notebook 1 `_. 5 | 6 | 7 | 8 | .. autosummary:: 9 | 10 | NB1_functions.psi_reg 11 | NB1_functions.psi_ener 12 | NB1_functions.markdown_wvfn 13 | NB1_functions.markdown_ener 14 | NB1_functions.isoplotter 15 | 16 | .. automodule:: NB1_functions 17 | :members: 18 | :member-order: bysource 19 | -------------------------------------------------------------------------------- /docs/source/NB2_functions.rst: -------------------------------------------------------------------------------- 1 | Notebook 2 Functions 2 | ==================== 3 | 4 | Below are the functions used for the geometry optimization, visualization, and electronic structure calculation of PAH molecules featured in `Notebook 2 `_. 5 | 6 | 7 | 8 | .. autosummary:: 9 | 10 | NB2_functions.FF_optimization 11 | NB2_functions.run_calculation 12 | NB2_functions.get_HL_gap 13 | NB2_functions.elec_properties 14 | NB2_functions.mo_diagram 15 | NB2_functions.draw_orbital 16 | 17 | .. automodule:: NB2_functions 18 | :members: 19 | :member-order: bysource 20 | -------------------------------------------------------------------------------- /docs/source/NB3_functions.rst: -------------------------------------------------------------------------------- 1 | Notebook 3 Functions 2 | ==================== 3 | 4 | See below for the functions needed to build the visualizations and DFT calculator in `Notebook 3 `_. 5 | 6 | .. autosummary:: 7 | 8 | NB3_functions.edge_cleaner 9 | NB3_functions.integ_3d 10 | NB3_functions.norm_psi_and_den 11 | NB3_functions.noninter_kin_e 12 | NB3_functions.grid_density 13 | NB3_functions.exch_equation 14 | NB3_functions.exch_pot_eq 15 | NB3_functions.LDA_c_display 16 | NB3_functions.GGAx_pot_eq 17 | NB3_functions.hartree_plotter 18 | NB3_functions.hamiltonian_display 19 | NB3_functions.energy_plot 20 | NB3_functions.hard_walls 21 | NB3_functions.hartree 22 | NB3_functions.lda_exchange 23 | NB3_functions.lda_correlation 24 | NB3_functions.RDG 25 | NB3_functions.pbe_exchange 26 | NB3_functions.cor_den_grad 27 | NB3_functions.pbe_correlation 28 | 29 | .. automodule:: NB3_functions 30 | :members: 31 | :member-order: bysource 32 | -------------------------------------------------------------------------------- /docs/source/citation.rst: -------------------------------------------------------------------------------- 1 | Citation 2 | ======== 3 | This work was published in the `*Journal of Chemical Education* `_. 4 | 5 | :Download .bib: :download:`bibtex` 6 | 7 | :: 8 | 9 | @article{DFTBlackBox, 10 | author = {Hirschi, Jacob S. and Bashirova, Dayana and Zuehlsdorff, Tim J.}, 11 | title = {Opening the Density Functional Theory Black Box: A Collection of Pedagogic Jupyter Notebooks}, 12 | journal = {J. Chem. Educ.}, 13 | volume = {100}, 14 | number = {11}, 15 | pages = {4496-4503}, 16 | year = {2023}, 17 | doi = {10.1021/acs.jchemed.3c00535}, 18 | URL = {https://doi.org/10.1021/acs.jchemed.3c00535} 19 | } 20 | 21 | -------------------------------------------------------------------------------- /docs/source/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 | import os 10 | import sys 11 | sys.path.insert(0, os.path.abspath('../../notebooks/notebook_functions')) 12 | 13 | project = 'DFT-PIB Docs' 14 | copyright = '2024, Zuehlsdorff Group' 15 | author = 'Zuehlsdorff Group' 16 | html_scaled_image_link = False 17 | 18 | 19 | # -- General configuration --------------------------------------------------- 20 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 21 | 22 | extensions = [ 'sphinx.ext.autodoc', 23 | 'sphinx.ext.doctest', 24 | 'sphinx.ext.autosummary', 25 | 'sphinx.ext.intersphinx', 26 | 'sphinx_design', 27 | #'sphinxcontrib.images', # disabled until they fix it 28 | ] 29 | 30 | templates_path = ['_templates'] 31 | exclude_patterns = [] 32 | 33 | 34 | 35 | # -- Options for HTML output ------------------------------------------------- 36 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 37 | 38 | html_theme = 'sphinx_rtd_theme' 39 | html_static_path = ['_static'] 40 | html_logo = 'figures/den_compare_blue.png' 41 | -------------------------------------------------------------------------------- /docs/source/figures/1D_hartree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/1D_hartree.png -------------------------------------------------------------------------------- /docs/source/figures/321_wavefunction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/321_wavefunction.png -------------------------------------------------------------------------------- /docs/source/figures/512_prob_den.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/512_prob_den.png -------------------------------------------------------------------------------- /docs/source/figures/GGA_exch_plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/GGA_exch_plot.png -------------------------------------------------------------------------------- /docs/source/figures/LDA_cor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/LDA_cor.png -------------------------------------------------------------------------------- /docs/source/figures/LDA_exch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/LDA_exch.png -------------------------------------------------------------------------------- /docs/source/figures/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/arrow.png -------------------------------------------------------------------------------- /docs/source/figures/connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/connect.png -------------------------------------------------------------------------------- /docs/source/figures/connected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/connected.png -------------------------------------------------------------------------------- /docs/source/figures/copy_drive_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/copy_drive_location.png -------------------------------------------------------------------------------- /docs/source/figures/copy_to_drive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/copy_to_drive.png -------------------------------------------------------------------------------- /docs/source/figures/den_compare_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/den_compare_blue.png -------------------------------------------------------------------------------- /docs/source/figures/density_comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/density_comparison.png -------------------------------------------------------------------------------- /docs/source/figures/grid_density.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/grid_density.png -------------------------------------------------------------------------------- /docs/source/figures/heptacene.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/heptacene.png -------------------------------------------------------------------------------- /docs/source/figures/heptacene_HOMO3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/heptacene_HOMO3.png -------------------------------------------------------------------------------- /docs/source/figures/heptacene_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/heptacene_diagram.png -------------------------------------------------------------------------------- /docs/source/figures/heptacene_scf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/heptacene_scf.png -------------------------------------------------------------------------------- /docs/source/figures/runtime1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/runtime1.png -------------------------------------------------------------------------------- /docs/source/figures/runtime2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/runtime2.png -------------------------------------------------------------------------------- /docs/source/figures/show_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/show_code.png -------------------------------------------------------------------------------- /docs/source/figures/show_code_anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/show_code_anim.gif -------------------------------------------------------------------------------- /docs/source/figures/show_code_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/show_code_button.png -------------------------------------------------------------------------------- /docs/source/figures/wavefunction_anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/docs/source/figures/wavefunction_anim.gif -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | Opening the DFT Black Box Documentation 2 | ======================================= 3 | 4 | :: 5 | 6 | -------------------------------------------------------------------------------- 7 | 8 | ________ ________ _________ ________ ___ ________ 9 | |\ ___ \|\ _____\\___ ___\ |\ __ \|\ \|\ __ \ 10 | \ \ \_|\ \ \ \__/\|___ \ \_|____________\ \ \|\ \ \ \ \ \|\ /_ 11 | \ \ \ \\ \ \ __\ \ \ \|\____________\ \ ____\ \ \ \ __ \ 12 | \ \ \_\\ \ \ \_| \ \ \|____________|\ \ \___|\ \ \ \ \|\ \ 13 | \ \_______\ \__\ \ \__\ \ \__\ \ \__\ \_______\ 14 | \|_______|\|__| \|__| \|__| \|__|\|_______| 15 | 16 | Software developed by Jacob Hirschi, Dayana Bashirova, and Tim Zuehlsdorff 17 | Oregon State University, Corvallis, OR 97331, USA 18 | 19 | -------------------------------------------------------------------------------- 20 | 21 | 22 | General Information 23 | ------------------- 24 | 25 | .. image:: https://readthedocs.org/projects/dft-pib-code/badge/?version=latest 26 | :target: https://dft-pib-code.readthedocs.io/en/latest/?badge=latest 27 | :alt: Documentation Status 28 | 29 | .. image:: https://img.shields.io/badge/python-3.10-blue.svg 30 | :target: https://www.python.org/ 31 | :alt: Python version 32 | 33 | .. image:: https://img.shields.io/badge/License-MIT-yellow.svg 34 | :target: https://opensource.org/licenses/MIT 35 | :alt: License 36 | 37 | .. image:: https://img.shields.io/github/v/release/tjz21/DFT_PIB_Code 38 | :target: https://github.com/tjz21/DFT_PIB_Code/releases/latest 39 | :alt: Code version 40 | 41 | .. image:: http://img.shields.io/badge/ChemRxiv-10.26434/chemrxiv--2023--jfgcl-EEEA62.svg 42 | :target: http://dx.doi.org/10.26434/chemrxiv-2023-jfgcl 43 | :alt: Preprint link 44 | 45 | .. image:: http://img.shields.io/badge/Paper_DOI-10.1021/acs.jchemed.3c00535-blue.svg 46 | :target: http://dx.doi.org/10.1021/acs.jchemed.3c00535 47 | :alt: Paper link 48 | 49 | Welcome to the documentation site for the educational software DFT-PIB, which applies density-functional theory to the 3D particle in a box model system. The three Google Colab Notebooks are accessible from the `GitHub repo `_ with the `problem worksheet `_, and the instructions are mostly self-contained. 50 | 51 | For completeness, we've also attached a :ref:`short guide ` for executing the code here and a page with other great :ref:`learning resources `. Although the code is not really intended to be used as an API, we've included descriptions of the main functions used in each notebook herein with some examples. 52 | 53 | **Have fun learning about DFT!** 54 | 55 | ---- 56 | 57 | Content 58 | ------- 59 | 60 | .. toctree:: 61 | :maxdepth: 1 62 | :caption: Getting Started 63 | 64 | running_code 65 | 66 | .. toctree:: 67 | :maxdepth: 1 68 | :caption: API-style docs 69 | 70 | NB1_functions 71 | NB2_functions 72 | NB3_functions 73 | 74 | .. toctree:: 75 | :maxdepth: 1 76 | :caption: Other Info/Links 77 | 78 | other_resources 79 | citation 80 | GitHub Repo Link 81 | -------------------------------------------------------------------------------- /docs/source/other_resources.rst: -------------------------------------------------------------------------------- 1 | .. _other-resources: 2 | 3 | Further Resources 4 | ================= 5 | 6 | Here is a curated list of useful pedagogic resources for learning more about DFT and related topics: 7 | 8 | * `eChem `_: Digital textbook addressing advanced topics in electronic structure. 9 | * `DFT by hand `_: Exercises that involve performing DFT calculations by hand on a simplified two-electron He atom system. 10 | * `python_1d_dft `_: GitHub repository with Jupyter Notebooks that considers DFT in the context of a 1D PIB. 11 | * `DFT with Slater functions `_: Jupyter Notebooks that calculate the DFT total energy of H2 with Slater orbitals. 12 | * `DFT MOOC `_: 25-hour online course delivered by Coursera on DFT. 13 | * `The ABC of DFT `_: Textbook pdf by Prof. Kieron Burke with great explanations and pen-and-paper exercises. 14 | * `HF with Excel `_: Microsoft Excel Spreadsheet with VBA Macros that perform HF/STO-nG calculations on H2. 15 | * `Basis sets with Excel `_: Excel sheets with interactive visualizations of gaussian- and slater-type basis sets. 16 | -------------------------------------------------------------------------------- /docs/source/running_code.rst: -------------------------------------------------------------------------------- 1 | .. _short-guide: 2 | 3 | Running the Code 4 | ================ 5 | 6 | Colab Notebook Code Blocks 7 | -------------------------- 8 | In order to run the code in the cloud using Colab, you'll need to first connect to a runtime. You can connect by either (1) clicking the |connect| button in the upper right or (2) trying to run any cell by clicking the |arrow| button alongside it: 9 | 10 | .. |connect| image:: figures/connect.png 11 | :scale: 30 % 12 | 13 | .. |arrow| image:: figures/arrow.png 14 | :scale: 30 % 15 | 16 | .. image:: figures/runtime1.png 17 | 18 | ---- 19 | 20 | .. image:: figures/runtime2.png 21 | 22 | Once the green checkmark appears |checkmark| , the code blocks can be executed or pressing SHIFT+ENTER. 23 | 24 | .. |checkmark| image:: figures/connected.png 25 | :scale: 50 % 26 | 27 | .. image:: figures/wavefunction_anim.gif 28 | 29 | .. note:: 30 | The GUI widgets become active only after executing the cell. 31 | 32 | Viewing the Code 33 | ---------------- 34 | If you would like to view the source code, click the |show-code| button near the top of any cell to reveal the Python code behind each animation: 35 | 36 | .. |show-code| image:: figures/show_code_button.png 37 | :scale: 25 % 38 | 39 | .. image:: figures/show_code_anim.gif 40 | 41 | Saving Changes 42 | -------------- 43 | Keep in mind that any cloud files created or changes made to the source code are lost when the runtime is disconnected. If you would like save your work, save a copy to your Google Drive account with the |drive-copy| button on the top menu (alternatively File -> Save a copy in Drive). 44 | 45 | .. |drive-copy| image:: figures/copy_to_drive.png 46 | :scale: 25 % 47 | 48 | .. image:: figures/copy_drive_location.png 49 | 50 | -------------------------------------------------------------------------------- /figures/NB1_wavefunction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/figures/NB1_wavefunction.png -------------------------------------------------------------------------------- /figures/NB2_anthracene.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/figures/NB2_anthracene.png -------------------------------------------------------------------------------- /figures/NB3_density.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/figures/NB3_density.png -------------------------------------------------------------------------------- /figures/graphical_abstract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/figures/graphical_abstract.png -------------------------------------------------------------------------------- /notebooks/notebook_functions/NB1_functions.py: -------------------------------------------------------------------------------- 1 | # Functions used in Colab Notebook 1 (https://colab.research.google.com/github/tjz21/DFT_PIB_Code/blob/main/notebooks/NB1_3D_PIB.ipynb) 2 | 3 | def psi_reg(x, y, z, q_nx=1, q_ny=1, q_nz=1, lx=1, ly=1, lz=1): 4 | '''Numeric representation of the normalized 3D PIB wavefunction. 5 | 6 | :param float x: Cartesian spatial x variable. 7 | :param float y: Idem for y. 8 | :param float z: Idem for z. 9 | :param int q_nx: Quantum number specifying the state along x. 10 | :param int q_ny: Idem for y. 11 | :param int q_nz: Idem for z. 12 | :param int lx: Box dimension along x in Bohr. 13 | :param int ly: Idem for y. 14 | :param int lz: Idem for z. 15 | 16 | :return: Wavefunction evaluated at a point or array if input x, y, z are arrays. 17 | 18 | Example: 19 | 20 | >>> # value of the wavefunction at the center of a 3x3x3 box 21 | >>> # in the state 111 22 | >>> psi_reg(1.5, 1.5, 1.5, 1, 1, 1, 3, 3, 3) 23 | 0.5443310539518174 24 | ''' 25 | wvfn = (np.sqrt(8 / (lx * ly * lz)) * np.sin((q_nx * np.pi * x) / lx) * np.sin((q_ny * np.pi * y) / ly) * np.sin((q_nz * np.pi * z) / lz) ) 26 | return wvfn 27 | 28 | def psi_ener(qnx, qny, qnz, lx, ly, lz): 29 | '''Calculates energy of 3D PIB state. 30 | 31 | :param int qnx: Quantum number specifying the state along x. 32 | :param int qny: Idem for y. 33 | :param int qnz: Idem for z. 34 | :param int lx: Box dimension along x in Bohr. 35 | :param int ly: Idem for y. 36 | :param int lz: Idem for z. 37 | 38 | :return: Float energy value in Ha. 39 | 40 | Example: 41 | 42 | >>> # Energy of 111 state of an 8x8x3 box 43 | >>> psi_ener(1, 1, 1, 8, 8, 3) 44 | 0.702524 45 | ''' 46 | e_level = (4*np.pi**2/8)*((qnx/lx)**2 + (qny/ly)**2 + (qnz/lz)**2) 47 | return np.round(e_level, decimals=6) 48 | 49 | def markdown_wvfn(q_nx, q_ny, q_nz, lx, ly, lz): 50 | '''Displays LaTeX equation of 3D wavefunction. 51 | 52 | :param int q_nx: Quantum number specifying the state along x. 53 | :param int q_ny: Idem for y. 54 | :param int q_nz: Idem for z. 55 | :param int lx: Box dimension along x in Bohr. 56 | :param int ly: Idem for y. 57 | :param int lz: Idem for z. 58 | 59 | Example: 60 | 61 | >>> # Wavefunction of 321 state in 8x8x3 box 62 | >>> markdown_wvfn(3, 2, 1, 8, 8, 3) 63 | 64 | .. math:: 65 | 66 | \\text{State:}\ \ \psi_{3,2,1}(x,y,z)=\sqrt{\\frac{8}{(8)(8)(3)}}\\sin\\bigg(\\frac{3\pi x}{8}\\bigg)\sin\\bigg(\\frac{2\pi y}{8}\\bigg)\sin\\bigg(\\frac{1\pi z}{3}\\bigg) 67 | 68 | ''' 69 | subscript = f'{q_nx},{q_ny},{q_nz}' 70 | sqrt_denom = f'({lx})({ly})({lz})' 71 | display(Markdown('## $$ \\text{State: \ \ } \psi_{' + subscript + '}(x,y,z)=' + 72 | '\sqrt{\\frac{8}{' + sqrt_denom + '}}' + 73 | '\sin{\\Big(\\frac{' + f'{q_nx}' '\pi x}{' + f'{lx}' + '}\\Big)}' + 74 | '\sin{\\Big(\\frac{' + f'{q_ny}' '\pi y}{' + f'{ly}' + '}\\Big)}' + 75 | '\sin{\\Big(\\frac{' + f'{q_nz}' '\pi z}{' + f'{lz}' + '}\\Big)} \\newline $$')) 76 | 77 | def markdown_ener(l_x, l_y, l_z): 78 | '''Displays LaTeX equation of energy. 79 | 80 | :param int l_x: Box dimension along x in Bohr. 81 | :param int l_y: Idem for y. 82 | :param int l_z: Idem for z. 83 | 84 | Example: 85 | 86 | >>> # Energy expression for a 16x8x3 Box 87 | >>> markdown_ener(16, 8, 3) 88 | 89 | .. math:: 90 | 91 | E_{n_x,n_y,n_z}= \\frac{\pi^2}{2}\\bigg[\\bigg(\\frac{n_x}{16}\\bigg)^2 + \\bigg(\\frac{n_y}{8}\\bigg)^2 +\\bigg(\\frac{n_z}{3}\\bigg)^2\\bigg] 92 | 93 | ''' 94 | display(Markdown('## $$ E_{n_x,n_y,n_z} = \\frac{\pi^2}{2} \\Bigl[ ' + 95 | ' \\Bigl(\\frac{n_x}{' + f'{l_x}' + '}\\Bigl)^2 +' + 96 | ' \\Bigl(\\frac{n_y}{' + f'{l_y}' + '}\\Bigl)^2 +' + 97 | ' \\Bigl(\\frac{n_z}{' + f'{l_z}' + '}\\Bigl)^2\\Bigl] \\newline$$')) 98 | 99 | def isoplotter(nx_val, ny_val, nz_val, lx, ly, lz, psi_square=False, plot_save=True, plotting_lib='plotly'): 100 | '''Displays and saves isosurface of 3D PIB wavefunction. 101 | 102 | :param int nx_val: Quantum number specifying the state along x. 103 | :param int ny_val: Idem for y. 104 | :param int nz_val: Idem for z. 105 | :param int lx: Box dimension along x in Bohr. 106 | :param int ly: Idem for y. 107 | :param int lz: Idem for z. 108 | :param bool psi_square: calculate prob. density (true) or wavefunction (false) 109 | :param bool plot_save: save a .png file of display 110 | :param str plotting_lib: 3d plotting library to use (plotly or ipyvol) 111 | 112 | Example: 113 | 114 | >>> # Render the 321 state of a 8x8x3 PIB system 115 | >>> isoplotter(3, 2, 1, 8, 8, 3, psi_square=False, plot_save=False, plotting_lib='plotly') 116 | 117 | .. image:: figures/321_wavefunction.png 118 | 119 | >>> # Render the 512 probability density of a 16x8x3 PIB system 120 | >>> isoplotter(5, 1, 2, 16, 8, 3, psi_square=True, plot_save=False, plotting_lib='plotly') 121 | 122 | .. image:: figures/512_prob_den.png 123 | 124 | ''' 125 | # construct 3d grid of points 126 | nx_p, ny_p, nz_p = 7 * lx, 7 * ly, 7 * lz 127 | xp, yp, zp = np.linspace(0, lx, nx_p), np.linspace(0, ly, ny_p), np.linspace(0, lz, nz_p) 128 | X, Y, Z = np.meshgrid(xp, yp, zp, indexing='ij') 129 | psi = psi_reg(X, Y, Z, nx_val, ny_val, nz_val, lx, ly, lz) 130 | norm_psi = psi_reg(X, Y, Z, nx_val, ny_val, nz_val, lx, ly, lz)**2 131 | 132 | # ipyvolume potting commands 133 | if plotting_lib == 'ipyvol': 134 | ipv.clear() 135 | fig = ipv.figure(title='PIB',width=500, height=500) 136 | fig.camera.type = 'OrthographicCamera' 137 | if psi_square: 138 | norm_sur = ipv.pylab.plot_isosurface(norm_psi, color='red', level=norm_psi.mean(), controls=True, 139 | description='prob. density') 140 | else: 141 | pos_values = np.ma.array(psi, mask = psi < 0.0) 142 | if nx_val == ny_val == nz_val == 1: 143 | pos_sur = ipv.pylab.plot_isosurface(psi,color='red', level=np.sqrt(norm_psi.mean()), controls=True, 144 | description='positive') 145 | else: 146 | pos_sur = ipv.pylab.plot_isosurface(psi, color='red', level=np.sqrt(norm_psi.mean()), controls=True, 147 | description='positive') 148 | neg_sur = ipv.pylab.plot_isosurface(psi, color='blue', level=-np.sqrt(norm_psi.mean()), controls=True, 149 | description='negative') 150 | ipv.style.box_off() 151 | ipv.squarelim() 152 | ipv.view(0,-75) 153 | ipv.xyzlabel('lx', 'ly', 'lz') 154 | ipv.show() 155 | 156 | # plotly potting commands 157 | elif plotting_lib == 'plotly': 158 | global figure 159 | if psi_square: 160 | den_isoval = norm_psi.mean() 161 | figure = go.Figure(data=go.Isosurface( 162 | x=X.flatten(), 163 | y=Y.flatten(), 164 | z=Z.flatten(), 165 | value=norm_psi.flatten(), 166 | colorscale='BlueRed', 167 | isomin=-den_isoval, 168 | isomax=den_isoval, 169 | surface_count=2, 170 | showscale=False, 171 | caps=dict(x_show=False, y_show=False, z_show=False) 172 | )) 173 | figure.update_layout(scene = dict( 174 | xaxis_title='x', 175 | yaxis_title='y', 176 | zaxis_title='z', 177 | aspectmode='data'), 178 | width=800, 179 | height=500, 180 | title_text=f'Prob. Den., isovalue = {den_isoval:.4f}') 181 | figure.show() 182 | else: 183 | wfn_isoval = np.sqrt(norm_psi.mean()) 184 | figure = go.Figure(data=go.Isosurface( 185 | x=X.flatten(), 186 | y=Y.flatten(), 187 | z=Z.flatten(), 188 | value=psi.flatten(), 189 | colorscale='BlueRed', 190 | isomin=-wfn_isoval, 191 | isomax=wfn_isoval, 192 | surface_count=2, 193 | showscale=False, 194 | caps=dict(x_show=False, y_show=False, z_show=False) 195 | )) 196 | figure.update_layout(scene = dict( 197 | xaxis_title='x', 198 | yaxis_title='y', 199 | zaxis_title='z', 200 | aspectmode='data'), 201 | width=800, 202 | height=500, 203 | title_text=f'Wavefunction, isovalue = {wfn_isoval:.4f}') 204 | figure.show() 205 | 206 | def plot_saver(b): 207 | if lib_dropdown.value == 'ipyvol': 208 | ipv.savefig(f'{filename_text_ipv.value}.png', width=1200, height=1200) 209 | elif lib_dropdown.value == 'plotly': 210 | #plotly.offline.plot(figure, filename='test.html') 211 | figure.write_image(f'{filename_text_ipv.value}.png', width=800, height=800) 212 | 213 | 214 | -------------------------------------------------------------------------------- /notebooks/notebook_functions/NB2_functions.py: -------------------------------------------------------------------------------- 1 | # Functions used in Colab Notebook 2 (https://colab.research.google.com/github/tjz21/DFT_PIB_Code/blob/main/notebooks/NB2_PAH_HF.ipynb) 2 | 3 | 4 | def FF_optimization(molecule_num:int): 5 | '''Performs a MMFF94 optimization of a PAH and displays 3D rendering of result. 6 | 7 | :param int molecule_num: Number corresponding to a PAH from the menu. 8 | 9 | PAH Options: 10 | 11 | .. list-table:: 12 | :widths: 10 15 5 20 15 15 13 | :header-rows: 1 14 | 15 | * - **Table Index** 16 | - **PAH** 17 | - **Rings** 18 | - **Dimensions (Bohr)** 19 | - **pi electrons** 20 | - **Fuse-type** 21 | * - 1 22 | - benzene 23 | - 1 24 | - 8 x 8 x 3 25 | - 6 26 | - linear 27 | * - 2 28 | - naphthalene 29 | - 2 30 | - 12 x 8 x 3 31 | - 10 32 | - linear 33 | * - 3 34 | - anthracene 35 | - 3 36 | - 16 x 8 x 3 37 | - 14 38 | - linear 39 | * - 4 40 | - tetracene 41 | - 4 42 | - 20 x 8 x 3 43 | - 18 44 | - linear 45 | * - 5 46 | - pentacene 47 | - 5 48 | - 24 x 8 x 3 49 | - 22 50 | - linear 51 | * - 6 52 | - hexacene 53 | - 6 54 | - 28 x 8 x 3 55 | - 26 56 | - linear 57 | * - 7 58 | - heptacene 59 | - 7 60 | - 32 x 8 x 3 61 | - 30 62 | - linear 63 | * - --- 64 | - --- 65 | - --- 66 | - --- 67 | - --- 68 | - --- 69 | * - 8 70 | - pyrene 71 | - 4 72 | - 16 x 12 x 3 73 | - 16 74 | - non-linear 75 | * - 9 76 | - perylene 77 | - 5 78 | - 20 x 12 x 3 79 | - 20 80 | - non-linear 81 | * - 10 82 | - coronene 83 | - 7 84 | - 20 x 16 x 3 85 | - 24 86 | - non-linear 87 | 88 | Example: 89 | 90 | >>> # Perform a geometry optimization of heptacene 91 | >>> FF_optimization(7) 92 | 93 | .. image:: figures/heptacene.png 94 | :scale: 70 % 95 | :align: center 96 | 97 | ''' 98 | global molecule_smiles, molecule_smiles_copy # global for the subsequent code block 99 | molecule_smiles = [] # list of tuples of rdkit object (from Smiles) and molecule name 100 | cyclobut = Chem.MolFromSmiles("c1ccc1"), 'cylobut' 101 | ring1 = Chem.MolFromSmiles("c1ccccc1"), 'Benzene' 102 | ring2 = Chem.MolFromSmiles("c1c2ccccc2ccc1"), 'Naphthalene' 103 | ring3 = Chem.MolFromSmiles("c1ccc2cc3ccccc3cc2c1"), 'Anthracene' 104 | ring4 = Chem.MolFromSmiles("c1c2cc3cc4ccccc4cc3cc2ccc1"), 'Tetracene' 105 | ring5 = Chem.MolFromSmiles("c1ccc2cc3cc4cc5ccccc5cc4cc3cc2c1"), 'Pentacene' 106 | ring6 = Chem.MolFromSmiles("c1c2cc3cc4cc5cc6ccccc6cc5cc4cc3cc2ccc1"), 'Hexacene' 107 | ring7 = Chem.MolFromSmiles("C1=CC=C2C=C3C=C4C=C5C=C6C=C7C=CC=CC7=CC6=CC5=CC4=CC3=CC2=C1"), 'Heptacene' 108 | ring4a = Chem.MolFromSmiles("c1cc2cccc3c2c4c1cccc4cc3"), 'Pyrene' 109 | ring5a = Chem.MolFromSmiles("c1ccc5cccc4c5c1c2cccc3cccc4c23"), 'Perylene' 110 | ring7a = Chem.MolFromSmiles("c1cc2ccc3ccc4ccc5ccc6ccc1c7c2c3c4c5c67"), 'Coronene' 111 | molecule_smiles.append(cyclobut) 112 | molecule_smiles.append(ring1) 113 | molecule_smiles.append(ring2) 114 | molecule_smiles.append(ring3) 115 | molecule_smiles.append(ring4) 116 | molecule_smiles.append(ring5) 117 | molecule_smiles.append(ring6) 118 | molecule_smiles.append(ring7) 119 | molecule_smiles.append(ring4a) 120 | molecule_smiles.append(ring5a) 121 | molecule_smiles.append(ring7a) 122 | molecule_smiles_copy = molecule_smiles.copy() 123 | 124 | molecule = molecule_smiles[molecule_num][0] # user-selected molecule from the list 125 | global mol, xyz_geom 126 | mol = Chem.Mol(molecule) 127 | mol = AllChem.AddHs(mol, addCoords=True) 128 | AllChem.EmbedMolecule(mol) 129 | AllChem.MMFFOptimizeMolecule(mol) # uses MMFF94 Force Field to optimize structure 130 | 131 | # format structure into readable xyz coordinates 132 | xyz_geom = [] 133 | for lines in Chem.MolToXYZBlock(mol).split("\n")[2:]: 134 | strip = lines.strip() 135 | if strip: 136 | xyz_geom.append(strip) 137 | xyz_geom = "\n".join(xyz_geom) 138 | 139 | # render 3D ball-and-stick model of structure 140 | display(Markdown('
')) 141 | display(Markdown(' MMFF94 Optimized Geometry')) 142 | view = py3Dmol.view( 143 | data=Chem.MolToMolBlock(mol), 144 | style={"stick": {}, "sphere": {"scale": 0.3}}, 145 | width=400, 146 | height=300, 147 | ) 148 | view.zoomTo() 149 | view.show() 150 | 151 | def run_calculation(xyz_struc, basis="STO-3G"): 152 | '''Performs a HF/STO-3G single-point calculation on a PAH structure. Energy results are displayed in a Markdown table. 153 | 154 | :param str xyz_struc: geometry of PAH in xyz format 155 | :param str basis: basis set; see options: https://pyscf.org/_modules/pyscf/gto/basis.html 156 | 157 | Heptacene Example: 158 | 159 | .. image:: figures/heptacene_scf.png 160 | 161 | ''' 162 | clear_output(wait=True) 163 | display(widgets.VBox([top_panel, output])) 164 | global mf, mol_quantum, molecule_index, molecule_name 165 | # create pyscf object from input structure 166 | mol_quantum = gto.M(atom=xyz_struc, basis=basis, unit="ANG", symmetry=False) 167 | mol_quantum.build(); 168 | molecule_index = PAH_d_options[PAH_dropdown.value-1][1] 169 | molecule_name = PAH_d_options[PAH_dropdown.value-1][0].split('-')[1] 170 | print(f"Peforming HF/STO-3G single-point caculation on {molecule_name}...") 171 | mf = scf.RHF(mol_quantum).run(verbose=0) 172 | print("SCF Converged!") 173 | mos = mf.mo_coeff 174 | elec_ener = mf.energy_elec()[0] # electronic energy 175 | nuc_ener = mf.energy_nuc() # nuclear " 176 | total_ener = mf.energy_tot() # total " 177 | # Markdown table display: 178 | display(Markdown('
')) 179 | display(Markdown(f'''Component|Energy (Ha) 180 | :---:|:---: 181 | electronic | {elec_ener:.6f} 182 | nuclear | {nuc_ener:.6f} 183 | **total** | {total_ener:.6f} 184 | ''')) 185 | display(Markdown('
')) 186 | 187 | def get_HL_gap(mf): 188 | '''Finds HOMO-LUMO gap of molecule from HF/STO-3G calculation result. 189 | 190 | :param pyscf.scf.RHF mf: pyscf HF calculation object 191 | :return: Float representing HL gap in electron-volts. 192 | 193 | ''' 194 | global homo_num, lumo_num 195 | homo_num = np.count_nonzero(mf.mo_occ == 2) - 1 # HOMO index 196 | lumo_num = np.count_nonzero(mf.mo_occ == 2) # LUMO index 197 | homo_en = mf.mo_energy[homo_num] # HOMO energy 198 | mos_occ = mf.mo_coeff[:,:lumo_num] 199 | lumo_en = mf.mo_energy[lumo_num] # LUMO energy 200 | mos_unocc = mf.mo_coeff[:,lumo_num:] 201 | hl_gap = abs(lumo_en - homo_en) 202 | hl_gap_ev = hl_gap * 27.2114 # 1 Ha = 27.2114 eV conversion 203 | return hl_gap_ev 204 | 205 | def elec_properties(b): 206 | '''Prints dipole moment, HL gap, and Mulliken charges from HF calculation result. 207 | 208 | :param b: Button click 209 | 210 | ''' 211 | HL_gap = get_HL_gap(mf) 212 | capture_output = mf.dip_moment(verbose=3) 213 | print(f"HOMO-LUMO Gap (eV): {HL_gap:.4f}") 214 | print("===============================") 215 | capture = mf.mulliken_pop(verbose=3) 216 | for atom in mol.GetAtoms(): 217 | atom.SetProp('molAtomMapNumber', str(atom.GetIdx()+1)) 218 | display(mol) 219 | properties_button.disabled = True 220 | 221 | def click_scf(b): 222 | run_calculation(xyz_geom) 223 | display(Markdown('---')) 224 | display(properties_button) 225 | display(Markdown('
')) 226 | scf_button.disabled = True 227 | properties_button.disabled = False 228 | 229 | def mo_diagram(mf, show_diagram, show_table, savefig, filename): 230 | '''Displays MO energy level diagram and dataframe. 231 | 232 | :param pyscf.scf.RHF mf: pyscf HF calculation object. 233 | :param bool show_diagram: Display matplotlib energy diagram. 234 | :param bool show_table: Display same data as Pandas dataframe. 235 | :param bool savefig: Save figure as .png file. 236 | :param str filename: filename to save .png file to (w/o file extension). 237 | 238 | Example: 239 | 240 | .. image:: figures/heptacene_diagram.png 241 | :scale: 70 % 242 | 243 | ''' 244 | # Find HOMO-LUMO gap 245 | homo_num = np.count_nonzero(mf.mo_occ == 2) - 1 246 | lumo_num = np.count_nonzero(mf.mo_occ == 2) 247 | homo_en = mf.mo_energy[homo_num] 248 | mos_occ = mf.mo_coeff[:,:lumo_num] 249 | lumo_en = mf.mo_energy[lumo_num] 250 | mos_unocc = mf.mo_coeff[:,lumo_num:] 251 | hl_gap = abs(lumo_en - homo_en) 252 | hl_gap_ev = hl_gap * 27.2114 253 | 254 | # Create a list MO labels (i.e. HOMO, HOMO-1, etc.) 255 | MO_labels = [] 256 | for i in range(len(mf.mo_occ)-lumo_num): 257 | if i == 0: 258 | MO_labels.append('LUMO') 259 | else: 260 | MO_labels.append(f'LUMO+{i}') 261 | MO_labels.reverse() 262 | for i in range(homo_num+1): 263 | if i == 0 : 264 | MO_labels.append('HOMO') 265 | else: 266 | MO_labels.append(f'HOMO-{i}') 267 | MO_labels.reverse() 268 | 269 | table = pd.DataFrame({"En (Ha)": mf.mo_energy, "Occ.": mf.mo_occ.astype('int'), "Type": MO_labels}) 270 | table.index.name = 'MO #' 271 | 272 | global frontier_nums, frontier_labels 273 | frontier_nums = np.arange(homo_num-4,lumo_num+5,1) 274 | frontier_labels = table.iloc[frontier_nums]['Type'].tolist() 275 | energies = table.iloc[homo_num-4:lumo_num+5][::-1]['En (Ha)'] 276 | 277 | MO_labels2 = [] 278 | # Create a list MO labels (i.e. HOMO, HOMO-1, etc.) 279 | for i in range(len(mf.mo_occ)-lumo_num): 280 | MO_labels2.append(f'LUMO+{i}') 281 | MO_labels2.reverse() 282 | for i in range(homo_num+1): 283 | MO_labels2.append(f'HOMO-{i}') 284 | MO_labels2.reverse() 285 | 286 | # pandas dataframe for the matplotlib diagram 287 | table2 = pd.DataFrame({"En (Ha)": mf.mo_energy, "Occ.": mf.mo_occ.astype('int'), "Type": MO_labels2}) 288 | datat = table2.values.tolist() 289 | 290 | y = [] # energy values 291 | orb = 5 292 | for i in datat: 293 | if int(i[2][5:]) < orb: 294 | y.append(round(i[0],4)) 295 | x = [1]*len(y) 296 | for i in range(0,len(y)): 297 | if y[i] in y[:i]: 298 | count = list(y[:i]).count(y[i]) 299 | x[i] += count*0.3 # x value offset for degenerate orbitals (avoids overlap) 300 | 301 | # Building up the diagram 302 | fig = plt.figure(figsize=(7, 6)) 303 | ax = fig.add_subplot(1, 1, 1) 304 | plt.ylabel("Energy (Ha)", labelpad=7) 305 | plt.scatter(x[orb:], y[orb:], marker=0, s=1200, linewidths=4, color='#F97306', label='virtual') 306 | plt.scatter(x[:orb], y[:orb], marker=0, s=1200, linewidths=4, color='green', label='occupied') 307 | annotations = [] 308 | annotations2 = [] 309 | for i in range(len(x)): 310 | if i < orb: 311 | if i != 0: 312 | annotations.append('HOMO-' + str(i)) 313 | annotations2.append('LUMO+' + str(i)) 314 | else: 315 | annotations.append('HOMO') 316 | annotations2.append('LUMO') 317 | all_annotations = annotations[::-1]+annotations2 318 | for i, label in enumerate(all_annotations): 319 | if list(y).count(y[i])==1: 320 | plt.annotate(label, (x[i] + 0.005, y[i]-0.01), size=8) 321 | plt.text(x[i]-0.42, y[i]-0.01, "{:.3f}".format(y[i]), size=8) 322 | else: 323 | plt.annotate(label, (x[i] - 0.20, y[i] + 0.015), size=8) 324 | if y[i] not in y[:i]: 325 | plt.text(x[i]-0.42, y[i]-0.01, "{:.3f}".format(y[i]), size=8) 326 | plt.rcParams["legend.markerscale"] = 0.50 327 | plt.legend(loc='upper right', handletextpad=-0.3) 328 | plt.title(f'{molecule_name} Frontier Orbitals') 329 | plt.xticks([]) 330 | plt.xlim([0.3, 3]) 331 | plt.ylim([min(y) - 0.1, max(y) + 0.1]) 332 | 333 | # display HOMO-LUMO gap value 334 | plt.text(0.80, 0.730, f'H-L Gap: {hl_gap:.3f} Ha\n ({hl_gap_ev:.3f} ev)', 335 | size=15, horizontalalignment='center', verticalalignment='center', 336 | transform=ax.transAxes) 337 | 338 | # adding the stick molecule 339 | mol_copy = molecule_smiles_copy[molecule_index][0] 340 | mol_copy_pil = MolToImage(mol_copy) 341 | imagebox = OffsetImage(mol_copy_pil, zoom=0.5, interpolation='bicubic') 342 | imagebox.image.axes = ax 343 | ab = AnnotationBbox(imagebox, (0.5,0.53), 344 | xybox=(120., -80.), 345 | xycoords='figure fraction', 346 | boxcoords="offset points", 347 | frameon=False) 348 | ax.add_artist(ab) 349 | plt.tight_layout() 350 | 351 | # save/display control events 352 | if savefig == True: 353 | plt.savefig(f'{filename}.png', dpi=800) 354 | if show_diagram == True: 355 | plt.show() 356 | elif show_diagram == False: 357 | plt.close() 358 | display(Markdown(' ')) 359 | if show_table == True: 360 | display(table.iloc[homo_num-4:lumo_num+5][::-1]) 361 | 362 | def on_click_save(b): 363 | mo_diagram(mf_mo, show_diagram=False, show_table=False, savefig=True, filename=filename_text_mpl.value) 364 | 365 | def get_orbital(i, label): 366 | '''Saves a .cube file of orbital and prob. density from the above calculation. 367 | 368 | :param int i: index to iterate over in for loop 369 | :param str label: MO label, i.e. HOMO, HOMO-1, etc 370 | 371 | ''' 372 | tools.cubegen.orbital(mol_quantum, f'{label}.cube', mf.mo_coeff[:,i],nx=80,ny=80,nz=80) 373 | square_cube = cube_tools.cube(f'{label}.cube') 374 | pos = np.ma.array(square_cube.data, mask = square_cube.data < 0) 375 | good_isovalues.append([f'{label}',pos.mean()]) 376 | square_cube.square_cube(power=2) 377 | pos = np.ma.array(square_cube.data, mask = square_cube.data < 0) 378 | good_isovalues.append([f'{label}_square',pos.mean()]) 379 | square_cube.write_cube(f'{label}_square.cube') 380 | 381 | def draw_orbital(label, psi_square=False, show_noninteractive_png=False): 382 | '''Renders isosurface of selected orbital from HF/STO-3G calculation. 383 | 384 | :param str label: orbital designation, e.g. HOMO, HOMO-1, etc 385 | :param bool psi_square: show orbital (False) or probability density (True) 386 | :param bool show_noninteractive_png: render a static image (True) or interactive 3D display (False) 387 | 388 | Example: 389 | 390 | >>> # Display probability density of HOMO-3 orbital of heptacene (calculated in a previous cell) 391 | >>> draw_orbital('HOMO-3', psi_square=True, show_noninteractive_png=False) 392 | 393 | .. image:: figures/heptacene_HOMO3.png 394 | :scale: 55 % 395 | :align: center 396 | 397 | ''' 398 | view = py3Dmol.view(width=500,height=500) 399 | if psi_square: 400 | index = int(np.where(good_isovalues == f'{label}_square')[0]) 401 | isoval = float(good_isovalues[index][1]) 402 | print('-----------------------------------------') 403 | print(f'isovalue: {isoval:.4f}') 404 | print('rendering...') 405 | view.addVolumetricData(grid_data[f'{label}_square'], "cube", {'isoval': isoval, 'color': "red", 'opacity': 0.90}) 406 | else: 407 | with open(f'{label}.cube') as f: 408 | cube_data = f.read() 409 | index = int(np.where(good_isovalues == f'{label}')[0]) 410 | isoval = float(good_isovalues[index][1])*2 411 | print('----------------------------------------') 412 | print(f'isovalue: {isoval:.4f}') 413 | print('rendering...') 414 | view.addVolumetricData(grid_data[f'{label}'], "cube", {'isoval': isoval, 'color': "red", 'opacity': 0.90}) 415 | view.addVolumetricData(grid_data[f'{label}'], "cube", {'isoval': -isoval, 'color': "blue", 'opacity': 0.90}) 416 | view.addModel(Chem.MolToMolBlock(mol), 'mol') 417 | view.setStyle({'stick':{}, 'sphere': {"scale":0.3}}) 418 | if show_noninteractive_png: 419 | view.zoomTo() 420 | view.show() 421 | view.png() 422 | else: 423 | view.zoomTo() 424 | view.show() 425 | -------------------------------------------------------------------------------- /notebooks/notebook_functions/NB3_functions.py: -------------------------------------------------------------------------------- 1 | # Functions used in Colab Notebook 3 (https://colab.research.google.com/github/tjz21/DFT_PIB_Code/blob/main/notebooks/NB3_DFT_PIB.ipynb) 2 | 3 | def edge_cleaner(func_3d, nx, ny, nz, num_edges=1): 4 | '''Sets outermost layer (or two) of grid values of a 3D function to zero. 5 | 6 | This is needed for the density because of the discontinuities in the numeric 2nd derivatives at the box edges. 7 | 8 | :param np.array func_3d: Flattened 3D grid of points. 9 | :param int nx: Number of grid points in x-direction. 10 | :param int ny: Idem for y. 11 | :param int nz: Idem for z. 12 | :param int num_edges: Number of border layers to set to zero (either 1 or 2). 13 | :return: A flattened array of grid points. 14 | ''' 15 | func_3d = func_3d.reshape(nx, ny, nz) 16 | if num_edges == 1: 17 | func_3d[0,:,:] = 0 18 | func_3d[-1,:,:] = 0 19 | func_3d[:,0,:] = 0 20 | func_3d[:,-1,:] = 0 21 | func_3d[:,:,0] = 0 22 | func_3d[:,:,-1] = 0 23 | elif num_edges == 2: 24 | func_3d[0,:,:] = 0 25 | func_3d[-1,:,:] = 0 26 | func_3d[:,0,:] = 0 27 | func_3d[:,-1,:] = 0 28 | func_3d[:,:,0] = 0 29 | func_3d[:,:,-1] = 0 30 | func_3d[1,:,:] = 0 31 | func_3d[-2,:,:] = 0 32 | func_3d[:,1,:] = 0 33 | func_3d[:,-2,:] = 0 34 | func_3d[:,:,1] = 0 35 | func_3d[:,:,-2] = 0 36 | return func_3d.flatten() 37 | 38 | def integ_3d(func_3d, dx, dy, dz): 39 | '''Integrates a 3D function over all defined space. 40 | 41 | :param np.array func_3d: 3D grid of points 42 | :param float dx: Differential volume element in x-direction. 43 | :param float dy: Idem for y. 44 | :param float dz: Idem for z. 45 | :return: Integrated 3D function as scalar value. 46 | 47 | ''' 48 | return np.sum(func_3d * dx * dy * dz) 49 | 50 | def norm_psi_and_den(e_vecs, occ_states, dx, dy, dz): 51 | '''Normalizes raw eigenvectors from the solver and finds electron density. 52 | 53 | :param np.array e_vecs: Array of eigenvectors from the eigenvalue solver. 54 | :param int occ_states: Number of occupied KS states (i.e. # electrons / 2). 55 | :param float dx: Differential volume element in x-direction. 56 | :param float dy: Idem for y. 57 | :param float dz: Idem for z. 58 | 59 | :return: 60 | - **norm_psi** (*np.array*): Array of normalized eigenvectors. 61 | - **el_den** (*np.array*): Electron density as array. 62 | ''' 63 | norm_psi = np.zeros_like(e_vecs) 64 | el_den = np.zeros_like(e_vecs[:,0]) 65 | for i in range(e_vecs.shape[1]): 66 | norm_psi[:,i] = e_vecs[:,i]/np.sqrt(integ_3d(e_vecs[:,i]**2, dx, dy, dz)) 67 | for i in range(occ_states): 68 | el_den += 2* norm_psi[:,i]**2 69 | return norm_psi, el_den 70 | 71 | def noninter_kin_e(norm_eigenvecs, occ_states, kin_mat, dx, dy, dz, nx, ny, nz): 72 | '''Finds noninteracting KS kinetic energy. 73 | 74 | :param np.array norm_eigenvecs: array of normalized eigenvectors as columns/rows 75 | :param int occ_states: number of occupied KS states (i.e. # electrons / 2) 76 | :param scipy.sparse.sparray kin_mat: kinetic energy operator as sparse matrix 77 | :param float dx: Differential volume element in x-direction. 78 | :param float dy: Idem for y. 79 | :param float dz: Idem for z. 80 | :param int nx: Number of grid points in each x-direction. 81 | :param int ny: Idem for y. 82 | :param int nz: Idem for z. 83 | :return: Scalar energy value in Ha. 84 | ''' 85 | kin_energy_values = [] 86 | for eig in norm_eigenvecs.T[:occ_states]: 87 | inner_prod = eig*kin_mat.dot(eig) 88 | inner_prod = edge_cleaner(inner_prod, nx, ny, nz, num_edges=1) 89 | orbital_k_en = integ_3d(inner_prod, dx, dy, dz) 90 | kin_energy_values.append(orbital_k_en) 91 | return sum(kin_energy_values) 92 | 93 | def grid_density(l_x, l_y, l_z, plotting_lib='plotly'): 94 | '''Generates 3D scatter plot representing grid for DFT calculations. 95 | 96 | :param int l_x: Box length in x-direction. 97 | :param int l_y: Idem for y. 98 | :param int l_z: Idem for z. 99 | 100 | Example: 101 | 102 | >>> # View numerical grid for a 16x8x3 Box 103 | >>> grid_density(16, 8, 3, plotting_lib='plotly') 104 | 105 | .. image:: figures/grid_density.png 106 | :align: center 107 | :scale: 60 % 108 | 109 | ''' 110 | nx, ny, nz = (5 * l_x), (5 * l_y), (5 * l_z) 111 | gpoints = (nx * ny * nz) 112 | xp, yp, zp = np.linspace(0, l_x, nx), np.linspace(0, l_y, ny), np.linspace(0, l_z, nz) 113 | X, Y, Z = np.meshgrid(xp, yp, zp, indexing='ij') 114 | label = '###
Grid Points (5 pts/bohr): ' 115 | label += '$x_p \\times y_p \\times z_p =' 116 | label += f'{nx}' + '\\times' + f'{ny}' + '\\times' + f'{nz}' + ' = '+ f'{gpoints}$
' 117 | display(Markdown(label)) 118 | if plotting_lib == 'ipyvol': 119 | fig1 = ipv.figure(title='PIB',width=450, height=450) 120 | fig1.camera.type = 'OrthographicCamera' 121 | ipv.quickscatter(X.flatten(), Y.flatten(), Z.flatten(), size=0.5, marker='box',description='grid points') 122 | ipv.squarelim() 123 | ipv.style.box_off() 124 | ipv.show() 125 | elif plotting_lib == 'plotly': 126 | scatter = go.Figure(data=[go.Scatter3d(x=X.flatten(), y=Y.flatten(), z=Z.flatten(), 127 | mode='markers', 128 | marker=dict(size=2,color='red',symbol='square',opacity=0.5))]) 129 | scatter.update_layout(scene = dict(xaxis_title='x', yaxis_title='y', zaxis_title='z', 130 | aspectmode='data'), width=900, height=500) 131 | scatter.show() 132 | 133 | def exch_equation(den_number): 134 | '''Displays LaTeX equation of LDA exchange potential. 135 | 136 | :param float den_number: Value of electron density, n(r), at a specific position.. 137 | 138 | Example: 139 | 140 | >>> # equation for electronic exchange at a point with a density of 0.50 e/Bohr**3 141 | >>> exch_equation(0.50) 142 | 143 | .. math:: 144 | 145 | v_{\\text{X, n(r)=0.50}}^{\\text{LDA}}= - \\frac{3}{4} \left(\\frac{3}{\pi}\\right)^{1/3} (0.50)^{1/3} = -0.586\ \\text{Ha/e} 146 | 147 | ''' 148 | exch_pot_number = np.round(-(3/4)*(3/np.pi)**(1/3)*den_number**(1/3), decimals=3) 149 | text = Markdown('## $v_{\\text{X},\ n(r)= \ ' + f'{den_number:.2f}' + '}^{\\text{LDA}} = -\\frac{3}{4}' + 150 | '\left(\\frac{3}{\pi}\\right)^{1/3}' + 151 | f'({den_number:.2f})' + '^{1/3}' + 152 | f'={exch_pot_number:.3f}\ ' + '\\text{Ha/e}$') 153 | display(text) 154 | clear_output(wait=True) 155 | 156 | def exch_pot_eq(den_number): 157 | '''Displays line plot of LDA exchange with scatterpoint at density value. 158 | 159 | :param float den_number: Value of electron density, n(r). 160 | 161 | Example: 162 | 163 | >>> # plot for lda electronic exchange with a point at a density of 0.50 e/Bohr**3 164 | >>> exch_pot_eq(0.50) 165 | 166 | .. image:: figures/LDA_exch.png 167 | 168 | ''' 169 | exch_pot_number = np.round(-(3/4)*(3/np.pi)**(1/3)*den_number**(1/3), decimals=3) 170 | display(Markdown('
')) 171 | x = np.linspace(0, 10, 300) 172 | y = np.round(-(3/4)*(3/np.pi)**(1/3)*x**(1/3), decimals=3) 173 | fig = plt.figure() 174 | ax = fig.add_subplot(1, 1, 1) 175 | plt.cla() 176 | plt.clf() 177 | clear_output(wait=True) 178 | plt.plot(x, y, label=r'Slater exchange') 179 | plt.hlines(exch_pot_number, 0, den_number, colors='black') 180 | plt.vlines(den_number, -2, exch_pot_number, colors='black') 181 | plt.scatter(den_number, exch_pot_number, marker="s", color='red', label='grid point') 182 | plt.title('LDA Exchange Potential', size=15) 183 | plt.xlabel('Density, $n(r)$', size=15) 184 | plt.ylabel('Potential, $v_{\mathrm{X}}^{\mathrm{LDA}}(n(r))$', size=15) 185 | plt.xlim(0,2) 186 | plt.ylim(-1.25,0) 187 | plt.rcParams["legend.markerscale"] = 1.5 188 | plt.legend(loc='upper right', fontsize=15) 189 | plt.show() 190 | 191 | def LDA_c_display(ws_radius): 192 | '''Displays line plot of Chachiyo LDA correlation potential with a scatterpoint and LaTeX equation at the Wigner-Seitz radius (r_s) value. 193 | 194 | :param float ws_radius: Wigner-Seitz radius (inversely related to density). 195 | 196 | Example: 197 | 198 | >>> # display equation and diagram for LDA correlation with point @ r_s = 2.00 199 | >>> LDA_c_display(2.00) 200 | 201 | .. math:: 202 | 203 | v_{\\text{C}, r_s=2.00}^{\\text{LDA}}=a\ln\left(1+\\frac{b}{(2.00)}+\\frac{b}{(2.00)^2}\\right) = -0.059\ \\text{Ha/e} 204 | 205 | .. image:: figures/LDA_cor.png 206 | 207 | ''' 208 | # a, b, c fitting constants used in paper: 10.1063/1.4958669 209 | a, b, c = (np.log(2)-1)/(2*np.pi**2), 20.4562557, (4*np.pi/3)**(1/3) 210 | lda_expression = lambda rad: a*np.log(1 + b*c*1/(rad) + b*(c**2)*1/(rad)) 211 | cor_pot_number = lda_expression(ws_radius) 212 | 213 | # LaTeX Equation 214 | text = Markdown('## $v_{\\text{C},\ r_s=' + f'{ws_radius:.2f}' + '}^{\\text{LDA}}' + '= a\cdot \\text{ln}\left(1+\\frac{b}{' + 215 | f'({ws_radius:.2f})' + '} + \\frac{b}{' + f'({ws_radius:.2f})^2' + 216 | '}\\right)=' + f'{cor_pot_number:.3f}\ ' + '\\text{Ha/e}$') 217 | display(text) 218 | 219 | # Matplotlib Diagram 220 | x = np.linspace(0.001, 10, 100) 221 | y = lda_expression(x) 222 | plt.plot(x, y, label='Chachiyo Correlation') 223 | plt.scatter(ws_radius, cor_pot_number, color='red', marker="s", label='grid point') 224 | plt.hlines(cor_pot_number, 0, ws_radius, colors='black') 225 | plt.vlines(ws_radius, -2, cor_pot_number, colors='black') 226 | plt.title('LDA Correlation Potential', size=15) 227 | plt.xlabel('Wigner-Seitz Radius, $r_s$', size=15) 228 | plt.ylabel('Potential, $v_{\mathrm{C}}^{\mathrm{LDA}}(r_s)$', size=15) 229 | plt.xlim(0,10) 230 | plt.ylim(-0.15,0) 231 | plt.rcParams["legend.markerscale"] = 1.5 232 | plt.legend(loc='upper right', fontsize=15) 233 | plt.show() 234 | 235 | def GGAx_pot_eq(den_num, grad_den_num): 236 | '''Generates line plot of PBE exchange enhancement factor with a LaTeX equation and scatterpoint at the specified density + gradient value. 237 | 238 | :param float den_num: value of the electron density 239 | :param float grad_den_num: gradient of electron density at same point 240 | 241 | Example: 242 | 243 | >>> # Display GGA exchange equation and diagram for n(r) = 0.15 and grad n(r) = 1.00 244 | >>> GGAx_pot_eq(0.15, 1.00) 245 | 246 | .. math:: 247 | 248 | s=\\frac{|1.00|}{2 \cdot 3^{1/3} \pi^{2/3} (0.150)^{4/3}}=2.028 \ \ \ F_{x,s=2.028} = 1 + \kappa - \\frac{\kappa}{\left(1 + \\frac{\mu (2.028)^2}{\kappa} \\right)} = 1.425 249 | 250 | .. image:: figures/GGA_exch_plot.png 251 | 252 | ''' 253 | kappa, mu = 0.804, 0.21951 # constants used in the paper, 10.1103/PhysRevLett.77.3865 254 | # reduced density gradient, s: 255 | calculate_s = lambda den, grad_den: abs(grad_den)/(2*3**(1/3)*np.pi**(2/3)*den**(4/3)) 256 | # enhancement factor, F_s: 257 | calculate_F_s = lambda s: 1 + kappa - kappa/(1 + mu*s**2 / kappa) 258 | s_num = calculate_s(den_num, grad_den_num) 259 | F_s_num = calculate_F_s(s_num) 260 | 261 | # LaTeX equation 262 | equation = '## $s = \\frac{' + f'|{grad_den_num:.3f}|' + '}{2\cdot3' + '^{1/3}\pi^{2/3}' + f'({den_num:.3f})' + '^{4/3}}=' + f'{s_num:.3f}\ ' 263 | equation += '\ \ \ \ F_{x,\ s=' + f'{s_num:.3f}' + '} = 1 + \kappa - \\frac{\kappa}{(1+\\frac{\mu' + f'({s_num:.3f})^2' + '}{\kappa})}=' + f'{F_s_num:.3f}\ ' + '$' 264 | equation = Markdown(equation) 265 | display(equation) 266 | display(Markdown('
')) 267 | 268 | # Matplotlib Diagram 269 | x_s = np.linspace(0, 10, 300) 270 | y = calculate_F_s(x_s) 271 | plt.plot(x_s, y, label=r'PBE Exch. Factor') 272 | plt.hlines(F_s_num, 0, s_num, colors='black') 273 | plt.vlines(s_num, -2, F_s_num, colors='black') 274 | plt.scatter(s_num, F_s_num, color='red', marker="s", label='grid point') 275 | plt.title('GGA Enchancement Factor', size=15) 276 | plt.xlabel('RDG, $s$', size=15) 277 | plt.ylabel('$F_x(s)$', size=15) 278 | plt.xlim(0,10) 279 | plt.ylim(0.95, 2.1) 280 | plt.legend(loc='upper left', fontsize=15) 281 | plt.show() 282 | 283 | def hartree_plotter(freq, solve_poisson): 284 | '''Display diagram solving for potential from model density in 1D. 285 | 286 | :param int freq: coefficient for frequency in trig function 287 | :param bool solve_poisson: display (True) potential from trig functionc 288 | 289 | Example: 290 | 291 | >>> # solve poisson equation for trig function with a frequency of 3 292 | >>> hartree_plotter(3, True) 293 | 294 | .. math:: 295 | 296 | n(x) = \sin^2(2\pi(3)x) 297 | 298 | \\nabla^2 V(x) = -4\pi n(x) \\xrightarrow[\\text{linalg.cg}]{\\text{scipy}} V(x) 299 | 300 | .. image:: figures/1D_hartree.png 301 | 302 | ''' 303 | # Data for density and Ax=b solver 304 | nx = 300 305 | x = np.linspace(0, np.pi, nx) 306 | y = np.sin(freq*x)**2 307 | diag1x = np.ones(nx)/(x[1]) 308 | D1x = sparse.spdiags(np.array([-diag1x, diag1x]), np.array([0,1]), nx, nx) 309 | diagx = np.ones(nx)/(x[1]**2) 310 | D2x = sparse.spdiags(np.array([diagx, -2*diagx, diagx]), np.array([-1,0,1]), nx, nx) 311 | T = -1/2 * D2x 312 | test = sparse.linalg.cg(-2*T, -4.*np.pi*y) # solve for V 313 | 314 | # LaTeX equation 315 | equation1 = '## $n(x) = \sin^2( 2 \pi ' + f'({freq})' + 'x)$' 316 | equation2 = '## $\\nabla^2V(x)=-4\pi n(x) \\xrightarrow[\\text{linalg.cg}]{\\text{scipy}} V(x)$' 317 | display(Markdown(equation1)) 318 | display(Markdown(equation2)) 319 | 320 | # Matplotlib Diagram 321 | plt.gcf() 322 | plt.clf() 323 | plt.title('1D Hartree Potential', size=15) 324 | plt.plot(x, y, label=f'n(x) = $\sin^2(2 \pi ({freq}) x)$') 325 | if solve_poisson: 326 | plt.plot(x, test[0], label='V(x)') 327 | plt.xlabel('x', size=15) 328 | plt.ylabel('Amplitude', size=15) 329 | plt.legend(loc='upper right') 330 | plt.grid() 331 | plt.show() 332 | print() # a little extra space 333 | 334 | def hamiltonian_display(functional, har, ex, cor): 335 | '''Generates LaTeX equation of effective single-particle Hamiltonian. 336 | 337 | :param str functional: XC potential (LDA or PBE) 338 | :param bool har: Hartree potential on or off 339 | :param bool ex: exchange term on or off 340 | :param bool cor: correlation term on or off 341 | 342 | Example: 343 | 344 | >>> # display effective hamiltonian with exch and hartree and without correlation 345 | >>> hamiltonian_display('LDA', True, True, False) 346 | 347 | .. math:: 348 | 349 | \hat{h}_i = \hat{T}_{\\text{kin},i} + v_{\\text{Ha}}(n(r)) + v_{\\text{X}}^{\\text{LDA}}(n(r)) + 0 350 | 351 | ''' 352 | ham = '## $$ \hat{h}_i = \hat{T}_{\\text{kin}, i} +' 353 | if har == True: 354 | ham += 'v_{\\text{Ha}}(n(r))' 355 | else: 356 | ham += '0' 357 | if ex == True: 358 | if functional == 'LDA': 359 | ham += '+ v_{\\text{X}}^{\\text{LDA}}(n(r))' 360 | elif functional == 'PBE': 361 | ham += '+ v_{\\text{X}}^{\\text{PBE}}(n(r), \\nabla n(r))' 362 | else: 363 | ham += '+ 0' 364 | if cor == True: 365 | if functional == 'LDA': 366 | ham += '+ v_{\\text{C}}^{\\text{LDA}}(n(r))' 367 | elif functional == 'PBE': 368 | ham += '+ v_{\\text{C}}^{\\text{PBE}}(n(r), \\nabla n(r))' 369 | else: 370 | ham += '+ 0' 371 | ham += '$$' 372 | display(Markdown(ham)) 373 | 374 | def energy_plot(den_log, ener_log, converge_state, show_fig=True, save_fig=False, filename=None): 375 | '''Generates iteration number vs total energy plot. 376 | 377 | :param list den_log: list of numpy arrays with electron density 378 | :param list ener_log: list of total energy values in Ha 379 | :param bool converge_state: SCF loop ended in a converged (True) or unconverged (False) state 380 | :param bool show_fig: display figure to output (True) or not (False) 381 | :param bool save_fig: save a .png file of diagram 382 | :param str filename: filename to save to (w/o extension) 383 | ''' 384 | fig = plt.figure(figsize=(6, 4)) 385 | ax = fig.add_subplot(1, 1, 1) 386 | plt.title('Convergence Plot') 387 | plt.scatter(0, energy_log[0], color='#F97306', label='noninter Energy') 388 | plt.plot(np.arange(1,len(energy_log[1:]) + 1), energy_log[1:], 'o-', label='DFT Energy') 389 | plt.legend(loc='upper right') 390 | plt.text(0.785, 0.800, f'iterations: {len(density_log) - 1}', 391 | horizontalalignment='center', verticalalignment='center', 392 | transform=ax.transAxes) 393 | plt.text(0.785, 0.730,f' energy (Ha): {energy_log[-1]:.5f}', 394 | size=10.5, horizontalalignment='center', verticalalignment='center', 395 | transform= ax.transAxes) 396 | if converge_state == True: 397 | plt.text(0.79, 0.660,f'converged', 398 | size=10.5, horizontalalignment='center', verticalalignment='center', 399 | transform= ax.transAxes, weight='bold') 400 | elif converge_state == False: 401 | plt.text(0.79, 0.660,f'unconverged', 402 | size=10.5, horizontalalignment='center', verticalalignment='center', 403 | transform= ax.transAxes, weight='bold') 404 | plt.ylabel('Energy (Ha)') 405 | plt.xlabel('iteration #') 406 | plt.tight_layout() 407 | if save_fig: 408 | plt.savefig(f'{filename}.png', dpi = 800) 409 | if show_fig: 410 | plt.show() 411 | else: 412 | plt.close() 413 | 414 | def hard_walls(potential, nx, ny, nz): 415 | '''Sets potential of outermost grid points to 1,000. Used for testing. 416 | 417 | :param np.array potential: KS (or other) potential as a flattened array 418 | :param int nx: Grid points in x-direction. 419 | :param int ny: Grid points in y-direction. 420 | :param int nz: Grid points in z-direction. 421 | :return: Flattened array of grid points. 422 | ''' 423 | potential = potential.reshape(nx, ny, nz) 424 | potential[0,:,:] = 1000 425 | potential[-1,:,:] = 1000 426 | potential[:,0,:] = 1000 427 | potential[:,-1,:] = 1000 428 | potential[:,:,0] = 1000 429 | potential[:,:,-1] = 1000 430 | return potential.flatten() 431 | 432 | def hartree(den, kin_oper, dx, dy, dz, nx, ny, nz): 433 | '''Uses Poisson's equation to find Hartree potential from density. 434 | 435 | :param np.array den: Electron density. 436 | :param np.array kin_oper: Kinetic energy operator as sparse matrix. 437 | :param float dx: Differential volume element in x-direction. 438 | :param float dy: Idem for y. 439 | :param float dz: Idem for z. 440 | :param int nx: Number of grid points in x-direction. 441 | :param int ny: Idem for y. 442 | :param int nz: Idem for z. 443 | :return: 444 | - **v_ha_flat** (*np.array*): Hartree potential as flattened array. 445 | - **v_ha_ener** (*float*): Hartree energy (in Ha units); needed to compute total energy 446 | ''' 447 | clean_den = np.ma.array(den, mask= abs(den) < 0.000001) # Mask the low-density points 448 | clean_den = np.ma.filled(clean_den, fill_value=0.0) # Fill with zeros 449 | den = clean_den 450 | den = edge_cleaner(den, nx, ny, nz, num_edges=1) # Set edges to zero 451 | v_ha_flat = sparse.linalg.cg(-2*kin_oper,-4.*np.pi*den)[0] 452 | v_ha_flat = edge_cleaner(v_ha_flat, nx, ny, nz, num_edges=1) 453 | v_ha_ener = (1/2)*integ_3d(v_ha_flat*den, dx, dy, dz) 454 | return v_ha_flat, v_ha_ener 455 | 456 | def lda_exchange(den, dx, dy, dz): 457 | '''Finds LDA exchange potential and energy from density. 458 | 459 | :param np.array den: Electron density. 460 | :param float dx: Differential volume element in x-direction. 461 | :param float dy: Idem for y. 462 | :param float dz: Idem for z. 463 | :return: 464 | - **exch_pot_lda** (*np.array*): LDA exchange potential. 465 | - **exch_ener_lda** (*float*): LDA exchnage energy (in Ha). 466 | ''' 467 | exch_pot_lda = -(3/4)*(3/np.pi)**(1/3)*(den)**(1/3) 468 | clean_den = np.ma.array(den, mask= abs(den) < 0.000001) 469 | clean_den = np.ma.filled(clean_den, fill_value=0.0) 470 | den = clean_den 471 | exch_ener_lda = -(3/4)*(3/np.pi)**(1/3)*integ_3d(den**(4/3), dx, dy, dz) 472 | return exch_pot_lda, exch_ener_lda 473 | 474 | def lda_correlation(den, dx, dy, dz): 475 | '''Finds LDA correlation potential and energy from density. 476 | 477 | :param np.array den: Electron density. 478 | :param float dx: Differential volume element in x-direction. 479 | :param float dy: Idem for y. 480 | :param float dz: Idem for z. 481 | :return: 482 | - **corr_pot** (*np.array*): LDA correlation potential. 483 | - **corr_en** (*float*): LDA correlation energy (in Ha). 484 | ''' 485 | # a, b, c fitting constants used in paper: 10.1063/1.4958669 486 | a, b, c = (np.log(2)-1)/(2*np.pi**2), 20.4562557, (4*np.pi/3)**(1/3) 487 | corr_pot = a*np.log(1 + b*c*den**(1/3) + b*(c**2)*den**(2/3)) 488 | clean_den = np.ma.array(den, mask= abs(den) < 0.000001) 489 | clean_den = np.ma.filled(clean_den, fill_value=0.0) 490 | den = clean_den 491 | corr_en = integ_3d(den*corr_pot, dx, dy, dz) 492 | return corr_pot, corr_en 493 | 494 | def RDG(den, der_1st, nx, ny, nz): 495 | '''Finds dimensionless reduced density gradient (needed for PBE exchange). 496 | 497 | :param np.array den: Electron density. 498 | :param np.array der_1st: 1st derivative operator as sparse matrix. 499 | :param int nx: Number of grid points in x-direction. 500 | :param int ny: Idem for y. 501 | :param int nz: Idem for z. 502 | :return: 503 | - **RDG** (*np.array*): Reduced density gradient as matrix. 504 | ''' 505 | clean_den = np.ma.array(den, mask = abs(den) < 0.000001) 506 | den = clean_den 507 | RDG = (2*3**(1/3)*np.pi**(2/3))**(-1) * abs(der_1st.dot(den)) * den**(-4/3) 508 | RDG = edge_cleaner(RDG, nx, ny, nz, num_edges=1) #set the edges to zero 509 | RDG = np.ma.filled(RDG, fill_value=0.0) # return zeros where density is very small 510 | return RDG 511 | 512 | def pbe_exchange(den, D1st, dx, dy, dz, nx, ny, nz): 513 | '''Finds PBE exchange potential and energy from density + gradient. 514 | 515 | :param np.array den: Electron density. 516 | :param np.array D1st: 1st derivative operator as sparse matrix. 517 | :param float dx: Differential volume element in x-direction. 518 | :param float dy: Idem for y. 519 | :param float dz: Idem for z. 520 | :param int nx: Number of grid points in x-direction. 521 | :param int ny: Idem for y. 522 | :param int nz: Idem for z. 523 | :return: 524 | - **exch_pot_pbe** (*np.array*): PBE exchange potential. 525 | - **exch_ener_pbe** (*float*): PBE exchange energy (in Ha). 526 | ''' 527 | # kappa and mu constants used in the paper, 10.1103/PhysRevLett.77.3865 528 | kappa, mu = 0.804, 0.2195149727645171 529 | s = RDG(den, D1st, nx, ny, nz) 530 | F_xs = 1 + kappa - kappa * (1 + mu * s**2 / kappa)**(-1) # exch enhancement factor 531 | exch_pot_pbe = F_xs * -(3/4)*(3/np.pi)**(1/3)*((den)**(1/3)) 532 | clean_den = np.ma.array(den, mask= abs(den) < 0.000001) 533 | clean_den = np.ma.filled(clean_den, fill_value=0.0) 534 | den = clean_den 535 | exch_ener_pbe = integ_3d(den*exch_pot_pbe, dx, dy, dz) 536 | return exch_pot_pbe, exch_ener_pbe 537 | 538 | def cor_den_grad(den, der_1st, nx, ny, nz): 539 | '''Finds dimensionless correlation density gradient (used in PBE correlation). 540 | 541 | :param np.array den: Electron density. 542 | :param np.array der_1st: 1st derivative operator as sparse matrix. 543 | :param int nx: Number of grid points in x-direction. 544 | :param int ny: Idem for y. 545 | :param int nz: Idem for z. 546 | :return: 547 | - **t** (*np.array*): Correlation density gradient. 548 | ''' 549 | d_g = abs(der_1st.dot(den)) 550 | clean_den = np.ma.array(den, mask = abs(den) < 0.000001) 551 | den = clean_den 552 | t = (d_g*np.pi**(1/6))/(4*3**(1/6)*den**(7/6)) 553 | t = edge_cleaner(t, nx, ny, nz, num_edges=1) 554 | t = np.ma.filled(t, fill_value=0.0) 555 | return t 556 | 557 | def pbe_correlation(den, der_1st, dx, dy, dz, nx, ny, nz): 558 | '''Finds PBE correlation potential and energy from density + gradient. 559 | 560 | :param np.array den: Electron density. 561 | :param np.array der_1st: 1st derivative operator as sparse matrix. 562 | :param float dx: Differential volume element in x-direction. 563 | :param float dy: Idem for y. 564 | :param float dz: Idem for z. 565 | :param int nx: Number of grid points in x-direction. 566 | :param int ny: Idem for y. 567 | :param int nz: Idem for z. 568 | :return: 569 | - **cor_pot_pbe** (*np.array*): PBE correlation potential. 570 | - **cor_ener_pbe** (*float*): PBE correlation energy (in Ha). 571 | ''' 572 | lda_c_pot = lda_correlation(den, dx, dy, dz)[0] 573 | # beta and gamma constants used in the paper, 10.1103/PhysRevLett.77.3865 574 | beta, gamma = 0.06672455060314922, 0.031090690869654894 575 | lda_c_pot = np.ma.array(lda_c_pot, mask = abs(lda_c_pot) < 0.000001) 576 | A = (beta/gamma)*((np.exp(-lda_c_pot/gamma)-1)**(-1)) 577 | t = cor_den_grad(den, der_1st, nx, ny, nz) 578 | H = gamma*np.log(1+(beta/gamma)*t**2*((1+A*(t**2))/(1+A*(t**2)+(A**2)*(t**4)))) 579 | cor_pot_pbe = lda_c_pot + H 580 | cor_ener_pbe = integ_3d(den*cor_pot_pbe, dx, dy, dz) 581 | return cor_pot_pbe, cor_ener_pbe 582 | 583 | -------------------------------------------------------------------------------- /notebooks/notebook_functions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjz21/DFT_PIB_Code/ae44cb9c8968be26f1a821c0a18764e682046bcb/notebooks/notebook_functions/__init__.py -------------------------------------------------------------------------------- /offline_jupyter/README.md: -------------------------------------------------------------------------------- 1 | # Offline Jupyter Notebooks 2 | Instead of using Google Colab, offline versions of the notebooks have been made available in this directory and can be run through the conventional Jupyter IDE. Instructions for setting up the conda virtual environment are provided here and have been tested on wsl-Ubuntu 22.04 using the Firefox browser. Although NB1 works fine here, **this is not the recommended approach and most of the GUI features in NB2 and NB3 do not display correctly** (because of differences in how ipywidgets runs on Colab vs a local Jupyter Notebook). 3 | ## Conda Instructions 4 | 1. Create a conda virtual environment 5 | ``` 6 | conda create --name DFT_code python=3.10 7 | ``` 8 | 2. Install dependencies to this environment from the requirements.txt file. This takes ~4 minutes to complete. 9 | ```sh 10 | conda activate DFT_code 11 | ``` 12 | ```sh 13 | pip install -r requirements.txt 14 | ``` 15 | 3. Launch a Jupyter notebook instance. 16 | ```sh 17 | jupyter notebook 18 | ``` 19 | 4. Download the offline versions of the notebooks and open them through the Jupyter IDE. The visualizations should become active after executing the code cells. 20 | -------------------------------------------------------------------------------- /offline_jupyter/old_ipv_compatible/info.txt: -------------------------------------------------------------------------------- 1 | These are the old versions of the offline notebooks made to work with ipyvolume instead of plotly. 2 | -------------------------------------------------------------------------------- /offline_jupyter/old_ipv_compatible/requirements.txt: -------------------------------------------------------------------------------- 1 | ipyvolume==0.6.0 2 | rdkit==2023.3.1 3 | py3Dmol==2.0.3 4 | pyscf==2.2.1 5 | pythreejs==2.4.2 6 | Cube-Toolz @ git+https://github.com/funkymunkycool/Cube-Toolz.git@915ccf277a25e1c6996365954db592ab39855f6a 7 | numpy==1.22.4 8 | matplotlib==3.7.1 9 | ipython==7.34.0 10 | ipywidgets==8.0.7 11 | termcolor==2.2.0 12 | pathlib==1.0.1 13 | pandas==1.5.3 14 | jupyter 15 | -------------------------------------------------------------------------------- /offline_jupyter/requirements.txt: -------------------------------------------------------------------------------- 1 | plotly==5.13.1 2 | kaleido 3 | rdkit==2023.3.1 4 | py3Dmol==2.0.3 5 | pyscf==2.2.1 6 | pythreejs==2.4.2 7 | Cube-Toolz @ git+https://github.com/funkymunkycool/Cube-Toolz.git@915ccf277a25e1c6996365954db592ab39855f6a 8 | numpy==1.22.4 9 | matplotlib==3.7.1 10 | ipython==7.34.0 11 | ipywidgets==8.0.7 12 | termcolor==2.2.0 13 | pathlib==1.0.1 14 | pandas==1.5.3 15 | jupyter==1.0.0 16 | --------------------------------------------------------------------------------