Adds gas mixing proc and updates gas mixer

Also includes a whole bunch of fixes and updates for atmos machinery
This commit is contained in:
mwerezak
2014-08-02 17:43:08 -04:00
parent d034511ee6
commit 3647fa4d7d
13 changed files with 244 additions and 179 deletions
+101 -15
View File
@@ -25,7 +25,7 @@
//Calculate the amount of energy required and limit transfer_moles based on available power
var/specific_power = calculate_specific_power(source, sink)/ATMOS_PUMP_EFFICIENCY //this has to be calculated before we modify any gas mixtures
if (available_power && specific_power > 0)
if (!isnull(available_power) && specific_power > 0)
transfer_moles = min(transfer_moles, available_power / specific_power)
if (transfer_moles < MINUMUM_MOLES_TO_PUMP) //if we cant transfer enough gas just stop to avoid further processing
@@ -82,7 +82,7 @@
total_transfer_moles = min(total_transfer_moles, total_filterable_moles)
//limit transfer_moles based on available power
if (available_power && total_specific_power > 0)
if (!isnull(available_power) && total_specific_power > 0)
total_transfer_moles = min(total_transfer_moles, available_power/total_specific_power)
if (total_transfer_moles < MINUMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing
@@ -122,6 +122,7 @@
filtering &= source.gas //only filter gasses that are actually there.
var/total_specific_power = 0 //the power required to remove one mole of input gas
var/total_filterable_moles = 0
var/total_unfilterable_moles = 0
var/list/specific_power_gas = list() //the power required to remove one mole of pure gas, for each gas type
@@ -135,9 +136,7 @@
else
specific_power_gas[g] = calculate_specific_power_gas(g, source, sink_clean)/ATMOS_FILTER_EFFICIENCY
total_unfilterable_moles += source.gas[g]
var/total_specific_power = 0 //the power required to remove one mole of input gas
for (var/g in source.gas)
var/ratio = source.gas[g]/source.total_moles //converts the specific power per mole of pure gas to specific power per mole of input gas mix
total_specific_power = specific_power_gas[g]*ratio
@@ -148,7 +147,7 @@
total_transfer_moles = min(total_transfer_moles, source.total_moles)
//limit transfer_moles based on available power
if (available_power && total_specific_power > 0)
if (!isnull(available_power) && total_specific_power > 0)
total_transfer_moles = min(total_transfer_moles, available_power/total_specific_power)
if (total_transfer_moles < MINUMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing
@@ -175,13 +174,79 @@
sink_filtered.update_values()
sink_clean.merge(removed)
//1LTD energy is conserved
if (filtered_power_used > 0)
sink_filtered.add_thermal_energy(filtered_power_used) //1st law - energy is conserved
sink_filtered.add_thermal_energy(filtered_power_used)
if (unfiltered_power_used > 0)
sink_clean.add_thermal_energy(unfiltered_power_used) //1st law - energy is conserved
sink_clean.add_thermal_energy(unfiltered_power_used)
return filtered_power_used + unfiltered_power_used
//Similar deal as the other atmos process procs.
//mix_sources maps input gas mixtures to mix ratios. The mix ratios MUST add up to 1.
/obj/machinery/atmospherics/proc/mix_gas(var/list/mix_sources, var/datum/gas_mixture/sink, var/total_transfer_moles = null, var/available_power = null)
if (!mix_sources.len)
return -1
var/total_specific_power = 0
var/total_mixing_moles = null
var/total_input_volume = 0 //for flow rate calculation
var/total_input_moles = 0 //for flow rate calculation
var/list/source_specific_power = list()
for (var/datum/gas_mixture/source in mix_sources)
if (source.total_moles < MINUMUM_MOLES_TO_FILTER)
return -1 //either mix at the set ratios or mix no gas at all
var/mix_ratio = mix_sources[source]
if (!mix_ratio)
continue
//mixing rate is limited by the source with the least amount of available gas
if (isnull(total_mixing_moles))
total_mixing_moles = source.total_moles/mix_ratio
else
total_mixing_moles = min(total_mixing_moles, source.total_moles/mix_ratio)
source_specific_power[source] = calculate_specific_power(source, sink)*mix_ratio/ATMOS_FILTER_EFFICIENCY
total_specific_power += source_specific_power[source]
total_input_volume += source.volume
total_input_moles += source.total_moles
if (total_mixing_moles < MINUMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing
return -1
if (!total_transfer_moles)
total_transfer_moles = total_mixing_moles
else
total_transfer_moles = min(total_mixing_moles, total_transfer_moles)
//limit transfer_moles based on available power
if (!isnull(available_power) && total_specific_power > 0)
total_transfer_moles = min(total_transfer_moles, available_power / total_specific_power)
if (total_transfer_moles < MINUMUM_MOLES_TO_FILTER) //if we cant transfer enough gas just stop to avoid further processing
return -1
last_flow_rate = (total_transfer_moles/total_input_moles)*total_input_volume //group_multiplier gets divided out here
var/total_power_draw = 0
for (var/datum/gas_mixture/source in mix_sources)
var/mix_ratio = mix_sources[source]
if (!mix_ratio)
continue
var/transfer_moles = total_transfer_moles * mix_ratio
var/datum/gas_mixture/removed = source.remove(transfer_moles)
var/power_draw = transfer_moles * source_specific_power[source]
removed.add_thermal_energy(power_draw) //conservation of energy
total_power_draw += power_draw
sink.merge(removed)
return total_power_draw
/*
Helper procs for various things.
*/
@@ -212,21 +277,42 @@
return specific_power
//This proc handles power usages so that we only have to call use_power() when the pump is loaded but not at full load.
//This proc handles power usages.
//Calling update_use_power() or use_power() too often will result in lag since updating area power can be costly.
//This proc implements an approximation scheme that will cause area power updates to be triggered less often.
//By having atmos machinery use this proc it is easy to change the power usage approximation for all atmos machines
/obj/machinery/atmospherics/proc/handle_power_draw(var/usage_amount)
//***This scheme errs on the side of using more power. Using this will mean that sometimes atmos machines use more power than they need, but won't get power for free.
if (usage_amount > idle_power_usage)
update_use_power(1)
else
use_power = 1 //Don't update here. We will use more power than we are supposed to, but trigger less area power updates.
switch (use_power)
if (0) return 0
if (1) return idle_power_usage
if (2 to INFINITY) return active_power_usage
/* Alternate power usage schemes
//***This scheme has the most accurate power usage, but triggers area power updates too often.
if (usage_amount > active_power_usage - 5)
update_use_power(2)
else
use_power = 1 //Don't update here. Sure, we will use more power than we are supposed to, but it's easier on CPU
/*
//This is the correct way to update pump power usage. Unfortunately it is also pretty laggy.
//Leaving this here in case someone finds a way to do this that doesn't involve doing area power updates all the time.
update_use_power(1)
if (usage_amount > idle_power_usage)
use_power(round(usage_amount))
*/
//***This scheme errs on the side of using less power. Using this will mean that sometimes atmos machines get free power.
if (usage_amount > active_power_usage - 5)
update_use_power(2)
else
use_power = 1 //Don't need to update here.
*/
/*
//DEBUG
@@ -93,12 +93,8 @@ Thus, the two variables affect pump operation are set in New():
use_power = 0 //don't force update - easier on CPU
last_power_draw = 0
last_flow_rate = 0
else if (power_draw > 0)
handle_power_draw(power_draw)
last_power_draw = power_draw
else
handle_power_draw(idle_power_usage)
last_power_draw = idle_power_usage
last_power_draw = handle_power_draw(power_draw)
return 1
@@ -8,6 +8,8 @@
var/list/filters = new()
var/datum/omni_port/input
var/datum/omni_port/output
var/target_pressure = ONE_ATMOSPHERE
/obj/machinery/atmospherics/omni/filter/Del()
input = null
@@ -13,6 +13,8 @@
var/tag_south_con
var/tag_east_con
var/tag_west_con
var/target_pressure = ONE_ATMOSPHERE
/obj/machinery/atmospherics/omni/mixer/New()
..()
@@ -11,7 +11,7 @@
var/on = 0
var/configuring = 0
var/target_pressure = ONE_ATMOSPHERE
//var/target_pressure = ONE_ATMOSPHERE //a base type as abstract as this should NOT be making these kinds of assumptions
var/tag_north = ATM_NONE
var/tag_south = ATM_NONE
@@ -15,16 +15,18 @@
var/max_flow_rate = 200 //L/s
var/set_flow_rate = 200
/*
Filter types:
-1: Nothing
0: Phoron: Phoron, Oxygen Agent B
1: Oxygen: Oxygen ONLY
2: Nitrogen: Nitrogen ONLY
3: Carbon Dioxide: Carbon Dioxide ONLY
4: Sleeping Agent (N2O)
*/
var/filter_type = 0
/*
Filter types:
-1: Nothing
0: Phoron: Phoron, Oxygen Agent B
1: Oxygen: Oxygen ONLY
2: Nitrogen: Nitrogen ONLY
3: Carbon Dioxide: Carbon Dioxide ONLY
4: Sleeping Agent (N2O)
*/
var/list/filtered_out = list()
var/frequency = 0
var/datum/radio_frequency/radio_connection
@@ -90,23 +92,7 @@ Filter types:
var/transfer_moles = (set_flow_rate/air1.volume)*air1.total_moles
var/power_draw = -1
if (transfer_moles > 0)
var/list/filtered_out
switch(filter_type)
if(0) //removing hydrocarbons
filtered_out = list("phoron", "oxygen_agent_b")
if(1) //removing O2
filtered_out = list("oxygen")
if(2) //removing N2
filtered_out = list("nitrogen")
if(3) //removing CO2
filtered_out = list("carbon_dioxide")
if(4)//removing N2O
filtered_out = list("sleeping_agent")
else
filtered_out = null
if (transfer_moles > MINUMUM_MOLES_TO_FILTER)
power_draw = filter_gas(filtered_out, air1, air2, air3, transfer_moles, active_power_usage)
if(network2)
@@ -122,10 +108,8 @@ Filter types:
//update_use_power(0)
use_power = 0 //don't force update - easier on CPU
last_flow_rate = 0
else if (power_draw > 0)
handle_power_draw(power_draw)
else
handle_power_draw(idle_power_usage)
handle_power_draw(power_draw)
return 1
@@ -190,9 +174,9 @@ Filter types:
<A href='?src=\ref[src];filterset=4'>Nitrous Oxide</A><BR>
<A href='?src=\ref[src];filterset=-1'>Nothing</A><BR>
<HR>
<B>Desirable input flow rate:</B>
<B>Set Flow Rate Limit:</B>
[src.set_flow_rate]L/s | <a href='?src=\ref[src];set_flow_rate=1'>Change</a><BR>
<B>Flow rate:</B>[last_flow_rate]L/s
<B>Flow rate: </B>[round(last_flow_rate, 0.1)]L/s
"}
/*
user << browse("<HEAD><TITLE>[src.name] control</TITLE></HEAD>[dat]","window=atmo_filter")
@@ -214,7 +198,22 @@ Filter types:
usr.set_machine(src)
src.add_fingerprint(usr)
if(href_list["filterset"])
src.filter_type = text2num(href_list["filterset"])
filter_type = text2num(href_list["filterset"])
filtered_out.Cut() //no need to create new lists unnecessarily
switch(filter_type)
if(0) //removing hydrocarbons
filtered_out += "phoron"
filtered_out += "oxygen_agent_b"
if(1) //removing O2
filtered_out += "oxygen"
if(2) //removing N2
filtered_out += "nitrogen"
if(3) //removing CO2
filtered_out += "carbon_dioxide"
if(4)//removing N2O
filtered_out += "sleeping_agent"
if (href_list["temp"])
src.temp = null
if(href_list["set_flow_rate"])
@@ -6,7 +6,15 @@
name = "Gas mixer"
var/target_pressure = ONE_ATMOSPHERE
use_power = 1
idle_power_usage = 150 //internal circuitry, friction losses and stuff
active_power_usage = 3700 //This also doubles as a measure of how powerful the mixer is, in Watts. 3700 W ~ 5 HP
var/max_flow_rate = 200 //L/s
var/set_flow_rate = 200
var/list/mixing_inputs
//for mapping
var/node1_concentration = 0.5
var/node2_concentration = 0.5
@@ -62,56 +70,39 @@
/obj/machinery/atmospherics/trinary/mixer/process()
..()
if(!on)
return 0
//For some reason this doesn't work even in initialize(), so it goes here.
if (!mixing_inputs)
mixing_inputs = list(src.air1 = node1_concentration, src.air2 = node2_concentration)
if((stat & (NOPOWER|BROKEN)) || !on)
update_use_power(0) //usually we get here because a player turned a pump off - definitely want to update.
last_flow_rate = 0
return
//Figure out the amount of moles to transfer
var/transfer_moles = (set_flow_rate*mixing_inputs[air1]/air1.volume)*air1.total_moles + (set_flow_rate*mixing_inputs[air1]/air2.volume)*air2.total_moles
var/power_draw = -1
if (transfer_moles > MINUMUM_MOLES_TO_FILTER)
power_draw = mix_gas(mixing_inputs, air3, transfer_moles, active_power_usage)
if(network1 && mixing_inputs[air1])
network1.update = 1
var/output_starting_pressure = air3.return_pressure()
if(network2 && mixing_inputs[air2])
network2.update = 1
if(output_starting_pressure >= target_pressure)
//No need to mix if target is already full!
return 1
//Calculate necessary moles to transfer using PV=nRT
var/pressure_delta = target_pressure - output_starting_pressure
var/transfer_moles1 = 0
var/transfer_moles2 = 0
if(air1.temperature > 0)
transfer_moles1 = (node1_concentration*pressure_delta)*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION)
if(air2.temperature > 0)
transfer_moles2 = (node2_concentration*pressure_delta)*air3.volume/(air2.temperature * R_IDEAL_GAS_EQUATION)
var/air1_moles = air1.total_moles
var/air2_moles = air2.total_moles
if((air1_moles < transfer_moles1) || (air2_moles < transfer_moles2))
if(!transfer_moles1 || !transfer_moles2) return
var/ratio = min(air1_moles/transfer_moles1, air2_moles/transfer_moles2)
transfer_moles1 *= ratio
transfer_moles2 *= ratio
//Actually transfer the gas
if(transfer_moles1 > 0)
var/datum/gas_mixture/removed1 = air1.remove(transfer_moles1)
air3.merge(removed1)
if(transfer_moles2 > 0)
var/datum/gas_mixture/removed2 = air2.remove(transfer_moles2)
air3.merge(removed2)
if(network1 && transfer_moles1)
network1.update = 1
if(network2 && transfer_moles2)
network2.update = 1
if(network3)
network3.update = 1
if(network3)
network3.update = 1
if (power_draw < 0)
//update_use_power(0)
use_power = 0 //don't force update - easier on CPU
last_flow_rate = 0
else
handle_power_draw(power_draw)
return 1
/obj/machinery/atmospherics/trinary/mixer/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
@@ -142,20 +133,22 @@
return
usr.set_machine(src)
var/dat = {"<b>Power: </b><a href='?src=\ref[src];power=1'>[on?"On":"Off"]</a><br>
<b>Desirable output pressure: </b>
[target_pressure]kPa | <a href='?src=\ref[src];set_press=1'>Change</a>
<b>Set Flow Rate Limit: </b>
[set_flow_rate]L/s | <a href='?src=\ref[src];set_press=1'>Change</a>
<br>
<b>Flow Rate: </b>[round(last_flow_rate, 0.1)]L/s
<br><hr>
<b>Node 1 Concentration:</b>
<a href='?src=\ref[src];node1_c=-0.1'><b>-</b></a>
<a href='?src=\ref[src];node1_c=-0.01'>-</a>
[node1_concentration]([node1_concentration*100]%)
[mixing_inputs[air1]]([mixing_inputs[air1]*100]%)
<a href='?src=\ref[src];node1_c=0.01'><b>+</b></a>
<a href='?src=\ref[src];node1_c=0.1'>+</a>
<br>
<b>Node 2 Concentration:</b>
<a href='?src=\ref[src];node2_c=-0.1'><b>-</b></a>
<a href='?src=\ref[src];node2_c=-0.01'>-</a>
[node2_concentration]([node2_concentration*100]%)
[mixing_inputs[air2]]([mixing_inputs[air2]*100]%)
<a href='?src=\ref[src];node2_c=0.01'><b>+</b></a>
<a href='?src=\ref[src];node2_c=0.1'>+</a>
"}
@@ -169,16 +162,16 @@
if(href_list["power"])
on = !on
if(href_list["set_press"])
var/new_pressure = input(usr,"Enter new output pressure (0-4500kPa)","Pressure control",src.target_pressure) as num
src.target_pressure = max(0, min(4500, new_pressure))
var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[max_flow_rate]L/s)","Flow Rate Control",src.set_flow_rate) as num
src.set_flow_rate = max(0, min(max_flow_rate, new_flow_rate))
if(href_list["node1_c"])
var/value = text2num(href_list["node1_c"])
src.node1_concentration = max(0, min(1, src.node1_concentration + value))
src.node2_concentration = max(0, min(1, src.node2_concentration - value))
src.mixing_inputs[air1] = max(0, min(1, src.mixing_inputs[air1] + value))
src.mixing_inputs[air2] = 1.0 - mixing_inputs[air1]
if(href_list["node2_c"])
var/value = text2num(href_list["node2_c"])
src.node2_concentration = max(0, min(1, src.node2_concentration + value))
src.node1_concentration = max(0, min(1, src.node1_concentration - value))
src.mixing_inputs[air2] = max(0, min(1, src.mixing_inputs[air2] + value))
src.mixing_inputs[air1] = 1.0 - mixing_inputs[air2]
src.update_icon()
src.updateUsrDialog()
return
@@ -204,6 +197,7 @@ obj/machinery/atmospherics/trinary/mixer/t_mixer/New()
initialize_directions = WEST|NORTH|SOUTH
obj/machinery/atmospherics/trinary/mixer/t_mixer/initialize()
..()
if(node1 && node2 && node3) return
var/node1_connect = turn(dir, -90)
@@ -249,6 +243,7 @@ obj/machinery/atmospherics/trinary/mixer/m_mixer/New()
initialize_directions = WEST|SOUTH|EAST
obj/machinery/atmospherics/trinary/mixer/m_mixer/initialize()
..()
if(node1 && node2 && node3) return
var/node1_connect = turn(dir, -180)
@@ -154,7 +154,7 @@
if(pressure_delta > 0.5)
if(pump_direction) //internal -> external
var/output_volume = environment.volume * environment.group_multiplier
var/air_temperature = environment.temperature? environment.volume : air_contents.temperature
var/air_temperature = environment.temperature? environment.temperature : air_contents.temperature
var/transfer_moles = pressure_delta*output_volume/(air_temperature * R_IDEAL_GAS_EQUATION)
power_draw = pump_gas(air_contents, environment, transfer_moles, active_power_usage)
@@ -176,12 +176,8 @@
last_flow_rate = 0
//update_use_power(0)
use_power = 0 //don't force update - easier on CPU
if (power_draw > 0)
handle_power_draw(power_draw)
last_power_draw = power_draw
else
handle_power_draw(idle_power_usage)
last_power_draw = idle_power_usage
last_power_draw = handle_power_draw(power_draw)
return 1
@@ -367,6 +363,7 @@
/obj/machinery/atmospherics/unary/vent_pump/examine()
set src in oview(1)
..()
usr << "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W"
if(welded)
usr << "It seems welded shut."
@@ -138,12 +138,9 @@
if (power_draw < 0)
//update_use_power(0)
use_power = 0 //don't force update. Sure, we will continue to use power even though we're not pumping anything, but it is easier on the CPU
else if (power_draw > 0)
else
//last_power_draw = power_draw
handle_power_draw(power_draw)
else
//last_power_draw = idle_power_usage
handle_power_draw(idle_power_usage)
if(network)
network.update = 1
@@ -250,6 +247,11 @@
new /obj/item/pipe(loc, make_from=src)
del(src)
/obj/machinery/atmospherics/unary/vent_scrubber/examine()
set src in oview(1)
..()
usr << "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s"
/obj/machinery/atmospherics/unary/vent_scrubber/Del()
if(initial_loc)
initial_loc.air_scrub_info -= id_tag