Merge pull request #505 from teyrana/fix_500_cg

[fixes #500] May Calculate CG for fins on zero-dimension mounts
This commit is contained in:
Wes Cravens 2019-01-02 16:52:26 -06:00 committed by GitHub
commit c5733b9b68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 143 additions and 80 deletions

View File

@ -412,8 +412,12 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
- outerArcAngle * filletRadius * filletRadius / 2
- innerArcAngle * bodyRadius * bodyRadius / 2);
// each fin has a fillet on each side
crossSectionArea *= 2;
if(Double.isNaN(crossSectionArea)) {
crossSectionArea = 0.;
}else {
// each fin has a fillet on each side
crossSectionArea *= 2;
}
// heuristic, relTo the body center
double yCentroid = bodyRadius + filletRadius /5;
@ -435,8 +439,8 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
* 5. Return twice that since there is a fillet on each side of the fin.
*/
protected Coordinate calculateFilletVolumeCentroid() {
Coordinate[] bodyPoints = this.getBodyPoints();
if (0 == bodyPoints.length) {
Coordinate[] mountPoints = this.getRootPoints();
if( null == mountPoints ){
return Coordinate.ZERO;
}
@ -446,11 +450,9 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
}
Coordinate filletVolumeCentroid = Coordinate.ZERO;
Coordinate prev = bodyPoints[0];
for (int index = 1; index < bodyPoints.length; index++) {
final Coordinate cur = bodyPoints[index];
Coordinate prev = mountPoints[0];
for (int index = 1; index < mountPoints.length; index++) {
final Coordinate cur = mountPoints[index];
// cross section at mid-segment
final double xAvg = (prev.x + cur.x) / 2;
@ -469,9 +471,6 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
prev = cur;
}
// translate to be relative to the fin-lead-root
filletVolumeCentroid = filletVolumeCentroid.sub(getAxialFront(), 0,0);
if (finCount == 1) {
Transformation rotation = Transformation.rotate_x( getAngleOffset());
return rotation.transform(filletVolumeCentroid);
@ -541,7 +540,7 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
final double xTabTrail_body = xFinFront_body + xTabTrail_fin;
// always returns x coordinates relTo fin front:
Coordinate[] upperCurve = getBodyPoints( xTabFront_body, xTabTrail_body );
Coordinate[] upperCurve = getMountInterval( xTabFront_body, xTabTrail_body );
// locate relative to fin/body centerline
upperCurve = translatePoints( upperCurve, -xFinFront_body, 0.0);
@ -573,14 +572,14 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
private Coordinate calculateSinglePlanformCentroid(){
final Coordinate finFront = getFinFront();
final Coordinate[] upperCurve = translatePoints( getFinPoints(), finFront.x, finFront.y );
final Coordinate[] lowerCurve = getBodyPoints();
final Coordinate[] upperCurve = getFinPoints();
final Coordinate[] lowerCurve = getRootPoints();
final Coordinate[] totalCurve = combineCurves( upperCurve, lowerCurve);
Coordinate planformCentroid = calculateCurveIntegral( totalCurve );
// return as a position relative to fin-root
return planformCentroid.sub(finFront.x,0,0);
return planformCentroid.add(0., finFront.y, 0);
}
/**
@ -1042,11 +1041,12 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
*
* @return points representing the fin-root points, relative to ( x: fin-front, y: centerline ) i.e. relto: fin Component reference point
*/
public Coordinate[] getBodyPoints() {
final double xFinStart = getAxialFront();
final double xFinEnd = xFinStart+getLength();
public Coordinate[] getMountPoints() {
if( null == parent){
return null;
}
return getBodyPoints( xFinStart, xFinEnd);
return getMountInterval(0., parent.getLength());
}
/**
@ -1055,18 +1055,21 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
* @return points representing the fin-root points, relative to ( x: fin-front, y: fin-root-radius )
*/
public Coordinate[] getRootPoints(){
if( null == parent){
return new Coordinate[]{Coordinate.ZERO};
}
final Coordinate finLead = getFinFront();
final double finTailX = finLead.x + getLength();
final Coordinate[] bodyPoints = getBodyPoints( finLead.x, finTailX);
final Coordinate[] bodyPoints = getMountInterval( finLead.x, finTailX);
return translatePoints(bodyPoints, -finLead.x, -finLead.y);
}
private Coordinate[] getBodyPoints( final double xStart, final double xEnd ) {
if( null == parent){
return new Coordinate[]{Coordinate.ZERO};
}
private Coordinate[] getMountInterval( final double xStart, final double xEnd ) {
// System.err.println(String.format(" .... >> mount interval/x: ( %g, %g)]", xStart, xEnd));
// for a simple bodies, one increment is perfectly accurate.
int divisionCount = 1;
@ -1125,7 +1128,8 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab
buf.append( getPointDescr( this.getFinPoints(), "Fin Points", ""));
if (null != parent) {
buf.append( getPointDescr( this.getBodyPoints(), "Body Points", ""));
buf.append( getPointDescr( this.getRootPoints(), "Root Points", ""));
buf.append( getPointDescr( this.getMountPoints(), "Mount Points", ""));
}
if( ! this.isTabTrivial() ) {

View File

@ -1179,13 +1179,13 @@ public class FreeformFinSetTest extends BaseTestCase {
final Coordinate[] finPointsFromBody = FinSet.translatePoints( finPoints, 0.0, fins.getFinFront().y);
{ // body points (relative to body)
final Coordinate[] bodyPoints = fins.getBodyPoints();
final Coordinate[] mountPoints = fins.getMountPoints();
assertEquals("Method should only generate minimal points for a conical transition fin body! ", 2, bodyPoints.length );
assertEquals("incorrect body points! ", finPointsFromBody[0].x, bodyPoints[0].x, EPSILON);
assertEquals("incorrect body points! ", finPointsFromBody[0].y, bodyPoints[0].y, EPSILON);
assertEquals("incorrect body points! ", finPointsFromBody[finPoints.length-1].x, bodyPoints[1].x, EPSILON);
assertEquals("incorrect body points! ", finPointsFromBody[finPoints.length-1].y, bodyPoints[1].y, EPSILON);
assertEquals("Method should only generate minimal points for a conical transition fin body! ", 2, mountPoints.length );
assertEquals("incorrect body points! ", finPointsFromBody[0].x, mountPoints[0].x, EPSILON);
assertEquals("incorrect body points! ", finPointsFromBody[0].y, mountPoints[0].y, EPSILON);
assertEquals("incorrect body points! ", finPointsFromBody[finPoints.length-1].x, mountPoints[1].x, EPSILON);
assertEquals("incorrect body points! ", finPointsFromBody[finPoints.length-1].y, mountPoints[1].y, EPSILON);
}
{ // root points (relative to fin-front)
final Coordinate[] rootPoints = fins.getRootPoints();
@ -1203,18 +1203,16 @@ public class FreeformFinSetTest extends BaseTestCase {
final Rocket rkt = createTemplateRocket();
final FreeformFinSet fins = (FreeformFinSet) rkt.getChild(0).getChild(2).getChild(0);
final Coordinate finFront = fins.getFinFront();
final Coordinate[] finPoints = fins.getFinPoints();
final Coordinate[] finPointsFromBody = FinSet.translatePoints( finPoints, finFront.x, finFront.y);
{ // body points (relative to body)
final Coordinate[] bodyPoints = fins.getBodyPoints();
final Coordinate[] bodyPoints = fins.getMountPoints();
assertEquals("Method should only generate minimal points for a conical transition fin body! ", 2, bodyPoints.length );
assertEquals("incorrect body points! ", finPointsFromBody[0].x, bodyPoints[0].x, EPSILON);
assertEquals("incorrect body points! ", finPointsFromBody[0].y, bodyPoints[0].y, EPSILON);
assertEquals("incorrect body points! ", finPointsFromBody[finPoints.length-1].x, bodyPoints[1].x, EPSILON);
assertEquals("incorrect body points! ", finPointsFromBody[finPoints.length-1].y, bodyPoints[1].y, EPSILON);
assertEquals("incorrect body points! ", 0.0, bodyPoints[0].x, EPSILON);
assertEquals("incorrect body points! ", 1.0, bodyPoints[0].y, EPSILON);
assertEquals("incorrect body points! ", 1.0, bodyPoints[1].x, EPSILON);
assertEquals("incorrect body points! ", 0.5, bodyPoints[1].y, EPSILON);
}
{ // body points (relative to root)
final Coordinate[] rootPoints = fins.getRootPoints();
@ -1236,7 +1234,6 @@ public class FreeformFinSetTest extends BaseTestCase {
final Coordinate finFront = fins.getFinFront();
final Coordinate[] finPoints = fins.getFinPoints();
{ // fin points (relative to fin) // preconditions
assertEquals(4, finPoints.length);
@ -1281,35 +1278,30 @@ public class FreeformFinSetTest extends BaseTestCase {
}
}
}{ // body points (relative to body)
// translate from fin-frame to body-frame
final Coordinate[] finPointsFromBody = FinSet.translatePoints( fins.getFinPoints(), finFront.x, finFront.y );
final Coordinate[] bodyPoints = fins.getBodyPoints();
assertEquals(101, bodyPoints.length);
final Coordinate expectedEndPoint = finPointsFromBody[ finPoints.length-1];
final Coordinate[] mountPoints = fins.getMountPoints();
assertEquals(101, mountPoints.length);
// trivial, and uninteresting:
assertEquals("incorrect body points! ", finPointsFromBody[0].x, bodyPoints[0].x, EPSILON);
assertEquals("incorrect body points! ", finPointsFromBody[0].y, bodyPoints[0].y, EPSILON);
assertEquals("incorrect body points! ", 0.0, mountPoints[0].x, EPSILON);
assertEquals("incorrect body points! ", 0.0, mountPoints[0].y, EPSILON);
// n.b.: This should match EXACTLY the end point of the fin. (in fin coordinates)
assertEquals("incorrect body points! ", expectedEndPoint.x, bodyPoints[bodyPoints.length-1].x, EPSILON);
assertEquals("incorrect body points! ", expectedEndPoint.y, bodyPoints[bodyPoints.length-1].y, EPSILON);
assertEquals("incorrect body points! ", 1.0, mountPoints[mountPoints.length-1].x, EPSILON);
assertEquals("incorrect body points! ", 1.0, mountPoints[mountPoints.length-1].y, EPSILON);
{// the tests within this scope is are rather fragile, and may break for reasons other than bugs :(
// the number of points is somewhat arbitrary, but if this test fails, the rest *definitely* will.
assertEquals("Method is generating how many points, in general? ", 101, bodyPoints.length );
assertEquals("Method is generating how many points, in general? ", 101, mountPoints.length );
final int[] testIndices = { 2, 5, 61, 88};
final double[] expectedX = { 0.036, 0.06, 0.508, 0.724};
final int[] testIndices = { 3, 12, 61, 88};
final double[] expectedX = { 0.03, 0.12, 0.61, 0.88};
for( int testCase = 0; testCase < testIndices.length; testCase++){
final int testIndex = testIndices[testCase];
assertEquals(String.format("Body points @ %d :: x coordinate mismatch!", testIndex),
expectedX[testCase], bodyPoints[testIndex].x, EPSILON);
expectedX[testCase], mountPoints[testIndex].x, EPSILON);
assertEquals(String.format("Body points @ %d :: y coordinate mismatch!", testIndex),
body.getRadius(bodyPoints[testIndex].x), bodyPoints[testIndex].y, EPSILON);
body.getRadius(mountPoints[testIndex].x), mountPoints[testIndex].y, EPSILON);
}
}
}

View File

@ -90,7 +90,7 @@ public class TrapezoidFinSetTest extends BaseTestCase {
final Rocket rkt = createSimpleTrapezoidalFin();
final TrapezoidFinSet fins = (TrapezoidFinSet)rkt.getChild(0).getChild(0).getChild(0);
// This is a simple square fin with sides of 1.0.
// This is a simple square fin with sides of 0.1.
fins.setFinShape(0.1, 0.1, 0.0, 0.1, .005);
// should return a single-fin-planform area
@ -170,14 +170,14 @@ public class TrapezoidFinSetTest extends BaseTestCase {
// / \
// [0] +--------+ [3]
//
assertEquals(0.06, fins.getLength(), EPSILON);
assertEquals("Body radius doesn't match: ", 0.1, body.getOuterRadius(), EPSILON);
final Coordinate actVolume = fins.calculateFilletVolumeCentroid();
assertEquals("Line volume doesn't match: ", 5.973e-07, actVolume.weight, EPSILON);
assertEquals("Line mass center.x doesn't match: ", 0.03, actVolume.x, EPSILON);
assertEquals("Line mass center.y doesn't match: ", 0.101, actVolume.y, EPSILON);
assertEquals("Fin volume doesn't match: ", 5.973e-07, actVolume.weight, EPSILON);
assertEquals("Fin mass center.x doesn't match: ", 0.03, actVolume.x, EPSILON);
assertEquals("Fin mass center.y doesn't match: ", 0.101, actVolume.y, EPSILON);
{ // and then, check that the fillet volume feeds into a correct overall CG:
@ -189,7 +189,6 @@ public class TrapezoidFinSetTest extends BaseTestCase {
@Test
public void testTrapezoidCGComputation() {
{
// This is a simple square fin with sides of 1.0.
TrapezoidFinSet fins = new TrapezoidFinSet();
@ -200,9 +199,7 @@ public class TrapezoidFinSetTest extends BaseTestCase {
assertEquals(1.0, fins.getPlanformArea(), 0.001);
assertEquals(0.5, coords.x, 0.001);
assertEquals(0.5, coords.y, 0.001);
}
{
}{
// This is a trapezoid. Height 1, root 1, tip 1/2 no sweep.
// It can be decomposed into a rectangle followed by a triangle
// +---+
@ -218,7 +215,77 @@ public class TrapezoidFinSetTest extends BaseTestCase {
assertEquals(0.3889, coords.x, 0.001);
assertEquals(0.4444, coords.y, 0.001);
}
}
@Test
public void testGetBodyPoints_phantomMount() {
final Rocket rkt = createSimpleTrapezoidalFin();
// set mount to have zero-dimensions:
final BodyTube mount = (BodyTube)rkt.getChild(0).getChild(0);
mount.setLength(0.0);
mount.setOuterRadius(0.0);
assertEquals( 0, mount.getLength(), 0.00001);
assertEquals( 0, mount.getOuterRadius(), 0.00001);
assertEquals( 0, mount.getInnerRadius(), 0.00001);
final TrapezoidFinSet fins = (TrapezoidFinSet)mount.getChild(0);
final Coordinate[] mountPoints = fins.getMountPoints();
assertEquals(2, mountPoints.length );
assertEquals( 0.00, mountPoints[0].x, 0.00001);
assertEquals( 0.00, mountPoints[0].y, 0.00001);
assertEquals( 0.00, mountPoints[1].x, 0.00001);
assertEquals( 0.00, mountPoints[1].y, 0.00001);
}
@Test
public void testGetBodyPoints_zeroLengthMount() {
final Rocket rkt = createSimpleTrapezoidalFin();
// set mount to have zero-dimensions:
final BodyTube mount = (BodyTube)rkt.getChild(0).getChild(0);
mount.setLength(0.0);
mount.setOuterRadius(0.1);
mount.setInnerRadius(0.08);
assertEquals( 0, mount.getLength(), 0.00001);
assertEquals( 0.1, mount.getOuterRadius(), 0.00001);
assertEquals( 0.08, mount.getInnerRadius(), 0.00001);
final TrapezoidFinSet fins = (TrapezoidFinSet)mount.getChild(0);
final Coordinate[] mountPoints = fins.getMountPoints();
assertEquals(2, mountPoints.length );
assertEquals( 0.0, mountPoints[0].x, 0.00001);
assertEquals( 0.1, mountPoints[0].y, 0.00001);
assertEquals( 0.0, mountPoints[1].x, 0.00001);
assertEquals( 0.1, mountPoints[1].y, 0.00001);
}
@Test
public void testTrapezoidCGComputation_phantomMount() {
final Rocket rkt = createSimpleTrapezoidalFin();
// set mount to have zero-dimensions:
final BodyTube mount = (BodyTube)rkt.getChild(0).getChild(0);
mount.setLength(0.0);
mount.setOuterRadius(0.0);
assertEquals( 0, mount.getLength(), 0.00001);
assertEquals( 0, mount.getOuterRadius(), 0.00001);
assertEquals( 0, mount.getInnerRadius(), 0.00001);
final TrapezoidFinSet fins = (TrapezoidFinSet)mount.getChild(0);
assertEquals( 0.06, fins.getLength(), 0.00001);
assertEquals( 0.05, fins.getHeight(), 0.00001);
assertEquals( 0.06, fins.getRootChord(), 0.00001);
assertEquals( 0.02, fins.getTipChord(), 0.00001);
final Coordinate coords = fins.getCG();
assertEquals(0.002, fins.getPlanformArea(), 0.001);
assertEquals(0.03, coords.x, 0.001);
assertEquals(0.02, coords.y, 0.001);
}
@Test