From faa9ead59488f956ab2471f9ef190c968a223a19 Mon Sep 17 00:00:00 2001 From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Date: Tue, 2 Jun 2026 12:52:50 -0700 Subject: [PATCH] Migrate Atmospherics to TensorPrimitives (#44139) --- Content.Benchmarks/DeltaPressureBenchmark.cs | 2 +- .../EntitySystems/AtmosphereSystem.Gases.cs | 19 +++++------ .../EntitySystems/AtmosphereSystem.API.cs | 8 ++--- .../AtmosphereSystem.DeltaPressure.cs | 7 ++-- .../EntitySystems/AtmosphereSystem.Gases.cs | 32 ++++++++----------- .../SharedAtmosphereSystem.Gases.cs | 5 +-- Content.Shared/Atmos/GasMixture.cs | 9 +++--- Content.Shared/Content.Shared.csproj | 1 + .../Server/Atmos/AddMolsToMixtureTest.cs | 4 +-- 9 files changed, 43 insertions(+), 44 deletions(-) diff --git a/Content.Benchmarks/DeltaPressureBenchmark.cs b/Content.Benchmarks/DeltaPressureBenchmark.cs index dac79e03760..f4ae62a4652 100644 --- a/Content.Benchmarks/DeltaPressureBenchmark.cs +++ b/Content.Benchmarks/DeltaPressureBenchmark.cs @@ -32,7 +32,7 @@ public class DeltaPressureBenchmark /// /// Number of entities (windows, really) to spawn with a . /// - [Params(1, 10, 100, 1000, 5000, 10000, 50000, 100000)] + [Params(100, 1000, 5000, 10000)] public int EntityCount; /// diff --git a/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index 24f3ffaa261..0619e26c4b2 100644 --- a/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -1,3 +1,4 @@ +using System.Numerics.Tensors; using System.Runtime.CompilerServices; using Content.Shared.Atmos; using Content.Shared.Atmos.Reactions; @@ -26,15 +27,15 @@ public sealed partial class AtmosphereSystem public override bool IsMixtureFuel(GasMixture mixture, float epsilon = Atmospherics.Epsilon) { var tmp = new float[Atmospherics.AdjustedNumberOfGases]; - NumericsHelpers.Multiply(mixture.Moles, GasFuelMask, tmp); - return NumericsHelpers.HorizontalAdd(tmp) > epsilon; + TensorPrimitives.Multiply(mixture.Moles, GasFuelMask, tmp); + return TensorPrimitives.Sum(tmp) > epsilon; } public override bool IsMixtureOxidizer(GasMixture mixture, float epsilon = Atmospherics.Epsilon) { var tmp = new float[Atmospherics.AdjustedNumberOfGases]; - NumericsHelpers.Multiply(mixture.Moles, GasOxidizerMask, tmp); - return NumericsHelpers.HorizontalAdd(tmp) > epsilon; + TensorPrimitives.Multiply(mixture.Moles, GasOxidizerMask, tmp); + return TensorPrimitives.Sum(tmp) > epsilon; } public override float GetMass(GasMixture mix) @@ -45,17 +46,17 @@ public sealed partial class AtmosphereSystem public override float GetMass(float[] moles) { var tmp = new float[moles.Length]; - NumericsHelpers.Multiply(moles, GasMolarMasses, tmp); + TensorPrimitives.Multiply(moles, GasMolarMasses, tmp); // Conversion of grams to kilograms. - return NumericsHelpers.HorizontalAdd(tmp) * Atmospherics.gToKg; + return TensorPrimitives.Sum(tmp) * Atmospherics.gToKg; } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override float GetHeatCapacityCalculation(float[] moles, bool space) { // Little hack to make space gas mixtures have heat capacity, therefore allowing them to cool down rooms. - if (space && MathHelper.CloseTo(NumericsHelpers.HorizontalAdd(moles), 0f)) + if (space && MathHelper.CloseTo(TensorPrimitives.Sum(moles), 0f)) { return Atmospherics.SpaceHeatCapacity; } @@ -65,9 +66,9 @@ public sealed partial class AtmosphereSystem // though this isnt the hottest code path so it should be fine // the gc can eat a little as a treat var tmp = new float[moles.Length]; - NumericsHelpers.Multiply(moles, GasMolarHeatCapacities, tmp); + TensorPrimitives.Multiply(moles, GasMolarHeatCapacities, tmp); // Adjust heat capacity by speedup, because this is primarily what // determines how quickly gases heat up/cool. - return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity); + return MathF.Max(TensorPrimitives.Sum(tmp), Atmospherics.MinimumHeatCapacity); } } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs index 27e9f33653a..50c3add7486 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs @@ -1,5 +1,6 @@ using System.Buffers; using System.Diagnostics; +using System.Numerics.Tensors; using Content.Server.NodeContainer.NodeGroups; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; @@ -366,10 +367,9 @@ public partial class AtmosphereSystem mixtMoles[i] = mixture.TotalMoles; } - // TODO NumericsHelpers need a method that substitutes NaNs with zeros. AVX-512 has one iirc but for 256/128 we need to do some masking bs - NumericsHelpers.Multiply(mixtMoles, Atmospherics.R); - NumericsHelpers.Multiply(mixtMoles, mixtTemp); - NumericsHelpers.Divide(mixtMoles, mixtVol, pressures); + TensorPrimitives.Multiply(mixtMoles, Atmospherics.R, mixtMoles); + TensorPrimitives.Multiply(mixtMoles, mixtTemp, mixtMoles); + TensorPrimitives.Divide(mixtMoles, mixtVol, pressures); } finally { diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs index 81fa1c6e6f7..fe11a62f14a 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs @@ -1,5 +1,6 @@ using System.Buffers; using System.Collections.Concurrent; +using System.Numerics.Tensors; using Content.Server.Atmos.Components; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; @@ -162,9 +163,9 @@ public sealed partial class AtmosphereSystem } // Time to get crankin - NumericsHelpers.Max(groupA, groupB, groupMax); - NumericsHelpers.Sub(groupA, groupB); - NumericsHelpers.Abs(groupA); + TensorPrimitives.Max(groupA, groupB, groupMax); + TensorPrimitives.Subtract(groupA, groupB, groupA); + TensorPrimitives.Abs(groupA, groupA); // Now go through each entity and determine their max pressure & delta pressure. // Queue for damage if necessary. diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index d3c51086ca1..9a4af9ebf0e 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Numerics.Tensors; using System.Runtime.CompilerServices; using Content.Server.Atmos.Reactions; using Content.Shared.Atmos; @@ -36,40 +37,40 @@ namespace Content.Server.Atmos.EntitySystems public override float GetMass(float[] moles) { Span tmp = stackalloc float[moles.Length]; - NumericsHelpers.Multiply(moles, GasMolarMasses, tmp); + TensorPrimitives.Multiply(moles, GasMolarMasses, tmp); // Conversion of grams to kilograms. - return NumericsHelpers.HorizontalAdd(tmp) * Atmospherics.gToKg; + return TensorPrimitives.Sum(tmp) * Atmospherics.gToKg; } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override float GetHeatCapacityCalculation(float[] moles, bool space) { // Little hack to make space gas mixtures have heat capacity, therefore allowing them to cool down rooms. - if (space && MathHelper.CloseTo(NumericsHelpers.HorizontalAdd(moles), 0f)) + if (space && MathHelper.CloseTo(TensorPrimitives.Sum(moles), 0f)) { return Atmospherics.SpaceHeatCapacity; } Span tmp = stackalloc float[moles.Length]; - NumericsHelpers.Multiply(moles, GasMolarHeatCapacities, tmp); + TensorPrimitives.Multiply(moles, GasMolarHeatCapacities, tmp); // Adjust heat capacity by speedup, because this is primarily what // determines how quickly gases heat up/cool. - return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity); + return MathF.Max(TensorPrimitives.Sum(tmp), Atmospherics.MinimumHeatCapacity); } public override bool IsMixtureFuel(GasMixture mixture, float epsilon = Atmospherics.Epsilon) { Span tmp = stackalloc float[Atmospherics.AdjustedNumberOfGases]; - NumericsHelpers.Multiply(mixture.Moles, GasFuelMask, tmp); - return NumericsHelpers.HorizontalAdd(tmp) > epsilon; + TensorPrimitives.Multiply(mixture.Moles, GasFuelMask, tmp); + return TensorPrimitives.Sum(tmp) > epsilon; } public override bool IsMixtureOxidizer(GasMixture mixture, float epsilon = Atmospherics.Epsilon) { Span tmp = stackalloc float[Atmospherics.AdjustedNumberOfGases]; - NumericsHelpers.Multiply(mixture.Moles, GasOxidizerMask, tmp); - return NumericsHelpers.HorizontalAdd(tmp) > epsilon; + TensorPrimitives.Multiply(mixture.Moles, GasOxidizerMask, tmp); + return TensorPrimitives.Sum(tmp) > epsilon; } /// @@ -131,8 +132,8 @@ namespace Content.Server.Atmos.EntitySystems } // transfer moles - NumericsHelpers.Multiply(source.Moles, fraction, buffer); - NumericsHelpers.Add(receiver.Moles, buffer); + TensorPrimitives.Multiply(source.Moles, fraction, buffer); + TensorPrimitives.Add(receiver.Moles, buffer, receiver.Moles); } } @@ -325,13 +326,8 @@ namespace Content.Server.Atmos.EntitySystems [PublicAPI] public static void AddMolsToMixture(GasMixture mixture, ReadOnlySpan molsToAdd) { - // Span length should be as long as the length of the gas array. - // Technically this is a redundant check because NumericsHelpers will do the same thing, - // but eh. - ArgumentOutOfRangeException.ThrowIfNotEqual(mixture.Moles.Length, molsToAdd.Length, nameof(mixture.Moles.Length)); - - NumericsHelpers.Add(mixture.Moles, molsToAdd); - NumericsHelpers.Max(mixture.Moles, 0f); + TensorPrimitives.Add(mixture.Moles, molsToAdd, mixture.Moles); + TensorPrimitives.Max(mixture.Moles, 0f, mixture.Moles); } public enum GasCompareResult diff --git a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs index 240711f048a..7a2c8339d7f 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs @@ -1,3 +1,4 @@ +using System.Numerics.Tensors; using System.Runtime.CompilerServices; using Content.Shared.Atmos.Prototypes; using Content.Shared.Atmos.Reactions; @@ -110,7 +111,7 @@ public abstract partial class SharedAtmosphereSystem [PublicAPI] public void GetFlammableMoles(GasMixture mixture, float[] buffer) { - NumericsHelpers.Multiply(mixture.Moles, GasOxidiserFuelMask, buffer); + TensorPrimitives.Multiply(mixture.Moles, GasOxidiserFuelMask, buffer); } /// @@ -213,7 +214,7 @@ public abstract partial class SharedAtmosphereSystem } } - NumericsHelpers.Add(receiver.Moles, giver.Moles); + TensorPrimitives.Add(receiver.Moles, giver.Moles, receiver.Moles); } /// diff --git a/Content.Shared/Atmos/GasMixture.cs b/Content.Shared/Atmos/GasMixture.cs index 3da7827cdd8..a5fc04e611b 100644 --- a/Content.Shared/Atmos/GasMixture.cs +++ b/Content.Shared/Atmos/GasMixture.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Numerics.Tensors; using System.Runtime.CompilerServices; using Content.Shared.Atmos.EntitySystems; using Content.Shared.Atmos.Reactions; @@ -42,7 +43,7 @@ namespace Content.Shared.Atmos public float TotalMoles { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => NumericsHelpers.HorizontalAdd(Moles); + get => TensorPrimitives.Sum(Moles); } [ViewVariables] @@ -177,9 +178,9 @@ namespace Content.Shared.Atmos var removed = new GasMixture(Volume) { Temperature = Temperature }; Moles.CopyTo(removed.Moles.AsSpan()); - NumericsHelpers.Multiply(removed.Moles, ratio); + TensorPrimitives.Multiply(removed.Moles, ratio, removed.Moles); if (!Immutable) - NumericsHelpers.Sub(Moles, removed.Moles); + TensorPrimitives.Subtract(Moles, removed.Moles, Moles); for (var i = 0; i < Moles.Length; i++) { @@ -223,7 +224,7 @@ namespace Content.Shared.Atmos public void Multiply(float multiplier) { if (Immutable) return; - NumericsHelpers.Multiply(Moles, multiplier); + TensorPrimitives.Multiply(Moles, multiplier, Moles); } void ISerializationHooks.AfterDeserialization() diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj index da32cdf817d..f8f986c9c55 100644 --- a/Content.Shared/Content.Shared.csproj +++ b/Content.Shared/Content.Shared.csproj @@ -6,6 +6,7 @@ + diff --git a/Content.Tests/Server/Atmos/AddMolsToMixtureTest.cs b/Content.Tests/Server/Atmos/AddMolsToMixtureTest.cs index 07b45bfe11d..936a9f13d6a 100644 --- a/Content.Tests/Server/Atmos/AddMolsToMixtureTest.cs +++ b/Content.Tests/Server/Atmos/AddMolsToMixtureTest.cs @@ -21,10 +21,8 @@ public sealed class AddMolsToMixtureTest var mixture = new GasMixture(); var wrongLength = new float[Atmospherics.AdjustedNumberOfGases + num]; - var ex = Assert.Throws(() => + Assert.Throws(() => AtmosphereSystem.AddMolsToMixture(mixture, wrongLength)); - - Assert.That(ex!.ParamName, Is.EqualTo("Length")); } ///