Skip to content

feat(grids): active-cell mask so stencil operators can skip inactive cells #61

Description

@kylebeggs

What's missing

MFO has no domain masking. Every mask in src/ is bounds-masking inside adjoint gathers (_maskedget, src/operators/abstract.jl:210) — there is no active-cell indicator, no irregular-geometry / cut-cell support, and no Field of Bool that participates in stencils. BlockForest refinement changes resolution, not participation, so AMR is not a substitute.

Why (downstream)

The microvascular transport model solves a reaction–diffusion system on a hemisphere of tissue embedded in a Cartesian box. Cells outside the hemisphere must be excluded from both the reaction and the diffusion stencil. Today the downstream package carries a per-cell active::Vector that it multiplies into every reaction term by hand, because a masked operator does not exist.

Getting this wrong is not cosmetic: leaving the box corners active created a spurious O₂ sink outside the domain that broke the radial-O₂ comparator entirely. The mask is load-bearing physics, and it currently lives downstream where the operator algebra can't see it.

Sketch

g = CartesianGrid(...; bc = ((Neumann(), Neumann()), ...))
active = scalar_field(Bool, g); set!(active, inside_dome)
L = laplacian(g; active)          # inactive cells contribute no flux and get no update

The interesting constraints:

  • Adjoint correctness. Whatever apply! does, apply_adjoint! has to remain its exact discrete transpose — masking a face flux must mask the same face on the way back, or ⟨Lx,y⟩ = ⟨x,Lᵀy⟩ breaks. That's the test that should gate this.
  • operator_diagonal. An inactive neighbour looks like a no-flux face, so the diagonal correction is the same (-2 + bc_sign)h⁻² form already handled for BC faces — but it now varies cell to cell, which the current implementation doesn't allow for.
  • Singularity. A fully masked-off region turns the operator singular, which matters for the multigrid coarsest-level LU (_build_coarsest, src/multigrid.jl:288) and for coarsening a mask consistently.

Scope note

Uniform CartesianGrid only would already unblock the downstream use. Masked BlockForest / distributed slabs can stay out of scope — same boundary you drew for Diffusion in #54.


🤖 Filed by Claude, who read the code so Kyle didn't have to scroll.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions