Files
TRIMSP/TrimSPlib.js
T

3935 lines
61 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);
}
// element data
const elements = {
"H": {
"Z": 1,
"A": 1.008,
"rho": 0.08,
"Elast": 0.1
},
"He": {
"Z": 2,
"A": 4.003,
"rho": 0.12,
"Elast": 0.1
},
"Li": {
"Z": 3,
"A": 6.939,
"rho": 0.53,
"Elast": 1.63
},
"Be": {
"Z": 4,
"A": 9.012,
"rho": 1.85,
"Elast": 3.32
},
"B": {
"Z": 5,
"A": 10.811,
"rho": 2.34,
"Elast": 5.77
},
"C": {
"Z": 6,
"A": 12.011,
"rho": 2.26,
"Elast": 7.37
},
"N": {
"Z": 7,
"A": 14.007,
"rho": 1.03,
"Elast": 4.92
},
"O": {
"Z": 8,
"A": 15.999,
"rho": 2,
"Elast": 2.6
},
"F": {
"Z": 9,
"A": 18.998,
"rho": 1.11,
"Elast": 0.84
},
"Ne": {
"Z": 10,
"A": 20.183,
"rho": 1.5,
"Elast": 0.02
},
"Na": {
"Z": 11,
"A": 22.99,
"rho": 0.97,
"Elast": 1.11
},
"Mg": {
"Z": 12,
"A": 24.312,
"rho": 1.74,
"Elast": 1.51
},
"Al": {
"Z": 13,
"A": 26.982,
"rho": 2.7,
"Elast": 3.39
},
"Si": {
"Z": 14,
"A": 28.086,
"rho": 2.33,
"Elast": 4.63
},
"P": {
"Z": 15,
"A": 30.974,
"rho": 1,
"Elast": 3.43
},
"S": {
"Z": 16,
"A": 32.064,
"rho": 2.07,
"Elast": 2.85
},
"Cl": {
"Z": 17,
"A": 35.453,
"rho": 2.03,
"Elast": 1.4
},
"Ar": {
"Z": 18,
"A": 39.948,
"rho": 1.77,
"Elast": 0.08
},
"K": {
"Z": 19,
"A": 39.102,
"rho": 0.86,
"Elast": 0.93
},
"Ca": {
"Z": 20,
"A": 40.08,
"rho": 1.55,
"Elast": 1.84
},
"Sc": {
"Z": 21,
"A": 44.956,
"rho": 2.99,
"Elast": 3.9
},
"Ti": {
"Z": 22,
"A": 47.9,
"rho": 4.54,
"Elast": 4.85
},
"V": {
"Z": 23,
"A": 50.942,
"rho": 6.11,
"Elast": 5.31
},
"Cr": {
"Z": 24,
"A": 51.996,
"rho": 7.19,
"Elast": 4.1
},
"Mn": {
"Z": 25,
"A": 54.938,
"rho": 7.43,
"Elast": 2.92
},
"Fe": {
"Z": 26,
"A": 55.847,
"rho": 7.87,
"Elast": 4.28
},
"Co": {
"Z": 27,
"A": 58.933,
"rho": 8.9,
"Elast": 4.39
},
"Ni": {
"Z": 28,
"A": 58.71,
"rho": 8.9,
"Elast": 4.44
},
"Cu": {
"Z": 29,
"A": 63.54,
"rho": 8.96,
"Elast": 3.49
},
"Zn": {
"Z": 30,
"A": 65.37,
"rho": 7.13,
"Elast": 1.35
},
"Ga": {
"Z": 31,
"A": 69.72,
"rho": 5.91,
"Elast": 2.81
},
"Ge": {
"Z": 32,
"A": 72.59,
"rho": 5.32,
"Elast": 3.85
},
"As": {
"Z": 33,
"A": 74.922,
"rho": 5.72,
"Elast": 2.96
},
"Se": {
"Z": 34,
"A": 78.96,
"rho": 4.79,
"Elast": 2.25
},
"Br": {
"Z": 35,
"A": 79.909,
"rho": 3.14,
"Elast": 1.22
},
"Kr": {
"Z": 36,
"A": 83.8,
"rho": 3.1,
"Elast": 0.12
},
"Rb": {
"Z": 37,
"A": 85.47,
"rho": 1.53,
"Elast": 0.85
},
"Sr": {
"Z": 38,
"A": 87.62,
"rho": 2.54,
"Elast": 1.72
},
"Y": {
"Z": 39,
"A": 88.905,
"rho": 4.47,
"Elast": 4.37
},
"Zr": {
"Z": 40,
"A": 91.22,
"rho": 6.51,
"Elast": 6.25
},
"Nb": {
"Z": 41,
"A": 92.906,
"rho": 8.57,
"Elast": 7.57
},
"Mo": {
"Z": 42,
"A": 95.94,
"rho": 10.22,
"Elast": 6.82
},
"Tc": {
"Z": 43,
"A": 98,
"rho": 11.5,
"Elast": 6.85
},
"Ru": {
"Z": 44,
"A": 101.07,
"rho": 12.37,
"Elast": 6.74
},
"Rh": {
"Z": 45,
"A": 102.905,
"rho": 12.41,
"Elast": 5.75
},
"Pd": {
"Z": 46,
"A": 106.4,
"rho": 12.02,
"Elast": 3.89
},
"Ag": {
"Z": 47,
"A": 107.87,
"rho": 10.5,
"Elast": 2.95
},
"Cd": {
"Z": 48,
"A": 112.4,
"rho": 8.65,
"Elast": 1.16
},
"In": {
"Z": 49,
"A": 114.82,
"rho": 7.31,
"Elast": 2.52
},
"Sn": {
"Z": 50,
"A": 118.69,
"rho": 7.31,
"Elast": 3.14
},
"Sb": {
"Z": 51,
"A": 121.75,
"rho": 6.68,
"Elast": 2.75
},
"Te": {
"Z": 52,
"A": 127.6,
"rho": 6.24,
"Elast": 2.23
},
"I": {
"Z": 53,
"A": 126.904,
"rho": 4.93,
"Elast": 1.11
},
"Xe": {
"Z": 54,
"A": 131.3,
"rho": 3.8,
"Elast": 0.16
},
"Cs": {
"Z": 55,
"A": 132.905,
"rho": 1.9,
"Elast": 0.8
},
"Ba": {
"Z": 56,
"A": 137.34,
"rho": 3.59,
"Elast": 1.9
},
"La": {
"Z": 57,
"A": 138.91,
"rho": 6.15,
"Elast": 4.47
},
"Ce": {
"Z": 58,
"A": 140.12,
"rho": 6.77,
"Elast": 4.32
},
"Pr": {
"Z": 59,
"A": 140.907,
"rho": 6.77,
"Elast": 3.7
},
"Nd": {
"Z": 60,
"A": 144.24001,
"rho": 7.01,
"Elast": 3.4
},
"Pm": {
"Z": 61,
"A": 147,
"rho": 7.22,
"Elast": 0.1
},
"Sm": {
"Z": 62,
"A": 150.35001,
"rho": 7.52,
"Elast": 2.14
},
"Eu": {
"Z": 63,
"A": 151.96001,
"rho": 5.24,
"Elast": 1.86
},
"Gd": {
"Z": 64,
"A": 157.25,
"rho": 7.9,
"Elast": 4.14
},
"Tb": {
"Z": 65,
"A": 158.924,
"rho": 8.23,
"Elast": 4.05
},
"Dy": {
"Z": 66,
"A": 162.5,
"rho": 8.55,
"Elast": 3.04
},
"Ho": {
"Z": 67,
"A": 164.92999,
"rho": 8.8,
"Elast": 3.14
},
"Er": {
"Z": 68,
"A": 167.25999,
"rho": 9.07,
"Elast": 3.29
},
"Tm": {
"Z": 69,
"A": 168.93401,
"rho": 9.32,
"Elast": 2.42
},
"Yb": {
"Z": 70,
"A": 173.03999,
"rho": 6.9,
"Elast": 1.6
},
"Lu": {
"Z": 71,
"A": 174.97,
"rho": 9.84,
"Elast": 4.43
},
"Hf": {
"Z": 72,
"A": 178.49001,
"rho": 13.31,
"Elast": 6.44
},
"Ta": {
"Z": 73,
"A": 180.948,
"rho": 16.65,
"Elast": 8.1
},
"W": {
"Z": 74,
"A": 183.85001,
"rho": 19.35,
"Elast": 8.9
},
"Re": {
"Z": 75,
"A": 186.2,
"rho": 21.04,
"Elast": 8.03
},
"Os": {
"Z": 76,
"A": 190.2,
"rho": 22.6,
"Elast": 8.17
},
"Ir": {
"Z": 77,
"A": 192.2,
"rho": 22.4,
"Elast": 6.94
},
"Pt": {
"Z": 78,
"A": 195.09,
"rho": 21.45,
"Elast": 5.84
},
"Au": {
"Z": 79,
"A": 196.967,
"rho": 19.32,
"Elast": 3.81
},
"Hg": {
"Z": 80,
"A": 200.59,
"rho": 13.55,
"Elast": 0.67
},
"Tl": {
"Z": 81,
"A": 204.37,
"rho": 11.85,
"Elast": 1.88
},
"Pb": {
"Z": 82,
"A": 207.19,
"rho": 11.35,
"Elast": 2.03
},
"Bi": {
"Z": 83,
"A": 208.98,
"rho": 9.75,
"Elast": 2.18
},
"Po": {
"Z": 84,
"A": 210,
"rho": 9.3,
"Elast": 1.5
},
"At": {
"Z": 85,
"A": 210,
"rho": 0,
"Elast": 0.1
},
"Rn": {
"Z": 86,
"A": 222,
"rho": 0,
"Elast": 0.2
},
"Fr": {
"Z": 87,
"A": 223,
"rho": 0,
"Elast": 0.1
},
"Ra": {
"Z": 88,
"A": 226,
"rho": 0,
"Elast": 1.66
},
"Ac": {
"Z": 89,
"A": 227,
"rho": 0,
"Elast": 4.25
},
"Th": {
"Z": 90,
"A": 232.03799,
"rho": 11.72,
"Elast": 6.2
},
"Pa": {
"Z": 91,
"A": 231,
"rho": 15.4,
"Elast": 0.1
},
"U": {
"Z": 92,
"A": 238.03,
"rho": 18.95,
"Elast": 5.55
},
"": {
"Z": 0,
"A": 0,
"rho": 0,
"Elast": 0
}
}
// projectile data
const projectiles = {
"muon": {
"Z": 1,
"A": 0.1134289259,
"rho": 0,
"Elast": 0,
"ICRU": [],
"IAEA": []
},
"Li-8": {
"Z": 3,
"A": 8.02248624,
"rho": 0,
"Elast": 0,
"ICRU": [],
"IAEA": []
},
"B-12": {
"Z": 5,
"A": 12.0143526,
"rho": 0,
"Elast": 0,
"ICRU": [],
"IAEA": []
},
"Mg-31": {
"Z": 12,
"A": 30.996648,
"rho": 0,
"Elast": 0,
"ICRU": [],
"IAEA": []
}
}
// electronic stopping coefficients from Anderson & Zeigler (1977).
const az_table = {
"H": {
"AZ": [
1.262,
1.44,
242.6,
12000.0,
0.1159
]
},
"He": {
"AZ": [
1.229,
1.397,
484.5,
5873.0,
0.05225
]
},
"Li": {
"AZ": [
1.411,
1.6,
725.6,
3013.0,
0.04578
]
},
"Be": {
"AZ": [
2.248,
2.59,
966.0,
153.8,
0.03475
]
},
"B": {
"AZ": [
2.474,
2.815,
1206.0,
1060.0,
0.02855
]
},
"C": {
"AZ": [
2.631,
2.989,
1445.0,
957.2,
0.02819
]
},
"N": {
"AZ": [
2.954,
3.35,
1683.0,
1900.0,
0.02513
]
},
"O": {
"AZ": [
2.652,
3.0,
1920.0,
2000.0,
0.0223
]
},
"F": {
"AZ": [
2.085,
2.352,
2157.0,
2634.0,
0.01816
]
},
"Ne": {
"AZ": [
1.951,
2.199,
2393.0,
2699.0,
0.01568
]
},
"Na": {
"AZ": [
2.542,
2.869,
2628.0,
1854.0,
0.01472
]
},
"Mg": {
"AZ": [
3.792,
4.293,
2862.0,
1009.0,
0.01397
]
},
"Al": {
"AZ": [
4.154,
4.739,
2766.0,
164.5,
0.01397
]
},
"Si": {
"AZ": [
4.15,
4.7,
3329.0,
550.0,
0.01321
]
},
"P": {
"AZ": [
3.232,
3.647,
3561.0,
1560.0,
0.01267
]
},
"S": {
"AZ": [
3.447,
3.891,
3792.0,
1219.0,
0.01211
]
},
"Cl": {
"AZ": [
5.047,
5.714,
4023.0,
878.6,
0.01178
]
},
"Ar": {
"AZ": [
5.731,
6.5,
4253.0,
530.0,
0.01123
]
},
"K": {
"AZ": [
5.151,
5.833,
4482.0,
545.7,
0.01129
]
},
"Ca": {
"AZ": [
5.521,
6.252,
4710.0,
553.3,
0.01112
]
},
"Sc": {
"AZ": [
5.201,
5.884,
4938.0,
560.9,
0.009995
]
},
"Ti": {
"AZ": [
4.862,
5.496,
5165.0,
568.5,
0.009474
]
},
"V": {
"AZ": [
4.48,
5.055,
5391.0,
952.3,
0.009117
]
},
"Cr": {
"AZ": [
3.983,
4.489,
5616.0,
1336.0,
0.008413
]
},
"Mn": {
"AZ": [
3.469,
3.907,
5725.0,
1461.0,
0.008829
]
},
"Fe": {
"AZ": [
3.519,
3.963,
6065.0,
1243.0,
0.007782
]
},
"Co": {
"AZ": [
3.14,
3.535,
6288.0,
1372.0,
0.007361
]
},
"Ni": {
"AZ": [
3.553,
4.004,
6205.0,
555.1,
0.008763
]
},
"Cu": {
"AZ": [
3.696,
4.175,
4673.0,
387.8,
0.02188
]
},
"Zn": {
"AZ": [
4.21,
4.75,
6953.0,
295.2,
0.006809
]
},
"Ga": {
"AZ": [
5.041,
5.697,
7173.0,
202.6,
0.006725
]
},
"Ge": {
"AZ": [
5.554,
6.3,
6496.0,
110.0,
0.009689
]
},
"As": {
"AZ": [
5.323,
6.012,
7611.0,
292.5,
0.006447
]
},
"Se": {
"AZ": [
5.874,
6.656,
7395.0,
117.5,
0.007684
]
},
"Br": {
"AZ": [
5.611,
6.335,
8046.0,
365.2,
0.006244
]
},
"Kr": {
"AZ": [
6.411,
7.25,
8262.0,
220.0,
0.006087
]
},
"Rb": {
"AZ": [
5.694,
6.429,
8478.0,
292.9,
0.006087
]
},
"Sr": {
"AZ": [
6.339,
7.159,
8693.0,
330.3,
0.006003
]
},
"Y": {
"AZ": [
6.407,
7.234,
8907.0,
367.8,
0.005889
]
},
"Zr": {
"AZ": [
6.734,
7.603,
9120.0,
405.2,
0.005765
]
},
"Nb": {
"AZ": [
6.902,
7.791,
9333.0,
442.7,
0.005587
]
},
"Mo": {
"AZ": [
6.425,
7.248,
9545.0,
480.2,
0.005367
]
},
"Tc": {
"AZ": [
6.799,
7.671,
9756.0,
517.6,
0.005315
]
},
"Ru": {
"AZ": [
6.108,
6.887,
9966.0,
555.1,
0.005151
]
},
"Rh": {
"AZ": [
5.924,
6.677,
10180.0,
592.5,
0.004919
]
},
"Pd": {
"AZ": [
5.238,
5.9,
10380.0,
630.0,
0.004758
]
},
"Ag": {
"AZ": [
5.623,
6.354,
7160.0,
337.6,
0.01394
]
},
"Cd": {
"AZ": [
5.814,
6.554,
10800.0,
355.5,
0.004626
]
},
"In": {
"AZ": [
6.23,
7.024,
11010.0,
370.9,
0.00454
]
},
"Sn": {
"AZ": [
6.41,
7.227,
11210.0,
386.4,
0.004474
]
},
"Sb": {
"AZ": [
7.5,
8.48,
8608.0,
348.0,
0.009074
]
},
"Te": {
"AZ": [
6.979,
7.871,
11620.0,
392.4,
0.004402
]
},
"I": {
"AZ": [
7.725,
8.716,
11830.0,
394.8,
0.004376
]
},
"Xe": {
"AZ": [
8.231,
9.289,
12030.0,
397.3,
0.004384
]
},
"Cs": {
"AZ": [
7.287,
8.218,
12230.0,
399.7,
0.004447
]
},
"Ba": {
"AZ": [
7.899,
8.911,
12430.0,
402.1,
0.004511
]
},
"La": {
"AZ": [
8.041,
9.071,
12630.0,
404.5,
0.00454
]
},
"Ce": {
"AZ": [
7.489,
8.444,
12830.0,
406.9,
0.00442
]
},
"Pr": {
"AZ": [
7.291,
8.219,
13030.0,
409.3,
0.004298
]
},
"Nd": {
"AZ": [
7.098,
8.0,
13230.0,
411.8,
0.004182
]
},
"Pm": {
"AZ": [
6.91,
7.786,
13430.0,
414.2,
0.004058
]
},
"Sm": {
"AZ": [
6.728,
7.58,
13620.0,
416.6,
0.003976
]
},
"Eu": {
"AZ": [
6.551,
7.38,
13820.0,
419.0,
0.003877
]
},
"Gd": {
"AZ": [
6.739,
7.592,
14020.0,
421.4,
0.003863
]
},
"Tb": {
"AZ": [
6.212,
6.996,
14210.0,
423.9,
0.003725
]
},
"Dy": {
"AZ": [
5.517,
6.21,
14400.0,
426.3,
0.003632
]
},
"Ho": {
"AZ": [
5.219,
5.874,
14600.0,
428.7,
0.003498
]
},
"Er": {
"AZ": [
5.071,
5.706,
14790.0,
433.0,
0.003405
]
},
"Tm": {
"AZ": [
4.926,
5.542,
14980.0,
433.5,
0.003342
]
},
"Yb": {
"AZ": [
4.787,
5.386,
15170.0,
435.9,
0.003292
]
},
"Lu": {
"AZ": [
4.893,
5.505,
15360.0,
438.4,
0.003243
]
},
"Hf": {
"AZ": [
5.028,
5.657,
15550.0,
440.8,
0.003195
]
},
"Ta": {
"AZ": [
4.738,
5.329,
15740.0,
443.2,
0.003186
]
},
"W": {
"AZ": [
4.574,
5.144,
15930.0,
442.4,
0.003144
]
},
"Re": {
"AZ": [
5.2,
5.851,
16120.0,
441.6,
0.003122
]
},
"Os": {
"AZ": [
5.07,
5.704,
16300.0,
440.9,
0.003082
]
},
"Ir": {
"AZ": [
4.945,
5.563,
16490.0,
440.1,
0.002965
]
},
"Pt": {
"AZ": [
4.476,
5.034,
16670.0,
439.3,
0.002871
]
},
"Au": {
"AZ": [
4.856,
5.46,
18320.0,
438.5,
0.002542
]
},
"Hg": {
"AZ": [
4.308,
4.843,
17040.0,
487.8,
0.002882
]
},
"Tl": {
"AZ": [
4.723,
5.311,
17220.0,
537.0,
0.002913
]
},
"Pb": {
"AZ": [
5.319,
5.982,
17400.0,
586.3,
0.002871
]
},
"Bi": {
"AZ": [
5.956,
6.7,
17800.0,
677.0,
0.00266
]
},
"Po": {
"AZ": [
6.158,
6.928,
17770.0,
586.3,
0.002812
]
},
"At": {
"AZ": [
6.204,
6.979,
17950.0,
586.3,
0.002776
]
},
"Rn": {
"AZ": [
6.181,
6.954,
18120.0,
586.3,
0.002748
]
},
"Fr": {
"AZ": [
6.949,
7.82,
18300.0,
586.3,
0.002737
]
},
"Ra": {
"AZ": [
7.506,
8.448,
18480.0,
586.3,
0.002727
]
},
"Ac": {
"AZ": [
7.649,
8.609,
18660.0,
586.3,
0.002697
]
},
"Th": {
"AZ": [
7.71,
8.679,
18830.0,
586.3,
0.002641
]
},
"Pa": {
"AZ": [
7.407,
8.336,
19010.0,
586.3,
0.002603
]
},
"U": {
"AZ": [
7.29,
8.204,
19180.0,
586.3,
0.002573
]
}
}
// electronic stopping coefficients from Berger et al. (1991)
const icru_table = {
"H": {
"ICRU": [
1.254,
1.44,
242.6,
12000.0,
0.1159
]
},
"He": {
"ICRU": [
1.229,
1.397,
484.5,
5873.0,
0.05225
]
},
"Li": {
"ICRU": [
1.411,
1.6,
725.6,
3013.0,
0.04578
]
},
"Be": {
"ICRU": [
2.248,
2.59,
966.0,
153.8,
0.03475
]
},
"B": {
"ICRU": [
2.474,
2.815,
1206.0,
1060.0,
0.02855
]
},
"C": {
"ICRU": [
2.2977695717982596,
2.601,
1701.0,
1279.0,
0.01638
]
},
"N": {
"ICRU": [
2.954,
3.35,
1683.0,
1900.0,
0.02513
]
},
"O": {
"ICRU": [
2.652,
3.0,
1920.0,
2000.0,
0.0223
]
},
"F": {
"ICRU": [
2.085,
2.352,
2157.0,
2634.0,
0.01816
]
},
"Ne": {
"ICRU": [
1.951,
2.199,
2393.0,
2699.0,
0.01568
]
},
"Na": {
"ICRU": [
2.542,
2.869,
2628.0,
1854.0,
0.01472
]
},
"Mg": {
"ICRU": [
3.791,
4.293,
2862.0,
1009.0,
0.01397
]
},
"Al": {
"ICRU": [
4.154,
4.739,
2766.0,
164.5,
0.02023
]
},
"Si": {
"ICRU": [
4.914,
5.598,
3193.0,
232.7,
0.01419
]
},
"P": {
"ICRU": [
3.232,
3.647,
3561.0,
1560.0,
0.01267
]
},
"S": {
"ICRU": [
3.447,
3.891,
3792.0,
1219.0,
0.01211
]
},
"Cl": {
"ICRU": [
5.301,
6.008,
3969.0,
645.1,
0.01183
]
},
"Ar": {
"ICRU": [
5.731,
6.5,
4253.0,
530.0,
0.01123
]
},
"K": {
"ICRU": [
5.152,
5.833,
4482.0,
545.7,
0.01129
]
},
"Ca": {
"ICRU": [
5.521,
6.252,
4710.0,
553.3,
0.01112
]
},
"Sc": {
"ICRU": [
5.201,
5.884,
4938.0,
560.9,
0.009995
]
},
"Ti": {
"ICRU": [
4.858,
5.489,
5260.0,
651.1,
0.00893
]
},
"V": {
"ICRU": [
4.479,
5.055,
5391.0,
952.3,
0.009117
]
},
"Cr": {
"ICRU": [
3.983,
4.489,
5616.0,
1336.0,
0.008413
]
},
"Mn": {
"ICRU": [
3.469,
3.907,
5725.0,
1461.0,
0.008829
]
},
"Fe": {
"ICRU": [
3.519,
3.963,
6065.0,
1243.0,
0.007782
]
},
"Co": {
"ICRU": [
3.14,
3.535,
6288.0,
1372.0,
0.007361
]
},
"Ni": {
"ICRU": [
3.553,
4.004,
6205.0,
555.1,
0.008763
]
},
"Cu": {
"ICRU": [
3.696,
4.194,
4649.0,
81.13,
0.02242
]
},
"Zn": {
"ICRU": [
4.21,
4.75,
6953.0,
295.2,
0.006809
]
},
"Ga": {
"ICRU": [
5.041,
5.697,
7173.0,
202.6,
0.006725
]
},
"Ge": {
"ICRU": [
5.554,
6.3,
6496.0,
110.0,
0.009689
]
},
"As": {
"ICRU": [
5.323,
6.012,
7611.0,
292.5,
0.006447
]
},
"Se": {
"ICRU": [
5.874,
6.656,
7395.0,
117.5,
0.007684
]
},
"Br": {
"ICRU": [
6.658,
7.536,
7694.0,
222.3,
0.006509
]
},
"Kr": {
"ICRU": [
6.413,
7.24,
11850.0,
153.7,
0.00288
]
},
"Rb": {
"ICRU": [
5.694,
6.429,
8478.0,
292.9,
0.006087
]
},
"Sr": {
"ICRU": [
6.339,
7.159,
8693.0,
330.3,
0.006003
]
},
"Y": {
"ICRU": [
6.407,
7.234,
8907.0,
367.8,
0.005889
]
},
"Zr": {
"ICRU": [
6.734,
7.603,
9120.0,
405.2,
0.005765
]
},
"Nb": {
"ICRU": [
6.901,
7.791,
9333.0,
442.7,
0.005587
]
},
"Mo": {
"ICRU": [
6.424,
7.248,
9545.0,
480.2,
0.005376
]
},
"Tc": {
"ICRU": [
6.799,
7.671,
9756.0,
517.6,
0.005315
]
},
"Ru": {
"ICRU": [
6.109,
6.887,
9966.0,
555.1,
0.005151
]
},
"Rh": {
"ICRU": [
5.924,
6.677,
10180.0,
592.5,
0.004919
]
},
"Pd": {
"ICRU": [
5.238,
5.9,
10380.0,
630.0,
0.004758
]
},
"Ag": {
"ICRU": [
5.345,
6.038,
6790.0,
397.8,
0.01676
]
},
"Cd": {
"ICRU": [
5.814,
6.554,
10800.0,
355.5,
0.004626
]
},
"In": {
"ICRU": [
6.229,
7.024,
11010.0,
370.9,
0.00454
]
},
"Sn": {
"ICRU": [
6.409,
7.227,
11210.0,
386.4,
0.004474
]
},
"Sb": {
"ICRU": [
7.5,
8.48,
8608.0,
348.0,
0.009074
]
},
"Te": {
"ICRU": [
6.979,
7.871,
11620.0,
392.4,
0.004402
]
},
"I": {
"ICRU": [
7.725,
8.716,
11830.0,
394.8,
0.004376
]
},
"Xe": {
"ICRU": [
8.337,
9.425,
10510.0,
269.6,
0.006206
]
},
"Cs": {
"ICRU": [
7.287,
8.218,
12230.0,
399.7,
0.004447
]
},
"Ba": {
"ICRU": [
7.899,
8.911,
12430.0,
402.1,
0.004511
]
},
"La": {
"ICRU": [
8.041,
9.071,
12630.0,
404.5,
0.00454
]
},
"Ce": {
"ICRU": [
7.488,
8.444,
12830.0,
406.9,
0.00442
]
},
"Pr": {
"ICRU": [
7.291,
8.219,
13030.0,
409.3,
0.004298
]
},
"Nd": {
"ICRU": [
7.098,
8.0,
13230.0,
411.8,
0.004182
]
},
"Pm": {
"ICRU": [
6.909,
7.786,
13430.0,
414.2,
0.004058
]
},
"Sm": {
"ICRU": [
6.728,
7.58,
13620.0,
416.6,
0.003976
]
},
"Eu": {
"ICRU": [
6.551,
7.38,
13820.0,
419.0,
0.003877
]
},
"Gd": {
"ICRU": [
6.739,
7.592,
14020.0,
421.4,
0.003863
]
},
"Tb": {
"ICRU": [
6.212,
6.996,
14210.0,
423.9,
0.003725
]
},
"Dy": {
"ICRU": [
5.517,
6.21,
14400.0,
426.3,
0.003632
]
},
"Ho": {
"ICRU": [
5.22,
5.874,
14600.0,
428.7,
0.003498
]
},
"Er": {
"ICRU": [
5.071,
5.706,
14790.0,
433.0,
0.003405
]
},
"Tm": {
"ICRU": [
4.926,
5.542,
14980.0,
433.5,
0.003342
]
},
"Yb": {
"ICRU": [
4.788,
5.386,
15170.0,
435.9,
0.003292
]
},
"Lu": {
"ICRU": [
4.893,
5.505,
15360.0,
438.4,
0.003243
]
},
"Hf": {
"ICRU": [
5.028,
5.657,
15550.0,
440.8,
0.003195
]
},
"Ta": {
"ICRU": [
4.738,
5.329,
15740.0,
443.2,
0.003186
]
},
"W": {
"ICRU": [
4.587,
5.16,
15410.0,
415.3,
0.003406
]
},
"Re": {
"ICRU": [
5.201,
5.851,
16120.0,
441.6,
0.003122
]
},
"Os": {
"ICRU": [
5.071,
5.704,
16300.0,
440.9,
0.003082
]
},
"Ir": {
"ICRU": [
4.946,
5.563,
16490.0,
440.1,
0.002965
]
},
"Pt": {
"ICRU": [
4.477,
5.034,
16670.0,
439.3,
0.002871
]
},
"Au": {
"ICRU": [
4.844,
5.458,
7852.0,
975.8,
0.02077
]
},
"Hg": {
"ICRU": [
4.307,
4.843,
17040.0,
487.8,
0.002882
]
},
"Tl": {
"ICRU": [
4.723,
5.311,
17220.0,
537.0,
0.002913
]
},
"Pb": {
"ICRU": [
5.319,
5.982,
17400.0,
586.3,
0.002871
]
},
"Bi": {
"ICRU": [
5.956,
6.7,
17800.0,
677.0,
0.00266
]
},
"Po": {
"ICRU": [
6.158,
6.928,
17770.0,
586.3,
0.002812
]
},
"At": {
"ICRU": [
6.203,
6.979,
17950.0,
586.3,
0.002776
]
},
"Rn": {
"ICRU": [
6.181,
6.954,
18120.0,
586.3,
0.002748
]
},
"Fr": {
"ICRU": [
6.949,
7.82,
18300.0,
586.3,
0.002737
]
},
"Ra": {
"ICRU": [
7.506,
8.448,
18480.0,
586.3,
0.002727
]
},
"Ac": {
"ICRU": [
7.648,
8.609,
18660.0,
586.3,
0.002697
]
},
"Th": {
"ICRU": [
7.711,
8.679,
18830.0,
586.3,
0.002641
]
},
"Pa": {
"ICRU": [
7.407,
8.336,
19010.0,
586.3,
0.002603
]
},
"U": {
"ICRU": [
7.29,
8.204,
19180.0,
586.3,
0.002673
]
}
}
// Electronic stopping coefficients derived from analyzing the IAEA database
// https://www-nds.iaea.org/stopping/
// <ost of the values here are derived from version 2021-12, with the exception
// of O, Si, and Nb, where version 2024-03 was used.
// Details of the analysis can be found in:
// R. M. L. McFadden et al., Phys. Rev. Appl. 19, 044018 (2023).
// R. M. L. McFadden et al., unpublished (2025).
const iaea_table = {
"H": {
"IAEA": [
1.21941306,
1.39705253,
316.72610292,
3622.08960714,
0.03609996
]
},
"He": {
"IAEA": []
},
"Li": {
"IAEA": [
2.55867734,
2.98398784,
672.22623329,
223.63937274,
0.05756479
]
},
"Be": {
"IAEA": [
2.13846776,
2.4302455,
1353.80005691,
500.78353377,
0.01143076
]
},
"B": {
"IAEA": [
2.92598306,
3.3327191,
1523.83401267,
574.31712932,
0.01671298
]
},
"C": {
"IAEA": [
2.51057212,
2.84874359,
1325.63086028,
2111.57332043,
0.03815981
]
},
"N": {
"IAEA": [
3.03231463,
3.44621744,
1287.27189959,
3445.25706665,
0.0690955
]
},
"O": {
"IAEA": [
2.64525591541112,
2.99207340296791,
1983.59086293942,
1899.40582231425,
0.0194714278702055
]
},
"F": {
"IAEA": []
},
"Ne": {
"IAEA": []
},
"Na": {
"IAEA": []
},
"Mg": {
"IAEA": [
4.3221962,
4.95332934,
2711.05757349,
99.32422512,
0.01692829
]
},
"Al": {
"IAEA": [
4.16266645,
4.7394264,
3007.21967155,
192.15430476,
0.01478852
]
},
"Si": {
"IAEA": [
4.92174725194867,
5.61439738985486,
2614.54841139624,
369.526489722846,
0.0253413402253883
]
},
"P": {
"IAEA": []
},
"S": {
"IAEA": []
},
"Cl": {
"IAEA": [
5.18954121,
5.87010049,
5726.20198003,
338.90592713,
0.00527894
]
},
"Ar": {
"IAEA": []
},
"K": {
"IAEA": []
},
"Ca": {
"IAEA": [
7.3841902,
8.41800474,
4257.94547049,
311.84732092,
0.01482602
]
},
"Sc": {
"IAEA": [
6.2736694,
7.11486087,
5202.32117316,
349.64931132,
0.00808596
]
},
"Ti": {
"IAEA": [
4.89215322,
5.5299609,
4733.05002706,
821.5492998,
0.01141794
]
},
"V": {
"IAEA": [
4.39418898,
4.95984106,
5216.28902299,
873.00768319,
0.00982301
]
},
"Cr": {
"IAEA": [
4.22355616,
4.76724205,
4805.40719904,
1062.25237873,
0.01508696
]
},
"Mn": {
"IAEA": [
3.45870888,
3.89580224,
5452.18588822,
1778.16054656,
0.0105738
]
},
"Fe": {
"IAEA": [
3.69846643,
4.16916938,
5392.90710239,
1040.38447053,
0.01152298
]
},
"Co": {
"IAEA": [
3.55890909,
4.00775461,
6796.04145497,
933.86447935,
0.00558368
]
},
"Ni": {
"IAEA": [
3.42262695,
3.8623488,
4759.65515188,
520.18934465,
0.02026669
]
},
"Cu": {
"IAEA": [
3.54912421,
4.02142201,
4344.22605325,
128.32905834,
0.02886218
]
},
"Zn": {
"IAEA": [
4.31895518,
4.91763977,
5346.0392556,
46.16548699,
0.01518164
]
},
"Ga": {
"IAEA": [
4.11015447,
4.63998531,
7681.41419449,
149.38563152,
0.00628772
]
},
"Ge": {
"IAEA": [
5.80984815,
6.60667983,
6039.04808738,
87.19744473,
0.01158192
]
},
"As": {
"IAEA": []
},
"Se": {
"IAEA": [
5.58734301,
6.31119973,
8423.01872583,
221.61559131,
0.00530434
]
},
"Br": {
"IAEA": [
6.28480648,
7.09489448,
11732.76411988,
150.96205589,
0.00281298
]
},
"Kr": {
"IAEA": [
6.47378266,
7.31581518,
9199.06183849,
216.98680923,
0.00437111
]
},
"Rb": {
"IAEA": [
8.48759187,
9.62951209,
7271.11124671,
272.67996745,
0.00818815
]
},
"Sr": {
"IAEA": [
8.66447838,
9.84912713,
5979.31960977,
334.15705556,
0.01184544
]
},
"Y": {
"IAEA": [
7.54854264,
8.54789357,
6885.34110669,
429.5325211,
0.00930644
]
},
"Zr": {
"IAEA": [
6.24168401,
7.04236496,
10674.06252597,
269.66181445,
0.00339805
]
},
"Nb": {
"IAEA": [
5.83800567770461,
6.58179495443619,
12601.224783492,
206.20626866439,
0.00270151372920993
]
},
"Mo": {
"IAEA": [
5.58867862,
6.29983727,
9573.1660566,
523.57577519,
0.00473082
]
},
"Tc": {
"IAEA": []
},
"Ru": {
"IAEA": []
},
"Rh": {
"IAEA": []
},
"Pd": {
"IAEA": [
4.74762031,
5.34871025,
8086.17802763,
942.05721488,
0.00842671
]
},
"Ag": {
"IAEA": [
5.4205224,
6.17824432,
4642.1821495,
90.75350922,
0.06061521
]
},
"Cd": {
"IAEA": [
4.89128643,
5.50754704,
11831.77188508,
396.06905848,
0.00412049
]
},
"In": {
"IAEA": [
5.98978289,
6.7705004,
6343.54720062,
564.65410792,
0.0227052
]
},
"Sn": {
"IAEA": [
6.73191335,
7.61854414,
6946.18830598,
347.1939131,
0.01677246
]
},
"Sb": {
"IAEA": [
7.92400389,
8.99628409,
5736.2988109,
403.11722206,
0.02973016
]
},
"Te": {
"IAEA": []
},
"I": {
"IAEA": []
},
"Xe": {
"IAEA": [
8.968264,
10.16224223,
8543.2730486,
283.90067433,
0.01019984
]
},
"Cs": {
"IAEA": []
},
"Ba": {
"IAEA": []
},
"La": {
"IAEA": [
8.16949326,
9.21144395,
13933.96869212,
429.99442382,
0.0034744
]
},
"Ce": {
"IAEA": []
},
"Pr": {
"IAEA": []
},
"Nd": {
"IAEA": [
8.60801371,
10.26060598,
6798.65217629,
7.06731816,
0.02713704
]
},
"Pm": {
"IAEA": []
},
"Sm": {
"IAEA": []
},
"Eu": {
"IAEA": []
},
"Gd": {
"IAEA": [
8.38379061,
9.50012183,
10804.38767518,
110.71066098,
0.00761735
]
},
"Tb": {
"IAEA": [
8.69907359,
10.36476708,
5985.09307671,
7.76175759,
0.04237031
]
},
"Dy": {
"IAEA": [
6.69350026,
7.81543484,
5289.65107666,
12.11560717,
0.05746489
]
},
"Ho": {
"IAEA": []
},
"Er": {
"IAEA": [
4.31497049,
4.85346547,
12995.54504003,
691.00380321,
0.00499022
]
},
"Tm": {
"IAEA": []
},
"Yb": {
"IAEA": [
6.38304258,
7.21491284,
12327.74719685,
82.30064162,
0.00516845
]
},
"Lu": {
"IAEA": [
7.00205766,
8.09268611,
4589.67801229,
31.06321362,
0.11142124
]
},
"Hf": {
"IAEA": [
5.32763157,
6.00140558,
13853.49562768,
206.93150053,
0.00411522
]
},
"Ta": {
"IAEA": [
5.15280708,
5.8054531,
12417.25115167,
231.48549646,
0.00530723
]
},
"W": {
"IAEA": [
4.81261962,
5.41579909,
15245.84079976,
286.09077182,
0.00344862
]
},
"Re": {
"IAEA": [
4.26021967,
4.80273372,
3191.05524918,
76051.48730893,
3.161e-05
]
},
"Os": {
"IAEA": []
},
"Ir": {
"IAEA": [
4.16543126,
4.68849191,
6887.94139926,
4263.90641919,
4e-08
]
},
"Pt": {
"IAEA": [
4.62228847,
5.20033612,
16308.49878855,
266.02263275,
0.0030956
]
},
"Au": {
"IAEA": [
4.84253074,
5.44769072,
15602.21662947,
412.25485588,
0.00331383
]
},
"Hg": {
"IAEA": []
},
"Tl": {
"IAEA": []
},
"Pb": {
"IAEA": [
9.42473604,
17.46235676,
11946.86905435,
1e-08,
0.00652912
]
},
"Bi": {
"IAEA": [
10.25823796,
12.26670424,
7765.05387481,
7.37274504,
0.0231005
]
},
"Po": {
"IAEA": []
},
"At": {
"IAEA": []
},
"Rn": {
"IAEA": []
},
"Fr": {
"IAEA": []
},
"Ra": {
"IAEA": []
},
"Ac": {
"IAEA": []
},
"Th": {
"IAEA": []
},
"Pa": {
"IAEA": []
},
"U": {
"IAEA": []
},
"": {
"IAEA": []
}
}
// list of JS objects containing all projectile/target element data
const dataObjects = [
elements,
projectiles,
az_table,
icru_table,
iaea_table
];
// empty "main" object - to be filled with *all* projectile/target data
const elemPars = {}
// loop over each JS object...
for (const d of dataObjects) {
// ...and deep-merge them to the "main" object
mergeDeep(elemPars, d);
};
// Extra densities (i.e., for chemical compounds)
const rhos = {
"NbN": 8.47,
"Bi2Se3": 7.51,
"La2CuO4": 7.08,
"La1.84Sr0.16CuO4": 6.94,
"N2": 1.145,
"ZnO": 5.61,
"ZnSe": 5.26,
"ZnS": 4.09,
"ZrO": 6.0,
"Gd3Ga5O12": 7.08,
"MgAl2O4": 3.6,
"NdGaO3": 7.57,
"YAlO3": 4.88,
"Y3Al5O12": 4.55,
"LiF": 2.6,
"CaF2": 3.18,
"BaFe": 4.83,
"MgF2": 3.18,
"SiO2": 2.65,
"TiO2": 4.26,
"KTaO3": 6.967,
"LaAlO3": 6.7,
"Al2O3": 3.98,
"SrTiO3": 5.12,
"SrLaGaO4": 6.389,
"SrLaAlO4": 5.924,
"NbSe2": 6.3,
"MgO": 3.58,
"YBa2Cu3O7": 6.54,
"GaAs": 5.32,
"C60": 1.65
}
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;
}
}
let transientWarningTimeout = null;
function showTransientWarning(message, targetElement, duration = 4000) {
let warning = document.getElementById('transientWarning');
if (!warning) {
warning = document.createElement('div');
warning.id = 'transientWarning';
warning.className = 'transient-warning';
document.body.appendChild(warning);
}
warning.textContent = message;
if (targetElement) {
const rect = targetElement.getBoundingClientRect();
warning.style.left = `${Math.max(12, rect.left + window.scrollX)}px`;
warning.style.top = `${rect.bottom + window.scrollY + 8}px`;
}
warning.classList.add('visible');
if (transientWarningTimeout) {
clearTimeout(transientWarningTimeout);
}
transientWarningTimeout = setTimeout(() => {
warning.classList.remove('visible');
}, duration);
}
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 (const key of Object.keys(layer_formula)) {
const amount = Number(layer_formula[key]);
if (isFinite(amount) && amount > 0) {
stoichiometry_sum += amount;
}
}
// estimate the density from additive elemental molar volumes:
// rho ~= total molar mass / total molar volume
let total_molar_mass = 0;
let total_molar_volume = 0;
for (const key of Object.keys(layer_formula)) {
const amount = Number(layer_formula[key]);
if (!isFinite(amount) || amount <= 0) {
continue;
}
if (!elemPars[key] || !isFinite(elemPars[key].rho) || !isFinite(elemPars[key].A) || elemPars[key].rho <= 0) {
continue;
}
total_molar_mass += amount * elemPars[key].A;
total_molar_volume += amount * (elemPars[key].A / elemPars[key].rho);
}
rho = 0;
if (total_molar_volume > 0) {
rho = total_molar_mass / total_molar_volume;
}
if (rho > 0) {
showTransientWarning("Warning: Estimated density from additive elemental molar volumes; please verify manually.", document.getElementById(rhoLi));
}
}
}
// 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;
}
}
async function getDensity(formula) {
function normalizeCodFormula(codFormula) {
return codFormula.replace(/-/g, ' ').replace(/\s+/g, '').trim();
}
function sameFormula(formulaA, formulaB) {
const a = parse_formula(formulaA);
const b = parse_formula(formulaB);
const aKeys = Object.keys(a).sort();
const bKeys = Object.keys(b).sort();
if (aKeys.length !== bKeys.length) {
return false;
}
for (let i = 0; i < aKeys.length; i++) {
const key = aKeys[i];
if (key !== bKeys[i] || Math.abs(a[key] - b[key]) > 1e-8) {
return false;
}
}
return true;
}
function scaleFormula(formulaObj, factor) {
const scaled = {};
for (const key of Object.keys(formulaObj)) {
scaled[key] = formulaObj[key] * factor;
}
return scaled;
}
function sameFormulaObject(formulaA, formulaB) {
const aKeys = Object.keys(formulaA).sort();
const bKeys = Object.keys(formulaB).sort();
if (aKeys.length !== bKeys.length) {
return false;
}
for (let i = 0; i < aKeys.length; i++) {
const key = aKeys[i];
if (key !== bKeys[i] || Math.abs(formulaA[key] - formulaB[key]) > 1e-8) {
return false;
}
}
return true;
}
function molarMass(formulaObj) {
let mass = 0;
for (const key of Object.keys(formulaObj)) {
if (!elemPars[key] || !elemPars[key].A) {
return null;
}
mass += formulaObj[key] * elemPars[key].A;
}
return mass;
}
try {
const parsedFormula = parse_formula(formula);
const formulaMass = molarMass(parsedFormula);
if (formulaMass == null) {
return null;
}
const encodedFormula = encodeURIComponent(formula);
const codRes = await fetch(`https://www.crystallography.net/cod/result?formula=${encodedFormula}&format=json`);
if (!codRes.ok) {
throw new Error(`COD lookup failed with status ${codRes.status}`);
}
let records = await codRes.json();
if ((!Array.isArray(records) || records.length === 0) && Object.keys(parsedFormula).length > 0) {
const params = new URLSearchParams();
const elements = Object.keys(parsedFormula).sort();
elements.forEach((element, index) => {
params.append(`el${index + 1}`, element);
});
params.append('strictmin', elements.length);
params.append('strictmax', elements.length);
params.append('format', 'json');
const fallbackRes = await fetch(`https://www.crystallography.net/cod/result?${params.toString()}`);
if (!fallbackRes.ok) {
throw new Error(`COD fallback lookup failed with status ${fallbackRes.status}`);
}
records = await fallbackRes.json();
}
if (!Array.isArray(records) || records.length === 0) {
return null;
}
for (const record of records) {
const z = parseFloat(record.Z);
const vol = parseFloat(record.vol);
if (!isFinite(z) || !isFinite(vol) || z <= 0 || vol <= 0) {
continue;
}
const recordFormula = normalizeCodFormula(record.formula || '');
if (!sameFormula(formula, recordFormula)) {
continue;
}
const cellFormula = normalizeCodFormula(record.cellformula || '');
if (cellFormula !== '') {
const parsedCellFormula = parse_formula(cellFormula);
const expectedCellFormula = scaleFormula(parsedFormula, z);
if (!sameFormulaObject(parsedCellFormula, expectedCellFormula)) {
continue;
}
}
// Convert unit-cell data (A^3, Z) into density in g/cm^3.
return 1.66053906660 * z * formulaMass / vol;
}
} catch (error) {
console.error("Error fetching data from COD:", error);
}
return null;
}