Skip to content

Commit 380965e

Browse files
committedOct 7, 2020
Make Plots an optional dependency
1 parent d9880b5 commit 380965e

9 files changed

+106
-81
lines changed
 

‎Project.toml

+2-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56"
2323
Optim = "429524aa-4258-5aef-a3af-852621145aeb"
2424
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
2525
PeriodicTable = "7b2266bf-644c-5ea3-82d8-af4bbd25a884"
26-
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
2726
Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45"
2827
Primes = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae"
2928
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
@@ -55,7 +54,6 @@ NLsolve = "4"
5554
Optim = "0.22, 1"
5655
OrderedCollections = "1"
5756
PeriodicTable = "1"
58-
Plots = "1"
5957
Polynomials = "1"
6058
Primes = "0.4, 0.5"
6159
ProgressMeter = "1"
@@ -73,8 +71,9 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
7371
DoubleFloats = "497a8b3b-efae-58df-a0af-a86822472b78"
7472
IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
7573
KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77"
74+
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
7675
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
7776
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
7877

7978
[targets]
80-
test = ["Test", "Aqua", "DoubleFloats", "IntervalArithmetic", "Random", "KrylovKit"]
79+
test = ["Test", "Aqua", "DoubleFloats", "IntervalArithmetic", "Plots", "Random", "KrylovKit"]

‎examples/collinear_magnetism.jl

+1-7
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,7 @@ idown = iup + length(scfres.basis.kpoints) ÷ 2
103103
# around the Fermi level, where the spin-up and spin-down DOS differ.
104104

105105
using Plots
106-
εs = range(minimum(minimum(scfres.eigenvalues)) - .5,
107-
maximum(maximum(scfres.eigenvalues)) + .5, length=1000)
108-
Dup = DOS.(εs, Ref(basis), Ref(scfres.eigenvalues), spins=(1, )) # DOS spin-up
109-
Ddown = DOS.(εs, Ref(basis), Ref(scfres.eigenvalues), spins=(2, )) # DOS spin-down
110-
q = plot(εs, Dup, label="DOS :up", color=:blue)
111-
plot!(q, εs, Ddown, label="DOS :down", color=:red)
112-
vline!(q, [scfres.εF], label="εF", color=:green, lw=1.5)
106+
plot_dos(scfres)
113107

114108
# Similarly the band structure shows clear differences between both spin components.
115109
plot_bandstructure(scfres, kline_density=3, unit=:eV)

‎examples/metallic_systems.jl

+1-6
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,4 @@ scfres.energies
5151

5252
# The fact that magnesium is a metal is confirmed
5353
# by plotting the density of states around the Fermi level.
54-
55-
εs = range(minimum(minimum(scfres.eigenvalues)) - .5,
56-
maximum(maximum(scfres.eigenvalues)) + .5, length=1000)
57-
Ds = DOS.(εs, Ref(basis), Ref(scfres.eigenvalues))
58-
q = plot(εs, Ds, label="DOS")
59-
vline!(q, [scfres.εF], label="εF")
54+
plot_dos(scfres)

‎src/DFTK.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,13 @@ include("external/pymatgen.jl")
156156

157157
export high_symmetry_kpath
158158
export compute_bands
159-
export plot_band_data
160159
export plot_bandstructure
161160
include("postprocess/band_structure.jl")
162161

163162
export DOS
164163
export LDOS
165164
export NOS
165+
export plot_dos
166166
include("postprocess/DOS.jl")
167167
export compute_χ0
168168
export apply_χ0
@@ -184,6 +184,7 @@ function __init__()
184184
@require DoubleFloats="497a8b3b-efae-58df-a0af-a86822472b78" begin
185185
!isdefined(DFTK, :GENERIC_FFT_LOADED) && include("fft_generic.jl")
186186
end
187+
@require Plots="91a5bcdd-55d7-5caf-9e0b-520d859cae80" include("plotting.jl")
187188
end
188189

189190
end # module DFTK

‎src/plotting.jl

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import Plots
2+
3+
# This is needed to flag that the plots-dependent code has been loaded
4+
const PLOTS_LOADED = true
5+
6+
"""
7+
Plot the trace of an SCF, i.e. the absolute error of the total energy at
8+
each iteration versus the converged energy in a semilog plot. By default
9+
a new plot canvas is generated, but an existing one can be passed and reused
10+
along with `kwargs` for the call to `plot!`.
11+
"""
12+
function ScfPlotTrace(plt=Plots.plot(yaxis=:log); kwargs...)
13+
energies = Float64[]
14+
function callback(info)
15+
if info.stage == :finalize
16+
minenergy = minimum(energies[max(1, end-5):end])
17+
error = abs.(energies .- minenergy)
18+
error[error .== 0] .= NaN
19+
extra = ifelse(:mark in keys(kwargs), (), (mark=:x, ))
20+
Plots.plot!(plt, error; extra..., kwargs...)
21+
display(plt)
22+
else
23+
push!(energies, info.energies.total)
24+
end
25+
end
26+
end
27+
28+
29+
function plot_band_data(band_data; εF=nothing,
30+
klabels=Dict{String, Vector{Float64}}(), unit=:eV, kwargs...)
31+
eshift = isnothing(εF) ? 0.0 : εF
32+
data = prepare_band_data(band_data, klabels=klabels)
33+
34+
# For each branch, plot all bands, spins and errors
35+
p = Plots.plot(xlabel="wave vector")
36+
for ibranch = 1:data.n_branches
37+
kdistances = data.kdistances[ibranch]
38+
for spin in data.spins, iband = 1:data.n_bands
39+
yerror = nothing
40+
if hasproperty(data, :λerror)
41+
yerror = data.λerror[ibranch][spin][iband, :] ./ unit_to_au(unit)
42+
end
43+
energies = (data.λ[ibranch][spin][iband, :] .- eshift) ./ unit_to_au(unit)
44+
45+
color = (spin == :up) ? :blue : :red
46+
Plots.plot!(p, kdistances, energies; color=color, label="", yerror=yerror,
47+
kwargs...)
48+
end
49+
end
50+
51+
# X-range: 0 to last kdistance value
52+
Plots.xlims!(p, (0, data.kdistances[end][end]))
53+
Plots.xticks!(p, data.ticks["distance"],
54+
[replace(l, raw"$\mid$" => " | ") for l in data.ticks["label"]])
55+
56+
ylims = [-4, 4]
57+
!isnothing(εF) && is_metal(band_data, εF) && (ylims = [-10, 10])
58+
ylims = round.(ylims * units.eV ./ unit_to_au(unit), sigdigits=2)
59+
if isnothing(εF)
60+
Plots.ylabel!(p, "eigenvalues ($(string(unit))")
61+
else
62+
Plots.ylabel!(p, "eigenvalues - ε_f ($(string(unit)))")
63+
Plots.ylims!(p, ylims...)
64+
end
65+
66+
p
67+
end
68+
69+
70+
function plot_dos(basis, eigenvalues; εF=nothing)
71+
n_spin = basis.model.n_spin_components
72+
εs = range(minimum(minimum(eigenvalues)) - .5,
73+
maximum(maximum(eigenvalues)) + .5, length=1000)
74+
75+
p = Plots.plot()
76+
spinlabels = spin_components(basis.model)
77+
colors = [:blue, :red]
78+
for σ in 1:n_spin
79+
D = DOS.(εs, Ref(basis), Ref(eigenvalues), spins=(σ, ))
80+
label = n_spin > 1 ? "DOS $(spinlabels[σ]) spin" : "DOS"
81+
Plots.plot!(p, εs, D, label=label, color=colors[σ])
82+
end
83+
if !isnothing(εF)
84+
Plots.vline!(p, [εF], label="εF", color=:green, lw=1.5)
85+
end
86+
p
87+
end
88+
plot_dos(scfres; kwargs...) = plot_dos(scfres.basis, scfres.eigenvalues; εF=scfres.εF, kwargs...)

‎src/postprocess/DOS.jl

+5
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,8 @@ function LDOS(ε, basis, eigenvalues, ψ; smearing=basis.model.smearing,
105105
end
106106
return sum(ρs[iσ] forin spins)
107107
end
108+
109+
"""
110+
Plot the density of states over a reasonable range
111+
"""
112+
function plot_dos end

‎src/postprocess/band_structure.jl

+5-42
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using PyCall
2-
import Plots
32

43
# Functionality for computing band structures, mostly using pymatgen
54

@@ -102,47 +101,6 @@ function is_metal(band_data, εF, tol=1e-4)
102101
false
103102
end
104103

105-
106-
function plot_band_data(band_data; εF=nothing,
107-
klabels=Dict{String, Vector{Float64}}(), unit=:eV, kwargs...)
108-
eshift = isnothing(εF) ? 0.0 : εF
109-
data = prepare_band_data(band_data, klabels=klabels)
110-
111-
# For each branch, plot all bands, spins and errors
112-
p = Plots.plot(xlabel="wave vector")
113-
for ibranch = 1:data.n_branches
114-
kdistances = data.kdistances[ibranch]
115-
for spin in data.spins, iband = 1:data.n_bands
116-
yerror = nothing
117-
if hasproperty(data, :λerror)
118-
yerror = data.λerror[ibranch][spin][iband, :] ./ unit_to_au(unit)
119-
end
120-
energies = (data.λ[ibranch][spin][iband, :] .- eshift) ./ unit_to_au(unit)
121-
122-
color = (spin == :up) ? :blue : :red
123-
Plots.plot!(p, kdistances, energies; color=color, label="", yerror=yerror,
124-
kwargs...)
125-
end
126-
end
127-
128-
# X-range: 0 to last kdistance value
129-
Plots.xlims!(p, (0, data.kdistances[end][end]))
130-
Plots.xticks!(p, data.ticks["distance"],
131-
[replace(l, raw"$\mid$" => " | ") for l in data.ticks["label"]])
132-
133-
ylims = [-4, 4]
134-
!isnothing(εF) && is_metal(band_data, εF) && (ylims = [-10, 10])
135-
ylims = round.(ylims * units.eV ./ unit_to_au(unit), sigdigits=2)
136-
if isnothing(εF)
137-
Plots.ylabel!(p, "eigenvalues ($(string(unit))")
138-
else
139-
Plots.ylabel!(p, "eigenvalues - ε_f ($(string(unit)))")
140-
Plots.ylims!(p, ylims...)
141-
end
142-
143-
p
144-
end
145-
146104
function detexify_kpoint(string)
147105
# For some reason Julia doesn't support this naively: https://github.com/JuliaLang/julia/issues/29849
148106
replacements = ("\\Gamma" => "Γ",
@@ -163,6 +121,10 @@ are plotted in `:eV` unless a different `unit` is selected.
163121
"""
164122
function plot_bandstructure(basis, ρ, ρspin, n_bands;
165123
εF=nothing, kline_density=20, unit=:eV, kwargs...)
124+
if !isdefined(DFTK, :PLOTS_LOADED)
125+
error("Plots not loaded. Run 'using Plots' before calling plot_bandstructure.")
126+
end
127+
166128
# Band structure calculation along high-symmetry path
167129
kcoords, klabels, kpath = high_symmetry_kpath(basis.model; kline_density=kline_density)
168130
println("Computing bands along kpath:")
@@ -173,6 +135,7 @@ function plot_bandstructure(basis, ρ, ρspin, n_bands;
173135
if kline_density 10
174136
plotargs = (markersize=2, markershape=:circle)
175137
end
138+
176139
plot_band_data(band_data; εF=εF, klabels=klabels, unit=unit, plotargs...)
177140
end
178141
function plot_bandstructure(scfres; n_bands=nothing, kwargs...)

‎src/scf/scf_callbacks.jl

+2-21
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,5 @@
1-
"""
2-
Plot the trace of an SCF, i.e. the absolute error of the total energy at
3-
each iteration versus the converged energy in a semilog plot. By default
4-
a new plot canvas is generated, but an existing one can be passed and reused
5-
along with `kwargs` for the call to `plot!`.
6-
"""
7-
function ScfPlotTrace(plt=plot(yaxis=:log); kwargs...)
8-
energies = Float64[]
9-
function callback(info)
10-
if info.stage == :finalize
11-
minenergy = minimum(energies[max(1, end-5):end])
12-
error = abs.(energies .- minenergy)
13-
error[error .== 0] .= NaN
14-
extra = ifelse(:mark in keys(kwargs), (), (mark=:x, ))
15-
plot!(plt, error; extra..., kwargs...)
16-
display(plt)
17-
else
18-
push!(energies, info.energies.total)
19-
end
20-
end
21-
end
1+
# For ScfPlotTrace() see DFTK.jl/src/plotting.jl, which is conditionally loaded upon
2+
# Plots.jl is included.
223

234
"""
245
Default callback function for `self_consistent_field`, which prints a convergence table

‎src/scf/self_consistent_field.jl

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using Plots
21
include("scf_callbacks.jl")
32

43
function default_n_bands(model)

0 commit comments

Comments
 (0)
Please sign in to comment.