public DState RungeKutta(ref SimulationState s, double dt)
		{
			DState ds1 = s.derivate();
			DState ds2 = s.increment(ds1, dt / 2).derivate();
			DState ds3 = s.increment(ds2, dt / 2).derivate();
			DState ds4 = s.increment(ds3, dt).derivate();

			s = s.increment(ds1, dt/6).increment(ds2, dt/3).increment(ds3, dt/3).increment(ds4, dt/6);
			return ds1;
		}
		public SimulationLogic(List<Part> parts, CelestialBody planet, double departureAltitude, double maxAcceleration, bool advancedSimulation, Vector3d forward)
		{
			this.advancedSimulation = advancedSimulation;
			state = new SimulationState(planet, departureAltitude, forward);
			state.maxAcceleration = maxAcceleration != 0 ? maxAcceleration : double.MaxValue;

			//Initialize first stage with available engines and launch clamps
			stages.Add(new StageDescription(0));
			foreach (Part p in parts)
			{
				if (p.Modules.OfType<LaunchClamp>().Count() > 0)
					stages[0].stageParts.Add(p);
				else
					state.availableNodes.Add(p, new Node(p, forward));
			}
			foreach (CompoundPart p in parts.OfType<CompoundPart>())
			{
				if (p.Modules.OfType<CompoundParts.CModuleFuelLine>().Count() > 0
					&& state.availableNodes.ContainsKey(p.target))
					state.availableNodes[p.target].linkedParts.Add(p.parent);
			}
			stages[0].stageParts.AddRange(state.updateEngines());
		}
Exemple #3
0
        public void computeStages()
        {
                        #if DEBUG
            DateTime startTime = DateTime.Now;
                        #endif
            double elapsedTime = 0;
            while (state.availableNodes.Count() > 0)
            {
                if (advancedSimulation)
                {
                    state.m = state.availableNodes.Sum(p => p.Value.mass);
                    // Compute flow for active engines
                    foreach (EngineWrapper e in state.activeEngines)
                    {
                        e.evaluateFuelFlow(state.atmDensity, state.machNumber, state.throttle, false);
                    }
                }
                else
                {
                    // Compute flow for active engines, in vacuum
                    foreach (EngineWrapper e in state.activeEngines)
                    {
                        e.evaluateFuelFlow(1, 1, 1, false);
                    }
                }

                double step = Math.Max(state.availableNodes.Min(node => node.Value.getNextEvent()), 1E-100);

                // Quit if there is no other event
                if (step == Double.MaxValue && state.throttle > 0)
                {
                    break;
                }

                if (advancedSimulation)
                {
                    if (step > simulationStep)
                    {
                        step = Math.Max(simulationStep, (elapsedTime + step - stages.Last().activationTime) / 100);
                    }

                    if (state.throttle == 0)
                    {
                        step = simulationStep;
                    }

                    float  throttle   = state.throttle;
                    var    savedState = state;
                    DState dState     = RungeKutta(ref state, step);
                    while (Math.Abs(state.throttle - throttle) > 0.05 && step > 1e-3)
                    {
                        state  = savedState;
                        step  /= 2;
                        dState = RungeKutta(ref state, step);
                    }
                    Sample sample;
                    sample.time         = elapsedTime + step;
                    sample.velocity     = state.v_surf;
                    sample.altitude     = state.r - state.planet.Radius;
                    sample.mass         = state.m;
                    sample.acceleration = Math.Sqrt(dState.ax_nograv * dState.ax_nograv + dState.ay_nograv * dState.ay_nograv);
                    sample.throttle     = state.throttle;
                    if (samples.Count == 0 || samples.Last().time + simulationStep <= sample.time)
                    {
                        samples.Add(sample);
                    }
                }
                elapsedTime += step;

                // Burn the fuel !
                bool eventHappens = false;
                foreach (Node node in state.availableNodes.Values)
                {
                    eventHappens |= node.applyFuelConsumption(step);
                }

                if (!eventHappens)
                {
                    continue;
                }

                // Add all decouplers in a new stage
                StageDescription dpStage = new StageDescription(elapsedTime);
                foreach (Node node in state.availableNodes.Values)
                {
                    ModuleDecouple          decoupler  = node.part.Modules.OfType <ModuleDecouple>().FirstOrDefault();
                    ModuleAnchoredDecoupler aDecoupler = node.part.Modules.OfType <ModuleAnchoredDecoupler>().FirstOrDefault();
                    if ((decoupler != null || aDecoupler != null) && !node.hasFuelInChildren(state.availableNodes))
                    {
                        dpStage.stageParts.Add(node.part);
                    }
                }

                if (dpStage.stageParts.Count > 0)
                {
                    stages.Add(dpStage);
                    List <Part> activableChildren = new List <Part>();

                    // Remove all decoupled elements, fire sepratrons and parachutes
                    foreach (Part part in dpStage.stageParts)
                    {
                        if (state.availableNodes.ContainsKey(part))
                        {
                            activableChildren.AddRange(state.availableNodes[part].getRelevantChildrenOnDecouple(state.availableNodes));
                            dropPartAndChildren(part);
                        }
                    }
                    dpStage.stageParts.AddRange(activableChildren);
                }

                // Update available engines and fuel flow
                List <Part> activeEngines = state.updateEngines();

                StageDescription newStage = new StageDescription(elapsedTime);
                newStage.stageParts.AddRange(activeEngines);
                stages.Add(newStage);
            }

            // Put all remaining items (parachutes?) in a separate 0 stage
            foreach (var stage in stages)
            {
                foreach (var part in stage.stageParts)
                {
                    state.availableNodes.Remove(part);
                }
            }

            // Put fairings in a separate stage before decoupling
            List <StageDescription> newStages = new List <StageDescription>();
            for (int i = 0; i < stages.Count; i++)
            {
                var fairings    = stages[i].stageParts.Where(p => p.Modules.OfType <ModuleProceduralFairing>().Count() != 0);
                var notfairings = stages[i].stageParts.Where(p => p.Modules.OfType <ModuleProceduralFairing>().Count() == 0);

                if (fairings.Count() != 0)
                {
                    StageDescription fairingStage = new StageDescription(stages[i].activationTime);
                    fairingStage.stageParts.AddRange(fairings);
                    newStages.Add(fairingStage);
                }

                StageDescription notfairingStage = new StageDescription(stages[i].activationTime);
                notfairingStage.stageParts.AddRange(notfairings);
                newStages.Add(notfairingStage);
            }
            stages = newStages;

            int initialStage = 0;
            foreach (Part part in state.availableNodes.Keys)
            {
                if (part.hasStagingIcon)
                {
                    part.inverseStage = 0;
                    initialStage      = 1;
                }
            }

            // doesn't seem to work in 1.1
            // Set stage number correctly
            //StageManager.SetStageCount (stages.Count);

            for (int stage = stages.Count - 1; stage >= 0; stage--)
            {
                var currentStage = stages[stage];
                foreach (Part part in currentStage.stageParts)
                {
                    part.inverseStage = stages.Count - 1 - stage + initialStage;
                }
            }

            // doesn't seem to work in 1.1
            //StageManager.Instance.SortIcons (true);

                        #if DEBUG
            var compTime = DateTime.Now - startTime;
            Debug.Log("Staging computed in " + compTime.TotalMilliseconds + "ms");
            if (samples.Count() > 0)
            {
                string result = "time;altitude;velocity;acceleration;mass;throttle\n";
                foreach (var sample in samples)
                {
                    result += sample.time + ";" + sample.altitude + ";" + sample.velocity + ";" + sample.acceleration + ";" + sample.mass + ";" + sample.throttle + "\n";
                }
                Debug.Log(result);
            }
                        #endif
        }
		public void computeStages()
		{
			#if DEBUG
			DateTime startTime = DateTime.Now;
			#endif
			double elapsedTime = 0;
			while (state.availableNodes.Count() > 0)
			{
				if (advancedSimulation)
				{
					state.m = state.availableNodes.Sum(p => p.Value.mass);
					// Compute flow for active engines
					foreach (EngineWrapper e in state.activeEngines)
						e.evaluateFuelFlow(state.atmDensity, state.machNumber, state.throttle, false);
				}
				else
				{
					// Compute flow for active engines, in vacuum
					foreach (EngineWrapper e in state.activeEngines)
						e.evaluateFuelFlow(1, 1, 1, false);
				}

				double step = Math.Max(state.availableNodes.Min(node => node.Value.getNextEvent()), 1E-100);

				// Quit if there is no other event
				if (step == Double.MaxValue && state.throttle > 0)
					break;

				if (advancedSimulation)
				{
					if (step > simulationStep)
						step = Math.Max(simulationStep, (elapsedTime + step - stages.Last().activationTime) / 100);

					if (state.throttle == 0)
						step = simulationStep;

					float throttle = state.throttle;
					var savedState = state;
					DState dState = RungeKutta(ref state, step);
					while (Math.Abs(state.throttle - throttle) > 0.05 && step > 1e-3)
					{
						state = savedState;
						step /= 2;
						dState = RungeKutta(ref state, step);
					}
					Sample sample;
					sample.time = elapsedTime + step;
					sample.velocity = state.v_surf;
					sample.altitude = state.r - state.planet.Radius;
					sample.mass = state.m;
					sample.acceleration = Math.Sqrt(dState.ax_nograv * dState.ax_nograv + dState.ay_nograv * dState.ay_nograv);
					sample.throttle = state.throttle;
					if (samples.Count == 0 || samples.Last().time + simulationStep <= sample.time)
						samples.Add(sample);
				}
				elapsedTime += step;

				// Burn the fuel !
				bool eventHappens = false;
				foreach (Node node in state.availableNodes.Values)
				{
					eventHappens |= node.applyFuelConsumption(step);
				}

				if (!eventHappens)
					continue;

				// Add all decouplers in a new stage
				StageDescription dpStage = new StageDescription(elapsedTime);
				foreach (Node node in state.availableNodes.Values)
				{
					ModuleDecouple decoupler = node.part.Modules.OfType<ModuleDecouple>().FirstOrDefault();
					ModuleAnchoredDecoupler aDecoupler= node.part.Modules.OfType<ModuleAnchoredDecoupler>().FirstOrDefault();
					if ((decoupler != null || aDecoupler != null)&& !node.hasFuelInChildren(state.availableNodes))
					{
						dpStage.stageParts.Add(node.part);
					}
				}

				if (dpStage.stageParts.Count > 0)
				{
					stages.Add(dpStage);
					List<Part> activableChildren = new List<Part>();

					// Remove all decoupled elements, fire sepratrons and parachutes
					foreach (Part part in dpStage.stageParts)
					{
						if (state.availableNodes.ContainsKey(part))
						{
							activableChildren.AddRange(state.availableNodes[part].getRelevantChildrenOnDecouple(state.availableNodes));
							dropPartAndChildren(part);
						}
					}
					dpStage.stageParts.AddRange(activableChildren);
				}

				// Update available engines and fuel flow
				List<Part> activeEngines = state.updateEngines();

				StageDescription newStage = new StageDescription(elapsedTime);
				newStage.stageParts.AddRange(activeEngines);
				stages.Add (newStage);
			}

			// Put fairings in a separate stage before decoupling
			List<StageDescription> newStages = new List<StageDescription>();
			for (int i = 0; i < stages.Count; i++)
			{
				var fairings = stages[i].stageParts.Where(p => p.Modules.OfType<ModuleProceduralFairing>().Count() != 0);
				var notfairings = stages[i].stageParts.Where(p => p.Modules.OfType<ModuleProceduralFairing>().Count() == 0);

				if (fairings.Count() != 0)
				{
					StageDescription fairingStage = new StageDescription(stages[i].activationTime);
					fairingStage.stageParts.AddRange(fairings);
					newStages.Add(fairingStage);
				}

				StageDescription notfairingStage = new StageDescription(stages[i].activationTime);
				notfairingStage.stageParts.AddRange(notfairings);
				newStages.Add(notfairingStage);
			}
			stages = newStages;

			// Put all remaining items (parachutes?) in a separate 0 stage
			foreach(var stage in stages)
			{
				foreach (var part in stage.stageParts)
					state.availableNodes.Remove(part);
			}
			var stage0 = new StageDescription(elapsedTime);
			stage0.stageParts = state.availableNodes.Keys.Where(p => p.hasStagingIcon).ToList();
			if (stage0.stageParts.Count != 0)
				stages.Add(stage0);

			stages.Reverse();
			// This is ugly, but on KSP 1.1 I have not found anything better
			// We call this private method for each part from the root, twice and it works...
			var SortIcons = typeof(KSP.UI.Screens.StageManager).GetMethod("SortIcons",
				System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, null,
				new Type[]{typeof(bool), typeof(Part), typeof(bool)}, null);
			var root = stages[0].stageParts[0];
			while (root.parent != null) root = root.parent;
			setStages(root, SortIcons);
			setStages(root, SortIcons);

			int initialStage = 0;
			foreach (Part part in state.availableNodes.Keys)
			{
				if (part.hasStagingIcon)
				{
					part.inverseStage = 0;
					initialStage = 1;
				}
			}

			// doesn't seem to work in 1.1
			// Set stage number correctly
			//StageManager.SetStageCount (stages.Count);

			for (int stage = stages.Count - 1 ; stage >= 0; stage--)
			{
				var currentStage = stages[stage];
				foreach(Part part in currentStage.stageParts)
				{
					part.inverseStage = stages.Count - 1 - stage + initialStage;
				}
			}

			// doesn't seem to work in 1.1
			//StageManager.Instance.SortIcons (true);

			#if DEBUG
			var compTime = DateTime.Now - startTime;
			Debug.Log("Staging computed in " + compTime.TotalMilliseconds + "ms");
			if (samples.Count() > 0)
			{
				string result = "time;altitude;velocity;acceleration;mass;throttle\n";
				foreach (var sample in samples)
				{
					result += sample.time + ";"+sample.altitude+";"+sample.velocity+";"+sample.acceleration+";"+sample.mass+";"+sample.throttle+"\n";
				}
				Debug.Log(result);
			}
			#endif
		}