Repository: yt-project/yt

Issue:
Issue plotting vorticity from amrvac data
### Bug report

**Bug summary**

Attempting to plot_2d the vorticity_magnitude derived field within yt will return an error when applied to non-periodic amrvac data. The traditional hack of manually setting ds.periodicity=[True]*3 is insufficient for successfully passing the command to plot as it gets hung up, then, on the unavailability of ghost cells within the dataset.

**Code for reproduction**

curldrop file--> http://use.yt/upload/5e14fb46

```python
file_index=500
overrides = dict(length_unit=(1e8, 'cm'), temperature_unit=(1e6, 'K'), numberdensity_unit=(1e8, 'cm**-3'))
ds=yt.load('jk2020{0:04d}'.format(file_index)+'.dat', units_override=overrides)
plot=yt.plot_2d(ds, "vorticity_magnitude")
plot.show()
```

**Actual outcome**

```
---------------------------------------------------------------------------
NeedsGridType                             Traceback (most recent call last)
~/codes/yt/yt/data_objects/data_containers.py in _generate_fluid_field(self, field)
    308         try:
--> 309             finfo.check_available(gen_obj)
    310         except NeedsGridType as ngt_exception:

~/codes/yt/yt/fields/derived_field.py in check_available(self, data)
    210         for validator in self.validators:
--> 211             validator(data)
    212         # If we don't get an exception, we're good to go

~/codes/yt/yt/fields/derived_field.py in __call__(self, data)
    487         if not getattr(data, '_spatial', False):
--> 488             raise NeedsGridType(self.ghost_zones,self.fields)
    489         if self.ghost_zones <= data._num_ghost_zones:

NeedsGridType: (1, [('gas', 'velocity_x'), ('gas', 'velocity_y'), ('gas', 'velocity_z')])

During handling of the above exception, another exception occurred:

RuntimeError                              Traceback (most recent call last)
<ipython-input-26-c39a34fa7c89> in <module>
      3 ds=yt.load('/Volumes/TRANSCEND/Projects/amrvac/2020_prom_form/spex_dm_4_24_528_4224_3G/jk2020{0:04d}'.format(file_index)+'.dat', units_override=overrides)
      4 #sub=ds.region(left_edge=[-11.9,0.1,0], right_edge=[11.9,24.9,0],center=)
----> 5 plot=yt.plot_2d(ds, "vorticity_magnitude")
      6 plot.show()

~/codes/yt/yt/visualization/plot_window.py in plot_2d(ds, fields, center, width, axes_unit, origin, fontsize, field_parameters, window_size, aspect, data_source)
   2174                                 field_parameters=field_parameters,
   2175                                 window_size=window_size, aspect=aspect,
-> 2176                                 data_source=data_source)

~/codes/yt/yt/visualization/plot_window.py in __init__(self, ds, axis, fields, center, width, axes_unit, origin, right_handed, fontsize, field_parameters, window_size, aspect, data_source, buff_size)
   1331             slc = ds.slice(axis, center[axis], field_parameters=field_parameters,
   1332                            center=center, data_source=data_source)
-> 1333             slc.get_data(fields)
   1334         validate_mesh_fields(slc, fields)
   1335         PWViewerMPL.__init__(self, slc, bounds, origin=origin,

~/codes/yt/yt/data_objects/data_containers.py in get_data(self, fields)
   1617 
   1618         fields_to_generate += gen_fluids + gen_particles
-> 1619         self._generate_fields(fields_to_generate)
   1620         for field in list(self.field_data.keys()):
   1621             if field not in ofields:

~/codes/yt/yt/data_objects/data_containers.py in _generate_fields(self, fields_to_generate)
   1637                 fi = self.ds._get_field_info(*field)
   1638                 try:
-> 1639                     fd = self._generate_field(field)
   1640                     if hasattr(fd, 'units'):
   1641                         fd.units.registry = self.ds.unit_registry

~/codes/yt/yt/data_objects/data_containers.py in _generate_field(self, field)
    291                 tr = self._generate_particle_field(field)
    292             else:
--> 293                 tr = self._generate_fluid_field(field)
    294             if tr is None:
    295                 raise YTCouldNotGenerateField(field, self.ds)

~/codes/yt/yt/data_objects/data_containers.py in _generate_fluid_field(self, field)
    309             finfo.check_available(gen_obj)
    310         except NeedsGridType as ngt_exception:
--> 311             rv = self._generate_spatial_fluid(field, ngt_exception.ghost_zones)
    312         else:
    313             rv = finfo(gen_obj)

~/codes/yt/yt/data_objects/data_containers.py in _generate_spatial_fluid(self, field, ngz)
    358                         ind += wogz.select(
    359                             self.selector,
--> 360                             gz[field][ngz:-ngz, ngz:-ngz, ngz:-ngz],
    361                             rv, ind)
    362         if accumulate:

~/codes/yt/yt/data_objects/data_containers.py in __getitem__(self, key)
    254                 return self.field_data[f]
    255             else:
--> 256                 self.get_data(f)
    257         # fi.units is the unit expression string. We depend on the registry
    258         # hanging off the dataset to define this unit object.

~/codes/yt/yt/data_objects/construction_data_containers.py in get_data(self, fields)
    727                 self._fill_particles(part)
    728 
--> 729         if len(fill) > 0: self._fill_fields(fill)
    730         for a, f in sorted(alias.items()):
    731             if f.sampling_type == 'particle' and not is_sph_field:

~/codes/yt/yt/data_objects/construction_data_containers.py in _fill_fields(self, fields)
   1186         if len(fields) == 0: return
   1187         ls = self._initialize_level_state(fields)
-> 1188         min_level = self._compute_minimum_level()
   1189         # NOTE: This usage of "refine_by" is actually *okay*, because it's
   1190         # being used with respect to iref, which is *already* scaled!

~/codes/yt/yt/data_objects/construction_data_containers.py in _compute_minimum_level(self)
   1172             ils.data_source.loose_selection = False
   1173             min_level = self.level
-> 1174             for chunk in ils.data_source.chunks([], "io"):
   1175                 # With our odd selection methods, we can sometimes get no-sized ires.
   1176                 ir = chunk.ires

~/codes/yt/yt/data_objects/data_containers.py in chunks(self, fields, chunking_style, **kwargs)
   1497     def chunks(self, fields, chunking_style, **kwargs):
   1498         # This is an iterator that will yield the necessary chunks.
-> 1499         self.get_data() # Ensure we have built ourselves
   1500         if fields is None: fields = []
   1501         # chunk_ind can be supplied in the keyword arguments.  If it's a

~/codes/yt/yt/data_objects/data_containers.py in get_data(self, fields)
   1540     def get_data(self, fields=None):
   1541         if self._current_chunk is None:
-> 1542             self.index._identify_base_chunk(self)
   1543         if fields is None: return
   1544         nfields = []

~/codes/yt/yt/geometry/grid_geometry_handler.py in _identify_base_chunk(self, dobj)
    272             dobj._chunk_info[0] = weakref.proxy(dobj)
    273         elif getattr(dobj, "_grids", None) is None:
--> 274             gi = dobj.selector.select_grids(self.grid_left_edge,
    275                                             self.grid_right_edge,
    276                                             self.grid_levels)

~/codes/yt/yt/data_objects/data_containers.py in selector(self)
   1492                 self, self._data_source.selector, sclass(self))
   1493         else:
-> 1494             self._selector = sclass(self)
   1495         return self._selector
   1496 

~/codes/yt/yt/geometry/selection_routines.pyx in yt.geometry.selection_routines.RegionSelector.__init__()

RuntimeError: Error: yt attempted to read outside the boundaries of a non-periodic domain along dimension 0.
Region left edge = -12.017045454545455 code_length, Region right edge = -11.84659090909091 code_length
Dataset left edge = -12.0 code_length, Dataset right edge = 12.0 code_length

This commonly happens when trying to compute ghost cells up to the domain boundary. Two possible solutions are to load a smaller region that does not border the edge or override the periodicity for this dataset.
```

**Expected outcome**

A plot of the vorticity across the whole field-of-view

**Version Information**
  * Operating System: MacOS 10.14.6
  * Python Version: 3.7.6
  * yt version: 4.0dev (edited by @neutrinoceros)
  * Other Libraries (if applicable): 

python was installed using conda
yt was installed using 'git clone [...]', cd <yt folder location>, 'pip install -e .'




Discussion:
Hi, and welcome to yt!  Thanks for opening your first issue.  We have an issue template that helps us to gather relevant information to help diagnosing and fixing the issue.

Error persists when creating a subregion of the data using .region() or .box(), not just the full field-of-view.
Thanks a lot for this report !
I'm trying to investigate whether this is a frontend-specific issue. Could you confirm that the geometry and dimensionality of your dataset are `"cartesian"` and `2` respectively ?
Also, could you add the git hash for yt ? Is it the current tip of the master branch ? Thanks !
I can confirm that the geometry is Cartesian and the dimensionality is 2.5D. I have just updated to the tip of the master branch and retried the plotting and it has still failed.

Resolving patch:
diff --git a/yt/frontends/amrvac/data_structures.py b/yt/frontends/amrvac/data_structures.py
--- a/yt/frontends/amrvac/data_structures.py
+++ b/yt/frontends/amrvac/data_structures.py
@@ -9,6 +9,7 @@
 import os
 import stat
 import struct
+import warnings
 import weakref
 
 import numpy as np
@@ -58,6 +59,18 @@ def get_global_startindex(self):
         self.start_index = np.rint(start_index).astype("int64").ravel()
         return self.start_index
 
+    def retrieve_ghost_zones(self, n_zones, fields, all_levels=False, smoothed=False):
+        if smoothed:
+            warnings.warn(
+                "ghost-zones interpolation/smoothing is not "
+                "currently supported for AMRVAC data.",
+                category=RuntimeWarning,
+            )
+            smoothed = False
+        return super(AMRVACGrid, self).retrieve_ghost_zones(
+            n_zones, fields, all_levels=all_levels, smoothed=smoothed
+        )
+
 
 class AMRVACHierarchy(GridIndex):
     grid = AMRVACGrid


Test patch: