HEXRD / hexrd

A cross-platform, open-source library for the analysis of X-ray diffraction data.
Other
55 stars 25 forks source link

A more user friendly initialization method for material.Material objects #561

Open argerlt opened 11 months ago

argerlt commented 11 months ago

I had a feature I wanted to add, but I want to make sure it doesn't already exist in a way I don't know about, and possibly have some experts weigh in on what to watch out for so I don't break any important features.

@donald-e-boyce @psavery @saransh13

Elevator Pitch of the Issue:

Allow users to more easily initalize Material Class objects without initializing from an h5 or CIF. The method commonly used in example scripts involves creating a default Nickel material, then selectively overwriting the relevant attributes. This requires some a-priori understanding of how the Materials class is organized and calculated in HEXRD, and can occasionally cause program-breaking errors if additional hidden attributes (eg, PlaneData and UnitCell objects) are not also overwritten in the correct order.

Longer Description

currently, initializing a material class WITHOUT an already existing hdf5 or cif calls Materials.init with the following defaults:

    DFLT_NAME = 'material.xtal'
    DFLT_XTAL = 'Ni'
    DFLT_SGNUM = 225
    DFLT_LPARMS = [_angstroms(3.61), _angstroms(3.61), _angstroms(3.61),
                   _degrees(90.0), _degrees(90.0), _degrees(90.0)]
    DFLT_SSMAX = 100
    DFLT_KEV = valWUnit('wavelength', 'energy', 80.725e0, 'keV')
    DFLT_STR = 0.0025
    DFLT_TTH = numpy.radians(0.25)
    DFLT_TTHMAX = None
    DFLT_ATOMINFO = numpy.array([[0., 0., 0., 1.]])
    DFLT_U = numpy.array([6.33E-3])
    DFLT_ATOMTYPE = numpy.array([28])
    DFLT_CHARGE = numpy.array(["0"])
    DFLT_DMIN = _angstroms(0.75)
    DFLT_STIFFNESS = numpy.eye(6)
    DFLT_SGSETTING = 0

If users want to create a new material outside of hexrdgui (say, testing a toy problem with randomly generated crystal structures or testing different beam energies), the easiest way is usually: 1) create a default nickel material 2) overwrite relevant default values 3) know enough about how the material class works to know to rerun the relevant secondary initialization functions to regenerate the correct PlaneData, Unitcell, etc.

Often, users will write some variation of a generator function like the one below to automate this:

def make_matl(mat_name, sgnum, lparms, hkl_ssq_max=50):
    matl = material.Material(mat_name)
    matl.sgnum = sgnum
    matl.latticeParameters = lparms
    matl.hklMax = hkl_ssq_max
    nhkls = len(matl.planeData.exclusions)
    matl.planeData.set_exclusions(np.zeros(nhkls, dtype=bool))
    return matl

This works, but it's clunky, and its also alienating to new users who do not know a priori how this function works or how to properly overwrite the correct values.

Synopsis of My Solution:

Instead of 20-odd default values that clutter up the namespace, there should just be a default values dictionary, and users should be able to selectively overwrite the values they want on initialization, as opposed to after the fact. this can be done both through custom dictionaries of changed values, or just by passing in wildcard keywords

Initial rules should stay unchanged: all the same inputs are still allowed, and values taken from a CIF or HDF5 still overwrite defaults. All I want is the ability to pass any of the following single lines and have all of them give a reasonable result

from hexrd.material import Material

ruby_1 = Material('Ruby', kev=78.6)
ruby_2 = Material('Ruby', kev=valWUnit("wavelength","ENERGY",78.6,"keV")
Mg1 = Material("Mg_alloy", sgsetting=[0.333,0.667,0.25,] A=3.17, B=3.17, C=5.14, alpha=90, beta=90, gamma=90)
Mg_alloy_from_file = Material("Mg_stretched,
different_me3 = Material('ME3', "materials.h5"], name="doped_me3",C=5.20)

DO NOTE: I am aware now that there are ways to initialize these materials using HEXRD as is, but they are not well documented or intuitive, and they slowed me down a lot while learning. this is less about expanding the capabilities, and more about simplifying the process and adding documentation so new users can have an easier time learning on their own.

saransh13 commented 11 months ago

I do support a simpler interface. If you have improvements then do submit a PR! One concern I have in giving users too much freedom. For eg., if only alpha angle is specified together with lattice parameters a and b, then that limits the space group to a subset of the 230. It just seems a lot of extra work to go through that.

I will point you to hexrd.mksupport.mk for a command line tool for making a material. Here's how it looks for a FCC material

In [1]: from hexrd.mksupport import mk

In [2]: filename = "test.h5"

In [3]: xtalname = "testxtal"

In [4]: mk(filename, xtalname)

    This is a program to create a HDF5 file for storing crystallographic information.
  This format is the same format as used in the EMsoft (electron microscoy) suite.
  The following inputs are required:
          Crystal System:
                 1. Cubic
                 2. Tetragonal
                 3. Orthorhombic
                 4. Hexagonal
                 5. Trigonal
                 6. Monoclinic
                 7. Triclinic

         Space group number
         Atomic number (Z) for all species in unit cell
         Asymmetric positions for all atoms in unit cell
         Debye-Waller factors for all atoms in the unit cell
         You'll be prompted for these values now

 Note about the trigonal system:
 -------------------------------
 Primitive trigonal crystals are defined with respect to a HEXAGONAL
 reference frame.  Rhombohedral crystals can be referenced with
 respect to a HEXAGONAL basis (first setting), or with respect to
 a RHOMBOHEDRAL basis (second setting).  The default setting for
 trigonal symmetry is the hexagonal setting.  When you select
 crystal system 5 above, you will be prompted for the setting. 

Crystal System (1-7 use the legend above): 1
a [nm] : 3

195: P 2 3      196: F 2 3      197: I 2 3      198: P 21 3     
199: I 21 3     200: P m 3      201: P n 3      202: F m 3      
203: F d 3      204: I m 3      205: P a 3      206: I a 3      
207: P 4 3 2    208: P 42 3 2   209: F 4 3 2    210: F 41 3 2   
211: I 4 3 2    212: P 43 3 2   213: P 41 3 2   214: I 41 3 2   
215: P -4 3 m   216: F -4 3 m   217: I -4 3 m   218: P -4 3 n   
219: F -4 3 c   220: I -4 3 d   221: P m 3 m    222: P n 3 n    
223: P m 3 n    224: P n 3 m    225: F m 3 m    226: F m 3 c    
227: F d 3 m    228: F d 3 c    229: I m 3 m    230: I a 3 d    

Space group number (use legend above): 225
 ------------------------------------ Periodic Table of the Elements --------------------------------------
1:H                                                                                                    2:He
3:Li  4:Be                                                               5:B   6:C   7:N   8:O   9:F  10:Ne
11:Na 12:Mg                                                             13:Al 14:Si 15:P  16:S  17:Cl 18:Ar
19:K  20:Ca 21:Sc 22:Ti 23:V  24:Cr 25:Mn 26:Fe 27:Co 28:Ni 29:Cu 30:Zn 31:Ga 32:Ge 33:As 34:Se 35:Br 36:Kr
37:Rb 38:Sr 39:Y  40:Zr 41:Nb 42:Mo 43:Tc 44:Ru 45:Rh 46:Pd 47:Ag 48:Cd 49:In 50:Sn 51:Sb 52:Te 53: I 54:Xe
55:Cs 56:Ba ----- 72:Hf 73:Ta 74:W  75:Re 76:Os 77:Ir 78:Pt 79:Au 80:Hg 81:Tl 82:Pb 83:Bi 84:Po 85:At 86:Rn
87:Fr 88:Ra -----
57:La 58:Ce 59:Pr 60:Nd 61:Pm 62:Sm 63:Eu 64:Gd 65:Tb 66:Dy 67:Ho 68:Er 69:Tm 70:Yb 71:Lu
89:Ac 90:Th 91:Pa 92:U
 ----------------------------------------------------------------------------------------------------------
Enter atomic number of species :   28
Enter asymmetric position of atom in unit cell          separated by comma (fractional coordinates) :  0,0,0
Enter site occupation :    1
Isotropic or anisotropic Debye-Waller factor? 
              1 for isotropic, 2 for anisotropic : 1
Enter isotropic Debye-Waller factor [nm^(-2)] : 0
Another atom? (y/n) : n

This creates a test.h5 file (screenshot below)

Screenshot 2023-10-06 at 11 58 08 AM
argerlt commented 11 months ago

hmmmmm......... I didn't know about this. I might try wrapping a part of this into a generator.

My fear is that drastic changes are going to break some part of HEXRD I am unfamiliar with, and because HEXRD isn't fully covered by tests, It won't be obvious until someone reports some catastrophic break to their pipeline I hadn't considered.

My approach would retain all the existing keyword inputs, so it's guaranteed (in theory) not to break anything currently in place.

Modeling off the CL version is much nicer though, and having the CL and module methods use identical functions for generation would be a big improvement for maintainable code.....

I'll try both ways, see what happens, and start a PR.

ZackAttack614 commented 1 month ago

@argerlt I would like to incorporate these changes into our upcoming restructuring of the library. Is this still on your radar? Would like to sync up about it at some point over the migration process.

argerlt commented 1 month ago

@ZackAttack614, Sorry for the slow response. It is, but it took a major back seat to other work.

I can submit a PR of how I think such the class should be laid out, and you can keep/toss what you want. Just let me know what branch is best to work off of, in light of the restructuring. Also, if I'm going slow, feel free to lap me and do it yourself, I'm working on finishing my Thesis right now so I'm a bit slow in general.

In short though, I think as long as the end result: 1) is ITOC-compliant 2) Has basic unittests for testing Laue/Space/Point group combinations 3) the CLI and the initiate-from-class approach produce identical results from the same inputs (not currently true in my experience) 4) there is at least minimal documentation

I will be happy.