{ "cells": [ { "cell_type": "markdown", "id": "c8dd1a63-cf7d-4a28-878b-54167df286c1", "metadata": {}, "source": [ "## Creating custom transformations\n", "\n", "The transformations and interpolation frameworks are intentionally written in an open-ended manner so that you can write your own transformations for your particular data. \n", "\n", "To write your own transformer class, you should inherit from the abstract `Transformer` and implement a number of methods: \n", "\n", "* `_calculate_transformed`\n", "* `_calculate_native`\n", "* `calculate_transformed_bbox`\n", "\n", "Additionally, the base `Transformer` allows arbitary coordinate names, so it is often helpful to override the `__init__` method in order to specify the expected coordinate names. \n", "\n", "So to get started, let's define a transformer to go from an arbitrary 2d coordinate system with coordinate axes `b` and `c` to 3D cartesian coordinates and being by overriding `__init__`:\n", "\n", "```python\n", "from yt_xarray.transformations import Transformer\n", "\n", "class MyTransformer(Transformer):\n", "\n", " def __init__(self): \n", " native_coords = ('b', 'c')\n", " transformed_coords = ('x', 'y', 'z')\n", " super().__init__(native_coords, transformed_coords)\n", "```\n", "\n", "\n", "Now let's define `_calculate_transformed` to describe the function x, y, z = f(b, c). `_calculate_transformed` must conform to a number of requirmements. First, `_calculate_transformed` must accept a `**coords` argument. That `**coords` keyword dictionary is **guaranteed** to have entries keyed by the `native_coords` tuple (validation is taken care by methods in the abstract class). `_calculate_transformed` must then return the coordinates in the transformed coordinate system. We'll do something competely arbitrary here... \n", "\n", "```python\n", "\n", " def _calculate_transformed(self, **coords):\n", " b = coords['b'] \n", " c = coords['c'] \n", " x = b * 2 \n", " y = c * 4\n", " z = np.sqrt(x**2 + y**2) \n", " return x, y, z\n", "```\n", "\n", "Now, add on `_calculate_native`, which in this exmaple will go from (x, y, z) to (b, c). \n", "\n", "```python \n", " \n", " def _calculate_native(self, **coords):\n", " x = coords['x']\n", " y = coords['y'] \n", " \n", " b = x / 2.0 \n", " c = y / 4.0 \n", " return b, c \n", "```\n", "\n", "And finally, `calculate_transformed_bbox` must provide a method for calculating the bounding range of coordinates in the transformed coordinate given bounds in the native coordinate system. In this arbitrary coordinate system, we can simply call the method's `to_transformed` at the bounds of the native range to get the bounding box in the transformed system: \n", "\n", "``` python\n", " def calculate_transformed_bbox(self, bbox_dict):\n", " b_min_max = bbox_dict['b']\n", " c_min_max = bbox_dict['c']\n", "\n", " xmin, ymin, zmin = self.to_transformed(b=b_min_max[0], \n", " c=c_min_max[0])\n", " xmax, ymax, zmax = self.to_transformed(b=b_min_max[1], \n", " c=c_min_max[1])\n", "```\n", "\n", "Putting it all together:\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "2add05c6-e95e-4504-a3cb-85c6d3720968", "metadata": {}, "outputs": [], "source": [ "import numpy as np \n", "from yt_xarray.transformations import Transformer\n", "\n", "class MyTransformer(Transformer):\n", "\n", " def __init__(self): \n", " native_coords = ('b', 'c')\n", " transformed_coords = ('x', 'y', 'z')\n", " super().__init__(native_coords, transformed_coords)\n", "\n", " def _calculate_transformed(self, **coords):\n", " b = coords['b'] \n", " c = coords['c'] \n", " x = b * 2. \n", " y = c * 4.\n", " z = np.sqrt(x**2 + y**2) \n", " return x, y, z\n", "\n", " def _calculate_native(self, **coords):\n", " x = coords['x']\n", " y = coords['y'] \n", " \n", " b = x / 2.0 \n", " c = y / 4.0 \n", " return b, c \n", "\n", " def calculate_transformed_bbox(self, bbox_dict):\n", " b_min_max = bbox_dict['b']\n", " c_min_max = bbox_dict['c']\n", "\n", " xmin, ymin, zmin = self.to_transformed(b=b_min_max[0], \n", " c=c_min_max[0])\n", " xmax, ymax, zmax = self.to_transformed(b=b_min_max[1], \n", " c=c_min_max[1])" ] }, { "cell_type": "markdown", "id": "06eed913-00b3-4440-a166-04fd446f61da", "metadata": {}, "source": [ "our transformer is now available to use! " ] }, { "cell_type": "code", "execution_count": 5, "id": "bf9f528b-2bf4-45c6-81a9-a92bb7462608", "metadata": {}, "outputs": [], "source": [ "mtf = MyTransformer()" ] }, { "cell_type": "code", "execution_count": 6, "id": "c764bf56-b3c4-434f-9cb6-c0ca0113736c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(0.0, 0.0, 0.0)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x, y, z = mtf.to_transformed(b=0,c=0)\n", "x, y, z" ] }, { "cell_type": "code", "execution_count": 7, "id": "a5654087-b37a-460e-9034-e0792cd3a167", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(0.0, 0.0)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mtf.to_native(x=x, y=y, z=z)" ] }, { "cell_type": "code", "execution_count": 8, "id": "f4933f94-6ab2-4827-8192-59363f116327", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.0 40.0 40.01249804748511\n" ] }, { "data": { "text/plain": [ "(0.5, 10.0)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x, y, z = mtf.to_transformed(b=0.5,c=10.)\n", "print(x, y, z)\n", "mtf.to_native(x=x, y=y, z=z)" ] }, { "cell_type": "markdown", "id": "07be7296-4be3-4c62-9a3e-95941b1b73ef", "metadata": {}, "source": [ "Additionally, as long as your custom transformer transforms to and from 3D cartesian coordinates and if the \"native\" coordinates match an xarray dataset field's dimensions, **you can hand off your custom transformer to `build_interpolated_cartesian_ds`** and build a yt cartesian dataset that reads and interpolates from an arbitrary coordinate system! " ] } ], "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.10.11" } }, "nbformat": 4, "nbformat_minor": 5 }