musica 0.11.1.2__cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl → 0.11.1.4__cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.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 +35 -40
- musica/__init__.py +51 -3
- musica/_musica.cpython-312-i386-linux-gnu.so +0 -0
- musica/_version.py +1 -1
- musica/binding_common.cpp +16 -0
- musica/binding_common.hpp +7 -0
- musica/cpu_binding.cpp +10 -0
- musica/cuda.cpp +12 -0
- musica/cuda.py +10 -0
- musica/gpu_binding.cpp +10 -0
- musica/mechanism_configuration.py +1 -1
- musica/musica.cpp +1 -1
- musica/test/test_analytical.py +13 -11
- musica/test/test_chapman.py +18 -2
- musica/test/test_parser.py +2 -2
- musica/tools/prepare_build_environment_linux.sh +8 -6
- musica/tools/repair_wheel_gpu.sh +25 -15
- musica/types.py +4 -3
- {musica-0.11.1.2.dist-info → musica-0.11.1.4.dist-info}/METADATA +124 -3
- musica-0.11.1.4.dist-info/RECORD +33 -0
- {musica-0.11.1.2.dist-info → musica-0.11.1.4.dist-info}/WHEEL +1 -1
- _musica.cpython-312-i386-linux-gnu.so +0 -0
- lib/libmusica.a +0 -0
- lib/libyaml-cpp.a +0 -0
- musica/binding.cpp +0 -19
- musica-0.11.1.2.dist-info/RECORD +0 -30
- /musica/test/examples/v1/{full_configuration.json → full_configuration/full_configuration.json} +0 -0
- /musica/test/examples/v1/{full_configuration.yaml → full_configuration/full_configuration.yaml} +0 -0
- {musica-0.11.1.2.dist-info → musica-0.11.1.4.dist-info}/licenses/LICENSE +0 -0
musica/CMakeLists.txt
CHANGED
|
@@ -1,47 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
################################################################################
|
|
2
|
+
# Python Extension Modules
|
|
3
|
+
|
|
4
|
+
#include(setup_muisca_target)
|
|
5
|
+
|
|
6
|
+
# Define the list of Python extension module targets
|
|
7
|
+
set(PY_MODULES _musica)
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
set(
|
|
10
|
+
MUSICA_PYTHON_SOURCES
|
|
11
|
+
binding_common.cpp
|
|
12
|
+
cuda.cpp
|
|
13
|
+
mechanism_configuration.cpp
|
|
14
|
+
musica.cpp
|
|
15
|
+
|
|
16
|
+
${MUSICA_SOURCES}
|
|
10
17
|
)
|
|
11
18
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
BUILD_WITH_INSTALL_RPATH TRUE
|
|
20
|
-
)
|
|
21
|
-
elseif(UNIX)
|
|
22
|
-
set(CUDA_RPATH
|
|
23
|
-
"$ORIGIN/../../nvidia/cublas/lib"
|
|
24
|
-
"$ORIGIN/../../nvidia/cuda_runtime/lib"
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
message(STATUS "Adding RPATH for python site packages libs at ${CUDA_RPATH}")
|
|
28
|
-
|
|
29
|
-
set_target_properties(_musica PROPERTIES
|
|
30
|
-
INSTALL_RPATH "$ORIGIN;${CUDA_RPATH}"
|
|
31
|
-
BUILD_WITH_INSTALL_RPATH TRUE
|
|
32
|
-
)
|
|
19
|
+
pybind11_add_module(_musica cpu_binding.cpp ${MUSICA_PYTHON_SOURCES})
|
|
20
|
+
musica_setup_target(_musica MODE CPU)
|
|
21
|
+
|
|
22
|
+
if (NOT ${MUSICA_GPU_TYPE} STREQUAL "None")
|
|
23
|
+
pybind11_add_module(_musica_gpu gpu_binding.cpp ${MUSICA_PYTHON_SOURCES})
|
|
24
|
+
musica_setup_target(_musica_gpu MODE GPU)
|
|
25
|
+
list(APPEND PY_MODULES _musica_gpu)
|
|
33
26
|
endif()
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
foreach(lib ${PY_MODULES})
|
|
29
|
+
if (APPLE)
|
|
30
|
+
set_target_properties(${lib} PROPERTIES
|
|
31
|
+
INSTALL_RPATH "@loader_path"
|
|
32
|
+
BUILD_WITH_INSTALL_RPATH TRUE
|
|
33
|
+
)
|
|
34
|
+
elseif(UNIX)
|
|
35
|
+
set_target_properties(${lib} PROPERTIES
|
|
36
|
+
INSTALL_RPATH "$ORIGIN"
|
|
37
|
+
BUILD_WITH_INSTALL_RPATH TRUE
|
|
38
|
+
)
|
|
42
39
|
endif()
|
|
43
|
-
else()
|
|
44
|
-
set(PYTHON_MODULE_PATH "${CMAKE_CURRENT_BINARY_DIR}")
|
|
45
|
-
endif()
|
|
46
40
|
|
|
47
|
-
install(TARGETS
|
|
41
|
+
install(TARGETS ${lib} LIBRARY DESTINATION musica)
|
|
42
|
+
endforeach()
|
musica/__init__.py
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import importlib.util
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _safe_find_spec(name):
|
|
5
|
+
try:
|
|
6
|
+
return importlib.util.find_spec(name)
|
|
7
|
+
except ModuleNotFoundError:
|
|
8
|
+
return None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _gpu_deps_installed():
|
|
12
|
+
return (
|
|
13
|
+
_safe_find_spec("nvidia.cublas") is not None or
|
|
14
|
+
_safe_find_spec("nvidia_cuda_runtime") is not None or
|
|
15
|
+
_safe_find_spec("nvidia-cublas-cu12") is not None or
|
|
16
|
+
_safe_find_spec("nvidia-cuda-runtime-cu12") is not None
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if _gpu_deps_installed():
|
|
21
|
+
from . import _musica_gpu as _backend
|
|
22
|
+
else:
|
|
23
|
+
from . import _musica as _backend
|
|
24
|
+
|
|
25
|
+
# Helper to re-export names from a module
|
|
26
|
+
def _export_all(module, names, globals_):
|
|
27
|
+
for name in names:
|
|
28
|
+
globals_[name] = getattr(module, name)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
_core_names = [
|
|
32
|
+
"_Conditions", "_SolverType", "_Solver", "_State", "_create_solver",
|
|
33
|
+
"_create_solver_from_mechanism", "_create_state", "_micm_solve", "_vector_size",
|
|
34
|
+
"_species_ordering", "_user_defined_rate_parameters_ordering",
|
|
35
|
+
]
|
|
36
|
+
_mechanism_names = [
|
|
37
|
+
"_ReactionType", "_Species", "_Phase", "_ReactionComponent", "_Arrhenius",
|
|
38
|
+
"_CondensedPhaseArrhenius", "_Troe", "_Branched", "_Tunneling", "_Surface",
|
|
39
|
+
"_Photolysis", "_CondensedPhasePhotolysis", "_Emission", "_FirstOrderLoss",
|
|
40
|
+
"_AqueousEquilibrium", "_WetDeposition", "_HenrysLaw", "_SimpolPhaseTransfer",
|
|
41
|
+
"_UserDefined", "_Reactions", "_ReactionsIterator", "_Mechanism", "_Version", "_Parser"
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
# this allows us to use the same symbols in both the GPU and CPU versionspp
|
|
45
|
+
_export_all(_backend._core, _core_names, globals())
|
|
46
|
+
_export_all(_backend._mechanism_configuration, _mechanism_names, globals())
|
|
47
|
+
|
|
48
|
+
__all__ = _core_names + _mechanism_names
|
|
49
|
+
|
|
50
|
+
from .types import MICM, SolverType, State, Conditions
|
|
51
|
+
from ._version import version as __version__
|
|
Binary file
|
musica/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
version = "0.11.1.
|
|
1
|
+
version = "0.11.1.4"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#include "binding_common.hpp"
|
|
2
|
+
|
|
3
|
+
void bind_cuda(py::module_ &);
|
|
4
|
+
void bind_musica(py::module_ &);
|
|
5
|
+
|
|
6
|
+
void bind_mechanism_configuration(py::module_ &);
|
|
7
|
+
|
|
8
|
+
void bind_all(py::module_ &m) {
|
|
9
|
+
py::module_ core = m.def_submodule("_core", "Wrapper classes for MUSICA C library structs and functions");
|
|
10
|
+
py::module_ mechanism_configuration = m.def_submodule("_mechanism_configuration", "Wrapper classes for Mechanism Configuration library structs and functions");
|
|
11
|
+
|
|
12
|
+
bind_cuda(core);
|
|
13
|
+
bind_musica(core);
|
|
14
|
+
|
|
15
|
+
bind_mechanism_configuration(mechanism_configuration);
|
|
16
|
+
}
|
musica/cpu_binding.cpp
ADDED
musica/cuda.cpp
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Copyright (C) 2023-2025 University Corporation for Atmospheric Research
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#include <musica/micm/cuda_availability.hpp>
|
|
4
|
+
|
|
5
|
+
#include <pybind11/pybind11.h>
|
|
6
|
+
|
|
7
|
+
namespace py = pybind11;
|
|
8
|
+
|
|
9
|
+
void bind_cuda(py::module_ &cuda)
|
|
10
|
+
{
|
|
11
|
+
cuda.def("_is_cuda_available", &musica::IsCudaAvailable, "Check if CUDA is available");
|
|
12
|
+
}
|
musica/cuda.py
ADDED
musica/gpu_binding.cpp
ADDED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of the musica Python package.
|
|
5
5
|
# For more information, see the LICENSE file in the top-level directory of this distribution.
|
|
6
6
|
from typing import Optional, Any, Dict, List, Union, Tuple
|
|
7
|
-
from
|
|
7
|
+
from musica import (
|
|
8
8
|
_ReactionType,
|
|
9
9
|
_Species,
|
|
10
10
|
_Phase,
|
musica/musica.cpp
CHANGED
musica/test/test_analytical.py
CHANGED
|
@@ -3,7 +3,7 @@ import numpy as np
|
|
|
3
3
|
import musica
|
|
4
4
|
import random
|
|
5
5
|
import musica.mechanism_configuration as mc
|
|
6
|
-
from
|
|
6
|
+
from musica.cuda import is_cuda_available
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def TestSingleGridCell(solver, state, time_step, places=5):
|
|
@@ -118,6 +118,7 @@ def TestMultipleGridCell(solver, state, num_grid_cells, time_step, places=5):
|
|
|
118
118
|
rate_constants["USER.reaction 2"].append(
|
|
119
119
|
0.002 + random.uniform(-0.0001, 0.0001))
|
|
120
120
|
|
|
121
|
+
|
|
121
122
|
state.set_conditions(temperatures, pressures) # Air density should be calculated in the state
|
|
122
123
|
state.set_concentrations(concentrations)
|
|
123
124
|
state.set_user_defined_rate_parameters(rate_constants)
|
|
@@ -127,6 +128,7 @@ def TestMultipleGridCell(solver, state, num_grid_cells, time_step, places=5):
|
|
|
127
128
|
initial_temperatures = state.get_conditions()["temperature"]
|
|
128
129
|
initial_pressures = state.get_conditions()["pressure"]
|
|
129
130
|
initial_air_density = state.get_conditions()["air_density"]
|
|
131
|
+
|
|
130
132
|
for i in range(num_grid_cells):
|
|
131
133
|
assert np.isclose(initial_concentrations["A"][i], concentrations["A"][i], atol=1e-13)
|
|
132
134
|
assert np.isclose(initial_concentrations["B"][i], concentrations["B"][i], atol=1e-13)
|
|
@@ -242,7 +244,7 @@ def GetMechanism():
|
|
|
242
244
|
|
|
243
245
|
def test_single_grid_cell_standard_rosenbrock():
|
|
244
246
|
solver = musica.MICM(
|
|
245
|
-
config_path="configs/analytical",
|
|
247
|
+
config_path="configs/v0/analytical",
|
|
246
248
|
solver_type=musica.SolverType.rosenbrock_standard_order)
|
|
247
249
|
state = solver.create_state()
|
|
248
250
|
TestSingleGridCell(solver, state, 200.0, 5)
|
|
@@ -251,16 +253,16 @@ def test_single_grid_cell_standard_rosenbrock():
|
|
|
251
253
|
def test_multiple_grid_cells_standard_rosenbrock():
|
|
252
254
|
for i in range(1, 11):
|
|
253
255
|
solver = musica.MICM(
|
|
254
|
-
config_path="configs/analytical",
|
|
256
|
+
config_path="configs/v0/analytical",
|
|
255
257
|
solver_type=musica.SolverType.rosenbrock_standard_order)
|
|
256
258
|
state = solver.create_state(i)
|
|
257
259
|
TestMultipleGridCell(solver, state, i, 200.0, 5)
|
|
258
260
|
|
|
259
261
|
|
|
260
262
|
def test_cuda_rosenbrock():
|
|
261
|
-
if
|
|
263
|
+
if is_cuda_available():
|
|
262
264
|
solver = musica.MICM(
|
|
263
|
-
config_path="configs/analytical",
|
|
265
|
+
config_path="configs/v0/analytical",
|
|
264
266
|
solver_type=musica.SolverType.cuda_rosenbrock)
|
|
265
267
|
state = solver.create_state()
|
|
266
268
|
TestSingleGridCell(solver, state, 200.0, 5)
|
|
@@ -270,7 +272,7 @@ def test_cuda_rosenbrock():
|
|
|
270
272
|
|
|
271
273
|
def test_single_grid_cell_backward_euler():
|
|
272
274
|
solver = musica.MICM(
|
|
273
|
-
config_path="configs/analytical",
|
|
275
|
+
config_path="configs/v0/analytical",
|
|
274
276
|
solver_type=musica.SolverType.backward_euler_standard_order)
|
|
275
277
|
state = solver.create_state()
|
|
276
278
|
TestSingleGridCell(solver, state, 10.0, places=2)
|
|
@@ -279,7 +281,7 @@ def test_single_grid_cell_backward_euler():
|
|
|
279
281
|
def test_multiple_grid_cells_backward_euler():
|
|
280
282
|
for i in range(1, 11):
|
|
281
283
|
solver = musica.MICM(
|
|
282
|
-
config_path="configs/analytical",
|
|
284
|
+
config_path="configs/v0/analytical",
|
|
283
285
|
solver_type=musica.SolverType.backward_euler_standard_order)
|
|
284
286
|
state = solver.create_state(i)
|
|
285
287
|
TestMultipleGridCell(solver, state, i, 10.0, places=2)
|
|
@@ -287,7 +289,7 @@ def test_multiple_grid_cells_backward_euler():
|
|
|
287
289
|
|
|
288
290
|
def test_single_grid_cell_rosenbrock():
|
|
289
291
|
solver = musica.MICM(
|
|
290
|
-
config_path="configs/analytical",
|
|
292
|
+
config_path="configs/v0/analytical",
|
|
291
293
|
solver_type=musica.SolverType.rosenbrock)
|
|
292
294
|
state = solver.create_state()
|
|
293
295
|
TestSingleGridCell(solver, state, 200.0, 5)
|
|
@@ -296,7 +298,7 @@ def test_single_grid_cell_rosenbrock():
|
|
|
296
298
|
def test_multiple_grid_cells_rosenbrock():
|
|
297
299
|
for i in range(1, 11):
|
|
298
300
|
solver = musica.MICM(
|
|
299
|
-
config_path="configs/analytical",
|
|
301
|
+
config_path="configs/v0/analytical",
|
|
300
302
|
solver_type=musica.SolverType.rosenbrock)
|
|
301
303
|
state = solver.create_state(i)
|
|
302
304
|
TestMultipleGridCell(solver, state, i, 200.0, 5)
|
|
@@ -304,7 +306,7 @@ def test_multiple_grid_cells_rosenbrock():
|
|
|
304
306
|
|
|
305
307
|
def test_single_grid_cell_backward_euler_standard_order():
|
|
306
308
|
solver = musica.MICM(
|
|
307
|
-
config_path="configs/analytical",
|
|
309
|
+
config_path="configs/v0/analytical",
|
|
308
310
|
solver_type=musica.SolverType.backward_euler_standard_order)
|
|
309
311
|
state = solver.create_state()
|
|
310
312
|
TestSingleGridCell(solver, state, 10.0, places=2)
|
|
@@ -313,7 +315,7 @@ def test_single_grid_cell_backward_euler_standard_order():
|
|
|
313
315
|
def test_multiple_grid_cells_backward_euler_standard_order():
|
|
314
316
|
for i in range(1, 11):
|
|
315
317
|
solver = musica.MICM(
|
|
316
|
-
config_path="configs/analytical",
|
|
318
|
+
config_path="configs/v0/analytical",
|
|
317
319
|
solver_type=musica.SolverType.backward_euler_standard_order)
|
|
318
320
|
state = solver.create_state(i)
|
|
319
321
|
TestMultipleGridCell(solver, state, i, 10.0, places=2)
|
musica/test/test_chapman.py
CHANGED
|
@@ -3,9 +3,25 @@ import musica
|
|
|
3
3
|
import musica.mechanism_configuration as mc
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
def
|
|
6
|
+
def test_solve_with_config_path_v0():
|
|
7
7
|
solver = musica.MICM(
|
|
8
|
-
config_path="configs/chapman",
|
|
8
|
+
config_path="configs/v0/chapman",
|
|
9
|
+
solver_type=musica.SolverType.rosenbrock_standard_order,
|
|
10
|
+
)
|
|
11
|
+
TestSolve(solver)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_solve_with_config_path_v1_json():
|
|
15
|
+
solver = musica.MICM(
|
|
16
|
+
config_path="configs/v1/chapman/config.json",
|
|
17
|
+
solver_type=musica.SolverType.rosenbrock_standard_order,
|
|
18
|
+
)
|
|
19
|
+
TestSolve(solver)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_solve_with_config_path_v1_yaml():
|
|
23
|
+
solver = musica.MICM(
|
|
24
|
+
config_path="configs/v1/chapman/config.yaml",
|
|
9
25
|
solver_type=musica.SolverType.rosenbrock_standard_order,
|
|
10
26
|
)
|
|
11
27
|
TestSolve(solver)
|
musica/test/test_parser.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
from musica.mechanism_configuration import *
|
|
3
|
-
from
|
|
3
|
+
from musica import _ReactionType
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def validate_species(species):
|
|
@@ -400,7 +400,7 @@ def test_parsed_full_v1_configuration():
|
|
|
400
400
|
parser = Parser()
|
|
401
401
|
extensions = [".yaml", ".json"]
|
|
402
402
|
for extension in extensions:
|
|
403
|
-
path = f"musica/test/examples/v1/full_configuration{extension}"
|
|
403
|
+
path = f"musica/test/examples/v1/full_configuration/full_configuration{extension}"
|
|
404
404
|
mechanism = parser.parse(path)
|
|
405
405
|
validate_full_v1_mechanism(mechanism)
|
|
406
406
|
|
|
@@ -11,7 +11,7 @@ sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo
|
|
|
11
11
|
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
|
|
12
12
|
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
|
|
13
13
|
|
|
14
|
-
yum install -y zip tree
|
|
14
|
+
yum install -y zip tree wget
|
|
15
15
|
|
|
16
16
|
# Use CIBW_ARCHS or CIBW_ARCH if set, else fallback to uname -m
|
|
17
17
|
if [ -n "$CIBW_ARCHS" ]; then
|
|
@@ -32,10 +32,12 @@ if [ "$target_arch" = "x86_64" ]; then
|
|
|
32
32
|
sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo
|
|
33
33
|
sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo
|
|
34
34
|
yum install --setopt=obsoletes=0 -y \
|
|
35
|
-
cuda-nvcc-12-2
|
|
36
|
-
cuda-cudart-devel-12-2
|
|
37
|
-
libcurand-devel-12-2
|
|
38
|
-
libcublas-devel-12-2
|
|
39
|
-
libnccl-devel-2.19.3-1+cuda12.2
|
|
35
|
+
cuda-nvcc-12-2 \
|
|
36
|
+
cuda-cudart-devel-12-2 \
|
|
37
|
+
libcurand-devel-12-2 \
|
|
38
|
+
libcublas-devel-12-2
|
|
40
39
|
ln -s cuda-12.2 /usr/local/cuda
|
|
40
|
+
|
|
41
|
+
# list the installed CUDA packages
|
|
42
|
+
tree -L 4 /usr/local/cuda-12.2
|
|
41
43
|
fi
|
musica/tools/repair_wheel_gpu.sh
CHANGED
|
@@ -7,21 +7,31 @@ for whl in "$2"/*.whl; do
|
|
|
7
7
|
tmpdir=$(mktemp -d)
|
|
8
8
|
unzip -q "$whl" -d "$tmpdir"
|
|
9
9
|
tree "$tmpdir"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
+
|
|
25
35
|
# Repack the wheel with correct structure
|
|
26
36
|
(cd "$tmpdir" && zip -qr "${whl%.whl}.patched.whl" .)
|
|
27
37
|
rm -rf "$tmpdir"
|
musica/types.py
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
#
|
|
4
4
|
# This file is part of the musica Python package.
|
|
5
5
|
# For more information, see the LICENSE file in the top-level directory of this distribution.
|
|
6
|
-
from typing import Optional,
|
|
6
|
+
from typing import Optional, Dict, List, Union, Tuple
|
|
7
7
|
from os import PathLike
|
|
8
8
|
import math
|
|
9
|
-
from
|
|
9
|
+
from musica import (
|
|
10
10
|
_Conditions,
|
|
11
11
|
_SolverType,
|
|
12
12
|
_Solver,
|
|
@@ -172,6 +172,7 @@ class State():
|
|
|
172
172
|
state.user_defined_rate_parameters[i_param * param_stride + i_cell * cell_stride] = value[k]
|
|
173
173
|
k += 1
|
|
174
174
|
|
|
175
|
+
|
|
175
176
|
def set_conditions(self,
|
|
176
177
|
temperatures: Union[Union[float, int], List[Union[float, int]]],
|
|
177
178
|
pressures: Union[Union[float, int], List[Union[float, int]]],
|
|
@@ -358,5 +359,5 @@ class MICM():
|
|
|
358
359
|
if not isinstance(time_step, (int, float)):
|
|
359
360
|
raise TypeError("time_step must be an int or float.")
|
|
360
361
|
states = state.get_internal_states()
|
|
361
|
-
for
|
|
362
|
+
for _state in states:
|
|
362
363
|
_micm_solve(self.__solver, _state, time_step)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: musica
|
|
3
|
-
Version: 0.11.1.
|
|
3
|
+
Version: 0.11.1.4
|
|
4
4
|
Summary: MUSICA is a Python library for performing computational simulations in atmospheric chemistry.
|
|
5
5
|
Author-Email: Matthew Dawsom <mattdawson@ucar.edu>, Jiwon Gim <jiwongim@ucar.edu>, David Fillmore <fillmore@ucar.edu>, Kyle Shores <kshores@ucar.edu>, Montek Thind <mthind@ucar.edu>
|
|
6
6
|
Maintainer-Email: ACOM MUSICA Developers <musica-support@ucar.edu>
|
|
@@ -240,7 +240,110 @@ At present the project encompasses these components
|
|
|
240
240
|
- [MICM](https://github.com/NCAR/micm)
|
|
241
241
|
- Model Independent Chemical Module
|
|
242
242
|
|
|
243
|
-
|
|
243
|
+
# Installation
|
|
244
|
+
MUSICA is installable via pip for Python or CMake for C++.
|
|
245
|
+
|
|
246
|
+
## Pip
|
|
247
|
+
```
|
|
248
|
+
pip install musica
|
|
249
|
+
```
|
|
250
|
+
## CMake
|
|
251
|
+
```
|
|
252
|
+
$ git clone https://github.com/NCAR/musica.git
|
|
253
|
+
$ cd musica
|
|
254
|
+
$ mkdir build
|
|
255
|
+
$ cd build
|
|
256
|
+
$ ccmake ..
|
|
257
|
+
$ make
|
|
258
|
+
$ make install
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
# Using the MUSICA Python API
|
|
262
|
+
MUSICA makes its chemical mechanism analysis and visualization available through a Python API. The following example works through solving a simple chemistry system. Please refer to the [official documentation](https://ncar.github.io/musica/index.html) for further tutorials and examples.
|
|
263
|
+
```
|
|
264
|
+
# --- Import Musica ---
|
|
265
|
+
import musica
|
|
266
|
+
import musica.mechanism_configuration as mc
|
|
267
|
+
|
|
268
|
+
# --- 1. Define the chemical system of interest ---
|
|
269
|
+
A = mc.Species(name="A")
|
|
270
|
+
B = mc.Species(name="B")
|
|
271
|
+
C = mc.Species(name="C")
|
|
272
|
+
species = [A, B, C]
|
|
273
|
+
gas = mc.Phase(name="gas", species=species)
|
|
274
|
+
|
|
275
|
+
# --- 2. Define a mechanism of interest ---
|
|
276
|
+
# Through Musica, several different mechanisms can be explored to define reaction rates. Here, we use the Arrhenius equation as a simple example.
|
|
277
|
+
|
|
278
|
+
r1 = mc.Arrhenius(name="A->B", A=4.0e-3, C=50, reactants=[A], products=[B], gas_phase=gas)
|
|
279
|
+
r2 = mc.Arrhenius(name="B->C", A=1.2e-4, B=2.5, C=75, D=50, E=0.5, reactants=[B], products=[C], gas_phase=gas)
|
|
280
|
+
|
|
281
|
+
mechanism = mc.Mechanism(name="musica_example", species=species, phases=[gas], reactions=[r1, r2])
|
|
282
|
+
|
|
283
|
+
# --- 3. Create MICM solver ---
|
|
284
|
+
# A solver must be initialized with either a configuration file or a mechanism:
|
|
285
|
+
|
|
286
|
+
solver = musica.MICM(mechanism=mechanism, solver_type=musica.SolverType.rosenbrock_standard_order)
|
|
287
|
+
|
|
288
|
+
# --- 4. Define environmental conditions ---
|
|
289
|
+
temperature=300.0
|
|
290
|
+
pressure=101000.0
|
|
291
|
+
|
|
292
|
+
# --- 5. Create and initialize State ---
|
|
293
|
+
# In the model, conditions represent the starting environment for the reactions and are assigned by modifying the state.
|
|
294
|
+
|
|
295
|
+
state = solver.create_state()
|
|
296
|
+
state.set_concentrations({"A": 1.0, "B": 3.0, "C": 5.0})
|
|
297
|
+
state.set_conditions(temperature, pressure)
|
|
298
|
+
initial_pressure = state.get_conditions()['air_density'][0] # store for visualization and output
|
|
299
|
+
|
|
300
|
+
# --- 6. Time parameters ---
|
|
301
|
+
time_step = 4 # stepping
|
|
302
|
+
sim_length = 20 # total simulation time
|
|
303
|
+
|
|
304
|
+
# --- (Optional) 7. Save initial state (t=0) for output visualization ---
|
|
305
|
+
initial_row = {"time.s": 0.0, "ENV.temperature.K": temperature, "ENV.pressure.Pa": pressure, "ENV.air number density.mol m-3": state.get_conditions()['air_density'][0]}
|
|
306
|
+
initial_row.update({f"CONC.{k}.mol m-3": v[0] for k, v in state.get_concentrations().items()})
|
|
307
|
+
|
|
308
|
+
# --- 8. Solve through time loop only ---
|
|
309
|
+
# The following loop simply solves the model per each time step:
|
|
310
|
+
|
|
311
|
+
curr_time = time_step
|
|
312
|
+
while curr_time <= sim_length:
|
|
313
|
+
solver.solve(state, time_step)
|
|
314
|
+
concentrations = state.get_concentrations()
|
|
315
|
+
curr_time += time_step
|
|
316
|
+
|
|
317
|
+
# --- 9. Solve and create DataFrame ---
|
|
318
|
+
# It is likely more useful to solve at each time step and store the associated data:
|
|
319
|
+
import pandas as pd
|
|
320
|
+
|
|
321
|
+
output_data = [] # prepare to store output per time step
|
|
322
|
+
output_data.append(initial_row) # save t=0 data
|
|
323
|
+
|
|
324
|
+
curr_time = time_step
|
|
325
|
+
while curr_time <= sim_length:
|
|
326
|
+
solver.solve(state, time_step)
|
|
327
|
+
row = {
|
|
328
|
+
"time.s": curr_time,
|
|
329
|
+
"ENV.temperature.K": state.get_conditions()['temperature'][0],
|
|
330
|
+
"ENV.pressure.Pa": state.get_conditions()['pressure'][0],
|
|
331
|
+
"ENV.air number density.mol m-3": state.get_conditions()['air_density'][0]
|
|
332
|
+
}
|
|
333
|
+
row.update({f"CONC.{k}.mol m-3": v[0] for k, v in state.get_concentrations().items()})
|
|
334
|
+
output_data.append(row)
|
|
335
|
+
curr_time += time_step
|
|
336
|
+
|
|
337
|
+
df = pd.DataFrame(output_data)
|
|
338
|
+
print(df)
|
|
339
|
+
|
|
340
|
+
# --- 10. Visualize Specific Results ---
|
|
341
|
+
import matplotlib.pyplot as plt
|
|
342
|
+
|
|
343
|
+
df.plot(x='time.s', y=['CONC.A.mol m-3', 'CONC.B.mol m-3', 'CONC.C.mol m-3'], title='Concentration over time', ylabel='Concentration (mol m-3)', xlabel='Time (s)')
|
|
344
|
+
plt.show()
|
|
345
|
+
```
|
|
346
|
+
# Available grids
|
|
244
347
|
|
|
245
348
|
Pre-made grids for use in MUSICA are available [here](https://wiki.ucar.edu/display/MUSICA/Available+Grids).
|
|
246
349
|
|
|
@@ -282,7 +385,25 @@ Specifying a specific version of `tuv-x` by has, but using the official reposito
|
|
|
282
385
|
### Python build
|
|
283
386
|
Musica has python bindings. If you want to install the python package, you may `pip install musica`.
|
|
284
387
|
|
|
285
|
-
|
|
388
|
+
#### PyPi
|
|
389
|
+
If you only want to use the CPU components,
|
|
390
|
+
|
|
391
|
+
```
|
|
392
|
+
pip install musica
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Note that GPU support has only been tested on linux. If you have an NVIDIA GPU and would like to take
|
|
396
|
+
advantage of our GPU solver, you must first [add the NVIDIA pypi index](https://docs.nvidia.com/cuda/cuda-quick-start-guide/#pip-wheels-linux) and then install musica with our gpu option.
|
|
397
|
+
|
|
398
|
+
```
|
|
399
|
+
pip install --upgrade setuptools pip wheel
|
|
400
|
+
pip install nvidia-pyindex
|
|
401
|
+
pip install musica[gpu]
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
#### Local build
|
|
405
|
+
|
|
406
|
+
Musica has python bindings. To build the package locally,
|
|
286
407
|
|
|
287
408
|
```
|
|
288
409
|
pip install -e .
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
musica-0.11.1.4.dist-info/WHEEL,sha256=N_uuhVhrG8_MQnCM1eax6HVVw78c3wdMjcndkwYfQoI,152
|
|
2
|
+
musica-0.11.1.4.dist-info/RECORD,,
|
|
3
|
+
musica-0.11.1.4.dist-info/METADATA,sha256=3S0lnuWJjva1wxEFXEmDsnNI1NO0Y1TY1W3RXgAJUoo,25120
|
|
4
|
+
musica-0.11.1.4.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
5
|
+
musica/cpu_binding.cpp,sha256=nOLBGDWvvtxeAyIbeP3bXIeB_xzZoU_ALduHanHjR0Q,224
|
|
6
|
+
musica/binding_common.cpp,sha256=40rvAVZQkOloAKXNhUngkiZNaD7LfHE8jnCcoOOXAak,560
|
|
7
|
+
musica/gpu_binding.cpp,sha256=wWkM_J_2Ky0OawxEXkdvPCzctfSsiH7THs2cVkr0S2s,228
|
|
8
|
+
musica/musica.cpp,sha256=cMi5OfncDV_ROlhjCSvvGtUHWBl_oYcUwTu4KFLTBjg,8400
|
|
9
|
+
musica/_version.py,sha256=uIJChf8jZtk47zX1PsPRzp-TnVMaygs153ACjtBx3Hs,21
|
|
10
|
+
musica/types.py,sha256=cycZ26-2q7mzx_oMnXWrTEvz6hEJnog5OQJoyizn6oA,14870
|
|
11
|
+
musica/cuda.cpp,sha256=qQOLKTUOB83gE-963rT579Me-NrqMUWnXDKmpTPMVFg,346
|
|
12
|
+
musica/__init__.py,sha256=TXWCmKd4hBa09jf9917-cM5_DQ0Qcio__a1ZkfysT5k,1748
|
|
13
|
+
musica/mechanism_configuration.cpp,sha256=E7GGeftgQGL303nsk2C7pwYpPiMAQcuxdbadohHb1QM,27100
|
|
14
|
+
musica/mechanism_configuration.py,sha256=UCwPLKznU5CWMwj0URd6xqn78DNQFecxcmD7MX3wc7M,57985
|
|
15
|
+
musica/_musica.cpython-312-i386-linux-gnu.so,sha256=oWvAdxugBxAVsX84yJsOnZAspig3JwnoFpb0Xr2MlwA,2995800
|
|
16
|
+
musica/cuda.py,sha256=A0U0plsFuQgezWrwN8217kkC5URxjlsKI0ymYJCSdDE,224
|
|
17
|
+
musica/binding_common.hpp,sha256=YVG7TS1JeVC4W_yisV4kL61qo3N6Dt6JdAh9bdr6z2w,102
|
|
18
|
+
musica/CMakeLists.txt,sha256=E5Z8tLfxUc0RAma1vKtN2-2s3CqHog2njYje9qHehQc,1050
|
|
19
|
+
musica/tools/prepare_build_environment_windows.sh,sha256=zMY_RIerqfMC1VlfpZ2KjjDWfBUOvvx8oRNk_jJhmII,826
|
|
20
|
+
musica/tools/prepare_build_environment_linux.sh,sha256=yKjBD_eFMMvqth_PSF1qzmr7VLSMWiPufBh1inhuIRs,1457
|
|
21
|
+
musica/tools/repair_wheel_gpu.sh,sha256=GJcKUJ2n9KMsj3QC5Shxt_ks9uFu2f9Than-4vVhG9U,1485
|
|
22
|
+
musica/test/test_chapman.py,sha256=gOISpApVIMLCEv6W0Hq1yNGF0xu2MlZMHQQhOGPmoQI,3512
|
|
23
|
+
musica/test/test_analytical.py,sha256=aTXQ3OQrThHEgDruaPvLjbMPU_wP2sZohfHnOqmBRJw,13800
|
|
24
|
+
musica/test/tuvx.py,sha256=rWvBcG6cgLJylqkf8M0MFMKT2AkyoaZlGdaKjVSbCR8,116
|
|
25
|
+
musica/test/test_parser.py,sha256=6jVyezmjArO3C0Alia3et_lpLnuaspw1b_HLYrYl0GE,24285
|
|
26
|
+
musica/test/examples/v1/full_configuration/full_configuration.json,sha256=7AqPhTHodSjtV8LOPoUsA9MtCu_Xc28RVOlhNGPI9GQ,8807
|
|
27
|
+
musica/test/examples/v1/full_configuration/full_configuration.yaml,sha256=s9Go7AQv9OFQ7zVb1k4wmDj2vpHYzVCTRqi8JZTHlUM,5235
|
|
28
|
+
musica/test/examples/v0/reactions.yaml,sha256=uEuQOz5i7vGyn0G7KsmdWv23V2Gn0QUmexrK030CFlo,2266
|
|
29
|
+
musica/test/examples/v0/config.json,sha256=7s6g5jxH2GiEiRcZmO13IJ5k4walg1C9t-dQr9o45U4,79
|
|
30
|
+
musica/test/examples/v0/species.yaml,sha256=vUYTs7bp2AYVWHDgXBFxnCkmqdO6ysJ_ulOQTAoKa1M,283
|
|
31
|
+
musica/test/examples/v0/species.json,sha256=xKTcwh9QV9sRjLPK9GvGEnatBrRPbP857NmPG0bCXqU,565
|
|
32
|
+
musica/test/examples/v0/reactions.json,sha256=7WJhItBPtGu89vLcPMK6izW8BvwUAIjfzqwjtDmfPPg,4188
|
|
33
|
+
musica/test/examples/v0/config.yaml,sha256=GgA18mP8ZCzOdY-AlAePK48M05PJFPeWCk-IfrPN_VE,44
|
|
Binary file
|
lib/libmusica.a
DELETED
|
Binary file
|
lib/libyaml-cpp.a
DELETED
|
Binary file
|
musica/binding.cpp
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// Copyright (C) 2023-2025 University Corporation for Atmospheric Research
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
#include <pybind11/pybind11.h>
|
|
5
|
-
|
|
6
|
-
namespace py = pybind11;
|
|
7
|
-
|
|
8
|
-
void bind_musica(py::module_ &);
|
|
9
|
-
void bind_mechanism_configuration(py::module_ &);
|
|
10
|
-
|
|
11
|
-
// Wraps micm.cpp
|
|
12
|
-
PYBIND11_MODULE(_musica, m)
|
|
13
|
-
{
|
|
14
|
-
py::module_ core = m.def_submodule("_core", "Wrapper classes for MUSICA C library structs and functions");
|
|
15
|
-
py::module_ mechanism_configuration = m.def_submodule("_mechanism_configuration", "Wrapper classes for Mechanism Configuration library structs and functions");
|
|
16
|
-
|
|
17
|
-
bind_musica(core);
|
|
18
|
-
bind_mechanism_configuration(mechanism_configuration);
|
|
19
|
-
}
|
musica-0.11.1.2.dist-info/RECORD
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
_musica.cpython-312-i386-linux-gnu.so,sha256=IAG8h8iKs1YR3KjS69NMCRhJM7tJSXbfhwJTraI6yjQ,3410392
|
|
2
|
-
lib/libmusica.a,sha256=g3JdnzNJOuVN6HOu6wH6zhBUI5C4TZJwPR65qxPkZy8,1870926
|
|
3
|
-
lib/libyaml-cpp.a,sha256=2-jDMKbyW2gp5wc4ACrAMb5BvzOrxUKLSWmD0r9BkAI,960542
|
|
4
|
-
musica/CMakeLists.txt,sha256=FJkD0MaB1qUNDpyCsjELAW4wpuSslfzyo-YCJwLkIp4,1128
|
|
5
|
-
musica/binding.cpp,sha256=zDD_TQ7icS_lNG7iUAp23VG7_odbptGJbtRdYjalsdc,656
|
|
6
|
-
musica/musica.cpp,sha256=BBRtL9_Q62ia_vhmpTqpnHHGimPBpCR5YVth8x0i0h4,8399
|
|
7
|
-
musica/__init__.py,sha256=CW7SVuK4kO8bYg8wB56YGun-NBRmBqWQO379qP21Yvo,82
|
|
8
|
-
musica/types.py,sha256=WQYTDT3aZQVvCyXmaEGuBYqtdx3IZk1Ao5bcmPUGzXM,14895
|
|
9
|
-
musica/_version.py,sha256=1jBkD_yRHAJvY5k1vGhWG2wbI0EGpqPngkvh7RDvAhs,21
|
|
10
|
-
musica/mechanism_configuration.cpp,sha256=E7GGeftgQGL303nsk2C7pwYpPiMAQcuxdbadohHb1QM,27100
|
|
11
|
-
musica/mechanism_configuration.py,sha256=pma248L_jjQecsxL9zIn-S4KxXNlSL1NL4LB4fN5jV0,58011
|
|
12
|
-
musica/test/test_chapman.py,sha256=Wm0wLy6E1Zm8p6cLaICQO9tSy_szMkYn9E7MPCtobno,3070
|
|
13
|
-
musica/test/test_parser.py,sha256=VhOPNrcDnFGgk2v_iMoLNq5JBP22VbkHTHLk3N09d3s,24292
|
|
14
|
-
musica/test/tuvx.py,sha256=rWvBcG6cgLJylqkf8M0MFMKT2AkyoaZlGdaKjVSbCR8,116
|
|
15
|
-
musica/test/test_analytical.py,sha256=5rIlpy5bzpTfZNv8wAND8O5KA8Wvh3Bm64pTRRlSHgc,13775
|
|
16
|
-
musica/test/examples/v1/full_configuration.yaml,sha256=s9Go7AQv9OFQ7zVb1k4wmDj2vpHYzVCTRqi8JZTHlUM,5235
|
|
17
|
-
musica/test/examples/v1/full_configuration.json,sha256=7AqPhTHodSjtV8LOPoUsA9MtCu_Xc28RVOlhNGPI9GQ,8807
|
|
18
|
-
musica/test/examples/v0/species.json,sha256=xKTcwh9QV9sRjLPK9GvGEnatBrRPbP857NmPG0bCXqU,565
|
|
19
|
-
musica/test/examples/v0/species.yaml,sha256=vUYTs7bp2AYVWHDgXBFxnCkmqdO6ysJ_ulOQTAoKa1M,283
|
|
20
|
-
musica/test/examples/v0/config.yaml,sha256=GgA18mP8ZCzOdY-AlAePK48M05PJFPeWCk-IfrPN_VE,44
|
|
21
|
-
musica/test/examples/v0/reactions.json,sha256=7WJhItBPtGu89vLcPMK6izW8BvwUAIjfzqwjtDmfPPg,4188
|
|
22
|
-
musica/test/examples/v0/reactions.yaml,sha256=uEuQOz5i7vGyn0G7KsmdWv23V2Gn0QUmexrK030CFlo,2266
|
|
23
|
-
musica/test/examples/v0/config.json,sha256=7s6g5jxH2GiEiRcZmO13IJ5k4walg1C9t-dQr9o45U4,79
|
|
24
|
-
musica/tools/repair_wheel_gpu.sh,sha256=EjtYb9ORfXZn_9j50YVPXhgPP3HJc9xJogw30q46w08,1317
|
|
25
|
-
musica/tools/prepare_build_environment_linux.sh,sha256=9rkT0bTVmu9QQqhS6eGRX1HXMQZLfkUGakkC3i5VKec,1466
|
|
26
|
-
musica/tools/prepare_build_environment_windows.sh,sha256=zMY_RIerqfMC1VlfpZ2KjjDWfBUOvvx8oRNk_jJhmII,826
|
|
27
|
-
musica-0.11.1.2.dist-info/WHEEL,sha256=R92Zje91y42BA3U7D0Id6S1JVDKO5kyx9GRJg4oMI88,152
|
|
28
|
-
musica-0.11.1.2.dist-info/METADATA,sha256=Aehj-KwjLoVTpAFwaocvKadc3T-meJRaq_OhiK8qRyM,20814
|
|
29
|
-
musica-0.11.1.2.dist-info/RECORD,,
|
|
30
|
-
musica-0.11.1.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
/musica/test/examples/v1/{full_configuration.json → full_configuration/full_configuration.json}
RENAMED
|
File without changes
|
/musica/test/examples/v1/{full_configuration.yaml → full_configuration/full_configuration.yaml}
RENAMED
|
File without changes
|
|
File without changes
|