public bool CanMerge(FARAeroSection otherSection) { if (mergeFactor >= 10) { return(false); //only merge up to 10 sections } float flatnessRelDiff = flatnessRatio - otherSection.flatnessRatio; flatnessRelDiff *= invFlatnessRatio; if (flatnessRelDiff < 0.05) //allow for 5% rel difference for merging { if (flatnessRatio >= 1.05) //if it's within 5% of 1, it's good //allow 5 degrees error for flatnessRatio { if (Math.Abs(Vector3.Dot(worldNormalVector, otherSection.worldNormalVector)) <= 0.999) { return(false); //too different in out-of-roundness, don't merge } } } float diameterRelDiff = diameter - otherSection.diameter; diameterRelDiff /= diameter; return(diameterRelDiff < 0.05); }
public void SimulateAeroProperties(out Vector3 aeroForce, out Vector3 aeroTorque, Vector3 velocityWorldVector, double altitude) { // Rodhern: It seems that this method, 'SimulateAeroProperties', is only used in FARAPI, which in turn can be used by say KSPTrajectories. // The parameter 'FARCenterQuery dummy' is from a code fix by Benjamin Chung (commit 18fbb9d29431679a4de9dfc22a443f400d2d4f8b). FARCenterQuery center = new FARCenterQuery(); FARCenterQuery dummy = new FARCenterQuery(); float pressure; float density; float temperature; float speedOfSound; CelestialBody body = vessel.mainBody; //Calculate main gas properties pressure = (float)body.GetPressure(altitude); temperature = (float)body.GetTemperature(altitude); density = (float)body.GetDensity(pressure, temperature); 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 pseudoKnudsenNumber = machNumber / (reynoldsNumber + machNumber); float skinFriction = (float)FARAeroUtil.SkinFrictionDrag(reynoldsNumber, machNumber); FlightEnv fenv = FlightEnv.NewPredicted(vessel.mainBody, altitude, machNumber); if (_currentAeroSections != null) { for (int i = 0; i < _currentAeroSections.Count; i++) { FARAeroSection curSection = _currentAeroSections[i]; if (curSection != null) { curSection.PredictionCalculateAeroForces(density, machNumber, reynoldsPerLength, pseudoKnudsenNumber, skinFriction, velocityWorldVector, center); } } for (int i = 0; i < _legacyWingModels.Count; i++) { FARWingAerodynamicModel curWing = _legacyWingModels[i]; if ((object)curWing != null) { Vector3d force = curWing.PrecomputeCenterOfLift(velocityWorldVector, fenv, dummy); center.AddForce(curWing.transform.position, force); } } } aeroForce = center.force; aeroTorque = center.TorqueAt(vessel.CoM); }
public void SimulateAeroProperties(out Vector3 aeroForce, out Vector3 aeroTorque, Vector3 velocityWorldVector, double altitude) { FARCenterQuery center = new FARCenterQuery(); float pressure; float density; float temperature; float speedOfSound; CelestialBody body = vessel.mainBody; //Calculate main gas properties pressure = (float)body.GetPressure(altitude); temperature = (float)body.GetTemperature(altitude); density = (float)body.GetDensity(pressure, temperature); 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) { for (int i = 0; i < _currentAeroSections.Count; i++) { FARAeroSection curSection = _currentAeroSections[i]; if (curSection != null) { curSection.PredictionCalculateAeroForces(density, machNumber, reynoldsPerLength, pseudoKnudsenNumber, skinFriction, velocityWorldVector, center); } } for (int i = 0; i < _legacyWingModels.Count; i++) { FARWingAerodynamicModel curWing = _legacyWingModels[i]; if ((object)curWing != null) { curWing.PrecomputeCenterOfLift(velocityWorldVector, machNumber, density, center); } } } aeroForce = center.force; aeroTorque = center.TorqueAt(vessel.CoM); }
public static FARAeroSection CreateNewAeroSection() { FARAeroSection section = new FARAeroSection(); section.xForcePressureAoA0 = new FARFloatCurve(6); section.xForcePressureAoA180 = new FARFloatCurve(6); section.xForceSkinFriction = new FARFloatCurve(3); section.partData = new List <PartData>(); section.handledAeroModulesIndexDict = new Dictionary <FARAeroPartModule, int>(ObjectReferenceEqualityComparer <FARAeroPartModule> .Default); GenerateCrossFlowDragCurve(); return(section); }
public void MergeAeroSection(FARAeroSection otherSection) { //increase merge factor each time we merge to maintain relative strength of sections mergeFactor += 1; float invMergeFactorP1 = 1 / (mergeFactor + 1); //add simple factors potentialFlowNormalForce += otherSection.potentialFlowNormalForce; viscCrossflowDrag += otherSection.viscCrossflowDrag; hypersonicMomentForward += otherSection.hypersonicMomentForward; hypersonicMomentBackward += otherSection.hypersonicMomentBackward; flatnessRatio = invMergeFactorP1 * (flatnessRatio * mergeFactor + otherSection.flatnessRatio); invFlatnessRatio = invMergeFactorP1 * (invFlatnessRatio * mergeFactor + otherSection.invFlatnessRatio); diameter = invMergeFactorP1 * (diameter * mergeFactor + otherSection.diameter); //merge the curves; don't scale because these are actual drag values xForcePressureAoA0.AddCurve(otherSection.xForcePressureAoA0); xForcePressureAoA180.AddCurve(otherSection.xForcePressureAoA180); xForceSkinFriction.AddCurve(otherSection.xForceSkinFriction); //prep old part data for merging for (int i = 0; i < partData.Count; ++i) { PartData oldData = partData[i]; oldData.dragFactor *= mergeFactor; //scale all of these up for the incoming data oldData.centroidPartSpace *= mergeFactor; oldData.xRefVectorPartSpace *= mergeFactor; oldData.nRefVectorPartSpace *= mergeFactor; partData[i] = oldData; } //merge PartData float mergeFactorP1 = mergeFactor + 1; for (int i = 0; i < otherSection.partData.Count; ++i) { PartData tmpOtherData = otherSection.partData[i]; int index = -1; if (handledAeroModulesIndexDict.TryGetValue(tmpOtherData.aeroModule, out index)) { PartData tmpData = partData[index]; tmpData.centroidPartSpace += tmpOtherData.centroidPartSpace; //prep'd for averaging tmpData.xRefVectorPartSpace += tmpOtherData.xRefVectorPartSpace; tmpData.nRefVectorPartSpace += tmpOtherData.nRefVectorPartSpace; tmpData.dragFactor += tmpOtherData.dragFactor; partData[index] = tmpData; } else { /*tmpOtherData.centroidPartSpace = mergeFactorP1 * (tmpOtherData.centroidPartSpace); //these must be scaled to completely counter the effect of the downscale at the end * tmpOtherData.xRefVectorPartSpace = (tmpOtherData.xRefVectorPartSpace); * tmpOtherData.nRefVectorPartSpace = (tmpOtherData.nRefVectorPartSpace); * tmpOtherData.dragFactor = invMergeFactor * (tmpOtherData.dragFactor); //this will already be scaled down at the end */ tmpOtherData.centroidPartSpace *= mergeFactorP1; //needs to be scaled this way to place the centroid in the correct location partData.Add(tmpOtherData); handledAeroModulesIndexDict.Add(tmpOtherData.aeroModule, partData.Count - 1); } } for (int i = 0; i < partData.Count; ++i) { PartData newData = partData[i]; newData.dragFactor *= invMergeFactorP1; //now scale everything back down to sane levels newData.centroidPartSpace *= invMergeFactorP1; newData.xRefVectorPartSpace.Normalize(); newData.nRefVectorPartSpace.Normalize(); partData[i] = newData; } }
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; double voxelVolume = _voxel.Volume; double filledVolume = 0; for (int i = front; i <= back; i++) filledVolume += _vehicleCrossSection[i].area; filledVolume *= _sectionThickness; //total volume taken up by the filled voxel double gridFillednessFactor = filledVolume / voxelVolume; //determines how fine the grid is compared to the vehicle. Accounts for loss in precision and added smoothing because of unused sections of voxel volume gridFillednessFactor *= 25; //used to handle relatively empty, but still alright, planes double stdDevCutoff = 3; stdDevCutoff *= gridFillednessFactor; if (stdDevCutoff < 0.5) stdDevCutoff = 0.5; if (stdDevCutoff > 3) stdDevCutoff = 3; 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) int extraLowFinessRatioDerivSmoothingPasses = (int)Math.Round((5f - finenessRatio) * 0.5f) * FARSettingsScenarioModule.Settings.numDerivSmoothingPasses; if (extraLowFinessRatioDerivSmoothingPasses < 0) extraLowFinessRatioDerivSmoothingPasses = 0; int extraAreaSmoothingPasses = (int)Math.Round((gridFillednessFactor / 25.0 - 0.5) * 4.0); if (extraAreaSmoothingPasses < 0) extraAreaSmoothingPasses = 0; ThreadSafeDebugLogger.Instance.RegisterMessage("Std dev for smoothing: " + stdDevCutoff + " voxel total vol: " + voxelVolume + " filled vol: " + filledVolume); AdjustCrossSectionForAirDucting(_vehicleCrossSection, _currentGeoModules, front, back, ref _maxCrossSectionArea); GaussianSmoothCrossSections(_vehicleCrossSection, stdDevCutoff, FARSettingsScenarioModule.Settings.gaussianVehicleLengthFractionForSmoothing, _sectionThickness, _length, front, back, FARSettingsScenarioModule.Settings.numAreaSmoothingPasses + extraAreaSmoothingPasses, FARSettingsScenarioModule.Settings.numDerivSmoothingPasses + extraLowFinessRatioDerivSmoothingPasses); CalculateSonicPressure(_vehicleCrossSection, front, back, _sectionThickness, _maxCrossSectionArea); validSectionCount = numSections; firstSection = front; //recalc these with adjusted cross-sections invMaxRadFactor = 1f / Math.Sqrt(_maxCrossSectionArea / Math.PI); 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 lowFinenessRatioFactor = 1f; lowFinenessRatioFactor += 1f/(1 + 0.5f * (float)finenessRatio); float lowFinenessRatioBlendFactor = lowFinenessRatioFactor--; _moduleAndAreasDict.Clear(); //_newAeroSections = new List<FARAeroSection>(); HashSet<FARAeroPartModule> tmpAeroModules = new HashSet<FARAeroPartModule>(); _sonicDragArea = 0; if (_newAeroSections.Capacity < numSections + 1) _newAeroSections.Capacity = numSections + 1; int aeroSectionIndex = 0; FARAeroSection prevSection = null; 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 (aeroSectionIndex < _newAeroSections.Count) currentSection = _newAeroSections[aeroSectionIndex]; 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; //^2 hypersonicDragForwardFrac *= hypersonicDragForwardFrac; //^4 //hypersonicDragForwardFrac *= hypersonicDragForwardFrac; //^8 //hypersonicDragForwardFrac *= hypersonicDragForwardFrac; //^16 //hypersonicDragForwardFrac *= hypersonicDragForwardFrac; //^32 hypersonicDragBackwardFrac *= hypersonicDragBackwardFrac; //^2 hypersonicDragBackwardFrac *= hypersonicDragBackwardFrac; //^4 //hypersonicDragBackwardFrac *= hypersonicDragBackwardFrac; //^8 //hypersonicDragBackwardFrac *= hypersonicDragBackwardFrac; //^16 //hypersonicDragBackwardFrac *= hypersonicDragBackwardFrac; //^32 if (flatnessRatio >= 1) { hypersonicDragForwardFrac /= (float)(flatnessRatio * flatnessRatio); hypersonicDragBackwardFrac /= (float)(flatnessRatio * flatnessRatio); } else { hypersonicDragForwardFrac *= (float)(flatnessRatio * flatnessRatio); hypersonicDragBackwardFrac *= (float)(flatnessRatio * flatnessRatio); } /*if (hypersonicDragForwardFrac > 1) hypersonicDragForwardFrac = 1; if (hypersonicDragBackwardFrac > 1) hypersonicDragBackwardFrac = 1;*/ 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; }*/ double areaForForces = ((curArea + prevArea) - (nextArea + curArea)) * 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) * lowFinenessRatioFactor, 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)) * lowFinenessRatioFactor, 0)); hypersonicDragBackwardFrac += 1f; //avg fracs with 1 to get intermediate frac hypersonicDragBackwardFrac *= 0.5f; hypersonicDragForwardFrac += 1f; hypersonicDragForwardFrac *= 0.5f; sonicAoA0Drag = -(float)(cPSonicForward * (areaForForces)) + 0.3f * hypersonicDragForward * hypersonicDragForwardFrac; sonicAoA0Drag *= (1 - lowFinenessRatioBlendFactor); //at high finenessRatios, use the entire above section for sonic drag sonicAoA0Drag += hypersonicDragForward * hypersonicDragForwardFrac * lowFinenessRatioBlendFactor * 1.4f; //at very low finenessRatios, use a boosted version of the hypersonic drag sonicAoA180Drag = (float)(cPSonicBackward * (-areaForForces)) + sonicBaseDrag - 0.3f * hypersonicDragBackward * hypersonicDragBackwardFrac; sonicAoA180Drag *= (1 - lowFinenessRatioBlendFactor); //at high finenessRatios, use the entire above section for sonic drag sonicAoA180Drag += (-hypersonicDragBackward * hypersonicDragBackwardFrac * 1.4f + sonicBaseDrag) * lowFinenessRatioBlendFactor; //at very low finenessRatios, use a boosted version of the hypersonic drag //if(i == 0) // sonicAoA180Drag += (float)(cPSonicBackward * (curArea)) + sonicBaseDrag - hypersonicDragBackward * 0.3f * hypersonicDragBackwardFrac; } else if (sonicBaseDrag < 0) { xForcePressureAoA0.SetPoint(0, new Vector3d(_criticalMach, (sonicBaseDrag * 0.2f + (0.325f * hypersonicDragForward * hypersonicDragForwardFrac)) * lowFinenessRatioFactor, 0)); xForcePressureAoA180.SetPoint(0, new Vector3d(_criticalMach, -(0.325f * hypersonicDragBackward * hypersonicDragBackwardFrac) * lowFinenessRatioFactor, 0)); hypersonicDragBackwardFrac += 1f; //avg fracs with 1 to get intermediate frac hypersonicDragBackwardFrac *= 0.5f; hypersonicDragForwardFrac += 1f; hypersonicDragForwardFrac *= 0.5f; sonicAoA0Drag = -(float)(cPSonicForward * (areaForForces)) + sonicBaseDrag + 0.3f * hypersonicDragForward * hypersonicDragForwardFrac; sonicAoA0Drag *= (1 - lowFinenessRatioBlendFactor); //at high finenessRatios, use the entire above section for sonic drag sonicAoA0Drag += (hypersonicDragForward * hypersonicDragForwardFrac * 1.4f + sonicBaseDrag) * lowFinenessRatioBlendFactor; //at very low finenessRatios, use a boosted version of the hypersonic drag sonicAoA180Drag = (float)(cPSonicBackward * (-areaForForces)) - 0.3f * hypersonicDragBackward * hypersonicDragBackwardFrac; sonicAoA180Drag *= (1 - lowFinenessRatioBlendFactor); //at high finenessRatios, use the entire above section for sonic drag sonicAoA180Drag += (-hypersonicDragBackward * hypersonicDragBackwardFrac * 1.4f) * lowFinenessRatioBlendFactor; //at very low finenessRatios, use a boosted version of the hypersonic drag //if (i == numSections) // sonicAoA0Drag += -(float)(cPSonicForward * (-curArea)) + sonicBaseDrag + hypersonicDragForward * 0.3f * hypersonicDragForwardFrac; } else { xForcePressureAoA0.SetPoint(0, new Vector3d(_criticalMach, (0.325f * hypersonicDragForward * hypersonicDragForwardFrac) * lowFinenessRatioFactor, 0)); xForcePressureAoA180.SetPoint(0, new Vector3d(_criticalMach, -(0.325f * hypersonicDragBackward * hypersonicDragBackwardFrac) * lowFinenessRatioFactor, 0)); hypersonicDragBackwardFrac += 1f; //avg fracs with 1 to get intermediate frac hypersonicDragBackwardFrac *= 0.5f; hypersonicDragForwardFrac += 1f; hypersonicDragForwardFrac *= 0.5f; sonicAoA0Drag = -(float)(cPSonicForward * (areaForForces)) + 0.3f * hypersonicDragForward * hypersonicDragForwardFrac; sonicAoA0Drag *= (1 - lowFinenessRatioBlendFactor); //at high finenessRatios, use the entire above section for sonic drag sonicAoA0Drag += hypersonicDragForward * hypersonicDragForwardFrac * lowFinenessRatioBlendFactor * 1.4f; //at very low finenessRatios, use a boosted version of the hypersonic drag sonicAoA180Drag = (float)(cPSonicBackward * (-areaForForces)) - 0.3f * hypersonicDragBackward * hypersonicDragBackwardFrac; sonicAoA180Drag *= (1 - lowFinenessRatioBlendFactor); //at high finenessRatios, use the entire above section for sonic drag sonicAoA180Drag += (-hypersonicDragBackward * hypersonicDragBackwardFrac * 1.4f) * lowFinenessRatioBlendFactor; //at very low finenessRatios, use a boosted version of the hypersonic drag } 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 (_moduleAndAreasDict.ContainsKey(m)) _moduleAndAreasDict[m] += areas; else _moduleAndAreasDict[m] = areas; weightingFactor += (float)pair.Value.exposedAreaCount; weighting.Add((float)pair.Value.exposedAreaCount); } weightingFactor = 1 / weightingFactor; for (int j = 0; j < weighting.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, _partWorldToLocalMatrixDict); if (prevSection != null && prevSection.CanMerge(currentSection)) { prevSection.MergeAeroSection(currentSection); currentSection.ClearAeroSection(); } else { if (aeroSectionIndex < _newAeroSections.Count) _newAeroSections[aeroSectionIndex] = currentSection; else _newAeroSections.Add(currentSection); prevSection = currentSection; ++aeroSectionIndex; } for (int j = 0; j < includedModules.Count; j++) { FARAeroPartModule a = includedModules[j]; tmpAeroModules.Add(a); } includedModules.Clear(); weighting.Clear(); } if (_newAeroSections.Count > aeroSectionIndex + 1) //deal with sections that are unneeded now { lock (_commonLocker) for (int i = _newAeroSections.Count - 1; i > aeroSectionIndex; --i) { FARAeroSection unusedSection = _newAeroSections[i]; _newAeroSections.RemoveAt(i); unusedSection.ClearAeroSection(); 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 = null; } } } foreach (KeyValuePair<FARAeroPartModule, FARAeroPartModule.ProjectedArea> pair in _moduleAndAreasDict) { 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); } //UpdateSonicDragArea(); }
public void MergeAeroSection(FARAeroSection otherSection) { //increase merge factor each time we merge to maintain relative strength of sections mergeFactor += 1; float invMergeFactor = 1 / (mergeFactor + 1); //merge simple factors potentialFlowNormalForce += otherSection.potentialFlowNormalForce; viscCrossflowDrag += otherSection.viscCrossflowDrag; hypersonicMomentForward += otherSection.hypersonicMomentForward; hypersonicMomentBackward += otherSection.hypersonicMomentBackward; flatnessRatio = invMergeFactor * (flatnessRatio * mergeFactor + otherSection.flatnessRatio); invFlatnessRatio = invMergeFactor * (invFlatnessRatio * mergeFactor + otherSection.invFlatnessRatio); diameter = invMergeFactor * (diameter * mergeFactor + otherSection.diameter); //merge the curves; don't scale because these are actual drag values xForcePressureAoA0.AddCurve(otherSection.xForcePressureAoA0); xForcePressureAoA180.AddCurve(otherSection.xForcePressureAoA180); xForceSkinFriction.AddCurve(otherSection.xForceSkinFriction); //prep old data for merging for (int i = 0; i < partData.Count; ++i) { PartData oldData = partData[i]; oldData.dragFactor *= mergeFactor; //scale all of these up for the incoming data oldData.centroidPartSpace *= mergeFactor; oldData.xRefVectorPartSpace *= mergeFactor; oldData.nRefVectorPartSpace *= mergeFactor; partData[i] = oldData; } //merge PartData float mergeFactorP1 = mergeFactor + 1; for (int i = 0; i < otherSection.partData.Count; ++i) { PartData tmpOtherData = otherSection.partData[i]; int index = -1; if (handledAeroModulesIndexDict.TryGetValue(tmpOtherData.aeroModule, out index)) { PartData tmpData = partData[index]; tmpData.centroidPartSpace += tmpOtherData.centroidPartSpace; //prep'd for averaging tmpData.xRefVectorPartSpace += tmpOtherData.xRefVectorPartSpace; tmpData.nRefVectorPartSpace += tmpOtherData.nRefVectorPartSpace; tmpData.dragFactor += tmpOtherData.dragFactor; partData[index] = tmpData; } else { /*tmpOtherData.centroidPartSpace = mergeFactorP1 * (tmpOtherData.centroidPartSpace); //these must be scaled to completely counter the effect of the downscale at the end tmpOtherData.xRefVectorPartSpace = (tmpOtherData.xRefVectorPartSpace); tmpOtherData.nRefVectorPartSpace = (tmpOtherData.nRefVectorPartSpace); tmpOtherData.dragFactor = invMergeFactor * (tmpOtherData.dragFactor); //this will already be scaled down at the end */ partData.Add(tmpOtherData); handledAeroModulesIndexDict.Add(tmpOtherData.aeroModule, partData.Count - 1); } } for (int i = 0; i < partData.Count; ++i) { PartData newData = partData[i]; newData.dragFactor *= invMergeFactor; //now scale everything back down to sane levels newData.centroidPartSpace *= invMergeFactor; newData.xRefVectorPartSpace.Normalize(); newData.nRefVectorPartSpace.Normalize(); partData[i] = newData; } }
public bool CanMerge(FARAeroSection otherSection) { if (mergeFactor >= 4) return false; //only merge up to 5 sections bool merge = true; float flatnessRelDiff = flatnessRatio - otherSection.flatnessRatio; flatnessRelDiff *= invFlatnessRatio; if (flatnessRelDiff < 0.05) //allow for 5% rel difference for merging if ((flatnessRatio - 1) < 0.05) //if it's within 5% of 1, it's good merge &= true; else if (Math.Abs(Vector3.Dot(worldNormalVector, otherSection.worldNormalVector)) > 0.999) //allow 5 degrees error for flatnessRatio merge &= true; else merge &= false; //too different in out-of-roundness, don't merge float diameterRelDiff = diameter - otherSection.diameter; diameterRelDiff /= diameter; if (diameterRelDiff < 0.05) merge &= true; else merge &= false; return merge; }