-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from wpbonelli/package
Minimal package impl
- Loading branch information
Showing
8 changed files
with
374 additions
and
124 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
from abc import ABCMeta | ||
from collections import UserDict | ||
from itertools import groupby | ||
from typing import Any | ||
|
||
from flopy4.block import MFBlock, MFBlockMeta, MFBlocks | ||
from flopy4.parameter import MFParameter, MFParameters | ||
from flopy4.utils import strip | ||
|
||
|
||
def get_block(clsname, params): | ||
return MFBlockMeta(clsname, (MFBlock,), params)(params=params) | ||
|
||
|
||
class MFPackageMeta(type): | ||
def __new__(cls, clsname, bases, attrs): | ||
new = super().__new__(cls, clsname, bases, attrs) | ||
if clsname == "MFPackage": | ||
return new | ||
|
||
# add parameter and block specification as class | ||
# attributes. subclass mfblock dynamically based | ||
# on each block parameter specification. | ||
pkg_name = clsname.replace("Package", "") | ||
params = MFParameters( | ||
{ | ||
k: v.with_name(k) | ||
for k, v in attrs.items() | ||
if issubclass(type(v), MFParameter) | ||
} | ||
) | ||
new.params = params | ||
new.blocks = MFBlocks( | ||
{ | ||
block_name: get_block( | ||
clsname=f"{pkg_name.title()}{block_name.title()}Block", | ||
params={p.name: p for p in block}, | ||
) | ||
for block_name, block in groupby( | ||
params.values(), lambda p: p.block | ||
) | ||
} | ||
) | ||
return new | ||
|
||
|
||
class MFPackageMappingMeta(MFPackageMeta, ABCMeta): | ||
# http://www.phyast.pitt.edu/~micheles/python/metatype.html | ||
pass | ||
|
||
|
||
class MFPackage(UserDict, metaclass=MFPackageMappingMeta): | ||
""" | ||
MF6 package base class. | ||
TODO: reimplement with `ChainMap`? | ||
""" | ||
|
||
def __getattribute__(self, name: str) -> Any: | ||
value = super().__getattribute__(name) | ||
if name == "data": | ||
return value | ||
|
||
# shortcut to parameter value for instance attribute. | ||
# the class attribute is the full parameter instance. | ||
params = { | ||
param_name: param | ||
for block in self.data.values() | ||
for param_name, param in block.items() | ||
} | ||
param = params.get(name) | ||
return params[name].value if param is not None else value | ||
|
||
@property | ||
def params(self) -> MFParameters: | ||
"""Package parameters.""" | ||
return MFParameters( | ||
{ | ||
name: param | ||
for block in self.data | ||
for name, param in block.items() | ||
} | ||
) | ||
|
||
@classmethod | ||
def load(cls, f): | ||
"""Load the package from file.""" | ||
blocks = dict() | ||
members = cls.blocks | ||
|
||
while True: | ||
pos = f.tell() | ||
line = f.readline() | ||
if line == "": | ||
break | ||
line = strip(line).lower() | ||
words = line.split() | ||
key = words[0] | ||
if key == "begin": | ||
name = words[1] | ||
block = members.get(name) | ||
if block is not None: | ||
f.seek(pos) | ||
blocks[name] = type(block).load(f) | ||
|
||
pkg = cls() | ||
pkg.update(blocks) | ||
return pkg | ||
|
||
def write(self, f): | ||
"""Write the package to file.""" | ||
for block in self.data.values(): | ||
block.write(f) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.