Skip to content

Step 4 — Assemble a Tiny Operator Graph

Capability

Write a tiny operator graph explicitly and inspect where each edge in the graph lives.

Problem Statement

Frameworks often jump straight to solver classes. flux should be teachable one operator edge at a time, because that is the design claim the repo is making.

Mathematical Idea

A small graph such as φ -> dφ -> ★dφ already exposes the interaction between degree changes, storage choices, and metric dependence.

Flux Concepts

  • Operator graphs are typed by the spaces on their edges.
  • OperatorContext assembles the graph edges lazily.
  • The graph framing scales from toy examples to full PDE systems.

Minimal Runnable Snippet

Commented walkthrough:

const std = @import("std"); const flux = @import("flux"); pub fn run(allocator: std.mem.Allocator) !void { const Mesh2D = flux.topology.Mesh(2, 2); var mesh = try Mesh2D.plane(allocator, 2, 2, 1.0, 1.0); defer mesh.deinit(allocator); var operators = try flux.operators.context.OperatorContext(Mesh2D).init(allocator, &mesh); defer operators.deinit(); var phi = try flux.forms.Cochain(Mesh2D, 0, flux.forms.Primal).init(allocator, &mesh); defer phi.deinit(allocator); phi.values[0] = 1.0; phi.values[1] = 0.0; phi.values[2] = -1.0; phi.values[3] = 0.5; // Think of this as a tiny operator graph: φ --d--> dφ --★--> ★dφ. var d_phi = try (try operators.exteriorDerivative(flux.forms.Primal, 0)).apply(allocator, phi); defer d_phi.deinit(allocator); var star_d_phi = try (try operators.hodgeStar(1)).apply(allocator, d_phi); defer star_d_phi.deinit(allocator); std.debug.assert(star_d_phi.values.len == mesh.num_edges()); } test "step 04 commented snippet compiles and runs" { try run(std.testing.allocator); }

Plain program:

const std = @import("std"); const flux = @import("flux"); pub fn run(allocator: std.mem.Allocator) !void { const Mesh2D = flux.topology.Mesh(2, 2); var mesh = try Mesh2D.plane(allocator, 2, 2, 1.0, 1.0); defer mesh.deinit(allocator); var operators = try flux.operators.context.OperatorContext(Mesh2D).init(allocator, &mesh); defer operators.deinit(); var phi = try flux.forms.Cochain(Mesh2D, 0, flux.forms.Primal).init(allocator, &mesh); defer phi.deinit(allocator); phi.values[0] = 1.0; phi.values[1] = 0.0; phi.values[2] = -1.0; phi.values[3] = 0.5; var d_phi = try (try operators.exteriorDerivative(flux.forms.Primal, 0)).apply(allocator, phi); defer d_phi.deinit(allocator); var star_d_phi = try (try operators.hodgeStar(1)).apply(allocator, d_phi); defer star_d_phi.deinit(allocator); std.debug.assert(star_d_phi.values.len == mesh.num_edges()); } test "step 04 plain snippet compiles and runs" { try run(std.testing.allocator); }

Expected Result

The snippet does not pretend to be a solver. It shows that the typed intermediate fields are the main object, and that this is enough to reason about a PDE discretization.

Possible Extensions

  • Add a second branch in the graph instead of forcing everything through one path.
  • Connect this page to the Maxwell example and identify the corresponding operator edges.
  • Compare this language to the architecture notes on systems and execution.

API Reference Jump

See flux.operators.context.OperatorContext.