Thanks to @Zarquan who pointed out that the bug previously reported did not suffice to explain known problems. This bug report supercedes that one. @Zarquan also points out that this is has been reported at least as far back by @mathmanican and @wachunga:
This post is intended to explain why this happens with specific code references, and provide a suggested "easy" fix (there are five different lines where it occurs, but in each case it is simply a matter of changing cells to updatedCells).
------------------------------------------------------------------------------------------------------
The Problem
The fundamental problem is that, when liquid "advection" (mass movement) occurs and the same cell participates both as mass donor and a mass recipient, heat gets deleted.
When we combine 200kg of a liquid at 100C and 100kg of a liquid at 0C, we should expect to get 300kg at 66.6C.
Here is a video from the previous bug report providing an easy case where this doesn't happen:
As seen, the situation on the right (where liquid flows from R->L) does settle to 66.6C. But the situation on the left (liquids flows from L->R) does not: heat has been deleted.
The previous bug report attributed this to "liquid falling" -- and that is true -- but @Zarquan observed that there are other instances without liquid falling.
This is exactly the same phenomenon as the previous situation but without any "falling" occurring. The liquids on the right average close to 66.6 but on the left, each individual cell is well under 66.6.
------------------------------------------------------------------------------------------------------
How this happens
(This is very code-specific, apologies.)
In the code during UpdateData(), there are two arrays kept: cells and updatedCells. We tend to think of these as pre-tick and post-tick, but that isn't quite accurate, because there are multiple rounds of computation:
1. Heat conduction occurs (cell-cell conduction). I think this is a good model for how things should work:
1a) cells is used to determine how much heat to move between a pair of cells.
1b) but that heat is applied to updatedCells.
The result: suppose a cell is first a heat recipient. So its updatedCells value is changed. Now, it in turn becomes a heat donor. The amount of heat it transfers is computed using the cells value (which is fine / good), but moreover, its updatedCells is modified in-place: updatedCells[ cell ] -= q / cellCapacity and updatedCells[ ncell ] += q / ncellCapacity. Heat is preserved, and everything in the garden is lovely.
2. CopyFrom() is called. This resets cells = updatedCells.
3. Gas diffusion occurs (in two varieties: ordinary gas diffusion, and gas overpressure). This again uses cells to compute updatedCells, but it's no longer quite right to call them "pre-tick" and "post-tick": they have both experienced heat conduction. Better to say pre-diffusion and post-diffusion.
4. CopyFrom() is called again. Once again cells = updatedCells.
5. UpdateLiquid() is called in a loop (B->T, L->R) to perform liquid advection (mass movement). This is where all five bugs occur. Again we use cells and updatedCells, but it's more precise to call them pre-advection and post-advection than "pre-tick" and "post-tick".
5a) As with temperature conduction, cells is used to determine how much mass to move. This is fine.
5b) However, unlike temperature conduction, cells is also used to determine what temperature is "used to push" that mass, and the result is stored (as before) in updatedCells.
So, now: suppose a cell is first a recipient of advection. It pre-advection (cells) temperature now differs from its post-advection (updatedCells) temperature. Fine. But now, it is called and it itself becomes a donor to an adjacent cell. It decides using pre-advection data how much mass to move, fine. But it does two things:
- It subtracts that mass from itself -- which, effectively, at this moment in time is at the temperature in updatedCells (post-advection).
- It "merges" that same mass into the neighboring cell using the temperature from cells (pre-advection).
The net of these two: we delete liquid mass at one temperature and recreate it elsewhere using a different temperature. This is the problem. The suggested (easy) solution: simply use the value from updatedCells instead.
The processing loop continues:
6. CopyFrom() is called
7. Diseases are computed... CopyFrom() is called again... post-cell processing (like evaporation, flaking, etc) is performed... etc.
------------------------------------------------------------------------------------------------------
Specific instances
The rule of thumb should be: whenever we push mass, we should be pushing it at the same temperature as we're about to delete, which is our present post-advection updatedCells value.
I've seen five places where this occurs, all within the scope of UpdateLiquid(). There are two main ways that liquid "moves": either it creates vapor by calling SpawnFallingLiquid() (this occurs when falling off a ledge, etc) or it pushes in immediately using UpdateNeighbourLiquidMass().
1. In UpdateLiquid(): liquid tries to push D, L, R, then U. Each of D, L, R may result in "falling" (liquid never "falls up"), meaning a call to SpawnFallingLiquid. Each of those three calls is made using cells (pre-advection temperature). Suggestion: change this to updatedCells. This was the previous bug report.
2. If it doesn't fall, then it calls UpdateNeighbourLiquidMass().
2a) If the neighbor is the same liquid, then it simply calls AddMassAndUpdateTemperature, but it does this again using cells. Suggestion: use updatedCells.
2b) Otherwise, it tries to DisplaceGas() and -- if that succeeds -- it again invokes AddMassAndUpdateTemperature on the empty cell, but again, using cells.
In short:
- In UpdateLiquid() there are three calls to SpawnFallingLiquid, each using pre-advection temperature.
- In UpdateNeighbourLiquidMass() there are two calls to AddMassAndUpdateTemperature, each using pre-advection temperature.
EDIT: stealth-adding a sixth instance of the bug (see comment below)
- In DoLiquidPressureDisplacement(), if it succeeds in pushing the neighboring liquid out of the way, then it copies the source cell into the neighboring cell using cells. Again, this should be updatedCells.
------------------------------------------------------------------------------------------------------
The suggested fix
Simply change those five six calls from cells to updatedCells. Often those values are the same, anyway -- but in any circumstance where they differ, we are going to delete our own mass at updatedCells, so that is the proper temperature to spawn new mass in the adjacent cell.
I cannot think of any possible scenario where this would break anything. It would be a pretty bizarre build that depended on processing order and heat deletion.
I'm sorry for the extreme verbosity, but hopeful that we can get this longstanding issue resolved.
See videos above.
-
1
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now