Home | Mathematics | * Applied Mathematics | * Storage Tank Modeling |     Share This Page
TankStepped Java Source Listing

A special-case analyzer for large storage tanks.

Copyright © 2009, Paul LutusMessage Page

 

/***************************************************************************
 *   Copyright (C) 2009 by Paul Lutus                                      *
 *   lutusp@arachnoid.com                                                  *
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) 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.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
import java.util.*;
import java.io.*;

/**
 *
 * @author lutusp.arachnoid.com
 */
final public class TankStepped {

    final public String VERSION = "1.1";
    final int fieldWidth = 18;
    final int decimalPlaces = 3;
    final String nFormat = "%" + fieldWidth + "." + decimalPlaces + "f";
    final String csvFormat = "%." + decimalPlaces + "f";
    final String sFormat = "%" + fieldWidth + "s";
    final int BOTTOMSECTION = 0;
    final int MIDDLESECTION = 1;
    final int TOPSECTION = 2;
    boolean csvMode = false;
    String name;

    class TankSection {

        double r;
        double h;

        public TankSection(double r, double h) {
            this.r = r;
            this.h = h;
        }
    };
    Vector<TankSection> sections;

    public TankStepped() {
        name = getClass().getName();
    }

    /*
     * The tank being modeled has an inverted cone at the bottom,
     * some stepped-radius cylinders in the middle section,
     * and a spherical-segment top.
     */
    double computeSectionPartialVolume(
            int section,
            double r,
            double h,
            double y) {
        double v = 0;
        double q;
        final double pid6 = Math.PI / 6.0;
        switch (section) {
            case BOTTOMSECTION: // inverted cone
                if (h != 0) {
                    q = r * (y / h);
                    v = Math.PI * q * q * y / 3.0;
                }
                break;
            case MIDDLESECTION: // all middle sections are cylinders
                v = Math.PI * r * r * y;
                break;
            case TOPSECTION: // spherical segment
                double mr = (r * r + h * h) / (2 * h);
                // compute a segment of a sphere
                // between y and h
                double hh = (h - y);
                double qr = mr - hh;
                double rr = Math.sqrt(mr * mr - qr * qr);
                // the full segment volume
                v = (3 * r * r + h * h) * h;
                // subtract the section above y
                v -= (3 * rr * rr + hh * hh) * hh;
                // multiply by constant
                v *= pid6;
                break;
        }
        return v;
    }

    String formatRecord(String format, double a, double b) {
        if (csvMode) {
            return String.format(csvFormat + "," + csvFormat, a, b);
        } else {
            return String.format(nFormat + " " + nFormat, a, b);
        }
    }

    String formatRecord(String format, String a, String b) {
        if (csvMode) {
            return String.format("%s,%s", a, b);
        } else {
            return String.format(sFormat + " " + sFormat, a, b);
        }
    }

    /**
     * @param by y bisector within tank height
     * @param nFormat numeric format for table record
     * @returns table record as string
     */
    String computeTankPartialVolume(double by, String nFormat) {
        double volume = 0;
        double y = by;
        Iterator<TankSection> it = sections.iterator();
        // step throught tank sections,
        // add full volumes of all sections
        // less than y, then add partial volume
        // for section bisected by y
        while (y > 0 && it.hasNext()) {
            TankSection s = it.next();
            // section = (first element = 0), (between = 1), (last element = 2)
            int tSec = (s == sections.firstElement()) ? BOTTOMSECTION : (s == sections.lastElement()) ? TOPSECTION : MIDDLESECTION;
            if (s.h >= y) {
                // y lies within this section's height,
                // so add partial volume for this section
                volume += computeSectionPartialVolume(tSec, s.r, s.h, y);
                // and set y to zero so loop exits
                y = 0;
            } else {
                // y is greater than present section height,
                // so add full volume for this section
                volume += computeSectionPartialVolume(tSec, s.r, s.h, s.h);
                // and subtract this section's height from y
                y -= s.h;
            }
        }
        return formatRecord(nFormat, by, volume);
    }

    /**
     * @param n table step size if < 0 or arg if > 0
     * @param height tank overall height
     */
    void createTable(double n, double height) {
        double y = 0, ty;
        double pv;
        Vector<String> vs = new Vector<String>();
        String fs = formatRecord(sFormat, "Height", "Volume");
        vs.add(fs);
        if (n < 0) {
            // user has entered a table step size
            for (int iy = 0; (ty = iy * -n) <= height; iy++) {
                y = ty;
                fs = computeTankPartialVolume(y, nFormat);
                //fs = String.format(nFormat,y,pv);
                vs.add(fs);
            }
            // compute one more value to deal with rounding issues
            String ns = computeTankPartialVolume(height, nFormat);
            // if this isn't identical to the prior entry
            if (!ns.equals(fs)) {
                vs.add(ns);
            }
        } else {
            // user has entered a single argument
            n = (n > height)?height:n;
            fs = computeTankPartialVolume(n, nFormat);
            vs.add(fs);
        }
        // now print the table
        Iterator<String> is = vs.iterator();
        while (is.hasNext()) {
            System.out.println(is.next());
        }
    }

    /**
     * @param args copy of command line arguments
     */
    void process(String[] args) {
        int i = 0;
        if (args.length > 0 && args[0].equals("-v")) {
            System.out.println(name + " version " + VERSION);
        } else {
            if (args.length < 7) {
                System.out.println("usage: [-v version] [-c (csv mode)] (tank section radius/height pairs) r h r h r h ... (-n = table stepsize or +n = arg) n");
                System.out.println("       see http://arachnoid.com/TankCalc/TankStepped.html for more instructions.");
            } else {
                try {
                    if (args[0].equals("-c")) {
                        csvMode = true;
                        i++;
                    }
                    double height = 0;
                    sections = new Vector<TankSection>();
                    int top = args.length;
                    for (; i < top - 1; i += 2) {
                        double r = Double.parseDouble(args[i]);
                        double h = Double.parseDouble(args[i + 1]);
                        sections.add(new TankSection(r, h));
                        height += h;
                    }
                    // if required, convert major radius entry
                    // to minor radius for spherical top cap
                    TankSection s = sections.lastElement();
                    if (s.h > s.r) {
                        height -= s.h;
                        s.h = s.h - Math.sqrt(s.h * s.h - s.r * s.r);
                        height += s.h;
                    }
                    if (i < args.length) {
                        double step = Double.parseDouble(args[i]);
                        createTable(step, height);
                    } else {
                        throw new Exception("No stepsize or argument entry.");
                    }
                } catch (Exception e) {
                    System.out.println(name + ": Error: " + e);
                }
            }
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        TankStepped ts = new TankStepped();
        ts.process(args);
    }
}
 

Home | Mathematics | * Applied Mathematics | * Storage Tank Modeling |     Share This Page