Repository: yt-project/yt

Issue:
set_zlim does not work for symlog scaling
### Bug report

**Bug summary**

`set_zlim` seems to not work when applying limits to symlog scalings on plots.  This is using tip of dev.

**Code for reproduction**


```python
import yt
ds = yt.testing.fake_random_ds(3)
p = yt.SlicePlot(ds, "z", ('gas', 'density'))
p.set_log(('gas', 'density'), True, linthresh=1.e-3)
p.set_zlim(('gas', 'density'), -1, 1)
p.save()
```

**Actual outcome**

![UniformGridData_Slice_z_velocity_x](https://user-images.githubusercontent.com/8250999/116455296-bcc8fc00-a815-11eb-8099-cbe133bf3ec4.png)



Discussion:
wow. Not only does it not work, it the cbar is also weirdly placed
<img width="136" alt="Screenshot 2021-05-01 at 16 07 48" src="https://user-images.githubusercontent.com/14075922/116784971-610da580-aa97-11eb-8e9a-e31a8650f129.png">

To be fair, I see that cbar effect all the time and I don't think it's related to this behavior.
Yup, I've seen it too occasionally.
Here, I *think*, is the code path it's taking:

```python
        for field in self.data_source._determine_fields(fields):
            myzmin = _sanitize_units(zmin, field)
            myzmax = _sanitize_units(zmax, field)
            if zmin == "min":
                myzmin = self.plots[field].image._A.min()
            if zmax == "max":
                myzmax = self.plots[field].image._A.max()
            if dynamic_range is not None:
                if zmax is None:
                    myzmax = myzmin * dynamic_range
                else:
                    myzmin = myzmax / dynamic_range
            if myzmin > 0.0 and self._field_transform[field] == symlog_transform:
                self._field_transform[field] = log_transform
            self.plots[field].zmin = myzmin
            self.plots[field].zmax = myzmax
```

I'm slightly curious if what we're seeing is some issues with the way `myzmin` etc are computed internally...
As a note, `density` is positive definite in your example code.
Here's an example where it's not positive definite -- still broken, but differently.

```python
import numpy as np
import yt

d = {'my_fake_density': np.random.random(size=(64,64,64)) - 0.5}

ds = yt.load_uniform_grid(d, [64,64,64])
p = yt.SlicePlot(ds, "z", ('stream', 'my_fake_density'))
p.set_log(('stream', 'my_fake_density'), True, linthresh=1.e-3)
p.set_zlim(('stream', 'my_fake_density'), -1, 1)
p.save()
```

![UniformGridData_Slice_z_my_fake_density](https://user-images.githubusercontent.com/89019/117355884-b9b6b700-ae78-11eb-964d-02950a1453ff.png)

I think the change we'd _need_ is deeper than fixing `set_zlim`. The user should be free of using any norm, and we could have a `set_norm` modifier *and* support the `norm` kwarg in plotting methods.
Obviously such a change is a lot deeper and we probably won't do it for 4.0

Also, I'm not convinced this should be a blocker if we can't find a quick solution. This bug has been around for a while and it's a pain, but if it didn't block 3.6 it shouldn't block 4.0
I really want to fix this soon, so I'm going to assign myself until I either resolve it or give up
Awesome!  Let me know if I can test things or review!

Resolving patch:
diff --git a/yt/visualization/base_plot_types.py b/yt/visualization/base_plot_types.py
--- a/yt/visualization/base_plot_types.py
+++ b/yt/visualization/base_plot_types.py
@@ -212,12 +212,10 @@ def _init_image(self, data, cbnorm, cblinthresh, cmap, extent, aspect):
             cbnorm_cls = matplotlib.colors.Normalize
         elif cbnorm == "symlog":
             # if cblinthresh is not specified, try to come up with a reasonable default
-            vmin = float(np.nanmin(data))
-            vmax = float(np.nanmax(data))
             if cblinthresh is None:
                 cblinthresh = np.nanmin(np.absolute(data)[data != 0])
 
-            cbnorm_kwargs.update(dict(linthresh=cblinthresh, vmin=vmin, vmax=vmax))
+            cbnorm_kwargs.update(dict(linthresh=cblinthresh))
             MPL_VERSION = Version(matplotlib.__version__)
             if MPL_VERSION >= Version("3.2.0"):
                 # note that this creates an inconsistency between mpl versions
@@ -275,27 +273,30 @@ def _init_image(self, data, cbnorm, cblinthresh, cmap, extent, aspect):
         if cbnorm == "symlog":
             formatter = matplotlib.ticker.LogFormatterMathtext(linthresh=cblinthresh)
             self.cb = self.figure.colorbar(self.image, self.cax, format=formatter)
-            if np.nanmin(data) >= 0.0:
-                yticks = [np.nanmin(data).v] + list(
+            zmin = float(self.zmin) if self.zmin is not None else np.nanmin(data)
+            zmax = float(self.zmax) if self.zmax is not None else np.nanmax(data)
+
+            if zmin >= 0.0:
+                yticks = [zmin] + list(
                     10
                     ** np.arange(
                         np.rint(np.log10(cblinthresh)),
-                        np.ceil(np.log10(np.nanmax(data))),
+                        np.ceil(np.log10(zmax)),
                     )
                 )
-            elif np.nanmax(data) <= 0.0:
+            elif zmax <= 0.0:
                 yticks = (
                     list(
                         -(
                             10
                             ** np.arange(
-                                np.floor(np.log10(-np.nanmin(data))),
+                                np.floor(np.log10(-zmin)),
                                 np.rint(np.log10(cblinthresh)) - 1,
                                 -1,
                             )
                         )
                     )
-                    + [np.nanmax(data).v]
+                    + [zmax]
                 )
             else:
                 yticks = (
@@ -303,7 +304,7 @@ def _init_image(self, data, cbnorm, cblinthresh, cmap, extent, aspect):
                         -(
                             10
                             ** np.arange(
-                                np.floor(np.log10(-np.nanmin(data))),
+                                np.floor(np.log10(-zmin)),
                                 np.rint(np.log10(cblinthresh)) - 1,
                                 -1,
                             )
@@ -314,7 +315,7 @@ def _init_image(self, data, cbnorm, cblinthresh, cmap, extent, aspect):
                         10
                         ** np.arange(
                             np.rint(np.log10(cblinthresh)),
-                            np.ceil(np.log10(np.nanmax(data))),
+                            np.ceil(np.log10(zmax)),
                         )
                     )
                 )


Test patch: