Skip to content

Commit

Permalink
Merge branch 'main' into 130-create-musicbox-configuration-for-ts1cap…
Browse files Browse the repository at this point in the history
…ram-24-reduced-mechanism
  • Loading branch information
K20shores committed Oct 13, 2024
2 parents ff91223 + d82d6ea commit 428f81c
Show file tree
Hide file tree
Showing 67 changed files with 13,617 additions and 16,606 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pep8_autoformat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: autopep8
uses: peter-evans/autopep8@v2
with:
args: --recursive --in-place --aggressive --aggressive --max-line-length 120 .
args: --recursive --in-place --aggressive --aggressive --max-line-length 180 .

- name: Check for changes
id: check-changes
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ doc/latex/
data/
__pycache__/
*.pyc
dist
dist

.vscode/
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cff-version: 1.2.0
message: If you use this software, please cite it as below.
title: MusicBox
version: v2.1.0
version: v2.5.0
authors:
- family-names: Dawson
given-names: Matthew
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ classifiers = ["License :: OSI Approved :: Apache Software License"]
dynamic = ["version", "description"]

dependencies = [
"musica==0.7.3",
"musica==0.8.1",
"xarray",
"colorlog",
"pandas",
Expand Down
11 changes: 2 additions & 9 deletions src/acom_music_box/__init__.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
"""
This is the music_box package.
An atmospheric chemistry box model. Powered by MUSICA.
This package contains modules for handling various aspects of a music box,
including species, products, reactants, reactions, and more.
"""
__version__ = "2.4.0"
__version__ = "2.5.3"

from .utils import convert_time, convert_pressure, convert_temperature, convert_concentration
from .species import Species
from .product import Product
from .reactant import Reactant
from .reaction import Reaction, Branched, Arrhenius, Tunneling, Troe_Ternary
from .species_list import SpeciesList
from .model_options import BoxModelOptions
from .species_concentration import SpeciesConcentration
from .reaction_rate import ReactionRate
from .conditions import Conditions

from .evolving_conditions import EvolvingConditions
Expand Down
155 changes: 55 additions & 100 deletions src/acom_music_box/conditions.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
from .utils import convert_time, convert_pressure, convert_temperature, convert_concentration
from .species_concentration import SpeciesConcentration
from .species import Species
from .reaction_rate import ReactionRate
from typing import List
import csv
from .utils import convert_pressure, convert_temperature, convert_concentration
import pandas as pd
import os
from typing import List
from .reaction_rate import ReactionRate
from .species import Species
from .species_concentration import SpeciesConcentration
from .utils import convert_time, convert_pressure, convert_temperature, convert_concentration

import logging
logger = logging.getLogger(__name__)


class Conditions:
"""
Represents conditions for a simulation with attributes such as pressure, temperature, species concentrations,
Expand All @@ -39,13 +29,13 @@ def __init__(
Args:
pressure (float): The pressure of the conditions in atmospheres.
temperature (float): The temperature of the conditions in Kelvin.
species_concentrations (List[SpeciesConcentration]): A list of species concentrations. Default is an empty list.
reaction_rates (List[ReactionRate]): A list of reaction rates. Default is an empty list.
species_concentrations (Dict[Species, float]): A dictionary of species concentrations.
reaction_rates (Dict[Reaction, float]): A dictionary of reaction rates.
"""
self.pressure = pressure
self.temperature = temperature
self.species_concentrations = species_concentrations if species_concentrations is not None else []
self.reaction_rates = reaction_rates if reaction_rates is not None else []
self.species_concentrations = species_concentrations if species_concentrations is not None else {}
self.reaction_rates = reaction_rates if reaction_rates is not None else {}

def __repr__(self):
return f"Conditions(pressure={self.pressure}, temperature={self.temperature}, species_concentrations={self.species_concentrations}, reaction_rates={self.reaction_rates})"
Expand All @@ -54,7 +44,7 @@ def __str__(self):
return f"Pressure: {self.pressure}, Temperature: {self.temperature}, Species Concentrations: {self.species_concentrations}, Reaction Rates: {self.reaction_rates}"

@classmethod
def from_UI_JSON(cls, UI_JSON, species_list, reaction_list):
def from_UI_JSON(self, UI_JSON, species_list, reaction_list):
"""
Creates an instance of the class from a UI JSON object.
Expand Down Expand Up @@ -108,19 +98,17 @@ def from_UI_JSON(cls, UI_JSON, species_list, reaction_list):

reaction_rates.append(ReactionRate(reaction_from_list, rate))

return cls(
return self(
pressure,
temperature,
species_concentrations,
reaction_rates)

@classmethod
def from_config_JSON(
cls,
self,
path_to_json,
config_JSON,
species_list,
reaction_list):
object):
"""
Creates an instance of the class from a configuration JSON object.
Expand All @@ -129,75 +117,51 @@ def from_config_JSON(
Args:
path_to_json (str): The path to the JSON file containing the initial conditions and settings.
config_JSON (dict): The configuration JSON object containing the initial conditions and settings.
species_list (SpeciesList): A SpeciesList containing the species involved in the simulation.
reaction_list (ReactionList): A ReactionList containing the reactions involved in the simulation.
object (dict): The configuration JSON object containing the initial conditions and settings.
Returns:
object: An instance of the Conditions class with the settings from the configuration JSON object.
"""
pressure = convert_pressure(
config_JSON['environmental conditions']['pressure'],
object['environmental conditions']['pressure'],
'initial value')

temperature = convert_temperature(
config_JSON['environmental conditions']['temperature'],
object['environmental conditions']['temperature'],
'initial value')

# Set initial species concentrations
species_concentrations = []
reaction_rates = []
initial_concentrations = {}
reaction_rates = {}

# reads initial conditions from csv if it is given
if 'initial conditions' in config_JSON and len(
list(config_JSON['initial conditions'].keys())) > 0:
if 'initial conditions' in object and len(
list(object['initial conditions'].keys())) > 0:

initial_conditions_path = os.path.join(
os.path.dirname(path_to_json),
list(config_JSON['initial conditions'].keys())[0])
list(object['initial conditions'].keys())[0])

reaction_rates = Conditions.read_initial_rates_from_file(
initial_conditions_path, reaction_list)
initial_conditions_path)

# reads from config file directly if present
if 'chemical species' in config_JSON:
for chem_spec in config_JSON['chemical species']:
species = Species(name=chem_spec)
concentration = convert_concentration(
config_JSON['chemical species'][chem_spec], 'initial value')

species_concentrations.append(
SpeciesConcentration(
species, concentration))

for species in species_list.species:
if species.tracer_type == 'THIRD_BODY':
continue
if not any(conc.species.name ==
species.name for conc in species_concentrations):
species_concentrations.append(SpeciesConcentration(species, 0))

# Set initial reaction rates
for reaction in reaction_list.reactions:
if (reaction.name is None):
continue
reaction_exists = False
for rate in reaction_rates:
if rate.reaction.name == reaction.name:
reaction_exists = True
break

if not reaction_exists:
reaction_rates.append(ReactionRate(reaction, 0))

return cls(
if 'chemical species' in object:
initial_concentrations = {
species: convert_concentration(
object['chemical species'][species], 'initial value', temperature, pressure
)
for species in object['chemical species']
}

return self(
pressure,
temperature,
species_concentrations,
initial_concentrations,
reaction_rates)

@classmethod
def read_initial_rates_from_file(cls, file_path, reaction_list):
def read_initial_rates_from_file(cls, file_path):
"""
Reads initial reaction rates from a file.
Expand All @@ -206,30 +170,33 @@ def read_initial_rates_from_file(cls, file_path, reaction_list):
Args:
file_path (str): The path to the file containing the initial reaction rates.
reaction_list (ReactionList): A ReactionList containing the reactions involved in the simulation.
Returns:
list: A list where each element represents the initial rate of a reaction.
dict: A dictionary of initial reaction rates.
"""

reaction_rates = []

with open(file_path, 'r') as csv_file:
initial_conditions = list(csv.reader(csv_file))

if (len(initial_conditions) > 1):
# The first row of the CSV contains headers
headers = initial_conditions[0]

# The second row of the CSV contains rates
rates = initial_conditions[1]

for reaction_rate, rate in zip(headers, rates):
type, name, *rest = reaction_rate.split('.')
for reaction in reaction_list.reactions:
if reaction.name == name and reaction.short_type() == type:
reaction_rates.append(ReactionRate(reaction, rate))

reaction_rates = {}

df = pd.read_csv(file_path)
rows, _ = df.shape
if rows > 1:
raise ValueError(f'Initial conditions file ({file_path}) may only have one row of data. There are {rows} rows present.')
for key in df.columns:
parts = key.split('.')
reaction_type, label = None, None
if len(parts) == 3:
reaction_type, label, units = parts
elif len(parts) == 2:
reaction_type, label = parts
else:
error = f"Unexpected format in key: {key}"
logger.error(error)
raise ValueError(error)
rate_name = f'{reaction_type}.{label}'
if rate_name in reaction_rates:
raise ValueError(f"Duplicate reaction rate found: {rate_name}")
reaction_rates[rate_name] = df.iloc[0][key]

return reaction_rates

def add_species_concentration(self, species_concentration):
Expand Down Expand Up @@ -295,18 +262,6 @@ def update_conditions(self, new_conditions):
self.pressure = new_conditions.pressure
if new_conditions.temperature is not None:
self.temperature = new_conditions.temperature
for conc in new_conditions.species_concentrations:
match = filter(
lambda x: x.species.name == conc.species.name,
self.species_concentrations)
for item in list(match):
item.concentration = conc.concentration

for rate in new_conditions.reaction_rates:

match = filter(
lambda x: x.reaction.name == rate.reaction.name,
self.reaction_rates)
self.species_concentrations.update(new_conditions.species_concentrations)

for item in list(match):
item.rate = rate.rate
self.reaction_rates.update(new_conditions.reaction_rates)
3 changes: 3 additions & 0 deletions src/acom_music_box/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
BOLTZMANN_CONSTANT = 1.380649e-23 # joules / Kelvin
AVOGADRO_CONSTANT = 6.02214076e23 # / mole
GAS_CONSTANT = BOLTZMANN_CONSTANT * AVOGADRO_CONSTANT # joules / Kelvin-mole
Loading

0 comments on commit 428f81c

Please sign in to comment.