From e496054b8497fbd74e5f30d3e30385fcefbdd89a Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:56:59 -0500 Subject: [PATCH 01/20] boilerplate --- docs/user-guide/weighted-average.ipynb | 45 ++++++++++++++++++++++++++ docs/userguide.rst | 3 ++ test/test_weighted_mean.py | 18 +++++++++++ uxarray/core/dataarray.py | 4 +++ 4 files changed, 70 insertions(+) create mode 100644 docs/user-guide/weighted-average.ipynb create mode 100644 test/test_weighted_mean.py diff --git a/docs/user-guide/weighted-average.ipynb b/docs/user-guide/weighted-average.ipynb new file mode 100644 index 000000000..c723f79a6 --- /dev/null +++ b/docs/user-guide/weighted-average.ipynb @@ -0,0 +1,45 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Weighted Average" + ], + "metadata": { + "collapsed": false + }, + "id": "4850ba8becab8b0d" + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false + }, + "id": "ee8c405203e6faf" + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/userguide.rst b/docs/userguide.rst index 47c43d562..18b566033 100644 --- a/docs/userguide.rst +++ b/docs/userguide.rst @@ -46,6 +46,9 @@ These user guides provide detailed explanations of the core functionality in UXa `Topological Aggregations `_ Aggregate data across grid dimensions +`Weighted Average `_ + Compute the weighted average + `Calculus Operators `_ Apply calculus operators (gradient, integral) on unstructured grid data diff --git a/test/test_weighted_mean.py b/test/test_weighted_mean.py new file mode 100644 index 000000000..628683a80 --- /dev/null +++ b/test/test_weighted_mean.py @@ -0,0 +1,18 @@ +import uxarray as ux +import os + +import pytest + +from pathlib import Path + +current_path = Path(os.path.dirname(os.path.realpath(__file__))) + + +quad_hex_grid_path = current_path / 'meshfiles' / "ugrid" / "quad-hexagon" / 'grid.nc' +quad_hex_data_path = current_path / 'meshfiles' / "ugrid" / "quad-hexagon" / 'data.nc' + + +def test_quad_hex(): + uxds = ux.open_dataset(quad_hex_grid_path, quad_hex_data_path) + + # add test here for weighted_mean diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index a99defd16..d9f61119f 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -404,6 +404,10 @@ def nodal_average(self): return self.topological_mean(destination="face") + def weighted_mean(self, **kwargs): + # implement weighted mean here + pass + def topological_mean( self, destination: Literal["node", "edge", "face"], From 23c7a7b793501a3098b462c11182b0b53175f4e2 Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> Date: Tue, 2 Jul 2024 13:42:55 -0500 Subject: [PATCH 02/20] update boilerplate --- uxarray/core/dataarray.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index d9f61119f..bab6746ae 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -404,9 +404,26 @@ def nodal_average(self): return self.topological_mean(destination="face") - def weighted_mean(self, **kwargs): - # implement weighted mean here - pass + def mean(self, dim=None, *, skipna=None, keep_attrs=None, weighted=True, **kwargs): + if weighted: + if self._face_centered(): + # use face areas as weight + pass + elif self._face_centered(): + # use edge magnitude as weight + pass + elif self._node_centered(): + # is there a weight that we could use? + pass + else: + # can't apply weighted mean over a non-grid dimension + raise ValueError + + # add computation below + # ... + + else: + return super().mean(dim=None, skipna=None, keep_attrs=None, **kwargs) def topological_mean( self, From 3094cfcf1208359dfc017645047c07b501fecef6 Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> Date: Wed, 3 Jul 2024 03:14:19 -0500 Subject: [PATCH 03/20] fix boilerplate --- test/test_weighted_mean.py | 10 ++++++---- uxarray/core/dataarray.py | 18 ++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/test/test_weighted_mean.py b/test/test_weighted_mean.py index 628683a80..3a4c88dbf 100644 --- a/test/test_weighted_mean.py +++ b/test/test_weighted_mean.py @@ -8,11 +8,13 @@ current_path = Path(os.path.dirname(os.path.realpath(__file__))) -quad_hex_grid_path = current_path / 'meshfiles' / "ugrid" / "quad-hexagon" / 'grid.nc' -quad_hex_data_path = current_path / 'meshfiles' / "ugrid" / "quad-hexagon" / 'data.nc' +csne30_grid_path = current_path / 'meshfiles' / "ugrid" / "outCSne30" / "outCSne30.ug" +csne30_data_path = current_path / 'meshfiles' / "ugrid" / "outCSne30" / "outCSne30_vortex.nc" -def test_quad_hex(): - uxds = ux.open_dataset(quad_hex_grid_path, quad_hex_data_path) +def test_weighted_mean(): + uxds = ux.open_dataset(csne30_grid_path, csne30_data_path) # add test here for weighted_mean + + res = uxds['psi'].mean(weighted=True) diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index bab6746ae..b969b0743 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -408,21 +408,23 @@ def mean(self, dim=None, *, skipna=None, keep_attrs=None, weighted=True, **kwarg if weighted: if self._face_centered(): # use face areas as weight + # weight = self.uxgrid.face_areas.values pass - elif self._face_centered(): + elif self._edge_centered(): # use edge magnitude as weight - pass - elif self._node_centered(): - # is there a weight that we could use? + # weight = None pass else: - # can't apply weighted mean over a non-grid dimension - raise ValueError + # apply regular Xarray mean + return super().mean(dim=None, skipna=None, keep_attrs=None, **kwargs) + + # compute weighted mean + # weighted_mean = (self.data * weight) / weight.sum() - # add computation below - # ... + # create a UxDataArray and return it else: + # apply regular Xarray mean return super().mean(dim=None, skipna=None, keep_attrs=None, **kwargs) def topological_mean( From 23a6422a1015b92635e664ec85b536469c82ad3e Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> Date: Fri, 5 Jul 2024 13:51:24 -0500 Subject: [PATCH 04/20] add quad hexagon to tests --- test/test_weighted_mean.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/test/test_weighted_mean.py b/test/test_weighted_mean.py index 3a4c88dbf..823419cda 100644 --- a/test/test_weighted_mean.py +++ b/test/test_weighted_mean.py @@ -1,6 +1,8 @@ import uxarray as ux import os +import numpy.testing as nt + import pytest from pathlib import Path @@ -11,10 +13,25 @@ csne30_grid_path = current_path / 'meshfiles' / "ugrid" / "outCSne30" / "outCSne30.ug" csne30_data_path = current_path / 'meshfiles' / "ugrid" / "outCSne30" / "outCSne30_vortex.nc" +quad_hex_grid_path = current_path / 'meshfiles' / "ugrid" / "quad-hexagon" / "grid.nc" +quad_hex_data_path = current_path / 'meshfiles' / "ugrid" / "quad-hexagon" / "data.nc" + + +def test_quad_hex(): + uxds = ux.open_dataset(quad_hex_grid_path, quad_hex_data_path) + + # create an array of expected values here (can compute by hand) + expected = ... + + result = uxds['t2m'].mean(weighted=True) + + # make sure the results are almost equal + # nt.assert_almost_equal(result.values, expected) + -def test_weighted_mean(): +def test_csne30(): uxds = ux.open_dataset(csne30_grid_path, csne30_data_path) # add test here for weighted_mean - res = uxds['psi'].mean(weighted=True) + result = uxds['psi'].mean(weighted=True) From 5dbaf5dde5d65a47cf9b999a79824aaad5cbafd2 Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> Date: Tue, 16 Jul 2024 15:16:12 -0500 Subject: [PATCH 05/20] write tests, work on api design --- test/test_weighted_mean.py | 50 ++++++++++++++++++++++++++++++-------- uxarray/core/dataarray.py | 27 +++++++++++++++----- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/test/test_weighted_mean.py b/test/test_weighted_mean.py index 823419cda..5f88c950a 100644 --- a/test/test_weighted_mean.py +++ b/test/test_weighted_mean.py @@ -2,6 +2,7 @@ import os import numpy.testing as nt +import numpy as np import pytest @@ -14,24 +15,53 @@ csne30_data_path = current_path / 'meshfiles' / "ugrid" / "outCSne30" / "outCSne30_vortex.nc" quad_hex_grid_path = current_path / 'meshfiles' / "ugrid" / "quad-hexagon" / "grid.nc" -quad_hex_data_path = current_path / 'meshfiles' / "ugrid" / "quad-hexagon" / "data.nc" +quad_hex_data_path_face_centered = current_path / 'meshfiles' / "ugrid" / "quad-hexagon" / "data.nc" +quad_hex_data_path_edge_centered = current_path / 'meshfiles' / "ugrid" / "quad-hexagon" / "random-edge-data.nc" -def test_quad_hex(): - uxds = ux.open_dataset(quad_hex_grid_path, quad_hex_data_path) +def test_quad_hex_face_centered(): + """Compares the weighted average computation for the quad hexagon grid + using a face centered data variable to the expected value computed by + hand.""" + uxds = ux.open_dataset(quad_hex_grid_path, quad_hex_data_path_face_centered) - # create an array of expected values here (can compute by hand) - expected = ... + # expected weighted average computed by hand + expected_weighted_mean = 297.55 + # compute the weighted mean result = uxds['t2m'].mean(weighted=True) - # make sure the results are almost equal - # nt.assert_almost_equal(result.values, expected) + # ensure values are within 3 decimal points of each other + nt.assert_almost_equal(result.values, expected_weighted_mean, decimal=3) +def test_quad_hex_edge_centered(): + """Compares the weighted average computation for the quad hexagon grid + using an edge centered data variable to the expected value computed by + hand.""" + uxds = ux.open_dataset(quad_hex_grid_path, quad_hex_data_path_edge_centered) -def test_csne30(): + # expected weighted average computed by hand + # expected_weighted_mean = 297.55 + expected_weighted_mean = (uxds['random_data_edge'].values * uxds.uxgrid.edge_node_distances).sum() / uxds.uxgrid.edge_node_distances.sum() + + # compute the weighted mean + result = uxds['random_data_edge'].mean(weighted=True) + + nt.assert_equal(result, expected_weighted_mean) + + +def test_csne30_equal_area(): + """Compute the weighted average with a grid that has equal-area faces and + compare the result to the regular mean.""" uxds = ux.open_dataset(csne30_grid_path, csne30_data_path) + face_areas = uxds.uxgrid.face_areas + + # set the area of each face to be one + uxds.uxgrid._ds['face_areas'].data = np.ones(uxds.uxgrid.n_face) + - # add test here for weighted_mean + weighted_mean = uxds['psi'].mean(weighted=True) + unweighted_mean = uxds['psi'].mean(weighted=False) - result = uxds['psi'].mean(weighted=True) + # with equal area, both should be equal + nt.assert_equal(weighted_mean, unweighted_mean) diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index b969b0743..ff3d16535 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -125,6 +125,12 @@ def uxgrid(self): def uxgrid(self, ugrid_obj): self._uxgrid = ugrid_obj + def from_xarray(self, data_array, uxgrid): + """Take a ``xarray.DataArray`` and convert it to a + ``uxarray.UxDataArray``""" + + pass + def to_geodataframe(self, override=False, cache=True, exclude_antimeridian=False): """Constructs a ``spatialpandas.GeoDataFrame`` with a "geometry" column, containing a collection of Shapely Polygons or MultiPolygons @@ -404,22 +410,31 @@ def nodal_average(self): return self.topological_mean(destination="face") - def mean(self, dim=None, *, skipna=None, keep_attrs=None, weighted=True, **kwargs): + def mean(self, dim=None, *, skipna=None, keep_attrs=None, weighted=False, **kwargs): if weighted: if self._face_centered(): # use face areas as weight - # weight = self.uxgrid.face_areas.values - pass + weights = self.uxgrid.face_areas elif self._edge_centered(): # use edge magnitude as weight - # weight = None - pass + weights = self.uxgrid.edge_node_distances else: # apply regular Xarray mean + warnings.warn( + "Attempting to perform a weighted mean calculation on a variable that does not have" + "associated weights. Weighted mean is only supported for face or edge centered " + "variables. Performing an unweighted mean." + ) + return super().mean(dim=None, skipna=None, keep_attrs=None, **kwargs) + # compute the total weight + total_weight = weights.sum() + # compute weighted mean - # weighted_mean = (self.data * weight) / weight.sum() + weighted_mean = (self.data * weights).sum() / total_weight + + return weighted_mean # create a UxDataArray and return it From 4c3290bcc1dbfa6ec834ce8bd0e58a1cbccfd0ac Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> Date: Wed, 17 Jul 2024 11:27:37 -0500 Subject: [PATCH 06/20] asv benchmark --- benchmarks/asv.conf.json | 2 +- benchmarks/mpas_ocean.py | 18 ++++++++++++++++++ uxarray/core/dataarray.py | 7 ++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/benchmarks/asv.conf.json b/benchmarks/asv.conf.json index 26987cee2..31a43921d 100644 --- a/benchmarks/asv.conf.json +++ b/benchmarks/asv.conf.json @@ -54,7 +54,7 @@ // If missing or the empty string, the tool will be automatically // determined by looking for tools on the PATH environment // variable. - "environment_type": "mamba", + "environment_type": "conda", "conda_channels": ["conda-forge"], // timeout in seconds for installing any dependencies in environment diff --git a/benchmarks/mpas_ocean.py b/benchmarks/mpas_ocean.py index e124386ff..6068551ea 100644 --- a/benchmarks/mpas_ocean.py +++ b/benchmarks/mpas_ocean.py @@ -97,3 +97,21 @@ def teardown(self, resolution): def time_n_nodes_per_face(self, resolution): self.uxds.uxgrid.n_nodes_per_face + + +class WeightedMean: + + param_names = ['resolution'] + params = ['480km', '120km'] + + def setup(self, resolution): + self.uxds = ux.open_dataset(file_path_dict[resolution][0], file_path_dict[resolution][1]) + _ = self.uxds.uxgrid.face_areas + _ = self.uxds.uxgrid.edge_node_distances + + def teardown(self, resolution): + del self.uxds + + + def time_weighted_mean_face_centered(self, resolution): + self.uxds['bottomDepth'].mean(weighted=True) diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index ff3d16535..693b436de 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -410,6 +410,8 @@ def nodal_average(self): return self.topological_mean(destination="face") + # weighted_mean() + # .weighted.mean() def mean(self, dim=None, *, skipna=None, keep_attrs=None, weighted=False, **kwargs): if weighted: if self._face_centered(): @@ -432,11 +434,10 @@ def mean(self, dim=None, *, skipna=None, keep_attrs=None, weighted=False, **kwar total_weight = weights.sum() # compute weighted mean - weighted_mean = (self.data * weights).sum() / total_weight - - return weighted_mean + weighted_mean = (self * weights).sum() / total_weight # create a UxDataArray and return it + return UxDataArray(weighted_mean, uxgrid=self.uxgrid) else: # apply regular Xarray mean From 6553163ed909ea2aae58a9b3e244ded0c73d7c60 Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> Date: Wed, 17 Jul 2024 11:37:02 -0500 Subject: [PATCH 07/20] update asv benchmark --- ci/asv.yml | 2 +- ci/environment.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/asv.yml b/ci/asv.yml index 76be35aeb..5fd70ebe2 100644 --- a/ci/asv.yml +++ b/ci/asv.yml @@ -28,5 +28,5 @@ dependencies: - pip: - antimeridian - pyfma - - asv<0.6.2 + - git+https://github.com/airspeed-velocity/asv - -e ../ diff --git a/ci/environment.yml b/ci/environment.yml index 56b53aeb5..0efda75f7 100644 --- a/ci/environment.yml +++ b/ci/environment.yml @@ -31,3 +31,4 @@ dependencies: - xarray - pip: - pyfma + - git+https://github.com/airspeed-velocity/asv From 003287005fea91394416695f5b294e106a144173 Mon Sep 17 00:00:00 2001 From: Rachel Tam <116197442+rytam2@users.noreply.github.com> Date: Fri, 26 Jul 2024 17:43:28 -0500 Subject: [PATCH 08/20] Committing weighted mean modifications from rtam/weighted mean to uxarray/weighted-mean (#866) * updated mean function with weighted arg * updated weighted-mean functionality in dataarray.py * edited weights to dask array --------- Co-authored-by: Rachel Yuen Sum Tam Co-authored-by: Rachel Yuen Sum Tam --- uxarray/core/dataarray.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) mode change 100644 => 100755 uxarray/core/dataarray.py diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py old mode 100644 new mode 100755 index ffab26b13..504a8663b --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -407,14 +407,17 @@ def nodal_average(self): # weighted_mean() # .weighted.mean() + # weights are only on grid - not supporting custom weights for now def mean(self, dim=None, *, skipna=None, keep_attrs=None, weighted=False, **kwargs): if weighted: if self._face_centered(): + grid_dim = "n_face" # use face areas as weight - weights = self.uxgrid.face_areas + weights = da.from_array(self.uxgrid.face_areas.values) elif self._edge_centered(): + grid_dim = "n_edge" # use edge magnitude as weight - weights = self.uxgrid.edge_node_distances + weights = da.from_array(self.uxgrid.edge_node_distances.values) else: # apply regular Xarray mean warnings.warn( @@ -423,13 +426,13 @@ def mean(self, dim=None, *, skipna=None, keep_attrs=None, weighted=False, **kwar "variables. Performing an unweighted mean." ) - return super().mean(dim=None, skipna=None, keep_attrs=None, **kwargs) + return super().mean(dim=dim, skipna=skipna, keep_attrs=keep_attrs, **kwargs) # compute the total weight total_weight = weights.sum() - # compute weighted mean - weighted_mean = (self * weights).sum() / total_weight + # compute weighted mean #assumption on index of dimension (last one is geometry) + weighted_mean = (self * weights).sum(dim=grid_dim) / total_weight # create a UxDataArray and return it return UxDataArray(weighted_mean, uxgrid=self.uxgrid) From f9a4d192ee9d1b53e69615c10e8543b54314be8a Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec Date: Thu, 10 Oct 2024 09:26:53 -0500 Subject: [PATCH 09/20] some cleanup --- benchmarks/mpas_ocean.py | 3 +-- uxarray/core/dataarray.py | 35 +++++++++++++++-------------------- uxarray/grid/grid.py | 3 +++ 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/benchmarks/mpas_ocean.py b/benchmarks/mpas_ocean.py index 274a2c3d0..746f9afbf 100644 --- a/benchmarks/mpas_ocean.py +++ b/benchmarks/mpas_ocean.py @@ -116,9 +116,8 @@ def setup(self, resolution): def teardown(self, resolution): del self.uxds - def time_weighted_mean_face_centered(self, resolution): - self.uxds['bottomDepth'].mean(weighted=True) + self.uxds['bottomDepth'].weighted_mean() class MatplotlibConversion: param_names = ['resolution', 'periodic_elements'] diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index 17ee069fb..8d91d20f4 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -428,32 +428,27 @@ def integrate( return uxda - def weighted_mean( - self, dim=None, *, skipna=None, keep_attrs=None, weighted=False, **kwargs - ): - if self._face_centered(): - grid_dim = "n_face" - # use face areas as weight - # weights = da.from_array(self.uxgrid.face_areas.values) - weights = None - elif self._edge_centered(): - grid_dim = "n_edge" - # use edge magnitude as weight - # weights = da.from_array(self.uxgrid.edge_node_distances.values) - weights = None + def weighted_mean(self, weights=None, **kwargs): + if weights is None: + if self._face_centered(): + weights = self.uxgrid.face_areas.data + elif self._edge_centered(): + weights = self.uxgrid.edge_node_distances.data + else: + warnings.warn( + "Attempting to perform a weighted mean calculation on a variable that does not have" + "associated weights. Weighted mean is only supported for face or edge centered " + "variables. Performing an unweighted mean." + ) else: - # apply regular Xarray mean - warnings.warn( - "Attempting to perform a weighted mean calculation on a variable that does not have" - "associated weights. Weighted mean is only supported for face or edge centered " - "variables. Performing an unweighted mean." - ) + # user-defined weights + assert weights.shape[-1] == self.shape[-1] # compute the total weight total_weight = weights.sum() # compute weighted mean #assumption on index of dimension (last one is geometry) - weighted_mean = (self * weights).sum(dim=grid_dim) / total_weight + weighted_mean = (self * weights).sum(axis=-1) / total_weight # create a UxDataArray and return it return UxDataArray(weighted_mean, uxgrid=self.uxgrid) diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index 211aa7949..2e6ba2bd5 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -1970,6 +1970,9 @@ def isel(self, **dim_kwargs): "Indexing must be along a grid dimension: ('n_node', 'n_edge', 'n_face')" ) + def get_weights(self): + pass + def get_edges_at_constant_latitude(self, lat, method="fast"): """Identifies the edges of the grid that intersect with a specified constant latitude. From efce99648063d01773697c0d988a6700cfb848b7 Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec Date: Thu, 10 Oct 2024 09:35:19 -0500 Subject: [PATCH 10/20] fix tests --- test/test_weighted_mean.py | 8 +++---- uxarray/core/dataarray.py | 48 ++++++++++++++++++++++++++++++++++++++ uxarray/grid/grid.py | 3 --- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/test/test_weighted_mean.py b/test/test_weighted_mean.py index 5f88c950a..e4cf03268 100644 --- a/test/test_weighted_mean.py +++ b/test/test_weighted_mean.py @@ -29,7 +29,7 @@ def test_quad_hex_face_centered(): expected_weighted_mean = 297.55 # compute the weighted mean - result = uxds['t2m'].mean(weighted=True) + result = uxds['t2m'].weighted_mean() # ensure values are within 3 decimal points of each other nt.assert_almost_equal(result.values, expected_weighted_mean, decimal=3) @@ -45,7 +45,7 @@ def test_quad_hex_edge_centered(): expected_weighted_mean = (uxds['random_data_edge'].values * uxds.uxgrid.edge_node_distances).sum() / uxds.uxgrid.edge_node_distances.sum() # compute the weighted mean - result = uxds['random_data_edge'].mean(weighted=True) + result = uxds['random_data_edge'].weighted_mean() nt.assert_equal(result, expected_weighted_mean) @@ -60,8 +60,8 @@ def test_csne30_equal_area(): uxds.uxgrid._ds['face_areas'].data = np.ones(uxds.uxgrid.n_face) - weighted_mean = uxds['psi'].mean(weighted=True) - unweighted_mean = uxds['psi'].mean(weighted=False) + weighted_mean = uxds['psi'].weighted_mean() + unweighted_mean = uxds['psi'].mean() # with equal area, both should be equal nt.assert_equal(weighted_mean, unweighted_mean) diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index 8d91d20f4..f558822b5 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -429,6 +429,54 @@ def integrate( return uxda def weighted_mean(self, weights=None, **kwargs): + """Compute the weighted mean of the data variable. + + This function calculates the weighted mean of a variable, + using the specified `weights`. If no weights are provided, it will automatically select + appropriate weights based on whether the variable is face-centered or edge-centered. If + the variable is neither face nor edge-centered. + + Parameters + ---------- + weights : np.ndarray or None, optional + The weights to use for the weighted mean calculation. If `None`, the function will + determine weights based on the variable's association: + - For face-centered variables: uses `self.uxgrid.face_areas.data` + - For edge-centered variables: uses `self.uxgrid.edge_node_distances.data` + If the variable is neither face-centered nor edge-centered, a warning is raised, and + an unweighted mean is computed instead. User-defined weights should match the shape + of the data variable's last dimension. + + **kwargs : dict, optional + Additional keyword arguments passed to the function (currently not used). TODO + + Returns + ------- + UxDataArray + A new `UxDataArray` object representing the weighted mean of the input variable. The + result is attached to the same `uxgrid` attribute as the original variable. + + Raises + ------ + AssertionError + If user-defined `weights` are provided and the shape of `weights` does not match + the shape of the data variable's last dimension. + + Warnings + -------- + UserWarning + Raised when attempting to compute a weighted mean on a variable without associated + weights. An unweighted mean will be computed in this case. + + Notes + ----- + - The weighted mean is computed along the last dimension of the data variable, which is + assumed to be the geometry dimension (e.g., faces, edges, or nodes). + - The computed weighted mean uses the formula: + + .. math:: + \text{{weighted mean}} = \frac{\sum (\text{{variable}} \times \text{{weights}})}{\sum \text{{weights}}} + """ if weights is None: if self._face_centered(): weights = self.uxgrid.face_areas.data diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index 2e6ba2bd5..211aa7949 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -1970,9 +1970,6 @@ def isel(self, **dim_kwargs): "Indexing must be along a grid dimension: ('n_node', 'n_edge', 'n_face')" ) - def get_weights(self): - pass - def get_edges_at_constant_latitude(self, lat, method="fast"): """Identifies the edges of the grid that intersect with a specified constant latitude. From 8c36fa061db799d3a9a5626c9a24deaab22a0ff9 Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:29:29 -0600 Subject: [PATCH 11/20] add initial dask test cases --- test/test_weighted_mean.py | 71 ++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/test/test_weighted_mean.py b/test/test_weighted_mean.py index e4cf03268..22b5ccf2e 100644 --- a/test/test_weighted_mean.py +++ b/test/test_weighted_mean.py @@ -1,12 +1,11 @@ -import uxarray as ux import os +from pathlib import Path -import numpy.testing as nt +import dask.array as da import numpy as np +import numpy.testing as nt -import pytest - -from pathlib import Path +import uxarray as ux current_path = Path(os.path.dirname(os.path.realpath(__file__))) @@ -34,6 +33,33 @@ def test_quad_hex_face_centered(): # ensure values are within 3 decimal points of each other nt.assert_almost_equal(result.values, expected_weighted_mean, decimal=3) +def test_quad_hex_face_centered_dask(): + """Compares the weighted average computation for the quad hexagon grid + using a face centered data variable on a dask-backed UxDataset & Grid to the expected value computed by + hand.""" + uxds = ux.open_dataset(quad_hex_grid_path, quad_hex_data_path_face_centered) + + # data to be dask + uxda = uxds['t2m'].chunk(n_face=1) + + # weights to be dask + uxda.uxgrid.face_areas = uxda.uxgrid.face_areas.chunk(n_face=1) + + # create lazy result + lazy_result = uxda.weighted_mean() + + assert isinstance(lazy_result.data, da.Array) + + # compute result + computed_result = lazy_result.compute() + + assert isinstance(computed_result.data, np.ndarray) + + expected_weighted_mean = 297.55 + + # ensure values are within 3 decimal points of each other + nt.assert_almost_equal(computed_result.values, expected_weighted_mean, decimal=3) + def test_quad_hex_edge_centered(): """Compares the weighted average computation for the quad hexagon grid using an edge centered data variable to the expected value computed by @@ -41,7 +67,6 @@ def test_quad_hex_edge_centered(): uxds = ux.open_dataset(quad_hex_grid_path, quad_hex_data_path_edge_centered) # expected weighted average computed by hand - # expected_weighted_mean = 297.55 expected_weighted_mean = (uxds['random_data_edge'].values * uxds.uxgrid.edge_node_distances).sum() / uxds.uxgrid.edge_node_distances.sum() # compute the weighted mean @@ -49,6 +74,35 @@ def test_quad_hex_edge_centered(): nt.assert_equal(result, expected_weighted_mean) +def test_quad_hex_edge_centered_dask(): + """Compares the weighted average computation for the quad hexagon grid + using an edge centered data variable on a dask-backed UxDataset & Grid to the expected value computed by + hand.""" + uxds = ux.open_dataset(quad_hex_grid_path, quad_hex_data_path_edge_centered) + + # data to be dask + uxda = uxds['random_data_edge'].chunk(n_edge=1) + + # weights to be dask + uxda.uxgrid.edge_node_distances = uxda.uxgrid.edge_node_distances.chunk(n_edge=1) + + # create lazy result + lazy_result = uxds['random_data_edge'].weighted_mean() + + assert isinstance(lazy_result.data, da.Array) + + # compute result + computed_result = lazy_result.compute() + + assert isinstance(computed_result.data, np.ndarray) + + # expected weighted average computed by hand + expected_weighted_mean = (uxds[ + 'random_data_edge'].values * uxds.uxgrid.edge_node_distances).sum() / uxds.uxgrid.edge_node_distances.sum() + + # ensure values are within 3 decimal points of each other + nt.assert_almost_equal(computed_result.values, expected_weighted_mean, decimal=3) + def test_csne30_equal_area(): """Compute the weighted average with a grid that has equal-area faces and @@ -65,3 +119,8 @@ def test_csne30_equal_area(): # with equal area, both should be equal nt.assert_equal(weighted_mean, unweighted_mean) + + +# TODO for Rachel +def test_csne30_equal_area_dask(): + pass From a3c87b8db86f40dd3519ae3654fac9de3899effd Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:31:47 -0600 Subject: [PATCH 12/20] use parametrize --- test/test_weighted_mean.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/test_weighted_mean.py b/test/test_weighted_mean.py index 22b5ccf2e..967a3d1a8 100644 --- a/test/test_weighted_mean.py +++ b/test/test_weighted_mean.py @@ -1,6 +1,8 @@ import os from pathlib import Path +import pytest + import dask.array as da import numpy as np import numpy.testing as nt @@ -122,5 +124,9 @@ def test_csne30_equal_area(): # TODO for Rachel -def test_csne30_equal_area_dask(): +@pytest.mark.parametrize("chunk_size", [1, 2, 4]) +def test_csne30_equal_area_dask(chunk_size): + + # ... .chunk(n_face=chunk_size) + pass From 7056451e786f85c14761afb6d0b248e1a9b427be Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:33:52 -0600 Subject: [PATCH 13/20] add boilerplate for example in docstring --- uxarray/core/dataarray.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index 76b5bcccb..02c769991 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -457,6 +457,10 @@ def weighted_mean(self, weights=None, **kwargs): A new `UxDataArray` object representing the weighted mean of the input variable. The result is attached to the same `uxgrid` attribute as the original variable. + Example + ------- + >>> print("TODO Rachel") + Raises ------ AssertionError From 944b42bb6db634fbb21f830cc550b1a868ae2520 Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:39:12 -0600 Subject: [PATCH 14/20] update docstring --- uxarray/core/dataarray.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index 02c769991..6cbae22c8 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -429,7 +429,7 @@ def integrate( return uxda - def weighted_mean(self, weights=None, **kwargs): + def weighted_mean(self, weights=None): """Compute the weighted mean of the data variable. This function calculates the weighted mean of a variable, @@ -448,9 +448,6 @@ def weighted_mean(self, weights=None, **kwargs): an unweighted mean is computed instead. User-defined weights should match the shape of the data variable's last dimension. - **kwargs : dict, optional - Additional keyword arguments passed to the function (currently not used). TODO - Returns ------- UxDataArray From 48273fb99405ceaf239800b98dda7331efdb97f4 Mon Sep 17 00:00:00 2001 From: Rachel Tam Date: Fri, 22 Nov 2024 16:08:48 -0600 Subject: [PATCH 15/20] added examples to weighted mean API, userguide and test case --- .../weightedmean/edge_face_weightedmean.png | Bin 0 -> 26476 bytes docs/user-guide/weighted_mean.ipynb | 516 ++++++++++++++++++ test/test_weighted_mean.py | 25 +- uxarray/core/dataarray.py | 8 +- 4 files changed, 543 insertions(+), 6 deletions(-) create mode 100644 docs/_static/examples/weightedmean/edge_face_weightedmean.png create mode 100644 docs/user-guide/weighted_mean.ipynb diff --git a/docs/_static/examples/weightedmean/edge_face_weightedmean.png b/docs/_static/examples/weightedmean/edge_face_weightedmean.png new file mode 100644 index 0000000000000000000000000000000000000000..868264662d9bc78a36f503808ec7fb347967cbb1 GIT binary patch literal 26476 zcmd43byQW`7e9JHBt!*4KtaNw1*BEF1nEXv1pzsP(hZ^*ARz)0igb53VgSO^R*?tZqA>i+nhQF>b2x?`hS$@#}1#Y^)jXOZ2RL-LiK#j_7sXj0`KETGi#uoEpof zGD_FXH8SU1)BU=!Gi3Ve`&k$a{KHI5*dzPrBZh=0&haOj5vk>W{)BY}kLvi7hsX7* zQ^%hOpWT2NBmXe{un3Mn2!H!9f^qx__cjs#@dp%!> zsxJM1I9?dg|4&!)|GkmbX4%elh51qPg_Coe#+6OmI=M2{Rp_hv{fRwC!4s-i?i75e ztCA>h+kCwEu(6i)7%p-&3v7LM-0U?TBt`x#%mv$~EMdF}omp;#O+aPlwmU;#NCKm@ zHhH0Idv^(k+NI^QUEk9L^>nSxr8}c?`*w#xm-+6gzbY#ya)7iKs=`O{Nko9Z~`%iHkvXy4enRe~&Rsq$z@ z067uVESfn#bzD|{O}-=$e5O50A?Q+6JQe9|R)%wYKa4mn{~9XihCFn46OXeRk<>dv zUB%!_(yz5@_z%|G;~S0ZsSJ9u9^pmcp)2EMK80hP!tO~Z{9rvZR9-44KO>Mb`kVeM z7yVb497=oo$43WS_WscA2=Ol$VmjHZ=Z`q(=njq z8L7>gAo3F1s4Gp=s6K?nsK3CxU@qiwH7>2^ko%EqAvTRJtSrXr4+5c3c*F(Yd*gzV zlyWs}EmPiV9i64T+d=new3R)hV%L=Kww{Ddw7SqN+)wT9a(0u-saeKoRT5tc-h!#* zioVjNiqd{Czl8id1DMX8DQfJxxw{*4#&>OJ4ebV=ORD4=$+hP^k+B}M?JhRPFg$}t zMN?1ZFoF@0_EyJ*Su32!Tq|QH#pt8Ad;{0hwx11QoHbp3(~tSBCuOw_l*2yhyYr6u#U zx3{+$KZQ9w5Lsp9q3H`>gtldQ=Ymos z>#nIF#Z%i@gQQ>0D>c-X{@MK&rM=(ALbNTin`?-lI$bGVW)?15YvNn@rSt9*WvoV~ zp3%s+7n_c)hsm-V$(p&Usj4Q85f~`pL&jI6J>isdaGOFB|CIH;G3uk~$X?c62T3Z& zxSge=L(BgjT{QmQ)K~Y#uJ`6c{bi2Ug$26sjO8MD%m=h?>NW@+ZW|OD$)>8O%jOz2 z4rt+Z79^2Mk)4GkHbZUJel8HWpr zU)@S}4wl@Px=?ZW@W^PoGIY@u2b(H$}eJ+ zA=|p=OJ1D+K$>gj_63sD{OQ_-cFvd9VAVr2D|YMB!6{vQEo%Bg_rfo~StsiIRx{}-<+)00nj9CV)G2{4ybI0`Q~G|0!>Jkb!4mF2G_Dua?o?-# zE}G)Bfq@X7I8%JO0NbLgNH|u~zhEwUq5sOL z4_n`hy-_94siHAIYjia>M{B+`Y|*!wgjx^L0Z-ND1&;RD?8+6s3fjF+zwUSYd&a5S zD_~thX!-EP43&uPsj664UpDT!;hWSu&)uzK@R7YFDwVdWBUzFhs%X@}|$8sNLhxCJhjP zHAX2_nm6)4n~DEa!ZN`a7UT0&qJw`cA;N-Vu*B9{`|(q|mv5-udgzRpVg!dYbZvvp z4?aqDXXrL2DaAGj?#+)CAO-=SC5t(+@(K7MqsfLa3!m(%Sm|1aBO$HV=O`vOu>x3; zr>R8Too!GPo*3y=cV0Eor@VmiQLH+rDYka0oq3_X3k*B+9M)p_)=*n7QBY5-EKZ8R zU2rSJdM&{WZC;w(QU|*moZx@boU40k$n+ed&*^ncY1IILVi5Og!PI8)3`)Wb(B}Tm z`~2*C44Uc^+c~qZb9byKdu%t_)tNs#s90g5JUY^~^6MHRc$;r(<<+{Zw@MoQ@+L8A z4Cl7Hi&m2Gw&idXF0b zU#Tix8zx_v^xZ-EA*Z=?quPM07BW_^&r^o4q64nfeflnePZjmeB#SRU!*M>lreSrW zeyr(xmgAaHsd$o?V%f}XFn6mR#&Xxw)r-m9Cw$l{Zc*@BW)V@A- zB~ZQM0vmgBv2>{*U-*JQEJ9r_15Kr31xmOx`D~}{Mx^uNeTP$#H+>()tSn=nzapiH z55Cju-RkL9sb#FsIjJA%6?nkr=ej;mjgjynO>o{?FTaVXgy@pE$PU!93Jv3 zk1a|wn=@5wW2_<$^PPK5VUaZ~mrgPwd~+0%i$g5%NbDt-T zm5GVtU~h}J+kn+d8AK&7wQ#j{s&}y7k<5JcO`Q0V?b;T|}gV zMqapcozwNbn7{y?p7Es?63jF2=dpkFGcQvww!50os7zw&=S4^1EG2jfyMWHPEW*_7 zlpI5wIOJ=j3*L9$!NjGiBa8DU<&V6kf={EuppT*v0A&7zoX0FQYwqVe`bZO;YH-FG z{ee_-UFn?0Uu;9dgxo9oiN2O7r#;C2e#K?E$^suXtW*)j6gU_3CJnB9pvD84-&D(qe2o(f zH^FZWXxUw{ZeS0Vf8KE0SooG=MO2DO)fiZbe>ak(t{$uCb!*TVEc--P z-CD8BOn+e}z>D|VVyv*+Ub)2yIB(jZCm6MN5hx$do!dX1QdHD45B|sK$Ln(zGLEzN zqN({%Lde!}`rH(G*5K4W1`D&OBs|Oj<_;_jVbjeX1A;AvijBGMQpw&=^;_5hj)I@q z-qqbRq&lWlXih_uz5l3og-h}O{bu5m>@dhnX#~O4~5!oJNh!f2JL$O z@=rs#9PgSgdr*VVQL|}Xm4cYG@w*p3tO8Z~FeUSAH#z}&V zrMoX5y%6;6o#>3&S;MaluP$+$cHBaDrLluM1K+aky?R;=poV`y^63D^AU0&>J`~TU zqz-8Pa@tzdoe|#Zj5nAQAZjhPT04z(>i@JH2V9p-;g}GIq|-+_#DuXMy#C_I^~Gk* zwoB7EY-eq%lw~DZRyX_c=`jNohUkuGO4Mn8$&+Ok8f@8L`+8)9CTTyX4B?l#C!em( zO-jzz#!u5%igz$Q!Fj2P;8Qi9jPCNg>N`GmHI(+D;EGG~Qlxs)h-RUq0rUIsVao&}>_!;SEJgVPB@ib!LIg9`g~GaT`;jl~-p(lSC{(dvlkUQt z;Dn0>zKLL?DUw=@x{G-3a2nK%a25*H8!KG`>=x}H5ua`ckrg168}%|Qej%@ zb3fX0j{pv!@uqeGOmCt^f?1erc@^wy*!sLyXS{5BLd96kSuB+lZV__fJH5U6DR6Lv zuWII-27ggnM$oBtAN<1TX5* zkJr7_b_s%a52Dm+&tB&EX^uh2rFM=K@iNv($ga&QAHkDeM`9-(0Dfyw{-;u=o?RxL|fhgo5+eCL42~6c|yZNkQb)=s5MoIGoqgLaWe1?(&)O zcQ$nPbuliCIyW3J5yWJISk|nvw01Z0V5b59@ieg*JX+o@|<@- zJz^17VM4?@{I)y59QbjXhqwr_xqq+hwGIss=NO`}V^AZ!quk>_NTG0XJ&{~M<%P_v zpCFVKMiJhGuiL&3=Zfz^*Zh+I{dyZAxgFmBHhJlS-{qJ5?ywLME z&i}^w(jRW^A^G1xu=Np=Ug4xCL4?q!qWNzYE(l+5b2q70T=lcJ;dQEc;?yV{Q-z>05VA!=p|Bdr%Eiz})laYuZU2~jtU5kU< z?B&VLT!1D)alHC(4QlC;=2J+sm+5Rrasr$oMmEceodiD7Lwqk$BZxqd_;3=q4dw$O zllBDP;)Bip#uT;Gfymk$@B}E3sTmNqWA@&OS>IFP12Ve524utc)V%wp#c@+J$Jz=t4LCCh5h5{LrutZQ+ z*zM;r2y)t)N&Gs{wF*$|X-4rR`P%MWW+{slJq&>v$P}s_AZZ&<5%52Z*uS=SeC-o} zCXeW3Lr1!p5^pqL_ve2+#urBb{ayyHO2G`+?e~{Cs|SFBw7}1K2P@*SJ=y%#Qaj^@ zmR7#$gx-frh>lrufmZ?A?6UFaMuTCxkX}3;<6|bFsVV)_X1b;7v-{!Bq!*bU10|c}RP@+P z(MLMD6fP0f5F-9JR8C3e`LSDq0FfJkf}De{e)Ln_)FPyoTjJKL)ja>A9>(M4)yd zq#g(feDOX}F2cw-R+%G-1Bvjj-+P~>J9s_lR>}6L5A&Biv6@cd-%ix!`W$vcj>Zu| z%2u-gs=3+9*8_Jlk9OI%%<#U{0B{)$X9svw5KoZe=WxyoPUxzhU>E9Fdz8qjwdEfK z5v;l;MJ%;9kYgBVJ^GG5MBVR~_FcPqldg2_sZE|^+@~OPkf+UQclWtkNAl?g*gj^; zlFt=>LjFaOT>o8l9&(gq3c3F!&MVY?s;gSrudCuE8oVXC8J=~GLtg&NU0XAW5y0f; zUeSJo`Dl&3&wiPt9pq>4=ptb(ClKv>4@m5hn6MY<@cFySl4Y+&#XNBw*{IPTb#SCV z%26f%wrmXFPvi7OHhDv`ZfSwzbo3iDS{)?0p?|cifpGj3V@dUaME9i6NHDaj2O(vhRZRo_|9R%u3E@5?)6qK#($9b*_& zUB6R#v~|=M_kp20^=6Zd*XX@+{Tq+sD$8|C?Katu2lw*}4W<`))PvdB^btw2Tef6k zlNZ2Z2WWcFbolC#Xh?t4aron9S2~)QS$Wte`&CdNK_XuZZP}OiTd=EACgf(ee>`Mg zH=T4s{F3XqX_X+{H6a#?>FWLPK>p&?j7tn95ZeikYm*j@q4vYBya}FJOzRcWHyC+H z4i$0k5X(_ZMQanns%yGfv1h?&Gzt`> z!TH-GsPVfayMcVOy0ApD#z;q$gN-6uZma8PfJ=>Z6Oi}if za#y)=UiI`y(etsobKl;N6J|ySd>9eYYnLc$G4%+zjBI07xfo5w&PJUTEiPM)wjxhS zO531fpqI^`d?DhNe#NkOcWc0UFfzCw0!bNzTth&1)dJFyOt`5WAu)%Wy}8B_xy^#5 zdB3(cy7et zs<+|(QYl?5N!9j=&)7>=ja-1_)S{9RWAPfKV#4uRi@w>lYG}9}n1cb4VI2Bt_Y;l6 zqry7NEnLfkZ2cUWF_-Z-4=fn@utRz)}Js`c>$_l20gRFW20N5NdM z<^2GEG zlmcZipgt>6ouee0Xg$?r*QG=`_V&8p+hmkkXG-u+``1ImkBohioN=aBXO)u)SKD#u^vLX)YMZ~K%y&{i3Q=$}m z@7GU{mFXTQf5S19?Un}GT$Or-oRAuiYCPX19YfRzfh_^f2CY;N&RxvF=H>4NA`n1FM^ zE{0l>4FKPTnMz4&SFXu{sphXsk@uUxSc?`8yE&U=mT!9BkvDugT;Xa0jCyoTOiY_O zTg7&avn?Lw*at}}JTJCs_Dq&ipyip?VF#6Hm~TO<(<_|oDbW`92MOqUx;$kJQlWUd zZaWk4HJy8ItH0?NHC68ozkEhWxK^y1XCkzq|8_Om1p`H`n1T8Gx}MCr37J39r;$zC zg}Pb}q2NF8IN-s#CyoOK<9Hhj0ssmIv>Yk~4Jb#SljdCjy#b&8Hj^REZ~itTS8JR!8)75P zfBT+9t|t52ObX2}PLMqf5K1UquKzGGMlYaT;fIJ3JU;wjgXi;$|;g9emWS&p4{tZS? zok3Ro{_#)*`$4608Avooa3X^t-^I$XFY>^W5*@FZpo-O06WH+tu zM!8XtW0Y1TJ`UCPN$)S}xF2lT9Y8$84Os*(;LUR9ygVGvdBCG!5|%p-a+kGx$NjiW zTE1ma+x0xMkb#i+6d~;1w=ctKkU5SNdI$0W1pYJw`iNwu*w_8xm2bOI;t*#9i5%Zz zJoaDBP+!E5Dp|RFrh-K8R%#u)OWva&*a&86t3(3~T)b>Z{F`!=d;0}irgO&03S<(% znfLWn#v?2xFaVqgTJ0@!%oYB7-3iB6rs2bDK+8YP}4{ z^1r$t_TB`F5Q(}bvumhwS#mb|T!hQ8vHB|yH`uH}N)#dJx*H1Mt=Al3nH~Rlk|EpF;p@YDTPuXJ&KyFNu1z)b0^P^tI|G}IpCU0hA2L?fz;E_u*ov6_mV$K~ zhEZC65>Jo~d%Pr|0?9S8j<51$pFsOm!m@msTLcSVSg=KI`V}5=V8y0vI2YsMIl?n9 zwHyv$tu}XqO_XG^5OQCG^t|p7h%wGNHA0f|4a<^pYA!*xOrRjdyZTp~+pZwN zrCZd3Sx3olc^%Ig(SSzeNNY9gP_icgL(W3OL z8;D(a5-7M#0yN8=@(&`LK+eQvIW(}y+^3&@Dc_P1f$-yN`4fN)j%5HKKC+gDb1k704Ya1pY zxZ1pOEvRS_qO@LsFXkgrsL_(S4C`jh&e#m;5{ndxIp)l=$A!sW^#~se@w;JZGV@jK z%jam44W|2ImdY~{TWcJ)|GY83rE~c}eGwFA&as&pogeBhQA!$Inzna>h&~}6-3cll zv&@L)mqxs5f%M8EiK_}M8F8Qzm;|NMaTdH6A`H2`&4q=nKkqr4vYP9W*p7I(0w}tx zzqio!Ak{J-r}v=dubZ!(LQXSbjQyh!vLs#z*p#w!m)hy@=e0i)_|wLs+7uDC7K zDiBdZ!mR$P1b*mLHchKT^9IC-SI!(sezItHp1Ts`y0=gm4cq@$8BfmirLh`+f&>;rbrMxPI_nCi zebK}XpG-CyfSC0m2}_^C-l<+z{UWM(I{_!y4Px+sUN8a5H~bVCHTSVDL8c4-#{oA_M9IObR$>FA=V}7l5eg4JoV6^yb@*Dje!p zexSo_bG0n`gS{uP3KU7obx8Jfhje7JtXw1`>vwLQzP$lY4c8ThY~ep{$X}%*MI9&z zsmX*ODoN_5xy(w7k60goOx@oCd|XpL)8;!25O+Y2)@Ci9T5sJAG5*e7`dIi$DKzvB zXYQE1P2ga%av2=o-y1%%Yo-|vJ`kWSW$;5JdIL(*;_m*}_L4@$x_>uD@V+r>`h3~6 zB}$}05SD<`sNrq#P>Sb7df`w`P0NxFyIPC^C|wwgsDJ6*)u4TB^B#|w?iIKsm0d(q zWj>Z~2ssTFF01j{(5)L)V_zrgKfM74PHoqnbcv55n&ZL!FSQlDZyQs+9nN(I`(P>L z$XwOeu4~KQD7@#FDDgx~?QvdQiHX4Sw^IdA4dRL&C1fC8srOZinYr=9%U7F|&SkWK zz-(KnhsXJhyOiB~kGiCtxO)M9ik!*IZw&n}91;{XdmX)8C}Jc*D9gtsa$Z6=`h%qn zh^8-*2PxMOerYFG;QwHwa)3+8b_J`aW;dc^Uhn0`wFm2iMe{lJpwZhX)F5NEwSJq> ztjXE>$}(_()PR&t&#Y3TA|{N@^ZlIC2IPkbHviG)QNBDYN!xcfMd%e>|SNEaK zmjR&bUxY*ZT_0(V+GigUjvLK{8p&ZID?Up3D&*m`~39 z2b9FmK=}39bDBLdsAtf!@&J~2ut8G4Io~~HjF#q-{GEx5Yg?5^gMM$4o?l>Q>JMts zFE9(!<}BD(>zyMW<-QmrT2U}<=})zM0Q1UWPodk{nN7_c=o&ZVs#0N~4#eD)Vo6Pk zuK~W&mRiwduc@oX;+hEzxOrkNhK^+|`={@nrrodaxwF|3?q%Zs)_xRZ&ISFKbixJEvH^ zTj3F-E4D}R_qIWx$PI3`lF~#?6?>Mhj{YZ_LDkE}TuC-%JXgzFve_X;x)^l#{oCxC zm*ti(*1d^K*np&Sc2%XR4L8$f#oppvq=cdp;7yNxC5G-;hmMqGm?P_#bBa%bES|ZP zCW~w$A!mVhl!XhkHdk;(1useKlJL9VIW|8im+VRXjYyPaN^+R^UXQZc^2rA4z4^Wj zw0gg^M(}S_+T^7`qd{*M9LU9*8?&#U2Id?CY|e~jr(?=zCcd#`BU`;ZlMmW5H;-NW zuZwBJ*3AM&f4+O!>Av&$s36!YcrR;)PeP6mL5s;s^|9$r2;4G zaS16bOI1q^k;}VxBLda6gt@Z>R2=7AccwkaPJ<@^ zJe!q{?gi4^&^vWzsxM}(JJjF1nQP@WIRS&+`vfXcwcu7P0u0nD9=C|_Gm^dyso#|6 zqSCyvZ22hIAHSkh)#Bw5ww>XF#E2wti1*$=DXJVY< zBUwq`>2@z^LXRo`bFC%$ZT;b7(9AatfX%RL9RqXPf98%pb|}5xDd~%K`4eD>{4b|I zpYO>o;C=g8n%2q2qK}j%IINg6eB>%hSP*JEa5F?pr~nbz^IT3}BR-em0%_e_+N5>K zCTEkpLu&1%b%~pAkDc9%AoL}UUka_*J0njL2V`|8k4UCenL$#FxLmi7kIw@8#brGR zf;M*?RHI|)7RiqkJ9PC_!r36>LnY=D_yrQ?1?e7P+~|;l@)>*Y?@>jv!<@nIKtDK< z#Rlm=e*{^M2}c6Bw@8n(T0xkrBjZ`T4nWpa=CNykbu>d)x|0rM3}D%%zBA-CqC(mLSg`q+oXkNa>L2;i0;EROs1XX_@sFQAi%28PZH6n2pzJp*Krn|*B-#HORITv?FBj|Ol$U?9Qf zEdv&7PA~ZLdjuAs2!W%d^E_>+6+q1I^-KV?CQ8LO)MC7_6#hs}E!~IY2U*E_o6Oi^ zszhZlI-@wjrhMK~1fr&X#X^h0$9iO%>$+XGOuGW3lwy_jhC~s&Ea@O&D^Hn~S~m2n zn?cBEofsXTx-L)($I}WnbB}Tx%Cr#iD>HP5xF78e2h%wsK2NApmAL;hwV@_waMH}9 zZJ`!(Mz;N{y^|UwN^8=#?s)D}pw0&8URsOjkRedZ4S#jYxnG?51N;$8^%KxXQx~2V zNo0K8sk1y_-JJh*2>~PDg?^TqCdhH>M_KK-2`D>mLXC^+ZTU+97PB#F&oONd@P=>n z(5>_(4A*cr^BdH-ra(3Ysy!Mn#!Rk)x+n7~82-V8;SUL$8uq^-^cVP(uT@!(r21Ju z{;zsC{ECR&K6h=A-#MYDMtu|(MO7hH&XUoK*|0yIq_?q!{wJny@oQ%!B zy6bS6ONo?a|Ekw__q;*2eQMaRQU&i+wqpoO#sg)DsKgqFe^4tkAU~j%ojG*6au@*H z`FEs3NsA|aVG0 zDLXHK4=TIPC4XL1hyrVD+dPR=*BNlI&z%rvQ0TL3oMY)IW337r4TzH4`-%YKpnhW8mMLe9nDufp@FH?hio|>jeeJ(&LOqqglV zLaG@)?Lkn`dl{db*$*(9$2{XU+9!v!XXUbjvIAO&p^jgfn3jvGPH~gUDss(=U;`)b5jY0M6gDRcmd>mP> zHf0Hm8+~p)kKXiZ;<_U83Z|c{@hzDwFHEXnJ4J*R4@1E86o*6Es1j6+cC0%eu1l5} z`@-qkzT>9g(DHO9H`>(`BB)R8=R=>fRT+>b`vfbeT8L@Nuhu2qSlJX9s@3;U4F zSHytY#hVNP(@<@&;1vC^${1LU#b*MTZIbU=k!m@BQiDx4y~Wm3p!#eO4f z&vBkWq@vgT=wR@=fe_j@yE~2h$r!h*amRvM|23}C5PyW=Wq<`n|0c!-PE7Zoa}f9? zTRbsS!vCLx(VOtu%P*lKx&@xE^wE%1ZmDjLyVPMI&-V43$9>E_i_)h$m zl>Y)Gw|PJ5+J%`DWlo0vSu6CR5`tiB5=?dWTIZ<_ERf(B5_MlkNy$Bbo=q8bi113E z^qi8@b^Ut)Aj@Xzl@IjUg=38Y+g7j=p7i+(r?KCHG8?3?WKtxa@Lb5msErqgw{XyNq*8qS;hqd@RIq&s0FPUau(~erfO+17V#?2wqcWGUp zX|GX_LyG-)?o-`gWuTh_M$%O<+^9sUfd}1nbFGd!GZgGxScU6ep8mzV?WB@{Y__lt&YyIV|HaS!jeb8X+3N10tgP$Kyc#Y+j?=# z1rDy=9+Fz7Z(9cJiJ}HA!$T$Fi^my-ZE?8 zw%33a-hPWkfcYXkY84^wg$tezh_VDjSZOc4^%Qb~PrK5>T(6{qU$qNqCuPwpVj-b- zbOB!bo5l?XLfj6lU&qvTZ$ejx=|-A_|JV+&VoWX>H`Q`a)Z|C<>y$s zw@7MXTkbxEuSQZ=ht~RSy=#)emaUwDtVmT0xL1%u$NJr^yGTM(oKNIE9JH-jVw2oI ziUT}3$YfrAj`{Z%mLNei0y&38qiXp09biV_s3Iz#(x0i$E>AiR1b1!8udhJ4#Vw@P zvOiE8a?nV0Gq|asXTj=~a2rrLO$jZtCPA7ki9;u+&boBL4Dw6vhHNCir2uU!%vX@2zc7wsNmf{Tj#I%z3`~hfsz~zxJte|Cjo}Z$#;@ZJt_s0I`f|;cq}| zfZ@;}E{C0q3#q!=%ch>Vq~HooD{+!p{}pMea475IzX6gvao2yJ8gks#18#c@Mu6Td z6i_G*h3+WB(Lzf5fmTG)Jm9j{XjhKZRrw^>_QRqLTsJt+zbvc*Ja}PWL3{H3CVZs<{+ErSAa6T1J77i0Q+@(1gr+ zUy{RX1fW^nuQ`fB?TDnulhp5jJz8^@Me5>;ULU7w?MfY1B1X#)CR)8=0}L5b&~upD zjdsuPARWZ@2dWW~wPY}ds7!o${LAdBp4eTa{Q0jyEI{*yLxA=18Sy}BE@VS*Ri&%R z-;adi&KrNeje(TIi;0r!^&bf`UDdvP@%t3CW~pqOruREfv!Bk);aoK3fX6v!fG z`C!Z6eLr>w0*-}j)pz!NPZPub{CLxU0Mzrtv1qAPdJ2er^8C&mLZwh3Fy)wCbC~ zV8Pj8bI=t^GUUP1D;6oT2*`u%Sq$Q1yJn*|ht)_bdh4H5Gy|3EdTVGU@SHn~mxB%} z@o;`cKdBG#LMoXm_g7`6fs;H7$4W208RD-S>rhf%q+u+t2o*iJ-ZVj}B^A?miSHzW z7L6tENj>S7S#7l*5Et)?S)aZmtCW~_N|URcX z`2k3f42>Bw8xezN6vL+)7vC6XqBXfpn4O6q2p{0A{(aCX6bd> zoPXMU4&^c1Q}o;*zWa8o?J`Jsv;4Vh-9am5)}8uR*RJ=M4v2g2`zAA#Sj?3mprdW< z-YZMDMkIlxtuq(?>D*6r@dwM5hGe-nUBl$*QnK^dVnDTd?~$|IT?F|e(b|E$A&}QV zxjWSs^eLt0+AZ{XRe+deYu*l+cNW*L0Mfj`4RUVDP9B$l1lL>HLTzC*MCM*lvZDRR zmdWMi6$CO%Hv_ZXl+AJxD#b6hOMI;iap-OVlR|f~0*`f2l6^QzdUYCj3knq}}FK9wO6UL=3e9Q7)&$3a=QkWyTkW{}_UJh#*fejj$% z)>QdEmF*VPf$Ck&6kP{q)5_Thr%KeS?E+1Zz`ZB+2oU^8e}TlchDi$>nD6$}iWrih z@flsDD4*VT`f5XW$(9PDCT3TEe73R{Y?-ioQ6Q!aPPJ0|O{sQcTEON@3d8T%IixN| z*GaHs(RJk)*<$bNx=tB7HbVuGH1?;y%J2bFh5#Y)hAtUXfG~ovQtjs5s@QSB8Zz~? zu3+^)PRDrVwLQ$7rHZF3A>n?fy7jSEbutHFS{V3%_H^?^qrkyZdp2Hfr?SZkNc3<) z4Qa}qxwKr698Ly=-`fS7&BW_qP1o8`ILnpq8s}(UM(c8c1IT}CX9^^f`txj5`pnPH z4vpdLX91?hQ;OYXxZJ1wBR?>?yx>KoCNl-gr@QU8+R^!C8(rF)p%+Z6DV|DaXkOr& zn1FhV1t&xcsPdTGmH-EHvj_Ba`OP!i;R088o%MB*f{ie6X0B;VB^7(l^Vx57aZphl zB~-eO6Ty>!Hwx!U>B~l z#jrG7e6xFO`JB^0$T^VIyU*X5+uef7;~tA@l7$H7pXg4p{Sb1kWts?+s+W0DbZ^6> zbXum;o3&n=qn|Azd&z=8KdBMo2J`P2VVYSFUkuWb%UVLvvzdLUn`8LS^(Oe<+^;g4 zcPGJY&U*v`jee6-Rr%DE1i{(l@M~WbxLlhd7=RTrm44I06kO^Z(2Nag{eI4MDW&b_x;pr+9&= z!OJEbiWd(Wn6>mA{+y<`c@N}+&1T<{m#X0qoA2?@iDr%nbMulDGhK#^zjH%S=DUAY zkC;Q=)d?x@|3~)Nyp6~n;gua1KQf#~(L_QK2(YA0I*=i>8wi}f(G1uaeGf|A5kYrB zP)pDxoS__&XWq|)wm%3R%qUD`^aAn=x%1P~Lr7{lsty5vwcPphZNhzL&WOvos*NEP zbfMtne`0^mH|@-6erIZEH)yL-NGhXyM&+p+h2_I#S;dFzkQVEEs9kyI9ClKrT&1t7 zX&NZ%89T@QMp<g()IESBOM3i zMK0ml!<7bpcdDCG=%oF`{TJe1NsWKcc!=(OXlfsIe4g1)z1j4$Be}2f5eRb#)Pv-zPqt<}~qzaAg&;ogByo z3q(9q%}QZ^cI`!mU^h6YHbGL`FUo`g@*ol^S@4b@_ro-mo9j)Gqp);?ms7a^^69pb zyH8hq9VyE#9CAVrh&Ljyjwo%}pVL3$07IKz?&bzkK06m;+rq2GFYCsg_p!%SxAA@X zKmMLsRu0qRbg3$AC%(5tah`NkeyR-AoGqYuCS_t`^6sq!4;KyGAv0P{FVpF$Z})KJLj&A7;2VzAQjF@{FCU#u9AH7Js2b6aVz3c|Zpy&5t^g zl%jw6koAwzp^;K;2tKOI>|Ro6jqN6%^sP(!Aj6@4GDBcS+?-Ucw2svDI+Q6=i6#w)BysL11x zZpP>=(&^;}S=4BFl?U_U;ge6G<3YIGMLo)#;Z`t`KL-ovW40V%O$V8o6W2<>9*j{X z=71myg{6>Cr=}|t32z3H{>YzPjlP>;5tnOgt)=!89VP*m6JdEvOs+mPoqbU7E_L@| zCB&W<>EbDkF0oH_F8DUaK6mVJv4YUju6dlQ(d8hvx2I!Y$2E5hbdu!eJUD((`NYBxzqAY#9hnHJItQfe=KnBNjPg`HB# zi$bZzqd%8?L(6_%Zp`b~YCDkNBw>6lL#u~B%-h4d6;3FY*}dK+cuj9jzoOdGgG_pr zzm~oOEB>pgT@s#j_PzD<0p)PYGbCDFpasPkn)>yP#Anl^THp9k)!w#_)EqT z7nd_x5kA4>l5L|Mh@MQH3=6+1b-iZ`qB)n1P7MnJp_T@C9D}8eh~1)QT=+o{a08B@%M~ z?db^SEdTe`o*?|RdW~#dq^uA{cm;k$pUObvVR0GE!frw}>M#;K zAq~Co*_~|3QqmsAv4$)e5hE&PNre_mqL3xaq@zNX zvL#gfK9|<#oZs)i-{X&7dYOCXy6^kCujTXoeBL8hY6tQheEnQhrAI=X9KR(7Fu#Ei z8b06x#KA|GQ~BGFp(t#&B!Epwhc4QskpK5Q@)~Dy-GV9-i~aLW>4A9wqWn1e(hP0i z_dQY9a&ByxezDOwQ`iLo(f4&+N!Uw|U{q;R0&9Y~_Hqgs7mVSEEeLuD&3!kFW zfCov-}&mDq=a@n6k&yF75u8H2hBbDjAEssw_yBNsoj= zG73Pg%AYAKd6#2KYS-;`hACTk#M{HoL2J_>op8kklt zmLr%-B~Spbgmr@t1gat6QL*1-M~|51yCjhPdKEDTM32=+?SnAVi5b!hBmxGq{$ZAv zRID4Yfe9}h$C^?etL5xbzyO3dL^EP5pq1J_nX5K1wyUVS4FPS=6aOy&m8Q#Pbm+WY zc@%<7AY_!p?_?DJwQKyC@vH#lpKpF=G(RB|9EK^vVYu;P5V`EHw3;Y86$SC@ z=cLR!8gpL8&>0yXXX$0@IRcOVov*N|XDH52<@)63kHBa|bgOzk1_EUg=oU}VFDkIl zZA%%8%%GJATzXjNE8epstUG?j7~X|aw;vikxDeO@$%Cl_bv6ZwdJ~D-n;aF=U&#{R zISIQ+NGr6Xx9#Tj3^Rp~d-I+fyt9hc?BW&6N7F@@d#k8_gUk3pXWWYSb#A$FAT&p| z)|ZrQ817~F?sarA+wE-pWOnmMg;c1ZFN=K##{6Hzk<(SnrZF6*rl#__)ACoU+2ktn z+!Xv18lr<2h?$@_2h@N1H2j2|8&qes;oLhOvEiRSmpri3bQG+yFmL{J9MD*ca2zrG z{PQ?08q~OYny$QCRGRNM?)IN-ly5DiyqX4v#F6Fj*1}+VCfA3~2j$<2e=8rKF@{&B?_Wae#dRAAuHC z!EwwsAMI9s|2*m&Af!PZ?fH)QX@U&w^bgv|W%pSxWdGhL4r31hKFdcGzN;(3ma)w{ z-`p8V#QQ#`d{P6NgEd$GqrW#1nTdZ4l_Mr|>2>Re{Q1$MC{JumGkK(=G>A5hNwmF<4NfOX;#mY;fx`o1xndf;10W;q1{vu;Swh;%@`NO3 zk!cF&b~Oy}ZRQhi5L46i$R>;l(j?~3w2)%5mua5lkNVCU$@l_fDjeVNV^A?n%Qn3h zI4BoD0UFAvCa`?j_iL5pVmAq$sxbHLo0%CH^OWu;$dG}h8~7y#`N*$;lKtNRB^AM% zFXOdtk-}`rQ4Y3wL;Cq%DDJ(DtIEA?2~A3D9tHvKZ-YRr91P~?<9D8PiCH2vF{2HRBQ4ghMIJjhC{yl&c4UZV8GLV6J4%;S%;0QuxBrE~HOBy4ypL@V8>+#8 ztA^I&N>p`RSsSn@_atdIzIqrzgD_5LSm;f4goXv%s|X80m2lVX(lA~m556oXuBuMP zpI;i|T^^j@9q-|??Kx(%&DdO>{x}tDSKB)>VHoLO%o4dTL;#rG&=`1f0jrsm_U+F# zNyq*%8ItR&=^BQRz7{B5u?MjKER$|`yVh+yT!k8x3&a{a=F0u~0RQQb zIQyC-lfy~<2gj!KCt(XvRq4|c8FiYV1lERH=G$#=5?L(=0p)eCqG}gjxQz-5Vaj%vFFvxC0Porq z#I5qMHWST1$s#qHeEZ=J+Vjw4h7 z)gX@xe(I>{{c@ZtZ4J2bXxbR20p;mxRN4%e6@R1rE{HcRuh~<&<3YMObwqB_;jz8v z?J|PHmE$xllzSk%o*PGJ>Gu-dkKUtnzyqO+mSNh7nO)F~l1M<+`H^U@H}In(;8ylF z2CoNfl)cOly@q1_n2e$*f778k(Ql<1uSm{r>LH7c3_3@Vp7;x7VQuId*8c`H=J*l6 z*%tB$mFiD>< zIJEB2khO(UB4}gDoB34N-hjijWk^40k>mNqr<+>N$*&!*EOX9~GKzi1oWAOtCa}2D zfO2YGHKpUZZ_mY(L#clNds9H@7+#~XM4=MAkRj!h#YmlY6qjkm)pwY5;Zn_Kp0Dpv&~z# z3nlbo$mx><{bLRbfYdj`-~i;)Y7Cm7F;feny$4jK1R0k!WSTtT)n&cqU2T$mgClyhzqN_5*YbKRv1zft zrD<17cYf;LvXyY1$T{8JO`==<;o=0*(Z8Sp$?5AD&f^jeK5zDT+t%C$=YUJ-P<_7x z7FT)N(cQxLPw^?^sl2UP=5njw_SRuGYV5Gi-g|wrzcKY41I+<-d zs?t-bW<3t`WCsO8Z7=ILWfS9kAQ#OycGaD1)<}MjO2e;IgS6ck4&KhEC;E??ZDcD! zW6=?mwZa^xWPE1pwXnj<;%VR2p&fB{qYuy8rqoS?x`|W=`fWxaHOp}p{Ow~bC^@Hgi_FpGTF}wZ=mbpiUYf4v&&K)Gq%x;* zbZ-{|@&?6n-7reFH}~hY;xS;SgqKJZ8u4R+T4-3Wf3uG%(;H?iHy5EpbLzgd?GY02 zG_NP*@jR_+0KI9b(zjh7B&KHD%E*;2S=N2`A>cn+wE0?GqQRry?rU6+e7>Y+R*o~X z4R?I%7A7q9uc?{60!kS*gIgFP_KSO6^_8z;G12NtX@2+5&^e^w=IY?6p)t~`xJ z_JGk68bdG7v2mV>rCv+IA~llj{zZe{OMpViYmk^GSs22-B+~(mHH&uk0dUX+8Dp_g zFqB`#!Au}I+xm@@`g8@9dqBmj&$PAN@{HscTYSiZrI7yw#8tWWx^b6=gc<*inz4{O zQmHMRf&qPq;aZ#J(EOJLg|A`8B@%~+guNz*kmH2CxJu-h?l0C*=<|FHv|x8WCYWgv zX{%8u7fHQ2InmocJSaI^_ruDDr4LcvbyjE-*N%>SoPJTFl9-u*iVr2Wcp7CTW!uS_ zp_|^<8?eo{|BJL^|IV(bhqp8>H`4UK9rcR&D<6eY!6{Lm z9uvM5sFKhSajn7U@Pjxl=BVHSmnlGU-56YU_K5E3=clSzSw@YdpPsp`2*e^P4B>6j zrg#tvY2tu8t92k;l#_LYzgF#V&$&COmlQ~!QFMA%)40Yc`jy@sPdEYvw&9+?d;Rj2 zFnlXA&h|1`23(6`gc+-|27+8XMcF=*6P}bh$c%H=IGoIsvqN%VB{9#n-Y{v6ysDQu zpXY@M7PWvqJ8a0zHicu`)jq;f+-q%hTG_kxSn@eL8`=m;aW7P=!j~>|7JP#}_Z-Q3 zOULl%eQ*BK4q{U4rwaUJ#x7AiQXELYl}Un?uY@T?_N*Ye+Lq?o9abt4W*xcvnh_K4 z6IRBgl6S1jKJ6IlSIv#`K06g9!&8!SOAOtYM?xWbY#a=nQ<}3D1^x zN6;w)Py;WN1fzJ7_XFju^q!5Bx7E^m4ar?eu6Vg$*(>3B1_uv`2QQ8K!$ydwL_~G& z7LVR!>oRjrqLHf|)%%czaiKXapKzQA?StU%{C5gjR>xhCb7wk6)LX53UJ`)w2lZT{ zobp159ECHaQJpI>hV%=sXL;(MTTOep9w6Nhm*ubn%Dn>VR&9v%TMAK)1!G{6Xd{`G zsGYCp*J9gBzMRCpWX7xl^2H#P^{_*`k;szqm~&tsU+nIS0!004eUHphw3;r~d(`q8UT zG?yi_aj=xw0R&XS1_em*zSaEHpuka5f+4RTn1jo?4P@M}2jBZ7dt^MQe`gXmjUi`K zDOje%$tC#huV^UQo#Ff_lu)_#GXt!V_$U9N%wAV$_vN?$(wD)gDRfS8Jip!Lzv=|v QcJhhawRUP|YwQX9A5wF9@c;k- literal 0 HcmV?d00001 diff --git a/docs/user-guide/weighted_mean.ipynb b/docs/user-guide/weighted_mean.ipynb new file mode 100644 index 000000000..684e88fd7 --- /dev/null +++ b/docs/user-guide/weighted_mean.ipynb @@ -0,0 +1,516 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "23f93b0f-62ac-4efd-975c-368477c9ebbe", + "metadata": {}, + "source": [ + "# Calculating Weighted Mean \n", + "\n", + "UXarray supports weighting means based on geometries, such as face areas. This user-guide section will discuss how to calculate variable means them using unstructured grid geometries as weights. Below showcases two examples using weighted mean with grid face areas or edge lengths as weights. \n", + "\n", + "Currently, `weighted-mean` does not support nodes as weights. " + ] + }, + { + "cell_type": "code", + "id": "ed637e46-672c-45ba-a5f1-ed3c3248c8a1", + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-22T20:19:40.863238Z", + "start_time": "2024-11-22T20:19:20.402282Z" + } + }, + "source": [ + "import uxarray as ux\n", + "import xarray as xr\n", + "import cartopy.crs as ccrs\n", + "\n", + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")" + ], + "outputs": [ + { + "data": { + "text/html": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.5.4/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.2.min.js\", \"https://cdn.holoviz.org/panel/1.5.4/dist/panel.min.js\"];\n const js_modules = [];\n const js_exports = {};\n const css_urls = [];\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (let i = 0; i < inline_js.length; i++) {\n try {\n inline_js[i].call(root, root.Bokeh);\n } catch(e) {\n if (!reloading) {\n throw e;\n }\n }\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));", + "application/vnd.holoviews_load.v0+json": "(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.5.4/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.2.min.js\", \"https://cdn.holoviz.org/panel/1.5.4/dist/panel.min.js\"];\n const js_modules = [];\n const js_exports = {};\n const css_urls = [];\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (let i = 0; i < inline_js.length; i++) {\n try {\n inline_js[i].call(root, root.Bokeh);\n } catch(e) {\n if (!reloading) {\n throw e;\n }\n }\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_load.v0+json": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n function JupyterCommManager() {\n }\n\n JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n comm_manager.register_target(comm_id, function(comm) {\n comm.on_msg(msg_handler);\n });\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n comm.onMsg = msg_handler;\n });\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n })\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n", + "application/javascript": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n function JupyterCommManager() {\n }\n\n JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n comm_manager.register_target(comm_id, function(comm) {\n comm.on_msg(msg_handler);\n });\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n comm.onMsg = msg_handler;\n });\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n })\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "application/vnd.holoviews_exec.v0+json": "" + }, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "fd4be7ca-9a2f-4096-a552-6a5c3401c0b5" + } + }, + "output_type": "display_data" + } + ], + "execution_count": 1 + }, + { + "cell_type": "markdown", + "id": "8518335d-2f75-4699-bc3d-862ab25830d1", + "metadata": {}, + "source": [ + "## Data\n", + "\n", + "Data used in this section is a 3-random-variable dataset on a quad hexagon mesh mapped to the nodes, edges, and faces." + ] + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-22T21:28:50.609427Z", + "start_time": "2024-11-22T21:28:50.577219Z" + } + }, + "cell_type": "code", + "source": [ + "grid_path = \"../../test/meshfiles/ugrid/quad-hexagon/grid.nc\"\n", + "quad_hex_data_path_edge_centered = \"../../test/meshfiles/ugrid/quad-hexagon/random-edge-data.nc\"\n", + "quad_hex_data_path_face_centered = \"../../test/meshfiles/ugrid/quad-hexagon/random-face-data.nc\"\n", + "\n", + "uxds_face = ux.open_mfdataset(grid_path, quad_hex_data_path_face_centered)\n", + "uxds_edge = ux.open_mfdataset(grid_path, quad_hex_data_path_edge_centered)" + ], + "id": "a2f58f07d7881d4f", + "outputs": [], + "execution_count": 28 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "The figure below visualizes the dataset. ", + "id": "b1192fd99760a758" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-22T21:22:10.513514Z", + "start_time": "2024-11-22T21:22:10.444239Z" + } + }, + "cell_type": "code", + "source": [ + "(\n", + " uxds.uxgrid.plot(line_color=\"black\")\n", + " * uxds_edge\n", + " .plot(\n", + " cmap=\"inferno\", size=150, marker=\"square\", clabel=None, tools=[\"hover\"]\n", + " )\n", + " .relabel(\"Edge Data\")\n", + " * uxds_face\n", + " .plot(\n", + " cmap=\"inferno\", size=150, marker=\"triangle\", clabel=None, tools=[\"hover\"]\n", + " )\n", + " .relabel(\"Face Data\")\n", + ").opts(legend_position=\"top_right\")" + ], + "id": "6fac99462efcccdc", + "outputs": [ + { + "ename": "ValueError", + "evalue": "UxDataset.plot cannot be called directly. Use an explicit plot method, e.g uxds.plot.scatter(...)", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mValueError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[1;32mIn[10], line 4\u001B[0m\n\u001B[0;32m 1\u001B[0m (\n\u001B[0;32m 2\u001B[0m uxds\u001B[38;5;241m.\u001B[39muxgrid\u001B[38;5;241m.\u001B[39mplot(line_color\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mblack\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m 3\u001B[0m \u001B[38;5;241m*\u001B[39m \u001B[43muxds_edge\u001B[49m\n\u001B[1;32m----> 4\u001B[0m \u001B[43m \u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mplot\u001B[49m\u001B[43m(\u001B[49m\n\u001B[0;32m 5\u001B[0m \u001B[43m \u001B[49m\u001B[43mcmap\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43minferno\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43msize\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;241;43m150\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mmarker\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43msquare\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mclabel\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mNone\u001B[39;49;00m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mtools\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m[\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mhover\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m]\u001B[49m\n\u001B[0;32m 6\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n\u001B[0;32m 7\u001B[0m \u001B[38;5;241m.\u001B[39mrelabel(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mEdge Data\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m 8\u001B[0m \u001B[38;5;241m*\u001B[39m uxds_face\n\u001B[0;32m 9\u001B[0m \u001B[38;5;241m.\u001B[39mplot(\n\u001B[0;32m 10\u001B[0m cmap\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124minferno\u001B[39m\u001B[38;5;124m\"\u001B[39m, size\u001B[38;5;241m=\u001B[39m\u001B[38;5;241m150\u001B[39m, marker\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mtriangle\u001B[39m\u001B[38;5;124m\"\u001B[39m, clabel\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m, tools\u001B[38;5;241m=\u001B[39m[\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mhover\u001B[39m\u001B[38;5;124m\"\u001B[39m]\n\u001B[0;32m 11\u001B[0m )\n\u001B[0;32m 12\u001B[0m \u001B[38;5;241m.\u001B[39mrelabel(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mFace Data\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m 13\u001B[0m )\u001B[38;5;241m.\u001B[39mopts(legend_position\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mtop_right\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n", + "File \u001B[1;32m~\\PycharmProjects\\uxarray\\uxarray\\plot\\accessor.py:564\u001B[0m, in \u001B[0;36mUxDatasetPlotAccessor.__call__\u001B[1;34m(self, **kwargs)\u001B[0m\n\u001B[0;32m 563\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21m__call__\u001B[39m(\u001B[38;5;28mself\u001B[39m, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m Any:\n\u001B[1;32m--> 564\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mValueError\u001B[39;00m(\n\u001B[0;32m 565\u001B[0m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mUxDataset.plot cannot be called directly. Use an explicit plot method, \u001B[39m\u001B[38;5;124m\"\u001B[39m\n\u001B[0;32m 566\u001B[0m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124me.g uxds.plot.scatter(...)\u001B[39m\u001B[38;5;124m\"\u001B[39m\n\u001B[0;32m 567\u001B[0m )\n", + "\u001B[1;31mValueError\u001B[0m: UxDataset.plot cannot be called directly. Use an explicit plot method, e.g uxds.plot.scatter(...)" + ] + } + ], + "execution_count": 10 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "## Weighted Mean based on Face Areas\n", + "Here we first look at the data values on each face and the faces' respsective areas. " + ], + "id": "bb77e5067497a0f5" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-22T21:26:06.221956Z", + "start_time": "2024-11-22T21:26:06.197646Z" + } + }, + "cell_type": "code", + "source": "uxds_face['random_data_face'].values", + "id": "c87fb018ca75ba5e", + "outputs": [ + { + "data": { + "text/plain": [ + "array([4.53649447, 3.31347744, 6.20654387, 2.48819151])" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 21 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-22T21:30:14.432490Z", + "start_time": "2024-11-22T21:30:14.421785Z" + } + }, + "cell_type": "code", + "source": "uxds_face.uxgrid.face_areas.data", + "id": "8f49a854de4e8131", + "outputs": [ + { + "data": { + "text/plain": [ + "array([1.95941760e-05, 1.96026212e-05, 1.96001041e-05, 1.95966022e-05])" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 31 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "We can simply call `.weighted_mean()` on the UXDataArray to compute the weighted mean. The differences between the weighted mean and the regular mean is small since the area differences across the faces are small. ", + "id": "def2b952ba7f1740" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-22T21:35:56.029750Z", + "start_time": "2024-11-22T21:35:55.969240Z" + } + }, + "cell_type": "code", + "source": [ + "result = uxds_face['random_data_face'].weighted_mean()\n", + "result.values" + ], + "id": "bd76b23993d9967f", + "outputs": [ + { + "data": { + "text/plain": [ + "array(4.13619375)" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 36 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-22T21:24:09.359761Z", + "start_time": "2024-11-22T21:24:09.335419Z" + } + }, + "cell_type": "code", + "source": [ + "unweighted_result = uxds_face['random_data_face'].mean()\n", + "unweighted_result.values" + ], + "id": "a92f056d6b2a567b", + "outputs": [ + { + "data": { + "text/plain": [ + "array(4.13617682)" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 18 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "## Weighted Mean Based on Edge Length\n", + "\n", + "Here we show the similar steps but for edge-centered data and the edge lengths. " + ], + "id": "e8ddb288f83486b8" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-22T21:26:35.150786Z", + "start_time": "2024-11-22T21:26:35.137787Z" + } + }, + "cell_type": "code", + "source": "uxds_edge['random_data_edge'].values", + "id": "1b01581da7ebacc", + "outputs": [ + { + "data": { + "text/plain": [ + "array([8.01802324, 9.72507993, 6.6914741 , 7.32080453, 3.30975404,\n", + " 0.22819568, 0.06583025, 6.35602489, 6.68668242, 9.7668741 ,\n", + " 6.30413784, 6.89570468, 1.90836517, 0.57331484, 0.81162917,\n", + " 7.37824547, 2.2410142 , 1.46405388, 1.90183779])" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 22 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-22T21:30:41.496315Z", + "start_time": "2024-11-22T21:30:39.715710Z" + } + }, + "cell_type": "code", + "source": "uxds_edge.uxgrid.edge_node_distances.data", + "id": "a50c11e691337cff", + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.00302971, 0.00276214, 0.00244141, 0.00241687, 0.00276214,\n", + " 0.00274047, 0.00302971, 0.00244141, 0.00276214, 0.00304932,\n", + " 0.00276214, 0.00304932, 0.0024657 , 0.00304932, 0.00276214,\n", + " 0.0024657 , 0.00244141, 0.00278363, 0.00304932], dtype=float32)" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 32 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "The differences between weighted and unweighted mean is more drastic (~0.1 value difference) since the edge lengths have a larger variance. ", + "id": "647630d11f001fcf" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-22T21:30:57.255355Z", + "start_time": "2024-11-22T21:30:57.197362Z" + } + }, + "cell_type": "code", + "source": [ + "result = uxds_edge['random_data_edge'].weighted_mean()\n", + "result.values" + ], + "id": "6bace8386234b942", + "outputs": [ + { + "data": { + "text/plain": [ + "array(4.58431796)" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 33 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-11-22T21:31:08.310566Z", + "start_time": "2024-11-22T21:31:08.291684Z" + } + }, + "cell_type": "code", + "source": [ + "unweighted_result = uxds_edge['random_data_edge'].mean()\n", + "unweighted_result.values" + ], + "id": "d81bc60c195be6ec", + "outputs": [ + { + "data": { + "text/plain": [ + "array(4.61300243)" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 34 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "## Custom Weights\n", + "\n", + "While custom weights are supported in `weighted-mean`, it is crucial that the weight inputs are mapped on the same unstructured grid as the data for accurate calculations. \n", + "\n", + "Data can be stored in various configurations in the unstructured grid. The figure below shows two possible configuration for face-centered data and edge-centered data can be stored for this particular grid. \n", + "\n", + "\n", + "\n", + "\n", + "\"Optional" + ], + "id": "6ff7f20189a4753c" + } + ], + "metadata": { + "kernelspec": { + "display_name": "rytam2uxarray", + "language": "python", + "name": "rytam2uxarray" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/test/test_weighted_mean.py b/test/test_weighted_mean.py index 967a3d1a8..f08b1c2a8 100644 --- a/test/test_weighted_mean.py +++ b/test/test_weighted_mean.py @@ -105,7 +105,6 @@ def test_quad_hex_edge_centered_dask(): # ensure values are within 3 decimal points of each other nt.assert_almost_equal(computed_result.values, expected_weighted_mean, decimal=3) - def test_csne30_equal_area(): """Compute the weighted average with a grid that has equal-area faces and compare the result to the regular mean.""" @@ -122,11 +121,27 @@ def test_csne30_equal_area(): # with equal area, both should be equal nt.assert_equal(weighted_mean, unweighted_mean) - -# TODO for Rachel @pytest.mark.parametrize("chunk_size", [1, 2, 4]) def test_csne30_equal_area_dask(chunk_size): + """Compares the weighted average computation for the quad hexagon grid + using a face centered data variable on a dask-backed UxDataset & Grid to the expected value computed by + hand.""" + uxds = ux.open_dataset(csne30_grid_path, csne30_data_path) + + # data and weights to be dask + uxda = uxds['psi'].chunk(n_face=chunk_size) + uxda.uxgrid.face_areas = uxda.uxgrid.face_areas.chunk(n_face=chunk_size) + + # Calculate lazy result + lazy_result = uxds['psi'].weighted_mean() + assert isinstance(lazy_result.data, da.Array) - # ... .chunk(n_face=chunk_size) + # compute result + computed_result = lazy_result.compute() + assert isinstance(computed_result.data, np.ndarray) - pass + # expected weighted average computed by hand + expected_weighted_mean = (uxds['psi'].values * uxds.uxgrid.face_areas).sum() / uxds.uxgrid.face_areas.sum() + + # ensure values are within 3 decimal points of each other + nt.assert_almost_equal(computed_result.values, expected_weighted_mean, decimal=3) diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index 6cbae22c8..2e33e9350 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -456,7 +456,13 @@ def weighted_mean(self, weights=None): Example ------- - >>> print("TODO Rachel") + >>> grid_path = "../../test/meshfiles/ugrid/quad-hexagon/grid.nc" + >>> face_data = "../../test/meshfiles/ugrid/quad-hexagon/data.nc" + >>> uxds = ux.open_dataset(grid_path, face_data) + >>> uxds['t2m'].values + + >>> weighted_mean = uxds['t2m'].weighted_mean() + Raises ------ From a5f0d3c905517e8f3581139b734cc02a706bc888 Mon Sep 17 00:00:00 2001 From: Rachel Tam Date: Tue, 3 Dec 2024 12:04:43 -0600 Subject: [PATCH 16/20] cleaned userguide output --- docs/user-guide/weighted_mean.ipynb | 350 +++------------------------- 1 file changed, 32 insertions(+), 318 deletions(-) diff --git a/docs/user-guide/weighted_mean.ipynb b/docs/user-guide/weighted_mean.ipynb index 684e88fd7..2e500154c 100644 --- a/docs/user-guide/weighted_mean.ipynb +++ b/docs/user-guide/weighted_mean.ipynb @@ -15,12 +15,7 @@ { "cell_type": "code", "id": "ed637e46-672c-45ba-a5f1-ed3c3248c8a1", - "metadata": { - "ExecuteTime": { - "end_time": "2024-11-22T20:19:40.863238Z", - "start_time": "2024-11-22T20:19:20.402282Z" - } - }, + "metadata": {}, "source": [ "import uxarray as ux\n", "import xarray as xr\n", @@ -30,132 +25,8 @@ "\n", "warnings.filterwarnings(\"ignore\")" ], - "outputs": [ - { - "data": { - "text/html": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/javascript": "(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.5.4/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.2.min.js\", \"https://cdn.holoviz.org/panel/1.5.4/dist/panel.min.js\"];\n const js_modules = [];\n const js_exports = {};\n const css_urls = [];\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (let i = 0; i < inline_js.length; i++) {\n try {\n inline_js[i].call(root, root.Bokeh);\n } catch(e) {\n if (!reloading) {\n throw e;\n }\n }\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));", - "application/vnd.holoviews_load.v0+json": "(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.5.4/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.2.min.js\", \"https://cdn.holoviz.org/panel/1.5.4/dist/panel.min.js\"];\n const js_modules = [];\n const js_exports = {};\n const css_urls = [];\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (let i = 0; i < inline_js.length; i++) {\n try {\n inline_js[i].call(root, root.Bokeh);\n } catch(e) {\n if (!reloading) {\n throw e;\n }\n }\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.holoviews_load.v0+json": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n function JupyterCommManager() {\n }\n\n JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n comm_manager.register_target(comm_id, function(comm) {\n comm.on_msg(msg_handler);\n });\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n comm.onMsg = msg_handler;\n });\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n })\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n", - "application/javascript": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n function JupyterCommManager() {\n }\n\n JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n comm_manager.register_target(comm_id, function(comm) {\n comm.on_msg(msg_handler);\n });\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n comm.onMsg = msg_handler;\n });\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n })\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "
\n", - "
\n", - "" - ], - "application/vnd.holoviews_exec.v0+json": "" - }, - "metadata": { - "application/vnd.holoviews_exec.v0+json": { - "id": "fd4be7ca-9a2f-4096-a552-6a5c3401c0b5" - } - }, - "output_type": "display_data" - } - ], - "execution_count": 1 + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -168,12 +39,7 @@ ] }, { - "metadata": { - "ExecuteTime": { - "end_time": "2024-11-22T21:28:50.609427Z", - "start_time": "2024-11-22T21:28:50.577219Z" - } - }, + "metadata": {}, "cell_type": "code", "source": [ "grid_path = \"../../test/meshfiles/ugrid/quad-hexagon/grid.nc\"\n", @@ -185,7 +51,7 @@ ], "id": "a2f58f07d7881d4f", "outputs": [], - "execution_count": 28 + "execution_count": null }, { "metadata": {}, @@ -194,12 +60,7 @@ "id": "b1192fd99760a758" }, { - "metadata": { - "ExecuteTime": { - "end_time": "2024-11-22T21:22:10.513514Z", - "start_time": "2024-11-22T21:22:10.444239Z" - } - }, + "metadata": {}, "cell_type": "code", "source": [ "(\n", @@ -217,21 +78,8 @@ ").opts(legend_position=\"top_right\")" ], "id": "6fac99462efcccdc", - "outputs": [ - { - "ename": "ValueError", - "evalue": "UxDataset.plot cannot be called directly. Use an explicit plot method, e.g uxds.plot.scatter(...)", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mValueError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[1;32mIn[10], line 4\u001B[0m\n\u001B[0;32m 1\u001B[0m (\n\u001B[0;32m 2\u001B[0m uxds\u001B[38;5;241m.\u001B[39muxgrid\u001B[38;5;241m.\u001B[39mplot(line_color\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mblack\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m 3\u001B[0m \u001B[38;5;241m*\u001B[39m \u001B[43muxds_edge\u001B[49m\n\u001B[1;32m----> 4\u001B[0m \u001B[43m \u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mplot\u001B[49m\u001B[43m(\u001B[49m\n\u001B[0;32m 5\u001B[0m \u001B[43m \u001B[49m\u001B[43mcmap\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43minferno\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43msize\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;241;43m150\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mmarker\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43msquare\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mclabel\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43;01mNone\u001B[39;49;00m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mtools\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m[\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mhover\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m]\u001B[49m\n\u001B[0;32m 6\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n\u001B[0;32m 7\u001B[0m \u001B[38;5;241m.\u001B[39mrelabel(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mEdge Data\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m 8\u001B[0m \u001B[38;5;241m*\u001B[39m uxds_face\n\u001B[0;32m 9\u001B[0m \u001B[38;5;241m.\u001B[39mplot(\n\u001B[0;32m 10\u001B[0m cmap\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124minferno\u001B[39m\u001B[38;5;124m\"\u001B[39m, size\u001B[38;5;241m=\u001B[39m\u001B[38;5;241m150\u001B[39m, marker\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mtriangle\u001B[39m\u001B[38;5;124m\"\u001B[39m, clabel\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m, tools\u001B[38;5;241m=\u001B[39m[\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mhover\u001B[39m\u001B[38;5;124m\"\u001B[39m]\n\u001B[0;32m 11\u001B[0m )\n\u001B[0;32m 12\u001B[0m \u001B[38;5;241m.\u001B[39mrelabel(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mFace Data\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m 13\u001B[0m )\u001B[38;5;241m.\u001B[39mopts(legend_position\u001B[38;5;241m=\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mtop_right\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n", - "File \u001B[1;32m~\\PycharmProjects\\uxarray\\uxarray\\plot\\accessor.py:564\u001B[0m, in \u001B[0;36mUxDatasetPlotAccessor.__call__\u001B[1;34m(self, **kwargs)\u001B[0m\n\u001B[0;32m 563\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21m__call__\u001B[39m(\u001B[38;5;28mself\u001B[39m, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m Any:\n\u001B[1;32m--> 564\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mValueError\u001B[39;00m(\n\u001B[0;32m 565\u001B[0m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mUxDataset.plot cannot be called directly. Use an explicit plot method, \u001B[39m\u001B[38;5;124m\"\u001B[39m\n\u001B[0;32m 566\u001B[0m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124me.g uxds.plot.scatter(...)\u001B[39m\u001B[38;5;124m\"\u001B[39m\n\u001B[0;32m 567\u001B[0m )\n", - "\u001B[1;31mValueError\u001B[0m: UxDataset.plot cannot be called directly. Use an explicit plot method, e.g uxds.plot.scatter(...)" - ] - } - ], - "execution_count": 10 + "outputs": [], + "execution_count": null }, { "metadata": {}, @@ -243,52 +91,20 @@ "id": "bb77e5067497a0f5" }, { - "metadata": { - "ExecuteTime": { - "end_time": "2024-11-22T21:26:06.221956Z", - "start_time": "2024-11-22T21:26:06.197646Z" - } - }, + "metadata": {}, "cell_type": "code", "source": "uxds_face['random_data_face'].values", "id": "c87fb018ca75ba5e", - "outputs": [ - { - "data": { - "text/plain": [ - "array([4.53649447, 3.31347744, 6.20654387, 2.48819151])" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 21 + "outputs": [], + "execution_count": null }, { - "metadata": { - "ExecuteTime": { - "end_time": "2024-11-22T21:30:14.432490Z", - "start_time": "2024-11-22T21:30:14.421785Z" - } - }, + "metadata": {}, "cell_type": "code", "source": "uxds_face.uxgrid.face_areas.data", "id": "8f49a854de4e8131", - "outputs": [ - { - "data": { - "text/plain": [ - "array([1.95941760e-05, 1.96026212e-05, 1.96001041e-05, 1.95966022e-05])" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 31 + "outputs": [], + "execution_count": null }, { "metadata": {}, @@ -297,58 +113,26 @@ "id": "def2b952ba7f1740" }, { - "metadata": { - "ExecuteTime": { - "end_time": "2024-11-22T21:35:56.029750Z", - "start_time": "2024-11-22T21:35:55.969240Z" - } - }, + "metadata": {}, "cell_type": "code", "source": [ "result = uxds_face['random_data_face'].weighted_mean()\n", "result.values" ], "id": "bd76b23993d9967f", - "outputs": [ - { - "data": { - "text/plain": [ - "array(4.13619375)" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 36 + "outputs": [], + "execution_count": null }, { - "metadata": { - "ExecuteTime": { - "end_time": "2024-11-22T21:24:09.359761Z", - "start_time": "2024-11-22T21:24:09.335419Z" - } - }, + "metadata": {}, "cell_type": "code", "source": [ "unweighted_result = uxds_face['random_data_face'].mean()\n", "unweighted_result.values" ], "id": "a92f056d6b2a567b", - "outputs": [ - { - "data": { - "text/plain": [ - "array(4.13617682)" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 18 + "outputs": [], + "execution_count": null }, { "metadata": {}, @@ -361,58 +145,20 @@ "id": "e8ddb288f83486b8" }, { - "metadata": { - "ExecuteTime": { - "end_time": "2024-11-22T21:26:35.150786Z", - "start_time": "2024-11-22T21:26:35.137787Z" - } - }, + "metadata": {}, "cell_type": "code", "source": "uxds_edge['random_data_edge'].values", "id": "1b01581da7ebacc", - "outputs": [ - { - "data": { - "text/plain": [ - "array([8.01802324, 9.72507993, 6.6914741 , 7.32080453, 3.30975404,\n", - " 0.22819568, 0.06583025, 6.35602489, 6.68668242, 9.7668741 ,\n", - " 6.30413784, 6.89570468, 1.90836517, 0.57331484, 0.81162917,\n", - " 7.37824547, 2.2410142 , 1.46405388, 1.90183779])" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 22 + "outputs": [], + "execution_count": null }, { - "metadata": { - "ExecuteTime": { - "end_time": "2024-11-22T21:30:41.496315Z", - "start_time": "2024-11-22T21:30:39.715710Z" - } - }, + "metadata": {}, "cell_type": "code", "source": "uxds_edge.uxgrid.edge_node_distances.data", "id": "a50c11e691337cff", - "outputs": [ - { - "data": { - "text/plain": [ - "array([0.00302971, 0.00276214, 0.00244141, 0.00241687, 0.00276214,\n", - " 0.00274047, 0.00302971, 0.00244141, 0.00276214, 0.00304932,\n", - " 0.00276214, 0.00304932, 0.0024657 , 0.00304932, 0.00276214,\n", - " 0.0024657 , 0.00244141, 0.00278363, 0.00304932], dtype=float32)" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 32 + "outputs": [], + "execution_count": null }, { "metadata": {}, @@ -421,58 +167,26 @@ "id": "647630d11f001fcf" }, { - "metadata": { - "ExecuteTime": { - "end_time": "2024-11-22T21:30:57.255355Z", - "start_time": "2024-11-22T21:30:57.197362Z" - } - }, + "metadata": {}, "cell_type": "code", "source": [ "result = uxds_edge['random_data_edge'].weighted_mean()\n", "result.values" ], "id": "6bace8386234b942", - "outputs": [ - { - "data": { - "text/plain": [ - "array(4.58431796)" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 33 + "outputs": [], + "execution_count": null }, { - "metadata": { - "ExecuteTime": { - "end_time": "2024-11-22T21:31:08.310566Z", - "start_time": "2024-11-22T21:31:08.291684Z" - } - }, + "metadata": {}, "cell_type": "code", "source": [ "unweighted_result = uxds_edge['random_data_edge'].mean()\n", "unweighted_result.values" ], "id": "d81bc60c195be6ec", - "outputs": [ - { - "data": { - "text/plain": [ - "array(4.61300243)" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 34 + "outputs": [], + "execution_count": null }, { "metadata": {}, From 97cdf920393220b99ddcb5377677284c13e286b8 Mon Sep 17 00:00:00 2001 From: Rachel Tam Date: Tue, 3 Dec 2024 13:03:10 -0600 Subject: [PATCH 17/20] restarted kernel --- docs/user-guide/weighted_mean.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/weighted_mean.ipynb b/docs/user-guide/weighted_mean.ipynb index 2e500154c..c102185ca 100644 --- a/docs/user-guide/weighted_mean.ipynb +++ b/docs/user-guide/weighted_mean.ipynb @@ -64,7 +64,7 @@ "cell_type": "code", "source": [ "(\n", - " uxds.uxgrid.plot(line_color=\"black\")\n", + " uxds_face.uxgrid.plot(line_color=\"black\")\n", " * uxds_edge\n", " .plot(\n", " cmap=\"inferno\", size=150, marker=\"square\", clabel=None, tools=[\"hover\"]\n", From 70a2b3fa907c03dba82e1b67b1a703d34e98bb8a Mon Sep 17 00:00:00 2001 From: Rachel Tam Date: Thu, 5 Dec 2024 09:31:38 -0600 Subject: [PATCH 18/20] removed duplicate notebook --- docs/user-guide/weighted-average.ipynb | 45 -------------------------- 1 file changed, 45 deletions(-) delete mode 100644 docs/user-guide/weighted-average.ipynb diff --git a/docs/user-guide/weighted-average.ipynb b/docs/user-guide/weighted-average.ipynb deleted file mode 100644 index c723f79a6..000000000 --- a/docs/user-guide/weighted-average.ipynb +++ /dev/null @@ -1,45 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Weighted Average" - ], - "metadata": { - "collapsed": false - }, - "id": "4850ba8becab8b0d" - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [], - "metadata": { - "collapsed": false - }, - "id": "ee8c405203e6faf" - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From d7a6d18c1d3b5caf52d26551c6c3b1702e2574b9 Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec Date: Thu, 5 Dec 2024 17:02:11 -0600 Subject: [PATCH 19/20] run pre-commit --- docs/user-guide/weighted_mean.ipynb | 162 +++++++++++++++------------- uxarray/core/dataarray.py | 4 +- 2 files changed, 87 insertions(+), 79 deletions(-) diff --git a/docs/user-guide/weighted_mean.ipynb b/docs/user-guide/weighted_mean.ipynb index c102185ca..627cd57da 100644 --- a/docs/user-guide/weighted_mean.ipynb +++ b/docs/user-guide/weighted_mean.ipynb @@ -14,8 +14,10 @@ }, { "cell_type": "code", + "execution_count": null, "id": "ed637e46-672c-45ba-a5f1-ed3c3248c8a1", "metadata": {}, + "outputs": [], "source": [ "import uxarray as ux\n", "import xarray as xr\n", @@ -24,9 +26,7 @@ "import warnings\n", "\n", "warnings.filterwarnings(\"ignore\")" - ], - "outputs": [], - "execution_count": null + ] }, { "cell_type": "markdown", @@ -39,158 +39,167 @@ ] }, { - "metadata": {}, "cell_type": "code", + "execution_count": null, + "id": "a2f58f07d7881d4f", + "metadata": {}, + "outputs": [], "source": [ "grid_path = \"../../test/meshfiles/ugrid/quad-hexagon/grid.nc\"\n", - "quad_hex_data_path_edge_centered = \"../../test/meshfiles/ugrid/quad-hexagon/random-edge-data.nc\"\n", - "quad_hex_data_path_face_centered = \"../../test/meshfiles/ugrid/quad-hexagon/random-face-data.nc\"\n", + "quad_hex_data_path_edge_centered = (\n", + " \"../../test/meshfiles/ugrid/quad-hexagon/random-edge-data.nc\"\n", + ")\n", + "quad_hex_data_path_face_centered = (\n", + " \"../../test/meshfiles/ugrid/quad-hexagon/random-face-data.nc\"\n", + ")\n", "\n", "uxds_face = ux.open_mfdataset(grid_path, quad_hex_data_path_face_centered)\n", "uxds_edge = ux.open_mfdataset(grid_path, quad_hex_data_path_edge_centered)" - ], - "id": "a2f58f07d7881d4f", - "outputs": [], - "execution_count": null + ] }, { - "metadata": {}, "cell_type": "markdown", - "source": "The figure below visualizes the dataset. ", - "id": "b1192fd99760a758" + "id": "b1192fd99760a758", + "metadata": {}, + "source": "The figure below visualizes the dataset. " }, { - "metadata": {}, "cell_type": "code", + "execution_count": null, + "id": "6fac99462efcccdc", + "metadata": {}, + "outputs": [], "source": [ "(\n", " uxds_face.uxgrid.plot(line_color=\"black\")\n", - " * uxds_edge\n", - " .plot(\n", + " * uxds_edge.plot(\n", " cmap=\"inferno\", size=150, marker=\"square\", clabel=None, tools=[\"hover\"]\n", - " )\n", - " .relabel(\"Edge Data\")\n", - " * uxds_face\n", - " .plot(\n", + " ).relabel(\"Edge Data\")\n", + " * uxds_face.plot(\n", " cmap=\"inferno\", size=150, marker=\"triangle\", clabel=None, tools=[\"hover\"]\n", - " )\n", - " .relabel(\"Face Data\")\n", + " ).relabel(\"Face Data\")\n", ").opts(legend_position=\"top_right\")" - ], - "id": "6fac99462efcccdc", - "outputs": [], - "execution_count": null + ] }, { - "metadata": {}, "cell_type": "markdown", + "id": "bb77e5067497a0f5", + "metadata": {}, "source": [ "## Weighted Mean based on Face Areas\n", "Here we first look at the data values on each face and the faces' respsective areas. " - ], - "id": "bb77e5067497a0f5" + ] }, { - "metadata": {}, "cell_type": "code", - "source": "uxds_face['random_data_face'].values", + "execution_count": null, "id": "c87fb018ca75ba5e", + "metadata": {}, "outputs": [], - "execution_count": null + "source": [ + "uxds_face[\"random_data_face\"].values" + ] }, { - "metadata": {}, "cell_type": "code", - "source": "uxds_face.uxgrid.face_areas.data", + "execution_count": null, "id": "8f49a854de4e8131", + "metadata": {}, "outputs": [], - "execution_count": null + "source": [ + "uxds_face.uxgrid.face_areas.data" + ] }, { - "metadata": {}, "cell_type": "markdown", - "source": "We can simply call `.weighted_mean()` on the UXDataArray to compute the weighted mean. The differences between the weighted mean and the regular mean is small since the area differences across the faces are small. ", - "id": "def2b952ba7f1740" + "id": "def2b952ba7f1740", + "metadata": {}, + "source": "We can simply call `.weighted_mean()` on the UXDataArray to compute the weighted mean. The differences between the weighted mean and the regular mean is small since the area differences across the faces are small. " }, { - "metadata": {}, "cell_type": "code", - "source": [ - "result = uxds_face['random_data_face'].weighted_mean()\n", - "result.values" - ], + "execution_count": null, "id": "bd76b23993d9967f", + "metadata": {}, "outputs": [], - "execution_count": null + "source": [ + "result = uxds_face[\"random_data_face\"].weighted_mean()\n", + "result.values" + ] }, { - "metadata": {}, "cell_type": "code", - "source": [ - "unweighted_result = uxds_face['random_data_face'].mean()\n", - "unweighted_result.values" - ], + "execution_count": null, "id": "a92f056d6b2a567b", + "metadata": {}, "outputs": [], - "execution_count": null + "source": [ + "unweighted_result = uxds_face[\"random_data_face\"].mean()\n", + "unweighted_result.values" + ] }, { - "metadata": {}, "cell_type": "markdown", + "id": "e8ddb288f83486b8", + "metadata": {}, "source": [ "## Weighted Mean Based on Edge Length\n", "\n", "Here we show the similar steps but for edge-centered data and the edge lengths. " - ], - "id": "e8ddb288f83486b8" + ] }, { - "metadata": {}, "cell_type": "code", - "source": "uxds_edge['random_data_edge'].values", + "execution_count": null, "id": "1b01581da7ebacc", + "metadata": {}, "outputs": [], - "execution_count": null + "source": [ + "uxds_edge[\"random_data_edge\"].values" + ] }, { - "metadata": {}, "cell_type": "code", - "source": "uxds_edge.uxgrid.edge_node_distances.data", + "execution_count": null, "id": "a50c11e691337cff", + "metadata": {}, "outputs": [], - "execution_count": null + "source": [ + "uxds_edge.uxgrid.edge_node_distances.data" + ] }, { - "metadata": {}, "cell_type": "markdown", - "source": "The differences between weighted and unweighted mean is more drastic (~0.1 value difference) since the edge lengths have a larger variance. ", - "id": "647630d11f001fcf" + "id": "647630d11f001fcf", + "metadata": {}, + "source": "The differences between weighted and unweighted mean is more drastic (~0.1 value difference) since the edge lengths have a larger variance. " }, { - "metadata": {}, "cell_type": "code", - "source": [ - "result = uxds_edge['random_data_edge'].weighted_mean()\n", - "result.values" - ], + "execution_count": null, "id": "6bace8386234b942", + "metadata": {}, "outputs": [], - "execution_count": null + "source": [ + "result = uxds_edge[\"random_data_edge\"].weighted_mean()\n", + "result.values" + ] }, { - "metadata": {}, "cell_type": "code", - "source": [ - "unweighted_result = uxds_edge['random_data_edge'].mean()\n", - "unweighted_result.values" - ], + "execution_count": null, "id": "d81bc60c195be6ec", + "metadata": {}, "outputs": [], - "execution_count": null + "source": [ + "unweighted_result = uxds_edge[\"random_data_edge\"].mean()\n", + "unweighted_result.values" + ] }, { - "metadata": {}, "cell_type": "markdown", + "id": "6ff7f20189a4753c", + "metadata": {}, "source": [ "## Custom Weights\n", "\n", @@ -202,8 +211,7 @@ "\n", "\n", "\"Optional" - ], - "id": "6ff7f20189a4753c" + ] } ], "metadata": { diff --git a/uxarray/core/dataarray.py b/uxarray/core/dataarray.py index 2e33e9350..2d220dee5 100644 --- a/uxarray/core/dataarray.py +++ b/uxarray/core/dataarray.py @@ -459,9 +459,9 @@ def weighted_mean(self, weights=None): >>> grid_path = "../../test/meshfiles/ugrid/quad-hexagon/grid.nc" >>> face_data = "../../test/meshfiles/ugrid/quad-hexagon/data.nc" >>> uxds = ux.open_dataset(grid_path, face_data) - >>> uxds['t2m'].values + >>> uxds["t2m"].values - >>> weighted_mean = uxds['t2m'].weighted_mean() + >>> weighted_mean = uxds["t2m"].weighted_mean() Raises From 1613a2d1b353c6e23249ea0ca4f52a8da1ad28f0 Mon Sep 17 00:00:00 2001 From: Philip Chmielowiec Date: Thu, 5 Dec 2024 17:03:44 -0600 Subject: [PATCH 20/20] re-run notebook --- docs/user-guide/weighted_mean.ipynb | 70 ++++++++++++++--------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/docs/user-guide/weighted_mean.ipynb b/docs/user-guide/weighted_mean.ipynb index 627cd57da..92a27905c 100644 --- a/docs/user-guide/weighted_mean.ipynb +++ b/docs/user-guide/weighted_mean.ipynb @@ -14,10 +14,8 @@ }, { "cell_type": "code", - "execution_count": null, "id": "ed637e46-672c-45ba-a5f1-ed3c3248c8a1", "metadata": {}, - "outputs": [], "source": [ "import uxarray as ux\n", "import xarray as xr\n", @@ -26,7 +24,9 @@ "import warnings\n", "\n", "warnings.filterwarnings(\"ignore\")" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -40,10 +40,8 @@ }, { "cell_type": "code", - "execution_count": null, "id": "a2f58f07d7881d4f", "metadata": {}, - "outputs": [], "source": [ "grid_path = \"../../test/meshfiles/ugrid/quad-hexagon/grid.nc\"\n", "quad_hex_data_path_edge_centered = (\n", @@ -55,7 +53,9 @@ "\n", "uxds_face = ux.open_mfdataset(grid_path, quad_hex_data_path_face_centered)\n", "uxds_edge = ux.open_mfdataset(grid_path, quad_hex_data_path_edge_centered)" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -65,10 +65,8 @@ }, { "cell_type": "code", - "execution_count": null, "id": "6fac99462efcccdc", "metadata": {}, - "outputs": [], "source": [ "(\n", " uxds_face.uxgrid.plot(line_color=\"black\")\n", @@ -79,7 +77,9 @@ " cmap=\"inferno\", size=150, marker=\"triangle\", clabel=None, tools=[\"hover\"]\n", " ).relabel(\"Face Data\")\n", ").opts(legend_position=\"top_right\")" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -92,23 +92,23 @@ }, { "cell_type": "code", - "execution_count": null, "id": "c87fb018ca75ba5e", "metadata": {}, - "outputs": [], "source": [ "uxds_face[\"random_data_face\"].values" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "8f49a854de4e8131", "metadata": {}, - "outputs": [], "source": [ "uxds_face.uxgrid.face_areas.data" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -118,25 +118,25 @@ }, { "cell_type": "code", - "execution_count": null, "id": "bd76b23993d9967f", "metadata": {}, - "outputs": [], "source": [ "result = uxds_face[\"random_data_face\"].weighted_mean()\n", "result.values" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "a92f056d6b2a567b", "metadata": {}, - "outputs": [], "source": [ "unweighted_result = uxds_face[\"random_data_face\"].mean()\n", "unweighted_result.values" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -150,23 +150,23 @@ }, { "cell_type": "code", - "execution_count": null, "id": "1b01581da7ebacc", "metadata": {}, - "outputs": [], "source": [ "uxds_edge[\"random_data_edge\"].values" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "a50c11e691337cff", "metadata": {}, - "outputs": [], "source": [ "uxds_edge.uxgrid.edge_node_distances.data" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -176,25 +176,25 @@ }, { "cell_type": "code", - "execution_count": null, "id": "6bace8386234b942", "metadata": {}, - "outputs": [], "source": [ "result = uxds_edge[\"random_data_edge\"].weighted_mean()\n", "result.values" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "code", - "execution_count": null, "id": "d81bc60c195be6ec", "metadata": {}, - "outputs": [], "source": [ "unweighted_result = uxds_edge[\"random_data_edge\"].mean()\n", "unweighted_result.values" - ] + ], + "outputs": [], + "execution_count": null }, { "cell_type": "markdown", @@ -216,9 +216,9 @@ ], "metadata": { "kernelspec": { - "display_name": "rytam2uxarray", + "display_name": "Python 3 (ipykernel)", "language": "python", - "name": "rytam2uxarray" + "name": "python3" }, "language_info": { "codemirror_mode": {