Contract Allocation

https://img.shields.io/badge/AIMMS_24.2-ZIP:_Contract_Alocation-blue https://img.shields.io/badge/AIMMS_24.2-Github:_Contract_Alocation-blue https://img.shields.io/badge/AIMMS_Community-Forum-yellow ../../_images/project.gif

Story

In this model we have a set of contracts, where every contract represents an amount of commodity that has to be supplied. The objective is to determine which of the producers will take care of which contract such that the total costs are minimal, under the following conditions:

  • The demand for every contract is met.

  • The amount supplied by each producer does not exceed the total amount available for supply.

  • If a producer supplies a part of a contract then this contribution has a given minimal size.

  • There is a minimal number of suppliers for every contract.

  • The total cost associated with all the deliveries is minimal.

Mathematical Model

This AIMMS project illustrates the use of a semi-continuous variable. A semi-continuous variable is either zero or within a certain range. This type of variables can be used in conditions like, whenever there is a transport this transport has a minimum size.

Contract Allocation Problem

Sets and indices:

\(P\), \(p \in P\)

Producers

\(C\), \(c \in C\)

Contracts

Parameters:

\(M_{p} \in \mathbb{R_{+}}\)

minimal delivery

\(A_{p} \in \mathbb{R_{+}}\)

available capacity

\(S_{c} \in \mathbb{R_{+}}\)

contract size

\(N_{c} \in \mathbb{R_{+}}\)

minimal number of contributors

\(T_{p,c} \in \mathbb{R_{+}}\)

delivery cost by p for c

Variables:

\(X_{p,c} \in \{0\} \cup \{M_{p}..10000\}\)

amount of commodity delivered by p to c

\(Y_{p,c} \in \{0..1\}\)

p produce to c

Constraints:

1

\(\forall p: \sum_c X_{p,c} \leq A_{p}\)

production capacity for p

2

\(\forall c: \sum_p X_{p,c} \geq S_{c}\)

demand fulfillment for c

3

\(\forall c: \sum_p X_{p,c} \geq N_{c}\)

minimal number of contributors to c

4

\(\forall p, c: X_{p,c} \geq M_{p} * Y_{p,c}\)

if p delivers to c

Minimize:

\(\sum_{p,c} T_{p,c} * X_{p,c}\)

The number of matches

Language

In this example, there are two main ways to import data: by a custom Excel file, and by a pre-defined Excel which is currently on the project’s main directory. You can choose which one to use on the inputs page through the import dialog page.

For the default data import, you will be importing 10 northwestern states for the contracts and 5 cities from that region for the producers. You can add more data freely without changing the sheets structure.

Procedure pr_importExcelData

This procedure will add and read the xml mapping available. Take a look at Mappings/inputs.xml.

 1dex::AddMapping(
 2   mappingName :  "inputs",
 3   mappingFile :  "Mappings/inputs.xml");
 4
 5dex::ReadFromFile(
 6   dataFile         :  "DefaultData.xlsx",
 7   mappingName      :  "inputs",
 8   emptyIdentifiers :  1,
 9   emptySets        :  1,
10   resetCounters    :  1);
11
12ep_actualContract := first(i_contract);
13ep_actualProducer := first(i_producer);

For the custom Excel file import, you can either copy the structure from the default data Excel or download the template file. Add your data, and then, use the upload button to select the Excel to import.

Procedure pr_uploadFile

This procedure will make you select a Excel file on your computer to import.

 1block ! import a custom Excel file
 2   ! we store the location of the file in string parameter UploadLocation
 3   UploadLocation := webui::GetIOFilePath(FileLocation);
 4
 5   dex::AddMapping("inputs", "Mappings/inputs.xml");
 6
 7   if dex::ReadFromFile(
 8      dataFile         :  UploadLocation,
 9      mappingName      :  "inputs",
10      emptyIdentifiers :  1,
11      emptySets        :  1,
12      resetCounters    :  1)
13   then
14      ! if successful, statusCode is set to 'OK' which will trigger the WebUI to show the message below in a grey box
15      StatusCode := webui::ReturnStatusCode('OK');
16
17      ! displaying the status message, and logging it in the WebUI messages
18      StatusDescription := "File was uploaded and read successfully";
19
20      FileDelete(UploadLocation);
21   endif;
22
23onerror ep_err do
24   ! setting the statusCode to 'ERROR'
25   statusCode := webui::ReturnStatusCode('ERROR');
26
27   !displaying a custom error message
28   statusDescription := "Error when reading file " + errh::Message( ep_err );
29   errh::MarkAsHandled(ep_err) ;
30
31   FileDelete(UploadLocation);
32endblock;

We also use create a page action and a dialog for users to export the results to several different DEX supported formats (Excel, JSON, CSV, etc.). You can add more identifiers to be exported by using the DEX annotations.

Procedure pr_openExportPage

This procedure will generate all the possible mappings in DEX based on current identifier DEX annotations. Details on how to setup annotations can be found here. It will also initialize identifiers used in our dialog and open that dialog page.

 1!Generating all possible mappings
 2dex::GenerateDatasetMappings();
 3
 4!Selecting the Excel mapping as initial value
 5if not ep_selectedMapping then
 6   ep_selectedMapping
 7   :=  First(i_generatedMappings | FindString(
 8            SearchString  :  i_generatedMappings,
 9            Key           :  "excel",
10            CaseSensitive :  0,
11            WordOnly      :  0,
12            IgnoreWhite   :  0));
13endif;
14
15!Defining Dialog and actions - Only done required
16s_actions:= data { Done };
17ep_pageId := 'export_page';
18
19!Opening dialog page - no action on done - webui::NoOp1 does nothing
20webui::OpenDialogPage(
21   pageId  :  ep_pageId,
22   title   :  "Export Data",
23   actions :  s_actions,
24   onDone  :  'webui::NoOp1');
Procedure pr_exportExcelData

This procedure will write the file and provide it for download using the download widget.

 1! we want to download a file
 2sp_out_fileLocation := sp_FileName;
 3
 4! we store the location of the file in string parameter FinalLocation
 5sp_loc_FinalLocation := webui::GetIOFilePath(sp_out_fileLocation);
 6
 7! writing the output file locally
 8dex::WriteToFile(
 9   dataFile    :  sp_loc_FinalLocation,
10   mappingName :  ep_selectedMapping,
11   pretty      :  1);
12
13! checking if the previous write statement was successful or not
14if FileExists(sp_loc_FinalLocation) then
15
16   ! if successful, statusCode is set to 'CREATED' which will trigger the download widget to show the Get button
17   p_out_statusCode := webui::ReturnStatusCode('CREATED');
18   ! displaying the status message as Ready to download exported data! instead of the default "File ready to download"
19   sp_out_statusDescription := "Ready to download exported data!";
20
21else    !if previous write statement was not successful
22
23   ! setting the statusCode to 'ERROR' and the download widget will not show the Get button anymore
24   p_out_statusCode := webui::ReturnStatusCode('ERROR');
25   !displaying a custom error message
26   sp_out_statusDescription := "Something went wrong when creating the file.";
27
28endif;

See also

To understand in depth check out DEX documentation.

WebUI Features

On input page, if you click around the graphs, a highlighted cell will appear identifying the last clicked element. The results are displayed in a combination chart (stacked bar chart).

The following WebUI features are used:

UI Styling

Below there are the css files you will find with comments on what they change.

 1:root {
 2/*---------------------------------------------------------------------
 3      COLORS
 4----------------------------------------------------------------------*/
 5--primary: #3DDAB4;
 6--primaryDark: #00B569;
 7--primary90Transparent: #3ddab33b;
 8
 9
10--bg_app-logo: 15px 50% / 30px 30px no-repeat url(/app-resources/resources/images/budgeting.png); /*app logo*/
11--spacing_app-logo_width: 45px;
12--color_border_app-header-divider: var(--primaryDark); /*line color after header*/
13--color_bg_app-canvas: url(/app-resources/resources/images/RightBackground.png) rgb(249, 249, 249) no-repeat left/contain; /*background color*/
14--border_widget-header: 1px solid var(--primaryDark); /*line color after widget header*/
15
16--color_bg_workflow_current: var(--primaryDark); /*bg color when step is selected*/
17--color_workflow_active: var(--primaryDark); /*font and icon color when step is active*/
18
19--color_bg_button_primary: var(--primaryDark);
20--color_bg_button_primary_hover: var(--primary);
21--color_text_edit-select-link: var(--primaryDark);
22--color_text_edit-select-link_hover:  var(--primary);
23
24/*---------------------------------------------------------------------
25      WORKFLOW
26----------------------------------------------------------------------*/
27/* Header text*/
28--color_workflow-header: #505767;
29
30/* Step background and content (text, icon) colors for the 4 states*/
31/*current + current with error*/
32--color_bg_workflow_current: var(--primaryDark);
33--color_workflow_current: var(--color_text_inverted);
34--color_bg_workflow_error-current: #d1454b;
35
36/*active*/
37--color_bg_workflow_active: #e6edff;
38--color_workflow_active: var(--primaryDark);
39
40/*inactive*/
41--color_bg_workflow_inactive: #dde0e8;
42--color_workflow_inactive: #b0b5c2;
43
44/*error*/
45--color_bg_workflow_error: #f9e9e9;
46--color_workflow_error: #d1454b;
47
48/* Child indentation, border colors */
49--spacing_workflow-child-indent: 1rem;
50--color_workflow-item-divider: var(--primaryDark);
51
52/* Icon background, border, for non-error state */
53--color_bg_workflow-icon: #ffffff;
54--color_workflow-icon-border: var(--primaryDark);
55}
 1.annotation-bkg-cell {
 2   background: var(--primary90Transparent);
 3}
 4
 5.annotation-bkg-cell-default {
 6   background: var(--primary90Transparent);
 7}
 8
 9.annotation-bkg-cell-default input{
10   color: transparent;
11}
12
13.annotation-reach-maximum {
14   background: rgba(255, 0, 0, 0.438);
15}
16
17.annotation-reach-minimum {
18   background: rgba(255, 255, 0, 0.438);
19}
20
21.annotation-between {
22   background: rgba(0, 128, 0, 0.438);
23}
 1/*Change table default text color*/
 2.tag-table .grid-viewport .cell.flag-default,
 3html:not(.using-touch) .tag-table .grid-viewport .cell.flag-default {
 4   color: white;
 5}
 6
 7/*Centering cells*/
 8.tag-table .cell.flag-string .cell-wrapper,
 9.tag-table .cell.flag-number input,
10.tag-table .cell.flag-string input{
11   text-align: center;
12}

Minimal Requirements

AIMMS Community license is sufficient for working with this example.

Release Notes

v1.4 (27/02/2024)

Upgrading AIMMS version, updating theme and fixing Default Data import.

v1.3 (09/08/2023)

Correcting download procedure, adding new options when importing data.

v1.2 (15/06/2023)

Updated to 4.95 and added dependent styling using annotation on Results page.

v1.1 (15/05/2023)

Updated to 4.94 and improved Input page for better UX flow.

v1.0 (17/03/2023)

First logged version with the new workflow structure and colors.