Commit d1ae9d81 authored by JosefBrandt's avatar JosefBrandt

AnotherStepOfRefactoringDone
parent 36904913
......@@ -22,7 +22,7 @@ If not, see <https://www.gnu.org/licenses/>.
from PyQt5 import QtCore, QtGui, QtWidgets
import numpy as np
import sys
import sys, os
import random
import colorsys
......@@ -34,7 +34,8 @@ from .analysiswidgets import ExpExcelDialog, AdditiveViewer, ParticleTypeView
from .loadresults import LoadWITecResults
from .particleeditor import ParticleEditor
from .database import DataBaseWindow
from .datastats import DataStats
#from .datastats import DataStats
from analysis import importSpectra
try:
from .sqlexport import SQLExport
sqlEnabled = True
......@@ -55,8 +56,8 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.parent = parent
self.dataset = dataset
self.particleContainer = dataset.particleContainer
self.datastats = DataStats(dataset)
self.editor = ParticleEditor(self.datastats, self)
# self.datastats = DataStats(dataset)
self.editor = ParticleEditor(self.particleContainer, self)
# self.additivePlot = None
self.importWindow = None
......@@ -226,7 +227,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.createActions()
self.createMenus()
self.initializeSpectraPlot()
self.loadSpectraAndInitializeSpecPlot()
self.applyHQIThresholdToResults()
def createActions(self):
......@@ -295,7 +296,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.menuBar().addMenu(self.dispMenu)
self.menuBar().addMenu(self.refMenu)
self.menuBar().addMenu(self.exportMenu)
def launchDBManager(self):
if self.dbWin.isHidden():
self.dbWin.show()
......@@ -311,8 +312,32 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.refSelector.addItems(self.dbWin.activeDatabase.spectraNames)
self.refSelector.setDisabled(False)
def initializeSpectraPlot(self): #formerly updateData(self)....
self.particleContainer.loadSpectraFromDisk()
def loadSpectraAndInitializeSpecPlot(self): #formerly updateData(self)....
def tryLoadingNumpySpecFile():
specPath = self.dataset.getSpectraFileName()
if os.path.exists(specPath):
return np.load(specPath)
else:
raise ImportError
try:
self.spectra = tryLoadingNumpySpecFile()
except ImportError:
fname = QtWidgets.QFileDialog.getOpenFileName(QtWidgets.QWidget(), 'Select Spectra File', self.dataset.path, 'text file (*.txt)')[0]
try:
self.spectra, spectraNames = importSpectra.importWITecSpectra(fname)
except ImportError:
try:
self.spectra, spectraNames = importSpectra.importRenishawSpectra(fname)
except ImportError:
self.spectra, spectraNames = importSpectra.importPerkinElmerSpectra(fname)
if self.spectra is None:
raise ImportError
else:
np.save(self.dataset.getSpectraFileName(), self.spectra)
self.specCanvas.draw()
# self.loadParticleData()
......@@ -332,7 +357,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
# self.formatResults()
def importTrueMatchResults(self):
self.importWindow = LoadWITecResults(self.datastats, self)
self.importWindow = LoadWITecResults(self.particleContainer, self)
self.importWindow.exec()
# @QtCore.pyqtSlot(int)
......@@ -435,32 +460,30 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.createPolymerOverlay()
def exportToExcel(self):
expWin = ExpExcelDialog(self.datastats, self)
expWin = ExpExcelDialog(self.particleContainer, self)
expWin.exec()
def exportToSQL(self):
sqlexp = SQLExport(self.datastats, self)
sqlexp = SQLExport(self.particleContainer, self)
sqlexp.exec()
def updateSpecPlot(self, centerOn=True, highlightContour=True):
#draw Sample Spectrum
specIndex = self.currentSpectrumIndex
spectra = self.particleContainer.spectra
particlestats = self.datastats.getParticleStats()
self.spec_ax.axis("on")
self.spec_ax.clear()
self.spec_ax.plot(spectra[:, 0], spectra[:, specIndex+1])
self.spec_ax.plot(self.spectra[:, 0], self.spectra[:, specIndex+1])
self.spec_ax.tick_params(axis='both', which='both', labelsize=15)
self.spec_ax.set_xlabel('Wavenumber (cm-1)', fontsize = 15)
self.spec_ax.set_ylabel('Counts', fontsize = 15)
self.spec_ax.set_title('ScanPoint Number {}, Size = {} µm'.format(specIndex+1,
np.round(particlestats[self.currentParticleIndex][2], 1)))
self.spec_ax.set_xbound(100, (3400 if spectra[-1, 0] > 3400 else spectra[-1, 0]))
wavenumber_diff = list(spectra[:, 0]-100)
self.particleContainer.getSizeOfParticleByIndex(self.currentParticleIndex)))
self.spec_ax.set_xbound(100, (3400 if self.spectra[-1, 0] > 3400 else self.spectra[-1, 0]))
wavenumber_diff = list(self.spectra[:, 0]-100)
y_start = wavenumber_diff.index(min(wavenumber_diff))
y_min = min(spectra[y_start:, specIndex+1])
y_max = max(spectra[y_start:, specIndex+1])
y_min = min(self.spectra[y_start:, specIndex+1])
y_max = max(self.spectra[y_start:, specIndex+1])
self.spec_ax.set_ybound(0.9*y_min, 1.1*y_max)
#draw Reference
......@@ -472,7 +495,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.ref_ax.plot(ref[:, 0], ref[:, 1], color = 'r')
self.ref_ax.set_ylabel('Ref. Intensity', fontsize = 15, color = 'r')
self.ref_ax.tick_params('y', colors = 'r')
self.ref_ax.set_xbound(100, (3400 if spectra[-1, 0] > 3400 else spectra[-1, 0]))
self.ref_ax.set_xbound(100, (3400 if self.spectra[-1, 0] > 3400 else self.spectra[-1, 0]))
# wavenumber_diff = list(ref[:, 0]-100)
# y_start = wavenumber_diff.index(min(wavenumber_diff))
# y_min = min(ref[y_start:, specIndex+1])
......@@ -486,50 +509,53 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.lastSpectrumInFocus = specIndex
def selectContour(self, index, centerOn=True):
uniquePolymers = self.datastats.getUniquePolymers()
if uniquePolymers is not None:
#the index is the contour index, find particle index:
specIndex = self.datastats.particles2spectra[index][0] #select first spectrum of partoicle
self.currentParticleIndex = index
self.currentSpectrumIndex = specIndex
selectedPolymer = self.datastats.currentPolymers[specIndex]
try:
self.polymerIndex = uniquePolymers.index(selectedPolymer)
except:
print(selectedPolymer)
raise
#subparticleIndex
partIndicesOfThatPolymer = self.datastats.indices[self.polymerIndex]
subPartInd = partIndicesOfThatPolymer.index(index)
#disconnect analysis widgets:
self.particleSelector.valueChanged.disconnect()
self.spectrumSelector.valueChanged.disconnect()
self.polymerComboBox.currentIndexChanged.disconnect()
#set widgets...
self.particleSelector.setValue(subPartInd+1)
self.particleSelector.setMaximum(len(partIndicesOfThatPolymer))
self.spectrumSelector.setValue(1)
self.spectrumSelector.setMaximum(len(self.datastats.particles2spectra[index]))
selectedPolymer = self.datastats.currentPolymers[specIndex]
self.polymerIndex = uniquePolymers.index(selectedPolymer)
self.polymerComboBox.setCurrentIndex(self.polymerIndex)
#reconnect all widgets:
self.particleSelector.valueChanged.connect(self.selectParticle)
self.spectrumSelector.valueChanged.connect(self.selectSpectrum)
self.polymerComboBox.currentIndexChanged.connect(self.displayNewPolymerType)
self.updateSpecPlot(centerOn=centerOn)
print('select Contour in analysisview not yet refactored')
# uniquePolymers = self.particleContainer.getUniquePolymers()
# if uniquePolymers is not None:
# #the index is the contour index, find particle index:
# specIndex = self.datastats.particles2spectra[index][0] #select first spectrum of partoicle
# self.currentParticleIndex = index
# self.currentSpectrumIndex = specIndex
#
# selectedPolymer = self.datastats.currentPolymers[specIndex]
# try:
# self.polymerIndex = uniquePolymers.index(selectedPolymer)
# except:
# print(selectedPolymer)
# raise
#
# #subparticleIndex
# partIndicesOfThatPolymer = self.datastats.indices[self.polymerIndex]
# subPartInd = partIndicesOfThatPolymer.index(index)
#
# #disconnect analysis widgets:
# self.particleSelector.valueChanged.disconnect()
# self.spectrumSelector.valueChanged.disconnect()
# self.polymerComboBox.currentIndexChanged.disconnect()
#
# #set widgets...
# self.particleSelector.setValue(subPartInd+1)
# self.particleSelector.setMaximum(len(partIndicesOfThatPolymer))
#
# self.spectrumSelector.setValue(1)
# self.spectrumSelector.setMaximum(len(self.datastats.particles2spectra[index]))
#
# selectedPolymer = self.datastats.currentPolymers[specIndex]
# self.polymerIndex = uniquePolymers.index(selectedPolymer)
# self.polymerComboBox.setCurrentIndex(self.polymerIndex)
#
# #reconnect all widgets:
# self.particleSelector.valueChanged.connect(self.selectParticle)
# self.spectrumSelector.valueChanged.connect(self.selectSpectrum)
# self.polymerComboBox.currentIndexChanged.connect(self.displayNewPolymerType)
#
# self.updateSpecPlot(centerOn=centerOn)
def displayNewPolymerType(self, resetCurrentIndex=True):
self.polymerIndex = self.polymerComboBox.currentIndex()
self.particleSelector.setMaximum(len(self.datastats.indices[self.polymerIndex]))
polymerName = self.polymerComboBox.currentText()
# self.particleSelector.setMaximum(len(self.datastats.indices[self.polymerIndex]))
self.particleSelector.setMaximum(self.particleContainer.getNumberOfParticlesOfAssignment(polymerName))
if resetCurrentIndex:
self.particleSelector.setValue(1)
self.spectrumSelector.setValue(1)
......@@ -586,7 +612,8 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.sizeHist_ax.axis('on')
self.bins = np.logspace(0.1, 3, 20)
self.sizes = [i[0] if np.isnan(i[2]) else i[2] for i in self.datastats.getParticleStats()] #extract long size (if ellipse fit is nan -> box fit)
self.sizes = self.particleContainer.getSizesOfAllParticles()
# self.sizes = [i[0] if np.isnan(i[2]) else i[2] for i in self.datastats.getParticleStats()] #extract long size (if ellipse fit is nan -> box fit)
sizehist = np.histogram(self.sizes, self.bins)
self.totalhistx = []
for i in range(19):
......@@ -603,16 +630,17 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.sizeHist_ax.semilogx(self.totalhistx, self.totalhisty, label = 'total')
#get selected boxes
selected = []
for i in self.polymerCheckBoxes:
if i.isChecked() == True:
selected.append(i.text())
for i in selected:
sizes = [self.sizes[index] for index in range(len(self.sizes))
if self.datastats.currentPolymers[index] == i]
selectedTypes = []
for checkbox in self.polymerCheckBoxes:
if checkbox.isChecked() == True:
selectedTypes.append(checkbox.text())
for polymType in selectedTypes:
sizes = self.particleContainer.getSizesOfParticleType(polymType)
# sizes = [self.sizes[index] for index in range(len(self.sizes))
# if self.datastats.currentPolymers[index] == i]
sizehist = np.histogram(sizes, self.bins)
self.sizeHist_ax.semilogx(self.totalhistx, sizehist[0], label = i, color = self.getColorFromName(i, base255 = False))
self.sizeHist_ax.semilogx(self.totalhistx, sizehist[0], label = polymType, color = self.getColorFromName(polymType, base255 = False))
self.sizeHist_ax.legend(prop = {'size': 15})
self.sizeHist_ax.tick_params(axis='both', which='both', labelsize=15)
......
......@@ -30,16 +30,17 @@ from matplotlib.figure import Figure
class ExpExcelDialog(QtWidgets.QDialog):
def __init__(self, datastats, parent):
def __init__(self, dataset, parent):
super(ExpExcelDialog, self).__init__(parent)
self.setWindowTitle('Export Options')
self.setGeometry(200, 200, 300, 300)
self.datastats = datastats
self.particles = self.datastats.getParticleStats()
self.polymers = self.datastats.particleContainer
self.additives = self.datastats.currentAdditives
self.hqis = self.datastats.hqis
self.dataset = dataset
self.particleContainer = self.dataset.particleContainer
# self.particles = self.datastats.getParticleStats()
self.polymers = self.particleContainer.getListOfParticleAssignments
# self.additives = self.datastats.currentAdditives
self.hqis = self.particleContainer.getListOfHighestHQIs
self.layout = QtWidgets.QHBoxLayout()
self.setLayout(self.layout)
......@@ -51,7 +52,7 @@ class ExpExcelDialog(QtWidgets.QDialog):
self.exportOptions = ['Polymer Type (mandatory)', 'Additives', 'Long Size (µm)', 'Short Size (µm)', 'Area (µm²)', 'HQI', 'Size Classes']
self.checkBoxes = []
self.sizeClasses = [5, 10, 20, 50, 100, 1e6]
self.directory = self.datastats.dataset.path
self.directory = self.dataset.path
for index, option in enumerate(self.exportOptions):
self.checkBoxes.append(QtWidgets.QCheckBox(self))
......@@ -69,7 +70,7 @@ class ExpExcelDialog(QtWidgets.QDialog):
excelvbox.addWidget(self.checkBoxes[-1])
self.xlsFileName = QtWidgets.QLineEdit()
self.xlsFileName.setText('{}_Particle_List'.format(self.datastats.dataset.name))
self.xlsFileName.setText('{}_Particle_List'.format(self.dataset.name))
excelvbox.addWidget(QtWidgets.QLabel('Filename:'))
excelvbox.addWidget(self.xlsFileName)
......@@ -86,7 +87,8 @@ class ExpExcelDialog(QtWidgets.QDialog):
def toExcel(self):
requiredcolumns = []
self.sizes = np.round(np.array([i[0] if np.isnan(i[2]) else i[2] for i in self.particles]), 1)
self.sizes = self.particleContainer.getSizesOfAllParticles()
for box in self.checkBoxes:
if box.isChecked() == True:
if box.text() != 'Size Classes':
......@@ -94,9 +96,10 @@ class ExpExcelDialog(QtWidgets.QDialog):
if box.text() == 'Long Size (µm)':
longSize = self.sizes
elif box.text() == 'Short Size (µm)':
shortSize = np.round(np.array([i[1] if np.isnan(i[3]) else i[3] for i in self.particles]), 1)
self.shortSizes = self.particleContainer.getShortSizesOfAllParticles()
elif box.text() == 'Area (µm²)':
area = np.array([np.round(float(entry[4]), 1) for entry in self.particles])
else:
requiredcolumns.append('0 - 5 µm')
requiredcolumns.append('5 - 10 µm')
......@@ -117,17 +120,16 @@ class ExpExcelDialog(QtWidgets.QDialog):
for colindex, column in enumerate(requiredcolumns):
if column == 'Polymer Type (mandatory)':
polymertypes[rowindex:rowindex+numentries] = self.polymers[indices]
if column == 'Additives':
finalData[rowindex:rowindex+numentries, colindex-1] = self.additives[indices]
# if column == 'Additives':
# finalData[rowindex:rowindex+numentries, colindex-1] = self.additives[indices]
if column == 'Long Size (µm)':
finalData[rowindex:rowindex+numentries, colindex-1] = longSize[indices]
if column == 'Short Size (µm)':
finalData[rowindex:rowindex+numentries, colindex-1] = shortSize[indices]
finalData[rowindex:rowindex+numentries, colindex-1] = self.shortSizes[indices]
if column == 'Area (µm²)':
finalData[rowindex:rowindex+numentries, colindex-1] = area[indices]
# hit quality index array does not match the data size if particles have been combined
#if column == 'HQI':
# finalData[rowindex:rowindex+numentries, colindex-1] = self.hqis[indices]
if column == 'HQI':
finalData[rowindex:rowindex+numentries, colindex-1] = self.hqis[indices]
if '> 100 µm' in requiredcolumns:
##append size classes
......
......@@ -21,9 +21,9 @@ If not, see <https://www.gnu.org/licenses/>.
import os
import numpy as np
import operator
from PyQt5 import QtWidgets
from PyQt5 import QtWidgets, QtCore
import importSpectra
from analysis import importSpectra
try:
from dataset import loadData, recursiveDictCompare
print('exported dataset methods from datastats')
......@@ -32,9 +32,11 @@ except:
class ParticleContainer(object):
def __init__(self, parent):
super(ParticleContainer, self).__init__()
self.parent = parent
self.particles = []
self.spectra = None
self.inconsistentParticles = []
self.typeHistogram = None
......@@ -43,31 +45,6 @@ class ParticleContainer(object):
for i in range(numParticles):
self.particles.append(Particle(i))
def loadSpectraFromDisk(self, fname):
try:
self.spectra = self.tryLoadingNumpySpecFile()
except ImportError:
fname = QtWidgets.QFileDialog.getOpenFileName(self.parent, 'Select Spectra File', self.dataset.path, 'text file (*.txt)')[0]
try:
self.spectra, spectraNames = importSpectra.importWITecSpectra(fname)
except ImportError:
try:
self.spectra, spectraNames = importSpectra.importRenishawSpectra(fname)
except ImportError:
self.spectra, spectraNames = importSpectra.importPerkinElmerSpectra(fname)
if self.spectra is None:
raise ImportError
def tryLoadingNumpySpecFile(self):
specPath = self.parent.getSpectraFileName()
if os.path.exists(specPath):
return np.load(specPath)
else:
raise ImportError
def setParticlecontours(self, contours):
assert len(self.particles) == len(contours)
for index, particle in enumerate(self.particles):
......@@ -77,34 +54,116 @@ class ParticleContainer(object):
assert len(self.particles) == len(particlestats)
#particlestats is list of [long, short, longellipse, shortellipse, cv2.contourArea(cnt)]
for index, particle in enumerate(self.particles):
particle.longSize_box = particlestats[0]
particle.shortSize_box = particlestats[1]
particle.longSize_ellipse = particlestats[2]
particle.shortSize_ellipse = particlestats[3]
particle.area = particlestats[4]
particle.longSize_box = float(particlestats[index][0])
particle.shortSize_box = float(particlestats[index][1])
particle.longSize_ellipse = float(particlestats[index][2])
particle.shortSize_ellipse = float(particlestats[index][3])
particle.area = float(particlestats[index][4])
def applyPixelScaleToParticleStats(self, pixelscale):
for index, particle in enumerate(self.particles):
particle.longSize_box *= pixelscale
particle.shortSize_box *= pixelscale
particle.longSize_ellipse *= pixelscale
particle.shortSize_ellipse *= pixelscale
particle.area *= pixelscale**2
def applyHQITresholdToParticles(self, minHQI):
for particle in self.particles:
particle.applyHQITresholdToMeasurements(minHQI)
def applyAssignmentListToParticleMeasurements(self, assignmentList):
indicesOfTransferredAssignments = []
for particle in self.particles:
for meas in particle.getMeasurements():
scanIndex = meas.getScanIndex()
meas.setAssignment(assignmentList[scanIndex])
indicesOfTransferredAssignments.append(scanIndex)
assert np.unique(indicesOfTransferredAssignments) == np.unique((range(len(assignmentList))))
def applyHQIListToParticleMeasurements(self, hqiList):
indicesOfTransferredAssignments = []
for particle in self.particles:
for meas in particle.getMeasurements():
scanIndex = meas.getScanIndex()
meas.setAssignment(hqiList[scanIndex])
indicesOfTransferredAssignments.append(scanIndex)
assert np.unique(indicesOfTransferredAssignments) == np.unique((range(len(hqiList))))
def getNumberOfParticles(self):
return len(self.particles)
def getParticleContours(self):
contours = [part.contour for part in self.particles]
return contours
def getParticleContoursByIndex(self, partIndices):
contours = []
for part in self.particles:
if part.index in partIndices:
contours.append(part.contour)
return contours
def getMeasurementPixelCoords(self):
coords = []
for particle in self.particles:
for measurement in particle.measurements:
for measurement in particle.getMeasurements():
coords.append([measurement.pixelcoord_x, measurement.pixelcoord_y])
return coords
def getNumberOfParticlesOfAssignment(self, assignment):
num = 0
for particle in self.particles:
if particle.getParticleAssignment() == assignment:
num += 1
return num
def getListOfParticleAssignments(self):
particleAssignments = []
for particle in self.particles:
particleAssignments.append(particle.getParticleAssignment())
return particleAssignments
def getListOfParticleSizes(self):
def getListOfHighestHQIs(self):
hqis = []
for particle in self.particles:
hqis.append(particle.getHighestHQI())
return hqis
def getInconsistentParticles(self): #i.e., particles that have multiple measurements with different assignments
self.inconsistentParticles = []
for particle in self.particles:
if not particle.measurementsHaveSameOrigAssignment():
self.inconsistentParticles.append(particle)
if len(self.inconsistentParticles) > 0:
print('Warning, inconsistent particles found!')
for particle in self.inconsistentParticles:
print(f'Particle with index {particle.index} has the following assignments:')
for assignment in particle.getOrigMeasurementAssignments():
print(assignment)
return self.inconsistentParticles
def getSizesOfAllParticles(self):
particleSizes = []
for particle in self.particles:
particleSizes.append(particle.getParticleSize())
def getShortSizesOfAllParticles(self):
shortSizes = []
for particle in self.particles:
shortSizes.append(particle.getShortParticleSize())
def getSizesOfParticleType(self, assignment):
particleSizes = []
for particle in self.particles:
if particle.getParticleAssignment() == assignment:
particleSizes.append(particle.getParticleSize())
def getSizeOfParticleByIndex(self, index):
for particle in self.particles:
if particle.index == index:
return particle.getParticleSize()
def getTypeHistogram(self):
uniquePolymers = self.getUniquePolymers()
......@@ -115,7 +174,7 @@ class ParticleContainer(object):
sorted_typehistogram = sorted(typehistogram.items(), key = operator.itemgetter(1), reverse = True)
#convert back to dict
final_typehistogram = {i[0]: i[1] for i in sorted_typehistogram}
return final_typehistogram
return final_typehistogram
def getUniquePolymers(self):
return np.unique(self.getListOfParticleAssignments())
......@@ -124,7 +183,7 @@ class ParticleContainer(object):
class Particle(object):
def __init__(self, index):
self.index = index
self.longSize_ellipse = None #TODO: IS CURRENTLY PIXEL SIZE, CONVERT TO MICRONS
self.longSize_ellipse = None
self.shortSize_ellipse = None
self.longSize_box = None
self.shortSize_box = None
......@@ -145,31 +204,59 @@ class Particle(object):
self.measurements[indexOfMeasurment].pixelcoord_y = y
def getParticleAssignment(self):
if self.measurementsYieldSameResult():
return self.measurements[0].getAssignment()
else:
print(f'ERROR: Particle with index {self.index} has non-unique analysis results!')
return None
return self.getMeasAssignmentWithHighestHQI() #probably another method could be more suitable...
def getHighestHQI(self):
hqis = []
for meas in self.measurements:
hqis.append(meas.getHQI())
return max(hqis)
def getMeasurements(self):
return self.measurements
def getMeasAssignmentWithHighestHQI(self):
hqis = []
assignments = []
for meas in self.measurements:
hqis.append(meas.getHQI())
assignments.append(meas.getAssignment())
indexOfHighestHQI = hqis.index(max(hqis))
return assignments[indexOfHighestHQI]
def getParticleSize(self):
if self.longSize_ellipse is not None:
return self.longSize_ellipse
return round(self.longSize_ellipse)
elif self.longSize_box is not None:
return self.longSize_box
return round(self.longSize_box)
else:
print(f'Error, particle size requested, but not yet set.\nParticle Index is {self.index}')
raise ValueError
def measurementsYieldSameResult(self):
allResults = [meas.getAssignment() for meas in self.measurements]
def getShortParticleSize(self):
if self.shortSize_ellipse is not None:
return round(self.shortSize_ellipse)
elif self.shortSize_box is not None:
return round(self.shortSize_box )
else:
print(f'Error, particle size requested, but not yet set.\nParticle Index is {self.index}')
raise ValueError