Skip to content

Commit 8a5e69f

Browse files
committed
Comments and docstrings for default hamiltonian
1 parent c0a17ef commit 8a5e69f

1 file changed

Lines changed: 107 additions & 5 deletions

File tree

WrightSim/hamiltonian/default.py

Lines changed: 107 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)