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

Visualise atom trace #657

Open
wants to merge 35 commits into
base: protos
Choose a base branch
from
Open

Conversation

MBartkowiakSTFC
Copy link
Collaborator

Description of work
This PR adds the option of visualising the 'atom trace' in the 3D view. It is intended to show the accumulated volume that was taken by an atom within the entire time of the simulation. This may be useful for identifying hopping paths in porous materials, ionic conductors, etc.

Screenshot 2025-02-05 at 10 31 51

Fixes

  1. Adapted the code from Eric's molecular viewer.
  2. Enabled multiple surfaces in a single view.
  3. Reimplemented the trace calculation method to limit the grid size and account for the atom radius.

To test

  1. Load a trajectory.
  2. Calculate the trace for several atoms.
  3. Check that the isosurface does not disappear when switching to a different frame.
  4. Check that the isosurface disappears when switching to a different trajectory.

@MBartkowiakSTFC MBartkowiakSTFC marked this pull request as draft February 5, 2025 10:59
MBartkowiakSTFC and others added 10 commits February 7, 2025 07:11
Co-authored-by: Jacob Wilkins <[email protected]>
Signed-off-by: Maciej Bartkowiak <[email protected]>
Co-authored-by: Jacob Wilkins <[email protected]>
Signed-off-by: Maciej Bartkowiak <[email protected]>
Co-authored-by: Jacob Wilkins <[email protected]>
Signed-off-by: Maciej Bartkowiak <[email protected]>
Co-authored-by: Jacob Wilkins <[email protected]>
Signed-off-by: Maciej Bartkowiak <[email protected]>
Co-authored-by: Jacob Wilkins <[email protected]>
Signed-off-by: Maciej Bartkowiak <[email protected]>
@MBartkowiakSTFC MBartkowiakSTFC marked this pull request as ready for review February 10, 2025 11:32
@MBartkowiakSTFC
Copy link
Collaborator Author

There are two features missing in this PR:

  1. There is no way to indicate to the user in the 3D view which atom the trace will be calculated for.
  2. The colour input field has no validation.
    This will be summarised in an issue and added in a later PR.

@@ -115,7 +134,7 @@ def __getitem__(self, frame):
except:
conv_factor = 1.0
else:
if pos_unit == "Ang":
if pos_unit == "Ang" or pos_unit == "Angstrom":
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if pos_unit == "Ang" or pos_unit == "Angstrom":
if pos_unit in ("Ang", "Angstrom"):

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

These changes are part of PR #655

@@ -193,7 +212,7 @@ def coordinates(self, frame):
except:
conv_factor = 1.0
else:
if pos_unit == "Ang":
if pos_unit == "Ang" or pos_unit == "Angstrom":
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if pos_unit == "Ang" or pos_unit == "Angstrom":
if pos_unit in ("Ang", "Angstrom"):

else:
if box_unit == "Ang":
if box_unit == "Ang" or box_unit == "Angstrom":
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if box_unit == "Ang" or box_unit == "Angstrom":
if pos_unit in ("Ang", "Angstrom"):

if cell.shape == (3, 3):
temp_array = np.array(cell)
elif cell.shape == (3,):
temp_array = np.diag(cell)
Copy link
Collaborator

Choose a reason for hiding this comment

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

N.B. L. 288-289, can also use L. 280-281.

@@ -70,10 +88,10 @@ def __init__(self, h5_filename: Union[Path, str]):
coords = self._h5_file["/particles/all/position/value"][0, :, :]
try:
pos_unit = self._h5_file["/particles/all/position/value"].attrs["unit"]
except:
except Exception:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
except Exception:
except KeyError:

ny = data.shape[1]
nz = data.shape[2]
def array_to_3d_imagedata(data: np.ndarray, spacing: Tuple[float]):
nx, ny, nz = data.shape
image = vtk.vtkImageData()
image.SetDimensions(nx, ny, nz)
dx, dy, dz = spacing
Copy link
Collaborator

Choose a reason for hiding this comment

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

I realise it's not really part of this PR, but for later reference, l. 57-60 could be:

    for (i, j, k), val in np.ndenumerate(data):
        image.SetScalarComponentFromDouble(i, j, k, 0, val)

or even

    for loc, val in np.ndenumerate(data):
        image.SetScalarComponentFromDouble(*loc, 0, val)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OK, the code now uses np.ndenumerate, the former implementation.

self._surface.PickableOff()
new_surface = vtk.vtkActor()
new_surface.SetMapper(mapper)
new_surface.GetProperty().SetColor((r, g, b))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason to unpack the tuple only to repack it here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I added input validation in the widget to make sure the user can only input a tuple of 3 numbers, and removed unpacking from this part of the code.

Comment on lines +102 to +108
for label, widget in [
("Remove the surface with index: ", self._surface_spinbox)
]:
temp_box = QGroupBox(label, self)
temp_layout = QVBoxLayout(temp_box)
temp_layout.addWidget(widget)
layout.addWidget(temp_box)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Any reason for this to be split off?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The layout is populated sequentially. I needed to add a button, which is not wrapped in a QGroupBox, before this input field, but the logic of adding the input fields was the same before and after the button.

params = {
"atom_number": 0,
"fine_sampling": 3,
"surface_colour": (0, 0.5, 0.75),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this value want to be a top-level constant somewherE?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Now this dictionary is a top-level constant and is used both in TraceWidget.py and MolecularViewer.py.

@MBartkowiakSTFC
Copy link
Collaborator Author

I think that I addressed the points dealing with the 3D view and isosurface calculation. Some other comments about H5MD were covered by #655

Also, I tried to remove unused code from the 3D viewer and add/improve docstrings.

Do you think that these changes could be merged?

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