| Quintiq file version 2.0 | 
| #parent: #root | 
| Method SetKPILowerBounds ( | 
|   CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program, | 
|   const RunContextForCapacityPlanning runcontext, | 
|   Boolean scopefrompreprod | 
| ) const | 
| { | 
|   TextBody: | 
|   [* | 
|     if ( runcontext.IsMetaIteration() and not this.IsFullPlanMetaPriorFocus() )  | 
|     { | 
|       message := '';  | 
|       debuginfo( ifexpr(  scopefrompreprod, 'Also fixing focus level because scope produced by pre-production selector', '' ) );   | 
|       rcm := this.GetRunContextMeta();  | 
|       if ( rcm.OptionCPLEXFixModelBoundsForGoals() )  | 
|       { | 
|         size := counter(  this, MacroPlan.StrategyMacroPlan.StrategyLevelMacroPlan, slm, true, slm.Level() >= 1 ); // exclude 0 level for slack | 
|         bounds := RealVector::Construct( size );  | 
|         usage := BooleanVector::Construct( size );  | 
|          | 
|         traverse( this,  | 
|                   MacroPlan.StrategyMacroPlan.StrategyLevelMacroPlan,  | 
|                   level,  | 
|                   1 <= level.Level()  | 
|                   and level.Level() <= this.FocusLevel() )  | 
|         { | 
|          level_TotalBoundFromPlan := program.RetrieveReal( 'collect_values_model_TotalBoundFromPlan' + [String] level.Level() ); // workaround because cannot write to attribute  | 
|            | 
|           // workaround for not being able to use attributes  | 
|           level_SetCPLEXBoundFromPlan := program.RetrieveReal( 'collect_values_model_SetCPLEXBoundFromPlan' + [String] level.Level() ) > 0.5;  | 
|           level_TotalBoundFromPlan := program.RetrieveReal( 'collect_values_model_TotalBoundFromPlan' + [String] level.Level() );  | 
|            | 
|           bounds.Set( level.Level() - 1, level_TotalBoundFromPlan );  | 
|           if ( level_SetCPLEXBoundFromPlan and level.Level() < this.FocusLevel() + ifexpr(  scopefrompreprod or runcontext.UseCampaignSequenceOptimizer(), 1, 0 ) ) // if scope is made by pre production selector then we also fix focus level  | 
|           {                                                                                                                                                         // for campaign sequence optimizer we also fix, because the campaign level gets reoptimized each time | 
|             message := message +  'Setting bound for level=' +[String] level.Level() +  'kpi >= '+ [String]  level_TotalBoundFromPlan + String::NewLine();  | 
|              | 
|             usage.Set( level.Level() - 1, true );  | 
|             goalvar := program.GoalForLevelVariables().Get( level );  | 
|             totallotsizevar := program.TotalLotSizeVariables().Get();  | 
|             totalspipcapvar := program.TotalStockingPointCapacityVariables().Get();  | 
|             totalblendingvar := program.TotalBlendingVariables().Get();  | 
|      | 
|             constr := program.GoalLevelBoundForMetaConstraints().New( level );  | 
|             constr.Sense( '>=' );  | 
|             constr.RHSValue( level_TotalBoundFromPlan );  | 
|             if ( level.NeedsMetaGoalSlack( this.FocusLevel() ) )  | 
|             { | 
|                | 
|               goalslackvar := program.GoalLevelSlackVariables().Get( level );  | 
|               scalefactorforslackdefconstraint := this.ScaleConstraintTerm( typeofexpression( goalslackvar ), typeof( MPTotalSlackConstraint ) ); | 
|               goalslackvar.UpperBound( 0.01 );  | 
|               constr.NewTerm( 1.0, program.GoalLevelSlackVariables().Get( level ) ); // deliberately omitting scale factor. | 
|               slackdefconstraint := program.TotalSlackConstraints().Get();  | 
|               slackdefconstraint.NewTerm( -1.0 * scalefactorforslackdefconstraint, goalslackvar );  | 
|             } | 
|              | 
|             this.FilterCPLEXNoise( constr, 1.0 ); // this constraint is excluded from the parallel init, so need to do the filtering separate | 
|             goalcons := program.GoalConstraints().Get( level ); | 
|             traverse( goalcons.Terms(),  | 
|                       Elements,  | 
|                       t,  | 
|                       not t.Variable() = goalvar | 
|                       and not typeofexpression( t.Variable() ) = typeof (MPGoalLevelSlackVariable ) | 
|                       and not typeofexpression( t.Variable() ) = typeof( MPTotalStockingPointCapacityMetaVariable ) ) | 
|             { | 
|               if ( t.Variable() = totallotsizevar )  | 
|               { | 
|                 c := program.UpperBoundForTotalLotSizeMetaConstraints().New(); | 
|                 value_collect_values_model_TotalLotSizeVariables := program.RetrieveReal( 'collect_values_model_TotalLotSizeVariables' );  // workaround for attribute  | 
|                 c.RHSValue( value_collect_values_model_TotalLotSizeVariables );  | 
|                 c.Sense( '<=' );  | 
|                 c.NewTerm( 1.0, totallotsizevar );  | 
|                 this.FilterCPLEXNoise( c, 1.0 ); // this constraint is excluded from the parallel init, so need to do the filtering separate | 
|                 message := message + 'Setting upper bound total lot size = ' + [String] c.RHSValue() + String::NewLine();  | 
|               } | 
|               else if ( t.Variable() = totalspipcapvar ) | 
|               { | 
|                 totalstockingpointviolation := program.RetrieveReal( 'collect_values_model_TotalStockingPointCapacityVariables' ); | 
|                 c := program.UpperBoundForTotalSPIPCapacityMetaConstraints().New();  | 
|                 c.RHSValue( totalstockingpointviolation );  | 
|                 c.Sense( '<=' );  | 
|                 c.NewTerm( 1.0, totalspipcapvar );  | 
|                 this.FilterCPLEXNoise( c, 1.0 ); // this constraint is excluded from the parallel init, so need to do the filtering separate | 
|                 message := message + 'Setting upper bound for spip cap variable = ' + [String] c.RHSValue() + String::NewLine();   | 
|               } | 
|               else if ( t.Variable() = totalblendingvar )  | 
|               { | 
|                 value_collectvalues_model_TotalBlendingViolation := program.RetrieveReal( 'collectvalues_model_TotalBlendingViolation' );  | 
|                 c := program.UpperBoundForTotalBlendingMetaConstraints().New();  | 
|                 c.RHSValue( value_collectvalues_model_TotalBlendingViolation );  | 
|                 c.Sense( '<=' );  | 
|                 c.NewTerm( 1.0, totalblendingvar );  | 
|                 this.FilterCPLEXNoise( c, 1.0 ); // this constraint is excluded from the parallel init, so need to do the filtering separate | 
|                 message := message + 'Setting upper bound total blending = ' + [String] c.RHSValue() + String::NewLine();  | 
|               } | 
|               else | 
|               { | 
|                 constr.NewTerm( t.Coefficient(), t.Variable() );  | 
|               }  | 
|             }  | 
|           } | 
|         } | 
|       } | 
|       debuginfo( message );  | 
|       program.StoreString( 'message_kpilowerbound', message ); // workaround because method needs to be const | 
|     } | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |