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.
OperatorContextassembles 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.