nucleus_multiple_class.py
Go to the documentation of this file.
1 from .nucleus_class import nucleus
2 import pandas as pd
3 import numpy as np
4 import os
5 from matplotlib import cm
6 from matplotlib.colors import ListedColormap, LinearSegmentedColormap
7 from matplotlib.collections import LineCollection
8 # from decay_net.decay_network import decay
9 
10 class nucleus_multiple(object):
11  """
12  Class to deal with lists of nucleus objects
13  """
14  def __init__(self,names=None,A=None,Z=None,N=None,Y=None,X=None):
15  """
16  Initialize the class
17  Input:
18  nuclist - list of nucleus names
19  """
20  self.names = names
21  self.A = A
22  self.Z = Z
23  self.N = N
24  # Get sophisticated names, A, Z, N
25  if not (names is None):
26  self.__init_with_list()
27  elif not(A is None) or not(Z is None) or not (N is None):
28  self.__init_with_data()
29 
30  if not(Y is None):
31  self.Y = np.array(Y)
32  elif not(X is None):
33  self.Y = np.array(X)/self.A
34  else:
35  self.Y = np.zeros(len(self.A))
36 
37  self.A = self.A.astype(int)
38  self.Z = self.Z.astype(int)
39  self.N = self.N.astype(int)
40 
41 
42  def __repr__(self):
43  return self.df.__str__()
44 
45  def __init_with_list(self):
46  """
47  Initialize the class using the list of nucleinames
48  """
49  self.A = np.array([nucleus(n).get_A() for n in self.names])
50  self.Z = np.array([nucleus(n).get_Z() for n in self.names])
51  self.N = self.A-self.Z
52  self.names = np.array(self.names)
53 
54  def __init_with_data(self):
55  """
56  Initialize the class using A,Z,N
57  """
58  # Sanity checks
59  if np.sum([not(self.A is None), not(self.Z is None), not (self.N is None)]) <2:
60  raise Exception("You need to provide at least two of A, Z, N!")
61 
62  if not (self.A is None):
63  if not (self.Z is None):
64  self.A = np.array(self.A).astype(int)
65  self.Z = np.array(self.Z).astype(int)
66  # Sanity check
67  if len(self.A)!=len(self.Z):
68  raise Exception("A and Z had different lengths (A: "+str(len(self.A))+", Z:"+str(len(self.Z))+")!")
69  self.N = self.A-self.Z
70  elif not (self.N is None):
71  self.A = np.array(self.A).astype(int)
72  self.N = np.array(self.N).astype(int)
73  # Sanity check
74  if len(self.A)!=len(self.N):
75  raise Exception("A and N had different lengths (A: "+str(len(self.A))+", N:"+str(len(self.N))+")!")
76  self.Z = self.A-self.N
77  else:
78  raise Exception("Something strange happened!")
79  elif not (self.Z is None):
80  self.N = np.array(self.N).astype(int)
81  self.Z = np.array(self.Z).astype(int)
82  if len(self.Z)!=len(self.N):
83  raise Exception("N and Z had different lengths (N: "+str(len(self.N))+", Z:"+str(len(self.Z))+")!")
84  self.A = self.Z+self.N
85  else:
86  raise Exception("Something strange happened!")
87 
88  # Sanity check
89  if np.min(self.N)<0:
90  raise Exception("N was smaller zero!")
91  elif np.min(self.Z)<0 :
92  raise Exception("Z was smaller zero!")
93  elif np.min(self.A)<0:
94  raise Exception("A was smaller zero!")
95 
96  # Create the names
97  self.names = np.array([nucleus(N=self.N[ind],Z=self.Z[ind]).get_name() for ind in range(len(self.N))])
98 
99 
100 
101  def __sum_over(self,A,Y):
102  max_A = max(A)
103  # second_dimension = len(Y[0,:])
104  Y_new = np.zeros([max_A+1,])
105  for i in range(len(A)):
106  Y_new[int(A[i])] += Y[i]
107  return np.array(range(max_A+1)),Y_new
108 
109 
110 
111  @property
112  def X(self):
113  """
114  Mass fraction
115  """
116  return self.Y*self.A
117 
118  @X.setter
119  def X(self,value):
120  """
121  Mass fraction
122  """
123  self.Y=value/self.A
124 
125 
126  @property
127  def A_X(self):
128  """
129  Get sum over equal A's
130  """
131  atmp,xtmp = self.__sum_over(self.A,self.X)
132  return np.array(atmp),np.array(xtmp)
133 
134  @property
135  def Z_Y(self):
136  """
137  Get sum over equal Z's
138  """
139  ztmp,ytmp = self.__sum_over(self.Z,self.Y)
140  return ztmp,ytmp
141 
142  @property
143  def Z_X(self):
144  """
145  Get sum over equal Z's
146  """
147  ztmp,ytmp = self.__sum_over(self.Z,self.X)
148  return ztmp,ytmp
149 
150 
151  @property
152  def Ye(self):
153  """
154  Abundance of protons
155  """
156  return np.sum(self.Y*self.Z)
157 
158  @property
159  def Yprot(self):
160  """
161  Abundance of protons
162  """
163  mask = (self.A==1) & (self.Z==1)
164  if sum(mask)!=1:
165  raise Exception("Problem when getting hydrogen abundances!")
166  else:
167  return self.Y[mask][0]
168  @property
169  def Yneut(self):
170  """
171  Abundance of protons
172  """
173  mask = (self.A==1) & (self.N==1)
174  if sum(mask)!=1:
175  raise Exception("Problem when getting neutron abundances!")
176  else:
177  return self.Y[mask][0]
178  @property
179  def Yhe4(self):
180  """
181  Abundance of protons
182  """
183  mask = (self.A==4) & (self.Z==2)
184  if sum(mask)!=1:
185  raise Exception("Problem when getting alpha abundances!")
186  else:
187  return self.Y[mask][0]
188 
189 
190  @property
191  def abar(self):
192  """
193  Mean mass number
194  """
195  return np.sum(self.Y*self.A)/np.sum(self.Y)
196 
197  @property
198  def zbar(self):
199  """
200  Mean mass number
201  """
202  return np.sum(self.Y*self.Z)/np.sum(self.Y)
203 
204  @property
205  def df(self):
206  """
207  Get a dataframe out
208  """
209  df = pd.DataFrame()
210  df["nucleus"] = self.names
211  df["A"] = self.A
212  df["Z"] = self.Z
213  df["N"] = self.N
214  df["Y"] = self.Y
215  df["X"] = self.Y*self.A
216  return df
217 
218  @property
219  def elnames(self):
220  """
221  Get a dataframe out
222  """
223  elnames = []
224 
225  for n in self.names:
226  # Take the name without the number at the end :
227  elnames.append(''.join([i for i in n if not i.isdigit()]))
228 
229  return np.array(elnames)
230 
231 
232 
233  def __merge_nuclei(self,A,Z):
234  """
235  Merge another list of nuclei into the own one.
236  """
237  A_merged = np.append(self.A,A)
238  Z_merged = np.append(self.Z,Z)
239  c_list = np.array([A_merged,Z_merged])
240  # print(A_merged)
241  # print(Z_merged)
242  unique=np.unique(c_list,axis=1)
243  return unique[0,:],unique[1,:]
244 
245 
246  def __mul__(self,other):
247  """
248  Multiply either two instances of nuclei lists or a float with this instance.
249  """
250  if isinstance(other,float) or isinstance(other,int):
251  self.Y=self.Y*other
252  elif isinstance(other,nucleus_multiple):
253  merge_A,merge_Z = self.__merge_nuclei(other.A,other.Z)
254  merge_N = merge_A-merge_Z
255  Y_list = np.zeros(len(merge_N))
256  for ind,atmp in enumerate(merge_A):
257  mask_self = (self.A ==merge_A[ind]) & (self.Z ==merge_Z[ind])
258  mask_other = (other.A==merge_A[ind]) & (other.Z==merge_Z[ind])
259  if (np.any(mask_self)) and (np.any(mask_other)):
260  Y_list[ind] = self.Y[mask_self][0]*other.Y[mask_other][0]
261  self.A = merge_A
262  self.Z = merge_Z
263  self.N = merge_N
264  self.Y = Y_list
265  self.__init_with_data()
266  return self
267 
268  def __truediv__(self,other):
269  """
270  Multiply either two instances of nuclei lists or a float with this instance.
271  """
272  if isinstance(other,float) or isinstance(other,int):
273  self.Y=self.Y/other
274  elif isinstance(other,nucleus_multiple):
275  merge_A,merge_Z = self.__merge_nuclei(other.A,other.Z)
276  merge_N = merge_A-merge_Z
277  Y_list = np.zeros(len(merge_N))
278  for ind,atmp in enumerate(merge_A):
279  mask_self = (self.A ==merge_A[ind]) & (self.Z ==merge_Z[ind])
280  mask_other = (other.A==merge_A[ind]) & (other.Z==merge_Z[ind])
281  if (np.any(mask_self)) and (np.any(mask_other)):
282  Y_list[ind] = self.Y[mask_self][0]/other.Y[mask_other][0]
283  self.A = merge_A
284  self.Z = merge_Z
285  self.N = merge_N
286  self.Y = Y_list
287  self.__init_with_data()
288  return self
289 
290 
291 
292  def __rmul__(self,other):
293  return self.__mul__(other)
294 
295 
296  def __add__(self,other):
297  """
298  Add to instances of this class. The abundances are added for each nucleus.
299  """
300  if isinstance(other,float) or isinstance(other,int):
301  self.Y=self.Y+other
302  elif isinstance(other,nucleus_multiple):
303  merge_A,merge_Z = self.__merge_nuclei(other.A,other.Z)
304  merge_N = merge_A-merge_Z
305  Y_list = np.zeros(len(merge_N))
306  for ind,atmp in enumerate(merge_A):
307  mask_self = (self.A ==merge_A[ind]) & (self.Z ==merge_Z[ind])
308  mask_other = (other.A==merge_A[ind]) & (other.Z==merge_Z[ind])
309  if (np.any(mask_self)):
310  Y_list[ind] += self.Y[mask_self][0]
311  if (np.any(mask_other)):
312  Y_list[ind] += other.Y[mask_other][0]
313  self.A = merge_A
314  self.Z = merge_Z
315  self.N = merge_N
316  self.Y = Y_list
317  self.__init_with_data()
318  return self
319 
320  def write_finab(self,path="finab.dat",min=1e-20):
321  """
322  Write the result (contained in self.__abundances) into a final abundance file. The format is:
323  |A Z N Yi Xi|
324  Input:
325  path - Path to output file
326  """
327  Y = self.Y
328  mask = Y>min
329  X = (self.Y*self.A)
330  A = self.A[mask]
331  Z = self.Z[mask]
332  N = self.N[mask]
333 
334  X = X[mask]
335  Y = Y[mask]
336 
337  output = np.array([A,Z,N,Y,X]).T
338  np.savetxt(path,output,fmt=['%7i','%7i','%7i',' %1.9e',' %1.9e'],header='A Z N Y X')
339 
340  # def decay(self):
341  # """
342  # Let the nuclei decay
343  # """
344  # dec = decay()
345  # dec.set_initial_composition(self.Y,self.A,self.Z)
346  # dec.solve_gear(1e16)
347 
348  # Y = dec.Yfinal
349  # mask = Y>1e-15
350  # A = dec.A[mask]
351  # Z = dec.Z[mask]
352  # N = dec.N[mask]
353  # Y = Y[mask]
354 
355  # self.A = A
356  # self.Z = Z
357  # self.N = N
358  # self.Y = Y
359  # self.__init_with_data()
360 
361  def write_seed(self,path=None,cutoff=1e-15):
362  """
363  Write a seed file
364  """
365  line=""
366  for i in range(len(self.A)):
367  X = self.Y[i]*self.A[i]
368  mafra = '%1.6e' % float(X)
369  if np.isnan(X) or X<=cutoff:
370  continue
371  nam = self.names[i].lower()
372  # Take care of special names
373  if (nam.strip() == 'neutron') or (nam.strip() == 'neutron1'):
374  nam = 'n'
375  elif nam.strip() == 'h1':
376  nam = 'p'
377  elif nam.strip() == 'h2':
378  nam = 'd'
379  elif nam.strip() == 'h3':
380  nam = 't'
381 
382  line+= nam.rjust(5) + ' '*7 + mafra
383  if i != len(self.A)-1:
384  line+="\n"
385  # Make a default path
386  if path is None:
387  path = "seed.dat"
388 
389  with open(path,"w") as f:
390  f.write(line)
391 
392 
393  def write_sunet(self,path=None):
394  """
395  Write a sunet file
396  """
397  line=""
398  for i in range(len(self.A)):
399  nam = self.names[i].lower()
400  # Take care of special names
401  if (nam.strip() == 'neutron') or (nam.strip() == 'neutron1'):
402  nam = 'n'
403  elif nam.strip() == 'h1':
404  nam = 'p'
405  elif nam.strip() == 'h2':
406  nam = 'd'
407  elif nam.strip() == 'h3':
408  nam = 't'
409 
410  line+= nam.rjust(5)
411  if i != len(self.A)-1:
412  line+="\n"
413  # Make a default path
414  if path is None:
415  path = "sunet.dat"
416 
417  with open(path,"w") as f:
418  f.write(line)
419 
420 
421  def set_A_Z_Y(self, A, Z, Y):
422  """
423  Abundance of protons
424  """
425  for i in range(len(A)):
426  indices = np.where((self.A == A[i]) & (self.Z == Z[i]))
427  self.Y[indices] = Y[i]
428 
429  def reset(self):
430  self.Y[:] = 0
431 
432 
433  def plot_nuclear_chart(self, ax, plot_outline=False, outline_color="k", **kwargs):
434  """
435  Plot the nuclear chart with the nuclei that are here
436  """
437  # Do this with imshow
438 
439  # Get the data
440  data = self.df
441 
442  Z = data["Z"].values
443  N = data["N"].values
444  X = data["X"].values
445 
446  # Get data to right format for imshow
447  Zmax = max(Z)
448  Nmax = max(N)
449  Zmin = min(Z)
450  Nmin = min(N)
451 
452  # Create the matrix
453  matrix = np.empty([Zmax-Zmin+2,Nmax-Nmin+2])
454  matrix[:,:] = np.nan
455  for i in range(len(Z)):
456  matrix[Z[i]-Zmin,N[i]-Nmin] = X[i]
457 
458 
459  # Make the data ready to use pcolor
460  X = np.arange(Nmin-0.5,Nmax+1.5,1)
461  Y = np.arange(Zmin-0.5,Zmax+1.5,1)
462  X,Y = np.meshgrid(X,Y)
463 
464  if "facecolor" in kwargs:
465  cmap = ListedColormap([kwargs["facecolor"]])
466  im = ax.pcolor(X,Y,matrix,cmap=cmap,**kwargs)
467  else:
468  im = ax.pcolor(X,Y,matrix,**kwargs)
469 
470 
471  if plot_outline:
472  matrix = np.empty([Zmax+1,Nmax+1])
473  matrix[:,:] = np.nan
474  for i in range(len(Z)):
475  matrix[Z[i],N[i]] = 1
476  # Plot the outline as well
477  matrix[~np.isnan(matrix)] = 1
478  matrix[np.isnan(matrix)] = 0
479 
480  bool_matrix = matrix.astype(bool)
481  plot_outlines(bool_matrix.T, ax=ax,color=outline_color)
482 
483  # Plot the data, ensure that the origin is on the lower left
484  ax.set_aspect("equal")
485 
486  # masked_matrix = np.ma.masked_where(np.isnan(matrix),matrix)
487  # ax.pcolormesh(masked_matrix,**kwargs)
488  # ax.set_aspect("equal")
489 
490  # Plot the data, ensure that the origin is on the lower left
491  # im = ax.imshow(matrix,origin='lower',extent=[Nmin-0.5,Nmax+0.5,Zmin-0.5,Zmax+0.5], \
492  # aspect="equal",**kwargs)
493  return ax
494 
495 def get_all_edges(bool_img):
496  """
497  Get a list of all edges (where the value changes from True to False) in the 2D boolean image.
498  The returned array edges has he dimension (n, 2, 2).
499  Edge i connects the pixels edges[i, 0, :] and edges[i, 1, :].
500  Note that the indices of a pixel also denote the coordinates of its lower left corner.
501  """
502  edges = []
503  ii, jj = np.nonzero(bool_img)
504  for i, j in zip(ii, jj):
505  # North
506  if j == bool_img.shape[1]-1 or not bool_img[i, j+1]:
507  edges.append(np.array([[i, j+1],
508  [i+1, j+1]]))
509  # East
510  if i == bool_img.shape[0]-1 or not bool_img[i+1, j]:
511  edges.append(np.array([[i+1, j],
512  [i+1, j+1]]))
513  # South
514  if j == 0 or not bool_img[i, j-1]:
515  edges.append(np.array([[i, j],
516  [i+1, j]]))
517  # West
518  if i == 0 or not bool_img[i-1, j]:
519  edges.append(np.array([[i, j],
520  [i, j+1]]))
521 
522  if not edges:
523  return np.zeros((0, 2, 2))
524  else:
525  return np.array(edges)
526 
527 
528 def close_loop_edges(edges):
529  """
530  Combine thee edges defined by 'get_all_edges' to closed loops around objects.
531  If there are multiple disconnected objects a list of closed loops is returned.
532  Note that it's expected that all the edges are part of exactly one loop (but not necessarily the same one).
533  """
534 
535  loop_list = []
536  while edges.size != 0:
537 
538  loop = [edges[0, 0], edges[0, 1]] # Start with first edge
539  edges = np.delete(edges, 0, axis=0)
540 
541  while edges.size != 0:
542  # Get next edge (=edge with common node)
543  ij = np.nonzero((edges == loop[-1]).all(axis=2))
544  if ij[0].size > 0:
545  i = ij[0][0]
546  j = ij[1][0]
547  else:
548  loop.append(loop[0])
549  # Uncomment to to make the start of the loop invisible when plotting
550  # loop.append(loop[1])
551  break
552 
553  loop.append(edges[i, (j + 1) % 2, :])
554  edges = np.delete(edges, i, axis=0)
555 
556  loop_list.append(np.array(loop))
557 
558  return loop_list
559 
560 
561 def plot_outlines(bool_img, ax=None, **kwargs):
562  if ax is None:
563  ax = plt.gca()
564 
565  edges = get_all_edges(bool_img=bool_img)
566  edges = edges - 0.5 # convert indices to coordinates; TODO adjust according to image extent
567  outlines = close_loop_edges(edges=edges)
568  cl = LineCollection(outlines, **kwargs)
569  ax.add_collection(cl)
570 
571 
572 if __name__ == "__main__":
573  """ Test the class """
574 
575  nlist = ["o16","ni56"]
576  A = [1,16,17,56]
577  Z = [1,8,8,28]
578  Y = [0.1,0.1,0.1,0.1]
579  nucl = nucleus_multiple(A=A,Z=Z,Y=Y)
580  A = [2,16,10,56,56]
581  Z = [1,8,8,28,26]
582  Y = [0.2,0.2,0.3,0.4,0.1]
583 
584  nucl2 = nucleus_multiple(A=A,Z=Z,Y=Y)
585  print(nucl+nucl2)
586  A,X=nucl2.A_X
src_files.nucleus_multiple_class.nucleus_multiple.elnames
def elnames(self)
Definition: nucleus_multiple_class.py:219
src_files.nucleus_multiple_class.nucleus_multiple.abar
def abar(self)
Definition: nucleus_multiple_class.py:191
src_files.nucleus_multiple_class.nucleus_multiple.__init_with_data
def __init_with_data(self)
Definition: nucleus_multiple_class.py:54
src_files.nucleus_multiple_class.nucleus_multiple.Z_Y
def Z_Y(self)
Definition: nucleus_multiple_class.py:135
src_files.nucleus_multiple_class.nucleus_multiple.df
def df(self)
Definition: nucleus_multiple_class.py:205
src_files.nucleus_multiple_class.nucleus_multiple.write_sunet
def write_sunet(self, path=None)
Definition: nucleus_multiple_class.py:393
src_files.nucleus_multiple_class.nucleus_multiple.write_seed
def write_seed(self, path=None, cutoff=1e-15)
Definition: nucleus_multiple_class.py:361
src_files.nucleus_multiple_class.nucleus_multiple.__truediv__
def __truediv__(self, other)
Definition: nucleus_multiple_class.py:268
src_files.nucleus_multiple_class.nucleus_multiple.Yprot
def Yprot(self)
Definition: nucleus_multiple_class.py:159
src_files.nucleus_multiple_class.nucleus_multiple.Ye
def Ye(self)
Definition: nucleus_multiple_class.py:152
src_files.nucleus_multiple_class.nucleus_multiple.__add__
def __add__(self, other)
Definition: nucleus_multiple_class.py:296
src_files.nucleus_multiple_class.nucleus_multiple.Yneut
def Yneut(self)
Definition: nucleus_multiple_class.py:169
src_files.nucleus_multiple_class.nucleus_multiple.set_A_Z_Y
def set_A_Z_Y(self, A, Z, Y)
Definition: nucleus_multiple_class.py:421
src_files.nucleus_multiple_class.nucleus_multiple.__rmul__
def __rmul__(self, other)
Definition: nucleus_multiple_class.py:292
src_files.nucleus_multiple_class.plot_outlines
def plot_outlines(bool_img, ax=None, **kwargs)
Definition: nucleus_multiple_class.py:561
src_files.nucleus_multiple_class.nucleus_multiple.A
A
Definition: nucleus_multiple_class.py:21
bin.create_alpha_decay_file.get_name
def get_name(Z, N, Zdict, Ndict)
Definition: create_alpha_decay_file.py:116
src_files.nucleus_multiple_class.nucleus_multiple.__sum_over
def __sum_over(self, A, Y)
Definition: nucleus_multiple_class.py:101
src_files.nucleus_multiple_class.nucleus_multiple.Yhe4
def Yhe4(self)
Definition: nucleus_multiple_class.py:179
src_files.nucleus_multiple_class.nucleus_multiple.__repr__
def __repr__(self)
Definition: nucleus_multiple_class.py:42
src_files.nucleus_multiple_class.nucleus_multiple.Y
Y
Definition: nucleus_multiple_class.py:31
src_files.nucleus_multiple_class.nucleus_multiple.__init__
def __init__(self, names=None, A=None, Z=None, N=None, Y=None, X=None)
Definition: nucleus_multiple_class.py:14
src_files.nucleus_multiple_class.get_all_edges
def get_all_edges(bool_img)
Definition: nucleus_multiple_class.py:495
src_files.nucleus_multiple_class.nucleus_multiple.A_X
def A_X(self)
Definition: nucleus_multiple_class.py:127
src_files.nucleus_class.nucleus
Definition: nucleus_class.py:9
src_files.nucleus_multiple_class.close_loop_edges
def close_loop_edges(edges)
Definition: nucleus_multiple_class.py:528
src_files.nucleus_multiple_class.nucleus_multiple.X
def X(self)
Definition: nucleus_multiple_class.py:112
src_files.nucleus_multiple_class.nucleus_multiple.plot_nuclear_chart
def plot_nuclear_chart(self, ax, plot_outline=False, outline_color="k", **kwargs)
Definition: nucleus_multiple_class.py:433
src_files.nucleus_multiple_class.nucleus_multiple.names
names
Definition: nucleus_multiple_class.py:20
src_files.nucleus_multiple_class.nucleus_multiple.zbar
def zbar(self)
Definition: nucleus_multiple_class.py:198
src_files.nucleus_multiple_class.nucleus_multiple.__mul__
def __mul__(self, other)
Definition: nucleus_multiple_class.py:246
src_files.nucleus_multiple_class.nucleus_multiple
Definition: nucleus_multiple_class.py:10
src_files.nucleus_multiple_class.nucleus_multiple.N
N
Definition: nucleus_multiple_class.py:23
src_files.nucleus_multiple_class.nucleus_multiple.Z
Z
Definition: nucleus_multiple_class.py:22
src_files.nucleus_multiple_class.nucleus_multiple.__merge_nuclei
def __merge_nuclei(self, A, Z)
Definition: nucleus_multiple_class.py:233
src_files.nucleus_multiple_class.nucleus_multiple.write_finab
def write_finab(self, path="finab.dat", min=1e-20)
Definition: nucleus_multiple_class.py:320
src_files.nucleus_multiple_class.nucleus_multiple.__init_with_list
def __init_with_list(self)
Definition: nucleus_multiple_class.py:45
src_files.nucleus_multiple_class.nucleus_multiple.reset
def reset(self)
Definition: nucleus_multiple_class.py:429
src_files.nucleus_multiple_class.nucleus_multiple.Z_X
def Z_X(self)
Definition: nucleus_multiple_class.py:143