GithubHelp home page GithubHelp logo

alexysong / inkstone Goto Github PK

View Code? Open in Web Editor NEW
38.0 6.0 14.0 308 KB

Efficient electromagnetic solver based on rigorous coupled-wave analysis for 3D and 2D multi-layered structures with in-plane periodicity, such as gratings, photonic-crystal slabs, metasurfaces, surface-emitting lasers, nano-antennas, and more.

License: GNU Affero General Public License v3.0

Python 100.00%
electromagnetics photonics nanostructures

inkstone's Introduction

logo

Inkstone simulates the electromagnetic properties of 3D and 2D multi-layered structures with in-plane periodicity, such as gratings, photonic-crystal slabs, metasurfaces, vertical-cavity or photonic-crystal surface-emitting lasers (VCSEL, PCSEL), (patterned) solar cells, nano-antennas, and more.

Internally, Inkstone implements rigorous coupled-wave analysis (RCWA), a. k. a. Fourier Modal Method (FMM).

Inkstone can calculate:

  • the reflection, transmission, and absorption of the structure
  • the total and by-order power fluxes of the propagating and the evanescent waves in each layer
  • electric and magnetic field amplitudes at any locations in the structure,
  • band-structures based on the determinant of the scattering matrix of the structure.

Features of Inkstone:

  • It supports efficient and flexible parameter-scanning. You can change part of your structure such as the shapes and sizes of some patterns, or some material parameters. Inkstone only recalculates the modified parts and produces the final results efficiently.
  • It allows both tensorial permittivities and tensorial permeabilities, such as in anisotropic, magneto-optical, or gyromagnetic materials.
  • It can calculate the determinant of the scattering matrix on the complex frequency plane.
  • Pre-defined shapes of patterns can be used, including rectangular, parallelogram, disk, ellipse, 1D, and polygons. Closed-form Fourier transforms and corrections for Gibbs phenomena are implemented.
  • It is fully 3D.
  • It is written in pure python, with heavy-lifting done in numpy and scipy.

Quick Start

Installation:

$ pip install inkstone

Or,

$ git clone git://github.com/alexysong/inkstone
$ pip install .

Usage

The examples folder contains various self-explaining examples to get you started.

Dependencies

  • python 3.6+
  • numpy
  • scipy

Units, conventions, and definitions

Unit system

We adopt a natural unit system, where vacuum permittivity, permeability, and light speed are $\varepsilon_0=\mu_0=c_0=1$.

Sign convention

Sign conventions in electromagnetic waves:

$$e^{i(kx-\omega t)}$$

where $k$ is the wavevector, $x$ is spatial location, $\omega$ is frequency, $t$ is time.

By this convention, a permittivity of $\varepsilon_r + i\varepsilon_i$ with $\varepsilon_i>0$ means material loss, and $\varepsilon_i<0$ means material gain.

Coordinates, incident angles, and polarizations

drawing

(Inkstone, Incident $\bm{k}$ on stacked periodic nano electromagnetic structures.)

$\theta$ is defined as the angle between the incident $\vec{k}$ and the normal to the $xy$ plane. $\phi$ is defined as the angle between the projection of $\vec{k}$ in plane and $\hat{x}$.

$s$ polarization is when $\vec{E}$ field of the incoming plane wave is in the $xy$ plane. $p$ polarization is orthogonal to it.

For $\theta=0$, $\phi=0$, $s$ is when $\vec{E}$ of incoming wave is along $\hat{y}$, $p$ is the orthogonal one.

In 2d simulations (1d grating), the space is assumed to be in $x$ and $z$. Here, $s$ is when $\vec{E}$ of the incoming wave is in and out of the solving 2d space. $p$ is again orthogonal to it.

What's new

  • Ver 0.3:

    Solved the convergence issue at Wood's anomaly. Now the calculation maintains the same convergence, stability, and speed near and at Wood's anomaly.

Citing

If you find Inkstone useful for your research, we would appreciate you citing our paper. For your convenience, you can use the following BibTex entry:

@article{song2018broadband,
  title={Broadband Control of Topological Nodes in Electromagnetic Fields},
  author={Song, Alex Y and Catrysse, Peter B and Fan, Shanhui},
  journal={Physical review letters},
  volume={120},
  number={19},
  pages={193903},
  year={2018},
  publisher={American Physical Society}
}

inkstone's People

Contributors

alexysong avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

inkstone's Issues

Unable to verify Fresnel equations

Thank you for your transparent and usable Python port of S4.

To verify that the code works correctly, I attempted to reproduce the Fresnel equations using a simple two layer model -- the first layer with n=1, and the second with n=1.5. I have been unable to get this to work in Inkstone, but I did get it to work with an equivalent code for Phoebe-P S4 . Attached are the codes I used for both Inkstone, fresnel_inkstone_te.py (which doesn't work); and S4, Fresnel_S4_TE.py (working).

In inkstone, when I use angle = np.linspace(0, 90, 91) , I get the error:
/inkstone/params.py:525: RuntimeWarning: Vacuum propagation constant 0 encountered. Possibly Wood's anomaly.
warn("Vacuum propagation constant 0 encountered. Possibly Wood's anomaly.", RuntimeWarning)

When I use angle = np.linspace(1, 90, 90) , I get the error:
Traceback (most recent call last):
File "fresnel_inkstone_te.py", line 71, in
glapf, glapb = s.GetPowerFlux('gla')
File "/inkstone/simulator.py", line 1204, in GetPowerFlux
self.solve()
File "/inkstone/simulator.py", line 890, in solve
self._calc_sm()
File "/inkstone/simulator.py", line 704, in _calc_sm
s = next(ll[-1] for ll in self.csms if ll[-1][1] == n_layers-2)
StopIteration

If between the "air" air and "gla" glass layers, I add an intermediate layer:
s.AddLayer(name='gla-int', thickness=1, material_background='glass')

and still keep
angle = np.linspace(1, 90, 90)
then I get the error

/.local/lib/python3.9/site-packages/inkstone/layer.py:545: RuntimeWarning: divide by zero encountered in divide
vh = -1j * p @ v / w[:, None, :]
/.local/lib/python3.9/site-packages/inkstone/layer.py:545: RuntimeWarning: invalid value encountered in divide
vh = -1j * p @ v / w[:, None, :]
Traceback (most recent call last):
File "/inkstone/Fresnel_Inkstone/fresnel_inkstone_te.py", line 72, in
glapf, glapb = s.GetPowerFlux('gla')
File "/.local/lib/python3.9/site-packages/inkstone/simulator.py", line 1204, in GetPowerFlux
self.solve()
File "/.local/lib/python3.9/site-packages/inkstone/simulator.py", line 890, in solve
self._calc_sm()
File "/.local/lib/python3.9/site-packages/inkstone/simulator.py", line 682, in _calc_sm
ll[ilm].solve()
File "/.local/lib/python3.9/site-packages/inkstone/layer.py", line 702, in solve
self._calc_im()
File "/.local/lib/python3.9/site-packages/inkstone/layer.py", line 652, in _calc_im
al0, bl0 = im(self.phil, self.psil, self.pr.phi0, self.pr.psi0, self._phil_is_idt)
File "/.local/lib/python3.9/site-packages/inkstone/im.py", line 36, in im
term2 = sla.solve(psi1, psi2)
File "/.local/lib/python3.9/site-packages/scipy/linalg/_basic.py", line 140, in solve
a1 = atleast_2d(_asarray_validated(a, check_finite=check_finite))
File "/.local/lib/python3.9/site-packages/scipy/_lib/_util.py", line 287, in _asarray_validated
a = toarray(a)
File "/.local/lib/python3.9/site-packages/numpy/lib/function_base.py", line 627, in asarray_chkfinite
raise ValueError(
ValueError: array must not contain infs or NaNs

Adding Multiple Patterns To Layers Sometimes Removes Prior Patterns From Simulation

I found that adding multiple patterns to a layer would, inconsistently, remove prior ones. They are not literally removed, as they are still listed within the Layer object, but they no longer impact the simulation. It also appears to depend on the order in which the objects are added.

I've written a simple script that adds 4 rectangle patterns to a layer in a simulator. When UPPER RIGHT is added first, all four patterns appear in ReconstructLayer. If UPPER RIGHT is added last, it is the only one to appear. Nothing else is changed aside from moving that AddPatternRectangle from the beginning to the end.

SetUpperRightFirst

SetUpperRightLast

sim = Inkstone()
sim.SetNumG(151)
sim.SetLattice(((2,0),(0,2)))

sim.AddMaterial(name = "si", epsilon = 3.4699**2)
sim.AddLayer(name = 'input', thickness = 0, material_background = 'si')
sim.AddLayer(name = 'slab', thickness = 6, material_background = 'vacuum')
sim.AddLayer(name = 'output', thickness = 0, material_background = 'vacuum')

sim.SetExcitation(
	theta=0,
	phi=0,
	s_amplitude=1,
	p_amplitude=0
)

width = 0.3
offset = 0.5


# Add square in -x +y corner
sim.AddPatternRectangle(
	layer = 'slab',
	material = 'si',
	center = [-offset,offset],
	side_lengths = (width,width),
	angle = 0,
	pattern_name="UPPER LEFT"
)

# Add square in +x -y corner
sim.AddPatternRectangle(
	layer = 'slab',
	material = 'si',
	center = [offset,-offset],
	side_lengths = (width,width),
	angle = 0,
	pattern_name="LOWER RIGHT"
)

# Add square in -x -y corner
sim.AddPatternRectangle(
	layer = 'slab',
	material = 'si',
	center = [-offset,-offset],
	side_lengths = (width,width),
	angle = 0,
	pattern_name="LOWER LEFT"
)

# Add square in +x +y corner
# Adding this rectangle first 'fixes' the issue.
sim.AddPatternRectangle(
	layer = 'slab',
	material = 'si',
	center = [offset,offset],
	side_lengths = (width,width),
	angle = 0,
	pattern_name="UPPER RIGHT"
)

print(sim.layers["slab"].patterns)

xx, yy, eps, mu = sim.ReconstructLayer('slab',100,100)
plt.pcolormesh(xx,yy,np.real(eps[:,:,0,0]))
plt.show()

Questions

Hi thanks for the inkstone package.

I'd like to ask about questions other than issues.

  1. What is the num_g parameter? Why the higher this value the smaller z distance I can calculate?
    for num_g=5
    image
    for num_g=50
    image

  2. And I set up a basic grating and use normal incidence (s.SetExcitation(theta=0., phi=0., s_amplitude=1, p_amplitude=0)) , is the field in x-z plane something I can see in real experiment using just laser and grating, nothing else? I did real experiment using laser to shine on this grating and I got diffracted plane waves into different directions, but here in simulation in the x-z plane E-field I see beams propagating parallel forward.
    simulation
    image
    experiment
    image

Thank you very much

Non-vacuum incidence medium

Hi,

I have been working to integrate Inkstone into RayFlare, an open-source package developed in our group at UNSW for simulating solar cells. I previously developed an interface with S4, which I have benchmarked extensively against e.g. the Fresnel equations and the transfer matrix method (for planar layers). So far, integrating Inkstone has been relatively straightforward because many of the methods are the same as S4, and for 'normal' cases (incidence medium = air/vacuum, n = 1), I am getting the same results with S4 and Inkstone. However, I cannot obtain reasonable results (i.e. in agreement with the Fresnel equations/TMM/S4) when the incidence medium has n > 1 (still non-absorbing). I attach two plots, for the following two stacks:

STACK 1:

n = 1 (incidence medium)


n = 4.12 + 0.33 i (50 nm)


n = 4.13 + 0.03 i (transmission medium)

STACK 2:

n = 1.52 (incidence medium)


n = 4.12 + 0.33 i (50 nm)


n = 4.13 + 0.03 i (transmission medium)

image image

Calculated for 0 (normal) to 89.9 degree angle of incidence from the incidence medium (see note about incident polarisation below). The results for stack 1 are in excellent agreement at all angles (lines for TMM and Inkstone are on top of each other), while the results for stack 2 are not. I note that the result is correct at normal incidence, but not at other incidence angles, and I saw a comment in the code which implies perhaps this feature is simply not implemented yet: "todo: if incident not vacuum, then the incident wave shouldn't be Fourier order (which in general is not eigen)". Could you comment on whether there is currently a way to correct the result obtained for off-normal incidence, or if this is something that will be implemented in future?

Re: definition of incident polarisation, these seem to be conflicting between my definition in the TMM solver (which is the same as in S4) and that in Inkstone, but switching the s and p amplitudes gives the same results (for stack 1); these results are for Inkstone p-polarized incident light.

I have also attached the script which makes the plots, which requires the RayFlare package to work (pip install rayflare).
inkstone_inc_issue.txt

IndexError when calling "ReconstructLayer"

Hi,

I'm trying to visualize the epsilon profile of the patterned layer named "slab" in the example file "phc_slab_circ_hole_spectrum.py", using ReconstructLayer (as defined on line 309 of simulator.py).

I'm not entirely sure about the correct usage of ReconstructLayer but I'm just doing:
s.ReconstructLayer('slab', 100, 100) or s.ReconstructLayer('slab') (since nx and ny both seem to default to 101). In both cases, I get the error:

Traceback (most recent call last):
  File "phc_slab_circ_hole_spectrum.py", line 32, in <module>
    s.ReconstructLayer('slab')
  File "/home/sachin/miniconda3/lib/python3.7/site-packages/inkstone/simulator.py", line 337, in ReconstructLayer
    result = self.layers[name].reconstruct(nx, ny)
  File "/home/sachin/miniconda3/lib/python3.7/site-packages/inkstone/layer.py", line 395, in reconstruct
    for em in [fft.ifftshift(self.epsi_fs, axes=(0, 1)), fft.ifftshift(self.epsi_inv_fs, axes=(0, 1)), fft.ifftshift(self.mu_fs, axes=(0, 1)), fft.ifftshift(self.mu_inv_fs, axes=(0, 1))]]
  File "<__array_function__ internals>", line 6, in ifftshift
  File "/home/sachin/miniconda3/lib/python3.7/site-packages/numpy/fft/helper.py", line 121, in ifftshift
    shift = [-(x.shape[ax] // 2) for ax in axes]
  File "/home/sachin/miniconda3/lib/python3.7/site-packages/numpy/fft/helper.py", line 121, in <listcomp>
    shift = [-(x.shape[ax] // 2) for ax in axes]
IndexError: tuple index out of range

Could you please help me with this?

Thanks!

Transmitted Phase for S-Pol has 90deg shift in 0.3.3

Hi,
I've run into a strange issue when updating from inkstone 0.2.18 to 0.3.3. I'm using the attributes (self.ao,self.bi) to calculate the transmitted phase difference for TE- and TM-waves incident on a 1D grating. I added a modified version of _calc_bi_ao(self) to do this:

def Get_bi_ao(sim_object):
#get the reflected amplitude bi and transmitted amplitude ao
sim_object.solve()
ai_v = sim_object.pr.ai.reshape((2 * sim_object.pr.num_g, 1))
bo_v = sim_object.pr.bo.reshape((2 * sim_object.pr.num_g, 1))
sm11 = sim_object.sm[0]
sm12 = sim_object.sm[1]
sm21 = sim_object.sm[2]
sm22 = sim_object.sm[3]
bi = sm11 @ ai_v + sm12 @ bo_v
ao = sm21 @ ai_v + sm22 @ bo_v
#ravel 2d array (just one column) to 1d array
bi = bi.ravel()
ao = ao.ravel()
return bi,ao

With np.angle(ao[0]) I then get the 0th-order phase for p-polarization and with np.angle(ao[num_g]) for s-pol. This doesn't give the desired results in 0.3.3 and Ive noticed that for the elements of self.ao corresponding to s-pol [index num_g to 2*num_g-1] the real and imaginary parts are exchanged which results in a phase shift of 90° for s-pol. The values corresponding to p-pol are still exactly the same. Is this phase shift of +90° for s-pol desired? I can simply subtract the difference but I don't unterstand where this change came from.

Output of Get_bi_ao() in version 0.2.18:
[ 1.17456367-0.12200208j -0.06660174+1.19661283j]
Output of Get_bi_ao() in version 0.3.3:
[ 1.17456367-0.12200208j 1.19661283+0.06660174j]

Attached Im posting my script that reproduces this issue.

grating_1D_phase.txt

Best regards,
Tim

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.