void UpdateThermodynamicsPre(ModularFI.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>(); //FARAeroPartModule aeroModule = (FARAeroPartModule)module; part.radiativeArea = CalculateAreaRadiative(fi, part, aeroModule); part.exposedArea = part.machNumber > 0 ? CalculateAreaExposed(fi, part, aeroModule) : part.radiativeArea; if (part.exposedArea > part.radiativeArea) { part.exposedArea = part.radiativeArea; //sanity check just in case } //fi.SetSkinProperties(ptd); } //fi.timeSinceLastUpdate = 0; //Debug.Log("MFI: " + fi.CoM + " " + Planetarium.GetUniversalTime()); }
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 } } }
double CalculateBodyArea(ModularFI.ModularFlightIntegrator fi, PartThermalData ptd) { FARAeroPartModule module = null; if (ptd.part.Modules.Contains <FARAeroPartModule>()) { module = ptd.part.Modules.GetModule <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)); } }
public static void UpdateThermodynamicsPre(ModularFI.ModularFlightIntegrator fi) { if (lastVessel != fi.Vessel || parts.Count != fi.partThermalDataList.Count) { parts.Clear(); lastVessel = fi.Vessel; for (int i = fi.partThermalDataList.Count; i-- > 0;) { var ptd = fi.partThermalDataList[i]; var part = ptd.part; if (part.GetComponent <ModuleNonReentryRated>()) { parts.Add(part); } } } for (int i = fi.partThermalDataList.Count; i-- > 0;) { PartThermalData ptd = fi.partThermalDataList[i]; var part = ptd.part; if (parts.Contains(part)) { ptd.convectionTempMultiplier = Math.Max(ptd.convectionTempMultiplier, 0.75d); ptd.convectionCoeffMultiplier = Math.Max(ptd.convectionCoeffMultiplier, 0.75d); ptd.convectionAreaMultiplier = Math.Max(ptd.convectionAreaMultiplier, 0.75d); ptd.postShockExtTemp = UtilMath.LerpUnclamped(part.vessel.atmosphericTemperature, part.vessel.externalTemperature, ptd.convectionTempMultiplier); ptd.finalCoeff = part.vessel.convectiveCoefficient * ptd.convectionArea * 0.001d * part.heatConvectiveConstant * ptd.convectionCoeffMultiplier; } } }
double CalculateSunArea(ModularFI.ModularFlightIntegrator fi, PartThermalData ptd) { FARAeroPartModule module = null; if (ptd.part.Modules.Contains <FARAeroPartModule>()) { module = ptd.part.Modules.GetModule <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)); } }
public override void UpdateRadiation(PartThermalData ptd) { if (updateRadiationOverride == null) { base.UpdateRadiation(ptd); } else { updateRadiationOverride(this, ptd); } }
public override double GetSunArea(PartThermalData ptd) { if (updateGetSunAreaOverride == null) { return(base.GetSunArea(ptd)); } else { return(updateGetSunAreaOverride(this, ptd)); } }
public override double GetBodyArea(PartThermalData ptd) { if (getBodyAreaOverride == null) { return(base.GetBodyArea(ptd)); } else { return(getBodyAreaOverride(this, ptd)); } }
public override void SetSkinProperties(PartThermalData ptd) { if (setSkinPropertiesOverride == null) { base.SetSkinProperties(ptd); } else { setSkinPropertiesOverride(this, 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)); }
public static void UpdateThermodynamicsPre(ModularFI.ModularFlightIntegrator fi) { for (int i = fi.partThermalDataList.Count; i-- > 0;) { PartThermalData ptd = fi.partThermalDataList[i]; if (ptd.part.maximum_drag == float.MinValue) { ptd.convectionTempMultiplier = Math.Max(ptd.convectionTempMultiplier, 0.5d); ptd.convectionTempMultiplier = Math.Max(ptd.convectionCoeffMultiplier, 0.5d); } } }
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 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 } } }
public static void UpdateThermodynamicsPre(ModularFI.ModularFlightIntegrator fi) { foreach (Part part in _registeredNonReentryParts) { if (part.vessel != fi.Vessel) { continue; } PartThermalData ptd = part.ptd; ptd.convectionTempMultiplier = Math.Max(ptd.convectionTempMultiplier, 0.75d); ptd.convectionCoeffMultiplier = Math.Max(ptd.convectionCoeffMultiplier, 0.75d); ptd.convectionAreaMultiplier = Math.Max(ptd.convectionAreaMultiplier, 0.75d); ptd.postShockExtTemp = UtilMath.LerpUnclamped(part.vessel.atmosphericTemperature, part.vessel.externalTemperature, ptd.convectionTempMultiplier); ptd.finalCoeff = part.vessel.convectiveCoefficient * ptd.convectionArea * 0.001d * part.heatConvectiveConstant * ptd.convectionCoeffMultiplier; } }
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; } var aeroModule = part.Modules.GetModule <FARAeroPartModule>(); part.radiativeArea = CalculateAreaRadiative(fi, part, aeroModule); part.exposedArea = part.machNumber > 0 ? CalculateAreaExposed(fi, part, aeroModule) : part.radiativeArea; if (part.exposedArea > part.radiativeArea) { part.exposedArea = part.radiativeArea; //sanity check just in case } } }
public void BaseFIetSkinPropertie(PartThermalData ptd) { base.SetSkinProperties(ptd); }
public double BaseFIBodyArea(PartThermalData ptd) { return(base.GetBodyArea(ptd)); }
//Update aerodynamics //Adapted from KSP Trajectories, Kerbal Wind Tunnel, FAR, and AeroGUI void UpdateAerodynamics(ModularFI.ModularFlightIntegrator fi, Part part) { Vessel v = FlightGlobals.ActiveVessel; wx_enabled = Util.getWindBool(); //Is weather enabled? use_climo = Util.useCLIM(); //Are we using the climatology use_point = Util.useWX(); //Are we using MPAS point time-series //Get main vessel and get reference to Kerbin (i.e. celestial body) kerbin = Util.getbody(); //Get wind vector (initialize to zero) Vector3d windVec = Vector3d.zero; if (use_climo) { //Retrieve wind vector from climatology windVec = KerbalWxClimo.getWSWind(); // .GetWind(FlightGlobals.currentMainBody, part, rb.position); } else if (use_point) { //Retrieve wind vector from point forecast windVec = KerbalWxPoint.getWSWind(); // .GetWind(FlightGlobals.currentMainBody, part, rb.position); } //Check to see if root part is in list. If not do not perform aero update. //This avoids updating aerodynamics of parts that have been decoupled and are no longer part of the active vessel. bool hasPart = false; for (int i = 0; i < fi.PartThermalDataCount; i++) { PartThermalData pttd = fi.partThermalDataList[i]; Part ppart = pttd.part; if (ppart == v.rootPart) { hasPart = true; } } if (!hasPart) { return; } //Get position of current vessel double vheight = v.altitude; double vlat = v.latitude; double vlng = v.longitude; double air_pressure; double air_density; double soundSpeed; //Define gamma (i.e. ratio between specific heat of dry air at constant pressure and specific heat of dry air at constant volume: cp/cv). double gamma = 1.4; //Util.Log("MFI wx_enabled: " + wx_enabled+", use_point: "+use_point+", use_climo: "+use_climo); bool inatmos = false; if ((FlightGlobals.ActiveVessel.mainBody == kerbin) && (v.altitude <= 70000) && (wx_enabled)) { if (use_climo) { //Retrieve air pressure/density at vessel location climate_api.wx_aero ptd = _clim_api.getPTD(vlat, vlng, vheight); air_density = ptd.density; air_pressure = ptd.pressure; } else { //Retrieve air pressure/density at vessel location weather_api.wx_aero ptd = _wx_api.getPTD(vheight); air_density = ptd.density; air_pressure = ptd.pressure; } //Compute speed of sound based on air pressure and air density. soundSpeed = Math.Sqrt(gamma * (air_pressure / air_density)); inatmos = true; } else { soundSpeed = v.speedOfSound; } //Get height from surface double hsfc = v.heightFromTerrain; //Determine if vessel is grounded (i.e. on the ground) bool isgrounded = true; if (hsfc >= 10) { isgrounded = false; } //If FAR is present FAR will handle aerodynamic updates or if we're not on Kerbin (i.e. in a different atmosphere) if (part.Modules.Contains <ModuleAeroSurface>() || (part.Modules.Contains("MissileLauncher") && part.vessel.rootPart == part) || (haveFAR) || (v.mainBody != kerbin) || (!inatmos)) { fi.BaseFIUpdateAerodynamics(part); if (((!part.DragCubes.None) && (!part.hasLiftModule)) || (part.name.Contains("kerbalEVA"))) { double drag = part.DragCubes.AreaDrag * PhysicsGlobals.DragCubeMultiplier * fi.pseudoReDragMult; float pscal = (float)(part.dynamicPressurekPa * drag * PhysicsGlobals.DragMultiplier); //Estimate lift force from body lift Vector3 lforce = part.transform.rotation * (part.bodyLiftScalar * part.DragCubes.LiftForce); lforce = Vector3.ProjectOnPlane(lforce, -part.dragVectorDir); } return; } else { fi.BaseFIUpdateAerodynamics(part); //Run base aerodynamic update first (fix bug where parachutes experience multiplicative acceleration) // Get rigid body Rigidbody rb = part.rb; if (rb) { if (part == v.rootPart) { v.mach = 0; } //Retrieve wind vector at vessel location //Vector3d windVec = KWPWind.GetWind(FlightGlobals.currentMainBody, part, rb.position); //Util.Log("MFI windVec: " + windVec + ", use_point: " + use_point + ", use_climo: " + use_climo); //Retrieve world velocity without wind vector Vector3 velocity_nowind = rb.velocity + Krakensbane.GetFrameVelocity(); //Compute mach number double mach_nowind = velocity_nowind.magnitude / soundSpeed; Vector3 drag_sum = Vector3.zero; Vector3 lift_sum = Vector3.zero; if (((!part.DragCubes.None) && (!part.hasLiftModule)) || (part.name.Contains("kerbalEVA"))) { //Estimate Drag force double pseudoreynolds = part.atmDensity * Math.Abs(velocity_nowind.magnitude); double pseudoReDragMult = PhysicsGlobals.DragCurvePseudoReynolds.Evaluate((float)pseudoreynolds); double drag = part.DragCubes.AreaDrag * PhysicsGlobals.DragCubeMultiplier * pseudoReDragMult; float pscal = (float)(part.dynamicPressurekPa * drag * PhysicsGlobals.DragMultiplier); drag_sum += -part.dragVectorDir * pscal; //Estimate lift force from body lift float pbodyLiftScalar = (float)(part.dynamicPressurekPa * part.bodyLiftMultiplier * PhysicsGlobals.BodyLiftMultiplier) * PhysicsGlobals.GetLiftingSurfaceCurve("BodyLift").liftMachCurve.Evaluate((float)part.machNumber); Vector3 lforce2 = part.transform.rotation * (pbodyLiftScalar * part.DragCubes.LiftForce); lforce2 = Vector3.ProjectOnPlane(lforce2, -part.dragVectorDir); lift_sum += lforce2; } //Util.Log("BodyLift Default: " + part.bodyLiftScalar + ", Lift Force: " + lift_force); //Loop through each part module to look for lifting surfaces for (int j = 0; j < part.Modules.Count; ++j) { var m = part.Modules[j]; if (m is ModuleLiftingSurface) { //Initialize force vectors Vector3 lforce = Vector3.zero; Vector3 dforce = Vector3.zero; //Convert kPa to Pa double liftQ = part.dynamicPressurekPa * 1000; ModuleLiftingSurface wing = (ModuleLiftingSurface)m; //Get wing coefficients Vector3 nVel = Vector3.zero; Vector3 liftVector = Vector3.zero; float liftdot; float absdot; wing.SetupCoefficients(velocity_nowind, out nVel, out liftVector, out liftdot, out absdot); //Get Lift/drag force lforce = wing.GetLiftVector(liftVector, liftdot, absdot, liftQ, (float)part.machNumber); dforce = wing.GetDragVector(nVel, absdot, liftQ); if (part.name.Contains("kerbalEVA")) { continue; } drag_sum += dforce; //lift_sum += lforce; if (isgrounded) { lift_sum += lforce; } } } //Retrieve world velocity and subtract off wind vector Vector3 velocity_wind = rb.velocity + Krakensbane.GetFrameVelocity() - windVec; //Compute mach number double mach_wind = velocity_wind.magnitude / soundSpeed; //Set mach number part.machNumber = mach_wind; // *mdiv; if (part == v.rootPart) { fi.mach = mach_wind; } //Get drag and lift forces with wind List <Vector3> total_forces = GetDragLiftForce(fi, part, velocity_wind, windVec, part.machNumber, isgrounded); //Compute difference in drag/lift with and without wind. Vector3 total_drag = total_forces[0] - drag_sum; Vector3 total_lift = total_forces[1] - lift_sum; //Calculate force due to wind Vector3 force = total_lift + total_drag; if (double.IsNaN(force.sqrMagnitude) || (((float)force.magnitude).NearlyEqual(0.0f))) { force = Vector3d.zero; } else { //Adapted from FAR - apply numerical control factor float numericalControlFactor = (float)(part.rb.mass * velocity_wind.magnitude * 0.67 / (force.magnitude * TimeWarp.fixedDeltaTime)); force *= Math.Min(numericalControlFactor, 1); part.AddForce(force); } v.mach = fi.mach; } } }
public double BaseFIGetSunArea(PartThermalData ptd) { return(base.GetSunArea(ptd)); }
public void UpdateThermodynamicsPre(ModularFI.ModularFlightIntegrator fi) { wx_enabled = Util.getWindBool(); use_climo = Util.useCLIM(); use_point = Util.useWX(); //Get reference to Kerbin (i.e. celestial body) kerbin = Util.getbody(); //Get vessel position Vessel v = FlightGlobals.ActiveVessel; double vheight = v.altitude; double vlat = v.latitude; double vlng = v.longitude; //Check to see if root part is in list. If not do not perform thermo update. //This avoids updating thermodynamics of parts that have been decoupled and are no longer part of the active vessel. bool hasPart = false; for (int i = 0; i < fi.PartThermalDataCount; i++) { PartThermalData pttd = fi.partThermalDataList[i]; Part part = pttd.part; if (part == v.rootPart) { hasPart = true; } } if (!hasPart) { return; } //Start out with Stock Thermodynamics before performing adjustments due to wind/weather. fi.BaseFIUpdateThermodynamics(); // Initialize External Temp and part drag/lift sum for GUI. // // This ensures GUI updates are smooth and don't bounce back and forth between stock aero and KWP aero updates // v.externalTemperature = fi.externalTemperature; if ((fi.CurrentMainBody != kerbin) || (v.altitude >= 70000)) { return; } //Check if in atmosphere if (wx_enabled) { //Define gamma (ratio of cp/cv) double gamma = 1.4; if (use_climo) { climate_api.wx_aero ptd = _clim_api.getPTD(vlat, vlng, vheight); //Retrieve pressure,temperature,and density from climate API //Adjust atmospheric constants CalculateConstantsAtmosphere_CLIMO(fi, ptd, v); } else { weather_api.wx_aero ptd = _wx_api.getPTD(vheight); //Retrieve pressure,temperature,and density from climate API //Adjust atmospheric constants CalculateConstantsAtmosphere_WX(fi, ptd, v); } // change density lerp double shockDensity = GetShockDensity(fi.density, fi.mach, gamma); fi.DensityThermalLerp = CalculateDensityThermalLerp(shockDensity); double lerpVal = fi.dynamicPressurekPa; if (lerpVal < 1d) { fi.convectiveCoefficient *= UtilMath.LerpUnclamped(0.1d, 1d, lerpVal); } // reset background temps fi.backgroundRadiationTemp = CalculateBackgroundRadiationTemperature(fi.atmosphericTemperature, fi.DensityThermalLerp); fi.backgroundRadiationTempExposed = CalculateBackgroundRadiationTemperature(fi.externalTemperature, fi.DensityThermalLerp); } else if (!wx_enabled) { fi.BaseFIUpdateThermodynamics(); } }
public void BaseFIUpdateRadiation(PartThermalData ptd) { base.UpdateRadiation(ptd); }