Results I/O (binary, VTK, PNG)¶
OpenPFC separates the kernel interface ResultsWriter from frontend implementations under include/openpfc/frontend/io/. How you attach writers depends on whether you use the JSON-driven App path or a custom main.
ResultsWriter (kernel)¶
include/openpfc/kernel/simulation/results_writer.hpp — abstract writer that sessions call from the on_save hook of pfc::sim::run when pfc::time::do_save is true. Implementations live in the frontend (binary/VTK) or in your app.
Session wiring¶
Sessions build named writers from JSON fields[] with
pfc::ui::parse_result_writers_from_json (below), call
pfc::apply_writer_domain(writer, field) so global size / local box come from
the Field, and write each field on the on_save hook while bumping their own
result counter. Unit tests can drive a writer directly with a small Field and
a mock writer; no simulator object is needed.
Binary output (MPI-IO)¶
include/openpfc/frontend/io/binary_writer.hpp — BinaryWriter: raw binary, collective MPI-IO. Documented caveats: all ranks in the communicator must participate consistently in write() to avoid deadlock. Both BinaryWriter::set_domain and kernel BinaryReader::set_domain fail closed on invalid geometry (non-positive extents, negative offsets, or a piece outside the global box); see binary_field_io_spec.md.
Format (layout, filename printf pattern, collectives): binary_field_io_spec.md.
JSON-driven App path¶
simulation_wiring_writers.hpp parse_result_writers_from_json takes a ResultsWriterCatalog at the call site (e.g. default_results_writer_catalog() for built-in binary). It returns named BinaryWriters (or catalog vtk/hdf5): for each fields[] entry it uses field["data"] as the path template. $NAME and ${NAME} environment references are expanded when the writer is created (unset or empty variables are a hard error); increment templating (%04d) runs afterwards. Sessions attach those writers on the on_save hook.
Requirements in settings: saveat > 0, fields array with name and data.
VTK (ParaView / VisIt)¶
include/openpfc/frontend/io/vtk_writer.hpp — VTKWriter: .vti / .pvti output. Extent/origin/spacing and local point-count checks are implemented in vtk_writer_validate.hpp (pfc::io::vtk_validate), separate from XML and file I/O. For programmatic output, construct VTKWriter, set_domain, set_origin, set_spacing, then add_results_writer or call from your step loop. See examples/output/vtk-series/model.cpp and Doxygen on VTKWriter.
PNG (2D grayscale, quick look)¶
include/openpfc/frontend/io/png_writer.hpp — pfc::io::write_mpi_scalar_field_png_xy: gathers a single z-slab (nz == 1 globally) to rank 0 and writes an 8-bit grayscale PNG. Used for lightweight visualization (e.g. Allen–Cahn apps/allen_cahn), not the main spectral App JSON pipeline.
Collective contract: All ranks in the communicator must participate consistently. Buffer size validation is fail-closed (communicator-wide agreement via MPI_Allreduce) before the MPI_Allgather/MPI_Gatherv calls, preventing deadlocks when a rank provides an incorrectly sized local_field.
Choosing a path¶
Goal |
Mechanism |
|---|---|
Field dumps and post-processing |
|
Durable accepted-state restart bundles |
Checkpoint publication, with metadata and accepted field bricks. |
Interactive visualization |
Set |
Quick 2D snapshot |
|
Crystal front and orientation¶
pfc::observables::planar_front_position reduces the root-mean-square of
a periodic density along the two directions transverse to a chosen axis.
The position is the linear crossing of a caller-supplied level, so the
reported front can move by less than one grid spacing. Velocity is not a
spatial derivative and does not unwrap the box: it is
(x(t1) - x(t0)) / (t1 - t0) for two samples whose validity is
FrontValidity::valid.
region_orientation estimates one real wave A cos(k · x + φ) from
order-2 central differences and folds k ~ -k so the first nonzero
component is positive. four_fold and six_fold then rotate about +z
into [0, π/2) and [0, π/3). cubic keeps the proper cubic image of
k or -k with the greatest z, then y, then x. label_grains votes
owned cells for the nearest supplied candidate under that representative.
pfc::io::record_crystal_diagnostics writes those values on the normal
diagnostics-series path. The columns are front_position,
front_velocity, front_valid, wave_x, wave_y, wave_z,
orientation_angle, orientation_valid, voting_cells and one
grain_i fraction per candidate. The tungsten and aluminum test drivers
call this function. Neither application keeps a copy of the estimator.
The host implementation does not read device memory.
See also¶
binary_field_io_spec.md— normative binary field file descriptionpostprocess_binary_fields.md— offline analysis of raw binary fieldsapp_pipeline.md— whereparse_result_writers_from_jsonrunsconfiguration.md— config file overviewtutorials/end_to_end_visualization.md— run once, inspect binary or PNG outputtutorials/vtk_paraview_workflow.md—11_write_results/12_cahn_hilliard+ ParaViewlearning_paths.md— documentation tracks by role
Compact selections from resident arrays¶
pfc::core::gather in
sparse_vector_ops_gpu.hpp
selects GPU-resident double, float, or std::uint64_t values into a
SparseVector. This preserves full-width identity values without converting
through floating point. An application can reuse a fixed index list for a
slice or a downsampled selection and download just its selected values:
using Backend = pfc::backend::HIPTag;
pfc::core::SparseVector<Backend, std::uint64_t> selected(indices);
pfc::core::gather(selected, resident_ids.data(), resident_ids.size());
auto compact_ids = selected.data().to_host();
Indices are sorted by the constructor, with duplicate entries retained. The matching selected values use that sorted order. The gather downloads the index list for bounds validation, leaves its output unchanged on an out-of-bounds index, and completes its device work before returning. Inputs must be ready on the default stream; synchronize a producer on a nondefault stream before calling gather. Source and selection storage must be disjoint. An empty selection accepts a null source. A nonempty null source is rejected. The CPU gather shares the successful selection and error-message contract; its existing bounds-check loop can partially write before an error.
This operation does not compute front locations, aggregate fields, or
identity-dependent reductions: those are consumer policy. Scatter remains
double only; typed selections do not establish a Float32 scatter capability.