Hi all,

I am trying to write a basic module to allow for simple 3D shapes in MEEP (for now, polyhedra). I am most interested in rectangular pyramids. I have two main questions:

 * first, is this an appropriate way to go about writing pyramid
   structures? All I did was make a class for the Pyramid (in my actual
   code, it inherits a Shape class), along with a material. Then I
   convert a list of these objects with MEEP's GeometricObject to a
   material_func for my Simulation object. I'm running into performance
   problems, especially for subpixel averaging. Is there a better way
   to go about this? It would be nice to just make a more general
   GeometricObject (say, a polyhedron defined by a set of planes) but I
   don't know how to do this.
 * I have subpixel averaging disabled in my code by using
   eps_average=False. I thought this would fix my problem, but it does
   not. Even with this extra line if I run the code it still gives
   "subpixel-averaging is 97.7767% done, 0.0910229 s remaining" etc.
   Why is it still doing this, and how should I get around this?


I scraped the rest of the code and left a bare-bones example in the .py file attached. The only output is some 2D cross-sectional plots of the pyramid in question. Output seems to be right but it takes a long time (and this is just a minimal example -- more realistically I'll have more shapes and a much higher resolution).

Best regards,
Philip
"""
:author JPHaupt
minimal example

the actual code in use is in a module
"""

# %% imports 
import meep as mp 
from meep import Vector3 as V # lazy shorthand
from meep.materials import cSi 
from matplotlib import pyplot as plt 

# %% function/class definitions 
# these are actually in a separate module
class Plane(object):
    """
    a plane parametrised by a normal vector and a point in the plane, useful for
    polyhedron shapes.
    """
    def __init__(self, normal, point):
        self.n = normal
        self.p = point
    
    def sameSide(self, point):
        v = point - self.p 
        proj = v.cdot(self.n)
        return (proj > 0)

class Pyramid(object):
    def __init__(self, apex, b1, b2, b3, b4, material=mp.Medium()):
        """
        initialises a rectangular pyramid object with points on the pyramid 
        given by vectors b1,b2,b3,b4 and the apex vector
        
        geometric object for a pyramid with some medium and consisting of points
        b1,b2,b3,b4 (on the rectangular base) and apex (the apex). Supports 
        oblique rectangular pyramids as well 
        
        :param Shape: [description]
        :type Shape: [type]
        :param apex: the apex of the pyramid
        :type apex: mp.Vector3
        :param b1: one of the vertices for the rectangular base of the pyramid
        :type b1: mp.Vector3
        :param b2: one of the vertices for the rectangular base of the pyramid
        :type b2: mp.Vector3
        :param b3: one of the vertices for the rectangular base of the pyramid
        :type b3: mp.Vector3
        :param b4: one of the vertices for the rectangular base of the pyramid
        :type b4: mp.Vector3

        .. note:: currently, we require that the points on the base of the 
                pyramid be given counterclockwise relative to outside (i.e. side
                **without** the apex). 
        """
        self.material = material 
        # TODO implement such that we don't need to add them counterclockwise
        self.planes = []
        # make base normal (pointing outside), partner with arbitrary point 
        base_normal = (b2-b1).cross(b3-b1)
        self.planes.append(Plane(base_normal, b1))

        # make triangular sides' normals, partner with (apex) point
        n1 = (apex-b1).cross(b2-b1)
        # n1 /= n1.norm()
        self.planes.append(Plane(n1, apex))
        n2 = (apex-b2).cross(b3-b2)
        # n2 /= n2.norm()
        self.planes.append(Plane(n2, apex))
        n3 = (apex-b3).cross(b4-b3)
        # n3 /= n3.norm()
        self.planes.append(Plane(n3, apex))
        n4 = (apex-b4).cross(b1-b4)
        # n4 /= n4.norm()
        self.planes.append(Plane(n4, apex))

        # do the rest, i.e. material
        # super(Pyramid, self).__init__(**kwargs)

    # override the contains function 
    def contains(self, point):
        """
        checks to see if the point is in the pyramid or not

        this is achieved by taking the projection of the vector connecting a 
        point on a given plane to the point in question onto the normal vector 
        of that same plane. If all of these projections are nonpositive, then 
        the point is inside the pyramid. If any one of them is positive, then it
        is outside the pyramid.
        
        :param point: a point to be checked if inside or outside the pyramid
        :type point: mp.Vector3
        """
        for plane in self.planes:
            if plane.sameSide(point):
                return False 
        return True

def geom2med(geometries, default_material=mp.Medium(epsilon=1.0)):
    """
    takes a mixed list of mp.GeometricObject and Shape (defined in this module)
    and returns a function of mp.Vector3 that returns the medium at the point
    
    :param geometries: list of geometric objects/shapes
    :type geometries: [mp.GeometricObject or Shape]
    :default_material: the material to be chosen if point not in any of the 
                       objects. Default is air (epsilon=1.0)
    :type default_material: mp.Medium

    .. note:: objects at the end of the list are checked first (i.e. if two 
            objects contain the point, then it is considered to be in the last 
            one).
    """
    return lambda v: _geom2med_helper(geometries, v, default_material)

def _geom2med_helper(geometries, point, default_material):
    """
    Function that geom2med wraps around 
    """
    for geom in reversed(geometries):
        if isinstance(geom, mp.GeometricObject):
            if mp.is_point_in_object(point, geom):
                return geom.material
        # elif isinstance(geom, shapes.Shape):
        elif isinstance(geom, Pyramid):
            if geom.contains(point):
                return geom.material 
    return default_material

# %% actual program

dpml = 0.5 # PML thickness 

k = mp.Vector3(0,0,0) # makes PBC
pml_layers = [mp.PML(dpml, direction=mp.Z, side=mp.ALL)]

# domain size
sx = 5
sy = 5
sz = 5

L = 2.5

resolution = 30
# recall: **must** put in anticlockwise order!
b1 = V(L/2, L/2, 0)
b2 = V(-L/2, L/2, 0)
b3 = V(-L/2, -L/2, 0)
b4 = V(L/2, -L/2, 0)
apex = V(0,0,-L/3)
tmp_pyr = Pyramid(apex, b1, b2, b3, b4, material=cSi)

# wavelengths 
# 400-1100 nm => 0.4-1.1 um
wvl_min = 0.3 # minimum wavelength
wvl_max = 1.2 # maximum wavelength

freq_min = 1/wvl_max
freq_max = 1/wvl_min
freq_centre = (freq_max+freq_min)/2
freq_width = freq_max-freq_min

tmp_source = [mp.Source(mp.GaussianSource(frequency=freq_centre, width=freq_width),
                        component=mp.Ex, 
                        center=mp.Vector3(0, 0, 0.5*sz-dpml-0.25),
                        # size=mp.Vector3(sx-2*pml_width,sy-2*pml_width,0))]
                        size=mp.Vector3(sx,sy,0))]

sim = mp.Simulation(cell_size=V(sx,sy,sz),
                    boundary_layers=pml_layers,
                    resolution=resolution,
                    k_point=k,
                    sources=tmp_source,
                    material_function=geom2med([tmp_pyr]),
                    eps_averaging=False, 
                    extra_materials=[cSi]) 

# visualise
num = 4
print('num=',num)
plt.figure()
sim.plot2D(output_plane=mp.Volume(center=mp.Vector3(0,0,0), size=mp.Vector3(sx,sy,0)))
plt.savefig('tmp_xy_' + str(num) + '.png')
plt.figure()
sim.plot2D(output_plane=mp.Volume(center=mp.Vector3(0,0,0), size=mp.Vector3(sx,0,sz)))
plt.savefig('tmp_xz_' + str(num) + '.png')
plt.figure()
sim.plot2D(output_plane=mp.Volume(center=mp.Vector3(0,0,0), size=mp.Vector3(0,sy,sz)))
plt.savefig('tmp_yz_' + str(num) + '.png')
_______________________________________________
meep-discuss mailing list
meep-discuss@ab-initio.mit.edu
http://ab-initio.mit.edu/cgi-bin/mailman/listinfo/meep-discuss

Reply via email to