From b8ee1409bddcbab76b561f1ccbbc62c6178b6277 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Sat, 23 Dec 2023 04:02:46 +0100 Subject: [PATCH] [MIRROR] Optimizations for reactions [MDB IGNORE] (#25788) * Optimizations for reactions (#80380) ## About The Pull Request 1. Fixes #80365 The lowers the rounding accuracy for reaction rate from `CHEMICAL_VOLUME_ROUNDING(0.01)` to `CHEMICAL_QUANTIZATION_LEVEL(0.0001)`. This stops reactions from abruptly ending when either their temperature/ph/volume become very low values. This also removes a call to `handle_reactions()` after the reaction has finished so it that it doesn't restart reactions that have failed due to failed environmental conditions(low ph/temps far below the optimal value). This not only saves overhead but can also stop potential infinite reaction loops(where if a reaction fails we try to restart it again) 2. Simplifies math for temperature handling and stops each react step from calling `handle_reactions()` when new reagent products are formed thus making reactions slightly faster. Also ensures early return if adding/removing reagents failed at any point & other smaller. nitpicks 3. Removes excess call to `update_total()` in each react step making it even faster ## Changelog :cl: fix: Reactions whose temps/ph values fall way below their optimal values no longer restart & prevents infinite loops. Made reaction code slightly faster /:cl: * Optimizations for reactions --------- Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> --- .../modules/reagents/chemistry/equilibrium.dm | 40 +++++++++++-------- .../reagents/chemistry/holder/holder.dm | 6 +-- .../reagents/chemistry/holder/properties.dm | 2 +- .../reagents/chemistry/holder/reactions.dm | 2 - 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/code/modules/reagents/chemistry/equilibrium.dm b/code/modules/reagents/chemistry/equilibrium.dm index a6f21a6c6df..c9d6226930c 100644 --- a/code/modules/reagents/chemistry/equilibrium.dm +++ b/code/modules/reagents/chemistry/equilibrium.dm @@ -168,7 +168,7 @@ step_target_vol = 0 reacted_vol = 0 //Because volumes can be lost mid reactions for(var/product in reaction.results) - step_target_vol += (multiplier * reaction.results[product]) + step_target_vol += multiplier * reaction.results[product] reacted_vol += holder.get_reagent_amount(product) target_vol = reacted_vol + step_target_vol return TRUE @@ -277,7 +277,7 @@ //Calculate DeltaT (Deviation of T from optimal) if(!reaction.is_cold_recipe) if (cached_temp < reaction.optimal_temp && cached_temp >= reaction.required_temp) - delta_t = (((cached_temp - reaction.required_temp) ** reaction.temp_exponent_factor) / ((reaction.optimal_temp - reaction.required_temp) ** reaction.temp_exponent_factor)) + delta_t = ((cached_temp - reaction.required_temp) / (reaction.optimal_temp - reaction.required_temp)) ** reaction.temp_exponent_factor else if (cached_temp >= reaction.optimal_temp) delta_t = 1 else //too hot @@ -286,7 +286,7 @@ return else if (cached_temp > reaction.optimal_temp && cached_temp <= reaction.required_temp) - delta_t = (((reaction.required_temp - cached_temp) ** reaction.temp_exponent_factor) / ((reaction.required_temp - reaction.optimal_temp) ** reaction.temp_exponent_factor)) + delta_t = ((reaction.required_temp - cached_temp) / (reaction.required_temp - reaction.optimal_temp)) ** reaction.temp_exponent_factor else if (cached_temp <= reaction.optimal_temp) delta_t = 1 else //Too cold @@ -312,34 +312,39 @@ purity *= purity_modifier //Now we calculate how much to add - this is normalised to the rate up limiter - var/delta_chem_factor = (reaction.rate_up_lim * delta_t) * seconds_per_tick//add/remove factor + var/delta_chem_factor = reaction.rate_up_lim * delta_t * seconds_per_tick//add/remove factor //keep limited if(delta_chem_factor > step_target_vol) delta_chem_factor = step_target_vol //Normalise to multiproducts - delta_chem_factor = round(delta_chem_factor / product_ratio, CHEMICAL_VOLUME_ROUNDING) + delta_chem_factor = round(delta_chem_factor / product_ratio, CHEMICAL_QUANTISATION_LEVEL) if(delta_chem_factor <= 0) to_delete = TRUE return //Calculate how much product to make and how much reactant to remove factors.. - for(var/reagent in reaction.required_reagents) - holder.remove_reagent(reagent, (delta_chem_factor * reaction.required_reagents[reagent])) + var/required_amount + for(var/datum/reagent/requirement as anything in reaction.required_reagents) + required_amount = reaction.required_reagents[requirement] + if(!holder.remove_reagent(requirement, delta_chem_factor * required_amount)) + to_delete = TRUE + return //Apply pH changes var/pH_adjust if(reaction.reaction_flags & REACTION_PH_VOL_CONSTANT) - pH_adjust = ((delta_chem_factor * reaction.required_reagents[reagent]) / target_vol) * (reaction.H_ion_release * h_ion_mod) + pH_adjust = ((delta_chem_factor * required_amount) / target_vol) * (reaction.H_ion_release * h_ion_mod) else //Default adds pH independant of volume - pH_adjust = (delta_chem_factor * reaction.required_reagents[reagent]) * (reaction.H_ion_release * h_ion_mod) - holder.adjust_specific_reagent_ph(reagent, pH_adjust) + pH_adjust = (delta_chem_factor * required_amount) * (reaction.H_ion_release * h_ion_mod) + holder.adjust_specific_reagent_ph(requirement, pH_adjust) var/step_add var/total_step_added = 0 - for(var/product in reaction.results) + for(var/datum/reagent/product as anything in reaction.results) //create the products - step_add = delta_chem_factor * reaction.results[product] - //Default handiling - holder.add_reagent(product, step_add, null, cached_temp, purity, override_base_ph = TRUE) + step_add = holder.add_reagent(product, delta_chem_factor * reaction.results[product], null, cached_temp, purity, override_base_ph = TRUE, no_react = TRUE) + if(!step_add) + to_delete = TRUE + return //Apply pH changes var/pH_adjust @@ -348,6 +353,8 @@ else pH_adjust = step_add * (reaction.H_ion_release * h_ion_mod) holder.adjust_specific_reagent_ph(product, pH_adjust) + + //record amounts created reacted_vol += step_add total_step_added += step_add @@ -377,14 +384,13 @@ //post reaction checks if(!(check_fail_states(total_step_added))) to_delete = TRUE + return //end reactions faster so plumbing is faster //length is so that plumbing is faster - but it doesn't disable competitive reactions. Basically, competitive reactions will likely reach their step target at the start, so this will disable that. We want to avoid that. But equally, we do want to full stop a holder from reacting asap so plumbing isn't waiting an tick to resolve. - if((step_add >= step_target_vol) && (length(holder.reaction_list == 1))) + if((step_add >= step_target_vol) && (length(holder.reaction_list) == 1)) to_delete = TRUE - holder.update_total() - /* * Calculates the total sum normalised purity of ALL reagents in a holder * Currently calculates it irrespective of required reagents at the start, but this should be changed if this is powergamed to required reagents diff --git a/code/modules/reagents/chemistry/holder/holder.dm b/code/modules/reagents/chemistry/holder/holder.dm index ed7e8349055..112c9fca8a1 100644 --- a/code/modules/reagents/chemistry/holder/holder.dm +++ b/code/modules/reagents/chemistry/holder/holder.dm @@ -643,7 +643,7 @@ reagent_volume = round(reagent.volume, CHEMICAL_QUANTISATION_LEVEL) //round to this many decimal places //remove very small amounts of reagents - if((reagent_volume <= 0.05 && !is_reacting) || reagent_volume <= CHEMICAL_QUANTISATION_LEVEL) + if(!reagent_volume || (reagent_volume <= 0.05 && !is_reacting)) //end metabolization if(isliving(my_atom)) if(reagent.metabolizing) @@ -663,14 +663,14 @@ //compute volume & ph like we would normally . += reagent_volume - total_ph += (reagent.ph * reagent_volume) + total_ph += reagent.ph * reagent_volume //reasign rounded value reagent.volume = reagent_volume //assign the final values, rounding up can sometimes cause overflow so bring it down total_volume = min(round(., CHEMICAL_VOLUME_ROUNDING), maximum_volume) - if(!.) + if(!total_volume) ph = CHEMICAL_NORMAL_PH else ph = clamp(total_ph / total_volume, CHEMICAL_MIN_PH, CHEMICAL_MAX_PH) diff --git a/code/modules/reagents/chemistry/holder/properties.dm b/code/modules/reagents/chemistry/holder/properties.dm index c714c922da5..b949866a6d4 100644 --- a/code/modules/reagents/chemistry/holder/properties.dm +++ b/code/modules/reagents/chemistry/holder/properties.dm @@ -32,7 +32,7 @@ //short cut to break when we have found our one exact type if(type_check == REAGENT_STRICT_TYPE) - break + return total_amount return round(total_amount, CHEMICAL_VOLUME_ROUNDING) diff --git a/code/modules/reagents/chemistry/holder/reactions.dm b/code/modules/reagents/chemistry/holder/reactions.dm index 3aff4db3d5b..ef9e333802b 100644 --- a/code/modules/reagents/chemistry/holder/reactions.dm +++ b/code/modules/reagents/chemistry/holder/reactions.dm @@ -236,8 +236,6 @@ is_reacting = FALSE LAZYNULL(previous_reagent_list) //reset it to 0 - because any change will be different now. update_total() - if(!QDELING(src)) - handle_reactions() //Should be okay without. Each step checks. /* * Force stops the current holder/reagents datum from reacting