a4922f06c4613a66dd86473af440458008ac0a7e
[sw/motorsim] / src / com / billkuker / rocketry / motorsim / fuel / PiecewiseLinearFuel.java
1 package com.billkuker.rocketry.motorsim.fuel;\r
2 \r
3 import java.net.URI;\r
4 import java.util.HashMap;\r
5 import java.util.Map;\r
6 import java.util.NoSuchElementException;\r
7 import java.util.SortedMap;\r
8 import java.util.TreeMap;\r
9 \r
10 import javax.measure.quantity.Dimensionless;\r
11 import javax.measure.quantity.Pressure;\r
12 import javax.measure.quantity.Velocity;\r
13 import javax.measure.quantity.VolumetricDensity;\r
14 import javax.measure.unit.SI;\r
15 \r
16 import org.apache.log4j.Logger;\r
17 import org.jscience.physics.amount.Amount;\r
18 \r
19 import com.billkuker.rocketry.motorsim.Fuel;\r
20 import com.billkuker.rocketry.motorsim.visual.Chart;\r
21 \r
22 public class PiecewiseLinearFuel implements Fuel{\r
23         private static final Logger log = Logger.getLogger(PiecewiseLinearFuel.class);\r
24         private static final Amount<Pressure> ZERO_PRESSURE =Amount.valueOf(0, SI.PASCAL);\r
25         private static final Amount<Velocity> ZERO_VELOCITY =Amount.valueOf(0, SI.METERS_PER_SECOND);\r
26 \r
27         private static class Entry implements Comparable<Entry>{\r
28                 Amount<Pressure> pressure;\r
29                 Amount<Velocity> burnRate;\r
30                 @Override\r
31                 public int compareTo(Entry o) {\r
32                         if ( o.pressure.approximates(pressure) )\r
33                                 return 0;\r
34                         return o.pressure.isGreaterThan(pressure)?-1:1;\r
35                 }\r
36         }\r
37 \r
38         \r
39         private String name = "New Linear Fuel";\r
40         private URI uri;\r
41         private double combustionEfficiency = .97;\r
42         private double densityRatio = .96;\r
43         private Amount<VolumetricDensity> density = Amount.valueOf(1889, 0, SI.KILOGRAM.divide(SI.METER.pow(3))).to(VolumetricDensity.UNIT);\r
44 \r
45         \r
46         private EditableCombustionProduct product = new EditableCombustionProduct();\r
47         private SortedMap<Amount<Pressure>, Entry> entries ;\r
48 \r
49         public PiecewiseLinearFuel(){\r
50                 clear();\r
51         }\r
52         public void add(final Amount<Pressure> p, final Amount<Velocity> r){\r
53                 entries.put(p, new Entry(){{pressure = p; burnRate = r;}});\r
54         }\r
55         \r
56         @Override\r
57         public Amount<Velocity> burnRate(final Amount<Pressure> pressure) {\r
58                 if ( pressure.isLessThan(ZERO_PRESSURE) )\r
59                         return ZERO_VELOCITY;\r
60                 \r
61                 if ( entries.size() == 1 ){\r
62                         return entries.get(entries.firstKey()).burnRate;\r
63                 }\r
64                 \r
65                 if ( entries.containsKey(pressure) ){\r
66                         return entries.get(pressure).burnRate;\r
67                 }\r
68                         \r
69                 Entry low = null;\r
70                 low = entries.get(entries.headMap(pressure).lastKey());\r
71                 Entry high = null;\r
72                 try {\r
73                         high = entries.get(entries.tailMap(pressure).firstKey());\r
74                 } catch ( NoSuchElementException e ){\r
75                         log.warn("Pressure " + pressure + " is outside of expiermental range for " + this.getName());\r
76                         high = low;\r
77                         low = entries.get(entries.headMap(low.pressure).lastKey());\r
78                 }\r
79                 \r
80                 Amount<Pressure> lowToHigh = high.pressure.minus(low.pressure);\r
81                 Amount<Pressure> lowToTarget = pressure.minus(low.pressure);\r
82                 Amount<Dimensionless> frac = lowToTarget.divide(lowToHigh).to(Dimensionless.UNIT);\r
83                 \r
84                 Amount<Velocity> vdiff = high.burnRate.minus(low.burnRate);\r
85                 Amount<Velocity> ret = low.burnRate.plus(vdiff.times(frac));\r
86                 \r
87                 if ( ret.isLessThan(ZERO_VELOCITY) )\r
88                         return ZERO_VELOCITY;\r
89                 \r
90                 return ret;\r
91                 \r
92         }\r
93         \r
94         public void clear(){\r
95                 entries = new TreeMap<Amount<Pressure>, Entry>();\r
96                 add(Amount.valueOf(0,SI.MEGA(SI.PASCAL)), Amount.valueOf(0, SI.METERS_PER_SECOND));\r
97         }\r
98         \r
99         @Override\r
100         public double getCombustionEfficiency() {\r
101                 return combustionEfficiency;\r
102         }\r
103         \r
104 \r
105         public void setCombustionEfficiency(double combustionEfficiency) {\r
106                 this.combustionEfficiency = combustionEfficiency;\r
107         }\r
108         \r
109         @Override\r
110         public EditableCombustionProduct getCombustionProduct(){\r
111                 return product;\r
112         }\r
113         \r
114         public void setCombustionProduct(final EditableCombustionProduct product){\r
115                 this.product = product;\r
116         }\r
117         \r
118         @Override\r
119         public double getDensityRatio() {\r
120                 return densityRatio;\r
121         }\r
122         \r
123         public void setDensityRatio(double densityRatio) {\r
124                 this.densityRatio = densityRatio;\r
125         }\r
126 \r
127         @Override\r
128         public Amount<VolumetricDensity> getIdealDensity() {\r
129                 return density;\r
130         }\r
131         \r
132         public void setIdealDensity(Amount<VolumetricDensity> density) {\r
133                 this.density = density;\r
134         }\r
135 \r
136         @Override\r
137         public String getName() {\r
138                 return name;\r
139         }\r
140         \r
141         public void setName(String name) {\r
142                 this.name = name;\r
143         }\r
144 \r
145         @Override\r
146         public URI getURI() {\r
147                 return uri;\r
148         }\r
149 \r
150 \r
151         \r
152         public Map<Amount<Pressure>, Amount<Velocity>> getEntries() {\r
153                 HashMap<Amount<Pressure>, Amount<Velocity>> ret = new HashMap<Amount<Pressure>, Amount<Velocity>>();\r
154                 for ( Entry e : entries.values() )\r
155                         ret.put(e.pressure, e.burnRate);\r
156                 return ret;\r
157         }\r
158         \r
159         public void setEntries(Map<Amount<Pressure>, Amount<Velocity>> in) {\r
160                 clear();\r
161                 for ( Map.Entry<Amount<Pressure>, Amount<Velocity>> e : in.entrySet()){\r
162                         add( e.getKey(), e.getValue());\r
163                 }\r
164         }\r
165 \r
166 \r
167         \r
168         public static void main( String args[]) throws Exception{\r
169                 PiecewiseLinearFuel f = new PiecewiseLinearFuel();\r
170                 f.add(Amount.valueOf(0,SI.MEGA(SI.PASCAL)), Amount.valueOf(2, SI.METERS_PER_SECOND));\r
171                 //f.add(Amount.valueOf(2,SI.MEGA(SI.PASCAL)), Amount.valueOf(2, SI.METERS_PER_SECOND));\r
172                 //f.add(Amount.valueOf(4,SI.MEGA(SI.PASCAL)), Amount.valueOf(1, SI.METERS_PER_SECOND));\r
173                 //f.add(Amount.valueOf(10,SI.MEGA(SI.PASCAL)), Amount.valueOf(3, SI.METERS_PER_SECOND));\r
174                 //f.add(Amount.valueOf(20,SI.MEGA(SI.PASCAL)), Amount.valueOf(4, SI.METERS_PER_SECOND));\r
175                 Chart<Pressure, Velocity> burnRate = new Chart<Pressure, Velocity>(\r
176                                 SI.MEGA(SI.PASCAL),\r
177                                 SI.METERS_PER_SECOND,\r
178                                 f,\r
179                                 "burnRate");\r
180                 burnRate.setDomain(\r
181                                 burnRate.new IntervalDomain(\r
182                                                 Amount.valueOf(0, SI.MEGA(SI.PASCAL)),\r
183                                                 Amount.valueOf(11, SI.MEGA(SI.PASCAL)),\r
184                                                 200\r
185                                                 ));\r
186                 \r
187                 burnRate.show();\r
188         }\r
189 }\r