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: |
$CONDA/bin/conda install --quiet --yes conda-build anaconda-client
$CONDA/bin/conda config --append channels conda-forge
$CONDA/bin/conda config --set channel_priority strict
$CONDA/bin/conda config --set anaconda_upload yes
- name: Build and upload

View File

@ -4,4 +4,4 @@ from pyzebra.h5 import *
from pyzebra.xtal 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"]
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(
[anatric_path, config_file],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
cwd=cwd,
check=True,
text=True,
)
print(" ".join(comp_proc.args))
@ -59,10 +60,13 @@ class AnatricConfig:
def save_as(self, filename):
self._tree.write(filename)
def tostring(self):
return ET.tostring(self._tree.getroot(), encoding="unicode")
def _get_attr(self, name, tag, attr):
elem = self._tree.find(name).find(tag)
if elem is None:
return None
return ""
return elem.attrib[attr]
def _set_attr(self, name, tag, attr, value):
@ -225,7 +229,7 @@ class AnatricConfig:
elem = self._tree.find("crystal").find("UB")
if elem is not None:
return elem.text
return None
return ""
@crystal_UB.setter
def crystal_UB(self, value):
@ -247,7 +251,7 @@ class AnatricConfig:
elem = self._tree.find("DataFactory").find("dist1")
if elem is not None:
return elem.attrib["value"]
return None
return ""
@dataFactory_dist1.setter
def dataFactory_dist1(self, value):
@ -258,7 +262,7 @@ class AnatricConfig:
elem = self._tree.find("DataFactory").find("dist2")
if elem is not None:
return elem.attrib["value"]
return None
return ""
@dataFactory_dist2.setter
def dataFactory_dist2(self, value):
@ -269,7 +273,7 @@ class AnatricConfig:
elem = self._tree.find("DataFactory").find("dist3")
if elem is not None:
return elem.attrib["value"]
return None
return ""
@dataFactory_dist3.setter
def dataFactory_dist3(self, value):
@ -310,7 +314,7 @@ class AnatricConfig:
def _get_alg_attr(self, alg, tag, attr):
param_elem = self._alg_elems[alg].find(tag)
if param_elem is None:
return None
return ""
return param_elem.attrib[attr]
def _set_alg_attr(self, alg, tag, attr, value):

View File

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

View File

@ -1,5 +1,6 @@
import base64
import io
import os
import re
import tempfile
@ -347,14 +348,14 @@ def create():
temp_file = temp_dir + "/config.xml"
config.save_as(temp_file)
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:
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()
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()
process_button = Button(label="Process", button_type="primary")
@ -389,11 +390,7 @@ def create():
)
async def update_config():
with tempfile.TemporaryDirectory() as temp_dir:
temp_file = temp_dir + "/config.xml"
config.save_as(temp_file)
with open(temp_file) as f_config:
output_config.value = f_config.read()
output_config.value = config.tostring()
doc.add_periodic_callback(update_config, 1000)

View File

@ -12,9 +12,12 @@ from bokeh.models import (
CheckboxGroup,
ColumnDataSource,
DataRange1d,
DataTable,
Div,
FileInput,
Grid,
MultiSelect,
NumberFormatter,
HoverTool,
Image,
Line,
@ -27,8 +30,10 @@ from bokeh.models import (
Rect,
ResetTool,
Select,
Slider,
Spacer,
Spinner,
TableColumn,
TextAreaInput,
TextInput,
Title,
@ -40,8 +45,8 @@ import pyzebra
IMAGE_W = 256
IMAGE_H = 128
IMAGE_PLOT_W = int(IMAGE_W * 2.5)
IMAGE_PLOT_H = int(IMAGE_H * 2.5)
IMAGE_PLOT_W = int(IMAGE_W * 2) + 52
IMAGE_PLOT_H = int(IMAGE_H * 2) + 27
def create():
@ -56,21 +61,19 @@ def create():
for file in os.listdir(proposal_path):
if file.endswith(".hdf"):
file_list.append((os.path.join(proposal_path, file), file))
filelist.options = file_list
filelist.value = file_list[0][0]
file_select.options = file_list
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)
def upload_button_callback(_attr, _old, new):
with io.StringIO(base64.b64decode(new).decode()) as file:
h5meta_list = pyzebra.parse_h5meta(file)
file_list = h5meta_list["filelist"]
filelist.options = [(entry, os.path.basename(entry)) for entry in file_list]
filelist.value = file_list[0]
file_select.options = [(entry, os.path.basename(entry)) for entry in file_list]
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)
def update_image(index=None):
@ -101,14 +104,14 @@ def create():
image_glyph.color_mapper.high = im_max
if "mf" in det_data:
mf_spinner.value = det_data["mf"][index]
metadata_table_source.data.update(mf=[det_data["mf"][index]])
else:
mf_spinner.value = None
metadata_table_source.data.update(mf=[None])
if "temp" in det_data:
temp_spinner.value = det_data["temp"][index]
metadata_table_source.data.update(temp=[det_data["temp"][index]])
else:
temp_spinner.value = None
metadata_table_source.data.update(temp=[None])
gamma, nu = calculate_pol(det_data, 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.bounds = (var_start, var_end)
def filelist_callback(_attr, _old, new):
def file_select_callback(_attr, old, new):
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.high = det_data["data"].shape[0] - 1
index_slider.end = det_data["data"].shape[0] - 1
zebra_mode = det_data["zebra_mode"]
if zebra_mode == "nb":
geometry_textinput.value = "normal beam"
metadata_table_source.data.update(geom=["normal beam"])
else: # zebra_mode == "bi"
geometry_textinput.value = "bisecting"
metadata_table_source.data.update(geom=["bisecting"])
update_image(0)
update_overview_plot()
filelist = Select(title="Available .hdf files:")
filelist.on_change("value", filelist_callback)
file_select = MultiSelect(title="Available .hdf files:", width=210, height=250)
file_select.on_change("value", file_select_callback)
def index_spinner_callback(_attr, _old, new):
def index_callback(_attr, _old, new):
update_image(new)
index_spinner = Spinner(title="Image index:", value=0, low=0, width=80)
index_spinner.on_change("value", index_spinner_callback)
index_slider = Slider(value=0, start=0, end=1, show_value=False, width=400)
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(
x_range=Range1d(0, IMAGE_W, bounds=(0, IMAGE_W)),
@ -321,7 +344,7 @@ def create():
y_range=frame_range,
extra_y_ranges={"scanning_motor": scanning_motor_range},
plot_height=400,
plot_width=IMAGE_PLOT_W,
plot_width=IMAGE_PLOT_W - 3,
)
# ---- tools
@ -359,7 +382,7 @@ def create():
y_range=frame_range,
extra_y_ranges={"scanning_motor": scanning_motor_range},
plot_height=400,
plot_width=IMAGE_PLOT_H,
plot_width=IMAGE_PLOT_H + 22,
)
# ---- tools
@ -398,7 +421,7 @@ def create():
roi_avg_plot = Plot(
x_range=DataRange1d(),
y_range=DataRange1d(),
plot_height=200,
plot_height=150,
plot_width=IMAGE_PLOT_W,
toolbar_location="left",
)
@ -548,7 +571,7 @@ def create():
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:
roi_selection[f"{filename_id}"].append(selection)
else:
@ -559,11 +582,23 @@ def create():
selection_button = Button(label="Add selection")
selection_button.on_click(selection_button_callback)
mf_spinner = Spinner(title="Magnetic field:", format="0.00", width=100, disabled=True)
temp_spinner = Spinner(title="Temperature:", format="0.00", width=100, disabled=True)
geometry_textinput = TextInput(title="Geometry:", width=120, disabled=True)
metadata_table_source = ColumnDataSource(dict(geom=[""], temp=[None], mf=[None]))
num_formatter = NumberFormatter(format="0.00", nan_format="")
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
import_layout = column(proposal_textinput, upload_div, upload_button, file_select)
layout_image = column(gridplot([[proj_v, None], [plot, proj_h]], merge_tools=False))
colormap_layout = column(
colormap,
@ -576,9 +611,9 @@ def create():
layout_controls = row(
column(selection_button, selection_list),
Spacer(width=20),
column(colormap_layout),
Spacer(width=20),
column(row(mf_spinner, temp_spinner), row(geometry_textinput, index_spinner), hkl_button),
column(
row(index_spinner, column(Spacer(height=25), index_slider)), metadata_table, hkl_button
),
)
layout_overview = column(
@ -591,13 +626,8 @@ def create():
)
tab_layout = row(
column(
row(
proposal_textinput, filelist, Spacer(width=100), column(upload_div, upload_button),
),
layout_overview,
layout_controls,
),
column(import_layout, colormap_layout),
column(layout_overview, layout_controls),
column(roi_avg_plot, layout_image),
)

View File

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

View File

@ -3,7 +3,6 @@ import math
import os
import subprocess
import tempfile
from collections import defaultdict
import numpy as np
from bokeh.layouts import column, row
@ -38,6 +37,9 @@ def create():
ub_matrices = []
def process_button_callback():
# drop table selection to clear result fields
results_table_source.selected.indices = []
nonlocal diff_vec
with tempfile.TemporaryDirectory() as temp_dir:
temp_peak_list_dir = os.path.join(temp_dir, "peak_list")
@ -52,7 +54,7 @@ def create():
"-n",
"2",
"python",
"spind/gen_hkl_table.py",
os.path.expanduser("~/spind/gen_hkl_table.py"),
lattice_const_textinput.value,
"--max-res",
str(max_res_spinner.value),
@ -79,7 +81,7 @@ def create():
"-n",
"2",
"python",
"spind/SPIND.py",
os.path.expanduser("~/spind/SPIND.py"),
temp_peak_list_dir,
temp_hkl_file,
"-o",
@ -102,9 +104,11 @@ def create():
print(comp_proc.stdout)
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:
with open(spind_out_file) as f_out:
spind_res = defaultdict(list)
for line in f_out:
c1, c2, c3, c4, c5, *c_rest = line.split()
spind_res["label"].append(c1)
@ -115,12 +119,10 @@ def create():
# last digits are spind UB matrix
vals = list(map(float, c_rest))
ub_matrix_spind = np.array(vals).reshape(3, 3)
ub_matrix = np.linalg.inv(np.transpose(ub_matrix_spind))
ub_matrix_spind = np.transpose(np.array(vals).reshape(3, 3))
ub_matrix = np.linalg.inv(ub_matrix_spind)
ub_matrices.append(ub_matrix)
spind_res["ub_matrix"].append(ub_matrix_spind)
results_table_source.data.update(spind_res)
spind_res["ub_matrix"].append(str(ub_matrix_spind * 1e-10))
print(f"Content of {spind_out_file}:")
with open(spind_out_file) as f:
@ -129,6 +131,8 @@ def create():
except FileNotFoundError:
print("No results from spind")
results_table_source.data.update(spind_res)
process_button = Button(label="Process", button_type="primary")
process_button.on_click(process_button_callback)
@ -145,10 +149,12 @@ def create():
ub_matrix_textareainput.value = str(ub_matrix * 1e10)
hkl_textareainput.value = res
else:
ub_matrix_textareainput.value = None
hkl_textareainput.value = None
ub_matrix_textareainput.value = ""
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(
source=results_table_source,
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"]
hkl_are_integers = isinstance(h, int) # if True, other indices are of type 'int' too
if hkl_are_integers:
hkl_str = f"{h:6}{k:6}{l:6}"
hkl_str = f"{h:4}{k:4}{l:4}"
else:
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():
if "amplitude" in name:
area_n = param.value
area_s = param.stderr
if param.stderr is None:
area_n = np.nan
area_s = np.nan
else:
area_n = param.value
area_s = param.stderr
# TODO: take into account multiple peaks
break
else:
area_n = 0
area_s = 0
if area_n is None or area_s is None:
print(f"Couldn't export scan: {scan['idx']}")
continue
# no peak functions in a fit model
area_n = np.nan
area_s = np.nan
# apply lorentz correction to area
if lorentz:

View File

@ -128,6 +128,17 @@ def fit_scan(scan, model_dict, fit_from=None, fit_to=None):
else:
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)
if model is None: