From 5483c3e6590ed20916e3c3ddfe47acba94e954fe Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Thu, 19 Mar 2020 11:01:14 -0600 Subject: [PATCH 01/12] Correct calculateFrictionDrag() to iterate through all instances of components --- .../aerodynamics/BarrowmanCalculator.java | 148 +++++++++--------- 1 file changed, 76 insertions(+), 72 deletions(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index 4667476c8..634a8ecf3 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -392,84 +392,90 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { double[] roughnessLimited = new double[Finish.values().length]; Arrays.fill(roughnessLimited, Double.NaN); - - for (RocketComponent c : configuration.getActiveComponents()) { + + final InstanceMap imap = configuration.getActiveInstances(); + for(Map.Entry> entry: imap.entrySet() ) { + final RocketComponent c = entry.getKey(); // Consider only SymmetricComponents and FinSets: if (!(c instanceof SymmetricComponent) && !(c instanceof FinSet)) continue; - - // Calculate the roughness-limited friction coefficient - Finish finish = ((ExternalComponent) c).getFinish(); - if (Double.isNaN(roughnessLimited[finish.ordinal()])) { - roughnessLimited[finish.ordinal()] = - 0.032 * Math.pow(finish.getRoughnessSize() / configuration.getLength(), 0.2) * - roughnessCorrection; - } - - /* - * Actual Cf is maximum of Cf and the roughness-limited value. - * For perfect finish require additionally that Re > 1e6 - */ - double componentCf; - if (configuration.getRocket().isPerfectFinish()) { - - // For perfect finish require Re > 1e6 - if ((Re > 1.0e6) && (roughnessLimited[finish.ordinal()] > Cf)) { - componentCf = roughnessLimited[finish.ordinal()]; - } else { - componentCf = Cf; - } - - } else { - - // For fully turbulent use simple max - componentCf = Math.max(Cf, roughnessLimited[finish.ordinal()]); - - } - - //Handle Overriden CD for Whole Rocket - if(c.isCDOverridden()) { - continue; - } - - - // Calculate the friction drag: - if (c instanceof SymmetricComponent) { - - SymmetricComponent s = (SymmetricComponent) c; - - bodyFriction += componentCf * s.getComponentWetArea(); - - if (map != null) { - // Corrected later - map.get(c).setFrictionCD(componentCf * s.getComponentWetArea() - / conditions.getRefArea()); - } - - double r = Math.max(s.getForeRadius(), s.getAftRadius()); - if (r > maxR) - maxR = r; - len += c.getLength(); - - } else if (c instanceof FinSet) { - - FinSet f = (FinSet) c; - double mac = ((FinSetCalc) calcMap.get(c)).getMACLength(); - double cd = componentCf * (1 + 2 * f.getThickness() / mac) * - 2 * f.getFinCount() * f.getPlanformArea(); - finFriction += cd; - - if (map != null) { - map.get(c).setFrictionCD(cd / conditions.getRefArea()); - } - - } - + // iterate across component instances + final ArrayList contextList = entry.getValue(); + for(InstanceContext context: contextList ) { + // Calculate the roughness-limited friction coefficient + Finish finish = ((ExternalComponent) c).getFinish(); + if (Double.isNaN(roughnessLimited[finish.ordinal()])) { + roughnessLimited[finish.ordinal()] = + 0.032 * Math.pow(finish.getRoughnessSize() / configuration.getLength(), 0.2) * + roughnessCorrection; + } + + /* + * Actual Cf is maximum of Cf and the roughness-limited value. + * For perfect finish require additionally that Re > 1e6 + */ + double componentCf; + if (configuration.getRocket().isPerfectFinish()) { + + // For perfect finish require Re > 1e6 + if ((Re > 1.0e6) && (roughnessLimited[finish.ordinal()] > Cf)) { + componentCf = roughnessLimited[finish.ordinal()]; + } else { + componentCf = Cf; + } + + } else { + + // For fully turbulent use simple max + componentCf = Math.max(Cf, roughnessLimited[finish.ordinal()]); + + } + + //Handle Overriden CD for Whole Rocket + if(c.isCDOverridden()) { + continue; + } + + + // Calculate the friction drag: + if (c instanceof SymmetricComponent) { + + SymmetricComponent s = (SymmetricComponent) c; + + bodyFriction += componentCf * s.getComponentWetArea(); + + if (map != null) { + // Corrected later + map.get(c).setFrictionCD(componentCf * s.getComponentWetArea() + / conditions.getRefArea()); + } + + double r = Math.max(s.getForeRadius(), s.getAftRadius()); + if (r > maxR) + maxR = r; + len += c.getLength(); + + } else if (c instanceof FinSet) { + + FinSet f = (FinSet) c; + double mac = ((FinSetCalc) calcMap.get(c)).getMACLength(); + double cd = componentCf * (1 + 2 * f.getThickness() / mac) * + 2 * f.getPlanformArea(); + finFriction += cd; + + if (map != null) { + map.get(c).setFrictionCD(cd / conditions.getRefArea()); + } + + } + + } } + // fB may be POSITIVE_INFINITY, but that's ok for us double fB = (len + 0.0001) / maxR; double correction = (1 + 1.0 / (2 * fB)); @@ -482,8 +488,6 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { } } } - - return (finFriction + correction * bodyFriction) / conditions.getRefArea(); } From 05185ff3d6f76cb8cf0adcf76412b20df32d804c Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Thu, 19 Mar 2020 11:13:40 -0600 Subject: [PATCH 02/12] Correct calculatePressureDrag() to iterate through all instances. --- .../aerodynamics/BarrowmanCalculator.java | 59 +++++++++++-------- .../aerodynamics/barrowman/FinSetCalc.java | 2 +- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index 634a8ecf3..6cbf1ca04 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -402,7 +402,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { !(c instanceof FinSet)) continue; - // iterate across component instances + // iterate across component instances final ArrayList contextList = entry.getValue(); for(InstanceContext context: contextList ) { @@ -515,39 +515,46 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { base = calculateBaseCD(conditions.getMach()); total = 0; - for (RocketComponent c : configuration.getActiveComponents()) { + final InstanceMap imap = configuration.getActiveInstances(); + for(Map.Entry> entry: imap.entrySet() ) { + final RocketComponent c = entry.getKey(); if (!c.isAerodynamic()) continue; - - // Pressure fore drag - double cd = calcMap.get(c).calculatePressureDragForce(conditions, stagnation, base, - warnings); - total += cd; - - if (map != null) { - map.get(c).setPressureCD(cd); - } - - if(c.isCDOverridden()) continue; - - // Stagnation drag - if (c instanceof SymmetricComponent) { - SymmetricComponent s = (SymmetricComponent) c; + // iterate across component instances + final ArrayList contextList = entry.getValue(); + for(InstanceContext context: contextList ) { - if (radius < s.getForeRadius()) { - double area = Math.PI * (pow2(s.getForeRadius()) - pow2(radius)); - cd = stagnation * area / conditions.getRefArea(); - total += cd; - if (map != null) { - map.get(c).setPressureCD(map.get(c).getPressureCD() + cd); - } + // Pressure fore drag + double cd = calcMap.get(c).calculatePressureDragForce(conditions, stagnation, base, + warnings); + total += cd; + + if (map != null) { + map.get(c).setPressureCD(cd); } - radius = s.getAftRadius(); + if(c.isCDOverridden()) continue; + + + // Stagnation drag + if (c instanceof SymmetricComponent) { + SymmetricComponent s = (SymmetricComponent) c; + + if (radius < s.getForeRadius()) { + double area = Math.PI * (pow2(s.getForeRadius()) - pow2(radius)); + cd = stagnation * area / conditions.getRefArea(); + total += cd; + if (map != null) { + map.get(c).setPressureCD(map.get(c).getPressureCD() + cd); + } + } + + radius = s.getAftRadius(); + } } } - + return total; } diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java index 8f7b80c51..c7d2b9754 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/FinSetCalc.java @@ -645,7 +645,7 @@ public class FinSetCalc extends RocketComponentCalc { // Airfoil assumed to have zero base drag // Scale to correct reference area - drag *= finCount * span * thickness / conditions.getRefArea(); + drag *= span * thickness / conditions.getRefArea(); return drag; } From dd3bda8d7e3a7084a649c2e2b68e8a94ab4663ec Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Fri, 20 Mar 2020 14:53:39 -0600 Subject: [PATCH 03/12] Corrected calculatePressureDrag() to iterate through all instances. Note this required modifying the function to explicitly get the radius of the forward symmetric component rather than simply iterating and assuming components would appear in order (as was the case with iterating through getActiveComponents()). The getPreviousSymmetricComponent() method contains a warning that it doesn't account for external pods, so this will probably need to be revisited and that method modified. --- .../aerodynamics/BarrowmanCalculator.java | 16 +++++++++------- .../rocketcomponent/SymmetricComponent.java | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index 6cbf1ca04..880db8151 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -506,7 +506,6 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { Map map, WarningSet warnings) { double stagnation, base, total; - double radius = 0; if (calcMap == null) buildCalcMap(configuration); @@ -524,33 +523,36 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { // iterate across component instances final ArrayList contextList = entry.getValue(); for(InstanceContext context: contextList ) { - + // Pressure fore drag double cd = calcMap.get(c).calculatePressureDragForce(conditions, stagnation, base, warnings); total += cd; - + if (map != null) { map.get(c).setPressureCD(cd); } if(c.isCDOverridden()) continue; - // Stagnation drag if (c instanceof SymmetricComponent) { SymmetricComponent s = (SymmetricComponent) c; + + double radius = 0; + final SymmetricComponent prevComponent = s.getPreviousSymmetricComponent(); + if (prevComponent != null) + radius = prevComponent.getAftRadius(); if (radius < s.getForeRadius()) { double area = Math.PI * (pow2(s.getForeRadius()) - pow2(radius)); cd = stagnation * area / conditions.getRefArea(); total += cd; + if (map != null) { map.get(c).setPressureCD(map.get(c).getPressureCD() + cd); } } - - radius = s.getAftRadius(); } } } @@ -558,7 +560,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { return total; } - + /** * Calculation of drag coefficient due to base * diff --git a/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java b/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java index a530bfe94..35957c459 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java @@ -566,7 +566,7 @@ public abstract class SymmetricComponent extends BodyComponent implements Radial * * @return the previous SymmetricComponent, or null. */ - protected final SymmetricComponent getPreviousSymmetricComponent() { + public final SymmetricComponent getPreviousSymmetricComponent() { RocketComponent c; for (c = this.getPreviousComponent(); c != null; c = c.getPreviousComponent()) { if (c instanceof SymmetricComponent) { From 5e5ce3d06ea794d67b6c867ca12b69fa186c8ad8 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Mon, 23 Mar 2020 13:48:49 -0600 Subject: [PATCH 04/12] Corrected calculateBaseDrag to iterate through all instances of components Corrected getPreviousSymmetricComponent and getNextSymmetricComponent to stop searching and return null when a PodSet is encountered in search (so it only returns the previous/next component if it's a child of the same pod). --- .../aerodynamics/BarrowmanCalculator.java | 67 +++++++++++-------- .../rocketcomponent/SymmetricComponent.java | 16 +++-- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index 880db8151..e0c0aab8d 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -571,11 +571,9 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { * @return */ private double calculateBaseDrag(FlightConfiguration configuration, FlightConditions conditions, - Map map, WarningSet warnings) { + Map map, WarningSet warnings) { double base, total; - double radius = 0; - RocketComponent prevComponent = null; if (calcMap == null) buildCalcMap(configuration); @@ -583,36 +581,51 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { base = calculateBaseCD(conditions.getMach()); total = 0; - for (RocketComponent c : configuration.getActiveComponents()) { + final InstanceMap imap = configuration.getActiveInstances(); + for(Map.Entry> entry: imap.entrySet() ) { + final RocketComponent c = entry.getKey(); + if (!(c instanceof SymmetricComponent)) continue; SymmetricComponent s = (SymmetricComponent) c; - - if(c.isCDOverridden()) { - total += c.getOverrideCD(); - continue; - } - if (radius > s.getForeRadius()) { - double area = Math.PI * (pow2(radius) - pow2(s.getForeRadius())); - double cd = base * area / conditions.getRefArea(); - total += cd; - if (map != null) { - map.get(prevComponent).setBaseCD(cd); + // iterate across component instances + final ArrayList contextList = entry.getValue(); + for(InstanceContext context: contextList ) { + if(c.isCDOverridden()) { + total += c.getOverrideCD(); + continue; + } + + // if aft radius of previous component is greater than my forward radius, set + // its aft CD + double radius = 0; + final SymmetricComponent prevComponent = s.getPreviousSymmetricComponent(); + if (prevComponent != null) { + radius = prevComponent.getAftRadius(); + } + + if (radius > s.getForeRadius()) { + double area = Math.PI * (pow2(radius) - pow2(s.getForeRadius())); + double cd = base * area / conditions.getRefArea(); + total += cd; + if ((map != null) && (prevComponent != null)) { + map.get(prevComponent).setBaseCD(cd); + } + } + + // if I'm the last componenet, set my base CD + // note I can't depend on the iterator serving up components in order, + // so I can't just do this after the last iteration. + if (s.getNextSymmetricComponent() == null) { + double area = Math.PI * pow2(s.getAftRadius()); + double cd = base * area / conditions.getRefArea(); + total += cd; + if (map != null) { + map.get(s).setBaseCD(cd); + } } - } - - radius = s.getAftRadius(); - prevComponent = c; - } - - if (radius > 0) { - double area = Math.PI * pow2(radius); - double cd = base * area / conditions.getRefArea(); - total += cd; - if (map != null) { - map.get(prevComponent).setBaseCD(cd); } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java b/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java index 35957c459..64958596e 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/SymmetricComponent.java @@ -7,6 +7,7 @@ import java.util.Collection; import java.util.List; import net.sf.openrocket.preset.ComponentPreset; +import net.sf.openrocket.rocketcomponent.PodSet; import net.sf.openrocket.rocketcomponent.position.AxialMethod; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; @@ -561,34 +562,37 @@ public abstract class SymmetricComponent extends BodyComponent implements Radial /** * Return the previous symmetric component, or null if none exists. - * NOTE: This method currently assumes that there are no external - * "pods". * * @return the previous SymmetricComponent, or null. */ public final SymmetricComponent getPreviousSymmetricComponent() { RocketComponent c; for (c = this.getPreviousComponent(); c != null; c = c.getPreviousComponent()) { + if (c instanceof PodSet) { + return null; + } if (c instanceof SymmetricComponent) { return (SymmetricComponent) c; } if (!(c instanceof AxialStage) && - (c.axialMethod == AxialMethod.AFTER)) + (c.axialMethod == AxialMethod.AFTER)) { return null; // Bad component type as "parent" + } } return null; } /** * Return the next symmetric component, or null if none exists. - * NOTE: This method currently assumes that there are no external - * "pods". * * @return the next SymmetricComponent, or null. */ - protected final SymmetricComponent getNextSymmetricComponent() { + public final SymmetricComponent getNextSymmetricComponent() { RocketComponent c; for (c = this.getNextComponent(); c != null; c = c.getNextComponent()) { + if (c instanceof PodSet) { + return null; + } if (c instanceof SymmetricComponent) { return (SymmetricComponent) c; } From 9845decb2056416f723c9445dab7ee10627942e8 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Wed, 25 Mar 2020 14:42:37 -0600 Subject: [PATCH 05/12] Adjust calculation of axial length (for fineness friction adjustment) to be from tip of rocket to aft-most axial component instead of simply adding all axial component lengths together. Necessary so body tubes on pods don't add to length incorrectly. TODO: I suspect we actually ought to be calculating the fineness of the tubes on each pod and adjusting friction on each of them accordinging. --- .../aerodynamics/BarrowmanCalculator.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index e0c0aab8d..37a49a1d6 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -388,7 +388,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { double finFriction = 0; double bodyFriction = 0; - double maxR = 0, len = 0; + double maxR = 0, minX = Double.MAX_VALUE, maxX = 0; double[] roughnessLimited = new double[Finish.values().length]; Arrays.fill(roughnessLimited, Double.NaN); @@ -453,11 +453,15 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { map.get(c).setFrictionCD(componentCf * s.getComponentWetArea() / conditions.getRefArea()); } - - double r = Math.max(s.getForeRadius(), s.getAftRadius()); - if (r > maxR) - maxR = r; - len += c.getLength(); + + final double componentMinX = context.getLocation().x; + minX = Math.min(minX, componentMinX); + + final double componentMaxX = componentMinX + c.getLength(); + maxX = Math.max(maxX, componentMaxX); + + final double componentMaxR = Math.max(s.getForeRadius(), s.getAftRadius()); + maxR = Math.max(maxR, componentMaxR); } else if (c instanceof FinSet) { @@ -477,7 +481,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { } // fB may be POSITIVE_INFINITY, but that's ok for us - double fB = (len + 0.0001) / maxR; + double fB = (maxX - minX + 0.0001) / maxR; double correction = (1 + 1.0 / (2 * fB)); // Correct body data in map From 00abce96ffc418c121517f1c54c7ec4a414c6a95 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Wed, 25 Mar 2020 14:52:52 -0600 Subject: [PATCH 06/12] Add unit test "testPhantomTubes" comparing a simple 3FNC rocket against the same rocket, implemented by replacing the fins by three pods, putting a 0-diameter body tube on each pod, and putting one fin on each pod. --- .../net/sf/openrocket/util/TestRockets.java | 86 ++++++++++++++++++- .../aerodynamics/BarrowmanCalculatorTest.java | 46 ++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/core/src/net/sf/openrocket/util/TestRockets.java b/core/src/net/sf/openrocket/util/TestRockets.java index 6c97eeb5c..bcf02c910 100644 --- a/core/src/net/sf/openrocket/util/TestRockets.java +++ b/core/src/net/sf/openrocket/util/TestRockets.java @@ -1,13 +1,16 @@ package net.sf.openrocket.util; +import java.util.ArrayList; +import java.io.FileOutputStream; +import java.util.Map; import java.util.Random; import net.sf.openrocket.appearance.Appearance; -import net.sf.openrocket.file.openrocket.OpenRocketSaver; import net.sf.openrocket.database.Databases; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.OpenRocketDocumentFactory; import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.file.openrocket.OpenRocketSaver; import net.sf.openrocket.material.Material; import net.sf.openrocket.material.Material.Type; import net.sf.openrocket.motor.Manufacturer; @@ -34,6 +37,8 @@ import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.FlightConfigurationId; import net.sf.openrocket.rocketcomponent.FreeformFinSet; import net.sf.openrocket.rocketcomponent.InnerTube; +import net.sf.openrocket.rocketcomponent.InstanceContext; +import net.sf.openrocket.rocketcomponent.InstanceMap; import net.sf.openrocket.rocketcomponent.InternalComponent; import net.sf.openrocket.rocketcomponent.LaunchLug; import net.sf.openrocket.rocketcomponent.MassComponent; @@ -1629,5 +1634,84 @@ public class TestRockets { rocket.enableEvents(); return rocketDoc; } + + // the following two models are used in testing + // otherwise-identical rockets, one created in the obvious way + // using a single finset and the other creating three pods, each + // with a single fin. + public static final Rocket make3FNCNoPods() { + + Rocket rocket = new Rocket(); + + rocket.enableEvents(); + + AxialStage stage = new AxialStage(); + stage.setName("Sustainer"); + rocket.addChild(stage); + + // shape, length, radius + NoseCone nosecone = new NoseCone(Transition.Shape.OGIVE, 0.102, 0.0125); + stage.addChild(nosecone); + + // length, outer radius, thickness + BodyTube bodytube = new BodyTube(0.305, 0.0125, 0.001); + bodytube.setName("Main Body"); + stage.addChild(bodytube); + + // number of fins, root chord, tip chord, sweep, height + TrapezoidFinSet trapezoidfinset = new TrapezoidFinSet(3, 0.051, 0.025, 0.038, 0.044); + bodytube.addChild(trapezoidfinset); + + // This is how we can dump a test rocket so we can look at it in OR to better + // visualize it + // + // OpenRocketDocument doc = OpenRocketDocumentFactory.createDocumentFromRocket(rocket); + // OpenRocketSaver saver = new OpenRocketSaver(); + // try { + // FileOutputStream str = new FileOutputStream("3fnc.ork"); + // saver.save(str, doc, null); + // } + // catch (Exception e) { + // System.err.println("exception " + e); + // } + + return rocket; + } + + // second model used to test with/without pods. In order to + // maintain consistency between the models, we'll create the + // no-pods first, and then modify it to make the with-pods version + public static final Rocket make3FNCWithPods() { + Rocket rocket = TestRockets.make3FNCNoPods(); + + // find the body and fins + final InstanceMap imap = rocket.getSelectedConfiguration().getActiveInstances(); + for(Map.Entry> entry: imap.entrySet() ) { + RocketComponent c = entry.getKey(); + if (c instanceof TrapezoidFinSet) { + final TrapezoidFinSet fins = (TrapezoidFinSet) c; + final BodyTube body = (BodyTube) fins.getParent(); + body.removeChild(fins); + + // create a PodSet to hook the fins to + PodSet podset = new PodSet(); + podset.setInstanceCount(fins.getFinCount()); + + body.addChild(podset); + + // put a phantom body tube on the pods + BodyTube podBody = new BodyTube(fins.getRootChord(), 0); + podBody.setName("Pod Body"); + podset.addChild(podBody); + + // change the number of fins to 1 and put the revised + // finset on the podbody + fins.setFinCount(1); + podBody.addChild(fins); + } + } + + return rocket; + } } diff --git a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java index ebcc07d78..2751c5234 100644 --- a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java +++ b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java @@ -231,4 +231,50 @@ public class BarrowmanCalculatorTest { assertFalse(" Missed discontinuity in Falcon 9 Heavy:", calc.isContinuous( rocket)); } + + @Test + public void testPhantomTubes() { + Rocket rocketNoPods = TestRockets.make3FNCNoPods(); + FlightConfiguration configNoPods = rocketNoPods.getSelectedConfiguration(); + FlightConditions conditionsNoPods = new FlightConditions(configNoPods); + WarningSet warningsNoPods = new WarningSet(); + + Rocket rocketWithPods = TestRockets.make3FNCWithPods(); + FlightConfiguration configPods = rocketWithPods.getSelectedConfiguration(); + FlightConditions conditionsPods = new FlightConditions(configPods); + WarningSet warningsPods = new WarningSet(); + AerodynamicCalculator calcPods = new BarrowmanCalculator(); + AerodynamicCalculator calcNoPods = new BarrowmanCalculator(); + + final AerodynamicForces forcesNoPods = calcPods.getAerodynamicForces(configNoPods, conditionsNoPods, warningsNoPods); + final AerodynamicForces forcesPods = calcPods.getAerodynamicForces(configPods, conditionsPods, warningsPods); + assertEquals(" 3FNC With Pods rocket CD value is incorrect:", forcesPods.getCD(), forcesNoPods.getCD(), EPSILON); + + // The "with pods" version has no way of seeing the fins are + // on the actual body tube rather than the phantom tubes, + // so CD won't take fin-body interference into consideration. + // So we'll adjust our CD in these tests. The magic numbers + // in x and w come from temporarily disabling the + // interference calculation in FinSetCalc and comparing + // results with and without it + // cpNoPods (0.34125,0.00000,0.00000,w=16.20502) -- interference disabled + // cpNoPods (0.34797,0.00000,0.00000,w=19.34773) -- interference enabled + + // another note: the fact that this is seen as three one-fin + // FinSets instead of a single three-fin FinSet means the CP + // will be off-axis (one of the fins is taken as having an + // angle of 0 to the airstream, so it has no contribution). + // This doesn't turn out to cause a problem in an actual + // simulation, so we are just not testing for it. Test with + // correction if we want some time is here but commented out + + final Coordinate cpNoPods = calcNoPods.getCP(configNoPods, conditionsNoPods, warningsNoPods); + final Coordinate cpPods = calcPods.getCP(configPods, conditionsPods, warningsPods); + + assertEquals(" 3FNC With Pods rocket cp x value is incorrect:", cpPods.x, cpNoPods.x - 0.00672, EPSILON); + // assertEquals(" 3FNC With Pods rocket cp y value is incorrect:", cpPods.y, cpNoPods.y - 0.00548, EPSILON); + // assertEquals(" 3FNC With Pods rocket cp z value is incorrect:", cpPods.z, cpNoPods.z, EPSILON); + assertEquals(" 3FNC With Pods rocket CNa value is incorrect:", cpPods.weight, cpNoPods.weight - 3.14271, EPSILON); + } + } From 202bb5f1e4345f85ed7776a483de46f02f8920ce Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Sat, 28 Mar 2020 15:05:10 -0600 Subject: [PATCH 07/12] style point -- moved a "continue;" to its own line for consistency --- .../net/sf/openrocket/aerodynamics/BarrowmanCalculator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index 37a49a1d6..865335609 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -537,7 +537,8 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { map.get(c).setPressureCD(cd); } - if(c.isCDOverridden()) continue; + if(c.isCDOverridden()) + continue; // Stagnation drag if (c instanceof SymmetricComponent) { From 6f9c023b1eca18528fe7300c9cb09c27d164c361 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Sat, 28 Mar 2020 15:22:58 -0600 Subject: [PATCH 08/12] commented out an import to avoid "unused import" warnings due to code commented out --- core/src/net/sf/openrocket/util/TestRockets.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/net/sf/openrocket/util/TestRockets.java b/core/src/net/sf/openrocket/util/TestRockets.java index bcf02c910..1d62cbd27 100644 --- a/core/src/net/sf/openrocket/util/TestRockets.java +++ b/core/src/net/sf/openrocket/util/TestRockets.java @@ -1,7 +1,7 @@ package net.sf.openrocket.util; import java.util.ArrayList; -import java.io.FileOutputStream; +// import java.io.FileOutputStream; import java.util.Map; import java.util.Random; From 67d507c4b4e46ee7a1daa7cb745ecb5be79eeabc Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Sat, 28 Mar 2020 18:11:12 -0600 Subject: [PATCH 09/12] Revert "commented out an import to avoid "unused import" warnings due to code commented out" This reverts commit 6f9c023b1eca18528fe7300c9cb09c27d164c361. --- core/src/net/sf/openrocket/util/TestRockets.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/net/sf/openrocket/util/TestRockets.java b/core/src/net/sf/openrocket/util/TestRockets.java index 1d62cbd27..bcf02c910 100644 --- a/core/src/net/sf/openrocket/util/TestRockets.java +++ b/core/src/net/sf/openrocket/util/TestRockets.java @@ -1,7 +1,7 @@ package net.sf.openrocket.util; import java.util.ArrayList; -// import java.io.FileOutputStream; +import java.io.FileOutputStream; import java.util.Map; import java.util.Random; From b1643e4d14dc6fbfaf646d557ae6356506633686 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Sat, 28 Mar 2020 18:22:43 -0600 Subject: [PATCH 10/12] create a dumpRockets() method to save a rocket to a file, so we can open and inspect it in OR --- .../net/sf/openrocket/util/TestRockets.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/core/src/net/sf/openrocket/util/TestRockets.java b/core/src/net/sf/openrocket/util/TestRockets.java index bcf02c910..43d28a7e3 100644 --- a/core/src/net/sf/openrocket/util/TestRockets.java +++ b/core/src/net/sf/openrocket/util/TestRockets.java @@ -1662,19 +1662,6 @@ public class TestRockets { TrapezoidFinSet trapezoidfinset = new TrapezoidFinSet(3, 0.051, 0.025, 0.038, 0.044); bodytube.addChild(trapezoidfinset); - // This is how we can dump a test rocket so we can look at it in OR to better - // visualize it - // - // OpenRocketDocument doc = OpenRocketDocumentFactory.createDocumentFromRocket(rocket); - // OpenRocketSaver saver = new OpenRocketSaver(); - // try { - // FileOutputStream str = new FileOutputStream("3fnc.ork"); - // saver.save(str, doc, null); - // } - // catch (Exception e) { - // System.err.println("exception " + e); - // } - return rocket; } @@ -1710,8 +1697,23 @@ public class TestRockets { podBody.addChild(fins); } } - + return rocket; } + /** + * dump a test rocket to a file, so we can open it in OR + */ + static void dumpRocket(Rocket rocket, String filename) { + + OpenRocketDocument doc = OpenRocketDocumentFactory.createDocumentFromRocket(rocket); + OpenRocketSaver saver = new OpenRocketSaver(); + try { + FileOutputStream str = new FileOutputStream(filename); + saver.save(str, doc, null); + } + catch (Exception e) { + System.err.println("exception " + e); + } + } } From ecc8a32891d314d2e7239a2d9edc62069b3e0406 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Sat, 28 Mar 2020 18:57:32 -0600 Subject: [PATCH 11/12] Modify "fins on pods" test to use a modified Alpha III --- .../net/sf/openrocket/util/TestRockets.java | 4 ++-- .../aerodynamics/BarrowmanCalculatorTest.java | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/src/net/sf/openrocket/util/TestRockets.java b/core/src/net/sf/openrocket/util/TestRockets.java index 43d28a7e3..edc66fb71 100644 --- a/core/src/net/sf/openrocket/util/TestRockets.java +++ b/core/src/net/sf/openrocket/util/TestRockets.java @@ -1668,8 +1668,8 @@ public class TestRockets { // second model used to test with/without pods. In order to // maintain consistency between the models, we'll create the // no-pods first, and then modify it to make the with-pods version - public static final Rocket make3FNCWithPods() { - Rocket rocket = TestRockets.make3FNCNoPods(); + public static final Rocket makeEstesAlphaIIIWithPods() { + Rocket rocket = TestRockets.makeEstesAlphaIII(); // find the body and fins final InstanceMap imap = rocket.getSelectedConfiguration().getActiveInstances(); diff --git a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java index 2751c5234..56a992836 100644 --- a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java +++ b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java @@ -234,12 +234,12 @@ public class BarrowmanCalculatorTest { @Test public void testPhantomTubes() { - Rocket rocketNoPods = TestRockets.make3FNCNoPods(); + Rocket rocketNoPods = TestRockets.makeEstesAlphaIII(); FlightConfiguration configNoPods = rocketNoPods.getSelectedConfiguration(); FlightConditions conditionsNoPods = new FlightConditions(configNoPods); WarningSet warningsNoPods = new WarningSet(); - Rocket rocketWithPods = TestRockets.make3FNCWithPods(); + Rocket rocketWithPods = TestRockets.makeEstesAlphaIIIWithPods(); FlightConfiguration configPods = rocketWithPods.getSelectedConfiguration(); FlightConditions conditionsPods = new FlightConditions(configPods); WarningSet warningsPods = new WarningSet(); @@ -248,7 +248,7 @@ public class BarrowmanCalculatorTest { final AerodynamicForces forcesNoPods = calcPods.getAerodynamicForces(configNoPods, conditionsNoPods, warningsNoPods); final AerodynamicForces forcesPods = calcPods.getAerodynamicForces(configPods, conditionsPods, warningsPods); - assertEquals(" 3FNC With Pods rocket CD value is incorrect:", forcesPods.getCD(), forcesNoPods.getCD(), EPSILON); + assertEquals(" Estes Alpha III With Pods rocket CD value is incorrect:", forcesPods.getCD(), forcesNoPods.getCD(), EPSILON); // The "with pods" version has no way of seeing the fins are // on the actual body tube rather than the phantom tubes, @@ -265,16 +265,16 @@ public class BarrowmanCalculatorTest { // will be off-axis (one of the fins is taken as having an // angle of 0 to the airstream, so it has no contribution). // This doesn't turn out to cause a problem in an actual - // simulation, so we are just not testing for it. Test with - // correction if we want some time is here but commented out + // simulation. final Coordinate cpNoPods = calcNoPods.getCP(configNoPods, conditionsNoPods, warningsNoPods); final Coordinate cpPods = calcPods.getCP(configPods, conditionsPods, warningsPods); - - assertEquals(" 3FNC With Pods rocket cp x value is incorrect:", cpPods.x, cpNoPods.x - 0.00672, EPSILON); - // assertEquals(" 3FNC With Pods rocket cp y value is incorrect:", cpPods.y, cpNoPods.y - 0.00548, EPSILON); - // assertEquals(" 3FNC With Pods rocket cp z value is incorrect:", cpPods.z, cpNoPods.z, EPSILON); - assertEquals(" 3FNC With Pods rocket CNa value is incorrect:", cpPods.weight, cpNoPods.weight - 3.14271, EPSILON); + System.out.printf("with pods %s\n", cpPods.toString()); + System.out.printf("without pods %s\n", cpNoPods.toString()); + assertEquals(" Alpha III With Pods rocket cp x value is incorrect:", cpNoPods.x - 0.002788761352, cpPods.x, EPSILON); + assertEquals(" Alpha III With Pods rocket cp y value is incorrect:", cpNoPods.y - 0.005460218430206499, cpPods.y, EPSILON); + assertEquals(" Alpha III With Pods rocket cp z value is incorrect:", cpNoPods.z, cpPods.z, EPSILON); + assertEquals(" Alpha III With Pods rocket CNa value is incorrect:", cpPods.weight, cpNoPods.weight - 3.91572, EPSILON); } } From 06d101c775fcf5224670dce275e37afe97df1238 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Sun, 29 Mar 2020 10:13:08 -0600 Subject: [PATCH 12/12] remove old 3FNC rocket; not needed since using Alpha III as base for pods test --- .../net/sf/openrocket/util/TestRockets.java | 34 +------------------ 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/core/src/net/sf/openrocket/util/TestRockets.java b/core/src/net/sf/openrocket/util/TestRockets.java index edc66fb71..be0c2e7f1 100644 --- a/core/src/net/sf/openrocket/util/TestRockets.java +++ b/core/src/net/sf/openrocket/util/TestRockets.java @@ -1635,39 +1635,7 @@ public class TestRockets { return rocketDoc; } - // the following two models are used in testing - // otherwise-identical rockets, one created in the obvious way - // using a single finset and the other creating three pods, each - // with a single fin. - public static final Rocket make3FNCNoPods() { - - Rocket rocket = new Rocket(); - - rocket.enableEvents(); - - AxialStage stage = new AxialStage(); - stage.setName("Sustainer"); - rocket.addChild(stage); - - // shape, length, radius - NoseCone nosecone = new NoseCone(Transition.Shape.OGIVE, 0.102, 0.0125); - stage.addChild(nosecone); - - // length, outer radius, thickness - BodyTube bodytube = new BodyTube(0.305, 0.0125, 0.001); - bodytube.setName("Main Body"); - stage.addChild(bodytube); - - // number of fins, root chord, tip chord, sweep, height - TrapezoidFinSet trapezoidfinset = new TrapezoidFinSet(3, 0.051, 0.025, 0.038, 0.044); - bodytube.addChild(trapezoidfinset); - - return rocket; - } - - // second model used to test with/without pods. In order to - // maintain consistency between the models, we'll create the - // no-pods first, and then modify it to make the with-pods version + // Alpha III modified to put fins on "phantom" pods public static final Rocket makeEstesAlphaIIIWithPods() { Rocket rocket = TestRockets.makeEstesAlphaIII();