Skip to content

Spin compartments and spin selection #777

@JanWP

Description

@JanWP

Feature Request

Hi,

I would like to get your thoughts on a small extension to Phantom: keeping explicit per-spin compartment labels, and adding helpers to select spins by those labels. I'm looking for early feedback, this is far from a finished PR proposal. Mostly I'd like to know whether the idea feels useful to anyone but me and whether you think the rough API direction fits in with the rest of KomaMRI.

Why?

I have started to think about this in the context of a larger project to make spin properties a bit more flexible, but then I thought this might be useful for the current simulators as well.

Several phantom constructors already use tissue, material, species, or region labels internally to assign contrast parameters. After construction, that membership is usually lost and downstream code only sees spin-indexed vectors.

Keeping those labels on the phantom could make a few workflows more direct:

  • tissue-specific editing of contrast parameters;
  • ROI extraction and debugging;
  • plotting or summarizing spins by tissue/material;
  • defining regional motion or other spin-subset operations;
  • preserving documented species/pool metadata from imported phantom formats.

Rough Model

The idea is to add static per-spin compartment labels to Phantom.

Compartments are discrete labels only. They do not define contrast values, motion, or simulation behavior. A label identifies membership in a tissue, material, species, region, or pool such as :GM, :WM, :fat, :blood, or :myocardium. For now, I would keep this simple: exactly one compartment label per spin.

When no labels are provided, existing behavior is preserved. We should probably assigning all spins to a default compartment in that case, tentatively :default.

My current thought is to normalize labels internally to Symbols, while accepting both Symbol and AbstractString at public boundaries. For built-in phantoms, I would reuse labels already present in source tables or masks. I think a global KomaMRI label ontology is out-of-scope, at least for now.

Fit With Existing Code

Labels should live on Phantom, since they are spin-aligned sample metadata. They would follow the usual phantom operations:

  • obj[p] and @view obj[p];
  • copy;
  • equality;
  • obj1 + obj2;
  • precision conversion without numeric conversion of labels;
  • no transfer to GPU memory unless future simulation methods consume it;
  • .phantom file read/write, with old files defaulting to :default.

Current GPU simulation kernels consume numeric spin fields and motion, not compartment labels. If labels are needed to choose a spin subset, that selection should be resolved before backend transfer. We could keep the field present but treat it as a non-transferred leaf to avoid surprising manual gpu(obj) users while avoiding unnecessary GPU memory use.

How to perform spin selection

Existing SpinSpan, AllSpins, and SpinRange would remain unchanged. They are already-resolved index selectors: once constructed, they contain the spin indices or index range to use.

A compartment label such as :GM is different because it is unresolved without a specific phantom’s compartment metadata. To use the compartment labels to select a subset of spins I am leaning toward explicit helpers, because they make the label-to-index resolution visible:

idx = spin_indices(obj, :GM)
obj_gm = obj[idx]

sel = spin_selector(obj, :GM)
motion = translate(..., sel)

spin_indices returns concrete indices and is useful immediately, including for irregular tissue masks.

spin_selector could return existing AbstractSpinSpan values where the selection is exactly representable, for example AllSpins() or SpinRange. Irregular selections probably need a future arbitrary-index span type. I think that part would need more discussion before coming up with an API.

Overloading indexing may be a bit too invasive in adding a new meaning for the indexing operation and it couldn't be used for motion. So I'd avoid:

sel = obj[:GM]

Possible PR Split

If this seems worth pursuing, I imagine splitting it into small PRs:

  1. Add compartment storage to Phantom, preserve all existing constructors and operations, and persist labels in .phantom files.
  2. Add spin_indices and possibly a limited spin_selector for selections representable by existing span types.
  3. Populate compartments in built-in phantoms and external readers where labels are already documented or implicit and unambiguous.

Questions

The obvious one is: Do you think compartments and per-compartment selection would be a useful addition to KomaMRI? I have briefly checked existing feature requests and have not found anything.

Do you see anything wrong with the direction I took in thinking about this? Any obvious design flaws?

And then there are the technicalities:

  • Did I forget any phantom operations we need to handle?
  • Do you think symbols are a good fit to store labels internally while accepting strings at API and file-I/O boundaries?
  • Should the first PR include only spin_indices, or also a limited spin_selector? Or do you prefer a larger PR once everything is in?
  • Is a future arbitrary-index AbstractSpinSpan desirable for irregular compartment selections, especially for motion APIs?
  • Does treating compartments as CPU-side leaf metadata during GPU transfer match expectations for gpu(obj)?
  • What are the requirements for backwards compatibility of the .phantom files? Would this require a version bump on KomaMRIFiles?

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions