[WIP] Add an ImpactX interface - #158
Open
ax3l wants to merge 5 commits into
Open
Conversation
ImpactX is an s-based beam dynamics code, the successor of IMPACT-Z, and openPMD-beamphysics had no interface for it. This adds one alongside the existing astra/bmad/elegant/gpt/impact interfaces: - beamphysics/interfaces/impactx.py: converters in both directions, plus a reader for ImpactX' openPMD BeamMonitor output that needs no ImpactX installation (openpmd-api is imported lazily). - ParticleGroup.to_impactx() / .from_impactx(), mirroring to_bmad/from_bmad. - Tests, and a small BeamMonitor fixture written by ImpactX 26.08 with the generate.py script beside it, so the reader is covered in CI without ImpactX. Coordinates: ImpactX holds particles at fixed s, so the result is in z-coordinates -- all z equal, the bunch length a spread in t -- as bmad.py does. The transverse coordinates stay in the local frame relative to the reference particle, since adding x_ref would be wrong wherever the reference orbit bends, while t is absolute lab time, which is unambiguous. A few ImpactX specifics are handled rather than papered over: t is c*t in metres there; qm is reported in different units depending on how the particles were inserted, so it is never trusted on read; the openPMD id record holds AMReX' packed idcpu, whose per-rank id field alone is not unique, so the whole value becomes the ParticleGroup id with only the validity bit moved to status; and every particle in a particles_lost file is lost by construction even though AMReX marks them valid, so they are not reported as alive. Spin and runtime per-particle components have no ParticleGroup representation and are refused rather than silently dropped, with strict=False to opt out. Verified against a live ImpactX 26.08 run: a bunch injected through add_n_particles and read back out of the monitor file matches to 2e-16 of the spread in x, y and t and 2.4e-13 in pz, with identical emittances; and the reader reproduces ImpactX' own reduced beam characteristics to 1e-12. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DhbdaEDSBqFN974t9QcXem
The module docstring had no link for lume-impactx (I had removed a guessed one) and named only the ImpactX documentation site. Add both source repositories: ImpactX is BLAST-ImpactX/impactx and lume-impactx is ax3l/lume-impactx. impactx.readthedocs.io stays as the documentation link, confirmed canonical by ImpactX' own README. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DhbdaEDSBqFN974t9QcXem
BLAST-ImpactX/impactx#1647 hands the beam's live reference particle to the lost-particle container, so particles_lost.* will carry a usable one instead of a default-constructed RefPart. Three consequences here: - The "zeroed reference particle" error now says which ImpactX versions it applies to, rather than presenting it as how ImpactX behaves. - The ref= caveat generalizes. It was written as a cost of *supplying* a reference particle from a monitor; it is really intrinsic. The momenta are normalized by beta_gamma at the reference particle's own s, so wherever the reference energy changes between the loss point and the reference particle in hand, the conversion is approximate. That is the same limitation ImpactX documents for the reference particle it now stores: the invariants (mass, charge) describe lost particles exactly, the kinematic attributes are end-of-tracking values. - The test asserted the fixture has a zeroed reference particle, which would have failed the day someone regenerated it against a newer ImpactX. It now branches on what the file actually holds and asserts the right behaviour for either. The lost-file detection needs no change: it keys on the s_lost record, which CollectLost always adds, and only falls back to the zeroed reference particle. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DhbdaEDSBqFN974t9QcXem
The ref= docstring claimed that ImpactX stores mass and charge as invariants and the kinematic attributes as end-of-tracking values for lost particles. That is the current shape of BLAST-ImpactX/impactx#1647, which is still in flux -- what to key on, and whether final state is even the right thing to store, is undecided. Keep only the part that does not depend on how that lands: a lost particle converts exactly when the reference energy did not change between the loss point and the reference particle in hand, and this reader applies no correction of its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DhbdaEDSBqFN974t9QcXem
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ImpactX is an s-based beam dynamics code, the successor of IMPACT-Z, and there is currently no interface for it here. This adds one alongside the existing
astra/bmad/elegant/gpt/impactinterfaces.Marked WIP: I would like feedback on the API choices below before this is considered for merging.
What's in it
beamphysics/interfaces/impactx.py— converters in both directions, plus a reader for ImpactX' openPMDBeamMonitoroutput.openpmd-apiis imported lazily, so the module costs nothing unless the reader is used, and nothing here needs ImpactX installed.ParticleGroup.to_impactx()/.from_impactx()— mirroringto_bmad/from_bmad.BeamMonitorfixture (177 KB) written by ImpactX 26.08, with thegenerate.pythat produced it beside the data — the same arrangement asdocs/examples/data/genesis4/run.sh. This lets the reader be covered in CI without ImpactX.openpmd-apiadded toenvironment.ymland thedevextra, without which the reader tests would silently skip.Coordinates
ImpactX holds particles at fixed
s, so the result is in z-coordinates — allzequal, the bunch length a spread int— the same conventionbmad.pyuses ("Zero by definition in z-coordinates"). Bmad is the closest existing analogue, and the signatures follow it.The transverse coordinates stay in the local frame relative to the reference particle: adding
x_refwould be wrong wherever the reference orbit bends, since localxis then not labx. Time is absolute (t = t_ref + position_t/c), which is unambiguous and is what openPMD'sposition/t + positionOffset/tmeans in ImpactX output.ImpactXRefPartcarries the lab-frame reference particle for anyone who needs to place the bunch.ImpactX specifics handled rather than papered over
tisc·tin metres in ImpactX;qmis reported in different units depending on how the particles were inserted, so it is never trusted on read — the species comes from the reference particle.idrecord holds AMReX' packedidcpu. Its id field comes from a per-rank counter, so it repeats across ranks and is not unique on its own; the whole value becomes theParticleGroupid, with only the validity bit moved intostatus(which also makes it fit in the signed 64-bit integer ids are stored in — the raw value overflows silently).particles_lostfile is lost by construction even though AMReX marks them valid, so they are not reported as alive.ParticleGrouprepresentation. The reader refuses rather than returning a silently zeroed bunch;strict=Falseopts out with a warning.Verification
Against a live ImpactX 26.08 run: a bunch injected via
add_n_particlesand read back out of the monitor file matches to 2e-16 of the spread inx,yandtand 2.4e-13 inpz, with identical emittances. The reader reproduces ImpactX' own reduced beam characteristics (sigma_x/y/t,mean_*,charge_C) to 1e-12, and the emittance to 1e-9 once then/(n-1)difference between this package's bias-corrected covariance and ImpactX' population moments is accounted for.Questions for review
ParticleGroupcannot carry extra per-particle arrays. ImpactX hasspin_x/y/zand arbitrary runtime components;bmad.py'swrite_bmadcarries aTODO: Spinfor the same reason. A general mechanism onParticleGroupwould close this for every s-based code at once. Worth a conversation — this interface could then carry them instead of refusing.UnrepresentableParticleDatasubclassesNotImplementedError. AValueErrorsubclass may read better for what is really a data-validation failure. Happy to change.ParticleStatushas onlyCATHODE = 0andALIVE = 1, and each interface passes its own source code's value through, so there is no universal "lost" value. This uses2;0is ruled out becauseastra.pywritesstatus == 0back out as Astra's-1, "at the cathode". Let me know if you would rather see a member added to the enum.docs/api/*.mdpage, so I did not add one — butfrom_impactx's docstring points atbeamphysics.interfaces.impactx, which is not rendered anywhere in the built site. Adocs/api/interfaces.mdpage, or a cell inread_examples.ipynb, would fix that if you want it.🤖 Generated with Claude Code
https://claude.ai/code/session_01DhbdaEDSBqFN974t9QcXem