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

Vasily/bandgap_monitor_update #2344

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from

Conversation

vasilyzabelin
Copy link
Contributor

Update of the energy bandgap monitor for the CHARGE solver

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.

Seems like it still needs a test to be added as per my comment on the other PR.

Copy link
Contributor

@marc-flex marc-flex left a 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

Comment on lines +245 to +247
field_data = {field: values.get(field) for field in ["Ec", "Ev", "Ei", "Efn", "Efp"]}

for field, data in field_data.items():
Copy link
Contributor

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

Copy link
Contributor Author

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.

Comment on lines 325 to 328
if not isinstance(self.Ec, TriangularGridDataset):
raise DataError(
"Bandgap monitor slice plot can be done only for a 2D unstructured dataset."
)
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Comment on lines 377 to 381
if not isinstance(self.Ec, TetrahedralGridDataset):
raise DataError(
"Bandgap monitor plane slice can be done only for a 3D unstructured dataset."
)

Copy link
Contributor

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)?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor

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

Copy link
Collaborator

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:

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.

Copy link
Contributor Author

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.

@vasilyzabelin
Copy link
Contributor Author

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.

Copy link
Contributor

@marc-flex marc-flex left a 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

Comment on lines 322 to 323
if "voltage" not in sel_kwargs:
raise DataError("'voltage' is not selected for the plot.")
Copy link
Contributor

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

@marc-flex
Copy link
Contributor

@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 TetrahedralGridDataset and only one selection coordinate is given, a DataError is raised.

@vasilyzabelin
Copy link
Contributor Author

I've added the unit test for the new monitor type.

Copy link
Contributor

@marc-flex marc-flex left a 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

Comment on lines +322 to +325
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."
)
Copy link
Contributor

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?

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 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?

Copy link
Contributor

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:

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

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 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:

So, I need to create the monitor data with the desired error?

Copy link
Contributor

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:

def test_cell_values():

Copy link
Contributor

@marc-flex marc-flex left a 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

Comment on lines +322 to +325
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."
)
Copy link
Contributor

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:

def test_cell_values():

Comment on lines +333 to +353
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."
)
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants