musica 0.12.2__cp39-cp39-manylinux_2_27_x86_64.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.
- musica/CMakeLists.txt +68 -0
- musica/__init__.py +11 -0
- musica/_musica.cpython-39-x86_64-linux-gnu.so +0 -0
- musica/_musica_gpu.cpython-39-x86_64-linux-gnu.so +0 -0
- musica/_version.py +1 -0
- musica/backend.py +41 -0
- musica/binding_common.cpp +33 -0
- musica/binding_common.hpp +7 -0
- musica/carma.cpp +911 -0
- musica/carma.py +1729 -0
- musica/constants.py +3 -0
- musica/cpu_binding.cpp +11 -0
- musica/cuda.cpp +12 -0
- musica/cuda.py +13 -0
- musica/examples/__init__.py +1 -0
- musica/examples/carma_aluminum.py +124 -0
- musica/examples/carma_sulfate.py +246 -0
- musica/examples/examples.py +165 -0
- musica/examples/sulfate_box_model.py +439 -0
- musica/examples/ts1_latin_hypercube.py +245 -0
- musica/gpu_binding.cpp +11 -0
- musica/main.py +89 -0
- musica/mechanism_configuration/__init__.py +1 -0
- musica/mechanism_configuration/aqueous_equilibrium.py +274 -0
- musica/mechanism_configuration/arrhenius.py +307 -0
- musica/mechanism_configuration/branched.py +299 -0
- musica/mechanism_configuration/condensed_phase_arrhenius.py +309 -0
- musica/mechanism_configuration/condensed_phase_photolysis.py +88 -0
- musica/mechanism_configuration/emission.py +71 -0
- musica/mechanism_configuration/first_order_loss.py +174 -0
- musica/mechanism_configuration/henrys_law.py +44 -0
- musica/mechanism_configuration/mechanism_configuration.py +234 -0
- musica/mechanism_configuration/phase.py +47 -0
- musica/mechanism_configuration/photolysis.py +88 -0
- musica/mechanism_configuration/reactions.py +73 -0
- musica/mechanism_configuration/simpol_phase_transfer.py +217 -0
- musica/mechanism_configuration/species.py +91 -0
- musica/mechanism_configuration/surface.py +94 -0
- musica/mechanism_configuration/ternary_chemical_activation.py +352 -0
- musica/mechanism_configuration/troe.py +352 -0
- musica/mechanism_configuration/tunneling.py +250 -0
- musica/mechanism_configuration/user_defined.py +88 -0
- musica/mechanism_configuration/utils.py +10 -0
- musica/mechanism_configuration/wet_deposition.py +52 -0
- musica/mechanism_configuration.cpp +607 -0
- musica/musica.cpp +201 -0
- musica/test/examples/v1/full_configuration/full_configuration.json +466 -0
- musica/test/examples/v1/full_configuration/full_configuration.yaml +295 -0
- musica/test/integration/test_analytical.py +324 -0
- musica/test/integration/test_carma.py +227 -0
- musica/test/integration/test_carma_aluminum.py +12 -0
- musica/test/integration/test_carma_sulfate.py +17 -0
- musica/test/integration/test_chapman.py +139 -0
- musica/test/integration/test_sulfate_box_model.py +34 -0
- musica/test/integration/test_tuvx.py +62 -0
- musica/test/unit/test_parser.py +64 -0
- musica/test/unit/test_serializer.py +69 -0
- musica/test/unit/test_state.py +325 -0
- musica/test/unit/test_util_full_mechanism.py +698 -0
- musica/tools/prepare_build_environment_linux.sh +32 -0
- musica/tools/prepare_build_environment_macos.sh +1 -0
- musica/tools/repair_wheel_gpu.sh +40 -0
- musica/tuvx.cpp +93 -0
- musica/tuvx.py +199 -0
- musica/types.py +407 -0
- musica-0.12.2.dist-info/METADATA +473 -0
- musica-0.12.2.dist-info/RECORD +111 -0
- musica-0.12.2.dist-info/WHEEL +6 -0
- musica-0.12.2.dist-info/entry_points.txt +3 -0
- musica-0.12.2.dist-info/licenses/AUTHORS.md +59 -0
- musica-0.12.2.dist-info/licenses/LICENSE +201 -0
- musica.libs/libaec-91f0ca0f.so.0.0.8 +0 -0
- musica.libs/libblas-fe34f726.so.3.8.0 +0 -0
- musica.libs/libbrotlicommon-6ce2a53c.so.1.0.6 +0 -0
- musica.libs/libbrotlidec-811d1be3.so.1.0.6 +0 -0
- musica.libs/libcom_err-448cbf49.so.2.1 +0 -0
- musica.libs/libcrypt-52aca757.so.1.1.0 +0 -0
- musica.libs/libcrypto-bdaed0ea.so.1.1.1k +0 -0
- musica.libs/libcurl-c96f124b.so.4.5.0 +0 -0
- musica.libs/libdf-2848fafa.so.0.0.0 +0 -0
- musica.libs/libgfortran-83c28eba.so.5.0.0 +0 -0
- musica.libs/libgssapi_krb5-323bbd21.so.2.2 +0 -0
- musica.libs/libhdf5-b5e70f84.so.103.1.0 +0 -0
- musica.libs/libhdf5_hl-0b60eabd.so.100.1.2 +0 -0
- musica.libs/libidn2-2f4a5893.so.0.3.6 +0 -0
- musica.libs/libjpeg-e87d2658.so.62.2.0 +0 -0
- musica.libs/libk5crypto-9a74ff38.so.3.1 +0 -0
- musica.libs/libkeyutils-2777d33d.so.1.6 +0 -0
- musica.libs/libkrb5-a55300e8.so.3.3 +0 -0
- musica.libs/libkrb5support-e6594cfc.so.0.1 +0 -0
- musica.libs/liblapack-31d7d384.so.3.8.0 +0 -0
- musica.libs/liblber-2-d20824ef.4.so.2.10.9 +0 -0
- musica.libs/libldap-2-cea2a960.4.so.2.10.9 +0 -0
- musica.libs/libmfhdf-38b34047.so.0.0.0 +0 -0
- musica.libs/libmvec-2-583a17db.28.so +0 -0
- musica.libs/libnetcdf-3915b03a.so.15.0.1 +0 -0
- musica.libs/libnetcdff-8cc3a1b0.so.7.0.0 +0 -0
- musica.libs/libnghttp2-39367a22.so.14.17.0 +0 -0
- musica.libs/libpcre2-8-516f4c9d.so.0.7.1 +0 -0
- musica.libs/libpsl-99becdd3.so.5.3.1 +0 -0
- musica.libs/libquadmath-2284e583.so.0.0.0 +0 -0
- musica.libs/libsasl2-7de4d792.so.3.0.0 +0 -0
- musica.libs/libselinux-d0805dcb.so.1 +0 -0
- musica.libs/libssh-c11d285b.so.4.8.7 +0 -0
- musica.libs/libssl-60250281.so.1.1.1k +0 -0
- musica.libs/libsz-0deeef7d.so.2.0.1 +0 -0
- musica.libs/libtirpc-cff449a4.so.3.0.0 +0 -0
- musica.libs/libunistring-05abdd40.so.2.1.0 +0 -0
musica/carma.cpp
ADDED
|
@@ -0,0 +1,911 @@
|
|
|
1
|
+
#include "binding_common.hpp"
|
|
2
|
+
|
|
3
|
+
#include <musica/carma/carma.hpp>
|
|
4
|
+
#include <musica/carma/carma_state.hpp>
|
|
5
|
+
|
|
6
|
+
#include <pybind11/numpy.h>
|
|
7
|
+
#include <pybind11/pybind11.h>
|
|
8
|
+
#include <pybind11/stl.h>
|
|
9
|
+
|
|
10
|
+
#include <iostream>
|
|
11
|
+
|
|
12
|
+
namespace py = pybind11;
|
|
13
|
+
|
|
14
|
+
auto to_vector_double = [](const py::object& obj) -> std::vector<double>
|
|
15
|
+
{
|
|
16
|
+
try
|
|
17
|
+
{
|
|
18
|
+
if (obj.is_none())
|
|
19
|
+
return {};
|
|
20
|
+
std::vector<double> result;
|
|
21
|
+
for (const auto& item : obj)
|
|
22
|
+
{
|
|
23
|
+
result.push_back(item.cast<double>());
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
catch (const std::exception& e)
|
|
28
|
+
{
|
|
29
|
+
std::cerr << "Error converting sequence to vector<double>: " << e.what() << std::endl;
|
|
30
|
+
std::cerr << "Object type: " << py::str(obj.get_type()).cast<std::string>() << std::endl;
|
|
31
|
+
std::cerr << "Object repr: " << py::repr(obj).cast<std::string>() << std::endl;
|
|
32
|
+
throw;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
auto array_2d_to_vector_double = [](const py::object& obj) -> std::tuple<std::vector<double>, int, int>
|
|
37
|
+
{
|
|
38
|
+
try
|
|
39
|
+
{
|
|
40
|
+
if (obj.is_none())
|
|
41
|
+
return {};
|
|
42
|
+
if (py::isinstance<py::array>(obj))
|
|
43
|
+
{
|
|
44
|
+
auto arr = obj.cast<py::array>();
|
|
45
|
+
if (arr.ndim() != 2)
|
|
46
|
+
throw std::invalid_argument("Expected 2D array for 2D double vector");
|
|
47
|
+
auto shape = arr.shape();
|
|
48
|
+
return { arr.cast<std::vector<double>>(), static_cast<int>(shape[0]), static_cast<int>(shape[1]) };
|
|
49
|
+
}
|
|
50
|
+
if (py::isinstance<py::list>(obj))
|
|
51
|
+
{
|
|
52
|
+
auto list = obj.cast<py::list>();
|
|
53
|
+
if (list.size() == 0 || !py::isinstance<py::list>(list[0]))
|
|
54
|
+
throw std::invalid_argument("Expected list of lists for 2D double vector");
|
|
55
|
+
int rows = static_cast<int>(list.size());
|
|
56
|
+
int cols = static_cast<int>(list[0].cast<py::list>().size());
|
|
57
|
+
std::vector<double> result;
|
|
58
|
+
for (const auto& row : list)
|
|
59
|
+
{
|
|
60
|
+
auto row_list = row.cast<py::list>();
|
|
61
|
+
if (row_list.size() != cols)
|
|
62
|
+
throw std::invalid_argument("All rows must have the same number of elements");
|
|
63
|
+
for (const auto& item : row_list)
|
|
64
|
+
result.push_back(item.cast<double>());
|
|
65
|
+
}
|
|
66
|
+
return { result, rows, cols };
|
|
67
|
+
}
|
|
68
|
+
throw std::invalid_argument("Expected a 2D array or list of lists for 2D double vector");
|
|
69
|
+
}
|
|
70
|
+
catch (const std::exception& e)
|
|
71
|
+
{
|
|
72
|
+
std::cerr << "Error converting sequence to vector<double>: " << e.what() << std::endl;
|
|
73
|
+
std::cerr << "Object type: " << py::str(obj.get_type()).cast<std::string>() << std::endl;
|
|
74
|
+
std::cerr << "Object repr: " << py::repr(obj).cast<std::string>() << std::endl;
|
|
75
|
+
throw;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
auto to_surface_properties = [](const py::object& obj) -> musica::CARMASurfaceProperties
|
|
80
|
+
{
|
|
81
|
+
if (obj.is_none())
|
|
82
|
+
return {};
|
|
83
|
+
if (py::isinstance<py::dict>(obj))
|
|
84
|
+
{
|
|
85
|
+
auto dict = obj.cast<py::dict>();
|
|
86
|
+
musica::CARMASurfaceProperties props;
|
|
87
|
+
if (dict.contains("surface_friction_velocity"))
|
|
88
|
+
props.surface_friction_velocity = dict["surface_friction_velocity"].cast<double>();
|
|
89
|
+
if (dict.contains("aerodynamic_resistance"))
|
|
90
|
+
props.aerodynamic_resistance = dict["aerodynamic_resistance"].cast<double>();
|
|
91
|
+
if (dict.contains("area_fraction"))
|
|
92
|
+
props.area_fraction = dict["area_fraction"].cast<double>();
|
|
93
|
+
return props;
|
|
94
|
+
}
|
|
95
|
+
if (py::isinstance<py::object>(obj))
|
|
96
|
+
{
|
|
97
|
+
musica::CARMASurfaceProperties props;
|
|
98
|
+
props.surface_friction_velocity = obj.attr("surface_friction_velocity").cast<double>();
|
|
99
|
+
props.aerodynamic_resistance = obj.attr("aerodynamic_resistance").cast<double>();
|
|
100
|
+
props.area_fraction = obj.attr("area_fraction").cast<double>();
|
|
101
|
+
return props;
|
|
102
|
+
}
|
|
103
|
+
throw std::invalid_argument("Expected a dictionary for surface properties");
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
void bind_carma(py::module_& carma)
|
|
107
|
+
{
|
|
108
|
+
carma.def("_get_carma_version", []() { return musica::CARMA::GetVersion(); }, "Get the version of the CARMA instance");
|
|
109
|
+
|
|
110
|
+
py::enum_<musica::ParticleType>(carma, "ParticleType")
|
|
111
|
+
.value("INVOLATILE", musica::ParticleType::INVOLATILE)
|
|
112
|
+
.value("VOLATILE", musica::ParticleType::VOLATILE)
|
|
113
|
+
.value("COREMASS", musica::ParticleType::COREMASS)
|
|
114
|
+
.value("VOLCORE", musica::ParticleType::VOLCORE)
|
|
115
|
+
.value("CORE2MOM", musica::ParticleType::CORE2MOM)
|
|
116
|
+
.export_values();
|
|
117
|
+
|
|
118
|
+
py::enum_<musica::ParticleComposition>(carma, "ParticleComposition")
|
|
119
|
+
.value("ALUMINUM", musica::ParticleComposition::ALUMINUM)
|
|
120
|
+
.value("H2SO4", musica::ParticleComposition::H2SO4)
|
|
121
|
+
.value("DUST", musica::ParticleComposition::DUST)
|
|
122
|
+
.value("ICE", musica::ParticleComposition::ICE)
|
|
123
|
+
.value("H2O", musica::ParticleComposition::H2O)
|
|
124
|
+
.value("BLACKCARBON", musica::ParticleComposition::BLACKCARBON)
|
|
125
|
+
.value("ORGANICCARBON", musica::ParticleComposition::ORGANICCARBON)
|
|
126
|
+
.value("OTHER", musica::ParticleComposition::OTHER)
|
|
127
|
+
.export_values();
|
|
128
|
+
|
|
129
|
+
py::enum_<musica::SulfateNucleationMethod>(carma, "SulfateNucleationMethod")
|
|
130
|
+
.value("NONE", musica::SulfateNucleationMethod::NONE)
|
|
131
|
+
.value("ZHAO_TURCO", musica::SulfateNucleationMethod::ZHAO_TURCO)
|
|
132
|
+
.value("VEHKAMAKI", musica::SulfateNucleationMethod::VEHKAMAKI)
|
|
133
|
+
.export_values();
|
|
134
|
+
|
|
135
|
+
carma.def(
|
|
136
|
+
"_create_carma",
|
|
137
|
+
[](py::dict params_dict)
|
|
138
|
+
{
|
|
139
|
+
// Convert Python dict to CARMAParameters
|
|
140
|
+
musica::CARMAParameters params;
|
|
141
|
+
|
|
142
|
+
if (params_dict.contains("nz"))
|
|
143
|
+
params.nz = params_dict["nz"].cast<int>();
|
|
144
|
+
if (params_dict.contains("nbin"))
|
|
145
|
+
params.nbin = params_dict["nbin"].cast<int>();
|
|
146
|
+
if (params_dict.contains("dtime"))
|
|
147
|
+
params.dtime = params_dict["dtime"].cast<double>();
|
|
148
|
+
|
|
149
|
+
// Handle groups configuration
|
|
150
|
+
if (params_dict.contains("groups"))
|
|
151
|
+
{
|
|
152
|
+
auto groups_py = params_dict["groups"];
|
|
153
|
+
if (!groups_py.is_none() && py::isinstance<py::list>(groups_py))
|
|
154
|
+
{
|
|
155
|
+
auto groups_list = groups_py.cast<py::list>();
|
|
156
|
+
for (auto group_py : groups_list)
|
|
157
|
+
{
|
|
158
|
+
auto group_dict = group_py.cast<py::dict>();
|
|
159
|
+
musica::CARMAGroupConfig group;
|
|
160
|
+
|
|
161
|
+
if (group_dict.contains("name"))
|
|
162
|
+
group.name = group_dict["name"].cast<std::string>();
|
|
163
|
+
if (group_dict.contains("shortname"))
|
|
164
|
+
group.shortname = group_dict["shortname"].cast<std::string>();
|
|
165
|
+
if (group_dict.contains("rmin"))
|
|
166
|
+
group.rmin = group_dict["rmin"].cast<double>();
|
|
167
|
+
if (group_dict.contains("rmrat"))
|
|
168
|
+
group.rmrat = group_dict["rmrat"].cast<double>();
|
|
169
|
+
if (group_dict.contains("rmassmin"))
|
|
170
|
+
group.rmassmin = group_dict["rmassmin"].cast<double>();
|
|
171
|
+
if (group_dict.contains("ishape"))
|
|
172
|
+
group.ishape = static_cast<musica::ParticleShape>(group_dict["ishape"].cast<int>());
|
|
173
|
+
if (group_dict.contains("eshape"))
|
|
174
|
+
group.eshape = group_dict["eshape"].cast<double>();
|
|
175
|
+
if (group_dict.contains("swelling_approach"))
|
|
176
|
+
{
|
|
177
|
+
auto swell_py = group_dict["swelling_approach"];
|
|
178
|
+
if (py::isinstance<py::dict>(swell_py))
|
|
179
|
+
{
|
|
180
|
+
auto swell_dict = swell_py.cast<py::dict>();
|
|
181
|
+
if (swell_dict.contains("algorithm"))
|
|
182
|
+
group.swelling_approach.algorithm =
|
|
183
|
+
static_cast<musica::ParticleSwellingAlgorithm>(swell_dict["algorithm"].cast<int>());
|
|
184
|
+
if (swell_dict.contains("composition"))
|
|
185
|
+
group.swelling_approach.composition =
|
|
186
|
+
static_cast<musica::ParticleSwellingComposition>(swell_dict["composition"].cast<int>());
|
|
187
|
+
}
|
|
188
|
+
else
|
|
189
|
+
{
|
|
190
|
+
throw py::type_error(
|
|
191
|
+
"Expected 'swelling_approach' to be a dictionary with 'algorithm' and 'composition' keys");
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (group_dict.contains("fall_velocity_routine"))
|
|
195
|
+
group.fall_velocity_routine =
|
|
196
|
+
static_cast<musica::FallVelocityAlgorithm>(group_dict["fall_velocity_routine"].cast<int>());
|
|
197
|
+
if (group_dict.contains("mie_calculation_algorithm"))
|
|
198
|
+
group.mie_calculation_algorithm =
|
|
199
|
+
static_cast<musica::MieCalculationAlgorithm>(group_dict["mie_calculation_algorithm"].cast<int>());
|
|
200
|
+
if (group_dict.contains("optics_algorithm"))
|
|
201
|
+
group.optics_algorithm = static_cast<musica::OpticsAlgorithm>(group_dict["optics_algorithm"].cast<int>());
|
|
202
|
+
if (group_dict.contains("is_ice"))
|
|
203
|
+
group.is_ice = group_dict["is_ice"].cast<bool>();
|
|
204
|
+
if (group_dict.contains("is_fractal"))
|
|
205
|
+
group.is_fractal = group_dict["is_fractal"].cast<bool>();
|
|
206
|
+
if (group_dict.contains("is_cloud"))
|
|
207
|
+
group.is_cloud = group_dict["is_cloud"].cast<bool>();
|
|
208
|
+
if (group_dict.contains("is_sulfate"))
|
|
209
|
+
group.is_sulfate = group_dict["is_sulfate"].cast<bool>();
|
|
210
|
+
if (group_dict.contains("do_wetdep"))
|
|
211
|
+
group.do_wetdep = group_dict["do_wetdep"].cast<bool>();
|
|
212
|
+
if (group_dict.contains("do_drydep"))
|
|
213
|
+
group.do_drydep = group_dict["do_drydep"].cast<bool>();
|
|
214
|
+
if (group_dict.contains("do_vtran"))
|
|
215
|
+
group.do_vtran = group_dict["do_vtran"].cast<bool>();
|
|
216
|
+
if (group_dict.contains("solfac"))
|
|
217
|
+
group.solfac = group_dict["solfac"].cast<double>();
|
|
218
|
+
if (group_dict.contains("scavcoef"))
|
|
219
|
+
group.scavcoef = group_dict["scavcoef"].cast<double>();
|
|
220
|
+
if (group_dict.contains("dpc_threshold"))
|
|
221
|
+
group.dpc_threshold = group_dict["dpc_threshold"].cast<double>();
|
|
222
|
+
if (group_dict.contains("rmon"))
|
|
223
|
+
group.rmon = group_dict["rmon"].cast<double>();
|
|
224
|
+
if (group_dict.contains("df"))
|
|
225
|
+
{
|
|
226
|
+
group.df = to_vector_double(group_dict["df"]);
|
|
227
|
+
}
|
|
228
|
+
if (group_dict.contains("falpha"))
|
|
229
|
+
group.falpha = group_dict["falpha"].cast<double>();
|
|
230
|
+
if (group_dict.contains("neutral_volfrc"))
|
|
231
|
+
group.neutral_volfrc = group_dict["neutral_volfrc"].cast<double>();
|
|
232
|
+
|
|
233
|
+
params.groups.push_back(group);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Handle elements configuration
|
|
239
|
+
if (params_dict.contains("elements"))
|
|
240
|
+
{
|
|
241
|
+
auto elements_py = params_dict["elements"];
|
|
242
|
+
if (!elements_py.is_none() && py::isinstance<py::list>(elements_py))
|
|
243
|
+
{
|
|
244
|
+
auto elements_list = elements_py.cast<py::list>();
|
|
245
|
+
for (auto element_py : elements_list)
|
|
246
|
+
{
|
|
247
|
+
auto element_dict = element_py.cast<py::dict>();
|
|
248
|
+
musica::CARMAElementConfig element;
|
|
249
|
+
|
|
250
|
+
if (element_dict.contains("igroup"))
|
|
251
|
+
element.igroup = element_dict["igroup"].cast<int>();
|
|
252
|
+
if (element_dict.contains("isolute"))
|
|
253
|
+
element.isolute = element_dict["isolute"].cast<int>();
|
|
254
|
+
if (element_dict.contains("name"))
|
|
255
|
+
element.name = element_dict["name"].cast<std::string>();
|
|
256
|
+
if (element_dict.contains("shortname"))
|
|
257
|
+
element.shortname = element_dict["shortname"].cast<std::string>();
|
|
258
|
+
if (element_dict.contains("itype"))
|
|
259
|
+
element.itype = static_cast<musica::ParticleType>(element_dict["itype"].cast<int>());
|
|
260
|
+
if (element_dict.contains("icomposition"))
|
|
261
|
+
element.icomposition = static_cast<musica::ParticleComposition>(element_dict["icomposition"].cast<int>());
|
|
262
|
+
if (element_dict.contains("is_shell"))
|
|
263
|
+
element.isShell = element_dict["is_shell"].cast<bool>();
|
|
264
|
+
if (element_dict.contains("rho"))
|
|
265
|
+
element.rho = element_dict["rho"].cast<double>();
|
|
266
|
+
if (element_dict.contains("rhobin"))
|
|
267
|
+
element.rhobin = to_vector_double(element_dict["rhobin"]);
|
|
268
|
+
if (element_dict.contains("arat"))
|
|
269
|
+
element.arat = to_vector_double(element_dict["arat"]);
|
|
270
|
+
if (element_dict.contains("kappa"))
|
|
271
|
+
element.kappa = element_dict["kappa"].cast<double>();
|
|
272
|
+
if (element_dict.contains("refidx"))
|
|
273
|
+
{
|
|
274
|
+
auto refidx_py = element_dict["refidx"];
|
|
275
|
+
if (!refidx_py.is_none() && py::isinstance<py::list>(refidx_py))
|
|
276
|
+
{
|
|
277
|
+
auto refidx_outer_list = refidx_py.cast<py::list>();
|
|
278
|
+
for (auto refidx_row_py : refidx_outer_list)
|
|
279
|
+
{
|
|
280
|
+
std::vector<musica::CARMAComplex> refidx_row;
|
|
281
|
+
if (!refidx_row_py.is_none() && py::isinstance<py::list>(refidx_row_py))
|
|
282
|
+
{
|
|
283
|
+
auto refidx_inner_list = refidx_row_py.cast<py::list>();
|
|
284
|
+
for (auto refidx_item : refidx_inner_list)
|
|
285
|
+
{
|
|
286
|
+
auto refidx_dict = refidx_item.cast<py::dict>();
|
|
287
|
+
musica::CARMAComplex refidx_value;
|
|
288
|
+
if (refidx_dict.contains("real"))
|
|
289
|
+
refidx_value.real = refidx_dict["real"].cast<double>();
|
|
290
|
+
if (refidx_dict.contains("imaginary"))
|
|
291
|
+
refidx_value.imaginary = refidx_dict["imaginary"].cast<double>();
|
|
292
|
+
refidx_row.push_back(refidx_value);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
element.refidx.push_back(refidx_row);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
params.elements.push_back(element);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Handle solutes configuration
|
|
305
|
+
if (params_dict.contains("solutes"))
|
|
306
|
+
{
|
|
307
|
+
auto solutes_py = params_dict["solutes"];
|
|
308
|
+
if (!solutes_py.is_none() && py::isinstance<py::list>(solutes_py))
|
|
309
|
+
{
|
|
310
|
+
auto solutes_list = solutes_py.cast<py::list>();
|
|
311
|
+
for (auto solute_py : solutes_list)
|
|
312
|
+
{
|
|
313
|
+
auto solute_dict = solute_py.cast<py::dict>();
|
|
314
|
+
musica::CARMASoluteConfig solute;
|
|
315
|
+
|
|
316
|
+
if (solute_dict.contains("name"))
|
|
317
|
+
solute.name = solute_dict["name"].cast<std::string>();
|
|
318
|
+
if (solute_dict.contains("shortname"))
|
|
319
|
+
solute.shortname = solute_dict["shortname"].cast<std::string>();
|
|
320
|
+
if (solute_dict.contains("ions"))
|
|
321
|
+
solute.ions = solute_dict["ions"].cast<int>();
|
|
322
|
+
if (solute_dict.contains("wtmol"))
|
|
323
|
+
solute.wtmol = solute_dict["wtmol"].cast<double>();
|
|
324
|
+
if (solute_dict.contains("rho"))
|
|
325
|
+
solute.rho = solute_dict["rho"].cast<double>();
|
|
326
|
+
|
|
327
|
+
params.solutes.push_back(solute);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Handle gases configuration
|
|
333
|
+
if (params_dict.contains("gases"))
|
|
334
|
+
{
|
|
335
|
+
auto gases_py = params_dict["gases"];
|
|
336
|
+
if (!gases_py.is_none() && py::isinstance<py::list>(gases_py))
|
|
337
|
+
{
|
|
338
|
+
auto gases_list = gases_py.cast<py::list>();
|
|
339
|
+
for (auto gas_py : gases_list)
|
|
340
|
+
{
|
|
341
|
+
auto gas_dict = gas_py.cast<py::dict>();
|
|
342
|
+
musica::CARMAGasConfig gas;
|
|
343
|
+
|
|
344
|
+
if (gas_dict.contains("name"))
|
|
345
|
+
gas.name = gas_dict["name"].cast<std::string>();
|
|
346
|
+
if (gas_dict.contains("shortname"))
|
|
347
|
+
gas.shortname = gas_dict["shortname"].cast<std::string>();
|
|
348
|
+
if (gas_dict.contains("wtmol"))
|
|
349
|
+
gas.wtmol = gas_dict["wtmol"].cast<double>();
|
|
350
|
+
if (gas_dict.contains("ivaprtn"))
|
|
351
|
+
gas.ivaprtn = static_cast<musica::VaporizationAlgorithm>(gas_dict["ivaprtn"].cast<int>());
|
|
352
|
+
if (gas_dict.contains("icomposition"))
|
|
353
|
+
gas.icomposition = static_cast<musica::GasComposition>(gas_dict["icomposition"].cast<int>());
|
|
354
|
+
if (gas_dict.contains("dgc_threshold"))
|
|
355
|
+
gas.dgc_threshold = gas_dict["dgc_threshold"].cast<double>();
|
|
356
|
+
if (gas_dict.contains("ds_threshold"))
|
|
357
|
+
gas.ds_threshold = gas_dict["ds_threshold"].cast<double>();
|
|
358
|
+
if (gas_dict.contains("refidx"))
|
|
359
|
+
{
|
|
360
|
+
auto refidx_py = gas_dict["refidx"];
|
|
361
|
+
if (!refidx_py.is_none() && py::isinstance<py::list>(refidx_py))
|
|
362
|
+
{
|
|
363
|
+
auto refidx_outer_list = refidx_py.cast<py::list>();
|
|
364
|
+
for (auto refidx_row_py : refidx_outer_list)
|
|
365
|
+
{
|
|
366
|
+
std::vector<musica::CARMAComplex> refidx_row;
|
|
367
|
+
if (!refidx_row_py.is_none() && py::isinstance<py::list>(refidx_row_py))
|
|
368
|
+
{
|
|
369
|
+
auto refidx_inner_list = refidx_row_py.cast<py::list>();
|
|
370
|
+
for (auto refidx_item : refidx_inner_list)
|
|
371
|
+
{
|
|
372
|
+
auto refidx_dict = refidx_item.cast<py::dict>();
|
|
373
|
+
musica::CARMAComplex refidx_value;
|
|
374
|
+
if (refidx_dict.contains("real"))
|
|
375
|
+
refidx_value.real = refidx_dict["real"].cast<double>();
|
|
376
|
+
if (refidx_dict.contains("imaginary"))
|
|
377
|
+
refidx_value.imaginary = refidx_dict["imaginary"].cast<double>();
|
|
378
|
+
refidx_row.push_back(refidx_value);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
gas.refidx.push_back(refidx_row);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
params.gases.push_back(gas);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Handle coagulation configuration
|
|
392
|
+
if (params_dict.contains("coagulations"))
|
|
393
|
+
{
|
|
394
|
+
auto coagulations_py = params_dict["coagulations"];
|
|
395
|
+
if (!coagulations_py.is_none() && py::isinstance<py::list>(coagulations_py))
|
|
396
|
+
{
|
|
397
|
+
auto coagulations_list = coagulations_py.cast<py::list>();
|
|
398
|
+
for (auto coagulation_py : coagulations_list)
|
|
399
|
+
{
|
|
400
|
+
auto coagulation_dict = coagulation_py.cast<py::dict>();
|
|
401
|
+
musica::CARMACoagulationConfig coagulation;
|
|
402
|
+
|
|
403
|
+
if (coagulation_dict.contains("igroup1"))
|
|
404
|
+
coagulation.igroup1 = coagulation_dict["igroup1"].cast<int>();
|
|
405
|
+
if (coagulation_dict.contains("igroup2"))
|
|
406
|
+
coagulation.igroup2 = coagulation_dict["igroup2"].cast<int>();
|
|
407
|
+
if (coagulation_dict.contains("igroup3"))
|
|
408
|
+
coagulation.igroup3 = coagulation_dict["igroup3"].cast<int>();
|
|
409
|
+
if (coagulation_dict.contains("algorithm"))
|
|
410
|
+
coagulation.algorithm =
|
|
411
|
+
static_cast<musica::ParticleCollectionAlgorithm>(coagulation_dict["algorithm"].cast<int>());
|
|
412
|
+
if (coagulation_dict.contains("ck0"))
|
|
413
|
+
coagulation.ck0 = coagulation_dict["ck0"].cast<double>();
|
|
414
|
+
if (coagulation_dict.contains("grav_e_coll0"))
|
|
415
|
+
coagulation.grav_e_coll0 = coagulation_dict["grav_e_coll0"].cast<double>();
|
|
416
|
+
if (coagulation_dict.contains("use_ccd"))
|
|
417
|
+
coagulation.use_ccd = coagulation_dict["use_ccd"].cast<bool>();
|
|
418
|
+
|
|
419
|
+
params.coagulations.push_back(coagulation);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Handle growth configuration
|
|
425
|
+
if (params_dict.contains("growths"))
|
|
426
|
+
{
|
|
427
|
+
auto growths_py = params_dict["growths"];
|
|
428
|
+
if (!growths_py.is_none() && py::isinstance<py::list>(growths_py))
|
|
429
|
+
{
|
|
430
|
+
auto growths_list = growths_py.cast<py::list>();
|
|
431
|
+
for (auto growth_py : growths_list)
|
|
432
|
+
{
|
|
433
|
+
auto growth_dict = growth_py.cast<py::dict>();
|
|
434
|
+
musica::CARMAGrowthConfig growth;
|
|
435
|
+
|
|
436
|
+
if (growth_dict.contains("ielem"))
|
|
437
|
+
growth.ielem = growth_dict["ielem"].cast<int>();
|
|
438
|
+
if (growth_dict.contains("igas"))
|
|
439
|
+
growth.igas = growth_dict["igas"].cast<int>();
|
|
440
|
+
|
|
441
|
+
params.growths.push_back(growth);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Handle nucleation configuration
|
|
447
|
+
if (params_dict.contains("nucleations"))
|
|
448
|
+
{
|
|
449
|
+
auto nucleations_py = params_dict["nucleations"];
|
|
450
|
+
if (!nucleations_py.is_none() && py::isinstance<py::list>(nucleations_py))
|
|
451
|
+
{
|
|
452
|
+
auto nucleations_list = nucleations_py.cast<py::list>();
|
|
453
|
+
for (auto nucleation_py : nucleations_list)
|
|
454
|
+
{
|
|
455
|
+
auto nucleation_dict = nucleation_py.cast<py::dict>();
|
|
456
|
+
musica::CARMANucleationConfig nucleation;
|
|
457
|
+
|
|
458
|
+
if (nucleation_dict.contains("ielemfrom"))
|
|
459
|
+
nucleation.ielemfrom = nucleation_dict["ielemfrom"].cast<int>();
|
|
460
|
+
if (nucleation_dict.contains("ielemto"))
|
|
461
|
+
nucleation.ielemto = nucleation_dict["ielemto"].cast<int>();
|
|
462
|
+
if (nucleation_dict.contains("algorithm"))
|
|
463
|
+
nucleation.algorithm =
|
|
464
|
+
static_cast<musica::ParticleNucleationAlgorithm>(nucleation_dict["algorithm"].cast<int>());
|
|
465
|
+
if (nucleation_dict.contains("rlh_nuc"))
|
|
466
|
+
nucleation.rlh_nuc = nucleation_dict["rlh_nuc"].cast<double>();
|
|
467
|
+
if (nucleation_dict.contains("igas"))
|
|
468
|
+
nucleation.igas = nucleation_dict["igas"].cast<int>();
|
|
469
|
+
if (nucleation_dict.contains("ievp2elem"))
|
|
470
|
+
nucleation.ievp2elem = nucleation_dict["ievp2elem"].cast<int>();
|
|
471
|
+
|
|
472
|
+
params.nucleations.push_back(nucleation);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Handle initialization configuration
|
|
478
|
+
if (params_dict.contains("initialization"))
|
|
479
|
+
{
|
|
480
|
+
auto initialization_py = params_dict["initialization"];
|
|
481
|
+
if (!initialization_py.is_none() && py::isinstance<py::dict>(initialization_py))
|
|
482
|
+
{
|
|
483
|
+
auto initialization_dict = initialization_py.cast<py::dict>();
|
|
484
|
+
|
|
485
|
+
if (initialization_dict.contains("do_cnst_rlh"))
|
|
486
|
+
params.initialization.do_cnst_rlh = initialization_dict["do_cnst_rlh"].cast<bool>();
|
|
487
|
+
if (initialization_dict.contains("do_detrain"))
|
|
488
|
+
params.initialization.do_detrain = initialization_dict["do_detrain"].cast<bool>();
|
|
489
|
+
if (initialization_dict.contains("do_fixedinit"))
|
|
490
|
+
params.initialization.do_fixedinit = initialization_dict["do_fixedinit"].cast<bool>();
|
|
491
|
+
if (initialization_dict.contains("do_incloud"))
|
|
492
|
+
params.initialization.do_incloud = initialization_dict["do_incloud"].cast<bool>();
|
|
493
|
+
if (initialization_dict.contains("do_explised"))
|
|
494
|
+
params.initialization.do_explised = initialization_dict["do_explised"].cast<bool>();
|
|
495
|
+
if (initialization_dict.contains("do_substep"))
|
|
496
|
+
params.initialization.do_substep = initialization_dict["do_substep"].cast<bool>();
|
|
497
|
+
if (initialization_dict.contains("do_thermo"))
|
|
498
|
+
params.initialization.do_thermo = initialization_dict["do_thermo"].cast<bool>();
|
|
499
|
+
if (initialization_dict.contains("do_vdiff"))
|
|
500
|
+
params.initialization.do_vdiff = initialization_dict["do_vdiff"].cast<bool>();
|
|
501
|
+
if (initialization_dict.contains("do_vtran"))
|
|
502
|
+
params.initialization.do_vtran = initialization_dict["do_vtran"].cast<bool>();
|
|
503
|
+
if (initialization_dict.contains("do_drydep"))
|
|
504
|
+
params.initialization.do_drydep = initialization_dict["do_drydep"].cast<bool>();
|
|
505
|
+
if (initialization_dict.contains("do_pheat"))
|
|
506
|
+
params.initialization.do_pheat = initialization_dict["do_pheat"].cast<bool>();
|
|
507
|
+
if (initialization_dict.contains("do_pheatatm"))
|
|
508
|
+
params.initialization.do_pheatatm = initialization_dict["do_pheatatm"].cast<bool>();
|
|
509
|
+
if (initialization_dict.contains("do_clearsky"))
|
|
510
|
+
params.initialization.do_clearsky = initialization_dict["do_clearsky"].cast<bool>();
|
|
511
|
+
if (initialization_dict.contains("do_partialinit"))
|
|
512
|
+
params.initialization.do_partialinit = initialization_dict["do_partialinit"].cast<bool>();
|
|
513
|
+
if (initialization_dict.contains("do_coremasscheck"))
|
|
514
|
+
params.initialization.do_coremasscheck = initialization_dict["do_coremasscheck"].cast<bool>();
|
|
515
|
+
if (initialization_dict.contains("sulfnucl_method"))
|
|
516
|
+
params.initialization.sulfnucl_method =
|
|
517
|
+
static_cast<musica::SulfateNucleationMethod>(initialization_dict["sulfnucl_method"].cast<int>());
|
|
518
|
+
if (initialization_dict.contains("vf_const"))
|
|
519
|
+
params.initialization.vf_const = initialization_dict["vf_const"].cast<double>();
|
|
520
|
+
if (initialization_dict.contains("minsubsteps"))
|
|
521
|
+
params.initialization.minsubsteps = initialization_dict["minsubsteps"].cast<int>();
|
|
522
|
+
if (initialization_dict.contains("maxsubsteps"))
|
|
523
|
+
params.initialization.maxsubsteps = initialization_dict["maxsubsteps"].cast<int>();
|
|
524
|
+
if (initialization_dict.contains("maxretries"))
|
|
525
|
+
params.initialization.maxretries = initialization_dict["maxretries"].cast<int>();
|
|
526
|
+
if (initialization_dict.contains("conmax"))
|
|
527
|
+
params.initialization.conmax = initialization_dict["conmax"].cast<double>();
|
|
528
|
+
if (initialization_dict.contains("dt_threshold"))
|
|
529
|
+
params.initialization.dt_threshold = initialization_dict["dt_threshold"].cast<double>();
|
|
530
|
+
if (initialization_dict.contains("cstick"))
|
|
531
|
+
params.initialization.cstick = initialization_dict["cstick"].cast<double>();
|
|
532
|
+
if (initialization_dict.contains("gsticki"))
|
|
533
|
+
params.initialization.gsticki = initialization_dict["gsticki"].cast<double>();
|
|
534
|
+
if (initialization_dict.contains("gstickl"))
|
|
535
|
+
params.initialization.gstickl = initialization_dict["gstickl"].cast<double>();
|
|
536
|
+
if (initialization_dict.contains("tstick"))
|
|
537
|
+
params.initialization.tstick = initialization_dict["tstick"].cast<double>();
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Handle wavelength bins
|
|
542
|
+
if (params_dict.contains("wavelength_bins"))
|
|
543
|
+
{
|
|
544
|
+
auto wavelength_bins_py = params_dict["wavelength_bins"];
|
|
545
|
+
if (!wavelength_bins_py.is_none() && py::isinstance<py::list>(wavelength_bins_py))
|
|
546
|
+
{
|
|
547
|
+
auto wavelength_bins_list = wavelength_bins_py.cast<py::list>();
|
|
548
|
+
for (auto bin_py : wavelength_bins_list)
|
|
549
|
+
{
|
|
550
|
+
auto bin_dict = bin_py.cast<py::dict>();
|
|
551
|
+
musica::CARMAWavelengthBin bin;
|
|
552
|
+
|
|
553
|
+
if (bin_dict.contains("center"))
|
|
554
|
+
bin.center = bin_dict["center"].cast<double>();
|
|
555
|
+
if (bin_dict.contains("width"))
|
|
556
|
+
bin.width = bin_dict["width"].cast<double>();
|
|
557
|
+
if (bin_dict.contains("do_emission"))
|
|
558
|
+
bin.do_emission = bin_dict["do_emission"].cast<bool>();
|
|
559
|
+
|
|
560
|
+
params.wavelength_bins.push_back(bin);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (params_dict.contains("number_of_refractive_indices"))
|
|
566
|
+
{
|
|
567
|
+
params.number_of_refractive_indices = params_dict["number_of_refractive_indices"].cast<int>();
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
try
|
|
571
|
+
{
|
|
572
|
+
auto carma_instance = new musica::CARMA(params);
|
|
573
|
+
return reinterpret_cast<std::uintptr_t>(carma_instance);
|
|
574
|
+
}
|
|
575
|
+
catch (const std::exception& e)
|
|
576
|
+
{
|
|
577
|
+
throw py::value_error("Error creating CARMA instance: " + std::string(e.what()));
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
"Create a CARMA instance");
|
|
581
|
+
|
|
582
|
+
carma.def(
|
|
583
|
+
"_delete_carma",
|
|
584
|
+
[](std::uintptr_t carma_ptr)
|
|
585
|
+
{
|
|
586
|
+
musica::CARMA* carma_instance = reinterpret_cast<musica::CARMA*>(carma_ptr);
|
|
587
|
+
delete carma_instance;
|
|
588
|
+
},
|
|
589
|
+
"Delete a CARMA instance");
|
|
590
|
+
|
|
591
|
+
carma.def(
|
|
592
|
+
"_get_dimensions",
|
|
593
|
+
[](std::uintptr_t carma_ptr)
|
|
594
|
+
{
|
|
595
|
+
musica::CARMA* carma_instance = reinterpret_cast<musica::CARMA*>(carma_ptr);
|
|
596
|
+
auto params = carma_instance->GetParameters();
|
|
597
|
+
py::dict result;
|
|
598
|
+
result["number_of_bins"] = params.nbin;
|
|
599
|
+
result["number_of_vertical_levels"] = params.nz;
|
|
600
|
+
result["number_of_wavelength_bins"] = params.wavelength_bins.size();
|
|
601
|
+
result["number_of_refractive_indices"] = params.number_of_refractive_indices;
|
|
602
|
+
result["number_of_groups"] = params.groups.size();
|
|
603
|
+
result["number_of_elements"] = params.elements.size();
|
|
604
|
+
result["number_of_solutes"] = params.solutes.size();
|
|
605
|
+
result["number_of_gases"] = params.gases.size();
|
|
606
|
+
return result;
|
|
607
|
+
},
|
|
608
|
+
"Get the dimensions of the CARMA instance");
|
|
609
|
+
|
|
610
|
+
carma.def(
|
|
611
|
+
"_get_group_properties",
|
|
612
|
+
[](std::uintptr_t carma_ptr, int group_index)
|
|
613
|
+
{
|
|
614
|
+
musica::CARMA* carma_instance = reinterpret_cast<musica::CARMA*>(carma_ptr);
|
|
615
|
+
musica::CARMAGroupProperties group_props = carma_instance->GetGroupProperties(group_index);
|
|
616
|
+
py::dict result;
|
|
617
|
+
|
|
618
|
+
result["bin_radius"] = group_props.bin_radius;
|
|
619
|
+
result["bin_radius_lower_bound"] = group_props.bin_radius_lower_bound;
|
|
620
|
+
result["bin_radius_upper_bound"] = group_props.bin_radius_upper_bound;
|
|
621
|
+
result["bin_width"] = group_props.bin_width;
|
|
622
|
+
result["bin_mass"] = group_props.bin_mass;
|
|
623
|
+
result["bin_width_mass"] = group_props.bin_width_mass;
|
|
624
|
+
result["bin_volume"] = group_props.bin_volume;
|
|
625
|
+
result["projected_area_ratio"] = group_props.projected_area_ratio;
|
|
626
|
+
result["radius_ratio"] = group_props.radius_ratio;
|
|
627
|
+
result["porosity_ratio"] = group_props.porosity_ratio;
|
|
628
|
+
result["extinction_coefficient"] = group_props.extinction_coefficient;
|
|
629
|
+
result["single_scattering_albedo"] = group_props.single_scattering_albedo;
|
|
630
|
+
result["asymmetry_factor"] = group_props.asymmetry_factor;
|
|
631
|
+
result["element_index_of_core_mass_elements"] = group_props.element_index_of_core_mass_elements;
|
|
632
|
+
result["number_of_monomers_per_bin"] = group_props.number_of_monomers_per_bin;
|
|
633
|
+
result["particle_number_element_for_group"] = group_props.particle_number_element_for_group;
|
|
634
|
+
result["number_of_core_mass_elements_for_group"] = group_props.number_of_core_mass_elements_for_group;
|
|
635
|
+
result["last_prognostic_bin"] = group_props.last_prognostic_bin;
|
|
636
|
+
|
|
637
|
+
return result;
|
|
638
|
+
},
|
|
639
|
+
"Get properties of a specific CARMA group");
|
|
640
|
+
|
|
641
|
+
carma.def(
|
|
642
|
+
"_get_element_properties",
|
|
643
|
+
[](std::uintptr_t carma_ptr, int element_index)
|
|
644
|
+
{
|
|
645
|
+
musica::CARMA* carma_instance = reinterpret_cast<musica::CARMA*>(carma_ptr);
|
|
646
|
+
musica::CARMAElementProperties element_props = carma_instance->GetElementProperties(element_index);
|
|
647
|
+
py::dict result;
|
|
648
|
+
|
|
649
|
+
result["group_index"] = element_props.group_index;
|
|
650
|
+
result["solute_index"] = element_props.solute_index;
|
|
651
|
+
result["type"] = element_props.type;
|
|
652
|
+
result["composition"] = element_props.composition;
|
|
653
|
+
result["is_shell"] = element_props.is_shell;
|
|
654
|
+
result["hygroscopicity_parameter"] = element_props.kappa;
|
|
655
|
+
result["mass_density"] = element_props.rho;
|
|
656
|
+
result["refractive_indices"] = element_props.refidx;
|
|
657
|
+
result["number_of_refractive_indices"] = element_props.number_of_refractive_indices;
|
|
658
|
+
|
|
659
|
+
return result;
|
|
660
|
+
},
|
|
661
|
+
"Get properties of a specific CARMA element");
|
|
662
|
+
|
|
663
|
+
carma.def(
|
|
664
|
+
"_create_carma_state",
|
|
665
|
+
[](std::uintptr_t carma_ptr, py::kwargs kwargs)
|
|
666
|
+
{
|
|
667
|
+
// Helper lambdas for robust flexible casting
|
|
668
|
+
musica::CARMAStateParameters params;
|
|
669
|
+
params.time = kwargs.contains("time") ? kwargs["time"].cast<double>() : 0.0;
|
|
670
|
+
params.time_step = kwargs.contains("time_step") ? kwargs["time_step"].cast<double>() : 0.0;
|
|
671
|
+
params.longitude = kwargs.contains("longitude") ? kwargs["longitude"].cast<double>() : 0.0;
|
|
672
|
+
params.latitude = kwargs.contains("latitude") ? kwargs["latitude"].cast<double>() : 0.0;
|
|
673
|
+
params.coordinates = kwargs.contains("coordinates")
|
|
674
|
+
? static_cast<musica::CarmaCoordinates>(kwargs["coordinates"].cast<int>())
|
|
675
|
+
: musica::CarmaCoordinates::CARTESIAN;
|
|
676
|
+
params.vertical_center = to_vector_double(kwargs["vertical_center"]);
|
|
677
|
+
params.vertical_levels = to_vector_double(kwargs["vertical_levels"]);
|
|
678
|
+
params.temperature = to_vector_double(kwargs["temperature"]);
|
|
679
|
+
params.original_temperature = to_vector_double(kwargs["original_temperature"]);
|
|
680
|
+
params.pressure = to_vector_double(kwargs["pressure"]);
|
|
681
|
+
params.pressure_levels = to_vector_double(kwargs["pressure_levels"]);
|
|
682
|
+
if (kwargs.contains("relative_humidity") && !kwargs["relative_humidity"].is_none())
|
|
683
|
+
{
|
|
684
|
+
params.relative_humidity = to_vector_double(kwargs["relative_humidity"]);
|
|
685
|
+
}
|
|
686
|
+
if (kwargs.contains("specific_humidity") && !kwargs["specific_humidity"].is_none())
|
|
687
|
+
{
|
|
688
|
+
params.specific_humidity = to_vector_double(kwargs["specific_humidity"]);
|
|
689
|
+
}
|
|
690
|
+
if (kwargs.contains("radiative_intensity") && !kwargs["radiative_intensity"].is_none())
|
|
691
|
+
{
|
|
692
|
+
auto array_2d = array_2d_to_vector_double(kwargs["radiative_intensity"]);
|
|
693
|
+
params.radiative_intensity = std::get<0>(array_2d);
|
|
694
|
+
params.radiative_intensity_dim_1_size = std::get<1>(array_2d);
|
|
695
|
+
params.radiative_intensity_dim_2_size = std::get<2>(array_2d);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
musica::CARMA* carma_instance = reinterpret_cast<musica::CARMA*>(carma_ptr);
|
|
699
|
+
try
|
|
700
|
+
{
|
|
701
|
+
auto carma_state = new musica::CARMAState(*carma_instance, params);
|
|
702
|
+
return reinterpret_cast<std::uintptr_t>(carma_state);
|
|
703
|
+
}
|
|
704
|
+
catch (const std::exception& e)
|
|
705
|
+
{
|
|
706
|
+
throw py::value_error("Error creating CARMA instance: " + std::string(e.what()));
|
|
707
|
+
}
|
|
708
|
+
},
|
|
709
|
+
py::arg("carma_pointer"),
|
|
710
|
+
"Create a CARMA state for a specific column with named arguments");
|
|
711
|
+
|
|
712
|
+
carma.def(
|
|
713
|
+
"_delete_carma_state",
|
|
714
|
+
[](std::uintptr_t carma_state_ptr)
|
|
715
|
+
{
|
|
716
|
+
auto carma_state = reinterpret_cast<musica::CARMAState*>(carma_state_ptr);
|
|
717
|
+
delete carma_state;
|
|
718
|
+
},
|
|
719
|
+
"Delete a CARMA state instance");
|
|
720
|
+
|
|
721
|
+
carma.def(
|
|
722
|
+
"_set_bin",
|
|
723
|
+
[](std::uintptr_t carma_state_ptr, int bin_index, int element_index, py::object value, double surface_mass)
|
|
724
|
+
{
|
|
725
|
+
auto carma_state = reinterpret_cast<musica::CARMAState*>(carma_state_ptr);
|
|
726
|
+
carma_state->SetBin(bin_index, element_index, to_vector_double(value), surface_mass);
|
|
727
|
+
},
|
|
728
|
+
"Set values for a specific bin and element in the CARMA state");
|
|
729
|
+
|
|
730
|
+
carma.def(
|
|
731
|
+
"_set_detrain",
|
|
732
|
+
[](std::uintptr_t carma_state_ptr, int bin_index, int element_index, py::object value)
|
|
733
|
+
{
|
|
734
|
+
auto carma_state = reinterpret_cast<musica::CARMAState*>(carma_state_ptr);
|
|
735
|
+
carma_state->SetDetrain(bin_index, element_index, to_vector_double(value));
|
|
736
|
+
},
|
|
737
|
+
"Set the mass of the detrained condensate for the bin");
|
|
738
|
+
|
|
739
|
+
carma.def(
|
|
740
|
+
"_set_gas",
|
|
741
|
+
[](std::uintptr_t carma_state_ptr,
|
|
742
|
+
int gas_index,
|
|
743
|
+
py::object value,
|
|
744
|
+
py::object old_mmr,
|
|
745
|
+
py::object gas_saturation_wrt_ice,
|
|
746
|
+
py::object gas_saturation_wrt_liquid)
|
|
747
|
+
{
|
|
748
|
+
auto carma_state = reinterpret_cast<musica::CARMAState*>(carma_state_ptr);
|
|
749
|
+
carma_state->SetGas(
|
|
750
|
+
gas_index,
|
|
751
|
+
to_vector_double(value),
|
|
752
|
+
to_vector_double(old_mmr),
|
|
753
|
+
to_vector_double(gas_saturation_wrt_ice),
|
|
754
|
+
to_vector_double(gas_saturation_wrt_liquid));
|
|
755
|
+
},
|
|
756
|
+
"Set the gas mass mixing ratio for a specific gas index in the CARMA state");
|
|
757
|
+
|
|
758
|
+
carma.def(
|
|
759
|
+
"_get_step_statistics",
|
|
760
|
+
[](std::uintptr_t carma_state_ptr)
|
|
761
|
+
{
|
|
762
|
+
auto carma_state = reinterpret_cast<musica::CARMAState*>(carma_state_ptr);
|
|
763
|
+
musica::CarmaStatistics stats = carma_state->GetStepStatistics();
|
|
764
|
+
py::dict result;
|
|
765
|
+
result["max_number_of_substeps"] = stats.max_number_of_substeps;
|
|
766
|
+
result["max_number_of_retries"] = stats.max_number_of_retries;
|
|
767
|
+
result["total_number_of_steps"] = stats.total_number_of_steps;
|
|
768
|
+
result["total_number_of_substeps"] = stats.total_number_of_substeps;
|
|
769
|
+
result["total_number_of_retries"] = stats.total_number_of_retries;
|
|
770
|
+
// check if stats.z_substeps is all -1s, if so, set the result to None
|
|
771
|
+
if (std::all_of(stats.z_substeps.begin(), stats.z_substeps.end(), [](int val) { return val == -1; }))
|
|
772
|
+
{
|
|
773
|
+
result["z_substeps"] = py::none();
|
|
774
|
+
}
|
|
775
|
+
else
|
|
776
|
+
{
|
|
777
|
+
result["z_substeps"] = stats.z_substeps;
|
|
778
|
+
}
|
|
779
|
+
result["xc"] = stats.xc;
|
|
780
|
+
result["yc"] = stats.yc;
|
|
781
|
+
return result;
|
|
782
|
+
},
|
|
783
|
+
"Get the step statistics for the current CARMAState");
|
|
784
|
+
|
|
785
|
+
carma.def(
|
|
786
|
+
"_get_bin",
|
|
787
|
+
[](std::uintptr_t carma_state_ptr, int bin_index, int element_index)
|
|
788
|
+
{
|
|
789
|
+
auto carma_state = reinterpret_cast<musica::CARMAState*>(carma_state_ptr);
|
|
790
|
+
musica::CarmaBinValues values = carma_state->GetBinValues(bin_index, element_index);
|
|
791
|
+
py::dict result;
|
|
792
|
+
result["mass_mixing_ratio"] = values.mass_mixing_ratio;
|
|
793
|
+
result["number_mixing_ratio"] = values.number_mixing_ratio;
|
|
794
|
+
result["number_density"] = values.number_density;
|
|
795
|
+
result["nucleation_rate"] = values.nucleation_rate;
|
|
796
|
+
result["wet_particle_radius"] = values.wet_particle_radius;
|
|
797
|
+
result["wet_particle_density"] = values.wet_particle_density;
|
|
798
|
+
result["dry_particle_density"] = values.dry_particle_density;
|
|
799
|
+
result["particle_mass_on_surface"] = values.particle_mass_on_surface;
|
|
800
|
+
result["sedimentation_flux"] = values.sedimentation_flux;
|
|
801
|
+
result["fall_velocity"] = values.fall_velocity;
|
|
802
|
+
result["deposition_velocity"] = values.deposition_velocity;
|
|
803
|
+
result["delta_particle_temperature"] = values.delta_particle_temperature;
|
|
804
|
+
result["kappa"] = values.kappa;
|
|
805
|
+
result["total_mass_mixing_ratio"] = values.total_mass_mixing_ratio;
|
|
806
|
+
return result;
|
|
807
|
+
},
|
|
808
|
+
"Get the values for a specific bin and element in the CARMA state");
|
|
809
|
+
|
|
810
|
+
carma.def(
|
|
811
|
+
"_get_detrain",
|
|
812
|
+
[](std::uintptr_t carma_state_ptr, int bin_index, int element_index)
|
|
813
|
+
{
|
|
814
|
+
auto carma_state = reinterpret_cast<musica::CARMAState*>(carma_state_ptr);
|
|
815
|
+
musica::CarmaDetrainValues values = carma_state->GetDetrain(bin_index, element_index);
|
|
816
|
+
py::dict result;
|
|
817
|
+
result["mass_mixing_ratio"] = values.mass_mixing_ratio;
|
|
818
|
+
result["number_mixing_ratio"] = values.number_mixing_ratio;
|
|
819
|
+
result["number_density"] = values.number_density;
|
|
820
|
+
result["wet_particle_radius"] = values.wet_particle_radius;
|
|
821
|
+
result["wet_particle_density"] = values.wet_particle_density;
|
|
822
|
+
return result;
|
|
823
|
+
},
|
|
824
|
+
"Get the detrained condensate values for a specific bin and element in the CARMA state");
|
|
825
|
+
|
|
826
|
+
carma.def(
|
|
827
|
+
"_get_gas",
|
|
828
|
+
[](std::uintptr_t carma_state_ptr, int gas_index)
|
|
829
|
+
{
|
|
830
|
+
auto carma_state = reinterpret_cast<musica::CARMAState*>(carma_state_ptr);
|
|
831
|
+
musica::CarmaGasValues values = carma_state->GetGas(gas_index);
|
|
832
|
+
py::dict result;
|
|
833
|
+
result["mass_mixing_ratio"] = values.mass_mixing_ratio;
|
|
834
|
+
result["gas_saturation_wrt_ice"] = values.gas_saturation_wrt_ice;
|
|
835
|
+
result["gas_saturation_wrt_liquid"] = values.gas_saturation_wrt_liquid;
|
|
836
|
+
result["gas_vapor_pressure_wrt_ice"] = values.gas_vapor_pressure_wrt_ice;
|
|
837
|
+
result["gas_vapor_pressure_wrt_liquid"] = values.gas_vapor_pressure_wrt_liquid;
|
|
838
|
+
result["weight_pct_aerosol_composition"] = values.weight_pct_aerosol_composition;
|
|
839
|
+
return result;
|
|
840
|
+
},
|
|
841
|
+
"Get the gas values for a specific element in the CARMA state");
|
|
842
|
+
|
|
843
|
+
carma.def(
|
|
844
|
+
"_get_environmental_values",
|
|
845
|
+
[](std::uintptr_t carma_state_ptr)
|
|
846
|
+
{
|
|
847
|
+
auto carma_state = reinterpret_cast<musica::CARMAState*>(carma_state_ptr);
|
|
848
|
+
musica::CarmaEnvironmentalValues values = carma_state->GetEnvironmentalValues();
|
|
849
|
+
py::dict result;
|
|
850
|
+
result["temperature"] = values.temperature;
|
|
851
|
+
result["pressure"] = values.pressure;
|
|
852
|
+
result["air_density"] = values.air_density;
|
|
853
|
+
if (std::all_of(values.latent_heat.begin(), values.latent_heat.end(), [](double val) { return val == -1; }))
|
|
854
|
+
{
|
|
855
|
+
result["latent_heat"] = py::none();
|
|
856
|
+
}
|
|
857
|
+
else
|
|
858
|
+
{
|
|
859
|
+
result["latent_heat"] = values.latent_heat;
|
|
860
|
+
}
|
|
861
|
+
return result;
|
|
862
|
+
},
|
|
863
|
+
"Get the state values for the current CARMAState");
|
|
864
|
+
|
|
865
|
+
carma.def(
|
|
866
|
+
"_set_temperature",
|
|
867
|
+
[](std::uintptr_t carma_state_ptr, py::object temperature)
|
|
868
|
+
{
|
|
869
|
+
auto carma_state = reinterpret_cast<musica::CARMAState*>(carma_state_ptr);
|
|
870
|
+
carma_state->SetTemperature(to_vector_double(temperature));
|
|
871
|
+
},
|
|
872
|
+
"Set the temperature profile [K] for the CARMA state");
|
|
873
|
+
|
|
874
|
+
carma.def(
|
|
875
|
+
"_set_air_density",
|
|
876
|
+
[](std::uintptr_t carma_state_ptr, py::object air_density)
|
|
877
|
+
{
|
|
878
|
+
auto carma_state = reinterpret_cast<musica::CARMAState*>(carma_state_ptr);
|
|
879
|
+
carma_state->SetAirDensity(to_vector_double(air_density));
|
|
880
|
+
},
|
|
881
|
+
"Set the air density profile [kg/m³] for the CARMA state");
|
|
882
|
+
|
|
883
|
+
carma.def(
|
|
884
|
+
"_step",
|
|
885
|
+
[](std::uintptr_t carma_state_ptr, py::kwargs kwargs)
|
|
886
|
+
{
|
|
887
|
+
auto carma_state = reinterpret_cast<musica::CARMAState*>(carma_state_ptr);
|
|
888
|
+
musica::CARMAStateStepConfig step_config;
|
|
889
|
+
|
|
890
|
+
if (kwargs.contains("cloud_fraction"))
|
|
891
|
+
step_config.cloud_fraction = to_vector_double(kwargs["cloud_fraction"]);
|
|
892
|
+
if (kwargs.contains("critical_relative_humidity"))
|
|
893
|
+
step_config.critical_relative_humidity = to_vector_double(kwargs["critical_relative_humidity"]);
|
|
894
|
+
if (kwargs.contains("land"))
|
|
895
|
+
step_config.land = to_surface_properties(kwargs["land"]);
|
|
896
|
+
if (kwargs.contains("ocean"))
|
|
897
|
+
step_config.ocean = to_surface_properties(kwargs["ocean"]);
|
|
898
|
+
if (kwargs.contains("ice"))
|
|
899
|
+
step_config.ice = to_surface_properties(kwargs["ice"]);
|
|
900
|
+
|
|
901
|
+
try
|
|
902
|
+
{
|
|
903
|
+
carma_state->Step(step_config);
|
|
904
|
+
}
|
|
905
|
+
catch (const std::exception& e)
|
|
906
|
+
{
|
|
907
|
+
throw py::value_error("Error stepping CARMA state: " + std::string(e.what()));
|
|
908
|
+
}
|
|
909
|
+
},
|
|
910
|
+
"Step the CARMA state with specified parameters");
|
|
911
|
+
}
|