def NormPDF(x,pdf,normalized_to,*other): ''' Inputs: x [type=list or vector], pdf [type = function or list or vector], normalized_to [type=float or int], *other are extra parameters that are needed for the pdf fuction used Output: a normalized pdf [type = function or vector] ''' cont = Normalize(x,pdf,normalized_to,*other) if callable(pdf) == True: return pdf(x,*other)*cont else: if isinstance(pdf,type(np.vector([0]))) != True: pdf = np.vector(pdf) return pdf*cont
def Temp(r,time): ''' Inputs: Radius [m], Time [s]; Outputs: Temperature of the plasma [keV] ''' ''' The .2 is the floor temperature that we allow the gas to be ''' T = T_max*(1-(r/(1.0001*R0))**2)**(2/7)*math.exp(-.5*((time/BurnSigma))**2) if isinstance(r,type(np.vector(0))) == True: T = list(T) for i in range(len(T)): if T[i] < .2: T[i] = .2 return np.vector(T) else: if T < .2: T = .2 return T
def trapazoid_2_d(function,x_1,x_2,*other): I = 0 del_x = (x_1[-1]-x_2[0])/(2*(len(x_1)-1)) A = 2*npp.identity(len(x_1)) A[0][0] , A[-1][-1] = 1 , 1 for t in x_2: y = function(np.vector(list(x_1)),t,*other) I += sum(A.dot(y))*del_x return I*((x_2[-1]-x_2[0])/len(x_2))
def CrossSection(energy, region): """ Energy is neutron energy in eV """ """ Decides which atom to collide with, records it, calculates that cross section and returns it.""" cross_section = list( np.vector(Fits(energy, region)) * Ratio[region] ) # this line takes into account the % of substance in the material's effect on the choice of what atom is collided with cross_section.append(0), cross_section.insert(0, 0) choice = DiscreteChoice(cross_section) xc = cross_section[choice + 1] * BarnToCmSquare return (xc, choice)
def CreateCDF(pdf,a,b,N,normalized_to,*other): ''' Generalized CDF Creation from Normalized continious PDF''' ''' Inputs: pdf [type = function or vector], a = beginign of domain[type = float or int], b end of domain = [type = float or int], N = [type = int], normalized_to [type = float or int], *other are extra parameters that are needed for the pdf fuction used. Outputs: a list containing the cdf values ''' if callable(pdf) == True : x = np.vector((linspace(a,b,N))) return trapazoid(x,NormPDF(x,pdf,normalized_to,*other),'Cumulative'),x ############################## else: x = linspace(a,b,len(pdf)-1) return trapazoid(x,NormPDF(x,pdf,normalized_to),'Cumulative'),x
def mini_data_func(position, velocity, energy, region, weighting): exponential = 0 ##vector_sum = position+detector_position velocity_vs_detector = velocity.dot(detector_position) / velocity.norm() / detector_position.norm() ##velocity_difference = velocity.dot(vector_sum)/velocity.norm()/vector_sum.norm() # CM_needed = math.cos(velocity_difference) CM_needed = velocity_vs_detector # math.cos(math.pi - velocity_vs_detector) # print('Velocity_against detector:',math.acos(velocity_vs_detector)*180/math.pi) # print('Center of mass:',math.acos(CM_needed)*180/math.pi) # def find_prob(region): Probs = [] for choice in range(len(A[region])): for i in range(len(Energy_dic_atoms)): if A[region][choice] == Energy_dic_atoms[i]: # print('Lab scatter needed:',(1 + A[region][choice]*CM_needed)/math.sqrt((A[region][choice]**2)+1+(2*A[region][choice]*CM_needed))) choice_index = i index_energy = min( range(len(Energy_dic[choice_index])), key=lambda i: abs(Energy_dic[choice_index][i] - energy) ) scatter_index = min( range(len(Probability[choice_index][index_energy])), key=lambda i: abs(Cosines[choice_index][index_energy][i] - CM_needed), ) Probs.append(Probability[choice_index][index_energy][scatter_index]) return Probs Probs = find_prob(region) while position.norm() < Radius[-2]: difference = detector_position - position region = Geometry(position) min_dis = GeometricDistance(position, difference, region) if min_dis <= 10e-9: """ this is necessary because the GeometricExpansionSphere function find the roots of a polynomial, and finding roots numerically is always difficult and leads to some amount of round off errors, this check is to ensure that if you are almost at a boundry there is a good reason to believe that if you are not excactly on the boundry, then you are supposed to be on the boundry but round off errors caused you to stay just below the boundry, check accounts for this.""" position += 1e-9 * difference / difference.norm() region = Geometry(position) difference = detector_position - position min_dis = abs(GeometricDistance(position, difference, region)) position += min_dis * (difference / difference.norm()) xc = (np.vector(Fits(energy, region)) * Ratio[region]).average() * BarnToCmSquare # Average cross section in cm exponential += -min_dis * (xc * NDensity[region] / 100) weighting *= math.exp(-exponential) * sum(Probs) / len(Probs) return weighting
fusion_list = [1,1,1] # Set to zero to shut off reaction [DT,DD,TT] Ballabio = 1 # Set to 0 for brysk and 1 for ballabio implosion = 1 # Set to 1 for implosion or 0 for static PeakVelocity = 150E3 M_hs = 15E-6 # Total mass of hot spot in grams M_ice = 171E-6-M_hs # Mass of ice in grams CH_rho_r = 200E-3 # Rho*R for ablator in g/cm^2 ################################### ######## Aparatus Features ######## ################################### ''' DT/DT/CH/VACCUM''' Radius = [25E-6,50E-6,50E-6+70E-6,20,30] # Radius of each region in meters Density = np.vector([M_hs/(4*math.pi*(Radius[0]*1E2)**3/3),M_ice/(4*math.pi*((Radius[1]*1E2)**3-(Radius[0]*1E2)**3)/3),CH_rho_r/(Radius[2]*1E2-Radius[1]*1E2),1E-10,1E-10]) #g/cm^3 A = [[2,3],[2,3],[12,1],[1],[1]] # Atomic mass of each substance with that region Ratio = np.vector([[.5,.5],[.5,.5],[(1/(1+1.3)),(1.3/(1+1.3))],[1],[1]]) # Ratio of each substance that make up material ''' DT/DT/CH/VACCUM/AL/VACCUM/AIR/W ''' #Radius = [33E-6,53E-6,53E-6+70E-6,.5E-2,.5E-2+.5E-3,11,19.98,20,30] # Radius of each region in meters #Density = np.vector([M_hs/(4*math.pi*(Radius[0]*1E2)**3/3),M_ice/(4*math.pi*((Radius[1]*1E2)**3-(Radius[0]*1E2)**3)/3),CH_rho_r/(Radius[2]*1E2-Radius[1]*1E2),1E-10,2.7,1E-10,.0012,18.3,1E-10]) #g/cm^3 #A = [[2,3],[2,3],[12,1],[1],[27],[1],[16,14,40],[183],[1]] # Atomic mass of each substance with that region #Ratio = np.vector([[.5,.5],[.5,.5],[(1/(1+1.3)),(1.3/(1+1.3))],[1],[1],[1],[.19,.8,.01],[1],[1]]) # Ratio of each substance that make up material ''' DT/DT/CH/VACCUM/AL/VACCUM/AIR ''' #Radius = [33E-6,53E-6,53E-6+70E-6,.5E-2,.5E-2+.5E-3,11,20,30] # Radius of each region in meters #Density = np.vector([M_hs/(4*math.pi*(Radius[0]*1E2)**3/3),M_ice/(4*math.pi*((Radius[1]*1E2)**3-(Radius[0]*1E2)**3)/3),CH_rho_r/(Radius[2]*1E2-Radius[1]*1E2),1E-10,2.7,1E-10,.0012,1E-10]) #g/cm^3 #A = [[2,3],[2,3],[12,1],[1],[27],[1],[16,14,40],[1]] # Atomic mass of each substance with that region #Ratio = np.vector([[.5,.5],[.5,.5],[(1/(1+1.3)),(1.3/(1+1.3))],[1],[1],[1],[.19,.8,.01],[1]]) R0 = Radius[0] # Edge radius of neutron production (largest radius at which a neuutron may be born) MaxRadius = Radius[2] # Where does this implosion stop?
def Parallel(NeutronAttributes): global collisions, Stepers, Radius Energy, radius_now, ThetaEmission, PhiEmission, Theta_momentum, Phi_momentum, reaction = ( NeutronAttributes[0], NeutronAttributes[1], NeutronAttributes[2], NeutronAttributes[3], NeutronAttributes[4], NeutronAttributes[5], NeutronAttributes[6], ) Position = ( np.vector([cos(ThetaEmission) * sin(PhiEmission), sin(ThetaEmission) * sin(PhiEmission), cos(PhiEmission)]) * radius_now ) BirthEnergy = Energy Region = ( 0 ) # For this simulation we assume all neutrons are born in the gas (region=0), the more general case would have be == Region=Geometry(Position) steps, collisions_counter = 0, 0 scatters = [] Weights = [] MINI_DATA = [] Tau_counter = 0 while Region < len(Radius) - 1: collision_pre = collisions_counter Position, Tau, Region, Energy, steps, collisions_counter, Theta_momentum, Phi_momentum, Velocity_direction, scatter_atom, Weight, Mini_Data = Transport( Position, Energy, Theta_momentum, Phi_momentum, Region, collisions_counter, steps, reaction, BirthEnergy, Tau_counter, ) MINI_DATA.append(Mini_Data) if type(Region) == int: Region = Geometry(Position) Weights.append(Weight) else: break scatters.append(scatter_atom) if collisions_counter > collision_pre: collisions += 1 Tau_counter += Tau Stepers += steps if Region != "Error": return MINI_DATA else: return [ "Error", "Error", "Error", "Error", "Error", "Error", "Error", "Error", "Error", "Error", "Error", "Error", ]
def mini_transport( position, energy, theta_momentum, phi_momentum, region, collision_counter, steps, velocity_direction, tau, scatter_atom, weighting, reaction, Tau, ): # PUT MINI DATA FUNC HERE global COUNT tag_transport = 0 steps += 1 if energy < 50e3 or steps > 50: region = "Error" position = np.vector([Radius[-2], 0, 0]) return ( position, 0, region, energy, steps, collision_counter, theta_momentum, phi_momentum, velocity_direction, 0, 0, weighting, Tau, ) velocity_direction = np.vector( [cos(theta_momentum) * sin(phi_momentum), sin(theta_momentum) * sin(phi_momentum), cos(phi_momentum)] ) Velocity = EnergyToVelocity(energy, Mn) * velocity_direction # vector weighting = mini_data_func(position, Velocity, energy, region, weighting) xc = (np.vector(Fits(energy, region)) * Ratio[region]).average() * BarnToCmSquare mean_free_path = (1 / NDensity[region] / xc) / 100 # converts cross section into mean free path in meters x = np.vector(linspace(0, 5 * mean_free_path, 100)) step = Choice(BeerLaw(x, mean_free_path), x) geometric_distance = GeometricDistance(position, Velocity, region) if geometric_distance <= 10e-9: """ this is necessary because the GeometricExpansionSphere function find the roots of a polynomial, and finding roots numerically is always difficult and leads to some amount of round off errors, this check is to ensure that if you are almost at a boundry there is a good reason to believe that if you are not excactly on the boundry, then you are supposed to be on the boundry but round off errors caused you to stay just below the boundry, check accounts for this.""" position += 1e-9 * Velocity / Velocity.norm() return ( position, 0, region, energy, steps, collision_counter, theta_momentum, phi_momentum, velocity_direction, 0, 0, weighting, Tau, ) if step < geometric_distance: COUNT += 1 collision_counter += 1 position += step * Velocity / Velocity.norm() tau += step / Velocity.norm() xc, atom = CrossSection(energy, region) mean_free_path = (1 / NDensity[region] / xc) / 100 # converts cross section into mean free path in meters cos_theta_CMF, phi, energy = ScatteringAngle(energy, atom, region) cos_theta_lab = (1 + A[region][atom] * cos_theta_CMF) / math.sqrt( (A[region][atom] ** 2) + 1 + (2 * A[region][atom] * cos_theta_CMF) ) theta_lab = math.acos(cos_theta_lab) theta_momentum += theta_lab phi_momentum += phi scatter_atom = A[region][atom] else: position += (geometric_distance) * Velocity / Velocity.norm() tau += geometric_distance / Velocity.norm() tag_transport = 1 Tau += tau return ( position, tau, region, energy, steps, collision_counter, theta_momentum, phi_momentum, velocity_direction, scatter_atom, tag_transport, weighting, Tau, )
def Transport( position, energy, theta_momentum, phi_momentum, region, collision_counter, steps, reaction, BirthEnergy, Tau ): def mini_transport( position, energy, theta_momentum, phi_momentum, region, collision_counter, steps, velocity_direction, tau, scatter_atom, weighting, reaction, Tau, ): # PUT MINI DATA FUNC HERE global COUNT tag_transport = 0 steps += 1 if energy < 50e3 or steps > 50: region = "Error" position = np.vector([Radius[-2], 0, 0]) return ( position, 0, region, energy, steps, collision_counter, theta_momentum, phi_momentum, velocity_direction, 0, 0, weighting, Tau, ) velocity_direction = np.vector( [cos(theta_momentum) * sin(phi_momentum), sin(theta_momentum) * sin(phi_momentum), cos(phi_momentum)] ) Velocity = EnergyToVelocity(energy, Mn) * velocity_direction # vector weighting = mini_data_func(position, Velocity, energy, region, weighting) xc = (np.vector(Fits(energy, region)) * Ratio[region]).average() * BarnToCmSquare mean_free_path = (1 / NDensity[region] / xc) / 100 # converts cross section into mean free path in meters x = np.vector(linspace(0, 5 * mean_free_path, 100)) step = Choice(BeerLaw(x, mean_free_path), x) geometric_distance = GeometricDistance(position, Velocity, region) if geometric_distance <= 10e-9: """ this is necessary because the GeometricExpansionSphere function find the roots of a polynomial, and finding roots numerically is always difficult and leads to some amount of round off errors, this check is to ensure that if you are almost at a boundry there is a good reason to believe that if you are not excactly on the boundry, then you are supposed to be on the boundry but round off errors caused you to stay just below the boundry, check accounts for this.""" position += 1e-9 * Velocity / Velocity.norm() return ( position, 0, region, energy, steps, collision_counter, theta_momentum, phi_momentum, velocity_direction, 0, 0, weighting, Tau, ) if step < geometric_distance: COUNT += 1 collision_counter += 1 position += step * Velocity / Velocity.norm() tau += step / Velocity.norm() xc, atom = CrossSection(energy, region) mean_free_path = (1 / NDensity[region] / xc) / 100 # converts cross section into mean free path in meters cos_theta_CMF, phi, energy = ScatteringAngle(energy, atom, region) cos_theta_lab = (1 + A[region][atom] * cos_theta_CMF) / math.sqrt( (A[region][atom] ** 2) + 1 + (2 * A[region][atom] * cos_theta_CMF) ) theta_lab = math.acos(cos_theta_lab) theta_momentum += theta_lab phi_momentum += phi scatter_atom = A[region][atom] else: position += (geometric_distance) * Velocity / Velocity.norm() tau += geometric_distance / Velocity.norm() tag_transport = 1 Tau += tau return ( position, tau, region, energy, steps, collision_counter, theta_momentum, phi_momentum, velocity_direction, scatter_atom, tag_transport, weighting, Tau, ) global COUNT tau, weighting = 0, 1 / math.pi / 4 velocity_direction = np.vector( [cos(theta_momentum) * sin(phi_momentum), sin(theta_momentum) * sin(phi_momentum), cos(phi_momentum)] ) Velocity = EnergyToVelocity(energy, Mn) * velocity_direction # vector if implosion == 1: implosion_velocity = interpol(position.norm(), ImplosionRadius, ImplosionVelocity) if steps == 0: # implosion velocity only changes the neutron at birth (aka before any transport so steps==0) if position.norm() == 0: # If its at the center implosion_direction = -1 * velocity_direction else: implosion_direction = -1 * position / radius Implosion_Velocity = implosion_velocity * implosion_direction # Vector Velocity = (Velocity + Implosion_Velocity) / ( 1 + (Velocity.norm() * implosion_velocity) / 3e16 ) # Add them relativistic # Velocity += Implosion_Velocity # Add them classically Momentum = Mn * (Velocity / 3e8) / math.sqrt(1 - (Velocity.dot(Velocity)) / (3e8) ** 2) energy = MomentumToEnergy(Momentum.norm(), Mn) * 1e6 scatter_atom = 0 if GeometricDistance(position, Velocity, region) <= 10e-9: """ this is necessary because the GeometricExpansionSphere function find the roots of a polynomial, and finding roots numerically is always difficult and leads to some amount of round off errors, this check is to ensure that if you are almost at a boundry there is a good reason to believe that if you are not excactly on the boundry, then you are supposed to be on the boundry but round off errors caused you to stay just below the boundry, check accounts for this.""" # region += 1 position += velocity_direction * 1e-9 Mini_Data = [] region = Geometry(position) if region == len(Radius) - 1: return ( position, 0, region, energy, steps, collision_counter, theta_momentum, phi_momentum, velocity_direction, 0, 0, Mini_Data, ) if region == 0: while position.norm() <= Radius[region]: position, tau, region, energy, steps, collision_counter, theta_momentum, phi_momentum, velocity_direction, scatter_atom, tag_transport, weighting, Tau = mini_transport( position, energy, theta_momentum, phi_momentum, region, collision_counter, steps, velocity_direction, tau, scatter_atom, weighting, reaction, Tau, ) # YOUR DATA WRITTEN OUT Mini_Data.append( [ reaction, BirthEnergy / 1e6, collision_counter, scatter_atom, energy / 1e6, ((detector_position.norm() - position.norm()) / EnergyToVelocity(energy, Mn) + tau) / 1e-9, weighting, ] ) if tag_transport == 1 or region == "Error": break else: while position.norm() <= Radius[region] and position.norm() >= Radius[region - 1]: position, tau, region, energy, steps, collision_counter, theta_momentum, phi_momentum, velocity_direction, scatter_atom, tag_transport, weighting, Tau = mini_transport( position, energy, theta_momentum, phi_momentum, region, collision_counter, steps, velocity_direction, tau, scatter_atom, weighting, reaction, Tau, ) # YOUR DATA WRITTEN OUT if region == len(Radius) - 1: Mini_Data.append( [reaction, BirthEnergy / 1e6, collision_counter, scatter_atom, energy / 1e6, Tau, weighting] ) else: Mini_Data.append( [ reaction, BirthEnergy / 1e6, collision_counter, scatter_atom, energy / 1e6, ((detector_position.norm() - position.norm()) / EnergyToVelocity(energy, Mn) + tau) / 1e-9, weighting, ] ) if tag_transport == 1 or region == "Error": break return ( position, tau, region, energy, steps, collision_counter, theta_momentum, phi_momentum, velocity_direction, scatter_atom, weighting, Mini_Data, )
def BeerLaw(x, mean_free_path): return 1 - (-1 * x / mean_free_path).exp() COUNT = 0 count_list = [] MaxImplosionRadius = max(ImplosionRadius) #################################### #### Create neutron at radius ###### #################################### Number = int(Number) # Number of neutrons to simulate # 1 Produced radius shells Neutron_Radius = np.vector(linspace(0, R0, NumberShells)) # Radius to create neutrons at Neutron_Time = np.vector( linspace( -(TotalBurnSigma / 2) * BurnSigma, TotalBurnSigma / 2 * BurnSigma, math.floor(BurnSigma * TotalBurnSigma / TimeStep), ) ) # Times to create neutrons at N_tot = M_hs * N_A / (Ratio[0][0] * (MH2 - MH3) + MH3) # Number Density of fuel ### Go To child process to integrate dN/dr/dt subprocess.call(["python3", "Integration.py"]) ### Open file the child process writes out for us f = open("BirthLocation.txt", "r") f = f.read().splitlines()
################################################### number_per_time ,number_per_r = [],[] t_delta = Neutron_Time[1]-Neutron_Time[0] ''' Normalize the integral ''' Total = trapazoid_2_d(Delta_Number,Neutron_Radius,Neutron_Time,0) def NORM(radius,time): return Delta_Number(radius,time,0)*Number/Total '''Figure out how many are in each time ''' for time in Neutron_Time: them = trapazoid_2_d(NORM,Neutron_Radius,[time,time+t_delta]) number_per_time.append(them) ''' Now for that time, figure out how many are at each radius ''' Neutron_Radius = np.vector(list(Neutron_Radius)) Neutron_Time = np.vector(list(Neutron_Time)) index = 0 for number in number_per_time: Normeder = NormPDF(Neutron_Radius,Delta_Number,number,Neutron_Time[index],0) Number_In = trapazoid(Neutron_Radius,Normeder,'Discrete') number_per_r.append(Number_In) index+=1 f = open('BirthLocation.txt','w') for i in number_per_r: for j in i: if j == i[-1]: # If this is the last element in the row vector f.write('%f\n' % float(j)) # Make a new line after this element else: f.write('%f ' % float(j))
def PhiPDF(x): ''' The neutron creation is assumed isotropic ''' return np.vector(ones(N_theta))