[Git][debian-gis-team/netcdf4-python][master] 6 commits: New upstream version 1.7.1
Bas Couwenberg (@sebastic)
gitlab at salsa.debian.org
Tue Jun 18 04:41:27 BST 2024
Bas Couwenberg pushed to branch master at Debian GIS Project / netcdf4-python
Commits:
60d82e28 by Bas Couwenberg at 2024-06-18T05:28:35+02:00
New upstream version 1.7.1
- - - - -
e2ea9a79 by Bas Couwenberg at 2024-06-18T05:28:37+02:00
Update upstream source from tag 'upstream/1.7.1'
Update to upstream version '1.7.1'
with Debian dir 61702372f86158f120b122bd251ac06c5d33a61f
- - - - -
e4f8d47f by Bas Couwenberg at 2024-06-18T05:30:47+02:00
New upstream release.
- - - - -
910a16d9 by Bas Couwenberg at 2024-06-18T05:32:13+02:00
Drop nc-complex.patch, included upstream.
- - - - -
984fb983 by Bas Couwenberg at 2024-06-18T05:34:35+02:00
Update copyright file.
- - - - -
d36b49bb by Bas Couwenberg at 2024-06-18T05:34:52+02:00
Set distribution to unstable.
- - - - -
16 changed files:
- − .ci/build_deps.sh
- .github/workflows/cibuildwheel.yml
- .gitmodules
- Changelog
- MANIFEST.in
- README.md
- debian/changelog
- debian/copyright
- − debian/patches/nc-complex.patch
- debian/patches/series
- + external/README
- + external/nc_complex/include/generated_fallbacks/nc_complex_version.h
- + external/nc_complex/include/nc_complex/nc_complex.h
- + external/nc_complex/src/nc_complex.c
- pyproject.toml
- src/netCDF4/_netCDF4.pyx
Changes:
=====================================
.ci/build_deps.sh deleted
=====================================
@@ -1,28 +0,0 @@
-#!/usr/bin/bash
-
-set -ex
-
-
-download_and_build_netcdf() {
- if [ ! -d "netcdf-c" ]; then
- netcdf_url=https://github.com/Unidata/netcdf-c
- netcdf_src=netcdf-c
- netcdf_build=netcdf-build
-
- git clone ${netcdf_url} -b v4.9.2 ${netcdf_src}
-
- cmake ${netcdf_src} -B ${netcdf_build} \
- -DENABLE_NETCDF4=on \
- -DENABLE_HDF5=on \
- -DENABLE_DAP=on \
- -DENABLE_TESTS=off \
- -DENABLE_PLUGIN_INSTALL=off \
- -DBUILD_SHARED_LIBS=on \
- -DCMAKE_BUILD_TYPE=Release
-
- cmake --build ${netcdf_build} \
- --target install
-fi
-}
-
-download_and_build_netcdf
=====================================
.github/workflows/cibuildwheel.yml
=====================================
@@ -47,14 +47,16 @@ jobs:
build_bdist:
name: "Build ${{ matrix.os }} (${{ matrix.arch }}) wheels"
runs-on: ${{ matrix.os }}
+ # Prevent hanging when building from emulation like aarch64.
+ timeout-minutes: 300
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
arch: x86_64
- # - os: ubuntu-22.04
- # arch: aarch64
+ - os: ubuntu-22.04
+ arch: aarch64
- os: macos-14
arch: arm64
CIBW_ENVIRONMENT: MACOSX_DEPLOYMENT_TARGET=14.0
@@ -66,8 +68,14 @@ jobs:
- uses: actions/checkout at v4
with:
fetch-depth: 0
- submodules: 'true'
-
+
+ # For aarch64 support
+ # https://cibuildwheel.pypa.io/en/stable/faq/#emulation
+ - uses: docker/setup-qemu-action at v3
+ with:
+ platforms: all
+ if: runner.os == 'Linux' && matrix.arch == 'aarch64'
+
- name: Build oldest and newest Python
shell: bash
# On PRs we run only oldest and newest Python versions to reduce CI load.
@@ -84,22 +92,23 @@ jobs:
echo "Setting CIBW_SKIP=$CIBW_SKIP"
- name: "Building ${{ matrix.os }} (${{ matrix.arch }}) wheels"
- uses: pypa/cibuildwheel at v2.18.1
+ uses: pypa/cibuildwheel at v2.19.1
env:
CIBW_SKIP: ${{ env.CIBW_SKIP }}
CIBW_ARCHS: ${{ matrix.arch }}
CIBW_BUILD_FRONTEND: build
- CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
- CIBW_BEFORE_BUILD_LINUX: >
- dnf install -y epel-release
- && dnf install -y hdf5-devel libcurl-devel
- && sh .ci/build_deps.sh
+ CIBW_MANYLINUX_X86_64_IMAGE: ghcr.io/ocefpaf/manylinux2014_x86_64-netcdf
+ CIBW_MANYLINUX_AARCH64_IMAGE: ghcr.io/ocefpaf/manylinux2014_aarch64-netcdf
+ # Emulation testing is slow, testing only latest Python.
+ CIBW_TEST_SKIP: "cp38-*_aarch64 cp39-*_aarch64 cp310-*_aarch64 cp311-*_aarch64"
CIBW_ENVIRONMENT: ${{ matrix.CIBW_ENVIRONMENT }}
CIBW_BEFORE_BUILD_MACOS: brew install hdf5 netcdf
CIBW_TEST_REQUIRES: pytest cython packaging
CIBW_TEST_COMMAND: >
python -c "import netCDF4; print(f'netCDF4 v{netCDF4.__version__}')"
&& pytest -s -rxs -v {project}/test
+ && URL="https://icdc.cen.uni-hamburg.de/thredds/dodsC/ftpthredds/hamtide/m2.hamtide11a.nc"
+ && python -c "from netCDF4 import Dataset; nc=Dataset(\"${URL}\"); print(nc)"
- uses: actions/upload-artifact at v4
with:
@@ -119,7 +128,6 @@ jobs:
- uses: actions/checkout at v4
with:
fetch-depth: 0
- submodules: 'true'
- uses: actions/setup-python at v4
name: Install Python
=====================================
.gitmodules
=====================================
@@ -1,3 +0,0 @@
-[submodule "external/nc_complex"]
- path = external/nc_complex
- url = https://github.com/PlasmaFAIR/nc-complex.git
=====================================
Changelog
=====================================
@@ -1,3 +1,8 @@
+ version 1.7.1 (tag v1.7.1rel)
+===============================
+ * include nc_complex source code from v0.2.0 tag (instead of using submodule).
+ * add aarch64 wheels.
+
version 1.7.0 (tag v1.7.0rel)
===============================
* add support for complex numbers via `auto_complex` keyword to `Dataset` (PR #1295)
=====================================
MANIFEST.in
=====================================
@@ -1,5 +1,6 @@
include docs/index.html
recursive-include man *
+recursive-include external *
include MANIFEST.in
include README.htmldocs
include Changelog
@@ -17,6 +18,9 @@ include src/netCDF4/plugins/empty.txt
include include/netCDF4.pxi
include include/mpi-compat.h
include include/membuf.pyx
+include include/netcdf-compat.h
+include include/no_parallel_support_imports.pxi.in
+include include/parallel_support_imports.pxi.in
include *.md
include *.py
include *.release
=====================================
README.md
=====================================
@@ -10,7 +10,9 @@
## News
For details on the latest updates, see the [Changelog](https://github.com/Unidata/netcdf4-python/blob/master/Changelog).
-06/??/2024: Version [1.7.0](https://pypi.python.org/pypi/netCDF4/1.7.0) released. Add support for complex numbers via `auto_complex` keyword to `Dataset` ([PR #1295](https://github.com/Unidata/netcdf4-python/pull/1295))
+06/17/2024: Version [1.7.1](https://pypi.python.org/pypi/netCDF4/1.7.1) released. Fixes for wheels, no code changes.
+
+06/13/2024: Version [1.7.0](https://pypi.python.org/pypi/netCDF4/1.7.0) released. Add support for complex numbers via `auto_complex` keyword to `Dataset` ([PR #1295](https://github.com/Unidata/netcdf4-python/pull/1295))
10/20/2023: Version [1.6.5](https://pypi.python.org/pypi/netCDF4/1.6.5) released.
Fix for issue #1271 (mask ignored if bool MA assinged to uint8 var),
=====================================
debian/changelog
=====================================
@@ -1,3 +1,11 @@
+netcdf4-python (1.7.1-1) unstable; urgency=medium
+
+ * New upstream release.
+ * Drop nc-complex.patch, included upstream.
+ * Update copyright file.
+
+ -- Bas Couwenberg <sebastic at debian.org> Tue, 18 Jun 2024 05:34:38 +0200
+
netcdf4-python (1.7.0-1) unstable; urgency=medium
* New upstream release.
=====================================
debian/copyright
=====================================
@@ -7,6 +7,10 @@ Files: *
Copyright: 2008, Jeffrey Whitaker
License: Expat
+Files: external/nc_complex/*
+Copyright: 2023, Peter Hill
+License: Expat
+
Files: debian/*
Copyright: 2015, Ross Gammon <rossgammon at mail.dk>
License: ISC
=====================================
debian/patches/nc-complex.patch deleted
=====================================
@@ -1,1177 +0,0 @@
-Description: Add nc-complex sources.
-Origin: https://github.com/PlasmaFAIR/nc-complex/tree/37310ed00f3910974bdefefcdfa4787588651f59
-Bug: https://github.com/Unidata/netcdf4-python/issues/1331
-
---- /dev/null
-+++ b/external/nc_complex/include/generated_fallbacks/nc_complex_version.h
-@@ -0,0 +1,6 @@
-+// This is a fallback header for when building the library without
-+// CMake -- you probably should use CMake to auto-generate this instead
-+#define NC_COMPLEX_GIT_SHA1 "unknown"
-+#define NC_COMPLEX_GIT_VERSION "0.1.0"
-+#define NC_COMPLEX_GIT_STATE "unknown"
-+#define NC_COMPLEX_GIT_DATE "unknown"
---- /dev/null
-+++ b/external/nc_complex/include/nc_complex/nc_complex.h
-@@ -0,0 +1,291 @@
-+/// nc-complex: A lightweight, drop-in extension for complex number support in
-+/// netCDF
-+///
-+/// Copyright (C) 2023 Peter Hill
-+///
-+/// SPDX-License-Identifier: MIT
-+
-+#ifndef PLASMA_FAIR_NC_COMPLEX
-+#define PLASMA_FAIR_NC_COMPLEX
-+
-+// This header is required when building as a DLL on Windows and is
-+// automatically generated by CMake. If you're not using CMake (and
-+// not on Windows) for some reason, then define `NC_COMPLEX_NO_EXPORT`
-+// to skip this.
-+#ifndef NC_COMPLEX_NO_EXPORT
-+#include "nc_complex/nc_complex_export.h"
-+#else
-+#define NC_COMPLEX_EXPORT
-+#endif
-+
-+#include <complex.h>
-+#include <netcdf.h>
-+#include <stdbool.h>
-+#include <stddef.h>
-+
-+#ifdef __cplusplus
-+#include <complex>
-+#endif
-+
-+//@{
-+/// Portable typedefs for complex numbers
-+///
-+/// These become aliases for `std::complex` with C++.
-+#ifdef _MSC_VER
-+typedef _Dcomplex double_complex;
-+typedef _Fcomplex float_complex;
-+#else
-+#if defined(__cplusplus) && defined(__clang__)
-+using double_complex = std::complex<double>;
-+using float_complex = std::complex<float>;
-+#else
-+typedef double _Complex double_complex;
-+typedef float _Complex float_complex;
-+#endif
-+#endif
-+//@}
-+
-+#ifdef __cplusplus
-+/// @name Helper functions
-+///@{
-+/// Helper functions for converting between (pointers to) C++ and C complex types
-+NC_COMPLEX_EXPORT inline double_complex* cpp_to_c_complex(std::complex<double>* data) {
-+ return reinterpret_cast<double_complex*>(data);
-+}
-+
-+NC_COMPLEX_EXPORT inline std::complex<double>* c_to_cpp_complex(double_complex* data) {
-+ return reinterpret_cast<std::complex<double>*>(data);
-+}
-+
-+NC_COMPLEX_EXPORT inline float_complex* cpp_to_c_complex(std::complex<float>* data) {
-+ return reinterpret_cast<float_complex*>(data);
-+}
-+
-+NC_COMPLEX_EXPORT inline std::complex<float>* c_to_cpp_complex(float_complex* data) {
-+ return reinterpret_cast<std::complex<float>*>(data);
-+}
-+///@}
-+extern "C" {
-+#endif
-+
-+/// @name Complex datatype defines
-+/// Datatype for complex numbers, for use with \rstref{pfnc_def_var}
-+///
-+/// @note
-+/// These *only* work when defining a variable with \rstref{pfnc_def_var}. To
-+/// check the type of an existing variable use \rstref{pfnc_var_is_complex}, and
-+/// to check if it is specifically using a compound datatype or a dimension use
-+/// \rstref{pfnc_var_is_complex_type} or \rstref{pfnc_var_has_complex_dimension}
-+/// respectively.
-+/// @endnote
-+///@{
-+
-+/// Uses complex compound datatype with netCDF4 format, and complex dimension otherwise
-+#define PFNC_FLOAT_COMPLEX (NC_FIRSTUSERTYPEID - 4)
-+/// Always use a complex dimension, regardless of file format
-+#define PFNC_FLOAT_COMPLEX_DIM (NC_FIRSTUSERTYPEID - 3)
-+/// Uses complex compound datatype with netCDF4 format, and complex dimension otherwise
-+#define PFNC_DOUBLE_COMPLEX (NC_FIRSTUSERTYPEID - 2)
-+/// Always use a complex dimension, regardless of file format
-+#define PFNC_DOUBLE_COMPLEX_DIM (NC_FIRSTUSERTYPEID - 1)
-+///@}
-+
-+/// Return true if variable is complex
-+NC_COMPLEX_EXPORT bool pfnc_var_is_complex(int ncid, int varid);
-+/// Return true if variable is complex and uses a compound datatype
-+NC_COMPLEX_EXPORT bool pfnc_var_is_complex_type(int ncid, int varid);
-+/// Return true if variable is complex and has a complex dimension
-+/// (assumed to be the last dimension)
-+NC_COMPLEX_EXPORT bool pfnc_var_has_complex_dimension(int ncid, int varid);
-+
-+/// Return true if dimension is complex
-+NC_COMPLEX_EXPORT bool pfnc_is_complex_dim(int ncid, int dim_id);
-+
-+/// Get the ID for the complex datatype with `double` elements, creating it if it doesn't already exist
-+NC_COMPLEX_EXPORT int pfnc_get_double_complex_typeid(int ncid, nc_type* nc_typeid);
-+/// Get the ID for the complex datatype with `float` elements, creating it if it doesn't already exist
-+NC_COMPLEX_EXPORT int pfnc_get_float_complex_typeid(int ncid, nc_type* nc_typeid);
-+
-+/// Get complex dimension, creating one if it doesn't already exist
-+NC_COMPLEX_EXPORT int pfnc_get_complex_dim(int ncid, int* nc_dim);
-+
-+/// Get the base numerical type of a complex type
-+///
-+/// Returns the type of the components for a compound type, or the
-+/// type of an element for a dimension type.
-+NC_COMPLEX_EXPORT int pfnc_complex_base_type(
-+ int ncid, nc_type nc_typeid, nc_type* base_type_id
-+);
-+
-+/// Get the base numerical type of a complex variable
-+NC_COMPLEX_EXPORT int pfnc_inq_var_complex_base_type(
-+ int ncid, int varid, nc_type* nc_typeid
-+);
-+
-+/// Return some information about the `nc-complex` library
-+NC_COMPLEX_EXPORT const char* pfnc_inq_libvers(void);
-+
-+/// @name Wrappers
-+/// Wrappers for the equivalent `nc_*` functions that correctly handle
-+/// the start/count/stride arrays for complex dimensions.
-+///
-+/// When the variable is stored using a complex dimension, the file
-+/// representation has one more dimension than the user-visible
-+/// in-memory representation. For example, a 1D array:
-+///
-+/// ```c
-+/// double_complex data[5];
-+/// ```
-+///
-+/// would be represented in the file with two dimensions (when not
-+/// using a compound datatype!), and so if we use the standard netCDF
-+/// API we would need to use `{5, 2}` for the `countp` arguments, for
-+/// example, while using nc-complex, we only need `{5}`.
-+///
-+/// NOTE: The `pfnc_put/get*` functions do *not* currently handle
-+/// conversion between `float/double` base types
-+///@{
-+
-+/// Extension to `nc_def_var` that also accepts
-+/// \rstref{PFNC_FLOAT_COMPLEX}, \rstref{PFNC_FLOAT_COMPLEX_DIM},
-+/// \rstref{PFNC_DOUBLE_COMPLEX}, and \rstref{PFNC_DOUBLE_COMPLEX_DIM}
-+NC_COMPLEX_EXPORT int pfnc_def_var(
-+ int ncid,
-+ const char* name,
-+ nc_type xtype,
-+ int ndims,
-+ const int* dimidsp,
-+ int* varidp
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_put_vara_double_complex(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const double_complex* op
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_get_vara_double_complex(
-+ int ncid, int varid, const size_t* startp, const size_t* countp, double_complex* ip
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_put_vars_double_complex(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const ptrdiff_t* stridep,
-+ const double_complex* op
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_get_vars_double_complex(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const ptrdiff_t* stridep,
-+ double_complex* ip
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_put_var1_double_complex(
-+ int ncid, int varid, const size_t* indexp, const double_complex* data
-+);
-+NC_COMPLEX_EXPORT int pfnc_get_var1_double_complex(
-+ int ncid, int varid, const size_t* indexp, double_complex* data
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_put_vara_float_complex(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const float_complex* op
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_get_vara_float_complex(
-+ int ncid, int varid, const size_t* startp, const size_t* countp, float_complex* ip
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_put_vars_float_complex(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const ptrdiff_t* stridep,
-+ const float_complex* op
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_get_vars_float_complex(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const ptrdiff_t* stridep,
-+ float_complex* ip
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_put_var1_float_complex(
-+ int ncid, int varid, const size_t* indexp, const float_complex* data
-+);
-+NC_COMPLEX_EXPORT int pfnc_get_var1_float_complex(
-+ int ncid, int varid, const size_t* indexp, float_complex* data
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_inq_var(
-+ int ncid,
-+ int varid,
-+ char* name,
-+ nc_type* xtypep,
-+ int* ndimsp,
-+ int* dimidsp,
-+ int* nattsp
-+);
-+
-+// NOLINTBEGIN(modernize-use-nullptr)
-+NC_COMPLEX_EXPORT inline int pfnc_inq_varndims(int ncid, int varid, int* ndimsp) {
-+ return pfnc_inq_var(ncid, varid, NULL, NULL, ndimsp, NULL, NULL);
-+}
-+NC_COMPLEX_EXPORT inline int pfnc_inq_vardimid(int ncid, int varid, int* dimidsp) {
-+ return pfnc_inq_var(ncid, varid, NULL, NULL, NULL, dimidsp, NULL);
-+}
-+// NOLINTEND(modernize-use-nullptr)
-+
-+NC_COMPLEX_EXPORT int pfnc_def_var_chunking(
-+ int ncid, int varid, int storage, const size_t* chunksizesp
-+);
-+NC_COMPLEX_EXPORT int pfnc_inq_var_chunking(
-+ int ncid, int varid, int* storagep, size_t* chunksizesp
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_get_vara(
-+ int ncid, int varid, const size_t* startp, const size_t* countp, void* ip
-+);
-+NC_COMPLEX_EXPORT int pfnc_get_vars(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const ptrdiff_t* stridep,
-+ void* ip
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_put_vara(
-+ int ncid, int varid, const size_t* startp, const size_t* countp, const void* op
-+);
-+
-+NC_COMPLEX_EXPORT int pfnc_put_vars(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const ptrdiff_t* stridep,
-+ const void* op
-+);
-+///@}
-+
-+#ifdef __cplusplus
-+}
-+#endif
-+
-+#endif
---- /dev/null
-+++ b/external/nc_complex/src/nc_complex.c
-@@ -0,0 +1,867 @@
-+#include "nc_complex/nc_complex.h"
-+
-+#include <ctype.h>
-+#include <netcdf.h>
-+#include <stdbool.h>
-+#include <stddef.h>
-+#include <stdlib.h>
-+#include <string.h>
-+
-+#include "nc_complex_version.h"
-+
-+// NOLINTBEGIN(bugprone-assignment-in-if-condition)
-+#define CHECK(func) \
-+ do { \
-+ int res; \
-+ if ((res = (func))) { \
-+ return res; \
-+ } \
-+ } while (0)
-+// NOLINTEND(bugprone-assignment-in-if-condition)
-+
-+// Vector of ones for get/put_var1 functions
-+static const size_t coord_one[NC_MAX_VAR_DIMS] = {1};
-+
-+static const char* double_complex_struct_name = "_PFNC_DOUBLE_COMPLEX_TYPE";
-+static const char* float_complex_struct_name = "_PFNC_FLOAT_COMPLEX_TYPE";
-+
-+#define COMPLEX_DIM_NAME "_pfnc_complex"
-+static const char* complex_dim_name = COMPLEX_DIM_NAME;
-+
-+static const char* known_dim_names[] = {COMPLEX_DIM_NAME, "complex", "ri"};
-+static const size_t num_known_dim_names =
-+ sizeof(known_dim_names) / sizeof(known_dim_names[0]);
-+
-+static const char pfnc_libvers[] = NC_COMPLEX_GIT_VERSION;
-+
-+const char* pfnc_inq_libvers(void) {
-+ return pfnc_libvers;
-+}
-+
-+bool pfnc_var_is_complex(int ncid, int varid) {
-+ return pfnc_var_is_complex_type(ncid, varid)
-+ || pfnc_var_has_complex_dimension(ncid, varid);
-+}
-+
-+int pfnc_complex_base_type(int ncid, nc_type nc_typeid, nc_type* base_type_id) {
-+ if (nc_typeid < NC_MAX_ATOMIC_TYPE) {
-+ *base_type_id = nc_typeid;
-+ return NC_NOERR;
-+ }
-+
-+ // TODO: This should probably handle vlens too
-+
-+ return nc_inq_compound_field(
-+ ncid, nc_typeid, 0, NULL, NULL, base_type_id, NULL, NULL
-+ );
-+}
-+
-+int pfnc_inq_var_complex_base_type(int ncid, int varid, nc_type* nc_typeid) {
-+ nc_type var_type_id;
-+ CHECK(nc_inq_vartype(ncid, varid, &var_type_id));
-+ return pfnc_complex_base_type(ncid, var_type_id, nc_typeid);
-+}
-+
-+/// Return true if a compound type is compatible with a known convention
-+bool compound_type_is_compatible(int ncid, nc_type nc_typeid) {
-+ // Does the name matching a known convention?
-+ char name[NC_MAX_NAME + 1];
-+ nc_inq_compound_name(ncid, nc_typeid, name);
-+ if (name == double_complex_struct_name) {
-+ return true;
-+ }
-+
-+ // Does it have exactly two fields?
-+ size_t num_fields;
-+ nc_inq_compound_nfields(ncid, nc_typeid, &num_fields);
-+ if (num_fields != 2) {
-+ return false;
-+ }
-+
-+ // As far as I can tell, all conventions put the real part first and
-+ // the imaginary part second. I'm pretty sure all native language
-+ // types are also this way round. That means we don't have to worry
-+ // about trying both combinations!
-+ char real_name[NC_MAX_NAME + 1];
-+ size_t real_offset;
-+ nc_type real_field_type;
-+ int real_rank;
-+ nc_inq_compound_field(
-+ ncid, nc_typeid, 0, real_name, &real_offset, &real_field_type, &real_rank, NULL
-+ );
-+
-+ // If it's not a floating type, we're not interested
-+ if (!(real_field_type == NC_FLOAT || real_field_type == NC_DOUBLE)) {
-+ return false;
-+ }
-+ // Also needs to be scalar
-+ if (real_rank != 0) {
-+ return false;
-+ }
-+
-+ // Now check names. For now, just check it starts with "r", in any case
-+ if (tolower(real_name[0]) != 'r') {
-+ return false;
-+ }
-+
-+ char imag_name[NC_MAX_NAME + 1];
-+ size_t imag_offset;
-+ nc_type imag_field_type;
-+ int imag_rank;
-+ nc_inq_compound_field(
-+ ncid, nc_typeid, 1, imag_name, &imag_offset, &imag_field_type, &imag_rank, NULL
-+ );
-+
-+ // Both component types better match
-+ if (imag_field_type != real_field_type) {
-+ return false;
-+ }
-+ if (imag_rank != 0) {
-+ return false;
-+ }
-+ if (tolower(imag_name[0]) != 'i') {
-+ return false;
-+ }
-+
-+ return true;
-+}
-+
-+/// Return true if file already has a complex type with the given base type
-+bool file_has_complex_struct(int ncid, nc_type* typeidp, nc_type base_type) {
-+ // Simplest case, check for our type name
-+ int err = nc_inq_typeid(ncid, double_complex_struct_name, typeidp);
-+ if (err == NC_NOERR) {
-+ return true;
-+ }
-+
-+ int ntypes;
-+ err = nc_inq_typeids(ncid, &ntypes, NULL);
-+ if (err != NC_NOERR) {
-+ return false;
-+ }
-+
-+ bool result = false;
-+
-+ nc_type* typeids = malloc((size_t)ntypes * sizeof(nc_type));
-+ err = nc_inq_typeids(ncid, NULL, typeids);
-+ if (err != NC_NOERR) {
-+ goto cleanup;
-+ }
-+
-+ for (size_t i = 0; i < (size_t)ntypes; i++) {
-+ if (compound_type_is_compatible(ncid, typeids[i])) {
-+ nc_type base_type_id;
-+ err = pfnc_complex_base_type(ncid, typeids[i], &base_type_id);
-+ if (err != NC_NOERR) {
-+ goto cleanup;
-+ }
-+ if (base_type_id == base_type) {
-+ *typeidp = typeids[i];
-+ result = true;
-+ goto cleanup;
-+ }
-+ }
-+ }
-+cleanup:
-+ free(typeids);
-+ return result;
-+}
-+
-+/// Return true if a given dimension matches a known convention
-+bool pfnc_is_complex_dim(int ncid, int dim_id) {
-+ size_t length;
-+ nc_inq_dimlen(ncid, dim_id, &length);
-+
-+ // Definitely can only be exactly two. Note that we can't catch
-+ // unlimited dimensions that only have two records so far.
-+ if (length != 2) {
-+ return false;
-+ }
-+
-+ // Not sure if this is the best way, but here we are.
-+ char name[NC_MAX_NAME + 1];
-+ nc_inq_dimname(ncid, dim_id, name);
-+
-+ const size_t name_length = strlen(name);
-+
-+ // Check against known names of complex dimensions
-+ for (size_t i = 0; i < num_known_dim_names; i++) {
-+ if (strncmp(name, known_dim_names[i], name_length) == 0) {
-+ return true;
-+ }
-+ }
-+
-+ return false;
-+}
-+
-+/// Return true if a variable uses the dimension-convention
-+bool pfnc_var_has_complex_dimension(int ncid, int nc_varid) {
-+ int num_dims;
-+ nc_inq_varndims(ncid, nc_varid, &num_dims);
-+
-+ int* dim_ids = (int*)malloc((size_t)num_dims * sizeof(int));
-+ nc_inq_vardimid(ncid, nc_varid, dim_ids);
-+
-+ // Now we check if any of the dimensions match one of our known
-+ // conventions. Do we need to check all of them, or just the
-+ // first/last?
-+ for (int i = 0; i < num_dims; i++) {
-+ if (pfnc_is_complex_dim(ncid, dim_ids[i])) {
-+ free(dim_ids);
-+ return true;
-+ }
-+ }
-+
-+ free(dim_ids);
-+ return false;
-+}
-+
-+/// Return true if a netCDF datatype is a compound type
-+bool is_compound_type(int ncid, int type_id) {
-+ // There appears to be no API for detecting whether a type ID is a
-+ // primitive type, so we have to check ourselves
-+ if (type_id <= NC_MAX_ATOMIC_TYPE) {
-+ return false;
-+ }
-+
-+ int class_type;
-+ nc_inq_user_type(ncid, type_id, NULL, NULL, NULL, NULL, &class_type);
-+ return class_type == NC_COMPOUND;
-+}
-+
-+/// Copy an array meant for a complex-dimensioned variable
-+size_t* copy_complex_dim_size_t_array(
-+ const size_t* old_array, int numdims, size_t complex_dim_value
-+) {
-+ size_t* new_buffer = NULL;
-+
-+ if (old_array != NULL) {
-+ new_buffer = (size_t*)malloc(sizeof(size_t) * (size_t)numdims);
-+
-+ size_t last_dim = (size_t)(numdims - 1);
-+ for (size_t i = 0; i < last_dim; i++) {
-+ new_buffer[i] = old_array[i];
-+ }
-+
-+ new_buffer[last_dim] = complex_dim_value;
-+ }
-+ return new_buffer;
-+}
-+
-+ptrdiff_t* copy_complex_dim_ptrdiff_t_array(
-+ const ptrdiff_t* old_array, int numdims, ptrdiff_t complex_dim_value
-+) {
-+ ptrdiff_t* new_buffer = NULL;
-+
-+ if (old_array != NULL) {
-+ new_buffer = (ptrdiff_t*)malloc(sizeof(ptrdiff_t) * (size_t)numdims);
-+
-+ size_t last_dim = (size_t)(numdims - 1);
-+ for (size_t i = 0; i < last_dim; i++) {
-+ new_buffer[i] = old_array[i];
-+ }
-+
-+ new_buffer[last_dim] = complex_dim_value;
-+ }
-+ return new_buffer;
-+}
-+
-+bool pfnc_var_is_complex_type(int ncid, int varid) {
-+ nc_type var_type_id;
-+ if (nc_inq_vartype(ncid, varid, &var_type_id)) {
-+ return false;
-+ }
-+
-+ if (is_compound_type(ncid, var_type_id)) {
-+ return compound_type_is_compatible(ncid, var_type_id);
-+ }
-+ return false;
-+}
-+
-+size_t complex_type_size(nc_type base_type) {
-+ switch (base_type) {
-+ case NC_FLOAT:
-+ return sizeof(float_complex);
-+ case NC_DOUBLE:
-+ return sizeof(double_complex);
-+ default:
-+ return 0;
-+ }
-+}
-+
-+size_t base_type_size(nc_type base_type) {
-+ switch (base_type) {
-+ case NC_FLOAT:
-+ return sizeof(float);
-+ case NC_DOUBLE:
-+ return sizeof(double);
-+ default:
-+ return 0;
-+ }
-+}
-+
-+int get_or_make_complex_struct(
-+ int ncid, nc_type* nc_typeid, nc_type base_type, const char* struct_name
-+) {
-+ // TODO: Error if not netCDF4
-+
-+ if (file_has_complex_struct(ncid, nc_typeid, base_type)) {
-+ return NC_NOERR;
-+ }
-+
-+ const size_t complex_size = complex_type_size(base_type);
-+ if (complex_size == 0) {
-+ return NC_EBADTYPE;
-+ }
-+ const size_t base_size = base_type_size(base_type);
-+ if (base_size == 0) {
-+ return NC_EBADTYPE;
-+ }
-+
-+ CHECK(nc_def_compound(ncid, complex_size, struct_name, nc_typeid));
-+ CHECK(nc_insert_compound(ncid, *nc_typeid, "r", 0, base_type));
-+ CHECK(nc_insert_compound(ncid, *nc_typeid, "i", base_size, base_type));
-+
-+ return NC_NOERR;
-+}
-+
-+int pfnc_get_double_complex_typeid(int ncid, nc_type* nc_typeid) {
-+ return get_or_make_complex_struct(
-+ ncid, nc_typeid, NC_DOUBLE, double_complex_struct_name
-+ );
-+}
-+
-+int pfnc_get_float_complex_typeid(int ncid, nc_type* nc_typeid) {
-+ return get_or_make_complex_struct(
-+ ncid, nc_typeid, NC_FLOAT, float_complex_struct_name
-+ );
-+}
-+
-+int pfnc_get_complex_dim(int ncid, int* nc_dim) {
-+ int num_dims;
-+ CHECK(nc_inq_ndims(ncid, &num_dims));
-+
-+ int* dim_ids = (int*)malloc((size_t)num_dims * sizeof(int));
-+ int ierr = nc_inq_dimids(ncid, NULL, dim_ids, true);
-+ if (ierr != NC_NOERR) {
-+ goto cleanup;
-+ }
-+
-+ // Now we check if any of the dimensions match one of our known
-+ // conventions. Do we need to check all of them, or just the
-+ // first/last?
-+ for (int i = 0; i < num_dims; i++) {
-+ if (pfnc_is_complex_dim(ncid, dim_ids[i])) {
-+ *nc_dim = dim_ids[i];
-+ goto cleanup;
-+ }
-+ }
-+
-+ ierr = nc_def_dim(ncid, complex_dim_name, 2, nc_dim);
-+
-+cleanup:
-+ free(dim_ids);
-+ return ierr;
-+}
-+
-+int pfnc_put_vara_double_complex(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const double_complex* op
-+) {
-+ return pfnc_put_vars_double_complex(ncid, varid, startp, countp, NULL, op);
-+}
-+
-+int pfnc_get_vara_double_complex(
-+ int ncid, int varid, const size_t* startp, const size_t* countp, double_complex* ip
-+) {
-+ return pfnc_get_vars_double_complex(ncid, varid, startp, countp, NULL, ip);
-+}
-+
-+int pfnc_put_vars_double_complex(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const ptrdiff_t* stridep,
-+ const double_complex* op
-+) {
-+ if (!pfnc_var_is_complex(ncid, varid)) {
-+ return NC_EBADTYPE;
-+ }
-+
-+ // TODO: handle converting different float sizes
-+
-+ // Check if we can get away without fudging count/start sizes
-+ if (((startp == NULL) && (countp == NULL) && (stridep == NULL))
-+ || !pfnc_var_has_complex_dimension(ncid, varid)) {
-+ return nc_put_vars(ncid, varid, startp, countp, stridep, op);
-+ }
-+
-+ // The real variable has a complex dimension, but we're pretending
-+ // it doesn't, so now we need start/count arrays of the real size
-+
-+ int numdims = 0;
-+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
-+
-+ // Copy start/count buffers, appending an extra element for the
-+ // complex dimension. This dimension starts at 0 and has 2 elements
-+ size_t* start_buffer = copy_complex_dim_size_t_array(startp, numdims, 0);
-+ size_t* count_buffer = copy_complex_dim_size_t_array(countp, numdims, 2);
-+ ptrdiff_t* stride_buffer = copy_complex_dim_ptrdiff_t_array(stridep, numdims, 1);
-+
-+ const int ierr =
-+ nc_put_vars(ncid, varid, start_buffer, count_buffer, stride_buffer, op);
-+
-+ if (start_buffer != NULL) {
-+ free(start_buffer);
-+ }
-+ if (count_buffer != NULL) {
-+ free(count_buffer);
-+ }
-+ if (stride_buffer != NULL) {
-+ free(stride_buffer);
-+ }
-+ return ierr;
-+}
-+
-+int pfnc_get_vars_double_complex(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const ptrdiff_t* stridep,
-+ double_complex* ip
-+) {
-+ if (!pfnc_var_is_complex(ncid, varid)) {
-+ return NC_EBADTYPE;
-+ }
-+
-+ // TODO: handle converting different float sizes
-+
-+ // Check if we can get away without fudging count/start sizes
-+ if (((startp == NULL) && (countp == NULL) && (stridep == NULL))
-+ || !pfnc_var_has_complex_dimension(ncid, varid)) {
-+ return nc_get_vars(ncid, varid, startp, countp, stridep, ip);
-+ }
-+
-+ // The real variable has a complex dimension, but we're pretending
-+ // it doesn't, so now we need start/count arrays of the real size
-+
-+ int numdims = 0;
-+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
-+
-+ // Copy start/count buffers, appending an extra element for the
-+ // complex dimension. This dimension starts at 0 and has 2 elements
-+ size_t* start_buffer = copy_complex_dim_size_t_array(startp, numdims, 0);
-+ size_t* count_buffer = copy_complex_dim_size_t_array(countp, numdims, 2);
-+ ptrdiff_t* stride_buffer = copy_complex_dim_ptrdiff_t_array(stridep, numdims, 1);
-+
-+ const int ierr =
-+ nc_get_vars(ncid, varid, start_buffer, count_buffer, stride_buffer, ip);
-+
-+ if (start_buffer != NULL) {
-+ free(start_buffer);
-+ }
-+ if (count_buffer != NULL) {
-+ free(count_buffer);
-+ }
-+ if (stride_buffer != NULL) {
-+ free(stride_buffer);
-+ }
-+ return ierr;
-+}
-+
-+int pfnc_put_var1_double_complex(
-+ int ncid, int varid, const size_t* indexp, const double_complex* data
-+) {
-+ return pfnc_put_vara_double_complex(ncid, varid, indexp, coord_one, data);
-+}
-+
-+int pfnc_get_var1_double_complex(
-+ int ncid, int varid, const size_t* indexp, double_complex* data
-+) {
-+ return pfnc_get_vara_double_complex(ncid, varid, indexp, coord_one, data);
-+}
-+
-+int pfnc_put_vara_float_complex(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const float_complex* op
-+) {
-+ return pfnc_put_vars_float_complex(ncid, varid, startp, countp, NULL, op);
-+}
-+
-+int pfnc_get_vara_float_complex(
-+ int ncid, int varid, const size_t* startp, const size_t* countp, float_complex* ip
-+) {
-+ return pfnc_get_vars_float_complex(ncid, varid, startp, countp, NULL, ip);
-+}
-+
-+int pfnc_put_vars_float_complex(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const ptrdiff_t* stridep,
-+ const float_complex* op
-+) {
-+ if (!pfnc_var_is_complex(ncid, varid)) {
-+ return NC_EBADTYPE;
-+ }
-+
-+ // TODO: handle converting different float sizes
-+
-+ // Check if we can get away without fudging count/start sizes
-+ if (((startp == NULL) && (countp == NULL) && (stridep == NULL))
-+ || !pfnc_var_has_complex_dimension(ncid, varid)) {
-+ return nc_put_vars(ncid, varid, startp, countp, stridep, op);
-+ }
-+
-+ // The real variable has a complex dimension, but we're pretending
-+ // it doesn't, so now we need start/count arrays of the real size
-+
-+ int numdims = 0;
-+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
-+
-+ // Copy start/count buffers, appending an extra element for the
-+ // complex dimension. This dimension starts at 0 and has 2 elements
-+ size_t* start_buffer = copy_complex_dim_size_t_array(startp, numdims, 0);
-+ size_t* count_buffer = copy_complex_dim_size_t_array(countp, numdims, 2);
-+ ptrdiff_t* stride_buffer = copy_complex_dim_ptrdiff_t_array(stridep, numdims, 1);
-+
-+ const int ierr =
-+ nc_put_vars(ncid, varid, start_buffer, count_buffer, stride_buffer, op);
-+
-+ if (start_buffer != NULL) {
-+ free(start_buffer);
-+ }
-+ if (count_buffer != NULL) {
-+ free(count_buffer);
-+ }
-+ if (stride_buffer != NULL) {
-+ free(stride_buffer);
-+ }
-+ return ierr;
-+}
-+
-+int pfnc_get_vars_float_complex(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const ptrdiff_t* stridep,
-+ float_complex* ip
-+) {
-+ if (!pfnc_var_is_complex(ncid, varid)) {
-+ return NC_EBADTYPE;
-+ }
-+
-+ // TODO: handle converting different float sizes
-+
-+ // Check if we can get away without fudging count/start sizes
-+ if (((startp == NULL) && (countp == NULL) && (stridep == NULL))
-+ || !pfnc_var_has_complex_dimension(ncid, varid)) {
-+ return nc_get_vars(ncid, varid, startp, countp, stridep, ip);
-+ }
-+
-+ // The real variable has a complex dimension, but we're pretending
-+ // it doesn't, so now we need start/count arrays of the real size
-+
-+ int numdims = 0;
-+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
-+
-+ // Copy start/count buffers, appending an extra element for the
-+ // complex dimension. This dimension starts at 0 and has 2 elements
-+ size_t* start_buffer = copy_complex_dim_size_t_array(startp, numdims, 0);
-+ size_t* count_buffer = copy_complex_dim_size_t_array(countp, numdims, 2);
-+ ptrdiff_t* stride_buffer = copy_complex_dim_ptrdiff_t_array(stridep, numdims, 1);
-+
-+ const int ierr =
-+ nc_get_vars(ncid, varid, start_buffer, count_buffer, stride_buffer, ip);
-+
-+ if (start_buffer != NULL) {
-+ free(start_buffer);
-+ }
-+ if (count_buffer != NULL) {
-+ free(count_buffer);
-+ }
-+ if (stride_buffer != NULL) {
-+ free(stride_buffer);
-+ }
-+ return ierr;
-+}
-+
-+int pfnc_put_var1_float_complex(
-+ int ncid, int varid, const size_t* indexp, const float_complex* data
-+) {
-+ return pfnc_put_vara_float_complex(ncid, varid, indexp, coord_one, data);
-+}
-+
-+int pfnc_get_var1_float_complex(
-+ int ncid, int varid, const size_t* indexp, float_complex* data
-+) {
-+ return pfnc_get_vara_float_complex(ncid, varid, indexp, coord_one, data);
-+}
-+
-+int pfnc_def_var(
-+ int ncid,
-+ const char* name,
-+ nc_type xtype,
-+ int ndims,
-+ const int* dimidsp,
-+ int* varidp
-+) {
-+ // If it's not a complex number, we don't need to do anything
-+ if (!(xtype == PFNC_DOUBLE_COMPLEX || xtype == PFNC_DOUBLE_COMPLEX_DIM
-+ || xtype == PFNC_FLOAT_COMPLEX || xtype == PFNC_FLOAT_COMPLEX_DIM)) {
-+ return nc_def_var(ncid, name, xtype, ndims, dimidsp, varidp);
-+ }
-+
-+ const bool base_is_double =
-+ (xtype == PFNC_DOUBLE_COMPLEX || xtype == PFNC_DOUBLE_COMPLEX_DIM);
-+
-+ // Check the format used by this file. If it's some variation on the
-+ // classic model, then we have to use a complex dimension. Also,
-+ // NcZarr, for some reason doesn't support compound types (yet?).
-+ // I _think_ DAP supports compound types
-+ int format = 0;
-+ int mode = 0;
-+ CHECK(nc_inq_format_extended(ncid, &format, &mode));
-+
-+ if ((format == NC_FORMAT_CLASSIC || format == NC_FORMAT_NETCDF4_CLASSIC)
-+ || (mode == NC_FORMATX_NCZARR)) {
-+ xtype = base_is_double ? PFNC_DOUBLE_COMPLEX_DIM : PFNC_FLOAT_COMPLEX_DIM;
-+ }
-+
-+ if (xtype == PFNC_DOUBLE_COMPLEX_DIM || xtype == PFNC_FLOAT_COMPLEX_DIM) {
-+ // Using a complex dimension. We need to get the complex dimension
-+ // used in this file and append it to the list of dimensions
-+ // passed in by the user
-+
-+ int complex_dim = 0;
-+ CHECK(pfnc_get_complex_dim(ncid, &complex_dim));
-+
-+ int new_dims = ndims + 1;
-+ int* dim_ids_buffer = (int*)malloc((size_t)new_dims * sizeof(int));
-+ for (size_t i = 0; i < (size_t)ndims; i++) {
-+ dim_ids_buffer[i] = dimidsp[i];
-+ }
-+ dim_ids_buffer[ndims] = complex_dim;
-+
-+ const nc_type base_type = base_is_double ? NC_DOUBLE : NC_FLOAT;
-+
-+ const int ierr =
-+ nc_def_var(ncid, name, base_type, new_dims, dim_ids_buffer, varidp);
-+ free(dim_ids_buffer);
-+ return ierr;
-+ }
-+
-+ // Using a complex type. We need to get the complex type used in
-+ // this file and pass that as `xtype`
-+ nc_type complex_type = 0;
-+ if (base_is_double) {
-+ CHECK(pfnc_get_double_complex_typeid(ncid, &complex_type));
-+ } else {
-+ CHECK(pfnc_get_float_complex_typeid(ncid, &complex_type));
-+ }
-+
-+ return nc_def_var(ncid, name, complex_type, ndims, dimidsp, varidp);
-+}
-+
-+int pfnc_inq_var(
-+ int ncid,
-+ int varid,
-+ char* name,
-+ nc_type* xtypep,
-+ int* ndimsp,
-+ int* dimidsp,
-+ int* nattsp
-+) {
-+ if (!pfnc_var_has_complex_dimension(ncid, varid)) {
-+ return nc_inq_var(ncid, varid, name, xtypep, ndimsp, dimidsp, nattsp);
-+ }
-+
-+ // Tricky bit: if variable has complex dimension, and user used
-+ // pfnc_inq_varndims, then dimidsp is one smaller than netCDF thinks
-+ // it should be. So we'll have to allocate our own array of the
-+ // correct size and copy out of that.
-+
-+ // This buffer will point to either the user's array, or our own one
-+ int* buffer = dimidsp;
-+ int numdims = 0;
-+
-+ if (dimidsp != NULL) {
-+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
-+ buffer = (int*)malloc(sizeof(int) * (size_t)numdims);
-+ }
-+
-+ int ierr = nc_inq_var(ncid, varid, name, xtypep, &numdims, buffer, nattsp);
-+
-+ if (ierr != NC_NOERR) {
-+ goto cleanup;
-+ }
-+
-+ if (dimidsp != NULL) {
-+ if (numdims <= 0) {
-+ // This should never happen
-+ goto cleanup;
-+ }
-+ const size_t other_dims = (size_t)(numdims - 1);
-+ for (size_t i = 0; i < other_dims; i++) {
-+ dimidsp[i] = buffer[i];
-+ }
-+ }
-+
-+ if (ndimsp != NULL) {
-+ *ndimsp = numdims - 1;
-+ }
-+
-+cleanup:
-+ free(buffer);
-+ return ierr;
-+}
-+
-+int pfnc_def_var_chunking(int ncid, int varid, int storage, const size_t* chunksizesp) {
-+ if (chunksizesp == NULL || !pfnc_var_has_complex_dimension(ncid, varid)) {
-+ return nc_def_var_chunking(ncid, varid, storage, chunksizesp);
-+ }
-+
-+ // The real variable has a complex dimension, but we're pretending
-+ // it doesn't, so now we need start/count arrays of the real size
-+
-+ int numdims = 0;
-+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
-+
-+ // Copy chunksize buffer, appending an extra element for the
-+ // complex dimension
-+ size_t* chunk_buffer = copy_complex_dim_size_t_array(chunksizesp, numdims, 2);
-+
-+ const int ierr = nc_def_var_chunking(ncid, varid, storage, chunk_buffer);
-+ free(chunk_buffer);
-+ return ierr;
-+}
-+
-+int pfnc_inq_var_chunking(int ncid, int varid, int* storagep, size_t* chunksizesp) {
-+ if (chunksizesp == NULL || !pfnc_var_has_complex_dimension(ncid, varid)) {
-+ return nc_inq_var_chunking(ncid, varid, storagep, chunksizesp);
-+ }
-+
-+ int numdims = 0;
-+
-+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
-+
-+ // Copy chunksize buffer, appending an extra element for the
-+ // complex dimension
-+ size_t* chunk_buffer = copy_complex_dim_size_t_array(chunksizesp, numdims, 2);
-+
-+ const int ierr = nc_inq_var_chunking(ncid, varid, storagep, chunk_buffer);
-+
-+ if (ierr != NC_NOERR) {
-+ goto cleanup;
-+ }
-+
-+ const size_t other_dims = (size_t)(numdims - 1);
-+ for (size_t i = 0; i < other_dims; i++) {
-+ chunksizesp[i] = chunk_buffer[i];
-+ }
-+
-+cleanup:
-+ free(chunk_buffer);
-+ return ierr;
-+}
-+
-+int pfnc_get_vara(
-+ int ncid, int varid, const size_t* startp, const size_t* countp, void* ip
-+) {
-+ if (pfnc_var_is_complex(ncid, varid)) {
-+ nc_type base_type;
-+ CHECK(pfnc_inq_var_complex_base_type(ncid, varid, &base_type));
-+ switch (base_type) {
-+ case NC_DOUBLE:
-+ return pfnc_get_vara_double_complex(ncid, varid, startp, countp, ip);
-+ case NC_FLOAT:
-+ return pfnc_get_vara_float_complex(ncid, varid, startp, countp, ip);
-+ default:
-+ return NC_EBADTYPE;
-+ }
-+ }
-+
-+ return nc_get_vara(ncid, varid, startp, countp, ip);
-+}
-+
-+int pfnc_put_vara(
-+ int ncid, int varid, const size_t* startp, const size_t* countp, const void* op
-+) {
-+ if (pfnc_var_is_complex(ncid, varid)) {
-+ nc_type base_type;
-+ CHECK(pfnc_inq_var_complex_base_type(ncid, varid, &base_type));
-+ switch (base_type) {
-+ case NC_DOUBLE:
-+ return pfnc_put_vara_double_complex(ncid, varid, startp, countp, op);
-+ case NC_FLOAT:
-+ return pfnc_put_vara_float_complex(ncid, varid, startp, countp, op);
-+ default:
-+ return NC_EBADTYPE;
-+ }
-+ }
-+ return nc_put_vara(ncid, varid, startp, countp, op);
-+}
-+
-+int pfnc_put_vars(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const ptrdiff_t* stridep,
-+ const void* op
-+) {
-+ if (pfnc_var_is_complex(ncid, varid)) {
-+ nc_type base_type;
-+ CHECK(pfnc_inq_var_complex_base_type(ncid, varid, &base_type));
-+ switch (base_type) {
-+ case NC_DOUBLE:
-+ return pfnc_put_vars_double_complex(
-+ ncid, varid, startp, countp, stridep, op
-+ );
-+ case NC_FLOAT:
-+ return pfnc_put_vars_float_complex(
-+ ncid, varid, startp, countp, stridep, op
-+ );
-+ default:
-+ return NC_EBADTYPE;
-+ }
-+ }
-+ return nc_put_vars(ncid, varid, startp, countp, stridep, op);
-+}
-+
-+int pfnc_get_vars(
-+ int ncid,
-+ int varid,
-+ const size_t* startp,
-+ const size_t* countp,
-+ const ptrdiff_t* stridep,
-+ void* ip
-+) {
-+ if (pfnc_var_is_complex(ncid, varid)) {
-+ nc_type base_type;
-+ CHECK(pfnc_inq_var_complex_base_type(ncid, varid, &base_type));
-+ switch (base_type) {
-+ case NC_DOUBLE:
-+ return pfnc_get_vars_double_complex(
-+ ncid, varid, startp, countp, stridep, ip
-+ );
-+ case NC_FLOAT:
-+ return pfnc_get_vars_float_complex(
-+ ncid, varid, startp, countp, stridep, ip
-+ );
-+ default:
-+ return NC_EBADTYPE;
-+ }
-+ }
-+ return nc_get_vars(ncid, varid, startp, countp, stridep, ip);
-+}
=====================================
debian/patches/series
=====================================
@@ -1,2 +1 @@
rpath.patch
-nc-complex.patch
=====================================
external/README
=====================================
@@ -0,0 +1 @@
+* 20240616: remove submodule, include v0.2.0 tag source files (https://github.com/PlasmaFAIR/nc-complex/releases/tag/v0.2.0).
=====================================
external/nc_complex/include/generated_fallbacks/nc_complex_version.h
=====================================
@@ -0,0 +1,4 @@
+#define NC_COMPLEX_GIT_SHA1 "37310ed00f3910974bdefefcdfa4787588651f59"
+#define NC_COMPLEX_GIT_VERSION "v0.2.0"
+#define NC_COMPLEX_GIT_STATE "clean"
+#define NC_COMPLEX_GIT_DATE "2023-12-08"
=====================================
external/nc_complex/include/nc_complex/nc_complex.h
=====================================
@@ -0,0 +1,291 @@
+/// nc-complex: A lightweight, drop-in extension for complex number support in
+/// netCDF
+///
+/// Copyright (C) 2023 Peter Hill
+///
+/// SPDX-License-Identifier: MIT
+
+#ifndef PLASMA_FAIR_NC_COMPLEX
+#define PLASMA_FAIR_NC_COMPLEX
+
+// This header is required when building as a DLL on Windows and is
+// automatically generated by CMake. If you're not using CMake (and
+// not on Windows) for some reason, then define `NC_COMPLEX_NO_EXPORT`
+// to skip this.
+#ifndef NC_COMPLEX_NO_EXPORT
+#include "nc_complex/nc_complex_export.h"
+#else
+#define NC_COMPLEX_EXPORT
+#endif
+
+#include <complex.h>
+#include <netcdf.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+#include <complex>
+#endif
+
+//@{
+/// Portable typedefs for complex numbers
+///
+/// These become aliases for `std::complex` with C++.
+#ifdef _MSC_VER
+typedef _Dcomplex double_complex;
+typedef _Fcomplex float_complex;
+#else
+#if defined(__cplusplus) && defined(__clang__)
+using double_complex = std::complex<double>;
+using float_complex = std::complex<float>;
+#else
+typedef double _Complex double_complex;
+typedef float _Complex float_complex;
+#endif
+#endif
+//@}
+
+#ifdef __cplusplus
+/// @name Helper functions
+///@{
+/// Helper functions for converting between (pointers to) C++ and C complex types
+NC_COMPLEX_EXPORT inline double_complex* cpp_to_c_complex(std::complex<double>* data) {
+ return reinterpret_cast<double_complex*>(data);
+}
+
+NC_COMPLEX_EXPORT inline std::complex<double>* c_to_cpp_complex(double_complex* data) {
+ return reinterpret_cast<std::complex<double>*>(data);
+}
+
+NC_COMPLEX_EXPORT inline float_complex* cpp_to_c_complex(std::complex<float>* data) {
+ return reinterpret_cast<float_complex*>(data);
+}
+
+NC_COMPLEX_EXPORT inline std::complex<float>* c_to_cpp_complex(float_complex* data) {
+ return reinterpret_cast<std::complex<float>*>(data);
+}
+///@}
+extern "C" {
+#endif
+
+/// @name Complex datatype defines
+/// Datatype for complex numbers, for use with \rstref{pfnc_def_var}
+///
+/// @note
+/// These *only* work when defining a variable with \rstref{pfnc_def_var}. To
+/// check the type of an existing variable use \rstref{pfnc_var_is_complex}, and
+/// to check if it is specifically using a compound datatype or a dimension use
+/// \rstref{pfnc_var_is_complex_type} or \rstref{pfnc_var_has_complex_dimension}
+/// respectively.
+/// @endnote
+///@{
+
+/// Uses complex compound datatype with netCDF4 format, and complex dimension otherwise
+#define PFNC_FLOAT_COMPLEX (NC_FIRSTUSERTYPEID - 4)
+/// Always use a complex dimension, regardless of file format
+#define PFNC_FLOAT_COMPLEX_DIM (NC_FIRSTUSERTYPEID - 3)
+/// Uses complex compound datatype with netCDF4 format, and complex dimension otherwise
+#define PFNC_DOUBLE_COMPLEX (NC_FIRSTUSERTYPEID - 2)
+/// Always use a complex dimension, regardless of file format
+#define PFNC_DOUBLE_COMPLEX_DIM (NC_FIRSTUSERTYPEID - 1)
+///@}
+
+/// Return true if variable is complex
+NC_COMPLEX_EXPORT bool pfnc_var_is_complex(int ncid, int varid);
+/// Return true if variable is complex and uses a compound datatype
+NC_COMPLEX_EXPORT bool pfnc_var_is_complex_type(int ncid, int varid);
+/// Return true if variable is complex and has a complex dimension
+/// (assumed to be the last dimension)
+NC_COMPLEX_EXPORT bool pfnc_var_has_complex_dimension(int ncid, int varid);
+
+/// Return true if dimension is complex
+NC_COMPLEX_EXPORT bool pfnc_is_complex_dim(int ncid, int dim_id);
+
+/// Get the ID for the complex datatype with `double` elements, creating it if it doesn't already exist
+NC_COMPLEX_EXPORT int pfnc_get_double_complex_typeid(int ncid, nc_type* nc_typeid);
+/// Get the ID for the complex datatype with `float` elements, creating it if it doesn't already exist
+NC_COMPLEX_EXPORT int pfnc_get_float_complex_typeid(int ncid, nc_type* nc_typeid);
+
+/// Get complex dimension, creating one if it doesn't already exist
+NC_COMPLEX_EXPORT int pfnc_get_complex_dim(int ncid, int* nc_dim);
+
+/// Get the base numerical type of a complex type
+///
+/// Returns the type of the components for a compound type, or the
+/// type of an element for a dimension type.
+NC_COMPLEX_EXPORT int pfnc_complex_base_type(
+ int ncid, nc_type nc_typeid, nc_type* base_type_id
+);
+
+/// Get the base numerical type of a complex variable
+NC_COMPLEX_EXPORT int pfnc_inq_var_complex_base_type(
+ int ncid, int varid, nc_type* nc_typeid
+);
+
+/// Return some information about the `nc-complex` library
+NC_COMPLEX_EXPORT const char* pfnc_inq_libvers(void);
+
+/// @name Wrappers
+/// Wrappers for the equivalent `nc_*` functions that correctly handle
+/// the start/count/stride arrays for complex dimensions.
+///
+/// When the variable is stored using a complex dimension, the file
+/// representation has one more dimension than the user-visible
+/// in-memory representation. For example, a 1D array:
+///
+/// ```c
+/// double_complex data[5];
+/// ```
+///
+/// would be represented in the file with two dimensions (when not
+/// using a compound datatype!), and so if we use the standard netCDF
+/// API we would need to use `{5, 2}` for the `countp` arguments, for
+/// example, while using nc-complex, we only need `{5}`.
+///
+/// NOTE: The `pfnc_put/get*` functions do *not* currently handle
+/// conversion between `float/double` base types
+///@{
+
+/// Extension to `nc_def_var` that also accepts
+/// \rstref{PFNC_FLOAT_COMPLEX}, \rstref{PFNC_FLOAT_COMPLEX_DIM},
+/// \rstref{PFNC_DOUBLE_COMPLEX}, and \rstref{PFNC_DOUBLE_COMPLEX_DIM}
+NC_COMPLEX_EXPORT int pfnc_def_var(
+ int ncid,
+ const char* name,
+ nc_type xtype,
+ int ndims,
+ const int* dimidsp,
+ int* varidp
+);
+
+NC_COMPLEX_EXPORT int pfnc_put_vara_double_complex(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const double_complex* op
+);
+
+NC_COMPLEX_EXPORT int pfnc_get_vara_double_complex(
+ int ncid, int varid, const size_t* startp, const size_t* countp, double_complex* ip
+);
+
+NC_COMPLEX_EXPORT int pfnc_put_vars_double_complex(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const ptrdiff_t* stridep,
+ const double_complex* op
+);
+
+NC_COMPLEX_EXPORT int pfnc_get_vars_double_complex(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const ptrdiff_t* stridep,
+ double_complex* ip
+);
+
+NC_COMPLEX_EXPORT int pfnc_put_var1_double_complex(
+ int ncid, int varid, const size_t* indexp, const double_complex* data
+);
+NC_COMPLEX_EXPORT int pfnc_get_var1_double_complex(
+ int ncid, int varid, const size_t* indexp, double_complex* data
+);
+
+NC_COMPLEX_EXPORT int pfnc_put_vara_float_complex(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const float_complex* op
+);
+
+NC_COMPLEX_EXPORT int pfnc_get_vara_float_complex(
+ int ncid, int varid, const size_t* startp, const size_t* countp, float_complex* ip
+);
+
+NC_COMPLEX_EXPORT int pfnc_put_vars_float_complex(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const ptrdiff_t* stridep,
+ const float_complex* op
+);
+
+NC_COMPLEX_EXPORT int pfnc_get_vars_float_complex(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const ptrdiff_t* stridep,
+ float_complex* ip
+);
+
+NC_COMPLEX_EXPORT int pfnc_put_var1_float_complex(
+ int ncid, int varid, const size_t* indexp, const float_complex* data
+);
+NC_COMPLEX_EXPORT int pfnc_get_var1_float_complex(
+ int ncid, int varid, const size_t* indexp, float_complex* data
+);
+
+NC_COMPLEX_EXPORT int pfnc_inq_var(
+ int ncid,
+ int varid,
+ char* name,
+ nc_type* xtypep,
+ int* ndimsp,
+ int* dimidsp,
+ int* nattsp
+);
+
+// NOLINTBEGIN(modernize-use-nullptr)
+NC_COMPLEX_EXPORT inline int pfnc_inq_varndims(int ncid, int varid, int* ndimsp) {
+ return pfnc_inq_var(ncid, varid, NULL, NULL, ndimsp, NULL, NULL);
+}
+NC_COMPLEX_EXPORT inline int pfnc_inq_vardimid(int ncid, int varid, int* dimidsp) {
+ return pfnc_inq_var(ncid, varid, NULL, NULL, NULL, dimidsp, NULL);
+}
+// NOLINTEND(modernize-use-nullptr)
+
+NC_COMPLEX_EXPORT int pfnc_def_var_chunking(
+ int ncid, int varid, int storage, const size_t* chunksizesp
+);
+NC_COMPLEX_EXPORT int pfnc_inq_var_chunking(
+ int ncid, int varid, int* storagep, size_t* chunksizesp
+);
+
+NC_COMPLEX_EXPORT int pfnc_get_vara(
+ int ncid, int varid, const size_t* startp, const size_t* countp, void* ip
+);
+NC_COMPLEX_EXPORT int pfnc_get_vars(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const ptrdiff_t* stridep,
+ void* ip
+);
+
+NC_COMPLEX_EXPORT int pfnc_put_vara(
+ int ncid, int varid, const size_t* startp, const size_t* countp, const void* op
+);
+
+NC_COMPLEX_EXPORT int pfnc_put_vars(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const ptrdiff_t* stridep,
+ const void* op
+);
+///@}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
=====================================
external/nc_complex/src/nc_complex.c
=====================================
@@ -0,0 +1,867 @@
+#include "nc_complex/nc_complex.h"
+
+#include <ctype.h>
+#include <netcdf.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nc_complex_version.h"
+
+// NOLINTBEGIN(bugprone-assignment-in-if-condition)
+#define CHECK(func) \
+ do { \
+ int res; \
+ if ((res = (func))) { \
+ return res; \
+ } \
+ } while (0)
+// NOLINTEND(bugprone-assignment-in-if-condition)
+
+// Vector of ones for get/put_var1 functions
+static const size_t coord_one[NC_MAX_VAR_DIMS] = {1};
+
+static const char* double_complex_struct_name = "_PFNC_DOUBLE_COMPLEX_TYPE";
+static const char* float_complex_struct_name = "_PFNC_FLOAT_COMPLEX_TYPE";
+
+#define COMPLEX_DIM_NAME "_pfnc_complex"
+static const char* complex_dim_name = COMPLEX_DIM_NAME;
+
+static const char* known_dim_names[] = {COMPLEX_DIM_NAME, "complex", "ri"};
+static const size_t num_known_dim_names =
+ sizeof(known_dim_names) / sizeof(known_dim_names[0]);
+
+static const char pfnc_libvers[] = NC_COMPLEX_GIT_VERSION;
+
+const char* pfnc_inq_libvers(void) {
+ return pfnc_libvers;
+}
+
+bool pfnc_var_is_complex(int ncid, int varid) {
+ return pfnc_var_is_complex_type(ncid, varid)
+ || pfnc_var_has_complex_dimension(ncid, varid);
+}
+
+int pfnc_complex_base_type(int ncid, nc_type nc_typeid, nc_type* base_type_id) {
+ if (nc_typeid < NC_MAX_ATOMIC_TYPE) {
+ *base_type_id = nc_typeid;
+ return NC_NOERR;
+ }
+
+ // TODO: This should probably handle vlens too
+
+ return nc_inq_compound_field(
+ ncid, nc_typeid, 0, NULL, NULL, base_type_id, NULL, NULL
+ );
+}
+
+int pfnc_inq_var_complex_base_type(int ncid, int varid, nc_type* nc_typeid) {
+ nc_type var_type_id;
+ CHECK(nc_inq_vartype(ncid, varid, &var_type_id));
+ return pfnc_complex_base_type(ncid, var_type_id, nc_typeid);
+}
+
+/// Return true if a compound type is compatible with a known convention
+bool compound_type_is_compatible(int ncid, nc_type nc_typeid) {
+ // Does the name matching a known convention?
+ char name[NC_MAX_NAME + 1];
+ nc_inq_compound_name(ncid, nc_typeid, name);
+ if (name == double_complex_struct_name) {
+ return true;
+ }
+
+ // Does it have exactly two fields?
+ size_t num_fields;
+ nc_inq_compound_nfields(ncid, nc_typeid, &num_fields);
+ if (num_fields != 2) {
+ return false;
+ }
+
+ // As far as I can tell, all conventions put the real part first and
+ // the imaginary part second. I'm pretty sure all native language
+ // types are also this way round. That means we don't have to worry
+ // about trying both combinations!
+ char real_name[NC_MAX_NAME + 1];
+ size_t real_offset;
+ nc_type real_field_type;
+ int real_rank;
+ nc_inq_compound_field(
+ ncid, nc_typeid, 0, real_name, &real_offset, &real_field_type, &real_rank, NULL
+ );
+
+ // If it's not a floating type, we're not interested
+ if (!(real_field_type == NC_FLOAT || real_field_type == NC_DOUBLE)) {
+ return false;
+ }
+ // Also needs to be scalar
+ if (real_rank != 0) {
+ return false;
+ }
+
+ // Now check names. For now, just check it starts with "r", in any case
+ if (tolower(real_name[0]) != 'r') {
+ return false;
+ }
+
+ char imag_name[NC_MAX_NAME + 1];
+ size_t imag_offset;
+ nc_type imag_field_type;
+ int imag_rank;
+ nc_inq_compound_field(
+ ncid, nc_typeid, 1, imag_name, &imag_offset, &imag_field_type, &imag_rank, NULL
+ );
+
+ // Both component types better match
+ if (imag_field_type != real_field_type) {
+ return false;
+ }
+ if (imag_rank != 0) {
+ return false;
+ }
+ if (tolower(imag_name[0]) != 'i') {
+ return false;
+ }
+
+ return true;
+}
+
+/// Return true if file already has a complex type with the given base type
+bool file_has_complex_struct(int ncid, nc_type* typeidp, nc_type base_type) {
+ // Simplest case, check for our type name
+ int err = nc_inq_typeid(ncid, double_complex_struct_name, typeidp);
+ if (err == NC_NOERR) {
+ return true;
+ }
+
+ int ntypes;
+ err = nc_inq_typeids(ncid, &ntypes, NULL);
+ if (err != NC_NOERR) {
+ return false;
+ }
+
+ bool result = false;
+
+ nc_type* typeids = malloc((size_t)ntypes * sizeof(nc_type));
+ err = nc_inq_typeids(ncid, NULL, typeids);
+ if (err != NC_NOERR) {
+ goto cleanup;
+ }
+
+ for (size_t i = 0; i < (size_t)ntypes; i++) {
+ if (compound_type_is_compatible(ncid, typeids[i])) {
+ nc_type base_type_id;
+ err = pfnc_complex_base_type(ncid, typeids[i], &base_type_id);
+ if (err != NC_NOERR) {
+ goto cleanup;
+ }
+ if (base_type_id == base_type) {
+ *typeidp = typeids[i];
+ result = true;
+ goto cleanup;
+ }
+ }
+ }
+cleanup:
+ free(typeids);
+ return result;
+}
+
+/// Return true if a given dimension matches a known convention
+bool pfnc_is_complex_dim(int ncid, int dim_id) {
+ size_t length;
+ nc_inq_dimlen(ncid, dim_id, &length);
+
+ // Definitely can only be exactly two. Note that we can't catch
+ // unlimited dimensions that only have two records so far.
+ if (length != 2) {
+ return false;
+ }
+
+ // Not sure if this is the best way, but here we are.
+ char name[NC_MAX_NAME + 1];
+ nc_inq_dimname(ncid, dim_id, name);
+
+ const size_t name_length = strlen(name);
+
+ // Check against known names of complex dimensions
+ for (size_t i = 0; i < num_known_dim_names; i++) {
+ if (strncmp(name, known_dim_names[i], name_length) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/// Return true if a variable uses the dimension-convention
+bool pfnc_var_has_complex_dimension(int ncid, int nc_varid) {
+ int num_dims;
+ nc_inq_varndims(ncid, nc_varid, &num_dims);
+
+ int* dim_ids = (int*)malloc((size_t)num_dims * sizeof(int));
+ nc_inq_vardimid(ncid, nc_varid, dim_ids);
+
+ // Now we check if any of the dimensions match one of our known
+ // conventions. Do we need to check all of them, or just the
+ // first/last?
+ for (int i = 0; i < num_dims; i++) {
+ if (pfnc_is_complex_dim(ncid, dim_ids[i])) {
+ free(dim_ids);
+ return true;
+ }
+ }
+
+ free(dim_ids);
+ return false;
+}
+
+/// Return true if a netCDF datatype is a compound type
+bool is_compound_type(int ncid, int type_id) {
+ // There appears to be no API for detecting whether a type ID is a
+ // primitive type, so we have to check ourselves
+ if (type_id <= NC_MAX_ATOMIC_TYPE) {
+ return false;
+ }
+
+ int class_type;
+ nc_inq_user_type(ncid, type_id, NULL, NULL, NULL, NULL, &class_type);
+ return class_type == NC_COMPOUND;
+}
+
+/// Copy an array meant for a complex-dimensioned variable
+size_t* copy_complex_dim_size_t_array(
+ const size_t* old_array, int numdims, size_t complex_dim_value
+) {
+ size_t* new_buffer = NULL;
+
+ if (old_array != NULL) {
+ new_buffer = (size_t*)malloc(sizeof(size_t) * (size_t)numdims);
+
+ size_t last_dim = (size_t)(numdims - 1);
+ for (size_t i = 0; i < last_dim; i++) {
+ new_buffer[i] = old_array[i];
+ }
+
+ new_buffer[last_dim] = complex_dim_value;
+ }
+ return new_buffer;
+}
+
+ptrdiff_t* copy_complex_dim_ptrdiff_t_array(
+ const ptrdiff_t* old_array, int numdims, ptrdiff_t complex_dim_value
+) {
+ ptrdiff_t* new_buffer = NULL;
+
+ if (old_array != NULL) {
+ new_buffer = (ptrdiff_t*)malloc(sizeof(ptrdiff_t) * (size_t)numdims);
+
+ size_t last_dim = (size_t)(numdims - 1);
+ for (size_t i = 0; i < last_dim; i++) {
+ new_buffer[i] = old_array[i];
+ }
+
+ new_buffer[last_dim] = complex_dim_value;
+ }
+ return new_buffer;
+}
+
+bool pfnc_var_is_complex_type(int ncid, int varid) {
+ nc_type var_type_id;
+ if (nc_inq_vartype(ncid, varid, &var_type_id)) {
+ return false;
+ }
+
+ if (is_compound_type(ncid, var_type_id)) {
+ return compound_type_is_compatible(ncid, var_type_id);
+ }
+ return false;
+}
+
+size_t complex_type_size(nc_type base_type) {
+ switch (base_type) {
+ case NC_FLOAT:
+ return sizeof(float_complex);
+ case NC_DOUBLE:
+ return sizeof(double_complex);
+ default:
+ return 0;
+ }
+}
+
+size_t base_type_size(nc_type base_type) {
+ switch (base_type) {
+ case NC_FLOAT:
+ return sizeof(float);
+ case NC_DOUBLE:
+ return sizeof(double);
+ default:
+ return 0;
+ }
+}
+
+int get_or_make_complex_struct(
+ int ncid, nc_type* nc_typeid, nc_type base_type, const char* struct_name
+) {
+ // TODO: Error if not netCDF4
+
+ if (file_has_complex_struct(ncid, nc_typeid, base_type)) {
+ return NC_NOERR;
+ }
+
+ const size_t complex_size = complex_type_size(base_type);
+ if (complex_size == 0) {
+ return NC_EBADTYPE;
+ }
+ const size_t base_size = base_type_size(base_type);
+ if (base_size == 0) {
+ return NC_EBADTYPE;
+ }
+
+ CHECK(nc_def_compound(ncid, complex_size, struct_name, nc_typeid));
+ CHECK(nc_insert_compound(ncid, *nc_typeid, "r", 0, base_type));
+ CHECK(nc_insert_compound(ncid, *nc_typeid, "i", base_size, base_type));
+
+ return NC_NOERR;
+}
+
+int pfnc_get_double_complex_typeid(int ncid, nc_type* nc_typeid) {
+ return get_or_make_complex_struct(
+ ncid, nc_typeid, NC_DOUBLE, double_complex_struct_name
+ );
+}
+
+int pfnc_get_float_complex_typeid(int ncid, nc_type* nc_typeid) {
+ return get_or_make_complex_struct(
+ ncid, nc_typeid, NC_FLOAT, float_complex_struct_name
+ );
+}
+
+int pfnc_get_complex_dim(int ncid, int* nc_dim) {
+ int num_dims;
+ CHECK(nc_inq_ndims(ncid, &num_dims));
+
+ int* dim_ids = (int*)malloc((size_t)num_dims * sizeof(int));
+ int ierr = nc_inq_dimids(ncid, NULL, dim_ids, true);
+ if (ierr != NC_NOERR) {
+ goto cleanup;
+ }
+
+ // Now we check if any of the dimensions match one of our known
+ // conventions. Do we need to check all of them, or just the
+ // first/last?
+ for (int i = 0; i < num_dims; i++) {
+ if (pfnc_is_complex_dim(ncid, dim_ids[i])) {
+ *nc_dim = dim_ids[i];
+ goto cleanup;
+ }
+ }
+
+ ierr = nc_def_dim(ncid, complex_dim_name, 2, nc_dim);
+
+cleanup:
+ free(dim_ids);
+ return ierr;
+}
+
+int pfnc_put_vara_double_complex(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const double_complex* op
+) {
+ return pfnc_put_vars_double_complex(ncid, varid, startp, countp, NULL, op);
+}
+
+int pfnc_get_vara_double_complex(
+ int ncid, int varid, const size_t* startp, const size_t* countp, double_complex* ip
+) {
+ return pfnc_get_vars_double_complex(ncid, varid, startp, countp, NULL, ip);
+}
+
+int pfnc_put_vars_double_complex(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const ptrdiff_t* stridep,
+ const double_complex* op
+) {
+ if (!pfnc_var_is_complex(ncid, varid)) {
+ return NC_EBADTYPE;
+ }
+
+ // TODO: handle converting different float sizes
+
+ // Check if we can get away without fudging count/start sizes
+ if (((startp == NULL) && (countp == NULL) && (stridep == NULL))
+ || !pfnc_var_has_complex_dimension(ncid, varid)) {
+ return nc_put_vars(ncid, varid, startp, countp, stridep, op);
+ }
+
+ // The real variable has a complex dimension, but we're pretending
+ // it doesn't, so now we need start/count arrays of the real size
+
+ int numdims = 0;
+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
+
+ // Copy start/count buffers, appending an extra element for the
+ // complex dimension. This dimension starts at 0 and has 2 elements
+ size_t* start_buffer = copy_complex_dim_size_t_array(startp, numdims, 0);
+ size_t* count_buffer = copy_complex_dim_size_t_array(countp, numdims, 2);
+ ptrdiff_t* stride_buffer = copy_complex_dim_ptrdiff_t_array(stridep, numdims, 1);
+
+ const int ierr =
+ nc_put_vars(ncid, varid, start_buffer, count_buffer, stride_buffer, op);
+
+ if (start_buffer != NULL) {
+ free(start_buffer);
+ }
+ if (count_buffer != NULL) {
+ free(count_buffer);
+ }
+ if (stride_buffer != NULL) {
+ free(stride_buffer);
+ }
+ return ierr;
+}
+
+int pfnc_get_vars_double_complex(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const ptrdiff_t* stridep,
+ double_complex* ip
+) {
+ if (!pfnc_var_is_complex(ncid, varid)) {
+ return NC_EBADTYPE;
+ }
+
+ // TODO: handle converting different float sizes
+
+ // Check if we can get away without fudging count/start sizes
+ if (((startp == NULL) && (countp == NULL) && (stridep == NULL))
+ || !pfnc_var_has_complex_dimension(ncid, varid)) {
+ return nc_get_vars(ncid, varid, startp, countp, stridep, ip);
+ }
+
+ // The real variable has a complex dimension, but we're pretending
+ // it doesn't, so now we need start/count arrays of the real size
+
+ int numdims = 0;
+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
+
+ // Copy start/count buffers, appending an extra element for the
+ // complex dimension. This dimension starts at 0 and has 2 elements
+ size_t* start_buffer = copy_complex_dim_size_t_array(startp, numdims, 0);
+ size_t* count_buffer = copy_complex_dim_size_t_array(countp, numdims, 2);
+ ptrdiff_t* stride_buffer = copy_complex_dim_ptrdiff_t_array(stridep, numdims, 1);
+
+ const int ierr =
+ nc_get_vars(ncid, varid, start_buffer, count_buffer, stride_buffer, ip);
+
+ if (start_buffer != NULL) {
+ free(start_buffer);
+ }
+ if (count_buffer != NULL) {
+ free(count_buffer);
+ }
+ if (stride_buffer != NULL) {
+ free(stride_buffer);
+ }
+ return ierr;
+}
+
+int pfnc_put_var1_double_complex(
+ int ncid, int varid, const size_t* indexp, const double_complex* data
+) {
+ return pfnc_put_vara_double_complex(ncid, varid, indexp, coord_one, data);
+}
+
+int pfnc_get_var1_double_complex(
+ int ncid, int varid, const size_t* indexp, double_complex* data
+) {
+ return pfnc_get_vara_double_complex(ncid, varid, indexp, coord_one, data);
+}
+
+int pfnc_put_vara_float_complex(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const float_complex* op
+) {
+ return pfnc_put_vars_float_complex(ncid, varid, startp, countp, NULL, op);
+}
+
+int pfnc_get_vara_float_complex(
+ int ncid, int varid, const size_t* startp, const size_t* countp, float_complex* ip
+) {
+ return pfnc_get_vars_float_complex(ncid, varid, startp, countp, NULL, ip);
+}
+
+int pfnc_put_vars_float_complex(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const ptrdiff_t* stridep,
+ const float_complex* op
+) {
+ if (!pfnc_var_is_complex(ncid, varid)) {
+ return NC_EBADTYPE;
+ }
+
+ // TODO: handle converting different float sizes
+
+ // Check if we can get away without fudging count/start sizes
+ if (((startp == NULL) && (countp == NULL) && (stridep == NULL))
+ || !pfnc_var_has_complex_dimension(ncid, varid)) {
+ return nc_put_vars(ncid, varid, startp, countp, stridep, op);
+ }
+
+ // The real variable has a complex dimension, but we're pretending
+ // it doesn't, so now we need start/count arrays of the real size
+
+ int numdims = 0;
+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
+
+ // Copy start/count buffers, appending an extra element for the
+ // complex dimension. This dimension starts at 0 and has 2 elements
+ size_t* start_buffer = copy_complex_dim_size_t_array(startp, numdims, 0);
+ size_t* count_buffer = copy_complex_dim_size_t_array(countp, numdims, 2);
+ ptrdiff_t* stride_buffer = copy_complex_dim_ptrdiff_t_array(stridep, numdims, 1);
+
+ const int ierr =
+ nc_put_vars(ncid, varid, start_buffer, count_buffer, stride_buffer, op);
+
+ if (start_buffer != NULL) {
+ free(start_buffer);
+ }
+ if (count_buffer != NULL) {
+ free(count_buffer);
+ }
+ if (stride_buffer != NULL) {
+ free(stride_buffer);
+ }
+ return ierr;
+}
+
+int pfnc_get_vars_float_complex(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const ptrdiff_t* stridep,
+ float_complex* ip
+) {
+ if (!pfnc_var_is_complex(ncid, varid)) {
+ return NC_EBADTYPE;
+ }
+
+ // TODO: handle converting different float sizes
+
+ // Check if we can get away without fudging count/start sizes
+ if (((startp == NULL) && (countp == NULL) && (stridep == NULL))
+ || !pfnc_var_has_complex_dimension(ncid, varid)) {
+ return nc_get_vars(ncid, varid, startp, countp, stridep, ip);
+ }
+
+ // The real variable has a complex dimension, but we're pretending
+ // it doesn't, so now we need start/count arrays of the real size
+
+ int numdims = 0;
+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
+
+ // Copy start/count buffers, appending an extra element for the
+ // complex dimension. This dimension starts at 0 and has 2 elements
+ size_t* start_buffer = copy_complex_dim_size_t_array(startp, numdims, 0);
+ size_t* count_buffer = copy_complex_dim_size_t_array(countp, numdims, 2);
+ ptrdiff_t* stride_buffer = copy_complex_dim_ptrdiff_t_array(stridep, numdims, 1);
+
+ const int ierr =
+ nc_get_vars(ncid, varid, start_buffer, count_buffer, stride_buffer, ip);
+
+ if (start_buffer != NULL) {
+ free(start_buffer);
+ }
+ if (count_buffer != NULL) {
+ free(count_buffer);
+ }
+ if (stride_buffer != NULL) {
+ free(stride_buffer);
+ }
+ return ierr;
+}
+
+int pfnc_put_var1_float_complex(
+ int ncid, int varid, const size_t* indexp, const float_complex* data
+) {
+ return pfnc_put_vara_float_complex(ncid, varid, indexp, coord_one, data);
+}
+
+int pfnc_get_var1_float_complex(
+ int ncid, int varid, const size_t* indexp, float_complex* data
+) {
+ return pfnc_get_vara_float_complex(ncid, varid, indexp, coord_one, data);
+}
+
+int pfnc_def_var(
+ int ncid,
+ const char* name,
+ nc_type xtype,
+ int ndims,
+ const int* dimidsp,
+ int* varidp
+) {
+ // If it's not a complex number, we don't need to do anything
+ if (!(xtype == PFNC_DOUBLE_COMPLEX || xtype == PFNC_DOUBLE_COMPLEX_DIM
+ || xtype == PFNC_FLOAT_COMPLEX || xtype == PFNC_FLOAT_COMPLEX_DIM)) {
+ return nc_def_var(ncid, name, xtype, ndims, dimidsp, varidp);
+ }
+
+ const bool base_is_double =
+ (xtype == PFNC_DOUBLE_COMPLEX || xtype == PFNC_DOUBLE_COMPLEX_DIM);
+
+ // Check the format used by this file. If it's some variation on the
+ // classic model, then we have to use a complex dimension. Also,
+ // NcZarr, for some reason doesn't support compound types (yet?).
+ // I _think_ DAP supports compound types
+ int format = 0;
+ int mode = 0;
+ CHECK(nc_inq_format_extended(ncid, &format, &mode));
+
+ if ((format == NC_FORMAT_CLASSIC || format == NC_FORMAT_NETCDF4_CLASSIC)
+ || (mode == NC_FORMATX_NCZARR)) {
+ xtype = base_is_double ? PFNC_DOUBLE_COMPLEX_DIM : PFNC_FLOAT_COMPLEX_DIM;
+ }
+
+ if (xtype == PFNC_DOUBLE_COMPLEX_DIM || xtype == PFNC_FLOAT_COMPLEX_DIM) {
+ // Using a complex dimension. We need to get the complex dimension
+ // used in this file and append it to the list of dimensions
+ // passed in by the user
+
+ int complex_dim = 0;
+ CHECK(pfnc_get_complex_dim(ncid, &complex_dim));
+
+ int new_dims = ndims + 1;
+ int* dim_ids_buffer = (int*)malloc((size_t)new_dims * sizeof(int));
+ for (size_t i = 0; i < (size_t)ndims; i++) {
+ dim_ids_buffer[i] = dimidsp[i];
+ }
+ dim_ids_buffer[ndims] = complex_dim;
+
+ const nc_type base_type = base_is_double ? NC_DOUBLE : NC_FLOAT;
+
+ const int ierr =
+ nc_def_var(ncid, name, base_type, new_dims, dim_ids_buffer, varidp);
+ free(dim_ids_buffer);
+ return ierr;
+ }
+
+ // Using a complex type. We need to get the complex type used in
+ // this file and pass that as `xtype`
+ nc_type complex_type = 0;
+ if (base_is_double) {
+ CHECK(pfnc_get_double_complex_typeid(ncid, &complex_type));
+ } else {
+ CHECK(pfnc_get_float_complex_typeid(ncid, &complex_type));
+ }
+
+ return nc_def_var(ncid, name, complex_type, ndims, dimidsp, varidp);
+}
+
+int pfnc_inq_var(
+ int ncid,
+ int varid,
+ char* name,
+ nc_type* xtypep,
+ int* ndimsp,
+ int* dimidsp,
+ int* nattsp
+) {
+ if (!pfnc_var_has_complex_dimension(ncid, varid)) {
+ return nc_inq_var(ncid, varid, name, xtypep, ndimsp, dimidsp, nattsp);
+ }
+
+ // Tricky bit: if variable has complex dimension, and user used
+ // pfnc_inq_varndims, then dimidsp is one smaller than netCDF thinks
+ // it should be. So we'll have to allocate our own array of the
+ // correct size and copy out of that.
+
+ // This buffer will point to either the user's array, or our own one
+ int* buffer = dimidsp;
+ int numdims = 0;
+
+ if (dimidsp != NULL) {
+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
+ buffer = (int*)malloc(sizeof(int) * (size_t)numdims);
+ }
+
+ int ierr = nc_inq_var(ncid, varid, name, xtypep, &numdims, buffer, nattsp);
+
+ if (ierr != NC_NOERR) {
+ goto cleanup;
+ }
+
+ if (dimidsp != NULL) {
+ if (numdims <= 0) {
+ // This should never happen
+ goto cleanup;
+ }
+ const size_t other_dims = (size_t)(numdims - 1);
+ for (size_t i = 0; i < other_dims; i++) {
+ dimidsp[i] = buffer[i];
+ }
+ }
+
+ if (ndimsp != NULL) {
+ *ndimsp = numdims - 1;
+ }
+
+cleanup:
+ free(buffer);
+ return ierr;
+}
+
+int pfnc_def_var_chunking(int ncid, int varid, int storage, const size_t* chunksizesp) {
+ if (chunksizesp == NULL || !pfnc_var_has_complex_dimension(ncid, varid)) {
+ return nc_def_var_chunking(ncid, varid, storage, chunksizesp);
+ }
+
+ // The real variable has a complex dimension, but we're pretending
+ // it doesn't, so now we need start/count arrays of the real size
+
+ int numdims = 0;
+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
+
+ // Copy chunksize buffer, appending an extra element for the
+ // complex dimension
+ size_t* chunk_buffer = copy_complex_dim_size_t_array(chunksizesp, numdims, 2);
+
+ const int ierr = nc_def_var_chunking(ncid, varid, storage, chunk_buffer);
+ free(chunk_buffer);
+ return ierr;
+}
+
+int pfnc_inq_var_chunking(int ncid, int varid, int* storagep, size_t* chunksizesp) {
+ if (chunksizesp == NULL || !pfnc_var_has_complex_dimension(ncid, varid)) {
+ return nc_inq_var_chunking(ncid, varid, storagep, chunksizesp);
+ }
+
+ int numdims = 0;
+
+ CHECK(nc_inq_varndims(ncid, varid, &numdims));
+
+ // Copy chunksize buffer, appending an extra element for the
+ // complex dimension
+ size_t* chunk_buffer = copy_complex_dim_size_t_array(chunksizesp, numdims, 2);
+
+ const int ierr = nc_inq_var_chunking(ncid, varid, storagep, chunk_buffer);
+
+ if (ierr != NC_NOERR) {
+ goto cleanup;
+ }
+
+ const size_t other_dims = (size_t)(numdims - 1);
+ for (size_t i = 0; i < other_dims; i++) {
+ chunksizesp[i] = chunk_buffer[i];
+ }
+
+cleanup:
+ free(chunk_buffer);
+ return ierr;
+}
+
+int pfnc_get_vara(
+ int ncid, int varid, const size_t* startp, const size_t* countp, void* ip
+) {
+ if (pfnc_var_is_complex(ncid, varid)) {
+ nc_type base_type;
+ CHECK(pfnc_inq_var_complex_base_type(ncid, varid, &base_type));
+ switch (base_type) {
+ case NC_DOUBLE:
+ return pfnc_get_vara_double_complex(ncid, varid, startp, countp, ip);
+ case NC_FLOAT:
+ return pfnc_get_vara_float_complex(ncid, varid, startp, countp, ip);
+ default:
+ return NC_EBADTYPE;
+ }
+ }
+
+ return nc_get_vara(ncid, varid, startp, countp, ip);
+}
+
+int pfnc_put_vara(
+ int ncid, int varid, const size_t* startp, const size_t* countp, const void* op
+) {
+ if (pfnc_var_is_complex(ncid, varid)) {
+ nc_type base_type;
+ CHECK(pfnc_inq_var_complex_base_type(ncid, varid, &base_type));
+ switch (base_type) {
+ case NC_DOUBLE:
+ return pfnc_put_vara_double_complex(ncid, varid, startp, countp, op);
+ case NC_FLOAT:
+ return pfnc_put_vara_float_complex(ncid, varid, startp, countp, op);
+ default:
+ return NC_EBADTYPE;
+ }
+ }
+ return nc_put_vara(ncid, varid, startp, countp, op);
+}
+
+int pfnc_put_vars(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const ptrdiff_t* stridep,
+ const void* op
+) {
+ if (pfnc_var_is_complex(ncid, varid)) {
+ nc_type base_type;
+ CHECK(pfnc_inq_var_complex_base_type(ncid, varid, &base_type));
+ switch (base_type) {
+ case NC_DOUBLE:
+ return pfnc_put_vars_double_complex(
+ ncid, varid, startp, countp, stridep, op
+ );
+ case NC_FLOAT:
+ return pfnc_put_vars_float_complex(
+ ncid, varid, startp, countp, stridep, op
+ );
+ default:
+ return NC_EBADTYPE;
+ }
+ }
+ return nc_put_vars(ncid, varid, startp, countp, stridep, op);
+}
+
+int pfnc_get_vars(
+ int ncid,
+ int varid,
+ const size_t* startp,
+ const size_t* countp,
+ const ptrdiff_t* stridep,
+ void* ip
+) {
+ if (pfnc_var_is_complex(ncid, varid)) {
+ nc_type base_type;
+ CHECK(pfnc_inq_var_complex_base_type(ncid, varid, &base_type));
+ switch (base_type) {
+ case NC_DOUBLE:
+ return pfnc_get_vars_double_complex(
+ ncid, varid, startp, countp, stridep, ip
+ );
+ case NC_FLOAT:
+ return pfnc_get_vars_float_complex(
+ ncid, varid, startp, countp, stridep, ip
+ );
+ default:
+ return NC_EBADTYPE;
+ }
+ }
+ return nc_get_vars(ncid, varid, startp, countp, stridep, ip);
+}
=====================================
pyproject.toml
=====================================
@@ -3,7 +3,7 @@ requires = [
"Cython>=0.29",
"oldest-supported-numpy ; python_version < '3.9'",
"numpy>=2.0.0rc1 ; python_version >= '3.9'",
- "setuptools>=61",
+ "setuptools>=61", "setuptools_scm[toml]>=3.4"
]
build-backend = "setuptools.build_meta"
@@ -75,3 +75,5 @@ where = ["src"]
[tool.pytest.ini_options]
pythonpath = ["test"]
+
+[tool.setuptools_scm]
=====================================
src/netCDF4/_netCDF4.pyx
=====================================
@@ -1,4 +1,4 @@
-"""Version 1.7.0
+"""Version 1.7.1
-------------
# Introduction
@@ -1272,7 +1272,7 @@ import sys
import functools
from typing import Union
-__version__ = "1.7.0"
+__version__ = "1.7.1"
# Initialize numpy
import posixpath
View it on GitLab: https://salsa.debian.org/debian-gis-team/netcdf4-python/-/compare/3a9efecb01a1e14543eb6bb302bc7c7b36c6fea3...d36b49bbe31a798d6ed6cfee3d24a3e53f5ce8d1
--
This project does not include diff previews in email notifications.
View it on GitLab: https://salsa.debian.org/debian-gis-team/netcdf4-python/-/compare/3a9efecb01a1e14543eb6bb302bc7c7b36c6fea3...d36b49bbe31a798d6ed6cfee3d24a3e53f5ce8d1
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20240618/13d12e45/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list