Driven dynamics with TLS¶
Here, we demonstrate the socket-free TLS workflow using the maxwelllink.LaserDrivenSimulation electromagnetic solver. By resonantly coupling one cosine driving field to a two-level system (TLS), we aim to monitor the driven population dynamics of the TLS.
1. Defining Molecule¶
We first create a Molecule instance using the non-socket mode, i.e., we directly initialize the TLS within the Molecule class:
[1]:
import numpy as np
import maxwelllink as mxl
frequency_au = 1.0
mu12 = 1
molecule = mxl.Molecule(
driver="tls",
driver_kwargs={
"omega": frequency_au,
"mu12": mu12,
"orientation": 2,
"pe_initial": 0e-3,
}
)
[Init Molecule] Operating in non-socket mode, using driver: tls
2. Defining the driven field¶
Then, we create a LaserDrivenSimulation instance which defines the parameters for a custom driven field. The pre-defined molecule is also attached to this class for coupled light-matter simulations.
[2]:
from maxwelllink.tools import cosine_drive
dt_au = 1e-1
total_steps = 2e4
# you are encouraged to try different field parameters
omega_au_field = frequency_au * 1.0
amplitude_au = 1e-2
sim = mxl.LaserDrivenSimulation(
molecules=[molecule],
coupling_axis="z",
drive=cosine_drive(omega_au=omega_au_field, amplitude_au=amplitude_au),
dt_au=dt_au,
record_history=True,
)
sim.run(steps=total_steps)
init TLSModel with dt = 0.100000 a.u., molecule ID = 0
[LaserDriven] Completed 1000/20000.0 [5.0%] steps, time/step: 7.49e-05 seconds, remaining time: 1.42 seconds.
[LaserDriven] Completed 2000/20000.0 [10.0%] steps, time/step: 6.84e-05 seconds, remaining time: 1.29 seconds.
[LaserDriven] Completed 3000/20000.0 [15.0%] steps, time/step: 6.60e-05 seconds, remaining time: 1.19 seconds.
[LaserDriven] Completed 4000/20000.0 [20.0%] steps, time/step: 8.24e-05 seconds, remaining time: 1.17 seconds.
[LaserDriven] Completed 5000/20000.0 [25.0%] steps, time/step: 8.32e-05 seconds, remaining time: 1.12 seconds.
[LaserDriven] Completed 6000/20000.0 [30.0%] steps, time/step: 6.35e-05 seconds, remaining time: 1.02 seconds.
[LaserDriven] Completed 7000/20000.0 [35.0%] steps, time/step: 5.97e-05 seconds, remaining time: 0.92 seconds.
[LaserDriven] Completed 8000/20000.0 [40.0%] steps, time/step: 6.63e-05 seconds, remaining time: 0.85 seconds.
[LaserDriven] Completed 9000/20000.0 [45.0%] steps, time/step: 5.97e-05 seconds, remaining time: 0.76 seconds.
[LaserDriven] Completed 10000/20000.0 [50.0%] steps, time/step: 5.81e-05 seconds, remaining time: 0.68 seconds.
[LaserDriven] Completed 11000/20000.0 [55.0%] steps, time/step: 5.82e-05 seconds, remaining time: 0.61 seconds.
[LaserDriven] Completed 12000/20000.0 [60.0%] steps, time/step: 6.72e-05 seconds, remaining time: 0.54 seconds.
[LaserDriven] Completed 13000/20000.0 [65.0%] steps, time/step: 7.82e-05 seconds, remaining time: 0.48 seconds.
[LaserDriven] Completed 14000/20000.0 [70.0%] steps, time/step: 6.13e-05 seconds, remaining time: 0.41 seconds.
[LaserDriven] Completed 15000/20000.0 [75.0%] steps, time/step: 5.84e-05 seconds, remaining time: 0.34 seconds.
[LaserDriven] Completed 16000/20000.0 [80.0%] steps, time/step: 7.86e-05 seconds, remaining time: 0.27 seconds.
[LaserDriven] Completed 17000/20000.0 [85.0%] steps, time/step: 8.04e-05 seconds, remaining time: 0.21 seconds.
[LaserDriven] Completed 18000/20000.0 [90.0%] steps, time/step: 5.81e-05 seconds, remaining time: 0.14 seconds.
[LaserDriven] Completed 19000/20000.0 [95.0%] steps, time/step: 7.13e-05 seconds, remaining time: 0.07 seconds.
[LaserDriven] Completed 20000/20000.0 [100.0%] steps, time/step: 8.34e-05 seconds, remaining time: 0.00 seconds.
3. Retrieve simulation observables¶
After the simulation, we can retrieve the TLS trajectory from molecule.extra.
[ ]:
# users can also use molecule.additional_data_history to access the time-resolved data recorded during the simulation,
# population = np.array([entry["Pe"] for entry in molecule.additional_data_history])
# tls_time_au = np.array([entry["time_au"] for entry in molecule.additional_data_history])
# but here we demonstrate the use of molecule.extra which is more convenient for post-processing and plotting.
population = molecule.extra["Pe"]
tls_time_au = molecule.extra["time_au"]
print(
f"Collected {population.size} TLS samples."
)
Collected 20000 TLS samples.
4. Inspect time-domain Rabi oscillations¶
According to the analytical rotating wave approximation, under near resonance excitation, the excited-state population \(P_{\rm e}(t)\) obeys:
where \(\Omega_0 \equiv \mu_{12} E_0\) and \(\Omega_{\rm R} = \sqrt{\Delta^2 + \Omega_0^2}\), with \(\Delta = \omega_0 - \omega_{\rm {ph}}\) denoting the light-matter detuning.
[4]:
import matplotlib.pyplot as plt
# analytical solution under RWA
Omega_0 = mu12 * amplitude_au
Omega_R = np.sqrt(Omega_0**2 + (omega_au_field - frequency_au) ** 2)
population_analytical = Omega_0**2 / Omega_R**2 * np.sin(Omega_R * tls_time_au / 2) ** 2
plt.figure(figsize=(7, 4))
plt.plot(tls_time_au, population, label="TLS excited-state population")
plt.plot(tls_time_au, population_analytical, label="Analytical population", linestyle="--")
plt.xlabel("time (a.u.)")
plt.ylabel("Pe")
plt.title("TLS population dynamics")
plt.legend()
plt.tight_layout()
plt.show()