[#1460] Exclude inactive stages in tbe simulation calculations

This commit is contained in:
SiboVG 2022-06-25 00:44:54 +02:00
parent 77bad60155
commit 027ed2eaa6
12 changed files with 95 additions and 65 deletions

View File

@ -68,5 +68,5 @@ public interface AerodynamicCalculator extends Monitorable {
*/
public AerodynamicCalculator newInstance();
public boolean isContinuous( final Rocket rkt);
public boolean isContinuous(FlightConfiguration configuration, final Rocket rkt);
}

View File

@ -81,7 +81,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
Map<RocketComponent, AerodynamicForces> assemblyMap = new LinkedHashMap<>();
// Calculate non-axial force data
calculateForceAnalysis(conditions, configuration.getRocket(), instMap, eachMap, assemblyMap, warnings);
calculateForceAnalysis(configuration, conditions, configuration.getRocket(), instMap, eachMap, assemblyMap, warnings);
// Calculate drag coefficient data
AerodynamicForces rocketForces = assemblyMap.get(configuration.getRocket());
@ -126,7 +126,8 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
return finalMap;
}
private AerodynamicForces calculateForceAnalysis( FlightConditions conds,
private AerodynamicForces calculateForceAnalysis( FlightConfiguration configuration,
FlightConditions conds,
RocketComponent comp,
InstanceMap instances,
Map<RocketComponent, AerodynamicForces> eachForces,
@ -154,12 +155,11 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
for( RocketComponent child : comp.getChildren()) {
// Ignore inactive stages
if (child instanceof AxialStage &&
!((AxialStage) child).isStageActive()) {
if (child instanceof AxialStage && !configuration.isStageActive(child.getStageNumber())) {
continue;
}
// forces particular to each component
AerodynamicForces childForces = calculateForceAnalysis(conds, child, instances, eachForces, assemblyForces, warnings);
AerodynamicForces childForces = calculateForceAnalysis(configuration, conds, child, instances, eachForces, assemblyForces, warnings);
if(null != childForces) {
aggregateForces.merge(childForces);
@ -246,7 +246,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
if (calcMap == null)
buildCalcMap(configuration);
if( ! isContinuous( configuration.getRocket() ) ){
if (!isContinuous(configuration, configuration.getRocket())){
warnings.add( Warning.DIAMETER_DISCONTINUITY);
}
@ -272,16 +272,15 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
}
@Override
public boolean isContinuous( final Rocket rkt){
return testIsContinuous( rkt);
public boolean isContinuous(FlightConfiguration configuration, final Rocket rkt){
return testIsContinuous(configuration, rkt);
}
private boolean testIsContinuous( final RocketComponent treeRoot ){
private boolean testIsContinuous(FlightConfiguration configuration, final RocketComponent treeRoot ){
Queue<RocketComponent> queue = new LinkedList<>();
for (RocketComponent child : treeRoot.getChildren()) {
// Ignore inactive stages
if (child instanceof AxialStage &&
!((AxialStage) child).isStageActive()) {
if (child instanceof AxialStage && !configuration.isStageActive(child.getStageNumber())) {
continue;
}
queue.add(child);
@ -294,8 +293,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
if( comp instanceof SymmetricComponent ){
for (RocketComponent child : comp.getChildren()) {
// Ignore inactive stages
if (child instanceof AxialStage &&
!((AxialStage) child).isStageActive()) {
if (child instanceof AxialStage && !configuration.isStageActive(child.getStageNumber())) {
continue;
}
queue.add(child);
@ -323,7 +321,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
prevComp = sym;
}else if( comp instanceof ComponentAssembly ){
isContinuous &= testIsContinuous( comp );
isContinuous &= testIsContinuous(configuration, comp);
}
}
@ -339,7 +337,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
* @param configuration Rocket configuration
* @param conditions Flight conditions taken into account
* @param map ?
* @param set Set to handle
* @param warningSet Set to handle warnings
* @return friction drag for entire rocket
*/
private double calculateFrictionCD(FlightConfiguration configuration, FlightConditions conditions,

View File

@ -104,7 +104,7 @@ public class MassCalculator implements Monitorable {
public static RigidBody calculate( final MassCalculation.Type _type, final SimulationStatus status ){
final FlightConfiguration config = status.getConfiguration();
final double time = status.getSimulationTime();
final Collection<MotorClusterState> activeMotorList = status.getMotors();
final Collection<MotorClusterState> activeMotorList = status.getActiveMotors();
MassCalculation calculation= new MassCalculation( _type, config, time, activeMotorList, config.getRocket(), Transformation.IDENTITY, null);
calculation.calculateAssembly();

View File

@ -4,6 +4,7 @@ import java.util.Locale;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.startup.Application;
@ -13,25 +14,25 @@ public enum IgnitionEvent {
//// Automatic (launch or ejection charge)
AUTOMATIC( "AUTOMATIC", "MotorMount.IgnitionEvent.AUTOMATIC"){
@Override
public boolean isActivationEvent(FlightEvent testEvent, RocketComponent targetComponent) {
public boolean isActivationEvent(FlightConfiguration config, FlightEvent testEvent, RocketComponent targetComponent) {
AxialStage targetStage = targetComponent.getStage();
if ( targetStage.isLaunchStage() ){
return LAUNCH.isActivationEvent(testEvent, targetComponent);
if (targetStage.isLaunchStage(config)) {
return LAUNCH.isActivationEvent(config, testEvent, targetComponent);
} else {
return EJECTION_CHARGE.isActivationEvent(testEvent, targetComponent);
return EJECTION_CHARGE.isActivationEvent(config, testEvent, targetComponent);
}
}
},
LAUNCH ( "LAUNCH", "MotorMount.IgnitionEvent.LAUNCH"){
@Override
public boolean isActivationEvent( FlightEvent fe, RocketComponent source){
public boolean isActivationEvent(FlightConfiguration config, FlightEvent fe, RocketComponent source){
return (fe.getType() == FlightEvent.Type.LAUNCH);
}
},
EJECTION_CHARGE ("EJECTION_CHARGE", "MotorMount.IgnitionEvent.EJECTION_CHARGE"){
@Override
public boolean isActivationEvent( FlightEvent testEvent, RocketComponent targetComponent){
public boolean isActivationEvent(FlightConfiguration config, FlightEvent testEvent, RocketComponent targetComponent){
if (testEvent.getType() != FlightEvent.Type.EJECTION_CHARGE){
return false;
}
@ -44,7 +45,7 @@ public enum IgnitionEvent {
},
BURNOUT ("BURNOUT", "MotorMount.IgnitionEvent.BURNOUT"){
@Override
public boolean isActivationEvent( FlightEvent testEvent, RocketComponent targetComponent){
public boolean isActivationEvent(FlightConfiguration config, FlightEvent testEvent, RocketComponent targetComponent){
if (testEvent.getType() != FlightEvent.Type.BURNOUT)
return false;
@ -64,7 +65,7 @@ public enum IgnitionEvent {
//public static final IgnitionEvent[] events = {AUTOMATIC, LAUNCH, EJECTION_CHARGE, BURNOUT, NEVER};
public boolean isActivationEvent( FlightEvent fe, RocketComponent source){
public boolean isActivationEvent(FlightConfiguration config, FlightEvent fe, RocketComponent source){
// default behavior. Also for the NEVER case.
return false;
}

View File

@ -130,11 +130,12 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC
/**
* returns if the object is a launch stage
* @param config the flight configuration which will check which stages are active
* @return if the object is a launch stage
*/
public boolean isLaunchStage(){
return ( this instanceof ParallelStage )
||( getRocket().getBottomCoreStage().equals(this));
public boolean isLaunchStage(FlightConfiguration config) {
return ((this instanceof ParallelStage && config.isStageActive(this.stageNumber))
||( getRocket().getBottomCoreStage(config).equals(this)));
}
/**

View File

@ -60,8 +60,8 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
}
/* Cached data */
final protected HashMap<Integer, StageFlags> stages = new HashMap<Integer, StageFlags>();
final protected HashMap<MotorConfigurationId, MotorConfiguration> motors = new HashMap<MotorConfigurationId, MotorConfiguration>();
final protected Map<Integer, StageFlags> stages = new HashMap<Integer, StageFlags>();
final protected Map<MotorConfigurationId, MotorConfiguration> motors = new HashMap<MotorConfigurationId, MotorConfiguration>();
final private Collection<MotorConfiguration> activeMotors = new ArrayList<MotorConfiguration>();
final private InstanceMap activeInstances = new InstanceMap();
@ -190,7 +190,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
* @param stageNumber stage number to flag
* @param _active inactive (<code>false</code>) or active (<code>true</code>)
*/
private void _setStageActive(final int stageNumber, final boolean _active ) {
public void _setStageActive(final int stageNumber, final boolean _active ) {
if ((0 <= stageNumber) && (stages.containsKey(stageNumber))) {
stages.get(stageNumber).active = _active;
fireChangeEvent();
@ -338,6 +338,18 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
return results;
}
/**
* Return all the stages in this configuration.
* @return all the stages in this configuration.
*/
public List<AxialStage> getAllStages() {
List<AxialStage> stages = new ArrayList<>();
for (StageFlags flags : this.stages.values()) {
stages.add( rocket.getStage(flags.stageNumber));
}
return stages;
}
public List<AxialStage> getActiveStages() {
List<AxialStage> activeStages = new ArrayList<>();

View File

@ -197,24 +197,37 @@ public class Rocket extends ComponentAssembly {
public AxialStage getStage( final int stageNumber ) {
return this.stageMap.get( stageNumber);
}
/*
* Returns the stage at the top of the central stack
*
* @Return a reference to the topmost stage
/**
* Get the topmost stage, only taking into account active stages from the flight configuration.
* @param config flight configuration dictating which stages are active
* @return the topmost active stage, or null if there are no active stages.
*/
public AxialStage getTopmostStage(){
return (AxialStage) getChild(0);
public AxialStage getTopmostStage(FlightConfiguration config) {
if (config == null) return null;
for (int i = 0; i < getChildCount(); i++) {
if (getChild(i) instanceof AxialStage && config.isStageActive(getChild(i).getStageNumber())) {
return (AxialStage) getChild(i);
}
}
return null;
}
/*
* Returns the stage at the top of the central stack
*
* @Return a reference to the topmost stage
/**
* Get the bottommost stage, only taking into account active stages from the flight configuration.
* @param config flight configuration dictating which stages are active
* @return the bottommost active stage, or null if there are no active stages.
*/
/*package-local*/ AxialStage getBottomCoreStage(){
// get last stage that's a direct child of the rocket.
return (AxialStage) children.get( children.size()-1 );
public AxialStage getBottomCoreStage(FlightConfiguration config) {
if (config == null) return null;
for (int i = getChildCount() - 1; i >= 0; i--) {
if (getChild(i) instanceof AxialStage && config.isStageActive(getChild(i).getStageNumber())) {
return (AxialStage) getChild(i);
}
}
return null;
}
@Override

View File

@ -45,7 +45,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
private final static double AOA_TUMBLE_CONDITION = Math.PI / 9.0;
// The thrust must be below this value for the transition to tumbling.
// TODO: this is an arbitrary value
// TODO HIGH: this is an arbitrary value
private final static double THRUST_TUMBLE_CONDITION = 0.01;
private SimulationStepper currentStepper;
@ -65,7 +65,9 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// Set up rocket configuration
this.fcid = simulationConditions.getFlightConfigurationID();
FlightConfiguration simulationConfig = simulationConditions.getRocket().getFlightConfiguration( this.fcid).clone();
FlightConfiguration origConfig = simulationConditions.getRocket().getFlightConfiguration(this.fcid);
FlightConfiguration simulationConfig = origConfig.clone();
simulationConfig.copyStages(origConfig); // Clone the stage activation configuration
if ( ! simulationConfig.hasMotors() ) {
throw new MotorIgnitionException(trans.get("BasicEventSimulationEngine.error.noMotorsDefined"));
}
@ -74,7 +76,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
currentStatus.getEventQueue().add(new FlightEvent(FlightEvent.Type.LAUNCH, 0, simulationConditions.getRocket()));
{
// main simulation branch
final String branchName = simulationConfig.getRocket().getTopmostStage().getName();
final String branchName = simulationConfig.getRocket().getTopmostStage(currentStatus.getConfiguration()).getName();
currentStatus.setFlightData(new FlightDataBranch( branchName, FlightDataType.TYPE_TIME));
}
toSimulate.push(currentStatus);
@ -273,9 +275,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// Check for motor ignition events, add ignition events to queue
for (MotorClusterState state : currentStatus.getActiveMotors() ){
if( state.testForIgnition(event )){
final double simulationTime = currentStatus.getSimulationTime() ;
if (state.testForIgnition(currentStatus.getConfiguration(), event)) {
MotorClusterState sourceState = (MotorClusterState) event.getData();
double ignitionDelay = 0;
if (event.getType() == FlightEvent.Type.BURNOUT)
@ -543,7 +543,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
}
// TODO : FUTURE : do not hard code the 1200 (maybe even make it configurable by the user)
// TODO FUTURE : do not hard code the 1200 (maybe even make it configurable by the user)
if( 1200 < currentStatus.getSimulationTime() ){
ret = false;
log.error("Simulation hit max time (1200s): aborting.");
@ -553,6 +553,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// If no motor has ignited, abort
if (!currentStatus.isMotorIgnited()) {
// TODO MEDIUM: display this as a warning to the user (e.g. highlight the cell in the simulation panel in red and a hover: 'make sure the motor ignition is correct' or something)
throw new MotorIgnitionException(trans.get("BasicEventSimulationEngine.error.noIgnition"));
}

View File

@ -172,7 +172,7 @@ public class FlightEvent implements Comparable<FlightEvent> {
* @return
*/
public void validate(){
if( this.time == Double.NaN ){
if(Double.isNaN(this.time)){
throw new IllegalStateException(type.name()+" event has a NaN time!");
}
switch( this.type ){

View File

@ -4,6 +4,7 @@ import net.sf.openrocket.motor.IgnitionEvent;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorConfiguration;
import net.sf.openrocket.motor.MotorConfigurationId;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RocketComponent;
@ -121,8 +122,8 @@ public class MotorClusterState {
/**
* Compute the average thrust over an interval.
*
* @param simulationTime
* @param cond
* @param startSimulationTime start time of the averaging interval
* @param endSimulationTime end time of the averaging interval
* @return
*/
public double getAverageThrust( final double startSimulationTime, final double endSimulationTime) {
@ -141,7 +142,6 @@ public class MotorClusterState {
* Compute the average thrust over an interval.
*
* @param simulationTime
* @param cond
* @return
*/
public double getThrust( final double simulationTime){
@ -182,9 +182,9 @@ public class MotorClusterState {
currentState = ThrustState.ARMED;
}
public boolean testForIgnition( final FlightEvent _event ){
public boolean testForIgnition(FlightConfiguration flightConfiguration, final FlightEvent _event ){
RocketComponent mount = (RocketComponent) this.getMount();
return getIgnitionEvent().isActivationEvent( _event, mount);
return getIgnitionEvent().isActivationEvent(flightConfiguration, _event, mount);
}
public String toDescription(){

View File

@ -267,22 +267,25 @@ public class BarrowmanCalculatorTest {
public void testContinuousRocket() {
Rocket rocket = TestRockets.makeEstesAlphaIII();
AerodynamicCalculator calc = new BarrowmanCalculator();
FlightConfiguration configuration = rocket.getSelectedConfiguration();
assertTrue("Estes Alpha III should be continous: ", calc.isContinuous( rocket));
assertTrue("Estes Alpha III should be continous: ", calc.isContinuous(configuration, rocket));
}
@Test
public void testContinuousRocketWithStrapOns() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
AerodynamicCalculator calc = new BarrowmanCalculator();
FlightConfiguration configuration = rocket.getSelectedConfiguration();
assertTrue("F9H should be continuous: ", calc.isContinuous( rocket));
assertTrue("F9H should be continuous: ", calc.isContinuous(configuration, rocket));
}
@Test
public void testRadialDiscontinuousRocket() {
Rocket rocket = TestRockets.makeEstesAlphaIII();
AerodynamicCalculator calc = new BarrowmanCalculator();
FlightConfiguration configuration = rocket.getSelectedConfiguration();
NoseCone nose = (NoseCone)rocket.getChild(0).getChild(0);
BodyTube body = (BodyTube)rocket.getChild(0).getChild(1);
@ -291,13 +294,14 @@ public class BarrowmanCalculatorTest {
body.setOuterRadius( 0.012 );
body.setName( body.getName()+" << discontinuous");
assertFalse(" Estes Alpha III has an undetected discontinuity:", calc.isContinuous( rocket));
assertFalse(" Estes Alpha III has an undetected discontinuity:", calc.isContinuous(configuration, rocket));
}
@Test
public void testRadialDiscontinuityWithStrapOns() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
AerodynamicCalculator calc = new BarrowmanCalculator();
FlightConfiguration configuration = rocket.getSelectedConfiguration();
final AxialStage coreStage = (AxialStage)rocket.getChild(1);
final ParallelStage booster = (ParallelStage)coreStage.getChild(0).getChild(0);
@ -309,7 +313,7 @@ public class BarrowmanCalculatorTest {
body.setOuterRadius( 0.012 );
body.setName( body.getName()+" << discontinuous");
assertFalse(" Missed discontinuity in Falcon 9 Heavy:", calc.isContinuous( rocket));
assertFalse(" Missed discontinuity in Falcon 9 Heavy:", calc.isContinuous(configuration, rocket));
}
@Test

View File

@ -295,8 +295,8 @@ public class FlightConfigurationTest extends BaseTestCase {
config.toggleStage(0);
assertThat(" toggle stage #0: ", config.isStageActive(0), equalTo(false));
AxialStage sustainer = rkt.getTopmostStage();
AxialStage booster = rkt.getBottomCoreStage();
AxialStage sustainer = rkt.getTopmostStage(config);
AxialStage booster = rkt.getBottomCoreStage(config);
assertThat(" sustainer stage is stage #0: ", sustainer.getStageNumber(), equalTo(0));
assertThat(" booster stage is stage #1: ", booster.getStageNumber(), equalTo(1));