from finesse.cymath.cmatrix cimport SubCCSView, SubCCSView2DArray
from finesse.cymath cimport complex_t
from finesse.cymath.complex cimport crotate, conj
from finesse.cymath.math cimport radians, cosh, sinh
from finesse.frequency cimport frequency_info_t
from finesse.simulations.simulation cimport BaseSimulation
from finesse.simulations.sparse.solver cimport SparseSolver
import numpy as np
cimport numpy as np
ctypedef (double*, double*, double*) ptr_tuple_3
cdef extern from "constants.h":
long double PI
double C_LIGHT
double DEG2RAD
double DB2R
[docs]cdef class SqueezerConnections:
def __cinit__(self, SparseSolver mtx):
cdef:
int Nfo = mtx.optical_frequencies.size
self.UPPER_P1o = SubCCSView2DArray(1, Nfo)
self.LOWER_P1o = SubCCSView2DArray(1, Nfo)
self.ptrs.UPPER_P1o = <PyObject***>self.UPPER_P1o.views
self.ptrs.LOWER_P1o = <PyObject***>self.LOWER_P1o.views
[docs]cdef class SqueezerNoiseSources:
def __cinit__(self, SparseSolver mtx):
cdef:
int Nfo = mtx.optical_frequencies.size
self.P1o = SubCCSView2DArray(Nfo, Nfo)
self.ptrs.P1o = <PyObject***>self.P1o.views
cdef class SqueezerValues(BaseCValues):
def __init__(self):
cdef ptr_tuple_3 ptr = (&self.db, &self.angle, &self.f)
cdef tuple params = ("db","angle","f")
self.setup(params, sizeof(ptr), <double**>&ptr)
cdef class SqueezerWorkspace(ConnectorWorkspace):
def __init__(self, object owner, BaseSimulation sim):
super().__init__(
owner,
sim,
None,
SqueezerConnections(sim.signal) if sim.signal else None,
values=SqueezerValues(),
noise_sources=SqueezerNoiseSources(sim.signal) if sim.signal else None
)
self.v = self.values
self.conns = self.signal.connections if sim.signal else None
self.ns = self.signal.noise_sources if sim.signal else None
self.qn_coeffs = np.zeros((sim.model_settings.num_HOMs, sim.model_settings.num_HOMs), dtype=np.complex128)
self.qn_coeffs_diag = np.zeros(sim.model_settings.num_HOMs, dtype=np.complex128)
self.hom_vector = np.zeros(sim.model_settings.num_HOMs, dtype=complex)
self.hom_vector[0] = 1 # TODO ddb - always HG00 for the squeezer for now
# fixed definitions for vector
self.c_p1_o.size = sim.model_settings.num_HOMs
self.c_p1_o.stride = 1
self.c_p1_o.ptr = &self.hom_vector[0]
squeezer_fill_qnoise = FillFuncWrapper.make_from_ptr(c_squeezer_fill_qnoise)
cdef object c_squeezer_fill_qnoise(ConnectorWorkspace cws) :
r"""
Fills the quantum noise right hand side (RHS) vector corresponding
to the squeezed-light source `squeezer`.
"""
cdef:
SqueezerWorkspace ws = cws
# Laser quantum noise injection
complex_t n = ws.sim.model_settings.UNIT_VACUUM / 2
complex_t phs = crotate(1, 2 * radians(ws.v.angle))
double r = ws.v.db * DB2R
complex_t qn
squeezer_noise_sources noises = ws.ns.ptrs
frequency_info_t *ifreq
frequency_info_t *ofreq
Py_ssize_t i
# TODO: Shouldn't this quantum noise be frequency-dependent, as for other noise sources?
ws.qn_coeffs_diag[:] = n
for i in range(ws.sim.signal.optical_frequencies.size):
ifreq = &(ws.sim.signal.optical_frequencies.frequency_info[i])
if ifreq.audio_carrier_index != ws.fsrc_car_idx:
(<SubCCSView>noises.P1o[ifreq.index][ifreq.index]).fill_za(n)
continue
for j in range(ws.sim.signal.optical_frequencies.size):
ofreq = &(ws.sim.signal.optical_frequencies.frequency_info[j])
if ofreq.audio_carrier_index != ws.fsrc_car_idx:
continue
# Reflections
if ws.sim.signal.optical_frequencies.frequency_info[i].audio_order > 0:
if i == j:
qn = n * cosh(2 * r)
else:
qn = n * sinh(2 * r) * phs
else:
if i == j:
qn = conj(n * cosh(2 * r))
else:
qn = conj(n * sinh(2 * r) * phs)
# We only want to squeeze the main mode of the interferometer, so just set the first
# element of the relevant matrix/diagonal
if i == j:
ws.qn_coeffs_diag[0] = qn
(<SubCCSView>noises.P1o[ifreq.index][ofreq.index]).fill_zd(ws.qn_coeffs_diag)
else:
ws.qn_coeffs[0][0] = qn
(<SubCCSView>noises.P1o[ifreq.index][ofreq.index]).fill_zm(ws.qn_coeffs)
squeezer_fill_rhs = FillFuncWrapper.make_from_ptr(c_squeezer_fill_rhs)
cdef object c_squeezer_fill_rhs(ConnectorWorkspace cws) :
cdef:
SqueezerWorkspace ws = <SqueezerWorkspace>cws
if not ws.sim.is_modal:
ws.sim.set_source_fast(
ws.node_id, ws.fsrc_car_idx, 0, 0, 0
)
else:
for i in range(ws.signal.nhoms):
ws.sim.set_source_fast(
ws.node_id, ws.fsrc_car_idx, i, 0, 0
)
squeezer_fill_signal = FillFuncWrapper.make_from_ptr(c_squeezer_fill_signal)
cdef object c_squeezer_fill_signal(ConnectorWorkspace cws) :
cdef:
SqueezerWorkspace ws = <SqueezerWorkspace>cws
squeezer_connections conns = <squeezer_connections>ws.conns.ptrs
complex_t factor = crotate(np.sqrt(2), radians(ws.v.angle))
frequency_info_t *f = NULL
# TODO ddb - these are all assuming a single electronic frequency here, hence first 0 index
f = &ws.sim.signal.optical_frequencies.frequency_info[ws.fcar_sig_sb_idx[0]]
if conns.UPPER_P1o[0][f.index]:
(<SubCCSView>conns.UPPER_P1o[0][f.index]).fill_negative_za_zv(factor, &ws.c_p1_o)
f = &ws.sim.signal.optical_frequencies.frequency_info[ws.fcar_sig_sb_idx[1]]
if conns.LOWER_P1o[0][f.index]:
(<SubCCSView>conns.LOWER_P1o[0][f.index]).fill_negative_za_zv(factor, &ws.c_p1_o)