mirror of
https://github.com/vale981/neutrino_oscillations
synced 2025-03-04 09:11:39 -05:00
475 lines
12 KiB
JavaScript
475 lines
12 KiB
JavaScript
///////////////////////////////////////////////////////////////////////////////
|
|
// Setup/Globals //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
const ctx = document.getElementById('neutrinorender').getContext('2d');
|
|
const render = new Chart(ctx, {
|
|
type: 'line',
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
tooltips: {
|
|
mode: 'index',
|
|
intersect: false,
|
|
},
|
|
animation: {
|
|
duration: 0,
|
|
},
|
|
scales: {
|
|
x: {
|
|
scaleLabel: {
|
|
display: true,
|
|
labelString: "Distance [km]"
|
|
}
|
|
},
|
|
y: {
|
|
scaleLabel: {
|
|
display: true,
|
|
labelString: "Survival Probability"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
});
|
|
|
|
const controls = document.forms.controls;
|
|
const settings = document.getElementById('settings');
|
|
const pmnsElement = document.getElementById("pmns");
|
|
|
|
// Mixing Angle Experimental Values, degrees
|
|
const t12 = 33.44;
|
|
const t23 = 49;
|
|
const t13 = 8.57;
|
|
|
|
// squared mass differences, 1e-4 eV^2
|
|
const ms12 = .75;
|
|
const ms23 = 24.4;
|
|
|
|
// default settings
|
|
const defaultNeutrinoData =
|
|
{
|
|
t12,
|
|
t23,
|
|
t13,
|
|
ms12,
|
|
ms23,
|
|
L_max: 100,
|
|
is_e: 1,
|
|
is_m: 0,
|
|
is_t: 0,
|
|
E: 6 // MeV
|
|
};
|
|
|
|
|
|
const control_categories = {
|
|
angles: {
|
|
title: 'Mixing Angles',
|
|
},
|
|
|
|
masses: {
|
|
title: 'Squared Mass Differences',
|
|
},
|
|
|
|
state: {
|
|
title: 'Initial State',
|
|
},
|
|
|
|
observatory: {
|
|
title: 'Observatory Settings',
|
|
},
|
|
};
|
|
|
|
const neutrino_controls = {
|
|
t12: {
|
|
title: '\\(\\theta_{12}\\)',
|
|
unit: '\\(^\\circ\\)',
|
|
category: 'angles',
|
|
control: {
|
|
type: 'slider',
|
|
min: 0,
|
|
max: 90,
|
|
step: .1,
|
|
}
|
|
},
|
|
|
|
t13: {
|
|
title: '\\(\\theta_{13}\\)',
|
|
unit: '\\(^\\circ\\)',
|
|
category: 'angles',
|
|
control: {
|
|
type: 'slider',
|
|
min: 0,
|
|
max: 90,
|
|
step: .1,
|
|
}
|
|
},
|
|
|
|
t23: {
|
|
title: '\\(\\theta_{23}\\)',
|
|
unit: '\\(^\\circ\\)',
|
|
category: 'angles',
|
|
control: {
|
|
type: 'slider',
|
|
min: 0,
|
|
max: 90,
|
|
step: .1,
|
|
}
|
|
},
|
|
|
|
ms12: {
|
|
title: '\\(\\Delta m_{12}^2\\)',
|
|
unit: '\\(\\cdot 10^{-4}\\text{eV}^2\\)',
|
|
category: 'masses',
|
|
control: {
|
|
type: 'slider',
|
|
min: 0,
|
|
max: 50,
|
|
step: .01,
|
|
}
|
|
},
|
|
|
|
ms23: {
|
|
title: '\\(\\Delta m_{23}^2\\)',
|
|
unit: '\\(\\cdot 10^{-4}\\text{eV}^2\\)',
|
|
category: 'masses',
|
|
control: {
|
|
type: 'slider',
|
|
min: 0,
|
|
max: 50,
|
|
step: .01,
|
|
}
|
|
},
|
|
|
|
L_max: {
|
|
title: '\\(L_\\text{max}\\)',
|
|
unit: ' \\(\\text{km}\\)',
|
|
category: 'observatory',
|
|
control: {
|
|
type: 'slider',
|
|
min: 0,
|
|
max: 1000,
|
|
step: .1,
|
|
}
|
|
},
|
|
|
|
E: {
|
|
title: '\\(E\\)',
|
|
unit: ' \\(\\text{MeV}\\)',
|
|
category: 'observatory',
|
|
control: {
|
|
type: 'slider',
|
|
min: 0,
|
|
max: 100,
|
|
step: .1,
|
|
}
|
|
},
|
|
|
|
is_e: {
|
|
title: '\\(e\\)',
|
|
unit: '',
|
|
category: 'state',
|
|
control: {
|
|
type: 'slider',
|
|
min: 0,
|
|
max: 1,
|
|
step: .01,
|
|
}
|
|
},
|
|
|
|
is_m: {
|
|
title: '\\(\\mu\\)',
|
|
unit: '',
|
|
category: 'state',
|
|
control: {
|
|
type: 'slider',
|
|
min: 0,
|
|
max: 1,
|
|
step: .01,
|
|
}
|
|
},
|
|
|
|
is_t: {
|
|
title: '\\(\\tau\\)',
|
|
unit: '',
|
|
category: 'state',
|
|
control: {
|
|
type: 'slider',
|
|
min: 0,
|
|
max: 1,
|
|
step: .01,
|
|
}
|
|
},
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Util //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
function deg_to_rad(x) {
|
|
return (x / 180) * math.pi
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Neutrino Mixing Specifics //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Compute the PMNS Matrix.
|
|
*
|
|
* Angle Arguments in Degrees.
|
|
*/
|
|
function pmns(t12, t23, t13) {
|
|
t12 = deg_to_rad(t12);
|
|
t13 = deg_to_rad(t13);
|
|
t23 = deg_to_rad(t23);
|
|
|
|
// Compute all sines, cosines
|
|
let c12 = math.cos(t12);
|
|
let c13 = math.cos(t13);
|
|
let c23 = math.cos(t23);
|
|
|
|
let s12 = math.sin(t12);
|
|
let s13 = math.sin(t13);
|
|
let s23 = math.sin(t23);
|
|
|
|
return math.matrix([
|
|
[c12 * c13, s12 * c13, s13],
|
|
[-s12 * c23 - c12 * s23 * s13, c12 * c23 - s12 * s23 * s13, s23 * c13],
|
|
[s12 * s23 - c12 * c23 * s13, -c12 * s23 - s12 * c23 * s13, c23 * c13]
|
|
]);
|
|
}
|
|
|
|
function propagation_step(pmns_matrix, ms12, ms23, dL, E) {
|
|
E = E / 1000;
|
|
ms12 = ms12 * 1e-4;
|
|
ms23 = ms23 * 1e-4;
|
|
|
|
const propagator = math.matrix([[math.exp(math.complex(0, -ms12 * 2.54 * dL / E)), 0, 0],
|
|
[0, 1, 0],
|
|
[0, 0, math.exp(math.complex(0, ms23 * 2.54 * dL / E))]]);
|
|
|
|
return math.multiply(pmns_matrix, propagator, math.transpose(pmns_matrix));
|
|
}
|
|
|
|
function propagate(initial_state, pmns_matrix, ms12, ms23, L, E) {
|
|
const propagator = propagation_step(pmns_matrix, ms12, ms23, L, E);
|
|
|
|
return math.multiply(propagation_step, initial_state);
|
|
}
|
|
|
|
function state_to_probabilies(state) {
|
|
return state.map(value => math.multiply(value.conjugate(), value).re);
|
|
}
|
|
|
|
function plot_propagation(neutrino_data) {
|
|
const {t12, t23, t13, ms12, ms23, L_max, E, is_e, is_m, is_t} = neutrino_data;
|
|
|
|
const norm = math.sqrt(is_e * is_e + is_m * is_m + is_t * is_t);
|
|
let state = math.matrix([
|
|
[is_e / norm],
|
|
[is_m / norm],
|
|
[is_t / norm]
|
|
]);
|
|
|
|
const pmns_matrix = pmns(t12, t23, t13);
|
|
|
|
const dL = math.round((L_max) / 1000, 6);
|
|
|
|
const lengths = math.range(0, L_max, dL);
|
|
|
|
const common_options = {
|
|
pointRadius: 0,
|
|
borderWidth: 1,
|
|
};
|
|
|
|
const datasets = [
|
|
{
|
|
data: [],
|
|
borderColor: 'green',
|
|
label: 'electron',
|
|
...common_options
|
|
},
|
|
{
|
|
data: [],
|
|
borderColor: 'blue',
|
|
label: 'muon',
|
|
...common_options
|
|
},
|
|
{
|
|
data: [],
|
|
borderColor: 'red',
|
|
label: 'tauon',
|
|
...common_options
|
|
}
|
|
];
|
|
|
|
const propagator = propagation_step(pmns_matrix, ms12, ms23, dL, E)
|
|
|
|
for (const length of lengths._data) {
|
|
state = math.multiply(propagator, state);
|
|
|
|
probs = state_to_probabilies(state);
|
|
probs.forEach((p, index) => {
|
|
datasets[index[0]].data.push(p);
|
|
})
|
|
}
|
|
|
|
render.data.labels = lengths.map(l => math.round(l, 2))._data;
|
|
render.data.datasets = datasets;
|
|
render.update({
|
|
duration: 0,
|
|
easing: 'easeInOutBack'
|
|
});
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// UI Stuff //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
function renderPmnsMatrix(pmns_matrix) {
|
|
let result = "$$U=\\begin{pmatrix}\n";
|
|
|
|
for (let row of pmns_matrix._data) {
|
|
for (let val of row) {
|
|
result += `${math.round(val, 3)} &`
|
|
}
|
|
result = result.slice(0, -1);
|
|
result += "\\\\\n";
|
|
}
|
|
|
|
result += "\\end{pmatrix}$$";
|
|
|
|
|
|
pmnsElement.textContent = result;
|
|
renderMathInElement(pmnsElement);
|
|
}
|
|
|
|
function setUpApp() {
|
|
const neutrino_data = {...defaultNeutrinoData, ...getWindowNeutrinoData()};
|
|
renderControls(neutrino_data);
|
|
|
|
const {t12, t23, t13} = getNeutrinoData();
|
|
renderPmnsMatrix(pmns(t12, t23, t13));
|
|
|
|
plot_propagation(neutrino_data);
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", setUpApp);
|
|
window.addEventListener("popstate", setUpApp);
|
|
|
|
function makeControlElement(id, element, default_value) {
|
|
const {min, max, step} = element.control;
|
|
|
|
const input_div = document.createElement("div");
|
|
const label = document.createElement("label");
|
|
const input = document.createElement("input");
|
|
|
|
|
|
label.innerHTML = `${element.title} = <span id="val_${id}">${default_value}</span>${element.unit}`;
|
|
label.htmlFor = id
|
|
|
|
input.type = "range";
|
|
input.step = step;
|
|
input.min = min;
|
|
input.max = max;
|
|
input.id = id;
|
|
input.value = default_value;
|
|
input.label = label;
|
|
|
|
input_div.className = "input-group vertical";
|
|
input_div.appendChild(label);
|
|
input_div.appendChild(input);
|
|
|
|
return input_div;
|
|
}
|
|
|
|
function renderControls(neutrino_data) {
|
|
controls.innerHTML = "";
|
|
for (let category in control_categories) {
|
|
const category_element = document.createElement("fieldset");
|
|
category_element.id = category;
|
|
|
|
const legend = document.createElement("legend");
|
|
legend.textContent = control_categories[category].title;
|
|
|
|
category_element.appendChild(legend);
|
|
controls.appendChild(category_element);
|
|
}
|
|
|
|
for (let id in neutrino_controls) {
|
|
const element = neutrino_controls[id];
|
|
const group = controls.querySelector("#" + element.category);
|
|
const control = makeControlElement(id, element, neutrino_data[id]);
|
|
|
|
group.appendChild(control);
|
|
}
|
|
|
|
renderMathInElement(controls);
|
|
controls.addEventListener('input', handleInput);
|
|
}
|
|
|
|
function getNeutrinoData() {
|
|
const data = {};
|
|
|
|
for (let element in neutrino_controls) {
|
|
data[element] = parseFloat(controls.elements[element].value);
|
|
}
|
|
|
|
return {...defaultNeutrinoData, ...data};
|
|
}
|
|
|
|
function updateControlLabel(control) {
|
|
control.label.childNodes.item(3).textContent = control.value;
|
|
}
|
|
|
|
|
|
function serializeNeutrinoData(data) {
|
|
return encodeURI(JSON.stringify(data));
|
|
}
|
|
|
|
function getWindowNeutrinoData() {
|
|
const data_string = decodeURI(window.location.href);
|
|
const settings_index = data_string.indexOf("?settings=");
|
|
|
|
if (settings_index === -1)
|
|
return defaultNeutrinoData;
|
|
|
|
const json = data_string.slice(data_string.indexOf("?settings=") + "?settings=".length);
|
|
|
|
return JSON.parse(json);
|
|
}
|
|
|
|
let historyTimeout = null;
|
|
|
|
function handleInput(event) {
|
|
const control = event.target;
|
|
normalizeSliders()
|
|
|
|
const neutrino_data = getNeutrinoData();
|
|
const current_href = window.location.href;
|
|
|
|
window.clearTimeout(historyTimeout);
|
|
historyTimeout = window.setTimeout(() => {
|
|
window.clearTimeout(historyTimeout);
|
|
window.history.pushState(neutrino_data, "",
|
|
current_href.slice(0, current_href.indexOf("?"))
|
|
+ "?settings=" + serializeNeutrinoData(neutrino_data))
|
|
}, 1000);
|
|
|
|
const {t12, t23, t13} = neutrino_data;
|
|
renderPmnsMatrix(pmns(t12, t23, t13));
|
|
|
|
updateControlLabel(control);
|
|
plot_propagation(neutrino_data);
|
|
}
|
|
|
|
function normalizeSliders() {
|
|
const {is_e, is_m, is_t} = getNeutrinoData();
|
|
const norm = math.sqrt(is_e * is_e + is_m * is_m + is_t * is_t);
|
|
|
|
for (let name of ['is_e', 'is_t', 'is_m']) {
|
|
const control = controls[name];
|
|
control.value = control.value / norm
|
|
updateControlLabel(control);
|
|
}
|
|
}
|