[BugFix] Fixed Continuity Test

- Reimplemented the continuity test in BarrowmanCalculator
- Added corresponding unit tests, too
This commit is contained in:
Daniel_M_Williams 2016-01-01 22:05:13 -05:00
parent 0ab8dde5fb
commit 3f9be1387d
8 changed files with 154 additions and 87 deletions

View File

@ -2,12 +2,8 @@ package net.sf.openrocket.aerodynamics;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.Coordinate;
@ -19,7 +15,6 @@ import net.sf.openrocket.util.Coordinate;
*/
public abstract class AbstractAerodynamicCalculator implements AerodynamicCalculator {
private static final Logger log = LoggerFactory.getLogger(AbstractAerodynamicCalculator.class);
/** Number of divisions used when calculating worst CP. */
public static final int DIVISIONS = 360;

View File

@ -3,6 +3,7 @@ package net.sf.openrocket.aerodynamics;
import java.util.Map;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Monitorable;
@ -66,5 +67,6 @@ public interface AerodynamicCalculator extends Monitorable {
* @return a new, independent instance of this aerodynamic calculator type
*/
public AerodynamicCalculator newInstance();
public boolean isContinuous( final Rocket rkt);
}

View File

@ -6,15 +6,19 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import net.sf.openrocket.aerodynamics.barrowman.FinSetCalc;
import net.sf.openrocket.aerodynamics.barrowman.RocketComponentCalc;
import net.sf.openrocket.rocketcomponent.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.ExternalComponent;
import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.RingInstanceable;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.SymmetricComponent;
import net.sf.openrocket.util.Coordinate;
@ -151,11 +155,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
}
/*
* Perform the actual CP calculation.
*/
private AerodynamicForces calculateNonAxialForces(FlightConfiguration configuration, FlightConditions conditions,
Map<RocketComponent, AerodynamicForces> map, WarningSet warnings) {
@ -164,8 +164,6 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
AerodynamicForces total = new AerodynamicForces();
total.zero();
double radius = 0; // aft radius of previous component
double componentX = 0; // aft coordinate of previous component
AerodynamicForces forces = new AerodynamicForces();
if (warnings == null)
@ -178,6 +176,11 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
if (calcMap == null)
buildCalcMap(configuration);
if( ! isContinuous( configuration.getRocket() ) ){
warnings.add( Warning.DIAMETER_DISCONTINUITY);
}
for (RocketComponent component : configuration.getActiveComponents()) {
// Skip non-aerodynamic components
@ -185,35 +188,6 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
continue;
// TODO: refactor this code block to a separate method, where it will operate on each stage separately.
//
// Developer's Note:
// !! this code assumes all SymmetricComponents are along the centerline
// With the implementation of ParallelStages and Pods, this is no longer true. -Daniel Williams
//
// // Check for discontinuities
if (component instanceof SymmetricComponent) {
SymmetricComponent sym = (SymmetricComponent) component;
// TODO:LOW: Ignores other cluster components (not clusterable)
double x = component.toAbsolute(Coordinate.NUL)[0].x;
// Check for lengthwise discontinuity
if (x > componentX + 0.0001) {
if (!MathUtil.equals(radius, 0)) {
warnings.add(Warning.DISCONTINUITY);
radius = 0;
}
}
componentX = component.toAbsolute(new Coordinate(component.getLength()))[0].x;
// Check for radius discontinuity
if (!MathUtil.equals(sym.getForeRadius(), radius)) {
warnings.add(Warning.DISCONTINUITY);
// TODO: MEDIUM: Apply correction to values to cp and to map
}
radius = sym.getAftRadius();
}
// Call calculation method
forces.zero();
RocketComponentCalc calcObj = calcMap.get(component);
@ -273,6 +247,51 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
}
@Override
public boolean isContinuous( final Rocket rkt){
return testIsContinuous( rkt);
}
private boolean testIsContinuous( final RocketComponent treeRoot ){
Queue<RocketComponent> queue = new LinkedList<RocketComponent>();
queue.addAll(treeRoot.getChildren());
boolean isContinuous = true;
SymmetricComponent prevComp = null;
while((isContinuous)&&( null != queue.peek())){
RocketComponent comp = queue.poll();
if( comp instanceof SymmetricComponent ){
queue.addAll( comp.getChildren());
SymmetricComponent sym = (SymmetricComponent) comp;
if( null == prevComp){
prevComp = sym;
continue;
}
// Check for radius discontinuity
if ( !MathUtil.equals(sym.getForeRadius(), prevComp.getAftRadius())) {
isContinuous = false;
}
// double x = component.toAbsolute(Coordinate.NUL)[0].x;
// // Check for lengthwise discontinuity
// if (x > componentX + 0.0001) {
// if (!MathUtil.equals(radius, 0)) {
// warnings.add(Warning.DISCONTINUITY);
// radius = 0;
//}
//componentX = component.toAbsolute(new Coordinate(component.getLength()))[0].x;
prevComp = sym;
}else if( comp instanceof ComponentAssembly ){
isContinuous &= testIsContinuous( comp );
}
}
return isContinuous;
}
//////////////// DRAG CALCULATIONS ////////////////

View File

@ -321,7 +321,7 @@ public abstract class Warning {
/** A <code>Warning</code> that the body diameter is discontinuous. */
////Discontinuity in rocket body diameter.
public static final Warning DISCONTINUITY =
public static final Warning DIAMETER_DISCONTINUITY =
new Other(trans.get("Warning.DISCONTINUITY"));
/** A <code>Warning</code> that the fins are thick compared to the rocket body. */

View File

@ -115,7 +115,7 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC
public boolean isAfter(){
return true;
}
public boolean isLaunchStage(){
return ( getRocket().getBottomCoreStage().equals(this));
}

View File

@ -1938,8 +1938,6 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
return (MathUtil.pow2(innerRadius) + MathUtil.pow2(outerRadius)) / 2;
}
//////////// OTHER

View File

@ -833,52 +833,53 @@ public class TestRockets {
coreFins.setHeight(0.12);
coreFins.setSweep(0.18);
coreBody.addChild(coreFins);
}
// ====== Booster Stage Set ======
// ====== ====== ====== ======
ParallelStage boosterStage = new ParallelStage();
boosterStage.setName("Booster Stage");
coreStage.addChild( boosterStage);
boosterStage.setRelativePositionMethod(Position.BOTTOM);
boosterStage.setAxialOffset(0.0);
boosterStage.setInstanceCount(2);
boosterStage.setRadialOffset(0.075);
{
NoseCone boosterCone = new NoseCone(Transition.Shape.POWER, 0.08, 0.0385);
boosterCone.setShapeParameter(0.5);
boosterCone.setName("Booster Nose");
boosterCone.setThickness(0.002);
//payloadFairingNoseCone.setLength(0.118);
//payloadFairingNoseCone.setAftRadius(0.052);
boosterCone.setAftShoulderRadius( 0.051 );
boosterCone.setAftShoulderLength( 0.02 );
boosterCone.setAftShoulderThickness( 0.001 );
boosterCone.setAftShoulderCapped( false );
boosterStage.addChild( boosterCone);
BodyTube boosterBody = new BodyTube(0.8, 0.0385, 0.001);
boosterBody.setName("Booster Body");
boosterBody.setOuterRadiusAutomatic(true);
boosterStage.addChild( boosterBody);
// ====== Booster Stage Set ======
// ====== ====== ====== ======
ParallelStage boosterStage = new ParallelStage();
boosterStage.setName("Booster Stage");
coreStage.addChild( boosterStage);
boosterStage.setRelativePositionMethod(Position.BOTTOM);
boosterStage.setAxialOffset(0.0);
boosterStage.setInstanceCount(2);
boosterStage.setRadialOffset(0.075);
{
InnerTube boosterMotorTubes = new InnerTube();
boosterMotorTubes.setName("Booster Motor Tubes");
boosterMotorTubes.setLength(0.15);
boosterMotorTubes.setOuterRadius(0.015); // => 29mm motors
boosterMotorTubes.setThickness(0.0005);
boosterMotorTubes.setClusterConfiguration( ClusterConfiguration.CONFIGURATIONS[5]); // 4-ring
//boosterMotorTubes.setClusterConfiguration( ClusterConfiguration.CONFIGURATIONS[13]); // 9-star
boosterMotorTubes.setClusterScale(1.0);
boosterBody.addChild( boosterMotorTubes);
NoseCone boosterCone = new NoseCone(Transition.Shape.POWER, 0.08, 0.0385);
boosterCone.setShapeParameter(0.5);
boosterCone.setName("Booster Nose");
boosterCone.setThickness(0.002);
//payloadFairingNoseCone.setLength(0.118);
//payloadFairingNoseCone.setAftRadius(0.052);
boosterCone.setAftShoulderRadius( 0.051 );
boosterCone.setAftShoulderLength( 0.02 );
boosterCone.setAftShoulderThickness( 0.001 );
boosterCone.setAftShoulderCapped( false );
boosterStage.addChild( boosterCone);
FlightConfigurationId motorConfigId = config.getFlightConfigurationID();
MotorConfiguration motorInstance = TestRockets.generateMotorInstance_G77_29mm();
motorInstance.setID( new MotorInstanceId( boosterMotorTubes.getName(), 1) );
boosterMotorTubes.setMotorInstance( motorConfigId, motorInstance);
boosterMotorTubes.setMotorOverhang(0.01234);
BodyTube boosterBody = new BodyTube(0.8, 0.0385, 0.001);
boosterBody.setName("Booster Body");
boosterBody.setOuterRadiusAutomatic(true);
boosterStage.addChild( boosterBody);
{
InnerTube boosterMotorTubes = new InnerTube();
boosterMotorTubes.setName("Booster Motor Tubes");
boosterMotorTubes.setLength(0.15);
boosterMotorTubes.setOuterRadius(0.015); // => 29mm motors
boosterMotorTubes.setThickness(0.0005);
boosterMotorTubes.setClusterConfiguration( ClusterConfiguration.CONFIGURATIONS[5]); // 4-ring
//boosterMotorTubes.setClusterConfiguration( ClusterConfiguration.CONFIGURATIONS[13]); // 9-star
boosterMotorTubes.setClusterScale(1.0);
boosterBody.addChild( boosterMotorTubes);
FlightConfigurationId motorConfigId = config.getFlightConfigurationID();
MotorConfiguration motorInstance = TestRockets.generateMotorInstance_G77_29mm();
motorInstance.setID( new MotorInstanceId( boosterMotorTubes.getName(), 1) );
boosterMotorTubes.setMotorInstance( motorConfigId, motorInstance);
boosterMotorTubes.setMotorOverhang(0.01234);
}
}
}

View File

@ -1,6 +1,8 @@
package net.sf.openrocket.aerodynamics;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.BeforeClass;
@ -12,7 +14,10 @@ import com.google.inject.Module;
import net.sf.openrocket.ServicesForTesting;
import net.sf.openrocket.plugin.PluginModule;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.NoseCone;
import net.sf.openrocket.rocketcomponent.ParallelStage;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Coordinate;
@ -102,4 +107,51 @@ public class BarrowmanCalculatorTest {
fail("Not yet implemented");
}
@Test
public void testContinuousRocket() {
Rocket rocket = TestRockets.makeEstesAlphaIII();
AerodynamicCalculator calc = new BarrowmanCalculator();
assertTrue("Estes Alpha III should be continous: ", calc.isContinuous( rocket));
}
@Test
public void testContinuousRocketWithStrapOns() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
AerodynamicCalculator calc = new BarrowmanCalculator();
assertTrue("F9H should be continuous: ", calc.isContinuous( rocket));
}
@Test
public void testRadialDiscontinuousRocket() {
Rocket rocket = TestRockets.makeEstesAlphaIII();
AerodynamicCalculator calc = new BarrowmanCalculator();
NoseCone nose = (NoseCone)rocket.getChild(0).getChild(0);
BodyTube body = (BodyTube)rocket.getChild(0).getChild(1);
nose.setAftRadius(0.015);
body.setOuterRadius( 0.012 );
body.setName( body.getName()+" << discontinuous");
assertFalse(" Estes Alpha III has an undetected discontinuity:", calc.isContinuous( rocket));
}
@Test
public void testRadialDiscontinuityWithStrapOns() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
AerodynamicCalculator calc = new BarrowmanCalculator();
ParallelStage booster = (ParallelStage)rocket.getChild(1).getChild(1);
NoseCone nose = (NoseCone)booster.getChild(0);
BodyTube body = (BodyTube)booster.getChild(1);
nose.setAftRadius(0.015);
body.setOuterRadius( 0.012 );
body.setName( body.getName()+" << discontinuous");
assertFalse(" Missed discontinuity in Falcon 9 Heavy:", calc.isContinuous( rocket));
}
}