diff --git a/.github/workflows/build_publish.yml b/.github/workflows/build_publish.yml index 56e9930..2c2a7f8 100644 --- a/.github/workflows/build_publish.yml +++ b/.github/workflows/build_publish.yml @@ -1,6 +1,8 @@ name: Build/publish package -on: push +on: + push: + branches-ignore: [joss] jobs: build_wheels: diff --git a/.github/workflows/draft_joss.yml b/.github/workflows/draft_joss.yml new file mode 100644 index 0000000..9bc0236 --- /dev/null +++ b/.github/workflows/draft_joss.yml @@ -0,0 +1,26 @@ +name: Draft JOSS +on: + push: + branches: [joss] + +jobs: + paper: + runs-on: ubuntu-latest + name: Paper Draft + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Build draft PDF + uses: openjournals/openjournals-draft-action@master + with: + journal: joss + # This should be the path to the paper within your repo. + paper-path: joss/paper.md + - name: Upload + uses: actions/upload-artifact@v4 + with: + name: paper + # This is the output path where Pandoc will write the compiled + # PDF. Note, this should be the same directory as the input + # paper.md + path: joss/paper.pdf \ No newline at end of file diff --git a/joss/.gitignore b/joss/.gitignore new file mode 100644 index 0000000..f5a5494 --- /dev/null +++ b/joss/.gitignore @@ -0,0 +1,5 @@ +# Ignore EPOCH files +*.sdf +deck.status +o1.visit +epoch2d.dat \ No newline at end of file diff --git a/joss/Electric_Field_Ey.gif b/joss/Electric_Field_Ey.gif new file mode 100644 index 0000000..229563a Binary files /dev/null and b/joss/Electric_Field_Ey.gif differ diff --git a/joss/Electric_Field_Ey.png b/joss/Electric_Field_Ey.png new file mode 100644 index 0000000..abf8c35 Binary files /dev/null and b/joss/Electric_Field_Ey.png differ diff --git a/joss/Electric_Field_Ey_frames.png b/joss/Electric_Field_Ey_frames.png new file mode 100644 index 0000000..450b260 Binary files /dev/null and b/joss/Electric_Field_Ey_frames.png differ diff --git a/joss/README.md b/joss/README.md new file mode 100644 index 0000000..f055a7d --- /dev/null +++ b/joss/README.md @@ -0,0 +1,25 @@ +# JOSS Submission + +This directory contains the paper and scripts for the submission to the Journal of Open Source Software + +- `paper.md`: Paper +- `paper.bib`: bibliography +- `generate_figures.py`: Generate the figures for the plot +- `input.deck`: The EPOCH setup script used to generate the data for the figures +- `Electric_Field_Ey.png`: A plot of a laser focusing in a vacuum of the 10th SDF file +- `Electric_Field_Ey.gif`: The animation of a laser focusing in a vacuum +- `Electric_Field_Ey_frames.png`: 4 plots of a laser focusing in a vacuum used in place of the GIF for the paper + +## Compiling and running EPOCH + +1. `git clone --recursive https://github.com/epochpic/epoch.git` +2. Install libraries + 1. (ON MAC) `brew install open-mpi` + 2. (LINUX) `sudo apt install gfortran openmpi-bin openmpi-common libopenmpi-dev libgtk2.0-dev` +3. Open the `epoch2d` folder +4. Compile the executable `make COMPILER=gfortran MPIF90=mpif90 -j4` +5. Run the simulation `mpirun -n 8 ./bin/epoch2d <<< sdf-xarray/joss` + +## Building the JOSS PDF + +`pandoc paper.md --citeproc --to=pdf -o paper.pdf` \ No newline at end of file diff --git a/joss/generate_figures.py b/joss/generate_figures.py new file mode 100644 index 0000000..7c500e6 --- /dev/null +++ b/joss/generate_figures.py @@ -0,0 +1,38 @@ +import matplotlib.pyplot as plt + +import sdf_xarray as sdfxr + +plt.rcParams.update({"font.size": 13}) + +# Save a plot of a single SDF +ds = sdfxr.open_dataset("0010.sdf") +ds = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid", "Y_Grid_mid"]) +ds["Electric_Field_Ey"].epoch.plot() +plt.tight_layout() +plt.savefig("Electric_Field_Ey.png", dpi=500) +plt.close() + +# Generate the animation +ds = sdfxr.open_mfdataset("*.sdf") +ds = ds.epoch.rescale_coords(1e15, "fs", "time") +ds = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid", "Y_Grid_mid"]) + +anim = ds["Electric_Field_Ey"].epoch.animate() +plt.tight_layout() +anim.save("Electric_Field_Ey.gif", fps=10, dpi=500) +plt.close() + +# Generate 4 frames in place of the animation +plt.rcParams.update({"font.size": 16}) +fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(14, 8)) + +flux_x = ds["Electric_Field_Ey"] +# Mimic the normalized colormap that would be built if this was an actual animation +norm = plt.Normalize(vmin=flux_x.min().values, vmax=flux_x.max().values) +flux_x.isel(time=5).epoch.plot(ax=ax1, norm=norm) +flux_x.isel(time=10).epoch.plot(ax=ax2, norm=norm) +flux_x.isel(time=15).epoch.plot(ax=ax3, norm=norm) +flux_x.isel(time=20).epoch.plot(ax=ax4, norm=norm) + +plt.tight_layout() +fig.savefig("Electric_Field_Ey_frames.png", dpi=500) diff --git a/joss/input.deck b/joss/input.deck new file mode 100644 index 0000000..aa1a79f --- /dev/null +++ b/joss/input.deck @@ -0,0 +1,61 @@ +begin:control + nx = 800 + ny = 400 + t_end = 200e-15 + x_min = 0 + x_max = 50e-6 + y_min = -10e-6 + y_max = 10e-6 + stdout_frequency = 100 +end:control + +begin:boundaries + bc_x_min = simple_laser + bc_x_max = open + bc_y_min = open + bc_y_max = open +end:boundaries + +begin:constant + I_fwhm = 2.0e-6 # FWHM of laser intensity + I_peak_Wcm2 = 1.0e18 # 0.5 * eps0 * c * E_peak^2 + las_lambda = 1.0e-6 # Laser wavelength + foc_dist = 25.0e-6 # Boundary to focal point distance + lambda_L = 0.8e-6 # Wavelength of the laser + tau_fwhm_L = 20e-15 # Laser full width half maximum + y_fwhm_L = 2.4e-6 # Laser focal spot originally 2.4 (3x wavelength) +end:constant + +begin:constant + w_t = tau_fwhm_L / sqrt(2*loge(2)) # Beam waist in t + w_y = y_fwhm_L / sqrt(2*loge(2)) # Beam waist in y + t_hw01m = 0.5 * tau_fwhm_L * sqrt(loge(10)/loge(2)) # Half width 0.1 maximum + + x_foc = foc_dist # Boundary to focal point distance + k_L = 2.0 * pi / lambda_L # Laser wave number + x_Rr = pi * w_y^2 / lambda_L # Rayleigh range + omega_L = 2 * pi * c / lambda_L # Angular frequency of the laser + + w_boundary = w_y * sqrt(1 + (x_foc/x_Rr)^2) # Waist on boundary + I_boundary = I_peak_Wcm2 * (w_y / w_boundary)^2 # Intens. on boundary + r_curve = x_foc * (1 + (x_Rr/x_foc)^2) # Boundary curv. rad. + phase_Gouy = atan(-x_foc/r_curve) # Boundary Gouy shift +end:constant + +begin:laser + boundary = x_min + intensity_w_cm2 = I_boundary + omega = omega_L + phase = k_L * y^2 / (2 * r_curve) - phase_Gouy + profile = gauss(y, 0, w_boundary) # Spatial profile + t_profile = gauss(time, t_hw01m, w_t) # Temporal profile + t_start = start + t_end = end +end:laser + +begin:output + name = o1 + dt_snapshot = 10 * femto + ey = always + bz = always +end:output diff --git a/joss/paper.bib b/joss/paper.bib new file mode 100644 index 0000000..a05aadb --- /dev/null +++ b/joss/paper.bib @@ -0,0 +1,199 @@ +@article{arber:2015, + title = {Contemporary particle-in-cell approach to laser-plasma modelling}, + volume = {57}, + issn = {1361-6587}, + url = {http://dx.doi.org/10.1088/0741-3335/57/11/113001}, + doi = {10.1088/0741-3335/57/11/113001}, + number = {11}, + journal = {Plasma Physics and Controlled Fusion}, + publisher = {IOP Publishing}, + author = {Arber, T D and Bennett, K and Brady, C S and Lawrence-Douglas, A and Ramsay, M G and Sircombe, N J and Gillies, P and Evans, R G and Schmitz, H and Bell, A R and Ridgers, C P}, + year = {2015}, + month = Sept, + pages = {113001} +} +@article{hoyer:2017, + title = {xarray: {N-D} labeled arrays and datasets in {Python}}, + author = {Hoyer, S. and J. Hamman}, + journal = {Journal of Open Research Software}, + volume = {5}, + number = {1}, + year = {2017}, + publisher = {Ubiquity Press}, + doi = {10.5334/jors.148}, + url = {https://doi.org/10.5334/jors.148} +} +@article{granger:2021, + author = {Granger, Brian E. and Pérez, Fernando}, + journal = {Computing in Science & Engineering}, + title = {Jupyter: Thinking and Storytelling With Code and Data}, + year = {2021}, + volume = {23}, + number = {2}, + pages = {7--14}, + keywords = {Open source software;Scientific computing;Machine learning;Data science;Open source software;Meteorology}, + doi = {10.1109/MCSE.2021.3059263} +} +@article{hunter:2007, + author = {Hunter, J. D.}, + title = {Matplotlib: A 2D graphics environment}, + journal = {Computing in Science \& Engineering}, + volume = {9}, + number = {3}, + pages = {90--95}, + publisher = {IEEE COMPUTER SOC}, + doi = {10.1109/MCSE.2007.55}, + year = 2007 +} +@article{behnel:2010, + author = {Behnel, S. and Bradshaw, R. and Citro, C. and Dalcin, L. and Seljebotn, D.S. and Smith, K.}, + journal = {Computing in Science Engineering}, + title = {Cython: The Best of Both Worlds}, + year = {2011}, + month = march-april, + volume = {13}, + number = {2}, + pages = {31 -39}, + keywords = {Cython language;Fortran code;Python language extension;numerical loops;programming language;C language;numerical analysis;}, + doi = {10.1109/MCSE.2010.118}, + issn = {1521-9615} +} +@article{harris:2020, + title = {Array programming with {NumPy}}, + author = {Charles R. Harris and K. Jarrod Millman and St{\'{e}}fan J. + van der Walt and Ralf Gommers and Pauli Virtanen and David + Cournapeau and Eric Wieser and Julian Taylor and Sebastian + Berg and Nathaniel J. Smith and Robert Kern and Matti Picus + and Stephan Hoyer and Marten H. van Kerkwijk and Matthew + Brett and Allan Haldane and Jaime Fern{\'{a}}ndez del + R{\'{i}}o and Mark Wiebe and Pearu Peterson and Pierre + G{\'{e}}rard-Marchant and Kevin Sheppard and Tyler Reddy and + Warren Weckesser and Hameer Abbasi and Christoph Gohlke and + Travis E. Oliphant}, + year = {2020}, + month = sep, + journal = {Nature}, + volume = {585}, + number = {7825}, + pages = {357--362}, + doi = {10.1038/s41586-020-2649-2}, + publisher = {Springer Science and Business Media {LLC}}, + url = {https://doi.org/10.1038/s41586-020-2649-2} +} +@inproceedings{mckinney:2010, + author = { {W}es {M}c{K}inney }, + title = { {D}ata {S}tructures for {S}tatistical {C}omputing in {P}ython }, + booktitle = { {P}roceedings of the 9th {P}ython in {S}cience {C}onference }, + pages = { 56 - 61 }, + year = { 2010 }, + editor = { {S}t\'efan van der {W}alt and {J}arrod {M}illman }, + doi = { 10.25080/Majora-92bf1922-00a } +} +@misc{rew:1989, + doi = {10.5065/D6H70CW6}, + url = {http://www.unidata.ucar.edu/software/netcdf/}, + author = {Rew, Russ and Davis, Glenn and Emmerson, Steve and Cormack, Cathy and Caron, John and Pincus, Robert and Hartnett, Ed and Heimbigner, Dennis and Appel, Lynton and Fisher, Ward}, + keywords = {Software, Free computer software, Open source software, Systems software, Application program interfaces, Metadata}, + language = {en}, + title = {Unidata NetCDF}, + publisher = {NSF Unidata}, + year = {1989}, + copyright = {UCAR/Unidata Open Source Software License} +} +@inproceedings{schreiner:2024, + author = {Schreiner, III, Henry and Fillion-Robin, Jean-Christophe and McCormick, Matt}, + doi = {10.25080/FMKR8387}, + month = jul, + series = {SciPy 2024}, + title = {{Scikit-build-core}}, + year = {2024} +} +@misc{pypa:2020, + author = {{cibuildwheel Developers}}, + title = {{cibuildwheel}}, + year = {2020}, + publisher = {{Python Packaging Authority}}, + journal = {GitHub repository}, + url = {https://github.com/pypa/cibuildwheel} +} +@software{hill:2024a, + author = {Hill, Peter and Adams, Joel L.}, + month = apr, + doi = {10.5281/zenodo.15586339}, + title = {{epydeck: EPOCH input file reader/writer}}, + url = {https://github.com/epochpic/epydeck}, + year = {2024} +} +@software{hill:2024b, + author = {Hill, Peter and Adams, Joel L. and Dior, Lawrence}, + month = may, + doi = {doi.org/10.5281/zenodo.17106949}, + title = {{epyscan: Generate EPOCH campaigns over a parameter space using different sampling methods}}, + url = {https://github.com/epochpic/epyscan}, + year = {2024} +} +@inproceedings{matthew:2015, + author = { Matthew Rocklin }, + title = { Dask: Parallel Computation with Blocked algorithms and Task Scheduling }, + booktitle = { Proceedings of the 14th Python in Science Conference }, + pages = { 130 - 136 }, + year = { 2015 }, + editor = { Kathryn Huff and James Bergstra }, + url = {https://www.dask.org} +} +@misc{childs:2012, + author = {Childs, Hank and Brugger, Eric and Whitlock, Brad and Meredith, Jeremy and Ahern, Sean and Pugmire, David and Biagas, Kathleen and Miller, Mark C. and Harrison, Cyrus and Weber, Gunther H. and Krishnan, Hari and Fogal, Thomas and Sanderson, Allen and Garth, Christoph and Bethel, E. Wes and Camp, David and Rubel, Oliver and Durant, Marc and Favre, Jean M. and Navratil, Paul}, + doi = {10.1201/b12985}, + title = {{High Performance Visualization--Enabling Extreme-Scale Scientific Insight}}, + url = {https://visit.llnl.gov}, + year = {2012} +} +@software{matlab:2022, + year = {2022}, + author = {The MathWorks Inc.}, + title = {MATLAB version: 9.13.0 (R2022b)}, + publisher = {The MathWorks Inc.}, + address = {Natick, Massachusetts, United States}, + url = {https://www.mathworks.com} +} +@misc{huebl:2015, + author = {Huebl, Axel and Lehe, R{\'e}mi and Vay, Jean-Luc and Grote, David P. and Sbalzarini, Ivo and Kuschel, Stephan and Sagan, David and P{\'e}rez, Fr{\'e}d{\'e}ric and Koller, Fabian and Bussmann, Michael}, + title = {{openPMD: A meta data standard for particle and mesh based data}}, + year = 2015, + publisher = {Zenodo}, + doi = {10.5281/zenodo.591699}, + url = {https://www.openPMD.org}, + howpublished = {https://github.com/openPMD} +} +@misc{bennett:2014a, + author = {Bennett, keith}, + title = {{SDF}}, + year = {2014}, + publisher = {Warwick Plasma Group}, + journal = {GitHub repository}, + url = {https://github.com/epochpic/SDF} +} +@misc{bennett:2014b, + author = {Bennett, keith and Wood, Andrew}, + title = {{SDF_C}}, + year = {2014}, + publisher = {Warwick Plasma Group}, + journal = {GitHub repository}, + url = {https://github.com/epochpic/SDF_C} +} +@misc{grecco:2012, + author = {{Pint Developers}}, + title = {{Pint}}, + year = {2012}, + publisher = {Hernan Grecco}, + journal = {GitHub repository}, + url = {https://github.com/hgrecco/pint} +} +@misc{magin:2020, + author = {Magin, Justus and Nicholas, Tom and Pflüger, Mika and Thielen, JT and Keys, Simon and Patel, Bhavin and van der Marel, Martijn and Bovy, Benoit}, + title = {{pint-xarray}}, + year = {2020}, + publisher = {Xarray Contributions}, + journal = {GitHub repository}, + url = {https://github.com/xarray-contrib/pint-xarray} +} \ No newline at end of file diff --git a/joss/paper.md b/joss/paper.md new file mode 100644 index 0000000..8a56c0e --- /dev/null +++ b/joss/paper.md @@ -0,0 +1,156 @@ +--- +title: 'sdf-xarray: Interactive data reading and visualisation for EPOCH particle-in-cell simulations' +tags: + - Python + - xarray + - EPOCH + - PIC + - interactivity + - data +authors: + - name: Joel Luca Adams + orcid: 0009-0005-4889-5231 + equal-contrib: true + corresponding: true + affiliation: 1 + - name: Peter Hill + orcid: 0000-0003-3092-1858 + equal-contrib: true + affiliation: 1 + - name: Liam Pattinson + orcid: 0000-0001-8604-6904 + affiliation: 1 + - name: Shaun Doherty + orcid: 0009-0005-0693-030X + affiliation: 1 + - name: Sviatoslav Shekhanov + orcid: 0000-0002-2125-8962 + affiliation: 1 + - name: Chris Herdman + orcid: 0000-0002-5159-0130 + affiliation: 1 + - name: Lawrence Dior + orcid: 0000-0003-4138-4576 + affiliation: 1 +affiliations: + - name: York Plasma Institute, University of York, United Kingdom + index: 1 + ror: 04m01e293 +date: 3 June 2026 +bibliography: paper.bib +--- + +# Summary + +EPOCH is a plasma physics code that outputs files using a custom binary format called SDF. `sdf-xarray` is a Python package which reads these files and parses them into structured N-D labelled datasets provided by `Xarray`. `sdf-xarray` has built-in functions for plotting, animation and unit conversion, allowing for simple interaction and manipulation for easy data analysis. + +# Statement of need + +EPOCH [@arber:2015], developed at the University of Warwick, is a Fortran-based Particle-In-Cell (PIC) code widely used in laser-plasma physics, primarily within the United Kingdom. The code was first developed at the University of Warwick in early 2008, prior to the standardisation of many modern output file formats and as a result uses the university's custom Self-Describing Format (SDF) binary files. + +Integrating a modern output module like `NetCDF` [@rew:1989] is challenging because the codebase accommodates one-, two-, and three-dimensional simulations across three variants: a regular grid geometry, a cylindrical geometry and a hybrid approximation. Multiple copies of the variants exist and are stored in forks and branches containing diverging commit histories making development of a unified module challenging. + +# State of field + +Several SDF wrappers [@bennett:2014a] for use in visualisation software have been created, including but not limited to: `VisIt` [@childs:2012]; `Matlab` [@matlab:2022] which is closed-source and requires a paid license; `OpenPMD` [@huebl:2015] which is not currently widely adopted; and a Python package called `sdf_helper`. + +Python and `Matplotlib` [@hunter:2007] are standard tools for plasma physics simulation analysis. The `sdf_helper` Python package supports this workflow by providing simple data extraction to `NumPy` [@harris:2020] and custom `Matplotlib` plotting routines. Unfortunately, it lacks native capabilities for concatenating multiple SDF files into time-resolved datasets. + +Developed as a modern successor to `sdf_helper`, `sdf-xarray` converts SDF files to `Xarray` [@hoyer:2017] datasets, enabling users to leverage core ecosystem features, including: + +- **Lazy data loading via `Dask` [@matthew:2015]:** Instantiates pointers rather than loading the entire dataset into memory, substantially mitigating RAM constraints when handling large-scale files on the order of 10-100 GB. +- **Deferred computation:** Leverages `Dask` to postpone execution on data arrays until explicitly required or evaluated. +- **Ecosystem interoperability:** Simplifies conversion of dataset arrays to `NumPy` or `Pandas` [@mckinney:2010] structures for downstream analysis. +- **Interactive development:** Provides native compatibility with `Jupyter` notebooks [@granger:2021] to support iterative workflows. +- **Built-in visualisation:** Contains integrated `Matplotlib` routines for rapid, native plotting of multidimensional data. +- **Multi-file aggregation:** Facilitates the simultaneous opening and unified handling of split-file datasets. + +In addition to leveraging `Xarray`'s core ecosystem, `sdf-xarray` has comprehensive [documentation](https://sdf-xarray.readthedocs.io/en/stable) which is actively maintained and makes use of `Jupyter` notebooks to illustrate the interactive nature of the package. + +Both `sdf_helper` and `sdf-xarray` serve as wrappers around the C-based `SDF-C` library [@bennett:2014b]. Although the underlying library historically required manual compilation via a Makefile, `sdf-xarray` utilises the modern `Scikit-build-core` [@schreiner:2024] backend to automate this process for local builds. Furthermore, during the release cycle, `sdf-xarray` leverages `cibuildwheel` [@pypa:2020] to distribute pre-compiled binaries across Windows, MacOS, and various Linux distributions. Originally, `sdf_helper`, released back in 2014, did not have this automated build process, making installation less consistent across operating systems. It also did not respect isolated Python virtual environments, causing some users to face system-wide dependency conflicts. Several months after the release of `sdf-xarray` in 2024, `sdf_helper` was updated to adopt a similar automated pipeline and published on PyPI as `sdfr` to alleviate these deployment challenges. + +# Software design + +This package's design can be separated into three key sections: loading, plotting and rescaling. + +## Loading SDF files + +Data ingestion follows a three-stage pipeline: raw binary reading via the underlying `SDF-C` library, decoding into Python dataclasses using `Cython` [@behnel:2010], and parsing into a custom `Xarray` backend. During this process, the backend sanitises variable names to Pythonic `snake_case`, attaches simulation input data using `epydeck` [@hill:2024a], and omits heavy particle data by default to conserve memory. + +For multi-file datasets, `sdf-xarray` aggregates data along a time dimension derived from each file's `time` attribute. However, this multi-file ingestion highlights a core limitation: `Xarray` strictly requires fixed grids, whereas EPOCH can output variable-grid data that changes with each step. Because these time-varying grids cannot natively coexist in a standard `Xarray` structure, `sdf-xarray` introduces a `separate_times` flag. This creates distinct time dimensions for variables with differing output frequencies, ensuring structural compatibility at the expense of a brief pre-evaluation RAM overhead. + +## Rescaling datasets + +EPOCH outputs all data in base International System (SI) units. It can be convenient to convert these units when handling coordinate data; to that end a custom function was developed and an example can be found below: + +```python +# Convert the time to femtoseconds +ds = ds.epoch.rescale_coords(1e15, "fs", "time") +# Convert the x and y coords to microns +ds = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid", "Y_Grid_mid"]) +``` + +This is often used in conjunction with generating plots and animations. + +Converting variables can also be done by either directly manipulating the underlying `NumPy` array or using the `Pint` [@grecco:2012] package along with the `Xarray` API `pint-xarray` [@magin:2020]. + +## Plotting + +`Xarray` provides a simple interface for plotting variables using `Matplotlib`. An example of which is: + +```python +import sdf_xarray as sdfxr + +# Opens a single-file dataset +ds = sdfxr.open_dataset("0010.sdf") +ds = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid", "Y_Grid_mid"]) + +ds["Electric_Field_Ey"].epoch.plot() +``` + +![Plot of the electric field of a laser focusing in a vacuum, generated through `sdf-xarray`.](Electric_Field_Ey.png){width="60%"} + +### Animations + +Building animations provides insight into how systems evolve over time. While `Matplotlib` allows users to build animations, it can require quite a complicated setup in order to work with `Xarray`. As a result, `sdf-xarray` contains a custom function that builds on top of `Matplotlib`'s implementation and provides a simple user interface. An example animation of a dataset variable evolving over time is as follows: + +```python +import sdf_xarray as sdfxr + +# Opens a multi-file dataset using globs or a list of paths +ds = sdfxr.open_mfdataset("*.sdf") +ds = ds.epoch.rescale_coords(1e15, "fs", "time") +ds = ds.epoch.rescale_coords(1e6, "µm", ["X_Grid_mid", "Y_Grid_mid"]) + +anim = ds["Electric_Field_Ey"].epoch.animate() +``` + +![Frames from an animation of the electric field of a laser focusing in a vacuum, generated through `sdf-xarray`.](Electric_Field_Ey_frames.png) + +The minimum and maximum values of a variable can change between each SDF file, so when generating an animation the limits must be found over the whole dataset. This behaviour exists by default in `sdf-xarray` animations. + +EPOCH provides a ``moving window" feature which shifts the axes as a simulation progresses (e.g. to track a laser pulse propagating through a long block of plasma). This saves computational resources by simulating only the most dynamic region of the domain rather than its full extent. `sdf-xarray` animations allow the user to specify a boolean value for this `moving_window` functionality to follow the simulation box instead of maintaining fixed axes. + +On top of building single variable animations, users can utilise the `animate_multiple()` function to overlay multiple variables. + +Further examples and explanation of animations can be seen in the [documentation](https://sdf-xarray.readthedocs.io/en/stable/animation.html). + +# Research impact statement + +This library was originally developed for the machine learning pipeline toolkit [BEAM](https://github.com/epochpic/sdf-xarray#broad-epoch-analysis-modules-beam). The initial software architecture was designed by Peter Hill, with subsequent maintenance, optimization, and feature iteration led by Joel Adams and community contributors. Since its inception, the library has been widely adopted by EPOCH users at the York Plasma Institute, fostering a collaborative development environment where active researchers contribute new features to the codebase. + +Beyond its foundational deployment at the University of York, `sdf-xarray` has achieved broader institutional adoption, seeing active use by researchers at the University of Strathclyde, Queen's University Belfast and First Light Fusion. In addition to driving primary research, the library serves a distinct educational role; it has been integrated into a course on PIC codes delivered to undergraduate students at the University of York and served as the primary data-interfacing utility for both the 2025 and 2026 international EPOCH user workshops. + +By adhering strictly to FAIR (Findable, Accessible, Interoperable, and Reusable) data principles, `sdf-xarray` alongside its sister BEAM packages, `epyscan` and `epydeck` [@hill:2024b] maintains a transparent, community-driven development model that encourages external contributions. In recognition of this commitment to open science and its measurable impact on the plasma physics community, the BEAM project was awarded the 2025 University of York Open Research Award in the Postgraduate Researcher (Sciences) category. + +# AI usage disclosure + +Some of the code and documentation in this library was partially developed with assistance from the GPT models by Google's Gemini, Anthropic's Claude and OpenAI's ChatGPT. The majority of the codebase was developed by humans and all generated code was verified by several developers prior to release. + +# Acknowledgements + +This project's initial development was funded by the PlasmaFAIR project, EPSRC Grant EP/V051822/1 + +We acknowledge the University of Warwick EPOCH development team for their support of the project and indirect contributions to this project by several anonymous contributors. + +# References