Honour the computer's default_mpiprocs_per_machine in HyperQueueJobResource - #49
Conversation
…source accepts_default_mpiprocs_per_machine() returned False, so aiida-core's Scheduler.preprocess_resources never injected the computer's default_mpiprocs_per_machine, and the backward-compatibility path num_machines * num_mpiprocs_per_machine fell back to a hard-coded 1: every builder-driven CalcJob on a HyperQueue computer silently ran on a single CPU regardless of the computer's configured default. Return True and handle the None that aiida-core injects when the computer defines no default (falling back to 1 as before). Also join the two halves of the deprecation warning, the second of which was a dead string statement that never reached the message. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
for more information, see https://pre-commit.ci
|
I should be able to have a look at this, soon |
khsrali
left a comment
There was a problem hiding this comment.
Thanks @elinscott ,
Sorry I had a lot other responsibilities, took some time to get into this.
The changes are sound.
Tests require a fix, see below.
And, the changes are technically backward incompatible, let's put them it in the changelog on this PR, before it gets forgotten. You can add them in a section like:
## Unpublished:
balah blah
Or, if you don't mind, make a new release and put them there.
There was a problem hiding this comment.
| from aiida_hyperqueue.scheduler import ( | |
| AiiDAHypereQueueDeprecationWarning, | |
| HyperQueueJobResource, | |
| HyperQueueScheduler, | |
| ) |
There was a problem hiding this comment.
Done. (Aside: Hyper-e-QueueDepecationWarning??!)
| def test_resource_validation_backward_compatibility(): | ||
| """Tests for the deprecated `num_machines` / `num_mpiprocs_per_machine` path.""" | ||
| # num_cpus is the product of the two legacy keys | ||
| with pytest.warns(Warning, match="deprecated"): | ||
| resource = HyperQueueJobResource(num_machines=2, num_mpiprocs_per_machine=8) | ||
| assert resource.num_cpus == 16 | ||
|
|
||
| # the computer's default_mpiprocs_per_machine, which aiida-core's | ||
| # `Scheduler.preprocess_resources` injects as `num_mpiprocs_per_machine`, | ||
| # must be honoured (this is what `accepts_default_mpiprocs_per_machine` | ||
| # returning True enables) | ||
| with pytest.warns(Warning, match="deprecated"): | ||
| resource = HyperQueueJobResource(num_machines=1, num_mpiprocs_per_machine=8) | ||
| assert resource.num_cpus == 8 | ||
|
|
||
| # a computer without a default leads aiida-core to inject `None`; fall back to 1 | ||
| with pytest.warns(Warning, match="deprecated"): | ||
| resource = HyperQueueJobResource(num_machines=1, num_mpiprocs_per_machine=None) | ||
| assert resource.num_cpus == 1 | ||
|
|
||
| # `num_mpiprocs_per_machine` omitted entirely also falls back to 1 | ||
| with pytest.warns(Warning, match="deprecated"): | ||
| resource = HyperQueueJobResource(num_machines=1) | ||
| assert resource.num_cpus == 1 | ||
|
|
||
|
|
||
| def test_accepts_default_mpiprocs_per_machine(): | ||
| """The resource class must accept the computer's default so it reaches validate_resources.""" | ||
| assert HyperQueueJobResource.accepts_default_mpiprocs_per_machine() | ||
|
|
||
|
|
There was a problem hiding this comment.
From what I understand, one cannot test fix this way. because HyperQueueJobResource bypasses preprocess_resources entirely. The way to go would be using HyperQueueScheduler().create_job_resource which returns the genuine resources considered.
| def test_resource_validation_backward_compatibility(): | |
| """Tests for the deprecated `num_machines` / `num_mpiprocs_per_machine` path.""" | |
| # num_cpus is the product of the two legacy keys | |
| with pytest.warns(Warning, match="deprecated"): | |
| resource = HyperQueueJobResource(num_machines=2, num_mpiprocs_per_machine=8) | |
| assert resource.num_cpus == 16 | |
| # the computer's default_mpiprocs_per_machine, which aiida-core's | |
| # `Scheduler.preprocess_resources` injects as `num_mpiprocs_per_machine`, | |
| # must be honoured (this is what `accepts_default_mpiprocs_per_machine` | |
| # returning True enables) | |
| with pytest.warns(Warning, match="deprecated"): | |
| resource = HyperQueueJobResource(num_machines=1, num_mpiprocs_per_machine=8) | |
| assert resource.num_cpus == 8 | |
| # a computer without a default leads aiida-core to inject `None`; fall back to 1 | |
| with pytest.warns(Warning, match="deprecated"): | |
| resource = HyperQueueJobResource(num_machines=1, num_mpiprocs_per_machine=None) | |
| assert resource.num_cpus == 1 | |
| # `num_mpiprocs_per_machine` omitted entirely also falls back to 1 | |
| with pytest.warns(Warning, match="deprecated"): | |
| resource = HyperQueueJobResource(num_machines=1) | |
| assert resource.num_cpus == 1 | |
| def test_accepts_default_mpiprocs_per_machine(): | |
| """The resource class must accept the computer's default so it reaches validate_resources.""" | |
| assert HyperQueueJobResource.accepts_default_mpiprocs_per_machine() | |
| @pytest.mark.parametrize( | |
| "resources, default_mpiprocs, expected_num_cpus", | |
| ( | |
| ({"num_machines": 1}, 8, 8), # the computer's default must be honoured | |
| ({"num_machines": 1}, None, 1), # no default on the computer -> fall back to 1 | |
| ({"num_machines": 2, "num_mpiprocs_per_machine": 8}, 4, 16), # explicit wins | |
| ({"num_cpus": 4}, 8, 4), # the modern path ignores the default | |
| ), | |
| ) | |
| @pytest.mark.filterwarnings("ignore:The `num_machines`") | |
| def test_resource_preprocessing(resources, default_mpiprocs, expected_num_cpus): | |
| """The computer's `default_mpiprocs_per_machine` must reach the deprecated `num_machines` path. | |
| Mirrors `CalcJob`, which calls `preprocess_resources` with the computer's default before the | |
| resources are validated. | |
| """ | |
| HyperQueueScheduler.preprocess_resources(resources, default_mpiprocs) | |
| resource = HyperQueueScheduler().create_job_resource(**resources) | |
| assert resource.num_cpus == expected_num_cpus | |
| def test_resource_deprecation(): | |
| """The `num_machines` / `num_mpiprocs_per_machine` path is deprecated.""" | |
| with pytest.warns(AiiDAHypereQueueDeprecationWarning, match="deprecated"): | |
| HyperQueueJobResource(num_machines=1, num_mpiprocs_per_machine=8) |
There was a problem hiding this comment.
Adopted. But for the record, the original tests did detect the fix — I ran both versions against the pre-fix base commit (84d8083) and against this branch, and both fail on the base and pass here. But the newer tests are cleaner, thanks!
- Replace the direct-construction resource tests with the reviewer's parametrized test that routes through preprocess_resources and create_job_resource, plus a dedicated deprecation-warning test - Import AiiDAHypereQueueDeprecationWarning in tests - Add an Unpublished changelog section noting the behavior change Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…into address-review-49 # Conflicts: # tests/test_scheduler.py
Done — added an |
|
Thanks @khsrali . Should be ready for another look from you |
khsrali
left a comment
There was a problem hiding this comment.
all good @elinscott
Thanks again!
Problem
HyperQueueJobResource.accepts_default_mpiprocs_per_machine()returnsFalse, soaiida-core'sScheduler.preprocess_resourcesnever injects the computer'sdefault_mpiprocs_per_machineinto the resources. The backward-compatibility path then computesnum_cpus = num_machines * num_mpiprocs_per_machinewith a hard-coded fallback of 1 — so every builder-drivenCalcJob(e.g. anything built throughaiida-quantumespresso'sget_builder_from_protocol, which seedsresources = {'num_machines': 1}) silently runs on a single CPU, regardless of the "Default #procs/machine" configured on the computer.See the existing TODO comment in
validate_resources, which already describes this hole.Change
accepts_default_mpiprocs_per_machine()now returnsTrue, so aiida-core injects the computer's default asnum_mpiprocs_per_machinewhenever the user did not set it explicitly. The backward-compatibility product picks it up unchanged.None— the fallback to 1 is kept for that casenum_cpuspath is untouched.Testing
New tests in
tests/test_scheduler.py