Skip to content

Commit

Permalink
fix(#831,#858): Data length check and case-insensitive lookup (#864)
Browse files Browse the repository at this point in the history
* fix(#831,#858): Flopy now verifies data line length meets minimum length criteria. Flopy now retrieves models by case-insensitive name when case-sensitive lookup fails.

* feat(#862): Include message about zero based index variables in the docs.
  • Loading branch information
spaulins-usgs authored May 1, 2020
1 parent f7554dd commit a177d59
Show file tree
Hide file tree
Showing 28 changed files with 5,819 additions and 5,506 deletions.
15 changes: 14 additions & 1 deletion autotest/t505_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ def np001():

model = ModflowGwf(sim, modelname=model_name,
model_nam_file='{}.nam'.format(model_name))
# test case insensitive lookup
assert(sim.get_model(model_name.upper()) is not None)

# test getting model using attribute
model = sim.np001_mod
assert(model is not None and model.name == 'np001_mod')
Expand Down Expand Up @@ -299,7 +302,7 @@ def np001():
timeseries=[(0.0, 60.0), (100000.0, 60.0)],
stress_period_data=[((100, 0, 0), np.nan,
'drn_1'), ((0, 0, 0),
10.0)])
10.0, 'drn_2')])
npf_package = ModflowGwfnpf(model, save_flows=True,
alternative_cell_averaging='logarithmic',
icelltype=1, k=100001.0, k33=1e-12)
Expand All @@ -310,6 +313,16 @@ def np001():
'checker threshold of 1e-11' in summary
assert 'npf package: horizontal hydraulic conductivity values above ' \
'checker threshold of 100000.0' in summary
data_invalid = False
try:
drn_package = ModflowGwfdrn(model, print_input=True, print_flows=True,
save_flows=True, maxbound=1,
timeseries=[(0.0, 60.0), (100000.0, 60.0)],
stress_period_data=[((0, 0, 0), 10.0)])
except MFDataException:
data_invalid = True
assert data_invalid

return


Expand Down
40 changes: 39 additions & 1 deletion flopy/mf6/data/mfdatalist.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from ...datbase import DataListInterface, DataType
from .mffileaccess import MFFileAccessList
from .mfdatastorage import DataStorage, DataStorageType, DataStructureType
from .mfdatautil import to_string
from .mfdatautil import to_string, iterable


class MFList(mfdata.MFMultiDimVar, DataListInterface):
Expand Down Expand Up @@ -271,6 +271,26 @@ def get_data(self, apply_mult=False, **kwargs):
return self._get_data(apply_mult, **kwargs)

def _set_data(self, data, autofill=False):
if isinstance(data, dict):
if 'data' in data:
data_check = data['data']
else:
data_check = None
else:
data_check = data
if iterable(data_check):
# verify data length
min_line_size = self.structure.get_min_record_entries()
if isinstance(data_check[0], np.record) or \
(iterable(data_check[0]) and not
isinstance(data_check[0], str)):
# data contains multiple records
for data_line in data_check:
self._check_line_size(data_line, min_line_size)
else:
# data is a single record
self._check_line_size(data_check, min_line_size)
# set data
self._resync()
try:
if self._get_storage_obj() is None:
Expand All @@ -286,6 +306,24 @@ def _set_data(self, data, autofill=False):
traceback_, None,
self._simulation_data.debug, ex)

def _check_line_size(self, data_line, min_line_size):
if 0 < len(data_line) < min_line_size:
min_line_size = self.structure.get_min_record_entries()
message = 'Data line {} only has {} entries, ' \
'minimum number of entries is ' \
'{}.'.format(data_line, len(data_line),
min_line_size)
type_, value_, traceback_ = sys.exc_info()
raise MFDataException(
self.structure.get_model(),
self.structure.get_package(),
self.structure.path,
'storing data',
self.structure.name,
inspect.stack()[0][3],
type_, value_, traceback_, message,
self._simulation_data.debug)

def set_data(self, data, autofill=False):
self._set_data(data, autofill)

Expand Down
5 changes: 5 additions & 0 deletions flopy/mf6/data/mfdatautil.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import sys, inspect
import numpy as np
from copy import deepcopy
from collections import Iterable
from ..mfbase import MFDataException, FlopyException
from .mfstructure import DatumType
from ...utils.datautil import PyListUtil, DatumUtil
import struct


def iterable(obj):
return isinstance(obj, Iterable)


def get_first_val(arr):
while isinstance(arr, list) or isinstance(arr, np.ndarray):
arr = arr[0]
Expand Down
36 changes: 34 additions & 2 deletions flopy/mf6/data/mfstructure.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
from ..mfbase import PackageContainer, StructException


numeric_index_text = 'This argument is an index variable, which means that ' \
'it should be treated as zero-based when working with ' \
'FloPy and Python. Flopy will automatically subtract ' \
'one when loading index variables and add one when ' \
'writing index variables.'


class DfnType(Enum):
common = 1
sim_name_file = 2
Expand Down Expand Up @@ -933,6 +940,10 @@ def get_type_string(self):
def get_description(self, line_size, initial_indent, level_indent):
item_desc = '* {} ({}) {}'.format(self.name, self.type_string,
self.description)
if self.numeric_index or self.is_cellid:
# append zero-based index text
item_desc = '{} {}'.format(item_desc,
numeric_index_text)
twr = TextWrapper(width=line_size, initial_indent=initial_indent,
drop_whitespace = True,
subsequent_indent=' {}'.format(initial_indent))
Expand All @@ -952,7 +963,6 @@ def get_doc_string(self, line_size, initial_indent, level_indent):
param_doc_string = '{}\n{}'.format(param_doc_string, description)
return param_doc_string


def get_keystring_desc(self, line_size, initial_indent, level_indent):
if self.type != DatumType.keystring:
raise StructException('Can not get keystring description for "{}" '
Expand Down Expand Up @@ -1169,6 +1179,9 @@ class MFDataStructure(object):
(<model>,<package>,<block>,<data>)
get_datatype : () : DataType
returns the DataType of this data (array, list, scalar, ...)
get_min_record_entries : () : int
gets the minimum number of entries, as entered in a package file,
for a single record. excludes optional data items
get_record_size : () : int
gets the number of data items, excluding keyword data items, in this
MFDataStructure
Expand Down Expand Up @@ -1431,6 +1444,18 @@ def is_mult_or_trans(self):
return True
return False

def get_min_record_entries(self):
count = 0
for data_item_structure in self.data_item_structures:
if not data_item_structure.optional:
if data_item_structure.type == DatumType.record:
count += data_item_structure.get_record_size()[0]
else:
if data_item_structure.type != DatumType.keyword or \
count > 0:
count += 1
return count

def get_record_size(self):
count = 0
repeating = False
Expand Down Expand Up @@ -1503,8 +1528,14 @@ def get_description(self, line_size=79, initial_indent=' ',
elif datastr.display_item(index):
if len(description.strip()) > 0:
description = '{}\n'.format(description)
item_desc = item.description
if item.numeric_index or item.is_cellid:
# append zero-based index text
item_desc = '{} {}'.format(item_desc,
numeric_index_text)

item_desc = '* {} ({}) {}'.format(item.name, itype,
item.description)
item_desc)
twr = TextWrapper(width=line_size,
initial_indent=initial_indent,
subsequent_indent=' {}'.format(
Expand Down Expand Up @@ -1553,6 +1584,7 @@ def get_doc_string(self, line_size=79, initial_indent=' ',
level_indent)
var_name = self.python_name
type_name = self.get_type_string()

param_doc_string = '{} : {}'.format(var_name, type_name)
twr = TextWrapper(width=line_size, initial_indent=initial_indent,
subsequent_indent=' {}'.format(initial_indent))
Expand Down
2 changes: 1 addition & 1 deletion flopy/mf6/mfbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ def get_package(self, name=None):
if name.lower() in self.package_key_dict:
return self.package_key_dict[name.lower()]

# search for partial package name
# search for partial and case-insensitive package name
for pp in self._packagelist:
if pp.package_name is not None:
# get first package of the type requested
Expand Down
Loading

0 comments on commit a177d59

Please sign in to comment.