From 5c811e0c41aabdd242f78907b7af1d0283c31750 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Wed, 14 Dec 2022 13:40:10 -0700 Subject: [PATCH 1/5] This is a rework of the parts of PR #766 related to railbutton aerodynamics. Drag calculations should be reasonably accurate up to transonic speeds --- .../barrowman/RailButtonCalc.java | 50 +++++++++++++++++++ .../rocketcomponent/RailButton.java | 6 --- 2 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java new file mode 100644 index 000000000..301566346 --- /dev/null +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java @@ -0,0 +1,50 @@ +package net.sf.openrocket.aerodynamics.barrowman; + +import net.sf.openrocket.aerodynamics.AerodynamicForces; +import net.sf.openrocket.aerodynamics.FlightConditions; +import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.RailButton; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.Transformation; + +public class RailButtonCalc extends RocketComponentCalc { + + private double refArea; + + public RailButtonCalc(RocketComponent component) { + super(component); + + final RailButton button = (RailButton) component; + + final double outerArea = button.getTotalHeight() * button.getOuterDiameter(); + final double notchArea = (button.getOuterDiameter() - button.getInnerDiameter()) * button.getInnerHeight(); + + refArea = (outerArea - notchArea) * button.getInstanceCount(); + } + + @Override + public double calculateFrictionCD(FlightConditions conditions, double componentCf, WarningSet warnings) { + // very small relative surface area, and slick + return 0.0; + } + + @Override + public void calculateNonaxialForces(FlightConditions conditions, Transformation transform, + AerodynamicForces forces, WarningSet warnings) { + // Nothing to be done + } + + @Override + public double calculatePressureCD(FlightConditions conditions, + double stagnationCD, double baseCD, WarningSet warnings) { + + // this is reasonably close for Reynolds numbers roughly 10e4 to 2*10e5, which takes us to low supersonic speeds. + // see Hoerner p. 3-9 fig 12, we summarizes a bunch of sources + // I expect we'll have compressibility effects having an impact well below that, so this is probably good up + // to the transonic regime. + double CDmul = 1.2; + + return CDmul*stagnationCD * refArea / conditions.getRefArea(); + } +} diff --git a/core/src/net/sf/openrocket/rocketcomponent/RailButton.java b/core/src/net/sf/openrocket/rocketcomponent/RailButton.java index 504de01e3..a25e616a0 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RailButton.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RailButton.java @@ -207,12 +207,6 @@ public class RailButton extends ExternalComponent implements AnglePositionable, clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - @Override - public boolean isAerodynamic(){ - // TODO: implement aerodynamics - return false; - } @Override public double getAngleOffset(){ From c9de2085f95bdf8bafcc2053ec6f408ddd0b3160 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Wed, 14 Dec 2022 14:38:38 -0700 Subject: [PATCH 2/5] add transonic warning --- core/resources/l10n/messages.properties | 2 +- core/src/net/sf/openrocket/aerodynamics/Warning.java | 2 ++ .../openrocket/aerodynamics/barrowman/RailButtonCalc.java | 8 +++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index c63e110ca..459cc134b 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -1865,7 +1865,7 @@ Warning.TUBE_SEPARATION = Space between tube fins may not result in accurate sim Warning.TUBE_OVERLAP = Overlapping tube fins may not result in accurate simulations. Warning.EMPTY_BRANCH = Simulation branch contains no data Warning.SEPARATION_ORDER = Stages separated in an unreasonable order - +Warning.RAILBUTTON_TRANSONIC = Railbutton drag calculations may be inaccurate at transonic or supersonic speeds ! Scale dialog ScaleDialog.lbl.scaleRocket = Entire rocket ScaleDialog.lbl.scaleSubselection = Selection and all subcomponents diff --git a/core/src/net/sf/openrocket/aerodynamics/Warning.java b/core/src/net/sf/openrocket/aerodynamics/Warning.java index ae7c94e35..2a3ff0eff 100644 --- a/core/src/net/sf/openrocket/aerodynamics/Warning.java +++ b/core/src/net/sf/openrocket/aerodynamics/Warning.java @@ -399,4 +399,6 @@ public abstract class Warning { public static final Warning SEPARATION_ORDER = new Other(trans.get("Warning.SEPARATION_ORDER")); public static final Warning EMPTY_BRANCH = new Other(trans.get("Warning.EMPTY_BRANCH")); + + public static final Warning RAILBUTTON_TRANSONIC = new Other(trans.get("Warning.RAILBUTTON_TRANSONIC")); } diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java index 301566346..d5b095b11 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java @@ -2,6 +2,7 @@ package net.sf.openrocket.aerodynamics.barrowman; import net.sf.openrocket.aerodynamics.AerodynamicForces; import net.sf.openrocket.aerodynamics.FlightConditions; +import net.sf.openrocket.aerodynamics.Warning; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.rocketcomponent.RailButton; import net.sf.openrocket.rocketcomponent.RocketComponent; @@ -44,7 +45,12 @@ public class RailButtonCalc extends RocketComponentCalc { // I expect we'll have compressibility effects having an impact well below that, so this is probably good up // to the transonic regime. double CDmul = 1.2; - + + // warn the user about accuracy if we're transonic (roughly Mach 0.8) or faster + if (conditions.getMach() > 0.8) { + warnings.add(Warning.RAILBUTTON_TRANSONIC); + } + return CDmul*stagnationCD * refArea / conditions.getRefArea(); } } From 56117261135e9dbdffbae11a074f3c8c1792ec53 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Fri, 16 Dec 2022 10:13:20 -0700 Subject: [PATCH 3/5] Implement full RailButton aerodynamics --- .../barrowman/RailButtonCalc.java | 81 +++++++++++++++---- 1 file changed, 64 insertions(+), 17 deletions(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java index d5b095b11..e76e5de00 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java @@ -1,27 +1,36 @@ package net.sf.openrocket.aerodynamics.barrowman; +import java.util.List; +import java.lang.Math; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import net.sf.openrocket.aerodynamics.AerodynamicForces; import net.sf.openrocket.aerodynamics.FlightConditions; import net.sf.openrocket.aerodynamics.Warning; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.rocketcomponent.RailButton; import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.Transformation; public class RailButtonCalc extends RocketComponentCalc { - - private double refArea; + private final static Logger log = LoggerFactory.getLogger(RailButtonCalc.class); + + // values transcribed from Gowen and Perkins, "Drag of Circular Cylinders for a Wide Range + // of Reynolds Numbers and Mach Numbers", NACA Technical Note 2960, Figure 7 + private static final List cdDomain = List.of(0.0, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 1.0, 1.6, 2.0, 2.8, 100.0); + private static final List cdRange = List.of(1.2, 1.22, 1.25, 1.3, 1.4, 1.5, 1.6, 2.1, 1.5, 1.45, 1.33, 1.33); + + private final RailButton button; public RailButtonCalc(RocketComponent component) { super(component); - final RailButton button = (RailButton) component; - - final double outerArea = button.getTotalHeight() * button.getOuterDiameter(); - final double notchArea = (button.getOuterDiameter() - button.getInnerDiameter()) * button.getInnerHeight(); - - refArea = (outerArea - notchArea) * button.getInstanceCount(); + // need to stash the button + button = (RailButton) component; } @Override @@ -38,17 +47,55 @@ public class RailButtonCalc extends RocketComponentCalc { @Override public double calculatePressureCD(FlightConditions conditions, - double stagnationCD, double baseCD, WarningSet warnings) { + double stagnationCD, double baseCD, WarningSet warnings) { + // grab relevant button params + + final int instanceCount = button.getInstanceCount(); + final Coordinate[] instanceOffsets = button.getInstanceOffsets(); - // this is reasonably close for Reynolds numbers roughly 10e4 to 2*10e5, which takes us to low supersonic speeds. - // see Hoerner p. 3-9 fig 12, we summarizes a bunch of sources - // I expect we'll have compressibility effects having an impact well below that, so this is probably good up - // to the transonic regime. - double CDmul = 1.2; + // compute button reference area + final double buttonHt = button.getTotalHeight(); + final double outerArea = buttonHt * button.getOuterDiameter(); + final double notchArea = (button.getOuterDiameter() - button.getInnerDiameter()) * button.getInnerHeight(); + final double refArea = outerArea - notchArea; - // warn the user about accuracy if we're transonic (roughly Mach 0.8) or faster - if (conditions.getMach() > 0.8) { - warnings.add(Warning.RAILBUTTON_TRANSONIC); + // accumulate Cd contribution from each rail button + double CDmul = 0.0; + for (int i = 0; i < button.getInstanceCount(); i++) { + + // compute boundary layer height at button location. I can't find a good reference for the + // formula, e.g. https://aerospaceengineeringblog.com/boundary-layers/ simply says it's the + // "scientific consensus". + double x = (button.toAbsolute(instanceOffsets[i]))[0].x; // location of button + double rex = calculateReynoldsNumber(x, conditions); // Reynolds number of button location + double del = 0.37 * x / Math.pow(rex, 0.2); // Boundary layer thickness + + // compute mean airspeed over button + // this assumes airspeed changes linearly through boundary layer + // and that all parts of the railbutton contribute equally to Cd, + // neither of which is true but both are plenty close enough for our purposes + + double mach; + if (buttonHt > del) { + // Case 1: button extends beyond boundary layer + // Mean velocity is 1/2 rocket velocity up to limit of boundary layer, + // full velocity after that + mach = (buttonHt - 0.5*del) * conditions.getMach()/buttonHt; + } else { + // Case 2: button is entirely within boundary layer + mach = MathUtil.map(buttonHt/2.0, 0, del, 0, conditions.getMach()); + } + + // look up Cd as function of speed. It's pretty constant as a function of Reynolds + // number when slow, so we can just use a function of Mach number + double cd = MathUtil.interpolate(cdDomain, cdRange, mach); + + // Since later drag force calculations don't consider boundary layer, compute "effective Cd" + // based on rocket velocity + cd = cd * MathUtil.pow2(mach)/MathUtil.pow2(conditions.getMach()); + + // add to CDmul + CDmul += cd; } return CDmul*stagnationCD * refArea / conditions.getRefArea(); From 8fd3e5869323e0f373997c49b463350bbe21586f Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Fri, 16 Dec 2022 10:15:52 -0700 Subject: [PATCH 4/5] Revert "add transonic warning". Implementing full aero instead This reverts commit c9de2085f95bdf8bafcc2053ec6f408ddd0b3160. --- core/resources/l10n/messages.properties | 2 +- core/src/net/sf/openrocket/aerodynamics/Warning.java | 2 -- .../sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 459cc134b..c63e110ca 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -1865,7 +1865,7 @@ Warning.TUBE_SEPARATION = Space between tube fins may not result in accurate sim Warning.TUBE_OVERLAP = Overlapping tube fins may not result in accurate simulations. Warning.EMPTY_BRANCH = Simulation branch contains no data Warning.SEPARATION_ORDER = Stages separated in an unreasonable order -Warning.RAILBUTTON_TRANSONIC = Railbutton drag calculations may be inaccurate at transonic or supersonic speeds + ! Scale dialog ScaleDialog.lbl.scaleRocket = Entire rocket ScaleDialog.lbl.scaleSubselection = Selection and all subcomponents diff --git a/core/src/net/sf/openrocket/aerodynamics/Warning.java b/core/src/net/sf/openrocket/aerodynamics/Warning.java index 2a3ff0eff..ae7c94e35 100644 --- a/core/src/net/sf/openrocket/aerodynamics/Warning.java +++ b/core/src/net/sf/openrocket/aerodynamics/Warning.java @@ -399,6 +399,4 @@ public abstract class Warning { public static final Warning SEPARATION_ORDER = new Other(trans.get("Warning.SEPARATION_ORDER")); public static final Warning EMPTY_BRANCH = new Other(trans.get("Warning.EMPTY_BRANCH")); - - public static final Warning RAILBUTTON_TRANSONIC = new Other(trans.get("Warning.RAILBUTTON_TRANSONIC")); } diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java index e76e5de00..a1800cf30 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java @@ -8,7 +8,6 @@ import org.slf4j.LoggerFactory; import net.sf.openrocket.aerodynamics.AerodynamicForces; import net.sf.openrocket.aerodynamics.FlightConditions; -import net.sf.openrocket.aerodynamics.Warning; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.rocketcomponent.RailButton; import net.sf.openrocket.rocketcomponent.RocketComponent; @@ -97,7 +96,6 @@ public class RailButtonCalc extends RocketComponentCalc { // add to CDmul CDmul += cd; } - return CDmul*stagnationCD * refArea / conditions.getRefArea(); } } From 955f00c8777879285a3f674849c54eaec458248a Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Sat, 17 Dec 2022 10:40:31 -0700 Subject: [PATCH 5/5] Add unit test --- .../barrowman/RailButtonCalc.java | 9 +- .../aerodynamics/RailButtonCalcTest.java | 101 ++++++++++++++++++ 2 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 core/test/net/sf/openrocket/aerodynamics/RailButtonCalcTest.java diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java index a1800cf30..a94bd76e5 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java @@ -47,8 +47,8 @@ public class RailButtonCalc extends RocketComponentCalc { @Override public double calculatePressureCD(FlightConditions conditions, double stagnationCD, double baseCD, WarningSet warnings) { + // grab relevant button params - final int instanceCount = button.getInstanceCount(); final Coordinate[] instanceOffsets = button.getInstanceOffsets(); @@ -65,10 +65,10 @@ public class RailButtonCalc extends RocketComponentCalc { // compute boundary layer height at button location. I can't find a good reference for the // formula, e.g. https://aerospaceengineeringblog.com/boundary-layers/ simply says it's the // "scientific consensus". - double x = (button.toAbsolute(instanceOffsets[i]))[0].x; // location of button + double x = (button.toAbsolute(instanceOffsets[i]))[0].x; // location of button double rex = calculateReynoldsNumber(x, conditions); // Reynolds number of button location double del = 0.37 * x / Math.pow(rex, 0.2); // Boundary layer thickness - + // compute mean airspeed over button // this assumes airspeed changes linearly through boundary layer // and that all parts of the railbutton contribute equally to Cd, @@ -96,6 +96,7 @@ public class RailButtonCalc extends RocketComponentCalc { // add to CDmul CDmul += cd; } - return CDmul*stagnationCD * refArea / conditions.getRefArea(); + + return CDmul * stagnationCD * refArea / conditions.getRefArea(); } } diff --git a/core/test/net/sf/openrocket/aerodynamics/RailButtonCalcTest.java b/core/test/net/sf/openrocket/aerodynamics/RailButtonCalcTest.java new file mode 100644 index 000000000..513b0c922 --- /dev/null +++ b/core/test/net/sf/openrocket/aerodynamics/RailButtonCalcTest.java @@ -0,0 +1,101 @@ +package net.sf.openrocket.aerodynamics; + +import static org.junit.Assert.assertEquals; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Module; + +import net.sf.openrocket.ServicesForTesting; +import net.sf.openrocket.aerodynamics.BarrowmanCalculator; +import net.sf.openrocket.aerodynamics.barrowman.RailButtonCalc; +import net.sf.openrocket.plugin.PluginModule; +import net.sf.openrocket.rocketcomponent.FlightConfiguration; +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.RailButton; +import net.sf.openrocket.rocketcomponent.LaunchLug; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.position.AxialMethod; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.TestRockets; +import net.sf.openrocket.util.Transformation; + +public class RailButtonCalcTest { + protected final double EPSILON = 0.0001; + + private static Injector injector; + @BeforeClass + public static void setup() { + Module applicationModule = new ServicesForTesting(); + Module pluginModule = new PluginModule(); + + injector = Guice.createInjector( applicationModule, pluginModule); + Application.setInjector(injector); + } + + @Test + public void testRailButtons() { + + Rocket rocket = TestRockets.makeEstesAlphaIII(); + FlightConfiguration config = rocket.getSelectedConfiguration(); + + // Get the body tube... + BodyTube tube = (BodyTube)rocket.getChild(0).getChild(1); + + // Replace the launch lug with a (single) railbutton + LaunchLug lug = (LaunchLug)tube.getChild(1); + rocket.removeChild(lug); + + RailButton button = new RailButton(); + tube.addChild(button); + + // Button parameters from Binder Design standard 1010 + button.setOuterDiameter(0.011); + button.setInnerDiameter(0.006); + + button.setBaseHeight(0.002); + button.setFlangeHeight(0.002); + button.setTotalHeight(0.008); + + button.setAxialMethod(AxialMethod.ABSOLUTE); + button.setAxialOffset(1.0); + + // Set up flight conditions + FlightConditions conditions = new FlightConditions(config); + conditions.setMach(1.0); + + BarrowmanCalculator barrowmanObj = new BarrowmanCalculator(); + RailButtonCalc calcObj = new RailButtonCalc(button); + + // Calculate effective CD for rail button + // Boundary layer height + double rex = calcObj.calculateReynoldsNumber(1.0, conditions); // Reynolds number of button location + double del = 0.37 * 1.0 / Math.pow(rex, 0.2); // Boundary layer height + + // Interpolate velocity at midpoint of railbutton + double mach = MathUtil.map(0.008/2.0, 0, del, 0, 1.0); + + // Interpolate to get CD + double cd = MathUtil.map(mach, 0.2, 0.3, 1.22, 1.25); + + // Reference area of rail button + final double outerArea = button.getTotalHeight() * button.getOuterDiameter(); + final double notchArea = (button.getOuterDiameter() - button.getInnerDiameter()) * button.getInnerHeight(); + final double refArea = outerArea - notchArea; + + // Get "effective" CD + double calccd = cd * MathUtil.pow2(mach) * barrowmanObj.calculateStagnationCD(conditions.getMach()) * refArea / conditions.getRefArea() ; + + // Now compare with value from RailButtonCalc + WarningSet warnings = new WarningSet(); + AerodynamicForces assemblyForces = new AerodynamicForces().zero(); + AerodynamicForces componentForces = new AerodynamicForces(); + + double testcd = calcObj.calculatePressureCD(conditions, barrowmanObj.calculateStagnationCD(conditions.getMach()), 0, warnings); + + assertEquals("Calculated rail button CD incorrect", calccd, testcd, EPSILON); + } +}