Skip to content

Commit 40744f4

Browse files
Merge pull request #19 from electronsandstuff/pierce/readme
Updates to README
2 parents 219ea9d + e2c8608 commit 40744f4

4 files changed

Lines changed: 201 additions & 33 deletions

File tree

.github/workflows/build_and_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
python-version: 3.12
2626
miniforge-version: latest
2727
environment-file: python/environment.yml
28-
activate-environment: openpmd-beamphysics-c-dev
28+
activate-environment: parcel-dev
2929

3030
- name: Verify conda environment
3131
shell: bash -el {0}

README.md

Lines changed: 199 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
# Parcel
1+
<img src="assets/parcel_openpmd_logo.png" alt="Parcel logo showing a cardboard box holding a beam of electrons" width="600" >
22

3-
**Par**cel: a package of **par**ticle and mesh data.
4-
A single header C implementation of the [OpenPMD standard](https://github.com/openPMD/openPMD-standard) with the BeamPhysics extension.
3+
----
54

5+
Read and write particle-tracking code and particle-in-cell code data in the [OpenPMD standard](https://github.com/openPMD/openPMD-standard).
6+
7+
Parcel is a single header C implementation of the OpenPMD standard with the BeamPhysics extension using [HDF5](https://www.hdfgroup.org/solutions/hdf5/) as its backend.
8+
It is designed for simple integration with existing physics simulation codes.
9+
Drop the single file `parcel.h` into your codebase and call it to allow your tool to interchange particle data with the many other codes that use the OpenPMD format.
10+
11+
12+
## Dependencies
13+
14+
Parcel is C99 compliant and its only dependency is HDF5.
615

716
## Usage
817

@@ -14,7 +23,193 @@ Then, in one and only one of your `.c` files, use the following line to include
1423
#include "../parcel.h"
1524
```
1625

17-
## Implementation Notes
26+
The following examples illustrate typical use-cases of reading and writing data to an OpenPMD file.
27+
28+
### Basic Read Example
29+
30+
```c
31+
#define PARCEL_IMPLEMENTATION
32+
#include "parcel.h"
33+
34+
int main(void) {
35+
pmd_series *series;
36+
pmd_iteration *iter;
37+
ParticleGroup *pg;
38+
pmd_status status;
39+
int64_t *iterations = NULL;
40+
int64_t num_iterations;
41+
42+
/* Open an OpenPMD data series */
43+
/* Use '%T' in the filename for a file-based series and parcel will autodetect */
44+
/* files of the form data_1.h5, data_2.h5, data_3.h5, etc. You may also use */
45+
/* the path of a single file in the file-based series, or the path to a */
46+
/* group-based series. */
47+
status = pmd_open_series("data_%T.h5", &series, PMD_RDONLY);
48+
if (status != PMD_SUCCESS) return 1;
49+
50+
/* List available iteration indices */
51+
status = pmd_list_iterations(series, &iterations, &num_iterations);
52+
if (status != PMD_SUCCESS) return 1;
53+
54+
printf("Found %lld iterations\n", (long long)num_iterations);
55+
for (int64_t i = 0; i < num_iterations; i++) {
56+
printf(" Iteration: %lld\n", (long long)iterations[i]);
57+
}
58+
59+
/* Open the first iteration */
60+
status = pmd_open_iteration(series, iterations[0], &iter);
61+
if (status != PMD_SUCCESS) return 1;
62+
63+
/* List available particle species */
64+
char **species_names = NULL;
65+
int num_species = 0;
66+
status = pmd_list_species(iter, &species_names, &num_species);
67+
if (status != PMD_SUCCESS) return 1;
68+
69+
printf("Found %d species:\n", num_species);
70+
for (int i = 0; i < num_species; i++) {
71+
int64_t particle_count;
72+
pmd_get_num_particles(iter, species_names[i], &particle_count);
73+
printf(" %s: %lld particles\n", species_names[i], (long long)particle_count);
74+
}
75+
76+
/* Allocate and read electron particle data */
77+
/* If integrating with existing physics code, you can set each pointer */
78+
/* in pmd_particle_group to an array inside an existing beam struct/class */
79+
/* to read data directly into it without having to copy. Set any pointer */
80+
/* to NULL to ignore reading. */
81+
status = pmd_allocate_particle_group(iter, "electron", &pg);
82+
if (status != PMD_SUCCESS) return 1;
83+
84+
status = pmd_read_particle_group(iter, "electron", pg);
85+
if (status != PMD_SUCCESS) return 1;
86+
87+
/* Use particle data - positions are in pg->x, pg->y, pg->z */
88+
printf("Read %lld particles\n", (long long)pg->num_particles);
89+
for (int64_t i = 0; i < pg->num_particles && i < 5; i++) {
90+
printf(" Particle %lld: x=%.3e, px=%.3e eV/c\n",
91+
(long long)i, pg->x[i], pg->px[i]);
92+
}
93+
94+
/* Clean up */
95+
for (int i = 0; i < num_species; i++) {
96+
free(species_names[i]);
97+
}
98+
free(species_names);
99+
free(iterations);
100+
pmd_free_particle_group(pg);
101+
pmd_close_iteration(iter);
102+
pmd_close_series(series);
103+
return 0;
104+
}
105+
```
106+
107+
### Basic Write Example
108+
109+
```c
110+
#define PARCEL_IMPLEMENTATION
111+
#include "parcel.h"
112+
113+
int main(void) {
114+
pmd_series *series;
115+
pmd_iteration *iter;
116+
ParticleGroup pg = {0};
117+
pmd_status status;
118+
const int64_t N = 1000;
119+
120+
/* Create a new OpenPMD data series */
121+
/* Using '%T' in filename will open series in file-based iteration mode */
122+
/* and embed iteration index in filename. Ommit '%T' in name to open in */
123+
/* group-based iteration mode and store iterations within HDF5 file */
124+
status = pmd_open_series("output_%T.h5", &series, PMD_TRUNC);
125+
if (status != PMD_SUCCESS) return 1;
126+
127+
/* Create and open iteration 0 for writing */
128+
status = pmd_open_iteration(series, 0, &iter);
129+
if (status != PMD_SUCCESS) return 1;
130+
131+
/* Allocate and populate particle data */
132+
pg.num_particles = N;
133+
pg.x = (double *)malloc(N * sizeof(double));
134+
pg.y = (double *)malloc(N * sizeof(double));
135+
pg.z = (double *)malloc(N * sizeof(double));
136+
pg.px = (double *)malloc(N * sizeof(double));
137+
pg.py = (double *)malloc(N * sizeof(double));
138+
pg.pz = (double *)malloc(N * sizeof(double));
139+
pg.weight = (double *)malloc(N * sizeof(double));
140+
141+
/* Fill with sample data */
142+
for (int64_t i = 0; i < N; i++) {
143+
pg.x[i] = 0.001 * i;
144+
pg.y[i] = 0.0;
145+
pg.z[i] = 0.0;
146+
pg.px[i] = 0.0; /* momentum in eV/c */
147+
pg.py[i] = 0.0;
148+
pg.pz[i] = 1e6;
149+
pg.weight[i] = 1.0;
150+
}
151+
152+
/* Write particle data */
153+
/* Instead of allocating arrays in pmd_particle_group, you may set pointers to */
154+
/* user-supplied arrays in existing code to write directly from them. Set any */
155+
/* pointer to NULL to ignore writing. */
156+
status = pmd_write_particle_group(iter, "electron", &pg);
157+
if (status != PMD_SUCCESS) return 1;
158+
159+
/* Clean up */
160+
free(pg.x); free(pg.y); free(pg.z);
161+
free(pg.px); free(pg.py); free(pg.pz);
162+
free(pg.weight);
163+
pmd_close_iteration(iter);
164+
pmd_close_series(series);
165+
return 0;
166+
}
167+
```
168+
169+
## Compiling and Running Tests
170+
171+
The tests in this project are built using CMake and have additional dependencies beyond HDF5 in order to generate test files.
172+
Follow these steps to build the tests and then run them.
173+
1) Install required dependencies through conda environment and activate. This will download and install HDF5 and the required python dependencies.
174+
```
175+
conda env create -f environment.yml
176+
conda activate parcel-dev
177+
```
178+
2) Use CMake to build the tests.
179+
```
180+
mkdir build
181+
cd build
182+
cmake ..
183+
make
184+
```
185+
3) Run the tests. This should run all C tests and check generated test files with the [OpenPMD Validator](https://github.com/openPMD/openPMD-validator).
186+
```
187+
# From inside of build/
188+
ctest
189+
```
190+
4) [Optional] If any errors occur, running the individual test binaries may reveal more information.
191+
```
192+
./tests/test_read
193+
./tests/test_write
194+
./tests/test_read_write
195+
./tests/test_utilitis
196+
./tests/test_generate_openpmd
197+
```
198+
199+
### Optional CMake Arguments
200+
The following options may be used in the CMake command to enable additional debug features.
201+
- `-DDEBUG=ON`: Enable debug flags (ie for use with valgrind).
202+
- `-DCLANG_TIDY=ON`: Run `clang-tidy` during builds and error out on issues.
203+
204+
205+
## Name
206+
207+
**Par**cel: a package containing **par**ticle data.
208+
209+
## License
210+
This code was written by Christopher M. Pierce and is released under the BSD 3-Clause License.
211+
212+
## Internal Implementation Notes
18213

19214
### File Handle Management
20215

@@ -37,30 +232,3 @@ pmd_open_iteration(series, index, &iter);
37232
// ... use iter->file_id ...
38233
pmd_close_iteration(iter);
39234
```
40-
41-
### Cache Invalidation
42-
43-
The library caches certain values for performance:
44-
45-
1. **Iteration list cache** (`series->num_iterations` and `series->iteration_indices`): Stores the list of available iterations
46-
2. **Series metadata cache** (`series->_author`, `series->_software`, etc.): Stores metadata attributes in the series handle
47-
48-
These caches must be invalidated when the underlying data changes:
49-
50-
- When creating a new iteration with `pmd_open_iteration()`, the iteration list cache must be invalidated:
51-
```c
52-
/* Invalidate iteration cache since we just created a new iteration */
53-
series->num_iterations = -1;
54-
free(series->iteration_indices);
55-
series->iteration_indices = NULL;
56-
```
57-
58-
- When setting series metadata (e.g., `pmd_set_author()`), the new value must be:
59-
1. Stored in the series handle cache
60-
2. Written to all existing iteration files for FILE_BASED series
61-
3. Written to the series file for GROUP_BASED series
62-
63-
Failing to invalidate caches can cause stale data to be returned and metadata updates to not propagate to all files.
64-
65-
## License
66-
This code was written by Christopher M. Pierce and is released under the BSD 3-Clause License.

assets/parcel_openpmd_logo.png

56.8 KB
Loading

python/environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: openpmd-beamphysics-c-dev
1+
name: parcel-dev
22
channels:
33
- conda-forge
44
dependencies:

0 commit comments

Comments
 (0)