///|
/// Numeric CSV only, no free-text labels and no user formula cells.
pub fn reflections_csv(
reflections : Array[Reflection],
) -> Result[String, Problem] {
if reflections.length() > 100000 {
return Err(Budget("CSV reflection count exceeds 100000"))
}
let lines = ["h,k,l,d_angstrom,intensity,two_theta_degree"]
for r in reflections {
let angle = match r.angle {
Some(x) => x.to_string()
None => ""
}
lines.push(
"\{r.index.h},\{r.index.k},\{r.index.l},\{r.d},\{r.intensity},\{angle}",
)
}
Ok(lines.join("\n") + "\n")
}
///|
/// Versioned structured output; null angles explicitly mark inaccessible reflections.
pub fn reflections_json(
reflections : Array[Reflection],
) -> Result[Json, Problem] {
if reflections.length() > 100000 {
return Err(Budget("JSON reflection count exceeds 100000"))
}
let rows : Array[Json] = reflections.map(r => {
let angle : Json = match r.angle {
Some(x) => x.to_json()
None => Json::null()
}
{
"h": r.index.h,
"k": r.index.k,
"l": r.index.l,
"d_angstrom": r.d,
"intensity": r.intensity,
"wavelength_angstrom": r.wavelength,
"two_theta_degree": angle,
}
})
Ok({
"format": "moondiffraction.reflections.v1",
"q_convention": "1/d; no 2pi",
"model": "real constant scatterers",
"reflections": rows,
})
}
///|
/// Profile density per degree, not counts per bin.
pub fn Profile::csv(self : Profile) -> String {
let lines = ["two_theta_degree,intensity_per_degree"]
for n = 0; n < self.values.length(); n = n + 1 {
let angle = self.start + n.to_double() * self.step
lines.push("\{angle},\{self.values[n]}")
}
lines.join("\n") + "\n"
}