musica 0.12.2__cp310-cp310-win32.whl → 0.13.0__cp310-cp310-win32.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.

Files changed (63) hide show
  1. musica/CMakeLists.txt +4 -0
  2. musica/_musica.cp310-win32.pyd +0 -0
  3. musica/_version.py +1 -1
  4. musica/binding_common.cpp +6 -9
  5. musica/binding_common.hpp +17 -1
  6. musica/grid.cpp +206 -0
  7. musica/grid.py +98 -0
  8. musica/grid_map.cpp +117 -0
  9. musica/grid_map.py +167 -0
  10. musica/mechanism_configuration/__init__.py +18 -1
  11. musica/mechanism_configuration/ancillary.py +6 -0
  12. musica/mechanism_configuration/arrhenius.py +111 -269
  13. musica/mechanism_configuration/branched.py +116 -275
  14. musica/mechanism_configuration/emission.py +63 -52
  15. musica/mechanism_configuration/first_order_loss.py +73 -157
  16. musica/mechanism_configuration/mechanism.py +93 -0
  17. musica/mechanism_configuration/phase.py +44 -33
  18. musica/mechanism_configuration/phase_species.py +58 -0
  19. musica/mechanism_configuration/photolysis.py +77 -67
  20. musica/mechanism_configuration/reaction_component.py +54 -0
  21. musica/mechanism_configuration/reactions.py +17 -58
  22. musica/mechanism_configuration/species.py +45 -71
  23. musica/mechanism_configuration/surface.py +78 -74
  24. musica/mechanism_configuration/taylor_series.py +136 -0
  25. musica/mechanism_configuration/ternary_chemical_activation.py +138 -330
  26. musica/mechanism_configuration/troe.py +138 -330
  27. musica/mechanism_configuration/tunneling.py +105 -229
  28. musica/mechanism_configuration/user_defined.py +79 -68
  29. musica/mechanism_configuration.cpp +54 -162
  30. musica/musica.cpp +2 -5
  31. musica/profile.cpp +294 -0
  32. musica/profile.py +93 -0
  33. musica/profile_map.cpp +117 -0
  34. musica/profile_map.py +167 -0
  35. musica/test/examples/v1/full_configuration/full_configuration.json +91 -233
  36. musica/test/examples/v1/full_configuration/full_configuration.yaml +191 -290
  37. musica/test/integration/test_chapman.py +2 -2
  38. musica/test/integration/test_tuvx.py +72 -15
  39. musica/test/unit/test_grid.py +137 -0
  40. musica/test/unit/test_grid_map.py +126 -0
  41. musica/test/unit/test_parser.py +10 -10
  42. musica/test/unit/test_profile.py +169 -0
  43. musica/test/unit/test_profile_map.py +137 -0
  44. musica/test/unit/test_serializer.py +17 -16
  45. musica/test/unit/test_state.py +17 -4
  46. musica/test/unit/test_util_full_mechanism.py +78 -298
  47. musica/tuvx.cpp +94 -15
  48. musica/tuvx.py +92 -22
  49. musica/types.py +13 -5
  50. {musica-0.12.2.dist-info → musica-0.13.0.dist-info}/METADATA +14 -14
  51. musica-0.13.0.dist-info/RECORD +80 -0
  52. musica/mechanism_configuration/aqueous_equilibrium.py +0 -274
  53. musica/mechanism_configuration/condensed_phase_arrhenius.py +0 -309
  54. musica/mechanism_configuration/condensed_phase_photolysis.py +0 -88
  55. musica/mechanism_configuration/henrys_law.py +0 -44
  56. musica/mechanism_configuration/mechanism_configuration.py +0 -234
  57. musica/mechanism_configuration/simpol_phase_transfer.py +0 -217
  58. musica/mechanism_configuration/wet_deposition.py +0 -52
  59. musica-0.12.2.dist-info/RECORD +0 -70
  60. {musica-0.12.2.dist-info → musica-0.13.0.dist-info}/WHEEL +0 -0
  61. {musica-0.12.2.dist-info → musica-0.13.0.dist-info}/entry_points.txt +0 -0
  62. {musica-0.12.2.dist-info → musica-0.13.0.dist-info}/licenses/AUTHORS.md +0 -0
  63. {musica-0.12.2.dist-info → musica-0.13.0.dist-info}/licenses/LICENSE +0 -0
musica/CMakeLists.txt CHANGED
@@ -18,6 +18,10 @@ set(
18
18
  if (MUSICA_ENABLE_TUVX)
19
19
  list(APPEND MUSICA_PYTHON_SOURCES
20
20
  tuvx.cpp
21
+ grid.cpp
22
+ grid_map.cpp
23
+ profile.cpp
24
+ profile_map.cpp
21
25
  )
22
26
  endif()
23
27
 
Binary file
musica/_version.py CHANGED
@@ -1 +1 @@
1
- version = "0.12.2"
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
- void bind_all(py::module_ &m);
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/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
+ }
musica/grid_map.py ADDED
@@ -0,0 +1,167 @@
1
+ # Copyright (C) 2023-2025 National Center for Atmospheric Research
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """
4
+ TUV-x GridMap class.
5
+
6
+ This module provides a class for managing collections of TUV-x grids.
7
+ The GridMap class allows dictionary-style access to grids using (name, units) tuples as keys.
8
+
9
+ Note: TUV-x is only available on macOS and Linux platforms.
10
+ """
11
+
12
+ from typing import Iterator, Sequence
13
+ from . import backend
14
+ from .grid import Grid
15
+
16
+ _backend = backend.get_backend()
17
+
18
+ GridMap = _backend._tuvx._GridMap if backend.tuvx_available() else None
19
+
20
+ if backend.tuvx_available():
21
+ original_init = GridMap.__init__
22
+
23
+ def __init__(self, **kwargs):
24
+ """Initialize a GridMap instance.
25
+
26
+ Args:
27
+ **kwargs: Additional arguments passed to the C++ constructor
28
+ """
29
+ original_init(self, **kwargs)
30
+
31
+ GridMap.__init__ = __init__
32
+
33
+ def __str__(self):
34
+ """User-friendly string representation."""
35
+ return f"GridMap(num_grids={len(self)})"
36
+
37
+ GridMap.__str__ = __str__
38
+
39
+ def __repr__(self):
40
+ """Detailed string representation for debugging."""
41
+ grid_details = []
42
+ for i in range(len(self)):
43
+ grid = self.get_grid_by_index(i)
44
+ grid_details.append(f"({grid.name}, {grid.units})")
45
+ return f"GridMap(grids={grid_details})"
46
+
47
+ GridMap.__repr__ = __repr__
48
+
49
+ def __len__(self):
50
+ """Return the number of grids in the map."""
51
+ return self.get_number_of_grids()
52
+
53
+ GridMap.__len__ = __len__
54
+
55
+ def __bool__(self):
56
+ """Return True if the map has any grids."""
57
+ return len(self) > 0
58
+
59
+ GridMap.__bool__ = __bool__
60
+
61
+ def __getitem__(self, key) -> Grid:
62
+ """Get a grid using dictionary-style access with (name, units) tuple as key.
63
+
64
+ Args:
65
+ key: A tuple of (grid_name, grid_units)
66
+
67
+ Returns:
68
+ The requested Grid object
69
+
70
+ Raises:
71
+ KeyError: If no grid matches the given name and units
72
+ TypeError: If key is not a tuple of (str, str)
73
+ """
74
+ if not isinstance(key, tuple) or len(key) != 2:
75
+ raise TypeError("Grid access requires a tuple of (name, units)")
76
+ name, units = key
77
+ try:
78
+ return self.get_grid(name, units)
79
+ except Exception as e:
80
+ raise KeyError(f"No grid found with name='{name}' and units='{units}'") from e
81
+
82
+ GridMap.__getitem__ = __getitem__
83
+
84
+ def __setitem__(self, key, grid):
85
+ """Add a grid to the map using dictionary-style access.
86
+
87
+ Args:
88
+ key: A tuple of (grid_name, grid_units)
89
+ grid: The Grid object to add
90
+
91
+ Raises:
92
+ TypeError: If key is not a tuple, or if key components are not strings
93
+ TypeError: If grid is not a Grid object
94
+ ValueError: If grid name/units don't match the key
95
+ """
96
+ if not isinstance(key, tuple) or len(key) != 2:
97
+ raise TypeError("Grid assignment requires a tuple of (name, units)")
98
+ name, units = key
99
+ if not isinstance(name, str):
100
+ raise TypeError("Grid name must be a string")
101
+ if not isinstance(units, str):
102
+ raise TypeError("Grid units must be a string")
103
+ if not isinstance(grid, Grid):
104
+ raise TypeError("Value must be a Grid object")
105
+ if grid.name != name or grid.units != units:
106
+ raise ValueError("Grid name/units must match the key tuple")
107
+ self.add_grid(grid)
108
+
109
+ GridMap.__setitem__ = __setitem__
110
+
111
+ def __iter__(self) -> Iterator:
112
+ """Return an iterator over (name, units) tuples of all grids."""
113
+ for i in range(len(self)):
114
+ grid = self.get_grid_by_index(i)
115
+ yield (grid.name, grid.units)
116
+
117
+ GridMap.__iter__ = __iter__
118
+
119
+ def __contains__(self, key) -> bool:
120
+ """Check if a grid with given name and units exists in the map.
121
+
122
+ Args:
123
+ key: A tuple of (grid_name, grid_units)
124
+
125
+ Returns:
126
+ True if a matching grid exists, False otherwise
127
+ """
128
+ if not isinstance(key, tuple) or len(key) != 2:
129
+ return False
130
+ name, units = key
131
+ try:
132
+ grid = self.get_grid(str(name), str(units))
133
+ return grid is not None
134
+ except (ValueError, KeyError):
135
+ return False
136
+
137
+ GridMap.__contains__ = __contains__
138
+
139
+ def clear(self):
140
+ """Remove all grids from the map."""
141
+ while len(self) > 0:
142
+ self.remove_grid_by_index(0)
143
+
144
+ GridMap.clear = clear
145
+
146
+ def items(self):
147
+ """Return an iterator over (key, grid) pairs, where key is (name, units)."""
148
+ for i in range(len(self)):
149
+ grid = self.get_grid_by_index(i)
150
+ yield ((grid.name, grid.units), grid)
151
+
152
+ GridMap.items = items
153
+
154
+ def keys(self):
155
+ """Return an iterator over grid keys (name, units) tuples."""
156
+ for i in range(len(self)):
157
+ grid = self.get_grid_by_index(i)
158
+ yield (grid.name, grid.units)
159
+
160
+ GridMap.keys = keys
161
+
162
+ def values(self):
163
+ """Return an iterator over Grid objects in the map."""
164
+ for i in range(len(self)):
165
+ yield self.get_grid_by_index(i)
166
+
167
+ GridMap.values = values
@@ -1 +1,18 @@
1
- from .mechanism_configuration import *
1
+ from .arrhenius import Arrhenius
2
+ from .branched import Branched
3
+ from .emission import Emission
4
+ from .first_order_loss import FirstOrderLoss
5
+ from .mechanism import Mechanism
6
+ from .ancillary import Version, Parser, ReactionType
7
+ from .phase import Phase
8
+ from .phase_species import PhaseSpecies
9
+ from .photolysis import Photolysis
10
+ from .reaction_component import ReactionComponent
11
+ from .reactions import Reactions
12
+ from .species import Species
13
+ from .surface import Surface
14
+ from .taylor_series import TaylorSeries
15
+ from .ternary_chemical_activation import TernaryChemicalActivation
16
+ from .troe import Troe
17
+ from .tunneling import Tunneling
18
+ from .user_defined import UserDefined
@@ -0,0 +1,6 @@
1
+ from .. import backend
2
+
3
+ _backend = backend.get_backend()
4
+ Version = _backend._mechanism_configuration._Version
5
+ Parser = _backend._mechanism_configuration._Parser
6
+ ReactionType = _backend._mechanism_configuration._ReactionType