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

Qiming/fix 2d near2far #1639

Draft
wants to merge 1 commit into
base: pre/2.7
Choose a base branch
from
Draft

Qiming/fix 2d near2far #1639

wants to merge 1 commit into from

Conversation

QimingFlex
Copy link

Implemented a preliminary fix for the 'near2far' in 2D. The key differences between 2D and 3D of 'near2far' in math are as follows:

  1. Field Components - In 3D, they include Er, Ephi, Etheta, Hr, Hphi, Htheta; in 2D, for TMz mode, they are Ez and Hphi, and for TEz mode, Ephi and Hz.
  2. Parameters - In 3D, these are theta, phi, and f; in 2D, phi and f.
  3. Propagation phase.
  4. RCS coefficient.

Given these differences, my initial idea was to develop a 2D variant of the FieldProjectionAngleMonitor, omitting the theta parameter and inheriting from the AbstractFieldProjectionMonitor. However, since the AbstractFieldProjectionMonitor is heavily designed for 3D features and depends on theta, it seems a 2D monitor would require a ground-up redesign. My current approach involves a rudimentary adaptation where the 3D monitor is used as 2D by setting theta = pi/2, and I added a 2D version of the far-field computation. This mirrors the 3D code with adjustments for the propagation phase and the RCS coefficient. I am relatively new to contributing to a large project, any comments and suggestions on how to more efficiently integrate these changes would be greatly appreciated.

@momchil-flex
Copy link
Collaborator

Thanks @QimingFlex!

I think it should be possible to reuse our monitors as they are. I think you haven't yet familiarized yourself with pydantic validators, which is what we use ubiquitously in the code to ensure the user can only set things the way we want them to. I think that's what we can use here and you should start looking through some examples of those in our code (@dmarek-flex could help if needed).

I think what you describe, setting theta = pi/2, works specifically because the simulation 0-dim is set to z. If we were to instead rotate everything such that e.g. the 0-dim is x, then we would do something like phi = 0 and scan theta from 0 to 2*pi right? So I think what we can do is

  1. Use a validator to enforce that no theta is provided to the user if a 2D simulation. Explain in the docstring that in 2D simulations, only phi is needed, and is always in the plane of the simulation.
  2. Under the hood, convert the phi-s provided by the user, with the definition above, to a (theta, phi) to be passed to a regular 3D monitor.
  3. Finally reframe the data back to that reference frame.

I think this can be done with the field data too. We don't necessarily want to introduce whole new objects to store the 2D data. We can use the same, and Etheta and Htheta will just always be zeros. We also don't currently explicitly split 2D sims into TE or TM. Depending on the exciting source, those could even be mixed. So it would just be again a matter of rotating everything from the global reference frame where z is the polar axis, to the 2D refarence frame that only has r and phi. Does that make sense?

@QimingFlex
Copy link
Author

Thanks for the detailed explanation and guidance @momchil-flex. I'll start with the pydantic validators and consult Damian. Your approach to simulations and data transformations is clear and helpful!

@momchil-flex
Copy link
Collaborator

Actually, on second thought I feel like everything should be handled internally. That is to say, there will be no difference in the reference frame used from the user perspective depending on whether the simulation is 2D or 3D. The reason why I'm thinking this may make the most sense is so that users easily get consistent results between 2D and 3D sim setups. For example here I want to be able to just change the simulation size along y to 0, nothing else, and have everything working. That means that in the 2D sim I will still get the far field w.r.t. theta, and theta is defined w.r.t. the z axis.

I feel like from a usability perspective this is maybe better than requiring the user to rotate the axes based on the simulation 0-sized-dim? @tomflexcompute any input here?

@tomflexcompute
Copy link
Contributor

Actually, on second thought I feel like everything should be handled internally. That is to say, there will be no difference in the reference frame used from the user perspective depending on whether the simulation is 2D or 3D. The reason why I'm thinking this may make the most sense is so that users easily get consistent results between 2D and 3D sim setups. For example here I want to be able to just change the simulation size along y to 0, nothing else, and have everything working. That means that in the 2D sim I will still get the far field w.r.t. theta, and theta is defined w.r.t. the z axis.

I feel like from a usability perspective this is maybe better than requiring the user to rotate the axes based on the simulation 0-sized-dim? @tomflexcompute any input here?

Yeah totally agree.

@momchil-flex
Copy link
Collaborator

On the flip side though if someone is doing scattering off a Cylinder they would have to set different settings in the projection monitor (different angles, specifically) depending on which axis is the zero-size dim. Still, I feel like this is the better way.

@QimingFlex QimingFlex force-pushed the Qiming/fix_2d_near2far branch 4 times, most recently from 6573628 to 36d233f Compare May 22, 2024 16:41
Normalization to power density

delete some unuseful comments

delete a wrong commnet in def integrate_2d

something

fix rcs

near2far fix

delete compute2d

format changes

format issues

fix

fix

format
@QimingFlex
Copy link
Author

Hi @momchil-flex , I just updated the revision. After some thought, I believe it's better to conduct near-to-far in the x-y plane for the following reasons:

  1. This conforms to the standards of cylindrical coordinates where phi starts from the x-axis and the z-component is along the infinite direction. I guess it's less common to consider a case where phi starts from the y-axis with the x-component along the infinite direction?
  2. Such a revision would require much more effort to me at this moment as I still need some more time to get familiar with the code. Perhaps the directivity monitor will help me understand the code more so this won't be a problem later.

In this revision, the simulation size along the z-direction determines whether it's a 2D or 3D simulation. If a 2D simulation is determined, theta is set to pi/2, and the propagation constant is related to the Hankel function. These computations are handled internally. However, I encountered an issue with computing the RCS. It seems related to the monitor, which is a 3D monitor and cannot determine if it's a 2D or 3D simulation. To address this, I introduced radar_cross_section_2d.

Copy link
Collaborator

@momchil-flex momchil-flex left a comment

Choose a reason for hiding this comment

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

Unfortunately I don't think this is a good approach @QimingFlex . An immediate case that comes to mind that is not handled well is a grating coupler. If you look at our grating coupler examples, the natural thing to do would be to compare 2d and 3d simulations where the 0D in the 2d sim is along y.

We could consider enabling this for now just so 2D projections are supported in some way, and make sure we validate things well and explain to users how to use the monitor. But I would prefer if we just handle the general case directly...

@@ -534,6 +559,10 @@ def project_fields(
return self._project_fields_cartesian(proj_monitor)
return self._project_fields_kspace(proj_monitor)

def is_3d_simulation(self) -> bool:
"""Determine if the simulation is 2D or 3D based on the size attribute."""
return self.sim_data.simulation.size[2] != 0.01
Copy link
Collaborator

Choose a reason for hiding this comment

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

I assume this was set for your specific test? Normally we check if sim.size[index] > 0. We could also consider if sim.grid.num_cells[index] > 1 to capture more general cases with a single pixel along a direction.

Copy link
Author

Choose a reason for hiding this comment

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

Thank you for the comments, @momchil-flex ! To achieve that goal for a general case, I'm considering how to modify the monitor if we allow 0-dim for every dimension. For example, Ez/Hphi and Hz/Ephi are generated when z is 0-dim, Ey/Hphi and Hy/Ephi are generated when y is 0-dim, and the same applies for x being 0-dim. Therefore, we would need to introduce new fields in the field monitor to consider all these three cases. Is this correct?

Copy link
Author

Choose a reason for hiding this comment

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

The condition if sim.grid.num_cells[index] > 1 seems to fit well!

@momchil-flex
Copy link
Collaborator

Thank you for the comments, @momchil-flex ! To achieve that goal for a general case, I'm considering how to modify the monitor if we allow 0-dim for every dimension. For example, Ez/Hphi and Hz/Ephi are generated when z is 0-dim, Ey/Hphi and Hy/Ephi are generated when y is 0-dim, and the same applies for x being 0-dim. Therefore, we would need to introduce new fields in the field monitor to consider all these three cases. Is this correct?

You should use the exact same data structures as for the 3D monitors, i.e. Er, Ephi, Etheta, with the polar coordinates defined w.r.t. the global z axis. For e.g. a 2D sim in the xy-plane then, you would have theta = pi / 2 and what you call Ez and Hphi for a TM sim would actually be Etheta and Hphi. All the other 3D components will be zero. If the plane is oriented along yz, then phi is set to 0, and what would be now Ex/Hphi in a purely 2D case correspond to Ephi/Htheta of the 3D data structure. For an xz plane, phi = pi/2 and Ey/Hphi again correspond to Ephi/Htheta. Try to visualize this and let me know if it makes sense to you.

I mean, as I'm writing this I do realize that it sounds convoluted. But like I mentioned above, the nice thing would be that I can just swap 2D and 3D sims without needing to change anything else in my scripts, i.e. take a 3D sim and choose to make one of the axes 0D and compare the projection results directly. I am not sure how much this complicates everything internally. Like, I feel like it should be possible to write the 2D projection as a special case of the 3D projection, and not have to do too much going back and forth between different coordinates. Conceptually, it should be possible to write the Green's function for a 2D sim with arbitrary normal axis as a 3D Green's function in the polar coordinates with a global z axis, i.e. in principle you can still write G(x, y) as a G(r, theta, phi), assuming G(x, y) is just constant with z. Now, practically there are a couple extra steps to actually put this in the code, but I still think eventually producing results in the same coordinate system as the 3D monitors would be best.

@tylerflex
Copy link
Collaborator

What's the status of this PR? is it for 2.7?
@QimingFlex @weiliangjin2021

@QimingFlex
Copy link
Author

Thank you for the comments, @momchil-flex ! To achieve that goal for a general case, I'm considering how to modify the monitor if we allow 0-dim for every dimension. For example, Ez/Hphi and Hz/Ephi are generated when z is 0-dim, Ey/Hphi and Hy/Ephi are generated when y is 0-dim, and the same applies for x being 0-dim. Therefore, we would need to introduce new fields in the field monitor to consider all these three cases. Is this correct?

You should use the exact same data structures as for the 3D monitors, i.e. Er, Ephi, Etheta, with the polar coordinates defined w.r.t. the global z axis. For e.g. a 2D sim in the xy-plane then, you would have theta = pi / 2 and what you call Ez and Hphi for a TM sim would actually be Etheta and Hphi. All the other 3D components will be zero. If the plane is oriented along yz, then phi is set to 0, and what would be now Ex/Hphi in a purely 2D case correspond to Ephi/Htheta of the 3D data structure. For an xz plane, phi = pi/2 and Ey/Hphi again correspond to Ephi/Htheta. Try to visualize this and let me know if it makes sense to you.

I mean, as I'm writing this I do realize that it sounds convoluted. But like I mentioned above, the nice thing would be that I can just swap 2D and 3D sims without needing to change anything else in my scripts, i.e. take a 3D sim and choose to make one of the axes 0D and compare the projection results directly. I am not sure how much this complicates everything internally. Like, I feel like it should be possible to write the 2D projection as a special case of the 3D projection, and not have to do too much going back and forth between different coordinates. Conceptually, it should be possible to write the Green's function for a 2D sim with arbitrary normal axis as a 3D Green's function in the polar coordinates with a global z axis, i.e. in principle you can still write G(x, y) as a G(r, theta, phi), assuming G(x, y) is just constant with z. Now, practically there are a couple extra steps to actually put this in the code, but I still think eventually producing results in the same coordinate system as the 3D monitors would be best.

I just went through the code for both Monitors and MonitorData today and got a better understanding of the monitor classes. What you posted makes sense to me. I will test all three 2D scenarios to ensure we get consistent results.

@QimingFlex
Copy link
Author

What's the status of this PR? is it for 2.7? @QimingFlex @weiliangjin2021

It still needs some time to be finished. I'm not sure if it is for 2.7, any ideas? @tomflexcompute @momchil-flex

@tomflexcompute
Copy link
Contributor

What's the status of this PR? is it for 2.7? @QimingFlex @weiliangjin2021

It still needs some time to be finished. I'm not sure if it is for 2.7, any ideas? @tomflexcompute @momchil-flex

If it's too much of a rush, I think we can certainly leave it for a later version release or pre-release.

@tylerflex tylerflex added the 2.8 will go into version 2.8.* label May 25, 2024
@tylerflex
Copy link
Collaborator

Ok, for now I'll tag it for 2.8 and mark as a draft just to focus on the PRs that are in their final stages.

@tylerflex tylerflex marked this pull request as draft May 25, 2024 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2.8 will go into version 2.8.*
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants