Skip to content
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

fix hankel overflow from high index/freq #2371

Merged
merged 1 commit into from
Jan 11, 2023
Merged

fix hankel overflow from high index/freq #2371

merged 1 commit into from
Jan 11, 2023

Conversation

hammy4815
Copy link
Contributor

This should fix the issues with overflows from the hankel functions. I had to reformulate and manipulate a few things so I will explain in detail:

Comment on lines +886 to +889
# Fix hankel functions for large imaginary arguments and remove nans and infs when non-indexed
scaled_hankel1 = lambda o, kr: hankel1e(o, kr) * np.exp(1j * kr - shift)
scaled_hankel2 = lambda o, kr: hankel2e(o, kr) * np.exp(-1j * kr - shift)
scaled_jv = lambda o, kr: jve(o, kr) * np.exp(np.abs(kr.imag) - shift)
Copy link
Contributor Author

@hammy4815 hammy4815 Jan 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am now using the exponential hankel functions and pass in the following wrappers that scale by the inverse of the exponential to correctly pass in a hankel function (or bessel at the origin).

In the exponential I am "shifting" by some amount shift, which really is just an extra factor being multiplied into the system. This is explained below:

Comment on lines +879 to +884
# Find large imaginary argument hankel shift
r, _ = self.get_r_rhat(X, X0)
kr = (k * r).flatten()
max_imag = np.abs(kr.imag[np.argmax(np.abs(kr.imag))])
max_float = int(np.log(float_info.max) / 2)
shift = 0 if np.abs(max_imag) < max_float else max_imag - max_float
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use sys.float_info.max to create a threshold. If the argument into the hankel functions ($kr$) has an imaginary component that is bigger (in magnitude) than this threshold, then I scale my entire system by a factor ($e^{-s}$ for scale s), to bring the magnitude smaller than what will overflow in the exponential. Without this extra scale component, reformulating with the exponential bessel functions would be useless because the overflow would instead still happen in my normalizing exponential term $e^{\pm kr}$ rather than in the old hankel functions.

If we want to use another way of deciding the threshold besides sys.float_info.max then I can change it.

Comment on lines +967 to +968
def get_r_rhat(self, X, X0):
"""Returns r and rhat before normalizing rhat to be used in green2d and get_fields for overflow prediction"""
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created this new function to minimize the amount of repeated code because I now need the term $kr$ in get_fields as well as green2d.

Comment on lines +892 to +893
o_fields2D = self.green2d(
X[:, outgoing_arg][..., np.newaxis],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am now passing in my masks for outgoing_arg, incoming_arg, and waist_points before calling green2D to eliminate the overflows that were happening when the wrong hankel was being used for a given argument despite those indices of X not being used. Before I was doing this while constructing the final fields2D array.

@stevengj
Copy link
Collaborator

stevengj commented Jan 11, 2023

So this changes the scaling of the resulting fields?

I would suggest rescaling all the time, not just when np.abs(max_imag) ≥ max_float. The reason is that we generally want Meep's outputs to be a continuous function of its inputs, not jump discontinuously when you vary some parameter (e.g. a beam waist) pass some threshold.

(Unfortunately, this complicates the meaning of the beam amplitude parameter.)

@hammy4815
Copy link
Contributor Author

hammy4815 commented Jan 11, 2023

So this changes the scaling of the resulting fields?

I would suggest rescaling all the time, not just when np.abs(max_imag) ≥ max_float. The reason is that we generally want Meep's outputs to be a continuous function of its inputs, not jump discontinuously when you vary some parameter (e.g. a beam waist) pass some threshold.

(Unfortunately, this complicates the meaning of the beam amplitude parameter.)

The scaling only changes the order of magnitude of the Hankel amplitudes. Phase is preserved. In addition, after calling green 2D where needed, the final fields are normalized such that the beam waist is amplitude 1. Thus, the scaling only affects the algorithm’s intermediate magnitudes, preserving the magnitude of the final output. Thus I wouldn’t expect a discontinuity in the output.

in other words, when rescaling $H(x)\rightarrow aH(x)$, when we choose $a$ shouldn’t matter because the final normalization $\frac{G(aH(x))}{G(aH(0))}$ is independent of $a$ since the greens function $G$ is a linear combination of Hankel functions $H$.

@stevengj stevengj merged commit 19b8abe into NanoComp:master Jan 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants