-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ref/properties #70
Ref/properties #70
Conversation
…y, add a check to ensure the cartesian origin is being set properly. Have x0 be passed as a kwarg in meshIO
Codecov Report
@@ Coverage Diff @@
## dev #70 +/- ##
==========================================
+ Coverage 76.98% 76.98% +<.01%
==========================================
Files 17 17
Lines 4797 4858 +61
==========================================
+ Hits 3693 3740 +47
- Misses 1104 1118 +14
Continue to review full report at Codecov.
|
…ent/properties#191, test serialization and deserialization of tree mesh
@@ -21,9 +21,6 @@ discretize | |||
:target: https://www.codacy.com/app/lindseyheagy/discretize?utm_source=github.com&utm_medium=referral&utm_content=simpeg/discretize&utm_campaign=Badge_Grade | |||
:alt: codacy status | |||
|
|||
.. image:: https://www.quantifiedcode.com/api/v1/project/97126041a7e7400fbf1c5555bed514dc/badge.svg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we weren't really using this and they discontinued it
|
||
class BaseRectangularMesh(BaseMesh): | ||
"""BaseRectangularMesh""" | ||
def __init__(self, n, x0=None): | ||
BaseMesh.__init__(self, n, x0) | ||
BaseMesh.__init__(self, n, x0=x0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this isn't strictly necessary. just for clarity
assert (type(x) == list or isinstance(x, np.ndarray)), "x must be either a list or a ndarray" | ||
assert xType in ['CC', 'N', 'F', 'Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', 'Ez'], "xType must be either 'CC', 'N', 'F', 'Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', or 'Ez'" | ||
assert outType in ['CC', 'N', 'F', 'Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', 'Ez'], "outType must be either 'CC', 'N', 'F', Fx', 'Fy', 'Fz', 'E', 'Ex', 'Ey', or 'Ez'" | ||
allowed_xType = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whitespace cleanup
discretize/CurvilinearMesh.py
Outdated
@@ -25,7 +27,62 @@ def normalize3D(x): | |||
return x/np.kron(np.ones((1, 3)), utils.mkvc(length3D(x), 2)) | |||
|
|||
|
|||
class CurvilinearMesh(BaseRectangularMesh, DiffOperators, InnerProducts, CurviView): | |||
class Array(properties.Array): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the implementation of an array that doesn't care about shape (I allow shape to be '*'
which could be a 1D, 2D or 3D array). See discussion on seequent/properties#192
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Union property could work well - see my comment on seequent/properties#192
discretize/TreeMesh.py
Outdated
min=0 | ||
) | ||
|
||
cells = properties.Set( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to keep this a private variable _cells
. See discussion on seequent/properties#191
@@ -1,8 +1,8 @@ | |||
/* Generated by Cython 0.24.1 */ | |||
/* Generated by Cython 0.25.2 */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you prefer, I can remove this. Updated cython cc @rowanc1
@@ -19,7 +19,6 @@ def setUp(self): | |||
def test_vectorN_2D(self): | |||
testNx = np.array([3, 4, 5, 6]) | |||
testNy = np.array([5, 6, 8]) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just whitespace in this file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good. A few minor comments on the min/max side of things.
discretize/BaseMesh.py
Outdated
n = properties.Array( | ||
"number of cells in each direction (dim, )", | ||
dtype=int, | ||
required=True, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want min_length
or max_length
here?
http://propertiespy.readthedocs.io/en/latest/content/container.html#properties.List
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, that is only for lists. Perhaps we could change properties to have a set
of tuples for defining the shape?
shape={(1,), (2,), (3,)}
??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be handy
discretize/BaseMesh.py
Outdated
@properties.validator('n') | ||
def check_n_shape(self, change): | ||
change['value'] = np.array(change['value'], dtype=int).ravel() | ||
if len(change['value']) > 3: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can be solved by min/max length in the list property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't a list (it is an array). Which I don't think has a min/max length
discretize/BaseMesh.py
Outdated
""" | ||
|
||
# Properties | ||
n = properties.Array( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we change this later? Having it as a public property seems to suggest that we can change it....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the user shouldn't be changing it. It might, for example, get updated if you refine an octree mesh.
Properties doesn't currently allow private variables. see seequent/properties#191
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed though - ideally, this should remain a private variable
discretize/CurvilinearMesh.py
Outdated
Array( | ||
"node locations in a dimension", | ||
shape='*' | ||
), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
min=2 and max=3 length on the list?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an array, so we can't, but I will implement the Union Property as suggested by @fwkoch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, I love the properties stuff - let me know if you have questions on any of my comments. I also had a few non-properties questions, mostly minor things. Also, I really like the whitespace cleanup everywhere!!
discretize/TreeMesh.py
Outdated
"cell number", | ||
min=0 | ||
), | ||
default=set() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should use default=set
- this dynamic default will get called every time, creating a new set
for each TreeMesh
instance. The way you have it now, this specific set()
will be shared across all instances, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, thanks
discretize/BaseMesh.py
Outdated
) | ||
|
||
@properties.validator('x0') | ||
def check_x0_vs_n(self, change): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple picky thoughts on this:
self.n
could beNone
if someone deletes it or sets it to undefined.len(None)
would give an ugly error. Possibly worth adding an extra sanity check for that?- Also, possibly worth running this check in the other direction (ie you validate
x0
vsn
, but notn
vsx0
) - may come up depending on how someone is using this.
discretize/BaseMesh.py
Outdated
@@ -44,7 +60,7 @@ def dim(self): | |||
:rtype: int | |||
:return: dim | |||
""" | |||
return self._dim | |||
return len(self.n) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For all of these, actually, possibly worth checking if things (self.n
, self.dims
, etc.) are None
.
discretize/CurvilinearMesh.py
Outdated
@@ -25,7 +27,62 @@ def normalize3D(x): | |||
return x/np.kron(np.ones((1, 3)), utils.mkvc(length3D(x), 2)) | |||
|
|||
|
|||
class CurvilinearMesh(BaseRectangularMesh, DiffOperators, InnerProducts, CurviView): | |||
class Array(properties.Array): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Union property could work well - see my comment on seequent/properties#192
discretize/CurvilinearMesh.py
Outdated
assert nodes_i.shape == nodes[0].shape, ("nodes[{0:d}] is not the " | ||
"same shape as nodes[0]" | ||
.format(i)) | ||
def __init__(self, nodes=nodes, **kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you are using nodes
, the properties.List
, as the default value...? Seems fishy... though I may be missing something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, this is a typo.
|
||
@properties.validator('cartesianOrigin') | ||
def check_cartesian_origin_shape(self, change): | ||
change['value'] = np.array(change['value'], dtype=float).ravel() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is cool! I like seeing that you are using the validator to (possibly) change the value.
discretize/TensorMesh.py
Outdated
|
||
else: | ||
assert 'h' in kwargs.keys(), 'cell widths must be provided' | ||
h = kwargs.pop('h') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a little funny - on first impulse, my question was "Why does h
get special treatment as an arg but not as a kwarg?" Looking into it further, though, as long as h is provided as an arg or a kwarg, it will set h_in
. So really, this line will never be reached; if the else
clause is reached, the assert
will fail (I think at least).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a bit odd, and maybe you have a suggestion for me on how this might be made more transparent?
The reason for this is to support two uses:
- creating the mesh with
h, x0
as args (orh_in
). This is currently how most people work with the API - the reason for having
h
also allowed as a kwarg is so that I can create a re-create a mesh from a serialized mesh
where load_mesh
deserializes the mesh (https://github.com/simpeg/discretize/blob/ref/properties/discretize/utils/meshutils.py#L17)
def load_mesh(filename):
"""
Open a json file and load the mesh into the target class
As long as there are no namespace conflicts, the target __class__
will be stored on the properties.HasProperties registry and may be
fetched from there.
:param str filename: name of file to read in
"""
with open(filename, 'r') as outfile:
jsondict = json.load(outfile)
data = properties.HasProperties.deserialize(jsondict, trusted=True)
return data
and for this I am under the impression that all properties in the serialized json needed to be able to be read in as kwargs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the need for h as both arg and kwarg - I think that is still covered.
This exposes a bit of a subtlety with how Python treats args/kwargs. If a named arg variable shows up anywhere in the kwargs (and is not provided in the args) it is assigned to the associated arg (and that variable does not show up in kwargs
internal to the function).
If you play around with this:
class Blah(object):
def __init__(self, h=None, x0=None, **kwargs):
print(h)
print(x0)
print(kwargs)
You will find for all of these, kwargs = {'something_else': 2}
(and h/x0 are assigned separately):
Blah(0, 1, something_else=2)
Blah(h=0, x0=1, something_else=2)
Blah(something_else=2, x0=1, h=0)
This errors (h assigned twice):
Blah(0, x0=1, something_else=2, h=3)
So bottom line, even if it is coming in as a deserialized dict, the argument will still be moved to the right place. Does that make sense...?
discretize/TensorMesh.py
Outdated
|
||
# Ensure h contains 1D vectors | ||
self._h = [utils.mkvc(x.astype(float)) for x in h] | ||
n = np.array([x.size for x in h]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be else
?
discretize/utils/meshutils.py
Outdated
""" | ||
Open a json file and load the mesh into the target class | ||
|
||
As long as there are no namespace conflicts, the target __class__ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want, you can narrow down your namespace a little by overriding the _REGISTRY
on your BaseMesh
class (ie just add _REGISTRY = {}
to the class). Then you could say data = BaseMesh.deserialize(...)
and it will only look at classes that inherit from BaseMesh
(and therefore are on the BaseMesh registry, rather than the HasProperties registry).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, cool, thanks!
…the testing to make sure errors are raised if the user messes with n.
…rialize onto BaseMesh rather than properties.HasProperties
Properties can now be private (see seequent/properties#194, thanks @fwkoch!!), so...
and Arrays can now be given a set for the shape (see seequent/properties#195), so
I am happy with the state of this pr at this point, @rowanc1, if you are as well, I will do a beta release and merge to dev. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me! Thanks @lheagy this is awesome.
@@ -290,11 +351,33 @@ def projectEdgeVector(self, eV): | |||
), 'eV must be an ndarray of shape (nE x dim)' | |||
return np.sum(eV*self.tangents, 1) | |||
|
|||
def save(self, filename='mesh.json', verbose=False): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might want to be on a base class for simpeg in future (or in the utils of discretize)? It looks like a pretty generic function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Once we have injected properties elsewhere in SimPEG we can abstract this up.
"You cannot change types when reshaping." | ||
) | ||
assert xType in outType, "You cannot change type of components." | ||
|
||
if type(x) == list: | ||
for i, xi in enumerate(x): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this never gets hit by tests. Did it before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only thing I changed above is whitespace. This particular line has not changed at all in this pr
"List of arrays describing the node locations", | ||
prop=properties.Array( | ||
"node locations in an n-dimensional array", | ||
shape={('*', '*'), ('*', '*', '*')} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool!
Implement properties in discretize
Use properties to define the properties of a class that are necessary to completely define the object. This allows us to serialize, save and load meshes (also copy!)
todos
@rowanc1, @fwkoch: if you guys would be willing to do a review, I would appreciate another set of eyes
Related issues