From 85dff39fb9e4ed76953cf7784d36d26b1c51f899 Mon Sep 17 00:00:00 2001 From: Daniel_M_Williams Date: Sat, 5 Sep 2015 11:54:29 -0400 Subject: [PATCH] folded BasicMassCalculator into MassCalculator --- .../sf/openrocket/document/Simulation.java | 3 +- .../file/rocksim/export/RocksimSaver.java | 5 +- .../masscalc/BasicMassCalculator.java | 403 ------------------ .../openrocket/masscalc/MassCalculator.java | 366 +++++++++++++++- .../domains/StabilityDomain.java | 13 +- .../parameters/StabilityParameter.java | 15 +- .../rocketcomponent/RocketUtils.java | 7 +- .../simulation/SimulationOptions.java | 5 +- .../gui/dialogs/ComponentAnalysisDialog.java | 5 +- .../sf/openrocket/gui/print/DesignReport.java | 3 +- .../gui/scalefigure/RocketPanel.java | 3 +- 11 files changed, 378 insertions(+), 450 deletions(-) delete mode 100644 core/src/net/sf/openrocket/masscalc/BasicMassCalculator.java diff --git a/core/src/net/sf/openrocket/document/Simulation.java b/core/src/net/sf/openrocket/document/Simulation.java index 5a8b8ed23..aea41c406 100644 --- a/core/src/net/sf/openrocket/document/Simulation.java +++ b/core/src/net/sf/openrocket/document/Simulation.java @@ -9,7 +9,6 @@ import net.sf.openrocket.aerodynamics.AerodynamicCalculator; import net.sf.openrocket.aerodynamics.BarrowmanCalculator; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.formatting.RocketDescriptor; -import net.sf.openrocket.masscalc.BasicMassCalculator; import net.sf.openrocket.masscalc.MassCalculator; import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.MotorInstanceConfiguration; @@ -93,7 +92,7 @@ public class Simulation implements ChangeSource, Cloneable { private Class simulationStepperClass = RK4SimulationStepper.class; private Class aerodynamicCalculatorClass = BarrowmanCalculator.class; @SuppressWarnings("unused") - private Class massCalculatorClass = BasicMassCalculator.class; + private Class massCalculatorClass = MassCalculator.class; /** Listeners for this object */ private List listeners = new ArrayList(); diff --git a/core/src/net/sf/openrocket/file/rocksim/export/RocksimSaver.java b/core/src/net/sf/openrocket/file/rocksim/export/RocksimSaver.java index a6e1f7b85..9d3d8f10f 100644 --- a/core/src/net/sf/openrocket/file/rocksim/export/RocksimSaver.java +++ b/core/src/net/sf/openrocket/file/rocksim/export/RocksimSaver.java @@ -13,11 +13,10 @@ import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.StorageOptions; import net.sf.openrocket.file.RocketSaver; import net.sf.openrocket.file.rocksim.RocksimCommonConstants; -import net.sf.openrocket.masscalc.BasicMassCalculator; import net.sf.openrocket.masscalc.MassCalculator; +import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.AxialStage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -93,7 +92,7 @@ public class RocksimSaver extends RocketSaver { private RocketDesignDTO toRocketDesignDTO(Rocket rocket) { RocketDesignDTO result = new RocketDesignDTO(); - MassCalculator massCalc = new BasicMassCalculator(); + MassCalculator massCalc = new MassCalculator(); final Configuration configuration = new Configuration(rocket); final double cg = massCalc.getCG(configuration, MassCalculator.MassCalcType.NO_MOTORS).x * diff --git a/core/src/net/sf/openrocket/masscalc/BasicMassCalculator.java b/core/src/net/sf/openrocket/masscalc/BasicMassCalculator.java deleted file mode 100644 index f419691f9..000000000 --- a/core/src/net/sf/openrocket/masscalc/BasicMassCalculator.java +++ /dev/null @@ -1,403 +0,0 @@ -package net.sf.openrocket.masscalc; - -import static net.sf.openrocket.util.MathUtil.pow2; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import net.sf.openrocket.motor.Motor; -import net.sf.openrocket.motor.MotorId; -import net.sf.openrocket.motor.MotorInstance; -import net.sf.openrocket.motor.MotorInstanceConfiguration; -import net.sf.openrocket.rocketcomponent.AxialStage; -import net.sf.openrocket.rocketcomponent.Configuration; -import net.sf.openrocket.rocketcomponent.MotorMount; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.simulation.MassData; -import net.sf.openrocket.util.Coordinate; -import net.sf.openrocket.util.MathUtil; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class BasicMassCalculator implements MassCalculator { - - private static final double MIN_MASS = 0.001 * MathUtil.EPSILON; - private static final Logger log = LoggerFactory.getLogger(MassCalculator.class); - - private int rocketMassModID = -1; - private int rocketTreeModID = -1; - - /* - * Cached data. All CG data is in absolute coordinates. All moments of inertia - * are relative to their respective CG. - */ - private Coordinate[] cgCache = null; - private double longitudinalInertiaCache[] = null; - private double rotationalInertiaCache[] = null; - - - - ////////////////// Mass property calculations /////////////////// - - - /** - * Return the CG of the rocket with the specified motor status (no motors, - * ignition, burnout). - */ - @Override - public Coordinate getCG(Configuration configuration, MassCalcType type) { - checkCache(configuration); - calculateStageCache(configuration); - - Coordinate totalCG = null; - - // Stage contribution - for (int stage : configuration.getActiveStages()) { - totalCG = cgCache[stage].average(totalCG); - } - - if (totalCG == null) - totalCG = Coordinate.NUL; - - // Add motor CGs - String motorId = configuration.getFlightConfigurationID(); - if (type != MassCalcType.NO_MOTORS && motorId != null) { - Iterator iterator = configuration.motorIterator(); - while (iterator.hasNext()) { - MotorMount mount = iterator.next(); - RocketComponent comp = (RocketComponent) mount; - Motor motor = mount.getMotor(motorId); - if (motor == null) - continue; - - Coordinate motorCG = type.getCG(motor).add(mount.getMotorPosition(motorId)); - Coordinate[] cgs = comp.toAbsolute(motorCG); - for (Coordinate cg : cgs) { - totalCG = totalCG.average(cg); - } - } - } - - return totalCG; - } - - - - - /** - * Return the CG of the rocket with the provided motor configuration. - */ - @Override - public Coordinate getCG(Configuration configuration, MotorInstanceConfiguration motors) { - checkCache(configuration); - calculateStageCache(configuration); - - Coordinate totalCG = getCG(configuration, MassCalcType.NO_MOTORS); - - // Add motor CGs - if (motors != null) { - for (MotorId id : motors.getMotorIDs()) { - int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber(); - if (configuration.isStageActive(stage)) { - - MotorInstance motor = motors.getMotorInstance(id); - Coordinate position = motors.getMotorPosition(id); - Coordinate cg = motor.getCG().add(position); - totalCG = totalCG.average(cg); - - } - } - } - return totalCG; - } - - /** - * Return the longitudinal inertia of the rocket with the specified motor instance - * configuration. - * - * @param configuration the current motor instance configuration - * @return the longitudinal inertia of the rocket - */ - @Override - public double getLongitudinalInertia(Configuration configuration, MotorInstanceConfiguration motors) { - checkCache(configuration); - calculateStageCache(configuration); - - final Coordinate totalCG = getCG(configuration, motors); - double totalInertia = 0; - - // Stages - for (int stage : configuration.getActiveStages()) { - Coordinate stageCG = cgCache[stage]; - - totalInertia += (longitudinalInertiaCache[stage] + - stageCG.weight * MathUtil.pow2(stageCG.x - totalCG.x)); - } - - - // Motors - if (motors != null) { - for (MotorId id : motors.getMotorIDs()) { - int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber(); - if (configuration.isStageActive(stage)) { - MotorInstance motor = motors.getMotorInstance(id); - Coordinate position = motors.getMotorPosition(id); - Coordinate cg = motor.getCG().add(position); - - double inertia = motor.getLongitudinalInertia(); - totalInertia += inertia + cg.weight * MathUtil.pow2(cg.x - totalCG.x); - } - } - } - - return totalInertia; - } - - - - /** - * Return the rotational inertia of the rocket with the specified motor instance - * configuration. - * - * @param configuration the current motor instance configuration - * @return the rotational inertia of the rocket - */ - @Override - public double getRotationalInertia(Configuration configuration, MotorInstanceConfiguration motors) { - checkCache(configuration); - calculateStageCache(configuration); - - final Coordinate totalCG = getCG(configuration, motors); - double totalInertia = 0; - - // Stages - for (int stage : configuration.getActiveStages()) { - Coordinate stageCG = cgCache[stage]; - - totalInertia += (rotationalInertiaCache[stage] + - stageCG.weight * (MathUtil.pow2(stageCG.y - totalCG.y) + - MathUtil.pow2(stageCG.z - totalCG.z))); - } - - - // Motors - if (motors != null) { - for (MotorId id : motors.getMotorIDs()) { - int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber(); - if (configuration.isStageActive(stage)) { - MotorInstance motor = motors.getMotorInstance(id); - Coordinate position = motors.getMotorPosition(id); - Coordinate cg = motor.getCG().add(position); - - double inertia = motor.getRotationalInertia(); - totalInertia += inertia + cg.weight * (MathUtil.pow2(cg.y - totalCG.y) + - MathUtil.pow2(cg.z - totalCG.z)); - } - } - } - - return totalInertia; - } - - /** - * Return the total mass of the motors - * - * @param configuration the current motor instance configuration - * @return the total mass of all motors - */ - @Override - public double getPropellantMass(Configuration configuration, MotorInstanceConfiguration motors) { - double mass = 0; - - // add up the masses of all motors in the rocket - if (motors != null) { - for (MotorId id : motors.getMotorIDs()) { - MotorInstance motor = motors.getMotorInstance(id); - mass = mass + motor.getCG().weight - motor.getParentMotor().getEmptyCG().weight; - } - } - return mass; - } - - @Override - public Map getCGAnalysis(Configuration configuration, MassCalcType type) { - checkCache(configuration); - calculateStageCache(configuration); - - Map map = new HashMap(); - - for (RocketComponent c : configuration) { - Coordinate[] cgs = c.toAbsolute(c.getCG()); - Coordinate totalCG = Coordinate.NUL; - for (Coordinate cg : cgs) { - totalCG = totalCG.average(cg); - } - map.put(c, totalCG); - } - - map.put(configuration.getRocket(), getCG(configuration, type)); - - return map; - } - - //////// Cache computations //////// - - private void calculateStageCache(Configuration config) { - if (cgCache == null) { - ArrayList stageList = config.getRocket().getStageList(); - int stageCount = stageList.size(); - - cgCache = new Coordinate[stageCount]; - longitudinalInertiaCache = new double[stageCount]; - rotationalInertiaCache = new double[stageCount]; - - for (int i = 0; i < stageCount; i++) { - RocketComponent stage = stageList.get(i); - MassData data = calculateAssemblyMassData(stage); - cgCache[i] = stage.toAbsolute(data.getCG())[0]; - longitudinalInertiaCache[i] = data.getLongitudinalInertia(); - rotationalInertiaCache[i] = data.getRotationalInertia(); - } - - } - } - - - - /** - * Returns the mass and inertia data for this component and all subcomponents. - * The inertia is returned relative to the CG, and the CG is in the coordinates - * of the specified component, not global coordinates. - */ - private MassData calculateAssemblyMassData(RocketComponent parent) { - Coordinate parentCG = Coordinate.ZERO; - double longitudinalInertia = 0.0; - double rotationalInertia = 0.0; - - // Calculate data for this component - parentCG = parent.getComponentCG(); - if (parentCG.weight < MIN_MASS) - parentCG = parentCG.setWeight(MIN_MASS); - - - // Override only this component's data - if (!parent.getOverrideSubcomponents()) { - if (parent.isMassOverridden()) - parentCG = parentCG.setWeight(MathUtil.max(parent.getOverrideMass(), MIN_MASS)); - if (parent.isCGOverridden()) - parentCG = parentCG.setXYZ(parent.getOverrideCG()); - } - - longitudinalInertia = parent.getLongitudinalUnitInertia() * parentCG.weight; - rotationalInertia = parent.getRotationalUnitInertia() * parentCG.weight; - - - // Combine data for subcomponents - for (RocketComponent sibling : parent.getChildren()) { - Coordinate combinedCG; - double dx2, dr2; - - // Compute data of sibling - MassData siblingData = calculateAssemblyMassData(sibling); - Coordinate[] siblingCGs = sibling.toRelative(siblingData.getCG(), parent); - - for (Coordinate siblingCG : siblingCGs) { - - // Compute CG of this + sibling - combinedCG = parentCG.average(siblingCG); - - // Add effect of this CG change to parent inertia - dx2 = pow2(parentCG.x - combinedCG.x); - longitudinalInertia += parentCG.weight * dx2; - - dr2 = pow2(parentCG.y - combinedCG.y) + pow2(parentCG.z - combinedCG.z); - rotationalInertia += parentCG.weight * dr2; - - - // Add inertia of sibling - longitudinalInertia += siblingData.getLongitudinalInertia(); - rotationalInertia += siblingData.getRotationalInertia(); - - // Add effect of sibling CG change - dx2 = pow2(siblingData.getCG().x - combinedCG.x); - longitudinalInertia += siblingData.getCG().weight * dx2; - - dr2 = pow2(siblingData.getCG().y - combinedCG.y) + pow2(siblingData.getCG().z - combinedCG.z); - rotationalInertia += siblingData.getCG().weight * dr2; - - // Set combined CG - parentCG = combinedCG; - } - } - - // Override total data - if (parent.getOverrideSubcomponents()) { - if (parent.isMassOverridden()) { - double oldMass = parentCG.weight; - double newMass = MathUtil.max(parent.getOverrideMass(), MIN_MASS); - longitudinalInertia = longitudinalInertia * newMass / oldMass; - rotationalInertia = rotationalInertia * newMass / oldMass; - parentCG = parentCG.setWeight(newMass); - } - if (parent.isCGOverridden()) { - double oldx = parentCG.x; - double newx = parent.getOverrideCGX(); - longitudinalInertia += parentCG.weight * pow2(oldx - newx); - parentCG = parentCG.setX(newx); - } - } - - MassData parentData = new MassData(parentCG, longitudinalInertia, rotationalInertia, 0); - return parentData; - } - - /** - * Check the current cache consistency. This method must be called by all - * methods that may use any cached data before any other operations are - * performed. If the rocket has changed since the previous call to - * checkCache(), then {@link #voidMassCache()} is called. - *

- * This method performs the checking based on the rocket's modification IDs, - * so that these method may be called from listeners of the rocket itself. - * - * @param configuration the configuration of the current call - */ - protected final void checkCache(Configuration configuration) { - if (rocketMassModID != configuration.getRocket().getMassModID() || - rocketTreeModID != configuration.getRocket().getTreeModID()) { - rocketMassModID = configuration.getRocket().getMassModID(); - rocketTreeModID = configuration.getRocket().getTreeModID(); - log.debug("Voiding the mass cache"); - voidMassCache(); - } - } - - /** - * Void cached mass data. This method is called whenever a change occurs in - * the rocket structure that affects the mass of the rocket and when a new - * Rocket is used. This method must be overridden to void any cached data - * necessary. The method must call super.voidMassCache() during - * its execution. - */ - protected void voidMassCache() { - this.cgCache = null; - this.longitudinalInertiaCache = null; - this.rotationalInertiaCache = null; - } - - - - - @Override - public int getModID() { - return 0; - } - - - - - -} diff --git a/core/src/net/sf/openrocket/masscalc/MassCalculator.java b/core/src/net/sf/openrocket/masscalc/MassCalculator.java index 5e657f7ab..e402dab7d 100644 --- a/core/src/net/sf/openrocket/masscalc/MassCalculator.java +++ b/core/src/net/sf/openrocket/masscalc/MassCalculator.java @@ -1,15 +1,29 @@ package net.sf.openrocket.masscalc; +import static net.sf.openrocket.util.MathUtil.pow2; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.motor.MotorId; +import net.sf.openrocket.motor.MotorInstance; import net.sf.openrocket.motor.MotorInstanceConfiguration; +import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.Configuration; +import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.simulation.MassData; import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.Monitorable; -public interface MassCalculator extends Monitorable { +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MassCalculator implements Monitorable { public static enum MassCalcType { NO_MOTORS { @@ -34,41 +48,186 @@ public interface MassCalculator extends Monitorable { public abstract Coordinate getCG(Motor motor); } + + private static final double MIN_MASS = 0.001 * MathUtil.EPSILON; + private static final Logger log = LoggerFactory.getLogger(MassCalculator.class); + + private int rocketMassModID = -1; + private int rocketTreeModID = -1; + + /* + * Cached data. All CG data is in absolute coordinates. All moments of inertia + * are relative to their respective CG. + */ + private Coordinate[] cgCache = null; + private double longitudinalInertiaCache[] = null; + private double rotationalInertiaCache[] = null; + + + + ////////////////// Mass property calculations /////////////////// + + /** - * Compute the CG of the provided configuration. + * Return the CG of the rocket with the specified motor status (no motors, + * ignition, burnout). * * @param configuration the rocket configuration * @param type the state of the motors (none, launch mass, burnout mass) * @return the CG of the configuration */ - public Coordinate getCG(Configuration configuration, MassCalcType type); + public Coordinate getCG(Configuration configuration, MassCalcType type) { + checkCache(configuration); + calculateStageCache(configuration); + + Coordinate totalCG = null; + + // Stage contribution + for (int stage : configuration.getActiveStages()) { + totalCG = cgCache[stage].average(totalCG); + } + + if (totalCG == null) + totalCG = Coordinate.NUL; + + // Add motor CGs + String motorId = configuration.getFlightConfigurationID(); + if (type != MassCalcType.NO_MOTORS && motorId != null) { + Iterator iterator = configuration.motorIterator(); + while (iterator.hasNext()) { + MotorMount mount = iterator.next(); + RocketComponent comp = (RocketComponent) mount; + Motor motor = mount.getMotorConfiguration().get(motorId).getMotor(); + if (motor == null) + continue; + + Coordinate motorCG = type.getCG(motor).add(mount.getMotorPosition(motorId)); + Coordinate[] cgs = comp.toAbsolute(motorCG); + for (Coordinate cg : cgs) { + totalCG = totalCG.average(cg); + } + } + } + + return totalCG; + } /** - * Compute the CG of the provided configuration with specified motors. + * Compute the CG of the rocket with the provided motor configuration. * * @param configuration the rocket configuration * @param motors the motor configuration * @return the CG of the configuration */ - public Coordinate getCG(Configuration configuration, MotorInstanceConfiguration motors); + public Coordinate getCG(Configuration configuration, MotorInstanceConfiguration motors) { + checkCache(configuration); + calculateStageCache(configuration); + + Coordinate totalCG = getCG(configuration, MassCalcType.NO_MOTORS); + + // Add motor CGs + if (motors != null) { + for (MotorId id : motors.getMotorIDs()) { + int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber(); + if (configuration.isStageActive(stage)) { + + MotorInstance motor = motors.getMotorInstance(id); + Coordinate position = motors.getMotorPosition(id); + Coordinate cg = motor.getCG().add(position); + totalCG = totalCG.average(cg); + + } + } + } + return totalCG; + } /** - * Compute the longitudinal inertia of the provided configuration with specified motors. + * Return the longitudinal inertia of the rocket with the specified motor instance + * configuration. * - * @param configuration the rocket configuration + * @param configuration the current motor instance configuration * @param motors the motor configuration - * @return the longitudinal inertia of the configuration + * @return the longitudinal inertia of the rocket */ - public double getLongitudinalInertia(Configuration configuration, MotorInstanceConfiguration motors); + public double getLongitudinalInertia(Configuration configuration, MotorInstanceConfiguration motors) { + checkCache(configuration); + calculateStageCache(configuration); + + final Coordinate totalCG = getCG(configuration, motors); + double totalInertia = 0; + + // Stages + for (int stage : configuration.getActiveStages()) { + Coordinate stageCG = cgCache[stage]; + + totalInertia += (longitudinalInertiaCache[stage] + + stageCG.weight * MathUtil.pow2(stageCG.x - totalCG.x)); + } + + + // Motors + if (motors != null) { + for (MotorId id : motors.getMotorIDs()) { + int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber(); + if (configuration.isStageActive(stage)) { + MotorInstance motor = motors.getMotorInstance(id); + Coordinate position = motors.getMotorPosition(id); + Coordinate cg = motor.getCG().add(position); + + double inertia = motor.getLongitudinalInertia(); + totalInertia += inertia + cg.weight * MathUtil.pow2(cg.x - totalCG.x); + } + } + } + + return totalInertia; + } + /** * Compute the rotational inertia of the provided configuration with specified motors. * - * @param configuration the rocket configuration + * @param configuration the current motor instance configuration * @param motors the motor configuration * @return the rotational inertia of the configuration */ - public double getRotationalInertia(Configuration configuration, MotorInstanceConfiguration motors); + public double getRotationalInertia(Configuration configuration, MotorInstanceConfiguration motors) { + checkCache(configuration); + calculateStageCache(configuration); + + final Coordinate totalCG = getCG(configuration, motors); + double totalInertia = 0; + + // Stages + for (int stage : configuration.getActiveStages()) { + Coordinate stageCG = cgCache[stage]; + + totalInertia += (rotationalInertiaCache[stage] + + stageCG.weight * (MathUtil.pow2(stageCG.y - totalCG.y) + + MathUtil.pow2(stageCG.z - totalCG.z))); + } + + + // Motors + if (motors != null) { + for (MotorId id : motors.getMotorIDs()) { + int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber(); + if (configuration.isStageActive(stage)) { + MotorInstance motor = motors.getMotorInstance(id); + Coordinate position = motors.getMotorPosition(id); + Coordinate cg = motor.getCG().add(position); + + double inertia = motor.getRotationalInertia(); + totalInertia += inertia + cg.weight * (MathUtil.pow2(cg.y - totalCG.y) + + MathUtil.pow2(cg.z - totalCG.z)); + } + } + } + + return totalInertia; + } + /** * Return the total mass of the motors @@ -77,7 +236,18 @@ public interface MassCalculator extends Monitorable { * @param configuration the current motor instance configuration * @return the total mass of all motors */ - public double getPropellantMass(Configuration configuration, MotorInstanceConfiguration motors); + public double getPropellantMass(Configuration configuration, MotorInstanceConfiguration motors) { + double mass = 0; + + // add up the masses of all motors in the rocket + if (motors != null) { + for (MotorId id : motors.getMotorIDs()) { + MotorInstance motor = motors.getMotorInstance(id); + mass = mass + motor.getCG().weight - motor.getParentMotor().getEmptyCG().weight; + } + } + return mass; + } /** * Compute an analysis of the per-component CG's of the provided configuration. @@ -90,7 +260,175 @@ public interface MassCalculator extends Monitorable { * @param type the state of the motors (none, launch mass, burnout mass) * @return a map from each rocket component to its corresponding CG. */ - public Map getCGAnalysis(Configuration configuration, MassCalcType type); + public Map getCGAnalysis(Configuration configuration, MassCalcType type) { + checkCache(configuration); + calculateStageCache(configuration); + + Map map = new HashMap(); + + for (RocketComponent c : configuration) { + Coordinate[] cgs = c.toAbsolute(c.getCG()); + Coordinate totalCG = Coordinate.NUL; + for (Coordinate cg : cgs) { + totalCG = totalCG.average(cg); + } + map.put(c, totalCG); + } + + map.put(configuration.getRocket(), getCG(configuration, type)); + + return map; + } + + //////// Cache computations //////// + + private void calculateStageCache(Configuration config) { + if (cgCache == null) { + ArrayList stageList = config.getRocket().getStageList(); + int stageCount = stageList.size(); + + cgCache = new Coordinate[stageCount]; + longitudinalInertiaCache = new double[stageCount]; + rotationalInertiaCache = new double[stageCount]; + + for (int i = 0; i < stageCount; i++) { + RocketComponent stage = stageList.get(i); + MassData data = calculateAssemblyMassData(stage); + cgCache[i] = stage.toAbsolute(data.getCG())[0]; + longitudinalInertiaCache[i] = data.getLongitudinalInertia(); + rotationalInertiaCache[i] = data.getRotationalInertia(); + } + + } + } + + + /** + * Returns the mass and inertia data for this component and all subcomponents. + * The inertia is returned relative to the CG, and the CG is in the coordinates + * of the specified component, not global coordinates. + */ + private MassData calculateAssemblyMassData(RocketComponent parent) { + Coordinate parentCG = Coordinate.ZERO; + double longitudinalInertia = 0.0; + double rotationalInertia = 0.0; + + // Calculate data for this component + parentCG = parent.getComponentCG(); + if (parentCG.weight < MIN_MASS) + parentCG = parentCG.setWeight(MIN_MASS); + + + // Override only this component's data + if (!parent.getOverrideSubcomponents()) { + if (parent.isMassOverridden()) + parentCG = parentCG.setWeight(MathUtil.max(parent.getOverrideMass(), MIN_MASS)); + if (parent.isCGOverridden()) + parentCG = parentCG.setXYZ(parent.getOverrideCG()); + } + + longitudinalInertia = parent.getLongitudinalUnitInertia() * parentCG.weight; + rotationalInertia = parent.getRotationalUnitInertia() * parentCG.weight; + + + // Combine data for subcomponents + for (RocketComponent sibling : parent.getChildren()) { + Coordinate combinedCG; + double dx2, dr2; + + // Compute data of sibling + MassData siblingData = calculateAssemblyMassData(sibling); + Coordinate[] siblingCGs = sibling.toRelative(siblingData.getCG(), parent); + + for (Coordinate siblingCG : siblingCGs) { + + // Compute CG of this + sibling + combinedCG = parentCG.average(siblingCG); + + // Add effect of this CG change to parent inertia + dx2 = pow2(parentCG.x - combinedCG.x); + longitudinalInertia += parentCG.weight * dx2; + + dr2 = pow2(parentCG.y - combinedCG.y) + pow2(parentCG.z - combinedCG.z); + rotationalInertia += parentCG.weight * dr2; + + + // Add inertia of sibling + longitudinalInertia += siblingData.getLongitudinalInertia(); + rotationalInertia += siblingData.getRotationalInertia(); + + // Add effect of sibling CG change + dx2 = pow2(siblingData.getCG().x - combinedCG.x); + longitudinalInertia += siblingData.getCG().weight * dx2; + + dr2 = pow2(siblingData.getCG().y - combinedCG.y) + pow2(siblingData.getCG().z - combinedCG.z); + rotationalInertia += siblingData.getCG().weight * dr2; + + // Set combined CG + parentCG = combinedCG; + } + } + + // Override total data + if (parent.getOverrideSubcomponents()) { + if (parent.isMassOverridden()) { + double oldMass = parentCG.weight; + double newMass = MathUtil.max(parent.getOverrideMass(), MIN_MASS); + longitudinalInertia = longitudinalInertia * newMass / oldMass; + rotationalInertia = rotationalInertia * newMass / oldMass; + parentCG = parentCG.setWeight(newMass); + } + if (parent.isCGOverridden()) { + double oldx = parentCG.x; + double newx = parent.getOverrideCGX(); + longitudinalInertia += parentCG.weight * pow2(oldx - newx); + parentCG = parentCG.setX(newx); + } + } + + MassData parentData = new MassData(parentCG, longitudinalInertia, rotationalInertia, 0); + return parentData; + } + + /** + * Check the current cache consistency. This method must be called by all + * methods that may use any cached data before any other operations are + * performed. If the rocket has changed since the previous call to + * checkCache(), then {@link #voidMassCache()} is called. + *

+ * This method performs the checking based on the rocket's modification IDs, + * so that these method may be called from listeners of the rocket itself. + * + * @param configuration the configuration of the current call + */ + protected final void checkCache(Configuration configuration) { + if (rocketMassModID != configuration.getRocket().getMassModID() || + rocketTreeModID != configuration.getRocket().getTreeModID()) { + rocketMassModID = configuration.getRocket().getMassModID(); + rocketTreeModID = configuration.getRocket().getTreeModID(); + log.debug("Voiding the mass cache"); + voidMassCache(); + } + } + + /** + * Void cached mass data. This method is called whenever a change occurs in + * the rocket structure that affects the mass of the rocket and when a new + * Rocket is used. This method must be overridden to void any cached data + * necessary. The method must call super.voidMassCache() during + * its execution. + */ + protected void voidMassCache() { + this.cgCache = null; + this.longitudinalInertiaCache = null; + this.rotationalInertiaCache = null; + } + + + + @Override + public int getModID() { + return 0; + } - } diff --git a/core/src/net/sf/openrocket/optimization/rocketoptimization/domains/StabilityDomain.java b/core/src/net/sf/openrocket/optimization/rocketoptimization/domains/StabilityDomain.java index 631b45c90..0043a8769 100644 --- a/core/src/net/sf/openrocket/optimization/rocketoptimization/domains/StabilityDomain.java +++ b/core/src/net/sf/openrocket/optimization/rocketoptimization/domains/StabilityDomain.java @@ -4,7 +4,6 @@ import net.sf.openrocket.aerodynamics.AerodynamicCalculator; import net.sf.openrocket.aerodynamics.BarrowmanCalculator; import net.sf.openrocket.aerodynamics.FlightConditions; import net.sf.openrocket.document.Simulation; -import net.sf.openrocket.masscalc.BasicMassCalculator; import net.sf.openrocket.masscalc.MassCalculator; import net.sf.openrocket.masscalc.MassCalculator.MassCalcType; import net.sf.openrocket.optimization.rocketoptimization.SimulationDomain; @@ -50,8 +49,8 @@ public class StabilityDomain implements SimulationDomain { } - - + + @Override public Pair getDistanceToDomain(Simulation simulation) { Coordinate cp, cg; @@ -64,9 +63,9 @@ public class StabilityDomain implements SimulationDomain { * Caching would in any case be inefficient since the rocket changes all the time. */ AerodynamicCalculator aerodynamicCalculator = new BarrowmanCalculator(); - MassCalculator massCalculator = new BasicMassCalculator(); + MassCalculator massCalculator = new MassCalculator(); + - Configuration configuration = simulation.getConfiguration(); FlightConditions conditions = new FlightConditions(configuration); conditions.setMach(Application.getPreferences().getDefaultMach()); @@ -87,7 +86,7 @@ public class StabilityDomain implements SimulationDomain { else cgx = Double.NaN; - + // Calculate the reference (absolute or relative) absolute = cpx - cgx; @@ -101,7 +100,7 @@ public class StabilityDomain implements SimulationDomain { } relative = absolute / diameter; - + Value desc; if (minAbsolute && maxAbsolute) { desc = new Value(absolute, UnitGroup.UNITS_LENGTH); diff --git a/core/src/net/sf/openrocket/optimization/rocketoptimization/parameters/StabilityParameter.java b/core/src/net/sf/openrocket/optimization/rocketoptimization/parameters/StabilityParameter.java index c1fde6aaf..9075d60ad 100644 --- a/core/src/net/sf/openrocket/optimization/rocketoptimization/parameters/StabilityParameter.java +++ b/core/src/net/sf/openrocket/optimization/rocketoptimization/parameters/StabilityParameter.java @@ -1,14 +1,10 @@ package net.sf.openrocket.optimization.rocketoptimization.parameters; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import net.sf.openrocket.aerodynamics.AerodynamicCalculator; import net.sf.openrocket.aerodynamics.BarrowmanCalculator; import net.sf.openrocket.aerodynamics.FlightConditions; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.l10n.Translator; -import net.sf.openrocket.masscalc.BasicMassCalculator; import net.sf.openrocket.masscalc.MassCalculator; import net.sf.openrocket.masscalc.MassCalculator.MassCalcType; import net.sf.openrocket.optimization.general.OptimizationException; @@ -21,6 +17,9 @@ import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * An optimization parameter that computes either the absolute or relative stability of a rocket. * @@ -31,7 +30,7 @@ public class StabilityParameter implements OptimizableParameter { private static final Logger log = LoggerFactory.getLogger(StabilityParameter.class); private static final Translator trans = Application.getTranslator(); - + private final boolean absolute; public StabilityParameter(boolean absolute) { @@ -57,9 +56,9 @@ public class StabilityParameter implements OptimizableParameter { * Caching would in any case be inefficient since the rocket changes all the time. */ AerodynamicCalculator aerodynamicCalculator = new BarrowmanCalculator(); - MassCalculator massCalculator = new BasicMassCalculator(); + MassCalculator massCalculator = new MassCalculator(); + - Configuration configuration = simulation.getConfiguration(); FlightConditions conditions = new FlightConditions(configuration); conditions.setMach(Application.getPreferences().getDefaultMach()); @@ -79,7 +78,7 @@ public class StabilityParameter implements OptimizableParameter { else cgx = Double.NaN; - + // Calculate the reference (absolute or relative) stability = cpx - cgx; diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketUtils.java b/core/src/net/sf/openrocket/rocketcomponent/RocketUtils.java index 71360b9a2..517aeaad4 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketUtils.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketUtils.java @@ -2,13 +2,12 @@ package net.sf.openrocket.rocketcomponent; import java.util.Collection; -import net.sf.openrocket.masscalc.BasicMassCalculator; import net.sf.openrocket.masscalc.MassCalculator; import net.sf.openrocket.masscalc.MassCalculator.MassCalcType; import net.sf.openrocket.util.Coordinate; public abstract class RocketUtils { - + public static double getLength(Rocket rocket) { double length = 0; Collection bounds = rocket.getDefaultConfiguration().getBounds(); @@ -24,9 +23,9 @@ public abstract class RocketUtils { } return length; } - + public static Coordinate getCG(Rocket rocket, MassCalcType calcType) { - MassCalculator massCalculator = new BasicMassCalculator(); + MassCalculator massCalculator = new MassCalculator(); Coordinate cg = massCalculator.getCG(rocket.getDefaultConfiguration(), calcType); return cg; } diff --git a/core/src/net/sf/openrocket/simulation/SimulationOptions.java b/core/src/net/sf/openrocket/simulation/SimulationOptions.java index 734ce1fea..5d6f6f2bf 100644 --- a/core/src/net/sf/openrocket/simulation/SimulationOptions.java +++ b/core/src/net/sf/openrocket/simulation/SimulationOptions.java @@ -8,7 +8,7 @@ import java.util.Random; import net.sf.openrocket.aerodynamics.BarrowmanCalculator; import net.sf.openrocket.formatting.MotorDescriptionSubstitutor; -import net.sf.openrocket.masscalc.BasicMassCalculator; +import net.sf.openrocket.masscalc.MassCalculator; import net.sf.openrocket.models.atmosphere.AtmosphericModel; import net.sf.openrocket.models.atmosphere.ExtendedISAModel; import net.sf.openrocket.models.gravity.GravityModel; @@ -37,6 +37,7 @@ import org.slf4j.LoggerFactory; */ public class SimulationOptions implements ChangeSource, Cloneable { + @SuppressWarnings("unused") private static final Logger log = LoggerFactory.getLogger(SimulationOptions.class); public static final double MAX_LAUNCH_ROD_ANGLE = Math.PI / 3; @@ -638,7 +639,7 @@ public class SimulationOptions implements ChangeSource, Cloneable { conditions.setGravityModel(gravityModel); conditions.setAerodynamicCalculator(new BarrowmanCalculator()); - conditions.setMassCalculator(new BasicMassCalculator()); + conditions.setMassCalculator(new MassCalculator()); conditions.setTimeStep(getTimeStep()); conditions.setMaximumAngleStep(getMaximumStepAngle()); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java index 5f2d90b3a..fee70b852 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java @@ -54,7 +54,6 @@ import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.scalefigure.RocketPanel; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.l10n.Translator; -import net.sf.openrocket.masscalc.BasicMassCalculator; import net.sf.openrocket.masscalc.MassCalculator; import net.sf.openrocket.masscalc.MassCalculator.MassCalcType; import net.sf.openrocket.rocketcomponent.Configuration; @@ -69,7 +68,7 @@ import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.StateChangeListener; public class ComponentAnalysisDialog extends JDialog implements StateChangeListener { - + private static final long serialVersionUID = 9131240570600307935L; private static ComponentAnalysisDialog singletonDialog = null; private static final Translator trans = Application.getTranslator(); @@ -80,7 +79,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe private final JToggleButton worstToggle; private boolean fakeChange = false; private AerodynamicCalculator aerodynamicCalculator; - private final MassCalculator massCalculator = new BasicMassCalculator(); + private final MassCalculator massCalculator = new MassCalculator(); private final ColumnTableModel cpTableModel; private final ColumnTableModel dragTableModel; diff --git a/swing/src/net/sf/openrocket/gui/print/DesignReport.java b/swing/src/net/sf/openrocket/gui/print/DesignReport.java index ed7c98c36..3235b06a3 100644 --- a/swing/src/net/sf/openrocket/gui/print/DesignReport.java +++ b/swing/src/net/sf/openrocket/gui/print/DesignReport.java @@ -13,7 +13,6 @@ import net.sf.openrocket.formatting.RocketDescriptor; import net.sf.openrocket.gui.figureelements.FigureElement; import net.sf.openrocket.gui.figureelements.RocketInfo; import net.sf.openrocket.gui.scalefigure.RocketPanel; -import net.sf.openrocket.masscalc.BasicMassCalculator; import net.sf.openrocket.masscalc.MassCalculator; import net.sf.openrocket.masscalc.MassCalculator.MassCalcType; import net.sf.openrocket.motor.Motor; @@ -325,7 +324,7 @@ public class DesignReport { DecimalFormat ttwFormat = new DecimalFormat("0.00"); - MassCalculator massCalc = new BasicMassCalculator(); + MassCalculator massCalc = new MassCalculator(); Configuration config = new Configuration(rocket); config.setFlightConfigurationID(motorId); diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 6654191b5..729391b41 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -54,7 +54,6 @@ import net.sf.openrocket.gui.main.componenttree.ComponentTreeModel; import net.sf.openrocket.gui.simulation.SimulationWorker; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; -import net.sf.openrocket.masscalc.BasicMassCalculator; import net.sf.openrocket.masscalc.MassCalculator; import net.sf.openrocket.masscalc.MassCalculator.MassCalcType; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; @@ -184,7 +183,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change // TODO: FUTURE: calculator selection aerodynamicCalculator = new BarrowmanCalculator(); - massCalculator = new BasicMassCalculator(); + massCalculator = new MassCalculator(); // Create figure and custom scroll pane figure = new RocketFigure(configuration);