Share and stream scientific datasets
Write Parquet datasets, share accepted files between jobs and choose between partial reads and full downloads.
The shared Python runtime, a saved CLI connection and a worker with the current storage SDK. A published MD trajectory for the window-reading example.
Scientific workflows produce datasets too large to store in database rows or pass around as JSON. In chemistry.tools, you write structured data to Parquet artifacts, pass lightweight references between jobs, and let workers or web clients stream or download only what they need.
In this guide, you will:
- Write and summarize a Parquet dataset in a two-stage pipeline.
- Share output files between jobs without re-uploading them.
- Choose between streaming partial windows and downloading whole files.
1. Use Parquet for structured data
Choose Parquet by default for measurement tables, trajectories, and per-item benchmark results:
Use chemistry_compute.parquet to read and write files. It handles encryption and internal Zstd compression automatically.
Note: Never wrap
.parquetfiles in.gzor.zst. Parquet compresses individual column chunks internally. Compressing the outside of the file destroys seekability and prevents byte-range streaming.
Keep full scientific fidelity in your columns. Parquet's column-level compression makes datasets compact without needing to truncate precision, drop coordinates, or reduce sampling rates.
2. Run a file-producing pipeline
The example in compute/platform/examples/files/ demonstrates file handling using two jobs:
- Producer: Generates four synthetic measurements and saves them to
results/measurements.parquet. - Consumer: Reads that accepted artifact from the producer and writes
results/summary.json.
Use a saved CLI connection and a CPU worker running the shared environment. From the repository root, run the pipeline:
If you are using the local development stack, add --config .local-dev/compute/client.json.
The script prints the job IDs and the calculated summary:
The consumer downloads summary.json under file-output/SUMMARY_JOB_UUID/results/. The original dataset remains attached to the producer job; the consumer does not upload a duplicate copy. To inspect or download that original file:
If a job fails, inspect its output with ct-cli logs JOB_UUID. If only the consumer stage fails, retain the producer's ID and submit a new consumer that depends on it. See run recovery for handling interrupted runs.
3. Write bounded batches and share the accepted file
Here are the producer and consumer functions from compute/platform/examples/files/jobs.py:
Key patterns in the code:
- Writing in batches:
job_context().output("measurements.parquet")creates a path underresults/. For large datasets, useparquet.writer(...)and write bounded batches rather than accumulating every row in memory. - Finishing the file: Exiting the
withblock closes the writer and flushes the Parquet footer. Always close writers before your job returns. - Reading upstream artifacts:
one_result(request, Dataset, artifacts=(...))validates that the parent job succeeded and locates its staged Parquet file on the worker.
Connecting stages in the submission script:
Setting depends_on=[produced.id] ensures the consumer only runs after the producer finishes with accepted outputs.
Because the jobs are linked, the consuming worker stages the parent artifact directly from storage into its working directory. Your local machine never has to download and re-upload the intermediate file just to pass it onward.
For pipelines with multiple producers, use results(request, Dataset) to iterate over a Collection, or use a Flow's .collect(...) reducer (see Build a scientific pipeline).
4. Stage existing inputs and collect outputs
To pass a local file from your machine into a remote job, declare it as an input:
Inside analysis.py, access the local staged path with job_context().input("measurements.parquet"):
Only explicitly declared inputs are uploaded; surrounding directory files are not included automatically.
Collecting outputs
Any file your script writes inside results/ is automatically uploaded and verified when the job finishes. Use ct-cli fetch or Python's job.fetch() to download them:
Downloads are verified against stored digests and replace destination files atomically.
5. Choose partial reads or a full download
The platform provides two distinct ways to access files:
Iterating in batches keeps memory usage low while processing data locally—it does not mean making hundreds of separate network calls. For bulk analysis across an entire run, download the file once and stream through it locally.
Inspect a trajectory window
For published, artifact-backed MD trajectories, inspect specific simulation steps without downloading the full dataset:
The reader fetches only the necessary column chunks and footer ranges directly from storage. Use next_step from the response to page through subsequent windows (None indicates the end of the trajectory).
In browser workspaces, the 3D viewer uses this exact streaming mechanism inside a Web Worker to scrub through trajectories smoothly without downloading gigabytes of coordinates. Note that streaming reads completed, accepted artifacts—it cannot tail an active simulation while it is still running.
Download once for whole-file processing
For bulk analysis or training where you need to examine every row or frame, download the complete artifact:
api.fetch(...) streams the file directly to disk and verifies its integrity. If the artifact was encrypted with native Parquet encryption, supply resolve_key=api.read_key when opening it. Inside a worker job, helper utilities resolve keys automatically.
6. Let the platform handle storage
When you use the shared chemistry_compute clients, the platform manages storage transparently:
- Direct transfers: Artifact bytes travel directly between workers or clients and S3/R2 storage via presigned URLs. Large file transfers never bottleneck through the central backend server.
- End-to-end encryption: Artifacts are encrypted before storage. Authorized clients resolve access keys on demand through the API; keys are never stored in plain URLs.
- Transparent chunking: Large files (over ~240 MiB) are split into bounded storage objects for reliable caching and parallel transfer, but they behave as a single logical file with continuous offsets.