Showing posts with label Summary. Show all posts
Showing posts with label Summary. Show all posts

Sunday, December 29, 2024

SWMM5 Delphi GUI Fproped.pas, Summary

 Below is a high-level summary of Fproped.pas, the Delphi unit in SWMM that defines the Property Editor form (TPropEditForm). This form hosts a TPropEdit component that behaves like an "object inspector," allowing users to edit properties of SWMM data objects in a grid-based interface.


1. Purpose

Fproped.pas implements TPropEditForm, a specialized form that:

  • Displays object properties in a two-column grid (property name vs. property value).
  • Supports editing, validating, and providing help/hints for each property.
  • Manages additional dialogues when a property requires deeper editing (e.g., time series, curves, infiltration parameters, etc.).

2. TPropEditForm: Key Elements and Behaviors

  1. TPropEdit Component:

    • Placed at runtime onto TPropEditForm.
    • Columns:
      • Property (read-only, descriptive name).
      • Value (editable, user input).
    • Tracks each property’s data type, default/edit style (e.g., numeric, combo list, file name, comment, etc.).
    • Has special event handlers for:
      • OnButtonClick: opens supplementary dialogs (time series editor, curve editor, etc.).
      • OnValidate: checks correctness of user input.
      • OnRowSelect: updates hint label describing the selected property.
  2. Form Lifecycle:

    • FormCreate: Instantiates the TPropEdit component, sets styling, event handlers, parent layout, etc.
    • FormDeactivate: Ensures last property edit is validated when user clicks away from the form.
    • FormShow: Makes the form fully visible (especially on first activation).
    • FormClose: Hides the form rather than destroys it.
  3. Editing Specialized Properties:

    • When user clicks the ellipsis button on a property, the ButtonClick event routes to specialized edit dialogs:
      • Time series
      • Rain file name
      • Curve (e.g., Pump curve, Rating curve, Tidal curve)
      • Infiltration
      • Groundwater
      • Snowpack
      • LID usage
      • Initial loadings
      • Node inflows / Pollutant treatment
      • Conduit cross-section
      • Storage curve
      • Label font
      • etc.
  4. Validation:

    • The OnValidate handler calls Uvalidate.ValidateEditor to check if a typed-in property value is valid (numeric range checks, permissible values, etc.).
    • If invalid, displays an error message and reverts focus to the property.
  5. Hints:

    • A small text label (HintLabel) shows context-sensitive help text about the selected property row.
    • RefreshPropertyHint is invoked after property changes or data refreshes.
  6. Navigation:

    • The user can navigate between items in the same object category (e.g., Subcatch #5 to Subcatch #6) via PageUp/PageDown keystrokes.
    • F1 triggers context-sensitive help for the active object type.
  7. Integration:

    • The form is typically hidden unless an object is being edited.
    • After user changes a property, HasChanged in main form is set so that SWMM knows data were modified.
    • Some property edits can invoke deeper sub-dialogs (e.g., infiltration, LID usage, or cross-section shape editors).

3. Summary

Within Fproped.pas, TPropEditForm centralizes property editing for SWMM objects by providing a TPropEdit grid-based property list. Custom logic in ButtonClick and OnValidate extends standard data entry, launching specialized dialogs or validating user input. The form also manages help hints and keyboard navigation (like PgUp/PgDown). Its integrated design ensures any changes are immediately recognized by SWMM and can trigger more advanced configuration dialogs when necessary.

SWMM5 Delphi GUI Dstorage.pas, Summary

 Below is an overview of Dstorage.pas, a Delphi unit from SWMM 5.2 that provides a StorageForm dialog for specifying the geometry of a storage unit node in SWMM. Storage units can have their surface area defined in different ways (functional, tabular, or shapes like cylindrical, conical, paraboloid, or pyramidal). These shapes control how surface area and volume vary with depth in the node.


1. Purpose and Context

In SWMM, storage units are nodes that hold water with a known area-depth or volume-depth relationship. The TStorageForm allows the user to choose one of several geometry options and provide parameters—either as simple numeric coefficients (functional shapes), direct tabular data (storage curves), or shape-based parameters. Once the user finishes, the data are stored back into the node’s Data[] array for use in hydraulic simulations.


2. Main Form: TStorageForm

2.1 Visual Components

  1. ListView1: A list of possible storage shapes:
    • 0: Elliptical or Circular Cylinder (Cylindrical)
    • 1: Truncated Elliptical Cone (Conical)
    • 2: Elliptical Paraboloid (Parabolic)
    • 3: Rectangular Pyramid/Box (Pyramidal)
    • 4: Functional (defined by a functional relationship)
    • 5: Tabular (defined by a named curve)
  2. CardPanel1 with separate Card pages for each geometry approach:
    • ShapeCard: For the first four items (cylindrical, conical, etc.), three numeric parameters (ParamEdit1..3) plus descriptions/labels.
    • FunctionalCard: For a functional shape (FuncEdit1..3).
    • TabularCard: Has a combo box (ComboBox1) to pick a storage curve from the project.
    • VolumeCard: A page for Area/Volume display at a given depth (DepthNumEdit).
  3. CalcLinkLabel (e.g., “Show Volume Calculator” or “Show Shape Selection”): Toggling between the shape parameters view and an interactive “calculator” view for area/volume at a user-specified depth.
  4. EditBitBtn: Launches the time series/curve editor to modify the chosen storage curve.
  5. OkBtn, CancelBtn, HelpBtn: Standard dialog actions.

2.2 Data Storage

SWMM uses string arrays (like Data[]) to store a node’s storage parameters:

  • Data[STORAGE_GEOMETRY_INDEX] : string indicating geometry type (CYLINDRICAL, CONICAL, PARABOLIC, PYRAMIDAL, FUNCTIONAL, TABULAR).
  • Data[STORAGE_COEFF0_INDEX], [STORAGE_COEFF1_INDEX], [STORAGE_COEFF2_INDEX]: numeric parameters describing the shape.
  • Data[STORAGE_ATABLE_INDEX]: name of a storage curve (if shape is TABULAR).

When the user chooses a shape and inputs parameters, these are eventually written back into Data[].


3. Workflow

3.1 SetData(Data[])

  • Called to populate the form’s controls with existing storage parameters from Data[].
  • If Data[STORAGE_GEOMETRY_INDEX] = 'FUNCTIONAL', the form’s ListView1 is set to index 4, and FuncEdit1..3 are loaded.
  • If Data[STORAGE_GEOMETRY_INDEX] = 'TABULAR', the form’s ListView1 is set to index 5, and ComboBox1 is set to the storage curve name.
  • Otherwise, the form picks the correct shape index (0..3) and loads the numeric parameters (ParamEdit1..3).

3.2 GetData(Data[])

  • After user hits OK, the form gathers user inputs back into the Data[] array.
  • If the shape is Functional (ListView1.ItemIndex=4), sets Data[STORAGE_GEOMETRY_INDEX] = 'FUNCTIONAL' and the 3 coefficients.
  • If Tabular (=5), sets geometry to 'TABULAR' and Data[STORAGE_ATABLE_INDEX] to the chosen curve name.
  • Otherwise, picks 'CYLINDRICAL', 'CONICAL', 'PARABOLIC', or 'PYRAMIDAL' depending on item index 0..3, and places the numeric parameters into STORAGE_COEFF0..2_INDEX.

3.3 OkBtnClick

  • If the shape is Tabular and the user hasn’t selected a curve, an error is raised. Otherwise, the form’s ModalResult is set to mrOK.

3.4 CalcLinkLabel / VolumeCard

  • The user can click the link “Show Volume Calculator” to see how area and volume vary with depth for the chosen shape. This triggers the code to:
    1. Fill placeholders if any required fields are blank.
    2. Temporarily store the shape parameters in local variables (A0, A1, A2, etc.).
    3. Switch to the VolumeCard page.
    4. The user can then type a depth in DepthNumEdit and see the resulting area/volume.
  • If the user clicks the link again, the form switches back to the original shape card.

3.5 Volume/Area Calculation Logic

  • For a shape-based geometry (cylindrical, conical, etc.), it uses formulas for area AA and volume VV as a function of depth:
    • E.g., Cylinder: A=π×L×W4A = \pi \times \frac{L \times W}{4}, V=A×DV = A \times D, etc.
    • E.g., Conical: area = πLW/4+D()\pi L W/4 + D(\dots), volume = \dots.
  • For a Functional shape, area is A=a0+a1×Da2A = a_0 + a_1 \times D^{a_2}.
  • For a Tabular shape, the code retrieves a user-defined curve and does a piecewise linear interpolation to find area and integrates it for volume up to the given depth.

4. Summaries of Key Methods

  • FormCreate: Prepares the UI, sets default labels for US or SI units, loads shapes into ListView1, etc.
  • ListView1SelectItem: Switches the visible card (ShapeCard, FunctionalCard, TabularCard, etc.) based on the selected shape.
  • CalcLinkLabelLinkClick: Toggles between shape-parameter editing and volume display mode.
  • DepthNumEditChange: Whenever the user modifies the depth in the calculator, updates the displayed area/volume.
  • SetData: Loads from Data[] to the form.
  • GetData: Writes from the form to Data[].
  • OkBtnClick: Final validation; if something is missing for a tabular shape (no curve name), it rejects.

5. Summary

Dstorage.pas provides a flexible UI for storage unit geometry in SWMM. It supports:

  1. Shape-based (cylindrical, conical, parabolic, pyramidal),
  2. Functional (user-supplied equation for area vs. depth),
  3. Tabular (area-depth data from a named curve).

Users can preview the area/volume at any depth, ensuring correct geometry data for subsequent hydraulic modeling.

Saturday, December 28, 2024

SWMM5 findroot.c, Summary

 Below is a step‐by‐step explanation of findroot.c, which provides two root-finding methods for solving 

f(x)=0f(x) = 0: a Newton‐Raphson method (with fallback bisection) and Ridder’s Method. Both functions iterate until either the root is found within a given accuracy or a maximum iteration limit is reached.


1. Overall Purpose

//  findroot.c
//
//  Finds solution of func(x) = 0 using either ...
  • This file is for finding roots of functions f(x)f(x) = 0, i.e., we want to find xx such that f(x)f(x) is zero within a tolerance.

  • It contains two main functions:

    1. findroot_Newton(...): A combination of Newton–Raphson and bisection.
    2. findroot_Ridder(...): Implementation of Ridder’s Method.
  • The user supplies:

    • A bracket (x1, x2) such that f(x1)f(x1) and f(x2)f(x2) have opposite signs (i.e., the function’s sign changes, indicating a root in between).
    • A function pointer that either returns f(x)f(x) & derivative or just f(x)f(x).

2. findroot_Newton(...)

int findroot_Newton(
    double x1, double x2, double* rts, double xacc,
    void (*func) (double x, double* f, double* df, void* p),
    void* p)
  1. Parameters:

    • x1, x2: The initial bracket endpoints.
    • *rts: On input, an initial guess for the root; on output, the refined root.
    • xacc: The required accuracy for the solution.
    • func(x, &f, &df, p): A user function that returns:
      • f: the function value f(x)f(x),
      • df: the derivative f(x)f'(x).
    • p: A pointer to any extra data needed by func(). (It can be NULL if no extra data is needed.)
  2. Algorithm:

    1. Initial Setup:
      • xlo = x1, xhi = x2, x = *rts.
      • Evaluate func(x, &f, &df, p).
      • Keep track of iteration count in n.
    2. Loop (up to MAXIT=60 times):
      • The method tries a Newton‐Raphson step: dx = f/df.
      • But checks if that step is out of range or not decreasing well:
        if ( ( (x - xhi)*df - f)*((x - xlo)*df - f) >= 0.0
          || (fabs(2.0*f) > fabs(dxold*df)) )
        {
          // If so, do a bisection step
          dx = 0.5*(xhi - xlo);
          x = xlo + dx;
        }
        else
        {
          // Otherwise, do Newton step
          dx = f/df;
          x -= dx;
        }
        
      • After adjusting x, check if |dx| < xacc (i.e., the change is within tolerance).
      • Evaluate func(x, &f, &df, p) again.
      • If f < 0.0, set xlo = x; else xhi = x.
    3. Convergence:
      • If it converges or if maximum iterations are reached, store x in *rts.
      • The function returns the number of function evaluations used or 0 if MAXIT was exceeded (i.e., no convergence in time).
  3. Notes:

    • This approach tries Newton’s method if possible (fast convergence) but falls back to bisection if the Newton step might overshoot or not reduce the bracket effectively.

3. findroot_Ridder(...)

double findroot_Ridder(
    double x1, double x2, double xacc,
    double (*func)(double, void* p), void* p)
  1. Parameters:

    • x1, x2: The bracket endpoints (sign of f(x1) and f(x2) must differ).
    • xacc: Accuracy required.
    • func(x, p): The function returning only f(x).
    • p: Optional user data pointer.
  2. Algorithm: Ridder’s method is a bracketing method that uses intermediate function evaluations and a special formula to find a better midpoint.

    1. Evaluate flo = func(x1, p), fhi = func(x2, p).
    2. If flo == 0.0, root is x1; if fhi == 0.0, root is x2.
    3. Check (flo > 0.0 && fhi < 0.0) || (flo < 0.0 && fhi > 0.0) to confirm it’s bracketed.
    4. Then iterate up to MAXIT=60:
      • xm = midpoint: (xlo + xhi)/2.
      • Evaluate fm = func(xm, p).
      • Compute s = sqrt( fm^2 - flo * fhi ).
      • If s == 0.0 => can’t proceed, return the midpoint.
      • Then a new approximation xnew is found by a Ridder formula: xnew=xm+(xmxlo)((flofhi?1:1)fm/s)xnew = xm + (xm - xlo) * \bigl( (\text{flo} \ge \text{fhi} ? 1 : -1)*fm / s \bigr)
      • If |xnew - ans| <= xacc, we break (converged).
      • Evaluate fnew = func(xnew,p).
      • Then we decide how to re-bracket:
        if ( SIGN(fm, fnew) != fm ) {
            xlo = xm; flo = fm;
            xhi = xnew; fhi = fnew;
        }
        else if ( SIGN(flo, fnew) != flo ) {
            xhi = xnew; fhi = fnew;
        }
        else if ( SIGN(fhi, fnew) != fhi ) {
            xlo = xnew; flo = fnew;
        }
        
      • If bracket becomes very small or we converge, we return.
    5. If no bracket or no convergence, returns -1.e20 indicating failure.
  3. Notes:

    • Ridder’s method is a robust bracketing approach with guaranteed convergence if sign change is present, typically faster than simple bisection but less complicated than Newton.

4. Implementation Details

  • MAXIT = 60 is the maximum iteration count for both methods.
  • SIGN(a,b) macro returns |a| if b >= 0, else -|a|.
  • Each function uses the bracket [x1, x2] and ensures it always encloses the root (i.e., f(xlo)*f(xhi) < 0).
  • findroot_Newton(...) uses a “mixed” approach: Newton’s method but does a bisection if the Newton step is questionable.
  • findroot_Ridder(...) purely uses Ridder’s formula, a special bracket-based improvement over bisection.

5. Key Takeaways

  • These functions assume the user has already found an initial bracket [x1, x2] with f(x1)*f(x2) < 0.
  • findroot_Newton() returns an integer: the count of function evaluations or zero if it fails.
  • findroot_Ridder() returns a double root approximation or -1.e20 if bracket was invalid or no success.
  • Both methods can be integrated easily by providing a function pointer for f(x) (and derivative if needed).

Hence, findroot.c is a utility file for any part of the SWMM code (or related code) needing a robust root-finding procedure.

A comprehensive explanation of how minimum travel distance relates to link length in InfoSewer

In hydraulic modeling of sewer networks, the minimum travel distance is a fundamental parameter that affects how accurately the model can si...