-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
291 additions
and
155 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,7 @@ | |
""" | ||
|
||
__version__ = "0.0.4" | ||
__version__ = "0.1.0" | ||
|
||
# Test pytorch version | ||
import torch | ||
|
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
components | ||
========== | ||
|
||
Each Component is generally defined by several key attributes defining the | ||
behavior of the component in a network. | ||
|
||
* `num_ports`: The number of ports of the components. | ||
|
||
* `S`: The scattering matrix of the component. | ||
|
||
* `C`: The connection matrix for the component (usually all zero for base | ||
components) | ||
|
||
* `sources_at`: The location of the sources in the component (usually all zero | ||
for base components) | ||
|
||
* `detectors_at`: The location of the detectors in the component (usually all | ||
zero for base components) | ||
|
||
* `actions_at`: The location of the active nodes in the component (usually all | ||
zero for passive components) | ||
|
||
* `delays`: delays introduced by the nodes of the component. | ||
|
||
Defining your own Component comes down to subclassing `Component` and | ||
redefining the relevant setters `set_*` for these attributes. For example:: | ||
|
||
class Waveguide(pt.Component): | ||
num_ports = 2 | ||
def __init__(self, length=1e-5, neff=2.34, ng=3.40, name=None): | ||
super(Waveguide, self).__init__(self, name=name) | ||
self.neff = float(neff) | ||
self.wl0 = float(wl0) | ||
self.ng = float(ng) | ||
self.length = float(length) | ||
def set_delays(self, delays): | ||
delays[:] = self.ng * self.length / self.env.c | ||
def set_S(self, S): | ||
wls = torch.tensor(self.env.wl, dtype=torch.float64, device=self.device) | ||
phase = (2 * np.pi * neff * self.length / wls) % (2 * np.pi) | ||
S[0, :, 0, 1] = S[0, :, 1, 0] = torch.cos(phase).to(torch.float32) # real part | ||
S[1, :, 0, 1] = S[1, :, 1, 0] = torch.sin(phase).to(torch.float32) # imag part | ||
|
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
detectors | ||
========= | ||
|
||
lowpassdetector | ||
--------------- | ||
|
||
responsivity | ||
^^^^^^^^^^^^ | ||
|
||
The detected photo-current :math:`I_p~[C/s]` can be described in terms of the | ||
optical power :math:`P_o~[W]` through the quantum efficiency :math:`\eta` and | ||
the carrier frequency :math:`f=c/\lambda~[1/s]` of the light: | ||
|
||
.. math:: | ||
\begin{align*} | ||
I_p = \eta \frac{q}{h f}P_o = \eta \frac{q \lambda}{h c}P_o | ||
\end{align*} | ||
However, it's often more useful to describe it in terms of a single quantity | ||
:math:`r~[A/W]`: | ||
|
||
.. math:: | ||
\begin{align*} | ||
I_p &= rP_o, | ||
\end{align*} | ||
where we defined :math:`r~[A/W]` as the responsivity of the detector. At a | ||
wavelength of :math:`1550nm` and for a quantum efficiency of :math:`\eta=1`, we | ||
have: | ||
|
||
.. math:: | ||
\begin{align*} | ||
r &= \eta \frac{q \lambda}{h c} = 1.25 A/W, | ||
\end{align*} | ||
hence a default value of 1 for the responsivity is appropriate. | ||
|
||
|
||
photodetector | ||
------------- | ||
|
||
responsivity | ||
^^^^^^^^^^^^ | ||
|
||
As mentioned above, a responsivity of :math:`r=1 A/W` is a valid value, but almost | ||
always it will be lower. If the detected signal is not as noisy as expected, | ||
you probably used a too high responsivity. | ||
|
||
thermal noise | ||
^^^^^^^^^^^^^ | ||
On top of the detected current, thermal noise should be added. The thermal | ||
noise variance :math:`\sigma_t^2` [:math:`C^2/s^2`] is given by: | ||
|
||
.. math:: | ||
\begin{align*} | ||
\sigma_t^2 &= N_t^2 f_c, | ||
\end{align*} | ||
with :math:`f_c~[1/s]` the cutoff frequency of the signal and | ||
:math:`N_t~[C/\sqrt s]` the thermal noise spectral density. This represenation | ||
(which is the representation used in VPI for example) requires you to use a | ||
quantity with :math:`N_t~[C/\sqrt s]` with weird units. We therefore prefer | ||
another representation (see for example | ||
`wikipedia <https://en.wikipedia.org/wiki/Johnson%E2%80%93Nyquist_noise>`_): | ||
|
||
.. math:: | ||
\begin{align*} | ||
\sigma_t^2 = \frac{4kT}{R_L} f_c | ||
\end{align*} | ||
To find a good default value for the load resistance :math:`R_L`, we can equate | ||
this equation with the previous one using the default VPI value for | ||
:math:`N_t=10^{-11}C/\sqrt s`: | ||
|
||
.. math:: | ||
\begin{align} | ||
R_L = 166 \Omega | ||
\end{align} | ||
where we assumed for the room temparture :math:`T=300K`. | ||
|
||
shot noise | ||
^^^^^^^^^^ | ||
The shot noise variance :math:`\sigma_t^2` [:math:`C^2/s^2`] for a PIN | ||
photodetector is given by: | ||
|
||
.. math:: | ||
\begin{align*} | ||
\sigma_s^2 = 2 q (I_p + I_d)f_c | ||
\end{align*} | ||
with :math:`q~[C]` the elementary charge and :math:`I_d` the dark current of | ||
the photodetector. Notice that this power is dependent on the photocurrent | ||
:math:`I_p~[C/s]` itself. | ||
|
||
In some representations, :math:`\mu_p=\left< I_p \right>~[C/s]` is used | ||
instead. We choose not to take this average to have a more accurate | ||
reprentation. Moreover, low-pass filtering already takes a kind of moving | ||
average into account. | ||
|
||
total noise | ||
^^^^^^^^^^^ | ||
The total post-detection noise variance :math:`\sigma_p^2` [:math:`C^2/s^2`] now depends on | ||
the thermal noise variance :math:`\sigma_t^2` and the shot noise variance | ||
:math:`\sigma_s^2`: | ||
|
||
.. math:: | ||
\begin{align*} | ||
\sigma_p^2 &= \sigma_t^2 + \sigma_s^2 | ||
\end{align*} | ||
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
environment | ||
=========== | ||
|
||
The `Environment` class contains all the necessary parameters to initialize a | ||
network for a simulation. |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
networks | ||
======== | ||
|
||
The Network is the core of Photontorch. | ||
|
||
This is where everything comes together. The Network is a special kind of | ||
torch.nn.Module, where all subcomponents are automatically initialized and | ||
connected in the right way. | ||
|
||
reduction of S-matrix | ||
--------------------- | ||
|
||
Each component can be described in terms of it's S matrix. For such a | ||
component, we have that the output fields :math:`\bf x_{\rm out}` are connected to | ||
the input fields :math:`x_{\rm in}` through a scattering matrix: | ||
|
||
.. math:: | ||
x_{\rm out} = S \cdot x_{\rm in} | ||
For a network of components, the field vectors :math:`x` will just be stacked on top of each other, | ||
and the S-matrix will just be the block-diagonal matrix of the S-matrices of the | ||
individual components. However, to connect the output fields to each other, we need | ||
a connection matrix, which connects the output fields of the individual components | ||
to input fields of other components in the fields vector :math:`x`: | ||
|
||
.. math:: | ||
x_{\rm in} = C \cdot x_{\rm out} | ||
a simulation (without delays) can thus simply be described by: | ||
|
||
.. math:: | ||
x(t+1) = C\cdot S\cdot x(t) | ||
However, when delays come in the picture, the situation is a bit more complex. | ||
We then split the fields vector :math:`x` in a memory_containing part (mc) and a | ||
memory-less part (ml): | ||
|
||
.. math:: | ||
\begin{pmatrix}x^{\rm mc} \\x^{\rm ml} \end{pmatrix}(t+1) = | ||
\begin{pmatrix} C^{\rm mcmc} & C^{\rm mcml} \\ C^{\rm mlmc} & C^{\rm mlml} \end{pmatrix} | ||
\begin{pmatrix} S^{\rm mcmc} & S^{\rm mcml} \\ S^{\rm mlmc} & S^{\rm mlml} \end{pmatrix} | ||
\cdot\begin{pmatrix}x^{\rm mc} \\x^{\rm ml} \end{pmatrix}(t) | ||
Usually, we are only interested in the memory-containing nodes, as memory-less nodes | ||
should be connected together and act all at once. After some matrix algebra we arrive at | ||
|
||
.. math:: | ||
\begin{align} | ||
x^{\rm mc}(t+1) &= \left( C^{\rm mcmc} + C^{\rm mcml}\cdot S^{\rm mlml}\cdot | ||
\left(1-C^{\rm mlml}S^{\rm mlml}\right)^{-1} C^{\rm mlmc}\right)S^{\rm mcmc} x^{\rm mc}(t) \\ | ||
&= C^{\rm red} x^{\rm mc}(t), | ||
\end{align} | ||
Which defines the reduced connection matrix used in the simulations. | ||
|
||
complex matrix inverse | ||
---------------------- | ||
|
||
PyTorch still does not allow complex valued Tensors. Therefore, the above | ||
equation was completely rewritten with matrices containing the real and | ||
imaginary parts. This would be fairly straightforward if it were not for the | ||
matrix inverse in the reduced connection matrix: | ||
|
||
.. math:: | ||
\begin{align} | ||
P^{-1} = \left(1-C^{\rm mlml}S^{\rm mlml}\right)^{-1} | ||
\end{align} | ||
unfortunately for complex matrices :math:`P^{-1} \neq {\rm real}(P)^{-1} + i{\rm | ||
imag}(P)^{-1}`, the actual case is a bit more complicated. | ||
|
||
It is however, pretty clear from the equations that the :math:`{\rm real}(P)^{-1}` | ||
will always exist, and thus we can write for the real and imaginary part of | ||
:math:`P^{-1}`: | ||
|
||
|
||
.. math:: | ||
\begin{align} | ||
{\rm real}(P^{-1}) &= \left({\rm real}(P) + {\rm imag}(P)\cdot {\rm real}(P)^{-1} | ||
\cdot {\rm imag}(P)\right)^{-1}\\ | ||
{\rm real}(P^{-1}) &= -{\rm real}(P^{-1})\cdot {\rm imag}(P) \cdot {\rm real}(P)^{-1} | ||
\end{align} | ||
This equation is valid, even if :math:`{\rm imag}(P)^{-1}` does not exist. | ||
|
Oops, something went wrong.