|
Objects in this document may appear out of sequence if not viewed with Internet Explorer |
|
ORMSware NMOD hands-on tutorial: Chapter 5 |
|
In this chapter you will learn the following (though not necessarily in the order listed):
Load C:\ORMSware\Tutorial\Chapter5\tPrimer.VSD into Visio and save it into C:\ORMSware\Tutorial.
As you have already done in Chapter 2, you can create a model's barebones NET file by executing a predefined report from Visio and then NETransing that report from the browser. Let us do that now, and then enter supplemental logic in some of the objects and then try to execute the model.
When Visio is finished writing the report of C:\ORMSware\Tutorial\tPrimer.VSD (jump to this particular topic in Chapter 2 if you need to review), switch to the browser and choose Advanced -- NETrans + Compile.Link.Run. Results in the output window must look familiar (reaching events limit, etc.). Double-click Source NET: line towards the bottom of the output window to load tPrimer.NET into the browser. As you can see, NETrans has created tPrimer's NET file.
This time, we will put in some real logic in [15]'s branch property to prevent cycling. But, to do that we have to define &L.CurveBuild somewhere before it is used in [15]. The right place to do that is in n[6]Initializations (look at CostPerPiece network diagram and you will understand the rationale). Since tPrimer is an exact replication of the Primer model in NMOD Primer at this stage, you can simply copy its supplemental content from C:\ORMSware\PrimerModels\Primer.NET into C:\ORMSware\Tutorial\tPrimer.NET, studying each piece of logic as you copy it over. Of course, you also have the option of selecting the entire C:\ORMSware\PrimerModels\Primer.NET file (Ctrl-A) in the browser, copying it to clipboard (Ctrl-C), switching to C:\ORMSware\Tutorial\tPrimer.NET, selecting the entire file there (Ctrl-A) and pasting over it with the clipboard content (Ctrl-V).
We will do the total file copying and pasting for expediency, but at the same time get a quick overview of what we are adding to what is already there now in tPrimer.NET. After that we will discuss below some of the key new entries that will be in tPrimer.NET once the copying is complete. Load c:\ORMSware\PrimerModels\Primer.NET into the browser. Choose File -- Compare Files from the main menu. In the Compare Files dialog box check only the Differences In Color option. Choose c:\ORMSware\PrimerModels\Primer.NET from First file to compare drop-down list and c:\ORMSware\Tutorial\tPrimer.NET from Second file to compare drop-down list and click Compare. A Compare window will open with certain lines highlighted in red. These are the lines of code we will be adding to tPrimer.NET from Primer.NET. Check out all the red records. It will give you a good feel for how the model will evolve. The records highlighted with yellow will show you the minor changes that will be made to some of the existing records in tPrimer.NET when copying is complete. But, observe that all yellow records in this case are property statements entered in Visio (in tPrimer.VSD), so they will prevail over their red counterparts when we run NETrans. Choose any of the method we discussed above to change the content of tPrimer.NET to that of Primer.NET.
Close C:\ORMSware\PrimerModels\Primer.NET when copying is complete. Go to CostPerPiece.[1] in C:\ORMSware\Tutorial\tPrimer.NET. As you already know, nop stands for network object property. Any piece of code you see in NET files with "{nop.X}Prop:" or "{nop.X}Prop=" prefixes, where X represents the type or nature of the property and Prop identifies the particular property of the network object, is inserted there by NETrans if the statement on the right-hand-side (RHS) of the colon or equal sign (: or =) is enveloped by a pair of braces ({}). This is pretty easy to see in CostPerPiece.[1]. Now let us move to a[15]. Notice that there are two {nop.L}Branch= statements there. However, the RHS of 2nd statement is not enclosed in {}. NETrans will overwrite the first nop statement with the corresponding nop statement in the most recent Visio report, but it will leave the second one alone (will translate whatever is there, of course), because NETrans will identify the second statement as supplemental logic entered in the NET file by the analyst. By the way, in the second statement observe that the analyst is using the value of &L.CurveBuild assigned to the network object's Branch property in the first statement (entered through the Visio interface).
One other item of interest at this point may be the SELECT CASE structure in [4]. Here we are accommodating the argument that cost of labor per hour is a function of the hourly rate of production; that for up to and including 29 pieces per hour production rate the labor cost is $25 per hour; that labor cost for production rate of 30 pieces per hour or more is $30 per hour. Please make a note that the case expression in the SELECT CASE statement has to be a scalar integer, logical or character variable. Looking at the CASE statements, you may have noticed that there will be a hole in this structure which cannot handle fractional production rate between 29 and 30 widgets per hour. You will see in a later version of this model in the Primer that we can avoid this gap by using a lookup table. Let us run the current model and see what happens. Choose Advanced -- NETrans + Compile.Link.Run. Instead of double-clicking the NMOD.TXT file name in output window (assuming NMOD.TXT is not one of the files already in the browser), let us open it this time another way. Choose File -- Favorite Files... A list of frequently-used files during NMOD modeling pops up. Double-click NMOD.TXT, jump to the end and move up as necessary to see the results.
In [6] and [5] you will find CALLs to procedures in NMOD's Optimization Module. Whenever optimization is not explicitly activated by the user (as in the current state of our model), calls to the Optimization Module produce no effects. If we want to find the value of an independent variable that produces optimum response from a variable dependent on it, or if we want to find the value of an independent variable that produces a desired response (goal) from a variable dependent on it, we have to explicitly evoke NMOD's optimization process through a PGM file. Optimization is discussed in detail in Chapter 18 (Modules and global procedures that make up Model.EXE) and Chapter 19 (Reinforcing concepts through PrimerSig's feedback file - Golden Section bookmark) in the Primer, and Problem 3 (Capacity Planning) and Problem 5 (Fleet Flow) in the Examples section.
Suppose we want to use certain variables or procedures in several network objects. ADD file allows us to declare variables and define procedures at the model level so that they are available to all objects in every network in a model.
You can start an ADD file by using a "blank" ADD file (Sample.ADD) already loaded into your c:\ORMSware\ProgramFiles folder during installation. You can easily open Sample.ADD in the browser through File -- Favorite Files menu. For our current model save Sample.ADD as c:\ORMSware\Tutorial\tPrimer.ADD. We will create one routine to be included in this ADD file. There is no limit to how many procedures, or how many or what kind of variables you can include in an ADD file.
It may help to look at an ADD file without any of the clutter of comments, so let us delete some of the comments and stick with just the logic statements as much as possible. To avoid typing and to make this easy, you may want to copy code from here and paste it into your tPrimer.ADD file. Copy the following records and click on tPrimer.ADD tab in the browser. INTEGER,PARAMETER:: intRows = 20 !Integer constant for rows limit in array declaration below CHARACTER(len=25),DIMENSION(0:intRows,2):: strResults ! 25-char array with 21 rows and 2 columns Delete lines 2 thru first line of dashes. Position cursor in column 1 of line below !Insert model-level declarations here, if any. Paste. We will now crate a routine for storing CostPerPiece results for each hourly production rate as execution cycles through the model as desired. We will use a row in the strResults character array to store one pair of PiecesPerHour-CostPerPiece (x-y) values and then retrieve and print all pairs together at the end of model execution. Move cursor past the required CONTAIN statement to column 1 of line below Insert procedures... line. Copy and paste the following procedure into that location. SUBROUTINE StoreResult(strX,strY) CHARACTER(len=*):: strX ! * indicates that strX's length will be the same as that of... CHARACTER(len=*):: strY ! the corresponding string in the CALL to StoreResult !----- IF(&I.ValueCount < intRows)THEN &I.ValueCount = &I.ValueCount + 1 strResults(&I.ValueCount,1) = strX !When RHS string is longer, it will be truncated to fit... strResults(&I.ValueCount,2) = strY !When shorter, remaining space will be padded with blanks ELSE WRITE(9,*)"No more rows left in strResult array. Increase size of " & //"parameter intRows in .ADD file beyond current value of",intRows STOP ENDIF END SUBROUTINE StoreResult Highlight SUBROUTINE StoreResult line above and copy to clipboard. Click tPrimer.NET tab. In CostPerPiece.[5], position the cursor in column 1 of -{Trans} property tag line. Paste. Change SUBROUTINE to CALL. Copy the following lines of code to clipboard. WRITE(&20.PiecesPerHour,*)@D.PiecesPerHour !Internal WRITE to ... WRITE(&20.CostPerPiece,*)&D.CostPerPiece !20-char global property variable Switch to tPrimer.NET window in browser. Position cursor in column 1 of CALL StoreResult line. Paste. In CALL StoreResult line replace strX with &20.PiecesPerHour and strY with &20.CostPerPiece. Notice above in the StoreResult procedure that it increments a row number variable before storing the results in the strResults array. This variable must be initialized before we ask StoreResult to use it the very first time in the model. The right place to initialize variables in a model is in a node or arc that will execute only once in the model and will execute before the variable of interest will be used elsewhere. Such a node in this model is [6]Initializations, or a[11]. We will use [6]. While at it let us also enter headers for the two columns of data we will be storing. Insert the following lines of code anywhere in [6]'s Trans property. &I.ValueCount = 0 strResults(0,:) = (/"Pieces Per Hour","Cost Per Piece"/) !Array section (row 0, cols 1 & 2) Let us also change the starting value of PiecesPerHour to 5 so that we can get sufficient number of x-y pairs to be clearly see the behavior of the resulting CostPerPiece curve. Set @D.PiecesPerHour = 5 towards the top of [6]'s Trans property code block. Let us also set the upper limit &B.PiecesLim = 40. One point in the model at which we will know whether we are at the end of a model run is a[15], because we know that if its Branch property evaluates to FALSE, there will be no more objects remaining to be executed. So, as we did in Chapter 2, let us put the results writing part in this arc's Branch property, which will execute always. We can set up logic to execute our write statements when the Branch condition is not satisfied, because that will happen only when the last x-y pair we want to calculate has been calculated. Copy the following block of code. Position cursor in column 1 of -{Branch} tag of [15]. Paste. IF(.NOT.{nop.L}Branch)THEN
WRITE(9,*)REPEAT("=",50)
DO intIndx=0,&I.ValueCount
WRITE(9,*)ADJUSTR(strResults(intIndx,:))
ENDDO
WRITE(9,*)REPEAT("=",50)
ENDIF
Notice how we used array section strResults(intIndx,:) to write results from each relevant row. ADJUSTR aligns the content of a string variable to the right as you will see in the output. Since we are still writing to Unit 9, the results will show up towards the bottom of the execution feedback file Nmod.TXT (because [15] will be the last object to execute). We are using an integer variable, which we decided to call intIndx, to loop through all value pairs stored in strResults. intIndx is a user-defined integer variable separate from global, customer and surrogate property variables. Recall that global, customer and surrogate property variables are declared automatically by NETrans when user defines them on the fly. User-defined variables such as intIndx, however, have to be declared manually either at the model level in ADD file, making it available to all objects in a model, or at the network object level inside a network object, making it available only inside that object. Declarations at network object level must be placed right under the object's ID information in NET file, before any property tag.
Copy the following line to clipboard. INTEGER:: intIndx Switch to tPrimer.NET window and position the cursor in column 1 of the +{Branch} tag of [15]. Paste. Choose Advanced -- NETrans + Compile.Link.Run. You already know how to load Nmod.TXT into the browser by double-clicking its name in the output window. Jump to the end to see the results. There is only one x-y pair result there. You may know why, but in case you don't, let us discover why. We expected NMOD to iterate through the model for PiecesPerHour values ranging from 5 to 40 in increments of 5 units. We know that the only object that can cause this cycling is a[15]. Looking at the Branch property of [15] in NET file, we can see that arc traversal depends on &L.CurveBuild and whether @D.PiecesPerHour < &B.PiecesLim. We know that the 2nd part will be TRUE when the first customer reaches a[15], so the problem may be &L.CurveBuild. So, let us do a reference check on &L.CurveBuild (highlight it and choose Advanced -- [ Reference check ]). Results of the reference check in output window shows that the only other place we have &L.CurveBuild is n[6]'s Trans property.
We can use the browser's search facility to jump to the occurrence of interest. Place cursor on any &L.CurveBuild in the main window. Press Ctrl-F (or choose Search -- Find). In the Find dialog box check List Lines Containing String. Click Find Next. If a message box pops up asking Continue search from other end of file?, click Yes. A list containing all occurrences pops up. We know from ORMSware reference check that the occurrence of interest to us is the &L.CurveBuild = & line. Double-click that in the popped-up list to jump to the occurrence. Close the list window. You know from the & at the end of the line that the statement continues to the next line. The next line tells us that &L.CurveBuild will be TRUE when [2] is of Arrival Type. Switch to Visio and look at [2]. The node's Type is Network. To turn &L.CurveBuild on (to True) let us change [2]'s Type property to Arrival. If there is an Arrival node creating arrivals of customers, we have to have at least one Departure node for them to leave the system. It should be apparent that the only proper choice for Departure in this model is [5]. So, let us turn it into a Departure node. Generate the report. Switch to the browser and Choose Advanced -- NETrans + Compile.Link.Run. Jump to the end of Nmod.TXT to see the results. It may interest you to observe what you may have expected about the nature of the cost-per-piece curve. Per piece cost is high at low rates of production. It goes down for a while as production rate increases, but starts to go up beyond a certain optimal production rate point (about 25 Pieces Per Hour in this case). If you want to see arrivals and departures of customers, you can use the browser's Search facility as we used above to get a list of all occurrences (of Customers) and jump to the ones that indicate arrivals and departures. You can save tPrimer's ADD, NET and VSD files into the Tutorial folder or Play folder if you wish. We will not be using these files again in this tutorial. In the next and final chapter of this tutorial we will cover reverse-engineering of spreadsheets.
|
|
Click to go to Chapter 4: Building ORMSware networks - beyond basics |
|
Click to go to Chapter 6: Converting spreadsheet model to NMOD model |
|
Click to go to Introduction and table of contents |