|
160 | 160 | " '611': sc.scalar(5.05, unit='deg'),\n", |
161 | 161 | "}\n", |
162 | 162 | "\n", |
163 | | - "results = {}\n", |
| 163 | + "reflectivity = {}\n", |
164 | 164 | "for file, angle in runs.items():\n", |
165 | 165 | " workflow[Filename[SampleRun]] = amor.data.amor_sample_run(file)\n", |
166 | 166 | " workflow[SampleRotation[SampleRun]] = angle\n", |
167 | | - " results[file] = workflow.compute(ReflectivityOverQ).hist()\n", |
| 167 | + " reflectivity[file] = workflow.compute(ReflectivityOverQ).hist()\n", |
168 | 168 | "\n", |
169 | | - "sc.plot(results, norm='log', vmin=1e-4)" |
| 169 | + "sc.plot(reflectivity, norm='log', vmin=1e-4)" |
| 170 | + ] |
| 171 | + }, |
| 172 | + { |
| 173 | + "cell_type": "markdown", |
| 174 | + "metadata": {}, |
| 175 | + "source": [ |
| 176 | + "## Scaling the reflectivity curves to overlap\n", |
| 177 | + "\n", |
| 178 | + "In case we know the curves are have been scaled by different factors (that are constant in Q) it can be useful to scale them so they overlap:" |
170 | 179 | ] |
171 | 180 | }, |
172 | 181 | { |
|
176 | 185 | "outputs": [], |
177 | 186 | "source": [ |
178 | 187 | "from ess.reflectometry.tools import scale_reflectivity_curves_to_overlap\n", |
179 | | - "results_scaled = dict(zip(\n", |
180 | | - " results.keys(),\n", |
181 | | - " scale_reflectivity_curves_to_overlap(results.values())[0],\n", |
182 | | - " strict=True\n", |
183 | | - "))\n", |
184 | | - "sc.plot(results_scaled, norm='log', vmin=1e-5)" |
| 188 | + "\n", |
| 189 | + "scaled_reflectivity_curves, scale_factors = scale_reflectivity_curves_to_overlap(reflectivity.values())\n", |
| 190 | + "sc.plot(dict(zip(reflectivity.keys(), scaled_reflectivity_curves, strict=True)), norm='log', vmin=1e-5)" |
| 191 | + ] |
| 192 | + }, |
| 193 | + { |
| 194 | + "cell_type": "markdown", |
| 195 | + "metadata": {}, |
| 196 | + "source": [ |
| 197 | + "Curves obtained from measurements at different angles can be combined to one common reflectivity curve:" |
185 | 198 | ] |
186 | 199 | }, |
187 | 200 | { |
|
191 | 204 | "outputs": [], |
192 | 205 | "source": [ |
193 | 206 | "from ess.reflectometry.tools import combine_curves\n", |
194 | | - "c = combine_curves(results_scaled.values(), workflow.compute(QBins))\n", |
195 | | - "c.plot(norm='log')" |
| 207 | + "combined = combine_curves(scaled_reflectivity_curves, workflow.compute(QBins))\n", |
| 208 | + "combined.plot(norm='log')" |
196 | 209 | ] |
197 | 210 | }, |
198 | 211 | { |
199 | 212 | "cell_type": "markdown", |
200 | 213 | "metadata": {}, |
201 | 214 | "source": [ |
202 | | - "### Additional diagnostics plots" |
| 215 | + "## Diagnostic figures" |
| 216 | + ] |
| 217 | + }, |
| 218 | + { |
| 219 | + "cell_type": "markdown", |
| 220 | + "metadata": {}, |
| 221 | + "source": [ |
| 222 | + "There are some useful visualizations that can be used to troubleshoot the instrument.\n", |
| 223 | + "They typically operate on the `ReflectivityData`.\n", |
| 224 | + "\n", |
| 225 | + "The difference between `ReflectivityData` and `ReflectivityOverQ` is that `ReflectivityData` is not binned in $Q$, but instead has the same shape as the reference.\n", |
| 226 | + "\n", |
| 227 | + "Essentially it represents a \"reflectivity\" computed in every wavelength-detector coordinate (`z_index`) bin.\n", |
| 228 | + "This makes it easier to spot inhomogeneities and diagnose problems." |
203 | 229 | ] |
204 | 230 | }, |
205 | 231 | { |
|
208 | 234 | "metadata": {}, |
209 | 235 | "outputs": [], |
210 | 236 | "source": [ |
211 | | - "workflow[Filename[SampleRun]] = amor.data.amor_sample_run(608)\n", |
212 | | - "workflow[SampleRotation[SampleRun]] = sc.scalar(0.85, unit='deg')\n", |
213 | | - "workflow.compute(ReflectivityDiagnosticsView)" |
| 237 | + "# Start by computing the `ReflectivityData` for each of the files\n", |
| 238 | + "diagnostics = {}\n", |
| 239 | + "for file, angle in runs.items():\n", |
| 240 | + " workflow[Filename[SampleRun]] = amor.data.amor_sample_run(file)\n", |
| 241 | + " workflow[SampleRotation[SampleRun]] = angle\n", |
| 242 | + " diagnostics[file] = workflow.compute((ReflectivityData, ThetaBins[SampleRun]))\n", |
| 243 | + "\n", |
| 244 | + "# Scale the results using the scale factors computed earlier\n", |
| 245 | + "for key, scale_factor in zip(reflectivity.keys(), scale_factors, strict=True):\n", |
| 246 | + " diagnostics[key][ReflectivityData] *= scale_factor" |
214 | 247 | ] |
215 | 248 | }, |
216 | 249 | { |
217 | 250 | "cell_type": "markdown", |
218 | 251 | "metadata": {}, |
219 | 252 | "source": [ |
220 | | - "## Make a $(\\lambda, \\theta)$ map\n", |
| 253 | + "### Make a $(\\lambda, \\theta)$ map\n", |
221 | 254 | "A good sanity check is to create a two-dimensional map of the counts in $\\lambda$ and $\\theta$ bins and make sure the triangles converge at the origin." |
222 | 255 | ] |
223 | 256 | }, |
|
227 | 260 | "metadata": {}, |
228 | 261 | "outputs": [], |
229 | 262 | "source": [ |
230 | | - "workflow.compute(WavelengthThetaFigure)" |
| 263 | + "from ess.amor.figures import wavelength_theta_figure\n", |
| 264 | + "\n", |
| 265 | + "wavelength_theta_figure(\n", |
| 266 | + " [res[ReflectivityData] for res in diagnostics.values()],\n", |
| 267 | + " theta_bins=[res[ThetaBins[SampleRun]] for res in diagnostics.values()],\n", |
| 268 | + " q_edges_to_display=(sc.scalar(0.018, unit='1/angstrom'), sc.scalar(0.113, unit='1/angstrom'))\n", |
| 269 | + ")" |
231 | 270 | ] |
232 | 271 | }, |
233 | 272 | { |
|
237 | 276 | "This plot can be used to check if the value of the sample rotation angle $\\omega$ is correct. The bright triangles should be pointing back to the origin $\\lambda = \\theta = 0$. In the figure above the black lines are all passing through the origin." |
238 | 277 | ] |
239 | 278 | }, |
| 279 | + { |
| 280 | + "cell_type": "markdown", |
| 281 | + "metadata": {}, |
| 282 | + "source": [ |
| 283 | + "### Make a $(Q, \\theta)$ map\n", |
| 284 | + "Another good sanity check is to create a two-dimensional map of the counts in $\\lambda$ and $Q$ and make sure the stripes are vertical. If they are not that could indicate that the `ChopperPhase` setting is incorrect." |
| 285 | + ] |
| 286 | + }, |
| 287 | + { |
| 288 | + "cell_type": "code", |
| 289 | + "execution_count": null, |
| 290 | + "metadata": {}, |
| 291 | + "outputs": [], |
| 292 | + "source": [ |
| 293 | + "from ess.amor.figures import q_theta_figure\n", |
| 294 | + "\n", |
| 295 | + "q_theta_figure(\n", |
| 296 | + " [res[ReflectivityData] for res in diagnostics.values()],\n", |
| 297 | + " theta_bins=[res[ThetaBins[SampleRun]] for res in diagnostics.values()],\n", |
| 298 | + " q_bins=workflow.compute(QBins)\n", |
| 299 | + ")" |
| 300 | + ] |
| 301 | + }, |
| 302 | + { |
| 303 | + "cell_type": "markdown", |
| 304 | + "metadata": {}, |
| 305 | + "source": [ |
| 306 | + "### Compare the sample measurement to the reference on the detector\n", |
| 307 | + "\n", |
| 308 | + "Here we compare the raw number of counts on the detector for the sample measurement and the reference respectively.\n", |
| 309 | + "\n", |
| 310 | + "`z_index` is the $z-$detector coordinate, that is, the detector coordinate in the direction of the scattering angle $\\theta$." |
| 311 | + ] |
| 312 | + }, |
| 313 | + { |
| 314 | + "cell_type": "code", |
| 315 | + "execution_count": null, |
| 316 | + "metadata": {}, |
| 317 | + "outputs": [], |
| 318 | + "source": [ |
| 319 | + "from ess.amor.figures import wavelength_z_figure\n", |
| 320 | + "\n", |
| 321 | + "workflow[Filename[SampleRun]] = amor.data.amor_sample_run('608')\n", |
| 322 | + "workflow[SampleRotation[SampleRun]] = runs['608']\n", |
| 323 | + "wavelength_z_figure(\n", |
| 324 | + " workflow.compute(FootprintCorrectedData[SampleRun]),\n", |
| 325 | + " wavelength_bins=workflow.compute(WavelengthBins),\n", |
| 326 | + " grid=False\n", |
| 327 | + ") + wavelength_z_figure(\n", |
| 328 | + " reference_result,\n", |
| 329 | + " grid=False\n", |
| 330 | + ")" |
| 331 | + ] |
| 332 | + }, |
240 | 333 | { |
241 | 334 | "cell_type": "markdown", |
242 | 335 | "metadata": {}, |
|
0 commit comments