Skip to content

A new gsParaview class to sanitize paraview file I/O - #876

Open
filiatra wants to merge 12 commits into
devfrom
gsParaview
Open

A new gsParaview class to sanitize paraview file I/O#876
filiatra wants to merge 12 commits into
devfrom
gsParaview

Conversation

@filiatra

@filiatra filiatra commented Apr 3, 2026

Copy link
Copy Markdown
Member

Introduces a new class gsParaview to avoid having all the free functions..

- New gsParaview<T> class with gsOptionList-based configuration
- Options: numPoints, precision, plotElements, plotControlNet, show, bezier, boundary, interfaces, etc.
- Default filenames for all write() methods based on object type
- write() methods for: gsGeometry, gsMultiPatch, gsField, gsBasis, gsMesh, gsTrimSurface, gsSolid, etc.
- writePoints(), writeBasisFunction(), writeTrimmedCurve() helper methods
- gsParaview_.cpp instantiation file with pybind11 bindings
- gsView2.cpp example demonstrating the new class usage
@filiatra filiatra changed the title A newe gsParaview class to sanitize paraview file I/O A new gsParaview class to sanitize paraview file I/O Apr 3, 2026
@hverhelst hverhelst added this to the G+Smo V26.12 milestone Jun 23, 2026
@hverhelst hverhelst modified the milestones: G+Smo V26.12, G+Smo V26.07 Jul 16, 2026
```
gsParaviewUtils.h        (format only; NO evaluator include)
        ▲
gsParaviewDataSet.{h,cpp} (real_t; .cpp includes evaluator, does expr sampling)
        ▲
gsParaviewCollection.{h,cpp} (real_t; the time-stepping API)
        ▲
gsParaview.{h,hpp}       (gsParaview<T>; one-shot write only)
        ▲
gsExprEvaluator.h        (includes gsParaviewCollection.h — no cycle)
```

No back-edge from Utils to the evaluator, so the include cycle cannot re-form in
any build mode (library build or header-only).

| Layer | File(s) | Role | Templated? |
|---|---|---|---|
| Format utils | `gsParaviewUtils.h/.hpp` | `toDataArray(matrix)`, `toParaview(field/funcset)` | free fns |
| Per-timestep bag | `gsParaviewDataSet.h/.cpp` | one multipatch's `.vts` files; `addField(expr\|field)` | **`real_t`** (internal) |
| Collection | `gsParaviewCollection.h/.cpp` | writes `.pvd`; time-stepping loop (`newTimeStep`/`addField`/`saveTimeStep`/`save`) | **`real_t`** |
| OO writer | `gsParaview.h/.hpp/_.cpp` | one-shot `write(...)` overloads only | `gsParaview<T>` |
| Evaluator | `gsExprEvaluator.h` | expr eval; deprecated `writeParaview(expr,G,fn)` convenience wrapper | `gsExprEvaluator<T>` |

- `gsParaview<T>` is a **one-shot writer only**. It has no filename/evaluator
  constructor and no time-stepping members; every write* method is const and
  self-contained.
- `gsParaviewCollection` is the **canonical, endorsed time-stepping API**
  (`newTimeStep`/`addField`/`addFields`/`saveTimeStep`/`save` are no longer
  deprecated). `gsParaviewDataSet` remains an internal implementation detail
  used by the collection (and directly, if needed) — not user-facing.
- `gsExprEvaluator<T>::writeParaview(expr, G, fn)` stays as a thin,
  `GISMO_DEPRECATED` convenience wrapper that builds one `gsParaviewCollection`
  timestep. It only works for `T = real_t`, matching `gsParaviewCollection`'s
  `real_t`-only evaluator pointer (this is a pre-existing constraint of
  keeping the collection untemplated, not a regression).
…, remove lifetime hacks

Make Paraview time-stepping generic over T, replace the evaluator/geometry borrowing hacks with explicit non-owning pointers, and unify legacy option keys behind canonical aliases while keeping tests green
@hverhelst
hverhelst marked this pull request as ready for review August 5, 2026 09:35
@hverhelst
hverhelst requested a review from a team as a code owner August 5, 2026 09:35
Copilot AI lite review requested due to automatic review settings August 5, 2026 09:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

This PR introduces an object-oriented gsParaview writer to replace scattered free functions, adds support for single-file unstructured-grid (.vtu) exports (optionally base64), and refactors Paraview collection/dataset utilities (including template-ization and legacy option aliases).

Changes:

  • Add gsParaview class (C++ + optional pybind11) and update many examples to use it.
  • Extend Paraview I/O APIs with skipPvd and add gsWriteParaviewUnstructuredGrid for one-file .vtu output.
  • Refactor gsParaviewCollection / gsParaviewDataSet into templated headers with new instantiation units and legacy option aliases.

Reviewed changes

Copilot reviewed 56 out of 56 changed files in this pull request and generated no comments.

Show a summary per file
File Description
unittests/gsParaview_test.cpp Adds unit tests covering defaults, single-file VTU output, and collection workflows.
src/gsIO/gsWriteParaview_.cpp Updates explicit instantiations + pybind bindings for modified function signatures.
src/gsIO/gsWriteParaview.hpp Adds base64 include, skipPvd plumbing, and unstructured-grid VTU writers.
src/gsIO/gsWriteParaview.h Updates public signatures (skipPvd) and declares unstructured-grid writers.
src/gsIO/gsParaview_.cpp Adds template instantiation + pybind11 bindings for gsParaview.
src/gsIO/gsParaviewUtils_.cpp Renames exported helper API from toVTK to toParaview.
src/gsIO/gsParaviewUtils.hpp Removes some heavy includes and renames helpers to toParaview.
src/gsIO/gsParaviewUtils.h Renames helpers to toParaview and removes expression-based toVTK overloads.
src/gsIO/gsParaviewDataSet_.cpp Adds explicit instantiation unit for templated gsParaviewDataSet.
src/gsIO/gsParaviewDataSet.hpp Implements templated dataset writer logic (structured-grid .vts).
src/gsIO/gsParaviewDataSet.h Templates dataset class, adds legacy option aliases, and evaluator bridge decl.
src/gsIO/gsParaviewDataSet.cpp Removes old non-templated implementation (migrated to header).
src/gsIO/gsParaviewCollection_.cpp Adds explicit instantiation unit for templated gsParaviewCollection.
src/gsIO/gsParaviewCollection.hpp Adds templated collection implementation (timestep/dataset management).
src/gsIO/gsParaviewCollection.h Templates collection API, updates addField forwarding, keeps makeCollection helper.
src/gsIO/gsParaviewCollection.cpp Removes old non-templated implementation (migrated to header).
src/gsIO/gsParaview.hpp Implements gsParaview writer that forwards to existing I/O primitives.
src/gsIO/gsParaview.h Declares gsParaview public API and options interface.
src/gsCore/gsForwardDeclarations.h Adds forward declarations for templated dataset/collection.
src/gsAssembler/gsExprEvaluator.h Rewires deprecated writeParaview to use new collection workflow and adds expr sampling bridge.
src/gismo.h Adjusts umbrella include set (drops gsParaviewDataSet.h).
examples/triangulatedMeshToSolid_example.cpp Switches Paraview output to gsParaview.
examples/thbSplineBasis_example.cpp Switches Paraview output to gsParaview.
examples/thbRefinement_example.cpp Switches Paraview output to gsParaview (with options).
examples/surfaceReparametrization_example.cpp Switches Paraview output to gsParaview.
examples/subdivisionSurfaces_example.cpp Switches Paraview output to gsParaview.
examples/stokes_ieti_example.cpp Switches Paraview output to gsParaview (numPoints set once).
examples/refitting_example.cpp Switches Paraview output to gsParaview and uses options for mesh/cnet.
examples/quadrature_example.cpp Switches Paraview points/basis output to gsParaview.
examples/precision_example.cpp Switches Paraview output to gsParaview.
examples/poissonTHB_example.cpp Switches Paraview output to gsParaview (changes numPoints between outputs).
examples/patches_from_mesh.cpp Switches Paraview output to gsParaview.
examples/paraview_example.cpp Switches Paraview output to gsParaview including Bezier via option.
examples/parametrization_example.cpp Switches Paraview output to gsParaview.
examples/multiGrid_example.cpp Switches Paraview output to gsParaview using show option.
examples/kirchhoff-Love_example.cpp Switches Paraview output to gsParaview.
examples/inputOutput_example.cpp Switches Paraview output to gsParaview including Bezier via option.
examples/ieti_example.cpp Switches Paraview output to gsParaview.
examples/ieti2_example.cpp Switches Paraview output to gsParaview.
examples/gsView2.cpp Adds a new CLI example using gsParaview.
examples/gsView.cpp Refactors existing CLI example to use gsParaview.
examples/gsMappedSpline_example.cpp Switches Paraview output to gsParaview for mapped spline/basis.
examples/gsHElement_marking_example.cpp Switches Paraview output to gsParaview for meshes and boxes.
examples/gsHBox_example.cpp Switches Paraview output to gsParaview for hbox exports.
examples/gsAdaptiveMeshing_example.cpp Switches Paraview output to gsParaview for marked boxes and fields.
examples/geometry_example.cpp Switches Paraview output to gsParaview.
examples/fitting_mspline.cpp Switches Paraview output to gsParaview with show.
examples/compositions_example.cpp Switches Paraview output to gsParaview and reuses object.
examples/biharmonic_example.cpp Switches Paraview output to gsParaview with show.
examples/basis_example.cpp Switches Paraview output to gsParaview.
examples/bSplineSurface_example.cpp Switches Paraview output to gsParaview (numPoints set for geometry).
examples/bSplineCurve_example.cpp Switches Paraview output to gsParaview (show/mesh/cnet options).
examples/bSplineBasis_example.cpp Switches Paraview output to gsParaview.
examples/assembly_example.cpp Switches Paraview output to gsParaview with show.
examples/adaptiveConvectionDiffusionReaction_example.cpp Switches Paraview output to gsParaview including show.
examples/adaptRefinementThb_example.cpp Switches Paraview output to gsParaview.
Suppressed comments (9)

src/gsIO/gsParaview.hpp:1

  • openIfRequested always opens fn + ".pvd", but several write paths can legitimately produce only .vtu (singleFile + writePvd=false / skipPvd) or .vtp (curve/mesh). This makes the show option unreliable and can open a non-existent file. Consider tracking the actual produced “entry file” per write call (e.g., .pvd when written, otherwise the concrete output like .vtu/.vts/.vtp) and open that instead.
    src/gsIO/gsParaview.hpp:1
  • The gsParaview option "writePvd" is explicitly not honored for the non-singleFile, non-bezier multipatch export path, which is surprising for API consumers and makes behavior inconsistent with write(geometry) / write(field). A tangible fix is to add a skipPvd (or writePvd) parameter to the relevant gsWriteParaview(const gsMultiPatch&, ...)/vector overloads (or route this path through the gsParaviewCollection object conditionally) so "writePvd" is consistently applied.
    src/gsIO/gsParaviewCollection.hpp:1
  • Explicit timesteps are truncated to an integer (cast<T,int>(time)), which loses information for non-integer times (e.g., 0.1, 0.5) and will write incorrect timestep="..." attributes in the .pvd. Since the public API accepts T time, m_time should store the full T value (or double) without integer truncation.
    src/gsIO/gsParaviewDataSet.hpp:1
  • isEmpty() always returning false makes the method misleading and undermines the defensive checks in gsParaviewCollection (!dataSet.isEmpty()). Even if an “empty dataset” is now structurally impossible (because constructors require a geometry reference), it’s better to reflect actual state (e.g., m_geometry == nullptr or m_filenames.empty()) or remove/replace isEmpty() from the contract to avoid future misuse.
    src/gsIO/gsWriteParaview.hpp:1
  • After detecting !file.is_open(), the function continues writing to file unconditionally, which will silently drop output and can mask failures. This should return early (or throw/assert) once opening fails, especially since the function later calls makeCollection, potentially creating a .pvd that references a missing .vtu.
    src/gsIO/gsWriteParaview.hpp:1
  • VTK inline binary (base64) expects the 64-bit byte-count header to match the declared byte_order (and typically assumes little-endian when byte_order="LittleEndian"). Here the header is emitted in host endianness with no conversion, so output can be invalid on big-endian systems. Consider explicitly serializing the header (and potentially numeric payload) to the chosen byte order before base64 encoding, or enforce/validate little-endian when export_base64=true.
    src/gsIO/gsWriteParaview_.cpp:1
  • The Python binding renames the keyword argument from controlNet to ctrlNet, which is a breaking change for users relying on keyword args. To maintain compatibility, keep the existing keyword (controlNet) (even if the C++ parameter is ctrlNet) or provide both names (e.g., keep controlNet in py::arg).
    src/gsAssembler/gsExprEvaluator.h:1
  • This “backward compatibility” logic will prefer numPoints/plotElements whenever those keys exist, even if users set only the legacy keys (plot.npts / plot.elements). Since defaultOptions() now adds the canonical keys, exists("numPoints") will likely always be true, effectively ignoring legacy values. Align this with the more robust alias logic used in gsParaviewDataSet::getNumPoints/getPlotElements (e.g., prefer canonical only when it differs from its default, otherwise fall back to legacy if it differs from default).
    unittests/gsParaview_test.cpp:1
  • The test likely doesn’t exercise the precision behavior because it guesses output filenames as fn + "0.vts" (no delimiter), while other tests already acknowledge multiple naming schemes (e.g., _0, _patch0, etc.). As written, the check can silently skip on most outputs, weakening coverage. Consider resolving the actual produced .vts filename(s) (e.g., using the configured patch delimiter, or searching expected variants and asserting one is found) and then asserting on size/content.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants