From a06aba793b49e07b80d48d2c476ccebece0c1060 Mon Sep 17 00:00:00 2001 From: Putnam3145 Date: Wed, 11 Aug 2021 19:24:41 -0700 Subject: [PATCH 1/3] Added specific_entropy, reorganized some stuff --- Cargo.toml | 1 + src/gas.rs | 1 + src/gas/constants.rs | 4 + src/gas/hooks.rs | 438 ++++++++++++++++++++++++++++++++++++ src/gas/mixture.rs | 33 ++- src/gas/types.rs | 66 +++--- src/helpers.rs | 52 +++++ src/lib.rs | 482 +--------------------------------------- src/turfs.rs | 4 +- src/turfs/monstermos.rs | 16 +- src/turfs/processing.rs | 4 +- 11 files changed, 570 insertions(+), 531 deletions(-) create mode 100644 src/gas/hooks.rs create mode 100644 src/helpers.rs diff --git a/Cargo.toml b/Cargo.toml index 8179b9f7..87058d04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" [features] default = ["auxcleanup_gas_deletion"] auxcleanup_gas_deletion = ["auxcleanup"] +specific_entropy = [] equalization = [] monstermos = ["equalization"] putnamos = ["equalization"] diff --git a/src/gas.rs b/src/gas.rs index 5790bcd3..60d7b3af 100644 --- a/src/gas.rs +++ b/src/gas.rs @@ -1,4 +1,5 @@ pub mod constants; +pub mod hooks; pub mod mixture; pub mod types; diff --git a/src/gas/constants.rs b/src/gas/constants.rs index eb7c10da..e655f726 100644 --- a/src/gas/constants.rs +++ b/src/gas/constants.rs @@ -1,5 +1,9 @@ /// kPa*L/(K*mol) pub const R_IDEAL_GAS_EQUATION: f32 = 8.31; +// (mol^3 * s^3) / (kg^3 * L). +pub const IDEAL_GAS_ENTROPY_CONSTANT: f32 = 1164.0; +// Makes the math better on vacuum stuff, arbitrary constant taken from bay +pub const SPECIFIC_ENTROPY_VACUUM: f32 = 150_000.0; /// kPa pub const ONE_ATMOSPHERE: f32 = 101.325; /// -270.3degC diff --git a/src/gas/hooks.rs b/src/gas/hooks.rs new file mode 100644 index 00000000..000e8d0b --- /dev/null +++ b/src/gas/hooks.rs @@ -0,0 +1,438 @@ +use auxtools::*; + +use auxcleanup::*; + +use crate::reaction::react_by_id; + +use super::{constants::*, *}; + +#[hook("/datum/gas_mixture/proc/__gasmixture_register")] +fn _register_gasmixture_hook() { + GasArena::register_mix(src) +} + +#[cfg(not(feature = "auxcleanup_gas_deletion"))] +#[hook("/datum/gas_mixture/proc/__gasmixture_unregister")] +fn _unregister_gasmixture_hook() { + GasMixtures::unregister_mix(unsafe { src.raw.data.id }); + Ok(Value::null()) +} + +#[cfg(feature = "auxcleanup_gas_deletion")] +#[datum_del] +fn _unregister_gasmixture_hook(v: u32) { + GasArena::unregister_mix(v); +} + +#[hook("/datum/gas_mixture/proc/heat_capacity")] +fn _heat_cap_hook() { + with_mix(src, |mix| Ok(Value::from(mix.heat_capacity()))) +} + +#[hook("/datum/gas_mixture/proc/set_min_heat_capacity")] +fn _min_heat_cap_hook() { + if args.is_empty() { + Err(runtime!( + "attempted to set min heat capacity with no argument" + )) + } else { + with_mix_mut(src, |mix| { + mix.set_min_heat_capacity(args[0].as_number().unwrap_or_default()); + Ok(Value::null()) + }) + } +} + +#[hook("/datum/gas_mixture/proc/total_moles")] +fn _total_moles_hook() { + with_mix(src, |mix| Ok(Value::from(mix.total_moles()))) +} + +#[hook("/datum/gas_mixture/proc/return_pressure")] +fn _return_pressure_hook() { + with_mix(src, |mix| Ok(Value::from(mix.return_pressure()))) +} + +#[hook("/datum/gas_mixture/proc/return_temperature")] +fn _return_temperature_hook() { + with_mix(src, |mix| Ok(Value::from(mix.get_temperature()))) +} + +#[hook("/datum/gas_mixture/proc/return_volume")] +fn _return_volume_hook() { + with_mix(src, |mix| Ok(Value::from(mix.volume))) +} + +#[hook("/datum/gas_mixture/proc/thermal_energy")] +fn _thermal_energy_hook() { + with_mix(src, |mix| Ok(Value::from(mix.thermal_energy()))) +} + +#[hook("/datum/gas_mixture/proc/merge")] +fn _merge_hook() { + if args.is_empty() { + Err(runtime!("Tried merging nothing into a gas mixture")) + } else { + with_mixes_mut(src, &args[0], |src_mix, giver_mix| { + src_mix.merge(giver_mix); + Ok(Value::null()) + }) + } +} + +#[hook("/datum/gas_mixture/proc/__remove_ratio")] +fn _remove_ratio_hook() { + if args.len() < 2 { + Err(runtime!("remove_ratio called with fewer than 2 arguments")) + } else { + with_mixes_mut(src, &args[0], |src_mix, into_mix| { + src_mix.remove_ratio_into(args[1].as_number().unwrap_or_default(), into_mix); + Ok(Value::null()) + }) + } +} + +#[hook("/datum/gas_mixture/proc/__remove")] +fn _remove_hook() { + if args.len() < 2 { + Err(runtime!("remove called with fewer than 2 arguments")) + } else { + with_mixes_mut(src, &args[0], |src_mix, into_mix| { + src_mix.remove_into(args[1].as_number().unwrap_or_default(), into_mix); + Ok(Value::null()) + }) + } +} + +#[hook("/datum/gas_mixture/proc/copy_from")] +fn _copy_from_hook() { + if args.is_empty() { + Err(runtime!("Tried copying a gas mix from nothing")) + } else { + with_mixes_mut(src, &args[0], |src_mix, giver_mix| { + src_mix.copy_from_mutable(giver_mix); + Ok(Value::null()) + }) + } +} + +#[hook("/datum/gas_mixture/proc/temperature_share")] +fn _temperature_share_hook() { + let arg_num = args.len(); + match arg_num { + 2 => with_mixes_mut(src, &args[0], |src_mix, share_mix| { + Ok(Value::from(src_mix.temperature_share( + share_mix, + args[1].as_number().unwrap_or_default(), + ))) + }), + 4 => with_mix_mut(src, |mix| { + Ok(Value::from(mix.temperature_share_non_gas( + args[1].as_number().unwrap_or_default(), + args[2].as_number().unwrap_or_default(), + args[3].as_number().unwrap_or_default(), + ))) + }), + _ => Err(runtime!("Invalid args for temperature_share")), + } +} + +#[hook("/datum/gas_mixture/proc/get_gases")] +fn _get_gases_hook() { + with_mix(src, |mix| { + let gases_list: List = List::new(); + mix.for_each_gas(|idx, gas| { + if gas > GAS_MIN_MOLES { + gases_list.append(Value::from_string(&*gas_idx_to_id(idx)?)?); + } + Ok(()) + })?; + Ok(Value::from(gases_list)) + }) +} + +#[hook("/datum/gas_mixture/proc/set_temperature")] +fn _set_temperature_hook() { + let v = args + .get(0) + .ok_or_else(|| runtime!("Wrong amount of arguments for set_temperature: 0!"))? + .as_number() + .map_err(|_| { + runtime!( + "Attempt to interpret non-number value as number {} {}:{}", + std::file!(), + std::line!(), + std::column!() + ) + })?; + if v.is_finite() { + with_mix_mut(src, |mix| { + mix.set_temperature(v.max(2.7)); + Ok(Value::null()) + }) + } else { + Err(runtime!( + "Attempted to set a temperature to a number that is NaN or infinite." + )) + } +} + +#[hook("/datum/gas_mixture/proc/partial_heat_capacity")] +fn _partial_heat_capacity() { + if args.is_empty() { + Err(runtime!("Incorrect arg len for partial_heat_capacity (0).")) + } else { + with_mix(src, |mix| { + Ok(Value::from( + mix.partial_heat_capacity(gas_idx_from_value(&args[0])?), + )) + }) + } +} + +#[hook("/datum/gas_mixture/proc/set_volume")] +fn _set_volume_hook() { + if args.is_empty() { + Err(runtime!("Attempted to set volume to nothing.")) + } else { + with_mix_mut(src, |mix| { + mix.volume = args[0].as_number().map_err(|_| { + runtime!( + "Attempt to interpret non-number value as number {} {}:{}", + std::file!(), + std::line!(), + std::column!() + ) + })?; + Ok(Value::null()) + }) + } +} + +#[hook("/datum/gas_mixture/proc/get_moles")] +fn _get_moles_hook() { + if args.is_empty() { + Err(runtime!("Incorrect arg len for get_moles (0).")) + } else { + with_mix(src, |mix| { + Ok(Value::from(mix.get_moles(gas_idx_from_value(&args[0])?))) + }) + } +} + +#[hook("/datum/gas_mixture/proc/set_moles")] +fn _set_moles_hook() { + if args.len() < 2 { + return Err(runtime!("Incorrect arg len for set_moles (less than 2).")); + } + let vf = args[1].as_number().unwrap_or_default(); + if !vf.is_finite() { + return Err(runtime!("Attempted to set moles to NaN or infinity.")); + } + if vf < 0.0 { + return Err(runtime!("Attempted to set moles to a negative number.")); + } + with_mix_mut(src, |mix| { + mix.set_moles(gas_idx_from_value(&args[0])?, vf); + Ok(Value::null()) + }) +} + +#[hook("/datum/gas_mixture/proc/adjust_moles")] +fn _adjust_moles_hook(id_val: Value, num_val: Value) { + let vf = num_val.as_number().unwrap_or_default(); + with_mix_mut(src, |mix| { + mix.adjust_moles(gas_idx_from_value(id_val)?, vf); + Ok(Value::null()) + }) +} + +#[hook("/datum/gas_mixture/proc/scrub_into")] +fn _scrub_into_hook(into: Value, ratio_v: Value, gas_list: Value) { + let ratio = ratio_v.as_number().map_err(|_| { + runtime!( + "Attempt to interpret non-number value as number {} {}:{}", + std::file!(), + std::line!(), + std::column!() + ) + })?; + let gases_to_scrub = gas_list.as_list().map_err(|_| { + runtime!( + "Attempt to interpret non-list value as list {} {}:{}", + std::file!(), + std::line!(), + std::column!() + ) + })?; + if gases_to_scrub.len() == 0 { + return Ok(Value::from(false)); + } + let gas_scrub_vec = (1..=gases_to_scrub.len()) + .filter_map(|idx| gas_idx_from_value(&gases_to_scrub.get(idx).unwrap()).ok()) + .collect::>(); + with_mixes_mut(src, into, |src_gas, dest_gas| { + src_gas.transfer_gases_to(ratio, &gas_scrub_vec, dest_gas); + Ok(Value::from(true)) + }) +} + +#[hook("/datum/gas_mixture/proc/mark_immutable")] +fn _mark_immutable_hook() { + with_mix_mut(src, |mix| { + mix.mark_immutable(); + Ok(Value::null()) + }) +} + +#[hook("/datum/gas_mixture/proc/clear")] +fn _clear_hook() { + with_mix_mut(src, |mix| { + mix.clear(); + Ok(Value::null()) + }) +} + +#[hook("/datum/gas_mixture/proc/compare")] +fn _compare_hook() { + if args.is_empty() { + Err(runtime!("Tried comparing a gas mix to nothing")) + } else { + with_mixes(src, &args[0], |gas_one, gas_two| { + Ok(Value::from( + gas_one.temperature_compare(gas_two) + || gas_one.compare_with(gas_two, MINIMUM_MOLES_DELTA_TO_MOVE), + )) + }) + } +} + +#[hook("/datum/gas_mixture/proc/multiply")] +fn _multiply_hook() { + with_mix_mut(src, |mix| { + mix.multiply(if args.is_empty() { + 1.0 + } else { + args[0].as_number().unwrap_or(1.0) + }); + Ok(Value::null()) + }) +} + +#[hook("/datum/gas_mixture/proc/react")] +fn _react_hook(holder: Value) { + let mut ret: i32 = 0; + let reactions = with_mix(src, |mix| Ok(mix.all_reactable()))?; + for reaction in reactions { + ret |= react_by_id(reaction, src, holder)? + .as_number() + .unwrap_or_default() as i32; + if ret & STOP_REACTIONS == STOP_REACTIONS { + return Ok(Value::from(ret as f32)); + } + } + Ok(Value::from(ret as f32)) +} + +#[hook("/datum/gas_mixture/proc/adjust_heat")] +fn _adjust_heat_hook() { + with_mix_mut(src, |mix| { + mix.adjust_heat( + args.get(0) + .ok_or_else(|| runtime!("Wrong number of args for adjust heat: 0"))? + .as_number() + .map_err(|_| { + runtime!( + "Attempt to interpret non-number value as number {} {}:{}", + std::file!(), + std::line!(), + std::column!() + ) + })?, + ); + Ok(Value::null()) + }) +} + +#[hook("/datum/gas_mixture/proc/transfer_to")] +fn _transfer_hook(other: Value, moles: Value) { + with_mixes_mut(src, other, |our_mix, other_mix| { + other_mix.merge(&our_mix.remove(moles.as_number().map_err(|_| { + runtime!( + "Attempt to interpret non-number value as number {} {}:{}", + std::file!(), + std::line!(), + std::column!() + ) + })?)); + Ok(Value::null()) + }) +} + +#[hook("/datum/gas_mixture/proc/transfer_ratio_to")] +fn _transfer_ratio_hook(other: Value, ratio: Value) { + with_mixes_mut(src, other, |our_mix, other_mix| { + other_mix.merge(&our_mix.remove_ratio(ratio.as_number().map_err(|_| { + runtime!( + "Attempt to interpret non-number value as number {} {}:{}", + std::file!(), + std::line!(), + std::column!() + ) + })?)); + Ok(Value::null()) + }) +} + +#[hook("/datum/gas_mixture/proc/equalize_with")] +fn _equalize_with_hook() { + with_mixes_custom( + src, + args.get(0) + .ok_or_else(|| runtime!("Wrong number of args for equalize_with: 0"))?, + |src_lock, total_lock| { + let src_gas = &mut src_lock.write(); + let vol = src_gas.volume; + let total_gas = total_lock.read(); + src_gas.copy_from_mutable(&total_gas); + src_gas.multiply(vol / total_gas.volume); + Ok(Value::null()) + }, + ) +} + +#[hook("/datum/gas_mixture/proc/get_fuel_amount")] +fn _fuel_amount_hook(temp: Value) { + with_mix(src, |air| { + Ok(Value::from(temp.as_number().ok().map_or_else( + || air.get_fuel_amount(), + |new_temp| { + let mut test_air = air.clone(); + test_air.set_temperature(new_temp); + test_air.get_fuel_amount() + }, + ))) + }) +} + +#[hook("/datum/gas_mixture/proc/get_oxidation_power")] +fn _oxidation_power_hook(temp: Value) { + with_mix(src, |air| { + Ok(Value::from(temp.as_number().ok().map_or_else( + || air.get_oxidation_power(), + |new_temp| { + let mut test_air = air.clone(); + test_air.set_temperature(new_temp); + test_air.get_oxidation_power() + }, + ))) + }) +} + +#[cfg(feature = "specific_entropy")] +#[hook("/datum/gas_mixture/proc/specific_entropy")] +fn _specific_entropy_hook() { + with_mix(src, |air| { + Ok(Value::from(air.specific_entropy())) + }) +} diff --git a/src/gas/mixture.rs b/src/gas/mixture.rs index 26abe1ce..6eea916d 100644 --- a/src/gas/mixture.rs +++ b/src/gas/mixture.rs @@ -14,7 +14,8 @@ use tinyvec::TinyVec; use crate::reaction::ReactionIdentifier; use super::{ - constants::*, gas_visibility, total_num_gases, with_reactions, with_specific_heats, GasIDX, + constants::*, gas_visibility, total_num_gases, with_molar_masses, with_reactions, + with_specific_heats, GasIDX, }; type SpecificFireInfo = (usize, f32, f32); @@ -177,7 +178,7 @@ impl Mixture { self.cached_heat_capacity.set(Some(heat_cap)); heat_cap } - /// The heat capacity of the material. [joules?]/mole-kelvin. + /// The heat capacity of the mixture. [joules?]/mole-kelvin. pub fn heat_capacity(&self) -> f32 { self.cached_heat_capacity .get() @@ -191,6 +192,30 @@ impl Mixture { .filter(|amt| amt.is_normal()) .map_or(0.0, |amt| amt * with_specific_heats(|heats| heats[idx])) } + /// Specific entropy of the mixture, the dimensional analysis is too strong for me sorry + pub fn specific_entropy(&self) -> f32 { + if self.total_moles() < GAS_MIN_MOLES { + SPECIFIC_ENTROPY_VACUUM + } else { + with_specific_heats(|heats| { + with_molar_masses(|masses| { + self.moles + .iter() + .copied() + .zip(heats) + .zip(masses) + .filter(|((amt, _), _)| amt.is_normal()) + .fold(0.0, |acc, ((amt, cap), mass)| { + acc + R_IDEAL_GAS_EQUATION + * (((IDEAL_GAS_ENTROPY_CONSTANT * self.volume + / (amt * self.temperature)) * (mass * cap * self.temperature) + .powf(2.0 / 3.0) + 1.0) + .ln() + 15.0) + }) + }) + }) + } + } /// The total mole count of the mixture. Moles. pub fn total_moles(&self) -> f32 { self.moles.iter().sum() @@ -401,7 +426,7 @@ impl Mixture { } /// Returns a tuple with oxidation power and fuel amount of this gas mixture. pub fn get_burnability(&self) -> (f32, f32) { - use crate::types::FireInfo; + use super::types::FireInfo; super::with_gas_info(|gas_info| { self.moles .iter() @@ -445,7 +470,7 @@ impl Mixture { &self, gas_info: &[super::GasType], ) -> (Vec, Vec) { - use crate::types::FireInfo; + use super::types::FireInfo; self.moles .iter() .zip(gas_info) diff --git a/src/gas/types.rs b/src/gas/types.rs index 8fb53c06..bf0d0546 100644 --- a/src/gas/types.rs +++ b/src/gas/types.rs @@ -109,6 +109,9 @@ pub struct GasType { /// The specific heat of the gas. Duplicated in the GAS_SPECIFIC_HEATS vector for speed. /// Byond: `specific_heat`, a number. pub specific_heat: f32, + /// Gas's molar mass, in g/mol. Used for certain operations in bay machinery, can be used for + /// calculating gas movement on turfs (but isn't) + pub molar_mass: f32, /// Gas's fusion power. Used in fusion hooking, so this can be removed and ignored if you don't have fusion. /// Byond: `fusion_power`, a number. pub fusion_power: f32, @@ -133,32 +136,17 @@ impl GasType { idx, id: gas.get_string(byond_string!("id"))?.into_boxed_str(), name: gas.get_string(byond_string!("name"))?.into_boxed_str(), - flags: gas.get_number(byond_string!("flags")).map_err(|_| { - runtime!( - "Attempt to interpret non-number value as number {} {}:{}", - std::file!(), - std::line!(), - std::column!() - ) - })? as u32, + flags: gas.get_number(byond_string!("flags")).unwrap_or_default() as u32, specific_heat: gas .get_number(byond_string!("specific_heat")) - .map_err(|_| { - runtime!( - "Attempt to interpret non-number value as number {} {}:{}", - std::file!(), - std::line!(), - std::column!() - ) - })?, - fusion_power: gas.get_number(byond_string!("fusion_power")).map_err(|_| { - runtime!( - "Attempt to interpret non-number value as number {} {}:{}", - std::file!(), - std::line!(), - std::column!() - ) - })?, + .unwrap_or(20.0), + molar_mass: gas + .get_number(byond_string!("molar_mass")) + // https://github.com/Baystation12/Baystation12/blob/dev/code/modules/xgm/xgm_gas_data.dm#L39 + .unwrap_or(0.032), + fusion_power: gas + .get_number(byond_string!("fusion_power")) + .unwrap_or_default(), moles_visible: gas.get_number(byond_string!("moles_visible")).ok(), fire_info: { if let Ok(temperature) = gas.get_number(byond_string!("oxidation_temperature")) { @@ -175,8 +163,10 @@ impl GasType { FireInfo::None } }, - fire_products: if let Ok(products) = gas.get_list(byond_string!("fire_products")) { - Some( + fire_products: gas + .get_list(byond_string!("fire_products")) + .ok() + .map(|products| { (1..=products.len()) .map(|i| { let s = products.get(i).unwrap(); @@ -185,12 +175,11 @@ impl GasType { products.get(s).unwrap().as_number().unwrap(), ) }) - .collect(), - ) - } else { - None - }, - fire_energy_released: gas.get_number(byond_string!("fire_energy_released"))?, + .collect() + }), + fire_energy_released: gas + .get_number(byond_string!("fire_energy_released")) + .unwrap_or_default(), }) } } @@ -201,6 +190,8 @@ static GAS_INFO_BY_IDX: RwLock>> = const_rwlock(None); static GAS_SPECIFIC_HEATS: RwLock>> = const_rwlock(None); +static GAS_MOLAR_MASSES: RwLock>> = const_rwlock(None); + #[init(partial)] fn _create_gas_info_structs() -> Result<(), String> { unsafe { @@ -208,6 +199,7 @@ fn _create_gas_info_structs() -> Result<(), String> { }; *GAS_INFO_BY_IDX.write() = Some(Vec::new()); *GAS_SPECIFIC_HEATS.write() = Some(Vec::new()); + *GAS_MOLAR_MASSES.write() = Some(Vec::new()); Ok(()) } @@ -218,6 +210,7 @@ fn _destroy_gas_info_structs() { }; *GAS_INFO_BY_IDX.write() = None; *GAS_SPECIFIC_HEATS.write() = None; + *GAS_MOLAR_MASSES.write() = None; TOTAL_NUM_GASES.store(0, Ordering::Release); CACHED_GAS_IDS.with(|gas_ids| { gas_ids.borrow_mut().clear(); @@ -236,6 +229,11 @@ fn _hook_register_gas(gas: Value) { .as_mut() .unwrap() .push(gas_cache.specific_heat); + GAS_MOLAR_MASSES + .write() + .as_mut() + .unwrap() + .push(gas_cache.molar_mass); GAS_INFO_BY_IDX.write().as_mut().unwrap().push(gas_cache); TOTAL_NUM_GASES.fetch_add(1, Ordering::Release); // this is the only thing that stores it other than shutdown Ok(Value::null()) @@ -293,6 +291,10 @@ pub fn with_specific_heats(f: impl FnOnce(&[f32]) -> T) -> T { f(GAS_SPECIFIC_HEATS.read().as_ref().unwrap().as_slice()) } +pub fn with_molar_masses(f: impl FnOnce(&[f32]) -> T) -> T { + f(GAS_MOLAR_MASSES.read().as_ref().unwrap().as_slice()) +} + #[cfg(feature = "reaction_hooks")] pub fn gas_fusion_power(idx: &GasIDX) -> f32 { GAS_INFO_BY_IDX diff --git a/src/helpers.rs b/src/helpers.rs new file mode 100644 index 00000000..a6130eb9 --- /dev/null +++ b/src/helpers.rs @@ -0,0 +1,52 @@ +use auxtools::*; + +use crate::gas::*; + +#[hook("/proc/equalize_all_gases_in_list")] +fn _equalize_all_hook() { + use std::collections::BTreeSet; + let value_list = args + .get(0) + .ok_or_else(|| runtime!("Wrong number of args for equalize all: 0"))? + .as_list() + .map_err(|_| { + runtime!( + "Attempt to interpret non-list value as list {} {}:{}", + std::file!(), + std::line!(), + std::column!() + ) + })?; + let gas_list: BTreeSet = (1..=value_list.len()) + .map(|i| { + value_list + .get(i) + .unwrap() + .get_number(byond_string!("_extools_pointer_gasmixture")) + .unwrap() + .to_bits() as usize + }) + .collect(); // collect because get_number is way slower than the one-time allocation + GasArena::with_all_mixtures(move |all_mixtures| { + let mut tot = Mixture::new(); + let mut tot_vol: f64 = 0.0; + for &id in &gas_list { + if let Some(src_gas_lock) = all_mixtures.get(id) { + let src_gas = src_gas_lock.read(); + tot.merge(&src_gas); + tot_vol += src_gas.volume as f64; + } + } + if tot_vol > 0.0 { + for &id in &gas_list { + if let Some(dest_gas_lock) = all_mixtures.get(id) { + let dest_gas = &mut dest_gas_lock.write(); + let vol = dest_gas.volume; // don't wanna borrow it in the below + dest_gas.copy_from_mutable(&tot); + dest_gas.multiply((vol as f64 / tot_vol) as f32); + } + } + } + }); + Ok(Value::null()) +} diff --git a/src/lib.rs b/src/lib.rs index 031b64e3..0245ae13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,493 +4,17 @@ pub mod turfs; pub mod reaction; -use auxtools::*; - -use auxcleanup::*; - -use gas::*; +pub mod helpers; -use reaction::react_by_id; +use auxtools::*; -use gas::constants::*; +use gas::{amt_gases, tot_gases}; #[hook("/proc/process_atmos_callbacks")] fn _atmos_callback_handle() { auxcallback::callback_processing_hook(args) } -#[hook("/datum/gas_mixture/proc/__gasmixture_register")] -fn _register_gasmixture_hook() { - gas::GasArena::register_mix(src) -} - -#[cfg(not(feature = "auxcleanup_gas_deletion"))] -#[hook("/datum/gas_mixture/proc/__gasmixture_unregister")] -fn _unregister_gasmixture_hook() { - gas::GasMixtures::unregister_mix(unsafe { src.raw.data.id }); - Ok(Value::null()) -} - -#[cfg(feature = "auxcleanup_gas_deletion")] -#[datum_del] -fn _unregister_gasmixture_hook(v: u32) { - gas::GasArena::unregister_mix(v); -} - -#[hook("/datum/gas_mixture/proc/heat_capacity")] -fn _heat_cap_hook() { - with_mix(src, |mix| Ok(Value::from(mix.heat_capacity()))) -} - -#[hook("/datum/gas_mixture/proc/set_min_heat_capacity")] -fn _min_heat_cap_hook() { - if args.is_empty() { - Err(runtime!( - "attempted to set min heat capacity with no argument" - )) - } else { - with_mix_mut(src, |mix| { - mix.set_min_heat_capacity(args[0].as_number().unwrap_or_default()); - Ok(Value::null()) - }) - } -} - -#[hook("/datum/gas_mixture/proc/total_moles")] -fn _total_moles_hook() { - with_mix(src, |mix| Ok(Value::from(mix.total_moles()))) -} - -#[hook("/datum/gas_mixture/proc/return_pressure")] -fn _return_pressure_hook() { - with_mix(src, |mix| Ok(Value::from(mix.return_pressure()))) -} - -#[hook("/datum/gas_mixture/proc/return_temperature")] -fn _return_temperature_hook() { - with_mix(src, |mix| Ok(Value::from(mix.get_temperature()))) -} - -#[hook("/datum/gas_mixture/proc/return_volume")] -fn _return_volume_hook() { - with_mix(src, |mix| Ok(Value::from(mix.volume))) -} - -#[hook("/datum/gas_mixture/proc/thermal_energy")] -fn _thermal_energy_hook() { - with_mix(src, |mix| Ok(Value::from(mix.thermal_energy()))) -} - -#[hook("/datum/gas_mixture/proc/merge")] -fn _merge_hook() { - if args.is_empty() { - Err(runtime!("Tried merging nothing into a gas mixture")) - } else { - with_mixes_mut(src, &args[0], |src_mix, giver_mix| { - src_mix.merge(giver_mix); - Ok(Value::null()) - }) - } -} - -#[hook("/datum/gas_mixture/proc/__remove_ratio")] -fn _remove_ratio_hook() { - if args.len() < 2 { - Err(runtime!("remove_ratio called with fewer than 2 arguments")) - } else { - with_mixes_mut(src, &args[0], |src_mix, into_mix| { - src_mix.remove_ratio_into(args[1].as_number().unwrap_or_default(), into_mix); - Ok(Value::null()) - }) - } -} - -#[hook("/datum/gas_mixture/proc/__remove")] -fn _remove_hook() { - if args.len() < 2 { - Err(runtime!("remove called with fewer than 2 arguments")) - } else { - with_mixes_mut(src, &args[0], |src_mix, into_mix| { - src_mix.remove_into(args[1].as_number().unwrap_or_default(), into_mix); - Ok(Value::null()) - }) - } -} - -#[hook("/datum/gas_mixture/proc/copy_from")] -fn _copy_from_hook() { - if args.is_empty() { - Err(runtime!("Tried copying a gas mix from nothing")) - } else { - with_mixes_mut(src, &args[0], |src_mix, giver_mix| { - src_mix.copy_from_mutable(giver_mix); - Ok(Value::null()) - }) - } -} - -#[hook("/datum/gas_mixture/proc/temperature_share")] -fn _temperature_share_hook() { - let arg_num = args.len(); - match arg_num { - 2 => with_mixes_mut(src, &args[0], |src_mix, share_mix| { - Ok(Value::from(src_mix.temperature_share( - share_mix, - args[1].as_number().unwrap_or_default(), - ))) - }), - 4 => with_mix_mut(src, |mix| { - Ok(Value::from(mix.temperature_share_non_gas( - args[1].as_number().unwrap_or_default(), - args[2].as_number().unwrap_or_default(), - args[3].as_number().unwrap_or_default(), - ))) - }), - _ => Err(runtime!("Invalid args for temperature_share")), - } -} - -#[hook("/datum/gas_mixture/proc/get_gases")] -fn _get_gases_hook() { - with_mix(src, |mix| { - let gases_list: List = List::new(); - mix.for_each_gas(|idx, gas| { - if gas > GAS_MIN_MOLES { - gases_list.append(Value::from_string(&*gas_idx_to_id(idx)?)?); - } - Ok(()) - })?; - Ok(Value::from(gases_list)) - }) -} - -#[hook("/datum/gas_mixture/proc/set_temperature")] -fn _set_temperature_hook() { - let v = args - .get(0) - .ok_or_else(|| runtime!("Wrong amount of arguments for set_temperature: 0!"))? - .as_number() - .map_err(|_| { - runtime!( - "Attempt to interpret non-number value as number {} {}:{}", - std::file!(), - std::line!(), - std::column!() - ) - })?; - if v.is_finite() { - with_mix_mut(src, |mix| { - mix.set_temperature(v.max(2.7)); - Ok(Value::null()) - }) - } else { - Err(runtime!( - "Attempted to set a temperature to a number that is NaN or infinite." - )) - } -} - -#[hook("/datum/gas_mixture/proc/partial_heat_capacity")] -fn _partial_heat_capacity() { - if args.is_empty() { - Err(runtime!("Incorrect arg len for partial_heat_capacity (0).")) - } else { - with_mix(src, |mix| { - Ok(Value::from( - mix.partial_heat_capacity(gas_idx_from_value(&args[0])?), - )) - }) - } -} - -#[hook("/datum/gas_mixture/proc/set_volume")] -fn _set_volume_hook() { - if args.is_empty() { - Err(runtime!("Attempted to set volume to nothing.")) - } else { - with_mix_mut(src, |mix| { - mix.volume = args[0].as_number().map_err(|_| { - runtime!( - "Attempt to interpret non-number value as number {} {}:{}", - std::file!(), - std::line!(), - std::column!() - ) - })?; - Ok(Value::null()) - }) - } -} - -#[hook("/datum/gas_mixture/proc/get_moles")] -fn _get_moles_hook() { - if args.is_empty() { - Err(runtime!("Incorrect arg len for get_moles (0).")) - } else { - with_mix(src, |mix| { - Ok(Value::from(mix.get_moles(gas_idx_from_value(&args[0])?))) - }) - } -} - -#[hook("/datum/gas_mixture/proc/set_moles")] -fn _set_moles_hook() { - if args.len() < 2 { - return Err(runtime!("Incorrect arg len for set_moles (less than 2).")); - } - let vf = args[1].as_number().unwrap_or_default(); - if !vf.is_finite() { - return Err(runtime!("Attempted to set moles to NaN or infinity.")); - } - if vf < 0.0 { - return Err(runtime!("Attempted to set moles to a negative number.")); - } - with_mix_mut(src, |mix| { - mix.set_moles(gas_idx_from_value(&args[0])?, vf); - Ok(Value::null()) - }) -} - -#[hook("/datum/gas_mixture/proc/adjust_moles")] -fn _adjust_moles_hook(id_val: Value, num_val: Value) { - let vf = num_val.as_number().unwrap_or_default(); - with_mix_mut(src, |mix| { - mix.adjust_moles(gas_idx_from_value(id_val)?, vf); - Ok(Value::null()) - }) -} - -#[hook("/datum/gas_mixture/proc/scrub_into")] -fn _scrub_into_hook(into: Value, ratio_v: Value, gas_list: Value) { - let ratio = ratio_v.as_number().map_err(|_| { - runtime!( - "Attempt to interpret non-number value as number {} {}:{}", - std::file!(), - std::line!(), - std::column!() - ) - })?; - let gases_to_scrub = gas_list.as_list().map_err(|_| { - runtime!( - "Attempt to interpret non-list value as list {} {}:{}", - std::file!(), - std::line!(), - std::column!() - ) - })?; - if gases_to_scrub.len() == 0 { - return Ok(Value::from(false)); - } - let gas_scrub_vec = (1..=gases_to_scrub.len()) - .filter_map(|idx| gas_idx_from_value(&gases_to_scrub.get(idx).unwrap()).ok()) - .collect::>(); - with_mixes_mut(src, into, |src_gas, dest_gas| { - src_gas.transfer_gases_to(ratio, &gas_scrub_vec, dest_gas); - Ok(Value::from(true)) - }) -} - -#[hook("/datum/gas_mixture/proc/mark_immutable")] -fn _mark_immutable_hook() { - with_mix_mut(src, |mix| { - mix.mark_immutable(); - Ok(Value::null()) - }) -} - -#[hook("/datum/gas_mixture/proc/clear")] -fn _clear_hook() { - with_mix_mut(src, |mix| { - mix.clear(); - Ok(Value::null()) - }) -} - -#[hook("/datum/gas_mixture/proc/compare")] -fn _compare_hook() { - if args.is_empty() { - Err(runtime!("Tried comparing a gas mix to nothing")) - } else { - with_mixes(src, &args[0], |gas_one, gas_two| { - Ok(Value::from( - gas_one.temperature_compare(gas_two) - || gas_one.compare_with(gas_two, MINIMUM_MOLES_DELTA_TO_MOVE), - )) - }) - } -} - -#[hook("/datum/gas_mixture/proc/multiply")] -fn _multiply_hook() { - with_mix_mut(src, |mix| { - mix.multiply(if args.is_empty() { - 1.0 - } else { - args[0].as_number().unwrap_or(1.0) - }); - Ok(Value::null()) - }) -} - -#[hook("/datum/gas_mixture/proc/react")] -fn _react_hook(holder: Value) { - let mut ret: i32 = 0; - let reactions = with_mix(src, |mix| Ok(mix.all_reactable()))?; - for reaction in reactions { - ret |= react_by_id(reaction, src, holder)? - .as_number() - .unwrap_or_default() as i32; - if ret & STOP_REACTIONS == STOP_REACTIONS { - return Ok(Value::from(ret as f32)); - } - } - Ok(Value::from(ret as f32)) -} - -#[hook("/datum/gas_mixture/proc/adjust_heat")] -fn _adjust_heat_hook() { - with_mix_mut(src, |mix| { - mix.adjust_heat( - args.get(0) - .ok_or_else(|| runtime!("Wrong number of args for adjust heat: 0"))? - .as_number() - .map_err(|_| { - runtime!( - "Attempt to interpret non-number value as number {} {}:{}", - std::file!(), - std::line!(), - std::column!() - ) - })?, - ); - Ok(Value::null()) - }) -} - -#[hook("/datum/gas_mixture/proc/transfer_to")] -fn _transfer_hook(other: Value, moles: Value) { - with_mixes_mut(src, other, |our_mix, other_mix| { - other_mix.merge(&our_mix.remove(moles.as_number().map_err(|_| { - runtime!( - "Attempt to interpret non-number value as number {} {}:{}", - std::file!(), - std::line!(), - std::column!() - ) - })?)); - Ok(Value::null()) - }) -} - -#[hook("/datum/gas_mixture/proc/transfer_ratio_to")] -fn _transfer_ratio_hook(other: Value, ratio: Value) { - with_mixes_mut(src, other, |our_mix, other_mix| { - other_mix.merge(&our_mix.remove_ratio(ratio.as_number().map_err(|_| { - runtime!( - "Attempt to interpret non-number value as number {} {}:{}", - std::file!(), - std::line!(), - std::column!() - ) - })?)); - Ok(Value::null()) - }) -} - -#[hook("/datum/gas_mixture/proc/equalize_with")] -fn _equalize_with_hook() { - with_mixes_custom( - src, - args.get(0) - .ok_or_else(|| runtime!("Wrong number of args for equalize_with: 0"))?, - |src_lock, total_lock| { - let src_gas = &mut src_lock.write(); - let vol = src_gas.volume; - let total_gas = total_lock.read(); - src_gas.copy_from_mutable(&total_gas); - src_gas.multiply(vol / total_gas.volume); - Ok(Value::null()) - }, - ) -} - -#[hook("/datum/gas_mixture/proc/get_fuel_amount")] -fn _fuel_amount_hook(temp: Value) { - with_mix(src, |air| { - Ok(Value::from(temp.as_number().ok().map_or_else( - || air.get_fuel_amount(), - |new_temp| { - let mut test_air = air.clone(); - test_air.set_temperature(new_temp); - test_air.get_fuel_amount() - }, - ))) - }) -} - -#[hook("/datum/gas_mixture/proc/get_oxidation_power")] -fn _oxidation_power_hook(temp: Value) { - with_mix(src, |air| { - Ok(Value::from(temp.as_number().ok().map_or_else( - || air.get_oxidation_power(), - |new_temp| { - let mut test_air = air.clone(); - test_air.set_temperature(new_temp); - test_air.get_oxidation_power() - }, - ))) - }) -} - -#[hook("/proc/equalize_all_gases_in_list")] -fn _equalize_all_hook() { - use std::collections::BTreeSet; - let value_list = args - .get(0) - .ok_or_else(|| runtime!("Wrong number of args for equalize all: 0"))? - .as_list() - .map_err(|_| { - runtime!( - "Attempt to interpret non-list value as list {} {}:{}", - std::file!(), - std::line!(), - std::column!() - ) - })?; - let gas_list: BTreeSet = (1..=value_list.len()) - .map(|i| { - value_list - .get(i) - .unwrap() - .get_number(byond_string!("_extools_pointer_gasmixture")) - .unwrap() - .to_bits() as usize - }) - .collect(); // collect because get_number is way slower than the one-time allocation - GasArena::with_all_mixtures(move |all_mixtures| { - let mut tot = gas::Mixture::new(); - let mut tot_vol: f64 = 0.0; - for &id in &gas_list { - if let Some(src_gas_lock) = all_mixtures.get(id) { - let src_gas = src_gas_lock.read(); - tot.merge(&src_gas); - tot_vol += src_gas.volume as f64; - } - } - if tot_vol > 0.0 { - for &id in &gas_list { - if let Some(dest_gas_lock) = all_mixtures.get(id) { - let dest_gas = &mut dest_gas_lock.write(); - let vol = dest_gas.volume; // don't wanna borrow it in the below - dest_gas.copy_from_mutable(&tot); - dest_gas.multiply((vol as f64 / tot_vol) as f32); - } - } - } - }); - Ok(Value::null()) -} - #[hook("/datum/controller/subsystem/air/proc/get_amt_gas_mixes")] fn _hook_amt_gas_mixes() { Ok(Value::from(amt_gases() as f32)) diff --git a/src/turfs.rs b/src/turfs.rs index 28037b4d..c87e813a 100644 --- a/src/turfs.rs +++ b/src/turfs.rs @@ -10,9 +10,7 @@ use crate::gas::Mixture; use auxtools::*; -use crate::constants::*; - -use crate::GasArena; +use crate::gas::{constants::*, GasArena}; use dashmap::DashMap; diff --git a/src/turfs/monstermos.rs b/src/turfs/monstermos.rs index 8011a34c..cac13def 100644 --- a/src/turfs/monstermos.rs +++ b/src/turfs/monstermos.rs @@ -163,7 +163,8 @@ fn explosively_depressurize( ) -> DMResult { let mut turfs: Vec = Vec::new(); let mut space_turfs: Vec = Vec::new(); - let mut decomp_found_turfs: std::collections::HashSet = std::collections::HashSet::new(); + let mut decomp_found_turfs: std::collections::HashSet = + std::collections::HashSet::new(); turfs.push((turf_idx, turf)); let cur_orig = info.entry(turf_idx).or_default(); let mut cur_info: MonstermosInfo = Default::default(); @@ -201,9 +202,7 @@ fn explosively_depressurize( if decomp_found_turfs.contains(&loc) { continue; } - let adj_m = { - *turf_gases().get(&loc).unwrap() - }; + let adj_m = { *turf_gases().get(&loc).unwrap() }; unsafe { Value::turf_by_id_unchecked(i) }.call( "consider_firelocks", &[&unsafe { Value::turf_by_id_unchecked(loc) }], @@ -211,7 +210,6 @@ fn explosively_depressurize( decomp_found_turfs.insert(loc); info.entry(loc).or_default().take(); turfs.push((loc, adj_m)); - } } } @@ -242,9 +240,7 @@ fn explosively_depressurize( if decomp_found_turfs.contains(&loc) { continue; } - let adj_m = { - *turf_gases().get(&loc).unwrap() - }; + let adj_m = { *turf_gases().get(&loc).unwrap() }; let adj_orig = info.entry(loc).or_default(); let mut adj_info = adj_orig.get(); if !adj_m.is_immutable() { @@ -289,9 +285,7 @@ fn explosively_depressurize( hpd.append(&unsafe { Value::turf_by_id_unchecked(*i) }); } let loc = adjacent_tile_id(cur_info.curr_transfer_dir as u8, *i, max_x, max_y); - let adj_m = { - *turf_gases().get(&loc).unwrap() - }; + let adj_m = { *turf_gases().get(&loc).unwrap() }; let sum = adj_m.total_moles(); cur_info.curr_transfer_amount += sum; cur_orig.set(cur_info); diff --git a/src/turfs/processing.rs b/src/turfs/processing.rs index 63915937..590edb42 100644 --- a/src/turfs/processing.rs +++ b/src/turfs/processing.rs @@ -4,7 +4,7 @@ use auxtools::*; use super::*; -use crate::GasArena; +use crate::gas::{constants::*, GasArena}; use std::time::{Duration, Instant}; @@ -334,7 +334,7 @@ fn process_cell( so we make sure we don't do that unless we absolutely need to. Saving is fast enough. */ - let mut end_gas = Mixture::from_vol(crate::constants::CELL_VOLUME); + let mut end_gas = Mixture::from_vol(CELL_VOLUME); let mut pressure_diffs: [(TurfID, f32); 6] = Default::default(); /* The pressure here is negative From 7ea7d0077c3990cbe5578b8603ce07b0d4d9b301 Mon Sep 17 00:00:00 2001 From: Putnam3145 Date: Thu, 12 Aug 2021 10:59:41 -0700 Subject: [PATCH 2/3] also needs specific_entropy_gas() apparently --- src/gas/hooks.rs | 10 +++++++++- src/gas/mixture.rs | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/gas/hooks.rs b/src/gas/hooks.rs index 000e8d0b..5b0a78e5 100644 --- a/src/gas/hooks.rs +++ b/src/gas/hooks.rs @@ -432,7 +432,15 @@ fn _oxidation_power_hook(temp: Value) { #[cfg(feature = "specific_entropy")] #[hook("/datum/gas_mixture/proc/specific_entropy")] fn _specific_entropy_hook() { + with_mix(src, |air| Ok(Value::from(air.specific_entropy()))) +} + +#[cfg(feature = "specific_entropy")] +#[hook("/datum/gas_mixture/proc/specific_entropy_gas")] +fn _specific_entropy_gas_hook(gas: Value) { with_mix(src, |air| { - Ok(Value::from(air.specific_entropy())) + Ok(Value::from( + air.specific_entropy_gas(gas_idx_from_value(&gas)?), + )) }) } diff --git a/src/gas/mixture.rs b/src/gas/mixture.rs index 6eea916d..eb94daf0 100644 --- a/src/gas/mixture.rs +++ b/src/gas/mixture.rs @@ -216,6 +216,25 @@ impl Mixture { }) } } + /// Specific entropy of a specific gas + pub fn specific_entropy_gas(&self, idx: GasIDX) -> f32 { + with_specific_heats(|heats| { + with_molar_masses(|masses| { + self.moles + .get(idx) + .map(|amt| { + let cap = heats.get(idx).unwrap_or(&20.0); + let mass = masses.get(idx).unwrap_or(&0.032); + R_IDEAL_GAS_EQUATION + * (((IDEAL_GAS_ENTROPY_CONSTANT * self.volume + / (amt * self.temperature)) * (mass * cap * self.temperature) + .powf(2.0 / 3.0) + 1.0) + .ln() + 15.0) + }) + .unwrap_or(SPECIFIC_ENTROPY_VACUUM) + }) + }) + } /// The total mole count of the mixture. Moles. pub fn total_moles(&self) -> f32 { self.moles.iter().sum() From 321729158ffea5e8f2c6ff81e7b2aeaac2545818 Mon Sep 17 00:00:00 2001 From: Putnam3145 Date: Mon, 16 Aug 2021 18:49:20 -0700 Subject: [PATCH 3/3] Adds heat radiation proc too --- src/gas/hooks.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/gas/hooks.rs b/src/gas/hooks.rs index 5b0a78e5..86da0b01 100644 --- a/src/gas/hooks.rs +++ b/src/gas/hooks.rs @@ -444,3 +444,17 @@ fn _specific_entropy_gas_hook(gas: Value) { )) }) } + +#[hook("/proc/radiate_heat")] +fn _radiate_heat(temperature_v: Value, area_v: Value, heat_cap_v: Value, env_temperature_v: Value) { + let temperature = temperature_v.as_number()? as f64; + let area = area_v.as_number()? as f64; + let heat_cap = heat_cap_v.as_number()? as f64; + let env_temperature = env_temperature_v.as_number().unwrap_or(TCMB) as f64; + let initial_energy = temperature * heat_cap; + let total_radiation = (STEFAN_BOLTZMANN_CONSTANT * env_temperature.powi(4) * area) + - (STEFAN_BOLTZMANN_CONSTANT * temperature.powi(4) * area); + Ok(Value::from( + ((initial_energy + total_radiation) / heat_cap) as f32, + )) +}