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

Demo fixes #100

Merged
merged 2 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ptychodus/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ def main() -> int:
fluorescenceInputFilePath: Path | None = None
fluorescenceOutputFilePath: Path | None = None

if parsedArgs.flourescence_input is not None:
if parsedArgs.fluorescence_input is not None:
fluorescenceInputFilePath = Path(parsedArgs.fluorescence_input.name)

if parsedArgs.flourescence_output is not None:
if parsedArgs.fluorescence_output is not None:
fluorescenceOutputFilePath = Path(parsedArgs.fluorescence_output.name)

return model.batchModeExecute(
Expand Down
1 change: 1 addition & 0 deletions ptychodus/api/patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .observer import Observable
from .tree import SimpleTreeNode

BooleanArrayType: TypeAlias = numpy.typing.NDArray[numpy.bool_]
DiffractionPatternArrayType: TypeAlias = numpy.typing.NDArray[numpy.integer[Any]]
DiffractionPatternIndexes: TypeAlias = numpy.typing.NDArray[numpy.integer[Any]]

Expand Down
14 changes: 14 additions & 0 deletions ptychodus/api/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pathlib import Path
from sys import getsizeof

from .constants import ELECTRON_VOLT_J, LIGHT_SPEED_M_PER_S, PLANCK_CONSTANT_J_PER_HZ
from .object import Object
from .probe import Probe
from .scan import Scan
Expand All @@ -18,6 +19,19 @@ class ProductMetadata:
probePhotonsPerSecond: float
exposureTimeInSeconds: float

@property
def probeEnergyInJoules(self) -> float:
return self.probeEnergyInElectronVolts * ELECTRON_VOLT_J

@property
def probeWavelengthInMeters(self) -> float:
hc_Jm = PLANCK_CONSTANT_J_PER_HZ * LIGHT_SPEED_M_PER_S

try:
return hc_Jm / self.probeEnergyInJoules
except ZeroDivisionError:
return 0.0

@property
def sizeInBytes(self) -> int:
sz = getsizeof(self.name)
Expand Down
3 changes: 2 additions & 1 deletion ptychodus/api/reconstructor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
from pathlib import Path

from .product import Product
from .patterns import DiffractionPatternArrayType
from .patterns import BooleanArrayType, DiffractionPatternArrayType


@dataclass(frozen=True)
class ReconstructInput:
patterns: DiffractionPatternArrayType
goodPixelMask: BooleanArrayType
product: Product


Expand Down
2 changes: 1 addition & 1 deletion ptychodus/model/analysis/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def __init__(
self._fluorescenceSettings = FluorescenceSettings(settingsRegistry)
self.fluorescenceEnhancer = FluorescenceEnhancer(
self._fluorescenceSettings,
dataMatcher,
productRepository,
upscalingStrategyChooser,
deconvolutionStrategyChooser,
fluorescenceFileReaderChooser,
Expand Down
30 changes: 14 additions & 16 deletions ptychodus/model/analysis/fluorescence.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from ptychodus.api.product import Product
from ptychodus.api.typing import RealArrayType

from ..reconstructor import DiffractionPatternPositionMatcher
from ..product import ProductRepository
from .settings import FluorescenceSettings

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -63,19 +63,19 @@ def get_axis_weights_and_indexes(


class VSPILinearOperator(LinearOperator):
def __init__(self, product: Product, xrf_nchannels: int) -> None:
def __init__(self, product: Product) -> None:
"""
M: number of XRF positions
N: number of ptychography object pixels
P: number of XRF channels

A[M,N] * X[N,P] = B[M,P]
"""
super().__init__(float, (len(product.scan), xrf_nchannels))
super().__init__(float, (len(product.scan), len(product.scan)))
self._product = product

def matmat(self, X: RealArrayType) -> RealArrayType:
AX = numpy.zeros(self.shape, dtype=self.dtype)
def _matvec(self, X: RealArrayType) -> RealArrayType:
AX = numpy.zeros(X.shape, dtype=self.dtype)

probeGeometry = self._product.probe.getGeometry()
dx_p_m = probeGeometry.pixelWidthInMeters
Expand Down Expand Up @@ -103,7 +103,7 @@ def matmat(self, X: RealArrayType) -> RealArrayType:
i_nz = numpy.ravel_multi_index(list(zip(IY.flat, IX.flat)), objectShape)
X_nz = X.take(i_nz, axis=0)

AX[index, :] = numpy.matmul(numpy.outer(wy, wx).ravel(), X_nz)
AX[index] = numpy.matmul(numpy.outer(wy, wx).ravel(), X_nz)

return AX

Expand All @@ -115,7 +115,7 @@ class FluorescenceEnhancer(Observable, Observer):
def __init__(
self,
settings: FluorescenceSettings,
dataMatcher: DiffractionPatternPositionMatcher, # FIXME match XRF too
productRepository: ProductRepository,
upscalingStrategyChooser: PluginChooser[UpscalingStrategy],
deconvolutionStrategyChooser: PluginChooser[DeconvolutionStrategy],
fileReaderChooser: PluginChooser[FluorescenceFileReader],
Expand All @@ -124,7 +124,7 @@ def __init__(
) -> None:
super().__init__()
self._settings = settings
self._dataMatcher = dataMatcher
self._productRepository = productRepository
self._upscalingStrategyChooser = upscalingStrategyChooser
self._deconvolutionStrategyChooser = deconvolutionStrategyChooser
self._fileReaderChooser = fileReaderChooser
Expand Down Expand Up @@ -152,7 +152,7 @@ def setProduct(self, productIndex: int) -> None:
self.notifyObservers()

def getProductName(self) -> str:
return self._dataMatcher.getProductName(self._productIndex)
return self._productRepository[self._productIndex].getName()

def getOpenFileFilterList(self) -> Sequence[str]:
return self._fileReaderChooser.getDisplayNameList()
Expand Down Expand Up @@ -222,14 +222,12 @@ def enhanceFluorescence(self) -> None:
if self._measured is None:
raise ValueError('Fluorescence dataset not loaded!')

reconstructInput = self._dataMatcher.matchDiffractionPatternsWithPositions(
self._productIndex
)
product = self._productRepository[self._productIndex].getProduct()
element_maps: list[ElementMap] = list()

if self._settings.useVSPI.getValue():
measured_emaps = self._measured.element_maps
A = VSPILinearOperator(reconstructInput.product, len(measured_emaps))
A = VSPILinearOperator(product)
B = numpy.stack([b.counts_per_second.flatten() for b in measured_emaps]).T
X, info = gmres(A, B, atol=1e-5) # TODO expose atol

Expand All @@ -246,8 +244,8 @@ def enhanceFluorescence(self) -> None:

for emap in self._measured.element_maps:
logger.info(f'Enhancing "{emap.name}"')
emap_upscaled = upscaler(emap, reconstructInput.product)
emap_enhanced = deconvolver(emap_upscaled, reconstructInput.product)
emap_upscaled = upscaler(emap, product)
emap_enhanced = deconvolver(emap_upscaled, product)
element_maps.append(emap_enhanced)

self._enhanced = FluorescenceDataset(
Expand All @@ -258,7 +256,7 @@ def enhanceFluorescence(self) -> None:
self.notifyObservers()

def getPixelGeometry(self) -> PixelGeometry:
return self._dataMatcher.getObjectPlanePixelGeometry(self._productIndex)
return self._productRepository[self._productIndex].getGeometry().getPixelGeometry()

def getEnhancedElementMap(self, channelIndex: int) -> ElementMap:
if self._enhanced is None:
Expand Down
10 changes: 10 additions & 0 deletions ptychodus/model/patterns/active.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from ptychodus.api.geometry import ImageExtent
from ptychodus.api.patterns import (
BooleanArrayType,
DiffractionDataset,
DiffractionMetadata,
DiffractionPatternArray,
Expand Down Expand Up @@ -141,6 +142,15 @@ def insertArray(self, array: DiffractionPatternArray) -> None:

self._changedEvent.set()

def getGoodPixelMask(self) -> BooleanArrayType: # FIXME
return numpy.full(
(
self._diffractionPatternSizer.getHeightInPixels(),
self._diffractionPatternSizer.getWidthInPixels(),
),
True,
)

def getAssembledIndexes(self) -> Sequence[int]:
indexes: list[int] = list()

Expand Down
8 changes: 4 additions & 4 deletions ptychodus/model/product/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def copyScan(self, sourceIndex: int, destinationIndex: int) -> None:
logger.warning(f'Failed to access destination scan {destinationIndex} for copying!')
return

destinationItem.assign(sourceItem)
destinationItem.assignItem(sourceItem)

def getSaveFileFilterList(self) -> Sequence[str]:
return self._builderFactory.getSaveFileFilterList()
Expand Down Expand Up @@ -220,7 +220,7 @@ def copyProbe(self, sourceIndex: int, destinationIndex: int) -> None:
logger.warning(f'Failed to access destination probe {destinationIndex} for copying!')
return

destinationItem.assign(sourceItem)
destinationItem.assignItem(sourceItem)

def getSaveFileFilterList(self) -> Sequence[str]:
return self._builderFactory.getSaveFileFilterList()
Expand Down Expand Up @@ -328,7 +328,7 @@ def copyObject(self, sourceIndex: int, destinationIndex: int) -> None:
logger.warning(f'Failed to access destination object {destinationIndex} for copying!')
return

destinationItem.assign(sourceItem)
destinationItem.assignItem(sourceItem)

def getSaveFileFilterList(self) -> Sequence[str]:
return self._builderFactory.getSaveFileFilterList()
Expand Down Expand Up @@ -370,7 +370,7 @@ def insertNewProduct(
likeIndex: int = -1,
) -> int:
return self._repository.insertNewProduct(
name,
name=name,
comments=comments,
detectorDistanceInMeters=detectorDistanceInMeters,
probeEnergyInElectronVolts=probeEnergyInElectronVolts,
Expand Down
18 changes: 18 additions & 0 deletions ptychodus/model/product/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,24 @@ def __init__(
self._addGroup('probe', self._probe, observe=True)
self._addGroup('object', self._object, observe=True)

def assignItem(self, item: ProductRepositoryItem, *, notify: bool = True) -> None:
self._metadata.assignItem(item.getMetadata())
self._scan.assignItem(item.getScan())
self._probe.assignItem(item.getProbe())
self._object.assignItem(item.getObject())
self._costs = list(item.getCosts())

if notify:
self._parent.handleCostsChanged(self)

def assign(self, product: Product) -> None:
self._metadata.assign(product.metadata)
self._scan.assign(product.scan)
self._probe.assign(product.probe)
self._object.assign(product.object_)
self._costs = list(product.costs)
self._parent.handleCostsChanged(self)

def syncToSettings(self) -> None:
self._metadata.syncToSettings()
self._scan.syncToSettings()
Expand Down
10 changes: 9 additions & 1 deletion ptychodus/model/product/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,22 @@ def __init__(

self._index = -1

def assign(self, item: MetadataRepositoryItem) -> None:
def assignItem(self, item: MetadataRepositoryItem, *, notify: bool = True) -> None:
self.setName(item.getName())
self.comments.setValue(item.comments.getValue())
self.detectorDistanceInMeters.setValue(item.detectorDistanceInMeters.getValue())
self.probeEnergyInElectronVolts.setValue(item.probeEnergyInElectronVolts.getValue())
self.probePhotonsPerSecond.setValue(item.probePhotonsPerSecond.getValue())
self.exposureTimeInSeconds.setValue(item.exposureTimeInSeconds.getValue())

def assign(self, metadata: ProductMetadata) -> None:
self.setName(metadata.name)
self.comments.setValue(metadata.comments)
self.detectorDistanceInMeters.setValue(metadata.detectorDistanceInMeters)
self.probeEnergyInElectronVolts.setValue(metadata.probeEnergyInElectronVolts)
self.probePhotonsPerSecond.setValue(metadata.probePhotonsPerSecond)
self.exposureTimeInSeconds.setValue(metadata.exposureTimeInSeconds)

def syncToSettings(self) -> None:
for parameter in self.parameters().values():
parameter.syncValueToParent()
Expand Down
2 changes: 2 additions & 0 deletions ptychodus/model/product/object/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,10 @@ def __init__(
super().__init__(settings, 'from_file')
self._settings = settings
self.filePath = settings.filePath.copy()
self.filePath.setValue(filePath)
self._addParameter('file_path', self.filePath)
self.fileType = settings.fileType.copy()
self.fileType.setValue(fileType)
self._addParameter('file_type', self.fileType)
self._fileReader = fileReader

Expand Down
8 changes: 6 additions & 2 deletions ptychodus/model/product/object/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from ptychodus.api.observer import Observable
from ptychodus.api.parametric import ParameterGroup

from .builder import ObjectBuilder
from .builder import FromMemoryObjectBuilder, ObjectBuilder
from .settings import ObjectSettings

logger = logging.getLogger(__name__)
Expand All @@ -32,10 +32,14 @@ def __init__(

self._rebuild()

def assign(self, item: ObjectRepositoryItem) -> None:
def assignItem(self, item: ObjectRepositoryItem) -> None:
self.layerDistanceInMeters.setValue(item.layerDistanceInMeters.getValue(), notify=False)
self.setBuilder(item.getBuilder().copy())

def assign(self, object_: Object) -> None:
builder = FromMemoryObjectBuilder(self._settings, object_)
self.setBuilder(builder)

def syncToSettings(self) -> None:
for parameter in self.parameters().values():
parameter.syncValueToParent()
Expand Down
2 changes: 2 additions & 0 deletions ptychodus/model/product/probe/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,10 @@ def __init__(
super().__init__(settings, 'from_file')
self._settings = settings
self.filePath = settings.filePath.copy()
self.filePath.setValue(filePath)
self._addParameter('file_path', self.filePath)
self.fileType = settings.fileType.copy()
self.fileType.setValue(fileType)
self._addParameter('file_type', self.fileType)
self._fileReader = fileReader

Expand Down
13 changes: 10 additions & 3 deletions ptychodus/model/product/probe/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
from ptychodus.api.parametric import ParameterGroup
from ptychodus.api.probe import Probe, ProbeGeometryProvider

from .builder import ProbeBuilder
from .builder import FromMemoryProbeBuilder, ProbeBuilder
from .multimodal import MultimodalProbeBuilder
from .settings import ProbeSettings

logger = logging.getLogger(__name__)

Expand All @@ -15,11 +16,13 @@ class ProbeRepositoryItem(ParameterGroup):
def __init__(
self,
geometryProvider: ProbeGeometryProvider,
settings: ProbeSettings,
builder: ProbeBuilder,
additionalModesBuilder: MultimodalProbeBuilder,
) -> None:
super().__init__()
self._geometryProvider = geometryProvider
self._settings = settings
self._builder = builder
self._additionalModesBuilder = additionalModesBuilder
self._probe = Probe()
Expand All @@ -29,14 +32,19 @@ def __init__(

self._rebuild()

def assign(self, item: ProbeRepositoryItem) -> None:
def assignItem(self, item: ProbeRepositoryItem) -> None:
self._removeGroup('additional_modes')
self._additionalModesBuilder.removeObserver(self)
self._additionalModesBuilder = item.getAdditionalModesBuilder().copy()
self._additionalModesBuilder.addObserver(self)
self._addGroup('additional_modes', self._additionalModesBuilder, observe=True)

self.setBuilder(item.getBuilder().copy())
self._rebuild()

def assign(self, probe: Probe) -> None:
builder = FromMemoryProbeBuilder(self._settings, probe)
self.setBuilder(builder)

def syncToSettings(self) -> None:
for parameter in self.parameters().values():
Expand All @@ -57,7 +65,6 @@ def setBuilder(self, builder: ProbeBuilder) -> None:
self._builder = builder
self._builder.addObserver(self)
self._addGroup('builder', self._builder, observe=True)
self._rebuild()

def _rebuild(self) -> None:
try:
Expand Down
Loading