Multimode Cavity¶
The maxwelllink.em_solvers.multimode_cavity module implements a Fabry-Pérot resonator described by an arbitrary
collection of damped harmonic oscillators (photon modes) coupled to a
spatially distributed molecular grid points, each representing a distinct molecular simulation cell.
See the following reference for the underlying theory:
Reference
TE Li. Mesoscale Molecular Simulations of Fabry–Pérot Vibrational Strong Coupling. J. Chem. Theory Comput. 20, 7016–7031 (2024).
Note
Under the dipole gauge, the total light-matter Hamiltonian is
Each photon mode (a classical harmonic oscillator) obeys the equation
and the effective electric field returned to the molecular drivers is
For parameters,
\(\omega_{k}\) denotes the angular frequency of the cavity mode \((k,\lambda)\),
\(\gamma_{k\lambda}\) is
damping_au,\(\varepsilon_{k\lambda}\) represents the light-matter coupling strength
coupling_strength,\(D_{k\lambda}(t)\) is the optional external drive,
\(\delta_{\mathrm{DSE}}\) is the dipole self-energy term (1 if included, 0 otherwise), and
\(\mathbf{f}_{k\lambda}(\mathbf{r})\) is the photonic mode function evaluated at the molecular grid point \(\mathbf{r}\).
The mode frequencies follow a planar Fabry-Pérot dispersion
\(\omega_{k} = \sqrt{\omega_{\rm c}^{2} + (l_{x}\Delta\omega_{x})^{2} + (l_{y}\Delta\omega_{y})^{2}}\)
controlled by frequency_au, delta_omega_x_au, delta_omega_y_au,
n_mode_x, and n_mode_y.
The dipole self-energy term (\(\delta_{\mathrm{DSE}} = 1\)) is included only when include_dse=True.
Requirements¶
No additional dependencies beyond the MaxwellLink Python stack are required.
Usage¶
The multimode solver is configured in two pieces:
FabryPerotCavitydescribing the cavity geometry (such as photonic mode functions), andMultiModeSimulationdescribing the time integrator and thermostatting.
The support of additional cavity geometries is planned for future versions.
Socket mode¶
import maxwelllink as mxl
hub = mxl.SocketHub(host="127.0.0.1", port=31415, timeout=10.0, latency=1e-5)
# 4 x 4 grid of molecules attached to the cavity, all in socket mode
molecules = [mxl.Molecule(hub=hub) for _ in range(16)]
cavity = mxl.FabryPerotCavity(
frequency_au=0.242,
coupling_strength=1e-4,
coupling_axis="y",
n_grid_x=4,
n_grid_y=4,
delta_omega_x_au=0.05,
delta_omega_y_au=0.05,
n_mode_x=4,
n_mode_y=4,
)
sim = mxl.MultiModeSimulation(
dt_au=0.5,
damping_au=0.0,
molecules=molecules,
cavity_geometry=cavity,
hub=hub,
include_dse=True,
)
sim.run(steps=4000, record_history=True, record_list=["all"])
Launch mxl_driver --model tls --port 31415 ... (or another driver) once for every molecule after running this script.
Non-socket mode¶
import maxwelllink as mxl
# 4 x 4 grid of embedded TLS molecules
molecules = [
mxl.Molecule(
driver="tls",
driver_kwargs=dict(
omega=0.242,
mu12=187.0,
orientation=2,
pe_initial=1e-4 if i == 0 else 0.0,
),
)
for i in range(16)
]
cavity = mxl.FabryPerotCavity(
frequency_au=0.242,
coupling_strength=1e-4,
coupling_axis="y",
n_grid_x=4,
n_grid_y=4,
delta_omega_x_au=0.05,
delta_omega_y_au=0.05,
n_mode_x=4,
n_mode_y=4,
)
sim = mxl.MultiModeSimulation(
dt_au=0.5,
damping_au=0.0,
molecules=molecules,
cavity_geometry=cavity,
include_dse=False,
)
sim.run(steps=4000, record_history=True, record_list=["all"])
NVT initialization and thermostat¶
The cavity-mode initial conditions and any per-step thermostat are now provided
as composable objects via the initializer and thermostat arguments.
maxwelllink.tools.harmonic_oscillator_helper ships
DummyInitializer /
DummyThermostat (the
defaults, NVE) and
MaxwellBoltzmannInitializer /
LangevinThermostat for
NVT runs.
from maxwelllink import (
MaxwellBoltzmannInitializer,
LangevinThermostat,
)
from maxwelllink.units import AU_TO_K
temperature_au = 300.0 / AU_TO_K
dt_au = 0.5
sim = mxl.MultiModeSimulation(
dt_au=dt_au,
damping_au=0.0,
molecules=molecules,
cavity_geometry=cavity,
include_dse=True,
initializer=MaxwellBoltzmannInitializer(temperature_au=temperature_au, random_seed=2026),
thermostat=LangevinThermostat(temperature_au=temperature_au, dt_au=dt_au, tau_au=4000.0, random_seed=2026),
)
sim.run(steps=4000, record_history=True, record_list=["all"])
Drop the thermostat argument (or keep the default DummyThermostat) to
get a thermal initial condition followed by NVE evolution.
K-parallel sources¶
For directional polariton wavepacket excitation, use
maxwelllink.tools.k_parallel_pulse() to build either a molecule-side
source grid list or a photon-side cavity-mode list.
For molecule-side excitation:
from maxwelllink.tools import gaussian_pulse, k_parallel_pulse
envelope = gaussian_pulse(
amplitude_au=1.0,
t0_au=0.0,
sigma_au=0.05 * steps * dt_au,
)
source = k_parallel_pulse(
cavity=cavity,
envelope=envelope,
omega_au=2580.0 * cm_to_au,
k_parallel_au=12.5 * cm_to_au,
direction="y",
target="molecule",
center=(0.5, 0.10),
size=(0.16, 0.20),
amplitude_au=1e-2,
)
sim = mxl.MultiModeSimulation(
dt_au=dt_au,
molecules=molecules,
cavity_geometry=cavity,
excited_grid_list=source.excited_grid_list,
molecule_pulse_drive=source,
molecule_pulse_axis="y",
)
For photon-side excitation, the same real-space source profile is projected onto cavity modes:
source = k_parallel_pulse(
cavity=cavity,
envelope=1.0,
omega_au=2580.0 * cm_to_au,
k_parallel_au=12.5 * cm_to_au,
direction="y",
target="photon",
projection_axis="y",
center=(0.5, 0.10),
size=(0.16, 0.20),
amplitude_au=1e-2,
)
sim = mxl.MultiModeSimulation(
dt_au=dt_au,
molecules=molecules,
cavity_geometry=cavity,
excited_mode_list=source.excited_mode_list,
photon_pulse_drive=source,
photon_pulse_axis="y",
)
directioncontrols the in-plane phase gradient;molecule_pulse_axisorphoton_pulse_axiscontrols field polarization.For photon-side sources,
projection_axisshould normally matchphoton_pulse_axis.envelopemay also be a scalar; for exampleenvelope=1.0gives a continuous cosine source with spatially dependent phases.
Cavity geometry parameters¶
These arguments are passed to
FabryPerotCavity and define
the photon mode dispersion together with the spatial grid of molecular sites.
Name |
Description |
|---|---|
|
Reference cavity angular frequency \(\omega_{\rm c}\) (a.u.) corresponding to the \(k_{\parallel}=0\) cavity mode. Required. |
|
Light-matter coupling strength \(\varepsilon\) for the fundamental cavity mode. Per-mode couplings are rescaled as
\(\varepsilon_{k\lambda} = \varepsilon\, \omega_{k\lambda}/\min_{k\lambda}\omega_{k\lambda}\).
Default: |
|
One or more dipole components to couple (case-insensitive union of
|
|
Explicit list of molecular grid coordinates along \(x\), in units of
the cavity length \(L_x\). Ignored if |
|
Explicit list of molecular grid coordinates along \(y\), in units of
the cavity length \(L_y\). Ignored if |
|
Number of uniformly spaced molecular sites along \(x\). Overrides
|
|
Number of uniformly spaced molecular sites along \(y\). Overrides
|
|
Number of times each \(x\) grid coordinate is repeated (useful for
stacking multiple molecules per spatial site). Default: |
|
Number of times each \(y\) grid coordinate is repeated. Default: |
|
Cavity frequency spacing \(\Delta\omega_{x}\) along \(x\) (a.u.) used in the planar dispersion relation. Required. |
|
Cavity frequency spacing \(\Delta\omega_{y}\) along \(y\) (a.u.). Required. |
|
Number of cavity modes along \(x\). Default: |
|
Number of cavity modes along \(y\). Default: |
|
Absorbing boundary condition cutoff applied to the molecular grid (in
fractional grid units). |
Simulation parameters¶
These arguments are passed to
MultiModeSimulation.
Name |
Description |
|---|---|
|
Integration time step in atomic units. Must be positive. |
|
Linear damping coefficient \(\kappa\) applied to all cavity-mode momenta (a.u.). |
|
Iterable of |
|
Optional |
|
|
|
Initial cavity-mode coordinates with shape |
|
Initial cavity-mode momenta with shape |
|
Initial total molecular dipole vector with shape |
|
Initial time derivative of the total molecular dipole vector with shape
|
|
Object that draws the initial cavity-mode coordinates and momenta.
Default:
|
|
Object whose |
|
When |
|
When |
|
When |
|
Gauge choice for the light-matter coupling. Currently only |
|
List of cavity-mode indices that receive the photon-side pulse defined
by |
|
Constant float or callable |
|
One or more axes ( |
|
List of molecular grid indices that receive the molecule-side pulse
defined by |
|
Constant float or callable |
|
One or more axes ( |
Returned data¶
Calling simu`=:class:`~maxwelllink.em_solvers.multimode_cavity.MultiModeSimulation
with record_history=True populates the following attributes:
simu.time_history– time stamps in atomic units.simu.qc_history– cavity-mode coordinates with shape(n_record, n_mode, 3).simu.pc_history– cavity-mode momenta with shape(n_record, n_mode, 3).simu.photon_drive_history– photon-side pulse values applied toexcited_mode_list.simu.molecule_pulse_history– molecule-side pulse values applied toexcited_grid_list.simu.energy_history– total energy of the cavity and coupled molecules.simu.effective_efield_history– effective electric field at each molecular grid point with shape(n_record, n_grid, 3).simu.molecule_response_history– \(\partial_t\boldsymbol{\mu}\) summed alongcoupling_axisfor every grid point.simu.molecule_dipole_history– total molecular dipole at every grid point with shape(n_record, n_grid, 3).
Each Molecule keeps
additional_data_history, which
records driver data (e.g., TLS populations, energies, timestamps).
Notes¶
Provide either
stepsoruntiltoMultiModeSimulation.run(), not both.Socket-mode molecules must all bind to the same
SocketHub; the simulation waits until every driver acknowledges initialization.The number of molecules supplied to
moleculesshould equaln_grid(=n_mode_x*n_mode_y) defined on theFabryPerotCavity. Each molecule is bound to a distinct grid point through itsmolecule_id.Using
abc_cutoff > 0is recommended for large planar grids to suppress spurious reflections of the photon field at the boundaries; the cutoff is expressed as a fraction of the cavity length.For NVT runs, supply both an
MaxwellBoltzmannInitializer(thermal initial condition) and aLangevinThermostat(per-step kick) toinitializerandthermostatrespectively. Pass the samerandom_seedto both for reproducible trajectories. UseMaxwellBoltzmannInitializeralone for a thermal IC followed by NVE dynamics.