From 4f02e3eaa9fade012b636f0375f470e3221fcb35 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Tue, 27 Aug 2024 18:26:54 -0700 Subject: [PATCH] Bug fixes for demo (#207) * fix missing last set of conditions * allow user-defined reactions in mechanism * add air density to output * adjust next time step to not pass output or updates to conditions * fix timestepping --------- Co-authored-by: Kyle Shores --- src/acom_music_box/music_box.py | 49 +++++++++++++++++++++------------ src/acom_music_box/reaction.py | 1 + tests/test_chapman.py | 2 +- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/acom_music_box/music_box.py b/src/acom_music_box/music_box.py index bad0f43e..d8d6305f 100644 --- a/src/acom_music_box/music_box.py +++ b/src/acom_music_box/music_box.py @@ -199,7 +199,9 @@ def generateConfig(self, directory): elif reaction_rate.reaction.reaction_type == "LOSS": name = "LOSS." + reaction_rate.reaction.name + ".s-1" elif reaction_rate.reaction.reaction_type == "EMISSION": - name = "EMISSION." + reaction_rate.reaction.name + ".s-1" + name = "EMIS." + reaction_rate.reaction.name + ".s-1" + elif reaction_rate.reaction.reaction_type == "USER_DEFINED": + name = "USER." + reaction_rate.reaction.name + ".s-1" reaction_names.append(name) reaction_rates.append(reaction_rate.rate) @@ -469,6 +471,7 @@ def solve(self, output_path=None): headers.append("time") headers.append("ENV.temperature") headers.append("ENV.pressure") + headers.append("ENV.number_density_air") if (self.solver is None): raise Exception("Error: MusicBox object {} has no solver." @@ -500,33 +503,21 @@ def solve(self, output_path=None): while (curr_time <= self.box_model_options.simulation_length): - # outputs to output_array if enough time has elapsed - if (next_output_time <= curr_time): - row = [] - row.append(next_output_time) - row.append(curr_conditions.temperature) - row.append(curr_conditions.pressure) - for conc in ordered_concentrations: - row.append(conc) - output_array.append(row) - next_output_time += self.box_model_options.output_step_time # iterates evolving conditions if enough time has elapsed while ( next_conditions is not None and next_conditions_time <= curr_time): curr_conditions.update_conditions(next_conditions) - + ordered_rate_constants = self.order_reaction_rates( + curr_conditions, rate_constant_ordering) + # iterates next_conditions if there are remaining evolving # conditions if (len(self.evolving_conditions) > next_conditions_index + 1): next_conditions_index += 1 next_conditions = self.evolving_conditions.conditions[next_conditions_index] next_conditions_time = self.evolving_conditions.times[next_conditions_index] - - ordered_rate_constants = self.order_reaction_rates( - curr_conditions, rate_constant_ordering) - else: next_conditions = None @@ -537,12 +528,32 @@ def solve(self, output_path=None): air_density = curr_conditions.pressure / \ (GAS_CONSTANT * curr_conditions.temperature) + # outputs to output_array if enough time has elapsed + if (next_output_time <= curr_time): + row = [] + row.append(next_output_time) + row.append(curr_conditions.temperature) + row.append(curr_conditions.pressure) + row.append(air_density) + for conc in ordered_concentrations: + row.append(conc) + output_array.append(row) + next_output_time += self.box_model_options.output_step_time + + # ensure the time step is not greater than the next update to the + # evolving conditions or the next output time + time_step = self.box_model_options.chem_step_time + if (next_conditions is not None and next_conditions_time > curr_time): + time_step = min(time_step, next_conditions_time - curr_time) + if (next_output_time > curr_time): + time_step = min(time_step, next_output_time - curr_time) + # solves and updates concentration values in concentration array if (not ordered_concentrations): logger.info("Warning: ordered_concentrations list is empty.") musica.micm_solve( self.solver, - self.box_model_options.chem_step_time, + time_step, curr_conditions.temperature, curr_conditions.pressure, air_density, @@ -550,7 +561,7 @@ def solve(self, output_path=None): ordered_rate_constants) # increments time - curr_time += self.box_model_options.chem_step_time + curr_time += time_step df = pd.DataFrame(output_array[1:], columns=output_array[0]) # outputs to file if output is present @@ -721,6 +732,8 @@ def order_reaction_rates(self, curr_conditions, rate_constant_ordering): key = "LOSS." + rate.reaction.name elif (rate.reaction.reaction_type == "EMISSION"): key = "EMIS." + rate.reaction.name + elif (rate.reaction.reaction_type == "USER_DEFINED"): + key = "USER." + rate.reaction.name rate_constants[key] = rate.rate ordered_rate_constants = len(rate_constants.keys()) * [0.0] diff --git a/src/acom_music_box/reaction.py b/src/acom_music_box/reaction.py index 003d8795..a6a6aa6c 100644 --- a/src/acom_music_box/reaction.py +++ b/src/acom_music_box/reaction.py @@ -75,6 +75,7 @@ def short_type(self): "ARRHENIUS": "ARRH", "TUNNELING": "TUNN", "TROE_TERNARY": "TROE", + "USER_DEFINED": "USER", } return type_map.get(self.reaction_type, "UNKNOWN") diff --git a/tests/test_chapman.py b/tests/test_chapman.py index 765144da..5e48b0e4 100644 --- a/tests/test_chapman.py +++ b/tests/test_chapman.py @@ -58,7 +58,7 @@ def test_run(self): assert math.isclose( float(model_output_concs[i][j]), float(test_output_concs[i][j]), - rel_tol=1e-8, + rel_tol=1e-7, abs_tol=1e-15, ), f"Arrays differ at index ({i}, {j}) for species {concs_to_test[j]}"