stochastic galerkin projection routines #300
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a stochastic (Monte Carlo / Sobol-sampled) workflow for assembling the Galerkin projection load vector on 2D linear triangular meshes, alongside renaming the existing mesh-intersection (exact) projection APIs for clarity.
Changes:
- Add Monte Carlo sampling + point-localization utilities and a Monte Carlo load-vector integrator.
- Split/rename mesh-intersection routines to
*MIvariants and add*MCsolver/RHS variants. - Add unit tests covering sampling/mapping/localization/field-evaluation and end-to-end Monte Carlo projection.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| test/test_monte_carlo_projection_functions.cpp | New tests for Sobol file read, barycentric sampling, mapping, localization, and field evaluation pipeline. |
| test/test_monte_carlo_field_transfer.cpp | New end-to-end PETSc-enabled test for Monte Carlo projection (constant/linear fields). |
| test/test_mesh_intersection_field_transfer.cpp | Update to renamed mesh-intersection projection entrypoint (solveGalerkinProjectionMI). |
| test/test_load_vector.cpp | Update to renamed mesh-intersection load-vector builder (buildLoadVectorMI). |
| test/CMakeLists.txt | Register new test sources (Omega_h + PETSc gates). |
| src/pcms/transfer/load_vector_integrator_mi.cpp | Rename mesh-intersection integrator entrypoint to buildLoadVectorMI. |
| src/pcms/transfer/load_vector_integrator_mc.cpp | New Monte Carlo sampling, mapping, localization, field-eval, and buildLoadVectorMC. |
| src/pcms/transfer/load_vector_integrator.hpp | Add MC API declarations + SamplingMethod; rename MI entrypoint declaration. |
| src/pcms/transfer/conservative_projection_solver.hpp | Add solveGalerkinProjectionMI/MC and computeRhsVectorMI/MC APIs + updated documentation. |
| src/pcms/transfer/conservative_projection_solver.cpp | Implement MI/MC projection solves and RHS-vector helpers using PETSc. |
| src/pcms/transfer/calculate_load_vector.hpp | Rename MI assembler to calculateLoadVectorMI; add MC assembler API. |
| src/pcms/transfer/calculate_load_vector.cpp | Implement calculateLoadVectorMI and new calculateLoadVectorMC. |
| src/pcms/transfer/CMakeLists.txt | Build both MI + MC integrator sources; keep PETSc pieces behind PCMS_ENABLE_PETSC. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const std::string filename = "tmp_sobol_barycentric_samples.txt"; | ||
| std::ofstream out(filename); | ||
| REQUIRE(out.is_open()); | ||
| out << "l0 l1 l2\n"; | ||
| out << "1.0 0.0 0.0\n"; | ||
| out << "0.0 1.0 0.0\n"; | ||
| out << "0.2 0.3 0.5\n"; | ||
| out.close(); | ||
|
|
There was a problem hiding this comment.
This test uses a fixed temporary filename (tmp_sobol_barycentric_samples.txt). If tests are executed in parallel (or rerun after a crash), this can cause collisions or stale data. Consider using a unique temp path and/or an RAII cleanup guard so the file is removed even when assertions fail.
|
|
||
| int nx = edge_length[0] * Kokkos::sqrt(mesh.nelems()); | ||
| int ny = edge_length[1] * Kokkos::sqrt(mesh.nelems()); | ||
|
|
There was a problem hiding this comment.
localize_points_in_mesh computes nx/ny by casting a floating expression to int. For small bounding boxes (edge length < 1) this can truncate to 0, which later causes division-by-zero in UniformGrid (divisions used as a divisor). Clamp nx/ny to at least 1 (and consider using Omega_h::LO/std::max).
| if (nx < 1) nx = 1; | |
| if (ny < 1) ny = 1; |
| #include <Omega_h_mesh.hpp> | ||
| #include <fstream> | ||
| #include <iostream> | ||
| #include <sstream> |
There was a problem hiding this comment.
load_vector_integrator.hpp declares APIs using std::string, but it does not include <string>. Please add the missing include to make the header self-contained, and consider removing heavy/unused includes like <iostream> (include it in the .cpp that needs it instead).
| #include <sstream> | |
| #include <sstream> | |
| #include <string> |
| const auto npoints = points.extent(0); | ||
|
|
There was a problem hiding this comment.
const auto npoints = points.extent(0); is unused, which will trigger warnings in this TU. Please remove it or use it for validation (e.g., early return on zero points).
| const auto npoints = points.extent(0); |
| const auto& npoints = results.extent(0); | ||
|
|
||
| const auto& faces2nodes = mesh.ask_down(Omega_h::FACE, Omega_h::VERT).ab2b; | ||
| const auto& coordinates = mesh.coords(); |
There was a problem hiding this comment.
const auto& coordinates = mesh.coords(); is unused in evaluate_field_from_point_localization. Please remove it to avoid warnings and keep the function focused.
| const auto& coordinates = mesh.coords(); |
|
|
||
| Vec vec; | ||
| PetscErrorCode ierr; | ||
| ierr = | ||
| calculateLoadVectorMC(target_mesh, field_values_at_points, | ||
| npoints_each_tri, method, &vec, sobol_filename); | ||
| CHKERRABORT(PETSC_COMM_WORLD, ierr); | ||
|
|
||
| auto rhsvector = vecToOmegaHReals(vec); | ||
| ierr = VecDestroy(&vec); | ||
| CHKERRABORT(PETSC_COMM_WORLD, ierr); | ||
|
|
||
| return rhsvector; | ||
| } | ||
|
|
There was a problem hiding this comment.
computeRhsVectorMC has inconsistent indentation and a trailing tab after the closing brace, which will fight auto-formatting and make diffs noisy. Please reformat this block to match the rest of the file.
| Vec vec; | |
| PetscErrorCode ierr; | |
| ierr = | |
| calculateLoadVectorMC(target_mesh, field_values_at_points, | |
| npoints_each_tri, method, &vec, sobol_filename); | |
| CHKERRABORT(PETSC_COMM_WORLD, ierr); | |
| auto rhsvector = vecToOmegaHReals(vec); | |
| ierr = VecDestroy(&vec); | |
| CHKERRABORT(PETSC_COMM_WORLD, ierr); | |
| return rhsvector; | |
| } | |
| Vec vec; | |
| PetscErrorCode ierr; | |
| ierr = | |
| calculateLoadVectorMC(target_mesh, field_values_at_points, | |
| npoints_each_tri, method, &vec, sobol_filename); | |
| CHKERRABORT(PETSC_COMM_WORLD, ierr); | |
| auto rhsvector = vecToOmegaHReals(vec); | |
| ierr = VecDestroy(&vec); | |
| CHKERRABORT(PETSC_COMM_WORLD, ierr); | |
| return rhsvector; | |
| } |
| #include <pcms/transfer/conservative_projection_solver.hpp> | ||
| #include <pcms/transfer/load_vector_integrator.hpp> | ||
|
|
||
| #include <fstream> |
There was a problem hiding this comment.
This test uses std::function (see evaluate_field_at_barycentric_samples) but does not include <functional>. Please add the missing include to avoid relying on transitive headers.
| #include <fstream> | |
| #include <fstream> | |
| #include <functional> |
| auto bary = make_sobol_barycentric_samples(); | ||
| const int npoints_each_tri = bary.extent(0); | ||
| const std::string sobol_filename = "tmp_mc_barycentric_samples.txt"; | ||
| write_barycentric_samples_file(sobol_filename, bary); | ||
|
|
There was a problem hiding this comment.
The test writes tmp_mc_barycentric_samples.txt but never deletes it. This can pollute the working directory and can also cause collisions when tests run concurrently. Add cleanup (ideally via an RAII guard) and consider using a unique filename per test run.
| test_interpolation_class.cpp | ||
| test_mesh_geometry.cpp | ||
| test_monte_carlo_projection_functions.cpp | ||
| test_omega_h_field2_outofbounds.cpp) |
There was a problem hiding this comment.
This added entry is indented with a tab, which is inconsistent with the surrounding CMake formatting and may fail format checks. Replace the tab with spaces to match the rest of the file.
| list(APPEND PCMS_UNIT_TEST_SOURCES | ||
| test_mesh_intersection_field_transfer.cpp) | ||
| test_mesh_intersection_field_transfer.cpp | ||
| test_monte_carlo_field_transfer.cpp) | ||
| endif() |
There was a problem hiding this comment.
This list append adds an entry with tab indentation. Use spaces to match the existing CMake formatting so automated formatters don't keep rewriting this line.
Sichao25
left a comment
There was a problem hiding this comment.
Looks good overall. Just commented a few minor issues.
| namespace pcms | ||
| { | ||
| PetscErrorCode calculateLoadVector(Omega_h::Mesh& target_mesh, | ||
| PetscErrorCode calculateLoadVectorMI(Omega_h::Mesh& target_mesh, |
There was a problem hiding this comment.
If I haven't missed anything, the calculateLoadVectorMI and calculateLoadVectorMC share roughly 90% of their code, there may be room to refactor and eliminate the duplication.
| buildLoadVectorMI(target_mesh, source_mesh, intersection, source_values); | ||
|
|
||
| auto hostElmLoadVector = Kokkos::create_mirror_view(elmLoadVector); | ||
| Kokkos::deep_copy(hostElmLoadVector, elmLoadVector); |
There was a problem hiding this comment.
There is a create_mirror_view_and_copy function.
| { | ||
| std::ifstream infile(file_path); | ||
| if (!infile) { | ||
| throw std::runtime_error("Could not open sobol sample file"); |
There was a problem hiding this comment.
We already have pcms_error.
This PR adds the stochastic Galerkin projection workflow. Currently, this works for 2D linear elements. A follow-up cleanup would be to remove the current file-based Sobol input path and add a function for Sobol sampling.