Skip to content

Commit cea7b09

Browse files
authored
Merge pull request #259 from NCAR/main
v2.5.3
2 parents fa32e7c + f4dc6e7 commit cea7b09

25 files changed

+3771
-4795
lines changed

.github/workflows/pep8_autoformat.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
- name: autopep8
1616
uses: peter-evans/autopep8@v2
1717
with:
18-
args: --recursive --in-place --aggressive --aggressive --max-line-length 120 .
18+
args: --recursive --in-place --aggressive --aggressive --max-line-length 180 .
1919

2020
- name: Check for changes
2121
id: check-changes

src/acom_music_box/__init__.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,10 @@
44
This package contains modules for handling various aspects of a music box,
55
including species, products, reactants, reactions, and more.
66
"""
7-
__version__ = "2.5.2"
7+
__version__ = "2.5.3"
88

99
from .utils import convert_time, convert_pressure, convert_temperature, convert_concentration
10-
from .species import Species
11-
from .product import Product
12-
from .reactant import Reactant
13-
from .reaction import Reaction, Branched, Arrhenius, Tunneling, Troe_Ternary
14-
from .species_list import SpeciesList
1510
from .model_options import BoxModelOptions
16-
from .species_concentration import SpeciesConcentration
17-
from .reaction_rate import ReactionRate
1811
from .conditions import Conditions
1912

2013
from .evolving_conditions import EvolvingConditions

src/acom_music_box/conditions.py

Lines changed: 48 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
from .utils import convert_pressure, convert_temperature, convert_concentration
2-
from .species_concentration import SpeciesConcentration
3-
from .species import Species
4-
from .reaction_rate import ReactionRate
5-
from typing import List
6-
import csv
2+
import pandas as pd
73
import os
8-
from typing import List
9-
from .reaction_rate import ReactionRate
10-
from .species import Species
11-
from .species_concentration import SpeciesConcentration
124

135
import logging
146
logger = logging.getLogger(__name__)
@@ -38,13 +30,13 @@ def __init__(
3830
Args:
3931
pressure (float): The pressure of the conditions in atmospheres.
4032
temperature (float): The temperature of the conditions in Kelvin.
41-
species_concentrations (List[SpeciesConcentration]): A list of species concentrations. Default is an empty list.
42-
reaction_rates (List[ReactionRate]): A list of reaction rates. Default is an empty list.
33+
species_concentrations (Dict[Species, float]): A dictionary of species concentrations.
34+
reaction_rates (Dict[Reaction, float]): A dictionary of reaction rates.
4335
"""
4436
self.pressure = pressure
4537
self.temperature = temperature
46-
self.species_concentrations = species_concentrations if species_concentrations is not None else []
47-
self.reaction_rates = reaction_rates if reaction_rates is not None else []
38+
self.species_concentrations = species_concentrations if species_concentrations is not None else {}
39+
self.reaction_rates = reaction_rates if reaction_rates is not None else {}
4840

4941
def __repr__(self):
5042
return f"Conditions(pressure={self.pressure}, temperature={self.temperature}, species_concentrations={self.species_concentrations}, reaction_rates={self.reaction_rates})"
@@ -117,9 +109,7 @@ def from_UI_JSON(self, UI_JSON, species_list, reaction_list):
117109
def from_config_JSON(
118110
self,
119111
path_to_json,
120-
config_JSON,
121-
species_list,
122-
reaction_list):
112+
object):
123113
"""
124114
Creates an instance of the class from a configuration JSON object.
125115
@@ -128,75 +118,51 @@ def from_config_JSON(
128118
129119
Args:
130120
path_to_json (str): The path to the JSON file containing the initial conditions and settings.
131-
config_JSON (dict): The configuration JSON object containing the initial conditions and settings.
132-
species_list (SpeciesList): A SpeciesList containing the species involved in the simulation.
133-
reaction_list (ReactionList): A ReactionList containing the reactions involved in the simulation.
121+
object (dict): The configuration JSON object containing the initial conditions and settings.
134122
135123
Returns:
136124
object: An instance of the Conditions class with the settings from the configuration JSON object.
137125
"""
138126
pressure = convert_pressure(
139-
config_JSON['environmental conditions']['pressure'],
127+
object['environmental conditions']['pressure'],
140128
'initial value')
141129

142130
temperature = convert_temperature(
143-
config_JSON['environmental conditions']['temperature'],
131+
object['environmental conditions']['temperature'],
144132
'initial value')
145133

146134
# Set initial species concentrations
147-
species_concentrations = []
148-
reaction_rates = []
135+
initial_concentrations = {}
136+
reaction_rates = {}
149137

150138
# reads initial conditions from csv if it is given
151-
if 'initial conditions' in config_JSON and len(
152-
list(config_JSON['initial conditions'].keys())) > 0:
139+
if 'initial conditions' in object and len(
140+
list(object['initial conditions'].keys())) > 0:
153141

154142
initial_conditions_path = os.path.join(
155143
os.path.dirname(path_to_json),
156-
list(config_JSON['initial conditions'].keys())[0])
144+
list(object['initial conditions'].keys())[0])
157145

158146
reaction_rates = Conditions.read_initial_rates_from_file(
159-
initial_conditions_path, reaction_list)
147+
initial_conditions_path)
160148

161149
# reads from config file directly if present
162-
if 'chemical species' in config_JSON:
163-
for chem_spec in config_JSON['chemical species']:
164-
species = Species(name=chem_spec)
165-
concentration = convert_concentration(
166-
config_JSON['chemical species'][chem_spec], 'initial value', temperature, pressure)
167-
168-
species_concentrations.append(
169-
SpeciesConcentration(
170-
species, concentration))
171-
172-
for species in species_list.species:
173-
if species.tracer_type == 'THIRD_BODY':
174-
continue
175-
if not any(conc.species.name ==
176-
species.name for conc in species_concentrations):
177-
species_concentrations.append(SpeciesConcentration(species, 0))
178-
179-
# Set initial reaction rates
180-
for reaction in reaction_list.reactions:
181-
if (reaction.name is None):
182-
continue
183-
reaction_exists = False
184-
for rate in reaction_rates:
185-
if rate.reaction.name == reaction.name:
186-
reaction_exists = True
187-
break
188-
189-
if not reaction_exists:
190-
reaction_rates.append(ReactionRate(reaction, 0))
150+
if 'chemical species' in object:
151+
initial_concentrations = {
152+
species: convert_concentration(
153+
object['chemical species'][species], 'initial value', temperature, pressure
154+
)
155+
for species in object['chemical species']
156+
}
191157

192158
return self(
193159
pressure,
194160
temperature,
195-
species_concentrations,
161+
initial_concentrations,
196162
reaction_rates)
197163

198164
@classmethod
199-
def read_initial_rates_from_file(self, file_path, reaction_list):
165+
def read_initial_rates_from_file(cls, file_path):
200166
"""
201167
Reads initial reaction rates from a file.
202168
@@ -205,29 +171,32 @@ def read_initial_rates_from_file(self, file_path, reaction_list):
205171
206172
Args:
207173
file_path (str): The path to the file containing the initial reaction rates.
208-
reaction_list (ReactionList): A ReactionList containing the reactions involved in the simulation.
209174
210175
Returns:
211-
list: A list where each element represents the initial rate of a reaction.
176+
dict: A dictionary of initial reaction rates.
212177
"""
213178

214-
reaction_rates = []
215-
216-
with open(file_path, 'r') as csv_file:
217-
initial_conditions = list(csv.reader(csv_file))
218-
219-
if (len(initial_conditions) > 1):
220-
# The first row of the CSV contains headers
221-
headers = initial_conditions[0]
222-
223-
# The second row of the CSV contains rates
224-
rates = initial_conditions[1]
225-
226-
for reaction_rate, rate in zip(headers, rates):
227-
type, name, *rest = reaction_rate.split('.')
228-
for reaction in reaction_list.reactions:
229-
if reaction.name == name and reaction.short_type() == type:
230-
reaction_rates.append(ReactionRate(reaction, rate))
179+
reaction_rates = {}
180+
181+
df = pd.read_csv(file_path)
182+
rows, _ = df.shape
183+
if rows > 1:
184+
raise ValueError(f'Initial conditions file ({file_path}) may only have one row of data. There are {rows} rows present.')
185+
for key in df.columns:
186+
parts = key.split('.')
187+
reaction_type, label = None, None
188+
if len(parts) == 3:
189+
reaction_type, label, units = parts
190+
elif len(parts) == 2:
191+
reaction_type, label = parts
192+
else:
193+
error = f"Unexpected format in key: {key}"
194+
logger.error(error)
195+
raise ValueError(error)
196+
rate_name = f'{reaction_type}.{label}'
197+
if rate_name in reaction_rates:
198+
raise ValueError(f"Duplicate reaction rate found: {rate_name}")
199+
reaction_rates[rate_name] = df.iloc[0][key]
231200

232201
return reaction_rates
233202

@@ -294,18 +263,6 @@ def update_conditions(self, new_conditions):
294263
self.pressure = new_conditions.pressure
295264
if new_conditions.temperature is not None:
296265
self.temperature = new_conditions.temperature
297-
for conc in new_conditions.species_concentrations:
298-
match = filter(
299-
lambda x: x.species.name == conc.species.name,
300-
self.species_concentrations)
301-
for item in list(match):
302-
item.concentration = conc.concentration
303-
304-
for rate in new_conditions.reaction_rates:
305-
306-
match = filter(
307-
lambda x: x.reaction.name == rate.reaction.name,
308-
self.reaction_rates)
266+
self.species_concentrations.update(new_conditions.species_concentrations)
309267

310-
for item in list(match):
311-
item.rate = rate.rate
268+
self.reaction_rates.update(new_conditions.reaction_rates)

0 commit comments

Comments
 (0)