MineralsCloud/QuantumESPRESSOParser.jl

Current implementation of `Base.parse(T::Type{<:Namelist}, content::AbstractString)`

Closed this issue · 2 comments

The current implementation of Base.parse(T::Type{<:Namelist}, content::AbstractString) has this code at the end:

function Base.parse(T::Type{<:Namelist}, content::AbstractString)
    ...
    final = merge(to_dict(T()), result)
    return T((final[f] for f in fieldnames(T))...)
end

First, construct a T instance and convert it to a Dict, then merge it with the user-modified Dict result. And then supply a new T with this final Dict. This uses the 2nd usage of Parameters.jl:

MM(;r=1000,a=error("no default for a")) =  MM(r,a) # outer kw, so no type-paras are needed when calling

However, this is semantically equivalent to the following code:

function Base.parse(T::Type{<:Namelist}, content::AbstractString)
    ...
    return T(T(); result...)
end

i.e., the 3rd usage of Parameters.jl

MM(m::MM; kws...) = reconstruct(mm,kws)

Or, equivalently, even simpler,

function Base.parse(T::Type{<:Namelist}, content::AbstractString)
    ...
    return T(T(), result)
end

that is, the 4th usage of

MM(m::MM, di::Union{AbstractDict, Tuple{Symbol,Any}}) = reconstruct(mm, di)

since

function reconstruct(pp::T, di) where T
    di = !isa(di, AbstractDict) ? Dict(di) : copy(di)
    if pp isa AbstractDict
        for (k,v) in di
            !haskey(pp, k) && error("Field $k not in type $T")
            pp[k] = v
        end
        return pp
    else
        # Also uses the order of `fieldnames`
        ns = fieldnames(T)
        args = []
        for (i,n) in enumerate(ns)
            push!(args, pop!(di, n, getfield(pp, n)))
        end
        length(di)!=0 && error("Fields $(keys(di)) not in type $T")
        return pp isa NamedTuple ? T(Tuple(args)) : T(args...)  # Just expand by the order mentioned
    end
end
reconstruct(pp; kws...) = reconstruct(pp, kws)

The current implementation will cause the following error. If we have an input like

&control
   calculation='scf',
   restart_mode='from_scratch',
   prefix='silicon'
   pseudo_dir = '$PSEUDO_DIR/',
   outdir='$TMP_DIR/'
/
&system
   ibrav = 2, celldm(1) =10.20, nat=  2, ntyp= 1,
   ecutwfc = 18.0
/
&electrons
   mixing_beta = 0.7
   conv_thr =  1.0d-8
/
ATOMIC_SPECIES
 Si  28.086  Si.pz-vbc.UPF
ATOMIC_POSITIONS (alat)
 Si 0.00 0.00 0.00
 Si 0.25 0.25 0.25
K_POINTS  (tpiba)
  10
   0.1250000  0.1250000  0.1250000   1.00
   0.1250000  0.1250000  0.3750000   3.00
   0.1250000  0.1250000  0.6250000   3.00
   0.1250000  0.1250000  0.8750000   3.00
   0.1250000  0.3750000  0.3750000   3.00
   0.1250000  0.3750000  0.6250000   6.00
   0.1250000  0.3750000  0.8750000   6.00
   0.1250000  0.6250000  0.6250000   3.00
   0.3750000  0.3750000  0.3750000   1.00
   0.3750000  0.3750000  0.6250000   3.00

After parsing we get

julia> pw.system
QuantumESPRESSOBase.Namelists.PWscf.SystemNamelist
  ibrav: Int64 2
  celldm: Array{Union{Missing, Float64}}((1,))
  A: Float64 0.0
  B: Float64 0.0
  C: Float64 0.0
  cosAB: Float64 0.0
  cosAC: Float64 0.0
  cosBC: Float64 0.0
  nat: Int64 2
  ntyp: Int64 1
  nbnd: Int64 0
  tot_charge: Float64 0.0
  starting_charge: Array{Union{Missing, Float64}}((0,))
  tot_magnetization: Float64 -1.0
  starting_magnetization: Array{Union{Missing, Float64}}((0,))
  ecutwfc: Float64 18.0
  ecutrho: Float64 0.0
  ecutfock: Float64 0.0
  nr1: Int64 0
  nr2: Int64 0
  nr3: Int64 0
  nr1s: Int64 0
  nr2s: Int64 0
  nr3s: Int64 0
  nosym: Bool false
  nosym_evc: Bool false
  noinv: Bool false
  no_t_rev: Bool false
  force_symmorphic: Bool false
  use_all_frac: Bool false
  occupations: String "fixed"
  one_atom_occupations: Bool false
  starting_spin_angle: Bool false
  degauss: Float64 0.0
  smearing: String "gaussian"
  nspin: Int64 1
  noncolin: Bool false
  ecfixed: Float64 0.0
  qcutz: Float64 0.0
  q2sigma: Float64 0.1
  input_dft: String "none"
  exx_fraction: Float64 0.25
  screening_parameter: Float64 0.106
  exxdiv_treatment: String "gygi-baldereschi"
  x_gamma_extrapolation: Bool true
  ecutvcut: Float64 0.0
  nqx1: Int64 1
  nqx2: Int64 1
  nqx3: Int64 1
  localization_thr: Float64 0.0
  lda_plus_u: Bool false
  lda_plus_u_kind: Int64 0
  Hubbard_U: Array{Union{Missing, Float64}}((0,))
  Hubbard_J0: Array{Union{Missing, Float64}}((0,))
  Hubbard_alpha: Array{Union{Missing, Float64}}((0,))
  Hubbard_beta: Array{Union{Missing, Float64}}((0,))
  Hubbard_J: Array{Union{Missing, Float64}}((0,))
  starting_ns_eigenvalue: Float64 -1.0
  U_projection_type: String "atomic"
  edir: Int64 1
  emaxpos: Float64 0.5
  eopreg: Float64 0.1
  eamp: Float64 0.001
  angle1: Array{Union{Missing, Float64}}((0,))
  angle2: Array{Union{Missing, Float64}}((0,))
  constrained_magnetization: String "none"
  fixed_magnetization: Array{Union{Missing, Float64}}((3,))
  lambda: Float64 1.0
  report: Int64 100
  lspinorb: Bool false
  assume_isolated: String "none"
  esm_bc: String "pbc"
  esm_w: Float64 0.0
  esm_efield: Float64 0.0
  esm_nfit: Int64 4
  fcp_mu: Float64 0.0
  vdw_corr: String "none"
  london: Bool false
  london_s6: Float64 0.75
  london_c6: Array{Union{Missing, Float64}}((0,))
  london_rvdw: Array{Union{Missing, Float64}}((0,))
  london_rcut: Float64 200.0
  ts_vdw_econv_thr: Float64 1.0e-6
  ts_vdw_isolated: Bool false
  xdm: Bool false
  xdm_a1: Float64 0.6836
  xdm_a2: Float64 1.5045
  space_group: Int64 0
  uniqueb: Bool false
  origin_choice: Int64 1
  rhombohedral: Bool true
  zgate: Float64 0.5
  relaxz: Bool false
  block: Bool false
  block_1: Float64 0.45
  block_2: Float64 0.55
  block_height: Float64 0.1

As can be seen, ntyp = 1. But angle2, etc., are still vectors of 0 elements (the default value of a SystemNamelist). That means the length checking will fail. The parameter angle2 should have length of ntyp. This is a big problem.

This bug is now fixed by commit "858dca36dea8153028ee741874b304b506da1292". A minor change but a powerful one. I follow the instruction on this answer.

Parsing the above-mentioned input

&control
   calculation='scf',
   restart_mode='from_scratch',
   prefix='silicon'
   pseudo_dir = '$PSEUDO_DIR/',
   outdir='$TMP_DIR/'
/
&system
   ibrav = 2, celldm(1) =10.20, nat=  2, ntyp= 1,
   ecutwfc = 18.0
/
&electrons
   mixing_beta = 0.7
   conv_thr =  1.0d-8
/
ATOMIC_SPECIES
 Si  28.086  Si.pz-vbc.UPF
ATOMIC_POSITIONS (alat)
 Si 0.00 0.00 0.00
 Si 0.25 0.25 0.25
K_POINTS  (tpiba)
  10
   0.1250000  0.1250000  0.1250000   1.00
   0.1250000  0.1250000  0.3750000   3.00
   0.1250000  0.1250000  0.6250000   3.00
   0.1250000  0.1250000  0.8750000   3.00
   0.1250000  0.3750000  0.3750000   3.00
   0.1250000  0.3750000  0.6250000   6.00
   0.1250000  0.3750000  0.8750000   6.00
   0.1250000  0.6250000  0.6250000   3.00
   0.3750000  0.3750000  0.3750000   1.00
   0.3750000  0.3750000  0.6250000   3.00

will result in

julia> parse(QuantumESPRESSOBase.Namelists.PWscf.SystemNamelist, str)
QuantumESPRESSOBase.Namelists.PWscf.SystemNamelist
  ibrav: Int64 2
  celldm: Array{Union{Nothing, Float64}}((1,))
  A: Float64 0.0
  B: Float64 0.0
  C: Float64 0.0
  cosAB: Float64 0.0
  cosAC: Float64 0.0
  cosBC: Float64 0.0
  nat: Int64 2
  ntyp: Int64 1
  nbnd: Int64 0
  tot_charge: Float64 0.0
  starting_charge: Array{Union{Nothing, Float64}}((1,))
  tot_magnetization: Float64 -1.0
  starting_magnetization: Array{Union{Nothing, Float64}}((1,))
  ecutwfc: Float64 18.0
  ecutrho: Float64 0.0
  ecutfock: Float64 0.0
  nr1: Int64 0
  nr2: Int64 0
  nr3: Int64 0
  nr1s: Int64 0
  nr2s: Int64 0
  nr3s: Int64 0
  nosym: Bool false
  nosym_evc: Bool false
  noinv: Bool false
  no_t_rev: Bool false
  force_symmorphic: Bool false
  use_all_frac: Bool false
  occupations: String "fixed"
  one_atom_occupations: Bool false
  starting_spin_angle: Bool false
  degauss: Float64 0.0
  smearing: String "gaussian"
  nspin: Int64 1
  noncolin: Bool false
  ecfixed: Float64 0.0
  qcutz: Float64 0.0
  q2sigma: Float64 0.1
  input_dft: String "none"
  exx_fraction: Float64 0.25
  screening_parameter: Float64 0.106
  exxdiv_treatment: String "gygi-baldereschi"
  x_gamma_extrapolation: Bool true
  ecutvcut: Float64 0.0
  nqx1: Int64 1
  nqx2: Int64 1
  nqx3: Int64 1
  localization_thr: Float64 0.0
  lda_plus_u: Bool false
  lda_plus_u_kind: Int64 0
  Hubbard_U: Array{Union{Nothing, Float64}}((1,))
  Hubbard_J0: Array{Union{Nothing, Float64}}((1,))
  Hubbard_alpha: Array{Union{Nothing, Float64}}((1,))
  Hubbard_beta: Array{Union{Nothing, Float64}}((1,))
  Hubbard_J: Array{Union{Nothing, Float64}}((1,))
  starting_ns_eigenvalue: Float64 -1.0
  U_projection_type: String "atomic"
  edir: Int64 1
  emaxpos: Float64 0.5
  eopreg: Float64 0.1
  eamp: Float64 0.001
  angle1: Array{Union{Nothing, Float64}}((1,))
  angle2: Array{Union{Nothing, Float64}}((1,))
  constrained_magnetization: String "none"
  fixed_magnetization: Array{Union{Nothing, Float64}}((3,))
  lambda: Float64 1.0
  report: Int64 100
  lspinorb: Bool false
  assume_isolated: String "none"
  esm_bc: String "pbc"
  esm_w: Float64 0.0
  esm_efield: Float64 0.0
  esm_nfit: Int64 4
  fcp_mu: Float64 0.0
  vdw_corr: String "none"
  london: Bool false
  london_s6: Float64 0.75
  london_c6: Array{Union{Nothing, Float64}}((1,))
  london_rvdw: Array{Union{Nothing, Float64}}((1,))
  london_rcut: Float64 200.0
  ts_vdw_econv_thr: Float64 1.0e-6
  ts_vdw_isolated: Bool false
  xdm: Bool false
  xdm_a1: Float64 0.6836
  xdm_a2: Float64 1.5045
  space_group: Int64 0
  uniqueb: Bool false
  origin_choice: Int64 1
  rhombohedral: Bool true
  zgate: Float64 0.5
  relaxz: Bool false
  block: Bool false
  block_1: Float64 0.45
  block_2: Float64 0.55
  block_height: Float64 0.

See the comparison (red: before change, green: after change):
diffchecker

The Nothing, Missing difference was due to another change.