定盤計画の基礎

rcpsp41.py


001
002
003
004
005
006
007
008
009
010
011
012
013
014
015

#rcpsp41.py
from optseq import *
import math
#====データセット
#i:[名前,期間,[l,b]]
data={
1:["BLK1",1,[2,2]],
2:["BLK2",1,[1,2]],
3:["BLK3",1,[2,1]]
}    
#=====アクティビティ
prob=Model()
act={}
for i in data.keys():
    act[i]=prob.addActivity(data[i][0])

016
017
018
019
020
021


022
023

#====資源制約  
res={}
for i in range(0,4):
    for j in range(0,4):  
        if i==1 and j==2 :
            res[i,j]=prob.addResource(\
                "place[{0:02d}_{1:02d}]".format(i,j),\
                capacity={(0,"inf"):0})            
        else:     
            res[i,j]=prob.addResource(\
                "place[{0:02d}_{1:02d}]".format(i,j),\
                capacity={(0,"inf"):1})  

024
025
026
027
028
029
030
031


032
033
034
035

#-----
mode={}  
L=5; B=5; L1=0; L2=4; B1=0; B2=4;
for i in data:
    l=data[i][2][0]; b=data[i][2][1];
    for j in range(L1,L2-l+1):
        for k in range(B1,B2-b+1):         
             mode[i,j,k]=Mode(\
                 "mode[{0:02d}]_[{1:02d}_{2:02d}][{3:02d}_{4:02d}]"\
                 .format(i,j,k,l,b),duration=data[i][1])
             for s in range(0,l):
                 for t in range(0,b):
                     mode[i,j,k].addResource(res[j+s,k+t],1)         
             act[i].addModes(mode[i,j,k])    

036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054

#====問題の規模:      
A=[]
for a in prob.act: A.append(a.name) 
print("アクティビティ:",A)  
N=len(prob.act)
print("アクティビティ総数(含待機数):",N)  
M=[]
for a in prob.act: M.append(len(a.modes)) 
print("モード数:",M)  
print("平均モード数:",round(sum(M)/N))
P=math.ceil(math.log10(round(sum(M)/N)**N))
print("問題の規模: 10**",P)       
#====求解
prob.Params.Makespan=True
prob.Params.TimeLimit=1
prob.Params.OutputFlag=True
prob.optimize()
prob.write("rcpsp41.txt")
prob.writeExcel("rcpsp41.csv")

【rcpsp41.csv】

補遺3

optseq.pyの変更

startとcompletionを表示するには、次のようにoptseq.pyの3か所を変更します。


001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102

%optseq.py
 :
    def writeExcel(self, filename="optseq_chart.csv", scale=1):
        """
        Output the gantt's chart as a csv file for printing using Excel.

            - Argument:
                - filename: Output file name. Default="optseq_chart.csv."

            - Example usage:

            >>> model.writeExcel("sample.csv")

        """
        f = open(filename, "w")
        horizon = 0
        actList = []
        for a in self.activities:
            actList.append(a)
            act = self.activities[a]
            horizon = max(act.completion, horizon)
        # print("planning horizon=",horizon)
        if scale <= 0:
            print("optseq write scale error")
            exit(0)
        original_horizon = horizon
        horizon = int(horizon/scale)+1
        actList.sort()
        title = " activity ,   mode,".center(20)+" duration,"
        width = len(str(horizon))  # period width =largest index of time
        for t in range(horizon):
            num = str(t+1)
            title += num.rjust(width)+","
        f.write(title+"\n")
        for a in actList:  # sorted order
            act = self.activities[a]  # act: activity object
            actstring = act.name.center(10)[:10]+","
            if len(act.modes) >= 2:
                actstring += str(act.selected.name).center(10)+","
                actstring += str(self.modes[act.selected.name].duration).center(10)+","
            else:
                actstring += str(act.modes[0].name).center(10)[:10]+","
                actstring += str(act.modes[0].duration).center(10)+","
            execute = [0 for t in range(horizon)]
            for (s, c) in act.execute:
                para = act.execute[s, c]
                for t in range(s, c):
                    t2 = int(t/scale)
                    execute[t2] = int(para)
            for t in range(horizon):
                if execute[t] >= 2:
                    actstring += "*"+str(execute[t]).rjust(width-1)+","
                elif execute[t] == 1:
                    actstring += ""+"="*(width)+","
                elif t >= int(act.start/scale) and t < int(act.completion/scale):
                    actstring += ""+"."*(width)+","
                else:
                    actstring += ""+" "*width+","
            f.write(actstring+"\n")
        resList = []
        for r in self.resources:
            resList.append(r)
        resList.sort()

        for r in resList:
            res = self.resources[r]
            if len(res.terms) == 0:  # output residual and capacity
                rstring = res.name.center(30)+", , ,"
                cap = [0 for t in range(horizon)]
                residual = [0 for t in range(horizon)]
                for (s, c) in res.residual:
                    amount = res.residual[(s, c)]
                    if c == "inf":
                        c = horizon
                    s = min(s, original_horizon)
                    c = min(c, original_horizon)
                    s2 = int(s/scale)
                    c2 = int(c/scale)
                    for t in range(s2, c2):
                        residual[t] += amount

                for (s, c) in res.capacity:
                    amount = res.capacity[(s, c)]
                    if c == "inf":
                        c = horizon
                    s = min(s, original_horizon)
                    c = min(c, original_horizon)
                    s2 = int(s/scale)
                    c2 = int(c/scale)
                    for t in range(s2, c2):
                        cap[t] += amount

                for t in range(horizon):
                    # num=str(cap[t]-residual[t])
                    rstring += str(residual[t]) + ","
                f.write(rstring+"\n")

                # rstring= str(" ").center(30)+", , ,"
                #
                # for t in range(horizon):
                #    num=str(cap[t])
                #    rstring+=""+num.rjust(width) +","
                # f.write(rstring+"\n")
        f.close()

その1


029
029

#       title = " activity ,   mode,".center(20)+" duration,"
        title=" activity,"+" mode,"+" start,"+" duration,"+" completion,"        

その2


037
038
039
040
041
042
043
037
038
039
040
040a
040b
041
042
043
043a
043b

#           actstring = act.name.center(10)[:10]+","
#           if len(act.modes) >= 2:
#               actstring += str(act.selected).center(10)+","
#               actstring += str(self.modes[act.selected].duration).center(10)+","
#           else:
#               actstring += str(act.modes[0].name).center(10)[:10]+","
#               actstring += str(act.modes[0].duration).center(10)+","
            actstring = act.name + ","
            if len(act.modes)>=2:
                actstring+=str(act.selected)+","
                actstring+=str(act.start)+","
                actstring+=str(self.modes[act.selected].duration)+","
                actstring+=str(act.completion)+","                
            else:
                actstring += str(act.modes[0].name) + ","
                actstring += str(act.start) + ","
                actstring += str(act.modes[0].duration)+","  
                actstring+=str(act.completion)+","                  

その3


068
068

#               rstring = res.name.center(30)+", , ,"
                rstring = ", , , ,"+res.name+","                

仮想アクティビティ

rcpsp34.py
cace 1


001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043

#rcpsp34.py
from optseq import *
#====データセット 
#i:[名前,期間,後続,資源]
data={
 1:["A01",5,[2],1],
 2:["A02",1,[0],0]
}
date10=10 #納期日
#====アクティビティ
prob=Model()
act={}
for i in data:
    act[i]=prob.addActivity(name=data[i][0])    
#====先行制約
for i in data.keys():
    for j in data[i][2]:
        if j>0: 
            prob.addTemporal(act[i],act[j],tempType="CS",delay=0)            
            prob.addTemporal(act[j],act[i],tempType="SC",delay=0) #重要             
#----A02固定
i==2
prob.addTemporal("source",act[i],tempType="CC",delay= date10)   
prob.addTemporal(act[i],"source",tempType="CC",delay=-date10)  
#====資源制約  
res=prob.addResource("place",capacity={(0,"inf"):1}) 
mode={}  
for i in data:
    mode[i]=Mode("M{0:02d}".format(i),duration=data[i][1])
    mode[i].addResource(res,requirement=data[i][3])
    if i==1:
        mode[i].addBreak(0,'inf')           #Case1
       #mode[i].addBreak(0,'inf',maxtime=2) #Case2
       #mode[i].addBreak(0,0)               #Case3
        mode[i].addResource(res,1,'break')
    act[i].addModes(mode[i])
#====求解
prob.Params.Makespan=True
prob.Params.TimeLimit=1
#prob.Params.OutputFlag=True
prob.optimize()
prob.write("rcpsp34.txt")
prob.writeExcel("rcpsp34.csv")

これを実行すると次を得ます。
【rcpsp34.txt】


001
002
003
004
005
006
007
008
009
010

  activity    mode   duration  1 2 3 4 5 6 7 8 910
------------------------------------------------------------
   A01       M01        5     ==========........  
   A02       M02        1                       ==
------------------------------------------------------------
   resource usage/capacity     
------------------------------------------------------------
            place              1 1 1 1 1 1 1 1 1 0
                               1 1 1 1 1 1 1 1 1 1
------------------------------------------------------------

cace 2


032
033
034
035

       #mode[i].addBreak(0,'inf')           #Case1
        mode[i].addBreak(0,'inf',maxtime=2) #Case2
       #mode[i].addBreak(0,0)               #Case3
        mode[i].addResource(res,1,'break') 

これを実行すると次を得ます。
【rcpsp34.txt】


001
002
003
004
005
006
007
008
009
010

  activity    mode   duration  1 2 3 4 5 6 7 8 910
------------------------------------------------------------
   A01       M01        5     ========....==....  
   A02       M02        1                       ==
------------------------------------------------------------
   resource usage/capacity     
------------------------------------------------------------
            place              1 1 1 1 1 1 1 1 1 0
                               1 1 1 1 1 1 1 1 1 1
------------------------------------------------------------

cace 3


032
033
034
035

       #mode[i].addBreak(0,'inf')           #Case1
       #mode[i].addBreak(0,'inf',maxtime=2) #Case2
        mode[i].addBreak(0,0)               #Case3
        mode[i].addResource(res,1,'break')

これを実行すると次を得ます。
【rcpsp34.txt】


001
002
003
004
005
006
007
008
009
010

  activity    mode   duration  1 2 3 4 5 6 7 8 910
------------------------------------------------------------
   A01       M01        5             ==========  
   A02       M02        1                       ==
------------------------------------------------------------
   resource usage/capacity     
------------------------------------------------------------
            place              0 0 0 0 1 1 1 1 1 0
                               1 1 1 1 1 1 1 1 1 1
------------------------------------------------------------

rcpsp35.py


001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058

#rcpsp35.py
from optseq import *
import math
#====データセット 
#i:[名前、後続、時数]
data={
 1:["搬入", [2],1],
 2:["作業1",[3],10],
 3:["作業2",[4],20],
 4:["搬出", [0],1],
}
#====アクティビティ
prob=Model()
act={}
for i in data:
    act[i]=prob.addActivity(name=data[i][0])
#====先行制約
for i in data:
    for j in data[i][1]:
        if j>0: prob.addTemporal(act[i],act[j])  
#----搬入日・搬出日の固定
prob.addTemporal("source",act[1],tempType="CC",delay= 1)   
prob.addTemporal(act[1],"source",tempType="CC",delay=-1)   
# prob.addTemporal("source",act[4],tempType="CC",delay= 10)   
# prob.addTemporal(act[4],"source",tempType="CC",delay=-10)      
#====資源制約  
res=prob.addResource("worker",capacity={(0,"inf"):5})
mode={}  
for i in data:
  if i in [2,3]:
    period=math.ceil(data[i][2]/8) #1人で何日かかるか
    mode[i]=Mode("M{0:02d}_{1:02d}".format(i,period),duration=period)
    mode[i].addResource(res,requirement=1)
    mode[i].addParallel(start=1,finish=period,maxparallel=2)
    act[i].addModes(mode[i])
  else:
    mode[i]=Mode("M{0:02d}".format(i),duration=1)
    act[i].addModes(mode[i])
#----場所取り
d_act={}
d_act=prob.addActivity("場所取り")
place=prob.addResource("place",capacity={(0,"inf"):1})
d_mode={}      
d_mode=Mode("M01_")
d_mode.addBreak(0,'inf')
d_mode.addResource(place,1,"break")
d_act.addModes(d_mode)  
prob.addTemporal(act[1],d_act,tempType="CS")
prob.addTemporal(d_act,act[1],tempType="SC") 
prob.addTemporal(d_act,act[4],tempType="CS")
prob.addTemporal(act[4],d_act,tempType="SC")
#====求解
prob.Params.Makespan=True
prob.Params.TimeLimit=1
#prob.Params.OutputFlag=True
prob.optimize()
prob.write("rcpsp35.txt")
prob.writeExcel("rcpsp35.csv")

場所取りを行う仮想アクティビティdactは、前後の搬入作業act[1]、搬出作業act[4]と「隙間なく」連結されています。これは不等式

C(act[1])+0\le S(dact)\ \Leftrightarrow\ C(act[1])\le S(dact)
S(dact)-0\le C(act[1])\ \Leftrightarrow\ S(dact)\le C(act[1])

C(dact)+0\le S(act[4])\ \Leftrightarrow\ C(dact)\le S(act[4])
S(act[4])-0\le C(dact)\ \Leftrightarrow\ S(act[4])\le C(dact)

が成り立つ、すなわち

C(act[1])=S(dact)
C(dact)=S(act[4])

を要求しているからです。

これを実行すると次を得ます。
【rcpsp35.txt】


001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016

  activity    mode   duration 12345
------------------------------------
   作業1     M02_02     2     *2   
   作業2     M03_03     3     *2= 
   場所取り   M01_       0      ... 
   搬入       M01        1     =    
   搬出       M04        1         =
------------------------------------
   resource usage/capacity     
------------------------------------
            place             01110
                              11111
------------------------------------
            worker            02210
                              55555
------------------------------------

いま、次のように24行と25行のコメントを外し、搬出作業日を固定します。


021
022
023
024
025

#----搬入日・搬出日の固定
prob.addTemporal("source",act[1],tempType="CC",delay= 1)   
prob.addTemporal(act[1],"source",tempType="CC",delay=-1)   
prob.addTemporal("source",act[4],tempType="CC",delay= 10)   
prob.addTemporal(act[4],"source",tempType="CC",delay=-10)    

これを実行すると次を得ます。
【rcpsp35.txt】


001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016

  activity    mode   duration  1 2 3 4 5 6 7 8 910
------------------------------------------------------------
   作業1      M02_02     2      *2                
   作業2      M03_03     3        *2==            
   場所取り    M01_       0     ................  
   搬入        M01        1   ==                  
   搬出        M04        1                     ==
------------------------------------------------------------
   resource usage/capacity     
------------------------------------------------------------
            place              0 1 1 1 1 1 1 1 1 0
                               1 1 1 1 1 1 1 1 1 1
------------------------------------------------------------
            worker             0 2 2 1 0 0 0 0 0 0
                               5 5 5 5 5 5 5 5 5 5
------------------------------------------------------------

小作業の並列化

rcpsp33.py




026
027
028
029
030
031
032

#rcpsp32.py 
 :
#====資源制約  
res=prob.addResource("Resource",capacity={(0,"inf"):10}) 
mode={}  
for i in data:
    mode[i]=Mode("M{0:02d}_{1:02d}".format(i,data[i][3]),duration=data[i][1])                    
    mode[i].addResource(res,requirement=data[i][3])
    act[i].addModes(mode[i])

この部分を次のように書き換えます。行030の前で、1人で何日かかるかをperiodとして計算し、1人でperiod期間仕事をするようにスケジューリングを行って、そのあとで並列化を行っています。




026
027
028
029
030b
030
031
031a
032

#rcpsp33.py 
 :
#====資源制約  
res=prob.addResource("Resource",capacity={(0,"inf"):10}) 
mode={}  
for i in data:
    period=data[i][1]*data[i][3] #1人で何日かかるか
    mode[i]=Mode("M{0:02d}_{1:02d}".format(i,period),duration=period)
    mode[i].addResource(res,requirement=1)
    mode[i].addParallel(start=1,finish=period,maxparallel=10)
    act[i].addModes(mode[i])  

これを実行すると次を得ます。
【rcpsp33.txt】


001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018

  activity    mode   duration  1 2 3 4 5 6 7 8 910111213
---------------------------------------------------------------------
   A01      M01_09      9     *6*3                      
   A02      M02_04      4     *4                        
   A03      M03_06      6       *6                      
   A04      M04_16      16        *10*6                  
   A05      M05_20      20                  *4*10*6      
   A06      M06_20      20          *4*10*6              
   A07      M07_20      20              *4*10*6          
   A08      M08_04      4                         *4    
   A09      M09_10      10                      *4*6    
   A10      M10_15      15                          *10*5
---------------------------------------------------------------------
   resource usage/capacity     
---------------------------------------------------------------------
           Resource           10 910101010101010101010 5
                              10101010101010101010101010
---------------------------------------------------------------------

とても簡単に究極の平準化が行えると言えます。

RCPSP求解プログラムの枠組み

rcpsp30.py
次はプッシュ型とプル型の結果を比べる簡単なプログラムですが、データセットの定義、アクティビティの定義(プッシュ型)、モードの付与(作業期間の指定)、求解からなる構成が見てとれます。


001
002

003
004
005
006
007
008

009
010
011
012
013

#rcpsp30.py
from optseq import *

data=["A01",5,10] #[名前,期間,納期]
prob=Model()
act=prob.addActivity(name=data[0])
#act=prob.addActivity(name=data[0],duedate=data[2]-1,backward=True)
mode=Mode("Mode",duration=data[1])
act.addModes(mode)

prob.Params.Makespan=True
prob.Params.TimeLimit=1
#prob.Params.OutputFlag=True
prob.optimize()
prob.write("rcpsp30.txt")

これを実行すると次を得ます。
【rcpsp30.txt】


001
002
003
004
005
006

  activity    mode   duration 12345
------------------------------------
   A01       Mode       5     =====
------------------------------------
   resource usage/capacity     
------------------------------------

納期日を10日とするプル型の結果を得るときは、


005
006

#act=prob.addActivity(name=data[0])
act=prob.addActivity(name=data[0],duedate=data[2]-1,backward=True) 

とします。納期日の前日まで作業を終わりたいので、duedateは納期日の前日としています。これを実行すると次を得ます。
【rcpsp30.txt】


001
002
003
004
005
00

  activity    mode   duration 123456789
------------------------------------------------
   A01       Mode       5         =====
------------------------------------------------
   resource usage/capacity     
------------------------------------------------

rcpsp31.py
次はRCPSP法のプログラムの基本的な枠組みを示すプログラムで、アクティビティに先行制約が、モードに資源制約が組み込まれています。


001
002
003
004
005
006
007
008
009

#rcpsp31.py
from optseq import *
#====データセット 
#i:[名前,期間,後続,資源]
data={
 1:["A01",5,[2],1],
 2:["A02",1,[0],0]
}
date10=10 #納期日

データセットはPythonの辞書データとしています。keyでアクティビティを識別し、名前、作業期間、後続作業のリスト(後続がないときは0)、必要とするリソースなどからなるリストを対応させています。


010
011
012
013
014
015

#====アクティビティ
prob=Model()
act={}
for i in data:
   #act[i]=prob.addActivity(name=data[i][0])   
    act[i]=prob.addActivity(name=data[i][0],duedate=date10,backward=True)  

データセットで定義された2つのアクティビティ(製作作業と納品作業)を定義しています。プル型のduedateは、2つとも納品作業のものを用いています。次に定義する先行関係があるので、納品作業は必ず製品作業が終わってから行うことになります。


010
011
012
013
014
015
016
017

#====先行制約
for i in data:
    for j in data[i][2]:
        if j>0: prob.addTemporal(act[i],act[j]) 
#----A02固定
i==2
prob.addTemporal("source",act[i],tempType="CC",delay= date10)   
prob.addTemporal(act[i],"source",tempType="CC",delay=-date10)     

プッシュ型計画を行うことも想定して、納期日は固定しています。これは2つの不等式

C("source")+data10\le C("A02")\ \Leftrightarrow\ 10\le C("A02")
C("A02")-data10\le C("source")\ \Leftrightarrow\ C("A02")\le 10

が成り立つ(C(\cdot)は完了日)、すなわちC("A02")=10を要求しているからです。


018
019
020
021
022
023
024

#====資源制約  
res=prob.addResource("worker",capacity={(0,"inf"):1}) 
mode={}  
for i in data:
    mode[i]=Mode("M{0:02d}".format(i),duration=data[i][1])
    mode[i].addResource(res,requirement=data[i][3])
    act[i].addModes(mode[i])   

リソースのキャパシティは1です。各アクティビティについてモードは1個です。各モードは名前で区別します。


025
026
027
028
029
030

#====求解
prob.Params.Makespan=True
prob.Params.TimeLimit=1
#prob.Params.OutputFlag=True
prob.optimize()
prob.write("rcpsp31.txt") 

これを実行すると次の結果を得ます。
【rcpsp31.txt】


001
002
003
004
005
006
007
008
009
010

  activity    mode   duration  1 2 3 4 5 6 7 8 910
------------------------------------------------------------
   A01       M01        5             ==========  
   A02       M02        1                       ==
------------------------------------------------------------
   resource usage/capacity     
------------------------------------------------------------
            worker             0 0 0 0 1 1 1 1 1 0
                               1 1 1 1 1 1 1 1 1 1
------------------------------------------------------------

上のプログラムは、プッシュ型かプル型か、評価関数はメイクススパンか納期遅れ総和かで、4通りの実行ができます。それぞれの目的関数の値を確かめると参考になるかと思います。

rcpsp32.py
例題1をRCPSP法で解くプログラムを次に示します。


001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016

#rcpsp32.py
from optseq import *
#====データセット
#i:[名前,期間,後続,資源]
data={
 1:["A01",3,[4,6],3],
 2:["A02",4,[5,7],1],
 3:["A03",6,[8]  ,1],
 4:["A04",4,[5,7],4],
 5:["A05",5,[8]  ,4],
 6:["A06",5,[9]  ,4],
 7:["A07",4,[10] ,5],
 8:["A08",2,[10] ,2],
 9:["A09",2,[10] ,5],
10:["A10",5,[0]  ,3],
}

10個のアクティビティについてデータが定義されています。


017
018
019
020
021

#====アクティビティ
prob=Model()
act={}
for i in data:
    act[i]=prob.addActivity(data[i][0])

プッシュ型の計画が想定されています。


022
023
024
025

#====先行制約
for i in data:
    for j in data[i][2]:
        if j>0: prob.addTemporal(act[i],act[j])  

先行関係は13個あります。


026
027
028
029
030

031
032

#====資源制約  
res=prob.addResource("Resource",capacity={(0,"inf"):10}) 
mode={}  
for i in data:
    mode[i]=Mode("M{0:02d}_{1:02d}".format(i,data[i][3]),
                                    duration=data[i][1])
    mode[i].addResource(res,requirement=data[i][3])
    act[i].addModes(mode[i])

リソースのキャパシティは10です。各アクティビティについてモードは1個です。


033
034
035
036
037
038
039

#====求解
prob.Params.Makespan=True
prob.Params.TimeLimit=1
#prob.Params.OutputFlag=True
prob.optimize()
prob.write("rcpsp32.txt")
prob.writeExcel("rcpsp32.csv")

これを実行すると次の結果を得ます。
【rcpsp32.txt】


001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018

  activity    mode   duration  1 2 3 4 5 6 7 8 910111213141516171819
-----------------------------------------------------------------------------
   A01      M01_03      3     ======                                
   A02      M02_01      4     ========                              
   A03      M03_01      6     ============                          
   A04      M04_04      4           ========                        
   A05      M05_04      5                   ==========              
   A06      M06_04      5           ==========                      
   A07      M07_05      4                         ========          
   A08      M08_02      2                             ====          
   A09      M09_05      2                     ====                  
   A10      M10_03      5                                 ==========
-----------------------------------------------------------------------------
   resource usage/capacity     
-----------------------------------------------------------------------------
           Resource            5 5 510 9 9 8 8 9 9 9 9 7 7 3 3 3 3 3
                              10101010101010101010101010101010101010
-----------------------------------------------------------------------------

補遺2

[1] 例題1(図1.1)へのカレンダー挿入(図2.19の作成)

工程’sファイル「例題1.kzd」をコピーして、「ex1.kdz」とします。その編集期間とカレンダー(0:デフォールト)を次のように変更します。

カレンダーを上書きすると、次が得られます。

ここで先行関係を整えるために、「日程調整」

を行うと、次が得られます。

[2] 図2.12へのカレンダー挿入(図2.20の作成)
工程’sファイル「例題1a (開始日を10日シフト).kzd」をコピーして、「ex2.kdz」とします。その編集期間とカレンダー(0:デフォールト)をex1.kdzと同じように変更します。カレンダーを上書きすると、次が得られます。

ここで先行関係を整えるために、「日程調整」を行うと、次が得られます。

[3] 図2.13へのカレンダー挿入(図2.21の作成)
工程’sファイル「例題1b (2つの計画を並べる).kzd」をコピーして、「ex3.kdz」とします。そのカレンダー(0:デフォールト)と同じように変更します。カレンダーを上書きすると、次が得られます。

いま、コピーされた計画の先頭の3つのアクティビティについて、日程調整をかけても移動させない指定を行います(バーの右上にマークが表示されます)。

ここで先行関係を整えるために、「日程調整」を行うと、次が得られます。

カレンダーと通し日

一般に、計画は稼働日を用いて行い、あとで非稼働日を挿入します。

[1]カレンダー

例題1に、休みなし、休みあり、土曜出勤も3種類のカレンダーを付与します。

カレンダー編集

[2]通し日

工程’sでは稼働日にシリアル番号を振った「通し日」の設定できます。

通し日の設定

第1章の例題1を通し日を用いて表すと次のようになります。

データセットの作成支援

[1] エクスポート・インポート機能

「工程’s」にはオプションで、エクスポート・インポート機能があって、工程データを12種類のTSV(Tab Separated Values)ファイルに出力できます。

例題1.kzdに対してエクスポートをかけます。

この操作を行うと、指定したフォルダに次のように13種類のtsvファイルが作成されます。

ここで重要なのが赤枠で囲んだ3つのファイルで、koutes.tsv、constraint.tsv、requirement.tsvはそれぞれ日程計画、先行制約、資源制約に関する情報を含んでいます。

tsvファイルを、EXCELにdrag-dropすれば、簡単に中身をチェックできます。いま適当に編集してtsvファイルに戻したとします。これらを次のようにインポートすれば、例題1.kzdが変更されます。

[2] テンプレート

一つの計画を再利用したい場面はいろいろ考えられます。「工程’s」では通常の.kzdファイルのほかに、テンプレートファイル.kzt出力して、これを再利用することができます。

[3] 工程’sファイルの複製

計画期間が重なる2つの計画に対して山積み行って、リソースの不足がないかをチェックしたい場合があります。この目的のために次図を考えます。

まず、(a)は元ファイル(たとえば「例題1.kzd」)とします。同じ計画を一定期間おいて繰り返す場合は、(b)のように全部のアクティビティを選択してコピーすればOKです。これは「工程’s」の編集操作の範囲内で行うもので、おのずとアクティビティの数が限られます。

次に、(c)は(a)のすべてのアクティビティを一括して扱うために、最上位のグループ(黄色のバー)を設置した様子を表しています。具体的には次図となります。

これを一定期間ずらした計画との山積みの重複の様子を見るには、最上位のグループをコピーして、(f)のようにその下にペーストします。これは同一ファイル内の操作になります。

一方、(c)からテンプレートファイルを(d)のように作成し、これを一定期間ずらして読み込んで、もう一つの計画(e)を作成しておきます。一方のファイルの最上位のグループをコピーして、(g)のように他方のファイルの中でペーストします。これは2つの異なるファイル間の操作になります。ここで、2つのファイルは全く別の計画(別のテンプレートから作成された計画)でもよいことに注意してください。

「工程’s」の基礎

第1章の例題1を「工程’s」を用いてstep-by-stepで作成してみます。

[1]準備
編集期間設定

現在日の設定

プロジェクト名称の設定

カレンダー編集(略)
通し日の設定(略)

[2]リソースの定義
資源マスター編集

[3]アクティビティの定義
WBSの設定
– グループの挿入

– グループプロパティの設定

– グループバーの表示・非表示(略)
– ツリービューの表示・非表示

アクティビティバーの設置

– IDの設定
– 名称の設定
– 必要資源の設定
– シンボル変更(略)

[4]先行制約の定義
先行制約の定義
山積み

– 山積みグラフの表示
– 供給資源量表示・非表示
– 供給資源量設定

補遺1

造船工程計画

造船工程計画とは、ブロック製作のために2種類のリソース(場所、作業員)のタイムライン化(時間軸に並べること)と言えます。2つの計画AB,ACは比較的簡単に求まりますが、この2つを共通の日程で作成するのは簡単なことではありません。「二兎を追う者は一兎をも得ず」という諺がありますが、造船工程計画の困難性もそのような状況かもしれません。