| (!*******************************************************
   Mosel Example Problems 
   ======================
   file coco3.mos
   ``````````````
   Coco Problem, mid-term model.
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, r2001, rev. Jun. 2023
*******************************************************!)
model "Coco3"
 uses "mmxprs"
 parameters
  DATAFILE = "coco2.dat"
 end-parameters
 
 declarations                         
  NPROD, NFACT, NRAW, NT: integer
 end-declarations                      
 initializations from DATAFILE
  NPROD NFACT NRAW NT
 end-initializations
 declarations
  PRODS = 1..NPROD                      ! Range of products (p)
  FACT = 1..NFACT                       !          factories (f)
  RAW = 1..NRAW                         !          raw materials (r)
  PERIODS = 1..NT                       !          time periods (t)
  REV: array(PRODS,PERIODS) of real     ! Unit selling price of products
  CMAKE: array(PRODS,FACT) of real      ! Unit cost to make product p 
                                        ! at factory f
  CBUY: array(RAW,PERIODS) of real      ! Unit cost to buy raw materials
  REQ: array(PRODS,RAW) of real         ! Requirement by unit of product p 
                                        ! for raw material r
  MXSELL: array(PRODS,PERIODS) of real  ! Max. amount of p that can be sold
  MXMAKE: array(FACT) of real           ! Max. amount factory f can make 
                                        ! over all products
  IPSTOCK: array(PRODS,FACT) of real    ! Initial product stock levels 
  IRSTOCK: array(RAW,FACT) of real      ! Initial raw material stock levels
  CPSTOCK: real                         ! Unit cost to store any product p
  CRSTOCK: real                         ! Unit cost to store any raw mat. r
  MXRSTOCK: real                        ! Raw material storage capacity
  make: array(PRODS,FACT,PERIODS) of mpvar ! Amount of products made at factories
  sell: array(PRODS,FACT,PERIODS) of mpvar ! Amount of product sold from factories
  buy: array(RAW,FACT,PERIODS) of mpvar    ! Amount of raw material bought
  pstock: array(PRODS,FACT,1..NT+1) of mpvar ! Product stock levels at start 
                                             ! of period t
  rstock: array(RAW,FACT,1..NT+1) of mpvar   ! Raw material stock levels  
                                             ! at start of period t 
 end-declarations
 initializations from DATAFILE
  REV CMAKE CBUY REQ MXSELL MXMAKE
  IPSTOCK IRSTOCK MXRSTOCK CPSTOCK CRSTOCK
 end-initializations
 
! Product stock balance
 forall(p in PRODS,f in FACT,t in PERIODS)
  PBal(p,f,t):= pstock(p,f,t+1) = pstock(p,f,t) + make(p,f,t) - sell(p,f,t)
! Raw material stock balance
 forall(r in RAW,f in FACT,t in PERIODS) 
  RBal(r,f,t):= rstock(r,f,t+1) = 
   rstock(r,f,t) + buy(r,f,t) - sum(p in PRODS) REQ(p,r)*make(p,f,t)
! Capacity limit at factories
 forall(f in FACT,t in PERIODS) 
  MxMake(f,t):= sum(p in PRODS) make(p,f,t) <= MXMAKE(f)
! Limit on the amount of prod. p to be sold
 forall(p in PRODS,t in PERIODS) sum(f in FACT) sell(p,f,t) <= MXSELL(p,t)
! Raw material stock limit
 forall(f in FACT,t in 2..NT+1) 
  MxRStock(f,t):= sum(r in RAW) rstock(r,f,t) <= MXRSTOCK
! Initial product and raw material stock levels
 forall(p in PRODS,f in FACT) pstock(p,f,1) = IPSTOCK(p,f)
 forall(r in RAW,f in FACT) rstock(r,f,1) = IRSTOCK(r,f)
! Objective: maximize total profit
 Profit:= 
  sum(p in PRODS,f in FACT,t in PERIODS) REV(p,t) * sell(p,f,t) -   ! revenue
  sum(p in PRODS,f in FACT,t in PERIODS) CMAKE(p,f) * make(p,f,t) - ! prod. cost
  sum(r in RAW,f in FACT,t in PERIODS) CBUY(r,t) * buy(r,f,t) -     ! raw mat. 
  sum(p in PRODS,f in FACT,t in 2..NT+1) CPSTOCK * pstock(p,f,t) -  ! p storage 
  sum(r in RAW,f in FACT,t in 2..NT+1) CRSTOCK * rstock(r,f,t)      ! r storage
   
! Solve the problem
 maximize(Profit)
! Solution printing
 writeln("Total profit: ", getobjval)
 writeln("Finished products:")
 forall(f in FACT) do
  writeln("Factory ", f, ":") 
  forall(p in PRODS) do
   write("  ", p, ": ")
   forall(t in PERIODS) write(strfmt(getsol(make(p,f,t)),6,1), "(", 
                           strfmt(getsol(pstock(p,f,t+1)),5,1), ")")
   writeln
  end-do 
 end-do 
 writeln("Raw material:")
 forall(f in FACT) do
  writeln("Factory ", f, ":") 
  forall(r in RAW) do
   write("  ", r, ": ")
   forall(t in PERIODS) write(strfmt(getsol(buy(r,f,t)),6,1), "(", 
                           strfmt(getsol(rstock(r,f,t+1)),5,1), ")")
   writeln
  end-do 
 end-do 
 writeln("Sales:")
 forall(f in FACT) do
  writeln("Factory ", f, ":") 
  forall(p in PRODS) do
   write("  ", p, ": ")
   forall(t in PERIODS) write(strfmt(getsol(sell(p,f,t)),4))
   writeln
  end-do 
 end-do 
end-model
 | 
| (!*******************************************************
   Mosel Example Problems 
   ======================
   file cocoMs.mos
   ```````````````
   Coco Problem, main model.
   -- Standard version --
   *** ATTENTION: This model will return an error if ***
   *** no more than one Xpress licence is available. ***
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, 2005, rev. Jun. 2023
*******************************************************!)
model "Coco3 Main"
 uses "mmxprs","mmjobs","mmsystem"
 parameters
  DATAFILE = "coco2.dat"
  ALG = 1                             ! 0: stop phase with 1st failed subpb.
                                      ! 1: stop when all subprob.s fail
 end-parameters
 forward procedure process_sub_result
 forward procedure solve_main(phase:integer)
 forward procedure process_main_result
 forward function true_solution:real
 forward function calc_solution:real
 forward procedure print_solution
 
 declarations                         
  PHASE_0=2                           ! Event codes sent to submodels
  PHASE_1=3
  PHASE_2=4
  PHASE_3=5
  EVENT_SOLVED=6                      ! Event codes sent by submodels
  EVENT_FAILED=7
  EVENT_READY=8
  NPROD, NFACT, NRAW, NT: integer
 end-declarations                      
 initializations from DATAFILE
  NPROD NFACT NRAW NT
 end-initializations
 declarations
  PRODS = 1..NPROD                    ! Range of products (p)
  FACT = 1..NFACT                     !          factories (f)
  RAW = 1..NRAW                       !          raw materials (r)
  PERIODS = 1..NT                     !          time periods (t)
  REV: array(PRODS,PERIODS) of real   ! Unit selling price of products
  CMAKE: array(PRODS,FACT) of real    ! Unit cost to make product p 
                                      ! at factory f
  CBUY: array(RAW,PERIODS) of real    ! Unit cost to buy raw materials
  MXSELL: array(PRODS,PERIODS) of real  ! Max. amount of p that can be sold
  CPSTOCK: real                       ! Unit cost to store any product p
  CRSTOCK: real                       ! Unit cost to store any raw mat. r
  submod: array(FACT) of Model        ! One subproblem per factory
  nIter: integer                      ! Iteration counter
  nPROP: array(FACT) of integer       ! Counters of proposals from subprob.s 
  
  Prop_make: array(PRODS,FACT,PERIODS,range) of real ! Amount of products made
  Prop_sell: array(PRODS,FACT,PERIODS,range) of real ! Amount of product sold
  Prop_buy: array(RAW,FACT,PERIODS,range) of real    ! Amount of raw mat. bought
  Prop_pstock: array(PRODS,FACT,1..NT+1,range) of real ! Product stock levels 
  Prop_rstock: array(RAW,FACT,1..NT+1,range) of real   ! Raw mat. stock levels  
  Prop_cost: array(FACT,range) of real  ! Cost/profit of each proposal
  Price_convex: array(FACT) of real     ! Dual price on convexity constraints
  Price_sell: array(PRODS,PERIODS) of real ! Dual price on sales limits
  Sol_make: array(PRODS,FACT,PERIODS) of real ! Solution value (products made)
  Sol_sell: array(PRODS,FACT,PERIODS) of real ! Solution value (product sold)
  Sol_buy: array(RAW,FACT,PERIODS) of real    ! Solution value (raw mat. bought)
  Sol_pstock: array(PRODS,FACT,1..NT+1) of real ! Sol. value (prod. stock) 
  Sol_rstock: array(RAW,FACT,1..NT+1) of real   ! Sol. value (raw mat. stock)
 end-declarations
 initializations from DATAFILE
  CMAKE REV CBUY MXSELL CPSTOCK CRSTOCK
 end-initializations
 initializations to "bin:shmem:pricedata"  ! Initial price data for submodels
  Price_sell 
 end-initializations
!**** Main problem ****
 declarations
  excessS: mpvar                      ! Violation of sales/buying limits
  weight: dynamic array(FACT,range) of mpvar  ! Weights for propasals
  MxSell: array(PRODS,PERIODS) of linctr ! Sales limit constraints
  Convex: array(FACT) of linctr       ! Convexity constraints
 end-declarations 
!**** Submodels ****
 declarations
  Stopped: set of integer
 end-declarations 
 res:= compile("g","cocoSubFs.mos")   ! Compile the submodel file
 forall(f in FACT) do                 ! Load & run one submodel per product 
  Price_convex(f):= 1
  load(submod(f), "cocoSubFs.bim")
  submod(f).uid:= f
  setworkdir(submod(f), ".")
  run(submod(f), "Factory=" + f + ",DATAFILE=" + DATAFILE)
  wait                                ! Wait for child model to be ready
  ev:=getnextevent
  if ev.class=EVENT_END then
   writeln("*** Cannot start all necessary models - aborting ***")
   exit(1)
  end-if
 end-do
!**** Phase 0: Crash ****
 nIter:=1; finished:=false
 writeln("\nPHASE 0 -- Iteration ", nIter); fflush
 forall(f in FACT)                    ! Start solving all submodels (Phase 1)
  send(submod(f), PHASE_0, 0)
 forall(f in FACT) do
  wait                                ! Wait for child (termination) events
  ev:= getnextevent
  if getclass(ev)=EVENT_SOLVED then
   process_sub_result                 ! Add new proposal to main problem
  elif getclass(ev)=EVENT_FAILED then
   finished:= true
  else
   writeln("*** Unexpected event '", ev, "' received from of submodel ***")
   fflush
   exit(1)
  end-if
 end-do
 if finished then
  writeln("Problem is infeasible")
  exit(1)
 end-if
 solve_main(1)                        ! Solve the updated Ph. 1 main problem
 process_main_result                  ! Store initial pricing data for submodels
 
!**** Phase 1: proposal generation (feasibility) ****
 repeat
  noimprove:= 0
  nIter+=1
  writeln("\nPHASE 1 -- Iteration ", nIter); fflush
  forall(f in FACT)                   ! Start solving all submodels (Phase 1)
   send(submod(f), PHASE_1, Price_convex(f))
  forall(f in FACT) do
   wait                               ! Wait for child (termination) events
   ev:= getnextevent
   if getclass(ev)=EVENT_SOLVED then
    process_sub_result                ! Add new proposal to main problem
   elif getclass(ev)=EVENT_FAILED then
    noimprove += 1
   else
    writeln("*** Unexpected event '", ev, "' received from of submodel ***")
    fflush
    exit(1)
   end-if
  end-do
  if noimprove = NFACT then 
   writeln("Problem is infeasible")
   exit(2)
  end-if
  if ALG=0 and noimprove > 0 then 
   writeln("No improvement by some subproblem(s)")
   break
  end-if
  solve_main(1)                       ! Solve the updated Ph. 1 main problem
  if getobjval>0.00001 then
   process_main_result                ! Store new pricing data for submodels
  end-if
 until getobjval <= 0.00001
 
!**** Phase 2: proposal generation (optimization) ****
 writeln("\n**** PHASE 2 ****")
 finished:=false
 repeat
  solve_main(2)                       ! Solve Phase 2 main problem
  process_main_result                 ! Store new pricing data for submodels
  nIter+=1
  writeln("\nPHASE 2 -- Iteration ", nIter); fflush
  forall(f in FACT)                   ! Start solving all submodels (Phase 2)
   send(submod(f), PHASE_2, Price_convex(f))
  forall(f in FACT) do
   wait                               ! Wait for child (termination) events
   ev:= getnextevent
   if getclass(ev)=EVENT_SOLVED then
    process_sub_result                ! Add new proposal to main problem
   elif getclass(ev)=EVENT_FAILED then        
    if ALG=0 then
     finished:=true                   ! 1st submodel w/o prop. stops phase 2
    else
     Stopped += {ev.fromuid}          ! Stop phase 2 only when no submodel
                                      ! generates a new proposal 
    end-if 
   else
    writeln("*** Unexpected event '", ev, "' received from of submodel ***")
    fflush
    exit(1)
   end-if
  end-do
  if getsize(Stopped) = NFACT then finished:= true; end-if
  
 until finished
!**** Phase 3: solution to the original problem ****
 writeln("\n**** PHASE 3 ****")
 forall(f in FACT) do
  send(submod(f), PHASE_3, 0)         ! Stop all submodels
  wait
  dropnextevent
 end-do
! writeln("Total Profit=", calc_solution)
 writeln("Total Profit=", true_solution)
 print_solution
 
!**** Cleaning up temporary files
 fdelete("mempipe:sol") 
 fdelete("cocoSubFs.bim")
 fdelete("shmem:pricedata")
 
!-----------------------------------------------------------
! Process the proposal generated by a subproblem
 procedure process_sub_result
  declarations
   f: integer                         ! Factory index
                                      ! Solution values of the proposal:
   sol_make: array(PRODS,PERIODS) of real   ! Amount of products made
   sol_sell: array(PRODS,PERIODS) of real   ! Amount of product sold
   sol_buy: array(RAW,PERIODS) of real      ! Amount of raw mat. bought
   sol_pstock: array(PRODS,1..NT+1) of real ! Product stock levels 
   sol_rstock: array(RAW,1..NT+1) of real   ! Raw mat. stock levels  
   pc: real                           ! Cost of the proposal
  end-declarations
 ! Read proposal data from memory
  initializations from "bin:mempipe:sol"
   f as "Factory"
   sol_make sol_sell sol_buy sol_pstock sol_rstock
   pc as "Prop_cost"
  end-initializations
 ! Add the new proposal to the main problem
  nPROP(f)+=1
  create(weight(f,nPROP(f)))
  forall(p in PRODS,t in PERIODS) do
   Prop_make(p,f,t,nPROP(f)):= sol_make(p,t)
   Prop_sell(p,f,t,nPROP(f)):= sol_sell(p,t)  
  end-do   
  forall(r in RAW,t in PERIODS) Prop_buy(r,f,t,nPROP(f)):= sol_buy(r,t)
  forall(p in PRODS,t in 1..NT+1) Prop_pstock(p,f,t,nPROP(f)):= sol_pstock(p,t)
  forall(r in RAW,t in 1..NT+1) Prop_rstock(r,f,t,nPROP(f)):= sol_rstock(r,t)
  Prop_cost(f,nPROP(f)):= pc
  writeln("Sol. for factory ", f, ":\n  make:   ", sol_make, "\n  sell:   ",
           sol_sell, "\n  buy:    ", sol_buy, "\n  pstock: ", sol_pstock, 
	   "\n  rstock: ", sol_rstock)
 end-procedure
!-----------------------------------------------------------
! (Re)solve the main problem
 procedure solve_main(phase: integer)
  forall(f in FACT)
   Convex(f):= sum (k in 1..nPROP(f)) weight(f,k) = 1
  if phase=1 then
   forall(p in PRODS,t in PERIODS)
    MxSell(p,t):=
     sum(f in FACT,k in 1..nPROP(f)) Prop_sell(p,f,t,k)*weight(f,k) -
      excessS <= MXSELL(p,t)
   minimize(excessS)
  else
   forall(p in PRODS,t in PERIODS)
    MxSell(p,t):=
     sum(f in FACT,k in 1..nPROP(f)) Prop_sell(p,f,t,k)*weight(f,k) <=
      MXSELL(p,t)
   maximize(sum(f in FACT, k in 1..nPROP(f)) Prop_cost(f,k) * weight(f,k))
  end-if
  writeln("Main problem objective: ", getobjval)
  write("  Weights:")
  forall(f in FACT,k in 1..nPROP(f)) write(" ", getsol(weight(f,k)))
  writeln
 end-procedure
!-----------------------------------------------------------
! Update pricing data for subproblems
 procedure process_main_result
  forall(p in PRODS,t in PERIODS) Price_sell(p,t):=getdual(MxSell(p,t))
  forall(f in FACT) Price_convex(f):=getdual(Convex(f))
  initializations to "bin:shmem:pricedata"
   Price_sell
  end-initializations
 end-procedure
!-----------------------------------------------------------
! Calculate solution to the original problem
 function true_solution: real 
  forall(p in PRODS,f in FACT,t in PERIODS) do
   Sol_sell(p,f,t):= 
    sum(k in 1..nPROP(f)) Prop_sell(p,f,t,k) * getsol(weight(f,k))
   Sol_make(p,f,t):= 
    sum(k in 1..nPROP(f)) Prop_make(p,f,t,k) * getsol(weight(f,k))
  end-do
  forall(r in RAW,f in FACT,t in PERIODS) Sol_buy(r,f,t):= 
    sum(k in 1..nPROP(f)) Prop_buy(r,f,t,k) * getsol(weight(f,k))
  forall(p in PRODS,f in FACT,t in 1..NT+1) Sol_pstock(p,f,t):=
   sum(k in 1..nPROP(f)) Prop_pstock(p,f,t,k) * getsol(weight(f,k)) 
  forall(r in RAW,f in FACT,t in 1..NT+1) Sol_rstock(r,f,t):=
   sum(k in 1..nPROP(f)) Prop_rstock(r,f,t,k) * getsol(weight(f,k)) 
  returned:=
   sum(p in PRODS,f in FACT,t in PERIODS) REV(p,t) * Sol_sell(p,f,t) -
   sum(p in PRODS,f in FACT,t in PERIODS) CMAKE(p,f) * Sol_make(p,f,t) -
   sum(r in RAW,f in FACT,t in PERIODS) CBUY(r,t) * Sol_buy(r,f,t) - 
   sum(p in PRODS,f in FACT,t in 2..NT+1) CPSTOCK * Sol_pstock(p,f,t) - 
   sum(r in RAW,f in FACT,t in 2..NT+1) CRSTOCK * Sol_rstock(r,f,t) 
 end-function
! Solve the original problem
 function calc_solution: real 
  declarations
   make: array(PRODS,FACT,PERIODS) of mpvar   ! Amount of products made
   sell: array(PRODS,FACT,PERIODS) of mpvar   ! Amount of product sold
   buy: array(RAW,FACT,PERIODS) of mpvar      ! Amount of raw material bought
   pstock: array(PRODS,FACT,1..NT+1) of mpvar ! Product stock levels at start 
                                              ! of period t
   rstock: array(RAW,FACT,1..NT+1) of mpvar   ! Raw material stock levels  
                                              ! at start of period t 
  end-declarations
 
  forall(p in PRODS,f in FACT,t in PERIODS) do
   sell(p,f,t) = sum(k in 1..nPROP(f)) Prop_sell(p,f,t,k) * weight(f,k)
   make(p,f,t) = sum(k in 1..nPROP(f)) Prop_make(p,f,t,k) * weight(f,k)
  end-do
  forall(r in RAW,f in FACT,t in PERIODS) 
   buy(r,f,t) = sum(k in 1..nPROP(f)) Prop_buy(r,f,t,k) * weight(f,k)
  forall(p in PRODS,f in FACT,t in 1..NT+1) 
   pstock(p,f,t) = sum(k in 1..nPROP(f)) Prop_pstock(p,f,t,k) * weight(f,k)
  forall(r in RAW,f in FACT,t in 1..NT+1) 
   rstock(r,f,t) = sum(k in 1..nPROP(f)) Prop_rstock(r,f,t,k) * weight(f,k)
  MaxProfit:= 
  sum(p in PRODS,f in FACT,t in PERIODS) REV(p,t) * sell(p,f,t) -   ! revenue
  sum(p in PRODS,f in FACT,t in PERIODS) CMAKE(p,f) * make(p,f,t) - ! prod. cost
  sum(r in RAW,f in FACT,t in PERIODS) CBUY(r,t) * buy(r,f,t) -     ! raw mat. 
  sum(p in PRODS,f in FACT,t in 2..NT+1) CPSTOCK * pstock(p,f,t) -  ! p storage 
  sum(r in RAW,f in FACT,t in 2..NT+1) CRSTOCK * rstock(r,f,t)      ! r storage 
  
  maximize(MaxProfit)
  
  returned:= getobjval
  forall(p in PRODS,f in FACT,t in PERIODS) do
   Sol_sell(p,f,t):= getsol(sell(p,f,t))
   Sol_make(p,f,t):= getsol(make(p,f,t))
  end-do
  forall(r in RAW,f in FACT,t in PERIODS) Sol_buy(r,f,t):= getsol(buy(r,f,t))
  forall(p in PRODS,f in FACT,t in 1..NT+1) 
   Sol_pstock(p,f,t):= getsol(pstock(p,f,t)) 
  forall(r in RAW,f in FACT,t in 1..NT+1) 
   Sol_rstock(r,f,t):= getsol(rstock(r,f,t)) 
 end-function
 
!----------------------------------------------------------- 
 procedure print_solution
 
  writeln("Finished products:")
  forall(f in FACT) do
   writeln("Factory ", f, ":") 
   forall(p in PRODS) do
    write("  ", p, ":    ")
    forall(t in PERIODS) write(strfmt(Sol_make(p,f,t),6,1), "(", 
                            strfmt(Sol_pstock(p,f,t+1),5,1), ")")
    writeln
   end-do 
  end-do 
  writeln("Raw material:")
  forall(f in FACT) do
   writeln("Factory ", f, ":") 
   forall(r in RAW) do
    write("  ", r, ": ")
    forall(t in PERIODS) write(strfmt(Sol_buy(r,f,t),6,1), "(", 
                            strfmt(Sol_rstock(r,f,t+1),5,1), ")")
    writeln
   end-do 
  end-do 
  writeln("Sales:")
  forall(f in FACT) do
   writeln("Factory ", f, ":") 
   forall(p in PRODS) do
    write("  ", p, ": ")
    forall(t in PERIODS) write(strfmt(Sol_sell(p,f,t),4))
    writeln
   end-do 
  end-do 
  writeln("\nComputation time: ", gettime)
 end-procedure
end-model
 | 
| (!*******************************************************
   Mosel Example Problems 
   ======================
   file cocoSubFs.mos
   ``````````````````
   Coco Problem, single factory subproblem.
   -- Standard version --
   *** Not intended to be run standalone - run from cocoMs.mos ***
   (c) 2008 Fair Isaac Corporation
       author: S. Heipcke, 2005, rev. Jun. 2023
*******************************************************!)
model "Coco Subproblem (factory based decomp.)"
 uses "mmxprs", "mmjobs"
 parameters
  Factory = 0
  TOL = 0.00001
  DATAFILE = "coco3.dat"
 end-parameters
 forward procedure process_solution
 
 declarations                         
  PHASE_0=2                             ! Event codes sent to submodels
  PHASE_1=3
  PHASE_2=4
  PHASE_3=5
  EVENT_SOLVED=6                        ! Event codes sent by submodels
  EVENT_FAILED=7
  EVENT_READY=8
  NPROD, NFACT, NRAW, NT: integer
 end-declarations                      
 send(EVENT_READY,0)                    ! Model is ready (= running)
 initializations from DATAFILE
  NPROD NFACT NRAW NT
 end-initializations
 declarations
  PRODS = 1..NPROD                      ! Range of products (p)
  FACT = 1..NFACT                       !          factories (f)
  RAW = 1..NRAW                         !          raw materials (r)
  PERIODS = 1..NT                       !          time periods (t)
  REV: array(PRODS,PERIODS) of real     ! Unit selling price of products
  CMAKE: array(PRODS,FACT) of real      ! Unit cost to make product p 
                                        ! at factory f
  CBUY: array(RAW,PERIODS) of real      ! Unit cost to buy raw materials
  REQ: array(PRODS,RAW) of real         ! Requirement by unit of product p 
                                        ! for raw material r
  MXSELL: array(PRODS,PERIODS) of real  ! Max. amount of p that can be sold
  MXMAKE: array(FACT) of real           ! Max. amount factory f can make 
                                        ! over all products
  IPSTOCK: array(PRODS,FACT) of real    ! Initial product stock levels 
  IRSTOCK: array(RAW,FACT) of real      ! Initial raw material stock levels
  CPSTOCK: real                         ! Unit cost to store any product p
  CRSTOCK: real                         ! Unit cost to store any raw mat. r
  MXRSTOCK: real                        ! Raw material storage capacity
  
  make: array(PRODS,PERIODS) of mpvar   ! Amount of products made at factory
  sell: array(PRODS,PERIODS) of mpvar   ! Amount of product sold from factory
  buy: array(RAW,PERIODS) of mpvar      ! Amount of raw material bought
  pstock: array(PRODS,1..NT+1) of mpvar ! Product stock levels at start 
                                        ! of period t
  rstock: array(RAW,1..NT+1) of mpvar   ! Raw material stock levels  
                                        ! at start of period t 
  sol_make: array(PRODS,PERIODS) of real   ! Amount of products made
  sol_sell: array(PRODS,PERIODS) of real   ! Amount of product sold
  sol_buy: array(RAW,PERIODS) of real      ! Amount of raw mat. bought
  sol_pstock: array(PRODS,1..NT+1) of real ! Product stock levels 
  sol_rstock: array(RAW,1..NT+1) of real   ! Raw mat. stock levels
   
  Profit: linctr                           ! Profit of proposal
  Price_sell: array(PRODS,PERIODS) of real ! Dual price on sales limits
 end-declarations
 initializations from DATAFILE
  REV CMAKE CBUY REQ MXSELL MXMAKE
  IPSTOCK IRSTOCK MXRSTOCK CPSTOCK CRSTOCK
 end-initializations
 
! Product stock balance
 forall(p in PRODS,t in PERIODS)
  PBal(p,t):= pstock(p,t+1) = pstock(p,t) + make(p,t) - sell(p,t)
! Raw material stock balance
 forall(r in RAW,t in PERIODS) 
  RBal(r,t):= rstock(r,t+1) = 
   rstock(r,t) + buy(r,t) - sum(p in PRODS) REQ(p,r)*make(p,t)
! Capacity limit at factories
 forall(t in PERIODS)
  MxMake(t):= sum(p in PRODS) make(p,t) <= MXMAKE(Factory)
! Limit on the amount of prod. p to be sold
 forall(p in PRODS,t in PERIODS) sell(p,t) <= MXSELL(p,t)
 
! Raw material stock limit
 forall(t in 2..NT+1) 
  MxRStock(t):= sum(r in RAW) rstock(r,t) <= MXRSTOCK
! Initial product and raw material stock levels
 forall(p in PRODS) pstock(p,1) = IPSTOCK(p,Factory)
 forall(r in RAW) rstock(r,1) = IRSTOCK(r,Factory)
 
! Total profit
 Profit:= 
  sum(p in PRODS,t in PERIODS) REV(p,t) * sell(p,t) -          ! revenue
  sum(p in PRODS,t in PERIODS) CMAKE(p,Factory) * make(p,t) -  ! prod. cost
  sum(r in RAW,t in PERIODS) CBUY(r,t) * buy(r,t) -            ! raw mat. 
  sum(p in PRODS,t in 2..NT+1) CPSTOCK * pstock(p,t) -         ! p storage 
  sum(r in RAW,t in 2..NT+1) CRSTOCK * rstock(r,t)             ! r storage   
! (Re)solve this model until it is stopped by event "PHASE_3"
 repeat
  wait
  ev:= getnextevent
  Phase:= getclass(ev)
  if Phase=PHASE_3 then               ! Stop the execution of this model
   break
  end-if
  Price_convex:= getvalue(ev)         ! Get new pricing data
  if Phase<>PHASE_0 then
   initializations from "bin:shmem:pricedata"
    Price_sell
   end-initializations
  end-if
  
 ! (Re)solve this model
  if Phase=PHASE_0 then
   maximize(Profit)
  elif Phase=PHASE_1 then
   maximize(sum(p in PRODS,t in PERIODS) Price_sell(p,t)*sell(p,t) + Price_convex)
  else        ! PHASE 2
   maximize(
    Profit - sum(p in PRODS,t in PERIODS) Price_sell(p,t)*sell(p,t) - 
     Price_convex)
  end-if
  writeln("Factory ", Factory, " - Obj: ", getobjval, 
          " Profit: ", getsol(Profit), " Price_sell: ",
          getsol(sum(p in PRODS,t in PERIODS) Price_sell(p,t)*sell(p,t) ), 
          " Price_convex: ", Price_convex)
  fflush
  
  if getobjval > TOL then             ! Solution found: send values to parent
   process_solution
  else   ! getobjval <= TOL           ! Problem is infeasible (Phase 0/1) or
   send(EVENT_FAILED,0)               ! no improved solution found (Phase 2)
  end-if   
 until false
!-----------------------------------------------------------
! Process solution data
 procedure process_solution
  forall(p in PRODS,t in PERIODS) do
   sol_make(p,t):= getsol(make(p,t))
   sol_sell(p,t):= getsol(sell(p,t))  
  end-do   
  forall(r in RAW,t in PERIODS) sol_buy(r,t):= getsol(buy(r,t))
  forall(p in PRODS,t in 1..NT+1) sol_pstock(p,t):= getsol(pstock(p,t))
  forall(r in RAW,t in 1..NT+1) sol_rstock(r,t):= getsol(rstock(r,t))
  Prop_cost:= getsol(Profit)
  send(EVENT_SOLVED,0)
  initializations to "bin:mempipe:sol"
   Factory
   sol_make sol_sell sol_buy sol_pstock sol_rstock
   Prop_cost
  end-initializations
 end-procedure 
end-model
 | 
| (!*******************************************************
   Mosel Example Problems 
   ======================
   file cocoMn.mos
   ```````````````
   Coco Problem, main model.
   -- Using notifications issued by mempipe --
   *** ATTENTION: This model will return an error if ***
   *** no more than one Xpress licence is available. ***
   (c) 2021 Fair Isaac Corporation
       author: S. Heipcke, Jan. 2021, rev. Jun. 2023
*******************************************************!)
model "Coco3 Main"
 uses "mmxprs","mmjobs","mmsystem"
 parameters
  DATAFILE = "coco2.dat"
  ALG = 1                             ! 0: stop phase with 1st failed subpb.
                                      ! 1: stop when all subprob.s fail
 end-parameters
 forward procedure process_sub_result
 forward procedure solve_main(phase:integer)
 forward procedure process_main_result
 forward function true_solution:real
 forward function calc_solution:real
 forward procedure print_solution
 
 declarations                         
  PHASE_0=2                           ! Event codes sent to submodels
  PHASE_1=3
  PHASE_2=4
  PHASE_3=5
  EVENT_SOLVED=6                      ! Event codes sent by submodels
  EVENT_FAILED=7
  EVENT_READY=8
  NPROD, NFACT, NRAW, NT: integer
 end-declarations                      
 initializations from DATAFILE
  NPROD NFACT NRAW NT
 end-initializations
 declarations
  PRODS = 1..NPROD                    ! Range of products (p)
  FACT = 1..NFACT                     !          factories (f)
  RAW = 1..NRAW                       !          raw materials (r)
  PERIODS = 1..NT                     !          time periods (t)
  REV: array(PRODS,PERIODS) of real   ! Unit selling price of products
  CMAKE: array(PRODS,FACT) of real    ! Unit cost to make product p 
                                      ! at factory f
  CBUY: array(RAW,PERIODS) of real    ! Unit cost to buy raw materials
  MXSELL: array(PRODS,PERIODS) of real  ! Max. amount of p that can be sold
  CPSTOCK: real                       ! Unit cost to store any product p
  CRSTOCK: real                       ! Unit cost to store any raw mat. r
  submod: array(FACT) of Model        ! One subproblem per factory
  nIter: integer                      ! Iteration counter
  nPROP: array(FACT) of integer       ! Counters of proposals from subprob.s 
  
  Prop_make: array(PRODS,FACT,PERIODS,range) of real ! Amount of products made
  Prop_sell: array(PRODS,FACT,PERIODS,range) of real ! Amount of product sold
  Prop_buy: array(RAW,FACT,PERIODS,range) of real    ! Amount of raw mat. bought
  Prop_pstock: array(PRODS,FACT,1..NT+1,range) of real ! Product stock levels 
  Prop_rstock: array(RAW,FACT,1..NT+1,range) of real   ! Raw mat. stock levels  
  Prop_cost: array(FACT,range) of real  ! Cost/profit of each proposal
  Price_convex: array(FACT) of real     ! Dual price on convexity constraints
  Price_sell: array(PRODS,PERIODS) of real ! Dual price on sales limits
  Sol_make: array(PRODS,FACT,PERIODS) of real ! Solution value (products made)
  Sol_sell: array(PRODS,FACT,PERIODS) of real ! Solution value (product sold)
  Sol_buy: array(RAW,FACT,PERIODS) of real    ! Solution value (raw mat. bought)
  Sol_pstock: array(PRODS,FACT,1..NT+1) of real ! Sol. value (prod. stock) 
  Sol_rstock: array(RAW,FACT,1..NT+1) of real   ! Sol. value (raw mat. stock)
 end-declarations
 initializations from DATAFILE
  CMAKE REV CBUY MXSELL CPSTOCK CRSTOCK
 end-initializations
 initializations to "bin:shmem:pricedata"  ! Initial price data for submodels
  Price_sell 
 end-initializations
!**** Main problem ****
 declarations
  excessS: mpvar                      ! Violation of sales/buying limits
  weight: dynamic array(FACT,range) of mpvar  ! Weights for propasals
  MxSell: array(PRODS,PERIODS) of linctr ! Sales limit constraints
  Convex: array(FACT) of linctr       ! Convexity constraints
 end-declarations 
!**** Submodels ****
 declarations
  Stopped: set of integer
 end-declarations 
 res:= compile("g","cocoSubFn.mos")   ! Compile the submodel file
 forall(f in FACT) do                 ! Load & run one submodel per product 
  Price_convex(f):= 1
  load(submod(f), "cocoSubFn.bim")
  submod(f).uid:= f
  setworkdir(submod(f), ".")
  run(submod(f), "Factory=" + f + ",DATAFILE=" + DATAFILE)
  wait                                ! Wait for child model to be ready
  ev:=getnextevent
  if ev.class=EVENT_END then
   writeln("*** Cannot start all necessary models - aborting ***")
   exit(1)
  end-if
 end-do
!**** Phase 0: Crash ****
 nIter:=1; finished:=false
 writeln("\nPHASE 0 -- Iteration ", nIter); fflush
 forall(f in FACT)                    ! Start solving all submodels (Phase 1)
  send(submod(f), PHASE_0, 0)
 asproc(pipenotify("sol", EVENT_SOLVED, 0))
 forall(f in FACT) do
  wait                                ! Wait for child (termination) events
  ev:= getnextevent
  writeln(ev)
  if getclass(ev)=EVENT_SOLVED then
   process_sub_result                 ! Add new proposal to main problem
   asproc(pipenotify("sol", EVENT_SOLVED, 0))
  elif getclass(ev)=EVENT_FAILED then
   finished:= true
  else
   writeln("*** Unexpected event '", ev, "' received from of submodel ***")
   fflush
   exit(1)
  end-if
 end-do
 asproc(pipenotify("sol", 0, 0))      ! Cancel mempipe monitoring on pipe 'sol'
 if finished then
  writeln("Problem is infeasible")
  exit(1)
 end-if
 solve_main(1)                        ! Solve the updated Ph. 1 main problem
 process_main_result                  ! Store initial pricing data for submodels
 
!**** Phase 1: proposal generation (feasibility) ****
 repeat
  noimprove:= 0
  nIter+=1
  writeln("\nPHASE 1 -- Iteration ", nIter); fflush
  forall(f in FACT)                   ! Start solving all submodels (Phase 1)
   send(submod(f), PHASE_1, Price_convex(f))
  asproc(pipenotify("sol", EVENT_SOLVED, 0))
  forall(f in FACT) do
   wait                               ! Wait for child (termination) events
   ev:= getnextevent
   if getclass(ev)=EVENT_SOLVED then
    process_sub_result                ! Add new proposal to main problem
    asproc(pipenotify("sol", EVENT_SOLVED, 0))
   elif getclass(ev)=EVENT_FAILED then
    noimprove += 1
   else
    writeln("*** Unexpected event '", ev, "' received from of submodel ***")
    fflush
    exit(1)
   end-if
  end-do
  asproc(pipenotify("sol", 0, 0))     ! Cancel mempipe monitoring on pipe 'sol'
  if noimprove = NFACT then 
   writeln("Problem is infeasible")
   exit(2)
  end-if
  if ALG=0 and noimprove > 0 then 
   writeln("No improvement by some subproblem(s)")
   break
  end-if
  solve_main(1)                       ! Solve the updated Ph. 1 main problem
  if getobjval>0.00001 then
   process_main_result                ! Store new pricing data for submodels
  end-if
 until getobjval <= 0.00001
 
!**** Phase 2: proposal generation (optimization) ****
 writeln("\n**** PHASE 2 ****")
 finished:=false
 repeat
  solve_main(2)                       ! Solve Phase 2 main problem
  process_main_result                 ! Store new pricing data for submodels
  nIter+=1
  writeln("\nPHASE 2 -- Iteration ", nIter); fflush
  forall(f in FACT)                   ! Start solving all submodels (Phase 2)
   send(submod(f), PHASE_2, Price_convex(f))
  asproc(pipenotify("sol", EVENT_SOLVED, 0))
  forall(f in FACT) do
   wait                               ! Wait for child (termination) events
   ev:= getnextevent
   if getclass(ev)=EVENT_SOLVED then
    process_sub_result                ! Add new proposal to main problem
    asproc(pipenotify("sol", EVENT_SOLVED, 0))
   elif getclass(ev)=EVENT_FAILED then        
    if ALG=0 then
     finished:=true                   ! 1st submodel w/o prop. stops phase 2
    else
     Stopped += {ev.fromuid}          ! Stop phase 2 only when no submodel
                                      ! generates a new proposal 
    end-if 
   else
    writeln("*** Unexpected event '", ev, "' received from of submodel ***")
    fflush
    exit(1)
   end-if
  end-do
  asproc(pipenotify("sol", 0, 0))     ! Cancel mempipe monitoring on pipe 'sol'
  if getsize(Stopped) = NFACT then finished:= true; end-if
  
 until finished
!**** Phase 3: solution to the original problem ****
 writeln("\n**** PHASE 3 ****")
 forall(f in FACT) do
  send(submod(f), PHASE_3, 0)         ! Stop all submodels
  wait
  dropnextevent
 end-do
! writeln("Total Profit=", calc_solution)
 writeln("Total Profit=", true_solution)
 print_solution
 
!**** Cleaning up temporary files
 fdelete("mempipe:sol") 
 fdelete("cocoSubFn.bim")
 fdelete("shmem:pricedata")
 
!-----------------------------------------------------------
! Process the proposal generated by a subproblem
 procedure process_sub_result
  declarations
   f: integer                         ! Factory index
                                      ! Solution values of the proposal:
   sol_make: array(PRODS,PERIODS) of real    ! Amount of products made
   sol_sell: array(PRODS,PERIODS) of real    ! Amount of product sold
   sol_buy: array(RAW,PERIODS) of real       ! Amount of raw mat. bought
   sol_pstock: array(PRODS,1..NT+1) of real  ! Product stock levels 
   sol_rstock: array(RAW,1..NT+1) of real    ! Raw mat. stock levels  
   pc: real                                  ! Cost of the proposal
  end-declarations
 ! Read proposal data from memory
  initializations from "bin:mempipe:sol"
   f as "Factory"
   sol_make sol_sell sol_buy sol_pstock sol_rstock
   pc as "Prop_cost"
  end-initializations
 ! Add the new proposal to the main problem
  nPROP(f)+=1
  create(weight(f,nPROP(f)))
  forall(p in PRODS,t in PERIODS) do
   Prop_make(p,f,t,nPROP(f)):= sol_make(p,t)
   Prop_sell(p,f,t,nPROP(f)):= sol_sell(p,t)  
  end-do   
  forall(r in RAW,t in PERIODS) Prop_buy(r,f,t,nPROP(f)):= sol_buy(r,t)
  forall(p in PRODS,t in 1..NT+1) Prop_pstock(p,f,t,nPROP(f)):= sol_pstock(p,t)
  forall(r in RAW,t in 1..NT+1) Prop_rstock(r,f,t,nPROP(f)):= sol_rstock(r,t)
  Prop_cost(f,nPROP(f)):= pc
  writeln("Sol. for factory ", f, ":\n  make:   ", sol_make, "\n  sell:   ",
           sol_sell, "\n  buy:    ", sol_buy, "\n  pstock: ", sol_pstock, 
	   "\n  rstock: ", sol_rstock)
 end-procedure
!-----------------------------------------------------------
! (Re)solve the main problem
 procedure solve_main(phase: integer)
  forall(f in FACT)
   Convex(f):= sum (k in 1..nPROP(f)) weight(f,k) = 1
  if phase=1 then
   forall(p in PRODS,t in PERIODS)
    MxSell(p,t):=
     sum(f in FACT,k in 1..nPROP(f)) Prop_sell(p,f,t,k)*weight(f,k) -
      excessS <= MXSELL(p,t)
   minimize(excessS)
  else
   forall(p in PRODS,t in PERIODS)
    MxSell(p,t):=
     sum(f in FACT,k in 1..nPROP(f)) Prop_sell(p,f,t,k)*weight(f,k) <=
      MXSELL(p,t)
   maximize(sum(f in FACT, k in 1..nPROP(f)) Prop_cost(f,k) * weight(f,k))
  end-if
  writeln("Main problem objective: ", getobjval)
  write("  Weights:")
  forall(f in FACT,k in 1..nPROP(f)) write(" ", getsol(weight(f,k)))
  writeln
 end-procedure
!-----------------------------------------------------------
! Update pricing data for subproblems
 procedure process_main_result
  forall(p in PRODS,t in PERIODS) Price_sell(p,t):=getdual(MxSell(p,t))
  forall(f in FACT) Price_convex(f):=getdual(Convex(f))
  initializations to "bin:shmem:pricedata"
   Price_sell
  end-initializations
 end-procedure
!-----------------------------------------------------------
! Calculate solution to the original problem
 function true_solution: real 
  forall(p in PRODS,f in FACT,t in PERIODS) do
   Sol_sell(p,f,t):= 
    sum(k in 1..nPROP(f)) Prop_sell(p,f,t,k) * getsol(weight(f,k))
   Sol_make(p,f,t):= 
    sum(k in 1..nPROP(f)) Prop_make(p,f,t,k) * getsol(weight(f,k))
  end-do
  forall(r in RAW,f in FACT,t in PERIODS) Sol_buy(r,f,t):= 
    sum(k in 1..nPROP(f)) Prop_buy(r,f,t,k) * getsol(weight(f,k))
  forall(p in PRODS,f in FACT,t in 1..NT+1) Sol_pstock(p,f,t):=
   sum(k in 1..nPROP(f)) Prop_pstock(p,f,t,k) * getsol(weight(f,k)) 
  forall(r in RAW,f in FACT,t in 1..NT+1) Sol_rstock(r,f,t):=
   sum(k in 1..nPROP(f)) Prop_rstock(r,f,t,k) * getsol(weight(f,k)) 
  returned:=
   sum(p in PRODS,f in FACT,t in PERIODS) REV(p,t) * Sol_sell(p,f,t) -
   sum(p in PRODS,f in FACT,t in PERIODS) CMAKE(p,f) * Sol_make(p,f,t) -
   sum(r in RAW,f in FACT,t in PERIODS) CBUY(r,t) * Sol_buy(r,f,t) - 
   sum(p in PRODS,f in FACT,t in 2..NT+1) CPSTOCK * Sol_pstock(p,f,t) - 
   sum(r in RAW,f in FACT,t in 2..NT+1) CRSTOCK * Sol_rstock(r,f,t) 
 end-function
! Solve the original problem
 function calc_solution: real 
  declarations
   make: array(PRODS,FACT,PERIODS) of mpvar   ! Amount of products made
   sell: array(PRODS,FACT,PERIODS) of mpvar   ! Amount of product sold
   buy: array(RAW,FACT,PERIODS) of mpvar      ! Amount of raw material bought
   pstock: array(PRODS,FACT,1..NT+1) of mpvar ! Product stock levels at start 
                                              ! of period t
   rstock: array(RAW,FACT,1..NT+1) of mpvar   ! Raw material stock levels  
                                              ! at start of period t 
  end-declarations
 
  forall(p in PRODS,f in FACT,t in PERIODS) do
   sell(p,f,t) = sum(k in 1..nPROP(f)) Prop_sell(p,f,t,k) * weight(f,k)
   make(p,f,t) = sum(k in 1..nPROP(f)) Prop_make(p,f,t,k) * weight(f,k)
  end-do
  forall(r in RAW,f in FACT,t in PERIODS) 
   buy(r,f,t) = sum(k in 1..nPROP(f)) Prop_buy(r,f,t,k) * weight(f,k)
  forall(p in PRODS,f in FACT,t in 1..NT+1) 
   pstock(p,f,t) = sum(k in 1..nPROP(f)) Prop_pstock(p,f,t,k) * weight(f,k)
  forall(r in RAW,f in FACT,t in 1..NT+1) 
   rstock(r,f,t) = sum(k in 1..nPROP(f)) Prop_rstock(r,f,t,k) * weight(f,k)
  MaxProfit:= 
  sum(p in PRODS,f in FACT,t in PERIODS) REV(p,t) * sell(p,f,t) -   ! revenue
  sum(p in PRODS,f in FACT,t in PERIODS) CMAKE(p,f) * make(p,f,t) - ! prod. cost
  sum(r in RAW,f in FACT,t in PERIODS) CBUY(r,t) * buy(r,f,t) -     ! raw mat. 
  sum(p in PRODS,f in FACT,t in 2..NT+1) CPSTOCK * pstock(p,f,t) -  ! p storage 
  sum(r in RAW,f in FACT,t in 2..NT+1) CRSTOCK * rstock(r,f,t)      ! r storage 
  
  maximize(MaxProfit)
  
  returned:= getobjval
  forall(p in PRODS,f in FACT,t in PERIODS) do
   Sol_sell(p,f,t):= getsol(sell(p,f,t))
   Sol_make(p,f,t):= getsol(make(p,f,t))
  end-do
  forall(r in RAW,f in FACT,t in PERIODS) Sol_buy(r,f,t):= getsol(buy(r,f,t))
  forall(p in PRODS,f in FACT,t in 1..NT+1) 
   Sol_pstock(p,f,t):= getsol(pstock(p,f,t)) 
  forall(r in RAW,f in FACT,t in 1..NT+1) 
   Sol_rstock(r,f,t):= getsol(rstock(r,f,t)) 
 end-function
 
!----------------------------------------------------------- 
 procedure print_solution
 
  writeln("Finished products:")
  forall(f in FACT) do
   writeln("Factory ", f, ":") 
   forall(p in PRODS) do
    write("  ", p, ":    ")
    forall(t in PERIODS) write(strfmt(Sol_make(p,f,t),6,1), "(", 
                            strfmt(Sol_pstock(p,f,t+1),5,1), ")")
    writeln
   end-do 
  end-do 
  writeln("Raw material:")
  forall(f in FACT) do
   writeln("Factory ", f, ":") 
   forall(r in RAW) do
    write("  ", r, ": ")
    forall(t in PERIODS) write(strfmt(Sol_buy(r,f,t),6,1), "(", 
                            strfmt(Sol_rstock(r,f,t+1),5,1), ")")
    writeln
   end-do 
  end-do 
  writeln("Sales:")
  forall(f in FACT) do
   writeln("Factory ", f, ":") 
   forall(p in PRODS) do
    write("  ", p, ": ")
    forall(t in PERIODS) write(strfmt(Sol_sell(p,f,t),4))
    writeln
   end-do 
  end-do 
  writeln("\nComputation time: ", gettime)
 end-procedure
end-model
 | 
| (!*******************************************************
   Mosel Example Problems 
   ======================
   file cocoSubFn.mos
   ``````````````````
   Coco Problem, single factory subproblem.
   -- No solution event sent by subproblems --
   *** Not intended to be run standalone - run from cocoMn.mos ***
   (c) 2021 Fair Isaac Corporation
       author: S. Heipcke, Jan. 2021, rev. Jun. 2023
*******************************************************!)
model "Coco Subproblem (factory based decomp.)"
 uses "mmxprs", "mmjobs"
 parameters
  Factory = 0
  TOL = 0.00001
  DATAFILE = "coco3.dat"
 end-parameters
 forward procedure process_solution
 
 declarations                         
  PHASE_0=2                             ! Event codes sent to submodels
  PHASE_1=3
  PHASE_2=4
  PHASE_3=5             
  EVENT_FAILED=7                        ! Event codes sent by submodels
  EVENT_READY=8
  NPROD, NFACT, NRAW, NT: integer
 end-declarations                      
 send(EVENT_READY,0)                    ! Model is ready (= running)
 initializations from DATAFILE
  NPROD NFACT NRAW NT
 end-initializations
 declarations
  PRODS = 1..NPROD                      ! Range of products (p)
  FACT = 1..NFACT                       !          factories (f)
  RAW = 1..NRAW                         !          raw materials (r)
  PERIODS = 1..NT                       !          time periods (t)
  REV: array(PRODS,PERIODS) of real     ! Unit selling price of products
  CMAKE: array(PRODS,FACT) of real      ! Unit cost to make product p 
                                        ! at factory f
  CBUY: array(RAW,PERIODS) of real      ! Unit cost to buy raw materials
  REQ: array(PRODS,RAW) of real         ! Requirement by unit of product p 
                                        ! for raw material r
  MXSELL: array(PRODS,PERIODS) of real  ! Max. amount of p that can be sold
  MXMAKE: array(FACT) of real           ! Max. amount factory f can make 
                                        ! over all products
  IPSTOCK: array(PRODS,FACT) of real    ! Initial product stock levels 
  IRSTOCK: array(RAW,FACT) of real      ! Initial raw material stock levels
  CPSTOCK: real                         ! Unit cost to store any product p
  CRSTOCK: real                         ! Unit cost to store any raw mat. r
  MXRSTOCK: real                        ! Raw material storage capacity
  
  make: array(PRODS,PERIODS) of mpvar   ! Amount of products made at factory
  sell: array(PRODS,PERIODS) of mpvar   ! Amount of product sold from factory
  buy: array(RAW,PERIODS) of mpvar      ! Amount of raw material bought
  pstock: array(PRODS,1..NT+1) of mpvar ! Product stock levels at start 
                                        ! of period t
  rstock: array(RAW,1..NT+1) of mpvar   ! Raw material stock levels  
                                        ! at start of period t 
  sol_make: array(PRODS,PERIODS) of real   ! Amount of products made
  sol_sell: array(PRODS,PERIODS) of real   ! Amount of product sold
  sol_buy: array(RAW,PERIODS) of real      ! Amount of raw mat. bought
  sol_pstock: array(PRODS,1..NT+1) of real ! Product stock levels 
  sol_rstock: array(RAW,1..NT+1) of real   ! Raw mat. stock levels
   
  Profit: linctr                           ! Profit of proposal
  Price_sell: array(PRODS,PERIODS) of real ! Dual price on sales limits
 end-declarations
 initializations from DATAFILE
  REV CMAKE CBUY REQ MXSELL MXMAKE
  IPSTOCK IRSTOCK MXRSTOCK CPSTOCK CRSTOCK
 end-initializations
 
! Product stock balance
 forall(p in PRODS,t in PERIODS)
  PBal(p,t):= pstock(p,t+1) = pstock(p,t) + make(p,t) - sell(p,t)
! Raw material stock balance
 forall(r in RAW,t in PERIODS) 
  RBal(r,t):= rstock(r,t+1) = 
   rstock(r,t) + buy(r,t) - sum(p in PRODS) REQ(p,r)*make(p,t)
! Capacity limit at factories
 forall(t in PERIODS)
  MxMake(t):= sum(p in PRODS) make(p,t) <= MXMAKE(Factory)
! Limit on the amount of prod. p to be sold
 forall(p in PRODS,t in PERIODS) sell(p,t) <= MXSELL(p,t)
 
! Raw material stock limit
 forall(t in 2..NT+1) 
  MxRStock(t):= sum(r in RAW) rstock(r,t) <= MXRSTOCK
! Initial product and raw material stock levels
 forall(p in PRODS) pstock(p,1) = IPSTOCK(p,Factory)
 forall(r in RAW) rstock(r,1) = IRSTOCK(r,Factory)
 
! Total profit
 Profit:= 
  sum(p in PRODS,t in PERIODS) REV(p,t) * sell(p,t) -          ! revenue
  sum(p in PRODS,t in PERIODS) CMAKE(p,Factory) * make(p,t) -  ! prod. cost
  sum(r in RAW,t in PERIODS) CBUY(r,t) * buy(r,t) -            ! raw mat. 
  sum(p in PRODS,t in 2..NT+1) CPSTOCK * pstock(p,t) -         ! p storage 
  sum(r in RAW,t in 2..NT+1) CRSTOCK * rstock(r,t)             ! r storage   
! (Re)solve this model until it is stopped by event "PHASE_3"
 repeat
  wait
  ev:= getnextevent
  Phase:= getclass(ev)
  if Phase=PHASE_3 then               ! Stop the execution of this model
   break
  end-if
  Price_convex:= getvalue(ev)         ! Get new pricing data
  if Phase<>PHASE_0 then
   initializations from "bin:shmem:pricedata"
    Price_sell
   end-initializations
  end-if
  
 ! (Re)solve this model
  if Phase=PHASE_0 then
   maximize(Profit)
  elif Phase=PHASE_1 then
   maximize(sum(p in PRODS,t in PERIODS) Price_sell(p,t)*sell(p,t) + Price_convex)
  else        ! PHASE 2
   maximize(
    Profit - sum(p in PRODS,t in PERIODS) Price_sell(p,t)*sell(p,t) - 
     Price_convex)
  end-if
  writeln("Factory ", Factory, " - Obj: ", getobjval, 
          " Profit: ", getsol(Profit), " Price_sell: ",
          getsol(sum(p in PRODS,t in PERIODS) Price_sell(p,t)*sell(p,t) ), 
          " Price_convex: ", Price_convex)
  fflush
  
  if getobjval > TOL then             ! Solution found: send values to parent
   process_solution
  else   ! getobjval <= TOL           ! Problem is infeasible (Phase 0/1) or
   send(EVENT_FAILED,0)               ! no improved solution found (Phase 2)
  end-if   
 until false
!-----------------------------------------------------------
! Process solution data
 procedure process_solution
  forall(p in PRODS,t in PERIODS) do
   sol_make(p,t):= getsol(make(p,t))
   sol_sell(p,t):= getsol(sell(p,t))  
  end-do   
  forall(r in RAW,t in PERIODS) sol_buy(r,t):= getsol(buy(r,t))
  forall(p in PRODS,t in 1..NT+1) sol_pstock(p,t):= getsol(pstock(p,t))
  forall(r in RAW,t in 1..NT+1) sol_rstock(r,t):= getsol(rstock(r,t))
  Prop_cost:= getsol(Profit)
  initializations to "bin:mempipe:sol"
   Factory
   sol_make sol_sell sol_buy sol_pstock sol_rstock
   Prop_cost
  end-initializations
 end-procedure 
end-model
 |