musica 0.12.1__cp310-cp310-win_arm64.whl → 0.13.0__cp310-cp310-win_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of musica might be problematic. Click here for more details.
- musica/CMakeLists.txt +4 -0
- musica/_musica.cp310-win_amd64.pyd +0 -0
- musica/_version.py +1 -1
- musica/binding_common.cpp +6 -9
- musica/binding_common.hpp +17 -1
- musica/examples/__init__.py +1 -1
- musica/examples/carma_aluminum.py +1 -0
- musica/examples/carma_sulfate.py +1 -0
- musica/examples/sulfate_box_model.py +2 -2
- musica/examples/ts1_latin_hypercube.py +1 -1
- musica/grid.cpp +206 -0
- musica/grid.py +98 -0
- musica/grid_map.cpp +117 -0
- musica/grid_map.py +167 -0
- musica/mechanism_configuration/__init__.py +18 -1
- musica/mechanism_configuration/ancillary.py +6 -0
- musica/mechanism_configuration/arrhenius.py +111 -269
- musica/mechanism_configuration/branched.py +116 -275
- musica/mechanism_configuration/emission.py +63 -52
- musica/mechanism_configuration/first_order_loss.py +73 -157
- musica/mechanism_configuration/mechanism.py +93 -0
- musica/mechanism_configuration/phase.py +44 -33
- musica/mechanism_configuration/phase_species.py +58 -0
- musica/mechanism_configuration/photolysis.py +77 -67
- musica/mechanism_configuration/reaction_component.py +54 -0
- musica/mechanism_configuration/reactions.py +17 -58
- musica/mechanism_configuration/species.py +45 -71
- musica/mechanism_configuration/surface.py +78 -74
- musica/mechanism_configuration/taylor_series.py +136 -0
- musica/mechanism_configuration/ternary_chemical_activation.py +138 -330
- musica/mechanism_configuration/troe.py +138 -330
- musica/mechanism_configuration/tunneling.py +105 -229
- musica/mechanism_configuration/user_defined.py +79 -68
- musica/mechanism_configuration.cpp +54 -162
- musica/musica.cpp +2 -5
- musica/profile.cpp +294 -0
- musica/profile.py +93 -0
- musica/profile_map.cpp +117 -0
- musica/profile_map.py +167 -0
- musica/test/examples/v1/full_configuration/full_configuration.json +91 -233
- musica/test/examples/v1/full_configuration/full_configuration.yaml +191 -290
- musica/test/integration/test_carma_aluminum.py +2 -1
- musica/test/integration/test_carma_sulfate.py +2 -1
- musica/test/integration/test_chapman.py +2 -2
- musica/test/integration/test_sulfate_box_model.py +1 -1
- musica/test/integration/test_tuvx.py +72 -15
- musica/test/unit/test_grid.py +137 -0
- musica/test/unit/test_grid_map.py +126 -0
- musica/test/unit/test_parser.py +10 -10
- musica/test/unit/test_profile.py +169 -0
- musica/test/unit/test_profile_map.py +137 -0
- musica/test/unit/test_serializer.py +17 -16
- musica/test/unit/test_state.py +338 -0
- musica/test/unit/test_util_full_mechanism.py +78 -298
- musica/tools/prepare_build_environment_linux.sh +7 -25
- musica/tuvx.cpp +94 -15
- musica/tuvx.py +92 -22
- musica/types.py +28 -17
- {musica-0.12.1.dist-info → musica-0.13.0.dist-info}/METADATA +15 -14
- musica-0.13.0.dist-info/RECORD +80 -0
- {musica-0.12.1.dist-info → musica-0.13.0.dist-info}/WHEEL +1 -1
- musica/mechanism_configuration/aqueous_equilibrium.py +0 -274
- musica/mechanism_configuration/condensed_phase_arrhenius.py +0 -309
- musica/mechanism_configuration/condensed_phase_photolysis.py +0 -88
- musica/mechanism_configuration/henrys_law.py +0 -44
- musica/mechanism_configuration/mechanism_configuration.py +0 -234
- musica/mechanism_configuration/simpol_phase_transfer.py +0 -217
- musica/mechanism_configuration/wet_deposition.py +0 -52
- musica-0.12.1.dist-info/RECORD +0 -69
- {musica-0.12.1.dist-info → musica-0.13.0.dist-info}/entry_points.txt +0 -0
- {musica-0.12.1.dist-info → musica-0.13.0.dist-info}/licenses/AUTHORS.md +0 -0
- {musica-0.12.1.dist-info → musica-0.13.0.dist-info}/licenses/LICENSE +0 -0
musica/CMakeLists.txt
CHANGED
|
Binary file
|
musica/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
version = "0.
|
|
1
|
+
version = "0.13.0"
|
musica/binding_common.cpp
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
#include "binding_common.hpp"
|
|
2
2
|
|
|
3
|
-
void bind_cuda(py::module_ &);
|
|
4
|
-
void bind_musica(py::module_ &);
|
|
5
|
-
#ifdef MUSICA_USE_TUVX
|
|
6
|
-
void bind_tuvx(py::module_ &);
|
|
7
|
-
#endif
|
|
8
|
-
#ifdef MUSICA_USE_CARMA
|
|
9
|
-
void bind_carma(py::module_ &);
|
|
10
|
-
#endif
|
|
11
|
-
|
|
12
3
|
void bind_mechanism_configuration(py::module_ &);
|
|
13
4
|
|
|
14
5
|
void bind_all(py::module_ &m)
|
|
15
6
|
{
|
|
7
|
+
py::bind_vector<std::vector<double>>(m, "VectorDouble");
|
|
8
|
+
|
|
16
9
|
py::module_ core = m.def_submodule("_core", "Wrapper classes for MUSICA C library structs and functions");
|
|
17
10
|
py::module_ mechanism_configuration = m.def_submodule(
|
|
18
11
|
"_mechanism_configuration", "Wrapper classes for Mechanism Configuration library structs and functions");
|
|
@@ -22,6 +15,10 @@ void bind_all(py::module_ &m)
|
|
|
22
15
|
bind_cuda(core);
|
|
23
16
|
bind_musica(core);
|
|
24
17
|
#ifdef MUSICA_USE_TUVX
|
|
18
|
+
bind_tuvx_grid(tuvx);
|
|
19
|
+
bind_tuvx_grid_map(tuvx);
|
|
20
|
+
bind_tuvx_profile(tuvx);
|
|
21
|
+
bind_tuvx_profile_map(tuvx);
|
|
25
22
|
bind_tuvx(tuvx);
|
|
26
23
|
#endif
|
|
27
24
|
|
musica/binding_common.hpp
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include <pybind11/pybind11.h>
|
|
4
|
+
#include <pybind11/stl.h>
|
|
5
|
+
#include <pybind11/stl_bind.h>
|
|
4
6
|
|
|
5
7
|
namespace py = pybind11;
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
PYBIND11_MAKE_OPAQUE(std::vector<double>)
|
|
10
|
+
|
|
11
|
+
void bind_all(py::module_ &m);
|
|
12
|
+
void bind_cuda(py::module_ &);
|
|
13
|
+
void bind_musica(py::module_ &);
|
|
14
|
+
#ifdef MUSICA_USE_TUVX
|
|
15
|
+
void bind_tuvx(py::module_ &);
|
|
16
|
+
void bind_tuvx_grid(py::module_ &);
|
|
17
|
+
void bind_tuvx_grid_map(py::module_ &);
|
|
18
|
+
void bind_tuvx_profile(py::module_ &);
|
|
19
|
+
void bind_tuvx_profile_map(py::module_ &);
|
|
20
|
+
#endif
|
|
21
|
+
#ifdef MUSICA_USE_CARMA
|
|
22
|
+
void bind_carma(py::module_ &);
|
|
23
|
+
#endif
|
musica/examples/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
from .examples import Examples
|
|
1
|
+
from .examples import Examples
|
musica/examples/carma_sulfate.py
CHANGED
|
@@ -272,8 +272,8 @@ def run_box_model():
|
|
|
272
272
|
state.set_concentrations(initial_concentrations)
|
|
273
273
|
state.set_user_defined_rate_parameters({"EMIS.SO2": np.full(NUMBER_OF_GRID_CELLS, 1.0e-8, dtype=np.float64)})
|
|
274
274
|
|
|
275
|
-
# Run the simulation for
|
|
276
|
-
time_hours =
|
|
275
|
+
# Run the simulation for 30 minutes with a timestep of 30 seconds
|
|
276
|
+
time_hours = 0.5
|
|
277
277
|
time_seconds = time_hours * np.float64(3600.0)
|
|
278
278
|
dt = np.float64(30.0)
|
|
279
279
|
num_steps = int(time_seconds / dt)
|
musica/grid.cpp
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// Copyright (C) 2023-2025 National Center for Atmospheric Research
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
//
|
|
4
|
+
// This file defines the Python bindings for the TUV-x Grid class in the musica library.
|
|
5
|
+
#include "binding_common.hpp"
|
|
6
|
+
|
|
7
|
+
#include <musica/tuvx/grid.hpp>
|
|
8
|
+
|
|
9
|
+
#include <pybind11/numpy.h>
|
|
10
|
+
#include <pybind11/pybind11.h>
|
|
11
|
+
#include <pybind11/stl.h>
|
|
12
|
+
|
|
13
|
+
namespace py = pybind11;
|
|
14
|
+
|
|
15
|
+
void bind_tuvx_grid(py::module_ &grid)
|
|
16
|
+
{
|
|
17
|
+
py::class_<musica::Grid>(grid, "_Grid")
|
|
18
|
+
.def(py::init(
|
|
19
|
+
[](const py::kwargs &kwargs)
|
|
20
|
+
{
|
|
21
|
+
if (!kwargs.contains("name"))
|
|
22
|
+
throw py::value_error("Missing required argument: name");
|
|
23
|
+
if (!kwargs.contains("units"))
|
|
24
|
+
throw py::value_error("Missing required argument: units");
|
|
25
|
+
if (!kwargs.contains("num_sections"))
|
|
26
|
+
throw py::value_error("Missing required argument: num_sections");
|
|
27
|
+
if (!py::isinstance<py::str>(kwargs["name"]))
|
|
28
|
+
throw py::value_error("Argument 'name' must be a string");
|
|
29
|
+
if (!py::isinstance<py::str>(kwargs["units"]))
|
|
30
|
+
throw py::value_error("Argument 'units' must be a string");
|
|
31
|
+
if (!py::isinstance<py::int_>(kwargs["num_sections"]))
|
|
32
|
+
throw py::value_error("Argument 'num_sections' must be an integer");
|
|
33
|
+
if (kwargs["num_sections"].cast<std::size_t>() <= 0)
|
|
34
|
+
throw py::value_error("Argument 'num_sections' must be greater than 0");
|
|
35
|
+
std::string name = kwargs["name"].cast<std::string>();
|
|
36
|
+
std::string units = kwargs["units"].cast<std::string>();
|
|
37
|
+
std::size_t num_sections = kwargs["num_sections"].cast<std::size_t>();
|
|
38
|
+
musica::Error error;
|
|
39
|
+
auto grid_instance = new musica::Grid(name.c_str(), units.c_str(), num_sections, &error);
|
|
40
|
+
if (!musica::IsSuccess(error))
|
|
41
|
+
{
|
|
42
|
+
std::string message = "Error creating grid: " + std::string(error.message_.value_);
|
|
43
|
+
musica::DeleteError(&error);
|
|
44
|
+
throw py::value_error(message);
|
|
45
|
+
}
|
|
46
|
+
return grid_instance;
|
|
47
|
+
}))
|
|
48
|
+
.def("__del__", [](musica::Grid &grid) {})
|
|
49
|
+
.def_property_readonly(
|
|
50
|
+
"name",
|
|
51
|
+
[](musica::Grid &self)
|
|
52
|
+
{
|
|
53
|
+
musica::Error error;
|
|
54
|
+
std::string name = self.GetName(&error);
|
|
55
|
+
if (!musica::IsSuccess(error))
|
|
56
|
+
{
|
|
57
|
+
std::string message = "Error getting grid name: " + std::string(error.message_.value_);
|
|
58
|
+
musica::DeleteError(&error);
|
|
59
|
+
throw py::value_error(message);
|
|
60
|
+
}
|
|
61
|
+
musica::DeleteError(&error);
|
|
62
|
+
return name;
|
|
63
|
+
},
|
|
64
|
+
"The name of the grid")
|
|
65
|
+
.def_property_readonly(
|
|
66
|
+
"units",
|
|
67
|
+
[](musica::Grid &self)
|
|
68
|
+
{
|
|
69
|
+
musica::Error error;
|
|
70
|
+
std::string units = self.GetUnits(&error);
|
|
71
|
+
if (!musica::IsSuccess(error))
|
|
72
|
+
{
|
|
73
|
+
std::string message = "Error getting grid units: " + std::string(error.message_.value_);
|
|
74
|
+
musica::DeleteError(&error);
|
|
75
|
+
throw py::value_error(message);
|
|
76
|
+
}
|
|
77
|
+
musica::DeleteError(&error);
|
|
78
|
+
return units;
|
|
79
|
+
},
|
|
80
|
+
"The units of the grid")
|
|
81
|
+
.def_property_readonly(
|
|
82
|
+
"num_sections",
|
|
83
|
+
[](musica::Grid &self)
|
|
84
|
+
{
|
|
85
|
+
musica::Error error;
|
|
86
|
+
std::size_t num_sections = self.GetNumberOfSections(&error);
|
|
87
|
+
if (!musica::IsSuccess(error))
|
|
88
|
+
{
|
|
89
|
+
std::string message = "Error getting number of grid sections: " + std::string(error.message_.value_);
|
|
90
|
+
musica::DeleteError(&error);
|
|
91
|
+
throw py::value_error(message);
|
|
92
|
+
}
|
|
93
|
+
return num_sections;
|
|
94
|
+
},
|
|
95
|
+
"The number of sections in the grid")
|
|
96
|
+
.def_property(
|
|
97
|
+
"edges",
|
|
98
|
+
// Getter - converts C++ array to numpy array
|
|
99
|
+
[](musica::Grid &self)
|
|
100
|
+
{
|
|
101
|
+
musica::Error error;
|
|
102
|
+
size_t size = self.GetNumberOfSections(&error) + 1;
|
|
103
|
+
if (!musica::IsSuccess(error))
|
|
104
|
+
{
|
|
105
|
+
std::string message = "Error getting number of grid sections: " + std::string(error.message_.value_);
|
|
106
|
+
musica::DeleteError(&error);
|
|
107
|
+
throw py::value_error(message);
|
|
108
|
+
}
|
|
109
|
+
auto result = py::array_t<double>(size);
|
|
110
|
+
py::buffer_info buf = result.request();
|
|
111
|
+
double *ptr = static_cast<double *>(buf.ptr);
|
|
112
|
+
self.GetEdges(ptr, size, &error);
|
|
113
|
+
if (!musica::IsSuccess(error))
|
|
114
|
+
{
|
|
115
|
+
std::string message = "Error getting grid edges: " + std::string(error.message_.value_);
|
|
116
|
+
musica::DeleteError(&error);
|
|
117
|
+
throw py::value_error(message);
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
},
|
|
121
|
+
// Setter - converts numpy array to C++ array
|
|
122
|
+
[](musica::Grid &self, py::array_t<double, py::array::c_style | py::array::forcecast> array)
|
|
123
|
+
{
|
|
124
|
+
py::buffer_info buf = array.request();
|
|
125
|
+
musica::Error error;
|
|
126
|
+
size_t size = self.GetNumberOfSections(&error) + 1;
|
|
127
|
+
if (!musica::IsSuccess(error))
|
|
128
|
+
{
|
|
129
|
+
std::string message = "Error getting number of grid sections: " + std::string(error.message_.value_);
|
|
130
|
+
musica::DeleteError(&error);
|
|
131
|
+
throw py::value_error(message);
|
|
132
|
+
}
|
|
133
|
+
if (buf.ndim != 1)
|
|
134
|
+
{
|
|
135
|
+
throw py::value_error("Number of dimensions must be one");
|
|
136
|
+
}
|
|
137
|
+
if (static_cast<size_t>(buf.size) != size)
|
|
138
|
+
{
|
|
139
|
+
throw py::value_error("Array size must be num_sections + 1");
|
|
140
|
+
}
|
|
141
|
+
double *ptr = static_cast<double *>(buf.ptr);
|
|
142
|
+
self.SetEdges(ptr, size, &error);
|
|
143
|
+
if (!musica::IsSuccess(error))
|
|
144
|
+
{
|
|
145
|
+
std::string message = "Error setting grid edges: " + std::string(error.message_.value_);
|
|
146
|
+
musica::DeleteError(&error);
|
|
147
|
+
throw py::value_error(message);
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"Grid edges array of length num_sections + 1")
|
|
151
|
+
.def_property(
|
|
152
|
+
"midpoints",
|
|
153
|
+
// Getter - converts C++ array to numpy array
|
|
154
|
+
[](musica::Grid &self)
|
|
155
|
+
{
|
|
156
|
+
musica::Error error;
|
|
157
|
+
size_t size = self.GetNumberOfSections(&error);
|
|
158
|
+
if (!musica::IsSuccess(error))
|
|
159
|
+
{
|
|
160
|
+
std::string message = "Error getting number of grid sections: " + std::string(error.message_.value_);
|
|
161
|
+
musica::DeleteError(&error);
|
|
162
|
+
throw py::value_error(message);
|
|
163
|
+
}
|
|
164
|
+
auto result = py::array_t<double>(size);
|
|
165
|
+
py::buffer_info buf = result.request();
|
|
166
|
+
double *ptr = static_cast<double *>(buf.ptr);
|
|
167
|
+
self.GetMidpoints(ptr, size, &error);
|
|
168
|
+
if (!musica::IsSuccess(error))
|
|
169
|
+
{
|
|
170
|
+
std::string message = "Error getting grid midpoints: " + std::string(error.message_.value_);
|
|
171
|
+
musica::DeleteError(&error);
|
|
172
|
+
throw py::value_error(message);
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
},
|
|
176
|
+
// Setter - converts numpy array to C++ array
|
|
177
|
+
[](musica::Grid &self, py::array_t<double, py::array::c_style | py::array::forcecast> array)
|
|
178
|
+
{
|
|
179
|
+
py::buffer_info buf = array.request();
|
|
180
|
+
musica::Error error;
|
|
181
|
+
size_t size = self.GetNumberOfSections(&error);
|
|
182
|
+
if (!musica::IsSuccess(error))
|
|
183
|
+
{
|
|
184
|
+
std::string message = "Error getting number of grid sections: " + std::string(error.message_.value_);
|
|
185
|
+
musica::DeleteError(&error);
|
|
186
|
+
throw py::value_error(message);
|
|
187
|
+
}
|
|
188
|
+
if (buf.ndim != 1)
|
|
189
|
+
{
|
|
190
|
+
throw py::value_error("Number of dimensions must be one");
|
|
191
|
+
}
|
|
192
|
+
if (static_cast<size_t>(buf.size) != size)
|
|
193
|
+
{
|
|
194
|
+
throw py::value_error("Array size must be num_sections");
|
|
195
|
+
}
|
|
196
|
+
double *ptr = static_cast<double *>(buf.ptr);
|
|
197
|
+
self.SetMidpoints(ptr, size, &error);
|
|
198
|
+
if (!musica::IsSuccess(error))
|
|
199
|
+
{
|
|
200
|
+
std::string message = "Error setting grid midpoints: " + std::string(error.message_.value_);
|
|
201
|
+
musica::DeleteError(&error);
|
|
202
|
+
throw py::value_error(message);
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
"Grid midpoints array of length num_sections");
|
|
206
|
+
}
|
musica/grid.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Copyright (C) 2023-2025 National Center for Atmospheric Research
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
"""
|
|
4
|
+
TUV-x Grid class.
|
|
5
|
+
|
|
6
|
+
This module provides a class for defining grids on which TUV-x profiles exist.
|
|
7
|
+
Typically, this would be used to define vertical and wavelength grids.
|
|
8
|
+
|
|
9
|
+
Note: TUV-x is only available on macOS and Linux platforms.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Dict, Optional
|
|
13
|
+
import numpy as np
|
|
14
|
+
from . import backend
|
|
15
|
+
|
|
16
|
+
_backend = backend.get_backend()
|
|
17
|
+
|
|
18
|
+
Grid = _backend._tuvx._Grid if backend.tuvx_available() else None
|
|
19
|
+
|
|
20
|
+
if backend.tuvx_available():
|
|
21
|
+
original_init = Grid.__init__
|
|
22
|
+
|
|
23
|
+
def __init__(self, *, name: str, units: str,
|
|
24
|
+
num_sections: Optional[int] = None,
|
|
25
|
+
edges: Optional[np.ndarray] = None,
|
|
26
|
+
midpoints: Optional[np.ndarray] = None,
|
|
27
|
+
**kwargs):
|
|
28
|
+
"""Initialize a Grid instance. Note that at least one of num_sections, edges, or midpoints
|
|
29
|
+
must be provided.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
name: Name of the grid
|
|
33
|
+
units: Units of the grid values
|
|
34
|
+
num_sections: Optional number of grid sections
|
|
35
|
+
edges: Optional array of edge values (length num_sections + 1)
|
|
36
|
+
midpoints: Optional array of midpoint values (length num_sections)
|
|
37
|
+
**kwargs: Additional arguments passed to the C++ constructor
|
|
38
|
+
"""
|
|
39
|
+
if (num_sections is None and edges is None and midpoints is None):
|
|
40
|
+
raise ValueError("At least one of num_sections, edges, or midpoints must be provided.")
|
|
41
|
+
if (num_sections is None):
|
|
42
|
+
if (edges is not None):
|
|
43
|
+
num_sections = len(edges) - 1
|
|
44
|
+
elif (midpoints is not None):
|
|
45
|
+
num_sections = len(midpoints)
|
|
46
|
+
# Call the original C++ constructor correctly
|
|
47
|
+
original_init(self, name=name, units=units, num_sections=num_sections, **kwargs)
|
|
48
|
+
|
|
49
|
+
# Set edges or midpoints if provided
|
|
50
|
+
if edges is not None:
|
|
51
|
+
self.edges = edges
|
|
52
|
+
if midpoints is not None:
|
|
53
|
+
self.midpoints = midpoints
|
|
54
|
+
|
|
55
|
+
Grid.__init__ = __init__
|
|
56
|
+
|
|
57
|
+
def __str__(self):
|
|
58
|
+
"""User-friendly string representation."""
|
|
59
|
+
return f"Grid(name={self.name}, units={self.units}, num_sections={self.num_sections})"
|
|
60
|
+
|
|
61
|
+
Grid.__str__ = __str__
|
|
62
|
+
|
|
63
|
+
def __repr__(self):
|
|
64
|
+
"""Detailed string representation for debugging."""
|
|
65
|
+
return (f"Grid(name={self.name}, units={self.units}, num_sections={self.num_sections}, "
|
|
66
|
+
f"edges={self.edges}, midpoints={self.midpoints})")
|
|
67
|
+
|
|
68
|
+
Grid.__repr__ = __repr__
|
|
69
|
+
|
|
70
|
+
def __len__(self):
|
|
71
|
+
"""Return the number of sections in the grid."""
|
|
72
|
+
return self.num_sections
|
|
73
|
+
|
|
74
|
+
Grid.__len__ = __len__
|
|
75
|
+
|
|
76
|
+
def __eq__(self, other):
|
|
77
|
+
"""Check equality with another Grid instance."""
|
|
78
|
+
if not isinstance(other, Grid):
|
|
79
|
+
return NotImplemented
|
|
80
|
+
return (self.name == other.name and
|
|
81
|
+
self.units == other.units and
|
|
82
|
+
self.num_sections == other.num_sections and
|
|
83
|
+
np.array_equal(self.edges, other.edges) and
|
|
84
|
+
np.array_equal(self.midpoints, other.midpoints))
|
|
85
|
+
|
|
86
|
+
Grid.__eq__ = __eq__
|
|
87
|
+
|
|
88
|
+
def __bool__(self):
|
|
89
|
+
"""Return True if the grid has sections."""
|
|
90
|
+
return self.num_sections > 0
|
|
91
|
+
|
|
92
|
+
Grid.__bool__ = __bool__
|
|
93
|
+
|
|
94
|
+
def __contains__(self, value):
|
|
95
|
+
"""Check if a value is within the grid bounds."""
|
|
96
|
+
return self.edges[0] <= value <= self.edges[-1]
|
|
97
|
+
|
|
98
|
+
Grid.__contains__ = __contains__
|
musica/grid_map.cpp
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Copyright (C) 2023-2025 National Center for Atmospheric Research
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#include "binding_common.hpp"
|
|
4
|
+
|
|
5
|
+
#include <musica/tuvx/grid.hpp>
|
|
6
|
+
#include <musica/tuvx/grid_map.hpp>
|
|
7
|
+
|
|
8
|
+
#include <pybind11/pybind11.h>
|
|
9
|
+
|
|
10
|
+
namespace py = pybind11;
|
|
11
|
+
|
|
12
|
+
void bind_tuvx_grid_map(py::module& m)
|
|
13
|
+
{
|
|
14
|
+
py::class_<musica::GridMap>(m, "_GridMap")
|
|
15
|
+
.def(py::init<>(
|
|
16
|
+
[]()
|
|
17
|
+
{
|
|
18
|
+
musica::Error error;
|
|
19
|
+
auto grid_map_instance = new musica::GridMap(&error);
|
|
20
|
+
if (!musica::IsSuccess(error))
|
|
21
|
+
{
|
|
22
|
+
std::string message = "Error creating GridMap: " + std::string(error.message_.value_);
|
|
23
|
+
musica::DeleteError(&error);
|
|
24
|
+
throw py::value_error(message);
|
|
25
|
+
}
|
|
26
|
+
return grid_map_instance;
|
|
27
|
+
}))
|
|
28
|
+
.def(
|
|
29
|
+
"add_grid",
|
|
30
|
+
[](musica::GridMap& self, musica::Grid* grid)
|
|
31
|
+
{
|
|
32
|
+
musica::Error error;
|
|
33
|
+
self.AddGrid(grid, &error);
|
|
34
|
+
if (error.code_ != 0)
|
|
35
|
+
{
|
|
36
|
+
std::string message = "Error adding grid: " + std::string(error.message_.value_);
|
|
37
|
+
musica::DeleteError(&error);
|
|
38
|
+
throw std::runtime_error(message);
|
|
39
|
+
}
|
|
40
|
+
DeleteError(&error);
|
|
41
|
+
})
|
|
42
|
+
.def(
|
|
43
|
+
"get_grid",
|
|
44
|
+
[](musica::GridMap& self, const std::string& name, const std::string& units)
|
|
45
|
+
{
|
|
46
|
+
musica::Error error;
|
|
47
|
+
musica::Grid* grid = self.GetGrid(name.c_str(), units.c_str(), &error);
|
|
48
|
+
if (!musica::IsSuccess(error))
|
|
49
|
+
{
|
|
50
|
+
std::string message = std::string(error.message_.value_);
|
|
51
|
+
musica::DeleteError(&error);
|
|
52
|
+
throw py::value_error("Error getting grid: " + message);
|
|
53
|
+
}
|
|
54
|
+
musica::DeleteError(&error);
|
|
55
|
+
return grid;
|
|
56
|
+
},
|
|
57
|
+
py::return_value_policy::reference)
|
|
58
|
+
.def(
|
|
59
|
+
"get_grid_by_index",
|
|
60
|
+
[](musica::GridMap& self, std::size_t index)
|
|
61
|
+
{
|
|
62
|
+
musica::Error error;
|
|
63
|
+
musica::Grid* grid = self.GetGridByIndex(index, &error);
|
|
64
|
+
if (!musica::IsSuccess(error))
|
|
65
|
+
{
|
|
66
|
+
std::string message = std::string(error.message_.value_);
|
|
67
|
+
musica::DeleteError(&error);
|
|
68
|
+
throw py::value_error("Error getting grid by index: " + message);
|
|
69
|
+
}
|
|
70
|
+
musica::DeleteError(&error);
|
|
71
|
+
return grid;
|
|
72
|
+
},
|
|
73
|
+
py::return_value_policy::reference)
|
|
74
|
+
.def(
|
|
75
|
+
"remove_grid",
|
|
76
|
+
[](musica::GridMap& self, const std::string& name, const std::string& units)
|
|
77
|
+
{
|
|
78
|
+
musica::Error error;
|
|
79
|
+
self.RemoveGrid(name.c_str(), units.c_str(), &error);
|
|
80
|
+
if (!musica::IsSuccess(error))
|
|
81
|
+
{
|
|
82
|
+
std::string message = std::string(error.message_.value_);
|
|
83
|
+
musica::DeleteError(&error);
|
|
84
|
+
throw py::value_error("Error removing grid: " + message);
|
|
85
|
+
}
|
|
86
|
+
musica::DeleteError(&error);
|
|
87
|
+
})
|
|
88
|
+
.def(
|
|
89
|
+
"remove_grid_by_index",
|
|
90
|
+
[](musica::GridMap& self, std::size_t index)
|
|
91
|
+
{
|
|
92
|
+
musica::Error error;
|
|
93
|
+
self.RemoveGridByIndex(index, &error);
|
|
94
|
+
if (!musica::IsSuccess(error))
|
|
95
|
+
{
|
|
96
|
+
std::string message = std::string(error.message_.value_);
|
|
97
|
+
musica::DeleteError(&error);
|
|
98
|
+
throw py::value_error("Error removing grid by index: " + message);
|
|
99
|
+
}
|
|
100
|
+
musica::DeleteError(&error);
|
|
101
|
+
})
|
|
102
|
+
.def(
|
|
103
|
+
"get_number_of_grids",
|
|
104
|
+
[](musica::GridMap& self)
|
|
105
|
+
{
|
|
106
|
+
musica::Error error;
|
|
107
|
+
std::size_t num_grids = self.GetNumberOfGrids(&error);
|
|
108
|
+
if (!musica::IsSuccess(error))
|
|
109
|
+
{
|
|
110
|
+
std::string message = std::string(error.message_.value_);
|
|
111
|
+
musica::DeleteError(&error);
|
|
112
|
+
throw py::value_error("Error getting number of grids: " + message);
|
|
113
|
+
}
|
|
114
|
+
musica::DeleteError(&error);
|
|
115
|
+
return num_grids;
|
|
116
|
+
});
|
|
117
|
+
}
|