Files
TRIMSP/TrimSPlib.js
T
ext-mcfadd_r 391c25ef93 more stopping power coefficient options
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.
2024-08-12 16:24:42 -07:00

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;
}
}