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);
        }
Example #2
0
        public DState derivate()
        {
            DState res = new DState();

            res.vx = vx;
            res.vy = vy;

            double r        = this.r;
            float  altitude = (float)(r - planet.Radius);

            double theta           = Math.Atan2(u_x, u_y);
            double thrustDirection = theta + ascentPath.FlightPathAngle(altitude);

            // gravity
            double grav_acc = -planet.gravParameter / (r * r);

            // drag
            var    dragForce = StockAeroUtil.SimAeroForce(availableNodes.Keys.ToList(), planet, new UnityEngine.Vector3(0, (float)v_surf, 0), altitude);
            double drag_acc  = dragForce.magnitude / m;

            //throttle
            double desiredThrust = maxAcceleration * m;

            if (maxThrust != minThrust)
            {
                throttle = ((float)desiredThrust - minThrust) / (maxThrust - minThrust);
            }
            else
            {
                throttle = 1;
            }
            throttle = Math.Max(0, Math.Min(1, throttle));

            // Effective thrust
            double F = activeEngines.Sum(e => e.thrust(throttle, pressure, machNumber, atmDensity));

            // Propellant mass variation
            res.dm = -activeEngines.Sum(e => e.evaluateFuelFlow(atmDensity, machNumber, throttle));

            res.ax_nograv = F / m * Math.Sin(thrustDirection);
            res.ay_nograv = F / m * Math.Cos(thrustDirection);
            if (v_surf != 0)
            {
                res.ax_nograv -= drag_acc * v_surf_x / v_surf;
                res.ay_nograv -= drag_acc * v_surf_y / v_surf;
            }
            res.ax = res.ax_nograv + grav_acc * u_x;
            res.ay = res.ay_nograv + grav_acc * u_y;

            return(res);
        }
Example #3
0
		public SimulationState increment(DState delta, double dt)
		{
			SimulationState res = (SimulationState) MemberwiseClone();
			res.x += dt * delta.vx;
			res.y += dt * delta.vy;
			res.vx += dt * delta.ax;
			res.vy += dt * delta.ay;
			res.m += dt * delta.dm;
			if (r2 <= res.planet.Radius * res.planet.Radius)
			{
				double r = res.r;
				res.x *= res.planet.Radius / r;
				res.y *= res.planet.Radius / r;
				res.vx = res.y * (planet.rotates ? (2 * Math.PI / planet.rotationPeriod) : 0);
				res.vy = - res.x * (planet.rotates ? (2 * Math.PI / planet.rotationPeriod) : 0);
			}
			return res;
		}
Example #4
0
        public SimulationState increment(DState delta, double dt)
        {
            SimulationState res = (SimulationState)MemberwiseClone();

            res.x  += dt * delta.vx;
            res.y  += dt * delta.vy;
            res.vx += dt * delta.ax;
            res.vy += dt * delta.ay;
            res.m  += dt * delta.dm;
            if (r2 <= res.planet.Radius * res.planet.Radius)
            {
                double r = res.r;
                res.x *= res.planet.Radius / r;
                res.y *= res.planet.Radius / r;
                res.vx = res.y * (planet.rotates ? (2 * Math.PI / planet.rotationPeriod) : 0);
                res.vy = -res.x * (planet.rotates ? (2 * Math.PI / planet.rotationPeriod) : 0);
            }
            return(res);
        }
        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 newStage = 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))
                    {
                        newStage.stageParts.Add(node.part);
                    }
                }

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

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

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

                if (newStage.stageParts.Count > 0)
                {
                    newStage.stageParts.AddRange(activeEngines);
                }
            }

            // 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...
            System.Reflection.MethodInfo SortIcons = null;

            // In KSP 1.10, an extra parameter was added to SortIcons
            if (Versioning.version_major == 1 && Versioning.version_minor >= 10)
            {
                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), typeof(bool) }, null);
            }
            else
            {
                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);


#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
        }
Example #6
0
		public DState derivate()
		{
			DState res = new DState();
			res.vx = vx;
			res.vy = vy;

			double r = this.r;
			float altitude = (float) (r - planet.Radius);

			double theta = Math.Atan2(u_x, u_y);
			double thrustDirection = theta + ascentPath.FlightPathAngle(altitude);

			// gravity
			double grav_acc = -planet.gravParameter / (r * r);

			// drag
			var dragForce = StockAeroUtil.SimAeroForce(availableNodes.Keys.ToList(), planet, new UnityEngine.Vector3(0,(float)v_surf, 0), altitude);
			double drag_acc = dragForce.magnitude / m;

			//throttle
			double desiredThrust = maxAcceleration * m;
			if (maxThrust != minThrust)
				throttle = ((float)desiredThrust - minThrust) / (maxThrust - minThrust);
			else
				throttle = 1;
			throttle = Math.Max(0, Math.Min(1, throttle));

			// Effective thrust
			double F = activeEngines.Sum(e => e.thrust(throttle, pressure, machNumber, atmDensity));

			// Propellant mass variation
			res.dm = - activeEngines.Sum(e => e.evaluateFuelFlow(atmDensity, machNumber, throttle));

			res.ax_nograv = F / m * Math.Sin(thrustDirection);
			res.ay_nograv = F / m * Math.Cos(thrustDirection);
			if (v_surf != 0)
			{
				res.ax_nograv -= drag_acc * v_surf_x/v_surf;
				res.ay_nograv -= drag_acc * v_surf_y/v_surf;
			}
			res.ax = res.ax_nograv + grav_acc * u_x;
			res.ay = res.ay_nograv + grav_acc * u_y;

			return res;
		}
Example #7
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
        }