Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ext/DFTKCUDAExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import DFTK: CPU, GPU, DispatchFunctional, precompilation_workflow
using DftFunctionals
using DFTK
using Libxc
import ForwardDiff: Dual

DFTK.synchronize_device(::GPU{<:CUDA.CuArray}) = CUDA.synchronize()

Expand All @@ -20,6 +21,13 @@ for fun in (:potential_terms, :kernel_terms)
end
end

# Ensure DFTK's custom ForwardDiff rule for FFTs is used.
# See: https://github.com/JuliaGPU/CUDA.jl/issues/3018
function Base.:*(p::CUFFT.CuFFTPlan{T,S,K,false},
x::CuArray{<:Complex{<:Dual{Tg}}}) where {T,S,K,Tg}
DFTK.dual_fft_mul(p, x)
end

# Insure pre-compilation can proceed without error (old Julia/packages versions)
if Libxc.has_cuda() && !isnothing(Base.get_extension(Libxc, :LibxcCudaExt))

Expand Down
2 changes: 1 addition & 1 deletion src/DispatchFunctional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function DftFunctionals.has_energy(func::LibxcFunctional)
0 in Libxc.supported_derivatives(Libxc.Functional(func.identifier))
end

function libxc_unfold_spin(data::Matrix, n_spin::Int)
function libxc_unfold_spin(data::AbstractMatrix, n_spin::Int)
n_p = size(data, 2)
if n_spin == 1
data # Only one spin component
Expand Down
6 changes: 3 additions & 3 deletions src/gpu/gpu_arrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ function LinearAlgebra.norm(A::Hermitian{T, <:AbstractGPUArray}) where {T}
sqrt(2upper_triangle - diago)
end


# Make sure that there is a CPU fallback for AbstractGPUArrays (e.g. for Duals)
for fun in (:potential_terms, :kernel_terms)
@eval function DftFunctionals.$fun(fun::DispatchFunctional, ρ::AT,
args...) where {AT <: AbstractGPUArray{Float64}}
args...) where {AT <: AbstractGPUArray}
# Fallback implementation for the GPU: Transfer to the CPU and run computation there
cpuify(::Nothing) = nothing
cpuify(x::AbstractArray) = Array(x)
$fun(fun, Array(ρ), cpuify.(args)...)
end
end
end
2 changes: 2 additions & 0 deletions src/response/chi0.jl
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ to the Hamiltonian change `δH` represented by the matrix-vector products `δHψ
# We then use the extra information we have from these additional bands,
# non-necessarily converged, to split the Sternheimer_solver with a Schur
# complement.
occupation = [to_cpu(oc) for oc in occupation]
(mask_occ, mask_extra) = occupied_empty_masks(occupation, occupation_threshold)

ψ_occ = [ψ[ik][:, maskk] for (ik, maskk) in enumerate(mask_occ)]
Expand Down Expand Up @@ -561,6 +562,7 @@ function construct_bandtol(Bandtol::Type, basis::PlaneWaveBasis, ψ, occupation:
Ω = basis.model.unit_cell_volume
Ng = prod(basis.fft_size)
Nk = length(basis.kpoints)
occupation = [to_cpu(oc) for oc in occupation]
mask_occ = occupied_empty_masks(occupation, occupation_threshold).mask_occ

# Including k-points the expression (3.11) in 2505.02319 becomes
Expand Down
13 changes: 8 additions & 5 deletions src/terms/local_nonlinearity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ end

function ene_ops(term::TermLocalNonlinearity, basis::PlaneWaveBasis{T}, ψ, occupation;
ρ, kwargs...) where {T}

# FD on the GPU, when T<:Dual causes all sorts of troubles, at least on AMD.
# Because this is not often use, simply treat it on the CPU, and move result to GPU.
fp(ρ) = ForwardDiff.derivative(term.f, ρ)
E = sum(fρ -> convert_dual(T, fρ), term.f.(ρ)) * basis.dvol
potential = convert_dual.(T, fp.(ρ))
potential = to_device(basis.architecture, convert_dual.(T, fp.(to_cpu(ρ))))

# In the case of collinear spin, the potential is spin-dependent
ops = [RealSpaceMultiplication(basis, kpt, potential[:, :, :, kpt.spin])
Expand All @@ -22,16 +25,16 @@ function ene_ops(term::TermLocalNonlinearity, basis::PlaneWaveBasis{T}, ψ, occu
end


function compute_kernel(term::TermLocalNonlinearity, ::AbstractBasis{T}; ρ, kwargs...) where {T}
function compute_kernel(term::TermLocalNonlinearity, basis::AbstractBasis{T}; ρ, kwargs...) where {T}
fp(ρ) = ForwardDiff.derivative(term.f, ρ)
fpp(ρ) = ForwardDiff.derivative(fp, ρ)
Diagonal(vec(convert_dual.(T, fpp.(ρ))))
Diagonal(to_device(basis.architecture, vec(convert_dual.(T, fpp.(to_cpu(ρ))))))
end

function apply_kernel(term::TermLocalNonlinearity, ::AbstractBasis{T},
function apply_kernel(term::TermLocalNonlinearity, basis::AbstractBasis{T},
δρ::AbstractArray{Tδρ}; ρ, kwargs...) where {T, Tδρ}
S = promote_type(T, Tδρ)
fp(ρ) = ForwardDiff.derivative(term.f, ρ)
fpp(ρ) = ForwardDiff.derivative(fp, ρ)
convert_dual.(S, fpp.(ρ) .* δρ)
to_device(basis.architecture, convert_dual.(S, fpp.(to_cpu(ρ)) .* to_cpu(δρ)))
end
15 changes: 12 additions & 3 deletions src/terms/xc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function (xc::Xc)(basis::PlaneWaveBasis{T}) where {T}
# Strip duals from functional parameters if needed
params = parameters(fun)
if !isempty(params)
newparams = convert_dual.(T, params)
newparams = map(p -> convert_dual(T, p), params)
fun = change_parameters(fun, newparams; keep_identifier=true)
end
fun
Expand Down Expand Up @@ -427,7 +427,7 @@ function apply_kernel(term::TermXc, basis::PlaneWaveBasis{T}, δρ::AbstractArra

# If the XC functional is not supported for an architecture, terms is on the CPU
terms = kernel_terms(term.functionals, density)
δV = zeros(Tδρ, size(ρ)...) # [ix, iy, iz, iσ]
δV = zeros_like(δρ, Tδρ, size(ρ)...) # [ix, iy, iz, iσ]

Vρρ = to_device(basis.architecture, reshape(terms.Vρρ, n_spin, n_spin, basis.fft_size...))
@views for s = 1:n_spin, t = 1:n_spin # LDA term
Expand Down Expand Up @@ -529,11 +529,20 @@ _matify(data::AbstractArray) = reshape(data, size(data, 1), :)

for fun in (:potential_terms, :kernel_terms)
@eval begin
function DftFunctionals.$fun(xc::Functional, density::LibxcDensities)
function DftFunctionals.$fun(xc::DispatchFunctional, density::LibxcDensities)
$fun(xc, _matify(density.ρ_real), _matify(density.σ_real),
_matify(density.τ_real), _matify(density.Δρ_real))
end

# Ensure functionals from DftFunctionals are sent to the CPU
# TODO: Allow GPUArrys once DftFunctionals is refactored to support GPU.
function DftFunctionals.$fun(fun::DftFunctionals.Functional, density::LibxcDensities)
Comment thread
mfherbst marked this conversation as resolved.
maticpuify(::Nothing) = nothing
maticpuify(x::AbstractArray) = reshape(Array(x), size(x, 1), :)
DftFunctionals.$fun(fun, maticpuify(density.ρ_real), maticpuify(density.σ_real),
maticpuify(density.τ_real), maticpuify(density.Δρ_real))
end

function DftFunctionals.$fun(xcs::Vector{Functional}, density::LibxcDensities)
isempty(xcs) && return NamedTuple()
result = $fun(xcs[1], density)
Expand Down
26 changes: 16 additions & 10 deletions src/workarounds/forwarddiff_rules.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ function LinearAlgebra.mul!(y::AbstractArray{<:Union{Complex{<:Dual}}},
x::AbstractArray{<:Union{Complex{<:Dual}}})
copyto!(y, p*x)
end
function Base.:*(p::AbstractFFTs.Plan, x::AbstractArray{<:Complex{<:Dual{Tg}}}) where {Tg}
# Custom rule for FFTs used to overload the Base.:* operator. This is a function
# of it's own because DFTKCUDAExt.jl uses it to implement a specific overload.
function dual_fft_mul(p::AbstractFFTs.Plan, x::AbstractArray{<:Complex{<:Dual{Tg}}}) where {Tg}
# TODO do we want x::AbstractArray{<:Dual{T}} too?
xtil = p * ForwardDiff.value.(x)
dxtils = ntuple(ForwardDiff.npartials(eltype(x))) do n
Expand All @@ -46,6 +48,9 @@ function Base.:*(p::AbstractFFTs.Plan, x::AbstractArray{<:Complex{<:Dual{Tg}}})
)
end
end
function Base.:*(p::AbstractFFTs.Plan, x::AbstractArray{<:Complex{<:Dual{Tg}}}) where {Tg}
dual_fft_mul(p, x)
end

function build_fft_plans!(tmp::AbstractArray{Complex{T}}) where {T<:Dual}
opFFT = AbstractFFTs.plan_fft(tmp)
Expand Down Expand Up @@ -230,9 +235,9 @@ end


@timing "self_consistent_field ForwardDiff" function self_consistent_field(
basis_dual::PlaneWaveBasis{T};
basis_dual::PlaneWaveBasis{<:Dual{Tg,V,N}};
response=ResponseOptions(),
kwargs...) where {T <: Dual}
kwargs...) where {Tg,V,N}
# Note: No guarantees on this interface yet.

# Primal pass
Expand All @@ -251,28 +256,29 @@ end
end
# Implicit differentiation
response.verbose && println("Solving response problem")
δresults = ntuple(ForwardDiff.npartials(T)) do α
δresults = ntuple(N) do α
δHextψ = [ForwardDiff.partials.(δHextψk, α) for δHextψk in Hψ_dual]
δtemperature = ForwardDiff.partials(basis_dual.model.temperature, α)
solve_ΩplusK_split(scfres, δHextψ; δtemperature,
tol=last(scfres.history_Δρ), response.verbose)
end

# Convert and combine
DT = Dual{ForwardDiff.tagtype(T)}
ψ = map(scfres.ψ, getfield.(δresults, :δψ)...) do ψk, δψk...
map(ψk, δψk...) do ψnk, δψnk...
Complex(DT(real(ψnk), real.(δψnk)),
DT(imag(ψnk), imag.(δψnk)))
Complex(Dual{Tg}(real(ψnk), real.(δψnk)),
Dual{Tg}(imag(ψnk), imag.(δψnk)))
end
end
eigenvalues = map(scfres.eigenvalues, getfield.(δresults, :δeigenvalues)...) do εk, δεk...
map((εnk, δεnk...) -> DT(εnk, δεnk), εk, δεk...)
map((εnk, δεnk...) -> Dual{Tg}(εnk, δεnk), εk, δεk...)
Comment thread
mfherbst marked this conversation as resolved.
end
occupation = map(scfres.occupation, getfield.(δresults, :δoccupation)...) do occk, δocck...
map((occnk, δoccnk...) -> DT(occnk, δoccnk), occk, δocck...)
occk_cpu = to_cpu(occk)
to_device(basis_dual.architecture,
map((occk_cpu, δocck...) -> Dual{Tg}(occk_cpu, δocck), occk_cpu, δocck...))
end
εF = DT(scfres.εF, getfield.(δresults, :δεF)...)
εF = Dual{Tg}(scfres.εF, getfield.(δresults, :δεF)...)

# For strain, basis_dual contributes an explicit lattice contribution which
# is not contained in δresults, so we need to recompute ρ here
Expand Down
Loading
Loading