Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
174cb94
Refs #16. refactor: moved PointCloud and McvineResolutionData to thei…
yxqd Apr 16, 2019
3ba2ee6
Refs #16. simdata: added karg "dhkl_ranges"
yxqd Apr 16, 2019
dc4e703
Refs #16. refactor: move violini model to its own module
yxqd Apr 16, 2019
c9ed0d1
Refs #16. workflow.create_violini_model added
yxqd Apr 17, 2019
f4795af
Refs #16. added methods to calculate 2D and 4D PDF
yxqd Apr 17, 2019
bd5dada
Refs #16. instruments.sequoia: support violini workflow method
yxqd Apr 17, 2019
c81327a
Refs #16. refactor: moved PointCloud and McvineResolutionData to thei…
yxqd Apr 16, 2019
0d3aa93
Refs #16. simdata: added karg "dhkl_ranges"
yxqd Apr 16, 2019
abd9053
Refs #16. refactor: move violini model to its own module
yxqd Apr 16, 2019
4208b81
Refs #16. workflow.create_violini_model added
yxqd Apr 17, 2019
26810b1
Refs #16. added methods to calculate 2D and 4D PDF
yxqd Apr 17, 2019
c903e9c
Refs #16. instruments.sequoia: support violini workflow method
yxqd Apr 17, 2019
7d119c3
Merge branch '16-violini-in-workflow' of github.com:mcvine/dgsres int…
yxqd Mar 15, 2022
db030c3
Merge branch 'master' into 16-violini-in-workflow
yxqd Feb 1, 2023
ff7d633
make sure it returns np arrays: dgsres/singlextal/spinw.py/wrap_spi…
yxqd Feb 1, 2023
9890b2a
renamed ellipsoid -> ellipse: dgsres/singlextal/plot.py
yxqd Feb 2, 2023
074a6c9
renamed ellipse -> qE_ellipse: dgsres/singlextal/plot.py
yxqd Feb 2, 2023
c4afa7a
no need to use np.matrix: dgsres/singlextal/violini.py
yxqd Feb 2, 2023
0b4aafe
new methods getIE and getIq: dgsres/singlextal/pointcloud.py
yxqd Feb 2, 2023
030b139
added methods to plot qq ellipse: plot.py
yxqd Feb 2, 2023
c59d2cf
added example notebook to show comparision between mcvine simulated r…
yxqd Feb 2, 2023
af1c107
use latest test data which includes a tar ball of one mcvine-simulate…
yxqd Mar 19, 2023
93fd142
extract tests/data/SEQUOIA_data/Mn3Si2Te6-mcvine-res-sim.tgz in CI: .…
yxqd Mar 19, 2023
ec311c3
16-violini-in-workflow updated to use data in {repo_root}/tests/data:…
yxqd Mar 19, 2023
86e5f33
16-violini-in-workflow started tests/singlextal/test_plot_ellipse.py
yxqd Mar 19, 2023
1fa6301
16-violini-in-workflow added q1q2 plots: tests/singlextal/test_plot_e…
yxqd Mar 20, 2023
0cd7a2b
16-violini-in-workflow added comments: tests/singlextal/test_plot_ell…
yxqd Mar 20, 2023
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
2 changes: 2 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ jobs:
run: |
unzip tests/data/PbTe-CNCS-HH4-Ei_12-E_5-q_0/dhkls.npy.zip -d tests/data/PbTe-CNCS-HH4-Ei_12-E_5-q_0
ls -tl tests/data/beam/ARCS/100meV/out/neutrons
mkdir tests/data/SEQUOIA_data/Mn3Si2Te6
tar xfz tests/data/SEQUOIA_data/Mn3Si2Te6-mcvine-res-sim.tgz --directory tests/data/SEQUOIA_data/Mn3Si2Te6/

- name: ~/.mantid
run: git clone https://github.com/yxqd/dotmantid ~/.mantid
Expand Down
8 changes: 8 additions & 0 deletions dgsres/instruments.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class sequoia:
name = 'SEQ',
detsys_radius = "5.5*meter",
L_m2s = "20.05*meter",
L_m2fc = "18*meter",
offset_sample2beam = "-0.15*meter" # offset from sample to saved beam. don't change this unless you are sure what you are doing
)

Expand All @@ -37,6 +38,13 @@ class sequoia:
def scattering_angle_constraints(cls, theta, phi):
return ((theta<60.) * (theta>-30)) * (phi<18) * (phi>-18)

class violini:

tau_P = 10
tau_M = 8
sigma_thetai = 0.01
sigma_phii = 0.01

class cncs:
instrument = sx.instrument(
name = 'CNCS',
Expand Down
173 changes: 41 additions & 132 deletions dgsres/singlextal/plot.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import os, numpy as np
from . import use_covmat
from .pointcloud import PointCloud
from .simdata import McvineResolutionData
from .violini import VioliniModel
# backward compatible
AnalyticalModel = VioliniModel

# obsolete
def createARCSAnalyticalModel(
tau_P, tau_M, pix_r, pix_h,
sample_thickness,
Expand All @@ -24,147 +30,22 @@ def createARCSAnalyticalModel(
height = "meter*%s" % pix_h,
pressure = "10*atm",
)
return AnalyticalModel(
return VioliniModel(
instrument, pixel, tofwidths, beamdivs,
sample_yml, samplethickness,
Ei, psi_scan)


class PointCloud:

"""resolution point cloud. can be regarded as "events" with dh,dk,dl,dE,weight data.
These data could be generated from mcvine simulation of the resolution function,
or generated from a simple normal distribution using a 4D cov matrix (see AnalyticalModel).
"""

def __init__(self, dhs, dks, dls, dEs, weights):
self.dhs = dhs
self.dks = dks
self.dls = dls
self.dEs = dEs
self.weights = weights
return

def getThinSlice(
self,
axis1=('h', -0.1,0.1, 0.002), axis2=('E', -3, 3., 0.04),
axis3=('k', -0.006, 0.006), axis4=('l', -0.006, 0.006)
):
axis1_name = axis1[0]; axis1_ticks = np.arange(*axis1[1:])
axis2_name = axis2[0]; axis2_ticks = np.arange(*axis2[1:])
condition = True
for ax in [axis3, axis4]:
name = ax[0]
min, max = ax[1:]
arr = self._evts(name)
condition *= (arr<max) * (arr>min)
continue
Ixy, xedges, yedges = np.histogram2d(
self._evts(axis1_name)[condition], self._evts(axis2_name)[condition],
bins=(axis1_ticks, axis2_ticks), weights=self.weights[condition])
xbc = (xedges[:-1] + xedges[1:])/2
ybc = (yedges[:-1] + yedges[1:])/2
xg, yg = np.meshgrid(xbc, ybc)
return xg,yg,Ixy

def _evts(self, name):
return getattr(self, 'd%ss' % name)


class McvineResolutionData:

"""resolution data simulated by mcvine.
The data was simulated earlier and saved to disk.
This class handles locating the data files and reading the data,
and convert the data to different forms: point cloud, cov matrix, etc.
"""

def __init__(self, parent_dir, dirname_template='E%s_hkl%s'):
self.parent_dir = parent_dir
self.dirname_template = dirname_template
return

def path(self, hkl, E):
return os.path.join(self.parent_dir, self.dirname_template % (E, '%s,%s,%s' % tuple(hkl)))

def loadData(self, hkl, E):
p = self.path(hkl, E)
return self._loadData(p)

def loadPointCloud(self, hkl, E):
dhs, dks, dls, dEs, probs = self.loadData(hkl, E)
return PointCloud(dhs, dks, dls, dEs, probs)

def _loadData(self, outdir1):
dhkls = np.load('%s/dhkls.npy' % outdir1)
dEs = np.load('%s/dEs.npy' % outdir1)
probs = np.load('%s/probs.npy' % outdir1)
dhs,dks,dls = dhkls.T
# there might be unreasonable data points with unreasonable weights
mask = (dhs> -2.)*(dhs<2.) \
* (dks> -2.)*(dks<2.) \
* (dls> -2.)*(dls<2.)
return np.array([dhs[mask], dks[mask], dls[mask], dEs[mask], probs[mask]])

def computeCovMat(self, hkl, E):
data = self.loadData(hkl, E)
Data = data[:4]; probs = data[-1]
return np.cov(Data, aweights=probs)

class AnalyticalModel:

"""analytical resolution model based on paper by Violini et al.
The main calculation is done in module .use_covmat.
"""

def __init__(
self, instrument, pixel, tofwidths, beamdivs,
sample_yml, samplethickness,
Ei, psi_scan):
self.instrument = instrument
self.pixel = pixel
self.tofwidths = tofwidths
self.beamdivs = beamdivs
self.sample_yml = sample_yml
self.samplethickness = samplethickness
self.Ei = Ei
self.psi_scan = psi_scan
return

def computePointCloud(self, hkl, E, N=int(1e6)):
covmat = self.computeCovMat(hkl, E)
events = np.random.multivariate_normal(np.zeros(4), covmat, size=N)
dhs, dks, dls, dEs = events.T
ws = np.ones(dhs.shape)
return PointCloud(dhs, dks, dls, dEs, ws)

def computeCovMat(self, hkl, E):
class dynamics:
hkl_dir = np.array([1.,0.,0.])
dq = 0
dynamics.hkl0 = hkl
dynamics.E = E
cm_res = use_covmat.compute(
self.sample_yml, self.Ei,
dynamics,
self.psi_scan,
self.instrument, self.pixel,
self.tofwidths, self.beamdivs, self.samplethickness,
plot=False)
# ellipsoid_trace = cm_res['u']
InvCov4D = cm_res['hklE_inv_cov']
return np.linalg.inv(InvCov4D)/2.355

def plotEllipsoid(covmat, q, symbol='.'):
def plot_qE_ellipse(covmat, q, symbol='.', **kwds):
"""plot 2d ellipsoid along a particular hkl direction, given the cov matrix.
"""
invcm = np.linalg.inv(covmat)
_, u = computeEllipsoid(invcm, q)
_, u = compute_qE_ellipse(invcm, q)
from matplotlib import pyplot as plt
plt.plot(u[:,0], u[:,1], symbol)
plt.plot(u[:,0], u[:,1], symbol, **kwds)
return

def computeEllipsoid(InvCov4D, q):
def compute_qE_ellipse(InvCov4D, q):
"compute ellipsoid along a q direction"
qE2qE = np.array(
[np.hstack([q, [0]]),
Expand All @@ -181,8 +62,36 @@ def computeEllipsoid(InvCov4D, q):
u = np.dot(up, mR.T)
return inv_cov_qE, u

def computeEllipsoids(InvCov4D, directions=None):
def compute_qE_ellipses(InvCov4D, directions=None):
if directions is None:
directions = np.eye(3, dtype=float)
return [(q, computeEllipsoid(InvCov4D, q)) for q in directions]
return [(q, compute_qE_ellipse(InvCov4D, q)) for q in directions]

def plot_qq_ellipse(covmat, q1, q2, symbol='.', **kwds):
"""plot 2d ellipsoid along two hkl directions, given the cov matrix.
"""
invcm = np.linalg.inv(covmat)
_, u = compute_qq_ellipse(invcm, q1, q2)
from matplotlib import pyplot as plt
plt.plot(u[:,0], u[:,1], symbol, **kwds)
return

def compute_qq_ellipse(InvCov4D, q1, q2):
"compute ellipsoid along a q direction"
q1q2_to_qE = np.array([
np.hstack([q1, [0]]),
np.hstack([q2, [0]]),
])
inv_cov = np.dot(q1q2_to_qE, np.dot(InvCov4D, q1q2_to_qE.T))
# print inv_cov_hE
r = np.linalg.eig(inv_cov)
mR = r[1]; lambdas = r[0]
RR = 2*np.log(2)
theta = np.arange(0, 360, 1.)*np.pi/180
u1p = np.sqrt(RR/lambdas[0])*np.cos(theta)
u2p = np.sqrt(RR/lambdas[1])*np.sin(theta)
up = np.array([u1p, u2p]).T
u = np.dot(up, mR.T)
return inv_cov, u


53 changes: 53 additions & 0 deletions dgsres/singlextal/pointcloud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import os, numpy as np

class PointCloud:

"""resolution point cloud. can be regarded as "events" with dh,dk,dl,dE,weight data.
These data could be generated from mcvine simulation of the resolution function,
or generated from a simple normal distribution using a 4D cov matrix (see AnalyticalModel).
"""

def __init__(self, dhs, dks, dls, dEs, weights):
self.dhs = dhs
self.dks = dks
self.dls = dls
self.dEs = dEs
self.weights = weights
return

def getThinSlice(
self,
axis1=('h', -0.1,0.1, 0.002), axis2=('E', -3, 3., 0.04),
axis3=('k', -0.006, 0.006), axis4=('l', -0.006, 0.006)
):
axis1_name = axis1[0]; axis1_ticks = np.arange(*axis1[1:])
axis2_name = axis2[0]; axis2_ticks = np.arange(*axis2[1:])
condition = True
for ax in [axis3, axis4]:
name = ax[0]
min, max = ax[1:]
arr = self._evts(name)
condition *= (arr<max) * (arr>min)
continue
Ixy, xedges, yedges = np.histogram2d(
self._evts(axis1_name)[condition], self._evts(axis2_name)[condition],
bins=(axis1_ticks, axis2_ticks), weights=self.weights[condition])
xbc = (xedges[:-1] + xedges[1:])/2
ybc = (yedges[:-1] + yedges[1:])/2
xg, yg = np.meshgrid(xbc, ybc)
return xg,yg,Ixy

def getIE(self, Emin, Emax, dE):
bins = np.arange(Emin, Emax, dE)
I, edges = np.histogram(self.dEs, bins=bins, weights=self.weights)
return edges, I

def getIq(self, direction, qmin, qmax, dq):
"getIq('h', -1., 1., 0.02)"
bins = np.arange(qmin, qmax, dq)
I, edges = np.histogram(self._evts(direction), bins=bins, weights=self.weights)
return edges, I

def _evts(self, name):
return getattr(self, 'd%ss' % name)

46 changes: 46 additions & 0 deletions dgsres/singlextal/simdata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import os, numpy as np
from .pointcloud import PointCloud

class McvineResolutionData:

"""resolution data simulated by mcvine.
The data was simulated earlier and saved to disk.
This class handles locating the data files and reading the data,
and convert the data to different forms: point cloud, cov matrix, etc.
"""

def __init__(self, parent_dir, dirname_template='E%s_hkl%s'):
self.parent_dir = parent_dir
self.dirname_template = dirname_template
return

def path(self, hkl, E):
return os.path.join(self.parent_dir, self.dirname_template % (E, '%s,%s,%s' % tuple(hkl)))

def loadData(self, hkl, E, dhkl_ranges=None):
p = self.path(hkl, E)
return self._loadData(p, dhkl_ranges=dhkl_ranges)

def loadPointCloud(self, hkl, E):
dhs, dks, dls, dEs, probs = self.loadData(hkl, E)
return PointCloud(dhs, dks, dls, dEs, probs)

def _loadData(self, outdir1, dhkl_ranges=None):
if dhkl_ranges is None:
dhkl_ranges = [ (-2., 2.) ] * 3
dhkls = np.load('%s/dhkls.npy' % outdir1)
dEs = np.load('%s/dEs.npy' % outdir1)
probs = np.load('%s/probs.npy' % outdir1)
dhs,dks,dls = dhkls.T
# there might be unreasonable data points with unreasonable weights
(dh_min, dh_max), (dk_min, dk_max), (dl_min, dl_max) = dhkl_ranges
mask = (dhs>dh_min)*(dhs<dh_max) \
* (dks>dk_min)*(dks<dk_max) \
* (dls>dl_min)*(dls<dl_max)
return np.array([dhs[mask], dks[mask], dls[mask], dEs[mask], probs[mask]])

def computeCovMat(self, hkl, E, dhkl_ranges=None):
data = self.loadData(hkl, E, dhkl_ranges=dhkl_ranges)
Data = data[:4]; probs = data[-1]
return np.cov(Data, aweights=probs)

2 changes: 1 addition & 1 deletion dgsres/singlextal/spinw.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@ def wrap_spinw_disp_func(f):
import matlab.engine, matlab
def _(start, end, Nq_disp):
sp = f(matlab.double(list(start)), matlab.double(list(end)), Nq_disp)
return sp['omega'], sp['swInt']
return np.array(sp['omega']), np.array(sp['swInt'])
return _
10 changes: 6 additions & 4 deletions dgsres/singlextal/use_covmat.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ def __init__(self, theta, phi):
return

def compute(
sample_yml, Ei, dynamics, psi_scan, instrument, pixel, tofwidths, beamdivs, samplethickness,
plot=False):
sample_yml, Ei, dynamics, psi_scan, instrument, pixel,
tofwidths, beamdivs, samplethickness,
plot=False, verbose=False):
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# should P be the T0 chopper?
L_PM=mcvine.units.parse(instrument.L_m2fc)/mcvine.units.meter # P chopper to M chopper distance
Expand Down Expand Up @@ -70,8 +71,9 @@ def compute(
from mcvine.workflow.singlextal.coords_transform import hkl2Q
for r in results:
xtalori.psi = r*np.pi/180
print("psi=%s, Q=%s" % (r, hkl2Q(hkl, xtalori)))
print("hkl2Q=%r\n(Q = hkl dot hkl2Q)" % (xtalori.hkl2cartesian_mat(),))
if verbose:
print("psi=%s, Q=%s" % (r, hkl2Q(hkl, xtalori)))
print("hkl2Q=%r\n(Q = hkl dot hkl2Q)" % (xtalori.hkl2cartesian_mat(),))
# these are the psi angles that the particular point of interest will be measured
# print results
assert len(results)
Expand Down
Loading