diff --git a/doc/citing.rst b/doc/citing.rst index 021755f781..6cdfabfc67 100644 --- a/doc/citing.rst +++ b/doc/citing.rst @@ -8,37 +8,36 @@ entry .. code-block:: bibtex - @misc{francisco_de_la_pena_2015_27735, - author = {Francisco de la Peña and - Vidar Tonaas Fauske and - Pierre Burdet and - Tomas Ostasevicius and - Mike Sarahan and - Magnus Nord and - Joshua A. Taillon and - Duncan Johnstone and - Katherine E. MacArthur and - Alberto Eljarrat and - Stefano Mazzucco and - Jan Caron and - Tom Furnival and - Michael Walls and - Eric Prestat and - Gaël Donval and - Ben Martineau and - Luiz Fernando Zagonel and - Petras Jokubauskas and - Thomas Aarholt and - and Andreas Garmannslund and - Ilya Iyengar and - }, - title = {hyperspy: HyperSpy 0.8.5}, - month = jun, - year = 2016, - doi = {10.5281/zenodo.58841}, - url = {http://dx.doi.org/10.5281/zenodo.58841} - } - + @misc{francisco_de_la_pena_2016_58841, + author = {Francisco de la Peña and + Tomas Ostasevicius and + Vidar Tonaas Fauske and + Pierre Burdet and + Petras Jokubauskas and + Mike Sarahan and + Duncan Johnstone and + Magnus Nord and + Josh Taillon and + Jan Caron and + Katherine E. MacArthur and + Alberto Eljarrat and + Stefano Mazzucco and + Tom Furnival and + Eric Prestat and + Michael Walls and + Gaël Donval and + Ben Martineau and + Luiz Fernando Zagonel and + Andreas Garmannslund and + Thomas Aarholt and + Christoph Gohlke and + Ilya Iyengar}, + title = {hyperspy: HyperSpy 1.0.1}, + month = jul, + year = 2016, + doi = {10.5281/zenodo.58841}, + url = {http://dx.doi.org/10.5281/zenodo.58841} + } or click on the DOI badge below for more citation formats. diff --git a/hyperspy/_components/eels_cl_edge.py b/hyperspy/_components/eels_cl_edge.py index 60c26fca75..c37b1631c9 100755 --- a/hyperspy/_components/eels_cl_edge.py +++ b/hyperspy/_components/eels_cl_edge.py @@ -306,6 +306,15 @@ def function(self, E): """Returns the number of counts in barns """ + shift = self.onset_energy.value - self.GOS.onset_energy + if shift != self.GOS.energy_shift: + # Because hspy Events are not executed in any given order, + # an external function could be in the same event execution list + # as _integrate_GOS and be executed first. That can potentially + # cause an error that enforcing _integrate_GOS here prevents. Note + # that this is suboptimal because _integrate_GOS is computed twice + # unnecessarily. + self._integrate_GOS() Emax = self.GOS.energy_axis[-1] + self.GOS.energy_shift cts = np.zeros((len(E))) bsignal = (E >= self.onset_energy.value) diff --git a/hyperspy/_signals/eels.py b/hyperspy/_signals/eels.py index 35387e0c63..8cc029fd07 100644 --- a/hyperspy/_signals/eels.py +++ b/hyperspy/_signals/eels.py @@ -170,12 +170,11 @@ def estimate_zero_loss_peak_centre(self, mask=None): self._check_signal_dimension_equals_one() self._check_navigation_mask(mask) zlpc = self.valuemax(-1) - if self.axes_manager.navigation_dimension == 1: - zlpc = zlpc.as_signal1D(0) - elif self.axes_manager.navigation_dimension > 1: - zlpc = zlpc.as_signal2D((0, 1)) if mask is not None: zlpc.data[mask.data] = np.nan + zlpc.set_signal_type("") + title = self.metadata.General.title + zlpc.metadata.General.title = "ZLP(%s)" % title return zlpc def align_zero_loss_peak( @@ -337,20 +336,22 @@ def estimate_elastic_scattering_intensity( if isinstance(threshold, numbers.Number): I0 = self.isig[:threshold].integrate1D(-1) else: - I0 = self._get_navigation_signal().transpose(signal_axes=0) - for i, s in progressbar(enumerate(self), - total=self.axes_manager.navigation_size, - disable=not show_progressbar, - leave=True): - threshold_ = threshold.isig[I0.axes_manager.indices].data[0] - if np.isnan(threshold_): - s.data[:] = np.nan - else: - s.data[:] = (self.inav[I0.axes_manager.indices].isig[ - :threshold_].integrate1D(-1).data) - I0 = I0.transpose(signal_axes=self.axes_manager.navigation_dimension) + I0 = self._get_navigation_signal() + I0.axes_manager.set_signal_dimension(0) + with progressbar(total=self.axes_manager.navigation_size, + disable=not show_progressbar, + leave=True) as pbar: + for i, (i0, th, s) in enumerate(zip(I0._iterate_signal(), + threshold._iterate_signal(), + self)): + if np.isnan(th[0]): + i0[:] = np.nan + else: + i0[:] = s.isig[:th[0]].integrate1D(-1).data + pbar.update(1) I0.metadata.General.title = ( self.metadata.General.title + ' elastic intensity') + I0.set_signal_type("") if self.tmp_parameters.has_item('filename'): I0.tmp_parameters.filename = ( self.tmp_parameters.filename + @@ -461,16 +462,15 @@ def estimate_elastic_scattering_threshold(self, # Create spectrum image, stop and return value threshold.metadata.General.title = ( self.metadata.General.title + - ' ZLP threshold') + ' elastic scattering threshold') if self.tmp_parameters.has_item('filename'): threshold.tmp_parameters.filename = ( self.tmp_parameters.filename + - '_ZLP_threshold') + '_elastic_scattering_threshold') threshold.tmp_parameters.folder = self.tmp_parameters.folder threshold.tmp_parameters.extension = \ self.tmp_parameters.extension - threshold = threshold.transpose(signal_axes=min( - 2, self.axes_manager.navigation_dimension)) + threshold.set_signal_type("") return threshold def estimate_thickness(self, @@ -532,6 +532,8 @@ def estimate_thickness(self, s.tmp_parameters.folder = self.tmp_parameters.folder s.tmp_parameters.extension = \ self.tmp_parameters.extension + s.axes_manager.set_signal_dimension(0) + s.set_signal_type("") return s def fourier_log_deconvolution(self, diff --git a/hyperspy/_signals/signal1d.py b/hyperspy/_signals/signal1d.py index 4e015c7b0e..0ba41f4da6 100644 --- a/hyperspy/_signals/signal1d.py +++ b/hyperspy/_signals/signal1d.py @@ -1221,6 +1221,9 @@ def estimate_peak_width(self, right.metadata.General.title = ( self.metadata.General.title + " full-width at %.1f maximum right position" % factor) + for signal in (left, width, right): + signal.axes_manager.set_signal_dimension(0) + signal.set_signal_type("") if return_interval is True: return [width, left, right] else: diff --git a/hyperspy/axes.py b/hyperspy/axes.py index 396276250f..c1f52aaa00 100644 --- a/hyperspy/axes.py +++ b/hyperspy/axes.py @@ -28,7 +28,6 @@ from hyperspy.misc.math_tools import isfloat import warnings -from hyperspy.exceptions import VisibleDeprecationWarning class ndindex_nat(np.ndindex): @@ -312,22 +311,6 @@ def __repr__(self): def __str__(self): return self._get_name() + " axis" - def connect(self, f): - warnings.warn( - "The method `DataAxis.connect()` has been deprecated and will " - "be removed in HyperSpy 0.10. Please use " - "`DataAxis.events.value_changed.connect()` instead.", - VisibleDeprecationWarning) - self.events.value_changed.connect(f, []) - - def disconnect(self, f): - warnings.warn( - "The method `DataAxis.disconnect()` has been deprecated and " - "will be removed in HyperSpy 0.10. Please use " - "`DataAxis.events.indices_changed.disconnect()` instead.", - VisibleDeprecationWarning) - self.events.value_changed.disconnect(f) - def update_index_bounds(self): self.high_index = self.size - 1 @@ -406,14 +389,6 @@ def value2index(self, value, rounding=round): else: raise ValueError("The value is out of the axis limits") - def set_index_from_value(self, value): - warnings.warn( - "The method `DataAxis.set_index_from_value()` has been deprecated " - "and will be removed in HyperSpy 0.10. Please set the value using " - "the `value` attribute and the index will update automatically.", - VisibleDeprecationWarning) - self.value = value - def index2value(self, index): if isinstance(index, np.ndarray): return self.axis[index.ravel()].reshape(index.shape) @@ -861,7 +836,7 @@ def update_axes_attributes_from(self, axes, self.events.any_axis_changed.trigger(obj=self) def _update_attributes(self): - getitem_tuple = () + getitem_tuple = [] values = [] self.signal_axes = () self.navigation_axes = () @@ -876,10 +851,12 @@ def _update_attributes(self): else: getitem_tuple += axis.slice, self.signal_axes += axis, + if not self.signal_axes and self.navigation_axes: + getitem_tuple[-1] = slice(axis.index, axis.index + 1) self.signal_axes = self.signal_axes[::-1] self.navigation_axes = self.navigation_axes[::-1] - self._getitem_tuple = getitem_tuple + self._getitem_tuple = tuple(getitem_tuple) self.signal_dimension = len(self.signal_axes) self.navigation_dimension = len(self.navigation_axes) if self.navigation_dimension != 0: @@ -929,22 +906,6 @@ def set_signal_dimension(self, value): for axis in self._axes: axis.navigate = tl.pop(0) - def connect(self, f): - warnings.warn( - "The method `AxesManager.connect()` has been deprecated and will " - "be removed in HyperSpy 0.10. Please use " - "`AxesManager.events.indices_changed.connect()` instead.", - VisibleDeprecationWarning) - self.events.indices_changed.connect(f, []) - - def disconnect(self, f): - warnings.warn( - "The method `AxesManager.disconnect()` has been deprecated and " - "will be removed in HyperSpy 0.10. Please use " - "`AxesManager.events.indices_changed.disconnect()` instead.", - VisibleDeprecationWarning) - self.events.indices_changed.disconnect(f) - def key_navigator(self, event): if len(self.navigation_axes) not in (1, 2): return diff --git a/hyperspy/component.py b/hyperspy/component.py index be0252f69e..651a1c6708 100755 --- a/hyperspy/component.py +++ b/hyperspy/component.py @@ -33,7 +33,6 @@ from hyperspy.misc.export_dictionary import export_to_dictionary, \ load_from_dictionary from hyperspy.events import Events, Event -from hyperspy.exceptions import VisibleDeprecationWarning import logging @@ -200,22 +199,6 @@ def __repr__(self): def __len__(self): return self._number_of_elements - def connect(self, f): - warnings.warn( - "The method `Parameter.connect()` has been deprecated and will be " - "removed in HyperSpy 0.10. Please use " - "`Parameter.events.value_changed.connect()` instead.", - VisibleDeprecationWarning) - self.events.value_changed.connect(f, []) - - def disconnect(self, f): - warnings.warn( - "The method `Parameter.disconnect()` has been deprecated and will " - "be removed in HyperSpy 0.10. Please use " - "`Parameter.events.value_changed.disconnect()` instead.", - VisibleDeprecationWarning) - self.events.value_changed.disconnect(f) - def _get_value(self): if self.twin is None: return self.__value @@ -839,22 +822,6 @@ def _axes_manager(self, value): parameter._axes_manager = value self.__axes_manager = value - def connect(self, f): - warnings.warn( - "The method `Component.connect()` has been deprecated and will be " - "removed in HyperSpy 0.10. Please use " - "`Component.events.active_changed.connect()` instead.", - VisibleDeprecationWarning) - self.events.active_changed.connect(f, []) - - def disconnect(self, f): - warnings.warn( - "The method `Component.disconnect()` has been deprecated and will " - "be removed in HyperSpy 0.10. Please use " - "`Component.events.active_changed.disconnect()` instead.", - VisibleDeprecationWarning) - self.events.active_changed.disconnect(f) - def _get_active(self): if self.active_is_multidimensional is True: # The following should set diff --git a/hyperspy/messages.py b/hyperspy/messages.py deleted file mode 100755 index b39de72f6f..0000000000 --- a/hyperspy/messages.py +++ /dev/null @@ -1,62 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2007-2016 The HyperSpy developers -# -# This file is part of HyperSpy. -# -# HyperSpy is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# HyperSpy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with HyperSpy. If not, see . - -import sys - -import logging -import warnings -from hyperspy.exceptions import VisibleDeprecationWarning - -_logger = logging.getLogger(__name__) - - -def warning_exit(text): - _logger.critical(text) - warnings.warn( - "The function `warning_exit()` has been deprecated, and " - "will be removed in HyperSpy 0.10. Please raise an appropriate " - "`exception instead.", - VisibleDeprecationWarning) - sys.exit(1) - - -def warning(text): - _logger.warning(text) - warnings.warn( - "The function `warning()` has been deprecated in favour of python " - "logging. It will be removed in HyperSpy 0.10. Please use " - "`logging.getLogger(__name__).warning()` instead.", - VisibleDeprecationWarning) - - -def information(text): - _logger.info(text) - warnings.warn( - "The function `information()` has been deprecated in favour of python " - "logging. It will be removed in HyperSpy 0.10. Please use " - "`logging.getLogger(__name__).info()` instead.", - VisibleDeprecationWarning) - - -def alert(text): - _logger.error(text) - warnings.warn( - "The function `alert()` has been deprecated in favour of python " - "logging. It will be removed in HyperSpy 0.10. Please use " - "`logging.getLogger(__name__).error()` instead.", - VisibleDeprecationWarning) diff --git a/hyperspy/misc/slicing.py b/hyperspy/misc/slicing.py index a085a3e8f2..2a0e0940a4 100644 --- a/hyperspy/misc/slicing.py +++ b/hyperspy/misc/slicing.py @@ -208,7 +208,8 @@ def _get_array_slices(self, slices, isNavigation=None): def _slicer(self, slices, isNavigation=None, out=None): array_slices = self._get_array_slices(slices, isNavigation) if out is None: - _obj = self._deepcopy_with_new_data(self.data[array_slices]) + _obj = self._deepcopy_with_new_data(self.data[array_slices], + copy_variance=True) _to_remove = [] for slice_, axis in zip(array_slices, _obj.axes_manager._axes): if (isinstance(slice_, slice) or diff --git a/hyperspy/model.py b/hyperspy/model.py index dda168b818..038bffa2ad 100755 --- a/hyperspy/model.py +++ b/hyperspy/model.py @@ -547,13 +547,18 @@ def suspend_update(self, update_on_resume=True): if update_on_resume is True: self.update_plot() + def _update_model_line(self): + if (self._plot_active is True and + self._model_line is not None): + self._model_line.update() + def _close_plot(self): if self._plot_components is True: self.disable_plot_components() self._disconnect_parameters2update_plot(components=self) self._model_line = None - def _update_model_repr(self): + def _update_model_line(self): if (self._plot_active is True and self._model_line is not None): self._model_line.update() diff --git a/hyperspy/signal.py b/hyperspy/signal.py index d378a95cc2..50717d1b4a 100644 --- a/hyperspy/signal.py +++ b/hyperspy/signal.py @@ -1653,7 +1653,7 @@ def _check_signal_dimension_equals_two(self): if self.axes_manager.signal_dimension != 2: raise SignalDimensionError(self.axes_manager.signal_dimension, 2) - def _deepcopy_with_new_data(self, data=None): + def _deepcopy_with_new_data(self, data=None, copy_variance=False): """Returns a deepcopy of itself replacing the data. This method has the advantage over deepcopy that it does not @@ -1668,12 +1668,16 @@ def _deepcopy_with_new_data(self, data=None): ns : Signal """ + old_np = None try: old_data = self.data self.data = None old_plot = self._plot self._plot = None old_models = self.models._models + if not copy_variance and "Noise_properties" in self.metadata.Signal: + old_np = self.metadata.Signal.Noise_properties + del self.metadata.Signal.Noise_properties self.models._models = DictionaryTreeBrowser() ns = self.deepcopy() ns.data = np.atleast_1d(data) @@ -1682,6 +1686,8 @@ def _deepcopy_with_new_data(self, data=None): self.data = old_data self._plot = old_plot self.models._models = old_models + if old_np is not None: + self.metadata.Signal.Noise_properties = old_np def _summary(self): string = "\n\tTitle: " @@ -2239,7 +2245,7 @@ def rebin(self, new_shape, out=None): new_shape[axis.index_in_axes_manager]) factors = (np.array(self.data.shape) / np.array(new_shape_in_array)) - s = out or self._deepcopy_with_new_data(None) + s = out or self._deepcopy_with_new_data(None, copy_variance=True) data = array_tools.rebin(self.data, new_shape_in_array) if out: out.data[:] = data @@ -2582,20 +2588,25 @@ def _iterate_signal(self): self._make_sure_data_is_contiguous() axes = [axis.index_in_array for axis in self.axes_manager.signal_axes] - unfolded_axis = ( - self.axes_manager.navigation_axes[0].index_in_array) - new_shape = [1] * len(self.data.shape) - for axis in axes: - new_shape[axis] = self.data.shape[axis] - new_shape[unfolded_axis] = -1 + if axes: + unfolded_axis = ( + self.axes_manager.navigation_axes[0].index_in_array) + new_shape = [1] * len(self.data.shape) + for axis in axes: + new_shape[axis] = self.data.shape[axis] + new_shape[unfolded_axis] = -1 + else: # signal_dimension == 0 + new_shape = (-1, 1) + axes = [1] + unfolded_axis = 0 # Warning! if the data is not contigous it will make a copy!! data = self.data.reshape(new_shape) + getitem = [0] * len(data.shape) + for axis in axes: + getitem[axis] = slice(None) for i in range(data.shape[unfolded_axis]): - getitem = [0] * len(data.shape) - for axis in axes: - getitem[axis] = slice(None) getitem[unfolded_axis] = i - yield(data[getitem]) + yield(data[tuple(getitem)]) def _remove_axis(self, axes): am = self.axes_manager @@ -3247,7 +3258,7 @@ def map(self, function, scale.add(self.axes_manager.signal_axes[i].scale) units.add(self.axes_manager.signal_axes[i].units) if len(units) != 1 or len(scale) != 1: - warnings.warn( + _logger.warning( "The function you applied does not take into " "account the difference of units and of scales in-between" " axes.") @@ -3990,12 +4001,15 @@ def iterable_not_string(thing): "The passed navigation_axes argument is not valid") else: raise ValueError("The passed signal_axes argument is not valid") + # translate to axes idx from actual objects for variance + idx_sig = [ax.index_in_axes_manager for ax in signal_axes] + idx_nav = [ax.index_in_axes_manager for ax in navigation_axes] # get data view array_order = tuple( ax.index_in_array for ax in navigation_axes) array_order += tuple(ax.index_in_array for ax in signal_axes) newdata = self.data.transpose(array_order) - res = self._deepcopy_with_new_data(newdata) + res = self._deepcopy_with_new_data(newdata, copy_variance=True) # reconfigure the axes of the axesmanager: ram = res.axes_manager @@ -4010,14 +4024,11 @@ def iterable_not_string(thing): ram._update_attributes() ram._update_trait_handlers(remove=False) res._assign_subclass() - # translate to axes names from actual objects for variance - names_sig = [ax.name for ax in signal_axes] - names_nav = [ax.name for ax in navigation_axes] if res.metadata.has_item("Signal.Noise_properties.variance"): var = res.metadata.Signal.Noise_properties.variance if isinstance(var, BaseSignal): - var = var.transpose(signal_axes=names_sig, - navigation_axes=names_nav, + var = var.transpose(signal_axes=idx_sig, + navigation_axes=idx_nav, optimize=optimize) res.metadata.set_item('Signal.Noise_properties.variance', var) if optimize: diff --git a/hyperspy/tests/signal/test_1D_tools.py b/hyperspy/tests/signal/test_1D_tools.py index 37fb5dc53a..578f97490b 100644 --- a/hyperspy/tests/signal/test_1D_tools.py +++ b/hyperspy/tests/signal/test_1D_tools.py @@ -237,6 +237,9 @@ def test_full_range(self): nt.assert_equal(width, 2.35482074) nt.assert_equal(left, 0.82258963) nt.assert_equal(right, 3.17741037) + for t in (width, left, right): + nt.assert_equal(t.metadata.Signal.signal_type, "") + nt.assert_equal(t.axes_manager.signal_dimension, 0) def test_too_narrow_range(self): width, left, right = self.s.estimate_peak_width( diff --git a/hyperspy/tests/signal/test_eels.py b/hyperspy/tests/signal/test_eels.py index d992da322d..91dfedec74 100644 --- a/hyperspy/tests/signal/test_eels.py +++ b/hyperspy/tests/signal/test_eels.py @@ -17,10 +17,10 @@ import numpy as np +import numpy.testing import nose.tools as nt import hyperspy.api as hs -from hyperspy.misc.test_utils import assert_warns class Test_Estimate_Elastic_Scattering_Threshold: @@ -53,6 +53,8 @@ def test_min_in_window_with_smoothing(self): tol=0.00001, ) np.testing.assert_allclose(thr.data, 2.5, atol=10e-3) + nt.assert_equal(thr.metadata.Signal.signal_type, "") + nt.assert_equal(thr.axes_manager.signal_dimension, 0) def test_min_in_window_without_smoothing_single_spectrum(self): s = self.signal.inav[0, 0] @@ -81,6 +83,23 @@ def test_min_not_in_window(self): ).data nt.assert_true(np.all(np.isnan(data))) + def test_estimate_elastic_scattering_intensity(self): + s = self.signal + threshold = s.estimate_elastic_scattering_threshold(window=4.) + # Threshold is nd signal + t = s.estimate_elastic_scattering_intensity(threshold=threshold) + nt.assert_equal(t.metadata.Signal.signal_type, "") + nt.assert_equal(t.axes_manager.signal_dimension, 0) + np.testing.assert_array_almost_equal(t.data, 249999.985133) + # Threshold is signal, 1 spectrum + s0 = s.inav[0] + t0 = s0.estimate_elastic_scattering_threshold(window=4.) + t = s0.estimate_elastic_scattering_intensity(threshold=t0) + np.testing.assert_array_almost_equal(t.data, 249999.985133) + # Threshold is value + t = s.estimate_elastic_scattering_intensity(threshold=2.5) + np.testing.assert_array_almost_equal(t.data, 249999.985133) + class TestEstimateZLPCentre: @@ -92,11 +111,13 @@ def setUp(self): def test_estimate_zero_loss_peak_centre(self): s = self.signal - np.testing.assert_allclose( - s.estimate_zero_loss_peak_centre().data, - np.arange(100, - 101, - 0.1)) + zlpc = s.estimate_zero_loss_peak_centre() + np.testing.assert_allclose(zlpc.data, + np.arange(100, + 101, + 0.1)) + nt.assert_equal(zlpc.metadata.Signal.signal_type, "") + nt.assert_equal(zlpc.axes_manager.signal_dimension, 0) class TestAlignZLP: diff --git a/hyperspy/tests/signal/test_apply_function.py b/hyperspy/tests/signal/test_map_method.py similarity index 77% rename from hyperspy/tests/signal/test_apply_function.py rename to hyperspy/tests/signal/test_map_method.py index f413d3f309..e1575ee18e 100644 --- a/hyperspy/tests/signal/test_apply_function.py +++ b/hyperspy/tests/signal/test_map_method.py @@ -77,3 +77,27 @@ def test_constant_sigma(self): ([[0.42207377, 1., 1.57792623], [3.42207377, 4., 4.57792623]])))) nt.assert_true(m.data_changed.called) + + +class TestSignal0D: + + def setup(self): + self.s = hs.signals.BaseSignal(np.arange(0., 6).reshape((2, 3))) + self.s.axes_manager.set_signal_dimension(0) + + def test(self): + s = self.s + m = mock.Mock() + s.events.data_changed.connect(m.data_changed) + s.map(lambda x, e: x ** e, e=2, show_progressbar=None) + nt.assert_true( + np.allclose(s.data, (np.arange(0., 6) ** 2).reshape((2, 3)))) + nt.assert_true(m.data_changed.called) + + def test_nav_dim_1(self): + s = self.s.inav[1, 1] + m = mock.Mock() + s.events.data_changed.connect(m.data_changed) + s.map(lambda x, e: x ** e, e=2, show_progressbar=None) + nt.assert_true(np.allclose(s.data, self.s.inav[1, 1].data ** 2)) + nt.assert_true(m.data_changed.called)