NLSE

NLSE: A Python package to solve the nonlinear Schrödinger equation - Published in JOSS (2024)

https://github.com/quantum-optics-lkb/nlse

Science Score: 100.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
    Found CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
    Found 4 DOI reference(s) in README and JOSS metadata
  • Academic publication links
    Links to: joss.theoj.org
  • Committers with academic emails
    1 of 8 committers (12.5%) from academic institutions
  • Institutional organization owner
  • JOSS paper metadata
    Published in Journal of Open Source Software

Keywords

simulation

Scientific Fields

Mathematics Computer Science - 31% confidence
Last synced: 4 months ago · JSON representation ·

Repository

Non linear Schrödinger equation solver

Basic Info
Statistics
  • Stars: 21
  • Watchers: 2
  • Forks: 4
  • Open Issues: 1
  • Releases: 5
Topics
simulation
Created over 3 years ago · Last pushed 6 months ago
Metadata Files
Readme License Citation

README.md

DOI

NLSE

A package to easily simulate all sorts of non linear Schrödinger equations. It uses a split-step spectral scheme to solve the equations.

Installation

First clone the repository:

bash git clone https://github.com/Quantum-Optics-LKB/NLSE.git cd NLSE

Then pip install the package:

bash pip install .

Basic usage

After installing NLSE, you can simply import one of the solvers and instantiate your problem as follows:

```python N = 2048 # number of points in solver n2 = -1.6e-9 # nonlinear index in m^2/W waist = 2.23e-3 # initial beam waist in m waist2 = 70e-6 # potential beam waist in m window = 4*waist # total computational window size in m power = 1.05 # input optical power in W Isat = 10e4 # saturation intensity in W/m^2 L = 10e-3 # Length of the medium in m alpha = 20 # linear losses coefficient in m^-1 backend = "GPU" # whether to run on the GPU or the CPU

simu = NLSE( alpha, power, window, n2, None, L, NX=N, NY=N, Isat=Isat, backend=backend )

Define input field and potential

E0 = np.exp(-(simu.XX2 + simu.YY2) / waist2) V = -1e-4 * np.exp(-(simu.XX2 + simu.YY2) / waist22) simu.outfield(E_0, L, verbose=True, plot=True, precision="single") ```

Requirements

Supported platforms

This code has been tested on the three main platforms: Linux, MacOs and Windows. The requirements are in the requirements.txt at the root of the repo.

GPU computing

For optimal speed, this code uses your GPU (graphics card). For this, you need specific libraries. For Nvidia cards, you need a CUDA install. For AMD cards, you need a ROCm install. Of course, you need to update your graphics driver to take full advantage of these. In any case we use CuPy for the Python interface to these libraries.

The cupy dependency is not included in setup.py in order to not break installation on platforms that do not support it !

PyFFTW

If the code does not find Cupy, it will fall back to a CPU based implementation that uses the CPU : PyFFTW. To make the best out of your computer, this library is multithreaded. By default it will use all available threads. If this is not what you want, you can disable this by setting the variable pyfftw.config.NUM_THREADS to a number of your choosing.

On Mac, you first need to install FFTW which can be done by simply using Homebrew brew install fftw. Some users reported this didn't work for them, in this case the next best bet is to build it from source following these instructions: FFTW - Installation and customization.

WARNING : The default flag passed to FFTW for planning is FFTW_PATIENT which means that the first run of the code can take a long time. This information is cached so subsequent runs just have to load the plans, removing this computation time.

PyVkFFT

We found out that PyVkFFT was outperforming CuFFT for our application so the GPU implementation uses this library for optimal performance.

Other than this, the code relies on these libraries :

  • numba : for best CPU performance on Intel CPU's, with icc_rt
  • pickle
  • numpy
  • scipy
  • matplotlib

Tests

Tests are included to check functionalities and benchmark performance. You can run all tests by using pytest at the root of the repo. It will test both CPU and GPU backends (if available). This can take some time !

The benchmarks can be run using examples/benchmarks.py and compare a "naive" numpy implementation of the main solver loop to our solver. We also compare for the example of the vortex precession presented in FourierGPE.jl to our solver. On a Nvidia RTX4090 GPU and Ryzen 7950X CPU, we test our solver to the following results: benchmarks

How does it work ?

Physical situation

The code offers to solve a typical non linear Schrödinger / Gross-Pitaevskii equation of the type : $$i\partial_{t}\psi = -\frac{1}{2}\nabla^2\psi+V\psi+g|\psi|^2\psi$$

In this particular instance, we solve in the formalism of the propagation of a pulse of light in a non linear medium. Within the paraxial approximation, the propagation equation for the field $E$ in V/m solved is:

$$ i\partial{z}E = -\frac{1}{2k0}\nabla{\perp}^2 E + \frac{D0}{2}\partial^2t E -\frac{k0}{2}\delta n(r) E - n2 \frac{k0}{2n}c\epsilon_0|E|^2E $$

Here, the constants are defined as followed :

In all generality, the interaction term can be non-local i.e $n2=n2(\mathbf{r})$. This means usually that the response will be described as a convolution by some non-local kernel:

$$ n2(\mathbf{r})|E|^2(\mathbf{r})=n2\int_{\mathbb{R}^2}\mathrm{d}\mathbf{r}' K(\mathbf{r}-\mathbf{r}')|E|^2(\mathbf{r}'), $$

where $K(\mathbf{r})$ is the non-local kernel, typically the Green function of some diffusion equation.

Please note that all of the code works with the "God given" units i.e SI units !

The NLSE class

The NLSE class aims at providing a minimal yet functional toolbox to solve non-linear Schrödinger type equation in optics / atomic physics settings such as the propagation of light in a Kerr medium or solving the Gross Pitaevskii equation for the evolution of cold gases.

The propagation equation is:

$$ i\partial{z}E = -\frac{1}{2k0}\nabla{\perp}^2 E + -\frac{k0}{2}\delta n(r) E - n2 \frac{k0}{2n}c\epsilon_0|E|^2E $$

Initialization

The physical parameters listed above are defined at the instantiation of the NLSE class (__init__ function). The backend (GPU or CPU) is tested when the library is imported, but you can then dynamically switch it when instantiating a NLSE class by setting the self.backend attribute to "GPU" or "CPU".

Broadcasting

Since numpy / cupy allow for natural broadcasting of arrays of compatible size, one can leverage this in order to run parallel realizations. For instance, if we wish to propagate various initial state with the same physical parameters, we simply have to initialize a tensor of fields of dimensions (N_real, Ny, Nx) where N_real is the number of initial states we wish to propagate.

Similarly, one can broadcast over the physical parameters by setting some parameters to be tensors as well. If we wish for instance to study the effect of the variation of $n2$, one can set the n2 attribute to be a `(Nreal, 1, 1)tensor. The field should then be initialized to a(Nreal, Ny, Nx)` tensor of identical fields and each slice over the first dimension will represent the same field propagated with different parameters. Finally, one can combine broadcasting over several parameters at the same time: if we wish to do a grid search over $n2$ and $\alpha$, one can instantiate n2 to be a (N_n2, 1, 1, 1) array, alpha to be a (1, N_alpha, 1, 1) and the field a (N_n2, N_alpha, Ny, Nx) array.

The take-home message is that the array shape should be compliant with numpy broadcasting rules.

WARNING : For now it is only available on the GPU backend !

Numerical precision

In order to reach the best performance, the numerical precision is hardcoded as a constant variable at the top of nlse.py. When importing the solvers like NLSE, the data types of the input arrays must match the data type given as input of out_field.

Callbacks

The out_field functions support callbacks with the following signature callback(self, A, z, i) where self is the class instance, A is the field, z is the current position and i the main loop index. For example if you want to print the number of steps every 100 steps, this is the callback you could write :

python def callback(nlse, A, z, i): n = int(z/nlse.delta_z) if n % 100 == 0: print(n)

Notice that since the class instance is passed to the callback, you have access to all of the classes attributes. Be mindful however that since the callback is running in the main solver loop, this function should not be called too often in order to not slow down the execution too much. You can find several generic callbacks in the callbacks sublibrary.

Propagation

The out_field method is the main function of the code that propagates the field for an arbitrary distance from an initial state E_in from z=0 (assumed to be the begining of the non linear medium) up to a specified distance z. This function simply works by iterating the spectral solver scheme i.e :

  • (If precision is 'double' apply real space terms)
  • Fourier transforming the field
  • Applying the laplacian operator (multiplication by a constant matrix)
  • Inverse Fourier transforming the field
  • Applying all real space terms (potential, losses and interactions)

The precision argument allows to switch between applicating the nonlinear terms in a single multiplication ("single"), or applying a "half" nonlinear term before and after computing the effect of losses, potential and interactions ("double"). The numerical error is $\mathcal{O}(\delta z)$ in the first case and $\mathcal{O}(\delta z^3)$ in the second case at the expense of two additional FFT's and another matrix multiplication (essentially doubling the runtime).\ The propagation step $\delta z$ is chosen to be the minimum between 1e-5 the Rayleigh length of the beam or 2.5e-2 $z{NL}=\frac{1}{k0 n2 I}$, but this can be hand tuned to reach desired speed or precision by setting the `deltaz` attribute.

Inheritance

In order to minimize duplication, all classes inherit from the main NLSE class according to the following graph: inheritance

The NLSE_1d class

NLSE_1d is a 1D specialization of NLSE for performance. It supports all of the features of the main NLSE class.

The propagation equation is:

$$ i\partial{z}E = -\frac{1}{2k0}\partial^2x E + -\frac{k0}{2}\delta n(r) E - n2 \frac{k0}{2n}c\epsilon_0|E|^2E $$

The NLSE_3d class

NLSE_3d solves the full paraxial propagation equation.

WARNING: Since this solves a 3D+1 equation, this is computationally very intensive ! The space complexity scales as $N^3$ if $N$ is the field array size.

The propagation equation is:

$$ i\partial{z}E = -\frac{1}{2k0}\nabla{\perp}^2 E + \frac{D0}{2}\partial^2t E -\frac{k0}{2}\delta n(r) E - n2 \frac{k0}{2n}c\epsilon_0|E|^2E $$

The CNLSE class

The CNLSE class is a coupled non-linear Schrödinger equation allowing to solve the following equation:

$$ \begin{split} i\frac{\partial\psif}{\partial z} &= -\frac{1}{2kf}\nabla^2\psif -\frac{1}{2}n2^f kf c\epsilon0|\psif|^2\psif + kf n2^{fd}c\epsilon0|\psid|^2\psif-\frac{i\alphaf}{2}\psif + \frac{\Omega}{2} \psid \ i\frac{\partial\psid}{\partial z} &= -\frac{1}{2kd}\nabla^2\psid -\frac{1}{2}n2^d kd c\epsilon0|\psid|^2\psid + kd n2^{fd}c\epsilon0|\psif|^2\psid-\frac{i\alphad}{2}\psid + \frac{\Omega}{2} \psif \end{split} $$

This allows to describe the back reaction of the fluid onto the defect as well as two components scenarii. In order to "turn on" different terms, it suffices to set the parameters value to something other than None. When None, the solver does not apply the corresponding evolution term for optimal performance.

The CNLSE_1d class

Similarly to NLSE_1d, the CNLSE_1d is a 1D specialization of CNLSE class.

The propagation equation is:

$$ \begin{split} i\frac{\partial\psif}{\partial z} &= -\frac{1}{2kf}\partial^2x\psif -\frac{1}{2}n2^f kf c\epsilon0|\psif|^2\psif + kf n2^{fd}c\epsilon0|\psid|^2\psif-\frac{i\alphaf}{2}\psif + \frac{\Omega}{2} \psid \ i\frac{\partial\psid}{\partial z} &= -\frac{1}{2kd}\partial^2x\psid -\frac{1}{2}n2^d kd c\epsilon0|\psid|^2\psid + kd n2^{fd}c\epsilon0|\psif|^2\psid-\frac{i\alphad}{2}\psid + \frac{\Omega}{2} \psif \end{split} $$

The GPE class

The GPE class allows to solve the 2D Gross-Pitaevskii equation describing the temporal evolution of a Bosonic field:

$$ i\partial_{t}\psi = -\frac{1}{2}\nabla^2\psi+V\psi+g|\psi|^2\psi. $$

It follows exactly the same conventions as the other classes a part from the fact that since it describes atoms, the units are the "atomic" units (masses in kg, times in s).

The DDGPE class

The DDGPE class allows to solve the temporal evolution of two coupled fields in a driven-dissipative context.

It was designed to study problems like the evolution of exciton polaritons in microcavities.

The equation solved in this context is the following:

$$ \begin{split} i\hbar \partialt\psiX(\textbf{r}, t)&= (\frac{\hbar^2}{2mX}\nabla^2 + VX(\textbf{r}) + \hbar gX|\psiX(\textbf{r}, t)|^2 - i\hbar\frac{\GammaX}{2})\psiX(\textbf{r}, t)+ \hbar\OmegaR\psiC(\textbf{r}, t) \ i\hbar \partialt \psiC(\textbf{r}, t)&= (\frac{\hbar^2}{2mC}\nabla^2 + VC(\textbf{r}) - i\hbar\frac{\GammaC}{2})\psiC(\textbf{r}, t) + \hbar\OmegaR\psiX(\textbf{r}, t) + \hbar F_p(\textbf{r},t) \end{split} $$

where

  • $\psi_X$ is the exciton field
  • $\psi_C$ is the cavity field
  • $V_X$ is the exciton potential
  • $V_C$ is the cavity potential
  • $g_X$ is the exciton interaction energy
  • $\Gamma_X$ is the exciton losses coefficient
  • $\Gamma_C$ is the cavity losses coefficient
  • $\Omega_R$ is the Rabi coupling between excitons and photons
  • $F_p$ is the pumping field impinging on the cavity

Contributing and issues

If you wish to contribute, do not hesitate to create a PR or email me (tangui.aladjidi[at]lkb.upmc.fr). If you encounter problems with this software, you can create an issue directly on this repository.

Owner

  • Name: Quantum Optics and Quantum Fluids team - LKB
  • Login: Quantum-Optics-LKB
  • Kind: organization
  • Email: quantumfluids@lkb.upmc.fr
  • Location: France

Quentin Glorieux - Alberto Bramati: Quantum Optics and Quantum FLuids of Light group at Laboratoire Kastler Brossel in Sorbonne Universite, ENS Paris PSL.

JOSS Publication

NLSE: A Python package to solve the nonlinear Schrödinger equation
Published
July 23, 2024
Volume 9, Issue 99, Page 6607
Authors
Tangui Aladjidi ORCID
Laboratoire Kastler Brossel, Sorbonne University, CNRS, ENS-PSL University, Collège de France; 4 Place Jussieu, 75005 Paris, France
Clara Piekarski ORCID
Laboratoire Kastler Brossel, Sorbonne University, CNRS, ENS-PSL University, Collège de France; 4 Place Jussieu, 75005 Paris, France
Quentin Glorieux ORCID
Laboratoire Kastler Brossel, Sorbonne University, CNRS, ENS-PSL University, Collège de France; 4 Place Jussieu, 75005 Paris, France
Editor
Rocco Meli ORCID
Tags
NLSE Evolution Nonlinear optics Quantum fluids

Citation (CITATION.cff)

cff-version: "1.2.0"
authors:
- family-names: Aladjidi
  given-names: Tangui
  orcid: "https://orcid.org/0000-0002-3109-9723"
- family-names: Piekarski
  given-names: Clara
  orcid: "https://orcid.org/0000-0001-6871-6003"
- family-names: Glorieux
  given-names: Quentin
  orcid: "https://orcid.org/0000-0003-0903-0233"
contact:
- family-names: Aladjidi
  given-names: Tangui
  orcid: "https://orcid.org/0000-0002-3109-9723"
doi: 10.5281/zenodo.12795395
message: If you use this software, please cite our article in the
  Journal of Open Source Software.
preferred-citation:
  authors:
  - family-names: Aladjidi
    given-names: Tangui
    orcid: "https://orcid.org/0000-0002-3109-9723"
  - family-names: Piekarski
    given-names: Clara
    orcid: "https://orcid.org/0000-0001-6871-6003"
  - family-names: Glorieux
    given-names: Quentin
    orcid: "https://orcid.org/0000-0003-0903-0233"
  date-published: 2024-07-23
  doi: 10.21105/joss.06607
  issn: 2475-9066
  issue: 99
  journal: Journal of Open Source Software
  publisher:
    name: Open Journals
  start: 6607
  title: "NLSE: A Python package to solve the nonlinear Schrödinger
    equation"
  type: article
  url: "https://joss.theoj.org/papers/10.21105/joss.06607"
  volume: 9
title: "NLSE: A Python package to solve the nonlinear Schrödinger
  equation"

GitHub Events

Total
  • Watch event: 14
  • Issue comment event: 1
  • Push event: 3
  • Pull request event: 2
  • Fork event: 1
  • Create event: 1
Last Year
  • Watch event: 14
  • Issue comment event: 1
  • Push event: 3
  • Pull request event: 2
  • Fork event: 1
  • Create event: 1

Committers

Last synced: 5 months ago

All Time
  • Total Commits: 348
  • Total Committers: 8
  • Avg Commits per committer: 43.5
  • Development Distribution Score (DDS): 0.092
Past Year
  • Commits: 6
  • Committers: 2
  • Avg Commits per committer: 3.0
  • Development Distribution Score (DDS): 0.167
Top Committers
Name Email Commits
Tangui Aladjidi t****i@g****m 316
Quentin GLX q****x@g****m 15
Killian k****o@l****r 7
clpiekarski 5****i 4
ruggiamp@gmail.com r****p@g****m 3
Rocco Meli r****i@b****h 1
clpiekarski c****i@p****u 1
Quentin q****x@l****r 1
Committer Domains (Top 20 + Academic)

Issues and Pull Requests

Last synced: 4 months ago

All Time
  • Total issues: 2
  • Total pull requests: 8
  • Average time to close issues: 8 days
  • Average time to close pull requests: 3 days
  • Total issue authors: 1
  • Total pull request authors: 4
  • Average comments per issue: 1.5
  • Average comments per pull request: 0.25
  • Merged pull requests: 5
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 0
  • Pull requests: 2
  • Average time to close issues: N/A
  • Average time to close pull requests: 3 days
  • Issue authors: 0
  • Pull request authors: 2
  • Average comments per issue: 0
  • Average comments per pull request: 0.5
  • Merged pull requests: 1
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
  • Abinashbunty (2)
Pull Request Authors
  • taladjidi (9)
  • Abinashbunty (2)
  • RMeli (2)
  • clpiekarski (1)
Top Labels
Issue Labels
Pull Request Labels