Repository: pydata/xarray

Issue:
Writing Datasets to netCDF4 with "inconsistent" chunks
#### Code Sample

```python
import xarray as xr
from dask.array import zeros, ones

# Construct two variables with the same dimensions, but different chunking
x = zeros((100,100),dtype='f4',chunks=(50,100))
x = xr.DataArray(data=x, dims=('lat','lon'), name='x')
y = ones((100,100),dtype='f4',chunks=(100,50))
y = xr.DataArray(data=y, dims=('lat','lon'), name='y')

# Put them both into the same dataset
dset = xr.Dataset({'x':x,'y':y})

# Save to a netCDF4 file.
dset.to_netcdf("test.nc")
```

The last line results in
```
ValueError: inconsistent chunks
```

#### Problem description

This error is triggered by `xarray.backends.api.to_netcdf`'s use of the `dataset.chunks` property in two places:

https://github.com/pydata/xarray/blob/bb581ca206c80eea80270ba508ec80ae0cd3941f/xarray/backends/api.py#L703

https://github.com/pydata/xarray/blob/bb581ca206c80eea80270ba508ec80ae0cd3941f/xarray/backends/api.py#L709

I'm assuming `to_netcdf` only needs to know if chunks are being used, not necessarily if they're consistent?

If I define a more general check
```python
have_chunks = any(v.chunks for v in dataset.variables.values())
```
and replace the instances of `dataset.chunks` with `have_chunks`, then the netCDF4 file gets written without any problems (although the data seems to be stored contiguously instead of chunked).

Is this change as straight-forward as I think, or Is there something intrinsic about `xarray.Dataset` objects or writing to netCDF4 that require consistent chunks?

#### Output of ``xr.show_versions()``

<details>

commit: bb581ca206c80eea80270ba508ec80ae0cd3941f
python: 2.7.12.final.0
python-bits: 64
OS: Linux
OS-release: 4.4.0-128-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: None.None

xarray: 0.10.7
pandas: 0.23.1
numpy: 1.14.5
scipy: None
netCDF4: 1.4.0
h5netcdf: None
h5py: None
Nio: None
zarr: None
bottleneck: None
cyordereddict: None
dask: 0.17.5
distributed: None
matplotlib: None
cartopy: None
seaborn: None
setuptools: 39.2.0
pip: 10.0.1
conda: None
pytest: None
IPython: None
sphinx: None

</details>


Discussion:
shoyer: Thanks @neishm, this looks great to me!

could you kindly add a brief note to bug fixes for 0.10.8 in `doc/whats-new.rst`?

Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index 55bd0d97..322e21ac 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -49,6 +49,10 @@ Bug fixes
   incomplete chunks in multiple dimensions (:issue:`2225`).
   By `Joe Hamman <https://github.com/jhamman>`_.
 
+- Fixed a bug in :py:meth:`~Dataset.to_netcdf` which prevented writing
+  datasets when the arrays had different chunk sizes (:issue:`2254`).
+  By `Mike Neish <https://github.com/neishm>`_.
+
 .. _whats-new.0.10.7:
 
 v0.10.7 (7 June 2018)
diff --git a/xarray/backends/api.py b/xarray/backends/api.py
index 753f8394..d5e2e8bb 100644
--- a/xarray/backends/api.py
+++ b/xarray/backends/api.py
@@ -700,13 +700,14 @@ def to_netcdf(dataset, path_or_file=None, mode='w', format=None, group=None,
 
     # handle scheduler specific logic
     scheduler = get_scheduler()
-    if (dataset.chunks and scheduler in ['distributed', 'multiprocessing'] and
+    have_chunks = any(v.chunks for v in dataset.variables.values())
+    if (have_chunks and scheduler in ['distributed', 'multiprocessing'] and
             engine != 'netcdf4'):
         raise NotImplementedError("Writing netCDF files with the %s backend "
                                   "is not currently supported with dask's %s "
                                   "scheduler" % (engine, scheduler))
     lock = _get_lock(engine, scheduler, format, path_or_file)
-    autoclose = (dataset.chunks and
+    autoclose = (have_chunks and
                  scheduler in ['distributed', 'multiprocessing'])
 
     target = path_or_file if path_or_file is not None else BytesIO()


Test patch:
diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py
index e83b80a6..9ec68bb0 100644
--- a/xarray/tests/test_backends.py
+++ b/xarray/tests/test_backends.py
@@ -1275,6 +1275,23 @@ class NetCDF4ViaDaskDataTest(NetCDF4DataTest):
         # caching behavior differs for dask
         pass
 
+    def test_write_inconsistent_chunks(self):
+        # Construct two variables with the same dimensions, but different
+        # chunk sizes.
+        x = da.zeros((100, 100), dtype='f4', chunks=(50, 100))
+        x = DataArray(data=x, dims=('lat', 'lon'), name='x')
+        x.encoding['chunksizes'] = (50, 100)
+        x.encoding['original_shape'] = (100, 100)
+        y = da.ones((100, 100), dtype='f4', chunks=(100, 50))
+        y = DataArray(data=y, dims=('lat', 'lon'), name='y')
+        y.encoding['chunksizes'] = (100, 50)
+        y.encoding['original_shape'] = (100, 100)
+        # Put them both into the same dataset
+        ds = Dataset({'x': x, 'y': y})
+        with self.roundtrip(ds) as actual:
+            assert actual['x'].encoding['chunksizes'] == (50, 100)
+            assert actual['y'].encoding['chunksizes'] == (100, 50)
+
 
 class NetCDF4ViaDaskDataTestAutocloseTrue(NetCDF4ViaDaskDataTest):
     autoclose = True
@@ -1340,7 +1357,6 @@ class BaseZarrTest(CFEncodedDataTest):
                 print(k)
                 assert v.chunks == actual[k].chunks
 
-
     def test_chunk_encoding(self):
         # These datasets have no dask chunks. All chunking specified in
         # encoding