DesignAnalysis/stopping_BBvsG4.py
2026-05-13 12:02:53 +02:00

318 lines
No EOL
7.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Mar 4 09:30:07 2026
@author: ava
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy.constants import *
import pandas as pd
from pathlib import Path
import sys
sys.path.append(str(Path(__file__).resolve().parents[1]))
import plotstyle # Dein plotstyle!
MODE = "paper"
plotstyle.set_style(MODE)
e0 = epsilon_0
me = m_e
def J_to_MeV(J):
return J / (e * 1e6)
def MeV_to_J(MeV):
return MeV * (e * 1e6)
Na = 6.022140857e23
u = 1e-3 / Na
particles_BB = {
'Proton': {
'E0_min': 30, # MeV
'E0_max': 10000, # MeV
'm0': m_p,
'z': 1
},
'Helium': {
'E0_min': 30, # MeV
'E0_max': 10000, # MeV
'm0': 6.644657e-27,
'z': 2
},
'Muon': {
'E0_min': 30, # MeV
'E0_max': 10000, # MeV
'm0': 1.883531627e-28,
'z': 1
}
}
materials = {
'BGO': {
# Elektronendichte in BGO (gewichteter Durchschnitt)
'ne': (4 * (9.78e3 / (208.98e-3)) * 83 * N_A + 3 * (5.323e3 / (72.63e-3)) * 32 * N_A + 12 * (1.429e3 / (16.00e-3)) * 8 * N_A) / (4 + 3 + 12),
# Ionisierungsenergie in BGO (gewichteter Durchschnitt)
'I': (4 * 250 * e + 3 * 290 * e + 12 * 150 * e) / (4 + 3 + 12),
# Dicke in m
'd': 2e-2
}
}
def rel_speed(Ekin, m0):
return c * np.sqrt(1 - 1 / ((Ekin / m0 / c / c + 1) ** 2))
def gamma(v):
return 1 / np.sqrt(1 - v * v / c / c)
def bethebloch(Ekin, m0, z, ne, I):
v = rel_speed(J_to_MeV(Ekin) if isinstance(Ekin, np.ndarray) else Ekin, m0)
lorentz_gamma = gamma(v)
C = ne * z**2 * e**4 / (4 * np.pi * me * v**2 * e0**2)
D = np.log(2 * lorentz_gamma**2 * me * v**2 / I)
beta = v**2 / c**2
return C * (D - beta)
def Eloss(E0, m0, z, ne, I, dx, distance):
steps = round(distance / dx)
E = MeV_to_J(E0)
for i in range(steps):
dE = bethebloch(E, m0, z, ne, I) * dx
E -= dE
Ediff = MeV_to_J(E0) - E
return J_to_MeV(Ediff)
# -----------------------------
# Geant4Konfiguration (ohne Elektron)
# -----------------------------
# Nur Proton, Helium, Mu (kein e-)
PARTICLES_BASE_G4 = {
'proton': 'G4outfiles/proton_70energies',
'alpha': 'G4outfiles/helium_70energies',
'mu-': 'G4outfiles/muon_70energies'
}
colors_G4 = {
'proton': 'red',
'alpha': 'green',
'mu-': (0.5, 0.3, 0.8)
}
LABELS_G4 = {
'proton': 'Proton',
'alpha': 'Helium',
'mu-': 'Muon'
}
LINSTYLES = {
2: '-', # 2 cm
4: '--', # 4 cm
6: ':', # 6 cm
}
THICKNESSES = [2, 4, 6]
# -----------------------------
# Funktion: GESAMTER Energieverlust (ALLE Zeilen!) nur für G4
# -----------------------------
def load_particle_data(filename, part_name, label):
try:
df = pd.read_csv(filename, sep='\t')
df['z_cm'] = df['z'] / 10.0
df_part = df[df['part'] == part_name].copy()
if len(df_part) == 0:
print(f"{label}: KEINE {part_name}-Hits gefunden!")
return None
total_eloss = df_part.groupby(['primaryE', 'event'])['edep'].sum().reset_index()
total_eloss.columns = ['E0', 'event', 'dE_total']
means = total_eloss.groupby('E0')['dE_total'].agg(['mean','std']).reset_index()
means.columns = ['E0', 'dE_mean', 'dE_std']
return means
except FileNotFoundError:
print(f" → Datei '{filename}' nicht gefunden!")
return None
except Exception as e:
print(f" → Fehler bei {label}: {e}")
return None
# -----------------------------
# 9 Dateien laden: Proton/Helium/Mu, 2/4/6 cm (ohne e-)
# -----------------------------
all_data = {} # (part, thickness) -> DataFrame
for part_g4 in PARTICLES_BASE_G4.keys():
label = LABELS_G4[part_g4]
base = PARTICLES_BASE_G4[part_g4]
for thickness in THICKNESSES:
thickness_label = f"{thickness} cm"
filename = f"{base}_{thickness}cm_0.hits"
print(f"\n=== {label} ({part_g4}), {thickness_label} ===")
print(f" Datei: {filename}")
df = load_particle_data(filename, part_g4, label)
if df is None:
print(f"{label} / {thickness_label}: KEINE DATEN")
else:
print(f"{label} / {thickness_label}: {len(df)} Energiepunkte")
all_data[(part_g4, thickness)] = df
# -----------------------------
# PLOT: BetheBloch + Geant4 in einem Plot
# -----------------------------
plt.figure()
material = materials['BGO']
dx = 1e-5
distances = {
'2 cm': 0.02,
'4 cm': 0.04,
'6 cm': 0.06
}
# Farben und Linestyles für BetheBloch (nur Proton/Helium/Mu, wie in BBSkript)
colors_BB = {
'Proton': 'red',
'Helium': 'green',
'Muon': (0.5, 0.3, 0.8)
}
linestyles_BB = {
'2 cm': '-',
'4 cm': '--',
'6 cm': ':'
}
# 1) BetheBlochKurven
for name, particle_params in particles_BB.items():
E_values = np.logspace(
np.log10(particle_params['E0_min']),
np.log10(particle_params['E0_max']),
700
)
for label, distance in distances.items():
Eloss_values = np.array([
Eloss(
E0,
particle_params['m0'],
particle_params['z'],
material['ne'],
material['I'],
dx,
distance
) for E0 in E_values
])
plt.plot(
E_values,
Eloss_values,
color=colors_BB[name],
linestyle=linestyles_BB[label],
linewidth=2,
label=f"{name} (BetheBloch, {label})"
)
# 2) Geant4Kurven (Errorbars)
for (part_g4, thickness), df in all_data.items():
if df is None or len(df) == 0:
continue
mask = df['dE_mean'] > 0
if mask.sum() == 0:
continue
color = colors_G4[part_g4]
linestyle = LINSTYLES[thickness]
x = df.loc[mask, 'E0']
y = df.loc[mask, 'dE_mean']
yerr = df.loc[mask, 'dE_std']
# Mittlere Linie für Geant4
plt.plot(
x, y,
color=color,
linestyle=linestyle,
linewidth=1,
label=f"{LABELS_G4[part_g4]} (G4, {thickness} cm)"
)
# Fehlerschlauch (50% sichtbar, leicht durchsichtig)
plt.fill_between(
x,
y - yerr,
y + yerr,
color=color,
linestyle=linestyle,
alpha=0.4
)
# Referenzlinie: IDEAL ΔE = E0
x = np.linspace(30, 10000, 100)
plt.plot(x, x, color="black", linewidth=1, linestyle='-', label=r"$\Delta E = E_{\text{kin}}$")
plt.xscale('log')
plt.yscale('log')
plt.ylim(10, 1000)
plt.xlim(29, 10000)
plt.xlabel('Kinetic energy $E_{kin}$ in MeV')
plt.ylabel('Energy loss $\\Delta E$ in MeV')
plt.grid(True, which="both", ls="--", lw=0.5)
# -----------------------------
# ZWEI LEGENDEN
# 1) Particles (mit BB und G4Labels, aber nur 3 Teilchen)
from matplotlib.lines import Line2D
legend_particle_handles = []
for name in ['Proton', 'Helium', 'Muon']:
color = colors_BB[name]
legend_particle_handles.append(
Line2D([0], [0], color=color, lw=2, label=name)
)
# 2) BGO thickness (2/4/6 cm, Linestyles, schwarz)
legend_thickness_handles = []
for label in distances.keys():
legend_thickness_handles.append(
Line2D([0], [0], color='black', linestyle=linestyles_BB[label], lw=2, label=label)
)
legend_particles = plt.legend(
handles=legend_particle_handles,
loc='upper right',
title="Particles"
)
legend_thickness = plt.legend(
handles=legend_thickness_handles,
loc='upper left',
title="BGO thickness"
)
plt.gca().add_artist(legend_particles)
plt.tight_layout()
plotstyle.savefig("BGO_Eloss_BetheBloch_vs_G4_246cm", category="BB")
plt.show()