PageRenderTime 27ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/farmR/src/java/src/jfm/model/Operation.java

https://code.google.com/p/javawfm/
Java | 333 lines | 266 code | 29 blank | 38 comment | 45 complexity | 92fd09b07afcc20b96e20b1ab200ee7e MD5 | raw file
Possible License(s): Apache-2.0
  1. package jfm.model;
  2. import java.util.*;
  3. import jfm.lp.ModelPrimitive;
  4. import jfm.model.Crop.CropCopy;
  5. import jfm.model.Types.*;
  6. import jfm.utils.JFMMath;
  7. import jfm.utils.MathPrint;
  8. import jfm.xml.XMLSyntaxException;
  9. /** Controls yield and cost penalties, workrates, machine requirements and constraints on an operation.
  10. * Holds all the data relating to an instance of an operation on a single crop. It is important to
  11. * note that because this is a model primitive class a separate instance is required for every year of
  12. * every crop to ensure that the final LP matrix is correctly constructed.
  13. *
  14. * @author Ira Cooke*/
  15. public final class Operation extends ModelPrimitive {
  16. private final int numPeriods;
  17. private int printLeadingDigits=2;
  18. private final HashMap<Integer,Double> yieldPenalties =new HashMap<Integer,Double>();
  19. private final HashMap<Integer,Double> costPenalties = new HashMap<Integer,Double>();
  20. private final HashMap<Integer,Double> minAreas = new HashMap<Integer,Double>();
  21. private boolean hasMinAreas=false;
  22. private final HashMap<Integer,Double> fuelCostCached =new HashMap<Integer,Double>();
  23. private final HashMap<Integer,Double> penaltyCostCached =new HashMap<Integer,Double>();
  24. private final Formula workrateF;
  25. private final HashMap<WorkerType,Integer> numMachines;
  26. private final WorkerType firstMachine;
  27. private CropCopy parentCrop=null;
  28. public final OperationType type;
  29. public final WorkabilityType workability;
  30. private boolean isSequential = true;
  31. private boolean isHandOver = false;
  32. private boolean isAttached=false;
  33. public final int gap;
  34. /** Return a working copy of the Operation. */
  35. public Operation copy(){
  36. Operation op = new Operation(type,numPeriods,workability,workrateF,numMachines,gap,firstMachine);
  37. // op.setAllowable(periodsAllowed);
  38. op.setYieldPenalties(yieldPenalties);
  39. op.setCostPenalties(costPenalties);
  40. op.setMinAreas(minAreas);
  41. if ( hasMinAreas ){ op.hasMinAreas=true;};
  42. // op.setHoursRequiredForWorker(hoursRequired);
  43. if ( isHandOver){ op.setHandOver(); };
  44. if ( !isSequential){ op.setNonSequential(); };
  45. return op;
  46. }
  47. /** Return the number of machines of a particular type required for this operation */
  48. Integer numRequiredForWorkerType(WorkerType wtype){
  49. if ( !numMachines.containsKey(wtype)){
  50. throw new Error("The worker type "+wtype+" is not defined for operation "+type);
  51. }
  52. return numMachines.get(wtype);
  53. }
  54. /** Returns true if this operation requires workers of the type of its argument */
  55. boolean requiresWorker(WorkerType wtype){
  56. return numMachines.containsKey(wtype);
  57. }
  58. Set<WorkerType> workerSet(){
  59. return numMachines.keySet();
  60. }
  61. /** Construct a new operation
  62. * @param type_ The operation type
  63. * @param nP The number of periods in the farming year (required to wrap periods )
  64. * @param wkability The workability type
  65. * @param formul A Formula giving the workrate as a function of location
  66. * @param numMachines_ The number of machines of each WorkerType required
  67. * @param gap_ The gap in periods to be enforced between this operation and the next
  68. * */
  69. public Operation(OperationType type_,int nP,WorkabilityType wkability,Formula formul,
  70. Map<WorkerType,Integer> numMachines_,int gap_,WorkerType firstMach) {
  71. gap=gap_;
  72. type=type_;
  73. numPeriods=nP;
  74. workability=wkability;
  75. numMachines=(HashMap<WorkerType,Integer>)numMachines_;
  76. firstMachine=firstMach;
  77. // This variable never changes for the life of the operation .. for now
  78. workrateF=formul;
  79. if ( formul!=null){
  80. workrateF.setVariable(VariableType.SIZEFIRSTMACHINE,firstMachine.size);
  81. }
  82. }
  83. public void attachToCrop(CropCopy parent){
  84. if ( !isAttached){
  85. parentCrop=parent;
  86. isAttached=true;
  87. } else {
  88. throw new Error("An Operation cannot be attached to more than one crop");
  89. }
  90. }
  91. protected void setFormulaVariables(){
  92. Farm parentFarm = parentComponent.getParent();
  93. Location loc = parentFarm.location();
  94. if ( workrateF!=null){
  95. for ( VariableType ltype:loc.variableSet()){
  96. // if ( loc.valueChanged(ltype)){
  97. workrateF.setVariable(ltype, loc.getVariable(ltype));
  98. // }
  99. }
  100. for ( CropInput cinp: parentCrop.baseCrop().inputSet()){
  101. // if ( cinp.valueChanged(cinp.associatedVariable)){
  102. workrateF.setVariable(cinp.associatedVariable, cinp.getVariable(cinp.associatedVariable));
  103. // }
  104. }
  105. workrateF.setVariable(VariableType.PRIMARYYIELD, parentCrop.baseCrop().getPrimaryYield());
  106. workrateF.setVariable(VariableType.SECONDARYYIELD, parentCrop.baseCrop().getSecondaryYield());
  107. }
  108. }
  109. protected void updateStructure(Object caller){
  110. Farm parentFarm=parentComponent.getParent();
  111. List<Integer> pallow = new ArrayList<Integer>(unfoldedAllowedSet());
  112. Collections.sort(pallow); // We need to ensure that columns for each set of periods
  113. for(Integer p:pallow){
  114. setCoefficient(ObjectiveType.PROFIT,-cost(p,(CropCopy)caller,parentFarm.location(),parentFarm.fuelPrice()),p);
  115. }
  116. }
  117. /** Calculate the cost of an operation.
  118. * @param period The period in the year
  119. * @param cp The CropCopy on which this operation is occuring
  120. * @param loc The location information used to calculate workrates
  121. * @param fuelPrice Price of fuel in pounds / liter */
  122. public double cost(int period,CropCopy cp,Location loc,double fuelPrice){
  123. double penaltyCost=0;
  124. double fuelCost=0;
  125. setFormulaVariables();
  126. if ( yieldPenalties.containsKey(period)){
  127. penaltyCost=cp.grossMarginLossForYieldPenalty(yieldPenalties.get(period));
  128. penaltyCost+=costPenalties.get(period);
  129. penaltyCostCached.put(period, penaltyCost);
  130. } else {
  131. throw new Error("Attempt to get cost for unknown period in operation "+this.toString());
  132. }
  133. if ( workrateF!=null){
  134. double fuel=0;
  135. for ( WorkerType wt:numMachines.keySet()){
  136. // Adding in cost of fuel per hectare
  137. fuel+=wt.litresPerHour();
  138. }
  139. if ( workrate(loc) <=0){
  140. throw new Error("Can't set cost for operation because workrate is "+workrateF.calculateValue()+" in op "+type+" with wkrateF "+workrateF.getFormula());
  141. }
  142. fuelCost+=fuel*fuelPrice*workrateF.calculateValue();
  143. }
  144. fuelCostCached.put(period, fuelCost);
  145. return fuelCost+penaltyCost;
  146. }
  147. public double solvedPenaltyCost(){
  148. double[] sol=getSolution(); // Should be sorted by period;
  149. if ( sol.length != penaltyCostCached.size()){
  150. throw new Error("Can't calculated solved penalty cost because of array length mismatch");
  151. }
  152. double total=0;
  153. int i=0;
  154. for ( Double cost:penaltyCostCached.values()){
  155. total+=cost*sol[i];
  156. i++;
  157. }
  158. return total;
  159. }
  160. public double solvedArea(int period){
  161. return getDependent(period).solution();
  162. }
  163. public double solvedFuelCost(){
  164. double[] sol=getSolution(); // Should be sorted by period;
  165. if ( sol.length != fuelCostCached.size()){
  166. throw new Error("Can't calculated solved fuel cost because of array length mismatch");
  167. }
  168. double total=0;
  169. int i=0;
  170. for ( Double cost:fuelCostCached.values()){
  171. total+=cost*sol[i];
  172. i++;
  173. }
  174. return total;
  175. }
  176. double workrate(Location loc){
  177. if ( !isAttached ){
  178. throw new Error("Cannot get workrate for operation because it is not attached to a crop");
  179. }
  180. if ( workrateF!=null){
  181. return workrateF.calculateValue();
  182. } else {
  183. return 0.0;
  184. }
  185. }
  186. // These are just for the copy constructor
  187. private void setYieldPenalties(HashMap<Integer,Double> oldpenalties){
  188. for(Integer i:oldpenalties.keySet()){
  189. yieldPenalties.put(i, oldpenalties.get(i));
  190. }
  191. }
  192. private void setCostPenalties(HashMap<Integer,Double> oldpenalties){
  193. for(Integer i:oldpenalties.keySet()){
  194. costPenalties.put(i,oldpenalties.get(i));
  195. }
  196. }
  197. private void setMinAreas(HashMap<Integer,Double> oldminareas){
  198. for(Integer i:oldminareas.keySet()){
  199. minAreas.put(i,oldminareas.get(i));
  200. }
  201. }
  202. public void setMinAreas(int[] perAllow,double[] mina) throws XMLSyntaxException {
  203. hasMinAreas=true;
  204. if ( perAllow.length != mina.length){ throw new XMLSyntaxException("Periods length mismatch "+perAllow.length+" vs "+mina.length);};
  205. for (int i = 0 ; i < perAllow.length;i++){
  206. minAreas.put(perAllow[i],mina[i]);
  207. }
  208. requireMatrixRebuild();
  209. }
  210. public void setYieldPenalties(int[] perAllow,double[] penalt) throws XMLSyntaxException {
  211. if ( perAllow.length != penalt.length){ throw new XMLSyntaxException("Periods length mismatch "+perAllow.length+" vs "+penalt.length);};
  212. for (int i = 0 ; i < perAllow.length;i++){
  213. yieldPenalties.put(perAllow[i],penalt[i]);
  214. }
  215. requireMatrixRebuild();
  216. }
  217. public void setCostPenalties(int[] perAllow,double[] costp) throws XMLSyntaxException {
  218. if ( perAllow.length != costp.length){ throw new XMLSyntaxException("Periods length mismatch for cost penalties");}
  219. for(int i=0;i< perAllow.length;i++){
  220. costPenalties.put(perAllow[i],costp[i]);
  221. }
  222. requireMatrixRebuild();
  223. }
  224. public String name(){return "Operation";};
  225. public boolean hasMinAreas(){return hasMinAreas;};
  226. public double minArea(int period){
  227. if ( hasMinAreas){
  228. return minAreas.get(period);
  229. } else {
  230. throw new Error("Attempt to get min area but non defined for this operation");
  231. }
  232. }
  233. public void setNonSequential(){
  234. isSequential=false;
  235. requireMatrixRebuild();
  236. };
  237. public boolean isSequential(){return isSequential;};
  238. public void setHandOver(){
  239. isHandOver= true;
  240. requireMatrixRebuild();
  241. };
  242. public boolean isHandOver(){ return isHandOver;};
  243. /** Return a set of allowed periods for this operation.
  244. * The set returned consists of unfolded periods (ie not wrapped around periodic boundaries )*/
  245. public Set<Integer> unfoldedAllowedSet(){
  246. return yieldPenalties.keySet();
  247. }
  248. /** Return a set of allowed periods for this operation.
  249. * The set returned consists of folded periods (ie wrapped around the periodic boundary )*/
  250. public Set<Integer> foldedAllowedSet(){
  251. HashSet<Integer> allow=new HashSet<Integer>();
  252. for (Integer p:yieldPenalties.keySet()){
  253. allow.add(Farm.wrapPeriod(p, numPeriods));
  254. }
  255. return allow;
  256. }
  257. public void setLeadingDigits(int ld){printLeadingDigits=ld;};
  258. public String printWorkRateDetails(Location loc){
  259. StringBuffer outstring = new StringBuffer();
  260. outstring.append(this.workrate(loc));
  261. outstring.append(',');
  262. outstring.append(this.workability);
  263. return outstring.toString();
  264. }
  265. public String printSolution(char sep){
  266. StringBuffer outstring = new StringBuffer();
  267. outstring.append(type.shortName);
  268. outstring.append(sep);
  269. double[] sol=getSolution();
  270. double[] fullsol=new double[numPeriods];
  271. JFMMath.doubleZero(fullsol);
  272. int i=0;
  273. // To give sensible output we need to sort periods in numerical order
  274. ArrayList<Integer> pallow= new ArrayList<Integer>(unfoldedAllowedSet());
  275. Collections.sort(pallow);
  276. for(Integer p:pallow){
  277. fullsol[Farm.wrapPeriod(p, numPeriods)]=sol[i];
  278. i++;
  279. }
  280. outstring.append(MathPrint.printVector(fullsol,printLeadingDigits,sep));
  281. return outstring.toString();
  282. }
  283. public String toString(){
  284. StringBuffer outstring = new StringBuffer();
  285. outstring.append("Operation: "+type.shortName);
  286. outstring.append(" isHandOver: "+isHandOver);
  287. outstring.append(" NumPeriods: ");
  288. outstring.append(numPeriods);
  289. outstring.append(" \n");
  290. // outstring.append("Periods Allowed: "+ReluMath.printVector(periodsAllowed));
  291. outstring.append("Areas by Period: "+MathPrint.printVector(getSolution(),2,' '));
  292. outstring.append("Penalties: "+MathPrint.printVector(getCoefficients(ObjectiveType.PROFIT),2,' '));
  293. for ( ObjectiveType ot:objectives()){
  294. if ( ot != ObjectiveType.PROFIT){
  295. outstring.append(ot+": "+MathPrint.printVector(getCoefficients(ot),2,' '));
  296. }
  297. }
  298. outstring.append("Hours Required: \n");
  299. for(WorkerType wt:WorkerType.values()){
  300. // outstring.append(wt.shortName+" "+ReluMath.printVector(hoursRequired.get(wt)));
  301. }
  302. return outstring.toString();
  303. }
  304. }