///|
/// Uniform 2theta grid, density per degree; finite window is not renormalized.
pub struct Profile {
start : Double
step : Double
values : Array[Double]
} derive(Debug)
///|
/// Gaussian lines with unit continuous area. Requires >=4 samples per sigma.
pub fn gaussian_profile(
peaks : Array[Peak],
start : Double,
end : Double,
samples : Int,
sigma : Double,
max_terms? : Int = 2000000,
) -> Result[Profile, Problem] {
if !finite(start) ||
!finite(end) ||
start < 0.0 ||
end > 180.0 ||
end <= start ||
samples < 2 ||
samples > 100000 {
return Err(Invalid("invalid 2theta grid"))
}
if !finite(sigma) || sigma <= 0.0 || sigma > 180.0 {
return Err(Invalid("invalid Gaussian sigma"))
}
let step = (end - start) / (samples - 1).to_double()
if step > sigma / 4.0 {
return Err(Invalid("grid undersamples Gaussian: step > sigma/4"))
}
if max_terms <= 0 ||
max_terms > 20000000 ||
samples.to_int64() * peaks.length().to_int64() > max_terms.to_int64() {
return Err(Budget("profile work exceeds budget"))
}
let values = Array::make(samples, 0.0)
let norm = 1.0 / (sigma * (2.0 * @math.PI).sqrt())
for n = 0; n < samples; n = n + 1 {
let angle = start + n.to_double() * step
let mut sum = 0.0
for p in peaks {
let z = (angle - p.angle) / sigma
sum = sum + p.intensity * norm * @math.exp(-0.5 * z * z)
}
values[n] = sum
}
Ok({ start, step, values, })
}
///|
/// Trapezoidal integral over the sampled window only.
pub fn Profile::area(self : Profile) -> Double {
let mut sum = 0.0
for n = 1; n < self.values.length(); n = n + 1 {
sum = sum + (self.values[n - 1] + self.values[n]) * 0.5 * self.step
}
sum
}