Repository: yt-project/yt

Issue:
Error accessing a user-defined field with an arbitrary_grid
**Code for reproduction**

```python
import yt


def _tracerf(field, data):
    return data['Metal_Density']/data['gas', 'density']


ds = yt.load("IsolatedGalaxy/galaxy0030/galaxy0030")

ds.add_field(("gas", "tracerf"), function=_tracerf, units="dimensionless",
             take_log=False)

ad = ds.all_data()
galgas = ds.arbitrary_grid([0.4, 0.4, 0.4], [0.99, 0.99, 0.99],
                           dims=[512, 512, 512])
tracerp = galgas['tracerf']
```
The above script produces the following traceback:

```
Traceback (most recent call last):
  File "yt_arbgridtest.py", line 16, in <module>
    tracerp = galgas['tracerf']
  File "/Users/goldbaum/Documents/yt-git-fixes/yt/data_objects/data_containers.py", line 281, in __getitem__
    self.get_data(f)
  File "/Users/goldbaum/Documents/yt-git-fixes/yt/data_objects/construction_data_containers.py", line 641, in get_data
    if len(fill) > 0: self._fill_fields(fill)
  File "/Users/goldbaum/Documents/yt-git-fixes/yt/data_objects/construction_data_containers.py", line 851, in _fill_fields
    assert(len(fields) == 1)
AssertionError
```


Discussion:
It works for me if I change `Metal_Density` to `density`, so I think this might be an issue with the field definition having an error in it.
@MatthewTurk Isn't her issue for user derived fields though? That wouldn't catch it, then.
@jzuhone I mean in the field definition -- I still access the derived field.
Oddly, I *am* able to do `galgas["Metal_Density"]/galgas["gas", "density"]` so it's not as clear cut as I thought with the error.  It's something more subtle, like a dependency not getting picked up during field dependency computation.
Also, if you call for the user-defined field with a cut_region it works.  So it is purely when you call for the user-defined field for the arbitrary_grid.  Maybe this was already clear, but just in case!
FWIW, I tried your script by making a different user-defined field without `Metal_Density`, and that did work. 
Good point, I can also find user-defined fields that work.  This one, with no Metal_Density call, also works for a cut region and not for the arbitrary grid--this is actually the first fail that clued me in to the problem (you can either take the absolute value as I do here or not divide and get the value with dimensions g/cm/s**2 and both fail):

```
def _boundval(field, data):
    Mdisk = 1.15e11 * Msun
    Mbulge = 1e10 * Msun
    a = 3.5e-3 * Mpc
    b = 7.0e-4 * Mpc
    rbulge = 6.0e-4 * Mpc
    rdm = 2.3e-2 * Mpc
    rhodm = 3.8e-25 * g/cm**3
    GravConst = 6.67e-8 *cm**3/g/s**2

    a = ds.arr(a,'Mpc').to('cm')
    b = ds.arr(b,'Mpc').to('cm')
    rbulge = ds.arr(rbulge,'Mpc').to('cm')
    rdm = ds.arr(rdm,'Mpc').to('cm')
    Mdisk = ds.arr(Mdisk,'Msun').to('g')
    Mbulge = ds.arr(Mbulge,'Msun').to('g')

    AMx = 0.0
    AMy = 0.342
    AMz = 0.93969

    xc = ds.arr(0.5,'code_length') #* code_length
    yc = ds.arr(0.5,'code_length') #* code_length
    zc = ds.arr(0.5,'code_length') #* code_length

    xd = data["x"].in_units('code_length') - xc
    yd = data["y"].in_units('code_length') - yc
    zd = data["z"].in_units('code_length') - zc
    
    xd = ds.arr(xd,'code_length').to('cm')
    yd = ds.arr(yd,'code_length').to('cm')
    zd = ds.arr(zd,'code_length').to('cm')

    zheight = AMx*xd + AMy*yd + AMz*zd
    xcyl = xd - zheight*AMx
    ycyl = yd - zheight*AMy
    zcyl = zd - zheight*AMz
    rsphere = np.sqrt(xd*xd + yd*yd + zd*zd)
    rcyl = np.sqrt(xcyl*xcyl+ycyl*ycyl+zcyl*zcyl)

    Epotbulge = -Mbulge/(rsphere + rbulge)
    Epotdisk = -Mdisk/(pow(rcyl*rcyl + pow(a + pow(zheight*zheight + b*b,0.5),2.0),0.5))
    Epotdm = -1.0*np.pi*rhodm*rdm*rdm*(np.pi - 2.0*(1.0+rdm/rsphere)* np.arctan(rsphere/rdm) + 2.0*(1.0+rdm/rsphere)*np.log(1.0+rsphere/rdm) - (1.0-rdm/rsphere)*np.log(1.0+(pow(rsphere/rdm,2))))
    Epottot = Epotbulge + Epotdisk + Epotdm
    return (GravConst*Epottot*data["gas","density"].in_cgs() + data["gas","thermal_energy"]*data["gas","density"].in_cgs() + data["gas","kinetic_energy"].in_cgs())/np.abs(GravConst*Epottot*data["gas","density"].in_cgs() + data["gas","thermal_energy"]*data["gas","density"].in_cgs() + data["gas","kinetic_energy"].in_cgs())
```

Resolving patch:
diff --git a/yt/data_objects/construction_data_containers.py b/yt/data_objects/construction_data_containers.py
--- a/yt/data_objects/construction_data_containers.py
+++ b/yt/data_objects/construction_data_containers.py
@@ -848,16 +848,17 @@ def __init__(self, left_edge, right_edge, dims,
     def _fill_fields(self, fields):
         fields = [f for f in fields if f not in self.field_data]
         if len(fields) == 0: return
-        assert(len(fields) == 1)
-        field = fields[0]
-        dest = np.zeros(self.ActiveDimensions, dtype="float64")
-        for chunk in self._data_source.chunks(fields, "io"):
-            fill_region_float(chunk.fcoords, chunk.fwidth, chunk[field],
-                              self.left_edge, self.right_edge, dest, 1,
-                              self.ds.domain_width,
-                              int(any(self.ds.periodicity)))
-        fi = self.ds._get_field_info(field)
-        self[field] = self.ds.arr(dest, fi.units)
+        # It may be faster to adapt fill_region_float to fill multiple fields
+        # instead of looping here
+        for field in fields:
+            dest = np.zeros(self.ActiveDimensions, dtype="float64")
+            for chunk in self._data_source.chunks(fields, "io"):
+                fill_region_float(chunk.fcoords, chunk.fwidth, chunk[field],
+                                  self.left_edge, self.right_edge, dest, 1,
+                                  self.ds.domain_width,
+                                  int(any(self.ds.periodicity)))
+            fi = self.ds._get_field_info(field)
+            self[field] = self.ds.arr(dest, fi.units)
 
 
 class LevelState(object):


Test patch:
diff --git a/yt/data_objects/tests/test_covering_grid.py b/yt/data_objects/tests/test_covering_grid.py
--- a/yt/data_objects/tests/test_covering_grid.py
+++ b/yt/data_objects/tests/test_covering_grid.py
@@ -149,3 +149,18 @@ def test_smoothed_covering_grid_2d_dataset():
     ds.periodicity = (True, True, True)
     scg = ds.smoothed_covering_grid(1, [0, 0, 0], [128, 128, 1])
     assert_equal(scg['density'].shape, [128, 128, 1])
+
+isogal = "IsolatedGalaxy/galaxy0030/galaxy0030"
+@requires_file(isogal)
+def test_arbitrary_grid_derived_field():
+    def _tracerf(field, data):
+        return data['Metal_Density']/data['gas', 'density']
+
+    ds = load(isogal)
+
+    ds.add_field(("gas", "tracerf"), function=_tracerf, units="dimensionless",
+                 take_log=False)
+
+    galgas = ds.arbitrary_grid([0.4, 0.4, 0.4], [0.99, 0.99, 0.99],
+                               dims=[32, 32, 32])
+    galgas['tracerf']