Jump to content

Cooling via hydrogen pipe network.


Recommended Posts

Looking a bit more deeply into the heat transfer code for pipes, I see this function in ConduitTemperatureManager (the code calls pipes Conduits):

  public void SimUpdate(float dt)
  {
    for (int index = 0; index < this.data.Count; ++index)
    {
      ConduitTemperatureManager.Data data1 = this.data[index];
      if ((double) data1.heatCapacity != 0.0)
      {
        StructureTemperatureData data2 = GameComps.StructureTemperatures.GetData(data1.conduitStructureTemperatureHandle);
        float temperature1 = data1.temperature;
        float temperature2 = data2.Temperature;
        float energyFlow = SimUtil.CalculateEnergyFlow(temperature1, data1.thermalConductivity, temperature2, data1.conduitThermalConductivity, this.contentsSurfaceArea * ConduitTemperatureManager.ContentsScaleFactor, 1f);
        float delta_kilojoules = SimUtil.ClampEnergyTransfer(dt, temperature1, data1.heatCapacity, temperature2, data1.conduitHeatCapacity, energyFlow);
        float kilojoules = -delta_kilojoules;
        float num = this.ModifyTemperature(data1.temperature, data1.heatCapacity, data2.Temperature, data1.conduitHeatCapacity, ref kilojoules);
        SimUtil.CheckValidValue(num);
        data1.temperature = num;
        this.data[index] = data1;
        if ((double) num < (double) data1.lowStateTransitionTemperature)
          data2.primaryElement.Trigger(-700727624, (object) null);
        else if ((double) num > (double) data1.highStateTransitionTemperature)
          data2.primaryElement.Trigger(-1152799878, (object) null);
        data2.ModifyEnergy(delta_kilojoules);
      }
    }
  }

ConntentsScaleFactor is a constant of 50, and it looks like contentsSurfaceArea gets passed into the constructor, which is only used in one place, passing in 1. SimUtil.CalculateEnergyFlow makes sense, it's just using a constant thickness of 1m and then the thermal conductivity and temperatures to correctly calculate how much energy flow would pass between. SimUtil.ClampEnergyTransfer, however, seems to have incorrect math, which may actually be responsible for the liquid cooling bug too (I renamed some of the local variables because they were all "num1" and stuff after decompilation):

    public static float ClampEnergyTransfer(float dt, float source_temp, float source_heat_capacity, float dest_temp, float dest_heat_capacity, float max_temp_watts_transferred)
    {
      float max_temp_joules = (float) ((double) max_temp_watts_transferred * (double) dt / 1000.0);
      SimUtil.CheckValidValue(max_temp_joules);
      float min_temp = Math.Min(source_temp, dest_temp);
      float max_temp = Math.Max(source_temp, dest_temp);
      float source_new_temp = source_temp - max_temp_joules / source_heat_capacity;
      float dest_new_temp = dest_temp + max_temp_joules / dest_heat_capacity;
      SimUtil.CheckValidValue(source_new_temp);
      SimUtil.CheckValidValue(dest_new_temp);
      float num4 = Mathf.Clamp(source_new_temp, min_temp, max_temp);
      float num5 = Mathf.Clamp(dest_new_temp, min_temp, max_temp);
      float num6 = Math.Abs(num4 - source_temp);
      float num7 = Math.Abs(num5 - dest_temp);
      float num8 = Math.Min(num6 * source_heat_capacity, num7 * dest_heat_capacity) * ((double) max_temp_watts_transferred >= 0.0 ? 1f : -1f);
      SimUtil.CheckValidValue(num8);
      return num8;
    }

As you can see it calculates a change in temperature of the source and destination, using the intended transfer of energy and the specific heat capacity... but it does this without accounting for the masses of the source and destination. Then for some reason it's clamping this in various ways, probably because doing it without the masses was super buggy, and clamping it made it somewhat reasonable?

Link to comment
Share on other sites

45 minutes ago, rezecib said:

but it does this without accounting for the masses of the source and destination.

Masses are irrelevant in heat transfer. Imagine you press the two materials into very thin foils and put one on the other - they'll equalize their temperatures almost instantly regardless of masses. Other factors play role here, such as area of contact and thermal conductivity in bulk of the material but both have very limited meaning in context of ONI where things don't have size or surface area.

 

Link to comment
Share on other sites

2 minutes ago, Kasuha said:

Masses are irrelevant in heat transfer. Imagine you press the two materials into very thin foils and put one on the other - they'll equalize their temperatures almost instantly regardless of masses. Other factors play role here, such as area of contact and thermal conductivity in bulk of the material but both have very limited meaning in context of ONI where things don't have size or surface area.

I know, but it's calculating a delta temperature based on the specific heat without a mass. That's where the error comes in.

Edit: this is the part that's really wrong:

      float source_new_temp = source_temp - max_temp_joules / source_heat_capacity;
      float dest_new_temp = dest_temp + max_temp_joules / dest_heat_capacity;

You can't just divide energy by heat capacity and add it to temperature. The units don't match up.

Link to comment
Share on other sites

The numbers don't add up on the theory side right now. I've just been testing by painting a block of gaseous hydrogen onto empty pipes. The equilibrium temperature is all messed up and doesn't follow the physical  laws. Something is seriously wrong with gas dynamics.

I've designed some situations where liquids work equilibrating to the temperature to within 0.5°C and verifiable by theoretical calculations. I also found that the game does simulate surface area calculations. On a block with 1 vs 2 sides exposed. 

However, I cannot get the same situation for gases.

Link to comment
Share on other sites

23 minutes ago, Kasuha said:

Masses are irrelevant in heat transfer. Imagine you press the two materials into very thin foils and put one on the other - they'll equalize their temperatures almost instantly regardless of masses. Other factors play role here, such as area of contact and thermal conductivity in bulk of the material but both have very limited meaning in context of ONI where things don't have size or surface area.

 

Mass is relevant, in that heat capacity = mass * specific heat.  If you put the materials in thin foils, they'll equalize their temperatures regardless of mass, but the equilibrium temp they reach will depend upon the masses and initial temperatures and specific heats of each material; and the failure of the code to account for this is the bug that rezecib has found.

Link to comment
Share on other sites

36 minutes ago, rezecib said:

I know, but it's calculating a delta temperature based on the specific heat without a mass. That's where the error comes in.

Hmmm let's think how I would do it. Let's assume we have two objects with masses m1 and m2 (g), heat capacity c1 and c2 (j/g/K), and at temperatures t1 and t2 (K).

First step, we need to calculate their thermal equilibrium temperature t=(m1*c1*t1+m2*c2*t2)/(m1*c1+m2*c2)

From that comes heat transfer qe=(t1-t)*c1*m1=(t-t2)*c2*m2

For purposes of the simulation, that heat is not transferred immediately, only certain percentage of it is transferred. qa=qe/p. That among others takes care of the equalization going faster with greater temperature difference.

Then this heat transfer is applied to acquire new temperatures:

t1'=t1-qa/(m1*c1)

t2'=t2+qa/(m2*c2)

(not sure if signs are correct)

No clamping is needed for such calculation. It cannot fail and cannot ever have a runaway effect.

The clamping in the code is obviously because heat transfer is not calculated as percentage of change towards equilibrium but based on temperature difference and material properties with potential that a small mass can get too much heat in single simulation step to go below absolute zero or jump to millions degrees. Which means yes it gets clamped, but the other mass gets a giant kick in the other direction and it is only clamped by the temperature of the other object.

If nothing else, the clamping should be done towards equilibrium temperature, not the temperature of the other object.

 

Edit: Note: the "p" coefficient is a tricky part. It depends on things like thermal conductivity or the context of the material of each object. I'm intentionally omitting that part but for runtime purposes, it can be generated through four-dimensional precomputed table indexed by material and context of each of the two objects. By context I mean whether it is ambient, a building, or a material contained within a building etc.

 

Link to comment
Share on other sites

There's an analogous function above it that takes into account mass, but for some reason this isn't used for pipes.

    public static float ClampEnergyTransfer(float dt, float source_temp, float source_mass, float source_specific_heat_capacity, float dest_temp, float dest_mass, float dest_specific_heat_capacity, float max_watts_transferred)
    {
      return SimUtil.ClampEnergyTransfer(dt, source_temp, source_mass * source_specific_heat_capacity, dest_temp, dest_mass * dest_specific_heat_capacity, max_watts_transferred);
    }

It's essentially calling the bugged function but passing in mass*capacity in place of capacity.

K - (J / (J / g K)(g)) checks out, results in just K units being added.

Link to comment
Share on other sites

if ((double) num < (double) data1.lowStateTransitionTemperature)

data2.primaryElement.Trigger(-700727624, (object) null);

else if ((double) num > (double) data1.highStateTransitionTemperature)

data2.primaryElement.Trigger(-1152799878, (object) null);

 

This makes me wonder if data1 and data2 represent different objects

Link to comment
Share on other sites

Here is my data for a liquid-solid equilibrium calculation,

Example

  1. Setup (paused status)
    1. Isolating two 3 tiles wide boxes (height irrelevant due to vacuum) with abyssalite, G = granite, W = water 
    2. System one: | G W W | (3 substance system - simplified to 2 in calculations)
    3. System two: | W G W |
  2. Parameters
    1. mass of granite = 20 kg, specific heat = 0.79 J/g*K, initial temperature = 200 K
    2. mass of water tile (each) = 50 kg, specific heat = 4.179 J/g*K, initial temperature = 363 K
  3. Once set up, just run simulation and allow system to reach equilibrium
  4. Bonus content: See that surface exposure (1 side vs 2) affects equilibrium speed (logically correct since surface area plays a huge role in heat transfer)
  5. Verifying calculations with actual in-game observation - the theoretical equilibrium temperatures for this setup is 84.0 °C or 357 K (use KELVIN when calculating q)
  6. ***If simplifying calculations - the mass of water, m2, is 2x50 kg or 100 kg

Observations

  1. When I try to apply it to gas-solid system, it does not compute and the temperature results are laughable. Devs pls review.

Notes Attached

  1. Scientific Theory & Mathematical formulaeFormulae.png
  2. Set up Visual & Parameterseq ex.jpg

 

Link to comment
Share on other sites

I believe data1 is the contents and data2 is the pipe. There's some code above that assigns contents.temperature to data1, and then data2 is acquired from the conduit_structure_temperature_handle, which sounds like it's referring to the structure.

I believe the object is organized to handle this temperature transfer by tracking the packets of gas and then only calculating exchange with the gas packets and the pipe segment they're in, as opposed to keeping track of the pipe segments and then checking for gas to transfer with.

Link to comment
Share on other sites

Okay, so I took a dive into the formula after all.

Quote

float max_temp_joules = (float) ((double) max_temp_watts_transferred * (double) dt / 1000.0);

This should be the single and only thing that needs to be clamped towards equilibrium temperature. Assuming the "heat capacity" is a multiple of specific heat capacity and mass of the object, the rest of the formulas is fine but this single thing may grow beyond all limits. All the clamping and checks that come after are there just to mitigate damage that already happened.

Link to comment
Share on other sites

Okay, so what I think should be there (assuming again that "xxxx_heat_capacity" is a product of mass and specific heat capacity):

    public static float ClampEnergyTransfer(float dt, float source_temp, float source_heat_capacity, float dest_temp, float dest_heat_capacity, float max_temp_watts_transferred)
    {
        float max_temp_joules = (float) ((double) max_temp_watts_transferred * (double) dt / 1000.0);
        float max_xfer_joules = source_heat_capacity * dest_heat_capacity * (source_temp - dest_temp) / (source_heat_capacity + dest_heat_capacity);
        if (Mathf.Abs(max_temp_joules) > Mathf.Abs(max_xfer_joules))
            return max_xfer_joules;
        else
            return max_temp_joules;
    }

Also, I'm not sure what exactly does the SimUtil.CheckValidValue function does so I skipped it although it might need to be used too.

Link to comment
Share on other sites

 

On 10 Июнь 2017 г. at 9:42 PM, rezecib said:

Edit: this is the part that's really wrong:


      float source_new_temp = source_temp - max_temp_joules / source_heat_capacity;
      float dest_new_temp = dest_temp + max_temp_joules / dest_heat_capacity;

You can't just divide energy by heat capacity and add it to temperature. The units don't match up.

Yeah, it should be float source_new_temp = source_temp - max_temp_joules / (source_heat_capacity * source_mass);

Unless the calculations are being made for a single unit of mass. OR the mass is accounted for somewhere else in the code
A-ha.

On 10 Июнь 2017 г. at 10:21 PM, rezecib said:

There's an analogous function above it that takes into account mass, but for some reason this isn't used for pipes.

The reason could be the mass of each pipe segment is always the same. Maybe it assumes the mass of packets is a constant, too. Left over from some previous version when packets could only be whole? Or for optimization reasons.

On 10 Июнь 2017 г. at 10:52 PM, rezecib said:

I believe data1 is the contents and data2 is the pipe. There's some code above that assigns contents.temperature to data1, and then data2 is acquired from the conduit_structure_temperature_handle, which sounds like it's referring to the structure.

Not only that, but there is also data1.lowStateTransitionTemperature; If it can change states, it must be the contents. :)
Is this the code that breaks pipes on state transitions?

data2.primaryElement.Trigger(-700727624, (object) null);

 

 

Link to comment
Share on other sites

Hi folks,

I was looking at the original question and I with the recent updates I found that my base was overheating and I came up with a solution that may work for mid-game use until something way cooler is built to take over.. I have only 181 hours on this alpha and I have done none of the math or research, so my solution is all hack and slash. 

The issue for me was my intake of hot water from the geyser was running thru outer walls and even with insulated tiles for that, I found my waste water storage tank and the showers/toilets themselves were contributing a lot of heat into the colony.

My solution was simple, I used insulated pipes to the nearest cold biome and then carved out a nice tank there to store the waste water, then when there was enough water and it was now down to 25F i pumped in cheap non insulated pipes around to all the zones including the waste water storage area.

The result was significant drop in temperature and since i was working with polluted water already I connected and mixed the polluted water going to the local storage and this further dropped the temp to about 20F without freezing out my poor colony.. I know my solution lacks the math and wiz kid approach but I am trying to keep my game as recreational as possible. Also the polluted water after it is cycled is sent to the fertilizer farm machines to cool and become food stuffs supply.

Capture.PNG

Capture1.PNG

Link to comment
Share on other sites

1 hour ago, ImperialWolf said:

The issue for me was my intake of hot water from the geyser was running thru outer walls and even with insulated tiles for that, I found my waste water storage tank and the showers/toilets themselves were contributing a lot of heat into the colony.

The "easy" solution to this problem is to seal the geyser in with a gold amalgam pump, which can stand the heat, and then pump it out with abyssalite pipes directly to the things that need it. This way the water just keeps the heat and it doesn't transfer, and then the things that use the water will effectively "delete" the heat by consuming the water.

Link to comment
Share on other sites

Rezecib - I do seem to have a differing experience with the pump and the insulated pipes. The pump is made of gold amalgam, and the insulated pipes still transfer to the tiles and area they go thru. Lastly the showers and the toilets when using the hot water do heat up the room or area. I have attached the screen shot from the geyser location. I am open to learning a different way of doing this. 

 

Capture2.PNG

Link to comment
Share on other sites

On 6/10/2017 at 0:17 PM, Kasuha said:

No clamping is needed for such calculation. It cannot fail and cannot ever have a runaway effect.

 

Ideally? Yes. In practice? Floats are annoying. There are ways around that, but even 'this should work' calculations tend to drift over time.

(One classic method, assuming your optimizer allows it. You have a secondary global accumulator variable, that gets rolled into all calculations and gets incremented by the delta between the previous and current total temperatures.)

But yes, it's much better than the current method.

It'd be interesting to enforce global conservation of energy...

Link to comment
Share on other sites

3 minutes ago, TLW said:

Ideally? Yes. In practice? Floats are annoying. There are ways around that, but even 'this should work' calculations tend to drift over time.

The formula in that post is stable. I know very well how annoying floats are but given the constraints the game already comes with, it's not going to cause any problems.

Link to comment
Share on other sites

On 6/10/2017 at 0:17 PM, Kasuha said:

t=(m1*c1*t1+m2*c2*t2)/(m1*c1+m2*c2)

qe=(t1-t)*c1*m1=(t-t2)*c2*m2

qa=qe/p.

t1'=t1-qa/(m1*c1)

t2'=t2+qa/(m2*c2)

 

6 hours ago, Kasuha said:

The formula in that post is stable.

Stable as in "won't run away toward +- infinity on its own"? Kind of, although classically you require a step size proportional to the inverse square (!) of the grid size, among other things, to be stable in the numerical methods sense.

Stable as in "(approximately) following the usual laws of thermodynamics"? Nope.

Consider what happens when m1*c1 >>>> m2*c2... you can get cases where the division underflows to 0 for one but not both sides - and all of a sudden you've gained / lost heat overall. That formula doesn't work well in general when the masses / heat capacities are sufficiently different.

You can also get spurious oscillations with thermal gradients, etc, etc.

Relevant: 1280px-HeatEquationExplicitApproximate.s

Link to comment
Share on other sites

4 hours ago, TLW said:

Consider what happens when m1*c1 >>>> m2*c2...

Nothing happens.

qe=(t1-t)*c1*m1=(t-t2)*c2*m2

qa=qe/p.

t1'=t1-qa/(m1*c1)

Ends up as t1'= t1-(t1-t)*p if you do it right, and by doing it right I mean removing the division completely, I only added it for clarity. Neither temperature exits the interval between intial and equilibrium temperature and the equilibrium temperature is always inside the t1, t2.

Edit: okay, it's a while since I posted that so let me correct myself a bit. What I meant as stable formula is the first one, the one that calculates equilibrium temperature. That's not going anywhere regardless what values you feed into it. And that's the important part since it establishes the value towards which the heat exchange converges. The last two formulas may get unstable if you use them as they are written but they can be easily adapted to be stable as well as i show above. The whole calculation can easily be done without any clamping and boundary tests.

Link to comment
Share on other sites

On 9/3/2017 at 9:46 PM, Kasuha said:

The last two formulas may get unstable if you use them as they are written but they can be easily adapted to be stable as well as i show above.

Please show the adapted stable formula.

I don't know of any explicit methods of integrating heat diffusion that are unconditionally stable, only implicit ones (and unless you're solving a system of equations per-tile...).

Link to comment
Share on other sites

1 hour ago, TLW said:

Please show the adapted stable formula.

t' = t + (T - t)*p where t is initial temperature, t' is final temperature, T is equilibrium temperature, and p is quotient expressing how much of the exchange happened in single step, between 0 and 1.

Do you still have any problems with my old, outdated, and largely irrelevant post?

 

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

Please be aware that the content of this thread may be outdated and no longer applicable.

×
  • Create New...