private static Vector3d CalculateDragVector(Vessel vessel, VesselData vesselData, Vector3d orbitalVector, Orbit vesselOrbit)
        {
            var normalizedOrbitalVector        = orbitalVector.normalized;
            var cosOrbitRaw                    = Vector3d.Dot(vessel.transform.up, normalizedOrbitalVector);
            var cosOrbitalDrag                 = Math.Abs(cosOrbitRaw);
            var squaredCosOrbitalDrag          = cosOrbitalDrag * cosOrbitalDrag;
            var atmosphericGasKgPerSquareMeter = AtmosphericFloatCurves.GetAtmosphericGasDensityKgPerCubicMeter(vesselOrbit.referenceBody, vessel.altitude);

            var effectiveSpeedForDrag   = Math.Max(0, orbitalVector.magnitude);
            var dragForcePerSquareMeter = atmosphericGasKgPerSquareMeter * 0.5 * effectiveSpeedForDrag * effectiveSpeedForDrag;
            var effectiveSurfaceArea    = cosOrbitalDrag * vesselData.ModulePhotonSail.surfaceArea;

            // calculate normal
            Vector3d vesselNormal = vessel.transform.up;

            if (cosOrbitRaw < 0)
            {
                vesselNormal = -vesselNormal;
            }

            // calculate specular drag
            var      specularDragCoefficient    = squaredCosOrbitalDrag + 3 * squaredCosOrbitalDrag;
            var      specularDragPerSquareMeter = specularDragCoefficient * dragForcePerSquareMeter * 0.1;
            var      specularDragInNewton       = specularDragPerSquareMeter * effectiveSurfaceArea;
            Vector3d specularDragForce          = specularDragInNewton * vesselNormal;

            // calculate Diffuse Drag
            var      diffuseDragCoefficient    = 2 + squaredCosOrbitalDrag * 1.3;
            var      diffuseDragPerSquareMeter = diffuseDragCoefficient * dragForcePerSquareMeter * 0.9;
            var      diffuseDragInNewton       = diffuseDragPerSquareMeter * effectiveSurfaceArea;
            Vector3d diffuseDragForceVector    = diffuseDragInNewton * normalizedOrbitalVector * -1;

            Vector3d combinedDragDecelerationVector = vesselData.TotalVesselMassInKg > 0
                ? (specularDragForce + diffuseDragForceVector) / vesselData.TotalVesselMassInKg
                : Vector3d.zero;

            return(combinedDragDecelerationVector);
        }
示例#2
0
        public void ExtractAir(IResourceManager resMan, double rateMultiplier, double powerFraction, double productionModifier, bool allowOverflow)
        {
            _effectiveMaxPower = productionModifier * PowerRequirements;
            _current_power     = _effectiveMaxPower * powerFraction;
            _current_rate      = CurrentPower / EnergyPerTon;

            try
            {
                // determine the maximum amount of a resource the vessel can hold (ie. tank capacities combined)
                // determine how much spare room there is in the vessel's resource tanks (for the resources this is going to produce)
                GetResourceMass(resMan, ResourceName.IntakeAtmosphere, _atmosphere, ref _spareRoomAtmosphereMass, ref _maxCapacityAtmosphereMass);
                GetResourceMass(resMan, ResourceName.AmmoniaLqd, _ammonia, ref _spareRoomAmmoniaMass, ref _maxCapacityAmmoniaMass);
                GetResourceMass(resMan, ResourceName.ArgonLqd, _argon, ref _spareRoomArgonMass, ref _maxCapacityArgonMass);
                GetResourceMass(resMan, ResourceName.ChlorineGas, _chlorine, ref _spareRoomChlorineMass, ref _maxCapacityChlorineMass);
                GetResourceMass(resMan, ResourceName.CarbonDioxideLqd, _dioxide, ref _spareRoomDioxideMass, ref _maxCapacityDioxideMass);
                GetResourceMass(resMan, ResourceName.Helium3Lqd, _helium3, ref _spareRoomHelium3Mass, ref _maxCapacityHelium3Mass);
                GetResourceMass(resMan, ResourceName.Helium4Lqd, _helium4, ref _spareRoomHelium4Mass, ref _maxCapacityHelium4Mass);
                GetResourceMass(resMan, ResourceName.HydrogenLqd, _hydrogen, ref _spareRoomHydrogenMass, ref _maxCapacityHydrogenMass);
                GetResourceMass(resMan, ResourceName.MethaneLqd, _methane, ref _spareRoomMethaneMass, ref _maxCapacityMethaneMass);
                GetResourceMass(resMan, ResourceName.CarbonMonoxideLqd, _monoxide, ref _spareRoomMonoxideMass, ref _maxCapacityMonoxideMass);
                GetResourceMass(resMan, ResourceName.NeonLqd, _neon, ref _spareRoomNeonMass, ref _maxCapacityNeonMass);
                GetResourceMass(resMan, ResourceName.NitrogenLqd, _nitrogen, ref _spareRoomNitrogenMass, ref _maxCapacityNitrogenMass);
                GetResourceMass(resMan, ResourceName.Nitrogen15Lqd, _nitrogen15, ref _spareRoomNitrogen15Mass, ref _maxCapacityNitrogen15Mass);
                GetResourceMass(resMan, ResourceName.OxygenLqd, _oxygen, ref _spareRoomOxygenMass, ref _maxCapacityOxygenMass);
                GetResourceMass(resMan, ResourceName.WaterPure, _water, ref _spareRoomWaterMass, ref _maxCapacityWaterMass);
                GetResourceMass(resMan, ResourceName.WaterHeavy, _heavyWater, ref _spareRoomHeavyWaterMass, ref _maxCapacityHeavyWaterMass);
                GetResourceMass(resMan, ResourceName.XenonLqd, _xenon, ref _spareRoomXenonMass, ref _maxCapacityXenonMass);
                GetResourceMass(resMan, ResourceName.DeuteriumLqd, _deuterium, ref _spareRoomDeuteriumMass, ref _maxCapacityDeuteriumMass);
                GetResourceMass(resMan, ResourceName.KryptonLqd, _krypton, ref _spareRoomKryptonMass, ref _maxCapacityKryptonMass);
                GetResourceMass(resMan, ResourceName.Sodium, _sodium, ref _spareRoomSodiumMass, ref _maxCapacitySodiumMass);
            }
            catch (Exception e)
            {
                Debug.LogError("[KSPI]: ExtractAir GetResourceMass Exception: " + e.Message);
            }

            // determine the amount of resources needed for processing (i.e. intake atmosphere) that the vessel actually holds
            _availableAtmosphereMass = _maxCapacityAtmosphereMass - _spareRoomAtmosphereMass;
            if (_scoopAnimation != null)
            {
                var animationState = _scoopAnimation[animName];
                normalizedTime = animationState.normalizedTime <= 0
                    ? isDeployed ? 1 : 0
                    : animationState.normalizedTime;
            }
            else
            {
                normalizedTime = 1;
            }

            // intake can only function when heading towards orbital path
            _intakeModifier = _scoopAnimation == null ? 1 : Math.Max(0, Vector3d.Dot(part.transform.up, part.vessel.obt_velocity.normalized));

            try
            {
                // calculate build in scoop capacity
                buildInAirIntake = normalizedTime <= 0.2 ? 0 :
                                   AtmosphericFloatCurves.GetAtmosphericGasDensityKgPerCubicMeter(_vessel) * (0.1 + _vessel.obt_speed) * surfaceArea * _intakeModifier * Math.Sqrt((normalizedTime - 0.2) * 1.25);
            }
            catch (Exception e)
            {
                Debug.LogError("[KSPI]: ExtractAir GetAtmosphericGasDensityKgPerCubicMeter Exception: " + e.Message);
            }


            atmosphereConsumptionRatio = _current_rate > 0
                        ? Math.Min(_current_rate, buildInAirIntake + _availableAtmosphereMass) / _current_rate
                        : 0;

            _fixedConsumptionRate = _current_rate * atmosphereConsumptionRatio;

            // begin the intake atmosphere processing
            // check if there is anything to consume and if there is spare room for at least one of the products
            if (_fixedConsumptionRate > 0 && (
                    _spareRoomAmmoniaMass > 0 ||
                    _spareRoomArgonMass > 0 ||
                    _spareRoomChlorineMass > 0 ||
                    _spareRoomHydrogenMass > 0 ||
                    _spareRoomHelium3Mass > 0 ||
                    _spareRoomHelium4Mass > 0 ||
                    _spareRoomMonoxideMass > 0 ||
                    _spareRoomNitrogenMass > 0 ||
                    _spareRoomNitrogen15Mass > 0 ||
                    _spareRoomDioxideMass > 0 ||
                    _spareRoomMethaneMass > 0 ||
                    _spareRoomNeonMass > 0 ||
                    _spareRoomWaterMass > 0 ||
                    _spareRoomHeavyWaterMass > 0 ||
                    _spareRoomOxygenMass > 0 ||
                    _spareRoomXenonMass > 0 ||
                    _spareRoomDeuteriumMass > 0 ||
                    _spareRoomKryptonMass > 0 ||
                    _spareRoomSodiumMass > 0))
            {
                /* Now to get the actual percentages from AtmosphericResourceHandler Freethinker extended.
                 * Calls getAtmosphericResourceContent which calls getAtmosphericCompositionForBody which (if there's no definition, i.e. we're using a custom solar system
                 * with weird and fantastic new planets) in turn calls the new GenerateCompositionFromCelestialBody function Freethinker created, which creates a composition
                 * for the upper-level functions based on the planet's size and temperatures. So even though this is calling one method, it's actually going through two or three
                 *  total. Since we like CPUs and want to save them the hassle, let's close this off behind a cheap check.
                 */
                if (FlightGlobals.currentMainBody.flightGlobalsIndex != lastBodyID) // did we change a SOI since last time? If yes, get new percentages. Should work the first time as well, since lastBodyID starts as -1, while bodies in the list start at 0
                {
                    try
                    {
                        Debug.Log("[KSPI]: looking up Atmosphere contents for " + FlightGlobals.currentMainBody.name);

                        // remember, all these are persistent. Once we get them, we won't need to calculate them again until we change SOI
                        _ammoniaPercentage    = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.AmmoniaLqd);
                        _argonPercentage      = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.ArgonLqd);
                        _chlorinePercentage   = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.ChlorineGas);
                        _monoxidePercentage   = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.CarbonMonoxideLqd);
                        _dioxidePercentage    = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.CarbonDioxideLqd);
                        _helium3Percentage    = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.Helium3Lqd);
                        _helium4Percentage    = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.Helium4Lqd);
                        _hydrogenPercentage   = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.HydrogenLqd);
                        _methanePercentage    = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.MethaneLqd);
                        _neonPercentage       = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.NeonLqd);
                        _nitrogenPercentage   = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.NitrogenLqd);
                        _nitrogen15Percentage = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.Nitrogen15Lqd);
                        _oxygenPercentage     = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.OxygenLqd);
                        _waterPercentage      = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.WaterPure);
                        _heavywaterPercentage = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.WaterHeavy);
                        _xenonPercentage      = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.XenonLqd);
                        _deuteriumPercentage  = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.DeuteriumLqd);
                        _kryptonPercentage    = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.KryptonLqd);
                        _sodiumPercentage     = AtmosphericResourceHandler.GetAtmosphericResourceContent(FlightGlobals.currentMainBody, KITResourceSettings.Sodium);

                        lastBodyID = FlightGlobals.currentMainBody.flightGlobalsIndex; // reassign the id of current body to the lastBodyID variable, ie. remember this planet, so that we skip this check next time!
                    }
                    catch (Exception e)
                    {
                        Debug.LogError("[KSPI]: ExtractAir getAtmosphericResourceContent Exception: " + e.Message);
                    }
                }

                // how much of the consumed atmosphere is going to end up as these?
                var fixedMaxAmmoniaRate    = _fixedConsumptionRate * _ammoniaPercentage;
                var fixedMaxArgonRate      = _fixedConsumptionRate * _argonPercentage;
                var fixedMaxChlorineRate   = _fixedConsumptionRate * _chlorinePercentage;
                var fixedMaxDioxideRate    = _fixedConsumptionRate * _dioxidePercentage;
                var fixedMaxHelium3Rate    = _fixedConsumptionRate * _helium3Percentage;
                var fixedMaxHelium4Rate    = _fixedConsumptionRate * _helium4Percentage;
                var fixedMaxHydrogenRate   = _fixedConsumptionRate * _hydrogenPercentage;
                var fixedMaxMethaneRate    = _fixedConsumptionRate * _methanePercentage;
                var fixedMaxMonoxideRate   = _fixedConsumptionRate * _monoxidePercentage;
                var fixedMaxNeonRate       = _fixedConsumptionRate * _neonPercentage;
                var fixedMaxNitrogenRate   = _fixedConsumptionRate * _nitrogenPercentage;
                var fixedMaxNitrogen15Rate = _fixedConsumptionRate * _nitrogen15Percentage;
                var fixedMaxOxygenRate     = _fixedConsumptionRate * _oxygenPercentage;
                var fixedMaxWaterRate      = _fixedConsumptionRate * _waterPercentage;
                var fixedMaxHeavyWaterRate = _fixedConsumptionRate * _heavywaterPercentage;
                var fixedMaxXenonRate      = _fixedConsumptionRate * _xenonPercentage;
                var fixedMaxDeuteriumRate  = _fixedConsumptionRate * _deuteriumPercentage;
                var fixedMaxKryptonRate    = _fixedConsumptionRate * _kryptonPercentage;
                var fixedMaxSodiumRate     = _fixedConsumptionRate * _sodiumPercentage;

                // how much can we add to the tanks per cycle? If allowOverflow is on, just push it all in, regardless of if the tank can hold the amount. Otherwise adjust accordingly
                var fixedMaxPossibleAmmoniaRate    = allowOverflow ? fixedMaxAmmoniaRate : Math.Min(_spareRoomAmmoniaMass, fixedMaxAmmoniaRate);
                var fixedMaxPossibleArgonRate      = allowOverflow ? fixedMaxArgonRate : Math.Min(_spareRoomArgonMass, fixedMaxArgonRate);
                var fixedMaxPossibleChlorineRate   = allowOverflow ? fixedMaxChlorineRate : Math.Min(_spareRoomChlorineMass, fixedMaxChlorineRate);
                var fixedMaxPossibleDioxideRate    = allowOverflow ? fixedMaxDioxideRate : Math.Min(_spareRoomDioxideMass, fixedMaxDioxideRate);
                var fixedMaxPossibleHelium3Rate    = allowOverflow ? fixedMaxHelium3Rate : Math.Min(_spareRoomHelium3Mass, fixedMaxHelium3Rate);
                var fixedMaxPossibleHelium4Rate    = allowOverflow ? fixedMaxHelium4Rate : Math.Min(_spareRoomHelium4Mass, fixedMaxHelium4Rate);
                var fixedMaxPossibleHydrogenRate   = allowOverflow ? fixedMaxHydrogenRate : Math.Min(_spareRoomHydrogenMass, fixedMaxHydrogenRate);
                var fixedMaxPossibleMethaneRate    = allowOverflow ? fixedMaxMethaneRate : Math.Min(_spareRoomMethaneMass, fixedMaxMethaneRate);
                var fixedMaxPossibleMonoxideRate   = allowOverflow ? fixedMaxMonoxideRate : Math.Min(_spareRoomMonoxideMass, fixedMaxMonoxideRate);
                var fixedMaxPossibleNeonRate       = allowOverflow ? fixedMaxNeonRate : Math.Min(_spareRoomNeonMass, fixedMaxNeonRate);
                var fixedMaxPossibleNitrogenRate   = allowOverflow ? fixedMaxNitrogenRate : Math.Min(_spareRoomNitrogenMass, fixedMaxNitrogenRate);
                var fixedMaxPossibleNitrogen15Rate = allowOverflow ? fixedMaxNitrogen15Rate : Math.Min(_spareRoomNitrogen15Mass, fixedMaxNitrogen15Rate);
                var fixedMaxPossibleOxygenRate     = allowOverflow ? fixedMaxOxygenRate : Math.Min(_spareRoomOxygenMass, fixedMaxOxygenRate);
                var fixedMaxPossibleWaterRate      = allowOverflow ? fixedMaxWaterRate : Math.Min(_spareRoomWaterMass, fixedMaxWaterRate);
                var fixedMaxPossibleHeavyWaterRate = allowOverflow ? fixedMaxHeavyWaterRate : Math.Min(_spareRoomHeavyWaterMass, fixedMaxHeavyWaterRate);
                var fixedMaxPossibleXenonRate      = allowOverflow ? fixedMaxXenonRate : Math.Min(_spareRoomXenonMass, fixedMaxXenonRate);
                var fixedMaxPossibleDeuteriumRate  = allowOverflow ? fixedMaxDeuteriumRate : Math.Min(_spareRoomDeuteriumMass, fixedMaxDeuteriumRate);
                var fixedMaxPossibleKryptonRate    = allowOverflow ? fixedMaxKryptonRate : Math.Min(_spareRoomKryptonMass, fixedMaxKryptonRate);
                var fixedMaxPossibleSodiumRate     = allowOverflow ? fixedMaxSodiumRate : Math.Min(_spareRoomSodiumMass, fixedMaxSodiumRate);

                // Check if the denominator for each is zero (in that case, assign zero outright, so that we don't end up with an infinite mess on our hands)
                var ammRatio        = (fixedMaxAmmoniaRate == 0) ? 0 : fixedMaxPossibleAmmoniaRate / fixedMaxAmmoniaRate;
                var arRatio         = (fixedMaxArgonRate == 0) ? 0 : fixedMaxPossibleArgonRate / fixedMaxArgonRate;
                var chlRatio        = (fixedMaxChlorineRate == 0) ? 0 : fixedMaxPossibleChlorineRate / fixedMaxChlorineRate;
                var dioxRatio       = (fixedMaxDioxideRate == 0) ? 0 : fixedMaxPossibleDioxideRate / fixedMaxDioxideRate;
                var he3Ratio        = (fixedMaxHelium3Rate == 0) ? 0 : fixedMaxPossibleHelium3Rate / fixedMaxHelium3Rate;
                var he4Ratio        = (fixedMaxHelium4Rate == 0) ? 0 : fixedMaxPossibleHelium4Rate / fixedMaxHelium4Rate;
                var hydroRatio      = (fixedMaxHydrogenRate == 0) ? 0 : fixedMaxPossibleHydrogenRate / fixedMaxHydrogenRate;
                var methRatio       = (fixedMaxMethaneRate == 0) ? 0 : fixedMaxPossibleMethaneRate / fixedMaxMethaneRate;
                var monoxRatio      = (fixedMaxMonoxideRate == 0) ? 0 : fixedMaxPossibleMonoxideRate / fixedMaxMonoxideRate;
                var neonRatio       = (fixedMaxNeonRate == 0) ? 0 : fixedMaxPossibleNeonRate / fixedMaxNeonRate;
                var nitroRatio      = (fixedMaxNitrogenRate == 0) ? 0 : fixedMaxPossibleNitrogenRate / fixedMaxNitrogenRate;
                var nitro15Ratio    = (fixedMaxNitrogen15Rate == 0) ? 0 : fixedMaxPossibleNitrogen15Rate / fixedMaxNitrogen15Rate;
                var oxyRatio        = (fixedMaxOxygenRate == 0) ? 0 : fixedMaxPossibleOxygenRate / fixedMaxOxygenRate;
                var waterRatio      = (fixedMaxWaterRate == 0) ? 0 : fixedMaxPossibleWaterRate / fixedMaxWaterRate;
                var heavywaterRatio = (fixedMaxHeavyWaterRate == 0) ? 0 : fixedMaxPossibleHeavyWaterRate / fixedMaxHeavyWaterRate;
                var xenonRatio      = (fixedMaxXenonRate == 0) ? 0 : fixedMaxPossibleXenonRate / fixedMaxXenonRate;
                var deuteriumRatio  = (fixedMaxDeuteriumRate == 0) ? 0 : fixedMaxPossibleDeuteriumRate / fixedMaxDeuteriumRate;
                var kryptonRatio    = (fixedMaxKryptonRate == 0) ? 0 : fixedMaxPossibleKryptonRate / fixedMaxKryptonRate;
                var sodiumRatio     = (fixedMaxSodiumRate == 0) ? 0 : fixedMaxPossibleSodiumRate / fixedMaxSodiumRate;

                /* finds a non-zero minimum of all the ratios (calculated above, as fixedMaxPossibleZZRate / fixedMaxZZRate). It needs to be non-zero
                 * so that the collecting works even when some of consitutents are absent from the local atmosphere (ie. when their definition is zero).
                 * Otherwise the consumptionStorageRatio would be zero and thus no atmosphere would be consumed. */
                _consumptionStorageRatio = new[] { ammRatio, arRatio, dioxRatio, he3Ratio, he4Ratio, hydroRatio, methRatio, monoxRatio, neonRatio, nitroRatio, nitro15Ratio, oxyRatio, waterRatio, heavywaterRatio, xenonRatio, deuteriumRatio, kryptonRatio, sodiumRatio }.Where(x => x > 0).Min();

                var maxAtmosphericConsumptionRate = _consumptionStorageRatio * _fixedConsumptionRate;

                // calculate atmospheric consumption per second
                _atmosphereConsumptionRate = buildInAirIntake;

                // calculate missing atmospheric which can be extracted from air intakes
                var remainingConsumptionNeeded = Math.Max(0, buildInAirIntake - maxAtmosphericConsumptionRate);

                // add the consumed atmosphere total atmospheric consumption rate
                _atmosphereConsumptionRate += resMan.Consume(ResourceName.IntakeAtmosphere, remainingConsumptionNeeded / _atmosphere.density) / _atmosphere.density;

                // produce the resources
                _ammoniaProductionRate    = _ammoniaPercentage <= 0 ? 0 : _atmosphereConsumptionRate * _ammoniaPercentage;
                _argonProductionRate      = _argonPercentage <= 0 ? 0 : _atmosphereConsumptionRate * _argonPercentage;
                _chlorineProductionRate   = _chlorinePercentage <= 0 ? 0 : _atmosphereConsumptionRate * _chlorinePercentage;
                _dioxideProductionRate    = _dioxidePercentage <= 0 ? 0 : _atmosphereConsumptionRate * _dioxidePercentage;
                _helium3ProductionRate    = _helium3Percentage <= 0 ? 0 : _atmosphereConsumptionRate * _helium3Percentage;
                _helium4ProductionRate    = _helium4Percentage <= 0 ? 0 : _atmosphereConsumptionRate * _helium4Percentage;
                _hydrogenProductionRate   = _hydrogenPercentage <= 0 ? 0 : _atmosphereConsumptionRate * _hydrogenPercentage;
                _methaneProductionRate    = _methanePercentage <= 0 ? 0 : _atmosphereConsumptionRate * _methanePercentage;
                _neonProductionRate       = _neonPercentage <= 0 ? 0 : _atmosphereConsumptionRate * _neonPercentage;
                _nitrogenProductionRate   = _nitrogenPercentage <= 0 ? 0 : -_atmosphereConsumptionRate * _nitrogenPercentage;
                _nitrogen15ProductionRate = _nitrogen15Percentage <= 0 ? 0 : _atmosphereConsumptionRate * _nitrogen15Percentage;
                _oxygenProductionRate     = _oxygenPercentage <= 0 ? 0 : _atmosphereConsumptionRate * _oxygenPercentage;
                _waterProductionRate      = _waterPercentage <= 0 ? 0 : _atmosphereConsumptionRate * _waterPercentage;
                _xenonProductionRate      = _xenonPercentage <= 0 ? 0 : _atmosphereConsumptionRate * _xenonPercentage;
                _deuteriumProductionRate  = _deuteriumPercentage <= 0 ? 0 : _atmosphereConsumptionRate * _deuteriumPercentage;
                _kryptonProductionRate    = _kryptonPercentage <= 0 ? 0 : _atmosphereConsumptionRate * _kryptonPercentage;
                _sodiumProductionRate     = _sodiumPercentage <= 0 ? 0 : _atmosphereConsumptionRate * _sodiumPercentage;

                resMan.Produce(ResourceName.AmmoniaLqd, _ammoniaProductionRate / _ammonia.density);
                resMan.Produce(ResourceName.ArgonLqd, _argonPercentage / _argon.density);
                resMan.Produce(ResourceName.ChlorineGas, _chlorinePercentage / _chlorine.density);
                resMan.Produce(ResourceName.CarbonDioxideLqd, _dioxidePercentage / _dioxide.density);
                resMan.Produce(ResourceName.Helium3Lqd, _helium3Percentage / _helium3.density);
                resMan.Produce(ResourceName.Helium4Lqd, _helium4Percentage / _helium4.density);
                resMan.Produce(ResourceName.MethaneLqd, _methanePercentage / _methane.density);
                resMan.Produce(ResourceName.CarbonMonoxideLqd, _monoxidePercentage / _monoxide.density);
                resMan.Produce(ResourceName.NeonLqd, _neonPercentage / _neon.density);
                resMan.Produce(ResourceName.NitrogenLqd, _nitrogenPercentage / _nitrogen.density);
                resMan.Produce(ResourceName.Nitrogen15Lqd, _nitrogen15Percentage / _nitrogen15.density);
                resMan.Produce(ResourceName.OxygenLqd, _oxygenPercentage / _oxygen.density);
                resMan.Produce(ResourceName.WaterPure, _waterPercentage / _water.density);
                resMan.Produce(ResourceName.WaterHeavy, _heavywaterPercentage / _heavyWater.density);
                resMan.Produce(ResourceName.XenonLqd, _xenonPercentage / _xenon.density);
                resMan.Produce(ResourceName.DeuteriumLqd, _deuteriumPercentage / _deuterium.density);
                resMan.Produce(ResourceName.KryptonLqd, _kryptonPercentage / _krypton.density);
                resMan.Produce(ResourceName.Sodium, _sodiumPercentage / _sodium.density);
            }
            else
            {
                _atmosphereConsumptionRate = 0;
                _ammoniaProductionRate     = 0;
                _argonProductionRate       = 0;
                _dioxideProductionRate     = 0;
                _helium3ProductionRate     = 0;
                _helium4ProductionRate     = 0;
                _hydrogenProductionRate    = 0;
                _methaneProductionRate     = 0;
                _monoxideProductionRate    = 0;
                _neonProductionRate        = 0;
                _nitrogenProductionRate    = 0;
                _nitrogen15ProductionRate  = 0;
                _oxygenProductionRate      = 0;
                _waterProductionRate       = 0;
                _heavyWaterProductionRate  = 0;
                _xenonProductionRate       = 0;
                _deuteriumProductionRate   = 0;
                _kryptonProductionRate     = 0;
                _sodiumProductionRate      = 0;
            }
        }
        public void ExtractAir(double rateMultiplier, double powerFraction, double productionModifier, bool allowOverflow, double fixedDeltaTime, bool offlineCollecting)
        {
            _effectiveMaxPower = productionModifier * PowerRequirements;
            _current_power     = _effectiveMaxPower * powerFraction;
            _current_rate      = CurrentPower / PluginHelper.ElectrolysisEnergyPerTon;

            // determine the maximum amount of a resource the vessel can hold (ie. tank capacities combined)
            // determine how much spare room there is in the vessel's resource tanks (for the resources this is going to produce)
            _part.GetResourceMass(_atmosphere, out _spareRoomAtmosphereMass, out _maxCapacityAtmosphereMass);
            _part.GetResourceMass(_ammonia, out _spareRoomAmmoniaMass, out _maxCapacityAmmoniaMass);
            _part.GetResourceMass(_argon, out _spareRoomArgonMass, out _maxCapacityArgonMass);
            _part.GetResourceMass(_dioxide, out _spareRoomDioxideMass, out _maxCapacityDioxideMass);
            _part.GetResourceMass(_helium3, out _spareRoomHelium3Mass, out _maxCapacityHelium3Mass);
            _part.GetResourceMass(_helium4, out _spareRoomHelium4Mass, out _maxCapacityHelium4Mass);
            _part.GetResourceMass(_hydrogen, out _spareRoomHydrogenMass, out _maxCapacityHydrogenMass);
            _part.GetResourceMass(_methane, out _spareRoomMethaneMass, out _maxCapacityMethaneMass);
            _part.GetResourceMass(_monoxide, out _spareRoomMonoxideMass, out _maxCapacityMonoxideMass);
            _part.GetResourceMass(_neon, out _spareRoomNeonMass, out _maxCapacityNeonMass);
            _part.GetResourceMass(_nitrogen, out _spareRoomNitrogenMass, out _maxCapacityNitrogenMass);
            _part.GetResourceMass(_nitrogen15, out _spareRoomNitrogen15Mass, out _maxCapacityNitrogen15Mass);
            _part.GetResourceMass(_oxygen, out _spareRoomOxygenMass, out _maxCapacityOxygenMass);
            _part.GetResourceMass(_water, out _spareRoomWaterMass, out _maxCapacityWaterMass);
            _part.GetResourceMass(_heavywater, out _spareRoomHeavyWaterMass, out _maxCapacityHeavyWaterMass);
            _part.GetResourceMass(_xenon, out _spareRoomXenonMass, out _maxCapacityXenonMass);
            _part.GetResourceMass(_deuterium, out _spareRoomDeuteriumMass, out _maxCapacityDeuteriumMass);
            _part.GetResourceMass(_krypton, out _spareRoomKryptonMass, out _maxCapacityKryptonMass);
            _part.GetResourceMass(_sodium, out _spareRoomSodiumMass, out _maxCapacitySodiumMass);

            // determine the amount of resources needed for processing (i.e. intake atmosphere) that the vessel actually holds
            _availableAtmosphereMass = _maxCapacityAtmosphereMass - _spareRoomAtmosphereMass;
            if (scoopAnimation != null)
            {
                var animationState = scoopAnimation[animName];
                normalizedTime = animationState.normalizedTime == 0
                    ? isDeployed ? 1 : 0
                    : animationState.normalizedTime;
            }
            else
            {
                normalizedTime = isDeployed ? 1 : 0;
            }

            // intake can only function when heading towards orbital path
            intakeModifier = scoopAnimation == null ? 1 : Math.Max(0, Vector3d.Dot(part.transform.up, part.vessel.obt_velocity.normalized));

            // calculate build in scoop capacity
            buildInAirIntake = normalizedTime <= 0.2 ? 0 :
                               AtmosphericFloatCurves.GetAtmosphericGasDensityKgPerCubicMeter(_vessel) * (1 + _vessel.obt_speed) * surfaceArea * intakeModifier * Math.Sqrt((normalizedTime - 0.2) * 1.25);


            atmosphereConsumptionRatio = offlineCollecting ? 1
                    : _current_rate > 0
                        ? Math.Min(_current_rate, buildInAirIntake + _availableAtmosphereMass) / _current_rate
                        : 0;

            _fixedConsumptionRate = _current_rate * fixedDeltaTime * atmosphereConsumptionRatio;

            // begin the intake atmosphere processing
            // check if there is anything to consume and if there is spare room for at least one of the products
            if (_fixedConsumptionRate > 0 && (
                    _spareRoomHydrogenMass > 0 || _spareRoomHelium3Mass > 0 || _spareRoomHelium4Mass > 0 || _spareRoomMonoxideMass > 0 ||
                    _spareRoomNitrogenMass > 0 || _spareRoomNitrogen15Mass > 0 || _spareRoomArgonMass > 0 || _spareRoomDioxideMass > 0 || _spareRoomMethaneMass > 0 ||
                    _spareRoomNeonMass > 0 || _spareRoomWaterMass > 0 || _spareRoomHeavyWaterMass > 0 || _spareRoomOxygenMass > 0 ||
                    _spareRoomXenonMass > 0 || _spareRoomDeuteriumMass > 0 || _spareRoomKryptonMass > 0 || _spareRoomSodiumMass > 0 || _spareRoomAmmoniaMass > 0))
            {
                /* Now to get the actual percentages from AtmosphericResourceHandler Freethinker extended.
                 * Calls getAtmosphericResourceContent which calls getAtmosphericCompositionForBody which (if there's no definition, i.e. we're using a custom solar system
                 * with weird and fantastic new planets) in turn calls the new GenerateCompositionFromCelestialBody function Freethinker created, which creates a composition
                 * for the upper-level functions based on the planet's size and temperatures. So even though this is calling one method, it's actually going through two or three
                 *  total. Since we like CPUs and want to save them the hassle, let's close this off behind a cheap check.
                 */
                if (FlightGlobals.currentMainBody.flightGlobalsIndex != lastBodyID) // did we change a SOI since last time? If yes, get new percentages. Should work the first time as well, since lastBodyID starts as -1, while bodies in the list start at 0
                {
                    Debug.Log("[KSPI] - looking up Atmosphere contents for " + FlightGlobals.currentMainBody.name);

                    // remember, all these are persistent. Once we get them, we won't need to calculate them again until we change SOI
                    _ammoniaPercentage    = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _ammonia_resource_name);
                    _argonPercentage      = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _argon_resource_name);
                    _monoxidePercentage   = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _monoxide_resource_name);
                    _dioxidePercentage    = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _dioxide_resource_name);
                    _helium3Percentage    = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _helium3_resource_name);
                    _helium4Percentage    = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _helium4_resource_name);
                    _hydrogenPercentage   = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _hydrogen_resource_name);
                    _methanePercentage    = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _methane_resource_name);
                    _neonPercentage       = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _neon_resource_name);
                    _nitrogenPercentage   = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _nitrogen_resource_name);
                    _nitrogen15Percentage = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _nitrogen15_resource_name);
                    _oxygenPercentage     = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _oxygen_resource_name);
                    _waterPercentage      = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _water_resource_name);
                    _heavywaterPercentage = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _heavywater_resource_name);
                    _xenonPercentage      = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _xenon_resource_name);
                    _deuteriumPercentage  = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _deuterium_resource_name);
                    _kryptonPercentage    = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _krypton_resource_name);
                    _sodiumPercentage     = AtmosphericResourceHandler.getAtmosphericResourceContent(FlightGlobals.currentMainBody.flightGlobalsIndex, _sodium_resource_name);

                    lastBodyID = FlightGlobals.currentMainBody.flightGlobalsIndex; // reassign the id of current body to the lastBodyID variable, ie. remember this planet, so that we skip this check next time!
                }

                if (offlineCollecting) // if we're collecting offline, we don't need to actually consume the resource, just provide the lines below with a number
                {
                    _atmosphere_consumption_rate = Math.Min(_current_rate, buildInAirIntake + GetTotalAirScoopedPerSecond());
                    ScreenMessages.PostScreenMessage("The atmospheric extractor processed " + _atmosphere_resource_name + " for " + fixedDeltaTime.ToString("F0") + " seconds", 60.0f, ScreenMessageStyle.UPPER_CENTER);
                }
                else
                {
                    // how much of the consumed atmosphere is going to end up as these?
                    var fixedMaxAmmoniaRate    = _fixedConsumptionRate * _ammoniaPercentage;
                    var fixedMaxArgonRate      = _fixedConsumptionRate * _argonPercentage;
                    var fixedMaxDioxideRate    = _fixedConsumptionRate * _dioxidePercentage;
                    var fixedMaxHelium3Rate    = _fixedConsumptionRate * _helium3Percentage;
                    var fixedMaxHelium4Rate    = _fixedConsumptionRate * _helium4Percentage;
                    var fixedMaxHydrogenRate   = _fixedConsumptionRate * _hydrogenPercentage;
                    var fixedMaxMethaneRate    = _fixedConsumptionRate * _methanePercentage;
                    var fixedMaxMonoxideRate   = _fixedConsumptionRate * _monoxidePercentage;
                    var fixedMaxNeonRate       = _fixedConsumptionRate * _neonPercentage;
                    var fixedMaxNitrogenRate   = _fixedConsumptionRate * _nitrogenPercentage;
                    var fixedMaxNitrogen15Rate = _fixedConsumptionRate * _nitrogen15Percentage;
                    var fixedMaxOxygenRate     = _fixedConsumptionRate * _oxygenPercentage;
                    var fixedMaxWaterRate      = _fixedConsumptionRate * _waterPercentage;
                    var fixedMaxHeavyWaterRate = _fixedConsumptionRate * _heavywaterPercentage;
                    var fixedMaxXenonRate      = _fixedConsumptionRate * _xenonPercentage;
                    var fixedMaxDeuteriumRate  = _fixedConsumptionRate * _deuteriumPercentage;
                    var fixedMaxKryptonRate    = _fixedConsumptionRate * _kryptonPercentage;
                    var fixedMaxSodiumRate     = _fixedConsumptionRate * _sodiumPercentage;

                    // how much can we add to the tanks per cycle? If allowOverflow is on, just push it all in, regardless of if the tank can hold the amount. Otherwise adjust accordingly
                    var fixedMaxPossibleAmmoniaRate    = allowOverflow ? fixedMaxAmmoniaRate : Math.Min(_spareRoomAmmoniaMass, fixedMaxAmmoniaRate);
                    var fixedMaxPossibleArgonRate      = allowOverflow ? fixedMaxArgonRate : Math.Min(_spareRoomArgonMass, fixedMaxArgonRate);
                    var fixedMaxPossibleDioxideRate    = allowOverflow ? fixedMaxDioxideRate : Math.Min(_spareRoomDioxideMass, fixedMaxDioxideRate);
                    var fixedMaxPossibleHelium3Rate    = allowOverflow ? fixedMaxHelium3Rate : Math.Min(_spareRoomHelium3Mass, fixedMaxHelium3Rate);
                    var fixedMaxPossibleHelium4Rate    = allowOverflow ? fixedMaxHelium4Rate : Math.Min(_spareRoomHelium4Mass, fixedMaxHelium4Rate);
                    var fixedMaxPossibleHydrogenRate   = allowOverflow ? fixedMaxHydrogenRate : Math.Min(_spareRoomHydrogenMass, fixedMaxHydrogenRate);
                    var fixedMaxPossibleMethaneRate    = allowOverflow ? fixedMaxMethaneRate : Math.Min(_spareRoomMethaneMass, fixedMaxMethaneRate);
                    var fixedMaxPossibleMonoxideRate   = allowOverflow ? fixedMaxMonoxideRate : Math.Min(_spareRoomMonoxideMass, fixedMaxMonoxideRate);
                    var fixedMaxPossibleNeonRate       = allowOverflow ? fixedMaxNeonRate : Math.Min(_spareRoomNeonMass, fixedMaxNeonRate);
                    var fixedMaxPossibleNitrogenRate   = allowOverflow ? fixedMaxNitrogenRate : Math.Min(_spareRoomNitrogenMass, fixedMaxNitrogenRate);
                    var fixedMaxPossibleNitrogen15Rate = allowOverflow ? fixedMaxNitrogen15Rate : Math.Min(_spareRoomNitrogen15Mass, fixedMaxNitrogen15Rate);
                    var fixedMaxPossibleOxygenRate     = allowOverflow ? fixedMaxOxygenRate : Math.Min(_spareRoomOxygenMass, fixedMaxOxygenRate);
                    var fixedMaxPossibleWaterRate      = allowOverflow ? fixedMaxWaterRate : Math.Min(_spareRoomWaterMass, fixedMaxWaterRate);
                    var fixedMaxPossibleHeavyWaterRate = allowOverflow ? fixedMaxHeavyWaterRate : Math.Min(_spareRoomHeavyWaterMass, fixedMaxHeavyWaterRate);
                    var fixedMaxPossibleXenonRate      = allowOverflow ? fixedMaxXenonRate : Math.Min(_spareRoomXenonMass, fixedMaxXenonRate);
                    var fixedMaxPossibleDeuteriumRate  = allowOverflow ? fixedMaxDeuteriumRate : Math.Min(_spareRoomDeuteriumMass, fixedMaxDeuteriumRate);
                    var fixedMaxPossibleKryptonRate    = allowOverflow ? fixedMaxKryptonRate : Math.Min(_spareRoomKryptonMass, fixedMaxKryptonRate);
                    var fixedMaxPossibleSodiumRate     = allowOverflow ? fixedMaxSodiumRate : Math.Min(_spareRoomSodiumMass, fixedMaxSodiumRate);

                    // Check if the denominator for each is zero (in that case, assign zero outright, so that we don't end up with an infinite mess on our hands)
                    var ammRatio        = (fixedMaxAmmoniaRate == 0) ? 0 : fixedMaxPossibleAmmoniaRate / fixedMaxAmmoniaRate;
                    var arRatio         = (fixedMaxArgonRate == 0) ? 0 : fixedMaxPossibleArgonRate / fixedMaxArgonRate;
                    var dioxRatio       = (fixedMaxDioxideRate == 0) ? 0 : fixedMaxPossibleDioxideRate / fixedMaxDioxideRate;
                    var he3Ratio        = (fixedMaxHelium3Rate == 0) ? 0 : fixedMaxPossibleHelium3Rate / fixedMaxHelium3Rate;
                    var he4Ratio        = (fixedMaxHelium4Rate == 0) ? 0 : fixedMaxPossibleHelium4Rate / fixedMaxHelium4Rate;
                    var hydroRatio      = (fixedMaxHydrogenRate == 0) ? 0 : fixedMaxPossibleHydrogenRate / fixedMaxHydrogenRate;
                    var methRatio       = (fixedMaxMethaneRate == 0) ? 0 : fixedMaxPossibleMethaneRate / fixedMaxMethaneRate;
                    var monoxRatio      = (fixedMaxMonoxideRate == 0) ? 0 : fixedMaxPossibleMonoxideRate / fixedMaxMonoxideRate;
                    var neonRatio       = (fixedMaxNeonRate == 0) ? 0 : fixedMaxPossibleNeonRate / fixedMaxNeonRate;
                    var nitroRatio      = (fixedMaxNitrogenRate == 0) ? 0 : fixedMaxPossibleNitrogenRate / fixedMaxNitrogenRate;
                    var nitro15Ratio    = (fixedMaxNitrogen15Rate == 0) ? 0 : fixedMaxPossibleNitrogen15Rate / fixedMaxNitrogen15Rate;
                    var oxyRatio        = (fixedMaxOxygenRate == 0) ? 0 : fixedMaxPossibleOxygenRate / fixedMaxOxygenRate;
                    var waterRatio      = (fixedMaxWaterRate == 0) ? 0 : fixedMaxPossibleWaterRate / fixedMaxWaterRate;
                    var heavywaterRatio = (fixedMaxHeavyWaterRate == 0) ? 0 : fixedMaxPossibleHeavyWaterRate / fixedMaxHeavyWaterRate;
                    var xenonRatio      = (fixedMaxXenonRate == 0) ? 0 : fixedMaxPossibleXenonRate / fixedMaxXenonRate;
                    var deuteriumRatio  = (fixedMaxDeuteriumRate == 0) ? 0 : fixedMaxPossibleDeuteriumRate / fixedMaxDeuteriumRate;
                    var kryptonRatio    = (fixedMaxKryptonRate == 0) ? 0 : fixedMaxPossibleKryptonRate / fixedMaxKryptonRate;
                    var sodiumRatio     = (fixedMaxSodiumRate == 0) ? 0 : fixedMaxPossibleSodiumRate / fixedMaxSodiumRate;

                    /* finds a non-zero minimum of all the ratios (calculated above, as fixedMaxPossibleZZRate / fixedMaxZZRate). It needs to be non-zero
                     * so that the collecting works even when some of consitutents are absent from the local atmosphere (ie. when their definition is zero).
                     * Otherwise the consumptionStorageRatio would be zero and thus no atmosphere would be consumed. */
                    _consumptionStorageRatio = new [] { ammRatio, arRatio, dioxRatio, he3Ratio, he4Ratio, hydroRatio, methRatio, monoxRatio, neonRatio, nitroRatio, nitro15Ratio, oxyRatio, waterRatio, heavywaterRatio, xenonRatio, deuteriumRatio, kryptonRatio, sodiumRatio }.Where(x => x > 0).Min();

                    var max_atmospheric_consumption_rate = _consumptionStorageRatio * _fixedConsumptionRate;

                    // calculate amospereic consumption per second
                    _atmosphere_consumption_rate = buildInAirIntake;

                    // calculate missing atmsophere which can be extracted from air intakes
                    var remainingConsumptionNeeded = Math.Max(0, buildInAirIntake - max_atmospheric_consumption_rate);

                    // add the consumed atmosphere total atmopheric consumption rate
                    _atmosphere_consumption_rate += _part.RequestResource(_atmosphere_resource_name, remainingConsumptionNeeded / _atmosphere.density) / fixedDeltaTime * _atmosphere.density;
                }

                // produce the resources
                _ammonia_production_rate    = _ammoniaPercentage == 0 ? 0 : -_part.RequestResource(_ammonia_resource_name, -_atmosphere_consumption_rate * _ammoniaPercentage * fixedDeltaTime / _ammonia.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _ammonia.density;
                _argon_production_rate      = _argonPercentage == 0 ? 0 : -_part.RequestResource(_argon_resource_name, -_atmosphere_consumption_rate * _argonPercentage * fixedDeltaTime / _argon.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _argon.density;
                _dioxide_production_rate    = _dioxidePercentage == 0 ? 0 : -_part.RequestResource(_dioxide_resource_name, -_atmosphere_consumption_rate * _dioxidePercentage * fixedDeltaTime / _dioxide.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _dioxide.density;
                _helium3_production_rate    = _helium3Percentage == 0 ? 0 : -_part.RequestResource(_helium3_resource_name, -_atmosphere_consumption_rate * _helium3Percentage * fixedDeltaTime / _helium3.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _helium3.density;
                _helium4_production_rate    = _helium4Percentage == 0 ? 0 : -_part.RequestResource(_helium4_resource_name, -_atmosphere_consumption_rate * _helium4Percentage * fixedDeltaTime / _helium4.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _helium4.density;
                _hydrogen_production_rate   = _hydrogenPercentage == 0 ? 0 : -_part.RequestResource(_hydrogen_resource_name, -_atmosphere_consumption_rate * _hydrogenPercentage * fixedDeltaTime / _hydrogen.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _hydrogen.density;
                _methane_production_rate    = _methanePercentage == 0 ? 0 : -_part.RequestResource(_methane_resource_name, -_atmosphere_consumption_rate * _methanePercentage * fixedDeltaTime / _methane.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _methane.density;
                _monoxide_production_rate   = _monoxidePercentage == 0 ? 0 : -_part.RequestResource(_monoxide_resource_name, -_atmosphere_consumption_rate * _monoxidePercentage * fixedDeltaTime / _monoxide.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _monoxide.density;
                _neon_production_rate       = _neonPercentage == 0 ? 0 : -_part.RequestResource(_neon_resource_name, -_atmosphere_consumption_rate * _neonPercentage * fixedDeltaTime / _neon.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _neon.density;
                _nitrogen_production_rate   = _nitrogenPercentage == 0 ? 0 : -_part.RequestResource(_nitrogen_resource_name, -_atmosphere_consumption_rate * _nitrogenPercentage * fixedDeltaTime / _nitrogen.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _nitrogen.density;
                _nitrogen15_production_rate = _nitrogen15Percentage == 0 ? 0 : -_part.RequestResource(_nitrogen15_resource_name, -_atmosphere_consumption_rate * _nitrogen15Percentage * fixedDeltaTime / _nitrogen15.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _nitrogen15.density;
                _oxygen_production_rate     = _oxygenPercentage == 0 ? 0 : -_part.RequestResource(_oxygen_resource_name, -_atmosphere_consumption_rate * _oxygenPercentage * fixedDeltaTime / _oxygen.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _oxygen.density;
                _water_production_rate      = _waterPercentage == 0 ? 0 : -_part.RequestResource(_water_resource_name, -_atmosphere_consumption_rate * _waterPercentage * fixedDeltaTime / _water.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _water.density;
                _heavywater_production_rate = _heavywaterPercentage == 0 ? 0 : -_part.RequestResource(_heavywater_resource_name, -_atmosphere_consumption_rate * _heavywaterPercentage * fixedDeltaTime / _heavywater.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _heavywater.density;
                _xenon_production_rate      = _xenonPercentage == 0 ? 0 : -_part.RequestResource(_xenon_resource_name, -_atmosphere_consumption_rate * _xenonPercentage * fixedDeltaTime / _xenon.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _xenon.density;
                _deuterium_production_rate  = _deuteriumPercentage == 0 ? 0 : -_part.RequestResource(_deuterium_resource_name, -_atmosphere_consumption_rate * _deuteriumPercentage * fixedDeltaTime / _deuterium.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _deuterium.density;
                _krypton_production_rate    = _kryptonPercentage == 0 ? 0 : -_part.RequestResource(_krypton_resource_name, -_atmosphere_consumption_rate * _kryptonPercentage * fixedDeltaTime / _krypton.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _krypton.density;
                _sodium_production_rate     = _sodiumPercentage == 0 ? 0 : -_part.RequestResource(_sodium_resource_name, -_atmosphere_consumption_rate * _sodiumPercentage * fixedDeltaTime / _sodium.density, ResourceFlowMode.ALL_VESSEL) / fixedDeltaTime * _sodium.density;
            }
            else
            {
                _atmosphere_consumption_rate = 0;
                _ammonia_production_rate     = 0;
                _argon_production_rate       = 0;
                _dioxide_production_rate     = 0;
                _helium3_production_rate     = 0;
                _helium4_production_rate     = 0;
                _hydrogen_production_rate    = 0;
                _methane_production_rate     = 0;
                _monoxide_production_rate    = 0;
                _neon_production_rate        = 0;
                _nitrogen_production_rate    = 0;
                _nitrogen15_production_rate  = 0;
                _oxygen_production_rate      = 0;
                _water_production_rate       = 0;
                _heavywater_production_rate  = 0;
                _xenon_production_rate       = 0;
                _deuterium_production_rate   = 0;
                _krypton_production_rate     = 0;
                _sodium_production_rate      = 0;
            }
        }