Repository: xCDAT/xcdat

Issue:
[Bug]: regridder storing NaN for lat bounds
### What happened?

It seems like (in some instances) the horizontal regridder is saving latitude bounds with `NaN` values.

### What did you expect to happen?

Since we are passing in a grid with valid `lat_bnds`, these should be saved to the regridded dataset. 

### Minimal Complete Verifiable Example

```python
# imports
from xcdat.regridder import grid
import xcdat as xc
import numpy as np

# example file
fn = '/p/user_pub/climate_work/pochedley1/cesm2le/tas/tas_Amon_CESM2_historical-ssp370_1251-005_gr_185001-210012.nc'

# create target grid
ngrid = grid.create_grid(np.arange(-88.75, 90, 2.5), np.arange(1.25, 360., 2.5))

# open file
ds = xc.open_dataset(fn)
dsr = ds.regridder.horizontal('tas', ngrid, tool='xesmf', method='bilinear', periodic=True)

# notice that spatial averaging produces NaNs
print(dsr.spatial.average('tas').tas.values)                                                                                          
```

> [nan nan nan ... nan nan nan]

```
# this likely results because bounds_lat has NaN values
print(dsr.bounds_lat)
```

> <xarray.DataArray 'bounds_lat' (lat: 72, bound: 2)>
> array([[nan, nan],
>        [nan, nan],
>        [nan, nan],
>        [nan, nan],
>        ...


### Relevant log output

_No response_

### Anything else we need to know?

A separate `lat_bnds` variable is saved in the regridded dataset (it was not in the original dataset) with the correct bounds, but the `ds.lat.bounds` attribute is `bounds_lat`. 

This may be as simple as using that attribute to choose the name of the new bounds (if it exists) when saving the new, regridded dataset. 

For some reason this doesn't seem to affect longitude (in this example). `ds.bounds_lon` has the right shape and does not have NaN values. 

### Environment

main branch

Discussion:
codecov-commenter: # [Codecov](https://codecov.io/gh/xCDAT/xcdat/pull/344?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT) Report
> Merging [#344](https://codecov.io/gh/xCDAT/xcdat/pull/344?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT) (ce2cadd) into [main](https://codecov.io/gh/xCDAT/xcdat/commit/f6bc40944bf3766a335e7db3e064cda863b0a81c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT) (f6bc409) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff            @@
##              main      #344   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           14        14           
  Lines         1196      1198    +2     
=========================================
+ Hits          1196      1198    +2     
```


| [Impacted Files](https://codecov.io/gh/xCDAT/xcdat/pull/344?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT) | Coverage Δ | |
|---|---|---|
| [xcdat/regridder/base.py](https://codecov.io/gh/xCDAT/xcdat/pull/344/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT#diff-eGNkYXQvcmVncmlkZGVyL2Jhc2UucHk=) | `100.00% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xCDAT)

pochedls: FYI – I did loop over a bunch of `tas` files and CDAT/xcdat agreed in every instance. 
tomvothecoder: @jasonb5 @pochedls, do you both think this will be merged today or tomorrow?
Just checking if it can make 0.3.2, which I am targeting by tomorrow.
pochedls: I think this can be merged upon approval from @jasonb5 and @tomvothecoder

Patch:
diff --git a/xcdat/regridder/base.py b/xcdat/regridder/base.py
index aa27216..e3419b0 100644
--- a/xcdat/regridder/base.py
+++ b/xcdat/regridder/base.py
@@ -10,11 +10,11 @@ logger = setup_custom_logger(__name__)
 
 
 def preserve_bounds(
-    source: xr.Dataset, source_grid: xr.Dataset, target: xr.Dataset
+    source: xr.Dataset, target_grid: xr.Dataset, target: xr.Dataset
 ) -> xr.Dataset:
     """Preserves bounds from sources to target.
 
-    Preserve the lat/lon bounds from `source_grid` to `target`.
+    Ensure the lat/lon bounds from `target_grid` are included in the `target` dataset.
 
     Preserve any additional bounds e.g. time, vertical from `source` to `target`.
 
@@ -22,8 +22,8 @@ def preserve_bounds(
     ----------
     source : xr.Dataset
         Source Dataset.
-    source_grid : xr.Dataset
-        Source grid Dataset.
+    target_grid : xr.Dataset
+        Target grid Dataset.
     target : xr.Dataset
         Target Dataset to preserve bounds to.
 
@@ -33,14 +33,14 @@ def preserve_bounds(
         Target Dataset with preserved bounds.
     """
     try:
-        lat_bnds = source_grid.bounds.get_bounds("Y")
+        lat_bnds = target_grid.bounds.get_bounds("Y")
     except KeyError:
         pass
     else:
         target[lat_bnds.name] = lat_bnds.copy()
 
     try:
-        lon_bnds = source_grid.bounds.get_bounds("X")
+        lon_bnds = target_grid.bounds.get_bounds("X")
     except KeyError:
         pass
     else:
@@ -54,6 +54,9 @@ def preserve_bounds(
         else:
             if source_bnds.name in target:
                 logger.debug(f"Bounds {source_bnds.name!r} already present")
+            elif dim_name in ["X", "Y"]:
+                # the X / Y bounds are copied from the target grid above
+                continue
             else:
                 target[source_bnds.name] = source_bnds.copy()
 


Test patch:
diff --git a/tests/test_regrid.py b/tests/test_regrid.py
index 4b27e85..7b8a256 100644
--- a/tests/test_regrid.py
+++ b/tests/test_regrid.py
@@ -662,8 +662,8 @@ class TestBase:
 
         output_ds = base.preserve_bounds(ds_with_bounds, ds_without_bounds, target)
 
-        assert "lat_bnds" in output_ds
-        assert "lon_bnds" in output_ds
+        assert "lat_bnds" not in output_ds
+        assert "lon_bnds" not in output_ds
         assert "time_bnds" in output_ds
 
         target = xr.Dataset()