Separation and Fractionation
Culsma separates material with sep(...), selects source-local separation portions inside transfer statements, and creates ordered fractions with frac(...).
Both forms take a sample and a program.
sep(...)
sep(...) splits one container into a fixed two-slot group.
let g = sep(sample = lysate, program = centrifuge_program(drive = 12000g));The result is a sep_container_group. Access its slots with group[0] and group[1].
output << [g[0]:40uL];When a separated portion is used only as the source of one transfer, write the selection directly on the source container:
waste << [sample.partition(magnetic_program(duration = 5min))[1]:120uL];source.partition(program)[i] uses the same two-slot separation semantics as sep(...), but it is not a group-producing operation. It is valid only inside the right-hand source list of <<. Use sep(...) when the separated slots need names, later reuse, or multiple downstream operations.
Separation Programs
sep(...) and source.partition(program)[i] accept mechanism-specific binary separation programs.
| Program | Required fields | Optional fields | Meaning |
|---|---|---|---|
centrifuge_program(...) | drive | keep_source | Centrifuge separation. |
magnetic_program(...) | none | duration, device | Magnetic separation. |
disrupt_program(...) | none | duration | Disruption or lysis-oriented separation. |
field_program(...) | field | duration | Field-driven separation. |
filtration_program(...) | membrane, drive | none | Filtration separation. |
phase_partition_program(...) | solvent | none | Phase partition. |
precipitation_program(...) | reagent | duration | Precipitation. |
centrifuge_program(...) does not take duration. To express "centrifuge for 5 min at 4C", put the sep(...) inside an environment block:
with env(thermal = 4C, duration = 5min) {
let g = sep(sample = lysate, program = centrifuge_program(drive = 12000g));
}Slot Meaning
The two slots have stable meanings determined by the program.
| Program | group[0] | group[1] |
|---|---|---|
centrifuge_program | supernatant | pellet |
magnetic_program | bound | flowthrough |
disrupt_program | lysate | debris_or_residue |
field_program | target_band_fraction | non_target_fraction |
filtration_program | filtrate | retentate |
phase_partition_program | target_phase | other_phase |
precipitation_program | precipitate | supernatant |
For source-local selection, the index has the same meaning: sample.partition(program)[0] selects the slot described in the group[0] column, and sample.partition(program)[1] selects the slot described in the group[1] column.
Material Partition Estimates
Runtime material compute estimates how much tracked content moves into each slot. This estimate is bookkeeping for protocol execution, not a scientific prediction.
Supported content classes use one of three ratio shapes:
| Ratio | Meaning |
|---|---|
0.99 / 0.01 | Near-complete separation into one slot. |
0.95 / 0.05 | Strong enrichment with a small carryover. |
0.50 / 0.50 | Conservative fallback when Culsma cannot infer a better partition. |
Unsupported, unknown, compatibility-normalized, or composite content can still run. In those cases the runtime uses the conservative 0.50 / 0.50 fallback and emits MAT_CONTENT_PARTITION_FALLBACK.
keep_source
keep_source is only for centrifuge_program(...).
let g = sep(
sample = lysate,
program = centrifuge_program(drive = 12000g, keep_source = pellet)
);keep_source = supernatant reuses the input container for group[0]. keep_source = pellet reuses the input container for group[1].
If keep_source is omitted, both slots are new containers.
frac(...)
frac(...) creates an ordered fraction group.
let fg = frac(
sample = tube,
program = density_gradient_program(axis = "density", order = "top_to_bottom", bins = 8)
);The result is a fraction_group. Access slots with fg[0], fg[1], and so on.
Fractionation Programs
| Program | Required fields | Meaning |
|---|---|---|
density_gradient_program(...) | axis, order, bins | Density or layer-based fractionation. |
chromatography_program(...) | axis, order, bins | Retention-order fractionation. |
Index Rules
Group indexes are zero-based.
sep_container_groupsupports onlygroup[0]andgroup[1].source.partition(program)[i]supports only static indexes0and1.fraction_group[i]must be within0 <= i < binswhenbinsis known.- Slices, multidimensional indexes, and dynamic index semantics are not part of this authoring surface.
