Repository: yt-project/yt

Issue:
[GDF] writing and loading dataset yields different result
Originally reported by: **Kacper Kowalik (Bitbucket: [xarthisius](https://bitbucket.org/xarthisius), GitHub: [xarthisius](https://github.com/xarthisius))**

----------------------------------------

As reported by Andrea Negri on the [yt-user ml](http://lists.spacepope.org/pipermail/yt-users-spacepope.org/2016-November/008301.html) following script:

```
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
import yt

def main():
    dims = np.array([2, 2, 2])

    grid_data = [
        dict(left_edge = [0.0, 0.0, 0.0],
             right_edge = [1.0, 1.0, 1.0],
             level = 0,
             dimensions = dims), 
        dict(left_edge = [0.25, 0.25, 0.25],
             right_edge = [0.75, 0.75, 0.75],
             level = 1,
             dimensions = dims),
       ]
    bbox = np.array([[0, 1], [0, 1], [0, 1]])
    ds = yt.load_amr_grids(grid_data, dims, bbox=bbox, periodicity=(0,0,0), \
            length_unit=1., refine_by=2)

    #make a nice slice
    slc = yt.SlicePlot(ds, "z", "grid_level")
    slc.set_log("grid_level", False)
    slc.save('original_slice.png')

    yt.utilities.grid_data_format.writer.write_to_gdf(ds, 'test.h5',
            clobber=True)

    da = yt.load('test.h5')
    slc = yt.SlicePlot(da, "z", "grid_level")
    slc.set_log("grid_level", False)
    slc.save('test.png')

if __name__ == '__main__':
    main()
```

yields different results ![original_slice.png] (https://bitbucket.org/repo/BXbAb/images/355574044-original_slice.png)

![test.png](https://bitbucket.org/repo/BXbAb/images/3456721044-test.png)

for the same dataset that was exported and read as a GDF file.

----------------------------------------
- Bitbucket: https://bitbucket.org/yt_analysis/yt/issue/1295



Discussion:

Resolving patch:
diff --git a/yt/frontends/stream/data_structures.py b/yt/frontends/stream/data_structures.py
--- a/yt/frontends/stream/data_structures.py
+++ b/yt/frontends/stream/data_structures.py
@@ -61,6 +61,8 @@
     MatchPointsToGrids
 from yt.utilities.decompose import \
     decompose_array, get_psize
+from yt.utilities.exceptions import \
+    YTIllDefinedAMR
 from yt.units.yt_array import \
     YTQuantity, \
     uconcatenate
@@ -857,6 +859,17 @@ def load_amr_grids(grid_data, domain_dimensions,
         for ci in ids:
             parent_ids[ci] = gi
 
+    # Check if the grid structure is properly aligned (bug #1295)
+    for lvl in range(grid_levels.min() + 1, grid_levels.max() + 1):
+        idx = grid_levels.flatten() == lvl
+        dims = domain_dimensions * refine_by ** (lvl - 1)
+        for iax, ax in enumerate('xyz'):
+            cell_edges = np.linspace(domain_left_edge[iax],
+                                     domain_right_edge[iax],
+                                     dims[iax], endpoint=False)
+            if set(grid_left_edges[idx, iax]) - set(cell_edges):
+                raise YTIllDefinedAMR(lvl, ax)
+
     if length_unit is None:
         length_unit = 'code_length'
     if mass_unit is None:
diff --git a/yt/utilities/exceptions.py b/yt/utilities/exceptions.py
--- a/yt/utilities/exceptions.py
+++ b/yt/utilities/exceptions.py
@@ -649,3 +649,15 @@ def __init__(self):
 
     def __str__(self):
         return "Boolean data objects must share a common dataset object."
+
+class YTIllDefinedAMR(YTException):
+    def __init__(self, level, axis):
+        self.level = level
+        self.axis = axis
+
+    def __str__(self):
+        msg = (
+            "Grids on the level {} are not properly aligned with cell edges "
+            "on the parent level ({} axis)"
+        ).format(self.level, self.axis)
+        return msg


Test patch:
diff --git a/yt/frontends/stream/tests/test_stream_amrgrids.py b/yt/frontends/stream/tests/test_stream_amrgrids.py
--- a/yt/frontends/stream/tests/test_stream_amrgrids.py
+++ b/yt/frontends/stream/tests/test_stream_amrgrids.py
@@ -1,5 +1,7 @@
 import numpy as np
-from yt.utilities.exceptions import YTIntDomainOverflow
+from yt.utilities.exceptions import \
+    YTIllDefinedAMR, \
+    YTIntDomainOverflow
 
 from yt import load_amr_grids, ProjectionPlot
 
@@ -49,3 +51,21 @@ def test_refine_by():
     domain_dimensions = np.array([8, 8, 8])
 
     load_amr_grids(grid_data, domain_dimensions, refine_by=ref_by)
+
+def test_validation():
+    dims = np.array([4, 2, 4])
+    grid_data = [
+        dict(left_edge = [0.0, 0.0, 0.0],
+             right_edge = [1.0, 1.0, 1.0],
+             level = 0,
+             dimensions = dims),
+        dict(left_edge = [0.25, 0.25, 0.25],
+             right_edge = [0.75, 0.75, 0.75],
+             level = 1,
+             dimensions = dims),
+       ]
+    bbox = np.array([[0, 1], [0, 1], [0, 1]])
+    def load_grids():
+        load_amr_grids(grid_data, dims, bbox=bbox, periodicity=(0, 0, 0),
+                       length_unit=1.0, refine_by=2)
+    assert_raises(YTIllDefinedAMR, load_grids)