Skip to content

Give each test class its own scratch directory instead of writing into arc/testing - #1008

Closed
calvinp0 wants to merge 7 commits into
mainfrom
fix_test_scratch_isolation
Closed

Give each test class its own scratch directory instead of writing into arc/testing#1008
calvinp0 wants to merge 7 commits into
mainfrom
fix_test_scratch_isolation

Conversation

@calvinp0

Copy link
Copy Markdown
Member

Test classes were writing scratch into ARC_TESTING_PATH, which holds read-only fixture inputs. Nineteen files did it, and eight of them rmtree'd a subdirectory of it in a teardown — so one module's cleanup deleted a directory another module was still writing into.

The result is a suite that is broadly unstable under xdist. Measured on main, each module run solo under -n 4 --dist worksteal, three runs each:

module main this branch
orca_test 2F / 2F / 2F 3 × 20 passed
xtb_test 6E / 1F+4E / 6E 3 × 8 passed
adapter_test 1F+2E / 2F / 2F 3 × 16 passed
molpro_test 4E / fail / 3E 3 × 5 passed
obabel_test 1F+1E / fail / 2F 3 × 4 passed
mockter_test 4E / 1F+2E / 2E 3 × 6 passed
cfour_test fail / 1F+1E / 1F 3 × 4 passed
arkane_test 1F / pass / pass 3 × 46 passed

Paired (7 job-adapter modules together, 5 runs): 5 failed, 58 passed × 5 → 63 passed × 5.

Solo failure is the important column. --dist worksteal can split a single TestCase class across workers, so each worker independently runs setUpClass and tearDownClass — a class does not own its directory merely because only that class uses it. A per-worker path suffix does not fix this; tempfile.mkdtemp() does, because it is unique per setUpClass invocation.

What changed

Every converted class allocates scratch with tempfile.mkdtemp() in setUpClass/setUp and releases it via addClassCleanup/addCleanup. All tearDownClass rmtrees of ARC_TESTING_PATH subdirectories are gone. Read-only fixture reads are untouched — those are shared safely, because nobody writes to them.

Eight of the nineteen were not findable by grepping for rmtree(os.path.join(ARC_TESTING_PATH,…)) because they build the path through a variable: pyscf_test, ase_test, uma_test, ts/orca_neb_test, ts/kinbot_test, ts/gcn_test, processor_test, common_test, reaction_test.

Cases needing more than a mechanical rewrite

  • common_test.test_globalize_paths asserted on a literal repo path. The fixture is now copytree'd into a tmpdir and both assertions rebuilt from project_directory — same semantics, and it allowed deleting _clean_globalized_restart_artifact(), a self-healing helper that existed only because the test wrote restart_paths_globalized.yml back into the repo on every run.
  • reaction_test.test_load_ts_xyz_user_guess_from_files passed a fixture directory as an ARC project_directory, so a Scheduler run created arc.log, output/ and log_and_restart_archive/ inside arc/testing/reactions/methanoate_hydrolysis. Its ts_xyz_guess entries resolve relative to project_directory, so the fixture is copied and the run happens in the copy.
  • kinbot_test had a hand-rolled _remove_test_dir written specifically to survive xdist; a per-class tmp root makes it dead code.

Not touched

arc/job/adapters/gaussian_test.py and arc/job/adapters/common_test.py are #1001's files and are converted there. They remain the one genuinely cross-module shared directory on main, and Group C measurements confirm that pair still races here — unchanged, as expected.

Verification

Full suite, serial: main 5 failed, 2803 passed, 36 skipped (386 s) → branch 5 failed, 2803 passed, 36 skipped (413 s). Identical counts; the 5 are the known torch_ani_test environmental failures. git status --porcelain clean after both runs — no arc/testing/ debris left behind.

Mutation-verified: reverting orca_test to main's version brings the failures straight back (1F+15P+4E, 2F+18P, 1F+19P), restoring it gives 20 passed × 3; same for xtb_test. Restored files md5-matched their snapshots.

Four commits, no file in more than one. Two pre-existing py/import-and-import-from violations fixed in files already being touched (gcn_test, kinbot_test).

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings August 22, 2026 08:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@codecov

codecov Bot commented Aug 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 64.57%. Comparing base (45d73a0) to head (a63e083).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1008      +/-   ##
==========================================
- Coverage   64.60%   64.57%   -0.04%     
==========================================
  Files         119      119              
  Lines       39785    39785              
  Branches    10307    10307              
==========================================
- Hits        25703    25690      -13     
- Misses      11105    11114       +9     
- Partials     2977     2981       +4     
Flag Coverage Δ
functionaltests 64.57% <ø> (-0.04%) ⬇️
unittests 64.57% <ø> (-0.04%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

calvinp0 added a commit that referenced this pull request Aug 22, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

ONLY THE STABILITY JOB READS A GUESS. Uploading a previous job's orbitals to every ORCA
job would change the SCF starting point of the whole workflow, including jobs at a
different level or basis, which is a larger change than this one and is not needed by
anything here: the stability analysis is the only job whose result depends on converging
to a particular one of several solutions.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.
calvinp0 added a commit that referenced this pull request Aug 22, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

EVERY ORCA JOB THAT RUNS AN SCF READS THE GUESS, as every Gaussian job carrying a
checkfile gets guess=read. OrcaAdapter.reads_orbital_guess is the single predicate behind
both halves of it, the !MORead and %moinp keywords and the guess.gbw upload, so the file
is uploaded for exactly the jobs that read it; emitting the keywords without the file
aborts the job on a missing guess. ORBITALS_GUESS_JOB_TYPES holds the job types this
adapter writes an SCF on one starting structure for: opt, conf_opt, optfreq and scan,
whose first SCF the guess seeds and whose later points ORCA propagates orbitals through
itself, and freq, sp, conf_sp and stability, each a single SCF. The rest are the job types
write_input_file emits no keyword for, so ORCA is handed no calculation for a guess to
seed -- composite, for which ORCA offers no composite method; irc and orbitals; and
directed_scan, for which this adapter writes neither the scan block nor the constraints
such a job needs -- plus gen_confs, tsg and onedmin, which belong to other adapters. A job
array is excluded because it writes no input file and its members share one remote path,
where one uploaded guess would stand in for every member, and a monatomic species because
ARC spawns it neither an optimization nor a frequency job.

WHAT THE CHAIN BUYS IS MEASURED. On a C5H10 TS at UKS B3LYP/def2-TZVP, same geometry and
same input but for the guess, a fresh guess collapsed to the closed-shell solution
(E = -196.344572 Eh, <S**2> = 0.000000) while !MORead held the broken-symmetry solution
(E = -196.364789 Eh, <S**2> = 0.864739), reproducing the followed solution to 1e-9 Eh --
12.7 kcal/mol apart. Without the chain the freq job converges from ORCA's own initial
guess while the stability job reads the optimization's orbitals, and those are two
different SCF solutions in exactly the cases the analysis exists to find.

NO LEVEL OR BASIS IS TRACKED, because ORCA projects a guess written in another basis onto
the basis of the job reading it. A def2-SVP job reading a def2-TZVP .gbw logs
'Atom 0: N(Shells)= 6 and 11 - projection required' and terminates normally at a sane
def2-SVP energy, so the chain crosses the basis change ARC makes between the optimization
and the single point on its own. The predicate is whether a checkfile this ESS wrote
exists, and no new state is stored on the species, in the restart dict or in output.yml.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.
calvinp0 added a commit that referenced this pull request Aug 22, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

EVERY ORCA JOB THAT RUNS AN SCF READS THE GUESS, as every Gaussian job carrying a
checkfile gets guess=read. OrcaAdapter.reads_orbital_guess is the single predicate behind
both halves of it, the !MORead and %moinp keywords and the guess.gbw upload, so the file
is uploaded for exactly the jobs that read it; emitting the keywords without the file
aborts the job on a missing guess. ORBITALS_GUESS_JOB_TYPES holds the job types this
adapter writes an SCF on one starting structure for: opt, conf_opt, optfreq and scan,
whose first SCF the guess seeds and whose later points ORCA propagates orbitals through
itself, and freq, sp, conf_sp and stability, each a single SCF. The rest are the job types
write_input_file emits no keyword for, so ORCA is handed no calculation for a guess to
seed -- composite, for which ORCA offers no composite method; irc and orbitals; and
directed_scan, for which this adapter writes neither the scan block nor the constraints
such a job needs -- plus gen_confs, tsg and onedmin, which belong to other adapters. A job
array is excluded because it writes no input file and its members share one remote path,
where one uploaded guess would stand in for every member, and a monatomic species because
ARC spawns it neither an optimization nor a frequency job.

WHAT THE CHAIN BUYS IS MEASURED. On a C5H10 TS at UKS B3LYP/def2-TZVP, same geometry and
same input but for the guess, a fresh guess collapsed to the closed-shell solution
(E = -196.344572 Eh, <S**2> = 0.000000) while !MORead held the broken-symmetry solution
(E = -196.364789 Eh, <S**2> = 0.864739), reproducing the followed solution to 1e-9 Eh --
12.7 kcal/mol apart. Without the chain the freq job converges from ORCA's own initial
guess while the stability job reads the optimization's orbitals, and those are two
different SCF solutions in exactly the cases the analysis exists to find.

NO LEVEL OR BASIS IS TRACKED, because ORCA projects a guess written in another basis onto
the basis of the job reading it. A def2-SVP job reading a def2-TZVP .gbw logs
'Atom 0: N(Shells)= 6 and 11 - projection required' and terminates normally at a sane
def2-SVP energy, so the chain crosses the basis change ARC makes between the optimization
and the single point on its own. The predicate is whether a checkfile this ESS wrote
exists, and no new state is stored on the species, in the restart dict or in output.yml.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.
calvinp0 added a commit that referenced this pull request Aug 22, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

EVERY ORCA JOB THAT RUNS AN SCF READS THE GUESS, as every Gaussian job carrying a
checkfile gets guess=read. OrcaAdapter.reads_orbital_guess is the single predicate behind
both halves of it, the !MORead and %moinp keywords and the guess.gbw upload, so the file
is uploaded for exactly the jobs that read it; emitting the keywords without the file
aborts the job on a missing guess. ORBITALS_GUESS_JOB_TYPES holds the job types this
adapter writes an SCF on one starting structure for: opt, conf_opt, optfreq and scan,
whose first SCF the guess seeds and whose later points ORCA propagates orbitals through
itself, and freq, sp, conf_sp and stability, each a single SCF. The rest are the job types
write_input_file emits no keyword for, so ORCA is handed no calculation for a guess to
seed -- composite, for which ORCA offers no composite method; irc and orbitals; and
directed_scan, for which this adapter writes neither the scan block nor the constraints
such a job needs -- plus gen_confs, tsg and onedmin, which belong to other adapters. A job
array is excluded because it writes no input file and its members share one remote path,
where one uploaded guess would stand in for every member, and a monatomic species because
ARC spawns it neither an optimization nor a frequency job.

WHAT THE CHAIN BUYS IS MEASURED. On a C5H10 TS at UKS B3LYP/def2-TZVP, same geometry and
same input but for the guess, a fresh guess collapsed to the closed-shell solution
(E = -196.344572 Eh, <S**2> = 0.000000) while !MORead held the broken-symmetry solution
(E = -196.364789 Eh, <S**2> = 0.864739), reproducing the followed solution to 1e-9 Eh --
12.7 kcal/mol apart. Without the chain the freq job converges from ORCA's own initial
guess while the stability job reads the optimization's orbitals, and those are two
different SCF solutions in exactly the cases the analysis exists to find.

NO LEVEL OR BASIS IS TRACKED, because ORCA projects a guess written in another basis onto
the basis of the job reading it. A def2-SVP job reading a def2-TZVP .gbw logs
'Atom 0: N(Shells)= 6 and 11 - projection required' and terminates normally at a sane
def2-SVP energy, so the chain crosses the basis change ARC makes between the optimization
and the single point on its own. The predicate is whether a checkfile this ESS wrote
exists, and no new state is stored on the species, in the restart dict or in output.yml.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.
calvinp0 added a commit that referenced this pull request Aug 22, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

EVERY ORCA JOB THAT RUNS AN SCF READS THE GUESS, as every Gaussian job carrying a
checkfile gets guess=read. OrcaAdapter.reads_orbital_guess is the single predicate behind
both halves of it, the !MORead and %moinp keywords and the guess.gbw upload, so the file
is uploaded for exactly the jobs that read it; emitting the keywords without the file
aborts the job on a missing guess. ORBITALS_GUESS_JOB_TYPES holds the job types this
adapter writes an SCF on one starting structure for: opt, conf_opt, optfreq and scan,
whose first SCF the guess seeds and whose later points ORCA propagates orbitals through
itself, and freq, sp, conf_sp and stability, each a single SCF. The rest are the job types
write_input_file emits no keyword for, so ORCA is handed no calculation for a guess to
seed -- composite, for which ORCA offers no composite method; irc and orbitals; and
directed_scan, for which this adapter writes neither the scan block nor the constraints
such a job needs -- plus gen_confs, tsg and onedmin, which belong to other adapters. A job
array is excluded because it writes no input file and its members share one remote path,
where one uploaded guess would stand in for every member, and a monatomic species because
ARC spawns it neither an optimization nor a frequency job.

WHAT THE CHAIN BUYS IS MEASURED. On a C5H10 TS at UKS B3LYP/def2-TZVP, same geometry and
same input but for the guess, a fresh guess collapsed to the closed-shell solution
(E = -196.344572 Eh, <S**2> = 0.000000) while !MORead held the broken-symmetry solution
(E = -196.364789 Eh, <S**2> = 0.864739), reproducing the followed solution to 1e-9 Eh --
12.7 kcal/mol apart. Without the chain the freq job converges from ORCA's own initial
guess while the stability job reads the optimization's orbitals, and those are two
different SCF solutions in exactly the cases the analysis exists to find.

NO LEVEL OR BASIS IS TRACKED, because ORCA projects a guess written in another basis onto
the basis of the job reading it. A def2-SVP job reading a def2-TZVP .gbw logs
'Atom 0: N(Shells)= 6 and 11 - projection required' and terminates normally at a sane
def2-SVP energy, so the chain crosses the basis change ARC makes between the optimization
and the single point on its own. The predicate is whether a checkfile this ESS wrote
exists, and no new state is stored on the species, in the restart dict or in output.yml.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.
calvinp0 added a commit that referenced this pull request Aug 23, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

EVERY ORCA JOB THAT RUNS AN SCF READS THE GUESS, as every Gaussian job carrying a
checkfile gets guess=read. OrcaAdapter.reads_orbital_guess is the single predicate behind
both halves of it, the !MORead and %moinp keywords and the guess.gbw upload, so the file
is uploaded for exactly the jobs that read it; emitting the keywords without the file
aborts the job on a missing guess. ORBITALS_GUESS_JOB_TYPES holds the job types this
adapter writes an SCF on one starting structure for: opt, conf_opt, optfreq and scan,
whose first SCF the guess seeds and whose later points ORCA propagates orbitals through
itself, and freq, sp, conf_sp and stability, each a single SCF. The rest are the job types
write_input_file emits no keyword for, so ORCA is handed no calculation for a guess to
seed -- composite, for which ORCA offers no composite method; irc and orbitals; and
directed_scan, for which this adapter writes neither the scan block nor the constraints
such a job needs -- plus gen_confs, tsg and onedmin, which belong to other adapters. A job
array is excluded because it writes no input file and its members share one remote path,
where one uploaded guess would stand in for every member, and a monatomic species because
ARC spawns it neither an optimization nor a frequency job.

WHAT THE CHAIN BUYS IS MEASURED. On a C5H10 TS at UKS B3LYP/def2-TZVP, same geometry and
same input but for the guess, a fresh guess collapsed to the closed-shell solution
(E = -196.344572 Eh, <S**2> = 0.000000) while !MORead held the broken-symmetry solution
(E = -196.364789 Eh, <S**2> = 0.864739), reproducing the followed solution to 1e-9 Eh --
12.7 kcal/mol apart. Without the chain the freq job converges from ORCA's own initial
guess while the stability job reads the optimization's orbitals, and those are two
different SCF solutions in exactly the cases the analysis exists to find.

NO LEVEL OR BASIS IS TRACKED, because ORCA projects a guess written in another basis onto
the basis of the job reading it. A def2-SVP job reading a def2-TZVP .gbw logs
'Atom 0: N(Shells)= 6 and 11 - projection required' and terminates normally at a sane
def2-SVP energy, so the chain crosses the basis change ARC makes between the optimization
and the single point on its own. The predicate is whether a checkfile this ESS wrote
exists, and no new state is stored on the species, in the restart dict or in output.yml.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.
calvinp0 added a commit that referenced this pull request Aug 23, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

EVERY ORCA JOB THAT RUNS AN SCF READS THE GUESS, as every Gaussian job carrying a
checkfile gets guess=read. OrcaAdapter.reads_orbital_guess is the single predicate behind
both halves of it, the !MORead and %moinp keywords and the guess.gbw upload, so the file
is uploaded for exactly the jobs that read it; emitting the keywords without the file
aborts the job on a missing guess. ORBITALS_GUESS_JOB_TYPES holds the job types this
adapter writes an SCF on one starting structure for: opt, conf_opt, optfreq and scan,
whose first SCF the guess seeds and whose later points ORCA propagates orbitals through
itself, and freq, sp, conf_sp and stability, each a single SCF. The rest are the job types
write_input_file emits no keyword for, so ORCA is handed no calculation for a guess to
seed -- composite, for which ORCA offers no composite method; irc and orbitals; and
directed_scan, for which this adapter writes neither the scan block nor the constraints
such a job needs -- plus gen_confs, tsg and onedmin, which belong to other adapters. A job
array is excluded because it writes no input file and its members share one remote path,
where one uploaded guess would stand in for every member, and a monatomic species because
ARC spawns it neither an optimization nor a frequency job.

WHAT THE CHAIN BUYS IS MEASURED. On a C5H10 TS at UKS B3LYP/def2-TZVP, same geometry and
same input but for the guess, a fresh guess collapsed to the closed-shell solution
(E = -196.344572 Eh, <S**2> = 0.000000) while !MORead held the broken-symmetry solution
(E = -196.364789 Eh, <S**2> = 0.864739), reproducing the followed solution to 1e-9 Eh --
12.7 kcal/mol apart. Without the chain the freq job converges from ORCA's own initial
guess while the stability job reads the optimization's orbitals, and those are two
different SCF solutions in exactly the cases the analysis exists to find.

NO LEVEL OR BASIS IS TRACKED, because ORCA projects a guess written in another basis onto
the basis of the job reading it. A def2-SVP job reading a def2-TZVP .gbw logs
'Atom 0: N(Shells)= 6 and 11 - projection required' and terminates normally at a sane
def2-SVP energy, so the chain crosses the basis change ARC makes between the optimization
and the single point on its own. The predicate is whether a checkfile this ESS wrote
exists, and no new state is stored on the species, in the restart dict or in output.yml.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.
calvinp0 added a commit that referenced this pull request Aug 23, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

EVERY ORCA JOB THAT RUNS AN SCF READS THE GUESS, as every Gaussian job carrying a
checkfile gets guess=read. OrcaAdapter.reads_orbital_guess is the single predicate behind
both halves of it, the !MORead and %moinp keywords and the guess.gbw upload, so the file
is uploaded for exactly the jobs that read it; emitting the keywords without the file
aborts the job on a missing guess. ORBITALS_GUESS_JOB_TYPES holds the job types this
adapter writes an SCF on one starting structure for: opt, conf_opt, optfreq and scan,
whose first SCF the guess seeds and whose later points ORCA propagates orbitals through
itself, and freq, sp, conf_sp and stability, each a single SCF. The rest are the job types
write_input_file emits no keyword for, so ORCA is handed no calculation for a guess to
seed -- composite, for which ORCA offers no composite method; irc and orbitals; and
directed_scan, for which this adapter writes neither the scan block nor the constraints
such a job needs -- plus gen_confs, tsg and onedmin, which belong to other adapters. A job
array is excluded because it writes no input file and its members share one remote path,
where one uploaded guess would stand in for every member, and a monatomic species because
ARC spawns it neither an optimization nor a frequency job.

WHAT THE CHAIN BUYS IS MEASURED. On a C5H10 TS at UKS B3LYP/def2-TZVP, same geometry and
same input but for the guess, a fresh guess collapsed to the closed-shell solution
(E = -196.344572 Eh, <S**2> = 0.000000) while !MORead held the broken-symmetry solution
(E = -196.364789 Eh, <S**2> = 0.864739), reproducing the followed solution to 1e-9 Eh --
12.7 kcal/mol apart. Without the chain the freq job converges from ORCA's own initial
guess while the stability job reads the optimization's orbitals, and those are two
different SCF solutions in exactly the cases the analysis exists to find.

NO LEVEL OR BASIS IS TRACKED, because ORCA projects a guess written in another basis onto
the basis of the job reading it. A def2-SVP job reading a def2-TZVP .gbw logs
'Atom 0: N(Shells)= 6 and 11 - projection required' and terminates normally at a sane
def2-SVP energy, so the chain crosses the basis change ARC makes between the optimization
and the single point on its own. The predicate is whether a checkfile this ESS wrote
exists, and no new state is stored on the species, in the restart dict or in output.yml.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.
ARC_TESTING_PATH holds read-only fixture inputs. Passing a subdirectory of it as a
project_directory made every test class in these modules write into one shared, fixed
path, and tearing that path down with shutil.rmtree at the end of the class.

Under pytest-xdist a single TestCase class is split across workers, so every worker runs
tearDownClass. One worker therefore deletes the shared directory while another is still
writing input files into it, which surfaces as FileExistsError / FileNotFoundError on an
unrelated test. A crashed run also leaves the directory behind inside the repository.

Each class now allocates its scratch with tempfile.mkdtemp() in setUpClass and releases it
with addClassCleanup. mkdtemp is unique per setUpClass invocation, so concurrent workers
never share a path and nothing is ever written under arc/testing.

Read-only fixture reads (the PBS time-limit err.txt in adapter_test) still come from
ARC_TESTING_PATH and are unchanged.
Same change as for the ESS job adapters: the TS search adapter tests wrote their project
directories into ARC_TESTING_PATH and removed them from tearDownClass or from ad-hoc
per-test cleanup helpers. Scratch now comes from tempfile.mkdtemp() and is released with
addClassCleanup / addCleanup.

The KinBot helper that removed a single test's subdirectory and then attempted to rmdir the
shared parent existed only to make the shared parent safe under xdist; with a per-class
temporary root the whole tree is released at once and the helper is dropped.

gcn_test.py also drops the `from arc.job.adapters.ts.gcn_ts import GCNAdapter` line, which
duplicated the `import arc.job.adapters.ts.gcn_ts as ts_gcn` already present; kinbot_test.py
imports unittest.mock as mock rather than `from unittest import mock` alongside
`import unittest`.
These modules built their project directories under ARC_TESTING_PATH and removed them in
tearDownClass, which is not safe when xdist splits the class across workers. Both UMA test
classes now allocate their own mkdtemp root, so the env-independent and model-dependent
classes can no longer reach into each other's tree either.
Four tests treated a fixture directory as writable scratch:

- common_test.test_globalize_paths wrote restart_paths_globalized.yml into
  arc/testing/restart/4_globalized_paths, which needed a self-healing cleanup helper called
  from both setUpClass and tearDownClass to keep the artifact from leaking into the repo.
  The fixture is now copied into a temporary directory and globalized there, and the two
  assertions that hard-coded the arc/testing path now assert against the temporary project
  directory.
- common_test.test_safe_copy_file copied its destination file next to the source fixture.
- processor_test.test_compare_rates wrote rate plots into arc/testing/process_kinetics.
- reaction_test.test_load_ts_xyz_user_guess_from_files passed the fixture directory itself
  as an ARC project_directory, so a Scheduler run created arc.log, output/ and
  log_and_restart_archive/ inside arc/testing/reactions/methanoate_hydrolysis. The fixture
  is now copied to a temporary directory first; the TS guess filenames in input_1.yml and
  input_2.yml resolve relative to project_directory, so the copy keeps them working.
- arkane_test.TestArkaneAdapter released its mkdtemp from tearDownClass rather than
  addClassCleanup, and wrote generate_arkane_input output into arc/testing.
The TestNMD and TestTSChecks classes built their job project directories under
ARC's shared Projects folder and deleted them in tearDownClass. pytest-xdist may
split a single TestCase class across workers, so each worker runs setUpClass and
tearDownClass independently and one worker's teardown removes a directory another
worker is still using. Allocate the scratch with tempfile.mkdtemp() in setUpClass
and release it with addClassCleanup instead.
…irectories

TestARC instantiated ARC without a project directory, so every instance created a
directory named after its project under ARC's shared Projects folder, several of
which were never deleted. TestPlotter additionally wrote its multi-species logs,
its BDE report and its IRC animation into the read-only arc/testing fixture tree,
where one test asserted the absence of files another test had created. Give each
of them a tempfile.mkdtemp() scratch directory released through addCleanup or
addClassCleanup, and drop the tearDownClass removals of shared paths.

The gcn_tst cleanup entry in species_test is dropped as well; nothing creates that
directory any more.
The restart tests derived their project directories from a per-xdist-worker
project name, which does not help when pytest-xdist splits the class across
workers. Allocate each project directory with tempfile.mkdtemp() and release it
with addCleanup.
@calvinp0
calvinp0 force-pushed the fix_test_scratch_isolation branch from 54c0848 to a63e083 Compare August 23, 2026 12:40
alongd pushed a commit that referenced this pull request Aug 23, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

EVERY ORCA JOB THAT RUNS AN SCF READS THE GUESS, as every Gaussian job carrying a
checkfile gets guess=read. OrcaAdapter.reads_orbital_guess is the single predicate behind
both halves of it, the !MORead and %moinp keywords and the guess.gbw upload, so the file
is uploaded for exactly the jobs that read it; emitting the keywords without the file
aborts the job on a missing guess. ORBITALS_GUESS_JOB_TYPES holds the job types this
adapter writes an SCF on one starting structure for: opt, conf_opt, optfreq and scan,
whose first SCF the guess seeds and whose later points ORCA propagates orbitals through
itself, and freq, sp, conf_sp and stability, each a single SCF. The rest are the job types
write_input_file emits no keyword for, so ORCA is handed no calculation for a guess to
seed -- composite, for which ORCA offers no composite method; irc and orbitals; and
directed_scan, for which this adapter writes neither the scan block nor the constraints
such a job needs -- plus gen_confs, tsg and onedmin, which belong to other adapters. A job
array is excluded because it writes no input file and its members share one remote path,
where one uploaded guess would stand in for every member, and a monatomic species because
ARC spawns it neither an optimization nor a frequency job.

WHAT THE CHAIN BUYS IS MEASURED. On a C5H10 TS at UKS B3LYP/def2-TZVP, same geometry and
same input but for the guess, a fresh guess collapsed to the closed-shell solution
(E = -196.344572 Eh, <S**2> = 0.000000) while !MORead held the broken-symmetry solution
(E = -196.364789 Eh, <S**2> = 0.864739), reproducing the followed solution to 1e-9 Eh --
12.7 kcal/mol apart. Without the chain the freq job converges from ORCA's own initial
guess while the stability job reads the optimization's orbitals, and those are two
different SCF solutions in exactly the cases the analysis exists to find.

NO LEVEL OR BASIS IS TRACKED, because ORCA projects a guess written in another basis onto
the basis of the job reading it. A def2-SVP job reading a def2-TZVP .gbw logs
'Atom 0: N(Shells)= 6 and 11 - projection required' and terminates normally at a sane
def2-SVP energy, so the chain crosses the basis change ARC makes between the optimization
and the single point on its own. The predicate is whether a checkfile this ESS wrote
exists, and no new state is stored on the species, in the restart dict or in output.yml.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.
calvinp0 added a commit that referenced this pull request Aug 23, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

EVERY ORCA JOB THAT RUNS AN SCF READS THE GUESS, as every Gaussian job carrying a
checkfile gets guess=read. OrcaAdapter.reads_orbital_guess is the single predicate behind
both halves of it, the !MORead and %moinp keywords and the guess.gbw upload, so the file
is uploaded for exactly the jobs that read it; emitting the keywords without the file
aborts the job on a missing guess. ORBITALS_GUESS_JOB_TYPES holds the job types this
adapter writes an SCF on one starting structure for: opt, conf_opt, optfreq and scan,
whose first SCF the guess seeds and whose later points ORCA propagates orbitals through
itself, and freq, sp, conf_sp and stability, each a single SCF. The rest are the job types
write_input_file emits no keyword for, so ORCA is handed no calculation for a guess to
seed -- composite, for which ORCA offers no composite method; irc and orbitals; and
directed_scan, for which this adapter writes neither the scan block nor the constraints
such a job needs -- plus gen_confs, tsg and onedmin, which belong to other adapters. A job
array is excluded because it writes no input file and its members share one remote path,
where one uploaded guess would stand in for every member, and a monatomic species because
ARC spawns it neither an optimization nor a frequency job.

WHAT THE CHAIN BUYS IS MEASURED. On a C5H10 TS at UKS B3LYP/def2-TZVP, same geometry and
same input but for the guess, a fresh guess collapsed to the closed-shell solution
(E = -196.344572 Eh, <S**2> = 0.000000) while !MORead held the broken-symmetry solution
(E = -196.364789 Eh, <S**2> = 0.864739), reproducing the followed solution to 1e-9 Eh --
12.7 kcal/mol apart. Without the chain the freq job converges from ORCA's own initial
guess while the stability job reads the optimization's orbitals, and those are two
different SCF solutions in exactly the cases the analysis exists to find.

NO LEVEL OR BASIS IS TRACKED, because ORCA projects a guess written in another basis onto
the basis of the job reading it. A def2-SVP job reading a def2-TZVP .gbw logs
'Atom 0: N(Shells)= 6 and 11 - projection required' and terminates normally at a sane
def2-SVP energy, so the chain crosses the basis change ARC makes between the optimization
and the single point on its own. The predicate is whether a checkfile this ESS wrote
exists, and no new state is stored on the species, in the restart dict or in output.yml.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.

AN EMPTY ORBITALS FILE IS NOT READ AS A GUESS. reads_orbital_guess tests the size of the
checkfile as well as its presence: the server-side copy of a .gbw an ORCA job died before
writing is silent, and a failed download leaves a zero-byte file behind, so the file can
be present and empty at the moment the input is composed. The adapter's fallback to an
orbitals file sitting in its own job directory goes through readable_checkfile, which
applies the same test, and is refused outright while the species carries an adopted
wavefunction-stability verdict and holds no checkfile, in which state the route to the
lower solution is BrokenSym rather than a guess.
calvinp0 added a commit that referenced this pull request Aug 23, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

EVERY ORCA JOB THAT RUNS AN SCF READS THE GUESS, as every Gaussian job carrying a
checkfile gets guess=read. OrcaAdapter.reads_orbital_guess is the single predicate behind
both halves of it, the !MORead and %moinp keywords and the guess.gbw upload, so the file
is uploaded for exactly the jobs that read it; emitting the keywords without the file
aborts the job on a missing guess. ORBITALS_GUESS_JOB_TYPES holds the job types this
adapter writes an SCF on one starting structure for: opt, conf_opt, optfreq and scan,
whose first SCF the guess seeds and whose later points ORCA propagates orbitals through
itself, and freq, sp, conf_sp and stability, each a single SCF. The rest are the job types
write_input_file emits no keyword for, so ORCA is handed no calculation for a guess to
seed -- composite, for which ORCA offers no composite method; irc and orbitals; and
directed_scan, for which this adapter writes neither the scan block nor the constraints
such a job needs -- plus gen_confs, tsg and onedmin, which belong to other adapters. A job
array is excluded because it writes no input file and its members share one remote path,
where one uploaded guess would stand in for every member, and a monatomic species because
ARC spawns it neither an optimization nor a frequency job.

WHAT THE CHAIN BUYS IS MEASURED. On a C5H10 TS at UKS B3LYP/def2-TZVP, same geometry and
same input but for the guess, a fresh guess collapsed to the closed-shell solution
(E = -196.344572 Eh, <S**2> = 0.000000) while !MORead held the broken-symmetry solution
(E = -196.364789 Eh, <S**2> = 0.864739), reproducing the followed solution to 1e-9 Eh --
12.7 kcal/mol apart. Without the chain the freq job converges from ORCA's own initial
guess while the stability job reads the optimization's orbitals, and those are two
different SCF solutions in exactly the cases the analysis exists to find.

NO LEVEL OR BASIS IS TRACKED, because ORCA projects a guess written in another basis onto
the basis of the job reading it. A def2-SVP job reading a def2-TZVP .gbw logs
'Atom 0: N(Shells)= 6 and 11 - projection required' and terminates normally at a sane
def2-SVP energy, so the chain crosses the basis change ARC makes between the optimization
and the single point on its own. The predicate is whether a checkfile this ESS wrote
exists, and no new state is stored on the species, in the restart dict or in output.yml.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.

AN EMPTY ORBITALS FILE IS NOT READ AS A GUESS. reads_orbital_guess tests the size of the
checkfile as well as its presence: the server-side copy of a .gbw an ORCA job died before
writing is silent, and a failed download leaves a zero-byte file behind, so the file can
be present and empty at the moment the input is composed. The adapter's fallback to an
orbitals file sitting in its own job directory goes through readable_checkfile, which
applies the same test, and is refused outright while the species carries an adopted
wavefunction-stability verdict and holds no checkfile, in which state the route to the
lower solution is BrokenSym rather than a guess.
calvinp0 added a commit that referenced this pull request Aug 24, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

EVERY ORCA JOB THAT RUNS AN SCF READS THE GUESS, as every Gaussian job carrying a
checkfile gets guess=read. OrcaAdapter.reads_orbital_guess is the single predicate behind
both halves of it, the !MORead and %moinp keywords and the guess.gbw upload, so the file
is uploaded for exactly the jobs that read it; emitting the keywords without the file
aborts the job on a missing guess. ORBITALS_GUESS_JOB_TYPES holds the job types this
adapter writes an SCF on one starting structure for: opt, conf_opt, optfreq and scan,
whose first SCF the guess seeds and whose later points ORCA propagates orbitals through
itself, and freq, sp, conf_sp and stability, each a single SCF. The rest are the job types
write_input_file emits no keyword for, so ORCA is handed no calculation for a guess to
seed -- composite, for which ORCA offers no composite method; irc and orbitals; and
directed_scan, for which this adapter writes neither the scan block nor the constraints
such a job needs -- plus gen_confs, tsg and onedmin, which belong to other adapters. A job
array is excluded because it writes no input file and its members share one remote path,
where one uploaded guess would stand in for every member, and a monatomic species because
ARC spawns it neither an optimization nor a frequency job.

WHAT THE CHAIN BUYS IS MEASURED. On a C5H10 TS at UKS B3LYP/def2-TZVP, same geometry and
same input but for the guess, a fresh guess collapsed to the closed-shell solution
(E = -196.344572 Eh, <S**2> = 0.000000) while !MORead held the broken-symmetry solution
(E = -196.364789 Eh, <S**2> = 0.864739), reproducing the followed solution to 1e-9 Eh --
12.7 kcal/mol apart. Without the chain the freq job converges from ORCA's own initial
guess while the stability job reads the optimization's orbitals, and those are two
different SCF solutions in exactly the cases the analysis exists to find.

NO LEVEL OR BASIS IS TRACKED, because ORCA projects a guess written in another basis onto
the basis of the job reading it. A def2-SVP job reading a def2-TZVP .gbw logs
'Atom 0: N(Shells)= 6 and 11 - projection required' and terminates normally at a sane
def2-SVP energy, so the chain crosses the basis change ARC makes between the optimization
and the single point on its own. The predicate is whether a checkfile this ESS wrote
exists, and no new state is stored on the species, in the restart dict or in output.yml.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.

AN EMPTY ORBITALS FILE IS NOT READ AS A GUESS. reads_orbital_guess tests the size of the
checkfile as well as its presence: the server-side copy of a .gbw an ORCA job died before
writing is silent, and a failed download leaves a zero-byte file behind, so the file can
be present and empty at the moment the input is composed. The adapter's fallback to an
orbitals file sitting in its own job directory goes through readable_checkfile, which
applies the same test, and is refused outright while the species carries an adopted
wavefunction-stability verdict and holds no checkfile, in which state the route to the
lower solution is BrokenSym rather than a guess.
calvinp0 added a commit that referenced this pull request Aug 25, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

EVERY ORCA JOB THAT RUNS AN SCF READS THE GUESS, as every Gaussian job carrying a
checkfile gets guess=read. OrcaAdapter.reads_orbital_guess is the single predicate behind
both halves of it, the !MORead and %moinp keywords and the guess.gbw upload, so the file
is uploaded for exactly the jobs that read it; emitting the keywords without the file
aborts the job on a missing guess. ORBITALS_GUESS_JOB_TYPES holds the job types this
adapter writes an SCF on one starting structure for: opt, conf_opt, optfreq and scan,
whose first SCF the guess seeds and whose later points ORCA propagates orbitals through
itself, and freq, sp, conf_sp and stability, each a single SCF. The rest are the job types
write_input_file emits no keyword for, so ORCA is handed no calculation for a guess to
seed -- composite, for which ORCA offers no composite method; irc and orbitals; and
directed_scan, for which this adapter writes neither the scan block nor the constraints
such a job needs -- plus gen_confs, tsg and onedmin, which belong to other adapters. A job
array is excluded because it writes no input file and its members share one remote path,
where one uploaded guess would stand in for every member, and a monatomic species because
ARC spawns it neither an optimization nor a frequency job.

WHAT THE CHAIN BUYS IS MEASURED. On a C5H10 TS at UKS B3LYP/def2-TZVP, same geometry and
same input but for the guess, a fresh guess collapsed to the closed-shell solution
(E = -196.344572 Eh, <S**2> = 0.000000) while !MORead held the broken-symmetry solution
(E = -196.364789 Eh, <S**2> = 0.864739), reproducing the followed solution to 1e-9 Eh --
12.7 kcal/mol apart. Without the chain the freq job converges from ORCA's own initial
guess while the stability job reads the optimization's orbitals, and those are two
different SCF solutions in exactly the cases the analysis exists to find.

NO LEVEL OR BASIS IS TRACKED, because ORCA projects a guess written in another basis onto
the basis of the job reading it. A def2-SVP job reading a def2-TZVP .gbw logs
'Atom 0: N(Shells)= 6 and 11 - projection required' and terminates normally at a sane
def2-SVP energy, so the chain crosses the basis change ARC makes between the optimization
and the single point on its own. The predicate is whether a checkfile this ESS wrote
exists, and no new state is stored on the species, in the restart dict or in output.yml.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.

AN EMPTY ORBITALS FILE IS NOT READ AS A GUESS. reads_orbital_guess tests the size of the
checkfile as well as its presence: the server-side copy of a .gbw an ORCA job died before
writing is silent, and a failed download leaves a zero-byte file behind, so the file can
be present and empty at the moment the input is composed. The adapter's fallback to an
orbitals file sitting in its own job directory goes through readable_checkfile, which
applies the same test, and is refused outright while the species carries an adopted
wavefunction-stability verdict and holds no checkfile, in which state the route to the
lower solution is BrokenSym rather than a guess.
calvinp0 added a commit that referenced this pull request Aug 26, 2026
Adds the ORCA side of the 'stability' job type: a single point at the frequency level and
on the frequency geometry that adds STABPerform and STABRestartUHFifUnstable to the
existing %scf block, reading the orbitals of the job under test.

THE INSTABILITY IS ALWAYS FOLLOWED, and that is not a preference. With
STABRestartUHFifUnstable false, ORCA 6.0.0 prints the verdict and the stability-matrix
roots and then dies in LEANSCF with a BLAS incompatible-matrices error and mpirun exit
code 62. Measured at eight processes and at one, and at six roots and at three, so it is
not an MPI artifact; LeanSCF false does not help, failing earlier, in the SCF, before any
verdict is printed. The three stable jobs run to a normal termination on the same
settings, so it is the re-entry into LeanSCF after an instability that breaks. ORCA
therefore has no equivalent of Gaussian's NoOpt, which reports an instability without
following it. With the key true the job terminates normally and the log holds two
analyses; the parser reads the verdict of the first, which is the wavefunction under
test. A crashed job would additionally be misread by determine_ess_status, whose orca
branch matches 'error termination in SCF' and not 'in LEANSCF', so the job would be an
unrecognised error and the verdict never read.

THE ORBITALS UNDER TEST ARE HANDED OVER, because ORCA's analysis is an SCF post-step: it
converges an SCF first, and from its own initial guess that need not be the solution the
frequency job reached. This is the hazard Gaussian's checkfile requirement exists to
prevent, and ARC's scheduler already refuses to spawn the job unless the species still
holds the checkfile its frequency job used. ORCA names its own orbitals after the input
file, so it cannot read and write one file the way Gaussian reuses a single checkfile:
the previous orbitals are uploaded as guess.gbw and read with !MORead and %moinp, while
the job's own input.gbw is downloaded and becomes the next job's guess. The adapter
adopts a checkfile on construction the way the Gaussian adapter does.

INPUT.GBW IS DOWNLOADED ONLY WHERE SOMETHING READS IT. A def2-TZVP .gbw runs to tens of MB
and the job type is off by default, so downloading one from every ORCA job would cost every
run bandwidth and disk for a file nothing opens. It is fetched for the job types the guess
chain actually reads from -- the opt, optfreq and composite jobs Scheduler.end_job adopts a
checkfile from -- plus the stability job, whose own orbitals are the relaxed solution. A job
array takes the data.hdf5 branch and fetches no orbitals at all, since its members share one
remote path; the docstring now says so rather than leaving it to be inferred.

EVERY ORCA JOB THAT RUNS AN SCF READS THE GUESS, as every Gaussian job carrying a
checkfile gets guess=read. OrcaAdapter.reads_orbital_guess is the single predicate behind
both halves of it, the !MORead and %moinp keywords and the guess.gbw upload, so the file
is uploaded for exactly the jobs that read it; emitting the keywords without the file
aborts the job on a missing guess. ORBITALS_GUESS_JOB_TYPES holds the job types this
adapter writes an SCF on one starting structure for: opt, conf_opt, optfreq and scan,
whose first SCF the guess seeds and whose later points ORCA propagates orbitals through
itself, and freq, sp, conf_sp and stability, each a single SCF. The rest are the job types
write_input_file emits no keyword for, so ORCA is handed no calculation for a guess to
seed -- composite, for which ORCA offers no composite method; irc and orbitals; and
directed_scan, for which this adapter writes neither the scan block nor the constraints
such a job needs -- plus gen_confs, tsg and onedmin, which belong to other adapters. A job
array is excluded because it writes no input file and its members share one remote path,
where one uploaded guess would stand in for every member, and a monatomic species because
ARC spawns it neither an optimization nor a frequency job.

WHAT THE CHAIN BUYS IS MEASURED. On a C5H10 TS at UKS B3LYP/def2-TZVP, same geometry and
same input but for the guess, a fresh guess collapsed to the closed-shell solution
(E = -196.344572 Eh, <S**2> = 0.000000) while !MORead held the broken-symmetry solution
(E = -196.364789 Eh, <S**2> = 0.864739), reproducing the followed solution to 1e-9 Eh --
12.7 kcal/mol apart. Without the chain the freq job converges from ORCA's own initial
guess while the stability job reads the optimization's orbitals, and those are two
different SCF solutions in exactly the cases the analysis exists to find.

NO LEVEL OR BASIS IS TRACKED, because ORCA projects a guess written in another basis onto
the basis of the job reading it. A def2-SVP job reading a def2-TZVP .gbw logs
'Atom 0: N(Shells)= 6 and 11 - projection required' and terminates normally at a sane
def2-SVP energy, so the chain crosses the basis change ARC makes between the optimization
and the single point on its own. The predicate is whether a checkfile this ESS wrote
exists, and no new state is stored on the species, in the restart dict or in output.yml.

The single point runs on defgrid3, the grid a frequency job uses, rather than the
defgrid2 an sp would take, so the SCF under test integrates on the grid the Hessian was
built on.

The input template gains two placeholders that render empty for every other job type, so
every existing ORCA input is emitted byte for byte as before. The test module's project
directory moves from a shared path under arc/testing to a private tempfile.mkdtemp(),
which is what the project requires of writable test scratch. It adopts the exact form open
PR #1008 uses for the same file -- cls.scratch_dir = tempfile.mkdtemp(prefix='arc_test_orca_')
with project_directory=os.path.join(cls.scratch_dir, 'test_OrcaAdapter') -- so the two PRs'
overlapping lines are textually identical and merge without conflict.

AN EMPTY ORBITALS FILE IS NOT READ AS A GUESS. reads_orbital_guess tests the size of the
checkfile as well as its presence: the server-side copy of a .gbw an ORCA job died before
writing is silent, and a failed download leaves a zero-byte file behind, so the file can
be present and empty at the moment the input is composed. The adapter's fallback to an
orbitals file sitting in its own job directory goes through readable_checkfile, which
applies the same test, and is refused outright while the species carries an adopted
wavefunction-stability verdict and holds no checkfile, in which state the route to the
lower solution is BrokenSym rather than a guess.
@calvinp0
calvinp0 requested a lite review from Copilot August 27, 2026 12:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@calvinp0

Copy link
Copy Markdown
Member Author

Superseded by #1028, which rebases this onto current main and combines both isolation branches into one. Measured there: random-order failure counts across eight seeds drop from 8-12 to 5-6, against a floor of 5 environmental torch_ani_test failures.

Closing in favour of #1028 rather than rebasing this one separately, since the two overlap in intent and main has moved past both.

@calvinp0 calvinp0 closed this Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants