double CalculateSunArea(ModularFI.ModularFlightIntegrator fi, FlightIntegrator.PartThermalData ptd) { FARAeroPartModule module = null; if (ptd.part.Modules.Contains("FARAeroPartModule")) { module = (FARAeroPartModule)ptd.part.Modules["FARAeroPartModule"]; } if ((object)module != null) { double sunArea = module.ProjectedAreaWorld(fi.sunVector) * ptd.sunAreaMultiplier; if (sunArea > 0) { return(sunArea); } else { return(fi.BaseFIGetSunArea(ptd)); } } else { return(fi.BaseFIGetSunArea(ptd)); } }
double CalculateAreaExposed(ModularFI.ModularFlightIntegrator fi, Part part, FARAeroPartModule aeroModule) { if ((object)aeroModule != null) { double exposedArea = aeroModule.ProjectedAreaLocal(-part.dragVectorDirLocal); if (exposedArea > 0) { return(exposedArea); } else { return(fi.BaseFICalculateAreaExposed(part)); } } else { return(fi.BaseFICalculateAreaExposed(part)); } /*else * { * if (stockRadArea > 0) * return aeroModule.ProjectedAreas.totalArea * dragCubeExposed / stockRadArea; * else * return aeroModule.ProjectedAreas.totalArea; * }*/ }
double CalculateBodyArea(ModularFI.ModularFlightIntegrator fi, FlightIntegrator.PartThermalData ptd) { FARAeroPartModule module = null; if (ptd.part.Modules.Contains("FARAeroPartModule")) { module = (FARAeroPartModule)ptd.part.Modules["FARAeroPartModule"]; } if ((object)module != null) { double bodyArea = module.ProjectedAreaWorld(-fi.Vessel.upAxis) * ptd.bodyAreaMultiplier; if (bodyArea > 0) { return(bodyArea); } else { return(fi.BaseFIBodyArea(ptd)); } } else { return(fi.BaseFIBodyArea(ptd)); } }
private static void UpdateThermodynamicsPre(ModularFlightIntegrator fi) { for (int i = 0; i < fi.PartThermalDataCount; i++) { PartThermalData ptd = fi.partThermalDataList[i]; Part part = ptd.part; if (!part.Modules.Contains <FARAeroPartModule>()) { continue; } FARAeroPartModule aeroModule = part.Modules.GetModule <FARAeroPartModule>(); // make sure drag cube areas are correct based on voxelization if (!part.DragCubes.None && aeroModule) { for (int j = 0; j < 6; j++) { part.DragCubes.AreaOccluded[FARAeroPartModule.ProjectedArea.FaceMap[j]] = (float)aeroModule.ProjectedAreas[j]; } } part.radiativeArea = CalculateAreaRadiative(fi, part, aeroModule); part.exposedArea = part.machNumber > 0 ? CalculateAreaExposed(fi, part, aeroModule) : part.radiativeArea; if (FARSettings.ExposedAreaLimited && part.exposedArea > part.radiativeArea) { part.exposedArea = part.radiativeArea; //sanity check just in case } } }
private void FixedUpdate() { if (_vehicleAero == null) { return; } if (_vehicleAero.CalculationCompleted) { _vehicleAero.GetNewAeroData(out _currentAeroModules, out _unusedAeroModules, out _currentAeroSections, out _legacyWingModels); if ((object)_flightGUI == null) { _flightGUI = _vessel.GetComponent <FerramAerospaceResearch.FARGUI.FARFlightGUI.FlightGUI>(); } _flightGUI.UpdateAeroModules(_currentAeroModules, _legacyWingModels); //Debug.Log("Updating " + _vessel.vesselName + " aero properties\n\rCross-Sectional Area: " + _vehicleAero.MaxCrossSectionArea + " Crit Mach: " + _vehicleAero.CriticalMach + "\n\rUnusedAeroCount: " + _unusedAeroModules.Count + " UsedAeroCount: " + _currentAeroModules.Count + " sectCount: " + _currentAeroSections.Count); for (int i = 0; i < _unusedAeroModules.Count; i++) { FARAeroPartModule a = _unusedAeroModules[i]; a.SetShielded(true); a.ForceLegacyAeroUpdates(); //Debug.Log(a.part.partInfo.title + " shielded, area: " + a.ProjectedAreas.totalArea); } for (int i = 0; i < _currentAeroModules.Count; i++) { FARAeroPartModule a = _currentAeroModules[i]; a.SetShielded(false); a.ForceLegacyAeroUpdates(); //Debug.Log(a.part.partInfo.title + " unshielded, area: " + a.ProjectedAreas.totalArea); } _vesselIntakeRamDrag.UpdateAeroData(_currentAeroModules, _unusedAeroModules); } if (FlightGlobals.ready && _currentAeroSections != null && _vessel) { CalculateAndApplyVesselAeroProperties(); } if (_currentGeoModules.Count > geoModulesReady) { CheckGeoModulesReady(); } if (_updateRateLimiter < FARSettingsScenarioModule.VoxelSettings.minPhysTicksPerUpdate) { _updateRateLimiter++; } else if (_updateQueued) { VesselUpdate(_recalcGeoModules); } }
double CalculateAreaRadiative(ModularFI.ModularFlightIntegrator fi, Part part, FARAeroPartModule aeroModule) { //double dragCubeExposed = fi.BaseFICalculateAreaExposed(part); if ((object)aeroModule == null) return fi.BaseFICalculateAreaRadiative(part); else { return aeroModule.ProjectedAreas.totalArea; } }
private void CalculateAndApplyVesselAeroProperties() { float atmDensity = (float)vessel.atmDensity; if (atmDensity <= 0) { MachNumber = 0; ReynoldsNumber = 0; return; } MachNumber = vessel.mach; ReynoldsNumber = FARAeroUtil.CalculateReynoldsNumber(vessel.atmDensity, Length, vessel.srfSpeed, MachNumber, FlightGlobals.getExternalTemperature((float)vessel .altitude, vessel.mainBody), vessel.mainBody.atmosphereAdiabaticIndex); float skinFrictionDragCoefficient = (float)FARAeroUtil.SkinFrictionDrag(ReynoldsNumber, MachNumber); float pseudoKnudsenNumber = (float)(MachNumber / (ReynoldsNumber + MachNumber)); Vector3 frameVel = Krakensbane.GetFrameVelocityV3f(); //start from the top and come down to improve performance if it needs to remove anything for (int i = _currentAeroModules.Count - 1; i >= 0; i--) { FARAeroPartModule m = _currentAeroModules[i]; if (m != null && m.part != null && m.part.partTransform != null) { m.UpdateVelocityAndAngVelocity(frameVel); } else { _currentAeroModules.RemoveAt(i); } } foreach (FARAeroSection aeroSection in _currentAeroSections) { aeroSection.FlightCalculateAeroForces((float)MachNumber, (float)(ReynoldsNumber / Length), pseudoKnudsenNumber, skinFrictionDragCoefficient); } _vesselIntakeRamDrag.ApplyIntakeRamDrag((float)MachNumber, vessel.srf_velocity.normalized); foreach (FARAeroPartModule m in _currentAeroModules) { m.ApplyForces(); } }
private void CalculateAndApplyVesselAeroProperties() { float atmDensity = (float)_vessel.atmDensity; if (atmDensity <= 0) { machNumber = 0; reynoldsNumber = 0; return; } machNumber = _vessel.mach; reynoldsNumber = FARAeroUtil.CalculateReynoldsNumber(_vessel.atmDensity, Length, _vessel.srfSpeed, machNumber, FlightGlobals.getExternalTemperature((float)_vessel.altitude, _vessel.mainBody), _vessel.mainBody.atmosphereAdiabaticIndex); float skinFrictionDragCoefficient = (float)FARAeroUtil.SkinFrictionDrag(reynoldsNumber, machNumber); Vector3 frameVel = Krakensbane.GetFrameVelocityV3f(); if (_updateQueued) //only happens if we have an voxelization scheduled, then we need to check for null { for (int i = _currentAeroModules.Count - 1; i >= 0; i--) //start from the top and come down to improve performance if it needs to remove anything { FARAeroPartModule m = _currentAeroModules[i]; if (m != null) { m.UpdateVelocityAndAngVelocity(frameVel); } else { _currentAeroModules.RemoveAt(i); i++; } } } else //otherwise, we don't need to do Unity's expensive "is this part dead" null-check { for (int i = _currentAeroModules.Count - 1; i >= 0; i--) //start from the top and come down to improve performance if it needs to remove anything { FARAeroPartModule m = _currentAeroModules[i]; m.UpdateVelocityAndAngVelocity(frameVel); } } for (int i = 0; i < _currentAeroSections.Count; i++) { _currentAeroSections[i].FlightCalculateAeroForces(atmDensity, (float)machNumber, (float)(reynoldsNumber / Length), skinFrictionDragCoefficient); } _vesselIntakeRamDrag.ApplyIntakeRamDrag((float)machNumber, _vessel.srf_velocity.normalized, (float)_vessel.dynamicPressurekPa); for (int i = 0; i < _currentAeroModules.Count; i++) { FARAeroPartModule m = _currentAeroModules[i]; m.ApplyForces(); } }
double CalculateAreaExposed(ModularFI.ModularFlightIntegrator fi, Part part) { FARAeroPartModule module = null; if (part.Modules.Contains <FARAeroPartModule>()) { module = part.Modules.GetModule <FARAeroPartModule>(); } return(CalculateAreaExposed(fi, part, module)); }
private static double CalculateAreaRadiative(ModularFlightIntegrator fi, Part part) { FARAeroPartModule module = null; if (part.Modules.Contains <FARAeroPartModule>()) { module = part.Modules.GetModule <FARAeroPartModule>(); } return(CalculateAreaRadiative(fi, part, module)); }
double CalculateAreaExposed(ModularFI.ModularFlightIntegrator fi, Part part) { FARAeroPartModule module = null; if (part.Modules.Contains("FARAeroPartModule")) { module = (FARAeroPartModule)part.Modules["FARAeroPartModule"]; } return(CalculateAreaExposed(fi, part, module)); }
private static double CalculateBodyArea(ModularFlightIntegrator fi, PartThermalData ptd) { FARAeroPartModule module = ptd.part.Modules.GetModule <FARAeroPartModule>(); if (module is null) { return(fi.BaseFIBodyArea(ptd)); } double bodyArea = module.ProjectedAreaWorld(-fi.Vessel.upAxis) * ptd.bodyAreaMultiplier; return(bodyArea > 0 ? bodyArea : fi.BaseFIBodyArea(ptd)); }
private static double CalculateSunArea(ModularFlightIntegrator fi, PartThermalData ptd) { FARAeroPartModule module = ptd.part.Modules.GetModule <FARAeroPartModule>(); if (module is null) { return(fi.BaseFIGetSunArea(ptd)); } double sunArea = module.ProjectedAreaWorld(fi.sunVector) * ptd.sunAreaMultiplier; return(sunArea > 0 ? sunArea : fi.BaseFIGetSunArea(ptd)); }
double CalculateAreaExposed(ModularFI.ModularFlightIntegrator fi, Part part, FARAeroPartModule aeroModule) { double dragCubeExposed = fi.BaseFICalculateAreaExposed(part); if (aeroModule == null) return dragCubeExposed; else { double cubeRadiative = fi.BaseFICalculateAreaRadiative(part); if (cubeRadiative > 0) return aeroModule.ProjectedAreas.totalArea * dragCubeExposed / cubeRadiative; else return aeroModule.ProjectedAreas.totalArea; } }
double CalculateAreaExposed(ModularFI.ModularFlightIntegrator fi, Part part, FARAeroPartModule aeroModule) { if ((object)aeroModule == null) return fi.BaseFICalculateAreaExposed(part); else return aeroModule.ProjectedAreaLocal(-part.dragVectorDirLocal); /*else { if (stockRadArea > 0) return aeroModule.ProjectedAreas.totalArea * dragCubeExposed / stockRadArea; else return aeroModule.ProjectedAreas.totalArea; }*/ }
private static double CalculateAreaRadiative( ModularFlightIntegrator fi, Part part, FARAeroPartModule aeroModule ) { if (aeroModule is null) { return(fi.BaseFICalculateAreaRadiative(part)); } double radArea = aeroModule.ProjectedAreas.totalArea; return(radArea > 0 ? radArea : fi.BaseFICalculateAreaRadiative(part)); }
private void FixedUpdate() { if (_vehicleAero == null) { return; } if (_vehicleAero.CalculationCompleted) { _vehicleAero.GetNewAeroData(out _currentAeroModules, out _unusedAeroModules, out _currentAeroSections, out _legacyWingModels); _vessel.SendMessage("UpdateAeroModules", _currentAeroModules); for (int i = 0; i < _unusedAeroModules.Count; i++) { FARAeroPartModule a = _unusedAeroModules[i]; a.SetShielded(true); //Debug.Log(a.part.partInfo.title + " shielded"); } for (int i = 0; i < _currentAeroModules.Count; i++) { FARAeroPartModule a = _currentAeroModules[i]; a.SetShielded(false); //Debug.Log(a.part.partInfo.title + " unshielded"); } _vesselIntakeRamDrag.UpdateAeroData(_currentAeroModules, _unusedAeroModules); } if (FlightGlobals.ready && _currentAeroSections != null) { CalculateAndApplyVesselAeroProperties(); } if (_currentGeoModules.Count > geoModulesReady) { CheckGeoModulesReady(); } if (_updateRateLimiter < FARSettingsScenarioModule.VoxelSettings.minPhysTicksPerUpdate) { _updateRateLimiter++; } else if (_updateQueued) { VesselUpdate(_recalcGeoModules); } }
private void CalculateAndApplyVesselAeroProperties() { float atmDensity = (float)_vessel.atmDensity; if (atmDensity <= 0) { machNumber = 0; reynoldsNumber = 0; return; } machNumber = _vessel.mach; reynoldsNumber = FARAeroUtil.CalculateReynoldsNumber(_vessel.atmDensity, Length, _vessel.srfSpeed, machNumber, FlightGlobals.getExternalTemperature((float)_vessel.altitude, _vessel.mainBody), _vessel.mainBody.atmosphereAdiabaticIndex); float skinFrictionDragCoefficient = (float)FARAeroUtil.SkinFrictionDrag(reynoldsNumber, machNumber); Vector3 frameVel = Krakensbane.GetFrameVelocityV3f(); for (int i = 0; i < _currentAeroModules.Count; i++) { FARAeroPartModule m = _currentAeroModules[i]; if (m != null) { m.UpdateVelocityAndAngVelocity(frameVel); } else { _currentAeroModules.RemoveAt(i); i--; } } for (int i = 0; i < _currentAeroSections.Count; i++) { _currentAeroSections[i].FlightCalculateAeroForces(atmDensity, (float)machNumber, (float)(reynoldsNumber / Length), skinFrictionDragCoefficient); } _vesselIntakeRamDrag.ApplyIntakeRamDrag((float)machNumber, _vessel.srf_velocity.normalized, (float)_vessel.dynamicPressurekPa); for (int i = 0; i < _currentAeroModules.Count; i++) { FARAeroPartModule m = _currentAeroModules[i]; if ((object)m != null) { m.ApplyForces(); } } }
private static void UpdateThermodynamicsPre(ModularFlightIntegrator fi) { bool voxelizationCompleted = (fi.Vessel.vesselModules.Find(module => module is FARVesselAero) as FARVesselAero)? .HasEverValidVoxelization() ?? false; for (int i = 0; i < fi.PartThermalDataCount; i++) { PartThermalData ptd = fi.partThermalDataList[i]; Part part = ptd.part; FARAeroPartModule aeroModule = part.Modules.GetModule <FARAeroPartModule>(); if (aeroModule is null) { continue; } // make sure drag cube areas are correct based on voxelization if (voxelizationCompleted) { if (!part.DragCubes.None && aeroModule) { for (int j = 0; j < 6; j++) { part.DragCubes.AreaOccluded[FARAeroPartModule.ProjectedArea.FaceMap[j]] = (float)aeroModule.ProjectedAreas[j]; } } part.radiativeArea = CalculateAreaRadiative(fi, part, aeroModule); part.exposedArea = part.machNumber > 0 ? CalculateAreaExposed(fi, part, aeroModule) : part.radiativeArea; } else { part.radiativeArea = fi.BaseFICalculateAreaRadiative(part); part.exposedArea = fi.BaseFICalculateAreaExposed(part); } if (FARSettings.ExposedAreaLimited && part.exposedArea > part.radiativeArea) { part.exposedArea = part.radiativeArea; //sanity check just in case } } }
void UpdateThermodynamicsPre(ModularFI.ModularFlightIntegrator fi) { for (int i = 0; i < fi.PartThermalDataCount; i++) { Part part = fi.partThermalDataList[i].part; if (!part.Modules.Contains("FARAeroPartModule")) { continue; } PartModule module = part.Modules["FARAeroPartModule"]; FARAeroPartModule aeroModule = (FARAeroPartModule)module; part.radiativeArea = CalculateAreaRadiative(fi, part, aeroModule); part.exposedArea = part.machNumber > 0 ? CalculateAreaExposed(fi, part, aeroModule) : 0; } //Debug.Log("MFI: " + fi.CoM + " " + Planetarium.GetUniversalTime()); }
double CalculateAreaExposed(ModularFI.ModularFlightIntegrator fi, Part part, FARAeroPartModule aeroModule) { double dragCubeExposed = fi.BaseFICalculateAreaExposed(part); if (aeroModule == null) { return(dragCubeExposed); } else { double cubeRadiative = fi.BaseFICalculateAreaRadiative(part); if (cubeRadiative > 0) { return(aeroModule.ProjectedAreas.totalArea * dragCubeExposed / cubeRadiative); } else { return(aeroModule.ProjectedAreas.totalArea); } } }
private void ApplyIntakeDrag(float currentRamDrag, Vector3 vesselVelNorm, float dynPres) { for (int i = _intakeTransforms.Count - 1; i >= 0; i--) { ModuleResourceIntake intake = _intakeModules[i]; if (!intake.intakeEnabled) { continue; } Transform transform = _intakeTransforms[i]; if (transform == null) { _intakeModules.RemoveAt(i); _intakeTransforms.RemoveAt(i); _aeroModulesWithIntakes.RemoveAt(i); //++i; continue; } float cosAoA = Vector3.Dot(_intakeTransforms[i].forward, vesselVelNorm); if (cosAoA < 0) { cosAoA = 0; } if (cosAoA <= 0) { continue; } FARAeroPartModule aeroModule = _aeroModulesWithIntakes[i]; Vector3 force = -aeroModule.partLocalVelNorm * cosAoA * currentRamDrag * (float)intake.area * 100f; //if(float.IsNaN(force.sqrMagnitude)) // force = Vector3.zero; aeroModule.AddLocalForce(force, Vector3.zero); } }
private void ApplyIntakeDrag(float currentRamDrag, Vector3 vesselVelNorm, float dynPres) { for (int i = 0; i < _intakeTransforms.Count; i++) { ModuleResourceIntake intake = _intakeModules[i]; if (!intake.intakeEnabled) { continue; } Transform transform = _intakeTransforms[i]; if (transform == null) { _intakeModules.RemoveAt(i); _intakeTransforms.RemoveAt(i); _aeroModulesWithIntakes.RemoveAt(i); --i; continue; } float cosAoA = Vector3.Dot(_intakeTransforms[i].forward, vesselVelNorm); if (cosAoA < 0) { cosAoA = 0; } if (cosAoA <= intake.aoaThreshold) { continue; } FARAeroPartModule aeroModule = _aeroModulesWithIntakes[i]; aeroModule.AddLocalForce(-aeroModule.partLocalVelNorm * dynPres * cosAoA * currentRamDrag * intake.area * 100, Vector3.zero); } }
private static double CalculateAreaExposed(ModularFlightIntegrator fi, Part part) { FARAeroPartModule module = part.Modules.GetModule <FARAeroPartModule>(); return(CalculateAreaExposed(fi, part, module)); }
double CalculateAreaRadiative(ModularFI.ModularFlightIntegrator fi, Part part, FARAeroPartModule aeroModule) { //double dragCubeExposed = fi.BaseFICalculateAreaExposed(part); if ((object)aeroModule == null) { return(fi.BaseFICalculateAreaRadiative(part)); } else { return(aeroModule.ProjectedAreas.totalArea); } }
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); }
public void FlightCalculateAeroForces(float atmDensity, float machNumber, float reynoldsPerUnitLength, float pseudoKnudsenNumber, float skinFrictionDrag) { 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); for (int i = 0; i < partData.Count; i++) { PartData data = partData[i]; FARAeroPartModule aeroModule = data.aeroModule; if ((object)aeroModule == null) { continue; } Vector3 xRefVector = data.xRefVectorPartSpace; Vector3 nRefVector = data.nRefVectorPartSpace; Vector3 velLocal = aeroModule.partLocalVel; Vector3 angVelLocal = aeroModule.partLocalAngVel; velLocal += Vector3.Cross(data.centroidPartSpace, angVelLocal); //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 = potentialFlowNormalForce * Math.Sign(cosAoA) * cosHalfAoA * sin2AoA; //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 * (float)sinAoA / normalForceFactor; 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); float dampingMoment = 4f * moment; 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; dampingMoment *= 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; dampingMoment *= momentFactor; } moment /= normalForceFactor; dampingMoment = Math.Abs(dampingMoment) * 0.1f; //dampingMoment += (float)Math.Abs(skinFrictionForce) * 0.1f; float rollDampingMoment = (float)(skinFrictionForce * 0.5 * diameter); //skin friction force times avg moment arm for vehicle rollDampingMoment *= (0.75f + flatnessRatio * 0.25f); //this is just an approximation for now Vector3 forceVector = (float)xForce * xRefVector + (float)nForce * localNormalForceVec; forceVector -= (float)localVelForce * velLocalNorm; Vector3 torqueVector = Vector3.Cross(xRefVector, localNormalForceVec) * moment; Vector3 axialAngLocalVel = Vector3.Dot(xRefVector, angVelLocal) * xRefVector; Vector3 nonAxialAngLocalVel = angVelLocal - axialAngLocalVel; if (velLocal.sqrMagnitude > 0.001f) { torqueVector -= (dampingMoment * nonAxialAngLocalVel) + (rollDampingMoment * axialAngLocalVel * axialAngLocalVel.magnitude) / velLocal.sqrMagnitude; } else { torqueVector -= (dampingMoment * nonAxialAngLocalVel) + (rollDampingMoment * axialAngLocalVel * axialAngLocalVel.magnitude) / 0.001f; } //float dynPresAndScaling = 0.0005f * atmDensity * velLocal.sqrMagnitude * data.dragFactor; //dyn pres and N -> kN conversion //forceVector *= dynPresAndScaling; //torqueVector *= dynPresAndScaling; forceVector *= data.dragFactor; torqueVector *= data.dragFactor; aeroModule.AddLocalForceAndTorque(forceVector, torqueVector, data.centroidPartSpace); } }
public void UpdateAeroData(List <FARAeroPartModule> allUsedAeroModules, List <FARAeroPartModule> allUnusedAeroModules) { _aeroModulesWithIntakes.Clear(); _intakeModules.Clear(); _intakeTransforms.Clear(); _airBreathingEngines.Clear(); HashSet <string> intakeResourceNames = new HashSet <string>(); for (int i = 0; i < allUsedAeroModules.Count; i++) //get all exposed intakes { FARAeroPartModule aeroModule = allUsedAeroModules[i]; if (aeroModule == null) { continue; } Part p = aeroModule.part; for (int j = 0; j < p.Modules.Count; j++) { PartModule m = p.Modules[j]; if (m is ModuleResourceIntake) { ModuleResourceIntake intake = (ModuleResourceIntake)m; if (intake.node != null && intake.node.attachedPart != null) { continue; } _aeroModulesWithIntakes.Add(aeroModule); _intakeModules.Add(intake); _intakeTransforms.Add(p.FindModelTransform(intake.intakeTransformName)); if (!intakeResourceNames.Contains(intake.resourceName)) { intakeResourceNames.Add(intake.resourceName); } } } } for (int i = 0; i < allUsedAeroModules.Count; i++) //get all exposed engines { FARAeroPartModule aeroModule = allUsedAeroModules[i]; if (aeroModule == null) { continue; } Part p = aeroModule.part; for (int j = 0; j < p.Modules.Count; j++) { PartModule m = p.Modules[j]; if (m is ModuleEngines) { ModuleEngines e = (ModuleEngines)m; if (FARAeroUtil.AJELoaded) { if (m.ClassID == AJE_JET_CLASS_ID || m.ClassID == AJE_PROP_CLASS_ID) { _airBreathingEngines.Add(e); continue; } } for (int k = 0; k < e.propellants.Count; k++) { Propellant prop = e.propellants[k]; if (intakeResourceNames.Contains(prop.name)) { _airBreathingEngines.Add(e); break; } } } } } }
private static double CalculateAreaExposed(ModularFlightIntegrator fi, Part part, FARAeroPartModule aeroModule) { if (aeroModule is null) { return(fi.BaseFICalculateAreaExposed(part)); } // Apparently stock exposed area is actually weighted by some function of mach number... // otherwise heating is much lower double exposedArea = FARSettings.ExposedAreaUsesKSPHack ? part.DragCubes.ExposedArea : aeroModule.ProjectedAreaLocal(-part.dragVectorDirLocal); return(exposedArea > 0 ? exposedArea : fi.BaseFICalculateAreaExposed(part)); }
private void CalculateAeroForces( float machNumber, float reynoldsPerUnitLength, float pseudoKnudsenNumber, float skinFrictionDrag, IForceContext forceContext ) { //this will be the same for each part, so why recalc it multiple times? double skinFrictionForce = skinFrictionDrag * xForceSkinFriction.Evaluate(machNumber); double xForceAoA0 = xForcePressureAoA0.Evaluate(machNumber); double xForceAoA180 = xForcePressureAoA180.Evaluate(machNumber); foreach (PartData data in partData) { FARAeroPartModule aeroModule = data.aeroModule; if (aeroModule is null) { continue; } Vector3 xRefVector = data.xRefVectorPartSpace; Vector3 nRefVector = data.nRefVectorPartSpace; Vector3 velLocal = forceContext.LocalVelocity(data); // Rejects both negligible speed and invalid simulation cases if (velLocal.sqrMagnitude.NearlyEqual(0.0f)) { continue; } Vector3 angVelLocal = aeroModule.partLocalAngVel; //some transform issue here, needs investigation velLocal += Vector3.Cross(data.centroidPartSpace, angVelLocal); 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)); //potential flow normal force double nForce = potentialFlowNormalForce * Math.Sign(cosAoA) * cosHalfAoA * sin2AoA; //potential flow is not significant over the rear face of things if (nForce < 0) { nForce = 0; } float normalForceFactor = Math.Abs(Vector3.Dot(localNormalForceVec, nRefVector)); normalForceFactor *= normalForceFactor; //accounts for changes in relative flatness of shape normalForceFactor = invFlatnessRatio * (1 - normalForceFactor) + flatnessRatio * normalForceFactor; float crossFlowMach = machNumber * (float)sinAoA; float crossFlowReynolds = reynoldsPerUnitLength * diameter * (float)sinAoA / normalForceFactor; //viscous crossflow normal force nForce += viscCrossflowDrag * sinSqrAoA * CalculateCrossFlowDrag(crossFlowMach, crossFlowReynolds); nForce *= normalForceFactor; double xForce = -skinFrictionForce *Math.Sign(cosAoA) * cosSqrAoA; double localVelForce = xForce * pseudoKnudsenNumber; xForce -= localVelForce; localVelForce = Math.Abs(localVelForce); float moment = (float)(cosAoA * sinAoA); float dampingMoment = 4f * moment; 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; } moment *= momentFactor; dampingMoment *= momentFactor; } else { xForce += cosSqrAoA * xForceAoA180; //negative to deal with the ref vector facing the opposite direction, causing the moment vector to point in the opposite direction float momentFactor; 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; } moment *= momentFactor; dampingMoment *= momentFactor; } moment /= normalForceFactor; dampingMoment = Math.Abs(dampingMoment) * 0.1f; //skin friction force times avg moment arm for vehicle float rollDampingMoment = (float)(skinFrictionForce * 0.5 * diameter); //this is just an approximation for now rollDampingMoment *= 0.75f + flatnessRatio * 0.25f; Vector3 forceVector = (float)xForce * xRefVector + (float)nForce * localNormalForceVec; forceVector -= (float)localVelForce * velLocalNorm; Vector3 torqueVector = Vector3.Cross(xRefVector, localNormalForceVec) * moment; Vector3 axialAngLocalVel = Vector3.Dot(xRefVector, angVelLocal) * xRefVector; Vector3 nonAxialAngLocalVel = angVelLocal - axialAngLocalVel; if (velLocal.sqrMagnitude > 0.001f) { torqueVector -= dampingMoment * nonAxialAngLocalVel + rollDampingMoment * axialAngLocalVel * axialAngLocalVel.magnitude / velLocal.sqrMagnitude; } else { torqueVector -= dampingMoment * nonAxialAngLocalVel + rollDampingMoment * axialAngLocalVel * axialAngLocalVel.magnitude / 0.001f; } forceVector *= data.dragFactor; torqueVector *= data.dragFactor; forceContext.ApplyForce(data, velLocal, forceVector, torqueVector); } }
private static double CalculateAreaExposed(ModularFlightIntegrator fi, Part part, FARAeroPartModule aeroModule) { if (aeroModule is null) { return(fi.BaseFICalculateAreaExposed(part)); } double exposedArea = aeroModule.ProjectedAreaLocal(-part.dragVectorDirLocal); return(exposedArea > 0 ? exposedArea : fi.BaseFICalculateAreaExposed(part)); }
public void UpdateAeroData(List <FARAeroPartModule> allUsedAeroModules, List <FARAeroPartModule> allUnusedAeroModules) { _aeroModulesWithIntakes.Clear(); _intakeModules.Clear(); _intakeTransforms.Clear(); _airBreathingEngines.Clear(); for (int i = 0; i < allUsedAeroModules.Count; i++) //get all exposed intakes and engines { FARAeroPartModule aeroModule = allUsedAeroModules[i]; if (aeroModule == null) { continue; } Part p = aeroModule.part; if (p.Modules.Contains("ModuleResourceIntake")) { ModuleResourceIntake intake = (ModuleResourceIntake)p.Modules["ModuleResourceIntake"]; _aeroModulesWithIntakes.Add(aeroModule); _intakeModules.Add(intake); _intakeTransforms.Add(p.FindModelTransform(intake.intakeTransformName)); } if (p.Modules.Contains("ModuleEngines")) { ModuleEngines engines = (ModuleEngines)p.Modules["ModuleEngines"]; for (int j = 0; j < engines.propellants.Count; j++) { Propellant prop = engines.propellants[j]; if (prop.name == "IntakeAir") { _airBreathingEngines.Add(engines); break; } } } if (p.Modules.Contains("ModuleEnginesFX")) { ModuleEnginesFX engines = (ModuleEnginesFX)p.Modules["ModuleEnginesFX"]; for (int j = 0; j < engines.propellants.Count; j++) { Propellant prop = engines.propellants[j]; if (prop.name == "IntakeAir") { _airBreathingEngines.Add(engines); break; } } } } for (int i = 0; i < allUnusedAeroModules.Count; i++) //get all covered airbreathing Engines { FARAeroPartModule aeroModule = allUnusedAeroModules[i]; if (aeroModule == null) { continue; } Part p = aeroModule.part; if (p.Modules.Contains("ModuleEngines")) { ModuleEngines engines = (ModuleEngines)p.Modules["ModuleEngines"]; for (int j = 0; j < engines.propellants.Count; j++) { Propellant prop = engines.propellants[j]; if (prop.name == "IntakeAir") { _airBreathingEngines.Add(engines); break; } } } if (p.Modules.Contains("ModuleEnginesFX")) { ModuleEnginesFX engines = (ModuleEnginesFX)p.Modules["ModuleEnginesFX"]; for (int j = 0; j < engines.propellants.Count; j++) { Propellant prop = engines.propellants[j]; if (prop.name == "IntakeAir") { _airBreathingEngines.Add(engines); break; } } } } }