-
Notifications
You must be signed in to change notification settings - Fork 52
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
Vasily/bandgap_monitor_update #2344
base: develop
Are you sure you want to change the base?
Conversation
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.
Seems like it still needs a test to be added as per my comment on the other PR.
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.
Thanks Vasily for this! My main comment would be the platting function and how this would work for a 3D monitor. But other than that looks good to me
field_data = {field: values.get(field) for field in ["Ec", "Ev", "Ei", "Efn", "Efp"]} | ||
|
||
for field, data in field_data.items(): |
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.
This field_data
seems redundant? The for-loop could be over the fields, i.e., for field in ["Ec", "Ev", "Ei", "Efn", "Efp"]:
as you're doing below.
Not particularly bother by it though
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.
Here I have the loop where I need co sweep "data" and "data" variables together. I thought that this way is better to preserve that I they are updated each iteration of the loop.
if not isinstance(self.Ec, TriangularGridDataset): | ||
raise DataError( | ||
"Bandgap monitor slice plot can be done only for a 2D unstructured dataset." | ||
) |
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.
Do we really need this restriction? If we had a 3D monitor we would have a TetrahedralGridDataset
that we could slice twice?
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.
The logic of the bandgap monitor is that we gather all fields (Ec, Ev, Efn, etc.) together and do operations like plane_slice() and plot_slice() on all variables. So, plot_slice() works only for a 2D data and plots the 1D cross-section for all fields, and plane_slice() works only for a 3D data and make a 2D crossection -> new bandgap monitor data object that we can use for plot_slice() etc.
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.
If user wants to do more detailed analysis, the original data fields (and all related operations) are available.
if not isinstance(self.Ec, TetrahedralGridDataset): | ||
raise DataError( | ||
"Bandgap monitor plane slice can be done only for a 3D unstructured dataset." | ||
) | ||
|
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.
So if I understand correctly, the user would first need to create a copy of the data using this plane_slice(axis, pos)
and then plot_slice(axis, pos)
. Wouldn't it make sense to have a plot function that would take all? So for instance, in the case of a 3D monitor it could be plot_diagram(x=1,y=3)
and when a 2D plot_diagram(x=1)
?
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.
It can be done this way.
But in the current implementation we need only two specific operations for the bandgap monitor with the syntax that is very close to operations for .sel() function and plane_slice(). So it is already familiar for users.
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.
I think it'd be nicer to have it all in one function so that the user doesn't need to go through a series of steps instead of doing it in a single step.
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.
So basically:
- If data is 3D
- user specifies only one coordinate (e.g., x=2) -> raise error
- user specifies 2 coordinates (e.g., x=2, z=4) -> OK
- if data is 2D
- user specifies only one coordinate (e.g., x=2) -> OK
- user specifies 2 coordinates (e.g., x=2, z=4) -> raise error
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.
Yeah that would also make sense and is along the lines of our field plotting, again bringing up the sel_kwargs
there:
tidy3d/tidy3d/components/data/sim_data.py
Lines 488 to 493 in 382f204
sel_kwargs : keyword arguments used to perform ``.sel()`` selection in the monitor data. | |
These kwargs can select over the spatial dimensions (``x``, ``y``, ``z``), | |
frequency or time dimensions (``f``, ``t``) or ``mode_index``, if applicable. | |
For the plotting to work appropriately, the resulting data after selection must contain | |
only two coordinates with len > 1. | |
Furthermore, these should be spatial coordinates (``x``, ``y``, or ``z``). |
In that case, we expect exactly two spatial coordinates to remain after all the selection is done. Here, we expect one.
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.
OK. I will update the code.
Regarding the unit tests. I want to add one, but I don't understand the logic of the existing unit tests for SteadyPotentialMonitor, SteadyFreeCarrierMonitor, etc. It looks like they just test creation of the new data. |
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.
Thanks Vasily!
For the naming of the function maybe plot
would suffice? Other than that I think this looks pretty much ready
if "voltage" not in sel_kwargs: | ||
raise DataError("'voltage' is not selected for the plot.") |
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.
We need to be a bit careful with this. If you run for a single voltage you should be able to get away without selecting a voltage
@vasilyzabelin with regards to the test, in those tests you're referring to I think we simply make sure we can create the structures. You can also test that the correct error is raised, For instance, you can check that if your data is of type |
I've added the unit test for the new monitor type. |
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.
Overall this looks good but it'd be great if you could update the test to capture the error when voltage isn't provide but we have fields with multiple voltages
if ("voltage" not in sel_kwargs) and (self.Ec.values.coords.sizes["voltage"] > 1): | ||
raise DataError( | ||
"'voltage' is not selected for the plot with multiple voltage data points." | ||
) |
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.
Can you check that this error is actually raised in your tests?
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.
I tried to do it, but I can't figure out the proper way. Can we have a meeting to discuss the logic of the unit tests here?
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.
Here's an example of what I mean:
tidy3d/tests/test_components/test_heat_charge.py
Line 1320 in 3dc27f1
with pytest.raises(KeyError): |
though there are more examples in that same file. Basically, we set up the component in the way we want the error to arise and check that it does. We can of course discuss it in a meeting. Feel free to schedule one
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.
I will try. As far as I see, the monitor data in various tests here is artificially generated, and not a result of an actual simulation, that is done in this function:
def monitors(): |
So, I need to create the monitor data with the desired error?
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.
yes, you can create the data. There's examples in here:
tidy3d/tests/test_data/test_datasets.py
Line 672 in 3dc27f1
def test_cell_values(): |
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.
This is close to finished. I guess at this point is only a matter of adding corresponding tests
if ("voltage" not in sel_kwargs) and (self.Ec.values.coords.sizes["voltage"] > 1): | ||
raise DataError( | ||
"'voltage' is not selected for the plot with multiple voltage data points." | ||
) |
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.
yes, you can create the data. There's examples in here:
tidy3d/tests/test_data/test_datasets.py
Line 672 in 3dc27f1
def test_cell_values(): |
if isinstance(self.Ec, TetrahedralGridDataset): | ||
if N_coords != 2: | ||
raise DataError( | ||
"2 spatial coordinate values have to be defined to plot the 1D cross-section figure for a 3D dataset." | ||
) | ||
|
||
elif isinstance(self.Ec, TriangularGridDataset): | ||
if N_coords != 1: | ||
raise DataError( | ||
"1 spatial coordinate value has to be defined to plot the 1D cross-section figure for a 2D dataset." | ||
) | ||
|
||
for index, coord_name in enumerate(["x", "y", "z"]): | ||
if coord_name in selection_data: | ||
axis = index | ||
continue | ||
|
||
if axis == self.Ec.normal_axis: | ||
raise DataError( | ||
f"Triangular grid (normal: {self.Ec.normal_axis}) cannot be sliced by a parallel plane." | ||
) |
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.
Let's add a test for these errors too
Update of the energy bandgap monitor for the CHARGE solver