-
Notifications
You must be signed in to change notification settings - Fork 626
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adjoint near2far with vol_src #1329
Merged
Merged
Changes from 44 commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
308707e
add yee grid to array slice
c58e737
add yee grid to array slice
8a8824d
fix gradients
smartalecH d61816b
rebase
smartalecH cf9c320
try better gradients
smartalecH 8d8df73
cleanup from revert
smartalecH 41b5f5e
fix multifreq bug
a79f297
mpi memory fixes
b414df8
fix memory leaks
a1d30fa
setup branch
8e9de90
correct scale
6e72da9
offset issue
bb30464
offset issue
aee0bb7
several frequencies
d418f03
several frequencies
a491986
several frequencies
3e76fd1
fourier
e99343c
update example
4bbfb44
near2far
ef9229b
near2far
eca1bb8
fix factor
a7c931f
typo
33efaa5
yee grid
6de5d0d
rebase
f191434
rebase
b9710fd
rebase
f2d145d
weight
9cb8370
add example
8e45164
Fix Fourier
bd489e0
change example
ddb802d
typo
eb2c0be
rebase
beebc6b
fix rebase
ac05905
fix rebase
1c9305b
fix rebase
1c08437
srcdata
9d910cc
vol_src
f77eaee
src_vol
63f481c
src_vol
ed848f3
src_vol
b1fe236
src_vol
2cfe2d9
add example
069fce5
add example
a673d01
Merge branch 'master' into near2far_src
mochen4 945c0ee
clean up
55ff5b4
merge
b8a4189
clean up
156f334
delete extra blank lines
6d10ca9
resolve issues
abc94d8
fix issues
37d65e8
fix issues
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
import numpy as np | ||
import meep as mp | ||
from .filter_source import FilteredSource | ||
from .optimization_problem import atleast_3d, Grid | ||
|
||
class ObjectiveQuantitiy(ABC): | ||
@abstractmethod | ||
|
@@ -35,15 +36,15 @@ def __init__(self,sim,volume,mode,forward=True,kpoint_func=None,**kwargs): | |
self.eval = None | ||
self.EigenMode_kwargs = kwargs | ||
return | ||
|
||
def register_monitors(self,frequencies): | ||
self.frequencies = np.asarray(frequencies) | ||
self.monitor = self.sim.add_mode_monitor(frequencies,mp.FluxRegion(center=self.volume.center,size=self.volume.size),yee_grid=True) | ||
self.normal_direction = self.monitor.normal_direction | ||
return self.monitor | ||
|
||
def place_adjoint_source(self,dJ): | ||
'''Places an equivalent eigenmode monitor facing the opposite direction. Calculates the | ||
'''Places an equivalent eigenmode monitor facing the opposite direction. Calculates the | ||
correct scaling/time profile. | ||
|
||
dJ ........ the user needs to pass the dJ/dMonitor evaluation | ||
|
@@ -64,31 +65,31 @@ def place_adjoint_source(self,dJ): | |
k0 == direction_scalar * mp.Vector3(z=1) | ||
else: | ||
k0 = direction_scalar * self.kpoint_func(self.time_src.frequency,1) | ||
|
||
# -------------------------------------- # | ||
# Get scaling factor | ||
# Get scaling factor | ||
# -------------------------------------- # | ||
# leverage linearity and combine source for multiple frequencies | ||
if dJ.ndim == 2: | ||
dJ = np.sum(dJ,axis=1) | ||
|
||
# Determine the correct resolution scale factor | ||
if self.sim.cell_size.y == 0: | ||
dV = 1/self.sim.resolution | ||
elif self.sim.cell_size.z == 0: | ||
dV = 1/self.sim.resolution * 1/self.sim.resolution | ||
else: | ||
dV = 1/self.sim.resolution * 1/self.sim.resolution * 1/self.sim.resolution | ||
|
||
da_dE = 0.5 * self.cscale # scalar popping out of derivative | ||
iomega = (1.0 - np.exp(-1j * (2 * np.pi * self.frequencies) * dt)) * (1.0 / dt) # scaled frequency factor with discrete time derivative fix | ||
|
||
src = self.time_src | ||
|
||
# an ugly way to calcuate the scaled dtft of the forward source | ||
y = np.array([src.swigobj.current(t,dt) for t in np.arange(0,T,dt)]) # time domain signal | ||
fwd_dtft = np.matmul(np.exp(1j*2*np.pi*self.frequencies[:,np.newaxis]*np.arange(y.size)*dt), y)*dt/np.sqrt(2*np.pi) # dtft | ||
|
||
# we need to compensate for the phase added by the time envelope at our freq of interest | ||
src_center_dtft = np.matmul(np.exp(1j*2*np.pi*np.array([src.frequency])[:,np.newaxis]*np.arange(y.size)*dt), y)*dt/np.sqrt(2*np.pi) | ||
adj_src_phase = np.exp(1j*np.angle(src_center_dtft)) | ||
|
@@ -101,7 +102,7 @@ def place_adjoint_source(self,dJ): | |
scale = da_dE * dV * dJ * iomega / adj_src_phase | ||
src = FilteredSource(self.time_src.frequency,self.frequencies,scale,dt) # generate source from broadband response | ||
amp = 1 | ||
|
||
# generate source object | ||
self.source = [mp.EigenModeSource(src, | ||
eig_band=self.mode, | ||
|
@@ -112,7 +113,7 @@ def place_adjoint_source(self,dJ): | |
size=self.volume.size, | ||
center=self.volume.center, | ||
**self.EigenMode_kwargs)] | ||
|
||
return self.source | ||
|
||
def __call__(self): | ||
|
@@ -122,7 +123,7 @@ def __call__(self): | |
# Eigenmode data | ||
direction = mp.NO_DIRECTION if self.kpoint_func else mp.AUTOMATIC | ||
ob = self.sim.get_eigenmode_coefficients(self.monitor,[self.mode],direction=direction,kpoint_func=self.kpoint_func,**self.EigenMode_kwargs) | ||
self.eval = np.squeeze(ob.alpha[:,:,self.forward]) # record eigenmode coefficients for scaling | ||
self.eval = np.squeeze(ob.alpha[:,:,self.forward]) # record eigenmode coefficients for scaling | ||
self.cscale = ob.cscale # pull scaling factor | ||
|
||
return self.eval | ||
|
@@ -132,4 +133,149 @@ def get_evaluation(self): | |
try: | ||
return self.eval | ||
except AttributeError: | ||
raise RuntimeError("You must first run a forward simulation before resquesting an eigenmode coefficient.") | ||
raise RuntimeError("You must first run a forward simulation before resquesting an eigenmode coefficient.") | ||
|
||
|
||
|
||
class Fourier_Coefficients(ObjectiveQuantitiy): | ||
def __init__(self,sim,volume, component): | ||
|
||
self.sim = sim | ||
self.volume=volume | ||
self.eval = None | ||
self.component = component | ||
return | ||
|
||
def register_monitors(self,frequencies): | ||
self.frequencies = np.asarray(frequencies) | ||
self.num_freq = len(self.frequencies) | ||
self.monitor = self.sim.add_dft_fields([self.component], self.frequencies, where=self.volume, yee_grid=False) | ||
|
||
return self.monitor | ||
|
||
def place_adjoint_source(self,dJ): | ||
|
||
dt = self.sim.fields.dt | ||
|
||
if self.sim.cell_size.y == 0: | ||
dV = 1/self.sim.resolution | ||
elif self.sim.cell_size.z == 0: | ||
dV = 1/self.sim.resolution * 1/self.sim.resolution | ||
else: | ||
dV = 1/self.sim.resolution * 1/self.sim.resolution * 1/self.sim.resolution | ||
|
||
self.sources = [] | ||
|
||
if self.num_freq == 1: | ||
scale = dV * 1j * 2 * np.pi * self.frequencies[0] / self.time_src.fourier_transform(self.frequencies[0]) | ||
amp = -atleast_3d(dJ[0]) * scale | ||
if self.component in [mp.Hx, mp.Hy, mp.Hz]: | ||
amp = -amp | ||
for zi in range(len(self.dg.z)): | ||
for yi in range(len(self.dg.y)): | ||
for xi in range(len(self.dg.x)): | ||
if amp[xi, yi, zi] != 0: | ||
self.sources += [mp.Source(self.time_src, component=self.component, amplitude= amp[xi, yi, zi], | ||
center=mp.Vector3(self.dg.x[xi], self.dg.y[yi], self.dg.z[zi]))] | ||
else: | ||
dJ_4d = np.array([atleast_3d(dJ[f]) for f in range(self.num_freq)]) | ||
if self.component in [mp.Hx, mp.Hy, mp.Hz]: | ||
dJ_4d = -dJ_4d | ||
for zi in range(len(self.dg.z)): | ||
for yi in range(len(self.dg.y)): | ||
for xi in range(len(self.dg.x)): | ||
scale = -dJ_4d[:,xi,yi,zi] * dV * 1j * 2 * np.pi * self.frequencies / np.array([self.time_src.fourier_transform(f) for f in self.frequencies]) | ||
src = FilteredSource(self.time_src.frequency,self.frequencies,scale,dt,self.time_src) | ||
self.sources += [mp.Source(src, component=self.component, amplitude= 1, | ||
center=mp.Vector3(self.dg.x[xi], self.dg.y[yi], self.dg.z[zi]))] | ||
|
||
return self.sources | ||
|
||
def __call__(self): | ||
self.time_src = self.sim.sources[0].src | ||
|
||
self.dg = Grid(*self.sim.get_array_metadata(dft_cell=self.monitor)) | ||
self.eval = np.array([self.sim.get_dft_array(self.monitor, self.component, i) for i in range(self.num_freq)]) #Shape = (num_freq, [pts]) | ||
return self.eval | ||
|
||
def get_evaluation(self): | ||
|
||
try: | ||
return self.eval | ||
except AttributeError: | ||
raise RuntimeError("You must first run a forward simulation.") | ||
|
||
|
||
|
||
class Far_Coefficients(ObjectiveQuantitiy): | ||
def __init__(self,sim,Near2FarRegions, far_pt): | ||
self.sim = sim | ||
self.Near2FarRegions=Near2FarRegions | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should always be a list, right? Maybe do a check so the user can supply a single region without a list. |
||
self.eval = None | ||
self.far_pt = far_pt | ||
return | ||
|
||
def register_monitors(self,frequencies): | ||
self.frequencies = np.asarray(frequencies) | ||
self.num_freq = len(self.frequencies) | ||
self.monitor = self.sim.add_near2far(self.frequencies, *self.Near2FarRegions, yee_grid=True) | ||
return self.monitor | ||
|
||
def place_adjoint_source(self,dJ): | ||
|
||
dt = self.sim.fields.dt | ||
|
||
if self.sim.cell_size.y == 0: | ||
dV = 1/self.sim.resolution | ||
elif self.sim.cell_size.z == 0: | ||
dV = 1/self.sim.resolution * 1/self.sim.resolution | ||
else: | ||
dV = 1/self.sim.resolution * 1/self.sim.resolution * 1/self.sim.resolution | ||
|
||
self.sources = ['src_vol'] | ||
|
||
|
||
freq_scale = 1j * 2 * np.pi * self.frequencies / np.array([self.time_src.fourier_transform(f) for f in self.frequencies]) | ||
stevengj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
dJ = list(dJ.flatten()) | ||
dJ_c = mp.ComplexVector(len(dJ)) | ||
for i in range(len(dJ)): | ||
dJ_c[i] = dJ[i] | ||
|
||
#TODO far_pts in 3d or cylindrical, perhaps py_v3_to_vec from simulation.py | ||
self.all_nearsrcdata = self.monitor.swigobj.near_sourcedata(mp.vec(self.far_pt.x, self.far_pt.y), dJ_c) | ||
|
||
|
||
stevengj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for near_data in self.all_nearsrcdata: | ||
cur_comp = near_data.near_fd_comp | ||
amp_arr = near_data.amp_arr.reshape(self.num_freq, -1) | ||
|
||
|
||
if cur_comp in [mp.Ex, mp.Ey, mp.Ez]: | ||
scale = -freq_scale * amp_arr | ||
else: | ||
scale = freq_scale * amp_arr | ||
stevengj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if self.num_freq == 1: | ||
##TODO | ||
self.sources += [mp.Source(self.time_src, component=cur_comp, amplitude=scale[0], center=near_pt)] | ||
stevengj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
else: | ||
src = FilteredSource(self.time_src.frequency,self.frequencies,scale,dt,self.time_src) | ||
self.sources+=[(src.nodes, src.time_src_bf, near_data)] | ||
stevengj marked this conversation as resolved.
Show resolved
Hide resolved
stevengj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
|
||
|
||
return self.sources | ||
|
||
def __call__(self): | ||
self.time_src = self.sim.sources[0].src | ||
self.eval = np.array(self.sim.get_farfield(self.monitor, self.far_pt)) | ||
self.eval = self.eval.reshape((self.num_freq, 6)) | ||
return self.eval | ||
|
||
def get_evaluation(self): | ||
try: | ||
return self.eval | ||
except AttributeError: | ||
raise RuntimeError("You must first run a forward simulation.") |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe call it
FourierCoefficients
(omit the underscore) to match the rest of the codebase.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe FourierFields and Near2FarFields