// Integrate for volume, CG, wetted area and planform area
- final double l = length / DIVISIONS;
- final double pil = Math.PI * l; // PI * l
- final double pil3 = Math.PI * l / 3; // PI * l/3
+ final double step = length / DIVISIONS;
+ final double pi3 = Math.PI / 3.0;
r1 = getRadius(0);
x = 0;
wetArea = 0;
* hyp is the length of the hypotenuse from r1 to r2
* height if the y-axis height of the component if not filled
*/
+ /*
+ * l is the step size for the current loop. Could also be called delta-x.
+ *
+ * to account for accumulated errors in the x position during the loop
+ * during the last iteration (n== DIVISIONS) we recompute l to be
+ * whatever is left.
+ */
+ double l = (n==DIVISIONS) ? length -x : step;
- r2 = getRadius(x + l);
+ // Further to prevent round off error from the previous statement,
+ // we clamp r2 to length at the last iteration.
+ r2 = getRadius((n==DIVISIONS) ? length : x + l);
+
final double hyp = MathUtil.hypot(r2 - r1, l);
-
// Volume differential elements
final double dV;
final double dFullV;
- dFullV = pil3 * (r1 * r1 + r1 * r2 + r2 * r2);
- if (filled || r1 < thickness || r2 < thickness) {
- // Filled piece
+ dFullV = pi3 * l * (r1 * r1 + r1 * r2 + r2 * r2);
+
+ if ( filled ) {
dV = dFullV;
} else {
- // Hollow piece
- final double height = thickness * hyp / l;
- dV = MathUtil.max(pil * height * (r1 + r2 - height), 0);
+ // hollow
+ // Thickness is normal to the surface of the component
+ // here we use simple trig to project the Thickness
+ // on to the y dimension (radius).
+ double height = thickness * hyp / l;
+ if (r1 < height || r2 < height) {
+ // Filled portion of piece
+ dV = dFullV;
+ } else {
+ // Hollow portion of piece
+ dV = MathUtil.max(Math.PI* l * height * (r1 + r2 - height), 0);
+ }
}
-
+
// Add to the volume-related components
volume += dV;
fullVolume += dFullV;
final double p = l * (r1 + r2);
planArea += p;
planCenter += (x + l / 2) * p;
-
+
// Update for next iteration
r1 = r2;
x += l;
--- /dev/null
+package net.sf.openrocket.rocketcomponent;
+
+import static org.junit.Assert.assertEquals;
+import net.sf.openrocket.material.Material;
+import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
+
+import org.junit.Test;
+
+public class SymmetricComponentVolumeTest extends BaseTestCase {
+
+ @Test
+ public void simpleConeFilled() {
+ NoseCone nc = new NoseCone();
+
+ final double epsilonPercent = 0.001;
+ final double density = 2.0;
+
+ nc.setLength(1.0);
+ nc.setFilled(true);
+ nc.setType( Transition.Shape.CONICAL );
+ nc.setAftRadius(1.0);
+ nc.setMaterial( new Material.Bulk("test",density,true));
+
+ System.out.println( nc.getComponentVolume() );
+
+ double volume = Math.PI / 3.0;
+
+ double mass = density * volume;
+
+ System.out.println( volume );
+
+ assertEquals( volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals( mass, nc.getMass(), epsilonPercent * mass );
+ }
+
+ @Test
+ public void simpleConeHollow() {
+ NoseCone nc = new NoseCone();
+
+ final double epsilonPercent = 0.001;
+ final double density = 2.0;
+
+ nc.setLength(1.0);
+ nc.setAftRadius(1.0);
+ nc.setThickness(0.5);
+ nc.setType( Transition.Shape.CONICAL );
+ nc.setMaterial( new Material.Bulk("test",density,true));
+
+ System.out.println( nc.getComponentVolume() );
+
+ double volume = Math.PI / 3.0; // outer volume
+
+ // manually projected Thickness of 0.5 on to radius to determine
+ // the innerConeDimen. Since the outer cone is "square" (height = radius),
+ // we only need to compute this one dimension in order to compute the
+ // volume of the inner cone.
+ double innerConeDimen = 1.0 - Math.sqrt(2.0) / 2.0;
+ double innerVolume = Math.PI / 3.0 * innerConeDimen * innerConeDimen * innerConeDimen;
+ volume -= innerVolume;
+
+ double mass = density * volume;
+
+ System.out.println( volume );
+
+ assertEquals( volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals( mass, nc.getMass(), epsilonPercent * mass );
+ }
+
+ @Test
+ public void simpleConeWithShoulderFilled() {
+ NoseCone nc = new NoseCone();
+
+ final double epsilonPercent = 0.001;
+ final double density = 2.0;
+
+ nc.setLength(1.0);
+ nc.setFilled(true);
+ nc.setType( Transition.Shape.CONICAL );
+ nc.setAftRadius(1.0);
+ nc.setAftShoulderRadius(1.0);
+ nc.setAftShoulderLength(1.0);
+ nc.setMaterial( new Material.Bulk("test",density,true));
+
+
+ System.out.println( nc.getComponentVolume() );
+
+ double volume = Math.PI / 3.0;
+ //volume += Math.PI;
+
+ double mass = density * volume;
+
+ System.out.println( volume );
+
+ // FIXME -
+ //assertEquals( volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals( mass, nc.getMass(), epsilonPercent * mass );
+ }
+
+ @Test
+ public void simpleTransitionFilled() {
+ Transition nc = new Transition();
+
+ final double epsilonPercent = 0.001;
+ final double density = 2.0;
+
+ nc.setLength(4.0);
+ nc.setFilled(true);
+ nc.setType( Transition.Shape.CONICAL );
+ nc.setForeRadius(1.0);
+ nc.setAftRadius(2.0);
+ nc.setMaterial( new Material.Bulk("test",density,true));
+
+ System.out.println( nc.getComponentVolume() );
+
+ double volume = Math.PI / 3.0 * (2.0*2.0 + 2.0 * 1.0 + 1.0 * 1.0) * 4.0;
+
+ double mass = density * volume;
+
+ System.out.println( volume );
+
+ assertEquals( volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals( mass, nc.getMass(), epsilonPercent * mass );
+ }
+
+ @Test
+ public void simpleTransitionHollow1() {
+ Transition nc = new Transition();
+
+ final double epsilonPercent = 0.001;
+ final double density = 2.0;
+
+ nc.setLength(1.0);
+ nc.setType( Transition.Shape.CONICAL );
+ nc.setForeRadius(0.5);
+ nc.setAftRadius(1.0);
+ nc.setThickness(0.5);
+ nc.setMaterial( new Material.Bulk("test",density,true));
+
+ System.out.println( nc.getComponentVolume() );
+
+ // Volume of filled transition =
+ double filledVolume = Math.PI /3.0 * ( 1.0*1.0 + 1.0 * 0.5 + 0.5 * 0.5 ) * 1.0;
+
+ // magic 2D cad drawing...
+ //
+ // Since the thickness >= fore radius, the
+ // hollowed out portion of the transition
+ // forms a cone.
+ // the dimensions of this cone were determined
+ // using a 2d cad tool.
+
+ double innerConeRadius = 0.441;
+ double innerConeLength = 0.882;
+ double innerVolume = Math.PI /3.0 * innerConeLength * innerConeRadius * innerConeRadius;
+
+ double volume = filledVolume - innerVolume;
+
+ double mass = density * volume;
+
+ System.out.println( volume );
+
+ assertEquals( volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals( mass, nc.getMass(), epsilonPercent * mass );
+ }
+
+ @Test
+ public void simpleTransitionHollow2() {
+ Transition nc = new Transition();
+
+ final double epsilonPercent = 0.001;
+ final double density = 2.0;
+
+ nc.setLength(1.0);
+ nc.setType( Transition.Shape.CONICAL );
+ nc.setForeRadius(0.5);
+ nc.setAftRadius(1.0);
+ nc.setThickness(0.25);
+ nc.setMaterial( new Material.Bulk("test",density,true));
+
+ System.out.println( nc.getComponentVolume() );
+
+ // Volume of filled transition =
+ double filledVolume = Math.PI /3.0 * ( 1.0*1.0 + 1.0 * 0.5 + 0.5 * 0.5 ) * 1.0;
+
+ // magic 2D cad drawing...
+ //
+ // Since the thickness < fore radius, the
+ // hollowed out portion of the transition
+ // forms a transition.
+ // the dimensions of this transition were determined
+ // using a 2d cad tool.
+
+ double innerTransitionAftRadius = 0.7205;
+ double innerTransitionForeRadius = 0.2205;
+ double innerVolume = Math.PI /3.0 * ( innerTransitionAftRadius * innerTransitionAftRadius + innerTransitionAftRadius * innerTransitionForeRadius + innerTransitionForeRadius * innerTransitionForeRadius);
+
+ double volume = filledVolume - innerVolume;
+
+ double mass = density * volume;
+
+ System.out.println( volume );
+
+ assertEquals( volume, nc.getComponentVolume(), epsilonPercent * volume);
+ assertEquals( mass, nc.getMass(), epsilonPercent * mass );
+ }
+
+}