Skip to content

Step 6 — Maxwell Walkthrough

Capability

Read a real example through the same operator and execution language used in the earlier steps.

Problem Statement

The tutorial sequence should end at a real shipped example, not a docs-only toy abstraction. That is where the teaching story proves it is actually the same language as the codebase.

Mathematical Idea

Treat the Maxwell example as a composed system, not as a magical monolith. Identify the mesh, cochains, operators, execution object, and listeners.

Flux Concepts

  • Example families are clients of the library, not a separate framework with different nouns.
  • The runnable surface lives under examples/, not inside src/.
  • The same System plus Evolution split still applies.

Minimal Runnable Snippet

Excerpt from the Maxwell example module:

const std = @import("std"); const flux = @import("flux"); const common = @import("examples_common"); const system_mod = @import("system.zig"); pub const system_api = system_mod; const SnapshotCadence = union(enum) { disabled, interval: u32, frames: u32, }; pub fn CavityConfig(comptime dim: u8) type { return struct { steps: u32 = 1000, counts: [dim]u32 = defaultCounts(dim), extents: [dim]f64 = @splat(1.0), courant: f64 = 0.1, time_step_override: ?f64 = null, output_dir: ?[]const u8 = null, snapshot_cadence: SnapshotCadence = defaultSnapshotCadence(dim), reference: bool = false, boundary: system_mod.BoundaryCondition = .pec, pub fn timeStep(self: @This()) f64 { if (self.time_step_override) |value| return value; return self.courant * minSpacing(dim, self.counts, self.extents); } pub fn snapshotInterval(self: @This()) ?u32 { if (self.output_dir == null) return null; return switch (self.snapshot_cadence) { .disabled => null, .interval => |value| value, .frames => |value| @max(@as(u32, 1), common.framesToInterval(self.steps, value)), }; } pub fn cavityOptions(self: @This()) system_mod.CavityOptions(dim) { return .{ .extents = self.extents, .time_step = self.timeStep(), .boundary = self.boundary, }; } pub fn measurementProvider(self: @This()) system_mod.CavityMeasurementProvider(dim) { return .{ .extents = self.extents, .time_step = self.timeStep(), }; } pub fn snapshotBaseName(self: @This()) []const u8 { return if (self.reference) "cavity_reference" else "cavity"; } }; } pub const DipoleConfig2D = struct { steps: u32 = 1000, counts: [2]u32 = .{ 32, 32 }, extents: [2]f64 = .{ 1.0, 1.0 }, courant: f64 = 0.1, time_step_override: ?f64 = null, frequency_hz: f64 = 0.0, amplitude: f64 = 1.0, output_dir: ?[]const u8 = null, snapshot_cadence: SnapshotCadence = .{ .frames = 100 }, boundary: system_mod.BoundaryCondition = .pec, pub fn timeStep(self: @This()) f64 { if (self.time_step_override) |value| return value; return self.courant * minSpacing(2, self.counts, self.extents); } pub fn sourceFrequency(self: @This()) f64 { if (self.frequency_hz != 0.0) return self.frequency_hz; return 1.0 / (2.0 * self.extents[0]); } pub fn snapshotInterval(self: @This()) ?u32 { if (self.output_dir == null) return null; return switch (self.snapshot_cadence) { .disabled => null, .interval => |value| value, .frames => |value| @max(@as(u32, 1), common.framesToInterval(self.steps, value)), }; } pub fn dipoleOptions(self: @This()) system_mod.DipoleOptions(2) { return .{ .center = .{ 0.5 * self.extents[0], 0.5 * self.extents[1] }, .frequency_hz = self.sourceFrequency(), .amplitude = self.amplitude, .boundary = self.boundary, }; } }; pub fn RunResult(comptime Summary: type) type { return struct { elapsed_s: f64, snapshot_count: u32, summary: Summary, }; } pub const CavitySummary = struct { energy_final: f64, electric_l2_final: ?f64 = null, magnetic_l2_final: ?f64 = null, }; pub fn runDipole( allocator: std.mem.Allocator, config: DipoleConfig2D, writer: *std.Io.Writer, ) !RunResult(CavitySummary) {

Excerpt from the umbrella CLI surface:

const std = @import("std"); const diffusion = @import("diffusion.zig"); const euler = @import("euler.zig"); const maxwell = @import("maxwell.zig"); pub const Subcommand = struct { name: []const u8, summary: []const u8, run: *const fn (allocator: std.mem.Allocator, args: []const [:0]const u8) anyerror!void, }; pub const subcommands = [_]Subcommand{ .{ .name = diffusion.name, .summary = diffusion.summary, .run = diffusion.run }, .{ .name = euler.name, .summary = euler.summary, .run = euler.run }, .{ .name = maxwell.name, .summary = maxwell.summary, .run = maxwell.run }, }; test { std.testing.refAllDeclsRecursive(@This()); }

Expected Result

By this point the code should read as a larger version of the earlier steps: typed spaces, assembled operators, and an execution layer with listeners and output.

Possible Extensions

  • Repeat the same read-through for diffusion and Euler.
  • Add a new teaching page for one concrete conserved quantity in Maxwell.
  • Once 3D Maxwell is stable, add a second “full example” tutorial rather than mutating this one into a catch-all.

API Reference Jump

Start from flux.evolution and then jump back into the example sources.