19 Commits
0.3.1 ... 0.3.2

Author SHA1 Message Date
a2fceffc1b Updating for version 0.3.2 2021-05-10 17:50:15 +02:00
415d68b4dc Return empty string for non-present anatric param 2021-05-10 17:29:36 +02:00
00ff4117ea Isolate anatric subprocesses 2021-05-10 17:06:20 +02:00
67853b8db4 Avoid using temp_dir for anatric xml config preview 2021-05-10 16:34:58 +02:00
60787bccb7 Clarify path to spind 2021-05-10 15:13:38 +02:00
880d86d750 Fix spind output update issues
Fix #27
2021-05-06 18:22:56 +02:00
7a88e5e254 Adapt spind results display according to #27 2021-05-06 17:43:20 +02:00
20f2a8ada4 Update preview on datatable content change 2021-05-04 17:14:59 +02:00
42c092fc14 Fix Safari browser double file download
Introduce a small time delay between .comm/.incomm file downloads

For #24
2021-05-04 16:50:51 +02:00
8153db9f67 Add an extra index spinner 2021-05-04 12:08:39 +02:00
62c969d6ad Allow .ccl files in param study
Fix #28
2021-05-04 11:09:48 +02:00
085620abae Set conda channel_priority to 'strict' 2021-04-23 11:16:57 +02:00
9ebe290966 Export nan for area value/error in case of a bad fit 2021-04-21 12:41:19 +02:00
c9cd96c521 Set lower and upper bounds for center and sigma 2021-04-20 18:25:14 +02:00
d745cda4a5 Reduce hkl columns width to 4 in comm files
Fix #26
2021-04-20 15:07:36 +02:00
1b5f70afa0 Set f0_intercept and f1_amplitude >= 0
For #26
2021-04-20 15:03:27 +02:00
a034065a09 Use Slider for image index 2021-04-12 18:35:58 +02:00
2a60c86b48 Display experiment conditions in DataTable 2021-04-12 18:18:59 +02:00
ccc075975f Unify data files discovery via proposal number 2021-04-12 17:28:25 +02:00
10 changed files with 173 additions and 115 deletions

View File

@ -16,6 +16,7 @@ jobs:
run: | run: |
$CONDA/bin/conda install --quiet --yes conda-build anaconda-client $CONDA/bin/conda install --quiet --yes conda-build anaconda-client
$CONDA/bin/conda config --append channels conda-forge $CONDA/bin/conda config --append channels conda-forge
$CONDA/bin/conda config --set channel_priority strict
$CONDA/bin/conda config --set anaconda_upload yes $CONDA/bin/conda config --set anaconda_upload yes
- name: Build and upload - name: Build and upload

View File

@ -4,4 +4,4 @@ from pyzebra.h5 import *
from pyzebra.xtal import * from pyzebra.xtal import *
from pyzebra.ccl_process import * from pyzebra.ccl_process import *
__version__ = "0.3.1" __version__ = "0.3.2"

View File

@ -23,12 +23,13 @@ REFLECTION_PRINTER_FORMATS = [
ALGORITHMS = ["adaptivemaxcog", "adaptivedynamic"] ALGORITHMS = ["adaptivemaxcog", "adaptivedynamic"]
def anatric(config_file, anatric_path="/afs/psi.ch/project/sinq/rhel7/bin/anatric"): def anatric(config_file, anatric_path="/afs/psi.ch/project/sinq/rhel7/bin/anatric", cwd=None):
comp_proc = subprocess.run( comp_proc = subprocess.run(
[anatric_path, config_file], [anatric_path, config_file],
check=True,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
cwd=cwd,
check=True,
text=True, text=True,
) )
print(" ".join(comp_proc.args)) print(" ".join(comp_proc.args))
@ -59,10 +60,13 @@ class AnatricConfig:
def save_as(self, filename): def save_as(self, filename):
self._tree.write(filename) self._tree.write(filename)
def tostring(self):
return ET.tostring(self._tree.getroot(), encoding="unicode")
def _get_attr(self, name, tag, attr): def _get_attr(self, name, tag, attr):
elem = self._tree.find(name).find(tag) elem = self._tree.find(name).find(tag)
if elem is None: if elem is None:
return None return ""
return elem.attrib[attr] return elem.attrib[attr]
def _set_attr(self, name, tag, attr, value): def _set_attr(self, name, tag, attr, value):
@ -225,7 +229,7 @@ class AnatricConfig:
elem = self._tree.find("crystal").find("UB") elem = self._tree.find("crystal").find("UB")
if elem is not None: if elem is not None:
return elem.text return elem.text
return None return ""
@crystal_UB.setter @crystal_UB.setter
def crystal_UB(self, value): def crystal_UB(self, value):
@ -247,7 +251,7 @@ class AnatricConfig:
elem = self._tree.find("DataFactory").find("dist1") elem = self._tree.find("DataFactory").find("dist1")
if elem is not None: if elem is not None:
return elem.attrib["value"] return elem.attrib["value"]
return None return ""
@dataFactory_dist1.setter @dataFactory_dist1.setter
def dataFactory_dist1(self, value): def dataFactory_dist1(self, value):
@ -258,7 +262,7 @@ class AnatricConfig:
elem = self._tree.find("DataFactory").find("dist2") elem = self._tree.find("DataFactory").find("dist2")
if elem is not None: if elem is not None:
return elem.attrib["value"] return elem.attrib["value"]
return None return ""
@dataFactory_dist2.setter @dataFactory_dist2.setter
def dataFactory_dist2(self, value): def dataFactory_dist2(self, value):
@ -269,7 +273,7 @@ class AnatricConfig:
elem = self._tree.find("DataFactory").find("dist3") elem = self._tree.find("DataFactory").find("dist3")
if elem is not None: if elem is not None:
return elem.attrib["value"] return elem.attrib["value"]
return None return ""
@dataFactory_dist3.setter @dataFactory_dist3.setter
def dataFactory_dist3(self, value): def dataFactory_dist3(self, value):
@ -310,7 +314,7 @@ class AnatricConfig:
def _get_alg_attr(self, alg, tag, attr): def _get_alg_attr(self, alg, tag, attr):
param_elem = self._alg_elems[alg].find(tag) param_elem = self._alg_elems[alg].find(tag)
if param_elem is None: if param_elem is None:
return None return ""
return param_elem.attrib[attr] return param_elem.attrib[attr]
def _set_alg_attr(self, alg, tag, attr, value): def _set_alg_attr(self, alg, tag, attr, value):

View File

@ -47,9 +47,11 @@ from pyzebra.ccl_io import AREA_METHODS
javaScript = """ javaScript = """
let j = 0;
for (let i = 0; i < js_data.data['fname'].length; i++) { for (let i = 0; i < js_data.data['fname'].length; i++) {
if (js_data.data['content'][i] === "") continue; if (js_data.data['content'][i] === "") continue;
setTimeout(function() {
const blob = new Blob([js_data.data['content'][i]], {type: 'text/plain'}) const blob = new Blob([js_data.data['content'][i]], {type: 'text/plain'})
const link = document.createElement('a'); const link = document.createElement('a');
document.body.appendChild(link); document.body.appendChild(link);
@ -59,6 +61,9 @@ for (let i = 0; i < js_data.data['fname'].length; i++) {
link.click(); link.click();
window.URL.revokeObjectURL(url); window.URL.revokeObjectURL(url);
document.body.removeChild(link); document.body.removeChild(link);
}, 100 * j)
j++;
} }
""" """
@ -72,11 +77,11 @@ def create():
proposal = new.strip() proposal = new.strip()
year = new[:4] year = new[:4]
proposal_path = f"/afs/psi.ch/project/sinqdata/{year}/zebra/{proposal}" proposal_path = f"/afs/psi.ch/project/sinqdata/{year}/zebra/{proposal}"
ccl_file_list = [] file_list = []
for file in os.listdir(proposal_path): for file in os.listdir(proposal_path):
if file.endswith((".ccl", ".dat")): if file.endswith((".ccl", ".dat")):
ccl_file_list.append((os.path.join(proposal_path, file), file)) file_list.append((os.path.join(proposal_path, file), file))
file_select.options = ccl_file_list file_select.options = file_list
proposal_textinput = TextInput(title="Proposal number:", width=210) proposal_textinput = TextInput(title="Proposal number:", width=210)
proposal_textinput.on_change("value", proposal_textinput_callback) proposal_textinput.on_change("value", proposal_textinput_callback)
@ -97,11 +102,7 @@ def create():
merge_dest_select.options = merge_options merge_dest_select.options = merge_options
merge_dest_select.value = merge_options[0][0] merge_dest_select.value = merge_options[0][0]
def ccl_file_select_callback(_attr, _old, _new):
pass
file_select = MultiSelect(title="Available .ccl/.dat files:", width=210, height=250) file_select = MultiSelect(title="Available .ccl/.dat files:", width=210, height=250)
file_select.on_change("value", ccl_file_select_callback)
def file_open_button_callback(): def file_open_button_callback():
nonlocal det_data nonlocal det_data
@ -120,7 +121,6 @@ def create():
js_data.data.update(fname=[base + ".comm", base + ".incomm"]) js_data.data.update(fname=[base + ".comm", base + ".incomm"])
_init_datatable() _init_datatable()
_update_preview()
file_open_button = Button(label="Open New", width=100) file_open_button = Button(label="Open New", width=100)
file_open_button.on_click(file_open_button_callback) file_open_button.on_click(file_open_button_callback)
@ -156,7 +156,6 @@ def create():
js_data.data.update(fname=[base + ".comm", base + ".incomm"]) js_data.data.update(fname=[base + ".comm", base + ".incomm"])
_init_datatable() _init_datatable()
_update_preview()
upload_div = Div(text="or upload new .ccl/.dat files:", margin=(5, 5, 0, 5)) upload_div = Div(text="or upload new .ccl/.dat files:", margin=(5, 5, 0, 5))
upload_button = FileInput(accept=".ccl,.dat", multiple=True, width=200) upload_button = FileInput(accept=".ccl,.dat", multiple=True, width=200)
@ -301,7 +300,12 @@ def create():
_update_plot(det_data[new[0]]) _update_plot(det_data[new[0]])
def scan_table_source_callback(_attr, _old, _new):
_update_preview()
scan_table_source = ColumnDataSource(dict(scan=[], hkl=[], fit=[], export=[])) scan_table_source = ColumnDataSource(dict(scan=[], hkl=[], fit=[], export=[]))
scan_table_source.on_change("data", scan_table_source_callback)
scan_table = DataTable( scan_table = DataTable(
source=scan_table_source, source=scan_table_source,
columns=[ columns=[
@ -425,8 +429,12 @@ def create():
) )
if function == "linear": if function == "linear":
fitparams["value"] = [0, 0] fitparams["value"] = [0, 1]
fitparams["vary"] = [False, True] fitparams["vary"] = [False, True]
fitparams["min"] = [None, 0]
elif function == "gaussian":
fitparams["min"] = [0, None, None]
return fitparams return fitparams
@ -463,7 +471,6 @@ def create():
_update_plot(_get_selected_scan()) _update_plot(_get_selected_scan())
_update_table() _update_table()
_update_preview()
fit_all_button = Button(label="Fit All", button_type="primary", width=145) fit_all_button = Button(label="Fit All", button_type="primary", width=145)
fit_all_button.on_click(fit_all_button_callback) fit_all_button.on_click(fit_all_button_callback)
@ -476,7 +483,6 @@ def create():
_update_plot(scan) _update_plot(scan)
_update_table() _update_table()
_update_preview()
fit_button = Button(label="Fit Current", width=145) fit_button = Button(label="Fit Current", width=145)
fit_button.on_click(fit_button_callback) fit_button.on_click(fit_button_callback)

View File

@ -1,5 +1,6 @@
import base64 import base64
import io import io
import os
import re import re
import tempfile import tempfile
@ -347,14 +348,14 @@ def create():
temp_file = temp_dir + "/config.xml" temp_file = temp_dir + "/config.xml"
config.save_as(temp_file) config.save_as(temp_file)
if doc.anatric_path: if doc.anatric_path:
pyzebra.anatric(temp_file, anatric_path=doc.anatric_path) pyzebra.anatric(temp_file, anatric_path=doc.anatric_path, cwd=temp_dir)
else: else:
pyzebra.anatric(temp_file) pyzebra.anatric(temp_file, cwd=temp_dir)
with open(config.logfile) as f_log: with open(os.path.join(temp_dir, config.logfile)) as f_log:
output_log.value = f_log.read() output_log.value = f_log.read()
with open(config.reflectionPrinter_file) as f_res: with open(os.path.join(temp_dir, config.reflectionPrinter_file)) as f_res:
output_res.value = f_res.read() output_res.value = f_res.read()
process_button = Button(label="Process", button_type="primary") process_button = Button(label="Process", button_type="primary")
@ -389,11 +390,7 @@ def create():
) )
async def update_config(): async def update_config():
with tempfile.TemporaryDirectory() as temp_dir: output_config.value = config.tostring()
temp_file = temp_dir + "/config.xml"
config.save_as(temp_file)
with open(temp_file) as f_config:
output_config.value = f_config.read()
doc.add_periodic_callback(update_config, 1000) doc.add_periodic_callback(update_config, 1000)

View File

@ -12,9 +12,12 @@ from bokeh.models import (
CheckboxGroup, CheckboxGroup,
ColumnDataSource, ColumnDataSource,
DataRange1d, DataRange1d,
DataTable,
Div, Div,
FileInput, FileInput,
Grid, Grid,
MultiSelect,
NumberFormatter,
HoverTool, HoverTool,
Image, Image,
Line, Line,
@ -27,8 +30,10 @@ from bokeh.models import (
Rect, Rect,
ResetTool, ResetTool,
Select, Select,
Slider,
Spacer, Spacer,
Spinner, Spinner,
TableColumn,
TextAreaInput, TextAreaInput,
TextInput, TextInput,
Title, Title,
@ -40,8 +45,8 @@ import pyzebra
IMAGE_W = 256 IMAGE_W = 256
IMAGE_H = 128 IMAGE_H = 128
IMAGE_PLOT_W = int(IMAGE_W * 2.5) IMAGE_PLOT_W = int(IMAGE_W * 2) + 52
IMAGE_PLOT_H = int(IMAGE_H * 2.5) IMAGE_PLOT_H = int(IMAGE_H * 2) + 27
def create(): def create():
@ -56,21 +61,19 @@ def create():
for file in os.listdir(proposal_path): for file in os.listdir(proposal_path):
if file.endswith(".hdf"): if file.endswith(".hdf"):
file_list.append((os.path.join(proposal_path, file), file)) file_list.append((os.path.join(proposal_path, file), file))
filelist.options = file_list file_select.options = file_list
filelist.value = file_list[0][0]
proposal_textinput = TextInput(title="Enter proposal number:", width=145) proposal_textinput = TextInput(title="Proposal number:", width=210)
proposal_textinput.on_change("value", proposal_textinput_callback) proposal_textinput.on_change("value", proposal_textinput_callback)
def upload_button_callback(_attr, _old, new): def upload_button_callback(_attr, _old, new):
with io.StringIO(base64.b64decode(new).decode()) as file: with io.StringIO(base64.b64decode(new).decode()) as file:
h5meta_list = pyzebra.parse_h5meta(file) h5meta_list = pyzebra.parse_h5meta(file)
file_list = h5meta_list["filelist"] file_list = h5meta_list["filelist"]
filelist.options = [(entry, os.path.basename(entry)) for entry in file_list] file_select.options = [(entry, os.path.basename(entry)) for entry in file_list]
filelist.value = file_list[0]
upload_div = Div(text="or upload .cami file:", margin=(5, 5, 0, 5)) upload_div = Div(text="or upload .cami file:", margin=(5, 5, 0, 5))
upload_button = FileInput(accept=".cami") upload_button = FileInput(accept=".cami", width=200)
upload_button.on_change("value", upload_button_callback) upload_button.on_change("value", upload_button_callback)
def update_image(index=None): def update_image(index=None):
@ -101,14 +104,14 @@ def create():
image_glyph.color_mapper.high = im_max image_glyph.color_mapper.high = im_max
if "mf" in det_data: if "mf" in det_data:
mf_spinner.value = det_data["mf"][index] metadata_table_source.data.update(mf=[det_data["mf"][index]])
else: else:
mf_spinner.value = None metadata_table_source.data.update(mf=[None])
if "temp" in det_data: if "temp" in det_data:
temp_spinner.value = det_data["temp"][index] metadata_table_source.data.update(temp=[det_data["temp"][index]])
else: else:
temp_spinner.value = None metadata_table_source.data.update(temp=[None])
gamma, nu = calculate_pol(det_data, index) gamma, nu = calculate_pol(det_data, index)
omega = np.ones((IMAGE_H, IMAGE_W)) * det_data["omega"][index] omega = np.ones((IMAGE_H, IMAGE_W)) * det_data["omega"][index]
@ -154,30 +157,50 @@ def create():
scanning_motor_range.reset_end = var_end scanning_motor_range.reset_end = var_end
scanning_motor_range.bounds = (var_start, var_end) scanning_motor_range.bounds = (var_start, var_end)
def filelist_callback(_attr, _old, new): def file_select_callback(_attr, old, new):
nonlocal det_data nonlocal det_data
det_data = pyzebra.read_detector_data(new) if not new:
# skip empty selections
return
# Avoid selection of multiple indicies (via Shift+Click or Ctrl+Click)
if len(new) > 1:
# drop selection to the previous one
file_select.value = old
return
if len(old) > 1:
# skip unnecessary update caused by selection drop
return
det_data = pyzebra.read_detector_data(new[0])
index_spinner.value = 0 index_spinner.value = 0
index_spinner.high = det_data["data"].shape[0] - 1 index_spinner.high = det_data["data"].shape[0] - 1
index_slider.end = det_data["data"].shape[0] - 1
zebra_mode = det_data["zebra_mode"] zebra_mode = det_data["zebra_mode"]
if zebra_mode == "nb": if zebra_mode == "nb":
geometry_textinput.value = "normal beam" metadata_table_source.data.update(geom=["normal beam"])
else: # zebra_mode == "bi" else: # zebra_mode == "bi"
geometry_textinput.value = "bisecting" metadata_table_source.data.update(geom=["bisecting"])
update_image(0) update_image(0)
update_overview_plot() update_overview_plot()
filelist = Select(title="Available .hdf files:") file_select = MultiSelect(title="Available .hdf files:", width=210, height=250)
filelist.on_change("value", filelist_callback) file_select.on_change("value", file_select_callback)
def index_spinner_callback(_attr, _old, new): def index_callback(_attr, _old, new):
update_image(new) update_image(new)
index_spinner = Spinner(title="Image index:", value=0, low=0, width=80) index_slider = Slider(value=0, start=0, end=1, show_value=False, width=400)
index_spinner.on_change("value", index_spinner_callback)
index_spinner = Spinner(title="Image index:", value=0, low=0, width=100)
index_spinner.on_change("value", index_callback)
index_slider.js_link("value_throttled", index_spinner, "value")
index_spinner.js_link("value", index_slider, "value")
plot = Plot( plot = Plot(
x_range=Range1d(0, IMAGE_W, bounds=(0, IMAGE_W)), x_range=Range1d(0, IMAGE_W, bounds=(0, IMAGE_W)),
@ -321,7 +344,7 @@ def create():
y_range=frame_range, y_range=frame_range,
extra_y_ranges={"scanning_motor": scanning_motor_range}, extra_y_ranges={"scanning_motor": scanning_motor_range},
plot_height=400, plot_height=400,
plot_width=IMAGE_PLOT_W, plot_width=IMAGE_PLOT_W - 3,
) )
# ---- tools # ---- tools
@ -359,7 +382,7 @@ def create():
y_range=frame_range, y_range=frame_range,
extra_y_ranges={"scanning_motor": scanning_motor_range}, extra_y_ranges={"scanning_motor": scanning_motor_range},
plot_height=400, plot_height=400,
plot_width=IMAGE_PLOT_H, plot_width=IMAGE_PLOT_H + 22,
) )
# ---- tools # ---- tools
@ -398,7 +421,7 @@ def create():
roi_avg_plot = Plot( roi_avg_plot = Plot(
x_range=DataRange1d(), x_range=DataRange1d(),
y_range=DataRange1d(), y_range=DataRange1d(),
plot_height=200, plot_height=150,
plot_width=IMAGE_PLOT_W, plot_width=IMAGE_PLOT_W,
toolbar_location="left", toolbar_location="left",
) )
@ -548,7 +571,7 @@ def create():
int(np.ceil(frame_range.end)), int(np.ceil(frame_range.end)),
] ]
filename_id = filelist.value[-8:-4] filename_id = file_select.value[0][-8:-4]
if filename_id in roi_selection: if filename_id in roi_selection:
roi_selection[f"{filename_id}"].append(selection) roi_selection[f"{filename_id}"].append(selection)
else: else:
@ -559,11 +582,23 @@ def create():
selection_button = Button(label="Add selection") selection_button = Button(label="Add selection")
selection_button.on_click(selection_button_callback) selection_button.on_click(selection_button_callback)
mf_spinner = Spinner(title="Magnetic field:", format="0.00", width=100, disabled=True) metadata_table_source = ColumnDataSource(dict(geom=[""], temp=[None], mf=[None]))
temp_spinner = Spinner(title="Temperature:", format="0.00", width=100, disabled=True) num_formatter = NumberFormatter(format="0.00", nan_format="")
geometry_textinput = TextInput(title="Geometry:", width=120, disabled=True) metadata_table = DataTable(
source=metadata_table_source,
columns=[
TableColumn(field="geom", title="Geometry", width=100),
TableColumn(field="temp", title="Temperature", formatter=num_formatter, width=100),
TableColumn(field="mf", title="Magnetic Field", formatter=num_formatter, width=100),
],
width=300,
height=50,
autosize_mode="none",
index_position=None,
)
# Final layout # Final layout
import_layout = column(proposal_textinput, upload_div, upload_button, file_select)
layout_image = column(gridplot([[proj_v, None], [plot, proj_h]], merge_tools=False)) layout_image = column(gridplot([[proj_v, None], [plot, proj_h]], merge_tools=False))
colormap_layout = column( colormap_layout = column(
colormap, colormap,
@ -576,9 +611,9 @@ def create():
layout_controls = row( layout_controls = row(
column(selection_button, selection_list), column(selection_button, selection_list),
Spacer(width=20), Spacer(width=20),
column(colormap_layout), column(
Spacer(width=20), row(index_spinner, column(Spacer(height=25), index_slider)), metadata_table, hkl_button
column(row(mf_spinner, temp_spinner), row(geometry_textinput, index_spinner), hkl_button), ),
) )
layout_overview = column( layout_overview = column(
@ -591,13 +626,8 @@ def create():
) )
tab_layout = row( tab_layout = row(
column( column(import_layout, colormap_layout),
row( column(layout_overview, layout_controls),
proposal_textinput, filelist, Spacer(width=100), column(upload_div, upload_button),
),
layout_overview,
layout_controls,
),
column(roi_avg_plot, layout_image), column(roi_avg_plot, layout_image),
) )

View File

@ -81,11 +81,11 @@ def create():
proposal = new.strip() proposal = new.strip()
year = new[:4] year = new[:4]
proposal_path = f"/afs/psi.ch/project/sinqdata/{year}/zebra/{proposal}" proposal_path = f"/afs/psi.ch/project/sinqdata/{year}/zebra/{proposal}"
dat_file_list = [] file_list = []
for file in os.listdir(proposal_path): for file in os.listdir(proposal_path):
if file.endswith(".dat"): if file.endswith((".ccl", ".dat")):
dat_file_list.append((os.path.join(proposal_path, file), file)) file_list.append((os.path.join(proposal_path, file), file))
file_select.options = dat_file_list file_select.options = file_list
proposal_textinput = TextInput(title="Proposal number:", width=210) proposal_textinput = TextInput(title="Proposal number:", width=210)
proposal_textinput.on_change("value", proposal_textinput_callback) proposal_textinput.on_change("value", proposal_textinput_callback)
@ -108,11 +108,7 @@ def create():
param_select.value = "user defined" param_select.value = "user defined"
def file_select_callback(_attr, _old, _new): file_select = MultiSelect(title="Available .ccl/.dat files:", width=210, height=250)
pass
file_select = MultiSelect(title="Available .dat files:", width=210, height=250)
file_select.on_change("value", file_select_callback)
def file_open_button_callback(): def file_open_button_callback():
nonlocal det_data nonlocal det_data
@ -130,7 +126,6 @@ def create():
js_data.data.update(fname=[base + ".comm", base + ".incomm"]) js_data.data.update(fname=[base + ".comm", base + ".incomm"])
_init_datatable() _init_datatable()
_update_preview()
file_open_button = Button(label="Open New", width=100) file_open_button = Button(label="Open New", width=100)
file_open_button.on_click(file_open_button_callback) file_open_button.on_click(file_open_button_callback)
@ -165,10 +160,9 @@ def create():
js_data.data.update(fname=[base + ".comm", base + ".incomm"]) js_data.data.update(fname=[base + ".comm", base + ".incomm"])
_init_datatable() _init_datatable()
_update_preview()
upload_div = Div(text="or upload new .dat files:", margin=(5, 5, 0, 5)) upload_div = Div(text="or upload new .ccl/.dat files:", margin=(5, 5, 0, 5))
upload_button = FileInput(accept=".dat", multiple=True, width=200) upload_button = FileInput(accept=".ccl,.dat", multiple=True, width=200)
upload_button.on_change("value", upload_button_callback) upload_button.on_change("value", upload_button_callback)
def append_upload_button_callback(_attr, _old, new): def append_upload_button_callback(_attr, _old, new):
@ -183,7 +177,7 @@ def create():
_init_datatable() _init_datatable()
append_upload_div = Div(text="append extra files:", margin=(5, 5, 0, 5)) append_upload_div = Div(text="append extra files:", margin=(5, 5, 0, 5))
append_upload_button = FileInput(accept=".dat", multiple=True, width=200) append_upload_button = FileInput(accept=".ccl,.dat", multiple=True, width=200)
append_upload_button.on_change("value", append_upload_button_callback) append_upload_button.on_change("value", append_upload_button_callback)
def monitor_spinner_callback(_attr, _old, new): def monitor_spinner_callback(_attr, _old, new):
@ -392,7 +386,12 @@ def create():
_update_plot() _update_plot()
def scan_table_source_callback(_attr, _old, _new):
_update_preview()
scan_table_source = ColumnDataSource(dict(file=[], scan=[], param=[], fit=[], export=[])) scan_table_source = ColumnDataSource(dict(file=[], scan=[], param=[], fit=[], export=[]))
scan_table_source.on_change("data", scan_table_source_callback)
scan_table = DataTable( scan_table = DataTable(
source=scan_table_source, source=scan_table_source,
columns=[ columns=[
@ -519,8 +518,12 @@ def create():
) )
if function == "linear": if function == "linear":
fitparams["value"] = [0, 0] fitparams["value"] = [0, 1]
fitparams["vary"] = [False, True] fitparams["vary"] = [False, True]
fitparams["min"] = [None, 0]
elif function == "gaussian":
fitparams["min"] = [0, None, None]
return fitparams return fitparams
@ -557,7 +560,6 @@ def create():
_update_plot() _update_plot()
_update_table() _update_table()
_update_preview()
fit_all_button = Button(label="Fit All", button_type="primary", width=145) fit_all_button = Button(label="Fit All", button_type="primary", width=145)
fit_all_button.on_click(fit_all_button_callback) fit_all_button.on_click(fit_all_button_callback)
@ -570,7 +572,6 @@ def create():
_update_plot() _update_plot()
_update_table() _update_table()
_update_preview()
fit_button = Button(label="Fit Current", width=145) fit_button = Button(label="Fit Current", width=145)
fit_button.on_click(fit_button_callback) fit_button.on_click(fit_button_callback)

View File

@ -3,7 +3,6 @@ import math
import os import os
import subprocess import subprocess
import tempfile import tempfile
from collections import defaultdict
import numpy as np import numpy as np
from bokeh.layouts import column, row from bokeh.layouts import column, row
@ -38,6 +37,9 @@ def create():
ub_matrices = [] ub_matrices = []
def process_button_callback(): def process_button_callback():
# drop table selection to clear result fields
results_table_source.selected.indices = []
nonlocal diff_vec nonlocal diff_vec
with tempfile.TemporaryDirectory() as temp_dir: with tempfile.TemporaryDirectory() as temp_dir:
temp_peak_list_dir = os.path.join(temp_dir, "peak_list") temp_peak_list_dir = os.path.join(temp_dir, "peak_list")
@ -52,7 +54,7 @@ def create():
"-n", "-n",
"2", "2",
"python", "python",
"spind/gen_hkl_table.py", os.path.expanduser("~/spind/gen_hkl_table.py"),
lattice_const_textinput.value, lattice_const_textinput.value,
"--max-res", "--max-res",
str(max_res_spinner.value), str(max_res_spinner.value),
@ -79,7 +81,7 @@ def create():
"-n", "-n",
"2", "2",
"python", "python",
"spind/SPIND.py", os.path.expanduser("~/spind/SPIND.py"),
temp_peak_list_dir, temp_peak_list_dir,
temp_hkl_file, temp_hkl_file,
"-o", "-o",
@ -102,9 +104,11 @@ def create():
print(comp_proc.stdout) print(comp_proc.stdout)
spind_out_file = os.path.join(temp_dir, "spind.txt") spind_out_file = os.path.join(temp_dir, "spind.txt")
spind_res = dict(
label=[], crystal_id=[], match_rate=[], matched_peaks=[], column_5=[], ub_matrix=[],
)
try: try:
with open(spind_out_file) as f_out: with open(spind_out_file) as f_out:
spind_res = defaultdict(list)
for line in f_out: for line in f_out:
c1, c2, c3, c4, c5, *c_rest = line.split() c1, c2, c3, c4, c5, *c_rest = line.split()
spind_res["label"].append(c1) spind_res["label"].append(c1)
@ -115,12 +119,10 @@ def create():
# last digits are spind UB matrix # last digits are spind UB matrix
vals = list(map(float, c_rest)) vals = list(map(float, c_rest))
ub_matrix_spind = np.array(vals).reshape(3, 3) ub_matrix_spind = np.transpose(np.array(vals).reshape(3, 3))
ub_matrix = np.linalg.inv(np.transpose(ub_matrix_spind)) ub_matrix = np.linalg.inv(ub_matrix_spind)
ub_matrices.append(ub_matrix) ub_matrices.append(ub_matrix)
spind_res["ub_matrix"].append(ub_matrix_spind) spind_res["ub_matrix"].append(str(ub_matrix_spind * 1e-10))
results_table_source.data.update(spind_res)
print(f"Content of {spind_out_file}:") print(f"Content of {spind_out_file}:")
with open(spind_out_file) as f: with open(spind_out_file) as f:
@ -129,6 +131,8 @@ def create():
except FileNotFoundError: except FileNotFoundError:
print("No results from spind") print("No results from spind")
results_table_source.data.update(spind_res)
process_button = Button(label="Process", button_type="primary") process_button = Button(label="Process", button_type="primary")
process_button.on_click(process_button_callback) process_button.on_click(process_button_callback)
@ -145,10 +149,12 @@ def create():
ub_matrix_textareainput.value = str(ub_matrix * 1e10) ub_matrix_textareainput.value = str(ub_matrix * 1e10)
hkl_textareainput.value = res hkl_textareainput.value = res
else: else:
ub_matrix_textareainput.value = None ub_matrix_textareainput.value = ""
hkl_textareainput.value = None hkl_textareainput.value = ""
results_table_source = ColumnDataSource(dict()) results_table_source = ColumnDataSource(
dict(label=[], crystal_id=[], match_rate=[], matched_peaks=[], column_5=[], ub_matrix=[])
)
results_table = DataTable( results_table = DataTable(
source=results_table_source, source=results_table_source,
columns=[ columns=[

View File

@ -261,22 +261,24 @@ def export_1D(data, path, area_method=AREA_METHODS[0], lorentz=False, hkl_precis
h, k, l = scan["h"], scan["k"], scan["l"] h, k, l = scan["h"], scan["k"], scan["l"]
hkl_are_integers = isinstance(h, int) # if True, other indices are of type 'int' too hkl_are_integers = isinstance(h, int) # if True, other indices are of type 'int' too
if hkl_are_integers: if hkl_are_integers:
hkl_str = f"{h:6}{k:6}{l:6}" hkl_str = f"{h:4}{k:4}{l:4}"
else: else:
hkl_str = f"{h:8.{hkl_precision}f}{k:8.{hkl_precision}f}{l:8.{hkl_precision}f}" hkl_str = f"{h:8.{hkl_precision}f}{k:8.{hkl_precision}f}{l:8.{hkl_precision}f}"
for name, param in scan["fit"].params.items(): for name, param in scan["fit"].params.items():
if "amplitude" in name: if "amplitude" in name:
if param.stderr is None:
area_n = np.nan
area_s = np.nan
else:
area_n = param.value area_n = param.value
area_s = param.stderr area_s = param.stderr
# TODO: take into account multiple peaks
break break
else: else:
area_n = 0 # no peak functions in a fit model
area_s = 0 area_n = np.nan
area_s = np.nan
if area_n is None or area_s is None:
print(f"Couldn't export scan: {scan['idx']}")
continue
# apply lorentz correction to area # apply lorentz correction to area
if lorentz: if lorentz:

View File

@ -128,6 +128,17 @@ def fit_scan(scan, model_dict, fit_from=None, fit_to=None):
else: else:
param_hints[hint_name] = tmp param_hints[hint_name] = tmp
if "center" in param_name:
if np.isneginf(param_hints["min"]):
param_hints["min"] = np.min(x_fit)
if np.isposinf(param_hints["max"]):
param_hints["max"] = np.max(x_fit)
if "sigma" in param_name:
if np.isposinf(param_hints["max"]):
param_hints["max"] = np.max(x_fit) - np.min(x_fit)
_model.set_param_hint(param_name, **param_hints) _model.set_param_hint(param_name, **param_hints)
if model is None: if model is None: