Issues (6)

build_scripts/generate_decay.js (2 issues)

Labels
Severity
1
/* eslint-env node */
2
/*jslint node: true */
3
'use strict';
4
5
let jsonfile = require('jsonfile');
6
7
let resources = jsonfile.readFileSync('build/data/resources.json');
8
let elements = jsonfile.readFileSync('build/data/elements.json');
9
10
let isotopeMatrix = {};
11
12
function getAtomBreakdown(isotope) {
13
  let element = isotope.replace(/^[0-9]*/, '');
14
  let number = parseInt(isotope.replace(element, ''), 10);
15
  let elementNumber = elements[element].number;
16
  let neutronNumber = number - elementNumber;
17
  return {
18
    atom: elementNumber,
19
    n: neutronNumber
20
  };
21
}
22
23
for (let element in elements) {
24
  for (let isotope in elements[element].isotopes) {
25
    let breakdown = getAtomBreakdown(isotope);
26
    if (!isotopeMatrix[breakdown.atom]) {
27
      isotopeMatrix[breakdown.atom] = {};
28
    }
29
    isotopeMatrix[breakdown.atom][breakdown.n] = isotope;
30
  }
31
}
32
33
for (let element in elements) {
34
  for (let key in elements[element].isotopes) {
35
    let isotope = resources[key];
36
    if (isotope.decay) {
37
      let ratioSum = 0;
38
      for (let decay in isotope.decay.decay_types) {
39
        ratioSum += isotope.decay.decay_types[decay].ratio;
40
        isotope.decay.decay_types[decay].reaction = generateReaction(key, decay);
41
      }
42
      let difference = 1 - ratioSum;
43
      if (Math.abs(difference) > 1e-7) {
44
        throw new Error('Ratios add up to '.concat(1 - difference, ' for ', key));
45
      }
46
    }
47
  }
48
}
49
50
function generateReaction(isotope, type) {
51
  switch (type) {
52
  case 'beta-':
53
    return calculateReaction(isotope, 1, 'e-', 1, 0);
54
  case '2beta-':
55
    return calculateReaction(isotope, 2, 'e-', 2, 0);
56
  case 'beta+':
57
    return calculateReaction(isotope, 1, 'e+', -1, 0);
58
  case '2beta+':
59
    return calculateReaction(isotope, 2, 'e+', -2, 0);
60
  case 'electron_capture':
61
    return calculateReaction(isotope, 1, null, -1, 0);
62
  case 'alpha':
63
    return calculateReaction(isotope, 1, 'He2+', -2, -4);
64
  case 'beta+p':
65
    return calculateReaction(isotope, 1, 'e-', -2, -1);
66
  case 'beta-n':
67
    return calculateReaction(isotope, 1, 'e+', 1, -1);
68
  case 'SF':
69
    return calculateSF(isotope);
70
  default:
71
    throw new Error('Unrecognized decay type: ' + type);
72
  }
73
}
74
75
function calculateReaction(isotope, number, particle, protonDifference, isotopeDifference) {
76
  let element = isotope.replace(/^[0-9]*/, '');
77
  let elementNumber = elements[element].number - 1;
78
  let listElements = Object.keys(elements);
79
  let otherElement = listElements[elementNumber + protonDifference];
80
81
  let isotopeNumber = Number(isotope.replace(element, '')) + isotopeDifference;
82
  let product = isotopeNumber + otherElement;
83
84
  // We need all this convoluted logic to work around missing isotopes in the data set
85
  // essentially we look for the closest isotope to the target one, and consume/produce
86
  // free neutrons in the process
87
  let distance = 0;
88
  if (!resources[product]) {
89
    let candidate = null;
90
    // first we start looking for lighter isotopes
91
    for (let otherNumber = isotopeNumber; otherNumber > 0; otherNumber--) {
92
      let otherProduct = otherNumber + otherElement;
93
      if (resources[otherProduct]) {
94
        candidate = otherProduct;
95
        distance = isotopeNumber - otherNumber;
96
        break;
97
      }
98
    }
99
    // pay attention to the upper bound. 300 is bigger than any known isotope, so it is safe
100
    for (let otherNumber = isotopeNumber; otherNumber < 300; otherNumber++) {
101
      let otherProduct = otherNumber + otherElement;
102
      if (resources[otherProduct]) {
103
        // we only replace the candidate if the distance is smaller
104
        if (isotopeNumber - otherNumber < Math.abs(distance)) {
105
          candidate = otherProduct;
106
          distance = isotopeNumber - otherNumber;
107
        }
108
        break;
109
      }
110
    }
111
    if (!candidate) {
112
      throw new Error('No candidate found for ' + isotope + ' replacing the missing isotope ' + product);
113
    }
114
    product = candidate;
115
  }
116
  let energy = resources[isotope].energy - resources[product].energy;
117
  if (distance < 0) {
118
    energy -= resources.n.energy * distance;
119
  }
120
121
  let reaction = {};
122
  reaction.reactant = {};
123
  reaction.reactant[isotope] = number;
124
  // if the isotope is heavier, the distance is negative and we produce neutrons
125
  if (distance < 0) {
126
    reaction.reactant.n = Math.abs(distance);
127
  }
128
  reaction.product = {};
129
  reaction.product[product] = number;
130
  if (particle) {
131
    reaction.product[particle] = number;
132
  }
133
  // if the isotope is lighter, the distance is positive and we produce neutrons
134
  if (distance > 0) {
135
    reaction.product.n = distance;
136
  }
137
  reaction.product.eV = energy;
138
139
  return reaction;
140
}
141
142
function calculateSF(isotope) {
143
  let breakdown = getAtomBreakdown(isotope);
144
  let halfNeutron = Math.floor(breakdown.n / 2);
145
  let halfAtom = Math.floor(breakdown.atom / 2);
146
  let firstHalf;
147
  for (let i = halfNeutron; i > 0; i--) {
148
    if (isotopeMatrix[halfAtom][i]) {
149
      firstHalf = isotopeMatrix[halfAtom][i];
150
      break;
151
    }
152
  }
153
  let secondHalfNeutron = breakdown.n - halfNeutron;
154
  let secondHalfAtom = breakdown.atom - halfAtom;
155
156
  let secondHalf;
157
  for (let i = secondHalfNeutron; i > 0; i--) {
158
    if (isotopeMatrix[secondHalfAtom][i]) {
159
      secondHalf = isotopeMatrix[secondHalfAtom][i];
160
      break;
161
    }
162
  }
163
  let firstHalfBreakdown = getAtomBreakdown(firstHalf);
0 ignored issues
show
The variable firstHalf seems to not be initialized for all possible execution paths. Are you sure getAtomBreakdown handles undefined variables?
Loading history...
164
  let secondHalfBreakdown = getAtomBreakdown(secondHalf);
0 ignored issues
show
The variable secondHalf seems to not be initialized for all possible execution paths. Are you sure getAtomBreakdown handles undefined variables?
Loading history...
165
  let neutronExcess = breakdown.n - firstHalfBreakdown.n - secondHalfBreakdown.n;
166
167
  let energy = resources[isotope].energy - resources[firstHalf].energy -
168
    resources[secondHalf].energy - resources.n.energy * neutronExcess;
169
170
  let reaction = {};
171
  reaction.reactant = {};
172
  reaction.reactant[isotope] = 1;
173
  reaction.product = {};
174
  reaction.product[firstHalf] = 1;
175
  reaction.product[secondHalf] = reaction.product[secondHalf]+1 || 1;
176
  if (neutronExcess > 0) {
177
    reaction.product.n = neutronExcess;
178
  }
179
  if (energy > 0) {
180
    reaction.product.eV = energy;
181
  }
182
  return reaction;
183
}
184
185
jsonfile.writeFileSync('build/data/resources.json', resources, {
186
  spaces: 2
187
});
188
189
jsonfile.writeFileSync('build/data/elements.json', elements, {
190
  spaces: 2
191
});
192