Disaggregating a Set with Python

Hi everyone.
I’m trying to create some sets with Python, but I lack any knowledge about Python and am running into some problems (right now, Python says mapping_com_rest would not be defined - but I’m sure there are more errors. I also noticed that some code of mine already worked with “$onembeddedCode Python”, but with my dynamic sets, I need to run Python in execution time).
I’ve tried to simplify my problem and would be very happy if somebody could take a look at it.

Here is what I’d like to do:
I have sectors i (beef, pork, dairy and vegetables) that include certain commodities com. They are mapped via mapping_com_sec.
Some commodities com_disagg are supposed to appear in a disaggregated form. For this I want to create a new set tl, in which the disaggregated sectors com_disagg appear, as well as the “rest” of the sectors.

So in the end, I would like to have a set tl that looks something like
beef1, beef3, beef4, rest_beef, pork2, pork3, rest_pork, dairy1, dairy2, dairy3, rest_dairy

and a mapping tl2com like
beef1.beef1
beef3.beef3
beef4.beef4
rest_beef.(beef2, beef5)
pork2.pork2
pork3.pork3
rest_pork.(pork1, pork4, pork5)
dairy1.dairy1
dairy2.dairy2
dairy3.dairy3
rest_dairy.()


This is my code:

Set sec   /beef, pork, dairy, vegetables/
    sec_disagg(sec)
    com /beef1*beef5, pork1*pork5, dairy1*dairy3, vegetables1*vegetables3/
    com_disagg(com) /beef1, beef3, beef4, pork2, pork3, dairy1, dairy2, dairy3/
    mapping_com_sec(sec,com) /beef.(beef1*beef5)
           pork.(pork1*pork5)
           dairy.(dairy1*dairy3)
           vegetables.(vegetables1*vegetables3)/
    tl(*)
    tl2com(*,com)

    sec_disagg(sec)
    mapping_com_rest(sec,com);

sec_disagg(sec) = Yes$sum(com_disagg, 1$mapping_com_sec(sec,com_disagg));

mapping_com_rest(sec_disagg,com) = Yes$mapping_com_sec(sec_disagg,com);
mapping_com_rest(sec_disagg,com_disagg) = No;

Display mapping_com_rest, sec_disagg;



embeddedCode Python:

tl = []
for com_disagg in gams.get("com_disagg"):
  tl.append(com_disagg)
for sec_disagg in gams.get("sec_disagg"):
  tl.append("rest_" + sec_disagg)
gams.set("tl",tl)

tl2com = []
for com_disagg in gams.get("com_disagg"):
  tl2com.append((com_disagg,com_disagg))
for sec_disagg in gams.get("sec_disagg"):
  for com in gams.get("com"):
    if mapping_com_rest in gams.get("mapping_com_rest"):
      tl2com.append(("rest_" + sec_disagg,com))
gams.set("tl2com",tl2com)


endEmbeddedCode tl tl2com

Display tl, tl2com;


Many thanks in advance!

Independent of the Python problem you can’t do what you like to do at execution time. GAMS needs to know all labels at compile time. You create labels rest_xyz at execution time but they will never make it into GAMS. So you need to do all this at compile time. Here is a solution by calling a sub-GAMS script at compile time that executes what you want and create the sets with put files. These you include at compile time in your outer program:

Set sec   /beef, pork, dairy, vegetables/
    sec_disagg(sec)
    com /beef1*beef5, pork1*pork5, dairy1*dairy3, vegetables1*vegetables3/
    com_disagg(com) /beef1, beef3, beef4, pork2, pork3, dairy1, dairy2, dairy3/
    mapping_com_sec(sec,com) /beef.(beef1*beef5)
           pork.(pork1*pork5)
           dairy.(dairy1*dairy3)
           vegetables.(vegetables1*vegetables3)/
    sec_disagg(sec)
    mapping_com_rest(sec,com);

$gdxOut data
$unload
$gdxOut
$onEchoV > create_t.gms
$call gdxdump data.gdx > data.gms
$include data.gms

sec_disagg(sec) = Yes$sum(com_disagg, 1$mapping_com_sec(sec,com_disagg));

mapping_com_rest(sec_disagg,com) = Yes$mapping_com_sec(sec_disagg,com);
mapping_com_rest(sec_disagg,com_disagg) = No;


file tl / tl.txt /; put tl;
loop(sec_disagg, put sec_disagg.tl:0 /);
loop(sec_disagg, put 'rest_' sec_disagg.tl:0 /);

file tl2com / tl2com.txt /; put tl2com;
loop(mapping_com_sec(sec_disagg,com), put 'rest_' sec_disagg.tl:0 '.' com.tl:0 /);
$offEcho
$call.checkErrorLevel gams create_t.gms lo=2 gdx=create_t

$gdxLoad create_t sec_disagg mapping_com_rest

set tl1(*) /
$include tl.txt
/
tl2com(*,com) /
$include tl2com.txt
/;

Display mapping_com_rest, sec_disagg;
display tl1, tl2com;

-Michael

Hi Bussiek,

thank you for taking the time o look into my problem. Your code didn’t give the sets that I desired, but I think you have a good point that I need to execute everything at compile time. So I’ve been sitting over this for quite a while since your answer - I couldn’t make the Python code work entirely, but I have strongly changed my code. If I’d be able to import my two-dimensional set mapping_com_sec and create the list sec_disagg in GAMS, I think something like this might work:

Set sec   /beef, pork, dairy, vegetables/
    com /beef1*beef5, pork1*pork5, dairy1*dairy3, vegetables1*vegetables3/
    com_disagg(com) /beef1, beef3, beef4, pork2, pork3, dairy1, dairy2, dairy3/
    mapping_com_sec(sec,com) /beef.(beef1*beef5)
           pork.(pork1*pork5)
           dairy.(dairy1*dairy3)
           vegetables.(vegetables1*vegetables3)/
    tl
    tl2com(tl<,com)

$onEmbeddedCode Python:

sec = list(gams.get("sec"))
com = list(gams.get("com"))
com_disagg(com) = list(gams.get("com_disagg"))
mapping_com_sec(sec,com) = gams.get('mapping_com_sec')

sec_disagg = []
for sec in gams.get("sec"):
  if sec in mapping_com_sec(sec,com_disagg):
    sec_disagg.append("sec")

tl2com = []
for com_disagg in gams.get("com_disagg"):
  tl2com.append((com_disagg,com_disagg))
for sec_disagg in sec_disagg:
  for com in gams.get("com"):
    if com not in gams.get("com_disagg"):
      if mapping_com_sec:
        tl2com.append(("rest_" + sec_disagg,com))
gams.set("tl2com",tl2com)

$offEmbeddedCode tl2com


Display tl2com;