diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index 0a0a56fb74..78f5d121ff 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -21,7 +21,11 @@ obj/machinery/atmospherics/binary/pump var/on = 0 var/target_pressure = ONE_ATMOSPHERE + var/power_rating = 7500 //A measure of how powerful the pump is, in Watts. 7500 W ~ 10 HP + use_power = 1 + idle_power_usage = 10 //10 W for internal circuitry and stuff + var/frequency = 0 var/id = null var/datum/radio_frequency/radio_connection @@ -64,13 +68,40 @@ obj/machinery/atmospherics/binary/pump return 1 //Calculate necessary moles to transfer using PV=nRT - if((air1.total_moles() > 0) && (air1.temperature>0)) + if((air1.total_moles() > 0) && (air1.temperature > 0 || air2.temperature > 0)) + var/air_temperature = (air2.temperature > 0)? air2.temperature : air1.temperature var/pressure_delta = target_pressure - output_starting_pressure - var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas + var/transfer_moles = pressure_delta*air2.volume/(air_temperature * R_IDEAL_GAS_EQUATION) //The number of moles that would have to be transfered to bring air2 to the target pressure + + //estimate the amount of energy required + var/specific_entropy = air2.specific_entropy() - air1.specific_entropy() //air2 is gaining moles, air1 is loosing + var/specific_power = 0 + + src.visible_message("DEBUG: [src] >>> terminal pressures: sink = [air2.return_pressure()] kPa, source = [air1.return_pressure()] kPa") + src.visible_message("DEBUG: [src] >>> specific entropy = [air2.specific_entropy()] - [air1.specific_entropy()] = [specific_entropy] J/K") + + //if specific_entropy >= 0 then gas just flows naturally and we are not limited by how powerful the pump is. + if (specific_entropy < 0) + specific_power = -specific_entropy*air_temperature //how much power we need per mole + + src.visible_message("DEBUG: [src] >>> limiting transfer_moles to [power_rating / (air_temperature * -specific_entropy)] mol") + transfer_moles = min(transfer_moles, power_rating / specific_power) + + //Actually transfer the gas var/datum/gas_mixture/removed = air1.remove(transfer_moles) air2.merge(removed) + + src.visible_message("DEBUG: [src] >>> entropy_change = [specific_entropy*transfer_moles] J/K") + + //if specific_entropy >= 0 then gas is flowing naturally and we don't need to use extra power + if (specific_entropy < 0) + //pump draws power and heats gas according to 2nd law of thermodynamics + + var/power_draw = transfer_moles*specific_power + air2.add_thermal_energy(power_draw) + use_power(power_draw) + + src.visible_message("DEBUG: [src] >>> drawing [power_draw] W of power.") if(network1) network1.update = 1 diff --git a/code/ZAS/_gas_mixture.dm b/code/ZAS/_gas_mixture.dm index 63c915a3e7..943fcbb685 100644 --- a/code/ZAS/_gas_mixture.dm +++ b/code/ZAS/_gas_mixture.dm @@ -4,16 +4,24 @@ What are the archived variables for? This prevents race conditions that arise based on the order of tile processing. */ -#define SPECIFIC_HEAT_TOXIN 200 -#define SPECIFIC_HEAT_AIR 20 -#define SPECIFIC_HEAT_CDO 30 +#define SPECIFIC_HEAT_TOXIN 200 // J/(mol*K) +#define SPECIFIC_HEAT_AIR 20 // J/(mol*K) +#define SPECIFIC_HEAT_CDO 30 // J/(mol*K) #define HEAT_CAPACITY_CALCULATION(oxygen,carbon_dioxide,nitrogen,phoron) \ max(0, carbon_dioxide * SPECIFIC_HEAT_CDO + (oxygen + nitrogen) * SPECIFIC_HEAT_AIR + phoron * SPECIFIC_HEAT_TOXIN) +//we should really have a datum for each gas instead of a bunch of constants +#define MOL_MASS_O2 0.032 // kg/mol +#define MOL_MASS_N2 0.028 // kg/mol +#define MOL_MASS_CDO 0.044 // kg/mol +#define MOL_MASS_PHORON 0.289 // kg/mol + #define MINIMUM_HEAT_CAPACITY 0.0003 #define QUANTIZE(variable) (round(variable,0.0001)) #define TRANSFER_FRACTION 5 //What fraction (1/#) of the air difference to try and transfer +#define SPECIFIC_ENTROPY_VACUUM 1500 //technically vacuum doesn't have a specific entropy. Just use a really big number here to show that it's easy to add gas to vacuum and hard to take gas out. + /hook/startup/proc/createGasOverlays() plmaster = new /obj/effect/overlay() plmaster.icon = 'icons/effects/tile_effects.dmi' @@ -28,11 +36,11 @@ What are the archived variables for? slmaster.mouse_opacity = 0 return 1 -/datum/gas/sleeping_agent/specific_heat = 40 //These are used for the "Trace Gases" stuff, but is buggy. +/datum/gas/sleeping_agent/specific_heat = 40 //These are used for the "Trace Gases" stuff, but is buggy. //J/(mol*K) -/datum/gas/oxygen_agent_b/specific_heat = 300 +/datum/gas/oxygen_agent_b/specific_heat = 300 //J/(mol*K) -/datum/gas/volatile_fuel/specific_heat = 30 +/datum/gas/volatile_fuel/specific_heat = 30 //J/(mol*K) /datum/gas var/moles = 0 @@ -164,6 +172,42 @@ What are the archived variables for? return heat_capacity()*(new_temperature - temperature) + +//This is so overkill for spessmen it's hilarious. +//While this proc will return an accurate measure of the entropy, it's much easier to use the specific_entropy_change() proc. +/datum/gas_mixture/proc/specific_entropy() + //Purpose: Returning the specific entropy of the gas mix, i.e. the entropy gained or lost per mole of gas added or removed. + //Called by: Anyone who wants to know how much energy it takes to move gases around in a steady state process (e.g. gas pumps) + //Inputs: None + //Outputs: Specific Entropy. + + //Jut assume everything is an ideal gas, so we can use the Ideal Gas Sackur-Tetrode equation. + //After we convert to moles and Liters and extract all those crazy constants inside the ln() we end up with: + //S = R * moles * ( ln[ constant * volume / moles * (molecular_mass * internal_energy / moles)^(3/2) ] + 5/2 ) + //Where constant is IDEAL_GAS_ENTROPY_CONSTANT defined in setup.dm + + //We need to do this calculation for each type of gas in the mix and add them all up, to properly capture the entropy of mixing. + //It would be nice if each gas type was a datum, then we could just iterate through a list + + //the number of moles inside the square root gets divided out + var/sp_entropy_oxygen = ( log( IDEAL_GAS_ENTROPY_CONSTANT * volume / (oxygen + 0.001) * sqrt( ( MOL_MASS_O2 * SPECIFIC_HEAT_AIR * (temperature + 1) ) ** 3 ) + 1) + 5/2 ) + + var/sp_entropy_nitrogen = ( log( IDEAL_GAS_ENTROPY_CONSTANT * volume / (nitrogen + 0.001) * sqrt( ( MOL_MASS_N2 * SPECIFIC_HEAT_AIR * (temperature + 1) ) ** 3 ) + 1 ) + 5/2 ) + + var/sp_entropy_carbon_dioxide = ( log( IDEAL_GAS_ENTROPY_CONSTANT * volume / (carbon_dioxide + 0.001) * sqrt( ( MOL_MASS_CDO * SPECIFIC_HEAT_CDO * (temperature + 1) + 1 ) ** 3 ) ) + 5/2 ) + + var/sp_entropy_phoron = ( log( IDEAL_GAS_ENTROPY_CONSTANT * volume / (phoron + 0.001) * sqrt( ( MOL_MASS_PHORON * SPECIFIC_HEAT_TOXIN * (temperature + 1) ) ** 3 ) + 1 ) + 5/2 ) + + if (total_moles > 0) + var/oxygen_ratio = oxygen/total_moles + var/nitrogen_ratio = nitrogen/total_moles + var/carbon_dioxide_ratio = carbon_dioxide/total_moles + var/phoron_ratio = phoron/total_moles + + return R_IDEAL_GAS_EQUATION * ( oxygen_ratio*sp_entropy_oxygen + nitrogen_ratio*sp_entropy_nitrogen + carbon_dioxide_ratio*sp_entropy_carbon_dioxide + phoron_ratio*sp_entropy_phoron ) + + return SPECIFIC_ENTROPY_VACUUM + /datum/gas_mixture/proc/total_moles() return total_moles /*var/moles = oxygen + carbon_dioxide + nitrogen + phoron diff --git a/code/setup.dm b/code/setup.dm index 8e46f4190a..5b4fd76e08 100644 --- a/code/setup.dm +++ b/code/setup.dm @@ -8,6 +8,7 @@ #define ONE_ATMOSPHERE 101.325 //kPa #define STEFAN_BOLTZMANN_CONSTANT 0.0000000567 //W/(m^2*K^4) +#define IDEAL_GAS_ENTROPY_CONSTANT 1164 //(mol^3 * s^3) / (kg^3 * L). Equal to (4*pi/(avrogadro's number * planck's constant)^2)^(3/2) / (avrogadro's number * 1000 Liters per m^3). #define CELL_VOLUME 2500 //liters in a cell #define MOLES_CELLSTANDARD (ONE_ATMOSPHERE*CELL_VOLUME/(T20C*R_IDEAL_GAS_EQUATION)) //moles in a 2.5 m^3 cell at 101.325 Pa and 20 degC