inital commit

This commit is contained in:
2024-05-17 15:26:56 +02:00
commit 6c54c16f87
15 changed files with 6315 additions and 0 deletions

1
bachelorarbeit/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

3934
bachelorarbeit/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

22
bachelorarbeit/Cargo.toml Normal file
View File

@@ -0,0 +1,22 @@
[package]
name = "bachelorarbeit"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
gauss-quad = "0.1.8"
num = "0.4.2"
egui = "0.27.2"
egui_extras = "0.27.2"
eframe = "0.27.2"
egui_plot = "0.27.2"
indicatif = "0.17.8"
nohash-hasher = "0.2.0"
[profile.release]
debug = true
# lto = "fat"
# codegen-units = 1

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 79 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 79 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

BIN
bachelorarbeit/perf.data Normal file

Binary file not shown.

Binary file not shown.

221
bachelorarbeit/src/calc.rs Normal file
View File

@@ -0,0 +1,221 @@
use core::f64::consts::PI;
use gauss_quad::GaussLegendre;
use std::collections::HashMap;
use std::io;
use std::rc::Rc;
use crate::gq_storage; // This should be moved to main.rs after new archtitecture (see main.rs TODO)
pub type Complex = num::complex::Complex<f64>;
const M_ROH: f64 = 0.7736;
const M_PI: f64 = 0.13957;
pub const S0: f64 = 4.0 * M_PI * M_PI;
const M_K: f64 = 0.496;
const B0: f64 = 1.055;
const B1: f64 = 0.15;
const LAMBDA1: f64 = 1.57;
const LAMBDA2: f64 = -1.96;
const LAMBDA_HIGH: f64 = 10.0;
pub const EPSILON: f64 = 1e-15;
const S_MID_CUTOFF: f64 = 1.42 * 1.42;
const INF: f64 = 1e5;
#[derive(Default)]
pub struct Cache {
pub gauss_quad_lut: HashMap<u32, (Rc<[f64]>, Rc<[f64]>), nohash_hasher::BuildNoHashHasher<u32>>,
pub delta_lut: HashMap<u64, f64, nohash_hasher::BuildNoHashHasher<u64>>,
pub omnes_lut: HashMap<u32, HashMap<u64, Complex, nohash_hasher::BuildNoHashHasher<u64>>, nohash_hasher::BuildNoHashHasher<u32>> // First key is n, second is s
}
impl Cache {
pub fn from_file(filepath: &str) -> io::Result<Cache> {
let me = Cache{
gauss_quad_lut: gq_storage::deserialize(filepath, |_| true)?.into_iter()
.map(|(roots, weights)| (u32::try_from(roots.len()).unwrap(), (Rc::from(roots), Rc::from(weights)))).collect(),
delta_lut: HashMap::with_hasher(Default::default()),
omnes_lut: HashMap::with_hasher(Default::default())
};
Ok(me)
}
}
// derivated values
fn lambda_0() -> f64 {
delta((2.0*M_K).powi(2))
}
fn delta_mid_cutoff() -> f64 {
delta(S_MID_CUTOFF)
}
fn atan_shift(x: f64) -> f64 {
let atan_std: f64 = x.atan();
if atan_std < 0.0 {
atan_std + PI
} else {
atan_std
}
}
fn integrate<G:FnMut(f64) -> f64>(roots: &[f64], weights: &[f64], a:f64, b:f64, mut f: G) -> f64 {
let mut sum: f64 = 0.0;
let mut i: usize = 0;
if roots.len() != weights.len() {
panic!("Error: roots and weights are of different length");
}
while i < roots.len() {
sum += weights[i] * f(roots[i]*(b-a)/2.0 + (a+b)/2.0);
i += 1;
}
sum * (b-a)/2.0
}
fn integrate_complex<G:FnMut(f64) -> Complex>(roots: &[f64], weights: &[f64], a:f64, b:f64, mut f: G) -> Complex {
let mut sum: Complex = Complex::new(0.0,0.0);
let mut i: usize = 0;
if roots.len() != weights.len() {
panic!("Error: roots and weights are of different length");
}
while i < roots.len() {
sum += weights[i] * f(roots[i]*(b-a)/2.0 + (a+b)/2.0);
i += 1;
}
sum * (b-a)/2.0
}
fn omega(s: f64) -> f64 {
let a = s.sqrt();
let b = (1.05f64.powi(2) - s).sqrt();
(a - b) / (a + b)
}
fn k(s: f64) -> f64 {
(s/4.0 - M_PI.powi(2)).sqrt()
}
pub fn delta_with_lut(cache:&mut Cache, s: f64) -> f64 {
match cache.delta_lut.get(&s.to_bits()) {
Some(val) => {
*val
}
None => {
let val = delta(s);
cache.delta_lut.insert(s.to_bits(), val);
val
}
}
}
pub fn delta(s: f64) -> f64 {
if s <= 2.0 * M_K {
atan_shift(
(s.sqrt() / (2.0*k(s).powi(3))
* (M_ROH.powi(2) - s)
* ( (2.0*M_PI.powi(3)) / (M_ROH.powi(2) * s.sqrt()) + B0 + B1 * omega(s))
).powi(-1)
)
} else if s <= S_MID_CUTOFF {
let par = 0.5 * s.sqrt() / M_K - 1.0;
lambda_0() + LAMBDA1 * par + LAMBDA2 * par.powi(2)
} else {
PI +
(delta_mid_cutoff() - PI)
* (LAMBDA_HIGH * LAMBDA_HIGH + S_MID_CUTOFF)
/ (LAMBDA_HIGH * LAMBDA_HIGH + s)
}
}
pub fn omnes_integrand_tan(cache:&mut Cache, s_tick:f64, s:f64) -> Complex {
let sub = s_tick.tan();
(delta_with_lut(cache, sub) - delta_with_lut(cache, s))
/ (
sub
* (sub - s + Complex::new(0.0, EPSILON))
* s_tick.cos().powi(2)
)
}
pub fn omnes_integrand(cache:&mut Cache, s_tick:f64, s:f64) -> Complex {
(delta_with_lut(cache, s_tick) - delta_with_lut(cache, s))
/ (s_tick * (s_tick - s + Complex::new(0.0, EPSILON)))
}
pub fn omnes(cache:&mut Cache, s:f64, n:u32, use_tan: bool) -> Complex {
let roots: Rc<[f64]>;
let weights: Rc<[f64]>;
if let Some(inner_lut) = cache.omnes_lut.get(&n) {
if let Some(res) = inner_lut.get(&s.to_bits()) {
return *res
}
}
if !cache.omnes_lut.contains_key(&n) {
cache.omnes_lut.insert(n, HashMap::with_hasher(Default::default()));
}
match cache.gauss_quad_lut.get_mut(&n) {
Some(gq_values) => {
roots = Rc::clone(&gq_values.0);
weights = Rc::clone(&gq_values.1);
}
None => {
let gq_values = GaussLegendre::nodes_and_weights(n.try_into().unwrap());
roots = Rc::from(gq_values.0.as_slice());
weights = Rc::from(gq_values.1.as_slice());
}
}
if !cache.gauss_quad_lut.contains_key(&n) {
let gq_values = GaussLegendre::nodes_and_weights(n.try_into().unwrap());
cache.gauss_quad_lut.insert(n, (Rc::from(gq_values.0), Rc::from(gq_values.1)));
println!("n_omnes={} is not included in the lookup file. Consider generating a new lookup file via the appropriate function in gq_storage.rs", n);
}
let intgrl = if use_tan {
integrate_complex(&roots, &weights, S0.atan(), PI / 2.0, |s_tick| omnes_integrand_tan(cache, s_tick, s))
} else {
integrate_complex(&roots, &weights, S0, INF, |s_tick| omnes_integrand(cache, s_tick, s))
};
let omnes_res = (s/PI*(intgrl)).exp() * (S0 / Complex::new(S0 - s, -EPSILON)).powf(delta_with_lut(cache, s) / PI);
//cache.omnes_lut.get_mut(&n).unwrap().insert(s.to_bits(), omnes_res);
println!("{:?}", omnes_res);
omnes_res
}
pub fn phi0_integrand(cache:&mut Cache, x: f64, s: f64, n_omnes:u32, use_tan_omnes: bool) -> f64 {
let x_sub = x.tan().powi(2) + S0;
let omnes_s = omnes(cache, s, n_omnes, use_tan_omnes);
let omnes_x = omnes(cache, x_sub, n_omnes, use_tan_omnes);
(omnes_x / omnes_s).norm_sqr().ln()
/ ( (x_sub)* (x_sub-s) )
/ x.cos().powi(2) // Don't use x_sub here, since this term is from tan substitution itself
}
pub fn phi0(cache:&mut Cache, s: f64, n_omnes: u32, n_phi0: u32, use_tan_omnes: bool, use_tan_phi0: bool, use_xsub: bool) -> f64 {
if !cache.gauss_quad_lut.contains_key(&n_phi0) {
let gq_values = GaussLegendre::nodes_and_weights(n_phi0.try_into().unwrap());
cache.gauss_quad_lut.insert(n_phi0, (Rc::from(gq_values.0), Rc::from(gq_values.1)));
println!("n_omnes={} is not included in the lookup file. Consider generating a new lookup file via the appropriate function in gq_storage.rs", n_phi0);
}
let (ref roots, ref weights) = cache.gauss_quad_lut.get(&n_phi0).unwrap();
let (roots, weights) = (Rc::clone(roots), Rc::clone(weights));
let analyt = omnes(cache, s, n_omnes, use_tan_omnes).norm_sqr().ln() / S0.sqrt() / 2.0;
let intgrl = s / PI * integrate(&*roots, &*weights, 0.0, PI / 2.0, |x| phi0_integrand(cache,x,s,n_omnes, use_tan_omnes));
-(s-S0).sqrt() * (intgrl - analyt)
}
#[allow(unused)]
pub fn bit_pattern_randomness_tester(a:f64, b:f64, n_points: u64) -> Vec<f64>{
let mut modulos = vec![0.0; n_points as usize];
let x_values: Vec<u64> = (0..modulos.len() as u32).map(|x| (f64::from(x) * ((b - a) / f64::from(modulos.len() as u32)) + a).to_bits()).collect();
for i in 0..x_values.len() {
let val = (x_values[i] % n_points) as usize;
modulos[val] += 1.0;
}
modulos
}

View File

@@ -0,0 +1,84 @@
//! This module writes and reads roots and weights of gauss-quad into files, using a dedicated file format.
//! The format works as follows: One u64 is written, indicating the n of the roots and weights that follow
//! (thereby hinting at the number of floats that are following before the next u64, which is 2*n). This is repeated for
//! arbitrary n.
#![allow(dead_code)]
use std::{
fs::File,
io::{self, BufReader, BufWriter, Read, Write, Seek, SeekFrom},
};
use gauss_quad::GaussLegendre;
/// Writes roots and weights of gauss-quad into a file at `filepath`. params contains roots and weights for multiple n.
pub fn serialize(filepath: &str, params: Vec<(Vec<f64>, Vec<f64>)>) -> io::Result<()> {
let file = File::create(filepath)?;
let mut file = BufWriter::new(file);
for (roots, weights) in params.iter() {
file.write_all(&u64::try_from(roots.len()).unwrap().to_le_bytes())?;
for root in roots {
file.write_all(&root.to_bits().to_le_bytes())?;
}
for weight in weights {
file.write_all(&weight.to_bits().to_le_bytes())?;
}
}
Ok(())
}
pub fn deserialize(filepath: &str, filter: impl Fn(u64) -> bool) -> io::Result<Vec<(Vec<f64>, Vec<f64>)>> {
let file = File::open(filepath)?;
let mut file = BufReader::new(file);
let mut buf = [0u8; 8];
let mut values = Vec::new();
'outer: loop {
let res = file.read(&mut buf)?;
if res < 8 {
break;
}
let n = u64::from_le_bytes(buf);
// skips stored floats for n that should not be stored
if !filter(n) {
file.seek(SeekFrom::Current(i64::try_from(2*n*8).unwrap())).unwrap();
continue;
}
// Stores roots and weights into values Vec
let mut roots = Vec::new();
for _ in 0..n {
let res = file.read(&mut buf)?;
if res < 8 {
break 'outer;
}
roots.push(f64::from_bits(u64::from_le_bytes(buf)));
}
let mut weights = Vec::new();
for _ in 0..n {
let res = file.read(&mut buf)?;
if res < 8 {
break 'outer;
}
weights.push(f64::from_bits(u64::from_le_bytes(buf)));
}
values.push((roots, weights));
}
Ok(values)
}
pub fn fill_with_gauss_quad(n_values: Vec<u64>) {
let mut params = Vec::new();
for i in n_values {
let gq = GaussLegendre::init(usize::try_from(i).unwrap());
params.push((gq.nodes, gq.weights));
}
serialize("./gauss_quad_lut.morello", params).unwrap();
let res = deserialize("./gauss_quad_lut.morello", |_| true);
println!("{:?}", res);
}

247
bachelorarbeit/src/main.rs Normal file
View File

@@ -0,0 +1,247 @@
// TODO
// - Lookup Table implementieren -> Weights, roots schon früher rausziehen?
// -> Warum macht delta_lut alles langsamer?
// - Omnes auch komplex integrieren (geht nicht mit Integralmethode von gauss-quad)
// - Textfelder und "Berechnen"-Button in UI, um z.B. Wertebereich zu setzen
// - Neue Architketur: struct calculator, das lookups speichert -> In die nötigen Funktionen übergeben. Dadurch kann man omnes auch als single calc machen und trotzdem die Punkte nur einmal berechnen
// - AutoImport ausschalten
use egui_plot::{log_grid_spacer, AxisHints, GridInput, GridMark, Legend, Line, Plot};
use num::complex::ComplexFloat;
use std::time::Instant;
mod gq_storage;
mod calc;
struct App {
plot_data: Vec<PlotCurve>,
plots_available: [String; 6],
calc_cache: calc::Cache,
scaling_type: ScalingType,
a: f64,
b: f64,
n_points: u32,
n_omnes: u32,
n_phi0: u32,
use_tan_omnes: bool,
use_tan_phi0: bool,
use_xsub_phi0: bool
}
#[derive(Clone)]
struct PlotCurve {
points: Vec<[f64;2]>,
name: String,
}
impl Default for App {
fn default() -> Self {
App {
plot_data: Vec::default(),
plots_available: ["Delta".to_string(), "Omnes Integrand".to_string(), "Omnes".to_string(), "Phi0 Integrand".to_string(), "Phi0".to_string(), "Prozentueller Fehler".to_string()],
calc_cache: calc::Cache::default(),
scaling_type: ScalingType::default(),
a: calc::S0,
b: 9.0,
n_points: 5000,
n_omnes:1000,
n_phi0:1000,
use_tan_omnes: true,
use_tan_phi0: true,
use_xsub_phi0: true
}
}
}
#[derive(Debug, Default, PartialEq, Clone, Copy)]
enum ScalingType {
Logarithmic,
#[default]
Linear
}
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
let cur_scaling_type = self.scaling_type;
egui::ComboBox::from_label("pick y-scaling").selected_text(format!("{:?}", self.scaling_type)).show_ui(ui, |ui| {
ui.selectable_value(&mut self.scaling_type, ScalingType::Logarithmic, "log10");
ui.selectable_value(&mut self.scaling_type, ScalingType::Linear, "linear");
});
float_text_edit_singleline(ui, &mut self.a);
float_text_edit_singleline(ui, &mut self.b);
int_text_edit_singleline(ui, &mut self.n_points);
int_text_edit_singleline(ui, &mut self.n_omnes);
int_text_edit_singleline(ui, &mut self.n_phi0);
ui.checkbox(&mut self.use_tan_omnes, "Use tan()-subst. for Omnes");
ui.checkbox(&mut self.use_tan_phi0, "Use tan()-subst. for Phi0");
ui.checkbox(&mut self.use_xsub_phi0, "Use second subst. for Phi0");
let button_clear = ui.button("Clear canvas");
if button_clear.clicked() {
self.plot_data = Vec::new();
}
for i in 0..self.plots_available.len() {
let button = ui.button( format!("Calculate {}", self.plots_available[i].clone()));
if button.clicked() {
let x_values: Vec<f64> = (0..self.n_points).map(|x| -> f64 {f64::from(x) * ((self.b - self.a) / f64::from(self.n_points)) + self.a}).collect();
match i {
0 => {
let y_values_delta:Vec<f64> = x_values.iter().map(|&x| calc::delta_with_lut(&mut self.calc_cache, x)).collect();
self.plot_data.push(PlotCurve {
points: x_values.iter().zip(y_values_delta.iter()).map(|(&x,&y)| [x.powf(0.5),y]).collect(),
name: "Delta".to_string()
})
}
1 => {
let y_values_omnes_integrand: Vec<f64> = x_values.iter().map(|&x| calc::omnes_integrand(&mut self.calc_cache, x, 100.0).abs()).collect();
self.plot_data.push(PlotCurve {
points: x_values.iter().zip(y_values_omnes_integrand.iter()).map(|(&x,&y)| [x.powf(0.5),y]).collect(),
name: "Omnes Integrand".to_string()
})
}
2 => {
let y_values_omnes: Vec<f64> = x_values.iter().map(|&x| calc::omnes(&mut self.calc_cache, x, self.n_omnes, self.use_tan_omnes).abs().powi(2)).collect();
self.plot_data.push(PlotCurve {
points: x_values.iter().zip(y_values_omnes.iter()).map(|(&x,&y)| [x.powf(0.5),y]).collect(),
name: "Omnes".to_string()
})
}
3 => {
let y_values_phi0_integrand: Vec<f64> = x_values.iter().map(|&x| calc::phi0_integrand(&mut self.calc_cache, x, 10000.0, self.n_omnes, self.use_tan_omnes)).collect();
self.plot_data.push(PlotCurve {
points: x_values.iter().zip(y_values_phi0_integrand.iter()).map(|(&x,&y)| [x.powf(0.5),y]).collect(),
name: "Phi0 Integrand".to_string()
})
}
4 => {
let t0 = Instant::now();
let y_values_phi0: Vec<f64> = x_values.iter().map(|&x| calc::phi0(&mut self.calc_cache, x, self.n_omnes, self.n_phi0, self.use_tan_omnes, self.use_tan_phi0, self.use_xsub_phi0)).collect();
println!("{:?}", t0.elapsed());
self.plot_data.push(PlotCurve {
points: x_values.iter().zip(y_values_phi0.iter()).map(|(&x,&y)| [x.powf(0.5),y]).collect(),
name: "Phi0".to_string()
})
// let y_values_phi0 = calc::bit_pattern_randomness_tester(self.a, self.b, self.n_points as u64);
// self.plot_data.push(PlotCurve {
// points: (0..self.n_points).zip(y_values_phi0.iter()).map(|(x,&y)| [x as f64,y]).collect(),
// name: "Phi0".to_string()
// })
}
5 => {
let y_values_phi0: Vec<f64> = x_values.iter().map(|&x| {
let t0 = Instant::now();
let val = calc::phi0(&mut self.calc_cache, x.powi(2), self.n_omnes, self.n_phi0, self.use_tan_omnes, self.use_tan_phi0, self.use_xsub_phi0) / calc::delta_with_lut(&mut self.calc_cache, x.powi(2)) - 1.0;
println!("time for x = {}: {:?}", x, t0.elapsed());
val
}).collect();
self.plot_data.push(PlotCurve {
points: x_values.iter().zip(y_values_phi0.iter()).map(|(&x,&y)| [x,y]).collect(),
name: "Phi0".to_string()
})
}
6_usize.. => {
panic!("Not all buttons have been assigned data!");
}
}
}
}
if self.scaling_type != cur_scaling_type {
for curve in self.plot_data.iter_mut() {
for [_x,y] in curve.points.iter_mut() {
if self.scaling_type == ScalingType::Linear {
*y = 10.0f64.powf(*y);
} else if self.scaling_type == ScalingType::Logarithmic {
*y = y.log10();
}
}
}
}
let plot = Plot::new("my_plot")
.grid_spacing(5.0f32..=300.0f32)
.x_axis_label("sqrt(s) [GeV]")
.y_axis_label("|Omega|^2")
.legend(Legend::default());
let plot = match self.scaling_type {
ScalingType::Linear => plot.custom_y_axes(vec![AxisHints::new_y().formatter(|mark, _l, _| scientific_notation(mark.value, false))]),
ScalingType::Logarithmic => {plot.y_grid_spacer(|input| {
let input_exp = GridInput {bounds: (inverse_log_map(input.bounds.0), inverse_log_map(input.bounds.1)), base_step_size: input.base_step_size};
let lin_gridmarks = (log_grid_spacer(10))(input_exp);
let mut log_gridmarks: Vec<GridMark> = Vec::new();
for v in lin_gridmarks.iter() {
if v.value - v.value.floor() < 0.9 {
log_gridmarks.push(GridMark{value:log_map(v.value), step_size:v.step_size / 1.14});
}
}
log_gridmarks
})
.label_formatter(|_,point| format!("x = {:.5}\ny = {:.5}", point.x, 10.0f64.powf(point.y)).to_string())
.custom_y_axes(vec![AxisHints::new_y().formatter(|mark, _l, _| scientific_notation(mark.value, true))])
}
};
plot.show(ui, |plot_ui| {
for i in 0..self.plot_data.len() {
plot_ui.line(Line::new(self.plot_data[i].points.clone()).name(self.plot_data[i].name.clone())); // is clone necessary?
}
});
});
}
}
fn main() {
let mut app = App::default();
app.calc_cache = calc::Cache::from_file("./gauss_quad_lut.morello").unwrap();
// let x_values: Vec<f64> = (0..app.n_points).map(|x| -> f64 {f64::from(x) * ((app.b - app.a) / f64::from(app.n_points)) + app.a}).collect();
// let t0 = Instant::now();
// let y_values_phi0: Vec<f64> = x_values.iter().map(|&x| calc::phi0(&mut app.calc_cache, x, app.n_omnes, app.n_phi0)).collect();
// std::hint::black_box(&y_values_phi0);
// println!("{:?}", t0.elapsed());
eframe::run_native("Morellus", eframe::NativeOptions::default(), Box::new(|_cc| Box::new(app))).unwrap();
}
fn log_map(x:f64) -> f64 {
x.floor() + (1.0 + (x - x.floor()) * 10.0).log10()
}
fn inverse_log_map(x:f64) -> f64 {
x.floor() + (10f64.powf(x - x.floor()) - 1.0) / 10.0
}
fn scientific_notation(x:f64, log:bool) -> String {
if log {
format!("{}*10^{}", format!(
"{:.15}", 10f64.powf(x - x.floor()))
.trim_end_matches('0').trim_end_matches('.').to_string(),
x.floor()).to_string()
} else {
format!("{:.9}", x).trim_end_matches('0').trim_end_matches('.').to_string()
}
}
fn int_text_edit_singleline(ui: &mut egui::Ui, value: &mut u32) -> egui::Response {
let mut tmp_value = format!("{}", value);
let res = ui.text_edit_singleline(&mut tmp_value);
if let Ok(result) = tmp_value.parse::<u32>() {
*value = result;
}
res
}
fn float_text_edit_singleline(ui: &mut egui::Ui, value: &mut f64) -> egui::Response {
let mut tmp_value = format!("{}", value);
let res = ui.text_edit_singleline(&mut tmp_value);
if let Ok(result) = tmp_value.parse::<f64>() {
*value = result;
}
res
}

19
bachelorarbeit/text.txt Normal file
View File

@@ -0,0 +1,19 @@
BTreeMap: 1.2234s
phi0_integrand:
omnes: 29.30%
get: 22.87%
integrate_complex: 5.01%
delta_with_lut
get: 2.50%
omnes_integrand: 37.98%
delta_with_lut -> get: 22.20%
HashMap: 1.0767s
phi0_integrand:
omnes: 35.22%
get: 30.40%
integrate_complex: 3.95%
delta_with_lut
get: 1.88%
omnes_integrand: 27.21%
delta_with_lut -> get: 14.03%