diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index 0a0a56fb74..3518fc4497 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -21,16 +21,29 @@ 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 + + //The maximum amount of volume in Liters the pump can transfer in 1 second. + //This is limited by how fast the pump can spin without breaking, and means you can't instantly fill up the distro even when it's empty (at 10000, it will take about 15 seconds) + var/max_volume_transfer = 10000 + 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 + + New() + ..() + active_power_usage = power_rating //make sure this is equal to the max power rating so that auto use power works correctly highcap name = "High capacity gas pump" desc = "A high capacity pump" - target_pressure = 15000000 + target_pressure = 15000000 //15 GPa? Really? + power_rating = 112500 //150 Horsepower on on = 1 @@ -58,19 +71,65 @@ obj/machinery/atmospherics/binary/pump return 0 var/output_starting_pressure = air2.return_pressure() - if( (target_pressure - output_starting_pressure) < 0.01) //No need to pump gas if target is already reached! + if (use_power >= 2) + update_use_power(1) return 1 + + var/output_volume = air2.volume + if (network2 && network2.air_transient) + output_volume = network2.air_transient.volume + output_volume = min(output_volume, max_volume_transfer) //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*output_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 = round(transfer_moles*specific_power) + air2.add_thermal_energy(power_draw) + + if (power_draw >= power_rating - 5) //if we are close enough to max power just active_power_usage + if (use_power < 2) + update_use_power(2) + else + if (use_power >= 2) + update_use_power(1) + //src.visible_message("DEBUG: [src] >>> Forcing area power update: use_power changed to [use_power]") + + if (power_draw > idle_power_usage) + use_power(power_draw) + else + if (use_power >= 2) + update_use_power(1) + //src.visible_message("DEBUG: [src] >>> Forcing area power update: use_power changed to [use_power]") + + //src.visible_message("DEBUG: [src] >>> drawing [power_draw] W of power.") if(network1) network1.update = 1 @@ -132,6 +191,7 @@ obj/machinery/atmospherics/binary/pump if("power_toggle" in signal.data) on = !on + update_use_power(on) if("set_output_pressure" in signal.data) target_pressure = between( 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/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index e13147f5fb..accd117cd2 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -157,6 +157,11 @@ Class Procs: if(prob(50)) del(src) +//sets the use_power var and then forces an area power update +/obj/machinery/proc/update_use_power(var/new_use_power) + use_power = new_use_power + use_power(0) //force area power update + /obj/machinery/proc/auto_use_power() if(!powered(power_channel)) return 0 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