From 4b9a6141fba9ae7810bfb4130926827da413564e Mon Sep 17 00:00:00 2001 From: Antoine Levitt Date: Mon, 25 Jul 2022 08:55:43 +0200 Subject: [PATCH 01/26] Overhaul docs organisation (#684) Co-authored-by: Michael F. Herbst --- .github/workflows/ci.yaml | 4 +- README.md | 2 +- docs/.gitignore | 8 +- docs/Project.toml | 5 +- docs/make.jl | 188 ++++++++++++------ .../{advanced => developer}/conventions.md | 9 +- .../data_structures.md | 0 .../src/{advanced => developer}/symmetries.md | 0 .../useful_formulas.md | 0 docs/src/features.md | 49 +++++ ...al_theory.md => introductory_resources.md} | 2 +- docs/src/guide/periodic_problems.jl | 4 +- docs/src/guide/tutorial.jl | 10 +- docs/src/index.md | 87 ++------ docs/src/publications.md | 1 + docs/src/{guide => tricks}/parallelization.md | 0 docs/src/{guide => tricks}/scaling.png | Bin .../src/tricks}/scf_checkpoints.jl | 0 examples/.gitignore | 2 + {docs/src/guide => examples}/Fe_afm.pwi | 0 examples/anyons.jl | 8 +- examples/atomsbase.jl | 19 +- examples/compare_solvers.jl | 43 ++-- examples/custom_potential.jl | 6 +- examples/dielectric.jl | 10 +- examples/{ase.jl => gaas_surface.jl} | 29 +-- examples/graphene.jl | 4 +- examples/gross_pitaevskii.jl | 2 +- examples/gross_pitaevskii_2D.jl | 2 +- {docs/src/guide => examples}/input_output.jl | 11 +- examples/metallic_systems.jl | 2 +- .../2020_silicon_scf_convergence.jl} | 0 .../2022_cazalis.jl} | 0 examples/publications/README.md | 4 + examples/pymatgen.jl | 41 ---- examples/scf_callbacks.jl | 2 +- examples/supercells.jl | 107 ++++++++++ test/runexamples.jl | 28 +-- 38 files changed, 400 insertions(+), 289 deletions(-) rename docs/src/{advanced => developer}/conventions.md (93%) rename docs/src/{advanced => developer}/data_structures.md (100%) rename docs/src/{advanced => developer}/symmetries.md (100%) rename docs/src/{advanced => developer}/useful_formulas.md (100%) create mode 100644 docs/src/features.md rename docs/src/guide/{density_functional_theory.md => introductory_resources.md} (96%) rename docs/src/{guide => tricks}/parallelization.md (100%) rename docs/src/{guide => tricks}/scaling.png (100%) rename {examples => docs/src/tricks}/scf_checkpoints.jl (100%) rename {docs/src/guide => examples}/Fe_afm.pwi (100%) rename examples/{ase.jl => gaas_surface.jl} (68%) rename {docs/src/guide => examples}/input_output.jl (87%) rename examples/{silicon_scf_convergence.jl => publications/2020_silicon_scf_convergence.jl} (100%) rename examples/{papers/cazalis.jl => publications/2022_cazalis.jl} (100%) create mode 100644 examples/publications/README.md delete mode 100644 examples/pymatgen.jl create mode 100644 examples/supercells.jl diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f55e4f660c..ae697f6afe 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -99,7 +99,7 @@ jobs: with: python-version: '3.7' - name: Install python dependencies - run: pip install ase pymatgen + run: pip install ase - uses: julia-actions/setup-julia@v1 with: version: '1.6' @@ -113,4 +113,4 @@ jobs: ${{ runner.os }}-test-${{ env.cache-name }}- ${{ runner.os }}-test- ${{ runner.os }}- - - run: julia --color=yes --project -e 'using Pkg; Pkg.test(test_args = ["example"])' + - run: julia --color=yes --project -e 'using Pkg; Pkg.test(test_args=["example"])' diff --git a/README.md b/README.md index 0ede2b0386..aeebc269f2 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ such as [Abinit](https://www.abinit.org/) or For getting started with DFTK, see [our documentation](https://docs.dftk.org): - [Installation instructions](https://docs.dftk.org/stable/guide/installation/) - [Tutorial](https://docs.dftk.org/stable/guide/tutorial/) -- [Examples](https://docs.dftk.org/stable/#example-index-1) +- [Basic DFT examples](https://docs.dftk.org/stable/examples/metallic_systems/) Note that at least **Julia 1.6** is required. diff --git a/docs/.gitignore b/docs/.gitignore index 20d40c9029..0d69dfdb41 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,11 +1,9 @@ build/ site/ -src/examples/*.md -src/examples/*.ipynb +src/examples/ src/guide/tutorial.md src/guide/tutorial.ipynb -src/guide/input_output.md -src/guide/input_output.ipynb src/guide/periodic_problems.ipynb src/guide/periodic_problems.md - +src/tricks/scf_checkpoints.ipynb +src/tricks/scf_checkpoints.md diff --git a/docs/Project.toml b/docs/Project.toml index a0776be15c..60144fa699 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,4 +1,5 @@ [deps] +AtomsBase = "a963bdd2-2df7-4f54-a1ee-49d51e6be12a" DFTK = "acf6eb54-70d9-11e9-0013-234b7a5f5337" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" @@ -6,8 +7,8 @@ IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433" -Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" LinearMaps = "7a12625a-238d-50fd-b39a-03d52299707e" +Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" Optim = "429524aa-4258-5aef-a3af-852621145aeb" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" @@ -20,4 +21,4 @@ WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" wannier90_jll = "c5400fa0-8d08-52c2-913f-1e3f656c1ce9" [compat] -Documenter = "~0.26" +Documenter = "~0.27" diff --git a/docs/make.jl b/docs/make.jl index 7a4bc0721b..c5b636b9fb 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,14 +1,97 @@ -import LibGit2 -import Pkg - # To manually generate the docs: +# 1. Install all python dependencies from the PYDEPS array below. +# 2. Run "julia make.jl" +# +# To add a new example to the docs: +# 1. Add the *.jl file to /examples, along with assets you require (e.g. input files, +# images, plots etc.) +# 2. Add the example to the PAGES variable below, the asset files to EXAMPLE_ASSETS. # -# 1. Install all python dependencies from the PYDEPS array below. -# 2. Run "julia make.jl" to generate the docs +# To add a new page to the docs (not an example): +# 1. Add the file to /docs/src, along with all assets. It can be a *.jl to be processed +# with Literate.jl and a *.md to be included as is. +# 2. Add the file to the PAGES variable below. You don't need to track the assets. + +# Structure of the docs. List files as *.jl or *.md here. All files +# ending in *.jl will be processed to *.md with Literate. +PAGES = [ + "Home" => "index.md", + "school2022.md", + "features.md", + "Getting started" => [ + # Installing DFTK, tutorial, theoretical background + "guide/installation.md", + "Tutorial" => "guide/tutorial.jl", + "guide/periodic_problems.jl", + "guide/introductory_resources.md", + ], + "Basic DFT calculations" => [ + # Ground-state DFT calculations, standard problems and modelling techniques + # Some basic show cases; may feature integration of DFTK with other packages. + "examples/metallic_systems.jl", + "examples/collinear_magnetism.jl", + "examples/supercells.jl", + "examples/gaas_surface.jl", + "examples/graphene.jl", + "examples/geometry_optimization.jl", + ], + "Response and properties" => [ + "examples/polarizability.jl", + "examples/forwarddiff.jl", + "examples/dielectric.jl", + ], + "Ecosystem integration" => [ + # This concerns the discussion of interfaces, IO and integration + # options we have + "examples/atomsbase.jl", + "examples/input_output.jl", + "examples/wannier90.jl", + ], + "Tipps and tricks" => [ + # Resolving convergence issues, what solver to use, improving performance or + # reliability of calculations. + "tricks/parallelization.md", + "tricks/scf_checkpoints.jl", + ], + "Solvers" => [ + "examples/custom_solvers.jl", + "examples/scf_callbacks.jl", + "examples/compare_solvers.jl", + ], + "Nonstandard models" => [ + "examples/gross_pitaevskii.jl", + "examples/gross_pitaevskii_2D.jl", + "examples/custom_potential.jl", + "examples/cohen_bergstresser.jl", + "examples/anyons.jl", + ], + "Error control" => [ + "examples/arbitrary_floattype.jl", + "examples/error_estimates_forces.jl", + ], + "Developer resources" => [ + "developer/conventions.md", + "developer/data_structures.md", + "developer/useful_formulas.md", + "developer/symmetries.md", + ], + "api.md", + "publications.md", +] -# Set to true to disable some checks and cleanup -DEBUG = false +# Files from the /examples folder that need to be copied over to the docs +# (typically images, input or data files etc.) +EXAMPLE_ASSETS = [ + "examples/Fe_afm.pwi", +] +# +# Configuration and setup +# +DEBUG = false # Set to true to disable some checks and cleanup + +import LibGit2 +import Pkg # Where to get files from and where to build them SRCPATH = joinpath(@__DIR__, "src") BUILDPATH = joinpath(@__DIR__, "build") @@ -18,8 +101,8 @@ DFTKREV = LibGit2.head(ROOTPATH) DFTKBRANCH = try LibGit2.branch(LibGit2.GitRepo(ROOTPATH)) catch end DFTKREPO = "github.com/JuliaMolSim/DFTK.jl.git" -# Python and Julia dependencies needed for running the notebooks -PYDEPS = ["ase", "pymatgen"] +# Python dependencies needed for running the notebooks +PYDEPS = ["ase"] # Setup julia dependencies for docs generation if not yet done Pkg.activate(@__DIR__) @@ -38,23 +121,33 @@ using DFTK using Documenter using Literate -# Collect examples from the example index (src/index.md) -# The chosen examples are taken from the examples/ folder to be processed by Literate -EXAMPLES = [String(m[1]) - for m in match.(r"\"(examples/[^\"]+.md)\"", - readlines(joinpath(SRCPATH, "index.md"))) - if !isnothing(m)] +# +# Generate the docs +# + +# Get list of files from PAGES +extract_paths(pages::AbstractArray) = collect(Iterators.flatten(extract_paths.(pages))) +extract_paths(file::AbstractString) = [file] +extract_paths(pair::Pair) = extract_paths(pair.second) + +# Transform files to *.md +transform_to_md(pages::AbstractArray) = transform_to_md.(pages) +transform_to_md(file::AbstractString) = first(splitext(file)) * ".md" +transform_to_md(pair::Pair) = (pair.first => transform_to_md(pair.second)) + +# Copy assets over +mkpath(joinpath(SRCPATH, "examples")) +for asset in EXAMPLE_ASSETS + cp(joinpath(ROOTPATH, asset), joinpath(SRCPATH, asset), force=true) +end # Collect files to treat with Literate (i.e. the examples and the .jl files in the docs) # The examples go to docs/literate_build/examples, the .jl files stay where they are -literate_files = [(src=joinpath(ROOTPATH, splitext(file)[1] * ".jl"), - dest=joinpath(SRCPATH, "examples"), example=true) - for file in EXAMPLES] -for (dir, directories, files) in walkdir(SRCPATH) - for file in files - if endswith(file, ".jl") - push!(literate_files, (src=joinpath(dir, file), dest=dir, example=false)) - end +literate_files = map(filter!(endswith(".jl"), extract_paths(PAGES))) do file + if startswith(file, "examples/") + (src=joinpath(ROOTPATH, file), dest=joinpath(SRCPATH, "examples"), example=true) + else + (src=joinpath(SRCPATH, file), dest=joinpath(SRCPATH, dirname(file)), example=false) end end @@ -78,16 +171,17 @@ end # Run Literate on them all for file in literate_files preprocess = file.example ? add_badges : identity - Literate.markdown(file.src, file.dest; documenter=true, credit=false, - preprocess=preprocess) + Literate.markdown(file.src, file.dest; + flavor=Literate.DocumenterFlavor(), + credit=false, preprocess) Literate.notebook(file.src, file.dest; credit=false, execute=CONTINUOUS_INTEGRATION || DEBUG) end # Generate the docs in BUILDPATH -makedocs( - modules = [DFTK], - format = Documenter.HTML( +makedocs(; + modules=[DFTK], + format=Documenter.HTML( # Use clean URLs, unless built as a "local" build prettyurls = CONTINUOUS_INTEGRATION, canonical = "https://docs.dftk.org/stable/", @@ -101,29 +195,9 @@ makedocs( # login screen and cause a warning: r"https://github.com/([A-Za-z0-9_.-]+)/([A-Za-z0-9_.-]+)/edit(.*)", ], - pages = [ - "Home" => "index.md", - "school2022.md", - "Getting started" => Any[ - "guide/installation.md", - "Tutorial" => "guide/tutorial.md", - "guide/input_output.md", - "guide/parallelization.md", - "Introduction to periodic problems" => "guide/periodic_problems.md", - "Density-functional theory" => "guide/density_functional_theory.md", - ], - "Examples" => EXAMPLES, - "Advanced topics" => Any[ - "advanced/conventions.md", - "advanced/data_structures.md", - "advanced/useful_formulas.md", - "advanced/symmetries.md", - ], - "api.md", - "publications.md", - ], + pages=transform_to_md(PAGES), checkdocs=:exports, - strict = !DEBUG, + strict=!DEBUG, ) # Dump files for managing dependencies in binder @@ -131,13 +205,13 @@ if CONTINUOUS_INTEGRATION && DFTKBRANCH == "master" cd(BUILDPATH) do open("environment.yml", "w") do io print(io, - """ - name: dftk - channels: - - defaults - - conda-forge - dependencies: - """) + """ + name: dftk + channels: + - defaults + - conda-forge + dependencies: + """) for dep in PYDEPS println(io, " - " * dep) end diff --git a/docs/src/advanced/conventions.md b/docs/src/developer/conventions.md similarity index 93% rename from docs/src/advanced/conventions.md rename to docs/src/developer/conventions.md index 09913d0b68..a83b854c96 100644 --- a/docs/src/advanced/conventions.md +++ b/docs/src/developer/conventions.md @@ -59,11 +59,10 @@ auconvert(Å, 1.2) # 1.2 Bohr in Ångström are Bohr (used by DFTK) and Ångström (used e.g. by ASE, 1Å ≈ 1.80 Bohr). When setting up a calculation for DFTK one needs to ensure to convert to Bohr and atomic units. - For some Python libraries (currently ASE and pymatgen) - DFTK directly ships conversion tools in form of the `load_lattice`, - `load_positions` and `load_atoms` functions, - which take care of such conversions. Examples which demonstrate this - are [Creating slabs with ASE](@ref) and [Creating supercells with pymatgen](@ref). + When structures are provided + as [AtomsBase.jl](https://github.com/JuliaMolSim/AtomsBase.jl)-compatible + objects, this unit conversion is automatically performed behind the scenes. + See [AtomsBase integration](@ref) for details. ## [Lattices and lattice vectors](@id conventions-lattice) Both the real-space lattice (i.e. `model.lattice`) and reciprocal-space lattice diff --git a/docs/src/advanced/data_structures.md b/docs/src/developer/data_structures.md similarity index 100% rename from docs/src/advanced/data_structures.md rename to docs/src/developer/data_structures.md diff --git a/docs/src/advanced/symmetries.md b/docs/src/developer/symmetries.md similarity index 100% rename from docs/src/advanced/symmetries.md rename to docs/src/developer/symmetries.md diff --git a/docs/src/advanced/useful_formulas.md b/docs/src/developer/useful_formulas.md similarity index 100% rename from docs/src/advanced/useful_formulas.md rename to docs/src/developer/useful_formulas.md diff --git a/docs/src/features.md b/docs/src/features.md new file mode 100644 index 0000000000..d47d6d2e18 --- /dev/null +++ b/docs/src/features.md @@ -0,0 +1,49 @@ +# [DFTK features](@id package-features) + +* Standard methods and models: + - Standard DFT models (LDA, GGA, meta-GGA): Any functional from the + [libxc](https://tddft.org/programs/libxc/) library + - Godecker norm-conserving pseudopotentials (GTH, HGH) + - Brillouin zone symmetry for ``k``-point sampling using [spglib](https://atztogo.github.io/spglib/) + - Standard smearing functions (including Methfessel-Paxton + and Marzari-Vanderbilt cold smearing) + - Collinear spin polarization for magnetic systems + - Self-consistent field approaches including Kerker mixing, + [LDOS mixing](https://doi.org/10.1088/1361-648X/abcbdb), + [adaptive damping](https://arxiv.org/abs/2109.14018) + - Direct minimization, Newton solver + - Multi-level threading (``k``-points eigenvectors, FFTs, linear algebra) + - MPI-based distributed parallelism (distribution over ``k``-points) + - Treat systems of 1000 electrons + +* Ground-state properties and post-processing: + - Total energy + - Forces, stresses + - Density of states (DOS), local density of states (LDOS) + - Band structures + - Easy access to all intermediate quantities (e.g. density, Bloch waves) + +* Unique features + - Support for arbitrary floating point types, including `Float32` (single precision) + or `Double64` (from [DoubleFloats.jl](https://github.com/JuliaMath/DoubleFloats.jl)). + - Forward-mode algorithmic differentiation (see [Polarizability using automatic differentiation](@ref)) + - Flexibility to build your own Kohn-Sham model: + Anything from [analytic potentials](@ref custom-potential), + linear [Cohen-Bergstresser model](@ref), + the [Gross-Pitaevskii equation](@ref gross-pitaevskii), + [Anyonic models](@ref), etc. + - Analytic potentials (see [Tutorial on periodic problems](@ref periodic-problems)) + - 1D / 2D / 3D systems (see [Tutorial on periodic problems](@ref periodic-problems)) + +* Third-party integrations: + - Seamless integration with many standard [Input and output formats](@ref). + - Integration with [ASE](https://wiki.fysik.dtu.dk/ase/) and + [AtomsBase](https://github.com/JuliaMolSim/AtomsBase.jl) for passing + atomic structures. + - [Wannierization using Wannier90](@ref) + + +* Runs out of the box on Linux, macOS and Windows + +Missing a feature? Look for an open issue or [create a new one](https://github.com/JuliaMolSim/DFTK.jl/issues). +Want to contribute? See our [contributing notes](https://github.com/JuliaMolSim/DFTK.jl#contributing). diff --git a/docs/src/guide/density_functional_theory.md b/docs/src/guide/introductory_resources.md similarity index 96% rename from docs/src/guide/density_functional_theory.md rename to docs/src/guide/introductory_resources.md index 9371913bc5..c78666a0f1 100644 --- a/docs/src/guide/density_functional_theory.md +++ b/docs/src/guide/introductory_resources.md @@ -1,4 +1,4 @@ -# [Introductory resources on density-functional theory and DFTK](@id density-functional-theory) +# [Introductory resources](@id introductory-resources) This page collects a bunch of articles, lecture notes, textbooks and recordings related to density-functional theory (DFT) and DFTK. diff --git a/docs/src/guide/periodic_problems.jl b/docs/src/guide/periodic_problems.jl index c12549809e..c9fd47321e 100644 --- a/docs/src/guide/periodic_problems.jl +++ b/docs/src/guide/periodic_problems.jl @@ -1,4 +1,4 @@ -# # [Introduction to periodic problems and plane-wave discretisations](@id periodic-problems) +# # [Problems and plane-wave discretisations](@id periodic-problems) #md # [![](https://mybinder.org/badge_logo.svg)](@__BINDER_ROOT_URL__/guide/@__NAME__.ipynb) #md # [![](https://img.shields.io/badge/show-nbviewer-579ACA.svg)](@__NBVIEWER_ROOT_URL__/guide/@__NAME__.ipynb) @@ -266,7 +266,7 @@ plot_bandstructure(basis; n_bands=6, kline_density=100) # * density-functial theory (DFT) models for treating electronic structures # (see the [Tutorial](@ref)). # * Gross-Pitaevskii models for bosonic systems -# (see [Gross-Pitaevskii equation in one dimension](@ref)) +# (see [Gross-Pitaevskii equation in one dimension](@ref gross-pitaevskii)) # * even some more unusual cases like anyonic models. # # In this tutorial we will go a little more low-level and directly provide diff --git a/docs/src/guide/tutorial.jl b/docs/src/guide/tutorial.jl index bcb26ffc91..e5deced792 100644 --- a/docs/src/guide/tutorial.jl +++ b/docs/src/guide/tutorial.jl @@ -13,8 +13,8 @@ #md # [Periodic problems](@ref periodic-problems) #nb # [Periodic problems](https://docs.dftk.org/stable/guide/periodic_problems/) # or the -#md # [density-functional theory](@ref density-functional-theory) -#nb # [density-functional theory](https://docs.dftk.org/stable/guide/density_functional_theory/) +#md # [Introductory resources](@ref introductory-resources) +#nb # [Introductory resources](https://docs.dftk.org/stable/guide/introductory_resources/) # chapters for some introductory material on the topic. # # !!! note "Convergence parameters in the documentation" @@ -99,9 +99,3 @@ plot_bandstructure(scfres; kline_density=10) # or get the cartesian forces (in Hartree / Bohr) compute_forces_cart(scfres) # As expected, they are almost zero in this highly symmetric configuration. - -# ## Where to go from here -# Take a look at the -#md # [example index](@ref example-index) -#nb # [example index](https://docs.dftk.org/stable/#example-index-1) -# to continue exploring DFTK. diff --git a/docs/src/index.md b/docs/src/index.md index 48416b34c7..7fdbd569e7 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -18,7 +18,11 @@ DFTK's source code is [publicly available on github](https://dftk.org). If you are new to density-functional theory or plane-wave methods, see our notes on [Periodic problems](@ref periodic-problems) and our -collection of [lectures, workshops and literature on DFT](@ref density-functional-theory). +collection of [Introductory resources](@ref introductory-resources). + +Found a bug, missing a feature? Look for an open issue or +[create a new one](https://github.com/JuliaMolSim/DFTK.jl/issues). +Want to contribute? See our [contributing notes](https://github.com/JuliaMolSim/DFTK.jl#contributing). !!! tip "DFTK summer school: 29th to 31st August 2022 in Paris, France" We will organise a summer school centred around the DFTK code @@ -26,78 +30,10 @@ collection of [lectures, workshops and literature on DFT](@ref density-functiona from **29 to 31 August 2022** at **Sorbonne Université, Paris**. For more details and registration info see the [school's website](https://school2022.dftk.org). -## [Package features](@id package-features) -* Methods and models: - - Standard DFT models (LDA, GGA, meta-GGA): Any functional from the - [libxc](https://tddft.org/programs/libxc/) library - - Flexibility to build your own Kohn-Sham model: - Anything from analytic potentials, linear Cohen-Bergstresser models, - Gross-Pitaevskii equations, anyonic models, etc. - - Analytic potentials or Godecker norm-conserving pseudopotentials (GTH, HGH) - - Brillouin zone symmetry for ``k``-point sampling using [spglib](https://atztogo.github.io/spglib/) - - Standard smearing functions (including Methfessel-Paxton - and Marzari-Vanderbilt cold smearing) - - Collinear spin polarization for magnetic systems - - Self-consistent field approaches including Kerker mixing, - [LDOS mixing](https://doi.org/10.1088/1361-648X/abcbdb), - [adaptive damping](https://arxiv.org/abs/2109.14018) - - Direct minimization, Newton solver - - Multi-level threading (``k``-points eigenvectors, FFTs, linear algebra) - - MPI-based distributed parallelism (distribution over ``k``-points) - - 1D / 2D / 3D systems - - External magnetic fields - - Treat systems of 1000 electrons -* Ground-state properties and post-processing: - - Total energy - - Forces, stresses - - Density of states (DOS), local density of states (LDOS) - - Band structures - - Easy access to all intermediate quantities (e.g. density, Bloch waves) -* Support for arbitrary floating point types, including `Float32` (single precision) - or `Double64` (from [DoubleFloats.jl](https://github.com/JuliaMath/DoubleFloats.jl)). -* Runs out of the box on Linux, macOS and Windows -* Third-party integrations: - - Seamless integration with many standard [Input and output formats](@ref). - - Use structures prepared in [pymatgen](https://pymatgen.org) or - [ASE](https://wiki.fysik.dtu.dk/ase/). - - [asedftk](https://github.com/mfherbst/asedftk): - DFTK-based calculator implementation for ASE. - -Missing a feature? Look for an open issue or [create a new one](https://github.com/JuliaMolSim/DFTK.jl/issues). -Want to contribute? See our [contributing notes](https://github.com/JuliaMolSim/DFTK.jl#contributing). - -## [Example index](@id example-index) -First, new users should take a look at the [Installation](@ref) -and [Tutorial](@ref) sections. More details about DFTK are explained -in the examples as we go along: - -```@contents -Pages = [ - "examples/metallic_systems.md", - "examples/pymatgen.md", - "examples/ase.md", - "examples/collinear_magnetism.md", - "examples/geometry_optimization.md", - "examples/scf_callbacks.md", - "examples/scf_checkpoints.md", - "examples/polarizability.md", - "examples/gross_pitaevskii.md", - "examples/gross_pitaevskii_2D.md", - "examples/cohen_bergstresser.md", - "examples/arbitrary_floattype.md", - "examples/forwarddiff.md", - "examples/custom_solvers.md", - "examples/custom_potential.md", - "examples/wannier90.md", - "examples/error_estimates_forces.md", - "examples/graphene.md", -] -Depth = 1 -``` - -These and more examples can be found in the -[`examples` directory](https://dftk.org/tree/master/examples) of the main code. - +# [Getting started](@id getting-started) +First, new users should take a look at the [Installation](@ref) and +[Tutorial](@ref) sections. Then, make your way through the various examples. +An ideal starting point are the [Examples on basic DFT calculations](@ref metallic-systems). !!! note "Convergence parameters in the documentation" In the documentation we use very rough convergence parameters to be able @@ -106,5 +42,6 @@ These and more examples can be found in the Tighter thresholds and larger grids should be used for more realistic results. -If you have a great example you think would fit here, -please open a [pull request](https://github.com/JuliaMolSim/DFTK.jl/pulls)! +If you have an idea for an addition to the docs or see something wrong, +please open an [issue](https://github.com/JuliaMolSim/DFTK.jl/issues) +or [pull request](https://github.com/JuliaMolSim/DFTK.jl/pulls)! diff --git a/docs/src/publications.md b/docs/src/publications.md index ebbd77b7c6..c0be980dbd 100644 --- a/docs/src/publications.md +++ b/docs/src/publications.md @@ -51,6 +51,7 @@ Feel free to drop us a line if you want your work to be added here. [*Convergence analysis of direct minimization and self-consistent iterations*](https://doi.org/10.1137/20M1332864) SIAM Journal on Matrix Analysis and Applications, **42**, 243 (2021). [ArXiv:2004.09088](https://arxiv.org/abs/2004.09088). + ([Computational script](https://github.com/JuliaMolSim/DFTK.jl/blob/80c7452ef728f5e9f413f70e6d5eb4f8357075bc/examples/silicon_scf_convergence.jl)). - M. F. Herbst, A. Levitt and E. Cancès. [*A posteriori error estimation for the non-self-consistent Kohn-Sham equations.*](https://doi.org/10.1039/D0FD00048E) diff --git a/docs/src/guide/parallelization.md b/docs/src/tricks/parallelization.md similarity index 100% rename from docs/src/guide/parallelization.md rename to docs/src/tricks/parallelization.md diff --git a/docs/src/guide/scaling.png b/docs/src/tricks/scaling.png similarity index 100% rename from docs/src/guide/scaling.png rename to docs/src/tricks/scaling.png diff --git a/examples/scf_checkpoints.jl b/docs/src/tricks/scf_checkpoints.jl similarity index 100% rename from examples/scf_checkpoints.jl rename to docs/src/tricks/scf_checkpoints.jl diff --git a/examples/.gitignore b/examples/.gitignore index 2bd18fa44c..61b8ce40f6 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -1 +1,3 @@ /*.pdf +/surface.png +/al_supercell.png diff --git a/docs/src/guide/Fe_afm.pwi b/examples/Fe_afm.pwi similarity index 100% rename from docs/src/guide/Fe_afm.pwi rename to examples/Fe_afm.pwi diff --git a/examples/anyons.jl b/examples/anyons.jl index 05a1207bd9..c6c14cb826 100644 --- a/examples/anyons.jl +++ b/examples/anyons.jl @@ -1,4 +1,4 @@ -# # Anyons +# # Anyonic models # We solve the almost-bosonic anyon model of https://arxiv.org/pdf/1901.10739.pdf @@ -6,14 +6,14 @@ using DFTK using StaticArrays using Plots -# Unit cell. Having one of the lattice vectors as zero means a 2D system +## Unit cell. Having one of the lattice vectors as zero means a 2D system a = 14 lattice = a .* [[1 0 0.]; [0 1 0]; [0 0 0]]; -# Confining scalar potential +## Confining scalar potential pot(x, y, z) = ((x - a/2)^2 + (y - a/2)^2) -# Parameters +## Parameters Ecut = 50 n_electrons = 1 β = 5 diff --git a/examples/atomsbase.jl b/examples/atomsbase.jl index 40bd047e55..15250d5c5b 100644 --- a/examples/atomsbase.jl +++ b/examples/atomsbase.jl @@ -1,9 +1,15 @@ +# # AtomsBase integration + +# [AtomsBase.jl](https://github.com/JuliaMolSim/AtomsBase.jl) is a common interface +# for representing atomic structures in Julia. DFTK directly supports using such +# structures to run a calculation as is demonstrated here. + using DFTK using AtomsBase using Unitful using UnitfulAtomic -# Construct system in the AtomsBase world +## Construct a system in the AtomsBase world a = 10.26u"bohr" # Silicon lattice constant lattice = a / 2 * [[0, 1, 1.], # Lattice as vector of vectors [1, 0, 1.], @@ -11,15 +17,14 @@ lattice = a / 2 * [[0, 1, 1.], # Lattice as vector of vectors atoms = [:Si => ones(3)/8, :Si => -ones(3)/8] system = periodic_system(atoms, lattice; fractional=true) -# Use it inside DFTK: -# Attach pseudopotentials, construct appropriate model, -# discretise and solve. +# System is an AtomsBase-compatible system. To use it in DFTK, +# we attach pseudopotentials, construct a DFT model, discretise and solve: system = attach_psp(system; family="hgh", functional="lda") model = model_LDA(system; temperature=1e-3) basis = PlaneWaveBasis(model; Ecut=15, kgrid=[4, 4, 4]) -scfres = self_consistent_field(basis, tol=1e-8) +scfres = self_consistent_field(basis, tol=1e-8); -# Get the DFTK model back into the AtomsBase world: +# At any point we can also get back the DFTK model as an +# AtomsBase-compatible `AbstractSystem`: newsystem = atomic_system(model) -@show atomic_number(newsystem) diff --git a/examples/compare_solvers.jl b/examples/compare_solvers.jl index 61e23da713..07a6ea76eb 100644 --- a/examples/compare_solvers.jl +++ b/examples/compare_solvers.jl @@ -1,7 +1,11 @@ -## We compare here three different solvers : SCF, direct minimization and newton -## algorithm. +# # Comparison of DFT solvers -using DFTK, LinearAlgebra +# We compare four different approaches for solving the DFT minimisation problem, +# namely a density-based SCF, a potential-based SCF, direct minimisation and Newton. + +# First we setup our problem +using DFTK +using LinearAlgebra a = 10.26 # Silicon lattice constant in Bohr lattice = a / 2 * [[0 1 1.]; @@ -13,23 +17,30 @@ positions = [ones(3)/8, -ones(3)/8] model = model_LDA(lattice, atoms, positions) basis = PlaneWaveBasis(model; Ecut=5, kgrid=[3, 3, 3]) + +## Convergence we desire tol = 1e-12 +is_converged = DFTK.ScfConvergenceDensity(tol) -## SCF -scfres_scf = self_consistent_field(basis; tol=tol, - is_converged=DFTK.ScfConvergenceDensity(tol)) +# ## Density-based self-consistent field +scfres_scf = self_consistent_field(basis; is_converged) -## Direct minimization -scfres_dm = direct_minimization(basis; tol=tol) +# ## Potential-based SCF +scfres_scfv = DFTK.scf_potential_mixing(basis; is_converged) + +# ## Direct minimization +scfres_dm = direct_minimization(basis; tol) ## Newton algorithm -# start not too far from the solution to ensure convergence : we use here the -# solution of a single iteration SCF -scfres_start = self_consistent_field(basis; maxiter=1) -# remove virtual orbitals +# Start not too far from the solution to ensure convergence: +# We run first a very crude SCF to get close and then switch to Newton. +scfres_start = self_consistent_field(basis; tol=1e-1) +# Remove the virtual orbitals (which Newton cannot treat yet) ψ, _ = DFTK.select_occupied_orbitals(basis, scfres_start.ψ, scfres_start.occupation) -scfres_newton = newton(basis, ψ; tol=tol) +scfres_newton = newton(basis, ψ; tol) + +## Comparison of results -println("|ρ_newton - ρ_scf| = ", norm(scfres_newton.ρ - scfres_scf.ρ)) -println("|ρ_newton - ρ_dm| = ", norm(scfres_newton.ρ - scfres_dm.ρ)) -println("|ρ_scf - ρ_dm| = ", norm(scfres_scf.ρ - scfres_dm.ρ)) +println("|ρ_newton - ρ_scf| = ", norm(scfres_newton.ρ - scfres_scf.ρ)) +println("|ρ_newton - ρ_scfv| = ", norm(scfres_newton.ρ - scfres_scfv.ρ)) +println("|ρ_newton - ρ_dm| = ", norm(scfres_newton.ρ - scfres_dm.ρ)) diff --git a/examples/custom_potential.jl b/examples/custom_potential.jl index 2d30ae86be..4863dfcdfb 100644 --- a/examples/custom_potential.jl +++ b/examples/custom_potential.jl @@ -1,8 +1,8 @@ -# # Custom potential +# # [Custom potential](@id custom-potential) # We solve the 1D Gross-Pitaevskii equation with a custom potential. -# This is similar to [Gross-Pitaevskii equation in one dimension](@ref) and we -# show how to define local potentials attached to atoms, which allows for +# This is similar to [Gross-Pitaevskii equation in 1D example](@ref gross-pitaevskii) +# and we show how to define local potentials attached to atoms, which allows for # instance to compute forces. using DFTK using LinearAlgebra diff --git a/examples/dielectric.jl b/examples/dielectric.jl index 2d7547f8df..8a1783a403 100644 --- a/examples/dielectric.jl +++ b/examples/dielectric.jl @@ -1,22 +1,24 @@ -# Compute a few eigenvalues of the dielectric matrix (q=0,ω=0) iteratively +# # Eigenvalues of the dielectric matrix +# +# We compute a few eigenvalues of the dielectric matrix (q=0,ω=0) iteratively. using DFTK using Plots using KrylovKit using Printf -# Calculation parameters +## Calculation parameters kgrid = [1, 1, 1] Ecut = 5 -# Silicon lattice +## Silicon lattice a = 10.26 lattice = a / 2 .* [[0 1 1.]; [1 0 1.]; [1 1 0.]] Si = ElementPsp(:Si, psp=load_psp("hgh/lda/Si-q4")) atoms = [Si, Si] positions = [ones(3)/8, -ones(3)/8] -# change the symmetry to compute the dielectric operator with and without symmetries +## Compute the dielectric operator without symmetries model = model_LDA(lattice, atoms, positions, symmetries=false) basis = PlaneWaveBasis(model; Ecut, kgrid) scfres = self_consistent_field(basis, tol=1e-14) diff --git a/examples/ase.jl b/examples/gaas_surface.jl similarity index 68% rename from examples/ase.jl rename to examples/gaas_surface.jl index ddd4fd48b3..5648d6ef27 100644 --- a/examples/ase.jl +++ b/examples/gaas_surface.jl @@ -1,31 +1,12 @@ -# # Creating slabs with ASE +# # Modelling a gallium arsenide surface # -# ASE is short for the +# This example shows how to use the # [atomistic simulation environment](https://wiki.fysik.dtu.dk/ase/index.html), -# a Python package to simplify the process of setting up, running and -# analysing results from atomistic simulations across different programs. -# Extremely powerful in this respect are the routines this code provides -# for setting up complicated systems (including surface-adsorption scenarios, -# defects, nanotubes, etc). -# See also the [ASE installation instructions](https://wiki.fysik.dtu.dk/ase/install.html). -# -# This example shows how to use ASE to setup a particular gallium arsenide surface +# or ASE for short, +# to set up a particular gallium arsenide surface # and run the resulting calculation in DFTK. -# If you are less interested in having access to the full playground of options in DFTK, -# but more interested in performing analysis in ASE itself, -# have a look at [asedftk](https://github.com/mfherbst/asedftk). -# This package provides an -# [ASE-compatible calculator based on DFTK](https://github.com/mfherbst/asedftk), -# such that one may write the usual Python scripts against ASE, -# but the calculations are still run in DFTK. -# -# The particular example we consider the (1, 1, 0) GaAs surface separated by vacuum -# with the setup slightly adapted from [^RCW2001]. +# The particular example we consider the (1, 1, 0) GaAs surface separated by vacuum. # -# [^RCW2001]: -# D. Raczkowski, A. Canning, and L. W. Wang -# *Thomas-Fermi charge mixing for obtaining self-consistency in density functional calculations* -# Phys. Rev. B **64**, 121101(R). # Parameters of the calculation. Since this surface is far from easy to converge, # we made the problem simpler by choosing a smaller `Ecut` and smaller values diff --git a/examples/graphene.jl b/examples/graphene.jl index dc93bf720a..ee7513ec49 100644 --- a/examples/graphene.jl +++ b/examples/graphene.jl @@ -1,6 +1,8 @@ # # Graphene band structure -# This example plots the band structure of graphene, a 2D material. 2D band structures are not supported natively (yet), so we manually build a custom path in reciprocal space. +# This example plots the band structure of graphene, a 2D material. 2D band +# structures are not supported natively (yet), so we manually build a custom +# path in reciprocal space. using DFTK using Unitful diff --git a/examples/gross_pitaevskii.jl b/examples/gross_pitaevskii.jl index c707acaa0b..b59bcf3d35 100644 --- a/examples/gross_pitaevskii.jl +++ b/examples/gross_pitaevskii.jl @@ -1,4 +1,4 @@ -# # Gross-Pitaevskii equation in one dimension +# # [Gross-Pitaevskii equation in one dimension](@id gross-pitaevskii) # In this example we will use DFTK to solve # the Gross-Pitaevskii equation, and use this opportunity to explore a few internals. diff --git a/examples/gross_pitaevskii_2D.jl b/examples/gross_pitaevskii_2D.jl index 3921797124..0237c40373 100644 --- a/examples/gross_pitaevskii_2D.jl +++ b/examples/gross_pitaevskii_2D.jl @@ -2,7 +2,7 @@ # We solve the 2D Gross-Pitaevskii equation with a magnetic field. # This is similar to the -# previous example ([Gross-Pitaevskii equation in one dimension](@ref)), +# previous example ([Gross-Pitaevskii equation in one dimension](@ref gross-pitaevskii)), # but with an extra term for the magnetic field. # We reproduce here the results of https://arxiv.org/pdf/1611.02045.pdf Fig. 10 diff --git a/docs/src/guide/input_output.jl b/examples/input_output.jl similarity index 87% rename from docs/src/guide/input_output.jl rename to examples/input_output.jl index 948836b54c..b30fe71e00 100644 --- a/docs/src/guide/input_output.jl +++ b/examples/input_output.jl @@ -1,6 +1,4 @@ # # Input and output formats -#md # [![](https://mybinder.org/badge_logo.svg)](@__BINDER_ROOT_URL__/guide/@__NAME__.ipynb) -#md # [![](https://img.shields.io/badge/show-nbviewer-579ACA.svg)](@__NBVIEWER_ROOT_URL__/guide/@__NAME__.ipynb) # This section provides an overview of the input and output formats # supported by DFTK, usually via integration with a third-party library. @@ -22,7 +20,8 @@ # For more details about calculations on magnetic systems # using collinear spin, see [Collinear spin and magnetic systems](@ref). # -# First we parse the Quantum Espresso input file to DFTK datastructures: +# First we parse the Quantum Espresso input file to DFTK datastructures using +# the `load_atoms`, `load_positions` and `load_lattice` functions. using DFTK @@ -38,7 +37,6 @@ magnetic_moments = load_magnetic_moments(qe_input) # Next we attach the pseudopotential information, since this information is currently # not exposed inside the ASE datastructures. -# See [Creating slabs with ASE](@ref) for more details. atoms = map(atoms) do el @assert el.symbol == :Fe @@ -52,11 +50,6 @@ basis = PlaneWaveBasis(model; Ecut=10, kgrid=(2, 2, 2)) ρ0 = guess_density(basis, magnetic_moments) scfres = self_consistent_field(basis, tol=1e-4, ρ=ρ0, mixing=KerkerMixing()); -# !!! note "DFTK and ASE" -# DFTK also supports using ASE to setup a calculation -# and provides two-way conversion routines between key DFTK -# and ASE datastructures. See [Creating slabs with ASE](@ref) for details. -# # ## Writing VTK files for visualization # For visualizing the density or the Kohn-Sham orbitals DFTK supports storing # the result of an SCF calculations in the form of VTK files. diff --git a/examples/metallic_systems.jl b/examples/metallic_systems.jl index f0f3b4c05a..5cc22909b5 100644 --- a/examples/metallic_systems.jl +++ b/examples/metallic_systems.jl @@ -1,4 +1,4 @@ -# # Temperature and metallic systems +# # [Temperature and metallic systems](@id metallic-systems) # # In this example we consider the modeling of a magnesium lattice # as a simple example for a metallic system. diff --git a/examples/silicon_scf_convergence.jl b/examples/publications/2020_silicon_scf_convergence.jl similarity index 100% rename from examples/silicon_scf_convergence.jl rename to examples/publications/2020_silicon_scf_convergence.jl diff --git a/examples/papers/cazalis.jl b/examples/publications/2022_cazalis.jl similarity index 100% rename from examples/papers/cazalis.jl rename to examples/publications/2022_cazalis.jl diff --git a/examples/publications/README.md b/examples/publications/README.md new file mode 100644 index 0000000000..1d6f8f8adb --- /dev/null +++ b/examples/publications/README.md @@ -0,0 +1,4 @@ +For the full list of publications discussing DFTK, one of its algorithms +or work conducted using DFTK, see + +https://docs.dftk.org/stable/publications/ diff --git a/examples/pymatgen.jl b/examples/pymatgen.jl deleted file mode 100644 index 770c23cea5..0000000000 --- a/examples/pymatgen.jl +++ /dev/null @@ -1,41 +0,0 @@ -# # Creating supercells with pymatgen -# -# The [Pymatgen](https://pymatgen.org/) python library allows to setup -# solid-state calculations using a flexible set of classes as well as an API -# to an online data base of structures. Its `Structure` and `Lattice` -# objects are directly supported by the DFTK `load_atoms` and `load_lattice` -# functions, such that DFTK may be readily used to run calculation on systems -# defined in pymatgen. Using the `pymatgen_structure` function a conversion -# from DFTK to pymatgen structures is also possible. In the following we -# use this to create a silicon supercell and find its LDA ground state -# using direct minimisation. To run this example Julia's `PyCall` package -# needs to be able to find an installation of `pymatgen`. - -# First we setup the silicon lattice in DFTK. -using DFTK - -a = 10.263141334305942 # Lattice constant in Bohr -lattice = a / 2 .* [[0 1 1.]; [1 0 1.]; [1 1 0.]] -Si = ElementPsp(:Si, psp=load_psp("hgh/lda/Si-q4")) -atoms = [Si, Si] -positions = [ones(3)/8, -ones(3)/8]; - -# Next we make a `[2, 2, 2]` supercell using pymatgen -pystruct = pymatgen_structure(lattice, atoms, positions) -pystruct.make_supercell([2, 2, 2]) -lattice = load_lattice(pystruct) -positions = load_positions(pystruct) -atoms = fill(Si, length(positions)); - -# Setup an LDA model and discretize using -# a single k-point and a small `Ecut` of 5 Hartree. -model = model_LDA(lattice, atoms, positions) -basis = PlaneWaveBasis(model; Ecut=5, kgrid=(1, 1, 1)) - -# Find the ground state using direct minimisation and newton -# (always using SCF is boring ...) -scfres = direct_minimization(basis; tol=1e-3); -ψ, _ = DFTK.select_occupied_orbitals(basis, scfres.ψ, scfres.occupation) -scfres_newton = newton(basis, ψ; tol=1e-12) -#- -scfres_newton.energies diff --git a/examples/scf_callbacks.jl b/examples/scf_callbacks.jl index 2eecadad35..20a0f7f179 100644 --- a/examples/scf_callbacks.jl +++ b/examples/scf_callbacks.jl @@ -11,7 +11,7 @@ # We setup silicon in an LDA model using the ASE interface # to build the silicon lattice, -# see [Creating slabs with ASE](@ref) for more details. +# see [Input and output formats](@ref) for more details. using DFTK using PyCall diff --git a/examples/supercells.jl b/examples/supercells.jl new file mode 100644 index 0000000000..aa41ba6a93 --- /dev/null +++ b/examples/supercells.jl @@ -0,0 +1,107 @@ +# # Creating and modelling metallic supercells +# +# In this section we will be concerned with modelling supercells of aluminium. +# When dealing with periodic problems there is no unique definition of the +# lattice: Clearly any duplication of the lattice along an axis is also a valid +# repetitive unit to describe exactly the same system. +# This is exactly what a **supercell** is: An $n$-fold repetition along one of the +# axes of the original lattice. +# +# The following code achieves this for aluminium: + +using DFTK +using LinearAlgebra + +function aluminium_setup(repeat=1; Ecut=7.0, kgrid=[2, 2, 2]) + a = 7.65339 + lattice = a * Matrix(I, 3, 3) + Al = ElementPsp(:Al, psp=load_psp("hgh/lda/al-q3")) + atoms = [Al, Al, Al, Al] + positions = [[0.0, 0.0, 0.0], [0.0, 0.5, 0.5], [0.5, 0.0, 0.5], [0.5, 0.5, 0.0]] + + ## Make supercell in ASE: + ## We convert our lattice to the conventions used in ASE + ## and then back ... + supercell = ase_atoms(lattice, atoms, positions) * (repeat, 1, 1) + lattice = load_lattice(supercell) + positions = load_positions(supercell) + atoms = fill(Al, length(positions)) + + ## Construct an LDA model and discretise + ## Note: We disable symmetries explicitly here. Otherwise the problem sizes + ## we are able to run on the CI are too simple to observe the numerical + ## instabilities we want to trigger here. + model = model_LDA(lattice, atoms, positions; temperature=1e-3, symmetries=false) + PlaneWaveBasis(model; Ecut, kgrid) +end + +# As part of the code we are using a routine inside the ASE, +# the [atomistic simulation environment](https://wiki.fysik.dtu.dk/ase/index.html) +# for creating the supercell and make use of the two-way interoperability of +# DFTK and ASE. For more details on this aspect see the documentation +# on [Input and output formats](@ref). + +# Write an example supercell structure to a file to plot it: +setup = aluminium_setup(5) +ase_atoms(setup.model).write("al_supercell.png") + +#md # ```@raw html +#md # +#md # ``` +#nb # + +# As we will see in this notebook the modelling of a system generally becomes +# harder if the system becomes larger. +# +# - This sounds like a trival statement as *per se* the cost per SCF step increases +# as the system (and thus $N$) gets larger. +# - But there is more to it: +# If one is not careful also the *number of SCF iterations* increases +# as the system gets larger. +# - The aim of a proper computational treatment of such supercells is therefore +# to ensure that the **number of SCF iterations remains constant** when the +# system size increases. + +# For achieving the latter DFTK by default employs the `LdosMixing` +# preconditioner [^HL2021] during the SCF iterations. This mixing approach is +# completely parameter free, but still automatically adapts to the treated +# system in order to efficiently prevent charge sloshing. As a result, +# modelling aluminium slabs indeed takes roughly the same number of SCF iterations +# irrespective of the supercell size: +# +# [^HL2021]: +# M. F. Herbst and A. Levitt. +# *Black-box inhomogeneous preconditioning for self-consistent field iterations in density functional theory.* +# J. Phys. Cond. Matt *33* 085503 (2021). [ArXiv:2009.01665](https://arxiv.org/abs/2009.01665) +# + +is_converged = DFTK.ScfConvergenceDensity(1e-4) # Flag convergence based on density +self_consistent_field(aluminium_setup(1); is_converged); + +#- + +self_consistent_field(aluminium_setup(2); is_converged); + +#- + +self_consistent_field(aluminium_setup(4); is_converged, n_bands=30); + +# When switching off explicitly the `LdosMixing`, by selecting `mixing=SimpleMixing()`, +# the performance of number of required SCF steps starts to increase as we increase +# the size of the modelled problem: + +self_consistent_field(aluminium_setup(1); is_converged, mixing=SimpleMixing()); + +#- + +self_consistent_field(aluminium_setup(4); is_converged, mixing=SimpleMixing(), n_bands=30); + +# For completion let us note that the more traditional `mixing=KerkerMixing()` +# approach would also help in this particular setting to obtain a constant +# number of SCF iterations for an increasing system size (try it!). In contrast +# to `LdosMixing`, however, `KerkerMixing` is only suitable to model bulk metallic +# system (like the case we are considering here). When modelling metallic surfaces +# or mixtures of metals and insulators, `KerkerMixing` fails, while `LdosMixing` +# still works well. See the [Modelling a gallium arsenide surface](@ref) example +# or [^HL2021] for details. Due to the general applicability of `LdosMixing` this +# method is the default mixing approach in DFTK. diff --git a/test/runexamples.jl b/test/runexamples.jl index 84307bbcc5..c3ac0907ef 100644 --- a/test/runexamples.jl +++ b/test/runexamples.jl @@ -2,31 +2,23 @@ using Test function list_examples() res = String[] - basedir = joinpath(@__DIR__, "..", "examples") - for file in readdir(basedir) - fullpath = joinpath(basedir, file) - if isfile(fullpath) && endswith(file, ".jl") - push!(res, fullpath) + for (root, dirs, files) in walkdir(joinpath(@__DIR__, "..", "examples")) + for file in files + if endswith(file, ".jl") + push!(res, joinpath(root, file)) + end end end res end -function example_tags(fullpath) - open(fullpath) do fp - for l in readlines(fp) - regex = "^#src tags:" - isnothing(match(Regex(regex), l)) || return split(l[length(regex):end]) - end - return Vector{String}() - end -end - @testset "Run examples" begin - for file in list_examples() - "long" in example_tags(file) && continue + for path in list_examples() + dir, file = splitdir(path) @testset "$(file)" begin - include(file) + cd(dir) do + include(path) + end end end end From 69f534c28261be200ac5488f7638b5bcacb01504 Mon Sep 17 00:00:00 2001 From: Antoine Levitt Date: Mon, 25 Jul 2022 13:10:50 +0200 Subject: [PATCH 02/26] minor corrections in docs --- docs/src/developer/symmetries.md | 2 +- docs/src/guide/periodic_problems.jl | 12 +++++------- docs/src/guide/tutorial.jl | 20 +++++++++++++------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/docs/src/developer/symmetries.md b/docs/src/developer/symmetries.md index 38b7a9fe05..ad9bf60bf2 100644 --- a/docs/src/developer/symmetries.md +++ b/docs/src/developer/symmetries.md @@ -1,4 +1,4 @@ -# Crystal symmetries +# [Crystal symmetries](@id crystal-symmetries) ## Theory In this discussion we will only describe the situation for a monoatomic crystal ``\mathcal C \subset \mathbb R^3``, the extension being easy. diff --git a/docs/src/guide/periodic_problems.jl b/docs/src/guide/periodic_problems.jl index c9fd47321e..5023cf612a 100644 --- a/docs/src/guide/periodic_problems.jl +++ b/docs/src/guide/periodic_problems.jl @@ -14,10 +14,10 @@ # \sin(x) = \sin(x + 2πm) \quad ∀ m ∈ \mathbb{Z}, # ``` # This is nothing else than saying that any translation by an integer multiple of ``2π`` -# keeps the ``\sin`` function invariant. In a more formal one can use the -# translation operator ``T_{-2πm}`` to write this as: +# keeps the ``\sin`` function invariant. More formally, one can use the +# translation operator ``T_{2πm}`` to write this as: # ```math -# T_{-2πm} \, \sin(x) = \sin(x + 2πm) = \sin(x). +# T_{2πm} \, \sin(x) = \sin(x - 2πm) = \sin(x). # ``` # # Whenever such periodicity exists one can exploit it to save computational work. @@ -36,9 +36,7 @@ # ``` # with lattice constant ``a``. Each cell of the lattice is an identical periodic image of # any of its neighbors. For finding ``f`` it is thus sufficient to consider only the -# problem inside a **unit cell** ``[-a/2, a/2)``. In passing we note that the definition -# of the unit cell is itself only unique up to translations. A choice ``[0, a)``, -# for example, would have done just as well. +# problem inside a **unit cell** ``[-a/2, a/2)`` (this is the convention used by DFTK, but this is arbitrary, and for instance ``[0,a)`` would have worked just as well). # # ## Periodic operators and the Bloch transform # Not only functions, but also operators can feature periodicity. @@ -133,7 +131,7 @@ # of all ``H_k`` blocks. # # [^1]: Notice that block-diagonal is a bit an abuse of terms here, since the Hamiltonian -# is not a matrix but an operator and the number of blocks is essentially infinite. +# is not a matrix but an operator and the number of blocks is infinite. # The mathematically precise term is that the Bloch transform reveals the fibers # of the Hamiltonian. # diff --git a/docs/src/guide/tutorial.jl b/docs/src/guide/tutorial.jl index e5deced792..8483df6667 100644 --- a/docs/src/guide/tutorial.jl +++ b/docs/src/guide/tutorial.jl @@ -37,7 +37,7 @@ using UnitfulAtomic a = 5.431u"angstrom" # Silicon lattice constant lattice = a / 2 * [[0 1 1.]; # Silicon lattice vectors [1 0 1.]; # specified column by column - [1 1 0.]] + [1 1 0.]]; # By default, all numbers passed as arguments are assumed to be in atomic # units. Quantities such as temperature, energy cutoffs, lattice vectors, and @@ -78,11 +78,17 @@ hcat(scfres.eigenvalues...) # matrix. # # The resulting matrix is 7 (number of computed eigenvalues) by 8 -# (number of k-points). There are 7 eigenvalues per k-point because -# there are 4 occupied states in the system (4 valence electrons per -# silicon atom, two atoms per unit cell, and paired spins), and the -# eigensolver gives itself some breathing room by computing some extra -# states (see `n_ep_extra` argument to `self_consistent_field`). +# (number of irreducible k-points). There are 7 eigenvalues per +# k-point because there are 4 occupied states in the system (4 valence +# electrons per silicon atom, two atoms per unit cell, and paired +# spins), and the eigensolver gives itself some breathing room by +# computing some extra states (see `n_ep_extra` argument to +# `self_consistent_field`). There are only 8 k-points (instead of +# 4x4x4) because symmetry has been used to reduce the amount of +# computations to just the irreducible k-points (see +#md # [here](@ref crystal-symmetries) +#nb # [here](https://docs.dftk.org/stable/developer/symmetries/) +# for details). # # We can check the occupations ... hcat(scfres.occupation...) @@ -98,4 +104,4 @@ plot(x, scfres.ρ[1, :, 1, 1], label="", xlabel="x", ylabel="ρ", marker=2) plot_bandstructure(scfres; kline_density=10) # or get the cartesian forces (in Hartree / Bohr) compute_forces_cart(scfres) -# As expected, they are almost zero in this highly symmetric configuration. +# As expected, they are numerically zero in this highly symmetric configuration. From 5081905437307d24f1ef92f1a82275f8f2f2bf83 Mon Sep 17 00:00:00 2001 From: Antoine Levitt Date: Mon, 25 Jul 2022 14:06:45 +0200 Subject: [PATCH 03/26] minor doc fix --- docs/src/developer/symmetries.md | 2 +- docs/src/guide/tutorial.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/developer/symmetries.md b/docs/src/developer/symmetries.md index ad9bf60bf2..38b7a9fe05 100644 --- a/docs/src/developer/symmetries.md +++ b/docs/src/developer/symmetries.md @@ -1,4 +1,4 @@ -# [Crystal symmetries](@id crystal-symmetries) +# Crystal symmetries ## Theory In this discussion we will only describe the situation for a monoatomic crystal ``\mathcal C \subset \mathbb R^3``, the extension being easy. diff --git a/docs/src/guide/tutorial.jl b/docs/src/guide/tutorial.jl index 8483df6667..b156e41fba 100644 --- a/docs/src/guide/tutorial.jl +++ b/docs/src/guide/tutorial.jl @@ -86,8 +86,8 @@ hcat(scfres.eigenvalues...) # `self_consistent_field`). There are only 8 k-points (instead of # 4x4x4) because symmetry has been used to reduce the amount of # computations to just the irreducible k-points (see -#md # [here](@ref crystal-symmetries) -#nb # [here](https://docs.dftk.org/stable/developer/symmetries/) +#md # [Crystal symmetries](@ref) +#nb # [Crystal symmetries](https://docs.dftk.org/stable/developer/symmetries/) # for details). # # We can check the occupations ... From 30fe9ea8218732980ef2e887caa54a03014f884d Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Fri, 29 Jul 2022 16:22:41 +0200 Subject: [PATCH 04/26] Fix dual handling for parametrised functionals (#699) --- Project.toml | 2 +- src/PlaneWaveBasis.jl | 2 +- src/terms/xc.jl | 11 +++++++++-- src/workarounds/forwarddiff_rules.jl | 2 +- test/forwarddiff.jl | 26 ++++++++++++++++++++++++++ test/interval_arithmetic.jl | 4 +++- 6 files changed, 41 insertions(+), 6 deletions(-) diff --git a/Project.toml b/Project.toml index 272578f7be..e15082259d 100644 --- a/Project.toml +++ b/Project.toml @@ -52,7 +52,7 @@ BlockArrays = "0.16.2" Brillouin = "0.5" ChainRulesCore = "1.15" Conda = "1" -DftFunctionals = "0.1" +DftFunctionals = "0.2" FFTW = "1" ForwardDiff = "0.10" InteratomicPotentials = "0.2" diff --git a/src/PlaneWaveBasis.jl b/src/PlaneWaveBasis.jl index 219f2315bf..43cc911a5f 100644 --- a/src/PlaneWaveBasis.jl +++ b/src/PlaneWaveBasis.jl @@ -292,7 +292,7 @@ end else factors = (1,) end - fft_size = compute_fft_size(model, Ecut, kcoords; factors=factors) + fft_size = compute_fft_size(model, Ecut, kcoords; factors) end PlaneWaveBasis(model, Ecut, fft_size, variational, kcoords, kweights, kgrid, kshift, symmetries_respect_rgrid, comm_kpts) diff --git a/src/terms/xc.jl b/src/terms/xc.jl index 6dc37bb3ff..4b1f6e9966 100644 --- a/src/terms/xc.jl +++ b/src/terms/xc.jl @@ -25,9 +25,16 @@ function Base.show(io::IO, xc::Xc) print(io, "Xc($fun$fac)") end -function (xc::Xc)(basis::PlaneWaveBasis{T}) where {T} +function (xc::Xc)(::PlaneWaveBasis{T}) where {T} isempty(xc.functionals) && return TermNoop() - TermXc(xc.functionals, convert_dual(T, xc.scaling_factor), T(xc.potential_threshold)) + functionals = map(xc.functionals) do fun + # Strip duals from functional parameters if needed + newparams = convert_dual.(T, parameters(fun)) + change_parameters(fun, newparams; keep_identifier=true) + end + TermXc(convert(Vector{Functional}, functionals), + convert_dual(T, xc.scaling_factor), + T(xc.potential_threshold)) end struct TermXc{T} <: TermNonlinear where {T} diff --git a/src/workarounds/forwarddiff_rules.jl b/src/workarounds/forwarddiff_rules.jl index 44194e05eb..3d5c877867 100644 --- a/src/workarounds/forwarddiff_rules.jl +++ b/src/workarounds/forwarddiff_rules.jl @@ -135,7 +135,7 @@ function _is_well_conditioned(A::AbstractArray{<:ForwardDiff.Dual}; kwargs...) _is_well_conditioned(ForwardDiff.value.(A); kwargs...) end -value_type(T::Type{<:ForwardDiff.Dual}) = ForwardDiff.valtype(T) +value_type(T::Type{<:ForwardDiff.Dual}) = value_type(ForwardDiff.valtype(T)) # TODO Should go to Model.jl / PlaneWaveBasis.jl as a constructor. # diff --git a/test/forwarddiff.jl b/test/forwarddiff.jl index 58fe620eaf..cb87bcdacc 100644 --- a/test/forwarddiff.jl +++ b/test/forwarddiff.jl @@ -86,3 +86,29 @@ end derivative_fd = ForwardDiff.derivative(compute_band_energies, 0.0) @test norm(derivative_fd - derivative_ε) < 1e-4 end + +@testset "Functional force sensitivity using ForwardDiff" begin + using DftFunctionals + + function compute_force(ε1::T) where {T} + pos = [[1.01, 1.02, 1.03] / 8, -ones(3) / 8] + pbec = DftFunctional(:gga_c_pbe) + pbex = DftFunctional(:gga_x_pbe) + pbex = change_parameters(pbex, parameters(pbex) + ComponentArray(κ=0, μ=ε1)) + + model = model_DFT(Matrix{T}(silicon.lattice), silicon.atoms, pos, [pbex, pbec]) + basis = PlaneWaveBasis(model; Ecut=5, kgrid=[2, 2, 2], kshift=[0, 0, 0]) + + is_converged = DFTK.ScfConvergenceDensity(1e-10) + scfres = self_consistent_field(basis; + is_converged, response=ResponseOptions(verbose=true)) + compute_forces_cart(scfres) + end + + derivative_ε = let ε = 1e-5 + (compute_force(ε) - compute_force(-ε)) / 2ε + end + derivative_fd = ForwardDiff.derivative(compute_force, 0.0) + @show derivative_ε derivative_fd + @test norm(derivative_ε - derivative_fd) < 1e-4 +end diff --git a/test/interval_arithmetic.jl b/test/interval_arithmetic.jl index 6e31d8ec76..5584335297 100644 --- a/test/interval_arithmetic.jl +++ b/test/interval_arithmetic.jl @@ -1,7 +1,6 @@ using Test using DFTK using GenericLinearAlgebra -using IntervalArithmetic include("testcases.jl") @@ -19,6 +18,8 @@ function discretized_hamiltonian(T, testcase) end @testset "Application of an LDA Hamiltonian with Intervals" begin + import IntervalArithmetic: Interval, radius, mid + T = Float64 ham = discretized_hamiltonian(T, silicon) hamInt = discretized_hamiltonian(Interval{T}, silicon) @@ -39,6 +40,7 @@ end end @testset "compute_occupation with Intervals" begin + import IntervalArithmetic: Interval, mid testcase = silicon model = model_LDA(Matrix{Interval{Float64}}(testcase.lattice), From a3bc6176b99cb76fcfc0a6ec046cac6ed3330a33 Mon Sep 17 00:00:00 2001 From: Gaspard Kemlin Date: Tue, 2 Aug 2022 14:16:40 +0200 Subject: [PATCH 05/26] Typo in dos.jl (#702) Typo in the top comments on LD definition + adding more details in `plots_dos` docstring. --- src/postprocess/dos.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/postprocess/dos.jl b/src/postprocess/dos.jl index 99fb573d5e..bf5c40ec67 100644 --- a/src/postprocess/dos.jl +++ b/src/postprocess/dos.jl @@ -5,7 +5,7 @@ # DOS (density of states) # D(ε) = N'(ε) # LDOS (local density of states) -# LD = sum_n f_n ψn +# LD = sum_n f_n |ψn|^2 """ Total density of states at energy ε @@ -54,6 +54,6 @@ function compute_ldos(ε, basis, eigenvalues, ψ; smearing=basis.model.smearing, end """ -Plot the density of states over a reasonable range +Plot the density of states over a reasonable range. Requires to load `Plots.jl` beforehand. """ function plot_dos end From f28b9ecf1721cc5b19c137ecb3296f4688e45318 Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Tue, 16 Aug 2022 13:26:28 +0200 Subject: [PATCH 06/26] fix spglib (#707) --- Project.toml | 2 +- src/external/spglib.jl | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index e15082259d..9ee35dd367 100644 --- a/Project.toml +++ b/Project.toml @@ -74,7 +74,7 @@ PyCall = "1" Requires = "1" Roots = "1, 2" SpecialFunctions = "1, 2" -Spglib = "0.5" +Spglib = "0.6" StaticArrays = "1" TimerOutputs = "0.5.11" Unitful = "1" diff --git a/src/external/spglib.jl b/src/external/spglib.jl index ab1ee42e19..5ca2bcd474 100644 --- a/src/external/spglib.jl +++ b/src/external/spglib.jl @@ -28,6 +28,9 @@ function spglib_atoms(atom_groups, spg_numbers = zeros(Cint, n_attypes) spg_spins = zeros(Cdouble, n_attypes) spg_positions = zeros(Cdouble, 3, n_attypes) + # Note: The storage format now used in Spglib.Cell is vector of vectors of length 3 + # but we stick to the matrix-based storage format for now to keep compatibility + # spglib_jll.libsymspg. arbitrary_spin = false offset = 0 @@ -164,7 +167,7 @@ function spglib_standardize_cell(lattice::AbstractArray{T}, atom_groups, positio no_idealize=!correct_symmetry) lattice = Matrix{T}(std_cell.lattice) - positions = Vec3{T}.(eachcol(std_cell.positions)) + positions = Vec3{T}.(std_cell.positions) magnetic_moments = normalize_magnetic_moment.(std_cell.magmoms) (; lattice, atom_groups, positions, magnetic_moments) end From 0684562907de3f6e70e2641fe20ad14f24360cb1 Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Tue, 16 Aug 2022 17:39:59 +0200 Subject: [PATCH 07/26] =?UTF-8?q?Bump=20version:=200.5.5=20=E2=86=92=200.5?= =?UTF-8?q?.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- Project.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 6b0ebeb324..85a07e7471 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.5.5 +current_version = 0.5.6 commit = True tag = False diff --git a/Project.toml b/Project.toml index 9ee35dd367..27715904b8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "DFTK" uuid = "acf6eb54-70d9-11e9-0013-234b7a5f5337" authors = ["Michael F. Herbst ", "Antoine Levitt "] -version = "0.5.5" +version = "0.5.6" [deps] AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c" From 06dbf3b3bcb56b87b53e090c36398aaafa806d40 Mon Sep 17 00:00:00 2001 From: Gabriel Birnbaum Date: Fri, 26 Aug 2022 12:34:43 -0400 Subject: [PATCH 08/26] Add documentation about conducting a convergence study (#701) Co-authored-by: Michael F. Herbst --- docs/make.jl | 1 + docs/src/assets/pregenerated/0_pregenerate.jl | 18 ++++ .../pregenerated/convergence_study_ecut.png | Bin 0 -> 87469 bytes .../pregenerated/convergence_study_kgrid.png | Bin 0 -> 89081 bytes examples/convergence_study.jl | 88 ++++++++++++++++++ 5 files changed, 107 insertions(+) create mode 100644 docs/src/assets/pregenerated/0_pregenerate.jl create mode 100644 docs/src/assets/pregenerated/convergence_study_ecut.png create mode 100644 docs/src/assets/pregenerated/convergence_study_kgrid.png create mode 100644 examples/convergence_study.jl diff --git a/docs/make.jl b/docs/make.jl index c5b636b9fb..0ceafb7563 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -30,6 +30,7 @@ PAGES = [ # Some basic show cases; may feature integration of DFTK with other packages. "examples/metallic_systems.jl", "examples/collinear_magnetism.jl", + "examples/convergence_study.jl", "examples/supercells.jl", "examples/gaas_surface.jl", "examples/graphene.jl", diff --git a/docs/src/assets/pregenerated/0_pregenerate.jl b/docs/src/assets/pregenerated/0_pregenerate.jl new file mode 100644 index 0000000000..0cc613fdc4 --- /dev/null +++ b/docs/src/assets/pregenerated/0_pregenerate.jl @@ -0,0 +1,18 @@ +using MKL +using DFTK +setup_threading(n_blas=2) + +let + include("../../../../examples/convergence_study.jl") + + result = converge_kgrid(nkpts; Ecut=mean(Ecuts), tol) + nkpt_conv = result.nkpt_conv + p = plot(result.nkpts, result.errors, dpi=300, lw=3, m=:o, yaxis=:log, + xlabel="k-grid", ylabel="energy absolute error") + savefig(p, "convergence_study_kgrid.png") + + result = converge_Ecut(Ecuts; nkpt=nkpt_conv, tol) + p = plot(result.Ecuts, result.errors, dpi=300, lw=3, m=:o, yaxis=:log, + xlabel="Ecut", ylabel="energy absolute error") + savefig(p, "convergence_study_ecut.png") +end diff --git a/docs/src/assets/pregenerated/convergence_study_ecut.png b/docs/src/assets/pregenerated/convergence_study_ecut.png new file mode 100644 index 0000000000000000000000000000000000000000..bf2b01bb0bdcd648f34b05dfc9d0af63ec2271b2 GIT binary patch literal 87469 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�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

w((=JV7!N`4{r3(@{N`zot*lSyzWze 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 qOrAnj~>8&N(fNE^hjQSv#BCXu)dA;&mtqjEkHV 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 +#md # +#md # ``` +#nb # +#nb # From 40fb53d7a6bbdeea98cac9a6d5e3e52aa992c509 Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Sat, 27 Aug 2022 12:41:57 +0200 Subject: [PATCH 09/26] Fix some image links in documentation (#713) --- .../{pregenerated => }/0_pregenerate.jl | 4 +-- .../convergence_study_ecut.png | Bin .../convergence_study_kgrid.png | Bin examples/convergence_study.jl | 24 +++++++++--------- examples/gaas_surface.jl | 2 +- examples/supercells.jl | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) rename docs/src/assets/{pregenerated => }/0_pregenerate.jl (77%) rename docs/src/assets/{pregenerated => }/convergence_study_ecut.png (100%) rename docs/src/assets/{pregenerated => }/convergence_study_kgrid.png (100%) diff --git a/docs/src/assets/pregenerated/0_pregenerate.jl b/docs/src/assets/0_pregenerate.jl similarity index 77% rename from docs/src/assets/pregenerated/0_pregenerate.jl rename to docs/src/assets/0_pregenerate.jl index 0cc613fdc4..4493d37ea7 100644 --- a/docs/src/assets/pregenerated/0_pregenerate.jl +++ b/docs/src/assets/0_pregenerate.jl @@ -8,11 +8,11 @@ let result = converge_kgrid(nkpts; Ecut=mean(Ecuts), tol) nkpt_conv = result.nkpt_conv p = plot(result.nkpts, result.errors, dpi=300, lw=3, m=:o, yaxis=:log, - xlabel="k-grid", ylabel="energy absolute error") + xlabel="k-grid", ylabel="energy absolute error", label="") savefig(p, "convergence_study_kgrid.png") result = converge_Ecut(Ecuts; nkpt=nkpt_conv, tol) p = plot(result.Ecuts, result.errors, dpi=300, lw=3, m=:o, yaxis=:log, - xlabel="Ecut", ylabel="energy absolute error") + xlabel="Ecut", ylabel="energy absolute error", label="") savefig(p, "convergence_study_ecut.png") end diff --git a/docs/src/assets/pregenerated/convergence_study_ecut.png b/docs/src/assets/convergence_study_ecut.png similarity index 100% rename from docs/src/assets/pregenerated/convergence_study_ecut.png rename to docs/src/assets/convergence_study_ecut.png diff --git a/docs/src/assets/pregenerated/convergence_study_kgrid.png b/docs/src/assets/convergence_study_kgrid.png similarity index 100% rename from docs/src/assets/pregenerated/convergence_study_kgrid.png rename to docs/src/assets/convergence_study_kgrid.png diff --git a/examples/convergence_study.jl b/examples/convergence_study.jl index 6a89c3964b..f42f43c24b 100644 --- a/examples/convergence_study.jl +++ b/examples/convergence_study.jl @@ -32,15 +32,15 @@ function run_scf(; a=5.0, Ecut, nkpt, tol) basis = PlaneWaveBasis(model; Ecut, kgrid=(nkpt, nkpt, nkpt)) println("nkpt = $nkpt Ecut = $Ecut") self_consistent_field(basis; tol) -end +end; # Moreover we define some parameters. To make the calculations run fast for the # automatic generation of this documentation we target only a convergence to # 1e-2. In practice smaller tolerances (and thus larger upper bounds for # `nkpts` and `Ecuts` are likely needed. -tol = 1e-2 # Tolerance to which we target to converge -nkpts = 1:7 # K-point range checked for convergence -Ecuts = 10:2:24 # Energy cutoff range checked for convergence +tol = 1e-2 # Tolerance to which we target to converge +nkpts = 1:7 # K-point range checked for convergence +Ecuts = 10:2:24; # Energy cutoff range checked for convergence # As the first step we converge in the number of kpoints employed in each # dimension of the Brillouin zone. @@ -72,17 +72,17 @@ Ecut_conv = result.Ecut_conv plot(result.Ecuts, result.errors, dpi=300, lw=3, m=:o, yaxis=:log, xlabel="Ecut", ylabel="energy absolute error") -## A more realistic example. -# Repeating the above exercise for more realistic settings, namely +# ## A more realistic example. +# Repeating the above exercise for more realistic settings, namely ... tol = 1e-4 # Tolerance to which we target to converge nkpts = 1:20 # K-point range checked for convergence -Ecuts = 20:1:50 +Ecuts = 20:1:50; -# one obtains the following two plots for the convergence in `kpoints` and `Ecut`. +# ...one obtains the following two plots for the convergence in `kpoints` and `Ecut`. #md # ```@raw html -#md # -#md # +#md # +#md # #md # ``` -#nb # -#nb # +#nb # +#nb # diff --git a/examples/gaas_surface.jl b/examples/gaas_surface.jl index 5648d6ef27..e2c5a59acc 100644 --- a/examples/gaas_surface.jl +++ b/examples/gaas_surface.jl @@ -38,7 +38,7 @@ pyimport("ase.io").write("surface.png", surface * (3, 3, 1), #md # ```@raw html #md # #md # ``` -#nb # +#nb # # Use the `load_atoms`, `load_positions` and `load_lattice` functions # to convert to DFTK datastructures. diff --git a/examples/supercells.jl b/examples/supercells.jl index aa41ba6a93..ca6228cb1d 100644 --- a/examples/supercells.jl +++ b/examples/supercells.jl @@ -48,7 +48,7 @@ ase_atoms(setup.model).write("al_supercell.png") #md # ```@raw html #md # #md # ``` -#nb # +#nb # # As we will see in this notebook the modelling of a system generally becomes # harder if the system becomes larger. From 222ae6ad08612f878db0cd1ad43fedd071919815 Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Mon, 29 Aug 2022 11:53:20 +0200 Subject: [PATCH 10/26] =?UTF-8?q?Bump=20version:=200.5.6=20=E2=86=92=200.5?= =?UTF-8?q?.7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- Project.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 85a07e7471..897229fe20 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.5.6 +current_version = 0.5.7 commit = True tag = False diff --git a/Project.toml b/Project.toml index 27715904b8..e763131a6a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "DFTK" uuid = "acf6eb54-70d9-11e9-0013-234b7a5f5337" authors = ["Michael F. Herbst ", "Antoine Levitt "] -version = "0.5.6" +version = "0.5.7" [deps] AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c" From 77ef797821f91e811973202a9df31b96f8556561 Mon Sep 17 00:00:00 2001 From: Antoine Levitt Date: Tue, 30 Aug 2022 09:44:49 +0200 Subject: [PATCH 11/26] typo --- src/workarounds/forwarddiff_rules.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workarounds/forwarddiff_rules.jl b/src/workarounds/forwarddiff_rules.jl index 3d5c877867..24c7ef1bb7 100644 --- a/src/workarounds/forwarddiff_rules.jl +++ b/src/workarounds/forwarddiff_rules.jl @@ -201,7 +201,7 @@ function self_consistent_field(basis_dual::PlaneWaveBasis{T}; basis_primal = construct_value(basis_dual) scfres = self_consistent_field(basis_primal; kwargs...) - ## Compute external perturbation (contained in ham_dual) and from matvec with bands + ## Compute external perturbation (contained in ham_dual) and form matvec with bands ham_dual, Hψ_dual = let occupation_dual = [T.(occk) for occk in scfres.occupation] ψ_dual = [Complex.(T.(real(ψk)), T.(imag(ψk))) for ψk in scfres.ψ] From ce348a913338d54d6aaef8787b7b255f44c0506d Mon Sep 17 00:00:00 2001 From: Niklas Schmitz Date: Wed, 31 Aug 2022 07:19:49 +0200 Subject: [PATCH 12/26] ForwardDiff: Return dual energies in scfres (#709) Co-authored-by: Gaspard Kemlin --- src/response/chi0.jl | 43 ++++++++++++++++++---------- src/response/hessian.jl | 18 ++++++------ src/workarounds/forwarddiff_rules.jl | 26 ++++++++++++----- test/forwarddiff.jl | 7 +++-- test/hessian.jl | 7 +++++ test/stresses.jl | 6 ++++ 6 files changed, 75 insertions(+), 32 deletions(-) diff --git a/src/response/chi0.jl b/src/response/chi0.jl index ebdee6ba68..06dcc4ec6a 100644 --- a/src/response/chi0.jl +++ b/src/response/chi0.jl @@ -270,21 +270,27 @@ end if temperature > 0 # First compute δocc without self-consistent Fermi δεF D = zero(T) - for ik = 1:Nk - for (n, εnk) in enumerate(ε_occ[ik]) - enred = (εnk - εF) / temperature - δεnk = real(dot(ψ_occ[ik][:, n], δHψ[ik][:, n])) - fpnk = (filled_occ - * Smearing.occupation_derivative(model.smearing, enred) - / temperature) - δocc[ik][n] = δεnk * fpnk - D += fpnk * basis.kweights[ik] - end + for ik = 1:Nk, (n, εnk) in enumerate(ε_occ[ik]) + enred = (εnk - εF) / temperature + δεnk = real(dot(ψ_occ[ik][:, n], δHψ[ik][:, n])) + fpnk = (filled_occ + * Smearing.occupation_derivative(model.smearing, enred) + / temperature) + δocc[ik][n] = δεnk * fpnk + D += fpnk * basis.kweights[ik] end # compute δεF D = mpi_sum(D, basis.comm_kpts) # equal to minus the total DOS δocc_tot = mpi_sum(sum(basis.kweights .* sum.(δocc)), basis.comm_kpts) δεF = δocc_tot / D + # recompute δocc + for ik = 1:Nk, (n, εnk) in enumerate(ε_occ[ik]) + enred = (εnk - εF) / temperature + fpnk = (filled_occ + * Smearing.occupation_derivative(model.smearing, enred) + / temperature) + δocc[ik][n] -= fpnk * δεF + end end # compute δψnk band per band @@ -303,7 +309,7 @@ end ddiff = Smearing.occupation_divided_difference ratio = filled_occ * ddiff(model.smearing, εk[m], εk[n], εF, temperature) αmn = compute_αmn(fmk, fnk, ratio) # fnk * αmn + fmk * αnm = ratio - δψk[:, n] .+= ψk[:, m] .* αmn .* (dot(ψk[:, m], δHψ[ik][:, n]) - (n == m) * δεF) + δψk[:, n] .+= ψk[:, m] .* αmn .* (dot(ψk[:, m], δHψ[ik][:, n]) * (n != m)) end # Sternheimer contribution @@ -312,9 +318,16 @@ end kwargs_sternheimer...) end end + + # pad δoccupation + δoccupation = zero.(occ) + for (ik, maskk) in enumerate(mask_occ) + δoccupation[ik][maskk] .+= δocc[ik] + end + # keeping zeros for extra bands to keep the output δψ with the same size # than the input ψ - δψ + (; δψ, δoccupation, δεF) end """ @@ -341,9 +354,9 @@ function apply_χ0(ham, ψ, occupation, εF, eigenvalues, δV; δHψ = [DFTK.RealSpaceMultiplication(basis, kpt, @views δV[:, :, :, kpt.spin]) * ψ[ik] for (ik, kpt) in enumerate(basis.kpoints)] - δψ = apply_χ0_4P(ham, ψ, occupation, εF, eigenvalues, δHψ; - occupation_threshold, kwargs_sternheimer...) - δρ = DFTK.compute_δρ(basis, ψ, δψ, occupation) + δψ, δoccupation, δεF = apply_χ0_4P(ham, ψ, occupation, εF, eigenvalues, δHψ; + occupation_threshold, kwargs_sternheimer...) + δρ = DFTK.compute_δρ(basis, ψ, δψ, occupation, δoccupation) δρ * normδV end diff --git a/src/response/hessian.jl b/src/response/hessian.jl index 458b496e30..e02e8ff0c7 100644 --- a/src/response/hessian.jl +++ b/src/response/hessian.jl @@ -138,10 +138,10 @@ function solve_ΩplusK_split(ham::Hamiltonian, ρ::AbstractArray{T}, ψ, occupat @assert size(rhs[1]) == size(ψ[1]) # Assume the same number of bands in ψ and rhs # compute δρ0 (ignoring interactions) - δψ0 = apply_χ0_4P(ham, ψ, occupation, εF, eigenvalues, -rhs; - reltol=0, abstol=tol_sternheimer, - occupation_threshold, kwargs...) # = -χ04P * rhs - δρ0 = compute_δρ(basis, ψ, δψ0, occupation) + δψ0, δoccupation0, δεF0 = apply_χ0_4P(ham, ψ, occupation, εF, eigenvalues, -rhs; + reltol=0, abstol=tol_sternheimer, + occupation_threshold, kwargs...) # = -χ04P * rhs + δρ0 = compute_δρ(basis, ψ, δψ0, occupation, δoccupation0) # compute total δρ pack(δρ) = vec(δρ) @@ -175,9 +175,11 @@ function solve_ΩplusK_split(ham::Hamiltonian, ρ::AbstractArray{T}, ψ, occupat end end - δψ = apply_χ0_4P(ham, ψ, occupation, εF, eigenvalues, δHψ; - occupation_threshold, abstol=tol_sternheimer, reltol=0, kwargs...) - (; δψ, δρ, δHψ, δVind, δeigenvalues, history) + δψ, δoccupation, δεF = apply_χ0_4P(ham, ψ, occupation, εF, eigenvalues, δHψ; + occupation_threshold, abstol=tol_sternheimer, + reltol=0, kwargs...) + + (; δψ, δρ, δHψ, δVind, δeigenvalues, δoccupation, δεF, history) end function solve_ΩplusK_split(basis::PlaneWaveBasis, ψ, rhs, occupation; kwargs...) @@ -185,7 +187,7 @@ function solve_ΩplusK_split(basis::PlaneWaveBasis, ψ, rhs, occupation; kwargs. _, H = energy_hamiltonian(basis, ψ, occupation; ρ) eigenvalues = [real.(eigvals(ψk'Hψk)) for (ψk, Hψk) in zip(ψ, H * ψ)] - occupation_threshold = kwargs.occupation_threshold + occupation_threshold = kwargs[:occupation_threshold] occupation, εF = compute_occupation(basis, eigenvalues; occupation_threshold) solve_ΩplusK_split(H, ρ, ψ, occupation, εF, eigenvalues, rhs; kwargs...) diff --git a/src/workarounds/forwarddiff_rules.jl b/src/workarounds/forwarddiff_rules.jl index 24c7ef1bb7..d7422f6dc2 100644 --- a/src/workarounds/forwarddiff_rules.jl +++ b/src/workarounds/forwarddiff_rules.jl @@ -198,19 +198,20 @@ function self_consistent_field(basis_dual::PlaneWaveBasis{T}; # Note: No guarantees on this interface yet. # Primal pass - basis_primal = construct_value(basis_dual) + basis_primal = construct_value(basis_dual) scfres = self_consistent_field(basis_primal; kwargs...) - ## Compute external perturbation (contained in ham_dual) and form matvec with bands - ham_dual, Hψ_dual = let + ## Compute external perturbation (contained in ham_dual) and from matvec with bands + Hψ_dual = let occupation_dual = [T.(occk) for occk in scfres.occupation] ψ_dual = [Complex.(T.(real(ψk)), T.(imag(ψk))) for ψk in scfres.ψ] ρ_dual = DFTK.compute_density(basis_dual, ψ_dual, occupation_dual) εF_dual = T(scfres.εF) # Only needed for entropy term eigenvalues_dual = [T.(εk) for εk in scfres.eigenvalues] _, ham_dual = energy_hamiltonian(basis_dual, ψ_dual, occupation_dual; - ρ=ρ_dual, eigenvalues=eigenvalues_dual, εF=εF_dual) - ham_dual, ham_dual * ψ_dual + ρ=ρ_dual, eigenvalues=eigenvalues_dual, + εF=εF_dual) + ham_dual * ψ_dual end ## Implicit differentiation @@ -232,12 +233,23 @@ function self_consistent_field(basis_dual::PlaneWaveBasis{T}; eigenvalues = map(scfres.eigenvalues, getfield.(δresults, :δeigenvalues)...) do εk, δεk... map((εnk, δεnk...) -> DT(εnk, δεnk), εk, δεk...) end + occupation = map(scfres.occupation, getfield.(δresults, :δoccupation)...) do occk, δocck... + map((occnk, δoccnk...) -> DT(occnk, δoccnk), occk, δocck...) + end + εF = DT(scfres.εF, getfield.(δresults, :δεF)...) # TODO Could add δresults[α].δVind the dual part of the total local potential in ham_dual # and in this way return a ham that represents also the total change in Hamiltonian - merge(scfres, (; ψ, ρ, eigenvalues, basis=basis_dual, - response=getfield.(δresults, :history))) + energies, ham = energy_hamiltonian(basis_dual, ψ, occupation; ρ, eigenvalues, εF) + + # This has to be changed whenever the scfres structure changes + (; ham, basis=basis_dual, energies, ρ, eigenvalues, occupation, εF, ψ, + # non-differentiable metadata: + response=getfield.(δresults, :history), + scfres.converged, scfres.occupation_threshold, scfres.α, scfres.n_iter, + scfres.n_ep_extra, scfres.diagonalization, scfres.stage, + scfres.algorithm, scfres.norm_Δρ) end # other workarounds diff --git a/test/forwarddiff.jl b/test/forwarddiff.jl index cb87bcdacc..fc55eaee74 100644 --- a/test/forwarddiff.jl +++ b/test/forwarddiff.jl @@ -76,7 +76,10 @@ end ComponentArray( eigenvalues=hcat([ev[1:end-3] for ev in scfres.eigenvalues]...), - ρ=scfres.ρ + ρ=scfres.ρ, + energies=collect(values(scfres.energies)), + εF=scfres.εF, + occupation=vcat(scfres.occupation...), ) end @@ -84,7 +87,7 @@ end (compute_band_energies(ε) - compute_band_energies(-ε)) / 2ε end derivative_fd = ForwardDiff.derivative(compute_band_energies, 0.0) - @test norm(derivative_fd - derivative_ε) < 1e-4 + @test norm(derivative_fd - derivative_ε) < 5e-4 end @testset "Functional force sensitivity using ForwardDiff" begin diff --git a/test/hessian.jl b/test/hessian.jl index 04daf2e4ac..ac03a68858 100644 --- a/test/hessian.jl +++ b/test/hessian.jl @@ -98,6 +98,13 @@ include("testcases.jl") atol=1e-7) end + @testset "solve_ΩplusK_split convenience methods" begin + δψ1 = solve_ΩplusK_split(scfres, rhs).δψ + δψ2 = solve_ΩplusK_split(basis, ψ, rhs, scfres.occupation; + occupation_threshold=scfres.occupation_threshold).δψ + @test norm(δψ1 - δψ2) < 1e-7 + end + end end diff --git a/test/stresses.jl b/test/stresses.jl index 545dabbb7f..25e4135469 100644 --- a/test/stresses.jl +++ b/test/stresses.jl @@ -45,6 +45,11 @@ include("testcases.jl") ref_recompute = FiniteDiff.finite_difference_derivative(0.0) do ε recompute_energy(lattice + ε*dir*lattice, false) end + ref_scfres = FiniteDiff.finite_difference_derivative(0.0) do ε + basis = make_basis(lattice + ε*dir*lattice, false) + scfres = self_consistent_field(basis; is_converged=DFTK.ScfConvergenceDensity(1e-13)) + scfres.energies.total + end ref_HF = FiniteDiff.finite_difference_derivative(0.0) do ε hellmann_feynman_energy(scfres_nosym, lattice+ε*dir*lattice, false) end @@ -52,6 +57,7 @@ include("testcases.jl") hellmann_feynman_energy(scfres_nosym, lattice+ε*(dir*lattice), false) end + @test isapprox(ref_recompute, ref_scfres, atol=1e-8) @test isapprox(ref_HF, ref_recompute, atol=1e-5) @test isapprox(ref_HF, FD_HF, atol=1e-5) @test isapprox(dE_stresses, ref_recompute, atol=1e-5) From 8279d3781f7d0de61f74262eea9d39f73bcf1cba Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Thu, 1 Sep 2022 15:02:24 +0200 Subject: [PATCH 13/26] Remove advertisement for summer school (#716) --- README.md | 9 --------- docs/make.jl | 2 +- docs/src/guide/introductory_resources.md | 5 ++--- docs/src/index.md | 6 ------ 4 files changed, 3 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index aeebc269f2..37d0f1cae3 100644 --- a/README.md +++ b/README.md @@ -45,15 +45,6 @@ For getting started with DFTK, see [our documentation](https://docs.dftk.org): Note that at least **Julia 1.6** is required. - -## DFTK summer school 2022 - -We will organise a summer school centred around the DFTK code -and modern numerical approaches to density-functional theory -from **29 to 31 August 2022** at **Sorbonne Université, Paris**. -For more details see the [school's website](https://school2022.dftk.org). - - ## Support and citation DFTK is mostly developed as part of academic research. Parts of DFTK have also been discussed in published papers. diff --git a/docs/make.jl b/docs/make.jl index 0ceafb7563..a12e800dd2 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -16,7 +16,6 @@ # ending in *.jl will be processed to *.md with Literate. PAGES = [ "Home" => "index.md", - "school2022.md", "features.md", "Getting started" => [ # Installing DFTK, tutorial, theoretical background @@ -24,6 +23,7 @@ PAGES = [ "Tutorial" => "guide/tutorial.jl", "guide/periodic_problems.jl", "guide/introductory_resources.md", + "school2022.md", ], "Basic DFT calculations" => [ # Ground-state DFT calculations, standard problems and modelling techniques diff --git a/docs/src/guide/introductory_resources.md b/docs/src/guide/introductory_resources.md index c78666a0f1..17382384cd 100644 --- a/docs/src/guide/introductory_resources.md +++ b/docs/src/guide/introductory_resources.md @@ -9,9 +9,8 @@ For a list of articles dealing with novel research aspects achieved using DFTK, see [Publications](@ref). - [DFTK school 2022: Numerical methods for density-functional theory simulations](https://school2022.dftk.org): - From **29 to 31 August 2022** we will organise a summer school centred around the DFTK code - and modern approaches to density-functional theory at **Sorbonne Université, Paris**. - [More details and registration](https://school2022.dftk.org). + Summer school centred around the DFTK code and modern approaches to density-functional theory. + [Programme and lecture notes](https://school2022.dftk.org). - [DFTK: A Julian approach for simulating electrons in solids](https://www.youtube.com/watch?v=-RomkxjlIcQ) by M. F. Herbst: Pre-recorded talk for JuliaCon 2020. diff --git a/docs/src/index.md b/docs/src/index.md index 7fdbd569e7..f18409fba8 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -24,12 +24,6 @@ Found a bug, missing a feature? Look for an open issue or [create a new one](https://github.com/JuliaMolSim/DFTK.jl/issues). Want to contribute? See our [contributing notes](https://github.com/JuliaMolSim/DFTK.jl#contributing). -!!! tip "DFTK summer school: 29th to 31st August 2022 in Paris, France" - We will organise a summer school centred around the DFTK code - and modern approaches to density-functional theory - from **29 to 31 August 2022** at **Sorbonne Université, Paris**. - For more details and registration info see the [school's website](https://school2022.dftk.org). - # [Getting started](@id getting-started) First, new users should take a look at the [Installation](@ref) and [Tutorial](@ref) sections. Then, make your way through the various examples. From 4603777b00accee11086ab6ca29ebc6c8c5dab1f Mon Sep 17 00:00:00 2001 From: bkaperick Date: Thu, 1 Sep 2022 22:13:07 +0200 Subject: [PATCH 14/26] Add fock_exchange term and energy for one kpoint --- src/DFTK.jl | 1 + src/terms/fock_exchange.jl | 48 ++++++++++++++++++++++++++++++++++++++ src/terms/terms.jl | 1 + 3 files changed, 50 insertions(+) create mode 100644 src/terms/fock_exchange.jl diff --git a/src/DFTK.jl b/src/DFTK.jl index a3550f76fb..766e8622e8 100644 --- a/src/DFTK.jl +++ b/src/DFTK.jl @@ -74,6 +74,7 @@ export Hamiltonian export HamiltonianBlock export energy_hamiltonian export Kinetic +export FockExchange export ExternalFromFourier export ExternalFromReal export AtomicLocal diff --git a/src/terms/fock_exchange.jl b/src/terms/fock_exchange.jl new file mode 100644 index 0000000000..296e8ad87d --- /dev/null +++ b/src/terms/fock_exchange.jl @@ -0,0 +1,48 @@ +""" +Exact exchange term: the Hartree-Fock exchange energy of the orbitals + + +-1/2 ∑ ∫∫ ϕ_i^*(r)ϕ_j^*(r')ϕ_i(r')ϕ_j(r) / |r - r'| dr dr' + +""" +struct FockExchange +end +(fock_exchange::FockExchange)(basis) = TermFockExchange(basis) + +struct TermFockExchange <: Term +end +function TermFockExchange(basis::PlaneWaveBasis{T}) where T + model = basis.model + + TermFockExchange() +end + +@timing "ene_ops: FockExchange" function ene_ops(term::TermFockExchange, basis::PlaneWaveBasis{T}, + ψ, occ; ρ, kwargs...) where {T} + ops = [NoopOperator(basis, kpoint) + for (ik, kpoint) in enumerate(basis.kpoints)] + isnothing(ψ) && return (E=T(0), ops=ops) + + ψ, occ = select_occupied_orbitals(basis, ψ, occ; threshold=0.1) + + # @assert length(ψ) == 1 # TODO: make it work for more kpoints + + poisson_green_coeffs = 4T(π) ./ [sum(abs2, G) for G in G_vectors_cart(basis)] + poisson_green_coeffs_kpt = 4T(π) ./ [sum(abs2, G) for G in G_vectors_cart(basis, basis.kpoints[1])] + + E = T(0) + for psi_i in eachcol(ψ[1]) + for psi_j in eachcol(ψ[1]) + rho_ij_real = conj(G_to_r(basis, basis.kpoints[1], psi_i)) .* G_to_r(basis, basis.kpoints[1], psi_j) + + rho_ij_real_conj = conj(rho_ij_real) + rho_ij_four = r_to_G(basis, rho_ij_real) + rho_ij_four_conj = r_to_G(basis, rho_ij_real_conj) + + vij_fourier = rho_ij_four_conj .* poisson_green_coeffs + E += real(dot(rho_ij_four, vij_fourier)) + end + end + + (E=E, ops=ops) +end \ No newline at end of file diff --git a/src/terms/terms.jl b/src/terms/terms.jl index 0baad0098c..93e93af055 100644 --- a/src/terms/terms.jl +++ b/src/terms/terms.jl @@ -50,6 +50,7 @@ include("ewald.jl") include("psp_correction.jl") include("entropy.jl") include("pairwise.jl") +include("fock_exchange.jl") include("magnetic.jl") breaks_symmetries(::Magnetic) = true From ddf11cf5edc019026f09c94a7ebb37efd89d2350 Mon Sep 17 00:00:00 2001 From: bkaperick Date: Thu, 1 Sep 2022 22:16:52 +0200 Subject: [PATCH 15/26] Add ExchangeOperator to fock_exchange with consistency test --- src/terms/fock_exchange.jl | 1 + src/terms/operators.jl | 21 +++++++++++++++++++++ test/hamiltonian_consistency.jl | 1 + 3 files changed, 23 insertions(+) diff --git a/src/terms/fock_exchange.jl b/src/terms/fock_exchange.jl index 296e8ad87d..9a57e7acee 100644 --- a/src/terms/fock_exchange.jl +++ b/src/terms/fock_exchange.jl @@ -44,5 +44,6 @@ end end end + ops = [ExchangeOperator(ψ[ik], poisson_green_coeffs, poisson_green_coeffs_kpt, basis, kpt) for (ik,kpt) in enumerate(basis.kpoints)] (E=E, ops=ops) end \ No newline at end of file diff --git a/src/terms/operators.jl b/src/terms/operators.jl index 1c2d7a2f4d..f0708f6d25 100644 --- a/src/terms/operators.jl +++ b/src/terms/operators.jl @@ -163,3 +163,24 @@ function optimize_operators_(ops) sum([op.potential for op in RSmults])) [nonRSmults..., combined_RSmults] end + +struct ExchangeOperator <: RealFourierOperator + ψocc + poisson_green_coeffs + poisson_green_coeffs_kpt + basis + kpoint +end + +function apply!(Hψ, op::ExchangeOperator, ψ) + for ψ_n in eachcol(op.ψocc) + integral_fourier = op.poisson_green_coeffs_kpt .* ψ_n .* ψ.fourier + integral_real = G_to_r(op.basis, op.kpoint, integral_fourier) + psi_n_real = G_to_r(op.basis, op.kpoint, ψ_n) + Hψ.real .-= integral_real .* psi_n_real + end +end + +function Matrix(op::ExchangeOperator) + # TODO - implement this for testing/debugging purposes +end diff --git a/test/hamiltonian_consistency.jl b/test/hamiltonian_consistency.jl index 6da64c40af..583a006523 100644 --- a/test/hamiltonian_consistency.jl +++ b/test/hamiltonian_consistency.jl @@ -87,6 +87,7 @@ end test_consistency_term(Hartree()) test_consistency_term(Ewald()) test_consistency_term(PspCorrection()) + test_consistency_term(FockExchange()) test_consistency_term(Xc(:lda_xc_teter93)) test_consistency_term(Xc(:lda_xc_teter93), spin_polarization=:collinear) test_consistency_term(Xc(:gga_x_pbe), spin_polarization=:collinear) From bf0e14bea97a4e249a5837d8068c40961d56ef12 Mon Sep 17 00:00:00 2001 From: bkaperick Date: Tue, 6 Sep 2022 22:32:46 +0200 Subject: [PATCH 16/26] WIP connecting to LibXc --- src/standard_models.jl | 13 +++++++++++++ src/terms/fock_exchange.jl | 18 ++++++++++++------ src/terms/xc.jl | 5 +++-- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/standard_models.jl b/src/standard_models.jl index 2042ed8e90..f364ae7553 100644 --- a/src/standard_models.jl +++ b/src/standard_models.jl @@ -72,3 +72,16 @@ function model_SCAN(lattice::AbstractMatrix, atoms::Vector{<:Element}, positions::Vector{<:AbstractVector}; kwargs...) model_DFT(lattice, atoms, positions, [:mgga_x_scan, :mgga_c_scan]; kwargs...) end + +""" +Build an PBE0 model from the specified atoms. +DOI:10.1103/PhysRevLett.77.3865 +https://www.vasp.at/tutorials/latest/hybrids/part1/ +""" +function model_PBE0(lattice::AbstractMatrix, atoms::Vector{<:Element}, + positions::Vector{<:AbstractVector}; kwargs...) + functional = DispatchFunctional(:hyb_gga_xc_pbeh) + libxcfun = Libxc.Functional(:hyb_gga_xc_pbeh) + scaling_factor = getproperty(libxcfun, :exx_coefficient) + model_DFT(lattice, atoms, positions, Xc([functional]), extra_terms=[FockExchange(; scaling_factor = scaling_factor)]; kwargs...) +end diff --git a/src/terms/fock_exchange.jl b/src/terms/fock_exchange.jl index 9a57e7acee..883ba5a705 100644 --- a/src/terms/fock_exchange.jl +++ b/src/terms/fock_exchange.jl @@ -6,15 +6,21 @@ Exact exchange term: the Hartree-Fock exchange energy of the orbitals """ struct FockExchange + scaling_factor::Real # to scale by an arbitrary factor (useful for hybrid models) +end +FockExchange(; scaling_factor=1) = FockExchange(scaling_factor) +(exchange::FockExchange)(basis) = TermFockExchange(basis, exchange.scaling_factor) +function Base.show(io::IO, exchange::FockExchange) + fac = isone(exchange.scaling_factor) ? "" : ", scaling_factor=$scaling_factor" + print(io, "FockExchange($fac)") end -(fock_exchange::FockExchange)(basis) = TermFockExchange(basis) - struct TermFockExchange <: Term + scaling_factor::Real # scaling factor, absorbed into poisson_green_coeffs end -function TermFockExchange(basis::PlaneWaveBasis{T}) where T +function TermFockExchange(basis::PlaneWaveBasis{T}, scaling_factor) where T model = basis.model - TermFockExchange() + TermFockExchange(T(scaling_factor)) end @timing "ene_ops: FockExchange" function ene_ops(term::TermFockExchange, basis::PlaneWaveBasis{T}, @@ -27,8 +33,8 @@ end # @assert length(ψ) == 1 # TODO: make it work for more kpoints - poisson_green_coeffs = 4T(π) ./ [sum(abs2, G) for G in G_vectors_cart(basis)] - poisson_green_coeffs_kpt = 4T(π) ./ [sum(abs2, G) for G in G_vectors_cart(basis, basis.kpoints[1])] + poisson_green_coeffs = term.scaling_factor * 4T(π) ./ [sum(abs2, G) for G in G_vectors_cart(basis)] + poisson_green_coeffs_kpt = term.scaling_factor * 4T(π) ./ [sum(abs2, G) for G in G_vectors_cart(basis, basis.kpoints[1])] E = T(0) for psi_i in eachcol(ψ[1]) diff --git a/src/terms/xc.jl b/src/terms/xc.jl index 4b1f6e9966..7ac6d54b1e 100644 --- a/src/terms/xc.jl +++ b/src/terms/xc.jl @@ -49,7 +49,7 @@ end model = basis.model n_spin = model.n_spin_components - @assert all(family(xc) in (:lda, :gga, :mgga, :mggal) for xc in term.functionals) + @assert all(family(xc) in (:lda, :gga, :hyb_gga, :mgga, :mggal) for xc in term.functionals) # Compute kinetic energy density, if needed. if isnothing(τ) && any(needs_τ, term.functionals) @@ -189,6 +189,7 @@ for derivatives wrt. σ_αβ or σ_βα. function max_required_derivative(functional) family(functional) == :lda && return 0 family(functional) == :gga && return 1 + family(functional) == :hyb_gga && return 1 family(functional) == :mgga && return 1 family(functional) == :mggal && return 2 error("Functional family $(family(functional)) not known.") @@ -292,7 +293,7 @@ end function apply_kernel(term::TermXc, basis::PlaneWaveBasis{T}, δρ; ρ, kwargs...) where {T} n_spin = basis.model.n_spin_components isempty(term.functionals) && return nothing - @assert all(family(xc) in (:lda, :gga) for xc in term.functionals) + @assert all(family(xc) in (:lda, :gga, :hyb_gga) for xc in term.functionals) # Take derivatives of the density and the perturbation if needed. max_ρ_derivs = maximum(max_required_derivative, term.functionals) From 5627ec14be506f59386a5b6f73cc9249e8412078 Mon Sep 17 00:00:00 2001 From: bkaperick Date: Tue, 6 Sep 2022 23:22:36 +0200 Subject: [PATCH 17/26] Copy-paste gga methods in DispatchFunctional for hyb_gga --- src/DispatchFunctional.jl | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/DispatchFunctional.jl b/src/DispatchFunctional.jl index 83062d40ad..4046d3b8b1 100644 --- a/src/DispatchFunctional.jl +++ b/src/DispatchFunctional.jl @@ -12,7 +12,7 @@ function LibxcFunctional(identifier::Symbol) @assert fun.kind in (:exchange, :correlation, :exchange_correlation) kind = Dict(:exchange => :x, :correlation => :c, :exchange_correlation => :xc)[fun.kind] - @assert fun.family in (:lda, :gga, :mgga) # Hybrids not supported yet. + @assert fun.family in (:lda, :gga, :hyb_gga, :mgga) if fun.family == :mgga && Libxc.needs_laplacian(fun) family = :mggal else @@ -75,6 +75,18 @@ function DftFunctionals.potential_terms(func::LibxcFunctional{:gga}, ρ::Matrix{ Vσ = reshape(terms.vsigma, s_σ, n_p) (; e, Vρ, Vσ) end +function DftFunctionals.potential_terms(func::LibxcFunctional{:hyb_gga}, ρ::Matrix{Float64}, + σ::Matrix{Float64}) + s_ρ, n_p = size(ρ) + s_σ = size(σ, 1) + fun = Libxc.Functional(func.identifier; n_spin=s_ρ) + derivatives = filter(in(Libxc.supported_derivatives(fun)), 0:1) + terms = Libxc.evaluate(fun; rho=ρ, sigma=σ, derivatives) + e = libxc_energy(terms, ρ) + Vρ = reshape(terms.vrho, s_ρ, n_p) + Vσ = reshape(terms.vsigma, s_σ, n_p) + (; e, Vρ, Vσ) +end function DftFunctionals.potential_terms(func::LibxcFunctional{:mgga}, ρ::Matrix{Float64}, σ::Matrix{Float64}, τ::Matrix{Float64}) s_ρ, n_p = size(ρ) @@ -129,7 +141,21 @@ function DftFunctionals.kernel_terms(func::LibxcFunctional{:gga}, ρ::Matrix{Flo Vσσ = libxc_unfold_spin(terms.v2sigma2, s_σ) (; e, Vρ, Vσ, Vρρ, Vρσ, Vσσ) end - +function DftFunctionals.kernel_terms(func::LibxcFunctional{:hyb_gga}, ρ::Matrix{Float64}, + σ::Matrix{Float64}) +s_ρ, n_p = size(ρ) +s_σ = size(σ, 1) +fun = Libxc.Functional(func.identifier; n_spin=s_ρ) +derivatives = filter(in(Libxc.supported_derivatives(fun)), 0:2) +terms = Libxc.evaluate(fun; rho=ρ, sigma=σ, derivatives) +e = libxc_energy(terms, ρ) +Vρ = reshape(terms.vrho, s_ρ, n_p) +Vσ = reshape(terms.vsigma, s_σ, n_p) +Vρρ = libxc_unfold_spin(terms.v2rho2, s_ρ) +Vρσ = permutedims(reshape(terms.v2rhosigma, s_σ, s_ρ, n_p), (2, 1, 3)) +Vσσ = libxc_unfold_spin(terms.v2sigma2, s_σ) +(; e, Vρ, Vσ, Vρρ, Vρσ, Vσσ) +end # # Automatic dispatching between Libxc (where possible) and the generic implementation # in DftFunctionals (where needed). From 3426b5e9c9a3ddd6f488158892ca594133da0f90 Mon Sep 17 00:00:00 2001 From: bkaperick Date: Tue, 6 Sep 2022 23:22:56 +0200 Subject: [PATCH 18/26] Add scaling_factor to fock_exchange term --- src/terms/fock_exchange.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/terms/fock_exchange.jl b/src/terms/fock_exchange.jl index 883ba5a705..6a4a6cd5bd 100644 --- a/src/terms/fock_exchange.jl +++ b/src/terms/fock_exchange.jl @@ -9,15 +9,16 @@ struct FockExchange scaling_factor::Real # to scale by an arbitrary factor (useful for hybrid models) end FockExchange(; scaling_factor=1) = FockExchange(scaling_factor) -(exchange::FockExchange)(basis) = TermFockExchange(basis, exchange.scaling_factor) +(exchange::FockExchange)(basis) = TermFockExchange(basis, exchange.scaling_factor) function Base.show(io::IO, exchange::FockExchange) - fac = isone(exchange.scaling_factor) ? "" : ", scaling_factor=$scaling_factor" + fac = isone(exchange.scaling_factor) ? "" : "scaling_factor=$(exchange.scaling_factor)" print(io, "FockExchange($fac)") end struct TermFockExchange <: Term scaling_factor::Real # scaling factor, absorbed into poisson_green_coeffs end function TermFockExchange(basis::PlaneWaveBasis{T}, scaling_factor) where T + println("scaling=$(scaling_factor)") model = basis.model TermFockExchange(T(scaling_factor)) From d008d4063bd9c17d583d160851f63f623bc9d774 Mon Sep 17 00:00:00 2001 From: bkaperick Date: Tue, 6 Sep 2022 23:23:28 +0200 Subject: [PATCH 19/26] Fix printing bug in other terms using scale_factor --- src/terms/hartree.jl | 2 +- src/terms/kinetic.jl | 2 +- src/terms/xc.jl | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/terms/hartree.jl b/src/terms/hartree.jl index 07dc61548a..c6122f0105 100644 --- a/src/terms/hartree.jl +++ b/src/terms/hartree.jl @@ -17,7 +17,7 @@ end Hartree(; scaling_factor=1) = Hartree(scaling_factor) (hartree::Hartree)(basis) = TermHartree(basis, hartree.scaling_factor) function Base.show(io::IO, hartree::Hartree) - fac = isone(hartree.scaling_factor) ? "" : ", scaling_factor=$scaling_factor" + fac = isone(hartree.scaling_factor) ? "" : ", scaling_factor=$(hartree.scaling_factor)" print(io, "Hartree($fac)") end diff --git a/src/terms/kinetic.jl b/src/terms/kinetic.jl index b5d757f24a..c53bf35193 100644 --- a/src/terms/kinetic.jl +++ b/src/terms/kinetic.jl @@ -7,7 +7,7 @@ end Kinetic(; scaling_factor=1) = Kinetic(scaling_factor) (kin::Kinetic)(basis) = TermKinetic(basis, kin.scaling_factor) function Base.show(io::IO, kin::Kinetic) - fac = isone(kin.scaling_factor) ? "" : ", scaling_factor=$scaling_factor" + fac = isone(kin.scaling_factor) ? "" : "scaling_factor=$(kin.scaling_factor)" print(io, "Kinetic($fac)") end diff --git a/src/terms/xc.jl b/src/terms/xc.jl index 7ac6d54b1e..c2d5493ea2 100644 --- a/src/terms/xc.jl +++ b/src/terms/xc.jl @@ -20,7 +20,7 @@ end Xc(functional; kwargs...) = Xc([functional]; kwargs...) function Base.show(io::IO, xc::Xc) - fac = isone(xc.scaling_factor) ? "" : ", scaling_factor=$scaling_factor" + fac = isone(xc.scaling_factor) ? "" : ", scaling_factor=$(xc.scaling_factor)" fun = length(xc.functionals) == 1 ? ":$(xc.functionals[1])" : "$(xc.functionals)" print(io, "Xc($fun$fac)") end @@ -187,11 +187,11 @@ for derivatives wrt. σ_αβ or σ_βα. =# function max_required_derivative(functional) - family(functional) == :lda && return 0 - family(functional) == :gga && return 1 - family(functional) == :hyb_gga && return 1 - family(functional) == :mgga && return 1 - family(functional) == :mggal && return 2 + family(functional) == :lda && return 0 + family(functional) == :gga && return 1 + family(functional) == :hyb_gga && return 1 + family(functional) == :mgga && return 1 + family(functional) == :mggal && return 2 error("Functional family $(family(functional)) not known.") end From ada9b29917dde1982c74b01d84c3c00a10bab568 Mon Sep 17 00:00:00 2001 From: bkaperick Date: Sat, 10 Sep 2022 19:43:55 +0200 Subject: [PATCH 20/26] Reduce code duplication (with Union) and implement hybrid lda and mgga --- examples/silicon.jl | 2 +- src/DispatchFunctional.jl | 41 +++++++-------------------------------- src/terms/xc.jl | 6 ++++-- 3 files changed, 12 insertions(+), 37 deletions(-) diff --git a/examples/silicon.jl b/examples/silicon.jl index 8ec15a6fa3..1c12e995ff 100644 --- a/examples/silicon.jl +++ b/examples/silicon.jl @@ -9,6 +9,6 @@ Si = ElementPsp(:Si, psp=load_psp("hgh/lda/Si-q4")) atoms = [Si, Si] positions = [ones(3)/8, -ones(3)/8] -model = model_LDA(lattice, atoms, positions) +model = DFTK.model_PBE0(lattice, atoms, positions) basis = PlaneWaveBasis(model; Ecut=15, kgrid=[4, 4, 4]) scfres = self_consistent_field(basis, tol=1e-8) diff --git a/src/DispatchFunctional.jl b/src/DispatchFunctional.jl index 4046d3b8b1..a39dc3bb39 100644 --- a/src/DispatchFunctional.jl +++ b/src/DispatchFunctional.jl @@ -12,8 +12,8 @@ function LibxcFunctional(identifier::Symbol) @assert fun.kind in (:exchange, :correlation, :exchange_correlation) kind = Dict(:exchange => :x, :correlation => :c, :exchange_correlation => :xc)[fun.kind] - @assert fun.family in (:lda, :gga, :hyb_gga, :mgga) - if fun.family == :mgga && Libxc.needs_laplacian(fun) + @assert fun.family in (:lda, :hyb_lda, :gga, :hyb_gga, :mgga, :hyb_mgga) + if (fun.family == :mgga || fun.family == :hyb_mgga) && Libxc.needs_laplacian(fun) family = :mggal else family = fun.family @@ -54,7 +54,7 @@ end libxc_energy(terms, ρ) = haskey(terms, :zk) ? reshape(terms.zk, 1, size(ρ, 2)) .* ρ : false -function DftFunctionals.potential_terms(func::LibxcFunctional{:lda}, ρ::Matrix{Float64}) +function DftFunctionals.potential_terms(func::Union{LibxcFunctional{:lda},LibxcFunctional{:hyb_lda}}, ρ::Matrix{Float64}) s_ρ, n_p = size(ρ) fun = Libxc.Functional(func.identifier; n_spin=s_ρ) derivatives = filter(in(Libxc.supported_derivatives(fun)), 0:1) @@ -63,7 +63,7 @@ function DftFunctionals.potential_terms(func::LibxcFunctional{:lda}, ρ::Matrix{ Vρ = reshape(terms.vrho, s_ρ, n_p) (; e, Vρ) end -function DftFunctionals.potential_terms(func::LibxcFunctional{:gga}, ρ::Matrix{Float64}, +function DftFunctionals.potential_terms(func::Union{LibxcFunctional{:gga},LibxcFunctional{:hyb_gga}}, ρ::Matrix{Float64}, σ::Matrix{Float64}) s_ρ, n_p = size(ρ) s_σ = size(σ, 1) @@ -75,19 +75,7 @@ function DftFunctionals.potential_terms(func::LibxcFunctional{:gga}, ρ::Matrix{ Vσ = reshape(terms.vsigma, s_σ, n_p) (; e, Vρ, Vσ) end -function DftFunctionals.potential_terms(func::LibxcFunctional{:hyb_gga}, ρ::Matrix{Float64}, - σ::Matrix{Float64}) - s_ρ, n_p = size(ρ) - s_σ = size(σ, 1) - fun = Libxc.Functional(func.identifier; n_spin=s_ρ) - derivatives = filter(in(Libxc.supported_derivatives(fun)), 0:1) - terms = Libxc.evaluate(fun; rho=ρ, sigma=σ, derivatives) - e = libxc_energy(terms, ρ) - Vρ = reshape(terms.vrho, s_ρ, n_p) - Vσ = reshape(terms.vsigma, s_σ, n_p) - (; e, Vρ, Vσ) -end -function DftFunctionals.potential_terms(func::LibxcFunctional{:mgga}, ρ::Matrix{Float64}, +function DftFunctionals.potential_terms(func::Union{LibxcFunctional{:mgga},LibxcFunctional{:hyb_mgga}}, ρ::Matrix{Float64}, σ::Matrix{Float64}, τ::Matrix{Float64}) s_ρ, n_p = size(ρ) s_σ = size(σ, 1) @@ -116,7 +104,7 @@ function DftFunctionals.potential_terms(func::LibxcFunctional{:mggal}, ρ::Matri (; e, Vρ, Vσ, Vτ, Vl) end -function DftFunctionals.kernel_terms(func::LibxcFunctional{:lda}, ρ::Matrix{Float64}) +function DftFunctionals.kernel_terms(func::Union{LibxcFunctional{:lda},LibxcFunctional{:hyb_lda}}, ρ::Matrix{Float64}) s_ρ, n_p = size(ρ) fun = Libxc.Functional(func.identifier; n_spin=s_ρ) derivatives = filter(in(Libxc.supported_derivatives(fun)), 0:2) @@ -126,7 +114,7 @@ function DftFunctionals.kernel_terms(func::LibxcFunctional{:lda}, ρ::Matrix{Flo Vρρ = libxc_unfold_spin(terms.v2rho2, s_ρ) (; e, Vρ, Vρρ) end -function DftFunctionals.kernel_terms(func::LibxcFunctional{:gga}, ρ::Matrix{Float64}, +function DftFunctionals.kernel_terms(func::Union{LibxcFunctional{:gga},LibxcFunctional{:hyb_gga}}, ρ::Matrix{Float64}, σ::Matrix{Float64}) s_ρ, n_p = size(ρ) s_σ = size(σ, 1) @@ -141,21 +129,6 @@ function DftFunctionals.kernel_terms(func::LibxcFunctional{:gga}, ρ::Matrix{Flo Vσσ = libxc_unfold_spin(terms.v2sigma2, s_σ) (; e, Vρ, Vσ, Vρρ, Vρσ, Vσσ) end -function DftFunctionals.kernel_terms(func::LibxcFunctional{:hyb_gga}, ρ::Matrix{Float64}, - σ::Matrix{Float64}) -s_ρ, n_p = size(ρ) -s_σ = size(σ, 1) -fun = Libxc.Functional(func.identifier; n_spin=s_ρ) -derivatives = filter(in(Libxc.supported_derivatives(fun)), 0:2) -terms = Libxc.evaluate(fun; rho=ρ, sigma=σ, derivatives) -e = libxc_energy(terms, ρ) -Vρ = reshape(terms.vrho, s_ρ, n_p) -Vσ = reshape(terms.vsigma, s_σ, n_p) -Vρρ = libxc_unfold_spin(terms.v2rho2, s_ρ) -Vρσ = permutedims(reshape(terms.v2rhosigma, s_σ, s_ρ, n_p), (2, 1, 3)) -Vσσ = libxc_unfold_spin(terms.v2sigma2, s_σ) -(; e, Vρ, Vσ, Vρρ, Vρσ, Vσσ) -end # # Automatic dispatching between Libxc (where possible) and the generic implementation # in DftFunctionals (where needed). diff --git a/src/terms/xc.jl b/src/terms/xc.jl index c2d5493ea2..847a580a66 100644 --- a/src/terms/xc.jl +++ b/src/terms/xc.jl @@ -49,7 +49,7 @@ end model = basis.model n_spin = model.n_spin_components - @assert all(family(xc) in (:lda, :gga, :hyb_gga, :mgga, :mggal) for xc in term.functionals) + @assert all(family(xc) in (:lda, :hyb_lda, :gga, :hyb_gga, :mgga, :hyb_mgga, :mggal) for xc in term.functionals) # Compute kinetic energy density, if needed. if isnothing(τ) && any(needs_τ, term.functionals) @@ -188,9 +188,11 @@ for derivatives wrt. σ_αβ or σ_βα. function max_required_derivative(functional) family(functional) == :lda && return 0 + family(functional) == :hyb_lda && return 0 family(functional) == :gga && return 1 family(functional) == :hyb_gga && return 1 family(functional) == :mgga && return 1 + family(functional) == :hyb_mgga && return 1 family(functional) == :mggal && return 2 error("Functional family $(family(functional)) not known.") end @@ -293,7 +295,7 @@ end function apply_kernel(term::TermXc, basis::PlaneWaveBasis{T}, δρ; ρ, kwargs...) where {T} n_spin = basis.model.n_spin_components isempty(term.functionals) && return nothing - @assert all(family(xc) in (:lda, :gga, :hyb_gga) for xc in term.functionals) + @assert all(family(xc) in (:lda, :hyb_lda, :gga, :hyb_gga) for xc in term.functionals) # Take derivatives of the density and the perturbation if needed. max_ρ_derivs = maximum(max_required_derivative, term.functionals) From f770fde5a58a99a0e054a1a2ad429064b1a297ae Mon Sep 17 00:00:00 2001 From: bkaperick Date: Sat, 10 Sep 2022 20:38:05 +0200 Subject: [PATCH 21/26] Compute green coeffs only once, and set first term to 0 --- src/terms/fock_exchange.jl | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/terms/fock_exchange.jl b/src/terms/fock_exchange.jl index 6a4a6cd5bd..1b660d5852 100644 --- a/src/terms/fock_exchange.jl +++ b/src/terms/fock_exchange.jl @@ -16,12 +16,20 @@ function Base.show(io::IO, exchange::FockExchange) end struct TermFockExchange <: Term scaling_factor::Real # scaling factor, absorbed into poisson_green_coeffs + poisson_green_coeffs::AbstractArray + poisson_green_coeffs_kpt::AbstractArray end function TermFockExchange(basis::PlaneWaveBasis{T}, scaling_factor) where T - println("scaling=$(scaling_factor)") model = basis.model - TermFockExchange(T(scaling_factor)) + poisson_green_coeffs = scaling_factor * 4T(π) ./ [sum(abs2, G) for G in G_vectors_cart(basis)] + poisson_green_coeffs_kpt = scaling_factor * 4T(π) ./ [sum(abs2, G) for G in G_vectors_cart(basis, basis.kpoints[1])] + + # Compensating charge background => Zero DC + poisson_green_coeffs[1] = 0 + poisson_green_coeffs_kpt[1] = 0 + + TermFockExchange(T(scaling_factor), poisson_green_coeffs, poisson_green_coeffs_kpt) end @timing "ene_ops: FockExchange" function ene_ops(term::TermFockExchange, basis::PlaneWaveBasis{T}, @@ -34,9 +42,6 @@ end # @assert length(ψ) == 1 # TODO: make it work for more kpoints - poisson_green_coeffs = term.scaling_factor * 4T(π) ./ [sum(abs2, G) for G in G_vectors_cart(basis)] - poisson_green_coeffs_kpt = term.scaling_factor * 4T(π) ./ [sum(abs2, G) for G in G_vectors_cart(basis, basis.kpoints[1])] - E = T(0) for psi_i in eachcol(ψ[1]) for psi_j in eachcol(ψ[1]) @@ -46,11 +51,10 @@ end rho_ij_four = r_to_G(basis, rho_ij_real) rho_ij_four_conj = r_to_G(basis, rho_ij_real_conj) - vij_fourier = rho_ij_four_conj .* poisson_green_coeffs + vij_fourier = rho_ij_four_conj .* term.poisson_green_coeffs E += real(dot(rho_ij_four, vij_fourier)) end end - - ops = [ExchangeOperator(ψ[ik], poisson_green_coeffs, poisson_green_coeffs_kpt, basis, kpt) for (ik,kpt) in enumerate(basis.kpoints)] + ops = [ExchangeOperator(ψ[ik], term.poisson_green_coeffs, term.poisson_green_coeffs_kpt, basis, kpt) for (ik,kpt) in enumerate(basis.kpoints)] (E=E, ops=ops) end \ No newline at end of file From b3d650c20c9ee07253b26de53935413bace8880f Mon Sep 17 00:00:00 2001 From: bkaperick Date: Fri, 23 Sep 2022 22:30:48 +0200 Subject: [PATCH 22/26] Michael comments part 1 --- examples/silicon.jl | 2 +- src/DFTK.jl | 2 +- src/DispatchFunctional.jl | 26 +++++++---- src/standard_models.jl | 5 +- src/terms/exact_exchange.jl | 81 +++++++++++++++++++++++++++++++++ src/terms/fock_exchange.jl | 60 ------------------------ src/terms/operators.jl | 7 +-- src/terms/terms.jl | 2 +- src/terms/xc.jl | 15 +++--- test/hamiltonian_consistency.jl | 5 +- 10 files changed, 115 insertions(+), 90 deletions(-) create mode 100644 src/terms/exact_exchange.jl delete mode 100644 src/terms/fock_exchange.jl diff --git a/examples/silicon.jl b/examples/silicon.jl index 1c12e995ff..8ec15a6fa3 100644 --- a/examples/silicon.jl +++ b/examples/silicon.jl @@ -9,6 +9,6 @@ Si = ElementPsp(:Si, psp=load_psp("hgh/lda/Si-q4")) atoms = [Si, Si] positions = [ones(3)/8, -ones(3)/8] -model = DFTK.model_PBE0(lattice, atoms, positions) +model = model_LDA(lattice, atoms, positions) basis = PlaneWaveBasis(model; Ecut=15, kgrid=[4, 4, 4]) scfres = self_consistent_field(basis, tol=1e-8) diff --git a/src/DFTK.jl b/src/DFTK.jl index 766e8622e8..56dea3efd8 100644 --- a/src/DFTK.jl +++ b/src/DFTK.jl @@ -74,7 +74,7 @@ export Hamiltonian export HamiltonianBlock export energy_hamiltonian export Kinetic -export FockExchange +export ExactExchange export ExternalFromFourier export ExternalFromReal export AtomicLocal diff --git a/src/DispatchFunctional.jl b/src/DispatchFunctional.jl index a39dc3bb39..5a5ceff6b2 100644 --- a/src/DispatchFunctional.jl +++ b/src/DispatchFunctional.jl @@ -12,12 +12,21 @@ function LibxcFunctional(identifier::Symbol) @assert fun.kind in (:exchange, :correlation, :exchange_correlation) kind = Dict(:exchange => :x, :correlation => :c, :exchange_correlation => :xc)[fun.kind] - @assert fun.family in (:lda, :hyb_lda, :gga, :hyb_gga, :mgga, :hyb_mgga) - if (fun.family == :mgga || fun.family == :hyb_mgga) && Libxc.needs_laplacian(fun) - family = :mggal + # Libxc maintains the distinction between hybrid and non-hybrid equivalents, + # but this distinction is not relevant for the functional interface + if fun.family == :hyb_lda + family = :lda + elseif fun.family == :hyb_gga + family = :gga + elseif fun.family == :hyb_mgga + family = :mgga else family = fun.family end + + if family == :mgga && Libxc.needs_laplacian(fun) + family = :mggal + end LibxcFunctional{family,kind}(identifier) end @@ -54,7 +63,7 @@ end libxc_energy(terms, ρ) = haskey(terms, :zk) ? reshape(terms.zk, 1, size(ρ, 2)) .* ρ : false -function DftFunctionals.potential_terms(func::Union{LibxcFunctional{:lda},LibxcFunctional{:hyb_lda}}, ρ::Matrix{Float64}) +function DftFunctionals.potential_terms(func::LibxcFunctional{:lda}, ρ::Matrix{Float64}) s_ρ, n_p = size(ρ) fun = Libxc.Functional(func.identifier; n_spin=s_ρ) derivatives = filter(in(Libxc.supported_derivatives(fun)), 0:1) @@ -63,7 +72,7 @@ function DftFunctionals.potential_terms(func::Union{LibxcFunctional{:lda},LibxcF Vρ = reshape(terms.vrho, s_ρ, n_p) (; e, Vρ) end -function DftFunctionals.potential_terms(func::Union{LibxcFunctional{:gga},LibxcFunctional{:hyb_gga}}, ρ::Matrix{Float64}, +function DftFunctionals.potential_terms(func::LibxcFunctional{:gga}, ρ::Matrix{Float64}, σ::Matrix{Float64}) s_ρ, n_p = size(ρ) s_σ = size(σ, 1) @@ -75,7 +84,7 @@ function DftFunctionals.potential_terms(func::Union{LibxcFunctional{:gga},LibxcF Vσ = reshape(terms.vsigma, s_σ, n_p) (; e, Vρ, Vσ) end -function DftFunctionals.potential_terms(func::Union{LibxcFunctional{:mgga},LibxcFunctional{:hyb_mgga}}, ρ::Matrix{Float64}, +function DftFunctionals.potential_terms(func::LibxcFunctional{:mgga}, ρ::Matrix{Float64}, σ::Matrix{Float64}, τ::Matrix{Float64}) s_ρ, n_p = size(ρ) s_σ = size(σ, 1) @@ -104,7 +113,7 @@ function DftFunctionals.potential_terms(func::LibxcFunctional{:mggal}, ρ::Matri (; e, Vρ, Vσ, Vτ, Vl) end -function DftFunctionals.kernel_terms(func::Union{LibxcFunctional{:lda},LibxcFunctional{:hyb_lda}}, ρ::Matrix{Float64}) +function DftFunctionals.kernel_terms(func::LibxcFunctional{:lda}, ρ::Matrix{Float64}) s_ρ, n_p = size(ρ) fun = Libxc.Functional(func.identifier; n_spin=s_ρ) derivatives = filter(in(Libxc.supported_derivatives(fun)), 0:2) @@ -114,7 +123,7 @@ function DftFunctionals.kernel_terms(func::Union{LibxcFunctional{:lda},LibxcFunc Vρρ = libxc_unfold_spin(terms.v2rho2, s_ρ) (; e, Vρ, Vρρ) end -function DftFunctionals.kernel_terms(func::Union{LibxcFunctional{:gga},LibxcFunctional{:hyb_gga}}, ρ::Matrix{Float64}, +function DftFunctionals.kernel_terms(func::LibxcFunctional{:gga}, ρ::Matrix{Float64}, σ::Matrix{Float64}) s_ρ, n_p = size(ρ) s_σ = size(σ, 1) @@ -129,6 +138,7 @@ function DftFunctionals.kernel_terms(func::Union{LibxcFunctional{:gga},LibxcFunc Vσσ = libxc_unfold_spin(terms.v2sigma2, s_σ) (; e, Vρ, Vσ, Vρρ, Vρσ, Vσσ) end + # # Automatic dispatching between Libxc (where possible) and the generic implementation # in DftFunctionals (where needed). diff --git a/src/standard_models.jl b/src/standard_models.jl index f364ae7553..d47ec2ca0c 100644 --- a/src/standard_models.jl +++ b/src/standard_models.jl @@ -81,7 +81,6 @@ https://www.vasp.at/tutorials/latest/hybrids/part1/ function model_PBE0(lattice::AbstractMatrix, atoms::Vector{<:Element}, positions::Vector{<:AbstractVector}; kwargs...) functional = DispatchFunctional(:hyb_gga_xc_pbeh) - libxcfun = Libxc.Functional(:hyb_gga_xc_pbeh) - scaling_factor = getproperty(libxcfun, :exx_coefficient) - model_DFT(lattice, atoms, positions, Xc([functional]), extra_terms=[FockExchange(; scaling_factor = scaling_factor)]; kwargs...) + model_DFT(lattice, atoms, positions, Xc([functional]), + extra_terms=[ExactExchange(; scaling_factor = 0.25)]; kwargs...) end diff --git a/src/terms/exact_exchange.jl b/src/terms/exact_exchange.jl new file mode 100644 index 0000000000..3b57d62f01 --- /dev/null +++ b/src/terms/exact_exchange.jl @@ -0,0 +1,81 @@ +""" +Exact exchange term: the Hartree-Exact exchange energy of the orbitals + + +-1/2 ∑ ∫∫ ϕ_i^*(r)ϕ_j^*(r')ϕ_i(r')ϕ_j(r) / |r - r'| dr dr' + +""" +struct ExactExchange + scaling_factor::Real # to scale by an arbitrary factor (useful for hybrid models) +end +ExactExchange(; scaling_factor=1) = ExactExchange(scaling_factor) +(exchange::ExactExchange)(basis) = TermExactExchange(basis, exchange.scaling_factor) +function Base.show(io::IO, exchange::ExactExchange) + fac = isone(exchange.scaling_factor) ? "" : "scaling_factor=$(exchange.scaling_factor)" + print(io, "ExactExchange($fac)") +end +struct TermExactExchange <: Term + scaling_factor::Real # scaling factor, absorbed into poisson_green_coeffs + poisson_green_coeffs::AbstractArray + poisson_green_coeffs_kpt::AbstractArray +end +function TermExactExchange(basis::PlaneWaveBasis{T}, scaling_factor) where T + model = basis.model + + poisson_green_coeffs = T(scaling_factor) .* 4T(π) ./ [sum(abs2, G) + for G in G_vectors_cart(basis)] + poisson_green_coeffs_kpt = T(scaling_factor) .* 4T(π) ./ [sum(abs2, G) + for G in G_vectors_cart(basis, basis.kpoints[1])] + + # Compensating charge background => Zero DC + poisson_green_coeffs[1] = 0 + poisson_green_coeffs_kpt[1] = 0 + + TermExactExchange(T(scaling_factor), poisson_green_coeffs, poisson_green_coeffs_kpt) +end + +@timing "ene_ops: ExactExchange" function ene_ops(term::TermExactExchange, + basis::PlaneWaveBasis{T}, ψ, occ; ρ, + kwargs...) where {T} + ops = [NoopOperator(basis, kpoint) + for (ik, kpoint) in enumerate(basis.kpoints)] + isnothing(ψ) && return (E=T(0), ops=ops) + + ψ, occ = select_occupied_orbitals(basis, ψ, occ; threshold=0.1) + @assert length(ψ) == 1 # TODO: make it work for more kpoints + + E = T(0) + for (k,kpoint) in enumerate(basis.kpoints) + for psi_i in eachcol(ψ[k]) + for psi_j in eachcol(ψ[k]) + + rho_ij_real = conj(G_to_r(basis, kpoint, psi_i)) .* G_to_r(basis, kpoint, psi_j) + rho_ij_four = r_to_G(basis, rho_ij_real) + + # Re-ordering of rho_ij_four coeffs to match opposing sign G vectors + # f(r) = Σc_G e^{iG⋅r} implies f^*(r) = Σc_G^* e^{i(-G)⋅r} + ind1 = cat(1,basis.fft_size[1]:-1:2;dims=1) + ind2 = cat(1,basis.fft_size[2]:-1:2;dims=1) + ind3 = cat(1,basis.fft_size[3]:-1:2;dims=1) + + + # equivalent to r_to_G(basis, kpoint, rho_ij_real_conj) + rho_ij_four_conj = conj(rho_ij_four[ind1,ind2,ind3]) + + # Solving poisson equation to obtain potential corresponding to + # ϕ_i(r')ϕ_j(r) / |r - r'| + vij_fourier = rho_ij_four_conj .* term.poisson_green_coeffs + + E += real(dot(rho_ij_four, vij_fourier)) + end + end + end + + ops = [ExchangeOperator(ψ[ik], term.poisson_green_coeffs_kpt, + basis, kpt) for (ik,kpt) in enumerate(basis.kpoints)] + (E=E, ops=ops) +end + +function pp(x,y) + println() +end \ No newline at end of file diff --git a/src/terms/fock_exchange.jl b/src/terms/fock_exchange.jl deleted file mode 100644 index 1b660d5852..0000000000 --- a/src/terms/fock_exchange.jl +++ /dev/null @@ -1,60 +0,0 @@ -""" -Exact exchange term: the Hartree-Fock exchange energy of the orbitals - - --1/2 ∑ ∫∫ ϕ_i^*(r)ϕ_j^*(r')ϕ_i(r')ϕ_j(r) / |r - r'| dr dr' - -""" -struct FockExchange - scaling_factor::Real # to scale by an arbitrary factor (useful for hybrid models) -end -FockExchange(; scaling_factor=1) = FockExchange(scaling_factor) -(exchange::FockExchange)(basis) = TermFockExchange(basis, exchange.scaling_factor) -function Base.show(io::IO, exchange::FockExchange) - fac = isone(exchange.scaling_factor) ? "" : "scaling_factor=$(exchange.scaling_factor)" - print(io, "FockExchange($fac)") -end -struct TermFockExchange <: Term - scaling_factor::Real # scaling factor, absorbed into poisson_green_coeffs - poisson_green_coeffs::AbstractArray - poisson_green_coeffs_kpt::AbstractArray -end -function TermFockExchange(basis::PlaneWaveBasis{T}, scaling_factor) where T - model = basis.model - - poisson_green_coeffs = scaling_factor * 4T(π) ./ [sum(abs2, G) for G in G_vectors_cart(basis)] - poisson_green_coeffs_kpt = scaling_factor * 4T(π) ./ [sum(abs2, G) for G in G_vectors_cart(basis, basis.kpoints[1])] - - # Compensating charge background => Zero DC - poisson_green_coeffs[1] = 0 - poisson_green_coeffs_kpt[1] = 0 - - TermFockExchange(T(scaling_factor), poisson_green_coeffs, poisson_green_coeffs_kpt) -end - -@timing "ene_ops: FockExchange" function ene_ops(term::TermFockExchange, basis::PlaneWaveBasis{T}, - ψ, occ; ρ, kwargs...) where {T} - ops = [NoopOperator(basis, kpoint) - for (ik, kpoint) in enumerate(basis.kpoints)] - isnothing(ψ) && return (E=T(0), ops=ops) - - ψ, occ = select_occupied_orbitals(basis, ψ, occ; threshold=0.1) - - # @assert length(ψ) == 1 # TODO: make it work for more kpoints - - E = T(0) - for psi_i in eachcol(ψ[1]) - for psi_j in eachcol(ψ[1]) - rho_ij_real = conj(G_to_r(basis, basis.kpoints[1], psi_i)) .* G_to_r(basis, basis.kpoints[1], psi_j) - - rho_ij_real_conj = conj(rho_ij_real) - rho_ij_four = r_to_G(basis, rho_ij_real) - rho_ij_four_conj = r_to_G(basis, rho_ij_real_conj) - - vij_fourier = rho_ij_four_conj .* term.poisson_green_coeffs - E += real(dot(rho_ij_four, vij_fourier)) - end - end - ops = [ExchangeOperator(ψ[ik], term.poisson_green_coeffs, term.poisson_green_coeffs_kpt, basis, kpt) for (ik,kpt) in enumerate(basis.kpoints)] - (E=E, ops=ops) -end \ No newline at end of file diff --git a/src/terms/operators.jl b/src/terms/operators.jl index f0708f6d25..617e344b2f 100644 --- a/src/terms/operators.jl +++ b/src/terms/operators.jl @@ -167,20 +167,17 @@ end struct ExchangeOperator <: RealFourierOperator ψocc poisson_green_coeffs - poisson_green_coeffs_kpt basis kpoint end function apply!(Hψ, op::ExchangeOperator, ψ) for ψ_n in eachcol(op.ψocc) - integral_fourier = op.poisson_green_coeffs_kpt .* ψ_n .* ψ.fourier + integral_fourier = op.poisson_green_coeffs .* ψ_n .* ψ.fourier integral_real = G_to_r(op.basis, op.kpoint, integral_fourier) psi_n_real = G_to_r(op.basis, op.kpoint, ψ_n) Hψ.real .-= integral_real .* psi_n_real end end -function Matrix(op::ExchangeOperator) - # TODO - implement this for testing/debugging purposes -end +# TODO Implement Matrix(op::ExchangeOperator) \ No newline at end of file diff --git a/src/terms/terms.jl b/src/terms/terms.jl index 93e93af055..2388f393cc 100644 --- a/src/terms/terms.jl +++ b/src/terms/terms.jl @@ -50,7 +50,7 @@ include("ewald.jl") include("psp_correction.jl") include("entropy.jl") include("pairwise.jl") -include("fock_exchange.jl") +include("exact_exchange.jl") include("magnetic.jl") breaks_symmetries(::Magnetic) = true diff --git a/src/terms/xc.jl b/src/terms/xc.jl index 847a580a66..9b9c1384a2 100644 --- a/src/terms/xc.jl +++ b/src/terms/xc.jl @@ -49,7 +49,7 @@ end model = basis.model n_spin = model.n_spin_components - @assert all(family(xc) in (:lda, :hyb_lda, :gga, :hyb_gga, :mgga, :hyb_mgga, :mggal) for xc in term.functionals) + @assert all(family(xc) in (:lda, :gga, :mgga, :mggal) for xc in term.functionals) # Compute kinetic energy density, if needed. if isnothing(τ) && any(needs_τ, term.functionals) @@ -187,13 +187,10 @@ for derivatives wrt. σ_αβ or σ_βα. =# function max_required_derivative(functional) - family(functional) == :lda && return 0 - family(functional) == :hyb_lda && return 0 - family(functional) == :gga && return 1 - family(functional) == :hyb_gga && return 1 - family(functional) == :mgga && return 1 - family(functional) == :hyb_mgga && return 1 - family(functional) == :mggal && return 2 + family(functional) == :lda && return 0 + family(functional) == :gga && return 1 + family(functional) == :mgga && return 1 + family(functional) == :mggal && return 2 error("Functional family $(family(functional)) not known.") end @@ -295,7 +292,7 @@ end function apply_kernel(term::TermXc, basis::PlaneWaveBasis{T}, δρ; ρ, kwargs...) where {T} n_spin = basis.model.n_spin_components isempty(term.functionals) && return nothing - @assert all(family(xc) in (:lda, :hyb_lda, :gga, :hyb_gga) for xc in term.functionals) + @assert all(family(xc) in (:lda, :gga) for xc in term.functionals) # Take derivatives of the density and the perturbation if needed. max_ρ_derivs = maximum(max_required_derivative, term.functionals) diff --git a/test/hamiltonian_consistency.jl b/test/hamiltonian_consistency.jl index 583a006523..da48b7facb 100644 --- a/test/hamiltonian_consistency.jl +++ b/test/hamiltonian_consistency.jl @@ -14,7 +14,8 @@ function test_matrix_repr_operator(hamk, ψk; atol=1e-8) @test norm(operator_matrix*ψk - operator*ψk) < atol catch e allowed_missing_operators = Union{DFTK.DivAgradOperator, - DFTK.MagneticFieldOperator} + DFTK.MagneticFieldOperator, + DFTK.ExchangeOperator} @assert operator isa allowed_missing_operators @info "Matrix of operator $(nameof(typeof(operator))) not yet supported" maxlog=1 end @@ -87,7 +88,7 @@ end test_consistency_term(Hartree()) test_consistency_term(Ewald()) test_consistency_term(PspCorrection()) - test_consistency_term(FockExchange()) + test_consistency_term(ExactExchange(), kgrid=[1,1,1]) test_consistency_term(Xc(:lda_xc_teter93)) test_consistency_term(Xc(:lda_xc_teter93), spin_polarization=:collinear) test_consistency_term(Xc(:gga_x_pbe), spin_polarization=:collinear) From 2ead1c5b84519b2766a984379ce148c80d98e17b Mon Sep 17 00:00:00 2001 From: bkaperick Date: Wed, 15 Mar 2023 22:33:11 +0100 Subject: [PATCH 23/26] Michael comments part 2 --- src/DispatchFunctional.jl | 12 +++++------- src/external/atomsbase.jl | 2 +- src/standard_models.jl | 3 +-- src/terms/exact_exchange.jl | 17 ++++++----------- 4 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/DispatchFunctional.jl b/src/DispatchFunctional.jl index 5a5ceff6b2..5ddea5baa8 100644 --- a/src/DispatchFunctional.jl +++ b/src/DispatchFunctional.jl @@ -11,15 +11,13 @@ function LibxcFunctional(identifier::Symbol) fun = Libxc.Functional(identifier) @assert fun.kind in (:exchange, :correlation, :exchange_correlation) kind = Dict(:exchange => :x, :correlation => :c, :exchange_correlation => :xc)[fun.kind] - + + @assert fun.family in (:lda, :gga, :mgga, :hyb_lda, :hyb_gga, :hyb_mgga) + # Libxc maintains the distinction between hybrid and non-hybrid equivalents, # but this distinction is not relevant for the functional interface - if fun.family == :hyb_lda - family = :lda - elseif fun.family == :hyb_gga - family = :gga - elseif fun.family == :hyb_mgga - family = :mgga + if startswith(string(fun.family), "hyb") + family = Symbol(string(fun.family)[5:end]) else family = fun.family end diff --git a/src/external/atomsbase.jl b/src/external/atomsbase.jl index 35c2b1e9a9..d1286be05c 100644 --- a/src/external/atomsbase.jl +++ b/src/external/atomsbase.jl @@ -90,7 +90,7 @@ Model(system::AbstractSystem; kwargs...) = _call_with_system(Model, system; kwar # Generate equivalent functions for AtomsBase -for fun in (:model_atomic, :model_DFT, :model_LDA, :model_PBE, :model_SCAN) +for fun in (:model_atomic, :model_DFT, :model_LDA, :model_PBE, :model_SCAN, :model_PBE0) @eval function $fun(system::AbstractSystem, args...; kwargs...) _call_with_system($fun, system, args...; kwargs...) end diff --git a/src/standard_models.jl b/src/standard_models.jl index d47ec2ca0c..9b8d15fc8d 100644 --- a/src/standard_models.jl +++ b/src/standard_models.jl @@ -76,11 +76,10 @@ end """ Build an PBE0 model from the specified atoms. DOI:10.1103/PhysRevLett.77.3865 -https://www.vasp.at/tutorials/latest/hybrids/part1/ """ function model_PBE0(lattice::AbstractMatrix, atoms::Vector{<:Element}, positions::Vector{<:AbstractVector}; kwargs...) functional = DispatchFunctional(:hyb_gga_xc_pbeh) model_DFT(lattice, atoms, positions, Xc([functional]), - extra_terms=[ExactExchange(; scaling_factor = 0.25)]; kwargs...) + extra_terms = [ExactExchange(; scaling_factor=0.25)]; kwargs...) end diff --git a/src/terms/exact_exchange.jl b/src/terms/exact_exchange.jl index 3b57d62f01..668d7ab169 100644 --- a/src/terms/exact_exchange.jl +++ b/src/terms/exact_exchange.jl @@ -25,7 +25,7 @@ function TermExactExchange(basis::PlaneWaveBasis{T}, scaling_factor) where T poisson_green_coeffs = T(scaling_factor) .* 4T(π) ./ [sum(abs2, G) for G in G_vectors_cart(basis)] poisson_green_coeffs_kpt = T(scaling_factor) .* 4T(π) ./ [sum(abs2, G) - for G in G_vectors_cart(basis, basis.kpoints[1])] + for G in Gplusk_vectors_cart(basis, basis.kpoints[1])] # Compensating charge background => Zero DC poisson_green_coeffs[1] = 0 @@ -37,12 +37,12 @@ end @timing "ene_ops: ExactExchange" function ene_ops(term::TermExactExchange, basis::PlaneWaveBasis{T}, ψ, occ; ρ, kwargs...) where {T} - ops = [NoopOperator(basis, kpoint) - for (ik, kpoint) in enumerate(basis.kpoints)] + ops = [NoopOperator(basis, kpoint) for (ik, kpoint) in enumerate(basis.kpoints)] isnothing(ψ) && return (E=T(0), ops=ops) ψ, occ = select_occupied_orbitals(basis, ψ, occ; threshold=0.1) - @assert length(ψ) == 1 # TODO: make it work for more kpoints + @assert length(ψ) == 1 # TODO: make it work for more kpoints + @assert temperature == 0 E = T(0) for (k,kpoint) in enumerate(basis.kpoints) @@ -58,7 +58,6 @@ end ind2 = cat(1,basis.fft_size[2]:-1:2;dims=1) ind3 = cat(1,basis.fft_size[3]:-1:2;dims=1) - # equivalent to r_to_G(basis, kpoint, rho_ij_real_conj) rho_ij_four_conj = conj(rho_ij_four[ind1,ind2,ind3]) @@ -71,11 +70,7 @@ end end end - ops = [ExchangeOperator(ψ[ik], term.poisson_green_coeffs_kpt, - basis, kpt) for (ik,kpt) in enumerate(basis.kpoints)] + ops = [ExchangeOperator(ψ[ik], term.poisson_green_coeffs_kpt, basis, kpt) + for (ik,kpt) in enumerate(basis.kpoints)] (E=E, ops=ops) -end - -function pp(x,y) - println() end \ No newline at end of file From 81854cc023f71d320271cfd1d8168c78617cf488 Mon Sep 17 00:00:00 2001 From: bkaperick Date: Wed, 15 Mar 2023 23:15:36 +0100 Subject: [PATCH 24/26] Clean term implementation --- src/DFTK.jl | 2 +- src/terms/exact_exchange.jl | 36 ++++++++++++++++-------------------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/DFTK.jl b/src/DFTK.jl index 56dea3efd8..a8dacdcd24 100644 --- a/src/DFTK.jl +++ b/src/DFTK.jl @@ -113,7 +113,7 @@ include("eigen/preconditioners.jl") include("eigen/diag.jl") export model_atomic -export model_DFT, model_PBE, model_LDA, model_SCAN +export model_DFT, model_PBE, model_LDA, model_SCAN, model_PBE0 include("standard_models.jl") export KerkerMixing, KerkerDosMixing, SimpleMixing, DielectricMixing diff --git a/src/terms/exact_exchange.jl b/src/terms/exact_exchange.jl index 668d7ab169..296e836c0d 100644 --- a/src/terms/exact_exchange.jl +++ b/src/terms/exact_exchange.jl @@ -42,35 +42,31 @@ end ψ, occ = select_occupied_orbitals(basis, ψ, occ; threshold=0.1) @assert length(ψ) == 1 # TODO: make it work for more kpoints - @assert temperature == 0 - + @assert basis.model.temperature == 0 # ground state E = T(0) for (k,kpoint) in enumerate(basis.kpoints) - for psi_i in eachcol(ψ[k]) - for psi_j in eachcol(ψ[k]) - - rho_ij_real = conj(G_to_r(basis, kpoint, psi_i)) .* G_to_r(basis, kpoint, psi_j) - rho_ij_four = r_to_G(basis, rho_ij_real) + for (i,psi_i) in enumerate(eachcol(ψ[k])) + for (j,psi_j) in enumerate(eachcol(ψ[k])) - # Re-ordering of rho_ij_four coeffs to match opposing sign G vectors - # f(r) = Σc_G e^{iG⋅r} implies f^*(r) = Σc_G^* e^{i(-G)⋅r} - ind1 = cat(1,basis.fft_size[1]:-1:2;dims=1) - ind2 = cat(1,basis.fft_size[2]:-1:2;dims=1) - ind3 = cat(1,basis.fft_size[3]:-1:2;dims=1) + psi_i_real = G_to_r(basis, kpoint, psi_i) + psi_j_real = G_to_r(basis, kpoint, psi_j) - # equivalent to r_to_G(basis, kpoint, rho_ij_real_conj) - rho_ij_four_conj = conj(rho_ij_four[ind1,ind2,ind3]) + # ρ_ij(r) = ψ_i(r)^* ψ_j(r) + rho_ij_real = conj(psi_j_real) .* psi_i_real + rho_ij_real_conj = conj(psi_i_real) .* psi_j_real - # Solving poisson equation to obtain potential corresponding to - # ϕ_i(r')ϕ_j(r) / |r - r'| - vij_fourier = rho_ij_four_conj .* term.poisson_green_coeffs + # Poisson solve - ∫ρ_ij(r') / |r-r'|dr' + rho_ij_four = r_to_G(basis, kpoint, rho_ij_real) + v_ij_four = rho_ij_four .* term.poisson_green_coeffs_kpt + + v_ij_real = G_to_r(basis, kpoint, v_ij_four) + E += real(dot(v_ij_real, rho_ij_real_conj)) - E += real(dot(rho_ij_four, vij_fourier)) end end end - + E_scaled = -.5 * E ops = [ExchangeOperator(ψ[ik], term.poisson_green_coeffs_kpt, basis, kpt) for (ik,kpt) in enumerate(basis.kpoints)] - (E=E, ops=ops) + (E=E_scaled, ops=ops) end \ No newline at end of file From b1a703d40221db184a018dd54c96d636ed794029 Mon Sep 17 00:00:00 2001 From: bkaperick Date: Wed, 15 Mar 2023 23:22:16 +0100 Subject: [PATCH 25/26] Clean operator --- src/terms/operators.jl | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/terms/operators.jl b/src/terms/operators.jl index 617e344b2f..1bb4e34bcc 100644 --- a/src/terms/operators.jl +++ b/src/terms/operators.jl @@ -164,20 +164,31 @@ function optimize_operators_(ops) [nonRSmults..., combined_RSmults] end -struct ExchangeOperator <: RealFourierOperator +struct ExchangeOperator{T <: Real} <: RealFourierOperator ψocc poisson_green_coeffs - basis - kpoint + basis::PlaneWaveBasis{T} + kpoint::Kpoint{T} end function apply!(Hψ, op::ExchangeOperator, ψ) + + # - ∑ ψ_n(r) ∫ ψ_n(r')* ψ(r') / |r-r'|dr' for ψ_n in eachcol(op.ψocc) - integral_fourier = op.poisson_green_coeffs .* ψ_n .* ψ.fourier - integral_real = G_to_r(op.basis, op.kpoint, integral_fourier) + + #1. Real-space multiply psi_n_real = G_to_r(op.basis, op.kpoint, ψ_n) - Hψ.real .-= integral_real .* psi_n_real + x_real = ψ.real .* psi_n_real + x_four = r_to_G(op.basis, op.kpoint, x_real) + + # Poisson solve + phi_four = x_four .* op.poisson_green_coeffs + + #Real-space multiply and accumulate + phi_real = G_to_r(op.basis, op.kpoint, phi_four) + Hψ.real .-= psi_n_real .* phi_real + Hψ.fourier .-= ψ_n .* phi_four end end -# TODO Implement Matrix(op::ExchangeOperator) \ No newline at end of file +# TODO Implement Matrix(op::ExchangeOperator) From 91a5ddaa4b8e8033862f64077ab3edf785719f94 Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Thu, 8 Jun 2023 14:42:50 +0200 Subject: [PATCH 26/26] Some progress --- .github/workflows/CompatHelper.yaml | 1 + src/DFTK.jl | 2 +- src/DispatchFunctional.jl | 13 ++- src/fft.jl | 2 +- src/standard_models.jl | 28 ++++- src/terms/Hamiltonian.jl | 4 +- src/terms/exact_exchange.jl | 91 ++++++++-------- src/terms/operators.jl | 51 +++++---- test/hamiltonian_consistency.jl | 154 +++++++++++++++------------- 9 files changed, 205 insertions(+), 141 deletions(-) diff --git a/.github/workflows/CompatHelper.yaml b/.github/workflows/CompatHelper.yaml index 06ec3fcc6d..0b7f2c98d7 100644 --- a/.github/workflows/CompatHelper.yaml +++ b/.github/workflows/CompatHelper.yaml @@ -6,6 +6,7 @@ on: jobs: CompatHelper: + if: github.repository_owner = "JuliaMolSim" runs-on: ubuntu-latest steps: - uses: julia-actions/setup-julia@latest diff --git a/src/DFTK.jl b/src/DFTK.jl index f523e915e7..866aa3a8ee 100644 --- a/src/DFTK.jl +++ b/src/DFTK.jl @@ -133,7 +133,7 @@ include("eigen/preconditioners.jl") include("eigen/diag.jl") export model_atomic -export model_DFT, model_PBE, model_LDA, model_SCAN, model_PBE0 +export model_DFT, model_PBE, model_LDA, model_SCAN, model_PBE0, model_HF include("standard_models.jl") export KerkerMixing, KerkerDosMixing, SimpleMixing, DielectricMixing diff --git a/src/DispatchFunctional.jl b/src/DispatchFunctional.jl index 15736ed142..f110340db6 100644 --- a/src/DispatchFunctional.jl +++ b/src/DispatchFunctional.jl @@ -11,9 +11,9 @@ function LibxcFunctional(identifier::Symbol) fun = Libxc.Functional(identifier) @assert fun.kind in (:exchange, :correlation, :exchange_correlation) kind = Dict(:exchange => :x, :correlation => :c, :exchange_correlation => :xc)[fun.kind] - + @assert fun.family in (:lda, :gga, :mgga, :hyb_lda, :hyb_gga, :hyb_mgga) - + # Libxc maintains the distinction between hybrid and non-hybrid equivalents, # but this distinction is not relevant for the functional interface if startswith(string(fun.family), "hyb") @@ -21,7 +21,7 @@ function LibxcFunctional(identifier::Symbol) else family = fun.family end - + if family == :mgga && Libxc.needs_laplacian(fun) family = :mggal end @@ -161,3 +161,10 @@ for fun in (:potential_terms, :kernel_terms) end end end + +# TODO This is hackish for now until Libxc has fully picked up the DftFunctionals.jl interface +exx_coefficient(::Functional{:lda}) = nothing +exx_coefficient(::Functional{:gga}) = nothing +exx_coefficient(::Functional{:mgga}) = nothing +exx_coefficient(fun::DispatchFunctional) = exx_coefficient(fun.inner) +exx_coefficient(fun::LibxcFunctional) = Libxc.Functional(fun.identifier).exx_coefficient diff --git a/src/fft.jl b/src/fft.jl index 2519eb6b88..60c2dcb59d 100644 --- a/src/fft.jl +++ b/src/fft.jl @@ -25,7 +25,7 @@ function ifft!(f_real::AbstractArray3, basis::PlaneWaveBasis, f_fourier::Abstrac f_real .*= basis.ifft_normalization end function ifft!(f_real::AbstractArray3, basis::PlaneWaveBasis, - kpt::Kpoint, f_fourier::AbstractVector; normalize=true) + kpt::Kpoint, f_fourier::AbstractVector; normalize=true) @assert length(f_fourier) == length(kpt.mapping) @assert size(f_real) == basis.fft_size diff --git a/src/standard_models.jl b/src/standard_models.jl index 399b97fb21..05b76bf405 100644 --- a/src/standard_models.jl +++ b/src/standard_models.jl @@ -33,8 +33,15 @@ function model_DFT(lattice::AbstractMatrix, xc::Xc; extra_terms=[], kwargs...) model_name = isempty(xc.functionals) ? "rHF" : join(string.(xc.functionals), "+") + exx = [ExactExchange(; scaling_factor=exx_coefficient(f)) + for f in xc.functionals if !isnothing(exx_coefficient(f))] + if !isempty(exx) + @assert length(exx) == 1 + @warn "Exact exchange in DFTK is hardly optimised and not yet production-ready." + end + model_atomic(lattice, atoms, positions; - extra_terms=[Hartree(), xc, extra_terms...], model_name, kwargs...) + extra_terms=[Hartree(), xc, exx..., extra_terms...], model_name, kwargs...) end function model_DFT(lattice::AbstractMatrix, atoms::Vector{<:Element}, @@ -81,14 +88,25 @@ DOI:10.1103/PhysRevLett.77.3865 """ function model_PBE0(lattice::AbstractMatrix, atoms::Vector{<:Element}, positions::Vector{<:AbstractVector}; kwargs...) - functional = DispatchFunctional(:hyb_gga_xc_pbeh) - model_DFT(lattice, atoms, positions, Xc([functional]), - extra_terms = [ExactExchange(; scaling_factor=0.25)]; kwargs...) + model_DFT(lattice, atoms, positions, [:hyb_gga_xc_pbeh]; kwargs...) +end + + +""" +Build an Hartree-Fock model from the specified atoms. +""" +function model_HF(lattice::AbstractMatrix, atoms::Vector{<:Element}, + positions::Vector{<:AbstractVector}; extra_terms=[], kwargs...) + @warn "Exact exchange in DFTK is hardly optimised and not yet production-ready." + model_atomic(lattice, atoms, positions; + extra_terms=[Hartree(), ExactExchange(), extra_terms...], + model_name="HF", kwargs...) end # Generate equivalent functions for AtomsBase -for fun in (:model_atomic, :model_DFT, :model_LDA, :model_PBE, :model_SCAN, :model_PBE0) +for fun in (:model_atomic, :model_DFT, :model_LDA, :model_PBE, :model_SCAN, + :model_PBE0, :model_HF) @eval function $fun(system::AbstractSystem, args...; kwargs...) @assert !(:magnetic_moments in keys(kwargs)) parsed = parse_system(system) diff --git a/src/terms/Hamiltonian.jl b/src/terms/Hamiltonian.jl index 54c16b683c..18e52aa50f 100644 --- a/src/terms/Hamiltonian.jl +++ b/src/terms/Hamiltonian.jl @@ -67,8 +67,8 @@ function Base.size(block::HamiltonianBlock) end import Base: Matrix, Array -Array(block::HamiltonianBlock) = Matrix(block) -Matrix(block::HamiltonianBlock) = sum(Matrix, block.operators) +Array(block::HamiltonianBlock) = Matrix(block) +Matrix(block::HamiltonianBlock) = sum(Matrix, block.operators) Matrix(block::GenericHamiltonianBlock) = sum(Matrix, block.optimized_operators) struct Hamiltonian diff --git a/src/terms/exact_exchange.jl b/src/terms/exact_exchange.jl index 296e836c0d..36dafc77b1 100644 --- a/src/terms/exact_exchange.jl +++ b/src/terms/exact_exchange.jl @@ -1,12 +1,11 @@ """ Exact exchange term: the Hartree-Exact exchange energy of the orbitals - -1/2 ∑ ∫∫ ϕ_i^*(r)ϕ_j^*(r')ϕ_i(r')ϕ_j(r) / |r - r'| dr dr' """ struct ExactExchange - scaling_factor::Real # to scale by an arbitrary factor (useful for hybrid models) + scaling_factor::Real # to scale the term (e.g. for hybrid models) end ExactExchange(; scaling_factor=1) = ExactExchange(scaling_factor) (exchange::ExactExchange)(basis) = TermExactExchange(basis, exchange.scaling_factor) @@ -17,56 +16,68 @@ end struct TermExactExchange <: Term scaling_factor::Real # scaling factor, absorbed into poisson_green_coeffs poisson_green_coeffs::AbstractArray - poisson_green_coeffs_kpt::AbstractArray end function TermExactExchange(basis::PlaneWaveBasis{T}, scaling_factor) where T - model = basis.model + scale = T(scaling_factor) + + # q = T[0.5, 0.5, 0.5] + # Gqs = map(G_vectors(basis)) do G + # recip_vector_red_to_cart(basis.model, G + q) + # end + # poisson_green_coeffs = 4T(π) * scale ./ norm2.(Gqs) - poisson_green_coeffs = T(scaling_factor) .* 4T(π) ./ [sum(abs2, G) - for G in G_vectors_cart(basis)] - poisson_green_coeffs_kpt = T(scaling_factor) .* 4T(π) ./ [sum(abs2, G) - for G in Gplusk_vectors_cart(basis, basis.kpoints[1])] - - # Compensating charge background => Zero DC - poisson_green_coeffs[1] = 0 - poisson_green_coeffs_kpt[1] = 0 + poisson_green_coeffs = 4T(π) * scale ./ norm2.(G_vectors_cart(basis)) + poisson_green_coeffs[1] = 0 # Compensating charge background => Zero DC - TermExactExchange(T(scaling_factor), poisson_green_coeffs, poisson_green_coeffs_kpt) + @assert iszero(G_vectors(basis, basis.kpoints[1])[1]) + + TermExactExchange(scale, poisson_green_coeffs) end -@timing "ene_ops: ExactExchange" function ene_ops(term::TermExactExchange, - basis::PlaneWaveBasis{T}, ψ, occ; ρ, +# Note: Implementing exact exchange in a scalable and numerically stable way, such that it +# rapidly converges with k-points is tricky. This implementation here is far too simple and +# slow to be useful. +# +# For further information (in particular on regularising the Coulomb), consider the following +# https://www.vasp.at/wiki/index.php/Coulomb_singularity +# https://journals.aps.org/prb/pdf/10.1103/PhysRevB.34.4405 (QE default) +# https://journals.aps.org/prb/pdf/10.1103/PhysRevB.73.205119 +# https://docs.abinit.org/topics/documents/hybrids-2017.pdf (Abinit apparently +# uses a short-ranged Coulomb) + +@timing "ene_ops: ExactExchange" function ene_ops(term::TermExactExchange, + basis::PlaneWaveBasis{T}, ψ, occupation; kwargs...) where {T} - ops = [NoopOperator(basis, kpoint) for (ik, kpoint) in enumerate(basis.kpoints)] - isnothing(ψ) && return (E=T(0), ops=ops) + if isnothing(ψ) || isnothing(occupation) + return (; E=T(0), ops=NoopOperator.(basis, basis.kpoints)) + end + + @assert iszero(basis.model.temperature) # ground state + ψ, occupation = select_occupied_orbitals(basis, ψ, occupation; threshold=0.1) + E = zero(T) - ψ, occ = select_occupied_orbitals(basis, ψ, occ; threshold=0.1) @assert length(ψ) == 1 # TODO: make it work for more kpoints - @assert basis.model.temperature == 0 # ground state - E = T(0) - for (k,kpoint) in enumerate(basis.kpoints) - for (i,psi_i) in enumerate(eachcol(ψ[k])) - for (j,psi_j) in enumerate(eachcol(ψ[k])) + ik = 1 + kpt = basis.kpoints[ik] + occk = occupation[ik] + ψk = ψ[ik] - psi_i_real = G_to_r(basis, kpoint, psi_i) - psi_j_real = G_to_r(basis, kpoint, psi_j) - - # ρ_ij(r) = ψ_i(r)^* ψ_j(r) - rho_ij_real = conj(psi_j_real) .* psi_i_real - rho_ij_real_conj = conj(psi_i_real) .* psi_j_real + for (n, ψn) in enumerate(eachcol(ψk)) + for (m, ψm) in enumerate(eachcol(ψk)) + ψn_real = ifft(basis, kpt, ψn) + ψm_real = ifft(basis, kpt, ψm) - # Poisson solve - ∫ρ_ij(r') / |r-r'|dr' - rho_ij_four = r_to_G(basis, kpoint, rho_ij_real) - v_ij_four = rho_ij_four .* term.poisson_green_coeffs_kpt + ρnm_real = conj(ψn_real) .* ψm_real + ρmn_real = conj(ψm_real) .* ψn_real - v_ij_real = G_to_r(basis, kpoint, v_ij_four) - E += real(dot(v_ij_real, rho_ij_real_conj)) + Vx_nm_four = fft(basis, ρnm_real) .* term.poisson_green_coeffs + Vx_nm_real = ifft(basis, Vx_nm_four) - end + occ_mn = occk[n] * occk[m] + E -= real(dot(Vx_nm_real, ρmn_real)) * basis.dvol * occ_mn / 2 end end - E_scaled = -.5 * E - ops = [ExchangeOperator(ψ[ik], term.poisson_green_coeffs_kpt, basis, kpt) - for (ik,kpt) in enumerate(basis.kpoints)] - (E=E_scaled, ops=ops) -end \ No newline at end of file + + ops = [ExchangeOperator(basis, kpt, term.poisson_green_coeffs, occk, ψk)] + (E=E, ops=ops) +end diff --git a/src/terms/operators.jl b/src/terms/operators.jl index bdf9de52e3..2025383fcb 100644 --- a/src/terms/operators.jl +++ b/src/terms/operators.jl @@ -9,6 +9,22 @@ They also implement mul! and Matrix(op) for exploratory use. """ abstract type RealFourierOperator end # RealFourierOperator contain fields `basis` and `kpoint` +# +Array(op::RealFourierOperator) = Matrix(op) + +# Slow fallback for getting operator as a dense matrix +function Matrix(op::RealFourierOperator) + T = complex(eltype(op.basis)) + n_G = length(G_vectors(op.basis, op.kpoint)) + H = zeros(T, n_G, n_G) + ψ = zeros(T, n_G) + @views for i in 1:n_G + ψ[i] = one(T) + mul!(H[:, i], op, ψ) + ψ[i] = zero(T) + end + H +end # Unoptimized fallback, intended for exploratory use only. # For performance, call through Hamiltonian which saves FFTs. @@ -164,30 +180,25 @@ function optimize_operators_(ops) end struct ExchangeOperator{T <: Real} <: RealFourierOperator - ψocc - poisson_green_coeffs basis::PlaneWaveBasis{T} kpoint::Kpoint{T} + poisson_green_coeffs # TODO This needs to be typed + occk # TODO This needs to be typed + ψk # TODO This needs to be typed end - function apply!(Hψ, op::ExchangeOperator, ψ) - - # - ∑ ψ_n(r) ∫ ψ_n(r')* ψ(r') / |r-r'|dr' - for ψ_n in eachcol(op.ψocc) - - #1. Real-space multiply - psi_n_real = G_to_r(op.basis, op.kpoint, ψ_n) - x_real = ψ.real .* psi_n_real - x_four = r_to_G(op.basis, op.kpoint, x_real) - - # Poisson solve - phi_four = x_four .* op.poisson_green_coeffs - - #Real-space multiply and accumulate - phi_real = G_to_r(op.basis, op.kpoint, phi_four) - Hψ.real .-= psi_n_real .* phi_real - Hψ.fourier .-= ψ_n .* phi_four + # Hψ = - ∑_n f_n ψ_n(r) ∫ (ψ_n)†(r') * ψ(r') / |r-r'|dr' + for (n, ψnk) in enumerate(eachcol(op.ψk)) + ψnk_real = ifft(op.basis, op.kpoint, ψnk) + x_real = conj(ψnk_real) .* ψ.real + + # Compute integral by Poisson solve + x_four = fft(op.basis, x_real) + Vx_four = x_four .* op.poisson_green_coeffs + Vx_real = ifft(op.basis, Vx_four) + + # Real-space multiply and accumulate + Hψ.real .-= op.occk[n] .* ψnk_real .* Vx_real end end - # TODO Implement Matrix(op::ExchangeOperator) diff --git a/test/hamiltonian_consistency.jl b/test/hamiltonian_consistency.jl index c42f71275a..76f5871eb4 100644 --- a/test/hamiltonian_consistency.jl +++ b/test/hamiltonian_consistency.jl @@ -7,105 +7,121 @@ include("testcases.jl") using Random Random.seed!(0) -function test_matrix_repr_operator(hamk, ψk; atol=1e-8) - for operator in hamk.operators - try - operator_matrix = Matrix(operator) - @test norm(operator_matrix*ψk - operator*ψk) < atol - catch e - allowed_missing_operators = Union{DFTK.DivAgradOperator, - DFTK.MagneticFieldOperator, - DFTK.ExchangeOperator} - @assert operator isa allowed_missing_operators - @info "Matrix of operator $(nameof(typeof(operator))) not yet supported" maxlog=1 - end - end -end - function test_consistency_term(term; rtol=1e-4, atol=1e-8, ε=1e-6, kgrid=[1, 2, 3], kshift=[0, 1, 0]/2, lattice=silicon.lattice, - Ecut=10, spin_polarization=:none) + atoms=silicon.atoms, positions=silicon.positions, + Ecut=10, spin_polarization=:none, integer_occupation=false, + test_matrixification=true + ) sspol = spin_polarization != :none ? " ($spin_polarization)" : "" xc = term isa Xc ? "($(first(term.functionals)))" : "" @testset "$(typeof(term))$xc $sspol" begin - n_dim = 3 - count(iszero, eachcol(lattice)) - Si = n_dim == 3 ? ElementPsp(14, psp=load_psp(silicon.psp)) : ElementCoulomb(:Si) - atoms = [Si, Si] - model = Model(lattice, atoms, silicon.positions; + model = Model(lattice, atoms, positions; terms=[term], spin_polarization, symmetries=true) basis = PlaneWaveBasis(model; Ecut, kgrid, kshift) - n_electrons = silicon.n_electrons - n_bands = div(n_electrons, 2, RoundUp) + n_bands = div(model.n_electrons, 2, RoundUp) filled_occ = DFTK.filled_occupation(model) ψ = [Matrix(qr(randn(ComplexF64, length(G_vectors(basis, kpt)), n_bands)).Q) for kpt in basis.kpoints] occupation = [filled_occ * rand(n_bands) for _ in 1:length(basis.kpoints)] - occ_scaling = n_electrons / sum(sum(occupation)) + occ_scaling = model.n_electrons / sum(sum(occupation)) occupation = [occ * occ_scaling for occ in occupation] + if integer_occupation + occupation = [filled_occ * ones(n_bands) for _ in 1:length(basis.kpoints)] + end + ρ = with_logger(NullLogger()) do compute_density(basis, ψ, occupation) end E0, ham = energy_hamiltonian(basis, ψ, occupation; ρ) - @assert length(basis.terms) == 1 - δψ = [randn(ComplexF64, size(ψ[ik])) for ik = 1:length(basis.kpoints)] - function compute_E(ε) - ψ_trial = ψ .+ ε .* δψ - ρ_trial = with_logger(NullLogger()) do - compute_density(basis, ψ_trial, occupation) + if test_matrixification + @testset "Matrixification of operator" begin + for ik in 1:length(basis.kpoints), operator in ham.blocks[ik].operators + mat_k = Matrix(ham.blocks[ik]) + @test maximum(abs, mat_k - mat_k') < atol + @test norm(mat_k * ψ[ik] - operator * ψ[ik]) < atol + end end - E = energy_hamiltonian(basis, ψ_trial, occupation; ρ=ρ_trial).energies - E.total end - diff = (compute_E(ε) - compute_E(-ε)) / (2ε) + @testset "Operator is derivative of energy" begin + δψ = [randn(ComplexF64, size(ψ[ik])) for ik = 1:length(basis.kpoints)] + function compute_E(ε) + ψ_trial = ψ .+ ε .* δψ + ρ_trial = with_logger(NullLogger()) do + compute_density(basis, ψ_trial, occupation) + end + E = energy_hamiltonian(basis, ψ_trial, occupation; ρ=ρ_trial).energies + E.total + end + diff = (compute_E(ε) - compute_E(-ε)) / (2ε) - diff_predicted = 0.0 - for ik in 1:length(basis.kpoints) - Hψk = ham.blocks[ik]*ψ[ik] - test_matrix_repr_operator(ham.blocks[ik], ψ[ik]; atol) - δψkHψk = sum(occupation[ik][iband] * real(dot(δψ[ik][:, iband], Hψk[:, iband])) - for iband=1:n_bands) - diff_predicted += 2 * basis.kweights[ik] * δψkHψk - end - diff_predicted = mpi_sum(diff_predicted, basis.comm_kpts) + diff_predicted = 0.0 + for ik in 1:length(basis.kpoints) + Hψk = ham.blocks[ik] * ψ[ik] + occk = occupation[ik] + δψkHψk = sum(occk[iband] * real(dot(δψ[ik][:, iband], Hψk[:, iband])) + for iband=1:n_bands) + diff_predicted += 2 * basis.kweights[ik] * δψkHψk + end + diff_predicted = mpi_sum(diff_predicted, basis.comm_kpts) - err = abs(diff - diff_predicted) - @test err < rtol * abs(E0.total) || err < atol + err = abs(diff - diff_predicted) + @test err < rtol * abs(E0.total) || err < atol + end end end @testset "Hamiltonian consistency" begin - test_consistency_term(Kinetic()) - test_consistency_term(AtomicLocal()) - test_consistency_term(AtomicNonlocal()) - test_consistency_term(ExternalFromReal(X -> cos(X[1]))) - test_consistency_term(ExternalFromFourier(X -> abs(norm(X)))) - test_consistency_term(LocalNonlinearity(ρ -> ρ^2)) - test_consistency_term(Hartree()) - test_consistency_term(Ewald()) - test_consistency_term(PspCorrection()) - test_consistency_term(ExactExchange(), kgrid=[1,1,1]) - test_consistency_term(Xc(:lda_xc_teter93)) - test_consistency_term(Xc(:lda_xc_teter93), spin_polarization=:collinear) - test_consistency_term(Xc(:gga_x_pbe), spin_polarization=:collinear) - test_consistency_term(Xc(:mgga_x_tpss)) - test_consistency_term(Xc(:mgga_x_scan)) - test_consistency_term(Xc(:mgga_c_scan), spin_polarization=:collinear) - test_consistency_term(Xc(:mgga_x_b00)) - test_consistency_term(Xc(:mgga_c_b94), spin_polarization=:collinear) + # test_consistency_term(Kinetic()) + # test_consistency_term(AtomicLocal()) + # test_consistency_term(AtomicNonlocal()) + # test_consistency_term(ExternalFromReal(X -> cos(X[1]))) + # test_consistency_term(ExternalFromFourier(X -> abs(norm(X)))) + # test_consistency_term(LocalNonlinearity(ρ -> ρ^2)) + # test_consistency_term(Hartree()) + # test_consistency_term(Ewald()) + # test_consistency_term(PspCorrection()) + # test_consistency_term(ExactExchange(), kgrid=(1, 1, 1)) + # test_consistency_term(Xc(:lda_xc_teter93)) + # test_consistency_term(Xc(:lda_xc_teter93), spin_polarization=:collinear) + # test_consistency_term(Xc(:gga_x_pbe), spin_polarization=:collinear) + # test_consistency_term(Xc(:mgga_x_tpss)) + # test_consistency_term(Xc(:mgga_x_scan)) + # test_consistency_term(Xc(:mgga_c_scan), spin_polarization=:collinear) + # test_consistency_term(Xc(:mgga_x_b00)) + # test_consistency_term(Xc(:mgga_c_b94), spin_polarization=:collinear) let - a = 6 - pot(x, y, z) = (x - a/2)^2 + (y - a/2)^2 - Apot(x, y, z) = .2 * [y - a/2, -(x - a/2), 0] - Apot(X) = Apot(X...) - test_consistency_term(Magnetic(Apot); kgrid=[1, 1, 1], kshift=[0, 0, 0], - lattice=[a 0 0; 0 a 0; 0 0 0], Ecut=20) - test_consistency_term(DFTK.Anyonic(2, 3.2); kgrid=[1, 1, 1], kshift=[0, 0, 0], - lattice=[a 0 0; 0 a 0; 0 0 0], Ecut=20) + lattice = 10diagm(ones(3)) + positions = [0.5ones(3)] + atoms = [ElementCoulomb(:Be)] + test_consistency_term(ExactExchange(); + lattice, positions, atoms, + kgrid=(1, 1, 1), Ecut=7, + integer_occupation=true) end + test_consistency_term(ExactExchange(); + kgrid=(1, 1, 1), Ecut=7, + integer_occupation=true) + + # let + # a = 6 + # pot(x, y, z) = (x - a/2)^2 + (y - a/2)^2 + # Apot(x, y, z) = .2 * [y - a/2, -(x - a/2), 0] + # Apot(X) = Apot(X...) + # + # Si = ElementCoulomb(:Si) + # atoms = [Si, Si] + # lattice = [[a 0 0; 0 a 0; 0 0 0] + # + # test_consistency_term(Magnetic(Apot); kgrid=[1, 1, 1], kshift=[0, 0, 0], + # lattice, atoms, Ecut=20) + # test_consistency_term(DFTK.Anyonic(2, 3.2); kgrid=[1, 1, 1], kshift=[0, 0, 0], + # lattice, atoms, Ecut=20) + # end end