From 7bc24ff884a08253ff00e91c7485a1223e46dca2 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Wed, 15 Nov 2023 12:30:14 +0000 Subject: [PATCH 01/22] Squashed commit of the following: commit 0b4e9f15a5682a506eb4588b93b8a8a71c507490 Author: David Hassell Date: Tue Nov 14 11:38:20 2023 +0000 depency versions commit 48f543ddaf8df214ffcfd3a73094d4adb97ec79f Author: David Hassell Date: Tue Nov 14 11:37:06 2023 +0000 cfdm version commit 31c05bedcbf2d54fefe2ac4ac3defbb76db14c6b Author: David Hassell Date: Tue Nov 14 11:36:40 2023 +0000 scipy required commit 0161159f6342b5a32f70fab66c0583e4b84e5f5d Merge: 4b87c223e 2d53ef6b6 Author: David Hassell Date: Tue Nov 14 11:24:47 2023 +0000 Merge branch 'main' into ugrid-0 commit 4b87c223ecad1553eb3e86257f001f4c87244367 Author: David Hassell Date: Tue Nov 14 11:07:49 2023 +0000 UGRIDVER -> 3.16.0 commit 6c18b1e495510c5e8d3d69abadfafc23f9a11a72 Author: David Hassell Date: Fri Nov 10 13:59:44 2023 +0000 missing methods in docs rst commit 67861983a689c91709f91fd2a4660805787ee7a7 Author: David Hassell Date: Fri Nov 10 13:40:02 2023 +0000 more ugrid tests commit 09d4335ef988025a49538be64099f7dcf31d2460 Author: David Hassell Date: Fri Nov 10 13:30:00 2023 +0000 sparse arrays commit 48ed306f875422eaa866f64631e16b6607e8f43a Author: David Hassell Date: Fri Nov 10 12:42:39 2023 +0000 format commit 6494aad7e888169310881ec0a52d7997aaa88455 Author: David Hassell Date: Fri Nov 10 12:39:39 2023 +0000 cartesian regridding ugrid commit 4af0948dfcfaf2ba8aa52378c33b29a468cb05a7 Author: David Hassell Date: Fri Nov 10 12:11:42 2023 +0000 UGRID ref commit 01ed055bca8babf743842f31b467d01000866cf5 Author: David Hassell Date: Fri Nov 10 12:10:38 2023 +0000 cfdm version commit 943664e9cbc7a430f3f1e9d572705d0fc1e5c0af Author: David Hassell Date: Fri Nov 10 12:09:28 2023 +0000 mandatory scipy commit b96ca212267fca0b9b2e66c063a9220d5eed9199 Author: David Hassell Date: Fri Nov 10 11:50:21 2023 +0000 comment commit 65bfb9eb58c018f23f06abf4c4e07e9a7f37f26f Author: David Hassell Date: Mon Nov 6 13:04:26 2023 +0000 remove commented code commit ead7e08bca8140c8f7b8fdeeb76803baffa0660b Author: David Hassell Date: Mon Nov 6 11:49:28 2023 +0000 fix ugrid file commit ab388f997e74db0930a23630c6a7e5a375357a32 Author: David Hassell Date: Mon Oct 23 19:05:50 2023 +0100 dev commit bc68bd7aead3b3328424c210a24f4f4f0757283b Author: David Hassell Date: Mon Oct 23 10:52:25 2023 +0100 dev commit e07bf2d6f2ec854bd56633b5ab5be66c9e72b7bf Author: David Hassell Date: Mon Oct 23 10:06:17 2023 +0100 weights_file commit cc034ecb2f6b3f3e439661f279aafcdd113cb1fb Merge: 4b1f7bd04 1668e0b46 Author: David Hassell Date: Mon Oct 23 09:45:25 2023 +0100 upstream merge commit 4b1f7bd04d5b65cc2f9dbcb927d13bdf73a7abef Author: David Hassell Date: Mon Oct 23 09:42:49 2023 +0100 dev commit 3ba860a4c51bfe34be2315f3ec1f843877cdeddf Merge: ee3bdacd2 492d42460 Author: David Hassell Date: Mon Oct 23 09:10:12 2023 +0100 dev commit 492d42460648c88e291f433d596bc8939663c262 Author: David Hassell Date: Thu Oct 19 17:50:22 2023 +0100 dev commit ee3bdacd203c13a91b0e8598169397488feea7d8 Author: David Hassell Date: Thu Oct 19 09:20:16 2023 +0100 dev commit 7552ff4f71527e9d0b2c532feb3fdacc2fb31a2c Author: David Hassell Date: Wed Oct 11 19:34:54 2023 +0100 dev commit c9984eb5d747a9bcdd1aeb9f5783e1a979b30d39 Author: David Hassell Date: Wed Oct 11 15:38:55 2023 +0100 dev commit 4cecab693ecbd9743c32a0706a616852089c925c Merge: f668a36d7 70bec028e Author: David Hassell Date: Wed Oct 11 14:48:38 2023 +0100 Merge branch 'main' of github.com:NCAS-CMS/cf-python into ugrid-0 commit f668a36d75772293987b48586177ae1502c648c0 Author: David Hassell Date: Wed Oct 11 13:36:31 2023 +0100 dev commit b185ae4025f9a415b90827f37702762684a8a40f Author: David Hassell Date: Wed Oct 11 13:31:37 2023 +0100 global UGRID files commit 68b2b0fd64c75d7dda878dca27f2652449a82c09 Author: David Hassell Date: Wed Oct 11 13:30:56 2023 +0100 ignore CDL files commit 0d68338d60832844840ea7d31aa8e8f75ad8e7f7 Author: David Hassell Date: Wed Oct 11 08:16:34 2023 +0100 pre-release commit 493d0e08ebeec34f50ac5c331fe75909c75b22e6 Merge: 5669ae3cc af6585535 Author: David Hassell Date: Tue Oct 10 15:28:36 2023 +0100 3.15.4 merge commit 5669ae3ccaf90bfe96f931c4440c951a26d267b3 Author: David Hassell Date: Tue Oct 10 13:04:58 2023 +0100 dev commit 750875e66d6092139e2745acda0cdfb5e99b95a1 Author: David Hassell Date: Mon Oct 9 21:42:09 2023 +0100 dev commit f25b956e54a74c35dbe48e6039785429a2421e26 Merge: 53ce87bc8 972cdaadc Author: David Hassell Date: Mon Oct 9 21:41:23 2023 +0100 Merge branch 'main' of github.com:NCAS-CMS/cf-python into ugrid-0 commit 53ce87bc87fa89464f2ea9f7e7ce6499a58d5c78 Author: David Hassell Date: Mon Oct 9 21:41:02 2023 +0100 dev commit 80c19f1c9b12111762fe5bd2aee3c61ed2b0f62e Author: David Hassell Date: Fri Oct 6 17:00:27 2023 +0100 dev commit 28d624bc338bb6b1b14b0d129b708e46d43445f8 Author: David Hassell Date: Fri Oct 6 10:09:30 2023 +0100 dev commit a0a4771228d000974d276c5c0d791e007cafd8d4 Author: David Hassell Date: Thu Oct 5 23:50:14 2023 +0100 dev commit 264780a18781dbe254371db8bfa6846e63f9d3f8 Author: David Hassell Date: Wed Oct 4 14:17:41 2023 +0100 dev commit 3f3f98ea00beec19ac6042005ad09c4142508ee1 Author: David Hassell Date: Sun Oct 1 12:50:55 2023 +0100 dev commit 2023f5a8d6787de0af65bad73d7c2c08267859a4 Author: David Hassell Date: Sat Sep 30 14:17:26 2023 +0100 dev commit 054b2727f47479328c4587d34a09662e7d9ad21e Author: David Hassell Date: Sat Sep 30 12:05:37 2023 +0100 dev commit 7ce67d261f36832a5c35e2719ce041d4253beb19 Author: David Hassell Date: Fri Sep 29 18:29:23 2023 +0100 dev commit 58209781b1e957cef504580c2c2c3b25a80b94c4 Author: David Hassell Date: Thu Sep 28 16:09:17 2023 +0100 regrid commit 726223e940c1adfc35e23f87df8e75650c50238c Author: David Hassell Date: Thu Sep 28 14:52:10 2023 +0100 geometry spherical polygon weights commit a7ce7b33f3638a49344c073e1bd5881b1cbd4118 Author: David Hassell Date: Wed Sep 27 16:05:30 2023 +0100 dev commit 5b8185d391643ce1906dc1188a74f868f16e9026 Author: David Hassell Date: Wed Sep 27 16:03:08 2023 +0100 dev commit 2ebe7b80c8b6d04a8bb1c683d85caa92dd20cb28 Author: David Hassell Date: Tue Sep 26 18:51:01 2023 +0100 dev commit 075dba91bc8fa58e281d53a598d4787dd4a3e2c1 Author: David Hassell Date: Tue Sep 26 16:45:37 2023 +0100 dev commit b89b8c247359699f59ab3fe843346d819f043a82 Author: David Hassell Date: Tue Sep 26 09:16:29 2023 +0100 dev commit 1afb34a720087143d18b5892664ecca744217c29 Author: David Hassell Date: Mon Sep 25 19:23:23 2023 +0100 dev commit 6009f36725c82b22090f0f76bc380c420ffb8bbf Author: David Hassell Date: Mon Sep 25 13:26:13 2023 +0100 dev commit 939aba4728499f745565f92c2161c18b44fc75f0 Merge: 05c1ed1a4 2cfc8e69d Author: David Hassell Date: Mon Sep 25 10:02:01 2023 +0100 Merge branch 'main' of github.com:NCAS-CMS/cf-python into ugrid-0 commit 05c1ed1a41f08f1b184c470865de69c165b69e5e Author: David Hassell Date: Fri Sep 22 18:01:31 2023 +0100 dev commit c542050f3d44516c589d80938dc383654f54a3d7 Author: David Hassell Date: Thu Sep 21 10:00:51 2023 +0100 dev commit e94cd3d59d9a444049cf09cd98aa567bb8dcd6c5 Merge: 094a60a3f 88febb797 Author: David Hassell Date: Thu Sep 21 08:44:30 2023 +0100 Merge branch 'main' of github.com:NCAS-CMS/cf-python into ugrid-0 commit 094a60a3f329b74ce3323e7667cf25b35e18d8ea Author: David Hassell Date: Wed Sep 20 23:06:52 2023 +0100 dev commit ebd47b4a3e0f56de0334ea1218d7df6d0256ff97 Author: David Hassell Date: Wed Sep 20 13:44:38 2023 +0100 dev commit e9a2d26146098e23b0823349700181f5187573db Author: David Hassell Date: Tue Sep 19 19:34:50 2023 +0100 dev commit 6b302801dd2d27c8ca71b34cf41f0af6168dc6d0 Author: David Hassell Date: Tue Sep 19 17:37:57 2023 +0100 dev commit 529b143263be5c0fe5f6fcfe710e32bf9b472b96 Author: David Hassell Date: Mon Sep 18 10:40:17 2023 +0100 dev commit e11422d493ebf93dfc7e4d7c3c39d80f8d615826 Author: David Hassell Date: Sun Sep 17 15:46:42 2023 +0100 dev commit 0c84d90b8615ef093a993de4e58e48a206e8a6d0 Author: David Hassell Date: Sun Sep 17 12:06:45 2023 +0100 dev commit dd5df6a172341d960726457cd3d075df84b3eecb Author: David Hassell Date: Thu Sep 14 16:01:33 2023 +0100 dev commit 3f0ad2a501d3bd1fc29d2940f2832969068753a6 Author: David Hassell Date: Thu Sep 14 14:09:48 2023 +0100 dev commit 26ac71a4b23503521f2513cf3a87e2dade6ec127 Author: David Hassell Date: Thu Sep 14 09:33:30 2023 +0100 dev commit 25142fa1947d91bad1620b103fba2a5406462be1 Author: David Hassell Date: Wed Sep 13 23:13:34 2023 +0100 dev commit 672cb98046499ff2997dbda5366bbbafe7deb608 Author: David Hassell Date: Wed Sep 13 16:19:33 2023 +0100 dev commit c3ad04c7aa4b6a9bbdbf305d7c4807346df15fb8 Author: David Hassell Date: Wed Sep 13 09:44:12 2023 +0100 dev commit 71f31f6ae040e2b74d47f0ec7634a4874dab83bd Author: David Hassell Date: Thu Sep 7 13:05:30 2023 +0100 dev commit 82efa8baa34fa87cf5569c9b9e56fcf5b4f1b4af Author: David Hassell Date: Wed Sep 6 18:35:01 2023 +0100 dev commit 229e3837d6a8c60281b2b546d6fe12e102a6bcdf Author: David Hassell Date: Tue Sep 5 19:51:31 2023 +0100 dev commit 4250b110f1f2146d0bea7d5737b42d596c70116d Author: David Hassell Date: Tue Sep 5 19:37:51 2023 +0100 upstream merge commit 2c70d9838fe351c9c3e27e56cc065da0c619840f Merge: 87ffa9b19 ed9e4c827 Author: David Hassell Date: Tue Sep 5 19:37:30 2023 +0100 Merge branch 'main' of github.com:NCAS-CMS/cf-python into ugrid-0 commit 87ffa9b1902e4f8486f99e6b739fb5b6228454af Author: David Hassell Date: Fri Aug 25 14:54:01 2023 +0100 dev commit e0d89c3a3df81856ccf8690708c70a512ba77205 Author: David Hassell Date: Fri Aug 25 10:51:18 2023 +0100 dev commit bff67d9153a311f4b300b4e9ad1072e9188c1e08 Author: David Hassell Date: Thu Aug 24 22:35:51 2023 +0100 dev commit 5bd3c5bec3455577d5e65dadf255eea06d6a4b4f Author: David Hassell Date: Thu Aug 24 15:13:17 2023 +0100 dev commit bc279f2bdb265a2bb4f77c36794d76359acbba70 Author: David Hassell Date: Wed Aug 23 18:31:14 2023 +0100 dev commit 354b6622ff987fdbc6afe052fd241793fb0f130f Author: David Hassell Date: Wed Aug 23 15:56:22 2023 +0100 dev commit 4aef6c7671a0debeeaf4b971cc97a351115c3f01 Author: David Hassell Date: Wed Aug 23 10:03:56 2023 +0100 dev commit 0e368b80ee0990442240ba715704ea06263fb8a8 Author: David Hassell Date: Tue Aug 22 17:23:42 2023 +0100 dev --- .gitignore | 1 + Changelog.rst | 16 +- MANIFEST.in | 4 +- README.md | 28 +- cf/__init__.py | 29 +- cf/aggregate.py | 522 +- cf/auxiliarycoordinate.py | 12 +- cf/bounds.py | 8 - cf/cellconnectivity.py | 185 + cf/cellmeasure.py | 8 - cf/cfimplementation.py | 15 + cf/constructs.py | 5 +- cf/count.py | 40 +- cf/data/array/__init__.py | 3 + cf/data/array/boundsfromnodesarray.py | 23 + cf/data/array/cellconnectivityarray.py | 25 + cf/data/array/gatheredarray.py | 94 - cf/data/array/mixin/__init__.py | 1 - cf/data/array/mixin/compressedarraymixin.py | 81 + cf/data/array/mixin/raggedarraymixin.py | 97 - cf/data/array/netcdfarray.py | 8 - cf/data/array/pointtopologyarray.py | 21 + cf/data/array/raggedcontiguousarray.py | 14 +- cf/data/array/raggedindexedarray.py | 14 +- cf/data/array/raggedindexedcontiguousarray.py | 17 +- cf/data/array/subsampledarray.py | 12 - cf/data/collapse/dask_collapse.py | 70 +- cf/data/dask_regrid.py | 67 +- cf/data/dask_utils.py | 7 +- cf/data/data.py | 329 +- cf/data/utils.py | 47 + cf/dimensioncoordinate.py | 8 - cf/docstring/docstring.py | 21 + cf/domain.py | 8 - cf/domainancillary.py | 8 - cf/domaintopology.py | 235 + cf/field.py | 1889 +-- cf/fieldancillary.py | 35 +- cf/functions.py | 6 +- cf/index.py | 48 +- cf/interiorring.py | 38 +- cf/interpolationparameter.py | 33 +- cf/list.py | 27 +- cf/mixin/fielddomain.py | 3 +- cf/mixin/propertiesdata.py | 57 - cf/mixin/propertiesdatabounds.py | 59 - cf/mixin2/container.py | 12 +- cf/mixin_container.py | 16 +- cf/nodecountproperties.py | 20 +- cf/partnodecountproperties.py | 25 +- cf/regrid/regrid.py | 743 +- cf/regrid/regridoperator.py | 24 +- cf/test/create_test_files.py | 10707 +--------------- cf/test/create_test_files_2.py | 10440 +++++++++++++++ cf/test/individual_tests.sh | 16 +- cf/test/run_tests.py | 5 + cf/test/test_CellConnectivity.py | 104 + cf/test/test_Data.py | 99 +- cf/test/test_DomainTopology.py | 98 + cf/test/test_Field.py | 47 +- cf/test/test_Maths.py | 24 - cf/test/test_RegridOperator.py | 2 + cf/test/test_UGRID.py | 189 + cf/test/test_aggregate.py | 41 +- cf/test/test_collapse.py | 40 +- cf/test/test_external.py | 2 +- cf/test/test_gathering.py | 2 +- cf/test/test_read_write.py | 12 +- cf/test/test_regrid_mesh.py | 294 + cf/test/test_weights.py | 299 + cf/test/ugrid_global_1.nc | Bin 0 -> 37356 bytes cf/test/ugrid_global_2.nc | Bin 0 -> 116164 bytes cf/tiepointindex.py | 53 +- cf/weights.py | 1903 +++ docs/source/class/cf.AuxiliaryCoordinate.rst | 9 +- docs/source/class/cf.Constructs.rst | 2 + docs/source/class/cf.Data.rst | 8 +- docs/source/class/cf.Domain.rst | 20 +- docs/source/class/cf.Field.rst | 24 +- docs/source/class/cf.GatheredArray.rst | 1 + .../source/class/cf.RaggedContiguousArray.rst | 1 + docs/source/class/cf.RaggedIndexedArray.rst | 1 + .../class/cf.RaggedIndexedContiguousArray.rst | 1 + docs/source/class/cf.RegridOperator.rst | 1 + docs/source/field_analysis.rst | 9 + docs/source/installation.rst | 10 +- docs/source/tutorial.rst | 43 +- requirements.txt | 3 +- 88 files changed, 16409 insertions(+), 13219 deletions(-) create mode 100644 cf/cellconnectivity.py create mode 100644 cf/data/array/boundsfromnodesarray.py create mode 100644 cf/data/array/cellconnectivityarray.py delete mode 100644 cf/data/array/mixin/raggedarraymixin.py create mode 100644 cf/data/array/pointtopologyarray.py create mode 100644 cf/domaintopology.py create mode 100644 cf/test/create_test_files_2.py create mode 100644 cf/test/test_CellConnectivity.py create mode 100644 cf/test/test_DomainTopology.py create mode 100644 cf/test/test_UGRID.py create mode 100644 cf/test/test_regrid_mesh.py create mode 100644 cf/test/test_weights.py create mode 100644 cf/test/ugrid_global_1.nc create mode 100644 cf/test/ugrid_global_2.nc create mode 100644 cf/weights.py diff --git a/.gitignore b/.gitignore index 8c91defd7f..d6604997d1 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ cf/test/dir/ cf/test/*.nc cf/test/*.nca +cf/test/*.cdl cf/test/*.txt # coverage reports from a running the test coverage script diff --git a/Changelog.rst b/Changelog.rst index 44690e6419..33c6a17657 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -3,10 +3,22 @@ version 3.16.0 **2023-??-??** +* Implemented the reading and manipulation of UGRID mesh topologies + (https://github.com/NCAS-CMS/cf-python/issues/696) +* New methods: `cf.Field.cell_connectivity`, + `cf.Field.cell_connectivities` + (https://github.com/NCAS-CMS/cf-python/issues/696) +* New methods: `cf.Field.domain_topology`, + `cf.Field.domain_topologies` + (https://github.com/NCAS-CMS/cf-python/issues/696) +* New methods: `cf.Data.arctan2`, `cf.Data.masked-values` + (https://github.com/NCAS-CMS/cf-python/issues/696) * Fix bug that caused `cf.Field.collapse` to give incorrect results for the "sum", "sum_of_weights" and "sum_of_weights2" methods, only in the case that weights have been requested (https://github.com/NCAS-CMS/cf-python/issues/701) +* Changed dependency: ``1.11.0.0<=cfdm<1.11.1.0`` +* New dependency: ``scipy>=1.10.0`` version 3.15.4 -------------- @@ -283,8 +295,8 @@ version 3.11.0 * Fix for `cf.aggregate` failures when a datum or coordinate conversion parameter has an array value (https://github.com/NCAS-CMS/cf-python/issues/230) -* Allow for regridding using a destination field featuring size 1 dimension(s) - (https://github.com/NCAS-CMS/cf-python/issues/250) +* Allow for regridding using a destination field featuring size 1 + dimension(s) (https://github.com/NCAS-CMS/cf-python/issues/250) * Fix bug that sometimes caused `cf.Field.autocyclic` to fail when setting a construct that is cyclic and has a defined period * Fix bug that sometimes caused a failure when reading PP extra data diff --git a/MANIFEST.in b/MANIFEST.in index b0ad4f679d..b8ba825ad8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -8,6 +8,6 @@ recursive-exclude cf *.o *.so *.a .nfs* recursive-exclude cf/data *.rst prune cf/test recursive-include cf/test __init__.py test_*.py -recursive-include cf/test cfa_test.sh run_tests.py setup_create_field.py create_test_files.py individual_tests.sh -recursive-include cf/test test_file.nc test_file[2-4].nc file.nc file[1-9].nc wgdos_packed.pp extra_data.pp file1.pp *.cdl +recursive-include cf/test cfa_test.sh run_tests.py setup_create_field.py create_test_files*.py individual_tests.sh +recursive-include cf/test test_file.nc test_file[2-4].nc file.nc file[1-9].nc ugrid_global_1.nc ugrid_global_2.nc wgdos_packed.pp extra_data.pp file1.pp *.cdl prune cf/test/dir diff --git a/README.md b/README.md index 2c76aa5913..c6425ed0a1 100644 --- a/README.md +++ b/README.md @@ -85,52 +85,34 @@ The `cf` package uses of its array manipulation and can: * read field constructs from netCDF, CDL, PP and UM datasets, - * create new field constructs in memory, - -* write and append field constructs to netCDF datasets on disk, - +* write and append field and domain constructs to netCDF datasets on disk, +* read, create, and manipulate UGRID mesh topologies, * read, write, and create coordinates defined by geometry cells, - * read netCDF and CDL datasets containing hierarchical groups, - * inspect field constructs, - * test whether two field constructs are the same, - * modify field construct metadata and data, - * create subspaces of field constructs, - * write field constructs to netCDF datasets on disk, - * incorporate, and create, metadata stored in external files, - * read, write, and create data that have been compressed by convention (i.e. ragged or gathered arrays, or coordinate arrays compressed by subsampling), whilst presenting a view of the data in its uncompressed form, - * combine field constructs arithmetically, - * manipulate field construct data by arithmetical and trigonometrical operations, - -* perform statistical collapses on field constructs, - +* perform weighted statistical collapses on field constructs, + including those with geometry cells and UGRID mesh topologies, * perform histogram, percentile and binning operations on field constructs, - * regrid field constructs with (multi-)linear, nearest neighbour, first- and second-order conservative and higher order patch recovery - methods, - + methods, to and from structured and unstructured grids, * apply convolution filters to field constructs, - * create running means from field constructs, - * apply differential operators to field constructs, - * create derived quantities (such as relative vorticity). Visualization diff --git a/cf/__init__.py b/cf/__init__.py index 27ae8680af..9c192d0d24 100644 --- a/cf/__init__.py +++ b/cf/__init__.py @@ -73,9 +73,9 @@ """ -__Conventions__ = "CF-1.10" -__date__ = "2023-10-10" -__version__ = "3.15.4" +__Conventions__ = "CF-1.11" +__date__ = "2023-??-??" +__version__ = "3.16.0" _requires = ( "numpy", @@ -86,6 +86,7 @@ "psutil", "dask", "packaging", + "scipy", ) x = ", ".join(_requires) @@ -144,6 +145,11 @@ except ImportError as error1: raise ImportError(_error0 + str(error1)) +try: + import scipy +except ImportError as error1: + raise ImportError(_error0 + str(error1)) + # Check the version of packaging _minimum_vn = "20.0" if Version(packaging.__version__) < Version(_minimum_vn): @@ -193,8 +199,8 @@ ) # Check the version of cfdm -_minimum_vn = "1.10.1.2" -_maximum_vn = "1.10.2.0" +_minimum_vn = "1.11.0.0" +_maximum_vn = "1.11.1.0" _cfdm_version = Version(cfdm.__version__) if not Version(_minimum_vn) <= _cfdm_version < Version(_maximum_vn): raise RuntimeError( @@ -218,6 +224,14 @@ f"or later. Got {platform.python_version()}" ) +# Check the version of scipy +_minimum_vn = "1.10.0" +if Version(scipy.__version__) < Version(_minimum_vn): + raise RuntimeError( + f"Bad scipy version: cf requires scipy>={_minimum_vn}. " + f"Got {scipy.__version__} at {scipy.__file__}" + ) + from .constructs import Constructs from .mixin import Coordinate @@ -248,18 +262,23 @@ from .dimensioncoordinate import DimensionCoordinate from .auxiliarycoordinate import AuxiliaryCoordinate from .coordinatereference import CoordinateReference +from .cellconnectivity import CellConnectivity from .cellmethod import CellMethod from .cellmeasure import CellMeasure from .domainancillary import DomainAncillary from .domainaxis import DomainAxis +from .domaintopology import DomainTopology from .fieldancillary import FieldAncillary from .field import Field from .data import Data from .data.array import ( + BoundsFromNodesArray, + CellConnectivityArray, CFANetCDFArray, FullArray, GatheredArray, NetCDFArray, + PointTopologyArray, RaggedContiguousArray, RaggedIndexedArray, RaggedIndexedContiguousArray, diff --git a/cf/aggregate.py b/cf/aggregate.py index 2771fbd7cb..728e5aebea 100644 --- a/cf/aggregate.py +++ b/cf/aggregate.py @@ -224,6 +224,8 @@ class _Meta: "Nd_coordinates", "Cell_measures", "Domain_ancillaries", + "Domain_topologies", + "Cell_connectivities", "Field_ancillaries", ), ) @@ -335,7 +337,11 @@ def __init__( self.key_to_identity = {} self.all_field_anc_identities = set() - self.all_domain_anc_identities = set() + # self.all_domain_anc_identities = set() + self.all_identities = { + "domain_ancillary": set(), + "field_ancillary": set(), + } self.message = "" self.info = info @@ -372,7 +378,7 @@ def __init__( # Parent field or domain # ------------------------------------------------------------ self.field = f - self.has_data = f.has_data() + self.has_field_data = f.has_data() self.identity = f.identity( strict=strict_identities, relaxed=relaxed_identities and not ncvar_identities, @@ -395,7 +401,7 @@ def __init__( return if self.identity is None: - if not allow_no_identity and self.has_data: + if not allow_no_identity and self.has_field_data: if info: self.message = ( "no identity; consider setting relaxed_identities=True" @@ -665,8 +671,11 @@ def __init__( "field_ancillary", todict=True ) for key, field_anc in field_ancs.items(): + if not self.has_data(field_anc): + return + # Find this field ancillary's identity - identity = self.field_ancillary_has_identity_and_data(field_anc) + identity = self.get_identity(field_anc) if identity is None: return @@ -722,11 +731,13 @@ def __init__( if anc is None: continue + if not self.has_data(anc): + return + # Set this domain ancillary's identity - identity = (ref_identity, term) - identity = self.domain_ancillary_has_identity_and_data( - anc, identity - ) + identity = self.get_identity(anc, (ref_identity, term)) + if identity is None: + return # Find the canonical units units = self.canonical_units( @@ -758,6 +769,9 @@ def __init__( if key in ancs_in_refs: continue + if not self.has_data(anc): + return + # Find this domain ancillary's identity identity = self.domain_ancillary_has_identity_and_data(anc) if identity is None: @@ -791,12 +805,11 @@ def __init__( self.msr = {} info_msr = {} for key, msr in f.cell_measures(todict=True).items(): - if not self.cell_measure_has_measure(msr): + if not self.has_measure(msr): return - if ( - not msr.nc_get_external() - and not self.cell_measure_has_data_and_units(msr) + if not msr.nc_get_external() and not ( + self.has_data(msr) and self.has_units(msr) ): return @@ -859,6 +872,111 @@ def __init__( "external": tuple([v["external"] for v in value]), } + # ------------------------------------------------------------ + # Domain topologies + # ------------------------------------------------------------ + self.domain_topology = {} + info_topology = {} + for key, topology in f.domain_topologies(todict=True).items(): + if not (self.has_cell(topology) and self.has_data(topology)): + return + + # Find axes' identities + axes = tuple( + [self.axis_to_id[axis] for axis in construct_axes[key]] + ) + + identity = topology.get_cell() + + # Find the canonical axes + canonical_axes = self.canonical_axes(topology, identity, axes) + canonical_axis = canonical_axes[0] + + if canonical_axis in info_topology: + # Check for ambiguous domain topologies, i.e. those + # which span the same axis. + if info: + self.message = f"duplicate {topology!r}" + + return + else: + info_topology[canonical_axis] = [] + + info_topology[canonical_axes[0]].append( + { + "cell": identity, + "key": key, + "axes": axes, + "canonical_axes": canonical_axes, + } + ) + + # For each domain topology's canonical axis, sort the + # information by axis identities. + for units, value in info_topology.items(): + self.domain_topology[canonical_axis] = { + "cell": tuple([v["cell"] for v in value]), + "keys": tuple([v["key"] for v in value]), + "axes": tuple([v["axes"] for v in value]), + "canonical_axes": tuple([v["canonical_axes"] for v in value]), + } + + # ------------------------------------------------------------ + # Cell connectivities + # ------------------------------------------------------------ + self.cell_connectivity = {} + info_connectivity = {} + for key, connectivity in f.cell_connectivities(todict=True).items(): + if not ( + self.has_connectivity(connectivity) + and self.has_data(connectivity) + ): + return + + # Find axes' identities + axes = tuple( + [self.axis_to_id[axis] for axis in construct_axes[key]] + ) + + identity = connectivity.get_connectivity() + + # Find the canonical axes + canonical_axes = self.canonical_axes(connectivity, identity, axes) + canonical_axis = canonical_axes[0] + + if canonical_axis in info_connectivity: + # Check for ambiguous cell connectivities, i.e. those + # which span the same axis with the same connectivity + # type. + for value in info_connectivity[canonical_axis]: + if identity == value["connectivity"]: + if info: + self.message = f"duplicate {connectivity!r}" + + return + else: + info_connectivity[canonical_axis] = [] + + info_connectivity[canonical_axes[0]].append( + { + "connectivity": identity, + "key": key, + "axes": axes, + "canonical_axes": canonical_axes, + } + ) + + # For each cell connectivity's canonical axis, sort the + # information by cell type. + for axis, value in info_connectivity.items(): + value.sort(key=itemgetter("connectivity")) + self.cell_connectivity[axis] = { + "connectivity": tuple([v["connectivity"] for v in value]), + "keys": tuple([v["key"] for v in value]), + "axes": tuple([v["axes"] for v in value]), + "canonical_axes": tuple([v["canonical_axes"] for v in value]), + } + # ------------------------------------------------------------ # Properties and attributes # ------------------------------------------------------------ @@ -1303,48 +1421,113 @@ def canonical_cell_methods(self, rtol=None, atol=None): return cms - def cell_measure_has_data_and_units(self, msr): - """True only if a cell measure has both data and units. + def has_cell(self, topology): + """Whether a domain topology construct has a connectivity type. + + .. versionadded:: 3.16.0 :Parameters: - msr: `CellMeasure` + topology: `DomainTopology` + The construct to test. :Returns: `bool` + `True` if the construct has a cell type, otherwise + `False`. """ - if not msr.Units: + if topology.get_cell(None) is None: if self.info: - self.message = f"{msr.identity()!r} cell measure has no units" + self.message = ( + f"{topology.identity()!r} " + "domain topology construct has no cell type" + ) return False - if not msr.has_data(): + return True + + def has_connectivity(self, connectivity): + """Whether a cell connectivity construct has a connectivity type. + + .. versionadded:: 3.16.0 + + :Parameters: + + connectivity: `CellConnectivity` + The construct to test. + + :Returns: + + `bool` + `True` if the construct has a connectivity type, + otherwise `False`. + + """ + if connectivity.get_connectivity(None) is None: if self.info: - self.message = f"{msr.identity()!r} cell measure has no data" + self.message = ( + f"{connectivity.identity()!r} " + "cell connectivity construct has no connectivity type" + ) return False return True - def cell_measure_has_measure(self, msr): - """True only if a cell measure has a measure. + def has_measure(self, msr): + """Whether a cell measure construct has a measure. + + .. versionadded:: 3.16.0 :Parameters: msr: `CellMeasure` + The construct to test. :Returns: `bool` + `True` if the construct has a measure, otherwise + `False`. """ - if not msr.get_measure(False): + if msr.get_measure(None) is None: if self.info: self.message = ( - f"{msr.identity()!r} cell measure has no measure" + f"{msr.identity()!r} " + "cell measure construct has no measure" + ) + + return False + + return True + + def has_units(self, construct): + """Whether a construct has units. + + .. versionadded:: 3.16.0 + + :Parameters: + + construct: Construct + The construct to test. + + :Returns: + + `bool` + `True` if the construct has units, otherwise `False`. + + """ + if not construct.Units: + if self.info: + construct_type = construct.construct_type + self.message = ( + f"{construct.identity()!r} " + f"{construct_type.replace('_', ' ')} construct " + "has no units" ) return False @@ -1366,8 +1549,8 @@ def coord_has_identity_and_data(self, coord, axes=None): :Returns: `str` or `None` - The coordinate construct's identity, or `None` if there is - no identity and/or no data. + The coordinate construct's identity, or `None` if + there is no identity and/or no data. """ identity = coord.identity( @@ -1396,47 +1579,33 @@ def coord_has_identity_and_data(self, coord, axes=None): if self.info: self.message = f"{coord!r} has no identity or no data" - def field_ancillary_has_identity_and_data(self, anc): - """Return a field ancillary's identity if it has one and has - data. + def has_data(self, construct): + """Whether a construct has data. + + .. versionadded:: 3.16.0 :Parameters: - coord: `FieldAncillary` + construct: Construct + The construct to test. :Returns: - `str` or `None` - The coordinate construct's identity, or `None` if - there is no identity and/or no data. + `bool` + `True` if the construct has data, otherwise `False`. """ - identity = anc.identity( - strict=self.strict_identities, - relaxed=self.relaxed_identities and not self.ncvar_identities, - nc_only=self.ncvar_identities, - default=None, - ) - - if identity is not None: - all_field_anc_identities = self.all_field_anc_identities - - if identity in all_field_anc_identities: - if self.info: - self.message = f"multiple {identity!r} field ancillaries" - - return + if not construct.has_data(): + if self.info: + construct_type = construct.construct_type + self.message = ( + f"{construct.identity()!r} " + f"{construct_type.replace('_', ' ')} has no data" + ) - if anc.has_data(): - all_field_anc_identities.add(identity) - return identity + return False - # Still here? - if self.info: - self.message = ( - f"{anc.identity()!r} field ancillary has no identity or " - "no data" - ) + return True def coordinate_reference_signatures(self, refs): """List the structural signatures of given coordinate @@ -1477,61 +1646,58 @@ def coordinate_reference_signatures(self, refs): return signatures - def domain_ancillary_has_identity_and_data(self, anc, identity=None): - """Return a domain ancillary's identity if it has one and has - data. + def get_identity(self, construct, identity=None): + """Return a construct's identity if it has one. + + .. versionadded:: 3.16.0 :Parameters: - anc: cf.DomainAncillary + construct: Construct + The construct to test. identity: optional :Returns: `str` or `None` - The domain ancillary identity, or None if there is no - identity and/or no data. + The construct identity, or `None` if there isn't one. """ if identity is not None: - anc_identity = identity + construct_identity = identity else: - anc_identity = anc.identity( + construct_identity = construct.identity( strict=self.strict_identities, relaxed=self.relaxed_identities and not self.ncvar_identities, nc_only=self.ncvar_identities, default=None, ) - if anc_identity is None: + construct_type = construct.construct_type + if construct_identity is None: if self.info: self.message = ( - f"{anc.identity()!r} domain ancillary has no identity" + f"{construct.identity()!r} " + f"{construct_type.replace('_', ' ')} has no identity" ) return - all_domain_anc_identities = self.all_domain_anc_identities - - if anc_identity in all_domain_anc_identities: - if self.info: - self.message = ( - f"multiple {anc.identity()!r} domain ancillaries" - ) - return - - if not anc.has_data(): - if self.info: - self.message = ( - f"{anc.identity()!r} domain ancillary has no data" - ) + all_identities = self.all_identities.get(construct_type) + if all_identities is not None: + if construct_identity in all_identities: + if self.info: + self.message = ( + f"multiple {construct.identity()!r} " + f"{construct_type.replace('_', ' ')} constructs" + ) - return + return - all_domain_anc_identities.add(anc_identity) + all_identities.add(construct_identity) - return anc_identity + return construct_identity @_manage_log_level_via_verbose_attr def print_info(self, signature=True): @@ -1605,16 +1771,11 @@ def structural_signature(self): Units = self.units.units Cell_methods = self.cell_methods - Data = self.has_data - # signature_append = signature.append + Data = self.has_field_data # Properties - # signature_append(('Properties', self.properties)) Properties = self.properties - # standard_error_multiplier - # signature_append(('standard_error_multiplier', - # f.get_property('standard_error_multiplier', None))) standard_error_multiplier = f.get_property( "standard_error_multiplier", None ) @@ -1715,6 +1876,30 @@ def structural_signature(self): ] Domain_ancillaries = tuple(x) + # Domain topologies + topology = self.domain_topology + x = [ + ( + identity, + ("cell", topology[identity]["cell"]), + ("axes", topology[identity]["canonical_axes"]), + ) + for identity in sorted(topology) + ] + Domain_topologies = tuple(x) + + # Cell connectivities + connectivity = self.cell_connectivity + x = [ + ( + identity, + ("connectivity", connectivity[identity]["connectivity"]), + ("axes", connectivity[identity]["canonical_axes"]), + ) + for identity in sorted(connectivity) + ] + Cell_connectivities = tuple(x) + # Field ancillaries field_anc = self.field_anc x = [ @@ -1747,6 +1932,8 @@ def structural_signature(self): Nd_coordinates=Nd_coordinates, Cell_measures=Cell_measures, Domain_ancillaries=Domain_ancillaries, + Domain_topologies=Domain_topologies, + Cell_connectivities=Cell_connectivities, Field_ancillaries=Field_ancillaries, ) @@ -2913,7 +3100,7 @@ def aggregate( # ] # }, # 'auxiliary_coordinate': {}, - # 'cell_interface': {}, + # 'cell_connectivity': {}, # 'cell_measure': {}, # 'domain_ancillary': {}, # 'domain_topology': {}, @@ -2940,8 +3127,8 @@ def aggregate( "cell_measure": {}, "domain_ancillary": {}, "field_ancillary": {}, - "domain_topology": {}, # UGRID - "cell_interface": {}, # UGRID + "domain_topology": {}, + "cell_connectivity": {}, } m0 = m[0].copy() @@ -3365,12 +3552,16 @@ def _create_hash_and_first_values( sort_indices = slice(None, None, -1) else: sort_indices = slice(None) - + elif identity in m.domain_topology: + # ... or which doesn't have a dimension coordinate but + # does have a domain topology ... + sort_indices = slice(None) + needs_sorting = False else: # ... or which doesn't have a dimension coordinate but # does have one or more 1-d auxiliary coordinates aux = m_axis_identity["keys"][0] - # '.data.compute()' is faster than '.array' + # Note: '.data.compute()' is faster than '.array' sort_indices = np.argsort(constructs[aux].data.compute()) m_sort_keys[axis] = aux needs_sorting = True @@ -3541,6 +3732,76 @@ def _create_hash_and_first_values( msr["hash_values"] = hash_values + # ------------------------------------------------------------ + # Domain topologies + # ------------------------------------------------------------ + if donotchecknonaggregatingaxes: + for topology in m.domain_topology.values(): + topology["hash_values"] = [(None,) * len(topology["keys"])] + else: + for topology in m.domain_topology.values(): + hash_values = [] + for key, canonical_axes in zip( + topology["keys"], topology["canonical_axes"] + ): + construct = constructs[key] + sort_indices, needs_sorting = _sort_indices( + m, canonical_axes + ) + + # Get the hash of the data array + h = _get_hfl( + construct, + _no_units, + sort_indices, + needs_sorting, + False, + False, + hfl_cache, + rtol, + atol, + ) + + hash_values.append((h,)) + + topology["hash_values"] = hash_values + + # ------------------------------------------------------------ + # Cell connectivities + # ------------------------------------------------------------ + if donotchecknonaggregatingaxes: + for connectivity in m.cell_connectivity.values(): + connectivity["hash_values"] = [ + (None,) * len(connectivity["keys"]) + ] + else: + for connectivity in m.cell_connectivity.values(): + hash_values = [] + for key, canonical_axes in zip( + connectivity["keys"], connectivity["canonical_axes"] + ): + construct = constructs[key] + sort_indices, needs_sorting = _sort_indices( + m, canonical_axes + ) + + # Get the hash of the data array + h = _get_hfl( + construct, + _no_units, + sort_indices, + needs_sorting, + False, + False, + hfl_cache, + rtol, + atol, + ) + + hash_values.append((h,)) + + connectivity["hash_values"] = hash_values + # ------------------------------------------------------------ # Field ancillaries # ------------------------------------------------------------ @@ -3921,6 +4182,42 @@ def _hash_values(m): groups_of_fields.append([m1]) continue + # Still here? Then check the domain topologies + topology = m0.domain_topology + for axis in topology: + for axes, hash_value0, hash_value1 in zip( + topology[axis]["axes"], + topology[axis]["hash_values"], + m1.domain_topology[axis]["hash_values"], + ): + if a_identity not in axes and hash_value0 != hash_value1: + # There is a matching pair of domain + # topologies that does not span the + # aggregating axis and they have different + # data array values + ok = False + break + + # Still here? Then check the cell connectivities + connectivity = m0.cell_connectivity + for axis in connectivity: + for axes, hash_value0, hash_value1 in zip( + connectivity[axis]["axes"], + connectivity[axis]["hash_values"], + m1.cell_connectivity[axis]["hash_values"], + ): + if a_identity not in axes and hash_value0 != hash_value1: + # There is a matching pair of cell + # connectivities that does not span the + # aggregating axis and they have different + # data array values + ok = False + break + + if not ok: + groups_of_fields.append([m1]) + continue + # Still here? Then set the identity of the aggregating # axis m0.a_identity = a_identity @@ -4137,39 +4434,24 @@ def _ok_coordinate_arrays( f"contiguous={bool(contiguous)} and " f"{m.axis[axis]['ids'][dim_coord_index]!r} " "dimension coordinate cells do not match " - "the cell spacing condition between fields " - f"({data1!r} - {data0!r} = {dim_diff!r}) " + "the cell spacing condition between fields: " + f"{data1!r} - {data0!r} = {dim_diff!r} " f"!= {diff!r}" ) return False else: # ------------------------------------------------------------ - # The aggregating axis does not have a dimension coordinate, - # but it does have at least one 1-d auxiliary coordinate. + # The aggregating axis does not have a dimension coordinate # ------------------------------------------------------------ - # Check for duplicate auxiliary coordinate values - for i, identity in enumerate(meta[0].axis[axis]["ids"]): - set_of_1d_aux_coord_values = set() - number_of_1d_aux_coord_values = 0 - for m in meta: - aux = m.axis[axis]["keys"][i] - # '.data.compute()' is faster than '.array' - array = m.field.constructs[aux].data.compute() - set_of_1d_aux_coord_values.update(array) - number_of_1d_aux_coord_values += array.size - if ( - len(set_of_1d_aux_coord_values) - != number_of_1d_aux_coord_values - ): - if info: - meta[0].message = ( - f"no {identity!r} dimension coordinates and " - f"{identity!r} auxiliary coordinates have " - "duplicate values" - ) + if axis in m.domain_topology or axis in m.cell_connectivity: + if info: + meta[0].message = ( + f"can't aggregate along the {axis!r} mesh topology " + "discrete axis" + ) - return False + return False # ---------------------------------------------------------------- # Still here? Then the aggregating axis does not overlap between @@ -4429,7 +4711,7 @@ def _aggregate_2_fields( # Update the size of the aggregating axis in the output parent # construct # ---------------------------------------------------------------- - if m0.has_data: + if m0.has_field_data: # ---------------------------------------------------------------- # Insert the data array from parent1 into the data array of # parent0 diff --git a/cf/auxiliarycoordinate.py b/cf/auxiliarycoordinate.py index fba4082529..46c28f0879 100644 --- a/cf/auxiliarycoordinate.py +++ b/cf/auxiliarycoordinate.py @@ -36,9 +36,7 @@ class AuxiliaryCoordinate( {{netCDF variable}} - The netCDF variable group structure may be accessed with the - `nc_set_variable`, `nc_get_variable`, `nc_variable_groups`, - `nc_clear_variable_groups` and `nc_set_variable_groups` methods. + {{netCDF UGRID node coordinate}} """ @@ -47,11 +45,3 @@ def __new__(cls, *args, **kwargs): instance = super().__new__(cls) instance._Bounds = Bounds return instance - - def __repr__(self): - """Called by the `repr` built-in function. - - x.__repr__() <==> repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " >>> i.classes() {'AuxiliaryCoordinate': cf.auxiliarycoordinate.AuxiliaryCoordinate, + 'BoundsFromNodesArray': cf.data.array.boundsfromnodesarray.BoundsFromNodesArray, + 'CellConnectivity': cf.cellconnectivity.CellConnectivity, + 'CellConnectivityArray': cf.data.array.cellconnectivityarray.CellConnectivityArray, 'CellMeasure': cf.cellmeasure.CellMeasure, 'CellMethod': cf.cellmethod.CellMethod, 'CFANetCDFArray': cf.data.array.cfanetcdfarray.CFANetCDFArray, @@ -202,6 +215,7 @@ def implementation(): 'Domain': cf.domain.Domain, 'DomainAncillary': cf.domainancillary.DomainAncillary, 'DomainAxis': cf.domainaxis.DomainAxis, + 'DomainTopology': cf.domaintopology.DomainTopology, 'Field': cf.field.Field, 'FieldAncillary': cf.fieldancillary.FieldAncillary, 'Bounds': cf.bounds.Bounds, @@ -217,6 +231,7 @@ def implementation(): 'Data': cf.data.data.Data, 'GatheredArray': cf.data.array.gatheredarray.GatheredArray, 'NetCDFArray': cf.data.array.netcdfarray.NetCDFArray, + 'PointTopologyArray': , 'RaggedContiguousArray': cf.data.array.raggedcontiguousarray.RaggedContiguousArray, 'RaggedIndexedArray': cf.data.array.raggedindexedarray.RaggedIndexedArray, 'RaggedIndexedContiguousArray': cf.data.array.raggedindexedcontiguousarray.RaggedIndexedContiguousArray, diff --git a/cf/constructs.py b/cf/constructs.py index 95d068f18d..29dbbc9dfc 100644 --- a/cf/constructs.py +++ b/cf/constructs.py @@ -47,6 +47,8 @@ class Constructs(cfdm.Constructs): * dimension coordinate constructs * domain ancillary constructs * domain axis constructs + * domain topology constructs + * cell connectivity constructs * cell method constructs * field ancillary constructs @@ -126,9 +128,6 @@ def _flip(self, axes): ] construct.flip(iaxes, inplace=True) - # ---------------------------------------------------------------- - # Methods - # ---------------------------------------------------------------- def close(self): """Close all files referenced by the metadata constructs. diff --git a/cf/count.py b/cf/count.py index c2db119324..090c14b6b3 100644 --- a/cf/count.py +++ b/cf/count.py @@ -4,42 +4,4 @@ class Count(mixin.PropertiesData, cfdm.Count): - """A count variable required to uncompress a ragged array. - - A collection of features stored using a contiguous ragged array - combines all features along a single dimension (the sample - dimension) such that each feature in the collection occupies a - contiguous block. - - The information needed to uncompress the data is stored in a count - variable that gives the size of each block. - - **NetCDF interface** - - The netCDF variable name of the count variable may be accessed - with the `nc_set_variable`, `nc_get_variable`, `nc_del_variable` - and `nc_has_variable` methods. - - The name of the netCDF dimension spanned by the count variable's - data may be accessed with the `nc_set_dimension`, - `nc_get_dimension`, `nc_del_dimension` and `nc_has_dimension` - methods. - - The name of the netCDF sample dimension spanned by the compressed - data (that is stored in the "sample_dimension" netCDF attribute - and which does not correspond to a domain axis construct) may be - accessed with the `nc_set_sample_dimension`, - `nc_get_sample_dimension`, `nc_del_sample_dimension` and - `nc_has_sample_dimension` methods. - - .. versionadded:: 3.0.0 - - """ - - def __repr__(self): - """Called by the `repr` built-in function. - - x.__repr__() <==> repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - .. versionadded:: 3.0.0 - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - .. versionadded:: 3.0.0 - - """ - return super().__repr__().replace("<", " repr(x) - - .. versionadded:: 3.0.0 - - """ - return super().__repr__().replace("<", " repr(x) - - .. versionadded:: 3.0.0 - - """ - return super().__repr__().replace("<", " repr(x) - - .. versionadded:: 3.0.0 - - """ - return super().__repr__().replace("<", "= + masked in the destination grid definition; or b) ``w_ji >= min_weight`` for those masked source grid cells i for which ``w_ji > 0``. **Conservative first-order regridding** Destination grid cell j will only be masked if a) it is - masked in destination grid definition; or b) The sum of - ``w_ji`` for all non-masked source grid cells i is + masked in the destination grid definition; or b) the sum + of ``w_ji`` for all non-masked source grid cells i is strictly less than *min_weight*. :Returns: @@ -168,8 +180,9 @@ def regrid( # are the gathered regridding axes and whose left-hand dimension # represent of all the other dimensions. # ---------------------------------------------------------------- + n_src_axes = len(src_shape) a = a.transpose(axis_order) - non_regrid_shape = a.shape[: a.ndim - len(src_shape)] + non_regrid_shape = a.shape[: a.ndim - n_src_axes] dst_size, src_size = weights.shape a = a.reshape(-1, src_size) a = a.T @@ -200,7 +213,7 @@ def regrid( if variable_mask or (src_mask is None and ref_src_mask.any()): raise ValueError( f"Can't regrid with the {method!r} method when the source " - f"data mask varies over different {len(src_shape)}-d " + f"data mask varies over different {n_src_axes}-d " "regridding slices" ) @@ -279,10 +292,40 @@ def regrid( a = a.T a = a.reshape(non_regrid_shape + tuple(dst_shape)) + n_dst_axes = len(dst_shape) + + if n_src_axes == n_dst_axes: + pass + elif n_src_axes == 1 and n_dst_axes == 2: + # The regridding operation increased the number of data axes + # by 1 => modify 'axis_order' to contain the new axis. + # + # E.g. UGRID -> regular lat-lon could change 'axis_order' from + # [0,2,1] to [0,3,1,2] + raxis = axis_order[-1] + axis_order = [ + i if i <= raxis else i + n_dst_axes - 1 for i in axis_order + ] + axis_order[-1:] = range(raxis, raxis + n_dst_axes) + elif n_src_axes == 2 and n_dst_axes == 1: + # The regridding operation decreased the number of data axes + # by 1 => modify 'axis_order' to remove the removed axis. + # + # E.g. regular lat-lon -> UGRID could change 'axis_order' from + # [0,2,4,5,1,3] to [0,2,3,4,1], or [0,2,4,5,3,1] to + # [0,1,3,4,2] + raxis0, raxis = axis_order[-2:] + axis_order = [i if i <= raxis else i - 1 for i in axis_order[:-1]] + else: + raise ValueError( + f"Can't (yet) regrid from {n_src_axes} dimensions to " + f"{n_dst_axes} dimensions" + ) + d = {k: i for i, k in enumerate(axis_order)} axis_reorder = [i for k, i in sorted(d.items())] - a = a.transpose(axis_reorder) + a = a.transpose(axis_reorder) return a @@ -421,10 +464,10 @@ def _regrid( # destination cell j that intersects with unmasked cells # of the source grid. # - # D_j = 1 - w_i1j - ... - w_iNj + # D_j = w_i1j + ... + w_iNj # - # where w_iXj is the unmasked weight for masked source - # cell i and destination cell j. + # where each w_iXj is the weight for unmasked source cell + # i and destination cell j. dst_size = weights.shape[0] if dst_mask is None: dst_mask = np.zeros((dst_size,), dtype=bool) @@ -451,7 +494,7 @@ def _regrid( continue w = data[i0:i1] - D_j = 1 - w[mask].sum() + D_j = w[~mask].sum() w = w / D_j w[mask] = 0 data[i0:i1] = w diff --git a/cf/data/dask_utils.py b/cf/data/dask_utils.py index fafbf82501..63291a4d96 100644 --- a/cf/data/dask_utils.py +++ b/cf/data/dask_utils.py @@ -9,6 +9,7 @@ import dask.array as da import numpy as np from dask.core import flatten +from scipy.ndimage import convolve1d from ..cfdatetime import dt, dt2rt, rt2dt from ..functions import atol as cf_atol @@ -128,12 +129,6 @@ def cf_contains(a, value): return np.array(value in a).reshape((1,) * a.ndim) -try: - from scipy.ndimage import convolve1d -except ImportError: - pass - - def cf_convolve1d(a, window=None, axis=-1, origin=0): """Calculate a 1-d convolution along the given axis. diff --git a/cf/data/data.py b/cf/data/data.py index 840be8c204..1b47fa2c5c 100644 --- a/cf/data/data.py +++ b/cf/data/data.py @@ -18,6 +18,7 @@ from dask.base import collections_to_dsk, is_dask_collection, tokenize from dask.highlevelgraph import HighLevelGraph from dask.optimization import cull +from scipy.sparse import issparse from ..cfdatetime import dt as cf_dt from ..constants import masked as cf_masked @@ -178,6 +179,7 @@ def __init__( copy=True, dtype=None, mask=None, + mask_value=None, to_memory=False, init_options=None, _use_array=True, @@ -266,6 +268,12 @@ def __init__( .. versionadded:: 3.0.5 + mask_value: scalar array_like + Mask *array* where it is equal to *mask_value*, using + numerically tolerant floating point equality. + + .. versionadded:: 3.16.0 + {{init source: optional}} hardmask: `bool`, optional @@ -392,6 +400,8 @@ def __init__( # No data has been set return + sparse_array = issparse(array) + try: ndim = array.ndim except AttributeError: @@ -482,8 +492,18 @@ def __init__( # Apply a mask if mask is not None: + if sparse_array: + raise ValueError("Can't mask sparse array") + self.where(mask, cf_masked, inplace=True) + # Apply masked values + if mask_value is not None: + if sparse_array: + raise ValueError("Can't mask sparse array") + + self.masked_values(mask_value, inplace=True) + @property def dask_compressed_array(self): """Returns a dask array of the compressed data. @@ -766,14 +786,6 @@ def __bool__(self): return bool(self.to_dask_array()) - def __repr__(self): - """Called by the `repr` built-in function. - - x.__repr__() <==> repr(x) - - """ - return super().__repr__().replace("<", ">> d.compute() array([1., 2., 3.]) + >>> from scipy.sparse import csr_array + >>> d = cf.Data(csr_array((2, 3))) + >>> d.compute() + <2x3 sparse array of type '' + with 0 stored elements in Compressed Sparse Row format> + >>>: d.array + array([[0., 0., 0.], + [0., 0., 0.]]) + >>> d.compute().toarray() + array([[0., 0., 0.], + [0., 0., 0.]]) + """ a = self.to_dask_array().compute() @@ -3734,6 +3760,7 @@ def _regrid( from .dask_regrid import regrid, regrid_weights shape = self.shape + ndim = self.ndim src_shape = tuple(shape[i] for i in regrid_axes) if src_shape != operator.src_shape: raise ValueError( @@ -3753,11 +3780,44 @@ def _regrid( ] dx = dx.rechunk(chunks) - # Define the regridded chunksizes - regridded_chunks = tuple( - (regridded_sizes[i],) if i in regridded_sizes else c - for i, c in enumerate(dx.chunks) - ) + # Define the regridded chunksizes (allowing for the regridded + # data to have more/fewer axes). + regridded_chunks = [] # The 'chunks' parameter to `map_blocks` + drop_axis = [] # The 'drop_axis' parameter to `map_blocks` + new_axis = [] # The 'new_axis' parameter to `map_blocks` + n = 0 + for i, c in enumerate(dx.chunks): + if i in regridded_sizes: + sizes = regridded_sizes[i] + n_sizes = len(sizes) + if not n_sizes: + drop_axis.append(i) + continue + + regridded_chunks.extend(sizes) + if n_sizes > 1: + new_axis.extend(range(n + 1, n + n_sizes)) + n += n_sizes - 1 + else: + regridded_chunks.extend(c) + + n += 1 + + # Update the axis identifiers. + # + # This is necessary when regridding changes the number of data + # dimensions (e.g. as happens when regridding a mesh topology + # axis to/from separate lat and lon axes). + if new_axis: + axes = list(self._axes) + for i in new_axis: + axes.insert(i, new_axis_identifier(tuple(axes))) + + self._axes = axes + elif drop_axis: + axes = self._axes + axes = [axes[i] for i in range(ndim) if i not in drop_axis] + self._axes = axes # Set the output data type if method in ("nearest_dtos", "nearest_stod"): @@ -3765,7 +3825,7 @@ def _regrid( else: dst_dtype = float - non_regrid_axes = [i for i in range(self.ndim) if i not in regrid_axes] + non_regrid_axes = [i for i in range(ndim) if i not in regrid_axes] src_mask = operator.src_mask if src_mask is not None: @@ -3790,6 +3850,8 @@ def _regrid( weights_dst_mask=weights_dst_mask, ref_src_mask=src_mask, chunks=regridded_chunks, + drop_axis=drop_axis, + new_axis=new_axis, meta=np.array((), dtype=dst_dtype), ) @@ -5094,7 +5156,9 @@ def array(self): """ array = self.compute().copy() - if not isinstance(array, np.ndarray): + if issparse(array): + array = array.toarray() + elif not isinstance(array, np.ndarray): array = np.asanyarray(array) return array @@ -5205,7 +5269,6 @@ def mask(self): return mask_data_obj - # `arctan2`, AT2 seealso @_inplace_enabled(default=False) def arctan(self, inplace=False): """Take the trigonometric inverse tangent of the data element- @@ -5215,7 +5278,7 @@ def arctan(self, inplace=False): .. versionadded:: 3.0.7 - .. seealso:: `tan`, `arcsin`, `arccos`, `arctanh` + .. seealso:: `tan`, `arcsin`, `arccos`, `arctanh`, `arctan2` :Parameters: @@ -5254,47 +5317,6 @@ def arctan(self, inplace=False): return d - # AT2 - # - # @classmethod - # def arctan2(cls, y, x): - # '''Take the "two-argument" trigonometric inverse tangent - # element-wise for `y`/`x`. - # - # Explicitly this returns, for all corresponding elements, the angle - # between the positive `x` axis and the line to the point (`x`, `y`), - # where the signs of both `x` and `y` are taken into account to - # determine the quadrant. Such knowledge of the signs of `x` and `y` - # are lost when the quotient is input to the standard "one-argument" - # `arctan` function, such that use of `arctan` leaves the quadrant - # ambiguous. `arctan2` may therefore be preferred. - # - # Units are ignored in the calculation. The result has units of radians. - # - # .. versionadded:: 3.2.0 - # - # .. seealso:: `arctan`, `tan` - # - # :Parameters: - # - # y: `Data` - # The data array to provide the numerator elements, corresponding - # to the `y` coordinates in the `arctan2` definition. - # - # x: `Data` - # The data array to provide the denominator elements, - # corresponding to the `x` coordinates in the `arctan2` - # definition. - # - # :Returns: - # - # `Data` - # - # **Examples** - # - # ''' - # return cls(np.arctan2(y, x), units=_units_radians) - @_inplace_enabled(default=False) def arctanh(self, inplace=False): """Take the inverse hyperbolic tangent of the data element-wise. @@ -7334,6 +7356,96 @@ def asdata(cls, d, dtype=None, copy=False): return data + @classmethod + def arctan2(cls, x1, x2): + """Element-wise arc tangent of ``x1/x2`` with correct quadrant. + + The quadrant (i.e. branch) is chosen so that ``arctan2(y, x)`` + is the signed angle in radians between the ray ending at the + origin and passing through the point ``(1, 0)``, and the ray + ending at the origin and passing through the point ``(x, y)``. + (Note the role reversal: the "y-coordinate" is the first + function parameter, the "x-coordinate" is the second.) By IEEE + convention, this function is defined for ``x = +/-0`` and for + either or both of ``y = +/-inf`` and ``x = +/-inf`` (see Notes + for specific values). + + `arctan2` is identical to the ``atan2`` function of the + underlying C library. The following special values are defined + in the C standard: + + ====== ====== =================== + *x1* *x2* ``arctan2(x1, x2)`` + ====== ====== =================== + +/- 0 +0 +/- 0 + +/- 0 -0 +/- pi + > 0 +/-inf +0 / +pi + < 0 +/-inf -0 / -pi + +/-inf +inf +/- (pi/4) + +/-inf -inf +/- (3*pi/4) + ====== ====== =================== + + Note that ``+0`` and ``-0`` are distinct floating point + numbers, as are ``+inf`` and ``-inf``. + + .. versionadded:: 3.16.0 + + .. seealso:: `arctan`, `tan` + + :Parameters: + + x1: array_like + Y coordinates. + + x2: array_like + X coordinates. *x1* and *x2* must be broadcastable to + a common shape (which becomes the shape of the + output). + + :Returns: + + `Data` + Array of angles in radians, in the range ``(-pi, + pi]``. + + **Examples** + + >>> import numpy as np + >>> x = cf.Data([-1, +1, +1, -1]) + >>> y = cf.Data([-1, -1, +1, +1]) + >>> print((cf.Data.arctan2(y, x) * 180 / np.pi).array) + [-135.0 -45.0 45.0 135.0] + >>> x[1] = cf.masked + >>> y[1] = cf.masked + >>> print((cf.Data.arctan2(y, x) * 180 / np.pi).array) + [-135.0 -- 45.0 135.0] + + >>> print(cf.Data.arctan2([0, 0, np.inf], [+0., -0., np.inf]).array) + [0.0 3.141592653589793 0.7853981633974483] + + >>> print((cf.Data.arctan2([1, -1], [0, 0]) * 180 / np.pi).array) + [90.0 -90.0] + + """ + try: + y = x1.to_dask_array() + except AttributeError: + y = cls.asdata(x1).to_dask_array() + + try: + x = x2.to_dask_array() + except AttributeError: + x = cls.asdata(x2).to_dask_array() + + mask = da.ma.getmaskarray(y) | da.ma.getmaskarray(x) + y = da.ma.filled(y, 1) + x = da.ma.filled(x, 1) + + dx = da.arctan2(y, x) + dx = da.ma.masked_array(dx, mask=mask) + + return cls(dx, units=_units_radians) + @_inplace_enabled(default=False) def compressed(self, inplace=False): """Return all non-masked values in a one dimensional data array. @@ -7761,6 +7873,45 @@ def second(self): """ return YMDhms(self, "second") + @property + def sparse_array(self): + """Return an independent `scipy` sparse array of the data. + + In-place changes to the returned sparse array do not affect + the underlying dask array. + + An `AttributeError` is raised if a sparse array representation + is not available. + + **Performance** + + `sparse_array` causes all delayed operations to be + computed. The returned sparse array is a deep copy of that + returned by created `compute`. + + .. versionadded:: 3.16.0 + + .. seealso:: `array` + + :Returns: + + An independent `scipy` sparse array of the data. + + **Examples** + + >>> from scipy.sparse import issparse + >>> issparse(d.sparse_array) + True + + """ + array = self.compute() + if issparse(array): + return array.copy() + + raise AttributeError( + "A sparse array representation of the data is not available" + ) + @_inplace_enabled(default=False) def uncompress(self, inplace=False): """Uncompress the data. @@ -9871,6 +10022,60 @@ def masked_all( d._set_dask(dx) return d + @_inplace_enabled(default=False) + def masked_values(self, value, rtol=None, atol=None, inplace=False): + """Mask using floating point equality. + + Masks the data where elements are approximately equal to the + given value. For integer types, exact equality is used. + + .. versionadded:: 3.16.0 + + .. seealso:: `mask` + + :Parameters: + + value: number + Masking value. + + {{rtol: number, optional}} + + {{atol: number, optional}} + + {{inplace: `bool`, optional}} + + :Returns: + + `{{class}}` or `None` + The result of masking the data where approximately + equal to *value*, or `None` if the operation was + in-place. + + **Examples** + + >>> d = {{package}}.{{class}}([1, 1.1, 2, 1.1, 3]) + >>> e = d.masked_values(1.1) + >>> print(e.array) + [1.0 -- 2.0 -- 3.0] + + """ + d = _inplace_enabled_define_and_cleanup(self) + + if rtol is None: + rtol = self._rtol + else: + rtol = float(rtol) + + if atol is None: + atol = self._atol + else: + atol = float(atol) + + dx = d.to_dask_array() + dx = da.ma.masked_values(dx, value, rtol=rtol, atol=atol) + d._set_dask(dx) + return d + @_inplace_enabled(default=False) @_deprecated_kwarg_check("i", version="3.0.0", removed_at="4.0.0") def mid_range( @@ -11168,7 +11373,7 @@ def tanh(self, inplace=False): .. versionadded:: 3.1.0 - .. seealso:: `arctanh`, `sinh`, `cosh`, `tan` + .. seealso:: `arctanh`, `sinh`, `cosh`, `tan`, `arctan2` :Parameters: diff --git a/cf/data/utils.py b/cf/data/utils.py index 82fe619f8d..747e703cfe 100644 --- a/cf/data/utils.py +++ b/cf/data/utils.py @@ -999,3 +999,50 @@ def parse_weights(d, weights, axis=None): # Return the product of the weights components, which will be # broadcastable to d return reduce(mul, w) + + +def normalize_chunks(chunks, shape=None, dtype=None): + """Normalize chunks to tuple of tuples. + + The shape may contain sizes of ``nan``. This could occur when the + underlying data is compressed in a way which makes the shape + impossible to infer without actually uncompressing the data. + + If *shape* contains no ``nan`` sizes then this function is + identical to `dask.array.core.normalize_chunks`. If it does, then + the output chunks for each such axis will be ``(nan,)``. + + .. versionadded 3.16.0 + + :Parameters: + + chunks: tuple, int, dict, or string + The chunks to be normalized. See + `dask.array.core.normalize_chunks` for details. + + shape: `tuple` + The shape of the data. + + dtype: data-type + The data-type for the data. + + :Returns: + + `tuple` + The normalized chunks. + + """ + from math import isnan, nan + + from dask.array.core import normalize_chunks + + if not any(map(isnan, shape)): + return normalize_chunks(chunks, shape=shape, dtype=dtype) + + out = [ + (nan,) + if isnan(size) + else normalize_chunks(chunk, shape=(size,), dtype=dtype)[0] + for chunk, size in zip(chunks, shape) + ] + return tuple(out) diff --git a/cf/dimensioncoordinate.py b/cf/dimensioncoordinate.py index c88fec0f14..347a0d7dd5 100644 --- a/cf/dimensioncoordinate.py +++ b/cf/dimensioncoordinate.py @@ -110,14 +110,6 @@ def __init__( if chars is not None: self._set_component("cell_characteristics", chars, copy=False) - def __repr__(self): - """Called by the `repr` built-in function. - - x.__repr__() <==> repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", ">> h = f._conform_for_data_broadcasting(g) - - """ - - other = self._conform_for_assignment(other, check_coordinates=True) - - # Remove leading size one dimensions - ndiff = other.ndim - self.ndim - if ndiff > 0 and set(other.shape[:ndiff]) == set((1,)): - for i in range(ndiff): - other = other.squeeze(0) - - return other - - @_manage_log_level_via_verbosity - def _equivalent_construct_data( - self, - field1, - key0=None, - key1=None, - s=None, - t=None, - atol=None, - rtol=None, - verbose=None, - axis_map=None, - ): - """True if the field has equivalent construct data to another. - - Two real numbers ``x`` and ``y`` are considered equal if - ``|x-y|<=atol+rtol|y|``, where ``atol`` (the tolerance on absolute - differences) and ``rtol`` (the tolerance on relative differences) - are positive, typically very small numbers. See the *atol* and - *rtol* parameters. - - :Parameters: - - key0: `str` - - key1: `str` - - field1: `Field` - - s: `dict`, optional - - t: `dict`, optional - - atol: `float`, optional - The tolerance on absolute differences between real - numbers. The default value is set by the `atol` function. - - rtol: `float`, optional - The tolerance on relative differences between real - numbers. The default value is set by the `rtol` function. - - {{verbose: `int` or `str` or `None`, optional}} - - """ - item0 = self.constructs[key0] - item1 = field1.constructs[key1] - - if item0.has_data() != item1.has_data(): - if is_log_level_info(logger): - logger.info( - f"{self.__class__.__name__}: Only one item has data" - ) # pragma: no cover - - return False - - if not item0.has_data(): - # Neither field has a data array - return True - - if item0.size != item1.size: - if is_log_level_info(logger): - logger.info( - f"{self.__class__.__name__}: Different metadata construct " - f"data array size: {item0.size} != {item1.size}" - ) # pragma: no cover - - return False - - if item0.ndim != item1.ndim: - if is_log_level_info(logger): - logger.info( - f"{self.__class__.__name__}: Different data array ranks " - f"({item0.ndim}, {item1.ndim})" - ) # pragma: no cover - - return False - - axes0 = self.get_data_axes(key0, default=()) - axes1 = field1.get_data_axes(key1, default=()) - - if s is None: - s = self.analyse_items() - if t is None: - t = field1.analyse_items() - - transpose_axes = [] - if axis_map is None: - for axis0 in axes0: - axis1 = t["id_to_axis"].get(s["axis_to_id"][axis0], None) - if axis1 is None: - if is_log_level_info(logger): - # TODO: improve message here (make user friendly): - logger.info( - "t['id_to_axis'] does not have a key " - f"s['axis_to_id'][axis0] for " - f"{self.__class__.__name__}" - ) # pragma: no cover - - return False - - transpose_axes.append(axes1.index(axis1)) - else: - for axis0 in axes0: - axis1 = axis_map.get(axis0) - if axis1 is None: - if is_log_level_info(logger): - # TODO: improve message here (make user friendly): - logger.info( - f"axis_map[axis0] is None for {self.__class__.__name__}" - ) # pragma: no cover - - return False - - transpose_axes.append(axes1.index(axis1)) - - copy1 = True - - if transpose_axes != list(range(item1.ndim)): - if copy1: - item1 = item1.copy() - copy1 = False - - item1.transpose(transpose_axes, inplace=True) - - if item0.shape != item1.shape: - # add traceback TODO - return False - - flip_axes = [ - i - for i, (axis1, axis0) in enumerate(zip(axes1, axes0)) - if field1.direction(axis1) != self.direction(axis0) - ] - - if flip_axes: - if copy1: - item1 = item1.copy() - copy1 = False - - item1.flip(flip_axes, inplace=True) - - if not item0._equivalent_data( - item1, rtol=rtol, atol=atol, verbose=verbose - ): - # add traceback TODO - return False - - return True - - # ---------------------------------------------------------------- - # Worker functions for weights - # ---------------------------------------------------------------- - def _weights_area_XY( - self, - comp, - weights_axes, - auto=False, - measure=False, - radius=None, - methods=False, - ): - """Calculate area weights from X and Y dimension coordinate - constructs. - - :Parameters: - - measure: `bool` - If True then make sure that the weights represent true - cell sizes. - - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as opposed to - the actual weights. - - :Returns: - - `bool` or `None` - - """ - xkey, xcoord = self.dimension_coordinate( - "X", item=True, default=(None, None) - ) - ykey, ycoord = self.dimension_coordinate( - "Y", item=True, default=(None, None) - ) - - if xcoord is None or ycoord is None: - if auto: - return - - raise ValueError( - "No unique 'X' and 'Y' dimension coordinate constructs for " - "calculating area weights" - ) - - if xcoord.Units.equivalent( - Units("radians") - ) and ycoord.Units.equivalent(Units("radians")): - pass - elif xcoord.Units.equivalent( - Units("metres") - ) and ycoord.Units.equivalent(Units("metres")): - radius = None - else: - if auto: - return - - raise ValueError( - "Insufficient coordinate constructs for calculating " - "area weights" - ) - - xaxis = self.get_data_axes(xkey)[0] - yaxis = self.get_data_axes(ykey)[0] - - for axis in (xaxis, yaxis): - if axis in weights_axes: - if auto: - return - - raise ValueError( - "Multiple weights specifications for " - f"{self.constructs.domain_axis_identity(axis)!r} axis" - ) - - if measure and radius is not None: - radius = self.radius(default=radius) - - if measure or xcoord.size > 1: - if not xcoord.has_bounds(): - if auto: - return - - raise ValueError( - "Can't create area weights: No bounds for " - f"{xcoord.identity()!r} axis" - ) - - if methods: - comp[(xaxis,)] = "linear " + xcoord.identity() - else: - cells = xcoord.cellsize - if xcoord.Units.equivalent(Units("radians")): - cells.Units = _units_radians - if measure: - cells *= radius - cells.override_units(radius.Units, inplace=True) - else: - cells.Units = Units("metres") - - comp[(xaxis,)] = cells - - weights_axes.add(xaxis) - - if measure or ycoord.size > 1: - if not ycoord.has_bounds(): - if auto: - return - - raise ValueError( - "Can't create area weights: No bounds for " - f"{ycoord.identity()!r} axis" - ) - - if ycoord.Units.equivalent(Units("radians")): - ycoord = ycoord.clip(-90, 90, units=Units("degrees")) - ycoord.sin(inplace=True) - - if methods: - comp[(yaxis,)] = "linear sine " + ycoord.identity() - else: - cells = ycoord.cellsize - if measure: - cells *= radius - - comp[(yaxis,)] = cells - else: - if methods: - comp[(yaxis,)] = "linear " + ycoord.identity() - else: - cells = ycoord.cellsize - comp[(yaxis,)] = cells - - weights_axes.add(yaxis) - - return True - - def _weights_data( - self, - w, - comp, - weights_axes, - axes=None, - data=False, - components=False, - methods=False, - ): - """Creates weights for the field construct's data array. - - :Parameters: - - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as - opposed to the actual weights. - - """ - # -------------------------------------------------------- - # Data weights - # -------------------------------------------------------- - field_data_axes = self.get_data_axes() - - if axes is not None: - if isinstance(axes, (str, int)): - axes = (axes,) - - if len(axes) != w.ndim: - raise ValueError( - "'axes' parameter must provide an axis identifier " - f"for each weights data dimension. Got {axes!r} for " - f"{w.ndim} dimension(s)." - ) - - iaxes = [ - field_data_axes.index(self.domain_axis(axis, key=True)) - for axis in axes - ] - - if data: - for i in range(self.ndim): - if i not in iaxes: - w = w.insert_dimension(position=i) - iaxes.insert(i, i) - - w = w.transpose(iaxes) - - if w.ndim > 0: - while w.shape[0] == 1: - w = w.squeeze(0) - - else: - iaxes = list(range(self.ndim - w.ndim, self.ndim)) - - if not (components or methods): - if not self._is_broadcastable(w.shape): - raise ValueError( - f"The 'Data' weights (shape {w.shape}) are not " - "broadcastable to the field construct's data " - f"(shape {self.shape})." - ) - - axes0 = field_data_axes[self.ndim - w.ndim :] - else: - axes0 = [field_data_axes[i] for i in iaxes] - - for axis0 in axes0: - if axis0 in weights_axes: - raise ValueError( - "Multiple weights specified for " - f"{self.constructs.domain_axis_identity(axis0)!r} axis" - ) - - if methods: - comp[tuple(axes0)] = "custom data" - else: - comp[tuple(axes0)] = w - - weights_axes.update(axes0) - - return True - - def _weights_field(self, fields, comp, weights_axes, methods=False): - """Creates a weights field.""" - s = self.analyse_items() - - domain_axes = self.domain_axes(todict=True) - # domain_axes_size_1 = self.domain_axes(filter_by_size=(1,), todict=True) - - for w in fields: - t = w.analyse_items() - # TODO CHECK this with org - - if t["undefined_axes"]: - # if set( - # t.domain_axes.filter_by_size(gt(1), view=True) - # ).intersection(t["undefined_axes"]): - w_domain_axes_1 = w.domain_axes( - filter_by_size=(1,), todict=True - ) - if set(w_domain_axes_1).intersection(t["undefined_axes"]): - raise ValueError( - f"Weights field {w} has at least one undefined " - f"domain axes: {w_domain_axes_1}." - ) - - w = w.squeeze() - - w_domain_axes = w.domain_axes(todict=True) - - axis1_to_axis0 = {} - - coordinate_references = self.coordinate_references(todict=True) - w_coordinate_references = w.coordinate_references(todict=True) - - for axis1 in w.get_data_axes(): - identity = t["axis_to_id"].get(axis1, None) - if identity is None: - raise ValueError( - "Weights field has unmatched, size > 1 " - f"{w.constructs.domain_axis_identity(axis1)!r} axis" - ) - - axis0 = s["id_to_axis"].get(identity, None) - if axis0 is None: - raise ValueError( - f"Weights field has unmatched, size > 1 {identity!r} " - "axis" - ) - - w_axis_size = w_domain_axes[axis1].get_size() - self_axis_size = domain_axes[axis0].get_size() - - if w_axis_size != self_axis_size: - raise ValueError( - f"Weights field has incorrectly sized {identity!r} " - f"axis ({w_axis_size} != {self_axis_size})" - ) - - axis1_to_axis0[axis1] = axis0 - - # Check that the defining coordinate data arrays are - # compatible - key0 = s["axis_to_coord"][axis0] - key1 = t["axis_to_coord"][axis1] - - if not self._equivalent_construct_data( - w, key0=key0, key1=key1, s=s, t=t - ): - raise ValueError( - f"Weights field has incompatible {identity!r} " - "coordinates" - ) - - # Still here? Then the defining coordinates have - # equivalent data arrays - - # If the defining coordinates are attached to - # coordinate references then check that those - # coordinate references are equivalent - refs0 = [ - key - for key, ref in coordinate_references.items() - if key0 in ref.coordinates() - ] - refs1 = [ - key - for key, ref in w_coordinate_references.items() - if key1 in ref.coordinates() - ] - - nrefs = len(refs0) - if nrefs > 1 or nrefs != len(refs1): - # The defining coordinate are associated with - # different numbers of coordinate references - equivalent_refs = False - elif not nrefs: - # Neither defining coordinate is associated with a - # coordinate reference - equivalent_refs = True - else: - # Each defining coordinate is associated with - # exactly one coordinate reference - equivalent_refs = self._equivalent_coordinate_references( - w, key0=refs0[0], key1=refs1[0], s=s, t=t - ) - - if not equivalent_refs: - raise ValueError( - "Input weights field has an incompatible " - "coordinate reference" - ) - - axes0 = tuple( - [axis1_to_axis0[axis1] for axis1 in w.get_data_axes()] - ) - - for axis0 in axes0: - if axis0 in weights_axes: - raise ValueError( - "Multiple weights specified for " - f"{self.constructs.domain_axis_identity(axis0)!r} " - "axis" - ) - - comp[tuple(axes0)] = w.data - - weights_axes.update(axes0) - - return True - - def _weights_field_scalar(self, methods=False): - """Return a scalar field of weights with long_name ``'weight'``. - - :Returns: - - `Field` - - """ - data = Data(1.0, "1") - - f = type(self)() - f.set_data(data, copy=False) - f.long_name = "weight" - f.comment = f"Weights for {self!r}" - - return f - - def _weights_geometry_area( - self, - domain_axis, - comp, - weights_axes, - auto=False, - measure=False, - radius=None, - great_circle=False, - return_areas=False, - methods=False, - ): - """Creates area weights for polygon geometry cells. - - .. versionadded:: 3.2.0 - - :Parameters: - - domain_axis : `str` or `None` - - measure: `bool` - If True then make sure that the weights represent true - cell sizes. - - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as opposed to - the actual weights. - - :Returns: - - `bool` or `Data` - - """ - axis, aux_X, aux_Y, aux_Z = self._weights_yyy( - domain_axis, "polygon", methods=methods, auto=auto - ) - - if axis is None: - if auto: - return False - - if domain_axis is None: - raise ValueError("No polygon geometries") - - raise ValueError( - "No polygon geometries for " - f"{self.constructs.domain_axis_identity(domain_axis)!r} axis" - ) - - if axis in weights_axes: - if auto: - return False - - raise ValueError( - "Multiple weights specifications for " - f"{self.constructs.domain_axis_identity(axis)!r} axis" - ) - - # Check for interior rings - interior_ring_X = aux_X.get_interior_ring(None) - interior_ring_Y = aux_Y.get_interior_ring(None) - if interior_ring_X is None and interior_ring_Y is None: - interior_ring = None - elif interior_ring_X is None: - raise ValueError( - "Can't find weights: X coordinates have missing " - "interior ring variable" - ) - elif interior_ring_Y is None: - raise ValueError( - "Can't find weights: Y coordinates have missing " - "interior ring variable" - ) - elif not interior_ring_X.data.equals(interior_ring_Y.data): - raise ValueError( - "Can't find weights: Interior ring variables for X and Y " - "coordinates have different data values" - ) - else: - interior_ring = interior_ring_X.data - if interior_ring.shape != aux_X.bounds.shape[:-1]: - raise ValueError( - "Can't find weights: Interior ring variables have " - f"incorrect shape. Got {interior_ring.shape}, expected " - f"{aux_X.bounds.shape[:-1]}" - ) - - x = aux_X.bounds.data - y = aux_Y.bounds.data - - if x.Units.equivalent(_units_metres) and y.Units.equivalent( - _units_metres - ): - # ---------------------------------------------------- - # Plane polygons defined by straight lines. - # - # Use the shoelace formula: - # https://en.wikipedia.org/wiki/Shoelace_formula - # - # Do this in preference to weights based on spherical - # polygons, which require the great circle assumption. - # ---------------------------------------------------- - spherical = False - - if methods: - comp[(axis,)] = "area plane polygon geometry" - return True - - y.Units = x.Units - - all_areas = (x[..., :-1] * y[..., 1:]).sum(-1, squeeze=True) - ( - x[..., 1:] * y[..., :-1] - ).sum(-1, squeeze=True) - - for i, (parts_x, parts_y) in enumerate(zip(x, y)): - for j, (nodes_x, nodes_y) in enumerate(zip(parts_x, parts_y)): - nodes_x = nodes_x.compressed() - nodes_y = nodes_y.compressed() - - if (nodes_x.size and nodes_x[0] != nodes_x[-1]) or ( - nodes_y.size and nodes_y[0] != nodes_y[-1] - ): - # First and last nodes of this polygon - # part are different => need to account - # for the "last" edge of the polygon that - # joins the first and last points. - all_areas[i, j] += x[-1] * y[0] - x[0] * y[-1] - - all_areas = all_areas.abs() * 0.5 - - elif x.Units.equivalent(_units_radians) and y.Units.equivalent( - _units_radians - ): - # ---------------------------------------------------- - # Spherical polygons defined by great circles - # - # The area of such a spherical polygon is given by the - # sum of the interior angles minus (N-2)pi, where N is - # the number of sides (Todhunter, - # https://en.wikipedia.org/wiki/Spherical_trigonometry#Spherical_polygons): - # - # Area of N-sided polygon on the unit sphere = - # \left(\sum _{n=1}^{N}A_{n}\right) - (N - 2)\pi - # - # where A_{n} denotes the n-th interior angle. - # ---------------------------------------------------- - spherical = True - - if not great_circle: - raise ValueError( - "Must set great_circle=True to allow the derivation of " - "area weights from spherical polygons composed from " - "great circle segments." - ) - - if methods: - comp[(axis,)] = "area spherical polygon geometry" - return True - - x.Units = _units_radians - y.Units = _units_radians - - interior_angle = self._weights_interior_angle(x, y) - - # Find the number of edges of each polygon (note that - # this number may be one too few, but we'll adjust for - # that later). - N = interior_angle.sample_size(-1, squeeze=True) - - all_areas = interior_angle.sum(-1, squeeze=True) - (N - 2) * np.pi - - for i, (parts_x, parts_y) in enumerate(zip(x, y)): - for j, (nodes_x, nodes_y) in enumerate(zip(parts_x, parts_y)): - nodes_x = nodes_x.compressed() - nodes_y = nodes_y.compressed() - - if (nodes_x.size and nodes_x[0] != nodes_x[-1]) or ( - nodes_y.size and nodes_y[0] != nodes_y[-1] - ): - # First and last nodes of this polygon - # part are different => need to account - # for the "last" edge of the polygon that - # joins the first and last points. - interior_angle = self._weights_interior_angle( - nodes_x[[0, -1]], nodes_y[[0, -1]] - ) - - all_areas[i, j] += interior_angle + np.pi - - area_min = all_areas.min() - if area_min < 0: - raise ValueError( - "A spherical polygon geometry part has negative area" - ) - else: - return False - - # Change the sign of areas for polygons that are interior - # rings - if interior_ring is not None: - all_areas.where(interior_ring, -all_areas, inplace=True) - - # Sum the areas of each part to get the total area of each - # cell - areas = all_areas.sum(-1, squeeze=True) - - if measure and spherical and aux_Z is not None: - # Multiply by radius squared, accounting for any Z - # coordinates, to get the actual area - z = aux_Z.get_data(None, _fill_value=False) - if z is None: - r = radius - else: - if not z.Units.equivalent(_units_metres): - raise ValueError( - "Z coordinates must have units equivalent to " - f"metres for area calculations. Got {z.Units!r}" - ) - - positive = aux_Z.get_property("positive", None) - if positive is None: - raise ValueError( - "Value of Z coordinate 'positive' property is not " - "defined" - ) - - if positive.lower() == "up": - r = radius + z - elif positive.lower() == "down": - r = radius - z - else: - raise ValueError( - "Bad value of Z coordinate 'positive' " - f"property: {positive!r}." - ) - - areas *= r**2 - - if return_areas: - return areas - - comp[(axis,)] = areas - - weights_axes.add(axis) - - return True - - def _weights_geometry_line( - self, - domain_axis, - comp, - weights_axes, - auto=False, - measure=False, - radius=None, - great_circle=False, - methods=False, - ): - """Creates line-length weights for line geometries. - - .. versionadded:: 3.2.0 - - :Parameters: - - measure: `bool` - If True then make sure that the weights represent true - cell sizes. - - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as opposed to - the actual weights. - - """ - axis, aux_X, aux_Y, aux_Z = self._weights_yyy( - domain_axis, "line", methods=methods, auto=auto - ) - - if axis is None: - if auto: - return False - - if domain_axis is None: - raise ValueError("No line geometries") - - raise ValueError( - "No line geometries for " - f"{self.constructs.domain_axis_identity(domain_axis)!r} axis" - ) - - if axis in weights_axes: - if auto: - return False - - raise ValueError( - "Multiple weights specifications for " - f"{self.constructs.domain_axis_identity(axis)!r} axis" - ) - - x = aux_X.bounds.data - y = aux_Y.bounds.data - - if x.Units.equivalent(_units_metres) and y.Units.equivalent( - _units_metres - ): - # ---------------------------------------------------- - # Plane lines. - # - # Each line segment is the simple cartesian distance - # between two adjacent nodes. - # ---------------------------------------------------- - if methods: - comp[(axis,)] = "linear plane line geometry" - return True - - y.Units = x.Units - - delta_x = x.diff(axis=-1) - delta_y = y.diff(axis=-1) - - all_lengths = (delta_x**2 + delta_y**2) ** 0.5 - all_lengths = all_lengths.sum(-1, squeeze=True) - - elif x.Units.equivalent(_units_radians) and y.Units.equivalent( - _units_radians - ): - # ---------------------------------------------------- - # Spherical lines. - # - # Each line segment is a great circle arc between two - # adjacent nodes. - # - # The length of the great circle arc is the the - # interior angle multiplied by the radius. The - # interior angle is calculated with a special case of - # the Vincenty formula: - # https://en.wikipedia.org/wiki/Great-circle_distance - # ---------------------------------------------------- - if not great_circle: - raise ValueError( - "Must set great_circle=True to allow the derivation " - "of line-length weights from great circle segments." - ) - - if methods: - comp[(axis,)] = "linear spherical line geometry" - return True - - x.Units = _units_radians - y.Units = _units_radians - - interior_angle = self._weights_interior_angle(x, y) - if interior_angle.min() < 0: - raise ValueError( - "A spherical line geometry segment has " - f"negative length: {interior_angle.min() * radius!r}" - ) - - all_lengths = interior_angle.sum(-1, squeeze=True) - - if measure: - all_lengths *= radius - else: - return False - - # Sum the lengths of each part to get the total length of - # each cell - lengths = all_lengths.sum(-1, squeeze=True) - - comp[(axis,)] = lengths - - weights_axes.add(axis) - - return True - - def _weights_geometry_volume( - self, - comp, - weights_axes, - auto=False, - measure=False, - radius=None, - great_circle=False, - methods=False, - ): - """Creates volume weights for polygon geometry cells. - - .. versionadded:: 3.2.0 - - :Parameters: - - measure: `bool` - If True then make sure that the weights represent true - cell sizes. - - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as opposed to - the actual weights. - - """ - axis, aux_X, aux_Y, aux_Z = self._weights_yyy( - "polygon", methods=methods, auto=auto - ) - - if axis is None and auto: - return False - - if axis in weights_axes: - if auto: - return False - - raise ValueError( - "Multiple weights specifications for " - f"{self.constructs.domain_axis_identity(axis)!r} axis" - ) - - x = aux_X.bounds.data - y = aux_Y.bounds.data - z = aux_Z.bounds.data - - if not z.Units.equivalent(_units_metres): - if auto: - return False - - raise ValueError( - "Z coordinate bounds must have units equivalent to " - f"metres for volume calculations. Got {z.Units!r}." - ) - - if not methods: - # Initialise cell volumes as the cell areas - volumes = self._weights_geometry_area( - comp, - weights_axes, - auto=auto, - measure=measure, - radius=radius, - great_circle=great_circle, - methods=False, - return_areas=True, - ) - - if measure: - delta_z = abs(z[..., 1] - z[..., 0]) - delta_z.squeeze(axis=-1, inplace=True) - - if x.Units.equivalent(_units_metres) and y.Units.equivalent( - _units_metres - ): - # ---------------------------------------------------- - # Plane polygons defined by straight lines. - # - # Do this in preference to weights based on spherical - # polygons, which require the great circle assumption. - # ---------------------------------------------------- - if methods: - comp[(axis,)] = "volume plane polygon geometry" - return True - - if measure: - volumes *= delta_z - - elif x.Units.equivalent(_units_radians) and y.Units.equivalent( - _units_radians - ): - # ---------------------------------------------------- - # Spherical polygons defined by great circles - # - # The area of such a spherical polygon is given by the - # sum of the interior angles minus (N-2)pi, where N is - # the number of sides (Todhunter): - # - # https://en.wikipedia.org/wiki/Spherical_trigonometry#Area_and_spherical_excess - # - # The interior angle of a side is calculated with a - # special case of the Vincenty formula: - # https://en.wikipedia.org/wiki/Great-circle_distance - # ---------------------------------------------------- - if not great_circle: - raise ValueError( - "Must set great_circle=True to allow the derivation " - "of volume weights from spherical polygons composed " - "from great circle segments." - ) - - if methods: - comp[(axis,)] = "volume spherical polygon geometry" - return True - - if measure: - r = radius - - # actual_volume = - # [actual_area/(4*pi*r**2)] - # * (4/3)*pi*[(r+delta_z)**3 - r**3)] - volumes *= ( - delta_z**3 / (3 * r**2) + delta_z**2 / r + delta_z - ) - else: - raise ValueError( - "X and Y coordinate bounds must both have units " - "equivalent to either metres, for plane polygon, or radians, " - "for spherical polygon, volume calculations. Got " - f"{x.Units!r} and {y.Units!r}." - ) - - comp[(axis,)] = volumes - - weights_axes.add(axis) - - return True - - def _weights_interior_angle(self, data_lambda, data_phi): - r"""Find the interior angle between each adjacent pair of - geometry nodes defined on a sphere. - - The interior angle of two points on the sphere is calculated with - a special case of the Vincenty formula - (https://en.wikipedia.org/wiki/Great-circle_distance): - - \Delta \sigma =\arctan { - \frac {\sqrt {\left(\cos \phi _{2}\sin(\Delta \lambda )\right)^{2} + - \left(\cos \phi _{1}\sin \phi _{2} - - \sin \phi _{1}\cos \phi _{2}\cos(\Delta \lambda )\right)^{2} } } - {\sin \phi _{1}\sin \phi _{2} + - \cos \phi _{1}\cos \phi _{2}\cos(\Delta \lambda )} - } - - :Parameters: - - data_lambda: `Data` - Longitudes. Must have units of radians, which is not - checked. - - data_phi: `Data` - Latitudes. Must have units of radians, which is not - checked. - - :Returns: - - `Data` - The interior angles in units of radians. - - """ - delta_lambda = data_lambda.diff(axis=-1) - - cos_phi = data_phi.cos() - sin_phi = data_phi.sin() - - cos_phi_1 = cos_phi[..., :-1] - cos_phi_2 = cos_phi[..., 1:] - - sin_phi_1 = sin_phi[..., :-1] - sin_phi_2 = sin_phi[..., 1:] - - cos_delta_lambda = delta_lambda.cos() - sin_delta_lambda = delta_lambda.sin() - - numerator = ( - (cos_phi_2 * sin_delta_lambda) ** 2 - + ( - cos_phi_1 * sin_phi_2 - - sin_phi_1 * cos_phi_2 * cos_delta_lambda - ) - ** 2 - ) ** 0.5 - - denominator = ( - sin_phi_1 * sin_phi_2 + cos_phi_1 * cos_phi_2 * cos_delta_lambda - ) - - # TODO RuntimeWarning: overflow encountered in true_divide comes from - # numerator/denominator with missing values - - interior_angle = (numerator / denominator).arctan() - - interior_angle.override_units(_units_1, inplace=True) - - return interior_angle - - def _weights_linear( - self, - axis, - comp, - weights_axes, - auto=False, - measure=False, - methods=False, - ): - """1-d linear weights from dimension coordinate constructs. - - :Parameters: - - axis: `str` - - comp: `dict` - - weights_axes: `set` - - auto: `bool` - - measure: `bool` - If True then make sure that the weights represent true - cell sizes. - - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as opposed to - the actual weights. - - :Returns: - - `bool` - - """ - da_key = self.domain_axis(axis, key=True, default=None) - if da_key is None: - if auto: - return False - - raise ValueError( - "Can't create weights: Can't find domain axis " - f"matching {axis!r}" - ) - - dim = self.dimension_coordinate(filter_by_axis=(da_key,), default=None) - if dim is None: - if auto: - return False - - raise ValueError( - f"Can't create linear weights for {axis!r} axis: Can't find " - "dimension coordinate construct." - ) - - if not measure and dim.size == 1: - return False - - if da_key in weights_axes: - if auto: - return False - - raise ValueError( - f"Can't create linear weights for {axis!r} axis: Multiple " - "axis specifications" - ) - - if not dim.has_bounds(): - # Dimension coordinate has no bounds - if auto: - return False - - raise ValueError( - f"Can't create linear weights for {axis!r} axis: No bounds" - ) - else: - # Bounds exist - if methods: - comp[ - (da_key,) - ] = "linear " + self.constructs.domain_axis_identity(da_key) - else: - comp[(da_key,)] = dim.cellsize + other, key0=refs0[0], key1=refs1[0], s=s, t=v + ): + raise ValueError(error_msg) - weights_axes.add(da_key) + return other - return True + def _conform_for_data_broadcasting(self, other): + """Conforms the field with another, ready for data broadcasting. - def _weights_measure( - self, measure, comp, weights_axes, methods=False, auto=False - ): - """Cell measure weights. + Note that the other field, *other*, is not changed in-place. :Parameters: - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as opposed to - the actual weights. + other: `Field` + The field to conform. :Returns: - `bool` - - """ - m = self.cell_measures(filter_by_measure=(measure,), todict=True) - len_m = len(m) - - if not len_m: - if measure == "area": - return False - - if auto: - return - - raise ValueError( - f"Can't find weights: No {measure!r} cell measure" - ) - - elif len_m > 1: - if auto: - return False - - raise ValueError( - f"Can't find weights: Multiple {measure!r} cell measures" - ) - - key, clm = m.popitem() - - clm_axes0 = self.get_data_axes(key) + `Field` + The conformed version of *other*. - clm_axes = tuple( - [axis for axis, n in zip(clm_axes0, clm.data.shape) if n > 1] - ) + **Examples** - for axis in clm_axes: - if axis in weights_axes: - if auto: - return False + >>> h = f._conform_for_data_broadcasting(g) - raise ValueError( - "Multiple weights specifications for " - f"{self.constructs.domain_axis_identity(axis)!r} axis" - ) + """ - clm = clm.get_data(_fill_value=False).copy() - if clm_axes != clm_axes0: - iaxes = [clm_axes0.index(axis) for axis in clm_axes] - clm.squeeze(iaxes, inplace=True) + other = self._conform_for_assignment(other, check_coordinates=True) - if methods: - comp[tuple(clm_axes)] = measure + " cell measure" - else: - comp[tuple(clm_axes)] = clm + # Remove leading size one dimensions + ndiff = other.ndim - self.ndim + if ndiff > 0 and set(other.shape[:ndiff]) == set((1,)): + for i in range(ndiff): + other = other.squeeze(0) - weights_axes.update(clm_axes) + return other - return True + @_manage_log_level_via_verbosity + def _equivalent_construct_data( + self, + field1, + key0=None, + key1=None, + s=None, + t=None, + atol=None, + rtol=None, + verbose=None, + axis_map=None, + ): + """True if the field has equivalent construct data to another. - def _weights_scale(self, w, scale): - """Scale the weights so that they are <= scale. + Two real numbers ``x`` and ``y`` are considered equal if + ``|x-y|<=atol+rtol|y|``, where ``atol`` (the tolerance on absolute + differences) and ``rtol`` (the tolerance on relative differences) + are positive, typically very small numbers. See the *atol* and + *rtol* parameters. :Parameters: - w: `Data` - The weights to be scaled. - - scale: number or `None` - The maximum value of the scaled weights. If `None` then no - scaling is applied. + key0: `str` - :Returns: + key1: `str` - `Data` - The scaled weights. + field1: `Field` - """ - if scale is None: - return w + s: `dict`, optional - if scale <= 0: - raise ValueError( - "Can't set 'scale' parameter to a negative number. " - f"Got {scale!r}" - ) + t: `dict`, optional - w = w / w.max() - if scale != 1: - w = w * scale + atol: `float`, optional + The tolerance on absolute differences between real + numbers. The default value is set by the `atol` function. - return w + rtol: `float`, optional + The tolerance on relative differences between real + numbers. The default value is set by the `rtol` function. - def _weights_yyy( - self, domain_axis, geometry_type, methods=False, auto=False - ): - """Checks whether weights can be created for given coordinates. + {{verbose: `int` or `str` or `None`, optional}} - .. versionadded:: 3.2.0 + """ + item0 = self.constructs[key0] + item1 = field1.constructs[key1] - :Parameters: + if item0.has_data() != item1.has_data(): + if is_log_level_info(logger): + logger.info( + f"{self.__class__.__name__}: Only one item has data" + ) # pragma: no cover - domain_axis : `str` or `None` + return False - geometry_type: `str` - Either ``'polygon'`` or ``'line'``. + if not item0.has_data(): + # Neither field has a data array + return True - auto: `bool` + if item0.size != item1.size: + if is_log_level_info(logger): + logger.info( + f"{self.__class__.__name__}: Different metadata construct " + f"data array size: {item0.size} != {item1.size}" + ) # pragma: no cover - :Returns: + return False - `tuple` + if item0.ndim != item1.ndim: + if is_log_level_info(logger): + logger.info( + f"{self.__class__.__name__}: Different data array ranks " + f"({item0.ndim}, {item1.ndim})" + ) # pragma: no cover - """ - aux_X = None - aux_Y = None - aux_Z = None - x_axis = None - y_axis = None - z_axis = None - - auxiliary_coordinates_1d = self.auxiliary_coordinates( - filter_by_naxes=(1,), todict=True - ) + return False - for key, aux in auxiliary_coordinates_1d.items(): - if aux.get_geometry(None) != geometry_type: - continue + axes0 = self.get_data_axes(key0, default=()) + axes1 = field1.get_data_axes(key1, default=()) - if aux.X: - aux_X = aux.copy() - x_axis = self.get_data_axes(key)[0] - if domain_axis is not None and x_axis != domain_axis: - aux_X = None - continue - elif aux.Y: - aux_Y = aux.copy() - y_axis = self.get_data_axes(key)[0] - if domain_axis is not None and y_axis != domain_axis: - aux_Y = None - continue - elif aux.Z: - aux_Z = aux.copy() - z_axis = self.get_data_axes(key)[0] - if domain_axis is not None and z_axis != domain_axis: - aux_Z = None - continue + if s is None: + s = self.analyse_items() + if t is None: + t = field1.analyse_items() - if aux_X is None or aux_Y is None: - if auto: - return (None,) * 4 + transpose_axes = [] + if axis_map is None: + for axis0 in axes0: + axis1 = t["id_to_axis"].get(s["axis_to_id"][axis0], None) + if axis1 is None: + if is_log_level_info(logger): + # TODO: improve message here (make user friendly): + logger.info( + "t['id_to_axis'] does not have a key " + f"s['axis_to_id'][axis0] for " + f"{self.__class__.__name__}" + ) # pragma: no cover - raise ValueError( - "Can't create weights: Need both X and Y nodes to " - f"calculate {geometry_type} geometry weights" - ) + return False - if x_axis != y_axis: - if auto: - return (None,) * 4 + transpose_axes.append(axes1.index(axis1)) + else: + for axis0 in axes0: + axis1 = axis_map.get(axis0) + if axis1 is None: + if is_log_level_info(logger): + # TODO: improve message here (make user friendly): + logger.info( + f"axis_map[axis0] is None for {self.__class__.__name__}" + ) # pragma: no cover - raise ValueError( - "Can't create weights: X and Y nodes span different " - "domain axes" - ) + return False - axis = x_axis + transpose_axes.append(axes1.index(axis1)) - if aux_X.get_bounds(None) is None or aux_Y.get_bounds(None) is None: - # Not both X and Y coordinates have bounds - if auto: - return (None,) * 4 + copy1 = True - raise ValueError("Not both X and Y coordinates have bounds") + if transpose_axes != list(range(item1.ndim)): + if copy1: + item1 = item1.copy() + copy1 = False - if aux_X.bounds.shape != aux_Y.bounds.shape: - if auto: - return (None,) * 4 + item1.transpose(transpose_axes, inplace=True) - raise ValueError( - "Can't find weights: X and Y geometry coordinate bounds " - "must have the same shape. " - f"Got {aux_X.bounds.shape} and {aux_Y.bounds.shape}" - ) + if item0.shape != item1.shape: + # add traceback TODO + return False - if aux_Z is None: - for key, aux in auxiliary_coordinates_1d.items(): - if aux.Z: - aux_Z = aux.copy() - z_axis = self.get_data_axes(key)[0] + flip_axes = [ + i + for i, (axis1, axis0) in enumerate(zip(axes1, axes0)) + if field1.direction(axis1) != self.direction(axis0) + ] - # Check Z coordinates - if aux_Z is not None: - if z_axis != x_axis: - if auto: - return (None,) * 4 + if flip_axes: + if copy1: + item1 = item1.copy() + copy1 = False - raise ValueError( - "Z coordinates span different domain axis to X and Y " - "geometry coordinates" - ) + item1.flip(flip_axes, inplace=True) - return axis, aux_X, aux_Y, aux_Z + if not item0._equivalent_data( + item1, rtol=rtol, atol=atol, verbose=verbose + ): + # add traceback TODO + return False - # ---------------------------------------------------------------- - # End of worker functions for weights - # ---------------------------------------------------------------- + return True - # ---------------------------------------------------------------- - # Attributes - # ---------------------------------------------------------------- @property def DSG(self): """True if the field contains a collection of discrete sampling @@ -4391,17 +3123,18 @@ def weights( ): """Return weights for the data array values. - The weights are those used during a statistical collapse of the - data. For example when computing a area weight average. + The weights are those used during a statistical collapse of + the data. For example when computing a area weight average. Weights for any combination of axes may be returned. Weights are either derived from the field construct's metadata - (such as coordinate cell sizes) or provided explicitly in the form - of other `Field` constructs. In any case, the outer product of - these weights components is returned in a field which is - broadcastable to the original field (see the *components* parameter - for returning the components individually). + (such as coordinate cell sizes) or provided explicitly in the + form of other `Field` constructs. In any case, the outer + product of these weights components is returned in a field + which is broadcastable to the original field (see the + *components* parameter for returning the components + individually). By default null, equal weights are returned. @@ -4420,56 +3153,65 @@ def weights( greater than 1, raising an exception if this is not possible (this is the default).; - * **Type 2** will always succeed in creating weights for - all axes of the field, even if some of those weights are - null. + * **Type 2** will always succeed in creating weights + for all axes of the field, even if some of those + weights are null. * **Type 3** allows particular types of weights to be - defined for particular axes, and an exception will be - raised if it is not possible to the create weights. + defined for particular axes, and an exception will + be raised if it is not possible to the create + weights. .. **Type 1** and **Type 2** come at the expense of not - always being able to control exactly how the weights are - created (although which methods were used can be inspected - with use of the *methods* parameter). + always being able to control exactly how the weights + are created (although which methods were used can be + inspected with use of the *methods* parameter). * **Type 1**: *weights* may be: - ========== ============================================ + ========== ======================================== *weights* Description - ========== ============================================ - `True` This is the default. Weights are created for - all axes (or a subset of them, see the - *axes* parameter). Set the *methods* - parameter to find out how the weights were - actually created. - - The weights components are created for axes - of the field by one or more of the following - methods, in order of preference, + ========== ======================================== + `True` This is the default. Weights are created + for all axes (or a subset of them, see + the *axes* parameter). Set the *methods* + parameter to find out how the weights + were actually created. + + The weights components are created for + axes of the field by one or more of the + following methods, in order of + preference, 1. Volume cell measures 2. Area cell measures - 3. Area calculated from (grid) latitude - and (grid) longitude dimension - coordinate constructs with bounds - 4. Cell sizes of dimension coordinate + 3. Area calculated from X and Y + dimension coordinate constructs + with bounds + 4. Area calculated from 1-d auxiliary + coordinate constructs for geomtries + or a UGRID mesh topology. + 5. Length calculated from 1-d auxiliary + coordinate constructs for geomtries + or a UGRID mesh topology. + 6. Cell sizes of dimension coordinate constructs with bounds - 5. Equal weights + 7. Equal weights and the outer product of these weights - components is returned in a field constructs - which is broadcastable to the original field - construct (see the *components* parameter). - ========== ============================================ + components is returned in a field + constructs which is broadcastable to the + original field construct (see the + *components* parameter). + ========== ======================================== * **Type 2**: *weights* may be one of: - ========== ============================================ + ========== ======================================== *weights* Description - ========== ============================================ + ========== ======================================== `None` Equal weights for all axes. `False` Equal weights for all axes. @@ -4479,23 +3221,32 @@ def weights( construct's data, unless the *axes* parameter is also set. - `dict` Explicit weights in a dictionary of the form - that is returned from a call to the + `dict` Explicit weights in a dictionary of the + form that is returned from a call to the `weights` method with ``component=True`` - ========== ============================================ + ========== ======================================== * **Type 3**: *weights* may be one, or a sequence, of: ============ ========================================== *weights* Description ============ ========================================== - ``'area'`` Cell area weights from the field - construct's area cell measure construct - or, if one doesn't exist, from (grid) - latitude and (grid) longitude dimension - coordinate constructs. Set the *methods* - parameter to find out how the weights were - actually created. + ``'area'`` Cell area weights. The weights + components are created for axes of the + field by the following methods, in + order of preference, + + 1. Area cell measures. + 2. X and Y dimension coordinate + constructs with bounds. + 3. X and Y 1-d auxiliary coordinate + constructs for polygon cells + defined by geometries or a UGRID + mesh topology. + + Set the *methods* parameter to find + out how the weights were actually + created. ``'volume'`` Cell volume weights from the field construct's volume cell measure construct. @@ -4510,8 +3261,8 @@ def weights( ============ ========================================== If *weights* is a sequence of any combination of the - above then the returned field contains the outer product - of the weights defined by each element of the + above then the returned field contains the outer + product of the weights defined by each element of the sequence. The ordering of the sequence is irrelevant. *Parameter example:* @@ -4521,15 +3272,15 @@ def weights( height: ``f.weights(['area', 'Z'])``. scale: number, optional - If set to a positive number then scale the weights so that - they are less than or equal to that number. If weights - components have been requested (see the *components* - parameter) then each component is scaled independently of - the others. + If set to a positive number then scale the weights so + that they are less than or equal to that number. If + weights components have been requested (see the + *components* parameter) then each component is scaled + independently of the others. *Parameter example:* - To scale all weights so that they lie between 0 and 1: - ``scale=1``. + To scale all weights so that they lie between 0 and + 1: ``scale=1``. measure: `bool`, optional Create weights that are cell measures, i.e. which @@ -4561,64 +3312,54 @@ def weights( the cell volumes will be calculated using the size of the vertical coordinate cells. - radius: optional - Specify the radius used for calculating the areas of - cells defined in spherical polar coordinates. The - radius is that which would be returned by this call of - the field construct's `~cf.Field.radius` method: - ``f.radius(radius)``. See the `cf.Field.radius` for - details. - - By default *radius* is ``'earth'`` which means that if - and only if the radius can not found from the datums - of any coordinate reference constructs, then the - default radius taken as 6371229 metres. + {{radius: optional}} components: `bool`, optional - If True then a dictionary of orthogonal weights components - is returned instead of a field. Each key is a tuple of - integers representing axis positions in the field - construct's data, with corresponding values of weights in - `Data` objects. The axes of weights match the axes of the - field construct's data array in the order given by their - dictionary keys. + If True then a dictionary of orthogonal weights + components is returned instead of a field. Each key is + a tuple of integers representing axis positions in the + field construct's data, with corresponding values of + weights in `Data` objects. The axes of weights match + the axes of the field construct's data array in the + order given by their dictionary keys. methods: `bool`, optional - If True, then return a dictionary describing methods used - to create the weights. + If True, then return a dictionary describing methods + used to create the weights. data: `bool`, optional - If True then return the weights in a `Data` instance that - is broadcastable to the original data. + If True then return the weights in a `Data` instance + that is broadcastable to the original data. .. versionadded:: 3.1.0 great_circle: `bool`, optional - If True then allow, if required, the derivation of i) area - weights from polygon geometry cells by assuming that each - cell part is a spherical polygon composed of great circle - segments; and ii) and the derivation of line-length - weights from line geometry cells by assuming that each - line part is composed of great circle segments. + If True then allow, if required, the derivation of i) + area weights from polygon cells by assuming that each + cell part is a spherical polygon composed of great + circle segments; and ii) and the derivation of + line-length weights line cells by assuming that each + line part is composed of great circle segments. Only + applies to geometry and UGRID cells. .. versionadded:: 3.2.0 axes: (sequence of) `int` or `str`, optional - Modify the behaviour when *weights* is `True` or a `Data` - instance. Ignored for any other value the *weights* - parameter. + Modify the behaviour when *weights* is `True` or a + `Data` instance. Ignored for any other value the + *weights* parameter. - If *weights* is `True` then weights are created only for - the specified axes (as opposed to all + If *weights* is `True` then weights are created only + for the specified axes (as opposed to all axes). I.e. ``weight=True, axes=axes`` is identical to ``weights=axes``. - If *weights* is a `Data` instance then the specified axes - identify each dimension of the given weights. If the - weights do not broadcast to the field construct's data - then setting the *axes* parameter is required so that the - broadcasting can be inferred, otherwise setting the *axes* - is not required. + If *weights* is a `Data` instance then the specified + axes identify each dimension of the given weights. If + the weights do not broadcast to the field construct's + data then setting the *axes* parameter is required so + that the broadcasting can be inferred, otherwise + setting the *axes* is not required. *Parameter example:* ``axes='T'`` @@ -4636,9 +3377,9 @@ def weights( :Returns: `Field` or `Data` or `dict` - The weights field; or if *data* is True, weights data in - broadcastable form; or if *components* is True, orthogonal - weights in a dictionary. + The weights field; or if *data* is True, weights data + in broadcastable form; or if *components* is True, + orthogonal weights in a dictionary. **Examples** @@ -4666,6 +3407,8 @@ def weights( (2,): 'linear longitude'} """ + from .weights import Weights + if isinstance(weights, str) and weights == "auto": _DEPRECATION_ERROR_KWARG_VALUE( self, @@ -4701,7 +3444,7 @@ def weights( return Data(1.0, "1") # Return a field containing a single weight of 1 - return self._weights_field_scalar() + return Weights.field_scalar(self) # Still here? if methods: @@ -4713,8 +3456,8 @@ def weights( # All axes which have weights weights_axes = set() - if radius is not None: - radius = self.radius(default=radius) + # if radius is not None: + # radius = self.radius(default=radius) if weights is True and axes is not None: # -------------------------------------------------------- @@ -4727,18 +3470,19 @@ def weights( # Auto-detect all weights # -------------------------------------------------------- # Volume weights - if self._weights_measure( - "volume", comp, weights_axes, methods=methods, auto=True + if Weights.cell_measure( + self, "volume", comp, weights_axes, methods=methods, auto=True ): # Found volume weights from cell measures pass - elif self._weights_measure( - "area", comp, weights_axes, methods=methods, auto=True + elif Weights.cell_measure( + self, "area", comp, weights_axes, methods=methods, auto=True ): # Found area weights from cell measures pass - elif self._weights_area_XY( + elif Weights.area_XY( + self, comp, weights_axes, measure=measure, @@ -4753,7 +3497,8 @@ def weights( domain_axes = self.domain_axes(todict=True) for da_key in domain_axes: - if self._weights_geometry_area( + if Weights.polygon_area( + self, da_key, comp, weights_axes, @@ -4765,7 +3510,8 @@ def weights( ): # Found area weights from polygon geometries pass - elif self._weights_geometry_line( + elif Weights.line_length( + self, da_key, comp, weights_axes, @@ -4777,7 +3523,8 @@ def weights( ): # Found linear weights from line geometries pass - elif self._weights_linear( + elif Weights.linear( + self, da_key, comp, weights_axes, @@ -4840,13 +3587,14 @@ def weights( # -------------------------------------------------------- # Field # -------------------------------------------------------- - self._weights_field([weights], comp, weights_axes) + Weights.field(self, [weights], comp, weights_axes) elif isinstance(weights, Data): # -------------------------------------------------------- # Data # -------------------------------------------------------- - self._weights_data( + Weights.data( + self, weights, comp, weights_axes, @@ -4869,10 +3617,10 @@ def weights( else: axes.append(weights) else: - # In rare edge cases, e.g. if a user sets: - # weights=f[0].cell_area - # when they mean weights=f[0].cell_area(), we reach this - # code but weights is not iterable. So check it is first: + # In rare edge cases (e.g. if a user sets + # `weights=f[0].cell_area` when they really meant + # `weights=f[0].cell_area()`) we reach this code but + # find that weights is not iterable. So check it is.x try: weights = iter(weights) except TypeError: @@ -4893,22 +3641,33 @@ def weights( axes.append(w) # Field weights - self._weights_field(fields, comp, weights_axes) + Weights.field(self, fields, comp, weights_axes) # Volume weights if "volume" in cell_measures: - self._weights_measure( - "volume", comp, weights_axes, methods=methods, auto=False + Weights.cell_measure( + self, + "volume", + comp, + weights_axes, + methods=methods, + auto=False, ) # Area weights if "area" in cell_measures: - if self._weights_measure( - "area", comp, weights_axes, methods=methods, auto=True + if Weights.cell_measure( + self, + "area", + comp, + weights_axes, + methods=methods, + auto=True, ): # Found area weights from cell measures pass - elif self._weights_area_XY( + elif Weights.area_XY( + self, comp, weights_axes, measure=measure, @@ -4920,8 +3679,9 @@ def weights( # coordinates pass else: - # Found area weights from polygon geometries - self._weights_geometry_area( + # Found area weights from UGRID/geometry cells + Weights.polygon_area( + self, None, comp, weights_axes, @@ -4940,7 +3700,8 @@ def weights( f"{axis!r}" ) - if self._weights_geometry_area( + if Weights.polygon_area( + self, da_key, comp, weights_axes, @@ -4952,7 +3713,8 @@ def weights( ): # Found area weights from polygon geometries pass - elif self._weights_geometry_line( + elif Weights.line_length( + self, da_key, comp, weights_axes, @@ -4965,7 +3727,8 @@ def weights( # Found linear weights from line geometries pass else: - self._weights_linear( + Weights.linear( + self, da_key, comp, weights_axes, @@ -4983,10 +3746,11 @@ def weights( del comp[(yaxis,)] weights_axes.discard(xaxis) weights_axes.discard(yaxis) - if not self._weights_measure( - "area", comp, weights_axes, methods=methods + if not Weights.cell_measure( + self, "area", comp, weights_axes, methods=methods ): - self._weights_area_XY( + Weights.area_XY( + self, comp, weights_axes, measure=measure, @@ -4996,23 +3760,16 @@ def weights( if not methods: if scale is not None: - # -------------------------------------------------------- + # ---------------------------------------------------- # Scale the weights so that they are <= scale - # -------------------------------------------------------- + # ---------------------------------------------------- for key, w in comp.items(): - comp[key] = self._weights_scale(w, scale) + comp[key] = Weights.scale(w, scale) for w in comp.values(): if not measure: w.override_units("1", inplace=True) - mn = w.minimum() - if mn <= 0: - raise ValueError( - "All weights must be positive. " - f"Got a weight of {mn}" - ) - if components or methods: # -------------------------------------------------------- # Return a dictionary of component weights, which may be @@ -5034,7 +3791,7 @@ def weights( # No component weights have been defined so return an # equal weights field # -------------------------------------------------------- - f = self._weights_field_scalar() + f = Weights.field_scalar(self) if data: return f.data @@ -5055,7 +3812,7 @@ def weights( # -------------------------------------------------------- # Scale the weights so that they are <= scale # -------------------------------------------------------- - wdata = self._weights_scale(wdata, scale) + wdata = Weights.scale(wdata, scale) # ------------------------------------------------------------ # Reorder the data so that its dimensions are in the same @@ -7788,6 +6545,7 @@ def collapse( a = self.domain_axis(x, key=True, default=None) if a is None: raise ValueError(msg.format(x)) + axes2.append(a) all_axes.append(axes2) @@ -7807,8 +6565,6 @@ def collapse( # # ------------------------------------------------------------ domain_axes = f.domain_axes(todict=False, cached=domain_axes) - # auxiliary_coordinates = f.auxiliary_coordinates(view=True) - # dimension_coordinates = f.dimension_coordinates(view=True) for method, axes, within, over, axes_in in zip( all_methods, all_axes, all_within, all_over, input_axes @@ -7818,8 +6574,6 @@ def collapse( raise ValueError(f"Unknown collapse method: {method!r}") method = method2 - - # collapse_axes_all_sizes = domain_axes.filter_by_key(*axes) collapse_axes_all_sizes = f.domain_axes( filter_by_key=axes, todict=False ) @@ -7923,6 +6677,8 @@ def collapse( "simultaneously" ) + axis = [a for a in collapse_axes][0] + # ------------------------------------------------------------ # Grouped collapse: Calculate weights # ------------------------------------------------------------ @@ -7954,12 +6710,17 @@ def collapse( radius=radius, great_circle=great_circle, ) - - if not g_weights: + if g_weights: + # For grouped collapses, bring the weights + # into memory. This is to prevent lazy + # operations being run on the entire weights + # array for every group. + iaxes = (self.get_data_axes().index(axis),) + if iaxes in g_weights: + g_weights[iaxes] = g_weights[iaxes].persist() + else: g_weights = None - axis = [a for a in collapse_axes][0] - f = f._collapse_grouped( method, axis, @@ -8050,7 +6811,6 @@ def collapse( radius=radius, great_circle=great_circle, ) - if d_weights: d_kwargs["weights"] = d_weights @@ -14408,6 +13168,7 @@ def section(self, axes=None, stop=None, min_step=1, **kwargs): @_deprecated_kwarg_check("i", version="3.0.0", removed_at="4.0.0") @_inplace_enabled(default=False) + @_manage_log_level_via_verbosity def regrids( self, dst, @@ -14425,6 +13186,7 @@ def regrids( check_coordinates=False, min_weight=None, weights_file=None, + verbose=None, inplace=False, i=False, _compute_field_mass=None, @@ -14465,6 +13227,12 @@ def regrids( the points on either side are together without a gap (as is the case for NEMO model outputs). + **UGRID meshes** + + Data defined on UGRID face or node cells may be regridded to + any other latitude-longitude grid, including other UGRID + meshes. + **Cyclicity of the X axis** The cyclicity of the X (longitude) axes of the source and @@ -14595,7 +13363,11 @@ def regrids( {{weights_file: `str` or `None`, optional}} - .. versionadded:: 3.15.2 + .. versionadded:: 3.15.2 + + {{verbose: `int` or `str` or `None`, optional}} + + .. versionadded:: 3.16.0 {{inplace: `bool`, optional}} @@ -14724,6 +13496,11 @@ def regridc( the field being regridded and the specification of the destination grid given by the *dst* parameter. + **UGRID meshes** + + At present, Cartesian regridding is only available when + neither the source nor destination grid is a UGRID mesh. + {{regrid Masked cells}} {{regrid Implementation}} @@ -14835,7 +13612,7 @@ def regridc( {{weights_file: `str` or `None`, optional}} - .. versionadded:: 3.15.2 + .. versionadded:: 3.15.2 {{inplace: `bool`, optional}} diff --git a/cf/fieldancillary.py b/cf/fieldancillary.py index c9241863d8..ca32eeda3c 100644 --- a/cf/fieldancillary.py +++ b/cf/fieldancillary.py @@ -4,37 +4,4 @@ class FieldAncillary(mixin.PropertiesData, cfdm.FieldAncillary): - """A field ancillary construct of the CF data model. - - The field ancillary construct provides metadata which are - distributed over the same sampling domain as the field itself. For - example, if a data variable holds a variable retrieved from a - satellite instrument, a related ancillary data variable might - provide the uncertainty estimates for those retrievals (varying - over the same spatiotemporal domain). - - The field ancillary construct consists of an array of the - ancillary data, which is zero-dimensional or which depends on one - or more of the domain axes, and properties to describe the - data. It is assumed that the data do not depend on axes of the - domain which are not spanned by the array, along which the values - are implicitly propagated. CF-netCDF ancillary data variables - correspond to field ancillary constructs. Note that a field - ancillary construct is constrained by the domain definition of the - parent field construct but does not contribute to the domain's - definition, unlike, for instance, an auxiliary coordinate - construct or domain ancillary construct. - - **NetCDF interface** - - {{netcdf variable}} - - """ - - def __repr__(self): - """Called by the `repr` built-in function. - - x.__repr__() <==> repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) + + .. versionadded:: 3.16.0 + + """ + return super().__repr__().replace("<", " repr(x) + + .. versionadded:: 3.16.0 + + """ + return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " 1: + mask = da.transpose(mask, axes=axes) return mask @@ -1819,6 +2166,13 @@ def update_coordinates(src, dst, src_grid, dst_grid): Replace the existing coordinate constructs that span the regridding axes with those from the destination grid. + Also, if the source grid is a mesh, remove the existing domain + topology and cell connectivity constructs that span the regridding + axis; and if the destination grid is a mesh copy omain topology + and cell connectivity constructs from the destination grid. + + .. versionadded:: 3.14.0 + :Parameters: src: `Field` @@ -1827,10 +2181,10 @@ def update_coordinates(src, dst, src_grid, dst_grid): dst: `Field` or `Domain` The field or domain containing the destination grid. - src_grid: `Grid` + src_grid: `Grid` or `Mesh` The definition of the source grid. - dst_grid: `Grid` + dst_grid: `Grid` or `Mesh` The definition of the destination grid. :Returns: @@ -1841,44 +2195,77 @@ def update_coordinates(src, dst, src_grid, dst_grid): src_axis_keys = src_grid.axis_keys dst_axis_keys = dst_grid.axis_keys - # Remove the source coordinates of new field - for key in src.coordinates( - filter_by_axis=src_axis_keys, axis_mode="or", todict=True + # Remove the source coordinate, domain topology and cell + # connectivity constructs from regridded field. + for key in src.constructs( + filter_by_type=( + "dimension_coordinate", + "auxiliary_coordinate", + "domain_topology", + "cell_connectivity", + ), + filter_by_axis=src_axis_keys, + axis_mode="or", + todict=True, ): src.del_construct(key) # Domain axes src_domain_axes = src.domain_axes(todict=True) dst_domain_axes = dst.domain_axes(todict=True) - for src_axis, dst_axis in zip(src_axis_keys, dst_axis_keys): - src_domain_axis = src_domain_axes[src_axis] - dst_domain_axis = dst_domain_axes[dst_axis] + if src_grid.n_regrid_axes == dst_grid.n_regrid_axes: + # Change the size of the regridded domain axes + for src_axis, dst_axis in zip(src_axis_keys, dst_axis_keys): + src_domain_axis = src_domain_axes[src_axis] + dst_domain_axis = dst_domain_axes[dst_axis] - src_domain_axis.set_size(dst_domain_axis.size) + src_domain_axis.set_size(dst_domain_axis.size) - ncdim = dst_domain_axis.nc_get_dimension(None) - if ncdim is not None: - src_domain_axis.nc_set_dimension(ncdim) + ncdim = dst_domain_axis.nc_get_dimension(None) + if ncdim is not None: + src_domain_axis.nc_set_dimension(ncdim) + else: + # The regridding has changed the number of data axes (e.g. by + # regridding a source mesh grid to a destination non-mesh + # grid, or vice versa), so insert new domain axis constructs + # for all of the new axes. + src_axis_keys = [ + src.set_construct(dst_domain_axes[dst_axis].copy()) + for dst_axis in dst_axis_keys + ] + src_grid.new_axis_keys = src_axis_keys - # Coordinates axis_map = { dst_axis: src_axis for dst_axis, src_axis in zip(dst_axis_keys, src_axis_keys) } dst_data_axes = dst.constructs.data_axes() - for key, aux in dst.coordinates( + # Copy coordinates constructs from the destination grid + for key, coord in dst.coordinates( filter_by_axis=dst_axis_keys, axis_mode="subset", todict=True ).items(): axes = [axis_map[axis] for axis in dst_data_axes[key]] - src.set_construct(aux, axes=axes) + src.set_construct(coord, axes=axes) + + # Copy domain topology and cell connectivity constructs from the + # destination grid + if dst_grid.is_mesh: + for key, topology in dst.constructs( + filter_by_type=("domain_topology", "cell_connectivity"), + filter_by_axis=dst_axis_keys, + axis_mode="exact", + todict=True, + ).items(): + axes = [axis_map[axis] for axis in dst_data_axes[key]] + src.set_construct(topology, axes=axes) -def update_non_coordinates( - src, dst, regrid_operator, src_grid=None, dst_grid=None -): +def update_non_coordinates(src, dst, src_grid, dst_grid, regrid_operator): """Update the coordinate references of the regridded field. + .. versionadded:: 3.14.0 + :Parameters: src: `Field` @@ -1923,14 +2310,17 @@ def update_non_coordinates( src.del_coordinate_reference(ref_key) # ---------------------------------------------------------------- - # Delete source grid cell measures and field ancillaries that span - # any of the regridding axes + # Delete source grid cell measure and field ancillary constructs + # that span any of the regridding axes. # ---------------------------------------------------------------- for key in src.constructs( - filter_by_type=("cell_measure", "field_ancillary"), todict=True + filter_by_type=("cell_measure", "field_ancillary"), + filter_by_axis=src_axis_keys, + axis_mode="or", + todict=True, ): - if set(data_axes[key]).intersection(src_axis_keys): - src.del_construct(key) + # if set(data_axes[key]).intersection(src_axis_keys): + src.del_construct(key) # ---------------------------------------------------------------- # Regrid any remaining source domain ancillaries that span all of @@ -2000,3 +2390,110 @@ def update_non_coordinates( if axes and set(axes).issubset(dst_axis_keys): src.set_coordinate_reference(ref, parent=dst, strict=True) + + +def update_data(src, regridded_data, src_grid): + """Insert the regridded field data. + + .. versionadded:: 3.16.0 + + .. seealso: `update_coordinates`, `update_non_coordinates` + + :Parameters: + + src`: `Field` + The regridded field construct, that will up updated + in-place. + + regridded_data: `numpy.ndarray` + The regridded array + + src_grid: `Grid` + The definition of the source grid. + + :Returns: + + `None` + + """ + data_axes = src.get_data_axes() + if src_grid.new_axis_keys: + # The regridding has changed the number of data axes (e.g. by + # regridding a source UGRID grid to a destination non-UGRID + # grid, or vice versa) => delete the old, superceded domain + # axis construct and update the list of data axes. + data_axes = list(data_axes) + index = data_axes.index(src_grid.axis_keys[0]) + for axis in src_grid.axis_keys: + data_axes.remove(axis) + src.del_construct(axis) + + data_axes[index:index] = src_grid.new_axis_keys + + src.set_data(regridded_data, axes=data_axes, copy=False) + + +def get_mesh(f): + """Get domain topology mesh information. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` or `Domain` + The construct from which to get the mesh information. + + :Returns: + + 3-`tuple` + If the field or domain has no domain topology construct + then ``(None, None, None)`` is returned. Otherwise the + tuple contains: + + * The domain topology construct + * The mesh location of the domain topology (e.g. ``'face'``) + * The identifier of domain axis construct that is spanned + by the domain topology construct + + """ + key, domain_topology = f.domain_topology(item=True, default=(None, None)) + if domain_topology is None: + return (None, None, None) + + return ( + domain_topology, + domain_topology.get_cell(""), + f.get_data_axes(key)[0], + ) + + +def has_coordinate_arrays(grid): + """Whether all grid coordinates have representative arrays. + + .. versionadded:: 3.16.0 + + :Parameters: + + grid: `Grid` + The definition of the grid. + + :Returns: + + `bool` + True if and only if all grid coordinates have + representative arrays. + + """ + if not grid.coords: + return False + + for coord in grid.coords: + try: + has_data = coord.has_data() + except AttributeError: + has_data = True + + if not has_data: + return False + + return True diff --git a/cf/regrid/regridoperator.py b/cf/regrid/regridoperator.py index 00ca216b6f..bcea12ec63 100644 --- a/cf/regrid/regridoperator.py +++ b/cf/regrid/regridoperator.py @@ -40,6 +40,7 @@ def __init__( dst_axes=None, dst=None, weights_file=None, + src_mesh_location=None, ): """**Initialisation** @@ -124,6 +125,13 @@ def __init__( .. versionadded:: 3.15.2 + src_mesh_location: `str`, optional + The UGRID mesh element of the source grid + (e.g. ``'face'``). An empty string should be used for + a non-UGRID source grid. + + .. versionadded:: 3.16.0 + """ super().__init__() @@ -149,6 +157,7 @@ def __init__( self._set_component("dst_axes", dst_axes, copy=False) self._set_component("dst", dst, copy=False) self._set_component("weights_file", weights_file, copy=False) + self._set_component("src_mesh_location", src_mesh_location, copy=False) def __repr__(self): """x.__repr__() <==> repr(x)""" @@ -313,6 +322,15 @@ def src_mask(self): """ return self._get_component("src_mask") + @property + def src_mesh_location(self): + """The UGRID mesh element of the source grid. + + .. versionadded:: 3.16.0 + + """ + return self._get_component("src_mesh_location") + @property def src_shape(self): """The shape of the source grid. @@ -391,6 +409,7 @@ def copy(self): dst_axes=self.dst_axes, dst=self.dst.copy(), weights_file=self.weights_file, + src_mesh_location=self.src_mesh_location, ) @_display_or_return @@ -431,6 +450,7 @@ def dump(self, display=True): "start_index", "src_axes", "dst_axes", + "src_mesh_location", "dst", "weights", "row", @@ -603,11 +623,11 @@ def tosparse(self): # Note: It is much more efficient to access 'weights.indptr' # and 'weights.data' directly, rather than iterating # over rows of 'weights' and using 'weights.getrow'. - count_nonzero = np.count_nonzero + # count_nonzero = np.count_nonzero indptr = weights.indptr.tolist() data = weights.data for j, (i0, i1) in enumerate(zip(indptr[:-1], indptr[1:])): - if not count_nonzero(data[i0:i1]): + if not data[i0:i1].size: dst_mask[j] = True if not dst_mask.any(): diff --git a/cf/test/create_test_files.py b/cf/test/create_test_files.py index 0cd4f5dd42..3adfa21b46 100644 --- a/cf/test/create_test_files.py +++ b/cf/test/create_test_files.py @@ -7,17 +7,20 @@ faulthandler.enable() # to debug seg faults and timeouts +import cfdm import netCDF4 -import cf - -VN = cf.CF() +VN = cfdm.CF() +# -------------------------------------------------------------------- +# DSG files +# -------------------------------------------------------------------- def _make_contiguous_file(filename): + """Make a netCDF file with a contiguous ragged array DSG feature.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" n.createDimension("station", 4) @@ -106,9 +109,10 @@ def _make_contiguous_file(filename): def _make_indexed_file(filename): + """Make a netCDF file with an indexed ragged array DSG feature.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" n.createDimension("station", 4) @@ -238,15 +242,16 @@ def _make_indexed_file(filename): def _make_indexed_contiguous_file(filename): + """Make a netCDF file with an indexed contiguous ragged array.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeriesProfile" # 3 stations n.createDimension("station", 3) # 58 profiles spreadover 4 stations, each at a different time - profile = n.createDimension("profile", 58) + n.createDimension("profile", 58) n.createDimension("obs", None) n.createDimension("name_strlen", 8) n.createDimension("bounds", 2) @@ -606,6 +611,9 @@ def _make_indexed_contiguous_file(filename): return filename +# -------------------------------------------------------------------- +# External variable files +# -------------------------------------------------------------------- def _make_external_files(): """Make netCDF files with external variables.""" @@ -622,7 +630,7 @@ def _pp( nc.createDimension("grid_latitude", 10) nc.createDimension("grid_longitude", 9) - nc.Conventions = "CF-" + VN + nc.Conventions = f"CF-{VN}" if parent: nc.external_variables = "areacella" @@ -715,10 +723,14 @@ def _pp( return parent_file, external_file, combined_file, external_missing_file +# -------------------------------------------------------------------- +# Gathered files +# -------------------------------------------------------------------- def _make_gathered_file(filename): """Make a netCDF file with a gathered array.""" def _jj(shape, list_values): + """Create and return a gathered array.""" array = np.ma.masked_all(shape) for i, (index, x) in enumerate(np.ndenumerate(array)): if i in list_values: @@ -727,7 +739,7 @@ def _jj(shape, list_values): n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" time = n.createDimension("time", 2) height = n.createDimension("height", 3) @@ -848,6 +860,9 @@ def _jj(shape, list_values): return filename +gathered = _make_gathered_file("gathered.nc") + + # -------------------------------------------------------------------- # Geometry files # -------------------------------------------------------------------- @@ -855,11 +870,12 @@ def _make_geometry_1_file(filename): """See n.comment for details.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" n.comment = ( - "Make a netCDF file with 2 node coordinates variables, each of " - "which has a corresponding auxiliary coordinate variable." + "Make a netCDF file with 2 node coordinates variables, " + "each of which has a corresponding auxiliary coordinate " + "variable." ) n.createDimension("time", 4) @@ -931,11 +947,12 @@ def _make_geometry_2_file(filename): """See n.comment for details.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" n.comment = ( - "A netCDF file with 3 node coordinates variables, only two of " - "which have a corresponding auxiliary coordinate variable." + "A netCDF file with 3 node coordinates variables, only " + "two of which have a corresponding auxiliary coordinate " + "variable." ) n.createDimension("time", 4) @@ -1011,16 +1028,18 @@ def _make_geometry_3_file(filename): """See n.comment for details.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" n.comment = ( - "A netCDF file with 3 node coordinates variables, each of which " - "contains only one point, only two of which have a corresponding " - "auxiliary coordinate variables. There is no node count variable." + "A netCDF file with 3 node coordinates variables, each of " + "which contains only one point, only two of which have a " + "corresponding auxiliary coordinate variables. There is no " + "node count variable." ) n.createDimension("time", 4) n.createDimension("instance", 3) + # node = n.createDimension('node' , 3) t = n.createVariable("time", "i4", ("time",)) t.units = "seconds since 2016-11-07 20:00 UTC" @@ -1087,11 +1106,11 @@ def _make_geometry_4_file(filename): """See n.comment for details.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" n.comment = ( - "A netCDF file with 2 node coordinates variables, none of which " - "have a corresponding auxiliary coordinate variable." + "A netCDF file with 2 node coordinates variables, none of " + "which have a corresponding auxiliary coordinate variable." ) n.createDimension("time", 4) @@ -1157,9 +1176,13 @@ def _make_interior_ring_file(filename): n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") # Global attributes - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" - n.comment = "TODO" + n.comment = ( + "A netCDF file with an interior ring variable of geometry " + "coordinates where x and y (but not z) are node " + "coordinates." + ) # Dimensions n.createDimension("time", 4) @@ -1234,7 +1257,7 @@ def _make_interior_ring_file(filename): datum.longitude_of_prime_meridian = 0.0 pr = n.createVariable("pr", "f8", ("instance", "time")) - pr.standard_name = "preciptitation_amount" + pr.standard_name = "precipitation_amount" pr.standard_units = "kg m-2" pr.coordinates = "time lat lon z instance_id" pr.grid_mapping = "datum" @@ -1257,9 +1280,12 @@ def _make_interior_ring_file_2(filename): n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") # Global attributes - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" - n.comment = "TODO" + n.comment = ( + "A netCDF file with an interior ring variable of geometry " + "coordinates where x, y and z are node coordinates." + ) # Dimensions n.createDimension("time", 4) @@ -1333,7 +1359,7 @@ def _make_interior_ring_file_2(filename): datum.longitude_of_prime_meridian = 0.0 pr = n.createVariable("pr", "f8", ("instance", "time")) - pr.standard_name = "preciptitation_amount" + pr.standard_name = "precipitation_amount" pr.standard_units = "kg m-2" pr.coordinates = "time lat lon z instance_id" pr.grid_mapping = "datum" @@ -1355,7 +1381,7 @@ def _make_string_char_file(filename): """See n.comment for details.""" n = netCDF4.Dataset(filename, "w", format="NETCDF4") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.comment = "A netCDF file with variables of string and char data types" n.createDimension("dim1", 1) @@ -1406,12 +1432,14 @@ def _make_string_char_file(filename): c_months1 = n.createVariable("c_months1", "S1", ("dim1", "strlen8")) c_months1.long_name = "char: One month" c_months1[:] = netCDF4.stringtochar(np.array(["December"], dtype="S8")) + c_months0 = n.createVariable("c_months0", "S1", ("strlen3",)) c_months0.long_name = "char: One month (scalar)" c_months0[:] = np.array(list("May")) c_numbers = n.createVariable("c_numbers", "S1", ("lat", "lon", "strlen5")) c_numbers.long_name = "char: Two dimensional" + np.empty((2, 3, 5), dtype="S1") c_numbers[...] = netCDF4.stringtochar(numbers) c_months4m = n.createVariable("c_months4m", "S1", ("time", "strlen7")) @@ -1424,73 +1452,6 @@ def _make_string_char_file(filename): return filename -def _make_broken_bounds_cdl(filename): - with open(filename, mode="w") as f: - f.write( - """netcdf broken_bounds { -dimensions: - lat = 180 ; - bnds = 2 ; - lon = 288 ; - time = UNLIMITED ; // (1825 currently) -variables: - double lat(lat) ; - lat:long_name = "latitude" ; - lat:units = "degrees_north" ; - lat:axis = "Y" ; - lat:bounds = "lat_bnds" ; - lat:standard_name = "latitude" ; - lat:cell_methods = "time: point" ; - double lat_bnds(lat, bnds) ; - lat_bnds:long_name = "latitude bounds" ; - lat_bnds:units = "degrees_north" ; - lat_bnds:axis = "Y" ; - double lon(lon) ; - lon:long_name = "longitude" ; - lon:units = "degrees_east" ; - lon:axis = "X" ; - lon:bounds = "lon_bnds" ; - lon:standard_name = "longitude" ; - lon:cell_methods = "time: point" ; - double lon_bnds(lon, bnds) ; - lon_bnds:long_name = "longitude bounds" ; - lon_bnds:units = "m" ; - lon_bnds:axis = "X" ; - float pr(time, lat, lon) ; - pr:long_name = "Precipitation" ; - pr:units = "kg m-2 s-1" ; - pr:missing_value = 1.e+20f ; - pr:_FillValue = 1.e+20f ; - pr:cell_methods = "area: time: mean" ; - pr:cell_measures = "area: areacella" ; - pr:standard_name = "precipitation_flux" ; - pr:interp_method = "conserve_order1" ; - pr:original_name = "pr" ; - double time(time) ; - time:long_name = "time" ; - time:units = "days since 1850-01-01 00:00:00" ; - time:axis = "T" ; - time:calendar_type = "noleap" ; - time:calendar = "noleap" ; - time:bounds = "time_bnds" ; - time:standard_name = "time" ; - time:description = "Temporal mean" ; - double time_bnds(time, bnds) ; - time_bnds:long_name = "time axis boundaries" ; - time_bnds:units = "days since 1850-01-01 00:00:00" ; - -// global attributes: - :external_variables = "areacella" ; - :Conventions = "CF-""" - + VN - + """" ; - :source = "model" ; - :comment = "Bounds variable has incompatible units to its parent coordinate variable" ; -} -""" - ) - - def _make_subsampled_1(filename): """Lossy compression by coordinate subsampling (1). @@ -5259,10354 +5220,271 @@ def _make_subsampled_2(filename): r[...] = np.arange(48 * 32).reshape(48, 32) n.close() - return filename -def _make_regrid_file(filename): - n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") +def _make_ugrid_1(filename): + """Create a UGRID file with a 2-d mesh topology.""" + n = netCDF4.Dataset(filename, "w") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN} UGRID-1.0" n.createDimension("time", 2) - n.createDimension("bounds2", 2) - n.createDimension("latitude", 30) - n.createDimension("longitude", 48) - n.createDimension("time_1", 1) - n.createDimension("lat", 73) - n.createDimension("lon", 96) - - latitude = n.createVariable("latitude", "f8", ("latitude",)) - latitude.standard_name = "latitude" - latitude.units = "degrees_north" - latitude.bounds = "latitude_bounds" - latitude[...] = np.arange(-87, 90.0, 6) - - longitude = n.createVariable("longitude", "f8", ("longitude",)) - longitude.standard_name = "longitude" - longitude.units = "degrees_east" - longitude.bounds = "longitude_bounds" - longitude[...] = np.arange(3.75, 360, 7.5) - - lat = n.createVariable("lat", "f8", ("lat",)) - lat.standard_name = "latitude" - lat.units = "degrees_north" - lat.bounds = "lat_bounds" - lat[...] = np.arange(-90, 91.0, 2.5) - - lon = n.createVariable("lon", "f8", ("lon",)) - lon.standard_name = "longitude" - lon.units = "degrees_east" - lon.bounds = "lon_bounds" - lon[...] = np.arange(3.75, 361, 3.75) + n.createDimension("nMesh2_node", 7) + n.createDimension("nMesh2_edge", 9) + n.createDimension("nMesh2_face", 3) + n.createDimension("Two", 2) + n.createDimension("Four", 4) + + Mesh2 = n.createVariable("Mesh2", "i4", ()) + Mesh2.cf_role = "mesh_topology" + Mesh2.topology_dimension = 2 + Mesh2.node_coordinates = "Mesh2_node_x Mesh2_node_y" + Mesh2.face_node_connectivity = "Mesh2_face_nodes" + Mesh2.edge_node_connectivity = "Mesh2_edge_nodes" + Mesh2.edge_dimension = "nMesh2_edge" + Mesh2.edge_coordinates = "Mesh2_edge_x Mesh2_edge_y" + Mesh2.face_coordinates = "Mesh2_face_x Mesh2_face_y" + Mesh2.face_edge_connectivity = "Mesh2_face_edges" + Mesh2.face_face_connectivity = "Mesh2_face_links" + Mesh2.edge_face_connectivity = "Mesh2_edge_face_links" + + Mesh2_face_nodes = n.createVariable( + "Mesh2_face_nodes", "i4", ("nMesh2_face", "Four"), fill_value=-99 + ) + Mesh2_face_nodes.long_name = "Maps every face to its corner nodes" + Mesh2_face_nodes[...] = [[2, 3, 1, 0], [4, 5, 3, 2], [1, 3, 6, -99]] - longitude_bounds = n.createVariable( - "longitude_bounds", "f8", ("longitude", "bounds2") + Mesh2_edge_nodes = n.createVariable( + "Mesh2_edge_nodes", "i4", ("Two", "nMesh2_edge") ) - longitude_bounds[..., 0] = longitude[...] - 3.75 - longitude_bounds[..., 1] = longitude[...] + 3.75 + Mesh2_edge_nodes.long_name = "Maps every edge to its two nodes" + Mesh2_edge_nodes[...] = [ + [1, 3, 3, 0, 2, 2, 2, 5, 3], + [6, 6, 1, 1, 0, 3, 4, 4, 5], + ] - latitude_bounds = n.createVariable( - "latitude_bounds", "f8", ("latitude", "bounds2") + # Optional mesh topology variables + Mesh2_face_edges = n.createVariable( + "Mesh2_face_edges", "i4", ("nMesh2_face", "Four"), fill_value=-99 ) - latitude_bounds[..., 0] = latitude[...] - 3 - latitude_bounds[..., 1] = latitude[...] + 3 + Mesh2_face_edges.long_name = "Maps every face to its edges." - lon_bounds = n.createVariable("lon_bounds", "f8", ("lon", "bounds2")) - lon_bounds[..., 0] = lon[...] - 1.875 - lon_bounds[..., 1] = lon[...] + 1.875 + Mesh2_face_links = n.createVariable( + "Mesh2_face_links", "i4", ("nMesh2_face", "Four"), fill_value=-99 + ) + Mesh2_face_links.long_name = "neighbour faces for faces" + Mesh2_face_links[...] = [ + [1, 2, -99, -99], + [0, -99, -99, -99], + [0, -99, -99, -99], + ] - lat_bounds = n.createVariable("lat_bounds", "f8", ("lat", "bounds2")) - lat_bounds[..., 0] = lat[...] - 1.25 - lat_bounds[..., 1] = lat[...] + 1.25 + Mesh2_edge_face_links = n.createVariable( + "Mesh2_edge_face_links", "i4", ("nMesh2_edge", "Two"), fill_value=-99 + ) + Mesh2_edge_face_links.long_name = "neighbour faces for edges" + + # Mesh node coordinates + Mesh2_node_x = n.createVariable("Mesh2_node_x", "f4", ("nMesh2_node",)) + Mesh2_node_x.standard_name = "longitude" + Mesh2_node_x.units = "degrees_east" + Mesh2_node_x[...] = [-45, -43, -45, -43, -45, -43, -40] + + Mesh2_node_y = n.createVariable("Mesh2_node_y", "f4", ("nMesh2_node",)) + Mesh2_node_y.standard_name = "latitude" + Mesh2_node_y.units = "degrees_north" + Mesh2_node_y[...] = [35, 35, 33, 33, 31, 31, 34] + + # Optional mesh face and edge coordinate variables + Mesh2_face_x = n.createVariable("Mesh2_face_x", "f4", ("nMesh2_face",)) + Mesh2_face_x.standard_name = "longitude" + Mesh2_face_x.units = "degrees_east" + Mesh2_face_x[...] = [-44, -44, -42] + + Mesh2_face_y = n.createVariable("Mesh2_face_y", "f4", ("nMesh2_face",)) + Mesh2_face_y.standard_name = "latitude" + Mesh2_face_y.units = "degrees_north" + Mesh2_face_y[...] = [34, 32, 34] + + Mesh2_edge_x = n.createVariable("Mesh2_edge_x", "f4", ("nMesh2_edge",)) + Mesh2_edge_x.standard_name = "longitude" + Mesh2_edge_x.units = "degrees_east" + Mesh2_edge_x[...] = [-41.5, -41.5, -43, -44, -45, -44, -45, -44, -43] + + Mesh2_edge_y = n.createVariable("Mesh2_edge_y", "f4", ("nMesh2_edge",)) + Mesh2_edge_y.standard_name = "latitude" + Mesh2_edge_y.units = "degrees_north" + Mesh2_edge_y[...] = [34.5, 33.5, 34, 35, 34, 33, 32, 31, 32] + + # Non-mesh coordinates + t = n.createVariable("time", "f8", ("time",)) + t.standard_name = "time" + t.units = "seconds since 2016-01-01 00:00:00" + t.bounds = "time_bounds" + t[...] = [43200, 129600] - time = n.createVariable("time", "f4", ("time",)) - time.standard_name = "time" - time.units = "days since 1860-1-1" - time.calendar = "360_day" - time.axis = "T" - time.bounds = "time_bounds" - time[...] = [15, 45] + t_bounds = n.createVariable("time_bounds", "f8", ("time", "Two")) + t_bounds[...] = [[0, 86400], [86400, 172800]] - time_bounds = n.createVariable("time_bounds", "f4", ("time", "bounds2")) - time_bounds[...] = [ - [ - 0, - 30, - ], - [30, 60], + # Data variables + ta = n.createVariable("ta", "f4", ("time", "nMesh2_face")) + ta.standard_name = "air_temperature" + ta.units = "K" + ta.mesh = "Mesh2" + ta.location = "face" + ta.coordinates = "Mesh2_face_x Mesh2_face_y" + ta[...] = [[282.96, 282.69, 283.21], [281.53, 280.99, 281.23]] + + v = n.createVariable("v", "f4", ("time", "nMesh2_edge")) + v.standard_name = "northward_wind" + v.units = "ms-1" + v.mesh = "Mesh2" + v.location = "edge" + v.coordinates = "Mesh2_edge_x Mesh2_edge_y" + v[...] = [ + [10.2, 10.63, 8.74, 9.05, 8.15, 10.89, 8.44, 10.66, 8.93], + [9.66, 10.74, 9.24, 10.58, 9.79, 10.27, 10.58, 11.68, 11.22], ] - time_1 = n.createVariable("time_1", "f4", ("time_1",)) - time_1.standard_name = "time" - time_1.units = "days since 1860-1-1" - time_1.calendar = "360_day" - time_1.axis = "T" - time_1.bounds = "time_1_bounds" - time_1[...] = 15 - - time_1_bounds = n.createVariable( - "time_1_bounds", "f4", ("time_1", "bounds2") - ) - time_1_bounds[...] = [0, 30] - - height = n.createVariable("height", "f8", ()) - height.units = "m" - height.standard_name = "height" - height.positive = "up" - height.axis = "Z" - height[...] = 2 - - src = n.createVariable("src", "f8", ("time", "latitude", "longitude")) - src.standard_name = "air_temperature" - src.units = "K" - src.coordinates = "height" - src.cell_methods = "time: mean" - - # Don't generate this data randomly - it's useful to see the real - # patterns of global temperature. - src[...] = [ - [ - [ - 243.6, - 242.4, - 241.8, - 241.5, - 241.2, - 240.8, - 240.5, - 240.4, - 240.2, - 240.5, - 241.2, - 241.9, - 242.5, - 243.0, - 243.4, - 244.1, - 245.2, - 246.4, - 247.0, - 247.4, - 248.3, - 250.2, - 251.6, - 252.3, - 253.9, - 255.8, - 257.6, - 258.7, - 258.5, - 257.7, - 256.8, - 255.6, - 254.1, - 252.0, - 251.6, - 252.4, - 254.0, - 255.1, - 254.8, - 254.3, - 253.4, - 252.6, - 251.5, - 249.9, - 248.3, - 246.4, - 244.9, - 244.2, - ], - [ - 243.3, - 241.0, - 239.8, - 238.3, - 237.4, - 237.4, - 238.9, - 240.3, - 241.3, - 241.2, - 241.2, - 240.8, - 241.0, - 242.6, - 244.4, - 245.5, - 246.4, - 248.2, - 249.8, - 251.0, - 254.3, - 260.7, - 265.4, - 266.1, - 265.9, - 265.9, - 266.4, - 266.1, - 265.3, - 264.2, - 262.8, - 261.8, - 261.0, - 258.7, - 256.9, - 257.3, - 259.5, - 262.7, - 264.4, - 264.9, - 265.4, - 264.2, - 264.7, - 263.4, - 258.3, - 251.8, - 247.2, - 245.4, - ], - [ - 248.6, - 245.7, - 245.6, - 244.7, - 243.3, - 243.3, - 244.2, - 245.2, - 249.4, - 251.7, - 248.8, - 245.0, - 243.0, - 244.0, - 245.7, - 245.2, - 244.7, - 246.0, - 246.9, - 248.7, - 252.4, - 257.6, - 266.8, - 269.5, - 269.7, - 270.1, - 270.5, - 270.6, - 270.2, - 269.2, - 266.7, - 266.1, - 266.7, - 267.5, - 267.3, - 266.2, - 266.5, - 268.1, - 267.3, - 267.5, - 271.5, - 271.5, - 271.5, - 271.0, - 267.6, - 261.5, - 255.5, - 252.0, - ], - [ - 270.3, - 269.9, - 269.6, - 268.4, - 266.0, - 264.0, - 258.8, - 256.8, - 262.0, - 265.2, - 263.6, - 257.3, - 254.3, - 253.9, - 255.6, - 256.9, - 255.9, - 253.9, - 254.5, - 261.0, - 263.8, - 267.9, - 270.5, - 271.8, - 272.3, - 272.6, - 273.4, - 273.9, - 273.7, - 273.6, - 273.3, - 273.4, - 273.9, - 273.8, - 273.8, - 273.6, - 274.4, - 274.6, - 271.1, - 268.0, - 271.2, - 271.5, - 271.4, - 271.3, - 271.6, - 271.5, - 270.8, - 270.5, - ], - [ - 274.5, - 274.3, - 274.4, - 274.3, - 274.5, - 274.5, - 273.4, - 273.1, - 273.2, - 273.3, - 273.0, - 271.5, - 271.6, - 272.5, - 272.8, - 272.8, - 272.6, - 272.5, - 272.1, - 272.5, - 273.5, - 273.6, - 274.0, - 274.8, - 274.9, - 275.0, - 275.0, - 275.0, - 274.9, - 274.7, - 274.8, - 274.9, - 275.1, - 275.2, - 275.5, - 275.8, - 276.0, - 276.2, - 276.0, - 273.4, - 272.8, - 273.9, - 274.5, - 274.6, - 274.7, - 274.7, - 274.7, - 274.6, - ], - [ - 275.3, - 275.2, - 275.4, - 275.3, - 275.4, - 275.7, - 275.7, - 275.8, - 275.9, - 275.5, - 274.9, - 274.5, - 274.3, - 274.6, - 274.7, - 275.0, - 275.2, - 275.3, - 275.5, - 275.6, - 276.0, - 276.7, - 277.0, - 276.8, - 276.8, - 277.0, - 277.1, - 276.8, - 276.7, - 277.1, - 277.0, - 277.4, - 277.3, - 277.2, - 277.2, - 277.3, - 277.5, - 278.0, - 278.8, - 279.1, - 278.3, - 278.2, - 277.8, - 277.3, - 277.4, - 276.9, - 276.4, - 275.8, - ], - [ - 278.6, - 278.7, - 278.4, - 278.0, - 277.8, - 278.0, - 278.3, - 278.2, - 278.1, - 278.0, - 278.7, - 279.4, - 279.1, - 279.2, - 279.1, - 278.9, - 279.0, - 279.6, - 279.8, - 280.1, - 280.2, - 280.6, - 281.1, - 280.0, - 280.0, - 280.5, - 281.2, - 281.0, - 280.8, - 281.3, - 281.6, - 281.4, - 281.1, - 280.7, - 280.3, - 280.1, - 280.3, - 280.7, - 280.8, - 283.0, - 281.4, - 280.3, - 279.6, - 279.4, - 279.7, - 279.9, - 279.1, - 278.6, - ], - [ - 283.9, - 283.2, - 282.5, - 281.8, - 281.6, - 282.0, - 282.4, - 282.3, - 282.3, - 283.0, - 284.3, - 284.2, - 284.0, - 283.7, - 283.6, - 283.7, - 284.0, - 284.2, - 284.3, - 285.0, - 285.2, - 285.1, - 285.2, - 286.9, - 287.3, - 286.5, - 286.2, - 285.7, - 285.6, - 285.7, - 285.5, - 285.2, - 285.0, - 284.5, - 284.0, - 283.4, - 283.0, - 283.1, - 283.0, - 288.0, - 285.5, - 285.2, - 284.0, - 283.2, - 283.4, - 284.7, - 284.9, - 284.6, - ], - [ - 289.3, - 288.6, - 288.9, - 289.4, - 288.8, - 289.1, - 289.5, - 289.3, - 289.2, - 289.6, - 289.4, - 288.9, - 288.4, - 288.1, - 288.0, - 288.2, - 288.3, - 288.4, - 289.4, - 290.3, - 291.6, - 290.6, - 290.8, - 291.8, - 291.8, - 290.8, - 290.1, - 290.1, - 290.7, - 290.5, - 290.1, - 289.9, - 289.6, - 289.1, - 288.5, - 287.6, - 286.8, - 286.9, - 287.5, - 294.9, - 293.2, - 291.4, - 289.9, - 289.6, - 290.1, - 290.5, - 290.3, - 289.8, - ], - [ - 292.5, - 292.3, - 293.5, - 292.9, - 296.4, - 295.3, - 294.8, - 294.7, - 294.4, - 293.9, - 293.1, - 292.5, - 291.8, - 291.3, - 291.9, - 293.9, - 292.1, - 293.6, - 297.5, - 295.3, - 295.3, - 295.4, - 295.1, - 294.6, - 294.5, - 294.0, - 294.0, - 294.6, - 295.1, - 295.1, - 295.1, - 295.0, - 294.7, - 294.1, - 293.3, - 292.0, - 290.6, - 289.5, - 293.3, - 301.4, - 299.6, - 296.1, - 294.7, - 294.5, - 294.6, - 294.8, - 294.0, - 293.0, - ], - [ - 293.4, - 293.4, - 295.6, - 293.0, - 297.2, - 299.2, - 298.2, - 297.2, - 296.2, - 295.3, - 294.6, - 294.2, - 294.3, - 294.2, - 295.8, - 298.7, - 297.5, - 299.4, - 300.3, - 298.5, - 297.7, - 297.8, - 297.4, - 297.0, - 296.9, - 296.7, - 297.0, - 297.1, - 297.3, - 297.4, - 297.2, - 296.8, - 296.2, - 295.6, - 294.8, - 293.6, - 292.1, - 290.7, - 290.9, - 297.7, - 300.3, - 297.1, - 298.1, - 296.9, - 295.9, - 295.2, - 294.4, - 293.7, - ], - [ - 295.2, - 295.6, - 295.6, - 295.3, - 298.0, - 300.5, - 298.8, - 298.5, - 297.4, - 296.6, - 296.5, - 296.6, - 296.7, - 296.8, - 298.6, - 301.8, - 301.4, - 299.2, - 299.3, - 299.3, - 300.0, - 299.5, - 299.1, - 299.1, - 298.9, - 298.8, - 299.0, - 299.0, - 298.9, - 298.5, - 297.5, - 296.3, - 295.0, - 294.3, - 293.7, - 292.8, - 292.0, - 292.6, - 290.3, - 292.7, - 299.9, - 296.4, - 297.0, - 297.4, - 296.1, - 295.3, - 294.6, - 294.5, - ], - [ - 297.4, - 296.9, - 294.4, - 294.9, - 296.7, - 300.0, - 299.4, - 299.2, - 298.7, - 298.7, - 299.3, - 299.7, - 299.7, - 299.8, - 300.7, - 301.9, - 301.3, - 300.1, - 301.1, - 301.1, - 301.4, - 301.1, - 300.7, - 300.3, - 300.3, - 300.6, - 300.5, - 300.4, - 300.0, - 299.4, - 298.2, - 296.5, - 294.9, - 293.7, - 292.9, - 292.9, - 293.9, - 293.8, - 289.6, - 297.1, - 298.9, - 296.4, - 296.1, - 298.7, - 297.6, - 296.6, - 296.1, - 296.4, - ], - [ - 300.5, - 299.9, - 295.9, - 295.1, - 295.0, - 299.8, - 300.3, - 300.1, - 300.2, - 300.4, - 300.7, - 300.8, - 301.0, - 301.1, - 301.5, - 301.9, - 302.1, - 302.2, - 302.0, - 300.7, - 301.9, - 301.7, - 301.5, - 301.3, - 301.3, - 301.3, - 301.1, - 300.9, - 300.6, - 300.0, - 299.0, - 297.6, - 296.4, - 295.7, - 295.5, - 296.0, - 297.4, - 295.2, - 298.4, - 300.2, - 298.8, - 298.2, - 297.6, - 299.5, - 299.2, - 298.7, - 298.8, - 299.4, - ], - [ - 301.4, - 299.9, - 298.7, - 296.7, - 294.5, - 299.3, - 300.2, - 300.1, - 300.2, - 300.5, - 300.6, - 300.7, - 301.1, - 300.2, - 301.2, - 300.7, - 301.7, - 302.3, - 300.1, - 300.7, - 300.8, - 300.6, - 300.4, - 300.1, - 299.8, - 299.5, - 299.0, - 298.5, - 298.2, - 297.9, - 297.5, - 297.2, - 297.1, - 297.3, - 297.8, - 298.5, - 299.2, - 297.0, - 300.9, - 300.5, - 300.2, - 299.5, - 299.7, - 300.3, - 300.4, - 300.5, - 300.7, - 301.0, - ], - [ - 301.7, - 298.3, - 298.4, - 297.1, - 296.4, - 299.3, - 299.3, - 299.7, - 300.1, - 300.2, - 300.1, - 300.5, - 301.3, - 299.8, - 301.0, - 299.6, - 301.9, - 301.9, - 301.7, - 301.3, - 300.8, - 300.5, - 300.2, - 300.0, - 299.6, - 299.3, - 299.2, - 298.9, - 298.6, - 298.5, - 298.3, - 298.3, - 298.4, - 298.8, - 299.2, - 299.6, - 299.7, - 297.9, - 298.3, - 299.1, - 300.2, - 299.9, - 299.9, - 300.2, - 300.6, - 301.0, - 301.3, - 301.3, - ], - [ - 296.5, - 296.5, - 298.6, - 298.1, - 297.5, - 291.4, - 294.6, - 298.1, - 298.8, - 299.6, - 297.7, - 299.7, - 300.3, - 299.2, - 300.4, - 301.3, - 299.5, - 300.9, - 300.8, - 300.5, - 300.2, - 299.9, - 299.5, - 299.3, - 299.1, - 299.1, - 299.1, - 299.0, - 299.0, - 299.1, - 299.3, - 299.4, - 299.8, - 299.8, - 299.3, - 299.6, - 299.1, - 298.2, - 298.7, - 300.1, - 299.2, - 299.0, - 298.6, - 298.6, - 299.0, - 299.9, - 298.2, - 297.5, - ], - [ - 296.9, - 296.4, - 296.6, - 295.1, - 297.1, - 293.1, - 290.8, - 295.7, - 296.5, - 297.2, - 293.9, - 297.9, - 297.6, - 293.8, - 295.8, - 298.8, - 298.2, - 298.9, - 298.9, - 299.1, - 298.9, - 298.2, - 297.5, - 296.9, - 296.6, - 296.3, - 296.2, - 296.0, - 296.0, - 296.4, - 296.8, - 297.1, - 297.6, - 298.7, - 296.1, - 294.9, - 296.5, - 298.6, - 298.3, - 298.6, - 298.2, - 297.6, - 297.2, - 296.9, - 296.7, - 297.3, - 297.9, - 297.5, - ], - [ - 290.4, - 289.2, - 288.8, - 289.8, - 293.2, - 290.9, - 288.7, - 291.5, - 295.4, - 292.9, - 289.2, - 292.3, - 289.4, - 284.6, - 287.3, - 293.6, - 294.7, - 293.8, - 294.2, - 295.1, - 295.9, - 296.3, - 296.0, - 295.3, - 294.6, - 294.0, - 293.4, - 293.0, - 292.7, - 292.7, - 292.7, - 292.7, - 293.2, - 293.8, - 284.9, - 292.3, - 296.2, - 295.6, - 296.5, - 297.3, - 296.9, - 296.4, - 295.8, - 295.4, - 295.3, - 294.3, - 292.3, - 292.0, - ], - [ - 287.2, - 286.3, - 287.4, - 286.9, - 287.2, - 284.8, - 288.8, - 285.3, - 285.1, - 284.1, - 282.3, - 276.0, - 275.1, - 274.6, - 277.2, - 277.9, - 286.6, - 288.8, - 289.0, - 289.7, - 290.8, - 291.9, - 292.5, - 292.4, - 292.0, - 291.4, - 290.7, - 290.2, - 289.9, - 290.0, - 290.0, - 289.9, - 289.7, - 284.1, - 279.8, - 286.4, - 287.8, - 291.0, - 293.1, - 293.7, - 294.0, - 294.2, - 294.1, - 293.8, - 293.4, - 293.4, - 289.8, - 287.6, - ], - [ - 280.6, - 285.5, - 288.7, - 288.8, - 286.8, - 281.0, - 278.3, - 276.1, - 275.1, - 274.5, - 259.3, - 252.7, - 252.3, - 264.4, - 271.9, - 273.4, - 278.4, - 279.7, - 279.1, - 284.5, - 286.3, - 287.4, - 287.9, - 288.1, - 287.9, - 287.4, - 287.1, - 287.1, - 287.3, - 287.4, - 287.5, - 286.6, - 280.6, - 274.1, - 274.0, - 274.2, - 274.0, - 280.3, - 288.9, - 290.1, - 290.6, - 290.9, - 291.3, - 291.6, - 291.5, - 291.3, - 289.6, - 281.6, - ], - [ - 283.4, - 283.8, - 281.9, - 278.6, - 274.7, - 270.8, - 275.5, - 275.8, - 273.4, - 263.9, - 261.0, - 262.5, - 259.6, - 261.2, - 263.1, - 263.7, - 257.6, - 263.7, - 270.0, - 273.9, - 278.9, - 279.9, - 281.1, - 282.0, - 282.3, - 282.1, - 282.3, - 283.0, - 283.8, - 284.4, - 284.7, - 279.8, - 267.4, - 263.8, - 267.3, - 266.6, - 266.9, - 269.3, - 279.5, - 284.7, - 286.3, - 286.8, - 287.5, - 288.5, - 289.0, - 288.4, - 285.2, - 278.3, - ], - [ - 274.1, - 268.7, - 266.7, - 267.4, - 275.8, - 272.9, - 272.3, - 266.8, - 265.1, - 263.9, - 260.3, - 255.9, - 254.0, - 254.2, - 254.2, - 251.0, - 246.6, - 247.9, - 260.6, - 268.4, - 272.8, - 274.1, - 275.4, - 275.8, - 276.4, - 277.3, - 278.2, - 279.1, - 280.3, - 281.3, - 282.2, - 276.3, - 266.5, - 261.3, - 260.0, - 260.9, - 266.2, - 262.4, - 264.5, - 274.0, - 276.3, - 278.6, - 281.2, - 283.9, - 285.7, - 285.4, - 284.7, - 279.6, - ], - [ - 272.7, - 265.5, - 263.2, - 260.9, - 259.3, - 258.8, - 258.3, - 257.4, - 256.2, - 256.5, - 257.0, - 255.2, - 252.8, - 252.1, - 249.8, - 244.3, - 241.6, - 242.7, - 251.1, - 267.8, - 266.7, - 266.4, - 271.4, - 272.1, - 273.2, - 274.5, - 275.8, - 276.8, - 278.2, - 279.6, - 278.7, - 268.7, - 262.3, - 257.8, - 255.5, - 256.1, - 257.5, - 260.0, - 258.8, - 260.5, - 268.1, - 275.4, - 278.5, - 280.7, - 282.4, - 283.3, - 281.9, - 275.9, - ], - [ - 276.9, - 267.1, - 268.1, - 255.3, - 249.1, - 247.3, - 247.4, - 249.0, - 248.9, - 249.3, - 249.4, - 250.5, - 250.6, - 247.2, - 243.4, - 239.2, - 236.3, - 236.0, - 243.7, - 256.7, - 256.2, - 249.1, - 261.6, - 264.2, - 268.2, - 271.2, - 270.2, - 272.0, - 273.9, - 273.1, - 266.5, - 262.1, - 260.5, - 256.4, - 251.8, - 250.0, - 252.0, - 257.4, - 252.0, - 254.8, - 270.0, - 275.2, - 278.1, - 280.0, - 281.3, - 282.1, - 282.0, - 275.4, - ], - [ - 274.8, - 263.2, - 258.6, - 246.4, - 242.3, - 240.9, - 240.9, - 240.7, - 238.5, - 238.7, - 239.0, - 239.7, - 238.6, - 235.6, - 233.2, - 230.9, - 229.4, - 231.4, - 234.8, - 238.1, - 238.7, - 241.6, - 244.2, - 247.5, - 257.9, - 264.2, - 254.9, - 256.2, - 256.3, - 254.9, - 255.7, - 253.4, - 251.6, - 249.1, - 245.6, - 245.1, - 247.2, - 248.4, - 248.9, - 257.5, - 270.1, - 259.8, - 265.1, - 275.8, - 277.7, - 274.3, - 278.0, - 278.9, - ], - [ - 273.2, - 268.4, - 256.0, - 247.6, - 250.2, - 244.8, - 239.2, - 234.9, - 234.6, - 230.2, - 229.6, - 230.8, - 231.4, - 229.9, - 228.3, - 227.7, - 228.3, - 230.0, - 230.5, - 231.3, - 231.8, - 235.8, - 238.5, - 239.8, - 242.0, - 246.1, - 248.1, - 244.2, - 246.0, - 247.0, - 246.2, - 243.8, - 242.5, - 241.8, - 242.7, - 241.8, - 241.7, - 242.4, - 245.5, - 254.0, - 263.4, - 250.4, - 244.5, - 248.4, - 255.2, - 261.2, - 267.8, - 270.9, - ], - [ - 268.5, - 269.1, - 268.5, - 265.7, - 262.3, - 243.8, - 236.0, - 236.2, - 235.6, - 235.6, - 234.7, - 229.1, - 228.7, - 227.4, - 228.8, - 233.2, - 233.0, - 235.3, - 235.9, - 236.5, - 236.9, - 237.3, - 237.9, - 238.7, - 239.2, - 239.6, - 241.5, - 239.2, - 239.7, - 240.2, - 241.2, - 240.7, - 240.0, - 240.8, - 242.7, - 240.8, - 239.1, - 245.3, - 251.1, - 258.6, - 247.1, - 239.5, - 237.2, - 235.4, - 241.2, - 246.8, - 247.9, - 258.9, - ], - [ - 259.5, - 258.8, - 258.0, - 253.9, - 243.8, - 239.9, - 238.3, - 237.7, - 235.8, - 233.8, - 233.3, - 233.2, - 234.4, - 232.8, - 234.4, - 236.1, - 236.5, - 237.4, - 237.5, - 237.6, - 237.8, - 236.8, - 236.3, - 236.2, - 236.1, - 235.9, - 235.7, - 235.3, - 234.9, - 234.6, - 235.6, - 236.4, - 236.5, - 236.5, - 237.7, - 239.6, - 235.8, - 232.6, - 234.3, - 238.0, - 236.9, - 235.3, - 233.4, - 235.4, - 238.5, - 241.4, - 243.3, - 252.2, - ], - [ - 235.4, - 235.8, - 236.3, - 235.0, - 234.9, - 236.2, - 235.8, - 235.5, - 236.3, - 236.6, - 235.6, - 235.9, - 236.6, - 236.9, - 237.2, - 237.9, - 238.5, - 238.7, - 238.6, - 238.4, - 238.2, - 238.1, - 237.9, - 237.2, - 236.7, - 236.2, - 235.8, - 235.2, - 234.3, - 233.1, - 232.7, - 233.3, - 233.8, - 233.0, - 232.9, - 233.1, - 234.2, - 233.5, - 233.0, - 234.8, - 234.5, - 234.2, - 234.7, - 236.3, - 237.7, - 238.0, - 236.8, - 236.1, - ], - ], + pa = n.createVariable("pa", "f4", ("time", "nMesh2_node")) + pa.standard_name = "air_pressure" + pa.units = "hPa" + pa.mesh = "Mesh2" + pa.location = "node" + pa.coordinates = "Mesh2_node_x Mesh2_node_y" + pa[...] = [ + [999.67, 1006.45, 999.85, 1006.55, 1006.14, 1005.68, 999.48], [ - [ - 242.9, - 242.6, - 241.9, - 242.3, - 240.2, - 240.4, - 240.3, - 241.3, - 240.0, - 239.5, - 242.1, - 241.0, - 243.0, - 243.7, - 244.3, - 243.8, - 244.9, - 246.1, - 247.5, - 247.8, - 248.7, - 249.7, - 250.9, - 253.1, - 254.7, - 256.3, - 257.2, - 259.0, - 258.2, - 257.7, - 256.6, - 255.9, - 254.4, - 251.2, - 250.6, - 252.5, - 254.6, - 254.5, - 255.0, - 254.9, - 253.3, - 253.3, - 251.8, - 249.6, - 248.8, - 247.2, - 244.7, - 244.3, - ], - [ - 243.6, - 240.8, - 239.4, - 237.9, - 237.0, - 237.5, - 239.4, - 239.8, - 241.0, - 240.8, - 240.8, - 241.1, - 240.8, - 241.7, - 245.4, - 246.1, - 246.6, - 249.1, - 250.7, - 250.8, - 254.7, - 260.1, - 265.0, - 266.1, - 265.4, - 265.8, - 265.6, - 266.5, - 266.2, - 263.3, - 262.6, - 261.4, - 261.5, - 258.0, - 256.7, - 256.6, - 260.0, - 262.8, - 264.3, - 265.7, - 265.2, - 264.2, - 265.5, - 263.1, - 257.6, - 252.6, - 246.3, - 245.4, - ], - [ - 248.9, - 245.5, - 246.5, - 244.3, - 242.8, - 243.0, - 243.2, - 244.5, - 249.7, - 252.2, - 249.1, - 244.7, - 242.8, - 244.1, - 245.0, - 245.6, - 245.6, - 245.5, - 247.5, - 249.4, - 252.2, - 257.6, - 266.6, - 269.0, - 270.0, - 271.0, - 270.0, - 270.2, - 271.0, - 269.2, - 266.4, - 266.2, - 266.8, - 267.2, - 268.0, - 265.9, - 266.2, - 268.4, - 268.2, - 267.6, - 271.8, - 271.2, - 272.1, - 271.8, - 267.5, - 261.8, - 255.2, - 252.6, - ], - [ - 270.4, - 269.6, - 269.1, - 267.5, - 266.9, - 264.2, - 259.1, - 256.5, - 262.1, - 266.1, - 263.7, - 256.9, - 255.1, - 253.5, - 255.6, - 256.0, - 256.6, - 254.5, - 253.5, - 261.3, - 264.7, - 268.4, - 271.0, - 271.3, - 271.5, - 273.1, - 273.2, - 273.8, - 272.9, - 274.1, - 272.8, - 273.2, - 273.5, - 274.4, - 274.7, - 274.6, - 275.0, - 275.3, - 271.3, - 267.3, - 271.9, - 271.2, - 271.7, - 272.3, - 271.2, - 270.5, - 271.0, - 270.8, - ], - [ - 274.9, - 273.8, - 273.7, - 273.6, - 274.8, - 275.0, - 272.5, - 273.9, - 274.2, - 273.1, - 272.3, - 270.9, - 272.0, - 273.3, - 272.1, - 272.1, - 271.9, - 273.3, - 271.6, - 272.1, - 272.6, - 273.1, - 273.3, - 275.7, - 275.6, - 274.0, - 275.1, - 274.8, - 274.0, - 275.1, - 275.1, - 275.7, - 274.1, - 276.1, - 275.1, - 275.3, - 276.5, - 275.4, - 275.2, - 274.4, - 273.3, - 274.3, - 274.7, - 274.3, - 274.9, - 273.7, - 274.4, - 274.1, - ], - [ - 275.7, - 275.4, - 275.7, - 275.7, - 275.1, - 276.0, - 275.5, - 276.2, - 276.8, - 276.4, - 274.5, - 274.0, - 275.1, - 274.5, - 273.7, - 274.0, - 274.7, - 274.8, - 275.1, - 275.3, - 275.5, - 277.2, - 277.0, - 276.6, - 277.7, - 277.5, - 276.2, - 277.7, - 275.8, - 277.9, - 277.5, - 276.7, - 277.8, - 276.8, - 277.9, - 277.7, - 278.0, - 278.0, - 279.4, - 278.9, - 278.5, - 278.0, - 278.3, - 277.3, - 276.7, - 276.7, - 277.3, - 276.1, - ], - [ - 278.9, - 277.7, - 279.3, - 277.1, - 278.2, - 277.6, - 278.7, - 277.7, - 277.1, - 278.5, - 279.2, - 279.3, - 280.0, - 279.7, - 278.1, - 278.9, - 278.1, - 279.8, - 279.1, - 279.4, - 279.6, - 280.6, - 282.1, - 280.3, - 279.7, - 280.1, - 281.1, - 281.5, - 281.2, - 280.9, - 281.0, - 281.9, - 281.7, - 281.3, - 279.7, - 281.1, - 280.6, - 279.9, - 281.6, - 283.4, - 281.9, - 281.2, - 279.1, - 278.9, - 279.7, - 279.8, - 280.1, - 277.7, - ], - [ - 283.2, - 283.7, - 283.3, - 281.8, - 280.9, - 282.2, - 281.4, - 282.6, - 282.6, - 283.4, - 284.8, - 284.4, - 283.7, - 283.1, - 283.2, - 283.2, - 283.5, - 283.5, - 284.9, - 284.3, - 284.3, - 285.9, - 285.9, - 286.5, - 287.0, - 286.6, - 285.7, - 285.0, - 286.4, - 285.3, - 286.5, - 284.7, - 285.6, - 284.4, - 284.4, - 284.4, - 283.2, - 282.3, - 282.4, - 287.8, - 285.9, - 285.2, - 284.4, - 282.4, - 283.1, - 284.1, - 284.7, - 283.6, - ], - [ - 289.1, - 288.0, - 288.6, - 289.2, - 288.4, - 290.0, - 289.4, - 290.2, - 289.0, - 290.2, - 288.5, - 288.4, - 288.8, - 288.6, - 288.0, - 288.2, - 287.6, - 288.3, - 289.4, - 290.0, - 292.5, - 290.1, - 290.6, - 291.5, - 290.9, - 291.0, - 290.4, - 289.9, - 290.0, - 290.9, - 289.8, - 289.7, - 289.9, - 289.9, - 289.0, - 288.4, - 286.7, - 286.6, - 287.3, - 294.3, - 294.1, - 291.8, - 289.5, - 288.8, - 289.5, - 290.4, - 290.2, - 289.5, - ], - [ - 292.9, - 292.7, - 294.2, - 292.1, - 295.9, - 295.1, - 294.9, - 295.3, - 294.4, - 293.0, - 294.0, - 292.5, - 291.2, - 291.9, - 291.3, - 293.6, - 291.4, - 292.9, - 298.4, - 294.4, - 294.8, - 296.3, - 294.1, - 295.1, - 294.3, - 293.7, - 293.0, - 295.5, - 294.5, - 295.8, - 294.5, - 294.1, - 295.2, - 294.6, - 292.5, - 291.9, - 290.4, - 290.1, - 293.1, - 301.1, - 300.5, - 296.7, - 294.3, - 295.1, - 294.9, - 295.8, - 293.6, - 292.4, - ], - [ - 292.9, - 293.7, - 294.7, - 292.5, - 296.6, - 299.8, - 297.2, - 297.8, - 297.0, - 294.5, - 293.6, - 294.1, - 295.3, - 294.8, - 296.5, - 299.1, - 297.0, - 298.8, - 301.1, - 298.9, - 297.6, - 298.1, - 298.1, - 296.9, - 296.0, - 297.2, - 296.4, - 297.4, - 298.0, - 297.6, - 296.7, - 296.0, - 297.0, - 296.5, - 293.8, - 294.4, - 293.0, - 290.0, - 291.8, - 297.6, - 300.3, - 296.8, - 297.4, - 296.3, - 295.4, - 295.0, - 294.4, - 294.4, - ], - [ - 294.2, - 296.5, - 295.0, - 294.8, - 298.9, - 301.2, - 298.0, - 297.8, - 297.7, - 297.2, - 296.2, - 296.7, - 295.8, - 297.6, - 298.9, - 301.0, - 301.3, - 299.2, - 299.8, - 298.9, - 299.2, - 298.7, - 299.5, - 299.7, - 299.2, - 299.6, - 298.3, - 298.5, - 299.4, - 298.3, - 297.4, - 297.2, - 294.1, - 294.2, - 293.2, - 293.0, - 291.2, - 293.2, - 289.3, - 293.7, - 300.1, - 296.6, - 297.9, - 298.4, - 296.4, - 295.5, - 293.9, - 293.7, - ], - [ - 297.7, - 297.4, - 294.5, - 294.2, - 295.9, - 300.2, - 300.0, - 299.2, - 298.5, - 298.0, - 298.8, - 300.5, - 300.2, - 300.3, - 300.8, - 302.5, - 301.6, - 300.5, - 300.4, - 301.9, - 302.1, - 300.4, - 300.7, - 301.3, - 300.3, - 301.6, - 300.2, - 301.3, - 300.6, - 298.6, - 298.8, - 297.1, - 295.0, - 293.9, - 292.3, - 293.1, - 294.7, - 293.0, - 289.4, - 296.3, - 298.8, - 296.1, - 295.2, - 297.8, - 297.1, - 296.7, - 296.4, - 296.2, - ], - [ - 300.9, - 300.6, - 295.8, - 294.8, - 294.3, - 298.8, - 300.8, - 299.5, - 299.8, - 300.7, - 299.8, - 301.8, - 300.1, - 302.0, - 301.2, - 301.7, - 301.6, - 303.0, - 301.8, - 301.0, - 302.5, - 301.1, - 301.4, - 300.5, - 302.0, - 300.5, - 300.9, - 300.5, - 300.0, - 300.5, - 298.4, - 297.2, - 295.7, - 296.2, - 294.8, - 295.3, - 298.1, - 295.8, - 298.0, - 299.8, - 298.9, - 298.9, - 296.7, - 298.8, - 298.5, - 299.0, - 298.4, - 299.5, - ], - [ - 300.4, - 298.9, - 298.5, - 296.9, - 294.5, - 299.1, - 300.3, - 300.9, - 299.8, - 299.7, - 301.0, - 301.5, - 301.9, - 300.9, - 302.0, - 301.2, - 302.0, - 302.0, - 300.1, - 300.1, - 300.2, - 299.8, - 301.2, - 300.2, - 299.4, - 299.9, - 299.0, - 298.7, - 297.6, - 298.2, - 298.4, - 298.0, - 297.5, - 296.6, - 297.5, - 297.6, - 300.0, - 297.6, - 301.7, - 299.9, - 300.8, - 300.3, - 300.6, - 300.6, - 299.8, - 300.4, - 300.1, - 301.7, - ], - [ - 300.8, - 297.9, - 298.4, - 296.2, - 296.4, - 298.5, - 299.0, - 299.9, - 299.2, - 300.2, - 300.1, - 301.3, - 301.3, - 299.8, - 300.0, - 299.7, - 301.7, - 301.9, - 301.5, - 302.0, - 299.9, - 300.8, - 300.8, - 299.0, - 300.0, - 300.0, - 299.1, - 299.4, - 298.5, - 298.9, - 297.4, - 298.9, - 298.3, - 298.2, - 298.7, - 298.7, - 299.5, - 297.3, - 298.7, - 299.8, - 300.7, - 299.8, - 300.5, - 300.8, - 300.7, - 301.3, - 301.1, - 300.3, - ], - [ - 296.6, - 295.8, - 297.6, - 298.2, - 298.2, - 290.9, - 294.3, - 297.5, - 297.9, - 298.7, - 297.3, - 300.1, - 300.3, - 298.3, - 299.8, - 301.3, - 299.0, - 300.6, - 301.3, - 300.6, - 301.1, - 299.9, - 299.2, - 300.3, - 299.1, - 298.7, - 299.0, - 299.7, - 299.8, - 298.2, - 299.5, - 300.3, - 299.6, - 299.6, - 299.9, - 298.9, - 300.0, - 297.6, - 297.8, - 299.6, - 299.2, - 298.7, - 298.9, - 298.2, - 299.4, - 300.4, - 298.8, - 297.0, - ], - [ - 296.9, - 296.6, - 296.4, - 295.2, - 297.7, - 292.7, - 289.9, - 296.6, - 295.7, - 297.8, - 294.4, - 298.8, - 297.4, - 294.0, - 296.1, - 298.2, - 297.8, - 299.4, - 298.7, - 298.4, - 299.4, - 298.7, - 296.9, - 296.5, - 295.9, - 295.3, - 296.4, - 296.5, - 296.9, - 295.6, - 297.0, - 296.2, - 296.8, - 297.9, - 295.1, - 294.6, - 295.8, - 298.3, - 298.7, - 297.9, - 297.6, - 297.2, - 297.4, - 297.9, - 296.4, - 296.4, - 298.9, - 296.7, - ], - [ - 291.3, - 290.2, - 288.0, - 289.4, - 292.3, - 290.7, - 288.5, - 290.8, - 294.6, - 292.4, - 289.6, - 291.6, - 289.7, - 284.3, - 288.0, - 294.4, - 293.9, - 294.1, - 293.4, - 295.5, - 295.5, - 296.2, - 295.2, - 294.9, - 293.9, - 294.2, - 292.7, - 292.6, - 293.6, - 291.7, - 292.2, - 293.6, - 292.9, - 294.3, - 285.4, - 293.1, - 295.9, - 295.9, - 296.1, - 297.0, - 297.1, - 297.1, - 295.0, - 296.2, - 296.1, - 294.4, - 292.3, - 292.1, - ], - [ - 287.1, - 286.7, - 286.8, - 287.3, - 288.0, - 285.4, - 288.1, - 284.9, - 285.0, - 283.9, - 283.1, - 276.6, - 274.9, - 275.4, - 276.8, - 278.7, - 285.8, - 289.5, - 288.4, - 289.9, - 290.2, - 292.1, - 291.7, - 293.1, - 291.6, - 290.8, - 291.0, - 290.7, - 289.5, - 290.8, - 290.5, - 289.8, - 289.7, - 284.3, - 280.6, - 285.4, - 287.7, - 290.5, - 292.7, - 293.9, - 294.3, - 294.3, - 293.7, - 293.2, - 294.4, - 292.7, - 289.9, - 287.8, - ], - [ - 280.2, - 285.6, - 288.6, - 289.1, - 287.3, - 280.4, - 277.4, - 276.9, - 274.4, - 273.7, - 259.0, - 252.1, - 252.8, - 264.0, - 272.6, - 274.0, - 278.0, - 279.8, - 278.2, - 283.8, - 286.2, - 286.4, - 287.5, - 288.6, - 287.6, - 286.8, - 287.2, - 287.6, - 288.1, - 287.8, - 288.5, - 286.2, - 280.6, - 273.6, - 274.5, - 274.8, - 273.3, - 280.8, - 288.7, - 290.5, - 289.6, - 291.6, - 291.4, - 291.5, - 292.3, - 291.7, - 290.0, - 281.8, - ], - [ - 283.8, - 283.9, - 282.7, - 279.1, - 274.5, - 270.1, - 276.5, - 276.1, - 273.0, - 264.4, - 261.2, - 262.8, - 258.6, - 260.2, - 262.5, - 264.3, - 257.8, - 262.8, - 270.9, - 273.5, - 279.0, - 280.7, - 280.7, - 282.3, - 282.4, - 282.9, - 281.4, - 284.0, - 282.9, - 284.8, - 284.4, - 280.3, - 267.5, - 264.5, - 267.8, - 265.6, - 266.5, - 269.4, - 279.7, - 284.7, - 285.4, - 286.4, - 286.7, - 288.1, - 289.2, - 288.4, - 284.5, - 278.6, - ], - [ - 274.9, - 269.1, - 266.5, - 266.7, - 275.0, - 272.5, - 272.6, - 267.4, - 265.5, - 263.8, - 259.4, - 256.3, - 253.8, - 253.2, - 255.1, - 251.7, - 247.0, - 248.7, - 261.2, - 268.9, - 272.9, - 274.4, - 275.9, - 275.2, - 276.7, - 277.9, - 278.2, - 279.5, - 280.6, - 280.5, - 281.7, - 276.6, - 265.7, - 261.9, - 261.0, - 260.2, - 265.8, - 262.9, - 264.8, - 274.3, - 276.4, - 278.3, - 280.9, - 283.7, - 286.2, - 285.0, - 284.4, - 278.7, - ], - [ - 272.0, - 266.1, - 263.0, - 260.3, - 258.5, - 258.8, - 258.1, - 257.8, - 256.0, - 256.2, - 257.7, - 255.5, - 252.9, - 251.7, - 250.8, - 243.5, - 241.2, - 243.1, - 250.8, - 268.8, - 266.8, - 267.2, - 271.3, - 272.6, - 273.9, - 273.7, - 275.7, - 275.9, - 277.4, - 280.2, - 279.5, - 268.3, - 262.4, - 258.0, - 255.4, - 255.9, - 257.5, - 259.5, - 259.1, - 259.6, - 269.1, - 275.4, - 278.9, - 279.8, - 282.9, - 283.3, - 282.8, - 275.7, - ], - [ - 277.0, - 267.4, - 267.9, - 255.7, - 248.9, - 247.9, - 248.2, - 249.4, - 249.1, - 249.9, - 248.7, - 249.5, - 250.6, - 247.9, - 244.2, - 238.8, - 236.1, - 236.8, - 244.3, - 257.5, - 255.3, - 248.7, - 262.3, - 265.0, - 268.4, - 271.1, - 270.5, - 272.9, - 274.2, - 273.9, - 266.9, - 261.4, - 260.6, - 257.2, - 252.1, - 249.6, - 251.7, - 256.5, - 251.5, - 254.6, - 269.6, - 274.4, - 279.0, - 279.3, - 281.8, - 281.5, - 282.5, - 274.8, - ], - [ - 274.1, - 262.4, - 258.9, - 246.6, - 242.9, - 240.3, - 240.1, - 240.9, - 239.4, - 239.1, - 239.1, - 239.6, - 237.9, - 236.6, - 233.5, - 230.9, - 228.4, - 231.3, - 235.3, - 238.9, - 238.5, - 242.5, - 244.2, - 247.1, - 257.4, - 264.3, - 255.1, - 257.1, - 256.5, - 255.2, - 256.2, - 253.6, - 251.6, - 248.7, - 245.2, - 246.0, - 247.4, - 248.1, - 249.6, - 257.7, - 269.3, - 259.9, - 265.2, - 274.8, - 277.1, - 273.9, - 277.3, - 279.2, - ], - [ - 272.7, - 268.2, - 256.6, - 248.0, - 249.9, - 245.1, - 240.2, - 234.0, - 234.0, - 231.0, - 229.3, - 231.7, - 231.6, - 229.1, - 228.7, - 228.7, - 228.6, - 230.4, - 231.1, - 231.5, - 232.6, - 236.6, - 238.2, - 240.7, - 241.6, - 245.2, - 247.1, - 244.1, - 246.0, - 248.0, - 245.9, - 242.9, - 242.4, - 241.6, - 242.4, - 241.2, - 242.2, - 241.6, - 245.7, - 254.9, - 263.2, - 250.5, - 243.6, - 248.7, - 254.6, - 261.3, - 267.8, - 271.1, - ], - [ - 267.5, - 269.3, - 267.7, - 265.7, - 262.4, - 242.9, - 236.2, - 235.2, - 235.7, - 235.3, - 233.8, - 228.7, - 229.6, - 227.4, - 229.6, - 232.4, - 233.1, - 236.1, - 236.5, - 237.0, - 236.0, - 238.1, - 238.4, - 239.0, - 239.5, - 239.2, - 242.1, - 238.3, - 238.9, - 239.6, - 241.2, - 240.3, - 239.0, - 240.0, - 242.4, - 239.9, - 238.9, - 245.3, - 251.6, - 259.2, - 247.7, - 238.6, - 236.8, - 236.2, - 240.5, - 246.3, - 248.2, - 259.4, - ], - [ - 258.7, - 258.8, - 258.6, - 254.5, - 243.2, - 240.2, - 239.2, - 238.7, - 235.0, - 233.4, - 233.7, - 233.9, - 235.1, - 233.6, - 234.7, - 236.3, - 235.6, - 237.5, - 237.8, - 236.8, - 237.7, - 236.3, - 235.8, - 237.0, - 235.2, - 234.9, - 235.7, - 235.3, - 235.5, - 235.0, - 235.9, - 236.9, - 236.8, - 237.2, - 238.5, - 238.8, - 236.7, - 232.0, - 235.2, - 237.8, - 237.1, - 236.2, - 233.4, - 235.0, - 239.1, - 240.4, - 242.8, - 253.1, - ], - [ - 235.7, - 235.5, - 235.4, - 235.1, - 234.4, - 236.7, - 235.6, - 234.8, - 236.0, - 236.9, - 235.7, - 235.4, - 237.2, - 237.8, - 237.3, - 237.0, - 237.5, - 239.1, - 239.4, - 238.7, - 237.5, - 238.9, - 238.8, - 237.6, - 235.8, - 236.8, - 236.4, - 235.0, - 234.6, - 233.8, - 233.6, - 233.5, - 233.2, - 233.9, - 233.4, - 233.1, - 234.8, - 234.0, - 233.3, - 234.8, - 235.3, - 234.6, - 233.7, - 235.7, - 238.6, - 239.0, - 237.2, - 236.8, - ], + 1003.48, + 1006.42, + 1000.83, + 1002.98, + 1008.28, + 1002.97, + 1002.47, ], ] - dst = n.createVariable("dst", "f4", ("time_1", "lat", "lon")) - dst.standard_name = "air_temperature" - dst.units = "K" - dst.cell_methods = "time_1: mean" + n.close() + return filename - # Don't generate this data randomly - it's useful to see the real - # patterns of global temperature. - dst[...] = [ - [ - [ - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - ], - [ - 243.6, - 243.3, - 243.0, - 242.7, - 242.5, - 242.5, - 242.5, - 242.4, - 242.5, - 242.5, - 242.4, - 242.2, - 241.9, - 241.7, - 241.5, - 241.4, - 241.4, - 241.4, - 241.4, - 241.4, - 241.5, - 241.7, - 241.8, - 241.9, - 242.1, - 242.1, - 241.9, - 241.9, - 241.8, - 242.0, - 242.2, - 242.5, - 243.2, - 243.7, - 244.1, - 244.5, - 244.8, - 244.8, - 244.7, - 244.8, - 244.9, - 245.1, - 245.7, - 246.3, - 247.6, - 248.1, - 248.3, - 247.9, - 247.2, - 246.8, - 246.9, - 247.3, - 247.9, - 248.2, - 248.5, - 249.1, - 249.6, - 249.8, - 250.1, - 250.5, - 250.8, - 250.7, - 250.8, - 250.3, - 250.3, - 250.2, - 249.8, - 249.6, - 249.8, - 250.3, - 250.7, - 251.2, - 252.0, - 252.7, - 252.5, - 251.5, - 250.5, - 249.8, - 248.6, - 248.0, - 247.9, - 248.2, - 248.3, - 248.3, - 248.2, - 247.8, - 247.5, - 247.0, - 246.5, - 246.4, - 246.0, - 245.4, - 244.9, - 244.4, - 244.0, - 243.8, - ], - [ - 244.1, - 243.6, - 242.8, - 241.9, - 241.3, - 241.0, - 240.8, - 240.8, - 240.5, - 240.2, - 239.8, - 239.5, - 239.4, - 239.5, - 239.5, - 239.3, - 239.1, - 239.0, - 239.1, - 239.5, - 240.1, - 240.7, - 241.2, - 241.7, - 242.0, - 242.5, - 243.1, - 243.5, - 243.6, - 243.9, - 244.3, - 244.8, - 245.3, - 246.0, - 246.8, - 247.5, - 248.0, - 248.4, - 248.7, - 248.9, - 249.3, - 250.2, - 251.5, - 252.8, - 253.6, - 254.2, - 254.3, - 255.1, - 256.9, - 258.4, - 259.8, - 261.2, - 262.7, - 264.0, - 264.8, - 265.3, - 265.1, - 264.4, - 263.6, - 262.6, - 261.6, - 261.1, - 260.4, - 259.4, - 258.2, - 257.2, - 255.3, - 253.5, - 252.7, - 252.5, - 253.0, - 253.5, - 254.1, - 255.2, - 257.1, - 258.0, - 258.3, - 258.3, - 258.7, - 258.5, - 257.9, - 257.0, - 256.0, - 255.5, - 255.0, - 254.2, - 252.9, - 251.7, - 250.8, - 249.6, - 248.3, - 246.9, - 245.7, - 245.0, - 244.5, - 244.3, - ], - [ - 244.9, - 243.6, - 242.0, - 240.5, - 239.4, - 238.6, - 238.1, - 237.5, - 237.2, - 237.1, - 236.9, - 236.8, - 237.1, - 237.6, - 237.9, - 237.9, - 237.9, - 237.8, - 237.6, - 237.9, - 238.6, - 239.3, - 239.9, - 240.2, - 241.1, - 242.4, - 243.5, - 244.0, - 244.7, - 245.4, - 245.9, - 246.1, - 246.5, - 247.1, - 247.9, - 248.8, - 249.8, - 250.5, - 250.9, - 251.4, - 252.2, - 253.6, - 256.0, - 259.4, - 262.9, - 265.9, - 267.3, - 267.7, - 267.6, - 267.4, - 267.0, - 266.6, - 266.5, - 266.6, - 266.5, - 265.9, - 265.3, - 265.1, - 265.0, - 264.8, - 264.8, - 264.8, - 264.7, - 264.5, - 263.5, - 262.0, - 259.9, - 257.4, - 255.7, - 255.3, - 255.3, - 255.7, - 256.5, - 257.5, - 258.9, - 260.6, - 261.8, - 262.8, - 263.3, - 263.8, - 264.2, - 264.0, - 263.1, - 262.2, - 262.4, - 262.4, - 261.2, - 259.3, - 257.5, - 255.4, - 253.0, - 250.5, - 248.2, - 246.6, - 245.9, - 245.6, - ], - [ - 244.3, - 242.9, - 241.7, - 241.1, - 240.7, - 240.2, - 239.1, - 238.1, - 237.5, - 237.1, - 236.8, - 237.0, - 237.9, - 239.2, - 240.3, - 241.1, - 241.9, - 242.5, - 242.5, - 241.8, - 241.4, - 241.2, - 240.8, - 240.2, - 239.5, - 239.5, - 240.4, - 241.8, - 243.1, - 244.2, - 245.0, - 245.5, - 245.8, - 246.5, - 247.3, - 248.4, - 249.5, - 250.3, - 250.7, - 251.4, - 253.3, - 255.7, - 259.1, - 263.3, - 265.9, - 266.1, - 266.4, - 266.0, - 265.7, - 265.5, - 265.5, - 265.6, - 265.8, - 266.2, - 266.3, - 265.9, - 265.5, - 265.3, - 264.6, - 264.1, - 263.4, - 262.4, - 261.5, - 261.2, - 261.0, - 260.8, - 260.0, - 258.8, - 257.4, - 256.8, - 256.8, - 257.4, - 258.7, - 260.1, - 261.9, - 263.6, - 265.0, - 265.2, - 264.8, - 264.8, - 265.5, - 265.2, - 264.3, - 262.9, - 263.7, - 265.2, - 266.0, - 265.8, - 263.1, - 260.8, - 256.8, - 252.4, - 249.0, - 247.0, - 245.9, - 245.1, - ], - [ - 245.2, - 243.7, - 242.2, - 241.3, - 241.5, - 241.7, - 241.3, - 240.1, - 238.9, - 238.5, - 238.6, - 239.0, - 239.9, - 241.3, - 242.4, - 243.3, - 245.4, - 246.5, - 246.4, - 246.4, - 246.4, - 245.9, - 245.2, - 243.8, - 242.2, - 241.2, - 241.2, - 241.7, - 242.5, - 243.3, - 243.8, - 244.2, - 244.4, - 244.5, - 245.4, - 246.5, - 247.4, - 247.6, - 247.6, - 248.0, - 248.9, - 250.8, - 254.1, - 259.1, - 262.6, - 265.6, - 265.6, - 265.2, - 264.8, - 264.8, - 265.4, - 266.1, - 266.6, - 267.2, - 267.2, - 267.2, - 266.8, - 266.7, - 265.6, - 263.3, - 261.6, - 259.5, - 258.2, - 258.3, - 259.4, - 260.8, - 261.7, - 261.7, - 261.5, - 260.9, - 260.2, - 260.3, - 261.3, - 262.4, - 263.9, - 266.1, - 267.0, - 267.7, - 267.5, - 267.2, - 271.5, - 271.5, - 271.6, - 271.7, - 271.6, - 271.5, - 271.6, - 271.4, - 263.7, - 260.6, - 256.3, - 252.6, - 249.8, - 248.1, - 247.1, - 246.2, - ], - [ - 248.4, - 246.8, - 245.6, - 244.3, - 244.1, - 244.4, - 244.0, - 242.7, - 241.0, - 240.4, - 240.9, - 242.0, - 243.3, - 243.7, - 243.4, - 243.9, - 245.3, - 248.8, - 250.6, - 250.9, - 250.5, - 248.5, - 246.7, - 245.5, - 244.2, - 243.3, - 243.6, - 244.7, - 245.7, - 245.8, - 245.3, - 244.4, - 243.7, - 243.7, - 244.8, - 246.1, - 246.7, - 246.4, - 246.4, - 247.1, - 249.2, - 251.9, - 255.3, - 259.6, - 261.3, - 270.2, - 270.4, - 271.0, - 271.1, - 271.2, - 271.4, - 271.5, - 271.5, - 271.5, - 271.5, - 271.4, - 271.3, - 271.7, - 272.0, - 272.1, - 267.4, - 266.9, - 266.4, - 266.3, - 266.4, - 266.6, - 267.3, - 268.2, - 268.5, - 268.2, - 266.9, - 265.2, - 264.3, - 264.3, - 265.8, - 267.1, - 267.0, - 266.0, - 265.7, - 266.6, - 271.6, - 271.6, - 271.5, - 271.5, - 271.4, - 271.5, - 271.6, - 271.5, - 271.3, - 271.4, - 262.7, - 259.6, - 254.4, - 252.3, - 251.2, - 249.6, - ], - [ - 257.5, - 254.5, - 252.1, - 249.8, - 249.2, - 250.4, - 251.5, - 250.9, - 250.6, - 250.5, - 249.1, - 248.0, - 247.6, - 247.1, - 246.9, - 247.4, - 249.1, - 253.6, - 256.8, - 257.8, - 255.1, - 251.7, - 247.6, - 245.1, - 244.0, - 243.2, - 243.2, - 244.8, - 246.9, - 247.9, - 247.8, - 247.1, - 246.2, - 245.6, - 245.4, - 245.6, - 246.3, - 246.7, - 248.1, - 250.9, - 253.0, - 253.8, - 254.5, - 254.6, - 256.0, - 270.2, - 270.8, - 271.3, - 271.2, - 271.3, - 271.4, - 271.4, - 271.3, - 271.8, - 272.6, - 272.6, - 271.1, - 271.1, - 271.3, - 271.6, - 271.7, - 271.7, - 271.6, - 271.5, - 271.5, - 271.5, - 271.4, - 271.4, - 271.2, - 271.6, - 271.7, - 271.6, - 271.5, - 272.3, - 272.1, - 271.5, - 271.8, - 268.5, - 265.9, - 264.9, - 271.5, - 271.3, - 271.3, - 271.4, - 271.5, - 271.5, - 271.5, - 271.5, - 271.3, - 271.2, - 271.1, - 271.2, - 271.3, - 263.1, - 261.2, - 259.6, - ], - [ - 271.3, - 271.4, - 271.5, - 271.6, - 271.6, - 271.5, - 271.3, - 271.3, - 264.0, - 264.2, - 263.1, - 259.6, - 256.1, - 254.7, - 252.4, - 250.5, - 251.9, - 254.3, - 259.4, - 262.6, - 263.1, - 258.8, - 255.1, - 253.2, - 252.1, - 251.8, - 251.8, - 252.1, - 252.9, - 253.4, - 253.9, - 254.0, - 253.7, - 252.8, - 251.7, - 250.5, - 249.8, - 250.2, - 252.7, - 254.8, - 256.8, - 258.2, - 259.7, - 270.3, - 270.2, - 270.5, - 271.2, - 271.1, - 271.3, - 271.5, - 271.6, - 271.6, - 272.2, - 273.2, - 274.1, - 274.1, - 273.8, - 273.9, - 274.0, - 273.9, - 273.4, - 272.8, - 272.5, - 272.7, - 273.6, - 274.0, - 273.8, - 273.6, - 273.7, - 273.8, - 273.2, - 272.8, - 273.5, - 274.4, - 275.4, - 275.3, - 272.6, - 268.4, - 265.9, - 265.2, - 271.4, - 271.3, - 271.0, - 271.2, - 271.5, - 271.5, - 271.3, - 271.2, - 271.2, - 271.2, - 271.2, - 271.3, - 271.4, - 271.4, - 271.3, - 271.3, - ], - [ - 272.8, - 273.1, - 273.0, - 272.9, - 272.8, - 272.5, - 271.3, - 271.2, - 271.1, - 271.2, - 271.1, - 271.2, - 271.0, - 263.1, - 260.1, - 260.2, - 270.2, - 269.5, - 269.5, - 269.8, - 270.1, - 269.9, - 270.9, - 260.1, - 259.0, - 257.8, - 256.3, - 255.8, - 256.3, - 258.0, - 259.9, - 261.0, - 260.8, - 259.9, - 258.9, - 257.2, - 255.9, - 257.6, - 261.3, - 270.5, - 270.7, - 270.4, - 270.6, - 271.1, - 271.4, - 271.2, - 271.3, - 272.4, - 272.8, - 273.2, - 273.4, - 273.4, - 273.6, - 274.1, - 274.2, - 274.2, - 274.2, - 274.2, - 274.0, - 273.9, - 273.9, - 273.9, - 274.0, - 274.1, - 274.4, - 274.6, - 274.5, - 274.4, - 274.3, - 274.4, - 274.6, - 274.5, - 274.5, - 274.8, - 275.1, - 275.2, - 275.1, - 274.6, - 268.9, - 267.9, - 271.2, - 271.1, - 271.2, - 272.2, - 271.4, - 271.1, - 271.0, - 271.0, - 271.6, - 272.2, - 271.4, - 271.7, - 271.3, - 271.7, - 271.6, - 272.3, - ], - [ - 274.2, - 274.2, - 274.1, - 274.3, - 274.3, - 274.3, - 274.3, - 274.5, - 274.1, - 274.5, - 274.7, - 274.5, - 271.5, - 271.2, - 271.1, - 270.9, - 270.8, - 270.7, - 270.8, - 270.9, - 271.0, - 271.1, - 271.0, - 270.8, - 270.8, - 270.8, - 270.9, - 270.9, - 271.0, - 271.1, - 271.0, - 271.0, - 271.1, - 271.1, - 271.1, - 271.1, - 271.0, - 271.0, - 270.9, - 270.9, - 271.1, - 271.1, - 271.2, - 271.3, - 271.4, - 272.1, - 273.3, - 273.9, - 274.1, - 274.3, - 274.6, - 274.5, - 274.5, - 274.5, - 274.4, - 274.3, - 274.2, - 274.2, - 274.1, - 274.0, - 273.9, - 274.1, - 274.3, - 274.4, - 274.4, - 274.6, - 274.6, - 274.7, - 274.8, - 274.9, - 275.1, - 275.4, - 275.4, - 275.4, - 275.6, - 275.6, - 275.4, - 275.2, - 275.1, - 269.6, - 271.3, - 271.3, - 272.3, - 272.8, - 272.9, - 273.0, - 273.2, - 273.4, - 273.5, - 273.8, - 273.8, - 274.0, - 274.0, - 274.1, - 274.0, - 274.0, - ], - [ - 274.7, - 274.6, - 274.4, - 274.4, - 274.5, - 274.4, - 274.3, - 274.4, - 274.4, - 274.5, - 274.8, - 275.0, - 274.8, - 274.4, - 274.0, - 273.8, - 274.0, - 274.5, - 274.5, - 274.4, - 274.3, - 274.3, - 272.9, - 271.3, - 271.2, - 271.3, - 273.0, - 272.9, - 273.0, - 273.6, - 273.6, - 273.4, - 272.9, - 272.7, - 272.9, - 272.8, - 271.8, - 271.6, - 271.6, - 272.3, - 274.1, - 274.6, - 274.6, - 274.5, - 274.8, - 274.7, - 274.9, - 275.2, - 275.1, - 275.0, - 275.0, - 275.0, - 275.1, - 275.2, - 275.2, - 275.2, - 275.2, - 275.0, - 274.9, - 274.8, - 274.9, - 274.9, - 274.9, - 275.0, - 275.0, - 275.1, - 275.1, - 275.2, - 275.4, - 275.5, - 275.8, - 276.0, - 275.9, - 276.1, - 276.4, - 276.5, - 276.4, - 276.1, - 275.5, - 273.0, - 271.6, - 272.0, - 273.1, - 273.8, - 274.5, - 274.8, - 274.9, - 274.9, - 274.3, - 274.8, - 274.9, - 274.8, - 274.7, - 274.8, - 274.8, - 274.8, - ], - [ - 275.0, - 274.9, - 274.6, - 274.3, - 274.3, - 274.4, - 274.2, - 274.2, - 274.3, - 274.5, - 274.6, - 274.7, - 274.8, - 274.9, - 275.0, - 275.1, - 275.1, - 275.1, - 275.1, - 275.0, - 274.6, - 274.3, - 274.0, - 271.7, - 272.1, - 272.6, - 273.2, - 274.1, - 274.3, - 274.4, - 274.6, - 274.7, - 274.7, - 274.8, - 274.9, - 274.6, - 274.7, - 274.9, - 274.9, - 275.0, - 275.2, - 275.4, - 275.5, - 275.4, - 275.5, - 275.4, - 275.7, - 275.7, - 275.6, - 275.6, - 275.6, - 275.6, - 275.7, - 275.7, - 275.6, - 275.6, - 275.6, - 275.6, - 275.9, - 275.7, - 275.5, - 275.6, - 275.7, - 275.8, - 275.8, - 275.8, - 275.9, - 276.0, - 276.0, - 276.1, - 276.3, - 276.4, - 276.4, - 276.5, - 276.6, - 276.8, - 276.9, - 277.2, - 277.2, - 277.0, - 276.4, - 275.6, - 275.8, - 276.0, - 276.3, - 276.2, - 276.1, - 276.3, - 276.2, - 276.2, - 276.1, - 275.7, - 275.5, - 275.5, - 275.3, - 275.0, - ], - [ - 275.2, - 275.1, - 274.9, - 274.9, - 275.2, - 275.3, - 275.2, - 275.0, - 275.0, - 275.2, - 275.5, - 275.6, - 275.5, - 275.5, - 275.5, - 275.6, - 275.8, - 275.9, - 275.8, - 275.3, - 274.9, - 274.5, - 274.6, - 274.2, - 274.3, - 273.7, - 273.7, - 273.7, - 273.9, - 274.0, - 274.4, - 274.5, - 274.6, - 274.9, - 275.0, - 275.0, - 275.0, - 275.1, - 275.1, - 275.4, - 275.6, - 275.9, - 276.3, - 276.6, - 277.0, - 276.5, - 276.5, - 276.5, - 276.5, - 276.6, - 276.6, - 276.7, - 277.0, - 276.7, - 276.4, - 276.3, - 276.1, - 276.2, - 277.1, - 276.7, - 276.5, - 276.4, - 276.8, - 277.0, - 276.9, - 276.9, - 277.0, - 277.0, - 276.9, - 276.9, - 277.0, - 276.9, - 276.9, - 277.1, - 277.2, - 277.7, - 277.9, - 278.7, - 279.2, - 279.0, - 278.1, - 278.2, - 278.1, - 278.3, - 278.6, - 278.0, - 277.3, - 277.0, - 277.1, - 277.1, - 277.0, - 276.7, - 276.4, - 276.3, - 276.1, - 275.6, - ], - [ - 276.0, - 275.8, - 275.7, - 275.8, - 276.1, - 276.2, - 276.2, - 276.1, - 275.9, - 276.0, - 276.3, - 276.4, - 276.3, - 276.4, - 276.3, - 276.3, - 276.5, - 276.5, - 276.3, - 276.0, - 275.7, - 275.1, - 275.7, - 275.6, - 275.7, - 275.7, - 276.0, - 275.6, - 276.0, - 275.4, - 275.8, - 275.7, - 275.6, - 275.6, - 275.9, - 275.9, - 276.2, - 276.5, - 276.2, - 276.1, - 276.3, - 276.1, - 277.0, - 277.3, - 278.2, - 278.2, - 277.7, - 277.5, - 277.5, - 277.6, - 277.5, - 277.8, - 278.2, - 278.2, - 278.1, - 278.0, - 277.6, - 277.2, - 278.1, - 278.1, - 278.0, - 278.0, - 278.8, - 278.7, - 278.4, - 278.3, - 278.2, - 278.2, - 278.1, - 278.0, - 278.1, - 278.1, - 278.3, - 278.4, - 278.5, - 278.9, - 279.5, - 280.2, - 279.7, - 281.3, - 280.2, - 279.7, - 279.5, - 279.3, - 279.1, - 278.3, - 277.8, - 277.9, - 278.3, - 278.3, - 278.2, - 277.7, - 277.4, - 277.2, - 276.7, - 276.3, - ], - [ - 277.2, - 277.2, - 277.4, - 277.5, - 277.5, - 277.5, - 277.4, - 277.2, - 276.9, - 276.9, - 277.1, - 277.2, - 277.4, - 277.5, - 277.5, - 277.4, - 277.4, - 277.2, - 277.2, - 277.0, - 276.8, - 276.2, - 277.4, - 277.5, - 277.4, - 277.2, - 277.5, - 277.7, - 277.9, - 277.6, - 278.0, - 277.4, - 277.5, - 277.4, - 277.6, - 277.9, - 278.4, - 278.3, - 278.2, - 278.6, - 279.2, - 278.8, - 279.5, - 279.4, - 280.0, - 280.7, - 280.1, - 278.8, - 278.6, - 278.9, - 278.8, - 279.0, - 279.6, - 280.1, - 280.0, - 279.8, - 279.7, - 279.4, - 279.7, - 280.1, - 280.3, - 280.5, - 280.7, - 280.3, - 280.1, - 280.1, - 279.9, - 279.8, - 279.5, - 279.3, - 279.3, - 279.3, - 279.4, - 279.5, - 279.7, - 280.1, - 280.4, - 279.8, - 282.0, - 281.8, - 281.2, - 281.1, - 280.4, - 279.5, - 279.1, - 278.7, - 278.6, - 278.7, - 279.0, - 279.1, - 279.5, - 279.0, - 278.5, - 278.0, - 277.2, - 277.3, - ], - [ - 279.0, - 279.3, - 279.5, - 279.4, - 279.1, - 278.8, - 278.6, - 278.3, - 278.3, - 278.2, - 278.2, - 278.3, - 278.6, - 278.8, - 278.6, - 278.5, - 278.4, - 278.3, - 278.7, - 278.3, - 278.5, - 280.1, - 281.1, - 280.7, - 280.3, - 280.1, - 280.0, - 280.2, - 280.1, - 279.9, - 279.7, - 279.5, - 279.9, - 279.8, - 280.0, - 280.9, - 280.9, - 280.8, - 280.9, - 281.1, - 281.1, - 280.8, - 281.1, - 281.1, - 281.6, - 281.8, - 281.0, - 280.1, - 280.2, - 280.5, - 281.0, - 281.1, - 281.4, - 282.2, - 281.8, - 281.6, - 281.5, - 281.4, - 281.6, - 282.2, - 282.3, - 282.4, - 282.1, - 281.9, - 281.9, - 281.8, - 281.4, - 281.2, - 280.9, - 280.7, - 280.6, - 280.5, - 280.6, - 280.7, - 280.9, - 281.0, - 281.1, - 279.2, - 284.1, - 283.7, - 282.6, - 281.6, - 280.6, - 280.3, - 280.1, - 279.7, - 279.5, - 279.5, - 279.9, - 279.9, - 280.4, - 280.3, - 280.0, - 279.7, - 279.0, - 279.3, - ], - [ - 281.7, - 281.7, - 281.4, - 281.2, - 280.9, - 280.5, - 280.2, - 279.8, - 280.0, - 280.0, - 279.8, - 280.0, - 280.2, - 280.3, - 280.0, - 279.9, - 279.9, - 279.8, - 280.4, - 280.0, - 281.9, - 282.4, - 282.6, - 282.2, - 282.3, - 282.2, - 281.9, - 281.9, - 281.7, - 281.5, - 281.4, - 281.6, - 282.0, - 282.0, - 282.3, - 282.7, - 282.4, - 282.5, - 282.5, - 282.5, - 282.7, - 282.6, - 282.7, - 282.8, - 283.2, - 283.1, - 282.7, - 282.0, - 282.5, - 283.3, - 283.9, - 283.5, - 283.7, - 284.1, - 283.9, - 283.6, - 283.4, - 283.4, - 283.6, - 283.9, - 283.9, - 283.8, - 283.6, - 283.6, - 283.6, - 283.4, - 283.1, - 282.8, - 282.6, - 282.3, - 282.2, - 281.9, - 281.8, - 281.9, - 281.9, - 281.9, - 281.9, - 277.8, - 287.9, - 285.9, - 283.8, - 282.2, - 282.2, - 282.4, - 282.3, - 281.9, - 281.1, - 281.0, - 281.1, - 281.0, - 281.5, - 282.3, - 282.3, - 282.3, - 282.0, - 282.0, - ], - [ - 284.2, - 283.9, - 283.5, - 283.1, - 282.7, - 282.4, - 282.0, - 281.6, - 281.8, - 281.5, - 281.5, - 281.9, - 281.9, - 282.1, - 281.9, - 281.9, - 282.0, - 281.8, - 282.2, - 282.2, - 284.0, - 284.2, - 284.2, - 284.1, - 284.2, - 283.9, - 283.7, - 283.6, - 283.6, - 283.6, - 283.6, - 283.8, - 283.9, - 283.9, - 284.2, - 284.1, - 284.1, - 284.2, - 284.4, - 285.2, - 285.7, - 285.1, - 284.9, - 284.9, - 284.9, - 283.6, - 287.7, - 288.0, - 286.9, - 288.5, - 287.4, - 286.6, - 287.0, - 286.3, - 286.2, - 285.8, - 285.7, - 285.7, - 285.7, - 285.8, - 285.7, - 285.6, - 285.3, - 285.2, - 285.0, - 284.9, - 284.7, - 284.4, - 284.3, - 283.9, - 283.6, - 283.4, - 283.0, - 283.0, - 282.8, - 283.1, - 283.2, - 280.2, - 289.8, - 289.2, - 285.5, - 284.6, - 284.9, - 285.1, - 284.9, - 283.8, - 283.0, - 283.1, - 283.0, - 282.7, - 283.5, - 284.8, - 284.8, - 284.9, - 285.1, - 284.7, - ], - [ - 286.4, - 286.1, - 285.7, - 285.2, - 284.9, - 284.5, - 284.0, - 283.6, - 283.8, - 283.3, - 283.2, - 284.3, - 284.8, - 285.0, - 285.3, - 285.1, - 284.9, - 284.8, - 285.3, - 285.4, - 286.6, - 286.3, - 286.2, - 286.2, - 286.2, - 286.0, - 285.9, - 285.7, - 285.7, - 285.7, - 285.5, - 285.5, - 285.6, - 285.8, - 285.8, - 285.9, - 286.2, - 286.3, - 286.4, - 287.2, - 287.9, - 287.7, - 287.4, - 287.2, - 287.5, - 288.2, - 286.9, - 291.5, - 291.3, - 290.5, - 289.1, - 288.8, - 288.6, - 287.9, - 287.7, - 287.4, - 287.5, - 287.6, - 287.6, - 287.6, - 287.3, - 287.1, - 286.9, - 286.9, - 286.8, - 286.6, - 286.3, - 286.1, - 286.0, - 285.7, - 285.4, - 285.1, - 284.6, - 284.2, - 284.0, - 284.6, - 284.8, - 281.5, - 289.1, - 290.4, - 287.6, - 289.3, - 289.9, - 287.9, - 286.6, - 286.2, - 286.0, - 285.5, - 285.5, - 286.1, - 287.1, - 287.8, - 287.2, - 287.5, - 287.3, - 287.0, - ], - [ - 288.7, - 288.5, - 288.1, - 287.6, - 287.1, - 286.8, - 287.2, - 286.7, - 286.5, - 285.8, - 286.0, - 287.4, - 287.8, - 288.3, - 288.7, - 288.2, - 287.8, - 288.0, - 288.5, - 288.7, - 289.1, - 288.6, - 288.5, - 288.2, - 288.0, - 287.9, - 287.6, - 287.4, - 287.4, - 287.4, - 287.3, - 287.3, - 287.5, - 287.6, - 287.7, - 287.8, - 287.9, - 288.0, - 288.1, - 289.0, - 290.0, - 291.0, - 290.2, - 289.5, - 289.6, - 290.0, - 291.1, - 290.8, - 292.5, - 291.4, - 290.5, - 290.1, - 289.7, - 289.4, - 289.2, - 289.3, - 289.6, - 289.9, - 290.0, - 289.6, - 289.3, - 289.2, - 289.0, - 289.0, - 289.0, - 288.7, - 288.4, - 288.2, - 288.0, - 287.6, - 287.2, - 286.8, - 286.3, - 286.0, - 285.8, - 286.3, - 286.6, - 283.4, - 291.9, - 296.0, - 290.3, - 292.7, - 292.4, - 290.3, - 289.5, - 289.2, - 288.9, - 288.8, - 289.2, - 289.7, - 289.9, - 289.6, - 289.5, - 289.6, - 289.4, - 289.1, - ], - [ - 290.7, - 290.8, - 290.4, - 290.0, - 290.5, - 291.8, - 292.7, - 293.0, - 293.4, - 292.1, - 291.7, - 292.0, - 291.9, - 291.8, - 291.6, - 291.2, - 291.2, - 291.3, - 291.3, - 291.3, - 291.1, - 290.7, - 290.7, - 290.2, - 289.7, - 289.5, - 289.3, - 289.1, - 289.1, - 289.1, - 289.2, - 289.5, - 289.8, - 289.6, - 289.4, - 289.5, - 289.6, - 290.2, - 293.9, - 290.9, - 292.4, - 294.3, - 292.9, - 291.8, - 291.8, - 291.9, - 292.2, - 292.8, - 292.7, - 292.4, - 292.3, - 291.9, - 291.4, - 291.0, - 291.1, - 291.3, - 291.6, - 292.4, - 292.3, - 292.0, - 291.8, - 291.6, - 291.5, - 291.4, - 291.3, - 291.0, - 290.7, - 290.5, - 290.2, - 289.8, - 289.4, - 288.8, - 288.3, - 287.9, - 287.5, - 288.1, - 288.3, - 286.7, - 294.7, - 298.5, - 297.5, - 294.4, - 293.8, - 292.8, - 292.0, - 291.3, - 291.0, - 291.1, - 291.2, - 291.5, - 291.8, - 291.9, - 291.9, - 291.5, - 291.3, - 291.1, - ], - [ - 292.2, - 292.3, - 292.0, - 292.2, - 292.3, - 291.9, - 293.8, - 295.6, - 296.0, - 296.2, - 295.2, - 293.9, - 293.5, - 293.3, - 293.3, - 293.1, - 293.2, - 293.2, - 293.1, - 292.8, - 292.5, - 292.1, - 292.1, - 291.7, - 291.3, - 291.0, - 290.6, - 290.4, - 290.3, - 290.4, - 290.5, - 290.1, - 291.1, - 291.0, - 290.9, - 291.1, - 291.6, - 297.3, - 298.4, - 294.6, - 291.0, - 296.0, - 294.8, - 294.0, - 294.1, - 294.0, - 293.7, - 294.0, - 293.6, - 293.7, - 293.5, - 293.1, - 292.7, - 292.5, - 292.9, - 293.4, - 293.7, - 294.1, - 294.1, - 294.0, - 293.9, - 293.8, - 293.7, - 293.6, - 293.5, - 293.4, - 293.0, - 292.8, - 292.5, - 292.1, - 291.4, - 290.8, - 290.1, - 289.5, - 289.0, - 289.7, - 288.3, - 292.3, - 297.3, - 301.5, - 301.0, - 299.1, - 295.6, - 294.8, - 293.4, - 293.0, - 292.9, - 293.0, - 293.2, - 293.5, - 293.9, - 294.0, - 293.8, - 293.3, - 292.8, - 292.4, - ], - [ - 292.9, - 292.5, - 291.9, - 292.0, - 291.5, - 295.9, - 289.6, - 290.0, - 297.4, - 297.2, - 295.9, - 295.2, - 295.1, - 295.2, - 295.3, - 295.2, - 295.1, - 294.9, - 294.6, - 294.2, - 293.8, - 293.4, - 293.2, - 292.8, - 292.3, - 291.8, - 291.6, - 291.1, - 291.5, - 292.2, - 293.2, - 297.0, - 294.3, - 292.2, - 292.0, - 292.5, - 298.2, - 298.2, - 297.6, - 295.0, - 292.4, - 296.8, - 295.8, - 295.7, - 295.8, - 295.4, - 295.0, - 294.9, - 294.6, - 294.8, - 294.5, - 294.1, - 294.0, - 294.2, - 294.6, - 294.9, - 295.3, - 295.4, - 295.4, - 295.4, - 295.5, - 295.4, - 295.4, - 295.4, - 295.4, - 295.3, - 294.9, - 294.5, - 294.2, - 293.8, - 293.2, - 292.4, - 291.6, - 290.8, - 290.5, - 290.7, - 287.0, - 294.5, - 299.9, - 301.8, - 303.2, - 299.2, - 297.4, - 296.8, - 295.7, - 294.8, - 294.9, - 294.9, - 294.8, - 295.0, - 295.3, - 295.2, - 294.9, - 294.3, - 293.7, - 293.3, - ], - [ - 293.3, - 293.0, - 292.8, - 293.8, - 293.2, - 298.3, - 293.4, - 289.7, - 290.7, - 298.8, - 297.3, - 297.2, - 297.1, - 296.7, - 296.7, - 296.6, - 296.2, - 295.8, - 295.4, - 295.0, - 294.6, - 294.1, - 293.7, - 293.3, - 293.1, - 293.1, - 293.4, - 292.4, - 292.9, - 294.0, - 295.2, - 299.8, - 293.3, - 293.0, - 293.2, - 297.7, - 300.7, - 298.8, - 297.4, - 298.5, - 294.3, - 297.7, - 296.6, - 296.9, - 296.7, - 296.2, - 295.8, - 295.7, - 295.6, - 295.8, - 295.4, - 295.3, - 295.4, - 295.8, - 295.9, - 296.0, - 296.3, - 296.4, - 296.5, - 296.6, - 296.7, - 296.6, - 296.6, - 296.6, - 296.5, - 296.3, - 295.9, - 295.6, - 295.2, - 294.6, - 294.0, - 293.3, - 292.5, - 291.7, - 291.1, - 290.3, - 286.5, - 294.7, - 297.7, - 304.8, - 304.4, - 300.7, - 297.3, - 298.0, - 297.6, - 296.6, - 296.3, - 296.2, - 295.9, - 295.8, - 295.8, - 295.6, - 295.1, - 294.6, - 293.9, - 293.6, - ], - [ - 293.5, - 293.2, - 293.4, - 293.4, - 291.1, - 297.3, - 294.5, - 292.8, - 290.8, - 299.7, - 299.5, - 299.4, - 299.1, - 298.2, - 297.6, - 297.2, - 296.7, - 296.1, - 295.6, - 295.2, - 294.8, - 294.4, - 294.1, - 293.8, - 293.9, - 294.2, - 294.2, - 293.8, - 294.3, - 295.6, - 297.2, - 300.9, - 295.2, - 296.9, - 298.7, - 299.6, - 301.0, - 300.8, - 300.3, - 300.0, - 295.9, - 298.4, - 297.6, - 297.7, - 297.5, - 297.2, - 296.9, - 296.8, - 296.9, - 296.8, - 296.5, - 296.5, - 296.7, - 296.9, - 296.9, - 296.9, - 297.1, - 297.2, - 297.3, - 297.3, - 297.3, - 297.2, - 297.1, - 297.0, - 296.7, - 296.3, - 296.0, - 295.7, - 295.4, - 294.9, - 294.3, - 293.7, - 293.0, - 292.2, - 291.3, - 290.6, - 290.3, - 293.8, - 283.8, - 303.0, - 303.3, - 301.1, - 295.7, - 296.0, - 299.0, - 298.0, - 297.4, - 297.0, - 296.4, - 296.0, - 295.7, - 295.3, - 294.8, - 294.4, - 293.9, - 293.7, - ], - [ - 293.7, - 293.6, - 294.4, - 293.9, - 294.2, - 296.8, - 296.3, - 294.3, - 293.1, - 298.0, - 300.5, - 300.5, - 298.9, - 299.1, - 298.0, - 297.7, - 297.0, - 296.4, - 295.9, - 295.5, - 295.1, - 294.9, - 294.9, - 294.7, - 295.0, - 295.3, - 295.1, - 295.0, - 295.6, - 296.9, - 298.5, - 301.8, - 297.3, - 301.1, - 302.4, - 299.7, - 299.6, - 301.0, - 300.8, - 298.4, - 297.0, - 299.1, - 298.5, - 298.5, - 298.3, - 298.1, - 298.0, - 298.0, - 297.9, - 297.6, - 297.5, - 297.6, - 297.7, - 297.8, - 297.7, - 297.7, - 297.7, - 297.9, - 298.0, - 297.8, - 297.6, - 297.4, - 297.1, - 296.7, - 296.3, - 295.9, - 295.6, - 295.4, - 295.2, - 294.7, - 294.2, - 293.6, - 292.9, - 292.1, - 291.3, - 290.9, - 291.9, - 294.1, - 280.6, - 297.3, - 301.2, - 301.0, - 296.7, - 296.5, - 299.8, - 298.7, - 297.8, - 297.1, - 296.3, - 295.8, - 295.4, - 295.0, - 294.6, - 294.3, - 293.9, - 293.8, - ], - [ - 294.3, - 294.7, - 294.9, - 294.8, - 300.4, - 293.8, - 295.8, - 294.9, - 295.8, - 298.2, - 301.2, - 301.1, - 295.4, - 299.3, - 298.7, - 298.3, - 297.7, - 297.1, - 296.5, - 296.1, - 295.8, - 295.8, - 295.9, - 295.6, - 295.9, - 296.1, - 295.9, - 296.0, - 296.6, - 297.9, - 299.7, - 302.8, - 300.9, - 303.2, - 301.0, - 298.0, - 298.2, - 299.2, - 299.3, - 300.2, - 299.3, - 300.0, - 299.3, - 299.1, - 298.8, - 298.6, - 298.6, - 298.7, - 298.7, - 298.5, - 298.5, - 298.4, - 298.5, - 298.5, - 298.5, - 298.5, - 298.5, - 298.6, - 298.6, - 298.4, - 298.0, - 297.5, - 297.0, - 296.4, - 295.8, - 295.3, - 294.9, - 294.7, - 294.5, - 294.2, - 293.7, - 293.2, - 292.5, - 291.7, - 291.5, - 291.8, - 293.3, - 293.4, - 281.5, - 294.6, - 300.3, - 299.7, - 299.1, - 296.2, - 294.6, - 299.0, - 298.1, - 297.1, - 296.2, - 295.7, - 295.3, - 295.0, - 294.7, - 294.3, - 294.0, - 294.1, - ], - [ - 295.1, - 295.6, - 295.7, - 295.2, - 294.5, - 294.9, - 296.9, - 295.3, - 294.1, - 297.9, - 301.5, - 300.9, - 301.8, - 298.2, - 298.7, - 298.8, - 298.2, - 297.6, - 297.1, - 296.8, - 296.7, - 296.9, - 297.0, - 297.0, - 297.1, - 297.0, - 296.9, - 297.1, - 297.6, - 298.7, - 300.4, - 302.7, - 302.9, - 301.2, - 299.5, - 299.3, - 299.9, - 299.3, - 298.6, - 297.4, - 300.6, - 300.3, - 299.9, - 299.7, - 299.4, - 299.4, - 299.4, - 299.4, - 299.4, - 299.2, - 299.1, - 299.0, - 299.0, - 299.3, - 299.4, - 299.5, - 299.3, - 299.2, - 299.2, - 298.7, - 298.1, - 297.5, - 296.9, - 296.1, - 295.4, - 294.8, - 294.3, - 294.0, - 293.7, - 293.4, - 293.1, - 292.5, - 292.0, - 291.8, - 292.4, - 293.3, - 293.7, - 292.8, - 281.2, - 293.6, - 299.9, - 300.7, - 298.9, - 296.3, - 294.1, - 296.1, - 298.5, - 297.5, - 296.7, - 296.1, - 295.7, - 295.4, - 295.0, - 294.7, - 294.5, - 294.7, - ], - [ - 295.7, - 296.3, - 296.6, - 296.3, - 293.9, - 295.3, - 295.8, - 295.6, - 294.4, - 297.6, - 300.6, - 300.6, - 301.4, - 298.0, - 298.9, - 299.1, - 298.6, - 298.0, - 297.7, - 297.6, - 297.7, - 298.2, - 298.5, - 298.5, - 298.5, - 298.5, - 298.2, - 298.4, - 298.8, - 299.7, - 300.9, - 302.2, - 302.6, - 300.0, - 297.9, - 299.1, - 299.7, - 300.4, - 299.2, - 301.1, - 300.7, - 300.8, - 300.9, - 300.7, - 300.4, - 300.2, - 300.0, - 299.7, - 299.6, - 299.5, - 299.7, - 299.8, - 299.8, - 299.8, - 300.0, - 300.1, - 299.9, - 299.6, - 299.3, - 299.1, - 298.4, - 297.7, - 296.8, - 295.8, - 295.0, - 294.4, - 293.9, - 293.5, - 293.1, - 292.8, - 292.6, - 292.2, - 292.2, - 292.7, - 293.3, - 293.3, - 290.7, - 293.3, - 283.6, - 295.9, - 300.0, - 301.3, - 296.9, - 295.9, - 295.1, - 296.2, - 298.6, - 298.3, - 297.6, - 296.9, - 296.4, - 295.9, - 295.6, - 295.3, - 295.2, - 295.3, - ], - [ - 296.7, - 297.4, - 298.1, - 298.3, - 293.0, - 294.1, - 295.4, - 294.8, - 296.0, - 296.7, - 297.1, - 300.4, - 301.3, - 298.5, - 299.2, - 299.2, - 298.8, - 298.5, - 298.4, - 298.7, - 299.1, - 299.4, - 299.6, - 299.8, - 299.9, - 299.8, - 299.6, - 299.8, - 300.2, - 300.8, - 301.3, - 301.9, - 302.2, - 302.4, - 299.3, - 299.7, - 300.3, - 302.5, - 300.3, - 301.2, - 301.4, - 301.5, - 301.5, - 301.3, - 300.9, - 300.6, - 300.4, - 300.2, - 300.2, - 300.2, - 300.6, - 301.0, - 300.8, - 300.5, - 300.4, - 300.4, - 300.2, - 300.0, - 299.7, - 299.4, - 298.9, - 298.1, - 297.2, - 296.3, - 295.5, - 294.7, - 294.1, - 293.5, - 293.0, - 292.6, - 292.3, - 292.4, - 292.9, - 293.6, - 294.6, - 295.1, - 293.8, - 280.5, - 293.1, - 299.5, - 298.8, - 298.8, - 297.6, - 295.5, - 295.4, - 294.9, - 298.9, - 299.0, - 298.2, - 297.5, - 296.9, - 296.4, - 296.2, - 296.0, - 295.9, - 296.3, - ], - [ - 298.1, - 298.6, - 299.5, - 299.7, - 293.4, - 293.8, - 294.6, - 293.7, - 293.8, - 296.3, - 297.1, - 300.2, - 300.4, - 299.7, - 299.6, - 299.5, - 299.3, - 299.5, - 299.6, - 299.9, - 300.1, - 300.3, - 300.6, - 300.8, - 300.8, - 300.8, - 300.7, - 300.9, - 301.2, - 301.6, - 301.8, - 302.0, - 302.1, - 302.2, - 302.2, - 302.3, - 300.9, - 302.6, - 301.9, - 301.9, - 302.0, - 301.8, - 301.7, - 301.4, - 301.2, - 301.1, - 301.1, - 301.0, - 300.9, - 300.9, - 301.2, - 301.3, - 301.1, - 300.9, - 300.8, - 300.7, - 300.6, - 300.4, - 300.1, - 299.9, - 299.6, - 299.0, - 298.1, - 297.3, - 296.3, - 295.4, - 294.7, - 294.1, - 293.8, - 293.4, - 293.4, - 293.8, - 294.6, - 295.3, - 296.4, - 296.5, - 286.2, - 293.0, - 299.3, - 299.6, - 298.9, - 298.4, - 298.4, - 297.8, - 295.5, - 294.9, - 298.6, - 299.3, - 298.8, - 298.2, - 297.7, - 297.2, - 296.9, - 296.9, - 297.2, - 297.7, - ], - [ - 299.3, - 300.1, - 300.9, - 301.0, - 296.5, - 295.0, - 295.4, - 294.5, - 293.6, - 293.2, - 298.3, - 300.1, - 300.6, - 300.2, - 300.2, - 300.2, - 300.1, - 300.3, - 300.4, - 300.5, - 300.6, - 300.7, - 300.8, - 300.9, - 301.0, - 301.1, - 301.2, - 301.2, - 301.4, - 301.6, - 301.8, - 301.9, - 301.9, - 302.0, - 302.2, - 302.2, - 302.4, - 302.3, - 302.5, - 302.3, - 302.1, - 301.8, - 301.7, - 301.6, - 301.5, - 301.5, - 301.4, - 301.3, - 301.3, - 301.4, - 301.4, - 301.3, - 301.2, - 301.1, - 301.1, - 301.0, - 300.9, - 300.7, - 300.5, - 300.2, - 299.8, - 299.2, - 298.4, - 297.5, - 296.7, - 296.1, - 295.6, - 295.3, - 295.1, - 295.0, - 295.1, - 295.5, - 296.1, - 297.1, - 297.6, - 296.6, - 292.3, - 299.6, - 300.4, - 299.8, - 299.4, - 298.4, - 298.4, - 298.9, - 296.7, - 296.7, - 300.1, - 299.5, - 299.2, - 298.8, - 298.4, - 298.2, - 298.2, - 298.4, - 298.7, - 298.9, - ], - [ - 300.7, - 301.3, - 301.7, - 301.6, - 297.2, - 296.6, - 296.7, - 296.4, - 294.2, - 293.5, - 299.5, - 299.8, - 300.8, - 300.4, - 300.1, - 300.1, - 300.2, - 300.4, - 300.5, - 300.5, - 300.6, - 300.8, - 300.8, - 300.8, - 300.8, - 300.9, - 300.9, - 300.9, - 301.2, - 301.4, - 301.6, - 301.8, - 302.0, - 302.1, - 302.2, - 302.4, - 302.4, - 302.4, - 300.7, - 296.4, - 302.0, - 301.9, - 301.9, - 301.8, - 301.8, - 301.7, - 301.6, - 301.5, - 301.5, - 301.4, - 301.4, - 301.4, - 301.3, - 301.2, - 301.0, - 300.9, - 300.8, - 300.7, - 300.4, - 299.9, - 299.4, - 298.8, - 298.2, - 297.7, - 297.3, - 296.9, - 296.6, - 296.4, - 296.3, - 296.3, - 296.5, - 296.9, - 297.4, - 298.4, - 298.5, - 291.0, - 298.6, - 300.5, - 300.8, - 300.9, - 300.1, - 298.7, - 298.5, - 298.4, - 297.9, - 297.8, - 299.0, - 299.5, - 299.9, - 299.7, - 299.6, - 299.5, - 299.5, - 299.6, - 299.9, - 300.1, - ], - [ - 301.2, - 301.4, - 301.5, - 301.6, - 298.1, - 298.3, - 298.3, - 297.6, - 294.4, - 293.5, - 297.6, - 299.1, - 300.7, - 300.6, - 300.2, - 299.9, - 300.0, - 300.2, - 300.4, - 300.4, - 300.5, - 300.6, - 300.5, - 300.6, - 300.7, - 300.8, - 300.9, - 301.5, - 299.1, - 302.0, - 302.1, - 302.2, - 302.6, - 302.4, - 302.2, - 302.3, - 302.2, - 298.0, - 295.6, - 301.5, - 301.8, - 301.7, - 301.6, - 301.5, - 301.3, - 301.2, - 301.1, - 301.0, - 301.0, - 300.9, - 300.7, - 300.6, - 300.3, - 300.0, - 299.9, - 299.8, - 299.7, - 299.6, - 299.4, - 299.2, - 298.8, - 298.5, - 298.1, - 297.8, - 297.8, - 297.7, - 297.6, - 297.5, - 297.6, - 297.7, - 297.9, - 298.2, - 298.8, - 299.5, - 299.3, - 294.1, - 300.7, - 301.2, - 301.1, - 300.9, - 300.2, - 299.5, - 298.9, - 298.7, - 298.8, - 297.7, - 300.7, - 300.1, - 300.4, - 300.3, - 300.3, - 300.3, - 300.4, - 300.5, - 300.7, - 300.9, - ], - [ - 301.2, - 301.3, - 301.4, - 299.1, - 297.8, - 299.4, - 298.8, - 297.3, - 291.6, - 294.2, - 297.2, - 299.3, - 300.3, - 300.2, - 300.3, - 300.2, - 300.1, - 300.2, - 300.3, - 300.5, - 300.5, - 300.6, - 300.6, - 300.6, - 300.8, - 301.1, - 301.2, - 298.7, - 300.5, - 302.1, - 300.4, - 300.7, - 296.8, - 302.3, - 302.2, - 302.3, - 302.6, - 299.3, - 301.6, - 301.2, - 300.7, - 300.4, - 300.3, - 300.2, - 300.1, - 300.0, - 300.0, - 299.7, - 299.7, - 299.4, - 299.3, - 299.2, - 298.8, - 298.6, - 298.3, - 297.9, - 297.7, - 297.5, - 297.4, - 297.5, - 297.3, - 297.2, - 297.1, - 297.0, - 297.2, - 297.0, - 297.3, - 297.4, - 297.7, - 298.0, - 298.0, - 298.6, - 298.9, - 299.3, - 299.3, - 294.4, - 300.5, - 301.2, - 300.9, - 300.7, - 299.8, - 300.0, - 301.0, - 299.5, - 299.3, - 300.7, - 300.7, - 300.2, - 300.4, - 300.5, - 300.4, - 300.5, - 300.6, - 300.7, - 300.9, - 301.0, - ], - [ - 301.4, - 301.5, - 301.4, - 299.0, - 298.3, - 299.5, - 299.6, - 297.9, - 293.7, - 293.8, - 296.3, - 302.4, - 300.0, - 299.5, - 299.7, - 300.1, - 300.3, - 300.3, - 300.5, - 300.6, - 300.6, - 300.6, - 300.8, - 300.9, - 301.3, - 301.6, - 301.5, - 298.7, - 301.6, - 301.6, - 298.6, - 299.5, - 302.0, - 301.9, - 302.1, - 302.2, - 302.1, - 301.8, - 301.2, - 300.6, - 300.2, - 300.0, - 299.8, - 299.7, - 299.6, - 299.4, - 299.4, - 299.1, - 299.0, - 298.6, - 298.5, - 298.2, - 298.0, - 297.8, - 297.6, - 297.3, - 297.0, - 297.0, - 296.6, - 296.8, - 296.5, - 296.5, - 296.3, - 296.3, - 296.0, - 296.1, - 296.5, - 296.8, - 297.0, - 297.8, - 298.0, - 298.8, - 299.0, - 299.3, - 299.4, - 293.3, - 299.8, - 300.5, - 300.4, - 299.8, - 300.2, - 302.2, - 300.8, - 300.1, - 300.2, - 300.1, - 300.0, - 300.2, - 300.2, - 300.3, - 300.4, - 300.6, - 300.8, - 300.9, - 301.1, - 301.2, - ], - [ - 301.9, - 301.9, - 301.6, - 298.0, - 297.9, - 299.2, - 298.9, - 297.8, - 295.1, - 295.1, - 300.5, - 301.9, - 300.2, - 299.3, - 299.3, - 299.8, - 299.9, - 300.1, - 300.2, - 300.3, - 300.2, - 300.1, - 300.3, - 300.5, - 301.1, - 301.6, - 301.5, - 299.3, - 300.9, - 301.2, - 299.4, - 297.5, - 301.9, - 301.9, - 301.9, - 301.9, - 301.9, - 301.7, - 301.6, - 301.3, - 301.0, - 300.7, - 300.6, - 300.4, - 300.3, - 300.1, - 299.9, - 299.8, - 299.7, - 299.5, - 299.3, - 299.0, - 299.1, - 299.2, - 299.0, - 298.8, - 298.6, - 298.5, - 298.4, - 298.4, - 298.1, - 298.1, - 298.3, - 298.3, - 298.2, - 298.4, - 298.7, - 298.8, - 299.1, - 299.3, - 299.4, - 299.7, - 299.7, - 299.7, - 299.9, - 300.1, - 293.9, - 299.3, - 300.0, - 298.1, - 302.8, - 301.0, - 299.5, - 300.0, - 299.9, - 299.9, - 300.0, - 300.2, - 300.4, - 300.6, - 300.8, - 301.0, - 301.3, - 301.4, - 301.7, - 301.8, - ], - [ - 302.3, - 302.5, - 299.8, - 295.9, - 296.7, - 297.9, - 297.1, - 295.9, - 298.0, - 299.7, - 292.1, - 298.2, - 298.1, - 299.2, - 298.9, - 299.5, - 299.8, - 300.0, - 300.1, - 300.0, - 299.8, - 299.7, - 299.8, - 300.2, - 300.7, - 301.2, - 301.3, - 296.9, - 300.8, - 301.4, - 302.1, - 298.0, - 302.2, - 301.9, - 301.5, - 301.8, - 301.8, - 301.7, - 301.6, - 301.5, - 301.4, - 301.3, - 301.2, - 301.1, - 301.0, - 300.8, - 300.7, - 300.6, - 300.4, - 300.3, - 300.1, - 300.0, - 300.1, - 299.9, - 299.9, - 299.9, - 299.8, - 299.7, - 299.7, - 299.8, - 299.7, - 299.6, - 299.5, - 299.4, - 299.4, - 299.6, - 299.7, - 299.8, - 300.0, - 300.1, - 300.1, - 300.1, - 300.1, - 299.9, - 300.1, - 301.1, - 290.3, - 299.6, - 299.7, - 298.0, - 296.0, - 299.6, - 299.4, - 299.8, - 299.6, - 299.6, - 299.9, - 300.1, - 300.4, - 300.7, - 301.0, - 301.3, - 301.6, - 302.2, - 299.7, - 300.3, - ], - [ - 298.4, - 297.2, - 296.5, - 293.9, - 297.7, - 298.4, - 294.5, - 296.0, - 301.3, - 299.8, - 288.7, - 292.5, - 294.3, - 296.1, - 298.5, - 298.7, - 299.2, - 299.5, - 299.7, - 299.7, - 299.6, - 299.5, - 299.6, - 300.0, - 300.4, - 300.7, - 300.8, - 300.4, - 300.8, - 301.3, - 301.5, - 301.9, - 302.0, - 297.3, - 301.3, - 301.4, - 301.3, - 301.2, - 301.1, - 301.0, - 300.9, - 300.8, - 300.8, - 300.7, - 300.5, - 300.3, - 300.3, - 300.2, - 300.0, - 300.0, - 300.0, - 299.9, - 299.9, - 299.9, - 299.9, - 299.9, - 299.9, - 299.8, - 299.7, - 299.8, - 299.8, - 299.8, - 299.8, - 299.8, - 299.9, - 300.1, - 300.2, - 300.1, - 299.9, - 299.8, - 299.8, - 300.0, - 299.9, - 299.7, - 299.7, - 298.3, - 295.7, - 296.7, - 303.6, - 300.4, - 299.1, - 299.5, - 299.7, - 299.7, - 299.0, - 298.9, - 299.1, - 299.3, - 299.6, - 299.9, - 300.0, - 300.5, - 301.6, - 298.6, - 297.4, - 298.3, - ], - [ - 296.5, - 294.9, - 293.1, - 296.6, - 300.2, - 300.2, - 298.6, - 299.1, - 301.0, - 299.0, - 290.4, - 291.1, - 290.9, - 290.4, - 297.5, - 297.8, - 298.0, - 298.4, - 299.1, - 299.6, - 299.8, - 293.2, - 299.1, - 299.5, - 299.9, - 300.2, - 300.3, - 297.6, - 298.5, - 301.0, - 301.0, - 301.2, - 301.1, - 298.1, - 300.7, - 300.7, - 300.6, - 300.5, - 300.4, - 300.2, - 299.9, - 299.7, - 299.6, - 299.4, - 299.2, - 299.0, - 298.9, - 298.8, - 298.6, - 298.6, - 298.6, - 298.6, - 298.7, - 298.6, - 298.6, - 298.7, - 298.7, - 298.7, - 298.7, - 298.8, - 299.0, - 299.1, - 299.2, - 299.3, - 299.5, - 299.8, - 299.9, - 299.6, - 299.2, - 299.0, - 299.1, - 299.6, - 300.1, - 300.1, - 294.4, - 299.6, - 300.2, - 299.3, - 301.1, - 300.2, - 299.0, - 299.5, - 299.1, - 298.7, - 298.5, - 298.2, - 298.1, - 298.1, - 298.3, - 298.6, - 298.7, - 299.2, - 300.9, - 295.8, - 295.9, - 296.5, - ], - [ - 298.7, - 297.5, - 297.7, - 299.4, - 300.9, - 299.8, - 297.2, - 296.9, - 299.0, - 301.1, - 291.8, - 289.3, - 288.2, - 297.1, - 297.1, - 296.8, - 296.8, - 297.3, - 298.3, - 299.3, - 300.1, - 291.1, - 298.5, - 298.8, - 299.1, - 299.3, - 298.9, - 295.9, - 297.2, - 296.0, - 300.4, - 300.4, - 300.1, - 297.6, - 300.2, - 300.1, - 300.1, - 300.1, - 300.0, - 299.8, - 299.6, - 299.3, - 299.0, - 298.6, - 298.3, - 298.1, - 297.9, - 297.7, - 297.6, - 297.5, - 297.5, - 297.4, - 297.5, - 297.5, - 297.4, - 297.2, - 297.2, - 297.3, - 297.2, - 297.5, - 297.8, - 298.1, - 298.3, - 298.6, - 298.8, - 299.2, - 299.5, - 299.4, - 298.8, - 298.1, - 298.0, - 298.6, - 300.1, - 296.6, - 299.2, - 297.7, - 297.0, - 296.4, - 297.7, - 298.7, - 298.7, - 298.8, - 298.4, - 298.2, - 298.1, - 297.8, - 297.5, - 297.3, - 297.2, - 297.5, - 297.7, - 297.9, - 299.8, - 296.7, - 297.8, - 298.6, - ], - [ - 298.5, - 297.9, - 297.1, - 296.8, - 297.3, - 297.1, - 294.6, - 293.7, - 296.6, - 299.4, - 293.8, - 298.8, - 284.9, - 289.3, - 297.1, - 295.9, - 295.7, - 296.3, - 297.4, - 298.5, - 291.5, - 294.5, - 298.3, - 298.2, - 298.4, - 298.2, - 297.4, - 293.2, - 293.8, - 293.9, - 299.2, - 299.3, - 295.4, - 299.2, - 299.3, - 299.1, - 299.2, - 299.2, - 299.3, - 299.4, - 299.3, - 299.0, - 298.6, - 298.1, - 297.6, - 297.3, - 297.0, - 296.8, - 296.7, - 296.6, - 296.5, - 296.2, - 296.3, - 296.3, - 296.1, - 296.1, - 296.1, - 296.1, - 296.3, - 296.5, - 296.8, - 297.0, - 297.2, - 297.2, - 297.3, - 297.7, - 298.4, - 299.1, - 298.9, - 297.9, - 297.2, - 296.4, - 291.5, - 293.8, - 299.1, - 299.1, - 298.8, - 298.8, - 298.9, - 298.9, - 298.5, - 298.2, - 297.8, - 297.5, - 297.4, - 297.3, - 297.1, - 296.9, - 296.7, - 296.5, - 296.5, - 296.9, - 299.4, - 299.6, - 298.4, - 298.6, - ], - [ - 295.1, - 294.7, - 293.0, - 292.3, - 293.5, - 294.7, - 292.3, - 293.1, - 295.3, - 296.1, - 294.7, - 300.3, - 284.4, - 288.6, - 289.2, - 295.6, - 294.9, - 295.7, - 296.8, - 298.0, - 291.2, - 291.8, - 290.6, - 298.1, - 298.1, - 297.6, - 288.6, - 289.9, - 289.8, - 296.0, - 297.7, - 298.2, - 298.0, - 297.6, - 297.2, - 297.0, - 297.2, - 297.5, - 297.7, - 297.9, - 298.2, - 298.4, - 298.3, - 298.0, - 297.6, - 297.1, - 296.6, - 296.3, - 295.9, - 295.7, - 295.4, - 295.1, - 295.0, - 294.9, - 294.7, - 294.7, - 294.6, - 294.6, - 294.8, - 295.2, - 295.4, - 295.3, - 295.4, - 295.3, - 295.2, - 295.4, - 296.6, - 298.2, - 298.9, - 289.8, - 287.0, - 289.8, - 294.7, - 298.4, - 298.6, - 298.7, - 299.0, - 299.0, - 298.6, - 298.5, - 298.2, - 297.8, - 297.5, - 297.1, - 296.7, - 296.6, - 296.6, - 296.4, - 296.2, - 296.1, - 296.1, - 295.9, - 296.5, - 296.6, - 295.4, - 295.0, - ], - [ - 292.2, - 290.9, - 290.4, - 290.4, - 290.2, - 288.6, - 289.6, - 290.2, - 292.8, - 292.5, - 299.2, - 289.1, - 287.6, - 289.6, - 290.8, - 290.7, - 295.2, - 295.5, - 296.5, - 297.5, - 289.3, - 291.2, - 287.7, - 298.1, - 298.2, - 288.9, - 286.8, - 285.9, - 286.0, - 292.4, - 295.0, - 296.9, - 296.4, - 295.5, - 294.8, - 294.5, - 294.8, - 295.2, - 295.6, - 295.9, - 296.3, - 296.7, - 296.9, - 297.0, - 296.8, - 296.5, - 296.1, - 295.8, - 295.3, - 294.9, - 294.6, - 294.3, - 294.1, - 293.8, - 293.6, - 293.4, - 293.2, - 293.1, - 293.0, - 293.1, - 293.1, - 293.0, - 293.0, - 293.0, - 292.9, - 293.4, - 294.6, - 296.7, - 290.7, - 283.2, - 283.4, - 296.3, - 294.1, - 297.6, - 297.4, - 297.6, - 295.5, - 295.2, - 298.1, - 297.8, - 297.6, - 297.3, - 297.1, - 296.8, - 296.3, - 295.9, - 295.8, - 295.7, - 295.6, - 295.5, - 295.5, - 294.7, - 293.2, - 292.9, - 292.1, - 292.2, - ], - [ - 291.1, - 288.9, - 286.0, - 287.7, - 287.6, - 286.6, - 287.5, - 287.1, - 290.2, - 290.0, - 297.1, - 287.9, - 286.7, - 288.3, - 290.4, - 288.3, - 295.3, - 295.2, - 295.8, - 288.5, - 288.9, - 288.4, - 287.4, - 287.3, - 291.2, - 286.0, - 284.9, - 280.5, - 282.4, - 280.3, - 283.8, - 293.2, - 293.6, - 293.6, - 292.4, - 292.2, - 292.3, - 292.5, - 293.2, - 293.8, - 294.2, - 294.8, - 295.2, - 295.5, - 295.6, - 295.5, - 295.2, - 294.8, - 294.4, - 294.0, - 293.7, - 293.4, - 293.1, - 292.8, - 292.6, - 292.4, - 292.2, - 292.0, - 291.8, - 291.8, - 291.7, - 291.7, - 291.8, - 291.8, - 291.6, - 292.0, - 293.2, - 295.5, - 284.4, - 280.6, - 286.9, - 294.5, - 295.2, - 296.1, - 295.2, - 291.2, - 296.1, - 296.7, - 296.8, - 296.6, - 296.5, - 296.5, - 296.3, - 296.1, - 295.7, - 295.3, - 295.1, - 294.9, - 294.9, - 294.8, - 294.9, - 294.4, - 290.6, - 290.8, - 290.8, - 291.1, - ], - [ - 290.4, - 288.1, - 285.2, - 285.7, - 286.9, - 287.4, - 287.2, - 286.0, - 287.6, - 289.6, - 290.1, - 285.8, - 284.8, - 287.0, - 289.7, - 288.5, - 294.5, - 294.6, - 286.5, - 286.4, - 284.1, - 285.4, - 287.0, - 288.2, - 287.0, - 282.8, - 281.1, - 278.0, - 278.5, - 280.5, - 279.8, - 276.8, - 289.8, - 292.0, - 290.9, - 290.5, - 290.5, - 290.4, - 290.8, - 291.3, - 291.9, - 292.6, - 293.2, - 293.7, - 294.0, - 294.1, - 294.0, - 293.9, - 293.6, - 293.2, - 292.8, - 292.4, - 292.1, - 291.6, - 291.3, - 291.1, - 290.9, - 290.9, - 290.9, - 290.8, - 290.7, - 290.8, - 290.9, - 290.9, - 290.6, - 290.9, - 292.0, - 293.9, - 279.2, - 280.3, - 285.6, - 293.0, - 293.5, - 293.6, - 292.5, - 293.9, - 294.4, - 294.8, - 295.0, - 295.2, - 295.2, - 295.3, - 295.3, - 295.3, - 295.1, - 294.9, - 294.6, - 294.4, - 294.2, - 294.0, - 294.0, - 294.2, - 294.0, - 289.0, - 289.9, - 290.0, - ], - [ - 288.3, - 287.8, - 287.2, - 286.6, - 286.8, - 287.7, - 288.2, - 287.0, - 286.6, - 287.5, - 284.8, - 283.8, - 284.1, - 293.8, - 294.4, - 283.1, - 280.0, - 280.8, - 283.8, - 284.8, - 281.4, - 284.1, - 284.0, - 275.8, - 272.7, - 278.9, - 274.1, - 272.2, - 277.0, - 274.2, - 277.1, - 276.9, - 275.1, - 288.0, - 288.3, - 289.1, - 289.0, - 288.8, - 288.8, - 289.2, - 289.6, - 290.3, - 290.9, - 291.6, - 292.0, - 292.2, - 292.2, - 292.2, - 292.1, - 291.9, - 291.6, - 291.3, - 291.0, - 290.6, - 290.3, - 290.0, - 289.7, - 289.6, - 289.7, - 289.9, - 289.8, - 289.7, - 289.8, - 289.8, - 289.5, - 289.8, - 290.7, - 279.3, - 278.3, - 280.4, - 281.1, - 288.7, - 288.6, - 288.4, - 288.4, - 291.6, - 292.2, - 292.7, - 293.0, - 293.3, - 293.5, - 293.8, - 293.9, - 294.1, - 294.1, - 294.0, - 293.8, - 293.7, - 293.6, - 293.4, - 293.1, - 293.1, - 293.5, - 287.8, - 287.2, - 287.1, - ], - [ - 283.4, - 285.0, - 285.9, - 285.5, - 286.3, - 287.6, - 287.1, - 286.6, - 286.6, - 284.8, - 282.4, - 282.7, - 284.0, - 293.1, - 274.8, - 275.5, - 279.8, - 279.6, - 278.0, - 283.2, - 282.9, - 276.8, - 261.2, - 245.8, - 259.6, - 256.2, - 259.3, - 267.7, - 277.8, - 274.4, - 275.5, - 275.4, - 274.8, - 284.1, - 284.2, - 285.8, - 286.5, - 286.5, - 287.1, - 287.7, - 288.2, - 288.7, - 289.2, - 289.7, - 290.1, - 290.3, - 290.4, - 290.2, - 290.2, - 290.0, - 289.8, - 289.6, - 289.3, - 289.1, - 289.0, - 288.9, - 288.9, - 288.7, - 288.7, - 288.9, - 288.9, - 288.6, - 288.8, - 288.7, - 288.3, - 288.6, - 281.2, - 277.0, - 276.8, - 277.0, - 275.8, - 277.1, - 276.5, - 276.5, - 276.7, - 288.3, - 290.4, - 291.3, - 291.5, - 291.7, - 291.9, - 292.1, - 292.4, - 292.6, - 292.8, - 292.9, - 292.8, - 292.8, - 292.7, - 292.6, - 292.3, - 292.2, - 292.5, - 292.7, - 282.1, - 283.9, - ], - [ - 279.1, - 280.6, - 283.5, - 283.6, - 289.8, - 290.2, - 290.1, - 290.0, - 290.4, - 290.8, - 280.5, - 281.3, - 283.8, - 276.7, - 274.6, - 276.6, - 276.4, - 275.7, - 272.5, - 281.0, - 278.8, - 254.8, - 251.9, - 253.4, - 256.7, - 247.5, - 257.2, - 263.1, - 270.5, - 272.0, - 273.2, - 273.7, - 274.2, - 280.9, - 279.7, - 282.0, - 282.5, - 282.2, - 284.2, - 285.2, - 286.2, - 287.0, - 287.5, - 287.9, - 288.2, - 288.4, - 288.6, - 288.5, - 288.3, - 288.2, - 287.9, - 287.7, - 287.4, - 287.3, - 287.3, - 287.4, - 287.6, - 287.5, - 287.5, - 287.6, - 287.8, - 287.6, - 287.7, - 287.6, - 287.1, - 279.8, - 279.5, - 273.8, - 273.7, - 274.4, - 274.8, - 275.3, - 275.0, - 275.1, - 274.7, - 284.1, - 288.4, - 289.8, - 290.2, - 290.2, - 290.6, - 290.7, - 290.9, - 291.1, - 291.3, - 291.6, - 291.8, - 291.8, - 291.7, - 291.6, - 291.5, - 291.4, - 291.5, - 291.4, - 282.4, - 278.9, - ], - [ - 278.8, - 277.5, - 278.0, - 288.2, - 288.2, - 288.1, - 288.2, - 288.6, - 289.0, - 289.3, - 278.3, - 280.4, - 277.2, - 272.0, - 275.3, - 276.3, - 275.6, - 273.5, - 269.1, - 269.9, - 255.7, - 246.4, - 251.9, - 253.4, - 245.8, - 246.2, - 258.6, - 263.1, - 267.9, - 270.2, - 270.5, - 271.0, - 276.2, - 276.7, - 274.4, - 277.2, - 269.0, - 266.8, - 280.2, - 282.3, - 283.3, - 284.2, - 285.0, - 285.6, - 285.8, - 286.0, - 286.3, - 286.5, - 286.4, - 286.3, - 286.1, - 285.9, - 285.7, - 285.6, - 285.5, - 285.7, - 286.1, - 286.1, - 286.2, - 286.4, - 286.5, - 286.5, - 286.6, - 286.7, - 277.9, - 276.5, - 273.3, - 269.3, - 271.2, - 272.5, - 272.2, - 271.8, - 271.7, - 271.3, - 270.9, - 270.7, - 284.5, - 287.6, - 288.5, - 288.9, - 289.5, - 289.8, - 289.6, - 289.7, - 290.0, - 290.2, - 290.5, - 290.7, - 290.8, - 290.7, - 290.7, - 290.5, - 290.1, - 290.0, - 289.2, - 281.4, - ], - [ - 285.2, - 286.4, - 286.4, - 286.0, - 286.2, - 286.3, - 286.6, - 286.0, - 276.0, - 274.1, - 274.7, - 274.2, - 270.0, - 272.4, - 287.2, - 274.7, - 275.2, - 275.3, - 275.2, - 260.8, - 251.5, - 268.3, - 270.1, - 263.4, - 252.5, - 263.2, - 252.3, - 262.8, - 264.5, - 265.2, - 266.5, - 268.0, - 272.0, - 270.4, - 247.3, - 271.7, - 275.1, - 266.1, - 277.3, - 278.6, - 280.3, - 281.6, - 282.2, - 282.6, - 283.1, - 283.4, - 283.9, - 284.3, - 284.5, - 284.3, - 283.8, - 283.5, - 283.5, - 283.4, - 283.6, - 284.0, - 284.5, - 284.7, - 284.9, - 285.2, - 285.3, - 285.5, - 285.4, - 285.4, - 273.3, - 267.6, - 265.5, - 264.9, - 263.3, - 271.6, - 270.0, - 268.6, - 268.3, - 268.5, - 268.7, - 270.4, - 281.5, - 285.3, - 286.5, - 287.6, - 288.1, - 288.5, - 288.7, - 288.6, - 288.7, - 288.8, - 289.0, - 289.6, - 289.7, - 289.8, - 289.6, - 289.3, - 288.6, - 287.8, - 279.7, - 279.1, - ], - [ - 273.8, - 284.8, - 284.3, - 283.4, - 284.1, - 284.3, - 269.9, - 276.7, - 275.1, - 272.3, - 266.6, - 264.7, - 269.8, - 274.6, - 283.5, - 272.9, - 272.1, - 273.2, - 272.5, - 263.6, - 259.2, - 256.8, - 262.2, - 266.6, - 266.4, - 261.5, - 258.6, - 264.1, - 262.1, - 262.1, - 260.5, - 263.4, - 255.3, - 246.1, - 253.9, - 266.2, - 270.5, - 272.7, - 262.4, - 270.6, - 276.3, - 277.7, - 277.8, - 277.9, - 278.5, - 279.6, - 280.3, - 280.7, - 281.2, - 281.3, - 281.1, - 281.1, - 281.3, - 281.5, - 281.7, - 282.3, - 282.9, - 283.3, - 283.5, - 284.0, - 284.2, - 284.4, - 284.4, - 278.8, - 269.8, - 264.9, - 264.1, - 262.6, - 262.6, - 268.9, - 265.4, - 264.7, - 264.5, - 265.8, - 266.6, - 265.8, - 267.8, - 279.0, - 281.4, - 283.4, - 284.6, - 285.3, - 285.8, - 286.0, - 286.8, - 286.9, - 287.1, - 288.1, - 288.7, - 288.8, - 288.4, - 288.0, - 287.3, - 286.7, - 277.4, - 276.5, - ], - [ - 274.9, - 283.4, - 282.1, - 269.1, - 281.3, - 261.9, - 270.2, - 267.7, - 280.6, - 281.8, - 282.8, - 273.9, - 265.5, - 281.3, - 272.6, - 269.8, - 268.9, - 269.3, - 271.0, - 266.4, - 261.9, - 257.4, - 261.4, - 253.0, - 261.0, - 261.9, - 257.6, - 260.1, - 257.9, - 258.3, - 256.1, - 252.3, - 255.7, - 247.3, - 251.4, - 240.9, - 265.6, - 270.0, - 261.5, - 269.7, - 270.6, - 274.3, - 274.8, - 275.4, - 276.4, - 277.5, - 277.5, - 277.6, - 278.1, - 278.5, - 278.6, - 279.0, - 279.3, - 279.7, - 280.0, - 280.5, - 281.1, - 281.7, - 282.1, - 282.5, - 283.0, - 283.1, - 283.4, - 276.3, - 268.0, - 265.9, - 262.4, - 261.1, - 261.6, - 262.4, - 262.7, - 263.0, - 262.7, - 264.7, - 265.1, - 264.7, - 266.2, - 265.9, - 276.9, - 279.0, - 280.8, - 281.2, - 280.9, - 281.5, - 283.2, - 283.6, - 284.3, - 286.1, - 287.3, - 287.6, - 286.8, - 286.8, - 286.5, - 286.3, - 275.0, - 276.1, - ], - [ - 274.5, - 271.0, - 266.0, - 268.2, - 266.9, - 266.2, - 266.2, - 263.3, - 277.4, - 279.3, - 280.8, - 273.2, - 273.8, - 278.1, - 269.3, - 267.1, - 265.8, - 264.3, - 266.5, - 265.0, - 266.8, - 262.1, - 257.7, - 260.3, - 253.8, - 252.7, - 247.7, - 255.3, - 255.3, - 253.8, - 254.4, - 250.6, - 250.4, - 243.1, - 244.6, - 248.9, - 249.0, - 266.6, - 262.0, - 270.0, - 272.0, - 273.2, - 273.3, - 274.0, - 274.7, - 275.4, - 275.5, - 275.6, - 275.9, - 276.1, - 276.5, - 277.1, - 277.7, - 278.1, - 278.4, - 278.9, - 279.7, - 280.2, - 280.6, - 281.2, - 281.9, - 282.1, - 282.5, - 276.1, - 272.3, - 267.5, - 260.7, - 261.8, - 262.1, - 259.5, - 259.8, - 261.4, - 261.6, - 272.4, - 273.0, - 259.2, - 261.9, - 262.4, - 264.8, - 274.8, - 276.8, - 276.2, - 276.9, - 277.8, - 279.4, - 280.8, - 282.1, - 284.0, - 285.3, - 286.1, - 285.4, - 285.1, - 285.2, - 285.8, - 285.1, - 283.7, - ], - [ - 273.0, - 274.0, - 266.9, - 261.6, - 264.6, - 265.3, - 264.3, - 262.6, - 262.2, - 264.1, - 268.2, - 268.7, - 267.6, - 266.1, - 263.6, - 262.5, - 260.5, - 260.1, - 259.8, - 258.0, - 258.9, - 259.2, - 256.6, - 251.1, - 247.7, - 252.2, - 249.4, - 249.4, - 253.3, - 249.6, - 249.9, - 246.2, - 246.0, - 244.9, - 242.8, - 246.4, - 249.2, - 250.5, - 264.2, - 269.5, - 271.8, - 271.9, - 271.8, - 272.8, - 273.5, - 273.9, - 273.9, - 274.0, - 274.2, - 274.5, - 275.1, - 275.7, - 276.4, - 276.9, - 277.2, - 277.7, - 278.5, - 279.1, - 279.6, - 280.3, - 281.1, - 281.4, - 281.0, - 275.6, - 271.9, - 266.2, - 263.0, - 260.8, - 257.4, - 257.4, - 256.9, - 258.9, - 259.5, - 269.7, - 252.8, - 258.9, - 261.3, - 262.3, - 264.2, - 272.0, - 273.8, - 268.0, - 274.9, - 276.0, - 277.5, - 279.2, - 280.6, - 282.1, - 283.3, - 284.4, - 284.4, - 284.1, - 285.0, - 285.1, - 284.0, - 282.6, - ], - [ - 271.1, - 272.8, - 268.2, - 266.4, - 264.6, - 262.8, - 263.0, - 261.6, - 261.5, - 261.0, - 260.6, - 260.5, - 260.5, - 260.2, - 259.6, - 258.6, - 256.7, - 257.0, - 257.7, - 256.5, - 256.8, - 257.7, - 258.7, - 253.6, - 252.2, - 251.6, - 251.0, - 251.5, - 252.9, - 251.5, - 249.3, - 244.7, - 242.7, - 242.7, - 240.6, - 242.4, - 248.5, - 248.8, - 265.8, - 269.7, - 270.9, - 270.4, - 269.2, - 271.3, - 272.1, - 272.4, - 272.5, - 272.7, - 273.0, - 273.5, - 274.1, - 274.7, - 275.4, - 275.9, - 276.3, - 276.8, - 277.6, - 278.3, - 279.1, - 279.8, - 280.5, - 280.6, - 280.3, - 266.9, - 265.4, - 261.0, - 259.6, - 258.2, - 256.0, - 255.5, - 255.1, - 257.3, - 257.7, - 258.2, - 257.1, - 260.2, - 258.5, - 260.7, - 260.2, - 260.6, - 264.8, - 267.5, - 274.2, - 275.5, - 276.9, - 278.5, - 279.6, - 280.7, - 281.8, - 282.9, - 282.9, - 283.2, - 284.6, - 283.9, - 281.7, - 280.7, - ], - [ - 272.9, - 278.0, - 266.3, - 264.1, - 264.1, - 261.7, - 260.5, - 260.0, - 258.1, - 256.2, - 255.4, - 254.6, - 254.8, - 255.0, - 254.6, - 256.0, - 254.5, - 254.0, - 255.1, - 256.0, - 256.1, - 256.0, - 257.0, - 257.5, - 256.7, - 254.9, - 252.3, - 253.8, - 251.8, - 248.4, - 245.3, - 242.5, - 240.9, - 240.6, - 239.5, - 239.7, - 245.4, - 244.2, - 260.7, - 268.4, - 269.1, - 268.7, - 240.9, - 267.9, - 269.2, - 270.1, - 270.8, - 271.1, - 272.0, - 272.8, - 273.2, - 274.0, - 274.8, - 275.5, - 276.1, - 276.5, - 277.1, - 277.9, - 278.7, - 279.4, - 280.0, - 279.9, - 267.4, - 266.1, - 263.0, - 261.9, - 258.8, - 256.7, - 255.0, - 255.2, - 254.8, - 254.5, - 256.1, - 255.6, - 256.0, - 268.1, - 253.7, - 258.8, - 255.8, - 255.8, - 261.0, - 266.4, - 273.4, - 275.0, - 276.6, - 278.6, - 279.7, - 280.2, - 281.0, - 281.7, - 282.3, - 282.5, - 283.6, - 282.1, - 269.5, - 272.3, - ], - [ - 279.5, - 278.6, - 276.0, - 264.8, - 272.6, - 272.6, - 259.8, - 257.3, - 254.5, - 251.7, - 250.6, - 250.5, - 250.5, - 250.1, - 250.8, - 252.4, - 253.3, - 251.8, - 252.0, - 252.7, - 252.6, - 252.2, - 252.9, - 254.6, - 255.4, - 255.2, - 255.5, - 251.6, - 249.8, - 244.5, - 243.6, - 239.9, - 238.9, - 237.6, - 238.1, - 239.3, - 240.5, - 250.0, - 255.8, - 266.6, - 266.7, - 267.2, - 245.0, - 243.8, - 265.6, - 267.4, - 268.2, - 268.7, - 270.2, - 271.3, - 272.1, - 272.9, - 273.6, - 274.7, - 275.5, - 276.4, - 277.0, - 277.7, - 278.3, - 278.6, - 278.1, - 269.4, - 266.8, - 263.5, - 263.3, - 262.7, - 260.3, - 257.5, - 254.8, - 253.6, - 252.4, - 251.2, - 251.1, - 251.3, - 262.3, - 268.0, - 249.2, - 256.3, - 252.8, - 250.1, - 256.0, - 271.0, - 273.2, - 275.2, - 277.1, - 278.8, - 279.9, - 280.3, - 280.9, - 281.6, - 282.6, - 282.9, - 283.4, - 282.9, - 280.9, - 270.4, - ], - [ - 279.7, - 277.9, - 275.4, - 273.1, - 257.7, - 272.2, - 269.6, - 250.4, - 251.0, - 248.2, - 245.6, - 246.1, - 246.9, - 246.0, - 247.1, - 248.1, - 248.7, - 248.0, - 248.3, - 248.7, - 248.9, - 248.9, - 248.9, - 249.6, - 250.4, - 250.2, - 247.7, - 246.4, - 244.8, - 244.6, - 241.7, - 239.0, - 238.3, - 236.0, - 233.8, - 235.6, - 236.8, - 240.2, - 250.0, - 258.6, - 257.6, - 257.7, - 256.0, - 242.0, - 257.2, - 264.2, - 264.5, - 265.3, - 266.7, - 268.3, - 269.3, - 271.1, - 272.2, - 272.0, - 273.1, - 274.7, - 275.8, - 276.5, - 277.1, - 277.2, - 266.4, - 266.3, - 262.2, - 262.1, - 260.9, - 261.2, - 259.3, - 256.6, - 254.0, - 251.6, - 249.0, - 248.4, - 251.3, - 250.0, - 253.9, - 263.0, - 245.9, - 252.7, - 254.5, - 246.1, - 268.6, - 271.9, - 273.7, - 275.7, - 277.1, - 278.2, - 279.5, - 280.0, - 280.7, - 281.3, - 281.8, - 281.8, - 281.7, - 282.2, - 281.6, - 265.3, - ], - [ - 279.8, - 277.7, - 255.1, - 258.3, - 255.5, - 270.2, - 266.8, - 246.2, - 247.0, - 245.2, - 242.0, - 243.4, - 243.2, - 243.2, - 244.7, - 245.2, - 244.9, - 243.2, - 243.9, - 244.2, - 244.7, - 244.7, - 244.2, - 244.3, - 245.4, - 243.4, - 240.8, - 239.1, - 238.9, - 238.3, - 236.7, - 236.1, - 234.8, - 233.7, - 232.2, - 231.0, - 229.6, - 237.8, - 243.5, - 244.1, - 242.9, - 240.6, - 250.7, - 250.6, - 239.3, - 252.1, - 250.7, - 250.8, - 256.8, - 263.2, - 267.1, - 268.8, - 269.6, - 250.4, - 257.9, - 258.2, - 259.1, - 262.1, - 261.1, - 259.5, - 259.4, - 261.1, - 259.4, - 257.8, - 257.6, - 257.5, - 255.0, - 254.4, - 251.8, - 248.8, - 245.5, - 249.3, - 250.1, - 249.3, - 250.3, - 252.9, - 246.2, - 249.3, - 255.3, - 261.8, - 268.1, - 271.6, - 273.4, - 274.7, - 275.4, - 276.2, - 278.5, - 279.4, - 280.3, - 280.9, - 281.3, - 281.2, - 281.0, - 281.4, - 281.4, - 280.7, - ], - [ - 278.7, - 277.8, - 264.9, - 264.0, - 254.2, - 267.9, - 249.4, - 242.7, - 244.0, - 242.0, - 240.6, - 241.8, - 241.4, - 241.1, - 241.8, - 241.7, - 240.0, - 238.2, - 238.6, - 239.1, - 239.4, - 239.5, - 239.9, - 240.3, - 240.7, - 238.7, - 236.5, - 235.7, - 234.5, - 233.6, - 232.0, - 229.2, - 230.4, - 227.2, - 226.2, - 229.9, - 232.8, - 235.1, - 237.5, - 239.0, - 239.8, - 240.1, - 240.2, - 241.7, - 245.4, - 245.5, - 246.0, - 244.4, - 255.6, - 260.1, - 262.5, - 266.9, - 267.4, - 250.2, - 259.4, - 258.6, - 258.3, - 257.9, - 256.5, - 254.3, - 254.3, - 256.5, - 256.6, - 254.8, - 251.6, - 252.2, - 251.5, - 249.3, - 246.8, - 245.4, - 244.0, - 242.9, - 248.6, - 248.7, - 248.8, - 248.2, - 249.5, - 252.3, - 246.9, - 258.9, - 267.3, - 270.3, - 272.2, - 248.1, - 253.5, - 273.7, - 276.3, - 277.8, - 279.1, - 279.8, - 281.3, - 281.1, - 280.7, - 280.8, - 280.4, - 279.8, - ], - [ - 276.4, - 276.5, - 275.5, - 275.0, - 250.6, - 252.0, - 256.1, - 241.6, - 241.3, - 241.2, - 239.5, - 239.0, - 238.2, - 239.2, - 238.2, - 237.3, - 236.0, - 234.8, - 234.5, - 234.7, - 235.0, - 234.5, - 234.9, - 236.3, - 236.9, - 235.5, - 234.5, - 233.6, - 231.4, - 229.8, - 228.6, - 228.8, - 227.9, - 227.4, - 232.3, - 234.1, - 234.0, - 232.7, - 232.6, - 234.1, - 233.7, - 233.4, - 235.4, - 237.4, - 237.3, - 240.4, - 241.4, - 242.2, - 251.4, - 252.7, - 255.0, - 261.0, - 261.3, - 245.5, - 252.5, - 252.0, - 250.5, - 251.8, - 252.1, - 252.0, - 251.6, - 252.7, - 252.8, - 248.1, - 247.8, - 248.3, - 247.9, - 245.9, - 244.3, - 243.9, - 243.3, - 243.7, - 243.5, - 242.5, - 247.1, - 246.3, - 247.3, - 246.6, - 245.6, - 252.1, - 266.7, - 269.9, - 271.9, - 249.9, - 252.0, - 243.4, - 273.0, - 270.7, - 270.1, - 271.5, - 278.6, - 255.6, - 257.5, - 277.9, - 277.3, - 276.7, - ], - [ - 274.7, - 274.7, - 274.0, - 273.4, - 240.9, - 248.1, - 247.8, - 246.6, - 243.0, - 240.4, - 237.7, - 244.3, - 241.4, - 241.3, - 232.1, - 232.7, - 233.6, - 232.8, - 231.5, - 231.0, - 230.8, - 230.1, - 230.8, - 232.7, - 234.3, - 232.2, - 231.2, - 230.9, - 229.7, - 228.1, - 227.9, - 227.8, - 227.4, - 228.2, - 229.6, - 229.1, - 229.2, - 230.8, - 231.3, - 231.9, - 231.8, - 231.5, - 231.8, - 234.2, - 236.6, - 238.2, - 240.1, - 241.3, - 240.1, - 238.5, - 239.6, - 243.8, - 255.4, - 242.5, - 250.6, - 249.5, - 245.8, - 245.5, - 244.6, - 244.6, - 244.8, - 244.0, - 244.3, - 245.4, - 245.0, - 244.5, - 243.8, - 243.1, - 242.6, - 242.6, - 241.2, - 242.2, - 244.2, - 243.3, - 240.8, - 245.5, - 246.3, - 245.5, - 243.6, - 247.8, - 266.3, - 269.5, - 247.1, - 253.6, - 249.5, - 247.9, - 247.0, - 250.8, - 266.3, - 261.0, - 263.1, - 270.9, - 274.4, - 275.1, - 274.2, - 273.9, - ], - [ - 272.2, - 272.7, - 272.0, - 271.5, - 270.3, - 270.5, - 242.2, - 243.8, - 247.3, - 263.6, - 258.9, - 246.2, - 240.0, - 240.6, - 239.1, - 235.5, - 236.5, - 238.7, - 229.9, - 227.0, - 227.7, - 226.9, - 226.5, - 229.8, - 232.2, - 230.6, - 230.0, - 229.6, - 229.5, - 228.5, - 227.8, - 227.6, - 227.5, - 228.6, - 229.5, - 229.6, - 229.0, - 228.5, - 228.8, - 229.7, - 230.1, - 230.4, - 231.9, - 238.6, - 238.6, - 238.3, - 238.2, - 237.5, - 239.9, - 244.9, - 246.5, - 245.2, - 248.3, - 255.0, - 234.9, - 238.8, - 242.9, - 248.6, - 248.2, - 250.8, - 250.9, - 247.1, - 248.3, - 241.2, - 241.0, - 240.8, - 240.6, - 240.3, - 239.9, - 244.3, - 242.7, - 240.5, - 241.9, - 241.4, - 237.7, - 237.7, - 244.5, - 244.5, - 247.1, - 252.4, - 266.4, - 269.8, - 249.9, - 253.8, - 245.5, - 238.3, - 238.4, - 242.4, - 247.6, - 249.2, - 245.4, - 255.7, - 264.5, - 264.9, - 266.0, - 269.0, - ], - [ - 269.0, - 269.4, - 269.4, - 269.8, - 271.1, - 270.8, - 268.2, - 266.5, - 265.6, - 264.8, - 261.3, - 246.1, - 235.9, - 237.4, - 238.2, - 235.0, - 235.9, - 237.4, - 237.4, - 228.2, - 237.7, - 235.9, - 223.6, - 225.8, - 227.0, - 228.1, - 227.4, - 226.5, - 226.1, - 226.3, - 226.9, - 228.1, - 227.3, - 226.7, - 227.5, - 235.2, - 235.5, - 235.8, - 235.5, - 235.1, - 235.1, - 235.6, - 236.4, - 238.5, - 239.4, - 239.6, - 240.6, - 240.2, - 239.7, - 241.4, - 243.6, - 241.9, - 241.5, - 251.9, - 244.3, - 240.9, - 241.6, - 242.6, - 242.3, - 243.6, - 243.1, - 242.9, - 245.9, - 238.0, - 238.6, - 238.3, - 238.3, - 240.3, - 241.8, - 244.4, - 243.1, - 238.5, - 239.1, - 239.7, - 239.8, - 242.1, - 247.8, - 245.0, - 251.2, - 264.4, - 268.3, - 269.9, - 232.1, - 250.7, - 244.8, - 242.9, - 241.2, - 237.9, - 239.4, - 246.4, - 245.0, - 249.2, - 248.3, - 247.0, - 251.1, - 263.6, - ], - [ - 267.8, - 268.5, - 268.3, - 269.1, - 269.9, - 268.6, - 266.7, - 265.9, - 264.3, - 262.9, - 255.5, - 237.1, - 230.7, - 236.7, - 238.3, - 235.6, - 234.2, - 235.0, - 234.6, - 237.0, - 240.1, - 234.9, - 234.3, - 231.2, - 225.1, - 227.0, - 228.0, - 227.5, - 227.0, - 226.7, - 228.7, - 237.0, - 234.4, - 235.6, - 236.8, - 236.5, - 235.3, - 235.5, - 236.0, - 236.8, - 237.3, - 237.7, - 237.5, - 237.0, - 237.2, - 237.3, - 237.9, - 238.6, - 238.3, - 238.3, - 239.5, - 239.2, - 238.4, - 240.3, - 238.0, - 237.5, - 238.4, - 239.1, - 239.2, - 239.7, - 240.3, - 241.0, - 242.1, - 241.8, - 241.6, - 240.7, - 240.6, - 241.3, - 242.6, - 243.3, - 242.9, - 242.6, - 236.4, - 238.7, - 241.0, - 250.4, - 251.0, - 253.4, - 262.5, - 268.7, - 268.8, - 231.8, - 243.0, - 241.4, - 239.4, - 238.4, - 234.8, - 235.0, - 237.2, - 241.8, - 242.2, - 247.0, - 246.7, - 247.1, - 248.8, - 257.9, - ], - [ - 266.3, - 268.4, - 267.8, - 267.9, - 267.3, - 266.4, - 265.5, - 264.8, - 262.8, - 262.1, - 259.4, - 245.0, - 231.9, - 235.1, - 237.9, - 236.7, - 235.8, - 235.2, - 235.2, - 236.9, - 236.9, - 234.1, - 232.7, - 231.0, - 231.8, - 233.7, - 234.0, - 225.9, - 227.3, - 236.4, - 237.8, - 239.8, - 236.5, - 236.2, - 236.3, - 236.5, - 236.3, - 237.0, - 237.4, - 237.8, - 238.1, - 237.6, - 236.9, - 236.0, - 235.8, - 236.1, - 236.8, - 237.4, - 237.5, - 237.2, - 237.3, - 237.1, - 237.1, - 237.9, - 237.9, - 238.0, - 238.1, - 237.9, - 237.4, - 237.1, - 237.5, - 238.2, - 238.8, - 239.8, - 240.2, - 240.6, - 240.6, - 240.6, - 240.6, - 241.0, - 242.2, - 243.4, - 236.8, - 239.2, - 240.8, - 241.8, - 249.6, - 254.1, - 240.6, - 239.5, - 239.6, - 236.0, - 232.0, - 229.8, - 229.2, - 228.7, - 227.9, - 229.8, - 234.4, - 238.3, - 241.0, - 250.1, - 247.6, - 247.8, - 249.5, - 254.9, - ], - [ - 266.7, - 266.9, - 265.6, - 265.9, - 265.2, - 264.6, - 264.6, - 262.5, - 250.8, - 241.4, - 240.2, - 239.9, - 236.9, - 238.9, - 240.3, - 238.4, - 236.9, - 236.5, - 234.4, - 233.6, - 234.0, - 234.2, - 233.2, - 233.4, - 235.1, - 233.9, - 231.8, - 232.0, - 232.3, - 234.2, - 233.9, - 235.5, - 235.1, - 235.7, - 236.7, - 237.4, - 237.2, - 237.1, - 237.0, - 236.9, - 237.4, - 237.5, - 237.4, - 236.3, - 235.6, - 235.6, - 235.7, - 235.6, - 235.7, - 235.7, - 235.7, - 235.6, - 235.7, - 235.7, - 235.5, - 235.5, - 235.5, - 235.4, - 235.2, - 235.1, - 235.5, - 236.5, - 237.0, - 237.1, - 237.2, - 237.3, - 237.3, - 237.2, - 237.6, - 239.0, - 241.1, - 246.3, - 232.2, - 234.5, - 234.2, - 231.2, - 231.1, - 233.8, - 236.8, - 238.4, - 236.6, - 236.1, - 234.8, - 235.3, - 233.6, - 233.4, - 235.0, - 237.4, - 239.0, - 239.4, - 240.2, - 241.5, - 242.2, - 244.1, - 247.4, - 257.0, - ], - [ - 247.2, - 247.8, - 250.1, - 246.0, - 246.8, - 247.5, - 245.6, - 243.0, - 238.0, - 235.9, - 237.4, - 238.7, - 238.8, - 239.1, - 240.2, - 236.9, - 235.1, - 236.5, - 234.2, - 231.7, - 231.0, - 231.8, - 232.2, - 232.4, - 235.5, - 235.5, - 236.2, - 235.3, - 234.9, - 235.0, - 235.4, - 236.6, - 236.9, - 237.6, - 238.0, - 238.2, - 238.0, - 238.2, - 238.3, - 238.1, - 238.4, - 238.2, - 238.0, - 237.4, - 236.9, - 237.3, - 236.8, - 236.2, - 236.2, - 236.3, - 236.1, - 235.6, - 235.2, - 234.8, - 234.3, - 233.9, - 233.5, - 233.2, - 232.8, - 232.6, - 233.1, - 234.0, - 234.2, - 234.4, - 234.2, - 234.0, - 233.9, - 233.9, - 233.8, - 234.4, - 235.7, - 238.1, - 235.7, - 235.8, - 240.5, - 224.1, - 225.3, - 227.2, - 232.5, - 240.2, - 239.2, - 238.6, - 239.2, - 239.4, - 235.6, - 235.0, - 233.9, - 235.2, - 235.9, - 236.5, - 239.9, - 238.9, - 238.5, - 239.7, - 242.2, - 245.1, - ], - [ - 237.3, - 237.3, - 238.2, - 237.7, - 238.5, - 239.3, - 238.2, - 236.9, - 235.5, - 236.1, - 237.4, - 237.9, - 237.4, - 237.0, - 237.1, - 236.0, - 235.7, - 236.5, - 236.7, - 236.4, - 235.3, - 234.4, - 234.8, - 234.9, - 235.4, - 235.8, - 236.0, - 235.9, - 236.1, - 236.4, - 236.9, - 237.4, - 237.9, - 238.3, - 238.7, - 238.8, - 238.8, - 238.6, - 238.6, - 238.6, - 238.5, - 238.4, - 238.4, - 238.2, - 238.0, - 238.1, - 237.6, - 236.9, - 236.6, - 236.4, - 236.1, - 235.7, - 235.5, - 235.2, - 234.8, - 234.4, - 234.1, - 233.5, - 232.7, - 231.8, - 231.6, - 232.0, - 232.2, - 232.9, - 233.0, - 232.7, - 232.3, - 232.0, - 231.9, - 232.1, - 232.4, - 233.0, - 233.1, - 234.2, - 236.6, - 232.8, - 232.1, - 232.7, - 233.6, - 236.6, - 235.3, - 235.1, - 236.3, - 234.4, - 234.0, - 234.8, - 235.8, - 237.1, - 238.1, - 239.5, - 240.2, - 240.6, - 239.4, - 238.3, - 238.6, - 238.1, - ], - [ - 231.9, - 231.5, - 231.7, - 231.8, - 232.1, - 232.1, - 231.8, - 231.6, - 231.6, - 232.3, - 233.4, - 233.9, - 233.7, - 233.5, - 233.6, - 234.0, - 235.2, - 236.4, - 237.1, - 237.5, - 237.2, - 237.2, - 237.4, - 237.6, - 237.9, - 238.2, - 238.5, - 238.7, - 238.8, - 238.8, - 238.9, - 239.0, - 239.1, - 239.1, - 239.1, - 239.0, - 238.9, - 239.0, - 238.5, - 238.3, - 238.1, - 238.1, - 238.2, - 238.2, - 238.1, - 238.0, - 237.9, - 237.8, - 237.6, - 237.3, - 237.1, - 237.2, - 237.1, - 236.9, - 236.6, - 236.4, - 236.2, - 235.6, - 235.0, - 234.7, - 234.3, - 233.6, - 233.3, - 233.8, - 235.0, - 235.9, - 235.3, - 234.1, - 234.0, - 234.1, - 233.5, - 233.1, - 233.1, - 233.7, - 234.0, - 232.8, - 232.8, - 233.1, - 233.4, - 233.7, - 233.0, - 232.6, - 232.8, - 233.1, - 233.6, - 234.3, - 234.8, - 235.2, - 235.4, - 235.3, - 235.0, - 234.6, - 234.0, - 233.5, - 233.5, - 232.7, - ], - [ - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - ], - ] - ] +def _make_ugrid_2(filename): + """Create a UGRID file with a 2-d mesh topology.""" + n = netCDF4.Dataset(filename, "w") -def _make_cfa_file(filename): - n = netCDF4.Dataset(filename, "w", format="NETCDF4") + n.Conventions = f"CF-{VN}" - n.Conventions = f"CF-{VN} CFA-0.6.2" - n.comment = ( - "A CFA-netCDF file with non-standarised aggregation instructions" + n.createDimension("time", 2) + n.createDimension("nMesh2_node", 7) + n.createDimension("nMesh2_edge", 9) + n.createDimension("nMesh2_face", 3) + n.createDimension("Two", 2) + n.createDimension("Four", 4) + + Mesh2 = n.createVariable("Mesh2", "i4", ()) + Mesh2.cf_role = "mesh_topology" + Mesh2.topology_dimension = 2 + Mesh2.node_coordinates = "Mesh2_node_x Mesh2_node_y" + Mesh2.face_node_connectivity = "Mesh2_face_nodes" + Mesh2.edge_node_connectivity = "Mesh2_edge_nodes" + Mesh2.face_dimension = "nMesh2_face" + Mesh2.edge_dimension = "nMesh2_edge" + Mesh2.face_face_connectivity = "Mesh2_face_links" + + Mesh2_face_nodes = n.createVariable( + "Mesh2_face_nodes", "i4", ("Four", "nMesh2_face"), fill_value=-99 ) + Mesh2_face_nodes.long_name = "Maps every face to its corner nodes" + Mesh2_face_nodes[...] = [[2, 4, 1], [3, 5, 3], [1, 3, 6], [0, 2, -99]] - n.createDimension("time", 12) - level = n.createDimension("level", 1) - lat = n.createDimension("lat", 73) - lon = n.createDimension("lon", 144) - n.createDimension("f_time", 2) - n.createDimension("f_level", 1) - n.createDimension("f_lat", 1) - n.createDimension("f_lon", 1) - n.createDimension("i", 4) - n.createDimension("j", 2) - - lon = n.createVariable("lon", "f4", ("lon",)) - lon.standard_name = "longitude" - lon.units = "degrees_east" - - lat = n.createVariable("lat", "f4", ("lat",)) - lat.standard_name = "latitude" - lat.units = "degrees_north" - - time = n.createVariable("time", "f4", ("time",)) - time.standard_name = "time" - time.units = "days since 2000-01-01" - - level = n.createVariable("level", "f4", ("level",)) + Mesh2_edge_nodes = n.createVariable( + "Mesh2_edge_nodes", "i4", ("nMesh2_edge", "Two") + ) + Mesh2_edge_nodes.long_name = "Maps every edge to its two nodes" + Mesh2_edge_nodes[...] = [ + [1, 6], + [3, 6], + [3, 1], + [0, 1], + [2, 0], + [2, 3], + [2, 4], + [5, 4], + [3, 5], + ] - tas = n.createVariable("tas", "f4", ()) - tas.standard_name = "air_temperature" - tas.units = "K" - tas.aggregated_dimensions = "time level lat lon" - tas.aggregated_data = "location: aggregation_location file: aggregation_file format: aggregation_format address: aggregation_address tracking_id: aggregation_tracking_id" + # Mesh node coordinates + Mesh2_node_x = n.createVariable("Mesh2_node_x", "f4", ("nMesh2_node",)) + Mesh2_node_x.standard_name = "longitude" + Mesh2_node_x.units = "degrees_east" + Mesh2_node_x[...] = [-45, -43, -45, -43, -45, -43, -40] - loc = n.createVariable("aggregation_location", "i4", ("i", "j")) - loc[0, :] = 6 - loc[1, 0] = level.size - loc[2, 0] = lat.size - loc[3, 0] = lon.size + Mesh2_node_y = n.createVariable("Mesh2_node_y", "f4", ("nMesh2_node",)) + Mesh2_node_y.standard_name = "latitude" + Mesh2_node_y.units = "degrees_north" + Mesh2_node_y[...] = [35, 35, 33, 33, 31, 31, 34] - fil = n.createVariable( - "aggregation_file", str, ("f_time", "f_level", "f_lat", "f_lon") + # Optional mesh topology variables + Mesh2_face_links = n.createVariable( + "Mesh2_face_links", "i4", ("Four", "nMesh2_face"), fill_value=-99 ) - fil[0, 0, 0, 0] = "January-June.nc" - fil[1, 0, 0, 0] = "July-December.nc" + Mesh2_face_links.long_name = "neighbour faces for faces" + Mesh2_face_links[...] = [ + [1, 0, 0], + [2, -99, -99], + [-99, -99, -99], + [-99, -99, -99], + ] - add = n.createVariable( - "aggregation_address", str, ("f_time", "f_level", "f_lat", "f_lon") - ) - add[0, 0, 0, 0] = "tas0" - add[1, 0, 0, 0] = "tas1" + # Non-mesh coordinates + t = n.createVariable("time", "f8", ("time",)) + t.standard_name = "time" + t.units = "seconds since 2016-01-01 00:00:00" + t.bounds = "time_bounds" + t[...] = [43200, 129600] - fmt = n.createVariable("aggregation_format", str, ()) - fmt[()] = "nc" + t_bounds = n.createVariable("time_bounds", "f8", ("time", "Two")) + t_bounds[...] = [[0, 86400], [86400, 172800]] - tid = n.createVariable( - "aggregation_tracking_id", str, ("f_time", "f_level", "f_lat", "f_lon") - ) - tid[0, 0, 0, 0] = "tracking_id0" - tid[1, 0, 0, 0] = "tracking_id1" + # Data variables + ta = n.createVariable("ta", "f4", ("time", "nMesh2_face")) + ta.standard_name = "air_temperature" + ta.units = "K" + ta.mesh = "Mesh2" + ta.location = "face" + ta[...] = [[282.96, 282.69, 283.21], [281.53, 280.99, 281.23]] + + v = n.createVariable("v", "f4", ("time", "nMesh2_edge")) + v.standard_name = "northward_wind" + v.units = "ms-1" + v.mesh = "Mesh2" + v.location = "edge" + v[...] = [ + [10.2, 10.63, 8.74, 9.05, 8.15, 10.89, 8.44, 10.66, 8.93], + [9.66, 10.74, 9.24, 10.58, 9.79, 10.27, 10.58, 11.68, 11.22], + ] - n.close() + pa = n.createVariable("pa", "f4", ("time", "nMesh2_node")) + pa.standard_name = "air_pressure" + pa.units = "hPa" + pa.mesh = "Mesh2" + pa.location = "node" + pa[...] = [ + [999.67, 1006.45, 999.85, 1006.55, 1006.14, 1005.68, 999.48], + [ + 1003.48, + 1006.42, + 1000.83, + 1002.98, + 1008.28, + 1002.97, + 1002.47, + ], + ] + n.close() return filename @@ -15632,21 +5510,16 @@ def _make_cfa_file(filename): "geometry_interior_ring_2.nc" ) -gathered = _make_gathered_file("gathered.nc") - string_char_file = _make_string_char_file("string_char.nc") -broken_bounds_file = _make_broken_bounds_cdl("broken_bounds.cdl") - subsampled_file_1 = _make_subsampled_1("subsampled_1.nc") subsampled_file_1 = _make_subsampled_2("subsampled_2.nc") -regrid_file = _make_regrid_file("regrid.nc") - -cfa_file = _make_cfa_file("cfa.nc") +ugrid_1 = _make_ugrid_1("ugrid_1.nc") +ugrid_2 = _make_ugrid_2("ugrid_2.nc") if __name__ == "__main__": print("Run date:", datetime.datetime.now()) - cf.environment() + cfdm.environment() print() unittest.main(verbosity=2) diff --git a/cf/test/create_test_files_2.py b/cf/test/create_test_files_2.py new file mode 100644 index 0000000000..24694623e1 --- /dev/null +++ b/cf/test/create_test_files_2.py @@ -0,0 +1,10440 @@ +import datetime +import faulthandler +import unittest + +import numpy as np + +faulthandler.enable() # to debug seg faults and timeouts + +import netCDF4 + +import cf + +VN = cf.CF() + + +def _make_broken_bounds_cdl(filename): + with open(filename, mode="w") as f: + f.write( + """netcdf broken_bounds { +dimensions: + lat = 180 ; + bnds = 2 ; + lon = 288 ; + time = UNLIMITED ; // (1825 currently) +variables: + double lat(lat) ; + lat:long_name = "latitude" ; + lat:units = "degrees_north" ; + lat:axis = "Y" ; + lat:bounds = "lat_bnds" ; + lat:standard_name = "latitude" ; + lat:cell_methods = "time: point" ; + double lat_bnds(lat, bnds) ; + lat_bnds:long_name = "latitude bounds" ; + lat_bnds:units = "degrees_north" ; + lat_bnds:axis = "Y" ; + double lon(lon) ; + lon:long_name = "longitude" ; + lon:units = "degrees_east" ; + lon:axis = "X" ; + lon:bounds = "lon_bnds" ; + lon:standard_name = "longitude" ; + lon:cell_methods = "time: point" ; + double lon_bnds(lon, bnds) ; + lon_bnds:long_name = "longitude bounds" ; + lon_bnds:units = "m" ; + lon_bnds:axis = "X" ; + float pr(time, lat, lon) ; + pr:long_name = "Precipitation" ; + pr:units = "kg m-2 s-1" ; + pr:missing_value = 1.e+20f ; + pr:_FillValue = 1.e+20f ; + pr:cell_methods = "area: time: mean" ; + pr:cell_measures = "area: areacella" ; + pr:standard_name = "precipitation_flux" ; + pr:interp_method = "conserve_order1" ; + pr:original_name = "pr" ; + double time(time) ; + time:long_name = "time" ; + time:units = "days since 1850-01-01 00:00:00" ; + time:axis = "T" ; + time:calendar_type = "noleap" ; + time:calendar = "noleap" ; + time:bounds = "time_bnds" ; + time:standard_name = "time" ; + time:description = "Temporal mean" ; + double time_bnds(time, bnds) ; + time_bnds:long_name = "time axis boundaries" ; + time_bnds:units = "days since 1850-01-01 00:00:00" ; + +// global attributes: + :external_variables = "areacella" ; + :Conventions = "CF-""" + + VN + + """" ; + :source = "model" ; + :comment = "Bounds variable has incompatible units to its parent coordinate variable" ; +} +""" + ) + + +def _make_regrid_file(filename): + n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") + + n.Conventions = "CF-" + VN + + n.createDimension("time", 2) + n.createDimension("bounds2", 2) + n.createDimension("latitude", 30) + n.createDimension("longitude", 48) + n.createDimension("time_1", 1) + n.createDimension("lat", 73) + n.createDimension("lon", 96) + + latitude = n.createVariable("latitude", "f8", ("latitude",)) + latitude.standard_name = "latitude" + latitude.units = "degrees_north" + latitude.bounds = "latitude_bounds" + latitude[...] = np.arange(-87, 90.0, 6) + + longitude = n.createVariable("longitude", "f8", ("longitude",)) + longitude.standard_name = "longitude" + longitude.units = "degrees_east" + longitude.bounds = "longitude_bounds" + longitude[...] = np.arange(3.75, 360, 7.5) + + lat = n.createVariable("lat", "f8", ("lat",)) + lat.standard_name = "latitude" + lat.units = "degrees_north" + lat.bounds = "lat_bounds" + lat[...] = np.arange(-90, 91.0, 2.5) + + lon = n.createVariable("lon", "f8", ("lon",)) + lon.standard_name = "longitude" + lon.units = "degrees_east" + lon.bounds = "lon_bounds" + lon[...] = np.arange(3.75, 361, 3.75) + + longitude_bounds = n.createVariable( + "longitude_bounds", "f8", ("longitude", "bounds2") + ) + longitude_bounds[..., 0] = longitude[...] - 3.75 + longitude_bounds[..., 1] = longitude[...] + 3.75 + + latitude_bounds = n.createVariable( + "latitude_bounds", "f8", ("latitude", "bounds2") + ) + latitude_bounds[..., 0] = latitude[...] - 3 + latitude_bounds[..., 1] = latitude[...] + 3 + + lon_bounds = n.createVariable("lon_bounds", "f8", ("lon", "bounds2")) + lon_bounds[..., 0] = lon[...] - 1.875 + lon_bounds[..., 1] = lon[...] + 1.875 + + lat_bounds = n.createVariable("lat_bounds", "f8", ("lat", "bounds2")) + lat_bounds[..., 0] = lat[...] - 1.25 + lat_bounds[..., 1] = lat[...] + 1.25 + + time = n.createVariable("time", "f4", ("time",)) + time.standard_name = "time" + time.units = "days since 1860-1-1" + time.calendar = "360_day" + time.axis = "T" + time.bounds = "time_bounds" + time[...] = [15, 45] + + time_bounds = n.createVariable("time_bounds", "f4", ("time", "bounds2")) + time_bounds[...] = [ + [ + 0, + 30, + ], + [30, 60], + ] + + time_1 = n.createVariable("time_1", "f4", ("time_1",)) + time_1.standard_name = "time" + time_1.units = "days since 1860-1-1" + time_1.calendar = "360_day" + time_1.axis = "T" + time_1.bounds = "time_1_bounds" + time_1[...] = 15 + + time_1_bounds = n.createVariable( + "time_1_bounds", "f4", ("time_1", "bounds2") + ) + time_1_bounds[...] = [0, 30] + + height = n.createVariable("height", "f8", ()) + height.units = "m" + height.standard_name = "height" + height.positive = "up" + height.axis = "Z" + height[...] = 2 + + src = n.createVariable("src", "f8", ("time", "latitude", "longitude")) + src.standard_name = "air_temperature" + src.units = "K" + src.coordinates = "height" + src.cell_methods = "time: mean" + + # Don't generate this data randomly - it's useful to see the real + # patterns of global temperature. + src[...] = [ + [ + [ + 243.6, + 242.4, + 241.8, + 241.5, + 241.2, + 240.8, + 240.5, + 240.4, + 240.2, + 240.5, + 241.2, + 241.9, + 242.5, + 243.0, + 243.4, + 244.1, + 245.2, + 246.4, + 247.0, + 247.4, + 248.3, + 250.2, + 251.6, + 252.3, + 253.9, + 255.8, + 257.6, + 258.7, + 258.5, + 257.7, + 256.8, + 255.6, + 254.1, + 252.0, + 251.6, + 252.4, + 254.0, + 255.1, + 254.8, + 254.3, + 253.4, + 252.6, + 251.5, + 249.9, + 248.3, + 246.4, + 244.9, + 244.2, + ], + [ + 243.3, + 241.0, + 239.8, + 238.3, + 237.4, + 237.4, + 238.9, + 240.3, + 241.3, + 241.2, + 241.2, + 240.8, + 241.0, + 242.6, + 244.4, + 245.5, + 246.4, + 248.2, + 249.8, + 251.0, + 254.3, + 260.7, + 265.4, + 266.1, + 265.9, + 265.9, + 266.4, + 266.1, + 265.3, + 264.2, + 262.8, + 261.8, + 261.0, + 258.7, + 256.9, + 257.3, + 259.5, + 262.7, + 264.4, + 264.9, + 265.4, + 264.2, + 264.7, + 263.4, + 258.3, + 251.8, + 247.2, + 245.4, + ], + [ + 248.6, + 245.7, + 245.6, + 244.7, + 243.3, + 243.3, + 244.2, + 245.2, + 249.4, + 251.7, + 248.8, + 245.0, + 243.0, + 244.0, + 245.7, + 245.2, + 244.7, + 246.0, + 246.9, + 248.7, + 252.4, + 257.6, + 266.8, + 269.5, + 269.7, + 270.1, + 270.5, + 270.6, + 270.2, + 269.2, + 266.7, + 266.1, + 266.7, + 267.5, + 267.3, + 266.2, + 266.5, + 268.1, + 267.3, + 267.5, + 271.5, + 271.5, + 271.5, + 271.0, + 267.6, + 261.5, + 255.5, + 252.0, + ], + [ + 270.3, + 269.9, + 269.6, + 268.4, + 266.0, + 264.0, + 258.8, + 256.8, + 262.0, + 265.2, + 263.6, + 257.3, + 254.3, + 253.9, + 255.6, + 256.9, + 255.9, + 253.9, + 254.5, + 261.0, + 263.8, + 267.9, + 270.5, + 271.8, + 272.3, + 272.6, + 273.4, + 273.9, + 273.7, + 273.6, + 273.3, + 273.4, + 273.9, + 273.8, + 273.8, + 273.6, + 274.4, + 274.6, + 271.1, + 268.0, + 271.2, + 271.5, + 271.4, + 271.3, + 271.6, + 271.5, + 270.8, + 270.5, + ], + [ + 274.5, + 274.3, + 274.4, + 274.3, + 274.5, + 274.5, + 273.4, + 273.1, + 273.2, + 273.3, + 273.0, + 271.5, + 271.6, + 272.5, + 272.8, + 272.8, + 272.6, + 272.5, + 272.1, + 272.5, + 273.5, + 273.6, + 274.0, + 274.8, + 274.9, + 275.0, + 275.0, + 275.0, + 274.9, + 274.7, + 274.8, + 274.9, + 275.1, + 275.2, + 275.5, + 275.8, + 276.0, + 276.2, + 276.0, + 273.4, + 272.8, + 273.9, + 274.5, + 274.6, + 274.7, + 274.7, + 274.7, + 274.6, + ], + [ + 275.3, + 275.2, + 275.4, + 275.3, + 275.4, + 275.7, + 275.7, + 275.8, + 275.9, + 275.5, + 274.9, + 274.5, + 274.3, + 274.6, + 274.7, + 275.0, + 275.2, + 275.3, + 275.5, + 275.6, + 276.0, + 276.7, + 277.0, + 276.8, + 276.8, + 277.0, + 277.1, + 276.8, + 276.7, + 277.1, + 277.0, + 277.4, + 277.3, + 277.2, + 277.2, + 277.3, + 277.5, + 278.0, + 278.8, + 279.1, + 278.3, + 278.2, + 277.8, + 277.3, + 277.4, + 276.9, + 276.4, + 275.8, + ], + [ + 278.6, + 278.7, + 278.4, + 278.0, + 277.8, + 278.0, + 278.3, + 278.2, + 278.1, + 278.0, + 278.7, + 279.4, + 279.1, + 279.2, + 279.1, + 278.9, + 279.0, + 279.6, + 279.8, + 280.1, + 280.2, + 280.6, + 281.1, + 280.0, + 280.0, + 280.5, + 281.2, + 281.0, + 280.8, + 281.3, + 281.6, + 281.4, + 281.1, + 280.7, + 280.3, + 280.1, + 280.3, + 280.7, + 280.8, + 283.0, + 281.4, + 280.3, + 279.6, + 279.4, + 279.7, + 279.9, + 279.1, + 278.6, + ], + [ + 283.9, + 283.2, + 282.5, + 281.8, + 281.6, + 282.0, + 282.4, + 282.3, + 282.3, + 283.0, + 284.3, + 284.2, + 284.0, + 283.7, + 283.6, + 283.7, + 284.0, + 284.2, + 284.3, + 285.0, + 285.2, + 285.1, + 285.2, + 286.9, + 287.3, + 286.5, + 286.2, + 285.7, + 285.6, + 285.7, + 285.5, + 285.2, + 285.0, + 284.5, + 284.0, + 283.4, + 283.0, + 283.1, + 283.0, + 288.0, + 285.5, + 285.2, + 284.0, + 283.2, + 283.4, + 284.7, + 284.9, + 284.6, + ], + [ + 289.3, + 288.6, + 288.9, + 289.4, + 288.8, + 289.1, + 289.5, + 289.3, + 289.2, + 289.6, + 289.4, + 288.9, + 288.4, + 288.1, + 288.0, + 288.2, + 288.3, + 288.4, + 289.4, + 290.3, + 291.6, + 290.6, + 290.8, + 291.8, + 291.8, + 290.8, + 290.1, + 290.1, + 290.7, + 290.5, + 290.1, + 289.9, + 289.6, + 289.1, + 288.5, + 287.6, + 286.8, + 286.9, + 287.5, + 294.9, + 293.2, + 291.4, + 289.9, + 289.6, + 290.1, + 290.5, + 290.3, + 289.8, + ], + [ + 292.5, + 292.3, + 293.5, + 292.9, + 296.4, + 295.3, + 294.8, + 294.7, + 294.4, + 293.9, + 293.1, + 292.5, + 291.8, + 291.3, + 291.9, + 293.9, + 292.1, + 293.6, + 297.5, + 295.3, + 295.3, + 295.4, + 295.1, + 294.6, + 294.5, + 294.0, + 294.0, + 294.6, + 295.1, + 295.1, + 295.1, + 295.0, + 294.7, + 294.1, + 293.3, + 292.0, + 290.6, + 289.5, + 293.3, + 301.4, + 299.6, + 296.1, + 294.7, + 294.5, + 294.6, + 294.8, + 294.0, + 293.0, + ], + [ + 293.4, + 293.4, + 295.6, + 293.0, + 297.2, + 299.2, + 298.2, + 297.2, + 296.2, + 295.3, + 294.6, + 294.2, + 294.3, + 294.2, + 295.8, + 298.7, + 297.5, + 299.4, + 300.3, + 298.5, + 297.7, + 297.8, + 297.4, + 297.0, + 296.9, + 296.7, + 297.0, + 297.1, + 297.3, + 297.4, + 297.2, + 296.8, + 296.2, + 295.6, + 294.8, + 293.6, + 292.1, + 290.7, + 290.9, + 297.7, + 300.3, + 297.1, + 298.1, + 296.9, + 295.9, + 295.2, + 294.4, + 293.7, + ], + [ + 295.2, + 295.6, + 295.6, + 295.3, + 298.0, + 300.5, + 298.8, + 298.5, + 297.4, + 296.6, + 296.5, + 296.6, + 296.7, + 296.8, + 298.6, + 301.8, + 301.4, + 299.2, + 299.3, + 299.3, + 300.0, + 299.5, + 299.1, + 299.1, + 298.9, + 298.8, + 299.0, + 299.0, + 298.9, + 298.5, + 297.5, + 296.3, + 295.0, + 294.3, + 293.7, + 292.8, + 292.0, + 292.6, + 290.3, + 292.7, + 299.9, + 296.4, + 297.0, + 297.4, + 296.1, + 295.3, + 294.6, + 294.5, + ], + [ + 297.4, + 296.9, + 294.4, + 294.9, + 296.7, + 300.0, + 299.4, + 299.2, + 298.7, + 298.7, + 299.3, + 299.7, + 299.7, + 299.8, + 300.7, + 301.9, + 301.3, + 300.1, + 301.1, + 301.1, + 301.4, + 301.1, + 300.7, + 300.3, + 300.3, + 300.6, + 300.5, + 300.4, + 300.0, + 299.4, + 298.2, + 296.5, + 294.9, + 293.7, + 292.9, + 292.9, + 293.9, + 293.8, + 289.6, + 297.1, + 298.9, + 296.4, + 296.1, + 298.7, + 297.6, + 296.6, + 296.1, + 296.4, + ], + [ + 300.5, + 299.9, + 295.9, + 295.1, + 295.0, + 299.8, + 300.3, + 300.1, + 300.2, + 300.4, + 300.7, + 300.8, + 301.0, + 301.1, + 301.5, + 301.9, + 302.1, + 302.2, + 302.0, + 300.7, + 301.9, + 301.7, + 301.5, + 301.3, + 301.3, + 301.3, + 301.1, + 300.9, + 300.6, + 300.0, + 299.0, + 297.6, + 296.4, + 295.7, + 295.5, + 296.0, + 297.4, + 295.2, + 298.4, + 300.2, + 298.8, + 298.2, + 297.6, + 299.5, + 299.2, + 298.7, + 298.8, + 299.4, + ], + [ + 301.4, + 299.9, + 298.7, + 296.7, + 294.5, + 299.3, + 300.2, + 300.1, + 300.2, + 300.5, + 300.6, + 300.7, + 301.1, + 300.2, + 301.2, + 300.7, + 301.7, + 302.3, + 300.1, + 300.7, + 300.8, + 300.6, + 300.4, + 300.1, + 299.8, + 299.5, + 299.0, + 298.5, + 298.2, + 297.9, + 297.5, + 297.2, + 297.1, + 297.3, + 297.8, + 298.5, + 299.2, + 297.0, + 300.9, + 300.5, + 300.2, + 299.5, + 299.7, + 300.3, + 300.4, + 300.5, + 300.7, + 301.0, + ], + [ + 301.7, + 298.3, + 298.4, + 297.1, + 296.4, + 299.3, + 299.3, + 299.7, + 300.1, + 300.2, + 300.1, + 300.5, + 301.3, + 299.8, + 301.0, + 299.6, + 301.9, + 301.9, + 301.7, + 301.3, + 300.8, + 300.5, + 300.2, + 300.0, + 299.6, + 299.3, + 299.2, + 298.9, + 298.6, + 298.5, + 298.3, + 298.3, + 298.4, + 298.8, + 299.2, + 299.6, + 299.7, + 297.9, + 298.3, + 299.1, + 300.2, + 299.9, + 299.9, + 300.2, + 300.6, + 301.0, + 301.3, + 301.3, + ], + [ + 296.5, + 296.5, + 298.6, + 298.1, + 297.5, + 291.4, + 294.6, + 298.1, + 298.8, + 299.6, + 297.7, + 299.7, + 300.3, + 299.2, + 300.4, + 301.3, + 299.5, + 300.9, + 300.8, + 300.5, + 300.2, + 299.9, + 299.5, + 299.3, + 299.1, + 299.1, + 299.1, + 299.0, + 299.0, + 299.1, + 299.3, + 299.4, + 299.8, + 299.8, + 299.3, + 299.6, + 299.1, + 298.2, + 298.7, + 300.1, + 299.2, + 299.0, + 298.6, + 298.6, + 299.0, + 299.9, + 298.2, + 297.5, + ], + [ + 296.9, + 296.4, + 296.6, + 295.1, + 297.1, + 293.1, + 290.8, + 295.7, + 296.5, + 297.2, + 293.9, + 297.9, + 297.6, + 293.8, + 295.8, + 298.8, + 298.2, + 298.9, + 298.9, + 299.1, + 298.9, + 298.2, + 297.5, + 296.9, + 296.6, + 296.3, + 296.2, + 296.0, + 296.0, + 296.4, + 296.8, + 297.1, + 297.6, + 298.7, + 296.1, + 294.9, + 296.5, + 298.6, + 298.3, + 298.6, + 298.2, + 297.6, + 297.2, + 296.9, + 296.7, + 297.3, + 297.9, + 297.5, + ], + [ + 290.4, + 289.2, + 288.8, + 289.8, + 293.2, + 290.9, + 288.7, + 291.5, + 295.4, + 292.9, + 289.2, + 292.3, + 289.4, + 284.6, + 287.3, + 293.6, + 294.7, + 293.8, + 294.2, + 295.1, + 295.9, + 296.3, + 296.0, + 295.3, + 294.6, + 294.0, + 293.4, + 293.0, + 292.7, + 292.7, + 292.7, + 292.7, + 293.2, + 293.8, + 284.9, + 292.3, + 296.2, + 295.6, + 296.5, + 297.3, + 296.9, + 296.4, + 295.8, + 295.4, + 295.3, + 294.3, + 292.3, + 292.0, + ], + [ + 287.2, + 286.3, + 287.4, + 286.9, + 287.2, + 284.8, + 288.8, + 285.3, + 285.1, + 284.1, + 282.3, + 276.0, + 275.1, + 274.6, + 277.2, + 277.9, + 286.6, + 288.8, + 289.0, + 289.7, + 290.8, + 291.9, + 292.5, + 292.4, + 292.0, + 291.4, + 290.7, + 290.2, + 289.9, + 290.0, + 290.0, + 289.9, + 289.7, + 284.1, + 279.8, + 286.4, + 287.8, + 291.0, + 293.1, + 293.7, + 294.0, + 294.2, + 294.1, + 293.8, + 293.4, + 293.4, + 289.8, + 287.6, + ], + [ + 280.6, + 285.5, + 288.7, + 288.8, + 286.8, + 281.0, + 278.3, + 276.1, + 275.1, + 274.5, + 259.3, + 252.7, + 252.3, + 264.4, + 271.9, + 273.4, + 278.4, + 279.7, + 279.1, + 284.5, + 286.3, + 287.4, + 287.9, + 288.1, + 287.9, + 287.4, + 287.1, + 287.1, + 287.3, + 287.4, + 287.5, + 286.6, + 280.6, + 274.1, + 274.0, + 274.2, + 274.0, + 280.3, + 288.9, + 290.1, + 290.6, + 290.9, + 291.3, + 291.6, + 291.5, + 291.3, + 289.6, + 281.6, + ], + [ + 283.4, + 283.8, + 281.9, + 278.6, + 274.7, + 270.8, + 275.5, + 275.8, + 273.4, + 263.9, + 261.0, + 262.5, + 259.6, + 261.2, + 263.1, + 263.7, + 257.6, + 263.7, + 270.0, + 273.9, + 278.9, + 279.9, + 281.1, + 282.0, + 282.3, + 282.1, + 282.3, + 283.0, + 283.8, + 284.4, + 284.7, + 279.8, + 267.4, + 263.8, + 267.3, + 266.6, + 266.9, + 269.3, + 279.5, + 284.7, + 286.3, + 286.8, + 287.5, + 288.5, + 289.0, + 288.4, + 285.2, + 278.3, + ], + [ + 274.1, + 268.7, + 266.7, + 267.4, + 275.8, + 272.9, + 272.3, + 266.8, + 265.1, + 263.9, + 260.3, + 255.9, + 254.0, + 254.2, + 254.2, + 251.0, + 246.6, + 247.9, + 260.6, + 268.4, + 272.8, + 274.1, + 275.4, + 275.8, + 276.4, + 277.3, + 278.2, + 279.1, + 280.3, + 281.3, + 282.2, + 276.3, + 266.5, + 261.3, + 260.0, + 260.9, + 266.2, + 262.4, + 264.5, + 274.0, + 276.3, + 278.6, + 281.2, + 283.9, + 285.7, + 285.4, + 284.7, + 279.6, + ], + [ + 272.7, + 265.5, + 263.2, + 260.9, + 259.3, + 258.8, + 258.3, + 257.4, + 256.2, + 256.5, + 257.0, + 255.2, + 252.8, + 252.1, + 249.8, + 244.3, + 241.6, + 242.7, + 251.1, + 267.8, + 266.7, + 266.4, + 271.4, + 272.1, + 273.2, + 274.5, + 275.8, + 276.8, + 278.2, + 279.6, + 278.7, + 268.7, + 262.3, + 257.8, + 255.5, + 256.1, + 257.5, + 260.0, + 258.8, + 260.5, + 268.1, + 275.4, + 278.5, + 280.7, + 282.4, + 283.3, + 281.9, + 275.9, + ], + [ + 276.9, + 267.1, + 268.1, + 255.3, + 249.1, + 247.3, + 247.4, + 249.0, + 248.9, + 249.3, + 249.4, + 250.5, + 250.6, + 247.2, + 243.4, + 239.2, + 236.3, + 236.0, + 243.7, + 256.7, + 256.2, + 249.1, + 261.6, + 264.2, + 268.2, + 271.2, + 270.2, + 272.0, + 273.9, + 273.1, + 266.5, + 262.1, + 260.5, + 256.4, + 251.8, + 250.0, + 252.0, + 257.4, + 252.0, + 254.8, + 270.0, + 275.2, + 278.1, + 280.0, + 281.3, + 282.1, + 282.0, + 275.4, + ], + [ + 274.8, + 263.2, + 258.6, + 246.4, + 242.3, + 240.9, + 240.9, + 240.7, + 238.5, + 238.7, + 239.0, + 239.7, + 238.6, + 235.6, + 233.2, + 230.9, + 229.4, + 231.4, + 234.8, + 238.1, + 238.7, + 241.6, + 244.2, + 247.5, + 257.9, + 264.2, + 254.9, + 256.2, + 256.3, + 254.9, + 255.7, + 253.4, + 251.6, + 249.1, + 245.6, + 245.1, + 247.2, + 248.4, + 248.9, + 257.5, + 270.1, + 259.8, + 265.1, + 275.8, + 277.7, + 274.3, + 278.0, + 278.9, + ], + [ + 273.2, + 268.4, + 256.0, + 247.6, + 250.2, + 244.8, + 239.2, + 234.9, + 234.6, + 230.2, + 229.6, + 230.8, + 231.4, + 229.9, + 228.3, + 227.7, + 228.3, + 230.0, + 230.5, + 231.3, + 231.8, + 235.8, + 238.5, + 239.8, + 242.0, + 246.1, + 248.1, + 244.2, + 246.0, + 247.0, + 246.2, + 243.8, + 242.5, + 241.8, + 242.7, + 241.8, + 241.7, + 242.4, + 245.5, + 254.0, + 263.4, + 250.4, + 244.5, + 248.4, + 255.2, + 261.2, + 267.8, + 270.9, + ], + [ + 268.5, + 269.1, + 268.5, + 265.7, + 262.3, + 243.8, + 236.0, + 236.2, + 235.6, + 235.6, + 234.7, + 229.1, + 228.7, + 227.4, + 228.8, + 233.2, + 233.0, + 235.3, + 235.9, + 236.5, + 236.9, + 237.3, + 237.9, + 238.7, + 239.2, + 239.6, + 241.5, + 239.2, + 239.7, + 240.2, + 241.2, + 240.7, + 240.0, + 240.8, + 242.7, + 240.8, + 239.1, + 245.3, + 251.1, + 258.6, + 247.1, + 239.5, + 237.2, + 235.4, + 241.2, + 246.8, + 247.9, + 258.9, + ], + [ + 259.5, + 258.8, + 258.0, + 253.9, + 243.8, + 239.9, + 238.3, + 237.7, + 235.8, + 233.8, + 233.3, + 233.2, + 234.4, + 232.8, + 234.4, + 236.1, + 236.5, + 237.4, + 237.5, + 237.6, + 237.8, + 236.8, + 236.3, + 236.2, + 236.1, + 235.9, + 235.7, + 235.3, + 234.9, + 234.6, + 235.6, + 236.4, + 236.5, + 236.5, + 237.7, + 239.6, + 235.8, + 232.6, + 234.3, + 238.0, + 236.9, + 235.3, + 233.4, + 235.4, + 238.5, + 241.4, + 243.3, + 252.2, + ], + [ + 235.4, + 235.8, + 236.3, + 235.0, + 234.9, + 236.2, + 235.8, + 235.5, + 236.3, + 236.6, + 235.6, + 235.9, + 236.6, + 236.9, + 237.2, + 237.9, + 238.5, + 238.7, + 238.6, + 238.4, + 238.2, + 238.1, + 237.9, + 237.2, + 236.7, + 236.2, + 235.8, + 235.2, + 234.3, + 233.1, + 232.7, + 233.3, + 233.8, + 233.0, + 232.9, + 233.1, + 234.2, + 233.5, + 233.0, + 234.8, + 234.5, + 234.2, + 234.7, + 236.3, + 237.7, + 238.0, + 236.8, + 236.1, + ], + ], + [ + [ + 242.9, + 242.6, + 241.9, + 242.3, + 240.2, + 240.4, + 240.3, + 241.3, + 240.0, + 239.5, + 242.1, + 241.0, + 243.0, + 243.7, + 244.3, + 243.8, + 244.9, + 246.1, + 247.5, + 247.8, + 248.7, + 249.7, + 250.9, + 253.1, + 254.7, + 256.3, + 257.2, + 259.0, + 258.2, + 257.7, + 256.6, + 255.9, + 254.4, + 251.2, + 250.6, + 252.5, + 254.6, + 254.5, + 255.0, + 254.9, + 253.3, + 253.3, + 251.8, + 249.6, + 248.8, + 247.2, + 244.7, + 244.3, + ], + [ + 243.6, + 240.8, + 239.4, + 237.9, + 237.0, + 237.5, + 239.4, + 239.8, + 241.0, + 240.8, + 240.8, + 241.1, + 240.8, + 241.7, + 245.4, + 246.1, + 246.6, + 249.1, + 250.7, + 250.8, + 254.7, + 260.1, + 265.0, + 266.1, + 265.4, + 265.8, + 265.6, + 266.5, + 266.2, + 263.3, + 262.6, + 261.4, + 261.5, + 258.0, + 256.7, + 256.6, + 260.0, + 262.8, + 264.3, + 265.7, + 265.2, + 264.2, + 265.5, + 263.1, + 257.6, + 252.6, + 246.3, + 245.4, + ], + [ + 248.9, + 245.5, + 246.5, + 244.3, + 242.8, + 243.0, + 243.2, + 244.5, + 249.7, + 252.2, + 249.1, + 244.7, + 242.8, + 244.1, + 245.0, + 245.6, + 245.6, + 245.5, + 247.5, + 249.4, + 252.2, + 257.6, + 266.6, + 269.0, + 270.0, + 271.0, + 270.0, + 270.2, + 271.0, + 269.2, + 266.4, + 266.2, + 266.8, + 267.2, + 268.0, + 265.9, + 266.2, + 268.4, + 268.2, + 267.6, + 271.8, + 271.2, + 272.1, + 271.8, + 267.5, + 261.8, + 255.2, + 252.6, + ], + [ + 270.4, + 269.6, + 269.1, + 267.5, + 266.9, + 264.2, + 259.1, + 256.5, + 262.1, + 266.1, + 263.7, + 256.9, + 255.1, + 253.5, + 255.6, + 256.0, + 256.6, + 254.5, + 253.5, + 261.3, + 264.7, + 268.4, + 271.0, + 271.3, + 271.5, + 273.1, + 273.2, + 273.8, + 272.9, + 274.1, + 272.8, + 273.2, + 273.5, + 274.4, + 274.7, + 274.6, + 275.0, + 275.3, + 271.3, + 267.3, + 271.9, + 271.2, + 271.7, + 272.3, + 271.2, + 270.5, + 271.0, + 270.8, + ], + [ + 274.9, + 273.8, + 273.7, + 273.6, + 274.8, + 275.0, + 272.5, + 273.9, + 274.2, + 273.1, + 272.3, + 270.9, + 272.0, + 273.3, + 272.1, + 272.1, + 271.9, + 273.3, + 271.6, + 272.1, + 272.6, + 273.1, + 273.3, + 275.7, + 275.6, + 274.0, + 275.1, + 274.8, + 274.0, + 275.1, + 275.1, + 275.7, + 274.1, + 276.1, + 275.1, + 275.3, + 276.5, + 275.4, + 275.2, + 274.4, + 273.3, + 274.3, + 274.7, + 274.3, + 274.9, + 273.7, + 274.4, + 274.1, + ], + [ + 275.7, + 275.4, + 275.7, + 275.7, + 275.1, + 276.0, + 275.5, + 276.2, + 276.8, + 276.4, + 274.5, + 274.0, + 275.1, + 274.5, + 273.7, + 274.0, + 274.7, + 274.8, + 275.1, + 275.3, + 275.5, + 277.2, + 277.0, + 276.6, + 277.7, + 277.5, + 276.2, + 277.7, + 275.8, + 277.9, + 277.5, + 276.7, + 277.8, + 276.8, + 277.9, + 277.7, + 278.0, + 278.0, + 279.4, + 278.9, + 278.5, + 278.0, + 278.3, + 277.3, + 276.7, + 276.7, + 277.3, + 276.1, + ], + [ + 278.9, + 277.7, + 279.3, + 277.1, + 278.2, + 277.6, + 278.7, + 277.7, + 277.1, + 278.5, + 279.2, + 279.3, + 280.0, + 279.7, + 278.1, + 278.9, + 278.1, + 279.8, + 279.1, + 279.4, + 279.6, + 280.6, + 282.1, + 280.3, + 279.7, + 280.1, + 281.1, + 281.5, + 281.2, + 280.9, + 281.0, + 281.9, + 281.7, + 281.3, + 279.7, + 281.1, + 280.6, + 279.9, + 281.6, + 283.4, + 281.9, + 281.2, + 279.1, + 278.9, + 279.7, + 279.8, + 280.1, + 277.7, + ], + [ + 283.2, + 283.7, + 283.3, + 281.8, + 280.9, + 282.2, + 281.4, + 282.6, + 282.6, + 283.4, + 284.8, + 284.4, + 283.7, + 283.1, + 283.2, + 283.2, + 283.5, + 283.5, + 284.9, + 284.3, + 284.3, + 285.9, + 285.9, + 286.5, + 287.0, + 286.6, + 285.7, + 285.0, + 286.4, + 285.3, + 286.5, + 284.7, + 285.6, + 284.4, + 284.4, + 284.4, + 283.2, + 282.3, + 282.4, + 287.8, + 285.9, + 285.2, + 284.4, + 282.4, + 283.1, + 284.1, + 284.7, + 283.6, + ], + [ + 289.1, + 288.0, + 288.6, + 289.2, + 288.4, + 290.0, + 289.4, + 290.2, + 289.0, + 290.2, + 288.5, + 288.4, + 288.8, + 288.6, + 288.0, + 288.2, + 287.6, + 288.3, + 289.4, + 290.0, + 292.5, + 290.1, + 290.6, + 291.5, + 290.9, + 291.0, + 290.4, + 289.9, + 290.0, + 290.9, + 289.8, + 289.7, + 289.9, + 289.9, + 289.0, + 288.4, + 286.7, + 286.6, + 287.3, + 294.3, + 294.1, + 291.8, + 289.5, + 288.8, + 289.5, + 290.4, + 290.2, + 289.5, + ], + [ + 292.9, + 292.7, + 294.2, + 292.1, + 295.9, + 295.1, + 294.9, + 295.3, + 294.4, + 293.0, + 294.0, + 292.5, + 291.2, + 291.9, + 291.3, + 293.6, + 291.4, + 292.9, + 298.4, + 294.4, + 294.8, + 296.3, + 294.1, + 295.1, + 294.3, + 293.7, + 293.0, + 295.5, + 294.5, + 295.8, + 294.5, + 294.1, + 295.2, + 294.6, + 292.5, + 291.9, + 290.4, + 290.1, + 293.1, + 301.1, + 300.5, + 296.7, + 294.3, + 295.1, + 294.9, + 295.8, + 293.6, + 292.4, + ], + [ + 292.9, + 293.7, + 294.7, + 292.5, + 296.6, + 299.8, + 297.2, + 297.8, + 297.0, + 294.5, + 293.6, + 294.1, + 295.3, + 294.8, + 296.5, + 299.1, + 297.0, + 298.8, + 301.1, + 298.9, + 297.6, + 298.1, + 298.1, + 296.9, + 296.0, + 297.2, + 296.4, + 297.4, + 298.0, + 297.6, + 296.7, + 296.0, + 297.0, + 296.5, + 293.8, + 294.4, + 293.0, + 290.0, + 291.8, + 297.6, + 300.3, + 296.8, + 297.4, + 296.3, + 295.4, + 295.0, + 294.4, + 294.4, + ], + [ + 294.2, + 296.5, + 295.0, + 294.8, + 298.9, + 301.2, + 298.0, + 297.8, + 297.7, + 297.2, + 296.2, + 296.7, + 295.8, + 297.6, + 298.9, + 301.0, + 301.3, + 299.2, + 299.8, + 298.9, + 299.2, + 298.7, + 299.5, + 299.7, + 299.2, + 299.6, + 298.3, + 298.5, + 299.4, + 298.3, + 297.4, + 297.2, + 294.1, + 294.2, + 293.2, + 293.0, + 291.2, + 293.2, + 289.3, + 293.7, + 300.1, + 296.6, + 297.9, + 298.4, + 296.4, + 295.5, + 293.9, + 293.7, + ], + [ + 297.7, + 297.4, + 294.5, + 294.2, + 295.9, + 300.2, + 300.0, + 299.2, + 298.5, + 298.0, + 298.8, + 300.5, + 300.2, + 300.3, + 300.8, + 302.5, + 301.6, + 300.5, + 300.4, + 301.9, + 302.1, + 300.4, + 300.7, + 301.3, + 300.3, + 301.6, + 300.2, + 301.3, + 300.6, + 298.6, + 298.8, + 297.1, + 295.0, + 293.9, + 292.3, + 293.1, + 294.7, + 293.0, + 289.4, + 296.3, + 298.8, + 296.1, + 295.2, + 297.8, + 297.1, + 296.7, + 296.4, + 296.2, + ], + [ + 300.9, + 300.6, + 295.8, + 294.8, + 294.3, + 298.8, + 300.8, + 299.5, + 299.8, + 300.7, + 299.8, + 301.8, + 300.1, + 302.0, + 301.2, + 301.7, + 301.6, + 303.0, + 301.8, + 301.0, + 302.5, + 301.1, + 301.4, + 300.5, + 302.0, + 300.5, + 300.9, + 300.5, + 300.0, + 300.5, + 298.4, + 297.2, + 295.7, + 296.2, + 294.8, + 295.3, + 298.1, + 295.8, + 298.0, + 299.8, + 298.9, + 298.9, + 296.7, + 298.8, + 298.5, + 299.0, + 298.4, + 299.5, + ], + [ + 300.4, + 298.9, + 298.5, + 296.9, + 294.5, + 299.1, + 300.3, + 300.9, + 299.8, + 299.7, + 301.0, + 301.5, + 301.9, + 300.9, + 302.0, + 301.2, + 302.0, + 302.0, + 300.1, + 300.1, + 300.2, + 299.8, + 301.2, + 300.2, + 299.4, + 299.9, + 299.0, + 298.7, + 297.6, + 298.2, + 298.4, + 298.0, + 297.5, + 296.6, + 297.5, + 297.6, + 300.0, + 297.6, + 301.7, + 299.9, + 300.8, + 300.3, + 300.6, + 300.6, + 299.8, + 300.4, + 300.1, + 301.7, + ], + [ + 300.8, + 297.9, + 298.4, + 296.2, + 296.4, + 298.5, + 299.0, + 299.9, + 299.2, + 300.2, + 300.1, + 301.3, + 301.3, + 299.8, + 300.0, + 299.7, + 301.7, + 301.9, + 301.5, + 302.0, + 299.9, + 300.8, + 300.8, + 299.0, + 300.0, + 300.0, + 299.1, + 299.4, + 298.5, + 298.9, + 297.4, + 298.9, + 298.3, + 298.2, + 298.7, + 298.7, + 299.5, + 297.3, + 298.7, + 299.8, + 300.7, + 299.8, + 300.5, + 300.8, + 300.7, + 301.3, + 301.1, + 300.3, + ], + [ + 296.6, + 295.8, + 297.6, + 298.2, + 298.2, + 290.9, + 294.3, + 297.5, + 297.9, + 298.7, + 297.3, + 300.1, + 300.3, + 298.3, + 299.8, + 301.3, + 299.0, + 300.6, + 301.3, + 300.6, + 301.1, + 299.9, + 299.2, + 300.3, + 299.1, + 298.7, + 299.0, + 299.7, + 299.8, + 298.2, + 299.5, + 300.3, + 299.6, + 299.6, + 299.9, + 298.9, + 300.0, + 297.6, + 297.8, + 299.6, + 299.2, + 298.7, + 298.9, + 298.2, + 299.4, + 300.4, + 298.8, + 297.0, + ], + [ + 296.9, + 296.6, + 296.4, + 295.2, + 297.7, + 292.7, + 289.9, + 296.6, + 295.7, + 297.8, + 294.4, + 298.8, + 297.4, + 294.0, + 296.1, + 298.2, + 297.8, + 299.4, + 298.7, + 298.4, + 299.4, + 298.7, + 296.9, + 296.5, + 295.9, + 295.3, + 296.4, + 296.5, + 296.9, + 295.6, + 297.0, + 296.2, + 296.8, + 297.9, + 295.1, + 294.6, + 295.8, + 298.3, + 298.7, + 297.9, + 297.6, + 297.2, + 297.4, + 297.9, + 296.4, + 296.4, + 298.9, + 296.7, + ], + [ + 291.3, + 290.2, + 288.0, + 289.4, + 292.3, + 290.7, + 288.5, + 290.8, + 294.6, + 292.4, + 289.6, + 291.6, + 289.7, + 284.3, + 288.0, + 294.4, + 293.9, + 294.1, + 293.4, + 295.5, + 295.5, + 296.2, + 295.2, + 294.9, + 293.9, + 294.2, + 292.7, + 292.6, + 293.6, + 291.7, + 292.2, + 293.6, + 292.9, + 294.3, + 285.4, + 293.1, + 295.9, + 295.9, + 296.1, + 297.0, + 297.1, + 297.1, + 295.0, + 296.2, + 296.1, + 294.4, + 292.3, + 292.1, + ], + [ + 287.1, + 286.7, + 286.8, + 287.3, + 288.0, + 285.4, + 288.1, + 284.9, + 285.0, + 283.9, + 283.1, + 276.6, + 274.9, + 275.4, + 276.8, + 278.7, + 285.8, + 289.5, + 288.4, + 289.9, + 290.2, + 292.1, + 291.7, + 293.1, + 291.6, + 290.8, + 291.0, + 290.7, + 289.5, + 290.8, + 290.5, + 289.8, + 289.7, + 284.3, + 280.6, + 285.4, + 287.7, + 290.5, + 292.7, + 293.9, + 294.3, + 294.3, + 293.7, + 293.2, + 294.4, + 292.7, + 289.9, + 287.8, + ], + [ + 280.2, + 285.6, + 288.6, + 289.1, + 287.3, + 280.4, + 277.4, + 276.9, + 274.4, + 273.7, + 259.0, + 252.1, + 252.8, + 264.0, + 272.6, + 274.0, + 278.0, + 279.8, + 278.2, + 283.8, + 286.2, + 286.4, + 287.5, + 288.6, + 287.6, + 286.8, + 287.2, + 287.6, + 288.1, + 287.8, + 288.5, + 286.2, + 280.6, + 273.6, + 274.5, + 274.8, + 273.3, + 280.8, + 288.7, + 290.5, + 289.6, + 291.6, + 291.4, + 291.5, + 292.3, + 291.7, + 290.0, + 281.8, + ], + [ + 283.8, + 283.9, + 282.7, + 279.1, + 274.5, + 270.1, + 276.5, + 276.1, + 273.0, + 264.4, + 261.2, + 262.8, + 258.6, + 260.2, + 262.5, + 264.3, + 257.8, + 262.8, + 270.9, + 273.5, + 279.0, + 280.7, + 280.7, + 282.3, + 282.4, + 282.9, + 281.4, + 284.0, + 282.9, + 284.8, + 284.4, + 280.3, + 267.5, + 264.5, + 267.8, + 265.6, + 266.5, + 269.4, + 279.7, + 284.7, + 285.4, + 286.4, + 286.7, + 288.1, + 289.2, + 288.4, + 284.5, + 278.6, + ], + [ + 274.9, + 269.1, + 266.5, + 266.7, + 275.0, + 272.5, + 272.6, + 267.4, + 265.5, + 263.8, + 259.4, + 256.3, + 253.8, + 253.2, + 255.1, + 251.7, + 247.0, + 248.7, + 261.2, + 268.9, + 272.9, + 274.4, + 275.9, + 275.2, + 276.7, + 277.9, + 278.2, + 279.5, + 280.6, + 280.5, + 281.7, + 276.6, + 265.7, + 261.9, + 261.0, + 260.2, + 265.8, + 262.9, + 264.8, + 274.3, + 276.4, + 278.3, + 280.9, + 283.7, + 286.2, + 285.0, + 284.4, + 278.7, + ], + [ + 272.0, + 266.1, + 263.0, + 260.3, + 258.5, + 258.8, + 258.1, + 257.8, + 256.0, + 256.2, + 257.7, + 255.5, + 252.9, + 251.7, + 250.8, + 243.5, + 241.2, + 243.1, + 250.8, + 268.8, + 266.8, + 267.2, + 271.3, + 272.6, + 273.9, + 273.7, + 275.7, + 275.9, + 277.4, + 280.2, + 279.5, + 268.3, + 262.4, + 258.0, + 255.4, + 255.9, + 257.5, + 259.5, + 259.1, + 259.6, + 269.1, + 275.4, + 278.9, + 279.8, + 282.9, + 283.3, + 282.8, + 275.7, + ], + [ + 277.0, + 267.4, + 267.9, + 255.7, + 248.9, + 247.9, + 248.2, + 249.4, + 249.1, + 249.9, + 248.7, + 249.5, + 250.6, + 247.9, + 244.2, + 238.8, + 236.1, + 236.8, + 244.3, + 257.5, + 255.3, + 248.7, + 262.3, + 265.0, + 268.4, + 271.1, + 270.5, + 272.9, + 274.2, + 273.9, + 266.9, + 261.4, + 260.6, + 257.2, + 252.1, + 249.6, + 251.7, + 256.5, + 251.5, + 254.6, + 269.6, + 274.4, + 279.0, + 279.3, + 281.8, + 281.5, + 282.5, + 274.8, + ], + [ + 274.1, + 262.4, + 258.9, + 246.6, + 242.9, + 240.3, + 240.1, + 240.9, + 239.4, + 239.1, + 239.1, + 239.6, + 237.9, + 236.6, + 233.5, + 230.9, + 228.4, + 231.3, + 235.3, + 238.9, + 238.5, + 242.5, + 244.2, + 247.1, + 257.4, + 264.3, + 255.1, + 257.1, + 256.5, + 255.2, + 256.2, + 253.6, + 251.6, + 248.7, + 245.2, + 246.0, + 247.4, + 248.1, + 249.6, + 257.7, + 269.3, + 259.9, + 265.2, + 274.8, + 277.1, + 273.9, + 277.3, + 279.2, + ], + [ + 272.7, + 268.2, + 256.6, + 248.0, + 249.9, + 245.1, + 240.2, + 234.0, + 234.0, + 231.0, + 229.3, + 231.7, + 231.6, + 229.1, + 228.7, + 228.7, + 228.6, + 230.4, + 231.1, + 231.5, + 232.6, + 236.6, + 238.2, + 240.7, + 241.6, + 245.2, + 247.1, + 244.1, + 246.0, + 248.0, + 245.9, + 242.9, + 242.4, + 241.6, + 242.4, + 241.2, + 242.2, + 241.6, + 245.7, + 254.9, + 263.2, + 250.5, + 243.6, + 248.7, + 254.6, + 261.3, + 267.8, + 271.1, + ], + [ + 267.5, + 269.3, + 267.7, + 265.7, + 262.4, + 242.9, + 236.2, + 235.2, + 235.7, + 235.3, + 233.8, + 228.7, + 229.6, + 227.4, + 229.6, + 232.4, + 233.1, + 236.1, + 236.5, + 237.0, + 236.0, + 238.1, + 238.4, + 239.0, + 239.5, + 239.2, + 242.1, + 238.3, + 238.9, + 239.6, + 241.2, + 240.3, + 239.0, + 240.0, + 242.4, + 239.9, + 238.9, + 245.3, + 251.6, + 259.2, + 247.7, + 238.6, + 236.8, + 236.2, + 240.5, + 246.3, + 248.2, + 259.4, + ], + [ + 258.7, + 258.8, + 258.6, + 254.5, + 243.2, + 240.2, + 239.2, + 238.7, + 235.0, + 233.4, + 233.7, + 233.9, + 235.1, + 233.6, + 234.7, + 236.3, + 235.6, + 237.5, + 237.8, + 236.8, + 237.7, + 236.3, + 235.8, + 237.0, + 235.2, + 234.9, + 235.7, + 235.3, + 235.5, + 235.0, + 235.9, + 236.9, + 236.8, + 237.2, + 238.5, + 238.8, + 236.7, + 232.0, + 235.2, + 237.8, + 237.1, + 236.2, + 233.4, + 235.0, + 239.1, + 240.4, + 242.8, + 253.1, + ], + [ + 235.7, + 235.5, + 235.4, + 235.1, + 234.4, + 236.7, + 235.6, + 234.8, + 236.0, + 236.9, + 235.7, + 235.4, + 237.2, + 237.8, + 237.3, + 237.0, + 237.5, + 239.1, + 239.4, + 238.7, + 237.5, + 238.9, + 238.8, + 237.6, + 235.8, + 236.8, + 236.4, + 235.0, + 234.6, + 233.8, + 233.6, + 233.5, + 233.2, + 233.9, + 233.4, + 233.1, + 234.8, + 234.0, + 233.3, + 234.8, + 235.3, + 234.6, + 233.7, + 235.7, + 238.6, + 239.0, + 237.2, + 236.8, + ], + ], + ] + + dst = n.createVariable("dst", "f4", ("time_1", "lat", "lon")) + dst.standard_name = "air_temperature" + dst.units = "K" + dst.cell_methods = "time_1: mean" + + # Don't generate this data randomly - it's useful to see the real + # patterns of global temperature. + dst[...] = [ + [ + [ + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + ], + [ + 243.6, + 243.3, + 243.0, + 242.7, + 242.5, + 242.5, + 242.5, + 242.4, + 242.5, + 242.5, + 242.4, + 242.2, + 241.9, + 241.7, + 241.5, + 241.4, + 241.4, + 241.4, + 241.4, + 241.4, + 241.5, + 241.7, + 241.8, + 241.9, + 242.1, + 242.1, + 241.9, + 241.9, + 241.8, + 242.0, + 242.2, + 242.5, + 243.2, + 243.7, + 244.1, + 244.5, + 244.8, + 244.8, + 244.7, + 244.8, + 244.9, + 245.1, + 245.7, + 246.3, + 247.6, + 248.1, + 248.3, + 247.9, + 247.2, + 246.8, + 246.9, + 247.3, + 247.9, + 248.2, + 248.5, + 249.1, + 249.6, + 249.8, + 250.1, + 250.5, + 250.8, + 250.7, + 250.8, + 250.3, + 250.3, + 250.2, + 249.8, + 249.6, + 249.8, + 250.3, + 250.7, + 251.2, + 252.0, + 252.7, + 252.5, + 251.5, + 250.5, + 249.8, + 248.6, + 248.0, + 247.9, + 248.2, + 248.3, + 248.3, + 248.2, + 247.8, + 247.5, + 247.0, + 246.5, + 246.4, + 246.0, + 245.4, + 244.9, + 244.4, + 244.0, + 243.8, + ], + [ + 244.1, + 243.6, + 242.8, + 241.9, + 241.3, + 241.0, + 240.8, + 240.8, + 240.5, + 240.2, + 239.8, + 239.5, + 239.4, + 239.5, + 239.5, + 239.3, + 239.1, + 239.0, + 239.1, + 239.5, + 240.1, + 240.7, + 241.2, + 241.7, + 242.0, + 242.5, + 243.1, + 243.5, + 243.6, + 243.9, + 244.3, + 244.8, + 245.3, + 246.0, + 246.8, + 247.5, + 248.0, + 248.4, + 248.7, + 248.9, + 249.3, + 250.2, + 251.5, + 252.8, + 253.6, + 254.2, + 254.3, + 255.1, + 256.9, + 258.4, + 259.8, + 261.2, + 262.7, + 264.0, + 264.8, + 265.3, + 265.1, + 264.4, + 263.6, + 262.6, + 261.6, + 261.1, + 260.4, + 259.4, + 258.2, + 257.2, + 255.3, + 253.5, + 252.7, + 252.5, + 253.0, + 253.5, + 254.1, + 255.2, + 257.1, + 258.0, + 258.3, + 258.3, + 258.7, + 258.5, + 257.9, + 257.0, + 256.0, + 255.5, + 255.0, + 254.2, + 252.9, + 251.7, + 250.8, + 249.6, + 248.3, + 246.9, + 245.7, + 245.0, + 244.5, + 244.3, + ], + [ + 244.9, + 243.6, + 242.0, + 240.5, + 239.4, + 238.6, + 238.1, + 237.5, + 237.2, + 237.1, + 236.9, + 236.8, + 237.1, + 237.6, + 237.9, + 237.9, + 237.9, + 237.8, + 237.6, + 237.9, + 238.6, + 239.3, + 239.9, + 240.2, + 241.1, + 242.4, + 243.5, + 244.0, + 244.7, + 245.4, + 245.9, + 246.1, + 246.5, + 247.1, + 247.9, + 248.8, + 249.8, + 250.5, + 250.9, + 251.4, + 252.2, + 253.6, + 256.0, + 259.4, + 262.9, + 265.9, + 267.3, + 267.7, + 267.6, + 267.4, + 267.0, + 266.6, + 266.5, + 266.6, + 266.5, + 265.9, + 265.3, + 265.1, + 265.0, + 264.8, + 264.8, + 264.8, + 264.7, + 264.5, + 263.5, + 262.0, + 259.9, + 257.4, + 255.7, + 255.3, + 255.3, + 255.7, + 256.5, + 257.5, + 258.9, + 260.6, + 261.8, + 262.8, + 263.3, + 263.8, + 264.2, + 264.0, + 263.1, + 262.2, + 262.4, + 262.4, + 261.2, + 259.3, + 257.5, + 255.4, + 253.0, + 250.5, + 248.2, + 246.6, + 245.9, + 245.6, + ], + [ + 244.3, + 242.9, + 241.7, + 241.1, + 240.7, + 240.2, + 239.1, + 238.1, + 237.5, + 237.1, + 236.8, + 237.0, + 237.9, + 239.2, + 240.3, + 241.1, + 241.9, + 242.5, + 242.5, + 241.8, + 241.4, + 241.2, + 240.8, + 240.2, + 239.5, + 239.5, + 240.4, + 241.8, + 243.1, + 244.2, + 245.0, + 245.5, + 245.8, + 246.5, + 247.3, + 248.4, + 249.5, + 250.3, + 250.7, + 251.4, + 253.3, + 255.7, + 259.1, + 263.3, + 265.9, + 266.1, + 266.4, + 266.0, + 265.7, + 265.5, + 265.5, + 265.6, + 265.8, + 266.2, + 266.3, + 265.9, + 265.5, + 265.3, + 264.6, + 264.1, + 263.4, + 262.4, + 261.5, + 261.2, + 261.0, + 260.8, + 260.0, + 258.8, + 257.4, + 256.8, + 256.8, + 257.4, + 258.7, + 260.1, + 261.9, + 263.6, + 265.0, + 265.2, + 264.8, + 264.8, + 265.5, + 265.2, + 264.3, + 262.9, + 263.7, + 265.2, + 266.0, + 265.8, + 263.1, + 260.8, + 256.8, + 252.4, + 249.0, + 247.0, + 245.9, + 245.1, + ], + [ + 245.2, + 243.7, + 242.2, + 241.3, + 241.5, + 241.7, + 241.3, + 240.1, + 238.9, + 238.5, + 238.6, + 239.0, + 239.9, + 241.3, + 242.4, + 243.3, + 245.4, + 246.5, + 246.4, + 246.4, + 246.4, + 245.9, + 245.2, + 243.8, + 242.2, + 241.2, + 241.2, + 241.7, + 242.5, + 243.3, + 243.8, + 244.2, + 244.4, + 244.5, + 245.4, + 246.5, + 247.4, + 247.6, + 247.6, + 248.0, + 248.9, + 250.8, + 254.1, + 259.1, + 262.6, + 265.6, + 265.6, + 265.2, + 264.8, + 264.8, + 265.4, + 266.1, + 266.6, + 267.2, + 267.2, + 267.2, + 266.8, + 266.7, + 265.6, + 263.3, + 261.6, + 259.5, + 258.2, + 258.3, + 259.4, + 260.8, + 261.7, + 261.7, + 261.5, + 260.9, + 260.2, + 260.3, + 261.3, + 262.4, + 263.9, + 266.1, + 267.0, + 267.7, + 267.5, + 267.2, + 271.5, + 271.5, + 271.6, + 271.7, + 271.6, + 271.5, + 271.6, + 271.4, + 263.7, + 260.6, + 256.3, + 252.6, + 249.8, + 248.1, + 247.1, + 246.2, + ], + [ + 248.4, + 246.8, + 245.6, + 244.3, + 244.1, + 244.4, + 244.0, + 242.7, + 241.0, + 240.4, + 240.9, + 242.0, + 243.3, + 243.7, + 243.4, + 243.9, + 245.3, + 248.8, + 250.6, + 250.9, + 250.5, + 248.5, + 246.7, + 245.5, + 244.2, + 243.3, + 243.6, + 244.7, + 245.7, + 245.8, + 245.3, + 244.4, + 243.7, + 243.7, + 244.8, + 246.1, + 246.7, + 246.4, + 246.4, + 247.1, + 249.2, + 251.9, + 255.3, + 259.6, + 261.3, + 270.2, + 270.4, + 271.0, + 271.1, + 271.2, + 271.4, + 271.5, + 271.5, + 271.5, + 271.5, + 271.4, + 271.3, + 271.7, + 272.0, + 272.1, + 267.4, + 266.9, + 266.4, + 266.3, + 266.4, + 266.6, + 267.3, + 268.2, + 268.5, + 268.2, + 266.9, + 265.2, + 264.3, + 264.3, + 265.8, + 267.1, + 267.0, + 266.0, + 265.7, + 266.6, + 271.6, + 271.6, + 271.5, + 271.5, + 271.4, + 271.5, + 271.6, + 271.5, + 271.3, + 271.4, + 262.7, + 259.6, + 254.4, + 252.3, + 251.2, + 249.6, + ], + [ + 257.5, + 254.5, + 252.1, + 249.8, + 249.2, + 250.4, + 251.5, + 250.9, + 250.6, + 250.5, + 249.1, + 248.0, + 247.6, + 247.1, + 246.9, + 247.4, + 249.1, + 253.6, + 256.8, + 257.8, + 255.1, + 251.7, + 247.6, + 245.1, + 244.0, + 243.2, + 243.2, + 244.8, + 246.9, + 247.9, + 247.8, + 247.1, + 246.2, + 245.6, + 245.4, + 245.6, + 246.3, + 246.7, + 248.1, + 250.9, + 253.0, + 253.8, + 254.5, + 254.6, + 256.0, + 270.2, + 270.8, + 271.3, + 271.2, + 271.3, + 271.4, + 271.4, + 271.3, + 271.8, + 272.6, + 272.6, + 271.1, + 271.1, + 271.3, + 271.6, + 271.7, + 271.7, + 271.6, + 271.5, + 271.5, + 271.5, + 271.4, + 271.4, + 271.2, + 271.6, + 271.7, + 271.6, + 271.5, + 272.3, + 272.1, + 271.5, + 271.8, + 268.5, + 265.9, + 264.9, + 271.5, + 271.3, + 271.3, + 271.4, + 271.5, + 271.5, + 271.5, + 271.5, + 271.3, + 271.2, + 271.1, + 271.2, + 271.3, + 263.1, + 261.2, + 259.6, + ], + [ + 271.3, + 271.4, + 271.5, + 271.6, + 271.6, + 271.5, + 271.3, + 271.3, + 264.0, + 264.2, + 263.1, + 259.6, + 256.1, + 254.7, + 252.4, + 250.5, + 251.9, + 254.3, + 259.4, + 262.6, + 263.1, + 258.8, + 255.1, + 253.2, + 252.1, + 251.8, + 251.8, + 252.1, + 252.9, + 253.4, + 253.9, + 254.0, + 253.7, + 252.8, + 251.7, + 250.5, + 249.8, + 250.2, + 252.7, + 254.8, + 256.8, + 258.2, + 259.7, + 270.3, + 270.2, + 270.5, + 271.2, + 271.1, + 271.3, + 271.5, + 271.6, + 271.6, + 272.2, + 273.2, + 274.1, + 274.1, + 273.8, + 273.9, + 274.0, + 273.9, + 273.4, + 272.8, + 272.5, + 272.7, + 273.6, + 274.0, + 273.8, + 273.6, + 273.7, + 273.8, + 273.2, + 272.8, + 273.5, + 274.4, + 275.4, + 275.3, + 272.6, + 268.4, + 265.9, + 265.2, + 271.4, + 271.3, + 271.0, + 271.2, + 271.5, + 271.5, + 271.3, + 271.2, + 271.2, + 271.2, + 271.2, + 271.3, + 271.4, + 271.4, + 271.3, + 271.3, + ], + [ + 272.8, + 273.1, + 273.0, + 272.9, + 272.8, + 272.5, + 271.3, + 271.2, + 271.1, + 271.2, + 271.1, + 271.2, + 271.0, + 263.1, + 260.1, + 260.2, + 270.2, + 269.5, + 269.5, + 269.8, + 270.1, + 269.9, + 270.9, + 260.1, + 259.0, + 257.8, + 256.3, + 255.8, + 256.3, + 258.0, + 259.9, + 261.0, + 260.8, + 259.9, + 258.9, + 257.2, + 255.9, + 257.6, + 261.3, + 270.5, + 270.7, + 270.4, + 270.6, + 271.1, + 271.4, + 271.2, + 271.3, + 272.4, + 272.8, + 273.2, + 273.4, + 273.4, + 273.6, + 274.1, + 274.2, + 274.2, + 274.2, + 274.2, + 274.0, + 273.9, + 273.9, + 273.9, + 274.0, + 274.1, + 274.4, + 274.6, + 274.5, + 274.4, + 274.3, + 274.4, + 274.6, + 274.5, + 274.5, + 274.8, + 275.1, + 275.2, + 275.1, + 274.6, + 268.9, + 267.9, + 271.2, + 271.1, + 271.2, + 272.2, + 271.4, + 271.1, + 271.0, + 271.0, + 271.6, + 272.2, + 271.4, + 271.7, + 271.3, + 271.7, + 271.6, + 272.3, + ], + [ + 274.2, + 274.2, + 274.1, + 274.3, + 274.3, + 274.3, + 274.3, + 274.5, + 274.1, + 274.5, + 274.7, + 274.5, + 271.5, + 271.2, + 271.1, + 270.9, + 270.8, + 270.7, + 270.8, + 270.9, + 271.0, + 271.1, + 271.0, + 270.8, + 270.8, + 270.8, + 270.9, + 270.9, + 271.0, + 271.1, + 271.0, + 271.0, + 271.1, + 271.1, + 271.1, + 271.1, + 271.0, + 271.0, + 270.9, + 270.9, + 271.1, + 271.1, + 271.2, + 271.3, + 271.4, + 272.1, + 273.3, + 273.9, + 274.1, + 274.3, + 274.6, + 274.5, + 274.5, + 274.5, + 274.4, + 274.3, + 274.2, + 274.2, + 274.1, + 274.0, + 273.9, + 274.1, + 274.3, + 274.4, + 274.4, + 274.6, + 274.6, + 274.7, + 274.8, + 274.9, + 275.1, + 275.4, + 275.4, + 275.4, + 275.6, + 275.6, + 275.4, + 275.2, + 275.1, + 269.6, + 271.3, + 271.3, + 272.3, + 272.8, + 272.9, + 273.0, + 273.2, + 273.4, + 273.5, + 273.8, + 273.8, + 274.0, + 274.0, + 274.1, + 274.0, + 274.0, + ], + [ + 274.7, + 274.6, + 274.4, + 274.4, + 274.5, + 274.4, + 274.3, + 274.4, + 274.4, + 274.5, + 274.8, + 275.0, + 274.8, + 274.4, + 274.0, + 273.8, + 274.0, + 274.5, + 274.5, + 274.4, + 274.3, + 274.3, + 272.9, + 271.3, + 271.2, + 271.3, + 273.0, + 272.9, + 273.0, + 273.6, + 273.6, + 273.4, + 272.9, + 272.7, + 272.9, + 272.8, + 271.8, + 271.6, + 271.6, + 272.3, + 274.1, + 274.6, + 274.6, + 274.5, + 274.8, + 274.7, + 274.9, + 275.2, + 275.1, + 275.0, + 275.0, + 275.0, + 275.1, + 275.2, + 275.2, + 275.2, + 275.2, + 275.0, + 274.9, + 274.8, + 274.9, + 274.9, + 274.9, + 275.0, + 275.0, + 275.1, + 275.1, + 275.2, + 275.4, + 275.5, + 275.8, + 276.0, + 275.9, + 276.1, + 276.4, + 276.5, + 276.4, + 276.1, + 275.5, + 273.0, + 271.6, + 272.0, + 273.1, + 273.8, + 274.5, + 274.8, + 274.9, + 274.9, + 274.3, + 274.8, + 274.9, + 274.8, + 274.7, + 274.8, + 274.8, + 274.8, + ], + [ + 275.0, + 274.9, + 274.6, + 274.3, + 274.3, + 274.4, + 274.2, + 274.2, + 274.3, + 274.5, + 274.6, + 274.7, + 274.8, + 274.9, + 275.0, + 275.1, + 275.1, + 275.1, + 275.1, + 275.0, + 274.6, + 274.3, + 274.0, + 271.7, + 272.1, + 272.6, + 273.2, + 274.1, + 274.3, + 274.4, + 274.6, + 274.7, + 274.7, + 274.8, + 274.9, + 274.6, + 274.7, + 274.9, + 274.9, + 275.0, + 275.2, + 275.4, + 275.5, + 275.4, + 275.5, + 275.4, + 275.7, + 275.7, + 275.6, + 275.6, + 275.6, + 275.6, + 275.7, + 275.7, + 275.6, + 275.6, + 275.6, + 275.6, + 275.9, + 275.7, + 275.5, + 275.6, + 275.7, + 275.8, + 275.8, + 275.8, + 275.9, + 276.0, + 276.0, + 276.1, + 276.3, + 276.4, + 276.4, + 276.5, + 276.6, + 276.8, + 276.9, + 277.2, + 277.2, + 277.0, + 276.4, + 275.6, + 275.8, + 276.0, + 276.3, + 276.2, + 276.1, + 276.3, + 276.2, + 276.2, + 276.1, + 275.7, + 275.5, + 275.5, + 275.3, + 275.0, + ], + [ + 275.2, + 275.1, + 274.9, + 274.9, + 275.2, + 275.3, + 275.2, + 275.0, + 275.0, + 275.2, + 275.5, + 275.6, + 275.5, + 275.5, + 275.5, + 275.6, + 275.8, + 275.9, + 275.8, + 275.3, + 274.9, + 274.5, + 274.6, + 274.2, + 274.3, + 273.7, + 273.7, + 273.7, + 273.9, + 274.0, + 274.4, + 274.5, + 274.6, + 274.9, + 275.0, + 275.0, + 275.0, + 275.1, + 275.1, + 275.4, + 275.6, + 275.9, + 276.3, + 276.6, + 277.0, + 276.5, + 276.5, + 276.5, + 276.5, + 276.6, + 276.6, + 276.7, + 277.0, + 276.7, + 276.4, + 276.3, + 276.1, + 276.2, + 277.1, + 276.7, + 276.5, + 276.4, + 276.8, + 277.0, + 276.9, + 276.9, + 277.0, + 277.0, + 276.9, + 276.9, + 277.0, + 276.9, + 276.9, + 277.1, + 277.2, + 277.7, + 277.9, + 278.7, + 279.2, + 279.0, + 278.1, + 278.2, + 278.1, + 278.3, + 278.6, + 278.0, + 277.3, + 277.0, + 277.1, + 277.1, + 277.0, + 276.7, + 276.4, + 276.3, + 276.1, + 275.6, + ], + [ + 276.0, + 275.8, + 275.7, + 275.8, + 276.1, + 276.2, + 276.2, + 276.1, + 275.9, + 276.0, + 276.3, + 276.4, + 276.3, + 276.4, + 276.3, + 276.3, + 276.5, + 276.5, + 276.3, + 276.0, + 275.7, + 275.1, + 275.7, + 275.6, + 275.7, + 275.7, + 276.0, + 275.6, + 276.0, + 275.4, + 275.8, + 275.7, + 275.6, + 275.6, + 275.9, + 275.9, + 276.2, + 276.5, + 276.2, + 276.1, + 276.3, + 276.1, + 277.0, + 277.3, + 278.2, + 278.2, + 277.7, + 277.5, + 277.5, + 277.6, + 277.5, + 277.8, + 278.2, + 278.2, + 278.1, + 278.0, + 277.6, + 277.2, + 278.1, + 278.1, + 278.0, + 278.0, + 278.8, + 278.7, + 278.4, + 278.3, + 278.2, + 278.2, + 278.1, + 278.0, + 278.1, + 278.1, + 278.3, + 278.4, + 278.5, + 278.9, + 279.5, + 280.2, + 279.7, + 281.3, + 280.2, + 279.7, + 279.5, + 279.3, + 279.1, + 278.3, + 277.8, + 277.9, + 278.3, + 278.3, + 278.2, + 277.7, + 277.4, + 277.2, + 276.7, + 276.3, + ], + [ + 277.2, + 277.2, + 277.4, + 277.5, + 277.5, + 277.5, + 277.4, + 277.2, + 276.9, + 276.9, + 277.1, + 277.2, + 277.4, + 277.5, + 277.5, + 277.4, + 277.4, + 277.2, + 277.2, + 277.0, + 276.8, + 276.2, + 277.4, + 277.5, + 277.4, + 277.2, + 277.5, + 277.7, + 277.9, + 277.6, + 278.0, + 277.4, + 277.5, + 277.4, + 277.6, + 277.9, + 278.4, + 278.3, + 278.2, + 278.6, + 279.2, + 278.8, + 279.5, + 279.4, + 280.0, + 280.7, + 280.1, + 278.8, + 278.6, + 278.9, + 278.8, + 279.0, + 279.6, + 280.1, + 280.0, + 279.8, + 279.7, + 279.4, + 279.7, + 280.1, + 280.3, + 280.5, + 280.7, + 280.3, + 280.1, + 280.1, + 279.9, + 279.8, + 279.5, + 279.3, + 279.3, + 279.3, + 279.4, + 279.5, + 279.7, + 280.1, + 280.4, + 279.8, + 282.0, + 281.8, + 281.2, + 281.1, + 280.4, + 279.5, + 279.1, + 278.7, + 278.6, + 278.7, + 279.0, + 279.1, + 279.5, + 279.0, + 278.5, + 278.0, + 277.2, + 277.3, + ], + [ + 279.0, + 279.3, + 279.5, + 279.4, + 279.1, + 278.8, + 278.6, + 278.3, + 278.3, + 278.2, + 278.2, + 278.3, + 278.6, + 278.8, + 278.6, + 278.5, + 278.4, + 278.3, + 278.7, + 278.3, + 278.5, + 280.1, + 281.1, + 280.7, + 280.3, + 280.1, + 280.0, + 280.2, + 280.1, + 279.9, + 279.7, + 279.5, + 279.9, + 279.8, + 280.0, + 280.9, + 280.9, + 280.8, + 280.9, + 281.1, + 281.1, + 280.8, + 281.1, + 281.1, + 281.6, + 281.8, + 281.0, + 280.1, + 280.2, + 280.5, + 281.0, + 281.1, + 281.4, + 282.2, + 281.8, + 281.6, + 281.5, + 281.4, + 281.6, + 282.2, + 282.3, + 282.4, + 282.1, + 281.9, + 281.9, + 281.8, + 281.4, + 281.2, + 280.9, + 280.7, + 280.6, + 280.5, + 280.6, + 280.7, + 280.9, + 281.0, + 281.1, + 279.2, + 284.1, + 283.7, + 282.6, + 281.6, + 280.6, + 280.3, + 280.1, + 279.7, + 279.5, + 279.5, + 279.9, + 279.9, + 280.4, + 280.3, + 280.0, + 279.7, + 279.0, + 279.3, + ], + [ + 281.7, + 281.7, + 281.4, + 281.2, + 280.9, + 280.5, + 280.2, + 279.8, + 280.0, + 280.0, + 279.8, + 280.0, + 280.2, + 280.3, + 280.0, + 279.9, + 279.9, + 279.8, + 280.4, + 280.0, + 281.9, + 282.4, + 282.6, + 282.2, + 282.3, + 282.2, + 281.9, + 281.9, + 281.7, + 281.5, + 281.4, + 281.6, + 282.0, + 282.0, + 282.3, + 282.7, + 282.4, + 282.5, + 282.5, + 282.5, + 282.7, + 282.6, + 282.7, + 282.8, + 283.2, + 283.1, + 282.7, + 282.0, + 282.5, + 283.3, + 283.9, + 283.5, + 283.7, + 284.1, + 283.9, + 283.6, + 283.4, + 283.4, + 283.6, + 283.9, + 283.9, + 283.8, + 283.6, + 283.6, + 283.6, + 283.4, + 283.1, + 282.8, + 282.6, + 282.3, + 282.2, + 281.9, + 281.8, + 281.9, + 281.9, + 281.9, + 281.9, + 277.8, + 287.9, + 285.9, + 283.8, + 282.2, + 282.2, + 282.4, + 282.3, + 281.9, + 281.1, + 281.0, + 281.1, + 281.0, + 281.5, + 282.3, + 282.3, + 282.3, + 282.0, + 282.0, + ], + [ + 284.2, + 283.9, + 283.5, + 283.1, + 282.7, + 282.4, + 282.0, + 281.6, + 281.8, + 281.5, + 281.5, + 281.9, + 281.9, + 282.1, + 281.9, + 281.9, + 282.0, + 281.8, + 282.2, + 282.2, + 284.0, + 284.2, + 284.2, + 284.1, + 284.2, + 283.9, + 283.7, + 283.6, + 283.6, + 283.6, + 283.6, + 283.8, + 283.9, + 283.9, + 284.2, + 284.1, + 284.1, + 284.2, + 284.4, + 285.2, + 285.7, + 285.1, + 284.9, + 284.9, + 284.9, + 283.6, + 287.7, + 288.0, + 286.9, + 288.5, + 287.4, + 286.6, + 287.0, + 286.3, + 286.2, + 285.8, + 285.7, + 285.7, + 285.7, + 285.8, + 285.7, + 285.6, + 285.3, + 285.2, + 285.0, + 284.9, + 284.7, + 284.4, + 284.3, + 283.9, + 283.6, + 283.4, + 283.0, + 283.0, + 282.8, + 283.1, + 283.2, + 280.2, + 289.8, + 289.2, + 285.5, + 284.6, + 284.9, + 285.1, + 284.9, + 283.8, + 283.0, + 283.1, + 283.0, + 282.7, + 283.5, + 284.8, + 284.8, + 284.9, + 285.1, + 284.7, + ], + [ + 286.4, + 286.1, + 285.7, + 285.2, + 284.9, + 284.5, + 284.0, + 283.6, + 283.8, + 283.3, + 283.2, + 284.3, + 284.8, + 285.0, + 285.3, + 285.1, + 284.9, + 284.8, + 285.3, + 285.4, + 286.6, + 286.3, + 286.2, + 286.2, + 286.2, + 286.0, + 285.9, + 285.7, + 285.7, + 285.7, + 285.5, + 285.5, + 285.6, + 285.8, + 285.8, + 285.9, + 286.2, + 286.3, + 286.4, + 287.2, + 287.9, + 287.7, + 287.4, + 287.2, + 287.5, + 288.2, + 286.9, + 291.5, + 291.3, + 290.5, + 289.1, + 288.8, + 288.6, + 287.9, + 287.7, + 287.4, + 287.5, + 287.6, + 287.6, + 287.6, + 287.3, + 287.1, + 286.9, + 286.9, + 286.8, + 286.6, + 286.3, + 286.1, + 286.0, + 285.7, + 285.4, + 285.1, + 284.6, + 284.2, + 284.0, + 284.6, + 284.8, + 281.5, + 289.1, + 290.4, + 287.6, + 289.3, + 289.9, + 287.9, + 286.6, + 286.2, + 286.0, + 285.5, + 285.5, + 286.1, + 287.1, + 287.8, + 287.2, + 287.5, + 287.3, + 287.0, + ], + [ + 288.7, + 288.5, + 288.1, + 287.6, + 287.1, + 286.8, + 287.2, + 286.7, + 286.5, + 285.8, + 286.0, + 287.4, + 287.8, + 288.3, + 288.7, + 288.2, + 287.8, + 288.0, + 288.5, + 288.7, + 289.1, + 288.6, + 288.5, + 288.2, + 288.0, + 287.9, + 287.6, + 287.4, + 287.4, + 287.4, + 287.3, + 287.3, + 287.5, + 287.6, + 287.7, + 287.8, + 287.9, + 288.0, + 288.1, + 289.0, + 290.0, + 291.0, + 290.2, + 289.5, + 289.6, + 290.0, + 291.1, + 290.8, + 292.5, + 291.4, + 290.5, + 290.1, + 289.7, + 289.4, + 289.2, + 289.3, + 289.6, + 289.9, + 290.0, + 289.6, + 289.3, + 289.2, + 289.0, + 289.0, + 289.0, + 288.7, + 288.4, + 288.2, + 288.0, + 287.6, + 287.2, + 286.8, + 286.3, + 286.0, + 285.8, + 286.3, + 286.6, + 283.4, + 291.9, + 296.0, + 290.3, + 292.7, + 292.4, + 290.3, + 289.5, + 289.2, + 288.9, + 288.8, + 289.2, + 289.7, + 289.9, + 289.6, + 289.5, + 289.6, + 289.4, + 289.1, + ], + [ + 290.7, + 290.8, + 290.4, + 290.0, + 290.5, + 291.8, + 292.7, + 293.0, + 293.4, + 292.1, + 291.7, + 292.0, + 291.9, + 291.8, + 291.6, + 291.2, + 291.2, + 291.3, + 291.3, + 291.3, + 291.1, + 290.7, + 290.7, + 290.2, + 289.7, + 289.5, + 289.3, + 289.1, + 289.1, + 289.1, + 289.2, + 289.5, + 289.8, + 289.6, + 289.4, + 289.5, + 289.6, + 290.2, + 293.9, + 290.9, + 292.4, + 294.3, + 292.9, + 291.8, + 291.8, + 291.9, + 292.2, + 292.8, + 292.7, + 292.4, + 292.3, + 291.9, + 291.4, + 291.0, + 291.1, + 291.3, + 291.6, + 292.4, + 292.3, + 292.0, + 291.8, + 291.6, + 291.5, + 291.4, + 291.3, + 291.0, + 290.7, + 290.5, + 290.2, + 289.8, + 289.4, + 288.8, + 288.3, + 287.9, + 287.5, + 288.1, + 288.3, + 286.7, + 294.7, + 298.5, + 297.5, + 294.4, + 293.8, + 292.8, + 292.0, + 291.3, + 291.0, + 291.1, + 291.2, + 291.5, + 291.8, + 291.9, + 291.9, + 291.5, + 291.3, + 291.1, + ], + [ + 292.2, + 292.3, + 292.0, + 292.2, + 292.3, + 291.9, + 293.8, + 295.6, + 296.0, + 296.2, + 295.2, + 293.9, + 293.5, + 293.3, + 293.3, + 293.1, + 293.2, + 293.2, + 293.1, + 292.8, + 292.5, + 292.1, + 292.1, + 291.7, + 291.3, + 291.0, + 290.6, + 290.4, + 290.3, + 290.4, + 290.5, + 290.1, + 291.1, + 291.0, + 290.9, + 291.1, + 291.6, + 297.3, + 298.4, + 294.6, + 291.0, + 296.0, + 294.8, + 294.0, + 294.1, + 294.0, + 293.7, + 294.0, + 293.6, + 293.7, + 293.5, + 293.1, + 292.7, + 292.5, + 292.9, + 293.4, + 293.7, + 294.1, + 294.1, + 294.0, + 293.9, + 293.8, + 293.7, + 293.6, + 293.5, + 293.4, + 293.0, + 292.8, + 292.5, + 292.1, + 291.4, + 290.8, + 290.1, + 289.5, + 289.0, + 289.7, + 288.3, + 292.3, + 297.3, + 301.5, + 301.0, + 299.1, + 295.6, + 294.8, + 293.4, + 293.0, + 292.9, + 293.0, + 293.2, + 293.5, + 293.9, + 294.0, + 293.8, + 293.3, + 292.8, + 292.4, + ], + [ + 292.9, + 292.5, + 291.9, + 292.0, + 291.5, + 295.9, + 289.6, + 290.0, + 297.4, + 297.2, + 295.9, + 295.2, + 295.1, + 295.2, + 295.3, + 295.2, + 295.1, + 294.9, + 294.6, + 294.2, + 293.8, + 293.4, + 293.2, + 292.8, + 292.3, + 291.8, + 291.6, + 291.1, + 291.5, + 292.2, + 293.2, + 297.0, + 294.3, + 292.2, + 292.0, + 292.5, + 298.2, + 298.2, + 297.6, + 295.0, + 292.4, + 296.8, + 295.8, + 295.7, + 295.8, + 295.4, + 295.0, + 294.9, + 294.6, + 294.8, + 294.5, + 294.1, + 294.0, + 294.2, + 294.6, + 294.9, + 295.3, + 295.4, + 295.4, + 295.4, + 295.5, + 295.4, + 295.4, + 295.4, + 295.4, + 295.3, + 294.9, + 294.5, + 294.2, + 293.8, + 293.2, + 292.4, + 291.6, + 290.8, + 290.5, + 290.7, + 287.0, + 294.5, + 299.9, + 301.8, + 303.2, + 299.2, + 297.4, + 296.8, + 295.7, + 294.8, + 294.9, + 294.9, + 294.8, + 295.0, + 295.3, + 295.2, + 294.9, + 294.3, + 293.7, + 293.3, + ], + [ + 293.3, + 293.0, + 292.8, + 293.8, + 293.2, + 298.3, + 293.4, + 289.7, + 290.7, + 298.8, + 297.3, + 297.2, + 297.1, + 296.7, + 296.7, + 296.6, + 296.2, + 295.8, + 295.4, + 295.0, + 294.6, + 294.1, + 293.7, + 293.3, + 293.1, + 293.1, + 293.4, + 292.4, + 292.9, + 294.0, + 295.2, + 299.8, + 293.3, + 293.0, + 293.2, + 297.7, + 300.7, + 298.8, + 297.4, + 298.5, + 294.3, + 297.7, + 296.6, + 296.9, + 296.7, + 296.2, + 295.8, + 295.7, + 295.6, + 295.8, + 295.4, + 295.3, + 295.4, + 295.8, + 295.9, + 296.0, + 296.3, + 296.4, + 296.5, + 296.6, + 296.7, + 296.6, + 296.6, + 296.6, + 296.5, + 296.3, + 295.9, + 295.6, + 295.2, + 294.6, + 294.0, + 293.3, + 292.5, + 291.7, + 291.1, + 290.3, + 286.5, + 294.7, + 297.7, + 304.8, + 304.4, + 300.7, + 297.3, + 298.0, + 297.6, + 296.6, + 296.3, + 296.2, + 295.9, + 295.8, + 295.8, + 295.6, + 295.1, + 294.6, + 293.9, + 293.6, + ], + [ + 293.5, + 293.2, + 293.4, + 293.4, + 291.1, + 297.3, + 294.5, + 292.8, + 290.8, + 299.7, + 299.5, + 299.4, + 299.1, + 298.2, + 297.6, + 297.2, + 296.7, + 296.1, + 295.6, + 295.2, + 294.8, + 294.4, + 294.1, + 293.8, + 293.9, + 294.2, + 294.2, + 293.8, + 294.3, + 295.6, + 297.2, + 300.9, + 295.2, + 296.9, + 298.7, + 299.6, + 301.0, + 300.8, + 300.3, + 300.0, + 295.9, + 298.4, + 297.6, + 297.7, + 297.5, + 297.2, + 296.9, + 296.8, + 296.9, + 296.8, + 296.5, + 296.5, + 296.7, + 296.9, + 296.9, + 296.9, + 297.1, + 297.2, + 297.3, + 297.3, + 297.3, + 297.2, + 297.1, + 297.0, + 296.7, + 296.3, + 296.0, + 295.7, + 295.4, + 294.9, + 294.3, + 293.7, + 293.0, + 292.2, + 291.3, + 290.6, + 290.3, + 293.8, + 283.8, + 303.0, + 303.3, + 301.1, + 295.7, + 296.0, + 299.0, + 298.0, + 297.4, + 297.0, + 296.4, + 296.0, + 295.7, + 295.3, + 294.8, + 294.4, + 293.9, + 293.7, + ], + [ + 293.7, + 293.6, + 294.4, + 293.9, + 294.2, + 296.8, + 296.3, + 294.3, + 293.1, + 298.0, + 300.5, + 300.5, + 298.9, + 299.1, + 298.0, + 297.7, + 297.0, + 296.4, + 295.9, + 295.5, + 295.1, + 294.9, + 294.9, + 294.7, + 295.0, + 295.3, + 295.1, + 295.0, + 295.6, + 296.9, + 298.5, + 301.8, + 297.3, + 301.1, + 302.4, + 299.7, + 299.6, + 301.0, + 300.8, + 298.4, + 297.0, + 299.1, + 298.5, + 298.5, + 298.3, + 298.1, + 298.0, + 298.0, + 297.9, + 297.6, + 297.5, + 297.6, + 297.7, + 297.8, + 297.7, + 297.7, + 297.7, + 297.9, + 298.0, + 297.8, + 297.6, + 297.4, + 297.1, + 296.7, + 296.3, + 295.9, + 295.6, + 295.4, + 295.2, + 294.7, + 294.2, + 293.6, + 292.9, + 292.1, + 291.3, + 290.9, + 291.9, + 294.1, + 280.6, + 297.3, + 301.2, + 301.0, + 296.7, + 296.5, + 299.8, + 298.7, + 297.8, + 297.1, + 296.3, + 295.8, + 295.4, + 295.0, + 294.6, + 294.3, + 293.9, + 293.8, + ], + [ + 294.3, + 294.7, + 294.9, + 294.8, + 300.4, + 293.8, + 295.8, + 294.9, + 295.8, + 298.2, + 301.2, + 301.1, + 295.4, + 299.3, + 298.7, + 298.3, + 297.7, + 297.1, + 296.5, + 296.1, + 295.8, + 295.8, + 295.9, + 295.6, + 295.9, + 296.1, + 295.9, + 296.0, + 296.6, + 297.9, + 299.7, + 302.8, + 300.9, + 303.2, + 301.0, + 298.0, + 298.2, + 299.2, + 299.3, + 300.2, + 299.3, + 300.0, + 299.3, + 299.1, + 298.8, + 298.6, + 298.6, + 298.7, + 298.7, + 298.5, + 298.5, + 298.4, + 298.5, + 298.5, + 298.5, + 298.5, + 298.5, + 298.6, + 298.6, + 298.4, + 298.0, + 297.5, + 297.0, + 296.4, + 295.8, + 295.3, + 294.9, + 294.7, + 294.5, + 294.2, + 293.7, + 293.2, + 292.5, + 291.7, + 291.5, + 291.8, + 293.3, + 293.4, + 281.5, + 294.6, + 300.3, + 299.7, + 299.1, + 296.2, + 294.6, + 299.0, + 298.1, + 297.1, + 296.2, + 295.7, + 295.3, + 295.0, + 294.7, + 294.3, + 294.0, + 294.1, + ], + [ + 295.1, + 295.6, + 295.7, + 295.2, + 294.5, + 294.9, + 296.9, + 295.3, + 294.1, + 297.9, + 301.5, + 300.9, + 301.8, + 298.2, + 298.7, + 298.8, + 298.2, + 297.6, + 297.1, + 296.8, + 296.7, + 296.9, + 297.0, + 297.0, + 297.1, + 297.0, + 296.9, + 297.1, + 297.6, + 298.7, + 300.4, + 302.7, + 302.9, + 301.2, + 299.5, + 299.3, + 299.9, + 299.3, + 298.6, + 297.4, + 300.6, + 300.3, + 299.9, + 299.7, + 299.4, + 299.4, + 299.4, + 299.4, + 299.4, + 299.2, + 299.1, + 299.0, + 299.0, + 299.3, + 299.4, + 299.5, + 299.3, + 299.2, + 299.2, + 298.7, + 298.1, + 297.5, + 296.9, + 296.1, + 295.4, + 294.8, + 294.3, + 294.0, + 293.7, + 293.4, + 293.1, + 292.5, + 292.0, + 291.8, + 292.4, + 293.3, + 293.7, + 292.8, + 281.2, + 293.6, + 299.9, + 300.7, + 298.9, + 296.3, + 294.1, + 296.1, + 298.5, + 297.5, + 296.7, + 296.1, + 295.7, + 295.4, + 295.0, + 294.7, + 294.5, + 294.7, + ], + [ + 295.7, + 296.3, + 296.6, + 296.3, + 293.9, + 295.3, + 295.8, + 295.6, + 294.4, + 297.6, + 300.6, + 300.6, + 301.4, + 298.0, + 298.9, + 299.1, + 298.6, + 298.0, + 297.7, + 297.6, + 297.7, + 298.2, + 298.5, + 298.5, + 298.5, + 298.5, + 298.2, + 298.4, + 298.8, + 299.7, + 300.9, + 302.2, + 302.6, + 300.0, + 297.9, + 299.1, + 299.7, + 300.4, + 299.2, + 301.1, + 300.7, + 300.8, + 300.9, + 300.7, + 300.4, + 300.2, + 300.0, + 299.7, + 299.6, + 299.5, + 299.7, + 299.8, + 299.8, + 299.8, + 300.0, + 300.1, + 299.9, + 299.6, + 299.3, + 299.1, + 298.4, + 297.7, + 296.8, + 295.8, + 295.0, + 294.4, + 293.9, + 293.5, + 293.1, + 292.8, + 292.6, + 292.2, + 292.2, + 292.7, + 293.3, + 293.3, + 290.7, + 293.3, + 283.6, + 295.9, + 300.0, + 301.3, + 296.9, + 295.9, + 295.1, + 296.2, + 298.6, + 298.3, + 297.6, + 296.9, + 296.4, + 295.9, + 295.6, + 295.3, + 295.2, + 295.3, + ], + [ + 296.7, + 297.4, + 298.1, + 298.3, + 293.0, + 294.1, + 295.4, + 294.8, + 296.0, + 296.7, + 297.1, + 300.4, + 301.3, + 298.5, + 299.2, + 299.2, + 298.8, + 298.5, + 298.4, + 298.7, + 299.1, + 299.4, + 299.6, + 299.8, + 299.9, + 299.8, + 299.6, + 299.8, + 300.2, + 300.8, + 301.3, + 301.9, + 302.2, + 302.4, + 299.3, + 299.7, + 300.3, + 302.5, + 300.3, + 301.2, + 301.4, + 301.5, + 301.5, + 301.3, + 300.9, + 300.6, + 300.4, + 300.2, + 300.2, + 300.2, + 300.6, + 301.0, + 300.8, + 300.5, + 300.4, + 300.4, + 300.2, + 300.0, + 299.7, + 299.4, + 298.9, + 298.1, + 297.2, + 296.3, + 295.5, + 294.7, + 294.1, + 293.5, + 293.0, + 292.6, + 292.3, + 292.4, + 292.9, + 293.6, + 294.6, + 295.1, + 293.8, + 280.5, + 293.1, + 299.5, + 298.8, + 298.8, + 297.6, + 295.5, + 295.4, + 294.9, + 298.9, + 299.0, + 298.2, + 297.5, + 296.9, + 296.4, + 296.2, + 296.0, + 295.9, + 296.3, + ], + [ + 298.1, + 298.6, + 299.5, + 299.7, + 293.4, + 293.8, + 294.6, + 293.7, + 293.8, + 296.3, + 297.1, + 300.2, + 300.4, + 299.7, + 299.6, + 299.5, + 299.3, + 299.5, + 299.6, + 299.9, + 300.1, + 300.3, + 300.6, + 300.8, + 300.8, + 300.8, + 300.7, + 300.9, + 301.2, + 301.6, + 301.8, + 302.0, + 302.1, + 302.2, + 302.2, + 302.3, + 300.9, + 302.6, + 301.9, + 301.9, + 302.0, + 301.8, + 301.7, + 301.4, + 301.2, + 301.1, + 301.1, + 301.0, + 300.9, + 300.9, + 301.2, + 301.3, + 301.1, + 300.9, + 300.8, + 300.7, + 300.6, + 300.4, + 300.1, + 299.9, + 299.6, + 299.0, + 298.1, + 297.3, + 296.3, + 295.4, + 294.7, + 294.1, + 293.8, + 293.4, + 293.4, + 293.8, + 294.6, + 295.3, + 296.4, + 296.5, + 286.2, + 293.0, + 299.3, + 299.6, + 298.9, + 298.4, + 298.4, + 297.8, + 295.5, + 294.9, + 298.6, + 299.3, + 298.8, + 298.2, + 297.7, + 297.2, + 296.9, + 296.9, + 297.2, + 297.7, + ], + [ + 299.3, + 300.1, + 300.9, + 301.0, + 296.5, + 295.0, + 295.4, + 294.5, + 293.6, + 293.2, + 298.3, + 300.1, + 300.6, + 300.2, + 300.2, + 300.2, + 300.1, + 300.3, + 300.4, + 300.5, + 300.6, + 300.7, + 300.8, + 300.9, + 301.0, + 301.1, + 301.2, + 301.2, + 301.4, + 301.6, + 301.8, + 301.9, + 301.9, + 302.0, + 302.2, + 302.2, + 302.4, + 302.3, + 302.5, + 302.3, + 302.1, + 301.8, + 301.7, + 301.6, + 301.5, + 301.5, + 301.4, + 301.3, + 301.3, + 301.4, + 301.4, + 301.3, + 301.2, + 301.1, + 301.1, + 301.0, + 300.9, + 300.7, + 300.5, + 300.2, + 299.8, + 299.2, + 298.4, + 297.5, + 296.7, + 296.1, + 295.6, + 295.3, + 295.1, + 295.0, + 295.1, + 295.5, + 296.1, + 297.1, + 297.6, + 296.6, + 292.3, + 299.6, + 300.4, + 299.8, + 299.4, + 298.4, + 298.4, + 298.9, + 296.7, + 296.7, + 300.1, + 299.5, + 299.2, + 298.8, + 298.4, + 298.2, + 298.2, + 298.4, + 298.7, + 298.9, + ], + [ + 300.7, + 301.3, + 301.7, + 301.6, + 297.2, + 296.6, + 296.7, + 296.4, + 294.2, + 293.5, + 299.5, + 299.8, + 300.8, + 300.4, + 300.1, + 300.1, + 300.2, + 300.4, + 300.5, + 300.5, + 300.6, + 300.8, + 300.8, + 300.8, + 300.8, + 300.9, + 300.9, + 300.9, + 301.2, + 301.4, + 301.6, + 301.8, + 302.0, + 302.1, + 302.2, + 302.4, + 302.4, + 302.4, + 300.7, + 296.4, + 302.0, + 301.9, + 301.9, + 301.8, + 301.8, + 301.7, + 301.6, + 301.5, + 301.5, + 301.4, + 301.4, + 301.4, + 301.3, + 301.2, + 301.0, + 300.9, + 300.8, + 300.7, + 300.4, + 299.9, + 299.4, + 298.8, + 298.2, + 297.7, + 297.3, + 296.9, + 296.6, + 296.4, + 296.3, + 296.3, + 296.5, + 296.9, + 297.4, + 298.4, + 298.5, + 291.0, + 298.6, + 300.5, + 300.8, + 300.9, + 300.1, + 298.7, + 298.5, + 298.4, + 297.9, + 297.8, + 299.0, + 299.5, + 299.9, + 299.7, + 299.6, + 299.5, + 299.5, + 299.6, + 299.9, + 300.1, + ], + [ + 301.2, + 301.4, + 301.5, + 301.6, + 298.1, + 298.3, + 298.3, + 297.6, + 294.4, + 293.5, + 297.6, + 299.1, + 300.7, + 300.6, + 300.2, + 299.9, + 300.0, + 300.2, + 300.4, + 300.4, + 300.5, + 300.6, + 300.5, + 300.6, + 300.7, + 300.8, + 300.9, + 301.5, + 299.1, + 302.0, + 302.1, + 302.2, + 302.6, + 302.4, + 302.2, + 302.3, + 302.2, + 298.0, + 295.6, + 301.5, + 301.8, + 301.7, + 301.6, + 301.5, + 301.3, + 301.2, + 301.1, + 301.0, + 301.0, + 300.9, + 300.7, + 300.6, + 300.3, + 300.0, + 299.9, + 299.8, + 299.7, + 299.6, + 299.4, + 299.2, + 298.8, + 298.5, + 298.1, + 297.8, + 297.8, + 297.7, + 297.6, + 297.5, + 297.6, + 297.7, + 297.9, + 298.2, + 298.8, + 299.5, + 299.3, + 294.1, + 300.7, + 301.2, + 301.1, + 300.9, + 300.2, + 299.5, + 298.9, + 298.7, + 298.8, + 297.7, + 300.7, + 300.1, + 300.4, + 300.3, + 300.3, + 300.3, + 300.4, + 300.5, + 300.7, + 300.9, + ], + [ + 301.2, + 301.3, + 301.4, + 299.1, + 297.8, + 299.4, + 298.8, + 297.3, + 291.6, + 294.2, + 297.2, + 299.3, + 300.3, + 300.2, + 300.3, + 300.2, + 300.1, + 300.2, + 300.3, + 300.5, + 300.5, + 300.6, + 300.6, + 300.6, + 300.8, + 301.1, + 301.2, + 298.7, + 300.5, + 302.1, + 300.4, + 300.7, + 296.8, + 302.3, + 302.2, + 302.3, + 302.6, + 299.3, + 301.6, + 301.2, + 300.7, + 300.4, + 300.3, + 300.2, + 300.1, + 300.0, + 300.0, + 299.7, + 299.7, + 299.4, + 299.3, + 299.2, + 298.8, + 298.6, + 298.3, + 297.9, + 297.7, + 297.5, + 297.4, + 297.5, + 297.3, + 297.2, + 297.1, + 297.0, + 297.2, + 297.0, + 297.3, + 297.4, + 297.7, + 298.0, + 298.0, + 298.6, + 298.9, + 299.3, + 299.3, + 294.4, + 300.5, + 301.2, + 300.9, + 300.7, + 299.8, + 300.0, + 301.0, + 299.5, + 299.3, + 300.7, + 300.7, + 300.2, + 300.4, + 300.5, + 300.4, + 300.5, + 300.6, + 300.7, + 300.9, + 301.0, + ], + [ + 301.4, + 301.5, + 301.4, + 299.0, + 298.3, + 299.5, + 299.6, + 297.9, + 293.7, + 293.8, + 296.3, + 302.4, + 300.0, + 299.5, + 299.7, + 300.1, + 300.3, + 300.3, + 300.5, + 300.6, + 300.6, + 300.6, + 300.8, + 300.9, + 301.3, + 301.6, + 301.5, + 298.7, + 301.6, + 301.6, + 298.6, + 299.5, + 302.0, + 301.9, + 302.1, + 302.2, + 302.1, + 301.8, + 301.2, + 300.6, + 300.2, + 300.0, + 299.8, + 299.7, + 299.6, + 299.4, + 299.4, + 299.1, + 299.0, + 298.6, + 298.5, + 298.2, + 298.0, + 297.8, + 297.6, + 297.3, + 297.0, + 297.0, + 296.6, + 296.8, + 296.5, + 296.5, + 296.3, + 296.3, + 296.0, + 296.1, + 296.5, + 296.8, + 297.0, + 297.8, + 298.0, + 298.8, + 299.0, + 299.3, + 299.4, + 293.3, + 299.8, + 300.5, + 300.4, + 299.8, + 300.2, + 302.2, + 300.8, + 300.1, + 300.2, + 300.1, + 300.0, + 300.2, + 300.2, + 300.3, + 300.4, + 300.6, + 300.8, + 300.9, + 301.1, + 301.2, + ], + [ + 301.9, + 301.9, + 301.6, + 298.0, + 297.9, + 299.2, + 298.9, + 297.8, + 295.1, + 295.1, + 300.5, + 301.9, + 300.2, + 299.3, + 299.3, + 299.8, + 299.9, + 300.1, + 300.2, + 300.3, + 300.2, + 300.1, + 300.3, + 300.5, + 301.1, + 301.6, + 301.5, + 299.3, + 300.9, + 301.2, + 299.4, + 297.5, + 301.9, + 301.9, + 301.9, + 301.9, + 301.9, + 301.7, + 301.6, + 301.3, + 301.0, + 300.7, + 300.6, + 300.4, + 300.3, + 300.1, + 299.9, + 299.8, + 299.7, + 299.5, + 299.3, + 299.0, + 299.1, + 299.2, + 299.0, + 298.8, + 298.6, + 298.5, + 298.4, + 298.4, + 298.1, + 298.1, + 298.3, + 298.3, + 298.2, + 298.4, + 298.7, + 298.8, + 299.1, + 299.3, + 299.4, + 299.7, + 299.7, + 299.7, + 299.9, + 300.1, + 293.9, + 299.3, + 300.0, + 298.1, + 302.8, + 301.0, + 299.5, + 300.0, + 299.9, + 299.9, + 300.0, + 300.2, + 300.4, + 300.6, + 300.8, + 301.0, + 301.3, + 301.4, + 301.7, + 301.8, + ], + [ + 302.3, + 302.5, + 299.8, + 295.9, + 296.7, + 297.9, + 297.1, + 295.9, + 298.0, + 299.7, + 292.1, + 298.2, + 298.1, + 299.2, + 298.9, + 299.5, + 299.8, + 300.0, + 300.1, + 300.0, + 299.8, + 299.7, + 299.8, + 300.2, + 300.7, + 301.2, + 301.3, + 296.9, + 300.8, + 301.4, + 302.1, + 298.0, + 302.2, + 301.9, + 301.5, + 301.8, + 301.8, + 301.7, + 301.6, + 301.5, + 301.4, + 301.3, + 301.2, + 301.1, + 301.0, + 300.8, + 300.7, + 300.6, + 300.4, + 300.3, + 300.1, + 300.0, + 300.1, + 299.9, + 299.9, + 299.9, + 299.8, + 299.7, + 299.7, + 299.8, + 299.7, + 299.6, + 299.5, + 299.4, + 299.4, + 299.6, + 299.7, + 299.8, + 300.0, + 300.1, + 300.1, + 300.1, + 300.1, + 299.9, + 300.1, + 301.1, + 290.3, + 299.6, + 299.7, + 298.0, + 296.0, + 299.6, + 299.4, + 299.8, + 299.6, + 299.6, + 299.9, + 300.1, + 300.4, + 300.7, + 301.0, + 301.3, + 301.6, + 302.2, + 299.7, + 300.3, + ], + [ + 298.4, + 297.2, + 296.5, + 293.9, + 297.7, + 298.4, + 294.5, + 296.0, + 301.3, + 299.8, + 288.7, + 292.5, + 294.3, + 296.1, + 298.5, + 298.7, + 299.2, + 299.5, + 299.7, + 299.7, + 299.6, + 299.5, + 299.6, + 300.0, + 300.4, + 300.7, + 300.8, + 300.4, + 300.8, + 301.3, + 301.5, + 301.9, + 302.0, + 297.3, + 301.3, + 301.4, + 301.3, + 301.2, + 301.1, + 301.0, + 300.9, + 300.8, + 300.8, + 300.7, + 300.5, + 300.3, + 300.3, + 300.2, + 300.0, + 300.0, + 300.0, + 299.9, + 299.9, + 299.9, + 299.9, + 299.9, + 299.9, + 299.8, + 299.7, + 299.8, + 299.8, + 299.8, + 299.8, + 299.8, + 299.9, + 300.1, + 300.2, + 300.1, + 299.9, + 299.8, + 299.8, + 300.0, + 299.9, + 299.7, + 299.7, + 298.3, + 295.7, + 296.7, + 303.6, + 300.4, + 299.1, + 299.5, + 299.7, + 299.7, + 299.0, + 298.9, + 299.1, + 299.3, + 299.6, + 299.9, + 300.0, + 300.5, + 301.6, + 298.6, + 297.4, + 298.3, + ], + [ + 296.5, + 294.9, + 293.1, + 296.6, + 300.2, + 300.2, + 298.6, + 299.1, + 301.0, + 299.0, + 290.4, + 291.1, + 290.9, + 290.4, + 297.5, + 297.8, + 298.0, + 298.4, + 299.1, + 299.6, + 299.8, + 293.2, + 299.1, + 299.5, + 299.9, + 300.2, + 300.3, + 297.6, + 298.5, + 301.0, + 301.0, + 301.2, + 301.1, + 298.1, + 300.7, + 300.7, + 300.6, + 300.5, + 300.4, + 300.2, + 299.9, + 299.7, + 299.6, + 299.4, + 299.2, + 299.0, + 298.9, + 298.8, + 298.6, + 298.6, + 298.6, + 298.6, + 298.7, + 298.6, + 298.6, + 298.7, + 298.7, + 298.7, + 298.7, + 298.8, + 299.0, + 299.1, + 299.2, + 299.3, + 299.5, + 299.8, + 299.9, + 299.6, + 299.2, + 299.0, + 299.1, + 299.6, + 300.1, + 300.1, + 294.4, + 299.6, + 300.2, + 299.3, + 301.1, + 300.2, + 299.0, + 299.5, + 299.1, + 298.7, + 298.5, + 298.2, + 298.1, + 298.1, + 298.3, + 298.6, + 298.7, + 299.2, + 300.9, + 295.8, + 295.9, + 296.5, + ], + [ + 298.7, + 297.5, + 297.7, + 299.4, + 300.9, + 299.8, + 297.2, + 296.9, + 299.0, + 301.1, + 291.8, + 289.3, + 288.2, + 297.1, + 297.1, + 296.8, + 296.8, + 297.3, + 298.3, + 299.3, + 300.1, + 291.1, + 298.5, + 298.8, + 299.1, + 299.3, + 298.9, + 295.9, + 297.2, + 296.0, + 300.4, + 300.4, + 300.1, + 297.6, + 300.2, + 300.1, + 300.1, + 300.1, + 300.0, + 299.8, + 299.6, + 299.3, + 299.0, + 298.6, + 298.3, + 298.1, + 297.9, + 297.7, + 297.6, + 297.5, + 297.5, + 297.4, + 297.5, + 297.5, + 297.4, + 297.2, + 297.2, + 297.3, + 297.2, + 297.5, + 297.8, + 298.1, + 298.3, + 298.6, + 298.8, + 299.2, + 299.5, + 299.4, + 298.8, + 298.1, + 298.0, + 298.6, + 300.1, + 296.6, + 299.2, + 297.7, + 297.0, + 296.4, + 297.7, + 298.7, + 298.7, + 298.8, + 298.4, + 298.2, + 298.1, + 297.8, + 297.5, + 297.3, + 297.2, + 297.5, + 297.7, + 297.9, + 299.8, + 296.7, + 297.8, + 298.6, + ], + [ + 298.5, + 297.9, + 297.1, + 296.8, + 297.3, + 297.1, + 294.6, + 293.7, + 296.6, + 299.4, + 293.8, + 298.8, + 284.9, + 289.3, + 297.1, + 295.9, + 295.7, + 296.3, + 297.4, + 298.5, + 291.5, + 294.5, + 298.3, + 298.2, + 298.4, + 298.2, + 297.4, + 293.2, + 293.8, + 293.9, + 299.2, + 299.3, + 295.4, + 299.2, + 299.3, + 299.1, + 299.2, + 299.2, + 299.3, + 299.4, + 299.3, + 299.0, + 298.6, + 298.1, + 297.6, + 297.3, + 297.0, + 296.8, + 296.7, + 296.6, + 296.5, + 296.2, + 296.3, + 296.3, + 296.1, + 296.1, + 296.1, + 296.1, + 296.3, + 296.5, + 296.8, + 297.0, + 297.2, + 297.2, + 297.3, + 297.7, + 298.4, + 299.1, + 298.9, + 297.9, + 297.2, + 296.4, + 291.5, + 293.8, + 299.1, + 299.1, + 298.8, + 298.8, + 298.9, + 298.9, + 298.5, + 298.2, + 297.8, + 297.5, + 297.4, + 297.3, + 297.1, + 296.9, + 296.7, + 296.5, + 296.5, + 296.9, + 299.4, + 299.6, + 298.4, + 298.6, + ], + [ + 295.1, + 294.7, + 293.0, + 292.3, + 293.5, + 294.7, + 292.3, + 293.1, + 295.3, + 296.1, + 294.7, + 300.3, + 284.4, + 288.6, + 289.2, + 295.6, + 294.9, + 295.7, + 296.8, + 298.0, + 291.2, + 291.8, + 290.6, + 298.1, + 298.1, + 297.6, + 288.6, + 289.9, + 289.8, + 296.0, + 297.7, + 298.2, + 298.0, + 297.6, + 297.2, + 297.0, + 297.2, + 297.5, + 297.7, + 297.9, + 298.2, + 298.4, + 298.3, + 298.0, + 297.6, + 297.1, + 296.6, + 296.3, + 295.9, + 295.7, + 295.4, + 295.1, + 295.0, + 294.9, + 294.7, + 294.7, + 294.6, + 294.6, + 294.8, + 295.2, + 295.4, + 295.3, + 295.4, + 295.3, + 295.2, + 295.4, + 296.6, + 298.2, + 298.9, + 289.8, + 287.0, + 289.8, + 294.7, + 298.4, + 298.6, + 298.7, + 299.0, + 299.0, + 298.6, + 298.5, + 298.2, + 297.8, + 297.5, + 297.1, + 296.7, + 296.6, + 296.6, + 296.4, + 296.2, + 296.1, + 296.1, + 295.9, + 296.5, + 296.6, + 295.4, + 295.0, + ], + [ + 292.2, + 290.9, + 290.4, + 290.4, + 290.2, + 288.6, + 289.6, + 290.2, + 292.8, + 292.5, + 299.2, + 289.1, + 287.6, + 289.6, + 290.8, + 290.7, + 295.2, + 295.5, + 296.5, + 297.5, + 289.3, + 291.2, + 287.7, + 298.1, + 298.2, + 288.9, + 286.8, + 285.9, + 286.0, + 292.4, + 295.0, + 296.9, + 296.4, + 295.5, + 294.8, + 294.5, + 294.8, + 295.2, + 295.6, + 295.9, + 296.3, + 296.7, + 296.9, + 297.0, + 296.8, + 296.5, + 296.1, + 295.8, + 295.3, + 294.9, + 294.6, + 294.3, + 294.1, + 293.8, + 293.6, + 293.4, + 293.2, + 293.1, + 293.0, + 293.1, + 293.1, + 293.0, + 293.0, + 293.0, + 292.9, + 293.4, + 294.6, + 296.7, + 290.7, + 283.2, + 283.4, + 296.3, + 294.1, + 297.6, + 297.4, + 297.6, + 295.5, + 295.2, + 298.1, + 297.8, + 297.6, + 297.3, + 297.1, + 296.8, + 296.3, + 295.9, + 295.8, + 295.7, + 295.6, + 295.5, + 295.5, + 294.7, + 293.2, + 292.9, + 292.1, + 292.2, + ], + [ + 291.1, + 288.9, + 286.0, + 287.7, + 287.6, + 286.6, + 287.5, + 287.1, + 290.2, + 290.0, + 297.1, + 287.9, + 286.7, + 288.3, + 290.4, + 288.3, + 295.3, + 295.2, + 295.8, + 288.5, + 288.9, + 288.4, + 287.4, + 287.3, + 291.2, + 286.0, + 284.9, + 280.5, + 282.4, + 280.3, + 283.8, + 293.2, + 293.6, + 293.6, + 292.4, + 292.2, + 292.3, + 292.5, + 293.2, + 293.8, + 294.2, + 294.8, + 295.2, + 295.5, + 295.6, + 295.5, + 295.2, + 294.8, + 294.4, + 294.0, + 293.7, + 293.4, + 293.1, + 292.8, + 292.6, + 292.4, + 292.2, + 292.0, + 291.8, + 291.8, + 291.7, + 291.7, + 291.8, + 291.8, + 291.6, + 292.0, + 293.2, + 295.5, + 284.4, + 280.6, + 286.9, + 294.5, + 295.2, + 296.1, + 295.2, + 291.2, + 296.1, + 296.7, + 296.8, + 296.6, + 296.5, + 296.5, + 296.3, + 296.1, + 295.7, + 295.3, + 295.1, + 294.9, + 294.9, + 294.8, + 294.9, + 294.4, + 290.6, + 290.8, + 290.8, + 291.1, + ], + [ + 290.4, + 288.1, + 285.2, + 285.7, + 286.9, + 287.4, + 287.2, + 286.0, + 287.6, + 289.6, + 290.1, + 285.8, + 284.8, + 287.0, + 289.7, + 288.5, + 294.5, + 294.6, + 286.5, + 286.4, + 284.1, + 285.4, + 287.0, + 288.2, + 287.0, + 282.8, + 281.1, + 278.0, + 278.5, + 280.5, + 279.8, + 276.8, + 289.8, + 292.0, + 290.9, + 290.5, + 290.5, + 290.4, + 290.8, + 291.3, + 291.9, + 292.6, + 293.2, + 293.7, + 294.0, + 294.1, + 294.0, + 293.9, + 293.6, + 293.2, + 292.8, + 292.4, + 292.1, + 291.6, + 291.3, + 291.1, + 290.9, + 290.9, + 290.9, + 290.8, + 290.7, + 290.8, + 290.9, + 290.9, + 290.6, + 290.9, + 292.0, + 293.9, + 279.2, + 280.3, + 285.6, + 293.0, + 293.5, + 293.6, + 292.5, + 293.9, + 294.4, + 294.8, + 295.0, + 295.2, + 295.2, + 295.3, + 295.3, + 295.3, + 295.1, + 294.9, + 294.6, + 294.4, + 294.2, + 294.0, + 294.0, + 294.2, + 294.0, + 289.0, + 289.9, + 290.0, + ], + [ + 288.3, + 287.8, + 287.2, + 286.6, + 286.8, + 287.7, + 288.2, + 287.0, + 286.6, + 287.5, + 284.8, + 283.8, + 284.1, + 293.8, + 294.4, + 283.1, + 280.0, + 280.8, + 283.8, + 284.8, + 281.4, + 284.1, + 284.0, + 275.8, + 272.7, + 278.9, + 274.1, + 272.2, + 277.0, + 274.2, + 277.1, + 276.9, + 275.1, + 288.0, + 288.3, + 289.1, + 289.0, + 288.8, + 288.8, + 289.2, + 289.6, + 290.3, + 290.9, + 291.6, + 292.0, + 292.2, + 292.2, + 292.2, + 292.1, + 291.9, + 291.6, + 291.3, + 291.0, + 290.6, + 290.3, + 290.0, + 289.7, + 289.6, + 289.7, + 289.9, + 289.8, + 289.7, + 289.8, + 289.8, + 289.5, + 289.8, + 290.7, + 279.3, + 278.3, + 280.4, + 281.1, + 288.7, + 288.6, + 288.4, + 288.4, + 291.6, + 292.2, + 292.7, + 293.0, + 293.3, + 293.5, + 293.8, + 293.9, + 294.1, + 294.1, + 294.0, + 293.8, + 293.7, + 293.6, + 293.4, + 293.1, + 293.1, + 293.5, + 287.8, + 287.2, + 287.1, + ], + [ + 283.4, + 285.0, + 285.9, + 285.5, + 286.3, + 287.6, + 287.1, + 286.6, + 286.6, + 284.8, + 282.4, + 282.7, + 284.0, + 293.1, + 274.8, + 275.5, + 279.8, + 279.6, + 278.0, + 283.2, + 282.9, + 276.8, + 261.2, + 245.8, + 259.6, + 256.2, + 259.3, + 267.7, + 277.8, + 274.4, + 275.5, + 275.4, + 274.8, + 284.1, + 284.2, + 285.8, + 286.5, + 286.5, + 287.1, + 287.7, + 288.2, + 288.7, + 289.2, + 289.7, + 290.1, + 290.3, + 290.4, + 290.2, + 290.2, + 290.0, + 289.8, + 289.6, + 289.3, + 289.1, + 289.0, + 288.9, + 288.9, + 288.7, + 288.7, + 288.9, + 288.9, + 288.6, + 288.8, + 288.7, + 288.3, + 288.6, + 281.2, + 277.0, + 276.8, + 277.0, + 275.8, + 277.1, + 276.5, + 276.5, + 276.7, + 288.3, + 290.4, + 291.3, + 291.5, + 291.7, + 291.9, + 292.1, + 292.4, + 292.6, + 292.8, + 292.9, + 292.8, + 292.8, + 292.7, + 292.6, + 292.3, + 292.2, + 292.5, + 292.7, + 282.1, + 283.9, + ], + [ + 279.1, + 280.6, + 283.5, + 283.6, + 289.8, + 290.2, + 290.1, + 290.0, + 290.4, + 290.8, + 280.5, + 281.3, + 283.8, + 276.7, + 274.6, + 276.6, + 276.4, + 275.7, + 272.5, + 281.0, + 278.8, + 254.8, + 251.9, + 253.4, + 256.7, + 247.5, + 257.2, + 263.1, + 270.5, + 272.0, + 273.2, + 273.7, + 274.2, + 280.9, + 279.7, + 282.0, + 282.5, + 282.2, + 284.2, + 285.2, + 286.2, + 287.0, + 287.5, + 287.9, + 288.2, + 288.4, + 288.6, + 288.5, + 288.3, + 288.2, + 287.9, + 287.7, + 287.4, + 287.3, + 287.3, + 287.4, + 287.6, + 287.5, + 287.5, + 287.6, + 287.8, + 287.6, + 287.7, + 287.6, + 287.1, + 279.8, + 279.5, + 273.8, + 273.7, + 274.4, + 274.8, + 275.3, + 275.0, + 275.1, + 274.7, + 284.1, + 288.4, + 289.8, + 290.2, + 290.2, + 290.6, + 290.7, + 290.9, + 291.1, + 291.3, + 291.6, + 291.8, + 291.8, + 291.7, + 291.6, + 291.5, + 291.4, + 291.5, + 291.4, + 282.4, + 278.9, + ], + [ + 278.8, + 277.5, + 278.0, + 288.2, + 288.2, + 288.1, + 288.2, + 288.6, + 289.0, + 289.3, + 278.3, + 280.4, + 277.2, + 272.0, + 275.3, + 276.3, + 275.6, + 273.5, + 269.1, + 269.9, + 255.7, + 246.4, + 251.9, + 253.4, + 245.8, + 246.2, + 258.6, + 263.1, + 267.9, + 270.2, + 270.5, + 271.0, + 276.2, + 276.7, + 274.4, + 277.2, + 269.0, + 266.8, + 280.2, + 282.3, + 283.3, + 284.2, + 285.0, + 285.6, + 285.8, + 286.0, + 286.3, + 286.5, + 286.4, + 286.3, + 286.1, + 285.9, + 285.7, + 285.6, + 285.5, + 285.7, + 286.1, + 286.1, + 286.2, + 286.4, + 286.5, + 286.5, + 286.6, + 286.7, + 277.9, + 276.5, + 273.3, + 269.3, + 271.2, + 272.5, + 272.2, + 271.8, + 271.7, + 271.3, + 270.9, + 270.7, + 284.5, + 287.6, + 288.5, + 288.9, + 289.5, + 289.8, + 289.6, + 289.7, + 290.0, + 290.2, + 290.5, + 290.7, + 290.8, + 290.7, + 290.7, + 290.5, + 290.1, + 290.0, + 289.2, + 281.4, + ], + [ + 285.2, + 286.4, + 286.4, + 286.0, + 286.2, + 286.3, + 286.6, + 286.0, + 276.0, + 274.1, + 274.7, + 274.2, + 270.0, + 272.4, + 287.2, + 274.7, + 275.2, + 275.3, + 275.2, + 260.8, + 251.5, + 268.3, + 270.1, + 263.4, + 252.5, + 263.2, + 252.3, + 262.8, + 264.5, + 265.2, + 266.5, + 268.0, + 272.0, + 270.4, + 247.3, + 271.7, + 275.1, + 266.1, + 277.3, + 278.6, + 280.3, + 281.6, + 282.2, + 282.6, + 283.1, + 283.4, + 283.9, + 284.3, + 284.5, + 284.3, + 283.8, + 283.5, + 283.5, + 283.4, + 283.6, + 284.0, + 284.5, + 284.7, + 284.9, + 285.2, + 285.3, + 285.5, + 285.4, + 285.4, + 273.3, + 267.6, + 265.5, + 264.9, + 263.3, + 271.6, + 270.0, + 268.6, + 268.3, + 268.5, + 268.7, + 270.4, + 281.5, + 285.3, + 286.5, + 287.6, + 288.1, + 288.5, + 288.7, + 288.6, + 288.7, + 288.8, + 289.0, + 289.6, + 289.7, + 289.8, + 289.6, + 289.3, + 288.6, + 287.8, + 279.7, + 279.1, + ], + [ + 273.8, + 284.8, + 284.3, + 283.4, + 284.1, + 284.3, + 269.9, + 276.7, + 275.1, + 272.3, + 266.6, + 264.7, + 269.8, + 274.6, + 283.5, + 272.9, + 272.1, + 273.2, + 272.5, + 263.6, + 259.2, + 256.8, + 262.2, + 266.6, + 266.4, + 261.5, + 258.6, + 264.1, + 262.1, + 262.1, + 260.5, + 263.4, + 255.3, + 246.1, + 253.9, + 266.2, + 270.5, + 272.7, + 262.4, + 270.6, + 276.3, + 277.7, + 277.8, + 277.9, + 278.5, + 279.6, + 280.3, + 280.7, + 281.2, + 281.3, + 281.1, + 281.1, + 281.3, + 281.5, + 281.7, + 282.3, + 282.9, + 283.3, + 283.5, + 284.0, + 284.2, + 284.4, + 284.4, + 278.8, + 269.8, + 264.9, + 264.1, + 262.6, + 262.6, + 268.9, + 265.4, + 264.7, + 264.5, + 265.8, + 266.6, + 265.8, + 267.8, + 279.0, + 281.4, + 283.4, + 284.6, + 285.3, + 285.8, + 286.0, + 286.8, + 286.9, + 287.1, + 288.1, + 288.7, + 288.8, + 288.4, + 288.0, + 287.3, + 286.7, + 277.4, + 276.5, + ], + [ + 274.9, + 283.4, + 282.1, + 269.1, + 281.3, + 261.9, + 270.2, + 267.7, + 280.6, + 281.8, + 282.8, + 273.9, + 265.5, + 281.3, + 272.6, + 269.8, + 268.9, + 269.3, + 271.0, + 266.4, + 261.9, + 257.4, + 261.4, + 253.0, + 261.0, + 261.9, + 257.6, + 260.1, + 257.9, + 258.3, + 256.1, + 252.3, + 255.7, + 247.3, + 251.4, + 240.9, + 265.6, + 270.0, + 261.5, + 269.7, + 270.6, + 274.3, + 274.8, + 275.4, + 276.4, + 277.5, + 277.5, + 277.6, + 278.1, + 278.5, + 278.6, + 279.0, + 279.3, + 279.7, + 280.0, + 280.5, + 281.1, + 281.7, + 282.1, + 282.5, + 283.0, + 283.1, + 283.4, + 276.3, + 268.0, + 265.9, + 262.4, + 261.1, + 261.6, + 262.4, + 262.7, + 263.0, + 262.7, + 264.7, + 265.1, + 264.7, + 266.2, + 265.9, + 276.9, + 279.0, + 280.8, + 281.2, + 280.9, + 281.5, + 283.2, + 283.6, + 284.3, + 286.1, + 287.3, + 287.6, + 286.8, + 286.8, + 286.5, + 286.3, + 275.0, + 276.1, + ], + [ + 274.5, + 271.0, + 266.0, + 268.2, + 266.9, + 266.2, + 266.2, + 263.3, + 277.4, + 279.3, + 280.8, + 273.2, + 273.8, + 278.1, + 269.3, + 267.1, + 265.8, + 264.3, + 266.5, + 265.0, + 266.8, + 262.1, + 257.7, + 260.3, + 253.8, + 252.7, + 247.7, + 255.3, + 255.3, + 253.8, + 254.4, + 250.6, + 250.4, + 243.1, + 244.6, + 248.9, + 249.0, + 266.6, + 262.0, + 270.0, + 272.0, + 273.2, + 273.3, + 274.0, + 274.7, + 275.4, + 275.5, + 275.6, + 275.9, + 276.1, + 276.5, + 277.1, + 277.7, + 278.1, + 278.4, + 278.9, + 279.7, + 280.2, + 280.6, + 281.2, + 281.9, + 282.1, + 282.5, + 276.1, + 272.3, + 267.5, + 260.7, + 261.8, + 262.1, + 259.5, + 259.8, + 261.4, + 261.6, + 272.4, + 273.0, + 259.2, + 261.9, + 262.4, + 264.8, + 274.8, + 276.8, + 276.2, + 276.9, + 277.8, + 279.4, + 280.8, + 282.1, + 284.0, + 285.3, + 286.1, + 285.4, + 285.1, + 285.2, + 285.8, + 285.1, + 283.7, + ], + [ + 273.0, + 274.0, + 266.9, + 261.6, + 264.6, + 265.3, + 264.3, + 262.6, + 262.2, + 264.1, + 268.2, + 268.7, + 267.6, + 266.1, + 263.6, + 262.5, + 260.5, + 260.1, + 259.8, + 258.0, + 258.9, + 259.2, + 256.6, + 251.1, + 247.7, + 252.2, + 249.4, + 249.4, + 253.3, + 249.6, + 249.9, + 246.2, + 246.0, + 244.9, + 242.8, + 246.4, + 249.2, + 250.5, + 264.2, + 269.5, + 271.8, + 271.9, + 271.8, + 272.8, + 273.5, + 273.9, + 273.9, + 274.0, + 274.2, + 274.5, + 275.1, + 275.7, + 276.4, + 276.9, + 277.2, + 277.7, + 278.5, + 279.1, + 279.6, + 280.3, + 281.1, + 281.4, + 281.0, + 275.6, + 271.9, + 266.2, + 263.0, + 260.8, + 257.4, + 257.4, + 256.9, + 258.9, + 259.5, + 269.7, + 252.8, + 258.9, + 261.3, + 262.3, + 264.2, + 272.0, + 273.8, + 268.0, + 274.9, + 276.0, + 277.5, + 279.2, + 280.6, + 282.1, + 283.3, + 284.4, + 284.4, + 284.1, + 285.0, + 285.1, + 284.0, + 282.6, + ], + [ + 271.1, + 272.8, + 268.2, + 266.4, + 264.6, + 262.8, + 263.0, + 261.6, + 261.5, + 261.0, + 260.6, + 260.5, + 260.5, + 260.2, + 259.6, + 258.6, + 256.7, + 257.0, + 257.7, + 256.5, + 256.8, + 257.7, + 258.7, + 253.6, + 252.2, + 251.6, + 251.0, + 251.5, + 252.9, + 251.5, + 249.3, + 244.7, + 242.7, + 242.7, + 240.6, + 242.4, + 248.5, + 248.8, + 265.8, + 269.7, + 270.9, + 270.4, + 269.2, + 271.3, + 272.1, + 272.4, + 272.5, + 272.7, + 273.0, + 273.5, + 274.1, + 274.7, + 275.4, + 275.9, + 276.3, + 276.8, + 277.6, + 278.3, + 279.1, + 279.8, + 280.5, + 280.6, + 280.3, + 266.9, + 265.4, + 261.0, + 259.6, + 258.2, + 256.0, + 255.5, + 255.1, + 257.3, + 257.7, + 258.2, + 257.1, + 260.2, + 258.5, + 260.7, + 260.2, + 260.6, + 264.8, + 267.5, + 274.2, + 275.5, + 276.9, + 278.5, + 279.6, + 280.7, + 281.8, + 282.9, + 282.9, + 283.2, + 284.6, + 283.9, + 281.7, + 280.7, + ], + [ + 272.9, + 278.0, + 266.3, + 264.1, + 264.1, + 261.7, + 260.5, + 260.0, + 258.1, + 256.2, + 255.4, + 254.6, + 254.8, + 255.0, + 254.6, + 256.0, + 254.5, + 254.0, + 255.1, + 256.0, + 256.1, + 256.0, + 257.0, + 257.5, + 256.7, + 254.9, + 252.3, + 253.8, + 251.8, + 248.4, + 245.3, + 242.5, + 240.9, + 240.6, + 239.5, + 239.7, + 245.4, + 244.2, + 260.7, + 268.4, + 269.1, + 268.7, + 240.9, + 267.9, + 269.2, + 270.1, + 270.8, + 271.1, + 272.0, + 272.8, + 273.2, + 274.0, + 274.8, + 275.5, + 276.1, + 276.5, + 277.1, + 277.9, + 278.7, + 279.4, + 280.0, + 279.9, + 267.4, + 266.1, + 263.0, + 261.9, + 258.8, + 256.7, + 255.0, + 255.2, + 254.8, + 254.5, + 256.1, + 255.6, + 256.0, + 268.1, + 253.7, + 258.8, + 255.8, + 255.8, + 261.0, + 266.4, + 273.4, + 275.0, + 276.6, + 278.6, + 279.7, + 280.2, + 281.0, + 281.7, + 282.3, + 282.5, + 283.6, + 282.1, + 269.5, + 272.3, + ], + [ + 279.5, + 278.6, + 276.0, + 264.8, + 272.6, + 272.6, + 259.8, + 257.3, + 254.5, + 251.7, + 250.6, + 250.5, + 250.5, + 250.1, + 250.8, + 252.4, + 253.3, + 251.8, + 252.0, + 252.7, + 252.6, + 252.2, + 252.9, + 254.6, + 255.4, + 255.2, + 255.5, + 251.6, + 249.8, + 244.5, + 243.6, + 239.9, + 238.9, + 237.6, + 238.1, + 239.3, + 240.5, + 250.0, + 255.8, + 266.6, + 266.7, + 267.2, + 245.0, + 243.8, + 265.6, + 267.4, + 268.2, + 268.7, + 270.2, + 271.3, + 272.1, + 272.9, + 273.6, + 274.7, + 275.5, + 276.4, + 277.0, + 277.7, + 278.3, + 278.6, + 278.1, + 269.4, + 266.8, + 263.5, + 263.3, + 262.7, + 260.3, + 257.5, + 254.8, + 253.6, + 252.4, + 251.2, + 251.1, + 251.3, + 262.3, + 268.0, + 249.2, + 256.3, + 252.8, + 250.1, + 256.0, + 271.0, + 273.2, + 275.2, + 277.1, + 278.8, + 279.9, + 280.3, + 280.9, + 281.6, + 282.6, + 282.9, + 283.4, + 282.9, + 280.9, + 270.4, + ], + [ + 279.7, + 277.9, + 275.4, + 273.1, + 257.7, + 272.2, + 269.6, + 250.4, + 251.0, + 248.2, + 245.6, + 246.1, + 246.9, + 246.0, + 247.1, + 248.1, + 248.7, + 248.0, + 248.3, + 248.7, + 248.9, + 248.9, + 248.9, + 249.6, + 250.4, + 250.2, + 247.7, + 246.4, + 244.8, + 244.6, + 241.7, + 239.0, + 238.3, + 236.0, + 233.8, + 235.6, + 236.8, + 240.2, + 250.0, + 258.6, + 257.6, + 257.7, + 256.0, + 242.0, + 257.2, + 264.2, + 264.5, + 265.3, + 266.7, + 268.3, + 269.3, + 271.1, + 272.2, + 272.0, + 273.1, + 274.7, + 275.8, + 276.5, + 277.1, + 277.2, + 266.4, + 266.3, + 262.2, + 262.1, + 260.9, + 261.2, + 259.3, + 256.6, + 254.0, + 251.6, + 249.0, + 248.4, + 251.3, + 250.0, + 253.9, + 263.0, + 245.9, + 252.7, + 254.5, + 246.1, + 268.6, + 271.9, + 273.7, + 275.7, + 277.1, + 278.2, + 279.5, + 280.0, + 280.7, + 281.3, + 281.8, + 281.8, + 281.7, + 282.2, + 281.6, + 265.3, + ], + [ + 279.8, + 277.7, + 255.1, + 258.3, + 255.5, + 270.2, + 266.8, + 246.2, + 247.0, + 245.2, + 242.0, + 243.4, + 243.2, + 243.2, + 244.7, + 245.2, + 244.9, + 243.2, + 243.9, + 244.2, + 244.7, + 244.7, + 244.2, + 244.3, + 245.4, + 243.4, + 240.8, + 239.1, + 238.9, + 238.3, + 236.7, + 236.1, + 234.8, + 233.7, + 232.2, + 231.0, + 229.6, + 237.8, + 243.5, + 244.1, + 242.9, + 240.6, + 250.7, + 250.6, + 239.3, + 252.1, + 250.7, + 250.8, + 256.8, + 263.2, + 267.1, + 268.8, + 269.6, + 250.4, + 257.9, + 258.2, + 259.1, + 262.1, + 261.1, + 259.5, + 259.4, + 261.1, + 259.4, + 257.8, + 257.6, + 257.5, + 255.0, + 254.4, + 251.8, + 248.8, + 245.5, + 249.3, + 250.1, + 249.3, + 250.3, + 252.9, + 246.2, + 249.3, + 255.3, + 261.8, + 268.1, + 271.6, + 273.4, + 274.7, + 275.4, + 276.2, + 278.5, + 279.4, + 280.3, + 280.9, + 281.3, + 281.2, + 281.0, + 281.4, + 281.4, + 280.7, + ], + [ + 278.7, + 277.8, + 264.9, + 264.0, + 254.2, + 267.9, + 249.4, + 242.7, + 244.0, + 242.0, + 240.6, + 241.8, + 241.4, + 241.1, + 241.8, + 241.7, + 240.0, + 238.2, + 238.6, + 239.1, + 239.4, + 239.5, + 239.9, + 240.3, + 240.7, + 238.7, + 236.5, + 235.7, + 234.5, + 233.6, + 232.0, + 229.2, + 230.4, + 227.2, + 226.2, + 229.9, + 232.8, + 235.1, + 237.5, + 239.0, + 239.8, + 240.1, + 240.2, + 241.7, + 245.4, + 245.5, + 246.0, + 244.4, + 255.6, + 260.1, + 262.5, + 266.9, + 267.4, + 250.2, + 259.4, + 258.6, + 258.3, + 257.9, + 256.5, + 254.3, + 254.3, + 256.5, + 256.6, + 254.8, + 251.6, + 252.2, + 251.5, + 249.3, + 246.8, + 245.4, + 244.0, + 242.9, + 248.6, + 248.7, + 248.8, + 248.2, + 249.5, + 252.3, + 246.9, + 258.9, + 267.3, + 270.3, + 272.2, + 248.1, + 253.5, + 273.7, + 276.3, + 277.8, + 279.1, + 279.8, + 281.3, + 281.1, + 280.7, + 280.8, + 280.4, + 279.8, + ], + [ + 276.4, + 276.5, + 275.5, + 275.0, + 250.6, + 252.0, + 256.1, + 241.6, + 241.3, + 241.2, + 239.5, + 239.0, + 238.2, + 239.2, + 238.2, + 237.3, + 236.0, + 234.8, + 234.5, + 234.7, + 235.0, + 234.5, + 234.9, + 236.3, + 236.9, + 235.5, + 234.5, + 233.6, + 231.4, + 229.8, + 228.6, + 228.8, + 227.9, + 227.4, + 232.3, + 234.1, + 234.0, + 232.7, + 232.6, + 234.1, + 233.7, + 233.4, + 235.4, + 237.4, + 237.3, + 240.4, + 241.4, + 242.2, + 251.4, + 252.7, + 255.0, + 261.0, + 261.3, + 245.5, + 252.5, + 252.0, + 250.5, + 251.8, + 252.1, + 252.0, + 251.6, + 252.7, + 252.8, + 248.1, + 247.8, + 248.3, + 247.9, + 245.9, + 244.3, + 243.9, + 243.3, + 243.7, + 243.5, + 242.5, + 247.1, + 246.3, + 247.3, + 246.6, + 245.6, + 252.1, + 266.7, + 269.9, + 271.9, + 249.9, + 252.0, + 243.4, + 273.0, + 270.7, + 270.1, + 271.5, + 278.6, + 255.6, + 257.5, + 277.9, + 277.3, + 276.7, + ], + [ + 274.7, + 274.7, + 274.0, + 273.4, + 240.9, + 248.1, + 247.8, + 246.6, + 243.0, + 240.4, + 237.7, + 244.3, + 241.4, + 241.3, + 232.1, + 232.7, + 233.6, + 232.8, + 231.5, + 231.0, + 230.8, + 230.1, + 230.8, + 232.7, + 234.3, + 232.2, + 231.2, + 230.9, + 229.7, + 228.1, + 227.9, + 227.8, + 227.4, + 228.2, + 229.6, + 229.1, + 229.2, + 230.8, + 231.3, + 231.9, + 231.8, + 231.5, + 231.8, + 234.2, + 236.6, + 238.2, + 240.1, + 241.3, + 240.1, + 238.5, + 239.6, + 243.8, + 255.4, + 242.5, + 250.6, + 249.5, + 245.8, + 245.5, + 244.6, + 244.6, + 244.8, + 244.0, + 244.3, + 245.4, + 245.0, + 244.5, + 243.8, + 243.1, + 242.6, + 242.6, + 241.2, + 242.2, + 244.2, + 243.3, + 240.8, + 245.5, + 246.3, + 245.5, + 243.6, + 247.8, + 266.3, + 269.5, + 247.1, + 253.6, + 249.5, + 247.9, + 247.0, + 250.8, + 266.3, + 261.0, + 263.1, + 270.9, + 274.4, + 275.1, + 274.2, + 273.9, + ], + [ + 272.2, + 272.7, + 272.0, + 271.5, + 270.3, + 270.5, + 242.2, + 243.8, + 247.3, + 263.6, + 258.9, + 246.2, + 240.0, + 240.6, + 239.1, + 235.5, + 236.5, + 238.7, + 229.9, + 227.0, + 227.7, + 226.9, + 226.5, + 229.8, + 232.2, + 230.6, + 230.0, + 229.6, + 229.5, + 228.5, + 227.8, + 227.6, + 227.5, + 228.6, + 229.5, + 229.6, + 229.0, + 228.5, + 228.8, + 229.7, + 230.1, + 230.4, + 231.9, + 238.6, + 238.6, + 238.3, + 238.2, + 237.5, + 239.9, + 244.9, + 246.5, + 245.2, + 248.3, + 255.0, + 234.9, + 238.8, + 242.9, + 248.6, + 248.2, + 250.8, + 250.9, + 247.1, + 248.3, + 241.2, + 241.0, + 240.8, + 240.6, + 240.3, + 239.9, + 244.3, + 242.7, + 240.5, + 241.9, + 241.4, + 237.7, + 237.7, + 244.5, + 244.5, + 247.1, + 252.4, + 266.4, + 269.8, + 249.9, + 253.8, + 245.5, + 238.3, + 238.4, + 242.4, + 247.6, + 249.2, + 245.4, + 255.7, + 264.5, + 264.9, + 266.0, + 269.0, + ], + [ + 269.0, + 269.4, + 269.4, + 269.8, + 271.1, + 270.8, + 268.2, + 266.5, + 265.6, + 264.8, + 261.3, + 246.1, + 235.9, + 237.4, + 238.2, + 235.0, + 235.9, + 237.4, + 237.4, + 228.2, + 237.7, + 235.9, + 223.6, + 225.8, + 227.0, + 228.1, + 227.4, + 226.5, + 226.1, + 226.3, + 226.9, + 228.1, + 227.3, + 226.7, + 227.5, + 235.2, + 235.5, + 235.8, + 235.5, + 235.1, + 235.1, + 235.6, + 236.4, + 238.5, + 239.4, + 239.6, + 240.6, + 240.2, + 239.7, + 241.4, + 243.6, + 241.9, + 241.5, + 251.9, + 244.3, + 240.9, + 241.6, + 242.6, + 242.3, + 243.6, + 243.1, + 242.9, + 245.9, + 238.0, + 238.6, + 238.3, + 238.3, + 240.3, + 241.8, + 244.4, + 243.1, + 238.5, + 239.1, + 239.7, + 239.8, + 242.1, + 247.8, + 245.0, + 251.2, + 264.4, + 268.3, + 269.9, + 232.1, + 250.7, + 244.8, + 242.9, + 241.2, + 237.9, + 239.4, + 246.4, + 245.0, + 249.2, + 248.3, + 247.0, + 251.1, + 263.6, + ], + [ + 267.8, + 268.5, + 268.3, + 269.1, + 269.9, + 268.6, + 266.7, + 265.9, + 264.3, + 262.9, + 255.5, + 237.1, + 230.7, + 236.7, + 238.3, + 235.6, + 234.2, + 235.0, + 234.6, + 237.0, + 240.1, + 234.9, + 234.3, + 231.2, + 225.1, + 227.0, + 228.0, + 227.5, + 227.0, + 226.7, + 228.7, + 237.0, + 234.4, + 235.6, + 236.8, + 236.5, + 235.3, + 235.5, + 236.0, + 236.8, + 237.3, + 237.7, + 237.5, + 237.0, + 237.2, + 237.3, + 237.9, + 238.6, + 238.3, + 238.3, + 239.5, + 239.2, + 238.4, + 240.3, + 238.0, + 237.5, + 238.4, + 239.1, + 239.2, + 239.7, + 240.3, + 241.0, + 242.1, + 241.8, + 241.6, + 240.7, + 240.6, + 241.3, + 242.6, + 243.3, + 242.9, + 242.6, + 236.4, + 238.7, + 241.0, + 250.4, + 251.0, + 253.4, + 262.5, + 268.7, + 268.8, + 231.8, + 243.0, + 241.4, + 239.4, + 238.4, + 234.8, + 235.0, + 237.2, + 241.8, + 242.2, + 247.0, + 246.7, + 247.1, + 248.8, + 257.9, + ], + [ + 266.3, + 268.4, + 267.8, + 267.9, + 267.3, + 266.4, + 265.5, + 264.8, + 262.8, + 262.1, + 259.4, + 245.0, + 231.9, + 235.1, + 237.9, + 236.7, + 235.8, + 235.2, + 235.2, + 236.9, + 236.9, + 234.1, + 232.7, + 231.0, + 231.8, + 233.7, + 234.0, + 225.9, + 227.3, + 236.4, + 237.8, + 239.8, + 236.5, + 236.2, + 236.3, + 236.5, + 236.3, + 237.0, + 237.4, + 237.8, + 238.1, + 237.6, + 236.9, + 236.0, + 235.8, + 236.1, + 236.8, + 237.4, + 237.5, + 237.2, + 237.3, + 237.1, + 237.1, + 237.9, + 237.9, + 238.0, + 238.1, + 237.9, + 237.4, + 237.1, + 237.5, + 238.2, + 238.8, + 239.8, + 240.2, + 240.6, + 240.6, + 240.6, + 240.6, + 241.0, + 242.2, + 243.4, + 236.8, + 239.2, + 240.8, + 241.8, + 249.6, + 254.1, + 240.6, + 239.5, + 239.6, + 236.0, + 232.0, + 229.8, + 229.2, + 228.7, + 227.9, + 229.8, + 234.4, + 238.3, + 241.0, + 250.1, + 247.6, + 247.8, + 249.5, + 254.9, + ], + [ + 266.7, + 266.9, + 265.6, + 265.9, + 265.2, + 264.6, + 264.6, + 262.5, + 250.8, + 241.4, + 240.2, + 239.9, + 236.9, + 238.9, + 240.3, + 238.4, + 236.9, + 236.5, + 234.4, + 233.6, + 234.0, + 234.2, + 233.2, + 233.4, + 235.1, + 233.9, + 231.8, + 232.0, + 232.3, + 234.2, + 233.9, + 235.5, + 235.1, + 235.7, + 236.7, + 237.4, + 237.2, + 237.1, + 237.0, + 236.9, + 237.4, + 237.5, + 237.4, + 236.3, + 235.6, + 235.6, + 235.7, + 235.6, + 235.7, + 235.7, + 235.7, + 235.6, + 235.7, + 235.7, + 235.5, + 235.5, + 235.5, + 235.4, + 235.2, + 235.1, + 235.5, + 236.5, + 237.0, + 237.1, + 237.2, + 237.3, + 237.3, + 237.2, + 237.6, + 239.0, + 241.1, + 246.3, + 232.2, + 234.5, + 234.2, + 231.2, + 231.1, + 233.8, + 236.8, + 238.4, + 236.6, + 236.1, + 234.8, + 235.3, + 233.6, + 233.4, + 235.0, + 237.4, + 239.0, + 239.4, + 240.2, + 241.5, + 242.2, + 244.1, + 247.4, + 257.0, + ], + [ + 247.2, + 247.8, + 250.1, + 246.0, + 246.8, + 247.5, + 245.6, + 243.0, + 238.0, + 235.9, + 237.4, + 238.7, + 238.8, + 239.1, + 240.2, + 236.9, + 235.1, + 236.5, + 234.2, + 231.7, + 231.0, + 231.8, + 232.2, + 232.4, + 235.5, + 235.5, + 236.2, + 235.3, + 234.9, + 235.0, + 235.4, + 236.6, + 236.9, + 237.6, + 238.0, + 238.2, + 238.0, + 238.2, + 238.3, + 238.1, + 238.4, + 238.2, + 238.0, + 237.4, + 236.9, + 237.3, + 236.8, + 236.2, + 236.2, + 236.3, + 236.1, + 235.6, + 235.2, + 234.8, + 234.3, + 233.9, + 233.5, + 233.2, + 232.8, + 232.6, + 233.1, + 234.0, + 234.2, + 234.4, + 234.2, + 234.0, + 233.9, + 233.9, + 233.8, + 234.4, + 235.7, + 238.1, + 235.7, + 235.8, + 240.5, + 224.1, + 225.3, + 227.2, + 232.5, + 240.2, + 239.2, + 238.6, + 239.2, + 239.4, + 235.6, + 235.0, + 233.9, + 235.2, + 235.9, + 236.5, + 239.9, + 238.9, + 238.5, + 239.7, + 242.2, + 245.1, + ], + [ + 237.3, + 237.3, + 238.2, + 237.7, + 238.5, + 239.3, + 238.2, + 236.9, + 235.5, + 236.1, + 237.4, + 237.9, + 237.4, + 237.0, + 237.1, + 236.0, + 235.7, + 236.5, + 236.7, + 236.4, + 235.3, + 234.4, + 234.8, + 234.9, + 235.4, + 235.8, + 236.0, + 235.9, + 236.1, + 236.4, + 236.9, + 237.4, + 237.9, + 238.3, + 238.7, + 238.8, + 238.8, + 238.6, + 238.6, + 238.6, + 238.5, + 238.4, + 238.4, + 238.2, + 238.0, + 238.1, + 237.6, + 236.9, + 236.6, + 236.4, + 236.1, + 235.7, + 235.5, + 235.2, + 234.8, + 234.4, + 234.1, + 233.5, + 232.7, + 231.8, + 231.6, + 232.0, + 232.2, + 232.9, + 233.0, + 232.7, + 232.3, + 232.0, + 231.9, + 232.1, + 232.4, + 233.0, + 233.1, + 234.2, + 236.6, + 232.8, + 232.1, + 232.7, + 233.6, + 236.6, + 235.3, + 235.1, + 236.3, + 234.4, + 234.0, + 234.8, + 235.8, + 237.1, + 238.1, + 239.5, + 240.2, + 240.6, + 239.4, + 238.3, + 238.6, + 238.1, + ], + [ + 231.9, + 231.5, + 231.7, + 231.8, + 232.1, + 232.1, + 231.8, + 231.6, + 231.6, + 232.3, + 233.4, + 233.9, + 233.7, + 233.5, + 233.6, + 234.0, + 235.2, + 236.4, + 237.1, + 237.5, + 237.2, + 237.2, + 237.4, + 237.6, + 237.9, + 238.2, + 238.5, + 238.7, + 238.8, + 238.8, + 238.9, + 239.0, + 239.1, + 239.1, + 239.1, + 239.0, + 238.9, + 239.0, + 238.5, + 238.3, + 238.1, + 238.1, + 238.2, + 238.2, + 238.1, + 238.0, + 237.9, + 237.8, + 237.6, + 237.3, + 237.1, + 237.2, + 237.1, + 236.9, + 236.6, + 236.4, + 236.2, + 235.6, + 235.0, + 234.7, + 234.3, + 233.6, + 233.3, + 233.8, + 235.0, + 235.9, + 235.3, + 234.1, + 234.0, + 234.1, + 233.5, + 233.1, + 233.1, + 233.7, + 234.0, + 232.8, + 232.8, + 233.1, + 233.4, + 233.7, + 233.0, + 232.6, + 232.8, + 233.1, + 233.6, + 234.3, + 234.8, + 235.2, + 235.4, + 235.3, + 235.0, + 234.6, + 234.0, + 233.5, + 233.5, + 232.7, + ], + [ + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + ], + ] + ] + + +def _make_cfa_file(filename): + n = netCDF4.Dataset(filename, "w", format="NETCDF4") + + n.Conventions = f"CF-{VN} CFA-0.6.2" + n.comment = ( + "A CFA-netCDF file with non-standarised aggregation instructions" + ) + + n.createDimension("time", 12) + level = n.createDimension("level", 1) + lat = n.createDimension("lat", 73) + lon = n.createDimension("lon", 144) + n.createDimension("f_time", 2) + n.createDimension("f_level", 1) + n.createDimension("f_lat", 1) + n.createDimension("f_lon", 1) + n.createDimension("i", 4) + n.createDimension("j", 2) + + lon = n.createVariable("lon", "f4", ("lon",)) + lon.standard_name = "longitude" + lon.units = "degrees_east" + + lat = n.createVariable("lat", "f4", ("lat",)) + lat.standard_name = "latitude" + lat.units = "degrees_north" + + time = n.createVariable("time", "f4", ("time",)) + time.standard_name = "time" + time.units = "days since 2000-01-01" + + level = n.createVariable("level", "f4", ("level",)) + + tas = n.createVariable("tas", "f4", ()) + tas.standard_name = "air_temperature" + tas.units = "K" + tas.aggregated_dimensions = "time level lat lon" + tas.aggregated_data = "location: aggregation_location file: aggregation_file format: aggregation_format address: aggregation_address tracking_id: aggregation_tracking_id" + + loc = n.createVariable("aggregation_location", "i4", ("i", "j")) + loc[0, :] = 6 + loc[1, 0] = level.size + loc[2, 0] = lat.size + loc[3, 0] = lon.size + + fil = n.createVariable( + "aggregation_file", str, ("f_time", "f_level", "f_lat", "f_lon") + ) + fil[0, 0, 0, 0] = "January-June.nc" + fil[1, 0, 0, 0] = "July-December.nc" + + add = n.createVariable( + "aggregation_address", str, ("f_time", "f_level", "f_lat", "f_lon") + ) + add[0, 0, 0, 0] = "tas0" + add[1, 0, 0, 0] = "tas1" + + fmt = n.createVariable("aggregation_format", str, ()) + fmt[()] = "nc" + + tid = n.createVariable( + "aggregation_tracking_id", str, ("f_time", "f_level", "f_lat", "f_lon") + ) + tid[0, 0, 0, 0] = "tracking_id0" + tid[1, 0, 0, 0] = "tracking_id1" + + n.close() + + return filename + + +broken_bounds_file = _make_broken_bounds_cdl("broken_bounds.cdl") + +regrid_file = _make_regrid_file("regrid.nc") + +cfa_file = _make_cfa_file("cfa.nc") + +if __name__ == "__main__": + print("Run date:", datetime.datetime.now()) + cf.environment() + print() + unittest.main(verbosity=2) diff --git a/cf/test/individual_tests.sh b/cf/test/individual_tests.sh index f02a941197..fea95ef58b 100755 --- a/cf/test/individual_tests.sh +++ b/cf/test/individual_tests.sh @@ -1,12 +1,14 @@ #!/bin/bash -file=create_test_files.py -echo "Running $file" -python $file -rc=$? -if [[ $rc != 0 ]]; then - exit $rc -fi +for file in create_test_files*.py +do + echo "Running $file" + python $file + rc=$? + if [[ $rc != 0 ]]; then + exit $rc + fi +done file=setup_create_field.py echo "Running $file" diff --git a/cf/test/run_tests.py b/cf/test/run_tests.py index c154fa0317..6026c4e123 100644 --- a/cf/test/run_tests.py +++ b/cf/test/run_tests.py @@ -64,6 +64,10 @@ def add_doctests(test_suite): test_loader().discover(test_dir, pattern="create_test_files.py") ) +testsuite_setup_0b = unittest.TestSuite() +testsuite_setup_0b.addTests( + test_loader().discover(test_dir, pattern="create_test_files_2.py") +) # Build the test suite from the tests found in the test files. testsuite_setup_1 = unittest.TestSuite() testsuite_setup_1.addTests( @@ -83,6 +87,7 @@ def add_doctests(test_suite): def run_test_suite_setup_0(verbosity=2): runner = unittest.TextTestRunner(verbosity=verbosity) runner.run(testsuite_setup_0) + runner.run(testsuite_setup_0b) def run_doctests_only(verbosity=2): diff --git a/cf/test/test_CellConnectivity.py b/cf/test/test_CellConnectivity.py new file mode 100644 index 0000000000..61515b9605 --- /dev/null +++ b/cf/test/test_CellConnectivity.py @@ -0,0 +1,104 @@ +import datetime +import faulthandler +import unittest + +faulthandler.enable() # to debug seg faults and timeouts + +import cf + +# Create testcell connectivity object +c = cf.CellConnectivity() +c.set_properties({"long_name": "neighbour faces for faces"}) +c.nc_set_variable("Mesh2_face_links") +data = cf.Data( + [ + [0, 1, 2, -99, -99], + [1, 0, -99, -99, -99], + [2, 0, -99, -99, -99], + ], + dtype="i4", +) +data.masked_values(-99, inplace=True) +c.set_data(data) +c.set_connectivity("edge") + + +class CellConnectivityTest(unittest.TestCase): + """Unit test for the CellConnectivity class.""" + + c = c + + def setUp(self): + """Preparations called immediately before each test method.""" + # Disable log messages to silence expected warnings + cf.log_level("DISABLE") + # Note: to enable all messages for given methods, lines or + # calls (those without a 'verbose' option to do the same) + # e.g. to debug them, wrap them (for methods, start-to-end + # internally) as follows: + # + # cf.LOG_LEVEL('DEBUG') + # < ... test code ... > + # cf.log_level('DISABLE') + + def test_CellConnectivity__repr__str__dump(self): + """Test all means of CellConnectivity inspection.""" + c = self.c + self.assertEqual( + repr(c), "" + ) + self.assertEqual(str(c), "connectivity:edge(3, 5) ") + self.assertEqual( + c.dump(display=False), + """Cell Connectivity: connectivity:edge + long_name = 'neighbour faces for faces' + Data(3, 5) = [[0, ..., --]]""", + ) + + def test_CellConnectivity_copy(self): + """Test the copy of CellConnectivity.""" + c = self.c + self.assertTrue(c.equals(c.copy())) + + def test_CellConnectivity_data(self): + """Test the data of CellConnectivity.""" + c = self.c + self.assertEqual(c.ndim, 1) + + def test_CellConnectivity_connectivity(self): + """Test the 'connectivity' methods of CellConnectivity.""" + c = self.c.copy() + self.assertTrue(c.has_connectivity()) + self.assertEqual(c.get_connectivity(), "edge") + self.assertEqual(c.del_connectivity(), "edge") + self.assertFalse(c.has_connectivity()) + self.assertIsNone(c.get_connectivity(None)) + self.assertIsNone(c.del_connectivity(None)) + + with self.assertRaises(ValueError): + c.get_connectivity() + + with self.assertRaises(ValueError): + c.del_connectivity() + + self.assertIsNone(c.set_connectivity("edge")) + self.assertTrue(c.has_connectivity()) + self.assertEqual(c.get_connectivity(), "edge") + + def test_CellConnectivity_transpose(self): + """Test the 'transpose' method of CellConnectivity.""" + c = self.c.copy() + d = c.transpose() + self.assertTrue(c.equals(d)) + self.assertIsNone(c.transpose(inplace=True)) + + for axes in ([1], [1, 0], [3]): + with self.assertRaises(ValueError): + c.transpose(axes) + + +if __name__ == "__main__": + print("Run date:", datetime.datetime.now()) + cf.environment() + print() + unittest.main(verbosity=2) diff --git a/cf/test/test_Data.py b/cf/test/test_Data.py index bf269a789c..2d439ba695 100644 --- a/cf/test/test_Data.py +++ b/cf/test/test_Data.py @@ -13,15 +13,7 @@ import dask.array as da import numpy as np - -SCIPY_AVAILABLE = False -try: - from scipy.ndimage import convolve1d - - SCIPY_AVAILABLE = True -# not 'except ImportError' as that can hide nested errors, catch anything: -except Exception: - pass # test with this dependency will then be skipped by unittest +from scipy.ndimage import convolve1d faulthandler.enable() # to debug seg faults and timeouts @@ -638,9 +630,6 @@ def test_Data_apply_masking(self): def test_Data_convolution_filter(self): """Test the `convolution_filter` Data method.""" # raise unittest.SkipTest("GSASL has no PLAIN support") - if not SCIPY_AVAILABLE: - raise unittest.SkipTest("SciPy must be installed for this test.") - d = cf.Data(self.ma, units="m", chunks=(2, 4, 5, 3)) window = [0.1, 0.15, 0.5, 0.15, 0.1] @@ -867,17 +856,6 @@ def test_Data_stats(self): }, ) - # NaN values aren't 'equal' to e/o, so check call works and that some - # representative values are as expected, in this case - s5 = cf.Data([[-2, -1, 0], [1, 2, 3]]).stats(all=True, weights=0) - - self.assertEqual(len(s5), 16) - self.assertEqual(s5["minimum"], -2) - self.assertEqual(s5["sum"], 0) - self.assertEqual(s5["sample_size"], 6) - self.assertTrue(np.isnan(s5["mean"])) - self.assertTrue(np.isnan(s5["variance"])) # needs all=True to show up - def test_Data__init__dtype_mask(self): """Test `__init__` for Data with `dtype` and `mask` keywords.""" for m in (1, 20, True): @@ -2868,7 +2846,6 @@ def test_Data_trigonometric_hyperbolic(self): (d.mask.array == c.mask).all(), "{}, {}, {}, {}".format(method, units, d.array, c), ) - # --- End: for # Also test masking behaviour: masking of invalid data occurs for # np.ma module by default but we don't want that so there is logic @@ -2898,25 +2875,22 @@ def test_Data_trigonometric_hyperbolic(self): self.assertTrue(np.isposinf(g[2])) self.assertIs(g[3], cf.masked) - # AT2 - # - # # Treat arctan2 separately (as is a class method & takes two inputs) - # for x in (1, -1): - # a1 = 0.9 * x * self.ma - # a2 = 0.5 * x * self.a - # # Transform data for 'a' into range more appropriate for inverse: - # a1 = np.sin(a1.data) - # a2 = np.cos(a2.data) - - # c = np.ma.arctan2(a1, a2) - # for units in (None, '', '1', 'radians', 'K'): - # d1 = cf.Data(a1, units=units) - # d2 = cf.Data(a2, units=units) - # e = cf.Data.arctan2(d1, d2) - # # Note: no inplace arg for arctan2 (operates on 2 arrays) - # self.assertEqual(d1.shape, c.shape) - # self.assertTrue((e.array == c).all()) - # self.assertTrue((d1.mask.array == c.mask).all()) + # Treat arctan2 separately (class method taking two inputs) + for x in (1, -1): + a1 = 0.9 * x * self.ma + a2 = 0.5 * x * self.a + # Transform data into range more appropriate for inverse + a1 = np.sin(a1.data) + a2 = np.cos(a2.data) + + c = np.ma.arctan2(a1, a2) + for units in (None, "", "1", "radians", "K"): + d1 = cf.Data(a1, units=units) + d2 = cf.Data(a2, units=units) + e = cf.Data.arctan2(d1, d2) + self.assertEqual(d1.shape, c.shape) + self.assertTrue((e.array == c).all()) + self.assertTrue((d1.mask.array == c.mask).all()) def test_Data_filled(self): """Test the `filled` Data method.""" @@ -4718,6 +4692,45 @@ def test_Data_todict(self): self.assertIn((key, 0), x) self.assertIn((key, 1), x) + def test_Data_masked_values(self): + """Test Data.masked_values.""" + array = np.array([[1, 1.1, 2, 1.1, 3]]) + d = cf.Data(array) + e = d.masked_values(1.1) + ea = e.array + a = np.ma.masked_values(array, 1.1, rtol=cf.rtol(), atol=cf.atol()) + self.assertTrue(np.isclose(ea, a).all()) + self.assertTrue((ea.mask == a.mask).all()) + self.assertIsNone(d.masked_values(1.1, inplace=True)) + self.assertTrue(d.equals(e)) + + array = np.array([[1, 1.1, 2, 1.1, 3]]) + d = cf.Data(array, mask_value=1.1) + da = e.array + self.assertTrue(np.isclose(da, a).all()) + self.assertTrue((da.mask == a.mask).all()) + + def test_Data_sparse_array(self): + """Test Data based on sparse arrays.""" + from scipy.sparse import csr_array + + indptr = np.array([0, 2, 3, 6]) + indices = np.array([0, 2, 2, 0, 1, 2]) + data = np.array([1, 2, 3, 4, 5, 6]) + s = csr_array((data, indices, indptr), shape=(3, 3)) + + d = cf.Data(s) + self.assertFalse((d.sparse_array != s).toarray().any()) + self.assertTrue((d.array == s.toarray()).all()) + + d = cf.Data(s, dtype=float) + self.assertEqual(d.sparse_array.dtype, float) + + # Can't mask sparse array during __init__ + mask = [[0, 0, 1], [0, 0, 0], [0, 0, 0]] + with self.assertRaises(ValueError): + cf.Data(s, mask=mask) + if __name__ == "__main__": print("Run date:", datetime.datetime.now()) diff --git a/cf/test/test_DomainTopology.py b/cf/test/test_DomainTopology.py new file mode 100644 index 0000000000..d7eb533759 --- /dev/null +++ b/cf/test/test_DomainTopology.py @@ -0,0 +1,98 @@ +import datetime +import faulthandler +import unittest + +faulthandler.enable() # to debug seg faults and timeouts + +import cf + +# Create test domain topology object +c = cf.DomainTopology() +c.set_properties({"long_name": "Maps every face to its corner nodes"}) +c.nc_set_variable("Mesh2_face_nodes") +data = cf.Data( + [[2, 3, 1, 0], [6, 7, 3, 2], [1, 3, 8, -99]], + dtype="i4", +) +data.masked_values(-99, inplace=True) +c.set_data(data) +c.set_cell("face") + + +class DomainTopologyTest(unittest.TestCase): + """Unit test for the DomainTopology class.""" + + d = c + + def setUp(self): + """Preparations called immediately before each test method.""" + # Disable log messages to silence expected warnings + cf.log_level("DISABLE") + # Note: to enable all messages for given methods, lines or + # calls (those without a 'verbose' option to do the same) + # e.g. to debug them, wrap them (for methods, start-to-end + # internally) as follows: + # + # cf.LOG_LEVEL('DEBUG') + # < ... test code ... > + # cf.log_level('DISABLE') + + def test_DomainTopology__repr__str__dump(self): + """Test all means of DomainTopology inspection.""" + d = self.d + self.assertEqual(repr(d), "") + self.assertEqual(str(d), "cell:face(3, 4) ") + self.assertEqual( + d.dump(display=False), + """Domain Topology: cell:face + long_name = 'Maps every face to its corner nodes' + Data(3, 4) = [[2, ..., --]]""", + ) + + def test_DomainTopology_copy(self): + """Test the copy of DomainTopology.""" + d = self.d + self.assertTrue(d.equals(d.copy())) + + def test_DomainTopology_data(self): + """Test the data of DomainTopology.""" + d = self.d + self.assertEqual(d.ndim, 1) + + def test_DomainTopology_cell(self): + """Test the 'cell' methods of DomainTopology.""" + d = self.d.copy() + self.assertTrue(d.has_cell()) + self.assertEqual(d.get_cell(), "face") + self.assertEqual(d.del_cell(), "face") + self.assertFalse(d.has_cell()) + self.assertIsNone(d.get_cell(None)) + self.assertIsNone(d.del_cell(None)) + + with self.assertRaises(ValueError): + d.get_cell() + + with self.assertRaises(ValueError): + d.set_cell("bad value") + + self.assertIsNone(d.set_cell("face")) + self.assertTrue(d.has_cell()) + self.assertEqual(d.get_cell(), "face") + + def test_DomainTopology_transpose(self): + """Test the 'transpose' method of DomainTopology.""" + d = self.d.copy() + e = d.transpose() + self.assertTrue(d.equals(e)) + self.assertIsNone(d.transpose(inplace=True)) + + for axes in ([1], [1, 0], [3]): + with self.assertRaises(ValueError): + d.transpose(axes) + + +if __name__ == "__main__": + print("Run date:", datetime.datetime.now()) + cf.environment() + print() + unittest.main(verbosity=2) diff --git a/cf/test/test_Field.py b/cf/test/test_Field.py index 945b119277..fb410ae9e8 100644 --- a/cf/test/test_Field.py +++ b/cf/test/test_Field.py @@ -9,21 +9,7 @@ import numpy import numpy as np - -SCIPY_AVAILABLE = False -try: - from scipy.ndimage import convolve1d - - # In some cases we don't need SciPy directly, since it is required by code - # here which uses 'convolve1d' under-the-hood. Without it installed, get: - # - # NameError: name 'convolve1d' is not defined. Did you - # mean: 'cf_convolve1d'? - - SCIPY_AVAILABLE = True -# not 'except ImportError' as that can hide nested errors, catch anything: -except Exception: - pass # test with this dependency will then be skipped by unittest +from scipy.ndimage import convolve1d faulthandler.enable() # to debug seg faults and timeouts @@ -78,6 +64,10 @@ class FieldTest(unittest.TestCase): os.path.dirname(os.path.abspath(__file__)), "DSG_timeSeriesProfile_indexed_contiguous.nc", ) + ugrid_global = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "ugrid_global_1.nc", + ) chunk_sizes = (100000, 300, 34, 17) original_chunksize = cf.chunksize() @@ -1696,9 +1686,6 @@ def test_Field_autocyclic(self): def test_Field_construct_key(self): self.f.construct_key("grid_longitude") - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_Field_convolution_filter(self): f = cf.read(self.filename1)[0] @@ -1731,9 +1718,6 @@ def test_Field_convolution_filter(self): (gx[:, 1] == [135, 180, 225, 270, 315, 360, 360, 360]).all() ) - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_Field_moving_window(self): weights = cf.Data([1, 2, 3, 10, 5, 6, 7, 8]) / 2 @@ -1874,9 +1858,6 @@ def test_Field_moving_window(self): self.assertEqual(len(g.cell_methods()), len(f.cell_methods()) + 1) - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_Field_derivative(self): f = cf.example_field(0) f[...] = np.arange(9)[1:] * 45 @@ -2336,9 +2317,6 @@ def test_Field_percentile(self): # TODO: add loop to check get same shape and close enough data # for every possible axis combo (see also test_Data_percentile). - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_Field_grad_xy(self): f = cf.example_field(0) @@ -2424,9 +2402,6 @@ def test_Field_grad_xy(self): y.dimension_coordinate("X").standard_name, "longitude" ) - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_Field_laplacian_xy(self): f = cf.example_field(0) @@ -2642,6 +2617,18 @@ def test_Field_auxiliary_to_dimension_to_auxiliary(self): with self.assertRaises(ValueError): f.auxiliary_to_dimension("latitude") + def test_Field_subspace_ugrid(self): + f = cf.read(self.ugrid_global)[0] + + with self.assertRaises(ValueError): + # Can't specify 2 conditions for 1 axis + g = f.subspace(X=cf.wi(40, 70), Y=cf.wi(-20, 30)) + + g = f.subspace(X=cf.wi(40, 70)) + g = g.subspace(Y=cf.wi(-20, 30)) + self.assertTrue(g.aux("X").data.range() < 30) + self.assertTrue(g.aux("Y").data.range() < 50) + if __name__ == "__main__": print("Run date:", datetime.datetime.now()) diff --git a/cf/test/test_Maths.py b/cf/test/test_Maths.py index 342b1ec0b6..0900692aeb 100644 --- a/cf/test/test_Maths.py +++ b/cf/test/test_Maths.py @@ -2,30 +2,12 @@ import faulthandler import unittest - -SCIPY_AVAILABLE = False -try: - # We don't need SciPy directly in this test, it is only required by code - # here which uses 'convolve1d' under-the-hood. Without it installed, get: - # - # NameError: name 'convolve1d' is not defined. Did you - # mean: 'cf_convolve1d'? - import scipy - - SCIPY_AVAILABLE = True -# not 'except ImportError' as that can hide nested errors, catch anything: -except Exception: - pass # test with this dependency will then be skipped by unittest - faulthandler.enable() # to debug seg faults and timeouts import cf class MathTest(unittest.TestCase): - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_curl_xy(self): f = cf.example_field(0) @@ -115,9 +97,6 @@ def test_curl_xy(self): c.dimension_coordinate("X").standard_name, "longitude" ) - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_div_xy(self): f = cf.example_field(0) @@ -206,9 +185,6 @@ def test_div_xy(self): d.dimension_coordinate("X").standard_name, "longitude" ) - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_differential_operators(self): f = cf.example_field(0) diff --git a/cf/test/test_RegridOperator.py b/cf/test/test_RegridOperator.py index 2c1542240f..b20cb0cfcf 100644 --- a/cf/test/test_RegridOperator.py +++ b/cf/test/test_RegridOperator.py @@ -29,6 +29,8 @@ def test_RegridOperator_attributes(self): self.assertEqual(self.r.weights.ndim, 2) self.assertIsNone(self.r.row) self.assertIsNone(self.r.col) + self.assertIsNone(self.r.weights_file) + self.assertEqual(self.r.src_mesh_location, "") def test_RegridOperator_copy(self): self.assertIsInstance(self.r.copy(), self.r.__class__) diff --git a/cf/test/test_UGRID.py b/cf/test/test_UGRID.py new file mode 100644 index 0000000000..7d80ba3166 --- /dev/null +++ b/cf/test/test_UGRID.py @@ -0,0 +1,189 @@ +import atexit +import datetime +import faulthandler +import os +import tempfile +import unittest + +import numpy as np + +faulthandler.enable() # to debug seg faults and timeouts + +import cf + +warnings = False + +# Set up temporary files +n_tmpfiles = 1 +tmpfiles = [ + tempfile.mkstemp("_test_read_write.nc", dir=os.getcwd())[1] + for i in range(n_tmpfiles) +] +[tmpfile1] = tmpfiles + + +def _remove_tmpfiles(): + """Remove temporary files created during tests.""" + for f in tmpfiles: + try: + os.remove(f) + except OSError: + pass + + +atexit.register(_remove_tmpfiles) + + +class UGRIDTest(unittest.TestCase): + """Test UGRID field constructs.""" + + filename1 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "ugrid_1.nc" + ) + + filename2 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "ugrid_2.nc" + ) + + def setUp(self): + """Preparations called immediately before each test method.""" + # Disable log messages to silence expected warnings + cf.LOG_LEVEL("DISABLE") + # Note: to enable all messages for given methods, lines or + # calls (those without a 'verbose' option to do the same) + # e.g. to debug them, wrap them (for methods, start-to-end + # internally) as follows: cf.LOG_LEVEL('DEBUG') + # + # < ... test code ... > + # cf.log_level('DISABLE') + + def test_UGRID_read(self): + """Test reading of UGRID files.""" + f1 = cf.read(self.filename1) + + self.assertEqual(len(f1), 3) + for g in f1: + self.assertEqual(len(g.domain_topologies()), 1) + self.assertEqual(len(g.auxiliary_coordinates()), 2) + self.assertEqual(len(g.dimension_coordinates()), 1) + + for aux in g.auxiliary_coordinates().values(): + self.assertTrue(aux.has_data()) + + if g.domain_topology().get_cell() == "face": + self.assertEqual(len(g.cell_connectivities()), 1) + self.assertEqual( + g.cell_connectivity().get_connectivity(), "edge" + ) + + # Check that all fields have the same mesh id + mesh_ids1 = set(g.get_mesh_id() for g in f1) + self.assertEqual(len(mesh_ids1), 1) + + f2 = cf.read(self.filename2) + self.assertEqual(len(f2), 3) + for g in f2: + self.assertEqual(len(g.domain_topologies()), 1) + self.assertEqual(len(g.auxiliary_coordinates()), 2) + self.assertEqual(len(g.dimension_coordinates()), 1) + + cell = g.domain_topology().get_cell() + if cell in ("edge", "face"): + for aux in g.auxiliary_coordinates().values(): + self.assertFalse(aux.has_data()) + + if cell == "face": + self.assertEqual(len(g.cell_connectivities()), 1) + self.assertEqual( + g.cell_connectivity().get_connectivity(), "edge" + ) + + # Check that all fields have the same mesh id + mesh_ids2 = set(g.get_mesh_id() for g in f2) + self.assertEqual(len(mesh_ids2), 1) + + # Check that the different files have different mesh ids + self.assertNotEqual(mesh_ids1, mesh_ids2) + + def test_UGRID_data(self): + """Test reading of UGRID data.""" + node1, face1, edge1 = cf.read(self.filename1) + node2, face2, edge2 = cf.read(self.filename2) + + # Domain topology arrays + domain_topology1 = face1.domain_topology() + self.assertTrue( + ( + domain_topology1.array + == np.array([[2, 3, 1, 0], [4, 5, 3, 2], [1, 3, 6, -99]]) + ).all() + ) + self.assertTrue(domain_topology1.equals(face2.domain_topology())) + + domain_topology1 = edge1.domain_topology() + self.assertTrue( + ( + domain_topology1.array + == np.array( + [ + [1, 6], + [3, 6], + [3, 1], + [0, 1], + [2, 0], + [2, 3], + [2, 4], + [5, 4], + [3, 5], + ] + ) + ).all() + ) + self.assertTrue(domain_topology1.equals(edge2.domain_topology())) + + # Cell connectivity arrays + cell_connectivity1 = face1.cell_connectivity() + self.assertTrue( + ( + cell_connectivity1.array + == np.array( + [ + [0, 1, 2, -99, -99], + [1, 0, -99, -99, -99], + [2, 0, -99, -99, -99], + ] + ) + ).all() + ) + self.assertTrue(cell_connectivity1.equals(face2.cell_connectivity())) + + def test_read_UGRID_domain(self): + """Test reading of UGRID files into domains.""" + d1 = cf.read(self.filename1, domain=True) + + self.assertEqual(len(d1), 3) + for g in d1: + self.assertIsInstance(g, cf.Domain) + self.assertEqual(len(g.domain_topologies()), 1) + self.assertEqual(len(g.auxiliary_coordinates()), 2) + self.assertEqual(len(g.dimension_coordinates()), 0) + + for aux in g.auxiliary_coordinates().values(): + self.assertTrue(aux.has_data()) + + if g.domain_topology().get_cell() == "face": + self.assertEqual(len(g.cell_connectivities()), 1) + self.assertEqual( + g.cell_connectivity().get_connectivity(), "edge" + ) + + # Check that all domains have the same mesh id + mesh_ids1 = set(g.get_mesh_id() for g in d1) + self.assertEqual(len(mesh_ids1), 1) + + +if __name__ == "__main__": + print("Run date:", datetime.datetime.now()) + cf.environment() + print("") + unittest.main(verbosity=2) diff --git a/cf/test/test_aggregate.py b/cf/test/test_aggregate.py index cd95ad72eb..784e9e5fe1 100644 --- a/cf/test/test_aggregate.py +++ b/cf/test/test_aggregate.py @@ -57,7 +57,7 @@ def test_basic_aggregate(self): g.equals(g0, verbose=2), "g != itself after aggregation" ) - self.assertTrue(h[0].equals(f, verbose=-1), "h[0] != f") + self.assertTrue(h[0].equals(f, verbose=2), "h[0] != f") with warnings.catch_warnings(): warnings.simplefilter("ignore", category=FutureWarning) @@ -610,6 +610,45 @@ def test_climatology_cells(self): condition = cells["T"][0]["cellsize"] self.assertTrue(condition.equals(cf.Y())) + def test_aggregate_ugrid(self): + """Test ugrid aggregation""" + f = cf.example_field(8) + + # Test that aggregation over a non-ugrid axis (time, in this + # case) works. + g = f.copy() + t = g.dim("T") + cf.bounds_combination_mode("OR") + t += 72000 + a = cf.aggregate([f, g]) + self.assertEqual(len(a), 1) + a = a[0] + self.assertEqual(len(a.domain_topologies()), 1) + self.assertEqual(len(a.cell_connectivities()), 1) + + # Test that aggregation over a non-ugrid axis doesn't work + # when the domain topology constructs are different + h = g.copy() + d = h.domain_topology() + d = d.data + d += 1 + self.assertEqual(len(cf.aggregate([f, h])), 2) + + # Test that aggregation over a non-ugrid axis doesn't work + # when the cell connnectivty constructs are different + h = g.copy() + c = h.cell_connectivity() + d = c.data + d += 1 + self.assertEqual(len(cf.aggregate([f, h])), 2) + + # Test that aggregation over a ugrid axis doesn't work + g = f.copy() + x = g.aux("X") + d = x.data + d += 0.1 + self.assertEqual(len(cf.aggregate([f, g])), 2) + if __name__ == "__main__": print("Run date:", datetime.datetime.now()) diff --git a/cf/test/test_collapse.py b/cf/test/test_collapse.py index 6faada8cd5..0525aecddb 100644 --- a/cf/test/test_collapse.py +++ b/cf/test/test_collapse.py @@ -665,16 +665,17 @@ def test_Field_collapse_GROUPS(self): def test_Field_collapse_sum(self): f = cf.example_field(0) - w = f.weights("area", measure=True) + w = f.weights("area", measure=True).persist() a = f.array wa = w.array ws = a * wa + ws_sum = ws.sum() g = f.collapse("area: sum") self.assertTrue((g.array == a.sum()).all()) g = f.collapse("area: sum", weights=w) - self.assertTrue((g.array == ws.sum()).all()) + self.assertTrue((g.array == ws_sum).all()) self.assertEqual(g.Units, cf.Units("1")) g = f.collapse("area: sum", weights=w, scale=1) @@ -682,7 +683,7 @@ def test_Field_collapse_sum(self): self.assertEqual(g.Units, cf.Units("1")) g = f.collapse("area: sum", weights=w) - self.assertTrue((g.array == ws.sum()).all()) + self.assertTrue((g.array == ws_sum).all()) self.assertEqual(g.Units, cf.Units("1")) # Can't set measure=True for 'sum' collapses @@ -691,13 +692,12 @@ def test_Field_collapse_sum(self): def test_Field_collapse_integral(self): f = cf.example_field(0) - w = f.weights("area", measure=True) + w = f.weights("area", measure=True).persist() a = f.array wa = w.array - ws = a * wa g = f.collapse("area: integral", weights=w, measure=True) - self.assertTrue((g.array == ws.sum()).all()) + self.assertTrue((g.array == (a * wa).sum()).all()) self.assertEqual(g.Units, cf.Units("m2")) # Must set the 'weights' parameter for 'integral' collapses @@ -714,7 +714,7 @@ def test_Field_collapse_integral(self): def test_Field_collapse_sum_weights(self): f = cf.example_field(0) - w = f.weights("area", measure=True) + w = f.weights("area", measure=True).persist() wa = w.array g = f.collapse("area: sum_of_weights") @@ -735,25 +735,45 @@ def test_Field_collapse_sum_weights(self): def test_Field_collapse_sum_weights2(self): f = cf.example_field(0) - w = f.weights("area", measure=True) + w = f.weights("area", measure=True).persist() wa = w.array**2 + wa_sum = wa.sum() g = f.collapse("area: sum_of_weights2") self.assertTrue((g.array == 40).all()) self.assertEqual(g.Units, cf.Units()) g = f.collapse("area: sum_of_weights2", weights=w) - self.assertTrue((g.array == wa.sum()).all()) + self.assertTrue((g.array == wa_sum).all()) self.assertEqual(g.Units, cf.Units("1")) g = f.collapse("area: sum_of_weights2", weights=w, measure=True) - self.assertTrue((g.array == wa.sum()).all()) + self.assertTrue((g.array == wa_sum).all()) self.assertEqual(g.Units, cf.Units("m4")) g = f.collapse("area: sum_of_weights2", weights=w, scale=1) self.assertTrue((g.array == (wa / wa.max()).sum()).all()) self.assertEqual(g.Units, cf.Units("1")) + def test_Field_collapse_non_positive_weights(self): + f = cf.example_field(0) + w = f.weights("area").persist() + + for method in ( + "mean", + "sum", + "root_mean_square", + "variance", + "sum_of_weights", + ): + for x in (0, -3.14): + w[0, 0] = x + g = f.collapse(axes="area", method=method, weights=w) + with self.assertRaises(ValueError): + # The check for non-positive weights occurs at + # compute time + g.array + if __name__ == "__main__": print("Run date:", datetime.datetime.now()) diff --git a/cf/test/test_external.py b/cf/test/test_external.py index dabd4d01a7..8d7489f78e 100644 --- a/cf/test/test_external.py +++ b/cf/test/test_external.py @@ -173,7 +173,7 @@ def test_EXTERNAL_AGGREGATE(self): # cell measure. f_lon_thirds = [f[:, :3], f[:, 3:6], f[:, 6:]] - g = cf.aggregate(f_lon_thirds) + g = cf.aggregate(f_lon_thirds, verbose=2) self.assertEqual(len(g), 1) diff --git a/cf/test/test_gathering.py b/cf/test/test_gathering.py index a26350dd18..bafa8efeda 100644 --- a/cf/test/test_gathering.py +++ b/cf/test/test_gathering.py @@ -31,7 +31,7 @@ def _remove_tmpfiles(): atexit.register(_remove_tmpfiles) -class DSGTest(unittest.TestCase): +class GatheringTest(unittest.TestCase): gathered = os.path.join( os.path.dirname(os.path.abspath(__file__)), "gathered.nc" ) diff --git a/cf/test/test_read_write.py b/cf/test/test_read_write.py index e4da245eab..0eefa1b2ac 100644 --- a/cf/test/test_read_write.py +++ b/cf/test/test_read_write.py @@ -336,6 +336,12 @@ def test_write_netcdf_mode(self): if fmt == "NETCDF4_CLASSIC" and ex_field_n in (6, 7): continue + print( + "TODOUGRID: excluding example fields 8, 9, 10 until writing UGRID is enabled" + ) + if ex_field_n in (8, 9, 10): + continue + cf.write(ex_field, tmpfile, fmt=fmt, mode="a") f = cf.read(tmpfile) @@ -404,7 +410,10 @@ def test_write_netcdf_mode(self): # Now do the same test, but appending all of the example fields in # one operation rather than one at a time, to check that it works. cf.write(g, tmpfile, fmt=fmt, mode="w") # 1. overwrite to wipe - append_ex_fields = cf.example_fields() + print( + "TODOUGRID: excluding example fields 8, 9, 10 until writing UGRID is enabled" + ) + append_ex_fields = cf.example_fields(0, 1, 2, 3, 4, 5, 6, 7) del append_ex_fields[1] # note: can remove after Issue #141 closed if fmt in "NETCDF4_CLASSIC": # Remove n=6 and =7 for reasons as given above (del => minus 1) @@ -663,6 +672,7 @@ def test_read_CDL(self): geometry_1_file = os.path.join( os.path.dirname(os.path.abspath(__file__)), "geometry_1.nc" ) + subprocess.run( " ".join(["ncdump", "-h", geometry_1_file, ">", tmpfileh2]), shell=True, diff --git a/cf/test/test_regrid_mesh.py b/cf/test/test_regrid_mesh.py new file mode 100644 index 0000000000..3095640135 --- /dev/null +++ b/cf/test/test_regrid_mesh.py @@ -0,0 +1,294 @@ +import datetime +import faulthandler +import os +import unittest + +faulthandler.enable() # to debug seg faults and timeouts + +import numpy as np + +import cf + +# ESMF renamed its Python module to `esmpy` at ESMF version 8.4.0. Allow +# either for now for backwards compatibility. +esmpy_imported = False +try: + import esmpy + + esmpy_imported = True +except ImportError: + try: + # Take the new name to use in preference to the old one. + import ESMF as esmpy + + esmpy_imported = True + except ImportError: + pass + +all_methods = ( + "linear", + "conservative", + "conservative_2nd", + "nearest_dtos", + "nearest_stod", + "patch", +) + + +# Set numerical comparison tolerances +atol = 2e-12 +rtol = 0 + +meshloc = { + "face": esmpy.MeshLoc.ELEMENT, + "node": esmpy.MeshLoc.NODE, +} + + +def esmpy_regrid(coord_sys, method, src, dst, **kwargs): + """Helper function that regrids one dimension of Field data using + pure esmpy. + + Used to verify `cf.Field.regridc` + + :Returns: + + Regridded numpy masked array. + + """ + esmpy_regrid = cf.regrid.regrid( + coord_sys, + src, + dst, + method, + return_esmpy_regrid_operator=True, + **kwargs + ) + + src_meshloc = None + dst_meshloc = None + + domain_topology = src.domain_topology(default=None) + if domain_topology is not None: + src_meshloc = meshloc[domain_topology.get_cell()] + + domain_topology = dst.domain_topology(default=None) + if domain_topology is not None: + dst_meshloc = meshloc[domain_topology.get_cell()] + + src_field = esmpy.Field( + esmpy_regrid.srcfield.grid, meshloc=src_meshloc, name="src" + ) + dst_field = esmpy.Field( + esmpy_regrid.dstfield.grid, meshloc=dst_meshloc, name="dst" + ) + + fill_value = 1e20 + array = np.squeeze(src.array) + if array.shape != src_field.data.shape: + array = array.transpose() + + src_field.data[...] = np.ma.MaskedArray(array, copy=False).filled( + fill_value + ) + dst_field.data[...] = fill_value + + esmpy_regrid(src_field, dst_field, zero_region=esmpy.Region.SELECT) + + out = dst_field.data + + return np.ma.MaskedArray(out.copy(), mask=(out == fill_value)) + + +class RegridMeshTest(unittest.TestCase): + # Get the test source and destination fields + src_mesh_file = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "ugrid_global_1.nc" + ) + dst_mesh_file = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "ugrid_global_2.nc" + ) + grid_file = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "regrid.nc" + ) + + src_mesh = cf.read(src_mesh_file)[0] + dst_mesh = cf.read(dst_mesh_file)[0] + grid = cf.read(grid_file)[0] + + def setUp(self): + """Preparations called immediately before each test method.""" + # Disable log messages to silence expected warnings + cf.log_level("DISABLE") + # Note: to enable all messages for given methods, lines or calls (those + # without a 'verbose' option to do the same) e.g. to debug them, wrap + # them (for methods, start-to-end internally) as follows: + # cfdm.log_level('DEBUG') + # < ... test code ... > + # cfdm.log_level('DISABLE') + + @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") + def test_Field_regrid_mesh_to_mesh(self): + self.assertFalse(cf.regrid_logging()) + + dst = self.dst_mesh.copy() + src = self.src_mesh.copy() + + # Mask some destination grid points + dst[0, 2:35] = cf.masked + + coord_sys = "spherical" + + for src_masked in (False, True): + if src_masked: + src = src.copy() + src[100:200] = cf.masked + + # Loop over whether or not to use the destination grid + # masked points + for use_dst_mask in (False, True): + for method in all_methods: + x = src.regrids( + dst, method=method, use_dst_mask=use_dst_mask + ) + a = x.array + + y = esmpy_regrid( + coord_sys, + method, + src, + dst, + use_dst_mask=use_dst_mask, + ) + + self.assertTrue(np.allclose(y, a, atol=atol, rtol=rtol)) + + if isinstance(a, np.ma.MaskedArray): + self.assertTrue((y.mask == a.mask).all()) + else: + self.assertFalse(y.mask.any()) + + @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") + def test_Field_regrid_mesh_to_grid(self): + self.assertFalse(cf.regrid_logging()) + + dst = self.grid.copy() + src = self.src_mesh.copy() + + # Mask some destination grid points + dst[0, 30, 2:35] = cf.masked + + coord_sys = "spherical" + + for src_masked in (False, True): + if src_masked: + src = src.copy() + src[100:200] = cf.masked + + # Loop over whether or not to use the destination grid + # masked points + for use_dst_mask in (False, True): + for method in all_methods: + if method == "nearest_dtos": + continue + + x = src.regrids( + dst, method=method, use_dst_mask=use_dst_mask + ) + a = x.array + a = a.transpose() + + y = esmpy_regrid( + coord_sys, + method, + src, + dst, + use_dst_mask=use_dst_mask, + ) + + self.assertTrue(np.allclose(y, a, atol=atol, rtol=rtol)) + + if isinstance(a, np.ma.MaskedArray): + self.assertTrue((y.mask == a.mask).all()) + else: + self.assertFalse(y.mask.any()) + + # nearest_dtos doesn't work at the moment + with self.assertRaises(ValueError): + src.regrids(dst, method="nearest_dtos") + + @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") + def test_Field_regrid_grid_to_mesh(self): + self.assertFalse(cf.regrid_logging()) + + src = self.grid.copy() + dst = self.src_mesh.copy() + + # Mask some destination grid points + dst[100:300] = cf.masked + + coord_sys = "spherical" + + for src_masked in (False, True): + if src_masked: + src = src.copy() + src[0, 30:34, 10:80] = cf.masked + + # Loop over whether or not to use the destination grid + # masked points + for use_dst_mask in (False, True): + for method in all_methods: + if method == "nearest_dtos": + continue + + x = src.regrids( + dst, method=method, use_dst_mask=use_dst_mask + ) + a = x.array + a = np.squeeze(a) + + y = esmpy_regrid( + coord_sys, + method, + src, + dst, + use_dst_mask=use_dst_mask, + ) + + self.assertTrue(np.allclose(y, a, atol=atol, rtol=rtol)) + + if isinstance(a, np.ma.MaskedArray): + self.assertTrue((y.mask == a.mask).all()) + else: + self.assertFalse(y.mask.any()) + + # nearest_dtos doesn't work at the moment + with self.assertRaises(ValueError): + src.regrids(dst, method="nearest_dtos") + + @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") + def test_Field_regrid_mesh_cartesian(self): + self.assertFalse(cf.regrid_logging()) + + # Cartesian regridding involving meshes is not currently + # supported + src = self.src_mesh + dst = self.dst_mesh + with self.assertRaises(ValueError): + src.regridc(dst, method="linear") + + dst = self.grid + with self.assertRaises(ValueError): + src.regridc(dst, method="linear") + + src = self.grid + dst = self.dst_mesh + with self.assertRaises(ValueError): + src.regridc(dst, method="linear") + + +if __name__ == "__main__": + print("Run date:", datetime.datetime.now()) + cf.environment() + print("") + unittest.main(verbosity=2) diff --git a/cf/test/test_weights.py b/cf/test/test_weights.py new file mode 100644 index 0000000000..77b300fdb5 --- /dev/null +++ b/cf/test/test_weights.py @@ -0,0 +1,299 @@ +import datetime +import unittest + +import numpy as np + +import cf + +# A radius greater than 1. Used since weights based on the unit +# spehere and non-speheres are tested separately. +r = 2 +radius = cf.Data(r, "m") + +# -------------------------------------------------------------------- +# Spherical polygon geometry with duplicated first/last node. +# +# The cells have areas pi/2 and pi +# The cells have line lengths 3pi/2 and 5pi/2 +# -------------------------------------------------------------------- +gps = cf.example_field(6) +gps.del_construct("auxiliarycoordinate3") +gps.del_construct("grid_mapping_name:latitude_longitude") + +lon = cf.AuxiliaryCoordinate() +lon.standard_name = "longitude" +bounds = cf.Data( + [[315, 45, 45, 315, 999, 999, 999], [90, 90, 0, 45, 45, 135, 90]], + "degrees_east", + mask_value=999, +).reshape(2, 1, 7) +lon.set_bounds(cf.Bounds(data=bounds)) +lon.set_geometry("polygon") + +lat = cf.AuxiliaryCoordinate() +lat.standard_name = "latitude" +bounds = cf.Data( + [[0, 0, 90, 0, 999, 999, 999], [0, 90, 0, 0, -90, 0, 0]], + "degrees_north", + mask_value=999, +).reshape(2, 1, 7) +lat.set_bounds(cf.Bounds(data=bounds)) +lat.set_geometry("polygon") + +gps.del_construct("longitude") +gps.del_construct("latitude") +gps.set_construct(lon, axes="domainaxis0", copy=False) +gps.set_construct(lat, axes="domainaxis0", copy=False) + +# -------------------------------------------------------------------- +# Plane polygon geometry with interior ring and without duplicated +# first/last node +# +# The cells have areas 3 and 8 +# The cells have line lengths 9 and 13 +# -------------------------------------------------------------------- +gppi = gps.copy() +lon = gppi.auxiliary_coordinate("X") +lat = gppi.auxiliary_coordinate("Y") + +lon.override_units("m", inplace=True) +lon.standard_name = "projection_x_coordinate" +bounds = cf.Data( + [ + [ + [2, 2, 0, 0, 999, 999, 999, 999], + [0.5, 1.5, 1.5, 0.5, 999, 999, 999, 999], + ], + [ + [2, 2, 0, 0, 1, 1, 3, 3], + [999, 999, 999, 999, 999, 999, 999, 999], + ], + ], + "m", + mask_value=999, +).reshape(2, 2, 8) +lon.set_bounds(cf.Bounds(data=bounds)) +lon.set_interior_ring(cf.InteriorRing(data=[[0, 1], [0, 0]])) + +lat.override_units("m", inplace=True) +lat.standard_name = "projection_y_coordinate" +bounds = cf.Data( + [ + [ + [-1, 1, 1, -1, 999, 999, 999, 999], + [0.5, 0.5, -0.5, -0.5, 999, 999, 999, 999], + ], + [ + [-1, 1, 1, -1, -1, -3, -3, -1], + [999, 999, 999, 999, 999, 999, 999, 999], + ], + ], + "m", + mask_value=999, +).reshape(2, 2, 8) +lat.set_bounds(cf.Bounds(data=bounds)) +lat.set_interior_ring(cf.InteriorRing(data=[[0, 1], [0, 0]])) + + +class WeightsTest(unittest.TestCase): + def test_weights_polygon_area_geometry(self): + # Spherical polygon geometry weights with duplicated first/last + # node + f = gps.copy() + lon = f.auxiliary_coordinate("X") + lat = f.auxiliary_coordinate("Y") + + # Surface area of unit sphere + sphere_area = 4 * np.pi + correct_weights = np.array([sphere_area / 8, sphere_area / 4]) + + # Spherical polygon geometry weights with duplicated first/last + # node + w = gps.weights("X", great_circle=True) + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("1")) + + w = gps.weights("area", great_circle=True, measure=True, radius=radius) + self.assertTrue((w.array == (r**2) * correct_weights).all()) + self.assertEqual(w.Units, cf.Units("m2")) + + # Spherical polygon geometry weights without duplicated + # first/last node + bounds = cf.Data( + [[315, 45, 45, 999, 999, 999], [90, 90, 0, 45, 45, 135]], + "degrees_east", + mask_value=999, + ).reshape(2, 1, 6) + lon.set_bounds(cf.Bounds(data=bounds)) + + bounds = cf.Data( + [[0, 0, 90, 999, 999, 999], [0, 90, 0, 0, -90, 0]], + "degrees_north", + mask_value=999, + ).reshape(2, 1, 6) + lat.set_bounds(cf.Bounds(data=bounds)) + + w = f.weights("X", great_circle=True) + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("1")) + + w = f.weights("area", great_circle=True, measure=True, radius=radius) + self.assertTrue((w.array == (r**2) * correct_weights).all()) + self.assertEqual(w.Units, cf.Units("m2")) + + # Plane polygon geometry with no duplicated first/last nodes, + # and an interior ring + correct_weights = np.array([3, 8]) + w = gppi.weights("area") + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("1")) + + w = gppi.weights("area", measure=True) + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("m2")) + + def test_weights_polygon_area_ugrid(self): + f = cf.example_field(8) + f = f[..., [0, 2]] + + # Surface area of unit sphere + sphere_area = 4 * np.pi + correct_weights = np.array([sphere_area / 8, sphere_area / 4]) + + # Spherical polygon weights + lon = f.auxiliary_coordinate("X") + lon.del_data() + bounds = cf.Data( + [[315, 45, 45, 999, 999, 999], [90, 90, 0, 45, 45, 135]], + "degrees_east", + mask_value=999, + ).reshape(2, 6) + lon.set_bounds(cf.Bounds(data=bounds)) + + lat = f.auxiliary_coordinate("Y") + bounds = cf.Data( + [[0, 0, 90, 999, 999, 999], [0, 90, 0, 0, -90, 0]], + "degrees_north", + mask_value=999, + ).reshape(2, 6) + lat.set_bounds(cf.Bounds(data=bounds)) + + w = f.weights("X", great_circle=True) + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("1")) + + w = f.weights("area", great_circle=True, measure=True, radius=radius) + self.assertTrue((w.array == (r**2) * correct_weights).all()) + self.assertEqual(w.Units, cf.Units("m2")) + + # Plane polygon weights + lon.override_units("m", inplace=True) + lon.standard_name = "projection_x_coordinate" + bounds = cf.Data( + [[2, 2, 0, 0, 999, 999, 999, 999], [2, 2, 0, 0, 1, 1, 3, 3]], + "m", + mask_value=999, + ).reshape(2, 8) + lon.set_bounds(cf.Bounds(data=bounds)) + + lat.override_units("m", inplace=True) + lat.standard_name = "projection_y_coordinate" + bounds = cf.Data( + [ + [-1, 1, 1, -1, 999, 999, 999, 999], + [-1, 1, 1, -1, -1, -3, -3, -1], + ], + "m", + mask_value=999, + ).reshape(2, 8) + lat.set_bounds(cf.Bounds(data=bounds)) + + correct_weights = np.array([4, 8]) + w = f.weights("area") + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("1")) + + w = f.weights("area", measure=True) + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("m2")) + + def test_weights_line_length_geometry(self): + # Spherical line geometry + gls = gps.copy() + lon = gls.auxiliary_coordinate("X") + lat = gls.auxiliary_coordinate("Y") + lon.set_geometry("line") + lat.set_geometry("line") + + # Cirumference of unit sphere + cirumference = 2 * np.pi + correct_weights = np.array( + [3 * cirumference / 4, 5 * cirumference / 4] + ) + + w = gls.weights("X", great_circle=True) + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("1")) + + w = gls.weights("X", great_circle=True, measure=True, radius=radius) + self.assertTrue((w.array == r * correct_weights).all()) + self.assertEqual(w.Units, cf.Units("m")) + + # Plane line geometry with multiple parts + gppm = gppi.copy() + lon = gppm.auxiliary_coordinate("X") + lat = gppm.auxiliary_coordinate("Y") + lon.set_geometry("line") + lat.set_geometry("line") + lon.del_interior_ring() + lat.del_interior_ring() + + correct_weights = np.array([9, 13]) + w = gppm.weights("X") + self.assertTrue((w.array == correct_weights).all()) + + def test_weights_line_area_ugrid(self): + f = cf.example_field(9) + f = f[..., 0:3] + lon = f.auxiliary_coordinate("X") + lat = f.auxiliary_coordinate("Y") + + lon.del_data() + bounds = cf.Data( + [[315, 45], [45, 45], [45, 315]], + "degrees_east", + mask_value=999, + ) + lon.set_bounds(cf.Bounds(data=bounds)) + + lat.del_data() + bounds = cf.Data( + [[0, 0], [0, 90], [90, 0]], + "degrees_north", + mask_value=999, + ) + lat.set_bounds(cf.Bounds(data=bounds)) + + # Cirumference of unit sphere + cirumference = 2 * np.pi + correct_weights = np.array([cirumference / 4] * 3) + + # Spherical line weights + w = f.weights("X", great_circle=True) + self.assertTrue( + np.isclose(w, correct_weights, rtol=0, atol=9e-15).all() + ) + self.assertEqual(w.Units, cf.Units("1")) + + w = f.weights("X", great_circle=True, measure=True, radius=radius) + self.assertTrue( + np.isclose(w, r * correct_weights, rtol=0, atol=9e-15).all() + ) + self.assertEqual(w.Units, cf.Units("m")) + + +if __name__ == "__main__": + print("Run date:", datetime.datetime.now()) + cf.environment() + print("") + unittest.main(verbosity=2) diff --git a/cf/test/ugrid_global_1.nc b/cf/test/ugrid_global_1.nc new file mode 100644 index 0000000000000000000000000000000000000000..25bc2afef512048636558de9a74272338eac594d GIT binary patch literal 37356 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 JrBVI%zW{mg^vM7K literal 0 HcmV?d00001 diff --git a/cf/test/ugrid_global_2.nc b/cf/test/ugrid_global_2.nc new file mode 100644 index 0000000000000000000000000000000000000000..4a262f835abd761f131860a507051c799b0928f6 GIT binary patch literal 116164 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 N-0+k&R;;1 repr(x) - - """ - return super().__repr__().replace("<", " 1: + if not xcoord.has_bounds(): + if auto: + return False + + raise ValueError( + "Can't create area weights: No bounds for " + f"{xcoord.identity()!r} axis" + ) + + if methods: + weights[(xaxis,)] = f"linear {xcoord.identity()}" + else: + cells = xcoord.cellsize + if xcoord.Units.equivalent(radians): + cells.Units = radians + if measure: + cells = cells * radius + cells.override_units(radius.Units, inplace=True) + else: + cells.Units = metres + + weights[(xaxis,)] = cells + + weights_axes.add(xaxis) + + if measure or ycoord.size > 1: + if not ycoord.has_bounds(): + if auto: + return False + + raise ValueError( + "Can't create area weights: No bounds for " + f"{ycoord.identity()!r} axis" + ) + + if ycoord.Units.equivalent(radians): + ycoord = ycoord.clip(-90, 90, units=Units("degrees")) + ycoord.sin(inplace=True) + + if methods: + weights[(yaxis,)] = f"linear sine {ycoord.identity()}" + else: + cells = ycoord.cellsize + if measure: + cells = cells * radius + + weights[(yaxis,)] = cells + else: + if methods: + weights[(yaxis,)] = f"linear {ycoord.identity()}" + else: + cells = ycoord.cellsize + weights[(yaxis,)] = cells + + weights_axes.add(yaxis) + + return True + + @classmethod + def data( + cls, + f, + w, + weights, + weights_axes, + axes=None, + data=False, + components=False, + methods=False, + ): + """Create weights from `Data`. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + w: `Data` + Create weights from this data, that must be + broadcastable to the data of *f*, unless the *axes* + parameter is also set. + + axes: (sequence of) `int` or `str`, optional + If not `None` then weights are created only for the + specified axes. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + data: `bool`, optional + If True then create weights in a `Data` instance that + is broadcastable to the original data. + + components: `bool`, optional + If True then the *weights* dictionary is updated with + orthogonal weights components. + + {{weights methods: `bool`, optional}} + + :Returns: + + `bool` + `True` if weights were created, otherwise `False`. + + """ + # ------------------------------------------------------------ + # Data weights + # ------------------------------------------------------------ + field_data_axes = f.get_data_axes() + + if axes is not None: + if isinstance(axes, (str, int)): + axes = (axes,) + + if len(axes) != w.ndim: + raise ValueError( + "'axes' parameter must provide an axis identifier " + f"for each weights data dimension. Got {axes!r} for " + f"{w.ndim} dimension(s)." + ) + + iaxes = [ + field_data_axes.index(f.domain_axis(axis, key=True)) + for axis in axes + ] + + if data: + for i in range(f.ndim): + if i not in iaxes: + w = w.insert_dimension(position=i) + iaxes.insert(i, i) + + w = w.transpose(iaxes) + + if w.ndim > 0: + while w.shape[0] == 1: + w = w.squeeze(0) + + else: + iaxes = list(range(f.ndim - w.ndim, f.ndim)) + + if not (components or methods): + if not f._is_broadcastable(w.shape): + raise ValueError( + f"The 'Data' weights (shape {w.shape}) are not " + "broadcastable to the field construct's data " + f"(shape {f.shape})." + ) + + axes0 = field_data_axes[f.ndim - w.ndim :] + else: + axes0 = [field_data_axes[i] for i in iaxes] + + for axis0 in axes0: + if axis0 in weights_axes: + raise ValueError( + "Multiple weights specified for " + f"{f.constructs.domain_axis_identity(axis0)!r} axis" + ) + + if methods: + weights[tuple(axes0)] = "custom data" + else: + weights[tuple(axes0)] = w + + weights_axes.update(axes0) + return True + + @classmethod + def field(cls, f, field_weights, weights, weights_axes): + """Create weights from other field constructs. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + field_weights: sequence of `Field` + The field constructs from which to create weights. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + :Returns: + + `bool` + `True` if weights were created, otherwise `False`. + + """ + s = f.analyse_items() + + domain_axes = f.domain_axes(todict=True) + for w in field_weights: + t = w.analyse_items() + # TODO CHECK this with org + + if t["undefined_axes"]: + w_domain_axes_1 = w.domain_axes( + filter_by_size=(1,), todict=True + ) + if set(w_domain_axes_1).intersection(t["undefined_axes"]): + raise ValueError( + f"Weights field {w} has at least one undefined " + f"domain axes: {w_domain_axes_1}." + ) + + w = w.squeeze() + + w_domain_axes = w.domain_axes(todict=True) + + axis1_to_axis0 = {} + + coordinate_references = f.coordinate_references(todict=True) + w_coordinate_references = w.coordinate_references(todict=True) + + for axis1 in w.get_data_axes(): + identity = t["axis_to_id"].get(axis1, None) + if identity is None: + raise ValueError( + "Weights field has unmatched, size > 1 " + f"{w.constructs.domain_axis_identity(axis1)!r} axis" + ) + + axis0 = s["id_to_axis"].get(identity, None) + if axis0 is None: + raise ValueError( + f"Weights field has unmatched, size > 1 {identity!r} " + "axis" + ) + + w_axis_size = w_domain_axes[axis1].get_size() + f_axis_size = domain_axes[axis0].get_size() + + if w_axis_size != f_axis_size: + raise ValueError( + f"Weights field has incorrectly sized {identity!r} " + f"axis ({w_axis_size} != {f_axis_size})" + ) + + axis1_to_axis0[axis1] = axis0 + + # Check that the defining coordinate data arrays are + # compatible + key0 = s["axis_to_coord"][axis0] + key1 = t["axis_to_coord"][axis1] + + if not f._equivalent_construct_data( + w, key0=key0, key1=key1, s=s, t=t + ): + raise ValueError( + f"Weights field has incompatible {identity!r} " + "coordinates" + ) + + # Still here? Then the defining coordinates have + # equivalent data arrays + + # If the defining coordinates are attached to + # coordinate references then check that those + # coordinate references are equivalent + refs0 = [ + key + for key, ref in coordinate_references.items() + if key0 in ref.coordinates() + ] + refs1 = [ + key + for key, ref in w_coordinate_references.items() + if key1 in ref.coordinates() + ] + + nrefs = len(refs0) + if nrefs > 1 or nrefs != len(refs1): + # The defining coordinate are associated with + # different numbers of coordinate references + equivalent_refs = False + elif not nrefs: + # Neither defining coordinate is associated with a + # coordinate reference + equivalent_refs = True + else: + # Each defining coordinate is associated with + # exactly one coordinate reference + equivalent_refs = f._equivalent_coordinate_references( + w, key0=refs0[0], key1=refs1[0], s=s, t=t + ) + + if not equivalent_refs: + raise ValueError( + "Input weights field has an incompatible " + "coordinate reference" + ) + + axes0 = tuple( + [axis1_to_axis0[axis1] for axis1 in w.get_data_axes()] + ) + + for axis in axes0: + if axis in weights_axes: + raise ValueError( + "Multiple weights specified for " + f"{f.constructs.domain_axis_identity(axis)!r} " + "axis" + ) + + weights[tuple(axes0)] = w.data + weights_axes.update(axes0) + + return True + + @classmethod + def field_scalar(cls, f): + """Return a scalar field of weights with long name ``'weight'``. + + .. versionadded:: 3.16.0 + + :Parameter: + + f: `Field` + The field for which the weights are being created. + + :Returns: + + `Field` + The scalar weights field, with a single array value of + ``1``. + + **Examples** + + >>> s = w.field_scalar(f) + >> print(s) + Field: long_name=weight + ----------------------- + Data : long_name=weight 1 + >>> print(s.array) + 1.0 + + """ + data = f._Data(1.0, "1") + + f = type(f)() + f.set_data(data, copy=False) + f.long_name = "weight" + f.comment = f"Weights for {f!r}" + return f + + @classmethod + def polygon_area( + cls, + f, + domain_axis, + weights, + weights_axes, + auto=False, + measure=False, + radius=None, + great_circle=False, + return_areas=False, + methods=False, + ): + """Creates area weights for polygon geometry cells. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + domain_axis: `str` or `None` + If set to a domain axis identifier + (e.g. ``'domainaxis1'``) then only accept cells that + recognise the given axis. If `None` then the cells may + span any axis. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + {{weights auto: `bool`, optional}} + + {{weights measure: `bool`, optional}} + + {{radius: optional}} + + great_circle: `bool`, optional + If True then assume that the edges of spherical + polygons are great circles. + + {{weights methods: `bool`, optional}} + + :Returns: + + `bool` or `Data` + `True` if weights were created, otherwise `False`. If + *return_areas* is True and weights were created, then + the weights are returned. + + """ + from .units import Units + + axis, aux_X, aux_Y, aux_Z, ugrid = cls._geometry_ugrid_cells( + f, domain_axis, "polygon", auto=auto + ) + + if axis is None: + if auto: + return False + + if domain_axis is None: + raise ValueError("No polygon cells") + + raise ValueError( + "No polygon cells for " + f"{f.constructs.domain_axis_identity(domain_axis)!r} axis" + ) + + if axis in weights_axes: + if auto: + return False + + raise ValueError( + "Multiple weights specifications for " + f"{f.constructs.domain_axis_identity(axis)!r} axis" + ) + + x = aux_X.bounds.data + y = aux_Y.bounds.data + + radians = Units("radians") + metres = Units("metres") + + if x.Units.equivalent(radians) and y.Units.equivalent(radians): + if not great_circle: + raise ValueError( + "Must set great_circle=True to allow the derivation of " + "area weights from spherical polygons composed from " + "great circle segments." + ) + + if methods: + weights[(axis,)] = "area spherical polygon" + return True + + spherical = True + x.Units = radians + elif x.Units.equivalent(metres) and y.Units.equivalent(metres): + if methods: + weights[(axis,)] = "area plane polygon" + return True + + spherical = False + else: + return False + + y.Units = x.Units + x = x.persist() + y = y.persist() + + # Find the number of nodes per polygon + n_nodes = x.count(axis=-1, keepdims=False).array + if (y.count(axis=-1, keepdims=False) != n_nodes).any(): + raise ValueError( + "Can't create area weights for " + f"{f.constructs.domain_axis_identity(axis)!r} axis: " + f"{aux_X!r} and {aux_Y!r} have inconsistent bounds " + "specifications" + ) + + if ugrid: + areas = cls._polygon_area_ugrid(f, x, y, n_nodes, spherical) + else: + areas = cls._polygon_area_geometry( + f, x, y, aux_X, aux_Y, n_nodes, spherical + ) + + del x, y, n_nodes + + if not measure: + areas.override_units(Units("1"), inplace=True) + elif spherical: + areas = cls._spherical_area_measure(f, areas, aux_Z, radius) + + if return_areas: + return areas + + weights[(axis,)] = areas + weights_axes.add(axis) + return True + + @classmethod + def _polygon_area_geometry(cls, f, x, y, aux_X, aux_Y, n_nodes, spherical): + """Creates area weights for polygon geometry cells. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + x: `Data` + The X coordinates of the polygon nodes, with units of + radians or equivalent to metres. + + y: `Data` + The Y coordinates of the polygon nodes, with the same + units as *x*. + + aux_X: `AuxiliaryCoordinate` + The X coordinate construct of *f*. + + aux_Y: `AuxiliaryCoordinate` + The Y coordinate construct of *f*. + + n_nodes: `numpy.ndarray` + The number of nodes per polygon, equal to the number + of non-missing values in the trailing dimension of + *x*. + + spherical: `bool` + True for spherical lines. + + :Returns: + + `Data` + The area of the geometry polygon cells, in units of + square metres. + + """ + import numpy as np + + x_units = x.Units + y_units = y.Units + + # Check for interior rings + interior_ring_X = aux_X.get_interior_ring(None) + interior_ring_Y = aux_Y.get_interior_ring(None) + if interior_ring_X is None and interior_ring_Y is None: + interior_rings = None + elif interior_ring_X is None: + raise ValueError( + "Can't find weights: X coordinates have missing " + "interior ring variable" + ) + elif interior_ring_Y is None: + raise ValueError( + "Can't find weights: Y coordinates have missing " + "interior ring variable" + ) + elif not interior_ring_X.data.equals(interior_ring_Y.data): + raise ValueError( + "Can't find weights: Interior ring variables for X and Y " + "coordinates have different data values" + ) + else: + interior_rings = interior_ring_X.data + if interior_rings.shape != aux_X.bounds.shape[:-1]: + raise ValueError( + "Can't find weights: Interior ring variables have " + f"incorrect shape. Got {interior_rings.shape}, " + f"expected {aux_X.bounds.shape[:-1]}" + ) + + rows = np.arange(x.shape[0]) + n_nodes_m1 = n_nodes - 1 + + duplicate = x[0, 0, 0].isclose(x[0, 0, n_nodes_m1[0, 0]]) & y[ + 0, 0, 0 + ].isclose(y[0, 0, n_nodes_m1[0, 0]]) + duplicate = duplicate.array + + y = y.array + + # Pad out the boundary vertex array for each cell with first + # and last bounds neighbours + empty_col_x = np.ma.masked_all(x.shape[:-1] + (1,), dtype=x.dtype) + empty_col_y = np.ma.masked_all(y.shape[:-1] + (1,), dtype=y.dtype) + + if not duplicate.any(): + # The first and last boundary vertices are different in + # all polygons, i.e. No. nodes = No. edges. + # + # Insert two new Y columns that duplicate the first and + # last nodes. + # + # E.g. for triangle cells: + # [[[4, 5, 6]]] -> [[[6, 4, 5, 6, 4]]] + # [[[4, 5, 6, --]]] -> [[[6, 4, 5, 6, 4, --]]] + n_nodes_p1 = n_nodes + 1 + y = np.ma.concatenate((empty_col_y, y, empty_col_y), axis=-1) + for i in range(x.shape[1]): + y[:, i, 0] = y[rows, i, n_nodes[:, i]] + y[rows, i, n_nodes_p1[:, i]] = y[rows, i, 1] + + if spherical: + # Spherical polygons defined by great circles: Also + # insert the columns into X. + x = x.array + x = np.ma.concatenate((empty_col_x, x, empty_col_x), axis=-1) + for i in range(x.shape[1]): + x[:, i, 0] = x[rows, i, n_nodes[:, i]] + x[rows, i, n_nodes_p1[:, i]] = x[rows, i, 1] + + # The number of edges in each polygon + N = n_nodes + + del n_nodes_p1 + elif duplicate.all(): + # The first and last boundary vertices coincide in all + # cells, i.e. No. nodes = No. edges + 1. + # + # E.g. for triangle cells: + # [[[4, 5, 6, 4]]] -> [[[6, 4, 5, 6, 4]]] + # [[[4, 5, 6, 4, --]]] -> [[[6, 4, 5, 6, 4, --]]] + if not spherical: + raise ValueError( + "Can't (yet) calculate weights for plane geometry " + "polygons with identical first and last nodes" + ) + + y = np.ma.concatenate((empty_col_y, y), axis=-1) + for i in range(x.shape[1]): + y[:, i, 0] = y[rows, i, n_nodes_m1[:, i]] + + x = x.array + x = np.ma.concatenate((empty_col_x, x), axis=-1) + for i in range(x.shape[1]): + x[:, i, 0] = x[rows, i, n_nodes_m1[:, i]] + + # The number of edges in each polygon + N = n_nodes_m1 + else: + raise ValueError( + "Can't calculate spherical geometry polygon cell areas " + "when the first and last boundary vertices coincide in " + "some cells but not all" + ) + + del duplicate, rows, n_nodes_m1 + + y = f._Data(y, units=y_units) + if spherical: + x = f._Data(x, units=x_units) + + if spherical: + # Spherical polygons defined by great circles + all_areas = cls._spherical_polygon_areas( + f, x, y, N, interior_rings + ) + else: + # Plane polygons defined by straight lines. + all_areas = cls._plane_polygon_areas(x, y) + + # Sum the areas of each geometry polygon part to get the total + # area of each cell + areas = all_areas.sum(-1, squeeze=True) + return areas + + @classmethod + def _polygon_area_ugrid(cls, f, x, y, n_nodes, spherical): + """Creates area weights for UGRID face cells. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + x: `Data` + The X coordinates of the polygon nodes, with units of + radians or equivalent to metres. + + y: `Data` + The Y coordinates of the polygon nodes, with the same + units as *x*. + + n_nodes: `numpy.ndarray` + The number of nodes per polygon, equal to the number + of non-missing values in the trailing dimension of + *x*. + + spherical: `bool` + True for spherical polygons. + + :Returns: + + `Data` + The area of the UGRID face cells, in units of square + metres. + + """ + import numpy as np + + y_units = y.Units + + n_nodes0 = n_nodes.item(0) + if (n_nodes == n_nodes0).all(): + # All cells have the same number of nodes, so we can use + # an integer and a slice in place of two integer arrays, + # which is much faster. + n_nodes = n_nodes0 + rows = slice(None) + else: + rows = np.arange(x.shape[0]) + + n_nodes_p1 = n_nodes + 1 + + # The first and last boundary vertices are different in + # all polygons, i.e. No. nodes = No. edges. + # + # Insert two new Y columns that duplicate the first and last + # nodes. + # + # E.g. for triangle cells: + # [[4, 5, 6]] -> [[6, 4, 5, 6, 4]] + # [[4, 5, 6, --]] -> [[6, 4, 5, 6, 4, --]] + y = y.array + empty_col_y = np.ma.masked_all((y.shape[0], 1), dtype=y.dtype) + y = np.ma.concatenate((empty_col_y, y, empty_col_y), axis=1) + y[:, 0] = y[rows, n_nodes] + y[rows, n_nodes_p1] = y[rows, 1] + y = f._Data(y, units=y_units) + + if spherical: + # Spherical polygons defined by great circles: Also insert + # the columns into X. + x_units = x.Units + x = x.array + empty_col_x = np.ma.masked_all((x.shape[0], 1), dtype=x.dtype) + x = np.ma.concatenate((empty_col_x, x, empty_col_x), axis=1) + x[:, 0] = x[rows, n_nodes] + x[rows, n_nodes_p1] = x[rows, 1] + x = f._Data(x, units=x_units) + + areas = cls._spherical_polygon_areas(f, x, y, n_nodes) + else: + # Plane polygons defined by straight lines + areas = cls._plane_polygon_areas(x, y) + + return areas + + @classmethod + def line_length( + cls, + f, + domain_axis, + weights, + weights_axes, + auto=False, + measure=False, + radius=None, + great_circle=False, + methods=False, + ): + """Creates line-length weights for line geometries. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + domain_axis: `str` or `None` + If set to a domain axis identifier + (e.g. ``'domainaxis1'``) then only recognise cells + that span the given axis. If `None` then the cells may + span any axis. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + {{weights auto: `bool`, optional}} + + {{weights measure: `bool`, optional}} + + {{radius: optional}} + + great_circle: `bool`, optional + If True then assume that the spherical lines are great + circles. + + {{weights methods: `bool`, optional}} + + :Returns: + + `bool` + `True` if weights were created, otherwise `False`. + + """ + from .units import Units + + axis, aux_X, aux_Y, aux_Z, ugrid = cls._geometry_ugrid_cells( + f, domain_axis, "line", auto=auto + ) + + if axis is None: + if auto: + return False + + if domain_axis is None: + raise ValueError("No line cells") + + raise ValueError( + "No line cells for " + f"{f.constructs.domain_axis_identity(domain_axis)!r} axis" + ) + + if axis in weights_axes: + if auto: + return False + + raise ValueError( + "Multiple weights specifications for " + f"{f.constructs.domain_axis_identity(axis)!r} axis" + ) + + radians = Units("radians") + metres = Units("metres") + + x = aux_X.bounds.data + y = aux_Y.bounds.data + + if x.Units.equivalent(radians) and y.Units.equivalent(radians): + if not great_circle: + raise ValueError( + "Must set great_circle=True to allow the derivation of " + "area weights from spherical polygons composed from " + "great circle segments." + ) + + if methods: + weights[(axis,)] = "linear spherical line" + return True + + spherical = True + x.Units = radians + elif x.Units.equivalent(metres) and y.Units.equivalent(metres): + if methods: + weights[(axis,)] = "linear plane line" + return True + + spherical = False + else: + return False + + y.Units = x.Units + + if ugrid: + lengths = cls._line_length_ugrid(f, x, y, spherical) + else: + lengths = cls._line_length_geometry(f, x, y, spherical) + + if not measure: + lengths.override_units(Units("1"), inplace=True) + elif spherical: + r = f.radius(default=radius) + r = r.override_units(Units("1")) + lengths = lengths * r + + weights[(axis,)] = lengths + weights_axes.add(axis) + return True + + @classmethod + def _line_length_geometry(cls, f, x, y, spherical): + """Creates line-length weights for line geometries. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + x: `Data` + The X coordinates of the line nodes. + + y: `Data` + The Y coordinates of the line nodes. + + spherical: `bool` + True for spherical lines. + + :Returns: + + `Data` + The length of the geometry line cells, in units of + metres. + + """ + from .units import Units + + if spherical: + all_lengths = cls.central_angles(f, x, y) + all_lengths.override_units(Units("m"), inplace=True) + else: + delta_x = x.diff(axis=-1) + delta_y = y.diff(axis=-1) + all_lengths = (delta_x**2 + delta_y**2) ** 0.5 + + # Sum the lengths of each part to get the total length of each + # cell + lengths = all_lengths.sum(axes=(-2, -1), squeeze=True) + return lengths + + @classmethod + def _line_length_ugrid(cls, f, x, y, spherical): + """Creates line-length weights for line geometries. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + x: `Data` + The X coordinates of the line nodes. + + y: `Data` + The Y coordinates of the line nodes. + + spherical: `bool` + True for spherical lines. + + :Returns: + + `Data` + The length of the UGRID edge cells, in units of + metres. + + """ + from .units import Units + + if spherical: + lengths = cls.central_angles(f, x, y) + lengths.override_units(Units("m"), inplace=True) + else: + delta_x = x.diff(axis=-1) + delta_y = y.diff(axis=-1) + lengths = (delta_x**2 + delta_y**2) ** 0.5 + + lengths.squeeze(axes=-1, inplace=True) + return lengths + + @classmethod + def geometry_volume( + cls, + f, + domain_axis, + weights, + weights_axes, + auto=False, + measure=False, + radius=None, + great_circle=False, + methods=False, + ): + """Creates volume weights for polygon geometry cells. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + domain_axis: `str` or `None` + If set to a domain axis identifier + (e.g. ``'domainaxis1'``) then only recognise cells + that span the given axis. If `None` then the cells may + span any axis. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + {{weights auto: `bool`, optional}} + + {{weights measure: `bool`, optional}} + + {{radius: optional}} + + great_circle: `bool`, optional + If True then assume that the edges of spherical + polygons are great circles. + + {{weights methods: `bool`, optional}} + + :Returns: + + `bool` + `True` if weights were created, otherwise `False`. + + """ + from .units import Units + + axis, aux_X, aux_Y, aux_Z, ugrid = cls._geometry_ugrid_cells( + f, domain_axis, "polygon", auto=auto + ) + + if axis is None and auto: + return False + + if axis in weights_axes: + if auto: + return False + + raise ValueError( + "Multiple weights specifications for " + f"{f.constructs.domain_axis_identity(axis)!r} axis" + ) + + x = aux_X.bounds.data + y = aux_Y.bounds.data + z = aux_Z.bounds.data + + radians = Units("radians") + metres = Units("metres") + + if not z.Units.equivalent(metres): + if auto: + return False + + raise ValueError( + "Z coordinate bounds must have units equivalent to " + f"metres for volume calculations. Got {z.Units!r}." + ) + + if not methods: + # Initialise cell volumes as the cell areas + areas = cls.polygon_area( + f, + weights, + weights_axes, + auto=auto, + measure=measure, + radius=radius, + great_circle=great_circle, + methods=False, + return_areas=True, + ) + if measure: + delta_z = abs(z[..., 1] - z[..., 0]) + delta_z.squeeze(axis=-1, inplace=True) + + if x.Units.equivalent(metres) and y.Units.equivalent(metres): + # -------------------------------------------------------- + # Plane polygons defined by straight lines. + # -------------------------------------------------------- + if methods: + weights[(axis,)] = "volume plane polygon geometry" + return True + + if measure: + volumes = areas * delta_z + else: + volumes = areas + + elif x.Units.equivalent(radians) and y.Units.equivalent(radians): + # -------------------------------------------------------- + # Spherical polygons defined by great circles + # -------------------------------------------------------- + if not great_circle: + raise ValueError( + "Must set great_circle=True to allow the derivation " + "of volume weights from spherical polygons composed " + "from great circle segments." + ) + + if methods: + weights[(axis,)] = "volume spherical polygon geometry" + return True + + if measure: + r = f.radius(default=radius) + + # actual_volume = + # [actual_area/(4*pi*r**2)] + # * (4/3)*pi*[(r+delta_z)**3 - r**3)] + volumes = areas * ( + delta_z**3 / (3 * r**2) + delta_z**2 / r + delta_z + ) + else: + volumes = areas + else: + raise ValueError( + "X and Y coordinate bounds must both have units " + "equivalent to either metres (for plane polygon) or " + "radians (for spherical polygon) volume calculations. Got " + f"{x.Units!r} and {y.Units!r}." + ) + + weights[(axis,)] = volumes + weights_axes.add(axis) + return True + + @classmethod + def interior_angles(cls, f, lon, lat, interior_rings=None): + """Find the interior angles at spherical polygon vertices. + + The interior angle at a vertex is that formed by two adjacent + sides. Each interior angle is therefore a function of three + vertices - the vertex at which the interior angle is required and + the two adjacent vertices either side of it. + + **Method** + + Bevis, M., Cambareri, G. Computing the area of a spherical polygon + of arbitrary shape. Math Geol 19, 335–346 (1987). + + http://bemlar.ism.ac.jp/zhuang/Refs/Refs/bevis1987mathgeol.pdf + + Abstract: An algorithm for determining the area of a spherical + polygon of arbitrary shape is presented. The kernel of the + problem is to compute the interior angle at each vertex of the + spherical polygon; a well-known relationship between the area of + a spherical polygon and the sum of its interior angles then may + be exploited. The algorithm has been implemented as a FORTRAN + subroutine and a listing is provided. + + .. versionadded:: 3.16.0 + + .. seealso:: `central_angles` + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + lon: `Data` + Longitudes. Must have units of radians, which is not + checked. + + lat: `Data` + Latitudes. Must have units of radians, which is not + checked. + + interior_ring: `Data`, optional + The interior ring indicators for parts of polygon + geometry cells. If set must have shape + ``lon.shape[:-1]``. + + :Returns: + + `Data` + The interior angles of each spherical polygon, in + units of radians. + + """ + import numpy as np + + from .query import lt + + # P denotes a vertex at which the interior angle is required, A + # denotes the adjacent point clockwise from P, and B denotes the + # adjacent point anticlockwise from P. + lon_A = lon[..., :-2] + lon_P = lon[..., 1:-1] + lon_B = lon[..., 2:] + + lon_A_minus_lon_P = lon_A - lon_P + lon_B_minus_lon_P = lon_B - lon_P + + cos_lat = lat.cos() + cos_lat_A = cos_lat[..., :-2] + cos_lat_P = cos_lat[..., 1:-1] + cos_lat_B = cos_lat[..., 2:] + + sin_lat = lat.sin() + sin_lat_A = sin_lat[..., :-2] + sin_lat_P = sin_lat[..., 1:-1] + sin_lat_B = sin_lat[..., 2:] + + y = lon_A_minus_lon_P.sin() * cos_lat_A + x = ( + sin_lat_A * cos_lat_P + - cos_lat_A * sin_lat_P * lon_A_minus_lon_P.cos() + ) + lat_A_primed = f._Data.arctan2(y, x) + + y = lon_B_minus_lon_P.sin() * cos_lat_B + x = ( + sin_lat_B * cos_lat_P + - cos_lat_B * sin_lat_P * lon_B_minus_lon_P.cos() + ) + lat_B_primed = f._Data.arctan2(y, x) + + # The CF vertices here are, in general, given in anticlockwise + # order, so we do "alpha_P = lat_B_primed - lat_A_primed", + # rather than the "alpha_P = lat_A_primed - lat_B_primed" + # given in Bevis and Cambareri, which assumes clockwise order. + alpha_P = lat_B_primed - lat_A_primed + + if interior_rings is not None: + # However, interior rings *are* given in clockwise order in + # CF, so we need to negate alpha_P in these cases. + alpha_P.where(interior_rings, -alpha_P, inplace=True) + + # Add 2*pi to negative values + alpha_P = alpha_P.where(lt(0), alpha_P + 2 * np.pi, alpha_P) + return alpha_P + + @classmethod + def central_angles(cls, f, lon, lat): + r"""Find the central angles for spherical great circle line segments. + + The central angle of two points on the sphere is the angle + subtended from the centre of the sphere by the two points on its + surface. It is calculated with a special case of the Vincenty + formula that is accurate for all spherical distances: + + **Method** + + \Delta \sigma =\arctan {\frac {\sqrt {\left(\cos \phi + _{2}\sin(\Delta \lambda )\right)^{2}+\left(\cos \phi _{1}\sin + \phi _{2}-\sin \phi _{1}\cos \phi _{2}\cos(\Delta \lambda + )\right)^{2}}}{\sin \phi _{1}\sin \phi _{2}+\cos \phi _{1}\cos + \phi _{2}\cos(\Delta \lambda )}} + + The quadrant for \Delta \sigma should be governed by the signs of + the numerator and denominator of the right hand side using the + atan2 function. + + (https://en.wikipedia.org/wiki/Great-circle_distance) + + .. versionadded:: 3.16.0 + + .. seealso:: `interior_angles` + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + lon: `Data` + Longitudes with units of radians. + + lat: `Data` + Latitudes with units of radians. + + :Returns: + + `Data` + The central angles in units of radians. + + """ + # A and B denote adjacent vertices that define each line segment + delta_lon = lon.diff(axis=-1) + + cos_lat = lat.cos() + sin_lat = lat.sin() + + cos_lat_A = cos_lat[..., :-1] + cos_lat_B = cos_lat[..., 1:] + + sin_lat_A = sin_lat[..., :-1] + sin_lat_B = sin_lat[..., 1:] + + cos_delta_lon = delta_lon.cos() + sin_delta_lon = delta_lon.sin() + + y = ( + (cos_lat_B * sin_delta_lon) ** 2 + + (cos_lat_A * sin_lat_B - sin_lat_A * cos_lat_B * cos_delta_lon) + ** 2 + ) ** 0.5 + x = sin_lat_A * sin_lat_B + cos_lat_A * cos_lat_B * cos_delta_lon + + delta_sigma = f._Data.arctan2(y, x) + return delta_sigma + + @classmethod + def linear( + cls, + f, + axis, + weights, + weights_axes, + auto=False, + measure=False, + methods=False, + ): + """1-d linear weights from dimension coordinate constructs. + + .. versionadded:: 3.16.0 + + :Parameters: + + axis: `str` + The identity of the domain axis over which to find the + weights. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + {{weights auto: `bool`, optional}} + + {{weights measure: `bool`, optional}} + + {{weights methods: `bool`, optional}} + + :Returns: + + `bool` + `True` if weights were created, otherwise `False`. + + """ + da_key = f.domain_axis(axis, key=True, default=None) + if da_key is None: + if auto: + return False + + raise ValueError( + "Can't create weights: Can't find domain axis matching " + f"{axis!r}" + ) + + dim = f.dimension_coordinate(filter_by_axis=(da_key,), default=None) + if dim is None: + if auto: + return False + + raise ValueError( + f"Can't create linear weights for {axis!r} axis: Can't find " + "dimension coordinate construct." + ) + + if not measure and dim.size == 1: + return False + + if da_key in weights_axes: + if auto: + return False + + raise ValueError( + f"Can't create linear weights for {axis!r} axis: Multiple " + "axis specifications" + ) + + if not dim.has_bounds(): + # Dimension coordinate has no bounds + if auto: + return False + + raise ValueError( + f"Can't create linear weights for {axis!r} axis: No bounds" + ) + else: + # Bounds exist + if methods: + weights[ + (da_key,) + ] = f"linear {f.constructs.domain_axis_identity(da_key)}" + else: + weights[(da_key,)] = dim.cellsize + + weights_axes.add(da_key) + return True + + @classmethod + def cell_measure( + cls, f, measure, weights, weights_axes, methods=False, auto=False + ): + """Cell measure weights. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + {{weights methods: `bool`, optional}} + + {{weights auto: `bool`, optional}} + + :Returns: + + `bool` + `True` if weights were created, otherwise `False`. + + """ + m = f.cell_measures(filter_by_measure=(measure,), todict=True) + len_m = len(m) + + if not len_m: + if measure == "area": + return False + + if auto: + return False + + raise ValueError( + f"Can't find weights: No {measure!r} cell measure" + ) + + elif len_m > 1: + if auto: + return False + + raise ValueError( + f"Can't find weights: Multiple {measure!r} cell measures" + ) + + key, clm = m.popitem() + + clm_axes0 = f.get_data_axes(key) + + clm_axes = tuple( + [axis for axis, n in zip(clm_axes0, clm.data.shape) if n > 1] + ) + + for axis in clm_axes: + if axis in weights_axes: + if auto: + return False + + raise ValueError( + "Multiple weights specifications for " + f"{f.constructs.domain_axis_identity(axis)!r} axis" + ) + + clm = clm.get_data(_fill_value=False).copy() + if clm_axes != clm_axes0: + iaxes = [clm_axes0.index(axis) for axis in clm_axes] + clm.squeeze(iaxes, inplace=True) + + if methods: + weights[tuple(clm_axes)] = measure + " cell measure" + else: + weights[tuple(clm_axes)] = clm + + weights_axes.update(clm_axes) + return True + + @classmethod + def scale(cls, w, scale): + """Scale the weights so that they are <= scale. + + .. versionadded:: 3.16.0 + + :Parameters: + + w: `Data` + The weights to be scaled. + + scale: number or `None` + The maximum value of the scaled weights. If `None` + then no scaling is applied. + + :Returns: + + `Data` + The scaled weights. + + """ + if scale is None: + return w + + if scale < 0: + raise ValueError( + "Can't set 'scale' parameter to a negatve number. Got " + f"{scale!r}" + ) + + w = w / w.max() + if scale != 1: + w = w * scale + + return w + + @classmethod + def _geometry_ugrid_cells(cls, f, domain_axis, cell_type, auto=False): + """Checks whether weights for geometry or UGRID cells can be created. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + domain_axis: `str` or `None` + If set to a domain axis identifier + (e.g. ``'domainaxis1'``) then only recognise cells + that span the given axis. If `None` then the cells may + span any axis. + + cell_type: `str` + Either ``'polygon'`` or ``'line'``. + + {{weights auto: `bool`, optional}} + + :Returns: + + 5-`tuple` + If no appropriate geometry/UGRID cells were found then + a `tuple` of all `None` is returned. Otherwise the + `tuple` comprises: + + * The domain axis identifier of the cells, or `None`. + * The X coordinate construct of *f*, or `None`. + * The Y coordinate construct of *f*, or `None`. + * The Z coordinate construct of *f*, or `None`. + * `True` if the cells are a UGRID mesh, `False` if + they are geometry cells, or `None`. + + """ + n_return = 5 + aux_X = None + aux_Y = None + aux_Z = None + x_axis = None + y_axis = None + z_axis = None + ugrid = None + + if cell_type == "polygon": + ugrid_cell_type = "face" + else: + ugrid_cell_type = "edge" + + auxiliary_coordinates_1d = f.auxiliary_coordinates( + filter_by_naxes=(1,), todict=True + ) + + for key, aux in auxiliary_coordinates_1d.items(): + aux_axis = f.get_data_axes(key)[0] + + ugrid = f.domain_topology(default=None, filter_by_axis=(aux_axis,)) + if ugrid is not None: + cell = ugrid.get_cell(None) + else: + cell = None + + if not ( + cell == ugrid_cell_type or aux.get_geometry(None) == cell_type + ): + continue + + # Still here? Then this auxiliary coordinate has either UGRID + # or geometry cells. + if aux.X: + aux_X = aux.copy() + x_axis = aux_axis + if domain_axis is not None and x_axis != domain_axis: + aux_X = None + continue + elif aux.Y: + aux_Y = aux.copy() + y_axis = aux_axis + if domain_axis is not None and y_axis != domain_axis: + aux_Y = None + continue + elif aux.Z: + aux_Z = aux.copy() + z_axis = aux_axis + if domain_axis is not None and z_axis != domain_axis: + aux_Z = None + continue + + if aux_X is None or aux_Y is None: + if auto: + return (None,) * n_return + + raise ValueError( + "Can't create weights: Need both X and Y nodes to " + f"calculate {cell_type} cell weights" + ) + + if x_axis != y_axis: + if auto: + return (None,) * n_return + + raise ValueError( + "Can't create weights: X and Y cells span different domain " + "axes" + ) + + axis = x_axis + + if aux_X.get_bounds(None) is None or aux_Y.get_bounds(None) is None: + # Not both X and Y coordinates have bounds + if auto: + return (None,) * n_return + + raise ValueError( + "Can't create weights: Not both X and Y coordinates have " + "bounds" + ) + + if aux_X.bounds.shape != aux_Y.bounds.shape: + if auto: + return (None,) * n_return + + raise ValueError( + "Can't create weights: UGRID/geometry X and Y coordinate " + "bounds must have the same shape. " + f"Got {aux_X.bounds.shape} and {aux_Y.bounds.shape}" + ) + + if aux_Z is None: + for key, aux in auxiliary_coordinates_1d.items(): + if aux.Z: + aux_Z = aux.copy() + z_axis = f.get_data_axes(key)[0] + + # Check Z coordinates + if aux_Z is not None: + if z_axis != x_axis: + if auto: + return (None,) * n_return + + raise ValueError( + "Z coordinates span different domain axis to X and Y " + "geometry coordinates" + ) + + return axis, aux_X, aux_Y, aux_Z, bool(ugrid) + + @classmethod + def _spherical_area_measure(cls, f, areas, aux_Z, radius=None): + """Convert spherical polygon weights to cell measures. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + areas: `Data` + The area of the polygon cells on the unit sphere, in + units of square metres. + + aux_Z: `AuxiliaryCoordinate` + The Z coordinate construct of *f*. + + {{radius: optional}} + + :Returns: + + `Data` + The area of each polygon on the surface of the defined + sphere, in units of square metres. + + """ + # Multiply by radius squared, accounting for any Z + # coordinates, to get the actual area + from .units import Units + + radius = f.radius(default=radius) + if aux_Z is None: + # No Z coordinates + r = radius + else: + z = aux_Z.get_data(None, _fill_value=False) + if z is None: + # No Z coordinates + r = radius + else: + if not z.Units.equivalent(Units("metres")): + raise ValueError( + "Z coordinates must have units equivalent to " + f"metres for area calculations. Got {z.Units!r}" + ) + + positive = aux_Z.get_property("positive", None) + if positive is None: + raise ValueError( + "Z coordinate 'positive' property is not defined" + ) + + if positive.lower() == "up": + r = radius + z + elif positive.lower() == "down": + r = radius - z + else: + raise ValueError( + "Bad value of Z coordinate 'positive' property: " + f"{positive!r}." + ) + + r = r.override_units(Units("1")) + areas = areas * r**2 + return areas + + @classmethod + def _plane_polygon_areas(cls, x, y): + r"""Calculate the areas of plane polygons. + + The area, A, is of a plane polygon is given by the shoelace + formula: + + A={\frac {1}{2}}\sum _{i=1}^{n}x_{i}(y_{i+1}-y_{i-1})} + + (https://en.wikipedia.org/wiki/Shoelace_formula). + + The formula gives a positive area for polygon nodes stored in + anticlockwise order as viewed from above, and a negative area + for polygon nodes stored in clockwise order. Note that + interior ring polygons are stored in clockwise order. + + .. versionadded:: 3.16.0 + + :Parameters: + + x: `Data` + The X coordinates of the polygon nodes, with no + duplication of the first and last nodes (i.e. the + polygons are represented by has ``N`` values, where + ``N`` is the number of edges). + + y: `Data` + The Y coordinates of the polygon nodes, with + wrap-araound duplication of the first and last nodes + (i.e. the polygons are represented by has ``N + 2`` + values, where ``N`` is the number of edges). + + :Returns: + + `Data` + The area of each plane polygon defined by the trailing + dimensions of *x* and *y*, in units of square metres. + + """ + areas = 0.5 * (x * (y[..., 2:] - y[..., :-2])).sum(-1, squeeze=True) + return areas + + @classmethod + def _spherical_polygon_areas(cls, f, x, y, N, interior_rings=None): + r"""Calculate the areas of polygons on the unit sphere. + + The area, A, of a polygon on the unit sphere, whose sides are + great circles, is given by (Todhunter): + + A=\left(\sum _{n=1}^{N}A_{n}\right)-(N-2)\pi + + where A_{n} is the n-th interior angle, and N is the number of + sides (https://en.wikipedia.org/wiki/Spherical_trigonometry). + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + x: array_like + The X coordinates of the polygon nodes, with + wrap-araound duplication of the first and last nodes + (i.e. the polygons are represented by has ``N + 2`` + values, where ``N`` is the number of edges). + + y: array_like + The Y coordinates of the polygon nodes, with + wrap-araound duplication of the first and last nodes + (i.e. the polygons are represented by has ``N + 2`` + values, where ``N`` is the number of edges). + + N: array_like + The number of edges in each polygon. + + interior_rings: array_like, optional + The interior ring indicators for parts of polygon + geometry cells. If set must have shape + ``x.shape[:-1]``. + + :Returns: + + `Data` + The area on the unit sphere of each polygon defined by + the trailing dimensions of *x* and *y*, in units of + square metres. + + """ + from numpy import pi + + from .units import Units + + interior_angles = cls.interior_angles(f, x, y, interior_rings) + areas = interior_angles.sum(-1, squeeze=True) - (N - 2) * pi + areas.override_units(Units("m2"), inplace=True) + return areas diff --git a/docs/source/class/cf.AuxiliaryCoordinate.rst b/docs/source/class/cf.AuxiliaryCoordinate.rst index f8e390faf5..4cc7dae7e0 100644 --- a/docs/source/class/cf.AuxiliaryCoordinate.rst +++ b/docs/source/class/cf.AuxiliaryCoordinate.rst @@ -492,7 +492,14 @@ NetCDF ~cf.AuxiliaryCoordinate.nc_del_variable ~cf.AuxiliaryCoordinate.nc_get_variable ~cf.AuxiliaryCoordinate.nc_has_variable - ~cf.AuxiliaryCoordinate.nc_set_variable + ~cf.AuxiliaryCoordinate.nc_set_variable + ~cf.AuxiliaryCoordinate.nc_del_node_coordinate_variable + ~cf.AuxiliaryCoordinate.nc_get_node_coordinate_variable + ~cf.AuxiliaryCoordinate.nc_has_node_coordinate_variable + ~cf.AuxiliaryCoordinate.nc_node_coordinate_variable_groups + ~cf.AuxiliaryCoordinate.nc_set_node_coordinate_variable + ~cf.AuxiliaryCoordinate.nc_set_node_coordinate_variable_groups + ~cf.AuxiliaryCoordinate.nc_clear_node_coordinate_variable_groups Groups ^^^^^^ diff --git a/docs/source/class/cf.Constructs.rst b/docs/source/class/cf.Constructs.rst index 2473606dad..05caf74fc8 100644 --- a/docs/source/class/cf.Constructs.rst +++ b/docs/source/class/cf.Constructs.rst @@ -32,6 +32,8 @@ Filtering ~cf.Constructs.filter_by_key ~cf.Constructs.filter_by_ncdim ~cf.Constructs.filter_by_ncvar + ~cf.Constructs.filter_by_cell + ~cf.Constructs.filter_by_connectivity ~cf.Constructs.filters_applied ~cf.Constructs.clear_filters_applied ~cf.Constructs.inverse_filter diff --git a/docs/source/class/cf.Data.rst b/docs/source/class/cf.Data.rst index 44305500dd..d4cd900d8e 100644 --- a/docs/source/class/cf.Data.rst +++ b/docs/source/class/cf.Data.rst @@ -19,7 +19,6 @@ Inspection :template: attribute.rst ~cf.Data.array - ~cf.Data.varray ~cf.Data.dtype ~cf.Data.ndim ~cf.Data.shape @@ -28,6 +27,8 @@ Inspection ~cf.Data.dump ~cf.Data.inspect ~cf.Data.isscalar + ~cf.Data.sparse_array + Units ----- @@ -324,6 +325,7 @@ Mask support ~cf.Data.filled ~cf.Data.harden_mask ~cf.Data.masked_invalid + ~cf.Data.masked_values ~cf.Data.del_fill_value ~cf.Data.get_fill_value ~cf.Data.has_fill_value @@ -360,7 +362,7 @@ Trigonometric functions ~cf.Data.arcsin ~cf.Data.arccos ~cf.Data.arctan -.. ~cf.Data.arctan2 [AT2] + ~cf.Data.arctan2 Hyperbolic functions ^^^^^^^^^^^^^^^^^^^^ @@ -877,3 +879,5 @@ Deprecated :template: attribute.rst ~cf.Data.ispartitioned + ~cf.Data.varray + diff --git a/docs/source/class/cf.Domain.rst b/docs/source/class/cf.Domain.rst index 65ea957db8..526700e480 100644 --- a/docs/source/class/cf.Domain.rst +++ b/docs/source/class/cf.Domain.rst @@ -119,12 +119,23 @@ Metadata constructs :template: method.rst ~cf.Domain.auxiliary_coordinates + ~cf.Domain.auxiliary_coordinate + ~cf.Domain.cell_connectivities + ~cf.Domain.cell_connectivity ~cf.Domain.cell_measures + ~cf.Domain.cell_measure ~cf.Domain.coordinates + ~cf.Domain.coordinate ~cf.Domain.coordinate_references + ~cf.Domain.coordinate_reference ~cf.Domain.dimension_coordinates + ~cf.Domain.dimension_coordinate ~cf.Domain.domain_ancillaries + ~cf.Domain.domain_ancillary ~cf.Domain.domain_axes + ~cf.Domain.domain_axis + ~cf.Domain.domain_topologies + ~cf.Domain.domain_topology ~cf.Domain.construct ~cf.Domain.construct_item ~cf.Domain.construct_key @@ -137,19 +148,12 @@ Metadata constructs ~cf.Domain.get_data_axes ~cf.Domain.has_data_axes ~cf.Domain.set_data_axes - ~cf.Domain.auxiliary_coordinate ~cf.Domain.auxiliary_to_dimension - ~cf.Domain.cell_measure - ~cf.Domain.coordinate - ~cf.Domain.coordinate_reference + ~cf.Domain.dimension_to_auxiliary ~cf.Domain.coordinate_reference_domain_axes ~cf.Domain.get_coordinate_reference ~cf.Domain.set_coordinate_reference ~cf.Domain.del_coordinate_reference - ~cf.Domain.dimension_coordinate - ~cf.Domain.dimension_to_auxiliary - ~cf.Domain.domain_ancillary - ~cf.Domain.domain_axis ~cf.Domain.domain_axis_key ~cf.Domain.del_domain_axis ~cf.Domain.climatological_time_axes diff --git a/docs/source/class/cf.Field.rst b/docs/source/class/cf.Field.rst index 76f616f30e..30bf221f5b 100644 --- a/docs/source/class/cf.Field.rst +++ b/docs/source/class/cf.Field.rst @@ -272,14 +272,27 @@ Metadata constructs :template: method.rst ~cf.Field.auxiliary_coordinates + ~cf.Field.auxiliary_coordinate + ~cf.Field.cell_connectivities + ~cf.Field.cell_connectivity ~cf.Field.cell_measures + ~cf.Field.cell_measure ~cf.Field.cell_methods + ~cf.Field.cell_method ~cf.Field.coordinates + ~cf.Field.coordinate ~cf.Field.coordinate_references + ~cf.Field.coordinate_reference ~cf.Field.dimension_coordinates + ~cf.Field.dimension_coordinate ~cf.Field.domain_ancillaries + ~cf.Field.domain_ancillary ~cf.Field.domain_axes + ~cf.Field.domain_axis + ~cf.Field.domain_topologies + ~cf.Field.domain_topology ~cf.Field.field_ancillaries + ~cf.Field.field_ancillary ~cf.Field.construct ~cf.Field.construct_item ~cf.Field.construct_key @@ -292,25 +305,16 @@ Metadata constructs ~cf.Field.get_data_axes ~cf.Field.has_data_axes ~cf.Field.set_data_axes - ~cf.Field.auxiliary_coordinate ~cf.Field.auxiliary_to_dimension - ~cf.Field.cell_measure - ~cf.Field.cell_method - ~cf.Field.coordinate - ~cf.Field.coordinate_reference + ~cf.Field.dimension_to_auxiliary ~cf.Field.coordinate_reference_domain_axes ~cf.Field.get_coordinate_reference ~cf.Field.set_coordinate_reference ~cf.Field.del_coordinate_reference - ~cf.Field.dimension_coordinate - ~cf.Field.dimension_to_auxiliary - ~cf.Field.domain_ancillary - ~cf.Field.domain_axis ~cf.Field.domain_axis_key ~cf.Field.domain_axis_position ~cf.Field.domain_mask ~cf.Field.del_domain_axis - ~cf.Field.field_ancillary ~cf.Field.map_axes ~cf.Field.climatological_time_axes diff --git a/docs/source/class/cf.GatheredArray.rst b/docs/source/class/cf.GatheredArray.rst index 61b55b93af..f6693a2a33 100644 --- a/docs/source/class/cf.GatheredArray.rst +++ b/docs/source/class/cf.GatheredArray.rst @@ -33,6 +33,7 @@ cf.GatheredArray ~cf.GatheredArray.source ~cf.GatheredArray.subarray_shapes ~cf.GatheredArray.subarrays + ~cf.GatheredArray.subarray_parameters ~cf.GatheredArray.to_dask_array ~cf.GatheredArray.to_memory diff --git a/docs/source/class/cf.RaggedContiguousArray.rst b/docs/source/class/cf.RaggedContiguousArray.rst index 4c3dd71c07..e1f6fd8dc1 100644 --- a/docs/source/class/cf.RaggedContiguousArray.rst +++ b/docs/source/class/cf.RaggedContiguousArray.rst @@ -35,6 +35,7 @@ cf.RaggedContiguousArray ~cf.RaggedContiguousArray.source ~cf.RaggedContiguousArray.subarray_shapes ~cf.RaggedContiguousArray.subarrays + ~cf.RaggedContiguousArray.subarray_parameters ~cf.RaggedContiguousArray.to_dask_array ~cf.RaggedContiguousArray.to_memory diff --git a/docs/source/class/cf.RaggedIndexedArray.rst b/docs/source/class/cf.RaggedIndexedArray.rst index b1853cfc11..abc4edc74a 100644 --- a/docs/source/class/cf.RaggedIndexedArray.rst +++ b/docs/source/class/cf.RaggedIndexedArray.rst @@ -34,6 +34,7 @@ cf.RaggedIndexedArray ~cf.RaggedIndexedArray.source ~cf.RaggedIndexedArray.subarray_shapes ~cf.RaggedIndexedArray.subarrays + ~cf.RaggedIndexedArray.subarray_parameters ~cf.RaggedIndexedArray.to_dask_array ~cf.RaggedIndexedArray.to_memory diff --git a/docs/source/class/cf.RaggedIndexedContiguousArray.rst b/docs/source/class/cf.RaggedIndexedContiguousArray.rst index 4d913bc082..af27e95277 100644 --- a/docs/source/class/cf.RaggedIndexedContiguousArray.rst +++ b/docs/source/class/cf.RaggedIndexedContiguousArray.rst @@ -34,6 +34,7 @@ cf.RaggedIndexedContiguousArray ~cf.RaggedIndexedContiguousArray.source ~cf.RaggedIndexedContiguousArray.subarray_shapes ~cf.RaggedIndexedContiguousArray.subarrays + ~cf.RaggedIndexedContiguousArray.subarray_parameters ~cf.RaggedIndexedContiguousArray.to_dask_array ~cf.RaggedIndexedContiguousArray.to_memory diff --git a/docs/source/class/cf.RegridOperator.rst b/docs/source/class/cf.RegridOperator.rst index d8361bbd98..dfe5b47d56 100644 --- a/docs/source/class/cf.RegridOperator.rst +++ b/docs/source/class/cf.RegridOperator.rst @@ -44,6 +44,7 @@ cf.RegridOperator ~cf.RegridOperator.src_cyclic ~cf.RegridOperator.src_mask ~cf.RegridOperator.src_shape + ~cf.RegridOperator.src_mesh_location ~cf.RegridOperator.start_index ~cf.RegridOperator.weights ~cf.RegridOperator.weights_file diff --git a/docs/source/field_analysis.rst b/docs/source/field_analysis.rst index e29f4baebb..c3ea848470 100644 --- a/docs/source/field_analysis.rst +++ b/docs/source/field_analysis.rst @@ -1357,18 +1357,27 @@ Spherical source domain Spherical destination domain `Latitude-longitude`_ `Rotated latitude-longitude`_ `Latitude-longitude`_ `Plane projection`_ `Latitude-longitude`_ `Tripolar`_ +`Latitude-longitude`_ `UGRID mesh`_ `Rotated latitude-longitude`_ `Latitude-longitude`_ `Rotated latitude-longitude`_ `Rotated latitude-longitude`_ `Rotated latitude-longitude`_ `Plane projection`_ `Rotated latitude-longitude`_ `Tripolar`_ +`Rotated latitude-longitude`_ `UGRID mesh`_ `Plane projection`_ `Latitude-longitude`_ `Plane projection`_ `Rotated latitude-longitude`_ `Plane projection`_ `Plane projection`_ `Plane projection`_ `Tripolar`_ +`Plane projection`_ `UGRID mesh`_ `Tripolar`_ `Latitude-longitude`_ `Tripolar`_ `Rotated latitude-longitude`_ `Tripolar`_ `Plane projection`_ `Tripolar`_ `Tripolar`_ +`Tripolar`_ `UGRID mesh`_ +`UGRID mesh`_ `Latitude-longitude`_ +`UGRID mesh`_ `Rotated latitude-longitude`_ +`UGRID mesh`_ `Plane projection`_ +`UGRID mesh`_ `Tripolar`_ +`UGRID mesh`_ `UGRID mesh`_ ============================== ============================== The most convenient usage is when the destination domain exists diff --git a/docs/source/installation.rst b/docs/source/installation.rst index e6a335077b..e744fd23ca 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -201,8 +201,10 @@ Required * `cftime `_, version 1.6.2 or newer (note that this package may be installed with netCDF4). -* `cfdm `_, version 1.10.1.2 or up to, - but not including, 1.10.2.0. +* `scipy `_, version 1.10.0 or newer. + +* `cfdm `_, version 1.11.0.0 or up to, + but not including, 1.11.1.0. * `cfunits `_, version 3.3.6 or newer. @@ -253,10 +255,6 @@ environments for which these features are not required. or may be installed from source. -.. rubric:: Convolution filters, derivatives and relative vorticity - -* `scipy `_, version 1.10.0 or newer. - .. rubric:: Subspacing based on N-dimensional construct cells (N > 1) containing a given value diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index b3a715296a..a1941fbc8d 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -120,7 +120,8 @@ elements. The following file types can be read: * All formats of netCDF3 and netCDF4 files can be read, containing - datasets for any version of CF up to and including CF-|version|. + datasets for any version of CF up to and including CF-|version|, + including :ref:`UGRID ` datasets. .. @@ -2671,6 +2672,45 @@ CF-netCDF geometry container variable is automatically created, and the cells encoded with the :ref:`compression ` techniques defined in the CF conventions. +---- + +.. _UGRID-mesh-topologies: + +**UGRID mesh topologies** +------------------------- + +A `UGRID_` mesh topology defines the geospatial topology of cells +arranged in two or three dimensions in real space but indexed by a +single dimension. It explicitly describes the topological +relationships between cells, i.e. spatial relationships which do not +depend on the cell locations, via a mesh of connected nodes. See the +`domain topology construct`_ and `cell connectivity construct`_ +descriptions in the CF data model (from CF-1.11) for more details, +including on how the mesh relates to the cells of the domain. + +.. code-block:: python + :caption: *Inspect a dataset containing a UGRID mesh topology.* + + >>> f = cf.example_field(8) + >>> print(f) + Field: air_temperature (ncvar%ta) + --------------------------------- + Data : air_temperature(time(2), ncdim%nMesh2_face(3)) K + Cell methods : time(2): point (interval: 3600 s) + Dimension coords: time(2) = [2016-01-02 01:00:00, 2016-01-02 11:00:00] gregorian + Auxiliary coords: longitude(ncdim%nMesh2_face(3)) = [-44.0, -44.0, -42.0] degrees_east + : latitude(ncdim%nMesh2_face(3)) = [34.0, 32.0, 34.0] degrees_north + Domain Topology : cell:face(ncdim%nMesh2_face(3), 4) = [[2, ..., --]] + Cell connects : connectivity:edge(ncdim%nMesh2_face(3), 5) = [[0, ..., --]] + +.. + COMMENTED OUT UNTIL THIS WORKS! When a field construct containing a + UGRID mesh topology is written to disk, a CF-netCDF UGRID mesh + topology variable is automatically created which will be shared by + any data variables that can make use of the same mesh. + +---- + .. _Domain-ancillaries: Domain ancillaries @@ -6992,3 +7032,4 @@ if any, are filtered out. .. _geometries: http://cfconventions.org/cf-conventions/cf-conventions.html#geometries .. _Hierarchical groups: http://cfconventions.org/cf-conventions/cf-conventions.html#groups .. _Lossy compression by coordinate subsampling: http://cfconventions.org/cf-conventions/cf-conventions.html#compression-by-coordinate-subsampling +.. _UGRID: https://cfconventions.org/cf-conventions/cf-conventions.html#ugrid-conventions diff --git a/requirements.txt b/requirements.txt index 3c5ef46ec4..a10ba9f71a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,9 @@ netCDF4>=1.5.4 cftime>=1.6.2 numpy>=1.22 -cfdm>=1.10.1.2, <1.10.2.0 +cfdm>=1.11.0.0, <1.11.1.0 psutil>=0.6.0 cfunits>=3.3.6 dask>=2022.12.1 packaging>=20.0 +scipy>=1.10.0 From bf747992fb135935f28b6224abcc3afb2cb6e784 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Wed, 15 Nov 2023 12:30:14 +0000 Subject: [PATCH 02/22] Internal representation and reading of UGRID mesh topologies --- .gitignore | 1 + Changelog.rst | 16 +- MANIFEST.in | 4 +- README.md | 28 +- cf/__init__.py | 29 +- cf/aggregate.py | 522 +- cf/auxiliarycoordinate.py | 12 +- cf/bounds.py | 8 - cf/cellconnectivity.py | 185 + cf/cellmeasure.py | 8 - cf/cfimplementation.py | 15 + cf/constructs.py | 5 +- cf/count.py | 40 +- cf/data/array/__init__.py | 3 + cf/data/array/boundsfromnodesarray.py | 23 + cf/data/array/cellconnectivityarray.py | 25 + cf/data/array/gatheredarray.py | 94 - cf/data/array/mixin/__init__.py | 1 - cf/data/array/mixin/compressedarraymixin.py | 81 + cf/data/array/mixin/raggedarraymixin.py | 97 - cf/data/array/netcdfarray.py | 8 - cf/data/array/pointtopologyarray.py | 21 + cf/data/array/raggedcontiguousarray.py | 14 +- cf/data/array/raggedindexedarray.py | 14 +- cf/data/array/raggedindexedcontiguousarray.py | 17 +- cf/data/array/subsampledarray.py | 12 - cf/data/collapse/dask_collapse.py | 70 +- cf/data/dask_regrid.py | 67 +- cf/data/dask_utils.py | 7 +- cf/data/data.py | 329 +- cf/data/utils.py | 47 + cf/dimensioncoordinate.py | 8 - cf/docstring/docstring.py | 21 + cf/domain.py | 8 - cf/domainancillary.py | 8 - cf/domaintopology.py | 235 + cf/field.py | 1889 +-- cf/fieldancillary.py | 35 +- cf/functions.py | 6 +- cf/index.py | 48 +- cf/interiorring.py | 38 +- cf/interpolationparameter.py | 33 +- cf/list.py | 27 +- cf/mixin/fielddomain.py | 3 +- cf/mixin/propertiesdata.py | 57 - cf/mixin/propertiesdatabounds.py | 59 - cf/mixin2/container.py | 12 +- cf/mixin_container.py | 16 +- cf/nodecountproperties.py | 20 +- cf/partnodecountproperties.py | 25 +- cf/regrid/regrid.py | 743 +- cf/regrid/regridoperator.py | 24 +- cf/test/create_test_files.py | 10707 +--------------- cf/test/create_test_files_2.py | 10440 +++++++++++++++ cf/test/individual_tests.sh | 16 +- cf/test/run_tests.py | 5 + cf/test/test_CellConnectivity.py | 104 + cf/test/test_Data.py | 99 +- cf/test/test_DomainTopology.py | 98 + cf/test/test_Field.py | 47 +- cf/test/test_Maths.py | 24 - cf/test/test_RegridOperator.py | 2 + cf/test/test_UGRID.py | 189 + cf/test/test_aggregate.py | 41 +- cf/test/test_collapse.py | 40 +- cf/test/test_external.py | 2 +- cf/test/test_gathering.py | 2 +- cf/test/test_read_write.py | 12 +- cf/test/test_regrid_mesh.py | 294 + cf/test/test_weights.py | 299 + cf/test/ugrid_global_1.nc | Bin 0 -> 37356 bytes cf/test/ugrid_global_2.nc | Bin 0 -> 116164 bytes cf/tiepointindex.py | 53 +- cf/weights.py | 1903 +++ docs/source/class/cf.AuxiliaryCoordinate.rst | 9 +- docs/source/class/cf.Constructs.rst | 2 + docs/source/class/cf.Data.rst | 8 +- docs/source/class/cf.Domain.rst | 20 +- docs/source/class/cf.Field.rst | 24 +- docs/source/class/cf.GatheredArray.rst | 1 + .../source/class/cf.RaggedContiguousArray.rst | 1 + docs/source/class/cf.RaggedIndexedArray.rst | 1 + .../class/cf.RaggedIndexedContiguousArray.rst | 1 + docs/source/class/cf.RegridOperator.rst | 1 + docs/source/field_analysis.rst | 9 + docs/source/installation.rst | 10 +- docs/source/tutorial.rst | 43 +- requirements.txt | 3 +- 88 files changed, 16409 insertions(+), 13219 deletions(-) create mode 100644 cf/cellconnectivity.py create mode 100644 cf/data/array/boundsfromnodesarray.py create mode 100644 cf/data/array/cellconnectivityarray.py delete mode 100644 cf/data/array/mixin/raggedarraymixin.py create mode 100644 cf/data/array/pointtopologyarray.py create mode 100644 cf/domaintopology.py create mode 100644 cf/test/create_test_files_2.py create mode 100644 cf/test/test_CellConnectivity.py create mode 100644 cf/test/test_DomainTopology.py create mode 100644 cf/test/test_UGRID.py create mode 100644 cf/test/test_regrid_mesh.py create mode 100644 cf/test/test_weights.py create mode 100644 cf/test/ugrid_global_1.nc create mode 100644 cf/test/ugrid_global_2.nc create mode 100644 cf/weights.py diff --git a/.gitignore b/.gitignore index 8c91defd7f..d6604997d1 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ cf/test/dir/ cf/test/*.nc cf/test/*.nca +cf/test/*.cdl cf/test/*.txt # coverage reports from a running the test coverage script diff --git a/Changelog.rst b/Changelog.rst index 44690e6419..33c6a17657 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -3,10 +3,22 @@ version 3.16.0 **2023-??-??** +* Implemented the reading and manipulation of UGRID mesh topologies + (https://github.com/NCAS-CMS/cf-python/issues/696) +* New methods: `cf.Field.cell_connectivity`, + `cf.Field.cell_connectivities` + (https://github.com/NCAS-CMS/cf-python/issues/696) +* New methods: `cf.Field.domain_topology`, + `cf.Field.domain_topologies` + (https://github.com/NCAS-CMS/cf-python/issues/696) +* New methods: `cf.Data.arctan2`, `cf.Data.masked-values` + (https://github.com/NCAS-CMS/cf-python/issues/696) * Fix bug that caused `cf.Field.collapse` to give incorrect results for the "sum", "sum_of_weights" and "sum_of_weights2" methods, only in the case that weights have been requested (https://github.com/NCAS-CMS/cf-python/issues/701) +* Changed dependency: ``1.11.0.0<=cfdm<1.11.1.0`` +* New dependency: ``scipy>=1.10.0`` version 3.15.4 -------------- @@ -283,8 +295,8 @@ version 3.11.0 * Fix for `cf.aggregate` failures when a datum or coordinate conversion parameter has an array value (https://github.com/NCAS-CMS/cf-python/issues/230) -* Allow for regridding using a destination field featuring size 1 dimension(s) - (https://github.com/NCAS-CMS/cf-python/issues/250) +* Allow for regridding using a destination field featuring size 1 + dimension(s) (https://github.com/NCAS-CMS/cf-python/issues/250) * Fix bug that sometimes caused `cf.Field.autocyclic` to fail when setting a construct that is cyclic and has a defined period * Fix bug that sometimes caused a failure when reading PP extra data diff --git a/MANIFEST.in b/MANIFEST.in index b0ad4f679d..b8ba825ad8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -8,6 +8,6 @@ recursive-exclude cf *.o *.so *.a .nfs* recursive-exclude cf/data *.rst prune cf/test recursive-include cf/test __init__.py test_*.py -recursive-include cf/test cfa_test.sh run_tests.py setup_create_field.py create_test_files.py individual_tests.sh -recursive-include cf/test test_file.nc test_file[2-4].nc file.nc file[1-9].nc wgdos_packed.pp extra_data.pp file1.pp *.cdl +recursive-include cf/test cfa_test.sh run_tests.py setup_create_field.py create_test_files*.py individual_tests.sh +recursive-include cf/test test_file.nc test_file[2-4].nc file.nc file[1-9].nc ugrid_global_1.nc ugrid_global_2.nc wgdos_packed.pp extra_data.pp file1.pp *.cdl prune cf/test/dir diff --git a/README.md b/README.md index 2c76aa5913..c6425ed0a1 100644 --- a/README.md +++ b/README.md @@ -85,52 +85,34 @@ The `cf` package uses of its array manipulation and can: * read field constructs from netCDF, CDL, PP and UM datasets, - * create new field constructs in memory, - -* write and append field constructs to netCDF datasets on disk, - +* write and append field and domain constructs to netCDF datasets on disk, +* read, create, and manipulate UGRID mesh topologies, * read, write, and create coordinates defined by geometry cells, - * read netCDF and CDL datasets containing hierarchical groups, - * inspect field constructs, - * test whether two field constructs are the same, - * modify field construct metadata and data, - * create subspaces of field constructs, - * write field constructs to netCDF datasets on disk, - * incorporate, and create, metadata stored in external files, - * read, write, and create data that have been compressed by convention (i.e. ragged or gathered arrays, or coordinate arrays compressed by subsampling), whilst presenting a view of the data in its uncompressed form, - * combine field constructs arithmetically, - * manipulate field construct data by arithmetical and trigonometrical operations, - -* perform statistical collapses on field constructs, - +* perform weighted statistical collapses on field constructs, + including those with geometry cells and UGRID mesh topologies, * perform histogram, percentile and binning operations on field constructs, - * regrid field constructs with (multi-)linear, nearest neighbour, first- and second-order conservative and higher order patch recovery - methods, - + methods, to and from structured and unstructured grids, * apply convolution filters to field constructs, - * create running means from field constructs, - * apply differential operators to field constructs, - * create derived quantities (such as relative vorticity). Visualization diff --git a/cf/__init__.py b/cf/__init__.py index 27ae8680af..9c192d0d24 100644 --- a/cf/__init__.py +++ b/cf/__init__.py @@ -73,9 +73,9 @@ """ -__Conventions__ = "CF-1.10" -__date__ = "2023-10-10" -__version__ = "3.15.4" +__Conventions__ = "CF-1.11" +__date__ = "2023-??-??" +__version__ = "3.16.0" _requires = ( "numpy", @@ -86,6 +86,7 @@ "psutil", "dask", "packaging", + "scipy", ) x = ", ".join(_requires) @@ -144,6 +145,11 @@ except ImportError as error1: raise ImportError(_error0 + str(error1)) +try: + import scipy +except ImportError as error1: + raise ImportError(_error0 + str(error1)) + # Check the version of packaging _minimum_vn = "20.0" if Version(packaging.__version__) < Version(_minimum_vn): @@ -193,8 +199,8 @@ ) # Check the version of cfdm -_minimum_vn = "1.10.1.2" -_maximum_vn = "1.10.2.0" +_minimum_vn = "1.11.0.0" +_maximum_vn = "1.11.1.0" _cfdm_version = Version(cfdm.__version__) if not Version(_minimum_vn) <= _cfdm_version < Version(_maximum_vn): raise RuntimeError( @@ -218,6 +224,14 @@ f"or later. Got {platform.python_version()}" ) +# Check the version of scipy +_minimum_vn = "1.10.0" +if Version(scipy.__version__) < Version(_minimum_vn): + raise RuntimeError( + f"Bad scipy version: cf requires scipy>={_minimum_vn}. " + f"Got {scipy.__version__} at {scipy.__file__}" + ) + from .constructs import Constructs from .mixin import Coordinate @@ -248,18 +262,23 @@ from .dimensioncoordinate import DimensionCoordinate from .auxiliarycoordinate import AuxiliaryCoordinate from .coordinatereference import CoordinateReference +from .cellconnectivity import CellConnectivity from .cellmethod import CellMethod from .cellmeasure import CellMeasure from .domainancillary import DomainAncillary from .domainaxis import DomainAxis +from .domaintopology import DomainTopology from .fieldancillary import FieldAncillary from .field import Field from .data import Data from .data.array import ( + BoundsFromNodesArray, + CellConnectivityArray, CFANetCDFArray, FullArray, GatheredArray, NetCDFArray, + PointTopologyArray, RaggedContiguousArray, RaggedIndexedArray, RaggedIndexedContiguousArray, diff --git a/cf/aggregate.py b/cf/aggregate.py index 2771fbd7cb..728e5aebea 100644 --- a/cf/aggregate.py +++ b/cf/aggregate.py @@ -224,6 +224,8 @@ class _Meta: "Nd_coordinates", "Cell_measures", "Domain_ancillaries", + "Domain_topologies", + "Cell_connectivities", "Field_ancillaries", ), ) @@ -335,7 +337,11 @@ def __init__( self.key_to_identity = {} self.all_field_anc_identities = set() - self.all_domain_anc_identities = set() + # self.all_domain_anc_identities = set() + self.all_identities = { + "domain_ancillary": set(), + "field_ancillary": set(), + } self.message = "" self.info = info @@ -372,7 +378,7 @@ def __init__( # Parent field or domain # ------------------------------------------------------------ self.field = f - self.has_data = f.has_data() + self.has_field_data = f.has_data() self.identity = f.identity( strict=strict_identities, relaxed=relaxed_identities and not ncvar_identities, @@ -395,7 +401,7 @@ def __init__( return if self.identity is None: - if not allow_no_identity and self.has_data: + if not allow_no_identity and self.has_field_data: if info: self.message = ( "no identity; consider setting relaxed_identities=True" @@ -665,8 +671,11 @@ def __init__( "field_ancillary", todict=True ) for key, field_anc in field_ancs.items(): + if not self.has_data(field_anc): + return + # Find this field ancillary's identity - identity = self.field_ancillary_has_identity_and_data(field_anc) + identity = self.get_identity(field_anc) if identity is None: return @@ -722,11 +731,13 @@ def __init__( if anc is None: continue + if not self.has_data(anc): + return + # Set this domain ancillary's identity - identity = (ref_identity, term) - identity = self.domain_ancillary_has_identity_and_data( - anc, identity - ) + identity = self.get_identity(anc, (ref_identity, term)) + if identity is None: + return # Find the canonical units units = self.canonical_units( @@ -758,6 +769,9 @@ def __init__( if key in ancs_in_refs: continue + if not self.has_data(anc): + return + # Find this domain ancillary's identity identity = self.domain_ancillary_has_identity_and_data(anc) if identity is None: @@ -791,12 +805,11 @@ def __init__( self.msr = {} info_msr = {} for key, msr in f.cell_measures(todict=True).items(): - if not self.cell_measure_has_measure(msr): + if not self.has_measure(msr): return - if ( - not msr.nc_get_external() - and not self.cell_measure_has_data_and_units(msr) + if not msr.nc_get_external() and not ( + self.has_data(msr) and self.has_units(msr) ): return @@ -859,6 +872,111 @@ def __init__( "external": tuple([v["external"] for v in value]), } + # ------------------------------------------------------------ + # Domain topologies + # ------------------------------------------------------------ + self.domain_topology = {} + info_topology = {} + for key, topology in f.domain_topologies(todict=True).items(): + if not (self.has_cell(topology) and self.has_data(topology)): + return + + # Find axes' identities + axes = tuple( + [self.axis_to_id[axis] for axis in construct_axes[key]] + ) + + identity = topology.get_cell() + + # Find the canonical axes + canonical_axes = self.canonical_axes(topology, identity, axes) + canonical_axis = canonical_axes[0] + + if canonical_axis in info_topology: + # Check for ambiguous domain topologies, i.e. those + # which span the same axis. + if info: + self.message = f"duplicate {topology!r}" + + return + else: + info_topology[canonical_axis] = [] + + info_topology[canonical_axes[0]].append( + { + "cell": identity, + "key": key, + "axes": axes, + "canonical_axes": canonical_axes, + } + ) + + # For each domain topology's canonical axis, sort the + # information by axis identities. + for units, value in info_topology.items(): + self.domain_topology[canonical_axis] = { + "cell": tuple([v["cell"] for v in value]), + "keys": tuple([v["key"] for v in value]), + "axes": tuple([v["axes"] for v in value]), + "canonical_axes": tuple([v["canonical_axes"] for v in value]), + } + + # ------------------------------------------------------------ + # Cell connectivities + # ------------------------------------------------------------ + self.cell_connectivity = {} + info_connectivity = {} + for key, connectivity in f.cell_connectivities(todict=True).items(): + if not ( + self.has_connectivity(connectivity) + and self.has_data(connectivity) + ): + return + + # Find axes' identities + axes = tuple( + [self.axis_to_id[axis] for axis in construct_axes[key]] + ) + + identity = connectivity.get_connectivity() + + # Find the canonical axes + canonical_axes = self.canonical_axes(connectivity, identity, axes) + canonical_axis = canonical_axes[0] + + if canonical_axis in info_connectivity: + # Check for ambiguous cell connectivities, i.e. those + # which span the same axis with the same connectivity + # type. + for value in info_connectivity[canonical_axis]: + if identity == value["connectivity"]: + if info: + self.message = f"duplicate {connectivity!r}" + + return + else: + info_connectivity[canonical_axis] = [] + + info_connectivity[canonical_axes[0]].append( + { + "connectivity": identity, + "key": key, + "axes": axes, + "canonical_axes": canonical_axes, + } + ) + + # For each cell connectivity's canonical axis, sort the + # information by cell type. + for axis, value in info_connectivity.items(): + value.sort(key=itemgetter("connectivity")) + self.cell_connectivity[axis] = { + "connectivity": tuple([v["connectivity"] for v in value]), + "keys": tuple([v["key"] for v in value]), + "axes": tuple([v["axes"] for v in value]), + "canonical_axes": tuple([v["canonical_axes"] for v in value]), + } + # ------------------------------------------------------------ # Properties and attributes # ------------------------------------------------------------ @@ -1303,48 +1421,113 @@ def canonical_cell_methods(self, rtol=None, atol=None): return cms - def cell_measure_has_data_and_units(self, msr): - """True only if a cell measure has both data and units. + def has_cell(self, topology): + """Whether a domain topology construct has a connectivity type. + + .. versionadded:: 3.16.0 :Parameters: - msr: `CellMeasure` + topology: `DomainTopology` + The construct to test. :Returns: `bool` + `True` if the construct has a cell type, otherwise + `False`. """ - if not msr.Units: + if topology.get_cell(None) is None: if self.info: - self.message = f"{msr.identity()!r} cell measure has no units" + self.message = ( + f"{topology.identity()!r} " + "domain topology construct has no cell type" + ) return False - if not msr.has_data(): + return True + + def has_connectivity(self, connectivity): + """Whether a cell connectivity construct has a connectivity type. + + .. versionadded:: 3.16.0 + + :Parameters: + + connectivity: `CellConnectivity` + The construct to test. + + :Returns: + + `bool` + `True` if the construct has a connectivity type, + otherwise `False`. + + """ + if connectivity.get_connectivity(None) is None: if self.info: - self.message = f"{msr.identity()!r} cell measure has no data" + self.message = ( + f"{connectivity.identity()!r} " + "cell connectivity construct has no connectivity type" + ) return False return True - def cell_measure_has_measure(self, msr): - """True only if a cell measure has a measure. + def has_measure(self, msr): + """Whether a cell measure construct has a measure. + + .. versionadded:: 3.16.0 :Parameters: msr: `CellMeasure` + The construct to test. :Returns: `bool` + `True` if the construct has a measure, otherwise + `False`. """ - if not msr.get_measure(False): + if msr.get_measure(None) is None: if self.info: self.message = ( - f"{msr.identity()!r} cell measure has no measure" + f"{msr.identity()!r} " + "cell measure construct has no measure" + ) + + return False + + return True + + def has_units(self, construct): + """Whether a construct has units. + + .. versionadded:: 3.16.0 + + :Parameters: + + construct: Construct + The construct to test. + + :Returns: + + `bool` + `True` if the construct has units, otherwise `False`. + + """ + if not construct.Units: + if self.info: + construct_type = construct.construct_type + self.message = ( + f"{construct.identity()!r} " + f"{construct_type.replace('_', ' ')} construct " + "has no units" ) return False @@ -1366,8 +1549,8 @@ def coord_has_identity_and_data(self, coord, axes=None): :Returns: `str` or `None` - The coordinate construct's identity, or `None` if there is - no identity and/or no data. + The coordinate construct's identity, or `None` if + there is no identity and/or no data. """ identity = coord.identity( @@ -1396,47 +1579,33 @@ def coord_has_identity_and_data(self, coord, axes=None): if self.info: self.message = f"{coord!r} has no identity or no data" - def field_ancillary_has_identity_and_data(self, anc): - """Return a field ancillary's identity if it has one and has - data. + def has_data(self, construct): + """Whether a construct has data. + + .. versionadded:: 3.16.0 :Parameters: - coord: `FieldAncillary` + construct: Construct + The construct to test. :Returns: - `str` or `None` - The coordinate construct's identity, or `None` if - there is no identity and/or no data. + `bool` + `True` if the construct has data, otherwise `False`. """ - identity = anc.identity( - strict=self.strict_identities, - relaxed=self.relaxed_identities and not self.ncvar_identities, - nc_only=self.ncvar_identities, - default=None, - ) - - if identity is not None: - all_field_anc_identities = self.all_field_anc_identities - - if identity in all_field_anc_identities: - if self.info: - self.message = f"multiple {identity!r} field ancillaries" - - return + if not construct.has_data(): + if self.info: + construct_type = construct.construct_type + self.message = ( + f"{construct.identity()!r} " + f"{construct_type.replace('_', ' ')} has no data" + ) - if anc.has_data(): - all_field_anc_identities.add(identity) - return identity + return False - # Still here? - if self.info: - self.message = ( - f"{anc.identity()!r} field ancillary has no identity or " - "no data" - ) + return True def coordinate_reference_signatures(self, refs): """List the structural signatures of given coordinate @@ -1477,61 +1646,58 @@ def coordinate_reference_signatures(self, refs): return signatures - def domain_ancillary_has_identity_and_data(self, anc, identity=None): - """Return a domain ancillary's identity if it has one and has - data. + def get_identity(self, construct, identity=None): + """Return a construct's identity if it has one. + + .. versionadded:: 3.16.0 :Parameters: - anc: cf.DomainAncillary + construct: Construct + The construct to test. identity: optional :Returns: `str` or `None` - The domain ancillary identity, or None if there is no - identity and/or no data. + The construct identity, or `None` if there isn't one. """ if identity is not None: - anc_identity = identity + construct_identity = identity else: - anc_identity = anc.identity( + construct_identity = construct.identity( strict=self.strict_identities, relaxed=self.relaxed_identities and not self.ncvar_identities, nc_only=self.ncvar_identities, default=None, ) - if anc_identity is None: + construct_type = construct.construct_type + if construct_identity is None: if self.info: self.message = ( - f"{anc.identity()!r} domain ancillary has no identity" + f"{construct.identity()!r} " + f"{construct_type.replace('_', ' ')} has no identity" ) return - all_domain_anc_identities = self.all_domain_anc_identities - - if anc_identity in all_domain_anc_identities: - if self.info: - self.message = ( - f"multiple {anc.identity()!r} domain ancillaries" - ) - return - - if not anc.has_data(): - if self.info: - self.message = ( - f"{anc.identity()!r} domain ancillary has no data" - ) + all_identities = self.all_identities.get(construct_type) + if all_identities is not None: + if construct_identity in all_identities: + if self.info: + self.message = ( + f"multiple {construct.identity()!r} " + f"{construct_type.replace('_', ' ')} constructs" + ) - return + return - all_domain_anc_identities.add(anc_identity) + all_identities.add(construct_identity) - return anc_identity + return construct_identity @_manage_log_level_via_verbose_attr def print_info(self, signature=True): @@ -1605,16 +1771,11 @@ def structural_signature(self): Units = self.units.units Cell_methods = self.cell_methods - Data = self.has_data - # signature_append = signature.append + Data = self.has_field_data # Properties - # signature_append(('Properties', self.properties)) Properties = self.properties - # standard_error_multiplier - # signature_append(('standard_error_multiplier', - # f.get_property('standard_error_multiplier', None))) standard_error_multiplier = f.get_property( "standard_error_multiplier", None ) @@ -1715,6 +1876,30 @@ def structural_signature(self): ] Domain_ancillaries = tuple(x) + # Domain topologies + topology = self.domain_topology + x = [ + ( + identity, + ("cell", topology[identity]["cell"]), + ("axes", topology[identity]["canonical_axes"]), + ) + for identity in sorted(topology) + ] + Domain_topologies = tuple(x) + + # Cell connectivities + connectivity = self.cell_connectivity + x = [ + ( + identity, + ("connectivity", connectivity[identity]["connectivity"]), + ("axes", connectivity[identity]["canonical_axes"]), + ) + for identity in sorted(connectivity) + ] + Cell_connectivities = tuple(x) + # Field ancillaries field_anc = self.field_anc x = [ @@ -1747,6 +1932,8 @@ def structural_signature(self): Nd_coordinates=Nd_coordinates, Cell_measures=Cell_measures, Domain_ancillaries=Domain_ancillaries, + Domain_topologies=Domain_topologies, + Cell_connectivities=Cell_connectivities, Field_ancillaries=Field_ancillaries, ) @@ -2913,7 +3100,7 @@ def aggregate( # ] # }, # 'auxiliary_coordinate': {}, - # 'cell_interface': {}, + # 'cell_connectivity': {}, # 'cell_measure': {}, # 'domain_ancillary': {}, # 'domain_topology': {}, @@ -2940,8 +3127,8 @@ def aggregate( "cell_measure": {}, "domain_ancillary": {}, "field_ancillary": {}, - "domain_topology": {}, # UGRID - "cell_interface": {}, # UGRID + "domain_topology": {}, + "cell_connectivity": {}, } m0 = m[0].copy() @@ -3365,12 +3552,16 @@ def _create_hash_and_first_values( sort_indices = slice(None, None, -1) else: sort_indices = slice(None) - + elif identity in m.domain_topology: + # ... or which doesn't have a dimension coordinate but + # does have a domain topology ... + sort_indices = slice(None) + needs_sorting = False else: # ... or which doesn't have a dimension coordinate but # does have one or more 1-d auxiliary coordinates aux = m_axis_identity["keys"][0] - # '.data.compute()' is faster than '.array' + # Note: '.data.compute()' is faster than '.array' sort_indices = np.argsort(constructs[aux].data.compute()) m_sort_keys[axis] = aux needs_sorting = True @@ -3541,6 +3732,76 @@ def _create_hash_and_first_values( msr["hash_values"] = hash_values + # ------------------------------------------------------------ + # Domain topologies + # ------------------------------------------------------------ + if donotchecknonaggregatingaxes: + for topology in m.domain_topology.values(): + topology["hash_values"] = [(None,) * len(topology["keys"])] + else: + for topology in m.domain_topology.values(): + hash_values = [] + for key, canonical_axes in zip( + topology["keys"], topology["canonical_axes"] + ): + construct = constructs[key] + sort_indices, needs_sorting = _sort_indices( + m, canonical_axes + ) + + # Get the hash of the data array + h = _get_hfl( + construct, + _no_units, + sort_indices, + needs_sorting, + False, + False, + hfl_cache, + rtol, + atol, + ) + + hash_values.append((h,)) + + topology["hash_values"] = hash_values + + # ------------------------------------------------------------ + # Cell connectivities + # ------------------------------------------------------------ + if donotchecknonaggregatingaxes: + for connectivity in m.cell_connectivity.values(): + connectivity["hash_values"] = [ + (None,) * len(connectivity["keys"]) + ] + else: + for connectivity in m.cell_connectivity.values(): + hash_values = [] + for key, canonical_axes in zip( + connectivity["keys"], connectivity["canonical_axes"] + ): + construct = constructs[key] + sort_indices, needs_sorting = _sort_indices( + m, canonical_axes + ) + + # Get the hash of the data array + h = _get_hfl( + construct, + _no_units, + sort_indices, + needs_sorting, + False, + False, + hfl_cache, + rtol, + atol, + ) + + hash_values.append((h,)) + + connectivity["hash_values"] = hash_values + # ------------------------------------------------------------ # Field ancillaries # ------------------------------------------------------------ @@ -3921,6 +4182,42 @@ def _hash_values(m): groups_of_fields.append([m1]) continue + # Still here? Then check the domain topologies + topology = m0.domain_topology + for axis in topology: + for axes, hash_value0, hash_value1 in zip( + topology[axis]["axes"], + topology[axis]["hash_values"], + m1.domain_topology[axis]["hash_values"], + ): + if a_identity not in axes and hash_value0 != hash_value1: + # There is a matching pair of domain + # topologies that does not span the + # aggregating axis and they have different + # data array values + ok = False + break + + # Still here? Then check the cell connectivities + connectivity = m0.cell_connectivity + for axis in connectivity: + for axes, hash_value0, hash_value1 in zip( + connectivity[axis]["axes"], + connectivity[axis]["hash_values"], + m1.cell_connectivity[axis]["hash_values"], + ): + if a_identity not in axes and hash_value0 != hash_value1: + # There is a matching pair of cell + # connectivities that does not span the + # aggregating axis and they have different + # data array values + ok = False + break + + if not ok: + groups_of_fields.append([m1]) + continue + # Still here? Then set the identity of the aggregating # axis m0.a_identity = a_identity @@ -4137,39 +4434,24 @@ def _ok_coordinate_arrays( f"contiguous={bool(contiguous)} and " f"{m.axis[axis]['ids'][dim_coord_index]!r} " "dimension coordinate cells do not match " - "the cell spacing condition between fields " - f"({data1!r} - {data0!r} = {dim_diff!r}) " + "the cell spacing condition between fields: " + f"{data1!r} - {data0!r} = {dim_diff!r} " f"!= {diff!r}" ) return False else: # ------------------------------------------------------------ - # The aggregating axis does not have a dimension coordinate, - # but it does have at least one 1-d auxiliary coordinate. + # The aggregating axis does not have a dimension coordinate # ------------------------------------------------------------ - # Check for duplicate auxiliary coordinate values - for i, identity in enumerate(meta[0].axis[axis]["ids"]): - set_of_1d_aux_coord_values = set() - number_of_1d_aux_coord_values = 0 - for m in meta: - aux = m.axis[axis]["keys"][i] - # '.data.compute()' is faster than '.array' - array = m.field.constructs[aux].data.compute() - set_of_1d_aux_coord_values.update(array) - number_of_1d_aux_coord_values += array.size - if ( - len(set_of_1d_aux_coord_values) - != number_of_1d_aux_coord_values - ): - if info: - meta[0].message = ( - f"no {identity!r} dimension coordinates and " - f"{identity!r} auxiliary coordinates have " - "duplicate values" - ) + if axis in m.domain_topology or axis in m.cell_connectivity: + if info: + meta[0].message = ( + f"can't aggregate along the {axis!r} mesh topology " + "discrete axis" + ) - return False + return False # ---------------------------------------------------------------- # Still here? Then the aggregating axis does not overlap between @@ -4429,7 +4711,7 @@ def _aggregate_2_fields( # Update the size of the aggregating axis in the output parent # construct # ---------------------------------------------------------------- - if m0.has_data: + if m0.has_field_data: # ---------------------------------------------------------------- # Insert the data array from parent1 into the data array of # parent0 diff --git a/cf/auxiliarycoordinate.py b/cf/auxiliarycoordinate.py index fba4082529..46c28f0879 100644 --- a/cf/auxiliarycoordinate.py +++ b/cf/auxiliarycoordinate.py @@ -36,9 +36,7 @@ class AuxiliaryCoordinate( {{netCDF variable}} - The netCDF variable group structure may be accessed with the - `nc_set_variable`, `nc_get_variable`, `nc_variable_groups`, - `nc_clear_variable_groups` and `nc_set_variable_groups` methods. + {{netCDF UGRID node coordinate}} """ @@ -47,11 +45,3 @@ def __new__(cls, *args, **kwargs): instance = super().__new__(cls) instance._Bounds = Bounds return instance - - def __repr__(self): - """Called by the `repr` built-in function. - - x.__repr__() <==> repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " >>> i.classes() {'AuxiliaryCoordinate': cf.auxiliarycoordinate.AuxiliaryCoordinate, + 'BoundsFromNodesArray': cf.data.array.boundsfromnodesarray.BoundsFromNodesArray, + 'CellConnectivity': cf.cellconnectivity.CellConnectivity, + 'CellConnectivityArray': cf.data.array.cellconnectivityarray.CellConnectivityArray, 'CellMeasure': cf.cellmeasure.CellMeasure, 'CellMethod': cf.cellmethod.CellMethod, 'CFANetCDFArray': cf.data.array.cfanetcdfarray.CFANetCDFArray, @@ -202,6 +215,7 @@ def implementation(): 'Domain': cf.domain.Domain, 'DomainAncillary': cf.domainancillary.DomainAncillary, 'DomainAxis': cf.domainaxis.DomainAxis, + 'DomainTopology': cf.domaintopology.DomainTopology, 'Field': cf.field.Field, 'FieldAncillary': cf.fieldancillary.FieldAncillary, 'Bounds': cf.bounds.Bounds, @@ -217,6 +231,7 @@ def implementation(): 'Data': cf.data.data.Data, 'GatheredArray': cf.data.array.gatheredarray.GatheredArray, 'NetCDFArray': cf.data.array.netcdfarray.NetCDFArray, + 'PointTopologyArray': , 'RaggedContiguousArray': cf.data.array.raggedcontiguousarray.RaggedContiguousArray, 'RaggedIndexedArray': cf.data.array.raggedindexedarray.RaggedIndexedArray, 'RaggedIndexedContiguousArray': cf.data.array.raggedindexedcontiguousarray.RaggedIndexedContiguousArray, diff --git a/cf/constructs.py b/cf/constructs.py index 95d068f18d..29dbbc9dfc 100644 --- a/cf/constructs.py +++ b/cf/constructs.py @@ -47,6 +47,8 @@ class Constructs(cfdm.Constructs): * dimension coordinate constructs * domain ancillary constructs * domain axis constructs + * domain topology constructs + * cell connectivity constructs * cell method constructs * field ancillary constructs @@ -126,9 +128,6 @@ def _flip(self, axes): ] construct.flip(iaxes, inplace=True) - # ---------------------------------------------------------------- - # Methods - # ---------------------------------------------------------------- def close(self): """Close all files referenced by the metadata constructs. diff --git a/cf/count.py b/cf/count.py index c2db119324..090c14b6b3 100644 --- a/cf/count.py +++ b/cf/count.py @@ -4,42 +4,4 @@ class Count(mixin.PropertiesData, cfdm.Count): - """A count variable required to uncompress a ragged array. - - A collection of features stored using a contiguous ragged array - combines all features along a single dimension (the sample - dimension) such that each feature in the collection occupies a - contiguous block. - - The information needed to uncompress the data is stored in a count - variable that gives the size of each block. - - **NetCDF interface** - - The netCDF variable name of the count variable may be accessed - with the `nc_set_variable`, `nc_get_variable`, `nc_del_variable` - and `nc_has_variable` methods. - - The name of the netCDF dimension spanned by the count variable's - data may be accessed with the `nc_set_dimension`, - `nc_get_dimension`, `nc_del_dimension` and `nc_has_dimension` - methods. - - The name of the netCDF sample dimension spanned by the compressed - data (that is stored in the "sample_dimension" netCDF attribute - and which does not correspond to a domain axis construct) may be - accessed with the `nc_set_sample_dimension`, - `nc_get_sample_dimension`, `nc_del_sample_dimension` and - `nc_has_sample_dimension` methods. - - .. versionadded:: 3.0.0 - - """ - - def __repr__(self): - """Called by the `repr` built-in function. - - x.__repr__() <==> repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - .. versionadded:: 3.0.0 - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - .. versionadded:: 3.0.0 - - """ - return super().__repr__().replace("<", " repr(x) - - .. versionadded:: 3.0.0 - - """ - return super().__repr__().replace("<", " repr(x) - - .. versionadded:: 3.0.0 - - """ - return super().__repr__().replace("<", " repr(x) - - .. versionadded:: 3.0.0 - - """ - return super().__repr__().replace("<", "= + masked in the destination grid definition; or b) ``w_ji >= min_weight`` for those masked source grid cells i for which ``w_ji > 0``. **Conservative first-order regridding** Destination grid cell j will only be masked if a) it is - masked in destination grid definition; or b) The sum of - ``w_ji`` for all non-masked source grid cells i is + masked in the destination grid definition; or b) the sum + of ``w_ji`` for all non-masked source grid cells i is strictly less than *min_weight*. :Returns: @@ -168,8 +180,9 @@ def regrid( # are the gathered regridding axes and whose left-hand dimension # represent of all the other dimensions. # ---------------------------------------------------------------- + n_src_axes = len(src_shape) a = a.transpose(axis_order) - non_regrid_shape = a.shape[: a.ndim - len(src_shape)] + non_regrid_shape = a.shape[: a.ndim - n_src_axes] dst_size, src_size = weights.shape a = a.reshape(-1, src_size) a = a.T @@ -200,7 +213,7 @@ def regrid( if variable_mask or (src_mask is None and ref_src_mask.any()): raise ValueError( f"Can't regrid with the {method!r} method when the source " - f"data mask varies over different {len(src_shape)}-d " + f"data mask varies over different {n_src_axes}-d " "regridding slices" ) @@ -279,10 +292,40 @@ def regrid( a = a.T a = a.reshape(non_regrid_shape + tuple(dst_shape)) + n_dst_axes = len(dst_shape) + + if n_src_axes == n_dst_axes: + pass + elif n_src_axes == 1 and n_dst_axes == 2: + # The regridding operation increased the number of data axes + # by 1 => modify 'axis_order' to contain the new axis. + # + # E.g. UGRID -> regular lat-lon could change 'axis_order' from + # [0,2,1] to [0,3,1,2] + raxis = axis_order[-1] + axis_order = [ + i if i <= raxis else i + n_dst_axes - 1 for i in axis_order + ] + axis_order[-1:] = range(raxis, raxis + n_dst_axes) + elif n_src_axes == 2 and n_dst_axes == 1: + # The regridding operation decreased the number of data axes + # by 1 => modify 'axis_order' to remove the removed axis. + # + # E.g. regular lat-lon -> UGRID could change 'axis_order' from + # [0,2,4,5,1,3] to [0,2,3,4,1], or [0,2,4,5,3,1] to + # [0,1,3,4,2] + raxis0, raxis = axis_order[-2:] + axis_order = [i if i <= raxis else i - 1 for i in axis_order[:-1]] + else: + raise ValueError( + f"Can't (yet) regrid from {n_src_axes} dimensions to " + f"{n_dst_axes} dimensions" + ) + d = {k: i for i, k in enumerate(axis_order)} axis_reorder = [i for k, i in sorted(d.items())] - a = a.transpose(axis_reorder) + a = a.transpose(axis_reorder) return a @@ -421,10 +464,10 @@ def _regrid( # destination cell j that intersects with unmasked cells # of the source grid. # - # D_j = 1 - w_i1j - ... - w_iNj + # D_j = w_i1j + ... + w_iNj # - # where w_iXj is the unmasked weight for masked source - # cell i and destination cell j. + # where each w_iXj is the weight for unmasked source cell + # i and destination cell j. dst_size = weights.shape[0] if dst_mask is None: dst_mask = np.zeros((dst_size,), dtype=bool) @@ -451,7 +494,7 @@ def _regrid( continue w = data[i0:i1] - D_j = 1 - w[mask].sum() + D_j = w[~mask].sum() w = w / D_j w[mask] = 0 data[i0:i1] = w diff --git a/cf/data/dask_utils.py b/cf/data/dask_utils.py index fafbf82501..63291a4d96 100644 --- a/cf/data/dask_utils.py +++ b/cf/data/dask_utils.py @@ -9,6 +9,7 @@ import dask.array as da import numpy as np from dask.core import flatten +from scipy.ndimage import convolve1d from ..cfdatetime import dt, dt2rt, rt2dt from ..functions import atol as cf_atol @@ -128,12 +129,6 @@ def cf_contains(a, value): return np.array(value in a).reshape((1,) * a.ndim) -try: - from scipy.ndimage import convolve1d -except ImportError: - pass - - def cf_convolve1d(a, window=None, axis=-1, origin=0): """Calculate a 1-d convolution along the given axis. diff --git a/cf/data/data.py b/cf/data/data.py index 840be8c204..1b47fa2c5c 100644 --- a/cf/data/data.py +++ b/cf/data/data.py @@ -18,6 +18,7 @@ from dask.base import collections_to_dsk, is_dask_collection, tokenize from dask.highlevelgraph import HighLevelGraph from dask.optimization import cull +from scipy.sparse import issparse from ..cfdatetime import dt as cf_dt from ..constants import masked as cf_masked @@ -178,6 +179,7 @@ def __init__( copy=True, dtype=None, mask=None, + mask_value=None, to_memory=False, init_options=None, _use_array=True, @@ -266,6 +268,12 @@ def __init__( .. versionadded:: 3.0.5 + mask_value: scalar array_like + Mask *array* where it is equal to *mask_value*, using + numerically tolerant floating point equality. + + .. versionadded:: 3.16.0 + {{init source: optional}} hardmask: `bool`, optional @@ -392,6 +400,8 @@ def __init__( # No data has been set return + sparse_array = issparse(array) + try: ndim = array.ndim except AttributeError: @@ -482,8 +492,18 @@ def __init__( # Apply a mask if mask is not None: + if sparse_array: + raise ValueError("Can't mask sparse array") + self.where(mask, cf_masked, inplace=True) + # Apply masked values + if mask_value is not None: + if sparse_array: + raise ValueError("Can't mask sparse array") + + self.masked_values(mask_value, inplace=True) + @property def dask_compressed_array(self): """Returns a dask array of the compressed data. @@ -766,14 +786,6 @@ def __bool__(self): return bool(self.to_dask_array()) - def __repr__(self): - """Called by the `repr` built-in function. - - x.__repr__() <==> repr(x) - - """ - return super().__repr__().replace("<", ">> d.compute() array([1., 2., 3.]) + >>> from scipy.sparse import csr_array + >>> d = cf.Data(csr_array((2, 3))) + >>> d.compute() + <2x3 sparse array of type '' + with 0 stored elements in Compressed Sparse Row format> + >>>: d.array + array([[0., 0., 0.], + [0., 0., 0.]]) + >>> d.compute().toarray() + array([[0., 0., 0.], + [0., 0., 0.]]) + """ a = self.to_dask_array().compute() @@ -3734,6 +3760,7 @@ def _regrid( from .dask_regrid import regrid, regrid_weights shape = self.shape + ndim = self.ndim src_shape = tuple(shape[i] for i in regrid_axes) if src_shape != operator.src_shape: raise ValueError( @@ -3753,11 +3780,44 @@ def _regrid( ] dx = dx.rechunk(chunks) - # Define the regridded chunksizes - regridded_chunks = tuple( - (regridded_sizes[i],) if i in regridded_sizes else c - for i, c in enumerate(dx.chunks) - ) + # Define the regridded chunksizes (allowing for the regridded + # data to have more/fewer axes). + regridded_chunks = [] # The 'chunks' parameter to `map_blocks` + drop_axis = [] # The 'drop_axis' parameter to `map_blocks` + new_axis = [] # The 'new_axis' parameter to `map_blocks` + n = 0 + for i, c in enumerate(dx.chunks): + if i in regridded_sizes: + sizes = regridded_sizes[i] + n_sizes = len(sizes) + if not n_sizes: + drop_axis.append(i) + continue + + regridded_chunks.extend(sizes) + if n_sizes > 1: + new_axis.extend(range(n + 1, n + n_sizes)) + n += n_sizes - 1 + else: + regridded_chunks.extend(c) + + n += 1 + + # Update the axis identifiers. + # + # This is necessary when regridding changes the number of data + # dimensions (e.g. as happens when regridding a mesh topology + # axis to/from separate lat and lon axes). + if new_axis: + axes = list(self._axes) + for i in new_axis: + axes.insert(i, new_axis_identifier(tuple(axes))) + + self._axes = axes + elif drop_axis: + axes = self._axes + axes = [axes[i] for i in range(ndim) if i not in drop_axis] + self._axes = axes # Set the output data type if method in ("nearest_dtos", "nearest_stod"): @@ -3765,7 +3825,7 @@ def _regrid( else: dst_dtype = float - non_regrid_axes = [i for i in range(self.ndim) if i not in regrid_axes] + non_regrid_axes = [i for i in range(ndim) if i not in regrid_axes] src_mask = operator.src_mask if src_mask is not None: @@ -3790,6 +3850,8 @@ def _regrid( weights_dst_mask=weights_dst_mask, ref_src_mask=src_mask, chunks=regridded_chunks, + drop_axis=drop_axis, + new_axis=new_axis, meta=np.array((), dtype=dst_dtype), ) @@ -5094,7 +5156,9 @@ def array(self): """ array = self.compute().copy() - if not isinstance(array, np.ndarray): + if issparse(array): + array = array.toarray() + elif not isinstance(array, np.ndarray): array = np.asanyarray(array) return array @@ -5205,7 +5269,6 @@ def mask(self): return mask_data_obj - # `arctan2`, AT2 seealso @_inplace_enabled(default=False) def arctan(self, inplace=False): """Take the trigonometric inverse tangent of the data element- @@ -5215,7 +5278,7 @@ def arctan(self, inplace=False): .. versionadded:: 3.0.7 - .. seealso:: `tan`, `arcsin`, `arccos`, `arctanh` + .. seealso:: `tan`, `arcsin`, `arccos`, `arctanh`, `arctan2` :Parameters: @@ -5254,47 +5317,6 @@ def arctan(self, inplace=False): return d - # AT2 - # - # @classmethod - # def arctan2(cls, y, x): - # '''Take the "two-argument" trigonometric inverse tangent - # element-wise for `y`/`x`. - # - # Explicitly this returns, for all corresponding elements, the angle - # between the positive `x` axis and the line to the point (`x`, `y`), - # where the signs of both `x` and `y` are taken into account to - # determine the quadrant. Such knowledge of the signs of `x` and `y` - # are lost when the quotient is input to the standard "one-argument" - # `arctan` function, such that use of `arctan` leaves the quadrant - # ambiguous. `arctan2` may therefore be preferred. - # - # Units are ignored in the calculation. The result has units of radians. - # - # .. versionadded:: 3.2.0 - # - # .. seealso:: `arctan`, `tan` - # - # :Parameters: - # - # y: `Data` - # The data array to provide the numerator elements, corresponding - # to the `y` coordinates in the `arctan2` definition. - # - # x: `Data` - # The data array to provide the denominator elements, - # corresponding to the `x` coordinates in the `arctan2` - # definition. - # - # :Returns: - # - # `Data` - # - # **Examples** - # - # ''' - # return cls(np.arctan2(y, x), units=_units_radians) - @_inplace_enabled(default=False) def arctanh(self, inplace=False): """Take the inverse hyperbolic tangent of the data element-wise. @@ -7334,6 +7356,96 @@ def asdata(cls, d, dtype=None, copy=False): return data + @classmethod + def arctan2(cls, x1, x2): + """Element-wise arc tangent of ``x1/x2`` with correct quadrant. + + The quadrant (i.e. branch) is chosen so that ``arctan2(y, x)`` + is the signed angle in radians between the ray ending at the + origin and passing through the point ``(1, 0)``, and the ray + ending at the origin and passing through the point ``(x, y)``. + (Note the role reversal: the "y-coordinate" is the first + function parameter, the "x-coordinate" is the second.) By IEEE + convention, this function is defined for ``x = +/-0`` and for + either or both of ``y = +/-inf`` and ``x = +/-inf`` (see Notes + for specific values). + + `arctan2` is identical to the ``atan2`` function of the + underlying C library. The following special values are defined + in the C standard: + + ====== ====== =================== + *x1* *x2* ``arctan2(x1, x2)`` + ====== ====== =================== + +/- 0 +0 +/- 0 + +/- 0 -0 +/- pi + > 0 +/-inf +0 / +pi + < 0 +/-inf -0 / -pi + +/-inf +inf +/- (pi/4) + +/-inf -inf +/- (3*pi/4) + ====== ====== =================== + + Note that ``+0`` and ``-0`` are distinct floating point + numbers, as are ``+inf`` and ``-inf``. + + .. versionadded:: 3.16.0 + + .. seealso:: `arctan`, `tan` + + :Parameters: + + x1: array_like + Y coordinates. + + x2: array_like + X coordinates. *x1* and *x2* must be broadcastable to + a common shape (which becomes the shape of the + output). + + :Returns: + + `Data` + Array of angles in radians, in the range ``(-pi, + pi]``. + + **Examples** + + >>> import numpy as np + >>> x = cf.Data([-1, +1, +1, -1]) + >>> y = cf.Data([-1, -1, +1, +1]) + >>> print((cf.Data.arctan2(y, x) * 180 / np.pi).array) + [-135.0 -45.0 45.0 135.0] + >>> x[1] = cf.masked + >>> y[1] = cf.masked + >>> print((cf.Data.arctan2(y, x) * 180 / np.pi).array) + [-135.0 -- 45.0 135.0] + + >>> print(cf.Data.arctan2([0, 0, np.inf], [+0., -0., np.inf]).array) + [0.0 3.141592653589793 0.7853981633974483] + + >>> print((cf.Data.arctan2([1, -1], [0, 0]) * 180 / np.pi).array) + [90.0 -90.0] + + """ + try: + y = x1.to_dask_array() + except AttributeError: + y = cls.asdata(x1).to_dask_array() + + try: + x = x2.to_dask_array() + except AttributeError: + x = cls.asdata(x2).to_dask_array() + + mask = da.ma.getmaskarray(y) | da.ma.getmaskarray(x) + y = da.ma.filled(y, 1) + x = da.ma.filled(x, 1) + + dx = da.arctan2(y, x) + dx = da.ma.masked_array(dx, mask=mask) + + return cls(dx, units=_units_radians) + @_inplace_enabled(default=False) def compressed(self, inplace=False): """Return all non-masked values in a one dimensional data array. @@ -7761,6 +7873,45 @@ def second(self): """ return YMDhms(self, "second") + @property + def sparse_array(self): + """Return an independent `scipy` sparse array of the data. + + In-place changes to the returned sparse array do not affect + the underlying dask array. + + An `AttributeError` is raised if a sparse array representation + is not available. + + **Performance** + + `sparse_array` causes all delayed operations to be + computed. The returned sparse array is a deep copy of that + returned by created `compute`. + + .. versionadded:: 3.16.0 + + .. seealso:: `array` + + :Returns: + + An independent `scipy` sparse array of the data. + + **Examples** + + >>> from scipy.sparse import issparse + >>> issparse(d.sparse_array) + True + + """ + array = self.compute() + if issparse(array): + return array.copy() + + raise AttributeError( + "A sparse array representation of the data is not available" + ) + @_inplace_enabled(default=False) def uncompress(self, inplace=False): """Uncompress the data. @@ -9871,6 +10022,60 @@ def masked_all( d._set_dask(dx) return d + @_inplace_enabled(default=False) + def masked_values(self, value, rtol=None, atol=None, inplace=False): + """Mask using floating point equality. + + Masks the data where elements are approximately equal to the + given value. For integer types, exact equality is used. + + .. versionadded:: 3.16.0 + + .. seealso:: `mask` + + :Parameters: + + value: number + Masking value. + + {{rtol: number, optional}} + + {{atol: number, optional}} + + {{inplace: `bool`, optional}} + + :Returns: + + `{{class}}` or `None` + The result of masking the data where approximately + equal to *value*, or `None` if the operation was + in-place. + + **Examples** + + >>> d = {{package}}.{{class}}([1, 1.1, 2, 1.1, 3]) + >>> e = d.masked_values(1.1) + >>> print(e.array) + [1.0 -- 2.0 -- 3.0] + + """ + d = _inplace_enabled_define_and_cleanup(self) + + if rtol is None: + rtol = self._rtol + else: + rtol = float(rtol) + + if atol is None: + atol = self._atol + else: + atol = float(atol) + + dx = d.to_dask_array() + dx = da.ma.masked_values(dx, value, rtol=rtol, atol=atol) + d._set_dask(dx) + return d + @_inplace_enabled(default=False) @_deprecated_kwarg_check("i", version="3.0.0", removed_at="4.0.0") def mid_range( @@ -11168,7 +11373,7 @@ def tanh(self, inplace=False): .. versionadded:: 3.1.0 - .. seealso:: `arctanh`, `sinh`, `cosh`, `tan` + .. seealso:: `arctanh`, `sinh`, `cosh`, `tan`, `arctan2` :Parameters: diff --git a/cf/data/utils.py b/cf/data/utils.py index 82fe619f8d..747e703cfe 100644 --- a/cf/data/utils.py +++ b/cf/data/utils.py @@ -999,3 +999,50 @@ def parse_weights(d, weights, axis=None): # Return the product of the weights components, which will be # broadcastable to d return reduce(mul, w) + + +def normalize_chunks(chunks, shape=None, dtype=None): + """Normalize chunks to tuple of tuples. + + The shape may contain sizes of ``nan``. This could occur when the + underlying data is compressed in a way which makes the shape + impossible to infer without actually uncompressing the data. + + If *shape* contains no ``nan`` sizes then this function is + identical to `dask.array.core.normalize_chunks`. If it does, then + the output chunks for each such axis will be ``(nan,)``. + + .. versionadded 3.16.0 + + :Parameters: + + chunks: tuple, int, dict, or string + The chunks to be normalized. See + `dask.array.core.normalize_chunks` for details. + + shape: `tuple` + The shape of the data. + + dtype: data-type + The data-type for the data. + + :Returns: + + `tuple` + The normalized chunks. + + """ + from math import isnan, nan + + from dask.array.core import normalize_chunks + + if not any(map(isnan, shape)): + return normalize_chunks(chunks, shape=shape, dtype=dtype) + + out = [ + (nan,) + if isnan(size) + else normalize_chunks(chunk, shape=(size,), dtype=dtype)[0] + for chunk, size in zip(chunks, shape) + ] + return tuple(out) diff --git a/cf/dimensioncoordinate.py b/cf/dimensioncoordinate.py index c88fec0f14..347a0d7dd5 100644 --- a/cf/dimensioncoordinate.py +++ b/cf/dimensioncoordinate.py @@ -110,14 +110,6 @@ def __init__( if chars is not None: self._set_component("cell_characteristics", chars, copy=False) - def __repr__(self): - """Called by the `repr` built-in function. - - x.__repr__() <==> repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", ">> h = f._conform_for_data_broadcasting(g) - - """ - - other = self._conform_for_assignment(other, check_coordinates=True) - - # Remove leading size one dimensions - ndiff = other.ndim - self.ndim - if ndiff > 0 and set(other.shape[:ndiff]) == set((1,)): - for i in range(ndiff): - other = other.squeeze(0) - - return other - - @_manage_log_level_via_verbosity - def _equivalent_construct_data( - self, - field1, - key0=None, - key1=None, - s=None, - t=None, - atol=None, - rtol=None, - verbose=None, - axis_map=None, - ): - """True if the field has equivalent construct data to another. - - Two real numbers ``x`` and ``y`` are considered equal if - ``|x-y|<=atol+rtol|y|``, where ``atol`` (the tolerance on absolute - differences) and ``rtol`` (the tolerance on relative differences) - are positive, typically very small numbers. See the *atol* and - *rtol* parameters. - - :Parameters: - - key0: `str` - - key1: `str` - - field1: `Field` - - s: `dict`, optional - - t: `dict`, optional - - atol: `float`, optional - The tolerance on absolute differences between real - numbers. The default value is set by the `atol` function. - - rtol: `float`, optional - The tolerance on relative differences between real - numbers. The default value is set by the `rtol` function. - - {{verbose: `int` or `str` or `None`, optional}} - - """ - item0 = self.constructs[key0] - item1 = field1.constructs[key1] - - if item0.has_data() != item1.has_data(): - if is_log_level_info(logger): - logger.info( - f"{self.__class__.__name__}: Only one item has data" - ) # pragma: no cover - - return False - - if not item0.has_data(): - # Neither field has a data array - return True - - if item0.size != item1.size: - if is_log_level_info(logger): - logger.info( - f"{self.__class__.__name__}: Different metadata construct " - f"data array size: {item0.size} != {item1.size}" - ) # pragma: no cover - - return False - - if item0.ndim != item1.ndim: - if is_log_level_info(logger): - logger.info( - f"{self.__class__.__name__}: Different data array ranks " - f"({item0.ndim}, {item1.ndim})" - ) # pragma: no cover - - return False - - axes0 = self.get_data_axes(key0, default=()) - axes1 = field1.get_data_axes(key1, default=()) - - if s is None: - s = self.analyse_items() - if t is None: - t = field1.analyse_items() - - transpose_axes = [] - if axis_map is None: - for axis0 in axes0: - axis1 = t["id_to_axis"].get(s["axis_to_id"][axis0], None) - if axis1 is None: - if is_log_level_info(logger): - # TODO: improve message here (make user friendly): - logger.info( - "t['id_to_axis'] does not have a key " - f"s['axis_to_id'][axis0] for " - f"{self.__class__.__name__}" - ) # pragma: no cover - - return False - - transpose_axes.append(axes1.index(axis1)) - else: - for axis0 in axes0: - axis1 = axis_map.get(axis0) - if axis1 is None: - if is_log_level_info(logger): - # TODO: improve message here (make user friendly): - logger.info( - f"axis_map[axis0] is None for {self.__class__.__name__}" - ) # pragma: no cover - - return False - - transpose_axes.append(axes1.index(axis1)) - - copy1 = True - - if transpose_axes != list(range(item1.ndim)): - if copy1: - item1 = item1.copy() - copy1 = False - - item1.transpose(transpose_axes, inplace=True) - - if item0.shape != item1.shape: - # add traceback TODO - return False - - flip_axes = [ - i - for i, (axis1, axis0) in enumerate(zip(axes1, axes0)) - if field1.direction(axis1) != self.direction(axis0) - ] - - if flip_axes: - if copy1: - item1 = item1.copy() - copy1 = False - - item1.flip(flip_axes, inplace=True) - - if not item0._equivalent_data( - item1, rtol=rtol, atol=atol, verbose=verbose - ): - # add traceback TODO - return False - - return True - - # ---------------------------------------------------------------- - # Worker functions for weights - # ---------------------------------------------------------------- - def _weights_area_XY( - self, - comp, - weights_axes, - auto=False, - measure=False, - radius=None, - methods=False, - ): - """Calculate area weights from X and Y dimension coordinate - constructs. - - :Parameters: - - measure: `bool` - If True then make sure that the weights represent true - cell sizes. - - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as opposed to - the actual weights. - - :Returns: - - `bool` or `None` - - """ - xkey, xcoord = self.dimension_coordinate( - "X", item=True, default=(None, None) - ) - ykey, ycoord = self.dimension_coordinate( - "Y", item=True, default=(None, None) - ) - - if xcoord is None or ycoord is None: - if auto: - return - - raise ValueError( - "No unique 'X' and 'Y' dimension coordinate constructs for " - "calculating area weights" - ) - - if xcoord.Units.equivalent( - Units("radians") - ) and ycoord.Units.equivalent(Units("radians")): - pass - elif xcoord.Units.equivalent( - Units("metres") - ) and ycoord.Units.equivalent(Units("metres")): - radius = None - else: - if auto: - return - - raise ValueError( - "Insufficient coordinate constructs for calculating " - "area weights" - ) - - xaxis = self.get_data_axes(xkey)[0] - yaxis = self.get_data_axes(ykey)[0] - - for axis in (xaxis, yaxis): - if axis in weights_axes: - if auto: - return - - raise ValueError( - "Multiple weights specifications for " - f"{self.constructs.domain_axis_identity(axis)!r} axis" - ) - - if measure and radius is not None: - radius = self.radius(default=radius) - - if measure or xcoord.size > 1: - if not xcoord.has_bounds(): - if auto: - return - - raise ValueError( - "Can't create area weights: No bounds for " - f"{xcoord.identity()!r} axis" - ) - - if methods: - comp[(xaxis,)] = "linear " + xcoord.identity() - else: - cells = xcoord.cellsize - if xcoord.Units.equivalent(Units("radians")): - cells.Units = _units_radians - if measure: - cells *= radius - cells.override_units(radius.Units, inplace=True) - else: - cells.Units = Units("metres") - - comp[(xaxis,)] = cells - - weights_axes.add(xaxis) - - if measure or ycoord.size > 1: - if not ycoord.has_bounds(): - if auto: - return - - raise ValueError( - "Can't create area weights: No bounds for " - f"{ycoord.identity()!r} axis" - ) - - if ycoord.Units.equivalent(Units("radians")): - ycoord = ycoord.clip(-90, 90, units=Units("degrees")) - ycoord.sin(inplace=True) - - if methods: - comp[(yaxis,)] = "linear sine " + ycoord.identity() - else: - cells = ycoord.cellsize - if measure: - cells *= radius - - comp[(yaxis,)] = cells - else: - if methods: - comp[(yaxis,)] = "linear " + ycoord.identity() - else: - cells = ycoord.cellsize - comp[(yaxis,)] = cells - - weights_axes.add(yaxis) - - return True - - def _weights_data( - self, - w, - comp, - weights_axes, - axes=None, - data=False, - components=False, - methods=False, - ): - """Creates weights for the field construct's data array. - - :Parameters: - - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as - opposed to the actual weights. - - """ - # -------------------------------------------------------- - # Data weights - # -------------------------------------------------------- - field_data_axes = self.get_data_axes() - - if axes is not None: - if isinstance(axes, (str, int)): - axes = (axes,) - - if len(axes) != w.ndim: - raise ValueError( - "'axes' parameter must provide an axis identifier " - f"for each weights data dimension. Got {axes!r} for " - f"{w.ndim} dimension(s)." - ) - - iaxes = [ - field_data_axes.index(self.domain_axis(axis, key=True)) - for axis in axes - ] - - if data: - for i in range(self.ndim): - if i not in iaxes: - w = w.insert_dimension(position=i) - iaxes.insert(i, i) - - w = w.transpose(iaxes) - - if w.ndim > 0: - while w.shape[0] == 1: - w = w.squeeze(0) - - else: - iaxes = list(range(self.ndim - w.ndim, self.ndim)) - - if not (components or methods): - if not self._is_broadcastable(w.shape): - raise ValueError( - f"The 'Data' weights (shape {w.shape}) are not " - "broadcastable to the field construct's data " - f"(shape {self.shape})." - ) - - axes0 = field_data_axes[self.ndim - w.ndim :] - else: - axes0 = [field_data_axes[i] for i in iaxes] - - for axis0 in axes0: - if axis0 in weights_axes: - raise ValueError( - "Multiple weights specified for " - f"{self.constructs.domain_axis_identity(axis0)!r} axis" - ) - - if methods: - comp[tuple(axes0)] = "custom data" - else: - comp[tuple(axes0)] = w - - weights_axes.update(axes0) - - return True - - def _weights_field(self, fields, comp, weights_axes, methods=False): - """Creates a weights field.""" - s = self.analyse_items() - - domain_axes = self.domain_axes(todict=True) - # domain_axes_size_1 = self.domain_axes(filter_by_size=(1,), todict=True) - - for w in fields: - t = w.analyse_items() - # TODO CHECK this with org - - if t["undefined_axes"]: - # if set( - # t.domain_axes.filter_by_size(gt(1), view=True) - # ).intersection(t["undefined_axes"]): - w_domain_axes_1 = w.domain_axes( - filter_by_size=(1,), todict=True - ) - if set(w_domain_axes_1).intersection(t["undefined_axes"]): - raise ValueError( - f"Weights field {w} has at least one undefined " - f"domain axes: {w_domain_axes_1}." - ) - - w = w.squeeze() - - w_domain_axes = w.domain_axes(todict=True) - - axis1_to_axis0 = {} - - coordinate_references = self.coordinate_references(todict=True) - w_coordinate_references = w.coordinate_references(todict=True) - - for axis1 in w.get_data_axes(): - identity = t["axis_to_id"].get(axis1, None) - if identity is None: - raise ValueError( - "Weights field has unmatched, size > 1 " - f"{w.constructs.domain_axis_identity(axis1)!r} axis" - ) - - axis0 = s["id_to_axis"].get(identity, None) - if axis0 is None: - raise ValueError( - f"Weights field has unmatched, size > 1 {identity!r} " - "axis" - ) - - w_axis_size = w_domain_axes[axis1].get_size() - self_axis_size = domain_axes[axis0].get_size() - - if w_axis_size != self_axis_size: - raise ValueError( - f"Weights field has incorrectly sized {identity!r} " - f"axis ({w_axis_size} != {self_axis_size})" - ) - - axis1_to_axis0[axis1] = axis0 - - # Check that the defining coordinate data arrays are - # compatible - key0 = s["axis_to_coord"][axis0] - key1 = t["axis_to_coord"][axis1] - - if not self._equivalent_construct_data( - w, key0=key0, key1=key1, s=s, t=t - ): - raise ValueError( - f"Weights field has incompatible {identity!r} " - "coordinates" - ) - - # Still here? Then the defining coordinates have - # equivalent data arrays - - # If the defining coordinates are attached to - # coordinate references then check that those - # coordinate references are equivalent - refs0 = [ - key - for key, ref in coordinate_references.items() - if key0 in ref.coordinates() - ] - refs1 = [ - key - for key, ref in w_coordinate_references.items() - if key1 in ref.coordinates() - ] - - nrefs = len(refs0) - if nrefs > 1 or nrefs != len(refs1): - # The defining coordinate are associated with - # different numbers of coordinate references - equivalent_refs = False - elif not nrefs: - # Neither defining coordinate is associated with a - # coordinate reference - equivalent_refs = True - else: - # Each defining coordinate is associated with - # exactly one coordinate reference - equivalent_refs = self._equivalent_coordinate_references( - w, key0=refs0[0], key1=refs1[0], s=s, t=t - ) - - if not equivalent_refs: - raise ValueError( - "Input weights field has an incompatible " - "coordinate reference" - ) - - axes0 = tuple( - [axis1_to_axis0[axis1] for axis1 in w.get_data_axes()] - ) - - for axis0 in axes0: - if axis0 in weights_axes: - raise ValueError( - "Multiple weights specified for " - f"{self.constructs.domain_axis_identity(axis0)!r} " - "axis" - ) - - comp[tuple(axes0)] = w.data - - weights_axes.update(axes0) - - return True - - def _weights_field_scalar(self, methods=False): - """Return a scalar field of weights with long_name ``'weight'``. - - :Returns: - - `Field` - - """ - data = Data(1.0, "1") - - f = type(self)() - f.set_data(data, copy=False) - f.long_name = "weight" - f.comment = f"Weights for {self!r}" - - return f - - def _weights_geometry_area( - self, - domain_axis, - comp, - weights_axes, - auto=False, - measure=False, - radius=None, - great_circle=False, - return_areas=False, - methods=False, - ): - """Creates area weights for polygon geometry cells. - - .. versionadded:: 3.2.0 - - :Parameters: - - domain_axis : `str` or `None` - - measure: `bool` - If True then make sure that the weights represent true - cell sizes. - - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as opposed to - the actual weights. - - :Returns: - - `bool` or `Data` - - """ - axis, aux_X, aux_Y, aux_Z = self._weights_yyy( - domain_axis, "polygon", methods=methods, auto=auto - ) - - if axis is None: - if auto: - return False - - if domain_axis is None: - raise ValueError("No polygon geometries") - - raise ValueError( - "No polygon geometries for " - f"{self.constructs.domain_axis_identity(domain_axis)!r} axis" - ) - - if axis in weights_axes: - if auto: - return False - - raise ValueError( - "Multiple weights specifications for " - f"{self.constructs.domain_axis_identity(axis)!r} axis" - ) - - # Check for interior rings - interior_ring_X = aux_X.get_interior_ring(None) - interior_ring_Y = aux_Y.get_interior_ring(None) - if interior_ring_X is None and interior_ring_Y is None: - interior_ring = None - elif interior_ring_X is None: - raise ValueError( - "Can't find weights: X coordinates have missing " - "interior ring variable" - ) - elif interior_ring_Y is None: - raise ValueError( - "Can't find weights: Y coordinates have missing " - "interior ring variable" - ) - elif not interior_ring_X.data.equals(interior_ring_Y.data): - raise ValueError( - "Can't find weights: Interior ring variables for X and Y " - "coordinates have different data values" - ) - else: - interior_ring = interior_ring_X.data - if interior_ring.shape != aux_X.bounds.shape[:-1]: - raise ValueError( - "Can't find weights: Interior ring variables have " - f"incorrect shape. Got {interior_ring.shape}, expected " - f"{aux_X.bounds.shape[:-1]}" - ) - - x = aux_X.bounds.data - y = aux_Y.bounds.data - - if x.Units.equivalent(_units_metres) and y.Units.equivalent( - _units_metres - ): - # ---------------------------------------------------- - # Plane polygons defined by straight lines. - # - # Use the shoelace formula: - # https://en.wikipedia.org/wiki/Shoelace_formula - # - # Do this in preference to weights based on spherical - # polygons, which require the great circle assumption. - # ---------------------------------------------------- - spherical = False - - if methods: - comp[(axis,)] = "area plane polygon geometry" - return True - - y.Units = x.Units - - all_areas = (x[..., :-1] * y[..., 1:]).sum(-1, squeeze=True) - ( - x[..., 1:] * y[..., :-1] - ).sum(-1, squeeze=True) - - for i, (parts_x, parts_y) in enumerate(zip(x, y)): - for j, (nodes_x, nodes_y) in enumerate(zip(parts_x, parts_y)): - nodes_x = nodes_x.compressed() - nodes_y = nodes_y.compressed() - - if (nodes_x.size and nodes_x[0] != nodes_x[-1]) or ( - nodes_y.size and nodes_y[0] != nodes_y[-1] - ): - # First and last nodes of this polygon - # part are different => need to account - # for the "last" edge of the polygon that - # joins the first and last points. - all_areas[i, j] += x[-1] * y[0] - x[0] * y[-1] - - all_areas = all_areas.abs() * 0.5 - - elif x.Units.equivalent(_units_radians) and y.Units.equivalent( - _units_radians - ): - # ---------------------------------------------------- - # Spherical polygons defined by great circles - # - # The area of such a spherical polygon is given by the - # sum of the interior angles minus (N-2)pi, where N is - # the number of sides (Todhunter, - # https://en.wikipedia.org/wiki/Spherical_trigonometry#Spherical_polygons): - # - # Area of N-sided polygon on the unit sphere = - # \left(\sum _{n=1}^{N}A_{n}\right) - (N - 2)\pi - # - # where A_{n} denotes the n-th interior angle. - # ---------------------------------------------------- - spherical = True - - if not great_circle: - raise ValueError( - "Must set great_circle=True to allow the derivation of " - "area weights from spherical polygons composed from " - "great circle segments." - ) - - if methods: - comp[(axis,)] = "area spherical polygon geometry" - return True - - x.Units = _units_radians - y.Units = _units_radians - - interior_angle = self._weights_interior_angle(x, y) - - # Find the number of edges of each polygon (note that - # this number may be one too few, but we'll adjust for - # that later). - N = interior_angle.sample_size(-1, squeeze=True) - - all_areas = interior_angle.sum(-1, squeeze=True) - (N - 2) * np.pi - - for i, (parts_x, parts_y) in enumerate(zip(x, y)): - for j, (nodes_x, nodes_y) in enumerate(zip(parts_x, parts_y)): - nodes_x = nodes_x.compressed() - nodes_y = nodes_y.compressed() - - if (nodes_x.size and nodes_x[0] != nodes_x[-1]) or ( - nodes_y.size and nodes_y[0] != nodes_y[-1] - ): - # First and last nodes of this polygon - # part are different => need to account - # for the "last" edge of the polygon that - # joins the first and last points. - interior_angle = self._weights_interior_angle( - nodes_x[[0, -1]], nodes_y[[0, -1]] - ) - - all_areas[i, j] += interior_angle + np.pi - - area_min = all_areas.min() - if area_min < 0: - raise ValueError( - "A spherical polygon geometry part has negative area" - ) - else: - return False - - # Change the sign of areas for polygons that are interior - # rings - if interior_ring is not None: - all_areas.where(interior_ring, -all_areas, inplace=True) - - # Sum the areas of each part to get the total area of each - # cell - areas = all_areas.sum(-1, squeeze=True) - - if measure and spherical and aux_Z is not None: - # Multiply by radius squared, accounting for any Z - # coordinates, to get the actual area - z = aux_Z.get_data(None, _fill_value=False) - if z is None: - r = radius - else: - if not z.Units.equivalent(_units_metres): - raise ValueError( - "Z coordinates must have units equivalent to " - f"metres for area calculations. Got {z.Units!r}" - ) - - positive = aux_Z.get_property("positive", None) - if positive is None: - raise ValueError( - "Value of Z coordinate 'positive' property is not " - "defined" - ) - - if positive.lower() == "up": - r = radius + z - elif positive.lower() == "down": - r = radius - z - else: - raise ValueError( - "Bad value of Z coordinate 'positive' " - f"property: {positive!r}." - ) - - areas *= r**2 - - if return_areas: - return areas - - comp[(axis,)] = areas - - weights_axes.add(axis) - - return True - - def _weights_geometry_line( - self, - domain_axis, - comp, - weights_axes, - auto=False, - measure=False, - radius=None, - great_circle=False, - methods=False, - ): - """Creates line-length weights for line geometries. - - .. versionadded:: 3.2.0 - - :Parameters: - - measure: `bool` - If True then make sure that the weights represent true - cell sizes. - - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as opposed to - the actual weights. - - """ - axis, aux_X, aux_Y, aux_Z = self._weights_yyy( - domain_axis, "line", methods=methods, auto=auto - ) - - if axis is None: - if auto: - return False - - if domain_axis is None: - raise ValueError("No line geometries") - - raise ValueError( - "No line geometries for " - f"{self.constructs.domain_axis_identity(domain_axis)!r} axis" - ) - - if axis in weights_axes: - if auto: - return False - - raise ValueError( - "Multiple weights specifications for " - f"{self.constructs.domain_axis_identity(axis)!r} axis" - ) - - x = aux_X.bounds.data - y = aux_Y.bounds.data - - if x.Units.equivalent(_units_metres) and y.Units.equivalent( - _units_metres - ): - # ---------------------------------------------------- - # Plane lines. - # - # Each line segment is the simple cartesian distance - # between two adjacent nodes. - # ---------------------------------------------------- - if methods: - comp[(axis,)] = "linear plane line geometry" - return True - - y.Units = x.Units - - delta_x = x.diff(axis=-1) - delta_y = y.diff(axis=-1) - - all_lengths = (delta_x**2 + delta_y**2) ** 0.5 - all_lengths = all_lengths.sum(-1, squeeze=True) - - elif x.Units.equivalent(_units_radians) and y.Units.equivalent( - _units_radians - ): - # ---------------------------------------------------- - # Spherical lines. - # - # Each line segment is a great circle arc between two - # adjacent nodes. - # - # The length of the great circle arc is the the - # interior angle multiplied by the radius. The - # interior angle is calculated with a special case of - # the Vincenty formula: - # https://en.wikipedia.org/wiki/Great-circle_distance - # ---------------------------------------------------- - if not great_circle: - raise ValueError( - "Must set great_circle=True to allow the derivation " - "of line-length weights from great circle segments." - ) - - if methods: - comp[(axis,)] = "linear spherical line geometry" - return True - - x.Units = _units_radians - y.Units = _units_radians - - interior_angle = self._weights_interior_angle(x, y) - if interior_angle.min() < 0: - raise ValueError( - "A spherical line geometry segment has " - f"negative length: {interior_angle.min() * radius!r}" - ) - - all_lengths = interior_angle.sum(-1, squeeze=True) - - if measure: - all_lengths *= radius - else: - return False - - # Sum the lengths of each part to get the total length of - # each cell - lengths = all_lengths.sum(-1, squeeze=True) - - comp[(axis,)] = lengths - - weights_axes.add(axis) - - return True - - def _weights_geometry_volume( - self, - comp, - weights_axes, - auto=False, - measure=False, - radius=None, - great_circle=False, - methods=False, - ): - """Creates volume weights for polygon geometry cells. - - .. versionadded:: 3.2.0 - - :Parameters: - - measure: `bool` - If True then make sure that the weights represent true - cell sizes. - - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as opposed to - the actual weights. - - """ - axis, aux_X, aux_Y, aux_Z = self._weights_yyy( - "polygon", methods=methods, auto=auto - ) - - if axis is None and auto: - return False - - if axis in weights_axes: - if auto: - return False - - raise ValueError( - "Multiple weights specifications for " - f"{self.constructs.domain_axis_identity(axis)!r} axis" - ) - - x = aux_X.bounds.data - y = aux_Y.bounds.data - z = aux_Z.bounds.data - - if not z.Units.equivalent(_units_metres): - if auto: - return False - - raise ValueError( - "Z coordinate bounds must have units equivalent to " - f"metres for volume calculations. Got {z.Units!r}." - ) - - if not methods: - # Initialise cell volumes as the cell areas - volumes = self._weights_geometry_area( - comp, - weights_axes, - auto=auto, - measure=measure, - radius=radius, - great_circle=great_circle, - methods=False, - return_areas=True, - ) - - if measure: - delta_z = abs(z[..., 1] - z[..., 0]) - delta_z.squeeze(axis=-1, inplace=True) - - if x.Units.equivalent(_units_metres) and y.Units.equivalent( - _units_metres - ): - # ---------------------------------------------------- - # Plane polygons defined by straight lines. - # - # Do this in preference to weights based on spherical - # polygons, which require the great circle assumption. - # ---------------------------------------------------- - if methods: - comp[(axis,)] = "volume plane polygon geometry" - return True - - if measure: - volumes *= delta_z - - elif x.Units.equivalent(_units_radians) and y.Units.equivalent( - _units_radians - ): - # ---------------------------------------------------- - # Spherical polygons defined by great circles - # - # The area of such a spherical polygon is given by the - # sum of the interior angles minus (N-2)pi, where N is - # the number of sides (Todhunter): - # - # https://en.wikipedia.org/wiki/Spherical_trigonometry#Area_and_spherical_excess - # - # The interior angle of a side is calculated with a - # special case of the Vincenty formula: - # https://en.wikipedia.org/wiki/Great-circle_distance - # ---------------------------------------------------- - if not great_circle: - raise ValueError( - "Must set great_circle=True to allow the derivation " - "of volume weights from spherical polygons composed " - "from great circle segments." - ) - - if methods: - comp[(axis,)] = "volume spherical polygon geometry" - return True - - if measure: - r = radius - - # actual_volume = - # [actual_area/(4*pi*r**2)] - # * (4/3)*pi*[(r+delta_z)**3 - r**3)] - volumes *= ( - delta_z**3 / (3 * r**2) + delta_z**2 / r + delta_z - ) - else: - raise ValueError( - "X and Y coordinate bounds must both have units " - "equivalent to either metres, for plane polygon, or radians, " - "for spherical polygon, volume calculations. Got " - f"{x.Units!r} and {y.Units!r}." - ) - - comp[(axis,)] = volumes - - weights_axes.add(axis) - - return True - - def _weights_interior_angle(self, data_lambda, data_phi): - r"""Find the interior angle between each adjacent pair of - geometry nodes defined on a sphere. - - The interior angle of two points on the sphere is calculated with - a special case of the Vincenty formula - (https://en.wikipedia.org/wiki/Great-circle_distance): - - \Delta \sigma =\arctan { - \frac {\sqrt {\left(\cos \phi _{2}\sin(\Delta \lambda )\right)^{2} + - \left(\cos \phi _{1}\sin \phi _{2} - - \sin \phi _{1}\cos \phi _{2}\cos(\Delta \lambda )\right)^{2} } } - {\sin \phi _{1}\sin \phi _{2} + - \cos \phi _{1}\cos \phi _{2}\cos(\Delta \lambda )} - } - - :Parameters: - - data_lambda: `Data` - Longitudes. Must have units of radians, which is not - checked. - - data_phi: `Data` - Latitudes. Must have units of radians, which is not - checked. - - :Returns: - - `Data` - The interior angles in units of radians. - - """ - delta_lambda = data_lambda.diff(axis=-1) - - cos_phi = data_phi.cos() - sin_phi = data_phi.sin() - - cos_phi_1 = cos_phi[..., :-1] - cos_phi_2 = cos_phi[..., 1:] - - sin_phi_1 = sin_phi[..., :-1] - sin_phi_2 = sin_phi[..., 1:] - - cos_delta_lambda = delta_lambda.cos() - sin_delta_lambda = delta_lambda.sin() - - numerator = ( - (cos_phi_2 * sin_delta_lambda) ** 2 - + ( - cos_phi_1 * sin_phi_2 - - sin_phi_1 * cos_phi_2 * cos_delta_lambda - ) - ** 2 - ) ** 0.5 - - denominator = ( - sin_phi_1 * sin_phi_2 + cos_phi_1 * cos_phi_2 * cos_delta_lambda - ) - - # TODO RuntimeWarning: overflow encountered in true_divide comes from - # numerator/denominator with missing values - - interior_angle = (numerator / denominator).arctan() - - interior_angle.override_units(_units_1, inplace=True) - - return interior_angle - - def _weights_linear( - self, - axis, - comp, - weights_axes, - auto=False, - measure=False, - methods=False, - ): - """1-d linear weights from dimension coordinate constructs. - - :Parameters: - - axis: `str` - - comp: `dict` - - weights_axes: `set` - - auto: `bool` - - measure: `bool` - If True then make sure that the weights represent true - cell sizes. - - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as opposed to - the actual weights. - - :Returns: - - `bool` - - """ - da_key = self.domain_axis(axis, key=True, default=None) - if da_key is None: - if auto: - return False - - raise ValueError( - "Can't create weights: Can't find domain axis " - f"matching {axis!r}" - ) - - dim = self.dimension_coordinate(filter_by_axis=(da_key,), default=None) - if dim is None: - if auto: - return False - - raise ValueError( - f"Can't create linear weights for {axis!r} axis: Can't find " - "dimension coordinate construct." - ) - - if not measure and dim.size == 1: - return False - - if da_key in weights_axes: - if auto: - return False - - raise ValueError( - f"Can't create linear weights for {axis!r} axis: Multiple " - "axis specifications" - ) - - if not dim.has_bounds(): - # Dimension coordinate has no bounds - if auto: - return False - - raise ValueError( - f"Can't create linear weights for {axis!r} axis: No bounds" - ) - else: - # Bounds exist - if methods: - comp[ - (da_key,) - ] = "linear " + self.constructs.domain_axis_identity(da_key) - else: - comp[(da_key,)] = dim.cellsize + other, key0=refs0[0], key1=refs1[0], s=s, t=v + ): + raise ValueError(error_msg) - weights_axes.add(da_key) + return other - return True + def _conform_for_data_broadcasting(self, other): + """Conforms the field with another, ready for data broadcasting. - def _weights_measure( - self, measure, comp, weights_axes, methods=False, auto=False - ): - """Cell measure weights. + Note that the other field, *other*, is not changed in-place. :Parameters: - methods: `bool`, optional - If True then add a description of the method used to - create the weights to the *comp* dictionary, as opposed to - the actual weights. + other: `Field` + The field to conform. :Returns: - `bool` - - """ - m = self.cell_measures(filter_by_measure=(measure,), todict=True) - len_m = len(m) - - if not len_m: - if measure == "area": - return False - - if auto: - return - - raise ValueError( - f"Can't find weights: No {measure!r} cell measure" - ) - - elif len_m > 1: - if auto: - return False - - raise ValueError( - f"Can't find weights: Multiple {measure!r} cell measures" - ) - - key, clm = m.popitem() - - clm_axes0 = self.get_data_axes(key) + `Field` + The conformed version of *other*. - clm_axes = tuple( - [axis for axis, n in zip(clm_axes0, clm.data.shape) if n > 1] - ) + **Examples** - for axis in clm_axes: - if axis in weights_axes: - if auto: - return False + >>> h = f._conform_for_data_broadcasting(g) - raise ValueError( - "Multiple weights specifications for " - f"{self.constructs.domain_axis_identity(axis)!r} axis" - ) + """ - clm = clm.get_data(_fill_value=False).copy() - if clm_axes != clm_axes0: - iaxes = [clm_axes0.index(axis) for axis in clm_axes] - clm.squeeze(iaxes, inplace=True) + other = self._conform_for_assignment(other, check_coordinates=True) - if methods: - comp[tuple(clm_axes)] = measure + " cell measure" - else: - comp[tuple(clm_axes)] = clm + # Remove leading size one dimensions + ndiff = other.ndim - self.ndim + if ndiff > 0 and set(other.shape[:ndiff]) == set((1,)): + for i in range(ndiff): + other = other.squeeze(0) - weights_axes.update(clm_axes) + return other - return True + @_manage_log_level_via_verbosity + def _equivalent_construct_data( + self, + field1, + key0=None, + key1=None, + s=None, + t=None, + atol=None, + rtol=None, + verbose=None, + axis_map=None, + ): + """True if the field has equivalent construct data to another. - def _weights_scale(self, w, scale): - """Scale the weights so that they are <= scale. + Two real numbers ``x`` and ``y`` are considered equal if + ``|x-y|<=atol+rtol|y|``, where ``atol`` (the tolerance on absolute + differences) and ``rtol`` (the tolerance on relative differences) + are positive, typically very small numbers. See the *atol* and + *rtol* parameters. :Parameters: - w: `Data` - The weights to be scaled. - - scale: number or `None` - The maximum value of the scaled weights. If `None` then no - scaling is applied. + key0: `str` - :Returns: + key1: `str` - `Data` - The scaled weights. + field1: `Field` - """ - if scale is None: - return w + s: `dict`, optional - if scale <= 0: - raise ValueError( - "Can't set 'scale' parameter to a negative number. " - f"Got {scale!r}" - ) + t: `dict`, optional - w = w / w.max() - if scale != 1: - w = w * scale + atol: `float`, optional + The tolerance on absolute differences between real + numbers. The default value is set by the `atol` function. - return w + rtol: `float`, optional + The tolerance on relative differences between real + numbers. The default value is set by the `rtol` function. - def _weights_yyy( - self, domain_axis, geometry_type, methods=False, auto=False - ): - """Checks whether weights can be created for given coordinates. + {{verbose: `int` or `str` or `None`, optional}} - .. versionadded:: 3.2.0 + """ + item0 = self.constructs[key0] + item1 = field1.constructs[key1] - :Parameters: + if item0.has_data() != item1.has_data(): + if is_log_level_info(logger): + logger.info( + f"{self.__class__.__name__}: Only one item has data" + ) # pragma: no cover - domain_axis : `str` or `None` + return False - geometry_type: `str` - Either ``'polygon'`` or ``'line'``. + if not item0.has_data(): + # Neither field has a data array + return True - auto: `bool` + if item0.size != item1.size: + if is_log_level_info(logger): + logger.info( + f"{self.__class__.__name__}: Different metadata construct " + f"data array size: {item0.size} != {item1.size}" + ) # pragma: no cover - :Returns: + return False - `tuple` + if item0.ndim != item1.ndim: + if is_log_level_info(logger): + logger.info( + f"{self.__class__.__name__}: Different data array ranks " + f"({item0.ndim}, {item1.ndim})" + ) # pragma: no cover - """ - aux_X = None - aux_Y = None - aux_Z = None - x_axis = None - y_axis = None - z_axis = None - - auxiliary_coordinates_1d = self.auxiliary_coordinates( - filter_by_naxes=(1,), todict=True - ) + return False - for key, aux in auxiliary_coordinates_1d.items(): - if aux.get_geometry(None) != geometry_type: - continue + axes0 = self.get_data_axes(key0, default=()) + axes1 = field1.get_data_axes(key1, default=()) - if aux.X: - aux_X = aux.copy() - x_axis = self.get_data_axes(key)[0] - if domain_axis is not None and x_axis != domain_axis: - aux_X = None - continue - elif aux.Y: - aux_Y = aux.copy() - y_axis = self.get_data_axes(key)[0] - if domain_axis is not None and y_axis != domain_axis: - aux_Y = None - continue - elif aux.Z: - aux_Z = aux.copy() - z_axis = self.get_data_axes(key)[0] - if domain_axis is not None and z_axis != domain_axis: - aux_Z = None - continue + if s is None: + s = self.analyse_items() + if t is None: + t = field1.analyse_items() - if aux_X is None or aux_Y is None: - if auto: - return (None,) * 4 + transpose_axes = [] + if axis_map is None: + for axis0 in axes0: + axis1 = t["id_to_axis"].get(s["axis_to_id"][axis0], None) + if axis1 is None: + if is_log_level_info(logger): + # TODO: improve message here (make user friendly): + logger.info( + "t['id_to_axis'] does not have a key " + f"s['axis_to_id'][axis0] for " + f"{self.__class__.__name__}" + ) # pragma: no cover - raise ValueError( - "Can't create weights: Need both X and Y nodes to " - f"calculate {geometry_type} geometry weights" - ) + return False - if x_axis != y_axis: - if auto: - return (None,) * 4 + transpose_axes.append(axes1.index(axis1)) + else: + for axis0 in axes0: + axis1 = axis_map.get(axis0) + if axis1 is None: + if is_log_level_info(logger): + # TODO: improve message here (make user friendly): + logger.info( + f"axis_map[axis0] is None for {self.__class__.__name__}" + ) # pragma: no cover - raise ValueError( - "Can't create weights: X and Y nodes span different " - "domain axes" - ) + return False - axis = x_axis + transpose_axes.append(axes1.index(axis1)) - if aux_X.get_bounds(None) is None or aux_Y.get_bounds(None) is None: - # Not both X and Y coordinates have bounds - if auto: - return (None,) * 4 + copy1 = True - raise ValueError("Not both X and Y coordinates have bounds") + if transpose_axes != list(range(item1.ndim)): + if copy1: + item1 = item1.copy() + copy1 = False - if aux_X.bounds.shape != aux_Y.bounds.shape: - if auto: - return (None,) * 4 + item1.transpose(transpose_axes, inplace=True) - raise ValueError( - "Can't find weights: X and Y geometry coordinate bounds " - "must have the same shape. " - f"Got {aux_X.bounds.shape} and {aux_Y.bounds.shape}" - ) + if item0.shape != item1.shape: + # add traceback TODO + return False - if aux_Z is None: - for key, aux in auxiliary_coordinates_1d.items(): - if aux.Z: - aux_Z = aux.copy() - z_axis = self.get_data_axes(key)[0] + flip_axes = [ + i + for i, (axis1, axis0) in enumerate(zip(axes1, axes0)) + if field1.direction(axis1) != self.direction(axis0) + ] - # Check Z coordinates - if aux_Z is not None: - if z_axis != x_axis: - if auto: - return (None,) * 4 + if flip_axes: + if copy1: + item1 = item1.copy() + copy1 = False - raise ValueError( - "Z coordinates span different domain axis to X and Y " - "geometry coordinates" - ) + item1.flip(flip_axes, inplace=True) - return axis, aux_X, aux_Y, aux_Z + if not item0._equivalent_data( + item1, rtol=rtol, atol=atol, verbose=verbose + ): + # add traceback TODO + return False - # ---------------------------------------------------------------- - # End of worker functions for weights - # ---------------------------------------------------------------- + return True - # ---------------------------------------------------------------- - # Attributes - # ---------------------------------------------------------------- @property def DSG(self): """True if the field contains a collection of discrete sampling @@ -4391,17 +3123,18 @@ def weights( ): """Return weights for the data array values. - The weights are those used during a statistical collapse of the - data. For example when computing a area weight average. + The weights are those used during a statistical collapse of + the data. For example when computing a area weight average. Weights for any combination of axes may be returned. Weights are either derived from the field construct's metadata - (such as coordinate cell sizes) or provided explicitly in the form - of other `Field` constructs. In any case, the outer product of - these weights components is returned in a field which is - broadcastable to the original field (see the *components* parameter - for returning the components individually). + (such as coordinate cell sizes) or provided explicitly in the + form of other `Field` constructs. In any case, the outer + product of these weights components is returned in a field + which is broadcastable to the original field (see the + *components* parameter for returning the components + individually). By default null, equal weights are returned. @@ -4420,56 +3153,65 @@ def weights( greater than 1, raising an exception if this is not possible (this is the default).; - * **Type 2** will always succeed in creating weights for - all axes of the field, even if some of those weights are - null. + * **Type 2** will always succeed in creating weights + for all axes of the field, even if some of those + weights are null. * **Type 3** allows particular types of weights to be - defined for particular axes, and an exception will be - raised if it is not possible to the create weights. + defined for particular axes, and an exception will + be raised if it is not possible to the create + weights. .. **Type 1** and **Type 2** come at the expense of not - always being able to control exactly how the weights are - created (although which methods were used can be inspected - with use of the *methods* parameter). + always being able to control exactly how the weights + are created (although which methods were used can be + inspected with use of the *methods* parameter). * **Type 1**: *weights* may be: - ========== ============================================ + ========== ======================================== *weights* Description - ========== ============================================ - `True` This is the default. Weights are created for - all axes (or a subset of them, see the - *axes* parameter). Set the *methods* - parameter to find out how the weights were - actually created. - - The weights components are created for axes - of the field by one or more of the following - methods, in order of preference, + ========== ======================================== + `True` This is the default. Weights are created + for all axes (or a subset of them, see + the *axes* parameter). Set the *methods* + parameter to find out how the weights + were actually created. + + The weights components are created for + axes of the field by one or more of the + following methods, in order of + preference, 1. Volume cell measures 2. Area cell measures - 3. Area calculated from (grid) latitude - and (grid) longitude dimension - coordinate constructs with bounds - 4. Cell sizes of dimension coordinate + 3. Area calculated from X and Y + dimension coordinate constructs + with bounds + 4. Area calculated from 1-d auxiliary + coordinate constructs for geomtries + or a UGRID mesh topology. + 5. Length calculated from 1-d auxiliary + coordinate constructs for geomtries + or a UGRID mesh topology. + 6. Cell sizes of dimension coordinate constructs with bounds - 5. Equal weights + 7. Equal weights and the outer product of these weights - components is returned in a field constructs - which is broadcastable to the original field - construct (see the *components* parameter). - ========== ============================================ + components is returned in a field + constructs which is broadcastable to the + original field construct (see the + *components* parameter). + ========== ======================================== * **Type 2**: *weights* may be one of: - ========== ============================================ + ========== ======================================== *weights* Description - ========== ============================================ + ========== ======================================== `None` Equal weights for all axes. `False` Equal weights for all axes. @@ -4479,23 +3221,32 @@ def weights( construct's data, unless the *axes* parameter is also set. - `dict` Explicit weights in a dictionary of the form - that is returned from a call to the + `dict` Explicit weights in a dictionary of the + form that is returned from a call to the `weights` method with ``component=True`` - ========== ============================================ + ========== ======================================== * **Type 3**: *weights* may be one, or a sequence, of: ============ ========================================== *weights* Description ============ ========================================== - ``'area'`` Cell area weights from the field - construct's area cell measure construct - or, if one doesn't exist, from (grid) - latitude and (grid) longitude dimension - coordinate constructs. Set the *methods* - parameter to find out how the weights were - actually created. + ``'area'`` Cell area weights. The weights + components are created for axes of the + field by the following methods, in + order of preference, + + 1. Area cell measures. + 2. X and Y dimension coordinate + constructs with bounds. + 3. X and Y 1-d auxiliary coordinate + constructs for polygon cells + defined by geometries or a UGRID + mesh topology. + + Set the *methods* parameter to find + out how the weights were actually + created. ``'volume'`` Cell volume weights from the field construct's volume cell measure construct. @@ -4510,8 +3261,8 @@ def weights( ============ ========================================== If *weights* is a sequence of any combination of the - above then the returned field contains the outer product - of the weights defined by each element of the + above then the returned field contains the outer + product of the weights defined by each element of the sequence. The ordering of the sequence is irrelevant. *Parameter example:* @@ -4521,15 +3272,15 @@ def weights( height: ``f.weights(['area', 'Z'])``. scale: number, optional - If set to a positive number then scale the weights so that - they are less than or equal to that number. If weights - components have been requested (see the *components* - parameter) then each component is scaled independently of - the others. + If set to a positive number then scale the weights so + that they are less than or equal to that number. If + weights components have been requested (see the + *components* parameter) then each component is scaled + independently of the others. *Parameter example:* - To scale all weights so that they lie between 0 and 1: - ``scale=1``. + To scale all weights so that they lie between 0 and + 1: ``scale=1``. measure: `bool`, optional Create weights that are cell measures, i.e. which @@ -4561,64 +3312,54 @@ def weights( the cell volumes will be calculated using the size of the vertical coordinate cells. - radius: optional - Specify the radius used for calculating the areas of - cells defined in spherical polar coordinates. The - radius is that which would be returned by this call of - the field construct's `~cf.Field.radius` method: - ``f.radius(radius)``. See the `cf.Field.radius` for - details. - - By default *radius* is ``'earth'`` which means that if - and only if the radius can not found from the datums - of any coordinate reference constructs, then the - default radius taken as 6371229 metres. + {{radius: optional}} components: `bool`, optional - If True then a dictionary of orthogonal weights components - is returned instead of a field. Each key is a tuple of - integers representing axis positions in the field - construct's data, with corresponding values of weights in - `Data` objects. The axes of weights match the axes of the - field construct's data array in the order given by their - dictionary keys. + If True then a dictionary of orthogonal weights + components is returned instead of a field. Each key is + a tuple of integers representing axis positions in the + field construct's data, with corresponding values of + weights in `Data` objects. The axes of weights match + the axes of the field construct's data array in the + order given by their dictionary keys. methods: `bool`, optional - If True, then return a dictionary describing methods used - to create the weights. + If True, then return a dictionary describing methods + used to create the weights. data: `bool`, optional - If True then return the weights in a `Data` instance that - is broadcastable to the original data. + If True then return the weights in a `Data` instance + that is broadcastable to the original data. .. versionadded:: 3.1.0 great_circle: `bool`, optional - If True then allow, if required, the derivation of i) area - weights from polygon geometry cells by assuming that each - cell part is a spherical polygon composed of great circle - segments; and ii) and the derivation of line-length - weights from line geometry cells by assuming that each - line part is composed of great circle segments. + If True then allow, if required, the derivation of i) + area weights from polygon cells by assuming that each + cell part is a spherical polygon composed of great + circle segments; and ii) and the derivation of + line-length weights line cells by assuming that each + line part is composed of great circle segments. Only + applies to geometry and UGRID cells. .. versionadded:: 3.2.0 axes: (sequence of) `int` or `str`, optional - Modify the behaviour when *weights* is `True` or a `Data` - instance. Ignored for any other value the *weights* - parameter. + Modify the behaviour when *weights* is `True` or a + `Data` instance. Ignored for any other value the + *weights* parameter. - If *weights* is `True` then weights are created only for - the specified axes (as opposed to all + If *weights* is `True` then weights are created only + for the specified axes (as opposed to all axes). I.e. ``weight=True, axes=axes`` is identical to ``weights=axes``. - If *weights* is a `Data` instance then the specified axes - identify each dimension of the given weights. If the - weights do not broadcast to the field construct's data - then setting the *axes* parameter is required so that the - broadcasting can be inferred, otherwise setting the *axes* - is not required. + If *weights* is a `Data` instance then the specified + axes identify each dimension of the given weights. If + the weights do not broadcast to the field construct's + data then setting the *axes* parameter is required so + that the broadcasting can be inferred, otherwise + setting the *axes* is not required. *Parameter example:* ``axes='T'`` @@ -4636,9 +3377,9 @@ def weights( :Returns: `Field` or `Data` or `dict` - The weights field; or if *data* is True, weights data in - broadcastable form; or if *components* is True, orthogonal - weights in a dictionary. + The weights field; or if *data* is True, weights data + in broadcastable form; or if *components* is True, + orthogonal weights in a dictionary. **Examples** @@ -4666,6 +3407,8 @@ def weights( (2,): 'linear longitude'} """ + from .weights import Weights + if isinstance(weights, str) and weights == "auto": _DEPRECATION_ERROR_KWARG_VALUE( self, @@ -4701,7 +3444,7 @@ def weights( return Data(1.0, "1") # Return a field containing a single weight of 1 - return self._weights_field_scalar() + return Weights.field_scalar(self) # Still here? if methods: @@ -4713,8 +3456,8 @@ def weights( # All axes which have weights weights_axes = set() - if radius is not None: - radius = self.radius(default=radius) + # if radius is not None: + # radius = self.radius(default=radius) if weights is True and axes is not None: # -------------------------------------------------------- @@ -4727,18 +3470,19 @@ def weights( # Auto-detect all weights # -------------------------------------------------------- # Volume weights - if self._weights_measure( - "volume", comp, weights_axes, methods=methods, auto=True + if Weights.cell_measure( + self, "volume", comp, weights_axes, methods=methods, auto=True ): # Found volume weights from cell measures pass - elif self._weights_measure( - "area", comp, weights_axes, methods=methods, auto=True + elif Weights.cell_measure( + self, "area", comp, weights_axes, methods=methods, auto=True ): # Found area weights from cell measures pass - elif self._weights_area_XY( + elif Weights.area_XY( + self, comp, weights_axes, measure=measure, @@ -4753,7 +3497,8 @@ def weights( domain_axes = self.domain_axes(todict=True) for da_key in domain_axes: - if self._weights_geometry_area( + if Weights.polygon_area( + self, da_key, comp, weights_axes, @@ -4765,7 +3510,8 @@ def weights( ): # Found area weights from polygon geometries pass - elif self._weights_geometry_line( + elif Weights.line_length( + self, da_key, comp, weights_axes, @@ -4777,7 +3523,8 @@ def weights( ): # Found linear weights from line geometries pass - elif self._weights_linear( + elif Weights.linear( + self, da_key, comp, weights_axes, @@ -4840,13 +3587,14 @@ def weights( # -------------------------------------------------------- # Field # -------------------------------------------------------- - self._weights_field([weights], comp, weights_axes) + Weights.field(self, [weights], comp, weights_axes) elif isinstance(weights, Data): # -------------------------------------------------------- # Data # -------------------------------------------------------- - self._weights_data( + Weights.data( + self, weights, comp, weights_axes, @@ -4869,10 +3617,10 @@ def weights( else: axes.append(weights) else: - # In rare edge cases, e.g. if a user sets: - # weights=f[0].cell_area - # when they mean weights=f[0].cell_area(), we reach this - # code but weights is not iterable. So check it is first: + # In rare edge cases (e.g. if a user sets + # `weights=f[0].cell_area` when they really meant + # `weights=f[0].cell_area()`) we reach this code but + # find that weights is not iterable. So check it is.x try: weights = iter(weights) except TypeError: @@ -4893,22 +3641,33 @@ def weights( axes.append(w) # Field weights - self._weights_field(fields, comp, weights_axes) + Weights.field(self, fields, comp, weights_axes) # Volume weights if "volume" in cell_measures: - self._weights_measure( - "volume", comp, weights_axes, methods=methods, auto=False + Weights.cell_measure( + self, + "volume", + comp, + weights_axes, + methods=methods, + auto=False, ) # Area weights if "area" in cell_measures: - if self._weights_measure( - "area", comp, weights_axes, methods=methods, auto=True + if Weights.cell_measure( + self, + "area", + comp, + weights_axes, + methods=methods, + auto=True, ): # Found area weights from cell measures pass - elif self._weights_area_XY( + elif Weights.area_XY( + self, comp, weights_axes, measure=measure, @@ -4920,8 +3679,9 @@ def weights( # coordinates pass else: - # Found area weights from polygon geometries - self._weights_geometry_area( + # Found area weights from UGRID/geometry cells + Weights.polygon_area( + self, None, comp, weights_axes, @@ -4940,7 +3700,8 @@ def weights( f"{axis!r}" ) - if self._weights_geometry_area( + if Weights.polygon_area( + self, da_key, comp, weights_axes, @@ -4952,7 +3713,8 @@ def weights( ): # Found area weights from polygon geometries pass - elif self._weights_geometry_line( + elif Weights.line_length( + self, da_key, comp, weights_axes, @@ -4965,7 +3727,8 @@ def weights( # Found linear weights from line geometries pass else: - self._weights_linear( + Weights.linear( + self, da_key, comp, weights_axes, @@ -4983,10 +3746,11 @@ def weights( del comp[(yaxis,)] weights_axes.discard(xaxis) weights_axes.discard(yaxis) - if not self._weights_measure( - "area", comp, weights_axes, methods=methods + if not Weights.cell_measure( + self, "area", comp, weights_axes, methods=methods ): - self._weights_area_XY( + Weights.area_XY( + self, comp, weights_axes, measure=measure, @@ -4996,23 +3760,16 @@ def weights( if not methods: if scale is not None: - # -------------------------------------------------------- + # ---------------------------------------------------- # Scale the weights so that they are <= scale - # -------------------------------------------------------- + # ---------------------------------------------------- for key, w in comp.items(): - comp[key] = self._weights_scale(w, scale) + comp[key] = Weights.scale(w, scale) for w in comp.values(): if not measure: w.override_units("1", inplace=True) - mn = w.minimum() - if mn <= 0: - raise ValueError( - "All weights must be positive. " - f"Got a weight of {mn}" - ) - if components or methods: # -------------------------------------------------------- # Return a dictionary of component weights, which may be @@ -5034,7 +3791,7 @@ def weights( # No component weights have been defined so return an # equal weights field # -------------------------------------------------------- - f = self._weights_field_scalar() + f = Weights.field_scalar(self) if data: return f.data @@ -5055,7 +3812,7 @@ def weights( # -------------------------------------------------------- # Scale the weights so that they are <= scale # -------------------------------------------------------- - wdata = self._weights_scale(wdata, scale) + wdata = Weights.scale(wdata, scale) # ------------------------------------------------------------ # Reorder the data so that its dimensions are in the same @@ -7788,6 +6545,7 @@ def collapse( a = self.domain_axis(x, key=True, default=None) if a is None: raise ValueError(msg.format(x)) + axes2.append(a) all_axes.append(axes2) @@ -7807,8 +6565,6 @@ def collapse( # # ------------------------------------------------------------ domain_axes = f.domain_axes(todict=False, cached=domain_axes) - # auxiliary_coordinates = f.auxiliary_coordinates(view=True) - # dimension_coordinates = f.dimension_coordinates(view=True) for method, axes, within, over, axes_in in zip( all_methods, all_axes, all_within, all_over, input_axes @@ -7818,8 +6574,6 @@ def collapse( raise ValueError(f"Unknown collapse method: {method!r}") method = method2 - - # collapse_axes_all_sizes = domain_axes.filter_by_key(*axes) collapse_axes_all_sizes = f.domain_axes( filter_by_key=axes, todict=False ) @@ -7923,6 +6677,8 @@ def collapse( "simultaneously" ) + axis = [a for a in collapse_axes][0] + # ------------------------------------------------------------ # Grouped collapse: Calculate weights # ------------------------------------------------------------ @@ -7954,12 +6710,17 @@ def collapse( radius=radius, great_circle=great_circle, ) - - if not g_weights: + if g_weights: + # For grouped collapses, bring the weights + # into memory. This is to prevent lazy + # operations being run on the entire weights + # array for every group. + iaxes = (self.get_data_axes().index(axis),) + if iaxes in g_weights: + g_weights[iaxes] = g_weights[iaxes].persist() + else: g_weights = None - axis = [a for a in collapse_axes][0] - f = f._collapse_grouped( method, axis, @@ -8050,7 +6811,6 @@ def collapse( radius=radius, great_circle=great_circle, ) - if d_weights: d_kwargs["weights"] = d_weights @@ -14408,6 +13168,7 @@ def section(self, axes=None, stop=None, min_step=1, **kwargs): @_deprecated_kwarg_check("i", version="3.0.0", removed_at="4.0.0") @_inplace_enabled(default=False) + @_manage_log_level_via_verbosity def regrids( self, dst, @@ -14425,6 +13186,7 @@ def regrids( check_coordinates=False, min_weight=None, weights_file=None, + verbose=None, inplace=False, i=False, _compute_field_mass=None, @@ -14465,6 +13227,12 @@ def regrids( the points on either side are together without a gap (as is the case for NEMO model outputs). + **UGRID meshes** + + Data defined on UGRID face or node cells may be regridded to + any other latitude-longitude grid, including other UGRID + meshes. + **Cyclicity of the X axis** The cyclicity of the X (longitude) axes of the source and @@ -14595,7 +13363,11 @@ def regrids( {{weights_file: `str` or `None`, optional}} - .. versionadded:: 3.15.2 + .. versionadded:: 3.15.2 + + {{verbose: `int` or `str` or `None`, optional}} + + .. versionadded:: 3.16.0 {{inplace: `bool`, optional}} @@ -14724,6 +13496,11 @@ def regridc( the field being regridded and the specification of the destination grid given by the *dst* parameter. + **UGRID meshes** + + At present, Cartesian regridding is only available when + neither the source nor destination grid is a UGRID mesh. + {{regrid Masked cells}} {{regrid Implementation}} @@ -14835,7 +13612,7 @@ def regridc( {{weights_file: `str` or `None`, optional}} - .. versionadded:: 3.15.2 + .. versionadded:: 3.15.2 {{inplace: `bool`, optional}} diff --git a/cf/fieldancillary.py b/cf/fieldancillary.py index c9241863d8..ca32eeda3c 100644 --- a/cf/fieldancillary.py +++ b/cf/fieldancillary.py @@ -4,37 +4,4 @@ class FieldAncillary(mixin.PropertiesData, cfdm.FieldAncillary): - """A field ancillary construct of the CF data model. - - The field ancillary construct provides metadata which are - distributed over the same sampling domain as the field itself. For - example, if a data variable holds a variable retrieved from a - satellite instrument, a related ancillary data variable might - provide the uncertainty estimates for those retrievals (varying - over the same spatiotemporal domain). - - The field ancillary construct consists of an array of the - ancillary data, which is zero-dimensional or which depends on one - or more of the domain axes, and properties to describe the - data. It is assumed that the data do not depend on axes of the - domain which are not spanned by the array, along which the values - are implicitly propagated. CF-netCDF ancillary data variables - correspond to field ancillary constructs. Note that a field - ancillary construct is constrained by the domain definition of the - parent field construct but does not contribute to the domain's - definition, unlike, for instance, an auxiliary coordinate - construct or domain ancillary construct. - - **NetCDF interface** - - {{netcdf variable}} - - """ - - def __repr__(self): - """Called by the `repr` built-in function. - - x.__repr__() <==> repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) + + .. versionadded:: 3.16.0 + + """ + return super().__repr__().replace("<", " repr(x) + + .. versionadded:: 3.16.0 + + """ + return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " repr(x) - - """ - return super().__repr__().replace("<", " 1: + mask = da.transpose(mask, axes=axes) return mask @@ -1819,6 +2166,13 @@ def update_coordinates(src, dst, src_grid, dst_grid): Replace the existing coordinate constructs that span the regridding axes with those from the destination grid. + Also, if the source grid is a mesh, remove the existing domain + topology and cell connectivity constructs that span the regridding + axis; and if the destination grid is a mesh copy omain topology + and cell connectivity constructs from the destination grid. + + .. versionadded:: 3.14.0 + :Parameters: src: `Field` @@ -1827,10 +2181,10 @@ def update_coordinates(src, dst, src_grid, dst_grid): dst: `Field` or `Domain` The field or domain containing the destination grid. - src_grid: `Grid` + src_grid: `Grid` or `Mesh` The definition of the source grid. - dst_grid: `Grid` + dst_grid: `Grid` or `Mesh` The definition of the destination grid. :Returns: @@ -1841,44 +2195,77 @@ def update_coordinates(src, dst, src_grid, dst_grid): src_axis_keys = src_grid.axis_keys dst_axis_keys = dst_grid.axis_keys - # Remove the source coordinates of new field - for key in src.coordinates( - filter_by_axis=src_axis_keys, axis_mode="or", todict=True + # Remove the source coordinate, domain topology and cell + # connectivity constructs from regridded field. + for key in src.constructs( + filter_by_type=( + "dimension_coordinate", + "auxiliary_coordinate", + "domain_topology", + "cell_connectivity", + ), + filter_by_axis=src_axis_keys, + axis_mode="or", + todict=True, ): src.del_construct(key) # Domain axes src_domain_axes = src.domain_axes(todict=True) dst_domain_axes = dst.domain_axes(todict=True) - for src_axis, dst_axis in zip(src_axis_keys, dst_axis_keys): - src_domain_axis = src_domain_axes[src_axis] - dst_domain_axis = dst_domain_axes[dst_axis] + if src_grid.n_regrid_axes == dst_grid.n_regrid_axes: + # Change the size of the regridded domain axes + for src_axis, dst_axis in zip(src_axis_keys, dst_axis_keys): + src_domain_axis = src_domain_axes[src_axis] + dst_domain_axis = dst_domain_axes[dst_axis] - src_domain_axis.set_size(dst_domain_axis.size) + src_domain_axis.set_size(dst_domain_axis.size) - ncdim = dst_domain_axis.nc_get_dimension(None) - if ncdim is not None: - src_domain_axis.nc_set_dimension(ncdim) + ncdim = dst_domain_axis.nc_get_dimension(None) + if ncdim is not None: + src_domain_axis.nc_set_dimension(ncdim) + else: + # The regridding has changed the number of data axes (e.g. by + # regridding a source mesh grid to a destination non-mesh + # grid, or vice versa), so insert new domain axis constructs + # for all of the new axes. + src_axis_keys = [ + src.set_construct(dst_domain_axes[dst_axis].copy()) + for dst_axis in dst_axis_keys + ] + src_grid.new_axis_keys = src_axis_keys - # Coordinates axis_map = { dst_axis: src_axis for dst_axis, src_axis in zip(dst_axis_keys, src_axis_keys) } dst_data_axes = dst.constructs.data_axes() - for key, aux in dst.coordinates( + # Copy coordinates constructs from the destination grid + for key, coord in dst.coordinates( filter_by_axis=dst_axis_keys, axis_mode="subset", todict=True ).items(): axes = [axis_map[axis] for axis in dst_data_axes[key]] - src.set_construct(aux, axes=axes) + src.set_construct(coord, axes=axes) + + # Copy domain topology and cell connectivity constructs from the + # destination grid + if dst_grid.is_mesh: + for key, topology in dst.constructs( + filter_by_type=("domain_topology", "cell_connectivity"), + filter_by_axis=dst_axis_keys, + axis_mode="exact", + todict=True, + ).items(): + axes = [axis_map[axis] for axis in dst_data_axes[key]] + src.set_construct(topology, axes=axes) -def update_non_coordinates( - src, dst, regrid_operator, src_grid=None, dst_grid=None -): +def update_non_coordinates(src, dst, src_grid, dst_grid, regrid_operator): """Update the coordinate references of the regridded field. + .. versionadded:: 3.14.0 + :Parameters: src: `Field` @@ -1923,14 +2310,17 @@ def update_non_coordinates( src.del_coordinate_reference(ref_key) # ---------------------------------------------------------------- - # Delete source grid cell measures and field ancillaries that span - # any of the regridding axes + # Delete source grid cell measure and field ancillary constructs + # that span any of the regridding axes. # ---------------------------------------------------------------- for key in src.constructs( - filter_by_type=("cell_measure", "field_ancillary"), todict=True + filter_by_type=("cell_measure", "field_ancillary"), + filter_by_axis=src_axis_keys, + axis_mode="or", + todict=True, ): - if set(data_axes[key]).intersection(src_axis_keys): - src.del_construct(key) + # if set(data_axes[key]).intersection(src_axis_keys): + src.del_construct(key) # ---------------------------------------------------------------- # Regrid any remaining source domain ancillaries that span all of @@ -2000,3 +2390,110 @@ def update_non_coordinates( if axes and set(axes).issubset(dst_axis_keys): src.set_coordinate_reference(ref, parent=dst, strict=True) + + +def update_data(src, regridded_data, src_grid): + """Insert the regridded field data. + + .. versionadded:: 3.16.0 + + .. seealso: `update_coordinates`, `update_non_coordinates` + + :Parameters: + + src`: `Field` + The regridded field construct, that will up updated + in-place. + + regridded_data: `numpy.ndarray` + The regridded array + + src_grid: `Grid` + The definition of the source grid. + + :Returns: + + `None` + + """ + data_axes = src.get_data_axes() + if src_grid.new_axis_keys: + # The regridding has changed the number of data axes (e.g. by + # regridding a source UGRID grid to a destination non-UGRID + # grid, or vice versa) => delete the old, superceded domain + # axis construct and update the list of data axes. + data_axes = list(data_axes) + index = data_axes.index(src_grid.axis_keys[0]) + for axis in src_grid.axis_keys: + data_axes.remove(axis) + src.del_construct(axis) + + data_axes[index:index] = src_grid.new_axis_keys + + src.set_data(regridded_data, axes=data_axes, copy=False) + + +def get_mesh(f): + """Get domain topology mesh information. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` or `Domain` + The construct from which to get the mesh information. + + :Returns: + + 3-`tuple` + If the field or domain has no domain topology construct + then ``(None, None, None)`` is returned. Otherwise the + tuple contains: + + * The domain topology construct + * The mesh location of the domain topology (e.g. ``'face'``) + * The identifier of domain axis construct that is spanned + by the domain topology construct + + """ + key, domain_topology = f.domain_topology(item=True, default=(None, None)) + if domain_topology is None: + return (None, None, None) + + return ( + domain_topology, + domain_topology.get_cell(""), + f.get_data_axes(key)[0], + ) + + +def has_coordinate_arrays(grid): + """Whether all grid coordinates have representative arrays. + + .. versionadded:: 3.16.0 + + :Parameters: + + grid: `Grid` + The definition of the grid. + + :Returns: + + `bool` + True if and only if all grid coordinates have + representative arrays. + + """ + if not grid.coords: + return False + + for coord in grid.coords: + try: + has_data = coord.has_data() + except AttributeError: + has_data = True + + if not has_data: + return False + + return True diff --git a/cf/regrid/regridoperator.py b/cf/regrid/regridoperator.py index 00ca216b6f..bcea12ec63 100644 --- a/cf/regrid/regridoperator.py +++ b/cf/regrid/regridoperator.py @@ -40,6 +40,7 @@ def __init__( dst_axes=None, dst=None, weights_file=None, + src_mesh_location=None, ): """**Initialisation** @@ -124,6 +125,13 @@ def __init__( .. versionadded:: 3.15.2 + src_mesh_location: `str`, optional + The UGRID mesh element of the source grid + (e.g. ``'face'``). An empty string should be used for + a non-UGRID source grid. + + .. versionadded:: 3.16.0 + """ super().__init__() @@ -149,6 +157,7 @@ def __init__( self._set_component("dst_axes", dst_axes, copy=False) self._set_component("dst", dst, copy=False) self._set_component("weights_file", weights_file, copy=False) + self._set_component("src_mesh_location", src_mesh_location, copy=False) def __repr__(self): """x.__repr__() <==> repr(x)""" @@ -313,6 +322,15 @@ def src_mask(self): """ return self._get_component("src_mask") + @property + def src_mesh_location(self): + """The UGRID mesh element of the source grid. + + .. versionadded:: 3.16.0 + + """ + return self._get_component("src_mesh_location") + @property def src_shape(self): """The shape of the source grid. @@ -391,6 +409,7 @@ def copy(self): dst_axes=self.dst_axes, dst=self.dst.copy(), weights_file=self.weights_file, + src_mesh_location=self.src_mesh_location, ) @_display_or_return @@ -431,6 +450,7 @@ def dump(self, display=True): "start_index", "src_axes", "dst_axes", + "src_mesh_location", "dst", "weights", "row", @@ -603,11 +623,11 @@ def tosparse(self): # Note: It is much more efficient to access 'weights.indptr' # and 'weights.data' directly, rather than iterating # over rows of 'weights' and using 'weights.getrow'. - count_nonzero = np.count_nonzero + # count_nonzero = np.count_nonzero indptr = weights.indptr.tolist() data = weights.data for j, (i0, i1) in enumerate(zip(indptr[:-1], indptr[1:])): - if not count_nonzero(data[i0:i1]): + if not data[i0:i1].size: dst_mask[j] = True if not dst_mask.any(): diff --git a/cf/test/create_test_files.py b/cf/test/create_test_files.py index 0cd4f5dd42..3adfa21b46 100644 --- a/cf/test/create_test_files.py +++ b/cf/test/create_test_files.py @@ -7,17 +7,20 @@ faulthandler.enable() # to debug seg faults and timeouts +import cfdm import netCDF4 -import cf - -VN = cf.CF() +VN = cfdm.CF() +# -------------------------------------------------------------------- +# DSG files +# -------------------------------------------------------------------- def _make_contiguous_file(filename): + """Make a netCDF file with a contiguous ragged array DSG feature.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" n.createDimension("station", 4) @@ -106,9 +109,10 @@ def _make_contiguous_file(filename): def _make_indexed_file(filename): + """Make a netCDF file with an indexed ragged array DSG feature.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" n.createDimension("station", 4) @@ -238,15 +242,16 @@ def _make_indexed_file(filename): def _make_indexed_contiguous_file(filename): + """Make a netCDF file with an indexed contiguous ragged array.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeriesProfile" # 3 stations n.createDimension("station", 3) # 58 profiles spreadover 4 stations, each at a different time - profile = n.createDimension("profile", 58) + n.createDimension("profile", 58) n.createDimension("obs", None) n.createDimension("name_strlen", 8) n.createDimension("bounds", 2) @@ -606,6 +611,9 @@ def _make_indexed_contiguous_file(filename): return filename +# -------------------------------------------------------------------- +# External variable files +# -------------------------------------------------------------------- def _make_external_files(): """Make netCDF files with external variables.""" @@ -622,7 +630,7 @@ def _pp( nc.createDimension("grid_latitude", 10) nc.createDimension("grid_longitude", 9) - nc.Conventions = "CF-" + VN + nc.Conventions = f"CF-{VN}" if parent: nc.external_variables = "areacella" @@ -715,10 +723,14 @@ def _pp( return parent_file, external_file, combined_file, external_missing_file +# -------------------------------------------------------------------- +# Gathered files +# -------------------------------------------------------------------- def _make_gathered_file(filename): """Make a netCDF file with a gathered array.""" def _jj(shape, list_values): + """Create and return a gathered array.""" array = np.ma.masked_all(shape) for i, (index, x) in enumerate(np.ndenumerate(array)): if i in list_values: @@ -727,7 +739,7 @@ def _jj(shape, list_values): n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" time = n.createDimension("time", 2) height = n.createDimension("height", 3) @@ -848,6 +860,9 @@ def _jj(shape, list_values): return filename +gathered = _make_gathered_file("gathered.nc") + + # -------------------------------------------------------------------- # Geometry files # -------------------------------------------------------------------- @@ -855,11 +870,12 @@ def _make_geometry_1_file(filename): """See n.comment for details.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" n.comment = ( - "Make a netCDF file with 2 node coordinates variables, each of " - "which has a corresponding auxiliary coordinate variable." + "Make a netCDF file with 2 node coordinates variables, " + "each of which has a corresponding auxiliary coordinate " + "variable." ) n.createDimension("time", 4) @@ -931,11 +947,12 @@ def _make_geometry_2_file(filename): """See n.comment for details.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" n.comment = ( - "A netCDF file with 3 node coordinates variables, only two of " - "which have a corresponding auxiliary coordinate variable." + "A netCDF file with 3 node coordinates variables, only " + "two of which have a corresponding auxiliary coordinate " + "variable." ) n.createDimension("time", 4) @@ -1011,16 +1028,18 @@ def _make_geometry_3_file(filename): """See n.comment for details.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" n.comment = ( - "A netCDF file with 3 node coordinates variables, each of which " - "contains only one point, only two of which have a corresponding " - "auxiliary coordinate variables. There is no node count variable." + "A netCDF file with 3 node coordinates variables, each of " + "which contains only one point, only two of which have a " + "corresponding auxiliary coordinate variables. There is no " + "node count variable." ) n.createDimension("time", 4) n.createDimension("instance", 3) + # node = n.createDimension('node' , 3) t = n.createVariable("time", "i4", ("time",)) t.units = "seconds since 2016-11-07 20:00 UTC" @@ -1087,11 +1106,11 @@ def _make_geometry_4_file(filename): """See n.comment for details.""" n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" n.comment = ( - "A netCDF file with 2 node coordinates variables, none of which " - "have a corresponding auxiliary coordinate variable." + "A netCDF file with 2 node coordinates variables, none of " + "which have a corresponding auxiliary coordinate variable." ) n.createDimension("time", 4) @@ -1157,9 +1176,13 @@ def _make_interior_ring_file(filename): n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") # Global attributes - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" - n.comment = "TODO" + n.comment = ( + "A netCDF file with an interior ring variable of geometry " + "coordinates where x and y (but not z) are node " + "coordinates." + ) # Dimensions n.createDimension("time", 4) @@ -1234,7 +1257,7 @@ def _make_interior_ring_file(filename): datum.longitude_of_prime_meridian = 0.0 pr = n.createVariable("pr", "f8", ("instance", "time")) - pr.standard_name = "preciptitation_amount" + pr.standard_name = "precipitation_amount" pr.standard_units = "kg m-2" pr.coordinates = "time lat lon z instance_id" pr.grid_mapping = "datum" @@ -1257,9 +1280,12 @@ def _make_interior_ring_file_2(filename): n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") # Global attributes - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.featureType = "timeSeries" - n.comment = "TODO" + n.comment = ( + "A netCDF file with an interior ring variable of geometry " + "coordinates where x, y and z are node coordinates." + ) # Dimensions n.createDimension("time", 4) @@ -1333,7 +1359,7 @@ def _make_interior_ring_file_2(filename): datum.longitude_of_prime_meridian = 0.0 pr = n.createVariable("pr", "f8", ("instance", "time")) - pr.standard_name = "preciptitation_amount" + pr.standard_name = "precipitation_amount" pr.standard_units = "kg m-2" pr.coordinates = "time lat lon z instance_id" pr.grid_mapping = "datum" @@ -1355,7 +1381,7 @@ def _make_string_char_file(filename): """See n.comment for details.""" n = netCDF4.Dataset(filename, "w", format="NETCDF4") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN}" n.comment = "A netCDF file with variables of string and char data types" n.createDimension("dim1", 1) @@ -1406,12 +1432,14 @@ def _make_string_char_file(filename): c_months1 = n.createVariable("c_months1", "S1", ("dim1", "strlen8")) c_months1.long_name = "char: One month" c_months1[:] = netCDF4.stringtochar(np.array(["December"], dtype="S8")) + c_months0 = n.createVariable("c_months0", "S1", ("strlen3",)) c_months0.long_name = "char: One month (scalar)" c_months0[:] = np.array(list("May")) c_numbers = n.createVariable("c_numbers", "S1", ("lat", "lon", "strlen5")) c_numbers.long_name = "char: Two dimensional" + np.empty((2, 3, 5), dtype="S1") c_numbers[...] = netCDF4.stringtochar(numbers) c_months4m = n.createVariable("c_months4m", "S1", ("time", "strlen7")) @@ -1424,73 +1452,6 @@ def _make_string_char_file(filename): return filename -def _make_broken_bounds_cdl(filename): - with open(filename, mode="w") as f: - f.write( - """netcdf broken_bounds { -dimensions: - lat = 180 ; - bnds = 2 ; - lon = 288 ; - time = UNLIMITED ; // (1825 currently) -variables: - double lat(lat) ; - lat:long_name = "latitude" ; - lat:units = "degrees_north" ; - lat:axis = "Y" ; - lat:bounds = "lat_bnds" ; - lat:standard_name = "latitude" ; - lat:cell_methods = "time: point" ; - double lat_bnds(lat, bnds) ; - lat_bnds:long_name = "latitude bounds" ; - lat_bnds:units = "degrees_north" ; - lat_bnds:axis = "Y" ; - double lon(lon) ; - lon:long_name = "longitude" ; - lon:units = "degrees_east" ; - lon:axis = "X" ; - lon:bounds = "lon_bnds" ; - lon:standard_name = "longitude" ; - lon:cell_methods = "time: point" ; - double lon_bnds(lon, bnds) ; - lon_bnds:long_name = "longitude bounds" ; - lon_bnds:units = "m" ; - lon_bnds:axis = "X" ; - float pr(time, lat, lon) ; - pr:long_name = "Precipitation" ; - pr:units = "kg m-2 s-1" ; - pr:missing_value = 1.e+20f ; - pr:_FillValue = 1.e+20f ; - pr:cell_methods = "area: time: mean" ; - pr:cell_measures = "area: areacella" ; - pr:standard_name = "precipitation_flux" ; - pr:interp_method = "conserve_order1" ; - pr:original_name = "pr" ; - double time(time) ; - time:long_name = "time" ; - time:units = "days since 1850-01-01 00:00:00" ; - time:axis = "T" ; - time:calendar_type = "noleap" ; - time:calendar = "noleap" ; - time:bounds = "time_bnds" ; - time:standard_name = "time" ; - time:description = "Temporal mean" ; - double time_bnds(time, bnds) ; - time_bnds:long_name = "time axis boundaries" ; - time_bnds:units = "days since 1850-01-01 00:00:00" ; - -// global attributes: - :external_variables = "areacella" ; - :Conventions = "CF-""" - + VN - + """" ; - :source = "model" ; - :comment = "Bounds variable has incompatible units to its parent coordinate variable" ; -} -""" - ) - - def _make_subsampled_1(filename): """Lossy compression by coordinate subsampling (1). @@ -5259,10354 +5220,271 @@ def _make_subsampled_2(filename): r[...] = np.arange(48 * 32).reshape(48, 32) n.close() - return filename -def _make_regrid_file(filename): - n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") +def _make_ugrid_1(filename): + """Create a UGRID file with a 2-d mesh topology.""" + n = netCDF4.Dataset(filename, "w") - n.Conventions = "CF-" + VN + n.Conventions = f"CF-{VN} UGRID-1.0" n.createDimension("time", 2) - n.createDimension("bounds2", 2) - n.createDimension("latitude", 30) - n.createDimension("longitude", 48) - n.createDimension("time_1", 1) - n.createDimension("lat", 73) - n.createDimension("lon", 96) - - latitude = n.createVariable("latitude", "f8", ("latitude",)) - latitude.standard_name = "latitude" - latitude.units = "degrees_north" - latitude.bounds = "latitude_bounds" - latitude[...] = np.arange(-87, 90.0, 6) - - longitude = n.createVariable("longitude", "f8", ("longitude",)) - longitude.standard_name = "longitude" - longitude.units = "degrees_east" - longitude.bounds = "longitude_bounds" - longitude[...] = np.arange(3.75, 360, 7.5) - - lat = n.createVariable("lat", "f8", ("lat",)) - lat.standard_name = "latitude" - lat.units = "degrees_north" - lat.bounds = "lat_bounds" - lat[...] = np.arange(-90, 91.0, 2.5) - - lon = n.createVariable("lon", "f8", ("lon",)) - lon.standard_name = "longitude" - lon.units = "degrees_east" - lon.bounds = "lon_bounds" - lon[...] = np.arange(3.75, 361, 3.75) + n.createDimension("nMesh2_node", 7) + n.createDimension("nMesh2_edge", 9) + n.createDimension("nMesh2_face", 3) + n.createDimension("Two", 2) + n.createDimension("Four", 4) + + Mesh2 = n.createVariable("Mesh2", "i4", ()) + Mesh2.cf_role = "mesh_topology" + Mesh2.topology_dimension = 2 + Mesh2.node_coordinates = "Mesh2_node_x Mesh2_node_y" + Mesh2.face_node_connectivity = "Mesh2_face_nodes" + Mesh2.edge_node_connectivity = "Mesh2_edge_nodes" + Mesh2.edge_dimension = "nMesh2_edge" + Mesh2.edge_coordinates = "Mesh2_edge_x Mesh2_edge_y" + Mesh2.face_coordinates = "Mesh2_face_x Mesh2_face_y" + Mesh2.face_edge_connectivity = "Mesh2_face_edges" + Mesh2.face_face_connectivity = "Mesh2_face_links" + Mesh2.edge_face_connectivity = "Mesh2_edge_face_links" + + Mesh2_face_nodes = n.createVariable( + "Mesh2_face_nodes", "i4", ("nMesh2_face", "Four"), fill_value=-99 + ) + Mesh2_face_nodes.long_name = "Maps every face to its corner nodes" + Mesh2_face_nodes[...] = [[2, 3, 1, 0], [4, 5, 3, 2], [1, 3, 6, -99]] - longitude_bounds = n.createVariable( - "longitude_bounds", "f8", ("longitude", "bounds2") + Mesh2_edge_nodes = n.createVariable( + "Mesh2_edge_nodes", "i4", ("Two", "nMesh2_edge") ) - longitude_bounds[..., 0] = longitude[...] - 3.75 - longitude_bounds[..., 1] = longitude[...] + 3.75 + Mesh2_edge_nodes.long_name = "Maps every edge to its two nodes" + Mesh2_edge_nodes[...] = [ + [1, 3, 3, 0, 2, 2, 2, 5, 3], + [6, 6, 1, 1, 0, 3, 4, 4, 5], + ] - latitude_bounds = n.createVariable( - "latitude_bounds", "f8", ("latitude", "bounds2") + # Optional mesh topology variables + Mesh2_face_edges = n.createVariable( + "Mesh2_face_edges", "i4", ("nMesh2_face", "Four"), fill_value=-99 ) - latitude_bounds[..., 0] = latitude[...] - 3 - latitude_bounds[..., 1] = latitude[...] + 3 + Mesh2_face_edges.long_name = "Maps every face to its edges." - lon_bounds = n.createVariable("lon_bounds", "f8", ("lon", "bounds2")) - lon_bounds[..., 0] = lon[...] - 1.875 - lon_bounds[..., 1] = lon[...] + 1.875 + Mesh2_face_links = n.createVariable( + "Mesh2_face_links", "i4", ("nMesh2_face", "Four"), fill_value=-99 + ) + Mesh2_face_links.long_name = "neighbour faces for faces" + Mesh2_face_links[...] = [ + [1, 2, -99, -99], + [0, -99, -99, -99], + [0, -99, -99, -99], + ] - lat_bounds = n.createVariable("lat_bounds", "f8", ("lat", "bounds2")) - lat_bounds[..., 0] = lat[...] - 1.25 - lat_bounds[..., 1] = lat[...] + 1.25 + Mesh2_edge_face_links = n.createVariable( + "Mesh2_edge_face_links", "i4", ("nMesh2_edge", "Two"), fill_value=-99 + ) + Mesh2_edge_face_links.long_name = "neighbour faces for edges" + + # Mesh node coordinates + Mesh2_node_x = n.createVariable("Mesh2_node_x", "f4", ("nMesh2_node",)) + Mesh2_node_x.standard_name = "longitude" + Mesh2_node_x.units = "degrees_east" + Mesh2_node_x[...] = [-45, -43, -45, -43, -45, -43, -40] + + Mesh2_node_y = n.createVariable("Mesh2_node_y", "f4", ("nMesh2_node",)) + Mesh2_node_y.standard_name = "latitude" + Mesh2_node_y.units = "degrees_north" + Mesh2_node_y[...] = [35, 35, 33, 33, 31, 31, 34] + + # Optional mesh face and edge coordinate variables + Mesh2_face_x = n.createVariable("Mesh2_face_x", "f4", ("nMesh2_face",)) + Mesh2_face_x.standard_name = "longitude" + Mesh2_face_x.units = "degrees_east" + Mesh2_face_x[...] = [-44, -44, -42] + + Mesh2_face_y = n.createVariable("Mesh2_face_y", "f4", ("nMesh2_face",)) + Mesh2_face_y.standard_name = "latitude" + Mesh2_face_y.units = "degrees_north" + Mesh2_face_y[...] = [34, 32, 34] + + Mesh2_edge_x = n.createVariable("Mesh2_edge_x", "f4", ("nMesh2_edge",)) + Mesh2_edge_x.standard_name = "longitude" + Mesh2_edge_x.units = "degrees_east" + Mesh2_edge_x[...] = [-41.5, -41.5, -43, -44, -45, -44, -45, -44, -43] + + Mesh2_edge_y = n.createVariable("Mesh2_edge_y", "f4", ("nMesh2_edge",)) + Mesh2_edge_y.standard_name = "latitude" + Mesh2_edge_y.units = "degrees_north" + Mesh2_edge_y[...] = [34.5, 33.5, 34, 35, 34, 33, 32, 31, 32] + + # Non-mesh coordinates + t = n.createVariable("time", "f8", ("time",)) + t.standard_name = "time" + t.units = "seconds since 2016-01-01 00:00:00" + t.bounds = "time_bounds" + t[...] = [43200, 129600] - time = n.createVariable("time", "f4", ("time",)) - time.standard_name = "time" - time.units = "days since 1860-1-1" - time.calendar = "360_day" - time.axis = "T" - time.bounds = "time_bounds" - time[...] = [15, 45] + t_bounds = n.createVariable("time_bounds", "f8", ("time", "Two")) + t_bounds[...] = [[0, 86400], [86400, 172800]] - time_bounds = n.createVariable("time_bounds", "f4", ("time", "bounds2")) - time_bounds[...] = [ - [ - 0, - 30, - ], - [30, 60], + # Data variables + ta = n.createVariable("ta", "f4", ("time", "nMesh2_face")) + ta.standard_name = "air_temperature" + ta.units = "K" + ta.mesh = "Mesh2" + ta.location = "face" + ta.coordinates = "Mesh2_face_x Mesh2_face_y" + ta[...] = [[282.96, 282.69, 283.21], [281.53, 280.99, 281.23]] + + v = n.createVariable("v", "f4", ("time", "nMesh2_edge")) + v.standard_name = "northward_wind" + v.units = "ms-1" + v.mesh = "Mesh2" + v.location = "edge" + v.coordinates = "Mesh2_edge_x Mesh2_edge_y" + v[...] = [ + [10.2, 10.63, 8.74, 9.05, 8.15, 10.89, 8.44, 10.66, 8.93], + [9.66, 10.74, 9.24, 10.58, 9.79, 10.27, 10.58, 11.68, 11.22], ] - time_1 = n.createVariable("time_1", "f4", ("time_1",)) - time_1.standard_name = "time" - time_1.units = "days since 1860-1-1" - time_1.calendar = "360_day" - time_1.axis = "T" - time_1.bounds = "time_1_bounds" - time_1[...] = 15 - - time_1_bounds = n.createVariable( - "time_1_bounds", "f4", ("time_1", "bounds2") - ) - time_1_bounds[...] = [0, 30] - - height = n.createVariable("height", "f8", ()) - height.units = "m" - height.standard_name = "height" - height.positive = "up" - height.axis = "Z" - height[...] = 2 - - src = n.createVariable("src", "f8", ("time", "latitude", "longitude")) - src.standard_name = "air_temperature" - src.units = "K" - src.coordinates = "height" - src.cell_methods = "time: mean" - - # Don't generate this data randomly - it's useful to see the real - # patterns of global temperature. - src[...] = [ - [ - [ - 243.6, - 242.4, - 241.8, - 241.5, - 241.2, - 240.8, - 240.5, - 240.4, - 240.2, - 240.5, - 241.2, - 241.9, - 242.5, - 243.0, - 243.4, - 244.1, - 245.2, - 246.4, - 247.0, - 247.4, - 248.3, - 250.2, - 251.6, - 252.3, - 253.9, - 255.8, - 257.6, - 258.7, - 258.5, - 257.7, - 256.8, - 255.6, - 254.1, - 252.0, - 251.6, - 252.4, - 254.0, - 255.1, - 254.8, - 254.3, - 253.4, - 252.6, - 251.5, - 249.9, - 248.3, - 246.4, - 244.9, - 244.2, - ], - [ - 243.3, - 241.0, - 239.8, - 238.3, - 237.4, - 237.4, - 238.9, - 240.3, - 241.3, - 241.2, - 241.2, - 240.8, - 241.0, - 242.6, - 244.4, - 245.5, - 246.4, - 248.2, - 249.8, - 251.0, - 254.3, - 260.7, - 265.4, - 266.1, - 265.9, - 265.9, - 266.4, - 266.1, - 265.3, - 264.2, - 262.8, - 261.8, - 261.0, - 258.7, - 256.9, - 257.3, - 259.5, - 262.7, - 264.4, - 264.9, - 265.4, - 264.2, - 264.7, - 263.4, - 258.3, - 251.8, - 247.2, - 245.4, - ], - [ - 248.6, - 245.7, - 245.6, - 244.7, - 243.3, - 243.3, - 244.2, - 245.2, - 249.4, - 251.7, - 248.8, - 245.0, - 243.0, - 244.0, - 245.7, - 245.2, - 244.7, - 246.0, - 246.9, - 248.7, - 252.4, - 257.6, - 266.8, - 269.5, - 269.7, - 270.1, - 270.5, - 270.6, - 270.2, - 269.2, - 266.7, - 266.1, - 266.7, - 267.5, - 267.3, - 266.2, - 266.5, - 268.1, - 267.3, - 267.5, - 271.5, - 271.5, - 271.5, - 271.0, - 267.6, - 261.5, - 255.5, - 252.0, - ], - [ - 270.3, - 269.9, - 269.6, - 268.4, - 266.0, - 264.0, - 258.8, - 256.8, - 262.0, - 265.2, - 263.6, - 257.3, - 254.3, - 253.9, - 255.6, - 256.9, - 255.9, - 253.9, - 254.5, - 261.0, - 263.8, - 267.9, - 270.5, - 271.8, - 272.3, - 272.6, - 273.4, - 273.9, - 273.7, - 273.6, - 273.3, - 273.4, - 273.9, - 273.8, - 273.8, - 273.6, - 274.4, - 274.6, - 271.1, - 268.0, - 271.2, - 271.5, - 271.4, - 271.3, - 271.6, - 271.5, - 270.8, - 270.5, - ], - [ - 274.5, - 274.3, - 274.4, - 274.3, - 274.5, - 274.5, - 273.4, - 273.1, - 273.2, - 273.3, - 273.0, - 271.5, - 271.6, - 272.5, - 272.8, - 272.8, - 272.6, - 272.5, - 272.1, - 272.5, - 273.5, - 273.6, - 274.0, - 274.8, - 274.9, - 275.0, - 275.0, - 275.0, - 274.9, - 274.7, - 274.8, - 274.9, - 275.1, - 275.2, - 275.5, - 275.8, - 276.0, - 276.2, - 276.0, - 273.4, - 272.8, - 273.9, - 274.5, - 274.6, - 274.7, - 274.7, - 274.7, - 274.6, - ], - [ - 275.3, - 275.2, - 275.4, - 275.3, - 275.4, - 275.7, - 275.7, - 275.8, - 275.9, - 275.5, - 274.9, - 274.5, - 274.3, - 274.6, - 274.7, - 275.0, - 275.2, - 275.3, - 275.5, - 275.6, - 276.0, - 276.7, - 277.0, - 276.8, - 276.8, - 277.0, - 277.1, - 276.8, - 276.7, - 277.1, - 277.0, - 277.4, - 277.3, - 277.2, - 277.2, - 277.3, - 277.5, - 278.0, - 278.8, - 279.1, - 278.3, - 278.2, - 277.8, - 277.3, - 277.4, - 276.9, - 276.4, - 275.8, - ], - [ - 278.6, - 278.7, - 278.4, - 278.0, - 277.8, - 278.0, - 278.3, - 278.2, - 278.1, - 278.0, - 278.7, - 279.4, - 279.1, - 279.2, - 279.1, - 278.9, - 279.0, - 279.6, - 279.8, - 280.1, - 280.2, - 280.6, - 281.1, - 280.0, - 280.0, - 280.5, - 281.2, - 281.0, - 280.8, - 281.3, - 281.6, - 281.4, - 281.1, - 280.7, - 280.3, - 280.1, - 280.3, - 280.7, - 280.8, - 283.0, - 281.4, - 280.3, - 279.6, - 279.4, - 279.7, - 279.9, - 279.1, - 278.6, - ], - [ - 283.9, - 283.2, - 282.5, - 281.8, - 281.6, - 282.0, - 282.4, - 282.3, - 282.3, - 283.0, - 284.3, - 284.2, - 284.0, - 283.7, - 283.6, - 283.7, - 284.0, - 284.2, - 284.3, - 285.0, - 285.2, - 285.1, - 285.2, - 286.9, - 287.3, - 286.5, - 286.2, - 285.7, - 285.6, - 285.7, - 285.5, - 285.2, - 285.0, - 284.5, - 284.0, - 283.4, - 283.0, - 283.1, - 283.0, - 288.0, - 285.5, - 285.2, - 284.0, - 283.2, - 283.4, - 284.7, - 284.9, - 284.6, - ], - [ - 289.3, - 288.6, - 288.9, - 289.4, - 288.8, - 289.1, - 289.5, - 289.3, - 289.2, - 289.6, - 289.4, - 288.9, - 288.4, - 288.1, - 288.0, - 288.2, - 288.3, - 288.4, - 289.4, - 290.3, - 291.6, - 290.6, - 290.8, - 291.8, - 291.8, - 290.8, - 290.1, - 290.1, - 290.7, - 290.5, - 290.1, - 289.9, - 289.6, - 289.1, - 288.5, - 287.6, - 286.8, - 286.9, - 287.5, - 294.9, - 293.2, - 291.4, - 289.9, - 289.6, - 290.1, - 290.5, - 290.3, - 289.8, - ], - [ - 292.5, - 292.3, - 293.5, - 292.9, - 296.4, - 295.3, - 294.8, - 294.7, - 294.4, - 293.9, - 293.1, - 292.5, - 291.8, - 291.3, - 291.9, - 293.9, - 292.1, - 293.6, - 297.5, - 295.3, - 295.3, - 295.4, - 295.1, - 294.6, - 294.5, - 294.0, - 294.0, - 294.6, - 295.1, - 295.1, - 295.1, - 295.0, - 294.7, - 294.1, - 293.3, - 292.0, - 290.6, - 289.5, - 293.3, - 301.4, - 299.6, - 296.1, - 294.7, - 294.5, - 294.6, - 294.8, - 294.0, - 293.0, - ], - [ - 293.4, - 293.4, - 295.6, - 293.0, - 297.2, - 299.2, - 298.2, - 297.2, - 296.2, - 295.3, - 294.6, - 294.2, - 294.3, - 294.2, - 295.8, - 298.7, - 297.5, - 299.4, - 300.3, - 298.5, - 297.7, - 297.8, - 297.4, - 297.0, - 296.9, - 296.7, - 297.0, - 297.1, - 297.3, - 297.4, - 297.2, - 296.8, - 296.2, - 295.6, - 294.8, - 293.6, - 292.1, - 290.7, - 290.9, - 297.7, - 300.3, - 297.1, - 298.1, - 296.9, - 295.9, - 295.2, - 294.4, - 293.7, - ], - [ - 295.2, - 295.6, - 295.6, - 295.3, - 298.0, - 300.5, - 298.8, - 298.5, - 297.4, - 296.6, - 296.5, - 296.6, - 296.7, - 296.8, - 298.6, - 301.8, - 301.4, - 299.2, - 299.3, - 299.3, - 300.0, - 299.5, - 299.1, - 299.1, - 298.9, - 298.8, - 299.0, - 299.0, - 298.9, - 298.5, - 297.5, - 296.3, - 295.0, - 294.3, - 293.7, - 292.8, - 292.0, - 292.6, - 290.3, - 292.7, - 299.9, - 296.4, - 297.0, - 297.4, - 296.1, - 295.3, - 294.6, - 294.5, - ], - [ - 297.4, - 296.9, - 294.4, - 294.9, - 296.7, - 300.0, - 299.4, - 299.2, - 298.7, - 298.7, - 299.3, - 299.7, - 299.7, - 299.8, - 300.7, - 301.9, - 301.3, - 300.1, - 301.1, - 301.1, - 301.4, - 301.1, - 300.7, - 300.3, - 300.3, - 300.6, - 300.5, - 300.4, - 300.0, - 299.4, - 298.2, - 296.5, - 294.9, - 293.7, - 292.9, - 292.9, - 293.9, - 293.8, - 289.6, - 297.1, - 298.9, - 296.4, - 296.1, - 298.7, - 297.6, - 296.6, - 296.1, - 296.4, - ], - [ - 300.5, - 299.9, - 295.9, - 295.1, - 295.0, - 299.8, - 300.3, - 300.1, - 300.2, - 300.4, - 300.7, - 300.8, - 301.0, - 301.1, - 301.5, - 301.9, - 302.1, - 302.2, - 302.0, - 300.7, - 301.9, - 301.7, - 301.5, - 301.3, - 301.3, - 301.3, - 301.1, - 300.9, - 300.6, - 300.0, - 299.0, - 297.6, - 296.4, - 295.7, - 295.5, - 296.0, - 297.4, - 295.2, - 298.4, - 300.2, - 298.8, - 298.2, - 297.6, - 299.5, - 299.2, - 298.7, - 298.8, - 299.4, - ], - [ - 301.4, - 299.9, - 298.7, - 296.7, - 294.5, - 299.3, - 300.2, - 300.1, - 300.2, - 300.5, - 300.6, - 300.7, - 301.1, - 300.2, - 301.2, - 300.7, - 301.7, - 302.3, - 300.1, - 300.7, - 300.8, - 300.6, - 300.4, - 300.1, - 299.8, - 299.5, - 299.0, - 298.5, - 298.2, - 297.9, - 297.5, - 297.2, - 297.1, - 297.3, - 297.8, - 298.5, - 299.2, - 297.0, - 300.9, - 300.5, - 300.2, - 299.5, - 299.7, - 300.3, - 300.4, - 300.5, - 300.7, - 301.0, - ], - [ - 301.7, - 298.3, - 298.4, - 297.1, - 296.4, - 299.3, - 299.3, - 299.7, - 300.1, - 300.2, - 300.1, - 300.5, - 301.3, - 299.8, - 301.0, - 299.6, - 301.9, - 301.9, - 301.7, - 301.3, - 300.8, - 300.5, - 300.2, - 300.0, - 299.6, - 299.3, - 299.2, - 298.9, - 298.6, - 298.5, - 298.3, - 298.3, - 298.4, - 298.8, - 299.2, - 299.6, - 299.7, - 297.9, - 298.3, - 299.1, - 300.2, - 299.9, - 299.9, - 300.2, - 300.6, - 301.0, - 301.3, - 301.3, - ], - [ - 296.5, - 296.5, - 298.6, - 298.1, - 297.5, - 291.4, - 294.6, - 298.1, - 298.8, - 299.6, - 297.7, - 299.7, - 300.3, - 299.2, - 300.4, - 301.3, - 299.5, - 300.9, - 300.8, - 300.5, - 300.2, - 299.9, - 299.5, - 299.3, - 299.1, - 299.1, - 299.1, - 299.0, - 299.0, - 299.1, - 299.3, - 299.4, - 299.8, - 299.8, - 299.3, - 299.6, - 299.1, - 298.2, - 298.7, - 300.1, - 299.2, - 299.0, - 298.6, - 298.6, - 299.0, - 299.9, - 298.2, - 297.5, - ], - [ - 296.9, - 296.4, - 296.6, - 295.1, - 297.1, - 293.1, - 290.8, - 295.7, - 296.5, - 297.2, - 293.9, - 297.9, - 297.6, - 293.8, - 295.8, - 298.8, - 298.2, - 298.9, - 298.9, - 299.1, - 298.9, - 298.2, - 297.5, - 296.9, - 296.6, - 296.3, - 296.2, - 296.0, - 296.0, - 296.4, - 296.8, - 297.1, - 297.6, - 298.7, - 296.1, - 294.9, - 296.5, - 298.6, - 298.3, - 298.6, - 298.2, - 297.6, - 297.2, - 296.9, - 296.7, - 297.3, - 297.9, - 297.5, - ], - [ - 290.4, - 289.2, - 288.8, - 289.8, - 293.2, - 290.9, - 288.7, - 291.5, - 295.4, - 292.9, - 289.2, - 292.3, - 289.4, - 284.6, - 287.3, - 293.6, - 294.7, - 293.8, - 294.2, - 295.1, - 295.9, - 296.3, - 296.0, - 295.3, - 294.6, - 294.0, - 293.4, - 293.0, - 292.7, - 292.7, - 292.7, - 292.7, - 293.2, - 293.8, - 284.9, - 292.3, - 296.2, - 295.6, - 296.5, - 297.3, - 296.9, - 296.4, - 295.8, - 295.4, - 295.3, - 294.3, - 292.3, - 292.0, - ], - [ - 287.2, - 286.3, - 287.4, - 286.9, - 287.2, - 284.8, - 288.8, - 285.3, - 285.1, - 284.1, - 282.3, - 276.0, - 275.1, - 274.6, - 277.2, - 277.9, - 286.6, - 288.8, - 289.0, - 289.7, - 290.8, - 291.9, - 292.5, - 292.4, - 292.0, - 291.4, - 290.7, - 290.2, - 289.9, - 290.0, - 290.0, - 289.9, - 289.7, - 284.1, - 279.8, - 286.4, - 287.8, - 291.0, - 293.1, - 293.7, - 294.0, - 294.2, - 294.1, - 293.8, - 293.4, - 293.4, - 289.8, - 287.6, - ], - [ - 280.6, - 285.5, - 288.7, - 288.8, - 286.8, - 281.0, - 278.3, - 276.1, - 275.1, - 274.5, - 259.3, - 252.7, - 252.3, - 264.4, - 271.9, - 273.4, - 278.4, - 279.7, - 279.1, - 284.5, - 286.3, - 287.4, - 287.9, - 288.1, - 287.9, - 287.4, - 287.1, - 287.1, - 287.3, - 287.4, - 287.5, - 286.6, - 280.6, - 274.1, - 274.0, - 274.2, - 274.0, - 280.3, - 288.9, - 290.1, - 290.6, - 290.9, - 291.3, - 291.6, - 291.5, - 291.3, - 289.6, - 281.6, - ], - [ - 283.4, - 283.8, - 281.9, - 278.6, - 274.7, - 270.8, - 275.5, - 275.8, - 273.4, - 263.9, - 261.0, - 262.5, - 259.6, - 261.2, - 263.1, - 263.7, - 257.6, - 263.7, - 270.0, - 273.9, - 278.9, - 279.9, - 281.1, - 282.0, - 282.3, - 282.1, - 282.3, - 283.0, - 283.8, - 284.4, - 284.7, - 279.8, - 267.4, - 263.8, - 267.3, - 266.6, - 266.9, - 269.3, - 279.5, - 284.7, - 286.3, - 286.8, - 287.5, - 288.5, - 289.0, - 288.4, - 285.2, - 278.3, - ], - [ - 274.1, - 268.7, - 266.7, - 267.4, - 275.8, - 272.9, - 272.3, - 266.8, - 265.1, - 263.9, - 260.3, - 255.9, - 254.0, - 254.2, - 254.2, - 251.0, - 246.6, - 247.9, - 260.6, - 268.4, - 272.8, - 274.1, - 275.4, - 275.8, - 276.4, - 277.3, - 278.2, - 279.1, - 280.3, - 281.3, - 282.2, - 276.3, - 266.5, - 261.3, - 260.0, - 260.9, - 266.2, - 262.4, - 264.5, - 274.0, - 276.3, - 278.6, - 281.2, - 283.9, - 285.7, - 285.4, - 284.7, - 279.6, - ], - [ - 272.7, - 265.5, - 263.2, - 260.9, - 259.3, - 258.8, - 258.3, - 257.4, - 256.2, - 256.5, - 257.0, - 255.2, - 252.8, - 252.1, - 249.8, - 244.3, - 241.6, - 242.7, - 251.1, - 267.8, - 266.7, - 266.4, - 271.4, - 272.1, - 273.2, - 274.5, - 275.8, - 276.8, - 278.2, - 279.6, - 278.7, - 268.7, - 262.3, - 257.8, - 255.5, - 256.1, - 257.5, - 260.0, - 258.8, - 260.5, - 268.1, - 275.4, - 278.5, - 280.7, - 282.4, - 283.3, - 281.9, - 275.9, - ], - [ - 276.9, - 267.1, - 268.1, - 255.3, - 249.1, - 247.3, - 247.4, - 249.0, - 248.9, - 249.3, - 249.4, - 250.5, - 250.6, - 247.2, - 243.4, - 239.2, - 236.3, - 236.0, - 243.7, - 256.7, - 256.2, - 249.1, - 261.6, - 264.2, - 268.2, - 271.2, - 270.2, - 272.0, - 273.9, - 273.1, - 266.5, - 262.1, - 260.5, - 256.4, - 251.8, - 250.0, - 252.0, - 257.4, - 252.0, - 254.8, - 270.0, - 275.2, - 278.1, - 280.0, - 281.3, - 282.1, - 282.0, - 275.4, - ], - [ - 274.8, - 263.2, - 258.6, - 246.4, - 242.3, - 240.9, - 240.9, - 240.7, - 238.5, - 238.7, - 239.0, - 239.7, - 238.6, - 235.6, - 233.2, - 230.9, - 229.4, - 231.4, - 234.8, - 238.1, - 238.7, - 241.6, - 244.2, - 247.5, - 257.9, - 264.2, - 254.9, - 256.2, - 256.3, - 254.9, - 255.7, - 253.4, - 251.6, - 249.1, - 245.6, - 245.1, - 247.2, - 248.4, - 248.9, - 257.5, - 270.1, - 259.8, - 265.1, - 275.8, - 277.7, - 274.3, - 278.0, - 278.9, - ], - [ - 273.2, - 268.4, - 256.0, - 247.6, - 250.2, - 244.8, - 239.2, - 234.9, - 234.6, - 230.2, - 229.6, - 230.8, - 231.4, - 229.9, - 228.3, - 227.7, - 228.3, - 230.0, - 230.5, - 231.3, - 231.8, - 235.8, - 238.5, - 239.8, - 242.0, - 246.1, - 248.1, - 244.2, - 246.0, - 247.0, - 246.2, - 243.8, - 242.5, - 241.8, - 242.7, - 241.8, - 241.7, - 242.4, - 245.5, - 254.0, - 263.4, - 250.4, - 244.5, - 248.4, - 255.2, - 261.2, - 267.8, - 270.9, - ], - [ - 268.5, - 269.1, - 268.5, - 265.7, - 262.3, - 243.8, - 236.0, - 236.2, - 235.6, - 235.6, - 234.7, - 229.1, - 228.7, - 227.4, - 228.8, - 233.2, - 233.0, - 235.3, - 235.9, - 236.5, - 236.9, - 237.3, - 237.9, - 238.7, - 239.2, - 239.6, - 241.5, - 239.2, - 239.7, - 240.2, - 241.2, - 240.7, - 240.0, - 240.8, - 242.7, - 240.8, - 239.1, - 245.3, - 251.1, - 258.6, - 247.1, - 239.5, - 237.2, - 235.4, - 241.2, - 246.8, - 247.9, - 258.9, - ], - [ - 259.5, - 258.8, - 258.0, - 253.9, - 243.8, - 239.9, - 238.3, - 237.7, - 235.8, - 233.8, - 233.3, - 233.2, - 234.4, - 232.8, - 234.4, - 236.1, - 236.5, - 237.4, - 237.5, - 237.6, - 237.8, - 236.8, - 236.3, - 236.2, - 236.1, - 235.9, - 235.7, - 235.3, - 234.9, - 234.6, - 235.6, - 236.4, - 236.5, - 236.5, - 237.7, - 239.6, - 235.8, - 232.6, - 234.3, - 238.0, - 236.9, - 235.3, - 233.4, - 235.4, - 238.5, - 241.4, - 243.3, - 252.2, - ], - [ - 235.4, - 235.8, - 236.3, - 235.0, - 234.9, - 236.2, - 235.8, - 235.5, - 236.3, - 236.6, - 235.6, - 235.9, - 236.6, - 236.9, - 237.2, - 237.9, - 238.5, - 238.7, - 238.6, - 238.4, - 238.2, - 238.1, - 237.9, - 237.2, - 236.7, - 236.2, - 235.8, - 235.2, - 234.3, - 233.1, - 232.7, - 233.3, - 233.8, - 233.0, - 232.9, - 233.1, - 234.2, - 233.5, - 233.0, - 234.8, - 234.5, - 234.2, - 234.7, - 236.3, - 237.7, - 238.0, - 236.8, - 236.1, - ], - ], + pa = n.createVariable("pa", "f4", ("time", "nMesh2_node")) + pa.standard_name = "air_pressure" + pa.units = "hPa" + pa.mesh = "Mesh2" + pa.location = "node" + pa.coordinates = "Mesh2_node_x Mesh2_node_y" + pa[...] = [ + [999.67, 1006.45, 999.85, 1006.55, 1006.14, 1005.68, 999.48], [ - [ - 242.9, - 242.6, - 241.9, - 242.3, - 240.2, - 240.4, - 240.3, - 241.3, - 240.0, - 239.5, - 242.1, - 241.0, - 243.0, - 243.7, - 244.3, - 243.8, - 244.9, - 246.1, - 247.5, - 247.8, - 248.7, - 249.7, - 250.9, - 253.1, - 254.7, - 256.3, - 257.2, - 259.0, - 258.2, - 257.7, - 256.6, - 255.9, - 254.4, - 251.2, - 250.6, - 252.5, - 254.6, - 254.5, - 255.0, - 254.9, - 253.3, - 253.3, - 251.8, - 249.6, - 248.8, - 247.2, - 244.7, - 244.3, - ], - [ - 243.6, - 240.8, - 239.4, - 237.9, - 237.0, - 237.5, - 239.4, - 239.8, - 241.0, - 240.8, - 240.8, - 241.1, - 240.8, - 241.7, - 245.4, - 246.1, - 246.6, - 249.1, - 250.7, - 250.8, - 254.7, - 260.1, - 265.0, - 266.1, - 265.4, - 265.8, - 265.6, - 266.5, - 266.2, - 263.3, - 262.6, - 261.4, - 261.5, - 258.0, - 256.7, - 256.6, - 260.0, - 262.8, - 264.3, - 265.7, - 265.2, - 264.2, - 265.5, - 263.1, - 257.6, - 252.6, - 246.3, - 245.4, - ], - [ - 248.9, - 245.5, - 246.5, - 244.3, - 242.8, - 243.0, - 243.2, - 244.5, - 249.7, - 252.2, - 249.1, - 244.7, - 242.8, - 244.1, - 245.0, - 245.6, - 245.6, - 245.5, - 247.5, - 249.4, - 252.2, - 257.6, - 266.6, - 269.0, - 270.0, - 271.0, - 270.0, - 270.2, - 271.0, - 269.2, - 266.4, - 266.2, - 266.8, - 267.2, - 268.0, - 265.9, - 266.2, - 268.4, - 268.2, - 267.6, - 271.8, - 271.2, - 272.1, - 271.8, - 267.5, - 261.8, - 255.2, - 252.6, - ], - [ - 270.4, - 269.6, - 269.1, - 267.5, - 266.9, - 264.2, - 259.1, - 256.5, - 262.1, - 266.1, - 263.7, - 256.9, - 255.1, - 253.5, - 255.6, - 256.0, - 256.6, - 254.5, - 253.5, - 261.3, - 264.7, - 268.4, - 271.0, - 271.3, - 271.5, - 273.1, - 273.2, - 273.8, - 272.9, - 274.1, - 272.8, - 273.2, - 273.5, - 274.4, - 274.7, - 274.6, - 275.0, - 275.3, - 271.3, - 267.3, - 271.9, - 271.2, - 271.7, - 272.3, - 271.2, - 270.5, - 271.0, - 270.8, - ], - [ - 274.9, - 273.8, - 273.7, - 273.6, - 274.8, - 275.0, - 272.5, - 273.9, - 274.2, - 273.1, - 272.3, - 270.9, - 272.0, - 273.3, - 272.1, - 272.1, - 271.9, - 273.3, - 271.6, - 272.1, - 272.6, - 273.1, - 273.3, - 275.7, - 275.6, - 274.0, - 275.1, - 274.8, - 274.0, - 275.1, - 275.1, - 275.7, - 274.1, - 276.1, - 275.1, - 275.3, - 276.5, - 275.4, - 275.2, - 274.4, - 273.3, - 274.3, - 274.7, - 274.3, - 274.9, - 273.7, - 274.4, - 274.1, - ], - [ - 275.7, - 275.4, - 275.7, - 275.7, - 275.1, - 276.0, - 275.5, - 276.2, - 276.8, - 276.4, - 274.5, - 274.0, - 275.1, - 274.5, - 273.7, - 274.0, - 274.7, - 274.8, - 275.1, - 275.3, - 275.5, - 277.2, - 277.0, - 276.6, - 277.7, - 277.5, - 276.2, - 277.7, - 275.8, - 277.9, - 277.5, - 276.7, - 277.8, - 276.8, - 277.9, - 277.7, - 278.0, - 278.0, - 279.4, - 278.9, - 278.5, - 278.0, - 278.3, - 277.3, - 276.7, - 276.7, - 277.3, - 276.1, - ], - [ - 278.9, - 277.7, - 279.3, - 277.1, - 278.2, - 277.6, - 278.7, - 277.7, - 277.1, - 278.5, - 279.2, - 279.3, - 280.0, - 279.7, - 278.1, - 278.9, - 278.1, - 279.8, - 279.1, - 279.4, - 279.6, - 280.6, - 282.1, - 280.3, - 279.7, - 280.1, - 281.1, - 281.5, - 281.2, - 280.9, - 281.0, - 281.9, - 281.7, - 281.3, - 279.7, - 281.1, - 280.6, - 279.9, - 281.6, - 283.4, - 281.9, - 281.2, - 279.1, - 278.9, - 279.7, - 279.8, - 280.1, - 277.7, - ], - [ - 283.2, - 283.7, - 283.3, - 281.8, - 280.9, - 282.2, - 281.4, - 282.6, - 282.6, - 283.4, - 284.8, - 284.4, - 283.7, - 283.1, - 283.2, - 283.2, - 283.5, - 283.5, - 284.9, - 284.3, - 284.3, - 285.9, - 285.9, - 286.5, - 287.0, - 286.6, - 285.7, - 285.0, - 286.4, - 285.3, - 286.5, - 284.7, - 285.6, - 284.4, - 284.4, - 284.4, - 283.2, - 282.3, - 282.4, - 287.8, - 285.9, - 285.2, - 284.4, - 282.4, - 283.1, - 284.1, - 284.7, - 283.6, - ], - [ - 289.1, - 288.0, - 288.6, - 289.2, - 288.4, - 290.0, - 289.4, - 290.2, - 289.0, - 290.2, - 288.5, - 288.4, - 288.8, - 288.6, - 288.0, - 288.2, - 287.6, - 288.3, - 289.4, - 290.0, - 292.5, - 290.1, - 290.6, - 291.5, - 290.9, - 291.0, - 290.4, - 289.9, - 290.0, - 290.9, - 289.8, - 289.7, - 289.9, - 289.9, - 289.0, - 288.4, - 286.7, - 286.6, - 287.3, - 294.3, - 294.1, - 291.8, - 289.5, - 288.8, - 289.5, - 290.4, - 290.2, - 289.5, - ], - [ - 292.9, - 292.7, - 294.2, - 292.1, - 295.9, - 295.1, - 294.9, - 295.3, - 294.4, - 293.0, - 294.0, - 292.5, - 291.2, - 291.9, - 291.3, - 293.6, - 291.4, - 292.9, - 298.4, - 294.4, - 294.8, - 296.3, - 294.1, - 295.1, - 294.3, - 293.7, - 293.0, - 295.5, - 294.5, - 295.8, - 294.5, - 294.1, - 295.2, - 294.6, - 292.5, - 291.9, - 290.4, - 290.1, - 293.1, - 301.1, - 300.5, - 296.7, - 294.3, - 295.1, - 294.9, - 295.8, - 293.6, - 292.4, - ], - [ - 292.9, - 293.7, - 294.7, - 292.5, - 296.6, - 299.8, - 297.2, - 297.8, - 297.0, - 294.5, - 293.6, - 294.1, - 295.3, - 294.8, - 296.5, - 299.1, - 297.0, - 298.8, - 301.1, - 298.9, - 297.6, - 298.1, - 298.1, - 296.9, - 296.0, - 297.2, - 296.4, - 297.4, - 298.0, - 297.6, - 296.7, - 296.0, - 297.0, - 296.5, - 293.8, - 294.4, - 293.0, - 290.0, - 291.8, - 297.6, - 300.3, - 296.8, - 297.4, - 296.3, - 295.4, - 295.0, - 294.4, - 294.4, - ], - [ - 294.2, - 296.5, - 295.0, - 294.8, - 298.9, - 301.2, - 298.0, - 297.8, - 297.7, - 297.2, - 296.2, - 296.7, - 295.8, - 297.6, - 298.9, - 301.0, - 301.3, - 299.2, - 299.8, - 298.9, - 299.2, - 298.7, - 299.5, - 299.7, - 299.2, - 299.6, - 298.3, - 298.5, - 299.4, - 298.3, - 297.4, - 297.2, - 294.1, - 294.2, - 293.2, - 293.0, - 291.2, - 293.2, - 289.3, - 293.7, - 300.1, - 296.6, - 297.9, - 298.4, - 296.4, - 295.5, - 293.9, - 293.7, - ], - [ - 297.7, - 297.4, - 294.5, - 294.2, - 295.9, - 300.2, - 300.0, - 299.2, - 298.5, - 298.0, - 298.8, - 300.5, - 300.2, - 300.3, - 300.8, - 302.5, - 301.6, - 300.5, - 300.4, - 301.9, - 302.1, - 300.4, - 300.7, - 301.3, - 300.3, - 301.6, - 300.2, - 301.3, - 300.6, - 298.6, - 298.8, - 297.1, - 295.0, - 293.9, - 292.3, - 293.1, - 294.7, - 293.0, - 289.4, - 296.3, - 298.8, - 296.1, - 295.2, - 297.8, - 297.1, - 296.7, - 296.4, - 296.2, - ], - [ - 300.9, - 300.6, - 295.8, - 294.8, - 294.3, - 298.8, - 300.8, - 299.5, - 299.8, - 300.7, - 299.8, - 301.8, - 300.1, - 302.0, - 301.2, - 301.7, - 301.6, - 303.0, - 301.8, - 301.0, - 302.5, - 301.1, - 301.4, - 300.5, - 302.0, - 300.5, - 300.9, - 300.5, - 300.0, - 300.5, - 298.4, - 297.2, - 295.7, - 296.2, - 294.8, - 295.3, - 298.1, - 295.8, - 298.0, - 299.8, - 298.9, - 298.9, - 296.7, - 298.8, - 298.5, - 299.0, - 298.4, - 299.5, - ], - [ - 300.4, - 298.9, - 298.5, - 296.9, - 294.5, - 299.1, - 300.3, - 300.9, - 299.8, - 299.7, - 301.0, - 301.5, - 301.9, - 300.9, - 302.0, - 301.2, - 302.0, - 302.0, - 300.1, - 300.1, - 300.2, - 299.8, - 301.2, - 300.2, - 299.4, - 299.9, - 299.0, - 298.7, - 297.6, - 298.2, - 298.4, - 298.0, - 297.5, - 296.6, - 297.5, - 297.6, - 300.0, - 297.6, - 301.7, - 299.9, - 300.8, - 300.3, - 300.6, - 300.6, - 299.8, - 300.4, - 300.1, - 301.7, - ], - [ - 300.8, - 297.9, - 298.4, - 296.2, - 296.4, - 298.5, - 299.0, - 299.9, - 299.2, - 300.2, - 300.1, - 301.3, - 301.3, - 299.8, - 300.0, - 299.7, - 301.7, - 301.9, - 301.5, - 302.0, - 299.9, - 300.8, - 300.8, - 299.0, - 300.0, - 300.0, - 299.1, - 299.4, - 298.5, - 298.9, - 297.4, - 298.9, - 298.3, - 298.2, - 298.7, - 298.7, - 299.5, - 297.3, - 298.7, - 299.8, - 300.7, - 299.8, - 300.5, - 300.8, - 300.7, - 301.3, - 301.1, - 300.3, - ], - [ - 296.6, - 295.8, - 297.6, - 298.2, - 298.2, - 290.9, - 294.3, - 297.5, - 297.9, - 298.7, - 297.3, - 300.1, - 300.3, - 298.3, - 299.8, - 301.3, - 299.0, - 300.6, - 301.3, - 300.6, - 301.1, - 299.9, - 299.2, - 300.3, - 299.1, - 298.7, - 299.0, - 299.7, - 299.8, - 298.2, - 299.5, - 300.3, - 299.6, - 299.6, - 299.9, - 298.9, - 300.0, - 297.6, - 297.8, - 299.6, - 299.2, - 298.7, - 298.9, - 298.2, - 299.4, - 300.4, - 298.8, - 297.0, - ], - [ - 296.9, - 296.6, - 296.4, - 295.2, - 297.7, - 292.7, - 289.9, - 296.6, - 295.7, - 297.8, - 294.4, - 298.8, - 297.4, - 294.0, - 296.1, - 298.2, - 297.8, - 299.4, - 298.7, - 298.4, - 299.4, - 298.7, - 296.9, - 296.5, - 295.9, - 295.3, - 296.4, - 296.5, - 296.9, - 295.6, - 297.0, - 296.2, - 296.8, - 297.9, - 295.1, - 294.6, - 295.8, - 298.3, - 298.7, - 297.9, - 297.6, - 297.2, - 297.4, - 297.9, - 296.4, - 296.4, - 298.9, - 296.7, - ], - [ - 291.3, - 290.2, - 288.0, - 289.4, - 292.3, - 290.7, - 288.5, - 290.8, - 294.6, - 292.4, - 289.6, - 291.6, - 289.7, - 284.3, - 288.0, - 294.4, - 293.9, - 294.1, - 293.4, - 295.5, - 295.5, - 296.2, - 295.2, - 294.9, - 293.9, - 294.2, - 292.7, - 292.6, - 293.6, - 291.7, - 292.2, - 293.6, - 292.9, - 294.3, - 285.4, - 293.1, - 295.9, - 295.9, - 296.1, - 297.0, - 297.1, - 297.1, - 295.0, - 296.2, - 296.1, - 294.4, - 292.3, - 292.1, - ], - [ - 287.1, - 286.7, - 286.8, - 287.3, - 288.0, - 285.4, - 288.1, - 284.9, - 285.0, - 283.9, - 283.1, - 276.6, - 274.9, - 275.4, - 276.8, - 278.7, - 285.8, - 289.5, - 288.4, - 289.9, - 290.2, - 292.1, - 291.7, - 293.1, - 291.6, - 290.8, - 291.0, - 290.7, - 289.5, - 290.8, - 290.5, - 289.8, - 289.7, - 284.3, - 280.6, - 285.4, - 287.7, - 290.5, - 292.7, - 293.9, - 294.3, - 294.3, - 293.7, - 293.2, - 294.4, - 292.7, - 289.9, - 287.8, - ], - [ - 280.2, - 285.6, - 288.6, - 289.1, - 287.3, - 280.4, - 277.4, - 276.9, - 274.4, - 273.7, - 259.0, - 252.1, - 252.8, - 264.0, - 272.6, - 274.0, - 278.0, - 279.8, - 278.2, - 283.8, - 286.2, - 286.4, - 287.5, - 288.6, - 287.6, - 286.8, - 287.2, - 287.6, - 288.1, - 287.8, - 288.5, - 286.2, - 280.6, - 273.6, - 274.5, - 274.8, - 273.3, - 280.8, - 288.7, - 290.5, - 289.6, - 291.6, - 291.4, - 291.5, - 292.3, - 291.7, - 290.0, - 281.8, - ], - [ - 283.8, - 283.9, - 282.7, - 279.1, - 274.5, - 270.1, - 276.5, - 276.1, - 273.0, - 264.4, - 261.2, - 262.8, - 258.6, - 260.2, - 262.5, - 264.3, - 257.8, - 262.8, - 270.9, - 273.5, - 279.0, - 280.7, - 280.7, - 282.3, - 282.4, - 282.9, - 281.4, - 284.0, - 282.9, - 284.8, - 284.4, - 280.3, - 267.5, - 264.5, - 267.8, - 265.6, - 266.5, - 269.4, - 279.7, - 284.7, - 285.4, - 286.4, - 286.7, - 288.1, - 289.2, - 288.4, - 284.5, - 278.6, - ], - [ - 274.9, - 269.1, - 266.5, - 266.7, - 275.0, - 272.5, - 272.6, - 267.4, - 265.5, - 263.8, - 259.4, - 256.3, - 253.8, - 253.2, - 255.1, - 251.7, - 247.0, - 248.7, - 261.2, - 268.9, - 272.9, - 274.4, - 275.9, - 275.2, - 276.7, - 277.9, - 278.2, - 279.5, - 280.6, - 280.5, - 281.7, - 276.6, - 265.7, - 261.9, - 261.0, - 260.2, - 265.8, - 262.9, - 264.8, - 274.3, - 276.4, - 278.3, - 280.9, - 283.7, - 286.2, - 285.0, - 284.4, - 278.7, - ], - [ - 272.0, - 266.1, - 263.0, - 260.3, - 258.5, - 258.8, - 258.1, - 257.8, - 256.0, - 256.2, - 257.7, - 255.5, - 252.9, - 251.7, - 250.8, - 243.5, - 241.2, - 243.1, - 250.8, - 268.8, - 266.8, - 267.2, - 271.3, - 272.6, - 273.9, - 273.7, - 275.7, - 275.9, - 277.4, - 280.2, - 279.5, - 268.3, - 262.4, - 258.0, - 255.4, - 255.9, - 257.5, - 259.5, - 259.1, - 259.6, - 269.1, - 275.4, - 278.9, - 279.8, - 282.9, - 283.3, - 282.8, - 275.7, - ], - [ - 277.0, - 267.4, - 267.9, - 255.7, - 248.9, - 247.9, - 248.2, - 249.4, - 249.1, - 249.9, - 248.7, - 249.5, - 250.6, - 247.9, - 244.2, - 238.8, - 236.1, - 236.8, - 244.3, - 257.5, - 255.3, - 248.7, - 262.3, - 265.0, - 268.4, - 271.1, - 270.5, - 272.9, - 274.2, - 273.9, - 266.9, - 261.4, - 260.6, - 257.2, - 252.1, - 249.6, - 251.7, - 256.5, - 251.5, - 254.6, - 269.6, - 274.4, - 279.0, - 279.3, - 281.8, - 281.5, - 282.5, - 274.8, - ], - [ - 274.1, - 262.4, - 258.9, - 246.6, - 242.9, - 240.3, - 240.1, - 240.9, - 239.4, - 239.1, - 239.1, - 239.6, - 237.9, - 236.6, - 233.5, - 230.9, - 228.4, - 231.3, - 235.3, - 238.9, - 238.5, - 242.5, - 244.2, - 247.1, - 257.4, - 264.3, - 255.1, - 257.1, - 256.5, - 255.2, - 256.2, - 253.6, - 251.6, - 248.7, - 245.2, - 246.0, - 247.4, - 248.1, - 249.6, - 257.7, - 269.3, - 259.9, - 265.2, - 274.8, - 277.1, - 273.9, - 277.3, - 279.2, - ], - [ - 272.7, - 268.2, - 256.6, - 248.0, - 249.9, - 245.1, - 240.2, - 234.0, - 234.0, - 231.0, - 229.3, - 231.7, - 231.6, - 229.1, - 228.7, - 228.7, - 228.6, - 230.4, - 231.1, - 231.5, - 232.6, - 236.6, - 238.2, - 240.7, - 241.6, - 245.2, - 247.1, - 244.1, - 246.0, - 248.0, - 245.9, - 242.9, - 242.4, - 241.6, - 242.4, - 241.2, - 242.2, - 241.6, - 245.7, - 254.9, - 263.2, - 250.5, - 243.6, - 248.7, - 254.6, - 261.3, - 267.8, - 271.1, - ], - [ - 267.5, - 269.3, - 267.7, - 265.7, - 262.4, - 242.9, - 236.2, - 235.2, - 235.7, - 235.3, - 233.8, - 228.7, - 229.6, - 227.4, - 229.6, - 232.4, - 233.1, - 236.1, - 236.5, - 237.0, - 236.0, - 238.1, - 238.4, - 239.0, - 239.5, - 239.2, - 242.1, - 238.3, - 238.9, - 239.6, - 241.2, - 240.3, - 239.0, - 240.0, - 242.4, - 239.9, - 238.9, - 245.3, - 251.6, - 259.2, - 247.7, - 238.6, - 236.8, - 236.2, - 240.5, - 246.3, - 248.2, - 259.4, - ], - [ - 258.7, - 258.8, - 258.6, - 254.5, - 243.2, - 240.2, - 239.2, - 238.7, - 235.0, - 233.4, - 233.7, - 233.9, - 235.1, - 233.6, - 234.7, - 236.3, - 235.6, - 237.5, - 237.8, - 236.8, - 237.7, - 236.3, - 235.8, - 237.0, - 235.2, - 234.9, - 235.7, - 235.3, - 235.5, - 235.0, - 235.9, - 236.9, - 236.8, - 237.2, - 238.5, - 238.8, - 236.7, - 232.0, - 235.2, - 237.8, - 237.1, - 236.2, - 233.4, - 235.0, - 239.1, - 240.4, - 242.8, - 253.1, - ], - [ - 235.7, - 235.5, - 235.4, - 235.1, - 234.4, - 236.7, - 235.6, - 234.8, - 236.0, - 236.9, - 235.7, - 235.4, - 237.2, - 237.8, - 237.3, - 237.0, - 237.5, - 239.1, - 239.4, - 238.7, - 237.5, - 238.9, - 238.8, - 237.6, - 235.8, - 236.8, - 236.4, - 235.0, - 234.6, - 233.8, - 233.6, - 233.5, - 233.2, - 233.9, - 233.4, - 233.1, - 234.8, - 234.0, - 233.3, - 234.8, - 235.3, - 234.6, - 233.7, - 235.7, - 238.6, - 239.0, - 237.2, - 236.8, - ], + 1003.48, + 1006.42, + 1000.83, + 1002.98, + 1008.28, + 1002.97, + 1002.47, ], ] - dst = n.createVariable("dst", "f4", ("time_1", "lat", "lon")) - dst.standard_name = "air_temperature" - dst.units = "K" - dst.cell_methods = "time_1: mean" + n.close() + return filename - # Don't generate this data randomly - it's useful to see the real - # patterns of global temperature. - dst[...] = [ - [ - [ - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - ], - [ - 243.6, - 243.3, - 243.0, - 242.7, - 242.5, - 242.5, - 242.5, - 242.4, - 242.5, - 242.5, - 242.4, - 242.2, - 241.9, - 241.7, - 241.5, - 241.4, - 241.4, - 241.4, - 241.4, - 241.4, - 241.5, - 241.7, - 241.8, - 241.9, - 242.1, - 242.1, - 241.9, - 241.9, - 241.8, - 242.0, - 242.2, - 242.5, - 243.2, - 243.7, - 244.1, - 244.5, - 244.8, - 244.8, - 244.7, - 244.8, - 244.9, - 245.1, - 245.7, - 246.3, - 247.6, - 248.1, - 248.3, - 247.9, - 247.2, - 246.8, - 246.9, - 247.3, - 247.9, - 248.2, - 248.5, - 249.1, - 249.6, - 249.8, - 250.1, - 250.5, - 250.8, - 250.7, - 250.8, - 250.3, - 250.3, - 250.2, - 249.8, - 249.6, - 249.8, - 250.3, - 250.7, - 251.2, - 252.0, - 252.7, - 252.5, - 251.5, - 250.5, - 249.8, - 248.6, - 248.0, - 247.9, - 248.2, - 248.3, - 248.3, - 248.2, - 247.8, - 247.5, - 247.0, - 246.5, - 246.4, - 246.0, - 245.4, - 244.9, - 244.4, - 244.0, - 243.8, - ], - [ - 244.1, - 243.6, - 242.8, - 241.9, - 241.3, - 241.0, - 240.8, - 240.8, - 240.5, - 240.2, - 239.8, - 239.5, - 239.4, - 239.5, - 239.5, - 239.3, - 239.1, - 239.0, - 239.1, - 239.5, - 240.1, - 240.7, - 241.2, - 241.7, - 242.0, - 242.5, - 243.1, - 243.5, - 243.6, - 243.9, - 244.3, - 244.8, - 245.3, - 246.0, - 246.8, - 247.5, - 248.0, - 248.4, - 248.7, - 248.9, - 249.3, - 250.2, - 251.5, - 252.8, - 253.6, - 254.2, - 254.3, - 255.1, - 256.9, - 258.4, - 259.8, - 261.2, - 262.7, - 264.0, - 264.8, - 265.3, - 265.1, - 264.4, - 263.6, - 262.6, - 261.6, - 261.1, - 260.4, - 259.4, - 258.2, - 257.2, - 255.3, - 253.5, - 252.7, - 252.5, - 253.0, - 253.5, - 254.1, - 255.2, - 257.1, - 258.0, - 258.3, - 258.3, - 258.7, - 258.5, - 257.9, - 257.0, - 256.0, - 255.5, - 255.0, - 254.2, - 252.9, - 251.7, - 250.8, - 249.6, - 248.3, - 246.9, - 245.7, - 245.0, - 244.5, - 244.3, - ], - [ - 244.9, - 243.6, - 242.0, - 240.5, - 239.4, - 238.6, - 238.1, - 237.5, - 237.2, - 237.1, - 236.9, - 236.8, - 237.1, - 237.6, - 237.9, - 237.9, - 237.9, - 237.8, - 237.6, - 237.9, - 238.6, - 239.3, - 239.9, - 240.2, - 241.1, - 242.4, - 243.5, - 244.0, - 244.7, - 245.4, - 245.9, - 246.1, - 246.5, - 247.1, - 247.9, - 248.8, - 249.8, - 250.5, - 250.9, - 251.4, - 252.2, - 253.6, - 256.0, - 259.4, - 262.9, - 265.9, - 267.3, - 267.7, - 267.6, - 267.4, - 267.0, - 266.6, - 266.5, - 266.6, - 266.5, - 265.9, - 265.3, - 265.1, - 265.0, - 264.8, - 264.8, - 264.8, - 264.7, - 264.5, - 263.5, - 262.0, - 259.9, - 257.4, - 255.7, - 255.3, - 255.3, - 255.7, - 256.5, - 257.5, - 258.9, - 260.6, - 261.8, - 262.8, - 263.3, - 263.8, - 264.2, - 264.0, - 263.1, - 262.2, - 262.4, - 262.4, - 261.2, - 259.3, - 257.5, - 255.4, - 253.0, - 250.5, - 248.2, - 246.6, - 245.9, - 245.6, - ], - [ - 244.3, - 242.9, - 241.7, - 241.1, - 240.7, - 240.2, - 239.1, - 238.1, - 237.5, - 237.1, - 236.8, - 237.0, - 237.9, - 239.2, - 240.3, - 241.1, - 241.9, - 242.5, - 242.5, - 241.8, - 241.4, - 241.2, - 240.8, - 240.2, - 239.5, - 239.5, - 240.4, - 241.8, - 243.1, - 244.2, - 245.0, - 245.5, - 245.8, - 246.5, - 247.3, - 248.4, - 249.5, - 250.3, - 250.7, - 251.4, - 253.3, - 255.7, - 259.1, - 263.3, - 265.9, - 266.1, - 266.4, - 266.0, - 265.7, - 265.5, - 265.5, - 265.6, - 265.8, - 266.2, - 266.3, - 265.9, - 265.5, - 265.3, - 264.6, - 264.1, - 263.4, - 262.4, - 261.5, - 261.2, - 261.0, - 260.8, - 260.0, - 258.8, - 257.4, - 256.8, - 256.8, - 257.4, - 258.7, - 260.1, - 261.9, - 263.6, - 265.0, - 265.2, - 264.8, - 264.8, - 265.5, - 265.2, - 264.3, - 262.9, - 263.7, - 265.2, - 266.0, - 265.8, - 263.1, - 260.8, - 256.8, - 252.4, - 249.0, - 247.0, - 245.9, - 245.1, - ], - [ - 245.2, - 243.7, - 242.2, - 241.3, - 241.5, - 241.7, - 241.3, - 240.1, - 238.9, - 238.5, - 238.6, - 239.0, - 239.9, - 241.3, - 242.4, - 243.3, - 245.4, - 246.5, - 246.4, - 246.4, - 246.4, - 245.9, - 245.2, - 243.8, - 242.2, - 241.2, - 241.2, - 241.7, - 242.5, - 243.3, - 243.8, - 244.2, - 244.4, - 244.5, - 245.4, - 246.5, - 247.4, - 247.6, - 247.6, - 248.0, - 248.9, - 250.8, - 254.1, - 259.1, - 262.6, - 265.6, - 265.6, - 265.2, - 264.8, - 264.8, - 265.4, - 266.1, - 266.6, - 267.2, - 267.2, - 267.2, - 266.8, - 266.7, - 265.6, - 263.3, - 261.6, - 259.5, - 258.2, - 258.3, - 259.4, - 260.8, - 261.7, - 261.7, - 261.5, - 260.9, - 260.2, - 260.3, - 261.3, - 262.4, - 263.9, - 266.1, - 267.0, - 267.7, - 267.5, - 267.2, - 271.5, - 271.5, - 271.6, - 271.7, - 271.6, - 271.5, - 271.6, - 271.4, - 263.7, - 260.6, - 256.3, - 252.6, - 249.8, - 248.1, - 247.1, - 246.2, - ], - [ - 248.4, - 246.8, - 245.6, - 244.3, - 244.1, - 244.4, - 244.0, - 242.7, - 241.0, - 240.4, - 240.9, - 242.0, - 243.3, - 243.7, - 243.4, - 243.9, - 245.3, - 248.8, - 250.6, - 250.9, - 250.5, - 248.5, - 246.7, - 245.5, - 244.2, - 243.3, - 243.6, - 244.7, - 245.7, - 245.8, - 245.3, - 244.4, - 243.7, - 243.7, - 244.8, - 246.1, - 246.7, - 246.4, - 246.4, - 247.1, - 249.2, - 251.9, - 255.3, - 259.6, - 261.3, - 270.2, - 270.4, - 271.0, - 271.1, - 271.2, - 271.4, - 271.5, - 271.5, - 271.5, - 271.5, - 271.4, - 271.3, - 271.7, - 272.0, - 272.1, - 267.4, - 266.9, - 266.4, - 266.3, - 266.4, - 266.6, - 267.3, - 268.2, - 268.5, - 268.2, - 266.9, - 265.2, - 264.3, - 264.3, - 265.8, - 267.1, - 267.0, - 266.0, - 265.7, - 266.6, - 271.6, - 271.6, - 271.5, - 271.5, - 271.4, - 271.5, - 271.6, - 271.5, - 271.3, - 271.4, - 262.7, - 259.6, - 254.4, - 252.3, - 251.2, - 249.6, - ], - [ - 257.5, - 254.5, - 252.1, - 249.8, - 249.2, - 250.4, - 251.5, - 250.9, - 250.6, - 250.5, - 249.1, - 248.0, - 247.6, - 247.1, - 246.9, - 247.4, - 249.1, - 253.6, - 256.8, - 257.8, - 255.1, - 251.7, - 247.6, - 245.1, - 244.0, - 243.2, - 243.2, - 244.8, - 246.9, - 247.9, - 247.8, - 247.1, - 246.2, - 245.6, - 245.4, - 245.6, - 246.3, - 246.7, - 248.1, - 250.9, - 253.0, - 253.8, - 254.5, - 254.6, - 256.0, - 270.2, - 270.8, - 271.3, - 271.2, - 271.3, - 271.4, - 271.4, - 271.3, - 271.8, - 272.6, - 272.6, - 271.1, - 271.1, - 271.3, - 271.6, - 271.7, - 271.7, - 271.6, - 271.5, - 271.5, - 271.5, - 271.4, - 271.4, - 271.2, - 271.6, - 271.7, - 271.6, - 271.5, - 272.3, - 272.1, - 271.5, - 271.8, - 268.5, - 265.9, - 264.9, - 271.5, - 271.3, - 271.3, - 271.4, - 271.5, - 271.5, - 271.5, - 271.5, - 271.3, - 271.2, - 271.1, - 271.2, - 271.3, - 263.1, - 261.2, - 259.6, - ], - [ - 271.3, - 271.4, - 271.5, - 271.6, - 271.6, - 271.5, - 271.3, - 271.3, - 264.0, - 264.2, - 263.1, - 259.6, - 256.1, - 254.7, - 252.4, - 250.5, - 251.9, - 254.3, - 259.4, - 262.6, - 263.1, - 258.8, - 255.1, - 253.2, - 252.1, - 251.8, - 251.8, - 252.1, - 252.9, - 253.4, - 253.9, - 254.0, - 253.7, - 252.8, - 251.7, - 250.5, - 249.8, - 250.2, - 252.7, - 254.8, - 256.8, - 258.2, - 259.7, - 270.3, - 270.2, - 270.5, - 271.2, - 271.1, - 271.3, - 271.5, - 271.6, - 271.6, - 272.2, - 273.2, - 274.1, - 274.1, - 273.8, - 273.9, - 274.0, - 273.9, - 273.4, - 272.8, - 272.5, - 272.7, - 273.6, - 274.0, - 273.8, - 273.6, - 273.7, - 273.8, - 273.2, - 272.8, - 273.5, - 274.4, - 275.4, - 275.3, - 272.6, - 268.4, - 265.9, - 265.2, - 271.4, - 271.3, - 271.0, - 271.2, - 271.5, - 271.5, - 271.3, - 271.2, - 271.2, - 271.2, - 271.2, - 271.3, - 271.4, - 271.4, - 271.3, - 271.3, - ], - [ - 272.8, - 273.1, - 273.0, - 272.9, - 272.8, - 272.5, - 271.3, - 271.2, - 271.1, - 271.2, - 271.1, - 271.2, - 271.0, - 263.1, - 260.1, - 260.2, - 270.2, - 269.5, - 269.5, - 269.8, - 270.1, - 269.9, - 270.9, - 260.1, - 259.0, - 257.8, - 256.3, - 255.8, - 256.3, - 258.0, - 259.9, - 261.0, - 260.8, - 259.9, - 258.9, - 257.2, - 255.9, - 257.6, - 261.3, - 270.5, - 270.7, - 270.4, - 270.6, - 271.1, - 271.4, - 271.2, - 271.3, - 272.4, - 272.8, - 273.2, - 273.4, - 273.4, - 273.6, - 274.1, - 274.2, - 274.2, - 274.2, - 274.2, - 274.0, - 273.9, - 273.9, - 273.9, - 274.0, - 274.1, - 274.4, - 274.6, - 274.5, - 274.4, - 274.3, - 274.4, - 274.6, - 274.5, - 274.5, - 274.8, - 275.1, - 275.2, - 275.1, - 274.6, - 268.9, - 267.9, - 271.2, - 271.1, - 271.2, - 272.2, - 271.4, - 271.1, - 271.0, - 271.0, - 271.6, - 272.2, - 271.4, - 271.7, - 271.3, - 271.7, - 271.6, - 272.3, - ], - [ - 274.2, - 274.2, - 274.1, - 274.3, - 274.3, - 274.3, - 274.3, - 274.5, - 274.1, - 274.5, - 274.7, - 274.5, - 271.5, - 271.2, - 271.1, - 270.9, - 270.8, - 270.7, - 270.8, - 270.9, - 271.0, - 271.1, - 271.0, - 270.8, - 270.8, - 270.8, - 270.9, - 270.9, - 271.0, - 271.1, - 271.0, - 271.0, - 271.1, - 271.1, - 271.1, - 271.1, - 271.0, - 271.0, - 270.9, - 270.9, - 271.1, - 271.1, - 271.2, - 271.3, - 271.4, - 272.1, - 273.3, - 273.9, - 274.1, - 274.3, - 274.6, - 274.5, - 274.5, - 274.5, - 274.4, - 274.3, - 274.2, - 274.2, - 274.1, - 274.0, - 273.9, - 274.1, - 274.3, - 274.4, - 274.4, - 274.6, - 274.6, - 274.7, - 274.8, - 274.9, - 275.1, - 275.4, - 275.4, - 275.4, - 275.6, - 275.6, - 275.4, - 275.2, - 275.1, - 269.6, - 271.3, - 271.3, - 272.3, - 272.8, - 272.9, - 273.0, - 273.2, - 273.4, - 273.5, - 273.8, - 273.8, - 274.0, - 274.0, - 274.1, - 274.0, - 274.0, - ], - [ - 274.7, - 274.6, - 274.4, - 274.4, - 274.5, - 274.4, - 274.3, - 274.4, - 274.4, - 274.5, - 274.8, - 275.0, - 274.8, - 274.4, - 274.0, - 273.8, - 274.0, - 274.5, - 274.5, - 274.4, - 274.3, - 274.3, - 272.9, - 271.3, - 271.2, - 271.3, - 273.0, - 272.9, - 273.0, - 273.6, - 273.6, - 273.4, - 272.9, - 272.7, - 272.9, - 272.8, - 271.8, - 271.6, - 271.6, - 272.3, - 274.1, - 274.6, - 274.6, - 274.5, - 274.8, - 274.7, - 274.9, - 275.2, - 275.1, - 275.0, - 275.0, - 275.0, - 275.1, - 275.2, - 275.2, - 275.2, - 275.2, - 275.0, - 274.9, - 274.8, - 274.9, - 274.9, - 274.9, - 275.0, - 275.0, - 275.1, - 275.1, - 275.2, - 275.4, - 275.5, - 275.8, - 276.0, - 275.9, - 276.1, - 276.4, - 276.5, - 276.4, - 276.1, - 275.5, - 273.0, - 271.6, - 272.0, - 273.1, - 273.8, - 274.5, - 274.8, - 274.9, - 274.9, - 274.3, - 274.8, - 274.9, - 274.8, - 274.7, - 274.8, - 274.8, - 274.8, - ], - [ - 275.0, - 274.9, - 274.6, - 274.3, - 274.3, - 274.4, - 274.2, - 274.2, - 274.3, - 274.5, - 274.6, - 274.7, - 274.8, - 274.9, - 275.0, - 275.1, - 275.1, - 275.1, - 275.1, - 275.0, - 274.6, - 274.3, - 274.0, - 271.7, - 272.1, - 272.6, - 273.2, - 274.1, - 274.3, - 274.4, - 274.6, - 274.7, - 274.7, - 274.8, - 274.9, - 274.6, - 274.7, - 274.9, - 274.9, - 275.0, - 275.2, - 275.4, - 275.5, - 275.4, - 275.5, - 275.4, - 275.7, - 275.7, - 275.6, - 275.6, - 275.6, - 275.6, - 275.7, - 275.7, - 275.6, - 275.6, - 275.6, - 275.6, - 275.9, - 275.7, - 275.5, - 275.6, - 275.7, - 275.8, - 275.8, - 275.8, - 275.9, - 276.0, - 276.0, - 276.1, - 276.3, - 276.4, - 276.4, - 276.5, - 276.6, - 276.8, - 276.9, - 277.2, - 277.2, - 277.0, - 276.4, - 275.6, - 275.8, - 276.0, - 276.3, - 276.2, - 276.1, - 276.3, - 276.2, - 276.2, - 276.1, - 275.7, - 275.5, - 275.5, - 275.3, - 275.0, - ], - [ - 275.2, - 275.1, - 274.9, - 274.9, - 275.2, - 275.3, - 275.2, - 275.0, - 275.0, - 275.2, - 275.5, - 275.6, - 275.5, - 275.5, - 275.5, - 275.6, - 275.8, - 275.9, - 275.8, - 275.3, - 274.9, - 274.5, - 274.6, - 274.2, - 274.3, - 273.7, - 273.7, - 273.7, - 273.9, - 274.0, - 274.4, - 274.5, - 274.6, - 274.9, - 275.0, - 275.0, - 275.0, - 275.1, - 275.1, - 275.4, - 275.6, - 275.9, - 276.3, - 276.6, - 277.0, - 276.5, - 276.5, - 276.5, - 276.5, - 276.6, - 276.6, - 276.7, - 277.0, - 276.7, - 276.4, - 276.3, - 276.1, - 276.2, - 277.1, - 276.7, - 276.5, - 276.4, - 276.8, - 277.0, - 276.9, - 276.9, - 277.0, - 277.0, - 276.9, - 276.9, - 277.0, - 276.9, - 276.9, - 277.1, - 277.2, - 277.7, - 277.9, - 278.7, - 279.2, - 279.0, - 278.1, - 278.2, - 278.1, - 278.3, - 278.6, - 278.0, - 277.3, - 277.0, - 277.1, - 277.1, - 277.0, - 276.7, - 276.4, - 276.3, - 276.1, - 275.6, - ], - [ - 276.0, - 275.8, - 275.7, - 275.8, - 276.1, - 276.2, - 276.2, - 276.1, - 275.9, - 276.0, - 276.3, - 276.4, - 276.3, - 276.4, - 276.3, - 276.3, - 276.5, - 276.5, - 276.3, - 276.0, - 275.7, - 275.1, - 275.7, - 275.6, - 275.7, - 275.7, - 276.0, - 275.6, - 276.0, - 275.4, - 275.8, - 275.7, - 275.6, - 275.6, - 275.9, - 275.9, - 276.2, - 276.5, - 276.2, - 276.1, - 276.3, - 276.1, - 277.0, - 277.3, - 278.2, - 278.2, - 277.7, - 277.5, - 277.5, - 277.6, - 277.5, - 277.8, - 278.2, - 278.2, - 278.1, - 278.0, - 277.6, - 277.2, - 278.1, - 278.1, - 278.0, - 278.0, - 278.8, - 278.7, - 278.4, - 278.3, - 278.2, - 278.2, - 278.1, - 278.0, - 278.1, - 278.1, - 278.3, - 278.4, - 278.5, - 278.9, - 279.5, - 280.2, - 279.7, - 281.3, - 280.2, - 279.7, - 279.5, - 279.3, - 279.1, - 278.3, - 277.8, - 277.9, - 278.3, - 278.3, - 278.2, - 277.7, - 277.4, - 277.2, - 276.7, - 276.3, - ], - [ - 277.2, - 277.2, - 277.4, - 277.5, - 277.5, - 277.5, - 277.4, - 277.2, - 276.9, - 276.9, - 277.1, - 277.2, - 277.4, - 277.5, - 277.5, - 277.4, - 277.4, - 277.2, - 277.2, - 277.0, - 276.8, - 276.2, - 277.4, - 277.5, - 277.4, - 277.2, - 277.5, - 277.7, - 277.9, - 277.6, - 278.0, - 277.4, - 277.5, - 277.4, - 277.6, - 277.9, - 278.4, - 278.3, - 278.2, - 278.6, - 279.2, - 278.8, - 279.5, - 279.4, - 280.0, - 280.7, - 280.1, - 278.8, - 278.6, - 278.9, - 278.8, - 279.0, - 279.6, - 280.1, - 280.0, - 279.8, - 279.7, - 279.4, - 279.7, - 280.1, - 280.3, - 280.5, - 280.7, - 280.3, - 280.1, - 280.1, - 279.9, - 279.8, - 279.5, - 279.3, - 279.3, - 279.3, - 279.4, - 279.5, - 279.7, - 280.1, - 280.4, - 279.8, - 282.0, - 281.8, - 281.2, - 281.1, - 280.4, - 279.5, - 279.1, - 278.7, - 278.6, - 278.7, - 279.0, - 279.1, - 279.5, - 279.0, - 278.5, - 278.0, - 277.2, - 277.3, - ], - [ - 279.0, - 279.3, - 279.5, - 279.4, - 279.1, - 278.8, - 278.6, - 278.3, - 278.3, - 278.2, - 278.2, - 278.3, - 278.6, - 278.8, - 278.6, - 278.5, - 278.4, - 278.3, - 278.7, - 278.3, - 278.5, - 280.1, - 281.1, - 280.7, - 280.3, - 280.1, - 280.0, - 280.2, - 280.1, - 279.9, - 279.7, - 279.5, - 279.9, - 279.8, - 280.0, - 280.9, - 280.9, - 280.8, - 280.9, - 281.1, - 281.1, - 280.8, - 281.1, - 281.1, - 281.6, - 281.8, - 281.0, - 280.1, - 280.2, - 280.5, - 281.0, - 281.1, - 281.4, - 282.2, - 281.8, - 281.6, - 281.5, - 281.4, - 281.6, - 282.2, - 282.3, - 282.4, - 282.1, - 281.9, - 281.9, - 281.8, - 281.4, - 281.2, - 280.9, - 280.7, - 280.6, - 280.5, - 280.6, - 280.7, - 280.9, - 281.0, - 281.1, - 279.2, - 284.1, - 283.7, - 282.6, - 281.6, - 280.6, - 280.3, - 280.1, - 279.7, - 279.5, - 279.5, - 279.9, - 279.9, - 280.4, - 280.3, - 280.0, - 279.7, - 279.0, - 279.3, - ], - [ - 281.7, - 281.7, - 281.4, - 281.2, - 280.9, - 280.5, - 280.2, - 279.8, - 280.0, - 280.0, - 279.8, - 280.0, - 280.2, - 280.3, - 280.0, - 279.9, - 279.9, - 279.8, - 280.4, - 280.0, - 281.9, - 282.4, - 282.6, - 282.2, - 282.3, - 282.2, - 281.9, - 281.9, - 281.7, - 281.5, - 281.4, - 281.6, - 282.0, - 282.0, - 282.3, - 282.7, - 282.4, - 282.5, - 282.5, - 282.5, - 282.7, - 282.6, - 282.7, - 282.8, - 283.2, - 283.1, - 282.7, - 282.0, - 282.5, - 283.3, - 283.9, - 283.5, - 283.7, - 284.1, - 283.9, - 283.6, - 283.4, - 283.4, - 283.6, - 283.9, - 283.9, - 283.8, - 283.6, - 283.6, - 283.6, - 283.4, - 283.1, - 282.8, - 282.6, - 282.3, - 282.2, - 281.9, - 281.8, - 281.9, - 281.9, - 281.9, - 281.9, - 277.8, - 287.9, - 285.9, - 283.8, - 282.2, - 282.2, - 282.4, - 282.3, - 281.9, - 281.1, - 281.0, - 281.1, - 281.0, - 281.5, - 282.3, - 282.3, - 282.3, - 282.0, - 282.0, - ], - [ - 284.2, - 283.9, - 283.5, - 283.1, - 282.7, - 282.4, - 282.0, - 281.6, - 281.8, - 281.5, - 281.5, - 281.9, - 281.9, - 282.1, - 281.9, - 281.9, - 282.0, - 281.8, - 282.2, - 282.2, - 284.0, - 284.2, - 284.2, - 284.1, - 284.2, - 283.9, - 283.7, - 283.6, - 283.6, - 283.6, - 283.6, - 283.8, - 283.9, - 283.9, - 284.2, - 284.1, - 284.1, - 284.2, - 284.4, - 285.2, - 285.7, - 285.1, - 284.9, - 284.9, - 284.9, - 283.6, - 287.7, - 288.0, - 286.9, - 288.5, - 287.4, - 286.6, - 287.0, - 286.3, - 286.2, - 285.8, - 285.7, - 285.7, - 285.7, - 285.8, - 285.7, - 285.6, - 285.3, - 285.2, - 285.0, - 284.9, - 284.7, - 284.4, - 284.3, - 283.9, - 283.6, - 283.4, - 283.0, - 283.0, - 282.8, - 283.1, - 283.2, - 280.2, - 289.8, - 289.2, - 285.5, - 284.6, - 284.9, - 285.1, - 284.9, - 283.8, - 283.0, - 283.1, - 283.0, - 282.7, - 283.5, - 284.8, - 284.8, - 284.9, - 285.1, - 284.7, - ], - [ - 286.4, - 286.1, - 285.7, - 285.2, - 284.9, - 284.5, - 284.0, - 283.6, - 283.8, - 283.3, - 283.2, - 284.3, - 284.8, - 285.0, - 285.3, - 285.1, - 284.9, - 284.8, - 285.3, - 285.4, - 286.6, - 286.3, - 286.2, - 286.2, - 286.2, - 286.0, - 285.9, - 285.7, - 285.7, - 285.7, - 285.5, - 285.5, - 285.6, - 285.8, - 285.8, - 285.9, - 286.2, - 286.3, - 286.4, - 287.2, - 287.9, - 287.7, - 287.4, - 287.2, - 287.5, - 288.2, - 286.9, - 291.5, - 291.3, - 290.5, - 289.1, - 288.8, - 288.6, - 287.9, - 287.7, - 287.4, - 287.5, - 287.6, - 287.6, - 287.6, - 287.3, - 287.1, - 286.9, - 286.9, - 286.8, - 286.6, - 286.3, - 286.1, - 286.0, - 285.7, - 285.4, - 285.1, - 284.6, - 284.2, - 284.0, - 284.6, - 284.8, - 281.5, - 289.1, - 290.4, - 287.6, - 289.3, - 289.9, - 287.9, - 286.6, - 286.2, - 286.0, - 285.5, - 285.5, - 286.1, - 287.1, - 287.8, - 287.2, - 287.5, - 287.3, - 287.0, - ], - [ - 288.7, - 288.5, - 288.1, - 287.6, - 287.1, - 286.8, - 287.2, - 286.7, - 286.5, - 285.8, - 286.0, - 287.4, - 287.8, - 288.3, - 288.7, - 288.2, - 287.8, - 288.0, - 288.5, - 288.7, - 289.1, - 288.6, - 288.5, - 288.2, - 288.0, - 287.9, - 287.6, - 287.4, - 287.4, - 287.4, - 287.3, - 287.3, - 287.5, - 287.6, - 287.7, - 287.8, - 287.9, - 288.0, - 288.1, - 289.0, - 290.0, - 291.0, - 290.2, - 289.5, - 289.6, - 290.0, - 291.1, - 290.8, - 292.5, - 291.4, - 290.5, - 290.1, - 289.7, - 289.4, - 289.2, - 289.3, - 289.6, - 289.9, - 290.0, - 289.6, - 289.3, - 289.2, - 289.0, - 289.0, - 289.0, - 288.7, - 288.4, - 288.2, - 288.0, - 287.6, - 287.2, - 286.8, - 286.3, - 286.0, - 285.8, - 286.3, - 286.6, - 283.4, - 291.9, - 296.0, - 290.3, - 292.7, - 292.4, - 290.3, - 289.5, - 289.2, - 288.9, - 288.8, - 289.2, - 289.7, - 289.9, - 289.6, - 289.5, - 289.6, - 289.4, - 289.1, - ], - [ - 290.7, - 290.8, - 290.4, - 290.0, - 290.5, - 291.8, - 292.7, - 293.0, - 293.4, - 292.1, - 291.7, - 292.0, - 291.9, - 291.8, - 291.6, - 291.2, - 291.2, - 291.3, - 291.3, - 291.3, - 291.1, - 290.7, - 290.7, - 290.2, - 289.7, - 289.5, - 289.3, - 289.1, - 289.1, - 289.1, - 289.2, - 289.5, - 289.8, - 289.6, - 289.4, - 289.5, - 289.6, - 290.2, - 293.9, - 290.9, - 292.4, - 294.3, - 292.9, - 291.8, - 291.8, - 291.9, - 292.2, - 292.8, - 292.7, - 292.4, - 292.3, - 291.9, - 291.4, - 291.0, - 291.1, - 291.3, - 291.6, - 292.4, - 292.3, - 292.0, - 291.8, - 291.6, - 291.5, - 291.4, - 291.3, - 291.0, - 290.7, - 290.5, - 290.2, - 289.8, - 289.4, - 288.8, - 288.3, - 287.9, - 287.5, - 288.1, - 288.3, - 286.7, - 294.7, - 298.5, - 297.5, - 294.4, - 293.8, - 292.8, - 292.0, - 291.3, - 291.0, - 291.1, - 291.2, - 291.5, - 291.8, - 291.9, - 291.9, - 291.5, - 291.3, - 291.1, - ], - [ - 292.2, - 292.3, - 292.0, - 292.2, - 292.3, - 291.9, - 293.8, - 295.6, - 296.0, - 296.2, - 295.2, - 293.9, - 293.5, - 293.3, - 293.3, - 293.1, - 293.2, - 293.2, - 293.1, - 292.8, - 292.5, - 292.1, - 292.1, - 291.7, - 291.3, - 291.0, - 290.6, - 290.4, - 290.3, - 290.4, - 290.5, - 290.1, - 291.1, - 291.0, - 290.9, - 291.1, - 291.6, - 297.3, - 298.4, - 294.6, - 291.0, - 296.0, - 294.8, - 294.0, - 294.1, - 294.0, - 293.7, - 294.0, - 293.6, - 293.7, - 293.5, - 293.1, - 292.7, - 292.5, - 292.9, - 293.4, - 293.7, - 294.1, - 294.1, - 294.0, - 293.9, - 293.8, - 293.7, - 293.6, - 293.5, - 293.4, - 293.0, - 292.8, - 292.5, - 292.1, - 291.4, - 290.8, - 290.1, - 289.5, - 289.0, - 289.7, - 288.3, - 292.3, - 297.3, - 301.5, - 301.0, - 299.1, - 295.6, - 294.8, - 293.4, - 293.0, - 292.9, - 293.0, - 293.2, - 293.5, - 293.9, - 294.0, - 293.8, - 293.3, - 292.8, - 292.4, - ], - [ - 292.9, - 292.5, - 291.9, - 292.0, - 291.5, - 295.9, - 289.6, - 290.0, - 297.4, - 297.2, - 295.9, - 295.2, - 295.1, - 295.2, - 295.3, - 295.2, - 295.1, - 294.9, - 294.6, - 294.2, - 293.8, - 293.4, - 293.2, - 292.8, - 292.3, - 291.8, - 291.6, - 291.1, - 291.5, - 292.2, - 293.2, - 297.0, - 294.3, - 292.2, - 292.0, - 292.5, - 298.2, - 298.2, - 297.6, - 295.0, - 292.4, - 296.8, - 295.8, - 295.7, - 295.8, - 295.4, - 295.0, - 294.9, - 294.6, - 294.8, - 294.5, - 294.1, - 294.0, - 294.2, - 294.6, - 294.9, - 295.3, - 295.4, - 295.4, - 295.4, - 295.5, - 295.4, - 295.4, - 295.4, - 295.4, - 295.3, - 294.9, - 294.5, - 294.2, - 293.8, - 293.2, - 292.4, - 291.6, - 290.8, - 290.5, - 290.7, - 287.0, - 294.5, - 299.9, - 301.8, - 303.2, - 299.2, - 297.4, - 296.8, - 295.7, - 294.8, - 294.9, - 294.9, - 294.8, - 295.0, - 295.3, - 295.2, - 294.9, - 294.3, - 293.7, - 293.3, - ], - [ - 293.3, - 293.0, - 292.8, - 293.8, - 293.2, - 298.3, - 293.4, - 289.7, - 290.7, - 298.8, - 297.3, - 297.2, - 297.1, - 296.7, - 296.7, - 296.6, - 296.2, - 295.8, - 295.4, - 295.0, - 294.6, - 294.1, - 293.7, - 293.3, - 293.1, - 293.1, - 293.4, - 292.4, - 292.9, - 294.0, - 295.2, - 299.8, - 293.3, - 293.0, - 293.2, - 297.7, - 300.7, - 298.8, - 297.4, - 298.5, - 294.3, - 297.7, - 296.6, - 296.9, - 296.7, - 296.2, - 295.8, - 295.7, - 295.6, - 295.8, - 295.4, - 295.3, - 295.4, - 295.8, - 295.9, - 296.0, - 296.3, - 296.4, - 296.5, - 296.6, - 296.7, - 296.6, - 296.6, - 296.6, - 296.5, - 296.3, - 295.9, - 295.6, - 295.2, - 294.6, - 294.0, - 293.3, - 292.5, - 291.7, - 291.1, - 290.3, - 286.5, - 294.7, - 297.7, - 304.8, - 304.4, - 300.7, - 297.3, - 298.0, - 297.6, - 296.6, - 296.3, - 296.2, - 295.9, - 295.8, - 295.8, - 295.6, - 295.1, - 294.6, - 293.9, - 293.6, - ], - [ - 293.5, - 293.2, - 293.4, - 293.4, - 291.1, - 297.3, - 294.5, - 292.8, - 290.8, - 299.7, - 299.5, - 299.4, - 299.1, - 298.2, - 297.6, - 297.2, - 296.7, - 296.1, - 295.6, - 295.2, - 294.8, - 294.4, - 294.1, - 293.8, - 293.9, - 294.2, - 294.2, - 293.8, - 294.3, - 295.6, - 297.2, - 300.9, - 295.2, - 296.9, - 298.7, - 299.6, - 301.0, - 300.8, - 300.3, - 300.0, - 295.9, - 298.4, - 297.6, - 297.7, - 297.5, - 297.2, - 296.9, - 296.8, - 296.9, - 296.8, - 296.5, - 296.5, - 296.7, - 296.9, - 296.9, - 296.9, - 297.1, - 297.2, - 297.3, - 297.3, - 297.3, - 297.2, - 297.1, - 297.0, - 296.7, - 296.3, - 296.0, - 295.7, - 295.4, - 294.9, - 294.3, - 293.7, - 293.0, - 292.2, - 291.3, - 290.6, - 290.3, - 293.8, - 283.8, - 303.0, - 303.3, - 301.1, - 295.7, - 296.0, - 299.0, - 298.0, - 297.4, - 297.0, - 296.4, - 296.0, - 295.7, - 295.3, - 294.8, - 294.4, - 293.9, - 293.7, - ], - [ - 293.7, - 293.6, - 294.4, - 293.9, - 294.2, - 296.8, - 296.3, - 294.3, - 293.1, - 298.0, - 300.5, - 300.5, - 298.9, - 299.1, - 298.0, - 297.7, - 297.0, - 296.4, - 295.9, - 295.5, - 295.1, - 294.9, - 294.9, - 294.7, - 295.0, - 295.3, - 295.1, - 295.0, - 295.6, - 296.9, - 298.5, - 301.8, - 297.3, - 301.1, - 302.4, - 299.7, - 299.6, - 301.0, - 300.8, - 298.4, - 297.0, - 299.1, - 298.5, - 298.5, - 298.3, - 298.1, - 298.0, - 298.0, - 297.9, - 297.6, - 297.5, - 297.6, - 297.7, - 297.8, - 297.7, - 297.7, - 297.7, - 297.9, - 298.0, - 297.8, - 297.6, - 297.4, - 297.1, - 296.7, - 296.3, - 295.9, - 295.6, - 295.4, - 295.2, - 294.7, - 294.2, - 293.6, - 292.9, - 292.1, - 291.3, - 290.9, - 291.9, - 294.1, - 280.6, - 297.3, - 301.2, - 301.0, - 296.7, - 296.5, - 299.8, - 298.7, - 297.8, - 297.1, - 296.3, - 295.8, - 295.4, - 295.0, - 294.6, - 294.3, - 293.9, - 293.8, - ], - [ - 294.3, - 294.7, - 294.9, - 294.8, - 300.4, - 293.8, - 295.8, - 294.9, - 295.8, - 298.2, - 301.2, - 301.1, - 295.4, - 299.3, - 298.7, - 298.3, - 297.7, - 297.1, - 296.5, - 296.1, - 295.8, - 295.8, - 295.9, - 295.6, - 295.9, - 296.1, - 295.9, - 296.0, - 296.6, - 297.9, - 299.7, - 302.8, - 300.9, - 303.2, - 301.0, - 298.0, - 298.2, - 299.2, - 299.3, - 300.2, - 299.3, - 300.0, - 299.3, - 299.1, - 298.8, - 298.6, - 298.6, - 298.7, - 298.7, - 298.5, - 298.5, - 298.4, - 298.5, - 298.5, - 298.5, - 298.5, - 298.5, - 298.6, - 298.6, - 298.4, - 298.0, - 297.5, - 297.0, - 296.4, - 295.8, - 295.3, - 294.9, - 294.7, - 294.5, - 294.2, - 293.7, - 293.2, - 292.5, - 291.7, - 291.5, - 291.8, - 293.3, - 293.4, - 281.5, - 294.6, - 300.3, - 299.7, - 299.1, - 296.2, - 294.6, - 299.0, - 298.1, - 297.1, - 296.2, - 295.7, - 295.3, - 295.0, - 294.7, - 294.3, - 294.0, - 294.1, - ], - [ - 295.1, - 295.6, - 295.7, - 295.2, - 294.5, - 294.9, - 296.9, - 295.3, - 294.1, - 297.9, - 301.5, - 300.9, - 301.8, - 298.2, - 298.7, - 298.8, - 298.2, - 297.6, - 297.1, - 296.8, - 296.7, - 296.9, - 297.0, - 297.0, - 297.1, - 297.0, - 296.9, - 297.1, - 297.6, - 298.7, - 300.4, - 302.7, - 302.9, - 301.2, - 299.5, - 299.3, - 299.9, - 299.3, - 298.6, - 297.4, - 300.6, - 300.3, - 299.9, - 299.7, - 299.4, - 299.4, - 299.4, - 299.4, - 299.4, - 299.2, - 299.1, - 299.0, - 299.0, - 299.3, - 299.4, - 299.5, - 299.3, - 299.2, - 299.2, - 298.7, - 298.1, - 297.5, - 296.9, - 296.1, - 295.4, - 294.8, - 294.3, - 294.0, - 293.7, - 293.4, - 293.1, - 292.5, - 292.0, - 291.8, - 292.4, - 293.3, - 293.7, - 292.8, - 281.2, - 293.6, - 299.9, - 300.7, - 298.9, - 296.3, - 294.1, - 296.1, - 298.5, - 297.5, - 296.7, - 296.1, - 295.7, - 295.4, - 295.0, - 294.7, - 294.5, - 294.7, - ], - [ - 295.7, - 296.3, - 296.6, - 296.3, - 293.9, - 295.3, - 295.8, - 295.6, - 294.4, - 297.6, - 300.6, - 300.6, - 301.4, - 298.0, - 298.9, - 299.1, - 298.6, - 298.0, - 297.7, - 297.6, - 297.7, - 298.2, - 298.5, - 298.5, - 298.5, - 298.5, - 298.2, - 298.4, - 298.8, - 299.7, - 300.9, - 302.2, - 302.6, - 300.0, - 297.9, - 299.1, - 299.7, - 300.4, - 299.2, - 301.1, - 300.7, - 300.8, - 300.9, - 300.7, - 300.4, - 300.2, - 300.0, - 299.7, - 299.6, - 299.5, - 299.7, - 299.8, - 299.8, - 299.8, - 300.0, - 300.1, - 299.9, - 299.6, - 299.3, - 299.1, - 298.4, - 297.7, - 296.8, - 295.8, - 295.0, - 294.4, - 293.9, - 293.5, - 293.1, - 292.8, - 292.6, - 292.2, - 292.2, - 292.7, - 293.3, - 293.3, - 290.7, - 293.3, - 283.6, - 295.9, - 300.0, - 301.3, - 296.9, - 295.9, - 295.1, - 296.2, - 298.6, - 298.3, - 297.6, - 296.9, - 296.4, - 295.9, - 295.6, - 295.3, - 295.2, - 295.3, - ], - [ - 296.7, - 297.4, - 298.1, - 298.3, - 293.0, - 294.1, - 295.4, - 294.8, - 296.0, - 296.7, - 297.1, - 300.4, - 301.3, - 298.5, - 299.2, - 299.2, - 298.8, - 298.5, - 298.4, - 298.7, - 299.1, - 299.4, - 299.6, - 299.8, - 299.9, - 299.8, - 299.6, - 299.8, - 300.2, - 300.8, - 301.3, - 301.9, - 302.2, - 302.4, - 299.3, - 299.7, - 300.3, - 302.5, - 300.3, - 301.2, - 301.4, - 301.5, - 301.5, - 301.3, - 300.9, - 300.6, - 300.4, - 300.2, - 300.2, - 300.2, - 300.6, - 301.0, - 300.8, - 300.5, - 300.4, - 300.4, - 300.2, - 300.0, - 299.7, - 299.4, - 298.9, - 298.1, - 297.2, - 296.3, - 295.5, - 294.7, - 294.1, - 293.5, - 293.0, - 292.6, - 292.3, - 292.4, - 292.9, - 293.6, - 294.6, - 295.1, - 293.8, - 280.5, - 293.1, - 299.5, - 298.8, - 298.8, - 297.6, - 295.5, - 295.4, - 294.9, - 298.9, - 299.0, - 298.2, - 297.5, - 296.9, - 296.4, - 296.2, - 296.0, - 295.9, - 296.3, - ], - [ - 298.1, - 298.6, - 299.5, - 299.7, - 293.4, - 293.8, - 294.6, - 293.7, - 293.8, - 296.3, - 297.1, - 300.2, - 300.4, - 299.7, - 299.6, - 299.5, - 299.3, - 299.5, - 299.6, - 299.9, - 300.1, - 300.3, - 300.6, - 300.8, - 300.8, - 300.8, - 300.7, - 300.9, - 301.2, - 301.6, - 301.8, - 302.0, - 302.1, - 302.2, - 302.2, - 302.3, - 300.9, - 302.6, - 301.9, - 301.9, - 302.0, - 301.8, - 301.7, - 301.4, - 301.2, - 301.1, - 301.1, - 301.0, - 300.9, - 300.9, - 301.2, - 301.3, - 301.1, - 300.9, - 300.8, - 300.7, - 300.6, - 300.4, - 300.1, - 299.9, - 299.6, - 299.0, - 298.1, - 297.3, - 296.3, - 295.4, - 294.7, - 294.1, - 293.8, - 293.4, - 293.4, - 293.8, - 294.6, - 295.3, - 296.4, - 296.5, - 286.2, - 293.0, - 299.3, - 299.6, - 298.9, - 298.4, - 298.4, - 297.8, - 295.5, - 294.9, - 298.6, - 299.3, - 298.8, - 298.2, - 297.7, - 297.2, - 296.9, - 296.9, - 297.2, - 297.7, - ], - [ - 299.3, - 300.1, - 300.9, - 301.0, - 296.5, - 295.0, - 295.4, - 294.5, - 293.6, - 293.2, - 298.3, - 300.1, - 300.6, - 300.2, - 300.2, - 300.2, - 300.1, - 300.3, - 300.4, - 300.5, - 300.6, - 300.7, - 300.8, - 300.9, - 301.0, - 301.1, - 301.2, - 301.2, - 301.4, - 301.6, - 301.8, - 301.9, - 301.9, - 302.0, - 302.2, - 302.2, - 302.4, - 302.3, - 302.5, - 302.3, - 302.1, - 301.8, - 301.7, - 301.6, - 301.5, - 301.5, - 301.4, - 301.3, - 301.3, - 301.4, - 301.4, - 301.3, - 301.2, - 301.1, - 301.1, - 301.0, - 300.9, - 300.7, - 300.5, - 300.2, - 299.8, - 299.2, - 298.4, - 297.5, - 296.7, - 296.1, - 295.6, - 295.3, - 295.1, - 295.0, - 295.1, - 295.5, - 296.1, - 297.1, - 297.6, - 296.6, - 292.3, - 299.6, - 300.4, - 299.8, - 299.4, - 298.4, - 298.4, - 298.9, - 296.7, - 296.7, - 300.1, - 299.5, - 299.2, - 298.8, - 298.4, - 298.2, - 298.2, - 298.4, - 298.7, - 298.9, - ], - [ - 300.7, - 301.3, - 301.7, - 301.6, - 297.2, - 296.6, - 296.7, - 296.4, - 294.2, - 293.5, - 299.5, - 299.8, - 300.8, - 300.4, - 300.1, - 300.1, - 300.2, - 300.4, - 300.5, - 300.5, - 300.6, - 300.8, - 300.8, - 300.8, - 300.8, - 300.9, - 300.9, - 300.9, - 301.2, - 301.4, - 301.6, - 301.8, - 302.0, - 302.1, - 302.2, - 302.4, - 302.4, - 302.4, - 300.7, - 296.4, - 302.0, - 301.9, - 301.9, - 301.8, - 301.8, - 301.7, - 301.6, - 301.5, - 301.5, - 301.4, - 301.4, - 301.4, - 301.3, - 301.2, - 301.0, - 300.9, - 300.8, - 300.7, - 300.4, - 299.9, - 299.4, - 298.8, - 298.2, - 297.7, - 297.3, - 296.9, - 296.6, - 296.4, - 296.3, - 296.3, - 296.5, - 296.9, - 297.4, - 298.4, - 298.5, - 291.0, - 298.6, - 300.5, - 300.8, - 300.9, - 300.1, - 298.7, - 298.5, - 298.4, - 297.9, - 297.8, - 299.0, - 299.5, - 299.9, - 299.7, - 299.6, - 299.5, - 299.5, - 299.6, - 299.9, - 300.1, - ], - [ - 301.2, - 301.4, - 301.5, - 301.6, - 298.1, - 298.3, - 298.3, - 297.6, - 294.4, - 293.5, - 297.6, - 299.1, - 300.7, - 300.6, - 300.2, - 299.9, - 300.0, - 300.2, - 300.4, - 300.4, - 300.5, - 300.6, - 300.5, - 300.6, - 300.7, - 300.8, - 300.9, - 301.5, - 299.1, - 302.0, - 302.1, - 302.2, - 302.6, - 302.4, - 302.2, - 302.3, - 302.2, - 298.0, - 295.6, - 301.5, - 301.8, - 301.7, - 301.6, - 301.5, - 301.3, - 301.2, - 301.1, - 301.0, - 301.0, - 300.9, - 300.7, - 300.6, - 300.3, - 300.0, - 299.9, - 299.8, - 299.7, - 299.6, - 299.4, - 299.2, - 298.8, - 298.5, - 298.1, - 297.8, - 297.8, - 297.7, - 297.6, - 297.5, - 297.6, - 297.7, - 297.9, - 298.2, - 298.8, - 299.5, - 299.3, - 294.1, - 300.7, - 301.2, - 301.1, - 300.9, - 300.2, - 299.5, - 298.9, - 298.7, - 298.8, - 297.7, - 300.7, - 300.1, - 300.4, - 300.3, - 300.3, - 300.3, - 300.4, - 300.5, - 300.7, - 300.9, - ], - [ - 301.2, - 301.3, - 301.4, - 299.1, - 297.8, - 299.4, - 298.8, - 297.3, - 291.6, - 294.2, - 297.2, - 299.3, - 300.3, - 300.2, - 300.3, - 300.2, - 300.1, - 300.2, - 300.3, - 300.5, - 300.5, - 300.6, - 300.6, - 300.6, - 300.8, - 301.1, - 301.2, - 298.7, - 300.5, - 302.1, - 300.4, - 300.7, - 296.8, - 302.3, - 302.2, - 302.3, - 302.6, - 299.3, - 301.6, - 301.2, - 300.7, - 300.4, - 300.3, - 300.2, - 300.1, - 300.0, - 300.0, - 299.7, - 299.7, - 299.4, - 299.3, - 299.2, - 298.8, - 298.6, - 298.3, - 297.9, - 297.7, - 297.5, - 297.4, - 297.5, - 297.3, - 297.2, - 297.1, - 297.0, - 297.2, - 297.0, - 297.3, - 297.4, - 297.7, - 298.0, - 298.0, - 298.6, - 298.9, - 299.3, - 299.3, - 294.4, - 300.5, - 301.2, - 300.9, - 300.7, - 299.8, - 300.0, - 301.0, - 299.5, - 299.3, - 300.7, - 300.7, - 300.2, - 300.4, - 300.5, - 300.4, - 300.5, - 300.6, - 300.7, - 300.9, - 301.0, - ], - [ - 301.4, - 301.5, - 301.4, - 299.0, - 298.3, - 299.5, - 299.6, - 297.9, - 293.7, - 293.8, - 296.3, - 302.4, - 300.0, - 299.5, - 299.7, - 300.1, - 300.3, - 300.3, - 300.5, - 300.6, - 300.6, - 300.6, - 300.8, - 300.9, - 301.3, - 301.6, - 301.5, - 298.7, - 301.6, - 301.6, - 298.6, - 299.5, - 302.0, - 301.9, - 302.1, - 302.2, - 302.1, - 301.8, - 301.2, - 300.6, - 300.2, - 300.0, - 299.8, - 299.7, - 299.6, - 299.4, - 299.4, - 299.1, - 299.0, - 298.6, - 298.5, - 298.2, - 298.0, - 297.8, - 297.6, - 297.3, - 297.0, - 297.0, - 296.6, - 296.8, - 296.5, - 296.5, - 296.3, - 296.3, - 296.0, - 296.1, - 296.5, - 296.8, - 297.0, - 297.8, - 298.0, - 298.8, - 299.0, - 299.3, - 299.4, - 293.3, - 299.8, - 300.5, - 300.4, - 299.8, - 300.2, - 302.2, - 300.8, - 300.1, - 300.2, - 300.1, - 300.0, - 300.2, - 300.2, - 300.3, - 300.4, - 300.6, - 300.8, - 300.9, - 301.1, - 301.2, - ], - [ - 301.9, - 301.9, - 301.6, - 298.0, - 297.9, - 299.2, - 298.9, - 297.8, - 295.1, - 295.1, - 300.5, - 301.9, - 300.2, - 299.3, - 299.3, - 299.8, - 299.9, - 300.1, - 300.2, - 300.3, - 300.2, - 300.1, - 300.3, - 300.5, - 301.1, - 301.6, - 301.5, - 299.3, - 300.9, - 301.2, - 299.4, - 297.5, - 301.9, - 301.9, - 301.9, - 301.9, - 301.9, - 301.7, - 301.6, - 301.3, - 301.0, - 300.7, - 300.6, - 300.4, - 300.3, - 300.1, - 299.9, - 299.8, - 299.7, - 299.5, - 299.3, - 299.0, - 299.1, - 299.2, - 299.0, - 298.8, - 298.6, - 298.5, - 298.4, - 298.4, - 298.1, - 298.1, - 298.3, - 298.3, - 298.2, - 298.4, - 298.7, - 298.8, - 299.1, - 299.3, - 299.4, - 299.7, - 299.7, - 299.7, - 299.9, - 300.1, - 293.9, - 299.3, - 300.0, - 298.1, - 302.8, - 301.0, - 299.5, - 300.0, - 299.9, - 299.9, - 300.0, - 300.2, - 300.4, - 300.6, - 300.8, - 301.0, - 301.3, - 301.4, - 301.7, - 301.8, - ], - [ - 302.3, - 302.5, - 299.8, - 295.9, - 296.7, - 297.9, - 297.1, - 295.9, - 298.0, - 299.7, - 292.1, - 298.2, - 298.1, - 299.2, - 298.9, - 299.5, - 299.8, - 300.0, - 300.1, - 300.0, - 299.8, - 299.7, - 299.8, - 300.2, - 300.7, - 301.2, - 301.3, - 296.9, - 300.8, - 301.4, - 302.1, - 298.0, - 302.2, - 301.9, - 301.5, - 301.8, - 301.8, - 301.7, - 301.6, - 301.5, - 301.4, - 301.3, - 301.2, - 301.1, - 301.0, - 300.8, - 300.7, - 300.6, - 300.4, - 300.3, - 300.1, - 300.0, - 300.1, - 299.9, - 299.9, - 299.9, - 299.8, - 299.7, - 299.7, - 299.8, - 299.7, - 299.6, - 299.5, - 299.4, - 299.4, - 299.6, - 299.7, - 299.8, - 300.0, - 300.1, - 300.1, - 300.1, - 300.1, - 299.9, - 300.1, - 301.1, - 290.3, - 299.6, - 299.7, - 298.0, - 296.0, - 299.6, - 299.4, - 299.8, - 299.6, - 299.6, - 299.9, - 300.1, - 300.4, - 300.7, - 301.0, - 301.3, - 301.6, - 302.2, - 299.7, - 300.3, - ], - [ - 298.4, - 297.2, - 296.5, - 293.9, - 297.7, - 298.4, - 294.5, - 296.0, - 301.3, - 299.8, - 288.7, - 292.5, - 294.3, - 296.1, - 298.5, - 298.7, - 299.2, - 299.5, - 299.7, - 299.7, - 299.6, - 299.5, - 299.6, - 300.0, - 300.4, - 300.7, - 300.8, - 300.4, - 300.8, - 301.3, - 301.5, - 301.9, - 302.0, - 297.3, - 301.3, - 301.4, - 301.3, - 301.2, - 301.1, - 301.0, - 300.9, - 300.8, - 300.8, - 300.7, - 300.5, - 300.3, - 300.3, - 300.2, - 300.0, - 300.0, - 300.0, - 299.9, - 299.9, - 299.9, - 299.9, - 299.9, - 299.9, - 299.8, - 299.7, - 299.8, - 299.8, - 299.8, - 299.8, - 299.8, - 299.9, - 300.1, - 300.2, - 300.1, - 299.9, - 299.8, - 299.8, - 300.0, - 299.9, - 299.7, - 299.7, - 298.3, - 295.7, - 296.7, - 303.6, - 300.4, - 299.1, - 299.5, - 299.7, - 299.7, - 299.0, - 298.9, - 299.1, - 299.3, - 299.6, - 299.9, - 300.0, - 300.5, - 301.6, - 298.6, - 297.4, - 298.3, - ], - [ - 296.5, - 294.9, - 293.1, - 296.6, - 300.2, - 300.2, - 298.6, - 299.1, - 301.0, - 299.0, - 290.4, - 291.1, - 290.9, - 290.4, - 297.5, - 297.8, - 298.0, - 298.4, - 299.1, - 299.6, - 299.8, - 293.2, - 299.1, - 299.5, - 299.9, - 300.2, - 300.3, - 297.6, - 298.5, - 301.0, - 301.0, - 301.2, - 301.1, - 298.1, - 300.7, - 300.7, - 300.6, - 300.5, - 300.4, - 300.2, - 299.9, - 299.7, - 299.6, - 299.4, - 299.2, - 299.0, - 298.9, - 298.8, - 298.6, - 298.6, - 298.6, - 298.6, - 298.7, - 298.6, - 298.6, - 298.7, - 298.7, - 298.7, - 298.7, - 298.8, - 299.0, - 299.1, - 299.2, - 299.3, - 299.5, - 299.8, - 299.9, - 299.6, - 299.2, - 299.0, - 299.1, - 299.6, - 300.1, - 300.1, - 294.4, - 299.6, - 300.2, - 299.3, - 301.1, - 300.2, - 299.0, - 299.5, - 299.1, - 298.7, - 298.5, - 298.2, - 298.1, - 298.1, - 298.3, - 298.6, - 298.7, - 299.2, - 300.9, - 295.8, - 295.9, - 296.5, - ], - [ - 298.7, - 297.5, - 297.7, - 299.4, - 300.9, - 299.8, - 297.2, - 296.9, - 299.0, - 301.1, - 291.8, - 289.3, - 288.2, - 297.1, - 297.1, - 296.8, - 296.8, - 297.3, - 298.3, - 299.3, - 300.1, - 291.1, - 298.5, - 298.8, - 299.1, - 299.3, - 298.9, - 295.9, - 297.2, - 296.0, - 300.4, - 300.4, - 300.1, - 297.6, - 300.2, - 300.1, - 300.1, - 300.1, - 300.0, - 299.8, - 299.6, - 299.3, - 299.0, - 298.6, - 298.3, - 298.1, - 297.9, - 297.7, - 297.6, - 297.5, - 297.5, - 297.4, - 297.5, - 297.5, - 297.4, - 297.2, - 297.2, - 297.3, - 297.2, - 297.5, - 297.8, - 298.1, - 298.3, - 298.6, - 298.8, - 299.2, - 299.5, - 299.4, - 298.8, - 298.1, - 298.0, - 298.6, - 300.1, - 296.6, - 299.2, - 297.7, - 297.0, - 296.4, - 297.7, - 298.7, - 298.7, - 298.8, - 298.4, - 298.2, - 298.1, - 297.8, - 297.5, - 297.3, - 297.2, - 297.5, - 297.7, - 297.9, - 299.8, - 296.7, - 297.8, - 298.6, - ], - [ - 298.5, - 297.9, - 297.1, - 296.8, - 297.3, - 297.1, - 294.6, - 293.7, - 296.6, - 299.4, - 293.8, - 298.8, - 284.9, - 289.3, - 297.1, - 295.9, - 295.7, - 296.3, - 297.4, - 298.5, - 291.5, - 294.5, - 298.3, - 298.2, - 298.4, - 298.2, - 297.4, - 293.2, - 293.8, - 293.9, - 299.2, - 299.3, - 295.4, - 299.2, - 299.3, - 299.1, - 299.2, - 299.2, - 299.3, - 299.4, - 299.3, - 299.0, - 298.6, - 298.1, - 297.6, - 297.3, - 297.0, - 296.8, - 296.7, - 296.6, - 296.5, - 296.2, - 296.3, - 296.3, - 296.1, - 296.1, - 296.1, - 296.1, - 296.3, - 296.5, - 296.8, - 297.0, - 297.2, - 297.2, - 297.3, - 297.7, - 298.4, - 299.1, - 298.9, - 297.9, - 297.2, - 296.4, - 291.5, - 293.8, - 299.1, - 299.1, - 298.8, - 298.8, - 298.9, - 298.9, - 298.5, - 298.2, - 297.8, - 297.5, - 297.4, - 297.3, - 297.1, - 296.9, - 296.7, - 296.5, - 296.5, - 296.9, - 299.4, - 299.6, - 298.4, - 298.6, - ], - [ - 295.1, - 294.7, - 293.0, - 292.3, - 293.5, - 294.7, - 292.3, - 293.1, - 295.3, - 296.1, - 294.7, - 300.3, - 284.4, - 288.6, - 289.2, - 295.6, - 294.9, - 295.7, - 296.8, - 298.0, - 291.2, - 291.8, - 290.6, - 298.1, - 298.1, - 297.6, - 288.6, - 289.9, - 289.8, - 296.0, - 297.7, - 298.2, - 298.0, - 297.6, - 297.2, - 297.0, - 297.2, - 297.5, - 297.7, - 297.9, - 298.2, - 298.4, - 298.3, - 298.0, - 297.6, - 297.1, - 296.6, - 296.3, - 295.9, - 295.7, - 295.4, - 295.1, - 295.0, - 294.9, - 294.7, - 294.7, - 294.6, - 294.6, - 294.8, - 295.2, - 295.4, - 295.3, - 295.4, - 295.3, - 295.2, - 295.4, - 296.6, - 298.2, - 298.9, - 289.8, - 287.0, - 289.8, - 294.7, - 298.4, - 298.6, - 298.7, - 299.0, - 299.0, - 298.6, - 298.5, - 298.2, - 297.8, - 297.5, - 297.1, - 296.7, - 296.6, - 296.6, - 296.4, - 296.2, - 296.1, - 296.1, - 295.9, - 296.5, - 296.6, - 295.4, - 295.0, - ], - [ - 292.2, - 290.9, - 290.4, - 290.4, - 290.2, - 288.6, - 289.6, - 290.2, - 292.8, - 292.5, - 299.2, - 289.1, - 287.6, - 289.6, - 290.8, - 290.7, - 295.2, - 295.5, - 296.5, - 297.5, - 289.3, - 291.2, - 287.7, - 298.1, - 298.2, - 288.9, - 286.8, - 285.9, - 286.0, - 292.4, - 295.0, - 296.9, - 296.4, - 295.5, - 294.8, - 294.5, - 294.8, - 295.2, - 295.6, - 295.9, - 296.3, - 296.7, - 296.9, - 297.0, - 296.8, - 296.5, - 296.1, - 295.8, - 295.3, - 294.9, - 294.6, - 294.3, - 294.1, - 293.8, - 293.6, - 293.4, - 293.2, - 293.1, - 293.0, - 293.1, - 293.1, - 293.0, - 293.0, - 293.0, - 292.9, - 293.4, - 294.6, - 296.7, - 290.7, - 283.2, - 283.4, - 296.3, - 294.1, - 297.6, - 297.4, - 297.6, - 295.5, - 295.2, - 298.1, - 297.8, - 297.6, - 297.3, - 297.1, - 296.8, - 296.3, - 295.9, - 295.8, - 295.7, - 295.6, - 295.5, - 295.5, - 294.7, - 293.2, - 292.9, - 292.1, - 292.2, - ], - [ - 291.1, - 288.9, - 286.0, - 287.7, - 287.6, - 286.6, - 287.5, - 287.1, - 290.2, - 290.0, - 297.1, - 287.9, - 286.7, - 288.3, - 290.4, - 288.3, - 295.3, - 295.2, - 295.8, - 288.5, - 288.9, - 288.4, - 287.4, - 287.3, - 291.2, - 286.0, - 284.9, - 280.5, - 282.4, - 280.3, - 283.8, - 293.2, - 293.6, - 293.6, - 292.4, - 292.2, - 292.3, - 292.5, - 293.2, - 293.8, - 294.2, - 294.8, - 295.2, - 295.5, - 295.6, - 295.5, - 295.2, - 294.8, - 294.4, - 294.0, - 293.7, - 293.4, - 293.1, - 292.8, - 292.6, - 292.4, - 292.2, - 292.0, - 291.8, - 291.8, - 291.7, - 291.7, - 291.8, - 291.8, - 291.6, - 292.0, - 293.2, - 295.5, - 284.4, - 280.6, - 286.9, - 294.5, - 295.2, - 296.1, - 295.2, - 291.2, - 296.1, - 296.7, - 296.8, - 296.6, - 296.5, - 296.5, - 296.3, - 296.1, - 295.7, - 295.3, - 295.1, - 294.9, - 294.9, - 294.8, - 294.9, - 294.4, - 290.6, - 290.8, - 290.8, - 291.1, - ], - [ - 290.4, - 288.1, - 285.2, - 285.7, - 286.9, - 287.4, - 287.2, - 286.0, - 287.6, - 289.6, - 290.1, - 285.8, - 284.8, - 287.0, - 289.7, - 288.5, - 294.5, - 294.6, - 286.5, - 286.4, - 284.1, - 285.4, - 287.0, - 288.2, - 287.0, - 282.8, - 281.1, - 278.0, - 278.5, - 280.5, - 279.8, - 276.8, - 289.8, - 292.0, - 290.9, - 290.5, - 290.5, - 290.4, - 290.8, - 291.3, - 291.9, - 292.6, - 293.2, - 293.7, - 294.0, - 294.1, - 294.0, - 293.9, - 293.6, - 293.2, - 292.8, - 292.4, - 292.1, - 291.6, - 291.3, - 291.1, - 290.9, - 290.9, - 290.9, - 290.8, - 290.7, - 290.8, - 290.9, - 290.9, - 290.6, - 290.9, - 292.0, - 293.9, - 279.2, - 280.3, - 285.6, - 293.0, - 293.5, - 293.6, - 292.5, - 293.9, - 294.4, - 294.8, - 295.0, - 295.2, - 295.2, - 295.3, - 295.3, - 295.3, - 295.1, - 294.9, - 294.6, - 294.4, - 294.2, - 294.0, - 294.0, - 294.2, - 294.0, - 289.0, - 289.9, - 290.0, - ], - [ - 288.3, - 287.8, - 287.2, - 286.6, - 286.8, - 287.7, - 288.2, - 287.0, - 286.6, - 287.5, - 284.8, - 283.8, - 284.1, - 293.8, - 294.4, - 283.1, - 280.0, - 280.8, - 283.8, - 284.8, - 281.4, - 284.1, - 284.0, - 275.8, - 272.7, - 278.9, - 274.1, - 272.2, - 277.0, - 274.2, - 277.1, - 276.9, - 275.1, - 288.0, - 288.3, - 289.1, - 289.0, - 288.8, - 288.8, - 289.2, - 289.6, - 290.3, - 290.9, - 291.6, - 292.0, - 292.2, - 292.2, - 292.2, - 292.1, - 291.9, - 291.6, - 291.3, - 291.0, - 290.6, - 290.3, - 290.0, - 289.7, - 289.6, - 289.7, - 289.9, - 289.8, - 289.7, - 289.8, - 289.8, - 289.5, - 289.8, - 290.7, - 279.3, - 278.3, - 280.4, - 281.1, - 288.7, - 288.6, - 288.4, - 288.4, - 291.6, - 292.2, - 292.7, - 293.0, - 293.3, - 293.5, - 293.8, - 293.9, - 294.1, - 294.1, - 294.0, - 293.8, - 293.7, - 293.6, - 293.4, - 293.1, - 293.1, - 293.5, - 287.8, - 287.2, - 287.1, - ], - [ - 283.4, - 285.0, - 285.9, - 285.5, - 286.3, - 287.6, - 287.1, - 286.6, - 286.6, - 284.8, - 282.4, - 282.7, - 284.0, - 293.1, - 274.8, - 275.5, - 279.8, - 279.6, - 278.0, - 283.2, - 282.9, - 276.8, - 261.2, - 245.8, - 259.6, - 256.2, - 259.3, - 267.7, - 277.8, - 274.4, - 275.5, - 275.4, - 274.8, - 284.1, - 284.2, - 285.8, - 286.5, - 286.5, - 287.1, - 287.7, - 288.2, - 288.7, - 289.2, - 289.7, - 290.1, - 290.3, - 290.4, - 290.2, - 290.2, - 290.0, - 289.8, - 289.6, - 289.3, - 289.1, - 289.0, - 288.9, - 288.9, - 288.7, - 288.7, - 288.9, - 288.9, - 288.6, - 288.8, - 288.7, - 288.3, - 288.6, - 281.2, - 277.0, - 276.8, - 277.0, - 275.8, - 277.1, - 276.5, - 276.5, - 276.7, - 288.3, - 290.4, - 291.3, - 291.5, - 291.7, - 291.9, - 292.1, - 292.4, - 292.6, - 292.8, - 292.9, - 292.8, - 292.8, - 292.7, - 292.6, - 292.3, - 292.2, - 292.5, - 292.7, - 282.1, - 283.9, - ], - [ - 279.1, - 280.6, - 283.5, - 283.6, - 289.8, - 290.2, - 290.1, - 290.0, - 290.4, - 290.8, - 280.5, - 281.3, - 283.8, - 276.7, - 274.6, - 276.6, - 276.4, - 275.7, - 272.5, - 281.0, - 278.8, - 254.8, - 251.9, - 253.4, - 256.7, - 247.5, - 257.2, - 263.1, - 270.5, - 272.0, - 273.2, - 273.7, - 274.2, - 280.9, - 279.7, - 282.0, - 282.5, - 282.2, - 284.2, - 285.2, - 286.2, - 287.0, - 287.5, - 287.9, - 288.2, - 288.4, - 288.6, - 288.5, - 288.3, - 288.2, - 287.9, - 287.7, - 287.4, - 287.3, - 287.3, - 287.4, - 287.6, - 287.5, - 287.5, - 287.6, - 287.8, - 287.6, - 287.7, - 287.6, - 287.1, - 279.8, - 279.5, - 273.8, - 273.7, - 274.4, - 274.8, - 275.3, - 275.0, - 275.1, - 274.7, - 284.1, - 288.4, - 289.8, - 290.2, - 290.2, - 290.6, - 290.7, - 290.9, - 291.1, - 291.3, - 291.6, - 291.8, - 291.8, - 291.7, - 291.6, - 291.5, - 291.4, - 291.5, - 291.4, - 282.4, - 278.9, - ], - [ - 278.8, - 277.5, - 278.0, - 288.2, - 288.2, - 288.1, - 288.2, - 288.6, - 289.0, - 289.3, - 278.3, - 280.4, - 277.2, - 272.0, - 275.3, - 276.3, - 275.6, - 273.5, - 269.1, - 269.9, - 255.7, - 246.4, - 251.9, - 253.4, - 245.8, - 246.2, - 258.6, - 263.1, - 267.9, - 270.2, - 270.5, - 271.0, - 276.2, - 276.7, - 274.4, - 277.2, - 269.0, - 266.8, - 280.2, - 282.3, - 283.3, - 284.2, - 285.0, - 285.6, - 285.8, - 286.0, - 286.3, - 286.5, - 286.4, - 286.3, - 286.1, - 285.9, - 285.7, - 285.6, - 285.5, - 285.7, - 286.1, - 286.1, - 286.2, - 286.4, - 286.5, - 286.5, - 286.6, - 286.7, - 277.9, - 276.5, - 273.3, - 269.3, - 271.2, - 272.5, - 272.2, - 271.8, - 271.7, - 271.3, - 270.9, - 270.7, - 284.5, - 287.6, - 288.5, - 288.9, - 289.5, - 289.8, - 289.6, - 289.7, - 290.0, - 290.2, - 290.5, - 290.7, - 290.8, - 290.7, - 290.7, - 290.5, - 290.1, - 290.0, - 289.2, - 281.4, - ], - [ - 285.2, - 286.4, - 286.4, - 286.0, - 286.2, - 286.3, - 286.6, - 286.0, - 276.0, - 274.1, - 274.7, - 274.2, - 270.0, - 272.4, - 287.2, - 274.7, - 275.2, - 275.3, - 275.2, - 260.8, - 251.5, - 268.3, - 270.1, - 263.4, - 252.5, - 263.2, - 252.3, - 262.8, - 264.5, - 265.2, - 266.5, - 268.0, - 272.0, - 270.4, - 247.3, - 271.7, - 275.1, - 266.1, - 277.3, - 278.6, - 280.3, - 281.6, - 282.2, - 282.6, - 283.1, - 283.4, - 283.9, - 284.3, - 284.5, - 284.3, - 283.8, - 283.5, - 283.5, - 283.4, - 283.6, - 284.0, - 284.5, - 284.7, - 284.9, - 285.2, - 285.3, - 285.5, - 285.4, - 285.4, - 273.3, - 267.6, - 265.5, - 264.9, - 263.3, - 271.6, - 270.0, - 268.6, - 268.3, - 268.5, - 268.7, - 270.4, - 281.5, - 285.3, - 286.5, - 287.6, - 288.1, - 288.5, - 288.7, - 288.6, - 288.7, - 288.8, - 289.0, - 289.6, - 289.7, - 289.8, - 289.6, - 289.3, - 288.6, - 287.8, - 279.7, - 279.1, - ], - [ - 273.8, - 284.8, - 284.3, - 283.4, - 284.1, - 284.3, - 269.9, - 276.7, - 275.1, - 272.3, - 266.6, - 264.7, - 269.8, - 274.6, - 283.5, - 272.9, - 272.1, - 273.2, - 272.5, - 263.6, - 259.2, - 256.8, - 262.2, - 266.6, - 266.4, - 261.5, - 258.6, - 264.1, - 262.1, - 262.1, - 260.5, - 263.4, - 255.3, - 246.1, - 253.9, - 266.2, - 270.5, - 272.7, - 262.4, - 270.6, - 276.3, - 277.7, - 277.8, - 277.9, - 278.5, - 279.6, - 280.3, - 280.7, - 281.2, - 281.3, - 281.1, - 281.1, - 281.3, - 281.5, - 281.7, - 282.3, - 282.9, - 283.3, - 283.5, - 284.0, - 284.2, - 284.4, - 284.4, - 278.8, - 269.8, - 264.9, - 264.1, - 262.6, - 262.6, - 268.9, - 265.4, - 264.7, - 264.5, - 265.8, - 266.6, - 265.8, - 267.8, - 279.0, - 281.4, - 283.4, - 284.6, - 285.3, - 285.8, - 286.0, - 286.8, - 286.9, - 287.1, - 288.1, - 288.7, - 288.8, - 288.4, - 288.0, - 287.3, - 286.7, - 277.4, - 276.5, - ], - [ - 274.9, - 283.4, - 282.1, - 269.1, - 281.3, - 261.9, - 270.2, - 267.7, - 280.6, - 281.8, - 282.8, - 273.9, - 265.5, - 281.3, - 272.6, - 269.8, - 268.9, - 269.3, - 271.0, - 266.4, - 261.9, - 257.4, - 261.4, - 253.0, - 261.0, - 261.9, - 257.6, - 260.1, - 257.9, - 258.3, - 256.1, - 252.3, - 255.7, - 247.3, - 251.4, - 240.9, - 265.6, - 270.0, - 261.5, - 269.7, - 270.6, - 274.3, - 274.8, - 275.4, - 276.4, - 277.5, - 277.5, - 277.6, - 278.1, - 278.5, - 278.6, - 279.0, - 279.3, - 279.7, - 280.0, - 280.5, - 281.1, - 281.7, - 282.1, - 282.5, - 283.0, - 283.1, - 283.4, - 276.3, - 268.0, - 265.9, - 262.4, - 261.1, - 261.6, - 262.4, - 262.7, - 263.0, - 262.7, - 264.7, - 265.1, - 264.7, - 266.2, - 265.9, - 276.9, - 279.0, - 280.8, - 281.2, - 280.9, - 281.5, - 283.2, - 283.6, - 284.3, - 286.1, - 287.3, - 287.6, - 286.8, - 286.8, - 286.5, - 286.3, - 275.0, - 276.1, - ], - [ - 274.5, - 271.0, - 266.0, - 268.2, - 266.9, - 266.2, - 266.2, - 263.3, - 277.4, - 279.3, - 280.8, - 273.2, - 273.8, - 278.1, - 269.3, - 267.1, - 265.8, - 264.3, - 266.5, - 265.0, - 266.8, - 262.1, - 257.7, - 260.3, - 253.8, - 252.7, - 247.7, - 255.3, - 255.3, - 253.8, - 254.4, - 250.6, - 250.4, - 243.1, - 244.6, - 248.9, - 249.0, - 266.6, - 262.0, - 270.0, - 272.0, - 273.2, - 273.3, - 274.0, - 274.7, - 275.4, - 275.5, - 275.6, - 275.9, - 276.1, - 276.5, - 277.1, - 277.7, - 278.1, - 278.4, - 278.9, - 279.7, - 280.2, - 280.6, - 281.2, - 281.9, - 282.1, - 282.5, - 276.1, - 272.3, - 267.5, - 260.7, - 261.8, - 262.1, - 259.5, - 259.8, - 261.4, - 261.6, - 272.4, - 273.0, - 259.2, - 261.9, - 262.4, - 264.8, - 274.8, - 276.8, - 276.2, - 276.9, - 277.8, - 279.4, - 280.8, - 282.1, - 284.0, - 285.3, - 286.1, - 285.4, - 285.1, - 285.2, - 285.8, - 285.1, - 283.7, - ], - [ - 273.0, - 274.0, - 266.9, - 261.6, - 264.6, - 265.3, - 264.3, - 262.6, - 262.2, - 264.1, - 268.2, - 268.7, - 267.6, - 266.1, - 263.6, - 262.5, - 260.5, - 260.1, - 259.8, - 258.0, - 258.9, - 259.2, - 256.6, - 251.1, - 247.7, - 252.2, - 249.4, - 249.4, - 253.3, - 249.6, - 249.9, - 246.2, - 246.0, - 244.9, - 242.8, - 246.4, - 249.2, - 250.5, - 264.2, - 269.5, - 271.8, - 271.9, - 271.8, - 272.8, - 273.5, - 273.9, - 273.9, - 274.0, - 274.2, - 274.5, - 275.1, - 275.7, - 276.4, - 276.9, - 277.2, - 277.7, - 278.5, - 279.1, - 279.6, - 280.3, - 281.1, - 281.4, - 281.0, - 275.6, - 271.9, - 266.2, - 263.0, - 260.8, - 257.4, - 257.4, - 256.9, - 258.9, - 259.5, - 269.7, - 252.8, - 258.9, - 261.3, - 262.3, - 264.2, - 272.0, - 273.8, - 268.0, - 274.9, - 276.0, - 277.5, - 279.2, - 280.6, - 282.1, - 283.3, - 284.4, - 284.4, - 284.1, - 285.0, - 285.1, - 284.0, - 282.6, - ], - [ - 271.1, - 272.8, - 268.2, - 266.4, - 264.6, - 262.8, - 263.0, - 261.6, - 261.5, - 261.0, - 260.6, - 260.5, - 260.5, - 260.2, - 259.6, - 258.6, - 256.7, - 257.0, - 257.7, - 256.5, - 256.8, - 257.7, - 258.7, - 253.6, - 252.2, - 251.6, - 251.0, - 251.5, - 252.9, - 251.5, - 249.3, - 244.7, - 242.7, - 242.7, - 240.6, - 242.4, - 248.5, - 248.8, - 265.8, - 269.7, - 270.9, - 270.4, - 269.2, - 271.3, - 272.1, - 272.4, - 272.5, - 272.7, - 273.0, - 273.5, - 274.1, - 274.7, - 275.4, - 275.9, - 276.3, - 276.8, - 277.6, - 278.3, - 279.1, - 279.8, - 280.5, - 280.6, - 280.3, - 266.9, - 265.4, - 261.0, - 259.6, - 258.2, - 256.0, - 255.5, - 255.1, - 257.3, - 257.7, - 258.2, - 257.1, - 260.2, - 258.5, - 260.7, - 260.2, - 260.6, - 264.8, - 267.5, - 274.2, - 275.5, - 276.9, - 278.5, - 279.6, - 280.7, - 281.8, - 282.9, - 282.9, - 283.2, - 284.6, - 283.9, - 281.7, - 280.7, - ], - [ - 272.9, - 278.0, - 266.3, - 264.1, - 264.1, - 261.7, - 260.5, - 260.0, - 258.1, - 256.2, - 255.4, - 254.6, - 254.8, - 255.0, - 254.6, - 256.0, - 254.5, - 254.0, - 255.1, - 256.0, - 256.1, - 256.0, - 257.0, - 257.5, - 256.7, - 254.9, - 252.3, - 253.8, - 251.8, - 248.4, - 245.3, - 242.5, - 240.9, - 240.6, - 239.5, - 239.7, - 245.4, - 244.2, - 260.7, - 268.4, - 269.1, - 268.7, - 240.9, - 267.9, - 269.2, - 270.1, - 270.8, - 271.1, - 272.0, - 272.8, - 273.2, - 274.0, - 274.8, - 275.5, - 276.1, - 276.5, - 277.1, - 277.9, - 278.7, - 279.4, - 280.0, - 279.9, - 267.4, - 266.1, - 263.0, - 261.9, - 258.8, - 256.7, - 255.0, - 255.2, - 254.8, - 254.5, - 256.1, - 255.6, - 256.0, - 268.1, - 253.7, - 258.8, - 255.8, - 255.8, - 261.0, - 266.4, - 273.4, - 275.0, - 276.6, - 278.6, - 279.7, - 280.2, - 281.0, - 281.7, - 282.3, - 282.5, - 283.6, - 282.1, - 269.5, - 272.3, - ], - [ - 279.5, - 278.6, - 276.0, - 264.8, - 272.6, - 272.6, - 259.8, - 257.3, - 254.5, - 251.7, - 250.6, - 250.5, - 250.5, - 250.1, - 250.8, - 252.4, - 253.3, - 251.8, - 252.0, - 252.7, - 252.6, - 252.2, - 252.9, - 254.6, - 255.4, - 255.2, - 255.5, - 251.6, - 249.8, - 244.5, - 243.6, - 239.9, - 238.9, - 237.6, - 238.1, - 239.3, - 240.5, - 250.0, - 255.8, - 266.6, - 266.7, - 267.2, - 245.0, - 243.8, - 265.6, - 267.4, - 268.2, - 268.7, - 270.2, - 271.3, - 272.1, - 272.9, - 273.6, - 274.7, - 275.5, - 276.4, - 277.0, - 277.7, - 278.3, - 278.6, - 278.1, - 269.4, - 266.8, - 263.5, - 263.3, - 262.7, - 260.3, - 257.5, - 254.8, - 253.6, - 252.4, - 251.2, - 251.1, - 251.3, - 262.3, - 268.0, - 249.2, - 256.3, - 252.8, - 250.1, - 256.0, - 271.0, - 273.2, - 275.2, - 277.1, - 278.8, - 279.9, - 280.3, - 280.9, - 281.6, - 282.6, - 282.9, - 283.4, - 282.9, - 280.9, - 270.4, - ], - [ - 279.7, - 277.9, - 275.4, - 273.1, - 257.7, - 272.2, - 269.6, - 250.4, - 251.0, - 248.2, - 245.6, - 246.1, - 246.9, - 246.0, - 247.1, - 248.1, - 248.7, - 248.0, - 248.3, - 248.7, - 248.9, - 248.9, - 248.9, - 249.6, - 250.4, - 250.2, - 247.7, - 246.4, - 244.8, - 244.6, - 241.7, - 239.0, - 238.3, - 236.0, - 233.8, - 235.6, - 236.8, - 240.2, - 250.0, - 258.6, - 257.6, - 257.7, - 256.0, - 242.0, - 257.2, - 264.2, - 264.5, - 265.3, - 266.7, - 268.3, - 269.3, - 271.1, - 272.2, - 272.0, - 273.1, - 274.7, - 275.8, - 276.5, - 277.1, - 277.2, - 266.4, - 266.3, - 262.2, - 262.1, - 260.9, - 261.2, - 259.3, - 256.6, - 254.0, - 251.6, - 249.0, - 248.4, - 251.3, - 250.0, - 253.9, - 263.0, - 245.9, - 252.7, - 254.5, - 246.1, - 268.6, - 271.9, - 273.7, - 275.7, - 277.1, - 278.2, - 279.5, - 280.0, - 280.7, - 281.3, - 281.8, - 281.8, - 281.7, - 282.2, - 281.6, - 265.3, - ], - [ - 279.8, - 277.7, - 255.1, - 258.3, - 255.5, - 270.2, - 266.8, - 246.2, - 247.0, - 245.2, - 242.0, - 243.4, - 243.2, - 243.2, - 244.7, - 245.2, - 244.9, - 243.2, - 243.9, - 244.2, - 244.7, - 244.7, - 244.2, - 244.3, - 245.4, - 243.4, - 240.8, - 239.1, - 238.9, - 238.3, - 236.7, - 236.1, - 234.8, - 233.7, - 232.2, - 231.0, - 229.6, - 237.8, - 243.5, - 244.1, - 242.9, - 240.6, - 250.7, - 250.6, - 239.3, - 252.1, - 250.7, - 250.8, - 256.8, - 263.2, - 267.1, - 268.8, - 269.6, - 250.4, - 257.9, - 258.2, - 259.1, - 262.1, - 261.1, - 259.5, - 259.4, - 261.1, - 259.4, - 257.8, - 257.6, - 257.5, - 255.0, - 254.4, - 251.8, - 248.8, - 245.5, - 249.3, - 250.1, - 249.3, - 250.3, - 252.9, - 246.2, - 249.3, - 255.3, - 261.8, - 268.1, - 271.6, - 273.4, - 274.7, - 275.4, - 276.2, - 278.5, - 279.4, - 280.3, - 280.9, - 281.3, - 281.2, - 281.0, - 281.4, - 281.4, - 280.7, - ], - [ - 278.7, - 277.8, - 264.9, - 264.0, - 254.2, - 267.9, - 249.4, - 242.7, - 244.0, - 242.0, - 240.6, - 241.8, - 241.4, - 241.1, - 241.8, - 241.7, - 240.0, - 238.2, - 238.6, - 239.1, - 239.4, - 239.5, - 239.9, - 240.3, - 240.7, - 238.7, - 236.5, - 235.7, - 234.5, - 233.6, - 232.0, - 229.2, - 230.4, - 227.2, - 226.2, - 229.9, - 232.8, - 235.1, - 237.5, - 239.0, - 239.8, - 240.1, - 240.2, - 241.7, - 245.4, - 245.5, - 246.0, - 244.4, - 255.6, - 260.1, - 262.5, - 266.9, - 267.4, - 250.2, - 259.4, - 258.6, - 258.3, - 257.9, - 256.5, - 254.3, - 254.3, - 256.5, - 256.6, - 254.8, - 251.6, - 252.2, - 251.5, - 249.3, - 246.8, - 245.4, - 244.0, - 242.9, - 248.6, - 248.7, - 248.8, - 248.2, - 249.5, - 252.3, - 246.9, - 258.9, - 267.3, - 270.3, - 272.2, - 248.1, - 253.5, - 273.7, - 276.3, - 277.8, - 279.1, - 279.8, - 281.3, - 281.1, - 280.7, - 280.8, - 280.4, - 279.8, - ], - [ - 276.4, - 276.5, - 275.5, - 275.0, - 250.6, - 252.0, - 256.1, - 241.6, - 241.3, - 241.2, - 239.5, - 239.0, - 238.2, - 239.2, - 238.2, - 237.3, - 236.0, - 234.8, - 234.5, - 234.7, - 235.0, - 234.5, - 234.9, - 236.3, - 236.9, - 235.5, - 234.5, - 233.6, - 231.4, - 229.8, - 228.6, - 228.8, - 227.9, - 227.4, - 232.3, - 234.1, - 234.0, - 232.7, - 232.6, - 234.1, - 233.7, - 233.4, - 235.4, - 237.4, - 237.3, - 240.4, - 241.4, - 242.2, - 251.4, - 252.7, - 255.0, - 261.0, - 261.3, - 245.5, - 252.5, - 252.0, - 250.5, - 251.8, - 252.1, - 252.0, - 251.6, - 252.7, - 252.8, - 248.1, - 247.8, - 248.3, - 247.9, - 245.9, - 244.3, - 243.9, - 243.3, - 243.7, - 243.5, - 242.5, - 247.1, - 246.3, - 247.3, - 246.6, - 245.6, - 252.1, - 266.7, - 269.9, - 271.9, - 249.9, - 252.0, - 243.4, - 273.0, - 270.7, - 270.1, - 271.5, - 278.6, - 255.6, - 257.5, - 277.9, - 277.3, - 276.7, - ], - [ - 274.7, - 274.7, - 274.0, - 273.4, - 240.9, - 248.1, - 247.8, - 246.6, - 243.0, - 240.4, - 237.7, - 244.3, - 241.4, - 241.3, - 232.1, - 232.7, - 233.6, - 232.8, - 231.5, - 231.0, - 230.8, - 230.1, - 230.8, - 232.7, - 234.3, - 232.2, - 231.2, - 230.9, - 229.7, - 228.1, - 227.9, - 227.8, - 227.4, - 228.2, - 229.6, - 229.1, - 229.2, - 230.8, - 231.3, - 231.9, - 231.8, - 231.5, - 231.8, - 234.2, - 236.6, - 238.2, - 240.1, - 241.3, - 240.1, - 238.5, - 239.6, - 243.8, - 255.4, - 242.5, - 250.6, - 249.5, - 245.8, - 245.5, - 244.6, - 244.6, - 244.8, - 244.0, - 244.3, - 245.4, - 245.0, - 244.5, - 243.8, - 243.1, - 242.6, - 242.6, - 241.2, - 242.2, - 244.2, - 243.3, - 240.8, - 245.5, - 246.3, - 245.5, - 243.6, - 247.8, - 266.3, - 269.5, - 247.1, - 253.6, - 249.5, - 247.9, - 247.0, - 250.8, - 266.3, - 261.0, - 263.1, - 270.9, - 274.4, - 275.1, - 274.2, - 273.9, - ], - [ - 272.2, - 272.7, - 272.0, - 271.5, - 270.3, - 270.5, - 242.2, - 243.8, - 247.3, - 263.6, - 258.9, - 246.2, - 240.0, - 240.6, - 239.1, - 235.5, - 236.5, - 238.7, - 229.9, - 227.0, - 227.7, - 226.9, - 226.5, - 229.8, - 232.2, - 230.6, - 230.0, - 229.6, - 229.5, - 228.5, - 227.8, - 227.6, - 227.5, - 228.6, - 229.5, - 229.6, - 229.0, - 228.5, - 228.8, - 229.7, - 230.1, - 230.4, - 231.9, - 238.6, - 238.6, - 238.3, - 238.2, - 237.5, - 239.9, - 244.9, - 246.5, - 245.2, - 248.3, - 255.0, - 234.9, - 238.8, - 242.9, - 248.6, - 248.2, - 250.8, - 250.9, - 247.1, - 248.3, - 241.2, - 241.0, - 240.8, - 240.6, - 240.3, - 239.9, - 244.3, - 242.7, - 240.5, - 241.9, - 241.4, - 237.7, - 237.7, - 244.5, - 244.5, - 247.1, - 252.4, - 266.4, - 269.8, - 249.9, - 253.8, - 245.5, - 238.3, - 238.4, - 242.4, - 247.6, - 249.2, - 245.4, - 255.7, - 264.5, - 264.9, - 266.0, - 269.0, - ], - [ - 269.0, - 269.4, - 269.4, - 269.8, - 271.1, - 270.8, - 268.2, - 266.5, - 265.6, - 264.8, - 261.3, - 246.1, - 235.9, - 237.4, - 238.2, - 235.0, - 235.9, - 237.4, - 237.4, - 228.2, - 237.7, - 235.9, - 223.6, - 225.8, - 227.0, - 228.1, - 227.4, - 226.5, - 226.1, - 226.3, - 226.9, - 228.1, - 227.3, - 226.7, - 227.5, - 235.2, - 235.5, - 235.8, - 235.5, - 235.1, - 235.1, - 235.6, - 236.4, - 238.5, - 239.4, - 239.6, - 240.6, - 240.2, - 239.7, - 241.4, - 243.6, - 241.9, - 241.5, - 251.9, - 244.3, - 240.9, - 241.6, - 242.6, - 242.3, - 243.6, - 243.1, - 242.9, - 245.9, - 238.0, - 238.6, - 238.3, - 238.3, - 240.3, - 241.8, - 244.4, - 243.1, - 238.5, - 239.1, - 239.7, - 239.8, - 242.1, - 247.8, - 245.0, - 251.2, - 264.4, - 268.3, - 269.9, - 232.1, - 250.7, - 244.8, - 242.9, - 241.2, - 237.9, - 239.4, - 246.4, - 245.0, - 249.2, - 248.3, - 247.0, - 251.1, - 263.6, - ], - [ - 267.8, - 268.5, - 268.3, - 269.1, - 269.9, - 268.6, - 266.7, - 265.9, - 264.3, - 262.9, - 255.5, - 237.1, - 230.7, - 236.7, - 238.3, - 235.6, - 234.2, - 235.0, - 234.6, - 237.0, - 240.1, - 234.9, - 234.3, - 231.2, - 225.1, - 227.0, - 228.0, - 227.5, - 227.0, - 226.7, - 228.7, - 237.0, - 234.4, - 235.6, - 236.8, - 236.5, - 235.3, - 235.5, - 236.0, - 236.8, - 237.3, - 237.7, - 237.5, - 237.0, - 237.2, - 237.3, - 237.9, - 238.6, - 238.3, - 238.3, - 239.5, - 239.2, - 238.4, - 240.3, - 238.0, - 237.5, - 238.4, - 239.1, - 239.2, - 239.7, - 240.3, - 241.0, - 242.1, - 241.8, - 241.6, - 240.7, - 240.6, - 241.3, - 242.6, - 243.3, - 242.9, - 242.6, - 236.4, - 238.7, - 241.0, - 250.4, - 251.0, - 253.4, - 262.5, - 268.7, - 268.8, - 231.8, - 243.0, - 241.4, - 239.4, - 238.4, - 234.8, - 235.0, - 237.2, - 241.8, - 242.2, - 247.0, - 246.7, - 247.1, - 248.8, - 257.9, - ], - [ - 266.3, - 268.4, - 267.8, - 267.9, - 267.3, - 266.4, - 265.5, - 264.8, - 262.8, - 262.1, - 259.4, - 245.0, - 231.9, - 235.1, - 237.9, - 236.7, - 235.8, - 235.2, - 235.2, - 236.9, - 236.9, - 234.1, - 232.7, - 231.0, - 231.8, - 233.7, - 234.0, - 225.9, - 227.3, - 236.4, - 237.8, - 239.8, - 236.5, - 236.2, - 236.3, - 236.5, - 236.3, - 237.0, - 237.4, - 237.8, - 238.1, - 237.6, - 236.9, - 236.0, - 235.8, - 236.1, - 236.8, - 237.4, - 237.5, - 237.2, - 237.3, - 237.1, - 237.1, - 237.9, - 237.9, - 238.0, - 238.1, - 237.9, - 237.4, - 237.1, - 237.5, - 238.2, - 238.8, - 239.8, - 240.2, - 240.6, - 240.6, - 240.6, - 240.6, - 241.0, - 242.2, - 243.4, - 236.8, - 239.2, - 240.8, - 241.8, - 249.6, - 254.1, - 240.6, - 239.5, - 239.6, - 236.0, - 232.0, - 229.8, - 229.2, - 228.7, - 227.9, - 229.8, - 234.4, - 238.3, - 241.0, - 250.1, - 247.6, - 247.8, - 249.5, - 254.9, - ], - [ - 266.7, - 266.9, - 265.6, - 265.9, - 265.2, - 264.6, - 264.6, - 262.5, - 250.8, - 241.4, - 240.2, - 239.9, - 236.9, - 238.9, - 240.3, - 238.4, - 236.9, - 236.5, - 234.4, - 233.6, - 234.0, - 234.2, - 233.2, - 233.4, - 235.1, - 233.9, - 231.8, - 232.0, - 232.3, - 234.2, - 233.9, - 235.5, - 235.1, - 235.7, - 236.7, - 237.4, - 237.2, - 237.1, - 237.0, - 236.9, - 237.4, - 237.5, - 237.4, - 236.3, - 235.6, - 235.6, - 235.7, - 235.6, - 235.7, - 235.7, - 235.7, - 235.6, - 235.7, - 235.7, - 235.5, - 235.5, - 235.5, - 235.4, - 235.2, - 235.1, - 235.5, - 236.5, - 237.0, - 237.1, - 237.2, - 237.3, - 237.3, - 237.2, - 237.6, - 239.0, - 241.1, - 246.3, - 232.2, - 234.5, - 234.2, - 231.2, - 231.1, - 233.8, - 236.8, - 238.4, - 236.6, - 236.1, - 234.8, - 235.3, - 233.6, - 233.4, - 235.0, - 237.4, - 239.0, - 239.4, - 240.2, - 241.5, - 242.2, - 244.1, - 247.4, - 257.0, - ], - [ - 247.2, - 247.8, - 250.1, - 246.0, - 246.8, - 247.5, - 245.6, - 243.0, - 238.0, - 235.9, - 237.4, - 238.7, - 238.8, - 239.1, - 240.2, - 236.9, - 235.1, - 236.5, - 234.2, - 231.7, - 231.0, - 231.8, - 232.2, - 232.4, - 235.5, - 235.5, - 236.2, - 235.3, - 234.9, - 235.0, - 235.4, - 236.6, - 236.9, - 237.6, - 238.0, - 238.2, - 238.0, - 238.2, - 238.3, - 238.1, - 238.4, - 238.2, - 238.0, - 237.4, - 236.9, - 237.3, - 236.8, - 236.2, - 236.2, - 236.3, - 236.1, - 235.6, - 235.2, - 234.8, - 234.3, - 233.9, - 233.5, - 233.2, - 232.8, - 232.6, - 233.1, - 234.0, - 234.2, - 234.4, - 234.2, - 234.0, - 233.9, - 233.9, - 233.8, - 234.4, - 235.7, - 238.1, - 235.7, - 235.8, - 240.5, - 224.1, - 225.3, - 227.2, - 232.5, - 240.2, - 239.2, - 238.6, - 239.2, - 239.4, - 235.6, - 235.0, - 233.9, - 235.2, - 235.9, - 236.5, - 239.9, - 238.9, - 238.5, - 239.7, - 242.2, - 245.1, - ], - [ - 237.3, - 237.3, - 238.2, - 237.7, - 238.5, - 239.3, - 238.2, - 236.9, - 235.5, - 236.1, - 237.4, - 237.9, - 237.4, - 237.0, - 237.1, - 236.0, - 235.7, - 236.5, - 236.7, - 236.4, - 235.3, - 234.4, - 234.8, - 234.9, - 235.4, - 235.8, - 236.0, - 235.9, - 236.1, - 236.4, - 236.9, - 237.4, - 237.9, - 238.3, - 238.7, - 238.8, - 238.8, - 238.6, - 238.6, - 238.6, - 238.5, - 238.4, - 238.4, - 238.2, - 238.0, - 238.1, - 237.6, - 236.9, - 236.6, - 236.4, - 236.1, - 235.7, - 235.5, - 235.2, - 234.8, - 234.4, - 234.1, - 233.5, - 232.7, - 231.8, - 231.6, - 232.0, - 232.2, - 232.9, - 233.0, - 232.7, - 232.3, - 232.0, - 231.9, - 232.1, - 232.4, - 233.0, - 233.1, - 234.2, - 236.6, - 232.8, - 232.1, - 232.7, - 233.6, - 236.6, - 235.3, - 235.1, - 236.3, - 234.4, - 234.0, - 234.8, - 235.8, - 237.1, - 238.1, - 239.5, - 240.2, - 240.6, - 239.4, - 238.3, - 238.6, - 238.1, - ], - [ - 231.9, - 231.5, - 231.7, - 231.8, - 232.1, - 232.1, - 231.8, - 231.6, - 231.6, - 232.3, - 233.4, - 233.9, - 233.7, - 233.5, - 233.6, - 234.0, - 235.2, - 236.4, - 237.1, - 237.5, - 237.2, - 237.2, - 237.4, - 237.6, - 237.9, - 238.2, - 238.5, - 238.7, - 238.8, - 238.8, - 238.9, - 239.0, - 239.1, - 239.1, - 239.1, - 239.0, - 238.9, - 239.0, - 238.5, - 238.3, - 238.1, - 238.1, - 238.2, - 238.2, - 238.1, - 238.0, - 237.9, - 237.8, - 237.6, - 237.3, - 237.1, - 237.2, - 237.1, - 236.9, - 236.6, - 236.4, - 236.2, - 235.6, - 235.0, - 234.7, - 234.3, - 233.6, - 233.3, - 233.8, - 235.0, - 235.9, - 235.3, - 234.1, - 234.0, - 234.1, - 233.5, - 233.1, - 233.1, - 233.7, - 234.0, - 232.8, - 232.8, - 233.1, - 233.4, - 233.7, - 233.0, - 232.6, - 232.8, - 233.1, - 233.6, - 234.3, - 234.8, - 235.2, - 235.4, - 235.3, - 235.0, - 234.6, - 234.0, - 233.5, - 233.5, - 232.7, - ], - [ - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - ], - ] - ] +def _make_ugrid_2(filename): + """Create a UGRID file with a 2-d mesh topology.""" + n = netCDF4.Dataset(filename, "w") -def _make_cfa_file(filename): - n = netCDF4.Dataset(filename, "w", format="NETCDF4") + n.Conventions = f"CF-{VN}" - n.Conventions = f"CF-{VN} CFA-0.6.2" - n.comment = ( - "A CFA-netCDF file with non-standarised aggregation instructions" + n.createDimension("time", 2) + n.createDimension("nMesh2_node", 7) + n.createDimension("nMesh2_edge", 9) + n.createDimension("nMesh2_face", 3) + n.createDimension("Two", 2) + n.createDimension("Four", 4) + + Mesh2 = n.createVariable("Mesh2", "i4", ()) + Mesh2.cf_role = "mesh_topology" + Mesh2.topology_dimension = 2 + Mesh2.node_coordinates = "Mesh2_node_x Mesh2_node_y" + Mesh2.face_node_connectivity = "Mesh2_face_nodes" + Mesh2.edge_node_connectivity = "Mesh2_edge_nodes" + Mesh2.face_dimension = "nMesh2_face" + Mesh2.edge_dimension = "nMesh2_edge" + Mesh2.face_face_connectivity = "Mesh2_face_links" + + Mesh2_face_nodes = n.createVariable( + "Mesh2_face_nodes", "i4", ("Four", "nMesh2_face"), fill_value=-99 ) + Mesh2_face_nodes.long_name = "Maps every face to its corner nodes" + Mesh2_face_nodes[...] = [[2, 4, 1], [3, 5, 3], [1, 3, 6], [0, 2, -99]] - n.createDimension("time", 12) - level = n.createDimension("level", 1) - lat = n.createDimension("lat", 73) - lon = n.createDimension("lon", 144) - n.createDimension("f_time", 2) - n.createDimension("f_level", 1) - n.createDimension("f_lat", 1) - n.createDimension("f_lon", 1) - n.createDimension("i", 4) - n.createDimension("j", 2) - - lon = n.createVariable("lon", "f4", ("lon",)) - lon.standard_name = "longitude" - lon.units = "degrees_east" - - lat = n.createVariable("lat", "f4", ("lat",)) - lat.standard_name = "latitude" - lat.units = "degrees_north" - - time = n.createVariable("time", "f4", ("time",)) - time.standard_name = "time" - time.units = "days since 2000-01-01" - - level = n.createVariable("level", "f4", ("level",)) + Mesh2_edge_nodes = n.createVariable( + "Mesh2_edge_nodes", "i4", ("nMesh2_edge", "Two") + ) + Mesh2_edge_nodes.long_name = "Maps every edge to its two nodes" + Mesh2_edge_nodes[...] = [ + [1, 6], + [3, 6], + [3, 1], + [0, 1], + [2, 0], + [2, 3], + [2, 4], + [5, 4], + [3, 5], + ] - tas = n.createVariable("tas", "f4", ()) - tas.standard_name = "air_temperature" - tas.units = "K" - tas.aggregated_dimensions = "time level lat lon" - tas.aggregated_data = "location: aggregation_location file: aggregation_file format: aggregation_format address: aggregation_address tracking_id: aggregation_tracking_id" + # Mesh node coordinates + Mesh2_node_x = n.createVariable("Mesh2_node_x", "f4", ("nMesh2_node",)) + Mesh2_node_x.standard_name = "longitude" + Mesh2_node_x.units = "degrees_east" + Mesh2_node_x[...] = [-45, -43, -45, -43, -45, -43, -40] - loc = n.createVariable("aggregation_location", "i4", ("i", "j")) - loc[0, :] = 6 - loc[1, 0] = level.size - loc[2, 0] = lat.size - loc[3, 0] = lon.size + Mesh2_node_y = n.createVariable("Mesh2_node_y", "f4", ("nMesh2_node",)) + Mesh2_node_y.standard_name = "latitude" + Mesh2_node_y.units = "degrees_north" + Mesh2_node_y[...] = [35, 35, 33, 33, 31, 31, 34] - fil = n.createVariable( - "aggregation_file", str, ("f_time", "f_level", "f_lat", "f_lon") + # Optional mesh topology variables + Mesh2_face_links = n.createVariable( + "Mesh2_face_links", "i4", ("Four", "nMesh2_face"), fill_value=-99 ) - fil[0, 0, 0, 0] = "January-June.nc" - fil[1, 0, 0, 0] = "July-December.nc" + Mesh2_face_links.long_name = "neighbour faces for faces" + Mesh2_face_links[...] = [ + [1, 0, 0], + [2, -99, -99], + [-99, -99, -99], + [-99, -99, -99], + ] - add = n.createVariable( - "aggregation_address", str, ("f_time", "f_level", "f_lat", "f_lon") - ) - add[0, 0, 0, 0] = "tas0" - add[1, 0, 0, 0] = "tas1" + # Non-mesh coordinates + t = n.createVariable("time", "f8", ("time",)) + t.standard_name = "time" + t.units = "seconds since 2016-01-01 00:00:00" + t.bounds = "time_bounds" + t[...] = [43200, 129600] - fmt = n.createVariable("aggregation_format", str, ()) - fmt[()] = "nc" + t_bounds = n.createVariable("time_bounds", "f8", ("time", "Two")) + t_bounds[...] = [[0, 86400], [86400, 172800]] - tid = n.createVariable( - "aggregation_tracking_id", str, ("f_time", "f_level", "f_lat", "f_lon") - ) - tid[0, 0, 0, 0] = "tracking_id0" - tid[1, 0, 0, 0] = "tracking_id1" + # Data variables + ta = n.createVariable("ta", "f4", ("time", "nMesh2_face")) + ta.standard_name = "air_temperature" + ta.units = "K" + ta.mesh = "Mesh2" + ta.location = "face" + ta[...] = [[282.96, 282.69, 283.21], [281.53, 280.99, 281.23]] + + v = n.createVariable("v", "f4", ("time", "nMesh2_edge")) + v.standard_name = "northward_wind" + v.units = "ms-1" + v.mesh = "Mesh2" + v.location = "edge" + v[...] = [ + [10.2, 10.63, 8.74, 9.05, 8.15, 10.89, 8.44, 10.66, 8.93], + [9.66, 10.74, 9.24, 10.58, 9.79, 10.27, 10.58, 11.68, 11.22], + ] - n.close() + pa = n.createVariable("pa", "f4", ("time", "nMesh2_node")) + pa.standard_name = "air_pressure" + pa.units = "hPa" + pa.mesh = "Mesh2" + pa.location = "node" + pa[...] = [ + [999.67, 1006.45, 999.85, 1006.55, 1006.14, 1005.68, 999.48], + [ + 1003.48, + 1006.42, + 1000.83, + 1002.98, + 1008.28, + 1002.97, + 1002.47, + ], + ] + n.close() return filename @@ -15632,21 +5510,16 @@ def _make_cfa_file(filename): "geometry_interior_ring_2.nc" ) -gathered = _make_gathered_file("gathered.nc") - string_char_file = _make_string_char_file("string_char.nc") -broken_bounds_file = _make_broken_bounds_cdl("broken_bounds.cdl") - subsampled_file_1 = _make_subsampled_1("subsampled_1.nc") subsampled_file_1 = _make_subsampled_2("subsampled_2.nc") -regrid_file = _make_regrid_file("regrid.nc") - -cfa_file = _make_cfa_file("cfa.nc") +ugrid_1 = _make_ugrid_1("ugrid_1.nc") +ugrid_2 = _make_ugrid_2("ugrid_2.nc") if __name__ == "__main__": print("Run date:", datetime.datetime.now()) - cf.environment() + cfdm.environment() print() unittest.main(verbosity=2) diff --git a/cf/test/create_test_files_2.py b/cf/test/create_test_files_2.py new file mode 100644 index 0000000000..24694623e1 --- /dev/null +++ b/cf/test/create_test_files_2.py @@ -0,0 +1,10440 @@ +import datetime +import faulthandler +import unittest + +import numpy as np + +faulthandler.enable() # to debug seg faults and timeouts + +import netCDF4 + +import cf + +VN = cf.CF() + + +def _make_broken_bounds_cdl(filename): + with open(filename, mode="w") as f: + f.write( + """netcdf broken_bounds { +dimensions: + lat = 180 ; + bnds = 2 ; + lon = 288 ; + time = UNLIMITED ; // (1825 currently) +variables: + double lat(lat) ; + lat:long_name = "latitude" ; + lat:units = "degrees_north" ; + lat:axis = "Y" ; + lat:bounds = "lat_bnds" ; + lat:standard_name = "latitude" ; + lat:cell_methods = "time: point" ; + double lat_bnds(lat, bnds) ; + lat_bnds:long_name = "latitude bounds" ; + lat_bnds:units = "degrees_north" ; + lat_bnds:axis = "Y" ; + double lon(lon) ; + lon:long_name = "longitude" ; + lon:units = "degrees_east" ; + lon:axis = "X" ; + lon:bounds = "lon_bnds" ; + lon:standard_name = "longitude" ; + lon:cell_methods = "time: point" ; + double lon_bnds(lon, bnds) ; + lon_bnds:long_name = "longitude bounds" ; + lon_bnds:units = "m" ; + lon_bnds:axis = "X" ; + float pr(time, lat, lon) ; + pr:long_name = "Precipitation" ; + pr:units = "kg m-2 s-1" ; + pr:missing_value = 1.e+20f ; + pr:_FillValue = 1.e+20f ; + pr:cell_methods = "area: time: mean" ; + pr:cell_measures = "area: areacella" ; + pr:standard_name = "precipitation_flux" ; + pr:interp_method = "conserve_order1" ; + pr:original_name = "pr" ; + double time(time) ; + time:long_name = "time" ; + time:units = "days since 1850-01-01 00:00:00" ; + time:axis = "T" ; + time:calendar_type = "noleap" ; + time:calendar = "noleap" ; + time:bounds = "time_bnds" ; + time:standard_name = "time" ; + time:description = "Temporal mean" ; + double time_bnds(time, bnds) ; + time_bnds:long_name = "time axis boundaries" ; + time_bnds:units = "days since 1850-01-01 00:00:00" ; + +// global attributes: + :external_variables = "areacella" ; + :Conventions = "CF-""" + + VN + + """" ; + :source = "model" ; + :comment = "Bounds variable has incompatible units to its parent coordinate variable" ; +} +""" + ) + + +def _make_regrid_file(filename): + n = netCDF4.Dataset(filename, "w", format="NETCDF3_CLASSIC") + + n.Conventions = "CF-" + VN + + n.createDimension("time", 2) + n.createDimension("bounds2", 2) + n.createDimension("latitude", 30) + n.createDimension("longitude", 48) + n.createDimension("time_1", 1) + n.createDimension("lat", 73) + n.createDimension("lon", 96) + + latitude = n.createVariable("latitude", "f8", ("latitude",)) + latitude.standard_name = "latitude" + latitude.units = "degrees_north" + latitude.bounds = "latitude_bounds" + latitude[...] = np.arange(-87, 90.0, 6) + + longitude = n.createVariable("longitude", "f8", ("longitude",)) + longitude.standard_name = "longitude" + longitude.units = "degrees_east" + longitude.bounds = "longitude_bounds" + longitude[...] = np.arange(3.75, 360, 7.5) + + lat = n.createVariable("lat", "f8", ("lat",)) + lat.standard_name = "latitude" + lat.units = "degrees_north" + lat.bounds = "lat_bounds" + lat[...] = np.arange(-90, 91.0, 2.5) + + lon = n.createVariable("lon", "f8", ("lon",)) + lon.standard_name = "longitude" + lon.units = "degrees_east" + lon.bounds = "lon_bounds" + lon[...] = np.arange(3.75, 361, 3.75) + + longitude_bounds = n.createVariable( + "longitude_bounds", "f8", ("longitude", "bounds2") + ) + longitude_bounds[..., 0] = longitude[...] - 3.75 + longitude_bounds[..., 1] = longitude[...] + 3.75 + + latitude_bounds = n.createVariable( + "latitude_bounds", "f8", ("latitude", "bounds2") + ) + latitude_bounds[..., 0] = latitude[...] - 3 + latitude_bounds[..., 1] = latitude[...] + 3 + + lon_bounds = n.createVariable("lon_bounds", "f8", ("lon", "bounds2")) + lon_bounds[..., 0] = lon[...] - 1.875 + lon_bounds[..., 1] = lon[...] + 1.875 + + lat_bounds = n.createVariable("lat_bounds", "f8", ("lat", "bounds2")) + lat_bounds[..., 0] = lat[...] - 1.25 + lat_bounds[..., 1] = lat[...] + 1.25 + + time = n.createVariable("time", "f4", ("time",)) + time.standard_name = "time" + time.units = "days since 1860-1-1" + time.calendar = "360_day" + time.axis = "T" + time.bounds = "time_bounds" + time[...] = [15, 45] + + time_bounds = n.createVariable("time_bounds", "f4", ("time", "bounds2")) + time_bounds[...] = [ + [ + 0, + 30, + ], + [30, 60], + ] + + time_1 = n.createVariable("time_1", "f4", ("time_1",)) + time_1.standard_name = "time" + time_1.units = "days since 1860-1-1" + time_1.calendar = "360_day" + time_1.axis = "T" + time_1.bounds = "time_1_bounds" + time_1[...] = 15 + + time_1_bounds = n.createVariable( + "time_1_bounds", "f4", ("time_1", "bounds2") + ) + time_1_bounds[...] = [0, 30] + + height = n.createVariable("height", "f8", ()) + height.units = "m" + height.standard_name = "height" + height.positive = "up" + height.axis = "Z" + height[...] = 2 + + src = n.createVariable("src", "f8", ("time", "latitude", "longitude")) + src.standard_name = "air_temperature" + src.units = "K" + src.coordinates = "height" + src.cell_methods = "time: mean" + + # Don't generate this data randomly - it's useful to see the real + # patterns of global temperature. + src[...] = [ + [ + [ + 243.6, + 242.4, + 241.8, + 241.5, + 241.2, + 240.8, + 240.5, + 240.4, + 240.2, + 240.5, + 241.2, + 241.9, + 242.5, + 243.0, + 243.4, + 244.1, + 245.2, + 246.4, + 247.0, + 247.4, + 248.3, + 250.2, + 251.6, + 252.3, + 253.9, + 255.8, + 257.6, + 258.7, + 258.5, + 257.7, + 256.8, + 255.6, + 254.1, + 252.0, + 251.6, + 252.4, + 254.0, + 255.1, + 254.8, + 254.3, + 253.4, + 252.6, + 251.5, + 249.9, + 248.3, + 246.4, + 244.9, + 244.2, + ], + [ + 243.3, + 241.0, + 239.8, + 238.3, + 237.4, + 237.4, + 238.9, + 240.3, + 241.3, + 241.2, + 241.2, + 240.8, + 241.0, + 242.6, + 244.4, + 245.5, + 246.4, + 248.2, + 249.8, + 251.0, + 254.3, + 260.7, + 265.4, + 266.1, + 265.9, + 265.9, + 266.4, + 266.1, + 265.3, + 264.2, + 262.8, + 261.8, + 261.0, + 258.7, + 256.9, + 257.3, + 259.5, + 262.7, + 264.4, + 264.9, + 265.4, + 264.2, + 264.7, + 263.4, + 258.3, + 251.8, + 247.2, + 245.4, + ], + [ + 248.6, + 245.7, + 245.6, + 244.7, + 243.3, + 243.3, + 244.2, + 245.2, + 249.4, + 251.7, + 248.8, + 245.0, + 243.0, + 244.0, + 245.7, + 245.2, + 244.7, + 246.0, + 246.9, + 248.7, + 252.4, + 257.6, + 266.8, + 269.5, + 269.7, + 270.1, + 270.5, + 270.6, + 270.2, + 269.2, + 266.7, + 266.1, + 266.7, + 267.5, + 267.3, + 266.2, + 266.5, + 268.1, + 267.3, + 267.5, + 271.5, + 271.5, + 271.5, + 271.0, + 267.6, + 261.5, + 255.5, + 252.0, + ], + [ + 270.3, + 269.9, + 269.6, + 268.4, + 266.0, + 264.0, + 258.8, + 256.8, + 262.0, + 265.2, + 263.6, + 257.3, + 254.3, + 253.9, + 255.6, + 256.9, + 255.9, + 253.9, + 254.5, + 261.0, + 263.8, + 267.9, + 270.5, + 271.8, + 272.3, + 272.6, + 273.4, + 273.9, + 273.7, + 273.6, + 273.3, + 273.4, + 273.9, + 273.8, + 273.8, + 273.6, + 274.4, + 274.6, + 271.1, + 268.0, + 271.2, + 271.5, + 271.4, + 271.3, + 271.6, + 271.5, + 270.8, + 270.5, + ], + [ + 274.5, + 274.3, + 274.4, + 274.3, + 274.5, + 274.5, + 273.4, + 273.1, + 273.2, + 273.3, + 273.0, + 271.5, + 271.6, + 272.5, + 272.8, + 272.8, + 272.6, + 272.5, + 272.1, + 272.5, + 273.5, + 273.6, + 274.0, + 274.8, + 274.9, + 275.0, + 275.0, + 275.0, + 274.9, + 274.7, + 274.8, + 274.9, + 275.1, + 275.2, + 275.5, + 275.8, + 276.0, + 276.2, + 276.0, + 273.4, + 272.8, + 273.9, + 274.5, + 274.6, + 274.7, + 274.7, + 274.7, + 274.6, + ], + [ + 275.3, + 275.2, + 275.4, + 275.3, + 275.4, + 275.7, + 275.7, + 275.8, + 275.9, + 275.5, + 274.9, + 274.5, + 274.3, + 274.6, + 274.7, + 275.0, + 275.2, + 275.3, + 275.5, + 275.6, + 276.0, + 276.7, + 277.0, + 276.8, + 276.8, + 277.0, + 277.1, + 276.8, + 276.7, + 277.1, + 277.0, + 277.4, + 277.3, + 277.2, + 277.2, + 277.3, + 277.5, + 278.0, + 278.8, + 279.1, + 278.3, + 278.2, + 277.8, + 277.3, + 277.4, + 276.9, + 276.4, + 275.8, + ], + [ + 278.6, + 278.7, + 278.4, + 278.0, + 277.8, + 278.0, + 278.3, + 278.2, + 278.1, + 278.0, + 278.7, + 279.4, + 279.1, + 279.2, + 279.1, + 278.9, + 279.0, + 279.6, + 279.8, + 280.1, + 280.2, + 280.6, + 281.1, + 280.0, + 280.0, + 280.5, + 281.2, + 281.0, + 280.8, + 281.3, + 281.6, + 281.4, + 281.1, + 280.7, + 280.3, + 280.1, + 280.3, + 280.7, + 280.8, + 283.0, + 281.4, + 280.3, + 279.6, + 279.4, + 279.7, + 279.9, + 279.1, + 278.6, + ], + [ + 283.9, + 283.2, + 282.5, + 281.8, + 281.6, + 282.0, + 282.4, + 282.3, + 282.3, + 283.0, + 284.3, + 284.2, + 284.0, + 283.7, + 283.6, + 283.7, + 284.0, + 284.2, + 284.3, + 285.0, + 285.2, + 285.1, + 285.2, + 286.9, + 287.3, + 286.5, + 286.2, + 285.7, + 285.6, + 285.7, + 285.5, + 285.2, + 285.0, + 284.5, + 284.0, + 283.4, + 283.0, + 283.1, + 283.0, + 288.0, + 285.5, + 285.2, + 284.0, + 283.2, + 283.4, + 284.7, + 284.9, + 284.6, + ], + [ + 289.3, + 288.6, + 288.9, + 289.4, + 288.8, + 289.1, + 289.5, + 289.3, + 289.2, + 289.6, + 289.4, + 288.9, + 288.4, + 288.1, + 288.0, + 288.2, + 288.3, + 288.4, + 289.4, + 290.3, + 291.6, + 290.6, + 290.8, + 291.8, + 291.8, + 290.8, + 290.1, + 290.1, + 290.7, + 290.5, + 290.1, + 289.9, + 289.6, + 289.1, + 288.5, + 287.6, + 286.8, + 286.9, + 287.5, + 294.9, + 293.2, + 291.4, + 289.9, + 289.6, + 290.1, + 290.5, + 290.3, + 289.8, + ], + [ + 292.5, + 292.3, + 293.5, + 292.9, + 296.4, + 295.3, + 294.8, + 294.7, + 294.4, + 293.9, + 293.1, + 292.5, + 291.8, + 291.3, + 291.9, + 293.9, + 292.1, + 293.6, + 297.5, + 295.3, + 295.3, + 295.4, + 295.1, + 294.6, + 294.5, + 294.0, + 294.0, + 294.6, + 295.1, + 295.1, + 295.1, + 295.0, + 294.7, + 294.1, + 293.3, + 292.0, + 290.6, + 289.5, + 293.3, + 301.4, + 299.6, + 296.1, + 294.7, + 294.5, + 294.6, + 294.8, + 294.0, + 293.0, + ], + [ + 293.4, + 293.4, + 295.6, + 293.0, + 297.2, + 299.2, + 298.2, + 297.2, + 296.2, + 295.3, + 294.6, + 294.2, + 294.3, + 294.2, + 295.8, + 298.7, + 297.5, + 299.4, + 300.3, + 298.5, + 297.7, + 297.8, + 297.4, + 297.0, + 296.9, + 296.7, + 297.0, + 297.1, + 297.3, + 297.4, + 297.2, + 296.8, + 296.2, + 295.6, + 294.8, + 293.6, + 292.1, + 290.7, + 290.9, + 297.7, + 300.3, + 297.1, + 298.1, + 296.9, + 295.9, + 295.2, + 294.4, + 293.7, + ], + [ + 295.2, + 295.6, + 295.6, + 295.3, + 298.0, + 300.5, + 298.8, + 298.5, + 297.4, + 296.6, + 296.5, + 296.6, + 296.7, + 296.8, + 298.6, + 301.8, + 301.4, + 299.2, + 299.3, + 299.3, + 300.0, + 299.5, + 299.1, + 299.1, + 298.9, + 298.8, + 299.0, + 299.0, + 298.9, + 298.5, + 297.5, + 296.3, + 295.0, + 294.3, + 293.7, + 292.8, + 292.0, + 292.6, + 290.3, + 292.7, + 299.9, + 296.4, + 297.0, + 297.4, + 296.1, + 295.3, + 294.6, + 294.5, + ], + [ + 297.4, + 296.9, + 294.4, + 294.9, + 296.7, + 300.0, + 299.4, + 299.2, + 298.7, + 298.7, + 299.3, + 299.7, + 299.7, + 299.8, + 300.7, + 301.9, + 301.3, + 300.1, + 301.1, + 301.1, + 301.4, + 301.1, + 300.7, + 300.3, + 300.3, + 300.6, + 300.5, + 300.4, + 300.0, + 299.4, + 298.2, + 296.5, + 294.9, + 293.7, + 292.9, + 292.9, + 293.9, + 293.8, + 289.6, + 297.1, + 298.9, + 296.4, + 296.1, + 298.7, + 297.6, + 296.6, + 296.1, + 296.4, + ], + [ + 300.5, + 299.9, + 295.9, + 295.1, + 295.0, + 299.8, + 300.3, + 300.1, + 300.2, + 300.4, + 300.7, + 300.8, + 301.0, + 301.1, + 301.5, + 301.9, + 302.1, + 302.2, + 302.0, + 300.7, + 301.9, + 301.7, + 301.5, + 301.3, + 301.3, + 301.3, + 301.1, + 300.9, + 300.6, + 300.0, + 299.0, + 297.6, + 296.4, + 295.7, + 295.5, + 296.0, + 297.4, + 295.2, + 298.4, + 300.2, + 298.8, + 298.2, + 297.6, + 299.5, + 299.2, + 298.7, + 298.8, + 299.4, + ], + [ + 301.4, + 299.9, + 298.7, + 296.7, + 294.5, + 299.3, + 300.2, + 300.1, + 300.2, + 300.5, + 300.6, + 300.7, + 301.1, + 300.2, + 301.2, + 300.7, + 301.7, + 302.3, + 300.1, + 300.7, + 300.8, + 300.6, + 300.4, + 300.1, + 299.8, + 299.5, + 299.0, + 298.5, + 298.2, + 297.9, + 297.5, + 297.2, + 297.1, + 297.3, + 297.8, + 298.5, + 299.2, + 297.0, + 300.9, + 300.5, + 300.2, + 299.5, + 299.7, + 300.3, + 300.4, + 300.5, + 300.7, + 301.0, + ], + [ + 301.7, + 298.3, + 298.4, + 297.1, + 296.4, + 299.3, + 299.3, + 299.7, + 300.1, + 300.2, + 300.1, + 300.5, + 301.3, + 299.8, + 301.0, + 299.6, + 301.9, + 301.9, + 301.7, + 301.3, + 300.8, + 300.5, + 300.2, + 300.0, + 299.6, + 299.3, + 299.2, + 298.9, + 298.6, + 298.5, + 298.3, + 298.3, + 298.4, + 298.8, + 299.2, + 299.6, + 299.7, + 297.9, + 298.3, + 299.1, + 300.2, + 299.9, + 299.9, + 300.2, + 300.6, + 301.0, + 301.3, + 301.3, + ], + [ + 296.5, + 296.5, + 298.6, + 298.1, + 297.5, + 291.4, + 294.6, + 298.1, + 298.8, + 299.6, + 297.7, + 299.7, + 300.3, + 299.2, + 300.4, + 301.3, + 299.5, + 300.9, + 300.8, + 300.5, + 300.2, + 299.9, + 299.5, + 299.3, + 299.1, + 299.1, + 299.1, + 299.0, + 299.0, + 299.1, + 299.3, + 299.4, + 299.8, + 299.8, + 299.3, + 299.6, + 299.1, + 298.2, + 298.7, + 300.1, + 299.2, + 299.0, + 298.6, + 298.6, + 299.0, + 299.9, + 298.2, + 297.5, + ], + [ + 296.9, + 296.4, + 296.6, + 295.1, + 297.1, + 293.1, + 290.8, + 295.7, + 296.5, + 297.2, + 293.9, + 297.9, + 297.6, + 293.8, + 295.8, + 298.8, + 298.2, + 298.9, + 298.9, + 299.1, + 298.9, + 298.2, + 297.5, + 296.9, + 296.6, + 296.3, + 296.2, + 296.0, + 296.0, + 296.4, + 296.8, + 297.1, + 297.6, + 298.7, + 296.1, + 294.9, + 296.5, + 298.6, + 298.3, + 298.6, + 298.2, + 297.6, + 297.2, + 296.9, + 296.7, + 297.3, + 297.9, + 297.5, + ], + [ + 290.4, + 289.2, + 288.8, + 289.8, + 293.2, + 290.9, + 288.7, + 291.5, + 295.4, + 292.9, + 289.2, + 292.3, + 289.4, + 284.6, + 287.3, + 293.6, + 294.7, + 293.8, + 294.2, + 295.1, + 295.9, + 296.3, + 296.0, + 295.3, + 294.6, + 294.0, + 293.4, + 293.0, + 292.7, + 292.7, + 292.7, + 292.7, + 293.2, + 293.8, + 284.9, + 292.3, + 296.2, + 295.6, + 296.5, + 297.3, + 296.9, + 296.4, + 295.8, + 295.4, + 295.3, + 294.3, + 292.3, + 292.0, + ], + [ + 287.2, + 286.3, + 287.4, + 286.9, + 287.2, + 284.8, + 288.8, + 285.3, + 285.1, + 284.1, + 282.3, + 276.0, + 275.1, + 274.6, + 277.2, + 277.9, + 286.6, + 288.8, + 289.0, + 289.7, + 290.8, + 291.9, + 292.5, + 292.4, + 292.0, + 291.4, + 290.7, + 290.2, + 289.9, + 290.0, + 290.0, + 289.9, + 289.7, + 284.1, + 279.8, + 286.4, + 287.8, + 291.0, + 293.1, + 293.7, + 294.0, + 294.2, + 294.1, + 293.8, + 293.4, + 293.4, + 289.8, + 287.6, + ], + [ + 280.6, + 285.5, + 288.7, + 288.8, + 286.8, + 281.0, + 278.3, + 276.1, + 275.1, + 274.5, + 259.3, + 252.7, + 252.3, + 264.4, + 271.9, + 273.4, + 278.4, + 279.7, + 279.1, + 284.5, + 286.3, + 287.4, + 287.9, + 288.1, + 287.9, + 287.4, + 287.1, + 287.1, + 287.3, + 287.4, + 287.5, + 286.6, + 280.6, + 274.1, + 274.0, + 274.2, + 274.0, + 280.3, + 288.9, + 290.1, + 290.6, + 290.9, + 291.3, + 291.6, + 291.5, + 291.3, + 289.6, + 281.6, + ], + [ + 283.4, + 283.8, + 281.9, + 278.6, + 274.7, + 270.8, + 275.5, + 275.8, + 273.4, + 263.9, + 261.0, + 262.5, + 259.6, + 261.2, + 263.1, + 263.7, + 257.6, + 263.7, + 270.0, + 273.9, + 278.9, + 279.9, + 281.1, + 282.0, + 282.3, + 282.1, + 282.3, + 283.0, + 283.8, + 284.4, + 284.7, + 279.8, + 267.4, + 263.8, + 267.3, + 266.6, + 266.9, + 269.3, + 279.5, + 284.7, + 286.3, + 286.8, + 287.5, + 288.5, + 289.0, + 288.4, + 285.2, + 278.3, + ], + [ + 274.1, + 268.7, + 266.7, + 267.4, + 275.8, + 272.9, + 272.3, + 266.8, + 265.1, + 263.9, + 260.3, + 255.9, + 254.0, + 254.2, + 254.2, + 251.0, + 246.6, + 247.9, + 260.6, + 268.4, + 272.8, + 274.1, + 275.4, + 275.8, + 276.4, + 277.3, + 278.2, + 279.1, + 280.3, + 281.3, + 282.2, + 276.3, + 266.5, + 261.3, + 260.0, + 260.9, + 266.2, + 262.4, + 264.5, + 274.0, + 276.3, + 278.6, + 281.2, + 283.9, + 285.7, + 285.4, + 284.7, + 279.6, + ], + [ + 272.7, + 265.5, + 263.2, + 260.9, + 259.3, + 258.8, + 258.3, + 257.4, + 256.2, + 256.5, + 257.0, + 255.2, + 252.8, + 252.1, + 249.8, + 244.3, + 241.6, + 242.7, + 251.1, + 267.8, + 266.7, + 266.4, + 271.4, + 272.1, + 273.2, + 274.5, + 275.8, + 276.8, + 278.2, + 279.6, + 278.7, + 268.7, + 262.3, + 257.8, + 255.5, + 256.1, + 257.5, + 260.0, + 258.8, + 260.5, + 268.1, + 275.4, + 278.5, + 280.7, + 282.4, + 283.3, + 281.9, + 275.9, + ], + [ + 276.9, + 267.1, + 268.1, + 255.3, + 249.1, + 247.3, + 247.4, + 249.0, + 248.9, + 249.3, + 249.4, + 250.5, + 250.6, + 247.2, + 243.4, + 239.2, + 236.3, + 236.0, + 243.7, + 256.7, + 256.2, + 249.1, + 261.6, + 264.2, + 268.2, + 271.2, + 270.2, + 272.0, + 273.9, + 273.1, + 266.5, + 262.1, + 260.5, + 256.4, + 251.8, + 250.0, + 252.0, + 257.4, + 252.0, + 254.8, + 270.0, + 275.2, + 278.1, + 280.0, + 281.3, + 282.1, + 282.0, + 275.4, + ], + [ + 274.8, + 263.2, + 258.6, + 246.4, + 242.3, + 240.9, + 240.9, + 240.7, + 238.5, + 238.7, + 239.0, + 239.7, + 238.6, + 235.6, + 233.2, + 230.9, + 229.4, + 231.4, + 234.8, + 238.1, + 238.7, + 241.6, + 244.2, + 247.5, + 257.9, + 264.2, + 254.9, + 256.2, + 256.3, + 254.9, + 255.7, + 253.4, + 251.6, + 249.1, + 245.6, + 245.1, + 247.2, + 248.4, + 248.9, + 257.5, + 270.1, + 259.8, + 265.1, + 275.8, + 277.7, + 274.3, + 278.0, + 278.9, + ], + [ + 273.2, + 268.4, + 256.0, + 247.6, + 250.2, + 244.8, + 239.2, + 234.9, + 234.6, + 230.2, + 229.6, + 230.8, + 231.4, + 229.9, + 228.3, + 227.7, + 228.3, + 230.0, + 230.5, + 231.3, + 231.8, + 235.8, + 238.5, + 239.8, + 242.0, + 246.1, + 248.1, + 244.2, + 246.0, + 247.0, + 246.2, + 243.8, + 242.5, + 241.8, + 242.7, + 241.8, + 241.7, + 242.4, + 245.5, + 254.0, + 263.4, + 250.4, + 244.5, + 248.4, + 255.2, + 261.2, + 267.8, + 270.9, + ], + [ + 268.5, + 269.1, + 268.5, + 265.7, + 262.3, + 243.8, + 236.0, + 236.2, + 235.6, + 235.6, + 234.7, + 229.1, + 228.7, + 227.4, + 228.8, + 233.2, + 233.0, + 235.3, + 235.9, + 236.5, + 236.9, + 237.3, + 237.9, + 238.7, + 239.2, + 239.6, + 241.5, + 239.2, + 239.7, + 240.2, + 241.2, + 240.7, + 240.0, + 240.8, + 242.7, + 240.8, + 239.1, + 245.3, + 251.1, + 258.6, + 247.1, + 239.5, + 237.2, + 235.4, + 241.2, + 246.8, + 247.9, + 258.9, + ], + [ + 259.5, + 258.8, + 258.0, + 253.9, + 243.8, + 239.9, + 238.3, + 237.7, + 235.8, + 233.8, + 233.3, + 233.2, + 234.4, + 232.8, + 234.4, + 236.1, + 236.5, + 237.4, + 237.5, + 237.6, + 237.8, + 236.8, + 236.3, + 236.2, + 236.1, + 235.9, + 235.7, + 235.3, + 234.9, + 234.6, + 235.6, + 236.4, + 236.5, + 236.5, + 237.7, + 239.6, + 235.8, + 232.6, + 234.3, + 238.0, + 236.9, + 235.3, + 233.4, + 235.4, + 238.5, + 241.4, + 243.3, + 252.2, + ], + [ + 235.4, + 235.8, + 236.3, + 235.0, + 234.9, + 236.2, + 235.8, + 235.5, + 236.3, + 236.6, + 235.6, + 235.9, + 236.6, + 236.9, + 237.2, + 237.9, + 238.5, + 238.7, + 238.6, + 238.4, + 238.2, + 238.1, + 237.9, + 237.2, + 236.7, + 236.2, + 235.8, + 235.2, + 234.3, + 233.1, + 232.7, + 233.3, + 233.8, + 233.0, + 232.9, + 233.1, + 234.2, + 233.5, + 233.0, + 234.8, + 234.5, + 234.2, + 234.7, + 236.3, + 237.7, + 238.0, + 236.8, + 236.1, + ], + ], + [ + [ + 242.9, + 242.6, + 241.9, + 242.3, + 240.2, + 240.4, + 240.3, + 241.3, + 240.0, + 239.5, + 242.1, + 241.0, + 243.0, + 243.7, + 244.3, + 243.8, + 244.9, + 246.1, + 247.5, + 247.8, + 248.7, + 249.7, + 250.9, + 253.1, + 254.7, + 256.3, + 257.2, + 259.0, + 258.2, + 257.7, + 256.6, + 255.9, + 254.4, + 251.2, + 250.6, + 252.5, + 254.6, + 254.5, + 255.0, + 254.9, + 253.3, + 253.3, + 251.8, + 249.6, + 248.8, + 247.2, + 244.7, + 244.3, + ], + [ + 243.6, + 240.8, + 239.4, + 237.9, + 237.0, + 237.5, + 239.4, + 239.8, + 241.0, + 240.8, + 240.8, + 241.1, + 240.8, + 241.7, + 245.4, + 246.1, + 246.6, + 249.1, + 250.7, + 250.8, + 254.7, + 260.1, + 265.0, + 266.1, + 265.4, + 265.8, + 265.6, + 266.5, + 266.2, + 263.3, + 262.6, + 261.4, + 261.5, + 258.0, + 256.7, + 256.6, + 260.0, + 262.8, + 264.3, + 265.7, + 265.2, + 264.2, + 265.5, + 263.1, + 257.6, + 252.6, + 246.3, + 245.4, + ], + [ + 248.9, + 245.5, + 246.5, + 244.3, + 242.8, + 243.0, + 243.2, + 244.5, + 249.7, + 252.2, + 249.1, + 244.7, + 242.8, + 244.1, + 245.0, + 245.6, + 245.6, + 245.5, + 247.5, + 249.4, + 252.2, + 257.6, + 266.6, + 269.0, + 270.0, + 271.0, + 270.0, + 270.2, + 271.0, + 269.2, + 266.4, + 266.2, + 266.8, + 267.2, + 268.0, + 265.9, + 266.2, + 268.4, + 268.2, + 267.6, + 271.8, + 271.2, + 272.1, + 271.8, + 267.5, + 261.8, + 255.2, + 252.6, + ], + [ + 270.4, + 269.6, + 269.1, + 267.5, + 266.9, + 264.2, + 259.1, + 256.5, + 262.1, + 266.1, + 263.7, + 256.9, + 255.1, + 253.5, + 255.6, + 256.0, + 256.6, + 254.5, + 253.5, + 261.3, + 264.7, + 268.4, + 271.0, + 271.3, + 271.5, + 273.1, + 273.2, + 273.8, + 272.9, + 274.1, + 272.8, + 273.2, + 273.5, + 274.4, + 274.7, + 274.6, + 275.0, + 275.3, + 271.3, + 267.3, + 271.9, + 271.2, + 271.7, + 272.3, + 271.2, + 270.5, + 271.0, + 270.8, + ], + [ + 274.9, + 273.8, + 273.7, + 273.6, + 274.8, + 275.0, + 272.5, + 273.9, + 274.2, + 273.1, + 272.3, + 270.9, + 272.0, + 273.3, + 272.1, + 272.1, + 271.9, + 273.3, + 271.6, + 272.1, + 272.6, + 273.1, + 273.3, + 275.7, + 275.6, + 274.0, + 275.1, + 274.8, + 274.0, + 275.1, + 275.1, + 275.7, + 274.1, + 276.1, + 275.1, + 275.3, + 276.5, + 275.4, + 275.2, + 274.4, + 273.3, + 274.3, + 274.7, + 274.3, + 274.9, + 273.7, + 274.4, + 274.1, + ], + [ + 275.7, + 275.4, + 275.7, + 275.7, + 275.1, + 276.0, + 275.5, + 276.2, + 276.8, + 276.4, + 274.5, + 274.0, + 275.1, + 274.5, + 273.7, + 274.0, + 274.7, + 274.8, + 275.1, + 275.3, + 275.5, + 277.2, + 277.0, + 276.6, + 277.7, + 277.5, + 276.2, + 277.7, + 275.8, + 277.9, + 277.5, + 276.7, + 277.8, + 276.8, + 277.9, + 277.7, + 278.0, + 278.0, + 279.4, + 278.9, + 278.5, + 278.0, + 278.3, + 277.3, + 276.7, + 276.7, + 277.3, + 276.1, + ], + [ + 278.9, + 277.7, + 279.3, + 277.1, + 278.2, + 277.6, + 278.7, + 277.7, + 277.1, + 278.5, + 279.2, + 279.3, + 280.0, + 279.7, + 278.1, + 278.9, + 278.1, + 279.8, + 279.1, + 279.4, + 279.6, + 280.6, + 282.1, + 280.3, + 279.7, + 280.1, + 281.1, + 281.5, + 281.2, + 280.9, + 281.0, + 281.9, + 281.7, + 281.3, + 279.7, + 281.1, + 280.6, + 279.9, + 281.6, + 283.4, + 281.9, + 281.2, + 279.1, + 278.9, + 279.7, + 279.8, + 280.1, + 277.7, + ], + [ + 283.2, + 283.7, + 283.3, + 281.8, + 280.9, + 282.2, + 281.4, + 282.6, + 282.6, + 283.4, + 284.8, + 284.4, + 283.7, + 283.1, + 283.2, + 283.2, + 283.5, + 283.5, + 284.9, + 284.3, + 284.3, + 285.9, + 285.9, + 286.5, + 287.0, + 286.6, + 285.7, + 285.0, + 286.4, + 285.3, + 286.5, + 284.7, + 285.6, + 284.4, + 284.4, + 284.4, + 283.2, + 282.3, + 282.4, + 287.8, + 285.9, + 285.2, + 284.4, + 282.4, + 283.1, + 284.1, + 284.7, + 283.6, + ], + [ + 289.1, + 288.0, + 288.6, + 289.2, + 288.4, + 290.0, + 289.4, + 290.2, + 289.0, + 290.2, + 288.5, + 288.4, + 288.8, + 288.6, + 288.0, + 288.2, + 287.6, + 288.3, + 289.4, + 290.0, + 292.5, + 290.1, + 290.6, + 291.5, + 290.9, + 291.0, + 290.4, + 289.9, + 290.0, + 290.9, + 289.8, + 289.7, + 289.9, + 289.9, + 289.0, + 288.4, + 286.7, + 286.6, + 287.3, + 294.3, + 294.1, + 291.8, + 289.5, + 288.8, + 289.5, + 290.4, + 290.2, + 289.5, + ], + [ + 292.9, + 292.7, + 294.2, + 292.1, + 295.9, + 295.1, + 294.9, + 295.3, + 294.4, + 293.0, + 294.0, + 292.5, + 291.2, + 291.9, + 291.3, + 293.6, + 291.4, + 292.9, + 298.4, + 294.4, + 294.8, + 296.3, + 294.1, + 295.1, + 294.3, + 293.7, + 293.0, + 295.5, + 294.5, + 295.8, + 294.5, + 294.1, + 295.2, + 294.6, + 292.5, + 291.9, + 290.4, + 290.1, + 293.1, + 301.1, + 300.5, + 296.7, + 294.3, + 295.1, + 294.9, + 295.8, + 293.6, + 292.4, + ], + [ + 292.9, + 293.7, + 294.7, + 292.5, + 296.6, + 299.8, + 297.2, + 297.8, + 297.0, + 294.5, + 293.6, + 294.1, + 295.3, + 294.8, + 296.5, + 299.1, + 297.0, + 298.8, + 301.1, + 298.9, + 297.6, + 298.1, + 298.1, + 296.9, + 296.0, + 297.2, + 296.4, + 297.4, + 298.0, + 297.6, + 296.7, + 296.0, + 297.0, + 296.5, + 293.8, + 294.4, + 293.0, + 290.0, + 291.8, + 297.6, + 300.3, + 296.8, + 297.4, + 296.3, + 295.4, + 295.0, + 294.4, + 294.4, + ], + [ + 294.2, + 296.5, + 295.0, + 294.8, + 298.9, + 301.2, + 298.0, + 297.8, + 297.7, + 297.2, + 296.2, + 296.7, + 295.8, + 297.6, + 298.9, + 301.0, + 301.3, + 299.2, + 299.8, + 298.9, + 299.2, + 298.7, + 299.5, + 299.7, + 299.2, + 299.6, + 298.3, + 298.5, + 299.4, + 298.3, + 297.4, + 297.2, + 294.1, + 294.2, + 293.2, + 293.0, + 291.2, + 293.2, + 289.3, + 293.7, + 300.1, + 296.6, + 297.9, + 298.4, + 296.4, + 295.5, + 293.9, + 293.7, + ], + [ + 297.7, + 297.4, + 294.5, + 294.2, + 295.9, + 300.2, + 300.0, + 299.2, + 298.5, + 298.0, + 298.8, + 300.5, + 300.2, + 300.3, + 300.8, + 302.5, + 301.6, + 300.5, + 300.4, + 301.9, + 302.1, + 300.4, + 300.7, + 301.3, + 300.3, + 301.6, + 300.2, + 301.3, + 300.6, + 298.6, + 298.8, + 297.1, + 295.0, + 293.9, + 292.3, + 293.1, + 294.7, + 293.0, + 289.4, + 296.3, + 298.8, + 296.1, + 295.2, + 297.8, + 297.1, + 296.7, + 296.4, + 296.2, + ], + [ + 300.9, + 300.6, + 295.8, + 294.8, + 294.3, + 298.8, + 300.8, + 299.5, + 299.8, + 300.7, + 299.8, + 301.8, + 300.1, + 302.0, + 301.2, + 301.7, + 301.6, + 303.0, + 301.8, + 301.0, + 302.5, + 301.1, + 301.4, + 300.5, + 302.0, + 300.5, + 300.9, + 300.5, + 300.0, + 300.5, + 298.4, + 297.2, + 295.7, + 296.2, + 294.8, + 295.3, + 298.1, + 295.8, + 298.0, + 299.8, + 298.9, + 298.9, + 296.7, + 298.8, + 298.5, + 299.0, + 298.4, + 299.5, + ], + [ + 300.4, + 298.9, + 298.5, + 296.9, + 294.5, + 299.1, + 300.3, + 300.9, + 299.8, + 299.7, + 301.0, + 301.5, + 301.9, + 300.9, + 302.0, + 301.2, + 302.0, + 302.0, + 300.1, + 300.1, + 300.2, + 299.8, + 301.2, + 300.2, + 299.4, + 299.9, + 299.0, + 298.7, + 297.6, + 298.2, + 298.4, + 298.0, + 297.5, + 296.6, + 297.5, + 297.6, + 300.0, + 297.6, + 301.7, + 299.9, + 300.8, + 300.3, + 300.6, + 300.6, + 299.8, + 300.4, + 300.1, + 301.7, + ], + [ + 300.8, + 297.9, + 298.4, + 296.2, + 296.4, + 298.5, + 299.0, + 299.9, + 299.2, + 300.2, + 300.1, + 301.3, + 301.3, + 299.8, + 300.0, + 299.7, + 301.7, + 301.9, + 301.5, + 302.0, + 299.9, + 300.8, + 300.8, + 299.0, + 300.0, + 300.0, + 299.1, + 299.4, + 298.5, + 298.9, + 297.4, + 298.9, + 298.3, + 298.2, + 298.7, + 298.7, + 299.5, + 297.3, + 298.7, + 299.8, + 300.7, + 299.8, + 300.5, + 300.8, + 300.7, + 301.3, + 301.1, + 300.3, + ], + [ + 296.6, + 295.8, + 297.6, + 298.2, + 298.2, + 290.9, + 294.3, + 297.5, + 297.9, + 298.7, + 297.3, + 300.1, + 300.3, + 298.3, + 299.8, + 301.3, + 299.0, + 300.6, + 301.3, + 300.6, + 301.1, + 299.9, + 299.2, + 300.3, + 299.1, + 298.7, + 299.0, + 299.7, + 299.8, + 298.2, + 299.5, + 300.3, + 299.6, + 299.6, + 299.9, + 298.9, + 300.0, + 297.6, + 297.8, + 299.6, + 299.2, + 298.7, + 298.9, + 298.2, + 299.4, + 300.4, + 298.8, + 297.0, + ], + [ + 296.9, + 296.6, + 296.4, + 295.2, + 297.7, + 292.7, + 289.9, + 296.6, + 295.7, + 297.8, + 294.4, + 298.8, + 297.4, + 294.0, + 296.1, + 298.2, + 297.8, + 299.4, + 298.7, + 298.4, + 299.4, + 298.7, + 296.9, + 296.5, + 295.9, + 295.3, + 296.4, + 296.5, + 296.9, + 295.6, + 297.0, + 296.2, + 296.8, + 297.9, + 295.1, + 294.6, + 295.8, + 298.3, + 298.7, + 297.9, + 297.6, + 297.2, + 297.4, + 297.9, + 296.4, + 296.4, + 298.9, + 296.7, + ], + [ + 291.3, + 290.2, + 288.0, + 289.4, + 292.3, + 290.7, + 288.5, + 290.8, + 294.6, + 292.4, + 289.6, + 291.6, + 289.7, + 284.3, + 288.0, + 294.4, + 293.9, + 294.1, + 293.4, + 295.5, + 295.5, + 296.2, + 295.2, + 294.9, + 293.9, + 294.2, + 292.7, + 292.6, + 293.6, + 291.7, + 292.2, + 293.6, + 292.9, + 294.3, + 285.4, + 293.1, + 295.9, + 295.9, + 296.1, + 297.0, + 297.1, + 297.1, + 295.0, + 296.2, + 296.1, + 294.4, + 292.3, + 292.1, + ], + [ + 287.1, + 286.7, + 286.8, + 287.3, + 288.0, + 285.4, + 288.1, + 284.9, + 285.0, + 283.9, + 283.1, + 276.6, + 274.9, + 275.4, + 276.8, + 278.7, + 285.8, + 289.5, + 288.4, + 289.9, + 290.2, + 292.1, + 291.7, + 293.1, + 291.6, + 290.8, + 291.0, + 290.7, + 289.5, + 290.8, + 290.5, + 289.8, + 289.7, + 284.3, + 280.6, + 285.4, + 287.7, + 290.5, + 292.7, + 293.9, + 294.3, + 294.3, + 293.7, + 293.2, + 294.4, + 292.7, + 289.9, + 287.8, + ], + [ + 280.2, + 285.6, + 288.6, + 289.1, + 287.3, + 280.4, + 277.4, + 276.9, + 274.4, + 273.7, + 259.0, + 252.1, + 252.8, + 264.0, + 272.6, + 274.0, + 278.0, + 279.8, + 278.2, + 283.8, + 286.2, + 286.4, + 287.5, + 288.6, + 287.6, + 286.8, + 287.2, + 287.6, + 288.1, + 287.8, + 288.5, + 286.2, + 280.6, + 273.6, + 274.5, + 274.8, + 273.3, + 280.8, + 288.7, + 290.5, + 289.6, + 291.6, + 291.4, + 291.5, + 292.3, + 291.7, + 290.0, + 281.8, + ], + [ + 283.8, + 283.9, + 282.7, + 279.1, + 274.5, + 270.1, + 276.5, + 276.1, + 273.0, + 264.4, + 261.2, + 262.8, + 258.6, + 260.2, + 262.5, + 264.3, + 257.8, + 262.8, + 270.9, + 273.5, + 279.0, + 280.7, + 280.7, + 282.3, + 282.4, + 282.9, + 281.4, + 284.0, + 282.9, + 284.8, + 284.4, + 280.3, + 267.5, + 264.5, + 267.8, + 265.6, + 266.5, + 269.4, + 279.7, + 284.7, + 285.4, + 286.4, + 286.7, + 288.1, + 289.2, + 288.4, + 284.5, + 278.6, + ], + [ + 274.9, + 269.1, + 266.5, + 266.7, + 275.0, + 272.5, + 272.6, + 267.4, + 265.5, + 263.8, + 259.4, + 256.3, + 253.8, + 253.2, + 255.1, + 251.7, + 247.0, + 248.7, + 261.2, + 268.9, + 272.9, + 274.4, + 275.9, + 275.2, + 276.7, + 277.9, + 278.2, + 279.5, + 280.6, + 280.5, + 281.7, + 276.6, + 265.7, + 261.9, + 261.0, + 260.2, + 265.8, + 262.9, + 264.8, + 274.3, + 276.4, + 278.3, + 280.9, + 283.7, + 286.2, + 285.0, + 284.4, + 278.7, + ], + [ + 272.0, + 266.1, + 263.0, + 260.3, + 258.5, + 258.8, + 258.1, + 257.8, + 256.0, + 256.2, + 257.7, + 255.5, + 252.9, + 251.7, + 250.8, + 243.5, + 241.2, + 243.1, + 250.8, + 268.8, + 266.8, + 267.2, + 271.3, + 272.6, + 273.9, + 273.7, + 275.7, + 275.9, + 277.4, + 280.2, + 279.5, + 268.3, + 262.4, + 258.0, + 255.4, + 255.9, + 257.5, + 259.5, + 259.1, + 259.6, + 269.1, + 275.4, + 278.9, + 279.8, + 282.9, + 283.3, + 282.8, + 275.7, + ], + [ + 277.0, + 267.4, + 267.9, + 255.7, + 248.9, + 247.9, + 248.2, + 249.4, + 249.1, + 249.9, + 248.7, + 249.5, + 250.6, + 247.9, + 244.2, + 238.8, + 236.1, + 236.8, + 244.3, + 257.5, + 255.3, + 248.7, + 262.3, + 265.0, + 268.4, + 271.1, + 270.5, + 272.9, + 274.2, + 273.9, + 266.9, + 261.4, + 260.6, + 257.2, + 252.1, + 249.6, + 251.7, + 256.5, + 251.5, + 254.6, + 269.6, + 274.4, + 279.0, + 279.3, + 281.8, + 281.5, + 282.5, + 274.8, + ], + [ + 274.1, + 262.4, + 258.9, + 246.6, + 242.9, + 240.3, + 240.1, + 240.9, + 239.4, + 239.1, + 239.1, + 239.6, + 237.9, + 236.6, + 233.5, + 230.9, + 228.4, + 231.3, + 235.3, + 238.9, + 238.5, + 242.5, + 244.2, + 247.1, + 257.4, + 264.3, + 255.1, + 257.1, + 256.5, + 255.2, + 256.2, + 253.6, + 251.6, + 248.7, + 245.2, + 246.0, + 247.4, + 248.1, + 249.6, + 257.7, + 269.3, + 259.9, + 265.2, + 274.8, + 277.1, + 273.9, + 277.3, + 279.2, + ], + [ + 272.7, + 268.2, + 256.6, + 248.0, + 249.9, + 245.1, + 240.2, + 234.0, + 234.0, + 231.0, + 229.3, + 231.7, + 231.6, + 229.1, + 228.7, + 228.7, + 228.6, + 230.4, + 231.1, + 231.5, + 232.6, + 236.6, + 238.2, + 240.7, + 241.6, + 245.2, + 247.1, + 244.1, + 246.0, + 248.0, + 245.9, + 242.9, + 242.4, + 241.6, + 242.4, + 241.2, + 242.2, + 241.6, + 245.7, + 254.9, + 263.2, + 250.5, + 243.6, + 248.7, + 254.6, + 261.3, + 267.8, + 271.1, + ], + [ + 267.5, + 269.3, + 267.7, + 265.7, + 262.4, + 242.9, + 236.2, + 235.2, + 235.7, + 235.3, + 233.8, + 228.7, + 229.6, + 227.4, + 229.6, + 232.4, + 233.1, + 236.1, + 236.5, + 237.0, + 236.0, + 238.1, + 238.4, + 239.0, + 239.5, + 239.2, + 242.1, + 238.3, + 238.9, + 239.6, + 241.2, + 240.3, + 239.0, + 240.0, + 242.4, + 239.9, + 238.9, + 245.3, + 251.6, + 259.2, + 247.7, + 238.6, + 236.8, + 236.2, + 240.5, + 246.3, + 248.2, + 259.4, + ], + [ + 258.7, + 258.8, + 258.6, + 254.5, + 243.2, + 240.2, + 239.2, + 238.7, + 235.0, + 233.4, + 233.7, + 233.9, + 235.1, + 233.6, + 234.7, + 236.3, + 235.6, + 237.5, + 237.8, + 236.8, + 237.7, + 236.3, + 235.8, + 237.0, + 235.2, + 234.9, + 235.7, + 235.3, + 235.5, + 235.0, + 235.9, + 236.9, + 236.8, + 237.2, + 238.5, + 238.8, + 236.7, + 232.0, + 235.2, + 237.8, + 237.1, + 236.2, + 233.4, + 235.0, + 239.1, + 240.4, + 242.8, + 253.1, + ], + [ + 235.7, + 235.5, + 235.4, + 235.1, + 234.4, + 236.7, + 235.6, + 234.8, + 236.0, + 236.9, + 235.7, + 235.4, + 237.2, + 237.8, + 237.3, + 237.0, + 237.5, + 239.1, + 239.4, + 238.7, + 237.5, + 238.9, + 238.8, + 237.6, + 235.8, + 236.8, + 236.4, + 235.0, + 234.6, + 233.8, + 233.6, + 233.5, + 233.2, + 233.9, + 233.4, + 233.1, + 234.8, + 234.0, + 233.3, + 234.8, + 235.3, + 234.6, + 233.7, + 235.7, + 238.6, + 239.0, + 237.2, + 236.8, + ], + ], + ] + + dst = n.createVariable("dst", "f4", ("time_1", "lat", "lon")) + dst.standard_name = "air_temperature" + dst.units = "K" + dst.cell_methods = "time_1: mean" + + # Don't generate this data randomly - it's useful to see the real + # patterns of global temperature. + dst[...] = [ + [ + [ + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + 246.0, + ], + [ + 243.6, + 243.3, + 243.0, + 242.7, + 242.5, + 242.5, + 242.5, + 242.4, + 242.5, + 242.5, + 242.4, + 242.2, + 241.9, + 241.7, + 241.5, + 241.4, + 241.4, + 241.4, + 241.4, + 241.4, + 241.5, + 241.7, + 241.8, + 241.9, + 242.1, + 242.1, + 241.9, + 241.9, + 241.8, + 242.0, + 242.2, + 242.5, + 243.2, + 243.7, + 244.1, + 244.5, + 244.8, + 244.8, + 244.7, + 244.8, + 244.9, + 245.1, + 245.7, + 246.3, + 247.6, + 248.1, + 248.3, + 247.9, + 247.2, + 246.8, + 246.9, + 247.3, + 247.9, + 248.2, + 248.5, + 249.1, + 249.6, + 249.8, + 250.1, + 250.5, + 250.8, + 250.7, + 250.8, + 250.3, + 250.3, + 250.2, + 249.8, + 249.6, + 249.8, + 250.3, + 250.7, + 251.2, + 252.0, + 252.7, + 252.5, + 251.5, + 250.5, + 249.8, + 248.6, + 248.0, + 247.9, + 248.2, + 248.3, + 248.3, + 248.2, + 247.8, + 247.5, + 247.0, + 246.5, + 246.4, + 246.0, + 245.4, + 244.9, + 244.4, + 244.0, + 243.8, + ], + [ + 244.1, + 243.6, + 242.8, + 241.9, + 241.3, + 241.0, + 240.8, + 240.8, + 240.5, + 240.2, + 239.8, + 239.5, + 239.4, + 239.5, + 239.5, + 239.3, + 239.1, + 239.0, + 239.1, + 239.5, + 240.1, + 240.7, + 241.2, + 241.7, + 242.0, + 242.5, + 243.1, + 243.5, + 243.6, + 243.9, + 244.3, + 244.8, + 245.3, + 246.0, + 246.8, + 247.5, + 248.0, + 248.4, + 248.7, + 248.9, + 249.3, + 250.2, + 251.5, + 252.8, + 253.6, + 254.2, + 254.3, + 255.1, + 256.9, + 258.4, + 259.8, + 261.2, + 262.7, + 264.0, + 264.8, + 265.3, + 265.1, + 264.4, + 263.6, + 262.6, + 261.6, + 261.1, + 260.4, + 259.4, + 258.2, + 257.2, + 255.3, + 253.5, + 252.7, + 252.5, + 253.0, + 253.5, + 254.1, + 255.2, + 257.1, + 258.0, + 258.3, + 258.3, + 258.7, + 258.5, + 257.9, + 257.0, + 256.0, + 255.5, + 255.0, + 254.2, + 252.9, + 251.7, + 250.8, + 249.6, + 248.3, + 246.9, + 245.7, + 245.0, + 244.5, + 244.3, + ], + [ + 244.9, + 243.6, + 242.0, + 240.5, + 239.4, + 238.6, + 238.1, + 237.5, + 237.2, + 237.1, + 236.9, + 236.8, + 237.1, + 237.6, + 237.9, + 237.9, + 237.9, + 237.8, + 237.6, + 237.9, + 238.6, + 239.3, + 239.9, + 240.2, + 241.1, + 242.4, + 243.5, + 244.0, + 244.7, + 245.4, + 245.9, + 246.1, + 246.5, + 247.1, + 247.9, + 248.8, + 249.8, + 250.5, + 250.9, + 251.4, + 252.2, + 253.6, + 256.0, + 259.4, + 262.9, + 265.9, + 267.3, + 267.7, + 267.6, + 267.4, + 267.0, + 266.6, + 266.5, + 266.6, + 266.5, + 265.9, + 265.3, + 265.1, + 265.0, + 264.8, + 264.8, + 264.8, + 264.7, + 264.5, + 263.5, + 262.0, + 259.9, + 257.4, + 255.7, + 255.3, + 255.3, + 255.7, + 256.5, + 257.5, + 258.9, + 260.6, + 261.8, + 262.8, + 263.3, + 263.8, + 264.2, + 264.0, + 263.1, + 262.2, + 262.4, + 262.4, + 261.2, + 259.3, + 257.5, + 255.4, + 253.0, + 250.5, + 248.2, + 246.6, + 245.9, + 245.6, + ], + [ + 244.3, + 242.9, + 241.7, + 241.1, + 240.7, + 240.2, + 239.1, + 238.1, + 237.5, + 237.1, + 236.8, + 237.0, + 237.9, + 239.2, + 240.3, + 241.1, + 241.9, + 242.5, + 242.5, + 241.8, + 241.4, + 241.2, + 240.8, + 240.2, + 239.5, + 239.5, + 240.4, + 241.8, + 243.1, + 244.2, + 245.0, + 245.5, + 245.8, + 246.5, + 247.3, + 248.4, + 249.5, + 250.3, + 250.7, + 251.4, + 253.3, + 255.7, + 259.1, + 263.3, + 265.9, + 266.1, + 266.4, + 266.0, + 265.7, + 265.5, + 265.5, + 265.6, + 265.8, + 266.2, + 266.3, + 265.9, + 265.5, + 265.3, + 264.6, + 264.1, + 263.4, + 262.4, + 261.5, + 261.2, + 261.0, + 260.8, + 260.0, + 258.8, + 257.4, + 256.8, + 256.8, + 257.4, + 258.7, + 260.1, + 261.9, + 263.6, + 265.0, + 265.2, + 264.8, + 264.8, + 265.5, + 265.2, + 264.3, + 262.9, + 263.7, + 265.2, + 266.0, + 265.8, + 263.1, + 260.8, + 256.8, + 252.4, + 249.0, + 247.0, + 245.9, + 245.1, + ], + [ + 245.2, + 243.7, + 242.2, + 241.3, + 241.5, + 241.7, + 241.3, + 240.1, + 238.9, + 238.5, + 238.6, + 239.0, + 239.9, + 241.3, + 242.4, + 243.3, + 245.4, + 246.5, + 246.4, + 246.4, + 246.4, + 245.9, + 245.2, + 243.8, + 242.2, + 241.2, + 241.2, + 241.7, + 242.5, + 243.3, + 243.8, + 244.2, + 244.4, + 244.5, + 245.4, + 246.5, + 247.4, + 247.6, + 247.6, + 248.0, + 248.9, + 250.8, + 254.1, + 259.1, + 262.6, + 265.6, + 265.6, + 265.2, + 264.8, + 264.8, + 265.4, + 266.1, + 266.6, + 267.2, + 267.2, + 267.2, + 266.8, + 266.7, + 265.6, + 263.3, + 261.6, + 259.5, + 258.2, + 258.3, + 259.4, + 260.8, + 261.7, + 261.7, + 261.5, + 260.9, + 260.2, + 260.3, + 261.3, + 262.4, + 263.9, + 266.1, + 267.0, + 267.7, + 267.5, + 267.2, + 271.5, + 271.5, + 271.6, + 271.7, + 271.6, + 271.5, + 271.6, + 271.4, + 263.7, + 260.6, + 256.3, + 252.6, + 249.8, + 248.1, + 247.1, + 246.2, + ], + [ + 248.4, + 246.8, + 245.6, + 244.3, + 244.1, + 244.4, + 244.0, + 242.7, + 241.0, + 240.4, + 240.9, + 242.0, + 243.3, + 243.7, + 243.4, + 243.9, + 245.3, + 248.8, + 250.6, + 250.9, + 250.5, + 248.5, + 246.7, + 245.5, + 244.2, + 243.3, + 243.6, + 244.7, + 245.7, + 245.8, + 245.3, + 244.4, + 243.7, + 243.7, + 244.8, + 246.1, + 246.7, + 246.4, + 246.4, + 247.1, + 249.2, + 251.9, + 255.3, + 259.6, + 261.3, + 270.2, + 270.4, + 271.0, + 271.1, + 271.2, + 271.4, + 271.5, + 271.5, + 271.5, + 271.5, + 271.4, + 271.3, + 271.7, + 272.0, + 272.1, + 267.4, + 266.9, + 266.4, + 266.3, + 266.4, + 266.6, + 267.3, + 268.2, + 268.5, + 268.2, + 266.9, + 265.2, + 264.3, + 264.3, + 265.8, + 267.1, + 267.0, + 266.0, + 265.7, + 266.6, + 271.6, + 271.6, + 271.5, + 271.5, + 271.4, + 271.5, + 271.6, + 271.5, + 271.3, + 271.4, + 262.7, + 259.6, + 254.4, + 252.3, + 251.2, + 249.6, + ], + [ + 257.5, + 254.5, + 252.1, + 249.8, + 249.2, + 250.4, + 251.5, + 250.9, + 250.6, + 250.5, + 249.1, + 248.0, + 247.6, + 247.1, + 246.9, + 247.4, + 249.1, + 253.6, + 256.8, + 257.8, + 255.1, + 251.7, + 247.6, + 245.1, + 244.0, + 243.2, + 243.2, + 244.8, + 246.9, + 247.9, + 247.8, + 247.1, + 246.2, + 245.6, + 245.4, + 245.6, + 246.3, + 246.7, + 248.1, + 250.9, + 253.0, + 253.8, + 254.5, + 254.6, + 256.0, + 270.2, + 270.8, + 271.3, + 271.2, + 271.3, + 271.4, + 271.4, + 271.3, + 271.8, + 272.6, + 272.6, + 271.1, + 271.1, + 271.3, + 271.6, + 271.7, + 271.7, + 271.6, + 271.5, + 271.5, + 271.5, + 271.4, + 271.4, + 271.2, + 271.6, + 271.7, + 271.6, + 271.5, + 272.3, + 272.1, + 271.5, + 271.8, + 268.5, + 265.9, + 264.9, + 271.5, + 271.3, + 271.3, + 271.4, + 271.5, + 271.5, + 271.5, + 271.5, + 271.3, + 271.2, + 271.1, + 271.2, + 271.3, + 263.1, + 261.2, + 259.6, + ], + [ + 271.3, + 271.4, + 271.5, + 271.6, + 271.6, + 271.5, + 271.3, + 271.3, + 264.0, + 264.2, + 263.1, + 259.6, + 256.1, + 254.7, + 252.4, + 250.5, + 251.9, + 254.3, + 259.4, + 262.6, + 263.1, + 258.8, + 255.1, + 253.2, + 252.1, + 251.8, + 251.8, + 252.1, + 252.9, + 253.4, + 253.9, + 254.0, + 253.7, + 252.8, + 251.7, + 250.5, + 249.8, + 250.2, + 252.7, + 254.8, + 256.8, + 258.2, + 259.7, + 270.3, + 270.2, + 270.5, + 271.2, + 271.1, + 271.3, + 271.5, + 271.6, + 271.6, + 272.2, + 273.2, + 274.1, + 274.1, + 273.8, + 273.9, + 274.0, + 273.9, + 273.4, + 272.8, + 272.5, + 272.7, + 273.6, + 274.0, + 273.8, + 273.6, + 273.7, + 273.8, + 273.2, + 272.8, + 273.5, + 274.4, + 275.4, + 275.3, + 272.6, + 268.4, + 265.9, + 265.2, + 271.4, + 271.3, + 271.0, + 271.2, + 271.5, + 271.5, + 271.3, + 271.2, + 271.2, + 271.2, + 271.2, + 271.3, + 271.4, + 271.4, + 271.3, + 271.3, + ], + [ + 272.8, + 273.1, + 273.0, + 272.9, + 272.8, + 272.5, + 271.3, + 271.2, + 271.1, + 271.2, + 271.1, + 271.2, + 271.0, + 263.1, + 260.1, + 260.2, + 270.2, + 269.5, + 269.5, + 269.8, + 270.1, + 269.9, + 270.9, + 260.1, + 259.0, + 257.8, + 256.3, + 255.8, + 256.3, + 258.0, + 259.9, + 261.0, + 260.8, + 259.9, + 258.9, + 257.2, + 255.9, + 257.6, + 261.3, + 270.5, + 270.7, + 270.4, + 270.6, + 271.1, + 271.4, + 271.2, + 271.3, + 272.4, + 272.8, + 273.2, + 273.4, + 273.4, + 273.6, + 274.1, + 274.2, + 274.2, + 274.2, + 274.2, + 274.0, + 273.9, + 273.9, + 273.9, + 274.0, + 274.1, + 274.4, + 274.6, + 274.5, + 274.4, + 274.3, + 274.4, + 274.6, + 274.5, + 274.5, + 274.8, + 275.1, + 275.2, + 275.1, + 274.6, + 268.9, + 267.9, + 271.2, + 271.1, + 271.2, + 272.2, + 271.4, + 271.1, + 271.0, + 271.0, + 271.6, + 272.2, + 271.4, + 271.7, + 271.3, + 271.7, + 271.6, + 272.3, + ], + [ + 274.2, + 274.2, + 274.1, + 274.3, + 274.3, + 274.3, + 274.3, + 274.5, + 274.1, + 274.5, + 274.7, + 274.5, + 271.5, + 271.2, + 271.1, + 270.9, + 270.8, + 270.7, + 270.8, + 270.9, + 271.0, + 271.1, + 271.0, + 270.8, + 270.8, + 270.8, + 270.9, + 270.9, + 271.0, + 271.1, + 271.0, + 271.0, + 271.1, + 271.1, + 271.1, + 271.1, + 271.0, + 271.0, + 270.9, + 270.9, + 271.1, + 271.1, + 271.2, + 271.3, + 271.4, + 272.1, + 273.3, + 273.9, + 274.1, + 274.3, + 274.6, + 274.5, + 274.5, + 274.5, + 274.4, + 274.3, + 274.2, + 274.2, + 274.1, + 274.0, + 273.9, + 274.1, + 274.3, + 274.4, + 274.4, + 274.6, + 274.6, + 274.7, + 274.8, + 274.9, + 275.1, + 275.4, + 275.4, + 275.4, + 275.6, + 275.6, + 275.4, + 275.2, + 275.1, + 269.6, + 271.3, + 271.3, + 272.3, + 272.8, + 272.9, + 273.0, + 273.2, + 273.4, + 273.5, + 273.8, + 273.8, + 274.0, + 274.0, + 274.1, + 274.0, + 274.0, + ], + [ + 274.7, + 274.6, + 274.4, + 274.4, + 274.5, + 274.4, + 274.3, + 274.4, + 274.4, + 274.5, + 274.8, + 275.0, + 274.8, + 274.4, + 274.0, + 273.8, + 274.0, + 274.5, + 274.5, + 274.4, + 274.3, + 274.3, + 272.9, + 271.3, + 271.2, + 271.3, + 273.0, + 272.9, + 273.0, + 273.6, + 273.6, + 273.4, + 272.9, + 272.7, + 272.9, + 272.8, + 271.8, + 271.6, + 271.6, + 272.3, + 274.1, + 274.6, + 274.6, + 274.5, + 274.8, + 274.7, + 274.9, + 275.2, + 275.1, + 275.0, + 275.0, + 275.0, + 275.1, + 275.2, + 275.2, + 275.2, + 275.2, + 275.0, + 274.9, + 274.8, + 274.9, + 274.9, + 274.9, + 275.0, + 275.0, + 275.1, + 275.1, + 275.2, + 275.4, + 275.5, + 275.8, + 276.0, + 275.9, + 276.1, + 276.4, + 276.5, + 276.4, + 276.1, + 275.5, + 273.0, + 271.6, + 272.0, + 273.1, + 273.8, + 274.5, + 274.8, + 274.9, + 274.9, + 274.3, + 274.8, + 274.9, + 274.8, + 274.7, + 274.8, + 274.8, + 274.8, + ], + [ + 275.0, + 274.9, + 274.6, + 274.3, + 274.3, + 274.4, + 274.2, + 274.2, + 274.3, + 274.5, + 274.6, + 274.7, + 274.8, + 274.9, + 275.0, + 275.1, + 275.1, + 275.1, + 275.1, + 275.0, + 274.6, + 274.3, + 274.0, + 271.7, + 272.1, + 272.6, + 273.2, + 274.1, + 274.3, + 274.4, + 274.6, + 274.7, + 274.7, + 274.8, + 274.9, + 274.6, + 274.7, + 274.9, + 274.9, + 275.0, + 275.2, + 275.4, + 275.5, + 275.4, + 275.5, + 275.4, + 275.7, + 275.7, + 275.6, + 275.6, + 275.6, + 275.6, + 275.7, + 275.7, + 275.6, + 275.6, + 275.6, + 275.6, + 275.9, + 275.7, + 275.5, + 275.6, + 275.7, + 275.8, + 275.8, + 275.8, + 275.9, + 276.0, + 276.0, + 276.1, + 276.3, + 276.4, + 276.4, + 276.5, + 276.6, + 276.8, + 276.9, + 277.2, + 277.2, + 277.0, + 276.4, + 275.6, + 275.8, + 276.0, + 276.3, + 276.2, + 276.1, + 276.3, + 276.2, + 276.2, + 276.1, + 275.7, + 275.5, + 275.5, + 275.3, + 275.0, + ], + [ + 275.2, + 275.1, + 274.9, + 274.9, + 275.2, + 275.3, + 275.2, + 275.0, + 275.0, + 275.2, + 275.5, + 275.6, + 275.5, + 275.5, + 275.5, + 275.6, + 275.8, + 275.9, + 275.8, + 275.3, + 274.9, + 274.5, + 274.6, + 274.2, + 274.3, + 273.7, + 273.7, + 273.7, + 273.9, + 274.0, + 274.4, + 274.5, + 274.6, + 274.9, + 275.0, + 275.0, + 275.0, + 275.1, + 275.1, + 275.4, + 275.6, + 275.9, + 276.3, + 276.6, + 277.0, + 276.5, + 276.5, + 276.5, + 276.5, + 276.6, + 276.6, + 276.7, + 277.0, + 276.7, + 276.4, + 276.3, + 276.1, + 276.2, + 277.1, + 276.7, + 276.5, + 276.4, + 276.8, + 277.0, + 276.9, + 276.9, + 277.0, + 277.0, + 276.9, + 276.9, + 277.0, + 276.9, + 276.9, + 277.1, + 277.2, + 277.7, + 277.9, + 278.7, + 279.2, + 279.0, + 278.1, + 278.2, + 278.1, + 278.3, + 278.6, + 278.0, + 277.3, + 277.0, + 277.1, + 277.1, + 277.0, + 276.7, + 276.4, + 276.3, + 276.1, + 275.6, + ], + [ + 276.0, + 275.8, + 275.7, + 275.8, + 276.1, + 276.2, + 276.2, + 276.1, + 275.9, + 276.0, + 276.3, + 276.4, + 276.3, + 276.4, + 276.3, + 276.3, + 276.5, + 276.5, + 276.3, + 276.0, + 275.7, + 275.1, + 275.7, + 275.6, + 275.7, + 275.7, + 276.0, + 275.6, + 276.0, + 275.4, + 275.8, + 275.7, + 275.6, + 275.6, + 275.9, + 275.9, + 276.2, + 276.5, + 276.2, + 276.1, + 276.3, + 276.1, + 277.0, + 277.3, + 278.2, + 278.2, + 277.7, + 277.5, + 277.5, + 277.6, + 277.5, + 277.8, + 278.2, + 278.2, + 278.1, + 278.0, + 277.6, + 277.2, + 278.1, + 278.1, + 278.0, + 278.0, + 278.8, + 278.7, + 278.4, + 278.3, + 278.2, + 278.2, + 278.1, + 278.0, + 278.1, + 278.1, + 278.3, + 278.4, + 278.5, + 278.9, + 279.5, + 280.2, + 279.7, + 281.3, + 280.2, + 279.7, + 279.5, + 279.3, + 279.1, + 278.3, + 277.8, + 277.9, + 278.3, + 278.3, + 278.2, + 277.7, + 277.4, + 277.2, + 276.7, + 276.3, + ], + [ + 277.2, + 277.2, + 277.4, + 277.5, + 277.5, + 277.5, + 277.4, + 277.2, + 276.9, + 276.9, + 277.1, + 277.2, + 277.4, + 277.5, + 277.5, + 277.4, + 277.4, + 277.2, + 277.2, + 277.0, + 276.8, + 276.2, + 277.4, + 277.5, + 277.4, + 277.2, + 277.5, + 277.7, + 277.9, + 277.6, + 278.0, + 277.4, + 277.5, + 277.4, + 277.6, + 277.9, + 278.4, + 278.3, + 278.2, + 278.6, + 279.2, + 278.8, + 279.5, + 279.4, + 280.0, + 280.7, + 280.1, + 278.8, + 278.6, + 278.9, + 278.8, + 279.0, + 279.6, + 280.1, + 280.0, + 279.8, + 279.7, + 279.4, + 279.7, + 280.1, + 280.3, + 280.5, + 280.7, + 280.3, + 280.1, + 280.1, + 279.9, + 279.8, + 279.5, + 279.3, + 279.3, + 279.3, + 279.4, + 279.5, + 279.7, + 280.1, + 280.4, + 279.8, + 282.0, + 281.8, + 281.2, + 281.1, + 280.4, + 279.5, + 279.1, + 278.7, + 278.6, + 278.7, + 279.0, + 279.1, + 279.5, + 279.0, + 278.5, + 278.0, + 277.2, + 277.3, + ], + [ + 279.0, + 279.3, + 279.5, + 279.4, + 279.1, + 278.8, + 278.6, + 278.3, + 278.3, + 278.2, + 278.2, + 278.3, + 278.6, + 278.8, + 278.6, + 278.5, + 278.4, + 278.3, + 278.7, + 278.3, + 278.5, + 280.1, + 281.1, + 280.7, + 280.3, + 280.1, + 280.0, + 280.2, + 280.1, + 279.9, + 279.7, + 279.5, + 279.9, + 279.8, + 280.0, + 280.9, + 280.9, + 280.8, + 280.9, + 281.1, + 281.1, + 280.8, + 281.1, + 281.1, + 281.6, + 281.8, + 281.0, + 280.1, + 280.2, + 280.5, + 281.0, + 281.1, + 281.4, + 282.2, + 281.8, + 281.6, + 281.5, + 281.4, + 281.6, + 282.2, + 282.3, + 282.4, + 282.1, + 281.9, + 281.9, + 281.8, + 281.4, + 281.2, + 280.9, + 280.7, + 280.6, + 280.5, + 280.6, + 280.7, + 280.9, + 281.0, + 281.1, + 279.2, + 284.1, + 283.7, + 282.6, + 281.6, + 280.6, + 280.3, + 280.1, + 279.7, + 279.5, + 279.5, + 279.9, + 279.9, + 280.4, + 280.3, + 280.0, + 279.7, + 279.0, + 279.3, + ], + [ + 281.7, + 281.7, + 281.4, + 281.2, + 280.9, + 280.5, + 280.2, + 279.8, + 280.0, + 280.0, + 279.8, + 280.0, + 280.2, + 280.3, + 280.0, + 279.9, + 279.9, + 279.8, + 280.4, + 280.0, + 281.9, + 282.4, + 282.6, + 282.2, + 282.3, + 282.2, + 281.9, + 281.9, + 281.7, + 281.5, + 281.4, + 281.6, + 282.0, + 282.0, + 282.3, + 282.7, + 282.4, + 282.5, + 282.5, + 282.5, + 282.7, + 282.6, + 282.7, + 282.8, + 283.2, + 283.1, + 282.7, + 282.0, + 282.5, + 283.3, + 283.9, + 283.5, + 283.7, + 284.1, + 283.9, + 283.6, + 283.4, + 283.4, + 283.6, + 283.9, + 283.9, + 283.8, + 283.6, + 283.6, + 283.6, + 283.4, + 283.1, + 282.8, + 282.6, + 282.3, + 282.2, + 281.9, + 281.8, + 281.9, + 281.9, + 281.9, + 281.9, + 277.8, + 287.9, + 285.9, + 283.8, + 282.2, + 282.2, + 282.4, + 282.3, + 281.9, + 281.1, + 281.0, + 281.1, + 281.0, + 281.5, + 282.3, + 282.3, + 282.3, + 282.0, + 282.0, + ], + [ + 284.2, + 283.9, + 283.5, + 283.1, + 282.7, + 282.4, + 282.0, + 281.6, + 281.8, + 281.5, + 281.5, + 281.9, + 281.9, + 282.1, + 281.9, + 281.9, + 282.0, + 281.8, + 282.2, + 282.2, + 284.0, + 284.2, + 284.2, + 284.1, + 284.2, + 283.9, + 283.7, + 283.6, + 283.6, + 283.6, + 283.6, + 283.8, + 283.9, + 283.9, + 284.2, + 284.1, + 284.1, + 284.2, + 284.4, + 285.2, + 285.7, + 285.1, + 284.9, + 284.9, + 284.9, + 283.6, + 287.7, + 288.0, + 286.9, + 288.5, + 287.4, + 286.6, + 287.0, + 286.3, + 286.2, + 285.8, + 285.7, + 285.7, + 285.7, + 285.8, + 285.7, + 285.6, + 285.3, + 285.2, + 285.0, + 284.9, + 284.7, + 284.4, + 284.3, + 283.9, + 283.6, + 283.4, + 283.0, + 283.0, + 282.8, + 283.1, + 283.2, + 280.2, + 289.8, + 289.2, + 285.5, + 284.6, + 284.9, + 285.1, + 284.9, + 283.8, + 283.0, + 283.1, + 283.0, + 282.7, + 283.5, + 284.8, + 284.8, + 284.9, + 285.1, + 284.7, + ], + [ + 286.4, + 286.1, + 285.7, + 285.2, + 284.9, + 284.5, + 284.0, + 283.6, + 283.8, + 283.3, + 283.2, + 284.3, + 284.8, + 285.0, + 285.3, + 285.1, + 284.9, + 284.8, + 285.3, + 285.4, + 286.6, + 286.3, + 286.2, + 286.2, + 286.2, + 286.0, + 285.9, + 285.7, + 285.7, + 285.7, + 285.5, + 285.5, + 285.6, + 285.8, + 285.8, + 285.9, + 286.2, + 286.3, + 286.4, + 287.2, + 287.9, + 287.7, + 287.4, + 287.2, + 287.5, + 288.2, + 286.9, + 291.5, + 291.3, + 290.5, + 289.1, + 288.8, + 288.6, + 287.9, + 287.7, + 287.4, + 287.5, + 287.6, + 287.6, + 287.6, + 287.3, + 287.1, + 286.9, + 286.9, + 286.8, + 286.6, + 286.3, + 286.1, + 286.0, + 285.7, + 285.4, + 285.1, + 284.6, + 284.2, + 284.0, + 284.6, + 284.8, + 281.5, + 289.1, + 290.4, + 287.6, + 289.3, + 289.9, + 287.9, + 286.6, + 286.2, + 286.0, + 285.5, + 285.5, + 286.1, + 287.1, + 287.8, + 287.2, + 287.5, + 287.3, + 287.0, + ], + [ + 288.7, + 288.5, + 288.1, + 287.6, + 287.1, + 286.8, + 287.2, + 286.7, + 286.5, + 285.8, + 286.0, + 287.4, + 287.8, + 288.3, + 288.7, + 288.2, + 287.8, + 288.0, + 288.5, + 288.7, + 289.1, + 288.6, + 288.5, + 288.2, + 288.0, + 287.9, + 287.6, + 287.4, + 287.4, + 287.4, + 287.3, + 287.3, + 287.5, + 287.6, + 287.7, + 287.8, + 287.9, + 288.0, + 288.1, + 289.0, + 290.0, + 291.0, + 290.2, + 289.5, + 289.6, + 290.0, + 291.1, + 290.8, + 292.5, + 291.4, + 290.5, + 290.1, + 289.7, + 289.4, + 289.2, + 289.3, + 289.6, + 289.9, + 290.0, + 289.6, + 289.3, + 289.2, + 289.0, + 289.0, + 289.0, + 288.7, + 288.4, + 288.2, + 288.0, + 287.6, + 287.2, + 286.8, + 286.3, + 286.0, + 285.8, + 286.3, + 286.6, + 283.4, + 291.9, + 296.0, + 290.3, + 292.7, + 292.4, + 290.3, + 289.5, + 289.2, + 288.9, + 288.8, + 289.2, + 289.7, + 289.9, + 289.6, + 289.5, + 289.6, + 289.4, + 289.1, + ], + [ + 290.7, + 290.8, + 290.4, + 290.0, + 290.5, + 291.8, + 292.7, + 293.0, + 293.4, + 292.1, + 291.7, + 292.0, + 291.9, + 291.8, + 291.6, + 291.2, + 291.2, + 291.3, + 291.3, + 291.3, + 291.1, + 290.7, + 290.7, + 290.2, + 289.7, + 289.5, + 289.3, + 289.1, + 289.1, + 289.1, + 289.2, + 289.5, + 289.8, + 289.6, + 289.4, + 289.5, + 289.6, + 290.2, + 293.9, + 290.9, + 292.4, + 294.3, + 292.9, + 291.8, + 291.8, + 291.9, + 292.2, + 292.8, + 292.7, + 292.4, + 292.3, + 291.9, + 291.4, + 291.0, + 291.1, + 291.3, + 291.6, + 292.4, + 292.3, + 292.0, + 291.8, + 291.6, + 291.5, + 291.4, + 291.3, + 291.0, + 290.7, + 290.5, + 290.2, + 289.8, + 289.4, + 288.8, + 288.3, + 287.9, + 287.5, + 288.1, + 288.3, + 286.7, + 294.7, + 298.5, + 297.5, + 294.4, + 293.8, + 292.8, + 292.0, + 291.3, + 291.0, + 291.1, + 291.2, + 291.5, + 291.8, + 291.9, + 291.9, + 291.5, + 291.3, + 291.1, + ], + [ + 292.2, + 292.3, + 292.0, + 292.2, + 292.3, + 291.9, + 293.8, + 295.6, + 296.0, + 296.2, + 295.2, + 293.9, + 293.5, + 293.3, + 293.3, + 293.1, + 293.2, + 293.2, + 293.1, + 292.8, + 292.5, + 292.1, + 292.1, + 291.7, + 291.3, + 291.0, + 290.6, + 290.4, + 290.3, + 290.4, + 290.5, + 290.1, + 291.1, + 291.0, + 290.9, + 291.1, + 291.6, + 297.3, + 298.4, + 294.6, + 291.0, + 296.0, + 294.8, + 294.0, + 294.1, + 294.0, + 293.7, + 294.0, + 293.6, + 293.7, + 293.5, + 293.1, + 292.7, + 292.5, + 292.9, + 293.4, + 293.7, + 294.1, + 294.1, + 294.0, + 293.9, + 293.8, + 293.7, + 293.6, + 293.5, + 293.4, + 293.0, + 292.8, + 292.5, + 292.1, + 291.4, + 290.8, + 290.1, + 289.5, + 289.0, + 289.7, + 288.3, + 292.3, + 297.3, + 301.5, + 301.0, + 299.1, + 295.6, + 294.8, + 293.4, + 293.0, + 292.9, + 293.0, + 293.2, + 293.5, + 293.9, + 294.0, + 293.8, + 293.3, + 292.8, + 292.4, + ], + [ + 292.9, + 292.5, + 291.9, + 292.0, + 291.5, + 295.9, + 289.6, + 290.0, + 297.4, + 297.2, + 295.9, + 295.2, + 295.1, + 295.2, + 295.3, + 295.2, + 295.1, + 294.9, + 294.6, + 294.2, + 293.8, + 293.4, + 293.2, + 292.8, + 292.3, + 291.8, + 291.6, + 291.1, + 291.5, + 292.2, + 293.2, + 297.0, + 294.3, + 292.2, + 292.0, + 292.5, + 298.2, + 298.2, + 297.6, + 295.0, + 292.4, + 296.8, + 295.8, + 295.7, + 295.8, + 295.4, + 295.0, + 294.9, + 294.6, + 294.8, + 294.5, + 294.1, + 294.0, + 294.2, + 294.6, + 294.9, + 295.3, + 295.4, + 295.4, + 295.4, + 295.5, + 295.4, + 295.4, + 295.4, + 295.4, + 295.3, + 294.9, + 294.5, + 294.2, + 293.8, + 293.2, + 292.4, + 291.6, + 290.8, + 290.5, + 290.7, + 287.0, + 294.5, + 299.9, + 301.8, + 303.2, + 299.2, + 297.4, + 296.8, + 295.7, + 294.8, + 294.9, + 294.9, + 294.8, + 295.0, + 295.3, + 295.2, + 294.9, + 294.3, + 293.7, + 293.3, + ], + [ + 293.3, + 293.0, + 292.8, + 293.8, + 293.2, + 298.3, + 293.4, + 289.7, + 290.7, + 298.8, + 297.3, + 297.2, + 297.1, + 296.7, + 296.7, + 296.6, + 296.2, + 295.8, + 295.4, + 295.0, + 294.6, + 294.1, + 293.7, + 293.3, + 293.1, + 293.1, + 293.4, + 292.4, + 292.9, + 294.0, + 295.2, + 299.8, + 293.3, + 293.0, + 293.2, + 297.7, + 300.7, + 298.8, + 297.4, + 298.5, + 294.3, + 297.7, + 296.6, + 296.9, + 296.7, + 296.2, + 295.8, + 295.7, + 295.6, + 295.8, + 295.4, + 295.3, + 295.4, + 295.8, + 295.9, + 296.0, + 296.3, + 296.4, + 296.5, + 296.6, + 296.7, + 296.6, + 296.6, + 296.6, + 296.5, + 296.3, + 295.9, + 295.6, + 295.2, + 294.6, + 294.0, + 293.3, + 292.5, + 291.7, + 291.1, + 290.3, + 286.5, + 294.7, + 297.7, + 304.8, + 304.4, + 300.7, + 297.3, + 298.0, + 297.6, + 296.6, + 296.3, + 296.2, + 295.9, + 295.8, + 295.8, + 295.6, + 295.1, + 294.6, + 293.9, + 293.6, + ], + [ + 293.5, + 293.2, + 293.4, + 293.4, + 291.1, + 297.3, + 294.5, + 292.8, + 290.8, + 299.7, + 299.5, + 299.4, + 299.1, + 298.2, + 297.6, + 297.2, + 296.7, + 296.1, + 295.6, + 295.2, + 294.8, + 294.4, + 294.1, + 293.8, + 293.9, + 294.2, + 294.2, + 293.8, + 294.3, + 295.6, + 297.2, + 300.9, + 295.2, + 296.9, + 298.7, + 299.6, + 301.0, + 300.8, + 300.3, + 300.0, + 295.9, + 298.4, + 297.6, + 297.7, + 297.5, + 297.2, + 296.9, + 296.8, + 296.9, + 296.8, + 296.5, + 296.5, + 296.7, + 296.9, + 296.9, + 296.9, + 297.1, + 297.2, + 297.3, + 297.3, + 297.3, + 297.2, + 297.1, + 297.0, + 296.7, + 296.3, + 296.0, + 295.7, + 295.4, + 294.9, + 294.3, + 293.7, + 293.0, + 292.2, + 291.3, + 290.6, + 290.3, + 293.8, + 283.8, + 303.0, + 303.3, + 301.1, + 295.7, + 296.0, + 299.0, + 298.0, + 297.4, + 297.0, + 296.4, + 296.0, + 295.7, + 295.3, + 294.8, + 294.4, + 293.9, + 293.7, + ], + [ + 293.7, + 293.6, + 294.4, + 293.9, + 294.2, + 296.8, + 296.3, + 294.3, + 293.1, + 298.0, + 300.5, + 300.5, + 298.9, + 299.1, + 298.0, + 297.7, + 297.0, + 296.4, + 295.9, + 295.5, + 295.1, + 294.9, + 294.9, + 294.7, + 295.0, + 295.3, + 295.1, + 295.0, + 295.6, + 296.9, + 298.5, + 301.8, + 297.3, + 301.1, + 302.4, + 299.7, + 299.6, + 301.0, + 300.8, + 298.4, + 297.0, + 299.1, + 298.5, + 298.5, + 298.3, + 298.1, + 298.0, + 298.0, + 297.9, + 297.6, + 297.5, + 297.6, + 297.7, + 297.8, + 297.7, + 297.7, + 297.7, + 297.9, + 298.0, + 297.8, + 297.6, + 297.4, + 297.1, + 296.7, + 296.3, + 295.9, + 295.6, + 295.4, + 295.2, + 294.7, + 294.2, + 293.6, + 292.9, + 292.1, + 291.3, + 290.9, + 291.9, + 294.1, + 280.6, + 297.3, + 301.2, + 301.0, + 296.7, + 296.5, + 299.8, + 298.7, + 297.8, + 297.1, + 296.3, + 295.8, + 295.4, + 295.0, + 294.6, + 294.3, + 293.9, + 293.8, + ], + [ + 294.3, + 294.7, + 294.9, + 294.8, + 300.4, + 293.8, + 295.8, + 294.9, + 295.8, + 298.2, + 301.2, + 301.1, + 295.4, + 299.3, + 298.7, + 298.3, + 297.7, + 297.1, + 296.5, + 296.1, + 295.8, + 295.8, + 295.9, + 295.6, + 295.9, + 296.1, + 295.9, + 296.0, + 296.6, + 297.9, + 299.7, + 302.8, + 300.9, + 303.2, + 301.0, + 298.0, + 298.2, + 299.2, + 299.3, + 300.2, + 299.3, + 300.0, + 299.3, + 299.1, + 298.8, + 298.6, + 298.6, + 298.7, + 298.7, + 298.5, + 298.5, + 298.4, + 298.5, + 298.5, + 298.5, + 298.5, + 298.5, + 298.6, + 298.6, + 298.4, + 298.0, + 297.5, + 297.0, + 296.4, + 295.8, + 295.3, + 294.9, + 294.7, + 294.5, + 294.2, + 293.7, + 293.2, + 292.5, + 291.7, + 291.5, + 291.8, + 293.3, + 293.4, + 281.5, + 294.6, + 300.3, + 299.7, + 299.1, + 296.2, + 294.6, + 299.0, + 298.1, + 297.1, + 296.2, + 295.7, + 295.3, + 295.0, + 294.7, + 294.3, + 294.0, + 294.1, + ], + [ + 295.1, + 295.6, + 295.7, + 295.2, + 294.5, + 294.9, + 296.9, + 295.3, + 294.1, + 297.9, + 301.5, + 300.9, + 301.8, + 298.2, + 298.7, + 298.8, + 298.2, + 297.6, + 297.1, + 296.8, + 296.7, + 296.9, + 297.0, + 297.0, + 297.1, + 297.0, + 296.9, + 297.1, + 297.6, + 298.7, + 300.4, + 302.7, + 302.9, + 301.2, + 299.5, + 299.3, + 299.9, + 299.3, + 298.6, + 297.4, + 300.6, + 300.3, + 299.9, + 299.7, + 299.4, + 299.4, + 299.4, + 299.4, + 299.4, + 299.2, + 299.1, + 299.0, + 299.0, + 299.3, + 299.4, + 299.5, + 299.3, + 299.2, + 299.2, + 298.7, + 298.1, + 297.5, + 296.9, + 296.1, + 295.4, + 294.8, + 294.3, + 294.0, + 293.7, + 293.4, + 293.1, + 292.5, + 292.0, + 291.8, + 292.4, + 293.3, + 293.7, + 292.8, + 281.2, + 293.6, + 299.9, + 300.7, + 298.9, + 296.3, + 294.1, + 296.1, + 298.5, + 297.5, + 296.7, + 296.1, + 295.7, + 295.4, + 295.0, + 294.7, + 294.5, + 294.7, + ], + [ + 295.7, + 296.3, + 296.6, + 296.3, + 293.9, + 295.3, + 295.8, + 295.6, + 294.4, + 297.6, + 300.6, + 300.6, + 301.4, + 298.0, + 298.9, + 299.1, + 298.6, + 298.0, + 297.7, + 297.6, + 297.7, + 298.2, + 298.5, + 298.5, + 298.5, + 298.5, + 298.2, + 298.4, + 298.8, + 299.7, + 300.9, + 302.2, + 302.6, + 300.0, + 297.9, + 299.1, + 299.7, + 300.4, + 299.2, + 301.1, + 300.7, + 300.8, + 300.9, + 300.7, + 300.4, + 300.2, + 300.0, + 299.7, + 299.6, + 299.5, + 299.7, + 299.8, + 299.8, + 299.8, + 300.0, + 300.1, + 299.9, + 299.6, + 299.3, + 299.1, + 298.4, + 297.7, + 296.8, + 295.8, + 295.0, + 294.4, + 293.9, + 293.5, + 293.1, + 292.8, + 292.6, + 292.2, + 292.2, + 292.7, + 293.3, + 293.3, + 290.7, + 293.3, + 283.6, + 295.9, + 300.0, + 301.3, + 296.9, + 295.9, + 295.1, + 296.2, + 298.6, + 298.3, + 297.6, + 296.9, + 296.4, + 295.9, + 295.6, + 295.3, + 295.2, + 295.3, + ], + [ + 296.7, + 297.4, + 298.1, + 298.3, + 293.0, + 294.1, + 295.4, + 294.8, + 296.0, + 296.7, + 297.1, + 300.4, + 301.3, + 298.5, + 299.2, + 299.2, + 298.8, + 298.5, + 298.4, + 298.7, + 299.1, + 299.4, + 299.6, + 299.8, + 299.9, + 299.8, + 299.6, + 299.8, + 300.2, + 300.8, + 301.3, + 301.9, + 302.2, + 302.4, + 299.3, + 299.7, + 300.3, + 302.5, + 300.3, + 301.2, + 301.4, + 301.5, + 301.5, + 301.3, + 300.9, + 300.6, + 300.4, + 300.2, + 300.2, + 300.2, + 300.6, + 301.0, + 300.8, + 300.5, + 300.4, + 300.4, + 300.2, + 300.0, + 299.7, + 299.4, + 298.9, + 298.1, + 297.2, + 296.3, + 295.5, + 294.7, + 294.1, + 293.5, + 293.0, + 292.6, + 292.3, + 292.4, + 292.9, + 293.6, + 294.6, + 295.1, + 293.8, + 280.5, + 293.1, + 299.5, + 298.8, + 298.8, + 297.6, + 295.5, + 295.4, + 294.9, + 298.9, + 299.0, + 298.2, + 297.5, + 296.9, + 296.4, + 296.2, + 296.0, + 295.9, + 296.3, + ], + [ + 298.1, + 298.6, + 299.5, + 299.7, + 293.4, + 293.8, + 294.6, + 293.7, + 293.8, + 296.3, + 297.1, + 300.2, + 300.4, + 299.7, + 299.6, + 299.5, + 299.3, + 299.5, + 299.6, + 299.9, + 300.1, + 300.3, + 300.6, + 300.8, + 300.8, + 300.8, + 300.7, + 300.9, + 301.2, + 301.6, + 301.8, + 302.0, + 302.1, + 302.2, + 302.2, + 302.3, + 300.9, + 302.6, + 301.9, + 301.9, + 302.0, + 301.8, + 301.7, + 301.4, + 301.2, + 301.1, + 301.1, + 301.0, + 300.9, + 300.9, + 301.2, + 301.3, + 301.1, + 300.9, + 300.8, + 300.7, + 300.6, + 300.4, + 300.1, + 299.9, + 299.6, + 299.0, + 298.1, + 297.3, + 296.3, + 295.4, + 294.7, + 294.1, + 293.8, + 293.4, + 293.4, + 293.8, + 294.6, + 295.3, + 296.4, + 296.5, + 286.2, + 293.0, + 299.3, + 299.6, + 298.9, + 298.4, + 298.4, + 297.8, + 295.5, + 294.9, + 298.6, + 299.3, + 298.8, + 298.2, + 297.7, + 297.2, + 296.9, + 296.9, + 297.2, + 297.7, + ], + [ + 299.3, + 300.1, + 300.9, + 301.0, + 296.5, + 295.0, + 295.4, + 294.5, + 293.6, + 293.2, + 298.3, + 300.1, + 300.6, + 300.2, + 300.2, + 300.2, + 300.1, + 300.3, + 300.4, + 300.5, + 300.6, + 300.7, + 300.8, + 300.9, + 301.0, + 301.1, + 301.2, + 301.2, + 301.4, + 301.6, + 301.8, + 301.9, + 301.9, + 302.0, + 302.2, + 302.2, + 302.4, + 302.3, + 302.5, + 302.3, + 302.1, + 301.8, + 301.7, + 301.6, + 301.5, + 301.5, + 301.4, + 301.3, + 301.3, + 301.4, + 301.4, + 301.3, + 301.2, + 301.1, + 301.1, + 301.0, + 300.9, + 300.7, + 300.5, + 300.2, + 299.8, + 299.2, + 298.4, + 297.5, + 296.7, + 296.1, + 295.6, + 295.3, + 295.1, + 295.0, + 295.1, + 295.5, + 296.1, + 297.1, + 297.6, + 296.6, + 292.3, + 299.6, + 300.4, + 299.8, + 299.4, + 298.4, + 298.4, + 298.9, + 296.7, + 296.7, + 300.1, + 299.5, + 299.2, + 298.8, + 298.4, + 298.2, + 298.2, + 298.4, + 298.7, + 298.9, + ], + [ + 300.7, + 301.3, + 301.7, + 301.6, + 297.2, + 296.6, + 296.7, + 296.4, + 294.2, + 293.5, + 299.5, + 299.8, + 300.8, + 300.4, + 300.1, + 300.1, + 300.2, + 300.4, + 300.5, + 300.5, + 300.6, + 300.8, + 300.8, + 300.8, + 300.8, + 300.9, + 300.9, + 300.9, + 301.2, + 301.4, + 301.6, + 301.8, + 302.0, + 302.1, + 302.2, + 302.4, + 302.4, + 302.4, + 300.7, + 296.4, + 302.0, + 301.9, + 301.9, + 301.8, + 301.8, + 301.7, + 301.6, + 301.5, + 301.5, + 301.4, + 301.4, + 301.4, + 301.3, + 301.2, + 301.0, + 300.9, + 300.8, + 300.7, + 300.4, + 299.9, + 299.4, + 298.8, + 298.2, + 297.7, + 297.3, + 296.9, + 296.6, + 296.4, + 296.3, + 296.3, + 296.5, + 296.9, + 297.4, + 298.4, + 298.5, + 291.0, + 298.6, + 300.5, + 300.8, + 300.9, + 300.1, + 298.7, + 298.5, + 298.4, + 297.9, + 297.8, + 299.0, + 299.5, + 299.9, + 299.7, + 299.6, + 299.5, + 299.5, + 299.6, + 299.9, + 300.1, + ], + [ + 301.2, + 301.4, + 301.5, + 301.6, + 298.1, + 298.3, + 298.3, + 297.6, + 294.4, + 293.5, + 297.6, + 299.1, + 300.7, + 300.6, + 300.2, + 299.9, + 300.0, + 300.2, + 300.4, + 300.4, + 300.5, + 300.6, + 300.5, + 300.6, + 300.7, + 300.8, + 300.9, + 301.5, + 299.1, + 302.0, + 302.1, + 302.2, + 302.6, + 302.4, + 302.2, + 302.3, + 302.2, + 298.0, + 295.6, + 301.5, + 301.8, + 301.7, + 301.6, + 301.5, + 301.3, + 301.2, + 301.1, + 301.0, + 301.0, + 300.9, + 300.7, + 300.6, + 300.3, + 300.0, + 299.9, + 299.8, + 299.7, + 299.6, + 299.4, + 299.2, + 298.8, + 298.5, + 298.1, + 297.8, + 297.8, + 297.7, + 297.6, + 297.5, + 297.6, + 297.7, + 297.9, + 298.2, + 298.8, + 299.5, + 299.3, + 294.1, + 300.7, + 301.2, + 301.1, + 300.9, + 300.2, + 299.5, + 298.9, + 298.7, + 298.8, + 297.7, + 300.7, + 300.1, + 300.4, + 300.3, + 300.3, + 300.3, + 300.4, + 300.5, + 300.7, + 300.9, + ], + [ + 301.2, + 301.3, + 301.4, + 299.1, + 297.8, + 299.4, + 298.8, + 297.3, + 291.6, + 294.2, + 297.2, + 299.3, + 300.3, + 300.2, + 300.3, + 300.2, + 300.1, + 300.2, + 300.3, + 300.5, + 300.5, + 300.6, + 300.6, + 300.6, + 300.8, + 301.1, + 301.2, + 298.7, + 300.5, + 302.1, + 300.4, + 300.7, + 296.8, + 302.3, + 302.2, + 302.3, + 302.6, + 299.3, + 301.6, + 301.2, + 300.7, + 300.4, + 300.3, + 300.2, + 300.1, + 300.0, + 300.0, + 299.7, + 299.7, + 299.4, + 299.3, + 299.2, + 298.8, + 298.6, + 298.3, + 297.9, + 297.7, + 297.5, + 297.4, + 297.5, + 297.3, + 297.2, + 297.1, + 297.0, + 297.2, + 297.0, + 297.3, + 297.4, + 297.7, + 298.0, + 298.0, + 298.6, + 298.9, + 299.3, + 299.3, + 294.4, + 300.5, + 301.2, + 300.9, + 300.7, + 299.8, + 300.0, + 301.0, + 299.5, + 299.3, + 300.7, + 300.7, + 300.2, + 300.4, + 300.5, + 300.4, + 300.5, + 300.6, + 300.7, + 300.9, + 301.0, + ], + [ + 301.4, + 301.5, + 301.4, + 299.0, + 298.3, + 299.5, + 299.6, + 297.9, + 293.7, + 293.8, + 296.3, + 302.4, + 300.0, + 299.5, + 299.7, + 300.1, + 300.3, + 300.3, + 300.5, + 300.6, + 300.6, + 300.6, + 300.8, + 300.9, + 301.3, + 301.6, + 301.5, + 298.7, + 301.6, + 301.6, + 298.6, + 299.5, + 302.0, + 301.9, + 302.1, + 302.2, + 302.1, + 301.8, + 301.2, + 300.6, + 300.2, + 300.0, + 299.8, + 299.7, + 299.6, + 299.4, + 299.4, + 299.1, + 299.0, + 298.6, + 298.5, + 298.2, + 298.0, + 297.8, + 297.6, + 297.3, + 297.0, + 297.0, + 296.6, + 296.8, + 296.5, + 296.5, + 296.3, + 296.3, + 296.0, + 296.1, + 296.5, + 296.8, + 297.0, + 297.8, + 298.0, + 298.8, + 299.0, + 299.3, + 299.4, + 293.3, + 299.8, + 300.5, + 300.4, + 299.8, + 300.2, + 302.2, + 300.8, + 300.1, + 300.2, + 300.1, + 300.0, + 300.2, + 300.2, + 300.3, + 300.4, + 300.6, + 300.8, + 300.9, + 301.1, + 301.2, + ], + [ + 301.9, + 301.9, + 301.6, + 298.0, + 297.9, + 299.2, + 298.9, + 297.8, + 295.1, + 295.1, + 300.5, + 301.9, + 300.2, + 299.3, + 299.3, + 299.8, + 299.9, + 300.1, + 300.2, + 300.3, + 300.2, + 300.1, + 300.3, + 300.5, + 301.1, + 301.6, + 301.5, + 299.3, + 300.9, + 301.2, + 299.4, + 297.5, + 301.9, + 301.9, + 301.9, + 301.9, + 301.9, + 301.7, + 301.6, + 301.3, + 301.0, + 300.7, + 300.6, + 300.4, + 300.3, + 300.1, + 299.9, + 299.8, + 299.7, + 299.5, + 299.3, + 299.0, + 299.1, + 299.2, + 299.0, + 298.8, + 298.6, + 298.5, + 298.4, + 298.4, + 298.1, + 298.1, + 298.3, + 298.3, + 298.2, + 298.4, + 298.7, + 298.8, + 299.1, + 299.3, + 299.4, + 299.7, + 299.7, + 299.7, + 299.9, + 300.1, + 293.9, + 299.3, + 300.0, + 298.1, + 302.8, + 301.0, + 299.5, + 300.0, + 299.9, + 299.9, + 300.0, + 300.2, + 300.4, + 300.6, + 300.8, + 301.0, + 301.3, + 301.4, + 301.7, + 301.8, + ], + [ + 302.3, + 302.5, + 299.8, + 295.9, + 296.7, + 297.9, + 297.1, + 295.9, + 298.0, + 299.7, + 292.1, + 298.2, + 298.1, + 299.2, + 298.9, + 299.5, + 299.8, + 300.0, + 300.1, + 300.0, + 299.8, + 299.7, + 299.8, + 300.2, + 300.7, + 301.2, + 301.3, + 296.9, + 300.8, + 301.4, + 302.1, + 298.0, + 302.2, + 301.9, + 301.5, + 301.8, + 301.8, + 301.7, + 301.6, + 301.5, + 301.4, + 301.3, + 301.2, + 301.1, + 301.0, + 300.8, + 300.7, + 300.6, + 300.4, + 300.3, + 300.1, + 300.0, + 300.1, + 299.9, + 299.9, + 299.9, + 299.8, + 299.7, + 299.7, + 299.8, + 299.7, + 299.6, + 299.5, + 299.4, + 299.4, + 299.6, + 299.7, + 299.8, + 300.0, + 300.1, + 300.1, + 300.1, + 300.1, + 299.9, + 300.1, + 301.1, + 290.3, + 299.6, + 299.7, + 298.0, + 296.0, + 299.6, + 299.4, + 299.8, + 299.6, + 299.6, + 299.9, + 300.1, + 300.4, + 300.7, + 301.0, + 301.3, + 301.6, + 302.2, + 299.7, + 300.3, + ], + [ + 298.4, + 297.2, + 296.5, + 293.9, + 297.7, + 298.4, + 294.5, + 296.0, + 301.3, + 299.8, + 288.7, + 292.5, + 294.3, + 296.1, + 298.5, + 298.7, + 299.2, + 299.5, + 299.7, + 299.7, + 299.6, + 299.5, + 299.6, + 300.0, + 300.4, + 300.7, + 300.8, + 300.4, + 300.8, + 301.3, + 301.5, + 301.9, + 302.0, + 297.3, + 301.3, + 301.4, + 301.3, + 301.2, + 301.1, + 301.0, + 300.9, + 300.8, + 300.8, + 300.7, + 300.5, + 300.3, + 300.3, + 300.2, + 300.0, + 300.0, + 300.0, + 299.9, + 299.9, + 299.9, + 299.9, + 299.9, + 299.9, + 299.8, + 299.7, + 299.8, + 299.8, + 299.8, + 299.8, + 299.8, + 299.9, + 300.1, + 300.2, + 300.1, + 299.9, + 299.8, + 299.8, + 300.0, + 299.9, + 299.7, + 299.7, + 298.3, + 295.7, + 296.7, + 303.6, + 300.4, + 299.1, + 299.5, + 299.7, + 299.7, + 299.0, + 298.9, + 299.1, + 299.3, + 299.6, + 299.9, + 300.0, + 300.5, + 301.6, + 298.6, + 297.4, + 298.3, + ], + [ + 296.5, + 294.9, + 293.1, + 296.6, + 300.2, + 300.2, + 298.6, + 299.1, + 301.0, + 299.0, + 290.4, + 291.1, + 290.9, + 290.4, + 297.5, + 297.8, + 298.0, + 298.4, + 299.1, + 299.6, + 299.8, + 293.2, + 299.1, + 299.5, + 299.9, + 300.2, + 300.3, + 297.6, + 298.5, + 301.0, + 301.0, + 301.2, + 301.1, + 298.1, + 300.7, + 300.7, + 300.6, + 300.5, + 300.4, + 300.2, + 299.9, + 299.7, + 299.6, + 299.4, + 299.2, + 299.0, + 298.9, + 298.8, + 298.6, + 298.6, + 298.6, + 298.6, + 298.7, + 298.6, + 298.6, + 298.7, + 298.7, + 298.7, + 298.7, + 298.8, + 299.0, + 299.1, + 299.2, + 299.3, + 299.5, + 299.8, + 299.9, + 299.6, + 299.2, + 299.0, + 299.1, + 299.6, + 300.1, + 300.1, + 294.4, + 299.6, + 300.2, + 299.3, + 301.1, + 300.2, + 299.0, + 299.5, + 299.1, + 298.7, + 298.5, + 298.2, + 298.1, + 298.1, + 298.3, + 298.6, + 298.7, + 299.2, + 300.9, + 295.8, + 295.9, + 296.5, + ], + [ + 298.7, + 297.5, + 297.7, + 299.4, + 300.9, + 299.8, + 297.2, + 296.9, + 299.0, + 301.1, + 291.8, + 289.3, + 288.2, + 297.1, + 297.1, + 296.8, + 296.8, + 297.3, + 298.3, + 299.3, + 300.1, + 291.1, + 298.5, + 298.8, + 299.1, + 299.3, + 298.9, + 295.9, + 297.2, + 296.0, + 300.4, + 300.4, + 300.1, + 297.6, + 300.2, + 300.1, + 300.1, + 300.1, + 300.0, + 299.8, + 299.6, + 299.3, + 299.0, + 298.6, + 298.3, + 298.1, + 297.9, + 297.7, + 297.6, + 297.5, + 297.5, + 297.4, + 297.5, + 297.5, + 297.4, + 297.2, + 297.2, + 297.3, + 297.2, + 297.5, + 297.8, + 298.1, + 298.3, + 298.6, + 298.8, + 299.2, + 299.5, + 299.4, + 298.8, + 298.1, + 298.0, + 298.6, + 300.1, + 296.6, + 299.2, + 297.7, + 297.0, + 296.4, + 297.7, + 298.7, + 298.7, + 298.8, + 298.4, + 298.2, + 298.1, + 297.8, + 297.5, + 297.3, + 297.2, + 297.5, + 297.7, + 297.9, + 299.8, + 296.7, + 297.8, + 298.6, + ], + [ + 298.5, + 297.9, + 297.1, + 296.8, + 297.3, + 297.1, + 294.6, + 293.7, + 296.6, + 299.4, + 293.8, + 298.8, + 284.9, + 289.3, + 297.1, + 295.9, + 295.7, + 296.3, + 297.4, + 298.5, + 291.5, + 294.5, + 298.3, + 298.2, + 298.4, + 298.2, + 297.4, + 293.2, + 293.8, + 293.9, + 299.2, + 299.3, + 295.4, + 299.2, + 299.3, + 299.1, + 299.2, + 299.2, + 299.3, + 299.4, + 299.3, + 299.0, + 298.6, + 298.1, + 297.6, + 297.3, + 297.0, + 296.8, + 296.7, + 296.6, + 296.5, + 296.2, + 296.3, + 296.3, + 296.1, + 296.1, + 296.1, + 296.1, + 296.3, + 296.5, + 296.8, + 297.0, + 297.2, + 297.2, + 297.3, + 297.7, + 298.4, + 299.1, + 298.9, + 297.9, + 297.2, + 296.4, + 291.5, + 293.8, + 299.1, + 299.1, + 298.8, + 298.8, + 298.9, + 298.9, + 298.5, + 298.2, + 297.8, + 297.5, + 297.4, + 297.3, + 297.1, + 296.9, + 296.7, + 296.5, + 296.5, + 296.9, + 299.4, + 299.6, + 298.4, + 298.6, + ], + [ + 295.1, + 294.7, + 293.0, + 292.3, + 293.5, + 294.7, + 292.3, + 293.1, + 295.3, + 296.1, + 294.7, + 300.3, + 284.4, + 288.6, + 289.2, + 295.6, + 294.9, + 295.7, + 296.8, + 298.0, + 291.2, + 291.8, + 290.6, + 298.1, + 298.1, + 297.6, + 288.6, + 289.9, + 289.8, + 296.0, + 297.7, + 298.2, + 298.0, + 297.6, + 297.2, + 297.0, + 297.2, + 297.5, + 297.7, + 297.9, + 298.2, + 298.4, + 298.3, + 298.0, + 297.6, + 297.1, + 296.6, + 296.3, + 295.9, + 295.7, + 295.4, + 295.1, + 295.0, + 294.9, + 294.7, + 294.7, + 294.6, + 294.6, + 294.8, + 295.2, + 295.4, + 295.3, + 295.4, + 295.3, + 295.2, + 295.4, + 296.6, + 298.2, + 298.9, + 289.8, + 287.0, + 289.8, + 294.7, + 298.4, + 298.6, + 298.7, + 299.0, + 299.0, + 298.6, + 298.5, + 298.2, + 297.8, + 297.5, + 297.1, + 296.7, + 296.6, + 296.6, + 296.4, + 296.2, + 296.1, + 296.1, + 295.9, + 296.5, + 296.6, + 295.4, + 295.0, + ], + [ + 292.2, + 290.9, + 290.4, + 290.4, + 290.2, + 288.6, + 289.6, + 290.2, + 292.8, + 292.5, + 299.2, + 289.1, + 287.6, + 289.6, + 290.8, + 290.7, + 295.2, + 295.5, + 296.5, + 297.5, + 289.3, + 291.2, + 287.7, + 298.1, + 298.2, + 288.9, + 286.8, + 285.9, + 286.0, + 292.4, + 295.0, + 296.9, + 296.4, + 295.5, + 294.8, + 294.5, + 294.8, + 295.2, + 295.6, + 295.9, + 296.3, + 296.7, + 296.9, + 297.0, + 296.8, + 296.5, + 296.1, + 295.8, + 295.3, + 294.9, + 294.6, + 294.3, + 294.1, + 293.8, + 293.6, + 293.4, + 293.2, + 293.1, + 293.0, + 293.1, + 293.1, + 293.0, + 293.0, + 293.0, + 292.9, + 293.4, + 294.6, + 296.7, + 290.7, + 283.2, + 283.4, + 296.3, + 294.1, + 297.6, + 297.4, + 297.6, + 295.5, + 295.2, + 298.1, + 297.8, + 297.6, + 297.3, + 297.1, + 296.8, + 296.3, + 295.9, + 295.8, + 295.7, + 295.6, + 295.5, + 295.5, + 294.7, + 293.2, + 292.9, + 292.1, + 292.2, + ], + [ + 291.1, + 288.9, + 286.0, + 287.7, + 287.6, + 286.6, + 287.5, + 287.1, + 290.2, + 290.0, + 297.1, + 287.9, + 286.7, + 288.3, + 290.4, + 288.3, + 295.3, + 295.2, + 295.8, + 288.5, + 288.9, + 288.4, + 287.4, + 287.3, + 291.2, + 286.0, + 284.9, + 280.5, + 282.4, + 280.3, + 283.8, + 293.2, + 293.6, + 293.6, + 292.4, + 292.2, + 292.3, + 292.5, + 293.2, + 293.8, + 294.2, + 294.8, + 295.2, + 295.5, + 295.6, + 295.5, + 295.2, + 294.8, + 294.4, + 294.0, + 293.7, + 293.4, + 293.1, + 292.8, + 292.6, + 292.4, + 292.2, + 292.0, + 291.8, + 291.8, + 291.7, + 291.7, + 291.8, + 291.8, + 291.6, + 292.0, + 293.2, + 295.5, + 284.4, + 280.6, + 286.9, + 294.5, + 295.2, + 296.1, + 295.2, + 291.2, + 296.1, + 296.7, + 296.8, + 296.6, + 296.5, + 296.5, + 296.3, + 296.1, + 295.7, + 295.3, + 295.1, + 294.9, + 294.9, + 294.8, + 294.9, + 294.4, + 290.6, + 290.8, + 290.8, + 291.1, + ], + [ + 290.4, + 288.1, + 285.2, + 285.7, + 286.9, + 287.4, + 287.2, + 286.0, + 287.6, + 289.6, + 290.1, + 285.8, + 284.8, + 287.0, + 289.7, + 288.5, + 294.5, + 294.6, + 286.5, + 286.4, + 284.1, + 285.4, + 287.0, + 288.2, + 287.0, + 282.8, + 281.1, + 278.0, + 278.5, + 280.5, + 279.8, + 276.8, + 289.8, + 292.0, + 290.9, + 290.5, + 290.5, + 290.4, + 290.8, + 291.3, + 291.9, + 292.6, + 293.2, + 293.7, + 294.0, + 294.1, + 294.0, + 293.9, + 293.6, + 293.2, + 292.8, + 292.4, + 292.1, + 291.6, + 291.3, + 291.1, + 290.9, + 290.9, + 290.9, + 290.8, + 290.7, + 290.8, + 290.9, + 290.9, + 290.6, + 290.9, + 292.0, + 293.9, + 279.2, + 280.3, + 285.6, + 293.0, + 293.5, + 293.6, + 292.5, + 293.9, + 294.4, + 294.8, + 295.0, + 295.2, + 295.2, + 295.3, + 295.3, + 295.3, + 295.1, + 294.9, + 294.6, + 294.4, + 294.2, + 294.0, + 294.0, + 294.2, + 294.0, + 289.0, + 289.9, + 290.0, + ], + [ + 288.3, + 287.8, + 287.2, + 286.6, + 286.8, + 287.7, + 288.2, + 287.0, + 286.6, + 287.5, + 284.8, + 283.8, + 284.1, + 293.8, + 294.4, + 283.1, + 280.0, + 280.8, + 283.8, + 284.8, + 281.4, + 284.1, + 284.0, + 275.8, + 272.7, + 278.9, + 274.1, + 272.2, + 277.0, + 274.2, + 277.1, + 276.9, + 275.1, + 288.0, + 288.3, + 289.1, + 289.0, + 288.8, + 288.8, + 289.2, + 289.6, + 290.3, + 290.9, + 291.6, + 292.0, + 292.2, + 292.2, + 292.2, + 292.1, + 291.9, + 291.6, + 291.3, + 291.0, + 290.6, + 290.3, + 290.0, + 289.7, + 289.6, + 289.7, + 289.9, + 289.8, + 289.7, + 289.8, + 289.8, + 289.5, + 289.8, + 290.7, + 279.3, + 278.3, + 280.4, + 281.1, + 288.7, + 288.6, + 288.4, + 288.4, + 291.6, + 292.2, + 292.7, + 293.0, + 293.3, + 293.5, + 293.8, + 293.9, + 294.1, + 294.1, + 294.0, + 293.8, + 293.7, + 293.6, + 293.4, + 293.1, + 293.1, + 293.5, + 287.8, + 287.2, + 287.1, + ], + [ + 283.4, + 285.0, + 285.9, + 285.5, + 286.3, + 287.6, + 287.1, + 286.6, + 286.6, + 284.8, + 282.4, + 282.7, + 284.0, + 293.1, + 274.8, + 275.5, + 279.8, + 279.6, + 278.0, + 283.2, + 282.9, + 276.8, + 261.2, + 245.8, + 259.6, + 256.2, + 259.3, + 267.7, + 277.8, + 274.4, + 275.5, + 275.4, + 274.8, + 284.1, + 284.2, + 285.8, + 286.5, + 286.5, + 287.1, + 287.7, + 288.2, + 288.7, + 289.2, + 289.7, + 290.1, + 290.3, + 290.4, + 290.2, + 290.2, + 290.0, + 289.8, + 289.6, + 289.3, + 289.1, + 289.0, + 288.9, + 288.9, + 288.7, + 288.7, + 288.9, + 288.9, + 288.6, + 288.8, + 288.7, + 288.3, + 288.6, + 281.2, + 277.0, + 276.8, + 277.0, + 275.8, + 277.1, + 276.5, + 276.5, + 276.7, + 288.3, + 290.4, + 291.3, + 291.5, + 291.7, + 291.9, + 292.1, + 292.4, + 292.6, + 292.8, + 292.9, + 292.8, + 292.8, + 292.7, + 292.6, + 292.3, + 292.2, + 292.5, + 292.7, + 282.1, + 283.9, + ], + [ + 279.1, + 280.6, + 283.5, + 283.6, + 289.8, + 290.2, + 290.1, + 290.0, + 290.4, + 290.8, + 280.5, + 281.3, + 283.8, + 276.7, + 274.6, + 276.6, + 276.4, + 275.7, + 272.5, + 281.0, + 278.8, + 254.8, + 251.9, + 253.4, + 256.7, + 247.5, + 257.2, + 263.1, + 270.5, + 272.0, + 273.2, + 273.7, + 274.2, + 280.9, + 279.7, + 282.0, + 282.5, + 282.2, + 284.2, + 285.2, + 286.2, + 287.0, + 287.5, + 287.9, + 288.2, + 288.4, + 288.6, + 288.5, + 288.3, + 288.2, + 287.9, + 287.7, + 287.4, + 287.3, + 287.3, + 287.4, + 287.6, + 287.5, + 287.5, + 287.6, + 287.8, + 287.6, + 287.7, + 287.6, + 287.1, + 279.8, + 279.5, + 273.8, + 273.7, + 274.4, + 274.8, + 275.3, + 275.0, + 275.1, + 274.7, + 284.1, + 288.4, + 289.8, + 290.2, + 290.2, + 290.6, + 290.7, + 290.9, + 291.1, + 291.3, + 291.6, + 291.8, + 291.8, + 291.7, + 291.6, + 291.5, + 291.4, + 291.5, + 291.4, + 282.4, + 278.9, + ], + [ + 278.8, + 277.5, + 278.0, + 288.2, + 288.2, + 288.1, + 288.2, + 288.6, + 289.0, + 289.3, + 278.3, + 280.4, + 277.2, + 272.0, + 275.3, + 276.3, + 275.6, + 273.5, + 269.1, + 269.9, + 255.7, + 246.4, + 251.9, + 253.4, + 245.8, + 246.2, + 258.6, + 263.1, + 267.9, + 270.2, + 270.5, + 271.0, + 276.2, + 276.7, + 274.4, + 277.2, + 269.0, + 266.8, + 280.2, + 282.3, + 283.3, + 284.2, + 285.0, + 285.6, + 285.8, + 286.0, + 286.3, + 286.5, + 286.4, + 286.3, + 286.1, + 285.9, + 285.7, + 285.6, + 285.5, + 285.7, + 286.1, + 286.1, + 286.2, + 286.4, + 286.5, + 286.5, + 286.6, + 286.7, + 277.9, + 276.5, + 273.3, + 269.3, + 271.2, + 272.5, + 272.2, + 271.8, + 271.7, + 271.3, + 270.9, + 270.7, + 284.5, + 287.6, + 288.5, + 288.9, + 289.5, + 289.8, + 289.6, + 289.7, + 290.0, + 290.2, + 290.5, + 290.7, + 290.8, + 290.7, + 290.7, + 290.5, + 290.1, + 290.0, + 289.2, + 281.4, + ], + [ + 285.2, + 286.4, + 286.4, + 286.0, + 286.2, + 286.3, + 286.6, + 286.0, + 276.0, + 274.1, + 274.7, + 274.2, + 270.0, + 272.4, + 287.2, + 274.7, + 275.2, + 275.3, + 275.2, + 260.8, + 251.5, + 268.3, + 270.1, + 263.4, + 252.5, + 263.2, + 252.3, + 262.8, + 264.5, + 265.2, + 266.5, + 268.0, + 272.0, + 270.4, + 247.3, + 271.7, + 275.1, + 266.1, + 277.3, + 278.6, + 280.3, + 281.6, + 282.2, + 282.6, + 283.1, + 283.4, + 283.9, + 284.3, + 284.5, + 284.3, + 283.8, + 283.5, + 283.5, + 283.4, + 283.6, + 284.0, + 284.5, + 284.7, + 284.9, + 285.2, + 285.3, + 285.5, + 285.4, + 285.4, + 273.3, + 267.6, + 265.5, + 264.9, + 263.3, + 271.6, + 270.0, + 268.6, + 268.3, + 268.5, + 268.7, + 270.4, + 281.5, + 285.3, + 286.5, + 287.6, + 288.1, + 288.5, + 288.7, + 288.6, + 288.7, + 288.8, + 289.0, + 289.6, + 289.7, + 289.8, + 289.6, + 289.3, + 288.6, + 287.8, + 279.7, + 279.1, + ], + [ + 273.8, + 284.8, + 284.3, + 283.4, + 284.1, + 284.3, + 269.9, + 276.7, + 275.1, + 272.3, + 266.6, + 264.7, + 269.8, + 274.6, + 283.5, + 272.9, + 272.1, + 273.2, + 272.5, + 263.6, + 259.2, + 256.8, + 262.2, + 266.6, + 266.4, + 261.5, + 258.6, + 264.1, + 262.1, + 262.1, + 260.5, + 263.4, + 255.3, + 246.1, + 253.9, + 266.2, + 270.5, + 272.7, + 262.4, + 270.6, + 276.3, + 277.7, + 277.8, + 277.9, + 278.5, + 279.6, + 280.3, + 280.7, + 281.2, + 281.3, + 281.1, + 281.1, + 281.3, + 281.5, + 281.7, + 282.3, + 282.9, + 283.3, + 283.5, + 284.0, + 284.2, + 284.4, + 284.4, + 278.8, + 269.8, + 264.9, + 264.1, + 262.6, + 262.6, + 268.9, + 265.4, + 264.7, + 264.5, + 265.8, + 266.6, + 265.8, + 267.8, + 279.0, + 281.4, + 283.4, + 284.6, + 285.3, + 285.8, + 286.0, + 286.8, + 286.9, + 287.1, + 288.1, + 288.7, + 288.8, + 288.4, + 288.0, + 287.3, + 286.7, + 277.4, + 276.5, + ], + [ + 274.9, + 283.4, + 282.1, + 269.1, + 281.3, + 261.9, + 270.2, + 267.7, + 280.6, + 281.8, + 282.8, + 273.9, + 265.5, + 281.3, + 272.6, + 269.8, + 268.9, + 269.3, + 271.0, + 266.4, + 261.9, + 257.4, + 261.4, + 253.0, + 261.0, + 261.9, + 257.6, + 260.1, + 257.9, + 258.3, + 256.1, + 252.3, + 255.7, + 247.3, + 251.4, + 240.9, + 265.6, + 270.0, + 261.5, + 269.7, + 270.6, + 274.3, + 274.8, + 275.4, + 276.4, + 277.5, + 277.5, + 277.6, + 278.1, + 278.5, + 278.6, + 279.0, + 279.3, + 279.7, + 280.0, + 280.5, + 281.1, + 281.7, + 282.1, + 282.5, + 283.0, + 283.1, + 283.4, + 276.3, + 268.0, + 265.9, + 262.4, + 261.1, + 261.6, + 262.4, + 262.7, + 263.0, + 262.7, + 264.7, + 265.1, + 264.7, + 266.2, + 265.9, + 276.9, + 279.0, + 280.8, + 281.2, + 280.9, + 281.5, + 283.2, + 283.6, + 284.3, + 286.1, + 287.3, + 287.6, + 286.8, + 286.8, + 286.5, + 286.3, + 275.0, + 276.1, + ], + [ + 274.5, + 271.0, + 266.0, + 268.2, + 266.9, + 266.2, + 266.2, + 263.3, + 277.4, + 279.3, + 280.8, + 273.2, + 273.8, + 278.1, + 269.3, + 267.1, + 265.8, + 264.3, + 266.5, + 265.0, + 266.8, + 262.1, + 257.7, + 260.3, + 253.8, + 252.7, + 247.7, + 255.3, + 255.3, + 253.8, + 254.4, + 250.6, + 250.4, + 243.1, + 244.6, + 248.9, + 249.0, + 266.6, + 262.0, + 270.0, + 272.0, + 273.2, + 273.3, + 274.0, + 274.7, + 275.4, + 275.5, + 275.6, + 275.9, + 276.1, + 276.5, + 277.1, + 277.7, + 278.1, + 278.4, + 278.9, + 279.7, + 280.2, + 280.6, + 281.2, + 281.9, + 282.1, + 282.5, + 276.1, + 272.3, + 267.5, + 260.7, + 261.8, + 262.1, + 259.5, + 259.8, + 261.4, + 261.6, + 272.4, + 273.0, + 259.2, + 261.9, + 262.4, + 264.8, + 274.8, + 276.8, + 276.2, + 276.9, + 277.8, + 279.4, + 280.8, + 282.1, + 284.0, + 285.3, + 286.1, + 285.4, + 285.1, + 285.2, + 285.8, + 285.1, + 283.7, + ], + [ + 273.0, + 274.0, + 266.9, + 261.6, + 264.6, + 265.3, + 264.3, + 262.6, + 262.2, + 264.1, + 268.2, + 268.7, + 267.6, + 266.1, + 263.6, + 262.5, + 260.5, + 260.1, + 259.8, + 258.0, + 258.9, + 259.2, + 256.6, + 251.1, + 247.7, + 252.2, + 249.4, + 249.4, + 253.3, + 249.6, + 249.9, + 246.2, + 246.0, + 244.9, + 242.8, + 246.4, + 249.2, + 250.5, + 264.2, + 269.5, + 271.8, + 271.9, + 271.8, + 272.8, + 273.5, + 273.9, + 273.9, + 274.0, + 274.2, + 274.5, + 275.1, + 275.7, + 276.4, + 276.9, + 277.2, + 277.7, + 278.5, + 279.1, + 279.6, + 280.3, + 281.1, + 281.4, + 281.0, + 275.6, + 271.9, + 266.2, + 263.0, + 260.8, + 257.4, + 257.4, + 256.9, + 258.9, + 259.5, + 269.7, + 252.8, + 258.9, + 261.3, + 262.3, + 264.2, + 272.0, + 273.8, + 268.0, + 274.9, + 276.0, + 277.5, + 279.2, + 280.6, + 282.1, + 283.3, + 284.4, + 284.4, + 284.1, + 285.0, + 285.1, + 284.0, + 282.6, + ], + [ + 271.1, + 272.8, + 268.2, + 266.4, + 264.6, + 262.8, + 263.0, + 261.6, + 261.5, + 261.0, + 260.6, + 260.5, + 260.5, + 260.2, + 259.6, + 258.6, + 256.7, + 257.0, + 257.7, + 256.5, + 256.8, + 257.7, + 258.7, + 253.6, + 252.2, + 251.6, + 251.0, + 251.5, + 252.9, + 251.5, + 249.3, + 244.7, + 242.7, + 242.7, + 240.6, + 242.4, + 248.5, + 248.8, + 265.8, + 269.7, + 270.9, + 270.4, + 269.2, + 271.3, + 272.1, + 272.4, + 272.5, + 272.7, + 273.0, + 273.5, + 274.1, + 274.7, + 275.4, + 275.9, + 276.3, + 276.8, + 277.6, + 278.3, + 279.1, + 279.8, + 280.5, + 280.6, + 280.3, + 266.9, + 265.4, + 261.0, + 259.6, + 258.2, + 256.0, + 255.5, + 255.1, + 257.3, + 257.7, + 258.2, + 257.1, + 260.2, + 258.5, + 260.7, + 260.2, + 260.6, + 264.8, + 267.5, + 274.2, + 275.5, + 276.9, + 278.5, + 279.6, + 280.7, + 281.8, + 282.9, + 282.9, + 283.2, + 284.6, + 283.9, + 281.7, + 280.7, + ], + [ + 272.9, + 278.0, + 266.3, + 264.1, + 264.1, + 261.7, + 260.5, + 260.0, + 258.1, + 256.2, + 255.4, + 254.6, + 254.8, + 255.0, + 254.6, + 256.0, + 254.5, + 254.0, + 255.1, + 256.0, + 256.1, + 256.0, + 257.0, + 257.5, + 256.7, + 254.9, + 252.3, + 253.8, + 251.8, + 248.4, + 245.3, + 242.5, + 240.9, + 240.6, + 239.5, + 239.7, + 245.4, + 244.2, + 260.7, + 268.4, + 269.1, + 268.7, + 240.9, + 267.9, + 269.2, + 270.1, + 270.8, + 271.1, + 272.0, + 272.8, + 273.2, + 274.0, + 274.8, + 275.5, + 276.1, + 276.5, + 277.1, + 277.9, + 278.7, + 279.4, + 280.0, + 279.9, + 267.4, + 266.1, + 263.0, + 261.9, + 258.8, + 256.7, + 255.0, + 255.2, + 254.8, + 254.5, + 256.1, + 255.6, + 256.0, + 268.1, + 253.7, + 258.8, + 255.8, + 255.8, + 261.0, + 266.4, + 273.4, + 275.0, + 276.6, + 278.6, + 279.7, + 280.2, + 281.0, + 281.7, + 282.3, + 282.5, + 283.6, + 282.1, + 269.5, + 272.3, + ], + [ + 279.5, + 278.6, + 276.0, + 264.8, + 272.6, + 272.6, + 259.8, + 257.3, + 254.5, + 251.7, + 250.6, + 250.5, + 250.5, + 250.1, + 250.8, + 252.4, + 253.3, + 251.8, + 252.0, + 252.7, + 252.6, + 252.2, + 252.9, + 254.6, + 255.4, + 255.2, + 255.5, + 251.6, + 249.8, + 244.5, + 243.6, + 239.9, + 238.9, + 237.6, + 238.1, + 239.3, + 240.5, + 250.0, + 255.8, + 266.6, + 266.7, + 267.2, + 245.0, + 243.8, + 265.6, + 267.4, + 268.2, + 268.7, + 270.2, + 271.3, + 272.1, + 272.9, + 273.6, + 274.7, + 275.5, + 276.4, + 277.0, + 277.7, + 278.3, + 278.6, + 278.1, + 269.4, + 266.8, + 263.5, + 263.3, + 262.7, + 260.3, + 257.5, + 254.8, + 253.6, + 252.4, + 251.2, + 251.1, + 251.3, + 262.3, + 268.0, + 249.2, + 256.3, + 252.8, + 250.1, + 256.0, + 271.0, + 273.2, + 275.2, + 277.1, + 278.8, + 279.9, + 280.3, + 280.9, + 281.6, + 282.6, + 282.9, + 283.4, + 282.9, + 280.9, + 270.4, + ], + [ + 279.7, + 277.9, + 275.4, + 273.1, + 257.7, + 272.2, + 269.6, + 250.4, + 251.0, + 248.2, + 245.6, + 246.1, + 246.9, + 246.0, + 247.1, + 248.1, + 248.7, + 248.0, + 248.3, + 248.7, + 248.9, + 248.9, + 248.9, + 249.6, + 250.4, + 250.2, + 247.7, + 246.4, + 244.8, + 244.6, + 241.7, + 239.0, + 238.3, + 236.0, + 233.8, + 235.6, + 236.8, + 240.2, + 250.0, + 258.6, + 257.6, + 257.7, + 256.0, + 242.0, + 257.2, + 264.2, + 264.5, + 265.3, + 266.7, + 268.3, + 269.3, + 271.1, + 272.2, + 272.0, + 273.1, + 274.7, + 275.8, + 276.5, + 277.1, + 277.2, + 266.4, + 266.3, + 262.2, + 262.1, + 260.9, + 261.2, + 259.3, + 256.6, + 254.0, + 251.6, + 249.0, + 248.4, + 251.3, + 250.0, + 253.9, + 263.0, + 245.9, + 252.7, + 254.5, + 246.1, + 268.6, + 271.9, + 273.7, + 275.7, + 277.1, + 278.2, + 279.5, + 280.0, + 280.7, + 281.3, + 281.8, + 281.8, + 281.7, + 282.2, + 281.6, + 265.3, + ], + [ + 279.8, + 277.7, + 255.1, + 258.3, + 255.5, + 270.2, + 266.8, + 246.2, + 247.0, + 245.2, + 242.0, + 243.4, + 243.2, + 243.2, + 244.7, + 245.2, + 244.9, + 243.2, + 243.9, + 244.2, + 244.7, + 244.7, + 244.2, + 244.3, + 245.4, + 243.4, + 240.8, + 239.1, + 238.9, + 238.3, + 236.7, + 236.1, + 234.8, + 233.7, + 232.2, + 231.0, + 229.6, + 237.8, + 243.5, + 244.1, + 242.9, + 240.6, + 250.7, + 250.6, + 239.3, + 252.1, + 250.7, + 250.8, + 256.8, + 263.2, + 267.1, + 268.8, + 269.6, + 250.4, + 257.9, + 258.2, + 259.1, + 262.1, + 261.1, + 259.5, + 259.4, + 261.1, + 259.4, + 257.8, + 257.6, + 257.5, + 255.0, + 254.4, + 251.8, + 248.8, + 245.5, + 249.3, + 250.1, + 249.3, + 250.3, + 252.9, + 246.2, + 249.3, + 255.3, + 261.8, + 268.1, + 271.6, + 273.4, + 274.7, + 275.4, + 276.2, + 278.5, + 279.4, + 280.3, + 280.9, + 281.3, + 281.2, + 281.0, + 281.4, + 281.4, + 280.7, + ], + [ + 278.7, + 277.8, + 264.9, + 264.0, + 254.2, + 267.9, + 249.4, + 242.7, + 244.0, + 242.0, + 240.6, + 241.8, + 241.4, + 241.1, + 241.8, + 241.7, + 240.0, + 238.2, + 238.6, + 239.1, + 239.4, + 239.5, + 239.9, + 240.3, + 240.7, + 238.7, + 236.5, + 235.7, + 234.5, + 233.6, + 232.0, + 229.2, + 230.4, + 227.2, + 226.2, + 229.9, + 232.8, + 235.1, + 237.5, + 239.0, + 239.8, + 240.1, + 240.2, + 241.7, + 245.4, + 245.5, + 246.0, + 244.4, + 255.6, + 260.1, + 262.5, + 266.9, + 267.4, + 250.2, + 259.4, + 258.6, + 258.3, + 257.9, + 256.5, + 254.3, + 254.3, + 256.5, + 256.6, + 254.8, + 251.6, + 252.2, + 251.5, + 249.3, + 246.8, + 245.4, + 244.0, + 242.9, + 248.6, + 248.7, + 248.8, + 248.2, + 249.5, + 252.3, + 246.9, + 258.9, + 267.3, + 270.3, + 272.2, + 248.1, + 253.5, + 273.7, + 276.3, + 277.8, + 279.1, + 279.8, + 281.3, + 281.1, + 280.7, + 280.8, + 280.4, + 279.8, + ], + [ + 276.4, + 276.5, + 275.5, + 275.0, + 250.6, + 252.0, + 256.1, + 241.6, + 241.3, + 241.2, + 239.5, + 239.0, + 238.2, + 239.2, + 238.2, + 237.3, + 236.0, + 234.8, + 234.5, + 234.7, + 235.0, + 234.5, + 234.9, + 236.3, + 236.9, + 235.5, + 234.5, + 233.6, + 231.4, + 229.8, + 228.6, + 228.8, + 227.9, + 227.4, + 232.3, + 234.1, + 234.0, + 232.7, + 232.6, + 234.1, + 233.7, + 233.4, + 235.4, + 237.4, + 237.3, + 240.4, + 241.4, + 242.2, + 251.4, + 252.7, + 255.0, + 261.0, + 261.3, + 245.5, + 252.5, + 252.0, + 250.5, + 251.8, + 252.1, + 252.0, + 251.6, + 252.7, + 252.8, + 248.1, + 247.8, + 248.3, + 247.9, + 245.9, + 244.3, + 243.9, + 243.3, + 243.7, + 243.5, + 242.5, + 247.1, + 246.3, + 247.3, + 246.6, + 245.6, + 252.1, + 266.7, + 269.9, + 271.9, + 249.9, + 252.0, + 243.4, + 273.0, + 270.7, + 270.1, + 271.5, + 278.6, + 255.6, + 257.5, + 277.9, + 277.3, + 276.7, + ], + [ + 274.7, + 274.7, + 274.0, + 273.4, + 240.9, + 248.1, + 247.8, + 246.6, + 243.0, + 240.4, + 237.7, + 244.3, + 241.4, + 241.3, + 232.1, + 232.7, + 233.6, + 232.8, + 231.5, + 231.0, + 230.8, + 230.1, + 230.8, + 232.7, + 234.3, + 232.2, + 231.2, + 230.9, + 229.7, + 228.1, + 227.9, + 227.8, + 227.4, + 228.2, + 229.6, + 229.1, + 229.2, + 230.8, + 231.3, + 231.9, + 231.8, + 231.5, + 231.8, + 234.2, + 236.6, + 238.2, + 240.1, + 241.3, + 240.1, + 238.5, + 239.6, + 243.8, + 255.4, + 242.5, + 250.6, + 249.5, + 245.8, + 245.5, + 244.6, + 244.6, + 244.8, + 244.0, + 244.3, + 245.4, + 245.0, + 244.5, + 243.8, + 243.1, + 242.6, + 242.6, + 241.2, + 242.2, + 244.2, + 243.3, + 240.8, + 245.5, + 246.3, + 245.5, + 243.6, + 247.8, + 266.3, + 269.5, + 247.1, + 253.6, + 249.5, + 247.9, + 247.0, + 250.8, + 266.3, + 261.0, + 263.1, + 270.9, + 274.4, + 275.1, + 274.2, + 273.9, + ], + [ + 272.2, + 272.7, + 272.0, + 271.5, + 270.3, + 270.5, + 242.2, + 243.8, + 247.3, + 263.6, + 258.9, + 246.2, + 240.0, + 240.6, + 239.1, + 235.5, + 236.5, + 238.7, + 229.9, + 227.0, + 227.7, + 226.9, + 226.5, + 229.8, + 232.2, + 230.6, + 230.0, + 229.6, + 229.5, + 228.5, + 227.8, + 227.6, + 227.5, + 228.6, + 229.5, + 229.6, + 229.0, + 228.5, + 228.8, + 229.7, + 230.1, + 230.4, + 231.9, + 238.6, + 238.6, + 238.3, + 238.2, + 237.5, + 239.9, + 244.9, + 246.5, + 245.2, + 248.3, + 255.0, + 234.9, + 238.8, + 242.9, + 248.6, + 248.2, + 250.8, + 250.9, + 247.1, + 248.3, + 241.2, + 241.0, + 240.8, + 240.6, + 240.3, + 239.9, + 244.3, + 242.7, + 240.5, + 241.9, + 241.4, + 237.7, + 237.7, + 244.5, + 244.5, + 247.1, + 252.4, + 266.4, + 269.8, + 249.9, + 253.8, + 245.5, + 238.3, + 238.4, + 242.4, + 247.6, + 249.2, + 245.4, + 255.7, + 264.5, + 264.9, + 266.0, + 269.0, + ], + [ + 269.0, + 269.4, + 269.4, + 269.8, + 271.1, + 270.8, + 268.2, + 266.5, + 265.6, + 264.8, + 261.3, + 246.1, + 235.9, + 237.4, + 238.2, + 235.0, + 235.9, + 237.4, + 237.4, + 228.2, + 237.7, + 235.9, + 223.6, + 225.8, + 227.0, + 228.1, + 227.4, + 226.5, + 226.1, + 226.3, + 226.9, + 228.1, + 227.3, + 226.7, + 227.5, + 235.2, + 235.5, + 235.8, + 235.5, + 235.1, + 235.1, + 235.6, + 236.4, + 238.5, + 239.4, + 239.6, + 240.6, + 240.2, + 239.7, + 241.4, + 243.6, + 241.9, + 241.5, + 251.9, + 244.3, + 240.9, + 241.6, + 242.6, + 242.3, + 243.6, + 243.1, + 242.9, + 245.9, + 238.0, + 238.6, + 238.3, + 238.3, + 240.3, + 241.8, + 244.4, + 243.1, + 238.5, + 239.1, + 239.7, + 239.8, + 242.1, + 247.8, + 245.0, + 251.2, + 264.4, + 268.3, + 269.9, + 232.1, + 250.7, + 244.8, + 242.9, + 241.2, + 237.9, + 239.4, + 246.4, + 245.0, + 249.2, + 248.3, + 247.0, + 251.1, + 263.6, + ], + [ + 267.8, + 268.5, + 268.3, + 269.1, + 269.9, + 268.6, + 266.7, + 265.9, + 264.3, + 262.9, + 255.5, + 237.1, + 230.7, + 236.7, + 238.3, + 235.6, + 234.2, + 235.0, + 234.6, + 237.0, + 240.1, + 234.9, + 234.3, + 231.2, + 225.1, + 227.0, + 228.0, + 227.5, + 227.0, + 226.7, + 228.7, + 237.0, + 234.4, + 235.6, + 236.8, + 236.5, + 235.3, + 235.5, + 236.0, + 236.8, + 237.3, + 237.7, + 237.5, + 237.0, + 237.2, + 237.3, + 237.9, + 238.6, + 238.3, + 238.3, + 239.5, + 239.2, + 238.4, + 240.3, + 238.0, + 237.5, + 238.4, + 239.1, + 239.2, + 239.7, + 240.3, + 241.0, + 242.1, + 241.8, + 241.6, + 240.7, + 240.6, + 241.3, + 242.6, + 243.3, + 242.9, + 242.6, + 236.4, + 238.7, + 241.0, + 250.4, + 251.0, + 253.4, + 262.5, + 268.7, + 268.8, + 231.8, + 243.0, + 241.4, + 239.4, + 238.4, + 234.8, + 235.0, + 237.2, + 241.8, + 242.2, + 247.0, + 246.7, + 247.1, + 248.8, + 257.9, + ], + [ + 266.3, + 268.4, + 267.8, + 267.9, + 267.3, + 266.4, + 265.5, + 264.8, + 262.8, + 262.1, + 259.4, + 245.0, + 231.9, + 235.1, + 237.9, + 236.7, + 235.8, + 235.2, + 235.2, + 236.9, + 236.9, + 234.1, + 232.7, + 231.0, + 231.8, + 233.7, + 234.0, + 225.9, + 227.3, + 236.4, + 237.8, + 239.8, + 236.5, + 236.2, + 236.3, + 236.5, + 236.3, + 237.0, + 237.4, + 237.8, + 238.1, + 237.6, + 236.9, + 236.0, + 235.8, + 236.1, + 236.8, + 237.4, + 237.5, + 237.2, + 237.3, + 237.1, + 237.1, + 237.9, + 237.9, + 238.0, + 238.1, + 237.9, + 237.4, + 237.1, + 237.5, + 238.2, + 238.8, + 239.8, + 240.2, + 240.6, + 240.6, + 240.6, + 240.6, + 241.0, + 242.2, + 243.4, + 236.8, + 239.2, + 240.8, + 241.8, + 249.6, + 254.1, + 240.6, + 239.5, + 239.6, + 236.0, + 232.0, + 229.8, + 229.2, + 228.7, + 227.9, + 229.8, + 234.4, + 238.3, + 241.0, + 250.1, + 247.6, + 247.8, + 249.5, + 254.9, + ], + [ + 266.7, + 266.9, + 265.6, + 265.9, + 265.2, + 264.6, + 264.6, + 262.5, + 250.8, + 241.4, + 240.2, + 239.9, + 236.9, + 238.9, + 240.3, + 238.4, + 236.9, + 236.5, + 234.4, + 233.6, + 234.0, + 234.2, + 233.2, + 233.4, + 235.1, + 233.9, + 231.8, + 232.0, + 232.3, + 234.2, + 233.9, + 235.5, + 235.1, + 235.7, + 236.7, + 237.4, + 237.2, + 237.1, + 237.0, + 236.9, + 237.4, + 237.5, + 237.4, + 236.3, + 235.6, + 235.6, + 235.7, + 235.6, + 235.7, + 235.7, + 235.7, + 235.6, + 235.7, + 235.7, + 235.5, + 235.5, + 235.5, + 235.4, + 235.2, + 235.1, + 235.5, + 236.5, + 237.0, + 237.1, + 237.2, + 237.3, + 237.3, + 237.2, + 237.6, + 239.0, + 241.1, + 246.3, + 232.2, + 234.5, + 234.2, + 231.2, + 231.1, + 233.8, + 236.8, + 238.4, + 236.6, + 236.1, + 234.8, + 235.3, + 233.6, + 233.4, + 235.0, + 237.4, + 239.0, + 239.4, + 240.2, + 241.5, + 242.2, + 244.1, + 247.4, + 257.0, + ], + [ + 247.2, + 247.8, + 250.1, + 246.0, + 246.8, + 247.5, + 245.6, + 243.0, + 238.0, + 235.9, + 237.4, + 238.7, + 238.8, + 239.1, + 240.2, + 236.9, + 235.1, + 236.5, + 234.2, + 231.7, + 231.0, + 231.8, + 232.2, + 232.4, + 235.5, + 235.5, + 236.2, + 235.3, + 234.9, + 235.0, + 235.4, + 236.6, + 236.9, + 237.6, + 238.0, + 238.2, + 238.0, + 238.2, + 238.3, + 238.1, + 238.4, + 238.2, + 238.0, + 237.4, + 236.9, + 237.3, + 236.8, + 236.2, + 236.2, + 236.3, + 236.1, + 235.6, + 235.2, + 234.8, + 234.3, + 233.9, + 233.5, + 233.2, + 232.8, + 232.6, + 233.1, + 234.0, + 234.2, + 234.4, + 234.2, + 234.0, + 233.9, + 233.9, + 233.8, + 234.4, + 235.7, + 238.1, + 235.7, + 235.8, + 240.5, + 224.1, + 225.3, + 227.2, + 232.5, + 240.2, + 239.2, + 238.6, + 239.2, + 239.4, + 235.6, + 235.0, + 233.9, + 235.2, + 235.9, + 236.5, + 239.9, + 238.9, + 238.5, + 239.7, + 242.2, + 245.1, + ], + [ + 237.3, + 237.3, + 238.2, + 237.7, + 238.5, + 239.3, + 238.2, + 236.9, + 235.5, + 236.1, + 237.4, + 237.9, + 237.4, + 237.0, + 237.1, + 236.0, + 235.7, + 236.5, + 236.7, + 236.4, + 235.3, + 234.4, + 234.8, + 234.9, + 235.4, + 235.8, + 236.0, + 235.9, + 236.1, + 236.4, + 236.9, + 237.4, + 237.9, + 238.3, + 238.7, + 238.8, + 238.8, + 238.6, + 238.6, + 238.6, + 238.5, + 238.4, + 238.4, + 238.2, + 238.0, + 238.1, + 237.6, + 236.9, + 236.6, + 236.4, + 236.1, + 235.7, + 235.5, + 235.2, + 234.8, + 234.4, + 234.1, + 233.5, + 232.7, + 231.8, + 231.6, + 232.0, + 232.2, + 232.9, + 233.0, + 232.7, + 232.3, + 232.0, + 231.9, + 232.1, + 232.4, + 233.0, + 233.1, + 234.2, + 236.6, + 232.8, + 232.1, + 232.7, + 233.6, + 236.6, + 235.3, + 235.1, + 236.3, + 234.4, + 234.0, + 234.8, + 235.8, + 237.1, + 238.1, + 239.5, + 240.2, + 240.6, + 239.4, + 238.3, + 238.6, + 238.1, + ], + [ + 231.9, + 231.5, + 231.7, + 231.8, + 232.1, + 232.1, + 231.8, + 231.6, + 231.6, + 232.3, + 233.4, + 233.9, + 233.7, + 233.5, + 233.6, + 234.0, + 235.2, + 236.4, + 237.1, + 237.5, + 237.2, + 237.2, + 237.4, + 237.6, + 237.9, + 238.2, + 238.5, + 238.7, + 238.8, + 238.8, + 238.9, + 239.0, + 239.1, + 239.1, + 239.1, + 239.0, + 238.9, + 239.0, + 238.5, + 238.3, + 238.1, + 238.1, + 238.2, + 238.2, + 238.1, + 238.0, + 237.9, + 237.8, + 237.6, + 237.3, + 237.1, + 237.2, + 237.1, + 236.9, + 236.6, + 236.4, + 236.2, + 235.6, + 235.0, + 234.7, + 234.3, + 233.6, + 233.3, + 233.8, + 235.0, + 235.9, + 235.3, + 234.1, + 234.0, + 234.1, + 233.5, + 233.1, + 233.1, + 233.7, + 234.0, + 232.8, + 232.8, + 233.1, + 233.4, + 233.7, + 233.0, + 232.6, + 232.8, + 233.1, + 233.6, + 234.3, + 234.8, + 235.2, + 235.4, + 235.3, + 235.0, + 234.6, + 234.0, + 233.5, + 233.5, + 232.7, + ], + [ + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + 235.4, + ], + ] + ] + + +def _make_cfa_file(filename): + n = netCDF4.Dataset(filename, "w", format="NETCDF4") + + n.Conventions = f"CF-{VN} CFA-0.6.2" + n.comment = ( + "A CFA-netCDF file with non-standarised aggregation instructions" + ) + + n.createDimension("time", 12) + level = n.createDimension("level", 1) + lat = n.createDimension("lat", 73) + lon = n.createDimension("lon", 144) + n.createDimension("f_time", 2) + n.createDimension("f_level", 1) + n.createDimension("f_lat", 1) + n.createDimension("f_lon", 1) + n.createDimension("i", 4) + n.createDimension("j", 2) + + lon = n.createVariable("lon", "f4", ("lon",)) + lon.standard_name = "longitude" + lon.units = "degrees_east" + + lat = n.createVariable("lat", "f4", ("lat",)) + lat.standard_name = "latitude" + lat.units = "degrees_north" + + time = n.createVariable("time", "f4", ("time",)) + time.standard_name = "time" + time.units = "days since 2000-01-01" + + level = n.createVariable("level", "f4", ("level",)) + + tas = n.createVariable("tas", "f4", ()) + tas.standard_name = "air_temperature" + tas.units = "K" + tas.aggregated_dimensions = "time level lat lon" + tas.aggregated_data = "location: aggregation_location file: aggregation_file format: aggregation_format address: aggregation_address tracking_id: aggregation_tracking_id" + + loc = n.createVariable("aggregation_location", "i4", ("i", "j")) + loc[0, :] = 6 + loc[1, 0] = level.size + loc[2, 0] = lat.size + loc[3, 0] = lon.size + + fil = n.createVariable( + "aggregation_file", str, ("f_time", "f_level", "f_lat", "f_lon") + ) + fil[0, 0, 0, 0] = "January-June.nc" + fil[1, 0, 0, 0] = "July-December.nc" + + add = n.createVariable( + "aggregation_address", str, ("f_time", "f_level", "f_lat", "f_lon") + ) + add[0, 0, 0, 0] = "tas0" + add[1, 0, 0, 0] = "tas1" + + fmt = n.createVariable("aggregation_format", str, ()) + fmt[()] = "nc" + + tid = n.createVariable( + "aggregation_tracking_id", str, ("f_time", "f_level", "f_lat", "f_lon") + ) + tid[0, 0, 0, 0] = "tracking_id0" + tid[1, 0, 0, 0] = "tracking_id1" + + n.close() + + return filename + + +broken_bounds_file = _make_broken_bounds_cdl("broken_bounds.cdl") + +regrid_file = _make_regrid_file("regrid.nc") + +cfa_file = _make_cfa_file("cfa.nc") + +if __name__ == "__main__": + print("Run date:", datetime.datetime.now()) + cf.environment() + print() + unittest.main(verbosity=2) diff --git a/cf/test/individual_tests.sh b/cf/test/individual_tests.sh index f02a941197..fea95ef58b 100755 --- a/cf/test/individual_tests.sh +++ b/cf/test/individual_tests.sh @@ -1,12 +1,14 @@ #!/bin/bash -file=create_test_files.py -echo "Running $file" -python $file -rc=$? -if [[ $rc != 0 ]]; then - exit $rc -fi +for file in create_test_files*.py +do + echo "Running $file" + python $file + rc=$? + if [[ $rc != 0 ]]; then + exit $rc + fi +done file=setup_create_field.py echo "Running $file" diff --git a/cf/test/run_tests.py b/cf/test/run_tests.py index c154fa0317..6026c4e123 100644 --- a/cf/test/run_tests.py +++ b/cf/test/run_tests.py @@ -64,6 +64,10 @@ def add_doctests(test_suite): test_loader().discover(test_dir, pattern="create_test_files.py") ) +testsuite_setup_0b = unittest.TestSuite() +testsuite_setup_0b.addTests( + test_loader().discover(test_dir, pattern="create_test_files_2.py") +) # Build the test suite from the tests found in the test files. testsuite_setup_1 = unittest.TestSuite() testsuite_setup_1.addTests( @@ -83,6 +87,7 @@ def add_doctests(test_suite): def run_test_suite_setup_0(verbosity=2): runner = unittest.TextTestRunner(verbosity=verbosity) runner.run(testsuite_setup_0) + runner.run(testsuite_setup_0b) def run_doctests_only(verbosity=2): diff --git a/cf/test/test_CellConnectivity.py b/cf/test/test_CellConnectivity.py new file mode 100644 index 0000000000..61515b9605 --- /dev/null +++ b/cf/test/test_CellConnectivity.py @@ -0,0 +1,104 @@ +import datetime +import faulthandler +import unittest + +faulthandler.enable() # to debug seg faults and timeouts + +import cf + +# Create testcell connectivity object +c = cf.CellConnectivity() +c.set_properties({"long_name": "neighbour faces for faces"}) +c.nc_set_variable("Mesh2_face_links") +data = cf.Data( + [ + [0, 1, 2, -99, -99], + [1, 0, -99, -99, -99], + [2, 0, -99, -99, -99], + ], + dtype="i4", +) +data.masked_values(-99, inplace=True) +c.set_data(data) +c.set_connectivity("edge") + + +class CellConnectivityTest(unittest.TestCase): + """Unit test for the CellConnectivity class.""" + + c = c + + def setUp(self): + """Preparations called immediately before each test method.""" + # Disable log messages to silence expected warnings + cf.log_level("DISABLE") + # Note: to enable all messages for given methods, lines or + # calls (those without a 'verbose' option to do the same) + # e.g. to debug them, wrap them (for methods, start-to-end + # internally) as follows: + # + # cf.LOG_LEVEL('DEBUG') + # < ... test code ... > + # cf.log_level('DISABLE') + + def test_CellConnectivity__repr__str__dump(self): + """Test all means of CellConnectivity inspection.""" + c = self.c + self.assertEqual( + repr(c), "" + ) + self.assertEqual(str(c), "connectivity:edge(3, 5) ") + self.assertEqual( + c.dump(display=False), + """Cell Connectivity: connectivity:edge + long_name = 'neighbour faces for faces' + Data(3, 5) = [[0, ..., --]]""", + ) + + def test_CellConnectivity_copy(self): + """Test the copy of CellConnectivity.""" + c = self.c + self.assertTrue(c.equals(c.copy())) + + def test_CellConnectivity_data(self): + """Test the data of CellConnectivity.""" + c = self.c + self.assertEqual(c.ndim, 1) + + def test_CellConnectivity_connectivity(self): + """Test the 'connectivity' methods of CellConnectivity.""" + c = self.c.copy() + self.assertTrue(c.has_connectivity()) + self.assertEqual(c.get_connectivity(), "edge") + self.assertEqual(c.del_connectivity(), "edge") + self.assertFalse(c.has_connectivity()) + self.assertIsNone(c.get_connectivity(None)) + self.assertIsNone(c.del_connectivity(None)) + + with self.assertRaises(ValueError): + c.get_connectivity() + + with self.assertRaises(ValueError): + c.del_connectivity() + + self.assertIsNone(c.set_connectivity("edge")) + self.assertTrue(c.has_connectivity()) + self.assertEqual(c.get_connectivity(), "edge") + + def test_CellConnectivity_transpose(self): + """Test the 'transpose' method of CellConnectivity.""" + c = self.c.copy() + d = c.transpose() + self.assertTrue(c.equals(d)) + self.assertIsNone(c.transpose(inplace=True)) + + for axes in ([1], [1, 0], [3]): + with self.assertRaises(ValueError): + c.transpose(axes) + + +if __name__ == "__main__": + print("Run date:", datetime.datetime.now()) + cf.environment() + print() + unittest.main(verbosity=2) diff --git a/cf/test/test_Data.py b/cf/test/test_Data.py index bf269a789c..2d439ba695 100644 --- a/cf/test/test_Data.py +++ b/cf/test/test_Data.py @@ -13,15 +13,7 @@ import dask.array as da import numpy as np - -SCIPY_AVAILABLE = False -try: - from scipy.ndimage import convolve1d - - SCIPY_AVAILABLE = True -# not 'except ImportError' as that can hide nested errors, catch anything: -except Exception: - pass # test with this dependency will then be skipped by unittest +from scipy.ndimage import convolve1d faulthandler.enable() # to debug seg faults and timeouts @@ -638,9 +630,6 @@ def test_Data_apply_masking(self): def test_Data_convolution_filter(self): """Test the `convolution_filter` Data method.""" # raise unittest.SkipTest("GSASL has no PLAIN support") - if not SCIPY_AVAILABLE: - raise unittest.SkipTest("SciPy must be installed for this test.") - d = cf.Data(self.ma, units="m", chunks=(2, 4, 5, 3)) window = [0.1, 0.15, 0.5, 0.15, 0.1] @@ -867,17 +856,6 @@ def test_Data_stats(self): }, ) - # NaN values aren't 'equal' to e/o, so check call works and that some - # representative values are as expected, in this case - s5 = cf.Data([[-2, -1, 0], [1, 2, 3]]).stats(all=True, weights=0) - - self.assertEqual(len(s5), 16) - self.assertEqual(s5["minimum"], -2) - self.assertEqual(s5["sum"], 0) - self.assertEqual(s5["sample_size"], 6) - self.assertTrue(np.isnan(s5["mean"])) - self.assertTrue(np.isnan(s5["variance"])) # needs all=True to show up - def test_Data__init__dtype_mask(self): """Test `__init__` for Data with `dtype` and `mask` keywords.""" for m in (1, 20, True): @@ -2868,7 +2846,6 @@ def test_Data_trigonometric_hyperbolic(self): (d.mask.array == c.mask).all(), "{}, {}, {}, {}".format(method, units, d.array, c), ) - # --- End: for # Also test masking behaviour: masking of invalid data occurs for # np.ma module by default but we don't want that so there is logic @@ -2898,25 +2875,22 @@ def test_Data_trigonometric_hyperbolic(self): self.assertTrue(np.isposinf(g[2])) self.assertIs(g[3], cf.masked) - # AT2 - # - # # Treat arctan2 separately (as is a class method & takes two inputs) - # for x in (1, -1): - # a1 = 0.9 * x * self.ma - # a2 = 0.5 * x * self.a - # # Transform data for 'a' into range more appropriate for inverse: - # a1 = np.sin(a1.data) - # a2 = np.cos(a2.data) - - # c = np.ma.arctan2(a1, a2) - # for units in (None, '', '1', 'radians', 'K'): - # d1 = cf.Data(a1, units=units) - # d2 = cf.Data(a2, units=units) - # e = cf.Data.arctan2(d1, d2) - # # Note: no inplace arg for arctan2 (operates on 2 arrays) - # self.assertEqual(d1.shape, c.shape) - # self.assertTrue((e.array == c).all()) - # self.assertTrue((d1.mask.array == c.mask).all()) + # Treat arctan2 separately (class method taking two inputs) + for x in (1, -1): + a1 = 0.9 * x * self.ma + a2 = 0.5 * x * self.a + # Transform data into range more appropriate for inverse + a1 = np.sin(a1.data) + a2 = np.cos(a2.data) + + c = np.ma.arctan2(a1, a2) + for units in (None, "", "1", "radians", "K"): + d1 = cf.Data(a1, units=units) + d2 = cf.Data(a2, units=units) + e = cf.Data.arctan2(d1, d2) + self.assertEqual(d1.shape, c.shape) + self.assertTrue((e.array == c).all()) + self.assertTrue((d1.mask.array == c.mask).all()) def test_Data_filled(self): """Test the `filled` Data method.""" @@ -4718,6 +4692,45 @@ def test_Data_todict(self): self.assertIn((key, 0), x) self.assertIn((key, 1), x) + def test_Data_masked_values(self): + """Test Data.masked_values.""" + array = np.array([[1, 1.1, 2, 1.1, 3]]) + d = cf.Data(array) + e = d.masked_values(1.1) + ea = e.array + a = np.ma.masked_values(array, 1.1, rtol=cf.rtol(), atol=cf.atol()) + self.assertTrue(np.isclose(ea, a).all()) + self.assertTrue((ea.mask == a.mask).all()) + self.assertIsNone(d.masked_values(1.1, inplace=True)) + self.assertTrue(d.equals(e)) + + array = np.array([[1, 1.1, 2, 1.1, 3]]) + d = cf.Data(array, mask_value=1.1) + da = e.array + self.assertTrue(np.isclose(da, a).all()) + self.assertTrue((da.mask == a.mask).all()) + + def test_Data_sparse_array(self): + """Test Data based on sparse arrays.""" + from scipy.sparse import csr_array + + indptr = np.array([0, 2, 3, 6]) + indices = np.array([0, 2, 2, 0, 1, 2]) + data = np.array([1, 2, 3, 4, 5, 6]) + s = csr_array((data, indices, indptr), shape=(3, 3)) + + d = cf.Data(s) + self.assertFalse((d.sparse_array != s).toarray().any()) + self.assertTrue((d.array == s.toarray()).all()) + + d = cf.Data(s, dtype=float) + self.assertEqual(d.sparse_array.dtype, float) + + # Can't mask sparse array during __init__ + mask = [[0, 0, 1], [0, 0, 0], [0, 0, 0]] + with self.assertRaises(ValueError): + cf.Data(s, mask=mask) + if __name__ == "__main__": print("Run date:", datetime.datetime.now()) diff --git a/cf/test/test_DomainTopology.py b/cf/test/test_DomainTopology.py new file mode 100644 index 0000000000..d7eb533759 --- /dev/null +++ b/cf/test/test_DomainTopology.py @@ -0,0 +1,98 @@ +import datetime +import faulthandler +import unittest + +faulthandler.enable() # to debug seg faults and timeouts + +import cf + +# Create test domain topology object +c = cf.DomainTopology() +c.set_properties({"long_name": "Maps every face to its corner nodes"}) +c.nc_set_variable("Mesh2_face_nodes") +data = cf.Data( + [[2, 3, 1, 0], [6, 7, 3, 2], [1, 3, 8, -99]], + dtype="i4", +) +data.masked_values(-99, inplace=True) +c.set_data(data) +c.set_cell("face") + + +class DomainTopologyTest(unittest.TestCase): + """Unit test for the DomainTopology class.""" + + d = c + + def setUp(self): + """Preparations called immediately before each test method.""" + # Disable log messages to silence expected warnings + cf.log_level("DISABLE") + # Note: to enable all messages for given methods, lines or + # calls (those without a 'verbose' option to do the same) + # e.g. to debug them, wrap them (for methods, start-to-end + # internally) as follows: + # + # cf.LOG_LEVEL('DEBUG') + # < ... test code ... > + # cf.log_level('DISABLE') + + def test_DomainTopology__repr__str__dump(self): + """Test all means of DomainTopology inspection.""" + d = self.d + self.assertEqual(repr(d), "") + self.assertEqual(str(d), "cell:face(3, 4) ") + self.assertEqual( + d.dump(display=False), + """Domain Topology: cell:face + long_name = 'Maps every face to its corner nodes' + Data(3, 4) = [[2, ..., --]]""", + ) + + def test_DomainTopology_copy(self): + """Test the copy of DomainTopology.""" + d = self.d + self.assertTrue(d.equals(d.copy())) + + def test_DomainTopology_data(self): + """Test the data of DomainTopology.""" + d = self.d + self.assertEqual(d.ndim, 1) + + def test_DomainTopology_cell(self): + """Test the 'cell' methods of DomainTopology.""" + d = self.d.copy() + self.assertTrue(d.has_cell()) + self.assertEqual(d.get_cell(), "face") + self.assertEqual(d.del_cell(), "face") + self.assertFalse(d.has_cell()) + self.assertIsNone(d.get_cell(None)) + self.assertIsNone(d.del_cell(None)) + + with self.assertRaises(ValueError): + d.get_cell() + + with self.assertRaises(ValueError): + d.set_cell("bad value") + + self.assertIsNone(d.set_cell("face")) + self.assertTrue(d.has_cell()) + self.assertEqual(d.get_cell(), "face") + + def test_DomainTopology_transpose(self): + """Test the 'transpose' method of DomainTopology.""" + d = self.d.copy() + e = d.transpose() + self.assertTrue(d.equals(e)) + self.assertIsNone(d.transpose(inplace=True)) + + for axes in ([1], [1, 0], [3]): + with self.assertRaises(ValueError): + d.transpose(axes) + + +if __name__ == "__main__": + print("Run date:", datetime.datetime.now()) + cf.environment() + print() + unittest.main(verbosity=2) diff --git a/cf/test/test_Field.py b/cf/test/test_Field.py index 945b119277..fb410ae9e8 100644 --- a/cf/test/test_Field.py +++ b/cf/test/test_Field.py @@ -9,21 +9,7 @@ import numpy import numpy as np - -SCIPY_AVAILABLE = False -try: - from scipy.ndimage import convolve1d - - # In some cases we don't need SciPy directly, since it is required by code - # here which uses 'convolve1d' under-the-hood. Without it installed, get: - # - # NameError: name 'convolve1d' is not defined. Did you - # mean: 'cf_convolve1d'? - - SCIPY_AVAILABLE = True -# not 'except ImportError' as that can hide nested errors, catch anything: -except Exception: - pass # test with this dependency will then be skipped by unittest +from scipy.ndimage import convolve1d faulthandler.enable() # to debug seg faults and timeouts @@ -78,6 +64,10 @@ class FieldTest(unittest.TestCase): os.path.dirname(os.path.abspath(__file__)), "DSG_timeSeriesProfile_indexed_contiguous.nc", ) + ugrid_global = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "ugrid_global_1.nc", + ) chunk_sizes = (100000, 300, 34, 17) original_chunksize = cf.chunksize() @@ -1696,9 +1686,6 @@ def test_Field_autocyclic(self): def test_Field_construct_key(self): self.f.construct_key("grid_longitude") - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_Field_convolution_filter(self): f = cf.read(self.filename1)[0] @@ -1731,9 +1718,6 @@ def test_Field_convolution_filter(self): (gx[:, 1] == [135, 180, 225, 270, 315, 360, 360, 360]).all() ) - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_Field_moving_window(self): weights = cf.Data([1, 2, 3, 10, 5, 6, 7, 8]) / 2 @@ -1874,9 +1858,6 @@ def test_Field_moving_window(self): self.assertEqual(len(g.cell_methods()), len(f.cell_methods()) + 1) - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_Field_derivative(self): f = cf.example_field(0) f[...] = np.arange(9)[1:] * 45 @@ -2336,9 +2317,6 @@ def test_Field_percentile(self): # TODO: add loop to check get same shape and close enough data # for every possible axis combo (see also test_Data_percentile). - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_Field_grad_xy(self): f = cf.example_field(0) @@ -2424,9 +2402,6 @@ def test_Field_grad_xy(self): y.dimension_coordinate("X").standard_name, "longitude" ) - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_Field_laplacian_xy(self): f = cf.example_field(0) @@ -2642,6 +2617,18 @@ def test_Field_auxiliary_to_dimension_to_auxiliary(self): with self.assertRaises(ValueError): f.auxiliary_to_dimension("latitude") + def test_Field_subspace_ugrid(self): + f = cf.read(self.ugrid_global)[0] + + with self.assertRaises(ValueError): + # Can't specify 2 conditions for 1 axis + g = f.subspace(X=cf.wi(40, 70), Y=cf.wi(-20, 30)) + + g = f.subspace(X=cf.wi(40, 70)) + g = g.subspace(Y=cf.wi(-20, 30)) + self.assertTrue(g.aux("X").data.range() < 30) + self.assertTrue(g.aux("Y").data.range() < 50) + if __name__ == "__main__": print("Run date:", datetime.datetime.now()) diff --git a/cf/test/test_Maths.py b/cf/test/test_Maths.py index 342b1ec0b6..0900692aeb 100644 --- a/cf/test/test_Maths.py +++ b/cf/test/test_Maths.py @@ -2,30 +2,12 @@ import faulthandler import unittest - -SCIPY_AVAILABLE = False -try: - # We don't need SciPy directly in this test, it is only required by code - # here which uses 'convolve1d' under-the-hood. Without it installed, get: - # - # NameError: name 'convolve1d' is not defined. Did you - # mean: 'cf_convolve1d'? - import scipy - - SCIPY_AVAILABLE = True -# not 'except ImportError' as that can hide nested errors, catch anything: -except Exception: - pass # test with this dependency will then be skipped by unittest - faulthandler.enable() # to debug seg faults and timeouts import cf class MathTest(unittest.TestCase): - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_curl_xy(self): f = cf.example_field(0) @@ -115,9 +97,6 @@ def test_curl_xy(self): c.dimension_coordinate("X").standard_name, "longitude" ) - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_div_xy(self): f = cf.example_field(0) @@ -206,9 +185,6 @@ def test_div_xy(self): d.dimension_coordinate("X").standard_name, "longitude" ) - @unittest.skipIf( - not SCIPY_AVAILABLE, "scipy must be installed for this test." - ) def test_differential_operators(self): f = cf.example_field(0) diff --git a/cf/test/test_RegridOperator.py b/cf/test/test_RegridOperator.py index 2c1542240f..b20cb0cfcf 100644 --- a/cf/test/test_RegridOperator.py +++ b/cf/test/test_RegridOperator.py @@ -29,6 +29,8 @@ def test_RegridOperator_attributes(self): self.assertEqual(self.r.weights.ndim, 2) self.assertIsNone(self.r.row) self.assertIsNone(self.r.col) + self.assertIsNone(self.r.weights_file) + self.assertEqual(self.r.src_mesh_location, "") def test_RegridOperator_copy(self): self.assertIsInstance(self.r.copy(), self.r.__class__) diff --git a/cf/test/test_UGRID.py b/cf/test/test_UGRID.py new file mode 100644 index 0000000000..7d80ba3166 --- /dev/null +++ b/cf/test/test_UGRID.py @@ -0,0 +1,189 @@ +import atexit +import datetime +import faulthandler +import os +import tempfile +import unittest + +import numpy as np + +faulthandler.enable() # to debug seg faults and timeouts + +import cf + +warnings = False + +# Set up temporary files +n_tmpfiles = 1 +tmpfiles = [ + tempfile.mkstemp("_test_read_write.nc", dir=os.getcwd())[1] + for i in range(n_tmpfiles) +] +[tmpfile1] = tmpfiles + + +def _remove_tmpfiles(): + """Remove temporary files created during tests.""" + for f in tmpfiles: + try: + os.remove(f) + except OSError: + pass + + +atexit.register(_remove_tmpfiles) + + +class UGRIDTest(unittest.TestCase): + """Test UGRID field constructs.""" + + filename1 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "ugrid_1.nc" + ) + + filename2 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "ugrid_2.nc" + ) + + def setUp(self): + """Preparations called immediately before each test method.""" + # Disable log messages to silence expected warnings + cf.LOG_LEVEL("DISABLE") + # Note: to enable all messages for given methods, lines or + # calls (those without a 'verbose' option to do the same) + # e.g. to debug them, wrap them (for methods, start-to-end + # internally) as follows: cf.LOG_LEVEL('DEBUG') + # + # < ... test code ... > + # cf.log_level('DISABLE') + + def test_UGRID_read(self): + """Test reading of UGRID files.""" + f1 = cf.read(self.filename1) + + self.assertEqual(len(f1), 3) + for g in f1: + self.assertEqual(len(g.domain_topologies()), 1) + self.assertEqual(len(g.auxiliary_coordinates()), 2) + self.assertEqual(len(g.dimension_coordinates()), 1) + + for aux in g.auxiliary_coordinates().values(): + self.assertTrue(aux.has_data()) + + if g.domain_topology().get_cell() == "face": + self.assertEqual(len(g.cell_connectivities()), 1) + self.assertEqual( + g.cell_connectivity().get_connectivity(), "edge" + ) + + # Check that all fields have the same mesh id + mesh_ids1 = set(g.get_mesh_id() for g in f1) + self.assertEqual(len(mesh_ids1), 1) + + f2 = cf.read(self.filename2) + self.assertEqual(len(f2), 3) + for g in f2: + self.assertEqual(len(g.domain_topologies()), 1) + self.assertEqual(len(g.auxiliary_coordinates()), 2) + self.assertEqual(len(g.dimension_coordinates()), 1) + + cell = g.domain_topology().get_cell() + if cell in ("edge", "face"): + for aux in g.auxiliary_coordinates().values(): + self.assertFalse(aux.has_data()) + + if cell == "face": + self.assertEqual(len(g.cell_connectivities()), 1) + self.assertEqual( + g.cell_connectivity().get_connectivity(), "edge" + ) + + # Check that all fields have the same mesh id + mesh_ids2 = set(g.get_mesh_id() for g in f2) + self.assertEqual(len(mesh_ids2), 1) + + # Check that the different files have different mesh ids + self.assertNotEqual(mesh_ids1, mesh_ids2) + + def test_UGRID_data(self): + """Test reading of UGRID data.""" + node1, face1, edge1 = cf.read(self.filename1) + node2, face2, edge2 = cf.read(self.filename2) + + # Domain topology arrays + domain_topology1 = face1.domain_topology() + self.assertTrue( + ( + domain_topology1.array + == np.array([[2, 3, 1, 0], [4, 5, 3, 2], [1, 3, 6, -99]]) + ).all() + ) + self.assertTrue(domain_topology1.equals(face2.domain_topology())) + + domain_topology1 = edge1.domain_topology() + self.assertTrue( + ( + domain_topology1.array + == np.array( + [ + [1, 6], + [3, 6], + [3, 1], + [0, 1], + [2, 0], + [2, 3], + [2, 4], + [5, 4], + [3, 5], + ] + ) + ).all() + ) + self.assertTrue(domain_topology1.equals(edge2.domain_topology())) + + # Cell connectivity arrays + cell_connectivity1 = face1.cell_connectivity() + self.assertTrue( + ( + cell_connectivity1.array + == np.array( + [ + [0, 1, 2, -99, -99], + [1, 0, -99, -99, -99], + [2, 0, -99, -99, -99], + ] + ) + ).all() + ) + self.assertTrue(cell_connectivity1.equals(face2.cell_connectivity())) + + def test_read_UGRID_domain(self): + """Test reading of UGRID files into domains.""" + d1 = cf.read(self.filename1, domain=True) + + self.assertEqual(len(d1), 3) + for g in d1: + self.assertIsInstance(g, cf.Domain) + self.assertEqual(len(g.domain_topologies()), 1) + self.assertEqual(len(g.auxiliary_coordinates()), 2) + self.assertEqual(len(g.dimension_coordinates()), 0) + + for aux in g.auxiliary_coordinates().values(): + self.assertTrue(aux.has_data()) + + if g.domain_topology().get_cell() == "face": + self.assertEqual(len(g.cell_connectivities()), 1) + self.assertEqual( + g.cell_connectivity().get_connectivity(), "edge" + ) + + # Check that all domains have the same mesh id + mesh_ids1 = set(g.get_mesh_id() for g in d1) + self.assertEqual(len(mesh_ids1), 1) + + +if __name__ == "__main__": + print("Run date:", datetime.datetime.now()) + cf.environment() + print("") + unittest.main(verbosity=2) diff --git a/cf/test/test_aggregate.py b/cf/test/test_aggregate.py index cd95ad72eb..784e9e5fe1 100644 --- a/cf/test/test_aggregate.py +++ b/cf/test/test_aggregate.py @@ -57,7 +57,7 @@ def test_basic_aggregate(self): g.equals(g0, verbose=2), "g != itself after aggregation" ) - self.assertTrue(h[0].equals(f, verbose=-1), "h[0] != f") + self.assertTrue(h[0].equals(f, verbose=2), "h[0] != f") with warnings.catch_warnings(): warnings.simplefilter("ignore", category=FutureWarning) @@ -610,6 +610,45 @@ def test_climatology_cells(self): condition = cells["T"][0]["cellsize"] self.assertTrue(condition.equals(cf.Y())) + def test_aggregate_ugrid(self): + """Test ugrid aggregation""" + f = cf.example_field(8) + + # Test that aggregation over a non-ugrid axis (time, in this + # case) works. + g = f.copy() + t = g.dim("T") + cf.bounds_combination_mode("OR") + t += 72000 + a = cf.aggregate([f, g]) + self.assertEqual(len(a), 1) + a = a[0] + self.assertEqual(len(a.domain_topologies()), 1) + self.assertEqual(len(a.cell_connectivities()), 1) + + # Test that aggregation over a non-ugrid axis doesn't work + # when the domain topology constructs are different + h = g.copy() + d = h.domain_topology() + d = d.data + d += 1 + self.assertEqual(len(cf.aggregate([f, h])), 2) + + # Test that aggregation over a non-ugrid axis doesn't work + # when the cell connnectivty constructs are different + h = g.copy() + c = h.cell_connectivity() + d = c.data + d += 1 + self.assertEqual(len(cf.aggregate([f, h])), 2) + + # Test that aggregation over a ugrid axis doesn't work + g = f.copy() + x = g.aux("X") + d = x.data + d += 0.1 + self.assertEqual(len(cf.aggregate([f, g])), 2) + if __name__ == "__main__": print("Run date:", datetime.datetime.now()) diff --git a/cf/test/test_collapse.py b/cf/test/test_collapse.py index 6faada8cd5..0525aecddb 100644 --- a/cf/test/test_collapse.py +++ b/cf/test/test_collapse.py @@ -665,16 +665,17 @@ def test_Field_collapse_GROUPS(self): def test_Field_collapse_sum(self): f = cf.example_field(0) - w = f.weights("area", measure=True) + w = f.weights("area", measure=True).persist() a = f.array wa = w.array ws = a * wa + ws_sum = ws.sum() g = f.collapse("area: sum") self.assertTrue((g.array == a.sum()).all()) g = f.collapse("area: sum", weights=w) - self.assertTrue((g.array == ws.sum()).all()) + self.assertTrue((g.array == ws_sum).all()) self.assertEqual(g.Units, cf.Units("1")) g = f.collapse("area: sum", weights=w, scale=1) @@ -682,7 +683,7 @@ def test_Field_collapse_sum(self): self.assertEqual(g.Units, cf.Units("1")) g = f.collapse("area: sum", weights=w) - self.assertTrue((g.array == ws.sum()).all()) + self.assertTrue((g.array == ws_sum).all()) self.assertEqual(g.Units, cf.Units("1")) # Can't set measure=True for 'sum' collapses @@ -691,13 +692,12 @@ def test_Field_collapse_sum(self): def test_Field_collapse_integral(self): f = cf.example_field(0) - w = f.weights("area", measure=True) + w = f.weights("area", measure=True).persist() a = f.array wa = w.array - ws = a * wa g = f.collapse("area: integral", weights=w, measure=True) - self.assertTrue((g.array == ws.sum()).all()) + self.assertTrue((g.array == (a * wa).sum()).all()) self.assertEqual(g.Units, cf.Units("m2")) # Must set the 'weights' parameter for 'integral' collapses @@ -714,7 +714,7 @@ def test_Field_collapse_integral(self): def test_Field_collapse_sum_weights(self): f = cf.example_field(0) - w = f.weights("area", measure=True) + w = f.weights("area", measure=True).persist() wa = w.array g = f.collapse("area: sum_of_weights") @@ -735,25 +735,45 @@ def test_Field_collapse_sum_weights(self): def test_Field_collapse_sum_weights2(self): f = cf.example_field(0) - w = f.weights("area", measure=True) + w = f.weights("area", measure=True).persist() wa = w.array**2 + wa_sum = wa.sum() g = f.collapse("area: sum_of_weights2") self.assertTrue((g.array == 40).all()) self.assertEqual(g.Units, cf.Units()) g = f.collapse("area: sum_of_weights2", weights=w) - self.assertTrue((g.array == wa.sum()).all()) + self.assertTrue((g.array == wa_sum).all()) self.assertEqual(g.Units, cf.Units("1")) g = f.collapse("area: sum_of_weights2", weights=w, measure=True) - self.assertTrue((g.array == wa.sum()).all()) + self.assertTrue((g.array == wa_sum).all()) self.assertEqual(g.Units, cf.Units("m4")) g = f.collapse("area: sum_of_weights2", weights=w, scale=1) self.assertTrue((g.array == (wa / wa.max()).sum()).all()) self.assertEqual(g.Units, cf.Units("1")) + def test_Field_collapse_non_positive_weights(self): + f = cf.example_field(0) + w = f.weights("area").persist() + + for method in ( + "mean", + "sum", + "root_mean_square", + "variance", + "sum_of_weights", + ): + for x in (0, -3.14): + w[0, 0] = x + g = f.collapse(axes="area", method=method, weights=w) + with self.assertRaises(ValueError): + # The check for non-positive weights occurs at + # compute time + g.array + if __name__ == "__main__": print("Run date:", datetime.datetime.now()) diff --git a/cf/test/test_external.py b/cf/test/test_external.py index dabd4d01a7..8d7489f78e 100644 --- a/cf/test/test_external.py +++ b/cf/test/test_external.py @@ -173,7 +173,7 @@ def test_EXTERNAL_AGGREGATE(self): # cell measure. f_lon_thirds = [f[:, :3], f[:, 3:6], f[:, 6:]] - g = cf.aggregate(f_lon_thirds) + g = cf.aggregate(f_lon_thirds, verbose=2) self.assertEqual(len(g), 1) diff --git a/cf/test/test_gathering.py b/cf/test/test_gathering.py index a26350dd18..bafa8efeda 100644 --- a/cf/test/test_gathering.py +++ b/cf/test/test_gathering.py @@ -31,7 +31,7 @@ def _remove_tmpfiles(): atexit.register(_remove_tmpfiles) -class DSGTest(unittest.TestCase): +class GatheringTest(unittest.TestCase): gathered = os.path.join( os.path.dirname(os.path.abspath(__file__)), "gathered.nc" ) diff --git a/cf/test/test_read_write.py b/cf/test/test_read_write.py index e4da245eab..0eefa1b2ac 100644 --- a/cf/test/test_read_write.py +++ b/cf/test/test_read_write.py @@ -336,6 +336,12 @@ def test_write_netcdf_mode(self): if fmt == "NETCDF4_CLASSIC" and ex_field_n in (6, 7): continue + print( + "TODOUGRID: excluding example fields 8, 9, 10 until writing UGRID is enabled" + ) + if ex_field_n in (8, 9, 10): + continue + cf.write(ex_field, tmpfile, fmt=fmt, mode="a") f = cf.read(tmpfile) @@ -404,7 +410,10 @@ def test_write_netcdf_mode(self): # Now do the same test, but appending all of the example fields in # one operation rather than one at a time, to check that it works. cf.write(g, tmpfile, fmt=fmt, mode="w") # 1. overwrite to wipe - append_ex_fields = cf.example_fields() + print( + "TODOUGRID: excluding example fields 8, 9, 10 until writing UGRID is enabled" + ) + append_ex_fields = cf.example_fields(0, 1, 2, 3, 4, 5, 6, 7) del append_ex_fields[1] # note: can remove after Issue #141 closed if fmt in "NETCDF4_CLASSIC": # Remove n=6 and =7 for reasons as given above (del => minus 1) @@ -663,6 +672,7 @@ def test_read_CDL(self): geometry_1_file = os.path.join( os.path.dirname(os.path.abspath(__file__)), "geometry_1.nc" ) + subprocess.run( " ".join(["ncdump", "-h", geometry_1_file, ">", tmpfileh2]), shell=True, diff --git a/cf/test/test_regrid_mesh.py b/cf/test/test_regrid_mesh.py new file mode 100644 index 0000000000..3095640135 --- /dev/null +++ b/cf/test/test_regrid_mesh.py @@ -0,0 +1,294 @@ +import datetime +import faulthandler +import os +import unittest + +faulthandler.enable() # to debug seg faults and timeouts + +import numpy as np + +import cf + +# ESMF renamed its Python module to `esmpy` at ESMF version 8.4.0. Allow +# either for now for backwards compatibility. +esmpy_imported = False +try: + import esmpy + + esmpy_imported = True +except ImportError: + try: + # Take the new name to use in preference to the old one. + import ESMF as esmpy + + esmpy_imported = True + except ImportError: + pass + +all_methods = ( + "linear", + "conservative", + "conservative_2nd", + "nearest_dtos", + "nearest_stod", + "patch", +) + + +# Set numerical comparison tolerances +atol = 2e-12 +rtol = 0 + +meshloc = { + "face": esmpy.MeshLoc.ELEMENT, + "node": esmpy.MeshLoc.NODE, +} + + +def esmpy_regrid(coord_sys, method, src, dst, **kwargs): + """Helper function that regrids one dimension of Field data using + pure esmpy. + + Used to verify `cf.Field.regridc` + + :Returns: + + Regridded numpy masked array. + + """ + esmpy_regrid = cf.regrid.regrid( + coord_sys, + src, + dst, + method, + return_esmpy_regrid_operator=True, + **kwargs + ) + + src_meshloc = None + dst_meshloc = None + + domain_topology = src.domain_topology(default=None) + if domain_topology is not None: + src_meshloc = meshloc[domain_topology.get_cell()] + + domain_topology = dst.domain_topology(default=None) + if domain_topology is not None: + dst_meshloc = meshloc[domain_topology.get_cell()] + + src_field = esmpy.Field( + esmpy_regrid.srcfield.grid, meshloc=src_meshloc, name="src" + ) + dst_field = esmpy.Field( + esmpy_regrid.dstfield.grid, meshloc=dst_meshloc, name="dst" + ) + + fill_value = 1e20 + array = np.squeeze(src.array) + if array.shape != src_field.data.shape: + array = array.transpose() + + src_field.data[...] = np.ma.MaskedArray(array, copy=False).filled( + fill_value + ) + dst_field.data[...] = fill_value + + esmpy_regrid(src_field, dst_field, zero_region=esmpy.Region.SELECT) + + out = dst_field.data + + return np.ma.MaskedArray(out.copy(), mask=(out == fill_value)) + + +class RegridMeshTest(unittest.TestCase): + # Get the test source and destination fields + src_mesh_file = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "ugrid_global_1.nc" + ) + dst_mesh_file = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "ugrid_global_2.nc" + ) + grid_file = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "regrid.nc" + ) + + src_mesh = cf.read(src_mesh_file)[0] + dst_mesh = cf.read(dst_mesh_file)[0] + grid = cf.read(grid_file)[0] + + def setUp(self): + """Preparations called immediately before each test method.""" + # Disable log messages to silence expected warnings + cf.log_level("DISABLE") + # Note: to enable all messages for given methods, lines or calls (those + # without a 'verbose' option to do the same) e.g. to debug them, wrap + # them (for methods, start-to-end internally) as follows: + # cfdm.log_level('DEBUG') + # < ... test code ... > + # cfdm.log_level('DISABLE') + + @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") + def test_Field_regrid_mesh_to_mesh(self): + self.assertFalse(cf.regrid_logging()) + + dst = self.dst_mesh.copy() + src = self.src_mesh.copy() + + # Mask some destination grid points + dst[0, 2:35] = cf.masked + + coord_sys = "spherical" + + for src_masked in (False, True): + if src_masked: + src = src.copy() + src[100:200] = cf.masked + + # Loop over whether or not to use the destination grid + # masked points + for use_dst_mask in (False, True): + for method in all_methods: + x = src.regrids( + dst, method=method, use_dst_mask=use_dst_mask + ) + a = x.array + + y = esmpy_regrid( + coord_sys, + method, + src, + dst, + use_dst_mask=use_dst_mask, + ) + + self.assertTrue(np.allclose(y, a, atol=atol, rtol=rtol)) + + if isinstance(a, np.ma.MaskedArray): + self.assertTrue((y.mask == a.mask).all()) + else: + self.assertFalse(y.mask.any()) + + @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") + def test_Field_regrid_mesh_to_grid(self): + self.assertFalse(cf.regrid_logging()) + + dst = self.grid.copy() + src = self.src_mesh.copy() + + # Mask some destination grid points + dst[0, 30, 2:35] = cf.masked + + coord_sys = "spherical" + + for src_masked in (False, True): + if src_masked: + src = src.copy() + src[100:200] = cf.masked + + # Loop over whether or not to use the destination grid + # masked points + for use_dst_mask in (False, True): + for method in all_methods: + if method == "nearest_dtos": + continue + + x = src.regrids( + dst, method=method, use_dst_mask=use_dst_mask + ) + a = x.array + a = a.transpose() + + y = esmpy_regrid( + coord_sys, + method, + src, + dst, + use_dst_mask=use_dst_mask, + ) + + self.assertTrue(np.allclose(y, a, atol=atol, rtol=rtol)) + + if isinstance(a, np.ma.MaskedArray): + self.assertTrue((y.mask == a.mask).all()) + else: + self.assertFalse(y.mask.any()) + + # nearest_dtos doesn't work at the moment + with self.assertRaises(ValueError): + src.regrids(dst, method="nearest_dtos") + + @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") + def test_Field_regrid_grid_to_mesh(self): + self.assertFalse(cf.regrid_logging()) + + src = self.grid.copy() + dst = self.src_mesh.copy() + + # Mask some destination grid points + dst[100:300] = cf.masked + + coord_sys = "spherical" + + for src_masked in (False, True): + if src_masked: + src = src.copy() + src[0, 30:34, 10:80] = cf.masked + + # Loop over whether or not to use the destination grid + # masked points + for use_dst_mask in (False, True): + for method in all_methods: + if method == "nearest_dtos": + continue + + x = src.regrids( + dst, method=method, use_dst_mask=use_dst_mask + ) + a = x.array + a = np.squeeze(a) + + y = esmpy_regrid( + coord_sys, + method, + src, + dst, + use_dst_mask=use_dst_mask, + ) + + self.assertTrue(np.allclose(y, a, atol=atol, rtol=rtol)) + + if isinstance(a, np.ma.MaskedArray): + self.assertTrue((y.mask == a.mask).all()) + else: + self.assertFalse(y.mask.any()) + + # nearest_dtos doesn't work at the moment + with self.assertRaises(ValueError): + src.regrids(dst, method="nearest_dtos") + + @unittest.skipUnless(esmpy_imported, "Requires esmpy/ESMF package.") + def test_Field_regrid_mesh_cartesian(self): + self.assertFalse(cf.regrid_logging()) + + # Cartesian regridding involving meshes is not currently + # supported + src = self.src_mesh + dst = self.dst_mesh + with self.assertRaises(ValueError): + src.regridc(dst, method="linear") + + dst = self.grid + with self.assertRaises(ValueError): + src.regridc(dst, method="linear") + + src = self.grid + dst = self.dst_mesh + with self.assertRaises(ValueError): + src.regridc(dst, method="linear") + + +if __name__ == "__main__": + print("Run date:", datetime.datetime.now()) + cf.environment() + print("") + unittest.main(verbosity=2) diff --git a/cf/test/test_weights.py b/cf/test/test_weights.py new file mode 100644 index 0000000000..77b300fdb5 --- /dev/null +++ b/cf/test/test_weights.py @@ -0,0 +1,299 @@ +import datetime +import unittest + +import numpy as np + +import cf + +# A radius greater than 1. Used since weights based on the unit +# spehere and non-speheres are tested separately. +r = 2 +radius = cf.Data(r, "m") + +# -------------------------------------------------------------------- +# Spherical polygon geometry with duplicated first/last node. +# +# The cells have areas pi/2 and pi +# The cells have line lengths 3pi/2 and 5pi/2 +# -------------------------------------------------------------------- +gps = cf.example_field(6) +gps.del_construct("auxiliarycoordinate3") +gps.del_construct("grid_mapping_name:latitude_longitude") + +lon = cf.AuxiliaryCoordinate() +lon.standard_name = "longitude" +bounds = cf.Data( + [[315, 45, 45, 315, 999, 999, 999], [90, 90, 0, 45, 45, 135, 90]], + "degrees_east", + mask_value=999, +).reshape(2, 1, 7) +lon.set_bounds(cf.Bounds(data=bounds)) +lon.set_geometry("polygon") + +lat = cf.AuxiliaryCoordinate() +lat.standard_name = "latitude" +bounds = cf.Data( + [[0, 0, 90, 0, 999, 999, 999], [0, 90, 0, 0, -90, 0, 0]], + "degrees_north", + mask_value=999, +).reshape(2, 1, 7) +lat.set_bounds(cf.Bounds(data=bounds)) +lat.set_geometry("polygon") + +gps.del_construct("longitude") +gps.del_construct("latitude") +gps.set_construct(lon, axes="domainaxis0", copy=False) +gps.set_construct(lat, axes="domainaxis0", copy=False) + +# -------------------------------------------------------------------- +# Plane polygon geometry with interior ring and without duplicated +# first/last node +# +# The cells have areas 3 and 8 +# The cells have line lengths 9 and 13 +# -------------------------------------------------------------------- +gppi = gps.copy() +lon = gppi.auxiliary_coordinate("X") +lat = gppi.auxiliary_coordinate("Y") + +lon.override_units("m", inplace=True) +lon.standard_name = "projection_x_coordinate" +bounds = cf.Data( + [ + [ + [2, 2, 0, 0, 999, 999, 999, 999], + [0.5, 1.5, 1.5, 0.5, 999, 999, 999, 999], + ], + [ + [2, 2, 0, 0, 1, 1, 3, 3], + [999, 999, 999, 999, 999, 999, 999, 999], + ], + ], + "m", + mask_value=999, +).reshape(2, 2, 8) +lon.set_bounds(cf.Bounds(data=bounds)) +lon.set_interior_ring(cf.InteriorRing(data=[[0, 1], [0, 0]])) + +lat.override_units("m", inplace=True) +lat.standard_name = "projection_y_coordinate" +bounds = cf.Data( + [ + [ + [-1, 1, 1, -1, 999, 999, 999, 999], + [0.5, 0.5, -0.5, -0.5, 999, 999, 999, 999], + ], + [ + [-1, 1, 1, -1, -1, -3, -3, -1], + [999, 999, 999, 999, 999, 999, 999, 999], + ], + ], + "m", + mask_value=999, +).reshape(2, 2, 8) +lat.set_bounds(cf.Bounds(data=bounds)) +lat.set_interior_ring(cf.InteriorRing(data=[[0, 1], [0, 0]])) + + +class WeightsTest(unittest.TestCase): + def test_weights_polygon_area_geometry(self): + # Spherical polygon geometry weights with duplicated first/last + # node + f = gps.copy() + lon = f.auxiliary_coordinate("X") + lat = f.auxiliary_coordinate("Y") + + # Surface area of unit sphere + sphere_area = 4 * np.pi + correct_weights = np.array([sphere_area / 8, sphere_area / 4]) + + # Spherical polygon geometry weights with duplicated first/last + # node + w = gps.weights("X", great_circle=True) + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("1")) + + w = gps.weights("area", great_circle=True, measure=True, radius=radius) + self.assertTrue((w.array == (r**2) * correct_weights).all()) + self.assertEqual(w.Units, cf.Units("m2")) + + # Spherical polygon geometry weights without duplicated + # first/last node + bounds = cf.Data( + [[315, 45, 45, 999, 999, 999], [90, 90, 0, 45, 45, 135]], + "degrees_east", + mask_value=999, + ).reshape(2, 1, 6) + lon.set_bounds(cf.Bounds(data=bounds)) + + bounds = cf.Data( + [[0, 0, 90, 999, 999, 999], [0, 90, 0, 0, -90, 0]], + "degrees_north", + mask_value=999, + ).reshape(2, 1, 6) + lat.set_bounds(cf.Bounds(data=bounds)) + + w = f.weights("X", great_circle=True) + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("1")) + + w = f.weights("area", great_circle=True, measure=True, radius=radius) + self.assertTrue((w.array == (r**2) * correct_weights).all()) + self.assertEqual(w.Units, cf.Units("m2")) + + # Plane polygon geometry with no duplicated first/last nodes, + # and an interior ring + correct_weights = np.array([3, 8]) + w = gppi.weights("area") + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("1")) + + w = gppi.weights("area", measure=True) + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("m2")) + + def test_weights_polygon_area_ugrid(self): + f = cf.example_field(8) + f = f[..., [0, 2]] + + # Surface area of unit sphere + sphere_area = 4 * np.pi + correct_weights = np.array([sphere_area / 8, sphere_area / 4]) + + # Spherical polygon weights + lon = f.auxiliary_coordinate("X") + lon.del_data() + bounds = cf.Data( + [[315, 45, 45, 999, 999, 999], [90, 90, 0, 45, 45, 135]], + "degrees_east", + mask_value=999, + ).reshape(2, 6) + lon.set_bounds(cf.Bounds(data=bounds)) + + lat = f.auxiliary_coordinate("Y") + bounds = cf.Data( + [[0, 0, 90, 999, 999, 999], [0, 90, 0, 0, -90, 0]], + "degrees_north", + mask_value=999, + ).reshape(2, 6) + lat.set_bounds(cf.Bounds(data=bounds)) + + w = f.weights("X", great_circle=True) + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("1")) + + w = f.weights("area", great_circle=True, measure=True, radius=radius) + self.assertTrue((w.array == (r**2) * correct_weights).all()) + self.assertEqual(w.Units, cf.Units("m2")) + + # Plane polygon weights + lon.override_units("m", inplace=True) + lon.standard_name = "projection_x_coordinate" + bounds = cf.Data( + [[2, 2, 0, 0, 999, 999, 999, 999], [2, 2, 0, 0, 1, 1, 3, 3]], + "m", + mask_value=999, + ).reshape(2, 8) + lon.set_bounds(cf.Bounds(data=bounds)) + + lat.override_units("m", inplace=True) + lat.standard_name = "projection_y_coordinate" + bounds = cf.Data( + [ + [-1, 1, 1, -1, 999, 999, 999, 999], + [-1, 1, 1, -1, -1, -3, -3, -1], + ], + "m", + mask_value=999, + ).reshape(2, 8) + lat.set_bounds(cf.Bounds(data=bounds)) + + correct_weights = np.array([4, 8]) + w = f.weights("area") + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("1")) + + w = f.weights("area", measure=True) + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("m2")) + + def test_weights_line_length_geometry(self): + # Spherical line geometry + gls = gps.copy() + lon = gls.auxiliary_coordinate("X") + lat = gls.auxiliary_coordinate("Y") + lon.set_geometry("line") + lat.set_geometry("line") + + # Cirumference of unit sphere + cirumference = 2 * np.pi + correct_weights = np.array( + [3 * cirumference / 4, 5 * cirumference / 4] + ) + + w = gls.weights("X", great_circle=True) + self.assertTrue((w.array == correct_weights).all()) + self.assertEqual(w.Units, cf.Units("1")) + + w = gls.weights("X", great_circle=True, measure=True, radius=radius) + self.assertTrue((w.array == r * correct_weights).all()) + self.assertEqual(w.Units, cf.Units("m")) + + # Plane line geometry with multiple parts + gppm = gppi.copy() + lon = gppm.auxiliary_coordinate("X") + lat = gppm.auxiliary_coordinate("Y") + lon.set_geometry("line") + lat.set_geometry("line") + lon.del_interior_ring() + lat.del_interior_ring() + + correct_weights = np.array([9, 13]) + w = gppm.weights("X") + self.assertTrue((w.array == correct_weights).all()) + + def test_weights_line_area_ugrid(self): + f = cf.example_field(9) + f = f[..., 0:3] + lon = f.auxiliary_coordinate("X") + lat = f.auxiliary_coordinate("Y") + + lon.del_data() + bounds = cf.Data( + [[315, 45], [45, 45], [45, 315]], + "degrees_east", + mask_value=999, + ) + lon.set_bounds(cf.Bounds(data=bounds)) + + lat.del_data() + bounds = cf.Data( + [[0, 0], [0, 90], [90, 0]], + "degrees_north", + mask_value=999, + ) + lat.set_bounds(cf.Bounds(data=bounds)) + + # Cirumference of unit sphere + cirumference = 2 * np.pi + correct_weights = np.array([cirumference / 4] * 3) + + # Spherical line weights + w = f.weights("X", great_circle=True) + self.assertTrue( + np.isclose(w, correct_weights, rtol=0, atol=9e-15).all() + ) + self.assertEqual(w.Units, cf.Units("1")) + + w = f.weights("X", great_circle=True, measure=True, radius=radius) + self.assertTrue( + np.isclose(w, r * correct_weights, rtol=0, atol=9e-15).all() + ) + self.assertEqual(w.Units, cf.Units("m")) + + +if __name__ == "__main__": + print("Run date:", datetime.datetime.now()) + cf.environment() + print("") + unittest.main(verbosity=2) diff --git a/cf/test/ugrid_global_1.nc b/cf/test/ugrid_global_1.nc new file mode 100644 index 0000000000000000000000000000000000000000..25bc2afef512048636558de9a74272338eac594d GIT binary patch literal 37356 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 JrBVI%zW{mg^vM7K literal 0 HcmV?d00001 diff --git a/cf/test/ugrid_global_2.nc b/cf/test/ugrid_global_2.nc new file mode 100644 index 0000000000000000000000000000000000000000..4a262f835abd761f131860a507051c799b0928f6 GIT binary patch literal 116164 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 N-0+k&R;;1 repr(x) - - """ - return super().__repr__().replace("<", " 1: + if not xcoord.has_bounds(): + if auto: + return False + + raise ValueError( + "Can't create area weights: No bounds for " + f"{xcoord.identity()!r} axis" + ) + + if methods: + weights[(xaxis,)] = f"linear {xcoord.identity()}" + else: + cells = xcoord.cellsize + if xcoord.Units.equivalent(radians): + cells.Units = radians + if measure: + cells = cells * radius + cells.override_units(radius.Units, inplace=True) + else: + cells.Units = metres + + weights[(xaxis,)] = cells + + weights_axes.add(xaxis) + + if measure or ycoord.size > 1: + if not ycoord.has_bounds(): + if auto: + return False + + raise ValueError( + "Can't create area weights: No bounds for " + f"{ycoord.identity()!r} axis" + ) + + if ycoord.Units.equivalent(radians): + ycoord = ycoord.clip(-90, 90, units=Units("degrees")) + ycoord.sin(inplace=True) + + if methods: + weights[(yaxis,)] = f"linear sine {ycoord.identity()}" + else: + cells = ycoord.cellsize + if measure: + cells = cells * radius + + weights[(yaxis,)] = cells + else: + if methods: + weights[(yaxis,)] = f"linear {ycoord.identity()}" + else: + cells = ycoord.cellsize + weights[(yaxis,)] = cells + + weights_axes.add(yaxis) + + return True + + @classmethod + def data( + cls, + f, + w, + weights, + weights_axes, + axes=None, + data=False, + components=False, + methods=False, + ): + """Create weights from `Data`. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + w: `Data` + Create weights from this data, that must be + broadcastable to the data of *f*, unless the *axes* + parameter is also set. + + axes: (sequence of) `int` or `str`, optional + If not `None` then weights are created only for the + specified axes. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + data: `bool`, optional + If True then create weights in a `Data` instance that + is broadcastable to the original data. + + components: `bool`, optional + If True then the *weights* dictionary is updated with + orthogonal weights components. + + {{weights methods: `bool`, optional}} + + :Returns: + + `bool` + `True` if weights were created, otherwise `False`. + + """ + # ------------------------------------------------------------ + # Data weights + # ------------------------------------------------------------ + field_data_axes = f.get_data_axes() + + if axes is not None: + if isinstance(axes, (str, int)): + axes = (axes,) + + if len(axes) != w.ndim: + raise ValueError( + "'axes' parameter must provide an axis identifier " + f"for each weights data dimension. Got {axes!r} for " + f"{w.ndim} dimension(s)." + ) + + iaxes = [ + field_data_axes.index(f.domain_axis(axis, key=True)) + for axis in axes + ] + + if data: + for i in range(f.ndim): + if i not in iaxes: + w = w.insert_dimension(position=i) + iaxes.insert(i, i) + + w = w.transpose(iaxes) + + if w.ndim > 0: + while w.shape[0] == 1: + w = w.squeeze(0) + + else: + iaxes = list(range(f.ndim - w.ndim, f.ndim)) + + if not (components or methods): + if not f._is_broadcastable(w.shape): + raise ValueError( + f"The 'Data' weights (shape {w.shape}) are not " + "broadcastable to the field construct's data " + f"(shape {f.shape})." + ) + + axes0 = field_data_axes[f.ndim - w.ndim :] + else: + axes0 = [field_data_axes[i] for i in iaxes] + + for axis0 in axes0: + if axis0 in weights_axes: + raise ValueError( + "Multiple weights specified for " + f"{f.constructs.domain_axis_identity(axis0)!r} axis" + ) + + if methods: + weights[tuple(axes0)] = "custom data" + else: + weights[tuple(axes0)] = w + + weights_axes.update(axes0) + return True + + @classmethod + def field(cls, f, field_weights, weights, weights_axes): + """Create weights from other field constructs. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + field_weights: sequence of `Field` + The field constructs from which to create weights. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + :Returns: + + `bool` + `True` if weights were created, otherwise `False`. + + """ + s = f.analyse_items() + + domain_axes = f.domain_axes(todict=True) + for w in field_weights: + t = w.analyse_items() + # TODO CHECK this with org + + if t["undefined_axes"]: + w_domain_axes_1 = w.domain_axes( + filter_by_size=(1,), todict=True + ) + if set(w_domain_axes_1).intersection(t["undefined_axes"]): + raise ValueError( + f"Weights field {w} has at least one undefined " + f"domain axes: {w_domain_axes_1}." + ) + + w = w.squeeze() + + w_domain_axes = w.domain_axes(todict=True) + + axis1_to_axis0 = {} + + coordinate_references = f.coordinate_references(todict=True) + w_coordinate_references = w.coordinate_references(todict=True) + + for axis1 in w.get_data_axes(): + identity = t["axis_to_id"].get(axis1, None) + if identity is None: + raise ValueError( + "Weights field has unmatched, size > 1 " + f"{w.constructs.domain_axis_identity(axis1)!r} axis" + ) + + axis0 = s["id_to_axis"].get(identity, None) + if axis0 is None: + raise ValueError( + f"Weights field has unmatched, size > 1 {identity!r} " + "axis" + ) + + w_axis_size = w_domain_axes[axis1].get_size() + f_axis_size = domain_axes[axis0].get_size() + + if w_axis_size != f_axis_size: + raise ValueError( + f"Weights field has incorrectly sized {identity!r} " + f"axis ({w_axis_size} != {f_axis_size})" + ) + + axis1_to_axis0[axis1] = axis0 + + # Check that the defining coordinate data arrays are + # compatible + key0 = s["axis_to_coord"][axis0] + key1 = t["axis_to_coord"][axis1] + + if not f._equivalent_construct_data( + w, key0=key0, key1=key1, s=s, t=t + ): + raise ValueError( + f"Weights field has incompatible {identity!r} " + "coordinates" + ) + + # Still here? Then the defining coordinates have + # equivalent data arrays + + # If the defining coordinates are attached to + # coordinate references then check that those + # coordinate references are equivalent + refs0 = [ + key + for key, ref in coordinate_references.items() + if key0 in ref.coordinates() + ] + refs1 = [ + key + for key, ref in w_coordinate_references.items() + if key1 in ref.coordinates() + ] + + nrefs = len(refs0) + if nrefs > 1 or nrefs != len(refs1): + # The defining coordinate are associated with + # different numbers of coordinate references + equivalent_refs = False + elif not nrefs: + # Neither defining coordinate is associated with a + # coordinate reference + equivalent_refs = True + else: + # Each defining coordinate is associated with + # exactly one coordinate reference + equivalent_refs = f._equivalent_coordinate_references( + w, key0=refs0[0], key1=refs1[0], s=s, t=t + ) + + if not equivalent_refs: + raise ValueError( + "Input weights field has an incompatible " + "coordinate reference" + ) + + axes0 = tuple( + [axis1_to_axis0[axis1] for axis1 in w.get_data_axes()] + ) + + for axis in axes0: + if axis in weights_axes: + raise ValueError( + "Multiple weights specified for " + f"{f.constructs.domain_axis_identity(axis)!r} " + "axis" + ) + + weights[tuple(axes0)] = w.data + weights_axes.update(axes0) + + return True + + @classmethod + def field_scalar(cls, f): + """Return a scalar field of weights with long name ``'weight'``. + + .. versionadded:: 3.16.0 + + :Parameter: + + f: `Field` + The field for which the weights are being created. + + :Returns: + + `Field` + The scalar weights field, with a single array value of + ``1``. + + **Examples** + + >>> s = w.field_scalar(f) + >> print(s) + Field: long_name=weight + ----------------------- + Data : long_name=weight 1 + >>> print(s.array) + 1.0 + + """ + data = f._Data(1.0, "1") + + f = type(f)() + f.set_data(data, copy=False) + f.long_name = "weight" + f.comment = f"Weights for {f!r}" + return f + + @classmethod + def polygon_area( + cls, + f, + domain_axis, + weights, + weights_axes, + auto=False, + measure=False, + radius=None, + great_circle=False, + return_areas=False, + methods=False, + ): + """Creates area weights for polygon geometry cells. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + domain_axis: `str` or `None` + If set to a domain axis identifier + (e.g. ``'domainaxis1'``) then only accept cells that + recognise the given axis. If `None` then the cells may + span any axis. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + {{weights auto: `bool`, optional}} + + {{weights measure: `bool`, optional}} + + {{radius: optional}} + + great_circle: `bool`, optional + If True then assume that the edges of spherical + polygons are great circles. + + {{weights methods: `bool`, optional}} + + :Returns: + + `bool` or `Data` + `True` if weights were created, otherwise `False`. If + *return_areas* is True and weights were created, then + the weights are returned. + + """ + from .units import Units + + axis, aux_X, aux_Y, aux_Z, ugrid = cls._geometry_ugrid_cells( + f, domain_axis, "polygon", auto=auto + ) + + if axis is None: + if auto: + return False + + if domain_axis is None: + raise ValueError("No polygon cells") + + raise ValueError( + "No polygon cells for " + f"{f.constructs.domain_axis_identity(domain_axis)!r} axis" + ) + + if axis in weights_axes: + if auto: + return False + + raise ValueError( + "Multiple weights specifications for " + f"{f.constructs.domain_axis_identity(axis)!r} axis" + ) + + x = aux_X.bounds.data + y = aux_Y.bounds.data + + radians = Units("radians") + metres = Units("metres") + + if x.Units.equivalent(radians) and y.Units.equivalent(radians): + if not great_circle: + raise ValueError( + "Must set great_circle=True to allow the derivation of " + "area weights from spherical polygons composed from " + "great circle segments." + ) + + if methods: + weights[(axis,)] = "area spherical polygon" + return True + + spherical = True + x.Units = radians + elif x.Units.equivalent(metres) and y.Units.equivalent(metres): + if methods: + weights[(axis,)] = "area plane polygon" + return True + + spherical = False + else: + return False + + y.Units = x.Units + x = x.persist() + y = y.persist() + + # Find the number of nodes per polygon + n_nodes = x.count(axis=-1, keepdims=False).array + if (y.count(axis=-1, keepdims=False) != n_nodes).any(): + raise ValueError( + "Can't create area weights for " + f"{f.constructs.domain_axis_identity(axis)!r} axis: " + f"{aux_X!r} and {aux_Y!r} have inconsistent bounds " + "specifications" + ) + + if ugrid: + areas = cls._polygon_area_ugrid(f, x, y, n_nodes, spherical) + else: + areas = cls._polygon_area_geometry( + f, x, y, aux_X, aux_Y, n_nodes, spherical + ) + + del x, y, n_nodes + + if not measure: + areas.override_units(Units("1"), inplace=True) + elif spherical: + areas = cls._spherical_area_measure(f, areas, aux_Z, radius) + + if return_areas: + return areas + + weights[(axis,)] = areas + weights_axes.add(axis) + return True + + @classmethod + def _polygon_area_geometry(cls, f, x, y, aux_X, aux_Y, n_nodes, spherical): + """Creates area weights for polygon geometry cells. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + x: `Data` + The X coordinates of the polygon nodes, with units of + radians or equivalent to metres. + + y: `Data` + The Y coordinates of the polygon nodes, with the same + units as *x*. + + aux_X: `AuxiliaryCoordinate` + The X coordinate construct of *f*. + + aux_Y: `AuxiliaryCoordinate` + The Y coordinate construct of *f*. + + n_nodes: `numpy.ndarray` + The number of nodes per polygon, equal to the number + of non-missing values in the trailing dimension of + *x*. + + spherical: `bool` + True for spherical lines. + + :Returns: + + `Data` + The area of the geometry polygon cells, in units of + square metres. + + """ + import numpy as np + + x_units = x.Units + y_units = y.Units + + # Check for interior rings + interior_ring_X = aux_X.get_interior_ring(None) + interior_ring_Y = aux_Y.get_interior_ring(None) + if interior_ring_X is None and interior_ring_Y is None: + interior_rings = None + elif interior_ring_X is None: + raise ValueError( + "Can't find weights: X coordinates have missing " + "interior ring variable" + ) + elif interior_ring_Y is None: + raise ValueError( + "Can't find weights: Y coordinates have missing " + "interior ring variable" + ) + elif not interior_ring_X.data.equals(interior_ring_Y.data): + raise ValueError( + "Can't find weights: Interior ring variables for X and Y " + "coordinates have different data values" + ) + else: + interior_rings = interior_ring_X.data + if interior_rings.shape != aux_X.bounds.shape[:-1]: + raise ValueError( + "Can't find weights: Interior ring variables have " + f"incorrect shape. Got {interior_rings.shape}, " + f"expected {aux_X.bounds.shape[:-1]}" + ) + + rows = np.arange(x.shape[0]) + n_nodes_m1 = n_nodes - 1 + + duplicate = x[0, 0, 0].isclose(x[0, 0, n_nodes_m1[0, 0]]) & y[ + 0, 0, 0 + ].isclose(y[0, 0, n_nodes_m1[0, 0]]) + duplicate = duplicate.array + + y = y.array + + # Pad out the boundary vertex array for each cell with first + # and last bounds neighbours + empty_col_x = np.ma.masked_all(x.shape[:-1] + (1,), dtype=x.dtype) + empty_col_y = np.ma.masked_all(y.shape[:-1] + (1,), dtype=y.dtype) + + if not duplicate.any(): + # The first and last boundary vertices are different in + # all polygons, i.e. No. nodes = No. edges. + # + # Insert two new Y columns that duplicate the first and + # last nodes. + # + # E.g. for triangle cells: + # [[[4, 5, 6]]] -> [[[6, 4, 5, 6, 4]]] + # [[[4, 5, 6, --]]] -> [[[6, 4, 5, 6, 4, --]]] + n_nodes_p1 = n_nodes + 1 + y = np.ma.concatenate((empty_col_y, y, empty_col_y), axis=-1) + for i in range(x.shape[1]): + y[:, i, 0] = y[rows, i, n_nodes[:, i]] + y[rows, i, n_nodes_p1[:, i]] = y[rows, i, 1] + + if spherical: + # Spherical polygons defined by great circles: Also + # insert the columns into X. + x = x.array + x = np.ma.concatenate((empty_col_x, x, empty_col_x), axis=-1) + for i in range(x.shape[1]): + x[:, i, 0] = x[rows, i, n_nodes[:, i]] + x[rows, i, n_nodes_p1[:, i]] = x[rows, i, 1] + + # The number of edges in each polygon + N = n_nodes + + del n_nodes_p1 + elif duplicate.all(): + # The first and last boundary vertices coincide in all + # cells, i.e. No. nodes = No. edges + 1. + # + # E.g. for triangle cells: + # [[[4, 5, 6, 4]]] -> [[[6, 4, 5, 6, 4]]] + # [[[4, 5, 6, 4, --]]] -> [[[6, 4, 5, 6, 4, --]]] + if not spherical: + raise ValueError( + "Can't (yet) calculate weights for plane geometry " + "polygons with identical first and last nodes" + ) + + y = np.ma.concatenate((empty_col_y, y), axis=-1) + for i in range(x.shape[1]): + y[:, i, 0] = y[rows, i, n_nodes_m1[:, i]] + + x = x.array + x = np.ma.concatenate((empty_col_x, x), axis=-1) + for i in range(x.shape[1]): + x[:, i, 0] = x[rows, i, n_nodes_m1[:, i]] + + # The number of edges in each polygon + N = n_nodes_m1 + else: + raise ValueError( + "Can't calculate spherical geometry polygon cell areas " + "when the first and last boundary vertices coincide in " + "some cells but not all" + ) + + del duplicate, rows, n_nodes_m1 + + y = f._Data(y, units=y_units) + if spherical: + x = f._Data(x, units=x_units) + + if spherical: + # Spherical polygons defined by great circles + all_areas = cls._spherical_polygon_areas( + f, x, y, N, interior_rings + ) + else: + # Plane polygons defined by straight lines. + all_areas = cls._plane_polygon_areas(x, y) + + # Sum the areas of each geometry polygon part to get the total + # area of each cell + areas = all_areas.sum(-1, squeeze=True) + return areas + + @classmethod + def _polygon_area_ugrid(cls, f, x, y, n_nodes, spherical): + """Creates area weights for UGRID face cells. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + x: `Data` + The X coordinates of the polygon nodes, with units of + radians or equivalent to metres. + + y: `Data` + The Y coordinates of the polygon nodes, with the same + units as *x*. + + n_nodes: `numpy.ndarray` + The number of nodes per polygon, equal to the number + of non-missing values in the trailing dimension of + *x*. + + spherical: `bool` + True for spherical polygons. + + :Returns: + + `Data` + The area of the UGRID face cells, in units of square + metres. + + """ + import numpy as np + + y_units = y.Units + + n_nodes0 = n_nodes.item(0) + if (n_nodes == n_nodes0).all(): + # All cells have the same number of nodes, so we can use + # an integer and a slice in place of two integer arrays, + # which is much faster. + n_nodes = n_nodes0 + rows = slice(None) + else: + rows = np.arange(x.shape[0]) + + n_nodes_p1 = n_nodes + 1 + + # The first and last boundary vertices are different in + # all polygons, i.e. No. nodes = No. edges. + # + # Insert two new Y columns that duplicate the first and last + # nodes. + # + # E.g. for triangle cells: + # [[4, 5, 6]] -> [[6, 4, 5, 6, 4]] + # [[4, 5, 6, --]] -> [[6, 4, 5, 6, 4, --]] + y = y.array + empty_col_y = np.ma.masked_all((y.shape[0], 1), dtype=y.dtype) + y = np.ma.concatenate((empty_col_y, y, empty_col_y), axis=1) + y[:, 0] = y[rows, n_nodes] + y[rows, n_nodes_p1] = y[rows, 1] + y = f._Data(y, units=y_units) + + if spherical: + # Spherical polygons defined by great circles: Also insert + # the columns into X. + x_units = x.Units + x = x.array + empty_col_x = np.ma.masked_all((x.shape[0], 1), dtype=x.dtype) + x = np.ma.concatenate((empty_col_x, x, empty_col_x), axis=1) + x[:, 0] = x[rows, n_nodes] + x[rows, n_nodes_p1] = x[rows, 1] + x = f._Data(x, units=x_units) + + areas = cls._spherical_polygon_areas(f, x, y, n_nodes) + else: + # Plane polygons defined by straight lines + areas = cls._plane_polygon_areas(x, y) + + return areas + + @classmethod + def line_length( + cls, + f, + domain_axis, + weights, + weights_axes, + auto=False, + measure=False, + radius=None, + great_circle=False, + methods=False, + ): + """Creates line-length weights for line geometries. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + domain_axis: `str` or `None` + If set to a domain axis identifier + (e.g. ``'domainaxis1'``) then only recognise cells + that span the given axis. If `None` then the cells may + span any axis. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + {{weights auto: `bool`, optional}} + + {{weights measure: `bool`, optional}} + + {{radius: optional}} + + great_circle: `bool`, optional + If True then assume that the spherical lines are great + circles. + + {{weights methods: `bool`, optional}} + + :Returns: + + `bool` + `True` if weights were created, otherwise `False`. + + """ + from .units import Units + + axis, aux_X, aux_Y, aux_Z, ugrid = cls._geometry_ugrid_cells( + f, domain_axis, "line", auto=auto + ) + + if axis is None: + if auto: + return False + + if domain_axis is None: + raise ValueError("No line cells") + + raise ValueError( + "No line cells for " + f"{f.constructs.domain_axis_identity(domain_axis)!r} axis" + ) + + if axis in weights_axes: + if auto: + return False + + raise ValueError( + "Multiple weights specifications for " + f"{f.constructs.domain_axis_identity(axis)!r} axis" + ) + + radians = Units("radians") + metres = Units("metres") + + x = aux_X.bounds.data + y = aux_Y.bounds.data + + if x.Units.equivalent(radians) and y.Units.equivalent(radians): + if not great_circle: + raise ValueError( + "Must set great_circle=True to allow the derivation of " + "area weights from spherical polygons composed from " + "great circle segments." + ) + + if methods: + weights[(axis,)] = "linear spherical line" + return True + + spherical = True + x.Units = radians + elif x.Units.equivalent(metres) and y.Units.equivalent(metres): + if methods: + weights[(axis,)] = "linear plane line" + return True + + spherical = False + else: + return False + + y.Units = x.Units + + if ugrid: + lengths = cls._line_length_ugrid(f, x, y, spherical) + else: + lengths = cls._line_length_geometry(f, x, y, spherical) + + if not measure: + lengths.override_units(Units("1"), inplace=True) + elif spherical: + r = f.radius(default=radius) + r = r.override_units(Units("1")) + lengths = lengths * r + + weights[(axis,)] = lengths + weights_axes.add(axis) + return True + + @classmethod + def _line_length_geometry(cls, f, x, y, spherical): + """Creates line-length weights for line geometries. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + x: `Data` + The X coordinates of the line nodes. + + y: `Data` + The Y coordinates of the line nodes. + + spherical: `bool` + True for spherical lines. + + :Returns: + + `Data` + The length of the geometry line cells, in units of + metres. + + """ + from .units import Units + + if spherical: + all_lengths = cls.central_angles(f, x, y) + all_lengths.override_units(Units("m"), inplace=True) + else: + delta_x = x.diff(axis=-1) + delta_y = y.diff(axis=-1) + all_lengths = (delta_x**2 + delta_y**2) ** 0.5 + + # Sum the lengths of each part to get the total length of each + # cell + lengths = all_lengths.sum(axes=(-2, -1), squeeze=True) + return lengths + + @classmethod + def _line_length_ugrid(cls, f, x, y, spherical): + """Creates line-length weights for line geometries. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + x: `Data` + The X coordinates of the line nodes. + + y: `Data` + The Y coordinates of the line nodes. + + spherical: `bool` + True for spherical lines. + + :Returns: + + `Data` + The length of the UGRID edge cells, in units of + metres. + + """ + from .units import Units + + if spherical: + lengths = cls.central_angles(f, x, y) + lengths.override_units(Units("m"), inplace=True) + else: + delta_x = x.diff(axis=-1) + delta_y = y.diff(axis=-1) + lengths = (delta_x**2 + delta_y**2) ** 0.5 + + lengths.squeeze(axes=-1, inplace=True) + return lengths + + @classmethod + def geometry_volume( + cls, + f, + domain_axis, + weights, + weights_axes, + auto=False, + measure=False, + radius=None, + great_circle=False, + methods=False, + ): + """Creates volume weights for polygon geometry cells. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + domain_axis: `str` or `None` + If set to a domain axis identifier + (e.g. ``'domainaxis1'``) then only recognise cells + that span the given axis. If `None` then the cells may + span any axis. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + {{weights auto: `bool`, optional}} + + {{weights measure: `bool`, optional}} + + {{radius: optional}} + + great_circle: `bool`, optional + If True then assume that the edges of spherical + polygons are great circles. + + {{weights methods: `bool`, optional}} + + :Returns: + + `bool` + `True` if weights were created, otherwise `False`. + + """ + from .units import Units + + axis, aux_X, aux_Y, aux_Z, ugrid = cls._geometry_ugrid_cells( + f, domain_axis, "polygon", auto=auto + ) + + if axis is None and auto: + return False + + if axis in weights_axes: + if auto: + return False + + raise ValueError( + "Multiple weights specifications for " + f"{f.constructs.domain_axis_identity(axis)!r} axis" + ) + + x = aux_X.bounds.data + y = aux_Y.bounds.data + z = aux_Z.bounds.data + + radians = Units("radians") + metres = Units("metres") + + if not z.Units.equivalent(metres): + if auto: + return False + + raise ValueError( + "Z coordinate bounds must have units equivalent to " + f"metres for volume calculations. Got {z.Units!r}." + ) + + if not methods: + # Initialise cell volumes as the cell areas + areas = cls.polygon_area( + f, + weights, + weights_axes, + auto=auto, + measure=measure, + radius=radius, + great_circle=great_circle, + methods=False, + return_areas=True, + ) + if measure: + delta_z = abs(z[..., 1] - z[..., 0]) + delta_z.squeeze(axis=-1, inplace=True) + + if x.Units.equivalent(metres) and y.Units.equivalent(metres): + # -------------------------------------------------------- + # Plane polygons defined by straight lines. + # -------------------------------------------------------- + if methods: + weights[(axis,)] = "volume plane polygon geometry" + return True + + if measure: + volumes = areas * delta_z + else: + volumes = areas + + elif x.Units.equivalent(radians) and y.Units.equivalent(radians): + # -------------------------------------------------------- + # Spherical polygons defined by great circles + # -------------------------------------------------------- + if not great_circle: + raise ValueError( + "Must set great_circle=True to allow the derivation " + "of volume weights from spherical polygons composed " + "from great circle segments." + ) + + if methods: + weights[(axis,)] = "volume spherical polygon geometry" + return True + + if measure: + r = f.radius(default=radius) + + # actual_volume = + # [actual_area/(4*pi*r**2)] + # * (4/3)*pi*[(r+delta_z)**3 - r**3)] + volumes = areas * ( + delta_z**3 / (3 * r**2) + delta_z**2 / r + delta_z + ) + else: + volumes = areas + else: + raise ValueError( + "X and Y coordinate bounds must both have units " + "equivalent to either metres (for plane polygon) or " + "radians (for spherical polygon) volume calculations. Got " + f"{x.Units!r} and {y.Units!r}." + ) + + weights[(axis,)] = volumes + weights_axes.add(axis) + return True + + @classmethod + def interior_angles(cls, f, lon, lat, interior_rings=None): + """Find the interior angles at spherical polygon vertices. + + The interior angle at a vertex is that formed by two adjacent + sides. Each interior angle is therefore a function of three + vertices - the vertex at which the interior angle is required and + the two adjacent vertices either side of it. + + **Method** + + Bevis, M., Cambareri, G. Computing the area of a spherical polygon + of arbitrary shape. Math Geol 19, 335–346 (1987). + + http://bemlar.ism.ac.jp/zhuang/Refs/Refs/bevis1987mathgeol.pdf + + Abstract: An algorithm for determining the area of a spherical + polygon of arbitrary shape is presented. The kernel of the + problem is to compute the interior angle at each vertex of the + spherical polygon; a well-known relationship between the area of + a spherical polygon and the sum of its interior angles then may + be exploited. The algorithm has been implemented as a FORTRAN + subroutine and a listing is provided. + + .. versionadded:: 3.16.0 + + .. seealso:: `central_angles` + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + lon: `Data` + Longitudes. Must have units of radians, which is not + checked. + + lat: `Data` + Latitudes. Must have units of radians, which is not + checked. + + interior_ring: `Data`, optional + The interior ring indicators for parts of polygon + geometry cells. If set must have shape + ``lon.shape[:-1]``. + + :Returns: + + `Data` + The interior angles of each spherical polygon, in + units of radians. + + """ + import numpy as np + + from .query import lt + + # P denotes a vertex at which the interior angle is required, A + # denotes the adjacent point clockwise from P, and B denotes the + # adjacent point anticlockwise from P. + lon_A = lon[..., :-2] + lon_P = lon[..., 1:-1] + lon_B = lon[..., 2:] + + lon_A_minus_lon_P = lon_A - lon_P + lon_B_minus_lon_P = lon_B - lon_P + + cos_lat = lat.cos() + cos_lat_A = cos_lat[..., :-2] + cos_lat_P = cos_lat[..., 1:-1] + cos_lat_B = cos_lat[..., 2:] + + sin_lat = lat.sin() + sin_lat_A = sin_lat[..., :-2] + sin_lat_P = sin_lat[..., 1:-1] + sin_lat_B = sin_lat[..., 2:] + + y = lon_A_minus_lon_P.sin() * cos_lat_A + x = ( + sin_lat_A * cos_lat_P + - cos_lat_A * sin_lat_P * lon_A_minus_lon_P.cos() + ) + lat_A_primed = f._Data.arctan2(y, x) + + y = lon_B_minus_lon_P.sin() * cos_lat_B + x = ( + sin_lat_B * cos_lat_P + - cos_lat_B * sin_lat_P * lon_B_minus_lon_P.cos() + ) + lat_B_primed = f._Data.arctan2(y, x) + + # The CF vertices here are, in general, given in anticlockwise + # order, so we do "alpha_P = lat_B_primed - lat_A_primed", + # rather than the "alpha_P = lat_A_primed - lat_B_primed" + # given in Bevis and Cambareri, which assumes clockwise order. + alpha_P = lat_B_primed - lat_A_primed + + if interior_rings is not None: + # However, interior rings *are* given in clockwise order in + # CF, so we need to negate alpha_P in these cases. + alpha_P.where(interior_rings, -alpha_P, inplace=True) + + # Add 2*pi to negative values + alpha_P = alpha_P.where(lt(0), alpha_P + 2 * np.pi, alpha_P) + return alpha_P + + @classmethod + def central_angles(cls, f, lon, lat): + r"""Find the central angles for spherical great circle line segments. + + The central angle of two points on the sphere is the angle + subtended from the centre of the sphere by the two points on its + surface. It is calculated with a special case of the Vincenty + formula that is accurate for all spherical distances: + + **Method** + + \Delta \sigma =\arctan {\frac {\sqrt {\left(\cos \phi + _{2}\sin(\Delta \lambda )\right)^{2}+\left(\cos \phi _{1}\sin + \phi _{2}-\sin \phi _{1}\cos \phi _{2}\cos(\Delta \lambda + )\right)^{2}}}{\sin \phi _{1}\sin \phi _{2}+\cos \phi _{1}\cos + \phi _{2}\cos(\Delta \lambda )}} + + The quadrant for \Delta \sigma should be governed by the signs of + the numerator and denominator of the right hand side using the + atan2 function. + + (https://en.wikipedia.org/wiki/Great-circle_distance) + + .. versionadded:: 3.16.0 + + .. seealso:: `interior_angles` + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + lon: `Data` + Longitudes with units of radians. + + lat: `Data` + Latitudes with units of radians. + + :Returns: + + `Data` + The central angles in units of radians. + + """ + # A and B denote adjacent vertices that define each line segment + delta_lon = lon.diff(axis=-1) + + cos_lat = lat.cos() + sin_lat = lat.sin() + + cos_lat_A = cos_lat[..., :-1] + cos_lat_B = cos_lat[..., 1:] + + sin_lat_A = sin_lat[..., :-1] + sin_lat_B = sin_lat[..., 1:] + + cos_delta_lon = delta_lon.cos() + sin_delta_lon = delta_lon.sin() + + y = ( + (cos_lat_B * sin_delta_lon) ** 2 + + (cos_lat_A * sin_lat_B - sin_lat_A * cos_lat_B * cos_delta_lon) + ** 2 + ) ** 0.5 + x = sin_lat_A * sin_lat_B + cos_lat_A * cos_lat_B * cos_delta_lon + + delta_sigma = f._Data.arctan2(y, x) + return delta_sigma + + @classmethod + def linear( + cls, + f, + axis, + weights, + weights_axes, + auto=False, + measure=False, + methods=False, + ): + """1-d linear weights from dimension coordinate constructs. + + .. versionadded:: 3.16.0 + + :Parameters: + + axis: `str` + The identity of the domain axis over which to find the + weights. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + {{weights auto: `bool`, optional}} + + {{weights measure: `bool`, optional}} + + {{weights methods: `bool`, optional}} + + :Returns: + + `bool` + `True` if weights were created, otherwise `False`. + + """ + da_key = f.domain_axis(axis, key=True, default=None) + if da_key is None: + if auto: + return False + + raise ValueError( + "Can't create weights: Can't find domain axis matching " + f"{axis!r}" + ) + + dim = f.dimension_coordinate(filter_by_axis=(da_key,), default=None) + if dim is None: + if auto: + return False + + raise ValueError( + f"Can't create linear weights for {axis!r} axis: Can't find " + "dimension coordinate construct." + ) + + if not measure and dim.size == 1: + return False + + if da_key in weights_axes: + if auto: + return False + + raise ValueError( + f"Can't create linear weights for {axis!r} axis: Multiple " + "axis specifications" + ) + + if not dim.has_bounds(): + # Dimension coordinate has no bounds + if auto: + return False + + raise ValueError( + f"Can't create linear weights for {axis!r} axis: No bounds" + ) + else: + # Bounds exist + if methods: + weights[ + (da_key,) + ] = f"linear {f.constructs.domain_axis_identity(da_key)}" + else: + weights[(da_key,)] = dim.cellsize + + weights_axes.add(da_key) + return True + + @classmethod + def cell_measure( + cls, f, measure, weights, weights_axes, methods=False, auto=False + ): + """Cell measure weights. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + {{weights weights: `dict`}} + + {{weights weights_axes: `set`}} + + {{weights methods: `bool`, optional}} + + {{weights auto: `bool`, optional}} + + :Returns: + + `bool` + `True` if weights were created, otherwise `False`. + + """ + m = f.cell_measures(filter_by_measure=(measure,), todict=True) + len_m = len(m) + + if not len_m: + if measure == "area": + return False + + if auto: + return False + + raise ValueError( + f"Can't find weights: No {measure!r} cell measure" + ) + + elif len_m > 1: + if auto: + return False + + raise ValueError( + f"Can't find weights: Multiple {measure!r} cell measures" + ) + + key, clm = m.popitem() + + clm_axes0 = f.get_data_axes(key) + + clm_axes = tuple( + [axis for axis, n in zip(clm_axes0, clm.data.shape) if n > 1] + ) + + for axis in clm_axes: + if axis in weights_axes: + if auto: + return False + + raise ValueError( + "Multiple weights specifications for " + f"{f.constructs.domain_axis_identity(axis)!r} axis" + ) + + clm = clm.get_data(_fill_value=False).copy() + if clm_axes != clm_axes0: + iaxes = [clm_axes0.index(axis) for axis in clm_axes] + clm.squeeze(iaxes, inplace=True) + + if methods: + weights[tuple(clm_axes)] = measure + " cell measure" + else: + weights[tuple(clm_axes)] = clm + + weights_axes.update(clm_axes) + return True + + @classmethod + def scale(cls, w, scale): + """Scale the weights so that they are <= scale. + + .. versionadded:: 3.16.0 + + :Parameters: + + w: `Data` + The weights to be scaled. + + scale: number or `None` + The maximum value of the scaled weights. If `None` + then no scaling is applied. + + :Returns: + + `Data` + The scaled weights. + + """ + if scale is None: + return w + + if scale < 0: + raise ValueError( + "Can't set 'scale' parameter to a negatve number. Got " + f"{scale!r}" + ) + + w = w / w.max() + if scale != 1: + w = w * scale + + return w + + @classmethod + def _geometry_ugrid_cells(cls, f, domain_axis, cell_type, auto=False): + """Checks whether weights for geometry or UGRID cells can be created. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + domain_axis: `str` or `None` + If set to a domain axis identifier + (e.g. ``'domainaxis1'``) then only recognise cells + that span the given axis. If `None` then the cells may + span any axis. + + cell_type: `str` + Either ``'polygon'`` or ``'line'``. + + {{weights auto: `bool`, optional}} + + :Returns: + + 5-`tuple` + If no appropriate geometry/UGRID cells were found then + a `tuple` of all `None` is returned. Otherwise the + `tuple` comprises: + + * The domain axis identifier of the cells, or `None`. + * The X coordinate construct of *f*, or `None`. + * The Y coordinate construct of *f*, or `None`. + * The Z coordinate construct of *f*, or `None`. + * `True` if the cells are a UGRID mesh, `False` if + they are geometry cells, or `None`. + + """ + n_return = 5 + aux_X = None + aux_Y = None + aux_Z = None + x_axis = None + y_axis = None + z_axis = None + ugrid = None + + if cell_type == "polygon": + ugrid_cell_type = "face" + else: + ugrid_cell_type = "edge" + + auxiliary_coordinates_1d = f.auxiliary_coordinates( + filter_by_naxes=(1,), todict=True + ) + + for key, aux in auxiliary_coordinates_1d.items(): + aux_axis = f.get_data_axes(key)[0] + + ugrid = f.domain_topology(default=None, filter_by_axis=(aux_axis,)) + if ugrid is not None: + cell = ugrid.get_cell(None) + else: + cell = None + + if not ( + cell == ugrid_cell_type or aux.get_geometry(None) == cell_type + ): + continue + + # Still here? Then this auxiliary coordinate has either UGRID + # or geometry cells. + if aux.X: + aux_X = aux.copy() + x_axis = aux_axis + if domain_axis is not None and x_axis != domain_axis: + aux_X = None + continue + elif aux.Y: + aux_Y = aux.copy() + y_axis = aux_axis + if domain_axis is not None and y_axis != domain_axis: + aux_Y = None + continue + elif aux.Z: + aux_Z = aux.copy() + z_axis = aux_axis + if domain_axis is not None and z_axis != domain_axis: + aux_Z = None + continue + + if aux_X is None or aux_Y is None: + if auto: + return (None,) * n_return + + raise ValueError( + "Can't create weights: Need both X and Y nodes to " + f"calculate {cell_type} cell weights" + ) + + if x_axis != y_axis: + if auto: + return (None,) * n_return + + raise ValueError( + "Can't create weights: X and Y cells span different domain " + "axes" + ) + + axis = x_axis + + if aux_X.get_bounds(None) is None or aux_Y.get_bounds(None) is None: + # Not both X and Y coordinates have bounds + if auto: + return (None,) * n_return + + raise ValueError( + "Can't create weights: Not both X and Y coordinates have " + "bounds" + ) + + if aux_X.bounds.shape != aux_Y.bounds.shape: + if auto: + return (None,) * n_return + + raise ValueError( + "Can't create weights: UGRID/geometry X and Y coordinate " + "bounds must have the same shape. " + f"Got {aux_X.bounds.shape} and {aux_Y.bounds.shape}" + ) + + if aux_Z is None: + for key, aux in auxiliary_coordinates_1d.items(): + if aux.Z: + aux_Z = aux.copy() + z_axis = f.get_data_axes(key)[0] + + # Check Z coordinates + if aux_Z is not None: + if z_axis != x_axis: + if auto: + return (None,) * n_return + + raise ValueError( + "Z coordinates span different domain axis to X and Y " + "geometry coordinates" + ) + + return axis, aux_X, aux_Y, aux_Z, bool(ugrid) + + @classmethod + def _spherical_area_measure(cls, f, areas, aux_Z, radius=None): + """Convert spherical polygon weights to cell measures. + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + areas: `Data` + The area of the polygon cells on the unit sphere, in + units of square metres. + + aux_Z: `AuxiliaryCoordinate` + The Z coordinate construct of *f*. + + {{radius: optional}} + + :Returns: + + `Data` + The area of each polygon on the surface of the defined + sphere, in units of square metres. + + """ + # Multiply by radius squared, accounting for any Z + # coordinates, to get the actual area + from .units import Units + + radius = f.radius(default=radius) + if aux_Z is None: + # No Z coordinates + r = radius + else: + z = aux_Z.get_data(None, _fill_value=False) + if z is None: + # No Z coordinates + r = radius + else: + if not z.Units.equivalent(Units("metres")): + raise ValueError( + "Z coordinates must have units equivalent to " + f"metres for area calculations. Got {z.Units!r}" + ) + + positive = aux_Z.get_property("positive", None) + if positive is None: + raise ValueError( + "Z coordinate 'positive' property is not defined" + ) + + if positive.lower() == "up": + r = radius + z + elif positive.lower() == "down": + r = radius - z + else: + raise ValueError( + "Bad value of Z coordinate 'positive' property: " + f"{positive!r}." + ) + + r = r.override_units(Units("1")) + areas = areas * r**2 + return areas + + @classmethod + def _plane_polygon_areas(cls, x, y): + r"""Calculate the areas of plane polygons. + + The area, A, is of a plane polygon is given by the shoelace + formula: + + A={\frac {1}{2}}\sum _{i=1}^{n}x_{i}(y_{i+1}-y_{i-1})} + + (https://en.wikipedia.org/wiki/Shoelace_formula). + + The formula gives a positive area for polygon nodes stored in + anticlockwise order as viewed from above, and a negative area + for polygon nodes stored in clockwise order. Note that + interior ring polygons are stored in clockwise order. + + .. versionadded:: 3.16.0 + + :Parameters: + + x: `Data` + The X coordinates of the polygon nodes, with no + duplication of the first and last nodes (i.e. the + polygons are represented by has ``N`` values, where + ``N`` is the number of edges). + + y: `Data` + The Y coordinates of the polygon nodes, with + wrap-araound duplication of the first and last nodes + (i.e. the polygons are represented by has ``N + 2`` + values, where ``N`` is the number of edges). + + :Returns: + + `Data` + The area of each plane polygon defined by the trailing + dimensions of *x* and *y*, in units of square metres. + + """ + areas = 0.5 * (x * (y[..., 2:] - y[..., :-2])).sum(-1, squeeze=True) + return areas + + @classmethod + def _spherical_polygon_areas(cls, f, x, y, N, interior_rings=None): + r"""Calculate the areas of polygons on the unit sphere. + + The area, A, of a polygon on the unit sphere, whose sides are + great circles, is given by (Todhunter): + + A=\left(\sum _{n=1}^{N}A_{n}\right)-(N-2)\pi + + where A_{n} is the n-th interior angle, and N is the number of + sides (https://en.wikipedia.org/wiki/Spherical_trigonometry). + + .. versionadded:: 3.16.0 + + :Parameters: + + f: `Field` + The field for which the weights are being created. + + x: array_like + The X coordinates of the polygon nodes, with + wrap-araound duplication of the first and last nodes + (i.e. the polygons are represented by has ``N + 2`` + values, where ``N`` is the number of edges). + + y: array_like + The Y coordinates of the polygon nodes, with + wrap-araound duplication of the first and last nodes + (i.e. the polygons are represented by has ``N + 2`` + values, where ``N`` is the number of edges). + + N: array_like + The number of edges in each polygon. + + interior_rings: array_like, optional + The interior ring indicators for parts of polygon + geometry cells. If set must have shape + ``x.shape[:-1]``. + + :Returns: + + `Data` + The area on the unit sphere of each polygon defined by + the trailing dimensions of *x* and *y*, in units of + square metres. + + """ + from numpy import pi + + from .units import Units + + interior_angles = cls.interior_angles(f, x, y, interior_rings) + areas = interior_angles.sum(-1, squeeze=True) - (N - 2) * pi + areas.override_units(Units("m2"), inplace=True) + return areas diff --git a/docs/source/class/cf.AuxiliaryCoordinate.rst b/docs/source/class/cf.AuxiliaryCoordinate.rst index f8e390faf5..4cc7dae7e0 100644 --- a/docs/source/class/cf.AuxiliaryCoordinate.rst +++ b/docs/source/class/cf.AuxiliaryCoordinate.rst @@ -492,7 +492,14 @@ NetCDF ~cf.AuxiliaryCoordinate.nc_del_variable ~cf.AuxiliaryCoordinate.nc_get_variable ~cf.AuxiliaryCoordinate.nc_has_variable - ~cf.AuxiliaryCoordinate.nc_set_variable + ~cf.AuxiliaryCoordinate.nc_set_variable + ~cf.AuxiliaryCoordinate.nc_del_node_coordinate_variable + ~cf.AuxiliaryCoordinate.nc_get_node_coordinate_variable + ~cf.AuxiliaryCoordinate.nc_has_node_coordinate_variable + ~cf.AuxiliaryCoordinate.nc_node_coordinate_variable_groups + ~cf.AuxiliaryCoordinate.nc_set_node_coordinate_variable + ~cf.AuxiliaryCoordinate.nc_set_node_coordinate_variable_groups + ~cf.AuxiliaryCoordinate.nc_clear_node_coordinate_variable_groups Groups ^^^^^^ diff --git a/docs/source/class/cf.Constructs.rst b/docs/source/class/cf.Constructs.rst index 2473606dad..05caf74fc8 100644 --- a/docs/source/class/cf.Constructs.rst +++ b/docs/source/class/cf.Constructs.rst @@ -32,6 +32,8 @@ Filtering ~cf.Constructs.filter_by_key ~cf.Constructs.filter_by_ncdim ~cf.Constructs.filter_by_ncvar + ~cf.Constructs.filter_by_cell + ~cf.Constructs.filter_by_connectivity ~cf.Constructs.filters_applied ~cf.Constructs.clear_filters_applied ~cf.Constructs.inverse_filter diff --git a/docs/source/class/cf.Data.rst b/docs/source/class/cf.Data.rst index 44305500dd..d4cd900d8e 100644 --- a/docs/source/class/cf.Data.rst +++ b/docs/source/class/cf.Data.rst @@ -19,7 +19,6 @@ Inspection :template: attribute.rst ~cf.Data.array - ~cf.Data.varray ~cf.Data.dtype ~cf.Data.ndim ~cf.Data.shape @@ -28,6 +27,8 @@ Inspection ~cf.Data.dump ~cf.Data.inspect ~cf.Data.isscalar + ~cf.Data.sparse_array + Units ----- @@ -324,6 +325,7 @@ Mask support ~cf.Data.filled ~cf.Data.harden_mask ~cf.Data.masked_invalid + ~cf.Data.masked_values ~cf.Data.del_fill_value ~cf.Data.get_fill_value ~cf.Data.has_fill_value @@ -360,7 +362,7 @@ Trigonometric functions ~cf.Data.arcsin ~cf.Data.arccos ~cf.Data.arctan -.. ~cf.Data.arctan2 [AT2] + ~cf.Data.arctan2 Hyperbolic functions ^^^^^^^^^^^^^^^^^^^^ @@ -877,3 +879,5 @@ Deprecated :template: attribute.rst ~cf.Data.ispartitioned + ~cf.Data.varray + diff --git a/docs/source/class/cf.Domain.rst b/docs/source/class/cf.Domain.rst index 65ea957db8..526700e480 100644 --- a/docs/source/class/cf.Domain.rst +++ b/docs/source/class/cf.Domain.rst @@ -119,12 +119,23 @@ Metadata constructs :template: method.rst ~cf.Domain.auxiliary_coordinates + ~cf.Domain.auxiliary_coordinate + ~cf.Domain.cell_connectivities + ~cf.Domain.cell_connectivity ~cf.Domain.cell_measures + ~cf.Domain.cell_measure ~cf.Domain.coordinates + ~cf.Domain.coordinate ~cf.Domain.coordinate_references + ~cf.Domain.coordinate_reference ~cf.Domain.dimension_coordinates + ~cf.Domain.dimension_coordinate ~cf.Domain.domain_ancillaries + ~cf.Domain.domain_ancillary ~cf.Domain.domain_axes + ~cf.Domain.domain_axis + ~cf.Domain.domain_topologies + ~cf.Domain.domain_topology ~cf.Domain.construct ~cf.Domain.construct_item ~cf.Domain.construct_key @@ -137,19 +148,12 @@ Metadata constructs ~cf.Domain.get_data_axes ~cf.Domain.has_data_axes ~cf.Domain.set_data_axes - ~cf.Domain.auxiliary_coordinate ~cf.Domain.auxiliary_to_dimension - ~cf.Domain.cell_measure - ~cf.Domain.coordinate - ~cf.Domain.coordinate_reference + ~cf.Domain.dimension_to_auxiliary ~cf.Domain.coordinate_reference_domain_axes ~cf.Domain.get_coordinate_reference ~cf.Domain.set_coordinate_reference ~cf.Domain.del_coordinate_reference - ~cf.Domain.dimension_coordinate - ~cf.Domain.dimension_to_auxiliary - ~cf.Domain.domain_ancillary - ~cf.Domain.domain_axis ~cf.Domain.domain_axis_key ~cf.Domain.del_domain_axis ~cf.Domain.climatological_time_axes diff --git a/docs/source/class/cf.Field.rst b/docs/source/class/cf.Field.rst index 76f616f30e..30bf221f5b 100644 --- a/docs/source/class/cf.Field.rst +++ b/docs/source/class/cf.Field.rst @@ -272,14 +272,27 @@ Metadata constructs :template: method.rst ~cf.Field.auxiliary_coordinates + ~cf.Field.auxiliary_coordinate + ~cf.Field.cell_connectivities + ~cf.Field.cell_connectivity ~cf.Field.cell_measures + ~cf.Field.cell_measure ~cf.Field.cell_methods + ~cf.Field.cell_method ~cf.Field.coordinates + ~cf.Field.coordinate ~cf.Field.coordinate_references + ~cf.Field.coordinate_reference ~cf.Field.dimension_coordinates + ~cf.Field.dimension_coordinate ~cf.Field.domain_ancillaries + ~cf.Field.domain_ancillary ~cf.Field.domain_axes + ~cf.Field.domain_axis + ~cf.Field.domain_topologies + ~cf.Field.domain_topology ~cf.Field.field_ancillaries + ~cf.Field.field_ancillary ~cf.Field.construct ~cf.Field.construct_item ~cf.Field.construct_key @@ -292,25 +305,16 @@ Metadata constructs ~cf.Field.get_data_axes ~cf.Field.has_data_axes ~cf.Field.set_data_axes - ~cf.Field.auxiliary_coordinate ~cf.Field.auxiliary_to_dimension - ~cf.Field.cell_measure - ~cf.Field.cell_method - ~cf.Field.coordinate - ~cf.Field.coordinate_reference + ~cf.Field.dimension_to_auxiliary ~cf.Field.coordinate_reference_domain_axes ~cf.Field.get_coordinate_reference ~cf.Field.set_coordinate_reference ~cf.Field.del_coordinate_reference - ~cf.Field.dimension_coordinate - ~cf.Field.dimension_to_auxiliary - ~cf.Field.domain_ancillary - ~cf.Field.domain_axis ~cf.Field.domain_axis_key ~cf.Field.domain_axis_position ~cf.Field.domain_mask ~cf.Field.del_domain_axis - ~cf.Field.field_ancillary ~cf.Field.map_axes ~cf.Field.climatological_time_axes diff --git a/docs/source/class/cf.GatheredArray.rst b/docs/source/class/cf.GatheredArray.rst index 61b55b93af..f6693a2a33 100644 --- a/docs/source/class/cf.GatheredArray.rst +++ b/docs/source/class/cf.GatheredArray.rst @@ -33,6 +33,7 @@ cf.GatheredArray ~cf.GatheredArray.source ~cf.GatheredArray.subarray_shapes ~cf.GatheredArray.subarrays + ~cf.GatheredArray.subarray_parameters ~cf.GatheredArray.to_dask_array ~cf.GatheredArray.to_memory diff --git a/docs/source/class/cf.RaggedContiguousArray.rst b/docs/source/class/cf.RaggedContiguousArray.rst index 4c3dd71c07..e1f6fd8dc1 100644 --- a/docs/source/class/cf.RaggedContiguousArray.rst +++ b/docs/source/class/cf.RaggedContiguousArray.rst @@ -35,6 +35,7 @@ cf.RaggedContiguousArray ~cf.RaggedContiguousArray.source ~cf.RaggedContiguousArray.subarray_shapes ~cf.RaggedContiguousArray.subarrays + ~cf.RaggedContiguousArray.subarray_parameters ~cf.RaggedContiguousArray.to_dask_array ~cf.RaggedContiguousArray.to_memory diff --git a/docs/source/class/cf.RaggedIndexedArray.rst b/docs/source/class/cf.RaggedIndexedArray.rst index b1853cfc11..abc4edc74a 100644 --- a/docs/source/class/cf.RaggedIndexedArray.rst +++ b/docs/source/class/cf.RaggedIndexedArray.rst @@ -34,6 +34,7 @@ cf.RaggedIndexedArray ~cf.RaggedIndexedArray.source ~cf.RaggedIndexedArray.subarray_shapes ~cf.RaggedIndexedArray.subarrays + ~cf.RaggedIndexedArray.subarray_parameters ~cf.RaggedIndexedArray.to_dask_array ~cf.RaggedIndexedArray.to_memory diff --git a/docs/source/class/cf.RaggedIndexedContiguousArray.rst b/docs/source/class/cf.RaggedIndexedContiguousArray.rst index 4d913bc082..af27e95277 100644 --- a/docs/source/class/cf.RaggedIndexedContiguousArray.rst +++ b/docs/source/class/cf.RaggedIndexedContiguousArray.rst @@ -34,6 +34,7 @@ cf.RaggedIndexedContiguousArray ~cf.RaggedIndexedContiguousArray.source ~cf.RaggedIndexedContiguousArray.subarray_shapes ~cf.RaggedIndexedContiguousArray.subarrays + ~cf.RaggedIndexedContiguousArray.subarray_parameters ~cf.RaggedIndexedContiguousArray.to_dask_array ~cf.RaggedIndexedContiguousArray.to_memory diff --git a/docs/source/class/cf.RegridOperator.rst b/docs/source/class/cf.RegridOperator.rst index d8361bbd98..dfe5b47d56 100644 --- a/docs/source/class/cf.RegridOperator.rst +++ b/docs/source/class/cf.RegridOperator.rst @@ -44,6 +44,7 @@ cf.RegridOperator ~cf.RegridOperator.src_cyclic ~cf.RegridOperator.src_mask ~cf.RegridOperator.src_shape + ~cf.RegridOperator.src_mesh_location ~cf.RegridOperator.start_index ~cf.RegridOperator.weights ~cf.RegridOperator.weights_file diff --git a/docs/source/field_analysis.rst b/docs/source/field_analysis.rst index e29f4baebb..c3ea848470 100644 --- a/docs/source/field_analysis.rst +++ b/docs/source/field_analysis.rst @@ -1357,18 +1357,27 @@ Spherical source domain Spherical destination domain `Latitude-longitude`_ `Rotated latitude-longitude`_ `Latitude-longitude`_ `Plane projection`_ `Latitude-longitude`_ `Tripolar`_ +`Latitude-longitude`_ `UGRID mesh`_ `Rotated latitude-longitude`_ `Latitude-longitude`_ `Rotated latitude-longitude`_ `Rotated latitude-longitude`_ `Rotated latitude-longitude`_ `Plane projection`_ `Rotated latitude-longitude`_ `Tripolar`_ +`Rotated latitude-longitude`_ `UGRID mesh`_ `Plane projection`_ `Latitude-longitude`_ `Plane projection`_ `Rotated latitude-longitude`_ `Plane projection`_ `Plane projection`_ `Plane projection`_ `Tripolar`_ +`Plane projection`_ `UGRID mesh`_ `Tripolar`_ `Latitude-longitude`_ `Tripolar`_ `Rotated latitude-longitude`_ `Tripolar`_ `Plane projection`_ `Tripolar`_ `Tripolar`_ +`Tripolar`_ `UGRID mesh`_ +`UGRID mesh`_ `Latitude-longitude`_ +`UGRID mesh`_ `Rotated latitude-longitude`_ +`UGRID mesh`_ `Plane projection`_ +`UGRID mesh`_ `Tripolar`_ +`UGRID mesh`_ `UGRID mesh`_ ============================== ============================== The most convenient usage is when the destination domain exists diff --git a/docs/source/installation.rst b/docs/source/installation.rst index e6a335077b..e744fd23ca 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -201,8 +201,10 @@ Required * `cftime `_, version 1.6.2 or newer (note that this package may be installed with netCDF4). -* `cfdm `_, version 1.10.1.2 or up to, - but not including, 1.10.2.0. +* `scipy `_, version 1.10.0 or newer. + +* `cfdm `_, version 1.11.0.0 or up to, + but not including, 1.11.1.0. * `cfunits `_, version 3.3.6 or newer. @@ -253,10 +255,6 @@ environments for which these features are not required. or may be installed from source. -.. rubric:: Convolution filters, derivatives and relative vorticity - -* `scipy `_, version 1.10.0 or newer. - .. rubric:: Subspacing based on N-dimensional construct cells (N > 1) containing a given value diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index b3a715296a..a1941fbc8d 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -120,7 +120,8 @@ elements. The following file types can be read: * All formats of netCDF3 and netCDF4 files can be read, containing - datasets for any version of CF up to and including CF-|version|. + datasets for any version of CF up to and including CF-|version|, + including :ref:`UGRID ` datasets. .. @@ -2671,6 +2672,45 @@ CF-netCDF geometry container variable is automatically created, and the cells encoded with the :ref:`compression ` techniques defined in the CF conventions. +---- + +.. _UGRID-mesh-topologies: + +**UGRID mesh topologies** +------------------------- + +A `UGRID_` mesh topology defines the geospatial topology of cells +arranged in two or three dimensions in real space but indexed by a +single dimension. It explicitly describes the topological +relationships between cells, i.e. spatial relationships which do not +depend on the cell locations, via a mesh of connected nodes. See the +`domain topology construct`_ and `cell connectivity construct`_ +descriptions in the CF data model (from CF-1.11) for more details, +including on how the mesh relates to the cells of the domain. + +.. code-block:: python + :caption: *Inspect a dataset containing a UGRID mesh topology.* + + >>> f = cf.example_field(8) + >>> print(f) + Field: air_temperature (ncvar%ta) + --------------------------------- + Data : air_temperature(time(2), ncdim%nMesh2_face(3)) K + Cell methods : time(2): point (interval: 3600 s) + Dimension coords: time(2) = [2016-01-02 01:00:00, 2016-01-02 11:00:00] gregorian + Auxiliary coords: longitude(ncdim%nMesh2_face(3)) = [-44.0, -44.0, -42.0] degrees_east + : latitude(ncdim%nMesh2_face(3)) = [34.0, 32.0, 34.0] degrees_north + Domain Topology : cell:face(ncdim%nMesh2_face(3), 4) = [[2, ..., --]] + Cell connects : connectivity:edge(ncdim%nMesh2_face(3), 5) = [[0, ..., --]] + +.. + COMMENTED OUT UNTIL THIS WORKS! When a field construct containing a + UGRID mesh topology is written to disk, a CF-netCDF UGRID mesh + topology variable is automatically created which will be shared by + any data variables that can make use of the same mesh. + +---- + .. _Domain-ancillaries: Domain ancillaries @@ -6992,3 +7032,4 @@ if any, are filtered out. .. _geometries: http://cfconventions.org/cf-conventions/cf-conventions.html#geometries .. _Hierarchical groups: http://cfconventions.org/cf-conventions/cf-conventions.html#groups .. _Lossy compression by coordinate subsampling: http://cfconventions.org/cf-conventions/cf-conventions.html#compression-by-coordinate-subsampling +.. _UGRID: https://cfconventions.org/cf-conventions/cf-conventions.html#ugrid-conventions diff --git a/requirements.txt b/requirements.txt index 3c5ef46ec4..a10ba9f71a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,9 @@ netCDF4>=1.5.4 cftime>=1.6.2 numpy>=1.22 -cfdm>=1.10.1.2, <1.10.2.0 +cfdm>=1.11.0.0, <1.11.1.0 psutil>=0.6.0 cfunits>=3.3.6 dask>=2022.12.1 packaging>=20.0 +scipy>=1.10.0 From 71ce0d89848b0861b1756bfaa86ccb2e37d6a7b9 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 21 Nov 2023 11:15:40 +0000 Subject: [PATCH 03/22] Clarify changelog Co-authored-by: Sadie L. Bartholomew --- Changelog.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Changelog.rst b/Changelog.rst index 33c6a17657..946116f29a 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -11,8 +11,10 @@ version 3.16.0 * New methods: `cf.Field.domain_topology`, `cf.Field.domain_topologies` (https://github.com/NCAS-CMS/cf-python/issues/696) -* New methods: `cf.Data.arctan2`, `cf.Data.masked-values` +* New method: `cf.Data.masked-values` (https://github.com/NCAS-CMS/cf-python/issues/696) +* New method: `cf.Data.arctan2` + (https://github.com/NCAS-CMS/cf-python/issues/38) * Fix bug that caused `cf.Field.collapse` to give incorrect results for the "sum", "sum_of_weights" and "sum_of_weights2" methods, only in the case that weights have been requested From 940320868710fe5b7a7809bfb91fb4ca3adb93cd Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 21 Nov 2023 11:19:52 +0000 Subject: [PATCH 04/22] Typos Co-authored-by: Sadie L. Bartholomew --- cf/data/array/boundsfromnodesarray.py | 4 ++-- cf/data/array/cellconnectivityarray.py | 4 ++-- cf/data/collapse/dask_collapse.py | 8 ++++---- cf/docstring/docstring.py | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cf/data/array/boundsfromnodesarray.py b/cf/data/array/boundsfromnodesarray.py index a096bf01d9..e65177fcd1 100644 --- a/cf/data/array/boundsfromnodesarray.py +++ b/cf/data/array/boundsfromnodesarray.py @@ -14,8 +14,8 @@ class BoundsFromNodesArray( The UGRID node coordinates contain the locations of the nodes of the domain topology. In UGRID, the bounds of edge, face and volume - cells may be defined by the these locations in conjunction with a - mapping from from each cell boundary vertex to its corresponding + cells may be defined by these locations in conjunction with a + mapping from each cell boundary vertex to its corresponding coordinate value. .. versionadded:: 3.16.0 diff --git a/cf/data/array/cellconnectivityarray.py b/cf/data/array/cellconnectivityarray.py index f21cf227ea..6f631176d8 100644 --- a/cf/data/array/cellconnectivityarray.py +++ b/cf/data/array/cellconnectivityarray.py @@ -13,8 +13,8 @@ class CellConnectivityArray( """A connectivity array derived from a UGRID connectivity variable. A UGRID connectivity variable contains indices which map each cell - to its neighbours, as found in a UGRID "face_face_connectivty" or - "volume_volume_connectivty" variable. + to its neighbours, as found in a UGRID "face_face_connectivity" or + "volume_volume_connectivity" variable. The connectivity array has one more column than the corresponding UGRID variable. The extra column, in the first position, contains diff --git a/cf/data/collapse/dask_collapse.py b/cf/data/collapse/dask_collapse.py index 3fd25c0a9a..ae5eb24be2 100644 --- a/cf/data/collapse/dask_collapse.py +++ b/cf/data/collapse/dask_collapse.py @@ -131,7 +131,7 @@ def sum_weights_chunk( raise ValueError( "All collapse weights must be positive. " f"Got a weight of {w_min!r}. Consider replacing " - "non-positve values with missing data." + "non-positive values with missing data." ) dtype = double_precision_dtype(weights) @@ -246,7 +246,7 @@ def cf_mean_chunk( check_weights: `bool`, optional If True then check that all weights are positive. - .. versionadded:: 3.16.0 + .. versionadded:: 3.16.0 See `dask.array.reductions` for details of the other parameters. @@ -933,7 +933,7 @@ def cf_sum_chunk( If True, the default, then check that all weights are positive. - .. versionadded:: 3.16.0 + .. versionadded:: 3.16.0 See `dask.array.reductions` for details of the other parameters. @@ -957,7 +957,7 @@ def cf_sum_chunk( raise ValueError( "All collapse weights must be positive. " f"Got a weight of {w_min!r}. Consider replacing " - "non-positve values with missing data." + "non-positive values with missing data." ) x = np.multiply(x, weights, dtype=dtype) diff --git a/cf/docstring/docstring.py b/cf/docstring/docstring.py index 511285c175..172ed469e4 100644 --- a/cf/docstring/docstring.py +++ b/cf/docstring/docstring.py @@ -582,7 +582,7 @@ # weights measure "{{weights measure: `bool`, optional}}": """measure: `bool`, optional If True then create weights that are actual cell sizes - with approriate units.""", + with appropriate units.""", # weights auto "{{weights auto: `bool`, optional}}": """auto: `bool`, optional If True then return `False` if weights can't be found, From a8cab4b442d743779ed58a633c32fc5d41b318bc Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 21 Nov 2023 11:21:12 +0000 Subject: [PATCH 05/22] doc string Co-authored-by: Sadie L. Bartholomew --- cf/cellconnectivity.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cf/cellconnectivity.py b/cf/cellconnectivity.py index 6c7e768e7d..3ed0d52df4 100644 --- a/cf/cellconnectivity.py +++ b/cf/cellconnectivity.py @@ -113,12 +113,12 @@ def identity( strict: `bool`, optional If True then the identity is the first found of only - the `connectivity` attribute, "standard_name" property + the "connectivity" attribute, "standard_name" property or the "id" attribute. relaxed: `bool`, optional If True then the identity is the first found of only - the `connectivity `attribute, the "standard_name" + the "connectivity" attribute, the "standard_name" property, the "id" attribute, the "long_name" property or the netCDF variable name. From eec0ee421ee5da81c88063489f25ab3875434253 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 21 Nov 2023 11:21:47 +0000 Subject: [PATCH 06/22] Correct to_dask_array version added Co-authored-by: Sadie L. Bartholomew --- cf/data/array/mixin/compressedarraymixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cf/data/array/mixin/compressedarraymixin.py b/cf/data/array/mixin/compressedarraymixin.py index e5bf3502a4..3e74f2ffaf 100644 --- a/cf/data/array/mixin/compressedarraymixin.py +++ b/cf/data/array/mixin/compressedarraymixin.py @@ -53,7 +53,7 @@ def _lock_file_read(self, array): def to_dask_array(self, chunks="auto"): """Convert the data to a `dask` array. - .. versionadded:: 3.16.0 + .. versionadded:: 3.14.0 :Parameters: From b1b374ad5972335bb11be6fdf301d7ecf9ee805f Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 21 Nov 2023 13:00:17 +0000 Subject: [PATCH 07/22] Reduce the size of the test suite's file creation script --- cf/test/create_test_files.npz | Bin 0 -> 8515 bytes cf/test/create_test_files.py | 3282 +--------- cf/test/create_test_files_2.npz | Bin 0 -> 16538 bytes cf/test/create_test_files_2.py | 10173 +----------------------------- 4 files changed, 17 insertions(+), 13438 deletions(-) create mode 100644 cf/test/create_test_files.npz create mode 100644 cf/test/create_test_files_2.npz diff --git a/cf/test/create_test_files.npz b/cf/test/create_test_files.npz new file mode 100644 index 0000000000000000000000000000000000000000..6febacde481c0529a05c726f1a3943eab7fca44b GIT binary patch literal 8515 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 bBk+Gnb}O@Ey#F)Ck$?H+KYPyTe`@~^+mHC- literal 0 HcmV?d00001 diff --git a/cf/test/create_test_files_2.py b/cf/test/create_test_files_2.py index 24694623e1..1433e6c6ab 100644 --- a/cf/test/create_test_files_2.py +++ b/cf/test/create_test_files_2.py @@ -1,5 +1,6 @@ import datetime import faulthandler +import os import unittest import numpy as np @@ -12,6 +13,12 @@ VN = cf.CF() +# Load large arrays +filename = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "create_test_files_2.npz" +) +arrays = np.load(filename) + def _make_broken_bounds_cdl(filename): with open(filename, mode="w") as f: @@ -182,3012 +189,7 @@ def _make_regrid_file(filename): # Don't generate this data randomly - it's useful to see the real # patterns of global temperature. - src[...] = [ - [ - [ - 243.6, - 242.4, - 241.8, - 241.5, - 241.2, - 240.8, - 240.5, - 240.4, - 240.2, - 240.5, - 241.2, - 241.9, - 242.5, - 243.0, - 243.4, - 244.1, - 245.2, - 246.4, - 247.0, - 247.4, - 248.3, - 250.2, - 251.6, - 252.3, - 253.9, - 255.8, - 257.6, - 258.7, - 258.5, - 257.7, - 256.8, - 255.6, - 254.1, - 252.0, - 251.6, - 252.4, - 254.0, - 255.1, - 254.8, - 254.3, - 253.4, - 252.6, - 251.5, - 249.9, - 248.3, - 246.4, - 244.9, - 244.2, - ], - [ - 243.3, - 241.0, - 239.8, - 238.3, - 237.4, - 237.4, - 238.9, - 240.3, - 241.3, - 241.2, - 241.2, - 240.8, - 241.0, - 242.6, - 244.4, - 245.5, - 246.4, - 248.2, - 249.8, - 251.0, - 254.3, - 260.7, - 265.4, - 266.1, - 265.9, - 265.9, - 266.4, - 266.1, - 265.3, - 264.2, - 262.8, - 261.8, - 261.0, - 258.7, - 256.9, - 257.3, - 259.5, - 262.7, - 264.4, - 264.9, - 265.4, - 264.2, - 264.7, - 263.4, - 258.3, - 251.8, - 247.2, - 245.4, - ], - [ - 248.6, - 245.7, - 245.6, - 244.7, - 243.3, - 243.3, - 244.2, - 245.2, - 249.4, - 251.7, - 248.8, - 245.0, - 243.0, - 244.0, - 245.7, - 245.2, - 244.7, - 246.0, - 246.9, - 248.7, - 252.4, - 257.6, - 266.8, - 269.5, - 269.7, - 270.1, - 270.5, - 270.6, - 270.2, - 269.2, - 266.7, - 266.1, - 266.7, - 267.5, - 267.3, - 266.2, - 266.5, - 268.1, - 267.3, - 267.5, - 271.5, - 271.5, - 271.5, - 271.0, - 267.6, - 261.5, - 255.5, - 252.0, - ], - [ - 270.3, - 269.9, - 269.6, - 268.4, - 266.0, - 264.0, - 258.8, - 256.8, - 262.0, - 265.2, - 263.6, - 257.3, - 254.3, - 253.9, - 255.6, - 256.9, - 255.9, - 253.9, - 254.5, - 261.0, - 263.8, - 267.9, - 270.5, - 271.8, - 272.3, - 272.6, - 273.4, - 273.9, - 273.7, - 273.6, - 273.3, - 273.4, - 273.9, - 273.8, - 273.8, - 273.6, - 274.4, - 274.6, - 271.1, - 268.0, - 271.2, - 271.5, - 271.4, - 271.3, - 271.6, - 271.5, - 270.8, - 270.5, - ], - [ - 274.5, - 274.3, - 274.4, - 274.3, - 274.5, - 274.5, - 273.4, - 273.1, - 273.2, - 273.3, - 273.0, - 271.5, - 271.6, - 272.5, - 272.8, - 272.8, - 272.6, - 272.5, - 272.1, - 272.5, - 273.5, - 273.6, - 274.0, - 274.8, - 274.9, - 275.0, - 275.0, - 275.0, - 274.9, - 274.7, - 274.8, - 274.9, - 275.1, - 275.2, - 275.5, - 275.8, - 276.0, - 276.2, - 276.0, - 273.4, - 272.8, - 273.9, - 274.5, - 274.6, - 274.7, - 274.7, - 274.7, - 274.6, - ], - [ - 275.3, - 275.2, - 275.4, - 275.3, - 275.4, - 275.7, - 275.7, - 275.8, - 275.9, - 275.5, - 274.9, - 274.5, - 274.3, - 274.6, - 274.7, - 275.0, - 275.2, - 275.3, - 275.5, - 275.6, - 276.0, - 276.7, - 277.0, - 276.8, - 276.8, - 277.0, - 277.1, - 276.8, - 276.7, - 277.1, - 277.0, - 277.4, - 277.3, - 277.2, - 277.2, - 277.3, - 277.5, - 278.0, - 278.8, - 279.1, - 278.3, - 278.2, - 277.8, - 277.3, - 277.4, - 276.9, - 276.4, - 275.8, - ], - [ - 278.6, - 278.7, - 278.4, - 278.0, - 277.8, - 278.0, - 278.3, - 278.2, - 278.1, - 278.0, - 278.7, - 279.4, - 279.1, - 279.2, - 279.1, - 278.9, - 279.0, - 279.6, - 279.8, - 280.1, - 280.2, - 280.6, - 281.1, - 280.0, - 280.0, - 280.5, - 281.2, - 281.0, - 280.8, - 281.3, - 281.6, - 281.4, - 281.1, - 280.7, - 280.3, - 280.1, - 280.3, - 280.7, - 280.8, - 283.0, - 281.4, - 280.3, - 279.6, - 279.4, - 279.7, - 279.9, - 279.1, - 278.6, - ], - [ - 283.9, - 283.2, - 282.5, - 281.8, - 281.6, - 282.0, - 282.4, - 282.3, - 282.3, - 283.0, - 284.3, - 284.2, - 284.0, - 283.7, - 283.6, - 283.7, - 284.0, - 284.2, - 284.3, - 285.0, - 285.2, - 285.1, - 285.2, - 286.9, - 287.3, - 286.5, - 286.2, - 285.7, - 285.6, - 285.7, - 285.5, - 285.2, - 285.0, - 284.5, - 284.0, - 283.4, - 283.0, - 283.1, - 283.0, - 288.0, - 285.5, - 285.2, - 284.0, - 283.2, - 283.4, - 284.7, - 284.9, - 284.6, - ], - [ - 289.3, - 288.6, - 288.9, - 289.4, - 288.8, - 289.1, - 289.5, - 289.3, - 289.2, - 289.6, - 289.4, - 288.9, - 288.4, - 288.1, - 288.0, - 288.2, - 288.3, - 288.4, - 289.4, - 290.3, - 291.6, - 290.6, - 290.8, - 291.8, - 291.8, - 290.8, - 290.1, - 290.1, - 290.7, - 290.5, - 290.1, - 289.9, - 289.6, - 289.1, - 288.5, - 287.6, - 286.8, - 286.9, - 287.5, - 294.9, - 293.2, - 291.4, - 289.9, - 289.6, - 290.1, - 290.5, - 290.3, - 289.8, - ], - [ - 292.5, - 292.3, - 293.5, - 292.9, - 296.4, - 295.3, - 294.8, - 294.7, - 294.4, - 293.9, - 293.1, - 292.5, - 291.8, - 291.3, - 291.9, - 293.9, - 292.1, - 293.6, - 297.5, - 295.3, - 295.3, - 295.4, - 295.1, - 294.6, - 294.5, - 294.0, - 294.0, - 294.6, - 295.1, - 295.1, - 295.1, - 295.0, - 294.7, - 294.1, - 293.3, - 292.0, - 290.6, - 289.5, - 293.3, - 301.4, - 299.6, - 296.1, - 294.7, - 294.5, - 294.6, - 294.8, - 294.0, - 293.0, - ], - [ - 293.4, - 293.4, - 295.6, - 293.0, - 297.2, - 299.2, - 298.2, - 297.2, - 296.2, - 295.3, - 294.6, - 294.2, - 294.3, - 294.2, - 295.8, - 298.7, - 297.5, - 299.4, - 300.3, - 298.5, - 297.7, - 297.8, - 297.4, - 297.0, - 296.9, - 296.7, - 297.0, - 297.1, - 297.3, - 297.4, - 297.2, - 296.8, - 296.2, - 295.6, - 294.8, - 293.6, - 292.1, - 290.7, - 290.9, - 297.7, - 300.3, - 297.1, - 298.1, - 296.9, - 295.9, - 295.2, - 294.4, - 293.7, - ], - [ - 295.2, - 295.6, - 295.6, - 295.3, - 298.0, - 300.5, - 298.8, - 298.5, - 297.4, - 296.6, - 296.5, - 296.6, - 296.7, - 296.8, - 298.6, - 301.8, - 301.4, - 299.2, - 299.3, - 299.3, - 300.0, - 299.5, - 299.1, - 299.1, - 298.9, - 298.8, - 299.0, - 299.0, - 298.9, - 298.5, - 297.5, - 296.3, - 295.0, - 294.3, - 293.7, - 292.8, - 292.0, - 292.6, - 290.3, - 292.7, - 299.9, - 296.4, - 297.0, - 297.4, - 296.1, - 295.3, - 294.6, - 294.5, - ], - [ - 297.4, - 296.9, - 294.4, - 294.9, - 296.7, - 300.0, - 299.4, - 299.2, - 298.7, - 298.7, - 299.3, - 299.7, - 299.7, - 299.8, - 300.7, - 301.9, - 301.3, - 300.1, - 301.1, - 301.1, - 301.4, - 301.1, - 300.7, - 300.3, - 300.3, - 300.6, - 300.5, - 300.4, - 300.0, - 299.4, - 298.2, - 296.5, - 294.9, - 293.7, - 292.9, - 292.9, - 293.9, - 293.8, - 289.6, - 297.1, - 298.9, - 296.4, - 296.1, - 298.7, - 297.6, - 296.6, - 296.1, - 296.4, - ], - [ - 300.5, - 299.9, - 295.9, - 295.1, - 295.0, - 299.8, - 300.3, - 300.1, - 300.2, - 300.4, - 300.7, - 300.8, - 301.0, - 301.1, - 301.5, - 301.9, - 302.1, - 302.2, - 302.0, - 300.7, - 301.9, - 301.7, - 301.5, - 301.3, - 301.3, - 301.3, - 301.1, - 300.9, - 300.6, - 300.0, - 299.0, - 297.6, - 296.4, - 295.7, - 295.5, - 296.0, - 297.4, - 295.2, - 298.4, - 300.2, - 298.8, - 298.2, - 297.6, - 299.5, - 299.2, - 298.7, - 298.8, - 299.4, - ], - [ - 301.4, - 299.9, - 298.7, - 296.7, - 294.5, - 299.3, - 300.2, - 300.1, - 300.2, - 300.5, - 300.6, - 300.7, - 301.1, - 300.2, - 301.2, - 300.7, - 301.7, - 302.3, - 300.1, - 300.7, - 300.8, - 300.6, - 300.4, - 300.1, - 299.8, - 299.5, - 299.0, - 298.5, - 298.2, - 297.9, - 297.5, - 297.2, - 297.1, - 297.3, - 297.8, - 298.5, - 299.2, - 297.0, - 300.9, - 300.5, - 300.2, - 299.5, - 299.7, - 300.3, - 300.4, - 300.5, - 300.7, - 301.0, - ], - [ - 301.7, - 298.3, - 298.4, - 297.1, - 296.4, - 299.3, - 299.3, - 299.7, - 300.1, - 300.2, - 300.1, - 300.5, - 301.3, - 299.8, - 301.0, - 299.6, - 301.9, - 301.9, - 301.7, - 301.3, - 300.8, - 300.5, - 300.2, - 300.0, - 299.6, - 299.3, - 299.2, - 298.9, - 298.6, - 298.5, - 298.3, - 298.3, - 298.4, - 298.8, - 299.2, - 299.6, - 299.7, - 297.9, - 298.3, - 299.1, - 300.2, - 299.9, - 299.9, - 300.2, - 300.6, - 301.0, - 301.3, - 301.3, - ], - [ - 296.5, - 296.5, - 298.6, - 298.1, - 297.5, - 291.4, - 294.6, - 298.1, - 298.8, - 299.6, - 297.7, - 299.7, - 300.3, - 299.2, - 300.4, - 301.3, - 299.5, - 300.9, - 300.8, - 300.5, - 300.2, - 299.9, - 299.5, - 299.3, - 299.1, - 299.1, - 299.1, - 299.0, - 299.0, - 299.1, - 299.3, - 299.4, - 299.8, - 299.8, - 299.3, - 299.6, - 299.1, - 298.2, - 298.7, - 300.1, - 299.2, - 299.0, - 298.6, - 298.6, - 299.0, - 299.9, - 298.2, - 297.5, - ], - [ - 296.9, - 296.4, - 296.6, - 295.1, - 297.1, - 293.1, - 290.8, - 295.7, - 296.5, - 297.2, - 293.9, - 297.9, - 297.6, - 293.8, - 295.8, - 298.8, - 298.2, - 298.9, - 298.9, - 299.1, - 298.9, - 298.2, - 297.5, - 296.9, - 296.6, - 296.3, - 296.2, - 296.0, - 296.0, - 296.4, - 296.8, - 297.1, - 297.6, - 298.7, - 296.1, - 294.9, - 296.5, - 298.6, - 298.3, - 298.6, - 298.2, - 297.6, - 297.2, - 296.9, - 296.7, - 297.3, - 297.9, - 297.5, - ], - [ - 290.4, - 289.2, - 288.8, - 289.8, - 293.2, - 290.9, - 288.7, - 291.5, - 295.4, - 292.9, - 289.2, - 292.3, - 289.4, - 284.6, - 287.3, - 293.6, - 294.7, - 293.8, - 294.2, - 295.1, - 295.9, - 296.3, - 296.0, - 295.3, - 294.6, - 294.0, - 293.4, - 293.0, - 292.7, - 292.7, - 292.7, - 292.7, - 293.2, - 293.8, - 284.9, - 292.3, - 296.2, - 295.6, - 296.5, - 297.3, - 296.9, - 296.4, - 295.8, - 295.4, - 295.3, - 294.3, - 292.3, - 292.0, - ], - [ - 287.2, - 286.3, - 287.4, - 286.9, - 287.2, - 284.8, - 288.8, - 285.3, - 285.1, - 284.1, - 282.3, - 276.0, - 275.1, - 274.6, - 277.2, - 277.9, - 286.6, - 288.8, - 289.0, - 289.7, - 290.8, - 291.9, - 292.5, - 292.4, - 292.0, - 291.4, - 290.7, - 290.2, - 289.9, - 290.0, - 290.0, - 289.9, - 289.7, - 284.1, - 279.8, - 286.4, - 287.8, - 291.0, - 293.1, - 293.7, - 294.0, - 294.2, - 294.1, - 293.8, - 293.4, - 293.4, - 289.8, - 287.6, - ], - [ - 280.6, - 285.5, - 288.7, - 288.8, - 286.8, - 281.0, - 278.3, - 276.1, - 275.1, - 274.5, - 259.3, - 252.7, - 252.3, - 264.4, - 271.9, - 273.4, - 278.4, - 279.7, - 279.1, - 284.5, - 286.3, - 287.4, - 287.9, - 288.1, - 287.9, - 287.4, - 287.1, - 287.1, - 287.3, - 287.4, - 287.5, - 286.6, - 280.6, - 274.1, - 274.0, - 274.2, - 274.0, - 280.3, - 288.9, - 290.1, - 290.6, - 290.9, - 291.3, - 291.6, - 291.5, - 291.3, - 289.6, - 281.6, - ], - [ - 283.4, - 283.8, - 281.9, - 278.6, - 274.7, - 270.8, - 275.5, - 275.8, - 273.4, - 263.9, - 261.0, - 262.5, - 259.6, - 261.2, - 263.1, - 263.7, - 257.6, - 263.7, - 270.0, - 273.9, - 278.9, - 279.9, - 281.1, - 282.0, - 282.3, - 282.1, - 282.3, - 283.0, - 283.8, - 284.4, - 284.7, - 279.8, - 267.4, - 263.8, - 267.3, - 266.6, - 266.9, - 269.3, - 279.5, - 284.7, - 286.3, - 286.8, - 287.5, - 288.5, - 289.0, - 288.4, - 285.2, - 278.3, - ], - [ - 274.1, - 268.7, - 266.7, - 267.4, - 275.8, - 272.9, - 272.3, - 266.8, - 265.1, - 263.9, - 260.3, - 255.9, - 254.0, - 254.2, - 254.2, - 251.0, - 246.6, - 247.9, - 260.6, - 268.4, - 272.8, - 274.1, - 275.4, - 275.8, - 276.4, - 277.3, - 278.2, - 279.1, - 280.3, - 281.3, - 282.2, - 276.3, - 266.5, - 261.3, - 260.0, - 260.9, - 266.2, - 262.4, - 264.5, - 274.0, - 276.3, - 278.6, - 281.2, - 283.9, - 285.7, - 285.4, - 284.7, - 279.6, - ], - [ - 272.7, - 265.5, - 263.2, - 260.9, - 259.3, - 258.8, - 258.3, - 257.4, - 256.2, - 256.5, - 257.0, - 255.2, - 252.8, - 252.1, - 249.8, - 244.3, - 241.6, - 242.7, - 251.1, - 267.8, - 266.7, - 266.4, - 271.4, - 272.1, - 273.2, - 274.5, - 275.8, - 276.8, - 278.2, - 279.6, - 278.7, - 268.7, - 262.3, - 257.8, - 255.5, - 256.1, - 257.5, - 260.0, - 258.8, - 260.5, - 268.1, - 275.4, - 278.5, - 280.7, - 282.4, - 283.3, - 281.9, - 275.9, - ], - [ - 276.9, - 267.1, - 268.1, - 255.3, - 249.1, - 247.3, - 247.4, - 249.0, - 248.9, - 249.3, - 249.4, - 250.5, - 250.6, - 247.2, - 243.4, - 239.2, - 236.3, - 236.0, - 243.7, - 256.7, - 256.2, - 249.1, - 261.6, - 264.2, - 268.2, - 271.2, - 270.2, - 272.0, - 273.9, - 273.1, - 266.5, - 262.1, - 260.5, - 256.4, - 251.8, - 250.0, - 252.0, - 257.4, - 252.0, - 254.8, - 270.0, - 275.2, - 278.1, - 280.0, - 281.3, - 282.1, - 282.0, - 275.4, - ], - [ - 274.8, - 263.2, - 258.6, - 246.4, - 242.3, - 240.9, - 240.9, - 240.7, - 238.5, - 238.7, - 239.0, - 239.7, - 238.6, - 235.6, - 233.2, - 230.9, - 229.4, - 231.4, - 234.8, - 238.1, - 238.7, - 241.6, - 244.2, - 247.5, - 257.9, - 264.2, - 254.9, - 256.2, - 256.3, - 254.9, - 255.7, - 253.4, - 251.6, - 249.1, - 245.6, - 245.1, - 247.2, - 248.4, - 248.9, - 257.5, - 270.1, - 259.8, - 265.1, - 275.8, - 277.7, - 274.3, - 278.0, - 278.9, - ], - [ - 273.2, - 268.4, - 256.0, - 247.6, - 250.2, - 244.8, - 239.2, - 234.9, - 234.6, - 230.2, - 229.6, - 230.8, - 231.4, - 229.9, - 228.3, - 227.7, - 228.3, - 230.0, - 230.5, - 231.3, - 231.8, - 235.8, - 238.5, - 239.8, - 242.0, - 246.1, - 248.1, - 244.2, - 246.0, - 247.0, - 246.2, - 243.8, - 242.5, - 241.8, - 242.7, - 241.8, - 241.7, - 242.4, - 245.5, - 254.0, - 263.4, - 250.4, - 244.5, - 248.4, - 255.2, - 261.2, - 267.8, - 270.9, - ], - [ - 268.5, - 269.1, - 268.5, - 265.7, - 262.3, - 243.8, - 236.0, - 236.2, - 235.6, - 235.6, - 234.7, - 229.1, - 228.7, - 227.4, - 228.8, - 233.2, - 233.0, - 235.3, - 235.9, - 236.5, - 236.9, - 237.3, - 237.9, - 238.7, - 239.2, - 239.6, - 241.5, - 239.2, - 239.7, - 240.2, - 241.2, - 240.7, - 240.0, - 240.8, - 242.7, - 240.8, - 239.1, - 245.3, - 251.1, - 258.6, - 247.1, - 239.5, - 237.2, - 235.4, - 241.2, - 246.8, - 247.9, - 258.9, - ], - [ - 259.5, - 258.8, - 258.0, - 253.9, - 243.8, - 239.9, - 238.3, - 237.7, - 235.8, - 233.8, - 233.3, - 233.2, - 234.4, - 232.8, - 234.4, - 236.1, - 236.5, - 237.4, - 237.5, - 237.6, - 237.8, - 236.8, - 236.3, - 236.2, - 236.1, - 235.9, - 235.7, - 235.3, - 234.9, - 234.6, - 235.6, - 236.4, - 236.5, - 236.5, - 237.7, - 239.6, - 235.8, - 232.6, - 234.3, - 238.0, - 236.9, - 235.3, - 233.4, - 235.4, - 238.5, - 241.4, - 243.3, - 252.2, - ], - [ - 235.4, - 235.8, - 236.3, - 235.0, - 234.9, - 236.2, - 235.8, - 235.5, - 236.3, - 236.6, - 235.6, - 235.9, - 236.6, - 236.9, - 237.2, - 237.9, - 238.5, - 238.7, - 238.6, - 238.4, - 238.2, - 238.1, - 237.9, - 237.2, - 236.7, - 236.2, - 235.8, - 235.2, - 234.3, - 233.1, - 232.7, - 233.3, - 233.8, - 233.0, - 232.9, - 233.1, - 234.2, - 233.5, - 233.0, - 234.8, - 234.5, - 234.2, - 234.7, - 236.3, - 237.7, - 238.0, - 236.8, - 236.1, - ], - ], - [ - [ - 242.9, - 242.6, - 241.9, - 242.3, - 240.2, - 240.4, - 240.3, - 241.3, - 240.0, - 239.5, - 242.1, - 241.0, - 243.0, - 243.7, - 244.3, - 243.8, - 244.9, - 246.1, - 247.5, - 247.8, - 248.7, - 249.7, - 250.9, - 253.1, - 254.7, - 256.3, - 257.2, - 259.0, - 258.2, - 257.7, - 256.6, - 255.9, - 254.4, - 251.2, - 250.6, - 252.5, - 254.6, - 254.5, - 255.0, - 254.9, - 253.3, - 253.3, - 251.8, - 249.6, - 248.8, - 247.2, - 244.7, - 244.3, - ], - [ - 243.6, - 240.8, - 239.4, - 237.9, - 237.0, - 237.5, - 239.4, - 239.8, - 241.0, - 240.8, - 240.8, - 241.1, - 240.8, - 241.7, - 245.4, - 246.1, - 246.6, - 249.1, - 250.7, - 250.8, - 254.7, - 260.1, - 265.0, - 266.1, - 265.4, - 265.8, - 265.6, - 266.5, - 266.2, - 263.3, - 262.6, - 261.4, - 261.5, - 258.0, - 256.7, - 256.6, - 260.0, - 262.8, - 264.3, - 265.7, - 265.2, - 264.2, - 265.5, - 263.1, - 257.6, - 252.6, - 246.3, - 245.4, - ], - [ - 248.9, - 245.5, - 246.5, - 244.3, - 242.8, - 243.0, - 243.2, - 244.5, - 249.7, - 252.2, - 249.1, - 244.7, - 242.8, - 244.1, - 245.0, - 245.6, - 245.6, - 245.5, - 247.5, - 249.4, - 252.2, - 257.6, - 266.6, - 269.0, - 270.0, - 271.0, - 270.0, - 270.2, - 271.0, - 269.2, - 266.4, - 266.2, - 266.8, - 267.2, - 268.0, - 265.9, - 266.2, - 268.4, - 268.2, - 267.6, - 271.8, - 271.2, - 272.1, - 271.8, - 267.5, - 261.8, - 255.2, - 252.6, - ], - [ - 270.4, - 269.6, - 269.1, - 267.5, - 266.9, - 264.2, - 259.1, - 256.5, - 262.1, - 266.1, - 263.7, - 256.9, - 255.1, - 253.5, - 255.6, - 256.0, - 256.6, - 254.5, - 253.5, - 261.3, - 264.7, - 268.4, - 271.0, - 271.3, - 271.5, - 273.1, - 273.2, - 273.8, - 272.9, - 274.1, - 272.8, - 273.2, - 273.5, - 274.4, - 274.7, - 274.6, - 275.0, - 275.3, - 271.3, - 267.3, - 271.9, - 271.2, - 271.7, - 272.3, - 271.2, - 270.5, - 271.0, - 270.8, - ], - [ - 274.9, - 273.8, - 273.7, - 273.6, - 274.8, - 275.0, - 272.5, - 273.9, - 274.2, - 273.1, - 272.3, - 270.9, - 272.0, - 273.3, - 272.1, - 272.1, - 271.9, - 273.3, - 271.6, - 272.1, - 272.6, - 273.1, - 273.3, - 275.7, - 275.6, - 274.0, - 275.1, - 274.8, - 274.0, - 275.1, - 275.1, - 275.7, - 274.1, - 276.1, - 275.1, - 275.3, - 276.5, - 275.4, - 275.2, - 274.4, - 273.3, - 274.3, - 274.7, - 274.3, - 274.9, - 273.7, - 274.4, - 274.1, - ], - [ - 275.7, - 275.4, - 275.7, - 275.7, - 275.1, - 276.0, - 275.5, - 276.2, - 276.8, - 276.4, - 274.5, - 274.0, - 275.1, - 274.5, - 273.7, - 274.0, - 274.7, - 274.8, - 275.1, - 275.3, - 275.5, - 277.2, - 277.0, - 276.6, - 277.7, - 277.5, - 276.2, - 277.7, - 275.8, - 277.9, - 277.5, - 276.7, - 277.8, - 276.8, - 277.9, - 277.7, - 278.0, - 278.0, - 279.4, - 278.9, - 278.5, - 278.0, - 278.3, - 277.3, - 276.7, - 276.7, - 277.3, - 276.1, - ], - [ - 278.9, - 277.7, - 279.3, - 277.1, - 278.2, - 277.6, - 278.7, - 277.7, - 277.1, - 278.5, - 279.2, - 279.3, - 280.0, - 279.7, - 278.1, - 278.9, - 278.1, - 279.8, - 279.1, - 279.4, - 279.6, - 280.6, - 282.1, - 280.3, - 279.7, - 280.1, - 281.1, - 281.5, - 281.2, - 280.9, - 281.0, - 281.9, - 281.7, - 281.3, - 279.7, - 281.1, - 280.6, - 279.9, - 281.6, - 283.4, - 281.9, - 281.2, - 279.1, - 278.9, - 279.7, - 279.8, - 280.1, - 277.7, - ], - [ - 283.2, - 283.7, - 283.3, - 281.8, - 280.9, - 282.2, - 281.4, - 282.6, - 282.6, - 283.4, - 284.8, - 284.4, - 283.7, - 283.1, - 283.2, - 283.2, - 283.5, - 283.5, - 284.9, - 284.3, - 284.3, - 285.9, - 285.9, - 286.5, - 287.0, - 286.6, - 285.7, - 285.0, - 286.4, - 285.3, - 286.5, - 284.7, - 285.6, - 284.4, - 284.4, - 284.4, - 283.2, - 282.3, - 282.4, - 287.8, - 285.9, - 285.2, - 284.4, - 282.4, - 283.1, - 284.1, - 284.7, - 283.6, - ], - [ - 289.1, - 288.0, - 288.6, - 289.2, - 288.4, - 290.0, - 289.4, - 290.2, - 289.0, - 290.2, - 288.5, - 288.4, - 288.8, - 288.6, - 288.0, - 288.2, - 287.6, - 288.3, - 289.4, - 290.0, - 292.5, - 290.1, - 290.6, - 291.5, - 290.9, - 291.0, - 290.4, - 289.9, - 290.0, - 290.9, - 289.8, - 289.7, - 289.9, - 289.9, - 289.0, - 288.4, - 286.7, - 286.6, - 287.3, - 294.3, - 294.1, - 291.8, - 289.5, - 288.8, - 289.5, - 290.4, - 290.2, - 289.5, - ], - [ - 292.9, - 292.7, - 294.2, - 292.1, - 295.9, - 295.1, - 294.9, - 295.3, - 294.4, - 293.0, - 294.0, - 292.5, - 291.2, - 291.9, - 291.3, - 293.6, - 291.4, - 292.9, - 298.4, - 294.4, - 294.8, - 296.3, - 294.1, - 295.1, - 294.3, - 293.7, - 293.0, - 295.5, - 294.5, - 295.8, - 294.5, - 294.1, - 295.2, - 294.6, - 292.5, - 291.9, - 290.4, - 290.1, - 293.1, - 301.1, - 300.5, - 296.7, - 294.3, - 295.1, - 294.9, - 295.8, - 293.6, - 292.4, - ], - [ - 292.9, - 293.7, - 294.7, - 292.5, - 296.6, - 299.8, - 297.2, - 297.8, - 297.0, - 294.5, - 293.6, - 294.1, - 295.3, - 294.8, - 296.5, - 299.1, - 297.0, - 298.8, - 301.1, - 298.9, - 297.6, - 298.1, - 298.1, - 296.9, - 296.0, - 297.2, - 296.4, - 297.4, - 298.0, - 297.6, - 296.7, - 296.0, - 297.0, - 296.5, - 293.8, - 294.4, - 293.0, - 290.0, - 291.8, - 297.6, - 300.3, - 296.8, - 297.4, - 296.3, - 295.4, - 295.0, - 294.4, - 294.4, - ], - [ - 294.2, - 296.5, - 295.0, - 294.8, - 298.9, - 301.2, - 298.0, - 297.8, - 297.7, - 297.2, - 296.2, - 296.7, - 295.8, - 297.6, - 298.9, - 301.0, - 301.3, - 299.2, - 299.8, - 298.9, - 299.2, - 298.7, - 299.5, - 299.7, - 299.2, - 299.6, - 298.3, - 298.5, - 299.4, - 298.3, - 297.4, - 297.2, - 294.1, - 294.2, - 293.2, - 293.0, - 291.2, - 293.2, - 289.3, - 293.7, - 300.1, - 296.6, - 297.9, - 298.4, - 296.4, - 295.5, - 293.9, - 293.7, - ], - [ - 297.7, - 297.4, - 294.5, - 294.2, - 295.9, - 300.2, - 300.0, - 299.2, - 298.5, - 298.0, - 298.8, - 300.5, - 300.2, - 300.3, - 300.8, - 302.5, - 301.6, - 300.5, - 300.4, - 301.9, - 302.1, - 300.4, - 300.7, - 301.3, - 300.3, - 301.6, - 300.2, - 301.3, - 300.6, - 298.6, - 298.8, - 297.1, - 295.0, - 293.9, - 292.3, - 293.1, - 294.7, - 293.0, - 289.4, - 296.3, - 298.8, - 296.1, - 295.2, - 297.8, - 297.1, - 296.7, - 296.4, - 296.2, - ], - [ - 300.9, - 300.6, - 295.8, - 294.8, - 294.3, - 298.8, - 300.8, - 299.5, - 299.8, - 300.7, - 299.8, - 301.8, - 300.1, - 302.0, - 301.2, - 301.7, - 301.6, - 303.0, - 301.8, - 301.0, - 302.5, - 301.1, - 301.4, - 300.5, - 302.0, - 300.5, - 300.9, - 300.5, - 300.0, - 300.5, - 298.4, - 297.2, - 295.7, - 296.2, - 294.8, - 295.3, - 298.1, - 295.8, - 298.0, - 299.8, - 298.9, - 298.9, - 296.7, - 298.8, - 298.5, - 299.0, - 298.4, - 299.5, - ], - [ - 300.4, - 298.9, - 298.5, - 296.9, - 294.5, - 299.1, - 300.3, - 300.9, - 299.8, - 299.7, - 301.0, - 301.5, - 301.9, - 300.9, - 302.0, - 301.2, - 302.0, - 302.0, - 300.1, - 300.1, - 300.2, - 299.8, - 301.2, - 300.2, - 299.4, - 299.9, - 299.0, - 298.7, - 297.6, - 298.2, - 298.4, - 298.0, - 297.5, - 296.6, - 297.5, - 297.6, - 300.0, - 297.6, - 301.7, - 299.9, - 300.8, - 300.3, - 300.6, - 300.6, - 299.8, - 300.4, - 300.1, - 301.7, - ], - [ - 300.8, - 297.9, - 298.4, - 296.2, - 296.4, - 298.5, - 299.0, - 299.9, - 299.2, - 300.2, - 300.1, - 301.3, - 301.3, - 299.8, - 300.0, - 299.7, - 301.7, - 301.9, - 301.5, - 302.0, - 299.9, - 300.8, - 300.8, - 299.0, - 300.0, - 300.0, - 299.1, - 299.4, - 298.5, - 298.9, - 297.4, - 298.9, - 298.3, - 298.2, - 298.7, - 298.7, - 299.5, - 297.3, - 298.7, - 299.8, - 300.7, - 299.8, - 300.5, - 300.8, - 300.7, - 301.3, - 301.1, - 300.3, - ], - [ - 296.6, - 295.8, - 297.6, - 298.2, - 298.2, - 290.9, - 294.3, - 297.5, - 297.9, - 298.7, - 297.3, - 300.1, - 300.3, - 298.3, - 299.8, - 301.3, - 299.0, - 300.6, - 301.3, - 300.6, - 301.1, - 299.9, - 299.2, - 300.3, - 299.1, - 298.7, - 299.0, - 299.7, - 299.8, - 298.2, - 299.5, - 300.3, - 299.6, - 299.6, - 299.9, - 298.9, - 300.0, - 297.6, - 297.8, - 299.6, - 299.2, - 298.7, - 298.9, - 298.2, - 299.4, - 300.4, - 298.8, - 297.0, - ], - [ - 296.9, - 296.6, - 296.4, - 295.2, - 297.7, - 292.7, - 289.9, - 296.6, - 295.7, - 297.8, - 294.4, - 298.8, - 297.4, - 294.0, - 296.1, - 298.2, - 297.8, - 299.4, - 298.7, - 298.4, - 299.4, - 298.7, - 296.9, - 296.5, - 295.9, - 295.3, - 296.4, - 296.5, - 296.9, - 295.6, - 297.0, - 296.2, - 296.8, - 297.9, - 295.1, - 294.6, - 295.8, - 298.3, - 298.7, - 297.9, - 297.6, - 297.2, - 297.4, - 297.9, - 296.4, - 296.4, - 298.9, - 296.7, - ], - [ - 291.3, - 290.2, - 288.0, - 289.4, - 292.3, - 290.7, - 288.5, - 290.8, - 294.6, - 292.4, - 289.6, - 291.6, - 289.7, - 284.3, - 288.0, - 294.4, - 293.9, - 294.1, - 293.4, - 295.5, - 295.5, - 296.2, - 295.2, - 294.9, - 293.9, - 294.2, - 292.7, - 292.6, - 293.6, - 291.7, - 292.2, - 293.6, - 292.9, - 294.3, - 285.4, - 293.1, - 295.9, - 295.9, - 296.1, - 297.0, - 297.1, - 297.1, - 295.0, - 296.2, - 296.1, - 294.4, - 292.3, - 292.1, - ], - [ - 287.1, - 286.7, - 286.8, - 287.3, - 288.0, - 285.4, - 288.1, - 284.9, - 285.0, - 283.9, - 283.1, - 276.6, - 274.9, - 275.4, - 276.8, - 278.7, - 285.8, - 289.5, - 288.4, - 289.9, - 290.2, - 292.1, - 291.7, - 293.1, - 291.6, - 290.8, - 291.0, - 290.7, - 289.5, - 290.8, - 290.5, - 289.8, - 289.7, - 284.3, - 280.6, - 285.4, - 287.7, - 290.5, - 292.7, - 293.9, - 294.3, - 294.3, - 293.7, - 293.2, - 294.4, - 292.7, - 289.9, - 287.8, - ], - [ - 280.2, - 285.6, - 288.6, - 289.1, - 287.3, - 280.4, - 277.4, - 276.9, - 274.4, - 273.7, - 259.0, - 252.1, - 252.8, - 264.0, - 272.6, - 274.0, - 278.0, - 279.8, - 278.2, - 283.8, - 286.2, - 286.4, - 287.5, - 288.6, - 287.6, - 286.8, - 287.2, - 287.6, - 288.1, - 287.8, - 288.5, - 286.2, - 280.6, - 273.6, - 274.5, - 274.8, - 273.3, - 280.8, - 288.7, - 290.5, - 289.6, - 291.6, - 291.4, - 291.5, - 292.3, - 291.7, - 290.0, - 281.8, - ], - [ - 283.8, - 283.9, - 282.7, - 279.1, - 274.5, - 270.1, - 276.5, - 276.1, - 273.0, - 264.4, - 261.2, - 262.8, - 258.6, - 260.2, - 262.5, - 264.3, - 257.8, - 262.8, - 270.9, - 273.5, - 279.0, - 280.7, - 280.7, - 282.3, - 282.4, - 282.9, - 281.4, - 284.0, - 282.9, - 284.8, - 284.4, - 280.3, - 267.5, - 264.5, - 267.8, - 265.6, - 266.5, - 269.4, - 279.7, - 284.7, - 285.4, - 286.4, - 286.7, - 288.1, - 289.2, - 288.4, - 284.5, - 278.6, - ], - [ - 274.9, - 269.1, - 266.5, - 266.7, - 275.0, - 272.5, - 272.6, - 267.4, - 265.5, - 263.8, - 259.4, - 256.3, - 253.8, - 253.2, - 255.1, - 251.7, - 247.0, - 248.7, - 261.2, - 268.9, - 272.9, - 274.4, - 275.9, - 275.2, - 276.7, - 277.9, - 278.2, - 279.5, - 280.6, - 280.5, - 281.7, - 276.6, - 265.7, - 261.9, - 261.0, - 260.2, - 265.8, - 262.9, - 264.8, - 274.3, - 276.4, - 278.3, - 280.9, - 283.7, - 286.2, - 285.0, - 284.4, - 278.7, - ], - [ - 272.0, - 266.1, - 263.0, - 260.3, - 258.5, - 258.8, - 258.1, - 257.8, - 256.0, - 256.2, - 257.7, - 255.5, - 252.9, - 251.7, - 250.8, - 243.5, - 241.2, - 243.1, - 250.8, - 268.8, - 266.8, - 267.2, - 271.3, - 272.6, - 273.9, - 273.7, - 275.7, - 275.9, - 277.4, - 280.2, - 279.5, - 268.3, - 262.4, - 258.0, - 255.4, - 255.9, - 257.5, - 259.5, - 259.1, - 259.6, - 269.1, - 275.4, - 278.9, - 279.8, - 282.9, - 283.3, - 282.8, - 275.7, - ], - [ - 277.0, - 267.4, - 267.9, - 255.7, - 248.9, - 247.9, - 248.2, - 249.4, - 249.1, - 249.9, - 248.7, - 249.5, - 250.6, - 247.9, - 244.2, - 238.8, - 236.1, - 236.8, - 244.3, - 257.5, - 255.3, - 248.7, - 262.3, - 265.0, - 268.4, - 271.1, - 270.5, - 272.9, - 274.2, - 273.9, - 266.9, - 261.4, - 260.6, - 257.2, - 252.1, - 249.6, - 251.7, - 256.5, - 251.5, - 254.6, - 269.6, - 274.4, - 279.0, - 279.3, - 281.8, - 281.5, - 282.5, - 274.8, - ], - [ - 274.1, - 262.4, - 258.9, - 246.6, - 242.9, - 240.3, - 240.1, - 240.9, - 239.4, - 239.1, - 239.1, - 239.6, - 237.9, - 236.6, - 233.5, - 230.9, - 228.4, - 231.3, - 235.3, - 238.9, - 238.5, - 242.5, - 244.2, - 247.1, - 257.4, - 264.3, - 255.1, - 257.1, - 256.5, - 255.2, - 256.2, - 253.6, - 251.6, - 248.7, - 245.2, - 246.0, - 247.4, - 248.1, - 249.6, - 257.7, - 269.3, - 259.9, - 265.2, - 274.8, - 277.1, - 273.9, - 277.3, - 279.2, - ], - [ - 272.7, - 268.2, - 256.6, - 248.0, - 249.9, - 245.1, - 240.2, - 234.0, - 234.0, - 231.0, - 229.3, - 231.7, - 231.6, - 229.1, - 228.7, - 228.7, - 228.6, - 230.4, - 231.1, - 231.5, - 232.6, - 236.6, - 238.2, - 240.7, - 241.6, - 245.2, - 247.1, - 244.1, - 246.0, - 248.0, - 245.9, - 242.9, - 242.4, - 241.6, - 242.4, - 241.2, - 242.2, - 241.6, - 245.7, - 254.9, - 263.2, - 250.5, - 243.6, - 248.7, - 254.6, - 261.3, - 267.8, - 271.1, - ], - [ - 267.5, - 269.3, - 267.7, - 265.7, - 262.4, - 242.9, - 236.2, - 235.2, - 235.7, - 235.3, - 233.8, - 228.7, - 229.6, - 227.4, - 229.6, - 232.4, - 233.1, - 236.1, - 236.5, - 237.0, - 236.0, - 238.1, - 238.4, - 239.0, - 239.5, - 239.2, - 242.1, - 238.3, - 238.9, - 239.6, - 241.2, - 240.3, - 239.0, - 240.0, - 242.4, - 239.9, - 238.9, - 245.3, - 251.6, - 259.2, - 247.7, - 238.6, - 236.8, - 236.2, - 240.5, - 246.3, - 248.2, - 259.4, - ], - [ - 258.7, - 258.8, - 258.6, - 254.5, - 243.2, - 240.2, - 239.2, - 238.7, - 235.0, - 233.4, - 233.7, - 233.9, - 235.1, - 233.6, - 234.7, - 236.3, - 235.6, - 237.5, - 237.8, - 236.8, - 237.7, - 236.3, - 235.8, - 237.0, - 235.2, - 234.9, - 235.7, - 235.3, - 235.5, - 235.0, - 235.9, - 236.9, - 236.8, - 237.2, - 238.5, - 238.8, - 236.7, - 232.0, - 235.2, - 237.8, - 237.1, - 236.2, - 233.4, - 235.0, - 239.1, - 240.4, - 242.8, - 253.1, - ], - [ - 235.7, - 235.5, - 235.4, - 235.1, - 234.4, - 236.7, - 235.6, - 234.8, - 236.0, - 236.9, - 235.7, - 235.4, - 237.2, - 237.8, - 237.3, - 237.0, - 237.5, - 239.1, - 239.4, - 238.7, - 237.5, - 238.9, - 238.8, - 237.6, - 235.8, - 236.8, - 236.4, - 235.0, - 234.6, - 233.8, - 233.6, - 233.5, - 233.2, - 233.9, - 233.4, - 233.1, - 234.8, - 234.0, - 233.3, - 234.8, - 235.3, - 234.6, - 233.7, - 235.7, - 238.6, - 239.0, - 237.2, - 236.8, - ], - ], - ] + src[...] = arrays["src"] dst = n.createVariable("dst", "f4", ("time_1", "lat", "lon")) dst.standard_name = "air_temperature" @@ -3196,7164 +198,7 @@ def _make_regrid_file(filename): # Don't generate this data randomly - it's useful to see the real # patterns of global temperature. - dst[...] = [ - [ - [ - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - 246.0, - ], - [ - 243.6, - 243.3, - 243.0, - 242.7, - 242.5, - 242.5, - 242.5, - 242.4, - 242.5, - 242.5, - 242.4, - 242.2, - 241.9, - 241.7, - 241.5, - 241.4, - 241.4, - 241.4, - 241.4, - 241.4, - 241.5, - 241.7, - 241.8, - 241.9, - 242.1, - 242.1, - 241.9, - 241.9, - 241.8, - 242.0, - 242.2, - 242.5, - 243.2, - 243.7, - 244.1, - 244.5, - 244.8, - 244.8, - 244.7, - 244.8, - 244.9, - 245.1, - 245.7, - 246.3, - 247.6, - 248.1, - 248.3, - 247.9, - 247.2, - 246.8, - 246.9, - 247.3, - 247.9, - 248.2, - 248.5, - 249.1, - 249.6, - 249.8, - 250.1, - 250.5, - 250.8, - 250.7, - 250.8, - 250.3, - 250.3, - 250.2, - 249.8, - 249.6, - 249.8, - 250.3, - 250.7, - 251.2, - 252.0, - 252.7, - 252.5, - 251.5, - 250.5, - 249.8, - 248.6, - 248.0, - 247.9, - 248.2, - 248.3, - 248.3, - 248.2, - 247.8, - 247.5, - 247.0, - 246.5, - 246.4, - 246.0, - 245.4, - 244.9, - 244.4, - 244.0, - 243.8, - ], - [ - 244.1, - 243.6, - 242.8, - 241.9, - 241.3, - 241.0, - 240.8, - 240.8, - 240.5, - 240.2, - 239.8, - 239.5, - 239.4, - 239.5, - 239.5, - 239.3, - 239.1, - 239.0, - 239.1, - 239.5, - 240.1, - 240.7, - 241.2, - 241.7, - 242.0, - 242.5, - 243.1, - 243.5, - 243.6, - 243.9, - 244.3, - 244.8, - 245.3, - 246.0, - 246.8, - 247.5, - 248.0, - 248.4, - 248.7, - 248.9, - 249.3, - 250.2, - 251.5, - 252.8, - 253.6, - 254.2, - 254.3, - 255.1, - 256.9, - 258.4, - 259.8, - 261.2, - 262.7, - 264.0, - 264.8, - 265.3, - 265.1, - 264.4, - 263.6, - 262.6, - 261.6, - 261.1, - 260.4, - 259.4, - 258.2, - 257.2, - 255.3, - 253.5, - 252.7, - 252.5, - 253.0, - 253.5, - 254.1, - 255.2, - 257.1, - 258.0, - 258.3, - 258.3, - 258.7, - 258.5, - 257.9, - 257.0, - 256.0, - 255.5, - 255.0, - 254.2, - 252.9, - 251.7, - 250.8, - 249.6, - 248.3, - 246.9, - 245.7, - 245.0, - 244.5, - 244.3, - ], - [ - 244.9, - 243.6, - 242.0, - 240.5, - 239.4, - 238.6, - 238.1, - 237.5, - 237.2, - 237.1, - 236.9, - 236.8, - 237.1, - 237.6, - 237.9, - 237.9, - 237.9, - 237.8, - 237.6, - 237.9, - 238.6, - 239.3, - 239.9, - 240.2, - 241.1, - 242.4, - 243.5, - 244.0, - 244.7, - 245.4, - 245.9, - 246.1, - 246.5, - 247.1, - 247.9, - 248.8, - 249.8, - 250.5, - 250.9, - 251.4, - 252.2, - 253.6, - 256.0, - 259.4, - 262.9, - 265.9, - 267.3, - 267.7, - 267.6, - 267.4, - 267.0, - 266.6, - 266.5, - 266.6, - 266.5, - 265.9, - 265.3, - 265.1, - 265.0, - 264.8, - 264.8, - 264.8, - 264.7, - 264.5, - 263.5, - 262.0, - 259.9, - 257.4, - 255.7, - 255.3, - 255.3, - 255.7, - 256.5, - 257.5, - 258.9, - 260.6, - 261.8, - 262.8, - 263.3, - 263.8, - 264.2, - 264.0, - 263.1, - 262.2, - 262.4, - 262.4, - 261.2, - 259.3, - 257.5, - 255.4, - 253.0, - 250.5, - 248.2, - 246.6, - 245.9, - 245.6, - ], - [ - 244.3, - 242.9, - 241.7, - 241.1, - 240.7, - 240.2, - 239.1, - 238.1, - 237.5, - 237.1, - 236.8, - 237.0, - 237.9, - 239.2, - 240.3, - 241.1, - 241.9, - 242.5, - 242.5, - 241.8, - 241.4, - 241.2, - 240.8, - 240.2, - 239.5, - 239.5, - 240.4, - 241.8, - 243.1, - 244.2, - 245.0, - 245.5, - 245.8, - 246.5, - 247.3, - 248.4, - 249.5, - 250.3, - 250.7, - 251.4, - 253.3, - 255.7, - 259.1, - 263.3, - 265.9, - 266.1, - 266.4, - 266.0, - 265.7, - 265.5, - 265.5, - 265.6, - 265.8, - 266.2, - 266.3, - 265.9, - 265.5, - 265.3, - 264.6, - 264.1, - 263.4, - 262.4, - 261.5, - 261.2, - 261.0, - 260.8, - 260.0, - 258.8, - 257.4, - 256.8, - 256.8, - 257.4, - 258.7, - 260.1, - 261.9, - 263.6, - 265.0, - 265.2, - 264.8, - 264.8, - 265.5, - 265.2, - 264.3, - 262.9, - 263.7, - 265.2, - 266.0, - 265.8, - 263.1, - 260.8, - 256.8, - 252.4, - 249.0, - 247.0, - 245.9, - 245.1, - ], - [ - 245.2, - 243.7, - 242.2, - 241.3, - 241.5, - 241.7, - 241.3, - 240.1, - 238.9, - 238.5, - 238.6, - 239.0, - 239.9, - 241.3, - 242.4, - 243.3, - 245.4, - 246.5, - 246.4, - 246.4, - 246.4, - 245.9, - 245.2, - 243.8, - 242.2, - 241.2, - 241.2, - 241.7, - 242.5, - 243.3, - 243.8, - 244.2, - 244.4, - 244.5, - 245.4, - 246.5, - 247.4, - 247.6, - 247.6, - 248.0, - 248.9, - 250.8, - 254.1, - 259.1, - 262.6, - 265.6, - 265.6, - 265.2, - 264.8, - 264.8, - 265.4, - 266.1, - 266.6, - 267.2, - 267.2, - 267.2, - 266.8, - 266.7, - 265.6, - 263.3, - 261.6, - 259.5, - 258.2, - 258.3, - 259.4, - 260.8, - 261.7, - 261.7, - 261.5, - 260.9, - 260.2, - 260.3, - 261.3, - 262.4, - 263.9, - 266.1, - 267.0, - 267.7, - 267.5, - 267.2, - 271.5, - 271.5, - 271.6, - 271.7, - 271.6, - 271.5, - 271.6, - 271.4, - 263.7, - 260.6, - 256.3, - 252.6, - 249.8, - 248.1, - 247.1, - 246.2, - ], - [ - 248.4, - 246.8, - 245.6, - 244.3, - 244.1, - 244.4, - 244.0, - 242.7, - 241.0, - 240.4, - 240.9, - 242.0, - 243.3, - 243.7, - 243.4, - 243.9, - 245.3, - 248.8, - 250.6, - 250.9, - 250.5, - 248.5, - 246.7, - 245.5, - 244.2, - 243.3, - 243.6, - 244.7, - 245.7, - 245.8, - 245.3, - 244.4, - 243.7, - 243.7, - 244.8, - 246.1, - 246.7, - 246.4, - 246.4, - 247.1, - 249.2, - 251.9, - 255.3, - 259.6, - 261.3, - 270.2, - 270.4, - 271.0, - 271.1, - 271.2, - 271.4, - 271.5, - 271.5, - 271.5, - 271.5, - 271.4, - 271.3, - 271.7, - 272.0, - 272.1, - 267.4, - 266.9, - 266.4, - 266.3, - 266.4, - 266.6, - 267.3, - 268.2, - 268.5, - 268.2, - 266.9, - 265.2, - 264.3, - 264.3, - 265.8, - 267.1, - 267.0, - 266.0, - 265.7, - 266.6, - 271.6, - 271.6, - 271.5, - 271.5, - 271.4, - 271.5, - 271.6, - 271.5, - 271.3, - 271.4, - 262.7, - 259.6, - 254.4, - 252.3, - 251.2, - 249.6, - ], - [ - 257.5, - 254.5, - 252.1, - 249.8, - 249.2, - 250.4, - 251.5, - 250.9, - 250.6, - 250.5, - 249.1, - 248.0, - 247.6, - 247.1, - 246.9, - 247.4, - 249.1, - 253.6, - 256.8, - 257.8, - 255.1, - 251.7, - 247.6, - 245.1, - 244.0, - 243.2, - 243.2, - 244.8, - 246.9, - 247.9, - 247.8, - 247.1, - 246.2, - 245.6, - 245.4, - 245.6, - 246.3, - 246.7, - 248.1, - 250.9, - 253.0, - 253.8, - 254.5, - 254.6, - 256.0, - 270.2, - 270.8, - 271.3, - 271.2, - 271.3, - 271.4, - 271.4, - 271.3, - 271.8, - 272.6, - 272.6, - 271.1, - 271.1, - 271.3, - 271.6, - 271.7, - 271.7, - 271.6, - 271.5, - 271.5, - 271.5, - 271.4, - 271.4, - 271.2, - 271.6, - 271.7, - 271.6, - 271.5, - 272.3, - 272.1, - 271.5, - 271.8, - 268.5, - 265.9, - 264.9, - 271.5, - 271.3, - 271.3, - 271.4, - 271.5, - 271.5, - 271.5, - 271.5, - 271.3, - 271.2, - 271.1, - 271.2, - 271.3, - 263.1, - 261.2, - 259.6, - ], - [ - 271.3, - 271.4, - 271.5, - 271.6, - 271.6, - 271.5, - 271.3, - 271.3, - 264.0, - 264.2, - 263.1, - 259.6, - 256.1, - 254.7, - 252.4, - 250.5, - 251.9, - 254.3, - 259.4, - 262.6, - 263.1, - 258.8, - 255.1, - 253.2, - 252.1, - 251.8, - 251.8, - 252.1, - 252.9, - 253.4, - 253.9, - 254.0, - 253.7, - 252.8, - 251.7, - 250.5, - 249.8, - 250.2, - 252.7, - 254.8, - 256.8, - 258.2, - 259.7, - 270.3, - 270.2, - 270.5, - 271.2, - 271.1, - 271.3, - 271.5, - 271.6, - 271.6, - 272.2, - 273.2, - 274.1, - 274.1, - 273.8, - 273.9, - 274.0, - 273.9, - 273.4, - 272.8, - 272.5, - 272.7, - 273.6, - 274.0, - 273.8, - 273.6, - 273.7, - 273.8, - 273.2, - 272.8, - 273.5, - 274.4, - 275.4, - 275.3, - 272.6, - 268.4, - 265.9, - 265.2, - 271.4, - 271.3, - 271.0, - 271.2, - 271.5, - 271.5, - 271.3, - 271.2, - 271.2, - 271.2, - 271.2, - 271.3, - 271.4, - 271.4, - 271.3, - 271.3, - ], - [ - 272.8, - 273.1, - 273.0, - 272.9, - 272.8, - 272.5, - 271.3, - 271.2, - 271.1, - 271.2, - 271.1, - 271.2, - 271.0, - 263.1, - 260.1, - 260.2, - 270.2, - 269.5, - 269.5, - 269.8, - 270.1, - 269.9, - 270.9, - 260.1, - 259.0, - 257.8, - 256.3, - 255.8, - 256.3, - 258.0, - 259.9, - 261.0, - 260.8, - 259.9, - 258.9, - 257.2, - 255.9, - 257.6, - 261.3, - 270.5, - 270.7, - 270.4, - 270.6, - 271.1, - 271.4, - 271.2, - 271.3, - 272.4, - 272.8, - 273.2, - 273.4, - 273.4, - 273.6, - 274.1, - 274.2, - 274.2, - 274.2, - 274.2, - 274.0, - 273.9, - 273.9, - 273.9, - 274.0, - 274.1, - 274.4, - 274.6, - 274.5, - 274.4, - 274.3, - 274.4, - 274.6, - 274.5, - 274.5, - 274.8, - 275.1, - 275.2, - 275.1, - 274.6, - 268.9, - 267.9, - 271.2, - 271.1, - 271.2, - 272.2, - 271.4, - 271.1, - 271.0, - 271.0, - 271.6, - 272.2, - 271.4, - 271.7, - 271.3, - 271.7, - 271.6, - 272.3, - ], - [ - 274.2, - 274.2, - 274.1, - 274.3, - 274.3, - 274.3, - 274.3, - 274.5, - 274.1, - 274.5, - 274.7, - 274.5, - 271.5, - 271.2, - 271.1, - 270.9, - 270.8, - 270.7, - 270.8, - 270.9, - 271.0, - 271.1, - 271.0, - 270.8, - 270.8, - 270.8, - 270.9, - 270.9, - 271.0, - 271.1, - 271.0, - 271.0, - 271.1, - 271.1, - 271.1, - 271.1, - 271.0, - 271.0, - 270.9, - 270.9, - 271.1, - 271.1, - 271.2, - 271.3, - 271.4, - 272.1, - 273.3, - 273.9, - 274.1, - 274.3, - 274.6, - 274.5, - 274.5, - 274.5, - 274.4, - 274.3, - 274.2, - 274.2, - 274.1, - 274.0, - 273.9, - 274.1, - 274.3, - 274.4, - 274.4, - 274.6, - 274.6, - 274.7, - 274.8, - 274.9, - 275.1, - 275.4, - 275.4, - 275.4, - 275.6, - 275.6, - 275.4, - 275.2, - 275.1, - 269.6, - 271.3, - 271.3, - 272.3, - 272.8, - 272.9, - 273.0, - 273.2, - 273.4, - 273.5, - 273.8, - 273.8, - 274.0, - 274.0, - 274.1, - 274.0, - 274.0, - ], - [ - 274.7, - 274.6, - 274.4, - 274.4, - 274.5, - 274.4, - 274.3, - 274.4, - 274.4, - 274.5, - 274.8, - 275.0, - 274.8, - 274.4, - 274.0, - 273.8, - 274.0, - 274.5, - 274.5, - 274.4, - 274.3, - 274.3, - 272.9, - 271.3, - 271.2, - 271.3, - 273.0, - 272.9, - 273.0, - 273.6, - 273.6, - 273.4, - 272.9, - 272.7, - 272.9, - 272.8, - 271.8, - 271.6, - 271.6, - 272.3, - 274.1, - 274.6, - 274.6, - 274.5, - 274.8, - 274.7, - 274.9, - 275.2, - 275.1, - 275.0, - 275.0, - 275.0, - 275.1, - 275.2, - 275.2, - 275.2, - 275.2, - 275.0, - 274.9, - 274.8, - 274.9, - 274.9, - 274.9, - 275.0, - 275.0, - 275.1, - 275.1, - 275.2, - 275.4, - 275.5, - 275.8, - 276.0, - 275.9, - 276.1, - 276.4, - 276.5, - 276.4, - 276.1, - 275.5, - 273.0, - 271.6, - 272.0, - 273.1, - 273.8, - 274.5, - 274.8, - 274.9, - 274.9, - 274.3, - 274.8, - 274.9, - 274.8, - 274.7, - 274.8, - 274.8, - 274.8, - ], - [ - 275.0, - 274.9, - 274.6, - 274.3, - 274.3, - 274.4, - 274.2, - 274.2, - 274.3, - 274.5, - 274.6, - 274.7, - 274.8, - 274.9, - 275.0, - 275.1, - 275.1, - 275.1, - 275.1, - 275.0, - 274.6, - 274.3, - 274.0, - 271.7, - 272.1, - 272.6, - 273.2, - 274.1, - 274.3, - 274.4, - 274.6, - 274.7, - 274.7, - 274.8, - 274.9, - 274.6, - 274.7, - 274.9, - 274.9, - 275.0, - 275.2, - 275.4, - 275.5, - 275.4, - 275.5, - 275.4, - 275.7, - 275.7, - 275.6, - 275.6, - 275.6, - 275.6, - 275.7, - 275.7, - 275.6, - 275.6, - 275.6, - 275.6, - 275.9, - 275.7, - 275.5, - 275.6, - 275.7, - 275.8, - 275.8, - 275.8, - 275.9, - 276.0, - 276.0, - 276.1, - 276.3, - 276.4, - 276.4, - 276.5, - 276.6, - 276.8, - 276.9, - 277.2, - 277.2, - 277.0, - 276.4, - 275.6, - 275.8, - 276.0, - 276.3, - 276.2, - 276.1, - 276.3, - 276.2, - 276.2, - 276.1, - 275.7, - 275.5, - 275.5, - 275.3, - 275.0, - ], - [ - 275.2, - 275.1, - 274.9, - 274.9, - 275.2, - 275.3, - 275.2, - 275.0, - 275.0, - 275.2, - 275.5, - 275.6, - 275.5, - 275.5, - 275.5, - 275.6, - 275.8, - 275.9, - 275.8, - 275.3, - 274.9, - 274.5, - 274.6, - 274.2, - 274.3, - 273.7, - 273.7, - 273.7, - 273.9, - 274.0, - 274.4, - 274.5, - 274.6, - 274.9, - 275.0, - 275.0, - 275.0, - 275.1, - 275.1, - 275.4, - 275.6, - 275.9, - 276.3, - 276.6, - 277.0, - 276.5, - 276.5, - 276.5, - 276.5, - 276.6, - 276.6, - 276.7, - 277.0, - 276.7, - 276.4, - 276.3, - 276.1, - 276.2, - 277.1, - 276.7, - 276.5, - 276.4, - 276.8, - 277.0, - 276.9, - 276.9, - 277.0, - 277.0, - 276.9, - 276.9, - 277.0, - 276.9, - 276.9, - 277.1, - 277.2, - 277.7, - 277.9, - 278.7, - 279.2, - 279.0, - 278.1, - 278.2, - 278.1, - 278.3, - 278.6, - 278.0, - 277.3, - 277.0, - 277.1, - 277.1, - 277.0, - 276.7, - 276.4, - 276.3, - 276.1, - 275.6, - ], - [ - 276.0, - 275.8, - 275.7, - 275.8, - 276.1, - 276.2, - 276.2, - 276.1, - 275.9, - 276.0, - 276.3, - 276.4, - 276.3, - 276.4, - 276.3, - 276.3, - 276.5, - 276.5, - 276.3, - 276.0, - 275.7, - 275.1, - 275.7, - 275.6, - 275.7, - 275.7, - 276.0, - 275.6, - 276.0, - 275.4, - 275.8, - 275.7, - 275.6, - 275.6, - 275.9, - 275.9, - 276.2, - 276.5, - 276.2, - 276.1, - 276.3, - 276.1, - 277.0, - 277.3, - 278.2, - 278.2, - 277.7, - 277.5, - 277.5, - 277.6, - 277.5, - 277.8, - 278.2, - 278.2, - 278.1, - 278.0, - 277.6, - 277.2, - 278.1, - 278.1, - 278.0, - 278.0, - 278.8, - 278.7, - 278.4, - 278.3, - 278.2, - 278.2, - 278.1, - 278.0, - 278.1, - 278.1, - 278.3, - 278.4, - 278.5, - 278.9, - 279.5, - 280.2, - 279.7, - 281.3, - 280.2, - 279.7, - 279.5, - 279.3, - 279.1, - 278.3, - 277.8, - 277.9, - 278.3, - 278.3, - 278.2, - 277.7, - 277.4, - 277.2, - 276.7, - 276.3, - ], - [ - 277.2, - 277.2, - 277.4, - 277.5, - 277.5, - 277.5, - 277.4, - 277.2, - 276.9, - 276.9, - 277.1, - 277.2, - 277.4, - 277.5, - 277.5, - 277.4, - 277.4, - 277.2, - 277.2, - 277.0, - 276.8, - 276.2, - 277.4, - 277.5, - 277.4, - 277.2, - 277.5, - 277.7, - 277.9, - 277.6, - 278.0, - 277.4, - 277.5, - 277.4, - 277.6, - 277.9, - 278.4, - 278.3, - 278.2, - 278.6, - 279.2, - 278.8, - 279.5, - 279.4, - 280.0, - 280.7, - 280.1, - 278.8, - 278.6, - 278.9, - 278.8, - 279.0, - 279.6, - 280.1, - 280.0, - 279.8, - 279.7, - 279.4, - 279.7, - 280.1, - 280.3, - 280.5, - 280.7, - 280.3, - 280.1, - 280.1, - 279.9, - 279.8, - 279.5, - 279.3, - 279.3, - 279.3, - 279.4, - 279.5, - 279.7, - 280.1, - 280.4, - 279.8, - 282.0, - 281.8, - 281.2, - 281.1, - 280.4, - 279.5, - 279.1, - 278.7, - 278.6, - 278.7, - 279.0, - 279.1, - 279.5, - 279.0, - 278.5, - 278.0, - 277.2, - 277.3, - ], - [ - 279.0, - 279.3, - 279.5, - 279.4, - 279.1, - 278.8, - 278.6, - 278.3, - 278.3, - 278.2, - 278.2, - 278.3, - 278.6, - 278.8, - 278.6, - 278.5, - 278.4, - 278.3, - 278.7, - 278.3, - 278.5, - 280.1, - 281.1, - 280.7, - 280.3, - 280.1, - 280.0, - 280.2, - 280.1, - 279.9, - 279.7, - 279.5, - 279.9, - 279.8, - 280.0, - 280.9, - 280.9, - 280.8, - 280.9, - 281.1, - 281.1, - 280.8, - 281.1, - 281.1, - 281.6, - 281.8, - 281.0, - 280.1, - 280.2, - 280.5, - 281.0, - 281.1, - 281.4, - 282.2, - 281.8, - 281.6, - 281.5, - 281.4, - 281.6, - 282.2, - 282.3, - 282.4, - 282.1, - 281.9, - 281.9, - 281.8, - 281.4, - 281.2, - 280.9, - 280.7, - 280.6, - 280.5, - 280.6, - 280.7, - 280.9, - 281.0, - 281.1, - 279.2, - 284.1, - 283.7, - 282.6, - 281.6, - 280.6, - 280.3, - 280.1, - 279.7, - 279.5, - 279.5, - 279.9, - 279.9, - 280.4, - 280.3, - 280.0, - 279.7, - 279.0, - 279.3, - ], - [ - 281.7, - 281.7, - 281.4, - 281.2, - 280.9, - 280.5, - 280.2, - 279.8, - 280.0, - 280.0, - 279.8, - 280.0, - 280.2, - 280.3, - 280.0, - 279.9, - 279.9, - 279.8, - 280.4, - 280.0, - 281.9, - 282.4, - 282.6, - 282.2, - 282.3, - 282.2, - 281.9, - 281.9, - 281.7, - 281.5, - 281.4, - 281.6, - 282.0, - 282.0, - 282.3, - 282.7, - 282.4, - 282.5, - 282.5, - 282.5, - 282.7, - 282.6, - 282.7, - 282.8, - 283.2, - 283.1, - 282.7, - 282.0, - 282.5, - 283.3, - 283.9, - 283.5, - 283.7, - 284.1, - 283.9, - 283.6, - 283.4, - 283.4, - 283.6, - 283.9, - 283.9, - 283.8, - 283.6, - 283.6, - 283.6, - 283.4, - 283.1, - 282.8, - 282.6, - 282.3, - 282.2, - 281.9, - 281.8, - 281.9, - 281.9, - 281.9, - 281.9, - 277.8, - 287.9, - 285.9, - 283.8, - 282.2, - 282.2, - 282.4, - 282.3, - 281.9, - 281.1, - 281.0, - 281.1, - 281.0, - 281.5, - 282.3, - 282.3, - 282.3, - 282.0, - 282.0, - ], - [ - 284.2, - 283.9, - 283.5, - 283.1, - 282.7, - 282.4, - 282.0, - 281.6, - 281.8, - 281.5, - 281.5, - 281.9, - 281.9, - 282.1, - 281.9, - 281.9, - 282.0, - 281.8, - 282.2, - 282.2, - 284.0, - 284.2, - 284.2, - 284.1, - 284.2, - 283.9, - 283.7, - 283.6, - 283.6, - 283.6, - 283.6, - 283.8, - 283.9, - 283.9, - 284.2, - 284.1, - 284.1, - 284.2, - 284.4, - 285.2, - 285.7, - 285.1, - 284.9, - 284.9, - 284.9, - 283.6, - 287.7, - 288.0, - 286.9, - 288.5, - 287.4, - 286.6, - 287.0, - 286.3, - 286.2, - 285.8, - 285.7, - 285.7, - 285.7, - 285.8, - 285.7, - 285.6, - 285.3, - 285.2, - 285.0, - 284.9, - 284.7, - 284.4, - 284.3, - 283.9, - 283.6, - 283.4, - 283.0, - 283.0, - 282.8, - 283.1, - 283.2, - 280.2, - 289.8, - 289.2, - 285.5, - 284.6, - 284.9, - 285.1, - 284.9, - 283.8, - 283.0, - 283.1, - 283.0, - 282.7, - 283.5, - 284.8, - 284.8, - 284.9, - 285.1, - 284.7, - ], - [ - 286.4, - 286.1, - 285.7, - 285.2, - 284.9, - 284.5, - 284.0, - 283.6, - 283.8, - 283.3, - 283.2, - 284.3, - 284.8, - 285.0, - 285.3, - 285.1, - 284.9, - 284.8, - 285.3, - 285.4, - 286.6, - 286.3, - 286.2, - 286.2, - 286.2, - 286.0, - 285.9, - 285.7, - 285.7, - 285.7, - 285.5, - 285.5, - 285.6, - 285.8, - 285.8, - 285.9, - 286.2, - 286.3, - 286.4, - 287.2, - 287.9, - 287.7, - 287.4, - 287.2, - 287.5, - 288.2, - 286.9, - 291.5, - 291.3, - 290.5, - 289.1, - 288.8, - 288.6, - 287.9, - 287.7, - 287.4, - 287.5, - 287.6, - 287.6, - 287.6, - 287.3, - 287.1, - 286.9, - 286.9, - 286.8, - 286.6, - 286.3, - 286.1, - 286.0, - 285.7, - 285.4, - 285.1, - 284.6, - 284.2, - 284.0, - 284.6, - 284.8, - 281.5, - 289.1, - 290.4, - 287.6, - 289.3, - 289.9, - 287.9, - 286.6, - 286.2, - 286.0, - 285.5, - 285.5, - 286.1, - 287.1, - 287.8, - 287.2, - 287.5, - 287.3, - 287.0, - ], - [ - 288.7, - 288.5, - 288.1, - 287.6, - 287.1, - 286.8, - 287.2, - 286.7, - 286.5, - 285.8, - 286.0, - 287.4, - 287.8, - 288.3, - 288.7, - 288.2, - 287.8, - 288.0, - 288.5, - 288.7, - 289.1, - 288.6, - 288.5, - 288.2, - 288.0, - 287.9, - 287.6, - 287.4, - 287.4, - 287.4, - 287.3, - 287.3, - 287.5, - 287.6, - 287.7, - 287.8, - 287.9, - 288.0, - 288.1, - 289.0, - 290.0, - 291.0, - 290.2, - 289.5, - 289.6, - 290.0, - 291.1, - 290.8, - 292.5, - 291.4, - 290.5, - 290.1, - 289.7, - 289.4, - 289.2, - 289.3, - 289.6, - 289.9, - 290.0, - 289.6, - 289.3, - 289.2, - 289.0, - 289.0, - 289.0, - 288.7, - 288.4, - 288.2, - 288.0, - 287.6, - 287.2, - 286.8, - 286.3, - 286.0, - 285.8, - 286.3, - 286.6, - 283.4, - 291.9, - 296.0, - 290.3, - 292.7, - 292.4, - 290.3, - 289.5, - 289.2, - 288.9, - 288.8, - 289.2, - 289.7, - 289.9, - 289.6, - 289.5, - 289.6, - 289.4, - 289.1, - ], - [ - 290.7, - 290.8, - 290.4, - 290.0, - 290.5, - 291.8, - 292.7, - 293.0, - 293.4, - 292.1, - 291.7, - 292.0, - 291.9, - 291.8, - 291.6, - 291.2, - 291.2, - 291.3, - 291.3, - 291.3, - 291.1, - 290.7, - 290.7, - 290.2, - 289.7, - 289.5, - 289.3, - 289.1, - 289.1, - 289.1, - 289.2, - 289.5, - 289.8, - 289.6, - 289.4, - 289.5, - 289.6, - 290.2, - 293.9, - 290.9, - 292.4, - 294.3, - 292.9, - 291.8, - 291.8, - 291.9, - 292.2, - 292.8, - 292.7, - 292.4, - 292.3, - 291.9, - 291.4, - 291.0, - 291.1, - 291.3, - 291.6, - 292.4, - 292.3, - 292.0, - 291.8, - 291.6, - 291.5, - 291.4, - 291.3, - 291.0, - 290.7, - 290.5, - 290.2, - 289.8, - 289.4, - 288.8, - 288.3, - 287.9, - 287.5, - 288.1, - 288.3, - 286.7, - 294.7, - 298.5, - 297.5, - 294.4, - 293.8, - 292.8, - 292.0, - 291.3, - 291.0, - 291.1, - 291.2, - 291.5, - 291.8, - 291.9, - 291.9, - 291.5, - 291.3, - 291.1, - ], - [ - 292.2, - 292.3, - 292.0, - 292.2, - 292.3, - 291.9, - 293.8, - 295.6, - 296.0, - 296.2, - 295.2, - 293.9, - 293.5, - 293.3, - 293.3, - 293.1, - 293.2, - 293.2, - 293.1, - 292.8, - 292.5, - 292.1, - 292.1, - 291.7, - 291.3, - 291.0, - 290.6, - 290.4, - 290.3, - 290.4, - 290.5, - 290.1, - 291.1, - 291.0, - 290.9, - 291.1, - 291.6, - 297.3, - 298.4, - 294.6, - 291.0, - 296.0, - 294.8, - 294.0, - 294.1, - 294.0, - 293.7, - 294.0, - 293.6, - 293.7, - 293.5, - 293.1, - 292.7, - 292.5, - 292.9, - 293.4, - 293.7, - 294.1, - 294.1, - 294.0, - 293.9, - 293.8, - 293.7, - 293.6, - 293.5, - 293.4, - 293.0, - 292.8, - 292.5, - 292.1, - 291.4, - 290.8, - 290.1, - 289.5, - 289.0, - 289.7, - 288.3, - 292.3, - 297.3, - 301.5, - 301.0, - 299.1, - 295.6, - 294.8, - 293.4, - 293.0, - 292.9, - 293.0, - 293.2, - 293.5, - 293.9, - 294.0, - 293.8, - 293.3, - 292.8, - 292.4, - ], - [ - 292.9, - 292.5, - 291.9, - 292.0, - 291.5, - 295.9, - 289.6, - 290.0, - 297.4, - 297.2, - 295.9, - 295.2, - 295.1, - 295.2, - 295.3, - 295.2, - 295.1, - 294.9, - 294.6, - 294.2, - 293.8, - 293.4, - 293.2, - 292.8, - 292.3, - 291.8, - 291.6, - 291.1, - 291.5, - 292.2, - 293.2, - 297.0, - 294.3, - 292.2, - 292.0, - 292.5, - 298.2, - 298.2, - 297.6, - 295.0, - 292.4, - 296.8, - 295.8, - 295.7, - 295.8, - 295.4, - 295.0, - 294.9, - 294.6, - 294.8, - 294.5, - 294.1, - 294.0, - 294.2, - 294.6, - 294.9, - 295.3, - 295.4, - 295.4, - 295.4, - 295.5, - 295.4, - 295.4, - 295.4, - 295.4, - 295.3, - 294.9, - 294.5, - 294.2, - 293.8, - 293.2, - 292.4, - 291.6, - 290.8, - 290.5, - 290.7, - 287.0, - 294.5, - 299.9, - 301.8, - 303.2, - 299.2, - 297.4, - 296.8, - 295.7, - 294.8, - 294.9, - 294.9, - 294.8, - 295.0, - 295.3, - 295.2, - 294.9, - 294.3, - 293.7, - 293.3, - ], - [ - 293.3, - 293.0, - 292.8, - 293.8, - 293.2, - 298.3, - 293.4, - 289.7, - 290.7, - 298.8, - 297.3, - 297.2, - 297.1, - 296.7, - 296.7, - 296.6, - 296.2, - 295.8, - 295.4, - 295.0, - 294.6, - 294.1, - 293.7, - 293.3, - 293.1, - 293.1, - 293.4, - 292.4, - 292.9, - 294.0, - 295.2, - 299.8, - 293.3, - 293.0, - 293.2, - 297.7, - 300.7, - 298.8, - 297.4, - 298.5, - 294.3, - 297.7, - 296.6, - 296.9, - 296.7, - 296.2, - 295.8, - 295.7, - 295.6, - 295.8, - 295.4, - 295.3, - 295.4, - 295.8, - 295.9, - 296.0, - 296.3, - 296.4, - 296.5, - 296.6, - 296.7, - 296.6, - 296.6, - 296.6, - 296.5, - 296.3, - 295.9, - 295.6, - 295.2, - 294.6, - 294.0, - 293.3, - 292.5, - 291.7, - 291.1, - 290.3, - 286.5, - 294.7, - 297.7, - 304.8, - 304.4, - 300.7, - 297.3, - 298.0, - 297.6, - 296.6, - 296.3, - 296.2, - 295.9, - 295.8, - 295.8, - 295.6, - 295.1, - 294.6, - 293.9, - 293.6, - ], - [ - 293.5, - 293.2, - 293.4, - 293.4, - 291.1, - 297.3, - 294.5, - 292.8, - 290.8, - 299.7, - 299.5, - 299.4, - 299.1, - 298.2, - 297.6, - 297.2, - 296.7, - 296.1, - 295.6, - 295.2, - 294.8, - 294.4, - 294.1, - 293.8, - 293.9, - 294.2, - 294.2, - 293.8, - 294.3, - 295.6, - 297.2, - 300.9, - 295.2, - 296.9, - 298.7, - 299.6, - 301.0, - 300.8, - 300.3, - 300.0, - 295.9, - 298.4, - 297.6, - 297.7, - 297.5, - 297.2, - 296.9, - 296.8, - 296.9, - 296.8, - 296.5, - 296.5, - 296.7, - 296.9, - 296.9, - 296.9, - 297.1, - 297.2, - 297.3, - 297.3, - 297.3, - 297.2, - 297.1, - 297.0, - 296.7, - 296.3, - 296.0, - 295.7, - 295.4, - 294.9, - 294.3, - 293.7, - 293.0, - 292.2, - 291.3, - 290.6, - 290.3, - 293.8, - 283.8, - 303.0, - 303.3, - 301.1, - 295.7, - 296.0, - 299.0, - 298.0, - 297.4, - 297.0, - 296.4, - 296.0, - 295.7, - 295.3, - 294.8, - 294.4, - 293.9, - 293.7, - ], - [ - 293.7, - 293.6, - 294.4, - 293.9, - 294.2, - 296.8, - 296.3, - 294.3, - 293.1, - 298.0, - 300.5, - 300.5, - 298.9, - 299.1, - 298.0, - 297.7, - 297.0, - 296.4, - 295.9, - 295.5, - 295.1, - 294.9, - 294.9, - 294.7, - 295.0, - 295.3, - 295.1, - 295.0, - 295.6, - 296.9, - 298.5, - 301.8, - 297.3, - 301.1, - 302.4, - 299.7, - 299.6, - 301.0, - 300.8, - 298.4, - 297.0, - 299.1, - 298.5, - 298.5, - 298.3, - 298.1, - 298.0, - 298.0, - 297.9, - 297.6, - 297.5, - 297.6, - 297.7, - 297.8, - 297.7, - 297.7, - 297.7, - 297.9, - 298.0, - 297.8, - 297.6, - 297.4, - 297.1, - 296.7, - 296.3, - 295.9, - 295.6, - 295.4, - 295.2, - 294.7, - 294.2, - 293.6, - 292.9, - 292.1, - 291.3, - 290.9, - 291.9, - 294.1, - 280.6, - 297.3, - 301.2, - 301.0, - 296.7, - 296.5, - 299.8, - 298.7, - 297.8, - 297.1, - 296.3, - 295.8, - 295.4, - 295.0, - 294.6, - 294.3, - 293.9, - 293.8, - ], - [ - 294.3, - 294.7, - 294.9, - 294.8, - 300.4, - 293.8, - 295.8, - 294.9, - 295.8, - 298.2, - 301.2, - 301.1, - 295.4, - 299.3, - 298.7, - 298.3, - 297.7, - 297.1, - 296.5, - 296.1, - 295.8, - 295.8, - 295.9, - 295.6, - 295.9, - 296.1, - 295.9, - 296.0, - 296.6, - 297.9, - 299.7, - 302.8, - 300.9, - 303.2, - 301.0, - 298.0, - 298.2, - 299.2, - 299.3, - 300.2, - 299.3, - 300.0, - 299.3, - 299.1, - 298.8, - 298.6, - 298.6, - 298.7, - 298.7, - 298.5, - 298.5, - 298.4, - 298.5, - 298.5, - 298.5, - 298.5, - 298.5, - 298.6, - 298.6, - 298.4, - 298.0, - 297.5, - 297.0, - 296.4, - 295.8, - 295.3, - 294.9, - 294.7, - 294.5, - 294.2, - 293.7, - 293.2, - 292.5, - 291.7, - 291.5, - 291.8, - 293.3, - 293.4, - 281.5, - 294.6, - 300.3, - 299.7, - 299.1, - 296.2, - 294.6, - 299.0, - 298.1, - 297.1, - 296.2, - 295.7, - 295.3, - 295.0, - 294.7, - 294.3, - 294.0, - 294.1, - ], - [ - 295.1, - 295.6, - 295.7, - 295.2, - 294.5, - 294.9, - 296.9, - 295.3, - 294.1, - 297.9, - 301.5, - 300.9, - 301.8, - 298.2, - 298.7, - 298.8, - 298.2, - 297.6, - 297.1, - 296.8, - 296.7, - 296.9, - 297.0, - 297.0, - 297.1, - 297.0, - 296.9, - 297.1, - 297.6, - 298.7, - 300.4, - 302.7, - 302.9, - 301.2, - 299.5, - 299.3, - 299.9, - 299.3, - 298.6, - 297.4, - 300.6, - 300.3, - 299.9, - 299.7, - 299.4, - 299.4, - 299.4, - 299.4, - 299.4, - 299.2, - 299.1, - 299.0, - 299.0, - 299.3, - 299.4, - 299.5, - 299.3, - 299.2, - 299.2, - 298.7, - 298.1, - 297.5, - 296.9, - 296.1, - 295.4, - 294.8, - 294.3, - 294.0, - 293.7, - 293.4, - 293.1, - 292.5, - 292.0, - 291.8, - 292.4, - 293.3, - 293.7, - 292.8, - 281.2, - 293.6, - 299.9, - 300.7, - 298.9, - 296.3, - 294.1, - 296.1, - 298.5, - 297.5, - 296.7, - 296.1, - 295.7, - 295.4, - 295.0, - 294.7, - 294.5, - 294.7, - ], - [ - 295.7, - 296.3, - 296.6, - 296.3, - 293.9, - 295.3, - 295.8, - 295.6, - 294.4, - 297.6, - 300.6, - 300.6, - 301.4, - 298.0, - 298.9, - 299.1, - 298.6, - 298.0, - 297.7, - 297.6, - 297.7, - 298.2, - 298.5, - 298.5, - 298.5, - 298.5, - 298.2, - 298.4, - 298.8, - 299.7, - 300.9, - 302.2, - 302.6, - 300.0, - 297.9, - 299.1, - 299.7, - 300.4, - 299.2, - 301.1, - 300.7, - 300.8, - 300.9, - 300.7, - 300.4, - 300.2, - 300.0, - 299.7, - 299.6, - 299.5, - 299.7, - 299.8, - 299.8, - 299.8, - 300.0, - 300.1, - 299.9, - 299.6, - 299.3, - 299.1, - 298.4, - 297.7, - 296.8, - 295.8, - 295.0, - 294.4, - 293.9, - 293.5, - 293.1, - 292.8, - 292.6, - 292.2, - 292.2, - 292.7, - 293.3, - 293.3, - 290.7, - 293.3, - 283.6, - 295.9, - 300.0, - 301.3, - 296.9, - 295.9, - 295.1, - 296.2, - 298.6, - 298.3, - 297.6, - 296.9, - 296.4, - 295.9, - 295.6, - 295.3, - 295.2, - 295.3, - ], - [ - 296.7, - 297.4, - 298.1, - 298.3, - 293.0, - 294.1, - 295.4, - 294.8, - 296.0, - 296.7, - 297.1, - 300.4, - 301.3, - 298.5, - 299.2, - 299.2, - 298.8, - 298.5, - 298.4, - 298.7, - 299.1, - 299.4, - 299.6, - 299.8, - 299.9, - 299.8, - 299.6, - 299.8, - 300.2, - 300.8, - 301.3, - 301.9, - 302.2, - 302.4, - 299.3, - 299.7, - 300.3, - 302.5, - 300.3, - 301.2, - 301.4, - 301.5, - 301.5, - 301.3, - 300.9, - 300.6, - 300.4, - 300.2, - 300.2, - 300.2, - 300.6, - 301.0, - 300.8, - 300.5, - 300.4, - 300.4, - 300.2, - 300.0, - 299.7, - 299.4, - 298.9, - 298.1, - 297.2, - 296.3, - 295.5, - 294.7, - 294.1, - 293.5, - 293.0, - 292.6, - 292.3, - 292.4, - 292.9, - 293.6, - 294.6, - 295.1, - 293.8, - 280.5, - 293.1, - 299.5, - 298.8, - 298.8, - 297.6, - 295.5, - 295.4, - 294.9, - 298.9, - 299.0, - 298.2, - 297.5, - 296.9, - 296.4, - 296.2, - 296.0, - 295.9, - 296.3, - ], - [ - 298.1, - 298.6, - 299.5, - 299.7, - 293.4, - 293.8, - 294.6, - 293.7, - 293.8, - 296.3, - 297.1, - 300.2, - 300.4, - 299.7, - 299.6, - 299.5, - 299.3, - 299.5, - 299.6, - 299.9, - 300.1, - 300.3, - 300.6, - 300.8, - 300.8, - 300.8, - 300.7, - 300.9, - 301.2, - 301.6, - 301.8, - 302.0, - 302.1, - 302.2, - 302.2, - 302.3, - 300.9, - 302.6, - 301.9, - 301.9, - 302.0, - 301.8, - 301.7, - 301.4, - 301.2, - 301.1, - 301.1, - 301.0, - 300.9, - 300.9, - 301.2, - 301.3, - 301.1, - 300.9, - 300.8, - 300.7, - 300.6, - 300.4, - 300.1, - 299.9, - 299.6, - 299.0, - 298.1, - 297.3, - 296.3, - 295.4, - 294.7, - 294.1, - 293.8, - 293.4, - 293.4, - 293.8, - 294.6, - 295.3, - 296.4, - 296.5, - 286.2, - 293.0, - 299.3, - 299.6, - 298.9, - 298.4, - 298.4, - 297.8, - 295.5, - 294.9, - 298.6, - 299.3, - 298.8, - 298.2, - 297.7, - 297.2, - 296.9, - 296.9, - 297.2, - 297.7, - ], - [ - 299.3, - 300.1, - 300.9, - 301.0, - 296.5, - 295.0, - 295.4, - 294.5, - 293.6, - 293.2, - 298.3, - 300.1, - 300.6, - 300.2, - 300.2, - 300.2, - 300.1, - 300.3, - 300.4, - 300.5, - 300.6, - 300.7, - 300.8, - 300.9, - 301.0, - 301.1, - 301.2, - 301.2, - 301.4, - 301.6, - 301.8, - 301.9, - 301.9, - 302.0, - 302.2, - 302.2, - 302.4, - 302.3, - 302.5, - 302.3, - 302.1, - 301.8, - 301.7, - 301.6, - 301.5, - 301.5, - 301.4, - 301.3, - 301.3, - 301.4, - 301.4, - 301.3, - 301.2, - 301.1, - 301.1, - 301.0, - 300.9, - 300.7, - 300.5, - 300.2, - 299.8, - 299.2, - 298.4, - 297.5, - 296.7, - 296.1, - 295.6, - 295.3, - 295.1, - 295.0, - 295.1, - 295.5, - 296.1, - 297.1, - 297.6, - 296.6, - 292.3, - 299.6, - 300.4, - 299.8, - 299.4, - 298.4, - 298.4, - 298.9, - 296.7, - 296.7, - 300.1, - 299.5, - 299.2, - 298.8, - 298.4, - 298.2, - 298.2, - 298.4, - 298.7, - 298.9, - ], - [ - 300.7, - 301.3, - 301.7, - 301.6, - 297.2, - 296.6, - 296.7, - 296.4, - 294.2, - 293.5, - 299.5, - 299.8, - 300.8, - 300.4, - 300.1, - 300.1, - 300.2, - 300.4, - 300.5, - 300.5, - 300.6, - 300.8, - 300.8, - 300.8, - 300.8, - 300.9, - 300.9, - 300.9, - 301.2, - 301.4, - 301.6, - 301.8, - 302.0, - 302.1, - 302.2, - 302.4, - 302.4, - 302.4, - 300.7, - 296.4, - 302.0, - 301.9, - 301.9, - 301.8, - 301.8, - 301.7, - 301.6, - 301.5, - 301.5, - 301.4, - 301.4, - 301.4, - 301.3, - 301.2, - 301.0, - 300.9, - 300.8, - 300.7, - 300.4, - 299.9, - 299.4, - 298.8, - 298.2, - 297.7, - 297.3, - 296.9, - 296.6, - 296.4, - 296.3, - 296.3, - 296.5, - 296.9, - 297.4, - 298.4, - 298.5, - 291.0, - 298.6, - 300.5, - 300.8, - 300.9, - 300.1, - 298.7, - 298.5, - 298.4, - 297.9, - 297.8, - 299.0, - 299.5, - 299.9, - 299.7, - 299.6, - 299.5, - 299.5, - 299.6, - 299.9, - 300.1, - ], - [ - 301.2, - 301.4, - 301.5, - 301.6, - 298.1, - 298.3, - 298.3, - 297.6, - 294.4, - 293.5, - 297.6, - 299.1, - 300.7, - 300.6, - 300.2, - 299.9, - 300.0, - 300.2, - 300.4, - 300.4, - 300.5, - 300.6, - 300.5, - 300.6, - 300.7, - 300.8, - 300.9, - 301.5, - 299.1, - 302.0, - 302.1, - 302.2, - 302.6, - 302.4, - 302.2, - 302.3, - 302.2, - 298.0, - 295.6, - 301.5, - 301.8, - 301.7, - 301.6, - 301.5, - 301.3, - 301.2, - 301.1, - 301.0, - 301.0, - 300.9, - 300.7, - 300.6, - 300.3, - 300.0, - 299.9, - 299.8, - 299.7, - 299.6, - 299.4, - 299.2, - 298.8, - 298.5, - 298.1, - 297.8, - 297.8, - 297.7, - 297.6, - 297.5, - 297.6, - 297.7, - 297.9, - 298.2, - 298.8, - 299.5, - 299.3, - 294.1, - 300.7, - 301.2, - 301.1, - 300.9, - 300.2, - 299.5, - 298.9, - 298.7, - 298.8, - 297.7, - 300.7, - 300.1, - 300.4, - 300.3, - 300.3, - 300.3, - 300.4, - 300.5, - 300.7, - 300.9, - ], - [ - 301.2, - 301.3, - 301.4, - 299.1, - 297.8, - 299.4, - 298.8, - 297.3, - 291.6, - 294.2, - 297.2, - 299.3, - 300.3, - 300.2, - 300.3, - 300.2, - 300.1, - 300.2, - 300.3, - 300.5, - 300.5, - 300.6, - 300.6, - 300.6, - 300.8, - 301.1, - 301.2, - 298.7, - 300.5, - 302.1, - 300.4, - 300.7, - 296.8, - 302.3, - 302.2, - 302.3, - 302.6, - 299.3, - 301.6, - 301.2, - 300.7, - 300.4, - 300.3, - 300.2, - 300.1, - 300.0, - 300.0, - 299.7, - 299.7, - 299.4, - 299.3, - 299.2, - 298.8, - 298.6, - 298.3, - 297.9, - 297.7, - 297.5, - 297.4, - 297.5, - 297.3, - 297.2, - 297.1, - 297.0, - 297.2, - 297.0, - 297.3, - 297.4, - 297.7, - 298.0, - 298.0, - 298.6, - 298.9, - 299.3, - 299.3, - 294.4, - 300.5, - 301.2, - 300.9, - 300.7, - 299.8, - 300.0, - 301.0, - 299.5, - 299.3, - 300.7, - 300.7, - 300.2, - 300.4, - 300.5, - 300.4, - 300.5, - 300.6, - 300.7, - 300.9, - 301.0, - ], - [ - 301.4, - 301.5, - 301.4, - 299.0, - 298.3, - 299.5, - 299.6, - 297.9, - 293.7, - 293.8, - 296.3, - 302.4, - 300.0, - 299.5, - 299.7, - 300.1, - 300.3, - 300.3, - 300.5, - 300.6, - 300.6, - 300.6, - 300.8, - 300.9, - 301.3, - 301.6, - 301.5, - 298.7, - 301.6, - 301.6, - 298.6, - 299.5, - 302.0, - 301.9, - 302.1, - 302.2, - 302.1, - 301.8, - 301.2, - 300.6, - 300.2, - 300.0, - 299.8, - 299.7, - 299.6, - 299.4, - 299.4, - 299.1, - 299.0, - 298.6, - 298.5, - 298.2, - 298.0, - 297.8, - 297.6, - 297.3, - 297.0, - 297.0, - 296.6, - 296.8, - 296.5, - 296.5, - 296.3, - 296.3, - 296.0, - 296.1, - 296.5, - 296.8, - 297.0, - 297.8, - 298.0, - 298.8, - 299.0, - 299.3, - 299.4, - 293.3, - 299.8, - 300.5, - 300.4, - 299.8, - 300.2, - 302.2, - 300.8, - 300.1, - 300.2, - 300.1, - 300.0, - 300.2, - 300.2, - 300.3, - 300.4, - 300.6, - 300.8, - 300.9, - 301.1, - 301.2, - ], - [ - 301.9, - 301.9, - 301.6, - 298.0, - 297.9, - 299.2, - 298.9, - 297.8, - 295.1, - 295.1, - 300.5, - 301.9, - 300.2, - 299.3, - 299.3, - 299.8, - 299.9, - 300.1, - 300.2, - 300.3, - 300.2, - 300.1, - 300.3, - 300.5, - 301.1, - 301.6, - 301.5, - 299.3, - 300.9, - 301.2, - 299.4, - 297.5, - 301.9, - 301.9, - 301.9, - 301.9, - 301.9, - 301.7, - 301.6, - 301.3, - 301.0, - 300.7, - 300.6, - 300.4, - 300.3, - 300.1, - 299.9, - 299.8, - 299.7, - 299.5, - 299.3, - 299.0, - 299.1, - 299.2, - 299.0, - 298.8, - 298.6, - 298.5, - 298.4, - 298.4, - 298.1, - 298.1, - 298.3, - 298.3, - 298.2, - 298.4, - 298.7, - 298.8, - 299.1, - 299.3, - 299.4, - 299.7, - 299.7, - 299.7, - 299.9, - 300.1, - 293.9, - 299.3, - 300.0, - 298.1, - 302.8, - 301.0, - 299.5, - 300.0, - 299.9, - 299.9, - 300.0, - 300.2, - 300.4, - 300.6, - 300.8, - 301.0, - 301.3, - 301.4, - 301.7, - 301.8, - ], - [ - 302.3, - 302.5, - 299.8, - 295.9, - 296.7, - 297.9, - 297.1, - 295.9, - 298.0, - 299.7, - 292.1, - 298.2, - 298.1, - 299.2, - 298.9, - 299.5, - 299.8, - 300.0, - 300.1, - 300.0, - 299.8, - 299.7, - 299.8, - 300.2, - 300.7, - 301.2, - 301.3, - 296.9, - 300.8, - 301.4, - 302.1, - 298.0, - 302.2, - 301.9, - 301.5, - 301.8, - 301.8, - 301.7, - 301.6, - 301.5, - 301.4, - 301.3, - 301.2, - 301.1, - 301.0, - 300.8, - 300.7, - 300.6, - 300.4, - 300.3, - 300.1, - 300.0, - 300.1, - 299.9, - 299.9, - 299.9, - 299.8, - 299.7, - 299.7, - 299.8, - 299.7, - 299.6, - 299.5, - 299.4, - 299.4, - 299.6, - 299.7, - 299.8, - 300.0, - 300.1, - 300.1, - 300.1, - 300.1, - 299.9, - 300.1, - 301.1, - 290.3, - 299.6, - 299.7, - 298.0, - 296.0, - 299.6, - 299.4, - 299.8, - 299.6, - 299.6, - 299.9, - 300.1, - 300.4, - 300.7, - 301.0, - 301.3, - 301.6, - 302.2, - 299.7, - 300.3, - ], - [ - 298.4, - 297.2, - 296.5, - 293.9, - 297.7, - 298.4, - 294.5, - 296.0, - 301.3, - 299.8, - 288.7, - 292.5, - 294.3, - 296.1, - 298.5, - 298.7, - 299.2, - 299.5, - 299.7, - 299.7, - 299.6, - 299.5, - 299.6, - 300.0, - 300.4, - 300.7, - 300.8, - 300.4, - 300.8, - 301.3, - 301.5, - 301.9, - 302.0, - 297.3, - 301.3, - 301.4, - 301.3, - 301.2, - 301.1, - 301.0, - 300.9, - 300.8, - 300.8, - 300.7, - 300.5, - 300.3, - 300.3, - 300.2, - 300.0, - 300.0, - 300.0, - 299.9, - 299.9, - 299.9, - 299.9, - 299.9, - 299.9, - 299.8, - 299.7, - 299.8, - 299.8, - 299.8, - 299.8, - 299.8, - 299.9, - 300.1, - 300.2, - 300.1, - 299.9, - 299.8, - 299.8, - 300.0, - 299.9, - 299.7, - 299.7, - 298.3, - 295.7, - 296.7, - 303.6, - 300.4, - 299.1, - 299.5, - 299.7, - 299.7, - 299.0, - 298.9, - 299.1, - 299.3, - 299.6, - 299.9, - 300.0, - 300.5, - 301.6, - 298.6, - 297.4, - 298.3, - ], - [ - 296.5, - 294.9, - 293.1, - 296.6, - 300.2, - 300.2, - 298.6, - 299.1, - 301.0, - 299.0, - 290.4, - 291.1, - 290.9, - 290.4, - 297.5, - 297.8, - 298.0, - 298.4, - 299.1, - 299.6, - 299.8, - 293.2, - 299.1, - 299.5, - 299.9, - 300.2, - 300.3, - 297.6, - 298.5, - 301.0, - 301.0, - 301.2, - 301.1, - 298.1, - 300.7, - 300.7, - 300.6, - 300.5, - 300.4, - 300.2, - 299.9, - 299.7, - 299.6, - 299.4, - 299.2, - 299.0, - 298.9, - 298.8, - 298.6, - 298.6, - 298.6, - 298.6, - 298.7, - 298.6, - 298.6, - 298.7, - 298.7, - 298.7, - 298.7, - 298.8, - 299.0, - 299.1, - 299.2, - 299.3, - 299.5, - 299.8, - 299.9, - 299.6, - 299.2, - 299.0, - 299.1, - 299.6, - 300.1, - 300.1, - 294.4, - 299.6, - 300.2, - 299.3, - 301.1, - 300.2, - 299.0, - 299.5, - 299.1, - 298.7, - 298.5, - 298.2, - 298.1, - 298.1, - 298.3, - 298.6, - 298.7, - 299.2, - 300.9, - 295.8, - 295.9, - 296.5, - ], - [ - 298.7, - 297.5, - 297.7, - 299.4, - 300.9, - 299.8, - 297.2, - 296.9, - 299.0, - 301.1, - 291.8, - 289.3, - 288.2, - 297.1, - 297.1, - 296.8, - 296.8, - 297.3, - 298.3, - 299.3, - 300.1, - 291.1, - 298.5, - 298.8, - 299.1, - 299.3, - 298.9, - 295.9, - 297.2, - 296.0, - 300.4, - 300.4, - 300.1, - 297.6, - 300.2, - 300.1, - 300.1, - 300.1, - 300.0, - 299.8, - 299.6, - 299.3, - 299.0, - 298.6, - 298.3, - 298.1, - 297.9, - 297.7, - 297.6, - 297.5, - 297.5, - 297.4, - 297.5, - 297.5, - 297.4, - 297.2, - 297.2, - 297.3, - 297.2, - 297.5, - 297.8, - 298.1, - 298.3, - 298.6, - 298.8, - 299.2, - 299.5, - 299.4, - 298.8, - 298.1, - 298.0, - 298.6, - 300.1, - 296.6, - 299.2, - 297.7, - 297.0, - 296.4, - 297.7, - 298.7, - 298.7, - 298.8, - 298.4, - 298.2, - 298.1, - 297.8, - 297.5, - 297.3, - 297.2, - 297.5, - 297.7, - 297.9, - 299.8, - 296.7, - 297.8, - 298.6, - ], - [ - 298.5, - 297.9, - 297.1, - 296.8, - 297.3, - 297.1, - 294.6, - 293.7, - 296.6, - 299.4, - 293.8, - 298.8, - 284.9, - 289.3, - 297.1, - 295.9, - 295.7, - 296.3, - 297.4, - 298.5, - 291.5, - 294.5, - 298.3, - 298.2, - 298.4, - 298.2, - 297.4, - 293.2, - 293.8, - 293.9, - 299.2, - 299.3, - 295.4, - 299.2, - 299.3, - 299.1, - 299.2, - 299.2, - 299.3, - 299.4, - 299.3, - 299.0, - 298.6, - 298.1, - 297.6, - 297.3, - 297.0, - 296.8, - 296.7, - 296.6, - 296.5, - 296.2, - 296.3, - 296.3, - 296.1, - 296.1, - 296.1, - 296.1, - 296.3, - 296.5, - 296.8, - 297.0, - 297.2, - 297.2, - 297.3, - 297.7, - 298.4, - 299.1, - 298.9, - 297.9, - 297.2, - 296.4, - 291.5, - 293.8, - 299.1, - 299.1, - 298.8, - 298.8, - 298.9, - 298.9, - 298.5, - 298.2, - 297.8, - 297.5, - 297.4, - 297.3, - 297.1, - 296.9, - 296.7, - 296.5, - 296.5, - 296.9, - 299.4, - 299.6, - 298.4, - 298.6, - ], - [ - 295.1, - 294.7, - 293.0, - 292.3, - 293.5, - 294.7, - 292.3, - 293.1, - 295.3, - 296.1, - 294.7, - 300.3, - 284.4, - 288.6, - 289.2, - 295.6, - 294.9, - 295.7, - 296.8, - 298.0, - 291.2, - 291.8, - 290.6, - 298.1, - 298.1, - 297.6, - 288.6, - 289.9, - 289.8, - 296.0, - 297.7, - 298.2, - 298.0, - 297.6, - 297.2, - 297.0, - 297.2, - 297.5, - 297.7, - 297.9, - 298.2, - 298.4, - 298.3, - 298.0, - 297.6, - 297.1, - 296.6, - 296.3, - 295.9, - 295.7, - 295.4, - 295.1, - 295.0, - 294.9, - 294.7, - 294.7, - 294.6, - 294.6, - 294.8, - 295.2, - 295.4, - 295.3, - 295.4, - 295.3, - 295.2, - 295.4, - 296.6, - 298.2, - 298.9, - 289.8, - 287.0, - 289.8, - 294.7, - 298.4, - 298.6, - 298.7, - 299.0, - 299.0, - 298.6, - 298.5, - 298.2, - 297.8, - 297.5, - 297.1, - 296.7, - 296.6, - 296.6, - 296.4, - 296.2, - 296.1, - 296.1, - 295.9, - 296.5, - 296.6, - 295.4, - 295.0, - ], - [ - 292.2, - 290.9, - 290.4, - 290.4, - 290.2, - 288.6, - 289.6, - 290.2, - 292.8, - 292.5, - 299.2, - 289.1, - 287.6, - 289.6, - 290.8, - 290.7, - 295.2, - 295.5, - 296.5, - 297.5, - 289.3, - 291.2, - 287.7, - 298.1, - 298.2, - 288.9, - 286.8, - 285.9, - 286.0, - 292.4, - 295.0, - 296.9, - 296.4, - 295.5, - 294.8, - 294.5, - 294.8, - 295.2, - 295.6, - 295.9, - 296.3, - 296.7, - 296.9, - 297.0, - 296.8, - 296.5, - 296.1, - 295.8, - 295.3, - 294.9, - 294.6, - 294.3, - 294.1, - 293.8, - 293.6, - 293.4, - 293.2, - 293.1, - 293.0, - 293.1, - 293.1, - 293.0, - 293.0, - 293.0, - 292.9, - 293.4, - 294.6, - 296.7, - 290.7, - 283.2, - 283.4, - 296.3, - 294.1, - 297.6, - 297.4, - 297.6, - 295.5, - 295.2, - 298.1, - 297.8, - 297.6, - 297.3, - 297.1, - 296.8, - 296.3, - 295.9, - 295.8, - 295.7, - 295.6, - 295.5, - 295.5, - 294.7, - 293.2, - 292.9, - 292.1, - 292.2, - ], - [ - 291.1, - 288.9, - 286.0, - 287.7, - 287.6, - 286.6, - 287.5, - 287.1, - 290.2, - 290.0, - 297.1, - 287.9, - 286.7, - 288.3, - 290.4, - 288.3, - 295.3, - 295.2, - 295.8, - 288.5, - 288.9, - 288.4, - 287.4, - 287.3, - 291.2, - 286.0, - 284.9, - 280.5, - 282.4, - 280.3, - 283.8, - 293.2, - 293.6, - 293.6, - 292.4, - 292.2, - 292.3, - 292.5, - 293.2, - 293.8, - 294.2, - 294.8, - 295.2, - 295.5, - 295.6, - 295.5, - 295.2, - 294.8, - 294.4, - 294.0, - 293.7, - 293.4, - 293.1, - 292.8, - 292.6, - 292.4, - 292.2, - 292.0, - 291.8, - 291.8, - 291.7, - 291.7, - 291.8, - 291.8, - 291.6, - 292.0, - 293.2, - 295.5, - 284.4, - 280.6, - 286.9, - 294.5, - 295.2, - 296.1, - 295.2, - 291.2, - 296.1, - 296.7, - 296.8, - 296.6, - 296.5, - 296.5, - 296.3, - 296.1, - 295.7, - 295.3, - 295.1, - 294.9, - 294.9, - 294.8, - 294.9, - 294.4, - 290.6, - 290.8, - 290.8, - 291.1, - ], - [ - 290.4, - 288.1, - 285.2, - 285.7, - 286.9, - 287.4, - 287.2, - 286.0, - 287.6, - 289.6, - 290.1, - 285.8, - 284.8, - 287.0, - 289.7, - 288.5, - 294.5, - 294.6, - 286.5, - 286.4, - 284.1, - 285.4, - 287.0, - 288.2, - 287.0, - 282.8, - 281.1, - 278.0, - 278.5, - 280.5, - 279.8, - 276.8, - 289.8, - 292.0, - 290.9, - 290.5, - 290.5, - 290.4, - 290.8, - 291.3, - 291.9, - 292.6, - 293.2, - 293.7, - 294.0, - 294.1, - 294.0, - 293.9, - 293.6, - 293.2, - 292.8, - 292.4, - 292.1, - 291.6, - 291.3, - 291.1, - 290.9, - 290.9, - 290.9, - 290.8, - 290.7, - 290.8, - 290.9, - 290.9, - 290.6, - 290.9, - 292.0, - 293.9, - 279.2, - 280.3, - 285.6, - 293.0, - 293.5, - 293.6, - 292.5, - 293.9, - 294.4, - 294.8, - 295.0, - 295.2, - 295.2, - 295.3, - 295.3, - 295.3, - 295.1, - 294.9, - 294.6, - 294.4, - 294.2, - 294.0, - 294.0, - 294.2, - 294.0, - 289.0, - 289.9, - 290.0, - ], - [ - 288.3, - 287.8, - 287.2, - 286.6, - 286.8, - 287.7, - 288.2, - 287.0, - 286.6, - 287.5, - 284.8, - 283.8, - 284.1, - 293.8, - 294.4, - 283.1, - 280.0, - 280.8, - 283.8, - 284.8, - 281.4, - 284.1, - 284.0, - 275.8, - 272.7, - 278.9, - 274.1, - 272.2, - 277.0, - 274.2, - 277.1, - 276.9, - 275.1, - 288.0, - 288.3, - 289.1, - 289.0, - 288.8, - 288.8, - 289.2, - 289.6, - 290.3, - 290.9, - 291.6, - 292.0, - 292.2, - 292.2, - 292.2, - 292.1, - 291.9, - 291.6, - 291.3, - 291.0, - 290.6, - 290.3, - 290.0, - 289.7, - 289.6, - 289.7, - 289.9, - 289.8, - 289.7, - 289.8, - 289.8, - 289.5, - 289.8, - 290.7, - 279.3, - 278.3, - 280.4, - 281.1, - 288.7, - 288.6, - 288.4, - 288.4, - 291.6, - 292.2, - 292.7, - 293.0, - 293.3, - 293.5, - 293.8, - 293.9, - 294.1, - 294.1, - 294.0, - 293.8, - 293.7, - 293.6, - 293.4, - 293.1, - 293.1, - 293.5, - 287.8, - 287.2, - 287.1, - ], - [ - 283.4, - 285.0, - 285.9, - 285.5, - 286.3, - 287.6, - 287.1, - 286.6, - 286.6, - 284.8, - 282.4, - 282.7, - 284.0, - 293.1, - 274.8, - 275.5, - 279.8, - 279.6, - 278.0, - 283.2, - 282.9, - 276.8, - 261.2, - 245.8, - 259.6, - 256.2, - 259.3, - 267.7, - 277.8, - 274.4, - 275.5, - 275.4, - 274.8, - 284.1, - 284.2, - 285.8, - 286.5, - 286.5, - 287.1, - 287.7, - 288.2, - 288.7, - 289.2, - 289.7, - 290.1, - 290.3, - 290.4, - 290.2, - 290.2, - 290.0, - 289.8, - 289.6, - 289.3, - 289.1, - 289.0, - 288.9, - 288.9, - 288.7, - 288.7, - 288.9, - 288.9, - 288.6, - 288.8, - 288.7, - 288.3, - 288.6, - 281.2, - 277.0, - 276.8, - 277.0, - 275.8, - 277.1, - 276.5, - 276.5, - 276.7, - 288.3, - 290.4, - 291.3, - 291.5, - 291.7, - 291.9, - 292.1, - 292.4, - 292.6, - 292.8, - 292.9, - 292.8, - 292.8, - 292.7, - 292.6, - 292.3, - 292.2, - 292.5, - 292.7, - 282.1, - 283.9, - ], - [ - 279.1, - 280.6, - 283.5, - 283.6, - 289.8, - 290.2, - 290.1, - 290.0, - 290.4, - 290.8, - 280.5, - 281.3, - 283.8, - 276.7, - 274.6, - 276.6, - 276.4, - 275.7, - 272.5, - 281.0, - 278.8, - 254.8, - 251.9, - 253.4, - 256.7, - 247.5, - 257.2, - 263.1, - 270.5, - 272.0, - 273.2, - 273.7, - 274.2, - 280.9, - 279.7, - 282.0, - 282.5, - 282.2, - 284.2, - 285.2, - 286.2, - 287.0, - 287.5, - 287.9, - 288.2, - 288.4, - 288.6, - 288.5, - 288.3, - 288.2, - 287.9, - 287.7, - 287.4, - 287.3, - 287.3, - 287.4, - 287.6, - 287.5, - 287.5, - 287.6, - 287.8, - 287.6, - 287.7, - 287.6, - 287.1, - 279.8, - 279.5, - 273.8, - 273.7, - 274.4, - 274.8, - 275.3, - 275.0, - 275.1, - 274.7, - 284.1, - 288.4, - 289.8, - 290.2, - 290.2, - 290.6, - 290.7, - 290.9, - 291.1, - 291.3, - 291.6, - 291.8, - 291.8, - 291.7, - 291.6, - 291.5, - 291.4, - 291.5, - 291.4, - 282.4, - 278.9, - ], - [ - 278.8, - 277.5, - 278.0, - 288.2, - 288.2, - 288.1, - 288.2, - 288.6, - 289.0, - 289.3, - 278.3, - 280.4, - 277.2, - 272.0, - 275.3, - 276.3, - 275.6, - 273.5, - 269.1, - 269.9, - 255.7, - 246.4, - 251.9, - 253.4, - 245.8, - 246.2, - 258.6, - 263.1, - 267.9, - 270.2, - 270.5, - 271.0, - 276.2, - 276.7, - 274.4, - 277.2, - 269.0, - 266.8, - 280.2, - 282.3, - 283.3, - 284.2, - 285.0, - 285.6, - 285.8, - 286.0, - 286.3, - 286.5, - 286.4, - 286.3, - 286.1, - 285.9, - 285.7, - 285.6, - 285.5, - 285.7, - 286.1, - 286.1, - 286.2, - 286.4, - 286.5, - 286.5, - 286.6, - 286.7, - 277.9, - 276.5, - 273.3, - 269.3, - 271.2, - 272.5, - 272.2, - 271.8, - 271.7, - 271.3, - 270.9, - 270.7, - 284.5, - 287.6, - 288.5, - 288.9, - 289.5, - 289.8, - 289.6, - 289.7, - 290.0, - 290.2, - 290.5, - 290.7, - 290.8, - 290.7, - 290.7, - 290.5, - 290.1, - 290.0, - 289.2, - 281.4, - ], - [ - 285.2, - 286.4, - 286.4, - 286.0, - 286.2, - 286.3, - 286.6, - 286.0, - 276.0, - 274.1, - 274.7, - 274.2, - 270.0, - 272.4, - 287.2, - 274.7, - 275.2, - 275.3, - 275.2, - 260.8, - 251.5, - 268.3, - 270.1, - 263.4, - 252.5, - 263.2, - 252.3, - 262.8, - 264.5, - 265.2, - 266.5, - 268.0, - 272.0, - 270.4, - 247.3, - 271.7, - 275.1, - 266.1, - 277.3, - 278.6, - 280.3, - 281.6, - 282.2, - 282.6, - 283.1, - 283.4, - 283.9, - 284.3, - 284.5, - 284.3, - 283.8, - 283.5, - 283.5, - 283.4, - 283.6, - 284.0, - 284.5, - 284.7, - 284.9, - 285.2, - 285.3, - 285.5, - 285.4, - 285.4, - 273.3, - 267.6, - 265.5, - 264.9, - 263.3, - 271.6, - 270.0, - 268.6, - 268.3, - 268.5, - 268.7, - 270.4, - 281.5, - 285.3, - 286.5, - 287.6, - 288.1, - 288.5, - 288.7, - 288.6, - 288.7, - 288.8, - 289.0, - 289.6, - 289.7, - 289.8, - 289.6, - 289.3, - 288.6, - 287.8, - 279.7, - 279.1, - ], - [ - 273.8, - 284.8, - 284.3, - 283.4, - 284.1, - 284.3, - 269.9, - 276.7, - 275.1, - 272.3, - 266.6, - 264.7, - 269.8, - 274.6, - 283.5, - 272.9, - 272.1, - 273.2, - 272.5, - 263.6, - 259.2, - 256.8, - 262.2, - 266.6, - 266.4, - 261.5, - 258.6, - 264.1, - 262.1, - 262.1, - 260.5, - 263.4, - 255.3, - 246.1, - 253.9, - 266.2, - 270.5, - 272.7, - 262.4, - 270.6, - 276.3, - 277.7, - 277.8, - 277.9, - 278.5, - 279.6, - 280.3, - 280.7, - 281.2, - 281.3, - 281.1, - 281.1, - 281.3, - 281.5, - 281.7, - 282.3, - 282.9, - 283.3, - 283.5, - 284.0, - 284.2, - 284.4, - 284.4, - 278.8, - 269.8, - 264.9, - 264.1, - 262.6, - 262.6, - 268.9, - 265.4, - 264.7, - 264.5, - 265.8, - 266.6, - 265.8, - 267.8, - 279.0, - 281.4, - 283.4, - 284.6, - 285.3, - 285.8, - 286.0, - 286.8, - 286.9, - 287.1, - 288.1, - 288.7, - 288.8, - 288.4, - 288.0, - 287.3, - 286.7, - 277.4, - 276.5, - ], - [ - 274.9, - 283.4, - 282.1, - 269.1, - 281.3, - 261.9, - 270.2, - 267.7, - 280.6, - 281.8, - 282.8, - 273.9, - 265.5, - 281.3, - 272.6, - 269.8, - 268.9, - 269.3, - 271.0, - 266.4, - 261.9, - 257.4, - 261.4, - 253.0, - 261.0, - 261.9, - 257.6, - 260.1, - 257.9, - 258.3, - 256.1, - 252.3, - 255.7, - 247.3, - 251.4, - 240.9, - 265.6, - 270.0, - 261.5, - 269.7, - 270.6, - 274.3, - 274.8, - 275.4, - 276.4, - 277.5, - 277.5, - 277.6, - 278.1, - 278.5, - 278.6, - 279.0, - 279.3, - 279.7, - 280.0, - 280.5, - 281.1, - 281.7, - 282.1, - 282.5, - 283.0, - 283.1, - 283.4, - 276.3, - 268.0, - 265.9, - 262.4, - 261.1, - 261.6, - 262.4, - 262.7, - 263.0, - 262.7, - 264.7, - 265.1, - 264.7, - 266.2, - 265.9, - 276.9, - 279.0, - 280.8, - 281.2, - 280.9, - 281.5, - 283.2, - 283.6, - 284.3, - 286.1, - 287.3, - 287.6, - 286.8, - 286.8, - 286.5, - 286.3, - 275.0, - 276.1, - ], - [ - 274.5, - 271.0, - 266.0, - 268.2, - 266.9, - 266.2, - 266.2, - 263.3, - 277.4, - 279.3, - 280.8, - 273.2, - 273.8, - 278.1, - 269.3, - 267.1, - 265.8, - 264.3, - 266.5, - 265.0, - 266.8, - 262.1, - 257.7, - 260.3, - 253.8, - 252.7, - 247.7, - 255.3, - 255.3, - 253.8, - 254.4, - 250.6, - 250.4, - 243.1, - 244.6, - 248.9, - 249.0, - 266.6, - 262.0, - 270.0, - 272.0, - 273.2, - 273.3, - 274.0, - 274.7, - 275.4, - 275.5, - 275.6, - 275.9, - 276.1, - 276.5, - 277.1, - 277.7, - 278.1, - 278.4, - 278.9, - 279.7, - 280.2, - 280.6, - 281.2, - 281.9, - 282.1, - 282.5, - 276.1, - 272.3, - 267.5, - 260.7, - 261.8, - 262.1, - 259.5, - 259.8, - 261.4, - 261.6, - 272.4, - 273.0, - 259.2, - 261.9, - 262.4, - 264.8, - 274.8, - 276.8, - 276.2, - 276.9, - 277.8, - 279.4, - 280.8, - 282.1, - 284.0, - 285.3, - 286.1, - 285.4, - 285.1, - 285.2, - 285.8, - 285.1, - 283.7, - ], - [ - 273.0, - 274.0, - 266.9, - 261.6, - 264.6, - 265.3, - 264.3, - 262.6, - 262.2, - 264.1, - 268.2, - 268.7, - 267.6, - 266.1, - 263.6, - 262.5, - 260.5, - 260.1, - 259.8, - 258.0, - 258.9, - 259.2, - 256.6, - 251.1, - 247.7, - 252.2, - 249.4, - 249.4, - 253.3, - 249.6, - 249.9, - 246.2, - 246.0, - 244.9, - 242.8, - 246.4, - 249.2, - 250.5, - 264.2, - 269.5, - 271.8, - 271.9, - 271.8, - 272.8, - 273.5, - 273.9, - 273.9, - 274.0, - 274.2, - 274.5, - 275.1, - 275.7, - 276.4, - 276.9, - 277.2, - 277.7, - 278.5, - 279.1, - 279.6, - 280.3, - 281.1, - 281.4, - 281.0, - 275.6, - 271.9, - 266.2, - 263.0, - 260.8, - 257.4, - 257.4, - 256.9, - 258.9, - 259.5, - 269.7, - 252.8, - 258.9, - 261.3, - 262.3, - 264.2, - 272.0, - 273.8, - 268.0, - 274.9, - 276.0, - 277.5, - 279.2, - 280.6, - 282.1, - 283.3, - 284.4, - 284.4, - 284.1, - 285.0, - 285.1, - 284.0, - 282.6, - ], - [ - 271.1, - 272.8, - 268.2, - 266.4, - 264.6, - 262.8, - 263.0, - 261.6, - 261.5, - 261.0, - 260.6, - 260.5, - 260.5, - 260.2, - 259.6, - 258.6, - 256.7, - 257.0, - 257.7, - 256.5, - 256.8, - 257.7, - 258.7, - 253.6, - 252.2, - 251.6, - 251.0, - 251.5, - 252.9, - 251.5, - 249.3, - 244.7, - 242.7, - 242.7, - 240.6, - 242.4, - 248.5, - 248.8, - 265.8, - 269.7, - 270.9, - 270.4, - 269.2, - 271.3, - 272.1, - 272.4, - 272.5, - 272.7, - 273.0, - 273.5, - 274.1, - 274.7, - 275.4, - 275.9, - 276.3, - 276.8, - 277.6, - 278.3, - 279.1, - 279.8, - 280.5, - 280.6, - 280.3, - 266.9, - 265.4, - 261.0, - 259.6, - 258.2, - 256.0, - 255.5, - 255.1, - 257.3, - 257.7, - 258.2, - 257.1, - 260.2, - 258.5, - 260.7, - 260.2, - 260.6, - 264.8, - 267.5, - 274.2, - 275.5, - 276.9, - 278.5, - 279.6, - 280.7, - 281.8, - 282.9, - 282.9, - 283.2, - 284.6, - 283.9, - 281.7, - 280.7, - ], - [ - 272.9, - 278.0, - 266.3, - 264.1, - 264.1, - 261.7, - 260.5, - 260.0, - 258.1, - 256.2, - 255.4, - 254.6, - 254.8, - 255.0, - 254.6, - 256.0, - 254.5, - 254.0, - 255.1, - 256.0, - 256.1, - 256.0, - 257.0, - 257.5, - 256.7, - 254.9, - 252.3, - 253.8, - 251.8, - 248.4, - 245.3, - 242.5, - 240.9, - 240.6, - 239.5, - 239.7, - 245.4, - 244.2, - 260.7, - 268.4, - 269.1, - 268.7, - 240.9, - 267.9, - 269.2, - 270.1, - 270.8, - 271.1, - 272.0, - 272.8, - 273.2, - 274.0, - 274.8, - 275.5, - 276.1, - 276.5, - 277.1, - 277.9, - 278.7, - 279.4, - 280.0, - 279.9, - 267.4, - 266.1, - 263.0, - 261.9, - 258.8, - 256.7, - 255.0, - 255.2, - 254.8, - 254.5, - 256.1, - 255.6, - 256.0, - 268.1, - 253.7, - 258.8, - 255.8, - 255.8, - 261.0, - 266.4, - 273.4, - 275.0, - 276.6, - 278.6, - 279.7, - 280.2, - 281.0, - 281.7, - 282.3, - 282.5, - 283.6, - 282.1, - 269.5, - 272.3, - ], - [ - 279.5, - 278.6, - 276.0, - 264.8, - 272.6, - 272.6, - 259.8, - 257.3, - 254.5, - 251.7, - 250.6, - 250.5, - 250.5, - 250.1, - 250.8, - 252.4, - 253.3, - 251.8, - 252.0, - 252.7, - 252.6, - 252.2, - 252.9, - 254.6, - 255.4, - 255.2, - 255.5, - 251.6, - 249.8, - 244.5, - 243.6, - 239.9, - 238.9, - 237.6, - 238.1, - 239.3, - 240.5, - 250.0, - 255.8, - 266.6, - 266.7, - 267.2, - 245.0, - 243.8, - 265.6, - 267.4, - 268.2, - 268.7, - 270.2, - 271.3, - 272.1, - 272.9, - 273.6, - 274.7, - 275.5, - 276.4, - 277.0, - 277.7, - 278.3, - 278.6, - 278.1, - 269.4, - 266.8, - 263.5, - 263.3, - 262.7, - 260.3, - 257.5, - 254.8, - 253.6, - 252.4, - 251.2, - 251.1, - 251.3, - 262.3, - 268.0, - 249.2, - 256.3, - 252.8, - 250.1, - 256.0, - 271.0, - 273.2, - 275.2, - 277.1, - 278.8, - 279.9, - 280.3, - 280.9, - 281.6, - 282.6, - 282.9, - 283.4, - 282.9, - 280.9, - 270.4, - ], - [ - 279.7, - 277.9, - 275.4, - 273.1, - 257.7, - 272.2, - 269.6, - 250.4, - 251.0, - 248.2, - 245.6, - 246.1, - 246.9, - 246.0, - 247.1, - 248.1, - 248.7, - 248.0, - 248.3, - 248.7, - 248.9, - 248.9, - 248.9, - 249.6, - 250.4, - 250.2, - 247.7, - 246.4, - 244.8, - 244.6, - 241.7, - 239.0, - 238.3, - 236.0, - 233.8, - 235.6, - 236.8, - 240.2, - 250.0, - 258.6, - 257.6, - 257.7, - 256.0, - 242.0, - 257.2, - 264.2, - 264.5, - 265.3, - 266.7, - 268.3, - 269.3, - 271.1, - 272.2, - 272.0, - 273.1, - 274.7, - 275.8, - 276.5, - 277.1, - 277.2, - 266.4, - 266.3, - 262.2, - 262.1, - 260.9, - 261.2, - 259.3, - 256.6, - 254.0, - 251.6, - 249.0, - 248.4, - 251.3, - 250.0, - 253.9, - 263.0, - 245.9, - 252.7, - 254.5, - 246.1, - 268.6, - 271.9, - 273.7, - 275.7, - 277.1, - 278.2, - 279.5, - 280.0, - 280.7, - 281.3, - 281.8, - 281.8, - 281.7, - 282.2, - 281.6, - 265.3, - ], - [ - 279.8, - 277.7, - 255.1, - 258.3, - 255.5, - 270.2, - 266.8, - 246.2, - 247.0, - 245.2, - 242.0, - 243.4, - 243.2, - 243.2, - 244.7, - 245.2, - 244.9, - 243.2, - 243.9, - 244.2, - 244.7, - 244.7, - 244.2, - 244.3, - 245.4, - 243.4, - 240.8, - 239.1, - 238.9, - 238.3, - 236.7, - 236.1, - 234.8, - 233.7, - 232.2, - 231.0, - 229.6, - 237.8, - 243.5, - 244.1, - 242.9, - 240.6, - 250.7, - 250.6, - 239.3, - 252.1, - 250.7, - 250.8, - 256.8, - 263.2, - 267.1, - 268.8, - 269.6, - 250.4, - 257.9, - 258.2, - 259.1, - 262.1, - 261.1, - 259.5, - 259.4, - 261.1, - 259.4, - 257.8, - 257.6, - 257.5, - 255.0, - 254.4, - 251.8, - 248.8, - 245.5, - 249.3, - 250.1, - 249.3, - 250.3, - 252.9, - 246.2, - 249.3, - 255.3, - 261.8, - 268.1, - 271.6, - 273.4, - 274.7, - 275.4, - 276.2, - 278.5, - 279.4, - 280.3, - 280.9, - 281.3, - 281.2, - 281.0, - 281.4, - 281.4, - 280.7, - ], - [ - 278.7, - 277.8, - 264.9, - 264.0, - 254.2, - 267.9, - 249.4, - 242.7, - 244.0, - 242.0, - 240.6, - 241.8, - 241.4, - 241.1, - 241.8, - 241.7, - 240.0, - 238.2, - 238.6, - 239.1, - 239.4, - 239.5, - 239.9, - 240.3, - 240.7, - 238.7, - 236.5, - 235.7, - 234.5, - 233.6, - 232.0, - 229.2, - 230.4, - 227.2, - 226.2, - 229.9, - 232.8, - 235.1, - 237.5, - 239.0, - 239.8, - 240.1, - 240.2, - 241.7, - 245.4, - 245.5, - 246.0, - 244.4, - 255.6, - 260.1, - 262.5, - 266.9, - 267.4, - 250.2, - 259.4, - 258.6, - 258.3, - 257.9, - 256.5, - 254.3, - 254.3, - 256.5, - 256.6, - 254.8, - 251.6, - 252.2, - 251.5, - 249.3, - 246.8, - 245.4, - 244.0, - 242.9, - 248.6, - 248.7, - 248.8, - 248.2, - 249.5, - 252.3, - 246.9, - 258.9, - 267.3, - 270.3, - 272.2, - 248.1, - 253.5, - 273.7, - 276.3, - 277.8, - 279.1, - 279.8, - 281.3, - 281.1, - 280.7, - 280.8, - 280.4, - 279.8, - ], - [ - 276.4, - 276.5, - 275.5, - 275.0, - 250.6, - 252.0, - 256.1, - 241.6, - 241.3, - 241.2, - 239.5, - 239.0, - 238.2, - 239.2, - 238.2, - 237.3, - 236.0, - 234.8, - 234.5, - 234.7, - 235.0, - 234.5, - 234.9, - 236.3, - 236.9, - 235.5, - 234.5, - 233.6, - 231.4, - 229.8, - 228.6, - 228.8, - 227.9, - 227.4, - 232.3, - 234.1, - 234.0, - 232.7, - 232.6, - 234.1, - 233.7, - 233.4, - 235.4, - 237.4, - 237.3, - 240.4, - 241.4, - 242.2, - 251.4, - 252.7, - 255.0, - 261.0, - 261.3, - 245.5, - 252.5, - 252.0, - 250.5, - 251.8, - 252.1, - 252.0, - 251.6, - 252.7, - 252.8, - 248.1, - 247.8, - 248.3, - 247.9, - 245.9, - 244.3, - 243.9, - 243.3, - 243.7, - 243.5, - 242.5, - 247.1, - 246.3, - 247.3, - 246.6, - 245.6, - 252.1, - 266.7, - 269.9, - 271.9, - 249.9, - 252.0, - 243.4, - 273.0, - 270.7, - 270.1, - 271.5, - 278.6, - 255.6, - 257.5, - 277.9, - 277.3, - 276.7, - ], - [ - 274.7, - 274.7, - 274.0, - 273.4, - 240.9, - 248.1, - 247.8, - 246.6, - 243.0, - 240.4, - 237.7, - 244.3, - 241.4, - 241.3, - 232.1, - 232.7, - 233.6, - 232.8, - 231.5, - 231.0, - 230.8, - 230.1, - 230.8, - 232.7, - 234.3, - 232.2, - 231.2, - 230.9, - 229.7, - 228.1, - 227.9, - 227.8, - 227.4, - 228.2, - 229.6, - 229.1, - 229.2, - 230.8, - 231.3, - 231.9, - 231.8, - 231.5, - 231.8, - 234.2, - 236.6, - 238.2, - 240.1, - 241.3, - 240.1, - 238.5, - 239.6, - 243.8, - 255.4, - 242.5, - 250.6, - 249.5, - 245.8, - 245.5, - 244.6, - 244.6, - 244.8, - 244.0, - 244.3, - 245.4, - 245.0, - 244.5, - 243.8, - 243.1, - 242.6, - 242.6, - 241.2, - 242.2, - 244.2, - 243.3, - 240.8, - 245.5, - 246.3, - 245.5, - 243.6, - 247.8, - 266.3, - 269.5, - 247.1, - 253.6, - 249.5, - 247.9, - 247.0, - 250.8, - 266.3, - 261.0, - 263.1, - 270.9, - 274.4, - 275.1, - 274.2, - 273.9, - ], - [ - 272.2, - 272.7, - 272.0, - 271.5, - 270.3, - 270.5, - 242.2, - 243.8, - 247.3, - 263.6, - 258.9, - 246.2, - 240.0, - 240.6, - 239.1, - 235.5, - 236.5, - 238.7, - 229.9, - 227.0, - 227.7, - 226.9, - 226.5, - 229.8, - 232.2, - 230.6, - 230.0, - 229.6, - 229.5, - 228.5, - 227.8, - 227.6, - 227.5, - 228.6, - 229.5, - 229.6, - 229.0, - 228.5, - 228.8, - 229.7, - 230.1, - 230.4, - 231.9, - 238.6, - 238.6, - 238.3, - 238.2, - 237.5, - 239.9, - 244.9, - 246.5, - 245.2, - 248.3, - 255.0, - 234.9, - 238.8, - 242.9, - 248.6, - 248.2, - 250.8, - 250.9, - 247.1, - 248.3, - 241.2, - 241.0, - 240.8, - 240.6, - 240.3, - 239.9, - 244.3, - 242.7, - 240.5, - 241.9, - 241.4, - 237.7, - 237.7, - 244.5, - 244.5, - 247.1, - 252.4, - 266.4, - 269.8, - 249.9, - 253.8, - 245.5, - 238.3, - 238.4, - 242.4, - 247.6, - 249.2, - 245.4, - 255.7, - 264.5, - 264.9, - 266.0, - 269.0, - ], - [ - 269.0, - 269.4, - 269.4, - 269.8, - 271.1, - 270.8, - 268.2, - 266.5, - 265.6, - 264.8, - 261.3, - 246.1, - 235.9, - 237.4, - 238.2, - 235.0, - 235.9, - 237.4, - 237.4, - 228.2, - 237.7, - 235.9, - 223.6, - 225.8, - 227.0, - 228.1, - 227.4, - 226.5, - 226.1, - 226.3, - 226.9, - 228.1, - 227.3, - 226.7, - 227.5, - 235.2, - 235.5, - 235.8, - 235.5, - 235.1, - 235.1, - 235.6, - 236.4, - 238.5, - 239.4, - 239.6, - 240.6, - 240.2, - 239.7, - 241.4, - 243.6, - 241.9, - 241.5, - 251.9, - 244.3, - 240.9, - 241.6, - 242.6, - 242.3, - 243.6, - 243.1, - 242.9, - 245.9, - 238.0, - 238.6, - 238.3, - 238.3, - 240.3, - 241.8, - 244.4, - 243.1, - 238.5, - 239.1, - 239.7, - 239.8, - 242.1, - 247.8, - 245.0, - 251.2, - 264.4, - 268.3, - 269.9, - 232.1, - 250.7, - 244.8, - 242.9, - 241.2, - 237.9, - 239.4, - 246.4, - 245.0, - 249.2, - 248.3, - 247.0, - 251.1, - 263.6, - ], - [ - 267.8, - 268.5, - 268.3, - 269.1, - 269.9, - 268.6, - 266.7, - 265.9, - 264.3, - 262.9, - 255.5, - 237.1, - 230.7, - 236.7, - 238.3, - 235.6, - 234.2, - 235.0, - 234.6, - 237.0, - 240.1, - 234.9, - 234.3, - 231.2, - 225.1, - 227.0, - 228.0, - 227.5, - 227.0, - 226.7, - 228.7, - 237.0, - 234.4, - 235.6, - 236.8, - 236.5, - 235.3, - 235.5, - 236.0, - 236.8, - 237.3, - 237.7, - 237.5, - 237.0, - 237.2, - 237.3, - 237.9, - 238.6, - 238.3, - 238.3, - 239.5, - 239.2, - 238.4, - 240.3, - 238.0, - 237.5, - 238.4, - 239.1, - 239.2, - 239.7, - 240.3, - 241.0, - 242.1, - 241.8, - 241.6, - 240.7, - 240.6, - 241.3, - 242.6, - 243.3, - 242.9, - 242.6, - 236.4, - 238.7, - 241.0, - 250.4, - 251.0, - 253.4, - 262.5, - 268.7, - 268.8, - 231.8, - 243.0, - 241.4, - 239.4, - 238.4, - 234.8, - 235.0, - 237.2, - 241.8, - 242.2, - 247.0, - 246.7, - 247.1, - 248.8, - 257.9, - ], - [ - 266.3, - 268.4, - 267.8, - 267.9, - 267.3, - 266.4, - 265.5, - 264.8, - 262.8, - 262.1, - 259.4, - 245.0, - 231.9, - 235.1, - 237.9, - 236.7, - 235.8, - 235.2, - 235.2, - 236.9, - 236.9, - 234.1, - 232.7, - 231.0, - 231.8, - 233.7, - 234.0, - 225.9, - 227.3, - 236.4, - 237.8, - 239.8, - 236.5, - 236.2, - 236.3, - 236.5, - 236.3, - 237.0, - 237.4, - 237.8, - 238.1, - 237.6, - 236.9, - 236.0, - 235.8, - 236.1, - 236.8, - 237.4, - 237.5, - 237.2, - 237.3, - 237.1, - 237.1, - 237.9, - 237.9, - 238.0, - 238.1, - 237.9, - 237.4, - 237.1, - 237.5, - 238.2, - 238.8, - 239.8, - 240.2, - 240.6, - 240.6, - 240.6, - 240.6, - 241.0, - 242.2, - 243.4, - 236.8, - 239.2, - 240.8, - 241.8, - 249.6, - 254.1, - 240.6, - 239.5, - 239.6, - 236.0, - 232.0, - 229.8, - 229.2, - 228.7, - 227.9, - 229.8, - 234.4, - 238.3, - 241.0, - 250.1, - 247.6, - 247.8, - 249.5, - 254.9, - ], - [ - 266.7, - 266.9, - 265.6, - 265.9, - 265.2, - 264.6, - 264.6, - 262.5, - 250.8, - 241.4, - 240.2, - 239.9, - 236.9, - 238.9, - 240.3, - 238.4, - 236.9, - 236.5, - 234.4, - 233.6, - 234.0, - 234.2, - 233.2, - 233.4, - 235.1, - 233.9, - 231.8, - 232.0, - 232.3, - 234.2, - 233.9, - 235.5, - 235.1, - 235.7, - 236.7, - 237.4, - 237.2, - 237.1, - 237.0, - 236.9, - 237.4, - 237.5, - 237.4, - 236.3, - 235.6, - 235.6, - 235.7, - 235.6, - 235.7, - 235.7, - 235.7, - 235.6, - 235.7, - 235.7, - 235.5, - 235.5, - 235.5, - 235.4, - 235.2, - 235.1, - 235.5, - 236.5, - 237.0, - 237.1, - 237.2, - 237.3, - 237.3, - 237.2, - 237.6, - 239.0, - 241.1, - 246.3, - 232.2, - 234.5, - 234.2, - 231.2, - 231.1, - 233.8, - 236.8, - 238.4, - 236.6, - 236.1, - 234.8, - 235.3, - 233.6, - 233.4, - 235.0, - 237.4, - 239.0, - 239.4, - 240.2, - 241.5, - 242.2, - 244.1, - 247.4, - 257.0, - ], - [ - 247.2, - 247.8, - 250.1, - 246.0, - 246.8, - 247.5, - 245.6, - 243.0, - 238.0, - 235.9, - 237.4, - 238.7, - 238.8, - 239.1, - 240.2, - 236.9, - 235.1, - 236.5, - 234.2, - 231.7, - 231.0, - 231.8, - 232.2, - 232.4, - 235.5, - 235.5, - 236.2, - 235.3, - 234.9, - 235.0, - 235.4, - 236.6, - 236.9, - 237.6, - 238.0, - 238.2, - 238.0, - 238.2, - 238.3, - 238.1, - 238.4, - 238.2, - 238.0, - 237.4, - 236.9, - 237.3, - 236.8, - 236.2, - 236.2, - 236.3, - 236.1, - 235.6, - 235.2, - 234.8, - 234.3, - 233.9, - 233.5, - 233.2, - 232.8, - 232.6, - 233.1, - 234.0, - 234.2, - 234.4, - 234.2, - 234.0, - 233.9, - 233.9, - 233.8, - 234.4, - 235.7, - 238.1, - 235.7, - 235.8, - 240.5, - 224.1, - 225.3, - 227.2, - 232.5, - 240.2, - 239.2, - 238.6, - 239.2, - 239.4, - 235.6, - 235.0, - 233.9, - 235.2, - 235.9, - 236.5, - 239.9, - 238.9, - 238.5, - 239.7, - 242.2, - 245.1, - ], - [ - 237.3, - 237.3, - 238.2, - 237.7, - 238.5, - 239.3, - 238.2, - 236.9, - 235.5, - 236.1, - 237.4, - 237.9, - 237.4, - 237.0, - 237.1, - 236.0, - 235.7, - 236.5, - 236.7, - 236.4, - 235.3, - 234.4, - 234.8, - 234.9, - 235.4, - 235.8, - 236.0, - 235.9, - 236.1, - 236.4, - 236.9, - 237.4, - 237.9, - 238.3, - 238.7, - 238.8, - 238.8, - 238.6, - 238.6, - 238.6, - 238.5, - 238.4, - 238.4, - 238.2, - 238.0, - 238.1, - 237.6, - 236.9, - 236.6, - 236.4, - 236.1, - 235.7, - 235.5, - 235.2, - 234.8, - 234.4, - 234.1, - 233.5, - 232.7, - 231.8, - 231.6, - 232.0, - 232.2, - 232.9, - 233.0, - 232.7, - 232.3, - 232.0, - 231.9, - 232.1, - 232.4, - 233.0, - 233.1, - 234.2, - 236.6, - 232.8, - 232.1, - 232.7, - 233.6, - 236.6, - 235.3, - 235.1, - 236.3, - 234.4, - 234.0, - 234.8, - 235.8, - 237.1, - 238.1, - 239.5, - 240.2, - 240.6, - 239.4, - 238.3, - 238.6, - 238.1, - ], - [ - 231.9, - 231.5, - 231.7, - 231.8, - 232.1, - 232.1, - 231.8, - 231.6, - 231.6, - 232.3, - 233.4, - 233.9, - 233.7, - 233.5, - 233.6, - 234.0, - 235.2, - 236.4, - 237.1, - 237.5, - 237.2, - 237.2, - 237.4, - 237.6, - 237.9, - 238.2, - 238.5, - 238.7, - 238.8, - 238.8, - 238.9, - 239.0, - 239.1, - 239.1, - 239.1, - 239.0, - 238.9, - 239.0, - 238.5, - 238.3, - 238.1, - 238.1, - 238.2, - 238.2, - 238.1, - 238.0, - 237.9, - 237.8, - 237.6, - 237.3, - 237.1, - 237.2, - 237.1, - 236.9, - 236.6, - 236.4, - 236.2, - 235.6, - 235.0, - 234.7, - 234.3, - 233.6, - 233.3, - 233.8, - 235.0, - 235.9, - 235.3, - 234.1, - 234.0, - 234.1, - 233.5, - 233.1, - 233.1, - 233.7, - 234.0, - 232.8, - 232.8, - 233.1, - 233.4, - 233.7, - 233.0, - 232.6, - 232.8, - 233.1, - 233.6, - 234.3, - 234.8, - 235.2, - 235.4, - 235.3, - 235.0, - 234.6, - 234.0, - 233.5, - 233.5, - 232.7, - ], - [ - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - 235.4, - ], - ] - ] + dst[...] = arrays["dst"] def _make_cfa_file(filename): From 0309d467be935af74868eef6199a25ff1179e618 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Mon, 27 Nov 2023 08:26:57 +0000 Subject: [PATCH 08/22] Typos Co-authored-by: Sadie L. Bartholomew --- cf/domaintopology.py | 2 +- cf/field.py | 10 +++++----- cf/test/test_weights.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cf/domaintopology.py b/cf/domaintopology.py index 2819f752cf..3ddea39825 100644 --- a/cf/domaintopology.py +++ b/cf/domaintopology.py @@ -168,7 +168,7 @@ def identity( relaxed: `bool`, optional If True then the identity is the first found of only - the `cell `attribute, the "standard_name" property, + the `cell` attribute, the "standard_name" property, the "id" attribute, the "long_name" property or the netCDF variable name. diff --git a/cf/field.py b/cf/field.py index 476fba39ca..8977d95b21 100644 --- a/cf/field.py +++ b/cf/field.py @@ -3124,7 +3124,7 @@ def weights( """Return weights for the data array values. The weights are those used during a statistical collapse of - the data. For example when computing a area weight average. + the data. For example when computing an area weight average. Weights for any combination of axes may be returned. @@ -3159,7 +3159,7 @@ def weights( * **Type 3** allows particular types of weights to be defined for particular axes, and an exception will - be raised if it is not possible to the create + be raised if it is not possible to create the weights. .. @@ -3194,7 +3194,7 @@ def weights( coordinate constructs for geomtries or a UGRID mesh topology. 5. Length calculated from 1-d auxiliary - coordinate constructs for geomtries + coordinate constructs for geometries or a UGRID mesh topology. 6. Cell sizes of dimension coordinate constructs with bounds @@ -3337,7 +3337,7 @@ def weights( If True then allow, if required, the derivation of i) area weights from polygon cells by assuming that each cell part is a spherical polygon composed of great - circle segments; and ii) and the derivation of + circle segments; and ii) the derivation of line-length weights line cells by assuming that each line part is composed of great circle segments. Only applies to geometry and UGRID cells. @@ -3620,7 +3620,7 @@ def weights( # In rare edge cases (e.g. if a user sets # `weights=f[0].cell_area` when they really meant # `weights=f[0].cell_area()`) we reach this code but - # find that weights is not iterable. So check it is.x + # find that weights is not iterable. So check it is. try: weights = iter(weights) except TypeError: diff --git a/cf/test/test_weights.py b/cf/test/test_weights.py index 77b300fdb5..7f3bbb5cdf 100644 --- a/cf/test/test_weights.py +++ b/cf/test/test_weights.py @@ -6,7 +6,7 @@ import cf # A radius greater than 1. Used since weights based on the unit -# spehere and non-speheres are tested separately. +# sphere and non-spheres are tested separately. r = 2 radius = cf.Data(r, "m") @@ -225,7 +225,7 @@ def test_weights_line_length_geometry(self): lon.set_geometry("line") lat.set_geometry("line") - # Cirumference of unit sphere + # Circumference of unit sphere cirumference = 2 * np.pi correct_weights = np.array( [3 * cirumference / 4, 5 * cirumference / 4] @@ -274,7 +274,7 @@ def test_weights_line_area_ugrid(self): ) lat.set_bounds(cf.Bounds(data=bounds)) - # Cirumference of unit sphere + # Circumference of unit sphere cirumference = 2 * np.pi correct_weights = np.array([cirumference / 4] * 3) From 4def36593aeff70ae0ef0960cc0e52937194b2bd Mon Sep 17 00:00:00 2001 From: David Hassell Date: Mon, 27 Nov 2023 08:46:44 +0000 Subject: [PATCH 09/22] parameter checks in 'identity' methods --- cf/cellconnectivity.py | 5 +++++ cf/cellmeasure.py | 5 +++++ cf/domain.py | 5 +++++ cf/domaintopology.py | 5 +++++ cf/mixin/propertiesdata.py | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/cf/cellconnectivity.py b/cf/cellconnectivity.py index 3ed0d52df4..1ce30537d8 100644 --- a/cf/cellconnectivity.py +++ b/cf/cellconnectivity.py @@ -161,6 +161,11 @@ def identity( return f"id%{n}" if relaxed: + if strict: + raise ValueError( + "'relaxed' and 'strict' parameters cannot both be True" + ) + n = self.get_property("long_name", None) if n is not None: return f"long_name={n}" diff --git a/cf/cellmeasure.py b/cf/cellmeasure.py index a776a0a9ee..44acd720fd 100644 --- a/cf/cellmeasure.py +++ b/cf/cellmeasure.py @@ -154,6 +154,11 @@ def identity( return f"id%{n}" if relaxed: + if strict: + raise ValueError( + "'relaxed' and 'strict' parameters cannot both be True" + ) + n = self.get_property("long_name", None) if n is not None: return f"long_name={n}" diff --git a/cf/domain.py b/cf/domain.py index 8e38c96431..262dd56b7f 100644 --- a/cf/domain.py +++ b/cf/domain.py @@ -666,6 +666,11 @@ def identity(self, default="", strict=False, relaxed=False, nc_only=False): return f"id%{n}" if relaxed: + if strict: + raise ValueError( + "'relaxed' and 'strict' parameters cannot both be True" + ) + n = self.get_property("long_name", None) if n is not None: return f"long_name={n}" diff --git a/cf/domaintopology.py b/cf/domaintopology.py index 3ddea39825..2910a49373 100644 --- a/cf/domaintopology.py +++ b/cf/domaintopology.py @@ -211,6 +211,11 @@ def identity( return f"id%{n}" if relaxed: + if strict: + raise ValueError( + "'relaxed' and 'strict' parameters cannot both be True" + ) + n = self.get_property("long_name", None) if n is not None: return f"long_name={n}" diff --git a/cf/mixin/propertiesdata.py b/cf/mixin/propertiesdata.py index 759e455736..a29c3cd748 100644 --- a/cf/mixin/propertiesdata.py +++ b/cf/mixin/propertiesdata.py @@ -4873,6 +4873,11 @@ def identity( return f"id%{n}" if relaxed: + if strict: + raise ValueError( + "'relaxed' and 'strict' parameters cannot both be True" + ) + n = self.get_property("long_name", None) if n is not None: return f"long_name={n}" From 8d20ed54de3a06ce40efd68f3d5b05b1a2ae97d8 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Mon, 27 Nov 2023 08:52:13 +0000 Subject: [PATCH 10/22] Clearer logical structure Co-authored-by: Sadie L. Bartholomew --- cf/data/dask_regrid.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cf/data/dask_regrid.py b/cf/data/dask_regrid.py index be74ad11d4..3b1326032b 100644 --- a/cf/data/dask_regrid.py +++ b/cf/data/dask_regrid.py @@ -294,9 +294,7 @@ def regrid( n_dst_axes = len(dst_shape) - if n_src_axes == n_dst_axes: - pass - elif n_src_axes == 1 and n_dst_axes == 2: + if n_src_axes == 1 and n_dst_axes == 2: # The regridding operation increased the number of data axes # by 1 => modify 'axis_order' to contain the new axis. # @@ -316,7 +314,7 @@ def regrid( # [0,1,3,4,2] raxis0, raxis = axis_order[-2:] axis_order = [i if i <= raxis else i - 1 for i in axis_order[:-1]] - else: + elif n_src_axes != n_dst_axes: raise ValueError( f"Can't (yet) regrid from {n_src_axes} dimensions to " f"{n_dst_axes} dimensions" From c62fac5fde79fe352110ed68854b94a9a59153cc Mon Sep 17 00:00:00 2001 From: David Hassell Date: Mon, 27 Nov 2023 08:56:36 +0000 Subject: [PATCH 11/22] Remove old commented code Co-authored-by: Sadie L. Bartholomew --- cf/field.py | 2 -- cf/regrid/regridoperator.py | 1 - 2 files changed, 3 deletions(-) diff --git a/cf/field.py b/cf/field.py index 8977d95b21..7edeeb3b1c 100644 --- a/cf/field.py +++ b/cf/field.py @@ -3456,8 +3456,6 @@ def weights( # All axes which have weights weights_axes = set() - # if radius is not None: - # radius = self.radius(default=radius) if weights is True and axes is not None: # -------------------------------------------------------- diff --git a/cf/regrid/regridoperator.py b/cf/regrid/regridoperator.py index bcea12ec63..a8f783f696 100644 --- a/cf/regrid/regridoperator.py +++ b/cf/regrid/regridoperator.py @@ -623,7 +623,6 @@ def tosparse(self): # Note: It is much more efficient to access 'weights.indptr' # and 'weights.data' directly, rather than iterating # over rows of 'weights' and using 'weights.getrow'. - # count_nonzero = np.count_nonzero indptr = weights.indptr.tolist() data = weights.data for j, (i0, i1) in enumerate(zip(indptr[:-1], indptr[1:])): From 71c6e9362b146987e6f62cce766d282883f697af Mon Sep 17 00:00:00 2001 From: David Hassell Date: Mon, 27 Nov 2023 09:07:37 +0000 Subject: [PATCH 12/22] remove old 'AT2' comments --- cf/data/data.py | 1 - cf/mixin/propertiesdata.py | 2 -- cf/mixin/propertiesdatabounds.py | 2 -- 3 files changed, 5 deletions(-) diff --git a/cf/data/data.py b/cf/data/data.py index 1b47fa2c5c..c672620b1e 100644 --- a/cf/data/data.py +++ b/cf/data/data.py @@ -11562,7 +11562,6 @@ def squeeze(self, axes=None, inplace=False, i=False): return d - # `arctan2`, AT2 seealso @_deprecated_kwarg_check("i", version="3.0.0", removed_at="4.0.0") @_inplace_enabled(default=False) def tan(self, inplace=False, i=False): diff --git a/cf/mixin/propertiesdata.py b/cf/mixin/propertiesdata.py index a29c3cd748..de0706251b 100644 --- a/cf/mixin/propertiesdata.py +++ b/cf/mixin/propertiesdata.py @@ -4025,7 +4025,6 @@ def sin(self, inplace=False, i=False): delete_props=True, ) - # `arctan2`, AT2 seealso @_deprecated_kwarg_check("i", version="3.0.0", removed_at="4.0.0") @_inplace_enabled(default=False) def arctan(self, inplace=False): @@ -4354,7 +4353,6 @@ def arccosh(self, inplace=False): delete_props=True, ) - # `arctan2`, AT2 seealso @_deprecated_kwarg_check("i", version="3.0.0", removed_at="4.0.0") @_inplace_enabled(default=False) def tan(self, inplace=False, i=False): diff --git a/cf/mixin/propertiesdatabounds.py b/cf/mixin/propertiesdatabounds.py index 8c3c4f2c5f..4f854a4b6d 100644 --- a/cf/mixin/propertiesdatabounds.py +++ b/cf/mixin/propertiesdatabounds.py @@ -2967,7 +2967,6 @@ def sin(self, bounds=True, inplace=False, i=False): i=i, ) - # `arctan2`, AT2 seealso @_deprecated_kwarg_check("i", version="3.0.0", removed_at="4.0.0") @_inplace_enabled(default=False) def arctan(self, bounds=True, inplace=False): @@ -3458,7 +3457,6 @@ def cosh(self, bounds=True, inplace=False): inplace=inplace, ) - # `arctan2`, AT2 seealso @_deprecated_kwarg_check("i", version="3.0.0", removed_at="4.0.0") @_inplace_enabled(default=False) def tan(self, bounds=True, inplace=False, i=False): From ac7b330b0950676f58c3716f377b6726f10c4fd4 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 28 Nov 2023 08:32:38 +0000 Subject: [PATCH 13/22] Typos Co-authored-by: Sadie L. Bartholomew --- cf/regrid/regrid.py | 8 ++++---- cf/weights.py | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cf/regrid/regrid.py b/cf/regrid/regrid.py index 61f6b83cc9..5fa7110e97 100644 --- a/cf/regrid/regrid.py +++ b/cf/regrid/regrid.py @@ -101,7 +101,7 @@ class Grid: # topology. E.g. '' or 'face'. mesh_location: str = "" # A domain topology construct that spans the regrid axis. A value - # of None means that the the grid is not a UGRID mesh topology. + # of None means that the grid is not a UGRID mesh topology. domain_topology: Any = None # The domain axis identifiers of new axes that result from the # regridding operation changing the number of data dimensions @@ -1510,7 +1510,7 @@ def create_esmpy_grid(grid, mask=None): f"{grid.method} regridding." ) - # Convert each bounds to a grid cwith no repeated values. + # Convert each bounds to a grid with no repeated values. if coords_1d: # Bounds for 1-d coordinates. # @@ -2168,7 +2168,7 @@ def update_coordinates(src, dst, src_grid, dst_grid): Also, if the source grid is a mesh, remove the existing domain topology and cell connectivity constructs that span the regridding - axis; and if the destination grid is a mesh copy omain topology + axis; and if the destination grid is a mesh copy domain topology and cell connectivity constructs from the destination grid. .. versionadded:: 3.14.0 @@ -2402,7 +2402,7 @@ def update_data(src, regridded_data, src_grid): :Parameters: src`: `Field` - The regridded field construct, that will up updated + The regridded field construct, that will be updated in-place. regridded_data: `numpy.ndarray` diff --git a/cf/weights.py b/cf/weights.py index fe18a5ab2f..1dda696216 100644 --- a/cf/weights.py +++ b/cf/weights.py @@ -380,7 +380,7 @@ def field(cls, f, field_weights, weights, weights_axes): nrefs = len(refs0) if nrefs > 1 or nrefs != len(refs1): - # The defining coordinate are associated with + # The defining coordinates are associated with # different numbers of coordinate references equivalent_refs = False elif not nrefs: @@ -1808,7 +1808,7 @@ def _spherical_area_measure(cls, f, areas, aux_Z, radius=None): def _plane_polygon_areas(cls, x, y): r"""Calculate the areas of plane polygons. - The area, A, is of a plane polygon is given by the shoelace + The area, A, of a plane polygon is given by the shoelace formula: A={\frac {1}{2}}\sum _{i=1}^{n}x_{i}(y_{i+1}-y_{i-1})} @@ -1827,13 +1827,13 @@ def _plane_polygon_areas(cls, x, y): x: `Data` The X coordinates of the polygon nodes, with no duplication of the first and last nodes (i.e. the - polygons are represented by has ``N`` values, where + polygons are represented by ``N`` values, where ``N`` is the number of edges). y: `Data` The Y coordinates of the polygon nodes, with - wrap-araound duplication of the first and last nodes - (i.e. the polygons are represented by has ``N + 2`` + wrap-around duplication of the first and last nodes + (i.e. the polygons are represented by ``N + 2`` values, where ``N`` is the number of edges). :Returns: @@ -1867,14 +1867,14 @@ def _spherical_polygon_areas(cls, f, x, y, N, interior_rings=None): x: array_like The X coordinates of the polygon nodes, with - wrap-araound duplication of the first and last nodes - (i.e. the polygons are represented by has ``N + 2`` + wrap-around duplication of the first and last nodes + (i.e. the polygons are represented by ``N + 2`` values, where ``N`` is the number of edges). y: array_like The Y coordinates of the polygon nodes, with - wrap-araound duplication of the first and last nodes - (i.e. the polygons are represented by has ``N + 2`` + wrap-around duplication of the first and last nodes + (i.e. the polygons are represented by ``N + 2`` values, where ``N`` is the number of edges). N: array_like From aba95648ad4b2be3b160b94289f57860d047ace7 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 28 Nov 2023 08:41:46 +0000 Subject: [PATCH 14/22] Improve exception message Co-authored-by: Sadie L. Bartholomew --- cf/regrid/regrid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cf/regrid/regrid.py b/cf/regrid/regrid.py index 5fa7110e97..2501e195ce 100644 --- a/cf/regrid/regrid.py +++ b/cf/regrid/regrid.py @@ -1156,7 +1156,7 @@ def Cartesian_grid(f, name=None, method=None, axes=None): elif n_axes == 2: coord_ids = axes[::-1] else: - raise ValueError("Can't provide 3 axes for mesh axis regridding") + raise ValueError("Can't provide 3 or more axes for mesh axis regridding") for coord_id in coord_ids: aux = f.auxiliary_coordinate( From 3eba5aff5cc5463e3b6d8516a27e27d5eb333d45 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 28 Nov 2023 08:46:19 +0000 Subject: [PATCH 15/22] Improve clarity of Boolean test Co-authored-by: Sadie L. Bartholomew --- cf/regrid/regrid.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cf/regrid/regrid.py b/cf/regrid/regrid.py index 2501e195ce..17f987cd9e 100644 --- a/cf/regrid/regrid.py +++ b/cf/regrid/regrid.py @@ -410,10 +410,7 @@ def regrid( "source and destination grids have coordinate arrays" ) - if method == "nearest_dtos" and not ( - (src_grid.is_mesh and dst_grid.is_mesh) - or (src_grid.is_grid and dst_grid.is_grid) - ): + if method == "nearest_dtos" and (src_grid.is_mesh is not dst_grid.is_mesh): raise ValueError( f"{method!r} regridding is (at the moment) only available " "when neither or both the source and destination grids are " From ddef83cd19647df94879688efe81f18a7127e021 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 28 Nov 2023 08:47:57 +0000 Subject: [PATCH 16/22] Remove print statement Co-authored-by: Sadie L. Bartholomew --- cf/regrid/regrid.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cf/regrid/regrid.py b/cf/regrid/regrid.py index 17f987cd9e..ea38ac6cc7 100644 --- a/cf/regrid/regrid.py +++ b/cf/regrid/regrid.py @@ -1703,13 +1703,6 @@ def create_esmpy_mesh(grid, mask=None): node_count = node_ids.size node_owners = np.zeros(node_count) - # print ( - # 'index=',index, - # 'index.shape=',index.shape, - # 'node_ids=',node_ids, - # 'node_ids.shape=',node_ids.shape, - # 'node_coords=',node_coords, - # 'node_coords.shape=',node_coords.shape) # Add nodes. This must be done before `add_elements`. esmpy_mesh.add_nodes( node_count=node_count, From 3fade924b32c8c6cc42caa917dbf1bc68bbd1b49 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 28 Nov 2023 08:49:56 +0000 Subject: [PATCH 17/22] Docstring clarity Co-authored-by: Sadie L. Bartholomew --- cf/regrid/regrid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cf/regrid/regrid.py b/cf/regrid/regrid.py index ea38ac6cc7..15cba5c1ac 100644 --- a/cf/regrid/regrid.py +++ b/cf/regrid/regrid.py @@ -2470,8 +2470,8 @@ def has_coordinate_arrays(grid): :Returns: `bool` - True if and only if all grid coordinates have - representative arrays. + True if and only if there are grid coordinates and they + all have representative arrays. """ if not grid.coords: From 96f446b8b299445710742e1cf0bab3c428ba3ab0 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 28 Nov 2023 08:57:05 +0000 Subject: [PATCH 18/22] Add helpful comment --- cf/regrid/regrid.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cf/regrid/regrid.py b/cf/regrid/regrid.py index 15cba5c1ac..1748f1829e 100644 --- a/cf/regrid/regrid.py +++ b/cf/regrid/regrid.py @@ -410,7 +410,9 @@ def regrid( "source and destination grids have coordinate arrays" ) - if method == "nearest_dtos" and (src_grid.is_mesh is not dst_grid.is_mesh): + if method == "nearest_dtos" and ( + src_grid.is_mesh is not dst_grid.is_mesh + ): raise ValueError( f"{method!r} regridding is (at the moment) only available " "when neither or both the source and destination grids are " @@ -1153,7 +1155,9 @@ def Cartesian_grid(f, name=None, method=None, axes=None): elif n_axes == 2: coord_ids = axes[::-1] else: - raise ValueError("Can't provide 3 or more axes for mesh axis regridding") + raise ValueError( + "Can't provide 3 or more axes for mesh axis regridding" + ) for coord_id in coord_ids: aux = f.auxiliary_coordinate( @@ -2481,6 +2485,9 @@ def has_coordinate_arrays(grid): try: has_data = coord.has_data() except AttributeError: + # 'coord' is not a construct, because it doesn't have a + # `has_data` attribute, and so must be something that + # certainly has data (e.g. a numpy array). has_data = True if not has_data: From 254770007df00ce5283d7065f9c350ef1fe78450 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 28 Nov 2023 09:24:15 +0000 Subject: [PATCH 19/22] remove old TODO comment --- cf/weights.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cf/weights.py b/cf/weights.py index 1dda696216..e796d3ec46 100644 --- a/cf/weights.py +++ b/cf/weights.py @@ -301,7 +301,6 @@ def field(cls, f, field_weights, weights, weights_axes): domain_axes = f.domain_axes(todict=True) for w in field_weights: t = w.analyse_items() - # TODO CHECK this with org if t["undefined_axes"]: w_domain_axes_1 = w.domain_axes( From ba891a7357996c22b4d5f6f6daa9d21d943f75e3 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 28 Nov 2023 09:25:55 +0000 Subject: [PATCH 20/22] Fix docstring --- cf/weights.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cf/weights.py b/cf/weights.py index e796d3ec46..8419a2a835 100644 --- a/cf/weights.py +++ b/cf/weights.py @@ -1017,7 +1017,7 @@ def _line_length_geometry(cls, f, x, y, spherical): @classmethod def _line_length_ugrid(cls, f, x, y, spherical): - """Creates line-length weights for line geometries. + """Creates line-length weights for UGRID edges. .. versionadded:: 3.16.0 From 09d9ba5363e53a329c7fec0080c7dc5caea31eee Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 28 Nov 2023 09:41:24 +0000 Subject: [PATCH 21/22] make angle methods private --- cf/weights.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cf/weights.py b/cf/weights.py index 8419a2a835..4a25c749a1 100644 --- a/cf/weights.py +++ b/cf/weights.py @@ -1003,7 +1003,7 @@ def _line_length_geometry(cls, f, x, y, spherical): from .units import Units if spherical: - all_lengths = cls.central_angles(f, x, y) + all_lengths = cls._central_angles(f, x, y) all_lengths.override_units(Units("m"), inplace=True) else: delta_x = x.diff(axis=-1) @@ -1045,7 +1045,7 @@ def _line_length_ugrid(cls, f, x, y, spherical): from .units import Units if spherical: - lengths = cls.central_angles(f, x, y) + lengths = cls._central_angles(f, x, y) lengths.override_units(Units("m"), inplace=True) else: delta_x = x.diff(axis=-1) @@ -1208,7 +1208,7 @@ def geometry_volume( return True @classmethod - def interior_angles(cls, f, lon, lat, interior_rings=None): + def _interior_angles(cls, f, lon, lat, interior_rings=None): """Find the interior angles at spherical polygon vertices. The interior angle at a vertex is that formed by two adjacent @@ -1233,7 +1233,7 @@ def interior_angles(cls, f, lon, lat, interior_rings=None): .. versionadded:: 3.16.0 - .. seealso:: `central_angles` + .. seealso:: `_central_angles` :Parameters: @@ -1314,7 +1314,7 @@ def interior_angles(cls, f, lon, lat, interior_rings=None): return alpha_P @classmethod - def central_angles(cls, f, lon, lat): + def _central_angles(cls, f, lon, lat): r"""Find the central angles for spherical great circle line segments. The central angle of two points on the sphere is the angle @@ -1338,7 +1338,7 @@ def central_angles(cls, f, lon, lat): .. versionadded:: 3.16.0 - .. seealso:: `interior_angles` + .. seealso:: `_interior_angles` :Parameters: @@ -1896,7 +1896,7 @@ def _spherical_polygon_areas(cls, f, x, y, N, interior_rings=None): from .units import Units - interior_angles = cls.interior_angles(f, x, y, interior_rings) + interior_angles = cls._interior_angles(f, x, y, interior_rings) areas = interior_angles.sum(-1, squeeze=True) - (N - 2) * pi areas.override_units(Units("m2"), inplace=True) return areas From 2b98d5476575e47542f381dd26df8edd9e80c256 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 28 Nov 2023 10:13:58 +0000 Subject: [PATCH 22/22] traceback TODOs --- cf/field.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/cf/field.py b/cf/field.py index 7edeeb3b1c..6fabf30cff 100644 --- a/cf/field.py +++ b/cf/field.py @@ -1801,7 +1801,12 @@ def _equivalent_construct_data( item1.transpose(transpose_axes, inplace=True) if item0.shape != item1.shape: - # add traceback TODO + if is_log_level_info(logger): + logger.info( + f"{self.__class__.__name__}: Different shapes: " + f"{item0.shape} != {item1.shape}" + ) # pragma: no cover + return False flip_axes = [ @@ -1820,7 +1825,11 @@ def _equivalent_construct_data( if not item0._equivalent_data( item1, rtol=rtol, atol=atol, verbose=verbose ): - # add traceback TODO + if is_log_level_info(logger): + logger.info( + f"{self.__class__.__name__}: Non-equivalent data" + ) # pragma: no cover + return False return True @@ -3456,7 +3465,6 @@ def weights( # All axes which have weights weights_axes = set() - if weights is True and axes is not None: # -------------------------------------------------------- # Restrict weights to the specified axes