diff --git a/sed/calibrator/energy.py b/sed/calibrator/energy.py index fae10ad7..ca555a8c 100644 --- a/sed/calibrator/energy.py +++ b/sed/calibrator/energy.py @@ -1987,7 +1987,7 @@ def residual(pars, time, data, binwidth, binning, energy_scale): value=E0_pars.get("value", min(vals)), min=E0_pars.get("min", -np.inf), max=E0_pars.get("max", np.inf), - vary=d_pars.get("vary", True), + vary=E0_pars.get("vary", True), ) fit = Minimizer( residual, @@ -2225,6 +2225,7 @@ def apply_energy_offset( df: Union[pd.DataFrame, dask.dataframe.DataFrame], columns: Union[str, Sequence[str]], signs: Union[int, Sequence[int]], + subtract_mean: Union[bool, Sequence[bool]] = True, energy_column: str = None, reductions: Union[str, Sequence[str]] = None, config: dict = None, @@ -2255,9 +2256,12 @@ def apply_energy_offset( columns = [columns] if isinstance(signs, int): signs = [signs] + if len(signs) != len(columns): + raise ValueError("signs and columns must have the same length.") + if isinstance(subtract_mean, bool): + subtract_mean = [subtract_mean] * len(columns) if reductions is None: reductions = [None] * len(columns) - columns_: List[str] = [] reductions_: List[str] = [] to_roll: List[str] = [] @@ -2280,6 +2284,7 @@ def apply_energy_offset( target_column=energy_column, offset_columns=columns_, signs=signs, + subtract_mean=subtract_mean, reductions=reductions_, inplace=True, ) diff --git a/sed/core/dfops.py b/sed/core/dfops.py index e4d43252..ee2c897b 100644 --- a/sed/core/dfops.py +++ b/sed/core/dfops.py @@ -331,6 +331,7 @@ def apply_offset_from_columns( offset_columns: Union[str, Sequence[str]], signs: Union[int, Sequence[int]], reductions: Union[str, Sequence[str]], + subtract_mean: Union[bool, Sequence[bool]], inplace: bool = True, ) -> Union[pd.DataFrame, dask.dataframe.DataFrame]: """Apply an offset to a column based on the values of other columns. @@ -359,10 +360,12 @@ def apply_offset_from_columns( if len(signs) != len(offset_columns): raise ValueError("signs and offset_columns must have the same length!") - for col, sign, red in zip(offset_columns, signs, reductions): + for col, sign, red, submean in zip(offset_columns, signs, reductions, subtract_mean): assert col in df.columns, f"{col} not in dataframe!" if red is not None: df[target_column] = df[target_column] + sign * df[col].agg(red) else: df[target_column] = df[target_column] + sign * df[col] + if submean: + df[target_column] = df[target_column] - sign * df[col].mean() return df diff --git a/sed/core/processor.py b/sed/core/processor.py index 13af0648..7997b43c 100644 --- a/sed/core/processor.py +++ b/sed/core/processor.py @@ -1168,6 +1168,7 @@ def apply_energy_offset( columns: Union[str, Sequence[str]] = None, signs: Union[int, Sequence[int]] = None, reductions: Union[str, Sequence[str]] = None, + subtract_mean: Union[bool, Sequence[bool]] = None, ) -> None: """Shift the energy axis of the dataframe by a given amount. @@ -1190,22 +1191,27 @@ def apply_energy_offset( f"Energy column {energy_column} not found in dataframe! " "Run energy calibration first", ) - self._dataframe, metadata = energy.apply_energy_offset( - df=self._dataframe, - columns=columns, - energy_column=energy_column, - signs=signs, - reductions=reductions, - config=self._config, - ) - self._dataframe[energy_column] += constant - metadata["offset"] = constant - self._attributes.add( - metadata, - "apply_energy_offset", - # TODO: allow only appending when no offset along this column(s) was applied - duplicate_policy="append", - ) + metadata = {} + if columns is not None: + self._dataframe, metadata = energy.apply_energy_offset( + df=self._dataframe, + columns=columns, + energy_column=energy_column, + signs=signs, + reductions=reductions, + subtract_mean=subtract_mean, + config=self._config, + ) + if constant is not None: + self._dataframe[energy_column] += constant + metadata["offset"] = constant + if len(metadata) > 0: + self._attributes.add( + metadata, + "apply_energy_offset", + # TODO: allow only appending when no offset along this column(s) was applied + duplicate_policy="append", + ) def append_tof_ns_axis( self, diff --git a/tutorial/5 - hextof workflow.ipynb b/tutorial/5 - hextof workflow.ipynb index ae5aa5b0..e7265e9c 100644 --- a/tutorial/5 - hextof workflow.ipynb +++ b/tutorial/5 - hextof workflow.ipynb @@ -12,6 +12,7 @@ "import sed\n", "import numpy as np\n", "\n", + "\n", "%matplotlib inline\n", "# %matplotlib ipympl\n", "import matplotlib.pyplot as plt" @@ -49,8 +50,11 @@ "metadata": {}, "outputs": [], "source": [ - "config={\"core\": {\"paths\": {\"data_raw_dir\": \"../../flash_test_data/fl1user3/\", \"data_parquet_dir\": \"../../flash_test_data/parquet/\"}}}\n", - "sp = SedProcessor(runs=[44638], config=config, user_config=config_file, system_config={}, collect_metadata=False)" + "config={\"core\": {\"paths\": {\n", + " \"data_raw_dir\": \"/asap3/flash/gpfs/pg2/2023/data/11019101/raw/hdf/offline/fl1user3\", \n", + " \"data_parquet_dir\": \"/home/agustsss/temp/sed_parquet/\"\n", + "}}}\n", + "sp = SedProcessor(runs=[44797], config=config, user_config=config_file, system_config={}, collect_metadata=False)" ] }, { @@ -67,26 +71,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Energy Calibration" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## using lmfit" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "axes = ['sampleBias', 'dldTimeSteps']\n", - "bins = [6, 500]\n", - "ranges = [[0,6], [4000, 8000]]\n", - "res = sp.compute(bins=bins, axes=axes, ranges=ranges)" + "# time-of-flight spectrum" ] }, { @@ -95,7 +80,7 @@ "metadata": {}, "outputs": [], "source": [ - "sp.load_bias_series(binned_data=res)" + "sp.append_tof_ns_axis()" ] }, { @@ -104,9 +89,10 @@ "metadata": {}, "outputs": [], "source": [ - "ranges=(5500, 6000)\n", - "ref_id=3\n", - "sp.find_bias_peaks(ranges=ranges, ref_id=ref_id)" + "axes = ['sampleBias','dldTime']\n", + "bins = [5, 250]\n", + "ranges = [[28,33], [650,800]]\n", + "res = sp.compute(bins=bins, axes=axes, ranges=ranges)" ] }, { @@ -115,32 +101,22 @@ "metadata": {}, "outputs": [], "source": [ - "sp.append_tof_ns_axis()" + "plt.figure()\n", + "res.plot.line(x='dldTime')" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "sp.dataframe.head()" + "# Energy Calibration" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "ref_id=3\n", - "ref_energy=0\n", - "sp.calibrate_energy_axis(\n", - " ref_id=ref_id,\n", - " ref_energy=ref_energy,\n", - " method=\"lmfit\",\n", - " energy_scale='kinetic',\n", - ")" + "## using lmfit" ] }, { @@ -149,17 +125,10 @@ "metadata": {}, "outputs": [], "source": [ - "ref_id=3\n", - "ref_energy=0\n", - "sp.calibrate_energy_axis(\n", - " ref_id=ref_id,\n", - " ref_energy=ref_energy,\n", - " method=\"lmfit\",\n", - " energy_scale='kinetic',\n", - " d={'value':1, 'min': 1, 'max': 1},\n", - " E0={'value':0, 'min': -100, 'max': 100,'vary':True},\n", - " t0={'value':225, 'min': -100, 'max': 250, 'vary':True},\n", - ")" + "axes = ['sampleBias', 'dldTimeSteps']\n", + "bins = [5, 500]\n", + "ranges = [[28,33], [4000, 4800]]\n", + "res = sp.compute(bins=bins, axes=axes, ranges=ranges)" ] }, { @@ -168,7 +137,10 @@ "metadata": {}, "outputs": [], "source": [ - "sp.append_energy_axis()" + "plt.figure()\n", + "dres = res.copy()\n", + "dres.data = np.gradient(res.data, axis=1)\n", + "dres.plot.line(x='dldTimeSteps')" ] }, { @@ -177,7 +149,7 @@ "metadata": {}, "outputs": [], "source": [ - "sp.append_tof_ns_axis()" + "sp.load_bias_series(binned_data=dres)" ] }, { @@ -186,7 +158,9 @@ "metadata": {}, "outputs": [], "source": [ - "sp.dataframe[['dldTime','dldTimeSteps','energy','dldSectorID']].head()" + "ranges=(4120, 4200)\n", + "ref_id=0\n", + "sp.find_bias_peaks(ranges=ranges, ref_id=ref_id)" ] }, { @@ -195,9 +169,9 @@ "metadata": {}, "outputs": [], "source": [ - "axes = ['sampleBias', 'energy']\n", - "bins = [5, 500]\n", - "ranges = [[28,33], [-10,10]]\n", + "axes = ['sampleBias', 'dldTimeSteps']\n", + "bins = [5, 2000]\n", + "ranges = [[28,33], [0, 2000]]\n", "res = sp.compute(bins=bins, axes=axes, ranges=ranges)" ] }, @@ -208,21 +182,7 @@ "outputs": [], "source": [ "plt.figure()\n", - "res.mean('sampleBias').plot.line(x='energy',linewidth=3);\n", - "res.plot.line(x='energy',linewidth=1,alpha=.5,label='all');\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sp.apply_energy_offset(\n", - " constant=-31.5,\n", - " columns=['sampleBias'],\n", - " signs=[+1],\n", - ")" + "res.plot.line(x='dldTimeSteps')" ] }, { @@ -231,30 +191,14 @@ "metadata": {}, "outputs": [], "source": [ - "axes = ['sampleBias', 'energy']\n", - "bins = [5, 500]\n", - "ranges = [[28,33], [-3,2]]\n", - "res_fit = sp.compute(bins=bins, axes=axes, ranges=ranges)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure()\n", - "ax = plt.subplot(111)\n", - "res_fit.energy.attrs['unit'] = 'eV'\n", - "res_fit.mean('sampleBias').plot.line(x='energy',linewidth=3, ax=ax);\n", - "res_fit.plot.line(x='energy',linewidth=1,alpha=.5,label='all',ax=ax);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## with poly fit" + "# ref_id=0\n", + "# ref_energy=0\n", + "# sp.calibrate_energy_axis(\n", + "# ref_id=ref_id,\n", + "# ref_energy=ref_energy,\n", + "# method=\"lmfit\",\n", + "# energy_scale='kinetic',\n", + "# )" ] }, { @@ -263,7 +207,7 @@ "metadata": {}, "outputs": [], "source": [ - "sp = SedProcessor(runs=[44797], config=config_file, collect_metadata=False)" + "from sed.calibrator.energy import tof2ev, tof2ns" ] }, { @@ -272,8 +216,17 @@ "metadata": {}, "outputs": [], "source": [ - "sp.add_jitter()\n", - "sp.align_dld_sectors()" + "ph_peak = 1e-9 * tof2ns(binwidth=sp.config['dataframe']['tof_binwidth'], binning=3,t=219)\n", + "sp.calibrate_energy_axis(\n", + " ref_id=0,\n", + " ref_energy=0,\n", + " method=\"lmfit\",\n", + " energy_scale='kinetic',\n", + " d={'value':1.,'min': .8, 'max':5, 'vary':True},\n", + " t0={'value':ph_peak, 'min': ph_peak*0.1, 'max': ph_peak*1.9, 'vary':False},\n", + " E0={'value': 0., 'min': -100, 'max': 100, 'vary': True},\n", + "\n", + ")" ] }, { @@ -282,10 +235,17 @@ "metadata": {}, "outputs": [], "source": [ - "axes = ['sampleBias', 'dldTimeSteps']\n", - "bins = [6, 500]\n", - "ranges = [[28,33], [4000, 4800]]\n", - "res = sp.compute(bins=bins, axes=axes, ranges=ranges)" + "# tof = np.linspace(4000,5000,500)\n", + "# plt.figure()\n", + "# plt.plot(tof, tof2ev(\n", + "# t=tof, \n", + "# tof_distance=1, \n", + "# time_offset=1e-9*tof2ns(binwidth=sp.config['dataframe']['tof_binwidth'], binning=3,t=218), \n", + "# energy_offset=0,\n", + "# binning=3,\n", + "# energy_scale='kinetic',\n", + "# binwidth=sp.config['dataframe']['tof_binwidth'])\n", + "# )" ] }, { @@ -294,7 +254,7 @@ "metadata": {}, "outputs": [], "source": [ - "sp.load_bias_series(binned_data=res)" + "sp.append_energy_axis()" ] }, { @@ -303,9 +263,7 @@ "metadata": {}, "outputs": [], "source": [ - "ranges=(4250, 4500)\n", - "ref_id=3\n", - "sp.find_bias_peaks(ranges=ranges, ref_id=ref_id)" + "sp.ec.calibration" ] }, { @@ -314,14 +272,7 @@ "metadata": {}, "outputs": [], "source": [ - "ref_id=3\n", - "ref_energy=-0.3\n", - "sp.calibrate_energy_axis(\n", - " ref_id=ref_id,\n", - " ref_energy=-0.3,\n", - " method=\"lstsq\",\n", - " order=2,\n", - ")" + "sp.dataframe[['dldTime','dldTimeSteps','energy','dldSectorID']].head()" ] }, { @@ -330,7 +281,10 @@ "metadata": {}, "outputs": [], "source": [ - "sp.append_energy_axis()" + "axes = ['sampleBias', 'energy']\n", + "bins = [5, 500]\n", + "ranges = [[28,33], [-10,10]]\n", + "res = sp.compute(bins=bins, axes=axes, ranges=ranges)" ] }, { @@ -339,10 +293,9 @@ "metadata": {}, "outputs": [], "source": [ - "axes = ['sampleBias', 'energy']\n", - "bins = [5, 500]\n", - "ranges = [[28,33], [-10,10]]\n", - "res = sp.compute(bins=bins, axes=axes, ranges=ranges)" + "plt.figure()\n", + "res.mean('sampleBias').plot.line(x='energy',linewidth=3)\n", + "res.plot.line(x='energy',linewidth=1,alpha=.5);" ] }, { @@ -351,9 +304,7 @@ "metadata": {}, "outputs": [], "source": [ - "plt.figure()\n", - "res.mean('sampleBias').plot.line(x='energy',linewidth=3);\n", - "res.plot.line(x='energy',linewidth=1,alpha=.5,label='all');\n" + "sp.dataframe" ] }, { @@ -363,9 +314,10 @@ "outputs": [], "source": [ "sp.apply_energy_offset(\n", - " constant=-31.5,\n", - " columns=['sampleBias'],\n", - " signs=[+1],\n", + " constant=2,\n", + " columns=['sampleBias','monochromatorPhotonEnergy','tofVoltage'],\n", + " signs=[1,-1,-1],\n", + " subtract_mean=True,\n", ")" ] }, @@ -377,8 +329,8 @@ "source": [ "axes = ['sampleBias', 'energy']\n", "bins = [5, 500]\n", - "ranges = [[28,33], [-3,2]]\n", - "res_poly = sp.compute(bins=bins, axes=axes, ranges=ranges)" + "ranges = [[28,33], [-10,2]]\n", + "res_fit = sp.compute(bins=bins, axes=axes, ranges=ranges)" ] }, { @@ -389,19 +341,16 @@ "source": [ "plt.figure()\n", "ax = plt.subplot(111)\n", - "res_poly.energy.attrs['unit'] = 'eV'\n", - "res_poly.mean('sampleBias').plot.line(x='energy',linewidth=3, ax=ax);\n", - "res_poly.plot.line(x='energy',linewidth=1,alpha=.5,label='all',ax=ax);" + "res_fit.energy.attrs['unit'] = 'eV'\n", + "res_fit.mean('sampleBias').plot.line(x='energy',linewidth=3, ax=ax)\n", + "res_fit.plot.line(x='energy',linewidth=1,alpha=.5,label='all',ax=ax);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# From manual values\n", - "these are obviously not the right values, I think I got them from an other calibraiton file.\n", - "They are here to show how the calibration works.\n", - "Also, I noticed the parameter energy_scale from the config has no effect..." + "# Fit the falling edge at E=0 to evaluate the quality of the energy calibration" ] }, { @@ -410,51 +359,7 @@ "metadata": {}, "outputs": [], "source": [ - "sp = SedProcessor(runs=[44797], config=config_file, collect_metadata=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sp.add_jitter()\n", - "sp.align_dld_sectors()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sp.append_energy_axis()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sp.apply_energy_offset(\n", - " constant=+31.5,\n", - " columns=['sampleBias'],\n", - " signs=[-1],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "axes = ['sampleBias', 'energy']\n", - "bins = [5, 500]\n", - "ranges = [[28,33], [-10,5]]\n", - "res_config = sp.compute(bins=bins, axes=axes, ranges=ranges)" + "from lmfit.models import Gaussian2dModel, LorentzianModel, LinearModel" ] }, { @@ -463,11 +368,22 @@ "metadata": {}, "outputs": [], "source": [ + "curve = res_fit.mean('sampleBias').sel(energy=slice(-0.15,1))\n", + "x = curve.energy\n", + "y = -np.gradient(curve.data)\n", + "full_y = np.gradient(res_fit.data, axis=1)\n", + "gm = LorentzianModel()\n", + "# lin = LinearModel()\n", + "model = gm #+ lin\n", + "params = gm.guess(y, x=x)\n", + "# params.update(lin.make_params())\n", + "result = model.fit(y, params, x=x)\n", + "\n", "plt.figure()\n", - "ax = plt.subplot(111)\n", - "res_config.energy.attrs['unit'] = 'eV'\n", - "res_config.mean('sampleBias').plot.line(x='energy',linewidth=3, ax=ax);\n", - "res_config.plot.line(x='energy',linewidth=1,alpha=.5,label='all',ax=ax);" + "plt.plot(x, y, 'bo')\n", + "plt.plot(x, result.best_fit, 'r-')\n", + "result.best_values\n", + "best = result.params" ] }, { @@ -476,19 +392,9 @@ "metadata": {}, "outputs": [], "source": [ - "sp.dataframe['energy'] = - sp.dataframe['energy']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "axes = ['sampleBias', 'energy']\n", - "bins = [5, 500]\n", - "ranges = [[28,33], [-3,2]]\n", - "res_config = sp.compute(bins=bins, axes=axes, ranges=ranges)" + "{'amplitude': 62.649384173227375,\n", + " 'center': 0.07618539040289204,\n", + " 'sigma': 0.0948828689156287}" ] }, { @@ -498,10 +404,15 @@ "outputs": [], "source": [ "plt.figure()\n", - "ax = plt.subplot(111)\n", - "res_config.energy.attrs['unit'] = 'eV'\n", - "res_config.mean('sampleBias').plot.line(x='energy',linewidth=3, ax=ax);\n", - "res_config.plot.line(x='energy',linewidth=1,alpha=.5,label='all',ax=ax);" + "for i in range(len(res_fit.sampleBias)):\n", + " x = res_fit.isel(sampleBias=i).sel(energy=slice(-0.15,1)).energy\n", + " y = res_fit.isel(sampleBias=i).sel(energy=slice(-0.15,1)).data\n", + " y = -np.gradient(y)\n", + " y = y/np.max(y)\n", + " result = model.fit(y, best, x=x)\n", + " print(f'{i}: {result.best_values}')\n", + " plt.plot(x, y+i, 'bo')\n", + " plt.plot(x, result.best_fit+i, 'r-')\n" ] }, { @@ -509,32 +420,14 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [ - "# compare the two methods\n", - "fig, ax = plt.subplots(1,3, figsize=(10,4), layout='constrained')\n", - "res_poly.energy.attrs['unit'] = 'eV'\n", - "res_poly.mean('sampleBias').plot.line(x='energy',linewidth=3, ax=ax[0], label='all');\n", - "res_poly.plot.line(x='energy',linewidth=1,alpha=.5,ax=ax[0]);\n", - "ax[0].set_title('poly')\n", - "res_fit.energy.attrs['unit'] = 'eV'\n", - "res_fit.mean('sampleBias').plot.line(x='energy',linewidth=3, ax=ax[1], label='all');\n", - "res_fit.plot.line(x='energy',linewidth=1,alpha=.5,ax=ax[1]);\n", - "ax[1].set_title('fit')\n", - "res_config.energy.attrs['unit'] = 'eV'\n", - "res_config.mean('sampleBias').plot.line(x='energy',linewidth=3, ax=ax[2]);\n", - "res_config.plot.line(x='energy',linewidth=1,alpha=.5,label='all',ax=ax[2]);\n", - "ax[2].set_title('config')\n", - "ax[0].set_xlim(-2, 1.5)\n", - "ax[1].set_xlim(-2, 1.5)\n", - "ax[2].set_xlim(-2, 1.5)" - ] + "source": [] } ], "metadata": { "kernelspec": { - "display_name": ".pyenv", + "display_name": "sed38", "language": "python", - "name": "python3" + "name": "sed38" }, "language_info": { "codemirror_mode": { @@ -546,7 +439,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.12" + "version": "3.8.18" } }, "nbformat": 4, diff --git a/tutorial/hextof_config.yaml b/tutorial/hextof_config.yaml index e76da9c0..0a8289af 100644 --- a/tutorial/hextof_config.yaml +++ b/tutorial/hextof_config.yaml @@ -5,9 +5,9 @@ core: beamline: pg2 instrument: hextof paths: - data_raw_dir: "/asap3/flash/gpfs/pg2/2023/data/11019101/raw/hdf/offline/fl1user3" + data_raw_dir: "/path/to/data" # change this to a local directory where you want to store the parquet files - data_parquet_dir: "/home/agustsss/temp/sed_parquet" + data_parquet_dir: "/path/to/parquet" binning: num_cores: 10 @@ -28,8 +28,8 @@ dataframe: tof_ns_column: dldTime corrected_tof_column: "tm" bias_column: "sampleBias" - tof_binwidth: 2.0576131995767355E-12 - tof_binning: 7 # with 3, 8 bins per step 2**3 + tof_binwidth: 2.0576131995767355E-11 # in seconds + tof_binning: 3 sector_id_column: dldSectorID sector_delays: [0., 0., 0., 0., 0., 0., 0., 0.] jitter_cols: ["dldPosX", "dldPosY", "dldTimeSteps"] @@ -122,14 +122,19 @@ dataframe: energy: calibration: - offset: 4150.0 - coeffs: [-2.01882455e-05, 1.94714008e-01] - Tmat: [[ 1.01040e+06, 1.20000e+02], - [ 7.18675e+05, 8.50000e+01], - [ 3.82275e+05, 4.50000e+01], - [-3.86325e+05, -4.50000e+01]] - bvec: [ 3., 2., 1., -1.] + d: 2.7342492951998603 + t0: 3.6049383256584405e-08 + E0: -51.289659014865784 energy_scale: kinetic - axis: -463.3385531514501 - E0: -463.3385531514501 - refid: 3 + refid: 0 + offset: + constant: 2.0 + sampleBias: + sign: 1 + substract_mean: True + monochromatorPhotonEnergy: + sign: -1 + substract_mean: True + tofVoltage: + sign: -1 + substract_mean: True