Sunday, July 23, 2017

#SWMM5 Delphi Pascal unit that manages the list of external tools made available through the Tools item on the Main Menu.

unit Utools;

{-------------------------------------------------------------------}
{                    Unit:    Utools.pas                            }
{                    Project: EPA SMM                               }
{                    Version: 5.1                                   }
{                    Date:    12/2/13     (5.1.000)                 }
{                             08/19/14    (5.1.007)                 }
{                             03/19/15    (5.1.008)                 }
{                    Author:  L. Rossman                            }
{                                                                   }
{   Delphi Pascal unit that manages the list of external tools      }
{   made available through the Tools item on the Main Menu.         }
{-------------------------------------------------------------------}

interface

uses
  Classes, Dialogs, Menus, SysUtils, IniFiles, Windows, StrUtils,
  Forms, Uglobals;

// Data structure that records info for each external SWMM tool
type
  TTool = class(TObject)
    ExeName:     String;
    DirName:     String;
    Params:      String;
    DisableSWMM: Boolean;
    UpdateSWMM:  Boolean;
    MnuItem:     TMenuItem;
  end;

const

  // Name of INI file where tool info is stored
  TOOLSFILE = 'swmm5tools.ini';

  // Names used to designate specific project attributes when used
  // as arguments in the command that launches a tool
  Macros: array[0..5] of String =
    ('$PROJDIR',        // Current project directory
     '$SWMMDIR',        // SWMM's program directory
     '$INPFILE',        // Text file containing current project data
     '$RPTFILE',        // Text file to contain a status report
     '$OUTFILE',        // Binary file to contain analysis results
     '$RIFFILE');       // SWMM runoff interface file

var
  ToolList: TStrings;   // Collection of registered tools

procedure OpenToolList;
procedure CloseToolList;
procedure UpdateTool(I: Integer; S1, S2, S3, S4: String; Flag1: Boolean;
          Flag2: Boolean);
procedure DeleteTool(I: Integer);
procedure ExchangeTools(const I1: Integer; const I2: Integer);
function  GetToolName(I: Integer): String;
procedure RunTool(S: String);
function  GetTmpInpFileName: String;

implementation

uses
  Fmain, Fmap, Fproped, Ubrowser, Uimport, Uexport, Uoutput, Uutils;

var
  Tool: TTool;


procedure ReadToolsIniFile;
//-----------------------------------------------------------------------------
//  Reads the collection of tools that were last registered by the user
//  from the swmm5tools.ini file.
//-----------------------------------------------------------------------------
var
  I: Integer;
  Sections: TStringList;
  S1, S2, S3, S4: String;
  Flag1: Boolean;
  Flag2: Boolean;
begin
  // Info on each tool is written to its own section of the ini file
  Sections := TStringlist.Create;
  with TIniFile.Create(IniFileDir + TOOLSFILE) do
  try
    // Read all sections of the ini file (this is a Delphi function)
    ReadSections(Sections);

    // Parse the info for each tool from its particular section
    for I := 0 to Sections.Count-1 do
    begin
      S1 := Sections[I];                     // This is the tool's name
      S2 := ReadString(S1, 'ExeName', '');
      S3 := ReadString(S1, 'DirName', '');
      S4 := ReadString(S1, 'Params', '');
      Flag1 := ReadBool(S1, 'Disabled', False);
      Flag2 := ReadBool(S1, 'Update', False);

      // Add this tool to the collection (-1 as first argument to
      // UpdateTool means we are adding rather than updating a tool)
      if Length(S2) > 0
      then UpdateTool(-1, S1, S2, S3, S4, Flag1, Flag2);
    end;
  finally
    Free;
    Sections.Free;
  end;
end;


procedure WriteToolsIniFile;
//-----------------------------------------------------------------------------
//  Writes the current collection of registered tools to the swmm5tools.ini
//  file.
//-----------------------------------------------------------------------------
var
  I: Integer;
  S1: String;
begin
  with TMemIniFile.Create(IniFileDir + TOOLSFILE) do
  try
    Clear;
    for I := 0 to ToolList.Count-1 do
    begin
      S1 := ToolList[I];
      Tool := TTool(ToolList.Objects[I]);
      WriteString(S1, 'ExeName', Tool.ExeName);
      WriteString(S1, 'DirName', Tool.DirName);
      WriteString(S1, 'Params', Tool.Params);
      WriteBool(S1, 'Disabled', Tool.DisableSWMM);
      WriteBool(S1, 'Update', Tool.UpdateSWMM);
    end;
    UpdateFile;
  finally
    Free;
  end;
end;


function GetNewTool: TTool;
//-----------------------------------------------------------------------------
//  Creates a new tool and its associated menu item under the Main Menu's
//  Tools menu.
//-----------------------------------------------------------------------------
begin
  Tool := TTool.Create;
  Tool.MnuItem := TMenuItem.Create(MainForm);
  Tool.MnuItem.OnClick := MainForm.ToolItemClick;
  MainForm.MnuTools.Add(Tool.MnuItem);
  Result := Tool;
end;


procedure OpenToolList;
//-----------------------------------------------------------------------------
//  Creates a collection of tools and populates it with the tools
//  registered at the end of the user's last session.
//-----------------------------------------------------------------------------
begin
  ToolList := TStringlist.Create;
  ReadToolsIniFile;
end;


procedure CloseToolList;
//-----------------------------------------------------------------------------
//  Saves the current collection of tools to file and then deletes them.
//-----------------------------------------------------------------------------
var
  I: Integer;
begin
  WriteToolsIniFile;
  with ToolList do
  try
   for I := (Count - 1) downto 0 do DeleteTool(I);
  finally
    ToolList.Free;
  end;
end;


function GetToolName(I: Integer): String;
//-----------------------------------------------------------------------------
//  Retrieves the name of the I-th tool in the tools collection.
//-----------------------------------------------------------------------------
begin
  if (I < 0) or (I >= ToolList.Count)
  then Result := ''
  else Result := ToolList[I];
end;


procedure UpdateTool(I: Integer; S1, S2, S3, S4: String; Flag1: Boolean;
  Flag2: Boolean);
//-----------------------------------------------------------------------------
//  Updates the properties of the tool whose index is I (if I is -1 then a
//  new tool is created and added to the tools collection).
//-----------------------------------------------------------------------------
begin
  if I < 0 then Tool := GetNewTool
  else Tool := TTool(ToolList.Objects[I]);
  Tool.ExeName := S2;
  Tool.DirName := S3;
  Tool.Params := S4;
  Tool.DisableSWMM := Flag1;
  Tool.UpdateSWMM := Flag2;
  Tool.MnuItem.Caption := S1;
  if I < 0 then ToolList.AddObject(S1, Tool)
  else ToolList[I] := S1;
end;


procedure DeleteTool(I: Integer);
//-----------------------------------------------------------------------------
//  Deletes tool I from the tools collection.
//-----------------------------------------------------------------------------
begin
  Tool := TTool(ToolList.Objects[I]);
  MainForm.MnuTools.Remove(Tool.MnuItem);
  Tool.MnuItem.Free;
  Tool.Free;
  ToolList.Delete(I);
end;


procedure ExchangeTools(const I1: Integer; const I2: Integer);
//-----------------------------------------------------------------------------
//  Exchanges the positions of tools I1 and I2 in the tools collection.
//-----------------------------------------------------------------------------
var
  I: Integer;
begin
  ToolList.Exchange(I1, I2);
  for I := 0 to ToolList.Count-1 do
  begin
    Tool := TTool(ToolList.Objects[I]);
    MainForm.MnuTools.Remove(Tool.MnuItem);
  end;
  for I := 0 to ToolList.Count-1 do
  begin
    Tool := TTool(ToolList.Objects[I]);
    MainForm.MnuTools.Add(Tool.MnuItem);
  end;
end;


function GetDir: String;
//-----------------------------------------------------------------------------
//  Replaces a directory macro with the actual directory name (which can
//  either be the current project directory or the SWMM program directory).
//-----------------------------------------------------------------------------
var
  S: String;
begin
  S := Tool.DirName;
  S := AnsiReplaceText(S, Macros[0], ProjectDir);
  S := AnsiReplaceText(S, Macros[1], EpaSwmmDir);
  Result := S;
end;


function GetTmpInpFileName: String;
//-----------------------------------------------------------------------------
//  Replaces the SWMM input file name macro with the name of a temporary
//  file to which the current project's input data has been written.
//-----------------------------------------------------------------------------
var
  S: TStringlist;
begin
  // Create a new temporary input file
  SysUtils.DeleteFile(Uglobals.TempInputFile);
  Uglobals.TempInputFile  := Uutils.GetTempFile(Uglobals.TempDir, 'swmm');

  // Export the current SWMM project data to this file
  S := TStringlist.Create;
  try
    Uexport.ExportProject(S);
    S.AddStrings(Project.Options.Report);
    Uexport.ExportTags(S);
    Uexport.ExportMap(S);
    Uexport.ExportProfiles(S);
    S.SaveToFile(TempInputFile);
  finally
    S.Free;
  end;
  Result := Uglobals.TempInputFile;
end;


function GetTmpRptFileName: String;
//-----------------------------------------------------------------------------
//  Replaces the SWMM report file name macro with the name of a temporary
//  report file.
//-----------------------------------------------------------------------------
begin
  if not FileExists(Uglobals.TempReportFile)
  then Uglobals.TempReportFile := Uutils.GetTempFile(Uglobals.TempDir, 'swmm');
  Result := Uglobals.TempReportFile;
end;


function GetTmpOutFileName: String;
//-----------------------------------------------------------------------------
//  Replaces the SWMM output file name macro with the name of a temporary
//  output file.
//-----------------------------------------------------------------------------
begin
  if not FileExists(Uglobals.TempOutputFile)
  then Uglobals.TempOutputFile := Uutils.GetTempFile(Uglobals.TempDir, 'swmm');
  Result := Uglobals.TempOutputFile;
end;


function GetRIFFileName: String;
//-----------------------------------------------------------------------------
//  Replaces the runoff interface file name macro with the name of
//  the current runoff interface file (or a blank string if no such file
//  exists).
//-----------------------------------------------------------------------------
var
  I: Integer;
  N: Integer;
  S: String;
  Tokens: TStringlist;
begin
  Result := '';
  Tokens := TStringlist.Create;
  try
    for I := 0 to Project.IfaceFiles.Count-1 do
    begin
      S := Project.IfaceFiles[I];
      Uutils.Tokenize(S, Tokens, N);
      if N >= 3 then
      begin
        if SameText(Tokens[1], 'RUNOFF') then Result := Tokens[2];
      end;
    end;
  finally
    Tokens.Free;
  end;
end;


function GetParams: String;
//-----------------------------------------------------------------------------
//  Replaces any macros appearing in a tool's command line parameters
//  with their actual names.
//-----------------------------------------------------------------------------
var
  S: String;
begin
  S := Tool.Params;
  S := AnsiReplaceText(S, Macros[0], Uglobals.ProjectDir);
  S := AnsiReplaceText(S, Macros[1], Uglobals.EpaSwmmDir);
  S := AnsiReplaceText(S, Macros[2], GetTmpInpFileName);
  S := AnsiReplaceText(S, Macros[3], GetTmpRptFileName);
  S := AnsiReplaceText(S, Macros[4], GetTmpOutFileName);
  S := AnsiReplaceText(S, Macros[5], GetRIFFileName);
  Result := S;
end;


procedure UpdateSWMMInput(const OutputUpdated: Boolean);
//-----------------------------------------------------------------------------
//  Replaces the current project data with that contained in the temporary
//  input file that was updated by a particular tool.
//-----------------------------------------------------------------------------
begin
  // Prepare the GUI to receive an updated set of project data
  PropEditForm.Hide;
  Project.Clear;
  Project.InitCurrentItems;
  Ubrowser.InitDataPage;

  // Import the project data contained in the temporary input file
  Uglobals.InputFileType := Uimport.OpenProject(Uglobals.TempInputFile);
  Uglobals.HasChanged := True;
  Uglobals.UpdateFlag := True;

  // Refresh any existing simulation results if the tool did
  // not update these as well
  if Uglobals.RunFlag and not OutputUpdated then
  begin
    Ubrowser.InitMapPage;
    MainForm.RefreshResults;
  end
  else MapForm.RedrawMap;
  MainForm.ShowRunStatus;
end;


procedure UpdateSWMMOutput;
//-----------------------------------------------------------------------------
//  Allows a new set of simulation results that were generated by the
//  tool to be displayed by the program's user interface.
//-----------------------------------------------------------------------------
begin
  Uoutput.ClearOutput;
  Ubrowser.InitMapPage;
  if GetFileSize(Uglobals.TempReportFile) <= 0
  then Uglobals.RunStatus := rsFailed
  else Uglobals.RunStatus := Uoutput.CheckRunStatus(Uglobals.TempOutputFile);
  if Uglobals.RunStatus in [rsSuccess, rsWarning]
  then Uglobals.RunFlag := True
  else Uglobals.RunFlag := False;
  if Uglobals.RunStatus = rsError
  then MainForm.MnuReportStatusClick(MainForm);
  MainForm.RefreshResults;
  MainForm.RefreshForms;
end;


procedure RunTool(S: String);
//-----------------------------------------------------------------------------
//  Launches the tool whose name is S.
//-----------------------------------------------------------------------------
var
  I, R: Integer;
  CmdLine: String;
  DirName: String;
  Params:  String;
  OutputUpdated: Boolean;
  OldInpTimeStamp: TDateTime;
  NewInpTimeStamp: TDateTime;
begin
  //Remove any menu accelerator key symbol from the tool's name
  Delete(S, Pos('&', S), 1);

  // Locate the index of the tool in the tool collection
  I := ToolList.IndexOf(S);
  if (I < 0)
  then Uutils.MsgDlg(S + ' is not a registered tool.', mtWarning, [mbOK])      //(5.1.008)
  else
  begin

    // Generate the command line for launching the tool
    Tool := TTool(ToolList.Objects[I]);
    DirName := GetDir;
    Params  := GetParams;
    CmdLine := Tool.ExeName + ' ' + Params;

    // Save SWMM input file's time stamp
    if AnsiContainsText(Params, Uglobals.TempInputFile) then
      FileAge(Uglobals.TempInputFile, OldInpTimeStamp)
    else OldInpTimeStamp := 0;

    // Launch the tool and disable SWMM if called for
    try
      if Tool.DisableSWMM then MainForm.Hide;
      R := Uutils.WinExecAndWait(CmdLine, DirName, SW_SHOWNORMAL,
                                 Tool.DisableSWMM);
    finally
      MainForm.Show;
    end;
    if R < 0 then
    begin
      Uutils.MsgDlg('Could not launch ' + ToolList[I], mtWarning, [mbOK]);     //(5.1.008)
      Exit;                                                                    //(5.1.007)
    end;

    // Update SWMM if called for
    if Tool.UpdateSWMM then
    begin

      // See if tool updates the simulation results
      OutputUpdated := False;
      if AnsiContainsText(Params, Uglobals.TempOutputFile)
      then OutputUpdated := True;

      // Update project's input data if called for
      if AnsiContainsText(Params, Uglobals.TempInputFile) then
      begin
        FileAge(Uglobals.TempInputFile, NewInpTimeStamp);
        if (OldInpTimeStamp > 0)
        and (OldInpTimeStamp < NewInpTimeStamp)
        then UpdateSWMMInput(OutputUpdated);
      end;

      // Update simulation results if called for
      if OutputUpdated then UpdateSWMMOutput;
    end;

  end;
end;

end.

#SWMM5 Delphi Pascal unit that retrieves a time series of measured observation data from a calibration file for use in a time series plot

unit Ucalib;

{-------------------------------------------------------------------}
{                    Unit:    Ucalib.pas                            }
{                    Project: EPA SWMM                              }
{                    Version: 5.1                                   }
{                    Date:    12/2/13      (5.1.000)                }
{                             03/19/15     (5.1.008)                }
{                    Author:  L. Rossman                            }
{                                                                   }
{   Delphi Pascal unit that retrieves a time series of measured     }
{   observation data from a calibration file for use in a time      }
{   series plot.                                                    }
{-------------------------------------------------------------------}

//  Mapping of view variable indexes to calibration file +indexes:
//  Variable                    Calibration File Index
//  ------------                ----------------------
//  Subcatchment RUNOFF          1
//  Subcatchment SNOWDEPTH       10
//  Subcatchment GW_FLOW         8
//  Subcatchment GW_ELEV         9
//  Subcatchment SUBCATCHQUAL    2
//  Node NODEDEPTH               3
//  Node LATFLOW                 6
//  Node OVERFLOW                7
//  Node NODEQUAL                5
//  Link FLOW                    4
//  Link DEPTH                   11
//  Link VELOCITY                12

interface

uses
  Classes, SysUtils, TeEngine, Series, Chart, Uglobals, Uutils, Dialogs;

procedure GetCalibData(VarIndex: Integer; ObjType: Integer; LocID: String;
  const aStartDate: TDateTime; const aEndDate: TDateTime;
  const aTimeFactor: Double;  const aDateTimeDisplay: Boolean;
  ChartSeries: TChartSeries);

implementation

var
  Toklist  : TStringList;
  StartDate: TDateTime;
  EndDate  : TDateTime;
  TimeFactor: Double;
  DateTimeDisplay: Boolean;
  SimStartDate: TDateTime;

procedure GetCalibFileIndex(VarIndex: Integer; ObjType: Integer;
  var FileIndex: Integer; var VarOffset: Integer);
begin
  FileIndex := 0;
  VarOffset := 0;

  // Variable is for a subcatchment
  if (ObjType = SUBCATCHMENTS) then
  begin
    if VarIndex = RUNOFF    then FileIndex := 1;
    if VarIndex = SNOWDEPTH then FileIndex := 10;
    if VarIndex = GW_FLOW   then FileIndex := 8;
    if VarIndex = GW_ELEV   then FileIndex := 9;
    if VarIndex >= SUBCATCHQUAL then
    begin
      FileIndex := 2;
      VarOffset := VarIndex - SUBCATCHQUAL;
    end;
  end

  // Variable is for a node
  else if (ObjType = NODES) then
  begin
    if VarIndex = NODEDEPTH then FileIndex := 3;
    if VarIndex = LATFLOW   then FileIndex := 6;
    if VarIndex = OVERFLOW  then FileIndex := 7;
    if VarIndex >= NODEQUAL then
    begin
      FileIndex := 5;
      VarOffset := VarIndex - NODEQUAL;
    end;
  end

  // Variable is for a link
  else if (ObjType = LINKS) then
  begin
    if VarIndex = FLOW then FileIndex := 4;
    if VarIndex = LINKDEPTH then FileIndex := 11;
    if VarIndex = VELOCITY  then FileIndex := 12;
  end;
end;

function AddCalibDataPoint(ChartSeries: TChartSeries; const DataTok: Integer): Boolean;
//-----------------------------------------------------------------------------
// Adds a calibration data value to the chart data series where
// DataTok = index of the token that contains the measurement value.
//-----------------------------------------------------------------------------
var
  Value: Extended;
  Days : Extended;
  Hours: TDateTime;
  X    : TDateTime;
  aDate: String;
begin
  try
    // Assume the data measurement time is a date/time value
    // (If its not, an exception will occur which is handled below).
    Result := False;
    aDate := TokList[0];
    Days := StrToDate(Uutils.ConvertDate(aDate), Uglobals.MyFormatSettings);
    Hours := Uutils.StrHoursToTime(TokList[1]);
    if Hours < 0 then Exit;
    X := Days + Hours;
  except
    // An exception occurs if measurement time is actually elapsed days/hours
    on EconvertError do
    begin
      Result := False;
      if not Uutils.GetExtended(TokList[0], Days) then Exit;
      Hours := Uutils.StrHoursToTime(TokList[1]);
      if Hours < 0 then Exit;

      // Adjust date to simulation start date.
      X := SimStartDate + Days + Hours;

    end;
  end;

  // Check that measurement time falls within plotting interval
  if (X < StartDate) or (X > EndDate) then Exit;

  // If the chart is not using date/time for X-axis,
  // convert measurement time to elapsed time if need be

  //  Adjust date to simulation start date.
  if not DateTimeDisplay then X := (X - SimStartDate)*TimeFactor;

  Result := True;

  // If the DataTok token contains a valid value then add
  // that value and its time to the calibration point series
  if Uutils.GetExtended(TokList[DataTok], Value)
  then with ChartSeries as TPointSeries do AddXY(X, Value, '', clTeeColor);
end;

procedure GetCalibData(VarIndex: Integer; ObjType: Integer; LocID: String;
  const aStartDate: TDateTime; const aEndDate: TDateTime;
  const aTimeFactor: Double;  const aDateTimeDisplay: Boolean;
  ChartSeries: TChartSeries);
var
  FileIndex: Integer;             //Index of calibration file
  VarOffset: Integer;             //Offset of variable in file
  P        : Integer;
  Ntoks    : Integer;
  DataTok  : Integer;
  UseData  : Boolean;
  F        : TextFile;
  Fname    : String;
  Line     : String;
  S        : String;
begin
  // Determine which calibration file corresponds to the variable of interest
  GetCalibFileIndex(VarIndex, ObjType, FileIndex, VarOffset);
  if FileIndex = 0 then Exit;

  // Check if the calibration file for the variable of interest
  // has data for the location of interest
  if (Pos('"'+LocID+'"', CalibData[FileIndex].Locations) = 0) then Exit;

  // Save shared variables
  StartDate := aStartDate;
  EndDate := aEndDate;
  TimeFactor := aTimeFactor;
  DateTimeDisplay := aDateTimeDisplay;

  // Compute simulation start date which is one reporting
  // period before the first reporting date.
  SimStartDate := StartDateTime - DeltaDateTime;

////  Following code segment re-written for release 5.1.008.  ////             //(5.1.008)
  // Try to open the calibration data file
  Fname := CalibData[FileIndex].FileName;
  if FileExists(Fname) then
  try
    // Create a list of string tokens
    Toklist := TStringList.Create;
    AssignFile(F,Fname);
    {$I-}
    Reset(F);
    {$I+}
    if IOResult = 0 then
    begin

      // Data token index is 2 + index of measurement variable
      DataTok := 2 + VarOffset;
      UseData := False;

      // Read lines from file until reach end of file
      while not EOF(F) do
      begin
        Readln(F, Line);
        S := Line;

        // Remove any comment & tokenize the line
        P := Pos(';', S);
        if (P > 0) then Delete(S, P, 256);
        Uutils.Tokenize(S, Toklist, Ntoks);

        // A single entry marks start of data for a new location.
        // If this is the location of interest, set UseData to True
        if (Ntoks = 1) then
        begin
          if (Toklist[0] = LocID) then
            UseData := True
          else
            UseData := False;
        end

        // If line has enough items then add data point to data lists
        else if (Ntoks > DataTok) and (UseData) then
        begin
          AddCalibDataPoint(ChartSeries, DataTok);
        end;
      end;
    end;

    // After finished reading the file, free the token list.
  finally
    Toklist.Free;
    CloseFile(F);
  end;
////////////////////////////////////////////////////////////////////////
end;

end.

Friday, July 21, 2017

Contents of EPANET2.ZIP

Contents of EPANET2.ZIP

=======================
This archive contains the source code for the EPANET 2
network hydraulic and water quality solver. The solver
provides functions for simulating the extended period
hydraulic and water quality behavior of water distribution
system pipe networks. It is written in ANSI-compatible C
and can be compiled into either a Windows Dynamic Link
Library of functions or into a command-line executable.

The archived code is set up for compilation as a DLL.
To compile it as a command line (or console) application
simply comment out the "#define DLL" macro statement at
the top of EPANET.C and un-comment the "#define CLE" macro.

The DLL version of the solver (epanet2.dll) is used with
the EPANET 2 user interface executable (epanet2w.exe) to
form a complete Windows modeling package. It also serves
as the function library for the EPANET Programmer's Toolkit,
allowing developers to construct their own customized pipe
network analysis applications.

The following C-code files are included in this archive:
    EPANET.C  -- main module providing supervisory control
    INPUT1.C  -- controls processing of input data
    INPUT2.C  -- reads data from input file
    INPUT3.C  -- parses individual lines of input data
    INPFILE.C -- saves modified input data to a text file
    RULES.C   -- implements rule-based control of piping system
    HYDRAUL.C -- computes extended period hydraulic behavior
    QUALITY.C -- tracks transport & fate of water quality
    OUTPUT.C  -- handles transfer of data to and from binary files
    REPORT.C  -- handles reporting of results to text file
    SMATRIX.C -- sparse matrix linear equation solver routines
    MEMPOOL.C -- memory pool management routines
    HASH.C    -- hash table routines

Also included are the following header files:
    TOOLKIT.H  -- function prototypes of exported DLL functions
    FUNCS.H    -- prototypes of all other functions
    TYPES.H    -- declaration of global constants and data structures
    VARS.H     -- declaration of global variables
    HASH.H     -- header file for hash table routines
    MEMPOOL.H  -- header file for memory pool routines
    ENUMSTXT.H -- string constants for enumerated types
    TEXT.H     -- declaration of all other string constants

The comments at the top of each file lists the date when the latest
update was made, and these updates can be located in the code by
searching for comments with the phrase "/*** Updated" or with the
release number (e.g., 2.00.12) in them.

Other useful documentation that can be consulted includes the EPANET
Programmers Toolkit Help file and the EPANET Version 2 Users Manual.

Thursday, July 20, 2017

TeeChart Standard v2017 VCL/FMX works with Delphi 10.1 Berlin and 10.2 Tokyo Starter, #SWMM5

For those interested in using the latest Delphi GUI with SWMM 5.1.012 and before.  TeeChart is the needed graphics.  The latest TeeChart Standard v2017 VCL/FMX works with Delphi 10.1 Berlin and 10.2 Tokyo Starter, but there are not specific installers, you should install theTeeChart Standard Starter packages manually following the steps below:

1- Open the Components->Installation Packages and add the DclFMXTeeStd924.bpl and DclTeeStd924.bpl from installation folder, concretely you find the packagesin the %Program Files (x86)%\Steema Software\Steema TeeChart Standard VCL FMX 2016.18\Delphi24\bin

The below image shows you where:
imap://sandra%40steema%2Ecom@vqs505.pair.com:993/fetch%3EUID%3E/INBOX%3E14206?header=quotebody&part=1.2&filename=image001.jpg


2- Then you should see in the Install Packages window the results below:
imap://sandra%40steema%2Ecom@vqs505.pair.com:993/fetch%3EUID%3E/INBOX%3E14206?header=quotebody&part=1.3&filename=image002.jpg


3- Create a simple application and run it.

Note the steps are for Rad Studio 10.1 Berlin, but you should follow the same steps to install Teechart Standard in Rad Studio 10.2 Tokyo.

Sunday, July 16, 2017

#SWMM5 Storm Water Management Model: Performance Review and Gap Analysis

Storm Water Management Model: Performance Review and Gap Analysis - link to PDF 

Storm Water Management Model: Performance Review and Gap Analysis
Mehran Niazi; Chris Nietch; Mahdi Maghrebi, A.M.ASCE; Nicole Jackson; Brittany R. Bennett; Michael Tryby; and Arash Massoudieh, M.ASCE


Abstract: The storm water management model (SWMM) is a widely used tool for urban drainage design and planning. Hundreds of peer reviewed
articles and conference proceedings have been written describing applications of SWMM. This review focuses on collecting information
on model performance with respect to calibration and validation in the peer-reviewed literature. The major developmental history
and applications of the model are also presented. The results provide utility to others looking for a quick reference to gauge the integrity of
their own unique SWMM application. A gap analysis assesses the model’s ability to perform water-quality simulations considering green
infrastructure (GI)/low impact development (LID) designs and effectiveness. It is concluded that the level of detail underlying the conceptual
model of SWMM versus its overall computational parsimony is well balanced—making it an adequate model for large and medium-scale
hydrologic applications. However, embedding a new mechanistic algorithm or providing user guidance for coupling with other models will
be necessary to realistically simulate diffuse pollutant sources, their fate and transport, and the effectiveness of GI/LID implementation
scenarios. DOI: 10.1061/JSWBAY.0000817. © 2017 American Society of Civil Engineers.

Saturday, July 15, 2017

EPA SWMM 5.1 RELEASE NOTES (in a different Format)

======================================================

EPA SWMM 5.1 RELEASE NOTES

======================================================
This file contains information concerning Version 5.1 of the EPA Storm Water Management Model (SWMM). A complete Users Manual as well as full source code and other updates/Technical Manuals are available at

www.epa.gov/nrmrl/wswrd/wq/models/swmm

I posted this on the SWMM Group on LinkedIn

World Class Software Documentation for SWMM5 from Lew Rossman and Wayne Huber (Hydrology)

I have noticed based on email questions and postings to the SWMM LIst Sever (a great resource hosted by CHI, Inc.) that many SWMM 5 users do not know about the really outstanding documentation on SWMM 5 posted on the EPA Website https://www.epa.gov/water-research/storm-water-management-model-swmm It consists of two now and in the near future three volumes on Hydrology, Water Quality, LID’s and SuDs and Hydraulics. The documentation is fantastically complete with detailed background on the theory, process parameters and completely worked out examples for all of the processes in SWMM5. It is truly an outstanding aid to modelers and modellers worldwide. It would benefit you to read them (if you have not already downloaded the PDF files). 
======================================================

INSTALLATION

======================================================
To install EPA SWMM 5.1 run the setup program epaswmm5_setup.exe. It will place the following files into the application folder you designate:
  epaswmm5.exe   -- the Windows user interface for SWMM
  epaswmm5.chm  -- the SWMM 5 help file
  swmm5.dll          -- the SWMM 5 computational engine
  swmm5.exe        -- the command line version of SWMM 5
  tutorial.chm        -- an online tutorial for SWMM 5
  notes.txt             -- this file (the source of this blog)

The setup program will also create a Start Menu group named EPA SWMM 5.1 and will register SWMM 5 with Windows. This will allow you to use the Add or Remove Programs option of the Windows Control Panel to un-install EPA SWMM 5.1.

======================================================

EXAMPLE DATA SETS

======================================================
Several example data sets have been included with this package. They are placed in a sub-folder named EPA SWMM Projects\Examples in your My Documents folder. Each example consists of a .INP file that holds the model data and a .TXT file with suggestions on running it.

* EXAMPLE1.INP models runoff quantity and quality from a small watershed and its routing through  a network of storm sewers. It can be run in either single event mode or in continuous mode using the companion rainfall file.

* EXAMPLE2.INP is Example 1 of the 1988 EXTRAN Users Manual. It illustrates how SWMM 5 can graphically compare its results to observed data stored in a text file.

* EXAMPLE3.INP illustrates the use of the rule-  based controls feature in SWMM 5 for simulating real-time control.

* EXAMPLE4.INP shows how the LID controls feature  in SWMM 5 was used to reduce runoff produced from   a 29 acre mixed development site.

======================================================

TERMS OF USE

======================================================
EPA SWMM 5 is public domain software that may be freely copied and distributed.

======================================================

DISCLAIMER

======================================================
The software product is provided on an "as-is" basis. US EPA makes no representations or warranties of any kind and expressly disclaim all other warranties express or implied, including, without limitation, warranties
of merchantability or fitness for a particular purpose. Although care has been used in preparing the software product, US EPA disclaim all liability for its accuracy or completeness, and the user shall be solely responsible for the selection, use,efficiency and suitability of the software product. Any person who uses this product does so at his sole risk and without liability to US EPA.
US EPA shall have no liability to users for the infringement of proprietary rights by the software product or any portion thereof.

XPSWMM Analytical Engine

The 1D-analytical engine performs all numerical computations for 1D-models. It is based on the EPA SWMM engine and has significant proprietary enhancements.

 When the engine is running the window displays statistics on the computational performance and progress of the simulation. Calculations may be paused (to view run time statistics) and then continued or stopped. If stopped before the assigned simulation end the model will contain full output and statistics up to the manually terminated point in time. The run time parameters for a link or node may be viewed graphically.
 
When the model contains 2D components, the 2D Results displays information regarding the progress of the 2D-simulation. 2D flow calculations are performed by the TUFLOW engine.
TUFLOW is a computer program for simulating depth-averaged, two and one-dimensional free-surface flows such as occurs from floods and tides. TUFLOW, originally developed for just two-dimensional (2D) flows, stands for Two-dimensional Unsteady FLOW. The fully 2D solution algorithm, based on Stelling 1984 and documented in Syme 1991, solves the full two-dimensional, depth averaged, momentum and continuity equations for free-surface flow. The initial development was carried out as a joint research and development project between WBM Oceanics Australia and The University of Queensland in 1990. The project successfully developed a 2D/1D dynamically linked modelling system (Syme 1991). Latter improvements from 1998 to today focus on hydraulic structures, flood modelling, advanced 2D/1D linking and using GIS for data management (Syme 2001a, Syme 2001b). TUFLOW has also been the subject of extensive testing and validation by WBM Pty Ltd and others (Barton 2001, Huxley, 2004).
TUFLOW is specifically orientated towards establishing flow patterns in coastal waters, estuaries, rivers, floodplains and urban areas where the flow patterns are essentially 2D in nature and cannot or would be awkward to represent using a 1D network model.
A powerful feature of TUFLOW is its ability to dynamically link to the 1D network of the xpswmm engine. In the xp environment, the user sets up a model as a combination of 1D network domains linked to 2D domains, i.e. the 2D and 1D domains are linked to form one model.

Friday, July 14, 2017

External Hour Format for Calibration Data in InfoSWMM and InfoSWMM SA

External Hour Format
Here is an example file format
0              54.2923
0.25         53.2923
0.5           56.2923
0.75         55.2923
1              60.5621
1.25         59.5621
1.5           58.5621
1.75         57.5621


Conduits in #SWMM5

Conduits
Conduits are pipes or channels that move water from one node to another in the conveyance system. Their cross-sectional shapes can be selected from a variety of standard open and closed geometries as listed in the following table. Irregular natural cross-section shapes and Dummy links are also supported.
SWMM5 Lnk Shape

Conduits are pipes or channels that move water from one node to another in the conveyance system. Their cross-sectional shapes can be selected from a variety of standard open and closed geometries as listed in Table 3-1.

Most open channels can be represented with a rectangular, trapezoidal, or user-defined irregular cross-section shape. For the latter, a Transect object is used to define how depth varies with distance across the cross-section (see Section 3.3.5 below). Most new drainage and sewer pipes are circular while culverts typically have elliptical or arch shapes. Elliptical and Arch pipes come in standard sizes that are listed in at the bottom of this page. The Filled Circular shape allows the bottom of a circular pipe to be filled with sediment and thus limit its flow capacity. The Custom Closed Shape allows any closed geometrical shape that is symmetrical about the center line to be defined by supplying a Shape Curve for the cross section (see Section3.3.11 below).

SWMM uses the Manning equation to express the relationship between flow rate (Q), crosssectional area (A), hydraulic radius (R), and slope (S) in all conduits. For standard U.S. units,

where n is the Manning roughness coefficient. The slope S is interpreted as either the conduit slope or the friction slope (i.e., head loss per unit length), depending on the flow routing method used. 

For pipes with Circular Force Main cross-sections either the Hazen-Williams or Darcy-Weisbach formula is used in place of the Manning equation for fully pressurized flow. For U.S. units the Hazen-Williams formula is:
where C is the Hazen-Williams C-factor which varies inversely with surface roughness and is supplied as one of the cross-section’s parameters. The Darcy-Weisbach formula is:

where g is the acceleration of gravity and f is the Darcy-Weisbach friction factor. For turbulent flow, the latter is determined from the height of the roughness elements on the walls of the pipe (supplied as an input parameter) and the flow’s Reynolds Number using the Colebrook-White equation. The choice of which equation to use is a user-supplied option.

A conduit does not have to be assigned a Force Main shape for it to pressurize. Any of the closed cross-section shapes can potentially pressurize and thus function as force mains that use the Manning equation to compute friction losses. 

A constant rate of exfiltration of water along the length of the conduit can be modeled by supplying a Seepage Rate value (in/hr or mm/hr). This only accounts for seepage losses, not infiltration of rainfall dependent groundwater. The latter can be modeled using SWMM’s RDII feature (see Section 3.3.6).

The principal input parameters for conduits are:
  • names of the inlet and outlet nodes
  • offset heights of the conduit above the inlet and outlet node inverts
  • conduit length
  • Manning’s roughness
  • cross-sectional geometry
  • entrance/exit losses
  • presence of a flap gate to prevent reverse flow.
A conduit can also be designated to act as a culvert (see Figure 3-2) if a Culvert Inlet Geometry code number is assigned to it. These code numbers are listed in Appendix A.10. Culvert conduits are checked continuously during dynamic wave flow routing to see if they operate under Inlet Control as defined in the Federal Highway Administration’s publication Hydraulic Design of Highway Culverts Third Edition (Publication No. FHWA-HIF-12-026, April 2012). Under inlet control a culvert obeys a particular flow versus inlet depth rating curve whose shape depends on the culvert’s shape, size, slope, and inlet geometry. 

Flow Regulators
Flow Regulators are structures or devices used to control and divert flows within a conveyance system. They are typically used to:
  • control releases from storage facilities
  • prevent unacceptable surcharging
  • divert flow to treatment facilities and interceptors
 InfoSWMM H2OMap SWMM InfoSWMM SA  can model the following types of flow regulators:
  • Orifices
  • Weirs
  • Outlets
The following Tables are copied from the EPA Manual on SWMM (Hydraulics) II




Wednesday, July 12, 2017

SWMM5_NR_ITERATIVE Fortran Routine from 2004

      SUBROUTINE SWMM5_NR_ITERATIVE
C EXTRAN BLOCK test for SWMM 5 beta solution - 12/12/2002
      INCLUDE 'TAPES.INC'
      INCLUDE 'STIMER.INC'
      INCLUDE 'BD.INC'
      INCLUDE 'BND.INC'
      INCLUDE 'HYFLOW.INC'
      INCLUDE 'CONTR.INC'
      INCLUDE 'JUNC.INC'
      INCLUDE 'PIPE.INC'
      INCLUDE 'TIDE.INC'
      INCLUDE 'OUT.INC'
      INCLUDE 'ORF.INC'
      INCLUDE 'WEIR.INC'
      INCLUDE 'FLODAT.INC'
      DOUBLE PRECISION AKON,QNEW,DELQ1,DELQ2,DELQ3,DELQ4,DELQ5
DIMENSION        AS1(NEE)
DOUBLE PRECISION df,f,n_omega
integer          good_nodes
C=======================================================================
C     STORE OLD TIME STEP FLOW VALUES
C=======================================================================
DO  N        = 1,NTL
      QO(N)        = Q(N)
      AT(N)        = A(N)
      VT(N)        = V(N)
enddo
C=======================================================================
C     INITIALIZE CONTINUITY PARAMETERS
C=======================================================================
DO J            = 1,NJ
if(othercom(63).eq.1) then
                     Y(J)      = Y(J) + 0.5 * ( YEX2(J) - 
     +                                  YEX1(J) + YEX1(J) - YO(J))
                     IF(Y(J).LT.FUDGE)      Y(J)=FUDGE
                     IF(Y(J).GT.SURELEV(J)) Y(J)=SURELEV(J)-Z(J)
                     yex2(j)   = yex1(j)
                     yex1(j)   = YO(j)
                     endif
cred  beginning time step value of the node area
asold(j)    = as(j)
      YO(J)       = Y(J) 
      GoodNode(j) = .FALSE.
enddo
good_nodes  = 0
      omega       = input_omega
      n_omega     = node_omega
loop_count  = 0
C=======================================================================
C     HALF-STEP AREA, RADIUS : VELOCITY
C     FULL-STEP FLOW
C=======================================================================
big_loop:  DO while(good_nodes.lt.nj.and.loop_count.le.itmax)
loop_count = loop_count + 1
      if(loop_count.ge.itmax-5) then
        omega   = 0.50 * omega
        n_omega = 0.50 * n_omega 
        endif

DO J           = 1,NJ
      AS(J)          = AMEN
      AS1(J)         = 0.0
      SUMQ(J)        = QIN(J)
      SUMQS(J)       = QIN(J)
      SUMAL(J)       = 0.0
enddo
CIM   FIRST COMPUTE GATED ORIFICE PARAMETERS
      CALL OGATES(DELT,Y,V)
c
      flow_loop: DO    N      = 1,NTC
      NL            = NJUNC(N,1)
      NH            = NJUNC(N,2)
C=======================================================================
      H(N,1)   = AMAX1(Y(NL) + Z(NL),ZU(N))
      H(N,2)   = AMAX1(Y(NH) + Z(NH),ZD(N))
      CALL nhead(N,NL,NH,H(N,1),H(N,2),Q(N),A(N),V(N),HRAD,
     +           ANH,ANL,RNL,RNH,YNL,YNH,width,IDOIT,LINK(N),AS1)
cred  bypass loop for nodes already converged
      bypass_loop: if(loop_count.gt.2.and.
     +  goodnode(nl).eq..TRUE..and.goodnode(nh).eq..TRUE.) then
bypass = bypass + 1.0
else
IF(HRAD.GT.HMAX(N)) HMAX(N) = HRAD
      IF(A(N).GT.AMAX(N)) AMAX(N) = A(N)
cred  save information for the culvert classification
      HRLAST(N)  = HRAD 
vup(n)     = anl
vdn(n)     = anh
if(loop_count.eq.1) then
                   aup(n)   = anl
                   rup(n)   = rnl
                   rmd(n)   = hrad
                   endif
      positive_flow: IF(IDOIT.gt.0) THEN
c
c       Q/ANH = velocity at downstream end used for exit loss
c       Q/ANL = velocity at upstream end used for entrance loss
c       The loss = 1/2*K*V^2/g is factored into momentum
c       equation similarly to the friction slope
c       The loss momentum term = g*A*[1/2*K*V^2/g]
c                              = g*A*[1/2*K*Q/A*Q/A/g]
C                               =g  *[1/2*K*|Q*A|  /g] * Q
C                               =    [1/2*K*|Q*A|    ] * Q
c       DELQ5 is sum of losses
c
cred  calculate the froude number for every conduit
c
      DELH      = H(N,1) - H(N,2)
      DELZP     = ZU(N)  - ZD(N)
      DIFF_mid  = 0.5 * (H(N,1) - ZU(N) + H(N,2) - ZD(N))
      IF(DIFF_mid.GT.0.0) THEN
           FROUDE_MID = ABS(Q(N))/A(N)/SQRT(GRVT*(DIFF_mid))
                  ELSE
           FROUDE_MID = 0.0
           ENDIF
      bfactor = 0.0
if(FROUDE_MID.ge.1.0) THEN
           bfactor = 0.0
           elseif(froude_mid.gt.0.9) then
bfactor = 1.0 - (10.0*FROUDE_MID-9.0)
           endif
cred  test for zero sloped conduits
      if(delzp.eq.0.0)     then
                    del_ratio   = delh/0.001
                    else
                    del_ratio   = delh/delzp
                    endif
cred  swmm 4 definition for normal flow
      if(del_ratio.le.1.0) then 
                    delfactor = 0.0
cred                       swmm 5 transition definition
                    else if(del_ratio.gt.1.10) then
delfactor = 1.0
                    else
                    delfactor = 10.0 * (del_ratio-1.0)
                      endif
      DELQ5          = 0.0
      IF(ANH.NE.0.0)   DELQ5 = DELQ5 + 0.5 * ABS(Q(N)/ANH)*ENTK(N)
      IF(ANL.NE.0.0)   DELQ5 = DELQ5 + 0.5 * ABS(Q(N)/ANL)*EXITK(N)
      IF(A(N).NE.0.0)  DELQ5 = DELQ5 + 0.5 * ABS(Q(N)/A(N))*OTHERK(N)
      DELQ5 =  DELQ5 * DELT/LEN(N)
c
      DELQ4  = DELT*V(N)*(ANH-ANL)/A(N)/LEN(N)                                          
cred  the mean area travels from the midpoint to the upstream area
cred  as the value of delh/delzp changes
      area_mean =  wt*anl + wd*aup(n)  + ( wt*a(n) + wd*at(n) - 
     +             wt*anl - wd*aup(n)) *   delfactor

      DELQ2  = DELT*GRVT*area_mean*((H(N,2) - H(N,1)))/LEN(N)
      DELQ3  = 2.0*(A(N)-AT(N))/A(N)
cred  the mean hydraulic radius travels from the midpoint to the 
cred  upstream hydraulic radius as the value of delh/delzp changes
      hrad_mean =  wt*rnl + wd*rup(n)  + ( wt*hrad + wd*rmd(n) - 
     +             wt*rnl - wd*rup(n)) *   delfactor
      DELQ1  = DELT*(ROUGH(N)/hrad_mean**1.33333)*ABS(V(N))

QNEW  = QO(N) - delq2
      AKON  = DELQ1 + DELQ5 - delq4*bfactor - DELQ3*bfactor
cred  Newton-Raphson iteration
      F         = Q(N) * ( 1.0 + akon) - qnew
DF        = 1.0 + akon
Q(N)      = Q(N) - omega*f/df
      DQDH      = 1.0/(1.0+AKON)*GRVT*DELT*A(N)/LEN2(N)
dqdh_old(n) = dqdh
C=======================================================================
C     DO NOT ALLOW A FLOW REVERSAL IN ONE TIME STEP
C=======================================================================
      DIRQT = SIGN(1.0,QO(N))
      DIRQ  = SIGN(1.0,Q(N))
      IF(DIRQT/DIRQ.LT.0.0) Q(N) = 0.001*DIRQ

v(n)   = q(n)/A(N)
C=======================================================================
C     COMPUTE CONTINUITY PARAMETERS
C=======================================================================
      SELECT CASE (INGATE(N))
      CASE (1)
      Q(N) = AMAX1(Q(N),0.0)
      CASE (2)
      Q(N) = AMIN1(Q(N),0.0)
      END SELECT

      IF (NKLASS(N).LE.21) THEN
                      Q(N) = AMAX1(STHETA(N),Q(N))
                           Q(N) = AMIN1(SPHI(N),Q(N))
                    ENDIF
endif positive_flow
      endif bypass_loop
      SUMQS(NL) = SUMQS(NL) - Q(N)*barrels(n)
      SUMAL(NL) = SUMAL(NL) + dqdh_old(n)*barrels(n)
      SUMQS(NH) = SUMQS(NH) + Q(N)*barrels(n)
      SUMAL(NH) = SUMAL(NH) + dqdh_old(n)*barrels(n)

      SUMQ(NL)   = SUMQ(NL)  - WT*Q(N)*barrels(n)  - WD*QO(N)*barrels(n)   
      SUMQ(NH)   = SUMQ(NH)  + WT*Q(N)*barrels(n)  + WD*QO(N)*barrels(n) 
      ENDDO  flow_loop
C=======================================================================
C     SET FULL STEP OUTFLOWS AND INTERNAL TRANSFERS
C=======================================================================
      CALL BOUND(Y,Y,Q,TIME,DELT)
C=======================================================================
      N1       = NTC+1
      DO 370 N = N1,NTL
      NL       = NJUNC(N,1)
      NH       = NJUNC(N,2)
      IF(ABS(Q(N)).LT.1E-10) Q(N) = 0.0
C=======================================================================
      SUMQ(NL)  = SUMQ(NL)  - WT*Q(N)*barrels(n)  - WD*QO(N)*barrels(n) 
      SUMQS(NL) = SUMQS(NL) - Q(N)*barrels(n)    
      IF(NH.NE.0) THEN
         SUMQ(NH)  = SUMQ(NH) + WT*Q(N)*barrels(n) + WD*QO(N)*barrels(n) 
         SUMQS(NH) = SUMQS(NH) + Q(N)*barrels(n)    
         ENDIF
  370 CONTINUE
C=======================================================================
C     CALCULATE THE FULL-STEP HEAD
C=======================================================================
      DO J = 1, NJ
         AS(J)    = AS(J)  + AS1(J) 
      ENDDO
      DO  J   = 1,NJ
      IF(JSKIP(J).le.0) then
C=======================================================================
cred    time weighted area average
      if(othercom(62).eq.1) then
                average_area = WT*as(j) + WD*asold(j)
                else
                average_area = as(j)
                endif
cred    Newton-Raphson iteration
        yt(j)     = y(j)
 if(y(j).le.ycrown(j)) then
 F         =  Y(J) * average_area - YO(J) * average_area   
     +                    - sumq(j)*DELT
   DF        =  average_area 
        ASFULL(J) = AS(J)
 else
   DF        =  sumal(j) 
        IF(Y(J).LT.1.25*YCROWN(J))  DF = DF + 
     +   (ASFULL(J)/DELT-SUMAL(J))*EXP(-15.*(Y(J)-YCROWN(J))/YCROWN(J))
cred       correction from SWMM 5 QA testing - 11/12/2004
cred       if a large delt or if there is a large value of sumal(j)
cred       - usually from a very large conduit connected to the current node - 
cred       the expression  ASFULL(J)/DELT-SUMAL(J) may be negative 
cred       invalidating the whole concept of a "transition" slot
           if(Y(J).LT.1.25*YCROWN(J).le.ASFULL(J)/DELT.le.SUMAL(J)) 
     +     denom = sumal(j)
        CORR     = 1.00
        IF(NCHAN(J,2).EQ.0) CORR  = 0.60
 F       =  Y(J) * DF - YO(J) * DF - CORR * sumqs(j)
cred    WRITE(*,*) ajun(j),df,f,y(j),ycrown(j),sumal(j)
 endif
     Y(J)    =  Y(J) - n_omega*F/DF
        IF(Y(J).LT.0.0) Y(J) = 0.0
        IF((Y(J)+Z(J)).GT.SURELEV(J)) Y(J) = SURELEV(J)-Z(J)
 endif
       enddo

cred  converge until all nodes meet the convergence criteria
good_nodes = 0 
error_node = 0
i_was_bad  = 1 
DO j       = 1,nj
if(jskip(j).le.0) then
                  if(abs(y(j)-yt(j)).le.node_toler) then
                                     good_nodes = good_nodes + 1
                                     GoodNode(j) = .TRUE.
                              endif
                   if(abs(y(j)-yt(j)).gt.error_node) then
       error_node = abs(y(j)-yt(j))
                          i_was_bad  = j
                   endif
                 else
                  good_nodes  = good_nodes + 1
           GoodNode(j) = .TRUE.
endif
enddo 
c     write(*,669) loop_count,nj-good_nodes,error_node,
c    +               delt,ajun(i_was_bad),y(i_was_bad),  
c    +               sumq(i_was_bad),omega
669   format(2I6,F10.4,F8.2,1x,a12,3F9.3)
enddo big_loop

cred  culvert information - 8/6/2002
cred  culvert information for the culvert comparison file - 8/6/2002
cred  culvert information - 8/6/2002
      if(othercom(90).eq.1) then
       do n = 1,nc
       if(nklass(n).eq.1.or.nklass(n).eq.21) then
       if(abs(q(n)).gt.culvert_loss(n,5).and.abs(v(n)).lt.20.0)then
                    HW          = H(N,1)   - ZU(N)
                    TW          = H(N,2)   - ZD(N)
                    vup(n)      = v(n)
                    vdn(n)      = v(n)
                    head_loss   = 0.5*ENTK(N)*vup(N)*vup(N)/GRVT  +
     +                            0.5*EXITK(N)*vdn(N)*vdn(N)/GRVT +
     +                            0.5*OTHERK(N)*v(N)*v(N)/GRVT 
                    sfloss  = ROUGH(N)/GRVT * 
     +                        abs(v(N))*ABS(v(N))/hrlast(n)**1.33333
cred                sfloss  = ROUGH(N)/GRVT * 
cred +                   Q(N)*ABS(Q(N))/(A(N)**2*HRLAST(N)**1.33333)
             culvert_loss(n,1) = len(n)*sfloss
             culvert_loss(n,2) = head_loss
             culvert_loss(n,3) = h(n,1)
             culvert_loss(n,4) = h(n,2)
             culvert_loss(n,5) = q(n)  
   endif 
             CALL DEPTHX(N,NKLASS(N),q(n),YC,YNORM)
cred                type 1     
                    IF(HW.LT.1.5*DEEP(N).AND.YC.LT.YNORM.and.
     +                         TW.LE.YC) then
                               culverted(N,1) = culverted(N,1) + DELT
                        culverted(n,8) = 1
                        endif
cred                type 2
                    IF(HW.LT.1.5*DEEP(N).AND.YC.LT.YNORM.AND.
     +                         TW.GT.YC.AND.TW.LE.DEEP(N)) then
                               culverted(N,2) = culverted(N,2) + DELT
                        culverted(n,8) = 2
                        endif
cred                 type 3
                     IF(YC.GE.YNORM.AND.TW.LT.ZU(N)-ZD(N)) then
                               culverted(N,3) = culverted(N,3) + DELT
                        culverted(n,8) = 3
                        endif
cred                 type 4
                     IF(YC.GE.YNORM.AND.TW.GE.ZU(N)-ZD(N)+YC.
     +                         AND.TW.LT.ZU(N)-ZD(N)+DEEP(N)) then
                               culverted(N,4) = culverted(N,4) + DELT
                        culverted(n,8) = 4
                        endif
cred                 type 5
                     IF(YC.LT.YNORM.AND.TW.GE.DEEP(N)) then
                               culverted(N,5) = culverted(N,5) + DELT
                        culverted(n,8) = 5
                        endif
cred                 type 6
                     IF(YC.GE.YNORM.AND.TW.GE.ZU(N)-ZD(N)+
     +                         DEEP(N)) then
                               culverted(N,6) = culverted(N,6) + DELT
                        culverted(n,8) = 6
                        endif
cred                 type 7
                     IF(HW.GE.1.5*DEEP(N).AND.TW.LT.DEEP(N)) then
                               culverted(N,7) = culverted(N,7) + DELT
                        culverted(n,8) = 7
                        endif
                      endif
                      enddo 

      endif
      RETURN
      END

AI Rivers of Wisdom about ICM SWMM

Here's the text "Rivers of Wisdom" formatted with one sentence per line: [Verse 1] 🌊 Beneath the ancient oak, where shadows p...