Completed
Push — master ( 93915b...c11f93 )
by Andres
8s
created

generate_decay.js ➔ generateReaction   C

Complexity

Conditions 11
Paths 9

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
nc 9
dl 0
loc 24
rs 5.3305
c 0
b 0
f 0
nop 2

How to fix   Complexity   

Complexity

Complex classes like generate_decay.js ➔ generateReaction often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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
    if(distance !== 0){
115
      console.log('Isotope ',isotope,'decays to',candidate,'instead of',product,'with distance',distance);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
116
    }
117
    product = candidate;
118
  }
119
  let energy = resources[isotope].energy - resources[product].energy;
120
  if (distance < 0) {
121
    energy -= resources.n.energy * distance;
122
  }
123
124
  let reaction = {};
125
  reaction.reactant = {};
126
  reaction.reactant[isotope] = number;
127
  // if the isotope is heavier, the distance is negative and we produce neutrons
128
  if (distance < 0) {
129
    reaction.reactant.n = Math.abs(distance);
130
  }
131
  reaction.product = {};
132
  reaction.product[product] = number;
133
  if (particle) {
134
    reaction.product[particle] = number;
135
  }
136
  // if the isotope is lighter, the distance is positive and we produce neutrons
137
  if (distance > 0) {
138
    reaction.product.n = distance;
139
  }
140
  reaction.product.eV = energy;
141
142
  return reaction;
143
}
144
145
function calculateSF(isotope) {
146
  let breakdown = getAtomBreakdown(isotope);
147
  let halfNeutron = Math.floor(breakdown.n / 2);
148
  let halfAtom = Math.floor(breakdown.atom / 2);
149
  let firstHalf;
150
  for (let i = halfNeutron; i > 0; i--) {
151
    if (isotopeMatrix[halfAtom][i]) {
152
      firstHalf = isotopeMatrix[halfAtom][i];
153
      break;
154
    }
155
  }
156
  let secondHalfNeutron = breakdown.n - halfNeutron;
157
  let secondHalfAtom = breakdown.atom - halfAtom;
158
159
  let secondHalf;
160
  for (let i = secondHalfNeutron; i > 0; i--) {
161
    if (isotopeMatrix[secondHalfAtom][i]) {
162
      secondHalf = isotopeMatrix[secondHalfAtom][i];
163
      break;
164
    }
165
  }
166
  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...
167
  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...
168
  let neutronExcess = breakdown.n - firstHalfBreakdown.n - secondHalfBreakdown.n;
169
170
  let energy = resources[isotope].energy - resources[firstHalf].energy -
171
    resources[secondHalf].energy - resources.n.energy * neutronExcess;
172
173
  let reaction = {};
174
  reaction.reactant = {};
175
  reaction.reactant[isotope] = 1;
176
  reaction.product = {};
177
  reaction.product[firstHalf] = 1;
178
  reaction.product[secondHalf] = reaction.product[secondHalf]+1 || 1;
179
  if (neutronExcess > 0) {
180
    reaction.product.n = neutronExcess;
181
  }
182
  if (energy > 0) {
183
    reaction.product.eV = energy;
184
  }
185
  return reaction;
186
}
187
188
jsonfile.writeFileSync('build/data/resources.json', resources, {
189
  spaces: 2
190
});
191
192
jsonfile.writeFileSync('build/data/elements.json', elements, {
193
  spaces: 2
194
});
195