diff --git a/prototypes/dynamicmethod.ipynb b/prototypes/dynamicmethod.ipynb new file mode 100644 index 0000000..3e083b4 --- /dev/null +++ b/prototypes/dynamicmethod.ipynb @@ -0,0 +1,554 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "b2c12528-0a66-4841-886a-3334c8233861", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext line_profiler" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2de6503b-a563-47d0-8006-2b99af1faf1c", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "1de23154-dec3-4fbf-8d3b-eb15c1af7404", + "metadata": {}, + "outputs": [], + "source": [ + "%autoreload\n", + "from functools import cache, wraps\n", + "import inspect\n", + "\n", + "import wrapt\n", + "from frozendict import frozendict\n", + "\n", + "from microscope_calibration.common.model import symbol_maker, lambdify as mc_lambdify\n", + "import sympy as sym\n", + "import jax\n", + "import jax_dataclasses as jdc" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "3f4c959f-a0fa-4a56-9248-3cc6bb44e0a1", + "metadata": {}, + "outputs": [], + "source": [ + "@jdc.pytree_dataclass\n", + "class Ray:\n", + " y: float\n", + " x: float\n", + "\n", + "@jdc.pytree_dataclass\n", + "class Params:\n", + " a: float\n", + " b: float\n", + "\n", + " @classmethod\n", + " @cache\n", + " def _mk_trace(cls, modules):\n", + " def _trace_impl(arg) -> Ray:\n", + " self, ray = arg\n", + " return Ray(\n", + " y=sym.Heaviside(self.a) * ray.y,\n", + " x=ray.x + self.b,\n", + " )\n", + "\n", + " p = symbol_maker(cls)\n", + " r = symbol_maker(Ray)\n", + " \n", + " outer = mc_lambdify((p, r), _trace_impl((p, r)), modules=modules)\n", + " return outer\n", + "\n", + " def trace(self, ray: Ray, modules=sym) -> Ray:\n", + " f = self._mk_trace(modules=modules)\n", + " res = f((self, ray))\n", + " return res" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6b456a6d-e009-4bd8-ba7e-ab4a704fb448", + "metadata": {}, + "outputs": [], + "source": [ + "%autoreload\n", + "p = symbol_maker(Params, postfix=\"param\")\n", + "r = symbol_maker(Ray, postfix=\"ray\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f001facd-c0a8-4d85-83ad-a7c08b1aba48", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 27.1 ms, sys: 158 μs, total: 27.3 ms\n", + "Wall time: 26.9 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "Ray(y=y_ray_0*Heaviside(a_param_0, 0.5), x=b_param_1 + x_ray_1)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time p.trace(r)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "64304f0d-bb53-4b11-9c9f-c7d4a5f800a0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 113 μs, sys: 0 ns, total: 113 μs\n", + "Wall time: 117 μs\n" + ] + }, + { + "data": { + "text/plain": [ + "Ray(y=y_ray_0*Heaviside(a_param_0, 0.5), x=b_param_1 + x_ray_1)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time p.trace(r)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "3b56fb18-0261-4861-8c8a-cc048cc367fe", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:2026-03-14 18:11:50,320:jax._src.xla_bridge:864: An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n" + ] + } + ], + "source": [ + "pp = Params(a=0, b=1)\n", + "rr = Ray(y=jax.numpy.array((0, 1, 2)), x=jax.numpy.array((3, 2, 1)))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a3f1f9fc-c239-49cf-a856-b66e9f3a9506", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 47 ms, sys: 3.71 ms, total: 50.7 ms\n", + "Wall time: 49.6 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "Ray(y=Array([0. , 0.5, 1. ], dtype=float64, weak_type=True), x=Array([4, 3, 2], dtype=int64))" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time pp.trace(rr, modules=jax.numpy)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "215a3697-64f4-43e0-aec5-beb218b0043a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 452 μs, sys: 25 μs, total: 477 μs\n", + "Wall time: 318 μs\n" + ] + }, + { + "data": { + "text/plain": [ + "Ray(y=Array([0. , 0.5, 1. ], dtype=float64, weak_type=True), x=Array([4, 3, 2], dtype=int64))" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time pp.trace(rr, modules=jax.numpy)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "0c8fc41b-2521-4f74-be10-5b9c95352459", + "metadata": {}, + "outputs": [], + "source": [ + "def deco_me(a: Ray, b: int=23):\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "e57c9d83-6bb3-424a-94c7-db6da69424c5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(23,), kwonlyargs=[], kwonlydefaults=None, annotations={'a': , 'b': })" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# inspect.get_annotations(deco_me)\n", + "inspect.getfullargspec(deco_me)" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "id": "ca9de0ad-04a2-42b9-a4f7-f48d8fa2ed70", + "metadata": {}, + "outputs": [], + "source": [ + "def normalized_args(wrapped, args, kwargs):\n", + " sig = inspect.signature(wrapped)\n", + " bound = sig.bind(*args, **kwargs)\n", + " bound.apply_defaults()\n", + " all_args = bound.arguments\n", + " return all_args\n", + "\n", + "\n", + "@cache\n", + "def actual_lambdify(wrapped, modules, sample_args, sample_kwargs, sym_kwargs):\n", + " sig = inspect.signature(wrapped)\n", + " spec = inspect.getfullargspec(wrapped)\n", + " partial_bound = sig.bind_partial(*sample_args, **sample_kwargs)\n", + " # partial_bound.apply_defaults()\n", + " generated_args = {}\n", + " for arg, cls in spec.annotations.items():\n", + " if arg not in partial_bound.arguments:\n", + " generated_args[arg] = symbol_maker(cls)\n", + " partial_bound.arguments.update(generated_args)\n", + " normalized = normalized_args(wrapped, args=tuple(), kwargs=partial_bound.arguments)\n", + "\n", + " f = mc_lambdify(normalized, wrapped(**normalized), modules=modules, **sym_kwargs)\n", + " return f\n", + "\n", + "\n", + "def lambdify(recurse_for=tuple(), modules=sym, args=None, kwargs=None, **sym_kwargs):\n", + " # Rename to keep outer API succinct\n", + " sample_args = tuple() if args is None else args \n", + " sample_kwargs = {} if kwargs is None else kwargs\n", + " \n", + " @wrapt.decorator\n", + " def lambdified(wrapped, instance, args, kwargs):\n", + " n_args = normalized_args(wrapped, args, kwargs)\n", + " f = actual_lambdify(\n", + " wrapped=wrapped,\n", + " modules=modules,\n", + " sample_args=frozendict(sample_args),\n", + " sample_kwargs=frozendict(sample_kwargs),\n", + " sym_kwargs=frozendict(sym_kwargs),\n", + " )\n", + " return f(n_args)\n", + "\n", + " return lambdified" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "id": "ec83ad1b-f605-442d-bea0-923ab4ba08e8", + "metadata": {}, + "outputs": [], + "source": [ + "@lambdify(modules=jax.numpy)\n", + "def deco_me(a: Ray, b: int=23):\n", + " print(\"deco_me\", a, b)\n", + " return (a, b)" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "5a070238-00bc-46a4-84c2-10d2a72de41f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 151 μs, sys: 0 ns, total: 151 μs\n", + "Wall time: 159 μs\n" + ] + }, + { + "data": { + "text/plain": [ + "(Ray(y=1, x=1), 23)" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time deco_me(Ray(y=1, x=1))" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "98a840ef-9fe4-4018-a262-6b8664feac8b", + "metadata": {}, + "outputs": [], + "source": [ + "sig = inspect.signature(deco_me)\n", + "spec = inspect.getfullargspec(deco_me)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "250efb8e-61be-4495-a6b3-32b43a1061d6", + "metadata": {}, + "outputs": [], + "source": [ + "partial_bound = sig.bind_partial(b=42)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "7416790e-7cc2-4aff-b548-8b2e3b22db16", + "metadata": {}, + "outputs": [], + "source": [ + "partial_bound.apply_defaults()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "7833e80b-5a50-4151-99b2-ea45f02c622d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'b': 42}" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "partial_bound.arguments" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "a943848a-0260-427e-9547-c362876d4f6b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Ray(y=y_0, x=x_1)" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%autoreload\n", + "symbol_maker(Ray)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "8a906a51-7789-4777-8441-b26e29e51a68", + "metadata": {}, + "outputs": [], + "source": [ + "@jdc.pytree_dataclass\n", + "class BadRay:\n", + " y: float\n", + " x: str" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "d89288f7-3723-4f93-add0-9c77416c63b5", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "Can't generate symbol for type .", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mTypeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[22]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43msymbol_maker\u001b[49m\u001b[43m(\u001b[49m\u001b[43mBadRay\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/src/Microscope-Calibration/src/microscope_calibration/common/model.py:232\u001b[39m, in \u001b[36msymbol_maker\u001b[39m\u001b[34m(params_cls, postfix, recurse_for)\u001b[39m\n\u001b[32m 229\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 230\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mCan\u001b[39m\u001b[33m'\u001b[39m\u001b[33mt generate symbol for type \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mparams_cls\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m.\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m232\u001b[39m (_, res) = \u001b[43msymbol_maker_inner\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 233\u001b[39m \u001b[43m \u001b[49m\u001b[43mparams_cls\u001b[49m\u001b[43m=\u001b[49m\u001b[43mparams_cls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 234\u001b[39m \u001b[43m \u001b[49m\u001b[43mpostfix\u001b[49m\u001b[43m=\u001b[49m\u001b[43mpostfix\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 235\u001b[39m \u001b[43m \u001b[49m\u001b[43mrecurse_for\u001b[49m\u001b[43m=\u001b[49m\u001b[43mrecurse_for\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 236\u001b[39m \u001b[43m \u001b[49m\u001b[43mindex\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m0\u001b[39;49m\n\u001b[32m 237\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 238\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m res\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/src/Microscope-Calibration/src/microscope_calibration/common/model.py:221\u001b[39m, in \u001b[36msymbol_maker..symbol_maker_inner\u001b[39m\u001b[34m(params_cls, postfix, recurse_for, index)\u001b[39m\n\u001b[32m 219\u001b[39m index += \u001b[32m1\u001b[39m\n\u001b[32m 220\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m221\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mCan\u001b[39m\u001b[33m'\u001b[39m\u001b[33mt generate symbol for type \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mcls\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m.\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 222\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m (index, params_cls(**symbols_dict))\n\u001b[32m 223\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28missubclass\u001b[39m(params_cls, Number):\n", + "\u001b[31mTypeError\u001b[39m: Can't generate symbol for type ." + ] + } + ], + "source": [ + "symbol_maker(BadRay)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e4911a9-7903-4ac0-876a-341b65fb8f69", + "metadata": {}, + "outputs": [], + "source": [ + "generated_args = {}\n", + "\n", + "for arg, cls in spec.annotations.items():\n", + " if arg not in partial_bound.arguments:\n", + " generated_args[arg] = symbol_maker(cls)\n", + "generated_args" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "655a825c-3ce8-40fa-9f08-86ff623542fb", + "metadata": {}, + "outputs": [], + "source": [ + "sig.parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec7ff917-f627-4d8f-90c6-d539b8b0a8ba", + "metadata": {}, + "outputs": [], + "source": [ + "from numbers import Number" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5fb0cd86-5a07-45e9-aade-e1070e2a9944", + "metadata": {}, + "outputs": [], + "source": [ + "issubclass(int, Number)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38e1b48e-c1f6-4275-b491-7757e28005b4", + "metadata": {}, + "outputs": [], + "source": [ + "int.__name__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c566e41-1ccb-4c05-a8ae-8dc212c8c197", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.13 (microscope_calibration)", + "language": "python", + "name": "calib313" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/prototypes/sympy/Adjustment_functions.ipynb b/prototypes/sympy/Adjustment_functions.ipynb new file mode 100644 index 0000000..a348192 --- /dev/null +++ b/prototypes/sympy/Adjustment_functions.ipynb @@ -0,0 +1,1155 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "80aeeab9-b0a9-4341-8d77-d25527d9fd0d", + "metadata": {}, + "outputs": [], + "source": [ + "from microscope_calibration.common.model import Parameters4DSTEM, DescanError, trace, PixelYX, Model4DSTEM, Ray, symbol_maker, sympy_equals, scale_rotate_flip\n", + "\n", + "from temgym_core.run import run_iter\n", + "\n", + "import jax\n", + "import jax.numpy as jnp\n", + "import numpy as np\n", + "import sympy as sym\n", + "from sympy import S\n", + "from types import ModuleType" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4a8c26d8-beb7-4e3a-8a80-d2b190933fbb", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy.abc import a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x\n", + "parms= Parameters4DSTEM(\n", + " overfocus=0.01*a,\n", + " scan_pixel_pitch=1e-6*b,\n", + " scan_center=PixelYX(0.1*c, 0.2*d),\n", + " scan_rotation=0.01*e,\n", + " camera_length=1.0*f,\n", + " detector_pixel_pitch=50e-6*g,\n", + " detector_center=PixelYX(0.1*h, 0.1*i),\n", + " semiconv=1e-3*j, # radian\n", + " flip_factor=1.0*k,\n", + " descan_error=DescanError(pxo_pxi=l, pxo_pyi=m, pyo_pxi=n, pyo_pyi=o, sxo_pxi=p, sxo_pyi=q, syo_pxi=r, syo_pyi=s, offpxi=t, offpyi=u, offsxi=v, offsyi=w),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e4462a06-a318-4397-9b95-7baf4061b4ce", + "metadata": {}, + "outputs": [], + "source": [ + "def make_a_function(a: str):\n", + " def f():\n", + " return f'hello {a}'\n", + " return f" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "98ecd5a8-e7fb-4795-ae00-1be31bac7495", + "metadata": {}, + "outputs": [], + "source": [ + "f = make_a_function('Margarita')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "5c8cdc3b-88d9-4781-bba2-6f6c7e248037", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'hello Margarita'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "eb4ad431-2c69-4778-8aec-3d21dd77c25c", + "metadata": {}, + "outputs": [], + "source": [ + "def scale(factor):\n", + " return sym.eye(2) * factor\n", + "\n", + "\n", + "def rotate(radians):\n", + " return sym.rot_givens(0, 1, radians, dim=2)\n", + "\n", + "\n", + "# The flip_factor is introduced to make it differentiable\n", + "def flip_y(flip_factor: sym.Float = sym.S(-1.0)):\n", + " return sym.Matrix([[flip_factor, 0], [0, 1]])\n", + "\n", + "\n", + "def identity():\n", + " return sym.eye(2)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "6a862dd5-a010-48fa-a536-efd4eca51ab5", + "metadata": {}, + "outputs": [], + "source": [ + "def scale_rotate_flip(mat: sym.Matrix):\n", + " \"\"\"\n", + " Deconstruct a matrix generated with scale() @ rotate() @ flip_y()\n", + " into the individual parameters\n", + " \"\"\"\n", + " scale_y = mat[:, 0].norm()\n", + " scale_x = mat[:, 1].norm()\n", + " \n", + " if not scale_x.equals(scale_y):\n", + " raise ValueError(f\"y scale {scale_y} and x scale {scale_x} are different.\")\n", + "\n", + " scan_rot_flip = mat / scale_y\n", + "\n", + " # 2D cross product\n", + " flip_factor = (\n", + " scan_rot_flip[0, 0] * scan_rot_flip[1, 1]\n", + " - scan_rot_flip[0, 1] * scan_rot_flip[1, 0]\n", + " )\n", + " # undo flip_y\n", + " rot = scan_rot_flip.copy()\n", + " rot[:, 0] = rot[:, 0] * flip_factor\n", + " rot = sym.simplify(rot)\n", + "\n", + " angle1 = sym.atan2(-rot[1, 0], rot[0, 0])\n", + " angle2 = sym.atan2(rot[0, 1], rot[1, 1])\n", + "\n", + " # So far not reached in tests since inconsistencies are caught as shear before\n", + " if not sym.Matrix([sym.sin(angle1), sym.cos(angle1)]).equals(sym.Matrix([sym.sin(angle2), sym.cos(angle2)])):\n", + " raise ValueError(\n", + " f\"Rotation angle 1 {angle1} and rotation angle 2 {angle2} are inconsistent.\"\n", + " )\n", + "\n", + " if sym.Or(sym.And(mat[0,0].equals(mat[1,1]), mat[0,1].equals(-mat[1,0])), sym.And(mat[0,0].equals(-mat[1,1]), mat[0,1].equals(mat[1,0]))) is not sym.S.true:\n", + " raise ValueError(f\"y scale {scale_y} and x scale {scale_x} are different or rotation angle 1 {angle1} and rotation angle 2 {angle2} are inconsistent.\")\n", + " \n", + " return (scale_y, angle1, flip_factor)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "32eecf14-49d8-4c85-a9fb-9a5da3d43eb6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle 0$" + ], + "text/plain": [ + "0" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a, b, c, d = sym.symbols('a b c d', real=True)\n", + "expr = sym.sqrt(a**2+c**2)-sym.sqrt(b**2+d**2)\n", + "sol_c = sym.solve(expr, c)[0]\n", + "sol_d = sym.solve(expr, d)[0]\n", + "expr_subs = expr.subs(c, sol_c).subs(d, sol_d)\n", + "expr_subs\n", + "#scale_rotate_flip(sym.Matrix([[a,b],[b,a]]))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "c37a920a-ba09-4d6c-90f1-b33d2ea3acd0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scale_rotate_flip(sym.Matrix([[0,0],[0,0]]))\n", + "sym.And(a.equals(a)) is sym.S.true" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "479d406c-ac96-4158-8922-441847448a03", + "metadata": {}, + "outputs": [], + "source": [ + "a, b, c, d = sym.symbols('a b c d', real=True)\n", + "mat = sym.Matrix([[a,b],[c,d]])\n", + "scale_y = mat[:, 0].norm()\n", + "mat = mat/scale_y\n", + "flip = mat[0,0]*mat[1,1] - mat[0,1]*mat[1,0]\n", + "mat[:,0] = mat[:,0]*flip\n", + "#sym.solve([expr, sym.sin(sym.atan2(-mat[1,0], mat[0,0]))-sym.sin(sym.atan2(mat[0,1],mat[1,1])), sym.cos(sym.atan2(-mat[1,0], mat[0,0]))-sym.cos(sym.atan2(mat[0,1],mat[1,1]))], [c, d], dict=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "630e48e7-45c5-416c-a811-6b5ff3121bf5", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "944a9250-68d8-408f-9e13-f59fb0fd9018", + "metadata": {}, + "outputs": [], + "source": [ + "def derive(\n", + " params: Parameters4DSTEM,\n", + " overfocus: sym.Basic | None = None, # m\n", + " scan_pixel_pitch: sym.Basic | None = None, # m\n", + " scan_center: PixelYX | None = None,\n", + " scan_rotation: sym.Basic | None = None, # rad\n", + " camera_length: sym.Basic | None = None, # m\n", + " detector_pixel_pitch: sym.Basic | None = None, # m\n", + " detector_center: PixelYX | None = None,\n", + " detector_rotation: sym.Basic | None = None, # rad\n", + " semiconv: sym.Basic | None = None, # rad\n", + " flip_y: sym.logic.boolalg.Boolean | None = None,\n", + " flip_factor: sym.Basic | None = None,\n", + " descan_error: DescanError | None = None,\n", + ") -> \"Parameters4DSTEM\":\n", + " if flip_factor is not None:\n", + " assert flip_y is None\n", + " if flip_y is not None:\n", + " flip_factor = -1. if flip_y else 1.\n", + " \n", + " self = params\n", + " \n", + " return Parameters4DSTEM(\n", + " overfocus=overfocus if overfocus is not None else self.overfocus,\n", + " scan_pixel_pitch=(\n", + " scan_pixel_pitch\n", + " if scan_pixel_pitch is not None\n", + " else self.scan_pixel_pitch\n", + " ),\n", + " scan_center=scan_center if scan_center is not None else self.scan_center,\n", + " scan_rotation=scan_rotation\n", + " if scan_rotation is not None\n", + " else self.scan_rotation,\n", + " camera_length=camera_length\n", + " if camera_length is not None\n", + " else self.camera_length,\n", + " detector_pixel_pitch=(\n", + " detector_pixel_pitch\n", + " if detector_pixel_pitch is not None\n", + " else self.detector_pixel_pitch\n", + " ),\n", + " detector_center=(\n", + " detector_center if detector_center is not None else self.detector_center\n", + " ),\n", + " detector_rotation=(\n", + " detector_rotation\n", + " if detector_rotation is not None\n", + " else self.detector_rotation\n", + " ),\n", + " semiconv=semiconv if semiconv is not None else self.semiconv,\n", + " flip_factor=flip_factor if flip_factor is not None else self.flip_factor,\n", + " descan_error=descan_error\n", + " if descan_error is not None\n", + " else self.descan_error,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "0c87344a-83c5-4b8b-94a7-4e854493679a", + "metadata": {}, + "outputs": [], + "source": [ + "def adjust_scan_rotation(params, scan_rotation) -> \"Parameters4DSTEM\":\n", + " self = params\n", + " de = self.descan_error\n", + " angle = scan_rotation - self.scan_rotation\n", + "\n", + " # Rotate the input direction\n", + " pxo_pyi, pxo_pxi = rotate(angle).multiply(sym.Matrix([de.pxo_pyi, de.pxo_pxi])) # add simplificatoin? dotprodsimp=True\n", + " pyo_pyi, pyo_pxi = rotate(angle).multiply(sym.Matrix([de.pyo_pyi, de.pyo_pxi]))\n", + " sxo_pyi, sxo_pxi = rotate(angle).multiply(sym.Matrix([de.sxo_pyi, de.sxo_pxi]))\n", + " syo_pyi, syo_pxi = rotate(angle).multiply(sym.Matrix([de.syo_pyi, de.syo_pxi]))\n", + " new_de = DescanError(\n", + " pxo_pyi=pxo_pyi,\n", + " pyo_pyi=pyo_pyi,\n", + " pxo_pxi=pxo_pxi,\n", + " pyo_pxi=pyo_pxi,\n", + " sxo_pyi=sxo_pyi,\n", + " syo_pyi=syo_pyi,\n", + " sxo_pxi=sxo_pxi,\n", + " syo_pxi=syo_pxi,\n", + " offpxi=de.offpxi,\n", + " offpyi=de.offpyi,\n", + " offsxi=de.offsxi,\n", + " offsyi=de.offsyi,\n", + " )\n", + " return self.derive(\n", + " scan_rotation=scan_rotation,\n", + " descan_error=new_de,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "d1e7c4be-f9f8-4e4f-ab1e-efaf245a946b", + "metadata": {}, + "outputs": [], + "source": [ + "def adjust_scan_pixel_pitch(params, scan_pixel_pitch: sym.Basic) -> \"Parameters4DSTEM\":\n", + " self = params\n", + " \n", + " de = self.descan_error\n", + " ratio = self.scan_pixel_pitch / scan_pixel_pitch\n", + "\n", + " new_de = DescanError(\n", + " pxo_pyi=de.pxo_pyi * ratio,\n", + " pyo_pyi=de.pyo_pyi * ratio,\n", + " pxo_pxi=de.pxo_pxi * ratio,\n", + " pyo_pxi=de.pyo_pxi * ratio,\n", + " sxo_pyi=de.sxo_pyi * ratio,\n", + " syo_pyi=de.syo_pyi * ratio,\n", + " sxo_pxi=de.sxo_pxi * ratio,\n", + " syo_pxi=de.syo_pxi * ratio,\n", + " offpxi=de.offpxi,\n", + " offpyi=de.offpyi,\n", + " offsxi=de.offsxi,\n", + " offsyi=de.offsyi,\n", + " )\n", + " return self.derive(\n", + " scan_pixel_pitch=scan_pixel_pitch,\n", + " descan_error=new_de,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "4ec2844f-121c-4c02-9ad4-3a5be0931c40", + "metadata": {}, + "outputs": [], + "source": [ + "def symbol_maker(params_cls, postfix, recurse_for=tuple()):\n", + " symbols_dict = {}\n", + " for attr in params_cls.__annotations__.keys():\n", + " cls = params_cls.__annotations__[attr]\n", + " if cls in recurse_for:\n", + " symbols_dict[attr] = symbol_maker(cls, postfix, recurse_for)\n", + " else:\n", + " symbols_dict[attr] = sym.symbols(f\"{attr}_{postfix}\")\n", + " return params_cls(**symbols_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "3e32d6cf-3198-4ff3-afd9-74ba1f54656a", + "metadata": {}, + "outputs": [], + "source": [ + "p = symbol_maker(Parameters4DSTEM, 'new', recurse_for=[DescanError, PixelYX])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52bee5af-20cc-4e25-ab74-e37b4dcc17e3", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "c0ae7318-2c5b-4289-95a8-099a4e45e2a2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Model4DSTEM(source=PointSource(z=0, semi_conv=0.023, offset_xy=CoordsXY(x=0.0, y=0.0)), scanner=Scanner(z=0.7, scan_pos_x=3.58496437813631, scan_pos_y=-13.9695393770694, scan_tilt_x=0.0, scan_tilt_y=0.0), specimen=Plane(z=0.7), descanner=Descanner(z=0.7, scan_pos_x=3.58496437813631, scan_pos_y=-13.9695393770694, scan_tilt_x=0.0, scan_tilt_y=0.0, descan_error=DescanError(pxo_pxi=2.37000000000000, pxo_pyi=0, pyo_pxi=0, pyo_pyi=0, sxo_pxi=0, sxo_pyi=0, syo_pxi=0, syo_pyi=0, offpxi=0.345, offpyi=0.0, offsxi=0.0, offsyi=0.0)), detector=Plane(z=3.0), _scan_to_real=Matrix([\n", + "[ 0.66093021614346, 1.88763641874927],\n", + "[-1.88763641874927, 0.66093021614346]]), _real_to_scan=Matrix([\n", + "[0.165232554035865, -0.471909104687317],\n", + "[0.471909104687317, 0.165232554035865]]), _detector_to_real=Matrix([\n", + "[-0.0247, 0],\n", + "[ 0, 0.0247]]), _real_to_detector=Matrix([\n", + "[-40.4858299595142, 0],\n", + "[ 0, 40.4858299595142]]), scan_center=PixelYX(y=17, x=13), detector_center=PixelYX(y=11, x=19), xp=)" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "params = Parameters4DSTEM(\n", + " overfocus=0.7,\n", + " scan_pixel_pitch=0.005,\n", + " scan_center=PixelYX(y=17, x=13),\n", + " scan_rotation=1.234,\n", + " camera_length=2.3,\n", + " detector_pixel_pitch=0.0247,\n", + " detector_center=PixelYX(y=11, x=19),\n", + " detector_rotation=2.134,\n", + " semiconv=0.023,\n", + " flip_factor=-1.,\n", + " descan_error=DescanError(offpxi=.345, pxo_pxi=948)\n", + " )\n", + "mod= Model4DSTEM.build(params=params, scan_pos=PixelYX(y=13, x=7))\n", + "\n", + "desc = mod._mk_adjust_scan_pixel_pitch()(mod, 2.)\n", + "desc" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "161685c2-69ea-44fc-a79b-dd56855429ec", + "metadata": {}, + "outputs": [], + "source": [ + "def adjust_detector_pixel_pitch(\n", + " params, detector_pixel_pitch: float\n", + " ) -> \"Parameters4DSTEM\":\n", + " de = params.descan_error\n", + " ratio = detector_pixel_pitch / params.detector_pixel_pitch\n", + "\n", + " new_de = DescanError(\n", + " pxo_pyi=de.pxo_pyi * ratio,\n", + " pyo_pyi=de.pyo_pyi * ratio,\n", + " pxo_pxi=de.pxo_pxi * ratio,\n", + " pyo_pxi=de.pyo_pxi * ratio,\n", + " sxo_pyi=de.sxo_pyi * ratio,\n", + " syo_pyi=de.syo_pyi * ratio,\n", + " sxo_pxi=de.sxo_pxi * ratio,\n", + " syo_pxi=de.syo_pxi * ratio,\n", + " offpxi=de.offpxi * ratio,\n", + " offpyi=de.offpyi * ratio,\n", + " offsxi=de.offsxi * ratio,\n", + " offsyi=de.offsyi * ratio,\n", + " )\n", + " return params.derive(\n", + " detector_pixel_pitch=detector_pixel_pitch,\n", + " descan_error=new_de,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "9bbdc9e0-9c1f-48cd-b2cf-91b1cba80814", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Parameters4DSTEM(overfocus=0.7, scan_pixel_pitch=0.005, scan_center=PixelYX(y=17, x=13), scan_rotation=1.234, camera_length=2.3, detector_pixel_pitch=2.0, detector_center=PixelYX(y=11, x=19), semiconv=0.023, flip_factor=-1.0, descan_error=DescanError(pxo_pxi=76761.13360323886, pxo_pyi=0.0, pyo_pxi=0.0, pyo_pyi=0.0, sxo_pxi=0.0, sxo_pyi=0.0, syo_pxi=0.0, syo_pyi=0.0, offpxi=27.935222672064775, offpyi=0.0, offsxi=0.0, offsyi=0.0), detector_rotation=2.134, xp=)" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "adjust_detector_pixel_pitch(params, 2.)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "e7f2ee0c-ba0c-483a-aa48-c8b8ce093645", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name '_mk_adjust_detector_pixel_pitch' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[19]\u001b[39m\u001b[32m, line 14\u001b[39m\n\u001b[32m 1\u001b[39m mod= Model4DSTEM.build(params=Parameters4DSTEM(\n\u001b[32m 2\u001b[39m overfocus=\u001b[32m0.7\u001b[39m,\n\u001b[32m 3\u001b[39m scan_pixel_pitch=\u001b[32m0.005\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 12\u001b[39m descan_error=DescanError(offpxi=\u001b[32m.345\u001b[39m, pxo_pxi=\u001b[32m948\u001b[39m)\n\u001b[32m 13\u001b[39m ), scan_pos=PixelYX(y=\u001b[32m13\u001b[39m, x=\u001b[32m7\u001b[39m))\n\u001b[32m---> \u001b[39m\u001b[32m14\u001b[39m m = \u001b[43m_mk_adjust_detector_pixel_pitch\u001b[49m()(mod, \u001b[32m2.\u001b[39m)\n\u001b[32m 15\u001b[39m \u001b[38;5;66;03m#m.params\u001b[39;00m\n\u001b[32m 16\u001b[39m mod\n", + "\u001b[31mNameError\u001b[39m: name '_mk_adjust_detector_pixel_pitch' is not defined" + ] + } + ], + "source": [ + "mod= Model4DSTEM.build(params=Parameters4DSTEM(\n", + " overfocus=0.7,\n", + " scan_pixel_pitch=0.005,\n", + " scan_center=PixelYX(y=17, x=13),\n", + " scan_rotation=1.234,\n", + " camera_length=2.3,\n", + " detector_pixel_pitch=0.0247,\n", + " detector_center=PixelYX(y=11, x=19),\n", + " detector_rotation=2.134,\n", + " semiconv=0.023,\n", + " flip_factor=-1.,\n", + " descan_error=DescanError(offpxi=.345, pxo_pxi=948)\n", + " ), scan_pos=PixelYX(y=13, x=7))\n", + "m = _mk_adjust_detector_pixel_pitch()(mod, 2.)\n", + "#m.params\n", + "mod" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "89c9b2af-be32-4e0d-9608-e54b79f65217", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle m$" + ], + "text/plain": [ + "m" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "d41f284c-c9f7-4f1e-9ee6-42e080e25fd3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(sym.Or(sym.And(sympy_equals(mat[0, 0], mat[1, 1]), sympy_equals(mat[0, 1], -mat[1, 0])),\n", + " sym.And(sympy_equals(mat[0, 0], -mat[1, 1]), sympy_equals(mat[0, 1], mat[1, 0])))\n", + " is sym.S.true)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "f268154c-e847-4222-a390-c01d8efc412f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sympy_equals(mat[0, 0], mat[1, 1])" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "a371ce22-72f5-4845-99b4-ca929589790f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sympy_equals(mat[0, 0], -mat[1, 1])" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "322f48db-76aa-45db-8308-adb3a5c2a10d", + "metadata": {}, + "outputs": [], + "source": [ + " def adjust_scan_rotation(params, scan_rotation: float) -> \"Parameters4DSTEM\":\n", + " self = params\n", + " de = self.descan_error\n", + " angle = scan_rotation - self.scan_rotation\n", + "\n", + " xp = self.xp\n", + "\n", + " # Rotate the input direction\n", + " pxo_pyi, pxo_pxi = rotate(angle) @ xp.array((de.pxo_pyi, de.pxo_pxi))\n", + " pyo_pyi, pyo_pxi = rotate(angle) @ xp.array((de.pyo_pyi, de.pyo_pxi))\n", + " sxo_pyi, sxo_pxi = rotate(angle) @ xp.array((de.sxo_pyi, de.sxo_pxi))\n", + " syo_pyi, syo_pxi = rotate(angle) @ xp.array((de.syo_pyi, de.syo_pxi))\n", + " new_de = DescanError(\n", + " pxo_pyi=pxo_pyi,\n", + " pyo_pyi=pyo_pyi,\n", + " pxo_pxi=pxo_pxi,\n", + " pyo_pxi=pyo_pxi,\n", + " sxo_pyi=sxo_pyi,\n", + " syo_pyi=syo_pyi,\n", + " sxo_pxi=sxo_pxi,\n", + " syo_pxi=syo_pxi,\n", + " offpxi=de.offpxi,\n", + " offpyi=de.offpyi,\n", + " offsxi=de.offsxi,\n", + " offsyi=de.offsyi,\n", + " )\n", + " return self.derive(\n", + " scan_rotation=scan_rotation,\n", + " descan_error=new_de,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "4beab556-1af9-40fc-965b-30eb32d7b681", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "DescanError(pxo_pxi=922.163869725063, pxo_pyi=-219.813096456279, pyo_pxi=0, pyo_pyi=0, sxo_pxi=0, sxo_pyi=0, syo_pxi=0, syo_pyi=0, offpxi=0.345, offpyi=0.0, offsxi=0.0, offsyi=0.0)" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "adjust_scan_rotation(params, 1).descan_error" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "09927198-f099-4903-8a05-361eb0706652", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'Symbol' object has no attribute 'array'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[26]\u001b[39m\u001b[32m, line 3\u001b[39m\n\u001b[32m 1\u001b[39m par_sympy = symbol_maker(Parameters4DSTEM, \u001b[33m'\u001b[39m\u001b[33msym\u001b[39m\u001b[33m'\u001b[39m, [DescanError, PixelYX])\n\u001b[32m 2\u001b[39m scan_rotation = sym.Symbol(\u001b[33m'\u001b[39m\u001b[33mscan_rotation\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m \u001b[43madjust_scan_rotation\u001b[49m\u001b[43m(\u001b[49m\u001b[43mpar_sympy\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mscan_rotation\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[24]\u001b[39m\u001b[32m, line 9\u001b[39m, in \u001b[36madjust_scan_rotation\u001b[39m\u001b[34m(params, scan_rotation)\u001b[39m\n\u001b[32m 6\u001b[39m xp = \u001b[38;5;28mself\u001b[39m.xp\n\u001b[32m 8\u001b[39m \u001b[38;5;66;03m# Rotate the input direction\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m9\u001b[39m pxo_pyi, pxo_pxi = rotate(angle) @ \u001b[43mxp\u001b[49m\u001b[43m.\u001b[49m\u001b[43marray\u001b[49m((de.pxo_pyi, de.pxo_pxi))\n\u001b[32m 10\u001b[39m pyo_pyi, pyo_pxi = rotate(angle) @ xp.array((de.pyo_pyi, de.pyo_pxi))\n\u001b[32m 11\u001b[39m sxo_pyi, sxo_pxi = rotate(angle) @ xp.array((de.sxo_pyi, de.sxo_pxi))\n", + "\u001b[31mAttributeError\u001b[39m: 'Symbol' object has no attribute 'array'" + ] + } + ], + "source": [ + "par_sympy = symbol_maker(Parameters4DSTEM, 'sym', [DescanError, PixelYX])\n", + "scan_rotation = sym.Symbol('scan_rotation')\n", + "adjust_scan_rotation(par_sympy, scan_rotation)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "7f7bf345-c40b-48d7-ba7c-cb5556add09a", + "metadata": {}, + "outputs": [], + "source": [ + " def adjust_scan_rotation(params, scan_rotation: float) -> \"Parameters4DSTEM\":\n", + " self = params\n", + " de = self.descan_error\n", + " angle = scan_rotation - self.scan_rotation\n", + "\n", + " xp = self.xp\n", + "\n", + " # Rotate the input direction\n", + " pxo_pyi, pxo_pxi = rotate(angle) @ sym.Matrix((de.pxo_pyi, de.pxo_pxi))\n", + " pyo_pyi, pyo_pxi = rotate(angle) @ sym.Matrix((de.pyo_pyi, de.pyo_pxi))\n", + " sxo_pyi, sxo_pxi = rotate(angle) @ sym.Matrix((de.sxo_pyi, de.sxo_pxi))\n", + " syo_pyi, syo_pxi = rotate(angle) @ sym.Matrix((de.syo_pyi, de.syo_pxi))\n", + " new_de = DescanError(\n", + " pxo_pyi=pxo_pyi,\n", + " pyo_pyi=pyo_pyi,\n", + " pxo_pxi=pxo_pxi,\n", + " pyo_pxi=pyo_pxi,\n", + " sxo_pyi=sxo_pyi,\n", + " syo_pyi=syo_pyi,\n", + " sxo_pxi=sxo_pxi,\n", + " syo_pxi=syo_pxi,\n", + " offpxi=de.offpxi,\n", + " offpyi=de.offpyi,\n", + " offsxi=de.offsxi,\n", + " offsyi=de.offsyi,\n", + " )\n", + " return self.derive(\n", + " scan_rotation=scan_rotation,\n", + " descan_error=new_de,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "c3167f13-cbdb-4f45-abe2-6b011caff94b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'pxo_pxi': pxo_pxi_old*cos(scan_rotation_new - scan_rotation_old) - pxo_pyi_old*sin(scan_rotation_new - scan_rotation_old),\n", + " 'pxo_pyi': pxo_pxi_old*sin(scan_rotation_new - scan_rotation_old) + pxo_pyi_old*cos(scan_rotation_new - scan_rotation_old),\n", + " 'pyo_pxi': pyo_pxi_old*cos(scan_rotation_new - scan_rotation_old) - pyo_pyi_old*sin(scan_rotation_new - scan_rotation_old),\n", + " 'pyo_pyi': pyo_pxi_old*sin(scan_rotation_new - scan_rotation_old) + pyo_pyi_old*cos(scan_rotation_new - scan_rotation_old),\n", + " 'sxo_pxi': sxo_pxi_old*cos(scan_rotation_new - scan_rotation_old) - sxo_pyi_old*sin(scan_rotation_new - scan_rotation_old),\n", + " 'sxo_pyi': sxo_pxi_old*sin(scan_rotation_new - scan_rotation_old) + sxo_pyi_old*cos(scan_rotation_new - scan_rotation_old),\n", + " 'syo_pxi': syo_pxi_old*cos(scan_rotation_new - scan_rotation_old) - syo_pyi_old*sin(scan_rotation_new - scan_rotation_old),\n", + " 'syo_pyi': syo_pxi_old*sin(scan_rotation_new - scan_rotation_old) + syo_pyi_old*cos(scan_rotation_new - scan_rotation_old),\n", + " 'offpxi': offpxi_old,\n", + " 'offpyi': offpyi_old,\n", + " 'offsxi': offsxi_old,\n", + " 'offsyi': offsyi_old}" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "par_sympy = symbol_maker(Parameters4DSTEM, 'old', [DescanError, PixelYX])\n", + "scan_rotation_new = sym.Symbol('scan_rotation_new')\n", + "adjust_scan_rotation(par_sympy, scan_rotation_new).descan_error._asdict()" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "7b2bd8cf-874f-4aec-a0c9-32c8c875778b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Parameters4DSTEM(overfocus=overfocus_old, scan_pixel_pitch=scan_pixel_pitch_old, scan_center=PixelYX(y=y_old, x=x_old), scan_rotation=scan_rotation_old, camera_length=camera_length_old, detector_pixel_pitch=detector_pixel_pitch_old, detector_center=PixelYX(y=y_old, x=x_old), semiconv=semiconv_old, flip_factor=flip_factor_old, descan_error=DescanError(pxo_pxi=pxo_pxi_old, pxo_pyi=pxo_pyi_old, pyo_pxi=pyo_pxi_old, pyo_pyi=pyo_pyi_old, sxo_pxi=sxo_pxi_old, sxo_pyi=sxo_pyi_old, syo_pxi=syo_pxi_old, syo_pyi=syo_pyi_old, offpxi=offpxi_old, offpyi=offpyi_old, offsxi=offsxi_old, offsyi=offsyi_old), detector_rotation=detector_rotation_old, xp=xp_old)" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "par_sympy" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "dafbb965-8c47-49b7-bd51-339b1f34bbf1", + "metadata": {}, + "outputs": [], + "source": [ + "def _mk_adjust_scan_rotation(cls=Model4DSTEM):\n", + " params_old = symbol_maker(Parameters4DSTEM, 'old', recurse_for=[DescanError, PixelYX])\n", + " params_new_tmp = symbol_maker(Parameters4DSTEM, 'new', recurse_for=[DescanError, PixelYX])\n", + "\n", + " params_new = params_old.derive(\n", + " descan_error=params_new_tmp.descan_error,\n", + " scan_rotation=params_new_tmp.scan_rotation\n", + " )\n", + "\n", + " def detector_px(m: Model4DSTEM):\n", + " ray = m.make_source_ray(\n", + " source_dy=0.,\n", + " source_dx=0.,\n", + " ).ray\n", + " data = m.trace(ray)\n", + " return data['detector'].sampling['detector_px']\n", + " \n", + " scan_pos_y, scan_pos_x = sym.symbols('scan_pos_y scan_pos_x')\n", + " scan_pos = PixelYX(scan_pos_y, scan_pos_x)\n", + "\n", + " model_old = cls.build(params=params_old, scan_pos=scan_pos)\n", + " model_new = cls.build(params=params_new, scan_pos=scan_pos)\n", + "\n", + " det_px_old = detector_px(model_old)\n", + " det_px_new = detector_px(model_new)\n", + "\n", + " expanded_expr_y = sym.expand(det_px_old.y - det_px_new.y)\n", + " expanded_expr_x = sym.expand(det_px_old.x - det_px_new.x)\n", + "\n", + " collected_y = sym.Poly(expanded_expr_y, params_old.camera_length, scan_pos_y, scan_pos_x)\n", + " collected_x = sym.Poly(expanded_expr_x, params_old.camera_length, scan_pos_y, scan_pos_x)\n", + " \n", + " coeff = collected_y.coeffs() + collected_x.coeffs()\n", + "\n", + " solution = sym.solve([eq for eq in coeff], [*params_new.descan_error],\n", + " simplify=True)\n", + "\n", + " def apply_function(m: Model4DSTEM, scan_rotation) -> Model4DSTEM:\n", + " substituted = sym.lambdify(\n", + " [\n", + " *params_old.descan_error,\n", + " params_old.scan_rotation,\n", + " params_new.scan_rotation\n", + " ], [solution[attr] for attr in [\n", + " *params_new.descan_error\n", + " ]])\n", + " \n", + " evaluated = substituted(\n", + " *m.descanner.descan_error,\n", + " float(m.params.scan_rotation), scan_rotation\n", + " )\n", + " new_de = DescanError(*evaluated)\n", + " new_params = m.params.derive(descan_error=new_de, scan_rotation=scan_rotation)\n", + " new_model = cls.build(new_params, m.scan_pos)\n", + " return new_model\n", + "\n", + " return apply_function" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "1dfc2247-36df-4680-b628-74ed59f93f31", + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "y scale 0.0247000000000000 and x scale 0.0247000000000000 are different or rotation angle 1 0 and rotation angle 2 0 are inconsistent.", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[31]\u001b[39m\u001b[32m, line 14\u001b[39m\n\u001b[32m 1\u001b[39m mod= Model4DSTEM.build(params=Parameters4DSTEM(\n\u001b[32m 2\u001b[39m overfocus=\u001b[32m0.7\u001b[39m,\n\u001b[32m 3\u001b[39m scan_pixel_pitch=\u001b[32m0.005\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 12\u001b[39m descan_error=DescanError(offpxi=\u001b[32m.345\u001b[39m, pxo_pxi=\u001b[32m948\u001b[39m)\n\u001b[32m 13\u001b[39m ), scan_pos=PixelYX(y=\u001b[32m13\u001b[39m, x=\u001b[32m7\u001b[39m))\n\u001b[32m---> \u001b[39m\u001b[32m14\u001b[39m \u001b[43m_mk_adjust_scan_rotation\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmod\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[32;43m1.\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[43mparams\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Microscope-Calibration/src/microscope_calibration/common/model.py:693\u001b[39m, in \u001b[36mModel4DSTEM.params\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 690\u001b[39m scan_scale, scan_rotation, scan_flip = scale_rotate_flip(\u001b[38;5;28mself\u001b[39m._scan_to_real)\n\u001b[32m 691\u001b[39m \u001b[38;5;66;03m# FIXME assert close to 1\u001b[39;00m\n\u001b[32m 692\u001b[39m \u001b[38;5;66;03m# assert scan_flip is False\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m693\u001b[39m detector_scale, detector_rotation, detector_flip = \u001b[43mscale_rotate_flip\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 694\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_detector_to_real\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 695\u001b[39m \u001b[38;5;66;03m# assert self.xp.allclose(detector_rotation, 0.0)\u001b[39;00m\n\u001b[32m 696\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m Parameters4DSTEM(\n\u001b[32m 697\u001b[39m overfocus=\u001b[38;5;28mself\u001b[39m.specimen.z - \u001b[38;5;28mself\u001b[39m.source.z,\n\u001b[32m 698\u001b[39m scan_pixel_pitch=scan_scale,\n\u001b[32m (...)\u001b[39m\u001b[32m 706\u001b[39m descan_error=\u001b[38;5;28mself\u001b[39m.descanner.descan_error,\n\u001b[32m 707\u001b[39m )\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Microscope-Calibration/src/microscope_calibration/common/model.py:81\u001b[39m, in \u001b[36mscale_rotate_flip\u001b[39m\u001b[34m(mat)\u001b[39m\n\u001b[32m 76\u001b[39m angle2 = sym.atan2(rot[\u001b[32m0\u001b[39m, \u001b[32m1\u001b[39m], rot[\u001b[32m1\u001b[39m, \u001b[32m1\u001b[39m])\n\u001b[32m 78\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m (sym.Or(sym.And(sympy_equals(mat[\u001b[32m0\u001b[39m, \u001b[32m0\u001b[39m], mat[\u001b[32m1\u001b[39m, \u001b[32m1\u001b[39m]), sympy_equals(mat[\u001b[32m0\u001b[39m, \u001b[32m1\u001b[39m], -mat[\u001b[32m1\u001b[39m, \u001b[32m0\u001b[39m])),\n\u001b[32m 79\u001b[39m sym.And(sympy_equals(mat[\u001b[32m0\u001b[39m, \u001b[32m0\u001b[39m], -mat[\u001b[32m1\u001b[39m, \u001b[32m1\u001b[39m]), sympy_equals(mat[\u001b[32m0\u001b[39m, \u001b[32m1\u001b[39m], mat[\u001b[32m1\u001b[39m, \u001b[32m0\u001b[39m])))\n\u001b[32m 80\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m sym.S.true):\n\u001b[32m---> \u001b[39m\u001b[32m81\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33my scale \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mscale_y\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m and x scale \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mscale_x\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m are different or rotation \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 82\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mangle 1 \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mangle1\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m and rotation angle 2 \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mangle2\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m are inconsistent.\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 84\u001b[39m \u001b[38;5;66;03m# So far not reached in tests since inconsistencies are caught as shear before\u001b[39;00m\n\u001b[32m 85\u001b[39m \u001b[38;5;66;03m# if not sym.Matrix([sym.sin(angle1), sym.cos(angle1)]).equals(sym.Matrix([sym.sin(angle2),\u001b[39;00m\n\u001b[32m 86\u001b[39m \u001b[38;5;66;03m# sym.cos(angle2)])):\u001b[39;00m\n\u001b[32m 87\u001b[39m \u001b[38;5;66;03m# raise ValueError(\u001b[39;00m\n\u001b[32m 88\u001b[39m \u001b[38;5;66;03m# f\"Rotation angle 1 {angle1} and rotation angle 2 {angle2} are inconsistent.\"\u001b[39;00m\n\u001b[32m 89\u001b[39m \u001b[38;5;66;03m# )\u001b[39;00m\n\u001b[32m 91\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m (sym.simplify(scale_y), sym.simplify(angle1), sym.simplify(flip_factor))\n", + "\u001b[31mValueError\u001b[39m: y scale 0.0247000000000000 and x scale 0.0247000000000000 are different or rotation angle 1 0 and rotation angle 2 0 are inconsistent." + ] + } + ], + "source": [ + "mod= Model4DSTEM.build(params=Parameters4DSTEM(\n", + " overfocus=0.7,\n", + " scan_pixel_pitch=0.005,\n", + " scan_center=PixelYX(y=17, x=13),\n", + " scan_rotation=1.234,\n", + " camera_length=2.3,\n", + " detector_pixel_pitch=0.0247,\n", + " detector_center=PixelYX(y=11, x=19),\n", + " detector_rotation=2.134,\n", + " semiconv=0.023,\n", + " flip_factor=-1.,\n", + " descan_error=DescanError(offpxi=.345, pxo_pxi=948)\n", + " ), scan_pos=PixelYX(y=13, x=7))\n", + "_mk_adjust_scan_rotation()(mod, 1.).params" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c5cf588-c563-485c-9bce-80ab4f640181", + "metadata": {}, + "outputs": [], + "source": [ + "adjust_scan_rotation(params, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "f53bef38-b856-4d46-bcc9-b986e791cf9e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(a_0, a_1, a_2, a_3, a_4, a_5, a_6, a_7, a_8, a_9)" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = sym.symbols('a_0:10')\n", + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "67f6c2d1-b207-4077-a9ff-a4c74d421101", + "metadata": {}, + "outputs": [], + "source": [ + "a, b = sym.symbols('b a')" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "9cfce567-07dd-4291-931a-efd20361e84c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle b$" + ], + "text/plain": [ + "b" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "3ccbbcd5-566e-4c85-8e28-69ca3253909d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(a, sym.Symbol)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "3ca55891-a6fa-4ce1-ae0b-5837453fd606", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy.abc import c" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "43cdf2c6-b023-4316-be90-f2aed815eb3e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(c, sym.Symbol)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "a68beed6-55f7-4398-9538-b91735534bf6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(sym.Float(1.0), sym.Number)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "397b5ec0-95ee-4620-8312-049cbcff49c4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(sym.Rational(1,2), sym.Number)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "44fe1063-bac5-4a74-b352-2b79455ee0fc", + "metadata": {}, + "outputs": [], + "source": [ + "a = 1" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "a2341a66-d8d0-4496-87fe-e548ba315f51", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1, sym.NumberSymbol)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "3eba1760-1ce8-4ba0-a6dc-685bc91f6989", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle 2^{a}$" + ], + "text/plain": [ + "2**a" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sym.sympify('2^a')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43aecbae-ed90-49f8-b323-5a20d8a75305", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/prototypes/sympy/Sympy_temgym.ipynb b/prototypes/sympy/Sympy_temgym.ipynb new file mode 100644 index 0000000..4b70002 --- /dev/null +++ b/prototypes/sympy/Sympy_temgym.ipynb @@ -0,0 +1,396 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "2761fdc4-4df4-4785-96bd-5c75b34e4c37", + "metadata": {}, + "outputs": [], + "source": [ + "from temgym_core.components import Component, Lens\n", + "from temgym_core.propagator import FreeSpaceParaxial\n", + "from temgym_core.ray import Ray\n", + "\n", + "import sympy as sym" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "041de1c0-a8a5-47b9-9bd0-9d853ae76576", + "metadata": {}, + "outputs": [], + "source": [ + "def test_propagate(ray, dist):\n", + " pr = FreeSpaceParaxial.propagate(ray, dist)\n", + " return pr" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "6c20b320-1642-48ef-8924-6da83dd79f23", + "metadata": {}, + "outputs": [], + "source": [ + "x, y, dx, dy, z, S, one, f = sym.symbols('x y dx dy z S one f')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "80bb1dea-2d59-4783-8e90-3e69c0bffd2a", + "metadata": {}, + "outputs": [], + "source": [ + "ray = Ray(x, y, dx, dy, z, pathlength=S, _one=one)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "c0680275-5c02-4e23-85b7-c897cd66cd77", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Ray(x=1.0*dx + x, y=1.0*dy + y, dx=dx, dy=dy, z=z + 1.0, pathlength=S + 0.5*dx**2 + 0.5*dy**2 + 1.0, _one=one)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_propagate(ray, 1.)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "81eaa360-c86d-42d2-ae60-9a1398a3c158", + "metadata": {}, + "outputs": [], + "source": [ + "z_0, z_1 = sym.symbols('z_0 z_1') # distance" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "f693f612-585b-475b-95ea-32a5f546d8a8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Ray(x=dx*z_0 + x, y=dy*z_0 + y, dx=dx, dy=dy, z=z + z_0, pathlength=S + z_0*(0.5*dx**2 + 0.5*dy**2 + 1), _one=one)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ray_prop = test_propagate(ray, z_0)\n", + "ray_prop" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "0dcf9edc-06c4-4498-bff8-59ee27ba48fd", + "metadata": {}, + "outputs": [], + "source": [ + "def test_lens(z, f, ray):\n", + " res_tmp = Lens(z, f).__call__(ray=ray)\n", + " return res_tmp" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d12b8fb3-131e-49ef-ae6c-ca4e4f596779", + "metadata": {}, + "outputs": [], + "source": [ + "ray_prop_lens = test_lens(z, f, ray_prop)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "2624980d-0be7-4e7f-b90d-c036448e7ff9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Ray(x=dx*z_0 + x, y=dy*z_0 + y, dx=dx + (-dx*z_0 - x)/f, dy=dy + (-dy*z_0 - y)/f, z=z + z_0, pathlength=S + z_0*(0.5*dx**2 + 0.5*dy**2 + 1) - ((dx*z_0 + x)**2 + (dy*z_0 + y)**2)/(2*f), _one=1.0*one)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ray_prop_lens" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "d705cd06-6c1d-4806-8a2f-ee2d071963d0", + "metadata": {}, + "outputs": [], + "source": [ + "# sym.diff(res, x)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "fd590351-0c5b-441c-a65f-b9ab34154557", + "metadata": {}, + "outputs": [], + "source": [ + "# sym.diff(res, y)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "ab53a0f1-6081-4bb2-bcb8-84ad10734234", + "metadata": {}, + "outputs": [], + "source": [ + "ray_prop_lens_prop = test_propagate(ray_prop_lens, z_1)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "06567884-1ac0-452f-be2a-6ba1d5f79987", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Ray(x=dx*z_0 + x + z_1*(dx + (-dx*z_0 - x)/f), y=dy*z_0 + y + z_1*(dy + (-dy*z_0 - y)/f), dx=dx + (-dx*z_0 - x)/f, dy=dy + (-dy*z_0 - y)/f, z=z + z_0 + z_1, pathlength=S + z_0*(0.5*dx**2 + 0.5*dy**2 + 1) + z_1*(0.5*(dx + (-dx*z_0 - x)/f)**2 + 0.5*(dy + (-dy*z_0 - y)/f)**2 + 1) - ((dx*z_0 + x)**2 + (dy*z_0 + y)**2)/(2*f), _one=1.0*one)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ray_prop_lens_prop" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "6e9ca7ea-3488-4ecd-b547-206799b9f51e", + "metadata": {}, + "outputs": [], + "source": [ + "def ray_to_matrix(ray):\n", + " list_ray = []\n", + " for comp in ['x', 'y', 'dx', 'dy', '_one']:\n", + " list_ray.append(ray.__dict__[comp])\n", + " matrix_ray = sym.Matrix(list_ray)\n", + " return matrix_ray" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "642c40a2-7617-4bed-95cd-fcd32ce896ee", + "metadata": {}, + "outputs": [], + "source": [ + "ray_matrix = ray_to_matrix(ray_prop_lens_prop)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "e1f1fe0a-8d6e-4503-8427-89ef5be7530f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}dx z_{0} + x + z_{1} \\left(dx + \\frac{- dx z_{0} - x}{f}\\right)\\\\dy z_{0} + y + z_{1} \\left(dy + \\frac{- dy z_{0} - y}{f}\\right)\\\\dx + \\frac{- dx z_{0} - x}{f}\\\\dy + \\frac{- dy z_{0} - y}{f}\\\\1.0 one\\end{matrix}\\right]$" + ], + "text/plain": [ + "Matrix([\n", + "[dx*z_0 + x + z_1*(dx + (-dx*z_0 - x)/f)],\n", + "[dy*z_0 + y + z_1*(dy + (-dy*z_0 - y)/f)],\n", + "[ dx + (-dx*z_0 - x)/f],\n", + "[ dy + (-dy*z_0 - y)/f],\n", + "[ 1.0*one]])" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ray_matrix" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "363eb233-f114-41fa-8edb-129e588113be", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}1 - \\frac{z_{1}}{f} & 0 & z_{0} + z_{1} \\left(1 - \\frac{z_{0}}{f}\\right) & 0 & 0\\\\0 & 1 - \\frac{z_{1}}{f} & 0 & z_{0} + z_{1} \\left(1 - \\frac{z_{0}}{f}\\right) & 0\\\\- \\frac{1}{f} & 0 & 1 - \\frac{z_{0}}{f} & 0 & 0\\\\0 & - \\frac{1}{f} & 0 & 1 - \\frac{z_{0}}{f} & 0\\\\0 & 0 & 0 & 0 & 1.0\\end{matrix}\\right]$" + ], + "text/plain": [ + "Matrix([\n", + "[1 - z_1/f, 0, z_0 + z_1*(1 - z_0/f), 0, 0],\n", + "[ 0, 1 - z_1/f, 0, z_0 + z_1*(1 - z_0/f), 0],\n", + "[ -1/f, 0, 1 - z_0/f, 0, 0],\n", + "[ 0, -1/f, 0, 1 - z_0/f, 0],\n", + "[ 0, 0, 0, 0, 1.0]])" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ray_matrix.jacobian([x, y, dx, dy, one])" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "5bbf7c9d-8e96-436b-9413-16b27c957f63", + "metadata": {}, + "outputs": [], + "source": [ + "def Lens_nonlin(z: float, focal_length: float, ray: Ray):\n", + " f = focal_length\n", + "\n", + " x, y, dx, dy = ray.x, ray.y, ray.dx, ray.dy\n", + "\n", + " new_dx = -x**3 / f + dx\n", + " new_dy = -y**3 / f + dy\n", + "\n", + " pathlength = ray.pathlength - (x**2 + y**2) / (2 * f)\n", + " one = ray._one * 1.0\n", + "\n", + " return Ray(\n", + " x=x, y=y, dx=new_dx, dy=new_dy, _one=one, pathlength=pathlength, z=ray.z\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "79ee539c-75f1-4897-8abe-f9d318eeea8b", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy.polys.ring_series import rs_series" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "73b9ac58-239b-440c-ace9-819b8e392960", + "metadata": {}, + "outputs": [], + "source": [ + "def test_lens_nonlin(z, f, ray):\n", + " res_tmp = Lens_nonlin(z, f, ray=ray)\n", + " return res_tmp" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "d3725e1b-5e47-429b-b1c0-fde110183be1", + "metadata": {}, + "outputs": [], + "source": [ + "ray_nonlin = test_lens_nonlin(z, f, ray_prop)\n", + "ray_prop_nonlin_prop = test_propagate(ray_nonlin, z_1)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "92331b24-c297-48fb-911e-72d275474a52", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Ray(x=dx*z_0 + x + z_1*(dx - (dx*z_0 + x)**3/f), y=dy*z_0 + y + z_1*(dy - (dy*z_0 + y)**3/f), dx=dx - (dx*z_0 + x)**3/f, dy=dy - (dy*z_0 + y)**3/f, z=z + z_0 + z_1, pathlength=S + z_0*(0.5*dx**2 + 0.5*dy**2 + 1) + z_1*(0.5*(dx - (dx*z_0 + x)**3/f)**2 + 0.5*(dy - (dy*z_0 + y)**3/f)**2 + 1) - ((dx*z_0 + x)**2 + (dy*z_0 + y)**2)/(2*f), _one=1.0*one)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ray_prop_nonlin_prop" + ] + }, + { + "cell_type": "raw", + "id": "42271c87-ba33-4205-80e4-665c9e88523b", + "metadata": {}, + "source": [] + }, + { + "cell_type": "raw", + "id": "7abe24b9-5357-478b-abfe-13bc0c65595d", + "metadata": {}, + "source": [] + }, + { + "cell_type": "raw", + "id": "17b9be9c-de93-48ed-b06f-26252df545fc", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/prototypes/sympy/lambdifytests.ipynb b/prototypes/sympy/lambdifytests.ipynb new file mode 100644 index 0000000..3d30938 --- /dev/null +++ b/prototypes/sympy/lambdifytests.ipynb @@ -0,0 +1,581 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 30, + "id": "2b83f25e-cc8e-435b-b898-bf415fc057a7", + "metadata": {}, + "outputs": [], + "source": [ + "import sympy as sym\n", + "from microscope_calibration.common.model import Parameters4DSTEM, Model4DSTEM, DescanError, PixelYX, symbol_maker, trace, is_sympy" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "96a6de07-c4d4-41c1-a6a2-3adf4b761bff", + "metadata": {}, + "outputs": [], + "source": [ + "params = symbol_maker(Parameters4DSTEM, postfix=None, recurse_for=[DescanError, PixelYX])" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "77c10ecd-c00b-47ed-8f21-47b1213ca57e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Parameters4DSTEM(overfocus=overfocus_0, scan_pixel_pitch=scan_pixel_pitch_1, scan_center=PixelYX(y=y_2, x=x_3), scan_rotation=scan_rotation_4, camera_length=camera_length_5, detector_pixel_pitch=detector_pixel_pitch_6, detector_center=PixelYX(y=y_7, x=x_8), semiconv=semiconv_9, flip_factor=flip_factor_10, descan_error=DescanError(pxo_pxi=pxo_pxi_11, pxo_pyi=pxo_pyi_12, pyo_pxi=pyo_pxi_13, pyo_pyi=pyo_pyi_14, sxo_pxi=sxo_pxi_15, sxo_pyi=sxo_pyi_16, syo_pxi=syo_pxi_17, syo_pyi=syo_pyi_18, offpxi=offpxi_19, offpyi=offpyi_20, offsxi=offsxi_21, offsyi=offsyi_22), detector_rotation=detector_rotation_23)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "params" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "31812774-38ff-427c-a9da-b1d1fa59d6c6", + "metadata": {}, + "outputs": [], + "source": [ + "scan_pos = symbol_maker(PixelYX, postfix=None)\n", + "source_dy, source_dx = sym.symbols('source_{dy} source_{dx}')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c3e26844-faf5-40e8-bc47-579e32a0d1ba", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'MutableDenseMatrix' object has no attribute 'to_matrix'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m res = \u001b[43mtrace\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 2\u001b[39m \u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[43m=\u001b[49m\u001b[43mparams\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 3\u001b[39m \u001b[43m \u001b[49m\u001b[43mscan_pos\u001b[49m\u001b[43m=\u001b[49m\u001b[43mscan_pos\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 4\u001b[39m \u001b[43m \u001b[49m\u001b[43msource_dy\u001b[49m\u001b[43m=\u001b[49m\u001b[43msource_dy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 5\u001b[39m \u001b[43m \u001b[49m\u001b[43msource_dx\u001b[49m\u001b[43m=\u001b[49m\u001b[43msource_dx\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 6\u001b[39m \u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/src/Microscope-Calibration/src/microscope_calibration/common/model.py:1204\u001b[39m, in \u001b[36mtrace\u001b[39m\u001b[34m(params, scan_pos, source_dx, source_dy, specimen, _one)\u001b[39m\n\u001b[32m 1202\u001b[39m model = Model4DSTEM.build(params, scan_pos=scan_pos, specimen=specimen)\n\u001b[32m 1203\u001b[39m ray = model.make_source_ray(source_dy=source_dy, source_dx=source_dx, _one=_one).ray\n\u001b[32m-> \u001b[39m\u001b[32m1204\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmodel\u001b[49m\u001b[43m.\u001b[49m\u001b[43mtrace\u001b[49m\u001b[43m(\u001b[49m\u001b[43mray\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/src/Microscope-Calibration/src/microscope_calibration/common/model.py:911\u001b[39m, in \u001b[36mModel4DSTEM.trace\u001b[39m\u001b[34m(self, ray)\u001b[39m\n\u001b[32m 909\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m TracerBoolConversionError:\n\u001b[32m 910\u001b[39m \u001b[38;5;28;01mpass\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m911\u001b[39m scan_px = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mreal_to_scan\u001b[49m\u001b[43m(\u001b[49m\u001b[43mCoordXY\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m=\u001b[49m\u001b[43mr\u001b[49m\u001b[43m.\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m=\u001b[49m\u001b[43mr\u001b[49m\u001b[43m.\u001b[49m\u001b[43my\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m_one\u001b[49m\u001b[43m=\u001b[49m\u001b[43mray\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_one\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 912\u001b[39m result[\u001b[33m\"\u001b[39m\u001b[33mspecimen\u001b[39m\u001b[33m\"\u001b[39m] = ResultSection(\n\u001b[32m 913\u001b[39m component=comp,\n\u001b[32m 914\u001b[39m ray=r,\n\u001b[32m 915\u001b[39m sampling={\u001b[33m\"\u001b[39m\u001b[33mscan_px\u001b[39m\u001b[33m\"\u001b[39m: scan_px},\n\u001b[32m 916\u001b[39m )\n\u001b[32m 918\u001b[39m \u001b[38;5;66;03m# Skip zero distance propagation between specimen and descanner\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/src/Microscope-Calibration/src/microscope_calibration/common/model.py:706\u001b[39m, in \u001b[36mModel4DSTEM.real_to_scan\u001b[39m\u001b[34m(self, coords, _one)\u001b[39m\n\u001b[32m 705\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mreal_to_scan\u001b[39m(\u001b[38;5;28mself\u001b[39m, coords: CoordXY, _one: \u001b[38;5;28mfloat\u001b[39m = \u001b[32m1.0\u001b[39m) -> PixelYX:\n\u001b[32m--> \u001b[39m\u001b[32m706\u001b[39m (y, x) = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_real_to_scan\u001b[49m\u001b[43m.\u001b[49m\u001b[43mto_matrix\u001b[49m() @ sym.ImmutableMatrix([coords.y, coords.x])\n\u001b[32m 707\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m PixelYX(y=y + \u001b[38;5;28mself\u001b[39m.scan_center.y * _one, x=x + \u001b[38;5;28mself\u001b[39m.scan_center.x * _one)\n", + "\u001b[31mAttributeError\u001b[39m: 'MutableDenseMatrix' object has no attribute 'to_matrix'" + ] + } + ], + "source": [ + "res = trace(\n", + " params=params,\n", + " scan_pos=scan_pos,\n", + " source_dy=source_dy,\n", + " source_dx=source_dx,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "f1d5ce3f-c3d7-4762-99f3-8862b382f585", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "OrderedDict([('source',\n", + " ResultSection(component=PointSource(z=0, semi_conv=semiconv, offset_xy=CoordsXY(x=0.0, y=0.0)), ray=Ray(x=0, y=0, dx=source_{dx}, dy=source_{dy}, z=0.0, pathlength=0.0, _one=1.0), sampling=None)),\n", + " ('overfocus',\n", + " ResultSection(component=Propagator(distance=overfocus, propagator=), ray=Ray(x=overfocus*source_{dx}, y=overfocus*source_{dy}, dx=source_{dx}, dy=source_{dy}, z=overfocus, pathlength=overfocus, _one=1.0), sampling=None)),\n", + " ('scanner',\n", + " ResultSection(component=Scanner(z=overfocus, scan_pos_x=0, scan_pos_y=0, scan_tilt_x=0.0, scan_tilt_y=0.0), ray=Ray(x=overfocus*source_{dx}, y=overfocus*source_{dy}, dx=source_{dx}, dy=source_{dy}, z=overfocus, pathlength=overfocus, _one=1.0), sampling=None)),\n", + " ('specimen',\n", + " ResultSection(component=Plane(z=overfocus), ray=Ray(x=overfocus*source_{dx}, y=overfocus*source_{dy}, dx=source_{dx}, dy=source_{dy}, z=1.0*overfocus, pathlength=overfocus, _one=1.0), sampling={'scan_px': PixelYX(y=-overfocus*source_{dx}*sin(scan_rotation)/scan_pixel_pitch + overfocus*source_{dy}*cos(scan_rotation)/scan_pixel_pitch + 1.0*y, x=overfocus*source_{dx}*cos(scan_rotation)/scan_pixel_pitch + overfocus*source_{dy}*sin(scan_rotation)/scan_pixel_pitch + 1.0*x)})),\n", + " ('descanner',\n", + " ResultSection(component=Descanner(z=overfocus, scan_pos_x=0, scan_pos_y=0, scan_tilt_x=0.0, scan_tilt_y=0.0, descan_error=DescanError(pxo_pxi=pxo_pxi, pxo_pyi=pxo_pyi, pyo_pxi=pyo_pxi, pyo_pyi=pyo_pyi, sxo_pxi=sxo_pxi, sxo_pyi=sxo_pyi, syo_pxi=syo_pxi, syo_pyi=syo_pyi, offpxi=offpxi, offpyi=offpyi, offsxi=offsxi, offsyi=offsyi)), ray=Ray(x=1.0*offpxi + overfocus*source_{dx}, y=1.0*offpyi + overfocus*source_{dy}, dx=1.0*offsxi + source_{dx}, dy=1.0*offsyi + source_{dy}, z=1.0*overfocus, pathlength=overfocus, _one=1.0), sampling=None)),\n", + " ('camera_length',\n", + " ResultSection(component=Propagator(distance=camera_length, propagator=), ray=Ray(x=camera_length*(1.0*offsxi + source_{dx}) + 1.0*offpxi + overfocus*source_{dx}, y=camera_length*(1.0*offsyi + source_{dy}) + 1.0*offpyi + overfocus*source_{dy}, dx=1.0*offsxi + source_{dx}, dy=1.0*offsyi + source_{dy}, z=camera_length + 1.0*overfocus, pathlength=camera_length + overfocus, _one=1.0), sampling=None)),\n", + " ('detector',\n", + " ResultSection(component=Plane(z=camera_length + overfocus), ray=Ray(x=camera_length*(1.0*offsxi + source_{dx}) + 1.0*offpxi + overfocus*source_{dx}, y=camera_length*(1.0*offsyi + source_{dy}) + 1.0*offpyi + overfocus*source_{dy}, dx=1.0*offsxi + source_{dx}, dy=1.0*offsyi + source_{dy}, z=camera_length + 1.0*overfocus, pathlength=camera_length + overfocus, _one=1.0), sampling={'detector_px': PixelYX(y=1.0*y - (camera_length*(1.0*offsxi + source_{dx}) + 1.0*offpxi + overfocus*source_{dx})*sin(detector_rotation)/(detector_pixel_pitch*flip_factor) + (camera_length*(1.0*offsyi + source_{dy}) + 1.0*offpyi + overfocus*source_{dy})*cos(detector_rotation)/(detector_pixel_pitch*flip_factor), x=1.0*x + (camera_length*(1.0*offsxi + source_{dx}) + 1.0*offpxi + overfocus*source_{dx})*cos(detector_rotation)/detector_pixel_pitch + (camera_length*(1.0*offsyi + source_{dy}) + 1.0*offpyi + overfocus*source_{dy})*sin(detector_rotation)/detector_pixel_pitch)}))])" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "82e20794-a5ad-4f80-86b2-36ef94cd4727", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'Zero' object is not subscriptable", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mTypeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[14]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m f = \u001b[43msym\u001b[49m\u001b[43m.\u001b[49m\u001b[43mlambdify\u001b[49m\u001b[43m(\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparams\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mscan_pos\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msource_dy\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msource_dx\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mres\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniforge3/envs/calib313/lib/python3.13/site-packages/sympy/utilities/lambdify.py:796\u001b[39m, in \u001b[36mlambdify\u001b[39m\u001b[34m(args, expr, modules, printer, use_imps, dummify, cse, docstring_limit)\u001b[39m\n\u001b[32m 794\u001b[39m \u001b[38;5;66;03m# First find any function implementations\u001b[39;00m\n\u001b[32m 795\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m use_imps:\n\u001b[32m--> \u001b[39m\u001b[32m796\u001b[39m namespaces.append(\u001b[43m_imp_namespace\u001b[49m\u001b[43m(\u001b[49m\u001b[43mexpr\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[32m 797\u001b[39m \u001b[38;5;66;03m# Check for dict before iterating\u001b[39;00m\n\u001b[32m 798\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(modules, (\u001b[38;5;28mdict\u001b[39m, \u001b[38;5;28mstr\u001b[39m)) \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(modules, \u001b[33m'\u001b[39m\u001b[33m__iter__\u001b[39m\u001b[33m'\u001b[39m):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniforge3/envs/calib313/lib/python3.13/site-packages/sympy/utilities/lambdify.py:1409\u001b[39m, in \u001b[36m_imp_namespace\u001b[39m\u001b[34m(expr, namespace)\u001b[39m\n\u001b[32m 1406\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m key, val \u001b[38;5;129;01min\u001b[39;00m expr.items():\n\u001b[32m 1407\u001b[39m \u001b[38;5;66;03m# functions can be in dictionary keys\u001b[39;00m\n\u001b[32m 1408\u001b[39m _imp_namespace(key, namespace)\n\u001b[32m-> \u001b[39m\u001b[32m1409\u001b[39m \u001b[43m_imp_namespace\u001b[49m\u001b[43m(\u001b[49m\u001b[43mval\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnamespace\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1410\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m namespace\n\u001b[32m 1411\u001b[39m \u001b[38;5;66;03m# SymPy expressions may be Functions themselves\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniforge3/envs/calib313/lib/python3.13/site-packages/sympy/utilities/lambdify.py:1403\u001b[39m, in \u001b[36m_imp_namespace\u001b[39m\u001b[34m(expr, namespace)\u001b[39m\n\u001b[32m 1401\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m is_sequence(expr):\n\u001b[32m 1402\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m arg \u001b[38;5;129;01min\u001b[39;00m expr:\n\u001b[32m-> \u001b[39m\u001b[32m1403\u001b[39m \u001b[43m_imp_namespace\u001b[49m\u001b[43m(\u001b[49m\u001b[43marg\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnamespace\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1404\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m namespace\n\u001b[32m 1405\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(expr, \u001b[38;5;28mdict\u001b[39m):\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniforge3/envs/calib313/lib/python3.13/site-packages/sympy/utilities/lambdify.py:1402\u001b[39m, in \u001b[36m_imp_namespace\u001b[39m\u001b[34m(expr, namespace)\u001b[39m\n\u001b[32m 1400\u001b[39m \u001b[38;5;66;03m# tuples, lists, dicts are valid expressions\u001b[39;00m\n\u001b[32m 1401\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m is_sequence(expr):\n\u001b[32m-> \u001b[39m\u001b[32m1402\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43marg\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mexpr\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 1403\u001b[39m \u001b[43m \u001b[49m\u001b[43m_imp_namespace\u001b[49m\u001b[43m(\u001b[49m\u001b[43marg\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnamespace\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1404\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m namespace\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/src/TemGymCore/src/temgym_core/ray.py:92\u001b[39m, in \u001b[36mRay.__getitem__\u001b[39m\u001b[34m(self, arg)\u001b[39m\n\u001b[32m 79\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__getitem__\u001b[39m(\u001b[38;5;28mself\u001b[39m, arg):\n\u001b[32m 80\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Index a vectorized ray to get a single-element Ray.\u001b[39;00m\n\u001b[32m 81\u001b[39m \n\u001b[32m 82\u001b[39m \u001b[33;03m Parameters\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 90\u001b[39m \u001b[33;03m Ray with fields indexed as requested.\u001b[39;00m\n\u001b[32m 91\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m92\u001b[39m params = {k: \u001b[43mv\u001b[49m\u001b[43m[\u001b[49m\u001b[43marg\u001b[49m\u001b[43m]\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m k, v \u001b[38;5;129;01min\u001b[39;00m dataclasses.asdict(\u001b[38;5;28mself\u001b[39m).items()}\n\u001b[32m 93\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mtype\u001b[39m(\u001b[38;5;28mself\u001b[39m)(**params)\n", + "\u001b[31mTypeError\u001b[39m: 'Zero' object is not subscriptable" + ] + } + ], + "source": [ + "f = sym.lambdify((params, scan_pos, source_dy, source_dx), res)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "49f4cefd-0f8f-4f80-9eae-6d670a0e4c1f", + "metadata": {}, + "outputs": [], + "source": [ + "import jax_dataclasses as jdc" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "18596d92-cdb8-47c3-b4f1-d6688c17b88f", + "metadata": {}, + "outputs": [], + "source": [ + "@jdc.pytree_dataclass\n", + "class Ray:\n", + " x: float\n", + " y: float" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "d8cf0687-2238-4c2f-bc70-854595f8d1a1", + "metadata": {}, + "outputs": [], + "source": [ + "@jdc.pytree_dataclass\n", + "class ZRay(Ray):\n", + " z: float" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "b9dd9ce4-9c0b-4913-ad01-30f052bd803f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ZRay(x=0.0, y=0.0, z=0.0)" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ZRay(x=0., y=0., z=0.)" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "id": "391d2d3f-cd70-4984-802a-c87e7a81a60f", + "metadata": {}, + "outputs": [], + "source": [ + "def mytrace(r: ZRay):\n", + " return ZRay(\n", + " x=r.x + 1,\n", + " y=r.y + 1,\n", + " z=\"abc\",\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "id": "17966259-eb25-4492-adf5-42c537bd450c", + "metadata": {}, + "outputs": [], + "source": [ + "r = ZRay(*sym.symbols('x, y'), 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "id": "6ccf76e5-1fac-407f-aa7e-e718a9e5245d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ZRay(x=x, y=y, z=1)" + ] + }, + "execution_count": 105, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "r" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "id": "087163d7-5068-4d77-a390-83d30fdee177", + "metadata": {}, + "outputs": [], + "source": [ + "res = mytrace(r)" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "id": "0c40b0e1-6b37-4326-8ead-a40a7e25af23", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ZRay(x=x + 1, y=y + 1, z='abc')" + ] + }, + "execution_count": 107, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "id": "9ac54228-ca1d-4b3e-af80-9bee24b13fe0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ZRay(x=2, y=3, z='abc')" + ] + }, + "execution_count": 108, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mytrace(ZRay(1, 2, 3))" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "id": "055a453c-44ce-4cf1-aaad-2aa8bd60370d", + "metadata": {}, + "outputs": [], + "source": [ + "import jax.tree" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "id": "aad3b844-9931-4a31-abe2-3dd6fdb9e472", + "metadata": {}, + "outputs": [], + "source": [ + "from copy import deepcopy\n", + "from typing import TypeVar, Callable" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "id": "c6351ca7-9466-4014-9776-661ba32b7d20", + "metadata": {}, + "outputs": [], + "source": [ + "SymbolJaxTree = TypeVar(\"SymbolJaxTree\")\n", + "\n", + "\n", + "def lambdify(inp: SymbolJaxTree, func: Callable[[SymbolJaxTree], SymbolJaxTree], **kwargs):\n", + " outp = func(inp)\n", + " inp_leaves, inp_treedef = jax.tree.flatten(inp)\n", + " outp_leaves, outp_treedef = jax.tree.flatten(outp)\n", + "\n", + " inp_indices = []\n", + " inp_symbols = []\n", + " inp_dups = {}\n", + "\n", + " for i, leave in enumerate(inp_leaves):\n", + " if isinstance(leave, sym.Symbol):\n", + " if leave in inp_symbols:\n", + " inp_dups[i] = inp_symbols.index(leave)\n", + " else:\n", + " inp_indices.append(i)\n", + " inp_symbols.append(leave)\n", + " elif is_sympy(leave) and not isinstance(leave, (sym.NumberSymbol, sym.Number)):\n", + " raise ValueError(\n", + " f\"SymPy leave {leave} found that is not a symbol or a constant number. \"\n", + " \"Only symbols and constants are allowed in the input definition.\"\n", + " )\n", + "\n", + " outp_indices = []\n", + " outp_exprs = []\n", + "\n", + " for i, leave in enumerate(outp_leaves):\n", + " if isinstance(leave, sym.Basic):\n", + " outp_indices.append(i)\n", + " outp_exprs.append(leave)\n", + "\n", + " inp_indices_set = set(inp_indices)\n", + " inner_f = sym.lambdify(inp_symbols, outp_exprs, **kwargs)\n", + "\n", + " def outer(ii):\n", + " ii_leaves, ii_treedef = jax.tree.flatten_with_path(ii)\n", + " if ii_treedef != inp_treedef:\n", + " raise ValueError(\n", + " f'Tree definition of input {ii_treedef} does not match expected '\n", + " f'tree definition {inp_treedef}.'\n", + " )\n", + " for i, (path, leave) in enumerate(ii_leaves):\n", + " if i not in inp_indices_set:\n", + " if i in inp_dups:\n", + " orig_i = inp_dups[i]\n", + " orig_path, orig_leave = ii_leaves[orig_i]\n", + " if orig_leave != leave:\n", + " raise ValueError(\n", + " f\"Input value {leave} with path {path} was a duplicate symbol in original input \"\n", + " f\"but is now not matching the input value {orig_leave} at {orig_path}\"\n", + " )\n", + " elif leave != inp_leaves[i]:\n", + " raise ValueError(\n", + " f\"Constant value {leave} doesn't match reference input \"\n", + " f\"{inp_leaves[i]} for {path}.\")\n", + "\n", + " ii_vals = [ii_leaves[i][1] for i in inp_indices]\n", + " oo_inner = inner_f(*ii_vals)\n", + " outp = deepcopy(outp_leaves)\n", + " for i, val in enumerate(oo_inner):\n", + " index = outp_indices[i]\n", + " outp[index] = val\n", + " return jax.tree.unflatten(outp_treedef, outp)\n", + "\n", + " return outer" + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "id": "c80f2877-466c-48cd-9d41-56f2a4d3fbcc", + "metadata": {}, + "outputs": [], + "source": [ + "x, y, z = sym.symbols('x y z')" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "id": "6ce36c38-22d1-4c75-a270-5ed2b0b694a1", + "metadata": {}, + "outputs": [], + "source": [ + "f = lambdify(ZRay(x, y, z), mytrace, cse=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "id": "ed65ac81-ee8f-440b-87c3-9a437da61e63", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ZRay(x=5, y=4, z='abc')" + ] + }, + "execution_count": 114, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f(ZRay(x=4, y=3, z=4))" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "id": "29cec86e-c4f4-4687-b417-45347a7a04b7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sympy.core.symbol.Symbol" + ] + }, + "execution_count": 118, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(sym.sympify(\"abc\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "id": "9926ffe7-9a24-401e-8368-b19f9dd58507", + "metadata": {}, + "outputs": [], + "source": [ + "expr = (x + y)**2 + z" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "id": "ffab972a-8816-465a-bf99-928d506cb950", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{2, x, y, z}" + ] + }, + "execution_count": 117, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr.atoms()" + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "id": "4529263c-9935-4452-905a-089d4fc651b0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{x, y, z}" + ] + }, + "execution_count": 150, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr.free_symbols" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "id": "0488df12-9bf4-42d6-b552-c6687be9190f", + "metadata": {}, + "outputs": [], + "source": [ + "m = sym.ImmutableMatrix(\n", + " [\n", + " (x, 0),\n", + " (0, y),\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "id": "eae2f85d-5efb-449c-945e-9a804acf5530", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle x$" + ], + "text/plain": [ + "x" + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m[0, 0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e00784fa-d5f7-4aff-ab4f-84afce290d9e", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.13 (microscope_calibration)", + "language": "python", + "name": "calib313" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/prototypes/sympy/sympy_test.ipynb b/prototypes/sympy/sympy_test.ipynb index 9213a3c..0132d3f 100644 --- a/prototypes/sympy/sympy_test.ipynb +++ b/prototypes/sympy/sympy_test.ipynb @@ -13,15 +13,17 @@ "metadata": {}, "outputs": [], "source": [ - "from microscope_calibration.common.model import Parameters4DSTEM, DescanError, trace, PixelYX, Model4DSTEM, Ray\n", + "from microscope_calibration.common.model import Parameters4DSTEM, DescanError, PixelYX, Model4DSTEM, Ray, trace, symbol_maker\n", "\n", "from temgym_core.run import run_iter\n", + "from temgym_core.components import Component\n", "\n", "import jax\n", "import jax.numpy as jnp\n", "import numpy as np\n", "import sympy as sym\n", - "from sympy import S" + "from sympy import S\n", + "from types import ModuleType" ] }, { @@ -51,43 +53,51 @@ "execution_count": 3, "id": "7f39f79a-98de-4933-82d8-8f1bc3b3359f", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "OrderedDict([('source',\n", - " ResultSection(component=PointSource(z=0, semi_conv=0.001, offset_xy=CoordsXY(x=0.0, y=0.0)), ray=Ray(x=0.0, y=0.0, dx=0.1, dy=0.1, z=0, pathlength=0.0, _one=1.0), sampling=None)),\n", - " ('overfocus',\n", - " ResultSection(component=Propagator(distance=0.01, propagator=), ray=Ray(x=0.001, y=0.001, dx=0.1, dy=0.1, z=0.01, pathlength=0.01, _one=1.0), sampling=None)),\n", - " ('scanner',\n", - " ResultSection(component=Scanner(z=0.01, scan_pos_x=np.float64(-1.989900167499164e-07), scan_pos_y=np.float64(-1.0199496670849987e-07), scan_tilt_x=0.0, scan_tilt_y=0.0), ray=Ray(x=np.float64(0.0009998010099832502), y=np.float64(0.0009998980050332914), dx=0.1, dy=0.1, z=0.01, pathlength=0.01, _one=1.0), sampling=None)),\n", - " ('specimen',\n", - " ResultSection(component=Plane(z=0.01), ray=Ray(x=np.float64(0.0009998010099832502), y=np.float64(0.0009998980050332914), dx=0.1, dy=0.1, z=0.01, pathlength=0.01, _one=1.0), sampling={'scan_px': PixelYX(y=np.float64(989.9501670824985), x=np.float64(1009.949833750832))})),\n", - " ('descanner',\n", - " ResultSection(component=Descanner(z=0.01, scan_pos_x=np.float64(-1.989900167499164e-07), scan_pos_y=np.float64(-1.0199496670849987e-07), scan_tilt_x=0.0, scan_tilt_y=0.0, descan_error=DescanError(pxo_pxi=0.0, pxo_pyi=0.0, pyo_pxi=0.0, pyo_pyi=0.0, sxo_pxi=1, sxo_pyi=0.0, syo_pxi=0.0, syo_pyi=-3, offpxi=0.0, offpyi=0.0, offsxi=0.0, offsyi=0.0)), ray=Ray(x=np.float64(0.001), y=np.float64(0.001), dx=np.float64(0.09999980100998325), dy=np.float64(0.10000030598490013), z=0.01, pathlength=0.01, _one=1.0), sampling=None)),\n", - " ('camera_length',\n", - " ResultSection(component=Propagator(distance=1.0, propagator=), ray=Ray(x=np.float64(0.10099980100998325), y=np.float64(0.10100030598490013), dx=np.float64(0.09999980100998325), dy=np.float64(0.10000030598490013), z=1.01, pathlength=1.01, _one=1.0), sampling=None)),\n", - " ('detector',\n", - " ResultSection(component=Plane(z=1.01), ray=Ray(x=np.float64(0.10099980100998325), y=np.float64(0.10100030598490013), dx=np.float64(0.09999980100998325), dy=np.float64(0.10000030598490013), z=1.01, pathlength=1.01, _one=1.0), sampling={'detector_px': PixelYX(y=np.float64(2020.1061196980027), x=np.float64(2020.096020199665))}))])" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "trace(\n", - " params=p,\n", + "def get_equation_sympy(de: DescanError, scan_px):\n", + " tr = trace(\n", + " params = Parameters4DSTEM(\n", + " overfocus=0.,\n", + " scan_pixel_pitch=scan_px,\n", + " scan_center=PixelYX(0., 0.),\n", + " scan_rotation=0.0,\n", + " camera_length=1.0,\n", + " detector_pixel_pitch=50e-6,\n", + " detector_center=PixelYX(0., 0.),\n", + " semiconv=1e-3, # radian\n", + " flip_factor=1.0,\n", + " descan_error=de,\n", + " xp=np),\n", " scan_pos=PixelYX(0., 0.),\n", - " source_dy=0.1,\n", - " source_dx=0.1,\n", - ")" + " source_dy=0.,\n", + " source_dx=0.,\n", + ")\n", + " \n", + " return tr['detector'].sampling['detector_px']" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "83dbc8ab-5235-4968-8eff-5db7e6e42007", + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": 4, + "id": "4272688c-5198-4e3e-8452-f9e5a49f8231", + "metadata": {}, + "outputs": [], + "source": [ + "#get_equation_sympy(DescanError(pxo_pxi=0.0, pxo_pyi=0.0, pyo_pxi=0.0, pyo_pyi=0.0, sxo_pxi=1, sxo_pyi=0.0, syo_pxi=0.0, syo_pyi=-3, offpxi=0.0, offpyi=0.0, offsxi=0.0, offsyi=0.0), 2.5)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, "id": "5dff3a6f-2ada-40c4-8d76-b090a358729a", "metadata": {}, "outputs": [], @@ -102,27 +112,6 @@ " return tr['detector'].sampling['detector_px'].x" ] }, - { - "cell_type": "code", - "execution_count": 5, - "id": "76760a69-0490-4601-897b-e166aa1c174d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "np.float64(2020.0980000999991)" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "test(0.1)" - ] - }, { "cell_type": "code", "execution_count": 6, @@ -145,52 +134,14 @@ "execution_count": 7, "id": "89fd43dc-cf30-4f06-b6a3-544b5fe340ab", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Array(20200., dtype=float64, weak_type=True)" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "jax.grad(testjax)(0.)" + "#jax.grad(testjax)(0.)" ] }, { "cell_type": "code", "execution_count": 8, - "id": "dfbfb538-b9d7-474e-9c03-839df1e7b21e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/latex": [ - "$\\displaystyle 100000$" - ], - "text/plain": [ - "100000" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x = sym.Symbol('x')\n", - "expr = x*20000\n", - "result = expr.subs(x,5) #x=5\n", - "result" - ] - }, - { - "cell_type": "code", - "execution_count": 9, "id": "1bc65b72-fb56-4bf9-9d66-319fc94d6315", "metadata": {}, "outputs": [ @@ -203,18 +154,19 @@ "20200.0*x + 0.0980000999991667" ] }, - "execution_count": 9, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "x = sym.symbols('x')\n", "test(np.array((x)))" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "id": "47ac09f5-9c37-4ab9-b30e-57ad3e1e9666", "metadata": {}, "outputs": [ @@ -224,7 +176,7 @@ "True" ] }, - "execution_count": 10, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -236,7 +188,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "id": "39baf764-c24b-41a2-85c0-d841fb175ca3", "metadata": {}, "outputs": [ @@ -261,7 +213,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 11, "id": "dd4ee3a6-49ed-412e-8050-0c24e183da7b", "metadata": {}, "outputs": [], @@ -276,7 +228,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "id": "bd97e490-6d23-41bf-ad2d-892d7edaef36", "metadata": {}, "outputs": [], @@ -294,7 +246,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "id": "3a4d14f5-b396-4e8c-8b72-9e67ef2ffc65", "metadata": {}, "outputs": [ @@ -304,7 +256,7 @@ "True" ] }, - "execution_count": 14, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -315,7 +267,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 14, "id": "d889e4b9-029a-46a8-9275-146006a4b422", "metadata": {}, "outputs": [ @@ -328,7 +280,7 @@ "0" ] }, - "execution_count": 15, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -339,7 +291,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 15, "id": "a52a4525-dcee-424a-a5a2-fcb85c495f6e", "metadata": {}, "outputs": [ @@ -352,7 +304,7 @@ "[0]" ] }, - "execution_count": 16, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -363,7 +315,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 16, "id": "cfb6df47-f115-41d4-9903-de9751549d22", "metadata": {}, "outputs": [ @@ -373,7 +325,7 @@ "True" ] }, - "execution_count": 17, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -384,7 +336,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 17, "id": "e5a778d6-e071-4493-8f87-d4b546b5c282", "metadata": {}, "outputs": [ @@ -394,7 +346,7 @@ "True" ] }, - "execution_count": 18, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -405,7 +357,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 18, "id": "6a7ca0a8-2c50-405a-8648-971cdd81ec67", "metadata": {}, "outputs": [], @@ -416,7 +368,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 19, "id": "cf3fad85-37c4-40df-985c-e3a2f9a4a148", "metadata": {}, "outputs": [ @@ -426,7 +378,7 @@ "False" ] }, - "execution_count": 20, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -437,7 +389,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 20, "id": "7e833c32-8fff-46ca-a89b-e1ae42b9f846", "metadata": {}, "outputs": [ @@ -447,7 +399,7 @@ "False" ] }, - "execution_count": 21, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -458,7 +410,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 21, "id": "bdfeaa77-dabf-40f7-b6ca-ce4314da4a54", "metadata": {}, "outputs": [ @@ -468,7 +420,7 @@ "False" ] }, - "execution_count": 22, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -479,7 +431,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 22, "id": "364f7f0f-fc02-435c-89fc-ff077f8f873d", "metadata": {}, "outputs": [ @@ -489,7 +441,7 @@ "True" ] }, - "execution_count": 23, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -502,7 +454,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 23, "id": "1a90abe3-77b0-4088-ad47-dcc990adc5fa", "metadata": {}, "outputs": [], @@ -514,7 +466,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 24, "id": "76f606d6-3e3a-4277-813f-e2fcdafbd901", "metadata": {}, "outputs": [ @@ -527,7 +479,7 @@ "20200.0*x + 0.0980000999991667" ] }, - "execution_count": 25, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -538,7 +490,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 25, "id": "0c728d5c-bd3a-4142-b356-bcdc84f89966", "metadata": {}, "outputs": [ @@ -548,7 +500,7 @@ "False" ] }, - "execution_count": 26, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -559,7 +511,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 26, "id": "64beadd0-a4a9-44d8-81b3-ea207fd59b65", "metadata": {}, "outputs": [ @@ -569,7 +521,7 @@ "True" ] }, - "execution_count": 27, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -580,7 +532,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 27, "id": "4674d42d-4203-4329-8b1d-1d8bbafd1312", "metadata": {}, "outputs": [ @@ -590,7 +542,7 @@ "True" ] }, - "execution_count": 28, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -601,7 +553,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 28, "id": "1b38e839-a39d-494a-b034-511bc45d0570", "metadata": {}, "outputs": [ @@ -611,7 +563,7 @@ "False" ] }, - "execution_count": 29, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -623,7 +575,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 29, "id": "571392b5-8576-48d8-9cc4-354c90a6950a", "metadata": {}, "outputs": [ @@ -633,7 +585,7 @@ "True" ] }, - "execution_count": 30, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -644,7 +596,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 30, "id": "0a8bdf90-6171-4df4-9c5f-6bfa68a4a784", "metadata": {}, "outputs": [ @@ -654,7 +606,7 @@ "False" ] }, - "execution_count": 31, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -665,7 +617,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 31, "id": "bd187886-6c7f-4142-96fe-3a0b1fcfd15b", "metadata": {}, "outputs": [ @@ -678,7 +630,7 @@ "True" ] }, - "execution_count": 32, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -689,7 +641,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 32, "id": "db737e4d-deae-4378-8a91-0819a9a1e820", "metadata": {}, "outputs": [ @@ -699,7 +651,7 @@ "True" ] }, - "execution_count": 33, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -710,7 +662,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 33, "id": "db7214e3-890a-41c1-887f-1bda590261cb", "metadata": {}, "outputs": [], @@ -720,7 +672,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 34, "id": "8a2ef1a4-2564-4fd7-b979-a4965e131e35", "metadata": {}, "outputs": [ @@ -730,7 +682,7 @@ "False" ] }, - "execution_count": 35, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -741,7 +693,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 35, "id": "58e5f9cd-0d80-4487-9232-3c8674ccf090", "metadata": {}, "outputs": [ @@ -751,7 +703,7 @@ "True" ] }, - "execution_count": 36, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -762,7 +714,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 36, "id": "dd8a518d-d57a-4fa7-a977-603275a2d902", "metadata": {}, "outputs": [ @@ -772,7 +724,7 @@ "array([ True, True])" ] }, - "execution_count": 37, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -783,7 +735,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 37, "id": "0cef4050-d40b-4a21-a74d-0cb6c9da1d19", "metadata": {}, "outputs": [], @@ -793,7 +745,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 38, "id": "2c67d9f4-88a3-480a-a75e-9c052ea3b38c", "metadata": {}, "outputs": [], @@ -803,7 +755,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 39, "id": "315f3847-99b1-4ccb-b0ef-6c16ae76f703", "metadata": {}, "outputs": [ @@ -816,7 +768,7 @@ "x" ] }, - "execution_count": 40, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -827,7 +779,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 40, "id": "cc01da33-0b60-4202-bd79-d33fb1d7ea47", "metadata": {}, "outputs": [ @@ -837,7 +789,7 @@ "'Symbol'" ] }, - "execution_count": 41, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -848,7 +800,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 41, "id": "8c933351-af2d-42ff-9240-fe791e32416f", "metadata": {}, "outputs": [ @@ -858,7 +810,7 @@ "False" ] }, - "execution_count": 42, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } @@ -869,7 +821,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 42, "id": "418a96bb-5440-4bf6-ab05-8974db54b606", "metadata": {}, "outputs": [ @@ -879,7 +831,7 @@ "True" ] }, - "execution_count": 43, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -890,7 +842,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 43, "id": "dab06a35-ab3a-4cd1-8244-01f823722cb7", "metadata": {}, "outputs": [ @@ -900,7 +852,7 @@ "True" ] }, - "execution_count": 44, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -911,7 +863,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 44, "id": "e8155091-c316-4ed5-81a4-9ae44401bd76", "metadata": {}, "outputs": [ @@ -921,7 +873,7 @@ "False" ] }, - "execution_count": 45, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -932,7 +884,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 45, "id": "7533e592-4787-4b37-81d2-1da4a635c737", "metadata": {}, "outputs": [ @@ -942,7 +894,7 @@ "True" ] }, - "execution_count": 46, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } @@ -953,7 +905,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 46, "id": "204921ae-e16a-484c-8cd4-701a1499bf1f", "metadata": {}, "outputs": [ @@ -963,7 +915,7 @@ "True" ] }, - "execution_count": 47, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } @@ -975,7 +927,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 47, "id": "cca8ebde-2825-4e5d-81ea-b1f2ded0ed4c", "metadata": {}, "outputs": [ @@ -985,7 +937,7 @@ "True" ] }, - "execution_count": 48, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -996,7 +948,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 48, "id": "94258602-7b25-4148-a131-1f5658cecfe7", "metadata": {}, "outputs": [ @@ -1006,7 +958,7 @@ "True" ] }, - "execution_count": 49, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -1017,7 +969,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 49, "id": "ec3a1a1b-aafc-4a7a-977c-100985f7188f", "metadata": {}, "outputs": [ @@ -1027,7 +979,7 @@ "True" ] }, - "execution_count": 50, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -1038,7 +990,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 50, "id": "ba252c9e-988c-4708-a2fb-caf4aa225d19", "metadata": {}, "outputs": [ @@ -1048,7 +1000,7 @@ "True" ] }, - "execution_count": 51, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -1059,7 +1011,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 51, "id": "35d8e772-90bf-4a12-aa69-68c73f8b14ad", "metadata": {}, "outputs": [ @@ -1069,7 +1021,7 @@ "True" ] }, - "execution_count": 52, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -1080,7 +1032,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 52, "id": "731f18d3-400a-47f6-ac13-f565be6cdecc", "metadata": {}, "outputs": [ @@ -1090,7 +1042,7 @@ "False" ] }, - "execution_count": 53, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -1101,7 +1053,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 53, "id": "f7e680f0-9af8-4a4d-aba6-e276852ec2d6", "metadata": {}, "outputs": [ @@ -1111,7 +1063,7 @@ "True" ] }, - "execution_count": 54, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -1122,7 +1074,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 54, "id": "0866c0a1-ccfe-4dac-baa1-34f7e52a9378", "metadata": {}, "outputs": [ @@ -1135,7 +1087,7 @@ "0" ] }, - "execution_count": 55, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -1146,7 +1098,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 55, "id": "f2218b2c-9e0c-41f4-b725-2c9e71f8e81b", "metadata": {}, "outputs": [ @@ -1156,7 +1108,7 @@ "False" ] }, - "execution_count": 56, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -1167,7 +1119,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 56, "id": "06bd5a23-a0bf-4b8a-84e5-fb98b99eff51", "metadata": {}, "outputs": [ @@ -1177,7 +1129,7 @@ "False" ] }, - "execution_count": 57, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -1188,7 +1140,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 57, "id": "b272694d-ceeb-40d3-923a-fb8b2e2cfe66", "metadata": {}, "outputs": [ @@ -1198,7 +1150,7 @@ "numpy.ndarray" ] }, - "execution_count": 58, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -1209,7 +1161,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 58, "id": "a8f6fefb-c02f-4b9f-822c-76890c65eab5", "metadata": {}, "outputs": [ @@ -1219,7 +1171,7 @@ "True" ] }, - "execution_count": 59, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -1231,7 +1183,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 59, "id": "5ec1083b-41b6-4207-9a89-b547c6853b91", "metadata": {}, "outputs": [], @@ -1243,7 +1195,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 60, "id": "6f46d67f-506d-423b-be35-9287037c6615", "metadata": {}, "outputs": [ @@ -1253,7 +1205,7 @@ "[x, [...]]" ] }, - "execution_count": 61, + "execution_count": 60, "metadata": {}, "output_type": "execute_result" } @@ -1264,7 +1216,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 61, "id": "a62edba7-e202-4185-b340-6ee758039ee4", "metadata": {}, "outputs": [], @@ -1274,7 +1226,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 62, "id": "37ba146b-22ab-4f67-a297-f5692d16d1e3", "metadata": {}, "outputs": [], @@ -1291,7 +1243,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 63, "id": "99d8f336-0b29-4b15-873a-a37eccfdf101", "metadata": {}, "outputs": [ @@ -1301,7 +1253,7 @@ "True" ] }, - "execution_count": 64, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } @@ -1312,7 +1264,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 64, "id": "da365085-8cc4-4789-a304-05a28f0a9b60", "metadata": {}, "outputs": [ @@ -1322,7 +1274,7 @@ "False" ] }, - "execution_count": 65, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" } @@ -1333,7 +1285,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 65, "id": "1c5702f7-1781-4e1a-9124-e52f26a740b2", "metadata": {}, "outputs": [ @@ -1343,7 +1295,7 @@ "True" ] }, - "execution_count": 66, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } @@ -1354,7 +1306,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 66, "id": "d2ee6777-80a7-4e36-bbc5-5786a65af0f5", "metadata": {}, "outputs": [ @@ -1364,7 +1316,7 @@ "True" ] }, - "execution_count": 67, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } @@ -1376,7 +1328,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 67, "id": "11536c2e-9e14-4c88-a87e-e2dbf24c5565", "metadata": {}, "outputs": [], @@ -1386,7 +1338,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 68, "id": "328e4795-13b3-4320-bb09-77ca09b82998", "metadata": {}, "outputs": [ @@ -1396,7 +1348,7 @@ "False" ] }, - "execution_count": 69, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } @@ -1407,7 +1359,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 69, "id": "86de4c5c-f8e2-41c0-8c85-cb09fa5cfaa8", "metadata": {}, "outputs": [], @@ -1418,7 +1370,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 70, "id": "77244efe-3cb8-4bc6-9f4c-05ce24cfee8a", "metadata": {}, "outputs": [ @@ -1428,7 +1380,7 @@ "True" ] }, - "execution_count": 71, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -1439,7 +1391,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 71, "id": "de87e9dc-8022-4ed1-b891-db7365b5f153", "metadata": {}, "outputs": [ @@ -1449,7 +1401,7 @@ "[1, 2]" ] }, - "execution_count": 72, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } @@ -1460,7 +1412,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 72, "id": "0417d0a3-88e9-431b-ac77-8f9978629502", "metadata": {}, "outputs": [ @@ -1470,7 +1422,7 @@ "False" ] }, - "execution_count": 73, + "execution_count": 72, "metadata": {}, "output_type": "execute_result" } @@ -1481,7 +1433,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 73, "id": "e110a060-407f-423d-ba7d-3fb8a3372a17", "metadata": {}, "outputs": [ @@ -1491,7 +1443,7 @@ "True" ] }, - "execution_count": 74, + "execution_count": 73, "metadata": {}, "output_type": "execute_result" } @@ -1502,7 +1454,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 74, "id": "ad7415fa-2794-44d6-8ca5-e607cae78afd", "metadata": {}, "outputs": [ @@ -1512,7 +1464,7 @@ "True" ] }, - "execution_count": 75, + "execution_count": 74, "metadata": {}, "output_type": "execute_result" } @@ -1527,7 +1479,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 75, "id": "6583f3b5-3450-4f25-bb7f-e8f2f2b666eb", "metadata": {}, "outputs": [ @@ -1537,7 +1489,7 @@ "True" ] }, - "execution_count": 76, + "execution_count": 75, "metadata": {}, "output_type": "execute_result" } @@ -1548,7 +1500,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 76, "id": "d797cfaa-441e-4a4a-8e66-5e4c95b2d0d1", "metadata": {}, "outputs": [], @@ -1566,7 +1518,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 77, "id": "33c76e4c-aa2f-4e1c-ab34-8f3ae42a3163", "metadata": {}, "outputs": [ @@ -1576,7 +1528,7 @@ "True" ] }, - "execution_count": 78, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } @@ -1587,7 +1539,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 78, "id": "fdcda7b3-cc29-4c6a-831a-152ff224c780", "metadata": {}, "outputs": [], @@ -1597,7 +1549,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 79, "id": "2c4e714e-7b2e-4938-a350-d6ae8b1841e8", "metadata": {}, "outputs": [], @@ -1607,7 +1559,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 80, "id": "1fa38845-fcd8-41ac-9e20-6c66021dfc56", "metadata": {}, "outputs": [ @@ -1617,7 +1569,7 @@ "True" ] }, - "execution_count": 81, + "execution_count": 80, "metadata": {}, "output_type": "execute_result" } @@ -1628,7 +1580,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 81, "id": "301cc4e0-ff8d-41df-bf80-2a21db5de48a", "metadata": {}, "outputs": [ @@ -1638,7 +1590,7 @@ "True" ] }, - "execution_count": 82, + "execution_count": 81, "metadata": {}, "output_type": "execute_result" } @@ -1649,7 +1601,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 82, "id": "3cb468b4-8da0-45c0-a86f-c5df9317cf4c", "metadata": {}, "outputs": [ @@ -1659,7 +1611,7 @@ "True" ] }, - "execution_count": 83, + "execution_count": 82, "metadata": {}, "output_type": "execute_result" } @@ -1670,7 +1622,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 83, "id": "d22d404d-9de6-4352-b29e-2d12aa71e915", "metadata": {}, "outputs": [ @@ -1683,7 +1635,7 @@ "Expr(0.0)" ] }, - "execution_count": 84, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } @@ -1694,7 +1646,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 84, "id": "e1080661-7f39-4551-8e32-d660db082292", "metadata": {}, "outputs": [], @@ -1704,7 +1656,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 85, "id": "b66cef0a-29cb-49b1-bf9e-7c2a424af591", "metadata": {}, "outputs": [], @@ -1715,7 +1667,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 86, "id": "70163f70-96fc-4914-ae99-7f8b6567bb2b", "metadata": {}, "outputs": [ @@ -1725,7 +1677,7 @@ "True" ] }, - "execution_count": 87, + "execution_count": 86, "metadata": {}, "output_type": "execute_result" } @@ -1736,17 +1688,17 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 87, "id": "7a74a708-74cc-4c08-8223-920de56aedd2", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 88, + "execution_count": 87, "metadata": {}, "output_type": "execute_result" } @@ -1757,7 +1709,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 88, "id": "1575fd61-bc85-47cc-a598-931efb55a324", "metadata": {}, "outputs": [ @@ -1767,7 +1719,7 @@ "jax.Array" ] }, - "execution_count": 89, + "execution_count": 88, "metadata": {}, "output_type": "execute_result" } @@ -1778,7 +1730,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 89, "id": "8a1ab39f-5c0b-4ffb-b389-4e95cfdd1dc6", "metadata": {}, "outputs": [], @@ -1789,7 +1741,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 90, "id": "7ae743f1-0df0-46d2-a9d8-c48d7ae86049", "metadata": {}, "outputs": [ @@ -1799,7 +1751,7 @@ "'it works'" ] }, - "execution_count": 91, + "execution_count": 90, "metadata": {}, "output_type": "execute_result" } @@ -1811,17 +1763,17 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 91, "id": "ceb061b8-354e-43bb-8a85-85323c89beea", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 92, + "execution_count": 91, "metadata": {}, "output_type": "execute_result" } @@ -1832,7 +1784,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 92, "id": "299fc770-ea06-4baa-9c21-bb560292e2a6", "metadata": {}, "outputs": [ @@ -1842,7 +1794,7 @@ "Array([1, 2, 3], dtype=int64)" ] }, - "execution_count": 93, + "execution_count": 92, "metadata": {}, "output_type": "execute_result" } @@ -1853,7 +1805,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 93, "id": "7e12551f-bb4a-4ced-a656-ec73df9ce295", "metadata": {}, "outputs": [ @@ -1863,7 +1815,7 @@ "False" ] }, - "execution_count": 94, + "execution_count": 93, "metadata": {}, "output_type": "execute_result" } @@ -1874,7 +1826,7 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 94, "id": "c9056ff0-d1a3-4b47-9869-a1d940c9bc4d", "metadata": {}, "outputs": [ @@ -1884,7 +1836,7 @@ "typing.Union[int, float, NoneType]" ] }, - "execution_count": 95, + "execution_count": 94, "metadata": {}, "output_type": "execute_result" } @@ -1897,7 +1849,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 95, "id": "64e36ef6-ed69-4374-8b1b-13c6ca2d8a5f", "metadata": {}, "outputs": [ @@ -1907,7 +1859,7 @@ "int | float | None" ] }, - "execution_count": 96, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" } @@ -1918,7 +1870,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 96, "id": "3062f32e-9c01-4b84-ba5f-6b60383ec44b", "metadata": {}, "outputs": [], @@ -1935,13 +1887,12 @@ " semiconv=1e-3*j, # radian\n", " flip_factor=1.0*k,\n", " descan_error=DescanError(pxo_pxi=l, pxo_pyi=m, pyo_pxi=n, pyo_pyi=o, sxo_pxi=p, sxo_pyi=q, syo_pxi=r, syo_pyi=s, offpxi=t, offpyi=u, offsxi=v, offsyi=w),\n", - " xp=np\n", ")" ] }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 97, "id": "a2dd6f2b-75be-42c3-ab5d-6b6ea8975e46", "metadata": {}, "outputs": [ @@ -1951,7 +1902,7 @@ "DescanError(pxo_pxi=l, pxo_pyi=m, pyo_pxi=n, pyo_pyi=o, sxo_pxi=p, sxo_pyi=q, syo_pxi=r, syo_pyi=s, offpxi=t, offpyi=u, offsxi=v, offsyi=w)" ] }, - "execution_count": 98, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -1962,7 +1913,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 98, "id": "298df249-f96e-43ba-8688-1d318700e8dc", "metadata": {}, "outputs": [], @@ -1977,7 +1928,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 99, "id": "b2abc9d9-4461-484c-9a1c-6418204ca6b9", "metadata": {}, "outputs": [ @@ -1987,7 +1938,7 @@ "DescanError(pxo_pxi=4.0e-7*b*l, pxo_pyi=4.0e-7*b*m, pyo_pxi=4.0e-7*b*n, pyo_pyi=4.0e-7*b*o, sxo_pxi=4.0e-7*b*p, sxo_pyi=4.0e-7*b*q, syo_pxi=4.0e-7*b*r, syo_pyi=4.0e-7*b*s, offpxi=t, offpyi=u, offsxi=v, offsyi=w)" ] }, - "execution_count": 100, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -1998,7 +1949,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 100, "id": "934fbf7a-cf95-419b-b478-85f98a30d241", "metadata": {}, "outputs": [ @@ -2011,7 +1962,7 @@ "l" ] }, - "execution_count": 101, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -2022,7 +1973,7 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 101, "id": "014b9948-4c25-474e-9e94-e1247cdebaff", "metadata": {}, "outputs": [ @@ -2052,7 +2003,7 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 102, "id": "b9f46b31-db92-4c34-9e40-29d904da6e9a", "metadata": {}, "outputs": [ @@ -2065,7 +2016,7 @@ "4.0e-7*b" ] }, - "execution_count": 103, + "execution_count": 102, "metadata": {}, "output_type": "execute_result" } @@ -2076,7 +2027,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 103, "id": "7770be6d-c398-47c9-83bd-11b275e80e09", "metadata": {}, "outputs": [ @@ -2086,7 +2037,7 @@ "[{add: 0.0, div: 2.50000000000000}]" ] }, - "execution_count": 104, + "execution_count": 103, "metadata": {}, "output_type": "execute_result" } @@ -2098,7 +2049,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 104, "id": "6bf283f0-397a-4f92-8ea6-63864dcd05f6", "metadata": {}, "outputs": [], @@ -2110,7 +2061,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 105, "id": "c5bc33e3-0535-47bf-8881-388ee35b65eb", "metadata": {}, "outputs": [ @@ -2120,7 +2071,7 @@ "[4.0e-7*b*l]" ] }, - "execution_count": 106, + "execution_count": 105, "metadata": {}, "output_type": "execute_result" } @@ -2131,7 +2082,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 106, "id": "11fd241d-3330-4e62-bf4c-e8db3131aaa2", "metadata": {}, "outputs": [], @@ -2146,7 +2097,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 107, "id": "329d91a9-6f02-441f-9351-b086f3e76557", "metadata": {}, "outputs": [ @@ -2167,7 +2118,7 @@ " [4.0e-7*b*w]]" ] }, - "execution_count": 108, + "execution_count": 107, "metadata": {}, "output_type": "execute_result" } @@ -2178,7 +2129,7 @@ }, { "cell_type": "code", - "execution_count": 109, + "execution_count": 108, "id": "d177530c-ac35-4f4c-81e7-6d1e278d68b6", "metadata": {}, "outputs": [ @@ -2191,7 +2142,7 @@ "l" ] }, - "execution_count": 109, + "execution_count": 108, "metadata": {}, "output_type": "execute_result" } @@ -2202,7 +2153,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 109, "id": "cf731508-762b-4271-8285-7aa616526994", "metadata": {}, "outputs": [ @@ -2212,7 +2163,7 @@ "dict_keys(['pxo_pxi', 'pxo_pyi', 'pyo_pxi', 'pyo_pyi', 'sxo_pxi', 'sxo_pyi', 'syo_pxi', 'syo_pyi', 'offpxi', 'offpyi', 'offsxi', 'offsyi'])" ] }, - "execution_count": 110, + "execution_count": 109, "metadata": {}, "output_type": "execute_result" } @@ -2223,39 +2174,26 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 110, "id": "c307121a-d448-4e19-ac20-73a11670c54f", "metadata": {}, - "outputs": [ - { - "ename": "IndentationError", - "evalue": "unindent does not match any outer indentation level (, line 6)", - "output_type": "error", - "traceback": [ - " \u001b[36mFile \u001b[39m\u001b[32m:6\u001b[39m\n\u001b[31m \u001b[39m\u001b[31mattr_updated = sym.symbols('attr_updated')\u001b[39m\n ^\n\u001b[31mIndentationError\u001b[39m\u001b[31m:\u001b[39m unindent does not match any outer indentation level\n" - ] - } - ], + "outputs": [], "source": [ - "def create_scan_pixel_pitch_fn(model_cls=Model4DSTEM):\n", - " \n", + "#def create_scan_pixel_pitch_fn(model_cls=Model4DSTEM):\n", " # just for documenting the signature\n", " #def update_scan_pixel_pitch(model: Model4DSTEM, scan_pixel_pitch) -> Model4DSTEM:\n", - " ...\n", - " attr_updated = sym.symbols('attr_updated')\n", - " def attr_update(attr, scan_pixel_pitch_old, scan_pixel_pitch_new):\n", - " return sym.solve(attr*scan_pixel_pitch_old/scan_pixel_pitch_new - attr_updated, attr_updated)\n", - " \n", - " def update_scan_pixel_pitch(model: Model4DSTEM, scan_pixel_pitch) -> Model4DSTEM:\n", - " \n", - " return Model4DSTEM(res...)\n", - " \n", - " return update_scan_pixel_pitch" + " # ...\n", + " #attr_updated = sym.symbols('attr_updated')\n", + " #def attr_update(attr, scan_pixel_pitch_old, scan_pixel_pitch_new):\n", + " # return sym.solve(attr*scan_pixel_pitch_old/scan_pixel_pitch_new - attr_updated, attr_updated)\n", + " #def update_scan_pixel_pitch(model: Model4DSTEM, scan_pixel_pitch) -> Model4DSTEM:\n", + " # return Model4DSTEM(res...)\n", + " #return update_scan_pixel_pitch" ] }, { "cell_type": "code", - "execution_count": 112, + "execution_count": 111, "id": "ab1fd32e-6c30-41c8-b5cf-65d201c66f2f", "metadata": {}, "outputs": [], @@ -2267,7 +2205,7 @@ }, { "cell_type": "code", - "execution_count": 113, + "execution_count": 112, "id": "8a200007-05d5-4b38-b3eb-7fa9c182a88a", "metadata": {}, "outputs": [ @@ -2277,7 +2215,7 @@ "[4.0e-7*b*l]" ] }, - "execution_count": 113, + "execution_count": 112, "metadata": {}, "output_type": "execute_result" } @@ -2288,7 +2226,7 @@ }, { "cell_type": "code", - "execution_count": 114, + "execution_count": 113, "id": "085f3be8-ab71-4950-9c06-88c2afd110a3", "metadata": {}, "outputs": [], @@ -2313,17 +2251,17 @@ }, { "cell_type": "code", - "execution_count": 115, + "execution_count": 114, "id": "f25f006f-4170-4d41-b9c8-18a98edc5ecd", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Ray(x=0.13187779553891976, y=0.6273852329775413, dx=0.8655456944780159, dy=0.07377967321790213, z=0.1990317215179357, pathlength=0.5800433275390626, _one=1.0)" + "Ray(x=0.43555645101056906, y=0.3077360496175696, dx=0.6626234433877776, dy=0.2728922500678306, z=0.8102081352228446, pathlength=0.9551051767587974, _one=1.0)" ] }, - "execution_count": 115, + "execution_count": 114, "metadata": {}, "output_type": "execute_result" } @@ -2337,7 +2275,7 @@ }, { "cell_type": "code", - "execution_count": 116, + "execution_count": 115, "id": "a232bb14-0eb4-4320-9a12-31c4672c2d1d", "metadata": {}, "outputs": [ @@ -2347,7 +2285,7 @@ "('x', 'y', 'dx', 'dy', 'z', 'pathlength')" ] }, - "execution_count": 116, + "execution_count": 115, "metadata": {}, "output_type": "execute_result" } @@ -2358,7 +2296,7 @@ }, { "cell_type": "code", - "execution_count": 117, + "execution_count": 116, "id": "3a8bbcd4-a5c1-4adc-990b-df465f1f5a0c", "metadata": {}, "outputs": [ @@ -2368,7 +2306,7 @@ "(True, True)" ] }, - "execution_count": 117, + "execution_count": 116, "metadata": {}, "output_type": "execute_result" } @@ -2382,23 +2320,23 @@ }, { "cell_type": "code", - "execution_count": 118, + "execution_count": 117, "id": "ee410ce4-4f16-43e9-988a-068e5aa29bc6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'x': 0.13187779553891976,\n", - " 'y': 0.6273852329775413,\n", - " 'dx': 0.8655456944780159,\n", - " 'dy': 0.07377967321790213,\n", - " 'z': 0.1990317215179357,\n", - " 'pathlength': 0.5800433275390626,\n", + "{'x': 0.43555645101056906,\n", + " 'y': 0.3077360496175696,\n", + " 'dx': 0.6626234433877776,\n", + " 'dy': 0.2728922500678306,\n", + " 'z': 0.8102081352228446,\n", + " 'pathlength': 0.9551051767587974,\n", " '_one': 1.0}" ] }, - "execution_count": 118, + "execution_count": 117, "metadata": {}, "output_type": "execute_result" } @@ -2409,7 +2347,7 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": 118, "id": "93c02ff9-8cfc-429c-b37e-607dd9f0e931", "metadata": {}, "outputs": [], @@ -2436,7 +2374,7 @@ }, { "cell_type": "code", - "execution_count": 120, + "execution_count": 119, "id": "bba0f37f-cda3-4e93-a1dc-1a3850d43af6", "metadata": {}, "outputs": [], @@ -2454,7 +2392,7 @@ }, { "cell_type": "code", - "execution_count": 121, + "execution_count": 120, "id": "563c4df6-a669-4674-a310-ccdd71618a3e", "metadata": {}, "outputs": [ @@ -2464,7 +2402,7 @@ "True" ] }, - "execution_count": 121, + "execution_count": 120, "metadata": {}, "output_type": "execute_result" } @@ -2475,17 +2413,17 @@ }, { "cell_type": "code", - "execution_count": 122, + "execution_count": 121, "id": "19bd2a9a-fa52-4118-9d1e-c33387f02d02", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Ray(x=0.13187779553891976, y=0.6273852329775413, dx=0.8655456944780159, dy=0.07377967321790213, z=0.1990317215179357, pathlength=0.5800433275390626, _one=1.0)" + "Ray(x=0.43555645101056906, y=0.3077360496175696, dx=0.6626234433877776, dy=0.2728922500678306, z=0.8102081352228446, pathlength=0.9551051767587974, _one=1.0)" ] }, - "execution_count": 122, + "execution_count": 121, "metadata": {}, "output_type": "execute_result" } @@ -2496,43 +2434,36 @@ }, { "cell_type": "code", - "execution_count": 123, + "execution_count": null, "id": "38af8989-083d-48b6-b275-95084146f9dc", "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'attr' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[123]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m left = ray.derive(**{\u001b[43mattr\u001b[49m: x})\n\u001b[32m 2\u001b[39m right = ray.derive(**{attr: \u001b[32m2\u001b[39m*x-x})\n\u001b[32m 3\u001b[39m res = equals(left, right)\n", - "\u001b[31mNameError\u001b[39m: name 'attr' is not defined" - ] - } - ], - "source": [ - "left = ray.derive(**{attr: x})\n", - "right = ray.derive(**{attr: 2*x-x})\n", - "res = equals(left, right)\n", - "res2 = equals(right, left)\n", - "res is True" - ] + "outputs": [], + "source": [] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 122, "id": "6c788642-94ec-4864-a9c9-2abbb048d1bf", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "sympy_equals(x, 0)" ] }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 123, "id": "de6204ec-30f5-4990-931a-f0a8f842efeb", "metadata": {}, "outputs": [], @@ -2542,7 +2473,7 @@ }, { "cell_type": "code", - "execution_count": 125, + "execution_count": 124, "id": "8e534d47-d22c-411f-a007-7f6115eee919", "metadata": {}, "outputs": [ @@ -2552,7 +2483,7 @@ "True" ] }, - "execution_count": 125, + "execution_count": 124, "metadata": {}, "output_type": "execute_result" } @@ -2563,7 +2494,7 @@ }, { "cell_type": "code", - "execution_count": 126, + "execution_count": 125, "id": "68bce69a-119b-4f8e-a4e5-3befc11d6a59", "metadata": {}, "outputs": [ @@ -2576,7 +2507,7 @@ "x + 1" ] }, - "execution_count": 126, + "execution_count": 125, "metadata": {}, "output_type": "execute_result" } @@ -2590,7 +2521,7 @@ }, { "cell_type": "code", - "execution_count": 127, + "execution_count": 126, "id": "09ae5334-131a-427f-9647-6ecdee0d6590", "metadata": {}, "outputs": [ @@ -2600,7 +2531,7 @@ "False" ] }, - "execution_count": 127, + "execution_count": 126, "metadata": {}, "output_type": "execute_result" } @@ -2611,7 +2542,7 @@ }, { "cell_type": "code", - "execution_count": 128, + "execution_count": 127, "id": "7d226309-cf8e-4280-a439-ce1aa87fd7fd", "metadata": {}, "outputs": [ @@ -2621,7 +2552,7 @@ "False" ] }, - "execution_count": 128, + "execution_count": 127, "metadata": {}, "output_type": "execute_result" } @@ -2632,7 +2563,7 @@ }, { "cell_type": "code", - "execution_count": 129, + "execution_count": 128, "id": "ad5a5671-bd13-47e1-9b0b-2a6957277938", "metadata": {}, "outputs": [], @@ -2642,7 +2573,7 @@ }, { "cell_type": "code", - "execution_count": 130, + "execution_count": 129, "id": "846916f5-3ce0-4533-8c11-d01b75f88705", "metadata": {}, "outputs": [ @@ -2652,7 +2583,7 @@ "False" ] }, - "execution_count": 130, + "execution_count": 129, "metadata": {}, "output_type": "execute_result" } @@ -2663,7 +2594,7 @@ }, { "cell_type": "code", - "execution_count": 131, + "execution_count": 130, "id": "6654c238-c720-47b3-aada-5231eff5e8f4", "metadata": {}, "outputs": [], @@ -2676,7 +2607,7 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": 131, "id": "81c568c4-1344-4cef-abf3-f6551312c6de", "metadata": {}, "outputs": [ @@ -2686,7 +2617,7 @@ "(2, 3)" ] }, - "execution_count": 132, + "execution_count": 131, "metadata": {}, "output_type": "execute_result" } @@ -2697,7 +2628,7 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 132, "id": "112ed769-c125-4169-9823-46bfcb75da3f", "metadata": {}, "outputs": [], @@ -2711,7 +2642,7 @@ }, { "cell_type": "code", - "execution_count": 134, + "execution_count": 133, "id": "ef1f5acf-0c74-4fa5-9894-d444049852c2", "metadata": {}, "outputs": [ @@ -2721,7 +2652,7 @@ "[2/3]" ] }, - "execution_count": 134, + "execution_count": 133, "metadata": {}, "output_type": "execute_result" } @@ -2732,7 +2663,7 @@ }, { "cell_type": "code", - "execution_count": 135, + "execution_count": 134, "id": "da9ca0c4-65c1-49d8-9944-018690820ecd", "metadata": {}, "outputs": [], @@ -2747,7 +2678,7 @@ }, { "cell_type": "code", - "execution_count": 136, + "execution_count": 135, "id": "afd959d6-b3c3-47b0-905d-5d0a8d1b0c85", "metadata": {}, "outputs": [ @@ -2757,7 +2688,7 @@ "True" ] }, - "execution_count": 136, + "execution_count": 135, "metadata": {}, "output_type": "execute_result" } @@ -2768,7 +2699,7 @@ }, { "cell_type": "code", - "execution_count": 137, + "execution_count": 136, "id": "764e9e09-eda4-47bf-bb1e-022c7175f316", "metadata": {}, "outputs": [], @@ -2791,8 +2722,8 @@ }, { "cell_type": "code", - "execution_count": 141, - "id": "2651a515", + "execution_count": 137, + "id": "6e76f707", "metadata": {}, "outputs": [], "source": [ @@ -2845,192 +2776,2998 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": 138, + "id": "7fe588ce-010c-498b-9ec2-840092c9eb67", + "metadata": {}, + "outputs": [], + "source": [ + "#create_scan_pixel_pitch_fn()(mod,2)" + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "id": "8d19aa7d-463c-434f-8285-c5e473cc794d", + "metadata": {}, + "outputs": [], + "source": [ + "def create_scan_pixel_pitch_fn(model_cls=Model4DSTEM):\n", + " \n", + " def update_scan_pixel_pitch(model: Model4DSTEM, scan_pixel_pitch) -> Model4DSTEM:\n", + " return Model4DSTEM.build(params=parameters, scan_pos=model.scan_pos)\n", + " return update_scan_pixel_pitch\n", + "\n", + "\n", + "def adjust_scan_pixel_pitch(params, scan_pixel_pitch: float) -> \"Parameters4DSTEM\":\n", + " \"\"\"\n", + " Adjust the scan pixel pitch while keeping the effective descan error\n", + " compensation the same.\n", + "\n", + " This allows first compensating descan error and then adjusting other parameters.\n", + " \"\"\"\n", + " self = params\n", + " de = self.descan_error\n", + " ratio = self.scan_pixel_pitch / scan_pixel_pitch\n", + "\n", + " new_de = DescanError(\n", + " pxo_pyi=de.pxo_pyi * ratio,\n", + " pyo_pyi=de.pyo_pyi * ratio,\n", + " pxo_pxi=de.pxo_pxi * ratio,\n", + " pyo_pxi=de.pyo_pxi * ratio,\n", + " sxo_pyi=de.sxo_pyi * ratio,\n", + " syo_pyi=de.syo_pyi * ratio,\n", + " sxo_pxi=de.sxo_pxi * ratio,\n", + " syo_pxi=de.syo_pxi * ratio,\n", + " offpxi=de.offpxi,\n", + " offpyi=de.offpyi,\n", + " offsxi=de.offsxi,\n", + " offsyi=de.offsyi,\n", + " )\n", + " return self.derive(\n", + " scan_pixel_pitch=scan_pixel_pitch,\n", + " descan_error=new_de,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "id": "74359509-7603-44f2-bbb1-984e7a4fcfff", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy.abc import a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z\n", + "s1, s2, v1, v2 = sym.symbols('s1 s2 v1 v2')\n", + "a, n = sym.symbols(r'overfocus_{scan_1} \\hat{n}')\n", + "def get_equation_sympy(de: DescanError, scan_px):\n", + " par = Parameters4DSTEM(\n", + " overfocus=a,\n", + " scan_pixel_pitch=scan_px,\n", + " scan_center=PixelYX(b, c),\n", + " scan_rotation=d,\n", + " camera_length=e,\n", + " detector_pixel_pitch=f,\n", + " detector_center=PixelYX(g, h),\n", + " semiconv=i, # radian\n", + " flip_factor=j,\n", + " descan_error=de,\n", + " xp=np)\n", + " \n", + " tr1 = trace(\n", + " params = par,\n", + " scan_pos=PixelYX(k, l),\n", + " source_dy=m,\n", + " source_dx=n,\n", + " )\n", + " p_adjusted = adjust_scan_pixel_pitch(par, s2)\n", + "\n", + " tr2 = trace(\n", + " params = p_adjusted,\n", + " scan_pos=PixelYX(k, l),\n", + " source_dy=m,\n", + " source_dx=n,\n", + " )\n", + " \n", + "\n", + " equation_y = sym.Eq(tr1['scanner'].component.scan_pos_y / tr2['scanner'].component.scan_pos_y, tr1['detector'].sampling['detector_px'].y / v1) # v1 = tr2['detector'].sampling['detector_px'].y\n", + " equation_x = sym.Eq(tr1['scanner'].component.scan_pos_x / tr2['scanner'].component.scan_pos_x, tr1['detector'].sampling['detector_px'].x / v2) # v2 = tr2['detector'].sampling['detector_px'].x\n", + " solution = sym.solve([equation_y, equation_x], x, y)\n", + " \n", + " #return res.descan_error == tr['descanner'].component.descan_error, res.detector_pixel_pitch == tr['detector'].sampling['detector_px']\n", + " return solution" + ] + }, + { + "cell_type": "code", + "execution_count": 141, "id": "4f334f23-d3d5-48ea-8adc-9728ebb17b8e", "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle \\left\\{ x : - \\hat{n} e - \\hat{n} overfocus_{scan_1} - b e s_{1} t \\sin{\\left(d \\right)} + b e s_{1} u \\cos{\\left(d \\right)} - b p s_{1} \\sin{\\left(d \\right)} + b q s_{1} \\cos{\\left(d \\right)} + c e s_{1} t \\cos{\\left(d \\right)} + c e s_{1} u \\sin{\\left(d \\right)} + c p s_{1} \\cos{\\left(d \\right)} + c q s_{1} \\sin{\\left(d \\right)} + e k s_{1} t \\sin{\\left(d \\right)} - e k s_{1} u \\cos{\\left(d \\right)} - e l s_{1} t \\cos{\\left(d \\right)} - e l s_{1} u \\sin{\\left(d \\right)} - e z - f h + \\frac{f s_{1} v_{2}}{s_{2}} + k p s_{1} \\sin{\\left(d \\right)} - k q s_{1} \\cos{\\left(d \\right)} - l p s_{1} \\cos{\\left(d \\right)} - l q s_{1} \\sin{\\left(d \\right)}, \\ y : - b e s_{1} v \\sin{\\left(d \\right)} + b e s_{1} w \\cos{\\left(d \\right)} - b r s_{1} \\sin{\\left(d \\right)} + b s s_{1} \\cos{\\left(d \\right)} + c e s_{1} v \\cos{\\left(d \\right)} + c e s_{1} w \\sin{\\left(d \\right)} + c r s_{1} \\cos{\\left(d \\right)} + c s s_{1} \\sin{\\left(d \\right)} + e k s_{1} v \\sin{\\left(d \\right)} - e k s_{1} w \\cos{\\left(d \\right)} - e l s_{1} v \\cos{\\left(d \\right)} - e l s_{1} w \\sin{\\left(d \\right)} - e m - e o - f g j + \\frac{f j s_{1} v_{1}}{s_{2}} + k r s_{1} \\sin{\\left(d \\right)} - k s s_{1} \\cos{\\left(d \\right)} - l r s_{1} \\cos{\\left(d \\right)} - l s s_{1} \\sin{\\left(d \\right)} - m overfocus_{scan_1}\\right\\}$" + ], + "text/plain": [ + "⎧ ↪\n", + "⎨x: -\\hat{n}⋅e - \\hat{n}⋅overfocus_{scan_1} - b⋅e⋅s₁⋅t⋅sin(d) + b⋅e⋅s₁⋅u⋅cos(d ↪\n", + "⎩ ↪\n", + "\n", + "↪ ↪\n", + "↪ ) - b⋅p⋅s₁⋅sin(d) + b⋅q⋅s₁⋅cos(d) + c⋅e⋅s₁⋅t⋅cos(d) + c⋅e⋅s₁⋅u⋅sin(d) + c⋅p⋅ ↪\n", + "↪ ↪\n", + "\n", + "↪ ↪\n", + "↪ s₁⋅cos(d) + c⋅q⋅s₁⋅sin(d) + e⋅k⋅s₁⋅t⋅sin(d) - e⋅k⋅s₁⋅u⋅cos(d) - e⋅l⋅s₁⋅t⋅cos ↪\n", + "↪ ↪\n", + "\n", + "↪ f⋅s₁⋅v₂ ↪\n", + "↪ (d) - e⋅l⋅s₁⋅u⋅sin(d) - e⋅z - f⋅h + ─────── + k⋅p⋅s₁⋅sin(d) - k⋅q⋅s₁⋅cos(d) ↪\n", + "↪ s₂ ↪\n", + "\n", + "↪ ↪\n", + "↪ - l⋅p⋅s₁⋅cos(d) - l⋅q⋅s₁⋅sin(d), y: -b⋅e⋅s₁⋅v⋅sin(d) + b⋅e⋅s₁⋅w⋅cos(d) - b⋅r ↪\n", + "↪ ↪\n", + "\n", + "↪ ↪\n", + "↪ ⋅s₁⋅sin(d) + b⋅s⋅s₁⋅cos(d) + c⋅e⋅s₁⋅v⋅cos(d) + c⋅e⋅s₁⋅w⋅sin(d) + c⋅r⋅s₁⋅cos( ↪\n", + "↪ ↪\n", + "\n", + "↪ ↪\n", + "↪ d) + c⋅s⋅s₁⋅sin(d) + e⋅k⋅s₁⋅v⋅sin(d) - e⋅k⋅s₁⋅w⋅cos(d) - e⋅l⋅s₁⋅v⋅cos(d) - e ↪\n", + "↪ ↪\n", + "\n", + "↪ f⋅j⋅s₁⋅v₁ ↪\n", + "↪ ⋅l⋅s₁⋅w⋅sin(d) - e⋅m - e⋅o - f⋅g⋅j + ───────── + k⋅r⋅s₁⋅sin(d) - k⋅s⋅s₁⋅cos( ↪\n", + "↪ s₂ ↪\n", + "\n", + "↪ ⎫\n", + "↪ d) - l⋅r⋅s₁⋅cos(d) - l⋅s⋅s₁⋅sin(d) - m⋅overfocus_{scan_1}⎬\n", + "↪ ⎭" + ] + }, + "execution_count": 141, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sym.init_printing(use_latex=True)\n", + "get_equation_sympy(DescanError(pxo_pxi=p, pxo_pyi=q, pyo_pxi=r, pyo_pyi=s, sxo_pxi=t, sxo_pyi=u, syo_pxi=v, syo_pyi=w, offpxi=x, offpyi=y, offsxi=z, offsyi=o), s1)" + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "id": "27bea541-43dd-4197-998b-f8255e5e8530", + "metadata": {}, "outputs": [], "source": [ - "p = Parameters4DSTEM(\n", - " overfocus=0.01,\n", - " scan_pixel_pitch=1e-6,\n", - " scan_center=PixelYX(0.1, 0.2),\n", - " scan_rotation=0.01,\n", - " camera_length=1.0,\n", - " detector_pixel_pitch=50e-6,\n", - " detector_center=PixelYX(0.1, 0.1),\n", - " semiconv=1e-3, # radian\n", - " flip_factor=1.0,\n", - " descan_error=DescanError(sxo_pxi=1, syo_pyi=-3),\n", - " xp=np\n", - ")" + "def adjust_pixel_pitch_sympy(model_cls=Model4DSTEM) -> Model4DSTEM:\n", + " \"\"\"\n", + " Adjust the scan pixel pitch while keeping the effective descan error\n", + " compensation the same.\n", + "\n", + " This allows first compensating descan error and then adjusting other parameters.\n", + " \n", + " This function first finds a general symbolic expression for each component of the\n", + " new descan error given old parameteres as symbols, then converts the sympy\n", + " expressions for each component into a function that allows fast numeric evaluation.\n", + " After that the new model is constructed.\n", + "\n", + " Use trace() to calculate detector_px() for the old and for the new parameters.\n", + " Then set the results equal to each other and solve the equation with respect\n", + " to the new descan error. For this purpose construct two expressions - for y and x\n", + " coordiates respectively - and apply sympy.solve().\n", + " Note that the equations should hold for every value of the scan position (in particular,\n", + " scan_pos_y scan_pos_x), and also for every value of the camera length. To implement that,\n", + " expand the expressions, then rearange them to be polynomials in the variables scan_pos_y,\n", + " scan_pos_x and camera_length, which automatically means collecting the coefficients\n", + " of each variable through the usage of sympy.Poly(). Finally, set the collected\n", + " coefficients of the polynomials to zero, which mathematically implies the desired\n", + " property: a polynomial is zero for all the values of its variables if and only if\n", + " all its coefficients equal to zero.\n", + " In such a way one receives equations for each of the collected coefficients, which\n", + " suffices to make the system solvable for all the 12 components of descan error as\n", + " variables.\n", + "\n", + " The received expressions for each component of the new DescanError fully coincide\n", + " with the ones calculated by hand earlier.\n", + "\n", + " apply_function() cconstructs a function using sympy.lambdify() that allows inserting\n", + " real values to each expression of the general solution and after that evaluating them\n", + " numerically. The lambdify() method can be supplemented with the specific\n", + " numerical backend by inserting e.g. \"numpy\", \"cupy\" or \"jax\" to the optional\n", + " argument \"modules=\" of sympy.lambdify().\n", + " \"\"\"\n", + " (overfocus, scan_center_y,\n", + " scan_center_x, scan_rotation,\n", + " camera_length, detector_pitch,\n", + " detector_center_y, detector_center_x,\n", + " semiconv, flip_factor) = sym.symbols(r'p_overfocus p_{scancenter_y} '\n", + " 'p_{scancenter_x} p_scanrot '\n", + " 'p_camera p_detectorpitch '\n", + " 'p_{detectorcenter_y} p_{detectorcenter_x} '\n", + " 'p_semiconv p_flip', nonzero=True)\n", + "\n", + "\n", + " (pxo_pxi_old, pxo_pyi_old,\n", + " pyo_pxi_old, pyo_pyi_old,\n", + " sxo_pxi_old, sxo_pyi_old,\n", + " syo_pxi_old, syo_pyi_old,\n", + " offpxi_old, offpyi_old,\n", + " offsxi_old, offsyi_old) = sym.symbols(r'pxopxi_old pxopyi_old '\n", + " 'pyopxi_old pyopyi_old '\n", + " 'sxopxi_old sxopyi_old '\n", + " 'syopxi_old syopyi_old '\n", + " 'offpxi_old offpyi_old '\n", + " 'offsxi_old offsyi_old')\n", + " (pxo_pxi_new, pxo_pyi_new,\n", + " pyo_pxi_new, pyo_pyi_new,\n", + " sxo_pxi_new, sxo_pyi_new,\n", + " syo_pxi_new, syo_pyi_new,\n", + " offpxi_new, offpyi_new,\n", + " offsxi_new, offsyi_new) = sym.symbols(r'pxopxi_new pxopyi_new '\n", + " 'pyopxi_new pyopyi_new '\n", + " 'sxopxi_new sxopyi_new '\n", + " 'syopxi_new syopyi_new '\n", + " 'offpxi_new offpyi_new '\n", + " 'offsxi_new offsyi_new')\n", + " \n", + " descan_error_old = DescanError(\n", + " pxo_pxi=pxo_pxi_old, pxo_pyi=pxo_pyi_old,\n", + " pyo_pxi=pyo_pxi_old, pyo_pyi=pyo_pyi_old,\n", + " sxo_pxi=sxo_pxi_old, sxo_pyi=sxo_pyi_old,\n", + " syo_pxi=syo_pxi_old, syo_pyi=syo_pyi_old,\n", + " offpxi=offpxi_old, offpyi=offpyi_old,\n", + " offsxi=offsxi_old, offsyi=offsyi_old)\n", + " \n", + " descan_error_new = DescanError(\n", + " pxo_pxi=pxo_pxi_new, pxo_pyi=pxo_pyi_new,\n", + " pyo_pxi=pyo_pxi_new, pyo_pyi=pyo_pyi_new,\n", + " sxo_pxi=sxo_pxi_new, sxo_pyi=sxo_pyi_new,\n", + " syo_pxi=syo_pxi_new, syo_pyi=syo_pyi_new,\n", + " offpxi=offpxi_new, offpyi=offpyi_new,\n", + " offsxi=offsxi_new, offsyi=offsyi_new\n", + " )\n", + " \n", + " pitch_old, pitch_new = sym.symbols(r'pitch_old pitch_new', nonzero=True)\n", + " \n", + " params_old = Parameters4DSTEM(\n", + " overfocus=overfocus,\n", + " scan_pixel_pitch=pitch_old,\n", + " scan_center=PixelYX(scan_center_y, scan_center_x),\n", + " scan_rotation=scan_rotation,\n", + " camera_length=camera_length,\n", + " detector_pixel_pitch=detector_pitch,\n", + " detector_center=PixelYX(detector_center_y, detector_center_x),\n", + " semiconv=semiconv, # radian\n", + " flip_factor=flip_factor,\n", + " descan_error=descan_error_old,\n", + " xp=np\n", + " )\n", + "\n", + " params_new = Parameters4DSTEM(\n", + " overfocus=overfocus,\n", + " scan_pixel_pitch=pitch_new,\n", + " scan_center=PixelYX(scan_center_y, scan_center_x),\n", + " scan_rotation=scan_rotation,\n", + " camera_length=camera_length,\n", + " detector_pixel_pitch=detector_pitch,\n", + " detector_center=PixelYX(detector_center_y, detector_center_x),\n", + " semiconv=semiconv, # radian\n", + " flip_factor=flip_factor,\n", + " descan_error=descan_error_new,\n", + " xp=np\n", + " )\n", + " \n", + " def detector_px(m: Model4DSTEM):\n", + " ray = m.make_source_ray(\n", + " source_dy=0.,\n", + " source_dx=0.,\n", + " ).ray\n", + " data = m.trace(ray)\n", + " return data['detector'].sampling['detector_px']\n", + "\n", + " scan_pos_y, scan_pos_x = sym.symbols(r'scan_{pos_y} scan_{pos_x}')\n", + " scan_pos = PixelYX(scan_pos_y, scan_pos_x)\n", + "\n", + " model_old = Model4DSTEM.build(params=params_old, scan_pos=scan_pos)\n", + " model_new = Model4DSTEM.build(params=params_new, scan_pos=scan_pos)\n", + " \n", + " det_px_old = detector_px(model_old)\n", + " det_px_new = detector_px(model_new)\n", + " \n", + " expanded_expr_y = sym.expand(det_px_old.y - det_px_new.y)\n", + " expanded_expr_x = sym.expand(det_px_old.x - det_px_new.x)\n", + "\n", + " collected_y = sym.Poly(expanded_expr_y, scan_pos_y, scan_pos_x, camera_length)\n", + " collected_x = sym.Poly(expanded_expr_x, scan_pos_y, scan_pos_x, camera_length)\n", + " \n", + " coeff = collected_y.coeffs() + collected_x.coeffs()\n", + " \n", + " solution = sym.solve([eq for eq in coeff], [pxo_pxi_new, pxo_pyi_new,\n", + " pyo_pxi_new, pyo_pyi_new,\n", + " sxo_pxi_new, sxo_pyi_new,\n", + " syo_pxi_new, syo_pyi_new,\n", + " offpxi_new, offpyi_new,\n", + " offsxi_new, offsyi_new], simplify=True)\n", + "\n", + " def apply_function(m: Model4DSTEM, new_scan_pixel_pitch) -> Model4DSTEM:\n", + " substituted = sym.lambdify([\n", + " pxo_pxi_old, pxo_pyi_old,\n", + " pyo_pxi_old, pyo_pyi_old,\n", + " sxo_pxi_old, sxo_pyi_old,\n", + " syo_pxi_old, syo_pyi_old,\n", + " offpxi_old, offpyi_old,\n", + " offsxi_old, offsyi_old,\n", + " pitch_old, pitch_new], [solution[attr] for attr in [pxo_pxi_new, pxo_pyi_new,\n", + " pyo_pxi_new, pyo_pyi_new,\n", + " sxo_pxi_new, sxo_pyi_new,\n", + " syo_pxi_new, syo_pyi_new,\n", + " offpxi_new, offpyi_new,\n", + " offsxi_new, offsyi_new]])\n", + " evaluated = substituted(*(m.descanner.descan_error._asdict()[key] for key in m.descanner.descan_error._asdict().keys()), m.params.scan_pixel_pitch, new_scan_pixel_pitch)\n", + " new_de = DescanError(*(item for item in evaluated))\n", + " new_params = m.params.derive(descan_error=new_de)\n", + " new_model = Model4DSTEM.build(new_params, m.scan_pos)\n", + " return new_model\n", + " \n", + " return apply_function" + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "id": "686d4941-70ef-4cf8-94b4-af438bcdf068", + "metadata": {}, + "outputs": [], + "source": [ + "sym.init_printing(use_latex=True)\n", + "params = Parameters4DSTEM(\n", + " overfocus=0.7,\n", + " scan_pixel_pitch=0.005,\n", + " scan_center=PixelYX(y=17, x=13),\n", + " scan_rotation=1.234,\n", + " camera_length=2.3,\n", + " detector_pixel_pitch=0.0247,\n", + " detector_center=PixelYX(y=11, x=19),\n", + " detector_rotation=2.134,\n", + " semiconv=0.023,\n", + " flip_factor=-1.,\n", + " descan_error=DescanError(offpxi=.345, pxo_pxi=948)\n", + " )\n", + "mod= Model4DSTEM.build(params=params, scan_pos=PixelYX(y=13, x=7))\n", + "\n", + "desc = adjust_pixel_pitch_sympy()(mod, 2.)" ] }, { "cell_type": "code", "execution_count": 144, - "id": "809731bb-ce0b-428d-bcfb-6bc95c81e015", + "id": "740fa25f-4e49-4c45-9f61-79654788f3b4", "metadata": {}, "outputs": [ { - "ename": "TypeError", - "evalue": "loop of ufunc does not support argument 0 of type Mul which has no callable cos method", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", - "\u001b[31mAttributeError\u001b[39m: 'Mul' object has no attribute 'cos'", - "\nThe above exception was the direct cause of the following exception:\n", - "\u001b[31mTypeError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[144]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m model = \u001b[43mModel4DSTEM\u001b[49m\u001b[43m.\u001b[49m\u001b[43mbuild\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparams\u001b[49m\u001b[43m=\u001b[49m\u001b[43mpar\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mscan_pos\u001b[49m\u001b[43m=\u001b[49m\u001b[43mPixelYX\u001b[49m\u001b[43m(\u001b[49m\u001b[32;43m0.0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[32;43m0.0\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 2\u001b[39m create_scan_pixel_pitch_fn()(model, \u001b[32m2.5\u001b[39m)\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Microscope-Calibration/src/microscope_calibration/common/model.py:603\u001b[39m, in \u001b[36mModel4DSTEM.build\u001b[39m\u001b[34m(cls, params, scan_pos, specimen)\u001b[39m\n\u001b[32m 596\u001b[39m \u001b[38;5;129m@classmethod\u001b[39m\n\u001b[32m 597\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mbuild\u001b[39m(\n\u001b[32m 598\u001b[39m \u001b[38;5;28mcls\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 601\u001b[39m specimen: Optional[Component] = \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[32m 602\u001b[39m ) -> \u001b[33m\"\u001b[39m\u001b[33mModel4DSTEM\u001b[39m\u001b[33m\"\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m603\u001b[39m scan_to_real = \u001b[43mrotate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparams\u001b[49m\u001b[43m.\u001b[49m\u001b[43mscan_rotation\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mxp\u001b[49m\u001b[43m=\u001b[49m\u001b[43mparams\u001b[49m\u001b[43m.\u001b[49m\u001b[43mxp\u001b[49m\u001b[43m)\u001b[49m @ scale(params.scan_pixel_pitch,\n\u001b[32m 604\u001b[39m xp=params.xp)\n\u001b[32m 605\u001b[39m real_to_scan = scale(\u001b[32m1\u001b[39m / params.scan_pixel_pitch, xp=params.xp) @ rotate(\n\u001b[32m 606\u001b[39m -params.scan_rotation, xp=params.xp)\n\u001b[32m 607\u001b[39m scan_y, scan_x = scan_to_real @ params.xp.array(\n\u001b[32m 608\u001b[39m (\n\u001b[32m 609\u001b[39m scan_pos.y - params.scan_center.y,\n\u001b[32m 610\u001b[39m scan_pos.x - params.scan_center.x,\n\u001b[32m 611\u001b[39m )\n\u001b[32m 612\u001b[39m )\n", - "\u001b[36mFile \u001b[39m\u001b[32m~/Microscope-Calibration/src/microscope_calibration/common/model.py:32\u001b[39m, in \u001b[36mrotate\u001b[39m\u001b[34m(radians, xp)\u001b[39m\n\u001b[32m 27\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mrotate\u001b[39m(radians, xp):\n\u001b[32m 28\u001b[39m \u001b[38;5;66;03m# https://en.wikipedia.org/wiki/Rotation_matrix\u001b[39;00m\n\u001b[32m 29\u001b[39m \u001b[38;5;66;03m# y, x instead of x, y\u001b[39;00m\n\u001b[32m 30\u001b[39m \u001b[38;5;66;03m# radians = jnp.astype(radians, jnp.float64)\u001b[39;00m\n\u001b[32m 31\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m xp.array(\n\u001b[32m---> \u001b[39m\u001b[32m32\u001b[39m [(\u001b[43mxp\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcos\u001b[49m\u001b[43m(\u001b[49m\u001b[43mradians\u001b[49m\u001b[43m)\u001b[49m, xp.sin(radians)), (-xp.sin(radians), xp.cos(radians))]\n\u001b[32m 33\u001b[39m )\n", - "\u001b[31mTypeError\u001b[39m: loop of ufunc does not support argument 0 of type Mul which has no callable cos method" - ] + "data": { + "text/plain": [ + "Model4DSTEM(source=PointSource(z=0, semi_conv=0.023, offset_xy=CoordsXY(x=0.0, y=0.0)), scanner=Scanner(z=0.7, scan_pos_x=0.00896241094534078, scan_pos_y=-0.0349238484426736, scan_tilt_x=0.0, scan_tilt_y=0.0), specimen=Plane(z=0.7), descanner=Descanner(z=0.7, scan_pos_x=0.00896241094534078, scan_pos_y=-0.0349238484426736, scan_tilt_x=0.0, scan_tilt_y=0.0, descan_error=DescanError(pxo_pxi=2.37000000000000, pxo_pyi=0, pyo_pxi=0, pyo_pyi=0, sxo_pxi=0, sxo_pyi=0, syo_pxi=0, syo_pyi=0, offpxi=0.345, offpyi=0.0, offsxi=0.0, offsyi=0.0)), detector=Plane(z=3.0), _scan_to_real=Matrix([\n", + "[ 0.00165232554035865, 0.00471909104687317],\n", + "[-0.00471909104687317, 0.00165232554035865]]), _real_to_scan=Matrix([\n", + "[ 66.093021614346, -188.763641874927],\n", + "[188.763641874927, 66.093021614346]]), _detector_to_real=Matrix([\n", + "[-0.0247, 0],\n", + "[ 0, 0.0247]]), _real_to_detector=Matrix([\n", + "[-40.4858299595142, 0],\n", + "[ 0, 40.4858299595142]]), scan_center=PixelYX(y=17, x=13), detector_center=PixelYX(y=11, x=19), xp=)" + ] + }, + "execution_count": 144, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "model = Model4DSTEM.build(params=par, scan_pos=PixelYX(0.0, 0.0))\n", - "create_scan_pixel_pitch_fn()(model, 2.5)" + "desc" ] }, { "cell_type": "code", - "execution_count": 146, - "id": "64ae1ff3-f319-4892-85b1-a6dcb99a459f", + "execution_count": 145, + "id": "6e5eed56-3ace-4f72-b192-8271cc24aef4", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAADoAAAAUCAYAAADcHS5uAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAArtJREFUeJzl10/IFVUYx/GP5iIoSlBKahFFSlAGhQsXhrYRVApKXKa2qEWIGAmRIM/7GJItKirBFoEvtQuiIAyjMKVoEfSHkqg2aS581UxEsqjsbTFzcRzu1TP3Xm3Rd3PmnjnPb85v7jPPOWfG9PS0/wMz/+sJXClm9S4ycxk+btz7ISLuuNITGoXMnIsTzb6ImEHDaIMD2I9fWiLPYxEWYC5+x2G8i50RcXLM85aZc/AQVmEhbsaf+Ba7sTsi/mmEnEXW1+txS+9GP6P7I2KiT/+T+BIf4jiuwWJM4PHMXBwRR4Y1NYA12IWjqmz7GTfiYbyOFZm5JiKmISLO1vPpZehFjQ7iuoj4o92ZmduxBc/gic5WLs6PeBB7mv9cZm7B51itMv32pYSKjfYzWfOWyuj8Uq0Oz9w3oH8qM1/DdixTYHQcVfeBuv1mDFpd+Ktu/y4Z3CV1QWZuxrW4XlWclqhM7uiqNSyZOQtr6597S2I6G8VmVUHosRfrI+LEgPGXgx24C+9HxAclAZ1TNyLm1WvTPFUhuA1fZea9XbWGITM34il8j0dK44b+RiPiWES8g+WYgzeG1SolMzfgZXyH+yPi19LYkYtRRByuH3xnvTO5LGTmJryKgyqTU13ix7XXvaluz41J7wIy82m8hK9VJo931SgqRpm5AMci4nSrfyaexQ34LCJOte5PYh0ejYjJrpOrNbZiG77A8i7p2qS06q7Ec5n5KX7CSVXlXaoqRlN4rE9cL2OK1ro2mblOZfIcPsHGzGwPO1TyEkuNfoTbVWvmPZiN31RbtDfxyoA3vRBnsKfwOW1urdursGnAmAOYvJRQkdGIOIgNJWN7ZOZs3I0X2ildSn24mBgmtk0/o5GZYfTz6H2qbdqLI2h0ot95tEfT6CHnz3K0zqNdiYj3cPUoGkPQPI9ewL9c+9YfZEcz4AAAAABJRU5ErkJggg==", + "text/latex": [ + "$\\displaystyle \\left[ 3, \\ 2\\right]$" + ], + "text/plain": [ + "[3, 2]" + ] + }, + "execution_count": 145, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "z = sym.Symbol('z')" + "f = sym.lambdify([y,x], [x+y+1, y+1])\n", + "f(*(1 for i in range(1)), 1)" ] }, { "cell_type": "code", - "execution_count": 150, - "id": "8d31bffa-5f10-417f-8643-c85c037b77e3", + "execution_count": 146, + "id": "955fdea0-5541-4f36-870d-ad98794fbd09", "metadata": {}, "outputs": [ { - "ename": "TypeError", - "evalue": "loop of ufunc does not support argument 0 of type Symbol which has no callable cos method", + "name": "stdout", + "output_type": "stream", + "text": [ + "Poly((A - 2)*x + (B - 3)*y + C, x, y, domain='ZZ[A,B,C]') Poly((A - 1)*x*y + D, x, y, domain='ZZ[A,D]')\n", + "Poly((A - 2)*x + B*y + C - 3*y, x, domain='ZZ[y,A,B,C]') Poly((B - 3)*y + A*x + C - 2*x, y, domain='ZZ[x,A,B,C]') Poly((A*y - y)*x + D, x, domain='ZZ[y,A,D]') Poly((A*x - x)*y + D, y, domain='ZZ[x,A,D]')\n", + "[[A - 2], [B - 3, C], [B - 3], [A - 2, C], [A - 1], [D], [A - 1], [D]]\n", + "[A - 2, B - 3, C, A - 1, D]\n" + ] + }, + { + "ename": "AttributeError", + "evalue": "'list' object has no attribute 'is_Relational'", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", - "\u001b[31mAttributeError\u001b[39m: 'Symbol' object has no attribute 'cos'", - "\nThe above exception was the direct cause of the following exception:\n", - "\u001b[31mTypeError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[150]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mnp\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcos\u001b[49m\u001b[43m(\u001b[49m\u001b[43mz\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[31mTypeError\u001b[39m: loop of ufunc does not support argument 0 of type Symbol which has no callable cos method" + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[146]\u001b[39m\u001b[32m, line 35\u001b[39m\n\u001b[32m 33\u001b[39m \u001b[38;5;66;03m# Solve for unknown parameters\u001b[39;00m\n\u001b[32m 34\u001b[39m solutions = sym.solve([coeff_1[\u001b[32m0\u001b[39m], coeff_1[\u001b[32m1\u001b[39m], coeff_1[\u001b[32m2\u001b[39m], coeff_2[\u001b[32m0\u001b[39m]], [A, B, C, D])\n\u001b[32m---> \u001b[39m\u001b[32m35\u001b[39m solutions_xy = \u001b[43msym\u001b[49m\u001b[43m.\u001b[49m\u001b[43msolve\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[43meq\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43meq\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mcoeff_xy\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43mA\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mB\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mC\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mD\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 37\u001b[39m \u001b[38;5;28mprint\u001b[39m(solutions_xy)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniforge3/envs/py313/lib/python3.13/site-packages/sympy/solvers/solvers.py:947\u001b[39m, in \u001b[36msolve\u001b[39m\u001b[34m(f, *symbols, **flags)\u001b[39m\n\u001b[32m 943\u001b[39m f[i] = fi\n\u001b[32m 945\u001b[39m \u001b[38;5;66;03m# *** dispatch and handle as a system of relationals\u001b[39;00m\n\u001b[32m 946\u001b[39m \u001b[38;5;66;03m# **************************************************\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m947\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[43mfi\u001b[49m\u001b[43m.\u001b[49m\u001b[43mis_Relational\u001b[49m:\n\u001b[32m 948\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(symbols) != \u001b[32m1\u001b[39m:\n\u001b[32m 949\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mcan only solve for one symbol at a time\u001b[39m\u001b[33m\"\u001b[39m)\n", + "\u001b[31mAttributeError\u001b[39m: 'list' object has no attribute 'is_Relational'" ] } ], "source": [ - "np.cos(z)" + "x, y = sym.symbols('x y', real=True)\n", + "\n", + "# Unknown parameters\n", + "A, B, C, D = sym.symbols('A B C D', real=True)\n", + "\n", + "# Example equations that must hold for all x,y\n", + "eq1 = A*x + B*y + C - (2*x + 3*y)\n", + "eq2 = A*x*y +D - (x*y)\n", + "\n", + "# Convert to polynomials in x,y\n", + "poly1 = sym.Poly(sym.expand(eq1), x, y)\n", + "poly2 = sym.Poly(sym.expand(eq2), x, y)\n", + "print(poly1, poly2)\n", + "\n", + "poly1_x = sym.Poly(sym.expand(eq1), x)\n", + "poly1_y = sym.Poly(sym.expand(eq1), y)\n", + "poly2_x = sym.Poly(sym.expand(eq2), x)\n", + "poly2_y = sym.Poly(sym.expand(eq2), y)\n", + "print(poly1_x, poly1_y, poly2_x, poly2_y)\n", + "coeff1_x = [sym.Poly(coef, y).coeffs() for coef in poly1_x.coeffs()]\n", + "coeff1_y = [sym.Poly(coef, x).coeffs() for coef in poly1_y.coeffs()]\n", + "coeff2_x = [sym.Poly(coef, y).coeffs() for coef in poly2_x.coeffs()]\n", + "coeff2_y = [sym.Poly(coef, x).coeffs() for coef in poly2_y.coeffs()]\n", + "coeff_xy = coeff1_x + coeff1_y + coeff2_x + coeff2_y\n", + "print(coeff_xy)\n", + "\n", + "# Collect all coefficient equations\n", + "coeff_1 = poly1.coeffs()\n", + "coeff_2 = poly2.coeffs()\n", + "coeff = coeff_1 + coeff_2\n", + "print(coeff)\n", + "\n", + "# Solve for unknown parameters\n", + "solutions = sym.solve([coeff_1[0], coeff_1[1], coeff_1[2], coeff_2[0]], [A, B, C, D])\n", + "solutions_xy = sym.solve([eq for eq in coeff_xy], [A, B, C, D])\n", + "\n", + "print(solutions_xy)" ] }, { "cell_type": "code", - "execution_count": null, - "id": "f9d74901-5c88-4132-8f2f-fba4f0f7e8dc", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a851215e-9c9a-4aa2-bae8-d57e60d63135", + "execution_count": 147, + "id": "c70fcb7b-c08f-48d6-b7c6-7bb630aa982c", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle \\operatorname{Poly}{\\left( 1.0 xy + 1.0 x + 1.0 y, x, y, domain=\\mathbb{R} \\right)}$" + ], + "text/plain": [ + "Poly(1.0*x*y + 1.0*x + 1.0*y, x, y, domain='RR')" + ] + }, + "execution_count": 147, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "model = Model4DSTEM.build(params=p, scan_pos=PixelYX(0.0, 0.0))\n", - "parameters = Parameters4DSTEM(overfocus=p.overfocus,\n", - " scan_pixel_pitch=p.scan_pixel_pitch,\n", - " scan_center=p.scan_center,\n", - " scan_rotation=p.scan_rotation,\n", - " camera_length=p.camera_length,\n", - " detector_pixel_pitch=p.detector_pixel_pitch,\n", - " detector_center=p.detector_center,\n", - " semiconv=p.semiconv, # radian\n", - " flip_factor=p.flip_factor,\n", - " descan_error=DescanError(),\n", - " xp=np)\n", - "model_updated = Model4DSTEM.build(params=parameters, scan_pos=model.scan_pos)" + "#xx, yy = desc.items()\n", + "#xx\n", + "sym.Poly(x+y+x*y, x, y, domain='RR')" ] }, { "cell_type": "code", - "execution_count": null, - "id": "e701106a-b289-412b-a97a-05f599a917cb", + "execution_count": 148, + "id": "ebaef689-9ee9-4c4e-a87f-d736c81f0926", "metadata": {}, "outputs": [], "source": [ - "s = {'x': 0, 'y': 1}\n", - "s['x']" + "def solve_test():\n", + " a_old, b = sym.symbols('a_old b')\n", + "\n", + " a_new = sym.symbols('a_new')\n", + "\n", + " x = sym.symbols('x')\n", + "\n", + " def fun(a, x):\n", + " return a * x + b\n", + "\n", + " old = fun(a_old, x)\n", + " new = fun(a_new, x)\n", + "\n", + " eq = sym.Eq(old, new)\n", + "\n", + " return sym.solve(eq, [a_old, a_new], dict=True)\n", + " " ] }, { "cell_type": "code", - "execution_count": null, - "id": "c30a799d-9fc9-445a-ad8d-1f46a333fa0c", + "execution_count": 149, + "id": "ebb573aa-b8b4-4687-a67e-7df3292bc2f7", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIMAAAAVCAYAAABlol04AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAABjJJREFUeJztmmuIVVUUx3+TlqHmK8ksw6zEBzlzx+xFWWoohpUSvagoTSmzEgks02j5LygTMTMRKyzTKMs0/JAQWRkRKmiNaZajH+zhI6xGzUxTmz6sfZoz13vPfYw3J5g/HPY5e6999l57r7P2f+2zy2pra2lCEwBOOdkdaELjQfPoRtIA4NNY2RYz65mtoqQ7gfFAL6A1cImZVZWkl01IhKSpwBTgV2AN8JiZVSfIdwT2xPPMrKx5BtnPgFXALwkv6w28CfwBLAF2ALsL0qAJJxKrgNOBFDAcOAe4LEH+IKBwPxLoCjHPEH+xmU3N0fhAoAyYYmaz8+xwE0oEM1uFGwSS1gP9JLU2swNZ5A8CU4P8AIIxFMsZOoT02yLrN6F0+A7/UNsXWjGTZ8gHzUJ6JJdg4BbDgEuBzsBRYBsw18xeL7L9RoNGqF80J80SpTKgpNGEpDOAhcAFwOfAHGAZ0A14TdLjpWy/WEhaIKlW0sgccv9L/bKhWM/QLqR/5pCrBbqYWT1yKelJoBoYBTxfZB8aAxqjfodC2rbQigV7BkllQH98IL5PkjWzA+kDFfJ3ATup4x6NDU/gIfP7SUKl0E/SNEkf5ZAZK2lrluLtIR1QaNt5ewZJQ4HBwDVAX+DlTAORVqc98DC+pvYA2lDfAL8qtMP/BcJk7solVyL9UkBVA2QWAWOBGZKuArYCr5rZ9lwNF+IZhgKPAv2ATcBLScKSyvFo42mc3S4GnsPj24VBbEMB7TcqlFC/FLmNKKuMme0I/TgC3ApMBs7Pp+G8PYOZTZD0FL7HsBj4UFJXMzuWpcoinFsMDHHwv5D0dLhdF8vrAvwIjAAexJei3cAYM/s0JncuruwwnDF/DIwzs59D+RpgmZlND8/zgfuAzma2W1IbfJPsxvR+FYiC9IuV9cJ5xLXAYeBdYKKZ/SnpbKATsa9eUgp4AbgCj1JGA31wIzwOkoYB84AvgAeAajPLGfVBgZzBzPab2XJgKXAuvq5m6tB5QDm+gbUqrawdTqwA1seKKkL6KDAjPG8CZsbqdgO+xCfzanxd7IgrH6EG3x5H0lnALcBv1MXd9wLbGmIIReoXeZPV+F7ApcDNwA3UTWwKJ+Vbgnx3fEd4LW4Ak3DjaUn2ZWJQSKea2Tf5GgIUH01ExDEbQYoY7QWSTo06JOlM4C2gCx6PV8XqpID9wO0RF5H0Hu4FIswD5pvZ5ChD0jN4OBdhL8EYgIdwEpgCOgTyO44cDF9SZ5yN7zKzfSdIP4BXgaVm9lh4rpY0F//aJ4Z+box52znAB2Y2KTxvkzQCuMnMdmbpfmT025N0zIRijSGytoyexcz2SPoEt9K1klbiGzLX4z/D/gY2m9mhWLUKXPE4Kb0Id41I6goMAfpLGh+TaYbvtUeoAc6QdDpOpIYAs/FBui6kb+fQ7zncg4wCFpwI/ST1wP8XjEl73WGgRbhPEQwoeJ8hHP+P4QjJBDOak6MJMokVC0U+hyDuwNfV83AOcCG+BDwb2k1fT1O4C42jkjrFK3DPUR5ko6tPkIuwF/cMdwObzGxDqBcx/3lmdjiP/udCofpdDBzj+C383sDGcJ+iTt/KIJ9OQvuSO9qA/OaoHor1DNFgZt3/NrM9wD1ZisviD5Ja4YOZzpArqVsCjgCtgN3ZfsAE1OAGMwGIdgD34QM9GCdViTCzkfjfvCSZvPUL+B03ktMIX62kTsBdwBhJLYHu1I1BLe71WgB/Bfn+wOXArISuRXNyKEEmIzIZg0kyks8zRBseoyWtA3aY2d+FNh5DeUirooyw/naJ5a3BJ3pRYOv78W3gEcAjsfb34kx9B7Ai5O0H7geWRFHHScBa/LzBNEkv4gR8FrASeAefZICvQ7oO/+hmSJoO9ATmhrKq9JdLaoF7zwG4vnvSZWKyx51ngPrLxHY8Ro6uOQmKrcCZ/jDgB+BYCIGKRQWwNe2Lr8S9wWYAM6vB1+S2+LpchUcdP6UZYhRNzDKzyFXui/Ia0McGIRDR4cCV+LLwBrAcuC30M4WPwcEgvwvnLEPxsZ6C85eD+Fb3vwiHWw7hBtcGmJnj44zOM8Qvyoo9AxkscRB1J51eybUj2YTSIJxJuBr3PKuLPXH2D7VRYJKN4W15AAAAAElFTkSuQmCC", + "text/latex": [ + "$\\displaystyle \\left[ \\left\\{ a_{new} : a_{old}\\right\\}\\right]$" + ], + "text/plain": [ + "[{a_new: a_old}]" + ] + }, + "execution_count": 149, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "s['x'] = 1" + "solve_test()" ] }, { "cell_type": "code", - "execution_count": null, - "id": "c82c3adf-18da-4759-9129-51db7cc94e3c", + "execution_count": 150, + "id": "e9e52597-3d6d-4b39-8f45-a4cf48c993cb", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "expression cannot contain assignment, perhaps you meant \"==\"? (2091338569.py, line 2)", + "output_type": "error", + "traceback": [ + " \u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[150]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[31m \u001b[39m\u001b[31msym.solve(5 = 1-x, implicit=True)\u001b[39m\n ^\n\u001b[31mSyntaxError\u001b[39m\u001b[31m:\u001b[39m expression cannot contain assignment, perhaps you meant \"==\"?\n" + ] + } + ], "source": [ - "s" + "detector_px(scan_p, descan, 2.0)['scanner'].component.__eq__(detector_px(scan_p, descan, 2.0)['scanner'].component)\n", + "sym.solve(5 = 1-x, implicit=True)" ] }, { "cell_type": "code", - "execution_count": null, - "id": "486adde0-b3c3-4c92-a491-e591b00ccfe1", + "execution_count": 151, + "id": "02afc306-1193-4da2-b6fb-ba215d86cb60", "metadata": {}, "outputs": [], "source": [ - "DescanError(1,2)" + "def comparison(data1, data2):\n", + " equation_y = sym.Eq(data1.y, data2.y)\n", + " equation_x = sym.Eq(data1.x, data2.x)\n", + " return sym.solve(equation_y, equation_y)" ] }, { "cell_type": "code", - "execution_count": null, - "id": "1dbc3efc-199d-4870-8a03-1f1347382f25", + "execution_count": 152, + "id": "f63e8d97-f96b-4330-977d-a35b57f01d3d", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'detector_px' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[152]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m comparison(\u001b[43mdetector_px\u001b[49m(scan_p, descan, \u001b[32m2.0\u001b[39m), detector_px(scan_p, descan, \u001b[32m2.0\u001b[39m))\n", + "\u001b[31mNameError\u001b[39m: name 'detector_px' is not defined" + ] + } + ], "source": [ - "x, y, z = sym.symbols('x y z')\n", - "solutions = sym.solve([1-x, 1-y, 1-z], [x, y, z])\n", - "[solutions[arg] for arg in solutions.keys()]" + "comparison(detector_px(scan_p, descan, 2.0), detector_px(scan_p, descan, 2.0))" ] }, { "cell_type": "code", - "execution_count": null, - "id": "6058ba73-7db8-4b8a-8685-7ee5d8de945a", + "execution_count": 153, + "id": "da53cd45-7aeb-42a5-a677-2a58ac044e8c", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'scan_px' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[153]\u001b[39m\u001b[32m, line 4\u001b[39m\n\u001b[32m 1\u001b[39m data = trace(\n\u001b[32m 2\u001b[39m params = Parameters4DSTEM(\n\u001b[32m 3\u001b[39m overfocus=a,\n\u001b[32m----> \u001b[39m\u001b[32m4\u001b[39m scan_pixel_pitch=\u001b[43mscan_px\u001b[49m,\n\u001b[32m 5\u001b[39m scan_center=PixelYX(b, c),\n\u001b[32m 6\u001b[39m scan_rotation=d,\n\u001b[32m 7\u001b[39m camera_length=e,\n\u001b[32m 8\u001b[39m detector_pixel_pitch=f,\n\u001b[32m 9\u001b[39m detector_center=PixelYX(g, h),\n\u001b[32m 10\u001b[39m semiconv=i, \u001b[38;5;66;03m# radian\u001b[39;00m\n\u001b[32m 11\u001b[39m flip_factor=j,\n\u001b[32m 12\u001b[39m descan_error=DescanError(pxo_pxi=\u001b[32m1.\u001b[39m, pxo_pyi=\u001b[32m1.\u001b[39m, pyo_pxi=\u001b[32m1.\u001b[39m, pyo_pyi=\u001b[32m1.\u001b[39m, sxo_pxi=\u001b[32m1.\u001b[39m, sxo_pyi=\u001b[32m1\u001b[39m, syo_pxi=\u001b[32m1.\u001b[39m, syo_pyi=\u001b[32m1.\u001b[39m, offpxi=\u001b[32m1.\u001b[39m, offpyi=\u001b[32m1.\u001b[39m, offsxi=\u001b[32m1.\u001b[39m, offsyi=\u001b[32m1.\u001b[39m),\n\u001b[32m 13\u001b[39m xp=np\n\u001b[32m 14\u001b[39m ),\n\u001b[32m 15\u001b[39m scan_pos=PixelYX(\u001b[32m1.\u001b[39m, \u001b[32m1.\u001b[39m),\n\u001b[32m 16\u001b[39m source_dy=m,\n\u001b[32m 17\u001b[39m source_dx=n,\n\u001b[32m 18\u001b[39m )\n\u001b[32m 19\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m key \u001b[38;5;129;01min\u001b[39;00m data.keys():\n\u001b[32m 20\u001b[39m \u001b[38;5;28mprint\u001b[39m(data[key].component)\n", + "\u001b[31mNameError\u001b[39m: name 'scan_px' is not defined" + ] + } + ], + "source": [ + "data = trace(\n", + " params = Parameters4DSTEM(\n", + " overfocus=a,\n", + " scan_pixel_pitch=scan_px,\n", + " scan_center=PixelYX(b, c),\n", + " scan_rotation=d,\n", + " camera_length=e,\n", + " detector_pixel_pitch=f,\n", + " detector_center=PixelYX(g, h),\n", + " semiconv=i, # radian\n", + " flip_factor=j,\n", + " descan_error=DescanError(pxo_pxi=1., pxo_pyi=1., pyo_pxi=1., pyo_pyi=1., sxo_pxi=1., sxo_pyi=1, syo_pxi=1., syo_pyi=1., offpxi=1., offpyi=1., offsxi=1., offsyi=1.),\n", + " xp=np\n", + " ),\n", + " scan_pos=PixelYX(1., 1.),\n", + " source_dy=m,\n", + " source_dx=n,\n", + " )\n", + "for key in data.keys():\n", + " print(data[key].component)" + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "id": "0ce14005-44b9-468f-a243-293232e72ea0", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'detector_px' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[154]\u001b[39m\u001b[32m, line 4\u001b[39m\n\u001b[32m 2\u001b[39m descan = DescanError(pxo_pxi=\u001b[32m1.\u001b[39m, pxo_pyi=\u001b[32m1.\u001b[39m, pyo_pxi=\u001b[32m1.\u001b[39m, pyo_pyi=\u001b[32m1.\u001b[39m, sxo_pxi=\u001b[32m1.\u001b[39m, sxo_pyi=\u001b[32m1\u001b[39m, syo_pxi=\u001b[32m1.\u001b[39m, syo_pyi=\u001b[32m1.\u001b[39m, offpxi=\u001b[32m1.\u001b[39m, offpyi=\u001b[32m1.\u001b[39m, offsxi=\u001b[32m1.\u001b[39m, offsyi=\u001b[32m1.\u001b[39m)\n\u001b[32m 3\u001b[39m pitch = \u001b[32m2.\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m4\u001b[39m \u001b[43mdetector_px\u001b[49m(scan_p, descan, pitch)\n", + "\u001b[31mNameError\u001b[39m: name 'detector_px' is not defined" + ] + } + ], + "source": [ + "scan_p = PixelYX(1., 1.)\n", + "descan = DescanError(pxo_pxi=1., pxo_pyi=1., pyo_pxi=1., pyo_pyi=1., sxo_pxi=1., sxo_pyi=1, syo_pxi=1., syo_pyi=1., offpxi=1., offpyi=1., offsxi=1., offsyi=1.)\n", + "pitch = 2.\n", + "detector_px(scan_p, descan, pitch)" + ] + }, + { + "cell_type": "code", + "execution_count": 155, + "id": "7abba50b-8637-404c-beb6-d940ef69f291", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'detector_px' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[155]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mdetector_px\u001b[49m(scan_p, descan, \u001b[32m4.\u001b[39m)\n", + "\u001b[31mNameError\u001b[39m: name 'detector_px' is not defined" + ] + } + ], + "source": [ + "detector_px(scan_p, descan, 4.)" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "id": "631bb9f3-dc9a-418c-9af8-75a4d9ac70fd", + "metadata": {}, + "outputs": [], + "source": [ + "#x = sym.Symbol('x')\n", + "from sympy.abc import a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z\n", + "def sympy_function(scan_pos, de1, pitch1, pitch2):\n", + " equation = sym.Eq(detector_px(scan_pos, de1, pitch1), detector_px(scan_pos, DescanError(pxo_pxi=p, pxo_pyi=q, pyo_pxi=r, pyo_pyi=s, sxo_pxi=t, sxo_pyi=u, syo_pxi=v, syo_pyi=w, offpxi=x, offpyi=y, offsxi=z, offsyi=o), pitch2))\n", + " solution = sym.solve(equation, p, q, r, s, t, u, v, w, x, y, z)\n", + "\n", + " return solution" + ] + }, + { + "cell_type": "code", + "execution_count": 157, + "id": "559a36a7-6451-4081-96c4-ec69c2c28adf", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'detector_px' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[157]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43msympy_function\u001b[49m\u001b[43m(\u001b[49m\u001b[43mscan_p\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdescan\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpitch\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[32;43m4.0\u001b[39;49m\u001b[43m)\u001b[49m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[156]\u001b[39m\u001b[32m, line 4\u001b[39m, in \u001b[36msympy_function\u001b[39m\u001b[34m(scan_pos, de1, pitch1, pitch2)\u001b[39m\n\u001b[32m 3\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34msympy_function\u001b[39m(scan_pos, de1, pitch1, pitch2):\n\u001b[32m----> \u001b[39m\u001b[32m4\u001b[39m equation = sym.Eq(\u001b[43mdetector_px\u001b[49m(scan_pos, de1, pitch1), detector_px(scan_pos, DescanError(pxo_pxi=p, pxo_pyi=q, pyo_pxi=r, pyo_pyi=s, sxo_pxi=t, sxo_pyi=u, syo_pxi=v, syo_pyi=w, offpxi=x, offpyi=y, offsxi=z, offsyi=o), pitch2))\n\u001b[32m 5\u001b[39m solution = sym.solve(equation, p, q, r, s, t, u, v, w, x, y, z)\n\u001b[32m 7\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m solution\n", + "\u001b[31mNameError\u001b[39m: name 'detector_px' is not defined" + ] + } + ], + "source": [ + "sympy_function(scan_p, descan, pitch, 4.0)" + ] + }, + { + "cell_type": "code", + "execution_count": 158, + "id": "8b862739-fd9e-4421-a888-510e8ae586de", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'detector_px' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[158]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43msympy_function\u001b[49m\u001b[43m(\u001b[49m\u001b[43mscan_p\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdescan\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpitch\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[32;43m4.\u001b[39;49m\u001b[43m)\u001b[49m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[156]\u001b[39m\u001b[32m, line 4\u001b[39m, in \u001b[36msympy_function\u001b[39m\u001b[34m(scan_pos, de1, pitch1, pitch2)\u001b[39m\n\u001b[32m 3\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34msympy_function\u001b[39m(scan_pos, de1, pitch1, pitch2):\n\u001b[32m----> \u001b[39m\u001b[32m4\u001b[39m equation = sym.Eq(\u001b[43mdetector_px\u001b[49m(scan_pos, de1, pitch1), detector_px(scan_pos, DescanError(pxo_pxi=p, pxo_pyi=q, pyo_pxi=r, pyo_pyi=s, sxo_pxi=t, sxo_pyi=u, syo_pxi=v, syo_pyi=w, offpxi=x, offpyi=y, offsxi=z, offsyi=o), pitch2))\n\u001b[32m 5\u001b[39m solution = sym.solve(equation, p, q, r, s, t, u, v, w, x, y, z)\n\u001b[32m 7\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m solution\n", + "\u001b[31mNameError\u001b[39m: name 'detector_px' is not defined" + ] + } + ], + "source": [ + "sympy_function(scan_p, descan, pitch, 4.)" + ] + }, + { + "cell_type": "code", + "execution_count": 159, + "id": "e944a681-a965-4708-9e76-26f100dbc86a", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAC4AAAAUCAYAAADyWA/8AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAAStJREFUeJztljFLw0AYhp+Kq04OboKDuOnqpOAf6OAo6OooOApv3n/i0t9h/RkOQgVxsaMUtzj0Es8SY9I2sQEfCPlydzkeku/uvl6apnSRtb8WmJf1LLB9AtxHfY+S9tsWirG9BbzFbZJ6EIlHPABDYNyAyBlwDBwCB8AGMJB0/sMrE8AhvgR2so4i8aGkZEmus9wyFX4HXoDSPyppAiSQZ0Qu3naOXwN7wCZwtchERV+8MSTla8h22dBf6eyu8i/eNpVz3PaIaFVXoGybW5g6i/MJ+Kgx/rWmSy0qi0s6bVKkLp3N8c6Kt3oA2e4D/fC4He5Htu9CPJZ0U2WuVsWZFlcXM2274QJ4BlZPPBRvyTLmKhKXbbGi9XhGLD7iq/aFBurxOYjr8W98AiMhRYPRUpGBAAAAAElFTkSuQmCC", + "text/latex": [ + "$\\displaystyle \\left[ -1\\right]$" + ], + "text/plain": [ + "[-1]" + ] + }, + "execution_count": 159, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x = sym.Symbol('x')\n", + "sym.solve(sym.Eq(2-x,3), x)" + ] + }, + { + "cell_type": "code", + "execution_count": 160, + "id": "809731bb-ce0b-428d-bcfb-6bc95c81e015", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'Symbol' object has no attribute 'scan_rotation'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[160]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m model = \u001b[43mModel4DSTEM\u001b[49m\u001b[43m.\u001b[49m\u001b[43mbuild\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparams\u001b[49m\u001b[43m=\u001b[49m\u001b[43mp\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mscan_pos\u001b[49m\u001b[43m=\u001b[49m\u001b[43mPixelYX\u001b[49m\u001b[43m(\u001b[49m\u001b[32;43m0.0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[32;43m0.0\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 2\u001b[39m create_scan_pixel_pitch_fn()(model, \u001b[32m2.5\u001b[39m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Microscope-Calibration/src/microscope_calibration/common/model.py:640\u001b[39m, in \u001b[36mModel4DSTEM.build\u001b[39m\u001b[34m(cls, params, scan_pos, specimen)\u001b[39m\n\u001b[32m 633\u001b[39m \u001b[38;5;129m@classmethod\u001b[39m\n\u001b[32m 634\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mbuild\u001b[39m(\n\u001b[32m 635\u001b[39m \u001b[38;5;28mcls\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 638\u001b[39m specimen: Optional[Component] = \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[32m 639\u001b[39m ) -> \u001b[33m\"\u001b[39m\u001b[33mModel4DSTEM\u001b[39m\u001b[33m\"\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m640\u001b[39m scan_to_real = rotate(\u001b[43mparams\u001b[49m\u001b[43m.\u001b[49m\u001b[43mscan_rotation\u001b[49m) @ scale(params.scan_pixel_pitch)\n\u001b[32m 641\u001b[39m real_to_scan = scale(\u001b[32m1\u001b[39m / params.scan_pixel_pitch) @ rotate(\n\u001b[32m 642\u001b[39m -params.scan_rotation)\n\u001b[32m 643\u001b[39m scan_y, scan_x = scan_to_real @ sym.Matrix(\n\u001b[32m 644\u001b[39m [\n\u001b[32m 645\u001b[39m scan_pos.y - params.scan_center.y,\n\u001b[32m 646\u001b[39m scan_pos.x - params.scan_center.x,\n\u001b[32m 647\u001b[39m ]\n\u001b[32m 648\u001b[39m )\n", + "\u001b[31mAttributeError\u001b[39m: 'Symbol' object has no attribute 'scan_rotation'" + ] + } + ], + "source": [ + "model = Model4DSTEM.build(params=p, scan_pos=PixelYX(0.0, 0.0))\n", + "create_scan_pixel_pitch_fn()(model, 2.5)" + ] + }, + { + "cell_type": "code", + "execution_count": 161, + "id": "64ae1ff3-f319-4892-85b1-a6dcb99a459f", + "metadata": {}, + "outputs": [], + "source": [ + "z, w = sym.symbols('z w')" + ] + }, + { + "cell_type": "code", + "execution_count": 162, + "id": "8d31bffa-5f10-417f-8643-c85c037b77e3", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAA0AAAAPCAYAAAA/I0V3AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAAQVJREFUeJyV0j1LHEAQxvHfnfcNLK3stbYWS0FQsVS/gAkHFgFBhikCdiraKXi1YKmY0jKCICgSUllKCEJa387i9uS83Pkyzezszn/mmd2tNJtNn7Vae5GZg5jGJEYxhDtcYA97EfEE1Y4Cc9jBGH5iAwcYwS72M7PyqhN+YwqH7YpFwQpOMYsZHFQ+MlMBv2M7Ir5U3wOK3Rf/0D1Tvy41LJTw+EMQ1rQu4ygifrwLZeZXLOMX5tv7faHMXMImrjAeEbdvQplZxxYuC3DTef4flJnfsI7zAvzpzql2Aatag59hIiL+9lLy8riZuYgGHou0fz3yryOi0fmNhosfQL1XB5yg8QxJ7lJzgqNRTgAAAABJRU5ErkJggg==", + "text/latex": [ + "$\\displaystyle 2$" + ], + "text/plain": [ + "2" + ] + }, + "execution_count": 162, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f = sym.lambdify(z, z+1)\n", + "f(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 163, + "id": "f9d74901-5c88-4132-8f2f-fba4f0f7e8dc", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAA0AAAAQCAYAAADNo/U5AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAARJJREFUeJyV0T9LmwEUhfGfwc2/QzUgiCDVycGCkwjdpX4HFxc3wUWX24uIOlb6DcTFybFQx+KokLEO4laRKA5iB0s65FViTDSe5XKHc8/huV21Ws171d24ZOYOZjCJD7jHBQ7xPSKqUGo6soIe/MQ37OMBX1HJzNEXSeiPiL/NdTJzE+tYw/KzpFaGQgfFnGhVr50WillpVe+xzip6MaAOZq4wbLc1YRXlhv0HFiPiCrpe+1NmljFbJPThS0ScvGpqMI/hN84iYqojU2E8xTSGOqUHI8X89wQiMydxGRG3TQklbGAYxxFx00hvHluZ+QvnqKoT/Ixx/MESz5Ef4aP6Tz5hEHfqAPawGxHXvIG8nf4D4URVGwd034wAAAAASUVORK5CYII=", + "text/latex": [ + "$\\displaystyle 3$" + ], + "text/plain": [ + "3" + ] + }, + "execution_count": 163, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "g = sym.lambdify([z, w], z+w)\n", + "g(1,2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6712813d-615f-4add-9c87-68d13e2debe5", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 164, + "id": "70e2c00c-a821-43de-9380-00da6d7c0798", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Array(-0.41614684, dtype=float64, weak_type=True)" + ] + }, + "execution_count": 164, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "jnp.cos(f(1))" + ] + }, + { + "cell_type": "code", + "execution_count": 165, + "id": "a851215e-9c9a-4aa2-bae8-d57e60d63135", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'Symbol' object has no attribute 'scan_rotation'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[165]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m model = \u001b[43mModel4DSTEM\u001b[49m\u001b[43m.\u001b[49m\u001b[43mbuild\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparams\u001b[49m\u001b[43m=\u001b[49m\u001b[43mp\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mscan_pos\u001b[49m\u001b[43m=\u001b[49m\u001b[43mPixelYX\u001b[49m\u001b[43m(\u001b[49m\u001b[32;43m0.0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[32;43m0.0\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 2\u001b[39m parameters = Parameters4DSTEM(overfocus=p.overfocus,\n\u001b[32m 3\u001b[39m scan_pixel_pitch=p.scan_pixel_pitch,\n\u001b[32m 4\u001b[39m scan_center=p.scan_center,\n\u001b[32m (...)\u001b[39m\u001b[32m 11\u001b[39m descan_error=DescanError(),\n\u001b[32m 12\u001b[39m xp=np)\n\u001b[32m 13\u001b[39m model_updated = Model4DSTEM.build(params=parameters, scan_pos=model.scan_pos)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/Microscope-Calibration/src/microscope_calibration/common/model.py:640\u001b[39m, in \u001b[36mModel4DSTEM.build\u001b[39m\u001b[34m(cls, params, scan_pos, specimen)\u001b[39m\n\u001b[32m 633\u001b[39m \u001b[38;5;129m@classmethod\u001b[39m\n\u001b[32m 634\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mbuild\u001b[39m(\n\u001b[32m 635\u001b[39m \u001b[38;5;28mcls\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 638\u001b[39m specimen: Optional[Component] = \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[32m 639\u001b[39m ) -> \u001b[33m\"\u001b[39m\u001b[33mModel4DSTEM\u001b[39m\u001b[33m\"\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m640\u001b[39m scan_to_real = rotate(\u001b[43mparams\u001b[49m\u001b[43m.\u001b[49m\u001b[43mscan_rotation\u001b[49m) @ scale(params.scan_pixel_pitch)\n\u001b[32m 641\u001b[39m real_to_scan = scale(\u001b[32m1\u001b[39m / params.scan_pixel_pitch) @ rotate(\n\u001b[32m 642\u001b[39m -params.scan_rotation)\n\u001b[32m 643\u001b[39m scan_y, scan_x = scan_to_real @ sym.Matrix(\n\u001b[32m 644\u001b[39m [\n\u001b[32m 645\u001b[39m scan_pos.y - params.scan_center.y,\n\u001b[32m 646\u001b[39m scan_pos.x - params.scan_center.x,\n\u001b[32m 647\u001b[39m ]\n\u001b[32m 648\u001b[39m )\n", + "\u001b[31mAttributeError\u001b[39m: 'Symbol' object has no attribute 'scan_rotation'" + ] + } + ], + "source": [ + "model = Model4DSTEM.build(params=p, scan_pos=PixelYX(0.0, 0.0))\n", + "parameters = Parameters4DSTEM(overfocus=p.overfocus,\n", + " scan_pixel_pitch=p.scan_pixel_pitch,\n", + " scan_center=p.scan_center,\n", + " scan_rotation=p.scan_rotation,\n", + " camera_length=p.camera_length,\n", + " detector_pixel_pitch=p.detector_pixel_pitch,\n", + " detector_center=p.detector_center,\n", + " semiconv=p.semiconv, # radian\n", + " flip_factor=p.flip_factor,\n", + " descan_error=DescanError(),\n", + " xp=np)\n", + "model_updated = Model4DSTEM.build(params=parameters, scan_pos=model.scan_pos)" + ] + }, + { + "cell_type": "code", + "execution_count": 166, + "id": "e701106a-b289-412b-a97a-05f599a917cb", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAA0AAAAQCAYAAADNo/U5AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAARBJREFUeJyV0rFKnFEQhuHHxSqLGAikUhAkegcxYCXC4k3Yi0LAOsUwgq2FYm4gF5DY2qRSvAPdFcHOSlTElK6FZ+X46wad5oOZeWeGc76Rfr/vvTHaTGTmBDawhE+4wB9kRFzBSL0pM6dxiM/Ywwm+YgFdzEfEZXPTzwJ8j4idatgW1rGJlVZjSwfn2G0MC9xhOTPbraqwUHQ/Iu6fERG3OMAHfKuh2aK95uOUOC06U0PjRW+GQIP8x9aQhv9GDQ0mjb/WWOWva6hbdGYI9KVor4b+Fu1k5rOzM3MM8/iHo6diRJxhH1NYa2xJtPErIu6ajlj1aKPtzFzEMeY8/mEPP2h4r5wy6aVhfxtm2LfGAyqWUdwO0gBiAAAAAElFTkSuQmCC", + "text/latex": [ + "$\\displaystyle 0$" + ], + "text/plain": [ + "0" + ] + }, + "execution_count": 166, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s = {'x': 0, 'y': 1}\n", + "s['x']" + ] + }, + { + "cell_type": "code", + "execution_count": 167, + "id": "c30a799d-9fc9-445a-ad8d-1f46a333fa0c", + "metadata": {}, + "outputs": [], + "source": [ + "s['x'] = 1" + ] + }, + { + "cell_type": "code", + "execution_count": 168, + "id": "c82c3adf-18da-4759-9129-51db7cc94e3c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'x': 1, 'y': 1}" + ] + }, + "execution_count": 168, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s" + ] + }, + { + "cell_type": "code", + "execution_count": 169, + "id": "486adde0-b3c3-4c92-a491-e591b00ccfe1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "DescanError(pxo_pxi=1, pxo_pyi=2, pyo_pxi=0.0, pyo_pyi=0.0, sxo_pxi=0.0, sxo_pyi=0.0, syo_pxi=0.0, syo_pyi=0.0, offpxi=0.0, offpyi=0.0, offsxi=0.0, offsyi=0.0)" + ] + }, + "execution_count": 169, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "DescanError(1,2)" + ] + }, + { + "cell_type": "code", + "execution_count": 170, + "id": "1dbc3efc-199d-4870-8a03-1f1347382f25", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAFgAAAAUCAYAAAAJD/ojAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAAYNJREFUeJztmLFKw0Ach7+Kg4vi4OAmOIiL2tVB9BE6OArq6Cg4Cr/+H0CfoYtPIM7Wx3BTcLPgoHRRqIOXGkNakybnUbkPSq5J777Lj8uR/huDwYCIP2ZCT+C/M5s0zGwPuE1du5e0/tcTmkbMbAl4Tp+T1IBUwCnugC7QywyyD+wCTWALmAeuJB3UPuPA3gmcfcBc+whYSS7kBdyV1M45f+5kb8AT8FerO4S3lFNSH2jDcCcYBlxmDz4F1oAF4KREv6qE8NbmzFvBuUga7s9mNu6ntRLCW6czvkV4JgbsmRiwZ2LAnokBeyYG7JkYsGcKvwdPgpl1gEPgWFLHpyukcxyFAzazFtByX5fdcdvdEEBP0lmmW/KEfEw4v0m8IZwjKbOCm3ytjDSr7gPwCGSlG8ArcFPCU9UbwjmSMn+V27iCRhHMbBHYBC4kvRTtV8UbwvkbeQHLzET1evAO8A5cVhhjGpy59eCEdMAPfNc0IVMPLouka2CuyhjT4HSk68E/+ATX6I48365TFQAAAABJRU5ErkJggg==", + "text/latex": [ + "$\\displaystyle \\left[ 1, \\ 1, \\ 1\\right]$" + ], + "text/plain": [ + "[1, 1, 1]" + ] + }, + "execution_count": 170, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x, y, z = sym.symbols('x y z')\n", + "solutions = sym.solve([1-x, 1-y, 1-z], [x, y, z])\n", + "[solutions[arg] for arg in solutions.keys()]" + ] + }, + { + "cell_type": "code", + "execution_count": 171, + "id": "6058ba73-7db8-4b8a-8685-7ee5d8de945a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}1 & 0\\\\0 & 1\\end{matrix}\\right]$" + ], + "text/plain": [ + "⎡1 0⎤\n", + "⎢ ⎥\n", + "⎣0 1⎦" + ] + }, + "execution_count": 171, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sym.eye(2)" + ] + }, + { + "cell_type": "code", + "execution_count": 172, + "id": "3ba56bb0-d573-4ee0-9624-8b7c835942d5", + "metadata": {}, + "outputs": [], + "source": [ + "def scale(factor):\n", + " return sym.eye(2) * factor" + ] + }, + { + "cell_type": "code", + "execution_count": 173, + "id": "94e290b9-3c27-4c77-bb7a-e8eee6ffe10e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}3 & 0\\\\0 & 3\\end{matrix}\\right]$" + ], + "text/plain": [ + "⎡3 0⎤\n", + "⎢ ⎥\n", + "⎣0 3⎦" + ] + }, + "execution_count": 173, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scale(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 174, + "id": "d5b48523-4e70-412f-93a4-29c4382dde05", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}x & 0\\\\0 & x\\end{matrix}\\right]$" + ], + "text/plain": [ + "⎡x 0⎤\n", + "⎢ ⎥\n", + "⎣0 x⎦" + ] + }, + "execution_count": 174, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scale(x)" + ] + }, + { + "cell_type": "code", + "execution_count": 175, + "id": "245dbba2-6f48-49e7-9f78-478b244ae1d3", + "metadata": {}, + "outputs": [], + "source": [ + "def rotate(radians, xp):\n", + " return xp.array(\n", + " [(xp.cos(radians), xp.sin(radians)), (-xp.sin(radians), xp.cos(radians))]\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 176, + "id": "17e03456-73c4-4097-b5df-cf611a09b438", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[-0.41614684, 0.90929743],\n", + " [-0.90929743, -0.41614684]])" + ] + }, + "execution_count": 176, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rotate(2, np)" + ] + }, + { + "cell_type": "code", + "execution_count": 177, + "id": "a5fd45d4-331a-4bc4-8547-fd19861c49d4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}\\cos{\\left(x \\right)} & \\sin{\\left(x \\right)}\\\\- \\sin{\\left(x \\right)} & \\cos{\\left(x \\right)}\\end{matrix}\\right]$" + ], + "text/plain": [ + "⎡cos(x) sin(x)⎤\n", + "⎢ ⎥\n", + "⎣-sin(x) cos(x)⎦" + ] + }, + "execution_count": 177, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sym.rot_givens(0,1,x, dim=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 178, + "id": "c9d6bccd-e338-48bd-89d6-aad87816814f", + "metadata": {}, + "outputs": [], + "source": [ + "def flip_y(xp, flip_factor):\n", + " return xp.array([(flip_factor, 0), (0, 1)], dtype=xp.float64)" + ] + }, + { + "cell_type": "code", + "execution_count": 179, + "id": "043f5d3f-fe48-4789-a833-2d4820700abe", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[2., 0.],\n", + " [0., 1.]])" + ] + }, + "execution_count": 179, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "flip_y(np, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 180, + "id": "41220ef7-747b-477d-8c5d-7dfca94f3260", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}\\frac{\\sqrt{2}}{2} & - \\frac{\\sqrt{2}}{2}\\\\\\frac{\\sqrt{2}}{2} & \\frac{\\sqrt{2}}{2}\\end{matrix}\\right]$" + ], + "text/plain": [ + "⎡√2 -√2 ⎤\n", + "⎢── ────⎥\n", + "⎢2 2 ⎥\n", + "⎢ ⎥\n", + "⎢√2 √2 ⎥\n", + "⎢── ── ⎥\n", + "⎣2 2 ⎦" + ] + }, + "execution_count": 180, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sym.rot_givens(1,0,sym.pi/4, dim=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 181, + "id": "a8d566d9-e63c-4491-88b0-9a11f5eb076c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}-1 & 0\\\\0 & 1\\end{matrix}\\right]$" + ], + "text/plain": [ + "⎡-1 0⎤\n", + "⎢ ⎥\n", + "⎣0 1⎦" + ] + }, + "execution_count": 181, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sym.Matrix([[-1, 0], [0, 1]])" + ] + }, + { + "cell_type": "code", + "execution_count": 182, + "id": "a113fa41-a517-4ab1-8f8c-44bdc72bf7bd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sympy.core.numbers.Float" + ] + }, + "execution_count": 182, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(sym.S(1.0))" + ] + }, + { + "cell_type": "code", + "execution_count": 183, + "id": "0eede1ec-689b-4b2e-b390-b8e1daae3bf8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Array([1, 4, 7], dtype=int64)" + ] + }, + "execution_count": 183, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f = jnp.array([[1,2, 3],[4,5,6],[7,8,9]])\n", + "f[:,0]" + ] + }, + { + "cell_type": "code", + "execution_count": 184, + "id": "b04c5dd0-a56e-41cf-a451-dfad03f1a22f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Array(8.1240384, dtype=float64)" + ] + }, + "execution_count": 184, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "jnp.linalg.norm(f[:,0])" + ] + }, + { + "cell_type": "code", + "execution_count": 185, + "id": "4d1b8d49-1f1e-41c3-be42-898cba536ded", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMQAAAAQCAYAAABJCdBSAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAACCpJREFUeJztmnuwV1UVxz83r2KhkoFKTxVGSiy89kDJRAiD8qJB0dQ4kDoD5igDyEOJoi+rxgQLFF+F2HhNmRozJBEiHjIRajqjNOpcUuJlVpJA2FUgBOmPtQ/sezjnd3/n3Mv9635nfrN/Z++199prnb33epxdc/DgQTrQgQ44atMVZlYPjAd6A12BfwHPAXMkPV3twGY2ArgYqAPOBU4EFkgamUHbFRgO1AOfAj4M7ANeBO4H7pf0bhU8RwIPhscxku7LofsI8EPgy5GMiwCT9J8U7Szgs0AvoBuwB9ga6O+StCOHR6v0eDRkKcsj0A4CxgL9gJOBHfj7mStpaYq2sM7MbAtweg77bZK6R7Q1wOjwOweoAdYD9wH3VlorLcnxngxBHgc+DSwD5gLPA18FngwKrBbfD4zrgH+0QPsNYD5wPvAMcDvwW+CTQciHgxJyYWYfBe4C3mqBrie+MK8GngVuAzbhi/fpsDlj3AB0Blbg+lgA7AdmAC8EvmkerdLjUZSlMI9AeyuwEl/kjwGzgSXAKcCAjC6FdRbwJmAZv5+m6B4C7gXOAH6Fr5H3AT8DGlojR21E3B2YDGwD+kj6d9Q2EHgCP4keymOYwg3Aa8DfcEuxugLtK8DlwJJ4d5vZNPxFfx34Gr5JsgStwS3JDmBhkCMP9wCnAuMk3RmNMSfM+Wbg2oj+JEl7M3jeDEwDvgtcF9W3So9HWZbCPMxsDDAFeAC4RtK+VPuxGd0K6SzCLkkz8uYSxhgOXAFsBvpK2h7qj8PXxygzWyRpYRk5Ygtxenh+Jn6JAJJWA034TqoKklZL2iCpxSBF0hOSFqdNnaTXgZ+HxwEVhhgHfBE/Kd/OIwon6mBgC3B3ehqh7ygz6xzN4YgXG/BwKM9K1bdWj0dNlhI8OuGb6lUyFhGApHcy6orqrAiGh3J2shkCz33A9PA4Nu5QRI54Q2zA/fa+ZtYtNWB/PAZY2QpByiJR+P6sRjM7G5iJ+4BrWhhrYCiXZ2y+JuBJ3PReUMW8LgvlC6n60npsD1kK8vgSvnkXAu+aWb2Z3WRm482sXwt9s5CnswSdzGykmU0LPAaa2TEpmiSW2JTRP6m7KFiMwnIccpkk7TSzm4A5QKOZLcJNak/cnVkBfKeStG0NM6sFvh0el+W0P4jv/GlVDPnxUL6S074BP3V7AatSvCYDJwBdcB/0C/iLnRnTldVje8hSgsfnQrkXWIfHdPGc1wAjJL2R1blanUXozuEgP8FmM7ta0h/Dc2IVzszo3yOUteH/X4vK0SyolnQ77qvXAmOAqXjA+3egIe0CtANm4pNfKukPGe0/AM4DrpK0p4rxuoTyzZz2pP79GW2TcVdkAv5ilwGDsxZDST22hyxFeZwayinAQeAi3ML1AZYD/YHfVOhftc7wmGYQvik649nGeXjg/HszOzfQLQnlRDP7QNI5xAAWjXdyGTmapV3N7Ebgx8AdeAbideATwC3AAjOrk3RjBQW0GcxsHDAJ3+WjMtrPx0+52UXSwWWRpP3M7DTg8/hmXWdmQyU9n5pbIT22hywleSQH5n7gcklbwvOLIbh9GbjYzPpljVlEZ5Is1f0l4FozewtfBzPw+OHX+HoYglvg3+En/yXAB3Hr9zEgdiOrluOQhTCzAcAs4DFJEyVtkrQ7THw4njqdZGaJWTpqMLOxeLquERgoaWeqvRb4Je4uTD9yhFwkp2aXnPakflfeAJK2SXoUd0e6hnnEcxtAAT22hyyt4LErlOuiRQSApN1AYrX7VhqkJZ21gCSp0j+MdQCPRaYCbwBXht8GfNM1BfrYClctR2whhobyiPSopN1m9iz+Qs8jO6BpE5jZBDyf/hIwKMe9OAH3jQH2mqUPFwDmm9l8PHicEOpeDmWvrA4czn7k+eWHIGmrmTUCdWbWLcp4FNVje8jSWh67cngkH/7em9PeDBV0VgmJexVn/t7BD51ZMaGZHY/LvV3S5qipajniGKJTKPNSgkn9ESmrtkIIRm8D/oJbhryY5X/AL3J+6wLN2vAcm/JkkQ42s/RHyROBC4HdwJ+rnPKHQnkgqiuqx/aQpSyPVbjP3TvNIyAJTjdntOUhS2eVkGTJqjmEvwUch3+si1G1HLGF+BOev73GzOZJOvR12cy+git4L/BUVN8TOBbYmJWPLgIzm45/sHoOD7x25tGGgHB0zjgz8NP3gfRVBEkbzWw5brqvB+6Mu+Kn0DxJb4exeuHXBpoFrkGpP8KDtadSVyQK6bG9ZCnJY6uZLcazY+PxwyrpNxj343cRZQDL6Cykgl+N5prUn4HHYBB9yDSzkyT9N0VbB/wEP+3Tmb+q5Yg3xCN4fvwSYL2ZPYoHg2fjbkANMDV1D2UV/iHqTPwDUTzBYcCw8JjkjvuZWUP4v13S5EB7Jb4ZDuALalyGWd8iqSFdWQLX4YvxDvN7LevxKyMDcffiexHtpcAtZrYWPwV3AKfhX9574PoZkxq/jB7bQ5ayuB7fMHPM72etw9/3MPx9jU4t/jI6+yYeV63B7zw14WnqeuB4YCnNr2+sMLM9uFvdhOu2Hr8zdZmkf5aV45D5CB93LsU/+Tfifu4k3GQtBYZImltZd81Qx+GAZ0io6xHVjYhok5zyMXiKThm/qwrwzoWkjXhOvAFfPJNw5c8FLkgt1JW4G3EKnkadgl8j2YmfwudIakyN39Z6bCtZyvJ4DfgMflKfhZ+wA4DFwIWS0tdpCusMd/8eD3O/ApiIb6C1+FoZmvq6/AieNh0ZaPvgd5t6R98rSslR03H9uwMdOIz/A++GmVmdh5sHAAAAAElFTkSuQmCC", + "text/latex": [ + "$\\displaystyle 8.12403840463596$" + ], + "text/plain": [ + "np.float64(8.12403840463596)" + ] + }, + "execution_count": 185, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.sqrt(1+16+49)" + ] + }, + { + "cell_type": "code", + "execution_count": 186, + "id": "c366f93e-1a87-4974-868b-5b6e20dfbce4", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle 8.12403840463596$" + ], + "text/plain": [ + "8.12403840463596" + ] + }, + "execution_count": 186, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "M = sym.Matrix([1,4,7])\n", + "M.norm().evalf()" + ] + }, + { + "cell_type": "code", + "execution_count": 187, + "id": "ec9e4f46-9627-4390-902e-c1a316d71d45", + "metadata": {}, + "outputs": [], + "source": [ + "def scale_rotate_flip_sympy(mat: sym.Matrix):\n", + " \"\"\"\n", + " Deconstruct a matrix generated with scale() @ rotate() @ flip_y()\n", + " into the individual parameters\n", + " \"\"\"\n", + " scale_y = mat[:, 0].norm()\n", + " scale_x = mat[:, 1].norm()\n", + " \n", + " if not scale_x.equals(scale_y):\n", + " raise ValueError(f\"y scale {scale_y} and x scale {scale_x} are different.\")\n", + "\n", + " scan_rot_flip = mat / scale_y\n", + "\n", + " # 2D cross product\n", + " flip_factor = (\n", + " scan_rot_flip[0, 0] * scan_rot_flip[1, 1]\n", + " - scan_rot_flip[0, 1] * scan_rot_flip[1, 0]\n", + " )\n", + " # undo flip_y\n", + " rot = scan_rot_flip.copy()\n", + " rot[:, 0] = rot[:, 0] * flip_factor\n", + " rot = sym.simplify(rot)\n", + "\n", + " angle1 = sym.atan2(-rot[1, 0], rot[0, 0])\n", + " angle2 = sym.atan2(rot[0, 1], rot[1, 1])\n", + "\n", + " # So far not reached in tests since inconsistencies are caught as shear before\n", + " if not sym.Matrix([sym.sin(angle1), sym.cos(angle1)]).equals(sym.Matrix([sym.sin(angle2), sym.cos(angle2)])):\n", + " raise ValueError(\n", + " f\"Rotation angle 1 {angle1} and rotation angle 2 {angle2} are inconsistent.\"\n", + " )\n", + " \n", + " return (scale_y, angle1, flip_factor)" + ] + }, + { + "cell_type": "code", + "execution_count": 188, + "id": "0d65262b-c782-415b-b6ca-e8a89f8fec09", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAACsAAAAZCAYAAACo79dmAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAAmdJREFUeJzV1z2IXFUYxvFfFkMQsdgisQsoBCEGQWKU4EcW1IhsEEU7W7VRsEonPLxgIQGDgRRpU6UQG12DEUXERcVmhWAhFipJoUUwgh+LJFmLewcn49zJXGaG4NOcC+c89/zPe977nnO3bW1t6aOq6meYo7b1ga2q/TiQ5NTikLq11HP8Kj5YBMg06gu7O8mFhZBMoalhq+oO/LxAlhuqT2SfwtlFgUyjPrAP4qtFgUyjqWCrajuuJrm2YJ6JumXKcQ9jfdKAqnoMr+IglnEJ53Eiydj06euZNg2exLkJoMfwMe7He3hLU+J2YmVenusiW1VLWEpyZWTccpJfOyZ9CUdxGi8n+Xukf/s8PAydYFW1jDM4k+T0kPEuPJ/k2JhJd+AC/sKe0Uk7FtfbM9BwZO/BLziiWfFAR3SfWk9otu1tXKuqVezDJr5O8uWcPBjK2STreAOHR7Zhb5JvO/wH2nYTG1jDmy3IF1X1WVXtnIPnetgW+HvNKfUoVNVt+KNrpdjVtkexhUdwO+7FR+173pmD57+wrdY0Ww+Pa77YLg38V/B0kvUkvyc5j2dxEYeq6uCMnomwq+3zCj6dAHu5bTeS/DjckeRP/5a7B2b0dMJ+jl1VdTd2JNmcAPvdCMCoBuXu1hk9GAPb1thzeB3fdGI2+kSTd3vbGj2qfW37w4ye8bCt1vCCG9yykvyE97Ebrw33VdVhzcl3GR/O4hmo625wVpNTFyfBtnoF9+F4WzM3cCeewVW8mOS3OXjGRzbJJTw3Bah2QftxEns00VrRRO+hJO/Ow0PPH8abrb7/YDdV/yvYfwBojRO0KMttXAAAAABJRU5ErkJggg==", + "text/latex": [ + "$\\displaystyle \\sqrt{66}$" + ], + "text/plain": [ + "√66" + ] + }, + "execution_count": 188, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A = sym.Matrix([[1,2, 3],[4,5,6],[7,8,9]])\n", + "A[:,0].norm()" + ] + }, + { + "cell_type": "code", + "execution_count": 189, + "id": "78e51807-1f90-4412-8442-3567a194dbcf", + "metadata": {}, + "outputs": [], + "source": [ + "B = sym.Matrix([[1,2, 3],[4,5,6],[7,8,9]])" + ] + }, + { + "cell_type": "code", + "execution_count": 190, + "id": "9a9ba3b4-4a79-44dd-b73b-68c01c789148", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 190, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A.equals(B)" + ] + }, + { + "cell_type": "code", + "execution_count": 191, + "id": "4f88cd54-3edd-45c4-91be-660785b97e0b", + "metadata": {}, + "outputs": [], + "source": [ + "a, b, c, d = sym.symbols('a b c d')\n", + "M = sym.Matrix([[a,b],[c,d]])" + ] + }, + { + "cell_type": "code", + "execution_count": 192, + "id": "37b8c3b2-e794-405b-9c31-fa0dd48e016a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}-3 & 6 & -3\\end{matrix}\\right]$" + ], + "text/plain": [ + "[-3 6 -3]" + ] + }, + "execution_count": 192, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A.row(0).cross(A.row(1))" + ] + }, + { + "cell_type": "code", + "execution_count": 193, + "id": "95e90036-8e40-4076-ac46-6bfcdda1d7d2", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAABoAAAAQCAYAAAAI0W+oAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAAepJREFUeJy91UuojmEQB/DfkZWDo47LSZTIsTg7KYqSlKRsWCsWShQJCyzGKGFDrjslsrFhKZds3FJSFuTIbYXkllwWOBbv89V7vsthdWbz75l3Zv4z884zT9fQ0JDRkLH1Q2YexgL0YzJ+4DUu42REfGgOkJkzsB8r0Ys3xT4j4lPDbkyT33Z04xqO4QJ+YR8eZebMJpI5eIANuI+jeIFtuJuZvW0rwsSI+Nkm6wPYg93YXPt0GlOxNSJO1OyPlKQPYFNLRe1IilwsOLepmhV4hVNN9oFvWJeZ3S1EI8jqgo9qumUFr0bEn6aEv+I2xmERra1rZLsT49GjGo4lheRQzWxewcEOyT1TVdyPG22JsBPTaucrWB8R72u6noJfOsRo6CfRoXUR0RcRXejDGszGw8yc3yHoP2XEfxQR7yLikqoFvTjXJuOeFsfh+s//JKoRvsZjDGTm5KJ+WrC/g1tjQgf/m6jI9IK/C94suCIzh8XJzAlYjO+4N4woM/szs6UNmTmmXNipuNNYKxHxHFcxC1ua3VQb5nxEfGP4eK/Cwcy8hZf4oJq8papheIuNTQE34w6OZ+ZyPMFC1R0bxN6GYb3k6ziDKapJ24W1+FgyHIiIx3WWUtUCnC0EOzBHtScX1Zdw12g9E38BD9GWaAM/xfAAAAAASUVORK5CYII=", + "text/latex": [ + "$\\displaystyle 30$" + ], + "text/plain": [ + "30" + ] + }, + "execution_count": 193, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "B.row(0).dot(B.col(0))" + ] + }, + { + "cell_type": "code", + "execution_count": 194, + "id": "2bdabb68-814d-4259-b789-01f1a6615b67", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAEkAAAASCAYAAAAXOvPoAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAA6BJREFUeJzt2FuIVlUUB/DfjFfKYjK0xhwqA3swrZSKqAdTKkJ8yIdIKS0Mwq4mkUGX1aoM6iGk0ocgKyOohy50EyTLih4KTTMj6ALaxbSwHiqK1KaHfT49fs2nVjMOhX/4WN/ee+21915nrf9e57R1d3c7hH2jvb838F/AwL40npld+BIvRMSMvlyrad15WIq5EbHs39rr60iaVMkP+nidVuuu7Q1jfe2kiZXslc3+DUzCb/i4N4z97yIpM4dgHDZExM7esPkXTsrMWZiGM9CJnfgcSyPi8R70B+JazMVYbFP44AElkrZExLbe2OwBYjwGYW1mnoY7MBlD8S4WRMTGniZm5mTMwzkYgR+wob1J6Qgsxxi8g0fwPE7Essxc2KQ/GK9hMXbhYazCXXgUx+qfVIPjFad04zElms/HG5nZUZ+QmW2ZuRhvYgpex4NVe1JzJHVjdERsbTJyOz7Flbi/NrSkWvhO3BsR3ZX+E3ir0jnYpN3gwbNwbkSsawxk5nJcjmtwX23OItyI5zAnIn6pzRm2VyRFxM/NDqr6v8UWDK9NPhNX4ZWIuKfhoEr/bXxSNfsrkm6pO6jCkkqOb3Rk5kQsxBrMqjuI4pO9Iikzj8J1CiedjCPtTe71Ra+v5KIWm91eyZaRlJmblLQ4UDwdEZftw94gxQGbFdpoRiMAhtb6blLOeGtE/N6T3d1OyswJWIlj8D6eUQ66U+Gk2fiwNveCavy9Fnseg+8i4ptWh8IXylV9oNiyn/FTMBgvt7jZGg9kc63vQvyo8E+PqEfSU+jAeRGxuq6UmXdXf9dU7aEYiXX1NKvpT8QorGh5HETE1H2N/wM0Um1Ti/GLK7mS3ecYgfUR8Ucro+2VchcmYHUPDupQCJs9/LKr+o1sYfe2SvZXpT28eSAzO3E1PlM5CW2VbHUO7OGbRsiPqfK6YfhoPIvRStqth4jYUS12XGZOb9rMQjTe0w42aTdutpmZeXhtT8MUjhqCGxqpGBG/YiNGZeYlzcYyc2xmDmhrfCrJzFVKjbBOqRM6cZGSqzOwMSJOrRmYjSexQ+GvrUrRNh7fowsnREQ9//sMVVH7k3KrHoYBeFFxzAwl/edHxENN86bhJSWqViivMh04XSmHOus316UKL3UpVedJWKDUE+0qPmogIpZjPr7GTMzBV0q12o3tB8tBFcYpt9YaTMVHSnpdoRx8SrODICJeVR7uCpyt3HbTFTK/GdoOfXTbP/4E1y4xEJANoMEAAAAASUVORK5CYII=", + "text/latex": [ + "$\\displaystyle a d - b c$" + ], + "text/plain": [ + "a⋅d - b⋅c" + ] + }, + "execution_count": 194, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a*d - b*c" + ] + }, + { + "cell_type": "raw", + "id": "6e07972f-0cf3-4888-8f67-c39e5d8aad25", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": 195, + "id": "21338b3c-5877-4e22-92b2-a8d4f5da1505", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy.physics.vector import cross" + ] + }, + { + "cell_type": "code", + "execution_count": 196, + "id": "79add443-aefa-4faa-bf5e-6e116490fdfb", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAGAAAAAUCAYAAAByKzjvAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAABSpJREFUeJzt2GuMXVUVB/DfWAM0aoUUQw2xHSuUR5pYnoEYSsHWgiAyWEJIwILhERIeAQqUhGR1iSJ8qUCUEuOHklIClTpAiogQIDyEFsSiBgzKQxpeykMUhFTL+GGfOzlzem9n7syYkuI/udn3rL32/6yz1tlrrbN7BgYG/B9bD5/Y2gZ83PHJrW3A/wKZeQOOxBcj4r2PgD374QmcHhE/rc/1jDYFZWYvXsANEXHKGG0cN2TmAViLRRGxtDG3AIdiFr6Mz2BlRJw0DOcueAXXRcQ5lewonIe9MRmv4jdYGhGPtuHox0HYPSLebcm3xRT0ffwDy9rMXYazlQC83AXnNxVf/Rwy8yqswb74Ja7Bk5XeI5nZLqA/wBScWxduUwHIzBmYi1UR8X4blfMxA5NwVhfUfXgTD2bmFCzC69g7Ik6LiMURsQDz0YPvNgkiYh3+iDMzc9DvQ2pAZp6Cb2AffB7/xu+xLCJurOktQVSXCzNzYY3m1IhY3g1fpdurSmlYgisVZ34af8CSiFgzjKO+UznglnaTEXF/7X7DUA3qTcLhuCkiNmXmNOXFXRsRf23yZ+Y/8bkOdDcrzzYPd7P5DliGaXgQV1cLpmFFZl5e03tA2XbwFLL2Wz8KvjqmYR16sUJx5kzcnpmHdVjTwlxswmPD6HWDo7Ed+qvrP2EjDszMneuKmTlbqSv3duB6pBrntQTNLmhmRDzXIN0Od2FxZl4fES9HxAOZ+aJShNZHxJIONxwRX2PNHOVtz9qam5RcexHu1waZ+Skltz8zzp1PH97DryAi3srMS7AUT2fmbUp6+hKOwT04swPX49U4uyUYsgOazqpkG/FjJVhf7cbyUfL9Bd9rrLkbL+HALdxuV0xQupFxQWbugCNwV0R8ULPnahynPMPpWIzjsQHLm6mptu4dfICpLVmzBkzFJYpjpmJig2PXLh9gNHzrI2JTG/kGHLyF202uxre7sXEYzFNqUH9dmJkX4wpcix/hNeypdDorM3NWRFzcgfMt7NK6GAxAZk5Xcu9OeEjZcu8oObUXC7H9SC0fA9/fO1D+x5a7tlbXs8NIbRwBjlPy/Z0tQWbOwVXoj4gLarpPZmYfnsWFVXp9vg3nxJqtQ3bABcpbNNjF1G56ouKwbjDefMOhte0nb1FrhMjMCUoHd1+VOlo4uho3q0UR8a/MXKfUjX0wJABV+7mj0u1h6Bu1WzWubmPPoW1krTQxocMzdMs3VryKv2GPceKbrQSzvyFv7dpOrWZLvrHN3B5Km7y+JagH4MVqnFNfkZnzcVobsrcxoFZQGuiWb0yIiAGl3d05M3cbTn8E6MOHuL0hf6gaz8jMITUsM4/EV5RC++s2nAdV4+Duqaeg63AqfpaZtypnHzOVLmAVTqgzRcS7mbkWh2TmSiX3bcIdEfG7bvnGCavxLeWL9M/Nycw8FsdWl1Oq8eDMXF79fyMiFmVmT6X3aES83qC5Venz5+KZ6oznNeylpKceLI6IN9vY9zXFR4NBHdwBldMOUyJ3lPKpPkkpRNd3eOCTlQJ1hPJlfLlyPjJavrFitVILvt1hfpZSexYqQYLpNdmCSrY/vqA6+6kjIj7E15VjjaeVnXKh8nb/AvMj4prmusz8rBLUNRGxoSUf9WnoRxWZeanSIu4bEb8dJccVuBTTI+KF4fRHyHmO0rYeEhEPt+Tb1GFchR8qH22bHYh1gT48NY7On6gEdHXd+WyDAai+WE/GE9XxxGg49oqIWeNoVi9+opyiDsF/Aevi4sQWdIwUAAAAAElFTkSuQmCC", + "text/latex": [ + "$\\displaystyle \\operatorname{atan}{\\left(\\frac{1}{8} \\right)}$" + ], + "text/plain": [ + "atan(1/8)" + ] + }, + "execution_count": 196, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sym.atan2(1,8)" + ] + }, + { + "cell_type": "code", + "execution_count": 197, + "id": "9b500e1d-f840-4f00-9cca-fdd865087a15", + "metadata": {}, + "outputs": [], + "source": [ + "def scale_rotate_flip(mat: npt.ArrayLike, xp):\n", + " \"\"\"\n", + " Deconstruct a matrix generated with scale() @ rotate() @ flip_y()\n", + " into the individual parameters\n", + " \"\"\"\n", + " scale_y = xp.linalg.norm(mat[:, 0])\n", + " scale_x = xp.linalg.norm(mat[:, 1])\n", + " if not xp.allclose(scale_y, scale_x):\n", + " raise ValueError(f\"y scale {scale_y} and x scale {scale_x} are different.\")\n", + "\n", + " scan_rot_flip = mat / scale_y\n", + " # 2D cross product\n", + " flip_factor = (\n", + " scan_rot_flip[0, 0] * scan_rot_flip[1, 1]\n", + " - scan_rot_flip[0, 1] * scan_rot_flip[1, 0]\n", + " )\n", + " # undo flip_y\n", + " rot = scan_rot_flip.copy()\n", + " if xp is jnp:\n", + " rot = rot.at[:, 0].set(rot[:, 0] * flip_factor)\n", + " else:\n", + " rot[:, 0] = rot[:, 0] * flip_factor\n", + "\n", + " angle1 = xp.arctan2(-rot[1, 0], rot[0, 0])\n", + " angle2 = xp.arctan2(rot[0, 1], rot[1, 1])\n", + "\n", + " # So far not reached in tests since inconsistencies are caught as shear before\n", + " if not xp.allclose(\n", + " xp.array((xp.sin(angle1), xp.cos(angle1))),\n", + " xp.array((xp.sin(angle2), xp.cos(angle2))),\n", + " ):\n", + " raise ValueError(\n", + " f\"Rotation angle 1 {angle1} and rotation angle 2 {angle2} are inconsistent.\"\n", + " )\n", + "\n", + " return (scale_y, angle1, flip_factor)" + ] + }, + { + "cell_type": "code", + "execution_count": 198, + "id": "6f07c9b9-a5a1-4de6-bd98-c65952cd48b1", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_163174/1257263358.py:11: RuntimeWarning: invalid value encountered in divide\n", + " scan_rot_flip = mat / scale_y\n" + ] + }, + { + "ename": "ValueError", + "evalue": "Rotation angle 1 nan and rotation angle 2 nan are inconsistent.", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[198]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mscale_rotate_flip\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnp\u001b[49m\u001b[43m.\u001b[49m\u001b[43marray\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m[\u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[197]\u001b[39m\u001b[32m, line 32\u001b[39m, in \u001b[36mscale_rotate_flip\u001b[39m\u001b[34m(mat, xp)\u001b[39m\n\u001b[32m 27\u001b[39m \u001b[38;5;66;03m# So far not reached in tests since inconsistencies are caught as shear before\u001b[39;00m\n\u001b[32m 28\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m xp.allclose(\n\u001b[32m 29\u001b[39m xp.array((xp.sin(angle1), xp.cos(angle1))),\n\u001b[32m 30\u001b[39m xp.array((xp.sin(angle2), xp.cos(angle2))),\n\u001b[32m 31\u001b[39m ):\n\u001b[32m---> \u001b[39m\u001b[32m32\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[32m 33\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mRotation angle 1 \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mangle1\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m and rotation angle 2 \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mangle2\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m are inconsistent.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 34\u001b[39m )\n\u001b[32m 36\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m (scale_y, angle1, flip_factor)\n", + "\u001b[31mValueError\u001b[39m: Rotation angle 1 nan and rotation angle 2 nan are inconsistent." + ] + } + ], + "source": [ + "scale_rotate_flip(np.array([[0,0],[0,0]]), np)" + ] + }, + { + "cell_type": "code", + "execution_count": 199, + "id": "525b53cd-a2b0-4934-91df-bfd1eeac63c2", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAfQAAAArCAYAAACOyXrEAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAEl5JREFUeJztnXvcXeOVx7+REDq0qKrbkIrSiSafN9SlLm2CuDTtiLRTtEIY1KV1ZwbtZ+WHUfRDMYa0PtpE4jLVpioJ4xaXoUNV55Woore4VGRGMejIEDJ/rGcnJ+c9+5y9z22f857n+/m8nyfvOc9lZb1772c/61lrPUNWrFhBHiStYWbv52oUiUQikUikJpKGmFm+iTkwLOdABwHLgDtytlsTeCdPm4hjZkOKliESiUQibeM0STea2ZK8DTNP6JIOBvY3syPzDgJMAE4ws2vqaBuJRCKRSK9wM3CTpMlm9mqehmtkqSRpB+Bs4IQ6hAOYCMyvs20kEolEIj2Bmf0J+A4wW1IuC23NCV3Sh4CfAMeb2dv1iciWZvZ8nW0jkUgkEukZzGw+8AZwTp52WUzuFwCPmNl/1COYpO2BX9fTNhKJRCLNR9JfA7OAjYHlwPlmdkuxUnU2BejsdGCRpJ+a2VNZGlRdoUsaCxwLfKsBoaK5PRKJRDqL5cApZjYK2Be4XNJfFSxTp9NWnQXT+7XAVVnb1DK5C7jdzH7XgFyfBn7eQPtIpKuRdIGk+4qWIxJJMLMlZtYf/v0y8AqwYaFCFUyt+7QgnV0GfFrSgVkqp5rcJY0CPo97qNdF2H9/y8zeq7ePsv7OBiYD2wH/BzwCnG1mTzaj/1bT7fJ3M5KGmdnygoYfC/QXNHakCRR8/bQUSTsCQ83shaJlKZjM92m7dGZmSyXdjO+l/6xW/Wor9FOBl4AFDcizH3BXA+3LGQdcDewG7IWbQO6R1C1vluPobvm7EklHAp8qUIQ+4D8LHD/SONMkfaBoIZpNePZcj2+t9jp9ZLhPC9DZTGBnSXvXqlhxhS5pOPB3wA31ZqwJHACc1UD71TCz/Up/lzQF+B9gd2Bus8ZpFd0ufzci6XBgAzN7pKDxNwY2A96TdC++BfUscKyZ/aIImToZSd8EvgxsDbyN+980EmHTLGYAMyQd0i2ZMmvpMjznbwUuMrOe3hbNep8WpLMHgBeAo4F7q1VMW6EfAHyIDEv8NEL83IZm9t/19pGB9fD/w2stHCMVScdLWiHpqDq7KFT+wY6knYGvAt8tUIy+UJ4OnIeb9V4EbpGUK1NjjzAMOB7YHjgU3/I7pUiBAIIf0QI86qdbSNVleD7PABaY2ayC5Osk+kKZep8WpbOwqP434G8lrVutbtoD5SDgfaCuULXATsBjDbTPwhX4nkcjcjbCjqF8vM72Rcs/AEkjgD8CM81sarHS1I+ktYAfAF9t0MrUKH3Au8BkM1sMIOksPJRzJPBMYZJ1IGY2reTX5yTNBz5RkDjlfB94VNJ8M3u4aGFqUUOXuwMHAwslTQqfTTGzRe2TsKPoo/Z9WqTO7gaOAb4A3JRWKW1C/yzwtJm9WWsUSTunmA4n0sAKP8O4lwF7AHs0y+muDnbEc9vnjrPvEPkHM6cCz5jZEwXLMRaYkzwkAolFZmj7xelcQpzvmcB4YHNgLWA4cEmRciWY2fuSzgemSxrbyU5ytXRpZg+RMVNoj1DzPi1YZw+GcjxVJvQBwknaHNgKqLq/J2ktSdOBB1Ni8cbSIkcgSd/FTUh7mdkf6uxjajCXj6uz/XDclLUw743dDPkj6YTr8UxyxG+2kD7gV2Wf7Qq8BTQSDjqokPRh3KK3CXAGsCfuyLiMzooQmIs/4A8vWpA0ukiXq9HoM7lB+ujg+9TMlgJL8b9lKpVW6LuE8tkaY/wN7mRxJL43c2vyhaRNgKVZTJ2SvoKv5ncCNsU9v38HXG1mP6xQ/wrc7DHezJ6u1X8LGQ2sCTwuqQ9PvjMOWBt4GDitUjhaEfJLmoqbasbiOn4XWARcY2azS+pNAyz8eoSkI0q6OdLMZuTpL9QdQTDhA9OAi4B9gHWBJ4FpZjavSf/VhGOA182s0Njv4BW9LSUr8bAPdzow28ziCYSrmIjfOwcnz41w/a1LB01CZrZC0kzgbEkzO9S61hW67BS66D59AthX0gZmVtHvqtKE/vFQLq7WczBlPiHpbvwCurXk688Bt9eSTtJ6uPv/Y8C/A/+Fp9U7EPiBpI3N7OKS+v8CTAEmAa+FFwfwWPe3ao3XZJL9863wCfwO4DrcO3ICsEDStmb2etKgQPmvwbcFHgSWAB/G/0azJG1nZkkmwPuB9YGT8Yvn1pI++uvor5TE6vMHPH3ihviLzc8k7dPkyfeoMtmLYgzui3J4SFjxCv5SsyV+DURW8Wd8wpkkaRHumHsO8CYdsEIq4yb8xXQC7qzUaXSTLjuBbrlPE2vuNqT4p1Wa0EeGcnHGQeYD39Tqh7LvQ7YYvRXAFiHrzkpCuMWz+Or/4pKvktPeyl33hf8B2skOodwF3wdfub0g6Xp84j4BuLCkTVHyf9LMfr/agO40dgfwj5Kmm9mfzOx+SYvxCb2/zKkmd39lbcbhq3GVtLkRfyCeCTRlQpc0BreenNKM/hqkD/g9cC7wI2AjXEe7mNkrBcrVidwOfA+35CzDj5C8Adi1YKfGAZjZ85Kewc3unTihd40uO4Q+uuM+TZ6pI8kxoW8VyqyHq8/Dk6XsgJuf1wSGZ1lxhjoD6pnZEkkvUZZWz8xyHSXXYpIV+lmlk3kgWYmPLv2wKPnLJ9/w2TvBYrAXsDduKWllf89RFvJjZndKeh7YOevYGZiIvygWHuNtZtOB6eHXOUXK0umEiebE8NMNLAAO68QMcl2oy0Lpovv0pVCOSKtQaUL/YCj/N8sIZvaCpIX4g/RxfNP+oSxtJW0AfD203S6MXeqo1xSnurDq3Crl6/sklX9WNWQrvLSMxiepShNhYnFYO5egLULSlsA/4BPtlsA6ZVU2b0N//Sn7jS/g2xSl/TeSIvczwG8L2IKJ9Bb9eIz3DnTAy2O30exnco/wRig/mFah0oSeeKxnmtAD8/C87+fhk/P3ajUIptG7gI/iN8TN+N7PcuBjuDmrWSFHl+N7w6X04Xv1Mxm4vdBfo79P4mEgc1PezpML9bnsIrYGSVvj+t0A91O4C89O9x7+pncEHs7S6v5eT+lyOQOjLcbhVp/HgCH4dXWPpFFm9moNEXcJckUirSSJPd6NOKHXw+U095ncCyTZElNPeKs0oSf5ivNO6GdL+iiwrZnV8pAHd4xaH/f2vr/0C0nnhX/+MocMqZjZ5eWfBU/tA4EZ5eNnIDG3L075/qBQNjOPfb2chjutrfRST5B0KD4BF9nfAOpNkStpI/xF48VGZYhEavB8KLctVIoupQXP5F5gWShzTejJinMo7vmXhUdxz8Cv484FVQlJD8YAd1aYzNfHneGg/gxsrSaZ0AccqiJpU+BrwG/pjAl9m1D+pMJ3n63wWWIWT0t6kre/ZpA1RW7i0Jnq/yGpp52C8vhxRF1V1dXL+PNxZJU6uRlMOm+Hz9Bg0hfU1NmaoXw3rUKlCT1Zma9drWGZEO9LugP3Vv5ChibJm8bWktY0s3dhZUKEG4Et8BeL/izjF0Di4X6opAvN7C8AIc/u9bjJ+aRmOMtImoGvegesiDOyOJTjKFndStoPT/Zfzmu4U9mWTeqvGWRNkbtRKFMzHHaYY2Wh1Lq2oq7SMbP3JL3JqmtuJY3cs1l0HhZEs/AQ3+XA+WZ2S55xmkEnyNFj+kqs539Jq1ApjV1SudzRqRbz8BeAB2pVDAe2LMBj3h+VdImkWXi+3DfwN9+nzGxZlW4KQZ6ofwzusPcO0C/pO5KuBJ7GHcVONbNmhbMkf6N6Xw6uxuW8RdLsoOvb8bCMH5dXDs5kjwJ7SrpBkkn6ZvB5yN1fo2hVitwvZkjikZiiOu666VAavbZ6nWV4vHc5rdbrcuAUMxsF7AtcnpKts9V0ihy16BQ5G5Wj5oReaYW+NJQfwRO9ZOVO4PocWXUOAS7Fkx4cjzuZnBbKL9Gk/fMWsD1uvfglHj/+z7iJHXwFeViT939G4yvO+fU0NrOFksbjIWMT8b/5E7gX+et4cpdypuAnlO2Pp6gdgu9LL6yzv7oIKXIPwf0ssqTITZzxGsrsNNjMeKWUrWgauragp3RViXeo7ADasF6rYWZLCNtKZvaypFfw7b/UB309SLoA2N3MxhcpR6MMIn2tF8rUebnShJ48ODcjx6EjZvYGOeIewyo9LR9yy019wRQ2o452T7C6fJObJNIAgj/BGODStFR/WTA/t3evlK8H6Nr8qMjUrZM8/ZkfdpD69zSzcZU+rzNFbuIFmtlrv8K4WwBTzWxmvX10A824tnpFV1UYDqwWddGsezYrknYEhprZCy3ofiwZtz2bIUe9z+Q8dLm+km3QVD+1ShN6UjlXbHKkJeyJb2NcVrQg7aSBFLnJm24j8f+fo4on/SCiGddWr+gqjXUYmBirbfespA1xn51jMtafASyukgGynD7gX5stR1HUI2dOnfXRWn2NCGWqtbLShL4wlKNyDhZpMmY2lw5JTtNm6k2RmyT0GeColINNg2lsUNOka6sndFUJSUPxPc2lpZ83654N6a+/DGyNW57mA8eb2dvh++H4eQUXBYtZU5G0MW6lfU/SvXjyp2eBY63kuOxWy5GVHtHXVvj/7Y9pFSpN6I/jzh6jK3wXibScBryrE+vSZvU0lrQ2q8z2WdsMFg/avOP1uq42wUM7W3XYyTDct+hF3Hl4Jn4+wbflJ4HNABaY2awWjd8XytOBU/GX5UtxZ9iRZra8TXJkZVDrS9Ia+Jz8aBIVVokBXu7Bqe0XrIq1jkS6guDHsYQquY5rsBf5D4kZLB60eel1XW0RymeaKlnAzKaZ2cNm9pyZ3YOvOD8Rvt4d9y+ZJKk//DR7AdaHbx1MNrMHzOwZ4Cx8HzeJvW+HHJnoAX2NwiMqqmbBrLRCB1fGZ0KqzacyDhiJdAIP4zHy9bAjZQfI1GIQedDmpdd1tX0oM51bkYdgQTgTGI/7Mq2FO+BdAmBmD1E55Li8n3PwY1MThgMrJJ1R8tkBZlZpkhgLzAlOrQmJk9/QPHK0mmbpK/RVr85ara+dQnl3tUppnd+EJxeZUOfgkUhR3A9sJD9AJi+lRwDnpss9aPPS67oai3u4ZzkwKDMhudZjuEn/DNzJ7lP4Nmh/zu6m4yvH5Oe2Cp+lhQf3Ab8q+2xX3AmwY85Ub7K+oH6d9dFafe2LbydUfYGsuEI3P0HtQTwe/IomCBOJtIufAlfiCX5+mLVRMH0tqlkxvX3PeBxHXQGe7Oi2Rl5qUpiIO9UdnPQt6Qjc3NqfpyPzg4xWhtWFzHavhrDUVCR9AM9RP7TksyH4/vDsHLlG2kHT9AX16azV+pK0Fh5Rcm2t6y3N5A7+UPyxpBFlZoRIpGMxs5eCl+kBpEzokj4GHG1m55Z8PAH4fkr9XvCgTRs76mrgGJvjLw1n1KhaD3/GJ6NJkhbh1/E5eKKadq2Mx+DZOg+XdB9+Tsc0fD94UptkyEov6GtvPKnMdbUqVrPn34o7fBzXBIEikXZyJfB5SWnnBo8Gpspz7yesWyXGPfGg3R7PnDcB96BN3sRn0D4P2vNwc++LuAftsBbLEXU1kMn44Ut5nQKzcDt+/PRM4Oe4x/YNeJbGdmXl68MjRs4FfoSHMq8D7GJmr7RJhqz0gr6Ow61Bv6lVMXWFbn7gynnAVZL+ycxSD7yIRDoJM5sn6TfAV/D9r/Lvb5M0Ed+XmhP24VJvvDJT73OSKnnQLpQ0KXw2xczqNklXoI9VHrSLASSdhWdyHIm/eLdEjqirihwNXGxmWU+jzEyYhE4kR9bNHH1PzVhvOqvumznNlqOZtFJfof+pGeq0TF+SRuDbCntkqV/N5I6Z3STpa8A3gAsbli4SaR+nAddJutYqH+oyF/gifgPuD1Q8TCd6HANRV6Wyjcf3bIuOu470BucAt5jZI1kqZ7moTwROlvSRhsSKRNqImT0APAj8fUqVe4FxIWHD1lbh8JfocbySqKtVTAOO6zDHsMggRNJ2uGP66VnbVF2hA5jZryWdj5++dVj94kUibedk4A5Jc8r3sszsbUlPAruRfsxl9Dgm6qqkr8OBx82sFXvnkchKwnV7DXCSmb2UtV0ms5OZXQUMkzSlTvkikbYT/D6OBi4NN0g5c4Fv4840lSj1oN1G0jeAiyjOg3ZXSdvg5t4tgW+1SQbocV1J2hb3IzizYSkjkdqchDv2zc7TKM8+0lTgS+EmiUS6AvOjV6+gcva4eXhKxYdTmveCB21Wel1Xk4CjUvwxIpGmIWlv/OU0s6k9YciKFdnvNXm+44+bWX/egSKRTkTSlBaGUA0qoq4ikdYTHC/vr+dF+P8BYmDGr+261bsAAAAASUVORK5CYII=", + "text/latex": [ + "$\\displaystyle \\left( \\sqrt{a^{2} + b^{2}}, \\ \\operatorname{atan}_{2}{\\left(\\frac{b}{\\sqrt{a^{2} + b^{2}}},\\frac{a}{\\sqrt{a^{2} + b^{2}}} \\right)}, \\ \\frac{a^{2}}{a^{2} + b^{2}} + \\frac{b^{2}}{a^{2} + b^{2}}\\right)$" + ], + "text/plain": [ + "⎛ _________ 2 2 ⎞\n", + "⎜ ╱ 2 2 ⎛ b a ⎞ a b ⎟\n", + "⎜╲╱ a + b , atan2⎜────────────, ────────────⎟, ─────── + ───────⎟\n", + "⎜ ⎜ _________ _________⎟ 2 2 2 2⎟\n", + "⎜ ⎜ ╱ 2 2 ╱ 2 2 ⎟ a + b a + b ⎟\n", + "⎝ ⎝╲╱ a + b ╲╱ a + b ⎠ ⎠" + ] + }, + "execution_count": 199, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sympy import init_printing\n", + "init_printing()\n", + "a, b, c, d = sym.symbols('a b c d', real=True)\n", + "scale_rotate_flip_sympy(sym.Matrix([[a,b],[-b, a]]))" + ] + }, + { + "cell_type": "code", + "execution_count": 200, + "id": "7fb0f130-947c-4a67-ad94-1c262e7a5855", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAHYAAAAUCAYAAABYm8lAAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAABFtJREFUeJztmVuIVlUUx3+WUpPVDChdMDSvQ2DQxVAo7aKORURWD9FDpVF2Q4uMmqGHf38rpTAtQacHH0bsoWwGC+ohHxIinbIr9WCZ1lCQUThmdpGspod9vpkzm/Pp5/nON5My/5cFa+19/nuvfVlr7TOsp6eHIZx4OGmwBzCE2mB4ltL2BuA6YLyk3wd2SEOoBLYvBT4C7pG0PrYPi69i25cBHwCPSloV2c4DlgHXAqOAvcDrgCXtr9EESgP8DmiUdCijTRcwDhgh6e/jgesIY6jYx7Y3AzOAyZJ+S9uyruJngF+B1ugjE4GPgYXADmA18A3wENBpe1TVszoyxgIP15hjMLh6kcPHK4BzgCXxt/otrO0pwBxgk6Q/o7brgLOAJZLmS2qWdE1C3kjYELXCfqAbaLY9uoY8A80V45h8LGkH8CVwr+1+axnH2LuAYcCraWWyk5qALmBt1EfAIuB220trFJP/AFYSJihgcSWdbC8AbgAuBs4FDgNfAK2SXi6Sq1pU4eNXgCeBucDbJWV8Fc8B/gHej/RXJ3KLpH/7MUoHgW3AaYT7vlZYC+wh7M7JFfZpJcTDd4EXCE4YB2y0/VTBXNUir4+3JXJuWtm7sLZHAhcBOzNOXWMid5UZ1NeJnHKUweeGpMNAMzACeLbCblMlTZO0QFKLpEXABOAdwlU7pkCuapHXxx8mclZamT6xY4CTCVlYjPpEHihDWtI3lLEXAkntQCdwk+0rKmi/J0P3F+FEDgdmF8VVAHL5WNIB4BAh4etFOsaWMq6alC0FYimwnRAHj3j12x4LPE5YwLFAXdQk88Tm4RpkdANnpxXpE1vKgk/N6FjaLfUZtrT+l7wjqxSSOoF2YLrtW8u1sz0B+AS4D/gRWA88DRjYkDQ7pQiuglCNj+voWz+g/4n9KZFZ9ehXiSwXQ0sJRrn4UDRagBuBFUmRnoVHCHNZKKktbbB9G3BngVxFIJePkzKnAfg2rU+f2L3Az/QF8TS2JrIprpdsnwFcTigT4my6JpC0m1Dzjad8OTIpkR0ZtisL5ioCeX3cSChRP0srez8gqYdQFoy2PSndKElCtgDnAw9GHzYwEtgYZ9O222z3JPVk0VhGuJaeAE7PsHcl8qpoTPOAuwvmqnqueX1MX+zfmlbGDxQdwC3APGB3ZHuAkEissT0b2AlMJ9RfuwiTjlHaOIW/qUrqtr0ceK5Mk3WEp7nXbLcDPwBTCW+wm4CKY2YFXFDMXPP4uInw9vBG1mBK6CDE2jvi3smOmga0JWRLgYnAi8AMSfsySC8EDgJvVTavY8Ya+k5mP0j6nOCQ7cD1wP3AmcDNwEtFciWoeq7H6mPb9cB84E1J36dtWX93WoDlwCWSPs07SNsNwD7geUmP5f3O8YDBmqvtxYQNN1PSe2lb1t+d1YTfVsuq5J1JeJtddbSGJwAGfK626wgZe0e8qJBxYpNOswjX2MqhH+3/T9i+gJAntEnqiu3/AZbEyz7kQ0SPAAAAAElFTkSuQmCC", + "text/latex": [ + "$\\displaystyle \\left( 0, \\ \\text{NaN}, \\ 0\\right)$" + ], + "text/plain": [ + "(0, nan, 0)" + ] + }, + "execution_count": 200, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scale_rotate_flip_sympy(sym.Matrix([[0,0],[0,0]]))" + ] + }, + { + "cell_type": "code", + "execution_count": 201, + "id": "32b2a0e3-4343-4460-a356-42024cb3bd14", + "metadata": {}, + "outputs": [], + "source": [ + "M = sym.Matrix([[a,b],[c,d]])" + ] + }, + { + "cell_type": "code", + "execution_count": 202, + "id": "5af2de94-9d95-4592-96b9-28e0804a4825", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAA0AAAASCAYAAACAa1QyAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAASVJREFUeJyd071KXVEQxfHfNX6hFmKnjQ+ggmhhkyJE8gBJZyEIKYIWIinsZJhXCMTOFD6BkC5FOjtNIB9YpPF29jYWwrG4+8TD0Qtep1mwmT+z1uy9O1VVGbSGBiYw3D7IzG0c4n1EfHnqpNWi54PYW8UN/j4JyswxLOBXRNz2g9qZljCC88xcxgFeYRyn+BgRf9r26jzzpanCEX7gDb5n5nR70krRNbyMiJ8N68fYxE6/SftNoNTnOsJ/KDNHSqYujj2sq6LjzUmLGMXXPpubL9ptQrW1y0cAeFv022PQTLs7M2fxAf/aUL25jcycbABTehnHsBsRt52qqmTmMK5xgQm8wElpfIc57EXEJ+6f0YLerZ9hHb+LnS29N/i6BqDznE94BwsCT4Q0M0kKAAAAAElFTkSuQmCC", + "text/latex": [ + "$\\displaystyle b$" + ], + "text/plain": [ + "b" + ] + }, + "execution_count": 202, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "M[0,1]" + ] + }, + { + "cell_type": "code", + "execution_count": 203, + "id": "390de6e8-1fb8-4f81-af5c-81bf1656d918", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAEIAAAAkCAYAAAA9+eyvAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAA2hJREFUeJztmV2IVVUYhp8zCpNTVwYDSQoWiQgTexK0jG4ExbqIwS68EbGi8FJIRFR4fTVRIkhQxMtB6yZBwh8QQkX8AUVqNLvIJgiMVBjGCweH/Ol4sdfome3uzN7jmbMGOy9s9j7r51vves/6vr32+irVapUWoC3WwLa/tH0q1vhZRBMC6Ab6Io4/CjGFSICfI44/CpUYMcJ2J3ALWAl8ArwDXAM+l3Sx6YSItyKScP8C2ErqJn8BB21PjUEoyqCkQtwHlkv6E8D2euBX4HXgt2YTirUiuoFDIyIE3A73Kc2nE9c1fsqUvQ0MAf1NZ0MEIWx3AHOo+edtV0jjxbeS7jWbE8SJEW8C/wKrwoZqANgCzAJ6IvAB4rhGAvwBbAK+B64A04CFkgYi8AEi7SMmI2LuLCcVWkIETAWw/b/2D0mVVowIaLlGQEuIgEIbKtuvAtcnmEs0SKoU3Vl+AMyQdGMiCcVEUdd45XkWAQq4hu0XgOEyRm3PBA4AncADYJukg+Ni+Awow6PIilgMlD1tfgCslTQPWArssv1iSRuNQGEeRYSYD1wqM7qkG5L6wvNN0i/M6WVsNAJleBQRoiJp1K6rTE7C9nxgiqSmvHVsr7H9e1kedWOE7S7gl5yqQjkJ29OB/cBnY7VtIBIy3IrwaKtpPNv29kz9EuDH/xisbk7CdjvwA7BT0vl6bTP9em1vKdp+LG5FedSuiC5gte0dkoZC2Us1zyOGO4EZwEPbJ8jJSYSjt17gpKQDzzCpurCdAN+Qnnf2A5+GeWwty+PxipB0GDhKGl2x/TJpcMkiCfd6OYl3gRVAj+2+cHWVnGdd2H4DOA1cIJ38BtITrw6euEZhHtkYcQT4CDgELAOO5/RJGCMnIeksE/8dswc4JmlD+N1vuwf4UNLfAGV4ZIU4Aey23Qa8Jum7nD4NzUnY3ghsrClqB6q219WUvS/pTE2fmaQrd0HG3H3GmVgepZakYeAqsIh0M5KHhMbmJPYFmyPX4Zyy7D6mG3gIXM6Uv8U4hch7fR4BdgCbsxUTkZOQNAgM1ti7AwxKqidqNXBoB+6Ffu8BC4FdZTlAvhBHSYU4l1M3WXISl4B/gK9tfwXMBfaGur7xGHwqkIRAs1ZSnmskTIKcRPgS/pg0oF8NfHqBu6Sv8tJ4BLrKVq7aLBttAAAAAElFTkSuQmCC", + "text/latex": [ + "$\\displaystyle \\frac{b}{\\sqrt{b^{2} + d^{2}}}$" + ], + "text/plain": [ + " b \n", + "────────────\n", + " _________\n", + " ╱ 2 2 \n", + "╲╱ b + d " + ] + }, + "execution_count": 203, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sym.sin(sym.atan2(b,d))" + ] + }, + { + "cell_type": "code", + "execution_count": 204, + "id": "71674e46-4532-4769-b3f0-214da09094b8", + "metadata": {}, + "outputs": [], + "source": [ + "bpos = sym.Symbol('bpos', nonnegative=True)\n", + "bneg = sym.Symbol('bneg', nonpositive=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 205, + "id": "0add72e0-386f-4fc0-bcb5-d9584a640876", + "metadata": {}, + "outputs": [], + "source": [ + "def normalize_types(self):\n", + " return self.derive(\n", + " overfocus=float(self.overfocus),\n", + " scan_pixel_pitch=float(self.scan_pixel_pitch),\n", + " scan_center=PixelYX(\n", + " y=float(self.scan_center.y),\n", + " x=float(self.scan_center.x),\n", + " ),\n", + " scan_rotation=float(self.scan_rotation),\n", + " camera_length=float(self.camera_length),\n", + " detector_pixel_pitch=float(self.detector_pixel_pitch),\n", + " detector_center=PixelYX(\n", + " y=float(self.detector_center.y),\n", + " x=float(self.detector_center.x),\n", + " ),\n", + " detector_rotation=float(self.detector_rotation),\n", + " semiconv=float(self.semiconv),\n", + " flip_factor=float(self.flip_factor),\n", + " descan_error=DescanError(\n", + " pxo_pyi=float(self.descan_error.pxo_pyi),\n", + " pyo_pyi=float(self.descan_error.pyo_pyi),\n", + " pxo_pxi=float(self.descan_error.pxo_pxi),\n", + " pyo_pxi=float(self.descan_error.pyo_pxi),\n", + " sxo_pyi=float(self.descan_error.sxo_pyi),\n", + " syo_pyi=float(self.descan_error.syo_pyi),\n", + " sxo_pxi=float(self.descan_error.sxo_pxi),\n", + " syo_pxi=float(self.descan_error.syo_pxi),\n", + " offpxi=float(self.descan_error.offpxi),\n", + " offpyi=float(self.descan_error.offpyi),\n", + " offsxi=float(self.descan_error.offsxi),\n", + " offsyi=float(self.descan_error.offsyi),\n", + " ),\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 206, + "id": "5e1fb37f-03c3-45df-9857-0220e0734b6a", + "metadata": {}, + "outputs": [], + "source": [ + "def derive(\n", + " params: Parameters4DSTEM,\n", + " overfocus: sym.Basic | None = None, # m\n", + " scan_pixel_pitch: sym.Basic | None = None, # m\n", + " scan_center: PixelYX | None = None,\n", + " scan_rotation: sym.Basic | None = None, # rad\n", + " camera_length: sym.Basic | None = None, # m\n", + " detector_pixel_pitch: sym.Basic | None = None, # m\n", + " detector_center: PixelYX | None = None,\n", + " detector_rotation: sym.Basic | None = None, # rad\n", + " semiconv: sym.Basic | None = None, # rad\n", + " flip_y: sym.logic.boolalg.Boolean | None = None,\n", + " flip_factor: sym.Basic | None = None,\n", + " descan_error: DescanError | None = None,\n", + ") -> \"Parameters4DSTEM\":\n", + " if flip_factor is not None:\n", + " assert flip_y is None\n", + " if flip_y is not None:\n", + " flip_factor = -1. if flip_y else 1.\n", + " \n", + " self = params\n", + " \n", + " return Parameters4DSTEM(\n", + " overfocus=overfocus if overfocus is not None else self.overfocus,\n", + " scan_pixel_pitch=(\n", + " scan_pixel_pitch\n", + " if scan_pixel_pitch is not None\n", + " else self.scan_pixel_pitch\n", + " ),\n", + " scan_center=scan_center if scan_center is not None else self.scan_center,\n", + " scan_rotation=scan_rotation\n", + " if scan_rotation is not None\n", + " else self.scan_rotation,\n", + " camera_length=camera_length\n", + " if camera_length is not None\n", + " else self.camera_length,\n", + " detector_pixel_pitch=(\n", + " detector_pixel_pitch\n", + " if detector_pixel_pitch is not None\n", + " else self.detector_pixel_pitch\n", + " ),\n", + " detector_center=(\n", + " detector_center if detector_center is not None else self.detector_center\n", + " ),\n", + " detector_rotation=(\n", + " detector_rotation\n", + " if detector_rotation is not None\n", + " else self.detector_rotation\n", + " ),\n", + " semiconv=semiconv if semiconv is not None else self.semiconv,\n", + " flip_factor=flip_factor if flip_factor is not None else self.flip_factor,\n", + " descan_error=descan_error\n", + " if descan_error is not None\n", + " else self.descan_error,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 207, + "id": "568d0194-fdd6-4d76-8fed-5de90021512e", + "metadata": {}, + "outputs": [], + "source": [ + "def adjust_scan_rotation(self, scan_rotation) -> \"Parameters4DSTEM\":\n", + " de = self.descan_error\n", + " angle = scan_rotation - self.scan_rotation\n", + "\n", + " # Rotate the input direction\n", + " pxo_pyi, pxo_pxi = rotate(angle).multiply(sym.Matrix([de.pxo_pyi, de.pxo_pxi])) # add simplificatoin? dotprodsimp=True\n", + " pyo_pyi, pyo_pxi = rotate(angle).multiply(sym.Matrix([de.pyo_pyi, de.pyo_pxi]))\n", + " sxo_pyi, sxo_pxi = rotate(angle).multiply(sym.Matrix([de.sxo_pyi, de.sxo_pxi]))\n", + " syo_pyi, syo_pxi = rotate(angle).multiply(sym.Matrix([de.syo_pyi, de.syo_pxi]))\n", + " new_de = DescanError(\n", + " pxo_pyi=pxo_pyi,\n", + " pyo_pyi=pyo_pyi,\n", + " pxo_pxi=pxo_pxi,\n", + " pyo_pxi=pyo_pxi,\n", + " sxo_pyi=sxo_pyi,\n", + " syo_pyi=syo_pyi,\n", + " sxo_pxi=sxo_pxi,\n", + " syo_pxi=syo_pxi,\n", + " offpxi=de.offpxi,\n", + " offpyi=de.offpyi,\n", + " offsxi=de.offsxi,\n", + " offsyi=de.offsyi,\n", + " )\n", + " return self.derive(\n", + " scan_rotation=scan_rotation,\n", + " descan_error=new_de,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 208, + "id": "17068a44-0b80-489a-bbbc-db36e5d1a4d3", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'Symbol' object has no attribute 'descan_error'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[208]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m de = \u001b[43mp\u001b[49m\u001b[43m.\u001b[49m\u001b[43mdescan_error\u001b[49m\n\u001b[32m 2\u001b[39m de.pxo_pyi\n\u001b[32m 3\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34msums\u001b[39m(mo, no):\n", + "\u001b[31mAttributeError\u001b[39m: 'Symbol' object has no attribute 'descan_error'" + ] + } + ], + "source": [ + "de = p.descan_error\n", + "de.pxo_pyi\n", + "def sums(mo, no):\n", + " return mo+no\n", + "sums(1,2)\n", + "def d(ex: sym.Basic | None=None):\n", + " return ex if ex is not None else 'error'\n", + "d(a)" + ] + }, + { + "cell_type": "code", + "execution_count": 209, + "id": "5be8ce64-1d93-4558-a83c-6fa52b530e89", + "metadata": {}, + "outputs": [], + "source": [ + "def rotate(radians):\n", + " return sym.rot_givens(0, 1, radians, dim=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 210, + "id": "78d636c9-d5c6-4f4a-8759-e47e13f92be8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}-1 & 0\\\\0 & -1\\end{matrix}\\right]$" + ], + "text/plain": [ + "⎡-1 0 ⎤\n", + "⎢ ⎥\n", + "⎣0 -1⎦" + ] + }, + "execution_count": 210, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A= rotate(sym.pi)\n", + "A" + ] + }, + { + "cell_type": "code", + "execution_count": 211, + "id": "814b243e-1184-4fd1-be96-a334259ea23b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}-1\\\\-1\\end{matrix}\\right]$" + ], + "text/plain": [ + "⎡-1⎤\n", + "⎢ ⎥\n", + "⎣-1⎦" + ] + }, + "execution_count": 211, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A.multiply(sym.Matrix([1,1]))" + ] + }, + { + "cell_type": "code", + "execution_count": 212, + "id": "531162da-93eb-45f7-9a7b-7bcf667a4997", + "metadata": {}, + "outputs": [], + "source": [ + "x = sym.Symbol('x')" + ] + }, + { + "cell_type": "code", + "execution_count": 213, + "id": "ef7d225e-a523-45cf-af9d-3a8b2576a596", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 213, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x is not None" + ] + }, + { + "cell_type": "code", + "execution_count": 214, + "id": "140b37da-24a2-457a-8210-eaca61d0a401", + "metadata": {}, + "outputs": [], + "source": [ + "def d(ex: sym.Basic | None=None):\n", + " return ex if ex is not None else 'error'" + ] + }, + { + "cell_type": "markdown", + "id": "1f631213-b828-4a96-a5e4-5603c619bb02", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": 215, + "id": "b1b7364d-2f28-4ce2-abfe-bdde20c524ba", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 215, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(x, sym.Basic)" + ] + }, + { + "cell_type": "code", + "execution_count": 216, + "id": "a0b5fbfd-c666-41c6-9ce8-d8853915f539", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 216, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(sym.logic.boolalg.BooleanTrue(), sym.logic.boolalg.Boolean)" + ] + }, + { + "cell_type": "code", + "execution_count": 217, + "id": "e6e7e74d-327f-497d-848e-cdf36ecfc53f", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "unsupported operand type(s) for *: 'float' and 'function'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mTypeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[217]\u001b[39m\u001b[32m, line 4\u001b[39m\n\u001b[32m 1\u001b[39m parms= Parameters4DSTEM(\n\u001b[32m 2\u001b[39m overfocus=\u001b[32m0.01\u001b[39m*a,\n\u001b[32m 3\u001b[39m scan_pixel_pitch=\u001b[32m1e-6\u001b[39m*b,\n\u001b[32m----> \u001b[39m\u001b[32m4\u001b[39m scan_center=PixelYX(\u001b[32m0.1\u001b[39m*c, \u001b[32;43m0.2\u001b[39;49m\u001b[43m*\u001b[49m\u001b[43md\u001b[49m),\n\u001b[32m 5\u001b[39m scan_rotation=\u001b[32m0.01\u001b[39m*e,\n\u001b[32m 6\u001b[39m camera_length=\u001b[32m1.0\u001b[39m*f,\n\u001b[32m 7\u001b[39m detector_pixel_pitch=\u001b[32m50e-6\u001b[39m*g,\n\u001b[32m 8\u001b[39m detector_center=PixelYX(\u001b[32m0.1\u001b[39m*h, \u001b[32m0.1\u001b[39m*i),\n\u001b[32m 9\u001b[39m semiconv=\u001b[32m1e-3\u001b[39m*j, \u001b[38;5;66;03m# radian\u001b[39;00m\n\u001b[32m 10\u001b[39m flip_factor=\u001b[32m1.0\u001b[39m*k,\n\u001b[32m 11\u001b[39m descan_error=DescanError(pxo_pxi=l, pxo_pyi=m, pyo_pxi=n, pyo_pyi=o, sxo_pxi=p, sxo_pyi=q, syo_pxi=r, syo_pyi=s, offpxi=t, offpyi=u, offsxi=v, offsyi=w),\n\u001b[32m 12\u001b[39m )\n\u001b[32m 13\u001b[39m parms\n", + "\u001b[31mTypeError\u001b[39m: unsupported operand type(s) for *: 'float' and 'function'" + ] + } + ], + "source": [ + "parms= Parameters4DSTEM(\n", + " overfocus=0.01*a,\n", + " scan_pixel_pitch=1e-6*b,\n", + " scan_center=PixelYX(0.1*c, 0.2*d),\n", + " scan_rotation=0.01*e,\n", + " camera_length=1.0*f,\n", + " detector_pixel_pitch=50e-6*g,\n", + " detector_center=PixelYX(0.1*h, 0.1*i),\n", + " semiconv=1e-3*j, # radian\n", + " flip_factor=1.0*k,\n", + " descan_error=DescanError(pxo_pxi=l, pxo_pyi=m, pyo_pxi=n, pyo_pyi=o, sxo_pxi=p, sxo_pyi=q, syo_pxi=r, syo_pyi=s, offpxi=t, offpyi=u, offsxi=v, offsyi=w),\n", + ")\n", + "parms" + ] + }, + { + "cell_type": "code", + "execution_count": 218, + "id": "d88325cb-c79b-4c58-8b80-75f4a8568d46", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'parms' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[218]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01msympy\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mabc\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m derive(params=\u001b[43mparms\u001b[49m, overfocus=a,\n\u001b[32m 3\u001b[39m scan_pixel_pitch=b,\n\u001b[32m 4\u001b[39m scan_center=PixelYX(y=c, x=d),\n\u001b[32m 5\u001b[39m scan_rotation=\u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[32m 6\u001b[39m camera_length=f,\n\u001b[32m 7\u001b[39m detector_pixel_pitch=g,\n\u001b[32m 8\u001b[39m detector_center=PixelYX(y=h, x=i),\n\u001b[32m 9\u001b[39m detector_rotation=j,\n\u001b[32m 10\u001b[39m semiconv=k,\n\u001b[32m 11\u001b[39m flip_y=S.true,\n\u001b[32m 12\u001b[39m flip_factor=\u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[32m 13\u001b[39m descan_error=DescanError(pxo_pxi=l, pxo_pyi=m, pyo_pxi=n, pyo_pyi=o, sxo_pxi=p, sxo_pyi=q, syo_pxi=r, syo_pyi=s, offpxi=t, offpyi=u, offsxi=v, offsyi=w))\n", + "\u001b[31mNameError\u001b[39m: name 'parms' is not defined" + ] + } + ], + "source": [ + "from sympy.abc import a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x\n", + "derive(params=parms, overfocus=a,\n", + " scan_pixel_pitch=b,\n", + " scan_center=PixelYX(y=c, x=d),\n", + " scan_rotation=None,\n", + " camera_length=f,\n", + " detector_pixel_pitch=g,\n", + " detector_center=PixelYX(y=h, x=i),\n", + " detector_rotation=j,\n", + " semiconv=k,\n", + " flip_y=S.true,\n", + " flip_factor=None,\n", + " descan_error=DescanError(pxo_pxi=l, pxo_pyi=m, pyo_pxi=n, pyo_pyi=o, sxo_pxi=p, sxo_pyi=q, syo_pxi=r, syo_pyi=s, offpxi=t, offpyi=u, offsxi=v, offsyi=w))" + ] + }, + { + "cell_type": "code", + "execution_count": 219, + "id": "633af256-c997-4db5-97b8-32645d258554", + "metadata": {}, + "outputs": [], + "source": [ + "def adjust_scan_pixel_pitch(params, scan_pixel_pitch: sym.Basic) -> \"Parameters4DSTEM\":\n", + " self = params\n", + " \n", + " de = self.descan_error\n", + " ratio = self.scan_pixel_pitch / scan_pixel_pitch\n", + "\n", + " new_de = DescanError(\n", + " pxo_pyi=de.pxo_pyi * ratio,\n", + " pyo_pyi=de.pyo_pyi * ratio,\n", + " pxo_pxi=de.pxo_pxi * ratio,\n", + " pyo_pxi=de.pyo_pxi * ratio,\n", + " sxo_pyi=de.sxo_pyi * ratio,\n", + " syo_pyi=de.syo_pyi * ratio,\n", + " sxo_pxi=de.sxo_pxi * ratio,\n", + " syo_pxi=de.syo_pxi * ratio,\n", + " offpxi=de.offpxi,\n", + " offpyi=de.offpyi,\n", + " offsxi=de.offsxi,\n", + " offsyi=de.offsyi,\n", + " )\n", + " return self.derive(\n", + " scan_pixel_pitch=scan_pixel_pitch,\n", + " descan_error=new_de,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 220, + "id": "1e30612f-b469-423e-8f4a-d9b795e19d52", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'parms' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[220]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m adjust_scan_rotation(\u001b[43mparms\u001b[49m, x)\n", + "\u001b[31mNameError\u001b[39m: name 'parms' is not defined" + ] + } + ], + "source": [ + "adjust_scan_rotation(parms, x)" + ] + }, + { + "cell_type": "code", + "execution_count": 221, + "id": "0d4916b0-be12-4b90-846a-ea18fc9e003b", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'parms' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[221]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m adjust_scan_pixel_pitch(\u001b[43mparms\u001b[49m, x)\n", + "\u001b[31mNameError\u001b[39m: name 'parms' is not defined" + ] + } + ], + "source": [ + "adjust_scan_pixel_pitch(parms, x)" + ] + }, + { + "cell_type": "code", + "execution_count": 222, + "id": "7bba1ae8-af31-464a-86ee-08e983938c6e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}\\cos{\\left(x \\right)} & \\sin{\\left(x \\right)}\\\\- \\sin{\\left(x \\right)} & \\cos{\\left(x \\right)}\\end{matrix}\\right]$" + ], + "text/plain": [ + "⎡cos(x) sin(x)⎤\n", + "⎢ ⎥\n", + "⎣-sin(x) cos(x)⎦" + ] + }, + "execution_count": 222, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rotate(x)" + ] + }, + { + "cell_type": "code", + "execution_count": 223, + "id": "67c8f235-7f39-4fcf-8945-42289be945f0", + "metadata": {}, + "outputs": [], + "source": [ + " def build(\n", + " cls,\n", + " params: Parameters4DSTEM,\n", + " scan_pos: PixelYX,\n", + " specimen: Optional[Component] = None,\n", + " ) -> \"Model4DSTEM\":\n", + " scan_to_real = rotate(params.scan_rotation) @ scale(params.scan_pixel_pitch)\n", + " real_to_scan = scale(1 / params.scan_pixel_pitch) @ rotate(\n", + " -params.scan_rotation)\n", + " scan_y, scan_x = scan_to_real @ sym.Matrix(\n", + " [\n", + " scan_pos.y - params.scan_center.y,\n", + " scan_pos.x - params.scan_center.x,\n", + " ]\n", + " )\n", + " detector_to_real = (\n", + " scale(params.detector_pixel_pitch)\n", + " @ rotate(params.detector_rotation)\n", + " @ flip_y(flip_factor=params.flip_factor)\n", + " )\n", + " real_to_detector = (\n", + " flip_y(flip_factor=1 / params.flip_factor)\n", + " @ rotate(-params.detector_rotation)\n", + " @ scale(1 / params.detector_pixel_pitch)\n", + " )\n", + " if specimen is None:\n", + " specimen = Plane(z=params.overfocus)\n", + " else:\n", + " try:\n", + " # FIXME better solution later?\n", + " assert params.xp.allclose(specimen.z, params.overfocus)\n", + " except TracerBoolConversionError:\n", + " pass\n", + " return cls(\n", + " source=PointSource(z=0, semi_conv=params.semiconv),\n", + " _scan_to_real=scan_to_real,\n", + " _real_to_scan=real_to_scan,\n", + " _detector_to_real=detector_to_real,\n", + " _real_to_detector=real_to_detector,\n", + " scanner=Scanner(z=params.overfocus, scan_pos_x=scan_x, scan_pos_y=scan_y),\n", + " specimen=specimen,\n", + " descanner=Descanner(\n", + " z=params.overfocus,\n", + " scan_pos_x=scan_x,\n", + " scan_pos_y=scan_y,\n", + " descan_error=params.descan_error,\n", + " ),\n", + " detector=Plane(z=params.overfocus + params.camera_length),\n", + " scan_center=params.scan_center,\n", + " detector_center=params.detector_center,\n", + " xp=params.xp,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 224, + "id": "f96c3f0b-d491-471e-8d7b-3ed4d45c054f", + "metadata": {}, + "outputs": [], + "source": [ + "number = 5\n", + "b = sym.symbols(f\"b_0:{number}\")\n", + "b_dict = {f\"b_{i}\": b[i] for i in range(number)}" + ] + }, + { + "cell_type": "code", + "execution_count": 225, + "id": "c78fddb9-3caf-40f7-b386-ae103dc8920a", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle \\left( b_{0}, \\ b_{1}, \\ b_{2}, \\ b_{3}, \\ b_{4}\\right)$" + ], + "text/plain": [ + "(b₀, b₁, b₂, b₃, b₄)" + ] + }, + "execution_count": 225, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b" + ] + }, + { + "cell_type": "code", + "execution_count": 226, + "id": "1497db41-933d-4d29-b284-05eff89d32c2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'b_0': b_0, 'b_1': b_1, 'b_2': b_2, 'b_3': b_3, 'b_4': b_4}" + ] + }, + "execution_count": 226, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b_dict" + ] + }, + { + "cell_type": "code", + "execution_count": 227, + "id": "b3dbc156-cfe9-4f9b-adca-d97dc77b7abf", + "metadata": {}, + "outputs": [], + "source": [ + "symbols = [sym.symbols(f\"{attr}\") for attr in par.__dict__.keys()]" + ] + }, + { + "cell_type": "code", + "execution_count": 228, + "id": "6b333ca3-6f98-4152-8e44-77b21e37831a", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABagAAAAXCAYAAADugKhWAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAHXlJREFUeJztnXm8HUWVx78BAiigLMqOKLKGkNwEAQWCUQiooCwCCn4koIwOyiIMyBY9HBFkcQFFUWdUlhHZBWRfZFEQBOElBKNsw6aAgMywhLD55o9TnVe3b3ffrr7Lu/fd+n0++dy87trPqbN0VZ0aNzw8TERERERERERERERERERERERERERERES3sVj6gareDswB5gNfE5EXu96qNkJV3wZ8E/gEsCowHjhSRE4Y1YZFRERERERERLQRqroG8BjwaxHZZbTbExGRhcinEWMRqnoI8B3gMyJyzmi3p5eQ548DrwLfxRuzOI7tRZS3g4NI64h+gqruBUwFJgA/E5HzIOMDNbAZ8O8iMtS95nUUvwR2AK4E/ht4A7hsVFsUEREREREREdF+bOx+726lEFU9mNRHg25hNOuO6Boin0aMRbzP/d41qq3oTeT540e5974siOPYXrRF3kb0BaJujegbiMhZwFmqehOwSvI86wP1mIGqro8pw2tEZPvRbk9ERERERERERAcx1f3+qcVyEien1XL6re6I7iDyacRYxBHAN4AHRrshvYQif1xVpwIvAfd7j+M4thftkrcRvY+oWyP6HmP6AzXwYfd70ai2IiIiIiIiIiKi82jXTqmNafxo0C2MZt0R3UHk04gxBxF5bLTb0KPI9MdVdSlgPeA2EflX8jyOY9sRd1APDqJujeh7jEtfkqiqw8CUdIgPVR0HzAT2ASYCSwHzgFNE5Ewv3ZHA8cCBIvKDdIWqupbLNxvYTESGvXefBPYFNgHeBjwK/AI4UUTe9NJ9CPgtFp/qfOBoYBqwnGvb+sCFOX2eICLzyvYn1fbpwH7AFsA7gX9i8bpPF5FLUv3/pIhcnMr/LtenS0Rk59S7acDBwGRgNeAF4BHgBhE5MqcvlRBSV5k+u3R7AttjtFsFO7r1IPAjEflFqswZwLXACdiRr6OArTEazAEOFpE72tfjiCJEfugvhMqKSLP+Qifo5dL7evMc4OvAVsCi7vl+IvKUqk4AZgHbAEsAvwO+lOUwtktni8h9Vfo0qFDVxYAvA58H1gWeBn4EnAT8HfiXiKyWylOWVicAh+dUvZeInB1appd+OgW8XbbuEPutLO9FtB9V+NTla8pXkU8HC4F2ahW91LJO9Mr7togclno3nXJ6Pcg3DbHFWvHPq8LRItcfB5YHfg98X0QOcnkyx1FVPwDcBpzmfr8ETAKWdG0+XERubLXN/YpOylsv7aj6iy79wPsfUbdG3dpNqOq1wAxgVxG5yHs+DqP3TIzmR7jnwbLahfi4REROgZI7qFX1LcAlwLYYI53pKtkZOENVVxOR413y2e53Yk5xJwKLYwJk2JW/KBaP6tOYULoAuzTho8Bx2OrqTK+M5PjCROBA4ArgJ8C7gL8AbwfUDchyrgyAYeD+wP4kBPgecBDwrKvvSWBNV8bmrjwYWbnKipu1ifutW9VS1aNcGx8DrnF1rITF4NoOu0SiLShbV0ifVXUZ4CzgTsxw+wewIrAj8HNVXVFETvSaMcX9ruvyXIfRYEPgY8Blqrp2v1/Q2Q+I/NBfCJEVkWb9hQ7TC0b05rqYQ3gV8DPgI8BOwBKqejrwK8yYOxOYjjkOZ7n/J21tt86u2qeBg6ouDlyOGYtDwA+AFYBjgLWBlYHfeOlDaXU3RvuZmGF5nffupiplBvB2mbqD7DdK8F5E+xHKpy5PCF9FPh0QBNipVfVSyzoxVd5CHy9Qr4fyDITZYsH+eRvwJAX+uHsO9Uf5G8bRIfGvt8E+cl0B/Nil3wa4UlXXG8Td112Qt73iL8KA+x9Rt0bdOgo4DKPNsap6ibf48G2MVj9NPk47tCyry4b4OAdjhKNE5FvJQ1UVjLhfV9XTReR5jFnABEUdVHULYFfgfBG51Xt1KsbkJwBfE5E3XPrDMIbcS1VPFJE/u/QJs20BbCUit6equk1V7wC+CswTkWNS7QjpD9hkOwg7mjRTRF728iyNCYYEGwPP5gx6cunDPV7+lbA4W78HthaR11JtfUdGOZUQWFdIn4eB1UXkqVR5szADZB/M8EmQ0G8asIWI+MbcRcAuQA1TVhEdQuSH/kIFWRFp1l/oJL1ghGabAu8XkTkuzzcwh2NbzPCfISJ/cO8Wx4zPrVR1SRFZ4Mpot86u2qdBxA8xx+TrwDe9hf4zgJtdGt+xD6KViJyvqstiRueZIvLTjDaE0r8Ub5esO9R+K8N7Ee1HKJ9CAF9FPh0MBNo9VfVSO3SiX57P1yF6PZRn/DrL2GJV/POWICLN/PGscAR5H6iT56sC00Xk91453wcOwD5AHdqe1vcVOipve8hfhOh/RN0adWtXISKzVfVsjC6fxRYCjgIOwXal75fK0rKsXqRZo1R1B2wl+QKfEVyDn8FWcZZIGiMiT2Bb9jdMlTMOu5HzVbzt+6q6GbaCeqmIHJkwuSvrdWxlBGAzr7ik4wcVMNoE4C007lYO6o/a5Q2HYzui9/Qnlcvzkog86tIuD7yb/KDuyQdqv03rY8fJ7k8LfFf+szllVUGpukL67P1dp1zc8yexoybLp14lq5/7+IrFYZ77XbJ0rxxU9Yeq+uvQfE3KPEFVr2uesr1tcPVe32o5TTCm+WEMorSsiDTLRyfkRKvoAr1gRG/unTjiLs+L2BHNRYHDEkfcvXsN+CswDjtC1zGdHdqnUNncaXRDZqvqpthxystF5FjxdrmJyC2MzL0/ufRVaAX5HwiCywzl7SZ1B9lvqfKK7MVC9IJt0a529CKfujytyJXIp2MXZe3UVvinJZ2YKm9h3NRA/7EKz0CALRbqn7cRmf64w1TgFa+tybOs+LNJ3w/wP3g4/JdXVx1U9d9U9WFVfUNVsz549TW6JG97xV+EPvY/WkXUrWNXt/aib5rC14AFgKjq/tiiwzXAZ8W7P8Chkqz2UWYH9Rfcb97Opefc76LesznAdFVd3SlEgD2wVeoTReQRL+0BmKKfr6rHZJSfHEVaBEDtQoV1sWMgZxS0O4/BQ/tzsKv7iCyhnFNnVngPsN3V/xCRv3vP7gP+D/icqr4Ti6d0rdSvkLcLZesK6TOquhywP3bsbT0sDpG/+OHvGF8aWAeLU3RlRnFrud+HynQohVnA6xXyFaGGHaGp1AZVPRmYJCLbdbjeKhjr/NASWqBdpxAiK8YMzarSoSBfJ+REq+gYvVz6RG8+LCJXZxS5Jua4npfz7kURSXRjR3R2aJ/ojoxsQAFfdaM9B7jf43LeJzRK7J4gWnmYis2Re3PaEFJmEG83qTvIfguwF5uhF2yLunb0uG0RyqdJnlBejXzaRvSgzQPl7Z6qeqkdOtEv7zbvo1EITwX72hVtsRD/vF3I9MdVdUlgA+AuGYl/mzWOqOoS2AeNx7HQAmkk4zM+Vcf6wOnAbsDtQMthH3pwnnRD3o66v+jS96zP2CVE3WroCd3aZv+0F33ThRCRx1X1FOAILKzMbcAuaZpWldVplPlA/UHgKRHJ2xW8ivv1Q1rMxmJzbQg84ZTQ8RiDpONnbet+92jSjmR1ZTLG6FdkfLH3kSjEtFMb2p/tgOeBG5u0D0ZirjSUrXb5xHJAnSEkIs+q6paAYLGTPg686XbWHJ2xOlgZAXWV7rOqTsIuK1gJ+CNwLsZ8bwDvAfZiJO4ZmHM0DrhOsmOcTcWU4P9U6F8nPurXsPhYVduwKXBLxXp/WSFfaYx1fmgDqtKuDqq6mL/CXBWBsqLnaRYwLlXpkJmvQ3KiVXSSXjCiNxt2TqrquzHddLHb/eC/WxqLaeevgLddZ1fsU40A2dxG5PFjjQ7LbGzsnwPyLgBaC1sE/5uXHsrTCrXLdzYC/iwir+a0IaTMEN5uVneo/VbWXixEL9gWGe3oWduCcD5N8kBJvop82hG0xeaBUbF7quqlduhEv7xKdhjVfO0a4bZYiH/eLuT545Ox7xB3p56lxxFsvo0HrsmZI+92v4+lnn8CmCsi7dyZ2FO+AV2Qtz3iL0KXfMY82lSlWT/ROurW0fFPe9Q3TeMZ7/+fF5H5GWmqyuo6FH6gVgte/zYs3lbW+0UxZvkHduQpQXJUaiK2/fsr2IrzF0XkBS//ktgtnreIyAeL2uIhUXTNbmidisU2GvLqC+qP176hkoydHDvJ+qj8MfebVtCIyFxgN7XYZlthK0S7AZuoBXvPmqSV0KwuTPCH9PlsYFngQyJyk/9CLY4b1O8oT+iX9RF/GWyl6+bUyvk7sElxCLAnxvyPAfuLyLUuzerYas0GIvIXVd0Vc8LWlZEjdKcCOwCbi8jTrr/fwlZuFwVuwG7mftqlXxlTnENl2uG3AXgYO6I2HotVNwuLvzbBlbUa8E3sQoFlMZ48xJWT1Puaql6JE8jAvtLmG6r7kR/cu9zx895n0taj005Y3KRpeOPrxqIZ7ZqVvQc2lptjR6F+3nzomqOMrKggt7oxh3PHRVU3wFbDP4gdMT0fu5DhTYrpMAvYHTPIXsEuYdivKF9aTrhyMusXkVea8UqTcS2FLtALCmhGwcIqptPG4XRaB3V2UJ/Sstk9qzznvTI2xo46b4rtwPkCFtNvBrZbII+vOi6z3divCNyT5ZipHaNcFbvoqyqtwHY+LEn28cqgMivwdlHdVezRQt7rF9si1Y7J2FzoSdsilE+9PKG8OjB86vJVtnnc+1wZCNxKRZsnVfao2D2M2KlV9FJLOjGjvHsgjKda8LWr2M+l/PM2o8Ef955D9gWJaV85ef5ITh3bu99rkweqej+22xZVHQYuEZGd82xHEXnFpc2ca1hs3TwbINeOdGV2ZI50Ud72gr8I1fyPSvJLVa/Nek6B35LyG/qV1oOmW/Po/wfyaZz7naBIvuTlwz76l/ZNU+3eiQ75p6lx2hO7FPEp7CLOg2iMPQ0VZHUW0sc603gVUyrvzHm/N7Za8YvUZElWvCaq6orYza5zsduRfYxzvyEXATYLo4FaPK3JwANSf4traH+S9q1Ysm3rA69LfSydZLv7F92fuTuiReQ1EbleRHbHVudXwJyJtqOgrtJ9VtU1gEnATRnKZVnscgOoVyTJR/ws+iWGX1rx1Nzvvlgco0mYkXWO2m2uYPSez0jMsouw4yGzXHsOxYTPR5wD+R6MFn8DtsR2FLwDu2XUr/cVRgRds3b4bXgD+IBLvxnGV1u4tqyOCcvlsMscJgInA4lxmNTzZezW28nY/Plu45C1B/3ED83GrwRtJ7vfQzBhmx7fItqVLftwV/aGwKUZfWsJTWRFz9GMnHFR20HxB+xii00weu6AXcaSSweHxTDluCE2t2dgzlZRvjo50aR+v915vNIOdJpeUKw3Ny54l150bbvOrtinGp5sbsOcR+0o8I2YbJmKHWM7D+OzIYr5quZ+Oymz33T/8vjkaPfbCq1gpC8Ni+kVygy1oYrqrmKPNrMXk/p63bbw2zGX3rYtQvkUqvFqzf2OeT5tg80DxTKwFZvHL3u07J5266UQnZguLy1/y/BUVV+7ii1W1j9vCwr8cRgZZ38sM8OBeM+XzahjeezD0uPU892WmNyehY3fTPc8z3ZsNtcy50kJOxI6N0e6JW8XYhT9RQjk+RblVxW/pai8VhF1a/ttQMim10MU07jIH8iVLwX5Qn1Tv92d9E9x7fkYFiJlLjZv/wrsq6rrZSSvIqsbUPiBWiyuyB+BNVR1m1QlW2O3eD6CrUz5uA+bRBsCiq14HCIuxpRX/iuYET5BVXfJaoOqbulWRRJMBV4jOzZNgnWBZUgxeGh/XPvmAquq6u4ZbVs31bbXgPGquo6XZils1Wyie3SP926Kqr43o9y1XfrHgCe852eo6rCq7l3Q90yUrSuwz8nt1Wup6ngvzQqYY786NhmHvCIS+s2hEVnGCpjAehPYWUR+KyL3Y4JkBWxRIElzb7JC54TTUcDeqnoEdtvt9iLygEv/Y+BnYgH+54nIEHAssHWq3ns9vm3WjoVtcO1YBYt3dqeIPCUjxzd+ghmJO4vIbSLyoIicKSPB+2vYEaXdReQ6EXkQuJCUUB5gfmg2fs1oW8OMzU85I6tufJvQrkzZC4DdRORKEXlI6uMUVqJZiKzoUZrVyB6X/wQuEpGvisj9Ypcp/Aj4RBM6ICLHiMitIvKoiFyPrVKv3yRfDU9OFNXvpc/lldS4VqJtF+gFIzSbm9GEot1idc5ih3R2lT7VqJfNLc15h+9jx0a/6sq4HPtg/aiI/G8JvuqozBY7av4AsJqqfjxV7uGYAQuOjhVpBe42dUY+avptCCqzgg1VVHcVe7QZ79XoD9si3Y6etS1C+dTlqcKrg8Snrdo8UCADW7R5krIz7Z4u2qlV9VJLOjH1fAHwZwjjqYo84/chxBYr5Z979Vemn0OmP+4wFfvodF/q2cJx9JD0Z1fnVyftWxo4B5uTXxGRBV6eF7BdjLc6nn4B8m1Hlyd3rhXMk2Z2JHRojnRD3vaQvwjhPN+K/Mp7XsZv6EtaOwySboUMegEnUUDjIp1ZJF8K8tUI802Tdjf1T1uV4WqhfS7EvjFsJ3YZ5SzsQ/yJGVmqyOoGlIlBfTR2DOhyVT0fu1V1MhZT5lFghoj8n59BRBaoHa2ZhDHHFSJyXU75h2HEu0jtZvE52Ifz1Vwnx4vIu1znksDbc6Q40HqeAVGlP0cAlwHnqupMTJEui63UrS4iq3hpr8FWOm5Ru4lzaUwIzgGeBN6KhX5IcADm5NyOre68hCnThAE/J/VHIpIFhTcK+p6HkLpK9VlEnlHV3wIfBu5w9FsFOxZ1I/AvLI7QAihFvzyjsAb8xjltCdLCazIpRSZ2pPVO7KjWx0XkTteONbHjFNNU9UAvy6LYCpZf71Dq76J2pNswBZgt9UeN1sTCvWwi2fGz/Hr8m43XpvFYy8DxQ7PxK0nbyZhMKhrfPNqVKftKpyiyUJVmobKiZ2jm0DAuaiuvm2K7Bn28it3CDBl0cHnXwHTHhzBdsbjLc1JRPrw5WrL+MrySoJX52BF6uX6Wodmj4i2keJiK7fSc5z1rq86u0ic82dyOOe/4aQYjuzcSvEp9DMQ8vqrReZkNZnifiY39udhRu+lYSIjHgTWot3tK08pDMnePU9WJwMvAfSJyQcUyQ2yoZnWXtt9K2os1+sO2SLejl20LCOdTCOergeDTNtk80FyXVbV5krLz7J5u2ant1EtBOtErb0jq45aG8FSQb1rVFgv0z6F1WZDpj6sddZ+I8dzr7lnmOOpIXNrZwNuBOap6GWan7YiFNThSRC5O1T2RkRNQSVm5tmNJGVo3T0rakdC5OQKdl7ej7i9COM+3QX5V9Vv6mdYwILrVQx29AuZ0ls5s5ptm5iPcN03ylPFPK/OcqtaAy7GNDTNE5EkAEblQVe8CdlTVaSLyO5e+qqxuwCLNEojIDdhH1tuw1Zn9sZWtY7EbKPM+xMx2DRoGDi0o/1os3stFrlMHAp/F4vhez8gxDzBlM57sVW0fuR+oQ/sjIldgk/8qbFv+wdjlAM9n9Os4bBfWMHbUYAK2HX8PLF7LUIohL8ViGa7g0h8KvB9bYZjs2upjI2zV5Yom/c9C6boC+/xpLI7UGtiRhvdixw2Ox/jLP1aRGAt5Ry02xhTf/annNRpX3zfHVrz8I7JDfgJV/TA2gccBT3uvJmPO3ySXL/m3ESPHh7LKbNaOMulrmJAo4uEadrTDxxQaV5IHkR9qFI9fGdrWaD6+NbIvdClT9s05bYPqNAuSFT1GM8gel4nYTp55qecTGFntrtF4i/cKwJ2YTD0Ui7v1PmweDuXl854nacrWX2YuQgvzsYP0ggK96Qz4FXLeLYHp4Tni7a7qkM4O7VONERq0Y85PxeRKelfOBjSXC2XKT9CKzEZEzsKOCj6B2RUzMadkC8zueE68EGOBtEry3II5pC+7X8H7cB9aZghvl6g7xH4rw3s1+sO2SOfJSl+jN2yLYD51eUL5alD4tEbrNk9SThHta1SzeZK8eXZPt+zUtuilKjrRK6/O5wzkqVBfu6otBiX9c4eWZAH5/vhGNI5Z5jhiu72XwPh3W6xPn8foOw/b1XdCRt013AkoKGU71ignQ/15UsaOTPJ1Yo50Q972gr8I4TzfqvzKel7Wb+hXWg+Sbk1Qo55eIXN6oSwo6Zs25POeJWlC6u+YPad2QuJqjK+2E9tZ7uNI93uy96yqrG5AmR3UiMjN2CpXaYjIHjS/4TNJeyewa4l0f2Iknk1RusOwFYy890H9cSsDvyuRbgEWNPygjNcNiwEicikl4xKpxWSaBHxHKtz0GVKXS1+2z89gN+1mYVwqbSH9RGSD9DO1APvr0Th+/wGcKyLz3RGC91K/Sj4Z+DUm4LbHVh23c69fB5bCboJ9KastqvpW7HKNe8q0w/Wrrg2Ycrwqlf51bN4tQ8YRlnS9HqYAF3vplmUA+YEm40cT2nq8Uji+5NOuTNlZJzdaolkovVyenqBZwbi8iM2nxXEru6q6EvAZRlaOs+iwPXaJxqe8XSwzsRMrQ3n5MuREYf0BvNLyfITO0Mulz6WZM2Tz3r2KGXdZ79qts0v3KUNGtmPOv4ntqnkrZhijdmHiFtTHc8viq67I7AQicip2lDGNNXPSl6JVKs9pwGkF74PKLMvbJesuZb81471+sS1y2tGztkWCUD51eUL5aszzKS3aPFBaBgbbPKmyG+yeUbBTW9ZLVXRik/JCeKq0b1rRfk7elfLP22TXZPrjWe0v6FPykfsesVBKHy1ZfY16n6yZ7bgOxXMNGudJUzu2k3MkQSflbS/4iy59KM9Xll9V/ZZ+p7WXfhB0ax6dy/im0CgLyvimDflCfdNUno7Zc+5j/8oF76+ncXyryuoGlPpAHdETmIYJ2+82SzjGMBGbAHuo6g3Y7ayzsGMMSTyjSe53DizcAXEVNiF/rqp/xI4ZTBe7jOF2bOXubLWbgl/AjivtBBwgdlSprswS7UinB5tf66vqqsB8sVX8O1zdP1bV47API1tisYhmZ5XjVuVWp17ADSo/NBu/QtoyMr5DSYE545tFu+CyUxhUmuWNyx3Ac8AJqnoqdhzqFGwF/jyXJosOz2EKfydVvRdTgEdhSv3BgnzpudWs/ven253DKzC4tB0NpOnYjjl/F3Z87mRV/Q7mqH4vnY9yfBVldn+gX2yLhnYQbYtBQks2T4rnhpJCM2gfbPPkle0h8lJ/o1fot/CjR2C+GmbPJWhmO/6T4rkGqXlCOTs2zpHRQSvyq6rf0uA3eIi07j1k0bnMnIZGWVDGN83KlyyslPVNM9vdI/ZcVVndgKYhPiJ6AyLyGxFZUupjzQwCatilAAL8CmP65YBp3lhMxm6Inq92Q+jVWJzFbwCIyFzgAkYuv3weExxvx2JdDWE3oD4hI3G0akmZJdsxOZUeLFbSp7GjOEndz2FHW9bElOftwKcYOSac1PuyV84UTMAsvDRkUPmh2fiVoG1CJ381vWF8yaZdlbL9tg8kzcgZF7E4YTtiR77uxeKqXYpd4DXskjXQAbgSu8jmTOyY1zrY8cM5TfLVzdES9ZfllUGm7WigRj0dW57zjm4zsfiTc7Bjg2cAz4jIw16+LL5K2hNldn+hRn/YFnXtcH9H22JA0AabB8rpsio2T17ZSdsjL/Uxeoh+SQiue5slTKCq47CPOEPe40LbsYQMhdQ8KWnHxjkyCmhRflX1WyKt+wsN9Co5p6FRZ5bxTbPyhfqmme2mN+y5YFmdh3HDw8N1D1R1GJgidtNpRMSoQlVPA1YUkYbbXwexHRERERERnYVzbq8GHhKRL412eyLaj17R6b3SjoiIiIheg6ougu18fUREJo52eyIiIiIiGtGqrFbVm4BLROQUiCE+InofNexDwWijRm+0IyIiIiKijVDVLbFYa3djFwEdjMn8fQqyRfQ3avSGTq/RG+2IiIiI6Cm4na5Lj3Y7IiIiIiLy0W5ZnfWB+lbgC6o6H1ARebFdlUVEhMA7onVSbEdERERERIewMnAiFuvtGeAmYGMR+ftoNiqiM+gVnd4r7YiIiIiIiIiIiIjoJlT1M1h4kvnA35LnDSE+IiIiIiIiIiIiIiIiIiIiIiIiIiIiIrqB/wd/35s/moI3sAAAAABJRU5ErkJggg==", + "text/latex": [ + "$\\displaystyle \\left[ overfocus, \\ scan_{pixel pitch}, \\ scan_{center}, \\ scan_{rotation}, \\ camera_{length}, \\ detector_{pixel pitch}, \\ detector_{center}, \\ semiconv, \\ flip_{factor}, \\ descan_{error}, \\ detector_{rotation}, \\ xp\\right]$" + ], + "text/plain": [ + "[overfocus, scan_pixel_pitch, scan_center, scanᵣₒₜₐₜᵢₒₙ, camera_length, detect ↪\n", + "\n", + "↪ or_pixel_pitch, detector_center, semiconv, flip_factor, descanₑᵣᵣₒᵣ, detecto ↪\n", + "\n", + "↪ rᵣₒₜₐₜᵢₒₙ, xp]" + ] + }, + "execution_count": 228, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "symbols" + ] + }, + { + "cell_type": "code", + "execution_count": 229, + "id": "d039f958-842c-4281-b38c-29e97a7d5036", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['pxo_pxi', 'pxo_pyi', 'pyo_pxi', 'pyo_pyi', 'sxo_pxi', 'sxo_pyi', 'syo_pxi', 'syo_pyi', 'offpxi', 'offpyi', 'offsxi', 'offsyi'])" + ] + }, + "execution_count": 229, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "de=DescanError()\n", + "de._asdict().keys()" + ] + }, + { + "cell_type": "code", + "execution_count": 230, + "id": "263d4090-d957-4e49-9893-e3e76d096175", + "metadata": {}, + "outputs": [], + "source": [ + "symbols_dict = {attr: sym.symbols(f\"{attr}\") for attr in par.__dict__.keys()}" + ] + }, + { + "cell_type": "code", + "execution_count": 231, + "id": "62fe5022-e79c-438a-8d0d-bb0243b4d8b6", + "metadata": {}, + "outputs": [], + "source": [ + "def symbol_maker(params_cls, postfix):\n", + " if postfix is not None:\n", + " symbols_dict = {attr: sym.symbols(f\"{attr}_{postfix}\") for attr in params_cls.__match_args__}\n", + " else:\n", + " symbols_dict = {attr: sym.symbols(f\"{attr}\") for attr in params_cls.__match_args__}\n", + " return params_cls(**symbols_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 232, + "id": "b767f35d-4975-4a50-8367-6e510acb12ea", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Parameters4DSTEM(overfocus=overfocus_new, scan_pixel_pitch=scan_pixel_pitch_new, scan_center=scan_center_new, scan_rotation=scan_rotation_new, camera_length=camera_length_new, detector_pixel_pitch=detector_pixel_pitch_new, detector_center=detector_center_new, semiconv=semiconv_new, flip_factor=flip_factor_new, descan_error=descan_error_new, detector_rotation=detector_rotation_new, xp=xp_new)" + ] + }, + "execution_count": 232, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "symbol_maker(Parameters4DSTEM, 'new')" + ] + }, + { + "cell_type": "code", + "execution_count": 233, + "id": "7983e522-e949-4d4a-ace6-9c3f667c51b1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 233, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "DescanError.__annotations__['pxo_pxi'] is float" + ] + }, + { + "cell_type": "markdown", + "id": "6757781d-98bd-47d8-a0f4-bac3ae403f7f", "metadata": {}, - "outputs": [], "source": [] }, + { + "cell_type": "code", + "execution_count": 234, + "id": "1b544a9f-e14b-420f-823f-b036227bf2e2", + "metadata": {}, + "outputs": [], + "source": [ + "def symbol_maker(params_cls, postfix, recurse_for=tuple()):\n", + " symbols_dict = {}\n", + " for attr in params_cls.__annotations__.keys():\n", + " cls = params_cls.__annotations__[attr]\n", + " if cls in recurse_for:\n", + " symbols_dict[attr] = symbol_maker(cls, postfix, recurse_for)\n", + " else:\n", + " symbols_dict[attr] = sym.symbols(f\"{attr}_{postfix}\")\n", + " return params_cls(**symbols_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 235, + "id": "f6205c1c-20f0-499e-bc0b-166044b7c8de", + "metadata": {}, + "outputs": [], + "source": [ + "p = symbol_maker(Parameters4DSTEM, 'new', recurse_for=[DescanError, PixelYX])" + ] + }, + { + "cell_type": "code", + "execution_count": 236, + "id": "f0a65b9a-8a91-4fd0-9776-cf54051a02a7", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'pxo_pxi_new' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[236]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mpxo_pxi_new\u001b[49m\n", + "\u001b[31mNameError\u001b[39m: name 'pxo_pxi_new' is not defined" + ] + } + ], + "source": [ + "pxo_pxi_new" + ] + }, + { + "cell_type": "code", + "execution_count": 237, + "id": "6dd58b1e-4bf1-4d00-bdc7-b6d560d75c26", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['pxo_pxi', 'pxo_pyi', 'pyo_pxi', 'pyo_pyi', 'sxo_pxi', 'sxo_pyi', 'syo_pxi', 'syo_pyi', 'offpxi', 'offpyi', 'offsxi', 'offsyi'])" + ] + }, + "execution_count": 237, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Parameters4DSTEM.__annotations__['descan_error'].__annotations__.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": 238, + "id": "714f8d00-e919-497b-b7d3-ea8c47dceeac", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "typing.Union[int, float]" + ] + }, + "execution_count": 238, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "PixelYX.__annotations__['y']" + ] + }, + { + "cell_type": "code", + "execution_count": 239, + "id": "81267e43-aa90-44b1-a1dd-ee039573149f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'PixelYX'" + ] + }, + "execution_count": 239, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "PixelYX.__name__" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "3ba56bb0-d573-4ee0-9624-8b7c835942d5", + "id": "f0e55679-1a23-4cbb-9211-857871c4ea91", "metadata": {}, "outputs": [], "source": [] }, + { + "cell_type": "code", + "execution_count": 240, + "id": "1d78c95a-e8a2-4d41-86a0-89b2f4b8eaa2", + "metadata": {}, + "outputs": [], + "source": [ + "def _mk_adjust_scan_pixel_pitch(cls=Model4DSTEM):\n", + " \"\"\"\n", + " This function first finds a general symbolic expression for each component of the\n", + " new descan error given old parameteres as symbols, then converts the sympy\n", + " expressions for each component into a function that allows fast numeric evaluation.\n", + " After that the new model is constructed.\n", + " \"\"\"\n", + " def symbol_maker(params_cls, postfix, postfix_vars=tuple(), recurse_for=tuple()):\n", + " symbols_dict = {}\n", + " for attr in params_cls.__annotations__.keys():\n", + " cls = params_cls.__annotations__[attr]\n", + " if cls in recurse_for:\n", + " symbols_dict[attr] = symbol_maker(cls, postfix, postfix_vars, recurse_for)\n", + " else:\n", + " if attr in postfix_vars:\n", + " symbols_dict[attr] = sym.symbols(f\"{attr}_{postfix}\")\n", + " else:\n", + " symbols_dict[attr] = sym.symbols(f\"{attr}\")\n", + " return params_cls(**symbols_dict)\n", + "\n", + " params_old = symbol_maker(Parameters4DSTEM, 'old', postfix_vars=['scan_pixel_pitch', *(DescanError.__match_args__)], recurse_for=[DescanError, PixelYX])\n", + " params_new = symbol_maker(Parameters4DSTEM, 'new', postfix_vars=['scan_pixel_pitch', *(DescanError.__match_args__)], recurse_for=[DescanError, PixelYX])\n", + "\n", + " # Use trace() to calculate detector_px() for the old and for the new parameters.\n", + " # Then set the results equal to each other and solve the equation with respect\n", + " # to the new descan error. For this purpose construct two expressions - for y and x\n", + " # coordiates respectively - and apply sympy.solve().\n", + " # Note that the equations should hold for every value of the scan position (in particular,\n", + " # scan_pos_y scan_pos_x), and also for every value of the camera length. To implement that,\n", + " # expand the expressions, then rearange them to be polynomials in the variables scan_pos_y,\n", + " # scan_pos_x and camera_length, which automatically means collecting the coefficients\n", + " # of each variable through the usage of sympy.Poly(). Finally, set the collected\n", + " # coefficients of the polynomials to zero, which mathematically implies the desired\n", + " # property: a polynomial is zero for all the values of its variables if and only if\n", + " # all its coefficients equal to zero.\n", + " # In such a way one receives equations for each of the collected coefficients, which\n", + " # suffices to make the system solvable for all the 12 components of descan error as\n", + " # variables.\n", + "\n", + " # The received expressions for each component of the new DescanError fully coincide\n", + " # with the ones calculated by hand earlier.\n", + " def detector_px(m: Model4DSTEM):\n", + " ray = m.make_source_ray(\n", + " source_dy=0.,\n", + " source_dx=0.,\n", + " ).ray\n", + " data = m.trace(ray)\n", + " return data['detector'].sampling['detector_px']\n", + "\n", + " scan_pos_y, scan_pos_x = sym.symbols('scan_pos_y scan_pos_x')\n", + " scan_pos = PixelYX(scan_pos_y, scan_pos_x)\n", + "\n", + " model_old = cls.build(params=params_old, scan_pos=scan_pos)\n", + " model_new = cls.build(params=params_new, scan_pos=scan_pos)\n", + "\n", + " det_px_old = detector_px(model_old)\n", + " det_px_new = detector_px(model_new)\n", + "\n", + " expanded_expr_y = sym.expand(det_px_old.y - det_px_new.y)\n", + " expanded_expr_x = sym.expand(det_px_old.x - det_px_new.x)\n", + "\n", + " collected_y = sym.Poly(expanded_expr_y, scan_pos_y, scan_pos_x, params_old.camera_length)\n", + " collected_x = sym.Poly(expanded_expr_x, scan_pos_y, scan_pos_x, params_old.camera_length)\n", + "\n", + " coeff = collected_y.coeffs() + collected_x.coeffs()\n", + "\n", + " solution = sym.solve([eq for eq in coeff], [params_new.descan_error.pxo_pxi,\n", + " params_new.descan_error.pxo_pyi,\n", + " params_new.descan_error.pyo_pxi,\n", + " params_new.descan_error.pyo_pyi,\n", + " params_new.descan_error.sxo_pxi,\n", + " params_new.descan_error.sxo_pyi,\n", + " params_new.descan_error.syo_pxi,\n", + " params_new.descan_error.syo_pyi,\n", + " params_new.descan_error.offpxi,\n", + " params_new.descan_error.offpyi,\n", + " params_new.descan_error.offsxi,\n", + " params_new.descan_error.offsyi],\n", + " simplify=True)\n", + " # apply_function() constructs a function using sympy.lambdify() that allows inserting\n", + " # real values to each expression of the general solution and after that evaluating them\n", + " # numerically. The lambdify() method can be supplemented with the specific\n", + " # numerical backend by inserting e.g. \"numpy\", \"cupy\" or \"jax\" to the optional\n", + " # argument \"modules=\" of sympy.lambdify().\n", + " def apply_function(m: Model4DSTEM, scan_pixel_pitch) -> Model4DSTEM:\n", + " substituted = sym.lambdify(\n", + " [\n", + " params_old.descan_error.pxo_pxi, params_old.descan_error.pxo_pyi,\n", + " params_old.descan_error.pyo_pxi, params_old.descan_error.pyo_pyi,\n", + " params_old.descan_error.sxo_pxi, params_old.descan_error.sxo_pyi,\n", + " params_old.descan_error.syo_pxi, params_old.descan_error.syo_pyi,\n", + " params_old.descan_error.offpxi, params_old.descan_error.offpyi,\n", + " params_old.descan_error.offsxi, params_old.descan_error.offsyi,\n", + " params_old.scan_pixel_pitch, params_new.scan_pixel_pitch\n", + " ], [solution[attr] for attr in [\n", + " params_new.descan_error.pxo_pxi, params_new.descan_error.pxo_pyi,\n", + " params_new.descan_error.pyo_pxi, params_new.descan_error.pyo_pyi,\n", + " params_new.descan_error.sxo_pxi, params_new.descan_error.sxo_pyi,\n", + " params_new.descan_error.syo_pxi, params_new.descan_error.syo_pyi,\n", + " params_new.descan_error.offpxi, params_new.descan_error.offpyi,\n", + " params_new.descan_error.offsxi, params_new.descan_error.offsyi\n", + " ]])\n", + " evaluated = substituted(\n", + " *m.descanner.descan_error,\n", + " m.params.scan_pixel_pitch, scan_pixel_pitch\n", + " )\n", + " new_de = DescanError(*evaluated)\n", + " new_params = m.params.derive(descan_error=new_de)\n", + " new_model = Model4DSTEM.build(new_params, m.scan_pos)\n", + " return new_model\n", + "\n", + " return apply_function" + ] + }, + { + "cell_type": "code", + "execution_count": 241, + "id": "e70359d7-1558-42c7-82cc-bfe1580b5281", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Model4DSTEM(source=PointSource(z=0, semi_conv=0.023, offset_xy=CoordsXY(x=0.0, y=0.0)), scanner=Scanner(z=0.7, scan_pos_x=0.00896241094534078, scan_pos_y=-0.0349238484426736, scan_tilt_x=0.0, scan_tilt_y=0.0), specimen=Plane(z=0.7), descanner=Descanner(z=0.7, scan_pos_x=0.00896241094534078, scan_pos_y=-0.0349238484426736, scan_tilt_x=0.0, scan_tilt_y=0.0, descan_error=DescanError(pxo_pxi=2.37000000000000, pxo_pyi=0, pyo_pxi=0, pyo_pyi=0, sxo_pxi=0, sxo_pyi=0, syo_pxi=0, syo_pyi=0, offpxi=0.345, offpyi=0.0, offsxi=0.0, offsyi=0.0)), detector=Plane(z=3.0), _scan_to_real=Matrix([\n", + "[ 0.00165232554035865, 0.00471909104687317],\n", + "[-0.00471909104687317, 0.00165232554035865]]), _real_to_scan=Matrix([\n", + "[ 66.093021614346, -188.763641874927],\n", + "[188.763641874927, 66.093021614346]]), _detector_to_real=Matrix([\n", + "[-0.0247, 0],\n", + "[ 0, 0.0247]]), _real_to_detector=Matrix([\n", + "[-40.4858299595142, 0],\n", + "[ 0, 40.4858299595142]]), scan_center=PixelYX(y=17, x=13), detector_center=PixelYX(y=11, x=19), xp=)" + ] + }, + "execution_count": 241, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "params = Parameters4DSTEM(\n", + " overfocus=0.7,\n", + " scan_pixel_pitch=0.005,\n", + " scan_center=PixelYX(y=17, x=13),\n", + " scan_rotation=1.234,\n", + " camera_length=2.3,\n", + " detector_pixel_pitch=0.0247,\n", + " detector_center=PixelYX(y=11, x=19),\n", + " detector_rotation=2.134,\n", + " semiconv=0.023,\n", + " flip_factor=-1.,\n", + " descan_error=DescanError(offpxi=.345, pxo_pxi=948)\n", + " )\n", + "mod= Model4DSTEM.build(params=params, scan_pos=PixelYX(y=13, x=7))\n", + "\n", + "desc = _mk_adjust_scan_pixel_pitch()(mod, 2.)\n", + "desc" + ] + }, + { + "cell_type": "code", + "execution_count": 242, + "id": "fd4bf004-4071-4db5-9d06-167f140abcf5", + "metadata": {}, + "outputs": [], + "source": [ + "def symbol_maker(params_cls, postfix, postfix_vars=tuple(), recurse_for=tuple()):\n", + " symbols_dict = {}\n", + " for attr in params_cls.__annotations__.keys():\n", + " cls = params_cls.__annotations__[attr]\n", + " if cls in recurse_for:\n", + " symbols_dict[attr] = symbol_maker(cls, postfix, postfix_vars, recurse_for)\n", + " else:\n", + " if attr in postfix_vars:\n", + " symbols_dict[attr] = sym.symbols(f\"{attr}_{postfix}\")\n", + " else:\n", + " symbols_dict[attr] = sym.symbols(f\"{attr}\")\n", + " return params_cls(**symbols_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 243, + "id": "3d6f0b7f-bc4a-43d6-b7be-d960909795f4", + "metadata": {}, + "outputs": [], + "source": [ + "p = symbol_maker(Parameters4DSTEM, 'new', postfix_vars=['scan_pixel_pitch', *(DescanError.__match_args__)], recurse_for=[DescanError, PixelYX])" + ] + }, + { + "cell_type": "code", + "execution_count": 244, + "id": "75cfca18-2226-4653-8ff5-f7130b26c11a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Parameters4DSTEM(overfocus=overfocus, scan_pixel_pitch=scan_pixel_pitch_new, scan_center=PixelYX(y=y, x=x), scan_rotation=scan_rotation, camera_length=camera_length, detector_pixel_pitch=detector_pixel_pitch, detector_center=PixelYX(y=y, x=x), semiconv=semiconv, flip_factor=flip_factor, descan_error=DescanError(pxo_pxi=pxo_pxi_new, pxo_pyi=pxo_pyi_new, pyo_pxi=pyo_pxi_new, pyo_pyi=pyo_pyi_new, sxo_pxi=sxo_pxi_new, sxo_pyi=sxo_pyi_new, syo_pxi=syo_pxi_new, syo_pyi=syo_pyi_new, offpxi=offpxi_new, offpyi=offpyi_new, offsxi=offsxi_new, offsyi=offsyi_new), detector_rotation=detector_rotation, xp=xp)" + ] + }, + "execution_count": 244, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p" + ] + }, + { + "cell_type": "code", + "execution_count": 245, + "id": "37e63d9a-da50-429e-8779-041ee4c07cda", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Model4DSTEM(source=PointSource(z=0, semi_conv=0.023, offset_xy=CoordsXY(x=0.0, y=0.0)), scanner=Scanner(z=0.7, scan_pos_x=0.00896241094534078, scan_pos_y=-0.0349238484426736, scan_tilt_x=0.0, scan_tilt_y=0.0), specimen=Plane(z=0.7), descanner=Descanner(z=0.7, scan_pos_x=0.00896241094534078, scan_pos_y=-0.0349238484426736, scan_tilt_x=0.0, scan_tilt_y=0.0, descan_error=DescanError(pxo_pxi=2.37000000000000, pxo_pyi=0, pyo_pxi=0, pyo_pyi=0, sxo_pxi=0, sxo_pyi=0, syo_pxi=0, syo_pyi=0, offpxi=0.345, offpyi=0.0, offsxi=0.0, offsyi=0.0)), detector=Plane(z=3.0), _scan_to_real=Matrix([\n", + "[ 0.00165232554035865, 0.00471909104687317],\n", + "[-0.00471909104687317, 0.00165232554035865]]), _real_to_scan=Matrix([\n", + "[ 66.093021614346, -188.763641874927],\n", + "[188.763641874927, 66.093021614346]]), _detector_to_real=Matrix([\n", + "[-0.0247, 0],\n", + "[ 0, 0.0247]]), _real_to_detector=Matrix([\n", + "[-40.4858299595142, 0],\n", + "[ 0, 40.4858299595142]]), scan_center=PixelYX(y=17, x=13), detector_center=PixelYX(y=11, x=19), xp=)" + ] + }, + "execution_count": 245, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "params = Parameters4DSTEM(\n", + " overfocus=0.7,\n", + " scan_pixel_pitch=0.005,\n", + " scan_center=PixelYX(y=17, x=13),\n", + " scan_rotation=1.234,\n", + " camera_length=2.3,\n", + " detector_pixel_pitch=0.0247,\n", + " detector_center=PixelYX(y=11, x=19),\n", + " detector_rotation=2.134,\n", + " semiconv=0.023,\n", + " flip_factor=-1.,\n", + " descan_error=DescanError(offpxi=.345, pxo_pxi=948)\n", + " )\n", + "mod= Model4DSTEM.build(params=params, scan_pos=PixelYX(y=13, x=7))\n", + "\n", + "desc = mod._mk_adjust_scan_pixel_pitch()(mod, 2.)\n", + "desc" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "94e290b9-3c27-4c77-bb7a-e8eee6ffe10e", + "id": "5e652edb-386b-4010-9cf3-74c6b55574d7", + "metadata": {}, + "outputs": [], + "source": [ + "p = symbol_maker(Parameters4DSTEM, 'new', [DescanError, PixelYX])\n", + "f = sym.lambdify([*p.descan_error, p.scan_pixel_pitch], p.descan_error.pxo_pxi+1)\n", + "f(*(1 for i in range(13)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cbb0f7b1-8738-4c12-bc45-c617a0121bc5", "metadata": {}, "outputs": [], "source": [] diff --git a/prototypes/sympy/taylor_expansions.ipynb b/prototypes/sympy/taylor_expansions.ipynb new file mode 100644 index 0000000..8a0bc20 --- /dev/null +++ b/prototypes/sympy/taylor_expansions.ipynb @@ -0,0 +1,285 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 5, + "id": "6434ce42-b3f6-4c5c-9b8c-51cf2df84c02", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy.polys.ring_series import rs_series\n", + "from sympy.polys import ring, QQ, RR, CC\n", + "from sympy.polys.rings import sring\n", + "import sympy as sym" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2033c83d-cf0c-4fdc-963b-a219927a9e9b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\mathtt{\\text{1/24}} {x}^{4} - \\mathtt{\\text{1/6}} {x}^{3} + \\mathtt{\\text{1/2}} {x}^{2} - x + \\mathtt{\\text{1}}$" + ], + "text/plain": [ + "1/24*x**4 - 1/6*x**3 + 1/2*x**2 - x + 1" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x, y, z = sym.symbols('x y z')\n", + "rs_series(sym.exp(-x), x, 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "56484714-22de-4aaa-b1ba-61f2cc793276", + "metadata": {}, + "outputs": [], + "source": [ + "import sympy as sp\n", + "from itertools import product\n", + "def multiindices(n, order):\n", + " \"\"\"All alpha in N^n with |alpha| <= order.\"\"\"\n", + " out = []\n", + " for alpha in product(range(order + 1), repeat=n):\n", + " if sum(alpha) <= order:\n", + " out.append(alpha)\n", + " return out\n", + " \n", + "def diff_multi(expr, vars_, alpha):\n", + " \"\"\"Mixed partial d^alpha expr.\"\"\"\n", + " out = expr\n", + " for v, a in zip(vars_, alpha):\n", + " if a:\n", + " out = sp.diff(out, v, a)\n", + " return sp.simplify(out)\n", + "\n", + "def taylor_polynomial(expr, vars_, base_, inc_, order):\n", + " \"\"\"\n", + " Multivariate Taylor polynomial of expr(vars_) around base_,\n", + " using increment symbols inc_.\n", + " \"\"\"\n", + " n = len(vars_)\n", + " subs_base = dict(zip(vars_, base_))\n", + " poly = 0\n", + " for alpha in multiindices(n, order):\n", + " coeff = diff_multi(expr, vars_, alpha).subs(subs_base)\n", + " alpha_fact = sp.prod(sp.factorial(a) for a in alpha)\n", + " monomial = sp.prod(h**a for h, a in zip(inc_, alpha))\n", + " poly += coeff * monomial / alpha_fact\n", + " return sp.expand(poly)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "23d015c9-db55-4356-8395-72f7b2b93bed", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle \\frac{hx^{5} y_{0}^{5} \\cos{\\left(x_{0} y_{0} \\right)}}{120} + \\frac{hx^{5} e^{x_{0}}}{120} + \\frac{hx^{4} hy x_{0} y_{0}^{4} \\cos{\\left(x_{0} y_{0} \\right)}}{24} + \\frac{hx^{4} hy y_{0}^{3} \\sin{\\left(x_{0} y_{0} \\right)}}{6} + \\frac{hx^{4} y_{0}^{4} \\sin{\\left(x_{0} y_{0} \\right)}}{24} + \\frac{hx^{4} e^{x_{0}}}{24} + \\frac{hx^{3} hy^{2} x_{0}^{2} y_{0}^{3} \\cos{\\left(x_{0} y_{0} \\right)}}{12} + \\frac{hx^{3} hy^{2} x_{0} y_{0}^{2} \\sin{\\left(x_{0} y_{0} \\right)}}{2} - \\frac{hx^{3} hy^{2} y_{0} \\cos{\\left(x_{0} y_{0} \\right)}}{2} + \\frac{hx^{3} hy x_{0} y_{0}^{3} \\sin{\\left(x_{0} y_{0} \\right)}}{6} - \\frac{hx^{3} hy y_{0}^{2} \\cos{\\left(x_{0} y_{0} \\right)}}{2} - \\frac{hx^{3} y_{0}^{3} \\cos{\\left(x_{0} y_{0} \\right)}}{6} + \\frac{hx^{3} e^{x_{0}}}{6} + \\frac{hx^{2} hy^{3} x_{0}^{3} y_{0}^{2} \\cos{\\left(x_{0} y_{0} \\right)}}{12} + \\frac{hx^{2} hy^{3} x_{0}^{2} y_{0} \\sin{\\left(x_{0} y_{0} \\right)}}{2} - \\frac{hx^{2} hy^{3} x_{0} \\cos{\\left(x_{0} y_{0} \\right)}}{2} + \\frac{hx^{2} hy^{2} x_{0}^{2} y_{0}^{2} \\sin{\\left(x_{0} y_{0} \\right)}}{4} - hx^{2} hy^{2} x_{0} y_{0} \\cos{\\left(x_{0} y_{0} \\right)} - \\frac{hx^{2} hy^{2} \\sin{\\left(x_{0} y_{0} \\right)}}{2} - \\frac{hx^{2} hy x_{0} y_{0}^{2} \\cos{\\left(x_{0} y_{0} \\right)}}{2} - hx^{2} hy y_{0} \\sin{\\left(x_{0} y_{0} \\right)} - \\frac{hx^{2} y_{0}^{2} \\sin{\\left(x_{0} y_{0} \\right)}}{2} + \\frac{hx^{2} e^{x_{0}}}{2} + \\frac{hx hy^{4} x_{0}^{4} y_{0} \\cos{\\left(x_{0} y_{0} \\right)}}{24} + \\frac{hx hy^{4} x_{0}^{3} \\sin{\\left(x_{0} y_{0} \\right)}}{6} + \\frac{hx hy^{3} x_{0}^{3} y_{0} \\sin{\\left(x_{0} y_{0} \\right)}}{6} - \\frac{hx hy^{3} x_{0}^{2} \\cos{\\left(x_{0} y_{0} \\right)}}{2} - \\frac{hx hy^{2} x_{0}^{2} y_{0} \\cos{\\left(x_{0} y_{0} \\right)}}{2} - hx hy^{2} x_{0} \\sin{\\left(x_{0} y_{0} \\right)} - hx hy x_{0} y_{0} \\sin{\\left(x_{0} y_{0} \\right)} + hx hy \\cos{\\left(x_{0} y_{0} \\right)} + hx y_{0} \\cos{\\left(x_{0} y_{0} \\right)} + hx e^{x_{0}} + \\frac{hy^{5} x_{0}^{5} \\cos{\\left(x_{0} y_{0} \\right)}}{120} + \\frac{hy^{4} x_{0}^{4} \\sin{\\left(x_{0} y_{0} \\right)}}{24} - \\frac{hy^{3} x_{0}^{3} \\cos{\\left(x_{0} y_{0} \\right)}}{6} - \\frac{hy^{2} x_{0}^{2} \\sin{\\left(x_{0} y_{0} \\right)}}{2} + hy x_{0} \\cos{\\left(x_{0} y_{0} \\right)} + e^{x_{0}} + \\sin{\\left(x_{0} y_{0} \\right)}$" + ], + "text/plain": [ + " 5 5 5 x₀ 4 4 4 3 ↪\n", + "hx ⋅y₀ ⋅cos(x₀⋅y₀) hx ⋅ℯ hx ⋅hy⋅x₀⋅y₀ ⋅cos(x₀⋅y₀) hx ⋅hy⋅y₀ ⋅sin(x₀⋅y₀ ↪\n", + "────────────────── + ─────── + ──────────────────────── + ──────────────────── ↪\n", + " 120 120 24 6 ↪\n", + "\n", + "↪ 4 4 4 x₀ 3 2 2 3 3 2 ↪\n", + "↪ ) hx ⋅y₀ ⋅sin(x₀⋅y₀) hx ⋅ℯ hx ⋅hy ⋅x₀ ⋅y₀ ⋅cos(x₀⋅y₀) hx ⋅hy ⋅x₀⋅y ↪\n", + "↪ ─ + ────────────────── + ─────── + ────────────────────────── + ──────────── ↪\n", + "↪ 24 24 12 ↪\n", + "\n", + "↪ 2 3 2 3 3 3 ↪\n", + "↪ ₀ ⋅sin(x₀⋅y₀) hx ⋅hy ⋅y₀⋅cos(x₀⋅y₀) hx ⋅hy⋅x₀⋅y₀ ⋅sin(x₀⋅y₀) hx ⋅hy⋅y₀ ↪\n", + "↪ ───────────── - ───────────────────── + ──────────────────────── - ───────── ↪\n", + "↪ 2 2 6 ↪\n", + "\n", + "↪ 2 3 3 3 x₀ 2 3 3 2 ↪\n", + "↪ ⋅cos(x₀⋅y₀) hx ⋅y₀ ⋅cos(x₀⋅y₀) hx ⋅ℯ hx ⋅hy ⋅x₀ ⋅y₀ ⋅cos(x₀⋅y₀) h ↪\n", + "↪ ──────────── - ────────────────── + ─────── + ────────────────────────── + ─ ↪\n", + "↪ 2 6 6 12 ↪\n", + "\n", + "↪ 2 3 2 2 3 2 2 2 2 ↪\n", + "↪ x ⋅hy ⋅x₀ ⋅y₀⋅sin(x₀⋅y₀) hx ⋅hy ⋅x₀⋅cos(x₀⋅y₀) hx ⋅hy ⋅x₀ ⋅y₀ ⋅sin(x₀⋅y₀ ↪\n", + "↪ ──────────────────────── - ───────────────────── + ───────────────────────── ↪\n", + "↪ 2 2 4 ↪\n", + "\n", + "↪ 2 2 2 2 ↪\n", + "↪ ) 2 2 hx ⋅hy ⋅sin(x₀⋅y₀) hx ⋅hy⋅x₀⋅y₀ ⋅cos(x₀⋅y₀) ↪\n", + "↪ ─ - hx ⋅hy ⋅x₀⋅y₀⋅cos(x₀⋅y₀) - ────────────────── - ──────────────────────── ↪\n", + "↪ 2 2 ↪\n", + "\n", + "↪ 2 2 2 x₀ 4 4 ↪\n", + "↪ 2 hx ⋅y₀ ⋅sin(x₀⋅y₀) hx ⋅ℯ hx⋅hy ⋅x₀ ⋅y₀⋅cos(x ↪\n", + "↪ - hx ⋅hy⋅y₀⋅sin(x₀⋅y₀) - ────────────────── + ─────── + ─────────────────── ↪\n", + "↪ 2 2 24 ↪\n", + "\n", + "↪ 4 3 3 3 3 2 ↪\n", + "↪ ₀⋅y₀) hx⋅hy ⋅x₀ ⋅sin(x₀⋅y₀) hx⋅hy ⋅x₀ ⋅y₀⋅sin(x₀⋅y₀) hx⋅hy ⋅x₀ ⋅cos(x₀ ↪\n", + "↪ ───── + ───────────────────── + ──────────────────────── - ───────────────── ↪\n", + "↪ 6 6 2 ↪\n", + "\n", + "↪ 2 2 ↪\n", + "↪ ⋅y₀) hx⋅hy ⋅x₀ ⋅y₀⋅cos(x₀⋅y₀) 2 ↪\n", + "↪ ──── - ──────────────────────── - hx⋅hy ⋅x₀⋅sin(x₀⋅y₀) - hx⋅hy⋅x₀⋅y₀⋅sin(x₀⋅ ↪\n", + "↪ 2 ↪\n", + "\n", + "↪ 5 5 ↪\n", + "↪ x₀ hy ⋅x₀ ⋅cos(x₀⋅y₀) hy ↪\n", + "↪ y₀) + hx⋅hy⋅cos(x₀⋅y₀) + hx⋅y₀⋅cos(x₀⋅y₀) + hx⋅ℯ + ────────────────── + ── ↪\n", + "↪ 120 ↪\n", + "\n", + "↪ 4 4 3 3 2 2 ↪\n", + "↪ ⋅x₀ ⋅sin(x₀⋅y₀) hy ⋅x₀ ⋅cos(x₀⋅y₀) hy ⋅x₀ ⋅sin(x₀⋅y₀) ↪\n", + "↪ ──────────────── - ────────────────── - ────────────────── + hy⋅x₀⋅cos(x₀⋅y₀ ↪\n", + "↪ 24 6 2 ↪\n", + "\n", + "↪ \n", + "↪ x₀ \n", + "↪ ) + ℯ + sin(x₀⋅y₀)\n", + "↪ " + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x, y, dx, dy = sp.symbols('x y dx dy')\n", + "# Expansion point\n", + "x0, y0, dx0, dy0 = sp.symbols('x0 y0 dx0 dy0')\n", + "# Increment variables\n", + "hx, hy, hdx, hdy = sp.symbols('hx hy hdx hdy')\n", + "# Example ray-map component after a component\n", + "f = sp.sin(x*y) + sym.exp(x)\n", + "poly = taylor_polynomial(\n", + " expr=f,\n", + " vars_=(x, y, dx, dy),\n", + " base_=(x0, y0, dx0, dy0),\n", + " inc_=(hx, hy, hdx, hdy),\n", + " order=5,\n", + ")\n", + "sym.init_printing(use_latex=True)\n", + "poly" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "1a400ad9-4608-4a50-9566-ba1df8f51277", + "metadata": {}, + "outputs": [], + "source": [ + "expr = (\n", + "sp.exp(x0)\n", + "+ sp.exp(x0)*hx\n", + "+ sp.Rational(1,2)*sp.exp(x0)*hx**2\n", + "+ sp.Rational(1,6)*sp.exp(x0)*hx**3\n", + "+ sp.Rational(1,24)*sp.exp(x0)*hx**4\n", + "+ sp.Rational(1,120)*sp.exp(x0)*hx**5\n", + "\n", + "+ hx*hy*sp.cos(x0*y0)\n", + "+ x0*hy*sp.cos(x0*y0)\n", + "- sp.Rational(1,2)*hx**2*x0*hy**3*sp.cos(x0*y0)\n", + "- sp.Rational(1,2)*hx*x0**2*hy**3*sp.cos(x0*y0)\n", + "- sp.Rational(1,6)*x0**3*hy**3*sp.cos(x0*y0)\n", + "+ sp.Rational(1,120)*x0**5*hy**5*sp.cos(x0*y0)\n", + "+ hx*y0*sp.cos(x0*y0)\n", + "- sp.Rational(1,2)*hx**3*hy**2*y0*sp.cos(x0*y0)\n", + "- hx**2*x0*hy**2*y0*sp.cos(x0*y0)\n", + "- sp.Rational(1,2)*hx*x0**2*hy**2*y0*sp.cos(x0*y0)\n", + "+ sp.Rational(1,24)*hx*x0**4*hy**4*y0*sp.cos(x0*y0)\n", + "- sp.Rational(1,2)*hx**3*hy*y0**2*sp.cos(x0*y0)\n", + "- sp.Rational(1,2)*hx**2*x0*hy*y0**2*sp.cos(x0*y0)\n", + "+ sp.Rational(1,12)*hx**2*x0**3*hy**3*y0**2*sp.cos(x0*y0)\n", + "- sp.Rational(1,6)*hx**3*y0**3*sp.cos(x0*y0)\n", + "+ sp.Rational(1,12)*hx**3*x0**2*hy**2*y0**3*sp.cos(x0*y0)\n", + "+ sp.Rational(1,24)*hx**4*x0*hy*y0**4*sp.cos(x0*y0)\n", + "+ sp.Rational(1,120)*hx**5*y0**5*sp.cos(x0*y0)\n", + "\n", + "+ sp.sin(x0*y0)\n", + "- sp.Rational(1,2)*hx**2*hy**2*sp.sin(x0*y0)\n", + "- hx*x0*hy**2*sp.sin(x0*y0)\n", + "- sp.Rational(1,2)*x0**2*hy**2*sp.sin(x0*y0)\n", + "+ sp.Rational(1,6)*hx*x0**3*hy**4*sp.sin(x0*y0)\n", + "+ sp.Rational(1,24)*x0**4*hy**4*sp.sin(x0*y0)\n", + "- hx**2*hy*y0*sp.sin(x0*y0)\n", + "- hx*x0*hy*y0*sp.sin(x0*y0)\n", + "+ sp.Rational(1,2)*hx**2*x0**2*hy**3*y0*sp.sin(x0*y0)\n", + "+ sp.Rational(1,6)*hx*x0**3*hy**3*y0*sp.sin(x0*y0)\n", + "- sp.Rational(1,2)*hx**2*y0**2*sp.sin(x0*y0)\n", + "+ sp.Rational(1,2)*hx**3*x0*hy**2*y0**2*sp.sin(x0*y0)\n", + "+ sp.Rational(1,4)*hx**2*x0**2*hy**2*y0**2*sp.sin(x0*y0)\n", + "+ sp.Rational(1,6)*hx**4*hy*y0**3*sp.sin(x0*y0)\n", + "+ sp.Rational(1,6)*hx**3*x0*hy*y0**3*sp.sin(x0*y0)\n", + "+ sp.Rational(1,24)*hx**4*y0**4*sp.sin(x0*y0)\n", + "\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "ec96f60f-1994-4aab-be2b-7f448fe40838", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr == poly" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0472736a-cb2a-43d1-9260-50c964656108", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/microscope_calibration/common/model.py b/src/microscope_calibration/common/model.py index 9fa88d5..080e5b4 100644 --- a/src/microscope_calibration/common/model.py +++ b/src/microscope_calibration/common/model.py @@ -1,14 +1,18 @@ -from typing import Optional, NamedTuple, Union, Any +from typing import Optional, NamedTuple, Union, Any, TypeVar +from collections.abc import Container from numbers import Number from types import ModuleType from collections import OrderedDict +from functools import cache +from copy import deepcopy import numpy.typing as npt import sympy as sym import jax; jax.config.update("jax_enable_x64", True) # noqa import jax_dataclasses as jdc -import jax.numpy as jnp +# import jax.numpy as jnp +import numpy as np from jax.errors import TracerBoolConversionError from temgym_core.ray import Ray @@ -20,39 +24,46 @@ # Jax-compatible versions of libertem.corrections.coordinates functions -def scale(factor, xp): - return xp.eye(2) * factor +def scale(factor): + return sym.eye(2) * factor -def rotate(radians, xp): - # https://en.wikipedia.org/wiki/Rotation_matrix - # y, x instead of x, y - # radians = jnp.astype(radians, jnp.float64) - return xp.array( - [(xp.cos(radians), xp.sin(radians)), (-xp.sin(radians), xp.cos(radians))] - ) +def rotate(radians): + return sym.rot_givens(0, 1, radians, dim=2) # The flip_factor is introduced to make it differentiable -def flip_y(xp, flip_factor: float = -1.0): - return xp.array([(flip_factor, 0), (0, 1)], dtype=xp.float64) +def flip_y(flip_factor: sym.Float = sym.S(-1.0)): + return sym.Matrix([[flip_factor, 0], [0, 1]]) -def identity(xp): - return xp.eye(2, dtype=xp.float64) +def identity(): + return sym.eye(2) -def scale_rotate_flip(mat: npt.ArrayLike, xp): +def scale_rotate_flip(mat: sym.Matrix): """ Deconstruct a matrix generated with scale() @ rotate() @ flip_y() - into the individual parameters + into the individual parameters. + + Use sympy functions to be able to handle symbols in e.g. sin() or cos() + calculations. + + Note that the check if the norms are equal and if the angles are consistent + will raise value error for arbitrary symbols due to indeterminacy. Symbols with + declared relations between them pass the check. The check is the simplified + and combined version of the old ones, which are commented out. It was developed + by solving the system of equations for the symbolic case. TODO: test cases + if the new check is fully equivivalent to the old ones in practice. """ - scale_y = xp.linalg.norm(mat[:, 0]) - scale_x = xp.linalg.norm(mat[:, 1]) - if not xp.allclose(scale_y, scale_x): - raise ValueError(f"y scale {scale_y} and x scale {scale_x} are different.") + scale_y = mat[:, 0].norm() + scale_x = mat[:, 1].norm() + + # if not scale_x.equals(scale_y): + # raise ValueError(f"y scale {scale_y} and x scale {scale_x} are different.") scan_rot_flip = mat / scale_y + # 2D cross product flip_factor = ( scan_rot_flip[0, 0] * scan_rot_flip[1, 1] @@ -60,24 +71,26 @@ def scale_rotate_flip(mat: npt.ArrayLike, xp): ) # undo flip_y rot = scan_rot_flip.copy() - if xp is jnp: - rot = rot.at[:, 0].set(rot[:, 0] * flip_factor) - else: - rot[:, 0] = rot[:, 0] * flip_factor + rot[:, 0] = rot[:, 0] * flip_factor + rot = sym.simplify(rot) + + angle1 = sym.atan2(-rot[1, 0], rot[0, 0]) + angle2 = sym.atan2(rot[0, 1], rot[1, 1]) - angle1 = xp.arctan2(-rot[1, 0], rot[0, 0]) - angle2 = xp.arctan2(rot[0, 1], rot[1, 1]) + if (sym.Or(sym.And(sympy_equals(mat[0, 0], mat[1, 1]), sympy_equals(mat[0, 1], -mat[1, 0])), + sym.And(sympy_equals(mat[0, 0], -mat[1, 1]), sympy_equals(mat[0, 1], mat[1, 0]))) + is not sym.S.true): + raise ValueError(f"y scale {scale_y} and x scale {scale_x} are different or rotation " + f"angle 1 {angle1} and rotation angle 2 {angle2} are inconsistent.") # So far not reached in tests since inconsistencies are caught as shear before - if not xp.allclose( - xp.array((xp.sin(angle1), xp.cos(angle1))), - xp.array((xp.sin(angle2), xp.cos(angle2))), - ): - raise ValueError( - f"Rotation angle 1 {angle1} and rotation angle 2 {angle2} are inconsistent." - ) + # if not sym.Matrix([sym.sin(angle1), sym.cos(angle1)]).equals(sym.Matrix([sym.sin(angle2), + # sym.cos(angle2)])): + # raise ValueError( + # f"Rotation angle 1 {angle1} and rotation angle 2 {angle2} are inconsistent." + # ) - return (scale_y, angle1, flip_factor) + return (sym.simplify(scale_y), sym.simplify(angle1), sym.simplify(flip_factor)) def is_sympy(expr: Any) -> bool: @@ -160,6 +173,140 @@ def equals(ray1: Ray, ray2: Ray) -> bool: return True +T = TypeVar("T") + + +def symbol_maker( + params_cls: type[T], postfix: str | None = None, + recurse_for: Container = tuple()) -> T: + """ + Declare sympy symbols for each attribute of a given parameter class. + + Use __annotations__ to access the class attributes and their types. + Make a dictionary of sympy symbols assigned to each class attribute of + a primitive type. The symbol names are declared as names of the attributes + with an appropriate postfix. After that construct a class instance with + the symbolic variables as parameters. + + Parameters + ---------- + params_cls: class + class which needs symbolic variables to be assigned to its parameters + postfix: str + postfix for each symbol name to specify the parameters (e.g. 'new' or 'old') + recurse_for: tuple + list of class attributes of non-primitive types to receive symbols for each component + + Returns + ------- + class instance + instance of the class with symbols as parameters + """ + def symbol_maker_inner(params_cls: type[T], postfix: str | None, + recurse_for: Container, index: int) -> tuple[int, T]: + if hasattr(params_cls, '__annotations__'): + symbols_dict = {} + for attr in params_cls.__annotations__.keys(): + cls = params_cls.__annotations__[attr] + if cls in recurse_for: + (index, symbols_dict[attr]) = symbol_maker_inner( + cls, postfix, recurse_for, index + ) + elif issubclass(cls, Number): + sym_name = attr if postfix is None else f"{attr}_{postfix}" + sym_name = f"{sym_name}_{index}" + symbols_dict[attr] = sym.Symbol(sym_name) + index += 1 + else: + raise TypeError(f"Can't generate symbol for type {cls}.") + return (index, params_cls(**symbols_dict)) + elif issubclass(params_cls, Number): + attr = params_cls.__name__ + sym_name = attr if postfix is None else f"{attr}_{postfix}" + sym_name = f"{sym_name}_{index}" + index += 1 + return (index, sym.Symbol(sym_name)) + else: + raise TypeError(f"Can't generate symbol for type {params_cls}.") + + (_, res) = symbol_maker_inner( + params_cls=params_cls, + postfix=postfix, + recurse_for=recurse_for, + index=0 + ) + return res + + +SymbolJaxTree = TypeVar("SymbolJaxTree") + + +def lambdify(inp: SymbolJaxTree, outp: SymbolJaxTree, **kwargs): + inp_leaves, inp_treedef = jax.tree.flatten(inp) + outp_leaves, outp_treedef = jax.tree.flatten(outp) + + inp_indices = [] + inp_symbols = [] + inp_dups = {} + + for i, leave in enumerate(inp_leaves): + if isinstance(leave, sym.Symbol): + if leave in inp_symbols: + inp_dups[i] = inp_symbols.index(leave) + else: + inp_indices.append(i) + inp_symbols.append(leave) + elif is_sympy(leave) and not isinstance(leave, (sym.NumberSymbol, sym.Number)): + raise ValueError( + f"SymPy leave {leave} found that is not a symbol or a constant number. " + "Only symbols and constants are allowed in the input definition." + ) + + outp_indices = [] + outp_exprs = [] + + for i, leave in enumerate(outp_leaves): + if isinstance(leave, sym.Basic): + outp_indices.append(i) + outp_exprs.append(leave) + + inp_indices_set = set(inp_indices) + inner_f = sym.lambdify(inp_symbols, outp_exprs, **kwargs) + + def outer(ii): + ii_leaves, ii_treedef = jax.tree.flatten_with_path(ii) + if ii_treedef != inp_treedef: + raise ValueError( + f'Tree definition of input {ii_treedef} does not match expected ' + f'tree definition {inp_treedef}.' + ) + for i, (path, leave) in enumerate(ii_leaves): + if i not in inp_indices_set: + if i in inp_dups: + orig_i = inp_dups[i] + orig_path, orig_leave = ii_leaves[orig_i] + if orig_leave != leave: + raise ValueError( + f"Input value {leave} with path {path} was a duplicate symbol in " + "original input " + f"but is now not matching the input value {orig_leave} at {orig_path}" + ) + elif leave != inp_leaves[i]: + raise ValueError( + f"Constant value {leave} doesn't match reference input " + f"{inp_leaves[i]} for {path}.") + + ii_vals = [ii_leaves[i][1] for i in inp_indices] + oo_inner = inner_f(*ii_vals) + outp = deepcopy(outp_leaves) + for i, val in enumerate(oo_inner): + index = outp_indices[i] + outp[index] = val + return jax.tree.unflatten(outp_treedef, outp) + + return outer + + # TODO use LiberTEM-schema later @jdc.pytree_dataclass class Parameters4DSTEM: @@ -174,7 +321,7 @@ class Parameters4DSTEM: flip_factor: float # 1.: no flip; -1.: flip descan_error: DescanError = DescanError() detector_rotation: float = 0.0 # rad - xp: ModuleType = jnp + xp: ModuleType = np def derive( self, @@ -281,10 +428,10 @@ def adjust_scan_rotation(self, scan_rotation: float) -> "Parameters4DSTEM": xp = self.xp # Rotate the input direction - pxo_pyi, pxo_pxi = rotate(angle, xp=xp) @ xp.array((de.pxo_pyi, de.pxo_pxi)) - pyo_pyi, pyo_pxi = rotate(angle, xp=xp) @ xp.array((de.pyo_pyi, de.pyo_pxi)) - sxo_pyi, sxo_pxi = rotate(angle, xp=xp) @ xp.array((de.sxo_pyi, de.sxo_pxi)) - syo_pyi, syo_pxi = rotate(angle, xp=xp) @ xp.array((de.syo_pyi, de.syo_pxi)) + pxo_pyi, pxo_pxi = rotate(angle) @ xp.array((de.pxo_pyi, de.pxo_pxi)) + pyo_pyi, pyo_pxi = rotate(angle) @ xp.array((de.pyo_pyi, de.pyo_pxi)) + sxo_pyi, sxo_pxi = rotate(angle) @ xp.array((de.sxo_pyi, de.sxo_pxi)) + syo_pyi, syo_pxi = rotate(angle) @ xp.array((de.syo_pyi, de.syo_pxi)) new_de = DescanError( pxo_pyi=pxo_pyi, pyo_pyi=pyo_pyi, @@ -372,12 +519,12 @@ def adjust_detector_rotation(self, detector_rotation: float) -> "Parameters4DSTE xp = self.xp # rotate the output direction - pyo_pyi, pxo_pyi = rotate(angle, xp=xp) @ xp.array((de.pyo_pyi, de.pxo_pyi)) - pyo_pxi, pxo_pxi = rotate(angle, xp=xp) @ xp.array((de.pyo_pxi, de.pxo_pxi)) - syo_pyi, sxo_pyi = rotate(angle, xp=xp) @ xp.array((de.syo_pyi, de.sxo_pyi)) - syo_pxi, sxo_pxi = rotate(angle, xp=xp) @ xp.array((de.syo_pxi, de.sxo_pxi)) - offpyi, offpxi = rotate(angle, xp=xp) @ xp.array((de.offpyi, de.offpxi)) - offsyi, offsxi = rotate(angle, xp=xp) @ xp.array((de.offsyi, de.offsxi)) + pyo_pyi, pxo_pyi = rotate(angle) @ xp.array((de.pyo_pyi, de.pxo_pyi)) + pyo_pxi, pxo_pxi = rotate(angle) @ xp.array((de.pyo_pxi, de.pxo_pxi)) + syo_pyi, sxo_pyi = rotate(angle) @ xp.array((de.syo_pyi, de.sxo_pyi)) + syo_pxi, sxo_pxi = rotate(angle) @ xp.array((de.syo_pxi, de.sxo_pxi)) + offpyi, offpxi = rotate(angle) @ xp.array((de.offpyi, de.offpxi)) + offsyi, offsxi = rotate(angle) @ xp.array((de.offsyi, de.offsxi)) new_de = DescanError( pxo_pyi=pxo_pyi, pyo_pyi=pyo_pyi, @@ -408,8 +555,7 @@ def adjust_flip_factor(self, flip_factor: float) -> "Parameters4DSTEM": if flip_factor != self.flip_factor: # Rotate into detector directions, flip, then rotate back - trans = rotate(angle, xp=xp) @ flip_y(xp, flip_factor/self.flip_factor) @ rotate(-angle, - xp=xp) + trans = rotate(angle) @ flip_y(flip_factor/self.flip_factor) @ rotate(-angle) # transform the output direction pyo_pyi, pxo_pyi = trans @ xp.array((de.pyo_pyi, de.pxo_pyi)) pyo_pxi, pxo_pxi = trans @ xp.array((de.pyo_pxi, de.pxo_pxi)) @@ -566,8 +712,8 @@ def scan_to_real(self, pixels: PixelYX, _one: float = 1.0) -> CoordXY: return CoordXY(y=y, x=x) def real_to_scan(self, coords: CoordXY, _one: float = 1.0) -> PixelYX: - xp = self.xp - (y, x) = self._real_to_scan @ xp.array((coords.y, coords.x)) + # xp = self.xp + (y, x) = self._real_to_scan @ sym.Matrix([coords.y, coords.x]) return PixelYX(y=y + self.scan_center.y * _one, x=x + self.scan_center.x * _one) def detector_to_real(self, pixels: PixelYX, _one: float = 1.0) -> CoordXY: @@ -581,8 +727,8 @@ def detector_to_real(self, pixels: PixelYX, _one: float = 1.0) -> CoordXY: return CoordXY(y=y, x=x) def real_to_detector(self, coords: CoordXY, _one: float = 1.0) -> PixelYX: - xp = self.xp - (y, x) = self._real_to_detector @ xp.array((coords.y, coords.x)) + # xp = self.xp + (y, x) = self._real_to_detector @ sym.Matrix((coords.y, coords.x)) return PixelYX( y=y + self.detector_center.y * _one, x=x + self.detector_center.x * _one ) @@ -594,25 +740,24 @@ def build( scan_pos: PixelYX, specimen: Optional[Component] = None, ) -> "Model4DSTEM": - scan_to_real = rotate(params.scan_rotation, xp=params.xp) @ scale(params.scan_pixel_pitch, - xp=params.xp) - real_to_scan = scale(1 / params.scan_pixel_pitch, xp=params.xp) @ rotate( - -params.scan_rotation, xp=params.xp) - scan_y, scan_x = scan_to_real @ params.xp.array( - ( + scan_to_real = rotate(params.scan_rotation) @ scale(params.scan_pixel_pitch) + real_to_scan = scale(1 / params.scan_pixel_pitch) @ rotate( + -params.scan_rotation) + scan_y, scan_x = scan_to_real @ sym.Matrix( + [ scan_pos.y - params.scan_center.y, scan_pos.x - params.scan_center.x, - ) + ] ) detector_to_real = ( - scale(params.detector_pixel_pitch, xp=params.xp) - @ rotate(params.detector_rotation, xp=params.xp) - @ flip_y(flip_factor=params.flip_factor, xp=params.xp) + scale(params.detector_pixel_pitch) + @ rotate(params.detector_rotation) + @ flip_y(flip_factor=params.flip_factor) ) real_to_detector = ( - flip_y(flip_factor=1 / params.flip_factor, xp=params.xp) - @ rotate(-params.detector_rotation, xp=params.xp) - @ scale(1 / params.detector_pixel_pitch, xp=params.xp) + flip_y(flip_factor=1 / params.flip_factor) + @ rotate(-params.detector_rotation) + @ scale(1 / params.detector_pixel_pitch) ) if specimen is None: specimen = Plane(z=params.overfocus) @@ -644,13 +789,12 @@ def build( @property def params(self) -> Parameters4DSTEM: - scan_scale, scan_rotation, scan_flip = scale_rotate_flip(self._scan_to_real, xp=self.xp) + scan_scale, scan_rotation, scan_flip = scale_rotate_flip(self._scan_to_real) # FIXME assert close to 1 # assert scan_flip is False detector_scale, detector_rotation, detector_flip = scale_rotate_flip( - self._detector_to_real, xp=self.xp - ) - assert self.xp.allclose(detector_rotation, 0.0) + self._detector_to_real) + # assert self.xp.allclose(detector_rotation, 0.0) return Parameters4DSTEM( overfocus=self.specimen.z - self.source.z, scan_pixel_pitch=scan_scale, @@ -743,7 +887,7 @@ def trace(self, ray: Ray) -> Result4DSTEM: comp, r = run_result.pop(0) try: assert isinstance(comp, Propagator) - assert comp.distance == 0.0 + # assert comp.distance == 0.0 assert isinstance(r, Ray) assert equals(r, result["scanner"].ray) except TracerBoolConversionError: @@ -766,7 +910,7 @@ def trace(self, ray: Ray) -> Result4DSTEM: comp, r = run_result.pop(0) try: assert isinstance(comp, Propagator) - assert comp.distance == 0.0 + # assert comp.distance == 0.0 assert equals(r, result["specimen"].ray) except TracerBoolConversionError: pass @@ -800,6 +944,241 @@ def trace(self, ray: Ray) -> Result4DSTEM: assert len(run_result) == 0 return result + @classmethod + @cache + def _mk_adjust_scan_pixel_pitch(cls): + """ + This function first finds a general symbolic expression for each component of the + new descan error given old parameteres as symbols, then converts the sympy + expressions for each component into a function that allows fast numeric evaluation. + After that the new model is constructed. + """ + + params_old = symbol_maker(Parameters4DSTEM, 'old', recurse_for=[DescanError, PixelYX]) + params_new_tmp = symbol_maker(Parameters4DSTEM, 'new', recurse_for=[DescanError, PixelYX]) + + params_new = params_old.derive( + descan_error=params_new_tmp.descan_error, + scan_pixel_pitch=params_new_tmp.scan_pixel_pitch + ) + + # Use trace() to calculate detector_px() for the old and for the new parameters. + # Then set the results equal to each other and solve the equation with respect + # to the new descan error. For this purpose construct two expressions - for y and x + # coordiates respectively - and apply sympy.solve(). + # Note that the equations should hold for every value of the scan position (in particular, + # scan_pos_y scan_pos_x), and also for every value of the camera length. To implement that, + # expand the expressions, then rearange them to be polynomials in the variables scan_pos_y, + # scan_pos_x and camera_length, which automatically means collecting the coefficients + # of each variable through the usage of sympy.Poly(). Finally, set the collected + # coefficients of the polynomials to zero, which mathematically implies the desired + # property: a polynomial is zero for all the values of its variables if and only if + # all its coefficients equal to zero. + # In such a way one receives equations for each of the collected coefficients, which + # suffices to make the system solvable for all the 12 components of descan error as + # variables. + + # The received expressions for each component of the new DescanError fully coincide + # with the ones calculated by hand earlier. + + def detector_px(m: Model4DSTEM): + ray = m.make_source_ray( + source_dy=0., + source_dx=0., + ).ray + data = m.trace(ray) + return data['detector'].sampling['detector_px'] + + scan_pos_y, scan_pos_x = sym.symbols('scan_pos_y scan_pos_x') + scan_pos = PixelYX(scan_pos_y, scan_pos_x) + + model_old = cls.build(params=params_old, scan_pos=scan_pos) + model_new = cls.build(params=params_new, scan_pos=scan_pos) + + det_px_old = detector_px(model_old) + det_px_new = detector_px(model_new) + + expanded_expr_y = sym.expand(det_px_old.y - det_px_new.y) + expanded_expr_x = sym.expand(det_px_old.x - det_px_new.x) + + collected_y = sym.Poly(expanded_expr_y, scan_pos_y, scan_pos_x, params_old.camera_length) + collected_x = sym.Poly(expanded_expr_x, scan_pos_y, scan_pos_x, params_old.camera_length) + + coeff = collected_y.coeffs() + collected_x.coeffs() + + solution = sym.solve([eq for eq in coeff], [*params_new.descan_error], + simplify=True) + # apply_function() constructs a function using sympy.lambdify() that allows inserting + # real values to each expression of the general solution and after that evaluating them + # numerically. The lambdify() method can be supplemented with the specific + # numerical backend by inserting e.g. "numpy", "cupy" or "jax" to the optional + # argument "modules=" of sympy.lambdify(). + + def apply_function(m: Model4DSTEM, scan_pixel_pitch) -> Model4DSTEM: + substituted = sym.lambdify( + [ + *params_old.descan_error, + params_old.scan_pixel_pitch, + params_new.scan_pixel_pitch + ], [solution[attr] for attr in [ + *params_new.descan_error + ]]) + evaluated = substituted( + *m.descanner.descan_error, + m.params.scan_pixel_pitch, scan_pixel_pitch + ) + new_de = DescanError(*evaluated) + new_params = m.params.derive(descan_error=new_de, scan_pixel_pitch=scan_pixel_pitch) + new_model = cls.build(new_params, m.scan_pos) + return new_model + + return apply_function + + def adjust_scan_pixel_pitch(self, scan_pixel_pitch: float) -> 'Model4DSTEM': + """ + Adjust the scan pixel pitch while keeping the effective descan error + compensation the same. + + This allows first compensating descan error and then adjusting other parameters. + """ + f = self._mk_adjust_scan_pixel_pitch() + return f(self, scan_pixel_pitch=scan_pixel_pitch) + + @classmethod + @cache + def _mk_adjust_detector_pixel_pitch(cls): + params_old = symbol_maker(Parameters4DSTEM, 'old', recurse_for=[DescanError, PixelYX]) + params_new_tmp = symbol_maker(Parameters4DSTEM, 'new', recurse_for=[DescanError, PixelYX]) + + params_new = params_old.derive( + descan_error=params_new_tmp.descan_error, + detector_pixel_pitch=params_new_tmp.detector_pixel_pitch + ) + + def detector_px(m: Model4DSTEM): + ray = m.make_source_ray( + source_dy=0., + source_dx=0., + ).ray + data = m.trace(ray) + return data['detector'].sampling['detector_px'] + + scan_pos_y, scan_pos_x = sym.symbols('scan_pos_y scan_pos_x') + scan_pos = PixelYX(scan_pos_y, scan_pos_x) + + model_old = cls.build(params=params_old, scan_pos=scan_pos) + model_new = cls.build(params=params_new, scan_pos=scan_pos) + + det_px_old = detector_px(model_old) + det_px_new = detector_px(model_new) + + expanded_expr_y = sym.expand(det_px_old.y - det_px_new.y) + expanded_expr_x = sym.expand(det_px_old.x - det_px_new.x) + + collected_y = sym.Poly(expanded_expr_y, scan_pos_y, scan_pos_x, params_old.camera_length) + collected_x = sym.Poly(expanded_expr_x, scan_pos_y, scan_pos_x, params_old.camera_length) + + coeff = collected_y.coeffs() + collected_x.coeffs() + + solution = sym.solve([eq for eq in coeff], [*params_new.descan_error], + simplify=True) + + def apply_function(m: Model4DSTEM, detector_pixel_pitch) -> Model4DSTEM: + substituted = sym.lambdify( + [ + *params_old.descan_error, + params_old.detector_pixel_pitch, + params_new.detector_pixel_pitch + ], [solution[attr] for attr in [ + *params_new.descan_error + ]]) + evaluated = substituted( + *m.descanner.descan_error, + m.params.detector_pixel_pitch, detector_pixel_pitch + ) + new_de = DescanError(*evaluated) + new_params = m.params.derive(descan_error=new_de, + detector_pixel_pitch=detector_pixel_pitch) + new_model = cls.build(new_params, m.scan_pos) + return new_model + + return apply_function + + def adjust_detector_pixel_pitch(self, detector_pixel_pitch: float) -> 'Model4DSTEM': + + f = self._mk_adjust_detector_pixel_pitch() + return f(self, detector_pixel_pitch=detector_pixel_pitch) + + @classmethod + @cache + def _mk_adjust_scan_rotation(cls): + params_old = symbol_maker(Parameters4DSTEM, 'old', recurse_for=[DescanError, PixelYX]) + params_new_tmp = symbol_maker(Parameters4DSTEM, 'new', recurse_for=[DescanError, PixelYX]) + + params_new = params_old.derive( + descan_error=params_new_tmp.descan_error, + scan_rotation=params_new_tmp.scan_rotation + ) + + def detector_px(m: Model4DSTEM): + ray = m.make_source_ray( + source_dy=0., + source_dx=0., + ).ray + data = m.trace(ray) + return data['detector'].sampling['detector_px'] + + scan_pos_y, scan_pos_x = sym.symbols('scan_pos_y scan_pos_x') + scan_pos = PixelYX(scan_pos_y, scan_pos_x) + + model_old = cls.build(params=params_old, scan_pos=scan_pos) + model_new = cls.build(params=params_new, scan_pos=scan_pos) + + det_px_old = detector_px(model_old) + det_px_new = detector_px(model_new) + + expanded_expr_y = sym.expand(det_px_old.y - det_px_new.y) + expanded_expr_x = sym.expand(det_px_old.x - det_px_new.x) + + collected_y = sym.Poly(expanded_expr_y, params_old.camera_length, scan_pos_y, scan_pos_x) + collected_x = sym.Poly(expanded_expr_x, params_old.camera_length, scan_pos_y, scan_pos_x) + + coeff = collected_y.coeffs() + collected_x.coeffs() + + solution = sym.solve([eq for eq in coeff], [*params_new.descan_error], + simplify=True) + + def apply_function(m: Model4DSTEM, scan_rotation) -> Model4DSTEM: + substituted = sym.lambdify( + [ + *params_old.descan_error, + params_old.scan_rotation, + params_new.scan_rotation + ], [solution[attr] for attr in [ + *params_new.descan_error + ]]) + + evaluated = substituted( + *m.descanner.descan_error, + float(m.params.scan_rotation), scan_rotation + ) + new_de = DescanError(*evaluated) + new_params = m.params.derive(descan_error=new_de, scan_rotation=scan_rotation) + new_model = cls.build(new_params, m.scan_pos) + return new_model + + return apply_function + + def adjust_scan_rotation(self, scan_rotation: float) -> 'Model4DSTEM': + """ + Adjust the scan rotation while keeping the effective descan error + compensation the same. + + This allows first compensating descan error and then adjusting other parameters. + """ + f = self._mk_adjust_scan_rotation() + return f(self, scan_rotation=scan_rotation) + def trace( params: Parameters4DSTEM,