From e9b7ff82490650f4380a52a9381c88974e417ba6 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Tue, 14 Mar 2023 08:05:49 -0600 Subject: [PATCH 1/4] Divide rail button CD by instance count, since it gets multiplied by instance count up in BarrowmanCalculator. --- .../sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java index a94bd76e5..eff4e465e 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java @@ -96,6 +96,10 @@ public class RailButtonCalc extends RocketComponentCalc { // add to CDmul CDmul += cd; } + + // since we'll be multiplying by the instance count up in BarrowmanCalculator, + // we want to return the mean CD instead of the total + CDmul /= button.getInstanceCount(); return CDmul * stagnationCD * refArea / conditions.getRefArea(); } From 769ac693002025bab2edfc4520b0ec43ef61ec36 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Tue, 14 Mar 2023 21:37:17 -0600 Subject: [PATCH 2/4] Unit test for multiple instances of rail buttons --- .../aerodynamics/BarrowmanCalculatorTest.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java index f51b9ee99..09b2ba1bc 100644 --- a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java +++ b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java @@ -20,6 +20,7 @@ import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.NoseCone; import net.sf.openrocket.rocketcomponent.ParallelStage; import net.sf.openrocket.rocketcomponent.PodSet; +import net.sf.openrocket.rocketcomponent.RailButton; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.Transition; import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; @@ -496,5 +497,58 @@ public class BarrowmanCalculatorTest { testCP = testCalc.getCP(testConfig, testConditions, warnings).x; assertEquals("should be warning from podset airframe overlap", 1, warnings.size()); } + + /** + * Tests railbutton drag. Really is testing instancing more than actual drag calculations + */ + @Test + public void testRailButtonDrag() { + // minimal rocket with nothing on it but two railbuttons + final Rocket rocket = new Rocket(); + + final AxialStage stage = new AxialStage(); + rocket.addChild(stage); + + // phantom tubes have no drag to confuse things + final BodyTube phantom = new BodyTube(); + phantom.setOuterRadius(0); + stage.addChild(phantom); + + // set up test environment + WarningSet warnings = new WarningSet(); + final FlightConfiguration config = rocket.getSelectedConfiguration(); + final FlightConditions conditions = new FlightConditions(config); + final BarrowmanCalculator calc = new BarrowmanCalculator(); + + // Put two individual railbuttons and get their CD + final RailButton button1 = new RailButton(); + button1.setInstanceCount(1); + button1.setAxialOffset(1.0); + phantom.addChild(button1); + + final RailButton button2 = new RailButton(); + button2.setInstanceCount(1); + button2.setAxialOffset(2.0); + phantom.addChild(button2); + + final AerodynamicForces individualForces = calc.getAerodynamicForces(config, conditions, warnings); + final double individualCD = individualForces.getCD(); + + // get rid of individual buttons and put in a railbutton set with two instances at same locations as original + // railbuttons + phantom.removeChild(button1); + phantom.removeChild(button2); + + final RailButton buttons = new RailButton(); + buttons.setInstanceCount(2); + buttons.setAxialOffset(1.0); + buttons.setInstanceSeparation(1.0); + + final AerodynamicForces pairForces = calc.getAerodynamicForces(config, conditions, warnings); + final double pairCD = pairForces.getCD(); + + assertEquals("two individual railbuttons should have same CD as a pair", individualCD, pairCD, EPSILON); + } } + From 206603e86549fce2b4b9696ddfe4523eec368d70 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Sat, 18 Mar 2023 10:47:50 -0600 Subject: [PATCH 3/4] Avoid division by 0 by just setting railbutton pressure CD when moving very slow Note CD really doesn't matter when velocity is 0, since there's no drag force anyway. --- .../barrowman/RailButtonCalc.java | 84 ++++++++++--------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java index eff4e465e..26a279fbf 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/RailButtonCalc.java @@ -58,48 +58,56 @@ public class RailButtonCalc extends RocketComponentCalc { final double notchArea = (button.getOuterDiameter() - button.getInnerDiameter()) * button.getInnerHeight(); final double refArea = outerArea - notchArea; - // accumulate Cd contribution from each rail button + // accumulate Cd contribution from each rail button. If velocity is 0 just set CDmul to a value previously + // competed for velocity MathUtil.EPSILON and skip the loop to avoid division by 0 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()); + if (conditions.getMach() > MathUtil.EPSILON) { + 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; + } - // 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()); + // since we'll be multiplying by the instance count up in BarrowmanCalculator, + // we want to return the mean CD instead of the total + CDmul /= button.getInstanceCount(); - // add to CDmul - CDmul += cd; - } - - // since we'll be multiplying by the instance count up in BarrowmanCalculator, - // we want to return the mean CD instead of the total - CDmul /= button.getInstanceCount(); + } else { + // value at velocity of MathUtil.EPSILON + CDmul = 8.786395072609939E-4; + } return CDmul * stagnationCD * refArea / conditions.getRefArea(); } From c3e87bb1383b9d7393a32331f5dc8815e2ec0cc3 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Sat, 18 Mar 2023 10:49:26 -0600 Subject: [PATCH 4/4] Add unit test for railbutton drag when velocity is 0 --- .../aerodynamics/BarrowmanCalculatorTest.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java index 09b2ba1bc..a3ec7c07b 100644 --- a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java +++ b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java @@ -26,6 +26,7 @@ import net.sf.openrocket.rocketcomponent.Transition; import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.TestRockets; public class BarrowmanCalculatorTest { @@ -499,7 +500,8 @@ public class BarrowmanCalculatorTest { } /** - * Tests railbutton drag. Really is testing instancing more than actual drag calculations + * Tests railbutton drag. Really is testing instancing more than actual drag calculations, and making + * sure we don't divide by 0 when not moving */ @Test public void testRailButtonDrag() { @@ -519,6 +521,8 @@ public class BarrowmanCalculatorTest { final FlightConfiguration config = rocket.getSelectedConfiguration(); final FlightConditions conditions = new FlightConditions(config); final BarrowmanCalculator calc = new BarrowmanCalculator(); + + // part 1: instancing // Put two individual railbuttons and get their CD final RailButton button1 = new RailButton(); @@ -548,7 +552,16 @@ public class BarrowmanCalculatorTest { final double pairCD = pairForces.getCD(); assertEquals("two individual railbuttons should have same CD as a pair", individualCD, pairCD, EPSILON); + + // part 2: test at Mach 0 + conditions.setMach(MathUtil.EPSILON); + final AerodynamicForces epsForces = calc.getAerodynamicForces(config, conditions, warnings); + final double epsCD = epsForces.getCD(); + + conditions.setMach(0); + final AerodynamicForces zeroForces = calc.getAerodynamicForces(config, conditions, warnings); + final double zeroCD = zeroForces.getCD(); + assertEquals("drag at mach 0 should equal drag at mach MathUtil.EPSILON", epsCD, zeroCD, EPSILON); } - }