Free Software for Sprinkler Hydraulic Calculations by Alan Ashfield

OPEN SOURCE SOFTWARE FOR FULL HYDRAULIC CALCULATIONS OF SPRINKLER SYSTEMS - SHP4FPC and SHP4MVB

Pascal source code for SHP4FPC [26th April 2012]

As you may have gathered by now, ALL my hydraulics programs are entirely FREE for you to use as you wish. However, my current 10 programs are defined as FREEWARE (where you just get the executable version) and not OPEN SOURCE (where you get the actual Basic, C++, Java, Delphi etc. coding) and sometimes called FOSS or FLOSS.

To continue to offer the best software for the Fire Protection Industry, I have now decided to release a complete and working hydraulics calculations program, called SHP4FPC as the source code listing below. This matches up with the excellent OPEN SOURCE Free Pascal Compiler at www.freepascal.org and, as I prefer the Pascal language, it did not take me long to produce the approx 700 lines. I have also shown what data it requires (as a little test job) and how the command line driven program actually operates and the Results printout so produced.

This will be especially useful if you want a foreign language version for your own country or you wish to learn a computer language such as Pascal. You can use, extend or modify this program as you like or convert it into another programming language entirely for NOTHING and without further reference to me but my Email address is at the top of this page if you want any more information.

Visual Basic source code for SHP4MVB [3rd October 2012]

As some of my users found the Pascal documentation a bit difficult to follow, I have redone virtually the same program suitable for the Microsoft Visual Basic 2010 Express application which you can download from the appropriate website (not the Visual Studio one). It does run to over 160 Mb and you then have to update the Microsoft .NET Framework 4 add-ones that downloads another 100 Mb, so set aside an hour for all this to happen automatically. Check that this runs on your computer and register it with Microsoft to extend the 30 day trial period to become a licenced user, entirely for free.

Now click here to download shp4mvbzip.zip and copy it to your computer - it is only 430 Kbytes so should not take long - you should then copy it from any "Downloads" folder onto the root of your C: or D: hard discs. Select this from the Windows Explorer to see 'shpzip' as a folder name - click on it and then on 'Extract all files' at the top of the screen, accept the defaults on the screen that follows to UNZIP the various folders and 80 files totalling about 1 Mb (again you should really choose a separate folder / directories, but you can use the same one as the zipped folder if you want).

Run Microsoft Visual Basic 2010 Express again and :-

If you already have the Microsoft .NET Framework 4 on your computer, then you may be able to run the SHP4MVB application (just 55 Kbytes) directly from the /Debug or /Release parts under the unzipped program folder. In either case, SHP4MVB looks like :-

SHP4MVB running

I have kept the SAME file, input data and results formats as the original SHP4FPC program listed on the rest of this page but you should have a monitor of at least 1366 x 768 pixels (preferably 1920 x 1080 HD) so can see the data and the results on the same screen. Obviously you can change the colours, positioning etc of the Form1 and there is lots of information about Visual Basic on the Microsoft MSDN website, in books from your Library or purchased on-line. You will find it virtually imposible to make any typing errors as the application is continually prompting hints and help as you go - so if you have a variable called FRED, then type in FRAD instead, then it underlines it in green and refuses to compile the program until you correct it.

Note that this is a complete Windows program, runs entirely within Windows and looks like all the other Windows programs you have - it even has "hints"! It would have been quite easy to produce an EN12845 version, but I only ever got requests from people wanting the NFPA 13/15 Rules since April / May this year. I hope you find this of interest but if you need any more information, then my Email address is at the top on the page. I have also removed the "You can convert it to another programming language ..." and "[27th April 2012] Suggested WINDOWS format ..." sections and the 3 little screen-shots in the previous version of this page as I have now done what I suggested you might want to try. I think I still prefer DELPHI to BASIC but appreciate you may not wish to learn another programming language, so here it is.

For the FREE PASCAL COMPILER, you should ...

Visit www.freepascal.org and get the version of the FREE PASCAL COMPILER corresponding to your chosen operating system - it is available for DOS, WINDOWS 98/ME/NT/2000/XP/Vista/7 in 32 and 64 bit, MAC OSX, LINUX, UNIX and many others. There is no registration, Email addresses, licences etc. Sound familiar? This will only take a few minutes to download and install - accept all the default options. There is copious documentation as several PDF files but you do not need to read them all and many hundreds of little examples explaining key features.

Create another directory / folder on your computer, perhaps C:\shp4fpc and click the mouse over the start of the '{ Copyright ...' line below and drag the mouse down until all the text to the last 'end.' is highlighted - press [CTRL]+C to copy it, open 'NotePad' or similar, then press [CTRL]+V to paste and see the 700 lines. Save it as 'shp4fpc.pp' in your new folder and select 'New' to clear the screen. Return to this webpage, go down to the Typical Data File heading and the start of the 'First Test Example' line and do the same highlight, drag down to the end of the '143 144 ...' line, [CTRL]+C, goto Notepad, [CTRL]+V but this time save the 37 lines to 'Test001.txt'.

You now need to compile the program - although there is a good Free Pascal IDE, I found it best to do this from the 'Command Prompt' in Windows. You only need to do this once, unless you change the shp4fpc.pp source code for any reason. Just type :-

CD \directory1

FPC \directory2\SHP4FPC.PP

where directory1 is the location of the Free Pascal Compiler (given when you hover your mouse over the 'Free Pascal IDE' icon on the desktop or if that doesn't work, then 'right-click' and select 'Properties') and directory2 is where you have saved the .pp and .txt files. After less than a second, the shp4fpc.exe program will be generated and saved back in your directory2 folder.

[27th April 2012 - To save yourself 15 minutes, and learning something useful, you can DOWNLOAD the DOS version shp4fpc.exe by clicking here (it is only about 100KBytes) and saving it to a specific directory - you MUST then "copy / paste" the Test001.txt file from below to this same directory as described in the above paragraph and then run the "Command Prompt" otherwise it won't work.]

You can now type :-

CD \directory2

SHP4FPC TEST001.TXT

to see the following on the screen :-

SHP4FPC by Alan Ashfield - April 2012
Please visit www.freehc.net for more information
Developed with the Free Pascal Compiler V2.6.0
at www.freepascal.org for multiple operating systems
Started checking Test001.TXT
and creating Test001.BAK
Started on calculations for 12 pipes to 6 heads
Pipe sizes from 25 to 100 mm and highest head = 5.000 m
Source flow = 566.2 L/min at 2.302 bars
Balanced in 10 steps, errors 0.000289 bars + 0.027 L/min
Results now stored in Test001Results.TXT
End of SHP4FPC by Alan Ashfield - www.freehc.net

You can now go back to Windows and use 'NotePad' or similar to view the Test001Results.TXT file (all in the same directory) which should look like

START OF RESULTS PRINTOUT FROM SHP4FPC BY ALAN ASHFIELD
. 
Q1  Project name     = First Test Example
Q2  Project number   = 1
Q3  Location         = Somewhere over the rainbow
Q4  Drawings         = Drawings
Q5  Remotest area    = Most Remote
Q6  Occupancy        = Dont know
Q7  System / Hazard  = Ordinary Hazard
Q8  Contractor       = Put your name here
Q9  Address          = Your address
Q10 Designer         = Alan Ashfield
Q11 A.H.J.           = AHJ
Q12 Water supply     = Calculate min duty
Q13 Static bars      = 0.000
Q14 Residual bars    = 0.000
Q15 Residual L/min   = 0
Q16 Application area = 54.0
Q17 Density mm/min   = 10.00
Q18 Head area sq.m   = 9.000
. 
SOURCE DUTY AT NODE 100 = 566.2 L/min at 2.302 bars
. 
Remote head is at node 144 = 90.0 L/min, 1.266 bars and 5.000 m
. 
Node1                  Flow Q  S i z e  Fittings  Length    Type      Pt
      Heights Kfactor  Head q     mm     ELT4BGS   Fitts  Cfactor     Pe
Node2                   L/min  B o r e   LTX5VVC  Totalm   mbar/m     Pf
--------------------------------------------------------------------------
  100   0.000           566.2   100                1.500    S40      2.302
                                         0000010   0.610    120      0.147
  110   1.500                   102.26             2.110      1.7    0.004
--------------------------------------------------------------------------
  110   1.500           566.2   100                3.000    S40      2.151
                                         0000100   3.658    120      0.294
  120   4.500                   102.26             6.658      1.7    0.012
--------------------------------------------------------------------------
  120   4.500           566.2   100               30.000    S40      1.845
                                         1000000   3.048    120      0.000
  130   4.500                   102.26            33.048      1.7    0.058
--------------------------------------------------------------------------
  130   4.500           283.8    40                0.500    S40      1.788
                                         0010000   2.438    120      0.049
  131   5.000                    40.90             2.938     42.1    0.124
--------------------------------------------------------------------------
  131   5.000           283.8    40                1.000    S40      1.615
                 80.0    98.7            1000000   1.219    120      0.000
  132   5.000                    40.90             2.219     42.1    0.093
--------------------------------------------------------------------------
  132   5.000           185.1    32                3.000    S40      1.522
                 80.0    94.7            0000000   0.000    120      0.000
  133   5.000                    35.10             3.000     40.2    0.121
--------------------------------------------------------------------------
  133   5.000            90.4    25                3.000    S40      1.401
                 80.0    90.4            0000000   0.000    120      0.000
  134   5.000                    26.60             3.000     41.2    0.124
--------------------------------------------------------------------------
  130   4.500           282.5    65                3.000    S40      1.788
                                         0000000   0.000    120      0.000
  140   4.500                    62.70             3.000      5.2    0.016
--------------------------------------------------------------------------
  140   4.500           282.5    40                0.500    S40      1.772
                                         0010000   2.438    120      0.049
  141   5.000                    40.90             2.938     41.7    0.123
--------------------------------------------------------------------------
  141   5.000           282.5    40                1.000    S40      1.600
                 80.0    98.2            1000000   1.219    120      0.000
  142   5.000                    40.90             2.219     41.7    0.093
--------------------------------------------------------------------------
  142   5.000           184.3    32                3.000    S40      1.508
                 80.0    94.3            0000000   0.000    120      0.000
  143   5.000                    35.10             3.000     39.9    0.120
--------------------------------------------------------------------------
  143   5.000            90.0    25                3.000    S40      1.388
                 80.0    90.0            0000000   0.000    120      0.000
  144   5.000                    26.60             3.000     40.9    0.123
--------------------------------------------------------------------------
. 
END OF PRINTOUT FROM SHP4FPC BY ALAN ASHFIELD
.

You can also view the 'Test001.BAK' file which shows the data file all properly formatted (not given here). You can now see how this simple program operates - one creates or edits a .txt job data file and saves it. You then run the program from the 'Run' box or 'Command prompt' (shp4fpc filename.txt), to see the messages generated, check if OK or action required and then one views the filenameResults.txt output file in 'NotePad'.

Data required for SHP4FPC ...

This program will carry out the calculations for the most hydraulically demanding design area of any hazard of end- or centre-fed branch line sprinkler system to the NFPA 13 Rules in metric units, either with Q13-15 all zero (as shown) or to any defined water supply. You can therefore apply it to roof only or roof + rack protection, ESFR, large drop, wet / dry systems or deluge / drencher / spray fire protection pipework layouts. I have just entered sizes from 25 to 300mm for a few pipe types and all 7 fittings given in the NFPA Tables. The results printout above is the minimum necessary and I have, of course, checked it against my existing FREESHP program as giving the same answers.

The first 18 questions should be obvious so one starts noding up at the source and proceeds outwards to the operating heads / nozzles. Node numbers are allocated at junctions where 2, 3 or 4 pipes meet, at operating heads and where changes in pipe size or type occur other than at elbows, tees etc. Pipes are therefore defined as being between the 'From' node to the 'To' node where the 'From' node is nearer to the source. In other words, you number up the pipes away from the source out into the pipework system, such that the 'From' node must have been given as a previous 'To' node and any operating heads / nozzles are at the 'To' nodes. The pipes data starts at line 26 and consists of :-

These 7 or 9 numbers are typed in, separated by one or more 'spaces', but you do not have to line them up under the specific headings but it sometimes makes it easier to check. Neither must you put in the required decimal values so 3 is the same as 3.0 or 3.000 as a horizontal / vertical length. In any case, SHP4FPC automatically produces a .BAK version of your data file with any corrected data all aligned properly.

This data can be typed out in any line or screen based editor applicable to your operating system EDIT for MS.DOS, NotePad for Windows or Vi for Unix. You can save it whenever you want and at the end when completed. When you run SHP4FPC, you will either see the results in a separate file or may need to get back to your input data and correct one or more items given in the warning / error messages.

What to do when you have tried out SHP4FPC ...

What you have obviously realised is that SHP4FPC is not as 'user friendly' as my other FREEWARE programs nor others you may have already used. Well, that is because it does not have a USER INTERFACE!

The Free Pascal Compiler is NOT just for Windows which you are currently using - it can produce programs for a wide range of operating systems (remember MS.DOS?) all from the SAME listing. This is both a good thing and a bad thing. Good, because once you get a specific function working you can apply it everywhere but bad, because it does not have a general way of displaying items (edit boxes, command buttons, menus, graphics screens etc) as these vary enormously between operating systems. Hence the compromise. Oh, did I mention that this is all FREE?

If you have paid hundreds for your existing program (and hundreds more a year for updates), then it must come as a bit of a surprise that the actual coding for the flow and pressure CALCULATIONS is only about a hundred lines long! Much more of the program is devoted to checking the information, producing suitable warning or error messages and generating the results printout. The calculations part is the easiest bit (for me but not for you I suspect, which is the whole reason for this page) - conceiving the best way to get the user (you!) to enter the data with the minimum number of errors or problems is the hard part.

By showing you all this (reasonably easy to follow) listing, I wish to demonstrate how meticulous and exact the programmer has to be to cope with all foreseeable eventualities but it is not some 'black magic' that only really clever people can create. The mathematics involved is really trivial and the 'reducing error' calculation method is widely available, if you know where to look, which is why several independent companies have all come up with just variations on this basic procedure, as there is obviously only one 'correct' result for any given sprinkler hydraulic calculation. Well, you know where to look now - this webpage!

You can ignore this page entirely ...

It really does not matter if the 3 days or so that it has taken me to conceive and produce SHP4FPC has been entirely wasted if nobody ever does anything with the Pascal coding. You can just carry on with one or more of my other FREEWARE programs (such as FREESHP) or your existing software package. It was really the innovation of the £30 Raspberry Pi single board PC running Linux or the VIA APC single board PC running Android. So, in an effort to get other people interested in computer programming, I thought I would release the code listing to match the Free Pascal Compiler project, as being much better / faster than Basic.

You can continue with the Free Pascal Compiler ...

There are plenty of sites on the Internet devoted to the Delphi variant of Pascal - you can buy the XE starter version for about £200 up to the full package for over £2000. This will enable you to produce Windows programs like FREESHP, AACALC7 and others. However, Free Pascal is what it says it is; FREE. So although you can visit sites like www.delphibasics.co.uk and www.delphi.about.com and get one of the many excellent books about Delphi, these may need adapting slightly to work under the Free Pascal Compiler. There are extensive PDF files provided on the www.freepascal.org site and these will be a good place to start if you wish to learn Pascal. Remember the 80/20 rule - for 80% of the time you may only use 20% of your knowledge about a specific topic. Well in Pascal, this is more like 95/5 - it is capable of doing far more than you will ever need. Most people will find Pascal easier to learn and apply than other alternatives like Basic or C++.

You will therefore have your OWN program to extend, revise and make better to suit your individual requirements. The first place to start would be the Results section to include extra information or a better format - just look at the "paid for" alternatives for ideas. Just make any changes on a backup copy so if they don't work out you can return to the previous iteration. Best of Luck.

You may want to produce a version for another set of Sprinkler Rules ...

Obviously, I have decided to include the NFPA 13 / 15 Rules in the SHP4FPC program as more sprinkler systems have been installed to them in the World than all the other ones put together. For fog / mist systems to NFPA 750, you will need smaller sizes of different pipe types / fittings and the Darcy-Weisbach pressure drop formula so the density and viscosity of the fluid can be changed. For the FM Global Rules, you will need to revise the 'Fittings' lines for some more equivalent lengths, extend the part on the input data / results and may need to make 'fact1:=1.0;' in the ' //adjust for not S40' part.

[31st May 2012] If you are interested in LPS 1283 WATERMIST systems (following on from DD 8489), then you can easily modify my source code for the COLEBROOK-WHITE pressure drop equation. LPS 1283 requires that hydraulic calculations are made before and after testing for the various categories of hazard / protection, so if you have produced your OWN program you will able to substantiate those calculations rather than relying on 'bought in' software which will be based on the Hazen-Williams formula, only used in the fire protection industry. The Colebrook-White formula is used extensively for single- and two-pipe heating, pumped and gravity fed hot and cold water distribution systems, chilled water circulation and supply / extract ventilation systems etc. by the rest of the building industry as the density / viscosity / temperature of the fluid is included.

For the CEA, CEN, CP52, DBI, AS2008 etc. Rules you will have to replace the 'head min L/min' column with the 'area covered' (and possibly the density, minimum pressure), do a separate summation of area to compare with Q16, redo Q12-15 for 8 to 11 flow / pressure points on a water supply / pump curve (and polynomial curve fitting - look it up - you cannot use straight line interpolation as that just wouldn't be right) as well as identifying the 4 most remotest heads and the matching area / density calculation. The new format of the Results presentation could be copied from my AACALC7 or EASYSHP programs shown elsewhere on this website after you have revised the 'PipeTypes' and 'Fittings' arrays. You may find such an exercise quite instructive and compelling and more aware of what is actually involved in doing the full hydraulic calculations for your future jobs.

You may want to produce your own language version ...

All my existing programs and those other "paid for" programs are usually only available in English, which may not be your first language nor that of any reviewers to which you may have to send hydraulic calculations results printouts. Unfortunately, the Free Pascal Compiler and all it's documentation is also only available in English as these computer languages were all developed in English speaking counties. So whichever way you may want, you are going to have to learn English (unless you are using an on-line 'translate' service to read this text).

I have done my best to help out in this respect within SHP4FPC. Only the 'Questions' part (the first 25 lines on the results) and the text of the 2 lines within each 'ShowError' lines in the listing will need translating from English into your own language. You don't use commencer and fin in French for begin and end, you use begin and end (even in France) because they are Pascal keywords.

You would use an editor like NotePad to amend the appropriate lines in the listing below (putting // at the beginning of a line makes it a 'comment' which is ignored so just leave the 'English' lines there before your translation). Save the new .pp file and run the Free Pascal Compiler (as originally) to produce the new .exe version. In the spirit of OPEN SOURCE network, if you do create a matching version of SHP4FPC in another language, please let me know (Email at top of page) and I will either publish a link to it or show the corresponding listing on this webpage. I mean, were you going to make a charge for such a program?

If you prefer BASIC then please go back to the second heading above about SHP4MVB

Free Pascal source code listing of shp4fpc.pp

{   Copyright (C) 2012  Alan Ashfield
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.}
//
program shp4fpc;
//
uses sysutils;
//
function Trim(sstrg1:String):String;
//       Removes leading/trailing blanks
begin
    Trim:=TrimLeft(TrimRight(sstrg1));
end; //of Trim
//
function Num2Str(sval,slen,sdec:Single):String;
//      formats a string:len:dec points
var sstrg:string;
begin
    Str(sval:Trunc(ABS(slen)):Trunc(sdec),sstrg);
             if slen<0 then sstrg:=Trim(sstrg);
    Num2Str :=sstrg;
end; //of Num2Str
//
procedure ShowError(sstrg1,sstrg2:string);
//       Shows 2 lines of messages on screen with 3rd blank
begin
    WriteLn(sstrg1);
    WriteLn(sstrg2);
    WriteLn(' ');
end; //of ShowError
//
procedure AllInOne(filename:string);
var
    numlines,numpipes,numheads,numsizes,numpipetypes     :SmallInt;
    numfittings,linenum,dspos,fromnode,tonode,sourcenode :Smallint;
    nomsize,ptnum,ptypesnum,mmnum,nearmm,sizenum,fitnum  :Smallint;
    fitquant,headnum,pipenum,upstream,givenb4,minsizemm  :SmallInt;
    maxsizemm,numsteps,remotepipe,numnoflow,headsunder   :Smallint;
    headsover:Smallint;
    staticp,residp,residf,darea,density,headsqm,m2bars   :Single;
    value,boremm,actboremm,horizm,vertm,actualm,fittingsm:Single;
    eqlfeet,hwcfactor,boreS40,headkf,headlmin,maxmpers   :Single;
    headbars,fact1,fact2,maxheadm,pipelmin,pipebar       :Single;
    maxbars,sourcelmin,sourcebars,totallmin,errorbar     :Single;
    icode:LongInt;
    lineofchars,origline,ss1,ss2,ss3,pttype,fittslist    :String;
    underline,bakfile,resultsfile:String;
    inout:TextFile;
    Sizemm       : array[1..20]         of SmallInt;  //Nominal sizes
    PipeTypes    : array[1..10]         of String;    //Bores in mm
    Fittings     : array[1..10]         of String;    //Eq lengths
    Questions    : array[1..23]         of String;    //For results
    JobData      : array[1..1200]       of String;    //All input data
    PipeI        : array[1..9 ,1..1000] of Smallint;  //Integer data
    PipeS        : array[1..19,1..1000] of Single;    //Real data
    PipeF        : array[1..1000]       of String[7]; //just fittings
begin
//
//                         SETUP
//
    ShowError('SHP4FPC by Alan Ashfield - April 2012',
              'Please visit www.freehc.net for more information');
    ShowError('Developed with the Free Pascal Compiler V2.6.0',
              'at www.freepascal.org for multiple operating systems');
    Sizemm[1]    :=25;
    Sizemm[2]    :=32;
    Sizemm[3]    :=40;
    Sizemm[4]    :=50;
    Sizemm[5]    :=65;
    Sizemm[6]    :=80;
    Sizemm[7]    :=90;
    Sizemm[8]    :=100;
    Sizemm[9]    :=125;
    Sizemm[10]   :=150;
    Sizemm[11]   :=200;
    Sizemm[12]   :=250;
    Sizemm[13]   :=300;
    numsizes     :=13;    //no of above
    PipeTypes[1] :='S40  26.60  35.10  40.90  52.50  62.70  77.90  90.10 '+
                       '102.26 128.20 154.10 202.70 254.50 303.20     120';
    PipeTypes[2] :='MW   27.31  35.97  41.86  52.98  68.67  80.68   0.00 '+
                       '105.14   0.00 155.32   0.00   0.00   0.00     120';
    PipeTypes[3] :='DIN  27.20  35.90  41.80  53.00  70.90  83.10   0.00 '+
                       '107.90   0.00 160.30 210.10 263.00 312.70     120';
    numpipetypes :=3;     //no of above
    Fittings [1] :='EL    2.0    3.0    4.0    5.0    6.0    7.0    8.0  '+
                       ' 10.0   12.0   14.0   18.0   22.0   27.0         ';
    Fittings [2] :='LT    2.0    2.0    2.0    3.0    4.0    5.0    5.0  '+
                       '  6.0    8.0    9.0   13.0   16.0   18.0         ';
    Fittings [3] :='TX    5.0    6.0    8.0   10.0   12.0   15.0   17.0  '+
                       ' 20.0   25.0   30.0   35.0   50.0   60.0         ';
    Fittings [4] :='45    1.0    1.0    2.0    2.0    3.0    3.0    3.0  '+
                       '  4.0    5.0    7.0    9.0   11.0   13.0         ';
    Fittings [5] :='BV    0.0    0.0    0.0    6.0    7.0   10.0    0.0  '+
                       ' 12.0    9.0   10.0   12.0   19.0   21.0         ';
    Fittings [6] :='GV    0.0    0.0    0.0    1.0    1.0    1.0    1.0  '+
                       '  2.0    2.0    3.0    4.0    5.0    6.0         ';
    Fittings [7] :='SC    5.0    7.0    9.0   11.0   14.0   16.0   19.0  '+
                       ' 22.0   27.0   32.0   45.0   55.0   65.0         ';
    numfittings  :=7;         //no of above
    m2bars       :=0.098;     //metres to bars
//  All this text will need translating into your language
    Questions[1] :='Q1  Project name     =';
    Questions[2] :='Q2  Project number   =';
    Questions[3] :='Q3  Location         =';
    Questions[4] :='Q4  Drawings         =';
    Questions[5] :='Q5  Remotest area    =';
    Questions[6] :='Q6  Occupancy        =';
    Questions[7] :='Q7  System / Hazard  =';
    Questions[8] :='Q8  Contractor       =';
    Questions[9] :='Q9  Address          =';
    Questions[10]:='Q10 Designer         =';
    Questions[11]:='Q11 A.H.J.           =';
    Questions[12]:='Q12 Water supply     =';
    Questions[13]:='Q13 Static bars      =';
    Questions[14]:='Q14 Residual bars    =';
    Questions[15]:='Q15 Residual L/min   =';
    Questions[16]:='Q16 Application area =';
    Questions[17]:='Q17 Density mm/min   =';
    Questions[18]:='Q18 Head area sq.m   =';
    Questions[19]:='Q19 Spare            =';
    Questions[20]:='Q20 Spare            =';
    Questions[21]:='Q21 Spare            =';
    Questions[22]:='Q22 Spare            =';
    Questions[23]:='Q23 Spare            =';
//
//                                   READ IN USERS DATA FILE
//
    dspos        :=Pos('.',filename);
    bakfile      :=Copy(filename,1,dspos)+'BAK';
    resultsfile  :=Copy(filename,1,dspos-1)+'Results.TXT';
    numlines     :=0;
    ShowError('Started checking '+filename,'and creating '+bakfile);
//  Open this file
    Assign(inout,filename);
    ReSet     (inout);
    While Not EOF(inout) do begin   //read in all lines into JobData
      ReadLn  (inout,lineofchars);
      numlines         :=numlines+1;
      JobData[numlines]:=Trim(lineofchars);
    end;
//  Close input file
    Close(inout);
//
//  Begin checking
//
    Val(JobData[13],staticp,icode);
    if staticp< 1.0   then staticp:=0.0;
    if staticp>30.0   then staticp:=30.0;
    Val(JobData[14],residp,icode);
    if residp<0.0     then residp :=0.0;
    if residp>staticp then residp :=staticp;
    if residp>30.0    then residp :=30.0;
    Val(JobData[15],residf,icode);
    if residf<0.0     then residf :=0.0;
    if residf>30000.0 then residf :=30000.0;
    if (staticp>1.0)  and (residf<500.0) then residf:=500.0;
    Val(JobData[16],darea,icode);
    if darea<0.0      then darea  :=0.0;
    if darea>2000.0   then darea  :=2000.0;
    Val(JobData[17],density,icode);
    if density<0.0    then density:=0.0;
    if density>99.0   then density:=99.0;
    Val(JobData[18],headsqm,icode);
    if headsqm<0.0    then headsqm:=0.0;
    if headsqm>50.0   then headsqm:=50.0;
//  Save BackUp file
    JobData[13]:=Num2Str(staticp,-9,3);
    JobData[14]:=Num2Str(residp,-9,3);
    JobData[15]:=Num2Str(residf,-9,0);
    JobData[16]:=Num2Str(darea,-9,1);
    JobData[17]:=Num2Str(density,-9,2);
    JobData[18]:=Num2Str(headsqm,-9,3);
    Assign    (inout,bakfile);
    ReWrite   (inout);
    for linenum:=1 to 25 do WriteLn(inout,JobData[linenum]);
    numpipes   :=0;
    numheads   :=0;
    maxheadm   :=-99999.9;
    minsizemm  :=999;
    maxsizemm  :=0;
    for pipenum:=1 to 1000 do begin
        for icode:=1 to 9  do PipeI[icode,pipenum]:=0;
        for icode:=1 to 19 do PipeS[icode,pipenum]:=0.0;
        PipeF[pipenum]:='       ';
    end;
//
//  Now go through data line by line
//
    for linenum    :=26 to numlines do begin
        lineofchars:=Trim(UpCase(JobData[linenum]))+' ';
        origline   :=lineofchars;
        if Length(lineofchars)<10 then Continue;  //skip cos too short
//  Remove all double spaces in line
        Repeat
            dspos  :=Pos('  ',lineofchars);
            if dspos>0   then lineofchars:=Copy(lineofchars,1,dspos-1)+
                              ' '+Copy(lineofchars,dspos+2,99);
        Until  dspos=0;
//  Check From node
        dspos      :=Pos(' ',lineofchars);
        Val(Copy(lineofchars,1,dspos-1),value,icode);
        if value<0.0    then value:=0.0;
        if value>9999.0 then value:=9999.0;
        lineofchars:=Copy(lineofchars,dspos+1,99);
        fromnode   :=Trunc(value);
        upstream   :=0;
        if numpipes=0   then sourcenode:=fromnode;  //first pipe
        if numpipes>0   then begin
//  Check if at start
           if fromnode=sourcenode then begin
              ShowError('Pipe '+Num2Str(numpipes+1,6,0)+' = '+origline,
                        'From node equals first node = '+
                        Num2Str(sourcenode,-6,0));
              fromnode:=0;
           end;
//  Find upstream pipe number
           for pipenum:=1 to numpipes do
               if (fromnode=PipeI[2,pipenum]) and (upstream=0) then
                   upstream:=pipenum;
//  Error if not there
           if (upstream=0) and (fromnode>0) then begin
              tonode  :=PipeI[2,numpipes];
              ShowError('Pipe '+Num2Str(numpipes+1,6,0)+' = '+origline,
                        'From node '+Num2Str(fromnode,-6,0)+
                        ' has not been given as a previous To node so'+
                        ' changed to '+Num2Str(tonode,-6,0));
              fromnode:=tonode;
              upstream:=numpipes;
           end;
        end;
//  Check To node
        dspos      :=Pos(' ',lineofchars);
        Val(Copy(lineofchars,1,dspos-1),value,icode);
        if value<0.0    then value:=0.0;
        if value>9999.0 then value:=9999.0;
        lineofchars:=Copy(lineofchars,dspos+1,99);
        tonode     :=Trunc(value);
//  Check if same as From node
        if fromnode=tonode then begin
           ShowError('Pipe '+Num2Str(numpipes+1,6,0)+' = '+origline,
                     'From node equals To node = '+
                     Num2Str(tonode,-6,0));
           fromnode:=0;
           tonode  :=0;
        end;
//  Check if at start
        if tonode=sourcenode then begin
           ShowError('Pipe '+Num2Str(numpipes+1,6,0)+' = '+origline,
                     'To node equals first From node = '+
                     Num2Str(tonode,-6,0));
           fromnode:=0;
           tonode  :=0;
        end;
//  Check if a repeat
        if (numpipes>0) and (tonode>0) then begin
           givenb4    :=0;
           for pipenum:=1 to numpipes do
              if (tonode=PipeI[2,pipenum]) and
                 (givenb4=0) then givenb4:=pipenum;
           if givenb4>0 then begin
              ShowError('Pipe '+Num2Str(numpipes+1,6,0)+' = '+origline,
                        'To node of '+Num2Str(tonode,-6,0)+
                        ' repeats a previous To node at pipe '+
                        Num2Str(givenb4,-6,0)+' so has been deleted.');
              fromnode:=0;
              tonode  :=0;
           end;
//  Check if a duplicate
           givenb4:=0;
           for pipenum:=1 to numpipes do
               if (fromnode=PipeI[1,pipenum]) and
                  (tonode  =PipeI[2,pipenum]) and
                  (givenb4=0) then givenb4:=pipenum;
           if givenb4>0 then begin
              ShowError('Pipe '+Num2Str(numpipes+1,6,0)+' = '+origline,
                        'Is a repeat of previous pipe '+
                        Num2Str(givenb4,-6,0)+' so has been deleted.');
              fromnode:=0;
              tonode  :=0;
           end;
        end;
        if (fromnode=0) or (tonode=0) then Continue;  //skip to next
//  Check nominal pipe size
        dspos      :=Pos(' ',lineofchars);
        Val(Copy(lineofchars,1,dspos-1),value,icode);
        if value<0.0    then value:=0.0;
        if value>9999.0 then value:=9999.0;
        lineofchars:=Copy(lineofchars,dspos+1,99);
        nomsize    :=Trunc(value);
//  Check pipe type given
        dspos      :=Pos(' ',lineofchars);
        pttype     :=Copy(lineofchars,1,dspos-1);
        lineofchars:=Copy(lineofchars,dspos+1,99);
        ptnum      :=0;
        for ptypesnum:=1 to numpipetypes do  //check if in list
           if pttype=Copy(PipeTypes[ptypesnum],1,Length(pttype)) then
                       ptnum:=ptypesnum;
        if ptnum=0 then begin
           ShowError('Pipe '+Num2Str(numpipes+1,6,0)+' = '+origline,
                     'Pipe type = '+pttype+' is not in database so'+
                     ' changed to '+Copy(PipeTypes[1],1,4));
           ptnum   :=1;
        end;
        ss1        :=Trim(Copy(PipeTypes[ptnum],
                          Length(PipeTypes[ptnum])-5,6));
        Val(ss1,hwcfactor,icode);
//  Check nominal size exists
        mmnum      :=0;
        actboremm  :=0.0;
        nearmm     :=999;
        for sizenum:=1 to numsizes do begin
            ss1    :=Trim(Copy(PipeTypes[ptnum],4+(sizenum-1)*7,7));
            Val(ss1,boremm,icode);
            if (Abs(nomsize-Sizemm[sizenum])<=nearmm) and
               (boremm>=1.0) then begin
                nearmm   :=Abs(nomsize-Sizemm[sizenum]);
                mmnum    :=sizenum;
                actboremm:=boremm;
            end;
        end;
        if nearmm<>0 then begin
           ShowError('Pipe '+Num2Str(numpipes+1,6,0)+' = '+origline,
                     'Pipe size of '+Num2Str(nomsize,-6,0)+
                     ' mm is not available in pipe type '+
                     Copy(PipeTypes[ptnum],1,4)+
                     ' so changed to nearest of '+
                     Num2Str(Sizemm[mmnum],-6,0)+' mm');
           nomsize:=Sizemm[mmnum];
        end;
        if nomsize<=minsizemm then minsizemm:=nomsize;
        if nomsize>=maxsizemm then maxsizemm:=nomsize;
//  Check this size is not BIGGER than upstream pipe
        if upstream>0 then begin
           if nomsize>PipeI[3,upstream] then
              ShowError('Pipe '+Num2Str(numpipes+1,6,0)+' = '+origline,
                        'Pipe size of '+Num2Str(nomsize,-6,0)+
                        ' mm is BIGGER than upstream pipe '+
                        Num2Str(PipeI[1,upstream],-6,0)+' to '+
                        Num2Str(PipeI[2,upstream],-6,0)+' which is '+
                        Num2Str(PipeI[3,upstream],-6,0)+' mm');
        end;
//  Check horizontal pipe length
        dspos      :=Pos(' ',lineofchars);
        Val(Copy(lineofchars,1,dspos-1),value,icode);
        if value<0.0   then value:=0.0;
        if value>500.0 then value:=500.0;
        lineofchars:=Copy(lineofchars,dspos+1,99);
        horizm     :=value;
//  Check vertical pipe length
        dspos      :=Pos(' ',lineofchars);
        Val(Copy(lineofchars,1,dspos-1),value,icode);
        if value<-50.0 then value:=-50.0;
        if value>50.0  then value:=50.0;
        lineofchars:=Copy(lineofchars,dspos+1,99);
        vertm      :=value;
        actualm    :=Sqrt(horizm*horizm+vertm*vertm);
                     if actualm<0.1 then actualm:=0.1;
        fittingsm  :=0.0;
//  Check fittings
        dspos      :=Pos(' ',lineofchars);
        fittslist  :=Copy(lineofchars,1,dspos-1)+'0000000000';
        lineofchars:=Copy(lineofchars,dspos+1,99);
        fact1      :=1.0;
        ss1        :=Trim(Copy(PipeTypes[1],4+(mmnum-1)*7,7));
                     Val(ss1,boreS40,icode);
        if (ptnum<>1) and (boreS40>1.0) then   //adjust for not S40
              fact1:=Exp(Ln(actboremm/boreS40)*4.87);
        fact2      :=Exp(Ln(hwcfactor/120.0)*1.85); //adjust for not 120
        for fitnum :=1 to numfittings do begin
            Val(Copy(fittslist,fitnum,1),fitquant,icode);
            if fitquant>0 then begin
               ss1      :=Trim(Copy(Fittings[fitnum],4+(mmnum-1)*7,7));
                          Val(ss1,eqlfeet,icode);
               fittingsm:=fittingsm+(fitquant*eqlfeet*0.3048*fact1*fact2);
            end;
        end;
//  Check head k factor and minimum flow rate
        headkf  :=0.0;
        headlmin:=0.0;
        headbars:=0;
        headnum :=0;
        if Length(lineofchars)>3 then begin
           dspos      :=Pos(' ',lineofchars);
           Val(Copy(lineofchars,1,dspos-1),value,icode);
           if value<1.0    then value:=1.0;
           if value>999.0  then value:=999.0;
           lineofchars:=Copy(lineofchars,dspos+1,99);
           headkf     :=value;
           dspos      :=Pos(' ',lineofchars);
           Val(Copy(lineofchars,1,dspos-1),value,icode);
           if value<10.0   then value:=10.0;
           if value>1999.0 then value:=1999.0;
           headlmin   :=value;
           value      :=(headlmin*headlmin)/(headkf*headkf);
           if value<7.0*0.0689 then headlmin:=headkf*Sqrt(7.0*0.0689);
           if value>12.0       then headlmin:=headkf*Sqrt(12.0);
           headbars   :=(headlmin*headlmin)/(headkf*headkf);
           numheads   :=numheads+1;
           headnum    :=numheads;
        end;
//
//  Remember all this data
//
        value             :=(actualm+fittingsm)*605000.0/
                            Exp(Ln(hwcfactor)*1.85)/Exp(Ln(actboremm)*4.87);
        numpipes          :=numpipes+1;
                            if numpipes>1000 then numpipes:=1000;
        PipeI[1 ,numpipes]:=fromnode;    //From or start node 1-9999
        PipeI[2 ,numpipes]:=tonode;      //To or end node 1-9999
        PipeI[3 ,numpipes]:=nomsize;     //size in mm 25-300
        PipeI[4 ,numpipes]:=mmnum;       //size code 1-13
        PipeI[5 ,numpipes]:=ptnum;       //pipe type 1-3
        PipeI[6 ,numpipes]:=upstream;    //upstream pipe number 0-numpipes
        PipeI[7 ,numpipes]:=headnum;     //head number or 0 if none
        PipeI[8 ,numpipes]:=0;           //Spare
        PipeI[9 ,numpipes]:=0;           //spare
        PipeS[1 ,numpipes]:=actualm;     //length in m 0.1-999
        PipeS[2 ,numpipes]:=vertm;       //elevation change in m 0 to +-50
        PipeS[3 ,numpipes]:=actboremm;   //internal diameter in mm
        PipeS[4 ,numpipes]:=fittingsm;   //fittings equivalent length
        PipeS[5 ,numpipes]:=hwcfactor;   //pipe "C" factor 120
        PipeS[6 ,numpipes]:=vertm;       //height of end node in m
        if upstream>0 then Pipes[6,numpipes]:=Pipes[6,upstream]+vertm;
        PipeS[7 ,numpipes]:=headkf;      //0 or head "K" factor 1-999
        PipeS[8 ,numpipes]:=headlmin;    //0 or head minimum flow
        PipeS[9 ,numpipes]:=headbars;    //0 or head min pressure
        PipeS[10,numpipes]:=headlmin;    //actual head flow rate
        PipeS[11,numpipes]:=headbars;    //actual head pressure
        PipeS[12,numpipes]:=value;       //H-W formula so just Q^1.85 to do
        PipeS[13,numpipes]:=0.0;         //Calculated flow rate
        PipeS[14,numpipes]:=0.0;         //Calculated pressure drop
        PipeS[15,numpipes]:=0.0;         //Velocity m/s
        PipeS[16,numpipes]:=0.0;         //Total pressure drop from source
        PipeS[17,numpipes]:=0.0;         //Start pressure bars
        PipeS[18,numpipes]:=0.0;         //End pressure bars
        PipeS[19,numpipes]:=0.0;         //Spare
        PipeF[numpipes]   :=fittslist;   //7 fitting quants
        if (headnum>0) and (PipeS[6,numpipes]>maxheadm) then
                  maxheadm:=PipeS[6,numpipes];
        ss1:=Num2Str(fromnode,4,0)+Num2Str(tonode,5,0)+
             Num2Str(nomsize,4,0)+'  '+Copy(PipeTypes[ptnum],1,4)+
             Num2Str(horizm,8,3)+Num2Str(vertm,8,3)+' '+
             Copy(fittslist,1,numfittings);
        if headkf>0 then ss1:=ss1+Num2Str(headkf,7,1)+
                                  Num2Str(headlmin,8,2);
        WriteLn(inout,ss1);    //same formatted line
    end; //of each pipe line so close backup file
    Close(inout);
//
//                      START CALCULATIONS
//
    ShowError('Started on calculations for '+Num2Str(numpipes,-6,0)+
              ' pipes to '+Num2Str(numheads,-6,0)+' heads',
              'Pipe sizes from '+Num2Str(minsizemm,-6,0)+' to '+
              Num2Str(maxsizemm,-6,0)+' mm and highest head = '+
              Num2Str(maxheadm,-12,3)+' m');
    numsteps     :=0;
    remotepipe   :=0;
    Repeat
      numsteps   :=numsteps+1;
      for pipenum:=1 to numpipes do PipeS[13,pipenum]:=0.0;
//    Cumulate head flows in pipes back to source
      for pipenum:=1 to numpipes do begin
          if PipeI[7,pipenum]>0 then begin
             upstream            :=pipenum;
             pipelmin            :=PipeS[10,pipenum];
             Repeat
               PipeS[13,upstream]:=PipeS[13,upstream]+pipelmin;
               upstream          :=PipeI[6 ,upstream];
             Until upstream=0;
          end; //next head
      end; //next pipe
//    Calculate pipe pressure drops
      for pipenum:=1 to numpipes do begin
          pipebar          :=0.0;
          if PipeS[13,pipenum]>0.001 then   //cant do Ln(0)
             pipebar       :=PipeS[12,pipenum]*
                             Exp(Ln(PipeS[13,pipenum])*1.85);
          PipeS[14,pipenum]:=pipebar;
      end;
//    Calculate total pressure drop to heads
      maxbars :=-99999.9;
      errorbar:=-99999.9;
      for pipenum:=1 to numpipes do PipeS[16,pipenum]:=0.0;
      for pipenum:=1 to numpipes do begin
          if PipeI[7,pipenum]>0 then begin
             upstream            :=pipenum;
             pipebar             :=PipeS[11,pipenum]+
                                   PipeS[6,pipenum]*m2bars;
             Repeat
               pipebar           :=pipebar+PipeS[14,upstream];
               upstream          :=PipeI[6 ,upstream];
             Until upstream=0;
             PipeS[16,pipenum]   :=pipebar;
             if pipebar>maxbars then begin
                maxbars          :=pipebar;
                if numsteps=1 then remotepipe:=pipenum;
             end;
          end; //next head
      end; //next pipe
      sourcelmin:=PipeS[13,1];
      sourcebars:=PipeS[16,remotepipe];
      totallmin :=0.0;
//    Check against specified source if given
      if (staticp >1.0)    and
         (residp  >0.8)    and
         (residf  >400.0)  then begin
            sourcebars:=staticp-(staticp-residp)*
                        Exp(Ln(sourcelmin/residf)*1.85);
            if sourcebars<=residp then sourcebars:=residp;
      end;
//    Calculate new flows / pressures at heads
      for pipenum:=1 to numpipes do begin
          if PipeI[7,pipenum]>0 then begin
             value            :=PipeS[11,pipenum];
             PipeS[11,pipenum]:=value+0.5*
                                (sourcebars-PipeS[16,pipenum]);
             if PipeS[11,pipenum]<0.001 then
                PipeS[11,pipenum]:=0.001;
             PipeS[10,pipenum]:=PipeS[7,pipenum]*
                                Sqrt(PipeS[11,pipenum]);
             totallmin        :=totallmin+PipeS[10,pipenum];
             if Abs(PipeS[11,pipenum]-value)>errorbar then
                     errorbar:=Abs(PipeS[11,pipenum]-value);
          end; //next head
      end; //next pipe
//    Remove comments to see progress of calcs
      {ShowError('Step '+Num2Str(numsteps,5,0)+Num2Str(sourcelmin,12,3)+
                ' L/min at'+Num2Str(sourcebars,12,6)+' bar',
                'Errors ='+Num2Str(Abs(sourcelmin-totallmin),12,6)+
                ' L/min and'+Num2Str(errorbar,12,6)+' bar',);}
    Until (errorbar<0.0005) or (numsteps>299);
//
//  Calculations all balanced - do velocities / end pressures
//
    maxmpers   :=0.0;
    numnoflow  :=0;
    headsunder :=0;
    headsover  :=0;
    for pipenum:=1 to numpipes do begin
        PipeS[15,pipenum]   :=PipeS[13,pipenum]*1000.0/60.0/
                              (Pi*PipeS[3,pipenum]*PipeS[3,pipenum]);
        if PipeS[15,pipenum]>maxmpers    then maxmpers:=PipeS[15,pipenum];
        if PipeS[13,pipenum]<0.01        then
                 numnoflow  :=numnoflow+1;
        value               :=PipeS[2,pipenum]*m2bars;
        upstream            :=PipeI[6,pipenum];
        if pipenum=1  then begin   //first pipe
           PipeS[17,1]      :=sourcebars;
           PipeS[18,1]      :=sourcebars-PipeS[14,1]-value;
        end;
        if upstream>0 then begin   //subsequent pipes
           PipeS[17,pipenum]:=PipeS[18,upstream];
           PipeS[18,pipenum]:=PipeS[17,pipenum]-PipeS[14,pipenum]-value;
        end;
        if PipeI[7,pipenum]>0 then begin  //check over / under pressure
           if PipeS[11,pipenum]>=12.0 then headsover:=headsover+1;
           if (PipeS[10,pipenum]<=PipeS[8,pipenum]-0.001) or
              (PipeS[11,pipenum]<=PipeS[9,pipenum]-0.001) then
               headsunder   :=headsunder+1;
        end;
    end; //next pipe
    ShowError('Source flow = '+Num2Str(sourcelmin,-12,1)+' L/min at '+
              Num2Str(sourcebars,-12,3)+' bars','Balanced in '+
              Num2Str(numsteps,-6,0)+' steps, errors '+
              Num2Str(errorbar,-12,6)+' bars + '+
              Num2Str(Abs(sourcelmin-totallmin),-12,3)+' L/min');
//  Give any error messages
    if (sourcelmin>=30000.0) or (sourcebars>=30.0) then
        ShowError('This appears much too high to be right',
                  'Perhaps wrong K factors, min flows or undersized'+
                  ' pipes');
    if headsunder>0   then
        ShowError('There are '+Num2Str(headsunder,-6,0)+
                  ' heads under their minimum flows / pressures',
                  'Perhaps caused by excessive pressure drops,'+
                  ' undersized pipes or inadequate water supply');
    if headsover >0   then
       ShowError('There are '+Num2Str(headsover,-6,0)+
                 ' heads with pressures over 12 bars',
                 'Perhaps too high water supply or execessive'+
                 ' flows based on their K factors');
    if maxmpers  >20  then
       ShowError('Maximum velocity is '+
                 Num2Str(maxmpers,-9,2)+' m/s',
                 'Perhaps one or more pipes are undesized or flows'+
                 ' are higher than anticipated');
    if numnoflow>0    then
       ShowError('There are '+Num2Str(numnoflow,-6,0)+
                 ' pipes with zero flow rate',
                 'Perhaps they do not feed any operating heads or'+
                 ' you have not finished the input data yet');
    if (staticp>1.0) and (sourcelmin>residf) then
       ShowError('The source flow of '+
                 Num2Str(sourcelmin,-9,1)+' L/min exceeds the '+
                 Num2Str(residf,-9,1)+' L/min given in'+
                 ' question 15',
                 'The Q15 value has been ignored so the source'+
                 ' pressure is the '+Num2Str(residp,-9,3)+
                 ' bars in Q14');
//
//                START ON RESULTS PRINTOUT
//
    Assign (inout,resultsfile);
    ReWrite(inout);
    WriteLn(inout,'START OF RESULTS PRINTOUT FROM SHP4FPC BY ALAN ASHFIELD');
    WriteLn(inout,' ');
//  First 18 lines of Questions
    for linenum:=1 to 18 do begin
        ss1    :=Questions[linenum]+' '+JobData[linenum];
        WriteLn(inout,ss1);
    end;
    WriteLn(inout,' ');
//  Source duty
    ss1        :='SOURCE DUTY AT NODE '+Num2Str(sourcenode,-6,0)+' = '+
                 Num2Str(sourcelmin,-9,1)+' L/min at '+
                 Num2Str(sourcebars,-9,3)+' bars';
    WriteLn(inout,ss1);
    WriteLn(inout,' ');
//  Remotest head
    ss1        :='Remote head is at node '+
                 Num2Str(PipeI[2, remotepipe],-6,0)+' = '+
                 Num2Str(PipeS[13,remotepipe],-9,1)+' L/min, '+
                 Num2Str(PipeS[18,remotepipe],-9,3)+' bars and '+
                 Num2Str(PipeS[6, remotepipe],-9,3)+' m';
    WriteLn(inout,ss1);
    WriteLn(inout,' ');
//  Any other summaries you want can go here
//
//  Show headings
//
    WriteLn(inout,'Node1                  Flow Q  S i z e  Fittings'+
                  '  Length    Type      Pt');
    WriteLn(inout,'      Heights Kfactor  Head q     mm     ELT4BGS'+
                  '   Fitts  Cfactor     Pe');
    WriteLn(inout,'Node2                   L/min  B o r e   LTX5VVC'+
                  '  Totalm   mbar/m     Pf');
    underline:=   '------------------------------------------------'+
                  '--------------------------';
    WriteLn(inout,underline);
    for pipenum:=1 to numpipes do begin
//  Line 1 of 3
        ss1:=Num2Str(PipeI[1 ,pipenum],5,0)+
             Num2Str(PipeS[6 ,pipenum]-PipeS[2,pipenum],8,3)+
             '        '+
             Num2Str(PipeS[13,pipenum],8,1)+
             Num2Str(PipeI[3 ,pipenum],6,0)+'   '+'          '+
             Num2Str(PipeS[1 ,pipenum],8,3)+'    '+
             Copy(PipeTypes[PipeI[5,pipenum]],1,4)+' '+
             Num2Str(PipeS[17,pipenum],9,3);
        WriteLn(inout,ss1);
//  Line 2 of 3
        ss2:=Num2Str(PipeS[7 ,pipenum],8,1);
        ss3:=Num2Str(PipeS[10,pipenum],8,1);
             if PipeS[7,pipenum]<0.1 then ss2:='        ';
             if PipeS[7,pipenum]<0.1 then ss3:='        ';
        ss1:='     '+'        '+ss2+ss3+'           '+' '+
             PipeF[pipenum]+
             Num2Str(PipeS[4,pipenum],8,3)+
             Num2Str(PipeS[5,pipenum],7,0)+'  '+
             Num2Str(PipeS[2,pipenum]*m2bars,9,3);
        WriteLn(inout,ss1);
//  Line 3 of 3
        value:=PipeS[14,pipenum]*1000.0/
               (PipeS[1,pipenum]+PipeS[4,pipenum]);
        ss1:=Num2Str(PipeI[2,pipenum],5,0)+
             Num2Str(PipeS[6,pipenum],8,3)+'        '+'        '+
             Num2Str(PipeS[3,pipenum],9,2)+'          '+
             Num2Str(PipeS[1,pipenum]+PipeS[4,pipenum],8,3)+
             Num2Str(value,9,1)+Num2Str(PipeS[14,pipenum],9,3);
        WriteLn(inout,ss1);
        WriteLn(inout,underline);
    end; //next pipe
    WriteLn(inout,' ');
    WriteLn(inout,'END OF PRINTOUT FROM SHP4FPC BY ALAN ASHFIELD');
    Close(inout);
    ShowError('Results now stored in '+resultsfile,
              'End of SHP4FPC by Alan Ashfield - www.freehc.net');
end;
//
//                 MAIN PROGRAM STARTS HERE
//
begin
    if FileExists(ParamStr(1))=False then
       ShowError('You have missed out the job file name'+
                 ' - correct command is',
                 'shp4fpc \directory\file name '+
                 '     eg. shp4fpc \jobs\myjob1.txt');
    if FileExists(ParamStr(1))=True then AllInOne(ParamStr(1));
end.                          //of shp4fpc by Alan Ashfield

Typical Data File - 2 ranges of 3 heads

First Test Example
1
Somewhere over the rainbow
Drawings
Most Remote
Dont know
Ordinary Hazard
Put your name here
Your address
Alan Ashfield
AHJ
Calculate min duty
0
0
0
54.0
10.00
9.00
Q19
Q20
Q21
Q22
Q23
From  To  Size Type  Pipe lengths   ELT4BGS  Head / Nozzle
node node  mm       Horiz m  Vert m LTX5VVC  Kfact   L/min
100  110  100   S40   0.0    1.5    000001
110  120  100   S40   0.0    3.0    00001
120  130  100   S40   30     0.0    1
130  131   40   S40   0.0    0.5    001
131  132   40   S40   1.0    0.0    1          80    90
132  133   32   S40   3.0    0.0    0          80    90
133  134   25   S40   3.0    0.0    0          80    90
130  140   65   S40   3.0    0.0    0
140  141   40   S40   0.0    0.5   001
141  142   40   S40   1.0    0.0    1          80    90
142  143   32   S40   3.0    0.0    0          80    90
143  144   25   S40   3.0    0.0    0          80    90

That's All Folks ...

Really critical or observant people may have realised that there are one or two superfluous equals signs '=' in some if---then statements above - although my underlying source code was right, some browsers corrupted the following lines - presumably looking at the '>' and '<' symbols and changing the HTML code! I could not find anyway around this - right click on the code and select "View Source ..." to check.