Originally posted by: PreranaJ
Hi Philipee,
Thank you for your reply.
The scheduling problem is as follows - There are n jobs which are to be scheduled on m workcenters. There are two types of operations a job has to undergo - 1. Assembly 2.Testing.
Testing workcenters are different from assembly workcenters. A job can need either one workcenter or two workcenters or three workcenters for an operation.
Hard Constraints - Testing of the job should happen immediately after the assembly. A workcenter can process only one job at a time. Few jobs have a fixed starting time.
Objective - Schedule all jobs and Minimize number of early jobs, late jobs, moved jobs, etc
The way I modeled it - I consider two separate jobs for assembly and testing. So if I have 100 jobs to schedule, I will have 200 jobs (considering testing and assembly) interval variables. Also every combination of job and workcenters thats allowed is an interval decision variable.
Here is the model -
/*********************************************
* OPL 12.6.2.0 Model
* Author: Prerana
* Creation Date: Oct 9, 2016 at 11:12:42 PM
*********************************************/
using CP;
range booleanValues = 0..1;
execute settings{
//cp.param.TimeLimit = 1000;
}
tuple Tjobs
{
key int jobID;
string jobdescription;
string jobpropertyID;
string jobpropertyType;
string AssmTestType;
int Early ;
int SRD ;
int Deadline;
int CurrentSolution ;
int Edit;
int Lock ;
int isAssembly;
int StartDeadline;
int jobtime;
};
{Tjobs} jobs with isAssembly in booleanValues = ...;
tuple Tworkcenter
{
key int WorkcenterID;
string WorkcenterDescription;
int WorkcenterTypeIsAssembly;
int usage;
};
{Tworkcenter} workCenters with WorkcenterTypeIsAssembly in booleanValues =...;
tuple Tjobduration
{
Tjobs jobID;
Tworkcenter workCenterID1;
Tworkcenter workCenterID2;
Tworkcenter workCenterID3;
int duration;
};
{Tjobduration} jobDuration with jobID in jobs,workCenterID1 in workCenters ,workCenterID2 in workCenters ,workCenterID3 in workCenters = ...;
tuple Tdependentjobs
{
Tjobs jobID1;
Tjobs jobID2;
}
{Tdependentjobs} dependentJobs with jobID1 in jobs, jobID2 in jobs = ...;
tuple TWorkCenterJobAssignment {
int jobId;
int workCenterId1;
int workCenterId2;
int workCenterId3;
float jobStartTime;
float jobFinishTime;
};
{TWorkCenterJobAssignment} workCenterJobAssignment;
{Tjobs} assemblyjobs = {c| c in jobs: c.isAssembly == 1};
{Tjobs} testjobs = {c| c in jobs: c.isAssembly == 0};
{Tjobs} assemblynoneditablejobs = {c| c in jobs: c.isAssembly == 1 && c.Edit == 0};
//preprocessing of data
int Duration[j in jobDuration]= j.duration;
int earlystartTime[w in jobDuration] = w.jobID.StartDeadline;
int H = 100;
int JobTime [j in jobs] = j.jobtime;
// declaraing decision variables
dvar interval nJobs [j in jobs] size JobTime[j] ;
dvar interval altWcJob [w in jobDuration] optional in earlystartTime[w]..H size Duration[w] ;
dvar sequence workcenter[k in workCenters] in all
(w in jobDuration: w.workCenterID1.WorkcenterID == k.WorkcenterID || w.workCenterID2.WorkcenterID == k.WorkcenterID || w.workCenterID3.WorkcenterID == k.WorkcenterID)altWcJob[w];
// calculation of decision expressions for objective value
//Distance from SRD
dexpr float DistanceFromSRD = sum (j in jobs: j.isAssembly == 0)abs(endOf(nJobs[j])-presenceOf(nJobs[j])*(j.SRD));
//Distance from the deadline
dexpr float DistanceFromDeadline = sum (j in jobs: j.isAssembly == 0)abs(endOf(nJobs[j])-presenceOf(nJobs[j])*(j.Deadline));
//Sum of Early Jobs
dexpr float SumofEarlyJobs = sum(j in assemblyjobs) (maxl(j.Early - startOf(nJobs[j]),0)/maxl(j.Early - startOf(nJobs[j]),0.1));
//Sum of Late Jobs
dexpr float SumofLateJobs = sum(j in testjobs)(maxl(endOf(nJobs[j]) - j.Deadline,0)/maxl(endOf(nJobs[j]) - j.Deadline,0.1));
//Sum of NonEdit Jobs That are Moved
dexpr float SumofNonEditJobsMoved = sum(j in assemblynoneditablejobs)abs(startOf(nJobs[j]) - j.CurrentSolution)/maxl(abs(startOf(nJobs[j]) - j.CurrentSolution),0.1);
//Total WorkcenterUsage
dexpr float WorkcenterUsage = sum(w in jobDuration)(presenceOf(altWcJob[w])*w.workCenterID1.usage);
// Objective function
minimize (0.1*DistanceFromSRD + 0.2*DistanceFromDeadline + 10*SumofEarlyJobs + 200*SumofLateJobs + 50*SumofNonEditJobsMoved + 0.01*WorkcenterUsage); //- noJobschedule;//(Add penalty)
subject to {
// for alternative options present for job and machine
forall(j in jobs)
CT1: alternative(nJobs[j], all ( w in jobDuration: j.jobID == w.jobID.jobID)altWcJob[w]);
// no overlap constraint: Every workcenter can run only one job at a time
forall(w in workCenters)
{
CT2: noOverlap(workcenter[w]);
}
//Start testing of job immediately after assembly
forall(j in jobs:j.isAssembly == 1, j1 in jobs: j1.jobID == j.jobID + 1000)
{
CT3_5: startOf(nJobs[j1]) == endOf(nJobs[j]);
}
//Fixed Lock Jobs(Hard Constraint)
forall(j in jobs: j.Lock == 1 && j.isAssembly == 1)
CT3: startOf(nJobs[j]) == j.CurrentSolution;
//EndTime of Job Less than 30 + deadline
forall(j in jobs: j.isAssembly == 0)
CT31: endOf(nJobs[j]) <= j.Deadline + 30;
}
execute writeresults
{
for(var w in jobDuration)
{
if (altWcJob[w].end > 0)
{
workCenterJobAssignment.add(w.jobID.jobID,w.workCenterID1.WorkcenterID, w.workCenterID2.WorkcenterID,w.workCenterID3.WorkcenterID,altWcJob[w].start,altWcJob[w].end);
writeln (w.jobID.jobID+" "+w.workCenterID1.WorkcenterID+" "+w.workCenterID2.WorkcenterID+" "+w.workCenterID3.WorkcenterID+" "+altWcJob[w].start+" "+altWcJob[w].end)
}
}
}
#CPOptimizer#DecisionOptimization