reaclib_class.py
Go to the documentation of this file.
1 # Reaclib class to analyze and change reaclib files
2 # Author: Moritz Reichert
3 # Date : 08.05.17
4 
5 from .nucleus_class import nucleus
6 import pandas as pd
7 import numpy as np
8 import matplotlib.pyplot as plt
9 import os
10 import sys
11 import warnings
12 
13 
14 
15 class reaclib(object):
16 
17  def __init__(self,path,quiet=False):
18  """
19  Analyze a rate file for a given path.
20  Input:
21  path - path to reaclib file
22  quiet - Do you want to have status informations of the progress?
23  """
24  # Turn off panda warning when changing copied frames
25  pd.options.mode.chained_assignment = None
26  # Make warnings to exceptions to be able to catch them
27  warnings.filterwarnings('error')
28 
29  self.__path = path # Path to reaclib file (independent from format)
30  self.__format = 1 # 1 original v1, without chapter 9-11
31 
32  self.__quiet = quiet # Verbosity of the class
33 
34  # give some column names
35  self.__column_names = ['chapter','reactants','products','label','type','reverse','q value','error','reaction type'] + ['a'+str(i) for i in range(7)]
36  self.__pd_reaclib = None
37  # Have also a backup of the frame to be able to reset it
38  self.__reset_reaclib = None
39  # Default settings of reaclib file format
40  self.__cpn = 5 # characters per nucleus
41  self.__lpo = 5+6*5+8 # position of the label
42  self.__lc = 4 # label characters
43  self.__cpe = 13 # characters per entry in the set
44 
45  # Fission rates are not matching their chapter
47  self.__fission_flags = ['ms99','fiss','mp01','sfis']
48 
49 
50  self.__temperature_range = np.logspace(-2,1,num=20) # Range for the temperature grid for plots and tests
51 
53 
54  def read_reaclib(self):
55  """
56  read the whole reaclib file.
57  """
58  # Use a different functions for different chapters
59  read1 = lambda x : ([x[0]] , [x[1]])
60  read2 = lambda x : ([x[0]] , [x[1],x[2]])
61  read3 = lambda x : ([x[0]] , [x[1],x[2],x[3]]) if (len([a for a in x if a != '']) == 4 or (not self.__format == 1)) else ([x[0]] , [x[1],x[2],x[3],x[4]])
62  read4 = lambda x : ([x[0],x[1]] , [x[2]])
63  read5 = lambda x : ([x[0],x[1]] , [x[2],x[3]])
64  read6 = lambda x : ([x[0],x[1]] , [x[2],x[3],x[4]])
65  read7 = lambda x : ([x[0],x[1]] , [x[2],x[3],x[4],x[5]])
66  read8 = lambda x : ([x[0],x[1],x[2]] , [x[3]]) if (len([a for a in x if a != '']) == 4 or (not self.__format == 1)) else ([x[0],x[1],x[2]] , [x[3],x[4]])
67  read9 = lambda x : ([x[0],x[1],x[2]] , [x[3],x[4]])
68  read10 = lambda x : ([x[0],x[1],x[2],x[3]] , [x[4],x[5]])
69  read11 = lambda x : ([x[0]] , [x[1],x[2],x[3],x[4]])
70 
71  # create a parser which picks the 'readX' function depending on the chapter.
72  r_parser = {1 : read1, 2 : read2, 3 : read3, 4 : read4, 5 : read5,
73  6 : read6, 7 : read7, 8 : read8, 9 : read9, 10 : read10,
74  11 : read11}
75 
76  check = 0
77  reaclib_data = []
78  # Open the reaclib file
79  with open(self.__path,'r') as reaclib_file:
80  count = 0
81  # read file content
82  for line in reaclib_file.readlines():
83  count += 1
84  if not self.__quiet and (count % 1000 == 0):
85  print('Reading line ' +str(count) +" ",end='\r')
86 
87  # Check for chapter line
88  if line.strip().isdigit():
89  chapter = int(line.strip())
90  continue
91 
92  # Check for empty lines after the chapter and ignore them
93  if line.strip() == '':
94  continue
95 
96  check +=1
97  # Line with nuclei and Q-value and so on
98  if check == 1:
99  involved = [line[i:i+self.__cpn].strip() for i in range(1*self.__cpn, 7*self.__cpn, self.__cpn)]
100  reactants, products = r_parser[chapter](involved)
101  # Read all the rest
102  loc = self.__lpo
103  label = line[loc:loc+self.__lc] # reaction label
104  loc += self.__lc
105  r_type = line[loc:loc+1] # reaction type
106  loc += 1
107  r_reverse = (line[loc:loc+1] == 'v') # reverse rate flag
108  loc += 4
109  r_qval = float(line[loc:loc+12].replace('D','e').replace('d','e')) # q value
110  # Make first error tests
111  if (len([a for a in involved if a != '']) != (len(reactants) + len(products))) and (not (self.__ignore_fission_errors and (label.strip() in self.__fission_flags))):
112  error = 'Not matching its chapter!'
113  else:
114  error = ''
115  elif check == 2:
116  a_factors = [line[i:i+self.__cpe] for i in range(0, 4*self.__cpe, self.__cpe)]
117  elif check == 3:
118  # three factors in the second line
119  a_factors.extend([line[i:i+self.__cpe] for i in range(0, 3*self.__cpe, self.__cpe)])
120  try:
121  a_factors = [float(x.replace('D','e').replace('d','e')) for x in a_factors]
122  except:
123  error = 'Fitting value is not a float!'
124  # gather information about the rate
125  reaction_type = self.__classify_reaction(chapter,reactants,products,r_type,label,a_factors)
126 
127  # Winnet requires neutrons first and heavier nuclei after that
128  if label not in self.__fission_flags:
129  reactants = sorted(reactants)
130  products = sorted(products)
131  # Save the rate
132  rate_data = [chapter,reactants,products,label,r_type,r_reverse,r_qval,error,reaction_type]
133  rate_data.extend(a_factors)
134 
135  reaclib_data.append(rate_data)
136  check = 0
137 
138  # Make the last output
139  if not self.__quiet:
140  print('Reading line ' +str(count) +" ")
141 
142  # create data frame
143  self.__pd_reaclib = pd.DataFrame(reaclib_data)
144  # set column names
145  self.__pd_reaclib.columns=self.__column_names
146  # Create a copy of it to be able to reset the dataframe
147  self.__reset_reaclib = self.__pd_reaclib.copy()
148 
149 
150  def reset_reaclib(self):
151  """
152  Reset the reaclib to the loaded one.
153  """
154  self.__pd_reaclib = self.__reset_reaclib.copy()
155 
156 
157 
158  def __classify_reaction(self,chapter,reactants,products,weak,label,afactors):
159  """
160  Classify a reaction if it is gamma-n, n-gamma, ...
161  Input:
162  chapter - reaclib chapter of the reaction (int)
163  reactants - all reactants of the reaction (list of strings)
164  products - all products of the reaction (list of strings)
165  weak - 'w' if weak, else ''
166  label - reaction label to identify fission rates
167  afactors - check whether a rate is constant in dependence of the temperature
168  Output:
169  Returns a string, containing the type of reaction
170  """
171 
172  # is fission reaction?
173  if label in self.__fission_flags:
174  reaction_type = 'fission'
175  # is alpha decay?
176  elif (chapter == 2) and ('he4' in products) and (sum(afactors[1:])==0):
177  reaction_type = 'a-decay'
178  # is weak reaction?
179  elif weak.strip() =='w':
180  reaction_type = 'weak'
181  # is n-gamma?
182  elif (chapter == 4) and ('n' in reactants):
183  reaction_type = 'n-gamma'
184  # is gamma-n?
185  elif (chapter == 2) and ('n' in products):
186  reaction_type = 'gamma-n'
187  # is p-gamma?
188  elif (chapter == 4) and ('p' in reactants):
189  reaction_type = 'p-gamma'
190  # is gamma-p?
191  elif (chapter == 2) and ('p' in products):
192  reaction_type = 'gamma-p'
193  # is a-gamma?
194  elif (chapter == 4) and ('he4' in reactants):
195  reaction_type = 'a-gamma'
196  # is gamma-a?
197  elif (chapter == 2) and ('he4' in products) and (sum(afactors[1:])!=0):
198  reaction_type = 'gamma-a'
199  # is n-p?
200  elif (chapter == 5) and ('n' in reactants) and ('p' in products):
201  reaction_type = 'n-p'
202  # is p-n?
203  elif (chapter == 5) and ('p' in reactants) and ('n' in products):
204  reaction_type = 'p-n'
205  # is n-a?
206  elif (chapter == 5) and ('n' in reactants) and ('he4' in products):
207  reaction_type = 'n-a'
208  # is a-n?
209  elif (chapter == 5) and ('he4' in reactants) and ('n' in products):
210  reaction_type = 'a-n'
211  # is p-a?
212  elif (chapter == 5) and ('p' in reactants) and ('he4' in products):
213  reaction_type = 'p-a'
214  # is a-p?
215  elif (chapter == 5) and ('he4' in reactants) and ('p' in products):
216  reaction_type = 'a-p'
217  else:
218  reaction_type = 'other'
219  return reaction_type
220 
221 
222  def get_statistics(self):
223  """
224  Get the amount of different rate types (n-gamma, gamma-n, weak ,alpha-n,...)
225  Output:
226  Returns a pandas series, containing the counts of the different reaction types and the total number of reactions
227  """
228  # Count the occurance of the types and append the amount of reactions
229  return (pd.value_counts(self.__pd_reaclib['reaction type'].values).append(pd.Series({'Total reactions' :self.__pd_reaclib.count()[0]})))
230 
231 
232  def set_rate(self,dataframe):
233  """
234  Set the dataframe (containing all reactions) of the class with the given one
235  """
236  self.__pd_reaclib = dataframe.copy()
237 
238 
239  def __reaclib_fit_function(self,temperature,coefficients):
240  """
241  Returns the value of the rate for a given temperature
242  Input:
243  temperature - Temperature [GK]
244  coefficients - The 7 fit coefficients from the reaclib format
245  """
246  # Calculate the exponent
247  exponent = coefficients[0] + coefficients[6] * np.log(temperature)
248 
249  for i in range(5):
250  exponent += coefficients[i+1]*temperature**((2.*(i+1)-5.)/3.)
251  # Calculate the rate
252  try:
253  rate = np.exp(exponent)
254  except RuntimeWarning:
255  rate = np.inf
256  return rate
257 
258  def test_reaclib(self, test_weak=True, test_mass=True, test_overflow=True):
259  """
260  Test the reaclib for failures
261  """
262  # Amount of reactions
263  total_reactions = self.__pd_reaclib['chapter'].count()
264  # Loop through the entries
265  for index, row in self.__pd_reaclib.iterrows():
266 
267  if not self.__quiet and (index % 100 == 0):
268  percentage = str(int((index+1.)/total_reactions * 10000.) / 100.).ljust(4)
269  print('Running tests: '+percentage.ljust(4) + " done! ",end='\r')
270 
271  # Has errors anyways
272  if row['error'] != '':
273  continue
274 
275  # Ignore fission for test or not
276  if self.__ignore_fission_errors and (self.__pd_reaclib['label'].iloc[index] in self.__fission_flags):
277  continue
278 
279  # Test for consistency of weak rates
280  if test_weak:
281  if ((row['type'].strip() == 'w') and self.__test_weakconsistency(row)):
282  self.__pd_reaclib['error'].iloc[index] = 'Weak rate is not consistent!'
283  continue
284 
285  # Test for mass consistency
286  if test_mass:
287  if self.__test_massconsistency(row) and test_mass:
288  self.__pd_reaclib['error'].iloc[index] = 'Mass inconsistency of the rate!'
289  continue
290 
291  # Test for overflows of the rate
292  if test_overflow:
293  if self.__test_overflows(row):
294  self.__pd_reaclib['error'].iloc[index] = 'Rate exceeds value of ' + str(self.__test_rate_upper_limit)
295  continue
296 
297  if not self.__quiet:
298  print('Running tests: 100 done! ')
299 
300 
301  def get_contained_nuc(self):
302  """
303  Get all nuclei contained in reaclib file.
304  Output:
305  list of strings, containing the names of all nuclei contained in the reaclib
306  """
307  flatten = lambda l: [item for sublist in l for item in sublist] # From https://stackoverflow.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python
308  prods = self.__pd_reaclib["products"].tolist()
309  reacts = self.__pd_reaclib["reactants"].tolist()
310  prods.extend(reacts)
311  return list(set(flatten(prods)))
312 
313  def test_net_consistency(self,path):
314  """
315  Test if nuclei are included in the net file, but not in the reaclib. Be careful, reaclib contains nuclei where you are trapped in.
316  There are reactions producing that nuclei, but no reaction to destroy it. (e.g. "f16" in the 2017 version of reaclib)
317  Input:
318  path - path to net file of WinNet
319  Output:
320  list of strings, containing problematic nuclei. If all nuclei have a corresponding reaction, an empty list is returned
321  """
322  # Nuclei from network file
323  sunet_nucs = np.loadtxt(path,unpack=True,dtype=str)
324  # Nuclei from reaclib
325  reaction_nucs = self.get_contained_nuc()
326 
327  # Get problematic nuclei
328  problem_nuc = []
329  for nuc in sunet_nucs:
330  if nuc not in reaction_nucs:
331  problem_nuc.append(nuc)
332 
333  return problem_nuc
334 
335  def __test_massconsistency(self,row):
336  """
337  Test the reaction rate for overflows!
338  """
339  error = False
340  reactants = list(row['reactants'])
341  products = list(row['products'])
342 
343  # Transform to Z values
344  reactants_A = [nucleus(x).get_A() for x in reactants]
345  products_A = [nucleus(x).get_A() for x in products]
346 
347  if sum(reactants_A) != sum(products_A):
348  error = True
349  return error
350 
351 
352  def __test_overflows(self,row,temp_range=None,upper_lim=None):
353  """
354  Test the reaction rate for overflows!
355  """
356  error = False
357  coefficients = list(row['a0':'a6'])
358 
359  if upper_lim is None:
360  upper_lim = self.__test_rate_upper_limit
361  if temp_range is None:
362  temp_range = self.__temperature_range
363 
364  # loop trough temperatures
365  for t in temp_range:
366  if self.__reaclib_fit_function(t,coefficients) >= upper_lim:
367  error = True
368  break
369  return error
370 
371 
372  def __test_weakconsistency(self,row):
373  """
374  Test the reaction for consistency
375  """
376  error = False
377  reactants = list(row['reactants'])
378  products = list(row['products'])
379 
380  # Transform to Z values
381  reactants_Z = [nucleus(x).get_Z() for x in reactants]
382  products_Z = [nucleus(x).get_Z() for x in products]
383 
384  if (row['reaction type'].strip() == 'a-decay'):
385  return error
386 
387  if sum(reactants_Z) == sum(products_Z):
388  error = True
389  return error
390 
391 
392  def get_rate_at_temp(self,temperature,reactants,products):
393  """
394  Get a reaction rate at given temperature
395  Input:
396  temperature - Temperature [GK]
397  reactants - list with reactants
398  products - list with products
399  Output:
400  Reaction rate at a given temperature
401  """
402  # Get matching reaction, got damn this was complicated
403  matching_reactants = self.__pd_reaclib[[x == sorted(reactants) for x in self.__pd_reaclib.reactants.values]]
404  reactions = matching_reactants[[x == sorted(products) for x in matching_reactants.products.values]]
405  # print(sum((self.__pd_reaclib.reactants.values == ['n']).astype(int)))
406  # Get the reaction rate
407  rat = 0.
408  for index, row in reactions.iterrows():
409  rat += self.__reaclib_fit_function(temperature,list(row['a0':'a6']))
410  return rat
411 
412 
413 
414 
415  def plot_rate(self,reactants,products,figure=None,axlabel=True,temp_grid=None,**kwargs):
416  """
417  Plot a rate.
418  Input:
419  reactants - list of reactants (list of strings)
420  products - list of products (list of strings)
421  """
422  # Make a figure if no figure was given
423  if figure == None:
424  final_fig = plt.figure()
425  else:
426  final_fig = figure
427 
428  ax = final_fig.gca()
429 
430  rat_list = []
431 
432  if temp_grid is None:
433  temp_grid = self.__temperature_range
434 
435  for t in temp_grid:
436  rat_list.append(self.get_rate_at_temp(t,reactants,products))
437  ax.plot(temp_grid,rat_list,**kwargs)
438 
439  if axlabel:
440  ax.set_xlabel('Temperature [GK]')
441  ax.set_ylabel('Rate')
442  ax.set_yscale('log')
443  ax.set_xscale('log')
444 
445  return final_fig
446 
447 
448  def get_rate_error(self):
449  """
450  Get a list with all rates that produce errors
451  """
452  return self.__pd_reaclib[self.__pd_reaclib['error'] != '']
453 
454 
455  def get_rate_error_html(self,path):
456  """
457  Get an html file with all rates that contain an error
458  Input:
459  path - Path to the html file that will be created
460  """
461  f = open(path,'w')
462  out = self.get_rate_error().to_html()
463  f.write(out)
464  f.close()
465 
466  def __convert_series_to_string(self,series):
467  """
468  Convert a pandas series to a reaclib string.
469  Input:
470  series - pandas series of one reaction
471  """
472  # Get one list with both, reactants and products
473  clean_copy = series.copy()
474  merged_nuclei = list(clean_copy['reactants'])
475  merged_nuclei.extend(list(clean_copy['products']))
476  # Make it out of 5 spaces again
477  merged_nuclei = [x.rjust(5) for x in merged_nuclei]
478  # Because reverse was saved as bool
479  if series['reverse']:
480  reverse = 'v'
481  else:
482  reverse = ' '
483 
484  five_spaces = 5 * ' '
485  # First line:
486  # Reaclib format: 5 spaces + 6 times 5spaces for nuclei + 8 spaces + 4 characters for the label + 1 character for a flag + 1 for reverse +
487  # 3 spaces + 12 characters for the Q-value + 10 spaces
488  out = five_spaces + ''.join(merged_nuclei) +(6-len(merged_nuclei)) * five_spaces + 8*' '+series['label'].ljust(4) + series['type'].ljust(1) + reverse + \
489  3*' ' + '%12.5e' % (series['q value']) + 10*' '
490  out += '\n'
491  # Second line:
492  # a0 - a3 followed by 22 spaces
493  out += ''.join(['%13.6e' % x for x in series['a0':'a3']])+ 22*' '
494  out += '\n'
495  # Third line:
496  # a4 - a6 followed by 35 spaces
497  out +=''.join(['%13.6e' % x for x in series['a4':'a6']])+ 35*' '
498  out += '\n'
499  return out
500 
501  def __sort_prods_reacs(self,in_list):
502  """
503  Sort the products and reactants of a given dataframe.
504  A nucleus is greater if it has an higher proton number
505  """
506  helplist = [nucleus(x) for x in in_list]
507  helplist = [x.get_input_name() for x in sorted(helplist)]
508  return helplist
509 
510 
511 
512  def save_reaclib(self,filename,dataframe=None,sort=False):
513  """
514  Save the pandas dataframe to a reaclib file with the correct format.
515  Input:
516  filename - Filename of the output.
517  dataframe - Pandas dataframe that contains reaction rates. If none, the reaction rates of the class are used
518  """
519  # String to cache the output, before writing it to file
520  reaclib_out = ''
521 
522  if dataframe is None:
523  input_reaclib = self.__pd_reaclib.copy()
524  else:
525  input_reaclib = dataframe.copy()
526 
527  # Get amount of reactions for status
528  amount_reactions = input_reaclib.count()[0]
529  # Show more content of the string (in principle not necessary, but nice to have)
530  pd.set_option('display.max_colwidth', -1)
531 
532  # Sort the reactions to correct order again
533  if sort:
534  if not self.__quiet:
535  print("Sorting database. This may take a while!")
536  print("Sorting products.")
537 
538  input_reaclib['products'] = input_reaclib['products'].apply(self.__sort_prods_reacs)
539  if not self.__quiet:
540  print("Sorting reactants.")
541  input_reaclib['reactants'] = input_reaclib['reactants'].apply(self.__sort_prods_reacs)
542 
543  input_reaclib['Z'] = input_reaclib['reactants'].apply(lambda x: nucleus(x[0]).get_Z() )
544  input_reaclib['A'] = input_reaclib['reactants'].apply(lambda x: nucleus(x[0]).get_A() )
545  input_reaclib.sort_values(['Z','A'],inplace=True)
546 
547 
548 
549 
550  if not self.__quiet:
551  print('Saving reaclib to file.')
552  print('Creating correct format, this may take a while.')
553 
554  # Create reaclib format and save it temporary into dataframe
555  input_reaclib['tmp_out'] = input_reaclib.apply(self.__convert_series_to_string,axis=1)
556  if not self.__quiet:
557  print('Saving chapterwise.')
558 
559  # Determine the maximal chapter (necessary for different formats)
560  max_chapter = input_reaclib['chapter'].max()
561 
562  # Cycle through chapters
563  if not isinstance(max_chapter,int):
564  if not self.__quiet:
565  print('Warning: Chapter was not an integer, it was '+str(max_chapter)+' instead!')
566 
567 
568  for i in range(1,int(max_chapter)+1):
569  if not self.__quiet:
570  print('Saving chapter '+str(i)+' of '+str(max_chapter)+'. ',end='\r')
571 
572  # Set the chapter before the rates
573  reaclib_out += str(i) + '\n\n\n'
574  # Get the entries of the chapters
575  tmp = (input_reaclib[input_reaclib['chapter']==i])
576  reaclib_out +=(''.join(tmp['tmp_out'].values))
577 
578  if not self.__quiet:
579  print('Saving to file. ')
580 
581  # Save reaclib to file
582  with open(filename,'w') as f_out:
583  f_out.write(reaclib_out)
584 
585  # Remove the format again
586  del input_reaclib['tmp_out']
587 
588 
589  def get_critical_low_temperature_rates(self,min_temperature=1e-4,amount_points=20,max_rate=1.e100):
590  """
591  Get rates that are critical for low temperatures (< 1e-2 GK)
592  """
593  temp_points = np.logspace(np.log10(min_temperature),-2,num=amount_points)
594 
595  # Amount of reactions
596  total_reactions = self.__pd_reaclib['chapter'].count()
597  # Store the index of problematic reactions in list
598  problematic_index = []
599  # Order the index
600  self.__pd_reaclib = self.__pd_reaclib.reset_index()
601  # Loop through the entries
602  for index, row in self.__pd_reaclib.iterrows():
603 
604  if not self.__quiet and (index % 100 == 0):
605  percentage = str(int((index+1.)/total_reactions * 10000.) / 100.).ljust(4)
606  print('Get critical rates: '+percentage.ljust(4) + " done! ",end='\r')
607 
608  # Test for overflows of the rate
609  if self.__test_overflows(row,temp_range=temp_points,upper_lim=max_rate):
610  problematic_index.append(index)
611  continue
612 
613  if not self.__quiet:
614  print('Running tests: 100 done! ')
615  problematic_index = np.array(problematic_index)
616 
617  return self.__pd_reaclib.iloc[problematic_index]
618 
619 
620  def drop_errors(self):
621  """
622  Drop errors from dataframe. Need to run test_reaclib() first
623  """
624  # Count the errors
625  if not self.__quiet:
626  amount_errors = self.__pd_reaclib[self.__pd_reaclib['error'] != ''].count()[0]
627  print('Dropped '+str(amount_errors)+' rates.')
628  # Only keep the rates without errors
629  self.__pd_reaclib = self.__pd_reaclib[self.__pd_reaclib['error'] == '']
630 
631 
632  def get_dataframe(self,reaction_type=None,chapter=None):
633  """
634  Returns the pandas dataframe, containing all reactions (after applying a filter) of the reaclib
635  Input:
636  reaction_type - only these reaction types (n-gamma,..), none for dont filter the type (list of strings or string)
637  chapter - only these chapters , none for dont filter the chapter (list of integers or integer)
638  """
639  return self.__filter_rates(self.__pd_reaclib.copy(),reaction_type,chapter)
640 
641 
642  def __analyze_update(self,dataframe):
643  """
644  Analyze a dataframe that contains the column "version".
645  Output:
646  Returns the amount of new inserted rates
647  """
648  # Count the values of new and old rates in the frame
649  count = pd.value_counts(dataframe['version'].values)
650  if 'new' in count.index:
651  new_count = count['new']
652  else:
653  new_count = 0
654 
655  if 'old' in count.index:
656  old_count = count['old']
657  else:
658  old_count = 0
659  return new_count
660 
662  """
663  Get all types of reactions, necessary to write in "reaction_type" when filtering rates
664  Output:
665  List of strings, containing the possible reaction types
666  """
667  types = self.get_statistics()
668  return (list(types.index)[:-1])
669 
670 
671  def is_included(self,series):
672  """
673  Is the rate included in the dataframe? Compared are only products and reactants.
674  Returns true if yes, false if not
675  """
676  # Filter for the chapter
677  chapter_reactions = self.__pd_reaclib[self.__pd_reaclib['chapter']==series['chapter']]
678  reactants = series['reactants']
679  products = series['products']
680  # Filter for reactants
681  for r in reactants:
682  rates = chapter_reactions[[r in x for x in chapter_reactions['reactants']]]
683  # Filter for products
684  if not rates.empty:
685  for p in products:
686  rates = rates[[p in x for x in rates['products']]]
687 
688  if rates.empty:
689  return False
690  else:
691  return True
692 
693 
694 
695 
696  def __filter_rates(self,dataframe,reaction_type,chapter):
697  """
698  Filter the reaction rates by type and chapter.
699  Input:
700  dataframe - Pandas dataframe that contains all reactions
701  reaction_type - only these reaction types (n-gamma,..), none for dont filter the type (list of strings or string)
702  chapter - only these chapters , none for dont filter the chapter (list of integers or integer)
703  Output:
704  Filtered dataframe, that contains only the reactions with given type and chapter
705  """
706  # First convert everything to a list if the input is no list
707  if isinstance(reaction_type,str):
708  reaction_type=[reaction_type]
709  if isinstance(chapter,int):
710  chapter=[chapter]
711  # Filter for the correct reactions
712  # Distinguish three cases
713  # First case: Filter for both
714  if reaction_type != None and chapter != None:
715  dataframe = dataframe[dataframe['chapter'].isin(chapter) & dataframe['reaction type'].isin(reaction_type)]
716  # Second case: Filter for the chapter only
717  elif reaction_type == None and chapter != None:
718  dataframe = dataframe[dataframe['chapter'].isin(chapter)]
719  # Third case: Filter for the reaction_type only
720  elif reaction_type != None and chapter == None:
721  dataframe = dataframe[dataframe['reaction type'].isin(reaction_type)]
722  # (Fourth case: Don't do anything)
723  return dataframe
724 
725 
726  def update(self,reaclib_path=None,dataframe=None,reaction_type=None,chapter=None,ignore_label=False,ignore_reverse=False,ignore_type=False,replace_specific_label=None):
727  """
728  Update the reaclib with rates from other reaclib. Dont include new rates in the file. Both reaclibs have to be in the "old version" (without chapter 9-10) or in the
729  "new version", with these chapters.
730  Input:
731  reaclib_path - path of the reaclib that contains new rates
732  dataframe - pandas dataframe that contains reactions. If this is given, you don't have to give a reaclib_path.
733  reaction_type - Reaction type that will be updated (string or list of strings), for possible values see ".get_statistics()"
734  chapter - Chapter that will be updated (list of integer or integer)
735  ignore_label - ignore the label for updating and only look at the reactions itself
736  ignore_reverse - ignore if the reaction is reverse
737  ignore_type - ignore the type
738  replace_specific_label- Should one specific label be replaced. E.g. Moeller 93 with Moeller 03 rates. For a list of the Jina labels see https://groups.nscl.msu.edu/jina/reaclib/db/labels.php
739  if yes, the input should be a list with [old label, new label]
740  """
741  # Function to categorize 3 cases
742  def categorize(x):
743  # First case: There are new and old rates for the label (-> take the new ones)
744  if ('new' in x) and ('old' in x):
745  return 1
746  # Second case: There are only new rates (-> don't take these rates)
747  if ('new' in x) and not ('old' in x):
748  return 2
749  # Third case: There are only old rates (-> take all old rates)
750  if not ('new' in x) and ('old' in x):
751  return 3
752 
753  if not self.__quiet:
754  print('Updating reaclib.')
755 
756  # Get the input values
757  # First case: reaclib path is given, so use this as new dataframe
758  if reaclib_path is not None and dataframe is None:
759  # Reading other reaclib to dataframe and bring it to correct shape
760  new_rates = reaclib(reaclib_path,quiet=True)
761  new_rates.read_reaclib()
762  new_rates_df = new_rates.get_dataframe()
763  # Second case: dataframe is given, so use these reaction rates
764  elif reaclib_path is None and dataframe is not None:
765  new_rates_df = dataframe
766  # Third case: None of them are given, raise an error
767  elif reaclib_path is None and dataframe is None:
768  raise Exception('Pass either a path of a reaclib or a pandas dataframe.')
769  # Fourth case: Both of them are given, raise an error
770  elif reaclib_path is not None and dataframe is not None:
771  raise Exception('Too much input. Pass either reaclib_path or pandas dataframe.')
772 
773  # Filter for the correct reactions that will be merged
774  new_rates_df= self.__filter_rates(new_rates_df,reaction_type,chapter)
775 
776  # Replace a specific label?
777  if not replace_specific_label is None:
778  oldlabel = replace_specific_label[0]
779  newlabel = replace_specific_label[1]
780  # Do not ignore labels for this
781  ignore_label = False
782  # self.__pd_reaclib.loc[self.__pd_reaclib['label'] == oldlabel,'label'] = 'totaldummylabel' # Something stupid that will never be in the reaclib
783  # new_rates_df.loc[new_rates_df['label'] == newlabel,'label'] = 'totaldummylabel' # Same for the other dataframe
784  # print(new_rates_df[new_rates_df['label'] == 'totaldummylabel'])
785 
786  # Pandas is really bad in dealing with lists as entry, so create a unique string for categorizing. (he4+c12 -> "chapterhe4c12label")
787  reactant_string = new_rates_df['reactants'].apply(lambda x: ''.join(x))
788  product_string = new_rates_df['products'].apply(lambda x: ''.join(x))
789  # Create a unique string for the reaction
790  new_rates_df['sort_string'] = new_rates_df['chapter'].astype(str) + reactant_string + product_string
791 
792  if not ignore_label:
793  if not replace_specific_label is None:
794  new_rates_df.loc[new_rates_df['label']==newlabel,'sort_string'] +='totaldummylabel'
795  new_rates_df.loc[new_rates_df['label']!=newlabel,'sort_string'] +='dontreplacethis'
796  else:
797  new_rates_df['sort_string'] += new_rates_df['label']
798  if not ignore_reverse:
799  new_rates_df['sort_string'] += new_rates_df['reverse'].astype(str)
800  if not ignore_type:
801  new_rates_df['sort_string'] += new_rates_df['type'].astype(str)
802  # "Flag" the origin of the rates
803  new_rates_df['version'] = 'new'
804 
805 
806  # Pandas is really bad in dealing with lists as entry, so create a unique string for categorizing. (he4+c12 -> "chapterhe4c12label")
807  reactant_string = self.__pd_reaclib['reactants'].apply(lambda x: ''.join(x))
808  product_string = self.__pd_reaclib['products'].apply(lambda x: ''.join(x))
809 
810 
811  # Create also a unique string for the the old reactions
812  self.__pd_reaclib['sort_string'] = self.__pd_reaclib['chapter'].astype(str) + reactant_string + product_string
813  if not ignore_label:
814  if not replace_specific_label is None:
815  self.__pd_reaclib.loc[self.__pd_reaclib['label']==oldlabel,'sort_string']+= 'totaldummylabel'
816  else:
817  self.__pd_reaclib['sort_string'] += self.__pd_reaclib['label']
818  if not ignore_reverse:
819  self.__pd_reaclib['sort_string'] += self.__pd_reaclib['reverse'].astype(str)
820  if not ignore_type:
821  self.__pd_reaclib['sort_string'] += self.__pd_reaclib['type'].astype(str)
822 
823  self.__pd_reaclib['version'] = 'old'
824  # Merge the dataframes
825  frames = [self.__pd_reaclib,new_rates_df]
826  merged_frame = pd.concat(frames)
827 
828  # Group them by the created string, create a new string like "oldoldnewold" and categorize that string
829  category = merged_frame.groupby(merged_frame['sort_string'])['version'].agg(lambda col: ''.join(col)).apply(categorize)
830  # Join this result to the original frame
831  merged_frame = merged_frame.join(category, on='sort_string',rsuffix='_cat')
832  # Filter for the correct reactions
833  self.__pd_reaclib = merged_frame[((merged_frame['version'] == 'old') & (merged_frame['version_cat'] == 3)) | ((merged_frame['version'] == 'new') & (merged_frame['version_cat'] == 1))]
834 
835  if not self.__quiet:
836  new_rates_count = self.__analyze_update(self.__pd_reaclib)
837  print('Updated '+str(new_rates_count)+' rates.')
838  print('Updating done!')
839 
840 
841  if not replace_specific_label is None:
842  self.__pd_reaclib.loc[self.__pd_reaclib['label'] == 'totaldummylabel','label'] = newlabel
843 
844  # Make sure to remove everything again. Remove the version and sort_string again
845  del self.__pd_reaclib['version']
846  del self.__pd_reaclib['version_cat']
847  del self.__pd_reaclib['sort_string']
848 
849 
850 
851  def add_new_rates(self,reaclib_path=None,dataframe=None,reaction_type=None,chapter=None,ignore_label=True,ignore_reverse=True,ignore_type=False):
852  """
853  Add new rates to existing ones
854  Input:
855  reaclib_path - path of the reaclib that contains new rates
856  dataframe - pandas dataframe that contains reactions. If this is given, you don't have to give a reaclib_path.
857  reaction_type - Reaction type that will be updated (string or list of strings), for possible values see ".get_statistics()"
858  chapter - Chapter that will be updated (list of integer or integer)
859  ignore_label - ignore the label for updating and only look at the reactions itself
860  ignore_reverse - ignore if the reaction is reverse
861  ignore_type - ignore the type
862  replace_specific_label- Should one specific label be replaced. E.g. Moeller 93 with Moeller 03 rates. For a list of the Jina labels see https://groups.nscl.msu.edu/jina/reaclib/db/labels.php
863  if yes, the input should be a list with [old label, new label]
864  """
865  # Function to categorize 3 cases
866  def categorize(x):
867  # First case: There are new and old rates for the label (-> take the new ones)
868  if ('new' in x) and ('old' in x):
869  return 1
870  # Second case: There are only new rates (-> don't take these rates)
871  if ('new' in x) and not ('old' in x):
872  return 2
873  # Third case: There are only old rates (-> take all old rates)
874  if not ('new' in x) and ('old' in x):
875  return 3
876 
877  if not self.__quiet:
878  print('Updating reaclib.')
879 
880  # Get the input values
881  # First case: reaclib path is given, so use this as new dataframe
882  if reaclib_path is not None and dataframe is None:
883  # Reading other reaclib to dataframe and bring it to correct shape
884  new_rates = reaclib(reaclib_path,quiet=True)
885  new_rates.read_reaclib()
886  new_rates_df = new_rates.get_dataframe()
887  # Second case: dataframe is given, so use these reaction rates
888  elif reaclib_path is None and dataframe is not None:
889  new_rates_df = dataframe
890  # Third case: None of them are given, raise an error
891  elif reaclib_path is None and dataframe is None:
892  raise Exception('Pass either a path of a reaclib or a pandas dataframe.')
893  # Fourth case: Both of them are given, raise an error
894  elif reaclib_path is not None and dataframe is not None:
895  raise Exception('Too much input. Pass either reaclib_path or pandas dataframe.')
896 
897  # Filter for the correct reactions that will be merged
898  new_rates_df= self.__filter_rates(new_rates_df,reaction_type,chapter)
899 
900  # Pandas is really bad in dealing with lists as entry, so create a unique string for categorizing. (he4+c12 -> "chapterhe4c12label")
901  reactant_string = new_rates_df['reactants'].apply(lambda x: ''.join(x))
902  product_string = new_rates_df['products'].apply(lambda x: ''.join(x))
903  # Create a unique string for the reaction
904  new_rates_df['sort_string'] = new_rates_df['chapter'].astype(str) + reactant_string + product_string
905 
906  if not ignore_label:
907  new_rates_df['sort_string'] += new_rates_df['label']
908  if not ignore_reverse:
909  new_rates_df['sort_string'] += new_rates_df['reverse'].astype(str)
910  if not ignore_type:
911  new_rates_df['sort_string'] += new_rates_df['type'].astype(str)
912  # "Flag" the origin of the rates
913  new_rates_df['version'] = 'new'
914 
915  # Pandas is really bad in dealing with lists as entry, so create a unique string for categorizing. (he4+c12 -> "chapterhe4c12label")
916  reactant_string = self.__pd_reaclib['reactants'].apply(lambda x: ''.join(x))
917  product_string = self.__pd_reaclib['products'].apply(lambda x: ''.join(x))
918 
919  # Create also a unique string for the the old reactions
920  self.__pd_reaclib['sort_string'] = self.__pd_reaclib['chapter'].astype(str) + reactant_string + product_string
921  if not ignore_label:
922  self.__pd_reaclib['sort_string'] += self.__pd_reaclib['label']
923  if not ignore_reverse:
924  self.__pd_reaclib['sort_string'] += self.__pd_reaclib['reverse'].astype(str)
925  if not ignore_type:
926  self.__pd_reaclib['sort_string'] += self.__pd_reaclib['type'].astype(str)
927 
928  self.__pd_reaclib['version'] = 'old'
929  # Merge the dataframes
930  frames = [self.__pd_reaclib,new_rates_df]
931  merged_frame = pd.concat(frames)
932 
933  # Group them by the created string, create a new string like "oldoldnewold" and categorize that string
934  category = merged_frame.groupby(merged_frame['sort_string'])['version'].agg(lambda col: ''.join(col)).apply(categorize)
935  # Join this result to the original frame
936  merged_frame = merged_frame.join(category, on='sort_string',rsuffix='_cat')
937  # Filter for the correct reactions
938  self.__pd_reaclib = merged_frame[((merged_frame['version'] == 'old') & (merged_frame['version_cat'] == 3)) | ((merged_frame['version'] == 'new') & (merged_frame['version_cat'] == 2))]
939 
940  if not self.__quiet:
941  new_rates_count = self.__analyze_update(self.__pd_reaclib)
942  print('Added '+str(new_rates_count)+' rates.')
943  print('Adding done!')
944 
945 
946  # Make sure to remove everything again. Remove the version and sort_string again
947  del self.__pd_reaclib['version']
948  del self.__pd_reaclib['version_cat']
949  del self.__pd_reaclib['sort_string']
950 
951 
952 
953 
954 
955  def add_rate(self,dataframe):
956  """
957  Add a given dataframe to the reaclib
958  """
959  self.__pd_reaclib = pd.concat([self.__pd_reaclib, dataframe], axis=0)
960 
961 
962  def filter_with_sunet(self,nucleus_names):
963  """
964  Filter the rates with contained nuclei.
965  Input:
966  Contained nuclei of the network
967  """
968  def contained(a_list,ref_list=None):
969  """
970  Check if all elements of a list(a_list) are contained in another list(ref_list)
971  """
972  if ref_list is None:
973  ref_list = nucleus_names
974 
975  for element in a_list:
976  if element not in ref_list:
977  return False
978  return True
979 
980 
981  # Get the dataframe locally
982  pd_dataframe = self.__pd_reaclib
983 
984  # Check that the rates are contained in sunet
985  pd_dataframe['filter_reactants'] = pd_dataframe['reactants'].apply(contained)
986  pd_dataframe['filter_products'] = pd_dataframe['products'].apply(contained)
987  # Check if the reaction should be contained for our given sunet
988  pd_dataframe['keep'] = list(map(lambda f_reac, f_prods: f_reac and f_prods, pd_dataframe['filter_reactants'], pd_dataframe['filter_products'] ))
989  pd_dataframe = pd_dataframe[pd_dataframe['keep'] == True]
990 
991  # Delete the additional columns again
992  del pd_dataframe['filter_reactants']
993  del pd_dataframe['filter_products']
994  del pd_dataframe['keep']
995 
996  # Set the rates
997  self.__pd_reaclib = pd_dataframe
998 
999 
1000 
1001 
1002 
1004  """
1005  Get a statistic which labels are involved in the database. An overview of all labels an their meaning can be found
1006  at https://groups.nscl.msu.edu/jina/reaclib/db/labels.php
1007  """
1008  # Count the occurance of the types and append the amount of reactions
1009  return (pd.value_counts(self.__pd_reaclib['label'].values).append(pd.Series({'Total reactions' :self.__pd_reaclib.count()[0]})))
1010 
1011 
1012 if __name__ == '__main__':
1013 
1014  print('Test reaclib class')
1015  r = reaclib('../testfiles/reaclib_testfiles/reaclib.Christian.121204_beta_sf',quiet=False)
1016  r.read_reaclib()
1017  print(r.get_dataframe())
1018  print(r.get_statistics())
1019  # print(r.get_dataframe())
1020  # r.save_reaclib('unchanged2.dat',r.get_critical_low_temperature_rates())
1021  # r.get_all_reactiontypes()
1022  # print(r.get_critical_low_temperature_rates())
1023  # r.update('../testfiles/reaclib_testfiles/20161205ReaclibV2.0no910',reaction_type=None,ignore_label=False,ignore_reverse=False,ignore_type=False)
1024  # r.save_reaclib('all_ignore_none.dat')
1025  # r.test_reaclib()
1026  # r.drop_errors()
1027  # r.get_rate_error_html('reaclib_errors.html')
bin.class_files.reaclib_class.reaclib.is_included
def is_included(self, series)
Definition: reaclib_class.py:671
bin.class_files.reaclib_class.reaclib.__column_names
__column_names
Definition: reaclib_class.py:35
bin.class_files.reaclib_class.reaclib.add_rate
def add_rate(self, dataframe)
Definition: reaclib_class.py:955
bin.class_files.reaclib_class.reaclib.set_rate
def set_rate(self, dataframe)
Definition: reaclib_class.py:232
bin.class_files.reaclib_class.reaclib
Definition: reaclib_class.py:15
bin.class_files.reaclib_class.reaclib.__test_overflows
def __test_overflows(self, row, temp_range=None, upper_lim=None)
Definition: reaclib_class.py:352
bin.class_files.reaclib_class.reaclib.reset_reaclib
def reset_reaclib(self)
Definition: reaclib_class.py:150
bin.class_files.reaclib_class.reaclib.__reaclib_fit_function
def __reaclib_fit_function(self, temperature, coefficients)
Definition: reaclib_class.py:239
bin.class_files.reaclib_class.reaclib.__lpo
__lpo
Definition: reaclib_class.py:41
bin.class_files.reaclib_class.reaclib.test_net_consistency
def test_net_consistency(self, path)
Definition: reaclib_class.py:313
bin.class_files.reaclib_class.reaclib.read_reaclib
def read_reaclib(self)
Definition: reaclib_class.py:54
bin.class_files.reaclib_class.reaclib.get_statistics
def get_statistics(self)
Definition: reaclib_class.py:222
bin.class_files.reaclib_class.reaclib.save_reaclib
def save_reaclib(self, filename, dataframe=None, sort=False)
Definition: reaclib_class.py:512
bin.class_files.reaclib_class.reaclib.__temperature_range
__temperature_range
Definition: reaclib_class.py:50
bin.class_files.reaclib_class.reaclib.__ignore_fission_errors
__ignore_fission_errors
Definition: reaclib_class.py:46
bin.class_files.reaclib_class.reaclib.__reset_reaclib
__reset_reaclib
Definition: reaclib_class.py:38
bin.class_files.reaclib_class.reaclib.get_all_reactiontypes
def get_all_reactiontypes(self)
Definition: reaclib_class.py:661
bin.class_files.reaclib_class.reaclib.get_dataframe
def get_dataframe(self, reaction_type=None, chapter=None)
Definition: reaclib_class.py:632
bin.class_files.reaclib_class.reaclib.test_reaclib
def test_reaclib(self, test_weak=True, test_mass=True, test_overflow=True)
Definition: reaclib_class.py:258
bin.class_files.reaclib_class.reaclib.get_rate_error
def get_rate_error(self)
Definition: reaclib_class.py:448
bin.class_files.reaclib_class.reaclib.__path
__path
Definition: reaclib_class.py:29
bin.class_files.reaclib_class.reaclib.__format
__format
Definition: reaclib_class.py:30
bin.class_files.reaclib_class.reaclib.__analyze_update
def __analyze_update(self, dataframe)
Definition: reaclib_class.py:642
bin.class_files.reaclib_class.reaclib.__test_massconsistency
def __test_massconsistency(self, row)
Definition: reaclib_class.py:335
bin.class_files.reaclib_class.reaclib.__test_weakconsistency
def __test_weakconsistency(self, row)
Definition: reaclib_class.py:372
bin.class_files.reaclib_class.reaclib.__quiet
__quiet
Definition: reaclib_class.py:32
bin.class_files.reaclib_class.reaclib.get_rate_at_temp
def get_rate_at_temp(self, temperature, reactants, products)
Definition: reaclib_class.py:392
bin.class_files.reaclib_class.reaclib.get_label_statistic
def get_label_statistic(self)
Definition: reaclib_class.py:1003
bin.class_files.reaclib_class.reaclib.update
def update(self, reaclib_path=None, dataframe=None, reaction_type=None, chapter=None, ignore_label=False, ignore_reverse=False, ignore_type=False, replace_specific_label=None)
Definition: reaclib_class.py:726
bin.class_files.reaclib_class.reaclib.__cpn
__cpn
Definition: reaclib_class.py:40
bin.class_files.reaclib_class.reaclib.__init__
def __init__(self, path, quiet=False)
Definition: reaclib_class.py:17
bin.class_files.reaclib_class.reaclib.get_contained_nuc
def get_contained_nuc(self)
Definition: reaclib_class.py:301
bin.class_files.reaclib_class.reaclib.__test_rate_upper_limit
__test_rate_upper_limit
Definition: reaclib_class.py:52
bin.class_files.reaclib_class.reaclib.get_rate_error_html
def get_rate_error_html(self, path)
Definition: reaclib_class.py:455
bin.class_files.reaclib_class.reaclib.filter_with_sunet
def filter_with_sunet(self, nucleus_names)
Definition: reaclib_class.py:962
bin.class_files.nucleus_class.nucleus
Definition: nucleus_class.py:9
bin.class_files.reaclib_class.reaclib.__convert_series_to_string
def __convert_series_to_string(self, series)
Definition: reaclib_class.py:466
bin.class_files.reaclib_class.reaclib.drop_errors
def drop_errors(self)
Definition: reaclib_class.py:620
bin.class_files.reaclib_class.reaclib.__sort_prods_reacs
def __sort_prods_reacs(self, in_list)
Definition: reaclib_class.py:501
bin.class_files.reaclib_class.reaclib.__lc
__lc
Definition: reaclib_class.py:42
bin.class_files.reaclib_class.reaclib.__filter_rates
def __filter_rates(self, dataframe, reaction_type, chapter)
Definition: reaclib_class.py:696
bin.class_files.reaclib_class.reaclib.get_critical_low_temperature_rates
def get_critical_low_temperature_rates(self, min_temperature=1e-4, amount_points=20, max_rate=1.e100)
Definition: reaclib_class.py:589
bin.class_files.reaclib_class.reaclib.plot_rate
def plot_rate(self, reactants, products, figure=None, axlabel=True, temp_grid=None, **kwargs)
Definition: reaclib_class.py:415
bin.class_files.reaclib_class.reaclib.__fission_flags
__fission_flags
Definition: reaclib_class.py:47
bin.class_files.reaclib_class.reaclib.__cpe
__cpe
Definition: reaclib_class.py:43
bin.class_files.reaclib_class.reaclib.__pd_reaclib
__pd_reaclib
Definition: reaclib_class.py:36
bin.class_files.reaclib_class.reaclib.__classify_reaction
def __classify_reaction(self, chapter, reactants, products, weak, label, afactors)
Definition: reaclib_class.py:158
bin.class_files.reaclib_class.reaclib.add_new_rates
def add_new_rates(self, reaclib_path=None, dataframe=None, reaction_type=None, chapter=None, ignore_label=True, ignore_reverse=True, ignore_type=False)
Definition: reaclib_class.py:851