public void UpdateSonicDragArea()
        {
            ferram4.FARCenterQuery center = new ferram4.FARCenterQuery();

            Vector3 worldMainAxis = _localToWorldMatrix.MultiplyVector(_vehicleMainAxis);
            worldMainAxis.Normalize();

            for (int i = 0; i < _newAeroSections.Count; i++)
            {
                FARAeroSection a = _newAeroSections[i];
                a.PredictionCalculateAeroForces(2f, 1f, 50000f, 0.005f, worldMainAxis, center);
            }

            _sonicDragArea = Vector3.Dot(center.force, worldMainAxis) * -1000;
        }
        public void PredictionCalculateAeroForces(float atmDensity, float machNumber, float reynoldsPerUnitLength, float pseudoKnudsenNumber, float skinFrictionDrag, Vector3 vel, ferram4.FARCenterQuery center)
        {
            if (partData.Count == 0)
            {
                return;
            }

            PartData          data       = partData[0];
            FARAeroPartModule aeroModule = null;

            for (int i = 0; i < partData.Count; i++)
            {
                data       = partData[i];
                aeroModule = data.aeroModule;
                if (aeroModule.part == null || aeroModule.part.partTransform == null)
                {
                    continue;
                }
                break;
            }
            if (aeroModule.part == null || aeroModule.part.transform == null)
            {
                return;
            }
            double skinFrictionForce = skinFrictionDrag * xForceSkinFriction.Evaluate(machNumber);      //this will be the same for each part, so why recalc it multiple times?
            double xForceAoA0        = xForcePressureAoA0.Evaluate(machNumber);
            double xForceAoA180      = xForcePressureAoA180.Evaluate(machNumber);

            Vector3 xRefVector = data.xRefVectorPartSpace;
            Vector3 nRefVector = data.nRefVectorPartSpace;

            Vector3 velLocal = aeroModule.part.partTransform.worldToLocalMatrix.MultiplyVector(vel);

            //Vector3 angVelLocal = aeroModule.partLocalAngVel;

            //velLocal += Vector3.Cross(angVelLocal, data.centroidPartSpace);       //some transform issue here, needs investigation
            Vector3 velLocalNorm = velLocal.normalized;

            Vector3 localNormalForceVec = Vector3.ProjectOnPlane(-velLocalNorm, xRefVector).normalized;

            double cosAoA     = Vector3.Dot(xRefVector, velLocalNorm);
            double cosSqrAoA  = cosAoA * cosAoA;
            double sinSqrAoA  = Math.Max(1 - cosSqrAoA, 0);
            double sinAoA     = Math.Sqrt(sinSqrAoA);
            double sin2AoA    = 2 * sinAoA * Math.Abs(cosAoA);
            double cosHalfAoA = Math.Sqrt(0.5 + 0.5 * Math.Abs(cosAoA));


            double nForce = 0;

            nForce = cosHalfAoA * sin2AoA * potentialFlowNormalForce * Math.Sign(cosAoA); //potential flow normal force
            if (nForce < 0)                                                               //potential flow is not significant over the rear face of things
            {
                nForce = 0;
            }
            //if (machNumber > 3)
            //    nForce *= 2d - machNumber * 0.3333333333333333d;

            float normalForceFactor = Math.Abs(Vector3.Dot(localNormalForceVec, nRefVector));

            normalForceFactor *= normalForceFactor;

            normalForceFactor = invFlatnessRatio * (1 - normalForceFactor) + flatnessRatio * normalForceFactor;     //accounts for changes in relative flatness of shape


            float crossFlowMach, crossFlowReynolds;

            crossFlowMach     = machNumber * (float)sinAoA;
            crossFlowReynolds = reynoldsPerUnitLength * diameter * normalForceFactor * (float)sinAoA;

            nForce += viscCrossflowDrag * sinSqrAoA * CalculateCrossFlowDrag(crossFlowMach, crossFlowReynolds);            //viscous crossflow normal force

            nForce *= normalForceFactor;

            double xForce = -skinFrictionForce *Math.Sign(cosAoA) * cosSqrAoA;

            double localVelForce = xForce * pseudoKnudsenNumber;

            xForce -= localVelForce;

            localVelForce = Math.Abs(localVelForce);

            float moment = (float)(cosAoA * sinAoA);


            if (cosAoA > 0)
            {
                xForce += cosSqrAoA * xForceAoA0;
                float momentFactor;
                if (machNumber > 6)
                {
                    momentFactor = hypersonicMomentForward;
                }
                else if (machNumber < 0.6)
                {
                    momentFactor = 0.6f * hypersonicMomentBackward;
                }
                else
                {
                    float tmp = (-0.185185185f * machNumber + 1.11111111111f);
                    momentFactor = tmp * hypersonicMomentBackward * 0.6f + (1 - tmp) * hypersonicMomentForward;
                }
                //if (machNumber < 1.5)
                //    momentFactor += hypersonicMomentBackward * (0.5f - machNumber * 0.33333333333333333333333333333333f) * 0.2f;

                moment *= momentFactor;
            }
            else
            {
                xForce += cosSqrAoA * xForceAoA180;
                float momentFactor;     //negative to deal with the ref vector facing the opposite direction, causing the moment vector to point in the opposite direction
                if (machNumber > 6)
                {
                    momentFactor = hypersonicMomentBackward;
                }
                else if (machNumber < 0.6)
                {
                    momentFactor = 0.6f * hypersonicMomentForward;
                }
                else
                {
                    float tmp = (-0.185185185f * machNumber + 1.11111111111f);
                    momentFactor = tmp * hypersonicMomentForward * 0.6f + (1 - tmp) * hypersonicMomentBackward;
                }
                //if (machNumber < 1.5)
                //    momentFactor += hypersonicMomentForward * (0.5f - machNumber * 0.33333333333333333333333333333333f) * 0.2f;

                moment *= momentFactor;
            }
            moment /= normalForceFactor;

            Vector3 forceVector = (float)xForce * xRefVector + (float)nForce * localNormalForceVec;

            forceVector -= (float)localVelForce * velLocalNorm;
            Vector3 torqueVector = Vector3.Cross(xRefVector, localNormalForceVec) * moment;

            Matrix4x4 localToWorld = aeroModule.part.partTransform.localToWorldMatrix;

            float dynPresAndScaling = 0.0005f * atmDensity * velLocal.sqrMagnitude;        //dyn pres and N -> kN conversion

            forceVector  *= dynPresAndScaling;
            torqueVector *= dynPresAndScaling;

            forceVector  = localToWorld.MultiplyVector(forceVector);
            torqueVector = localToWorld.MultiplyVector(torqueVector);
            Vector3 centroid = Vector3.zero;

            for (int i = 0; i < partData.Count; i++)
            {
                PartData          data2  = partData[i];
                FARAeroPartModule module = data2.aeroModule;
                if ((object)module == null)
                {
                    continue;
                }

                if (module.part == null || module.part.partTransform == null)
                {
                    continue;
                }

                centroid = module.part.partTransform.localToWorldMatrix.MultiplyPoint3x4(data2.centroidPartSpace);
                center.AddForce(centroid, forceVector * data2.dragFactor);
            }
            center.AddTorque(torqueVector);
        }
        private void CalculateVesselAeroProperties()
        {
            int front, back, numSections;

            _voxel.CrossSectionData(_vehicleCrossSection, _vehicleMainAxis, out front, out back, out _sectionThickness, out _maxCrossSectionArea);

            numSections = back - front;
            _length = _sectionThickness * numSections;

            AdjustCrossSectionForAirDucting(_vehicleCrossSection, _currentGeoModules, front, back, ref _maxCrossSectionArea);

            GaussianSmoothCrossSections(_vehicleCrossSection, 3, FARSettingsScenarioModule.Settings.gaussianVehicleLengthFractionForSmoothing, _sectionThickness, _length, front, back, FARSettingsScenarioModule.Settings.numAreaSmoothingPasses, FARSettingsScenarioModule.Settings.numDerivSmoothingPasses);

            CalculateSonicPressure(_vehicleCrossSection, front, back, _sectionThickness, _maxCrossSectionArea);

            validSectionCount = numSections;
            firstSection = front;
            double invMaxRadFactor = 1f / Math.Sqrt(_maxCrossSectionArea / Math.PI);

            double finenessRatio = _sectionThickness * numSections * 0.5 * invMaxRadFactor;       //vehicle length / max diameter, as calculated from sect thickness * num sections / (2 * max radius)

            //skin friction and pressure drag for a body, taken from 1978 USAF Stability And Control DATCOM, Section 4.2.3.1, Paragraph A
            double viscousDragFactor = 0;
            viscousDragFactor = 60 / (finenessRatio * finenessRatio * finenessRatio) + 0.0025 * finenessRatio;     //pressure drag for a subsonic / transonic body due to skin friction
            viscousDragFactor++;

            viscousDragFactor /= (double)numSections;   //fraction of viscous drag applied to each section

            double criticalMachNumber = CalculateCriticalMachNumber(finenessRatio);

            _criticalMach = criticalMachNumber * CriticalMachFactorForUnsmoothCrossSection(_vehicleCrossSection, finenessRatio, _sectionThickness);

            float lowFinenessRatioSubsonicFactor = 1f;
            lowFinenessRatioSubsonicFactor += 1f/(2f * (float)finenessRatio);

            _moduleAndAreas.Clear();
            //_newAeroSections = new List<FARAeroSection>();

            HashSet<FARAeroPartModule> tmpAeroModules = new HashSet<FARAeroPartModule>();
            _sonicDragArea = 0;

            if (_newAeroSections.Capacity < numSections)
                _newAeroSections.Capacity = numSections;

            for (int i = 0; i <= numSections; i++)  //index in the cross sections
            {
                int index = i + front;      //index along the actual body

                double prevArea, curArea, nextArea;

                curArea = _vehicleCrossSection[index].area;
                if (i == 0)
                    prevArea = 0;
                else
                    prevArea = _vehicleCrossSection[index - 1].area;
                if (i == numSections)
                    nextArea = 0;
                else
                    nextArea = _vehicleCrossSection[index + 1].area;

                FARAeroSection currentSection = null;

                if (i < _newAeroSections.Count)
                    currentSection = _newAeroSections[i];
                else
                {
                    lock (_commonLocker)
                        if (currentlyUnusedSections.Count > 0)
                            currentSection = currentlyUnusedSections.Pop();
                }

                if (currentSection == null)
                    currentSection = new FARAeroSection();

                FARFloatCurve xForcePressureAoA0 = currentSection.xForcePressureAoA0;
                FARFloatCurve xForcePressureAoA180 = currentSection.xForcePressureAoA180;
                FARFloatCurve xForceSkinFriction = currentSection.xForceSkinFriction;

                //Potential and Viscous lift calcs
                float potentialFlowNormalForce;
                if(i == 0)
                    potentialFlowNormalForce = (float)(nextArea - curArea);
                else if(i == numSections)
                    potentialFlowNormalForce = (float)(curArea - prevArea);
                else
                    potentialFlowNormalForce = (float)(nextArea - prevArea) * 0.5f;      //calcualted from area change

                float areaChangeMax = (float)Math.Min(Math.Min(nextArea, prevArea) * 0.1, _length * 0.01);

                float sonicBaseDrag = 0.21f;

                sonicBaseDrag *= potentialFlowNormalForce;    //area base drag acts over

                if (potentialFlowNormalForce > areaChangeMax)
                    potentialFlowNormalForce = areaChangeMax;
                else if (potentialFlowNormalForce < -areaChangeMax)
                    potentialFlowNormalForce = -areaChangeMax;
                else
                    if(areaChangeMax != 0)
                        sonicBaseDrag *= Math.Abs(potentialFlowNormalForce / areaChangeMax);      //some scaling for small changes in cross-section

                double flatnessRatio = _vehicleCrossSection[index].flatnessRatio;
                if (flatnessRatio >= 1)
                    sonicBaseDrag /= (float)(flatnessRatio * flatnessRatio);
                else
                    sonicBaseDrag *= (float)(flatnessRatio * flatnessRatio);

                //float sonicWaveDrag = (float)CalculateTransonicWaveDrag(i, index, numSections, front, _sectionThickness, Math.Min(_maxCrossSectionArea * 2, curArea * 16));//Math.Min(maxCrossSectionArea * 0.1, curArea * 0.25));
                //sonicWaveDrag *= (float)FARSettingsScenarioModule.Settings.fractionTransonicDrag;     //this is just to account for the higher drag being felt due to the inherent blockiness of the model being used and noise introduced by the limited control over shape and through voxelization
                float hypersonicDragForward = (float)CalculateHypersonicDrag(prevArea, curArea, _sectionThickness);     //negative forces
                float hypersonicDragBackward = (float)CalculateHypersonicDrag(nextArea, curArea, _sectionThickness);

                float hypersonicDragForwardFrac = 0, hypersonicDragBackwardFrac = 0;

                if(curArea - prevArea != 0)
                    hypersonicDragForwardFrac = Math.Abs(hypersonicDragForward * 0.5f / (float)(curArea - prevArea));
                if(curArea - nextArea != 0)
                    hypersonicDragBackwardFrac = Math.Abs(hypersonicDragBackward * 0.5f / (float)(curArea - nextArea));

                hypersonicDragForwardFrac *= hypersonicDragForwardFrac;
                hypersonicDragForwardFrac *= hypersonicDragForwardFrac;

                hypersonicDragBackwardFrac *= hypersonicDragBackwardFrac;
                hypersonicDragBackwardFrac *= hypersonicDragBackwardFrac;

                if (flatnessRatio >= 1)
                {
                    hypersonicDragForwardFrac /= (float)(flatnessRatio * flatnessRatio);
                    hypersonicDragBackwardFrac /= (float)(flatnessRatio * flatnessRatio);
                }
                else
                {
                    hypersonicDragForwardFrac *= (float)(flatnessRatio * flatnessRatio);
                    hypersonicDragBackwardFrac *= (float)(flatnessRatio * flatnessRatio);
                }

                float hypersonicMomentForward = (float)CalculateHypersonicMoment(prevArea, curArea, _sectionThickness);
                float hypersonicMomentBackward = (float)CalculateHypersonicMoment(nextArea, curArea, _sectionThickness);

                xForcePressureAoA0.SetPoint(5, new Vector3d(35, hypersonicDragForward, 0));
                xForcePressureAoA180.SetPoint(5, new Vector3d(35, -hypersonicDragBackward, 0));

                float sonicAoA0Drag, sonicAoA180Drag;

                double cPSonicForward, cPSonicBackward;

                cPSonicForward = _vehicleCrossSection[index].cpSonicForward;
                if (index > front)
                {
                    cPSonicForward += _vehicleCrossSection[index - 1].cpSonicForward;
                    cPSonicForward *= 0.5;
                }

                cPSonicBackward = _vehicleCrossSection[index].cpSonicBackward;
                if (index < back)
                {
                    cPSonicBackward += _vehicleCrossSection[index + 1].cpSonicBackward;
                    cPSonicBackward *= 0.5;
                }

                if (sonicBaseDrag > 0)      //occurs with increase in area; force applied at 180 AoA
                {
                    xForcePressureAoA0.SetPoint(0, new Vector3d(_criticalMach, (0.325f * hypersonicDragForward * hypersonicDragForwardFrac) * lowFinenessRatioSubsonicFactor, 0));    //hypersonic drag used as a proxy for effects due to flow separation
                    xForcePressureAoA180.SetPoint(0, new Vector3d(_criticalMach, (sonicBaseDrag * 0.2f - (0.325f * hypersonicDragBackward * hypersonicDragBackwardFrac)) * lowFinenessRatioSubsonicFactor, 0));

                    hypersonicDragBackwardFrac += 1f;       //avg fracs with 1 to get intermediate frac
                    hypersonicDragBackwardFrac *= 0.5f;

                    hypersonicDragForwardFrac += 1f;
                    hypersonicDragForwardFrac *= 0.5f;

                    sonicAoA0Drag = -(float)(cPSonicForward * (curArea - prevArea)) + hypersonicDragForward * 0.3f * hypersonicDragForwardFrac;
                    sonicAoA180Drag = (float)(cPSonicBackward * (curArea - nextArea)) + sonicBaseDrag - hypersonicDragBackward * 0.3f * hypersonicDragBackwardFrac;
                }
                else if (sonicBaseDrag < 0)
                {
                    xForcePressureAoA0.SetPoint(0, new Vector3d(_criticalMach, (sonicBaseDrag * 0.2f + (0.3f * hypersonicDragForward * hypersonicDragForwardFrac)) * lowFinenessRatioSubsonicFactor, 0));
                    xForcePressureAoA180.SetPoint(0, new Vector3d(_criticalMach, -(0.3f * hypersonicDragBackward * hypersonicDragBackwardFrac) * lowFinenessRatioSubsonicFactor, 0));

                    hypersonicDragBackwardFrac += 1f;       //avg fracs with 1 to get intermediate frac
                    hypersonicDragBackwardFrac *= 0.5f;

                    hypersonicDragForwardFrac += 1f;
                    hypersonicDragForwardFrac *= 0.5f;

                    sonicAoA0Drag = -(float)(cPSonicForward * (curArea - prevArea)) + sonicBaseDrag + hypersonicDragForward * 0.3f * hypersonicDragForwardFrac;
                    sonicAoA180Drag = (float)(cPSonicBackward * (curArea - nextArea)) - hypersonicDragBackward * 0.3f * hypersonicDragBackwardFrac;

                }
                else
                {
                    xForcePressureAoA0.SetPoint(0, new Vector3d(_criticalMach, (0.3f * hypersonicDragForward * hypersonicDragForwardFrac) * lowFinenessRatioSubsonicFactor, 0));
                    xForcePressureAoA180.SetPoint(0, new Vector3d(_criticalMach, -(0.3f * hypersonicDragBackward * hypersonicDragBackwardFrac) * lowFinenessRatioSubsonicFactor, 0));

                    hypersonicDragBackwardFrac += 1f;       //avg fracs with 1 to get intermediate frac
                    hypersonicDragBackwardFrac *= 0.5f;

                    hypersonicDragForwardFrac += 1f;
                    hypersonicDragForwardFrac *= 0.5f;

                    sonicAoA0Drag = -(float)(cPSonicForward * (curArea - prevArea)) + hypersonicDragForward * 0.3f * hypersonicDragForwardFrac;
                    sonicAoA180Drag = (float)(cPSonicBackward * (curArea - nextArea)) - hypersonicDragBackward * 0.3f * hypersonicDragBackwardFrac;
                }
                float diffSonicHyperAoA0 = Math.Abs(sonicAoA0Drag) - Math.Abs(hypersonicDragForward);
                float diffSonicHyperAoA180 = Math.Abs(sonicAoA180Drag) - Math.Abs(hypersonicDragBackward);

                xForcePressureAoA0.SetPoint(1, new Vector3d(1f, sonicAoA0Drag, 0));
                xForcePressureAoA180.SetPoint(1, new Vector3d(1f, sonicAoA180Drag, 0));

                xForcePressureAoA0.SetPoint(2, new Vector3d(2f, sonicAoA0Drag * 0.5773503f + (1 - 0.5773503f) * hypersonicDragForward, -0.2735292 * diffSonicHyperAoA0));            //need to recalc slope here
                xForcePressureAoA180.SetPoint(2, new Vector3d(2f, sonicAoA180Drag * 0.5773503f - (1 - 0.5773503f) * hypersonicDragBackward, -0.2735292 * diffSonicHyperAoA180));

                xForcePressureAoA0.SetPoint(3, new Vector3d(5f, sonicAoA0Drag * 0.2041242f + (1 - 0.2041242f) * hypersonicDragForward, -0.04252587f * diffSonicHyperAoA0));
                xForcePressureAoA180.SetPoint(3, new Vector3d(5f, sonicAoA180Drag * 0.2041242f - (1 - 0.2041242f) * hypersonicDragBackward, -0.04252587f * diffSonicHyperAoA180));

                xForcePressureAoA0.SetPoint(4, new Vector3d(10f, sonicAoA0Drag * 0.1005038f + (1 - 0.1005038f) * hypersonicDragForward, -0.0101519f * diffSonicHyperAoA0));
                xForcePressureAoA180.SetPoint(4, new Vector3d(10f, sonicAoA180Drag * 0.1005038f - (1 - 0.1005038f) * hypersonicDragBackward, -0.0101519f * diffSonicHyperAoA180));

                Vector3 xRefVector;
                if (index == front || index == back)
                    xRefVector = _vehicleMainAxis;
                else
                {
                    xRefVector = (Vector3)(_vehicleCrossSection[index - 1].centroid - _vehicleCrossSection[index + 1].centroid);
                    Vector3 offMainAxisVec = Vector3.ProjectOnPlane(xRefVector, _vehicleMainAxis);
                    float tanAoA = offMainAxisVec.magnitude / (2f * (float)_sectionThickness);
                    if (tanAoA > 0.17632698070846497347109038686862f)
                    {
                        offMainAxisVec.Normalize();
                        offMainAxisVec *= 0.17632698070846497347109038686862f;      //max acceptable is 10 degrees
                        xRefVector = _vehicleMainAxis + offMainAxisVec;
                    }
                    xRefVector.Normalize();
                }

                Vector3 nRefVector = Matrix4x4.TRS(Vector3.zero, Quaternion.FromToRotation(_vehicleMainAxis, xRefVector), Vector3.one).MultiplyVector(_vehicleCrossSection[index].flatNormalVector);

                Vector3 centroid = _localToWorldMatrix.MultiplyPoint3x4(_vehicleCrossSection[index].centroid);
                xRefVector = _localToWorldMatrix.MultiplyVector(xRefVector);
                nRefVector = _localToWorldMatrix.MultiplyVector(nRefVector);

                Dictionary<Part, VoxelCrossSection.SideAreaValues> includedPartsAndAreas = _vehicleCrossSection[index].partSideAreaValues;

                float weightingFactor = 0;

                double surfaceArea = 0;
                foreach (KeyValuePair<Part, VoxelCrossSection.SideAreaValues> pair in includedPartsAndAreas)
                {
                    VoxelCrossSection.SideAreaValues areas = pair.Value;
                    surfaceArea += areas.iN + areas.iP + areas.jN + areas.jP + areas.kN + areas.kP;

                    Part key = pair.Key;
                    if (key == null)
                        continue;

                    if (!key.Modules.Contains("FARAeroPartModule"))
                        continue;

                    FARAeroPartModule m = (FARAeroPartModule)key.Modules["FARAeroPartModule"];
                    if ((object)m != null)
                        includedModules.Add(m);

                    if (_moduleAndAreas.ContainsKey(m))
                        _moduleAndAreas[m] += areas;
                    else
                        _moduleAndAreas[m] = areas;

                    weightingFactor += (float)pair.Value.exposedAreaCount;
                    weighting.Add((float)pair.Value.exposedAreaCount);
                }

                weightingFactor = 1 / weightingFactor;
                for (int j = 0; j < includedPartsAndAreas.Count; j++)
                {
                    weighting[j] *= weightingFactor;
                }

                float viscCrossflowDrag = (float)(Math.Sqrt(curArea / Math.PI) * _sectionThickness * 2d);

                xForceSkinFriction.SetPoint(0, new Vector3d(0, (surfaceArea * viscousDragFactor), 0));   //subsonic incomp visc drag
                xForceSkinFriction.SetPoint(1, new Vector3d(1, (surfaceArea * viscousDragFactor), 0));   //transonic visc drag
                xForceSkinFriction.SetPoint(2, new Vector3d(2, (float)surfaceArea, 0));                     //above Mach 1.4, visc is purely surface drag, no pressure-related components simulated

                currentSection.UpdateAeroSection(potentialFlowNormalForce, viscCrossflowDrag
                    ,viscCrossflowDrag / (float)(_sectionThickness), (float)flatnessRatio, hypersonicMomentForward, hypersonicMomentBackward,
                    centroid, xRefVector, nRefVector, _localToWorldMatrix, _vehicleMainAxis, includedModules, weighting, _partWorldToLocalMatrix);

                if (i < _newAeroSections.Count)
                    _newAeroSections[i] = currentSection;
                else
                    _newAeroSections.Add(currentSection);

                for (int j = 0; j < includedModules.Count; j++)
                {
                    FARAeroPartModule a = includedModules[j];
                    tmpAeroModules.Add(a);
                }

                includedModules.Clear();
                weighting.Clear();

            }
            if(_newAeroSections.Count > numSections)        //deal with sections that are unneeded now
            {
                lock (_commonLocker)
                    for (int i = _newAeroSections.Count - 1; i > numSections; --i)
                    {
                        FARAeroSection unusedSection = _newAeroSections[i];
                        _newAeroSections.RemoveAt(i);

                        if (currentlyUnusedSections.Count < 64)
                            currentlyUnusedSections.Push(unusedSection);        //if there aren't that many extra ones stored, add them to the stack to be reused
                        else
                        {
                            unusedSection.ClearAeroSection();
                            unusedSection = null;
                        }
                    }
            }
            foreach (KeyValuePair<FARAeroPartModule, FARAeroPartModule.ProjectedArea> pair in _moduleAndAreas)
            {
                pair.Key.SetProjectedArea(pair.Value, _localToWorldMatrix);
            }

            //_newAeroModules = tmpAeroModules.ToList();        //this method creates lots of garbage
            int aeroIndex = 0;
            if (_newAeroModules.Capacity < tmpAeroModules.Count)
                _newAeroModules.Capacity = tmpAeroModules.Count;

            foreach(FARAeroPartModule module in tmpAeroModules)
            {
                if (aeroIndex < _newAeroModules.Count)
                    _newAeroModules[aeroIndex] = module;
                else
                    _newAeroModules.Add(module);
                ++aeroIndex;
            }
            //at this point, aeroIndex is what the count of _newAeroModules _should_ be, but due to the possibility of the previous state having more modules, this is not guaranteed
            for (int i = _newAeroModules.Count - 1; i >= aeroIndex; --i)
            {
                _newAeroModules.RemoveAt(i);        //steadily remove the modules from the end that shouldn't be there
            }

            _newUnusedAeroModules.Clear();

            for (int i = 0; i < _currentGeoModules.Count; i++)
            {
                if (!_currentGeoModules[i])
                    continue;

                FARAeroPartModule aeroModule = _currentGeoModules[i].GetComponent<FARAeroPartModule>();
                if (aeroModule != null && !tmpAeroModules.Contains(aeroModule))
                    _newUnusedAeroModules.Add(aeroModule);
            }

            if (HighLogic.LoadedSceneIsEditor)
            {
                ferram4.FARCenterQuery center = new ferram4.FARCenterQuery();

                Vector3 worldMainAxis = _localToWorldMatrix.MultiplyVector(_vehicleMainAxis);
                worldMainAxis.Normalize();

                for (int i = 0; i < _newAeroSections.Count; i++)
                {
                    FARAeroSection a = _newAeroSections[i];
                    a.PredictionCalculateAeroForces(2f, 1f, 50000f, 0.005f, worldMainAxis, center);
                }

                _sonicDragArea = Vector3.Dot(center.force, worldMainAxis) * -1000;
            }
        }