Small fix, and it is not the real problem -- the real problem is #1, which I will do. This issue is just to get your branch into a shape that does not have to be undone later. It should take about 15 minutes.
What is going on
Four classes do two different jobs:
- one job = one 1D sweep. The worker runs this. It has its own data.
- the aggregate = all the jobs collected together. This is what you analyze at the end. It gets an extra attribute,
batch_expts, which is the list of child jobs.
MBRSpectrumExperiment.analyze is written for the aggregate. Its first line that touches data is analysis_expts = self.batch_expts (experiments/qsim/mbr_spectrum.py:163). A single child job has no
batch_expts, so that line raises AttributeError.
You already fixed this correctly for display in a94880b: you added a check for batch_expts and fell back to the parent class. Do the same thing for analyze, and then the changes to the worker and the runner are not needed.
Why not change the worker
The worker calls expt.go(analyze=True, ...). That has been true since the worker was written. It is deliberate: go() runs analyze before save (slab/experiment.py:174-177), so small processed results (rotated IQ, fits) get stored in the same HDF5 file as the raw data. We want to keep that.
We are still in the middle of the refactor. The four MBR classes are not in great shape yet. When the refactor is done, each object knows who it is: a 1D Experiment class only does 1D analysis and a 2D aggregate class only does 2D processing. So the fix belongs in those classes, not in the worker. We keep track of correct behavior via "which class do I belong to" accurately for each object, not via an exploding number of config dict keys, which leads to literally an exponential number
of possible states and that's exactly how code becomes unmaintainable and why we are refactoring.
Steps
-
Undo the worker and runner change. It is one commit and touches only those
two files, so a plain revert is enough:
-
In each of these four files, add two lines at the very top of the analyze
method body (before anything else, including if data is not None):
if not hasattr(self, "batch_expts"):
return super().analyze(data=data)
| file |
method starts at |
experiments/qsim/mbr_spectrum.py |
line 107 |
experiments/qsim/mbr_orthogonality.py |
line 228 |
experiments/qsim/mbr_phase_correction.py |
line 420 |
experiments/qsim/mbr_propagator.py |
line 168 |
super().analyze(...) ends up at the base class, which does nothing and
returns None. That is safe: acquire has already set self.data
(qsim_base.py:482), and save_data falls back to self.data when it is
handed None (slab/experiment.py:191-192). So the child job saves its raw
data exactly as before.
-
Check it without needing hardware. This should print None and not raise:
pixi run python -c "
from experiments.qsim.mbr_spectrum import MBRSpectrumExperiment as E
e = E.__new__(E); e.data = {}
print(e.analyze())
"
Repeat for the other three classes if you want to be sure.
Why this is better than the runner flag
The flag is only set by BatchRunner.execute. So it only protects the batch queue path. A single child job run any other way -- run_local, or runner.execute with use_queue=False -- still uses analyze=True by default and still crashes. Putting the check in the class covers every path at once.
Same reasoning applies to your display guards: leave them in, they are right.
Blocked by / superseded by #1: once #1 is done, child jobs will not be instances of these classes at all, and these guards become unnecessary. Leaving them in is harmless.
Small fix, and it is not the real problem -- the real problem is #1, which I will do. This issue is just to get your branch into a shape that does not have to be undone later. It should take about 15 minutes.
What is going on
Four classes do two different jobs:
batch_expts, which is the list of child jobs.MBRSpectrumExperiment.analyzeis written for the aggregate. Its first line that touches data isanalysis_expts = self.batch_expts(experiments/qsim/mbr_spectrum.py:163). A single child job has nobatch_expts, so that line raisesAttributeError.You already fixed this correctly for
displayin a94880b: you added a check forbatch_exptsand fell back to the parent class. Do the same thing foranalyze, and then the changes to the worker and the runner are not needed.Why not change the worker
The worker calls
expt.go(analyze=True, ...). That has been true since the worker was written. It is deliberate:go()runsanalyzebeforesave(slab/experiment.py:174-177), so small processed results (rotated IQ, fits) get stored in the same HDF5 file as the raw data. We want to keep that.We are still in the middle of the refactor. The four MBR classes are not in great shape yet. When the refactor is done, each object knows who it is: a 1D Experiment class only does 1D analysis and a 2D aggregate class only does 2D processing. So the fix belongs in those classes, not in the worker. We keep track of correct behavior via "which class do I belong to" accurately for each object, not via an exploding number of config dict keys, which leads to literally an exponential number
of possible states and that's exactly how code becomes unmaintainable and why we are refactoring.
Steps
Undo the worker and runner change. It is one commit and touches only those
two files, so a plain revert is enough:
In each of these four files, add two lines at the very top of the
analyzemethod body (before anything else, including
if data is not None):experiments/qsim/mbr_spectrum.pyexperiments/qsim/mbr_orthogonality.pyexperiments/qsim/mbr_phase_correction.pyexperiments/qsim/mbr_propagator.pysuper().analyze(...)ends up at the base class, which does nothing andreturns
None. That is safe:acquirehas already setself.data(
qsim_base.py:482), andsave_datafalls back toself.datawhen it ishanded
None(slab/experiment.py:191-192). So the child job saves its rawdata exactly as before.
Check it without needing hardware. This should print
Noneand not raise:Repeat for the other three classes if you want to be sure.
Why this is better than the runner flag
The flag is only set by
BatchRunner.execute. So it only protects the batch queue path. A single child job run any other way --run_local, orrunner.executewithuse_queue=False-- still usesanalyze=Trueby default and still crashes. Putting the check in the class covers every path at once.Same reasoning applies to your
displayguards: leave them in, they are right.Blocked by / superseded by #1: once #1 is done, child jobs will not be instances of these classes at all, and these guards become unnecessary. Leaving them in is harmless.