Merge pull request #603 from JoePfeiffer/fix-568

[Fixes 568] Consider all instances of components when calculating drag
This commit is contained in:
Daniel Williams 2020-03-29 19:53:46 -04:00 committed by GitHub
commit 18a8caa5b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 273 additions and 138 deletions

View File

@ -388,18 +388,24 @@ 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);
for (RocketComponent c : configuration.getActiveComponents()) {
final InstanceMap imap = configuration.getActiveInstances();
for(Map.Entry<RocketComponent, ArrayList<InstanceContext>> entry: imap.entrySet() ) {
final RocketComponent c = entry.getKey();
// Consider only SymmetricComponents and FinSets:
if (!(c instanceof SymmetricComponent) &&
!(c instanceof FinSet))
continue;
// iterate across component instances
final ArrayList<InstanceContext> contextList = entry.getValue();
for(InstanceContext context: contextList ) {
// Calculate the roughness-limited friction coefficient
Finish finish = ((ExternalComponent) c).getFinish();
if (Double.isNaN(roughnessLimited[finish.ordinal()])) {
@ -448,17 +454,21 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
/ 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) {
FinSet f = (FinSet) c;
double mac = ((FinSetCalc) calcMap.get(c)).getMACLength();
double cd = componentCf * (1 + 2 * f.getThickness() / mac) *
2 * f.getFinCount() * f.getPlanformArea();
2 * f.getPlanformArea();
finFriction += cd;
if (map != null) {
@ -467,11 +477,11 @@ 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
@ -483,8 +493,6 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
}
}
return (finFriction + correction * bodyFriction) / conditions.getRefArea();
}
@ -502,7 +510,6 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
Map<RocketComponent, AerodynamicForces> map, WarningSet warnings) {
double stagnation, base, total;
double radius = 0;
if (calcMap == null)
buildCalcMap(configuration);
@ -511,10 +518,16 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
base = calculateBaseCD(conditions.getMach());
total = 0;
for (RocketComponent c : configuration.getActiveComponents()) {
final InstanceMap imap = configuration.getActiveInstances();
for(Map.Entry<RocketComponent, ArrayList<InstanceContext>> entry: imap.entrySet() ) {
final RocketComponent c = entry.getKey();
if (!c.isAerodynamic())
continue;
// iterate across component instances
final ArrayList<InstanceContext> contextList = entry.getValue();
for(InstanceContext context: contextList ) {
// Pressure fore drag
double cd = calcMap.get(c).calculatePressureDragForce(conditions, stagnation, base,
warnings);
@ -524,23 +537,28 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
map.get(c).setPressureCD(cd);
}
if(c.isCDOverridden()) continue;
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();
}
}
}
@ -561,8 +579,6 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
Map<RocketComponent, AerodynamicForces> map, WarningSet warnings) {
double base, total;
double radius = 0;
RocketComponent prevComponent = null;
if (calcMap == null)
buildCalcMap(configuration);
@ -570,36 +586,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<RocketComponent, ArrayList<InstanceContext>> entry: imap.entrySet() ) {
final RocketComponent c = entry.getKey();
if (!(c instanceof SymmetricComponent))
continue;
SymmetricComponent s = (SymmetricComponent) c;
// iterate across component instances
final ArrayList<InstanceContext> 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) {
if ((map != null) && (prevComponent != null)) {
map.get(prevComponent).setBaseCD(cd);
}
}
radius = s.getAftRadius();
prevComponent = c;
}
if (radius > 0) {
double area = Math.PI * pow2(radius);
// 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(prevComponent).setBaseCD(cd);
map.get(s).setBaseCD(cd);
}
}
}
}

View File

@ -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;
}

View File

@ -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.
*/
protected final SymmetricComponent getPreviousSymmetricComponent() {
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;
}

View File

@ -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;
@ -1630,4 +1635,53 @@ public class TestRockets {
return rocketDoc;
}
// Alpha III modified to put fins on "phantom" pods
public static final Rocket makeEstesAlphaIIIWithPods() {
Rocket rocket = TestRockets.makeEstesAlphaIII();
// find the body and fins
final InstanceMap imap = rocket.getSelectedConfiguration().getActiveInstances();
for(Map.Entry<RocketComponent, ArrayList<InstanceContext>> 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;
}
/**
* 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);
}
}
}

View File

@ -231,4 +231,50 @@ public class BarrowmanCalculatorTest {
assertFalse(" Missed discontinuity in Falcon 9 Heavy:", calc.isContinuous( rocket));
}
@Test
public void testPhantomTubes() {
Rocket rocketNoPods = TestRockets.makeEstesAlphaIII();
FlightConfiguration configNoPods = rocketNoPods.getSelectedConfiguration();
FlightConditions conditionsNoPods = new FlightConditions(configNoPods);
WarningSet warningsNoPods = new WarningSet();
Rocket rocketWithPods = TestRockets.makeEstesAlphaIIIWithPods();
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(" 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,
// 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.
final Coordinate cpNoPods = calcNoPods.getCP(configNoPods, conditionsNoPods, warningsNoPods);
final Coordinate cpPods = calcPods.getCP(configPods, conditionsPods, warningsPods);
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);
}
}