Objects in this document may appear out of sequence if not viewed with Internet Explorer

ORMSware NMOD hands-on tutorial: Chapter 5


Storing more content in a model's NET and ADD files

 

If you wish to print this document, please set your browser page margins to <= 0.5 inches.

In this chapter you will learn the following (though not necessarily in the order listed):

  • Using a model's NET file to enter more property logic for any given network object
  • Why some network objects in a model's network diagrams are not in its NET file
  • Creating ADD file
  • Using a model's ADD file to accommodate user-defined arrays, special structures and procedures which will be available for use in any network object in the model.
  • Model level variable declarations and procedures
  • Network-object-level variable declarations
  • How NETrans recognizes and handles network object properties (nops)
  • CALLs to NMOD's Optimization Module
  • SELECT CASE construct
  • DO construct
  • IF-THEN-ELSE construct
  • Array sections
  • Aligning contents of character variables
  • UltraEdit's Search in conjunction with ORMSware reference check

Load C:\ORMSware\Tutorial\Chapter5\tPrimer.VSD into Visio and save it into C:\ORMSware\Tutorial.

Creating NET file

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.

Note: From this point on, though we will be leading by the fingers, you will be entering logic into models (though mostly by copying and pasting what we have already written for you to eliminate typing, reduce the risk of errors, and to move things along quickly); you will also be NETransing and compiling models. In other words, you wil actually be turning models into programs. This means the following can happen:

1. If you make any mistakes in the steps below (in this chapter and beyond), or miss any of the steps below, you will get errors that we cannot predict at this point. We will do our best to help you through the tutorial, but for free license customers, it will have to be on a time-available basis.

2. When you feel proficient enough to venture further into NMOD territory on your own, sooner or later you will start seeing errors such as ACCESS VIOLATION, followed by trace-back messages showing all procedure CALLs that led to the point of error. There is no need to panic when you see such errors. If you follow the error tracking processes described in this tutorial, you will be able to zero in on the problem very quickly.

By the way, in case you are not familiar, access violations, which scares many people, occur when you try to use a variable before its value is set in your model logic. As mentioned above, you can easily track down such problems by using reference check and track-back-error processes we have already covered in this tutorial.

No matter how much and how carefully a tool like this is tested, odds are always good that there still are undetected errors "hiding" to be uncovered by users! Please do let us know if you come across problems. Before you do, however, please double-check your input data, your model formulation, and coding of your model logic. Though most of NMOD code is hidden an protected from the user, there are essential and unavoidable interface points between customer code and engine code through which error's in customer code can penetrate NMOD and produce error messages as if the problem code is in NMOD. 

Invariably, the problem is in the data or the analyst's coding of model logic. Even when a problem is something for which we should have or could have included an error trap, the error-causing conditions will disappear when the data or logic is corrected. If you do feel that a particular problem you found should have an error trap, please do let us know. And, if after checking your code and data you think you have found a bug, please do contact us at your earliest convenience.

3. Recall that if you get any compilation errors, you never have to worry about errors below Compiling program unit NetAndEntProcsAndProps at line... in the compiler feedback file. These errors will disappear when you fix the problems displayed above this compiler feedback line.

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.

Note: While creating a model, you will find yourself switching frequently between Visio and the browser. You may find it much easier to develop a model when both windows are visible simultaneously to the maximum extent possible, especially in the early stages of a model. The toughest phase of every modeling project using NMOD is the very beginning. As you get over that hump, things start to come together faster and faster an you will begin to feel the fun and ease of modeling with NMOD. This happens every time you start a new project. It always feels like the first time.

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).

Note: You can use any of the many ways you already know to load c:\ORMSware\PrimerModels\Primer.NET into the browser, or you can use still another method as follows:

Click CommandList.TXT tab in the browser (c:\ORMSware\ProgramFiles\CommandList.TXT must have been preloaded when you brought up UltraEdit browser the very first time). Triple-click the line with EchoToOutputWindow: c:\ORMSware\PrimerModels\Primer.NET. Choose Advanced -- Execute selected command from the main menu. Double-click the file name when it appears in the output window.

You can add to this list and organize the list the way you like to access some of the tasks you find yourself doing frequently in the browser. Whether it is worth maintaining and using this list is strictly a matter of personal preference.

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.

Note: While comparing the two files above you may have noticed that some objects in the network are not listed at all in the NET file. NETrans does not enter some objects in the NET file, because values of all fields in those objects are default values or have behavior known a priori, and therefore, are already built into NMOD. Code blocks for such objects do not contribute anything new to the model.

How NETrans recognizes and handles Network object properties (nops)

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).

Note: Keep in mind that when you use an existing NET file (such as Primer.NET in our current situation) to supplement logic entered in Visio for a new model, NETrans will write over all of the nop statements in the existing NET file with the new model's nop statements entered in Visio. Only supplemental logic in existing NET file will be used. NETrans synchronizes a model's logic in Visio and its NET file by keying to the Page-ObjectID-property-tag combinations in existing NET file and the VSD (Visio) file of the new model.

SELECT CASE construct

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.

CALLs to Optimization Module

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.

Alert: NET, and ADD and PGM files (if any), along with the VSD (Visio) file of a model form the file group that are necessary for a model to function properly. If you save from Visio a model's VSD file into a different folder than the one you have been working in, be sure to move its corresponding NET, ADD and PGM files to the new folder, too.

Creating ADD file

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.

Notes

1. We can also link other user-supplied source and object files to a model, which is the preferred approach if the analyst already has routines that can perform any of the functions required at any network object in his/her model.

2. ORMSware notations can be used in ADD files, since NETrans translates ADD files the same way it translates NET and PGM files.

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.

Notes

1. The routine we include here is, of course, strictly for illustrating how to use ADD file to declare and define model level variables and procedures, and to demonstrate a few language functionalities while we are at it.

2. In the instructions below, please align the lines you paste into ADD and NET files for readability and appearance, though we do not specifically remind you to do it each time. There is, of course, no need to indent code, nor use Hungarian notation in naming variables for the code to function properly. However, as you may know, both "proper" indentation and Hungarian notation do help avoid unnecessary brain damage!

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.

Note: You can find out more about declaring and defining derived data types, pointers, etc. in Chapter 17 (User-defined scalars, arrays, derived types and procedures) of the Primer.

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.

UltraEdit Search in conjunction with ORMSware's reference check

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.

Caution: NETrans may abort when an analyst's model logic contain certain errors (such as no matching quotes in an intended literal string), in the middle of updating the model's NET file. In most of such cases NETrans issues a warming message indicating that updating is not complete and reminding you that the NET file can be recovered by executing the recover command.

In general, if NETrans has not issued the message NETrans ended normally, you should check the NET file to make sure that it is complete. If it is not, you can recover it as follows: Click the CommandList.TXT tab in the browser, or open that file if it is not already loaded; triple-click c:\ORMSware\ProgramFiles\Recover.BAT to select the whole line. Choose Advanced - Execute Selected Command, or press Ctrl-Shift-6. ORMSware will write a message to the output window when the recovery of the active model's NET file is complete.

 

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