<jupyter_start><jupyter_text>Natas representation of gridsNata has two type of representation for grids, `GridArray` and `GridDataset`. Both types are **array-like containers** which have a common interface for- Annotations (`.name`, `.label`, `.unit`)- Plugins (`.plot`, `.fft`, ...)and both can be created **from a file** (using `.from_path`) or **from an array-like object** (using `.from_array`).<jupyter_code>from nata.containers import GridArray
from nata.containers import GridDataset<jupyter_output><empty_output><jupyter_text>How to create GridArray and GridDataset Create an `GridArray` from a numpy array with annotations.<jupyter_code>import numpy as np

example_array = np.arange(32, dtype=np.float64).reshape((4, 8))

grid = GridArray.from_array(example_array)
grid<jupyter_output><empty_output><jupyter_text>By default nata will provide some default parameters when creating a container object. This can be changed at creation<jupyter_code>from nata.containers import Axis

grid = GridArray.from_array(
    example_array,
    name="some_name",
    label="some label",
    unit="some unit",
    time=10.0,
    axes=(
        Axis.from_array([0, 1, 2, 3], name="a1"),
        Axis.from_array(np.arange(8) - 10, name="a2"),
    )
)
grid<jupyter_output><empty_output><jupyter_text>or by changing each property individually<jupyter_code>grid.name = "some_new_name"
grid.label = "some new label"
grid.unit = "some new unit"
grid<jupyter_output><empty_output><jupyter_text>The `GridArray` represent a grid at **one** specific time step<jupyter_code>print(f"{grid.time.shape = }")
print(f"{grid.axes[0].shape = }")
print(f"{grid.axes[1].shape = }")<jupyter_output>grid.time.shape = ()
grid.axes[0].shape = (4,)
grid.axes[1].shape = (8,)<jupyter_text>but the `GridDataset` represents a grid at **different time steps**<jupyter_code>grid_ds = GridDataset.from_array(example_array)
grid_ds
print(f"{grid_ds.shape = }")
print(f"{grid_ds.time.shape = }")
print(f"{grid_ds.axes[0].shape = }")
print(f"{grid_ds.axes[1].shape = }")<jupyter_output>grid_ds.shape = (4, 8)
grid_ds.time.shape = (4,)
grid_ds.axes[0].shape = (4,)
grid_ds.axes[1].shape = (4, 8)<jupyter_text>Reading from pathGrids can be created by using the `.from_path` method.<jupyter_code>from nata import examples<jupyter_output><empty_output><jupyter_text>Here are some example grid files:<jupyter_code>!ls -l {examples.grids}<jupyter_output>total 48
-rwxr-xr-x 1 natadev natadev 7336 May 13 22:53 grid-1d-0.h5
-rwxr-xr-x 1 natadev natadev 7336 May 13 22:53 grid-1d-1.h5
-rwxr-xr-x 1 natadev natadev 7336 May 13 22:53 grid-1d-2.h5
-rwxr-xr-x 1 natadev natadev 7336 May 13 22:53 grid-1d-3.h5
-rwxr-xr-x 1 natadev natadev 7336 May 13 22:53 grid-1d-4.h5
-rwxr-xr-x 1 natadev natadev 7336 May 13 22:53 grid-1d-5.h5<jupyter_text>To create a `GridArray` from **one file** simply use the `.from_path` method<jupyter_code>one_grid = GridArray.from_path(examples.grids / "grid-1d-0.h5")
one_grid<jupyter_output><empty_output><jupyter_text>or create a `GridDataset` from **several files**<jupyter_code>multiple_grids = GridDataset.from_path(examples.grids / "*")
multiple_grids<jupyter_output><empty_output><jupyter_text>Indexing Nata's container support object indexing, e.g. indexing along the temporal axis<jupyter_code>multiple_grids[2]<jupyter_output><empty_output><jupyter_text>or indexing along the spatial axis<jupyter_code>multiple_grids[::2, 10:-25:3]<jupyter_output><empty_output><jupyter_text>Getting the data stored<jupyter_code>type(multiple_grids.to_numpy())
multiple_grids[-1, 10:25].to_numpy()<jupyter_output><empty_output><jupyter_text>Data in nata is stored in dask array. This allow<jupyter_code>multiple_grids.to_dask()<jupyter_output><empty_output><jupyter_text>Grid containers behave like numpy arrays<jupyter_code>example = GridArray.from_array([0, 1, 4, 9], name="example", label="\chi")
example
sqrt_example = np.sqrt(example)
sqrt_example.to_numpy()<jupyter_output><empty_output><jupyter_text>While numpy functions are directly supported by using numpy's dispatch mechanism, annotations are not conserved<jupyter_code>sqrt_example<jupyter_output><empty_output><jupyter_text>To cover this special cases, nata introduces plugins. They are handy helper for repeatable tasks<jupyter_code>from nata.containers import register_plugin

@register_plugin(name="sqrt")
def sqrt_for_grids(data: GridArray):
    result = np.sqrt(data)
    result.name = "sqrt_" + data.name
    result.label = f"\sqrt({data.label})"
    
    return result

example.sqrt()
nata_grid = GridArray.from_array([0, 1, 0, 2])

import matplotlib.pyplot as plt

plt.plot(nata_grid)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12953813104189044, 'fraction_numerical': 0.030719656283566057, 'mean_word_length': 6.473515248796148, 'pattern_counts': {'":': 0, '<': 66, '<?xml version=': 0, '>': 66, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '17', 'dir': 'jupyter-structured-clean-dedup', 'id': '504619', 'license': 'MIT', 'n_tokens_mistral': 1833, 'n_tokens_neox': 1718, 'n_words': 374, 'path': 'examples/Grids.ipynb', 'repo_name': 'babjarob/nata', 'size': '4655'}