build_scripts/generate_decay.js   B
last analyzed

Complexity

Total Complexity 39
Complexity/F 9.75

Size

Lines of Code 189
Function Count 4

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
wmc 39
c 1
b 0
f 0
nc 20
mnd 4
bc 29
fnc 4
dl 0
loc 189
rs 8.2857
bpm 7.25
cpm 9.75
noi 2

4 Functions

Rating   Name   Duplication   Size   Complexity  
A generate_decay.js ➔ getAtomBreakdown 0 10 1
C generate_decay.js ➔ generateReaction 0 24 11
C generate_decay.js ➔ calculateSF 0 42 7
C generate_decay.js ➔ calculateReaction 0 66 12
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
Bug introduced by
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
Bug introduced by
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