Skip to content

Commit 7ffbeec

Browse files
committed
essentially done with lmfit as fitter
modified: src/egui_plot_stuff/egui_line.rs modified: src/fitter/common.rs modified: src/fitter/fit_handler.rs modified: src/fitter/fit_settings.rs modified: src/fitter/main_fitter.rs modified: src/fitter/models/exponential.rs modified: src/fitter/models/gaussian.rs modified: src/fitter/models/linear.rs modified: src/fitter/models/mod.rs modified: src/fitter/models/powerlaw.rs modified: src/fitter/models/quadratic.rs modified: src/histoer/histo1d/histogram1d.rs modified: src/histogram_scripter/manual_histogram_script.rs renamed: lmfit_test.py -> tests/lmfit_test.py
1 parent c3f963e commit 7ffbeec

File tree

14 files changed

+963
-375
lines changed

14 files changed

+963
-375
lines changed

src/egui_plot_stuff/egui_line.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@ impl EguiLine {
6161
}
6262

6363
pub fn new_with_points(points: Vec<[f64; 2]>) -> Self {
64-
let mut line = EguiLine::default();
65-
line.points = points;
66-
line
64+
EguiLine {
65+
points,
66+
..EguiLine::default()
67+
}
6768
}
6869

6970
pub fn clear_points(&mut self) {

src/fitter/common.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#[derive(PartialEq, Debug, Clone, serde::Deserialize, serde::Serialize)]
1+
#[derive(PartialEq, Default, Debug, Clone, serde::Deserialize, serde::Serialize)]
22
pub struct Data {
33
pub x: Vec<f64>,
44
pub y: Vec<f64>,
@@ -12,7 +12,7 @@ pub struct Parameter {
1212
pub initial_guess: f64,
1313
pub vary: bool,
1414
pub value: Option<f64>,
15-
pub uncertainity: Option<f64>,
15+
pub uncertainty: Option<f64>,
1616
}
1717

1818
impl Default for Parameter {
@@ -24,7 +24,7 @@ impl Default for Parameter {
2424
initial_guess: 0.0,
2525
vary: true,
2626
value: None,
27-
uncertainity: None,
27+
uncertainty: None,
2828
}
2929
}
3030
}
@@ -62,7 +62,7 @@ impl Parameter {
6262
if let Some(value) = self.value {
6363
ui.separator();
6464
ui.label(format!("{:.3}", value));
65-
ui.label(format!("{:.3}", self.uncertainity.unwrap_or(0.0)));
65+
ui.label(format!("{:.3}", self.uncertainty.unwrap_or(0.0)));
6666
}
6767
}
6868
}

src/fitter/fit_handler.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -171,18 +171,21 @@ impl Fits {
171171
ui.label("Mean");
172172
ui.label("FWHM");
173173
ui.label("Area");
174+
ui.label("Amplitude");
175+
ui.label("Sigma");
176+
174177
ui.end_row();
175178

176179
if self.temp_fit.is_some() {
177-
ui.label("Current");
180+
ui.label("Temp");
178181

179-
// if let Some(temp_fit) = &self.temp_fit {
180-
// temp_fit.fitter_stats(ui);
181-
// }
182+
if let Some(temp_fit) = &mut self.temp_fit {
183+
temp_fit.fitter_stats(ui, true);
184+
}
182185
}
183186

184187
if !self.stored_fits.is_empty() {
185-
for (i, fit) in self.stored_fits.iter().enumerate() {
188+
for (i, fit) in self.stored_fits.iter_mut().enumerate() {
186189
ui.horizontal(|ui| {
187190
ui.label(format!("{}", i));
188191

@@ -194,7 +197,7 @@ impl Fits {
194197

195198
ui.separator();
196199
});
197-
// fit.fitter_stats(ui);
200+
fit.fitter_stats(ui, true);
198201
}
199202
}
200203
});
@@ -236,6 +239,10 @@ impl Fits {
236239
if let Some(temp_fit) = &mut self.temp_fit {
237240
temp_fit.fit_result_ui(ui);
238241
}
242+
243+
for fit in &mut self.stored_fits {
244+
fit.fit_result_ui(ui);
245+
}
239246
});
240247
}
241248
}

src/fitter/fit_settings.rs

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -59,29 +59,8 @@ impl FitSettings {
5959
ui.separator();
6060

6161
ui.horizontal(|ui| {
62-
ui.label("Show Fit Lines: ");
63-
ui.checkbox(&mut self.show_decomposition, "Decomposition")
64-
.on_hover_text("Show the decomposition peaks");
65-
ui.checkbox(&mut self.show_composition, "Composition")
66-
.on_hover_text("Show the composition line");
67-
ui.checkbox(&mut self.show_background, "Background")
68-
.on_hover_text("Show the background line");
69-
});
62+
ui.label("Background Models");
7063

71-
ui.separator();
72-
73-
ui.heading("Gaussian Fit Settings");
74-
ui.horizontal(|ui| {
75-
ui.checkbox(&mut self.equal_stddev, "Equal Standard Deviation")
76-
.on_hover_text("Allow the standard deviation of the Gaussian to be free");
77-
ui.checkbox(&mut self.free_position, "Free Position")
78-
.on_hover_text("Allow the position of the Gaussian to be free");
79-
});
80-
81-
ui.separator();
82-
83-
ui.heading("Background Models");
84-
ui.horizontal(|ui| {
8564
ui.radio_value(
8665
&mut self.background_model,
8766
BackgroundModel::Linear(self.linear_params.clone()),
@@ -126,5 +105,27 @@ impl FitSettings {
126105
}
127106

128107
ui.separator();
108+
109+
ui.horizontal(|ui| {
110+
ui.label("Gaussian Fit Settings");
111+
ui.checkbox(&mut self.equal_stddev, "Equal Standard Deviation")
112+
.on_hover_text("Allow the standard deviation of the Gaussian to be free");
113+
ui.checkbox(&mut self.free_position, "Free Position")
114+
.on_hover_text("Allow the position of the Gaussian to be free");
115+
});
116+
117+
ui.separator();
118+
119+
ui.horizontal(|ui| {
120+
ui.label("Show Fit Lines: ");
121+
ui.checkbox(&mut self.show_decomposition, "Decomposition")
122+
.on_hover_text("Show the decomposition peaks");
123+
ui.checkbox(&mut self.show_composition, "Composition")
124+
.on_hover_text("Show the composition line");
125+
ui.checkbox(&mut self.show_background, "Background")
126+
.on_hover_text("Show the background line");
127+
});
128+
129+
ui.separator();
129130
}
130131
}

src/fitter/main_fitter.rs

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use crate::egui_plot_stuff::egui_line::EguiLine;
2-
use super::models::gaussian::{GaussianFitter};
31
use super::common::Data;
42
use super::models::exponential::{ExponentialFitter, ExponentialParameters};
3+
use super::models::gaussian::GaussianFitter;
54
use super::models::linear::{LinearFitter, LinearParameters};
65
use super::models::powerlaw::{PowerLawFitter, PowerLawParameters};
76
use super::models::quadratic::{QuadraticFitter, QuadraticParameters};
7+
use crate::egui_plot_stuff::egui_line::EguiLine;
88

99
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq)]
1010
pub enum FitModel {
@@ -34,6 +34,17 @@ pub enum BackgroundResult {
3434
Exponential(ExponentialFitter),
3535
}
3636

37+
impl BackgroundResult {
38+
pub fn get_fit_points(&self) -> Vec<[f64; 2]> {
39+
match self {
40+
BackgroundResult::Linear(fit) => fit.fit_points.clone(),
41+
BackgroundResult::Quadratic(fit) => fit.fit_points.clone(),
42+
BackgroundResult::PowerLaw(fit) => fit.fit_points.clone(),
43+
BackgroundResult::Exponential(fit) => fit.fit_points.clone(),
44+
}
45+
}
46+
}
47+
3748
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
3849
pub struct Fitter {
3950
pub name: String,
@@ -66,12 +77,12 @@ impl Fitter {
6677
fit_result: None,
6778

6879
background_line: EguiLine::new(egui::Color32::GREEN),
69-
composition_line: EguiLine::new(egui::Color32::DARK_BLUE),
80+
composition_line: EguiLine::new(egui::Color32::BLUE),
7081
decomposition_lines: Vec::new(),
7182
}
7283
}
7384

74-
pub fn fit(&mut self){
85+
pub fn fit(&mut self) {
7586
match &self.fit_model {
7687
FitModel::Gaussian(peak_markers, equal_stdev, free_position, bin_width) => {
7788
let mut fit = GaussianFitter::new(
@@ -87,6 +98,19 @@ impl Fitter {
8798
match fit.lmfit() {
8899
Ok(_) => {
89100
self.composition_line.points = fit.fit_points.clone();
101+
for fit in &fit.fit_result {
102+
let mut line = EguiLine::new(egui::Color32::from_rgb(150, 0, 255));
103+
line.points = fit.fit_points.clone();
104+
self.decomposition_lines.push(line);
105+
}
106+
107+
if self.background_result.is_none() {
108+
if let Some(background_result) = &fit.background_result {
109+
self.background_line.points = background_result.get_fit_points();
110+
self.background_result = Some(background_result.clone());
111+
}
112+
}
113+
90114
self.fit_result = Some(FitResult::Gaussian(fit));
91115
}
92116
Err(e) => {
@@ -98,7 +122,7 @@ impl Fitter {
98122
log::info!("No fitting required for 'None'");
99123
}
100124
}
101-
}
125+
}
102126

103127
pub fn fit_background(&mut self) {
104128
log::info!("Fitting background");
@@ -178,6 +202,20 @@ impl Fitter {
178202
log::info!("Finished fitting background");
179203
}
180204

205+
pub fn get_peak_markers(&self) -> Vec<f64> {
206+
if self.fit_result.is_none() {
207+
match &self.fit_model {
208+
FitModel::Gaussian(peak_markers, _, _, _) => peak_markers.clone(),
209+
FitModel::None => Vec::new(),
210+
}
211+
} else {
212+
match &self.fit_result {
213+
Some(FitResult::Gaussian(fit)) => fit.peak_markers.clone(),
214+
None => Vec::new(),
215+
}
216+
}
217+
}
218+
181219
pub fn set_background_color(&mut self, color: egui::Color32) {
182220
self.background_line.color = color;
183221
}
@@ -214,21 +252,14 @@ impl Fitter {
214252
}
215253

216254
self.background_line.name = format!("{}-Background", name);
255+
self.name = name;
217256
}
218257

219258
pub fn fit_result_ui(&mut self, ui: &mut egui::Ui) {
220259
ui.collapsing(self.name.clone(), |ui| {
221260
egui::ScrollArea::vertical()
222261
.min_scrolled_height(300.0)
223262
.show(ui, |ui| {
224-
if let Some(fit_result) = &self.fit_result {
225-
self.composition_line.menu_button(ui);
226-
}
227-
228-
for line in &mut self.decomposition_lines {
229-
line.menu_button(ui);
230-
}
231-
232263
ui.separator();
233264

234265
if let Some(background_result) = &self.background_result {
@@ -252,10 +283,43 @@ impl Fitter {
252283
self.background_line.menu_button(ui);
253284
});
254285
}
286+
287+
if self.fit_result.is_some() {
288+
egui::Grid::new("fit_params_grid")
289+
.striped(true)
290+
.show(ui, |ui| {
291+
ui.label("Peak");
292+
ui.label("Mean");
293+
ui.label("FWHM");
294+
ui.label("Area");
295+
ui.label("Amplitude");
296+
ui.label("Sigma");
297+
298+
ui.end_row();
299+
300+
self.fitter_stats(ui, false);
301+
});
302+
303+
for line in &mut self.decomposition_lines {
304+
line.menu_button(ui);
305+
}
306+
307+
self.composition_line.menu_button(ui);
308+
}
255309
});
256310
});
257311
}
258312

313+
pub fn fitter_stats(&mut self, ui: &mut egui::Ui, skip_one: bool) {
314+
if let Some(fit_result) = &self.fit_result {
315+
match fit_result {
316+
FitResult::Gaussian(fit) => {
317+
fit.fit_params_ui(ui, skip_one);
318+
}
319+
}
320+
}
321+
}
322+
259323
// Draw the background, decomposition, and composition lines
260324
pub fn draw(&self, plot_ui: &mut egui_plot::PlotUi) {
261325
for line in &self.decomposition_lines {

src/fitter/models/exponential.rs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ impl Default for ExponentialParameters {
1616
},
1717
decay: Parameter {
1818
name: "decay".to_string(),
19+
initial_guess: 500.0,
1920
..Default::default()
2021
},
2122
}
@@ -66,6 +67,39 @@ impl ExponentialFitter {
6667
}
6768
}
6869

70+
pub fn new_from_parameters(
71+
amplitude: (f64, f64),
72+
decay: (f64, f64),
73+
min_x: f64,
74+
max_x: f64,
75+
) -> Self {
76+
let mut fitter = ExponentialFitter {
77+
data: Data::default(),
78+
paramaters: ExponentialParameters::default(),
79+
fit_points: Vec::new(),
80+
fit_report: "Fitter with other model".to_string(),
81+
};
82+
83+
// Set the parameter values and uncertainties
84+
fitter.paramaters.amplitude.value = Some(amplitude.0);
85+
fitter.paramaters.amplitude.uncertainty = Some(amplitude.1);
86+
fitter.paramaters.decay.value = Some(decay.0);
87+
fitter.paramaters.decay.uncertainty = Some(decay.1);
88+
89+
// Optionally generate fit points based on the x-range (min_x to max_x)
90+
let num_points = 100;
91+
let step_size = (max_x - min_x) / (num_points as f64);
92+
fitter.fit_points.clear();
93+
for i in 0..=num_points {
94+
let x = min_x + i as f64 * step_size;
95+
let y = fitter.paramaters.amplitude.value.unwrap_or(1.0)
96+
* (-x / fitter.paramaters.decay.value.unwrap_or(1.0)).exp();
97+
fitter.fit_points.push([x, y]);
98+
}
99+
100+
fitter
101+
}
102+
69103
pub fn lmfit(&mut self) -> PyResult<()> {
70104
log::info!("Fitting data with a Exponential line using `lmfit`.");
71105
Python::with_gil(|py| {
@@ -177,9 +211,9 @@ def ExponentialFit(x_data: list, y_data: list, amplitude: list = ("amplitude", -
177211
let fit_report = result.get_item(3)?.extract::<String>()?;
178212

179213
self.paramaters.amplitude.value = Some(params[0].1);
180-
self.paramaters.amplitude.uncertainity = Some(params[0].2);
214+
self.paramaters.amplitude.uncertainty = Some(params[0].2);
181215
self.paramaters.decay.value = Some(params[1].1);
182-
self.paramaters.decay.uncertainity = Some(params[1].2);
216+
self.paramaters.decay.uncertainty = Some(params[1].2);
183217

184218
self.fit_points = x.iter().zip(y.iter()).map(|(&x, &y)| [x, y]).collect();
185219
self.fit_report = fit_report;
@@ -195,15 +229,15 @@ def ExponentialFit(x_data: list, y_data: list, amplitude: list = ("amplitude", -
195229
ui.label(format!(
196230
"amplitude: {:.3} ± {:.3}",
197231
amplitude,
198-
self.paramaters.amplitude.uncertainity.unwrap_or(0.0)
232+
self.paramaters.amplitude.uncertainty.unwrap_or(0.0)
199233
));
200234
}
201235
ui.separator();
202236
if let Some(decay) = &self.paramaters.decay.value {
203237
ui.label(format!(
204238
"decay: {:.3} ± {:.3}",
205239
decay,
206-
self.paramaters.decay.uncertainity.unwrap_or(0.0)
240+
self.paramaters.decay.uncertainty.unwrap_or(0.0)
207241
));
208242
}
209243
ui.separator();

0 commit comments

Comments
 (0)