247 lines
12 KiB
Markdown
247 lines
12 KiB
Markdown
|
|
# RayFEM visualization
|
||
|
|
|
||
|
|
RayFEM is a local source snapshot of `GLVis++` in `subprojects/rayfem`. The
|
||
|
|
`rayfem.wrap` URL deliberately uses the reserved `.invalid` domain: the checked-out
|
||
|
|
folder supplies the source. Keep the folder alongside the wrap; the inert URL
|
||
|
|
cannot restore this snapshot in another checkout. Configure with
|
||
|
|
`--wrap-mode=nodownload` to require local dependencies. The snapshot includes the
|
||
|
|
RayFEM library, desktop viewer, and their raylib/CZMQ/ImGui sources; the parent
|
||
|
|
supplies MFEM and CLI11. ZeroMQ remains a system dependency. Existing repository
|
||
|
|
ignore rules exclude vendored subproject directories from version control.
|
||
|
|
|
||
|
|
RayFEM uses SERiF's MFEM dependency and original SERiF library for archive loading.
|
||
|
|
The downstream `serif_dep` additionally exports `rayfem_dep`, so programs already
|
||
|
|
using `dependencies: serif_dep` can include the RayFEM headers and call its API.
|
||
|
|
The core SERiF library does not depend on RayFEM; no solver hooks or numerical
|
||
|
|
policies are changed. The viewer is enabled by default. Library-only builds can
|
||
|
|
set `-Drayfem:build_viewer=disabled`. RayFEM requires double-precision MFEM with
|
||
|
|
exceptions, as checked by its existing compatibility source.
|
||
|
|
|
||
|
|
## Build and open a saved solution
|
||
|
|
|
||
|
|
For optimized stellar solves, configure the whole build in release mode:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
meson setup --reconfigure build --buildtype=release --wrap-mode=nodownload
|
||
|
|
meson compile -C build complete_sandbox
|
||
|
|
```
|
||
|
|
|
||
|
|
For a new directory, omit `--reconfigure`. Release uses Meson's standard `-O3`
|
||
|
|
and disables `MFEM_DEBUG` in the bundled MFEM and every consumer, including
|
||
|
|
SERiF, STROID and RayFEM. To select the `-O2` level and debugging symbols used
|
||
|
|
in the performance comparison, apply the overrides after setting the build type:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
meson configure build -Doptimization=2 -Ddebug=true
|
||
|
|
meson compile -C build complete_sandbox
|
||
|
|
```
|
||
|
|
|
||
|
|
Meson 1.9.1 can reset explicit optimization/symbol settings when the build type
|
||
|
|
is changed in the same command; separate commands avoid that ordering issue.
|
||
|
|
Debugging symbols do not re-enable MFEM's checks. `--buildtype=debugoptimized`
|
||
|
|
also provides `-O2` with symbols and MFEM debug checks off.
|
||
|
|
`--buildtype=debug` enables MFEM's checks again.
|
||
|
|
|
||
|
|
Meson's `b_ndebug` setting controls ordinary C/C++ assertions separately and
|
||
|
|
remains unchanged (default `false`). Numerical tolerances, geometry guards and
|
||
|
|
physical acceptance criteria are unchanged. Switching build modes rebuilds the
|
||
|
|
private MFEM bundle and its consumers; the first build can take substantially
|
||
|
|
longer than an incremental build. A preinstalled MFEM with debug checks enabled
|
||
|
|
is rejected for non-debug build types; `-Dallow_preinstalled=false` selects the
|
||
|
|
matching source build. Custom optimization, symbol and assertion settings are
|
||
|
|
forwarded to MFEM's C/C++ compilation rather than replaced by CMake's defaults.
|
||
|
|
CUDA backend compiler flags retain their existing policy; this configuration was
|
||
|
|
validated with the CPU backend.
|
||
|
|
|
||
|
|
Use SERiF's existing compiler/MFEM configuration and Meson >= 1.9.1. For an existing
|
||
|
|
build directory:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
meson setup --reconfigure build --wrap-mode=nodownload
|
||
|
|
meson compile -C build RayFEM
|
||
|
|
./build/subprojects/rayfem/src/RayFEM --open saved-solution.serif
|
||
|
|
```
|
||
|
|
|
||
|
|
On the current macOS machine, Homebrew LLVM selects the CommandLineTools SDK
|
||
|
|
while CZMQ's optional curl dependency discovers the Xcode SDK. Match them explicitly
|
||
|
|
if using `llvm_compiler.ini`; otherwise exported SDK headers can precede libc++:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
meson configure build \
|
||
|
|
"-Dc_args=['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk']" \
|
||
|
|
"-Dcpp_args=['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk']"
|
||
|
|
meson compile -C build RayFEM
|
||
|
|
```
|
||
|
|
|
||
|
|
These are toolchain settings, not changes to RayFEM. Preserve any additional
|
||
|
|
existing `c_args`/`cpp_args` when adding the SDK setting. Use a matching installed
|
||
|
|
SDK on other machines. The isolated validation build reused SERiF's existing MFEM
|
||
|
|
installation and redirected compiler caches into its own workspace.
|
||
|
|
|
||
|
|
The viewer also accepts drag-and-drop or **Open**. The archive reader reconstructs
|
||
|
|
saved serial H1, L2 and H(div) fields, including constrained/nonconforming DOFs,
|
||
|
|
and preserves field material support. Multiple distinct saved states require an
|
||
|
|
explicit `RayFEM::SerifLoadOptions::state_path` through the C++ loader; identical
|
||
|
|
checkpoint/state duplicates are deduplicated.
|
||
|
|
|
||
|
|
Saved nodal `displacement.vector` data can deform the display. Surface-harmonic
|
||
|
|
coefficients are not nodal displacement: without a saved nodal displacement,
|
||
|
|
the viewer shows reference geometry. The compactified exterior stays in saved
|
||
|
|
mesh coordinates; this integration does not reconstruct SERiF's physical exterior
|
||
|
|
mapping. Global scalars and unsupported surface coordinates are reported rather
|
||
|
|
than rendered as spatial fields. Archive snapshots have no inferred physical time.
|
||
|
|
|
||
|
|
## Stream a solution or intermediate field
|
||
|
|
|
||
|
|
Call the existing RayFEM API from the application or its observer. Start the
|
||
|
|
viewer above, or set `RayFEM::WindowParams::executable` to the built viewer and
|
||
|
|
call `RayFEM::init` once. For existing MFEM grid functions on one mesh:
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
#include "libRayfem/network/client.hpp"
|
||
|
|
#include <stdexcept>
|
||
|
|
|
||
|
|
RayFEM::Client viewer("127.0.0.1", 5555);
|
||
|
|
if (!viewer.sendMesh(1, mesh)) {
|
||
|
|
throw std::runtime_error(viewer.getLastError());
|
||
|
|
}
|
||
|
|
|
||
|
|
// Repeat after updating the fields; frame_index must increase.
|
||
|
|
RayFEM::FrameMetadata metadata{.frame_index = 0, .sim_time = 0.0};
|
||
|
|
if (!viewer.sendFrame(1, {{"solution", &solution_field},
|
||
|
|
{"intermediate", &intermediate_field}}, metadata)) {
|
||
|
|
throw std::runtime_error(viewer.getLastError());
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Each frame's grid functions must reference the **same registered mesh**. Fields
|
||
|
|
on a material submesh and a full mesh need separate mesh IDs and frames. Keep
|
||
|
|
meshes/spaces alive through their registrations, and grid functions alive through
|
||
|
|
each send. Use one client from one thread. Re-register changed topology or changed
|
||
|
|
reference geometry; optionally pass a pointwise vector displacement as the fourth
|
||
|
|
`sendFrame` argument. Displacement is measured from the registered reference mesh,
|
||
|
|
not from the preceding frame. Sending succeeds when the local transport accepts
|
||
|
|
the packet; it is not an acknowledgment of rendering.
|
||
|
|
|
||
|
|
SERiF state blocks contain supported independent coefficients, not necessarily a
|
||
|
|
full MFEM `GridFunction`. Use the matching term binding's `scatter` operation to
|
||
|
|
expand a copied block and satisfy nonconforming constraints:
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
#include "mfem.hpp"
|
||
|
|
#include <span>
|
||
|
|
#include <stdexcept>
|
||
|
|
|
||
|
|
template<class Binding>
|
||
|
|
mfem::GridFunction field_for_viewer(const Binding& binding,
|
||
|
|
std::span<const double> values) {
|
||
|
|
if (values.size() != static_cast<std::size_t>(binding.size())) {
|
||
|
|
throw std::invalid_argument("State and visualization binding differ");
|
||
|
|
}
|
||
|
|
mfem::Vector reduced(binding.size());
|
||
|
|
for (int i = 0; i < reduced.Size(); ++i) { reduced[i] = values[i]; }
|
||
|
|
mfem::GridFunction field(&binding.space());
|
||
|
|
field = binding.scatter(reduced);
|
||
|
|
return field;
|
||
|
|
}
|
||
|
|
|
||
|
|
// For a spatial Term from the matching discretization/state:
|
||
|
|
// auto field = field_for_viewer(finite_elements.template binding<Term>(),
|
||
|
|
// state.template get<Term>());
|
||
|
|
```
|
||
|
|
|
||
|
|
Recreate or update this visualization copy at the state you want to inspect;
|
||
|
|
it does not change the solver state. A Newton residual is a dual vector and must
|
||
|
|
not be presented as physical field coefficients without an explicit conversion.
|
||
|
|
The streaming protocol does not transmit SERiF's material-support metadata: exclude
|
||
|
|
unsupported materials in the viewer, use a suitable submesh, or load an archive
|
||
|
|
when support-aware inspection is required. Zero extension outside support is not
|
||
|
|
physical data. Geometry/displacement must correspond to the same state as the
|
||
|
|
fields; copying scalar coefficients alone does not reconstruct the deformed star.
|
||
|
|
|
||
|
|
See the vendored [RayFEM README](../subprojects/rayfem/README.md) for clipping,
|
||
|
|
profiles, vector flow, frame history, tracking, and viewer controls.
|
||
|
|
|
||
|
|
## Watch a stellar solve
|
||
|
|
|
||
|
|
`complete_sandbox` has an opt-in live observer; without arguments its existing
|
||
|
|
nonrotating n=1 solve and numerical settings are unchanged. Build and run:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
meson compile -C build complete_sandbox
|
||
|
|
OMP_NUM_THREADS=1 ./build/tests/sandbox/complete_sandbox \
|
||
|
|
--angular-momentum 0.15 --surface-degree 2 --visualize
|
||
|
|
```
|
||
|
|
|
||
|
|
Building `complete_sandbox` also builds the enabled RayFEM viewer. `--visualize`
|
||
|
|
uses `RayFEM::init` to reuse a compatible running viewer on port 5555 or launch
|
||
|
|
the viewer from this build automatically. `--viewer PATH` and `RAYFEM_EXECUTABLE`
|
||
|
|
remain explicit overrides. If the build has no viewer, normal sibling/PATH
|
||
|
|
discovery remains available. `--viewer-port PORT` selects another
|
||
|
|
stream/control port pair. `--help` lists defaults. With no options it solves the
|
||
|
|
n=2, J=0.1 star (Omega/Omega_K about 0.70) at surface degree 12, which converges
|
||
|
|
from the spherical seed in 19 Newton steps and matches its ESTER/TOP reference
|
||
|
|
within 0.55 PLATO sigma. Units are M=G=R_nonrotating=1; angular momentum is the
|
||
|
|
target J in those code units.
|
||
|
|
These are accepted Newton iterates toward equilibrium, not a time evolution.
|
||
|
|
|
||
|
|
### Refine the stellar surface
|
||
|
|
|
||
|
|
The sandbox exposes mesh refinement separately from `--surface-degree`, which
|
||
|
|
controls the spherical-harmonic shape basis. Refinement is applied before
|
||
|
|
creating the field spaces; the defaults are surface 1, equatorial 1 (10 degree
|
||
|
|
band) and radial surface 3, with no uniform refinement:
|
||
|
|
|
||
|
|
- `--mesh-refinement N` uniformly refines the whole mesh N times.
|
||
|
|
- `--surface-refinement N` adds N isotropic refinement levels to the cells
|
||
|
|
touching the stellar surface, on both the stellar and vacuum sides.
|
||
|
|
- `--equatorial-refinement N` then adds N latitude splits to rings intersecting
|
||
|
|
the equatorial band. These concentrate resolution toward the equator while
|
||
|
|
keeping the polar caps at their previous resolution.
|
||
|
|
- `--equatorial-band-degrees D` sets the band's half-width, default 10 degrees;
|
||
|
|
the supported range is `0 < D <= 35`.
|
||
|
|
- `--rotation-axis X Y Z` selects the rotation axis, default `0 0 1`. Any finite
|
||
|
|
nonzero vector is normalized and used for both rotation physics and mesh
|
||
|
|
alignment.
|
||
|
|
|
||
|
|
For example, refine the entire stellar surface once and add two equatorial
|
||
|
|
levels around a tilted axis:
|
||
|
|
|
||
|
|
```sh
|
||
|
|
OMP_NUM_THREADS=1 ./build/tests/sandbox/complete_sandbox \
|
||
|
|
--angular-momentum 0.15 --surface-degree 2 \
|
||
|
|
--surface-refinement 1 --equatorial-refinement 2 \
|
||
|
|
--equatorial-band-degrees 15 --rotation-axis 1 1 1 --visualize
|
||
|
|
```
|
||
|
|
|
||
|
|
Use `--mesh-only` in place of `--visualize` to print element and surface-face
|
||
|
|
counts without solving. Counts are also printed at the start of normal solves.
|
||
|
|
On the coarsest mesh, three equatorial levels increase stellar surface faces
|
||
|
|
from 6 to 26 while leaving the two polar faces unchanged.
|
||
|
|
|
||
|
|
Equatorial refinement splits latitude only, following complete rings so that
|
||
|
|
the moving-surface representation remains compatible with the volume mesh.
|
||
|
|
Whole intersecting rings can extend beyond the requested band, especially at
|
||
|
|
low refinement levels. The 35-degree limit keeps these rings within the four
|
||
|
|
side blocks of the cubed-sphere mesh; the polar blocks begin at approximately
|
||
|
|
35.26 degrees latitude. This is fixed refinement chosen before the solve, not
|
||
|
|
adaptive refinement during Newton iterations. Field polynomial orders and
|
||
|
|
solver tolerances are unchanged; additional cells increase solve cost.
|
||
|
|
|
||
|
|
The reusable example helper `tests/sandbox/stellar_rayfem.hpp` copies density,
|
||
|
|
potential, and the evaluator's existing nodal displacement onto a submesh of the
|
||
|
|
stellar material attributes. It excludes the compactified exterior and sends the
|
||
|
|
unmodified interior reference mesh once. Each frame supplies the full current
|
||
|
|
displacement from that reference, so deformation is applied exactly once.
|
||
|
|
Scalar fields retain their physical code units. The displacement is also available
|
||
|
|
as a field for inspection; select density or potential to color the star.
|
||
|
|
|
||
|
|
The observer publishes only initial/resumed/accepted events. It checks the cached
|
||
|
|
surface parameters match the callback state, then expands the cached displacement
|
||
|
|
through its actual term binding (including ordering and constraints). A `finished`
|
||
|
|
event can leave a rejected trial in the evaluator cache and is deliberately not
|
||
|
|
streamed. The helper owns field copies and does not move solver mesh nodes or
|
||
|
|
change coefficients. Transport errors disable visualization while the solve
|
||
|
|
continues. Frame labels and terminal output give iteration and residual; frame time
|
||
|
|
is zero because no physical time integration is being performed.
|