This is a static, read-only snapshot — sliders and other controls will not respond.
» Back to notebook list
·
» Run it yourself
%23%20%2F%2F%2F%20script%0A%23%20%5Btool.marimo.opengraph%5D%0A%23%20title%20%3D%20%22MoselPy%3A%20Mining%20and%20Process%22%0A%23%20description%20%3D%20%22Blending%2C%20refinery%2C%20sugar%2C%20mining%22%0A%23%20image%20%3D%20%22__marimo__%2Fthumbnail-moselpy-miningprocess.svg%22%0A%23%20%2F%2F%2F%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.25.2%22%0Aapp%20%3D%20marimo.App()%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20**FICO%26reg%3B%20Xpress%20MoselPy%20-%20Chapter%20A%3A%20Blending%20and%20process%20planning**%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20***moselpy_mining_process.py***%20-%20Mining%20and%20process%20industries%3A%20blending%2C%20refinery%2C%20sugar%20harvest%20processing%2C%20mining%20and%20electricity%20dispatch%20examples%20using%20MoselPy.%0A%0A%20%20%20%20%26copy%3B%20Copyright%202026%20Fair%20Isaac%20Corporation.%20The%20use%20of%20this%20example%20is%20subject%20to%20%5Blegal%20and%20license%20requirements%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fxpress-community%23legal-and-license-requirements).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20cell%20below%20imports%20the%20necessary%20libraries%20and%20set%20%60MODELS%60%20and%20%60DATA%60%2C%20the%20path%20variables%20pointing%20to%20the%20Mosel%20model%20files%20and%20data%20files%20used%20in%20this%20notebook.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20%23%20On%20Linux%2C%20preload%20the%20Xpress%20solver%20library%20so%20the%20Mosel%20optimizer%20module%0A%20%20%20%20%23%20(mmxprs)%20can%20resolve%20it%20at%20runtime.%0A%20%20%20%20import%20sys%2C%20os%2C%20glob%2C%20ctypes%2C%20xpresslibs%0A%20%20%20%20if%20sys.platform%20%3D%3D%20%22linux%22%3A%0A%20%20%20%20%20%20%20%20for%20_lib%20in%20glob.glob(os.path.join(xpresslibs.solver_libs_dir%2C%20%22libxprs.so*%22))%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20ctypes.CDLL(_lib%2C%20mode%3Dctypes.RTLD_GLOBAL)%0A%0A%20%20%20%20import%20moselpy%20as%20mp%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20matplotlib.patches%20as%20mpatches%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%0A%20%20%20%20_root%20%3D%20mo.notebook_dir()%20%2F%20%22public%22%20%2F%20%22moselpy%22%0A%20%20%20%20MODELS%20%3D%20str(_root%20%2F%20%22models%22%20%2F%20%22mining_process%22)%0A%20%20%20%20DATA%20%20%20%3D%20str(_root%20%2F%20%22data%22%20%2F%20%22mining_process%22)%0A%0A%20%20%20%20print(f%22MoselPy%20%7Bmp.version%7D%20%20%7C%20%20Mosel%20%7Bmp.mosel_version%7D%20%20%7C%20%20Xpress%20%7Bmp.xpress_version%7D%22)%0A%20%20%20%20mp.config.set_default_stream(mp.StreamType.OUTPUT%2C%20%22null%3A%22)%0A%20%20%20%20return%20MODELS%2C%20mp%2C%20mpatches%2C%20np%2C%20pd%2C%20plt%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Mining%20and%20process%20industries%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20This%20notebook%20covers%20six%20LP%20and%20MIP%20examples%20from%20Chapter%20A%20of%20the%20Xpress%20Applications%20Book%2C%20solved%20via%20%5BMoselPy%5D(https%3A%2F%2Fpypi.org%2Fproject%2Fmoselpy%2F).%20All%20models%20share%20a%20common%20theme%3A%20relating%20to%20typical%20problems%20in%20the%20process%20and%20mining%20industry%20-%20determining%20how%20to%20**blend%20raw%20materials**%2C%20**plan%20production%20processes**%2C%20or%20**schedule%20harvest%20processing**%20to%20meet%20requirements%20at%20minimum%20cost.%0A%0A%20%20%20%20%3E%20**Mosel%20source%20files%3A**%20%5B%60models%2Fmining_process%2F%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Ftree%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fmining_process)%20%26nbsp%3B%7C%26nbsp%3B%20**Data%20files%3A**%20%5B%60data%2Fmining_process%2F%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Ftree%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fdata%2Fmining_process)%0A%0A%20%20%20%20%3E%20**Requirements%3A**%20%60pip%20install%20moselpy%20mosellibs%20xpresslibs%60%20-%20%60mosellibs%60%20provides%20the%20Mosel%20runtime%20and%20%60xpresslibs%60%20the%20Xpress%20solver%20libraries.%20No%20local%20Xpress%20installation%20is%20needed%20to%20run%20these%20notebooks%2C%20as%20long%20as%20the%20packages%20with%20libraries%20have%20been%20installed.%0A%0A%20%20%20%20%3E%20A%20local%20FICO%26reg%3B%20Xpress%20installation%20is%20recommended%20if%20you%20want%20access%20to%20the%20full%20set%20of%20examples%2C%20user%20guides%20and%20documentation%2C%20solver%20libraries%2C%20and%20Xpress%20Workbench.%20Obtain%20one%20via%20the%20%5BFICO%26reg%3B%20Xpress%20community%20license%5D(https%3A%2F%2Fwww.fico.com%2Fen%2Ffico-xpress-community-license)%20page.%20If%20a%20local%20installation%20is%20present%2C%20set%20%60XPRESSDIR%60%20before%20starting%20marimo%20to%20use%20it%20instead%20of%20the%20%60mosellibs%60%20and%20%60xpresslibs%60%20packages.%0A%0A%20%20%20%20%3E%20**MoselPy%20documentation%3A**%20%5BMoselPy%20User%20Guide%20and%20Reference%20Manual%5D(https%3A%2F%2Fwww.fico.com%2Ffico-xpress-optimization%2Fdocs%2Flatest%2Fmosel%2FMoselPy%2Fdhtml)%0A%0A%20%20%20%20%3E%20**Mosel%20examples%20on%20the%20web%3A**%20%5Bexamples.xpress.fico.com%5D(https%3A%2F%2Fexamples.xpress.fico.com%2Fexample.pl%3Fid%3Dmosel_app_1)%0A%0A%20%20%20%20%23%23%23%20MoselPy%20features%20demonstrated%20in%20this%20chapter%0A%0A%20%20%20%20-%20**Compile-load-run%20sequence**%3A%20A-1%2C%20A-5%2C%20and%20A-6%20compile%20the%20model%20to%20a%20%60.bim%60%20file%20and%20call%20%60load_model()%60%20to%20enable%20multiple%20runs%20or%20introspection.%0A%20%20%20%20-%20**Single%20execution%20command**%3A%20A-2%2C%20A-3%2C%20and%20A-4%20use%20%60mp.execute_model()%60%20for%20one-off%20runs%20with%20no%20%60.bim%60%20written%20to%20disk.%0A%20%20%20%20-%20**%60find_identifier(use_pandas%3DTrue)%60%20%2B%20%60unstack()%60**%3A%20A-2%20retrieves%20the%202D%20blending%20solution%20as%20a%20MultiIndex%20Series%20and%20pivots%20it%20into%20a%20readable%20DataFrame.%0A%20%20%20%20-%20**Sensitivity%20analysis**%3A%20A-6%20runs%20a%20parametric%20loop%20varying%20demand%20and%20collects%20total%20costs%20into%20a%20%60pd.DataFrame%60.%0A%0A%20%20%20%20%23%23%23%20Workflow%20patterns%0A%0A%20%20%20%20This%20notebook%20uses%20different%20MoselPy%20patterns%20depending%20on%20the%20use%20case%3A%0A%0A%20%20%20%20-%20The%20Mosel%20source%20files%20in%20%5B%60models%2Fmining_process%2F%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Ftree%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fmining_process)%20have%20been%20adapted%20to%20receive%20data%20from%20Python%20via%20%60input_data%60%2C%20while%20remaining%20runnable%20as%20standalone%20Mosel%20models%20using%20their%20%60.dat%60%20files.%0A%20%20%20%20-%20**One-step%20compile%20and%20run**%20(%60execute_model%60)%3A%20compiles%20and%20runs%20the%20model%20in%20a%20single%20call%2C%20returning%20%60(model%2C%20output_dict)%60.%20Used%20for%20models%20that%20run%20once%20and%20need%20no%20%60.bim%60%20on%20disk.%0A%20%20%20%20-%20**Three-step**%20(%60compile_model%60%20-%3E%20%60load_model%60%20-%3E%20%60run%60)%3A%20when%20the%20same%20compiled%20model%20is%20used%20multiple%20times%20(e.g.%20running%20multiple%20scenarios)%20or%20when%20model%20introspection%20is%20useful.%0A%20%20%20%20-%20**Context%20manager**%20(%60with%20mp.load_model(...)%20as%20model%3A%60)%3A%20ensures%20Mosel%20resources%20are%20freed%20as%20soon%20as%20the%20block%20exits%2C%20without%20needing%20an%20explicit%20close.%0A%0A%20%20%20%20Input%20data%20is%20passed%20from%20Python%20via%20%60input_data%60%3B%20the%20standalone%20%60.dat%60%20files%20remain%20available%20for%20running%20models%20outside%20Python.%20Results%20come%20back%20via%20%60model.run()%60's%20return%20dict%20(for%20items%20in%20%60initializations%20to%20OUTFILE%60)%20or%20via%20%60model.find_identifier()%60%20for%20any%20%60public%60-declared%20entity.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20A-1%20Production%20of%20alloys%20(LP)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20*Applications%20Book%20Chapter%20A%2C%20Example%201*%0A%0A%20%20%20%20%3E%20**Mosel%20model%3A**%20%5B%60a1alloy_p.mos%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Fblob%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fmining_process%2Fa1alloy_p.mos)%0A%0A%20%20%20%20A%20steelworks%20must%20produce%20500%20tonnes%20of%20steel%20satisfying%20carbon%20(C)%2C%20copper%20(Cu)%2C%20and%20manganese%20(Mn)%20grade%20requirements.%20Seven%20raw%20materials%20(iron%20and%20copper%20alloys%2C%20aluminium)%20are%20available%20at%20different%20costs%20and%20in%20limited%20quantities.%20The%20objective%20is%20to%20minimize%20total%20raw%20material%20cost.%0A%0A%20%20%20%20**Sets%3A**%20%24%5Ctext%7BRAW%7D%20%3D%201..7%24%20(raw%20materials)%2C%20%24%5Ctext%7BCOMP%7D%20%3D%201..3%24%20(chemical%20elements%3A%20C%2C%20Cu%2C%20Mn)%0A%0A%20%20%20%20**Decision%20variables%3A**%20%24%5Ctext%7Buse%7D_r%20%5Cgeq%200%24%20(tonnes%20of%20raw%20material%20%24r%24%20used)%3B%20%24%5Ctext%7Bproduce%7D%20%5Cgeq%200%24%20(total%20steel%20produced).%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cbegin%7Balign*%7D%0A%20%20%20%20%5Ctext%7Bminimize%7D%20%20%20%5Cquad%20%26%20%5Csum_%7Br%7D%20%5Ctext%7BCOST%7D_r%20%5Ccdot%20%5Ctext%7Buse%7D_r%20%5C%5C%0A%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%5Cquad%20%26%20%5Ctext%7Bproduce%7D%20%3D%20%5Csum_r%20%5Ctext%7Buse%7D_r%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Ctext%7BPMIN%7D_c%20%5Ccdot%20%5Ctext%7Bproduce%7D%20%5Cleq%20%5Csum_r%20P_%7Br%2Cc%7D%20%5Ccdot%20%5Ctext%7Buse%7D_r%20%5Cleq%20%5Ctext%7BPMAX%7D_c%20%5Ccdot%20%5Ctext%7Bproduce%7D%20%5Cquad%20%5Cforall%5C%2C%20c%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Ctext%7Buse%7D_r%20%5Cleq%20%5Ctext%7BAVAIL%7D_r%20%5Cquad%20%5Cforall%5C%2C%20r%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Ctext%7Bproduce%7D%20%5Cgeq%20%5Ctext%7BDEM%7D%0A%20%20%20%20%5Cend%7Balign*%7D%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Define%20the%20seven%20raw%20materials%20with%20their%20chemical%20compositions%20(%24P%24%20matrix)%2C%20costs%2C%20and%20availabilities.%0A%0A%20%20%20%20For%202-D%20Mosel%20arrays%2C%20MoselPy%20requires%20a%20dict%20with%20%60(row%2C%20col)%60%20tuple%20keys.%20A%201-D%20list%20maps%20element-by-element%20to%20the%20declared%20index%20set%20(1-indexed).%0A%0A%20%20%20%20The%20heatmap%20below%20shows%20the%20percentage%20of%20each%20chemical%20element%20(Carbon%2C%20Copper%2C%20Manganese)%20in%20each%20raw%20material.%20Most%20materials%20are%20specialists%3A%20iron%20alloys%20carry%20carbon%20and%20manganese%3B%20copper%20alloys%20are%20almost%20pure%20copper.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20plt)%3A%0A%20%20%20%20NR%2C%20NC%20%3D%20(7%2C%203)%0A%20%20%20%20P_data%20%3D%20%5B%5B2.5%2C%200%2C%201.3%5D%2C%20%5B3.0%2C%200%2C%200.8%5D%2C%20%5B0%2C%200.3%2C%200%5D%2C%20%5B0%2C%2090%2C%200%5D%2C%20%5B0%2C%2096%2C%204%5D%2C%20%5B0%2C%200.4%2C%201.2%5D%2C%20%5B0%2C%200.6%2C%200%5D%5D%0A%20%20%20%20P%20%3D%20%7B(r%20%2B%201%2C%20c%20%2B%201)%3A%20P_data%5Br%5D%5Bc%5D%20for%20r%20in%20range(NR)%20for%20c%20in%20range(NC)%7D%20%20%23%20composition%20(%25%20C%2C%20Cu%2C%20Mn)%20per%20raw%20material%0A%20%20%20%20PMIN%20%3D%20%5B2.0%2C%200.4%2C%201.2%5D%20%20%23%20dict%20with%20(raw%2C%20comp)%20tuple%20keys%0A%20%20%20%20PMAX%20%3D%20%5B3.0%2C%200.6%2C%201.65%5D%20%20%23%20min%20grade%20requirement%20per%20element%20(%25)%0A%20%20%20%20AVAIL%20%3D%20%5B400%2C%20300%2C%20600%2C%20500%2C%20200%2C%20300%2C%20250%5D%20%20%23%20max%20grade%20requirement%20per%20element%20(%25)%0A%20%20%20%20COST%20%3D%20%5B200%2C%20250%2C%20150%2C%20220%2C%20240%2C%20200%2C%20165%5D%20%20%23%20availability%20(tonnes)%20per%20raw%20material%0A%20%20%20%20NAMES%20%3D%20%5B'iron%201'%2C%20'iron%202'%2C%20'iron%203'%2C%20'copper%201'%2C%20'copper%202'%2C%20'aluminum%201'%2C%20'aluminum%202'%5D%20%20%23%20cost%20(%24%2Ftonne)%20per%20raw%20material%0A%20%20%20%20DEM%20%3D%20500%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(5%2C%205))%20%20%23%20raw%20material%20names%0A%20%20%20%20comp_data%20%3D%20P_data%20%20%23%20steel%20demand%20(tonnes)%0A%20%20%20%20im%20%3D%20_ax.imshow(comp_data%2C%20cmap%3D'YlOrRd'%2C%20aspect%3D'auto')%0A%20%20%20%20_ax.set_xticks(%5B0%2C%201%2C%202%5D)%0A%20%20%20%20_ax.set_xticklabels(%5B'Carbon%20(%25)'%2C%20'Copper%20(%25)'%2C%20'Manganese%20(%25)'%5D)%0A%20%20%20%20_ax.set_yticks(range(NR))%0A%20%20%20%20_ax.set_yticklabels(NAMES)%0A%20%20%20%20for%20_r%20in%20range(NR)%3A%0A%20%20%20%20%20%20%20%20for%20_c%20in%20range(NC)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.text(_c%2C%20_r%2C%20f'%7Bcomp_data%5B_r%5D%5B_c%5D%7D'%2C%20ha%3D'center'%2C%20va%3D'center'%2C%20fontsize%3D9)%0A%20%20%20%20plt.colorbar(im%2C%20ax%3D_ax%2C%20label%3D'Composition%20(%25)')%0A%20%20%20%20_ax.set_title('Raw%20material%20composition')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%20AVAIL%2C%20COST%2C%20DEM%2C%20NAMES%2C%20NC%2C%20NR%2C%20P%2C%20PMAX%2C%20PMIN%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20code%20cell%20below%20uses%20%60compile_model%60%2C%20%60load_model%60%2C%20and%20%60run%60%20to%20compile%20and%20solve%20the%20alloy%20blending%20LP.%0A%0A%20%20%20%20%60exec_params%60%20overrides%20the%20model's%20%60DATASOURCE%60%20and%20%60OUTFILE%60%20parameters%20to%20%60%22moselpy%3A%22%60%2C%20redirecting%20data%20I%2FO%20through%20Python%20instead%20of%20files.%20%60input_data%60%20passes%20the%20%60NR%60%20%26times%3B%20%60NC%60%20composition%20matrix%20%60P%60%20(as%20a%20dict%20with%20%60(row%2C%20col)%60%20tuple%20keys)%2C%20grade%20bounds%2C%20availabilities%2C%20costs%2C%20and%20demand%20defined%20above.%20After%20%60load_model()%60%2C%20%60model.name%60%20and%20%60model.size%60%20are%20available%20for%20introspection%20before%20any%20%60run()%60%20call.%20After%20the%20run%2C%20%60model.find_identifier()%60%20retrieves%20any%20%60public%60-declared%20model%20entity%20by%20name%20-%20scalars%20come%20back%20as%20%60float%60%2C%20arrays%20as%20dicts%20keyed%20by%20index.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(AVAIL%2C%20COST%2C%20DEM%2C%20MODELS%2C%20NAMES%2C%20NC%2C%20NR%2C%20P%2C%20PMAX%2C%20PMIN%2C%20mp)%3A%0A%20%20%20%20mp.compile_model(f'%7BMODELS%7D%2Fa1alloy_p.mos'%2C%20f'%7BMODELS%7D%2Fa1alloy_p.bim')%0A%20%20%20%20model%20%3D%20mp.load_model(f'%7BMODELS%7D%2Fa1alloy_p.bim')%0A%20%20%20%20print(f%22Model%3A%20'%7Bmodel.name%7D'%20%20%7C%20%20compiled%20size%3A%20%7Bmodel.size%3A%2C%7D%20bytes%22)%0A%20%20%20%20_output%20%3D%20model.run(exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D%2C%20input_data%3D%7B'NR'%3A%20NR%2C%20'NC'%3A%20NC%2C%20'P'%3A%20P%2C%20'PMIN'%3A%20PMIN%2C%20'PMAX'%3A%20PMAX%2C%20'AVAIL'%3A%20AVAIL%2C%20'COST'%3A%20COST%2C%20'DEM'%3A%20DEM%2C%20'NAMES'%3A%20NAMES%7D)%0A%20%20%20%20obj_val%20%3D%20_output%5B'obj_val'%5D%0A%20%20%20%20sol_produce%20%3D%20_output%5B'sol_produce'%5D%0A%20%20%20%20sol_use%20%3D%20_output%5B'sol_use'%5D%0A%20%20%20%20print(f'Total%20cost%3A%20%20%20%20%24%7Bobj_val%3A%2C.2f%7D')%0A%20%20%20%20print(f'Steel%20produced%3A%20%7Bsol_produce%3A.0f%7D%20tonnes')%0A%20%20%20%20print()%0A%20%20%20%20print(f'%7B'Raw%20material'%3A%3C14%7D%20%7B'Used%20(t)'%3A%3E10%7D')%0A%20%20%20%20for%20_r%20in%20range(1%2C%208)%3A%0A%20%20%20%20%20%20%20%20_v%20%3D%20sol_use%5B_r%5D%0A%20%20%20%20%20%20%20%20if%20_v%20%3E%200.01%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f'%20%20%7BNAMES%5B_r%20-%201%5D%3A%3C12%7D%20%7B_v%3A%3E10.2f%7D')%0A%20%20%20%20return%20obj_val%2C%20sol_produce%2C%20sol_use%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20pie%20chart%20shows%20which%20raw%20materials%20contribute%20to%20the%20optimal%20blend%20and%20in%20what%20proportion%2C%20filtered%20to%20materials%20used%20above%200.01%20t.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(NAMES%2C%20mo%2C%20obj_val%2C%20plt%2C%20sol_produce%2C%20sol_use)%3A%0A%20%20%20%20blend%20%3D%20%7BNAMES%5Br%20-%201%5D%3A%20sol_use%5Br%5D%20for%20r%20in%20range(1%2C%208)%20if%20sol_use%5Br%5D%20%3E%200.01%7D%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(6%2C%205))%0A%20%20%20%20_ax.pie(list(blend.values())%2C%20labels%3Dlist(blend.keys())%2C%20autopct%3D'%251.1f%25%25'%2C%20startangle%3D90)%0A%20%20%20%20_ax.set_title(f'A-1%20Alloy%20blend%20composition%20(%7Bsol_produce%3A.0f%7D%20t%20steel%2C%20cost%20%24%7Bobj_val%3A%2C.0f%7D)')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20A-2%20Animal%20food%20production%20(LP)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20*Applications%20Book%20Chapter%20A%2C%20Example%202*%0A%0A%20%20%20%20%3E%20**Mosel%20model%3A**%20%5B%60a2food_p.mos%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Fblob%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fmining_process%2Fa2food_p.mos)%0A%0A%20%20%20%20A%20farm%20produces%20two%20types%20of%20animal%20food%20by%20blending%20oat%2C%20maize%2C%20and%20molasses.%20Each%20food%20type%20must%20meet%20minimum%20protein%20and%20lipid%20requirements%20and%20a%20maximum%20fibre%20content.%20Raw%20material%20availabilities%20are%20limited.%20Four%20processing%20operations%20each%20add%20a%20cost%20per%20unit%20processed%3A%20grinding%20(oat%20and%20maize%20only)%2C%20blending%20(all%20raw%20materials)%2C%20granulating%20(food%20type%201)%2C%20and%20sieving%20(food%20type%202).%20Minimize%20total%20production%20cost.%0A%0A%20%20%20%20**Sets%3A**%20%24%5Ctext%7BRAW%7D%20%3D%20%5C%7B%5Ctext%7Boat%2C%20maize%2C%20molasses%7D%5C%7D%24%2C%20%24%5Ctext%7BFOOD%7D%20%3D%201..2%24%2C%20%24%5Ctext%7BCOMP%7D%20%3D%201..3%24%20(protein%2C%20lipid%2C%20fibre)%0A%0A%20%20%20%20**Decision%20variables%3A**%20%24%5Ctext%7Buse%7D_%7Br%2Cf%7D%20%5Cgeq%200%24%20(raw%20material%20%24r%24%20used%20for%20food%20type%20%24f%24)%3B%20%24%5Ctext%7Bproduce%7D_f%20%5Cgeq%200%24%20(quantity%20of%20food%20%24f%24%20produced).%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cbegin%7Balign*%7D%0A%20%20%20%20%5Ctext%7Bminimize%7D%20%20%20%5Cquad%20%26%20%5Csum_%7Br%2C%5C%2C%20f%7D%20%5Ctext%7BCOST%7D_r%20%5Ccdot%20%5Ctext%7Buse%7D_%7Br%2Cf%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2B%20%5Ctext%7BPCOST%7D_%7B%5Ctext%7Bgrd%7D%7D%20%5Csum_%7Br%20%5Cneq%20%5Ctext%7Bmol%7D%2C%5C%2C%20f%7D%20%5Ctext%7Buse%7D_%7Br%2Cf%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%2B%20%5Ctext%7BPCOST%7D_%7B%5Ctext%7Bbld%7D%7D%20%5Csum_%7Br%2Cf%7D%20%5Ctext%7Buse%7D_%7Br%2Cf%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2B%20%5Ctext%7BPCOST%7D_%7B%5Ctext%7Bgra%7D%7D%20%5Csum_r%20%5Ctext%7Buse%7D_%7Br%2C1%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2B%20%5Ctext%7BPCOST%7D_%7B%5Ctext%7Bsiv%7D%7D%20%5Csum_r%20%5Ctext%7Buse%7D_%7Br%2C2%7D%20%5C%5C%0A%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%5Cquad%20%26%20%5Csum_r%20%5Ctext%7Buse%7D_%7Br%2Cf%7D%20%3D%20%5Ctext%7Bproduce%7D_f%20%5Cquad%20%5Cforall%5C%2C%20f%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_r%20P_%7Br%2Cc%7D%20%5Ccdot%20%5Ctext%7Buse%7D_%7Br%2Cf%7D%20%5Cgeq%20%5Ctext%7BREQ%7D_c%20%5Ccdot%20%5Ctext%7Bproduce%7D_f%20%5Cquad%20%5Cforall%5C%2C%20f%2C%5C%2C%20c%20%5Cin%20%5C%7B1%2C2%5C%7D%20%5Cquad%20%5Ctext%7B(min%20protein%2C%20lipid)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_r%20P_%7Br%2C3%7D%20%5Ccdot%20%5Ctext%7Buse%7D_%7Br%2Cf%7D%20%5Cleq%20%5Ctext%7BREQ%7D_3%20%5Ccdot%20%5Ctext%7Bproduce%7D_f%20%5Cquad%20%5Cforall%5C%2C%20f%20%5Cquad%20%5Ctext%7B(max%20fibre)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_f%20%5Ctext%7Buse%7D_%7Br%2Cf%7D%20%5Cleq%20%5Ctext%7BAVAIL%7D_r%20%5Cquad%20%5Cforall%5C%2C%20r%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Ctext%7Bproduce%7D_f%20%5Cgeq%20%5Ctext%7BDEM%7D_f%20%5Cquad%20%5Cforall%5C%2C%20f%0A%20%20%20%20%5Cend%7Balign*%7D%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Define%20the%20raw%20material%20data%20as%20a%20%60pd.DataFrame%60%20--%20one%20row%20per%20raw%20material%2C%20one%20column%20for%20availability%20and%20one%20for%20cost.%20The%20individual%20columns%20are%20passed%20as%20%60pd.Series%60%20to%20%60input_data%60%2C%20which%20MoselPy%20maps%20automatically%20to%20Mosel%20string-indexed%20arrays.%20Nutritional%20compositions%2C%20requirements%2C%20and%20demands%20are%20passed%20as%20plain%20dicts.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20pd%2C%20plt)%3A%0A%20%20%20%20NF%2C%20NC_1%20%3D%20(2%2C%203)%0A%20%20%20%20raw_materials%20%3D%20%5B'oat'%2C%20'maize'%2C%20'molasses'%5D%0A%20%20%20%20raw_df%20%3D%20pd.DataFrame(%7B'availability'%3A%20%7B'oat'%3A%2011900%2C%20'maize'%3A%2023500%2C%20'molasses'%3A%20750%7D%2C%20'cost'%3A%20%7B'oat'%3A%200.13%2C%20'maize'%3A%200.17%2C%20'molasses'%3A%200.12%7D%7D%2C%20index%3Draw_materials)%0A%20%20%20%20print(raw_df.to_string())%0A%20%20%20%20P_rows%20%3D%20%7B'oat'%3A%20%5B13.6%2C%207.1%2C%207.0%5D%2C%20'maize'%3A%20%5B4.1%2C%202.4%2C%203.7%5D%2C%20'molasses'%3A%20%5B5.0%2C%200.3%2C%2025.0%5D%7D%0A%20%20%20%20P_1%20%3D%20%7B(r%2C%20c%20%2B%201)%3A%20P_rows%5Br%5D%5Bc%5D%20for%20r%20in%20raw_materials%20for%20c%20in%20range(3)%7D%0A%20%20%20%20REQ%20%3D%20%7B1%3A%209.5%2C%202%3A%202.0%2C%203%3A%206.0%7D%0A%20%20%20%20PCOST%20%3D%20%7B'grinding'%3A%200.25%2C%20'blending'%3A%200.05%2C%20'granulating'%3A%200.42%2C%20'sieving'%3A%200.17%7D%0A%20%20%20%20DEM_1%20%3D%20%7B1%3A%209000%2C%202%3A%2012000%7D%0A%20%20%20%20_fig%2C%20_axes%20%3D%20plt.subplots(1%2C%202%2C%20figsize%3D(7%2C%203))%0A%20%20%20%20_axes%5B0%5D.bar(raw_materials%2C%20raw_df%5B'availability'%5D%2C%20color%3D'steelblue')%0A%20%20%20%20_axes%5B0%5D.set_ylabel('Availability%20(kg%2Fday)')%0A%20%20%20%20_axes%5B0%5D.set_title('Raw%20material%20availability')%0A%20%20%20%20_axes%5B1%5D.bar(raw_materials%2C%20raw_df%5B'cost'%5D%2C%20color%3D'darkorange')%0A%20%20%20%20_axes%5B1%5D.set_ylabel('Cost%20(%24%2Fkg)')%0A%20%20%20%20_axes%5B1%5D.set_title('Raw%20material%20cost')%0A%20%20%20%20plt.suptitle('A-2%20Food%3A%20raw%20material%20data')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%20DEM_1%2C%20NC_1%2C%20NF%2C%20PCOST%2C%20P_1%2C%20REQ%2C%20raw_df%2C%20raw_materials%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%60mp.execute_model()%60%20compiles%20and%20runs%20the%20model%20in%20one%20step%2C%20returning%20%60(model%2C%20output_dict)%60.%20%60exec_params%60%20overrides%20both%20%60DATASOURCE%60%20and%20%60OUTFILE%60%20to%20%60%22moselpy%3A%22%60%2C%20routing%20all%20data%20I%2FO%20through%20Python%20instead%20of%20files.%20%60input_data%60%20delivers%20the%20Python-side%20data%20at%20runtime.%20The%20returned%20%60model%60%20object%20remains%20usable%20after%20the%20run%3B%20the%20pandas%20cell%20below%20calls%20%60model.find_identifier(%22sol_use%22%2C%20use_pandas%3DTrue)%60%20to%20retrieve%20the%20result%20array%20directly%20as%20a%20pandas%20object.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(DEM_1%2C%20MODELS%2C%20NC_1%2C%20NF%2C%20PCOST%2C%20P_1%2C%20REQ%2C%20mp%2C%20raw_df%2C%20raw_materials)%3A%0A%20%20%20%20model_1%2C%20_output%20%3D%20mp.execute_model(f'%7BMODELS%7D%2Fa2food_p.mos'%2C%20exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D%2C%20input_data%3D%7B'NF'%3A%20NF%2C%20'NC'%3A%20NC_1%2C%20'P'%3A%20P_1%2C%20'REQ'%3A%20REQ%2C%20'AVAIL'%3A%20raw_df%5B'availability'%5D%2C%20'COST'%3A%20raw_df%5B'cost'%5D%2C%20'PCOST'%3A%20PCOST%2C%20'DEM'%3A%20DEM_1%7D)%0A%20%20%20%20obj_val_1%20%3D%20_output%5B'obj_val'%5D%0A%20%20%20%20sol_use_1%20%3D%20model_1.find_identifier('sol_use')%0A%20%20%20%20sol_produce_1%20%3D%20model_1.find_identifier('sol_produce')%0A%20%20%20%20print(f'Total%20cost%3A%20%24%7Bobj_val_1%3A%2C.2f%7D')%0A%20%20%20%20print()%0A%20%20%20%20print(f'%7B'Food%20type'%3A%3C12%7D'%2C%20end%3D'')%0A%20%20%20%20for%20_r%20in%20raw_materials%3A%0A%20%20%20%20%20%20%20%20print(f'%7B_r%3A%3E10%7D'%2C%20end%3D'')%0A%20%20%20%20print()%0A%20%20%20%20for%20f%20in%20%5B1%2C%202%5D%3A%0A%20%20%20%20%20%20%20%20print(f'%20%20type%20%7Bf%7D%20%20%20%20%20'%2C%20end%3D'')%0A%20%20%20%20%20%20%20%20for%20_r%20in%20raw_materials%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f'%7Bsol_use_1%5B_r%2C%20f%5D%3A%3E10.2f%7D'%2C%20end%3D'')%0A%20%20%20%20%20%20%20%20print()%0A%20%20%20%20return%20model_1%2C%20obj_val_1%2C%20sol_use_1%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20stacked%20bar%20chart%20shows%20how%20each%20raw%20material%20is%20distributed%20across%20the%20two%20food%20types%20in%20the%20optimal%20blend.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20np%2C%20obj_val_1%2C%20plt%2C%20sol_use_1)%3A%0A%20%20%20%20raw_materials_1%20%3D%20%5B'oat'%2C%20'maize'%2C%20'molasses'%5D%0A%20%20%20%20food_labels%20%3D%20%5B'Food%20type%201'%2C%20'Food%20type%202'%5D%0A%20%20%20%20data%20%3D%20np.array(%5B%5Bsol_use_1%5Br%2C%20f%5D%20for%20r%20in%20raw_materials_1%5D%20for%20f%20in%20%5B1%2C%202%5D%5D)%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(6%2C%204))%0A%20%20%20%20x%20%3D%20np.arange(2)%0A%20%20%20%20_bottom%20%3D%20np.zeros(2)%0A%20%20%20%20_colors%20%3D%20%5B'%234CAF50'%2C%20'%23FFC107'%2C%20'%232196F3'%5D%0A%20%20%20%20for%20i%2C%20(_r%2C%20_c)%20in%20enumerate(zip(raw_materials_1%2C%20_colors))%3A%0A%20%20%20%20%20%20%20%20_ax.bar(x%2C%20data%5B%3A%2C%20i%5D%2C%20bottom%3D_bottom%2C%20label%3D_r%2C%20color%3D_c)%0A%20%20%20%20%20%20%20%20_bottom%20%3D%20_bottom%20%2B%20data%5B%3A%2C%20i%5D%0A%20%20%20%20_ax.set_xticks(x)%0A%20%20%20%20_ax.set_xticklabels(food_labels)%0A%20%20%20%20_ax.set_ylabel('Quantity%20(kg%2Fday)')%0A%20%20%20%20_ax.set_title(f'A-2%20Food%20blend%20composition%20-%20total%20cost%20%24%7Bobj_val_1%3A%2C.0f%7D')%0A%20%20%20%20_ax.legend()%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Pandas%20output%20via%20%60find_identifier%60%0A%0A%20%20%20%20%60find_identifier(%22name%22%2C%20use_pandas%3DTrue)%60%20returns%20array%20results%20as%20%60pandas%60%20objects%3A%201D%20arrays%20come%20back%20as%20%60pd.Series%60%20and%202D%20arrays%20as%20a%20%60MultiIndex%20Series%60.%20Call%20%60.unstack()%60%20to%20pivot%20one%20index%20level%20into%20columns%20and%20produce%20a%20%60DataFrame%60%20directly%20from%20a%202D%20result.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(model_1%2C%20obj_val_1)%3A%0A%20%20%20%20%23%20find_identifier%20with%20use_pandas%3DTrue%20returns%20a%20MultiIndex%20Series%20for%202D%20arrays.%0A%20%20%20%20%23%20unstack%20pivots%20one%20index%20level%20to%20columns%20-%20here%20level%200%20(raw%20material)%20becomes%20column%20headers.%0A%20%20%20%20sol_use_df%20%3D%20model_1.find_identifier('sol_use'%2C%20use_pandas%3DTrue).unstack(level%3D0)%0A%20%20%20%20sol_use_df.index%20%3D%20%5Bf'Food%20type%20%7Bf%7D'%20for%20f%20in%20sol_use_df.index%5D%0A%20%20%20%20print(f'Raw%20material%20usage%20(kg%2Fday)%20%20-%20total%20cost%20%24%7Bobj_val_1%3A%2C.2f%7D')%0A%20%20%20%20print(sol_use_df.round(2).to_string())%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20A-3%20Refinery%20planning%20(LP)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20*Applications%20Book%20Chapter%20A%2C%20Example%203*%0A%0A%20%20%20%20%3E%20**Mosel%20model%3A**%20%5B%60a3refine_p.mos%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Fblob%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fmining_process%2Fa3refine_p.mos)%0A%0A%20%20%20%20A%20refinery%20processes%20two%20crude%20oils%20through%20four%20operations%20(distillation%2C%20reforming%2C%20cracking%2C%20desulfurization)%20to%20produce%20butane%2C%20petrol%2C%20diesel%2C%20and%20heating%20oil.%20Monthly%20demands%20must%20be%20met%2C%20and%20petrol%20must%20satisfy%20octane%2C%20vapour%20pressure%2C%20and%20volatility%20specifications%3B%20diesel%20must%20meet%20a%20sulphur%20limit.%20Minimize%20total%20crude%20and%20processing%20cost.%0A%0A%20%20%20%20**Sets%3A**%20%24%5Ctext%7BCRUDES%7D%24%20(crude%20types)%3B%20%24%5Ctext%7BIDIST%7D%24%2C%20%24%5Ctext%7BIREF%7D%24%2C%20%24%5Ctext%7BICRACK%7D%24%20(distillation%2C%20reforming%2C%20cracking%20streams)%3B%20%24%5Ctext%7BIPETROL%7D%24%2C%20%24%5Ctext%7BIDIESEL%7D%24%2C%20%24%5Ctext%7BIHO%7D%24%20(blend%20component%20streams)%3B%20%24%5Ctext%7BFINAL%7D%20%3D%20%5C%7B%5Ctext%7Bbutane%2C%20petrol%2C%20diesel%2C%20heating%7D%5C%7D%24.%0A%0A%20%20%20%20**Decision%20variables%3A**%20%24%5Ctext%7Buse%7D_c%20%5Cgeq%200%24%20(barrels%20of%20crude%20%24c%24%20processed%20per%20month)%3B%20%24%5Ctext%7Bproduce%7D_p%20%5Cgeq%200%24%20(barrels%20of%20product%20%24p%24).%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cbegin%7Balign*%7D%0A%20%20%20%20%5Ctext%7Bminimize%7D%20%20%20%5Cquad%20%26%20%5Csum_%7Bc%20%5Cin%20%5Ctext%7BCRUDES%7D%7D%20%5Ctext%7BCOST%7D_c%20%5Ccdot%20%5Ctext%7Buse%7D_c%20%2B%20%5Csum_%7Bp%20%5Cin%20%5Ctext%7BIDIST%7D%7D%20%5Ctext%7BCOST%7D_p%20%5Ccdot%20%5Ctext%7Bproduce%7D_p%20%5C%5C%0A%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%5Cquad%20%26%20%5Ctext%7Bproduce%7D_p%20%5Cleq%20%5Csum_c%20%5Ctext%7BDIST%7D_%7Bc%2Cp%7D%20%5Ccdot%20%5Ctext%7Buse%7D_c%20%5Cquad%20%5Cforall%5C%2C%20p%20%5Cin%20%5Ctext%7BIDIST%7D%20%5Cquad%20%5Ctext%7B(distillation%20yields)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Ctext%7Bproduce%7D_p%20%5Cleq%20%5Ctext%7BREF%7D_p%20%5Ccdot%20%5Ctext%7Bproduce%7D_%7B%5Ctext%7Bnaphtha%7D%7D%20%5Cquad%20%5Cforall%5C%2C%20p%20%5Cin%20%5Ctext%7BIREF%7D%20%5Cquad%20%5Ctext%7B(reforming)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Ctext%7Bproduce%7D_p%20%5Cleq%20%5Ctext%7BCRACK%7D_p%20%5Ccdot%20%5Ctext%7Bproduce%7D_%7B%5Ctext%7Bresidue%7D%7D%20%5Cquad%20%5Cforall%5C%2C%20p%20%5Cin%20%5Ctext%7BICRACK%7D%20%5Cquad%20%5Ctext%7B(cracking)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Ctext%7Bproduce%7D_p%20%3D%20%5Csum_%7Bs%20%5Cin%20%5Ctext%7Bstreams%7D(p)%7D%20%5Ctext%7Bproduce%7D_s%20%5Cquad%20%5Cforall%5C%2C%20p%20%5Cin%20%5Ctext%7BFINAL%7D%20%5Cquad%20%5Ctext%7B(product%20assembly)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_%7Bp%20%5Cin%20%5Ctext%7BIPETROL%7D%7D%20%5Ctext%7BOCT%7D_p%20%5Ccdot%20%5Ctext%7Bproduce%7D_p%20%5Cgeq%2094%20%5Ccdot%20%5Ctext%7Bproduce%7D_%7B%5Ctext%7Bpetrol%7D%7D%20%5Cquad%20%5Ctext%7B(octane%20spec)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_%7Bp%20%5Cin%20%5Ctext%7BIPETROL%7D%7D%20%5Ctext%7BVAP%7D_p%20%5Ccdot%20%5Ctext%7Bproduce%7D_p%20%5Cleq%2012.7%20%5Ccdot%20%5Ctext%7Bproduce%7D_%7B%5Ctext%7Bpetrol%7D%7D%20%5Cquad%20%5Ctext%7B(vapour%20pressure%20spec)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_%7Bp%20%5Cin%20%5Ctext%7BIPETROL%7D%7D%20%5Ctext%7BVOL%7D_p%20%5Ccdot%20%5Ctext%7Bproduce%7D_p%20%5Cgeq%2017%20%5Ccdot%20%5Ctext%7Bproduce%7D_%7B%5Ctext%7Bpetrol%7D%7D%20%5Cquad%20%5Ctext%7B(volatility%20spec)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_%7Bp%20%5Cin%20%5Ctext%7BIDIESEL%7D%7D%20%5Ctext%7BSULF%7D_p%20%5Ccdot%20%5Ctext%7Bproduce%7D_p%20%5Cleq%200.05%20%5Ccdot%20%5Ctext%7Bproduce%7D_%7B%5Ctext%7Bdiesel%7D%7D%20%5Cquad%20%5Ctext%7B(sulphur%20spec)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Ctext%7Buse%7D_c%20%5Cleq%20%5Ctext%7BAVAIL%7D_c%20%5Cquad%20%5Cforall%5C%2C%20c%3B%20%5Cquad%20%5Ctext%7Bproduce%7D_p%20%5Cgeq%20%5Ctext%7BDEM%7D_p%20%5Cquad%20%5Cforall%5C%2C%20p%20%5Cin%20%5Ctext%7BFINAL%7D%0A%20%20%20%20%5Cend%7Balign*%7D%0A%20%20%20%20%24%24%0A%0A%20%20%20%20This%20is%20a%20large-scale%20LP%20with%20dynamically%20populated%20string%20sets%3B%20the%20set%20of%20intermediate%20and%20final%20products%20is%20built%20from%20the%20input%20data.%20The%20adapted%20model%20makes%20the%20final%20product%20quantities%20and%20crude%20usage%20accessible%20via%20%60public%60%20declarations.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20All%20refinery%20data%20is%20defined%20in%20Python.%20String-indexed%20sets%20(%60CRUDES%60%2C%20%60FINAL%60%2C%20etc.)%20are%20auto-populated%20by%20Mosel%20from%20the%20dict%20keys%20when%20data%20is%20passed%20via%20%60input_data%60%20-%20there%20is%20no%20need%20to%20pass%20them%20explicitly.%20Only%20%60IHO%60%20(a%20standalone%20set%20with%20no%20corresponding%20array)%20must%20be%20passed%20as%20a%20Python%20%60set%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20AVAIL_1%20%3D%20%7B'crude1'%3A%20250000%2C%20'crude2'%3A%20500000%7D%0A%20%20%20%20COST_1%20%3D%20%7B'crude1'%3A%202.1%2C%20'crude2'%3A%202.1%2C%20'distbutane'%3A%200%2C%20'naphtha'%3A%204.18%2C%20'residue'%3A%200.6%2C%20'gasoil'%3A%202.04%7D%0A%20%20%20%20DEM_2%20%3D%20%7B'butane'%3A%2020000%2C%20'petrol'%3A%2040000%2C%20'diesel'%3A%2030000%2C%20'heating'%3A%2042000%7D%0A%20%20%20%20OCT%20%3D%20%7B'petbutane'%3A%20120%2C%20'reformate'%3A%20100%2C%20'petcrknaphtha'%3A%2074%7D%0A%20%20%20%20VAP%20%3D%20%7B'petbutane'%3A%2060%2C%20'reformate'%3A%202.6%2C%20'petcrknaphtha'%3A%204.1%7D%0A%20%20%20%20VOL%20%3D%20%7B'petbutane'%3A%20105%2C%20'reformate'%3A%203%2C%20'petcrknaphtha'%3A%2012%7D%0A%20%20%20%20SULF%20%3D%20%7B'dslgasoil'%3A%200.03%2C%20'dslcrknaphtha'%3A%200.12%2C%20'dslcrkgasoil'%3A%200.76%7D%0A%20%20%20%20REF%20%3D%20%7B'refbutane'%3A%200.15%2C%20'reformate'%3A%200.85%7D%0A%20%20%20%20CRACK%20%3D%20%7B'crknaphtha'%3A%200.4%2C%20'crkgasoil'%3A%200.35%7D%0A%20%20%20%20DIST%20%3D%20%7B('crude1'%2C%20'distbutane')%3A%200.03%2C%20('crude1'%2C%20'naphtha')%3A%200.15%2C%20('crude1'%2C%20'gasoil')%3A%200.4%2C%20('crude1'%2C%20'residue')%3A%200.15%2C%20('crude2'%2C%20'distbutane')%3A%200.05%2C%20('crude2'%2C%20'naphtha')%3A%200.2%2C%20('crude2'%2C%20'gasoil')%3A%200.35%2C%20('crude2'%2C%20'residue')%3A%200.1%7D%0A%20%20%20%20IHO%20%3D%20%7B'hogasoil'%2C%20'hocrknaphtha'%2C%20'hocrkgasoil'%7D%0A%20%20%20%20CAP%20%3D%20%7B'naphtha'%3A%2030000%2C%20'gasoil'%3A%2050000%2C%20'residue'%3A%2040000%7D%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20AVAIL_1%2C%0A%20%20%20%20%20%20%20%20CAP%2C%0A%20%20%20%20%20%20%20%20COST_1%2C%0A%20%20%20%20%20%20%20%20CRACK%2C%0A%20%20%20%20%20%20%20%20DEM_2%2C%0A%20%20%20%20%20%20%20%20DIST%2C%0A%20%20%20%20%20%20%20%20IHO%2C%0A%20%20%20%20%20%20%20%20OCT%2C%0A%20%20%20%20%20%20%20%20REF%2C%0A%20%20%20%20%20%20%20%20SULF%2C%0A%20%20%20%20%20%20%20%20VAP%2C%0A%20%20%20%20%20%20%20%20VOL%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20All%20data%20is%20injected%20via%20%60input_data%60%20with%20%60DATASOURCE%3D%22moselpy%3A%22%60.%20Results%20are%20returned%20in%20the%20output%20dict%3A%20crude%20usage%20(%60sol_crude%60)%20and%20final%20product%20quantities%20(%60sol_final%60).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20AVAIL_1%2C%0A%20%20%20%20CAP%2C%0A%20%20%20%20COST_1%2C%0A%20%20%20%20CRACK%2C%0A%20%20%20%20DEM_2%2C%0A%20%20%20%20DIST%2C%0A%20%20%20%20IHO%2C%0A%20%20%20%20MODELS%2C%0A%20%20%20%20OCT%2C%0A%20%20%20%20REF%2C%0A%20%20%20%20SULF%2C%0A%20%20%20%20VAP%2C%0A%20%20%20%20VOL%2C%0A%20%20%20%20mp%2C%0A)%3A%0A%20%20%20%20model_2%2C%20_output%20%3D%20mp.execute_model(f'%7BMODELS%7D%2Fa3refine_p.mos'%2C%20exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D%2C%20input_data%3D%7B'DEM'%3A%20DEM_2%2C%20'COST'%3A%20COST_1%2C%20'OCT'%3A%20OCT%2C%20'VAP'%3A%20VAP%2C%20'VOL'%3A%20VOL%2C%20'SULF'%3A%20SULF%2C%20'AVAIL'%3A%20AVAIL_1%2C%20'DIST'%3A%20DIST%2C%20'REF'%3A%20REF%2C%20'CRACK'%3A%20CRACK%2C%20'IHO'%3A%20IHO%2C%20'CAP'%3A%20CAP%7D)%0A%20%20%20%20obj_val_2%20%3D%20_output%5B'obj_val'%5D%0A%20%20%20%20sol_crude%20%3D%20_output%5B'sol_crude'%5D%0A%20%20%20%20sol_final%20%3D%20_output%5B'sol_final'%5D%0A%20%20%20%20print(f'Production%20cost%3A%20%24%7Bobj_val_2%3A%2C.0f%7D')%0A%20%20%20%20print()%0A%20%20%20%20print('Crude%20usage%20(barrels%2Fmonth)%3A')%0A%20%20%20%20for%20_c%2C%20_v%20in%20sorted(sol_crude.items())%3A%0A%20%20%20%20%20%20%20%20print(f'%20%20%7B_c%3A%3C10%7D%20%7B_v%3A%3E10%2C.0f%7D')%0A%20%20%20%20print()%0A%20%20%20%20print('Final%20products%20(barrels%2Fmonth)%3A')%0A%20%20%20%20for%20_p%2C%20_v%20in%20sorted(sol_final.items())%3A%0A%20%20%20%20%20%20%20%20print(f'%20%20%7B_p%3A%3C10%7D%20%7B_v%3A%3E10%2C.0f%7D')%0A%20%20%20%20return%20obj_val_2%2C%20sol_final%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20bar%20chart%20below%20shows%20the%20monthly%20output%20(barrels)%20for%20each%20final%20product%20-%20butane%2C%20petrol%2C%20diesel%2C%20and%20heating%20oil.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20obj_val_2%2C%20plt%2C%20sol_final)%3A%0A%20%20%20%20products%20%3D%20sorted(sol_final.keys())%0A%20%20%20%20values%20%3D%20%5Bsol_final%5Bp%5D%20for%20p%20in%20products%5D%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(6%2C%203))%0A%20%20%20%20_ax.bar(products%2C%20values%2C%20color%3D%5B'%23E53935'%2C%20'%23FB8C00'%2C%20'%2343A047'%2C%20'%231E88E5'%5D)%0A%20%20%20%20_ax.set_ylabel('Barrels%2Fmonth')%0A%20%20%20%20_ax.set_title(f'A-3%20Refinery%20-%20final%20product%20output%20(cost%20%24%7Bobj_val_2%20%2F%201000000.0%3A.2f%7DM)')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20A-4%20Cane%20sugar%20production%20(MIP)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20*Applications%20Book%20Chapter%20A%2C%20Example%204*%0A%0A%20%20%20%20%3E%20**Mosel%20model%3A**%20%5B%60a4sugar_p.mos%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Fblob%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fmining_process%2Fa4sugar_p.mos)%0A%0A%20%20%20%20Eleven%20wagon%20loads%20of%20sugar%20cane%20must%20be%20processed%20on%203%20production%20lines%20over%20at%20most%204%20time%20slots.%20Each%20wagon%20has%20a%20loss%20rate%20(kg%2Fhour)%3B%20the%20later%20a%20wagon%20is%20processed%2C%20the%20more%20sugar%20is%20lost%20through%20fermentation.%20Binary%20variable%20%24%5Ctext%7Bprocess%7D_%7Bw%2Cs%7D%20%3D%201%24%20if%20wagon%20%24w%24%20is%20assigned%20to%20slot%20%24s%24.%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cbegin%7Balign*%7D%0A%20%20%20%20%5Ctext%7Bminimize%7D%20%20%20%5Cquad%20%26%20%5Csum_%7Bw%2Cs%7D%20s%20%5Ccdot%20%5Ctext%7BDUR%7D%20%5Ccdot%20%5Ctext%7BLOSS%7D_w%20%5Ccdot%20%5Ctext%7Bprocess%7D_%7Bw%2Cs%7D%20%5C%5C%0A%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%5Cquad%20%26%20%5Csum_s%20%5Ctext%7Bprocess%7D_%7Bw%2Cs%7D%20%3D%201%20%5Cquad%20%5Cforall%5C%2C%20w%20%5Cquad%20%5Ctext%7B(each%20wagon%20in%20exactly%20one%20slot)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_w%20%5Ctext%7Bprocess%7D_%7Bw%2Cs%7D%20%5Cleq%203%20%5Cquad%20%5Cforall%5C%2C%20s%20%5Cquad%20%5Ctext%7B(3%20lines%20per%20slot)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Ctext%7Blife%20constraints%20per%20wagon%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Ctext%7Bprocess%7D_%7Bw%2Cs%7D%20%5Cin%20%5C%7B0%2C%201%5C%7D%0A%20%20%20%20%5Cend%7Balign*%7D%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Define%20the%20wagon%20fermentation%20loss%20rates%20and%20processing%20deadlines%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20plt)%3A%0A%20%20%20%20NW%2C%20NL%2C%20NS%20%3D%20(11%2C%203%2C%204)%0A%20%20%20%20LOSS%20%3D%20%5B43%2C%2026%2C%2037%2C%2028%2C%2013%2C%2054%2C%2062%2C%2049%2C%2019%2C%2028%2C%2030%5D%0A%20%20%20%20LIFE%20%3D%20%5B8%2C%208%2C%202%2C%208%2C%204%2C%208%2C%208%2C%208%2C%206%2C%208%2C%208%5D%0A%20%20%20%20DUR%20%3D%202%0A%20%20%20%20_fig%2C%20_axes%20%3D%20plt.subplots(1%2C%202%2C%20figsize%3D(9%2C%203))%0A%20%20%20%20_wagons%20%3D%20list(range(1%2C%20NW%20%2B%201))%0A%20%20%20%20_axes%5B0%5D.bar(_wagons%2C%20LOSS%2C%20color%3D'tomato')%0A%20%20%20%20_axes%5B0%5D.set_xlabel('Wagon')%0A%20%20%20%20_axes%5B0%5D.set_ylabel('Loss%20rate%20(kg%2Fh)')%0A%20%20%20%20_axes%5B0%5D.set_title(f'Fermentation%20loss%20rates%20%20(slot%20duration%3A%20%7BDUR%7D%20h)')%0A%20%20%20%20_axes%5B1%5D.bar(_wagons%2C%20LIFE%2C%20color%3D'steelblue')%0A%20%20%20%20_axes%5B1%5D.set_xlabel('Wagon')%0A%20%20%20%20_axes%5B1%5D.set_ylabel('Max%20delay%20(slots)')%0A%20%20%20%20_axes%5B1%5D.set_title('Processing%20deadlines')%0A%20%20%20%20plt.suptitle('A-4%20Sugar%3A%20input%20data')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%20DUR%2C%20LIFE%2C%20LOSS%2C%20NL%2C%20NS%2C%20NW%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Compile%20and%20solve%20the%20scheduling%20MIP%20with%20%60execute_model()%60%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(DUR%2C%20LIFE%2C%20LOSS%2C%20MODELS%2C%20NL%2C%20NS%2C%20NW%2C%20mp)%3A%0A%20%20%20%20model_3%2C%20_output%20%3D%20mp.execute_model(f'%7BMODELS%7D%2Fa4sugar_p.mos'%2C%20exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D%2C%20input_data%3D%7B'NW'%3A%20NW%2C%20'NL'%3A%20NL%2C%20'LOSS'%3A%20LOSS%2C%20'LIFE'%3A%20LIFE%2C%20'DUR'%3A%20DUR%7D)%0A%20%20%20%20obj_val_3%20%3D%20_output%5B'obj_val'%5D%0A%20%20%20%20sol_process%20%3D%20_output%5B'sol_process'%5D%0A%20%20%20%20schedule%20%3D%20%7Bs%3A%20%5Bw%20for%20w%20in%20range(1%2C%20NW%20%2B%201)%20if%20sol_process.get((w%2C%20s)%2C%200)%20%3E%200.5%5D%20for%20s%20in%20range(1%2C%20NS%20%2B%201)%7D%0A%20%20%20%20print(f'Total%20sugar%20loss%3A%20%7Bobj_val_3%3A.0f%7D%20kg')%0A%20%20%20%20print()%0A%20%20%20%20for%20_s%2C%20_wagons%20in%20schedule.items()%3A%0A%20%20%20%20%20%20%20%20print(f'Slot%20%7B_s%7D%3A%20wagons%20%7B_wagons%7D')%0A%20%20%20%20return%20obj_val_3%2C%20sol_process%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20np%2C%20obj_val_3%2C%20plt%2C%20sol_process)%3A%0A%20%20%20%20NW_1%2C%20NS_1%20%3D%20(11%2C%204)%0A%20%20%20%20grid%20%3D%20np.zeros((NW_1%2C%20NS_1))%0A%20%20%20%20for%20(w%2C%20_s)%2C%20_v%20in%20sol_process.items()%3A%0A%20%20%20%20%20%20%20%20if%20_v%20%3E%200.5%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20grid%5Bw%20-%201%2C%20_s%20-%201%5D%20%3D%201%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(6%2C%205))%0A%20%20%20%20_ax.imshow(grid%2C%20cmap%3D'Blues'%2C%20aspect%3D'auto'%2C%20vmin%3D0%2C%20vmax%3D1)%0A%20%20%20%20_ax.set_xticks(range(NS_1))%0A%20%20%20%20_ax.set_xticklabels(%5Bf'Slot%20%7Bs%7D'%20for%20s%20in%20range(1%2C%20NS_1%20%2B%201)%5D)%0A%20%20%20%20_ax.set_yticks(range(NW_1))%0A%20%20%20%20_ax.set_yticklabels(%5Bf'Wagon%20%7Bw%7D'%20for%20w%20in%20range(1%2C%20NW_1%20%2B%201)%5D)%0A%20%20%20%20_ax.set_title(f'A-4%20Sugar%20schedule%20-%20total%20loss%20%7Bobj_val_3%3A.0f%7D%20kg')%0A%20%20%20%20for%20(w%2C%20_s)%2C%20_v%20in%20sol_process.items()%3A%0A%20%20%20%20%20%20%20%20if%20_v%20%3E%200.5%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.text(_s%20-%201%2C%20w%20-%201%2C%20'%5Cu2713'%2C%20ha%3D'center'%2C%20va%3D'center'%2C%20color%3D'white'%2C%20fontsize%3D14%2C%20fontweight%3D'bold')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20A-5%20Opencast%20mining%20(MIP)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20*Applications%20Book%20Chapter%20A%2C%20Example%205*%0A%0A%20%20%20%20%3E%20**Mosel%20model%3A**%20%5B%60a5mine_p.mos%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Fblob%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fmining_process%2Fa5mine_p.mos)%0A%0A%20%20%20%20An%20opencast%20uranium%20mine%20has%2018%20blocks%20in%203%20levels%3A%208%20blocks%20in%20level%201%2C%206%20in%20level%202%2C%20and%204%20in%20level%203.%20To%20extract%20a%20block%20in%20level%202%20or%203%2C%20the%203%20blocks%20directly%20above%20it%20in%20the%20previous%20level%20must%20first%20be%20extracted.%20Each%20block%20has%20an%20extraction%20cost%20and%20a%20uranium%20value.%0A%0A%20%20%20%20%24%5Ctext%7BLEVEL23%7D%20%3D%20%5Ctext%7BNLO%7D..%5Ctext%7BNHI%7D%20%3D%209..18%24%20is%20the%20range%20of%20blocks%20subject%20to%20precedence%20constraints.%20%24%5Ctext%7BARC%7D_%7Bb%2Ci%7D%24%20gives%20the%20%24i%24-th%20predecessor%20block%20that%20must%20be%20extracted%20before%20block%20%24b%24.%20Binary%20variable%20%24%5Ctext%7Bextract%7D_b%20%3D%201%24%20if%20block%20%24b%24%20is%20excavated.%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cbegin%7Balign*%7D%0A%20%20%20%20%5Ctext%7Bmaximize%7D%20%20%20%5Cquad%20%26%20%5Csum_%7Bb%7D%20(%5Ctext%7BVALUE%7D_b%20-%20%5Ctext%7BCOST%7D_b)%20%5Ccdot%20%5Ctext%7Bextract%7D_b%20%5C%5C%0A%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%5Cquad%20%26%20%5Ctext%7Bextract%7D_b%20%5Cleq%20%5Ctext%7Bextract%7D_%7B%5Ctext%7BARC%7D_%7Bb%2Ci%7D%7D%20%5Cquad%20%5Cforall%5C%2C%20b%20%5Cin%20%5Ctext%7BLEVEL23%7D%2C%5C%20i%20%5Cin%20%5C%7B1%2C2%2C3%5C%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Ctext%7Bextract%7D_b%20%5Cin%20%5C%7B0%2C1%5C%7D%0A%20%20%20%20%5Cend%7Balign*%7D%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Define%20block%20extraction%20costs%2C%20uranium%20values%2C%20and%20the%20predecessor%20arc%20structure%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20mpatches%2C%20plt)%3A%0A%20%20%20%20NB%2C%20NLO%2C%20NHI%20%3D%20(18%2C%209%2C%2018)%0A%20%20%20%20COST_2%20%3D%20%5B100%2C%20100%2C%20100%2C%20100%2C%20100%2C%20100%2C%20100%2C%20100%2C%201000%2C%20200%2C%20200%2C%20200%2C%20200%2C%201000%2C%201000%2C%201000%2C%20300%2C%201000%5D%0A%20%20%20%20VALUE%20%3D%20%7B1%3A%20200%2C%207%3A%20300%2C%2010%3A%20500%2C%2012%3A%20200%2C%2017%3A%201000%2C%2018%3A%201200%7D%0A%20%20%20%20ARC%20%3D%20%7B(9%2C%201)%3A%201%2C%20(9%2C%202)%3A%202%2C%20(9%2C%203)%3A%203%2C%20(10%2C%201)%3A%202%2C%20(10%2C%202)%3A%203%2C%20(10%2C%203)%3A%204%2C%20(11%2C%201)%3A%203%2C%20(11%2C%202)%3A%204%2C%20(11%2C%203)%3A%205%2C%20(12%2C%201)%3A%204%2C%20(12%2C%202)%3A%205%2C%20(12%2C%203)%3A%206%2C%20(13%2C%201)%3A%205%2C%20(13%2C%202)%3A%206%2C%20(13%2C%203)%3A%207%2C%20(14%2C%201)%3A%206%2C%20(14%2C%202)%3A%207%2C%20(14%2C%203)%3A%208%2C%20(15%2C%201)%3A%209%2C%20(15%2C%202)%3A%2010%2C%20(15%2C%203)%3A%2011%2C%20(16%2C%201)%3A%2010%2C%20(16%2C%202)%3A%2011%2C%20(16%2C%203)%3A%2012%2C%20(17%2C%201)%3A%2011%2C%20(17%2C%202)%3A%2012%2C%20(17%2C%203)%3A%2013%2C%20(18%2C%201)%3A%2012%2C%20(18%2C%202)%3A%2013%2C%20(18%2C%203)%3A%2014%7D%0A%20%20%20%20WEIGHT%20%3D%2010000%0A%20%20%20%20_levels%20%3D%20%5B(1%2C%206)%2C%20(7%2C%2012)%2C%20(13%2C%2018)%5D%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(9%2C%204))%0A%20%20%20%20for%20_row%2C%20(_lo%2C%20_hi)%20in%20enumerate(_levels)%3A%0A%20%20%20%20%20%20%20%20for%20_col%2C%20_b%20in%20enumerate(range(_lo%2C%20_hi%20%2B%201))%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_v%20%3D%20VALUE.get(_b%2C%200)%0A%20%20%20%20%20%20%20%20%20%20%20%20_color%20%3D%20'%23FFE0B2'%20if%20_v%20%3E%200%20else%20'%23CFD8DC'%0A%20%20%20%20%20%20%20%20%20%20%20%20_rect%20%3D%20mpatches.FancyBboxPatch((_col%20-%200.45%2C%20-(_row%20%2B%200.45))%2C%200.9%2C%200.9%2C%20boxstyle%3D'round%2Cpad%3D0.05'%2C%20linewidth%3D1%2C%20edgecolor%3D'white'%2C%20facecolor%3D_color)%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.add_patch(_rect)%0A%20%20%20%20%20%20%20%20%20%20%20%20label%20%3D%20f'%7B_b%7D%5Cn%24%7B_v%3A%2C%7D'%20if%20_v%20%3E%200%20else%20str(_b)%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.text(_col%2C%20-_row%2C%20label%2C%20ha%3D'center'%2C%20va%3D'center'%2C%20fontsize%3D9%2C%20fontweight%3D'bold'%20if%20_v%20%3E%200%20else%20'normal')%0A%20%20%20%20_ax.set_xlim(-0.6%2C%205.6)%0A%20%20%20%20_ax.set_ylim(-2.6%2C%200.6)%0A%20%20%20%20_ax.set_yticks(%5B0%2C%20-1%2C%20-2%5D)%0A%20%20%20%20_ax.set_yticklabels(%5B'Level%201'%2C%20'Level%202'%2C%20'Level%203'%5D)%0A%20%20%20%20_ax.set_xticks(%5B%5D)%0A%20%20%20%20_ax.set_title('A-5%20Mine%20blocks%3A%20uranium%20value%20per%20block')%0A%20%20%20%20_ax.legend(handles%3D%5Bmpatches.Patch(color%3D'%23FFE0B2'%2C%20label%3D'Has%20value')%2C%20mpatches.Patch(color%3D'%23CFD8DC'%2C%20label%3D'No%20value')%5D%2C%20loc%3D'lower%20right')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%20ARC%2C%20COST_2%2C%20VALUE%2C%20WEIGHT%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Compile%20and%20run%20%60a5mine_p.mos%60%20with%20predecessor%20data%20passed%20from%20Python.%20The%20%60ARC%60%20dict%20maps%20each%20level-2%2F3%20block%20to%20its%20three%20required%20predecessor%20blocks%20using%20%60(block%2C%20position)%60%20tuple%20keys.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(ARC%2C%20COST_2%2C%20MODELS%2C%20VALUE%2C%20WEIGHT%2C%20mp)%3A%0A%20%20%20%20mp.compile_model(f'%7BMODELS%7D%2Fa5mine_p.mos'%2C%20f'%7BMODELS%7D%2Fa5mine_p.bim')%0A%20%20%20%20model_4%20%3D%20mp.load_model(f'%7BMODELS%7D%2Fa5mine_p.bim')%0A%20%20%20%20model_4.set_default_stream(mp.StreamType.OUTPUT%2C%20'null%3A')%0A%20%20%20%20out1%20%3D%20model_4.run(exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D%2C%20input_data%3D%7B'COST'%3A%20COST_2%2C%20'VALUE'%3A%20VALUE%2C%20'ARC'%3A%20ARC%2C%20'WEIGHT'%3A%20WEIGHT%7D)%0A%20%20%20%20if%20model_4.prob_status.is_solution_available%3A%0A%20%20%20%20%20%20%20%20obj_val_4%20%3D%20out1%5B'obj_val'%5D%0A%20%20%20%20%20%20%20%20sol_extract%20%3D%20out1%5B'sol_extract'%5D%0A%20%20%20%20%20%20%20%20_extracted%20%3D%20sorted(%5Bb%20for%20b%2C%20v%20in%20sol_extract.items()%20if%20v%20%3E%200.5%5D)%0A%20%20%20%20%20%20%20%20print(f'Total%20profit%3A%20%24%7Bobj_val_4%3A%2C.0f%7D')%0A%20%20%20%20%20%20%20%20print(f'Blocks%20extracted%3A%20%7B_extracted%7D')%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print(f'No%20solution%20found%20(%7Bmodel_4.prob_status%7D)')%0A%20%20%20%20return%20obj_val_4%2C%20sol_extract%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20mpatches%2C%20obj_val_4%2C%20plt%2C%20sol_extract)%3A%0A%20%20%20%20n_per_level%20%3D%206%0A%20%20%20%20_levels%20%3D%20%5B(1%2C%206)%2C%20(7%2C%2012)%2C%20(13%2C%2018)%5D%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(9%2C%204))%0A%20%20%20%20for%20_row%2C%20(_lo%2C%20_hi)%20in%20enumerate(_levels)%3A%0A%20%20%20%20%20%20%20%20for%20_col%2C%20_b%20in%20enumerate(range(_lo%2C%20_hi%20%2B%201))%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_extracted%20%3D%20sol_extract.get(_b%2C%200)%20%3E%200.5%0A%20%20%20%20%20%20%20%20%20%20%20%20_color%20%3D%20'%231565C0'%20if%20_extracted%20else%20'%23CFD8DC'%0A%20%20%20%20%20%20%20%20%20%20%20%20_rect%20%3D%20mpatches.FancyBboxPatch((_col%20-%200.45%2C%20-(_row%20%2B%200.45))%2C%200.9%2C%200.9%2C%20boxstyle%3D'round%2Cpad%3D0.05'%2C%20linewidth%3D1%2C%20edgecolor%3D'white'%2C%20facecolor%3D_color)%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.add_patch(_rect)%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.text(_col%2C%20-_row%2C%20str(_b)%2C%20ha%3D'center'%2C%20va%3D'center'%2C%20color%3D'white'%20if%20_extracted%20else%20'%23546E7A'%2C%20fontsize%3D11%2C%20fontweight%3D'bold')%0A%20%20%20%20_ax.set_xlim(-0.6%2C%20n_per_level%20-%200.4)%0A%20%20%20%20_ax.set_ylim(-2.6%2C%200.6)%0A%20%20%20%20_ax.set_yticks(%5B-0%2C%20-1%2C%20-2%5D)%0A%20%20%20%20_ax.set_yticklabels(%5B'Level%201'%2C%20'Level%202'%2C%20'Level%203'%5D)%0A%20%20%20%20_ax.set_xticks(%5B%5D)%0A%20%20%20%20_ax.set_title(f'A-5%20Mine%20extraction%20plan%20-%20total%20profit%20%24%7Bobj_val_4%3A%2C.0f%7D')%0A%20%20%20%20blue_patch%20%3D%20mpatches.Patch(color%3D'%231565C0'%2C%20label%3D'Extracted')%0A%20%20%20%20grey_patch%20%3D%20mpatches.Patch(color%3D'%23CFD8DC'%2C%20label%3D'Not%20extracted')%0A%20%20%20%20_ax.legend(handles%3D%5Bblue_patch%2C%20grey_patch%5D%2C%20loc%3D'lower%20right')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20A-6%20Electricity%20production%20(MIP)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20*Applications%20Book%20Chapter%20A%2C%20Example%206*%0A%0A%20%20%20%20%3E%20**Mosel%20model%3A**%20%5B%60a6electr_p.mos%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Fblob%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fmining_process%2Fa6electr_p.mos)%0A%0A%20%20%20%20Four%20types%20of%20power%20generators%20must%20cover%20daily%20electricity%20demand%20across%207%20time%20periods%2C%20plus%20a%2020%25%20security%20reserve.%20Each%20generator%20type%20has%20a%20minimum%20output%20(below%20which%20it%20cannot%20run)%20and%20a%20maximum%20capacity.%20Generators%20can%20only%20be%20started%2Fstopped%20at%20period%20boundaries.%20Integer%20variable%20%24%5Ctext%7Bwork%7D_%7Bp%2Ct%7D%24%20counts%20generators%20of%20type%20%24p%24%20running%20in%20period%20%24t%24%3B%20continuous%20%24%5Ctext%7Bpadd%7D_%7Bp%2Ct%7D%20%5Cgeq%200%24%20is%20extra%20output%20above%20the%20minimum.%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cbegin%7Balign*%7D%0A%20%20%20%20%5Ctext%7Bminimize%7D%20%20%20%5Cquad%20%26%20%5Csum_%7Bp%2Ct%7D%20%5Cleft(%20%5Ctext%7BCSTART%7D_p%20%5Ccdot%20%5Ctext%7Bstart%7D_%7Bp%2Ct%7D%20%2B%20%5Ctext%7BLEN%7D_t(%5Ctext%7BCMIN%7D_p%20%5Ccdot%20%5Ctext%7Bwork%7D_%7Bp%2Ct%7D%20%2B%20%5Ctext%7BCADD%7D_p%20%5Ccdot%20%5Ctext%7Bpadd%7D_%7Bp%2Ct%7D)%20%5Cright)%20%5C%5C%0A%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%5Cquad%20%26%20%5Ctext%7Bstart%7D_%7Bp%2Ct%7D%20%5Cgeq%20%5Ctext%7Bwork%7D_%7Bp%2Ct%7D%20-%20%5Ctext%7Bwork%7D_%7Bp%2Ct-1%7D%20%5Cquad%20%5Ctext%7B(start-up%20count)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_p%20(%5Ctext%7BPMIN%7D_p%20%5Ccdot%20%5Ctext%7Bwork%7D_%7Bp%2Ct%7D%20%2B%20%5Ctext%7Bpadd%7D_%7Bp%2Ct%7D)%20%5Cgeq%20%5Ctext%7BDEM%7D_t%20%5Cquad%20%5Ctext%7B(demand)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_p%20%5Ctext%7BPMAX%7D_p%20%5Ccdot%20%5Ctext%7Bwork%7D_%7Bp%2Ct%7D%20%5Cgeq%201.2%5C%2C%5Ctext%7BDEM%7D_t%20%5Cquad%20%5Ctext%7B(20%5C%25%20reserve)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Ctext%7Bwork%7D_%7Bp%2Ct%7D%20%5Cin%20%5Cmathbb%7BZ%7D_%7B%5Cgeq%200%7D%2C%5C%20%5Ctext%7Bwork%7D_%7Bp%2Ct%7D%20%5Cleq%20%5Ctext%7BAVAIL%7D_p%0A%20%20%20%20%5Cend%7Balign*%7D%0A%20%20%20%20%24%24%0A%0A%20%20%20%20The%20graphical%20output%20originally%20produced%20by%20%60a6electrg.mos%60%20is%20reproduced%20here%20as%20a%20Python%20visualization.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Define%20the%20four%20generator%20types%20and%20the%20hourly%20demand%20profile%20across%20seven%20time%20periods%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20plt)%3A%0A%20%20%20%20NT%2C%20NP%20%3D%20(7%2C%204)%0A%20%20%20%20LEN%20%3D%20%5B6%2C%203%2C%203%2C%202%2C%204%2C%204%2C%202%5D%0A%20%20%20%20DEM_3%20%3D%20%5B12000%2C%2032000%2C%2025000%2C%2036000%2C%2025000%2C%2030000%2C%2018000%5D%0A%20%20%20%20PMIN_1%20%3D%20%5B750%2C%201000%2C%201200%2C%201800%5D%0A%20%20%20%20PMAX_1%20%3D%20%5B1750%2C%201500%2C%202000%2C%203500%5D%0A%20%20%20%20CSTART%20%3D%20%5B5000%2C%201600%2C%202400%2C%201200%5D%0A%20%20%20%20CMIN%20%3D%20%5B2250%2C%201800%2C%203750%2C%204800%5D%0A%20%20%20%20CADD%20%3D%20%5B2.7%2C%202.2%2C%201.8%2C%203.8%5D%0A%20%20%20%20AVAIL_2%20%3D%20%5B10%2C%204%2C%208%2C%203%5D%0A%20%20%20%20cum_h%20%3D%20%5B0%5D%0A%20%20%20%20for%20_l%20in%20LEN%3A%0A%20%20%20%20%20%20%20%20cum_h.append(cum_h%5B-1%5D%20%2B%20_l)%0A%20%20%20%20dem_x%2C%20dem_y%20%3D%20(%5B%5D%2C%20%5B%5D)%0A%20%20%20%20for%20_t%20in%20range(NT)%3A%0A%20%20%20%20%20%20%20%20dem_x%20%3D%20dem_x%20%2B%20%5Bcum_h%5B_t%5D%2C%20cum_h%5B_t%20%2B%201%5D%5D%0A%20%20%20%20%20%20%20%20dem_y%20%3D%20dem_y%20%2B%20%5BDEM_3%5B_t%5D%2C%20DEM_3%5B_t%5D%5D%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(8%2C%203))%0A%20%20%20%20_ax.fill_between(dem_x%2C%20dem_y%2C%20alpha%3D0.25%2C%20color%3D'steelblue')%0A%20%20%20%20_ax.plot(dem_x%2C%20dem_y%2C%20color%3D'steelblue'%2C%20linewidth%3D2)%0A%20%20%20%20for%20_t%20in%20range(NT)%3A%0A%20%20%20%20%20%20%20%20_ax.text((cum_h%5B_t%5D%20%2B%20cum_h%5B_t%20%2B%201%5D)%20%2F%202%2C%20DEM_3%5B_t%5D%20%2B%20400%2C%20f'P%7B_t%20%2B%201%7D'%2C%20ha%3D'center'%2C%20fontsize%3D9)%0A%20%20%20%20_ax.set_xlabel('Hour%20of%20day')%0A%20%20%20%20_ax.set_ylabel('Demand%20(MW)')%0A%20%20%20%20_ax.set_title('A-6%20Electricity%3A%20demand%20profile')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%20AVAIL_2%2C%20CADD%2C%20CMIN%2C%20CSTART%2C%20DEM_3%2C%20LEN%2C%20NP%2C%20NT%2C%20PMAX_1%2C%20PMIN_1%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Compile%20and%20solve%20the%20electricity%20dispatch%20MIP%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20AVAIL_2%2C%0A%20%20%20%20CADD%2C%0A%20%20%20%20CMIN%2C%0A%20%20%20%20CSTART%2C%0A%20%20%20%20DEM_3%2C%0A%20%20%20%20LEN%2C%0A%20%20%20%20MODELS%2C%0A%20%20%20%20NP%2C%0A%20%20%20%20NT%2C%0A%20%20%20%20PMAX_1%2C%0A%20%20%20%20PMIN_1%2C%0A%20%20%20%20mp%2C%0A)%3A%0A%20%20%20%20mp.compile_model(f'%7BMODELS%7D%2Fa6electr_p.mos'%2C%20f'%7BMODELS%7D%2Fa6electr_p.bim')%0A%20%20%20%20model_5%20%3D%20mp.load_model(f'%7BMODELS%7D%2Fa6electr_p.bim')%0A%20%20%20%20_output%20%3D%20model_5.run(exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D%2C%20input_data%3D%7B'NT'%3A%20NT%2C%20'NP'%3A%20NP%2C%20'LEN'%3A%20LEN%2C%20'DEM'%3A%20DEM_3%2C%20'PMIN'%3A%20PMIN_1%2C%20'PMAX'%3A%20PMAX_1%2C%20'CSTART'%3A%20CSTART%2C%20'CMIN'%3A%20CMIN%2C%20'CADD'%3A%20CADD%2C%20'AVAIL'%3A%20AVAIL_2%7D)%0A%20%20%20%20obj_val_5%20%3D%20_output%5B'obj_val'%5D%0A%20%20%20%20sol_work%20%3D%20_output%5B'sol_work'%5D%0A%20%20%20%20sol_padd%20%3D%20_output%5B'sol_padd'%5D%0A%20%20%20%20print(f'Daily%20cost%3A%20%24%7Bobj_val_5%3A%2C.0f%7D')%0A%20%20%20%20print()%0A%20%20%20%20print(f'%7B'Period'%3A%3C8%7D'%2C%20end%3D'')%0A%20%20%20%20for%20_t%20in%20range(1%2C%20NT%20%2B%201)%3A%0A%20%20%20%20%20%20%20%20print(f'%7B_t%3A%3E8%7D'%2C%20end%3D'')%0A%20%20%20%20print()%0A%20%20%20%20print(f'%7B'Demand'%3A%3C8%7D'%2C%20end%3D'')%0A%20%20%20%20for%20_t%20in%20range(1%2C%20NT%20%2B%201)%3A%0A%20%20%20%20%20%20%20%20print(f'%7BDEM_3%5B_t%20-%201%5D%3A%3E8.0f%7D'%2C%20end%3D'')%0A%20%20%20%20print()%0A%20%20%20%20for%20_p%20in%20range(1%2C%20NP%20%2B%201)%3A%0A%20%20%20%20%20%20%20%20print(f'Type%20%7B_p%3A%3C3%7D'%2C%20end%3D'')%0A%20%20%20%20%20%20%20%20for%20_t%20in%20range(1%2C%20NT%20%2B%201)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20pwr%20%3D%20PMIN_1%5B_p%20-%201%5D%20*%20sol_work%5B_p%2C%20_t%5D%20%2B%20sol_padd%5B_p%2C%20_t%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f'%7Bpwr%3A%3E8.0f%7D'%2C%20end%3D'')%0A%20%20%20%20%20%20%20%20print()%0A%20%20%20%20return%20model_5%2C%20obj_val_5%2C%20sol_padd%2C%20sol_work%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20DEM_3%2C%0A%20%20%20%20LEN%2C%0A%20%20%20%20PMIN_1%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20mpatches%2C%0A%20%20%20%20np%2C%0A%20%20%20%20obj_val_5%2C%0A%20%20%20%20plt%2C%0A%20%20%20%20sol_padd%2C%0A%20%20%20%20sol_work%2C%0A)%3A%0A%20%20%20%20NT_1%2C%20NP_1%20%3D%20(7%2C%204)%0A%20%20%20%20type_labels%20%3D%20%5Bf'Type%20%7Bp%7D'%20for%20p%20in%20range(1%2C%20NP_1%20%2B%201)%5D%0A%20%20%20%20_colors%20%3D%20%5B'%231E88E5'%2C%20'%23FB8C00'%2C%20'%2343A047'%2C%20'%23E53935'%5D%0A%20%20%20%20cum_hours%20%3D%20%5B0%5D%0A%20%20%20%20for%20_l%20in%20LEN%3A%0A%20%20%20%20%20%20%20%20cum_hours.append(cum_hours%5B-1%5D%20%2B%20_l)%0A%20%20%20%20pwr_matrix%20%3D%20np.array(%5B%5BPMIN_1%5Bp%20-%201%5D%20*%20sol_work%5Bp%2C%20t%5D%20%2B%20sol_padd%5Bp%2C%20t%5D%20for%20t%20in%20range(1%2C%20NT_1%20%2B%201)%5D%20for%20p%20in%20range(1%2C%20NP_1%20%2B%201)%5D)%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(10%2C%205))%0A%20%20%20%20_bottom%20%3D%20np.zeros(NT_1)%0A%20%20%20%20for%20p_idx%20in%20range(NP_1)%3A%0A%20%20%20%20%20%20%20%20for%20_t%20in%20range(NT_1)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20x0%2C%20x1%20%3D%20(cum_hours%5B_t%5D%2C%20cum_hours%5B_t%20%2B%201%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20h%20%3D%20pwr_matrix%5Bp_idx%2C%20_t%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.fill_between(%5Bx0%2C%20x1%5D%2C%20%5B_bottom%5B_t%5D%2C%20_bottom%5B_t%5D%5D%2C%20%5B_bottom%5B_t%5D%20%2B%20h%2C%20_bottom%5B_t%5D%20%2B%20h%5D%2C%20color%3D_colors%5Bp_idx%5D%2C%20alpha%3D0.85)%0A%20%20%20%20%20%20%20%20%20%20%20%20_bottom%5B_t%5D%20%3D%20_bottom%5B_t%5D%20%2B%20h%0A%20%20%20%20demand_x%20%3D%20%5B%5D%0A%20%20%20%20demand_y%20%3D%20%5B%5D%0A%20%20%20%20for%20_t%20in%20range(1%2C%20NT_1%20%2B%201)%3A%0A%20%20%20%20%20%20%20%20demand_x%20%3D%20demand_x%20%2B%20%5Bcum_hours%5B_t%20-%201%5D%2C%20cum_hours%5B_t%5D%5D%0A%20%20%20%20%20%20%20%20demand_y%20%3D%20demand_y%20%2B%20%5BDEM_3%5B_t%20-%201%5D%2C%20DEM_3%5B_t%20-%201%5D%5D%0A%20%20%20%20_ax.step(demand_x%2C%20demand_y%2C%20where%3D'post'%2C%20color%3D'black'%2C%20linewidth%3D2%2C%20linestyle%3D'--'%2C%20label%3D'Demand')%0A%20%20%20%20reserve_x%20%3D%20%5B%5D%0A%20%20%20%20reserve_y%20%3D%20%5B%5D%0A%20%20%20%20for%20_t%20in%20range(1%2C%20NT_1%20%2B%201)%3A%0A%20%20%20%20%20%20%20%20reserve_x%20%3D%20reserve_x%20%2B%20%5Bcum_hours%5B_t%20-%201%5D%2C%20cum_hours%5B_t%5D%5D%0A%20%20%20%20%20%20%20%20reserve_y%20%3D%20reserve_y%20%2B%20%5B1.2%20*%20DEM_3%5B_t%20-%201%5D%2C%201.2%20*%20DEM_3%5B_t%20-%201%5D%5D%0A%20%20%20%20_ax.step(reserve_x%2C%20reserve_y%2C%20where%3D'post'%2C%20color%3D'red'%2C%20linewidth%3D1.5%2C%20linestyle%3D'%3A'%2C%20label%3D'20%25%20reserve')%0A%20%20%20%20_ax.set_xlabel('Hour%20of%20day')%0A%20%20%20%20_ax.set_ylabel('Power%20output%20(MW)')%0A%20%20%20%20_ax.set_title(f'A-6%20Electricity%20dispatch%20-%20daily%20cost%20%24%7Bobj_val_5%3A%2C.0f%7D')%0A%20%20%20%20legend_patches%20%3D%20%5Bmpatches.Patch(color%3D_colors%5Bi%5D%2C%20label%3Dtype_labels%5Bi%5D)%20for%20i%20in%20range(NP_1)%5D%0A%20%20%20%20legend_patches.append(plt.Line2D(%5B0%5D%2C%20%5B0%5D%2C%20color%3D'black'%2C%20linewidth%3D2%2C%20linestyle%3D'--'%2C%20label%3D'Demand'))%0A%20%20%20%20legend_patches.append(plt.Line2D(%5B0%5D%2C%20%5B0%5D%2C%20color%3D'red'%2C%20linewidth%3D1.5%2C%20linestyle%3D'%3A'%2C%20label%3D'20%25%20reserve'))%0A%20%20%20%20_ax.legend(handles%3Dlegend_patches%2C%20loc%3D'upper%20left')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%20NP_1%2C%20NT_1%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Sensitivity%20analysis%3A%20varying%20demand%0A%0A%20%20%20%20The%20loaded%20%60model%60%20can%20be%20run%20multiple%20times%20with%20different%20%60input_data%60%3B%20%60run()%60%20resets%20model%20state%20automatically%20before%20each%20execution.%20%60prob_status.is_solution_optimal%60%20confirms%20a%20valid%20solution%20before%20recording%20results.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20AVAIL_2%2C%0A%20%20%20%20CADD%2C%0A%20%20%20%20CMIN%2C%0A%20%20%20%20CSTART%2C%0A%20%20%20%20DEM_3%2C%0A%20%20%20%20LEN%2C%0A%20%20%20%20NP_1%2C%0A%20%20%20%20NT_1%2C%0A%20%20%20%20PMAX_1%2C%0A%20%20%20%20PMIN_1%2C%0A%20%20%20%20model_5%2C%0A%20%20%20%20pd%2C%0A)%3A%0A%20%20%20%20demand_factors%20%3D%20%5B0.7%2C%200.8%2C%200.9%2C%201.0%5D%0A%20%20%20%20rows%20%3D%20%5B%5D%0A%20%20%20%20for%20factor%20in%20demand_factors%3A%0A%20%20%20%20%20%20%20%20scaled_DEM%20%3D%20%7Bt%3A%20int(DEM_3%5Bt%20-%201%5D%20*%20factor)%20for%20t%20in%20range(1%2C%20NT_1%20%2B%201)%7D%0A%20%20%20%20%20%20%20%20out%20%3D%20model_5.run(exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D%2C%20input_data%3D%7B'NT'%3A%20NT_1%2C%20'NP'%3A%20NP_1%2C%20'LEN'%3A%20LEN%2C%20'DEM'%3A%20scaled_DEM%2C%20'PMIN'%3A%20PMIN_1%2C%20'PMAX'%3A%20PMAX_1%2C%20'CSTART'%3A%20CSTART%2C%20'CMIN'%3A%20CMIN%2C%20'CADD'%3A%20CADD%2C%20'AVAIL'%3A%20AVAIL_2%7D)%0A%20%20%20%20%20%20%20%20if%20model_5.prob_status.is_solution_optimal%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20rows.append(%7B'demand_factor'%3A%20factor%2C%20'daily_cost'%3A%20out%5B'obj_val'%5D%7D)%0A%20%20%20%20df%20%3D%20pd.DataFrame(rows).set_index('demand_factor')%0A%20%20%20%20print('A-6%20Daily%20cost%20vs.%20demand%20scaling%3A')%0A%20%20%20%20print(df.to_string(float_format%3D'%24%7B%3A%2C.0f%7D'.format))%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
e8489933979fb4f8f0f2937290799bde