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