///|
/// Ask whether all the named chemical steps could have zero net transfer.
/// This is a joint hypothesis: ruling it out requires at least one of them,
/// not every named step, to be nonzero within the declared model.
pub(all) struct ZeroTransferClaim {
study : AliquotStudy
zero_steps : Array[String]
} derive(Debug, ToJson, FromJson)
///|
pub extend ZeroTransferClaim with @debug.Debug::{to_repr}
///|
pub extend ZeroTransferClaim with @json.ToJson::{to_json}
///|
pub extend ZeroTransferClaim with @json.FromJson::{from_json}
///|
pub(all) struct ClaimAssessment {
status : String
zero_steps : Array[String]
base : AliquotAssessment
assumed_zero : AliquotAssessment?
diagnostics : Array[String]
} derive(Debug, ToJson)
///|
pub extend ClaimAssessment with @debug.Debug::{to_repr}
///|
pub extend ClaimAssessment with @json.ToJson::{to_json}
///|
/// Challenge a specific sample-handling explanation. An inconsistent baseline
/// never proves that a process occurred; a possible history never proves it did.
pub fn challenge_zero_transfer(
claim : ZeroTransferClaim,
) -> Result[ClaimAssessment, String] {
let compiled = match compile_study(claim.study) {
Ok(c) => c
Err(error) => return Err(error)
}
if claim.zero_steps.length() == 0 {
return Err(
"name at least one chemical step whose net transfer is claimed zero",
)
}
let names : Array[String] = []
let rows = compiled.rows.copy()
for id in claim.zero_steps {
if names.contains(id) {
return Err("zero-transfer step ids must be unique")
}
names.push(id)
let steps = claim.study.steps.filter(fn(s) { s.id == id })
if steps.length() != 1 ||
!["co2", "calcite", "hcl", "naoh"].contains(steps[0].kind) {
return Err(
"zero-transfer claims require existing co2/calcite/hcl/naoh steps",
)
}
let dose = input_form(
"step:" + id,
steps[0].amount_nmol,
compiled.parameters,
)
rows.push(inequality("claim:zero:" + id + ":upper", dose, r(0)))
rows.push(
inequality("claim:zero:" + id + ":lower", scale(dose, r(-1)), r(0)),
)
}
let base = assess_compiled(compiled)
if base.status != "Compatible" {
return Ok({
status: if base.status == "Conflict" {
"BaseConflict"
} else {
"ResourceLimit"
},
zero_steps: names,
base,
assumed_zero: None,
diagnostics: [
"The baseline has no established compatible history. Do not attribute its disagreement to a named process.",
],
})
}
let tested = assess_compiled({ ..compiled, rows, })
let (status, diagnostic) = match tested.status {
"Compatible" =>
(
"ZeroTransferPossible", "One shared history satisfies every assay with ALL named transfers zero. This does not establish that the real transfers were zero.",
)
"Conflict" =>
(
"ZeroTransferRuledOut", "The baseline fits, but ALL named transfers being zero is impossible. At least one named step must be nonzero within this declared model; this does not identify every step or establish physical causation.",
)
_ => ("ResourceLimit", "The zero-transfer hypothesis remains undecided.")
}
Ok({
status,
zero_steps: names,
base,
assumed_zero: Some(tested),
diagnostics: [
diagnostic, "Conditional on exact declared volumes, complete counterions, the listed process alternatives and inclusive assay bounds. Net zero does not mean no forward/reverse reaction.",
],
})
}