NanoComp / meep

free finite-difference time-domain (FDTD) software for electromagnetic simulations
GNU General Public License v2.0
1.22k stars 621 forks source link

clarify mp.get_GDSII_prisms vs mp.GDSII_vol #1908

Open cephLpod opened 2 years ago

cephLpod commented 2 years ago

Hello, I'm having trouble with some Prism objects imported from a GDSII file. A rectangular Block object from the same file seems to work, as so some rectangular Prism objects, but not a more complex (heart-shaped) Prism with 40 vertices.

The imported Prism objects do not mix well with non-imported (coded) Prism or Block objects. When combined with a coded Prism or Block object in a geometry, the imported Prism's vertices and dielectric constant appear correctly in Meep's 'Initializing structure... ' output section, but are not in the epsilon output, and the simulation results show that those imported objects are definitely not there. If I copy and paste the vertices of the heart-shaped prism from the GDS file in Klayout and create a mp.Prism object, that will appear in epsilon.

Please let me know what other information I can provide about this issue.

prismTestMeep.txt vertices.txt

oskooi commented 2 years ago

When combined with a coded Prism or Block object in a geometry, the imported Prism's vertices and dielectric constant appear correctly in Meep's 'Initializing structure... ' output section, but are not in the epsilon output, and the simulation results show that those imported objects are definitely not there.

If the Prism objects' metadata appears as part of the runtime output then rest assured it is part of the simulation. I think the problem here is that you are combining two different Prism objects in the output: one is a meep.metal with ε=-∞ and the other is a lossless dielectric with ε=12. The lossless dielectric object may be obscured by the colorscale of the image due to the presence of the metal. A simple test, for visualization purposes only, would be to just replace meep.metal with e.g. mp.Medium(epsilon=2.0) and check whether the output changes.

(Note that, for debugging purposes, it is probably more convenient to use the plot2D function rather than output_epsilon because the former unlike the latter does not require the structure object to be initialized (which can be slow simply because there may be a large number of voxels in the cell volume).)

cephLpod commented 2 years ago

A simple test, for visualization purposes only, would be to just replace meep.metal with e.g. mp.Medium(epsilon=2.0) and check whether the output changes. Yep, I tried that early on, with the same result. Now I am using a simple Threshold filter in ParaView to distinguish between the epsilon levels.

Here's a simpler case with the same symptoms:

This code works as expected: ` import numpy from future import division import meep as mp import math

zsize = 0.061 zcenter = 0.0305 zpcb_bot = 0.010 zpcb_top = 0.011 zpcb_cent = 0.0105

gdsfile = '/home/cel/Documents/Geometries/antennaTestTiny00.gds'

cell = mp.Vector3(0.100, 0.100, zsize) geometry_center = mp.Vector3(0.055, 0.055, zcenter)

view layers:

print(mp.GDSII_layers(gdsfile))

lbot = mp.GDSII_vol(gdsfile, 3, zpcb_bot - 0.0005, zpcb_bot)
ltop = mp.GDSII_vol(gdsfile, 4, zpcb_top, zpcb_top+ 0.0005) ltop2 = mp.GDSII_vol(gdsfile, 5, zpcb_top, zpcb_top+ 0.0005)

lbotv = mp.Block(material=mp.metal, size=lbot.size, center=lbot.center) ltopv = mp.Block(material=mp.metal, size=ltop.size, center=ltop.center) ltop2v = mp.Block(material=mp.metal, size=ltop2.size, center=ltop2.center) pcbcenter = mp.Vector3(0.05, 0.05, zpcb_cent) pcbsize = mp.Vector3(0.100, 0.100, 0.001) pcb = mp.Block(material=mp.Medium(epsilon=12), center=pcbcenter, size=pcbsize)

geo = [pcb, lbotv, ltopv, ltop2v]

src = [mp.Source(mp.ContinuousSource(frequency=8.3), component=mp.Ey, center=mp.Vector3(0.050, 0.005, zpcb_top), size = mp.Vector3(0, 0, 0), amplitude = 1000)]

sim = mp.Simulation(resolution = 4000, cell_size = mp.Vector3(0.100, 0.100, zsize), geometry_center = mp.Vector3(0.050, 0.050, zcenter), boundary_layers = [mp.PML(0.005)], sources = src, geometry = geo) sim.run(mp.at_beginning(mp.output_epsilon), mp.to_appended("Eedge", mp.at_every(0.01, mp.output_efield)), until=0.001)

`

Meep output:

Initializing structure... time for choose_chunkdivision = 0.00160003 s Working in 3D dimensions. Computational cell is 0.1 x 0.1 x 0.061 with resolution 4000 block, center = (0.05,0.05,0.0105) size (0.1,0.1,0.001) axes (1,0,0), (0,1,0), (0,0,1) dielectric constant epsilon diagonal = (12,12,12) block, center = (0.05,0.05,0.00975) size (0.1,0.1,0.0005) axes (1,0,0), (0,1,0), (0,0,1) dielectric constant epsilon diagonal = (-1e+20,-1e+20,-1e+20) block, center = (0.05,0.0725,0.01125) size (0.03,0.025,0.0005) axes (1,0,0), (0,1,0), (0,0,1) dielectric constant epsilon diagonal = (-1e+20,-1e+20,-1e+20) block, center = (0.05,0.03,0.01125) size (0.002,0.06,0.0005) axes (1,0,0), (0,1,0), (0,0,1) dielectric constant epsilon diagonal = (-1e+20,-1e+20,-1e+20) subpixel-averaging is 18.2434% done, 17.9279 s remaining ...

GSDII file, epsilon output, and simulation result (Ez between metal layers) are attached. meepGDSIIvolumeInput.pdf

cephLpod commented 2 years ago

With a code change to import Prism objects from the GDSII file:

`

view layers:

print(mp.GDSII_layers(gdsfile))

bottomstuff = mp.get_GDSII_prisms(mp.metal, gdsfile , 3, zpcb_bot - 0.0005, zpcb_bot) topstuff = mp.get_GDSII_prisms(mp.metal, gdsfile, 4, zpcb_top, zpcb_top + 0.0005) top2stuff = mp.get_GDSII_prisms(mp.metal, gdsfile, 5, zpcb_top, zpcb_top + 0.0005) geo = bottomstuff + topstuff + top2stuff

pcbcenter = mp.Vector3(0.05, 0.05, zpcb_cent) pcbsize = mp.Vector3(0.100, 0.100, 0.001) pcb = mp.Block(material=mp.Medium(epsilon=12), center=pcbcenter, size=pcbsize) geo = [pcb] + geo`

...

Hmm, this seems to be working as expected now. I think maybe I was combining the two types of objects incorrectly.

geo = [pcb] + geo

seems to be the correct way.

I'll try the more complex GDSII Prism import and report back later. Thanks!

cephLpod commented 2 years ago

I'm seeing the same issue with the heart-shaped Prism import. Code: `import matplotlib.pyplot as plt import numpy from future import division import meep as mp import math import h5py

zsize = 0.061 zcenter = 0.0305 zpcb_bot = 0.010 zpcb_top = 0.011 zpcb_cent = 0.0105

gdsfile = '/home/cel/Documents/Geometries/HeartPCBantenna3.gds' groundfile = '/home/cel/Documents/Geometries/antennaTestTiny00.gds'

cell = mp.Vector3(0.100, 0.100, zsize)

view layers:

print(mp.GDSII_layers(gdsfile)) print(mp.GDSII_layers(groundfile))

pcbcenter = mp.Vector3(0.05, 0.05, zpcb_cent) pcbsize = mp.Vector3(0.100, 0.100, 0.001) pcbBlock = mp.Block(material=mp.Medium(epsilon=12), center=pcbcenter, size=pcbsize)

topstuff = mp.get_GDSII_prisms(mp.metal, gdsfile, 2, zpcb_top, zpcb_top + 0.0005) pcb = mp.get_GDSII_prisms(mp.Medium(epsilon=12), groundfile , 3, zpcb_bot, zpcb_top)

geo = pcb + topstuff # no errors, pcb appears in epsilon, but topstuff does not appear in epsilon

geo = topstuff # no errors, but topstuff does not appear in epsilon

geo = heartprism # no errors, heartprism does appear in epsilon (heartprism is a mp.Prism() copy-paste with topstuff vertices)

geo = pcb + heartprism # immediate error: "TypeError: meep.geom.Vector3() argument after * must be an iterable, not float"

geo = [pcb, heartprism] # error on sim.rum(): "AttributeError: 'list' object has no attribute 'material' "

geo = pcb # no errors, and pcb (a square) appears in epsilon

geo = [pcbBlock] + topstuff # no errors, but topstuff does not appear in epsilon

src = [mp.Source(mp.ContinuousSource(frequency=8.3), component=mp.Ey, center=mp.Vector3(0, -0.045, zpcb_top), size = mp.Vector3(0, 0, 0), amplitude = 1000)]

sim = mp.Simulation(resolution = 4000, cell_size = mp.Vector3(0.100, 0.100, zsize), boundary_layers = [mp.PML(0.005)], sources = src, geometry = geo) sim.run(mp.at_beginning(mp.output_epsilon), mp.to_appended("Eedge", mp.at_every(0.01, mp.output_efield)), until=0.001) `

Same effect, all 40 vertices of the imported Prism are displayed, but aren't seen in epsilon. I'm pretty confused now.

meepGDSIIheartPrismInput.pdf

smartalecH commented 2 years ago

The function mp.get_GDSII_prisms returns a list of prisms, not one big prism object itself. That's why you have to concatenate the list objects (using the + operator).

The objects don't show in this example because you specified their z coordinates such that they never cross the origin.

If you use plot2D out of the box, it plots the plane through the origin.

Might want to read through the docs to learn how to plot arbitrary cross sections (or just temporarily change the z coordinates of your gds prisms)

smartalecH commented 2 years ago

Also I would rescale your units. Your frequency is really high, but all your shapes are really small (at least in thickness). It's a bit hard to parse (I'm not a fan of counting zeros after decimal points for every attribute).

cephLpod commented 2 years ago

Thanks for your replies, and this was exactly the piece of information that I needed:

The function mp.get_GDSII_prisms returns a list of prisms, not one big prism object itself.

(I'm actually using output_epsilon and viewing with a Threshold filter in ParaView, not plot 2D. And have read through the docs many times in my 10+ years as a Meep user. :) There's always something new to learn.)

Also, thank you all for your work on the Python interface; it's a great thing to have!

cephLpod commented 2 years ago

Is it possible to morph this issue into this?:

Clarify the difference in functionality of mp.get_GDSII_prisms and mp.GDSII_vol in the documentation/GDSII Import tutorial.

Thanks again.

stevengj commented 2 years ago

@cephLpod, feel free to submit a PR to clarify the documentation.