Skip to content

initializing spectroscopy #196

Open
smribet wants to merge 201 commits into
devfrom
spectroscopy
Open

initializing spectroscopy #196
smribet wants to merge 201 commits into
devfrom
spectroscopy

Conversation

@smribet

@smribet smribet commented Mar 19, 2026

Copy link
Copy Markdown
Collaborator

initializing spectroscopy. more details coming soon.

smribet and others added 30 commits September 3, 2025 16:59
@cophus cophus self-requested a review June 11, 2026 12:28

@cophus cophus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Really nice to see the spectroscopy module come together! I went through the data-layout side carefully and the (E, x, y), (scan, scan, energy) refactor is consistent across both EELS and EDS. Energy is the last axis everywhere, including the torch fitting path.

One thing that would make it more robust: the two scan axes get three different names across the module (n_x, n_y / n_y, n_x / w, h), and the EELS class docstring says (scan_y, scan_x, energy). The indexing is correct everywhere, so these aren't bugs, but it makes the code hard to read and easy to break later. Could we standardize on one convention?

Proposed: arrays are (scan_row, scan_col, energy); unpack as scan_row, scan_col, n_energy = self.array.shape; loop indices i_row, i_col; spectra as self.array[i_row, i_col, :].

self,
title: str | None = None,
returnfig: bool = False,
**kwargs,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

**kwargs presumably should be passed through to plot?

Comment thread src/quantem/core/io/file_readers.py Outdated
from quantem.core.datastructures import Dataset3d as Dataset3d
from quantem.core.datastructures import Dataset4dstem as Dataset4dstem
from quantem.spectroscopy import (
Dataset3deds as Dataset3deds,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Are we agreed on naming it "eds?" I prefer the more explicit name "xeds" i.e. x-ray energy dispersive spectrometry.

f"expected 4D. Shape: {imported_data['data'].shape}"
)
else:
# Automatically find first 3D dataset

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We could add a function to print out the dataset indices, shapes, and other metadata line names if available.

)


class Dataset3deds(Dataset3dspectroscopy):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As mentioned above, maybe should be:
Dataset3dxeds

if return_maps:
return images, titles

def Integrate(self, spec, width=0.15, return_maps=False, show=True, **kwargs):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we should support "Integrate" and "integrate." Generally methods should all be lowercase.

>>> import numpy as np
>>> from quantem.core.datastructures import Dataset1d
>>> arr = np.random.rand(10)
>>> data = Dataset3d.from_array(arr)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

dataset1d.py:99, 143-146, 186-216, copy-paste docstring errors in the new Dataset1d

abundance_maps=abundance_maps,
element_names=list(global_model.peak_model.element_names),
)
if hasattr(self, "_spectrum_images_pytorch"):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

dataset3deds.py:3431-3441 — dead if/else: the else sets {} then immediately rebuilds from {}, so both branches do the same thing. Collapses to:

base = getattr(self, "_spectrum_images_pytorch", {})
self._spectrum_images_pytorch = {**base, **pytorch_spectrum_images}

Only returned when *return_maps* is ``True``.
"""
if elements is None:
if self.model_elements is None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

dataset3deds.py:487, if self.model_elements is None: never fires (it's initialized to an empty _ModelElementsDict, never None), so the "elements must be specified" guard is dead and users hit a confusing downstream error instead. Use if not self.model_elements: (matches the sibling checks at :2815, :3091).

return background


class ExponentialBackground(nn.Module):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ExponentialBackground is never used anywhere, and its decay acts on the raw (un-normalized) energy axis unlike PolynomialBackground. Remove it, or wire it up consistently.

Comment thread src/quantem/spectroscopy/utils.py Outdated
continue
energy_kev = energy_ev / 1000.0

# Use the normalized CSV column as the X-ray line weight.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

_parse_float(row, ("col4_norm", "weight", ...)) references a col4_norm column that no shipped CSV has; the "normalized CSV column" comment is misleading. Works only by falling through to weight, drop the stale key.

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.

4 participants