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
Add adapters - Base design + 'structures' (+ 'references'... sort of) #241
Conversation
Codecov Report
@@ Coverage Diff @@
## master #241 +/- ##
=======================================
Coverage 89.32% 89.32%
=======================================
Files 54 54
Lines 2220 2220
=======================================
Hits 1983 1983
Misses 237 237
Continue to review full report at Codecov.
|
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 @CasperWA , I think having these converters in the optimade python tools is very useful.
May I suggest to add both individual extras for the corresponding dependencies + a converters
extra that contains them all?
"pdb": get_pdb, | ||
"pdbx_mmcif": get_pdbx_mmcif, | ||
"pymatgen": get_pymatgen_structure, | ||
} |
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.
Eventually, this dictionary might become an instance of a ConverterCollection
class that could include checks on the interface of the functions it contains etc.
For the moment, this is fine
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.
Great idea. And definitely one for the future, i.e., not something I will explore at this time.
I'm not fully seeing what this does. How does this plug into the server for instance? |
It's not for server use, more for client use. The main goal is to interpret OPTIMADE structures and retrieve their information/convert them to client-usable data formats and constructs. Edit: Since this repository is meant to host OPTIMADE tools for use in Python, it should/could cover both server side and client side tools, right? :) |
Thanks :) Me too, hence the "fold back" from the client I'm working on.
I thought about this, got tired at the idea, and set it aside for a time when I have not more exciting ideas for how the adapter should work. I.e., I will definitely add this to the Edit: I have added the dependencies, but named them |
Not going to review this until everyone else has finished hacking at it (ping me @CasperWA), but think this looks very useful and is definitely in the spirit of "optimade-python-tools". Once we get this in, I think we should focus for a while on writing docs that showcase how this repo can be used by implementers of servers and clients. Would we even want to provide a simple Python/CLI client implementation in this repo, given that we have most of the tools already? (I guess this would depend on how much this treads on the toes of your fancier client @CasperWA) |
712662d
to
42c70f6
Compare
@ml-evs @ltalirz @shyamd I consider this ready for review now. Some of the conversion function tests are the same from test file to test file. This may be made more elegant, but in order to properly separate out the test functions if one should not have the specific dependency installed, this is the design I chose. Note also, the kind of ugly addition to our CI workflows, due to AiiDA. Finally, if you have any optimizations for the various converters, now is the time! :) If you want to add a bunch of new ones (especially to the |
8aa4017
to
d3ccda7
Compare
Consider yourself "pinged" @ml-evs :) (But not until Tuesday the 14th and onwards).
I think that would definitely be the next big "project" for the repo. Also, we should make sure to update to v1.0.0 ASAP (both the package version, but I was thinking mainly of the OPTIMADE API version). Maybe v1 of the package should indeed wait until we have docs in place.
So as soon as this PR goes through, we could (in theory) adopt my client. That way I can update the current version with this adapter. And I don't know how fancy it is... But you can now check it out on binder to judge for yourself 😅 But I don't know if we want an OPTIMADE client in Jupyter under the Materials-Consortia umbrella? In any case! We should probably create an issue for this discussion, or rather pick it up at a consortium meeting. |
The goal of the adapters is to contain a conversion or adaptation ability in a single Python class. The class will be a proxy for `optimade.models.EntityResource`, where the specific entity will take the place, e.g., `StructureResource`. In this way, one can instatiate the adapter class with a JSON resource object matching it, and get all the properties as attributes. E.g., one could get the lattice vectors of a structure then by calling `structures_adapter_class_object.attributes.lattice_vectors`. The reason to not have the adapter class be a direct link or subclass of the model class, is to not inherit all that comes with it, i.e., do not inherit `pyndantic`'s `BaseModel`, etc. While it can trip most IDEs, it is a more elegant solution that opens the doors to other possibilities, in my opinion. Besides being a handy wrapper for an entity resource object for a client, it also serves as a converter/adapter to other known and widely used libraries for the specific entity type. For a structures resource object, this may be libraries such as pymatgen, ASE, AiiDA, etc... It may also be nice to be able to convert the structures resource object to a Crystallographic Information File (CIF) file or other. These adapter/conversion functions may be added in a separate file and then referenced in the adapter class. Thus, the adapter class stays small at first glance, but spreads its tentacles wide throughout both the current package, but also a multitude of other libraries, packages, and data formats. In general, whenever the adapter class tries to get an attribute, it goes through the following steps: 1. Try to fetch the OPTIMADE model property. 2. If the attribute starts with `get_`, try to look up if this is a known conversion, then perform the conversion and return it as a string or specific package/library Python class. 3. Raise AttributeError if the attribute is still unknown, i.e., the previous steps failed and didn't raise already.
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 looks great so far @CasperWA, very nice and thorough. I don't have any general comments, except that I agree we should probably split this up into namespace packages as @shyamd suggested, after this PR and before we write the docs.
I've reviewed everything apart from the PDB, CIF and PDBx/mmCIF stuff as I'd like to play around with the results a bit and make sure we're producing valid output files.
Co-Authored-By: Matthew Evans <7916000+ml-evs@users.noreply.github.com>
Co-Authored-By: Matthew Evans <me388@cam.ac.uk>
For now, add `json` and `dict`, which refer to pydantic's `BaseModel`'s `.json()` and `.dict()` methods. See https://pydantic-docs.helpmanual.io/usage/exporting_models for more information.
Use pytest fixtures to create special structures. Convert null/None values to float("nan"), which resulted in some conversion functions working in more edge cases.
@ml-evs I haven't addressed all your comments yet, and I don't know how much more I will be able to do today. |
Update references tests with pytest fixtures as was done for structures tests.
All right @ml-evs ! I have finished addressing your review comments, it should be ready for re-review. |
Properly handle nested attributes for `getattr()` function. Co-Authored-By: Matthew Evans <me388@cam.ac.uk>
This is to properly get positional coordinates for some viewers, since SCALE is used to get fractional coordinates. Co-Authored-By: Matthew Evans <me388@cam.ac.uk>
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 looking great to me now, thanks for addressing all the comments @CasperWA!
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.
Once more for luck...
The goal of the adapters is to contain a conversion or adaptation ability in a single Python class.
The class will be a proxy for
optimade.models.EntryResource
, where the specific entries will take the place, e.g.,StructureResource
.In this way, one can instatiate the adapter class with a JSON resource object matching it, and get all the properties as attributes. E.g., one could get the lattice vectors of a structure then by calling
structures_adapter_class_object.attributes.lattice_vectors
. The reason to not have the adapter class be a direct link or subclass of the model class, is to not inherit all that comes with it, i.e., do not inheritpyndantic
'sBaseModel
, etc. While it can trip most IDEs, it is a more elegant solution that opens the doors to other possibilities, in my opinion.Besides being a handy wrapper for an entity resource object for a client, it also serves as a converter/adapter to other known and widely used libraries for the specific entity type. For a structures resource object, this may be libraries such as pymatgen, ASE, AiiDA, etc...
It may also be nice to be able to convert the structures resource object to a Crystallographic Information File (CIF) file or other. These adapter/conversion functions may be added in a separate file and then referenced in the adapter class.
Thus, the adapter class stays small at first glance, but spreads its tentacles wide throughout both the current package, but also a multitude of other libraries, packages, and data formats.
In general, whenever the adapter class tries to get an attribute, it goes through the following steps:
get_
, try to look up if this is a known conversion, then perform the conversion and return it as a string or specific package/library Python class.Missing parts of this PR:
Structure
inherit from it.Reference
.(Currently added as an empty inheritence, meaning there are currently no conversions available for
Reference
).Helpful additions/corrections to this PR:
get_cif
andget_pbdx_mmcif
).(Keeping in mind that some of these libraries have a large quantity of converters already, e.g., ASE already converts to PDB and CIF formats, and so therefore these should only be included if going through, e.g., ASE, may end in a loss of data - I may not have been entirely strict enough with this myself already 😅).