Linear Optimization in SAP

In SAP is possible to create optimizing tools based on Simplex, Mixed Integer Linear Programming or other algorithms: the package GENIOS_SOLVER  in the software component SAP_BS_FND  (Application component CA-EPT-GEN) contains such kind of functions and programs.

Below there are some examples in which these methods are used and a Demo Report in SAP.

Let’s consider the following mathematical model:

We have three workflow types and 100 workflows of type 1, 50 of type 2 and 40 of type 3.

Each workflow of type 1 needs an amount of 10, type 2 needs an amount of 50 resource units and type 3 needs 80 units.

We have at most 2000 units of resources.

Each completed workflow of type 1 has a benefit of 15, workflow type 2 has a benefit of 10 and workflow type 3 has 40

Please be aware that the numbers I’m using here are hypothetical. In the real world most probably a consultant would measure those values and calculate the numbers. But please be aware that those numbers can and will change because of following reasons: the cost/benefit ration changes or the number of people who do the work. Perhaps the values will even change day by day and if this happens the linear constraints and the linear function changes, too, but the solver will still find a new solution.

Now we have to find a more mathematical formulation of above introduced model. So we have to maximize the objective function 15 x1 + 10 x2 + 40 x3 with regards to following constraints:

x1 <= 100, x2 <= 50 and x3 <= 40 and

10 x1 + 50 x2 + 80 x3 <= 2000.

The first inequalities defines the number of workflows of each type. The second inequality defines the resources constraints we have to complete workitems. If we calculate non-negative numbers for x1, x2 und x3, we could select a set of workflows and raise their priority according to the computed result.

To solve a linear optimization problem you can use LP solvers you can use commercial and free tools on the java stack, but there is also an ABAP tool.

(referred to https://blogs.sap.com/2011/10/04/operations-research-abap)

 

DATA:   lr_model       
TYPE REF TO cl_genios_model, "problem instance   
lr_objective   TYPE REF TO cl_genios_objective, "objective function   
lr_environment TYPE REF TO cl_genios_environment,   
lr_x1  TYPE REF TO cl_genios_variable, "variables   
lr_x2          TYPE REF TO cl_genios_variable,   
lr_x3          TYPE REF TO cl_genios_variable,   
lr_constraint  TYPE REF TO cl_genios_linearconstraint,   
lr_solver      TYPE REF TO cl_genios_solver,   
ls_result      TYPE genioss_solver_result,   
ls_variable    TYPE genioss_variable,   
lt_variables   TYPE geniost_variable,   
lv_value       TYPE genios_float,   
lv_name        TYPE string. "name of the variable 
lr_environment = cl_genios_environment=>get_environment( ).lr_model = lr_environment->create_model( 'PRIORIZATION' ). "model name * Do maximazation 
lr_objective = lr_model->create_objective(   if_genios_model_c=>gc_obj_maximization ). * We have three continous variables x1, x2 and x3 lr_x1 = lr_model->create_variable( iv_name = 'x1'   iv_type = if_genios_model_c=>gc_var_continuous ). lr_x2 = lr_model->create_variable( iv_name = 'x2'   iv_type = if_genios_model_c=>gc_var_continuous ). lr_x3 = lr_model->create_variable( iv_name = 'x3'   iv_type = if_genios_model_c=>gc_var_continuous ). * Define objective function as 15 x1 + 10 x2 + 40 x3 lr_objective->add_monom( io_variable = lr_x1 iv_coefficient = 15 ). lr_objective->add_monom( io_variable = lr_x2 iv_coefficient = 10 ). lr_objective->add_monom( io_variable = lr_x3 iv_coefficient = 40 ). * Definition of linear constraints: x1 <= 100 lr_constraint = lr_model->create_linearconstraint( iv_name = 'c1'   iv_type = if_genios_model_c=>gc_con_lessorequal iv_righthandside = 100 ). lr_constraint->add_monom( io_variable = lr_x1 iv_coefficient = 1 ). * Definition of linear constraints: x2 <= 50 lr_constraint = lr_model->create_linearconstraint( iv_name = 'c2'   iv_type = if_genios_model_c=>gc_con_lessorequal iv_righthandside = 50 ). lr_constraint->add_monom( io_variable = lr_x2 iv_coefficient = 1 ). * Definition of linear constraints: x3 <= 40 lr_constraint = lr_model->create_linearconstraint( iv_name = 'c3'   iv_type = if_genios_model_c=>gc_con_lessorequal iv_righthandside = 40 ). lr_constraint->add_monom( io_variable = lr_x3 iv_coefficient = 1 ). * Definition of linear constraints: 10 x1 + 50 x2 + 80 x3 <= 2000 lr_constraint = lr_model->create_linearconstraint( iv_name = 'c4'   iv_type = if_genios_model_c=>gc_con_lessorequal iv_righthandside = 2000 ). lr_constraint->add_monom( io_variable = lr_x1 iv_coefficient= 10 ). lr_constraint->add_monom( io_variable = lr_x2 iv_coefficient = 50 ). lr_constraint->add_monom( io_variable = lr_x3 iv_coefficient = 80 ). * Use the simplex solver lr_solver = lr_environment->create_solver( 'SIMP' ). lr_solver->load_model( lr_model ). "load the model into the solver ls_result = lr_solver->solve( ).   "solve the problem * Get the result IF ls_result-solution_status = if_genios_solver_result_c=>gc_optimal OR    ls_result-solution_status = if_genios_solver_result_c=>gc_abortfeasible.   lt_variables = lr_model->get_variables( ).   LOOP AT lt_variables INTO ls_variable.     lv_name  = ls_variable-variable_ref->gv_name.     lv_value = ls_variable-variable_ref->get_primalvalue( ).

WRITE: /,lv_name,' = ',lv_value.

ENDLOOP.

ENDIF.

lr_environment->destroy_solver( 'SIMP' ).

lr_environment->destroy_model( 'PRIORIZATION' ).

 

In SAP there is the standard report GENIOS_TEST_DEMO to be used as model in future developments.

*&---------------------------------------------------------------------* *& Report  GENIOS_TEST_DEMO *& *&---------------------------------------------------------------------* *&  Demo report to give some basic help on how to start with GENIOS *& *&  Problem: *& *&  min lv25*x11 + lv17*x12 + lv18*x13 + lv25*x21 + lv18*x22 + lv14*x23 *&  subject to *&    x11 + x12 + x13 <= 350 *&    x21 + x22 + x23 <= 600 *&          x11 + x21 >= 325 *&          x12 + x22 >= 300 *&          x13 + x23 >= 275 
*& *&-----------------------------------------------------*

REPORT  genios_test_demo.

PARAMETERS: SolverID type genios_solverid default 'SIMP'.

CONSTANTS: lc_modelname TYPE genios_name VALUE 'DEMO'. "SolverID TYPE genios_solverid VALUE 'SIMP'.

DATA: lo_env TYPE REF TO cl_genios_environment, lx_env TYPE REF TO cx_genios_environment, lv_msg TYPE string.

* 1) create a genios environment object lo_env = cl_genios_environment=>get_environment( ).

DATA: lo_model TYPE REF TO cl_genios_model. TRY. * 2) create a genios model (with a context-unique name) lo_model = lo_env->create_model( lc_modelname ). CATCH cx_genios_environment INTO lx_env. lv_msg = lx_env->get_text( ). WRITE: lv_msg, /. EXIT. ENDTRY.

* 3) fill the model with data * 3.1) create the objective object DATA: lo_obj TYPE REF TO cl_genios_objective. lo_obj = lo_model->create_objective( if_genios_model_c=>gc_obj_minimization ).

* 3.2) create the needed variables DATA: lo_x11 TYPE REF TO cl_genios_variable, lo_x12 TYPE REF TO cl_genios_variable, lo_x13 TYPE REF TO cl_genios_variable, lo_x21 TYPE REF TO cl_genios_variable, lo_x22 TYPE REF TO cl_genios_variable, lo_x23 TYPE REF TO cl_genios_variable. lo_x11 = lo_model->create_variable( iv_name = 'x11' iv_type = if_genios_model_c=>gc_var_continuous ). lo_x12 = lo_model->create_variable( iv_name = 'x12' iv_type = if_genios_model_c=>gc_var_continuous ). lo_x13 = lo_model->create_variable( iv_name = 'x13' iv_type = if_genios_model_c=>gc_var_continuous ). lo_x21 = lo_model->create_variable( iv_name = 'x21' iv_type = if_genios_model_c=>gc_var_continuous ). lo_x22 = lo_model->create_variable( iv_name = 'x22' iv_type = if_genios_model_c=>gc_var_continuous ). lo_x23 = lo_model->create_variable( iv_name = 'x23' iv_type = if_genios_model_c=>gc_var_continuous ).

* 3.3) add the monom for the objective function *      this is the coefficient for each variable in the objective function DATA: lv_25 TYPE genios_float, lv_17 TYPE genios_float, lv_18 TYPE genios_float, lv_14 TYPE genios_float. PERFORM compute_coefficient CHANGING lv_14 lv_17 lv_18 lv_25. lo_obj->add_monom( io_variable = lo_x11 iv_coefficient = lv_25 ). lo_obj->add_monom( io_variable = lo_x12 iv_coefficient = lv_17 ). lo_obj->add_monom( io_variable = lo_x13 iv_coefficient = lv_18 ). lo_obj->add_monom( io_variable = lo_x21 iv_coefficient = lv_25 ). lo_obj->add_monom( io_variable = lo_x22 iv_coefficient = lv_18 ). lo_obj->add_monom( io_variable = lo_x23 iv_coefficient = lv_14 ).

* 3.4) add the linear constraints with their monomes (coefficients for the variables DATA: lo_lin TYPE REF TO cl_genios_linearconstraint.

lo_lin = lo_model->create_linearconstraint( iv_name = 'i1' iv_type = if_genios_model_c=>gc_con_lessorequal iv_righthandside = 350 ). lo_lin->add_monom( io_variable = lo_x11 iv_coefficient = 1 ). lo_lin->add_monom( io_variable = lo_x12 iv_coefficient = 1 ). lo_lin->add_monom( io_variable = lo_x13 iv_coefficient = 1 ).

lo_lin = lo_model->create_linearconstraint( iv_name = 'i2' iv_type = if_genios_model_c=>gc_con_lessorequal iv_righthandside = 600 ). lo_lin->add_monom( io_variable = lo_x21 iv_coefficient = 1 ). lo_lin->add_monom( io_variable = lo_x22 iv_coefficient = 1 ). lo_lin->add_monom( io_variable = lo_x23 iv_coefficient = 1 ).

lo_lin = lo_model->create_linearconstraint( iv_name = 'j1' iv_type = if_genios_model_c=>gc_con_greaterorequal iv_righthandside = 325 ). lo_lin->add_monom( io_variable = lo_x11 iv_coefficient = 1 ). lo_lin->add_monom( io_variable = lo_x21 iv_coefficient = 1 ).

lo_lin = lo_model->create_linearconstraint( iv_name = 'j2' iv_type = if_genios_model_c=>gc_con_greaterorequal iv_righthandside = 300 ). lo_lin->add_monom( io_variable = lo_x12 iv_coefficient = 1 ). lo_lin->add_monom( io_variable = lo_x22 iv_coefficient = 1 ).

lo_lin = lo_model->create_linearconstraint( iv_name = 'j3' iv_type = if_genios_model_c=>gc_con_greaterorequal iv_righthandside = 275 ). lo_lin->add_monom( io_variable = lo_x13 iv_coefficient = 1 ). lo_lin->add_monom( io_variable = lo_x23 iv_coefficient = 1 ).

* 4) as the model is filled, we now create a solver with a ID out of tx genios_solver (in this case, the default SIMPLEX solver) DATA: lo_solver TYPE REF TO cl_genios_solver, lx_solver TYPE REF TO cx_genios_solver. TRY. lo_solver ?= lo_env->create_solver( SolverID ). CATCH cx_genios_environment INTO lx_env. lv_msg = lx_env->get_text( ). WRITE: lv_msg, /. EXIT. ENDTRY. * 4.1) load the model into the solver and solve it DATA: ls_result TYPE genioss_solver_result. TRY. lo_solver->load_model( lo_model ). ls_result = lo_solver->solve( ). CATCH cx_genios_solver INTO lx_solver. lv_msg = lx_solver->get_text( ). WRITE: lv_msg, /. EXIT. ENDTRY. * 4.2) evaluate the results DATA: lt_variables TYPE geniost_variable, ls_variable TYPE genioss_variable, lv_primalvalue TYPE genios_float, lv_name TYPE string, lv_index TYPE string. IF ( ls_result-solution_status = if_genios_solver_result_c=>gc_optimal OR ls_result-solution_status = if_genios_solver_result_c=>gc_abortfeasible ). * 4.3) found a solution => output the objective value as well as the variable values lv_primalvalue = lo_obj->get_value( ). WRITE: /,'Objective value: ',lv_primalvalue.              "#EC NOTEXT lt_variables = lo_model->get_variables( ). LOOP AT lt_variables INTO ls_variable. lv_primalvalue = 0. lv_name = ls_variable-variable_ref->gv_name. lv_index = ls_variable-variable_index. lv_primalvalue = ls_variable-variable_ref->get_primalvalue( ). WRITE: /,lv_name,'[',lv_index,'] = ',lv_primalvalue. ENDLOOP. ENDIF.

* 5) some cleanup IF ( lo_env IS BOUND ). lo_env->destroy_solver( SolverID ). lo_env->destroy_model( lc_modelname ). ENDIF.

*&---------------------------------------------------------------------* *&      Form  compute_coefficient *&---------------------------------------------------------------------* *       helper to compute the distance costs *----------------------------------------------------------------------* *----------------------------------------------------------------------* FORM compute_coefficient CHANGING lv_14 TYPE genios_float lv_17 TYPE genios_float lv_18 TYPE genios_float lv_25 TYPE genios_float.

CONSTANTS: lc_f TYPE i VALUE 90.

lv_25 = '2.5' * lc_f / 1000.

lv_17 = '1.7' * lc_f / 1000.

lv_18 = '1.8' * lc_f / 1000.

lv_14 = '1.4' * lc_f / 1000.

ENDFORM.                    "compute_coefficient

The  package contains the listed objects and it would be nice to create an open Function module that helps, once the matrix constraints and objectives are available, to execute the algorithms. If I’ll do it I will publish it, if somebody will do it, it would appreciated to have some info about it in this post.

GENIOS_TEST_DEMO               GENIOS: Demo report for a basic solver run

LGENIOS_SOLVER_MAIN$01

LGENIOS_SOLVER_MAINTOP

LGENIOS_SOLVER_MAINU01         GENIOSF_SHLP_EXIT_SOLVER_CLS

LGENIOS_SOLVER_MAINUXX

LGENIOS_SOLVER_MILP_DATA$01

LGENIOS_SOLVER_MILP_DATA$02

LGENIOS_SOLVER_MILP_DATA$03

LGENIOS_SOLVER_MILP_DATATOP

LGENIOS_SOLVER_MILP_DATAU01    GENIOSF_SOLVER_MILP_DOWNLOAD

LGENIOS_SOLVER_MILP_DATAU02    GENIOSF_SOLVER_MILP_UPLOAD

LGENIOS_SOLVER_MILP_DATAU03    GENIOSF_SOLVER_MILP_SOLVER_EXC

LGENIOS_SOLVER_MILP_DATAUXX

LGENIOS_SOLVER_MILP_DATAV01

LGENIOS_SOLVER_MILP_DATAV02

LGENIOS_SOLVER$01

LGENIOS_SOLVER$02

LGENIOS_SOLVERF00

LGENIOS_SOLVERI00

LGENIOS_SOLVERT00

LGENIOS_SOLVERTOP

LGENIOS_SOLVERU01              TABLEFRAME_GENIOS_SOLVER

LGENIOS_SOLVERU02              TABLEPROC_GENIOS_SOLVER

LGENIOS_SOLVERUXX

LGENIOSF_CUST$01

LGENIOSF_CUST$02

LGENIOSF_CUSTF00

LGENIOSF_CUSTI00

LGENIOSF_CUSTT00

LGENIOSF_CUSTTOP

LGENIOSF_CUSTU01               TABLEFRAME_GENIOSF_CUST

LGENIOSF_CUSTU02               TABLEPROC_GENIOSF_CUST

LGENIOSF_CUSTUXX

SAPLGENIOS_SOLVER              Extended Table Maintenance (Generated)

SAPLGENIOS_SOLVER_MAIN         GENIOS: main solver functions

SAPLGENIOS_SOLVER_MILP_DATA    GENIOS: data functions for the milp solv

SAPLGENIOSF_CUST               Extended Table Maintenance (Generated)

 

Some other typical problems can be solved through linear programming.

Product Mix Example

Imagine that you manage a factory that produces four different types of wood paneling. Each type of paneling is made by gluing and pressing together a different mixture of pine and oak chips. The following table summarizes the required amount of gluing, pressing, and mixture of wood chips required to produce a pallet of 50 units of each type of paneling:

 

Resources Required per Pallet of Paneling Type
Tahoe Pacific Savannah Aspen
Glue (quarts) 50 50 100 50
Pressing (hours) 5 15 10 5
Pine chips (pounds) 500 400 300 200
Oak chips (pounds) 500 750 250 500

 

In the next production cycle, you have 5,800 quarts of glue; 730 hours of pressing capacity; 29,200 pounds of pine chips; and 60,500 pounds of oak chips available. Further assume that each pallet of Tahoe, Pacific, Savannah, and Aspen panels can be sold for profits of $450, $1,150, $800, and $400, respectively.

Writing the Formulas

Before we implement this problem statement in either Excel or Visual Basic, let’s write out formulas corresponding to the verbal description above. If we temporarily use the symbol X1 for the number of Tahoe pallets produced, X2 for the number of Pacific pallets produced, and X3 for the number of Savannah pallets produced, and X4 for the number of Aspen pallets produced, the objective (calculating total profit) is:

Maximize: 450 X1 + 1150 X2 + 800 X3 + 400 X4

A pallet of each type of panel requires a certain amount of glue, pressing, pine chips, and oak chips. The amount of resources used (calculated by the left hand side of each constraint) depends on the mix of products built, and we have a limited amount of each type of resource available (corresponding to the constraint right hand side values).  The constraints for this problem are expressed as follows:

Subject to:

50 X1 + 50 X2 + 100 X3 + 50 X4 <= 5800 (Glue) 5 X1 + 15 X2 + 10 X3 + 5 X4 <= 730 (Pressing) 500 X1 + 400 X2 + 300 X3 + 200 X4 <= 29200 (Pine chips) 500 X1 + 750 X2 + 250 X3 + 500 X4 <= 60500 (Oak chips)

Since the number of products built cannot be negative, we’ll also have non-negativity conditions on the variables:

X1, X2, X3, X4 >= 0.

 

 

Chemical Engineering Example

One more difficult problem is shown below and affects the Optimization in Chemical Engineering (the example comes from McMaster University Department of Chemical Engineering).

 

If you think this post is useful please share it on your social forum (below the links), to share this information.
Thanks!!!!

SAP Finite Capacity Scheduling

The finite capacity scheduling is not available in SAP,  the unique functionality is the capacity leveling that could help to smooth overloads.

In this post, some attempts to obtain finite
capacity scheduling inside SAP are shown: they are based on changes of standard SAP program and custom programs.

What I thought many years ago is that I could put in relation the scheduling and the capacity reports: they are both available in SAP, but they are not linked, that’s why the scheduling is based on infinite capacity.

What I will show below is the list of programs and tables to modify or create.

The model of course is basic and can be improved, it allow a finite capacity check during the creation of each planner order (if needed also in production order, in the planned order created directly in the sales order, etc.).

First of all there is a table in which you put the work centers to be considered in finite capacity check (for instance only bottlenecks, some WC, all, etc.), material of course must be activated and there is also a the inter-setup table (setup depending on material and the couple previous/subsequent work centers).

 

Also a video is available to show a video demo. 

 

pdf explanation of working finite-capacity-check.pdf (121 downloads)

List of programs.zip (97 downloads)
  • The function group  /PRCF/GFWI (must be renamed in Z…GFWI) contains all the custom functions
  • the FM fase_shift contains all the criteria to individuate new dates taking in account the load of the work center. It is used in the elementary SAP standard forms in which are calculated times (starting/ending) in the scheduling
  • the FM bom_check_sch takes in account (if needed) also the scheduling depending on the availability of components. It is activated by the table CHKBOM. I suggest to start without this kind of check.
  • the report /PRCF/SCHED (to be renamed starting with “Z” or “Y”)  is the one shown in the video demo.

If you think this post is useful please share it on your social forum (below the links), to share this information.
Thanks!!!!