示例#1
0
        public void SimulateAeroProperties(
            out Vector3 aeroForce,
            out Vector3 aeroTorque,
            Vector3 velocityWorldVector,
            double altitude
            )
        {
            var center = new FARCenterQuery();
            var dummy  = new FARCenterQuery();

            //Calculate main gas properties
            GasProperties properties = FARAtmosphere.GetGasProperties(vessel, altitude, Planetarium.GetUniversalTime());

            if (properties.Pressure <= 0 || properties.Temperature <= 0)
            {
                aeroForce  = Vector3.zero;
                aeroTorque = Vector3.zero;
                return;
            }

            float velocityMag    = velocityWorldVector.magnitude;
            float machNumber     = (float)(velocityMag / properties.SpeedOfSound);
            float reynoldsNumber = (float)FARAeroUtil.CalculateReynoldsNumber(properties.Density,
                                                                              Length,
                                                                              velocityMag,
                                                                              machNumber,
                                                                              properties.Temperature,
                                                                              properties.AdiabaticIndex);

            float reynoldsPerLength = reynoldsNumber / (float)Length;
            float skinFriction      = (float)FARAeroUtil.SkinFrictionDrag(reynoldsNumber, machNumber);

            float pseudoKnudsenNumber = machNumber / (reynoldsNumber + machNumber);

            if (_currentAeroSections != null)
            {
                foreach (FARAeroSection curSection in _currentAeroSections)
                {
                    curSection?.PredictionCalculateAeroForces((float)properties.Density,
                                                              machNumber,
                                                              reynoldsPerLength,
                                                              pseudoKnudsenNumber,
                                                              skinFriction,
                                                              velocityWorldVector,
                                                              center);
                }

                foreach (FARWingAerodynamicModel curWing in _legacyWingModels)
                {
                    if (curWing != null)
                    {
                        center.AddForce(curWing.transform.position,
                                        curWing.PrecomputeCenterOfLift(velocityWorldVector,
                                                                       machNumber,
                                                                       properties.Density,
                                                                       dummy));
                    }
                }
            }

            aeroForce  = center.force;
            aeroTorque = center.TorqueAt(vessel.CoM);
        }
示例#2
0
        public void GetClCdCmSteady(
            InstantConditionSimInput input,
            out InstantConditionSimOutput output,
            bool clear,
            bool reset_stall = false
            )
        {
            output = new InstantConditionSimOutput();

            double area = 0;
            double MAC  = 0;
            double b_2  = 0;

            Vector3d forward = Vector3.forward;
            Vector3d up      = Vector3.up;
            Vector3d right   = Vector3.right;

            Vector3d CoM = Vector3d.zero;

            if (EditorDriver.editorFacility == EditorFacility.VAB)
            {
                forward = Vector3.up;
                up      = -Vector3.forward;
            }

            double      mass      = 0;
            List <Part> partsList = EditorLogic.SortedShipList;

            foreach (Part p in partsList)
            {
                if (FARAeroUtil.IsNonphysical(p))
                {
                    continue;
                }

                double partMass = p.mass;
                if (p.Resources.Count > 0)
                {
                    partMass += p.GetResourceMass();
                }

                // If you want to use GetModuleMass, you need to start from p.partInfo.mass, not p.mass
                CoM  += partMass * (Vector3d)p.transform.TransformPoint(p.CoMOffset);
                mass += partMass;
            }

            CoM /= mass;

            // Rodhern: The original reference directions (velocity, liftVector, sideways) did not form an orthonormal
            //  basis. That in turn produced some counterintuitive calculation results, such as coupled yaw and pitch
            //  derivatives. A more thorough discussion of the topic can be found on the KSP forums:
            //  https://forum.kerbalspaceprogram.com/index.php?/topic/19321-131-ferram-aerospace-research-v01591-liepmann-4218/&do=findComment&comment=2781270
            //  The reference directions have been replaced by new ones that are orthonormal by construction.
            //  In dkavolis branch Vector3.Cross() and Vector3d.Normalize() are used explicitly. There is no apparent
            //  benefit to this other than possibly improved readability.

            double sinAlpha = Math.Sin(input.alpha * Math.PI / 180);
            double cosAlpha = Math.Sqrt(Math.Max(1 - sinAlpha * sinAlpha, 0));

            double sinBeta = Math.Sin(input.beta * Math.PI / 180);
            double cosBeta = Math.Sqrt(Math.Max(1 - sinBeta * sinBeta, 0));

            double sinPhi = Math.Sin(input.phi * Math.PI / 180);
            double cosPhi = Math.Sqrt(Math.Max(1 - sinPhi * sinPhi, 0));

            double alphaDot = input.alphaDot * Math.PI / 180;
            double betaDot  = input.betaDot * Math.PI / 180;
            double phiDot   = input.phiDot * Math.PI / 180;

            Vector3d velocity = forward * cosAlpha * cosBeta;

            velocity += right * (sinPhi * sinAlpha * cosBeta + cosPhi * sinBeta);
            velocity += -up * (cosPhi * sinAlpha * cosBeta - sinPhi * sinBeta);
            velocity.Normalize();

            Vector3d liftDown = -forward * sinAlpha;

            liftDown += right * sinPhi * cosAlpha;
            liftDown += -up * cosPhi * cosAlpha;
            liftDown.Normalize();

            Vector3d sideways = Vector3.Cross(velocity, liftDown);

            sideways.Normalize();

            Vector3d angVel = forward * (phiDot - sinAlpha * betaDot);

            angVel += right * (cosPhi * alphaDot + cosAlpha * sinPhi * betaDot);
            angVel += up * (sinPhi * alphaDot - cosAlpha * cosPhi * betaDot);


            foreach (FARWingAerodynamicModel w in _wingAerodynamicModel)
            {
                if (!(w && w.part))
                {
                    continue;
                }

                w.ComputeForceEditor(velocity, input.machNumber, 2);

                if (clear)
                {
                    w.EditorClClear(reset_stall);
                }

                Vector3d relPos = w.GetAerodynamicCenter() - CoM;

                Vector3d vel = velocity + Vector3d.Cross(angVel, relPos);

                if (w is FARControllableSurface controllableSurface)
                {
                    controllableSurface.SetControlStateEditor(CoM,
                                                              vel,
                                                              (float)input.pitchValue,
                                                              0,
                                                              0,
                                                              input.flaps,
                                                              input.spoilers);
                }
                else if (w.isShielded)
                {
                    continue;
                }

                Vector3d force = w.ComputeForceEditor(vel.normalized, input.machNumber, 2) * 1000;

                output.Cl += -Vector3d.Dot(force, liftDown);
                output.Cy += Vector3d.Dot(force, sideways);
                output.Cd += -Vector3d.Dot(force, velocity);

                Vector3d moment = -Vector3d.Cross(relPos, force);

                output.Cm     += Vector3d.Dot(moment, sideways);
                output.Cn     += Vector3d.Dot(moment, liftDown);
                output.C_roll += Vector3d.Dot(moment, velocity);

                area += w.S;
                MAC  += w.GetMAC() * w.S;
                b_2  += w.Getb_2() * w.S;
            }

            var center = new FARCenterQuery();

            foreach (FARAeroSection aeroSection in _currentAeroSections)
            {
                aeroSection.PredictionCalculateAeroForces(2,
                                                          (float)input.machNumber,
                                                          10000,
                                                          0,
                                                          0.005f,
                                                          velocity.normalized,
                                                          center);
            }

            Vector3d centerForce = center.force * 1000;

            output.Cl += -Vector3d.Dot(centerForce, liftDown);
            output.Cy += Vector3d.Dot(centerForce, sideways);
            output.Cd += -Vector3d.Dot(centerForce, velocity);

            Vector3d centerMoment = -center.TorqueAt(CoM) * 1000;

            output.Cm     += Vector3d.Dot(centerMoment, sideways);
            output.Cn     += Vector3d.Dot(centerMoment, liftDown);
            output.C_roll += Vector3d.Dot(centerMoment, velocity);

            if (area.NearlyEqual(0))
            {
                area = _maxCrossSectionFromBody;
                b_2  = 1;
                MAC  = _bodyLength;
            }

            double recipArea = 1 / area;

            MAC           *= recipArea;
            b_2           *= recipArea;
            output.Cl     *= recipArea;
            output.Cd     *= recipArea;
            output.Cm     *= recipArea / MAC;
            output.Cy     *= recipArea;
            output.Cn     *= recipArea / b_2;
            output.C_roll *= recipArea / b_2;
        }
示例#3
0
        void UpdateAerodynamicCenter()
        {
            FARCenterQuery aeroSection, dummy;

            aeroSection = new FARCenterQuery();
            dummy       = new FARCenterQuery();

            if ((object)EditorLogic.RootPart == null)
            {
                return;
            }

            Vector3 vel_base, vel_fuzz;

            Transform rootPartTrans = EditorLogic.RootPart.partTransform;

            if (EditorDriver.editorFacility == EditorFacility.SPH)
            {
                vel_base = Vector3.forward;
                vel_fuzz = 0.02f * Vector3.up;
            }
            else
            {
                vel_base = Vector3.up;
                vel_fuzz = -0.02f * Vector3.forward;
            }

            Vector3 vel = (vel_base - vel_fuzz).normalized;

            for (int i = 0; i < _currentAeroSections.Count; i++)
            {
                FARAeroSection section = _currentAeroSections[i];
                section.PredictionCalculateAeroForces(1, 0.5f, 100000, 0, 0.005f, vel, aeroSection);
            }

            FARBaseAerodynamics.PrecomputeGlobalCenterOfLift(aeroSection, dummy, vel);

            Vector3 pos  = Vector3.zero;//rootPartTrans.position;
            float   mass = 0;

            for (int i = 0; i < EditorLogic.SortedShipList.Count; i++)
            {
                Part  p       = EditorLogic.SortedShipList[i];
                float tmpMass = p.mass + p.GetResourceMass();
                mass += tmpMass;
                pos  += p.partTransform.position * tmpMass;
            }
            pos /= mass;

            Vector3 avgForcePos = Vector3.zero;

            Vector3 force0, moment0;

            force0       = aeroSection.force;
            moment0      = aeroSection.TorqueAt(pos);
            avgForcePos += aeroSection.GetPos();

            //aeroSection.force = -aeroSection.force;
            //aeroSection.torque = -aeroSection.torque;

            aeroSection.ClearAll();

            vel = (vel_base + vel_fuzz).normalized;

            for (int i = 0; i < _currentAeroSections.Count; i++)
            {
                FARAeroSection section = _currentAeroSections[i];
                section.PredictionCalculateAeroForces(1, 0.5f, 100000, 0, 0.005f, vel, aeroSection);
            }

            FARBaseAerodynamics.PrecomputeGlobalCenterOfLift(aeroSection, dummy, vel);

            Vector3 force1, moment1;

            force1       = aeroSection.force;
            moment1      = aeroSection.TorqueAt(pos);
            avgForcePos += aeroSection.GetPos();

            aeroSection.ClearAll();

            avgForcePos *= 0.5f;

            Vector3 deltaForce  = force1 - force0;
            Vector3 deltaMoment = moment1 - moment0;

            Vector3 deltaForcePerp    = Vector3.ProjectOnPlane(deltaForce, vel_base);
            float   deltaForcePerpMag = deltaForcePerp.magnitude;

            Vector3 deltaForcePerpNorm = deltaForcePerp / deltaForcePerpMag;

            Vector3 deltaMomentPerp = deltaMoment - Vector3.Dot(deltaMoment, deltaForcePerpNorm) * deltaForcePerpNorm - Vector3.Project(deltaMoment, vel_base);

            //float dist = deltaMomentPerp.magnitude / deltaForcePerpMag;
            //vesselRootLocalAeroCenter = vel_base * dist;

            vesselRootLocalAeroCenter = deltaMomentPerp.magnitude / deltaForcePerpMag * vel_base * Math.Sign(Vector3.Dot(Vector3.Cross(deltaForce, deltaMoment), vel_base));

            //FARLogger.Info("" + dist + " " + deltaMomentPerp.magnitude + " " + deltaForcePerpMag);
            //vesselRootLocalAeroCenter += avgForcePos;
            //avgForcePos = rootPartTrans.worldToLocalMatrix.MultiplyPoint3x4(avgForcePos);
            //vesselRootLocalAeroCenter += Vector3.ProjectOnPlane(avgForcePos, Vector3.up);
            //vesselRootLocalAeroCenter = aeroSection.GetPos();
            vesselRootLocalAeroCenter += pos;//Vector3.ProjectOnPlane(avgForcePos - pos, vesselRootLocalAeroCenter) + pos;
            vesselRootLocalAeroCenter  = rootPartTrans.worldToLocalMatrix.MultiplyPoint3x4(vesselRootLocalAeroCenter);
        }
        public void GetClCdCmSteady(InstantConditionSimInput input, out InstantConditionSimOutput output, bool clear, bool reset_stall = false)
        {
            output = new InstantConditionSimOutput();

            double area = 0;
            double MAC  = 0;
            double b_2  = 0;

            Vector3d forward = Vector3.forward;
            Vector3d up      = Vector3.up;
            Vector3d right   = Vector3.right;

            Vector3d    CoM       = Vector3d.zero;
            double      mass      = 0;
            List <Part> partsList = EditorLogic.SortedShipList;

            for (int i = 0; i < partsList.Count; i++)
            {
                Part p = partsList[i];

                if (FARAeroUtil.IsNonphysical(p))
                {
                    continue;
                }

                double partMass = p.mass;
                if (p.Resources.Count > 0)
                {
                    partMass += p.GetResourceMass();
                }

                //partMass += p.GetModuleMass(p.mass);
                // If you want to use GetModuleMass, you need to start from p.partInfo.mass, not p.mass
                CoM  += partMass * (Vector3d)p.transform.TransformPoint(p.CoMOffset);
                mass += partMass;
            }
            CoM /= mass;

            if (EditorDriver.editorFacility == EditorFacility.VAB)
            {
                forward = Vector3.up;
                up      = -Vector3.forward;
            }

            double sinAlpha = Math.Sin(input.alpha * Math.PI / 180);
            double cosAlpha = Math.Sqrt(Math.Max(1 - sinAlpha * sinAlpha, 0));

            double sinBeta = Math.Sin(input.beta * Math.PI / 180);
            double cosBeta = Math.Sqrt(Math.Max(1 - sinBeta * sinBeta, 0));

            double sinPhi = Math.Sin(input.phi * Math.PI / 180);
            double cosPhi = Math.Sqrt(Math.Max(1 - sinPhi * sinPhi, 0));

            double alphaDot = input.alphaDot * Math.PI / 180;
            double betaDot  = input.betaDot * Math.PI / 180;
            double phiDot   = input.phiDot * Math.PI / 180;

            Vector3d AngVel = (phiDot - sinAlpha * betaDot) * forward;

            AngVel += (cosPhi * alphaDot + cosAlpha * sinPhi * betaDot) * right;
            AngVel += (sinPhi * alphaDot - cosAlpha * cosPhi * betaDot) * up;

            Vector3d velocity = forward * cosAlpha * cosBeta;

            velocity += right * (sinPhi * cosAlpha * cosBeta + cosPhi * sinBeta);
            velocity += -up * cosPhi * (sinAlpha * cosBeta + sinBeta);

            velocity.Normalize();

            //this is negative wrt the ground
            Vector3d liftVector = -forward * sinAlpha + right * sinPhi * cosAlpha - up * cosPhi * cosAlpha;

            Vector3d sideways = Vector3.Cross(velocity, liftVector).normalized;


            for (int i = 0; i < _wingAerodynamicModel.Count; i++)
            {
                FARWingAerodynamicModel w = _wingAerodynamicModel[i];
                if (!(w && w.part))
                {
                    continue;
                }

                w.ComputeForceEditor(velocity.normalized, input.machNumber, 2);

                if (clear)
                {
                    w.EditorClClear(reset_stall);
                }

                Vector3d relPos = w.GetAerodynamicCenter() - CoM;

                Vector3d vel = velocity + Vector3d.Cross(AngVel, relPos);

                if (w is FARControllableSurface)
                {
                    (w as FARControllableSurface).SetControlStateEditor(CoM, vel, (float)input.pitchValue, 0, 0, input.flaps, input.spoilers);
                }
                else if (w.isShielded)
                {
                    continue;
                }


                //w.ComputeForceEditor(velocity, input.machNumber);     //do this just to get the AC right

                Vector3d force = w.ComputeForceEditor(vel.normalized, input.machNumber, 2) * 1000;

                output.Cl += -Vector3d.Dot(force, liftVector);
                output.Cy += Vector3d.Dot(force, sideways);
                output.Cd += -Vector3d.Dot(force, velocity);

                Vector3d moment = -Vector3d.Cross(relPos, force);

                output.Cm     += Vector3d.Dot(moment, sideways);
                output.Cn     += Vector3d.Dot(moment, liftVector);
                output.C_roll += Vector3d.Dot(moment, velocity);

                //w.ComputeClCdEditor(vel.normalized, input.machNumber);

                /*double tmpCl = w.GetCl() * w.S;
                 * output.Cl += tmpCl * -Vector3d.Dot(w.GetLiftDirection(), liftVector);
                 * output.Cy += tmpCl * -Vector3d.Dot(w.GetLiftDirection(), sideways);
                 * double tmpCd = w.GetCd() * w.S;
                 * output.Cd += tmpCd;
                 * output.Cm += tmpCl * Vector3d.Dot((relPos), velocity) * -Vector3d.Dot(w.GetLiftDirection(), liftVector) + tmpCd * -Vector3d.Dot((relPos), liftVector);
                 * output.Cn += tmpCd * Vector3d.Dot((relPos), sideways) + tmpCl * Vector3d.Dot((relPos), velocity) * -Vector3d.Dot(w.GetLiftDirection(), sideways);
                 * output.C_roll += tmpCl * Vector3d.Dot((relPos), sideways) * -Vector3d.Dot(w.GetLiftDirection(), liftVector);*/
                area += w.S;
                MAC  += w.GetMAC() * w.S;
                b_2  += w.Getb_2() * w.S;
            }
            FARCenterQuery center = new FARCenterQuery();

            for (int i = 0; i < _currentAeroSections.Count; i++)
            {
                _currentAeroSections[i].PredictionCalculateAeroForces(2, (float)input.machNumber, 10000, 0.005f, velocity.normalized, center);
            }

            Vector3d centerForce = center.force * 1000;

            output.Cl += -Vector3d.Dot(centerForce, liftVector);
            output.Cy += Vector3d.Dot(centerForce, sideways);
            output.Cd += -Vector3d.Dot(centerForce, velocity);

            Vector3d centerMoment = -center.TorqueAt(CoM) * 1000;

            output.Cm     += Vector3d.Dot(centerMoment, sideways);
            output.Cn     += Vector3d.Dot(centerMoment, liftVector);
            output.C_roll += Vector3d.Dot(centerMoment, velocity);


            /*for (int i = 0; i < FARAeroUtil.CurEditorParts.Count; i++)
             * {
             *  Part p = FARAeroUtil.CurEditorParts[i];
             *  if (FARAeroUtil.IsNonphysical(p))
             *      continue;
             *
             *  Vector3 part_pos = p.transform.TransformPoint(p.CoMOffset) - CoM;
             *  double partMass = p.mass;
             *  if (p.Resources.Count > 0)
             *      partMass += p.GetResourceMass();
             *
             *  double stock_drag = partMass * p.maximum_drag * FlightGlobals.DragMultiplier * 1000;
             *  output.Cd += stock_drag;
             *  output.Cm += stock_drag * -Vector3d.Dot(part_pos, liftVector);
             *  output.Cn += stock_drag * Vector3d.Dot(part_pos, sideways);
             * }*/

            if (area == 0)
            {
                area = _maxCrossSectionFromBody;
                b_2  = 1;
                MAC  = _bodyLength;
            }

            double recipArea = 1 / area;

            MAC           *= recipArea;
            b_2           *= recipArea;
            output.Cl     *= recipArea;
            output.Cd     *= recipArea;
            output.Cm     *= recipArea / MAC;
            output.Cy     *= recipArea;
            output.Cn     *= recipArea / b_2;
            output.C_roll *= recipArea / b_2;
        }
        private void UpdateAerodynamicCenter()
        {
            var aeroSection = new FARCenterQuery();
            var dummy       = new FARCenterQuery();

            if (EditorLogic.RootPart is null)
            {
                return;
            }

            Vector3 vel_base, vel_fuzz;

            Transform rootPartTrans = EditorLogic.RootPart.partTransform;

            if (EditorDriver.editorFacility == EditorFacility.SPH)
            {
                vel_base = Vector3.forward;
                vel_fuzz = 0.02f * Vector3.up;
            }
            else
            {
                vel_base = Vector3.up;
                vel_fuzz = -0.02f * Vector3.forward;
            }

            Vector3 vel = (vel_base - vel_fuzz).normalized;

            foreach (FARAeroSection section in _currentAeroSections)
            {
                section.PredictionCalculateAeroForces(1, 0.5f, 100000, 0, 0.005f, vel, aeroSection);
            }

            FARBaseAerodynamics.PrecomputeGlobalCenterOfLift(aeroSection, dummy, vel, 1);

            Vector3 pos  = Vector3.zero;
            float   mass = 0;

            foreach (Part p in EditorLogic.SortedShipList)
            {
                float tmpMass = p.mass + p.GetResourceMass();
                mass += tmpMass;
                pos  += p.partTransform.position * tmpMass;
            }

            pos /= mass;

            Vector3 force0  = aeroSection.force;
            Vector3 moment0 = aeroSection.TorqueAt(pos);

            aeroSection.ClearAll();

            vel = (vel_base + vel_fuzz).normalized;

            foreach (FARAeroSection section in _currentAeroSections)
            {
                section.PredictionCalculateAeroForces(1, 0.5f, 100000, 0, 0.005f, vel, aeroSection);
            }

            FARBaseAerodynamics.PrecomputeGlobalCenterOfLift(aeroSection, dummy, vel, 1);

            Vector3 force1  = aeroSection.force;
            Vector3 moment1 = aeroSection.TorqueAt(pos);

            aeroSection.ClearAll();

            Vector3 deltaForce  = force1 - force0;
            Vector3 deltaMoment = moment1 - moment0;

            Vector3 deltaForcePerp    = Vector3.ProjectOnPlane(deltaForce, vel_base);
            float   deltaForcePerpMag = deltaForcePerp.magnitude;

            Vector3 deltaForcePerpNorm = deltaForcePerp / deltaForcePerpMag;

            Vector3 deltaMomentPerp = deltaMoment -
                                      Vector3.Dot(deltaMoment, deltaForcePerpNorm) * deltaForcePerpNorm -
                                      Vector3.Project(deltaMoment, vel_base);

            vesselRootLocalAeroCenter = deltaMomentPerp.magnitude /
                                        deltaForcePerpMag *
                                        Math.Sign(Vector3.Dot(Vector3.Cross(deltaForce, deltaMoment), vel_base)) *
                                        vel_base;

            vesselRootLocalAeroCenter += pos;
            vesselRootLocalAeroCenter  = rootPartTrans.worldToLocalMatrix.MultiplyPoint3x4(vesselRootLocalAeroCenter);
        }
示例#6
0
        public void SimulateAeroProperties(
            out Vector3 aeroForce,
            out Vector3 aeroTorque,
            Vector3 velocityWorldVector,
            double altitude
            )
        {
            var center = new FARCenterQuery();
            var dummy  = new FARCenterQuery();

            CelestialBody body         = vessel.mainBody; //Calculate main gas properties
            float         pressure     = (float)body.GetPressure(altitude);
            float         temperature  = (float)body.GetTemperature(altitude);
            float         density      = (float)body.GetDensity(pressure, temperature);
            float         speedOfSound = (float)body.GetSpeedOfSound(pressure, density);

            if (pressure <= 0 || temperature <= 0 || density <= 0 || speedOfSound <= 0)
            {
                aeroForce  = Vector3.zero;
                aeroTorque = Vector3.zero;
                return;
            }

            float velocityMag    = velocityWorldVector.magnitude;
            float machNumber     = velocityMag / speedOfSound;
            float reynoldsNumber =
                (float)FARAeroUtil.CalculateReynoldsNumber(density,
                                                           Length,
                                                           velocityMag,
                                                           machNumber,
                                                           temperature,
                                                           body.atmosphereAdiabaticIndex);

            float reynoldsPerLength = reynoldsNumber / (float)Length;
            float skinFriction      = (float)FARAeroUtil.SkinFrictionDrag(reynoldsNumber, machNumber);

            float pseudoKnudsenNumber = machNumber / (reynoldsNumber + machNumber);

            if (_currentAeroSections != null)
            {
                foreach (FARAeroSection curSection in _currentAeroSections)
                {
                    curSection?.PredictionCalculateAeroForces(density,
                                                              machNumber,
                                                              reynoldsPerLength,
                                                              pseudoKnudsenNumber,
                                                              skinFriction,
                                                              velocityWorldVector,
                                                              center);
                }

                foreach (FARWingAerodynamicModel curWing in _legacyWingModels)
                {
                    if (!(curWing is null))
                    {
                        center.AddForce(curWing.transform.position,
                                        curWing.PrecomputeCenterOfLift(velocityWorldVector,
                                                                       machNumber,
                                                                       density,
                                                                       dummy));
                    }
                }
            }

            aeroForce  = center.force;
            aeroTorque = center.TorqueAt(vessel.CoM);
        }