391c25ef93
This patch modularizes the code responsible for selecting the stopping power coefficients. Specifically, it: - moves the data in `elemPars` to seperate JSON files in the `data/` directory. This makes it easier to modify the existing data or add new data sources. Note that this refactoring does not alter the previous functionality - the contents of each file are simply read and deep-merged into an initially empty `elemPars` object. - adds the original Varelas-Biersack parameters from Anderson & Ziegler (1977). - updates the "Other Parameters" tab in the GUI for easy toggling between the different stopping power coefficient data sources.
844 lines
27 KiB
JavaScript
844 lines
27 KiB
JavaScript
// This file contains function that are generic Javascript for TrimSP
|
|
|
|
// This is an object that contains all GUI ids and values
|
|
var All = new Object();
|
|
|
|
|
|
function isObject(item) {
|
|
// simple object check
|
|
// https://stackoverflow.com/a/34749873
|
|
return (item && typeof item === 'object' && !Array.isArray(item));
|
|
}
|
|
|
|
|
|
function mergeDeep(target, ...sources) {
|
|
// function for deep-merging objects
|
|
// https://stackoverflow.com/a/34749873
|
|
|
|
if (!sources.length) {
|
|
return target;
|
|
}
|
|
|
|
const source = sources.shift();
|
|
|
|
if (isObject(target) && isObject(source)) {
|
|
for (const key in source) {
|
|
if (isObject(source[key])) {
|
|
if (!target[key]) Object.assign(target, { [key]: {} });
|
|
mergeDeep(target[key], source[key]);
|
|
} else {
|
|
Object.assign(target, { [key]: source[key] });
|
|
}
|
|
}
|
|
}
|
|
|
|
return mergeDeep(target, ...sources);
|
|
}
|
|
|
|
|
|
// list of json files containing all projectile/target element data
|
|
const dataFiles = [
|
|
"./data/elements.json",
|
|
"./data/projectiles.json",
|
|
"./data/az_table.json",
|
|
"./data/icru_table.json",
|
|
"./data/iaea_table.json"
|
|
];
|
|
|
|
// empty "main" object - to be filled with *all* projectile/target data
|
|
const elemPars = {}
|
|
|
|
// loop over each json file
|
|
for (const df of dataFiles) {
|
|
// read its contents...
|
|
const data = require(df)
|
|
// ...and deep-merge them to the "main" object
|
|
mergeDeep(elemPars, data);
|
|
};
|
|
|
|
// Extra densities (i.e., for chemical compounds)
|
|
var rhos = require("./data/densities.json")
|
|
|
|
|
|
function parse_formula (mf) {
|
|
// This function takes a chemical formula and returns
|
|
// an object that contains the atoms and their number
|
|
|
|
// replace all types of brackets into ()
|
|
var mf = mf.replace(/\[/g,"(").replace(/\]/g,")").replace(/\{/g,"(").replace(/\}/g,")").replace(/\s+/g,"");
|
|
if(/^\d+/.test(mf)) throw new SyntaxError("Molecular formula should not start with a number!");
|
|
var mol = {};
|
|
// go into brackets and collect info
|
|
while(mf.includes("(")){
|
|
var x = mf.lastIndexOf("(");
|
|
var y = mf.indexOf(")", mf.lastIndexOf("("));
|
|
|
|
var z = mf.substr(x+1, y-x-1);
|
|
var c = mf.substr(y+1);
|
|
c = /^\d+/.exec(c);
|
|
c=(c!= null)?Number(c[0]):1;
|
|
// recursive
|
|
var a = this.parse_formula(z);
|
|
var b = "";
|
|
for(var k in a){
|
|
b+=k+""+a[k]*c;
|
|
}
|
|
mf = mf.substr(0, x)+ b + mf.substr(y+1+((c>1)?(c+'').length:0));
|
|
}
|
|
|
|
var m = "";
|
|
// Separate element name from stoichiometry
|
|
// while(m = /([A-Z]{1}[a-z]{0,}[0-9]{0,})/.exec(mf)){
|
|
while(m = /([A-Z]{1}[a-z]{0,}[0-9]{0,}\.?[0-9]{0,})/.exec(mf)){
|
|
// Element
|
|
var m1 = /([A-Z]{1,}[a-z]{0,})/.exec(m[0]);
|
|
// Stoichiometry
|
|
//var m2 = /\d+/.exec(m[0]);
|
|
var m2 = /\d+\.?\d{0,}/.exec(m[0]);
|
|
if(typeof mol[m1[0]] == 'undefined') mol[m1[0]]=(m2!=null)?Number(m2[0]):1;
|
|
else mol[m1[0]] += (m2!=null)?Number(m2[0]):1;
|
|
mf = mf.replace(m[0], "");
|
|
}
|
|
return mol;
|
|
}
|
|
|
|
function StoppingCoefficients(Element, flag)
|
|
{
|
|
// Select which tabulation to use for the Varelas-Biersack parameterization
|
|
// of electronic stopping cross sections for proton-like projectiles
|
|
// implanted in elemental targets.
|
|
|
|
// flag = 1: use coefficients from Anderson & Ziegler (1977).
|
|
if (flag == 1) {
|
|
return elemPars[Element].AZ;
|
|
}
|
|
|
|
// flag = 2: use coefficients from ICRU Report 49 (1973).
|
|
if (flag == 2) {
|
|
return elemPars[Element].ICRU;
|
|
}
|
|
|
|
// flag = 3: use coefficients from McFadden et al. (2023) and McFadden et al. (unpublished).
|
|
//
|
|
// NOTE: these values were derived from up-to-date stopping data found inthe
|
|
// IAEA's database of electronic stopping cross sections
|
|
// (https://www-nds.iaea.org/stopping/).
|
|
// For further details on their determination, see, e.g.:
|
|
// Appendix A in McFadden at al., Phys. Rev. Applied 19, 044018 (2023).
|
|
// https://doi.org/10.1103/PhysRevApplied.19.044018
|
|
if (flag == 3 && elemPars[Element].IAEA.length == 5) {
|
|
return elemPars[Element].IAEA;
|
|
// in the event that updated coefficients are unavailable for given element
|
|
// fall back to using values from ICRU Report 49
|
|
} else {
|
|
return elemPars[Element].ICRU;
|
|
}
|
|
}
|
|
|
|
|
|
function rho_fun()
|
|
{
|
|
var irow = this.parentNode.parentNode.rowIndex;
|
|
var caller = this.id;
|
|
var chem = document.getElementById(caller).value;
|
|
var rhoLi = "L"+irow+"rho";
|
|
|
|
var rho = 0;
|
|
|
|
if (elemPars[chem]) {
|
|
rho = elemPars[chem].rho;
|
|
} else {
|
|
// Did not find density in elements list
|
|
if (isFinite(rhos[chem])) {
|
|
// Found in densities list of compositions
|
|
rho = rhos[chem];
|
|
} else {
|
|
// suggest a material density based on composition (when it's undefined)
|
|
let layer_formula = parse_formula(chem);
|
|
|
|
// determine the stoichiometry sum (for normalization)
|
|
let stoichiometry_sum = 0;
|
|
for (key of Object.keys(layer_formula)) {
|
|
stoichiometry_sum = stoichiometry_sum + layer_formula[key];
|
|
}
|
|
|
|
// determine the density using on a weighted average of the elemental densities
|
|
let density_estimate = 0;
|
|
for (key of Object.keys(layer_formula)) {
|
|
if (layer_formula[key] != null) {
|
|
density_estimate = density_estimate + (layer_formula[key] / stoichiometry_sum) * elemPars[key].rho;
|
|
}
|
|
}
|
|
|
|
rho = density_estimate;
|
|
alert("Warning: The density for this layer is only an estimate!")
|
|
}
|
|
}
|
|
// Set value in appropriate cell
|
|
document.getElementById(rhoLi).value = rho;
|
|
}
|
|
|
|
function openTab(event, tabName) {
|
|
// Declare all variables
|
|
var i, tabcontent, tablinks;
|
|
// Get all elements with class="tabcontent" and hide them
|
|
tabcontent = document.getElementsByClassName("tabcontent");
|
|
for (i = 0; i < tabcontent.length; i++) {
|
|
tabcontent[i].style.display = "none";
|
|
}
|
|
|
|
// Get all elements with class="tablinks" and remove the class "active"
|
|
tablinks = document.getElementsByClassName("tablinks");
|
|
for (i = 0; i < tablinks.length; i++) {
|
|
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
|
}
|
|
|
|
// Show the current tab, and add an "active" class to the button that opened the tab
|
|
document.getElementById(tabName).style.display = "block";
|
|
if (event) event.currentTarget.className += " active";
|
|
}
|
|
|
|
function adjust_table()
|
|
{
|
|
var numLayer = document.getElementById("numLayer").value;
|
|
var LTable = document.getElementById("LTable");
|
|
var Nrows = LTable.rows.length;
|
|
|
|
var Comps = [];
|
|
var Rhos = [];
|
|
var Ds = [];
|
|
|
|
// Need to loop to get the right number of rows when reloading
|
|
if (numLayer >= Nrows) {
|
|
for (i = Nrows;i<=numLayer;i++) {
|
|
var row = LTable.insertRow(i);
|
|
var Li = row.insertCell(0);
|
|
Li.innerHTML = i;
|
|
var compL = row.insertCell(1);
|
|
var compCell = document.createElement("input");
|
|
compCell.id = "L"+i+"Comp";
|
|
compCell.name = "L"+i+"Comp";
|
|
compCell.size = 15;
|
|
compCell.onchange = rho_fun;
|
|
if (Comps[i-1]) {compCell.value = Comps[i-1];}else{compCell.value = 'SrTiO3';}
|
|
compL.appendChild(compCell);
|
|
var rhoL = row.insertCell(2);
|
|
var rhoCell = document.createElement("input");
|
|
rhoCell.id = "L"+i+"rho";
|
|
rhoCell.name = "L"+i+"rho";
|
|
rhoCell.size = 5;
|
|
rhoL.appendChild(rhoCell);
|
|
if (Rhos[i-1]) {rhoCell.value = Rhos[i-1];}else{rhoCell.value = '5.12';}
|
|
var thickL = row.insertCell(3);
|
|
var thickCell = document.createElement("input");
|
|
thickCell.id = "L"+i+"d";
|
|
thickCell.name = "L"+i+"d";
|
|
thickCell.size = 7;
|
|
thickL.appendChild(thickCell);
|
|
if (Ds[i-1]) {thickCell.value = Ds[i-1];}else{thickCell.value = '10000';}
|
|
}
|
|
} else {
|
|
for (i = Nrows-1;i>numLayer;i--) {
|
|
LTable.deleteRow(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
function adjust_scans()
|
|
{
|
|
// Deal with scans checkbox and type
|
|
var scanSeq = document.getElementById("scanSeq").checked;
|
|
var sender = document.getElementById("scanSeq");
|
|
var scanType = document.getElementById("scanType").value;
|
|
// consider only if scans checkbox is checked
|
|
if (scanSeq) {
|
|
// Which type of scan
|
|
if (scanType == "scanLoop") {
|
|
ShowHide(1,'ScansTable');
|
|
ShowHide(0,'ScansLine');
|
|
} else {
|
|
ShowHide(1,'ScansLine');
|
|
ShowHide(0,'ScansTable');
|
|
}
|
|
} else {
|
|
// Hide both
|
|
ShowHide(0,'ScansTable');
|
|
ShowHide(0,'ScansLine');
|
|
}
|
|
}
|
|
|
|
function ProjSmartDefaults()
|
|
{
|
|
var Proj = document.getElementById("ProjType").value;
|
|
var sigEnergy = document.getElementById("sigEnergy");
|
|
var sigAngle = document.getElementById("sigAngle");
|
|
|
|
if (Proj == "muon") {
|
|
sigEnergy.value = "450";
|
|
sigAngle.value = "15";
|
|
} else {
|
|
sigEnergy.value = "0";
|
|
sigAngle.value = "0";
|
|
}
|
|
}
|
|
|
|
function ProjNumberLimit()
|
|
{
|
|
var numberProj = document.getElementById("numberProj").value;
|
|
|
|
if (numberProj > 5000) {
|
|
alert("Maximum number is 5000");
|
|
document.getElementById("numberProj").value = 5000;
|
|
}
|
|
}
|
|
|
|
|
|
function ShowHide(onoff,item) {
|
|
// this function can switch item to visible/hidden onoff=1/0
|
|
if(onoff){
|
|
// document.getElementById(item).style.display = 'block';
|
|
document.getElementById(item).style.visibility = "visible";
|
|
} else {
|
|
// document.getElementById(item).style.display = 'none';
|
|
document.getElementById(item).style.visibility = "hidden";
|
|
}
|
|
}
|
|
|
|
function prep_cfg(toggle) {
|
|
// This function collects various values and returns
|
|
// toggle=1 : returns object with all values
|
|
// toggle!=1 : returns string containing cfg file
|
|
let TrimSPcfg = "";
|
|
let strtmp = "";
|
|
let valtmp = "";
|
|
|
|
All['trimPath']=document.getElementById("trimPath").value;
|
|
|
|
// Prepare Layers section
|
|
let LayersSec = "[Layers]\n";
|
|
let numLayer = document.getElementById("numLayer").value;
|
|
All['numLayer']=numLayer;
|
|
LayersSec = LayersSec + "numLayer="+numLayer+"\n";
|
|
for (i = 1;i<=numLayer;i++) {
|
|
strtmp = "L"+i+"Comp";
|
|
valtmp = document.getElementById(strtmp).value;
|
|
All[strtmp]=valtmp;
|
|
LayersSec = LayersSec + strtmp +"="+ valtmp+"\n";
|
|
strtmp = "L"+i+"rho";
|
|
valtmp = document.getElementById(strtmp).value;
|
|
All[strtmp]=valtmp;
|
|
LayersSec = LayersSec + strtmp +"="+ valtmp+"\n";
|
|
strtmp = "L"+i+"d";
|
|
valtmp = document.getElementById(strtmp).value;
|
|
All[strtmp]=valtmp;
|
|
LayersSec = LayersSec + strtmp +"="+ valtmp+"\n";
|
|
}
|
|
|
|
// Prepare projectile parameters section
|
|
let parProj = ["workPath","fileNamePrefix","ProjType","numberProj","z0","dz","valEnergy","sigEnergy","valAngle","sigAngle","ranSeed"];
|
|
let ProjSec = "[ProjectileParameters]\n";
|
|
for (key of parProj) {
|
|
All[key]= document.getElementById(key).value;
|
|
ProjSec = ProjSec + key + "=" + All[key] + "\n";
|
|
}
|
|
|
|
// Prepare Files section
|
|
let FilesSec = "[Files]\n";
|
|
FilesSec = FilesSec + "fileNamePrefix="+All["fileNamePrefix"]+"\nworkPath="+All["workPath"]+"\n";
|
|
|
|
// Prepare ScanSequence section
|
|
let ScanSec = "[ScanSequence]\n";
|
|
strtmp = "scanSeq";
|
|
valtmp = 1*(document.getElementById(strtmp).checked);
|
|
All[strtmp]=valtmp;
|
|
ScanSec = ScanSec + strtmp +"="+ valtmp+"\n";
|
|
let parScan = ["comboScan","scanType","scanFrom","scanStep","scanTo","scanList","scanListdz"];
|
|
for (key of parScan) {
|
|
All[key]= document.getElementById(key).value;
|
|
ScanSec = ScanSec + key + "=" + All[key] + "\n";
|
|
}
|
|
|
|
// Collect other parameters
|
|
let parOther = ["parEF", "parESB", "parSHEATH", "parERC", "parRD", "parCA", "parKK0", "parKK0R", "parKDEE1", "parKDEE2", "parIPOT", "parIPOTR", "parIRL"];
|
|
for (key of parOther) {
|
|
All[key]= document.getElementById(key).value;
|
|
}
|
|
|
|
// Get the stopping power coefficient (SPC) flag
|
|
All["flagSPC"] = document.getElementById("flagSPC").value;
|
|
|
|
// Construct full content of cfg file
|
|
TrimSPcfg = FilesSec + LayersSec + ProjSec + ScanSec;
|
|
if (toggle) {
|
|
return All;
|
|
} else {
|
|
return TrimSPcfg;
|
|
}
|
|
}
|
|
|
|
function setValues(content) {
|
|
// This function takes cfg file content and substitutes values in the GUI
|
|
// Split file content into lines
|
|
var event = new Event('change');
|
|
var lines = content.toString().split('\n');
|
|
// Remove comment lines, empty lines and those starting with "["
|
|
var PATTERN= /^(?!\[)(?!\#)(\S)/,
|
|
flines = lines.filter(function (str) {return PATTERN.test(str);});
|
|
for (var i=0; i<flines.length; i++) {
|
|
// Loop over parameters, set appropriate values and trigger associated onChange
|
|
var param=flines[i].replace(/\s+/,'').split('=');
|
|
if (param[0] == 'scanSeq') {
|
|
// This is a checkbox
|
|
var seqChecked = true;
|
|
if (param[1] == 0) {seqChecked = false;}
|
|
document.getElementById(param[0]).checked = seqChecked;
|
|
document.getElementById(param[0]).dispatchEvent(event);
|
|
} else {
|
|
document.getElementById(param[0]).value = param[1];
|
|
document.getElementById(param[0]).dispatchEvent(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function CreateInpFile() {
|
|
// Create and return input file for the Trim.SP simulation binary
|
|
// Takes object All as input for all relevant parameters
|
|
|
|
// This is the form of the begining of the input file:
|
|
var TemplateFile = "ProjZ ProjAM valEnergy sigEnergy valAngle sigAngle parEF parESB parSHEATH parERC\n"
|
|
TemplateFile += "numberProj ranSeed ranSeed ranSeed z0 parRD dz parCA parKK0 parKK0R parKDEE1 parKDEE2 parIPOT parIPOTR parIRL";
|
|
|
|
// Then comes the number of layers (new format) for example 4 layers:
|
|
// N_Layers=4
|
|
TemplateFile=TemplateFile+"\n"+"N_Layers=numLayer";
|
|
|
|
// Then loop over the layers and for each give the following structure
|
|
// chemical formula, thickness, density, correction factor, number of elements
|
|
var TemplateLayer = "L1Comp L1d L1rho L1CK L1Elem\n";
|
|
// Z number of elements
|
|
TemplateLayer += "L1ELZ1 L1ELZ2 L1ELZ3 L1ELZ4 L1ELZ5\n";
|
|
// A number of elements
|
|
TemplateLayer += "L1ELW1 L1ELW2 L1ELW3 L1ELW4 L1ELW5\n";
|
|
// Weigth of element in composition
|
|
TemplateLayer += "L1ELC1 L1ELC2 L1ELC3 L1ELC4 L1ELC5\n";
|
|
// Surface binding energy of elements
|
|
TemplateLayer += "L1ELE1 L1ELE2 L1ELE3 L1ELE4 L1ELE5\n";
|
|
// Displacement energy of elements, always 30 eV??
|
|
TemplateLayer += "L10301 L10302 L10303 L10304 L10305\n";
|
|
// Bulk binding energy of elements, usually zero
|
|
TemplateLayer += "0 0 0 0 0\n";
|
|
// value A-1 of the ziegler tables
|
|
TemplateLayer += "L1ELST11 L1ELST21 L1ELST31 L1ELST41 L1ELST51\n";
|
|
// value A-2 of the ziegler tables
|
|
TemplateLayer += "L1ELST12 L1ELST22 L1ELST32 L1ELST42 L1ELST52\n";
|
|
// value A-3 of the ziegler tables
|
|
TemplateLayer += "L1ELST13 L1ELST23 L1ELST33 L1ELST43 L1ELST53\n";
|
|
// value A-4 of the ziegler tables
|
|
TemplateLayer += "L1ELST14 L1ELST24 L1ELST34 L1ELST44 L1ELST54\n";
|
|
// value A-5 of the ziegler tables
|
|
TemplateLayer += "L1ELST15 L1ELST25 L1ELST35 L1ELST45 L1ELST55";
|
|
|
|
let projectile = All['ProjType'];
|
|
All['ProjZ'] = elemPars[projectile].Z;
|
|
All['ProjAM'] = elemPars[projectile].A;
|
|
|
|
// This is the flag for checking layers
|
|
let Check=0;
|
|
let Layer='';
|
|
|
|
// Loop over layers an create appropriate values
|
|
for (var i=1; i<=All['numLayer'];i++){
|
|
// Arry containing 12 lines for each layer
|
|
let newLayer = ["","","","","","","","","","","",""];
|
|
|
|
Check=0;
|
|
// Composition of layers
|
|
let LComp="L"+i+"Comp";
|
|
let Comp = All[LComp];
|
|
if (Comp == "" || typeof Comp == 'undefined') {Check++;}
|
|
let LElComp=parse_formula(Comp);
|
|
for (key of Object.keys(LElComp)) {
|
|
// Check if composition is understood
|
|
if (key=="" || elemPars[key].Z =="") {
|
|
Check++;
|
|
}
|
|
}
|
|
|
|
// Number of elements
|
|
let LElem = "L"+i+"Elem";
|
|
All[LElem] = Object.keys(LElComp).length;
|
|
let nEl = All[LElem];
|
|
|
|
// Densities of layers
|
|
let Lrho="L"+i+"rho";
|
|
let rho = 1*All[Lrho];
|
|
|
|
if (rho==0) {Check++;}
|
|
|
|
// Thickness of layers
|
|
let Ld ="L"+i+"d";
|
|
let d = All[Ld];
|
|
if (d=="") {Check++;}
|
|
|
|
// Sanity check, is the layer supposed to have value? are they all there?
|
|
if (Check!=0) {
|
|
ErrMsg="Error: Bad chemical formula in Layer $i.\nPlease check!\n";
|
|
console.log($ErrMsg);
|
|
return ErrMsg;
|
|
}
|
|
|
|
let Sum = 0;
|
|
for (key of Object.keys(LElComp)) {
|
|
Sum=Sum+LElComp[key];
|
|
}
|
|
if (Sum==0) {Sum=1;}
|
|
|
|
let Els = Object.keys(LElComp);
|
|
|
|
// first line: chemical formula, thickness, density, correction factor, number of elements
|
|
newLayer[0] += Comp + " " + d + " " + rho + " 1 " + nEl;
|
|
for (var j=0;j<Els.length;j++) {
|
|
let El = Els[j];
|
|
let ElSTs = StoppingCoefficients(El, All["flagSPC"]);
|
|
|
|
// Next 11 lines depend on number of elements
|
|
newLayer[1] += elemPars[El].Z + " " ;
|
|
newLayer[2] += elemPars[El].A + " " ;
|
|
newLayer[3] += LElComp[El]/Sum + " " ;
|
|
newLayer[4] += elemPars[El].Elast + " " ;
|
|
newLayer[5] += "30 " ;
|
|
newLayer[6] += "0 " ;
|
|
newLayer[7] += ElSTs[0] + " " ;
|
|
newLayer[8] += ElSTs[1] + " " ;
|
|
newLayer[9] += ElSTs[2] + " " ;
|
|
newLayer[10] += ElSTs[3] + " " ;
|
|
newLayer[11] += ElSTs[4] + " " ;
|
|
}
|
|
TemplateFile += "\n" + newLayer.join("\n");
|
|
}
|
|
|
|
for (let key of Object.keys(All)) {
|
|
if (All[key]!=null){
|
|
if (key=="ranSeed") {
|
|
// Seed repeats three times
|
|
TemplateFile = TemplateFile.replace(/ranSeed/g,All[key]);
|
|
} else {
|
|
TemplateFile = TemplateFile.replace(key,All[key]);
|
|
}
|
|
}
|
|
}
|
|
return TemplateFile;
|
|
}
|
|
|
|
function sum(array){
|
|
// Takes and array and returns the sum of its elements
|
|
let sum_array = array.reduce(function(a, b){
|
|
return a + b;
|
|
}, 0);
|
|
return sum_array;
|
|
}
|
|
|
|
function startSequence() {
|
|
let cmd = '';
|
|
let trimBin = All['trimPath']+"/trimspNL";
|
|
let randStr = '';
|
|
let webOrApp = amIWeb();
|
|
// Only for Node.js
|
|
if (!webOrApp) {
|
|
// Check if the trimspNL binary is found
|
|
if (!fileExists(trimBin)) {
|
|
// check if it is in rpm
|
|
trimBin = "/usr/lib/TrimSP/resources/app/trimspNL";
|
|
if (!fileExists(trimBin)) {
|
|
// if not found, try in PATH and hope for the best
|
|
trimBin = "trimspNL";
|
|
}
|
|
}
|
|
// Check if workPath exists otherwise create it
|
|
checkDir(All['workPath']); // from TrimSPelec.js, Electron/Node specific
|
|
} else {
|
|
// Add random string (5 char) to file names
|
|
randStr = (Math.random() + 1).toString(36).substring(7);
|
|
All["fileNamePrefix"] = randStr;
|
|
All["workPath"] = "/tmp/" + randStr;
|
|
}
|
|
|
|
let Progress =0;
|
|
document.getElementById("pBar").style.width = Progress + "%";
|
|
document.getElementById("pBar").innerHTML = Progress + "%";
|
|
|
|
// This is a flag to indicate whether the lists of values and inclements are the same size
|
|
All["SdzFlag"]=0;
|
|
var SValues = [];
|
|
let ScanName = "E";
|
|
let ScanAttrib = "valEnergy";
|
|
// Generate an arry of scanned values (one element for a single run)
|
|
if (All["scanSeq"]) {
|
|
// For multiple runs or a scan
|
|
if (All["scanType"]=="scanVals") {
|
|
var SValues=All["scanList"].split(/,/);
|
|
var SdzValues=All["scanListdz"].split(/,/);
|
|
if (SValues.length == SdzValues.length) {All["SdzFlag"]=1;}
|
|
} else {
|
|
for (let Val=parseInt(All["scanFrom"]);Val<=parseInt(All["scanTo"]);Val=Val+parseInt(All["scanStep"])) {
|
|
SValues.push(Val);
|
|
}
|
|
}
|
|
|
|
if (All["comboScan"]==1) {
|
|
ScanName = "SigE";
|
|
ScanAttrib = "sigEnergy";
|
|
} else if (All["comboScan"]==2) {
|
|
ScanName = "Angle";
|
|
ScanAttrib = "valAngle";
|
|
} else if (All["comboScan"]==3) {
|
|
ScanName = "SigAngle";
|
|
ScanAttrib = "sigAngle";
|
|
} else if (All["comboScan"]==4) {
|
|
ScanName = "N";
|
|
ScanAttrib = "numberProj";
|
|
} else if (All["comboScan"]==5) {
|
|
ScanName = "Ld"+All["scandL"];
|
|
ScanAttrib = "L"+All["scandL"]+"d";
|
|
}
|
|
} else {
|
|
// For single run, array with single value of valEnergy
|
|
SValues.push(parseInt(All["valEnergy"]));
|
|
}
|
|
|
|
// Now start the actual simulation
|
|
//setInterval(() => {
|
|
let iScan=0;
|
|
for (var SValue of SValues) {
|
|
// Update value in GUI
|
|
document.getElementById(ScanAttrib).value = SValue;
|
|
All[ScanAttrib]=SValue;
|
|
if ( All["SdzFlag"] == 1) {
|
|
if (All["comboScan"]=="Energy") {
|
|
document.getElementById(ScanAttrib).value = SdzValues[iScan];
|
|
} else if (All["comboScan"]=="") {
|
|
document.getElementById(ScanAttrib).value = SdzValues[iScan];
|
|
}
|
|
}
|
|
// Update GUI progress bar
|
|
Progress=Math.round(Progress+90/SValues.length);
|
|
document.getElementById("pBar").style.width = Progress + "%";
|
|
document.getElementById("pBar").innerHTML = Progress + "%";
|
|
|
|
// Single run: Start
|
|
let eingabe1=CreateInpFile();
|
|
if (eingabe1=="ERROR") {return(0);}
|
|
let FILENAME=All["fileNamePrefix"]+"_"+ScanName+SValue;
|
|
writeAsciiFile(All["workPath"]+"/"+FILENAME+".inp",eingabe1);
|
|
if (!webOrApp) {
|
|
// Prepare command and execute
|
|
cmd = "cd " + All["workPath"];
|
|
//cmd += ";cp " + FILENAME + ".inp eingabe1.inp";
|
|
cmd += ";" + trimBin + ' ' + FILENAME;
|
|
//cmd += "; mv -f ausgabe1.rge " + FILENAME + ".rge";
|
|
//cmd += "; mv -f ausgabe1.out " + FILENAME + ".out";
|
|
//cmd += "; mv -f ausgabe1.err " + FILENAME + ".err";
|
|
execute(cmd);
|
|
}
|
|
// Single run: End
|
|
|
|
// Read stopping profiels
|
|
let [cols,data]= readDatFile(All["workPath"]+"/"+FILENAME+".rge");
|
|
// convert depth to nm and normalize stopping profile
|
|
let depth=data[0];
|
|
let nmuons=data[1];
|
|
let dz = (depth[1]-depth[0])/10;
|
|
let norm = dz*sum(nmuons)/100;
|
|
for (let i=0; i<depth.length; i++) {
|
|
depth[i]=depth[i]/10;
|
|
nmuons[i]=nmuons[i]/norm;
|
|
}
|
|
Plot_xy("plotRge",depth,nmuons,['Depth (nm)','Stopped muons (%/nm)',ScanName+'='+Number(SValue)/1000+'keV']);
|
|
// Read the sequence data
|
|
let [cfort,cdata]= readDatFile(All["workPath"]+"/fort.33");
|
|
let lyrs = [6]; // back scattered muons
|
|
for (let ilayer=18;ilayer<cdata.length;ilayer++){lyrs.push(ilayer);} // the rest of the layers
|
|
// Now loop over all
|
|
Plotly.purge("plotFrac")
|
|
for (let ilayer of lyrs) {
|
|
// Normalize
|
|
let idata = cdata[ilayer];
|
|
for (let i=0; i<idata.length; i++) {
|
|
idata[i]=100*idata[i]/cdata[4][i];
|
|
}
|
|
let chem = cfort[ilayer];
|
|
if (ilayer != 6) {
|
|
let LComp = "L"+(ilayer-17)+"Comp";
|
|
chem = All[LComp];
|
|
}
|
|
Plot_xy("plotFrac",cdata[0],cdata[ilayer],['E (keV)','Fraction of muons (%)',chem]);
|
|
}
|
|
iScan++;
|
|
}
|
|
|
|
let data = readAsciiFile(All["workPath"]+"/"+"fort.33");
|
|
let LComp = "";
|
|
let chem_formula = "";
|
|
let place_holder = "";
|
|
let re = new RegExp(place_holder,"g");
|
|
for (let i=1;i<=All["numLayer"];i++) {
|
|
LComp = "L"+i+"Comp";
|
|
chem_formula = All[LComp];
|
|
place_holder = "impL"+i;
|
|
re = new RegExp(place_holder,"g");
|
|
data = data.replace(re, chem_formula);
|
|
}
|
|
let seq_file = All["workPath"]+"/"+All["fileNamePrefix"]+"_Seq_Results.dat";
|
|
writeAsciiFile(seq_file, data);
|
|
|
|
// Remove redundant files and change the name fort.33
|
|
if (!webOrApp) {
|
|
// cmd="cd " + All["workPath"] + ";rm -f eingabe1.inp; mv -f fort.33 " + seq_file;
|
|
cmd="cd " + All["workPath"] + "; mv -f fort.33 " + seq_file;
|
|
execute(cmd);
|
|
}
|
|
Progress=100;
|
|
document.getElementById("pBar").style.width = Progress + "%";
|
|
document.getElementById("pBar").innerHTML = Progress + "%";
|
|
return(0);
|
|
}
|
|
|
|
function readDatFile(filename) {
|
|
// Read column data file and return
|
|
// cols - labels of columns
|
|
// data - 2D array with the columns
|
|
let lines = "";
|
|
lines = readAsciiFile(filename);
|
|
|
|
let data = [];
|
|
let i=0; // line counter
|
|
for (let line of lines.trim().split('\n')) {
|
|
// take labels from the first row
|
|
if (i==0) {
|
|
var cols = line.trim().split(/[#\s]+/g);
|
|
} else {
|
|
let words = line.trim().split(/[#\s]+/g);
|
|
for (let j=0; j<cols.length;j++) {
|
|
if (data[j] == null) {data[j]=[];}
|
|
data[j].push(parseFloat(words[j]));
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
return [cols,data];
|
|
}
|
|
|
|
function plotProfiles(filenames) {
|
|
// Plot rge files filenames
|
|
|
|
var win = window.open("", "_blank", "toolbar=no, menubar=no");
|
|
win.document.open();
|
|
win.document.write('<html><head><link rel="stylesheet" href="plotly.css"></head><body>');
|
|
win.document.write('<table style="width: 100%"><tr><td><div id="newPlot"><!-- Plotly chart will be drawn inside this DIV --></div></td></tr></table>');
|
|
win.document.write('</body></html>');
|
|
win.addEventListener('resize', sizePlot);
|
|
win.document.close();
|
|
var plotDiv=win.document.getElementById("newPlot");
|
|
|
|
// Extract energies from run files if available
|
|
var Es = [];
|
|
for (let i=0;i<filenames.length;i++) {
|
|
Es[i]=Number(filenames[i].substring(filenames[i].indexOf('_')+2,filenames[i].indexOf('.')));
|
|
}
|
|
|
|
// loop on sorted values and plot
|
|
for (let i=0;i<Es.length;i++) {
|
|
let [cols,data]= readDatFile(filenames[i]);
|
|
// convert depth to nm and normalize profile
|
|
let depth=data[0];
|
|
let nmuons=data[1];
|
|
let dz = (depth[1]-depth[0])/10;
|
|
let norm = dz*sum(nmuons)/100;
|
|
for (let i=0; i<depth.length; i++) {
|
|
depth[i]=depth[i]/10;
|
|
nmuons[i]=nmuons[i]/norm;
|
|
}
|
|
Plot_xy(plotDiv,depth,nmuons,['Depth (nm)','Stopped muons (%/nm)','E='+(Es[i]/1000)+'keV']);
|
|
}
|
|
|
|
}
|
|
|
|
function plotFractions(filename) {
|
|
// Plot fractions stopped in layers from sequence file
|
|
|
|
var win = window.open("", "_blank", "toolbar=no, menubar=no");
|
|
win.document.open();
|
|
win.document.write('<html><head><link rel="stylesheet" href="plotly.css"></head><body>');
|
|
win.document.write('<table style="width: 100%"><tr><td><div id="newPlot"><!-- Plotly chart will be drawn inside this DIV --></div></td></tr></table>');
|
|
win.document.write('</body></html>');
|
|
win.addEventListener('resize', sizePlot);
|
|
win.document.close();
|
|
var plotDiv=win.document.getElementById("newPlot");
|
|
|
|
var [cfort,cdata]= readDatFile(filename);
|
|
let lyrs = [6]; // back scattered muons
|
|
for (let ilayer=18;ilayer<cdata.length;ilayer++){lyrs.push(ilayer);} // the rest of the layers
|
|
// Now loop over all
|
|
for (let ilayer of lyrs) {
|
|
// Normalize
|
|
let idata = cdata[ilayer];
|
|
for (let i=0; i<idata.length; i++) {
|
|
idata[i]=100*idata[i]/cdata[4][i];
|
|
}
|
|
Plot_xy(plotDiv,cdata[0],cdata[ilayer],['E (keV)','Fraction of muons (%)',cfort[ilayer]]);
|
|
}
|
|
plotDiv=win.document.getElementById("newPlot");
|
|
}
|
|
|
|
function plotMean(filename) {
|
|
// Plot mean and straggeling from sequence file
|
|
|
|
var win = window.open("", "_blank", "toolbar=no, menubar=no");
|
|
win.document.open();
|
|
win.document.write('<html><head><link rel="stylesheet" href="plotly.css"></head><body>');
|
|
win.document.write('<table style="width: 100%"><tr><td><div id="newPlot" style="width: 500px; height: 400px;"><!-- Plotly chart will be drawn inside this DIV --></div></td></tr></table>');
|
|
win.document.write('</body></html>');
|
|
win.addEventListener('resize', sizePlot);
|
|
win.document.close();
|
|
var plotDiv=win.document.getElementById("newPlot");
|
|
|
|
let [cfort,cdata]= readDatFile(filename);
|
|
let mean = cdata[10];
|
|
let strag = cdata[11];
|
|
for (let i=0; i<mean.length; i++) {
|
|
mean[i]=mean[i]/10;
|
|
strag[i]=strag[i]/10;
|
|
}
|
|
Plot_xy(plotDiv,cdata[0],mean,['E (keV)','Mean Depth/Stragg. (nm)',cfort[10]]);
|
|
Plot_xy(plotDiv,cdata[0],strag,['E (keV)','Mean Depth/Stragg. (nm)',cfort[11]]);
|
|
}
|
|
|
|
function startSim() {
|
|
// Collect data from GUI
|
|
All=prep_cfg(1);
|
|
// Switch to plots tab
|
|
document.getElementById("btnPlots").click();
|
|
// Clear plots first
|
|
Plotly.purge("plotFrac");
|
|
Plotly.purge("plotRge");
|
|
|
|
// start simulation sequence
|
|
startSequence();
|
|
//CreateInpFile();
|
|
// document.location =
|
|
//
|
|
// "http://musruser.psi.ch/cgi-bin/TrimSP.cgi?TrimSPcfg="+TrimSPcfg+'go=start';
|
|
}
|
|
|
|
function amIWeb() {
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
// Run as standalone app
|
|
return 0;
|
|
} else {
|
|
// Run as a web app
|
|
return 1;
|
|
}
|
|
}
|
|
|