Fixed an edge case null pointer on a 0 thrust motor
[sw/motorsim] / src / com / billkuker / rocketry / motorsim / ConvergentDivergentNozzle.java
1 package com.billkuker.rocketry.motorsim;\r
2 \r
3 import java.awt.Shape;\r
4 import java.awt.geom.AffineTransform;\r
5 import java.awt.geom.GeneralPath;\r
6 import java.awt.geom.Line2D;\r
7 \r
8 import javax.measure.quantity.Area;\r
9 import javax.measure.quantity.Dimensionless;\r
10 import javax.measure.quantity.Force;\r
11 import javax.measure.quantity.Length;\r
12 import javax.measure.quantity.Pressure;\r
13 import javax.measure.unit.SI;\r
14 \r
15 import org.jscience.physics.amount.Amount;\r
16 \r
17 public class ConvergentDivergentNozzle extends MotorPart implements Nozzle {\r
18 \r
19         private Amount<Length> throatDiameter;\r
20         \r
21         private Amount<Length> exitDiameter;\r
22         \r
23         private double efficiency = 1.0;\r
24         \r
25         \r
26         public Amount<Area> throatArea() {\r
27                 return throatDiameter.divide(2).pow(2).times(Math.PI).to(Area.UNIT);\r
28         }\r
29         \r
30         public Amount<Area> exitArea() {\r
31                 return exitDiameter.divide(2).pow(2).times(Math.PI).to(Area.UNIT);\r
32         }\r
33 \r
34 \r
35         public Amount<Length> getThroatDiameter() {\r
36                 return throatDiameter;\r
37         }\r
38 \r
39 \r
40         public void setThroatDiameter(Amount<Length> throatDiameter) {\r
41                 if ( exitDiameter != null && throatDiameter.isGreaterThan(exitDiameter))\r
42                         throw new IllegalArgumentException("Throat > Exit");\r
43                 Amount<Length> old = this.throatDiameter;\r
44                 this.throatDiameter = throatDiameter;\r
45                 firePropertyChange("throatDiameter", old, throatDiameter);\r
46         }\r
47         \r
48 \r
49         public Amount<Length> getExitDiameter() {\r
50                 return exitDiameter;\r
51         }\r
52 \r
53 \r
54         public void setExitDiameter(Amount<Length> exitDiameter) {\r
55                 if ( throatDiameter != null && exitDiameter.isLessThan(throatDiameter))\r
56                         throw new IllegalArgumentException("Throat > Exit");\r
57                 Amount<Length> old = this.exitDiameter;\r
58                 this.exitDiameter = exitDiameter;\r
59                 firePropertyChange("exitDiameter", old, exitDiameter);\r
60         }\r
61         \r
62         public Amount<Force> thrust(Amount<Pressure> Po, Amount<Pressure> Pe, Amount<Pressure> Patm, final double k ){\r
63                 double cF = thrustCoefficient(Po, Pe, Patm, k);\r
64                 return Po.times(throatArea()).times(cF).to(Force.UNIT);\r
65         }\r
66 \r
67         public double thrustCoefficient(Amount<Pressure> Po, Amount<Pressure> Pe, Amount<Pressure> Patm, final double k ){\r
68                 double pRatio = Pe.divide(Po).to(Dimensionless.UNIT).doubleValue(Dimensionless.UNIT);\r
69 \r
70                 double a = (2*k*k)/(k-1);\r
71                 \r
72                 double b = Math.pow(2/(k+1), (k+1)/(k-1) );\r
73                 \r
74                 double c = 1.0 - Math.pow(pRatio, (k-1)/k);\r
75                 \r
76                 Amount<Pressure> pDiff = Pe.minus(Patm);\r
77                 \r
78                 double d = pDiff.times(exitArea()).divide(Po.times(throatArea())).to(Dimensionless.UNIT).doubleValue(Dimensionless.UNIT);\r
79                 \r
80                 double Cf = efficiency * Math.sqrt(a*b*c)+d;\r
81                 \r
82                 return Cf;\r
83         }\r
84 \r
85         public double getEfficiency() {\r
86                 return efficiency;\r
87         }\r
88 \r
89         public void setEfficiency(double efficiency) {\r
90                 double old = this.efficiency;\r
91                 this.efficiency = efficiency;\r
92                 firePropertyChange("efficiency", old, efficiency);\r
93         }\r
94 \r
95         public Shape nozzleShape(Amount<Length> chamberDiameter){\r
96                 GeneralPath s = new GeneralPath();\r
97                 double throatR = throatDiameter.divide(2).doubleValue(SI.MILLIMETER);\r
98                 double exitR = exitDiameter.divide(2).doubleValue(SI.MILLIMETER);\r
99                 double cR;\r
100                 if (chamberDiameter == null)\r
101                         cR = exitR;\r
102                 else\r
103                         cR = chamberDiameter.divide(2).doubleValue(SI.MILLIMETER);\r
104                 \r
105                 double diff = exitR-throatR;\r
106                 double cDiff = cR-throatR;\r
107                 \r
108                 s.append(new Line2D.Double(0,0,diff,diff*3), false);\r
109                 s.append(new Line2D.Double(0,0,cDiff,-cDiff), true);\r
110                 \r
111                 s.transform(AffineTransform.getScaleInstance(-1, 1));\r
112                 s.transform(AffineTransform.getTranslateInstance(-throatR, 0));\r
113                 \r
114                 s.append(new Line2D.Double(0,0,diff,diff*3), false);\r
115                 s.append(new Line2D.Double(0,0,cDiff,-cDiff), true);\r
116                 \r
117                 //a.add(new java.awt.geom.Area( new Ellipse2D.Double(0,0,5,5)));\r
118                 \r
119                 return s;\r
120         }\r
121 }\r