musica 0.12.0__cp311-cp311-win32.whl → 0.12.1__cp311-cp311-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of musica might be problematic. Click here for more details.

Files changed (75) hide show
  1. musica/CMakeLists.txt +28 -2
  2. musica/__init__.py +9 -49
  3. musica/_musica.cp311-win32.pyd +0 -0
  4. musica/_version.py +1 -1
  5. musica/backend.py +41 -0
  6. musica/binding_common.cpp +23 -6
  7. musica/carma.cpp +911 -0
  8. musica/carma.py +1729 -0
  9. musica/constants.py +1 -1
  10. musica/cpu_binding.cpp +2 -1
  11. musica/cuda.py +4 -1
  12. musica/examples/__init__.py +1 -0
  13. musica/examples/carma_aluminum.py +123 -0
  14. musica/examples/carma_sulfate.py +245 -0
  15. musica/examples/examples.py +165 -0
  16. musica/examples/sulfate_box_model.py +439 -0
  17. musica/examples/ts1_latin_hypercube.py +245 -0
  18. musica/gpu_binding.cpp +2 -1
  19. musica/main.py +89 -0
  20. musica/mechanism_configuration/__init__.py +1 -1
  21. musica/mechanism_configuration/aqueous_equilibrium.py +227 -54
  22. musica/mechanism_configuration/arrhenius.py +228 -42
  23. musica/mechanism_configuration/branched.py +249 -66
  24. musica/mechanism_configuration/condensed_phase_arrhenius.py +243 -50
  25. musica/mechanism_configuration/condensed_phase_photolysis.py +16 -19
  26. musica/mechanism_configuration/emission.py +10 -6
  27. musica/mechanism_configuration/first_order_loss.py +133 -26
  28. musica/mechanism_configuration/henrys_law.py +7 -48
  29. musica/mechanism_configuration/mechanism_configuration.py +114 -41
  30. musica/mechanism_configuration/phase.py +6 -2
  31. musica/mechanism_configuration/photolysis.py +12 -7
  32. musica/mechanism_configuration/reactions.py +20 -8
  33. musica/mechanism_configuration/simpol_phase_transfer.py +180 -51
  34. musica/mechanism_configuration/species.py +23 -4
  35. musica/mechanism_configuration/surface.py +14 -9
  36. musica/mechanism_configuration/ternary_chemical_activation.py +352 -0
  37. musica/mechanism_configuration/troe.py +259 -44
  38. musica/mechanism_configuration/tunneling.py +196 -49
  39. musica/mechanism_configuration/user_defined.py +9 -4
  40. musica/mechanism_configuration/wet_deposition.py +11 -8
  41. musica/mechanism_configuration.cpp +184 -95
  42. musica/musica.cpp +48 -61
  43. musica/test/examples/v1/full_configuration/full_configuration.json +39 -22
  44. musica/test/examples/v1/full_configuration/full_configuration.yaml +29 -20
  45. musica/test/{test_analytical.py → integration/test_analytical.py} +0 -1
  46. musica/test/integration/test_carma.py +227 -0
  47. musica/test/integration/test_carma_aluminum.py +11 -0
  48. musica/test/integration/test_carma_sulfate.py +16 -0
  49. musica/test/integration/test_sulfate_box_model.py +34 -0
  50. musica/test/integration/test_tuvx.py +62 -0
  51. musica/test/unit/test_parser.py +64 -0
  52. musica/test/{test_serializer.py → unit/test_serializer.py} +2 -2
  53. musica/test/{test_util_full_mechanism.py → unit/test_util_full_mechanism.py} +152 -122
  54. musica/tools/prepare_build_environment_linux.sh +39 -32
  55. musica/tools/prepare_build_environment_macos.sh +1 -0
  56. musica/tuvx.cpp +93 -0
  57. musica/tuvx.py +199 -0
  58. musica/types.py +104 -60
  59. {musica-0.12.0.dist-info → musica-0.12.1.dist-info}/METADATA +40 -39
  60. musica-0.12.1.dist-info/RECORD +69 -0
  61. {musica-0.12.0.dist-info → musica-0.12.1.dist-info}/WHEEL +1 -1
  62. musica-0.12.1.dist-info/entry_points.txt +3 -0
  63. musica/test/examples/v0/config.json +0 -7
  64. musica/test/examples/v0/config.yaml +0 -3
  65. musica/test/examples/v0/reactions.json +0 -193
  66. musica/test/examples/v0/reactions.yaml +0 -142
  67. musica/test/examples/v0/species.json +0 -40
  68. musica/test/examples/v0/species.yaml +0 -19
  69. musica/test/test_parser.py +0 -57
  70. musica/test/tuvx.py +0 -10
  71. musica/tools/prepare_build_environment_windows.sh +0 -22
  72. musica-0.12.0.dist-info/RECORD +0 -57
  73. /musica/test/{test_chapman.py → integration/test_chapman.py} +0 -0
  74. {musica-0.12.0.dist-info → musica-0.12.1.dist-info}/licenses/AUTHORS.md +0 -0
  75. {musica-0.12.0.dist-info → musica-0.12.1.dist-info}/licenses/LICENSE +0 -0
musica/constants.py CHANGED
@@ -1,3 +1,3 @@
1
1
  AVOGADRO = 6.02214076e23 # mol^-1
2
2
  BOLTZMANN = 1.380649e-23 # J K^-1
3
- GAS_CONSTANT = AVOGADRO * BOLTZMANN # J K^-1 mol^-1
3
+ GAS_CONSTANT = AVOGADRO * BOLTZMANN # J K^-1 mol^-1
musica/cpu_binding.cpp CHANGED
@@ -1,9 +1,10 @@
1
1
  // Copyright (C) 2023-2025 University Corporation for Atmospheric Research
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- #include <pybind11/pybind11.h>
5
4
  #include "binding_common.hpp"
6
5
 
6
+ #include <pybind11/pybind11.h>
7
+
7
8
  PYBIND11_MODULE(_musica, m)
8
9
  {
9
10
  bind_all(m);
musica/cuda.py CHANGED
@@ -1,4 +1,7 @@
1
- from . import _backend
1
+ from . import backend
2
+
3
+ _backend = backend.get_backend()
4
+
2
5
 
3
6
  def is_cuda_available() -> bool:
4
7
  """
@@ -0,0 +1 @@
1
+ from .examples import Examples
@@ -0,0 +1,123 @@
1
+ """
2
+ Python example for CARMA aluminum aerosols.
3
+
4
+ This script creates one grid box with an initial concentration of aluminum particles
5
+ and allows them to coagulate.
6
+ """
7
+ import xarray as xr
8
+ import numpy as np
9
+ import musica
10
+ import ussa1976
11
+
12
+ available = musica.backend.carma_available()
13
+
14
+ def run_carma_aluminum_example():
15
+ group = musica.carma.CARMAGroupConfig(
16
+ name="aluminum",
17
+ shortname="PRALUM",
18
+ rmrat=2.0,
19
+ rmin=21.5e-6,
20
+ rmon=21.5e-6,
21
+ ishape=musica.carma.ParticleShape.SPHERE,
22
+ eshape=1.0,
23
+ mie_calculation_algorithm=musica.carma.MieCalculationAlgorithm.TOON_1981,
24
+ is_ice=False,
25
+ is_fractal=True,
26
+ do_wetdep=False,
27
+ do_drydep=True,
28
+ do_vtran=True,
29
+ solfac=0.0,
30
+ scavcoef=0.0,
31
+ df=[1.6] * 5, # 5 bins with fractal dimension 1.6
32
+ falpha=1.0
33
+ )
34
+
35
+ # Create aluminum element
36
+ element = musica.carma.CARMAElementConfig(
37
+ igroup=1,
38
+ isolute=0,
39
+ name="Aluminum",
40
+ shortname="ALUM",
41
+ itype=musica.carma.ParticleType.INVOLATILE,
42
+ icomposition=musica.carma.ParticleComposition.ALUMINUM,
43
+ rho=0.00395, # kg/m3
44
+ arat=[1.0] * 5, # 5 bins with area ratio 1.0
45
+ kappa=0.0,
46
+ )
47
+
48
+ # Create coagulation
49
+ coagulation = musica.carma.CARMACoagulationConfig(
50
+ igroup1=1,
51
+ igroup2=1,
52
+ igroup3=1,
53
+ algorithm=musica.carma.ParticleCollectionAlgorithm.FUCHS)
54
+
55
+ params = musica.carma.CARMAParameters(
56
+ nbin=5,
57
+ nz=1,
58
+ dtime=1800.0,
59
+ groups=[group],
60
+ elements=[element],
61
+ coagulations=[coagulation]
62
+ )
63
+
64
+ FIVE_DAYS_IN_SECONDS = 432000
65
+ params.nstep = FIVE_DAYS_IN_SECONDS // params.dtime
66
+ params.initialization.do_vtran = False
67
+
68
+ n_levels = params.nz
69
+ deltaz = 1000.0
70
+ zmin = 16500.0
71
+
72
+ vertical_center = zmin + (np.arange(n_levels) + 0.5) * deltaz
73
+ vertical_levels = zmin + np.arange(n_levels + 1) * deltaz
74
+
75
+ centered_variables = ussa1976.compute(z=vertical_center, variables=["t", "p", "rho"])
76
+ edge_variables = ussa1976.compute(z=vertical_levels, variables=["p"])
77
+
78
+ temperature = centered_variables.t.values
79
+ pressure = centered_variables.p.values
80
+ pressure_levels = edge_variables.p.values
81
+ density = centered_variables.rho.values
82
+
83
+ carma = musica.CARMA(params)
84
+
85
+ mmr_initial = 5e9 / (deltaz * 2.57474699e14) / density[0]
86
+
87
+ state = carma.create_state(
88
+ time_step=params.dtime,
89
+ temperature=temperature,
90
+ pressure=pressure,
91
+ pressure_levels=pressure_levels,
92
+ vertical_center=vertical_center,
93
+ vertical_levels=vertical_levels,
94
+ longitude=0.0,
95
+ latitude=-105.0,
96
+ coordinates=musica.carma.CarmaCoordinates.CARTESIAN,
97
+ )
98
+
99
+ for i in range(params.nbin):
100
+ for j in range(len(params.elements)):
101
+ state.set_bin(i + 1, j + 1, mmr_initial)
102
+
103
+ bin_data = state.get_bins()
104
+ bin_data = bin_data.expand_dims({"time": [0]})
105
+ env = state.get_environmental_values()
106
+ env = env.expand_dims({"time": [0]})
107
+
108
+ # Run the simulation for the specified number of steps
109
+ for step in range(1, int(params.nstep)):
110
+ state.step()
111
+ bin_data = xr.concat([bin_data, state.get_bins().expand_dims({"time": [step * params.dtime]})], dim="time")
112
+ env = xr.concat([env, state.get_environmental_values().expand_dims(
113
+ {"time": [step * params.dtime]})], dim="time")
114
+
115
+ return xr.merge([bin_data, env])
116
+
117
+
118
+ if __name__ == '__main__':
119
+ if not available:
120
+ print("CARMA backend is not available.")
121
+ else:
122
+ state = run_carma_aluminum_example()
123
+ print(state)
@@ -0,0 +1,245 @@
1
+ """
2
+ Python example for CARMA sulfate that mimics the logic of the CARMA Fortran test.
3
+
4
+ This script creates one grid box with an initial concentration of sulfate particles
5
+ at the smallest size, then allows that to grow using H2SO4 gas. The total mass
6
+ of particles + gas should be conserved.
7
+
8
+ Based on carma_sulfatetest.F90
9
+ """
10
+
11
+ import xarray as xr
12
+ import numpy as np
13
+ import musica
14
+
15
+ available = musica.backend.carma_available()
16
+
17
+ def run_carma_sulfate_example():
18
+ """Test CARMA sulfate growth from gas condensation in a simple single grid box."""
19
+
20
+ GRAVITY = 9.806 # Acceleration due to gravity in m/s^2
21
+
22
+ # Simplified constants for debugging
23
+ NZ = 1
24
+ NELEM = 1
25
+ NBIN = 38
26
+
27
+ dtime = 1800.0 # Time step in seconds
28
+ deltaz = 10000.0 # Grid box height in meters
29
+ nsteps = int(180000 / dtime)
30
+
31
+ # Sulfate density
32
+ rho_sulfate_kg_m3 = 1923.0 # kg/m³
33
+
34
+ # Grid setup
35
+ latitude = -40.0
36
+ longitude = -105.0
37
+ vertical_center = [17000.0]
38
+ vertical_levels = [vertical_center[0] - deltaz, vertical_center[0] + deltaz]
39
+
40
+ # Standard atmosphere
41
+ pressure_centers = np.array([9000.0])
42
+ temperature_centers = np.array([250.0])
43
+ # this is directly from the original CARMA test, but it seems like there are some unit conversion issues
44
+ air_mass_density_centers = pressure_centers * 10.0 / (8.31430e07 / 28.966 * temperature_centers) * (1.0e-3 * 1.0e6)
45
+ pressure_levels = np.zeros(2)
46
+ pressure_levels[0] = pressure_centers[0] - \
47
+ (vertical_levels[0] - vertical_center[0]) * air_mass_density_centers[0] * GRAVITY
48
+ pressure_levels[1] = pressure_centers[0] - \
49
+ (vertical_levels[1] - vertical_center[0]) * air_mass_density_centers[0] * GRAVITY
50
+
51
+ # Initial mass mixing ratios
52
+ mass_mixing_ratios = {
53
+ "H2O": [1.0e-4],
54
+ "H2SO4": [0.1e-9 * (98.0 / 29.0)],
55
+ "SULFATE": [[0.0 for _ in range(NBIN)]]
56
+ }
57
+ satliq = {
58
+ "H2O": [-1.0],
59
+ "H2SO4": [-1.0]
60
+ }
61
+ satice = {
62
+ "H2O": [-1.0],
63
+ "H2SO4": [-1.0]
64
+ }
65
+
66
+ # Set up CARMA parameters
67
+ params = musica.CARMAParameters()
68
+ params.nz = NZ
69
+ params.nbin = NBIN
70
+
71
+ # Create sulfate group - simplified
72
+ sulfate_group = musica.carma.CARMAGroupConfig(
73
+ name="sulfate",
74
+ shortname="SULF",
75
+ rmin=2.e-10, # Minimum radius in m
76
+ rmrat=2.0, # Mass ratio between bins
77
+ swelling_approach={
78
+ "algorithm": musica.carma.ParticleSwellingAlgorithm.WEIGHT_PERCENT_H2SO4,
79
+ "composition": musica.carma.ParticleSwellingComposition.NONE
80
+ },
81
+ do_drydep=True,
82
+ is_sulfate=True
83
+ )
84
+ params.add_group(sulfate_group)
85
+
86
+ # Create sulfate element
87
+ sulfate_element = musica.carma.CARMAElementConfig(
88
+ igroup=1,
89
+ name="Sulfate",
90
+ shortname="SULF",
91
+ rho=rho_sulfate_kg_m3,
92
+ itype=musica.carma.ParticleType.VOLATILE,
93
+ icomposition=musica.carma.ParticleComposition.SULFURIC_ACID
94
+ )
95
+ params.add_element(sulfate_element)
96
+
97
+ # Create gases - simplified to match successful test_carma.py pattern
98
+ water_gas = musica.carma.CARMAGasConfig(
99
+ name="Water Vapor",
100
+ shortname="H2O",
101
+ wtmol=0.018015, # kg/mol
102
+ ivaprtn=musica.carma.VaporizationAlgorithm.H2O_MURPHY_2005,
103
+ icomposition=musica.carma.GasComposition.H2O,
104
+ dgc_threshold=0.1,
105
+ ds_threshold=0.1
106
+ )
107
+ params.add_gas(water_gas)
108
+
109
+ # Create H2SO4 gas
110
+ h2so4_gas = musica.carma.CARMAGasConfig(
111
+ name="Sulfuric Acid",
112
+ shortname="H2SO4",
113
+ wtmol=0.098079, # kg/mol
114
+ ivaprtn=musica.carma.VaporizationAlgorithm.H2SO4_AYERS_1980,
115
+ icomposition=musica.carma.GasComposition.H2SO4,
116
+ dgc_threshold=0.1,
117
+ ds_threshold=0.1
118
+ )
119
+ params.add_gas(h2so4_gas)
120
+
121
+ # Add growth process
122
+ growth = musica.carma.CARMAGrowthConfig(
123
+ ielem=1, # Sulfate element
124
+ igas=2 # H2SO4 gas
125
+ )
126
+ params.add_growth(growth)
127
+
128
+ # Add nucleation process
129
+ nucleation = musica.carma.CARMANucleationConfig(
130
+ ielemfrom=1,
131
+ ielemto=1,
132
+ algorithm=musica.carma.ParticleNucleationAlgorithm.HOMOGENEOUS_NUCLEATION,
133
+ rlh_nuc=0.0,
134
+ igas=2 # H2SO4 gas
135
+ )
136
+ params.add_nucleation(nucleation)
137
+
138
+ # Add coagulation
139
+ coagulation = musica.carma.CARMACoagulationConfig(
140
+ igroup1=1,
141
+ igroup2=1,
142
+ igroup3=1,
143
+ algorithm=musica.carma.ParticleCollectionAlgorithm.FUCHS
144
+ )
145
+ params.add_coagulation(coagulation)
146
+
147
+ # Initialization
148
+ params.initialization.do_substep = True
149
+ params.initialization.do_thermo = True
150
+ params.initialization.maxretries = 16
151
+ params.initialization.maxsubsteps = 32
152
+ params.initialization.dt_threshold = 1.0
153
+ params.initialization.sulfnucl_method = musica.carma.SulfateNucleationMethod.ZHAO_TURCO.value
154
+
155
+ # Create CARMA instance
156
+ carma = musica.CARMA(params)
157
+
158
+ # Output group properties
159
+ group_props, _ = carma.get_group_properties()
160
+ # Print bin radius and bin mass from group_props xarray Dataset
161
+ for i_bin in range(NBIN):
162
+ print(
163
+ f"Bin {i_bin + 1}: bin radius = {group_props.isel(bin=i_bin)['bin_radius']}; bin mass = {group_props.isel(bin=i_bin)['bin_mass']}"
164
+ )
165
+
166
+ bin_state = None
167
+ gas_state = None
168
+ env_state = None
169
+
170
+ for i_step in range(nsteps):
171
+
172
+ # Create state
173
+ state = carma.create_state(
174
+ time_step=dtime,
175
+ temperature=temperature_centers,
176
+ original_temperature=temperature_centers,
177
+ pressure=pressure_centers,
178
+ pressure_levels=pressure_levels,
179
+ vertical_center=vertical_center,
180
+ vertical_levels=vertical_levels,
181
+ longitude=longitude,
182
+ latitude=latitude,
183
+ coordinates=musica.carma.CarmaCoordinates.CARTESIAN,
184
+ specific_humidity=mass_mixing_ratios["H2O"],
185
+ )
186
+
187
+ # Initialize particle concentrations to zero
188
+ for ibin in range(1, NBIN + 1):
189
+ for ielem in range(1, NELEM + 1):
190
+ state.set_bin(ibin, ielem, mass_mixing_ratios["SULFATE"][0][ibin - 1])
191
+
192
+ # Set H2O concentration
193
+ state.set_gas(
194
+ gas_index=1,
195
+ value=mass_mixing_ratios["H2O"],
196
+ old_mmr=mass_mixing_ratios["H2O"],
197
+ gas_saturation_wrt_ice=satice["H2O"],
198
+ gas_saturation_wrt_liquid=satliq["H2O"],
199
+ )
200
+
201
+ # Set H2SO4 concentration
202
+ state.set_gas(
203
+ gas_index=2,
204
+ value=[mmr * 1.05 for mmr in mass_mixing_ratios["H2SO4"]],
205
+ old_mmr=mass_mixing_ratios["H2SO4"],
206
+ gas_saturation_wrt_ice=satice["H2SO4"],
207
+ gas_saturation_wrt_liquid=satliq["H2SO4"],
208
+ )
209
+
210
+ # Perform model step
211
+ state.step()
212
+ stats = state.get_step_statistics()
213
+ print(f"Step {i_step + 1}/{nsteps}: {stats}")
214
+
215
+ # Get updated state values
216
+ if bin_state is None:
217
+ bin_state = state.get_bins()
218
+ bin_state = bin_state.expand_dims({"time": [i_step * dtime]})
219
+ else:
220
+ bin_state = xr.concat([bin_state, state.get_bins().expand_dims({"time": [i_step * dtime]})], dim="time")
221
+
222
+ if gas_state is None:
223
+ gas_state = state.get_gases()[0]
224
+ gas_state = gas_state.expand_dims({"time": [i_step * dtime]})
225
+ else:
226
+ gas_state = xr.concat([gas_state, state.get_gases()[0].expand_dims({"time": [i_step * dtime]})], dim="time")
227
+
228
+ if env_state is None:
229
+ env_state = state.get_environmental_values()
230
+ env_state = env_state.expand_dims({"time": [i_step * dtime]})
231
+ else:
232
+ env_state = xr.concat([env_state, state.get_environmental_values(
233
+ ).expand_dims({"time": [i_step * dtime]})], dim="time")
234
+
235
+ return env_state, gas_state, bin_state
236
+
237
+
238
+ if __name__ == '__main__':
239
+ if not available:
240
+ print("CARMA backend is not available.")
241
+ else:
242
+ env_state, gas_state, bin_state = run_carma_sulfate_example()
243
+ print(env_state)
244
+ print(gas_state)
245
+ print(bin_state)
@@ -0,0 +1,165 @@
1
+ """MUSICA Examples Module.
2
+
3
+ This module provides a centralized way to access and manage MUSICA examples.
4
+ It defines the Example class for representing individual examples and the
5
+ Examples singleton for accessing all available examples.
6
+
7
+ Example:
8
+ >>> from musica.examples import Examples
9
+ >>> print(Examples.TS1LatinHyperCube)
10
+ >>> for example in Examples:
11
+ ... print(example.name)
12
+ """
13
+
14
+
15
+ class Example:
16
+ """A class representing a MUSICA example.
17
+
18
+ This class encapsulates information about a specific example, including its name,
19
+ short name, description, and file path. It provides methods for string representation
20
+ and class-based construction.
21
+ """
22
+
23
+ def __init__(self, name, short_name, description, path):
24
+ """Initialize an Example instance.
25
+
26
+ Args:
27
+ name (str): The display name of the example.
28
+ short_name (str): A short identifier for the example.
29
+ description (str): A detailed description of what the example demonstrates.
30
+ path (str): The file path to the example script.
31
+ """
32
+ self.name = name
33
+ self.short_name = short_name
34
+ self.description = description
35
+ self.path = path
36
+
37
+ def __str__(self):
38
+ """Return a string representation of the example.
39
+
40
+ Returns:
41
+ str: A formatted string showing the name and description.
42
+ """
43
+ return f'{self.name}: {self.description}'
44
+
45
+ def __repr__(self):
46
+ """Return a string representation of the example for debugging.
47
+
48
+ Returns:
49
+ str: A formatted string showing the name and description.
50
+ """
51
+ return f'{self.name}: {self.description}'
52
+
53
+ @classmethod
54
+ def from_config(cls, display_name, path, short_name, description):
55
+ """Create an Example instance from configuration parameters.
56
+
57
+ This class method provides an alternative constructor that creates an Example
58
+ instance with more explicit parameter naming.
59
+
60
+ Args:
61
+ display_name (str): The display name of the example.
62
+ path (str): The file path to the example script.
63
+ short_name (str): A short identifier for the example.
64
+ description (str): A detailed description of what the example demonstrates.
65
+
66
+ Returns:
67
+ Example: A new Example instance with the provided configuration.
68
+ """
69
+ return cls(name=display_name, short_name=short_name, description=description, path=path)
70
+
71
+
72
+ class _Examples:
73
+ """A container class for managing MUSICA examples.
74
+
75
+ This class provides a centralized way to access and manage all available MUSICA
76
+ examples. It includes predefined examples and supports iteration, indexing, and
77
+ attribute access patterns.
78
+ """
79
+ CARMA_Aluminum = Example.from_config(
80
+ display_name='CARMA Aluminum',
81
+ short_name='CARMA_Aluminum',
82
+ path='carma_aluminum.py',
83
+ description='A CARMA example for simulating aluminum aerosol particles.')
84
+ CARMA_Sulfate = Example.from_config(
85
+ display_name='CARMA Sulfate',
86
+ short_name='CARMA_Sulfate',
87
+ path='carma_sulfate.py',
88
+ description='A CARMA example for simulating sulfate aerosol particles.')
89
+ Sulfate_Box_Model = Example.from_config(
90
+ display_name='Sulfate Box Model',
91
+ short_name='Sulfate_Box_Model',
92
+ path='sulfate_box_model.py',
93
+ description='A box model example for simulating sulfate aerosol particles.')
94
+ TS1LatinHyperCube = Example.from_config(
95
+ display_name='TS1 Latin Hypercube',
96
+ short_name='TS1LatinHyperCube',
97
+ path='ts1_latin_hypercube.py',
98
+ description='A Latin hypercube sampling example for the TS1 mechanism. This script shows how to sample an input space and run multiple box models in parallel on a single mechanism.')
99
+
100
+ @classmethod
101
+ def get_all(cls):
102
+ """Get all available examples.
103
+
104
+ Returns:
105
+ list[Example]: A list of all available Example instances.
106
+ """
107
+ return [cls.CARMA_Aluminum, cls.CARMA_Sulfate, cls.Sulfate_Box_Model, cls.TS1LatinHyperCube]
108
+
109
+ def __iter__(self):
110
+ """Make the class iterable over examples.
111
+
112
+ Returns:
113
+ iterator: An iterator over all available examples.
114
+ """
115
+ return iter(self.get_all())
116
+
117
+ def __getattr__(self, item):
118
+ """Handle attribute access for examples.
119
+
120
+ Args:
121
+ item (str): The attribute name to access.
122
+
123
+ Returns:
124
+ Any: The requested attribute value.
125
+
126
+ Raises:
127
+ AttributeError: If the attribute doesn't exist.
128
+ """
129
+ # Check if the attribute exists in the class definition
130
+ if hasattr(self.__class__, item):
131
+ return getattr(self.__class__, item)
132
+ raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'")
133
+
134
+ def __getitem__(self, item):
135
+ """Support indexing to access examples by position.
136
+
137
+ Args:
138
+ item (int): The index of the example to retrieve.
139
+
140
+ Returns:
141
+ Example: The example at the specified index.
142
+
143
+ Raises:
144
+ IndexError: If the index is out of range.
145
+ """
146
+ return self.get_all()[item]
147
+
148
+ def __repr__(self):
149
+ """Return a string representation for debugging.
150
+
151
+ Returns:
152
+ str: A formatted string showing all available examples.
153
+ """
154
+ return f'Examples: {self.get_all()}'
155
+
156
+ def __str__(self):
157
+ """Return a string representation of the examples collection.
158
+
159
+ Returns:
160
+ str: A formatted string showing all available examples.
161
+ """
162
+ return f'Examples: {self.get_all()}'
163
+
164
+
165
+ Examples = _Examples()