def run_nuclide(nuc): bateman = defaultdict(emptytime) bateman[nuc][0] = 1 crammed = defaultdict(emptytime) crammed[nuc][0] = 1 diagexp = defaultdict(emptytime) diagexp[nuc][0] = 1 n0 = Material({nuc: 1.0}, mass=1.0, atoms_per_molecule=1.0) for i, t in enumerate(TIMES[1:], 1): # compute Bateman try: b1 = n0.decay(t).to_atom_frac() except RuntimeError: # decay can't handle all of the same nuclides CRAM can b1 = {} for key, val in b1.items(): n = nucname.name(key) bateman[n][i] = val # compute CRAM c1 = n0.cram(DECAY_MATS[t], order=16).to_atom_frac() for key, val in c1.items(): n = nucname.name(key) crammed[n][i] = val # compute e^x of the diagonal of the decay matrix, ie the nuc itself nuc_idx = cram.NUCS_IDX[nuc] mat_idx = cram.IJ[nuc_idx, nuc_idx] diagexp[nuc][i] = exp(-DECAY_MATS[t][mat_idx]).evalf(n=30) return bateman, crammed, diagexp
class Ejecta(object): """ Radioactive Ejecta composition Parameters ---------- mass: ~float mass in solar masses composition: ~dict a composition dictionary, e.g. {'Co56':0.5, 'Ni56':0.5} will be normalized to 1 """ @classmethod def from_yann_file(cls, fname): data = pd.read_table(fname, names=['isotope', 'mass'], delim_whitespace=True) data = data.set_index('isotope') mass = data.mass.sum() data['norm_mass'] = data.mass / mass composition = data.norm_mass.to_dict() return cls(mass, composition) @classmethod def from_masses(cls, **kwargs): """ Initialize the ejecta from masses Parameters ---------- **kwargs: key, value pairs like Co56=1e33*u.g """ mass = sum(kwargs.values()).to(u.Msun) composition = { key: float(value / mass) for key, value in kwargs.items() } return cls(mass.value, composition) def __init__(self, mass_msol, composition): self.mass_g = mass_msol * msun_to_cgs self.material = Material(self._normalize_composition(composition)) self._pad_material() atomic_masses = self.get_masses() self.n_per_g = [ 1 / atomic_masses[item] for item in self.get_all_children_nuc_name() ] @property def mass(self): return self.mass_g * u.g def __getitem__(self, item): return self.material.__getitem__(item) def __setitem__(self, key, value): self.material.__setitem__(key, value) def keys(self): return self.material.keys() @property def isotopes(self): return [nucname.name(id) for id in self.keys()] def get_decay_constant(self): return OrderedDict( (nuc_name, data.decay_const(nuc_id)) for nuc_id, nuc_name in zip( self.get_all_children(), self.get_all_children_nuc_name())) def get_half_life(self): return [data.half_life(nuc_id) for nuc_id in self.keys()] def get_masses(self): return { nuc_name: data.atomic_mass(nuc_id) * u_to_g for nuc_id, nuc_name in zip(self.keys(), self.isotopes) } def get_all_children(self): children_set = set() def get_child(nuc_id): children = data.decay_children(nuc_id) if len(children) != 0: children_set.update(children) for child_nuc_id in children: get_child(child_nuc_id) for nuc_id in self.material: children_set.add(nuc_id) get_child(nuc_id) return sorted(children_set) def get_all_children_nuc_name(self): return [nucname.name(nuc_id) for nuc_id in self.get_all_children()] @staticmethod def _normalize_composition(composition): composition_sum = np.sum(list(composition.values())) normed_composition = { key: value / composition_sum for key, value in composition.items() } return normed_composition def _pad_material(self): for isotope in self.get_all_children_nuc_name(): try: self.material[isotope] except KeyError: self.material[isotope] = 0.0 def decay(self, epochs): """ Decay the ejecta material Parameters ---------- epochs: numpy or quantity array Returns ------- : ~pd.DataFrame """ epochs = u.Quantity(epochs, u.day) isotope_children = self.get_all_children() columns = self.get_all_children_nuc_name() decayed_fractions = pd.DataFrame(index=epochs.value, columns=columns, dtype=np.float64) day_to_s = 24 * 3600 for epoch, row in decayed_fractions.iterrows(): new_material = self.material.decay(epoch * day_to_s) fractions = [ 0.0 if key not in new_material else new_material[key] for key in isotope_children ] row.values[:] = fractions return decayed_fractions def get_decayed_numbers(self, epochs): epochs = u.Quantity(epochs, u.day) fractions = self.decay(epochs) return fractions * self.mass_g * self.n_per_g def __repr__(self): return self.material.__str__() def get_numbers(self): N = {} for nuc_id, nuc_name in zip(self.keys(), self.isotopes): mass = self.material[nuc_id] * self.mass_g N[nuc_name] = (1 / (data.atomic_mass(nuc_id) * u_to_g) * mass) return N @property def N(self): return self.get_number()