@@ -31,6 +31,50 @@ def __init__(self, rho=None, tau=None, mu=None,
3131 propagator = None , phase_cycle = False ,
3232 labels = ['00' ,'01 -2' ,'10 2\' ' ,'10 1' ,'20 1+2\' ' ,'11 1-2' ,'11 2\' -2' , '10 1-2+2\' ' , '21 1-2+2\' ' ],
3333 time_orderings = list (range (1 ,7 )), recorded_indices = [7 , 8 ]):
34+ """Create a Hamiltonian object.
35+
36+ Parameters
37+ ----------
38+ rho : 1-D array <Complex>
39+ The initial density vector, length defines N.
40+ Default is 1. in the ground state (index 0), and 0. everywhere else.
41+ tau : scalar or 1-D array
42+ The lifetime of the states in femptoseconds.
43+ If tau is scalar, the same lifetime is used for all transitions,
44+ except populations, which default to np.inf.
45+ Default is 50. fs (scalar, then brodcast as above.
46+ mu : 1-D array <Complex>
47+ The magnetic suceptabilities used either in computing state densities or output intensities
48+ Array like structures are converted to the proper data type.
49+ Order mattersd, and meaning is dependent on the individual Hamiltonian.
50+ Default is two values, both initially 1.
51+ omega : 1-D array <float64>
52+ The energies of various transitions.
53+ The defalut uses w_central and coupling parameters to compute the appropriate
54+ values for a TRIVE Hamiltonian
55+ w_central : float
56+ The cetral frequency of a resonance for a TRIVE Hamiltonian.
57+ Used only when omega is None.
58+ coupling : float
59+ The copuling of states for a TRIVE Hamiltonian.
60+ Used only when omega is None.
61+ propagator : function
62+ The evolution function to use to evolve the density matrix over time.
63+ Default is runge_kutta.
64+ phase_cycle : bool
65+ Whether or not to use phase cycling.
66+ Not applicable to all Hamiltonians.
67+ Default is ``False``
68+ labels : list of string
69+ Human readable labels for the states. Not used in computation, only to keep track.
70+ time_orderings : list of int
71+ Which time orderings to use in the simulation.
72+ Default is all for a three interaction process: ``[1,2,3,4,5,6]``.
73+ recorded_indices : list of int
74+ Which density vector states to record through the simulation.
75+ Default is [7, 8], the output of a TRIVE Hamiltonian.
76+
77+ """
3478 if rho is None :
3579 self .rho = np .zeros (len (labels ), dtype = np .complex128 )
3680 self .rho [0 ] = 1.
@@ -44,6 +88,7 @@ def __init__(self, rho=None, tau=None, mu=None,
4488 else :
4589 self .tau = tau
4690
91+ #TODO: Think about dictionaries or some other way of labeling Mu values
4792 if mu is None :
4893 self .mu = np .array ([1. , 1. ], dtype = np .complex128 )
4994 else :
@@ -55,7 +100,7 @@ def __init__(self, rho=None, tau=None, mu=None,
55100 w_2ag = 2 * w_ag - coupling
56101 w_gg = 0.
57102 w_aa = w_gg
58- self .omega = np .array ( [w_gg , - w_ag , w_ag , w_ag , w_2ag , w_aa , w_aa , w_ag , w_2aa ] )
103+ self .omega = np .array ([w_gg , - w_ag , w_ag , w_ag , w_2ag , w_aa , w_aa , w_ag , w_2aa ])
59104 else :
60105 self .omega = w_0
61106
@@ -71,23 +116,33 @@ def __init__(self, rho=None, tau=None, mu=None,
71116 self .Gamma = 1. / self .tau
72117
73118 def to_device (self , pointer ):
119+ """Transfer the Hamiltonian to a C struct in CUDA device memory.
120+
121+ Currently expects a pointer to an already allocated chunk of memory.
122+ """
74123 import pycuda .driver as cuda
124+ #TODO: Reorganize to allocate here and return the pointer, this is more friendly
125+ # Transfer the arrays which make up the hamiltonian
75126 rho = cuda .to_device (self .rho )
76127 mu = cuda .to_device (self .mu )
77128 omega = cuda .to_device (self .omega )
78129 Gamma = cuda .to_device (self .Gamma )
79130
131+ # Convert time orderings into a C boolean array of 1 and 0, offset by one
80132 tos = [1 if i in self .time_orderings else 0 for i in range (1 ,7 )]
81133
134+ # Transfer time orderings and recorded indices
82135 time_orderings = cuda .to_device (np .array (tos , dtype = np .int8 ))
83136 recorded_indices = cuda .to_device (np .array (self .recorded_indices , dtype = np .int32 ))
84137
138+ # Transfer metadata about the lengths of feilds
85139 cuda .memcpy_htod (pointer , np .array ([len (self .rho )], dtype = np .int32 ))
86140 cuda .memcpy_htod (int (pointer ) + 4 , np .array ([len (self .mu )], dtype = np .int32 ))
87141 #TODO: generalize nTimeOrderings
88142 cuda .memcpy_htod (int (pointer ) + 8 , np .array ([6 ], dtype = np .int32 ))
89143 cuda .memcpy_htod (int (pointer ) + 12 , np .array ([len (self .recorded_indices )], dtype = np .int32 ))
90144
145+ # Set pointers in the struct
91146 cuda .memcpy_htod (int (pointer ) + 16 , np .intp (int (rho )))
92147 cuda .memcpy_htod (int (pointer ) + 24 , np .intp (int (mu )))
93148 cuda .memcpy_htod (int (pointer ) + 32 , np .intp (int (omega )))
@@ -98,35 +153,56 @@ def to_device(self, pointer):
98153
99154
100155 def matrix (self , efields , time ):
156+ """Generate the time dependant Hamiltonian Coupling Matrix.
157+
158+ Parameters
159+ ----------
160+ efields : ndarray<Complex>
161+ Contains the time dependent electric fields.
162+ Shape (M x T) where M is number of electric fields, and T is number of timesteps.
163+ time : 1-D array <float64>
164+ The time step values
165+
166+ Returns
167+ -------
168+ ndarray <Complex>
169+ Shape T x N x N array with the full Hamiltonian at each time step.
170+ N is the number of states in the Density vector.
171+ """
172+ #TODO: Just put the body of this method in here, rather than calling _gen_matrix
101173 E1 ,E2 ,E3 = efields [0 :3 ]
102174 return self ._gen_matrix (E1 , E2 , E3 , time , self .omega )
103175
104176 def _gen_matrix (self , E1 , E2 , E3 , time , energies ):
105177 """
106178 creates the coupling array given the input e-fields values for a specific time, t
107- w1first selects whether w1 or w2p is the first interacting positive field
108179
109180 Currently neglecting pathways where w2 and w3 require different frequencies
110181 (all TRIVE space, or DOVE on diagonal)
111182
112183 Matrix formulated such that dephasing/relaxation is accounted for
113184 outside of the matrix
114185 """
186+ # Define transition energies
115187 wag = energies [1 ]
116188 w2aa = energies [- 1 ]
117189
190+ # Define magnetic susceptabilities
118191 mu_ag = self .mu [0 ]
119192 mu_2aa = self .mu [- 1 ]
120193
194+ # Define helpful variables
121195 A_1 = 0.5j * mu_ag * E1 * np .exp (- 1j * wag * time )
122196 A_2 = 0.5j * mu_ag * E2 * np .exp (1j * wag * time )
123197 A_2prime = 0.5j * mu_ag * E3 * np .exp (- 1j * wag * time )
124198 B_1 = 0.5j * mu_2aa * E1 * np .exp (- 1j * w2aa * time )
125199 B_2 = 0.5j * mu_2aa * E2 * np .exp (1j * w2aa * time )
126200 B_2prime = 0.5j * mu_2aa * E3 * np .exp (- 1j * w2aa * time )
127201
202+ # Initailze the full array of all hamiltonians to zero
128203 out = np .zeros ((len (time ), len (energies ), len (energies )), dtype = np .complex128 )
129204
205+ # Add appropriate array elements, according to the time orderings
130206 if 3 in self .time_orderings or 5 in self .time_orderings :
131207 out [:,1 ,0 ] = - A_2
132208 if 4 in self .time_orderings or 6 in self .time_orderings :
@@ -155,39 +231,64 @@ def _gen_matrix(self, E1, E2, E3, time, energies):
155231 out [:,7 ,6 ] = - 2 * A_1
156232 out [:,8 ,6 ] = B_1
157233
234+ # Add Gamma along the diagonal
158235 for i in range (len (self .Gamma )):
159236 out [:,i ,i ] = - 1 * self .Gamma [i ]
160237
161238 #NOTE: NISE multiplied outputs by the approriate mu in here
162239 # This mu factors out, remember to use it where needed later
163- # Removed for clarity and aligning with Equation S15 of Kohler2016
240+ # Removed for clarity and aligning with Equation S15 of Kohler2017
164241
165242 return out
166243
167244 cuda_matrix_source = """
245+ /**
246+ * Hamiltonian_matrix: Computes the Hamiltonian matrix for an indevidual time step.
247+ * NOTE: This differs from the Python implementation, which computes the full time
248+ * dependant hamiltonian, this only computes for a single time step (to conserve memory).
249+ *
250+ * Parameters
251+ * ----------
252+ * Hamiltonian ham: A struct which represents a hamiltonian,
253+ * containing orrays omega, mu, and Gamma
254+ * cmplx* efields: A pointer to an array containg the complex valued
255+ * electric fields to use for evaluation
256+ * double time: the current time step counter
257+ *
258+ * Output
259+ * -------
260+ * cmplx* out: an N x N matrix containing the transition probabilities
261+ *
262+ */
168263 __device__ void Hamiltonian_matrix(Hamiltonian ham, pycuda::complex<double>* efields,
169- double time, pycuda::complex<double>* out)
264+ double time, pycuda::complex<double>* out)
170265 {
266+ // Define state energies
171267 double wag = ham.omega[1];
172268 double w2aa = ham.omega[8];
173269
270+ // Define magnetic dipoles
271+ //TODO: don't assume one, generalize
174272 pycuda::complex<double> mu_ag = 1.;//ham.mu[0];
175273 pycuda::complex<double> mu_2aa = 1.;//ham.mu[1];
176274
275+ // Define the electric field values
177276 pycuda::complex<double> E1 = efields[0];
178277 pycuda::complex<double> E2 = efields[1];
179278 pycuda::complex<double> E3 = efields[2];
180-
181279
280+ // Define helpful variables
182281 pycuda::complex<double> A_1 = 0.5 * I * mu_ag * E1 * pycuda::exp(-1. * I * wag * time);
183282 pycuda::complex<double> A_2 = 0.5 * I * mu_ag * E2 * pycuda::exp(I * wag * time);
184283 pycuda::complex<double> A_2prime = 0.5 * I * mu_ag * E3 * pycuda::exp(-1. * I * wag * time);
185284 pycuda::complex<double> B_1 = 0.5 * I * mu_2aa * E1 * pycuda::exp(-1. * I * w2aa * time);
186285 pycuda::complex<double> B_2 = 0.5 * I * mu_2aa * E2 * pycuda::exp(I * w2aa * time);
187286 pycuda::complex<double> B_2prime = 0.5 * I * mu_2aa * E3 * pycuda::exp(-1. * I * w2aa * time);
188287
288+ //TODO: zero once, take this loop out of the inner most loop
189289 for (int i=0; i<ham.nStates * ham.nStates; i++) out[i] = pycuda::complex<double>();
190290
291+ // Fill in appropriate matrix elements
191292 if(ham.time_orderings[2] || ham.time_orderings[4])
192293 out[1*ham.nStates + 0] = -1. * A_2;
193294 if(ham.time_orderings[3] || ham.time_orderings[5])
@@ -222,6 +323,7 @@ def _gen_matrix(self, E1, E2, E3, time, energies):
222323 out[8*ham.nStates + 6] = B_1;
223324 }
224325
326+ // Put Gamma along the diagonal
225327 for(int i=0; i<ham.nStates; i++) out[i*ham.nStates + i] = -1. * ham.Gamma[i];
226328 }
227329"""
0 commit comments