From 8328bec26de93eff785dd7718356b6f46488097b Mon Sep 17 00:00:00 2001 From: Adam Erispaha Date: Thu, 28 Mar 2019 10:31:53 -0400 Subject: [PATCH 1/9] add json to manifest, remove rounding of coordinates --- MANIFEST.in | 1 + swmmio/__init__.py | 2 +- swmmio/utils/dataframes.py | 9 +++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 3fad5dc..791946a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,3 +3,4 @@ include lib/windows/swmm5_22.exe include lib/linux/swmm5 include swmmio/reporting/basemaps/*.html include swmmio/graphics/fonts/*.ttf +include swmmio/defs/*.json diff --git a/swmmio/__init__.py b/swmmio/__init__.py index 4373c28..77e8318 100644 --- a/swmmio/__init__.py +++ b/swmmio/__init__.py @@ -5,7 +5,7 @@ '''Python SWMM Input/Output Tools''' -VERSION_INFO = (0, 3, 4) +VERSION_INFO = (0, 3, 4, 'post1') # post release to include json in manifest __version__ = '.'.join(map(str, VERSION_INFO)) __author__ = 'Adam Erispaha' __copyright__ = 'Copyright (c) 2016' diff --git a/swmmio/utils/dataframes.py b/swmmio/utils/dataframes.py index c9564e2..5ce65dc 100644 --- a/swmmio/utils/dataframes.py +++ b/swmmio/utils/dataframes.py @@ -77,10 +77,11 @@ def get_link_coords(row, nodexys, verticies): outlet_id = str(row.OutletNode) xys_str = nodexys.rename(index=str) - x1 = round(xys_str.at[inlet_id, 'X'], 4) - y1 = round(xys_str.at[inlet_id, 'Y'], 4) - x2 = round(xys_str.at[outlet_id, 'X'], 4) - y2 = round(xys_str.at[outlet_id, 'Y'], 4) + x1 = xys_str.at[inlet_id, 'X'] + y1 = xys_str.at[inlet_id, 'Y'] + x2 = xys_str.at[outlet_id, 'X'] + y2 = xys_str.at[outlet_id, 'Y'] + if None in [x1, x2, y1, y2]: print(row.name, 'problem, no coords') # grab any extra verts, place in between up/dwn nodes From 5b3c8fdf922a4250f6587ff8168fdc2b0bb166ba Mon Sep 17 00:00:00 2001 From: Adam Erispaha Date: Thu, 28 Mar 2019 10:34:26 -0400 Subject: [PATCH 2/9] set dev version num --- swmmio/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swmmio/__init__.py b/swmmio/__init__.py index 77e8318..eea4b0e 100644 --- a/swmmio/__init__.py +++ b/swmmio/__init__.py @@ -5,7 +5,7 @@ '''Python SWMM Input/Output Tools''' -VERSION_INFO = (0, 3, 4, 'post1') # post release to include json in manifest +VERSION_INFO = (0, 3, 5, 'dev') __version__ = '.'.join(map(str, VERSION_INFO)) __author__ = 'Adam Erispaha' __copyright__ = 'Copyright (c) 2016' From 2f5ff1252f500f46888746346c7f267e4b74c494 Mon Sep 17 00:00:00 2001 From: Adam Erispaha Date: Fri, 19 Apr 2019 19:37:42 -0400 Subject: [PATCH 3/9] add more inp get/sets and a inp.save func --- swmmio/core.py | 89 ++++++++++++++++------- swmmio/defs/section_headers.json | 6 +- swmmio/elements.py | 2 +- swmmio/utils/dataframes.py | 8 +- swmmio/version_control/version_control.py | 2 +- 5 files changed, 75 insertions(+), 32 deletions(-) diff --git a/swmmio/core.py b/swmmio/core.py index 8c32526..f0a9965 100644 --- a/swmmio/core.py +++ b/swmmio/core.py @@ -160,30 +160,14 @@ def conduits(self): return df + @property def orifices(self): """ collect all useful and available data related model orifices and organize in one dataframe. """ - # check if this has been done already and return that data accordingly - if self._orifices_df is not None: - return self._orifices_df - - # parse out the main objects of this model - inp = self.inp - rpt = self.rpt - - # create dataframes of relevant sections from the INP - orifices_df = create_dataframeINP(inp.path, "[ORIFICES]", comment_cols=False) - if orifices_df.empty: - return pd.DataFrame() - - # add conduit coordinates - xys = orifices_df.apply(lambda r: get_link_coords(r, self.inp.coordinates, self.inp.vertices), axis=1) - df = orifices_df.assign(coords=xys.map(lambda x: x[0])) - df.InletNode = df.InletNode.astype(str) - df.OutletNode = df.OutletNode.astype(str) + df = ModelSection(self, 'orifices') self._orifices_df = df return df @@ -244,7 +228,8 @@ def pumps(self): def links(self): """ - create a DataFrame containing all link objects in the model including conduits, pumps, weirs, and orifices. + create a DataFrame containing all link objects in the model including + conduits, pumps, weirs, and orifices. :return: dataframe containing all link objects in the model """ if self._links_df is not None: @@ -365,7 +350,7 @@ def to_crs(self, *args, **kwargs): >>> m.to_crs("+init=EPSG:4326") # convert to WGS84 web mercator >>> m.inp.coordinates X Y - Name + Name J3 -74.866424 42.365958 1 -74.870614 42.368292 2 -74.867615 42.367916 @@ -377,7 +362,7 @@ def to_crs(self, *args, **kwargs): J1 -74.868861 42.366968 >>> m.inp.vertices X Y - Name + Name C1:C2 -74.868703 42.366833 C2.1 -74.868034 42.366271 C2.1 -74.867305 42.365974 @@ -538,14 +523,24 @@ def __init__(self, file_path): self._conduits_df = None self._junctions_df = None self._outfalls_df = None + self._storage_df = None self._coordinates_df = None self._vertices_df = None self._polygons_df = None + self._subcatchments_df = None SWMMIOFile.__init__(self, file_path) # run the superclass init - self._sections = [self._conduits_df, self._junctions_df, self._outfalls_df, - self._coordinates_df, self._vertices_df, self._polygons_df] + self._sections = [ + '[CONDUITS]', + '[JUNCTIONS]', + '[STORAGE]', + '[OUTFALLS]', + '[VERTICES]', + '[SUBCATCHMENTS]' + ] + # self._outfalls_df, + # self._coordinates_df, self._vertices_df, self._polygons_df] def save(self, target_path=None): ''' @@ -553,11 +548,17 @@ def save(self, target_path=None): is provided ''' from swmmio.utils.modify_model import replace_inp_section + import shutil target_path = target_path if target_path is not None else self.path + shutil.copyfile(self.path, target_path) for section in self._sections: - if section is not None: - replace_inp_section() + # reformate the [SECTION] to section (and _section_df) + sect_id = section.translate({ord(i): None for i in '[]'}).lower() + sect_id_private = f'_{sect_id}_df' + data = getattr(self, sect_id_private) + if data is not None: + replace_inp_section(target_path, section, data) @property def conduits(self): @@ -652,6 +653,44 @@ def outfalls(self, df): """Set inp.outfalls DataFrame.""" self._outfalls_df = df + @property + def storage(self): + """ + Get/set storage section of the INP file. + + :return: storage section of the INP file + :rtype: pandas.DataFrame + + Examples: + """ + if self._storage_df is None: + self._storage_df = create_dataframeINP(self.path, "[STORAGE]", comment_cols=False) + return self._storage_df + + @storage.setter + def storage(self, df): + """Set inp.storage DataFrame.""" + self._storage_df = df + + @property + def subcatchments(self): + """ + Get/set subcatchments section of the INP file. + + :return: subcatchments section of the INP file + :rtype: pandas.DataFrame + + Examples: + """ + if self._subcatchments_df is None: + self._subcatchments_df = create_dataframeINP(self.path, "[SUBCATCHMENTS]", comment_cols=False) + return self._subcatchments_df + + @subcatchments.setter + def subcatchments(self, df): + """Set inp.subcatchments DataFrame.""" + self._subcatchments_df = df + @property def coordinates(self): """ diff --git a/swmmio/defs/section_headers.json b/swmmio/defs/section_headers.json index 6a641e3..b522edb 100644 --- a/swmmio/defs/section_headers.json +++ b/swmmio/defs/section_headers.json @@ -9,5 +9,9 @@ "pumps": { "inp_sections": ["[PUMPS]"], "rpt_sections": ["Link Flow Summary"] + }, + "orifices": { + "inp_sections": ["[ORIFICES]"], + "rpt_sections": ["Link Flow Summary"] } -} \ No newline at end of file +} diff --git a/swmmio/elements.py b/swmmio/elements.py index bc19b7a..0ff2b5c 100644 --- a/swmmio/elements.py +++ b/swmmio/elements.py @@ -58,6 +58,7 @@ def __call__(self): df_other = create_dataframeINP(self.inp.path, sect, comment_cols=False) df = df.join(df_other) + # if there is an RPT available, grab relevant sections if self.rpt: for rpt_sect in self.config['rpt_sections']: df = df.join(create_dataframeRPT(self.rpt.path, rpt_sect)) @@ -71,4 +72,3 @@ def __call__(self): df.OutletNode = df.OutletNode.astype(str) return df - diff --git a/swmmio/utils/dataframes.py b/swmmio/utils/dataframes.py index 5ce65dc..e327f54 100644 --- a/swmmio/utils/dataframes.py +++ b/swmmio/utils/dataframes.py @@ -48,17 +48,17 @@ def create_dataframeINP(inp_path, section='[CONDUITS]', ignore_comments=True, if headerdefs['headers'][section] == 'blob': # return the whole row, without specifc col headers - df = pd.read_table(tempfilepath, delim_whitespace=False, comment=comment_str) + df = pd.read_csv(tempfilepath, delim_whitespace=False, comment=comment_str) elif section == '[CURVES]' or section == '[TIMESERIES]': # return the whole row, without specifc col headers - df = pd.read_table(tempfilepath, delim_whitespace=False) # , index_col=0)#, skiprows=[0]) + df = pd.read_csv(tempfilepath, delim_whitespace=False) # , index_col=0)#, skiprows=[0]) else: # this section header is recognized and will be organized into known columns headerlist = headerdefs['headers'][section].split() if comment_cols: headerlist = headerlist + [';', 'Comment', 'Origin'] - df = pd.read_table(tempfilepath, header=None, delim_whitespace=True, skiprows=[0], - index_col=0, names=headerlist, comment=comment_str) + df = pd.read_csv(tempfilepath, header=None, delim_whitespace=True, skiprows=[0], + index_col=0, names=headerlist, comment=comment_str)# , encoding='latin1') if comment_cols: # add new blank comment column after a semicolon column diff --git a/swmmio/version_control/version_control.py b/swmmio/version_control/version_control.py index aab35e4..5544e28 100644 --- a/swmmio/version_control/version_control.py +++ b/swmmio/version_control/version_control.py @@ -4,7 +4,7 @@ import fileinput import itertools from datetime import datetime -from swmmio.swmmio import Model +from swmmio import Model from swmmio.utils import functions as funcs from swmmio.utils.dataframes import create_dataframeINP # from swmmio.utils import swmm_utils as su From 57ad7c1532639851c830cbffaf45109dd4e96be8 Mon Sep 17 00:00:00 2001 From: Adam Erispaha Date: Wed, 24 Apr 2019 14:50:20 -0400 Subject: [PATCH 4/9] remove invalid model links --- swmmio/core.py | 113 ++++++++- swmmio/defs/sectionheaders.py | 4 +- swmmio/elements.py | 3 + swmmio/tests/data/__init__.py | 2 + swmmio/tests/data/invalid_model.inp | 337 +++++++++++++++++++++++++++ swmmio/tests/test_version_control.py | 26 ++- swmmio/utils/dataframes.py | 11 +- swmmio/utils/functions.py | 45 +++- swmmio/version_control/inp.py | 3 +- 9 files changed, 528 insertions(+), 16 deletions(-) create mode 100644 swmmio/tests/data/invalid_model.inp diff --git a/swmmio/core.py b/swmmio/core.py index f0a9965..ab24c2a 100644 --- a/swmmio/core.py +++ b/swmmio/core.py @@ -235,7 +235,7 @@ def links(self): if self._links_df is not None: return self._links_df - df = pd.concat([self.conduits(), self.orifices(), self.weirs(), self.pumps()]) + df = pd.concat([self.conduits(), self.orifices(), self.weirs(), self.pumps()], sort=True) df['facilityid'] = df.index self._links_df = df return df @@ -260,7 +260,7 @@ def nodes(self, bbox=None, subset=None): storage_df = create_dataframeINP(inp.path, "[STORAGE]") # concatenate the DFs and keep only relevant cols - all_nodes = pd.concat([juncs_df, outfalls_df, storage_df]) + all_nodes = pd.concat([juncs_df, outfalls_df, storage_df], sort=True) cols = ['InvertElev', 'MaxDepth', 'SurchargeDepth', 'PondedArea'] all_nodes = all_nodes[cols] @@ -520,7 +520,13 @@ class inp(SWMMIOFile): # make sure INP has been saved in the GUI before using this def __init__(self, file_path): + self._options_df = None + self._files_df = None self._conduits_df = None + self._xsections_df = None + self._pumps_df = None + self._orifices_df = None + self._weirs_df = None self._junctions_df = None self._outfalls_df = None self._storage_df = None @@ -532,15 +538,19 @@ def __init__(self, file_path): SWMMIOFile.__init__(self, file_path) # run the superclass init self._sections = [ + '[OPTIONS]', + '[FILES]', '[CONDUITS]', + '[XSECTIONS]', + '[PUMPS]', + '[ORIFICES]', + '[WEIRS]', '[JUNCTIONS]', '[STORAGE]', '[OUTFALLS]', '[VERTICES]', '[SUBCATCHMENTS]' ] - # self._outfalls_df, - # self._coordinates_df, self._vertices_df, self._polygons_df] def save(self, target_path=None): ''' @@ -560,6 +570,45 @@ def save(self, target_path=None): if data is not None: replace_inp_section(target_path, section, data) + @property + def options(self): + """ + Get/set options section of the INP file. + + :return: options section of the INP file + :rtype: pandas.DataFrame + + Examples: + """ + if self._options_df is None: + self._options_df = create_dataframeINP(self.path, "[OPTIONS]", comment_cols=False) + return self._options_df + + @options.setter + def options(self, df): + """Set inp.options DataFrame.""" + self._options_df = df + + @property + def files(self): + """ + Get/set files section of the INP file. + + :return: files section of the INP file + :rtype: pandas.DataFrame + + Examples: + """ + if self._files_df is None: + self._files_df = create_dataframeINP(self.path, "[FILES]", comment_cols=False) + return self._files_df.reset_index() + + @files.setter + def files(self, df): + """Set inp.files DataFrame.""" + first_col = df.columns[0] + self._files_df = df.set_index(first_col) + @property def conduits(self): """ @@ -593,6 +642,62 @@ def conduits(self, df): """Set inp.conduits DataFrame.""" self._conduits_df = df + @property + def xsections(self): + """ + Get/set pumps section of the INP file. + """ + if self._xsections_df is None: + self._xsections_df = create_dataframeINP(self.path, "[XSECTIONS]", comment_cols=False) + return self._xsections_df + + @xsections.setter + def xsections(self, df): + """Set inp.xsections DataFrame.""" + self._xsections_df = df + + @property + def pumps(self): + """ + Get/set pumps section of the INP file. + """ + if self._pumps_df is None: + self._pumps_df = create_dataframeINP(self.path, "[PUMPS]", comment_cols=False) + return self._pumps_df + + @pumps.setter + def pumps(self, df): + """Set inp.pumps DataFrame.""" + self._pumps_df = df + + @property + def orifices(self): + """ + Get/set orifices section of the INP file. + """ + if self._orifices_df is None: + self._orifices_df = create_dataframeINP(self.path, "[ORIFICES]", comment_cols=False) + return self._orifices_df + + @orifices.setter + def orifices(self, df): + """Set inp.orifices DataFrame.""" + self._orifices_df = df + + @property + def weirs(self): + """ + Get/set weirs section of the INP file. + """ + if self._weirs_df is None: + self._weirs_df = create_dataframeINP(self.path, "[WEIRS]", comment_cols=False) + return self._weirs_df + + @weirs.setter + def weirs(self, df): + """Set inp.weirs DataFrame.""" + self._weirs_df = df + @property def junctions(self): """ diff --git a/swmmio/defs/sectionheaders.py b/swmmio/defs/sectionheaders.py index 86e4b69..1c80d18 100644 --- a/swmmio/defs/sectionheaders.py +++ b/swmmio/defs/sectionheaders.py @@ -6,6 +6,7 @@ inp_header_dict = { '[TITLE]':'blob', '[OPTIONS]':'Name Value', + '[FILES]': 'Action FileType FileName', '[CONDUITS]': 'Name InletNode OutletNode Length ManningN InletOffset OutletOffset InitFlow MaxFlow', '[COORDINATES]': 'Name X Y', '[JUNCTIONS]': 'Name InvertElev MaxDepth InitDepth SurchargeDepth PondedArea', @@ -13,8 +14,7 @@ '[OUTFALLS]': 'Name InvertElev OutfallType StageOrTimeseries TideGate', '[STORAGE]': 'Name InvertElev MaxD InitDepth StorageCurve Coefficient Exponent Constant PondedArea EvapFrac SuctionHead Conductivity InitialDeficit', '[VERTICES]': 'Name X Y', - '[WEIRS]': 'Name InletNode OutletNode WeirType CrestHeight DischCoeff FlapGate EndCon EndCoeff', - '[PUMPS]':'Name', + '[WEIRS]': 'Name InletNode OutletNode WeirType CrestHeight DischCoeff FlapGate EndCon EndCoeff Surcharge RoadWidth RoadSurf', '[XSECTIONS]':'Link Shape Geom1 Geom2 Geom3 Geom4 Barrels', '[SUBCATCHMENTS]':'Name Raingage Outlet Area PercImperv Width PercSlope CurbLength SnowPack', '[SUBAREAS]':'Name N-Imperv N-Perv S-Imperv S-Perv PctZero RouteTo PctRouted', diff --git a/swmmio/elements.py b/swmmio/elements.py index 0ff2b5c..c558733 100644 --- a/swmmio/elements.py +++ b/swmmio/elements.py @@ -58,6 +58,9 @@ def __call__(self): df_other = create_dataframeINP(self.inp.path, sect, comment_cols=False) df = df.join(df_other) + if df.empty: + return df + # if there is an RPT available, grab relevant sections if self.rpt: for rpt_sect in self.config['rpt_sections']: diff --git a/swmmio/tests/data/__init__.py b/swmmio/tests/data/__init__.py index 0a348a4..3e83c8e 100644 --- a/swmmio/tests/data/__init__.py +++ b/swmmio/tests/data/__init__.py @@ -18,6 +18,7 @@ DATA_PATH, 'model_full_features_network_xy.inp') MODEL_FULL_FEATURES__NET_PATH = os.path.join( DATA_PATH, 'model_full_features_network.inp') +MODEL_FULL_FEATURES_INVALID = os.path.join(DATA_PATH, 'invalid_model.inp') MODEL_BROWARD_COUNTY_PATH = os.path.join(DATA_PATH, 'RUNOFF46_SW5.INP') # version control test models @@ -25,5 +26,6 @@ MODEL_XSECTION_ALT_01 = os.path.join(DATA_PATH, 'alt_test1.inp') MODEL_XSECTION_ALT_02 = os.path.join(DATA_PATH, 'alt_test2.inp') MODEL_XSECTION_ALT_03 = os.path.join(DATA_PATH, 'alt_test3.inp') +MODEL_BLANK = os.path.join(DATA_PATH, 'blank_model.inp') df_test_coordinates_csv = os.path.join(DATA_PATH, 'df_test_coordinates.csv') diff --git a/swmmio/tests/data/invalid_model.inp b/swmmio/tests/data/invalid_model.inp new file mode 100644 index 0000000..651e2bb --- /dev/null +++ b/swmmio/tests/data/invalid_model.inp @@ -0,0 +1,337 @@ +[TITLE] +;;Project Title/Notes + +[OPTIONS] +;;Option Value +FLOW_UNITS CFS +INFILTRATION HORTON +FLOW_ROUTING DYNWAVE +LINK_OFFSETS DEPTH +MIN_SLOPE 0 +ALLOW_PONDING NO +SKIP_STEADY_STATE NO + +START_DATE 11/01/2015 +START_TIME 00:00:00 +REPORT_START_DATE 11/01/2015 +REPORT_START_TIME 00:00:00 +END_DATE 11/04/2015 +END_TIME 00:00:00 +SWEEP_START 01/01 +SWEEP_END 12/31 +DRY_DAYS 0 +REPORT_STEP 00:01:00 +WET_STEP 00:05:00 +DRY_STEP 00:05:00 +ROUTING_STEP 0:00:01 + +INERTIAL_DAMPING NONE +NORMAL_FLOW_LIMITED BOTH +FORCE_MAIN_EQUATION H-W +VARIABLE_STEP 0.75 +LENGTHENING_STEP 0 +MIN_SURFAREA 12.557 +MAX_TRIALS 8 +HEAD_TOLERANCE 0.005 +SYS_FLOW_TOL 5 +LAT_FLOW_TOL 5 +MINIMUM_STEP 0.5 +THREADS 1 + +[EVAPORATION] +;;Data Source Parameters +;;-------------- ---------------- +CONSTANT 0.0 +DRY_ONLY NO + +[RAINGAGES] +;;Name Format Interval SCF Source +;;-------------- --------- ------ ------ ---------- +SCS_24h_Type_I_1in INTENSITY 0:15 1.0 TIMESERIES SCS_24h_Type_I_1in + +[SUBCATCHMENTS] +;;Name Rain Gage Outlet Area %Imperv Width %Slope CurbLen SnowPack +;;-------------- ---------------- ---------------- -------- -------- -------- -------- -------- ---------------- +S1 SCS_24h_Type_I_1in J1 3 30 500 0.5 0 +S2 SCS_24h_Type_I_1in J2 2 100 500 0.5 0 +S3 SCS_24h_Type_I_1in j3 3 100 500 0.5 0 +S4 SCS_24h_Type_I_1in 1 20 25 500 0.5 0 +InvalidS5 SCS_24h_Type_I_1in NonExistantnode1 5 25 500 0.5 0 + +[SUBAREAS] +;;Subcatchment N-Imperv N-Perv S-Imperv S-Perv PctZero RouteTo PctRouted +;;-------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- +S1 0.01 0.1 0.05 0.05 25 OUTLET +S2 0.01 0.1 0.05 0.05 25 OUTLET +S3 0.01 0.1 0.05 0.05 25 OUTLET +S4 0.01 0.1 0.05 0.05 25 OUTLET +InvalidS5 0.01 0.1 0.05 0.05 25 OUTLET + +[INFILTRATION] +;;Subcatchment MaxRate MinRate Decay DryTime MaxInfil +;;-------------- ---------- ---------- ---------- ---------- ---------- +S1 3 0.5 4 7 0 +S2 3 0.5 4 7 0 +S3 3 0.5 4 7 0 +S4 3.0 0.5 4 7 0 +InvalidS5 3.0 0.5 4 7 0 + +[JUNCTIONS] +;;Name Elevation MaxDepth InitDepth SurDepth Aponded +;;-------------- ---------- ---------- ---------- ---------- ---------- +J3 6.547 15 0 0 0 +1 17 0 0 0 0 +2 17 0 0 0 0 +3 16.5 0 0 0 0 +4 16 0 0 0 0 +5 15 0 0 0 0 +J2 13.0 15 0 0 0 +; NonExistantNode2 0 0 0 0 0 +; NonExistantNode1 0 0 0 0 0 + +[OUTFALLS] +;;Name Elevation Type Stage Data Gated Route To +;;-------------- ---------- ---------- ---------------- -------- ---------------- +J4 0 FREE NO + +[STORAGE] +;;Name Elev. MaxDepth InitDepth Shape Curve Name/Params N/A Fevap Psi Ksat IMD +;;-------------- -------- ---------- ----------- ---------- ---------------------------- -------- -------- -------- -------- +J1 13.392 15 0 FUNCTIONAL 1000 0 0 0 0 + +[CONDUITS] +;;Name From Node To Node Length Roughness InOffset OutOffset InitFlow MaxFlow +;;-------------- ---------------- ---------------- ---------- ---------- ---------- ---------- ---------- ---------- +C1:C2 J1 J2 244.63 0.01 0 0 0 0 +C2.1 J2 J3 666 0.01 0 0 0 0 +1 1 4 400 0.01 0 0 0 0 +2 4 5 400 0.01 0 0 0 0 +4 3 4 400 0.01 0 0 0 0 +5 2 5 400 0.01 0 0 0 0 +InvalidLink2 NonExistantNode2 NonExistantNode1 400 0.01 0 0 0 0 +InvalidLink1 NonExistantNode1 3 400 0.01 0 0 0 0 + +[PUMPS] +;;Name From Node To Node Pump Curve Status Sartup Shutoff +;;-------------- ---------------- ---------------- ---------------- ------ -------- -------- +C2 J2 J3 P1_Curve ON 0 0 + +[ORIFICES] +;;Name From Node To Node Type Offset Qcoeff Gated CloseTime +;;-------------- ---------------- ---------------- ------------ ---------- ---------- -------- ---------- +OR1 5 J1 SIDE 0 0.65 NO 0 + +[WEIRS] +;;Name From Node To Node Type CrestHt Qcoeff Gated EndCon EndCoeff Surcharge RoadWidth RoadSurf +;;-------------- ---------------- ---------------- ------------ ---------- ---------- -------- -------- ---------- ---------- ---------- ---------- +C3 J3 J4 TRANSVERSE 0 3.33 NO 0 0 NO + +[XSECTIONS] +;;Link Shape Geom1 Geom2 Geom3 Geom4 Barrels Culvert +;;-------------- ------------ ---------------- ---------- ---------- ---------- ---------- ---------- +C1:C2 CIRCULAR 1 0 0 0 1 +C2.1 CIRCULAR 1 0 0 0 1 +1 CIRCULAR 1 0 0 0 1 +2 CIRCULAR 1 0 0 0 1 +4 CIRCULAR 1 0 0 0 1 +5 CIRCULAR 1 0 0 0 1 +InvalidLink2 CIRCULAR 1 0 0 0 1 +InvalidLink1 CIRCULAR 1 0 0 0 1 +OR1 CIRCULAR 1 0 0 0 +C3 RECT_OPEN 5 1 0 0 + +[CONTROLS] +RULE ValidControl +IF NODE J2 DEPTH > 0 +THEN WEIR C3 SETTING = 0.5 + +; invalid because there are element refs not found in the model +RULE InValidControl +IF NODE J2 DEPTH > 0 +AND LINK 165 FLOW > 100 +THEN WEIR CC33 SETTING = 0.5 + + +[INFLOWS] +;;Node Constituent Time Series Type Mfactor Sfactor Baseline Pattern +;;-------------- ---------------- ---------------- -------- -------- -------- -------- -------- +J3 Flow "" FLOW 1.0 1 1 +J2 FLOW "" FLOW 1.0 1 1 +J1 FLOW "" FLOW 1.0 1 1 + +[CURVES] +;;Name Type X-Value Y-Value +;;-------------- ---------- ---------- ---------- +P1_Curve Pump4 0 10 +P1_Curve 5 20 + +[TIMESERIES] +;;Name Date Time Value +;;-------------- ---------- ---------- ---------- +;SCS_24h_Type_I_1in design storm, total rainfall = 1 in, rain units = in/hr. +SCS_24h_Type_I_1in 0:00 0.0175 +SCS_24h_Type_I_1in 0:15 0.0175 +SCS_24h_Type_I_1in 0:30 0.0175 +SCS_24h_Type_I_1in 0:45 0.0175 +SCS_24h_Type_I_1in 1:00 0.0175 +SCS_24h_Type_I_1in 1:15 0.0175 +SCS_24h_Type_I_1in 1:30 0.0175 +SCS_24h_Type_I_1in 1:45 0.0175 +SCS_24h_Type_I_1in 2:00 0.0205 +SCS_24h_Type_I_1in 2:15 0.0205 +SCS_24h_Type_I_1in 2:30 0.0205 +SCS_24h_Type_I_1in 2:45 0.0205 +SCS_24h_Type_I_1in 3:00 0.0205 +SCS_24h_Type_I_1in 3:15 0.0205 +SCS_24h_Type_I_1in 3:30 0.0205 +SCS_24h_Type_I_1in 3:45 0.0205 +SCS_24h_Type_I_1in 4:00 0.0245 +SCS_24h_Type_I_1in 4:15 0.0245 +SCS_24h_Type_I_1in 4:30 0.0245 +SCS_24h_Type_I_1in 4:45 0.0245 +SCS_24h_Type_I_1in 5:00 0.0245 +SCS_24h_Type_I_1in 5:15 0.0245 +SCS_24h_Type_I_1in 5:30 0.0245 +SCS_24h_Type_I_1in 5:45 0.0245 +SCS_24h_Type_I_1in 6:00 0.031 +SCS_24h_Type_I_1in 6:15 0.031 +SCS_24h_Type_I_1in 6:30 0.031 +SCS_24h_Type_I_1in 6:45 0.031 +SCS_24h_Type_I_1in 7:00 0.038 +SCS_24h_Type_I_1in 7:15 0.038 +SCS_24h_Type_I_1in 7:30 0.038 +SCS_24h_Type_I_1in 7:45 0.038 +SCS_24h_Type_I_1in 8:00 0.05 +SCS_24h_Type_I_1in 8:15 0.05 +SCS_24h_Type_I_1in 8:30 0.07 +SCS_24h_Type_I_1in 8:45 0.07 +SCS_24h_Type_I_1in 9:00 0.098 +SCS_24h_Type_I_1in 9:15 0.098 +SCS_24h_Type_I_1in 9:30 0.236 +SCS_24h_Type_I_1in 9:45 0.612 +SCS_24h_Type_I_1in 10:00 0.136 +SCS_24h_Type_I_1in 10:15 0.136 +SCS_24h_Type_I_1in 10:30 0.082 +SCS_24h_Type_I_1in 10:45 0.082 +SCS_24h_Type_I_1in 11:00 0.06 +SCS_24h_Type_I_1in 11:15 0.06 +SCS_24h_Type_I_1in 11:30 0.06 +SCS_24h_Type_I_1in 11:45 0.052 +SCS_24h_Type_I_1in 12:00 0.048 +SCS_24h_Type_I_1in 12:15 0.048 +SCS_24h_Type_I_1in 12:30 0.042 +SCS_24h_Type_I_1in 12:45 0.042 +SCS_24h_Type_I_1in 13:00 0.042 +SCS_24h_Type_I_1in 13:15 0.042 +SCS_24h_Type_I_1in 13:30 0.038 +SCS_24h_Type_I_1in 13:45 0.038 +SCS_24h_Type_I_1in 14:00 0.0315 +SCS_24h_Type_I_1in 14:15 0.0315 +SCS_24h_Type_I_1in 14:30 0.0315 +SCS_24h_Type_I_1in 14:45 0.0315 +SCS_24h_Type_I_1in 15:00 0.0315 +SCS_24h_Type_I_1in 15:15 0.0315 +SCS_24h_Type_I_1in 15:30 0.0315 +SCS_24h_Type_I_1in 15:45 0.0315 +SCS_24h_Type_I_1in 16:00 0.024 +SCS_24h_Type_I_1in 16:15 0.024 +SCS_24h_Type_I_1in 16:30 0.024 +SCS_24h_Type_I_1in 16:45 0.024 +SCS_24h_Type_I_1in 17:00 0.024 +SCS_24h_Type_I_1in 17:15 0.024 +SCS_24h_Type_I_1in 17:30 0.024 +SCS_24h_Type_I_1in 17:45 0.024 +SCS_24h_Type_I_1in 18:00 0.024 +SCS_24h_Type_I_1in 18:15 0.024 +SCS_24h_Type_I_1in 18:30 0.024 +SCS_24h_Type_I_1in 18:45 0.024 +SCS_24h_Type_I_1in 19:00 0.024 +SCS_24h_Type_I_1in 19:15 0.024 +SCS_24h_Type_I_1in 19:30 0.024 +SCS_24h_Type_I_1in 19:45 0.024 +SCS_24h_Type_I_1in 20:00 0.0185 +SCS_24h_Type_I_1in 20:15 0.0185 +SCS_24h_Type_I_1in 20:30 0.0185 +SCS_24h_Type_I_1in 20:45 0.0185 +SCS_24h_Type_I_1in 21:00 0.0185 +SCS_24h_Type_I_1in 21:15 0.0185 +SCS_24h_Type_I_1in 21:30 0.0185 +SCS_24h_Type_I_1in 21:45 0.0185 +SCS_24h_Type_I_1in 22:00 0.0185 +SCS_24h_Type_I_1in 22:15 0.0185 +SCS_24h_Type_I_1in 22:30 0.0185 +SCS_24h_Type_I_1in 22:45 0.0185 +SCS_24h_Type_I_1in 23:00 0.0185 +SCS_24h_Type_I_1in 23:15 0.0185 +SCS_24h_Type_I_1in 23:30 0.0185 +SCS_24h_Type_I_1in 23:45 0.0185 +SCS_24h_Type_I_1in 24:00 0 + +[REPORT] +;;Reporting Options +INPUT YES +CONTROLS YES +SUBCATCHMENTS NONE +NODES ALL +LINKS NONE + +[TAGS] + +[MAP] +DIMENSIONS -100.036 -181.979 708.126 213.879 +Units Feet + +[COORDINATES] +;;Node X-Coord Y-Coord +;;-------------- ------------------ ------------------ +J3 459.058 -113.145 +1 -77.021 -78.321 +2 -84.988 43.833 +3 -18.600 -71.239 +4 -67.284 -37.603 +5 -56.662 15.507 +J2 238.750 -53.332 +NonExistantNode2 45.495 -131.516 +NonExistantNode1 -14.111 -103.649 +J4 671.391 -163.985 +J1 0.000 0.000 + +[VERTICES] +;;Link X-Coord Y-Coord +;;-------------- ------------------ ------------------ +C2.1 295.636 -159.756 +C2.1 360.253 -181.886 +4 -23.911 -51.766 +5 -85.873 19.933 + +[Polygons] +;;Subcatchment X-Coord Y-Coord +;;-------------- ------------------ ------------------ +S1 110.154 195.885 +S1 110.154 47.351 +S1 -56.323 42.367 +S1 -63.301 181.928 +S1 110.154 195.885 +S2 394.261 131.088 +S2 410.211 -20.436 +S2 245.728 -19.439 +S2 235.759 110.154 +S2 394.261 131.088 +S3 660.425 55.326 +S3 657.435 -104.173 +S3 519.867 -96.198 +S3 509.898 50.342 +S3 660.425 55.326 +S4 -63.523 -116.383 +S4 -82.996 -174.805 +S4 -154.695 -168.608 +S4 -148.499 -126.120 +InvalidS5 28.465 -92.811 +InvalidS5 43.173 -69.588 +InvalidS5 99.682 -90.489 +InvalidS5 61.751 -112.164 + +[SYMBOLS] +;;Gage X-Coord Y-Coord +;;-------------- ------------------ ------------------ + diff --git a/swmmio/tests/test_version_control.py b/swmmio/tests/test_version_control.py index b19c80f..cdd96e1 100644 --- a/swmmio/tests/test_version_control.py +++ b/swmmio/tests/test_version_control.py @@ -1,6 +1,6 @@ -from swmmio.tests.data import (MODEL_XSECTION_BASELINE, MODEL_XSECTION_ALT_01, - MODEL_XSECTION_ALT_02, MODEL_XSECTION_ALT_03) -from swmmio import swmmio +from swmmio.tests.data import (MODEL_XSECTION_BASELINE, MODEL_FULL_FEATURES_PATH, + MODEL_XSECTION_ALT_02, MODEL_XSECTION_ALT_03, MODEL_BLANK) + from swmmio.version_control import utils as vc_utils from swmmio.version_control import inp from swmmio.utils import functions as funcs @@ -33,3 +33,23 @@ def test_create_inp_build_instructions(): juncs = bi.instructions['[JUNCTIONS]'] assert(all(j in juncs.altered.index for j in [ 'dummy_node1', 'dummy_node5'])) + + +# def test_add_models(): +# inp.create_inp_build_instructions(MODEL_BLANK, +# MODEL_XSECTION_ALT_03, +# 'vc_dir', +# 'test_version_id', 'cool comments') +# bi_1 = vc_utils.newest_file('vc_dir') +# bi1 = inp.BuildInstructions(bi_1) +# +# inp.create_inp_build_instructions(MODEL_BLANK, +# MODEL_FULL_FEATURES_PATH, +# 'vc_dir', +# 'test_model_full_feat', 'cool comments') +# bi_2 = vc_utils.newest_file('vc_dir') +# bi2 = inp.BuildInstructions(bi_2) +# +# bi3 = bi1 + bi2 +# bi3.build(MODEL_BLANK, 'added_model.inp') + diff --git a/swmmio/utils/dataframes.py b/swmmio/utils/dataframes.py index e327f54..d07f05f 100644 --- a/swmmio/utils/dataframes.py +++ b/swmmio/utils/dataframes.py @@ -14,8 +14,8 @@ def create_dataframeBI(bi_path, section='[CONDUITS]'): tempfilepath = txt.extract_section_from_inp(bi_path, section, headerdefs=headerdefs, skipheaders=True) - df = pd.read_table(tempfilepath, header=None, delim_whitespace=True, - skiprows=[0], index_col=0, names=headerlist, comment=None) + df = pd.read_csv(tempfilepath, header=None, delim_whitespace=True, + skiprows=[0], index_col=0, names=headerlist, comment=None) os.remove(tempfilepath) # clean up @@ -57,8 +57,9 @@ def create_dataframeINP(inp_path, section='[CONDUITS]', ignore_comments=True, headerlist = headerdefs['headers'][section].split() if comment_cols: headerlist = headerlist + [';', 'Comment', 'Origin'] + dtypes = {'InletNode': str, 'OutletNode': str} df = pd.read_csv(tempfilepath, header=None, delim_whitespace=True, skiprows=[0], - index_col=0, names=headerlist, comment=comment_str)# , encoding='latin1') + index_col=0, names=headerlist, comment=comment_str, dtype=dtypes)# , encoding='latin1') if comment_cols: # add new blank comment column after a semicolon column @@ -125,13 +126,13 @@ def create_dataframeRPT(rpt_path, section='Link Flow Summary', element_id=None): if element_id: # we'retrying to pull a time series, parse the datetimes by # concatenating the Date Time columns (cols 1,2) - df0 = pd.read_table(tempfilepath, delim_whitespace=True) + df0 = pd.read_csv(tempfilepath, delim_whitespace=True) df = df0[df0.columns[2:]] # the data sans date time columns df.index = pd.to_datetime(df0['Date'] + ' ' + df0['Time']) df.index.name = "".join(df0.columns[:2]) else: # this section header is recognized, will be organized into known cols - df = pd.read_table(tempfilepath, delim_whitespace=True, index_col=0) + df = pd.read_csv(tempfilepath, delim_whitespace=True, index_col=0) os.remove(tempfilepath) diff --git a/swmmio/utils/functions.py b/swmmio/utils/functions.py index e6e45e4..86dd220 100644 --- a/swmmio/utils/functions.py +++ b/swmmio/utils/functions.py @@ -1,6 +1,7 @@ from swmmio.defs.sectionheaders import inp_header_dict, rpt_header_dict from collections import deque import pandas as pd +from swmmio.tests.data import MODEL_FULL_FEATURES_INVALID def random_alphanumeric(n=6): @@ -52,7 +53,7 @@ def multidigraph_from_edges(edges, source, target): nodes = nodes.join(flows) conduits = model.conduits() - links = pd.concat([conduits, model.orifices(), model.weirs(), model.pumps()]) + links = pd.concat([conduits, model.orifices(), model.weirs(), model.pumps()], sort=True) links['facilityid'] = links.index # create a nx.MultiDiGraph from the combined model links, add node data, set CRS @@ -78,6 +79,48 @@ def multidigraph_from_edges(edges, source, target): return G +def find_invalid_links(model, link_type, drop=False): + nids = model.nodes().index + elems = getattr(model.inp, link_type) + invalids = elems.index[~(elems.InletNode.isin(nids) & elems.OutletNode.isin(nids))] + if drop: + df = elems.loc[elems.InletNode.isin(nids) & elems.OutletNode.isin(nids)] + setattr(model.inp, link_type, df) + return invalids.tolist() + + +def drop_invalid_model_elements(model): + """ + Identify references to elements in the model that are undefined and remove them from the + model. These should coincide with warnings/errors produced by SWMM5 when undefined elements + are referenced in links, subcatchments, and controls. + :param model: swmmio.Model + :return: + >>> import swmmio + >>> m = swmmio.Model(MODEL_FULL_FEATURES_INVALID) + >>> drop_invalid_model_elements(m) + ['InvalidLink2', 'InvalidLink1'] + >>> m.inp.conduits.index + Index(['C1:C2', 'C2.1', '1', '2', '4', '5'], dtype='object', name='Name') + """ + + nids = model.nodes().index + + # drop links with bad refs to inlet/outlet nodes + inv_conds = find_invalid_links(model, 'conduits', drop=True) + inv_pumps = find_invalid_links(model, 'pumps', drop=True) + inv_orifs = find_invalid_links(model, 'orifices', drop=True) + inv_weirs = find_invalid_links(model, 'weirs', drop=True) + + invalids = inv_conds + inv_pumps + inv_orifs + inv_weirs + + # drop other parts of bad links, subcatchments + model.inp.xsections = model.inp.xsections.loc[~model.inp.xsections.index.isin(invalids)] + model.inp.subcatchments = model.inp.subcatchments.loc[model.inp.subcatchments['Outlet'].isin(nids)] + + return invalids + + # Todo: use an OrderedDict instead of a dict and a "order" list def complete_inp_headers(inpfilepath): """ diff --git a/swmmio/version_control/inp.py b/swmmio/version_control/inp.py index 375cd16..bf363c9 100644 --- a/swmmio/version_control/inp.py +++ b/swmmio/version_control/inp.py @@ -153,7 +153,8 @@ def __init__(self, model1=None, model2=None, section='[JUNCTIONS]', build_instr_ #drop dupes on the set, all things that did not changed should have 1 row changes_with_dupes = full_set.drop_duplicates() #duplicate indicies are rows that have changes, isolate these - changed_ids = changes_with_dupes.index.get_duplicates() + # idx[idx.duplicated()].unique() + changed_ids = changes_with_dupes.index[changes_with_dupes.index.duplicated()].unique() #.get_duplicates() added = df2.loc[added_ids].copy() added['Comment'] = 'Added'# from model {}'.format(model2.inp.path) From db9de0c61cce863a72c741a9a0d12f6bc0d455b1 Mon Sep 17 00:00:00 2001 From: Adam Erispaha Date: Wed, 24 Apr 2019 21:38:36 -0400 Subject: [PATCH 5/9] added draft model validator function --- .gitignore | 7 + swmmio/core.py | 124 ++++++++- swmmio/defs/section_headers.json | 37 +++ swmmio/tests/data/model_curve_num.inp | 306 ++++++++++++++++++++++ swmmio/tests/data/model_mod_greenampt.inp | 306 ++++++++++++++++++++++ swmmio/tests/data/model_mod_horton.inp | 306 ++++++++++++++++++++++ swmmio/utils/dataframes.py | 25 +- swmmio/utils/functions.py | 85 +++--- 8 files changed, 1149 insertions(+), 47 deletions(-) create mode 100644 swmmio/tests/data/model_curve_num.inp create mode 100644 swmmio/tests/data/model_mod_greenampt.inp create mode 100644 swmmio/tests/data/model_mod_horton.inp diff --git a/.gitignore b/.gitignore index f889e66..75811c2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,10 @@ _build/ # IDE Stuff *.idea/ + +# SWMM files +*.out +#*.ini +*.chi +*.~inp +*.thm \ No newline at end of file diff --git a/swmmio/core.py b/swmmio/core.py index ab24c2a..552d28d 100644 --- a/swmmio/core.py +++ b/swmmio/core.py @@ -13,6 +13,8 @@ import warnings import swmmio from swmmio.elements import ModelSection +from swmmio.defs import HEADERS +# from swmmio.utils.functions import find_invalid_links class Model(object): @@ -534,6 +536,9 @@ def __init__(self, file_path): self._vertices_df = None self._polygons_df = None self._subcatchments_df = None + self._subareas_df = None + self._infiltration_df = None + self._headers = None SWMMIOFile.__init__(self, file_path) # run the superclass init @@ -549,7 +554,9 @@ def __init__(self, file_path): '[STORAGE]', '[OUTFALLS]', '[VERTICES]', - '[SUBCATCHMENTS]' + '[SUBCATCHMENTS]', + '[SUBAREAS]', + '[INFILTRATION]' ] def save(self, target_path=None): @@ -570,6 +577,31 @@ def save(self, target_path=None): if data is not None: replace_inp_section(target_path, section, data) + def validate(self): + """ + Detect and remove invalid model elements + :return: None + """ + drop_invalid_model_elements(self) + + @property + def headers(self): + """ + Get the proper section headers for the INP file. + """ + if self._headers is None: + + infil_type = self.options.loc['INFILTRATION', 'Value'] + infil_cols = HEADERS['infiltration_cols'][infil_type] + + # overwrite the dynamic sections with proper header cols + h = dict(HEADERS['inp_sections']) + h['[INFILTRATION]'] = list(infil_cols) + self._headers = h + + else: + return self._headers + @property def options(self): """ @@ -581,7 +613,8 @@ def options(self): Examples: """ if self._options_df is None: - self._options_df = create_dataframeINP(self.path, "[OPTIONS]", comment_cols=False) + self._options_df = create_dataframeINP(self.path, "[OPTIONS]", comment_cols=False, + headers=self._headers) return self._options_df @options.setter @@ -589,6 +622,16 @@ def options(self, df): """Set inp.options DataFrame.""" self._options_df = df + # update the headers + infil_type = df.loc['INFILTRATION', 'Value'] + infil_cols = HEADERS['infiltration_cols'][infil_type] + + # overwrite the dynamic sections with proper header cols + h = dict(HEADERS['inp_sections']) + h['[INFILTRATION]'] = list(infil_cols) + self._headers = h + self._infiltration_df = None + @property def files(self): """ @@ -788,7 +831,8 @@ def subcatchments(self): Examples: """ if self._subcatchments_df is None: - self._subcatchments_df = create_dataframeINP(self.path, "[SUBCATCHMENTS]", comment_cols=False) + self._subcatchments_df = create_dataframeINP(self.path, "[SUBCATCHMENTS]", comment_cols=False, + headers=self._headers) return self._subcatchments_df @subcatchments.setter @@ -796,6 +840,36 @@ def subcatchments(self, df): """Set inp.subcatchments DataFrame.""" self._subcatchments_df = df + @property + def subareas(self): + """ + Get/set subareas section of the INP file. + """ + if self._subareas_df is None: + self._subareas_df = create_dataframeINP(self.path, "[SUBAREAS]", comment_cols=False, + headers=self._headers) + return self._subareas_df + + @subareas.setter + def subareas(self, df): + """Set inp.subareas DataFrame.""" + self._subareas_df = df + + @property + def infiltration(self): + """ + Get/set infiltration section of the INP file. + """ + if self._infiltration_df is None: + self._infiltration_df = create_dataframeINP(self.path, "[INFILTRATION]", comment_cols=False, + headers=self._headers) + return self._infiltration_df + + @infiltration.setter + def infiltration(self, df): + """Set inp.infiltration DataFrame.""" + self._infiltration_df = df + @property def coordinates(self): """ @@ -804,7 +878,8 @@ def coordinates(self): """ if self._coordinates_df is not None: return self._coordinates_df - self._coordinates_df = create_dataframeINP(self.path, "[COORDINATES]", comment_cols=False) + self._coordinates_df = create_dataframeINP(self.path, "[COORDINATES]", comment_cols=False, + headers=self._headers) return self._coordinates_df @coordinates.setter @@ -843,3 +918,44 @@ def polygons(self): def polygons(self, df): """Set inp.polygons DataFrame.""" self._polygons_df = df + + +def drop_invalid_model_elements(inp): + """ + Identify references to elements in the model that are undefined and remove them from the + model. These should coincide with warnings/errors produced by SWMM5 when undefined elements + are referenced in links, subcatchments, and controls. + :param model: swmmio.Model + :return: + >>> import swmmio + >>> from swmmio.tests.data import MODEL_FULL_FEATURES_INVALID + >>> m = swmmio.Model(MODEL_FULL_FEATURES_INVALID) + >>> drop_invalid_model_elements(m.inp) + ['InvalidLink2', 'InvalidLink1'] + >>> m.inp.conduits.index + Index(['C1:C2', 'C2.1', '1', '2', '4', '5'], dtype='object', name='Name') + """ + from swmmio.utils.dataframes import create_dataframeINP + juncs = create_dataframeINP(inp.path, "[JUNCTIONS]").index.tolist() + outfs = create_dataframeINP(inp.path, "[OUTFALLS]").index.tolist() + stors = create_dataframeINP(inp.path, "[STORAGE]").index.tolist() + nids = juncs + outfs + stors + + # drop links with bad refs to inlet/outlet nodes + from swmmio.utils.functions import find_invalid_links + inv_conds = find_invalid_links(inp, nids, 'conduits', drop=True) + inv_pumps = find_invalid_links(inp, nids, 'pumps', drop=True) + inv_orifs = find_invalid_links(inp, nids, 'orifices', drop=True) + inv_weirs = find_invalid_links(inp, nids, 'weirs', drop=True) + + # drop other parts of bad links + invalid_links = inv_conds + inv_pumps + inv_orifs + inv_weirs + inp.xsections = inp.xsections.loc[~inp.xsections.index.isin(invalid_links)] + + # drop invalid subcats and their related components + invalid_subcats = inp.subcatchments.index[~inp.subcatchments['Outlet'].isin(nids)] + inp.subcatchments = inp.subcatchments.loc[~inp.subcatchments.index.isin(invalid_subcats)] + inp.subareas = inp.subareas.loc[~inp.subareas.index.isin(invalid_subcats)] + inp.infiltration = inp.infiltration.loc[~inp.infiltration.index.isin(invalid_subcats)] + + return invalid_links + invalid_subcats \ No newline at end of file diff --git a/swmmio/defs/section_headers.json b/swmmio/defs/section_headers.json index b522edb..2ea897d 100644 --- a/swmmio/defs/section_headers.json +++ b/swmmio/defs/section_headers.json @@ -13,5 +13,42 @@ "orifices": { "inp_sections": ["[ORIFICES]"], "rpt_sections": ["Link Flow Summary"] + }, + "subcatchments": { + "inp_sections": ["[SUBCATCHMENTS]"], + "rpt_sections": [], + "columns": ["Name", "Raingage", "Outlet", "Area", "PercImperv", "Width", "PercSlope", "CurbLength", "SnowPack"] + }, + "inp_sections": { + "[TITLE]": ["blob"], + "[OPTIONS]": ["Name", "Value"], + "[FILES]": ["Action", "FileType", "FileName"], + "[CONDUITS]": ["Name", "InletNode", "OutletNode", "Length", "ManningN", "InletOffset", "OutletOffset", "InitFlow", "MaxFlow"], + "[COORDINATES]": ["Name", "X", "Y"], + "[JUNCTIONS]": ["Name", "InvertElev", "MaxDepth", "InitDepth", "SurchargeDepth", "PondedArea"], + "[ORIFICES]": ["Name", "InletNode", "OutletNode", "OrificeType", "CrestHeight", "DischCoeff", "FlapGate", "OpenCloseTime"], + "[OUTFALLS]": ["Name", "InvertElev", "OutfallType", "StageOrTimeseries", "TideGate"], + "[STORAGE]": ["Name", "InvertElev", "MaxD", "InitDepth", "StorageCurve", "Coefficient", "Exponent", "Constant", "PondedArea", "EvapFrac", "SuctionHead", "Conductivity", "InitialDeficit"], + "[VERTICES]": ["Name", "X", "Y"], + "[WEIRS]": ["Name", "InletNode", "OutletNode", "WeirType", "CrestHeight", "DischCoeff", "FlapGate", "EndCon", "EndCoeff", "Surcharge", "RoadWidth", "RoadSurf"], + "[XSECTIONS]": ["Link", "Shape", "Geom1", "Geom2", "Geom3", "Geom4", "Barrels"], + "[SUBCATCHMENTS]": ["Name", "Raingage", "Outlet", "Area", "PercImperv", "Width", "PercSlope", "CurbLength", "SnowPack"], + "[SUBAREAS]": ["Name", "N-Imperv", "N-Perv", "S-Imperv", "S-Perv", "PctZero", "RouteTo", "PctRouted"], + "[LOSSES]": ["Link", "Inlet", "Outlet", "Average", "FlapGate"], + "[PUMPS]": ["Name", "InletNode", "OutletNode", "PumpCurve", "InitStatus", "Depth", "ShutoffDepth"], + "[DWF]": ["Node", "Parameter", "AverageValue", "TimePatterns"], + "[RAINGAGES]": ["Name", "RainType", "TimeIntrv", "SnowCatch", "DataSourceType", "DataSourceName"], + "[INFILTRATION]": ["Subcatchment", "Suction", "HydCon", "IMDmax"], + "[Polygons]": ["Name", "X", "Y"], + "[REPORT]": ["Param", "Status"], + "[TAGS]": ["ElementType", "Name", "Tag"], + "[MAP]": ["x1", "y1", "x2", "y2"] + }, + "infiltration_cols": { + "HORTON": ["Subcatchment", "MaxRate", "MinRate", "Decay", "DryTime", "MaxInfil"], + "MODIFIED_HORTON": ["Subcatchment", "MaxRate", "MinRate", "Decay", "DryTime", "MaxInfil"], + "GREEN_AMPT": ["Subcatchment", "Suction", "HydCon", "IMDmax"], + "MODIFIED_GREEN_AMPT": ["Subcatchment", "Suction", "Ksat", "IMD"], + "CURVE_NUMBER": ["CurveNum", "Conductivity (depreciated)", "DryTime"] } } diff --git a/swmmio/tests/data/model_curve_num.inp b/swmmio/tests/data/model_curve_num.inp new file mode 100644 index 0000000..927b3d9 --- /dev/null +++ b/swmmio/tests/data/model_curve_num.inp @@ -0,0 +1,306 @@ +[TITLE] +;;Project Title/Notes + +[OPTIONS] +;;Option Value +FLOW_UNITS CFS +INFILTRATION CURVE_NUMBER +FLOW_ROUTING DYNWAVE +LINK_OFFSETS DEPTH +MIN_SLOPE 0 +ALLOW_PONDING NO +SKIP_STEADY_STATE NO + +START_DATE 11/01/2015 +START_TIME 00:00:00 +REPORT_START_DATE 11/01/2015 +REPORT_START_TIME 00:00:00 +END_DATE 11/04/2015 +END_TIME 00:00:00 +SWEEP_START 01/01 +SWEEP_END 12/31 +DRY_DAYS 0 +REPORT_STEP 00:01:00 +WET_STEP 00:05:00 +DRY_STEP 00:05:00 +ROUTING_STEP 0:00:01 + +INERTIAL_DAMPING NONE +NORMAL_FLOW_LIMITED BOTH +FORCE_MAIN_EQUATION H-W +VARIABLE_STEP 0.75 +LENGTHENING_STEP 0 +MIN_SURFAREA 12.557 +MAX_TRIALS 8 +HEAD_TOLERANCE 0.005 +SYS_FLOW_TOL 5 +LAT_FLOW_TOL 5 +MINIMUM_STEP 0.5 +THREADS 1 + +[EVAPORATION] +;;Data Source Parameters +;;-------------- ---------------- +CONSTANT 0.0 +DRY_ONLY NO + +[RAINGAGES] +;;Name Format Interval SCF Source +;;-------------- --------- ------ ------ ---------- +SCS_24h_Type_I_1in INTENSITY 0:15 1.0 TIMESERIES SCS_24h_Type_I_1in + +[SUBCATCHMENTS] +;;Name Rain Gage Outlet Area %Imperv Width %Slope CurbLen SnowPack +;;-------------- ---------------- ---------------- -------- -------- -------- -------- -------- ---------------- +S1 SCS_24h_Type_I_1in J1 3 30 500 0.5 0 +S2 SCS_24h_Type_I_1in J2 2 100 500 0.5 0 +S3 SCS_24h_Type_I_1in j3 3 100 500 0.5 0 +S4 SCS_24h_Type_I_1in 1 20 25 500 0.5 0 + +[SUBAREAS] +;;Subcatchment N-Imperv N-Perv S-Imperv S-Perv PctZero RouteTo PctRouted +;;-------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- +S1 0.01 0.1 0.05 0.05 25 OUTLET +S2 0.01 0.1 0.05 0.05 25 OUTLET +S3 0.01 0.1 0.05 0.05 25 OUTLET +S4 0.01 0.1 0.05 0.05 25 OUTLET + +[INFILTRATION] +;;Subcatchment CurveNum DryTime +;;-------------- ---------- ---------- ---------- +S1 3 0.5 4 +S2 3 0.5 3.2 +S3 3 0.5 4 +S4 3.0 0.5 4 + +[JUNCTIONS] +;;Name Elevation MaxDepth InitDepth SurDepth Aponded +;;-------------- ---------- ---------- ---------- ---------- ---------- +J3 6.547 15 0 0 0 +1 17 0 0 0 0 +2 17 0 0 0 0 +3 16.5 0 0 0 0 +4 16 0 0 0 0 +5 15 0 0 0 0 +J2 13.0 15 0 0 0 + +[OUTFALLS] +;;Name Elevation Type Stage Data Gated Route To +;;-------------- ---------- ---------- ---------------- -------- ---------------- +J4 0 FREE NO + +[STORAGE] +;;Name Elev. MaxDepth InitDepth Shape Curve Name/Params N/A Fevap Psi Ksat IMD +;;-------------- -------- ---------- ----------- ---------- ---------------------------- -------- -------- -------- -------- +J1 13.392 15 0 FUNCTIONAL 1000 0 0 0 0 + +[CONDUITS] +;;Name From Node To Node Length Roughness InOffset OutOffset InitFlow MaxFlow +;;-------------- ---------------- ---------------- ---------- ---------- ---------- ---------- ---------- ---------- +C1:C2 J1 J2 244.63 0.01 0 0 0 0 +C2.1 J2 J3 666 0.01 0 0 0 0 +1 1 4 400 0.01 0 0 0 0 +2 4 5 400 0.01 0 0 0 0 +3 5 J1 400 0.01 0 0 0 0 +4 3 4 400 0.01 0 0 0 0 +5 2 5 400 0.01 0 0 0 0 + +[PUMPS] +;;Name From Node To Node Pump Curve Status Sartup Shutoff +;;-------------- ---------------- ---------------- ---------------- ------ -------- -------- +C2 J2 J3 P1_Curve ON 0 0 + +[WEIRS] +;;Name From Node To Node Type CrestHt Qcoeff Gated EndCon EndCoeff Surcharge RoadWidth RoadSurf +;;-------------- ---------------- ---------------- ------------ ---------- ---------- -------- -------- ---------- ---------- ---------- ---------- +C3 J3 J4 TRANSVERSE 0 3.33 NO 0 0 NO + +[XSECTIONS] +;;Link Shape Geom1 Geom2 Geom3 Geom4 Barrels Culvert +;;-------------- ------------ ---------------- ---------- ---------- ---------- ---------- ---------- +C1:C2 CIRCULAR 1 0 0 0 1 +C2.1 CIRCULAR 1 0 0 0 1 +1 CIRCULAR 1 0 0 0 1 +2 CIRCULAR 1 0 0 0 1 +3 CIRCULAR 1 0 0 0 1 +4 CIRCULAR 1 0 0 0 1 +5 CIRCULAR 1 0 0 0 1 +C3 RECT_OPEN 5 1 0 0 + +[INFLOWS] +;;Node Constituent Time Series Type Mfactor Sfactor Baseline Pattern +;;-------------- ---------------- ---------------- -------- -------- -------- -------- -------- +J3 Flow "" FLOW 1.0 1 1 +J2 FLOW "" FLOW 1.0 1 1 +J1 FLOW "" FLOW 1.0 1 1 + +[CURVES] +;;Name Type X-Value Y-Value +;;-------------- ---------- ---------- ---------- +P1_Curve Pump4 0 10 +P1_Curve 5 20 + +[TIMESERIES] +;;Name Date Time Value +;;-------------- ---------- ---------- ---------- +;SCS_24h_Type_I_1in design storm, total rainfall = 1 in, rain units = in/hr. +SCS_24h_Type_I_1in 0:00 0.0175 +SCS_24h_Type_I_1in 0:15 0.0175 +SCS_24h_Type_I_1in 0:30 0.0175 +SCS_24h_Type_I_1in 0:45 0.0175 +SCS_24h_Type_I_1in 1:00 0.0175 +SCS_24h_Type_I_1in 1:15 0.0175 +SCS_24h_Type_I_1in 1:30 0.0175 +SCS_24h_Type_I_1in 1:45 0.0175 +SCS_24h_Type_I_1in 2:00 0.0205 +SCS_24h_Type_I_1in 2:15 0.0205 +SCS_24h_Type_I_1in 2:30 0.0205 +SCS_24h_Type_I_1in 2:45 0.0205 +SCS_24h_Type_I_1in 3:00 0.0205 +SCS_24h_Type_I_1in 3:15 0.0205 +SCS_24h_Type_I_1in 3:30 0.0205 +SCS_24h_Type_I_1in 3:45 0.0205 +SCS_24h_Type_I_1in 4:00 0.0245 +SCS_24h_Type_I_1in 4:15 0.0245 +SCS_24h_Type_I_1in 4:30 0.0245 +SCS_24h_Type_I_1in 4:45 0.0245 +SCS_24h_Type_I_1in 5:00 0.0245 +SCS_24h_Type_I_1in 5:15 0.0245 +SCS_24h_Type_I_1in 5:30 0.0245 +SCS_24h_Type_I_1in 5:45 0.0245 +SCS_24h_Type_I_1in 6:00 0.031 +SCS_24h_Type_I_1in 6:15 0.031 +SCS_24h_Type_I_1in 6:30 0.031 +SCS_24h_Type_I_1in 6:45 0.031 +SCS_24h_Type_I_1in 7:00 0.038 +SCS_24h_Type_I_1in 7:15 0.038 +SCS_24h_Type_I_1in 7:30 0.038 +SCS_24h_Type_I_1in 7:45 0.038 +SCS_24h_Type_I_1in 8:00 0.05 +SCS_24h_Type_I_1in 8:15 0.05 +SCS_24h_Type_I_1in 8:30 0.07 +SCS_24h_Type_I_1in 8:45 0.07 +SCS_24h_Type_I_1in 9:00 0.098 +SCS_24h_Type_I_1in 9:15 0.098 +SCS_24h_Type_I_1in 9:30 0.236 +SCS_24h_Type_I_1in 9:45 0.612 +SCS_24h_Type_I_1in 10:00 0.136 +SCS_24h_Type_I_1in 10:15 0.136 +SCS_24h_Type_I_1in 10:30 0.082 +SCS_24h_Type_I_1in 10:45 0.082 +SCS_24h_Type_I_1in 11:00 0.06 +SCS_24h_Type_I_1in 11:15 0.06 +SCS_24h_Type_I_1in 11:30 0.06 +SCS_24h_Type_I_1in 11:45 0.052 +SCS_24h_Type_I_1in 12:00 0.048 +SCS_24h_Type_I_1in 12:15 0.048 +SCS_24h_Type_I_1in 12:30 0.042 +SCS_24h_Type_I_1in 12:45 0.042 +SCS_24h_Type_I_1in 13:00 0.042 +SCS_24h_Type_I_1in 13:15 0.042 +SCS_24h_Type_I_1in 13:30 0.038 +SCS_24h_Type_I_1in 13:45 0.038 +SCS_24h_Type_I_1in 14:00 0.0315 +SCS_24h_Type_I_1in 14:15 0.0315 +SCS_24h_Type_I_1in 14:30 0.0315 +SCS_24h_Type_I_1in 14:45 0.0315 +SCS_24h_Type_I_1in 15:00 0.0315 +SCS_24h_Type_I_1in 15:15 0.0315 +SCS_24h_Type_I_1in 15:30 0.0315 +SCS_24h_Type_I_1in 15:45 0.0315 +SCS_24h_Type_I_1in 16:00 0.024 +SCS_24h_Type_I_1in 16:15 0.024 +SCS_24h_Type_I_1in 16:30 0.024 +SCS_24h_Type_I_1in 16:45 0.024 +SCS_24h_Type_I_1in 17:00 0.024 +SCS_24h_Type_I_1in 17:15 0.024 +SCS_24h_Type_I_1in 17:30 0.024 +SCS_24h_Type_I_1in 17:45 0.024 +SCS_24h_Type_I_1in 18:00 0.024 +SCS_24h_Type_I_1in 18:15 0.024 +SCS_24h_Type_I_1in 18:30 0.024 +SCS_24h_Type_I_1in 18:45 0.024 +SCS_24h_Type_I_1in 19:00 0.024 +SCS_24h_Type_I_1in 19:15 0.024 +SCS_24h_Type_I_1in 19:30 0.024 +SCS_24h_Type_I_1in 19:45 0.024 +SCS_24h_Type_I_1in 20:00 0.0185 +SCS_24h_Type_I_1in 20:15 0.0185 +SCS_24h_Type_I_1in 20:30 0.0185 +SCS_24h_Type_I_1in 20:45 0.0185 +SCS_24h_Type_I_1in 21:00 0.0185 +SCS_24h_Type_I_1in 21:15 0.0185 +SCS_24h_Type_I_1in 21:30 0.0185 +SCS_24h_Type_I_1in 21:45 0.0185 +SCS_24h_Type_I_1in 22:00 0.0185 +SCS_24h_Type_I_1in 22:15 0.0185 +SCS_24h_Type_I_1in 22:30 0.0185 +SCS_24h_Type_I_1in 22:45 0.0185 +SCS_24h_Type_I_1in 23:00 0.0185 +SCS_24h_Type_I_1in 23:15 0.0185 +SCS_24h_Type_I_1in 23:30 0.0185 +SCS_24h_Type_I_1in 23:45 0.0185 +SCS_24h_Type_I_1in 24:00 0 + +[REPORT] +;;Reporting Options +INPUT YES +CONTROLS YES +SUBCATCHMENTS NONE +NODES ALL +LINKS NONE + +[TAGS] + +[MAP] +DIMENSIONS -100.036 -181.979 708.126 213.879 +Units Feet + +[COORDINATES] +;;Node X-Coord Y-Coord +;;-------------- ------------------ ------------------ +J3 459.058 -113.145 +1 -77.021 -78.321 +2 -84.988 43.833 +3 -18.600 -71.239 +4 -67.284 -37.603 +5 -56.662 15.507 +J2 238.750 -53.332 +J4 671.391 -163.985 +J1 0.000 0.000 + +[VERTICES] +;;Link X-Coord Y-Coord +;;-------------- ------------------ ------------------ +C2.1 295.636 -159.756 +C2.1 360.253 -181.886 +4 -23.911 -51.766 +5 -85.873 19.933 + +[Polygons] +;;Subcatchment X-Coord Y-Coord +;;-------------- ------------------ ------------------ +S1 110.154 195.885 +S1 110.154 47.351 +S1 -56.323 42.367 +S1 -63.301 181.928 +S1 110.154 195.885 +S2 394.261 131.088 +S2 410.211 -20.436 +S2 245.728 -19.439 +S2 235.759 110.154 +S2 394.261 131.088 +S3 660.425 55.326 +S3 657.435 -104.173 +S3 519.867 -96.198 +S3 509.898 50.342 +S3 660.425 55.326 +S4 -63.523 -116.383 +S4 -82.996 -174.805 +S4 -154.695 -168.608 +S4 -148.499 -126.120 + +[SYMBOLS] +;;Gage X-Coord Y-Coord +;;-------------- ------------------ ------------------ + diff --git a/swmmio/tests/data/model_mod_greenampt.inp b/swmmio/tests/data/model_mod_greenampt.inp new file mode 100644 index 0000000..5364562 --- /dev/null +++ b/swmmio/tests/data/model_mod_greenampt.inp @@ -0,0 +1,306 @@ +[TITLE] +;;Project Title/Notes + +[OPTIONS] +;;Option Value +FLOW_UNITS CFS +INFILTRATION MODIFIED_GREEN_AMPT +FLOW_ROUTING DYNWAVE +LINK_OFFSETS DEPTH +MIN_SLOPE 0 +ALLOW_PONDING NO +SKIP_STEADY_STATE NO + +START_DATE 11/01/2015 +START_TIME 00:00:00 +REPORT_START_DATE 11/01/2015 +REPORT_START_TIME 00:00:00 +END_DATE 11/04/2015 +END_TIME 00:00:00 +SWEEP_START 01/01 +SWEEP_END 12/31 +DRY_DAYS 0 +REPORT_STEP 00:01:00 +WET_STEP 00:05:00 +DRY_STEP 00:05:00 +ROUTING_STEP 0:00:01 + +INERTIAL_DAMPING NONE +NORMAL_FLOW_LIMITED BOTH +FORCE_MAIN_EQUATION H-W +VARIABLE_STEP 0.75 +LENGTHENING_STEP 0 +MIN_SURFAREA 12.557 +MAX_TRIALS 8 +HEAD_TOLERANCE 0.005 +SYS_FLOW_TOL 5 +LAT_FLOW_TOL 5 +MINIMUM_STEP 0.5 +THREADS 1 + +[EVAPORATION] +;;Data Source Parameters +;;-------------- ---------------- +CONSTANT 0.0 +DRY_ONLY NO + +[RAINGAGES] +;;Name Format Interval SCF Source +;;-------------- --------- ------ ------ ---------- +SCS_24h_Type_I_1in INTENSITY 0:15 1.0 TIMESERIES SCS_24h_Type_I_1in + +[SUBCATCHMENTS] +;;Name Rain Gage Outlet Area %Imperv Width %Slope CurbLen SnowPack +;;-------------- ---------------- ---------------- -------- -------- -------- -------- -------- ---------------- +S1 SCS_24h_Type_I_1in J1 3 30 500 0.5 0 +S2 SCS_24h_Type_I_1in J2 2 100 500 0.5 0 +S3 SCS_24h_Type_I_1in j3 3 100 500 0.5 0 +S4 SCS_24h_Type_I_1in 1 20 25 500 0.5 0 + +[SUBAREAS] +;;Subcatchment N-Imperv N-Perv S-Imperv S-Perv PctZero RouteTo PctRouted +;;-------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- +S1 0.01 0.1 0.05 0.05 25 OUTLET +S2 0.01 0.1 0.05 0.05 25 OUTLET +S3 0.01 0.1 0.05 0.05 25 OUTLET +S4 0.01 0.1 0.05 0.05 25 OUTLET + +[INFILTRATION] +;;Subcatchment Suction Ksat IMD +;;-------------- ---------- ---------- ---------- +S1 3 0.5 4 +S2 3 0.5 4 +S3 3 0.5 4 +S4 3.0 0.5 4 + +[JUNCTIONS] +;;Name Elevation MaxDepth InitDepth SurDepth Aponded +;;-------------- ---------- ---------- ---------- ---------- ---------- +J3 6.547 15 0 0 0 +1 17 0 0 0 0 +2 17 0 0 0 0 +3 16.5 0 0 0 0 +4 16 0 0 0 0 +5 15 0 0 0 0 +J2 13.0 15 0 0 0 + +[OUTFALLS] +;;Name Elevation Type Stage Data Gated Route To +;;-------------- ---------- ---------- ---------------- -------- ---------------- +J4 0 FREE NO + +[STORAGE] +;;Name Elev. MaxDepth InitDepth Shape Curve Name/Params N/A Fevap Psi Ksat IMD +;;-------------- -------- ---------- ----------- ---------- ---------------------------- -------- -------- -------- -------- +J1 13.392 15 0 FUNCTIONAL 1000 0 0 0 0 + +[CONDUITS] +;;Name From Node To Node Length Roughness InOffset OutOffset InitFlow MaxFlow +;;-------------- ---------------- ---------------- ---------- ---------- ---------- ---------- ---------- ---------- +C1:C2 J1 J2 244.63 0.01 0 0 0 0 +C2.1 J2 J3 666 0.01 0 0 0 0 +1 1 4 400 0.01 0 0 0 0 +2 4 5 400 0.01 0 0 0 0 +3 5 J1 400 0.01 0 0 0 0 +4 3 4 400 0.01 0 0 0 0 +5 2 5 400 0.01 0 0 0 0 + +[PUMPS] +;;Name From Node To Node Pump Curve Status Sartup Shutoff +;;-------------- ---------------- ---------------- ---------------- ------ -------- -------- +C2 J2 J3 P1_Curve ON 0 0 + +[WEIRS] +;;Name From Node To Node Type CrestHt Qcoeff Gated EndCon EndCoeff Surcharge RoadWidth RoadSurf +;;-------------- ---------------- ---------------- ------------ ---------- ---------- -------- -------- ---------- ---------- ---------- ---------- +C3 J3 J4 TRANSVERSE 0 3.33 NO 0 0 NO + +[XSECTIONS] +;;Link Shape Geom1 Geom2 Geom3 Geom4 Barrels Culvert +;;-------------- ------------ ---------------- ---------- ---------- ---------- ---------- ---------- +C1:C2 CIRCULAR 1 0 0 0 1 +C2.1 CIRCULAR 1 0 0 0 1 +1 CIRCULAR 1 0 0 0 1 +2 CIRCULAR 1 0 0 0 1 +3 CIRCULAR 1 0 0 0 1 +4 CIRCULAR 1 0 0 0 1 +5 CIRCULAR 1 0 0 0 1 +C3 RECT_OPEN 5 1 0 0 + +[INFLOWS] +;;Node Constituent Time Series Type Mfactor Sfactor Baseline Pattern +;;-------------- ---------------- ---------------- -------- -------- -------- -------- -------- +J3 Flow "" FLOW 1.0 1 1 +J2 FLOW "" FLOW 1.0 1 1 +J1 FLOW "" FLOW 1.0 1 1 + +[CURVES] +;;Name Type X-Value Y-Value +;;-------------- ---------- ---------- ---------- +P1_Curve Pump4 0 10 +P1_Curve 5 20 + +[TIMESERIES] +;;Name Date Time Value +;;-------------- ---------- ---------- ---------- +;SCS_24h_Type_I_1in design storm, total rainfall = 1 in, rain units = in/hr. +SCS_24h_Type_I_1in 0:00 0.0175 +SCS_24h_Type_I_1in 0:15 0.0175 +SCS_24h_Type_I_1in 0:30 0.0175 +SCS_24h_Type_I_1in 0:45 0.0175 +SCS_24h_Type_I_1in 1:00 0.0175 +SCS_24h_Type_I_1in 1:15 0.0175 +SCS_24h_Type_I_1in 1:30 0.0175 +SCS_24h_Type_I_1in 1:45 0.0175 +SCS_24h_Type_I_1in 2:00 0.0205 +SCS_24h_Type_I_1in 2:15 0.0205 +SCS_24h_Type_I_1in 2:30 0.0205 +SCS_24h_Type_I_1in 2:45 0.0205 +SCS_24h_Type_I_1in 3:00 0.0205 +SCS_24h_Type_I_1in 3:15 0.0205 +SCS_24h_Type_I_1in 3:30 0.0205 +SCS_24h_Type_I_1in 3:45 0.0205 +SCS_24h_Type_I_1in 4:00 0.0245 +SCS_24h_Type_I_1in 4:15 0.0245 +SCS_24h_Type_I_1in 4:30 0.0245 +SCS_24h_Type_I_1in 4:45 0.0245 +SCS_24h_Type_I_1in 5:00 0.0245 +SCS_24h_Type_I_1in 5:15 0.0245 +SCS_24h_Type_I_1in 5:30 0.0245 +SCS_24h_Type_I_1in 5:45 0.0245 +SCS_24h_Type_I_1in 6:00 0.031 +SCS_24h_Type_I_1in 6:15 0.031 +SCS_24h_Type_I_1in 6:30 0.031 +SCS_24h_Type_I_1in 6:45 0.031 +SCS_24h_Type_I_1in 7:00 0.038 +SCS_24h_Type_I_1in 7:15 0.038 +SCS_24h_Type_I_1in 7:30 0.038 +SCS_24h_Type_I_1in 7:45 0.038 +SCS_24h_Type_I_1in 8:00 0.05 +SCS_24h_Type_I_1in 8:15 0.05 +SCS_24h_Type_I_1in 8:30 0.07 +SCS_24h_Type_I_1in 8:45 0.07 +SCS_24h_Type_I_1in 9:00 0.098 +SCS_24h_Type_I_1in 9:15 0.098 +SCS_24h_Type_I_1in 9:30 0.236 +SCS_24h_Type_I_1in 9:45 0.612 +SCS_24h_Type_I_1in 10:00 0.136 +SCS_24h_Type_I_1in 10:15 0.136 +SCS_24h_Type_I_1in 10:30 0.082 +SCS_24h_Type_I_1in 10:45 0.082 +SCS_24h_Type_I_1in 11:00 0.06 +SCS_24h_Type_I_1in 11:15 0.06 +SCS_24h_Type_I_1in 11:30 0.06 +SCS_24h_Type_I_1in 11:45 0.052 +SCS_24h_Type_I_1in 12:00 0.048 +SCS_24h_Type_I_1in 12:15 0.048 +SCS_24h_Type_I_1in 12:30 0.042 +SCS_24h_Type_I_1in 12:45 0.042 +SCS_24h_Type_I_1in 13:00 0.042 +SCS_24h_Type_I_1in 13:15 0.042 +SCS_24h_Type_I_1in 13:30 0.038 +SCS_24h_Type_I_1in 13:45 0.038 +SCS_24h_Type_I_1in 14:00 0.0315 +SCS_24h_Type_I_1in 14:15 0.0315 +SCS_24h_Type_I_1in 14:30 0.0315 +SCS_24h_Type_I_1in 14:45 0.0315 +SCS_24h_Type_I_1in 15:00 0.0315 +SCS_24h_Type_I_1in 15:15 0.0315 +SCS_24h_Type_I_1in 15:30 0.0315 +SCS_24h_Type_I_1in 15:45 0.0315 +SCS_24h_Type_I_1in 16:00 0.024 +SCS_24h_Type_I_1in 16:15 0.024 +SCS_24h_Type_I_1in 16:30 0.024 +SCS_24h_Type_I_1in 16:45 0.024 +SCS_24h_Type_I_1in 17:00 0.024 +SCS_24h_Type_I_1in 17:15 0.024 +SCS_24h_Type_I_1in 17:30 0.024 +SCS_24h_Type_I_1in 17:45 0.024 +SCS_24h_Type_I_1in 18:00 0.024 +SCS_24h_Type_I_1in 18:15 0.024 +SCS_24h_Type_I_1in 18:30 0.024 +SCS_24h_Type_I_1in 18:45 0.024 +SCS_24h_Type_I_1in 19:00 0.024 +SCS_24h_Type_I_1in 19:15 0.024 +SCS_24h_Type_I_1in 19:30 0.024 +SCS_24h_Type_I_1in 19:45 0.024 +SCS_24h_Type_I_1in 20:00 0.0185 +SCS_24h_Type_I_1in 20:15 0.0185 +SCS_24h_Type_I_1in 20:30 0.0185 +SCS_24h_Type_I_1in 20:45 0.0185 +SCS_24h_Type_I_1in 21:00 0.0185 +SCS_24h_Type_I_1in 21:15 0.0185 +SCS_24h_Type_I_1in 21:30 0.0185 +SCS_24h_Type_I_1in 21:45 0.0185 +SCS_24h_Type_I_1in 22:00 0.0185 +SCS_24h_Type_I_1in 22:15 0.0185 +SCS_24h_Type_I_1in 22:30 0.0185 +SCS_24h_Type_I_1in 22:45 0.0185 +SCS_24h_Type_I_1in 23:00 0.0185 +SCS_24h_Type_I_1in 23:15 0.0185 +SCS_24h_Type_I_1in 23:30 0.0185 +SCS_24h_Type_I_1in 23:45 0.0185 +SCS_24h_Type_I_1in 24:00 0 + +[REPORT] +;;Reporting Options +INPUT YES +CONTROLS YES +SUBCATCHMENTS NONE +NODES ALL +LINKS NONE + +[TAGS] + +[MAP] +DIMENSIONS -100.036 -181.979 708.126 213.879 +Units Feet + +[COORDINATES] +;;Node X-Coord Y-Coord +;;-------------- ------------------ ------------------ +J3 459.058 -113.145 +1 -77.021 -78.321 +2 -84.988 43.833 +3 -18.600 -71.239 +4 -67.284 -37.603 +5 -56.662 15.507 +J2 238.750 -53.332 +J4 671.391 -163.985 +J1 0.000 0.000 + +[VERTICES] +;;Link X-Coord Y-Coord +;;-------------- ------------------ ------------------ +C2.1 295.636 -159.756 +C2.1 360.253 -181.886 +4 -23.911 -51.766 +5 -85.873 19.933 + +[Polygons] +;;Subcatchment X-Coord Y-Coord +;;-------------- ------------------ ------------------ +S1 110.154 195.885 +S1 110.154 47.351 +S1 -56.323 42.367 +S1 -63.301 181.928 +S1 110.154 195.885 +S2 394.261 131.088 +S2 410.211 -20.436 +S2 245.728 -19.439 +S2 235.759 110.154 +S2 394.261 131.088 +S3 660.425 55.326 +S3 657.435 -104.173 +S3 519.867 -96.198 +S3 509.898 50.342 +S3 660.425 55.326 +S4 -63.523 -116.383 +S4 -82.996 -174.805 +S4 -154.695 -168.608 +S4 -148.499 -126.120 + +[SYMBOLS] +;;Gage X-Coord Y-Coord +;;-------------- ------------------ ------------------ + diff --git a/swmmio/tests/data/model_mod_horton.inp b/swmmio/tests/data/model_mod_horton.inp new file mode 100644 index 0000000..76921d2 --- /dev/null +++ b/swmmio/tests/data/model_mod_horton.inp @@ -0,0 +1,306 @@ +[TITLE] +;;Project Title/Notes + +[OPTIONS] +;;Option Value +FLOW_UNITS CFS +INFILTRATION MODIFIED_HORTON +FLOW_ROUTING DYNWAVE +LINK_OFFSETS DEPTH +MIN_SLOPE 0 +ALLOW_PONDING NO +SKIP_STEADY_STATE NO + +START_DATE 11/01/2015 +START_TIME 00:00:00 +REPORT_START_DATE 11/01/2015 +REPORT_START_TIME 00:00:00 +END_DATE 11/04/2015 +END_TIME 00:00:00 +SWEEP_START 01/01 +SWEEP_END 12/31 +DRY_DAYS 0 +REPORT_STEP 00:01:00 +WET_STEP 00:05:00 +DRY_STEP 00:05:00 +ROUTING_STEP 0:00:01 + +INERTIAL_DAMPING NONE +NORMAL_FLOW_LIMITED BOTH +FORCE_MAIN_EQUATION H-W +VARIABLE_STEP 0.75 +LENGTHENING_STEP 0 +MIN_SURFAREA 12.557 +MAX_TRIALS 8 +HEAD_TOLERANCE 0.005 +SYS_FLOW_TOL 5 +LAT_FLOW_TOL 5 +MINIMUM_STEP 0.5 +THREADS 1 + +[EVAPORATION] +;;Data Source Parameters +;;-------------- ---------------- +CONSTANT 0.0 +DRY_ONLY NO + +[RAINGAGES] +;;Name Format Interval SCF Source +;;-------------- --------- ------ ------ ---------- +SCS_24h_Type_I_1in INTENSITY 0:15 1.0 TIMESERIES SCS_24h_Type_I_1in + +[SUBCATCHMENTS] +;;Name Rain Gage Outlet Area %Imperv Width %Slope CurbLen SnowPack +;;-------------- ---------------- ---------------- -------- -------- -------- -------- -------- ---------------- +S1 SCS_24h_Type_I_1in J1 3 30 500 0.5 0 +S2 SCS_24h_Type_I_1in J2 2 100 500 0.5 0 +S3 SCS_24h_Type_I_1in j3 3 100 500 0.5 0 +S4 SCS_24h_Type_I_1in 1 20 25 500 0.5 0 + +[SUBAREAS] +;;Subcatchment N-Imperv N-Perv S-Imperv S-Perv PctZero RouteTo PctRouted +;;-------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- +S1 0.01 0.1 0.05 0.05 25 OUTLET +S2 0.01 0.1 0.05 0.05 25 OUTLET +S3 0.01 0.1 0.05 0.05 25 OUTLET +S4 0.01 0.1 0.05 0.05 25 OUTLET + +[INFILTRATION] +;;Subcatchment MaxRate MinRate Decay DryTime MaxInfil +;;-------------- ---------- ---------- ---------- ---------- ---------- +S1 3 0.5 4 7 0 +S2 3 0.5 4 7 0 +S3 3 0.5 4 7 0 +S4 3.0 0.5 4 7 0 + +[JUNCTIONS] +;;Name Elevation MaxDepth InitDepth SurDepth Aponded +;;-------------- ---------- ---------- ---------- ---------- ---------- +J3 6.547 15 0 0 0 +1 17 0 0 0 0 +2 17 0 0 0 0 +3 16.5 0 0 0 0 +4 16 0 0 0 0 +5 15 0 0 0 0 +J2 13.0 15 0 0 0 + +[OUTFALLS] +;;Name Elevation Type Stage Data Gated Route To +;;-------------- ---------- ---------- ---------------- -------- ---------------- +J4 0 FREE NO + +[STORAGE] +;;Name Elev. MaxDepth InitDepth Shape Curve Name/Params N/A Fevap Psi Ksat IMD +;;-------------- -------- ---------- ----------- ---------- ---------------------------- -------- -------- -------- -------- +J1 13.392 15 0 FUNCTIONAL 1000 0 0 0 0 + +[CONDUITS] +;;Name From Node To Node Length Roughness InOffset OutOffset InitFlow MaxFlow +;;-------------- ---------------- ---------------- ---------- ---------- ---------- ---------- ---------- ---------- +C1:C2 J1 J2 244.63 0.01 0 0 0 0 +C2.1 J2 J3 666 0.01 0 0 0 0 +1 1 4 400 0.01 0 0 0 0 +2 4 5 400 0.01 0 0 0 0 +3 5 J1 400 0.01 0 0 0 0 +4 3 4 400 0.01 0 0 0 0 +5 2 5 400 0.01 0 0 0 0 + +[PUMPS] +;;Name From Node To Node Pump Curve Status Sartup Shutoff +;;-------------- ---------------- ---------------- ---------------- ------ -------- -------- +C2 J2 J3 P1_Curve ON 0 0 + +[WEIRS] +;;Name From Node To Node Type CrestHt Qcoeff Gated EndCon EndCoeff Surcharge RoadWidth RoadSurf +;;-------------- ---------------- ---------------- ------------ ---------- ---------- -------- -------- ---------- ---------- ---------- ---------- +C3 J3 J4 TRANSVERSE 0 3.33 NO 0 0 NO + +[XSECTIONS] +;;Link Shape Geom1 Geom2 Geom3 Geom4 Barrels Culvert +;;-------------- ------------ ---------------- ---------- ---------- ---------- ---------- ---------- +C1:C2 CIRCULAR 1 0 0 0 1 +C2.1 CIRCULAR 1 0 0 0 1 +1 CIRCULAR 1 0 0 0 1 +2 CIRCULAR 1 0 0 0 1 +3 CIRCULAR 1 0 0 0 1 +4 CIRCULAR 1 0 0 0 1 +5 CIRCULAR 1 0 0 0 1 +C3 RECT_OPEN 5 1 0 0 + +[INFLOWS] +;;Node Constituent Time Series Type Mfactor Sfactor Baseline Pattern +;;-------------- ---------------- ---------------- -------- -------- -------- -------- -------- +J3 Flow "" FLOW 1.0 1 1 +J2 FLOW "" FLOW 1.0 1 1 +J1 FLOW "" FLOW 1.0 1 1 + +[CURVES] +;;Name Type X-Value Y-Value +;;-------------- ---------- ---------- ---------- +P1_Curve Pump4 0 10 +P1_Curve 5 20 + +[TIMESERIES] +;;Name Date Time Value +;;-------------- ---------- ---------- ---------- +;SCS_24h_Type_I_1in design storm, total rainfall = 1 in, rain units = in/hr. +SCS_24h_Type_I_1in 0:00 0.0175 +SCS_24h_Type_I_1in 0:15 0.0175 +SCS_24h_Type_I_1in 0:30 0.0175 +SCS_24h_Type_I_1in 0:45 0.0175 +SCS_24h_Type_I_1in 1:00 0.0175 +SCS_24h_Type_I_1in 1:15 0.0175 +SCS_24h_Type_I_1in 1:30 0.0175 +SCS_24h_Type_I_1in 1:45 0.0175 +SCS_24h_Type_I_1in 2:00 0.0205 +SCS_24h_Type_I_1in 2:15 0.0205 +SCS_24h_Type_I_1in 2:30 0.0205 +SCS_24h_Type_I_1in 2:45 0.0205 +SCS_24h_Type_I_1in 3:00 0.0205 +SCS_24h_Type_I_1in 3:15 0.0205 +SCS_24h_Type_I_1in 3:30 0.0205 +SCS_24h_Type_I_1in 3:45 0.0205 +SCS_24h_Type_I_1in 4:00 0.0245 +SCS_24h_Type_I_1in 4:15 0.0245 +SCS_24h_Type_I_1in 4:30 0.0245 +SCS_24h_Type_I_1in 4:45 0.0245 +SCS_24h_Type_I_1in 5:00 0.0245 +SCS_24h_Type_I_1in 5:15 0.0245 +SCS_24h_Type_I_1in 5:30 0.0245 +SCS_24h_Type_I_1in 5:45 0.0245 +SCS_24h_Type_I_1in 6:00 0.031 +SCS_24h_Type_I_1in 6:15 0.031 +SCS_24h_Type_I_1in 6:30 0.031 +SCS_24h_Type_I_1in 6:45 0.031 +SCS_24h_Type_I_1in 7:00 0.038 +SCS_24h_Type_I_1in 7:15 0.038 +SCS_24h_Type_I_1in 7:30 0.038 +SCS_24h_Type_I_1in 7:45 0.038 +SCS_24h_Type_I_1in 8:00 0.05 +SCS_24h_Type_I_1in 8:15 0.05 +SCS_24h_Type_I_1in 8:30 0.07 +SCS_24h_Type_I_1in 8:45 0.07 +SCS_24h_Type_I_1in 9:00 0.098 +SCS_24h_Type_I_1in 9:15 0.098 +SCS_24h_Type_I_1in 9:30 0.236 +SCS_24h_Type_I_1in 9:45 0.612 +SCS_24h_Type_I_1in 10:00 0.136 +SCS_24h_Type_I_1in 10:15 0.136 +SCS_24h_Type_I_1in 10:30 0.082 +SCS_24h_Type_I_1in 10:45 0.082 +SCS_24h_Type_I_1in 11:00 0.06 +SCS_24h_Type_I_1in 11:15 0.06 +SCS_24h_Type_I_1in 11:30 0.06 +SCS_24h_Type_I_1in 11:45 0.052 +SCS_24h_Type_I_1in 12:00 0.048 +SCS_24h_Type_I_1in 12:15 0.048 +SCS_24h_Type_I_1in 12:30 0.042 +SCS_24h_Type_I_1in 12:45 0.042 +SCS_24h_Type_I_1in 13:00 0.042 +SCS_24h_Type_I_1in 13:15 0.042 +SCS_24h_Type_I_1in 13:30 0.038 +SCS_24h_Type_I_1in 13:45 0.038 +SCS_24h_Type_I_1in 14:00 0.0315 +SCS_24h_Type_I_1in 14:15 0.0315 +SCS_24h_Type_I_1in 14:30 0.0315 +SCS_24h_Type_I_1in 14:45 0.0315 +SCS_24h_Type_I_1in 15:00 0.0315 +SCS_24h_Type_I_1in 15:15 0.0315 +SCS_24h_Type_I_1in 15:30 0.0315 +SCS_24h_Type_I_1in 15:45 0.0315 +SCS_24h_Type_I_1in 16:00 0.024 +SCS_24h_Type_I_1in 16:15 0.024 +SCS_24h_Type_I_1in 16:30 0.024 +SCS_24h_Type_I_1in 16:45 0.024 +SCS_24h_Type_I_1in 17:00 0.024 +SCS_24h_Type_I_1in 17:15 0.024 +SCS_24h_Type_I_1in 17:30 0.024 +SCS_24h_Type_I_1in 17:45 0.024 +SCS_24h_Type_I_1in 18:00 0.024 +SCS_24h_Type_I_1in 18:15 0.024 +SCS_24h_Type_I_1in 18:30 0.024 +SCS_24h_Type_I_1in 18:45 0.024 +SCS_24h_Type_I_1in 19:00 0.024 +SCS_24h_Type_I_1in 19:15 0.024 +SCS_24h_Type_I_1in 19:30 0.024 +SCS_24h_Type_I_1in 19:45 0.024 +SCS_24h_Type_I_1in 20:00 0.0185 +SCS_24h_Type_I_1in 20:15 0.0185 +SCS_24h_Type_I_1in 20:30 0.0185 +SCS_24h_Type_I_1in 20:45 0.0185 +SCS_24h_Type_I_1in 21:00 0.0185 +SCS_24h_Type_I_1in 21:15 0.0185 +SCS_24h_Type_I_1in 21:30 0.0185 +SCS_24h_Type_I_1in 21:45 0.0185 +SCS_24h_Type_I_1in 22:00 0.0185 +SCS_24h_Type_I_1in 22:15 0.0185 +SCS_24h_Type_I_1in 22:30 0.0185 +SCS_24h_Type_I_1in 22:45 0.0185 +SCS_24h_Type_I_1in 23:00 0.0185 +SCS_24h_Type_I_1in 23:15 0.0185 +SCS_24h_Type_I_1in 23:30 0.0185 +SCS_24h_Type_I_1in 23:45 0.0185 +SCS_24h_Type_I_1in 24:00 0 + +[REPORT] +;;Reporting Options +INPUT YES +CONTROLS YES +SUBCATCHMENTS NONE +NODES ALL +LINKS NONE + +[TAGS] + +[MAP] +DIMENSIONS -100.036 -181.979 708.126 213.879 +Units Feet + +[COORDINATES] +;;Node X-Coord Y-Coord +;;-------------- ------------------ ------------------ +J3 459.058 -113.145 +1 -77.021 -78.321 +2 -84.988 43.833 +3 -18.600 -71.239 +4 -67.284 -37.603 +5 -56.662 15.507 +J2 238.750 -53.332 +J4 671.391 -163.985 +J1 0.000 0.000 + +[VERTICES] +;;Link X-Coord Y-Coord +;;-------------- ------------------ ------------------ +C2.1 295.636 -159.756 +C2.1 360.253 -181.886 +4 -23.911 -51.766 +5 -85.873 19.933 + +[Polygons] +;;Subcatchment X-Coord Y-Coord +;;-------------- ------------------ ------------------ +S1 110.154 195.885 +S1 110.154 47.351 +S1 -56.323 42.367 +S1 -63.301 181.928 +S1 110.154 195.885 +S2 394.261 131.088 +S2 410.211 -20.436 +S2 245.728 -19.439 +S2 235.759 110.154 +S2 394.261 131.088 +S3 660.425 55.326 +S3 657.435 -104.173 +S3 519.867 -96.198 +S3 509.898 50.342 +S3 660.425 55.326 +S4 -63.523 -116.383 +S4 -82.996 -174.805 +S4 -154.695 -168.608 +S4 -148.499 -126.120 + +[SYMBOLS] +;;Gage X-Coord Y-Coord +;;-------------- ------------------ ------------------ + diff --git a/swmmio/utils/dataframes.py b/swmmio/utils/dataframes.py index d07f05f..d7ba3a7 100644 --- a/swmmio/utils/dataframes.py +++ b/swmmio/utils/dataframes.py @@ -1,7 +1,8 @@ +import os +import pandas as pd from swmmio.utils import text as txt from swmmio.utils import functions as funcs -import pandas as pd -import os +from swmmio.defs import HEADERS def create_dataframeBI(bi_path, section='[CONDUITS]'): @@ -23,7 +24,7 @@ def create_dataframeBI(bi_path, section='[CONDUITS]'): def create_dataframeINP(inp_path, section='[CONDUITS]', ignore_comments=True, - comment_str=';', comment_cols=True): + comment_str=';', comment_cols=True, headers=None): """ given a path to an INP file, create a dataframe of data in the given section. @@ -35,14 +36,22 @@ def create_dataframeINP(inp_path, section='[CONDUITS]', ignore_comments=True, tempfilepath = txt.extract_section_from_inp(inp_path, section, headerdefs=headerdefs, ignore_comments=ignore_comments) + if ignore_comments: comment_str = None if not tempfilepath: # if this head (section) was not found in the textfile, return a # blank dataframe with the appropriate schema - print('header "{}" not found in "{}"'.format(section, inp_path)) - print('returning empty dataframe') - headerlist = headerdefs['headers'].get(section, 'blob').split() + [';', 'Comment', 'Origin'] + print('header {} not found in {}\nReturning empty DataFrame.'.format(section, inp_path)) + # headerlist = headerdefs['headers'].get(section, 'blob').split() + [';', 'Comment', 'Origin'] + + headerlist = HEADERS['inp_sections'][section] + if headers is not None: + headerlist = headers[section] + + if comment_cols: + headerlist = headerlist + [';', 'Comment', 'Origin'] + blank_df = pd.DataFrame(data=None, columns=headerlist).set_index(headerlist[0]) return blank_df @@ -54,7 +63,9 @@ def create_dataframeINP(inp_path, section='[CONDUITS]', ignore_comments=True, df = pd.read_csv(tempfilepath, delim_whitespace=False) # , index_col=0)#, skiprows=[0]) else: # this section header is recognized and will be organized into known columns - headerlist = headerdefs['headers'][section].split() + headerlist = HEADERS['inp_sections'][section] + if headers is not None: + headerlist = headers[section] if comment_cols: headerlist = headerlist + [';', 'Comment', 'Origin'] dtypes = {'InletNode': str, 'OutletNode': str} diff --git a/swmmio/utils/functions.py b/swmmio/utils/functions.py index 86dd220..51ab6a2 100644 --- a/swmmio/utils/functions.py +++ b/swmmio/utils/functions.py @@ -79,46 +79,59 @@ def multidigraph_from_edges(edges, source, target): return G -def find_invalid_links(model, link_type, drop=False): - nids = model.nodes().index - elems = getattr(model.inp, link_type) - invalids = elems.index[~(elems.InletNode.isin(nids) & elems.OutletNode.isin(nids))] +def find_invalid_links(inp, node_ids=None, link_type='conduits', drop=False): + # from swmmio.utils.dataframes import create_dataframeINP + # if node_ids is None: + # juncs = create_dataframeINP(inp.path, "[JUNCTIONS]").index.tolist() + # outfs = create_dataframeINP(inp.path, "[OUTFALLS]").index.tolist() + # stors = create_dataframeINP(inp.path, "[STORAGE]").index.tolist() + # node_ids = juncs + outfs + stors + + elems = getattr(inp, link_type) + invalids = elems.index[~(elems.InletNode.isin(node_ids) & elems.OutletNode.isin(node_ids))] if drop: - df = elems.loc[elems.InletNode.isin(nids) & elems.OutletNode.isin(nids)] - setattr(model.inp, link_type, df) + df = elems.loc[elems.InletNode.isin(node_ids) & elems.OutletNode.isin(node_ids)] + setattr(inp, link_type, df) return invalids.tolist() -def drop_invalid_model_elements(model): - """ - Identify references to elements in the model that are undefined and remove them from the - model. These should coincide with warnings/errors produced by SWMM5 when undefined elements - are referenced in links, subcatchments, and controls. - :param model: swmmio.Model - :return: - >>> import swmmio - >>> m = swmmio.Model(MODEL_FULL_FEATURES_INVALID) - >>> drop_invalid_model_elements(m) - ['InvalidLink2', 'InvalidLink1'] - >>> m.inp.conduits.index - Index(['C1:C2', 'C2.1', '1', '2', '4', '5'], dtype='object', name='Name') - """ - - nids = model.nodes().index - - # drop links with bad refs to inlet/outlet nodes - inv_conds = find_invalid_links(model, 'conduits', drop=True) - inv_pumps = find_invalid_links(model, 'pumps', drop=True) - inv_orifs = find_invalid_links(model, 'orifices', drop=True) - inv_weirs = find_invalid_links(model, 'weirs', drop=True) - - invalids = inv_conds + inv_pumps + inv_orifs + inv_weirs - - # drop other parts of bad links, subcatchments - model.inp.xsections = model.inp.xsections.loc[~model.inp.xsections.index.isin(invalids)] - model.inp.subcatchments = model.inp.subcatchments.loc[model.inp.subcatchments['Outlet'].isin(nids)] - - return invalids +# def drop_invalid_model_elements(inp): +# """ +# Identify references to elements in the model that are undefined and remove them from the +# model. These should coincide with warnings/errors produced by SWMM5 when undefined elements +# are referenced in links, subcatchments, and controls. +# :param model: swmmio.Model +# :return: +# >>> import swmmio +# >>> m = swmmio.Model(MODEL_FULL_FEATURES_INVALID) +# >>> drop_invalid_model_elements(m.inp) +# ['InvalidLink2', 'InvalidLink1'] +# >>> m.inp.conduits.index +# Index(['C1:C2', 'C2.1', '1', '2', '4', '5'], dtype='object', name='Name') +# """ +# from swmmio.utils.dataframes import create_dataframeINP +# juncs = create_dataframeINP(inp.path, "[JUNCTIONS]").index.tolist() +# outfs = create_dataframeINP(inp.path, "[OUTFALLS]").index.tolist() +# stors = create_dataframeINP(inp.path, "[STORAGE]").index.tolist() +# nids = juncs + outfs + stors +# +# # drop links with bad refs to inlet/outlet nodes +# inv_conds = find_invalid_links(inp, nids, 'conduits', drop=True) +# inv_pumps = find_invalid_links(inp, nids, 'pumps', drop=True) +# inv_orifs = find_invalid_links(inp, nids, 'orifices', drop=True) +# inv_weirs = find_invalid_links(inp, nids, 'weirs', drop=True) +# +# # drop other parts of bad links +# invalid_links = inv_conds + inv_pumps + inv_orifs + inv_weirs +# inp.xsections = inp.xsections.loc[~inp.xsections.index.isin(invalid_links)] +# +# # drop invalid subcats and their related components +# invalid_subcats = inp.subcatchments.index[~inp.subcatchments['Outlet'].isin(nids)] +# inp.subcatchments = inp.subcatchments.loc[~inp.subcatchments.index.isin(invalid_subcats)] +# inp.subareas = inp.subareas.loc[~inp.subareas.index.isin(invalid_subcats)] +# inp.infiltration= inp.infiltration.loc[~inp.infiltration.index.isin(invalid_subcats)] +# +# return invalid_links + invalid_subcats # Todo: use an OrderedDict instead of a dict and a "order" list From 96162a86654bba15f3c12fd30f73beeb00281e9f Mon Sep 17 00:00:00 2001 From: Adam Erispaha Date: Mon, 17 Jun 2019 16:58:53 -0400 Subject: [PATCH 6/9] add more sections to Model.inp, code style, modify_model bugfix --- swmmio/core.py | 9 +++- swmmio/reporting/batch.py | 2 +- swmmio/run_models/run.py | 7 +-- swmmio/run_models/start_pool.py | 22 +++------- swmmio/tests/data/__init__.py | 1 + swmmio/tests/data/outfalls_modified_10.csv | 2 + swmmio/tests/test_version_control.py | 51 ++++++++++++++++++---- swmmio/utils/functions.py | 15 ++++--- swmmio/utils/modify_model.py | 3 +- swmmio/version_control/inp.py | 2 + swmmio/version_control/version_control.py | 7 --- 11 files changed, 75 insertions(+), 46 deletions(-) create mode 100644 swmmio/tests/data/outfalls_modified_10.csv diff --git a/swmmio/core.py b/swmmio/core.py index 552d28d..8e1c399 100644 --- a/swmmio/core.py +++ b/swmmio/core.py @@ -15,6 +15,7 @@ from swmmio.elements import ModelSection from swmmio.defs import HEADERS # from swmmio.utils.functions import find_invalid_links +from swmmio.utils.functions import trim_section_to_nodes class Model(object): @@ -556,7 +557,8 @@ def __init__(self, file_path): '[VERTICES]', '[SUBCATCHMENTS]', '[SUBAREAS]', - '[INFILTRATION]' + '[INFILTRATION]', + '[COORDINATES]' ] def save(self, target_path=None): @@ -584,6 +586,11 @@ def validate(self): """ drop_invalid_model_elements(self) + def trim_to_nodes(self, node_ids): + + for section in ['junctions', 'storage', 'outfalls', 'coordinates']: + trim_section_to_nodes(self, node_ids, node_type=section) + @property def headers(self): """ diff --git a/swmmio/reporting/batch.py b/swmmio/reporting/batch.py index aea313f..bdf9957 100644 --- a/swmmio/reporting/batch.py +++ b/swmmio/reporting/batch.py @@ -1,4 +1,4 @@ -from swmmio.swmmio import Model +from swmmio import Model from swmmio.reporting import reporting, serialize from swmmio.reporting import functions from swmmio.utils import spatial diff --git a/swmmio/run_models/run.py b/swmmio/run_models/run.py index 356c77d..b634f03 100644 --- a/swmmio/run_models/run.py +++ b/swmmio/run_models/run.py @@ -1,17 +1,12 @@ import subprocess import os import pandas as pd -from swmmio.version_control import version_control as vc from swmmio.utils.modify_model import replace_inp_section -from swmmio.utils import dataframes from swmmio.run_models import defs -from swmmio.swmmio import Model +from swmmio import Model from swmmio.defs.config import SWMM_ENGINE_PATH -#path to the SWMM5 Engine -# SWMM_ENGINE_PATH = r'\\PWDHQR\Data\Planning & Research\Flood Risk Management\07_Software\swmm5_22.exe' - def run_simple(inp_path, swmm_eng=SWMM_ENGINE_PATH): """ run a model once as is. diff --git a/swmmio/run_models/start_pool.py b/swmmio/run_models/start_pool.py index 9f280d0..8a1b32c 100644 --- a/swmmio/run_models/start_pool.py +++ b/swmmio/run_models/start_pool.py @@ -1,15 +1,13 @@ from swmmio.run_models import run -from swmmio.swmmio import Model -# from swmmio.reporting import reporting -# from swmmio.utils import swmm_utils as su -from swmmio.reporting import batch +from swmmio import Model from multiprocessing import Pool, cpu_count from datetime import datetime import os -import sys -wd = os.getcwd() +wd = os.getcwd() log_start_time = datetime.now().strftime("%y%m%d_%H%M") + + def run_swmm_engine(inp_folder): logfile = os.path.join(wd, 'log_'+log_start_time+'.txt') @@ -28,6 +26,7 @@ def run_swmm_engine(inp_folder): with open (logfile, 'a') as f: f.write('{}: skipped (up-to-date)\n'.format(m.inp.name)) + def main(inp_paths, cores_left): """ @@ -36,16 +35,9 @@ def main(inp_paths, cores_left): """ # create multiprocessing Pool object using all cores less the -cores_left - #beware of setting -cores_left=0, CPU usage will max the F out + # beware of setting -cores_left=0, CPU usage will max the F out pool = Pool(cpu_count() - cores_left) - #create a process pool with the run_swmm_engine() func on each directory + # create a process pool with the run_swmm_engine() func on each directory res = pool.map(run_swmm_engine, inp_paths) - # #post process - # ADMIN_DIR = os.path.join(wd, 'ProjectAdmin') - # results_file = os.path(ADMIN_DIR, '170210_results.csv') - # add_data = os.path(ADMIN_DIR, 'additional_costs.csv') - # join_data = os.path(ADMIN_DIR, 'equivalentgeoms.csv') - # batch.batch_reports(wd, results_file=results_file, - # additional_costs=add_data, join_data=join_data) diff --git a/swmmio/tests/data/__init__.py b/swmmio/tests/data/__init__.py index 3e83c8e..771fa37 100644 --- a/swmmio/tests/data/__init__.py +++ b/swmmio/tests/data/__init__.py @@ -29,3 +29,4 @@ MODEL_BLANK = os.path.join(DATA_PATH, 'blank_model.inp') df_test_coordinates_csv = os.path.join(DATA_PATH, 'df_test_coordinates.csv') +OUTFALLS_MODIFIED = os.path.join(DATA_PATH, 'outfalls_modified_10.csv') \ No newline at end of file diff --git a/swmmio/tests/data/outfalls_modified_10.csv b/swmmio/tests/data/outfalls_modified_10.csv new file mode 100644 index 0000000..5b5f7d3 --- /dev/null +++ b/swmmio/tests/data/outfalls_modified_10.csv @@ -0,0 +1,2 @@ +Name,InvertElev,OutfallType,StageOrTimeseries,TideGate +J4,10,FIXED,NO, diff --git a/swmmio/tests/test_version_control.py b/swmmio/tests/test_version_control.py index cdd96e1..f79ab22 100644 --- a/swmmio/tests/test_version_control.py +++ b/swmmio/tests/test_version_control.py @@ -1,5 +1,6 @@ -from swmmio.tests.data import (MODEL_XSECTION_BASELINE, MODEL_FULL_FEATURES_PATH, - MODEL_XSECTION_ALT_02, MODEL_XSECTION_ALT_03, MODEL_BLANK) +from swmmio.tests.data import (MODEL_XSECTION_BASELINE, MODEL_FULL_FEATURES_XY, + MODEL_XSECTION_ALT_02, MODEL_XSECTION_ALT_03, MODEL_BLANK, + OUTFALLS_MODIFIED) from swmmio.version_control import utils as vc_utils from swmmio.version_control import inp @@ -7,7 +8,6 @@ def test_complete_inp_headers(): - headers = [ '[TITLE]', '[OPTIONS]', '[EVAPORATION]', '[JUNCTIONS]', '[OUTFALLS]', '[CONDUITS]', '[XSECTIONS]', '[DWF]', '[REPORT]', '[TAGS]', '[MAP]', @@ -16,12 +16,11 @@ def test_complete_inp_headers(): h1 = funcs.complete_inp_headers(MODEL_XSECTION_BASELINE) - assert(all(h in h1['headers'] for h in headers)) - assert(h1['order'] == headers) + assert (all(h in h1['headers'] for h in headers)) + assert (h1['order'] == headers) def test_create_inp_build_instructions(): - inp.create_inp_build_instructions(MODEL_XSECTION_BASELINE, MODEL_XSECTION_ALT_03, 'vc_dir', @@ -31,8 +30,8 @@ def test_create_inp_build_instructions(): bi = inp.BuildInstructions(latest_bi) juncs = bi.instructions['[JUNCTIONS]'] - assert(all(j in juncs.altered.index for j in [ - 'dummy_node1', 'dummy_node5'])) + assert (all(j in juncs.altered.index for j in [ + 'dummy_node1', 'dummy_node5'])) # def test_add_models(): @@ -53,3 +52,39 @@ def test_create_inp_build_instructions(): # bi3 = bi1 + bi2 # bi3.build(MODEL_BLANK, 'added_model.inp') + +def test_modify_model(): + from swmmio.utils.modify_model import replace_inp_section + from swmmio import Model, create_dataframeINP + import pandas as pd + import os + import shutil + + # initialize a baseline model object + baseline = Model(MODEL_FULL_FEATURES_XY) + of_test = pd.read_csv(OUTFALLS_MODIFIED, index_col=0) + rise = 10.0 # set the starting sea level rise condition + + # create a dataframe of the model's outfalls + outfalls = create_dataframeINP(baseline.inp.path, '[OUTFALLS]') + + # add the current rise to the outfalls' stage elevation + outfalls['OutfallType'] = 'FIXED' + outfalls.loc[:, 'InvertElev'] = pd.to_numeric(outfalls.loc[:, 'InvertElev']) + rise + of_test.loc[:, 'InvertElev'] = pd.to_numeric(of_test.loc[:, 'InvertElev']) + + + # copy the base model into a new directory + newdir = os.path.join(baseline.inp.dir, str(rise)) + os.mkdir(newdir) + newfilepath = os.path.join(newdir, baseline.inp.name + "_" + str(rise) + '_SLR.inp') + shutil.copyfile(baseline.inp.path, newfilepath) + + # Overwrite the OUTFALLS section of the new model with the adjusted data + replace_inp_section(newfilepath, '[OUTFALLS]', outfalls) + + m2 = Model(newfilepath) + of2 = m2.inp.outfalls + shutil.rmtree(newdir) + # of2.to_csv(os.path.join(newdir, baseline.inp.name + "_new_outfalls.csv")) + assert(of2.loc['J4', 'InvertElev'].round(1) == of_test.loc['J4', 'InvertElev'].round(1)) diff --git a/swmmio/utils/functions.py b/swmmio/utils/functions.py index 51ab6a2..c8042ad 100644 --- a/swmmio/utils/functions.py +++ b/swmmio/utils/functions.py @@ -80,12 +80,6 @@ def multidigraph_from_edges(edges, source, target): def find_invalid_links(inp, node_ids=None, link_type='conduits', drop=False): - # from swmmio.utils.dataframes import create_dataframeINP - # if node_ids is None: - # juncs = create_dataframeINP(inp.path, "[JUNCTIONS]").index.tolist() - # outfs = create_dataframeINP(inp.path, "[OUTFALLS]").index.tolist() - # stors = create_dataframeINP(inp.path, "[STORAGE]").index.tolist() - # node_ids = juncs + outfs + stors elems = getattr(inp, link_type) invalids = elems.index[~(elems.InletNode.isin(node_ids) & elems.OutletNode.isin(node_ids))] @@ -95,6 +89,15 @@ def find_invalid_links(inp, node_ids=None, link_type='conduits', drop=False): return invalids.tolist() +def trim_section_to_nodes(inp, node_ids=None, node_type='junctions', drop=True): + + elems = getattr(inp, node_type) + invalids = elems.index[~(elems.index.isin(node_ids))] + if drop: + df = elems.loc[elems.index.isin(node_ids)] + setattr(inp, node_type, df) + return invalids.tolist() + # def drop_invalid_model_elements(inp): # """ # Identify references to elements in the model that are undefined and remove them from the diff --git a/swmmio/utils/modify_model.py b/swmmio/utils/modify_model.py index 802ce82..741e302 100644 --- a/swmmio/utils/modify_model.py +++ b/swmmio/utils/modify_model.py @@ -1,10 +1,9 @@ -from swmmio.version_control import version_control as vc from swmmio.version_control import utils as vc_utils from swmmio.utils.functions import complete_inp_headers from swmmio import swmmio -import pandas as pd import os + def replace_inp_section(inp_path, modified_section_header, new_data, overwrite=True): """ diff --git a/swmmio/version_control/inp.py b/swmmio/version_control/inp.py index bf363c9..57a441c 100644 --- a/swmmio/version_control/inp.py +++ b/swmmio/version_control/inp.py @@ -150,6 +150,8 @@ def __init__(self, model1=None, model2=None, section='[JUNCTIONS]', build_instr_ common_ids = df1.index.difference(removed_ids) #original - removed = in common #both dfs concatenated, with matched indices for each element full_set = pd.concat([df1.loc[common_ids], df2.loc[common_ids]]) + # remove whitespace + full_set = full_set.apply(lambda x: x.str.strip() if x.dtype == "object" else x) #drop dupes on the set, all things that did not changed should have 1 row changes_with_dupes = full_set.drop_duplicates() #duplicate indicies are rows that have changes, isolate these diff --git a/swmmio/version_control/version_control.py b/swmmio/version_control/version_control.py index 5544e28..1eba452 100644 --- a/swmmio/version_control/version_control.py +++ b/swmmio/version_control/version_control.py @@ -1,17 +1,11 @@ import pandas as pd -import shutil import os -import fileinput import itertools from datetime import datetime from swmmio import Model -from swmmio.utils import functions as funcs -from swmmio.utils.dataframes import create_dataframeINP -# from swmmio.utils import swmm_utils as su from swmmio.version_control import utils as vc_utils from swmmio.version_control import inp -#from .utils.text import * #functions for processing inp/rpt/txt files pd.options.display.max_colwidth = 200 @@ -19,7 +13,6 @@ def propagate_changes_from_baseline(baseline_dir, alternatives_dir, combi_dir, version_id='', comments=''): - #stuff """ if the baseline model has changes that need to be propogated to all models, iterate through each model and rebuild the INPs with the new baseline and From 4258e565f09c46f4b227c8669158c7a2d6b465a7 Mon Sep 17 00:00:00 2001 From: Adam Erispaha Date: Mon, 17 Jun 2019 17:05:11 -0400 Subject: [PATCH 7/9] set release version --- README.md | 3 ++- swmmio/__init__.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 67e4ae9..60492ed 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# SWMMIO +# SWMMIO +v0.3.5 [![Build status](https://ci.appveyor.com/api/projects/status/qywujm5w2wm0y2tv/branch/master?svg=true)](https://ci.appveyor.com/project/aerispaha/swmmio/branch/master) [![Build Status](https://travis-ci.com/aerispaha/swmmio.svg?branch=master)](https://travis-ci.com/aerispaha/swmmio) diff --git a/swmmio/__init__.py b/swmmio/__init__.py index eea4b0e..16eca70 100644 --- a/swmmio/__init__.py +++ b/swmmio/__init__.py @@ -5,7 +5,7 @@ '''Python SWMM Input/Output Tools''' -VERSION_INFO = (0, 3, 5, 'dev') +VERSION_INFO = (0, 3, 5) __version__ = '.'.join(map(str, VERSION_INFO)) __author__ = 'Adam Erispaha' __copyright__ = 'Copyright (c) 2016' From 4971076932bfd385919a3b737a773fc4431fdc09 Mon Sep 17 00:00:00 2001 From: Adam Erispaha Date: Mon, 17 Jun 2019 17:12:21 -0400 Subject: [PATCH 8/9] dont use f strings --- swmmio/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swmmio/core.py b/swmmio/core.py index 8e1c399..9a95249 100644 --- a/swmmio/core.py +++ b/swmmio/core.py @@ -574,7 +574,7 @@ def save(self, target_path=None): for section in self._sections: # reformate the [SECTION] to section (and _section_df) sect_id = section.translate({ord(i): None for i in '[]'}).lower() - sect_id_private = f'_{sect_id}_df' + sect_id_private = '_{}_df'.format(sect_id) data = getattr(self, sect_id_private) if data is not None: replace_inp_section(target_path, section, data) From 6f7f324a4055d527012e1b35331a8f1546c81ecc Mon Sep 17 00:00:00 2001 From: Adam Erispaha Date: Tue, 18 Jun 2019 18:54:56 -0400 Subject: [PATCH 9/9] add test data to package dist, update setup for new pypi settings --- MANIFEST.in | 3 ++ setup.py | 51 ++++++++++-------- swmmio/.DS_Store | Bin 6148 -> 0 bytes .../{RUNOFF46_SW5.INP => RUNOFF46_SW5.inp} | 0 swmmio/tests/data/__init__.py | 2 +- 5 files changed, 34 insertions(+), 22 deletions(-) delete mode 100644 swmmio/.DS_Store rename swmmio/tests/data/{RUNOFF46_SW5.INP => RUNOFF46_SW5.inp} (100%) diff --git a/MANIFEST.in b/MANIFEST.in index 791946a..6895dd4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,3 +4,6 @@ include lib/linux/swmm5 include swmmio/reporting/basemaps/*.html include swmmio/graphics/fonts/*.ttf include swmmio/defs/*.json +include swmmio/tests/data/*.inp +include swmmio/tests/data/*.rpt +include swmmio/tests/data/*.csv \ No newline at end of file diff --git a/setup.py b/setup.py index 2948f54..c507331 100644 --- a/setup.py +++ b/setup.py @@ -7,6 +7,7 @@ HERE = os.path.abspath(os.path.dirname(__file__)) + def get_version(module='swmmio'): """Get version.""" with open(os.path.join(HERE, module, '__init__.py'), 'r') as f: @@ -19,8 +20,12 @@ def get_version(module='swmmio'): break return version -def read(fname): - return open(os.path.join(os.path.dirname(__file__), fname)).read() + +def get_description(): + """Get long description.""" + with open(os.path.join(HERE, 'README.md'), 'r') as f: + data = f.read() + return data AUTHOR_NAME = 'Adam Erispaha' @@ -32,32 +37,36 @@ def read(fname): 'pandas', 'pyshp', 'geojson', - ] +] tests_require = [ 'pytest', ] -setup(name = 'swmmio', - version = get_version(), - description = 'Tools for reading, writing, visualizing, and versioning EPA SWMM5 models.', - author = AUTHOR_NAME, - url = 'https://github.com/aerispaha/swmmio', - author_email = AUTHOR_EMAIL, - packages = find_packages(exclude = ('tests')), - entry_points = { - "console_scripts": ['swmmio_run = swmmio.run_models.run:run_simple'] - }, - install_requires = install_requires, - tests_require = tests_require, - long_description = read('README.rst'), - include_package_data = True, - platforms = "OS Independent", - license = "MIT License", - classifiers = [ +setup(name='swmmio', + version=get_version(), + description='Tools for interacting with, editing, and visualizing EPA SWMM5 models', + author=AUTHOR_NAME, + url='https://github.com/aerispaha/swmmio', + author_email=AUTHOR_EMAIL, + packages=find_packages(exclude=('tests')), + entry_points={ + "console_scripts": ['swmmio_run = swmmio.run_models.run:run_simple'] + }, + install_requires=install_requires, + tests_require=tests_require, + long_description=get_description(), + long_description_content_type="text/markdown", + include_package_data=True, + platforms="OS Independent", + license="MIT License", + classifiers=[ "Development Status :: 3 - Alpha", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.6", ] -) + ) + + diff --git a/swmmio/.DS_Store b/swmmio/.DS_Store deleted file mode 100644 index 6bc1530b0b10f864cc48c789ab618e56e6f48027..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~%Wl&^6o${VZt4_4v#5aOY}jSd2tmrCl9jd#c1S_6fGV|PlUQ;+k?k~U(^P&7 zcq1Nx*WsTF(WEKr4xwm{H1m(onPY1{&Ba4RtT&B2L@grnQKWi%s2(sGmu1bijOQ7Y zsdtPhiOXRY36ZV!b`D3t5%}K-$hF&`m_oWlvDPmZGdoXV%8JWJTBFHK7`d6x#~lM*7Nzi`=-s`br)?ue|ywz^P|_D#lrVryy_gk zzqq`y6k>{%;gzUmiwCh`$FO1}>{1rSYWOeqbiw3C_$ zB?t*X)z%|!OVKp)|7591yPO~Ge?`6kr#%2rYCAS@BWy_pED0ov#+_dR3{y29kj z0SnX1PT5z1f=b{Lis?{k1;&ChyX$h^+YPfe{3o{4wH*ORz!3lexj%5Dfzev4+B#6F zD*)7kTN&E&E+KQd(ZFb}6%iQIsZgED%oT&_bojaH7Z|Oz>U3h}^1;lPnYp1b^>w_T ztM0@Ct*&+i9D!8=+xoF3&;PT(-~U&W+>;~V2y7Gq=AHIWdl-^ATh|6B&svUhfg(-z ntF@{WROUEV4S5v5L{WwqmknrOwAKm_%>4)`46bqn{waaq0*DUR diff --git a/swmmio/tests/data/RUNOFF46_SW5.INP b/swmmio/tests/data/RUNOFF46_SW5.inp similarity index 100% rename from swmmio/tests/data/RUNOFF46_SW5.INP rename to swmmio/tests/data/RUNOFF46_SW5.inp diff --git a/swmmio/tests/data/__init__.py b/swmmio/tests/data/__init__.py index 771fa37..c8550be 100644 --- a/swmmio/tests/data/__init__.py +++ b/swmmio/tests/data/__init__.py @@ -19,7 +19,7 @@ MODEL_FULL_FEATURES__NET_PATH = os.path.join( DATA_PATH, 'model_full_features_network.inp') MODEL_FULL_FEATURES_INVALID = os.path.join(DATA_PATH, 'invalid_model.inp') -MODEL_BROWARD_COUNTY_PATH = os.path.join(DATA_PATH, 'RUNOFF46_SW5.INP') +MODEL_BROWARD_COUNTY_PATH = os.path.join(DATA_PATH, 'RUNOFF46_SW5.inp') # version control test models MODEL_XSECTION_BASELINE = os.path.join(DATA_PATH, 'baseline_test.inp')