Fix pipe net conservation laws (#8540)
This commit is contained in:
@@ -108,6 +108,52 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
NumericsHelpers.Add(receiver.Moles, giver.Moles);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides a source gas mixture into several recipient mixtures, scaled by their relative volumes. Does not
|
||||
/// modify the source gas mixture. Used for pipe network splitting. Note that the total destination volume
|
||||
/// may be larger or smaller than the source mixture.
|
||||
/// </summary>
|
||||
public void DivideInto(GasMixture source, List<GasMixture> receivers)
|
||||
{
|
||||
var totalVolume = 0f;
|
||||
foreach (var receiver in receivers)
|
||||
{
|
||||
if (!receiver.Immutable)
|
||||
totalVolume += receiver.Volume;
|
||||
}
|
||||
|
||||
float? sourceHeatCapacity = null;
|
||||
var buffer = new float[Atmospherics.AdjustedNumberOfGases];
|
||||
|
||||
foreach (var receiver in receivers)
|
||||
{
|
||||
if (receiver.Immutable)
|
||||
continue;
|
||||
|
||||
var fraction = receiver.Volume / totalVolume;
|
||||
|
||||
// Set temperature, if necessary.
|
||||
if (MathF.Abs(receiver.Temperature - source.Temperature) > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
// Often this divides a pipe net into new and completely empty pipe nets
|
||||
if (receiver.TotalMoles == 0)
|
||||
receiver.Temperature = source.Temperature;
|
||||
else
|
||||
{
|
||||
sourceHeatCapacity ??= GetHeatCapacity(source);
|
||||
var receiverHeatCapacity = GetHeatCapacity(receiver);
|
||||
var combinedHeatCapacity = receiverHeatCapacity + sourceHeatCapacity.Value * fraction;
|
||||
if (combinedHeatCapacity > 0f)
|
||||
receiver.Temperature = (GetThermalEnergy(source, sourceHeatCapacity.Value * fraction) + GetThermalEnergy(receiver, receiverHeatCapacity)) / combinedHeatCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
// transfer moles
|
||||
NumericsHelpers.Multiply(source.Moles, fraction, buffer);
|
||||
NumericsHelpers.Add(receiver.Moles, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shares gas between two gas mixtures. Part of LINDA.
|
||||
/// </summary>
|
||||
|
||||
@@ -21,8 +21,6 @@ namespace Content.Server.NodeContainer.NodeGroups
|
||||
{
|
||||
[ViewVariables] public GasMixture Air { get; set; } = new() {Temperature = Atmospherics.T20C};
|
||||
|
||||
[ViewVariables] private readonly List<PipeNode> _pipes = new();
|
||||
|
||||
[ViewVariables] private AtmosphereSystem? _atmosphereSystem;
|
||||
|
||||
public GridId Grid => GridId;
|
||||
@@ -47,7 +45,6 @@ namespace Content.Server.NodeContainer.NodeGroups
|
||||
foreach (var node in groupNodes)
|
||||
{
|
||||
var pipeNode = (PipeNode) node;
|
||||
_pipes.Add(pipeNode);
|
||||
Air.Volume += pipeNode.Volume;
|
||||
}
|
||||
}
|
||||
@@ -56,32 +53,27 @@ namespace Content.Server.NodeContainer.NodeGroups
|
||||
{
|
||||
base.RemoveNode(node);
|
||||
|
||||
var pipeNode = (PipeNode) node;
|
||||
Air.Volume -= pipeNode.Volume;
|
||||
// TODO: Bad O(n^2)
|
||||
_pipes.Remove(pipeNode);
|
||||
// if the node is simply being removed into a separate group, we do nothing, as gas redistribution will be
|
||||
// handled by AfterRemake(). But if it is being deleted, we actually want to remove the gas stored in this node.
|
||||
if (!node.Deleting || node is not PipeNode pipe)
|
||||
return;
|
||||
|
||||
Air.Multiply(1f - pipe.Volume / Air.Volume);
|
||||
Air.Volume -= pipe.Volume;
|
||||
}
|
||||
|
||||
public override void AfterRemake(IEnumerable<IGrouping<INodeGroup?, Node>> newGroups)
|
||||
{
|
||||
RemoveFromGridAtmos();
|
||||
|
||||
var buffer = new GasMixture(Air.Volume) {Temperature = Air.Temperature};
|
||||
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
|
||||
var newAir = new List<GasMixture>(newGroups.Count());
|
||||
foreach (var newGroup in newGroups)
|
||||
{
|
||||
if (newGroup.Key is not IPipeNet newPipeNet)
|
||||
continue;
|
||||
|
||||
var newAir = newPipeNet.Air;
|
||||
var newVolume = newGroup.Cast<PipeNode>().Sum(n => n.Volume);
|
||||
|
||||
buffer.Clear();
|
||||
atmosphereSystem.Merge(buffer, Air);
|
||||
buffer.Multiply(MathF.Min(newVolume / Air.Volume, 1f));
|
||||
atmosphereSystem.Merge(newAir, buffer);
|
||||
if (newGroup.Key is IPipeNet newPipeNet)
|
||||
newAir.Add(newPipeNet.Air);
|
||||
}
|
||||
|
||||
_atmosphereSystem!.DivideInto(Air, newAir);
|
||||
}
|
||||
|
||||
private void RemoveFromGridAtmos()
|
||||
|
||||
@@ -30,7 +30,9 @@ namespace Content.Server.Rotatable
|
||||
|
||||
private void AddRotateVerbs(EntityUid uid, RotatableComponent component, GetVerbsEvent<Verb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
if (!args.CanAccess
|
||||
|| !args.CanInteract
|
||||
|| Transform(uid).NoLocalRotation) // Good ol prototype inheritance, eh?
|
||||
return;
|
||||
|
||||
// Check if the object is anchored, and whether we are still allowed to rotate it.
|
||||
|
||||
Reference in New Issue
Block a user