public static void UpdateRampPositions(float frameTime, PowerState state)
        {
            // Update supplies to move their ramp position towards target, if necessary.
            foreach (var supply in state.Supplies.Values)
            {
                if (supply.Paused)
                {
                    continue;
                }

                if (!supply.Enabled)
                {
                    // If disabled, set ramp to 0.
                    supply.SupplyRampPosition = 0;
                    continue;
                }

                var rampDev = supply.SupplyRampTarget - supply.SupplyRampPosition;
                if (Math.Abs(rampDev) > 0.001f)
                {
                    float newPos;
                    if (rampDev > 0)
                    {
                        // Position below target, go up.
                        newPos = Math.Min(
                            supply.SupplyRampTarget,
                            supply.SupplyRampPosition + supply.SupplyRampRate * frameTime);
                    }
                    else
                    {
                        // Other way around, go down
                        newPos = Math.Max(
                            supply.SupplyRampTarget,
                            supply.SupplyRampPosition - supply.SupplyRampRate * frameTime);
                    }

                    supply.SupplyRampPosition = Math.Clamp(newPos, 0, supply.MaxSupply);
                }
                else
                {
                    supply.SupplyRampPosition = supply.SupplyRampTarget;
                }
            }

            // Batteries too.
            foreach (var battery in state.Batteries.Values)
            {
                if (battery.Paused)
                {
                    continue;
                }

                if (!battery.Enabled)
                {
                    // If disabled, set ramp to 0.
                    battery.SupplyRampPosition = 0;
                    continue;
                }

                var rampDev = battery.SupplyRampTarget - battery.SupplyRampPosition;
                if (Math.Abs(rampDev) > 0.001f)
                {
                    float newPos;
                    if (rampDev > 0)
                    {
                        // Position below target, go up.
                        newPos = Math.Min(
                            battery.SupplyRampTarget,
                            battery.SupplyRampPosition + battery.SupplyRampRate * frameTime);
                    }
                    else
                    {
                        // Other way around, go down
                        newPos = Math.Max(
                            battery.SupplyRampTarget,
                            battery.SupplyRampPosition - battery.SupplyRampRate * frameTime);
                    }

                    battery.SupplyRampPosition = Math.Clamp(newPos, 0, battery.MaxSupply);
                }
                else
                {
                    battery.SupplyRampPosition = battery.SupplyRampTarget;
                }
            }
        }
Exemple #2
0
        public void Tick(float frameTime, PowerState state)
        {
            foreach (var load in state.Loads.Values)
            {
                load.ReceivingPower = 0;
            }

            foreach (var supply in state.Supplies.Values)
            {
                supply.CurrentSupply = 0;
            }

            foreach (var network in state.Networks.Values)
            {
                // Clear some stuff.
                network.LocalDemandMet = 0;

                // Add up demands in network.
                network.LocalDemandTotal = network.Loads
                                           .Select(l => state.Loads[l])
                                           .Where(c => c.Enabled)
                                           .Sum(c => c.DesiredPower);

                // Add up supplies in network.
                var availableSupplySum = 0f;
                var maxSupplySum       = 0f;
                foreach (var supplyId in network.Supplies)
                {
                    var supply = state.Supplies[supplyId];
                    if (!supply.Enabled)
                    {
                        continue;
                    }

                    var rampMax         = supply.SupplyRampPosition + supply.SupplyRampTolerance;
                    var effectiveSupply = Math.Min(rampMax, supply.MaxSupply);
                    supply.EffectiveMaxSupply = effectiveSupply;
                    availableSupplySum       += effectiveSupply;
                    maxSupplySum += supply.MaxSupply;
                }

                network.AvailableSupplyTotal   = availableSupplySum;
                network.TheoreticalSupplyTotal = maxSupplySum;
            }

            // Sort networks by tree height so that suppliers that have less possible loads go FIRST.
            // Idea being that a backup generator on a small subnet should do more work
            // so that a larger generator that covers more networks can put its power elsewhere.
            var sortedByHeight = state.Networks.Values.OrderBy(v => TotalSubLoadCount(state, v)).ToArray();

            // Go over every network with supply to send power.
            foreach (var network in sortedByHeight)
            {
                // Find all loads recursively, and sum them up.
                var subNets     = new List <Network>();
                var totalDemand = 0f;
                GetLoadingNetworksRecursively(state, network, subNets, ref totalDemand);

                if (totalDemand == 0)
                {
                    continue;
                }

                // Calculate power delivered.
                var power = Math.Min(totalDemand, network.AvailableSupplyTotal);

                // Distribute load across supplies in network.
                foreach (var supplyId in network.Supplies)
                {
                    var supply = state.Supplies[supplyId];
                    if (!supply.Enabled)
                    {
                        continue;
                    }

                    if (supply.EffectiveMaxSupply != 0)
                    {
                        var ratio = supply.EffectiveMaxSupply / network.AvailableSupplyTotal;

                        supply.CurrentSupply = ratio * power;
                    }
                    else
                    {
                        supply.CurrentSupply = 0;
                    }

                    if (supply.MaxSupply != 0)
                    {
                        var ratio = supply.MaxSupply / network.TheoreticalSupplyTotal;

                        supply.SupplyRampTarget = ratio * totalDemand;
                    }
                    else
                    {
                        supply.SupplyRampTarget = 0;
                    }
                }

                // Distribute supply across subnet loads.
                foreach (var subNet in subNets)
                {
                    var rem   = subNet.RemainingDemand;
                    var ratio = rem / totalDemand;

                    subNet.LocalDemandMet += ratio * power;
                }
            }

            // Distribute power across loads in networks.
            foreach (var network in state.Networks.Values)
            {
                if (network.LocalDemandMet == 0)
                {
                    continue;
                }

                foreach (var loadId in network.Loads)
                {
                    var load = state.Loads[loadId];
                    if (!load.Enabled)
                    {
                        continue;
                    }

                    var ratio = load.DesiredPower / network.LocalDemandTotal;
                    load.ReceivingPower = ratio * network.LocalDemandMet;
                }
            }

            PowerSolverShared.UpdateRampPositions(frameTime, state);
        }