Skip to content

Commit c2fbf52

Browse files
andyclaude
andcommitted
Quantum ESPRESSO: add the Geometry runtype (calculation='relax')
Reported from a live session: "Tested QE implementation. Very broken ... Nothing in Runtype details -- can't edit. Final edit doesn't bring anything up. Launch doesn't do anything." Most of that follows from one line. QuantumESPRESSO.edml declared a single <runtype>Energy</runtype>, by design -- ai.qe's header still says "PHASE 1, REDUCED SCOPE. calculation='scf' only". So QE could do single points and nothing else: qeruntype.py built a dialog only for Energy and was empty for anything else, and with no usable runtype there is nothing to edit and nothing to launch. Geometry now maps to calculation='relax', end to end: * QuantumESPRESSO.edml offers the runtype. * ai.qe gains QECalculationType(), which maps the ECCE runtype name onto pw.x's &CONTROL calculation value and dies in the MAIN FLOW for anything unsupported -- not inside a ##token## resolver, where such a die used to be swallowed and emit a deck with the line silently missing. * A relax forces tprnfor on regardless of the checkbox, because pw.x needs forces to move the ions. The control is not hidden; the deck stays valid even if someone unticks it. * qe.tpl gains a ##QEIons## slot. The resolver returns empty for scf, so the line is dropped -- pw.x rejects &IONS outright on an scf run. * qeruntype.py gains a Geometry branch exposing forc_conv_thr and nstep, the two values that decide whether an optimisation finishes in six steps or sixty, or stops at the cap WITHOUT converging while still writing a final geometry that looks like success. Forces are deliberately not offered there, since ai.qe overrides them. Verified the whole chain rather than the pieces: ai.qe generated the deck from a .frag/.param pair with RunType: Geometry, the real pw.x 6.7 accepted it and ran to convergence ("JOB DONE", final coordinates written), and qe.desc read the trajectory back. That output is now the fixture, replacing the hand-written input I had used for the GEOMTRACE work -- which matches the convention the other QE cases already state, that every fixture deck is generated by ai.qe itself. Still absent, each needing more than a runtype entry: vc-relax (needs the CELL namelist and LATTICEVEC extraction so the changing box can be animated), nscf/bands, md, and phonons (ph.x is a separate executable). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EFMUNbw2UpRPmgsqviAowb
1 parent 31cde65 commit c2fbf52

8 files changed

Lines changed: 498 additions & 853 deletions

File tree

‎data/client/cap/QuantumESPRESSO.edml‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,24 @@
9696
Energy maps to calculation='scf'. Forces and the stress tensor
9797
are available on an scf run (tprnfor / tstress) and are exposed as
9898
Runtype Details checkboxes, so a separate Gradient runtype would
99-
add nothing in phase 1. -->
99+
add nothing.
100+
101+
Geometry maps to calculation='relax', added once qe.desc could
102+
extract GEOMTRACE; there is no point offering an optimisation
103+
whose trajectory ECCE cannot display. It forces tprnfor on
104+
regardless of the checkbox, because pw.x needs forces to move the
105+
ions, and it emits an &IONS namelist that an scf run must not have.
106+
107+
Still absent, and each needs work beyond a runtype entry:
108+
vc-relax (needs the CELL namelist and LATTICEVEC extraction so the
109+
changing box can be animated), nscf/bands (a second run reusing the
110+
first one's charge density, which does not fit
111+
one-input/one-output), md, and phonons (ph.x is a separate
112+
executable). -->
100113
<Editor theorydialog="qetheory.py" runtypedialog="qeruntype.py">
101114
<Theory category="PW" name="PW" needsBasis="false">
102115
<runtype>Energy</runtype>
116+
<runtype>Geometry</runtype>
103117
</Theory>
104118

105119
<TheorySummary topLabel="Plane Waves-">

‎scripts/codereg/qeruntype.py‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,43 @@ def __init__(self, parent, helpURL):
7373
propSizer.AddWidget(self.printStress)
7474
self.panelSizer.Add(propSizer)
7575

76+
elif EcceGlobals.RunType == "Geometry":
77+
78+
# calculation='relax'. Forces are NOT offered as a choice
79+
# here: pw.x needs them to move the ions, so ai.qe forces
80+
# tprnfor on for a relax whatever a checkbox said. Offering
81+
# a control that is then overridden would be worse than
82+
# offering none.
83+
convSizer = EcceBoxSizer(self, label="Convergence", cols=1)
84+
85+
# forc_conv_thr, in Ry/bohr. pw.x's own default is 1.0d-3,
86+
# stated explicitly so the deck does not change meaning if
87+
# that default ever does. Exposed because it is the single
88+
# knob that decides whether an optimisation finishes in six
89+
# steps or sixty.
90+
self.forceConv = EcceExpInput(self,
91+
unit="Ry/bohr",
92+
name="ES.Runtype.PW.ForcConvThr",
93+
default=1e-3,
94+
hardRange="(0..)",
95+
label="Force Convergence:",
96+
export=1)
97+
convSizer.AddWidget(self.forceConv)
98+
99+
# nstep: pw.x defaults to 50 for relax. A run that hits the
100+
# cap stops WITHOUT converging and still writes a final
101+
# geometry, which looks like success, so the value is worth
102+
# having in front of the user.
103+
self.maxSteps = EcceSpinCtrl(self,
104+
hardRange="[1..)",
105+
softRange="[1..500]",
106+
name="ES.Runtype.PW.NStep",
107+
default=50,
108+
label="Maximum Steps:",
109+
export=1)
110+
convSizer.AddWidget(self.maxSteps)
111+
self.panelSizer.Add(convSizer)
112+
76113
self.AddButtons()
77114

78115
def CheckDependency(self):

‎scripts/parsers/ai.qe‎

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,15 +492,73 @@ sub warnOnOddElectronCount {
492492
# &CONTROL. outdir and pseudo_dir are also rewritten at launch time by
493493
# qe.launchpp, which is where launch-time-only knowledge belongs; what is
494494
# written here has to be valid on its own so a hand-run deck works.
495+
# Maps ECCE's runtype name onto pw.x's &CONTROL calculation value.
496+
#
497+
# Phase 1 was scf only. "Geometry" was added once GEOMTRACE extraction
498+
# existed to read the result back (verified against a real 6-step BFGS
499+
# water relax with pw.x 6.7) -- there is no point offering an
500+
# optimisation whose trajectory ECCE cannot display.
501+
#
502+
# Dies in the MAIN FLOW rather than inside a ##token## resolver: ai.*
503+
# template engines run resolvers through eval, and a die inside one used
504+
# to be swallowed silently, emitting a deck with the line missing. That
505+
# is fixed (a6d0512) but the rule stands -- validate before generation,
506+
# as &verifyPeriodic does.
507+
sub QECalculationType {
508+
my $runtype = $AbiDict{"RunType"};
509+
return "scf" if ($runtype eq "Energy");
510+
return "relax" if ($runtype eq "Geometry");
511+
die "Runtype '$runtype' is not supported for Quantum ESPRESSO.\n" .
512+
" Supported: Energy (scf), Geometry (relax).\n";
513+
}
514+
495515
sub QEControl {
496516
my @l = ();
517+
my $calc = &QECalculationType;
497518
push(@l, "&CONTROL");
498-
push(@l, " calculation = 'scf'");
519+
push(@l, " calculation = '" . $calc . "'");
499520
push(@l, " prefix = '" . &Prefix . "'");
500521
push(@l, " outdir = './qe.scratch'");
501522
push(@l, " pseudo_dir = '" . &pseudoDir . "/'");
502-
push(@l, " tprnfor = " . &FortranLogical("ES.Runtype.PW.PrintForces", 1));
523+
# A relax cannot run without forces, whatever the checkbox says --
524+
# pw.x needs them to move the ions. Forcing it here rather than
525+
# hiding the control is deliberate: the generated deck stays valid
526+
# even if someone unticks it.
527+
my $forces = ($calc eq "relax")
528+
? ".true."
529+
: &FortranLogical("ES.Runtype.PW.PrintForces", 1);
530+
push(@l, " tprnfor = " . $forces);
503531
push(@l, " tstress = " . &FortranLogical("ES.Runtype.PW.PrintStress", 0));
532+
if ($calc eq "relax") {
533+
# Both come from the Geometry branch of qeruntype.py. Defaults match
534+
# pw.x's own (1.0d-3 Ry/bohr, 50 steps) and are stated explicitly so
535+
# the deck does not change meaning if those defaults ever do.
536+
push(@l, " forc_conv_thr = " . &numOr("ES.Runtype.PW.ForcConvThr", "1.0d-3"));
537+
push(@l, " nstep = " . &numOr("ES.Runtype.PW.NStep", "50"));
538+
}
539+
push(@l, "/");
540+
my $r = join("\n", @l);
541+
$_ = $r;
542+
return $r;
543+
}
544+
545+
# &IONS. Only present for a relax: pw.x rejects the namelist outright on
546+
# an scf run ("calculation='scf': namelist &IONS not needed"), so this
547+
# resolver returns the empty string and modifyInputFile drops the line.
548+
#
549+
# NOTE for whoever adds vc-relax: it needs &CELL as well, and the cell
550+
# then changes per step, which means LATTICEVEC extraction (qe.desc has
551+
# none yet) before it is worth offering -- GTStepCmd already animates a
552+
# changing box, it just has no producer for QE.
553+
sub QEIons {
554+
my $calc = &QECalculationType;
555+
return "" if ($calc ne "relax");
556+
557+
my @l = ();
558+
push(@l, "&IONS");
559+
# bfgs is pw.x's own default for relax; stated explicitly so the deck
560+
# does not change meaning if that default ever does.
561+
push(@l, " ion_dynamics = 'bfgs'");
504562
push(@l, "/");
505563
my $r = join("\n", @l);
506564
$_ = $r;

‎scripts/parsers/qe.tpl‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
##QEControl##
22
##QESystem##
33
##QEElectrons##
4+
##QEIons##
45
##QEAtomicSpecies##
56
##QECellParameters##
67
##QEAtomicPositions##

‎tests/parsers/cases.py‎

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,22 +1075,27 @@
10751075
dict(
10761076
# The only QE fixture that is an OPTIMISATION rather than a single
10771077
# point, and therefore the only one that exercises [GEOMTRACE].
1078-
# Produced by a real pw.x 6.7 run on this machine (input checked in
1079-
# alongside it), deliberately started from a distorted water
1080-
# geometry so BFGS has something to do: 6 steps to convergence.
10811078
#
1082-
# 7 ATOMIC_POSITIONS blocks, not 6: the last is the duplicate
1083-
# printed inside "Begin final coordinates". Harmless for animation
1084-
# (the last frame simply repeats) and asserted here so that if a
1085-
# future QE version stops echoing it, the count change is noticed
1079+
# Like its siblings, the deck was GENERATED BY ai.qe (with
1080+
# RunType: Geometry) and then run to completion by the real
1081+
# pw.x 6.7 on this machine -- so this case exercises the whole
1082+
# chain: the Geometry runtype reaching calculation='relax', the
1083+
# &IONS namelist an scf run must not have, forc_conv_thr and
1084+
# nstep from the runtype dialog's keys, pw.x accepting all of it,
1085+
# and qe.desc reading the trajectory back.
1086+
#
1087+
# 4 ATOMIC_POSITIONS blocks: 3 BFGS steps plus the duplicate
1088+
# printed inside "Begin final coordinates". The duplicate is
1089+
# harmless for animation (the last frame simply repeats) and is
1090+
# asserted so that a future QE version dropping it is noticed
10861091
# rather than silently absorbed.
10871092
name='qe-h2o-relax',
10881093
desc='qe.desc',
10891094
fixture='qe/h2o_relax.pwout',
10901095
parse_args=('.', 'GeomOpt', 'PW', 'PW', '0'),
10911096
expect={
10921097
'VERSION': dict(keys={'VERSION': {'values': 'PWSCF 6.7MaX'}}),
1093-
'GEOMTRACE': dict(blocks=7, keys={'GEOMTRACE': {
1098+
'GEOMTRACE': dict(blocks=4, keys={'GEOMTRACE': {
10941099
'size': '1 3 3',
10951100
'units': 'Angstrom'}}),
10961101
},

0 commit comments

Comments
 (0)