import sys
sys.path.append('.')
sys.path.append('..')
sys.path.append('../..')
import math,numpy as np
from MJOLNIR.Geometry import GeometryConcept,Analyser,Detector
from MJOLNIR import _tools
import warnings
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
[docs]class Wedge(GeometryConcept.GeometryConcept):
"""Wedge object to keep track of analysers and detectors. To be used as a storage object and facilitate easy movement of multiple detectors and analysers as once."""
[docs] @_tools.KwargChecker(include=[''])
def __init__(self,position=(0.0,0.0,0.0),detectors=[],analysers=[],concept='ManyToMany',**kwargs):
"""
Args:
- position (float 3): Position of wedge (default (0,0,0))
Kwargs:
- detectors (list or single detector): Either a list or a single detector (default empty)
- analysers (list or single analyser): Either a list or a single analyser (default empty)
- concept (string "ManyToMany" or "OneToOne"): Setting to controle if there is a "one to one" correspondence between analysers and detectors or a "many to many" relationship.
.. note::
A wedge does not have a direction. The direction of analysers and detectors are to be set individually.
"""
super(Wedge,self).__init__(position)
self._analysers = []
self._detectors = []
self.append(analysers)
self.append(detectors)
self._settings = {}
for key in kwargs:
self.settings[key]=kwargs[key]
self.settings['concept']=concept
@property
def analysers(self):
return self._analysers
@analysers.getter
def analysers(self):
return self._analysers
@analysers.setter
def analysers(self,Analysers):
if len(self.analysers)!=0:
warnings.warn('The list of analysers is not empty! Appending new analyser(s)')
if isinstance(Analysers, list):
for ana in Analysers:
if not issubclass(type(ana),Analyser.Analyser):
raise AttributeError('Object is not an analyser or a simple list of these')
self._analysers.append(ana)
else:
if not issubclass(type(Analysers),Analyser.Analyser):
raise AttributeError('Object is not an analyser or a simple list of these')
self._analysers.append(Analysers)
@property
def detectors(self):
return self._detectors
@detectors.getter
def detectors(self):
return self._detectors
@detectors.setter
def detectors(self,Detectors):
if len(self.detectors)!=0:
warnings.warn('The list of detectors is not empty! Appending new detector(s)')
if isinstance(Detectors, list):
for det in Detectors:
if not issubclass(type(det),Detector.Detector):
raise AttributeError('Object is not a detector or a simple list of these')
self._detectors.append(det)
else:
if not issubclass(type(Detectors),Detector.Detector):
raise AttributeError('Object is not a detector or a simple list of these')
self._detectors.append(Detectors)
@property
def settings(self):
return self._settings
@settings.getter
def settings(self):
return self._settings
@settings.setter
def settings(self,*args,**kwargs):
raise NotImplementedError('Settings cannot be overwritten.')
[docs] def append(self,Object):
"""Append Object(s) to corresponding list.
Args:
- object (Detector(s)/Analyser(s)): Single detector/analyser of list of detectors/analysers
"""
if isinstance(Object,list):
for obj in Object:
if issubclass(type(obj),Analyser.Analyser):
self._analysers.append(obj)
elif issubclass(type(obj),Detector.Detector):
self._detectors.append(obj)
else:
raise AttributeError('Object not analyser or detector or a simple list of these')
else:
if issubclass(type(Object),Analyser.Analyser):
self._analysers.append(Object)
elif issubclass(type(Object),Detector.Detector):
self._detectors.append(Object)
else:
raise AttributeError('Object not analyser or detector or a simple list of these')
[docs] @_tools.KwargChecker()
def plot(self,ax,offset=(0,0,0)):
"""Recursive plotting routine."""
for obj in self.analysers+self.detectors:
obj.plot(ax,offset=np.array(self.position,dtype=float)+np.array(offset,dtype=float))
def __str__(self):
string = ''
string+="{} with settings:\nPosition = {}\n".format(self.__class__,self.position)
string+='Containing analysers:\n'
for ana in self.analysers:
string+='\t'+str(ana)+'\n'
string+='Containing detectors:\n'
for det in self.detectors:
string+='\t'+str(det)+'\n'
return string
[docs] def calculateDetectorAnalyserPositions(self):
"""Find neutron position on analyser and detector. Assuming that the analyser is in the z=0 plane."""
if(len(self.detectors)==0 or len(self.analysers)==0):
raise ValueError('Wedge does not contain detectors and/or analysers.')
detectorPixelPositions = []
analyserPixelPositions = []
if self.settings['concept']=='OneToOne':
if len(self.detectors)!=len(self.analysers):
raise RuntimeError('Concept set to OneToOne but number of detectors does not mach analysers ({}!?{}'.format(len(self.detectors),len(self.analysers)))
detectorCounter = 0
for det in self.detectors:
PixelPos = det.getPixelPositions()+self.position
if len(PixelPos)!=1:
raise ValueError("OneToOne concept chosen by detector split into multiple parts!")
detectorPixelPositions.append(PixelPos)
LDA = PixelPos[0]-self.analysers[detectorCounter].position # Detector - analyser vector
LAS = self.analysers[detectorCounter].position+self.position
vertical = np.array([0,0,1])
perpVect = np.cross(vertical,LAS)
deltaXD = np.dot(PixelPos,perpVect)
LD = np.linalg.norm(LDA,axis=1)
LA = np.linalg.norm(LAS)
deltaXDprime = deltaXD/(LD/LA+1.0)
analyserPixelPositions.append(np.outer(deltaXDprime,perpVect)+self.analysers[detectorCounter].position+self.position)
detectorCounter+=1
elif self.settings['concept']=='ManyToMany':
for det in self.detectors:
PixelPos = [x +self.position for x in det.getPixelPositions()]
if len(PixelPos)!=len(self.analysers):
raise ValueError("ManyToMany concept chosen by detector split into number of parts not matching number of analysers!")
detectorPixelPositions.append(np.concatenate(PixelPos))
vertical = np.array([0,0,1])
LAS = [self.analysers[i].position+self.position for i in range(len(PixelPos))]
perpVect = [np.cross(vertical,LAS[i])/(np.linalg.norm(np.cross(vertical,LAS[i]))) for i in range(len(PixelPos))]
deltaXD = [np.dot(PixelPos[i],perpVect[i]) for i in range(len(PixelPos))]
LDA = [np.array(PixelPos[i])-np.array(deltaXD[i]).reshape(-1,1)*np.array(perpVect[i]).reshape(1,3)-self.analysers[i].position for i in range(len(PixelPos))]
LD = [np.linalg.norm(LDA[i],axis=1) for i in range(len(PixelPos))]
LA = [np.linalg.norm(LAS[i]) for i in range(len(PixelPos))]
deltaXDprime = [deltaXD[i]/(LD[i]/LA[i]+1.0) for i in range(len(PixelPos))]
analyserPixelPositions.append(np.concatenate([np.outer(deltaXDprime[i],perpVect[i])+self.analysers[i].position+self.position for i in range(len(PixelPos))]))
else:
raise ValueError("Wedge does not contain a Concept setting that is understood. Should be either 'OneToOne' or 'ManyToMany'")
return detectorPixelPositions,analyserPixelPositions