musica 0.12.1__cp312-cp312-manylinux_2_28_x86_64.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 (107) hide show
  1. musica/CMakeLists.txt +68 -0
  2. musica/__init__.py +11 -0
  3. musica/_musica.cpython-312-x86_64-linux-gnu.so +0 -0
  4. musica/_musica_gpu.cpython-312-x86_64-linux-gnu.so +0 -0
  5. musica/_version.py +1 -0
  6. musica/backend.py +41 -0
  7. musica/binding_common.cpp +33 -0
  8. musica/binding_common.hpp +7 -0
  9. musica/carma.cpp +911 -0
  10. musica/carma.py +1729 -0
  11. musica/constants.py +3 -0
  12. musica/cpu_binding.cpp +11 -0
  13. musica/cuda.cpp +12 -0
  14. musica/cuda.py +13 -0
  15. musica/examples/__init__.py +1 -0
  16. musica/examples/carma_aluminum.py +123 -0
  17. musica/examples/carma_sulfate.py +245 -0
  18. musica/examples/examples.py +165 -0
  19. musica/examples/sulfate_box_model.py +439 -0
  20. musica/examples/ts1_latin_hypercube.py +245 -0
  21. musica/gpu_binding.cpp +11 -0
  22. musica/main.py +89 -0
  23. musica/mechanism_configuration/__init__.py +1 -0
  24. musica/mechanism_configuration/aqueous_equilibrium.py +274 -0
  25. musica/mechanism_configuration/arrhenius.py +307 -0
  26. musica/mechanism_configuration/branched.py +299 -0
  27. musica/mechanism_configuration/condensed_phase_arrhenius.py +309 -0
  28. musica/mechanism_configuration/condensed_phase_photolysis.py +88 -0
  29. musica/mechanism_configuration/emission.py +71 -0
  30. musica/mechanism_configuration/first_order_loss.py +174 -0
  31. musica/mechanism_configuration/henrys_law.py +44 -0
  32. musica/mechanism_configuration/mechanism_configuration.py +234 -0
  33. musica/mechanism_configuration/phase.py +47 -0
  34. musica/mechanism_configuration/photolysis.py +88 -0
  35. musica/mechanism_configuration/reactions.py +73 -0
  36. musica/mechanism_configuration/simpol_phase_transfer.py +217 -0
  37. musica/mechanism_configuration/species.py +91 -0
  38. musica/mechanism_configuration/surface.py +94 -0
  39. musica/mechanism_configuration/ternary_chemical_activation.py +352 -0
  40. musica/mechanism_configuration/troe.py +352 -0
  41. musica/mechanism_configuration/tunneling.py +250 -0
  42. musica/mechanism_configuration/user_defined.py +88 -0
  43. musica/mechanism_configuration/utils.py +10 -0
  44. musica/mechanism_configuration/wet_deposition.py +52 -0
  45. musica/mechanism_configuration.cpp +607 -0
  46. musica/musica.cpp +201 -0
  47. musica/test/examples/v1/full_configuration/full_configuration.json +466 -0
  48. musica/test/examples/v1/full_configuration/full_configuration.yaml +295 -0
  49. musica/test/integration/test_analytical.py +324 -0
  50. musica/test/integration/test_carma.py +227 -0
  51. musica/test/integration/test_carma_aluminum.py +11 -0
  52. musica/test/integration/test_carma_sulfate.py +16 -0
  53. musica/test/integration/test_chapman.py +139 -0
  54. musica/test/integration/test_sulfate_box_model.py +34 -0
  55. musica/test/integration/test_tuvx.py +62 -0
  56. musica/test/unit/test_parser.py +64 -0
  57. musica/test/unit/test_serializer.py +69 -0
  58. musica/test/unit/test_util_full_mechanism.py +698 -0
  59. musica/tools/prepare_build_environment_linux.sh +50 -0
  60. musica/tools/prepare_build_environment_macos.sh +1 -0
  61. musica/tools/repair_wheel_gpu.sh +40 -0
  62. musica/tuvx.cpp +93 -0
  63. musica/tuvx.py +199 -0
  64. musica/types.py +404 -0
  65. musica-0.12.1.dist-info/METADATA +472 -0
  66. musica-0.12.1.dist-info/RECORD +110 -0
  67. musica-0.12.1.dist-info/WHEEL +5 -0
  68. musica-0.12.1.dist-info/entry_points.txt +3 -0
  69. musica-0.12.1.dist-info/licenses/AUTHORS.md +59 -0
  70. musica-0.12.1.dist-info/licenses/LICENSE +201 -0
  71. musica.libs/libaec-91f0ca0f.so.0.0.8 +0 -0
  72. musica.libs/libblas-fe34f726.so.3.8.0 +0 -0
  73. musica.libs/libbrotlicommon-6ce2a53c.so.1.0.6 +0 -0
  74. musica.libs/libbrotlidec-811d1be3.so.1.0.6 +0 -0
  75. musica.libs/libcom_err-448cbf49.so.2.1 +0 -0
  76. musica.libs/libcrypt-52aca757.so.1.1.0 +0 -0
  77. musica.libs/libcrypto-bdaed0ea.so.1.1.1k +0 -0
  78. musica.libs/libcurl-c96f124b.so.4.5.0 +0 -0
  79. musica.libs/libdf-2848fafa.so.0.0.0 +0 -0
  80. musica.libs/libgfortran-8f1e9814.so.5.0.0 +0 -0
  81. musica.libs/libgssapi_krb5-323bbd21.so.2.2 +0 -0
  82. musica.libs/libhdf5-b5e70f84.so.103.1.0 +0 -0
  83. musica.libs/libhdf5_hl-0b60eabd.so.100.1.2 +0 -0
  84. musica.libs/libidn2-2f4a5893.so.0.3.6 +0 -0
  85. musica.libs/libjpeg-e87d2658.so.62.2.0 +0 -0
  86. musica.libs/libk5crypto-9a74ff38.so.3.1 +0 -0
  87. musica.libs/libkeyutils-2777d33d.so.1.6 +0 -0
  88. musica.libs/libkrb5-a55300e8.so.3.3 +0 -0
  89. musica.libs/libkrb5support-e6594cfc.so.0.1 +0 -0
  90. musica.libs/liblapack-31d7d384.so.3.8.0 +0 -0
  91. musica.libs/liblber-2-d20824ef.4.so.2.10.9 +0 -0
  92. musica.libs/libldap-2-cea2a960.4.so.2.10.9 +0 -0
  93. musica.libs/libmfhdf-38b34047.so.0.0.0 +0 -0
  94. musica.libs/libmvec-2-583a17db.28.so +0 -0
  95. musica.libs/libnetcdf-3915b03a.so.15.0.1 +0 -0
  96. musica.libs/libnetcdff-8cc3a1b0.so.7.0.0 +0 -0
  97. musica.libs/libnghttp2-39367a22.so.14.17.0 +0 -0
  98. musica.libs/libpcre2-8-516f4c9d.so.0.7.1 +0 -0
  99. musica.libs/libpsl-99becdd3.so.5.3.1 +0 -0
  100. musica.libs/libquadmath-828275a7.so.0.0.0 +0 -0
  101. musica.libs/libsasl2-7de4d792.so.3.0.0 +0 -0
  102. musica.libs/libselinux-d0805dcb.so.1 +0 -0
  103. musica.libs/libssh-c11d285b.so.4.8.7 +0 -0
  104. musica.libs/libssl-60250281.so.1.1.1k +0 -0
  105. musica.libs/libsz-0deeef7d.so.2.0.1 +0 -0
  106. musica.libs/libtirpc-cff449a4.so.3.0.0 +0 -0
  107. musica.libs/libunistring-05abdd40.so.2.1.0 +0 -0
@@ -0,0 +1,50 @@
1
+ #!/bin/bash
2
+ set -e
3
+ set -x
4
+
5
+ target_arch="$(uname -m)"
6
+ echo "Detected target_arch: $target_arch"
7
+
8
+ # Set package manager based on architecture
9
+ # x86_64 and aarch64 use manylinux_2_28 (AlmaLinux 8) with dnf
10
+ # i686 uses manylinux2014 (CentOS 7) with yum
11
+ if [ "$target_arch" = "i686" ]; then
12
+ PKG_MGR="yum"
13
+
14
+ # CentOS 7 is EOL, so we need to use vault.centos.org for i686 builds
15
+ # Replace the repo files to point to vault.centos.org
16
+ sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*.repo
17
+ sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*.repo
18
+ else
19
+ PKG_MGR="dnf"
20
+ fi
21
+
22
+ echo "Using package manager: $PKG_MGR"
23
+
24
+ $PKG_MGR -y update
25
+
26
+ if [ "$target_arch" = "x86_64" ] || [ "$target_arch" = "aarch64" ]; then
27
+ # For manylinux_2_28 (AlmaLinux 8), epel-release is required to get netcdf, for some reason
28
+ $PKG_MGR install -y epel-release
29
+ $PKG_MGR install -y netcdf-devel netcdf-fortran-devel
30
+ fi
31
+
32
+ $PKG_MGR install -y tree wget zip lapack-devel
33
+
34
+ if [ "$target_arch" = "x86_64" ]; then
35
+ # Install CUDA 12.8 for x86_64 on AlmaLinux 8 (manylinux_2_28) - supports GCC 14
36
+ dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo
37
+ dnf install --setopt=obsoletes=0 -y \
38
+ cuda-nvcc-12-8 \
39
+ cuda-cudart-devel-12-8 \
40
+ libcurand-devel-12-8 \
41
+ libcublas-devel-12-8
42
+ ln -sf cuda-12.8 /usr/local/cuda
43
+
44
+ # Verify CUDA installation
45
+ echo "=== CUDA Installation Verification ==="
46
+ /usr/local/cuda/bin/nvcc --version
47
+
48
+ # list the installed CUDA packages
49
+ # tree -L 4 /usr/local/cuda
50
+ fi
@@ -0,0 +1 @@
1
+ brew install netcdf netcdf-fortran lapack
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+
4
+ auditwheel repair --exclude libcublas --exclude libcublasLt --exclude libcudart -w "$2" "$1"
5
+
6
+ for whl in "$2"/*.whl; do
7
+ tmpdir=$(mktemp -d)
8
+ unzip -q "$whl" -d "$tmpdir"
9
+ tree "$tmpdir"
10
+
11
+ so_files=("$tmpdir"/musica/_musica_gpu*.so)
12
+
13
+ if [[ -f "${so_files[0]}" ]]; then
14
+ so_path="${so_files[0]}"
15
+ ls $so_path
16
+ echo "Before patchelf:"
17
+ readelf -d $so_path
18
+ # Use patchelf to fix the rpath and library dependencies
19
+ patchelf --remove-rpath $so_path
20
+ patchelf --set-rpath "\$ORIGIN:\$ORIGIN/../nvidia/cublas/lib:\$ORIGIN/../nvidia/cuda_runtime/lib" --force-rpath $so_path
21
+ # these may need to be periodically updated
22
+ patchelf --replace-needed libcudart-b5a066d7.so.12.2.140 libcudart.so.12 $so_path
23
+ patchelf --replace-needed libcublas-e779a79d.so.12.2.5.6 libcublas.so.12 $so_path
24
+ patchelf --replace-needed libcublasLt-fbfbc8a1.so.12.2.5.6 libcublasLt.so.12 $so_path
25
+ # Remove bundled CUDA libraries
26
+ rm -f "$tmpdir"/musica.libs/libcudart-*.so*
27
+ rm -f "$tmpdir"/musica.libs/libcublas-*.so*
28
+ rm -f "$tmpdir"/musica.libs/libcublasLt-*.so*
29
+ echo "After patchelf:"
30
+ readelf -d $so_path
31
+ else
32
+ echo "No GPU .so file found, skipping patchelf steps"
33
+ fi
34
+
35
+ # Repack the wheel with correct structure
36
+ (cd "$tmpdir" && zip -qr "${whl%.whl}.patched.whl" .)
37
+ rm -rf "$tmpdir"
38
+ # Replace the original wheel with the patched one
39
+ mv "${whl%.whl}.patched.whl" "$whl"
40
+ done
musica/tuvx.cpp ADDED
@@ -0,0 +1,93 @@
1
+ #include "binding_common.hpp"
2
+
3
+ #include <musica/tuvx/tuvx.hpp>
4
+
5
+ #include <pybind11/numpy.h>
6
+ #include <pybind11/pybind11.h>
7
+ #include <pybind11/stl.h>
8
+
9
+ namespace py = pybind11;
10
+
11
+ void bind_tuvx(py::module_& tuvx)
12
+ {
13
+ tuvx.def("_get_tuvx_version", []() { return musica::TUVX::GetVersion(); }, "Get the version of the TUV-x instance");
14
+
15
+ tuvx.def(
16
+ "_create_tuvx",
17
+ [](const char* config_path)
18
+ {
19
+ try
20
+ {
21
+ auto tuvx_instance = new musica::TUVX();
22
+ tuvx_instance->CreateFromConfigOnly(config_path);
23
+ return reinterpret_cast<std::uintptr_t>(tuvx_instance);
24
+ }
25
+ catch (const std::exception& e)
26
+ {
27
+ throw py::value_error(
28
+ "Error creating TUV-x instance from config file: " + std::string(config_path) + " - " + e.what());
29
+ }
30
+ },
31
+ "Create a TUV-x instance from a JSON configuration file");
32
+
33
+ tuvx.def(
34
+ "_delete_tuvx",
35
+ [](std::uintptr_t tuvx_ptr)
36
+ {
37
+ musica::TUVX* tuvx_instance = reinterpret_cast<musica::TUVX*>(tuvx_ptr);
38
+ delete tuvx_instance;
39
+ },
40
+ "Delete a TUV-x instance");
41
+
42
+ tuvx.def(
43
+ "_run_tuvx",
44
+ [](std::uintptr_t tuvx_ptr)
45
+ {
46
+ musica::TUVX* tuvx_instance = reinterpret_cast<musica::TUVX*>(tuvx_ptr);
47
+
48
+ // Get dimensions
49
+ int n_photolysis = tuvx_instance->GetPhotolysisRateCount();
50
+
51
+ int n_heating = tuvx_instance->GetHeatingRateCount();
52
+
53
+ int n_layers = tuvx_instance->GetNumberOfLayers();
54
+
55
+ int n_sza_steps = tuvx_instance->GetNumberOfSzaSteps();
56
+
57
+ // Allocate output arrays (3D: sza_step, layer, reaction/heating_type)
58
+ std::vector<double> photolysis_rates(n_sza_steps * n_layers * n_photolysis);
59
+ std::vector<double> heating_rates(n_sza_steps * n_layers * n_heating);
60
+
61
+ // Run TUV-x (everything comes from the JSON config)
62
+ tuvx_instance->RunFromConfig(photolysis_rates.data(), heating_rates.data());
63
+
64
+ // Return as numpy arrays with shape (n_sza_steps, n_layers, n_reactions/n_heating)
65
+ py::array_t<double> py_photolysis =
66
+ py::array_t<double>({ n_sza_steps, n_layers, n_photolysis }, photolysis_rates.data());
67
+ py::array_t<double> py_heating = py::array_t<double>({ n_sza_steps, n_layers, n_heating }, heating_rates.data());
68
+
69
+ return py::make_tuple(py_photolysis, py_heating);
70
+ },
71
+ "Run TUV-x (all parameters come from JSON config)",
72
+ py::arg("tuvx_instance"));
73
+
74
+ tuvx.def(
75
+ "_get_photolysis_rate_names",
76
+ [](std::uintptr_t tuvx_ptr)
77
+ {
78
+ musica::TUVX* tuvx_instance = reinterpret_cast<musica::TUVX*>(tuvx_ptr);
79
+
80
+ return tuvx_instance->GetPhotolysisRateNames();
81
+ },
82
+ "Get photolysis rate names");
83
+
84
+ tuvx.def(
85
+ "_get_heating_rate_names",
86
+ [](std::uintptr_t tuvx_ptr)
87
+ {
88
+ musica::TUVX* tuvx_instance = reinterpret_cast<musica::TUVX*>(tuvx_ptr);
89
+
90
+ return tuvx_instance->GetHeatingRateNames();
91
+ },
92
+ "Get heating rate names");
93
+ }
musica/tuvx.py ADDED
@@ -0,0 +1,199 @@
1
+ """
2
+ TUV-x photolysis calculator Python interface.
3
+
4
+ This module provides a simplified Python interface to the TUV-x photolysis calculator.
5
+ It allows users to create a TUV-x instance from a JSON configuration file and
6
+ calculate photolysis rates and heating rates.
7
+
8
+ Note: TUV-x is only available on macOS and Linux platforms.
9
+ """
10
+
11
+ import os
12
+ import json
13
+ import tempfile
14
+ from typing import Dict, Tuple, List
15
+ import numpy as np
16
+ from . import backend
17
+
18
+ _backend = backend.get_backend()
19
+
20
+ version = _backend._tuvx._get_tuvx_version() if backend.tuvx_available() else None
21
+
22
+
23
+ class TUVX:
24
+ """
25
+ A Python interface to the TUV-x photolysis calculator.
26
+
27
+ This class provides a simplified interface that only requires a JSON configuration
28
+ file to set up and run TUV-x calculations. All parameters (solar zenith angle,
29
+ earth-sun distance, atmospheric profiles, etc.) are specified in the JSON config.
30
+ """
31
+
32
+ def __init__(self, config_path: str):
33
+ """
34
+ Initialize a TUV-x instance from a configuration file.
35
+
36
+ Args:
37
+ config_path: Path to the JSON configuration file
38
+
39
+ Raises:
40
+ FileNotFoundError: If the configuration file doesn't exist
41
+ ValueError: If TUV-x initialization fails or TUVX is not available
42
+ """
43
+ if not backend.tuvx_available():
44
+ raise ValueError("TUV-x backend is not available on windows.")
45
+
46
+ if not os.path.exists(config_path):
47
+ raise FileNotFoundError(
48
+ f"Configuration file not found: {config_path}")
49
+
50
+ self._tuvx_instance = _backend._tuvx._create_tuvx(config_path)
51
+ self._config_path = config_path
52
+
53
+ # Cache the names for efficiency
54
+ self._photolysis_names = None
55
+ self._heating_names = None
56
+
57
+ def __del__(self):
58
+ """Clean up the TUV-x instance."""
59
+ if hasattr(self, '_tuvx_instance') and self._tuvx_instance is not None:
60
+ _backend._tuvx._delete_tuvx(self._tuvx_instance)
61
+
62
+ @property
63
+ def photolysis_rate_names(self) -> List[str]:
64
+ """
65
+ Get the names of photolysis rates.
66
+
67
+ Returns:
68
+ List of photolysis rate names
69
+ """
70
+ if self._photolysis_names is None:
71
+ self._photolysis_names = _backend._tuvx._get_photolysis_rate_names(
72
+ self._tuvx_instance)
73
+ return self._photolysis_names
74
+
75
+ @property
76
+ def heating_rate_names(self) -> List[str]:
77
+ """
78
+ Get the names of heating rates.
79
+
80
+ Returns:
81
+ List of heating rate names
82
+ """
83
+ if self._heating_names is None:
84
+ self._heating_names = _backend._tuvx._get_heating_rate_names(
85
+ self._tuvx_instance)
86
+ return self._heating_names
87
+
88
+ def run(self) -> Tuple[np.ndarray, np.ndarray]:
89
+ """
90
+ Run the TUV-x photolysis calculator.
91
+
92
+ All parameters (solar zenith angle, Earth-Sun distance, atmospheric profiles,
93
+ etc.) are read from the JSON configuration file.
94
+
95
+ Returns:
96
+ Tuple of (photolysis_rate_constants, heating_rates) as numpy arrays
97
+ - photolysis_rate_constants: Shape (n_layers, n_reactions) [s^-1]
98
+ - heating_rates: Shape (n_layers, n_heating_rates) [K s^-1]
99
+ """
100
+ photolysis_rates, heating_rates = _backend._tuvx._run_tuvx(
101
+ self._tuvx_instance)
102
+
103
+ return photolysis_rates, heating_rates
104
+
105
+ def get_photolysis_rate_constant(
106
+ self,
107
+ reaction_name: str,
108
+ photolysis_rates: np.ndarray
109
+ ) -> np.ndarray:
110
+ """
111
+ Extract photolysis rate constants for a specific reaction.
112
+
113
+ Args:
114
+ reaction_name: Name of the photolysis reaction
115
+ photolysis_rates: Output from run() method
116
+
117
+ Returns:
118
+ 1D array of photolysis rate constants for all layers [s^-1]
119
+
120
+ Raises:
121
+ KeyError: If reaction_name is not found
122
+ """
123
+ names = self.photolysis_rate_names
124
+ if reaction_name not in names:
125
+ raise KeyError(
126
+ f"Reaction '{reaction_name}' not found. "
127
+ f"Available reactions: {names}"
128
+ )
129
+
130
+ reaction_index = names.index(reaction_name)
131
+ return photolysis_rates[:, reaction_index]
132
+
133
+ def get_heating_rate(
134
+ self,
135
+ rate_name: str,
136
+ heating_rates: np.ndarray
137
+ ) -> np.ndarray:
138
+ """
139
+ Extract heating rates for a specific rate type.
140
+
141
+ Args:
142
+ rate_name: Name of the heating rate
143
+ heating_rates: Output from run() method
144
+
145
+ Returns:
146
+ 1D array of heating rates for all layers [K s^-1]
147
+
148
+ Raises:
149
+ KeyError: If rate_name is not found
150
+ """
151
+ names = self.heating_rate_names
152
+ if rate_name not in names:
153
+ raise KeyError(
154
+ f"Heating rate '{rate_name}' not found. "
155
+ f"Available rates: {names}"
156
+ )
157
+
158
+ rate_index = names.index(rate_name)
159
+ return heating_rates[:, rate_index]
160
+
161
+ @staticmethod
162
+ def create_config_from_dict(config_dict: Dict) -> 'TUVX':
163
+ """
164
+ Create a TUVX instance from a configuration dictionary.
165
+
166
+ Args:
167
+ config_dict: Configuration dictionary
168
+
169
+ Returns:
170
+ TUVX instance initialized with the configuration
171
+
172
+ Raises:
173
+ ValueError: If TUV-x backend is not available
174
+ FileNotFoundError: If required data files are not found
175
+ """
176
+ with tempfile.NamedTemporaryFile(
177
+ mode='w', suffix='.json', delete=True) as temp_file:
178
+ json.dump(config_dict, temp_file, indent=2)
179
+ temp_file.flush() # Ensure all data is written to disk
180
+ return TUVX(temp_file.name)
181
+
182
+ @staticmethod
183
+ def create_config_from_json_string(json_string: str) -> 'TUVX':
184
+ """
185
+ Create a TUVX instance from a JSON configuration string.
186
+
187
+ Args:
188
+ json_string: JSON configuration as string
189
+
190
+ Returns:
191
+ TUVX instance initialized with the configuration
192
+
193
+ Raises:
194
+ json.JSONDecodeError: If json_string is not valid JSON
195
+ ValueError: If TUV-x backend is not available
196
+ FileNotFoundError: If required data files are not found
197
+ """
198
+ config_dict = json.loads(json_string)
199
+ return TUVX.create_config_from_dict(config_dict)