-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathpysrim.py
212 lines (156 loc) · 6.18 KB
/
pysrim.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
"""PySrim - A program to simulate the implantations of ions in matter through a monte-carlo simulation
Usage:
pysrim.py [--numIons=<num>] [--elementIon=<elem>] [--ionEnergy=<eV>] [-o FILE]
pysrim.py (-h | --help)
pysrim.py --version
Options:
-h --help Show this screen.
--version Show version.
--numIons=<num> Number of Ions in Simulation [default: 10]
--elementIon=<element> The element of the Ion being shot [default: Ge]
--ionEnergy=<keV> The energy for each ion [default: 1000000]
-o Output filename [default: temp.csv]
"""
from ion import Ion
from element import Element, ElementTable
from target import Target, Layer, Compound
from statistics import IonStatistics
import numpy as np
import math
import copy
from numpy import dot
from numpy.linalg import norm
from docopt import docopt
from mpi4py import MPI
tollerance = 1E-8
def shootIon(ion, target, ionStatistics):
"""
A function to simulate one ion runing into a target
"""
testIon = copy.deepcopy(ion)
ionQueue = []
ionQueue.append(testIon)
while (len(ionQueue) != 0):
currentIon = ionQueue.pop()
ionStatistics.saveIonState(currentIon)
SimulateElectronicStopping(currentIon, target)
if (target.isPositionIn(currentIon.position) == True):
SimulateNuclearStopping(currentIon, target, ionQueue)
return "One Ion Cascade Complete"
def runSimulation(ion, target, numIons = 10, genStats = True):
"""
Simulates shooting numIons into a target
"""
ionStatistics = IonStatistics()
for i in range(numIons):
shootIon(ion, target, ionStatistics)
return ionStatistics
# Electronic Stopping Calculations
def SimulateElectronicStopping(ion, target):
"""
Simulates the movement of an ion within a target losing
energy to the electron clouds of the target. Returns
true if the ion is still traveling
"""
energyLoss, pathLength = calcElectronicEnergyLoss(ion, target)
ion.moveIonByDistance(pathLength)
if (ion.energy() <= energyLoss):
ion.velocity = np.array([0.0, 0.0, 0.0])
else:
unitDirection = ion.velocity / norm(ion.velocity, 2)
ion.velocity = math.sqrt((2 * (ion.energy() - energyLoss)) / ion.mass) * unitDirection
def calcElectronicEnergyLoss(ion, target):
"""
Calculates the energy lost of an ion while traveling
through the target to electrons
"""
pathLength = calcFreePathLength(ion, target)
energyLoss = 4.0 * pathLength
return (energyLoss, pathLength)
def calcFreePathLength(ion, target):
"""
Calculates the distance the given ion travels until it
hits the nucleus of an atom
"""
return max(math.sqrt(norm(ion.velocity, 2)), 5.0)
# Nuclear Stopping Calculations
def SimulateNuclearStopping(ion, target, ionQueue):
"""
Simluates the collision of an ion with an element within
the target. If a collision occurs
"""
atom = selectCollisionAtom(ion, target)
ion1, ion2 = calcCollision(ion, atom)
if (ion1.energy() > target.thresholdDisplacement(ion1)):
ionQueue.append(ion1)
if (ion2.energy() > target.thresholdDisplacement(ion2)):
ionQueue.append(ion2)
def selectCollisionAtom(ion, target):
"""
Based on the location of the ion within the target an atom
is chosen with a probablility equal to the stochiometry of the
layer. This is an amorphous assumption
"""
from numpy.random import rand
X = rand()
layer = target.get_layerByPosition(ion.position)
compound = layer.compound
for i in range(len(compound.stochiometry)):
if (X < compound.stochiometry[i] or i +1 == len(compound.stochiometry)):
return compound.elements[i]
else:
X = X - compound.stochiometry[i]
def calcCollision(ion, atom):
"""
Given an incoming ion and collision atom calculate the resulting
trajectories of the ions after collision. Energy is conserved.
The resulting trajectories is determined using the magic angle
formula.
"""
E0 = ion.energy()
V0 = ion.velocity
weight = np.random.rand()
unitDirection = np.random.rand(3) - 0.5
unitDirection = unitDirection / norm(unitDirection, 2)
while (dot(unitDirection, ion.velocity) < 0):
unitDirection = np.random.rand(3) - 0.5
unitDirection = unitDirection / norm(unitDirection, 2)
V1 = math.sqrt((2 * weight * E0) / ion.mass) * unitDirection
unitDirection = np.random.rand(3) - 0.5
unitDirection = unitDirection / norm(unitDirection, 2)
while (dot(unitDirection, ion.velocity) < 0):
unitDirection = np.random.rand(3) - 0.5
unitDirection = unitDirection / norm(unitDirection, 2)
V2 = math.sqrt((2 * (1 - weight) * E0) / atom.mass) * unitDirection
newIon = Ion(ion.position, V2 , atom)
ion.velocity = V1
return (ion, newIon)
if __name__ == "__main__":
arguments = docopt(__doc__, version='PySrim 0.1')
numIons = int(arguments['--numIons'])
element_table = ElementTable()
# Define the Ion
ionElement = element_table.get_elementbysymbol(arguments['--elementIon'])
position = np.array([0.0, 0.0, 0.0])
ionEnergy = float(arguments['--ionEnergy'])
velocity = math.sqrt(ionEnergy / ionElement.mass) * np.array([1.0, 0.0, 0.0])
ion = Ion(position, velocity, ionElement)
# Define the target material
Na = element_table.get_elementbysymbol('Na')
compoundNa = Compound([1.0], [Na], [15])
layerNa = Layer(1000000, compoundNa)
target = Target([layerNa])
# Initialize MPI Enviroment
comm = MPI.COMM_WORLD #Wow that was simple
size = comm.Get_size()
rank = comm.Get_rank()
# We split the job among all the nodes running the job
myNumIons = numIons / size
if ((numIons % size) > rank):
myNumIons += 1
print "My rank %d of %d running %d ions" % (rank, size, myNumIons)
ionStatistics = runSimulation(ion, target, numIons = myNumIons)
ionStatistics.consolidate(comm)
if (rank == 0):
outputFilename = arguments['FILE']
ionStatistics.to_csv(outputFilename)