#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import numpy
import bfdtd
from GWL.GWL_parser import *
from bfdtd.bfdtd_parser import *
import GWL.tilted_grating
[docs]class Woodpile(object):
'''
This class allows you to create BFDTD and GWL files for a woodpile crystal.
.. todo:: Is there a better way to define properties? (shorter code)
.. todo:: Same as with meshing: First carefully think through the GWL/BFDTD woodpile system, then implement. Should be as flexible as possible (offsets, shifts, etc), while remaining simple to use.
'''
@property
def Nlayers_Z(self):
"""Number of layers in the Z direction"""
return self._Nlayers_Z
@Nlayers_Z.setter
def Nlayers_Z(self, value):
try:
self._Nlayers_Z = int(value)
except ValueError:
print("Could not convert value to an integer.")
raise
@property
def NRodsPerLayer_X(self):
"""Number of rods in a layer in the X direction"""
return self._NRodsPerLayer_X
@NRodsPerLayer_X.setter
def NRodsPerLayer_X(self, value):
try:
self._NRodsPerLayer_X = int(value)
except ValueError:
print("Could not convert value to an integer.")
raise
@property
def NRodsPerLayer_Y(self):
"""Number of rods in a layer in the Y direction"""
return self._NRodsPerLayer_Y
@NRodsPerLayer_Y.setter
def NRodsPerLayer_Y(self, value):
try:
self._NRodsPerLayer_Y = int(value)
except ValueError:
print("Could not convert value to an integer.")
raise
def __init__(self):
self._Nlayers_Z = 12
self._NRodsPerLayer_X = 17
self._NRodsPerLayer_Y = 17
# TODO: create get/set functions for rod width/height + interRodDistance/interLayerDistance
# NOTE: Keep rod width/height + interRodDistance/interLayerDistance, etc separate for more flexibility or not?
self.rod_width = 0.100
self.rod_height = 0.200
self.LineNumber_X = 1
self.LineDistance_X = 0.050
self.LineNumber_Y = 1
self.LineDistance_Y = 0.050
self.LineNumber_Z = 1
self.LineDistance_Z = 0.100
# TODO: Pass function/class name directly in order the facilitate extendability. Might require arguments to be class attributes so the function/class can access them.
#self.rod_type = 'line'
self.rod_type = 'block'
#self.rod_type = 'cylinder'
self.interLayerDistance = 0.212
self.interRodDistance = 0.600
# TODO: Get rid of this, since there are already GWL translating functions?
self.offset = numpy.array([0,0,0]) # general offset of the whole structure, added to all points
# self.bottomLayerYPeriodic = True: ->first written layer will be periodic in the Y direction
# else: first written layer will be periodic in the X direction
self.bottomLayerYPeriodic = False
self.BottomToTop = False # True=write from bottom to top, False=write from top to bottom
self.isSymmetrical = True # If True, the layers will alternate between N and N+1 rods per layer to enable central symmetry, otherwise all layers will have the same number of rods.
# These variables determine whether the first written layer of type X-periodic or Y-periodic is shifted or not.
# The following layers will be changed accordingly. Essentially, it allows vertical shifting of the layers.
# This allows you to create "ADCB" instead of "ABCD" woodpiles.
# (affected by BottomToTop! (TODO: fix this))
# TODO: Allow creation of "ABAB" woodpiles... Or even more complex?
self.shiftInitialLayerType_X = False
self.shiftInitialLayerType_Y = False
# TODO: find better name? redundant with X/Yoffset and X/Ymin/max, no?
#self.additionalRodLength = 0
# TODO: create setSize functions?
self.Xmin = -5.5
self.Xmax = 5.5
self.Ymin = -5.5
self.Ymax = 5.5
# additional separate offsets for X and Y layers...
self.Xoffset = 0
self.Yoffset = 0
[docs] def setRodType():
return
[docs] def setRodWidth():
''' set rod width (WARNING: will set LineNumber and LineDistance accordingly) '''
return
[docs] def setRodHeight():
''' set rod height (WARNING: will set LineNumber and LineDistance accordingly) '''
return
[docs] def setLineNumberAndDistance_XY():
''' set LineNumber and LineDistance (WARNING: will set rod width accordingly) '''
return
[docs] def setLineNumberAndDistance_Z():
''' set LineNumber and LineDistance (WARNING: will set rod height accordingly) '''
return
[docs] def setVoxelSize3():
return
[docs] def setOverlap3():
#self.overlap =
return
[docs] def adaptXYMinMax(self):
# TODO: should take into account the width of logs, etc
if self.isSymmetrical:
#LX = self.NRodsPerLayer_X*self.interRodDistance + 2*self.additionalRodLength
#LY = self.NRodsPerLayer_Y*self.interRodDistance + 2*self.additionalRodLength
LX = self.NRodsPerLayer_X*self.interRodDistance
LY = self.NRodsPerLayer_Y*self.interRodDistance
else:
#LX = self.NRodsPerLayer_X*self.interRodDistance - 0.5*self.interRodDistance + 2*self.additionalRodLength
#LY = self.NRodsPerLayer_Y*self.interRodDistance - 0.5*self.interRodDistance + 2*self.additionalRodLength
LX = self.NRodsPerLayer_X*self.interRodDistance - 0.5*self.interRodDistance
LY = self.NRodsPerLayer_Y*self.interRodDistance - 0.5*self.interRodDistance
self.Xmin = -0.5*LX
self.Xmax = 0.5*LX
self.Ymin = -0.5*LY
self.Ymax = 0.5*LY
[docs] def getGWLandBFDTDobjects(self):
GWL_obj = GWLobject()
BFDTD_obj = BFDTDobject()
layer_type_X = self.shiftInitialLayerType_X
layer_type_Y = self.shiftInitialLayerType_Y
if self.BottomToTop:
layer_idx_list = range(self.Nlayers_Z)
else:
layer_idx_list = range(self.Nlayers_Z-1,-1,-1)
for layer_idx in layer_idx_list:
direction = layer_idx % 2 + self.bottomLayerYPeriodic % 2
# TODO: reduce code duplication here between lines in X and lines in Y direction.
if direction % 2 == 0: # lines in the Y direction
if self.isSymmetrical:
N = self.NRodsPerLayer_X + (layer_type_X+1)%2
else:
N = self.NRodsPerLayer_X
# NOTE: some leftover from fitting the nanoscribe-style woodpile? Add option for it...? :/
#for rod_idx in range(N-1,-1,-1):
for rod_idx in range(N):
X = self.Xmin + self.Xoffset + layer_type_X*0.5*self.interRodDistance + rod_idx*self.interRodDistance
P1 = self.offset + numpy.array([X, self.Ymax, layer_idx*self.interLayerDistance])
P2 = self.offset + numpy.array([X, self.Ymin, layer_idx*self.interLayerDistance])
if self.rod_type == 'line':
GWL_obj.addLine(P1,P2)
elif self.rod_type == 'blockContinuous':
obj = GWL.tilted_grating.Parallelepiped()
obj.e1_vec3 = [0,1,0]
obj.e2_vec3 = [1,0,0]
if self.BottomToTop:
obj.e3_vec3 = [0,0,1]
else:
obj.e3_vec3 = [0,0,-1]
width = self.LineNumber_X*self.LineDistance_X
height = self.LineNumber_Z*self.LineDistance_Z
obj.voxelsize_vec3 = numpy.array([self.LineDistance_X,self.LineDistance_Y,self.LineDistance_Z])
obj.size_vec3 = [abs(P2[1]-P1[1])+obj.voxelsize_vec3[0],width,height]
obj.overlap_vec3 = numpy.array([0,0,0])
obj.center_vec3 = 0.5*(P1+P2)
obj.computePoints()
GWL_obj.addGWLobject(obj)
else:
GWL_obj.addYblock(P1, P2, self.LineNumber_X, self.LineDistance_X, self.LineNumber_Z, self.LineDistance_Z, self.BottomToTop)
block = bfdtd.Block()
block.setLowerAbsolute( P1 - 0.5*self.rod_width*numpy.array([1,0,0]) - 0.5*self.rod_height*numpy.array([0,0,1]) )
block.setUpperAbsolute( P2 + 0.5*self.rod_width*numpy.array([1,0,0]) + 0.5*self.rod_height*numpy.array([0,0,1]) )
block.setName("woodpile")
BFDTD_obj.geometry_object_list.append(block)
layer_type_X = (layer_type_X + 1) % 2
else: # lines in the X direction
if self.isSymmetrical:
N = self.NRodsPerLayer_Y + (layer_type_Y+1)%2
else:
N = self.NRodsPerLayer_Y
for rod_idx in range(N):
Y = self.Ymin + self.Yoffset + layer_type_Y*0.5*self.interRodDistance + rod_idx*self.interRodDistance
P1 = self.offset + numpy.array([self.Xmin, Y, layer_idx*self.interLayerDistance])
P2 = self.offset + numpy.array([self.Xmax, Y, layer_idx*self.interLayerDistance])
if self.rod_type == 'line':
GWL_obj.addLine(P1,P2)
# TODO: This is a quick hack at the moment. A lot of stuff needs to be rewritten to make things nicer.
elif self.rod_type == 'blockContinuous':
obj = GWL.tilted_grating.Parallelepiped()
obj.e1_vec3 = [1,0,0]
obj.e2_vec3 = [0,1,0]
if self.BottomToTop:
obj.e3_vec3 = [0,0,1]
else:
obj.e3_vec3 = [0,0,-1]
width = self.LineNumber_Y*self.LineDistance_Y
height = self.LineNumber_Z*self.LineDistance_Z
obj.voxelsize_vec3 = numpy.array([self.LineDistance_X,self.LineDistance_Y,self.LineDistance_Z])
obj.size_vec3 = [abs(P2[0]-P1[0])+obj.voxelsize_vec3[0],width,height]
obj.overlap_vec3 = numpy.array([0,0,0])
obj.center_vec3 = 0.5*(P1+P2)
obj.computePoints()
GWL_obj.addGWLobject(obj)
else:
GWL_obj.addXblock(P1, P2, self.LineNumber_Y, self.LineDistance_Y, self.LineNumber_Z, self.LineDistance_Z, self.BottomToTop)
block = bfdtd.Block()
block.setLowerAbsolute( P1 - 0.5*self.rod_width*numpy.array([0,1,0]) - 0.5*self.rod_height*numpy.array([0,0,1]) )
block.setUpperAbsolute( P2 + 0.5*self.rod_width*numpy.array([0,1,0]) + 0.5*self.rod_height*numpy.array([0,0,1]) )
block.setName("woodpile")
BFDTD_obj.geometry_object_list.append(block)
layer_type_Y = (layer_type_Y + 1) % 2
# optional: just add another write to easily distinguish layers inside file
# TODO: Replace with comment. Requires addition of some addComment() command.
GWL_obj.startNewVoxelSequence()
return (GWL_obj, BFDTD_obj)
[docs] def write_GWL(self, filename):
raise('Deprecated: Use writeGWL() instead.')
return
[docs] def write_BFDTD(self, filename):
raise('Deprecated: Use writeBFDTD() instead.')
return
[docs] def writeGWL(self, filename):
(GWL_obj, BFDTD_obj) = self.getGWLandBFDTDobjects()
(Pmin, Pmax) = GWL_obj.getLimits()
GWL_obj.writeGWL(filename, writingOffset = [0,0,-Pmin[2],0] ) # write object so that Zmin = 0
[docs] def writeBFDTD(self, filename):
# TODO: finish implementing this function
(GWL_obj, BFDTD_obj) = self.getGWLandBFDTDobjects()
print('Writing GWL to '+filename)
BFDTD_obj.writeGeoFile(filename)
[docs]def main():
woodpile_obj = Woodpile()
woodpile_obj.rod_width = 0.2
woodpile_obj.rod_height = 1
woodpile_obj.interLayerDistance = 1
woodpile_obj.interRodDistance = 2
woodpile_obj.LineNumber_X = 2
woodpile_obj.LineDistance_X = 0.050
woodpile_obj.LineNumber_Y = 2
woodpile_obj.LineDistance_Y = 0.050
woodpile_obj.LineNumber_Z = 2
woodpile_obj.LineDistance_Z = 0.100
woodpile_obj.rod_type='block'
woodpile_obj.adaptXYMinMax()
woodpile_obj.writeGWL('woodpile_test.gwl')
woodpile_obj.writeBFDTD('woodpile_test.geo')
if __name__ == "__main__":
main()