Skip to content

Commit 31e8dba

Browse files
committed
add Hubbard
1. add read and write for hubbard card 2. add some utility functions for parsing pw inputs
1 parent b2e3f49 commit 31e8dba

3 files changed

Lines changed: 230 additions & 9 deletions

File tree

src/common/const.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,20 @@ This is the default (Physical constants, SI (NIST 2018)) value in QE
66
`Modules/constants.f90`.
77
"""
88
const Bohr_QE::Float64 = 0.529177210903
9+
10+
"""
11+
pw.x input keywords
12+
"""
13+
const PW_KEYWORDS = Set([
14+
"atomic_species",
15+
"atomic_positions",
16+
"k_points",
17+
"additional_k_points",
18+
"cell_parameters",
19+
"constraints",
20+
"occupations",
21+
"atomic_velocities",
22+
"atomic_forces",
23+
"solvents",
24+
"hubbard",
25+
])

src/namelist.jl

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ function read_namelists(io::IO; all_lines::Bool=false)
8484
end
8585
return namelists
8686
end
87-
8887
end
8988

9089
function read_namelists(filename::AbstractString; kwargs...)
@@ -168,12 +167,61 @@ find_card(lines, name)
168167
"""
169168
function find_card(lines::AbstractVector, name::AbstractString)
170169
lname = lowercase(name)
171-
return findfirst(
172-
line -> startswith(lowercase(remove_comment(line)), lname),
173-
lines,
174-
)
170+
return findfirst(line -> startswith(lowercase(remove_comment(line)), lname), lines)
171+
end
172+
173+
# TODO only considers pw keywords for now
174+
function is_namelist_keyword(line; keywords=PW_KEYWORDS)
175+
line = strip(line)
176+
startswith(line, "&") && return true
177+
178+
sl = split(line)[1]
179+
lowercase(sl) in keywords && return true
180+
return false
181+
end
182+
183+
"""
184+
$(SIGNATURES)
185+
186+
Find the last non-empty line in current card, which is any of the following:
187+
1. end of file
188+
2. the line before next card keyword
189+
3. empty line (this is assuming no empty lines in the middle of the card)
190+
191+
# Arguments
192+
- `lines::AbstractVector`: The lines to read from.
193+
- `icard::Integer`: Beginning of the current card.
194+
195+
# Returns
196+
- The index of the last line in the card.
197+
198+
# Examples
199+
```jldoctest; setup = :(using QuantumEspressoIO: end_of_card)
200+
lines = [
201+
"HUBBARD atomic",
202+
"U Ni-3d 5.77",
203+
"U Ni1-3d 5.77",
204+
"ATOMIC_POSITIONS"
205+
]
206+
207+
end_of_card(lines, 1)
208+
# output
209+
3
210+
"""
211+
function end_of_card(lines::AbstractVector, istart::Integer)
212+
# advance to next line
213+
istart += 1
214+
while istart <= length(lines)
215+
line = strip(lines[istart])
216+
isempty(line) && return istart - 1
217+
is_namelist_keyword(line) && return istart - 1
218+
istart += 1
219+
end
220+
# end of lines
221+
return istart - 1
175222
end
176223

224+
177225
"""
178226
$(SIGNATURES)
179227
@@ -189,6 +237,10 @@ Get the option of a card.
189237
```jldoctest; setup = :(using QuantumEspressoIO: parse_card_option)
190238
julia> parse_card_option("POSITIONS angstrom ! comment")
191239
"angstrom"
240+
julia> parse_card_option("POSITIONS (automatic) ! comment")
241+
"automatic"
242+
julia> parse_card_option("POSITIONS {automatic} ! comment")
243+
"automatic"
192244
```
193245
```jldoctest; setup = :(using QuantumEspressoIO: parse_card_option)
194246
julia> parse_card_option("POSITIONS {angstrom} ! comment")
@@ -197,6 +249,7 @@ julia> parse_card_option("POSITIONS {angstrom} ! comment")
197249
"""
198250
@inline function parse_card_option(line::AbstractString)
199251
cardline = remove_comment(line)
252+
cardline = replace(cardline, "("=>"", ")"=>"", "{"=>"", "}"=>"")
200253
parts = split(cardline; limit=2)
201254
if length(parts) < 2
202255
return nothing
@@ -237,7 +290,9 @@ println(lines)
237290
[" name2 = value2"]
238291
```
239292
"""
240-
function parse_card!(lines::AbstractVector, name::AbstractString, n_lines::Union{Integer,Nothing}=nothing)
293+
function parse_card!(
294+
lines::AbstractVector, name::AbstractString, n_lines::Union{Integer,Nothing}=nothing
295+
)
241296
istart = find_card(lines, name)
242297
# nothing found
243298
isnothing(istart) && return nothing
@@ -315,7 +370,7 @@ function write_namelist(io::IO, name::AbstractString, params::AbstractDict)
315370
@printf(io, "%s%s = %s\n", ' '^indent, k, fv)
316371
end
317372
end
318-
println(io, "/")
373+
return println(io, "/")
319374
end
320375

321376
"""
@@ -337,7 +392,7 @@ end
337392
function write_namelist(io::Union{IO,AbstractString}, namelist::Pair)
338393
name = first(namelist)
339394
params = second(namelist)
340-
write_namelist(io, name, params)
395+
return write_namelist(io, name, params)
341396
end
342397

343398
"""

src/pw.jl

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,100 @@ function read_k_points!(lines::AbstractVector; name::AbstractString="k_points")
239239
return name => card
240240
end
241241

242+
"""
243+
$(SIGNATURES)
244+
245+
Count how many valid lines from istart to iend in lines inclusive.
246+
247+
# Examples
248+
```jldoctest; setup = :(using QuantumEspressoIO: num_valid_lines)
249+
lines = [
250+
"HUBBARD atomic",
251+
"! a comment line",
252+
"! another comment line",
253+
"U Ni-3d 5.77",
254+
"V Ni-3d O-2p 1 12 3.5",
255+
"ALPHA Ni-3d 0.05",
256+
"J0 Ni-3d 0.05",
257+
"following",
258+
]
259+
nlines = valid_hubbard_lines(lines, 1, 8)
260+
println(nlines)
261+
# output
262+
4
263+
"""
264+
function valid_hubbard_lines(lines::AbstractVector, istart::Integer, iend::Integer)
265+
i = istart
266+
num_lines = iend - istart + 1
267+
hubbard_types = ["U", "ALPHA", "J0", "J", "B", "E2", "E3", "V"]
268+
for line in lines[istart:iend]
269+
line = remove_comment(line)
270+
# valid lines should start with hubbard symbol and end with value
271+
if isempty(line) || !(endswith(line, r"\d+$") && any([startswith(line, t) for t in hubbard_types]))
272+
num_lines -= 1
273+
end
274+
end
275+
return num_lines
276+
end
277+
278+
"""
279+
$(SIGNATURES)
280+
281+
Parse the `hubbard` card of `pw.x` input.
282+
283+
# Arguments
284+
- `lines::AbstractVector`: The lines of the input file.
285+
- `n_species::Integer`: The number of species in the input file.
286+
287+
# Returns
288+
- A `Pair` of card name to card content. The card option is stored under
289+
the `:option` key in the card content.
290+
291+
# Examples
292+
```jldoctest; setup = :(using QuantumEspressoIO: read_hubbard!)
293+
lines = [
294+
"HUBBARD atomic",
295+
"! a comment line",
296+
"! another comment line",
297+
"U Ni-3d 5.77",
298+
"U Ni1-3d 5.77",
299+
"V Ni-3d O-2p 1 12 3.5",
300+
"following lines",
301+
]
302+
card = read_hubbard!(lines)
303+
println(card)
304+
println(lines)
305+
# output
306+
:hubbard => OrderedCollections.OrderedDict{Symbol, Any}(:option => "atomic", :types => ["U", "U", "V"], :manifolds => ["Ni-3d", "Ni1-3d", "Ni-3d O-2p 1 12"], :values => [5.77, 5.77, 3.5])
307+
["following lines"]
308+
```
309+
"""
310+
function read_hubbard!(lines::AbstractVector)
311+
name = "hubbard"
312+
icard = find_card(lines, name)
313+
# no hubbard card found
314+
isnothing(icard) && return nothing
315+
316+
# hubbard projector must be specified
317+
# https://gitlab.com/QEF/q-e/-/blob/develop/Doc/Hubbard_input.tex?ref_type=heads
318+
# FIXME in what degree should we consider the integrity of the input?
319+
option = parse_card_option(lines[icard])
320+
if isempty(option)
321+
@warn "Hubbard project is not specified!"
322+
end
323+
324+
# number of hubbard lines is not known a priori
325+
iend = end_of_card(lines, icard)
326+
nline = valid_hubbard_lines(lines, icard, iend)
327+
content = parse_card!(lines, name, nline)
328+
card = OrderedDict{Symbol,Any}()
329+
card[:option] = content[1]
330+
card[:types] = map(x -> String(split(x)[1]), content[2])
331+
card[:manifolds] = map(x -> join(split(x)[2:(end - 1)], " "), content[2])
332+
card[:values] = map(x -> parse_float(split(x)[end]), content[2])
333+
return Symbol(name) => card
334+
end
335+
242336
"""
243337
$(SIGNATURES)
244338
@@ -317,6 +411,17 @@ function read_pw_in(io::Union{IO,AbstractString})
317411
return params
318412
end
319413

414+
"""
415+
$(SIGNATURES)
416+
417+
Read HUBBARD.dat from hp.x calculations.
418+
"""
419+
function read_hubbard_dat(io::Union{IO, AbstractString})
420+
_, cards = read_namelists(io; all_lines=true)
421+
card = read_hubbard!(cards)
422+
return card.second
423+
end
424+
320425
"""
321426
$(SIGNATURES)
322427
@@ -445,7 +550,9 @@ function write_k_points(io::IO, card::AbstractDict; name::AbstractString="K_POIN
445550
option = get(card, "option", "")
446551
loption = lowercase(option)
447552

448-
valid_options_list = ["tpiba", "crystal", "tpiba_b", "crystal_b", "tpiba_c", "crystal_c"]
553+
valid_options_list = [
554+
"tpiba", "crystal", "tpiba_b", "crystal_b", "tpiba_c", "crystal_c"
555+
]
449556
valid_options_auto = ["automatic"]
450557
valid_options_gamma = ["gamma"]
451558
valid_options = vcat(valid_options_list, valid_options_auto, valid_options_gamma)
@@ -472,6 +579,48 @@ function write_k_points(io::IO, card::AbstractDict; name::AbstractString="K_POIN
472579
end
473580
end
474581

582+
"""
583+
$(SIGNATURES)
584+
585+
Write the `hubbard` card of `pw.x`.
586+
587+
# Examples
588+
```jldoctest; setup = :(using QuantumEspressoIO: write_hubbard)
589+
inputs = Dict(
590+
:option => "atomic",
591+
:types => ["U", "U"],
592+
:values => [5.0, 5.0],
593+
:manifolds => ["Ni-3d", "Ni1-3d"],
594+
)
595+
write_hubbard(stdout, inputs)
596+
# output
597+
HUBBARD atomic
598+
U Ni-3d 5.0
599+
U Ni1-3d 5.0
600+
```
601+
"""
602+
function write_hubbard(io::IO, card::AbstractDict)
603+
option = get(card, :option, "")
604+
# FIXME general question: how do we check for valid input?
605+
isempty(option) && @error "Hubbard projector is not specified."
606+
projector_types = Set(["atomic", "ortho-atomic", "norm-atomic", "wf", "pseudo"])
607+
if option in projector_types
608+
println(io, "HUBBARD $option")
609+
else
610+
@error "Hubbard projector $option is not valid."
611+
end
612+
613+
valid_types = Set(["U", "J0", "J", "B", "E2", "E3", "V"])
614+
for t in card[:types]
615+
if !in(uppercase(t), valid_types)
616+
@error "Hubbard type $t is not valid"
617+
end
618+
end
619+
for (t, m, v) in zip(card[:types], card[:manifolds], card[:values])
620+
println(io, "$(uppercase(t)) $m $v")
621+
end
622+
end
623+
475624
"""
476625
$(SIGNATURES)
477626

0 commit comments

Comments
 (0)