Files
MeanField_SERiF/docs/rayfem.md
Emily Boudreaux 6b57738330 docs(docs): built docs
Note for now much of the docs is LLM written. This was done to save time and focus on code development. The docs have been LOOSLEY reviewed. However, the code itself has been either fully written by hand or fully reviewed. Therefore, the docs should at the moment be seen as a loose resource rather than authoratiative (and this is on top of the general ability for docs to drift)
2026-11-09 14:09:44 -05:00

12 KiB

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:

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:

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:

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++:

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:

#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:

#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 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:

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:

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.